From f06322b3d1518217cdaf45c7e0dbf8cc3efaff39 Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Sun, 22 Dec 2024 11:59:37 +1100 Subject: [PATCH 1/3] Combine Fixtures into Physics The only reason these were ever split is due to lacking delta states at the time and grid fixtures lagging people so hard they dropped their connections. --- .../Physics/BoxStackBenchmark.cs | 7 +- .../Physics/CircleStackBenchmark.cs | 7 +- Robust.Benchmarks/Physics/PyramidBenchmark.cs | 5 +- Robust.Benchmarks/Physics/TumblerBenchmark.cs | 14 +- Robust.Client/Debugging/DebugPhysicsSystem.cs | 8 +- .../GridChunkBoundsDebugSystem.cs | 5 +- Robust.Client/GameStates/NetGraphOverlay.cs | 5 +- .../Console/Commands/ScaleCommand.cs | 11 +- .../Systems/EntityLookup.Queries.cs | 29 +- .../EntityLookupSystem.ComponentQueries.cs | 21 +- .../GameObjects/Systems/EntityLookupSystem.cs | 53 ++- .../Systems/SharedGridFixtureSystem.cs | 24 +- .../Systems/SharedMapSystem.Grid.cs | 7 +- .../GameObjects/Systems/SharedMapSystem.cs | 6 +- .../Map/Components/GridTreeComponent.cs | 3 +- Robust.Shared/Map/MapManager.Queries.cs | 7 +- .../Components/PhysicsComponent.Physics.cs | 22 +- .../Components/PhysicsComponentState.cs | 8 + Robust.Shared/Physics/Dynamics/Fixture.cs | 12 +- .../Physics/Events/MassChangedEvent.cs | 8 +- Robust.Shared/Physics/FixturesComponent.cs | 43 -- .../Physics/Systems/FixtureSystem.Shapes.cs | 278 ------------- .../Physics/Systems/FixtureSystem.cs | 384 ------------------ .../Physics/Systems/FixturesChangeSystem.cs | 12 +- .../Physics/Systems/SharedBroadphaseSystem.cs | 47 +-- .../Systems/SharedPhysicsSystem.Components.cs | 200 ++++++--- .../Systems/SharedPhysicsSystem.Contacts.cs | 10 +- .../Systems/SharedPhysicsSystem.Fixtures.cs | 325 +++++++++++---- .../SharedPhysicsSystem.FixturesChange.cs | 4 +- .../Systems/SharedPhysicsSystem.Queries.cs | 35 +- .../Systems/SharedPhysicsSystem.Shapes.cs | 291 ++++++++++++- .../Systems/SharedPhysicsSystem.Velocities.cs | 10 +- .../Physics/Systems/SharedPhysicsSystem.cs | 17 +- Robust.UnitTesting/RobustUnitTest.cs | 2 - .../Server/RobustServerSimulation.cs | 2 - .../Shared/EntityLookup_Test.cs | 2 +- .../Shared/Map/GridFixtures_Tests.cs | 21 +- .../Physics/BroadphaseNetworkingTest.cs | 3 +- .../Shared/Physics/Broadphase_Test.cs | 14 +- .../Shared/Physics/Collision_Test.cs | 18 +- .../Shared/Physics/FixtureShape_Test.cs | 7 +- .../Shared/Physics/Fixtures_Test.cs | 3 +- .../Shared/Physics/GridMovement_Test.cs | 13 +- .../Shared/Physics/JointDeletion_Test.cs | 14 +- .../Shared/Physics/Joints_Test.cs | 11 +- .../Shared/Physics/PhysicsComponent_Test.cs | 5 +- .../Shared/Physics/PhysicsMap_Test.cs | 5 +- .../Shared/Physics/RayCast_Test.cs | 2 +- .../Shared/Physics/Stack_Test.cs | 32 +- 49 files changed, 905 insertions(+), 1167 deletions(-) delete mode 100644 Robust.Shared/Physics/FixturesComponent.cs delete mode 100644 Robust.Shared/Physics/Systems/FixtureSystem.Shapes.cs delete mode 100644 Robust.Shared/Physics/Systems/FixtureSystem.cs diff --git a/Robust.Benchmarks/Physics/BoxStackBenchmark.cs b/Robust.Benchmarks/Physics/BoxStackBenchmark.cs index 406264f54b3..30230344f2d 100644 --- a/Robust.Benchmarks/Physics/BoxStackBenchmark.cs +++ b/Robust.Benchmarks/Physics/BoxStackBenchmark.cs @@ -49,16 +49,15 @@ public void BoxStack() private void SetupTumbler(IEntityManager entManager, MapId mapId) { var physics = entManager.System(); - var fixtures = entManager.System(); var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); var ground = entManager.AddComponent(groundUid); var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0)); - fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground); + physics.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground); var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10)); - fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground); + physics.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground); var xs = new[] { @@ -84,7 +83,7 @@ private void SetupTumbler(IEntityManager entManager, MapId mapId) shape = new PolygonShape(); shape.SetAsBox(0.5f, 0.5f); physics.SetFixedRotation(boxUid, false, body: box); - fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true), body: box); + physics.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true), body: box); physics.WakeBody(boxUid, body: box); physics.SetSleepingAllowed(boxUid, box, false); diff --git a/Robust.Benchmarks/Physics/CircleStackBenchmark.cs b/Robust.Benchmarks/Physics/CircleStackBenchmark.cs index eb79a6b9a53..530284db19a 100644 --- a/Robust.Benchmarks/Physics/CircleStackBenchmark.cs +++ b/Robust.Benchmarks/Physics/CircleStackBenchmark.cs @@ -46,16 +46,15 @@ public void CircleStack() private void SetupTumbler(IEntityManager entManager, MapId mapId) { var physics = entManager.System(); - var fixtures = entManager.System(); var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); var ground = entManager.AddComponent(groundUid); var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0)); - fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground); + physics.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground); var vertical = new EdgeShape(new Vector2(20, 0), new Vector2(20, 20)); - fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground); + physics.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground); var xs = new[] { @@ -81,7 +80,7 @@ private void SetupTumbler(IEntityManager entManager, MapId mapId) physics.SetFixedRotation(boxUid, false, body: box); // TODO: Need to detect shape and work out if we need to use fixedrotation - fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f)); + physics.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f)); physics.WakeBody(boxUid, body: box); physics.SetSleepingAllowed(boxUid, box, false); } diff --git a/Robust.Benchmarks/Physics/PyramidBenchmark.cs b/Robust.Benchmarks/Physics/PyramidBenchmark.cs index 505176dfa65..58798e0d367 100644 --- a/Robust.Benchmarks/Physics/PyramidBenchmark.cs +++ b/Robust.Benchmarks/Physics/PyramidBenchmark.cs @@ -50,12 +50,11 @@ private void SetupTumbler(IEntityManager entManager, MapId mapId) // Setup ground var physics = entManager.System(); - var fixtures = entManager.System(); var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); var ground = entManager.AddComponent(groundUid); var horizontal = new EdgeShape(new Vector2(40, 0), new Vector2(-40, 0)); - fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground); + physics.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground); physics.WakeBody(groundUid, body: ground); // Setup boxes @@ -78,7 +77,7 @@ private void SetupTumbler(IEntityManager entManager, MapId mapId) var box = entManager.AddComponent(boxUid); physics.SetBodyType(boxUid, BodyType.Dynamic, body: box); - fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f), body: box); + physics.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f), body: box); y += deltaY; physics.WakeBody(boxUid, body: box); diff --git a/Robust.Benchmarks/Physics/TumblerBenchmark.cs b/Robust.Benchmarks/Physics/TumblerBenchmark.cs index f2e44185be6..18822334291 100644 --- a/Robust.Benchmarks/Physics/TumblerBenchmark.cs +++ b/Robust.Benchmarks/Physics/TumblerBenchmark.cs @@ -25,7 +25,6 @@ public void Setup() var entManager = _sim.Resolve(); var physics = entManager.System(); - var fixtures = entManager.System(); entManager.System().CreateMap(out var mapId); SetupTumbler(entManager, mapId); @@ -38,7 +37,7 @@ public void Setup() physics.SetFixedRotation(boxUid, false, body: box); var shape = new PolygonShape(); shape.SetAsBox(0.125f, 0.125f); - fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 0.0625f), body: box); + physics.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 0.0625f), body: box); physics.WakeBody(boxUid, body: box); physics.SetSleepingAllowed(boxUid, box, false); } @@ -58,14 +57,13 @@ public void Tumbler() private void SetupTumbler(IEntityManager entManager, MapId mapId) { var physics = entManager.System(); - var fixtures = entManager.System(); var joints = entManager.System(); var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 0f, mapId)); var ground = entManager.AddComponent(groundUid); // Due to lookup changes fixtureless bodies are invalid, so var cShape = new PhysShapeCircle(1f); - fixtures.CreateFixture(groundUid, "fix1", new Fixture(cShape, 0, 0, false)); + physics.CreateFixture(groundUid, "fix1", new Fixture(cShape, 0, 0, false)); var bodyUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId)); var body = entManager.AddComponent(bodyUid); @@ -78,19 +76,19 @@ private void SetupTumbler(IEntityManager entManager, MapId mapId) // TODO: Box2D just deref, bleh shape structs someday var shape1 = new PolygonShape(); shape1.SetAsBox(0.5f, 10.0f, new Vector2(10.0f, 0.0f), 0.0f); - fixtures.CreateFixture(bodyUid, "fix1", new Fixture(shape1, 2, 0, true, 20f)); + physics.CreateFixture(bodyUid, "fix1", new Fixture(shape1, 2, 0, true, 20f)); var shape2 = new PolygonShape(); shape2.SetAsBox(0.5f, 10.0f, new Vector2(-10.0f, 0.0f), 0f); - fixtures.CreateFixture(bodyUid, "fix2", new Fixture(shape2, 2, 0, true, 20f)); + physics.CreateFixture(bodyUid, "fix2", new Fixture(shape2, 2, 0, true, 20f)); var shape3 = new PolygonShape(); shape3.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, 10.0f), 0f); - fixtures.CreateFixture(bodyUid, "fix3", new Fixture(shape3, 2, 0, true, 20f)); + physics.CreateFixture(bodyUid, "fix3", new Fixture(shape3, 2, 0, true, 20f)); var shape4 = new PolygonShape(); shape4.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, -10.0f), 0f); - fixtures.CreateFixture(bodyUid, "fix4", new Fixture(shape4, 2, 0, true, 20f)); + physics.CreateFixture(bodyUid, "fix4", new Fixture(shape4, 2, 0, true, 20f)); physics.WakeBody(groundUid, body: ground); physics.WakeBody(bodyUid, body: body); diff --git a/Robust.Client/Debugging/DebugPhysicsSystem.cs b/Robust.Client/Debugging/DebugPhysicsSystem.cs index c916c264648..74e25bb9940 100644 --- a/Robust.Client/Debugging/DebugPhysicsSystem.cs +++ b/Robust.Client/Debugging/DebugPhysicsSystem.cs @@ -250,7 +250,7 @@ private void DrawWorld(DrawingHandleWorld worldHandle, OverlayDrawArgs args) const float AlphaModifier = 0.2f; - foreach (var fixture in _entityManager.GetComponent(physBody).Fixtures.Values) + foreach (var fixture in _entityManager.GetComponent(physBody).Fixtures.Values) { // Invalid shape - Box2D doesn't check for IsSensor but we will for sanity. if (comp.BodyType == BodyType.Dynamic && fixture.Density == 0f && fixture.Hard) @@ -315,7 +315,7 @@ private void DrawWorld(DrawingHandleWorld worldHandle, OverlayDrawArgs args) const float AlphaModifier = 0.2f; Box2? aabb = null; - foreach (var fixture in _entityManager.GetComponent(physBody).Fixtures.Values) + foreach (var fixture in _entityManager.GetComponent(physBody).Fixtures.Values) { for (var i = 0; i < fixture.Shape.ChildCount; i++) { @@ -443,10 +443,10 @@ private void DrawScreen(DrawingHandleScreen screenHandle, OverlayDrawArgs args) foreach (var ent in _lookup.GetEntitiesIntersecting(mapPos, flags)) { - if (!_entityManager.TryGetComponent(ent, out var managerB)) + if (!_entityManager.TryGetComponent(ent, out var managerB)) continue; - if (_physicsSystem.TryGetDistance(player.Value, ent, out var distance, managerB: managerB)) + if (_physicsSystem.TryGetDistance(player.Value, ent, out var distance, bodyB: managerB)) { screenHandle.DrawString(_font, mousePos.Position, $"Ent: {_entityManager.ToPrettyString(ent)}\nDistance: {distance:0.00}"); break; diff --git a/Robust.Client/GameObjects/EntitySystems/GridChunkBoundsDebugSystem.cs b/Robust.Client/GameObjects/EntitySystems/GridChunkBoundsDebugSystem.cs index 15d469ef76f..4a1f63d0fd4 100644 --- a/Robust.Client/GameObjects/EntitySystems/GridChunkBoundsDebugSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/GridChunkBoundsDebugSystem.cs @@ -9,6 +9,7 @@ using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Components; using Robust.Shared.Utility; namespace Robust.Client.GameObjects @@ -82,7 +83,7 @@ protected internal override void Draw(in OverlayDrawArgs args) var viewport = args.WorldBounds; var worldHandle = args.WorldHandle; - var fixturesQuery = _entityManager.GetEntityQuery(); + var physicsQuery = _entityManager.GetEntityQuery(); _grids.Clear(); _mapManager.FindGridsIntersecting(currentMap, viewport, ref _grids); foreach (var grid in _grids) @@ -90,7 +91,7 @@ protected internal override void Draw(in OverlayDrawArgs args) var worldMatrix = _transformSystem.GetWorldMatrix(grid); worldHandle.SetTransform(worldMatrix); var transform = new Transform(Vector2.Zero, Angle.Zero); - var fixtures = fixturesQuery.Comp(grid.Owner); + var fixtures = physicsQuery.Comp(grid.Owner); var chunkEnumerator = _mapSystem.GetMapChunks(grid.Owner, grid.Comp, viewport); diff --git a/Robust.Client/GameStates/NetGraphOverlay.cs b/Robust.Client/GameStates/NetGraphOverlay.cs index 4622b201801..8734d7b8136 100644 --- a/Robust.Client/GameStates/NetGraphOverlay.cs +++ b/Robust.Client/GameStates/NetGraphOverlay.cs @@ -85,6 +85,9 @@ private void HandleGameStateApplied(GameStateAppliedArgs args) if (entStates.HasContents) { var sb = new StringBuilder(); + + sb.Append($"\nSize: {args.AppliedState.PayloadSize}"); + foreach (var entState in entStates.Span) { var uid = _entManager.GetEntity(entState.NetEntity); @@ -102,7 +105,7 @@ private void HandleGameStateApplied(GameStateAppliedArgs args) foreach (var compChange in entState.ComponentChanges.Span) { var registration = _componentFactory.GetRegistration(compChange.NetID); - sb.Append($"\n [{compChange.NetID}:{registration.Name}"); + sb.Append($"\n n[{compChange.NetID}:{registration.Name}"); if (compChange.State is not null) sb.Append($"\n STATE:{compChange.State.GetType().Name}"); diff --git a/Robust.Server/Console/Commands/ScaleCommand.cs b/Robust.Server/Console/Commands/ScaleCommand.cs index 601e775c3c0..06ce147662a 100644 --- a/Robust.Server/Console/Commands/ScaleCommand.cs +++ b/Robust.Server/Console/Commands/ScaleCommand.cs @@ -7,6 +7,7 @@ using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; namespace Robust.Server.Console.Commands; @@ -58,9 +59,9 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) appearance.SetData(uid, ScaleVisuals.Scale, oldScale * scale, appearanceComponent); - if (_entityManager.TryGetComponent(uid, out FixturesComponent? manager)) + if (_entityManager.TryGetComponent(uid, out PhysicsComponent? body)) { - foreach (var (id, fixture) in manager.Fixtures) + foreach (var (id, fixture) in body.Fixtures) { switch (fixture.Shape) { @@ -70,10 +71,10 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) edge.Vertex0 * scale, edge.Vertex1 * scale, edge.Vertex2 * scale, - edge.Vertex3 * scale, manager); + edge.Vertex3 * scale, body); break; case PhysShapeCircle circle: - physics.SetPositionRadius(uid, id, fixture, circle, circle.Position * scale, circle.Radius * scale, manager); + physics.SetPositionRadius(uid, id, fixture, circle, circle.Position * scale, circle.Radius * scale, body); break; case PolygonShape poly: var verts = poly.Vertices; @@ -83,7 +84,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) verts[i] *= scale; } - physics.SetVertices(uid, id, fixture, poly, verts, manager); + physics.SetVertices(uid, id, fixture, poly, verts, body); break; default: throw new NotImplementedException(); diff --git a/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs b/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs index 5dde1eaed7c..ca9ac1e8714 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs @@ -10,6 +10,7 @@ using Robust.Shared.Physics; using Robust.Shared.Physics.Collision; using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Shapes; using Robust.Shared.Physics.Systems; @@ -87,11 +88,10 @@ private void AddEntitiesIntersecting(MapId mapId, var state = new EntityQueryState(intersecting, shape, shapeTransform, - _fixtures, this, _physics, _manifoldManager, - _fixturesQuery, + _physicsQuery, flags); // Need to include maps @@ -128,11 +128,10 @@ private void AddEntitiesIntersecting( intersecting, shape, localShapeTransform, - _fixtures, this, _physics, _manifoldManager, - _fixturesQuery, + _physicsQuery, flags); if ((flags & LookupFlags.Dynamic) != 0x0) @@ -217,7 +216,7 @@ static bool SundriesQuery(ref EntityQueryState state, in EntityUid value) return true; } - if (state.Fixtures.TestPoint(state.Shape, state.Transform, intersectingTransform.Position)) + if (state.Physics.TestPoint(state.Shape, state.Transform, intersectingTransform.Position)) state.Intersecting.Add(value); return true; @@ -238,11 +237,10 @@ private bool AnyEntitiesIntersecting(MapId mapId, ignored, shape, shapeTransform, - _fixtures, this, _physics, _manifoldManager, - _fixturesQuery, + _physicsQuery, flags); // Need to include maps @@ -287,11 +285,10 @@ private bool AnyEntitiesIntersecting(EntityUid lookupUid, ignored, shape, shapeTransform, - _fixtures, this, _physics, _manifoldManager, - _fixturesQuery, + _physicsQuery, flags); if ((flags & LookupFlags.Dynamic) != 0x0) @@ -391,7 +388,7 @@ static bool SundriesQuery(ref AnyEntityQueryState state, in EntityUid value) return true; } - if (state.Fixtures.TestPoint(state.Shape, state.Transform, intersectingTransform.Position)) + if (state.Physics.TestPoint(state.Shape, state.Transform, intersectingTransform.Position)) { state.Found = true; return false; @@ -548,13 +545,13 @@ public void GetEntitiesIntersecting(EntityUid uid, HashSet intersecti var existing = intersecting.Contains(uid); var transform = new Transform(worldPos, worldRot); - var state = (uid, transform, intersecting, _fixturesQuery, this, _physics, flags); + var state = (uid, transform, intersecting, _physicsQuery, this, _physics, flags); // Unfortuantely I can't think of a way to de-dupe this with the other ones as it's slightly different. _mapManager.FindGridsIntersecting(mapId, worldAABB, ref state, static (EntityUid gridUid, MapGridComponent grid, ref (EntityUid entity, Transform transform, HashSet intersecting, - EntityQuery fixturesQuery, EntityLookupSystem lookup, SharedPhysicsSystem physics, LookupFlags flags) state) => + EntityQuery fixturesQuery, EntityLookupSystem lookup, SharedPhysicsSystem physics, LookupFlags flags) state) => { EntityIntersectingQuery(gridUid, state); @@ -573,7 +570,7 @@ public void GetEntitiesIntersecting(EntityUid uid, HashSet intersecti return; static void EntityIntersectingQuery(EntityUid lookupUid, (EntityUid entity, Transform shapeTransform, HashSet intersecting, - EntityQuery fixturesQuery, EntityLookupSystem lookup, SharedPhysicsSystem physics, LookupFlags flags) state) + EntityQuery fixturesQuery, EntityLookupSystem lookup, SharedPhysicsSystem physics, LookupFlags flags) state) { var localTransform = state.physics.GetRelativePhysicsTransform(state.shapeTransform, lookupUid); @@ -837,11 +834,10 @@ private record struct AnyEntityQueryState( EntityUid? Ignored, IPhysShape Shape, Transform Transform, - FixtureSystem Fixtures, EntityLookupSystem Lookup, SharedPhysicsSystem Physics, IManifoldManager Manifolds, - EntityQuery FixturesQuery, + EntityQuery FixturesQuery, LookupFlags Flags ); @@ -849,11 +845,10 @@ private readonly record struct EntityQueryState( HashSet Intersecting, IPhysShape Shape, Transform Transform, - FixtureSystem Fixtures, EntityLookupSystem Lookup, SharedPhysicsSystem Physics, IManifoldManager Manifolds, - EntityQuery FixturesQuery, + EntityQuery FixturesQuery, LookupFlags Flags ); } diff --git a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs index 634c1774199..d5c234d62b1 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs @@ -9,6 +9,7 @@ using Robust.Shared.Physics; using Robust.Shared.Physics.Collision; using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Shapes; using Robust.Shared.Physics.Systems; @@ -79,7 +80,7 @@ private bool IsIntersecting(MapId mapId, EntityUid uid, TransformComponent xform return false; } - if (_fixturesQuery.TryGetComponent(uid, out var fixtures)) + if (_physicsQuery.TryGetComponent(uid, out var fixtures)) { var transform = new Transform(entPos, entRot); bool anyFixture = false; @@ -104,7 +105,7 @@ private bool IsIntersecting(MapId mapId, EntityUid uid, TransformComponent xform return false; } - if (!_fixtures.TestPoint(shape, shapeTransform, entPos)) + if (!_physics.TestPoint(shape, shapeTransform, entPos)) return false; return true; @@ -143,11 +144,10 @@ private void AddEntitiesIntersecting( intersecting, shape, localTransform, - _fixtures, _physics, _manifoldManager, query, - _fixturesQuery, + _physicsQuery, (flags & LookupFlags.Sensors) != 0, (flags & LookupFlags.Approximate) != 0x0 ); @@ -232,7 +232,7 @@ static bool SundriesQuery(ref QueryState state, in EntityUid value) return true; } - if (state.Fixtures.TestPoint(state.Shape, state.Transform, intersectingTransform.Position)) + if (state.Physics.TestPoint(state.Shape, state.Transform, intersectingTransform.Position)) state.Intersecting.Add((value, comp)); return true; @@ -279,11 +279,10 @@ private bool AnyComponentsIntersecting( ignored, shape, shapeTransform, - _fixtures, _physics, _manifoldManager, query, - _fixturesQuery, + _physicsQuery, flags); if ((flags & LookupFlags.Dynamic) != 0x0) @@ -385,7 +384,7 @@ static bool SundriesQuery(ref AnyQueryState state, in EntityUid value) return true; } - if (state.Fixtures.TestPoint(state.Shape, state.Transform, intersectingTransform.Position)) + if (state.Physics.TestPoint(state.Shape, state.Transform, intersectingTransform.Position)) { state.Found = true; return false; @@ -844,11 +843,10 @@ private record struct AnyQueryState( EntityUid? Ignored, IPhysShape Shape, Transform Transform, - FixtureSystem Fixtures, SharedPhysicsSystem Physics, IManifoldManager Manifolds, EntityQuery Query, - EntityQuery FixturesQuery, + EntityQuery FixturesQuery, LookupFlags Flags ) where T : IComponent; @@ -856,11 +854,10 @@ private readonly record struct QueryState( HashSet> Intersecting, IPhysShape Shape, Transform Transform, - FixtureSystem Fixtures, SharedPhysicsSystem Physics, IManifoldManager Manifolds, EntityQuery Query, - EntityQuery FixturesQuery, + EntityQuery FixturesQuery, bool Sensors, bool Approximate ) where T : IComponent; diff --git a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs index 9d49df23b96..b520796f76a 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs @@ -77,14 +77,12 @@ public sealed partial class EntityLookupSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly INetManager _netMan = default!; [Dependency] private readonly SharedContainerSystem _container = default!; - [Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; private EntityQuery _broadQuery; private EntityQuery _containerQuery; - private EntityQuery _fixturesQuery; private EntityQuery _gridQuery; private EntityQuery _metaQuery; @@ -114,7 +112,6 @@ public override void Initialize() _broadQuery = GetEntityQuery(); _containerQuery = GetEntityQuery(); - _fixturesQuery = GetEntityQuery(); _gridQuery = GetEntityQuery(); _metaQuery = GetEntityQuery(); _physicsQuery = GetEntityQuery(); @@ -176,14 +173,14 @@ private void RemoveChildrenFromTerminatingBroadphase(TransformComponent xform, DebugTools.Assert(childXform.Broadphase.Value.Uid == component.Owner); DebugTools.Assert(!_mapManager.IsGrid(child)); - if (childXform.Broadphase.Value.CanCollide && _fixturesQuery.TryGetComponent(child, out var fixtures)) + if (childXform.Broadphase.Value.CanCollide && _physicsQuery.TryGetComponent(child, out var body)) { if (map == null) _mapQuery.TryGetComponent(childXform.Broadphase.Value.PhysicsMap, out map); DebugTools.Assert(map == null || childXform.Broadphase.Value.PhysicsMap == map.Owner); var tree = childXform.Broadphase.Value.Static ? component.StaticTree : component.DynamicTree; - foreach (var fixture in fixtures.Fixtures.Values) + foreach (var fixture in body.Fixtures.Values) { DestroyProxies(fixture, tree, map); } @@ -261,9 +258,9 @@ private void InitializeChild( if (!_broadQuery.TryGetComponent(xform.Broadphase.Value.Uid, out var oldBroadphase)) { DebugTools.Assert("Encountered deleted broadphase."); - if (_fixturesQuery.TryGetComponent(child, out var fixtures)) + if (_physicsQuery.TryGetComponent(child, out var body)) { - foreach (var fixture in fixtures.Fixtures.Values) + foreach (var fixture in body.Fixtures.Values) { fixture.ProxyCount = 0; fixture.Proxies = Array.Empty(); @@ -391,26 +388,25 @@ private void UpdatePhysicsBroadphase(EntityUid uid, TransformComponent xform, Ph return; // broadphase probably got deleted. // remove from the old broadphase - var fixtures = Comp(uid); if (old.CanCollide) { _mapQuery.TryGetComponent(old.PhysicsMap, out var physicsMap); - RemoveBroadTree(broadphase, fixtures, old.Static, physicsMap); + RemoveBroadTree(broadphase, body, old.Static, physicsMap); } else (old.Static ? broadphase.StaticSundriesTree : broadphase.SundriesTree).Remove(uid); // Add to new broadphase if (body.CanCollide) - AddPhysicsTree(uid, old.Uid, broadphase, xform, body, fixtures); + AddPhysicsTree(uid, old.Uid, broadphase, xform, body); else AddOrUpdateSundriesTree(old.Uid, broadphase, uid, xform, body.BodyType == BodyType.Static); } - private void RemoveBroadTree(BroadphaseComponent lookup, FixturesComponent manager, bool staticBody, PhysicsMapComponent? map) + private void RemoveBroadTree(BroadphaseComponent lookup, PhysicsComponent body, bool staticBody, PhysicsMapComponent? map) { var tree = staticBody ? lookup.StaticTree : lookup.DynamicTree; - foreach (var fixture in manager.Fixtures.Values) + foreach (var fixture in body.Fixtures.Values) { DestroyProxies(fixture, tree, map); } @@ -430,7 +426,7 @@ internal void DestroyProxies(Fixture fixture, IBroadPhase tree, PhysicsMapCompon fixture.Proxies = Array.Empty(); } - private void AddPhysicsTree(EntityUid uid, EntityUid broadUid, BroadphaseComponent broadphase, TransformComponent xform, PhysicsComponent body, FixturesComponent fixtures) + private void AddPhysicsTree(EntityUid uid, EntityUid broadUid, BroadphaseComponent broadphase, TransformComponent xform, PhysicsComponent body) { var broadphaseXform = _xformQuery.GetComponent(broadUid); @@ -440,7 +436,7 @@ private void AddPhysicsTree(EntityUid uid, EntityUid broadUid, BroadphaseCompone if (!_mapQuery.TryGetComponent(broadphaseXform.MapUid, out var physMap)) throw new InvalidOperationException($"Physics Broadphase is missing physics map. {ToPrettyString(broadUid)}"); - AddOrUpdatePhysicsTree(uid, broadUid, broadphase, broadphaseXform, physMap, xform, body, fixtures); + AddOrUpdatePhysicsTree(uid, broadUid, broadphase, broadphaseXform, physMap, xform, body); } private void AddOrUpdatePhysicsTree( @@ -450,8 +446,7 @@ private void AddOrUpdatePhysicsTree( TransformComponent broadphaseXform, PhysicsMapComponent physicsMap, TransformComponent xform, - PhysicsComponent body, - FixturesComponent manager) + PhysicsComponent body) { DebugTools.Assert(!_container.IsEntityOrParentInContainer(body.Owner, null, xform)); DebugTools.Assert(xform.Broadphase == null || xform.Broadphase == new BroadphaseData(broadphase.Owner, physicsMap.Owner, body.CanCollide, body.BodyType == BodyType.Static)); @@ -467,7 +462,7 @@ private void AddOrUpdatePhysicsTree( // TODO BROADPHASE PARENTING this just assumes local = world var broadphaseTransform = new Transform(Vector2.Transform(mapTransform.Position, broadphaseXform.InvLocalMatrix), mapTransform.Quaternion2D.Angle - broadphaseXform.LocalRotation); - foreach (var (id, fixture) in manager.Fixtures) + foreach (var (id, fixture) in body.Fixtures) { AddOrMoveProxies(uid, id, fixture, body, tree, broadphaseTransform, mapTransform, physicsMap.MoveBuffer); } @@ -596,15 +591,17 @@ private void RecursiveOnGridChangedMap( DebugTools.Assert(_netMan.IsClient || !xform.Broadphase.Value.PhysicsMap.IsValid() || xform.Broadphase.Value.PhysicsMap == oldMap); xform.Broadphase = xform.Broadphase.Value with { PhysicsMap = newMap }; - if (!_fixturesQuery.TryGetComponent(uid, out var fixtures)) + if (!_physicsQuery.TryGetComponent(uid, out var fixtures)) return; if (oldBuffer != null) { foreach (var fix in fixtures.Fixtures.Values) - foreach (var prox in fix.Proxies) { - oldBuffer.Remove(prox); + foreach (var prox in fix.Proxies) + { + oldBuffer.Remove(prox); + } } } @@ -643,9 +640,9 @@ private void UpdateParent(EntityUid uid, TransformComponent xform) DebugTools.Assert("Encountered deleted broadphase."); // broadphase was probably deleted. - if (_fixturesQuery.TryGetComponent(uid, out var fixtures)) + if (_physicsQuery.TryGetComponent(uid, out var body)) { - foreach (var fixture in fixtures.Fixtures.Values) + foreach (var fixture in body.Fixtures.Values) { fixture.ProxyCount = 0; fixture.Proxies = Array.Empty(); @@ -757,7 +754,7 @@ private void AddOrUpdateEntityTree( } else { - AddOrUpdatePhysicsTree(uid, broadUid, broadphase, broadphaseXform, physicsMap, xform, body, _fixturesQuery.GetComponent(uid)); + AddOrUpdatePhysicsTree(uid, broadUid, broadphase, broadphaseXform, physicsMap, xform, body); } if (xform.ChildCount == 0 || !recursive) @@ -841,7 +838,7 @@ private void RemoveFromEntityTree( if (old.CanCollide) { DebugTools.Assert(old.PhysicsMap == (physicsMap?.Owner ?? default)); - RemoveBroadTree(broadphase, _fixturesQuery.GetComponent(uid), old.Static, physicsMap); + RemoveBroadTree(broadphase, _physicsQuery.GetComponent(uid), old.Static, physicsMap); } else if (old.Static) broadphase.StaticSundriesTree.Remove(uid); @@ -874,9 +871,9 @@ public bool TryGetCurrentBroadphase(TransformComponent xform, [NotNullWhen(true) // broadphase was probably deleted DebugTools.Assert("Encountered deleted broadphase."); - if (_fixturesQuery.TryGetComponent(xform.Owner, out FixturesComponent? fixtures)) + if (_physicsQuery.TryGetComponent(xform.Owner, out PhysicsComponent? body)) { - foreach (var fixture in fixtures.Fixtures.Values) + foreach (var fixture in body.Fixtures.Values) { fixture.ProxyCount = 0; fixture.Proxies = Array.Empty(); @@ -954,13 +951,13 @@ public Box2 GetAABB(EntityUid uid, Vector2 position, Angle angle, TransformCompo /// public Box2 GetAABBNoContainer(EntityUid uid, Vector2 position, Angle angle) { - if (_fixturesQuery.TryGetComponent(uid, out var fixtures)) + if (_physicsQuery.TryGetComponent(uid, out var body)) { var transform = new Transform(position, angle); var bounds = new Box2(transform.Position, transform.Position); // TODO cache this to speed up entity lookups & tree updating - foreach (var fixture in fixtures.Fixtures.Values) + foreach (var fixture in body.Fixtures.Values) { for (var i = 0; i < fixture.Shape.ChildCount; i++) { diff --git a/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs b/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs index 0de5870a86c..9cd19a5602f 100644 --- a/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs @@ -22,7 +22,7 @@ namespace Robust.Shared.GameObjects { public abstract class SharedGridFixtureSystem : EntitySystem { - [Dependency] private readonly FixtureSystem _fixtures = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; @@ -76,12 +76,6 @@ internal void RegenerateCollision( return; } - if (!EntityManager.TryGetComponent(uid, out FixturesComponent? manager)) - { - Log.Error($"Trying to regenerate collision for {uid} that doesn't have {nameof(manager)}"); - return; - } - if (!EntityManager.TryGetComponent(uid, out TransformComponent? xform)) { Log.Error($"Trying to regenerate collision for {uid} that doesn't have {nameof(TransformComponent)}"); @@ -92,16 +86,16 @@ internal void RegenerateCollision( foreach (var (chunk, rectangles) in mapChunks) { - UpdateFixture(uid, chunk, rectangles, body, manager, xform); + UpdateFixture(uid, chunk, rectangles, body, xform); foreach (var id in chunk.Fixtures) { - fixtures[id] = manager.Fixtures[id]; + fixtures[id] = body.Fixtures[id]; } } EntityManager.EventBus.RaiseLocalEvent(uid,new GridFixtureChangeEvent {NewFixtures = fixtures}, true); - _fixtures.FixtureUpdate(uid, manager: manager, body: body); + _physics.FixtureUpdate(uid, body: body); CheckSplit(uid, mapChunks, removedChunks); } @@ -111,7 +105,7 @@ internal virtual void CheckSplit(EntityUid gridEuid, Dictionary rectangles) {} - private bool UpdateFixture(EntityUid uid, MapChunk chunk, List rectangles, PhysicsComponent body, FixturesComponent manager, TransformComponent xform) + private bool UpdateFixture(EntityUid uid, MapChunk chunk, List rectangles, PhysicsComponent body, TransformComponent xform) { var origin = chunk.Indices * chunk.ChunkSize; @@ -159,7 +153,7 @@ private bool UpdateFixture(EntityUid uid, MapChunk chunk, List rectangles foreach (var oldId in chunk.Fixtures) { - var oldFixture = manager.Fixtures[oldId]; + var oldFixture = body.Fixtures[oldId]; var existing = false; // Handle deleted / updated fixtures @@ -186,7 +180,7 @@ private bool UpdateFixture(EntityUid uid, MapChunk chunk, List rectangles // TODO add a DestroyFixture() override that takes in a list. // reduced broadphase lookups chunk.Fixtures.Remove(id); - _fixtures.DestroyFixture(uid, id, fixture, false, body: body, manager: manager, xform: xform); + _physics.DestroyFixture(uid, id, fixture, false, body: body, xform: xform); } if (newFixtures.Count > 0 || toRemove.Count > 0) @@ -198,7 +192,7 @@ private bool UpdateFixture(EntityUid uid, MapChunk chunk, List rectangles foreach (var (id, fixture) in newFixtures.Span) { chunk.Fixtures.Add(id); - var existingFixture = _fixtures.GetFixtureOrNull(uid, id, manager: manager); + var existingFixture = _physics.GetFixtureOrNull(uid, id, body: body); // Check if it's the same (otherwise remove anyway). if (existingFixture?.Shape is PolygonShape poly && poly.EqualsApprox((PolygonShape) fixture.Shape)) @@ -207,7 +201,7 @@ private bool UpdateFixture(EntityUid uid, MapChunk chunk, List rectangles continue; } - _fixtures.CreateFixture(uid, id, fixture, false, manager, body, xform); + _physics.CreateFixture(uid, id, fixture, false, body, xform); } return updated; diff --git a/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs b/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs index 0fade481c5e..4c2b6c2afb1 100644 --- a/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs +++ b/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs @@ -508,7 +508,7 @@ private void OnGridInit(EntityUid uid, MapGridComponent component, ComponentInit if (TryComp(xform.MapUid, out var gridTree)) { - var proxy = gridTree.Tree.CreateProxy(in aabb, (uid, _fixturesQuery.Comp(uid), component)); + var proxy = gridTree.Tree.CreateProxy(in aabb, (uid, _physicsQuery.Comp(uid), component)); DebugTools.Assert(component.MapProxy == DynamicTree.Proxy.Free); component.MapProxy = proxy; } @@ -562,7 +562,7 @@ private void AddGrid(EntityUid uid, MapGridComponent grid) if (TryComp(xform.MapUid, out var gridTree)) { - var proxy = gridTree.Tree.CreateProxy(in aabb, (uid, _fixturesQuery.Comp(uid), grid)); + var proxy = gridTree.Tree.CreateProxy(in aabb, (uid, _physicsQuery.Comp(uid), grid)); DebugTools.Assert(grid.MapProxy == DynamicTree.Proxy.Free); grid.MapProxy = proxy; } @@ -638,13 +638,12 @@ internal void RegenerateCollision(EntityUid uid, MapGridComponent grid, IReadOnl { // Gone. Reduced to atoms // Need to do this before RemoveChunk because it clears fixtures. - FixturesComponent? manager = null; PhysicsComponent? body = null; TransformComponent? xform = null; foreach (var id in mapChunk.Fixtures) { - _fixtures.DestroyFixture(uid, id, false, manager: manager, body: body, xform: xform); + _physics.DestroyFixture(uid, id, false, body: body, xform: xform); } RemoveChunk(uid, grid, mapChunk.Indices); diff --git a/Robust.Shared/GameObjects/Systems/SharedMapSystem.cs b/Robust.Shared/GameObjects/Systems/SharedMapSystem.cs index f87a98e33cb..9cdb6be8c8b 100644 --- a/Robust.Shared/GameObjects/Systems/SharedMapSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedMapSystem.cs @@ -6,6 +6,7 @@ using Robust.Shared.Maths; using Robust.Shared.Network; using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; using Robust.Shared.Timing; @@ -18,16 +19,15 @@ public abstract partial class SharedMapSystem : EntitySystem [Dependency] protected readonly IMapManager MapManager = default!; [Dependency] private readonly IMapManagerInternal _mapInternal = default!; [Dependency] private readonly INetManager _netManager = default!; - [Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly IComponentFactory _factory = default!; [Dependency] private readonly MetaDataSystem _meta = default!; - private EntityQuery _fixturesQuery; private EntityQuery _mapQuery; private EntityQuery _gridQuery; private EntityQuery _metaQuery; + private EntityQuery _physicsQuery; private EntityQuery _xformQuery; internal Dictionary Maps { get; } = new(); @@ -36,9 +36,9 @@ public override void Initialize() { base.Initialize(); - _fixturesQuery = GetEntityQuery(); _mapQuery = GetEntityQuery(); _gridQuery = GetEntityQuery(); + _physicsQuery = GetEntityQuery(); _metaQuery = GetEntityQuery(); _xformQuery = GetEntityQuery(); diff --git a/Robust.Shared/Map/Components/GridTreeComponent.cs b/Robust.Shared/Map/Components/GridTreeComponent.cs index 8a38b1b2b50..e66b169cfa9 100644 --- a/Robust.Shared/Map/Components/GridTreeComponent.cs +++ b/Robust.Shared/Map/Components/GridTreeComponent.cs @@ -1,6 +1,7 @@ using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; using Robust.Shared.ViewVariables; namespace Robust.Shared.Map.Components; @@ -9,5 +10,5 @@ namespace Robust.Shared.Map.Components; public sealed partial class GridTreeComponent : Component { [ViewVariables] - public readonly B2DynamicTree<(EntityUid Uid, FixturesComponent Fixtures, MapGridComponent Grid)> Tree = new(); + public readonly B2DynamicTree<(EntityUid Uid, PhysicsComponent Fixtures, MapGridComponent Grid)> Tree = new(); } diff --git a/Robust.Shared/Map/MapManager.Queries.cs b/Robust.Shared/Map/MapManager.Queries.cs index e61e572747c..b5a0b2b1a01 100644 --- a/Robust.Shared/Map/MapManager.Queries.cs +++ b/Robust.Shared/Map/MapManager.Queries.cs @@ -8,6 +8,7 @@ using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Shapes; namespace Robust.Shared.Map; @@ -18,7 +19,7 @@ private bool IsIntersecting( ChunkEnumerator enumerator, IPhysShape shape, Transform shapeTransform, - Entity grid) + Entity grid) { var gridTransform = _physics.GetPhysicsTransform(grid); @@ -347,7 +348,7 @@ private readonly record struct GridQueryState( Box2 WorldAABB, IPhysShape Shape, Transform Transform, - B2DynamicTree<(EntityUid Uid, FixturesComponent Fixtures, MapGridComponent Grid)> Tree, + B2DynamicTree<(EntityUid Uid, PhysicsComponent Fixtures, MapGridComponent Grid)> Tree, SharedMapSystem MapSystem, MapManager MapManager, SharedTransformSystem TransformSystem, @@ -359,7 +360,7 @@ private record struct GridQueryState( Box2 WorldAABB, IPhysShape Shape, Transform Transform, - B2DynamicTree<(EntityUid Uid, FixturesComponent Fixtures, MapGridComponent Grid)> Tree, + B2DynamicTree<(EntityUid Uid, PhysicsComponent Fixtures, MapGridComponent Grid)> Tree, SharedMapSystem MapSystem, MapManager MapManager, SharedTransformSystem TransformSystem, diff --git a/Robust.Shared/Physics/Components/PhysicsComponent.Physics.cs b/Robust.Shared/Physics/Components/PhysicsComponent.Physics.cs index 79da7944bef..e70e0c0e577 100644 --- a/Robust.Shared/Physics/Components/PhysicsComponent.Physics.cs +++ b/Robust.Shared/Physics/Components/PhysicsComponent.Physics.cs @@ -26,7 +26,7 @@ using System.Numerics; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; -using Robust.Shared.Maths; +using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Dynamics.Contacts; using Robust.Shared.Physics.Systems; using Robust.Shared.Serialization.Manager.Attributes; @@ -56,6 +56,20 @@ public sealed partial class PhysicsComponent : Component, IComponentDelta [ViewVariables] public int ContactCount => Contacts.Count; + /// + /// Allows us to reference a specific fixture when we contain multiple + /// This is useful for stuff like slippery objects that might have a non-hard layer for mob collisions and + /// a hard layer for wall collisions. + /// + /// We can also use this for networking to make cross-referencing fixtures easier. + /// Won't call Dirty() by default + /// + /// + [ViewVariables(VVAccess.ReadWrite), DataField("fixtures", customTypeSerializer:typeof(FixtureSerializer))] + [NeverPushInheritance] + [Access(typeof(SharedPhysicsSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends + public Dictionary Fixtures = new(); + /// /// Linked-list of all of our contacts. /// @@ -103,19 +117,19 @@ public sealed partial class PhysicsComponent : Component, IComponentDelta /// /// This is useful for triggers or such to detect collision without actually causing a blockage. /// - [ViewVariables, Access(typeof(SharedPhysicsSystem), typeof(FixtureSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] + [ViewVariables, Access(typeof(SharedPhysicsSystem), typeof(SharedPhysicsSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] public bool Hard { get; internal set; } /// /// Bitmask of the collision layers this component is a part of. /// - [ViewVariables, Access(typeof(SharedPhysicsSystem), typeof(FixtureSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] + [ViewVariables, Access(typeof(SharedPhysicsSystem), typeof(SharedPhysicsSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] public int CollisionLayer { get; internal set; } /// /// Bitmask of the layers this component collides with. /// - [ViewVariables, Access(typeof(SharedPhysicsSystem), typeof(FixtureSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] + [ViewVariables, Access(typeof(SharedPhysicsSystem), typeof(SharedPhysicsSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] public int CollisionMask { get; internal set; } /// diff --git a/Robust.Shared/Physics/Components/PhysicsComponentState.cs b/Robust.Shared/Physics/Components/PhysicsComponentState.cs index 567476ea0fb..d1c6e988b28 100644 --- a/Robust.Shared/Physics/Components/PhysicsComponentState.cs +++ b/Robust.Shared/Physics/Components/PhysicsComponentState.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Numerics; using Robust.Shared.GameObjects; +using Robust.Shared.Physics.Dynamics; using Robust.Shared.Serialization; namespace Robust.Shared.Physics.Components; @@ -74,6 +75,8 @@ public sealed class PhysicsComponentState : IComponentState public Vector2 Force; public float Torque; + public Dictionary Fixtures = new(); + public PhysicsComponentState() {} public PhysicsComponentState(PhysicsComponentState existing) @@ -93,5 +96,10 @@ public PhysicsComponentState(PhysicsComponentState existing) Force = existing.Force; Torque = existing.Torque; + + foreach (var (key, value) in existing.Fixtures) + { + Fixtures[key] = new(value); + } } } diff --git a/Robust.Shared/Physics/Dynamics/Fixture.cs b/Robust.Shared/Physics/Dynamics/Fixture.cs index 3d95d65653c..93f0f2fbad9 100644 --- a/Robust.Shared/Physics/Dynamics/Fixture.cs +++ b/Robust.Shared/Physics/Dynamics/Fixture.cs @@ -65,14 +65,14 @@ public sealed partial class Fixture : IEquatable, ISerializationHooks /// /// Contact friction between 2 bodies. Not tile-friction for top-down. /// - [ViewVariables(VVAccess.ReadWrite), DataField("friction"), Access(typeof(SharedPhysicsSystem), typeof(FixtureSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] + [ViewVariables(VVAccess.ReadWrite), DataField("friction"), Access(typeof(SharedPhysicsSystem), typeof(SharedPhysicsSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] public float Friction = PhysicsConstants.DefaultContactFriction; /// /// AKA how much bounce there is on a collision. /// 0.0 for inelastic collision and 1.0 for elastic. /// - [ViewVariables(VVAccess.ReadWrite), DataField("restitution"), Access(typeof(SharedPhysicsSystem), typeof(FixtureSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] + [ViewVariables(VVAccess.ReadWrite), DataField("restitution"), Access(typeof(SharedPhysicsSystem), typeof(SharedPhysicsSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] public float Restitution = PhysicsConstants.DefaultRestitution; /// @@ -82,7 +82,7 @@ public sealed partial class Fixture : IEquatable, ISerializationHooks /// /// This is useful for triggers or such to detect collision without actually causing a blockage. /// - [ViewVariables(VVAccess.ReadWrite), DataField("hard"), Access(typeof(SharedPhysicsSystem), typeof(FixtureSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] + [ViewVariables(VVAccess.ReadWrite), DataField("hard"), Access(typeof(SharedPhysicsSystem), typeof(SharedPhysicsSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] public bool Hard = true; /// @@ -149,6 +149,12 @@ public Fixture() { } + public Fixture(Fixture fixture) + { + var thisFix = new Fixture(); + fixture.CopyTo(thisFix); + } + /// /// As a bunch of things aren't serialized we need to instantiate Fixture from an empty ctor and then copy values across. /// diff --git a/Robust.Shared/Physics/Events/MassChangedEvent.cs b/Robust.Shared/Physics/Events/MassChangedEvent.cs index b7324290466..2f51273c789 100644 --- a/Robust.Shared/Physics/Events/MassChangedEvent.cs +++ b/Robust.Shared/Physics/Events/MassChangedEvent.cs @@ -13,15 +13,15 @@ namespace Robust.Shared.Physics.Events; /// The old (local) center of mass of the physics body. [ByRefEvent] public readonly record struct MassDataChangedEvent( - Entity Entity, + Entity Entity, float OldMass, float OldInertia, Vector2 OldCenter ) { - public float NewMass => Entity.Comp1._mass; - public float NewInertia => Entity.Comp1._inertia; - public Vector2 NewCenter => Entity.Comp1._localCenter; + public float NewMass => Entity.Comp._mass; + public float NewInertia => Entity.Comp._inertia; + public Vector2 NewCenter => Entity.Comp._localCenter; public bool MassChanged => NewMass != OldMass; public bool InertiaChanged => NewInertia != OldInertia; public bool CenterChanged => NewCenter != OldCenter; diff --git a/Robust.Shared/Physics/FixturesComponent.cs b/Robust.Shared/Physics/FixturesComponent.cs deleted file mode 100644 index 9c6772b8f34..00000000000 --- a/Robust.Shared/Physics/FixturesComponent.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; -using Robust.Shared.Maths; -using Robust.Shared.Physics.Dynamics; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; - -namespace Robust.Shared.Physics -{ - /// - /// Storage for physics fixtures - /// - /// - /// In its own component to decrease physics comp state size significantly. - /// - [RegisterComponent, NetworkedComponent] - public sealed partial class FixturesComponent : Component - { - // This is a snowflake component whose main job is making physics states smaller for massive bodies - // (e.g. grids) - // Content generally shouldn't care about its existence. - - [ViewVariables] - public int FixtureCount => Fixtures.Count; - - /// - /// Allows us to reference a specific fixture when we contain multiple - /// This is useful for stuff like slippery objects that might have a non-hard layer for mob collisions and - /// a hard layer for wall collisions. - /// - /// We can also use this for networking to make cross-referencing fixtures easier. - /// Won't call Dirty() by default - /// - /// - [ViewVariables(VVAccess.ReadWrite), DataField("fixtures", customTypeSerializer:typeof(FixtureSerializer))] - [NeverPushInheritance] - [Access(typeof(FixtureSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends - public Dictionary Fixtures = new(); - } -} diff --git a/Robust.Shared/Physics/Systems/FixtureSystem.Shapes.cs b/Robust.Shared/Physics/Systems/FixtureSystem.Shapes.cs deleted file mode 100644 index c13315396cc..00000000000 --- a/Robust.Shared/Physics/Systems/FixtureSystem.Shapes.cs +++ /dev/null @@ -1,278 +0,0 @@ -using System; -using System.Numerics; -using Robust.Shared.Maths; -using Robust.Shared.Physics.Collision.Shapes; -using Robust.Shared.Physics.Dynamics; -using Robust.Shared.Physics.Shapes; -using Robust.Shared.Utility; - -namespace Robust.Shared.Physics.Systems -{ - public partial class FixtureSystem - { - /// - /// Tests whether a particular point is contained in the shape. - /// - public bool TestPoint(IPhysShape shape, Transform xform, Vector2 worldPoint) - { - switch (shape) - { - case ChainShape: - case EdgeShape: - return false; - case PhysShapeAabb aabb: - // TODO: When we get actual AABBs it will be a stupid ez check, - var polygon = (PolygonShape) aabb; - return TestPoint(polygon, xform, worldPoint); - case PhysShapeCircle circle: - var center = xform.Position + Physics.Transform.Mul(xform.Quaternion2D, circle.Position); - var distance = worldPoint - center; - return Vector2.Dot(distance, distance) <= circle.Radius * circle.Radius; - case PolygonShape poly: - { - var pLocal = Physics.Transform.MulT(xform.Quaternion2D, worldPoint - xform.Position); - - for (var i = 0; i < poly.VertexCount; i++) - { - var dot = Vector2.Dot(poly.Normals[i], pLocal - poly.Vertices[i]); - if (dot > 0f) return false; - } - - return true; - } - case Polygon poly: - { - var pLocal = Physics.Transform.MulT(xform.Quaternion2D, worldPoint - xform.Position); - - for (var i = 0; i < poly.VertexCount; i++) - { - var dot = Vector2.Dot(poly.Normals[i], pLocal - poly.Vertices[i]); - if (dot > 0f) return false; - } - - return true; - } - default: - throw new ArgumentOutOfRangeException($"No implemented TestPoint for {shape.GetType()}"); - } - } - - public static MassData GetMassData(IPhysShape shape, float density) - { - var data = new MassData(); - - // Box2D just calls fixture.GetMassData which just calls the shape method anyway soooo - // we can just cut out the middle-man - switch (shape) - { - case ChainShape: - data.Mass = 0f; - data.Center = Vector2.Zero; - data.I = 0f; - break; - case EdgeShape edge: - data.Mass = 0.0f; - data.Center = (edge.Vertex1 + edge.Vertex2) * 0.5f; - data.I = 0.0f; - break; - case PhysShapeCircle circle: - // massData->mass = density * b2_pi * m_radius * m_radius; - data.Center = circle.Position; - - // inertia about the local origin - data.I = data.Mass * (0.5f * circle.Radius * circle.Radius + Vector2.Dot(circle.Position, circle.Position)); - break; - case PhysShapeAabb aabb: - var polygon = (PolygonShape) aabb; - GetMassData(polygon, ref data, density); - break; - case PolygonShape poly: - // Polygon mass, centroid, and inertia. - // Let rho be the polygon density in mass per unit area. - // Then: - // mass = rho * int(dA) - // centroid.x = (1/mass) * rho * int(x * dA) - // centroid.y = (1/mass) * rho * int(y * dA) - // I = rho * int((x*x + y*y) * dA) - // - // We can compute these integrals by summing all the integrals - // for each triangle of the polygon. To evaluate the integral - // for a single triangle, we make a change of variables to - // the (u,v) coordinates of the triangle: - // x = x0 + e1x * u + e2x * v - // y = y0 + e1y * u + e2y * v - // where 0 <= u && 0 <= v && u + v <= 1. - // - // We integrate u from [0,1-v] and then v from [0,1]. - // We also need to use the Jacobian of the transformation: - // D = cross(e1, e2) - // - // Simplification: triangle centroid = (1/3) * (p1 + p2 + p3) - // - // The rest of the derivation is handled by computer algebra. - - var count = poly.VertexCount; - DebugTools.Assert(count >= 3); - - Vector2 center = new(0.0f, 0.0f); - var area = 0.0f; - var I = 0.0f; - - // Get a reference point for forming triangles. - // Use the first vertex to reduce round-off errors. - var s = poly.Vertices[0]; - - const float k_inv3 = 1.0f / 3.0f; - - for (var i = 0; i < count; ++i) - { - // Triangle vertices. - var e1 = poly.Vertices[i] - s; - var e2 = i + 1 < count ? poly.Vertices[i+1] - s : poly.Vertices[0] - s; - - var D = Vector2Helpers.Cross(e1, e2); - - var triangleArea = 0.5f * D; - area += triangleArea; - - // Area weighted centroid - center += (e1 + e2) * triangleArea * k_inv3; - - float ex1 = e1.X, ey1 = e1.Y; - float ex2 = e2.X, ey2 = e2.Y; - - var intx2 = ex1*ex1 + ex2*ex1 + ex2*ex2; - var inty2 = ey1*ey1 + ey2*ey1 + ey2*ey2; - - I += (0.25f * k_inv3 * D) * (intx2 + inty2); - } - - // Total mass - data.Mass = density * area; - - // Center of mass - DebugTools.Assert(area > float.Epsilon); - center *= 1.0f / area; - data.Center = center + s; - - // Inertia tensor relative to the local origin (point s). - data.I = density * I; - - // Shift to center of mass then to original body origin. - data.I += data.Mass * (Vector2.Dot(data.Center, data.Center) - Vector2.Dot(center, center)); - break; - default: - throw new NotImplementedException($"Cannot get MassData for {shape} as it's not implemented!"); - } - - return data; - } - - public static void GetMassData(IPhysShape shape, ref MassData data, float density) - { - // Box2D just calls fixture.GetMassData which just calls the shape method anyway soooo - // we can just cut out the middle-man - switch (shape) - { - case ChainShape: - data.Mass = 0f; - data.Center = Vector2.Zero; - data.I = 0f; - break; - case EdgeShape edge: - data.Mass = 0.0f; - data.Center = (edge.Vertex1 + edge.Vertex2) * 0.5f; - data.I = 0.0f; - break; - case PhysShapeCircle circle: - data.Mass = density * MathF.PI * circle.Radius * circle.Radius; - data.Center = circle.Position; - - // inertia about the local origin - data.I = data.Mass * (0.5f * circle.Radius * circle.Radius + Vector2.Dot(circle.Position, circle.Position)); - break; - case PhysShapeAabb aabb: - var polygon = (PolygonShape) aabb; - GetMassData(polygon, ref data, density); - break; - case PolygonShape poly: - // Polygon mass, centroid, and inertia. - // Let rho be the polygon density in mass per unit area. - // Then: - // mass = rho * int(dA) - // centroid.x = (1/mass) * rho * int(x * dA) - // centroid.y = (1/mass) * rho * int(y * dA) - // I = rho * int((x*x + y*y) * dA) - // - // We can compute these integrals by summing all the integrals - // for each triangle of the polygon. To evaluate the integral - // for a single triangle, we make a change of variables to - // the (u,v) coordinates of the triangle: - // x = x0 + e1x * u + e2x * v - // y = y0 + e1y * u + e2y * v - // where 0 <= u && 0 <= v && u + v <= 1. - // - // We integrate u from [0,1-v] and then v from [0,1]. - // We also need to use the Jacobian of the transformation: - // D = cross(e1, e2) - // - // Simplification: triangle centroid = (1/3) * (p1 + p2 + p3) - // - // The rest of the derivation is handled by computer algebra. - - var count = poly.VertexCount; - DebugTools.Assert(count >= 3); - - Vector2 center = new(0.0f, 0.0f); - float area = 0.0f; - float I = 0.0f; - - // Get a reference point for forming triangles. - // Use the first vertex to reduce round-off errors. - var s = poly.Vertices[0]; - - const float k_inv3 = 1.0f / 3.0f; - - for (var i = 0; i < count; ++i) - { - // Triangle vertices. - var e1 = poly.Vertices[i] - s; - var e2 = i + 1 < count ? poly.Vertices[i+1] - s : poly.Vertices[0] - s; - - float D = Vector2Helpers.Cross(e1, e2); - - float triangleArea = 0.5f * D; - area += triangleArea; - - // Area weighted centroid - center += (e1 + e2) * triangleArea * k_inv3; - - float ex1 = e1.X, ey1 = e1.Y; - float ex2 = e2.X, ey2 = e2.Y; - - float intx2 = ex1*ex1 + ex2*ex1 + ex2*ex2; - float inty2 = ey1*ey1 + ey2*ey1 + ey2*ey2; - - I += (0.25f * k_inv3 * D) * (intx2 + inty2); - } - - // Total mass - data.Mass = density * area; - - // Center of mass - DebugTools.Assert(area > float.Epsilon); - center *= 1.0f / area; - data.Center = center + s; - - // Inertia tensor relative to the local origin (point s). - data.I = density * I; - - // Shift to center of mass then to original body origin. - data.I += data.Mass * (Vector2.Dot(data.Center, data.Center) - Vector2.Dot(center, center)); - break; - default: - throw new NotImplementedException($"Cannot get MassData for {shape} as it's not implemented!"); - } - } - } -} diff --git a/Robust.Shared/Physics/Systems/FixtureSystem.cs b/Robust.Shared/Physics/Systems/FixtureSystem.cs deleted file mode 100644 index ac22c96cc6a..00000000000 --- a/Robust.Shared/Physics/Systems/FixtureSystem.cs +++ /dev/null @@ -1,384 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Robust.Shared.Collections; -using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; -using Robust.Shared.IoC; -using Robust.Shared.Physics.Collision.Shapes; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Dynamics; -using Robust.Shared.Physics.Events; -using Robust.Shared.Serialization; -using Robust.Shared.Utility; - -namespace Robust.Shared.Physics.Systems -{ - /// - /// Manages physics fixtures. - /// - public sealed partial class FixtureSystem : EntitySystem - { - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - private EntityQuery _mapQuery; - private EntityQuery _physicsQuery; - private EntityQuery _fixtureQuery; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnGetState); - SubscribeLocalEvent(OnHandleState); - _mapQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); - _fixtureQuery = GetEntityQuery(); - } - - private void OnShutdown(EntityUid uid, FixturesComponent component, ComponentShutdown args) - { - // TODO: Need a better solution to this because the only reason I don't throw is that allcomponents test - // Yes it is actively making the game buggier but I would essentially double the size of this PR trying to fix it - // my best solution rn is move the broadphase property onto FixturesComponent and then refactor - // SharedBroadphaseSystem a LOT. - if (!_physicsQuery.TryGetComponent(uid, out var body)) - return; - - // Can't just get physicscomp on shutdown as it may be touched completely independently. - _physics.DestroyContacts(body); - } - - #region Public - - public bool TryCreateFixture( - EntityUid uid, - IPhysShape shape, - string id, - float density = PhysicsConstants.DefaultDensity, - bool hard = true, - int collisionLayer = 0, - int collisionMask = 0, - float friction = PhysicsConstants.DefaultContactFriction, - float restitution = PhysicsConstants.DefaultRestitution, - bool updates = true, - FixturesComponent? manager = null, - PhysicsComponent? body = null, - TransformComponent? xform = null) - { - if (!_physicsQuery.Resolve(uid, ref body) || !_fixtureQuery.Resolve(uid, ref manager)) - return false; - - if (manager.Fixtures.ContainsKey(id)) - return false; - - var fixture = new Fixture(shape, collisionLayer, collisionMask, hard, density, friction, restitution); - CreateFixture(uid, id, fixture, updates, manager, body, xform); - return true; - } - - internal void CreateFixture( - EntityUid uid, - string fixtureId, - Fixture fixture, - bool updates = true, - FixturesComponent? manager = null, - PhysicsComponent? body = null, - TransformComponent? xform = null) - { - DebugTools.Assert(MetaData(uid).EntityLifeStage < EntityLifeStage.Terminating); - - if (!_physicsQuery.Resolve(uid, ref body) || !_fixtureQuery.Resolve(uid, ref manager)) - { - DebugTools.Assert(false); - return; - } - - if (string.IsNullOrEmpty(fixtureId)) - { - throw new InvalidOperationException($"Tried to create a fixture without an ID!"); - } - - manager.Fixtures.Add(fixtureId, fixture); - fixture.Owner = uid; - - if (body.CanCollide && Resolve(uid, ref xform)) - { - _lookup.CreateProxies(uid, fixtureId, fixture, xform, body); - } - - // Supposed to be wrapped in density but eh - if (updates) - { - // Don't need to dirty here as we'll just manually call it after (we 100% need to call it). - FixtureUpdate(uid, false, manager: manager, body: body); - // Don't need to ResetMassData as FixtureUpdate already does it. - Dirty(uid, manager); - } - - // TODO: Set newcontacts to true. - } - - /// - /// Attempts to get the with the specified ID for this body. - /// - public Fixture? GetFixtureOrNull(EntityUid uid, string id, FixturesComponent? manager = null) - { - if (!_fixtureQuery.Resolve(uid, ref manager)) - return null; - - return manager.Fixtures.TryGetValue(id, out var fixture) ? fixture : null; - } - - /// - /// Destroys the specified attached to the body. - /// - /// The specified body - /// The fixture ID - /// Whether to update mass etc. Set false if you're doing a bulk operation - public void DestroyFixture( - EntityUid uid, - string id, - bool updates = true, - PhysicsComponent? body = null, - FixturesComponent? manager = null, - TransformComponent? xform = null) - { - if (!_fixtureQuery.Resolve(uid, ref manager)) - return; - - var fixture = GetFixtureOrNull(uid, id, manager); - if (fixture != null) - DestroyFixture(uid, id, fixture, updates, body, manager, xform); - } - - /// - /// Destroys the specified - /// - /// Whether to update mass etc. Set false if you're doing a bulk operation - public void DestroyFixture( - EntityUid uid, - string fixtureId, - Fixture fixture, - bool updates = true, - PhysicsComponent? body = null, - FixturesComponent? manager = null, - TransformComponent? xform = null) - { - if (!Resolve(uid, ref body, ref manager, ref xform)) - { - return; - } - - // TODO: Assert world locked - DebugTools.Assert(manager.FixtureCount > 0); - - if (!manager.Fixtures.Remove(fixtureId)) - { - Log.Error($"Tried to remove fixture from {ToPrettyString(uid)} that was already removed."); - return; - } - - foreach (var contact in fixture.Contacts.Values.ToArray()) - { - _physics.DestroyContact(contact); - } - - if (_lookup.TryGetCurrentBroadphase(xform, out var broadphase)) - { - DebugTools.Assert(xform.MapUid == Transform(broadphase.Owner).MapUid); - _mapQuery.TryGetComponent(xform.MapUid, out var physicsMap); - _lookup.DestroyProxies(uid, fixtureId, fixture, xform, broadphase, physicsMap); - } - - if (updates) - { - var resetMass = fixture.Density > 0f; - FixtureUpdate(uid, resetMass: resetMass, manager: manager, body: body); - } - } - - #endregion - - internal void OnPhysicsInit(EntityUid uid, FixturesComponent component, PhysicsComponent? body = null) - { - // Can't ACTUALLY add it to the broadphase here because transform is still in a transient dimension on the 5th plane - // hence we'll just make sure its body is set and SharedBroadphaseSystem will deal with it later. - if (Resolve(uid, ref body, false)) - { - foreach (var (id, fixture) in component.Fixtures) - { - if (string.IsNullOrEmpty(id)) - { - throw new InvalidOperationException($"Tried to setup fixture on init for {ToPrettyString(uid)} with no ID!"); - } - - fixture.Owner = uid; - } - - // Make sure all the right stuff is set on the body - FixtureUpdate(uid, dirty: false, manager: component, body: body); - } - } - - private void OnGetState(EntityUid uid, FixturesComponent component, ref ComponentGetState args) - { - args.State = new FixtureManagerComponentState - { - Fixtures = component.Fixtures, - }; - } - - private void OnHandleState(EntityUid uid, FixturesComponent component, ref ComponentHandleState args) - { - if (args.Current is not FixtureManagerComponentState state) return; - - if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physics)) - { - Log.Error($"Tried to apply fixture state for an entity without physics: {ToPrettyString(uid)}"); - return; - } - foreach (var fixture in component.Fixtures.Values) - { - fixture.Owner = uid; - } - - var toAddFixtures = new ValueList<(string Id, Fixture Fixture)>(); - var toRemoveFixtures = new ValueList<(string Id, Fixture Fixture)>(); - var computeProperties = false; - - // Given a bunch of data isn't serialized need to sort of re-initialise it - var newFixtures = new Dictionary(state.Fixtures.Count); - - foreach (var (id, fixture) in state.Fixtures) - { - var newFixture = new Fixture(); - fixture.CopyTo(newFixture); - newFixtures.Add(id, newFixture); - newFixture.Owner = uid; - } - - TransformComponent? xform = null; - - // Add / update new fixtures - // FUTURE SLOTH - // Do not touch this or I WILL GLASS YOU. - // Updating fixtures in place causes prediction issues with contacts. - // See PR #3431 for when this started. - foreach (var (id, fixture) in newFixtures) - { - if (!component.Fixtures.TryGetValue(id, out var existing)) - { - toAddFixtures.Add((id, fixture)); - } - else if (!existing.Equivalent(fixture)) - { - toRemoveFixtures.Add((id, existing)); - toAddFixtures.Add((id, fixture)); - } - } - - // Remove old fixtures - foreach (var (existingId, existing) in component.Fixtures) - { - if (!newFixtures.ContainsKey(existingId)) - { - toRemoveFixtures.Add((existingId, existing)); - } - } - - // TODO add a DestroyFixture() override that takes in a list. - // reduced broadphase lookups - foreach (var (id, fixture) in toRemoveFixtures.Span) - { - computeProperties = true; - DestroyFixture(uid, id, fixture, false, physics, component); - } - - // TODO: We also still need event listeners for shapes (Probably need C# events) - // Or we could just make it so shapes can only be updated via fixturesystem which handles it - // automagically (friends or something?) - foreach (var (id, fixture) in toAddFixtures.Span) - { - computeProperties = true; - CreateFixture(uid, id, fixture, false, component, physics, xform); - } - - if (computeProperties) - { - FixtureUpdate(uid, manager: component, body: physics); - } - } - - #region Restitution - - public void SetRestitution(EntityUid uid, string fixtureId, Fixture fixture, float value, bool update = true, FixturesComponent? manager = null) - { - fixture.Restitution = value; - if (update && Resolve(uid, ref manager)) - FixtureUpdate(uid, manager: manager); - } - - #endregion - - /// - /// Updates all of the cached physics information on the body derived from fixtures. - /// - public void FixtureUpdate(EntityUid uid, bool dirty = true, bool resetMass = true, FixturesComponent? manager = null, PhysicsComponent? body = null) - { - if (!_physicsQuery.Resolve(uid, ref body) || !_fixtureQuery.Resolve(uid, ref manager)) - return; - - var mask = 0; - var layer = 0; - var hard = false; - - foreach (var fixture in manager.Fixtures.Values) - { - mask |= fixture.CollisionMask; - layer |= fixture.CollisionLayer; - hard |= fixture.Hard; - } - - if (resetMass) - _physics.ResetMassData(uid, manager, body); - - // Save the old layer to see if an event should be raised later. - var oldLayer = body.CollisionLayer; - - // Normally this method is called when fixtures need to be dirtied anyway so no point in returning early I think - body.CollisionMask = mask; - body.CollisionLayer = layer; - body.Hard = hard; - - if (manager.FixtureCount == 0) - _physics.SetCanCollide(uid, false, manager: manager, body: body); - - if (oldLayer != layer) - { - var ev = new CollisionLayerChangeEvent((uid, body)); - RaiseLocalEvent(ref ev); - } - - if (dirty) - Dirty(uid, manager); - } - - public int GetFixtureCount(EntityUid uid, FixturesComponent? manager = null) - { - if (!_fixtureQuery.Resolve(uid, ref manager)) - { - return 0; - } - - return manager.FixtureCount; - } - - [Serializable, NetSerializable] - private sealed class FixtureManagerComponentState : ComponentState - { - public Dictionary Fixtures = default!; - } - } -} diff --git a/Robust.Shared/Physics/Systems/FixturesChangeSystem.cs b/Robust.Shared/Physics/Systems/FixturesChangeSystem.cs index bfdc432854b..0b4cdeabeeb 100644 --- a/Robust.Shared/Physics/Systems/FixturesChangeSystem.cs +++ b/Robust.Shared/Physics/Systems/FixturesChangeSystem.cs @@ -6,16 +6,13 @@ namespace Robust.Shared.Physics.Systems; public sealed class FixturesChangeSystem : EntitySystem { - [Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; - private EntityQuery _fixturesQuery; private EntityQuery _physicsQuery; public override void Initialize() { base.Initialize(); - _fixturesQuery = GetEntityQuery(); _physicsQuery = GetEntityQuery(); SubscribeLocalEvent(OnChangeStartup); SubscribeLocalEvent(OnChangeShutdown); @@ -23,12 +20,12 @@ public override void Initialize() private void OnChangeStartup(Entity ent, ref ComponentStartup args) { - if (!_physicsQuery.TryComp(ent, out var physics) || !_fixturesQuery.TryComp(ent, out var fixtures)) + if (!_physicsQuery.TryComp(ent, out var physics)) return; foreach (var (id, fixture) in ent.Comp.Fixtures) { - _fixtures.TryCreateFixture(ent.Owner, + _physics.TryCreateFixture(ent.Owner, fixture.Shape, id, fixture.Density, @@ -37,19 +34,18 @@ private void OnChangeStartup(Entity ent, ref ComponentS fixture.CollisionMask, fixture.Friction, fixture.Restitution, - manager: fixtures, body: physics); } // TODO: Fixture creation should be handling this. - _physics.WakeBody(ent.Owner, manager: fixtures, body: physics); + _physics.WakeBody(ent.Owner, body: physics); } private void OnChangeShutdown(Entity ent, ref ComponentShutdown args) { foreach (var id in ent.Comp.Fixtures.Keys) { - _fixtures.DestroyFixture(ent.Owner, id); + _physics.DestroyFixture(ent.Owner, id); } } } diff --git a/Robust.Shared/Physics/Systems/SharedBroadphaseSystem.cs b/Robust.Shared/Physics/Systems/SharedBroadphaseSystem.cs index bec02a11eec..6d93dcd27e2 100644 --- a/Robust.Shared/Physics/Systems/SharedBroadphaseSystem.cs +++ b/Robust.Shared/Physics/Systems/SharedBroadphaseSystem.cs @@ -32,12 +32,13 @@ public abstract class SharedBroadphaseSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; private EntityQuery _broadphaseQuery; - private EntityQuery _fixturesQuery; private EntityQuery _gridQuery; private EntityQuery _physicsQuery; private EntityQuery _xformQuery; private EntityQuery _mapQuery; + private Dictionary _gridMoveBuffer = new(); + private float _broadphaseExpand; /* @@ -61,7 +62,6 @@ public override void Initialize() }; _broadphaseQuery = GetEntityQuery(); - _fixturesQuery = GetEntityQuery(); _gridQuery = GetEntityQuery(); _physicsQuery = GetEntityQuery(); _xformQuery = GetEntityQuery(); @@ -87,8 +87,7 @@ private void SetBroadphaseExpand(float value) private void FindGridContacts( PhysicsMapComponent component, MapId mapId, - HashSet movedGrids, - Dictionary gridMoveBuffer) + HashSet movedGrids) { // None moved this tick if (movedGrids.Count == 0) return; @@ -107,13 +106,13 @@ private void FindGridContacts( DebugTools.Assert(xform.MapID == mapId); var worldAABB = _transform.GetWorldMatrix(xform).TransformBox(grid.LocalAABB); var enlargedAABB = worldAABB.Enlarged(_broadphaseExpand); - var state = (moveBuffer, gridMoveBuffer); + var state = (moveBuffer, _gridMoveBuffer); QueryMapBroadphase(mapBroadphase.DynamicTree, ref state, enlargedAABB); QueryMapBroadphase(mapBroadphase.StaticTree, ref state, enlargedAABB); } - foreach (var (proxy, worldAABB) in gridMoveBuffer) + foreach (var (proxy, worldAABB) in _gridMoveBuffer) { moveBuffer[proxy] = worldAABB; // If something is in our AABB then try grid traversal for it @@ -161,10 +160,12 @@ internal void FindNewContacts(PhysicsMapComponent component, MapId mapId) var moveBuffer = component.MoveBuffer; var mapUid = _map.GetMapOrInvalid(mapId); var movedGrids = Comp(mapUid).MovedGrids; - var gridMoveBuffer = new Dictionary(); + + _gridMoveBuffer.Clear(); + moveBuffer.EnsureCapacity(moveBuffer.Count); // Find any entities being driven over that might need to be considered - FindGridContacts(component, mapId, movedGrids, gridMoveBuffer); + FindGridContacts(component, mapId, movedGrids); // There is some mariana trench levels of bullshit going on. // We essentially need to re-create Box2D's FindNewContacts but in a way that allows us to check every @@ -209,8 +210,6 @@ internal void FindNewContacts(PhysicsMapComponent component, MapId mapId) var proxyA = _contactJob.MoveBuffer[i].Proxy; var proxyABody = proxyA.Body; - _fixturesQuery.TryGetComponent(proxyA.Entity, out var manager); - foreach (var other in proxies) { var otherBody = other.Body; @@ -220,9 +219,9 @@ internal void FindNewContacts(PhysicsMapComponent component, MapId mapId) // This is because we generate a contact across 2 different broadphases where both bodies aren't // moving locally but are moving in world-terms. if (proxyA.Fixture.Hard && other.Fixture.Hard && - (gridMoveBuffer.ContainsKey(proxyA) || gridMoveBuffer.ContainsKey(other))) + (_gridMoveBuffer.ContainsKey(proxyA) || _gridMoveBuffer.ContainsKey(other))) { - _physicsSystem.WakeBody(proxyA.Entity, force: true, manager: manager, body: proxyABody); + _physicsSystem.WakeBody(proxyA.Entity, force: true, body: proxyABody); _physicsSystem.WakeBody(other.Entity, force: true, body: otherBody); } @@ -250,32 +249,29 @@ private void HandleGridCollisions( // TODO: Need to handle grids colliding with non-grid entities with the same layer // (nothing in SS14 does this yet). - var fixture = _fixturesQuery.Comp(gridUid); var physics = _physicsQuery.Comp(gridUid); var transform = _physicsSystem.GetPhysicsTransform(gridUid); var state = ( - new Entity(gridUid, fixture, grid, physics), + new Entity(gridUid, grid, physics), transform, worldMatrix, invWorldMatrix, _map, _physicsSystem, _transform, - _fixturesQuery, _physicsQuery, _xformQuery); _mapManager.FindGridsIntersecting(mapId, aabb, ref state, static (EntityUid uid, MapGridComponent component, - ref (Entity grid, + ref (Entity grid, Transform transform, Matrix3x2 worldMatrix, Matrix3x2 invWorldMatrix, SharedMapSystem _map, SharedPhysicsSystem _physicsSystem, SharedTransformSystem xformSystem, - EntityQuery fixturesQuery, EntityQuery physicsQuery, EntityQuery xformQuery) tuple) => { @@ -290,20 +286,19 @@ private void HandleGridCollisions( var otherTransform = tuple._physicsSystem.GetPhysicsTransform(uid); // Get Grid2 AABB in grid1 ref - var aabb1 = tuple.grid.Comp2.LocalAABB.Intersect(tuple.invWorldMatrix.TransformBox(otherGridBounds)); + var aabb1 = tuple.grid.Comp1.LocalAABB.Intersect(tuple.invWorldMatrix.TransformBox(otherGridBounds)); // TODO: AddPair has a nasty check in there that's O(n) but that's also a general physics problem. var ourChunks = tuple._map.GetLocalMapChunks(tuple.grid.Owner, tuple.grid, aabb1); - var physicsA = tuple.grid.Comp3; + var physicsA = tuple.grid.Comp2; var physicsB = tuple.physicsQuery.GetComponent(uid); - var fixturesB = tuple.fixturesQuery.Comp(uid); // Only care about chunks on other grid overlapping us. while (ourChunks.MoveNext(out var ourChunk)) { var ourChunkWorld = tuple.worldMatrix.TransformBox( - ourChunk.CachedBounds.Translated(ourChunk.Indices * tuple.grid.Comp2.ChunkSize)); + ourChunk.CachedBounds.Translated(ourChunk.Indices * tuple.grid.Comp1.ChunkSize)); var ourChunkOtherRef = otherGridInvMatrix.TransformBox(ourChunkWorld); var collidingChunks = tuple._map.GetLocalMapChunks(uid, component, ourChunkOtherRef); @@ -311,7 +306,7 @@ private void HandleGridCollisions( { foreach (var ourId in ourChunk.Fixtures) { - var fixture = tuple.grid.Comp1.Fixtures[ourId]; + var fixture = tuple.grid.Comp2.Fixtures[ourId]; for (var i = 0; i < fixture.Shape.ChildCount; i++) { @@ -319,7 +314,7 @@ private void HandleGridCollisions( foreach (var otherId in collidingChunk.Fixtures) { - var otherFixture = fixturesB.Fixtures[otherId]; + var otherFixture = physicsB.Fixtures[otherId]; for (var j = 0; j < otherFixture.Shape.ChildCount; j++) { @@ -413,10 +408,10 @@ private void QueryBroadphase(IBroadPhase broadPhase, (List, Fixtur }, aabb, true); } - public void RegenerateContacts(EntityUid uid, PhysicsComponent body, FixturesComponent? fixtures = null, TransformComponent? xform = null) + public void RegenerateContacts(EntityUid uid, PhysicsComponent body, TransformComponent? xform = null) { _physicsSystem.DestroyContacts(body); - if (!Resolve(uid, ref xform, ref fixtures)) + if (!Resolve(uid, ref xform)) return; if (xform.MapUid == null) @@ -428,7 +423,7 @@ public void RegenerateContacts(EntityUid uid, PhysicsComponent body, FixturesCom _physicsSystem.SetAwake((uid, body), true); var matrix = _transform.GetWorldMatrix(broadphase); - foreach (var fixture in fixtures.Fixtures.Values) + foreach (var fixture in body.Fixtures.Values) { TouchProxies(xform.MapUid.Value, matrix, fixture); } diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs index 6a97bea3fb6..cba83ed6ae8 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs @@ -44,11 +44,10 @@ public partial class SharedPhysicsSystem private void OnPhysicsInit(EntityUid uid, PhysicsComponent component, ComponentInit args) { var xform = Transform(uid); - var manager = EnsureComp(uid); if (component.CanCollide && (_containerSystem.IsEntityOrParentInContainer(uid) || xform.MapID == MapId.Nullspace)) { - SetCanCollide(uid, false, false, manager: manager, body: component); + SetCanCollide(uid, false, false, body: component); } if (component.CanCollide) @@ -59,16 +58,33 @@ private void OnPhysicsInit(EntityUid uid, PhysicsComponent component, ComponentI } } - // Gets added to broadphase via fixturessystem - _fixtures.OnPhysicsInit(uid, manager, component); + foreach (var (id, fixture) in component.Fixtures) + { + if (string.IsNullOrEmpty(id)) + { + throw new InvalidOperationException($"Tried to setup fixture on init for {ToPrettyString(uid)} with no ID!"); + } + + fixture.Owner = uid; + } + + // Make sure all the right stuff is set on the body + FixtureUpdate(uid, dirty: false, body: component); - if (manager.FixtureCount == 0) + if (component.Fixtures.Count == 0) component.CanCollide = false; var ev = new CollisionChangeEvent(uid, component, component.CanCollide); RaiseLocalEvent(ref ev); } + private void OnPhysicsShutdown(EntityUid uid, PhysicsComponent component, ComponentShutdown args) + { + DestroyContacts(component); + SetCanCollide(uid, false, false, body: component); + DebugTools.Assert(!component.Awake); + } + private void OnPhysicsGetState(EntityUid uid, PhysicsComponent component, ref ComponentGetState args) { if (args.FromTick > component.CreationTick && component.LastFieldUpdate >= args.FromTick) @@ -135,16 +151,14 @@ private void OnPhysicsHandleState(EntityUid uid, PhysicsComponent component, ref // So transform doesn't apply MapId in the HandleComponentState because ??? so MapId can still be 0. // Fucking kill me, please. You have no idea deep the rabbit hole of shitcode goes to make this work. - _fixturesQuery.TryComp(uid, out var manager); - if (args.Current is PhysicsLinearVelocityDeltaState linearState) { - SetLinearVelocity(uid, linearState.LinearVelocity, body: component, manager: manager); + SetLinearVelocity(uid, linearState.LinearVelocity, body: component); } else if (args.Current is PhysicsVelocityDeltaState velocityState) { - SetLinearVelocity(uid, velocityState.LinearVelocity, body: component, manager: manager); - SetAngularVelocity(uid, velocityState.AngularVelocity, body: component, manager: manager); + SetLinearVelocity(uid, velocityState.LinearVelocity, body: component); + SetAngularVelocity(uid, velocityState.AngularVelocity, body: component); } else if (args.Current is PhysicsComponentState newState) { @@ -153,14 +167,86 @@ private void OnPhysicsHandleState(EntityUid uid, PhysicsComponent component, ref SetCanCollide(uid, newState.CanCollide, body: component); component.BodyStatus = newState.Status; - SetLinearVelocity(uid, newState.LinearVelocity, body: component, manager: manager); - SetAngularVelocity(uid, newState.AngularVelocity, body: component, manager: manager); - SetBodyType(uid, newState.BodyType, manager, component); + SetLinearVelocity(uid, newState.LinearVelocity, body: component); + SetAngularVelocity(uid, newState.AngularVelocity, body: component); + SetBodyType(uid, newState.BodyType, component); SetFriction(uid, component, newState.Friction); SetLinearDamping(uid, component, newState.LinearDamping); SetAngularDamping(uid, component, newState.AngularDamping); component.Force = newState.Force; component.Torque = newState.Torque; + + // Fixtures + foreach (var fixture in component.Fixtures.Values) + { + fixture.Owner = uid; + } + + var toAddFixtures = new ValueList<(string Id, Fixture Fixture)>(); + var toRemoveFixtures = new ValueList<(string Id, Fixture Fixture)>(); + var computeProperties = false; + + // Given a bunch of data isn't serialized need to sort of re-initialise it + var newFixtures = new Dictionary(newState.Fixtures.Count); + + foreach (var (id, fixture) in newState.Fixtures) + { + var newFixture = new Fixture(); + fixture.CopyTo(newFixture); + newFixtures.Add(id, newFixture); + newFixture.Owner = uid; + } + + TransformComponent? xform = null; + + // Add / update new fixtures + // FUTURE SLOTH + // Do not touch this or I WILL GLASS YOU. + // Updating fixtures in place causes prediction issues with contacts. + // See PR #3431 for when this started. + foreach (var (id, fixture) in newFixtures) + { + if (!component.Fixtures.TryGetValue(id, out var existing)) + { + toAddFixtures.Add((id, fixture)); + } + else if (!existing.Equivalent(fixture)) + { + toRemoveFixtures.Add((id, existing)); + toAddFixtures.Add((id, fixture)); + } + } + + // Remove old fixtures + foreach (var (existingId, existing) in component.Fixtures) + { + if (!newFixtures.ContainsKey(existingId)) + { + toRemoveFixtures.Add((existingId, existing)); + } + } + + // TODO add a DestroyFixture() override that takes in a list. + // reduced broadphase lookups + foreach (var (id, fixture) in toRemoveFixtures.Span) + { + computeProperties = true; + DestroyFixture(uid, id, fixture, false, component); + } + + // TODO: We also still need event listeners for shapes (Probably need C# events) + // Or we could just make it so shapes can only be updated via fixturesystem which handles it + // automagically (friends or something?) + foreach (var (id, fixture) in toAddFixtures.Span) + { + computeProperties = true; + CreateFixture(uid, id, fixture, false, component, xform); + } + + if (computeProperties) + { + FixtureUpdate(uid, body: component); + } } } @@ -173,9 +259,9 @@ private bool IsMoveable(PhysicsComponent body) #region Impulses - public void ApplyAngularImpulse(EntityUid uid, float impulse, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void ApplyAngularImpulse(EntityUid uid, float impulse, PhysicsComponent? body = null) { - if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body)) + if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, body: body)) { return; } @@ -183,9 +269,9 @@ public void ApplyAngularImpulse(EntityUid uid, float impulse, FixturesComponent? SetAngularVelocity(uid, body.AngularVelocity + impulse * body.InvI, body: body); } - public void ApplyForce(EntityUid uid, Vector2 force, Vector2 point, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void ApplyForce(EntityUid uid, Vector2 force, Vector2 point, PhysicsComponent? body = null) { - if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body)) + if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, body: body)) { return; } @@ -194,9 +280,9 @@ public void ApplyForce(EntityUid uid, Vector2 force, Vector2 point, FixturesComp body.Torque += Vector2Helpers.Cross(point - body._localCenter, force); } - public void ApplyForce(EntityUid uid, Vector2 force, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void ApplyForce(EntityUid uid, Vector2 force, PhysicsComponent? body = null) { - if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body)) + if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, body: body)) { return; } @@ -204,9 +290,9 @@ public void ApplyForce(EntityUid uid, Vector2 force, FixturesComponent? manager body.Force += force; } - public void ApplyTorque(EntityUid uid, float torque, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void ApplyTorque(EntityUid uid, float torque, PhysicsComponent? body = null) { - if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body)) + if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, body: body)) { return; } @@ -215,9 +301,9 @@ public void ApplyTorque(EntityUid uid, float torque, FixturesComponent? manager DirtyField(uid, body, nameof(PhysicsComponent.Torque)); } - public void ApplyLinearImpulse(EntityUid uid, Vector2 impulse, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void ApplyLinearImpulse(EntityUid uid, Vector2 impulse, PhysicsComponent? body = null) { - if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body)) + if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, body: body)) { return; } @@ -225,9 +311,9 @@ public void ApplyLinearImpulse(EntityUid uid, Vector2 impulse, FixturesComponent SetLinearVelocity(uid,body.LinearVelocity + impulse * body._invMass, body: body); } - public void ApplyLinearImpulse(EntityUid uid, Vector2 impulse, Vector2 point, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void ApplyLinearImpulse(EntityUid uid, Vector2 impulse, Vector2 point, PhysicsComponent? body = null) { - if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body)) + if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, body: body)) { return; } @@ -295,14 +381,11 @@ public void ResetDynamics(EntityUid uid, PhysicsComponent body, bool dirty = tru } } - public void ResetMassData(EntityUid uid, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void ResetMassData(EntityUid uid, PhysicsComponent? body = null) { if (!PhysicsQuery.Resolve(uid, ref body)) return; - if (!_fixturesQuery.Resolve(uid, ref manager)) - return; - var oldMass = body._mass; var oldInertia = body._inertia; @@ -312,12 +395,12 @@ public void ResetMassData(EntityUid uid, FixturesComponent? manager = null, Phys body.InvI = 0.0f; var localCenter = Vector2.Zero; - foreach (var fixture in manager.Fixtures.Values) + foreach (var fixture in body.Fixtures.Values) { if (fixture.Density <= 0.0f) continue; var data = new MassData(); - FixtureSystem.GetMassData(fixture.Shape, ref data, fixture.Density); + GetMassData(fixture.Shape, ref data, fixture.Density); body._mass += data.Mass; localCenter += data.Center * data.Mass; @@ -372,11 +455,11 @@ public void ResetMassData(EntityUid uid, FixturesComponent? manager = null, Phys if (body._mass == oldMass && body._inertia == oldInertia && oldCenter == localCenter) return; - var ev = new MassDataChangedEvent((uid, body, manager), oldMass, oldInertia, oldCenter); + var ev = new MassDataChangedEvent((uid, body), oldMass, oldInertia, oldCenter); RaiseLocalEvent(uid, ref ev); } - public bool SetAngularVelocity(EntityUid uid, float value, bool dirty = true, FixturesComponent? manager = null, PhysicsComponent? body = null) + public bool SetAngularVelocity(EntityUid uid, float value, bool dirty = true, PhysicsComponent? body = null) { if (!PhysicsQuery.Resolve(uid, ref body)) return false; @@ -386,7 +469,7 @@ public bool SetAngularVelocity(EntityUid uid, float value, bool dirty = true, Fi if (value * value > 0.0f) { - if (!WakeBody(uid, manager: manager, body: body)) + if (!WakeBody(uid, body: body)) return false; } @@ -405,7 +488,7 @@ public bool SetAngularVelocity(EntityUid uid, float value, bool dirty = true, Fi /// /// Attempts to set the body to collidable, wake it, then move it. /// - public bool SetLinearVelocity(EntityUid uid, Vector2 velocity, bool dirty = true, bool wakeBody = true, FixturesComponent? manager = null, PhysicsComponent? body = null) + public bool SetLinearVelocity(EntityUid uid, Vector2 velocity, bool dirty = true, bool wakeBody = true, PhysicsComponent? body = null) { if (!PhysicsQuery.Resolve(uid, ref body)) return false; @@ -415,7 +498,7 @@ public bool SetLinearVelocity(EntityUid uid, Vector2 velocity, bool dirty = true if (wakeBody && Vector2.Dot(velocity, velocity) > 0.0f) { - if (!WakeBody(uid, manager: manager, body: body)) + if (!WakeBody(uid, body: body)) return false; } @@ -496,17 +579,16 @@ public void SetAwake(Entity ent, bool value, bool updateSleepT UpdateMapAwakeState(uid, body); } - public void TrySetBodyType(EntityUid uid, BodyType value, FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null) + public void TrySetBodyType(EntityUid uid, BodyType value, PhysicsComponent? body = null, TransformComponent? xform = null) { - if (_fixturesQuery.Resolve(uid, ref manager, false) && - PhysicsQuery.Resolve(uid, ref body, false) && - _xformQuery.Resolve(uid, ref xform, false)) + if (PhysicsQuery.Resolve(uid, ref body, false) && + _xformQuery.Resolve(uid, ref xform, false)) { - SetBodyType(uid, value, manager, body, xform); + SetBodyType(uid, value, body, xform); } } - public void SetBodyType(EntityUid uid, BodyType value, FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null) + public void SetBodyType(EntityUid uid, BodyType value, PhysicsComponent? body = null, TransformComponent? xform = null) { if (!PhysicsQuery.Resolve(uid, ref body)) return; @@ -516,7 +598,7 @@ public void SetBodyType(EntityUid uid, BodyType value, FixturesComponent? manage var oldType = body.BodyType; body.BodyType = value; - ResetMassData(uid, manager, body); + ResetMassData(uid, body); if (body.BodyType == BodyType.Static) { @@ -546,7 +628,7 @@ public void SetBodyType(EntityUid uid, BodyType value, FixturesComponent? manage DirtyField(uid, body, nameof(PhysicsComponent.Torque)); } - _broadphase.RegenerateContacts(uid, body, manager, xform); + _broadphase.RegenerateContacts(uid, body, xform); if (body.Initialized) { @@ -574,7 +656,6 @@ public bool SetCanCollide( bool value, bool dirty = true, bool force = false, - FixturesComponent? manager = null, PhysicsComponent? body = null) { if (!PhysicsQuery.Resolve(uid, ref body)) @@ -591,13 +672,13 @@ public bool SetCanCollide( if (_containerSystem.IsEntityOrParentInContainer(uid)) return false; - if (!_fixturesQuery.Resolve(uid, ref manager) || manager.FixtureCount == 0 && !_mapManager.IsGrid(uid)) + if (body.Fixtures.Count == 0 && !_mapManager.IsGrid(uid)) return false; } else { DebugTools.Assert(!_containerSystem.IsEntityOrParentInContainer(uid)); - DebugTools.Assert((Resolve(uid, ref manager) && manager.FixtureCount > 0) || _mapManager.IsGrid(uid)); + DebugTools.Assert(body.Fixtures.Count > 0 || _mapManager.IsGrid(uid)); } } @@ -616,7 +697,7 @@ public bool SetCanCollide( return value; } - public void SetFixedRotation(EntityUid uid, bool value, bool dirty = true, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void SetFixedRotation(EntityUid uid, bool value, bool dirty = true, PhysicsComponent? body = null) { if (!PhysicsQuery.Resolve(uid, ref body) || body.FixedRotation == value) return; @@ -630,7 +711,7 @@ public void SetFixedRotation(EntityUid uid, bool value, bool dirty = true, Fixtu DirtyField(uid, body, nameof(PhysicsComponent.AngularVelocity)); } - ResetMassData(uid, manager: manager, body: body); + ResetMassData(uid, body: body); } public void SetFriction(EntityUid uid, PhysicsComponent body, float value, bool dirty = true) @@ -696,12 +777,12 @@ public void SetSleepTime(PhysicsComponent body, float value) /// /// Bypasses fixture and container checks /// true if the body is collidable and awake - public bool WakeBody(EntityUid uid, bool force = false, FixturesComponent? manager = null, PhysicsComponent? body = null) + public bool WakeBody(EntityUid uid, bool force = false, PhysicsComponent? body = null) { if (!PhysicsQuery.Resolve(uid, ref body)) return false; - if (!SetCanCollide(uid, true, manager: manager, body: body, force: force)) + if (!SetCanCollide(uid, true, body: body, force: force)) return false; SetAwake((uid, body), true); @@ -771,9 +852,9 @@ public Transform GetPhysicsTransform(EntityUid uid, TransformComponent? xform = /// /// Gets the physics World AABB, only considering fixtures. /// - public Box2 GetWorldAABB(EntityUid uid, FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null) + public Box2 GetWorldAABB(EntityUid uid, PhysicsComponent? body = null, TransformComponent? xform = null) { - if (!Resolve(uid, ref manager, ref body, ref xform)) + if (!Resolve(uid, ref body, ref xform)) return new Box2(); var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform); @@ -782,7 +863,7 @@ public Box2 GetWorldAABB(EntityUid uid, FixturesComponent? manager = null, Physi var bounds = new Box2(transform.Position, transform.Position); - foreach (var fixture in manager.Fixtures.Values) + foreach (var fixture in body.Fixtures.Values) { for (var i = 0; i < fixture.Shape.ChildCount; i++) { @@ -794,10 +875,9 @@ public Box2 GetWorldAABB(EntityUid uid, FixturesComponent? manager = null, Physi return bounds; } - public Box2 GetHardAABB(EntityUid uid, FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null) + public Box2 GetHardAABB(EntityUid uid, PhysicsComponent? body = null, TransformComponent? xform = null) { if (!PhysicsQuery.Resolve(uid, ref body) - || !_fixturesQuery.Resolve(uid, ref manager) || !Resolve(uid, ref xform)) { return Box2.Empty; @@ -809,7 +889,7 @@ public Box2 GetHardAABB(EntityUid uid, FixturesComponent? manager = null, Physic var bounds = new Box2(transform.Position, transform.Position); - foreach (var fixture in manager.Fixtures.Values) + foreach (var fixture in body.Fixtures.Values) { if (!fixture.Hard) continue; @@ -823,9 +903,9 @@ public Box2 GetHardAABB(EntityUid uid, FixturesComponent? manager = null, Physic return bounds; } - public (int Layer, int Mask) GetHardCollision(EntityUid uid, FixturesComponent? manager = null) + public (int Layer, int Mask) GetHardCollision(EntityUid uid, PhysicsComponent? body = null) { - if (!_fixturesQuery.Resolve(uid, ref manager, false)) + if (!PhysicsQuery.Resolve(uid, ref body, false)) { return (0, 0); } @@ -833,7 +913,7 @@ public Box2 GetHardAABB(EntityUid uid, FixturesComponent? manager = null, Physic var layer = 0; var mask = 0; - foreach (var fixture in manager.Fixtures.Values) + foreach (var fixture in body.Fixtures.Values) { if (!fixture.Hard) continue; diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Contacts.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Contacts.cs index f7cee57fa47..880e142a308 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Contacts.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Contacts.cs @@ -762,9 +762,9 @@ public void RegenerateContacts(Entity entity) /// /// Fixture we should ignore if applicable [Pure] - public int GetTouchingContacts(Entity entity, string? ignoredFixtureId = null) + public int GetTouchingContacts(Entity entity, string? ignoredFixtureId = null) { - if (!_fixturesQuery.Resolve(entity.Owner, ref entity.Comp)) + if (!PhysicsQuery.Resolve(entity.Owner, ref entity.Comp)) return 0; var count = 0; @@ -790,9 +790,9 @@ public int GetTouchingContacts(Entity entity, string? ignore /// Returns all of this entity's contacts. /// [Pure] - public ContactEnumerator GetContacts(Entity entity) + public ContactEnumerator GetContacts(Entity entity) { - _fixturesQuery.Resolve(entity.Owner, ref entity.Comp); + PhysicsQuery.Resolve(entity.Owner, ref entity.Comp); return new ContactEnumerator(entity.Comp); } } @@ -804,7 +804,7 @@ public record struct ContactEnumerator private Dictionary.ValueCollection.Enumerator _fixtureEnumerator; private Dictionary.ValueCollection.Enumerator _contactEnumerator; - public ContactEnumerator(FixturesComponent? fixtures) + public ContactEnumerator(PhysicsComponent? fixtures) { if (fixtures == null || fixtures.Fixtures.Count == 0) { diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Fixtures.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Fixtures.cs index 907a03a4775..7d8cca0c6e6 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Fixtures.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Fixtures.cs @@ -1,74 +1,250 @@ +using System; +using System.Collections.Generic; +using System.Linq; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; +using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Physics.Events; using Robust.Shared.Utility; namespace Robust.Shared.Physics.Systems; public abstract partial class SharedPhysicsSystem { - [Dependency] private readonly FixtureSystem _fixtures = default!; + public bool TryCreateFixture( + EntityUid uid, + IPhysShape shape, + string id, + float density = PhysicsConstants.DefaultDensity, + bool hard = true, + int collisionLayer = 0, + int collisionMask = 0, + float friction = PhysicsConstants.DefaultContactFriction, + float restitution = PhysicsConstants.DefaultRestitution, + bool updates = true, + PhysicsComponent? body = null, + TransformComponent? xform = null) + { + if (!PhysicsQuery.Resolve(uid, ref body)) + return false; + + if (body.Fixtures.ContainsKey(id)) + return false; + + var fixture = new Fixture(shape, collisionLayer, collisionMask, hard, density, friction, restitution); + CreateFixture(uid, id, fixture, updates, body, xform); + return true; + } + + internal void CreateFixture( + EntityUid uid, + string fixtureId, + Fixture fixture, + bool updates = true, + PhysicsComponent? body = null, + TransformComponent? xform = null) + { + DebugTools.Assert(MetaData(uid).EntityLifeStage < EntityLifeStage.Terminating); + + if (!PhysicsQuery.Resolve(uid, ref body)) + { + DebugTools.Assert(false); + return; + } + + if (string.IsNullOrEmpty(fixtureId)) + { + throw new InvalidOperationException($"Tried to create a fixture without an ID!"); + } + + body.Fixtures.Add(fixtureId, fixture); + fixture.Owner = uid; + + if (body.CanCollide && Resolve(uid, ref xform)) + { + _lookup.CreateProxies(uid, fixtureId, fixture, xform, body); + } + + // Supposed to be wrapped in density but eh + if (updates) + { + // Don't need to dirty here as we'll just manually call it after (we 100% need to call it). + FixtureUpdate(uid, false, body: body); + // Don't need to ResetMassData as FixtureUpdate already does it. + Dirty(uid, body); + } + + // TODO: Set newcontacts to true. + } + + /// + /// Attempts to get the with the specified ID for this body. + /// + public Fixture? GetFixtureOrNull(EntityUid uid, string id, PhysicsComponent? body = null) + { + if (!PhysicsQuery.Resolve(uid, ref body)) + return null; + + return body.Fixtures.GetValueOrDefault(id); + } + + /// + /// Destroys the specified attached to the body. + /// + /// The specified body + /// The fixture ID + /// Whether to update mass etc. Set false if you're doing a bulk operation + public void DestroyFixture( + EntityUid uid, + string id, + bool updates = true, + PhysicsComponent? body = null, + TransformComponent? xform = null) + { + if (!PhysicsQuery.Resolve(uid, ref body)) + return; + + var fixture = GetFixtureOrNull(uid, id); + if (fixture != null) + DestroyFixture(uid, id, fixture, updates, body, xform); + } + + /// + /// Destroys the specified + /// + /// Whether to update mass etc. Set false if you're doing a bulk operation + public void DestroyFixture( + EntityUid uid, + string fixtureId, + Fixture fixture, + bool updates = true, + PhysicsComponent? body = null, + TransformComponent? xform = null) + { + if (!Resolve(uid, ref body, ref xform)) + { + return; + } + + // TODO: Assert world locked + DebugTools.Assert(body.Fixtures.Count > 0); + + if (!body.Fixtures.Remove(fixtureId)) + { + Log.Error($"Tried to remove fixture from {ToPrettyString(uid)} that was already removed."); + return; + } + + foreach (var contact in fixture.Contacts.Values.ToArray()) + { + DestroyContact(contact); + } + + if (_lookup.TryGetCurrentBroadphase(xform, out var broadphase)) + { + DebugTools.Assert(xform.MapUid == Transform(broadphase.Owner).MapUid); + PhysMapQuery.TryGetComponent(xform.MapUid, out var physicsMap); + _lookup.DestroyProxies(uid, fixtureId, fixture, xform, broadphase, physicsMap); + } + + if (updates) + { + var resetMass = fixture.Density > 0f; + FixtureUpdate(uid, resetMass: resetMass, body: body); + } + } - public void SetDensity(EntityUid uid, string fixtureId, Fixture fixture, float value, bool update = true, FixturesComponent? manager = null) + /// + /// Updates all of the cached physics information on the body derived from fixtures. + /// + public void FixtureUpdate(EntityUid uid, bool dirty = true, bool resetMass = true, PhysicsComponent? body = null) + { + if (!PhysicsQuery.Resolve(uid, ref body)) + return; + + var mask = 0; + var layer = 0; + var hard = false; + + foreach (var fixture in body.Fixtures.Values) + { + mask |= fixture.CollisionMask; + layer |= fixture.CollisionLayer; + hard |= fixture.Hard; + } + + if (resetMass) + ResetMassData(uid, body); + + // Save the old layer to see if an event should be raised later. + var oldLayer = body.CollisionLayer; + + // Normally this method is called when fixtures need to be dirtied anyway so no point in returning early I think + body.CollisionMask = mask; + body.CollisionLayer = layer; + body.Hard = hard; + + if (body.Fixtures.Count == 0) + SetCanCollide(uid, false, body: body); + + if (oldLayer != layer) + { + var ev = new CollisionLayerChangeEvent((uid, body)); + RaiseLocalEvent(ref ev); + } + + if (dirty) + Dirty(uid, body); + } + + public void SetDensity(EntityUid uid, string fixtureId, Fixture fixture, float value, bool update = true, PhysicsComponent? body = null) { DebugTools.Assert(value >= 0f); if (fixture.Density.Equals(value)) return; - if (!Resolve(uid, ref manager)) - return; - fixture.Density = value; if (update) - _fixtures.FixtureUpdate(uid, manager: manager); + FixtureUpdate(uid, body: body); } - public void SetFriction(EntityUid uid, string fixtureId, Fixture fixture, float value, bool update = true, FixturesComponent? manager = null) + public void SetFriction(EntityUid uid, string fixtureId, Fixture fixture, float value, bool update = true, PhysicsComponent? body = null) { DebugTools.Assert(value >= 0f); if (fixture.Friction.Equals(value)) return; - if (!Resolve(uid, ref manager)) - return; - fixture.Friction = value; if (update) - _fixtures.FixtureUpdate(uid, manager: manager); + FixtureUpdate(uid, body: body); } - public void SetHard(EntityUid uid, Fixture fixture, bool value, FixturesComponent? manager = null) + public void SetHard(EntityUid uid, Fixture fixture, bool value, PhysicsComponent? body = null) { if (fixture.Hard.Equals(value)) return; - if (!Resolve(uid, ref manager)) - return; - fixture.Hard = value; - _fixtures.FixtureUpdate(uid, manager: manager); - WakeBody(uid); + FixtureUpdate(uid, body: body); + WakeBody(uid, body: body); } - public void SetRestitution(EntityUid uid, Fixture fixture, float value, bool update = true, FixturesComponent? manager = null) + public void SetRestitution(EntityUid uid, Fixture fixture, float value, bool update = true, PhysicsComponent? body = null) { DebugTools.Assert(value >= 0f); if (fixture.Restitution.Equals(value)) return; - if (!Resolve(uid, ref manager)) - return; - fixture.Restitution = value; if (update) - _fixtures.FixtureUpdate(uid, manager: manager); + FixtureUpdate(uid, body: body); } #region Collision Masks & Layers @@ -76,18 +252,16 @@ public void SetRestitution(EntityUid uid, Fixture fixture, float value, bool upd /// /// Similar to IsHardCollidable but also checks whether both entities are set to CanCollide /// - public bool IsCurrentlyHardCollidable(Entity bodyA, Entity bodyB) + public bool IsCurrentlyHardCollidable(Entity bodyA, Entity bodyB) { - if (!_fixturesQuery.Resolve(bodyA, ref bodyA.Comp1, false) || - !_fixturesQuery.Resolve(bodyB, ref bodyB.Comp1, false) || - !PhysicsQuery.Resolve(bodyA, ref bodyA.Comp2, false) || - !PhysicsQuery.Resolve(bodyB, ref bodyB.Comp2, false)) + if (!PhysicsQuery.Resolve(bodyA, ref bodyA.Comp, false) || + !PhysicsQuery.Resolve(bodyB, ref bodyB.Comp, false)) { return false; } - if (!bodyA.Comp2.CanCollide || - !bodyB.Comp2.CanCollide) + if (!bodyA.Comp.CanCollide || + !bodyB.Comp.CanCollide) { return false; } @@ -98,32 +272,30 @@ public bool IsCurrentlyHardCollidable(Entity /// Returns true if both entities are hard-collidable with each other. /// - public bool IsHardCollidable(Entity bodyA, Entity bodyB) + public bool IsHardCollidable(Entity bodyA, Entity bodyB) { - if (!_fixturesQuery.Resolve(bodyA, ref bodyA.Comp1, false) || - !_fixturesQuery.Resolve(bodyB, ref bodyB.Comp1, false) || - !PhysicsQuery.Resolve(bodyA, ref bodyA.Comp2, false) || - !PhysicsQuery.Resolve(bodyB, ref bodyB.Comp2, false)) + if (!PhysicsQuery.Resolve(bodyA, ref bodyA.Comp, false) || + !PhysicsQuery.Resolve(bodyB, ref bodyB.Comp, false)) { return false; } // Fast check - if (!bodyA.Comp2.Hard || - !bodyB.Comp2.Hard || - ((bodyA.Comp2.CollisionLayer & bodyB.Comp2.CollisionMask) == 0x0 && - (bodyA.Comp2.CollisionMask & bodyB.Comp2.CollisionLayer) == 0x0)) + if (!bodyA.Comp.Hard || + !bodyB.Comp.Hard || + ((bodyA.Comp.CollisionLayer & bodyB.Comp.CollisionMask) == 0x0 && + (bodyA.Comp.CollisionMask & bodyB.Comp.CollisionLayer) == 0x0)) { return false; } // Slow check - foreach (var fix in bodyA.Comp1.Fixtures.Values) + foreach (var fix in bodyA.Comp.Fixtures.Values) { if (!fix.Hard) continue; - foreach (var other in bodyB.Comp1.Fixtures.Values) + foreach (var other in bodyB.Comp.Fixtures.Values) { if (!other.Hard) continue; @@ -141,108 +313,85 @@ public bool IsHardCollidable(Entity bodyA return false; } - public void AddCollisionMask(EntityUid uid, string fixtureId, Fixture fixture, int mask, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void AddCollisionMask(EntityUid uid, string fixtureId, Fixture fixture, int mask, PhysicsComponent? body = null) { if ((fixture.CollisionMask & mask) == mask) return; - if (!Resolve(uid, ref manager)) + if (!PhysicsQuery.Resolve(uid, ref body)) return; - DebugTools.Assert(manager.Fixtures.ContainsKey(fixtureId)); + DebugTools.Assert(body.Fixtures.ContainsKey(fixtureId)); fixture.CollisionMask |= mask; - if (body != null || TryComp(uid, out body)) - { - _fixtures.FixtureUpdate(uid, manager: manager, body: body); - } - + FixtureUpdate(uid, body: body); _broadphase.Refilter(uid, fixture); } - public void SetCollisionMask(EntityUid uid, string fixtureId, Fixture fixture, int mask, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void SetCollisionMask(EntityUid uid, string fixtureId, Fixture fixture, int mask, PhysicsComponent? body = null) { if (fixture.CollisionMask == mask) return; - if (!Resolve(uid, ref manager)) + if (!PhysicsQuery.Resolve(uid, ref body)) return; - DebugTools.Assert(manager.Fixtures.ContainsKey(fixtureId)); + DebugTools.Assert(body.Fixtures.ContainsKey(fixtureId)); fixture.CollisionMask = mask; - if (body != null || TryComp(uid, out body)) - { - _fixtures.FixtureUpdate(uid, manager: manager, body: body); - } - + FixtureUpdate(uid, body: body); _broadphase.Refilter(uid, fixture); } - public void RemoveCollisionMask(EntityUid uid, string fixtureId, Fixture fixture, int mask, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void RemoveCollisionMask(EntityUid uid, string fixtureId, Fixture fixture, int mask, PhysicsComponent? body = null) { if ((fixture.CollisionMask & mask) == 0x0) return; - if (!Resolve(uid, ref manager)) + if (!PhysicsQuery.Resolve(uid, ref body)) return; - DebugTools.Assert(manager.Fixtures.ContainsKey(fixtureId)); + DebugTools.Assert(body.Fixtures.ContainsKey(fixtureId)); fixture.CollisionMask &= ~mask; - if (body != null || TryComp(uid, out body)) - { - _fixtures.FixtureUpdate(uid, manager: manager, body: body); - } - + FixtureUpdate(uid, body: body); _broadphase.Refilter(uid, fixture); } - public void AddCollisionLayer(EntityUid uid, string fixtureId, Fixture fixture, int layer, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void AddCollisionLayer(EntityUid uid, string fixtureId, Fixture fixture, int layer, PhysicsComponent? body = null) { if ((fixture.CollisionLayer & layer) == layer) return; - if (!Resolve(uid, ref manager)) + if (!PhysicsQuery.Resolve(uid, ref body)) return; - DebugTools.Assert(manager.Fixtures.ContainsKey(fixtureId)); + DebugTools.Assert(body.Fixtures.ContainsKey(fixtureId)); fixture.CollisionLayer |= layer; - if (body != null || TryComp(uid, out body)) - { - _fixtures.FixtureUpdate(uid, manager: manager, body: body); - } - + FixtureUpdate(uid, body: body); _broadphase.Refilter(uid, fixture); } - public void SetCollisionLayer(EntityUid uid, string fixtureId, Fixture fixture, int layer, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void SetCollisionLayer(EntityUid uid, string fixtureId, Fixture fixture, int layer, PhysicsComponent? body = null) { if (fixture.CollisionLayer.Equals(layer)) return; - if (!Resolve(uid, ref manager)) + if (!PhysicsQuery.Resolve(uid, ref body)) return; fixture.CollisionLayer = layer; - if (body != null || TryComp(uid, out body)) - { - _fixtures.FixtureUpdate(uid, manager: manager, body: body); - } - + FixtureUpdate(uid, body: body); _broadphase.Refilter(uid, fixture); } - public void RemoveCollisionLayer(EntityUid uid, string fixtureId, Fixture fixture, int layer, FixturesComponent? manager = null, PhysicsComponent? body = null) + public void RemoveCollisionLayer(EntityUid uid, string fixtureId, Fixture fixture, int layer, PhysicsComponent? body = null) { - if ((fixture.CollisionLayer & layer) == 0x0 || !Resolve(uid, ref manager)) return; + if ((fixture.CollisionLayer & layer) == 0x0 || !PhysicsQuery.Resolve(uid, ref body)) + return; - DebugTools.Assert(manager.Fixtures.ContainsKey(fixtureId)); + DebugTools.Assert(body.Fixtures.ContainsKey(fixtureId)); fixture.CollisionLayer &= ~layer; - if (body != null || TryComp(uid, out body)) - { - _fixtures.FixtureUpdate(uid, manager: manager, body: body); - } - + FixtureUpdate(uid, body: body); _broadphase.Refilter(uid, fixture); } diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.FixturesChange.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.FixturesChange.cs index 4985b27ddf8..59ba343baef 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.FixturesChange.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.FixturesChange.cs @@ -15,7 +15,7 @@ private void OnChangeStartup(Entity ent, ref ComponentS { foreach (var (id, fixture) in ent.Comp.Fixtures) { - _fixtures.TryCreateFixture(ent.Owner, + TryCreateFixture(ent.Owner, fixture.Shape, id, fixture.Density, @@ -34,7 +34,7 @@ private void OnChangeShutdown(Entity ent, ref Component { foreach (var id in ent.Comp.Fixtures.Keys) { - _fixtures.DestroyFixture(ent.Owner, id); + DestroyFixture(ent.Owner, id); } } } diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Queries.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Queries.cs index 7beb786c484..ce024202137 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Queries.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Queries.cs @@ -91,12 +91,11 @@ public HashSet GetEntitiesIntersectingBody( int collisionMask, bool approximate = true, PhysicsComponent? body = null, - FixturesComponent? fixtureComp = null, TransformComponent? xform = null) { var entities = new HashSet(); - if (!Resolve(uid, ref body, ref fixtureComp, ref xform, false)) + if (!Resolve(uid, ref body, ref xform, false)) return entities; if (!_lookup.TryGetCurrentBroadphase(xform, out var broadphase)) @@ -104,7 +103,7 @@ public HashSet GetEntitiesIntersectingBody( var state = (body, entities); - foreach (var fixture in fixtureComp.Fixtures.Values) + foreach (var fixture in body.Fixtures.Values) { foreach (var proxy in fixture.Proxies) { @@ -498,10 +497,9 @@ public float IntersectRayPenetration(MapId mapId, CollisionRay ray, float maxLen public bool TryGetDistance(EntityUid uidA, EntityUid uidB, out float distance, TransformComponent? xformA = null, TransformComponent? xformB = null, - FixturesComponent? managerA = null, FixturesComponent? managerB = null, PhysicsComponent? bodyA = null, PhysicsComponent? bodyB = null) { - return TryGetNearest(uidA, uidB, out _, out _, out distance, xformA, xformB, managerA, managerB, bodyA, bodyB); + return TryGetNearest(uidA, uidB, out _, out _, out distance, xformA, xformB, bodyA, bodyB); } /// @@ -510,10 +508,9 @@ public bool TryGetDistance(EntityUid uidA, EntityUid uidB, public bool TryGetNearestPoints(EntityUid uidA, EntityUid uidB, out Vector2 pointA, out Vector2 pointB, TransformComponent? xformA = null, TransformComponent? xformB = null, - FixturesComponent? managerA = null, FixturesComponent? managerB = null, PhysicsComponent? bodyA = null, PhysicsComponent? bodyB = null) { - return TryGetNearest(uidA, uidB, out pointA, out pointB, out _, xformA, xformB, managerA, managerB, bodyA, bodyB); + return TryGetNearest(uidA, uidB, out pointA, out pointB, out _, xformA, xformB, bodyA, bodyB); } public bool TryGetNearest(EntityUid uidA, EntityUid uidB, @@ -521,16 +518,15 @@ public bool TryGetNearest(EntityUid uidA, EntityUid uidB, out Vector2 pointB, out float distance, Transform xfA, Transform xfB, - FixturesComponent? managerA = null, FixturesComponent? managerB = null, PhysicsComponent? bodyA = null, PhysicsComponent? bodyB = null) { pointA = Vector2.Zero; pointB = Vector2.Zero; - if (!Resolve(uidA, ref managerA, ref bodyA) || - !Resolve(uidB, ref managerB, ref bodyB) || - managerA.FixtureCount == 0 || - managerB.FixtureCount == 0) + if (!Resolve(uidA, ref bodyA) || + !Resolve(uidB, ref bodyB) || + bodyA.Fixtures.Count == 0 || + bodyB.Fixtures.Count == 0) { distance = 0f; return false; @@ -545,7 +541,7 @@ public bool TryGetNearest(EntityUid uidA, EntityUid uidB, }; // No requirement on collision being enabled so chainshapes will fail - foreach (var fixtureA in managerA.Fixtures.Values) + foreach (var fixtureA in bodyA.Fixtures.Values) { if (bodyA.Hard && !fixtureA.Hard) continue; @@ -554,7 +550,7 @@ public bool TryGetNearest(EntityUid uidA, EntityUid uidB, { input.ProxyA.Set(fixtureA.Shape, i); - foreach (var fixtureB in managerB.Fixtures.Values) + foreach (var fixtureB in bodyB.Fixtures.Values) { if (bodyB.Hard && !fixtureB.Hard) continue; @@ -584,7 +580,7 @@ public bool TryGetNearest(EntityUid uidA, EntityUid uidB, /// public bool TryGetNearest(EntityUid uid, MapCoordinates coordinates, out Vector2 point, out float distance, - TransformComponent? xformA = null, FixturesComponent? manager = null, PhysicsComponent? body = null) + TransformComponent? xformA = null, PhysicsComponent? body = null) { if (!Resolve(uid, ref xformA) || xformA.MapID != coordinates.MapId) @@ -596,8 +592,8 @@ public bool TryGetNearest(EntityUid uid, MapCoordinates coordinates, point = Vector2.Zero; - if (!Resolve(uid, ref manager, ref body) || - manager.FixtureCount == 0) + if (!Resolve(uid, ref body) || + body.Fixtures.Count == 0) { distance = 0f; return false; @@ -615,7 +611,7 @@ public bool TryGetNearest(EntityUid uid, MapCoordinates coordinates, var pointShape = new PhysShapeCircle(10 * float.Epsilon, Vector2.Zero); // No requirement on collision being enabled so chainshapes will fail - foreach (var fixtureA in manager.Fixtures.Values) + foreach (var fixtureA in body.Fixtures.Values) { // We ignore non-hard fixtures if there is at least one hard fixture (i.e., if the body is hard) if (body.Hard && !fixtureA.Hard) @@ -646,7 +642,6 @@ public bool TryGetNearest(EntityUid uidA, EntityUid uidB, out Vector2 pointB, out float distance, TransformComponent? xformA = null, TransformComponent? xformB = null, - FixturesComponent? managerA = null, FixturesComponent? managerB = null, PhysicsComponent? bodyA = null, PhysicsComponent? bodyB = null) { if (!Resolve(uidA, ref xformA) || !Resolve(uidB, ref xformB) || @@ -661,7 +656,7 @@ public bool TryGetNearest(EntityUid uidA, EntityUid uidB, var xfA = GetPhysicsTransform(uidA, xformA); var xfB = GetPhysicsTransform(uidB, xformB); - return TryGetNearest(uidA, uidB, out point, out pointB, out distance, xfA, xfB, managerA, managerB, bodyA, bodyB); + return TryGetNearest(uidA, uidB, out point, out pointB, out distance, xfA, xfB, bodyA, bodyB); } #endregion diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Shapes.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Shapes.cs index ddece0e5d0e..b561f32c3d9 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Shapes.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Shapes.cs @@ -5,23 +5,288 @@ using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Physics.Shapes; using Robust.Shared.Utility; namespace Robust.Shared.Physics.Systems; public abstract partial class SharedPhysicsSystem { + /// + /// Tests whether a particular point is contained in the shape. + /// + public bool TestPoint(IPhysShape shape, Transform xform, Vector2 worldPoint) + { + switch (shape) + { + case ChainShape: + case EdgeShape: + return false; + case PhysShapeAabb aabb: + // TODO: When we get actual AABBs it will be a stupid ez check, + var polygon = (PolygonShape) aabb; + return TestPoint(polygon, xform, worldPoint); + case PhysShapeCircle circle: + var center = xform.Position + Physics.Transform.Mul(xform.Quaternion2D, circle.Position); + var distance = worldPoint - center; + return Vector2.Dot(distance, distance) <= circle.Radius * circle.Radius; + case PolygonShape poly: + { + var pLocal = Physics.Transform.MulT(xform.Quaternion2D, worldPoint - xform.Position); + + for (var i = 0; i < poly.VertexCount; i++) + { + var dot = Vector2.Dot(poly.Normals[i], pLocal - poly.Vertices[i]); + if (dot > 0f) return false; + } + + return true; + } + case Polygon poly: + { + var pLocal = Physics.Transform.MulT(xform.Quaternion2D, worldPoint - xform.Position); + + for (var i = 0; i < poly.VertexCount; i++) + { + var dot = Vector2.Dot(poly.Normals[i], pLocal - poly.Vertices[i]); + if (dot > 0f) return false; + } + + return true; + } + default: + throw new ArgumentOutOfRangeException($"No implemented TestPoint for {shape.GetType()}"); + } + } + + public static MassData GetMassData(IPhysShape shape, float density) + { + var data = new MassData(); + + // Box2D just calls fixture.GetMassData which just calls the shape method anyway soooo + // we can just cut out the middle-man + switch (shape) + { + case ChainShape: + data.Mass = 0f; + data.Center = Vector2.Zero; + data.I = 0f; + break; + case EdgeShape edge: + data.Mass = 0.0f; + data.Center = (edge.Vertex1 + edge.Vertex2) * 0.5f; + data.I = 0.0f; + break; + case PhysShapeCircle circle: + // massData->mass = density * b2_pi * m_radius * m_radius; + data.Center = circle.Position; + + // inertia about the local origin + data.I = data.Mass * (0.5f * circle.Radius * circle.Radius + Vector2.Dot(circle.Position, circle.Position)); + break; + case PhysShapeAabb aabb: + var polygon = (PolygonShape) aabb; + GetMassData(polygon, ref data, density); + break; + case PolygonShape poly: + // Polygon mass, centroid, and inertia. + // Let rho be the polygon density in mass per unit area. + // Then: + // mass = rho * int(dA) + // centroid.x = (1/mass) * rho * int(x * dA) + // centroid.y = (1/mass) * rho * int(y * dA) + // I = rho * int((x*x + y*y) * dA) + // + // We can compute these integrals by summing all the integrals + // for each triangle of the polygon. To evaluate the integral + // for a single triangle, we make a change of variables to + // the (u,v) coordinates of the triangle: + // x = x0 + e1x * u + e2x * v + // y = y0 + e1y * u + e2y * v + // where 0 <= u && 0 <= v && u + v <= 1. + // + // We integrate u from [0,1-v] and then v from [0,1]. + // We also need to use the Jacobian of the transformation: + // D = cross(e1, e2) + // + // Simplification: triangle centroid = (1/3) * (p1 + p2 + p3) + // + // The rest of the derivation is handled by computer algebra. + + var count = poly.VertexCount; + DebugTools.Assert(count >= 3); + + Vector2 center = new(0.0f, 0.0f); + var area = 0.0f; + var I = 0.0f; + + // Get a reference point for forming triangles. + // Use the first vertex to reduce round-off errors. + var s = poly.Vertices[0]; + + const float k_inv3 = 1.0f / 3.0f; + + for (var i = 0; i < count; ++i) + { + // Triangle vertices. + var e1 = poly.Vertices[i] - s; + var e2 = i + 1 < count ? poly.Vertices[i+1] - s : poly.Vertices[0] - s; + + var D = Vector2Helpers.Cross(e1, e2); + + var triangleArea = 0.5f * D; + area += triangleArea; + + // Area weighted centroid + center += (e1 + e2) * triangleArea * k_inv3; + + float ex1 = e1.X, ey1 = e1.Y; + float ex2 = e2.X, ey2 = e2.Y; + + var intx2 = ex1*ex1 + ex2*ex1 + ex2*ex2; + var inty2 = ey1*ey1 + ey2*ey1 + ey2*ey2; + + I += (0.25f * k_inv3 * D) * (intx2 + inty2); + } + + // Total mass + data.Mass = density * area; + + // Center of mass + DebugTools.Assert(area > float.Epsilon); + center *= 1.0f / area; + data.Center = center + s; + + // Inertia tensor relative to the local origin (point s). + data.I = density * I; + + // Shift to center of mass then to original body origin. + data.I += data.Mass * (Vector2.Dot(data.Center, data.Center) - Vector2.Dot(center, center)); + break; + default: + throw new NotImplementedException($"Cannot get MassData for {shape} as it's not implemented!"); + } + + return data; + } + + public static void GetMassData(IPhysShape shape, ref MassData data, float density) + { + // Box2D just calls fixture.GetMassData which just calls the shape method anyway soooo + // we can just cut out the middle-man + switch (shape) + { + case ChainShape: + data.Mass = 0f; + data.Center = Vector2.Zero; + data.I = 0f; + break; + case EdgeShape edge: + data.Mass = 0.0f; + data.Center = (edge.Vertex1 + edge.Vertex2) * 0.5f; + data.I = 0.0f; + break; + case PhysShapeCircle circle: + data.Mass = density * MathF.PI * circle.Radius * circle.Radius; + data.Center = circle.Position; + + // inertia about the local origin + data.I = data.Mass * (0.5f * circle.Radius * circle.Radius + Vector2.Dot(circle.Position, circle.Position)); + break; + case PhysShapeAabb aabb: + var polygon = (PolygonShape) aabb; + GetMassData(polygon, ref data, density); + break; + case PolygonShape poly: + // Polygon mass, centroid, and inertia. + // Let rho be the polygon density in mass per unit area. + // Then: + // mass = rho * int(dA) + // centroid.x = (1/mass) * rho * int(x * dA) + // centroid.y = (1/mass) * rho * int(y * dA) + // I = rho * int((x*x + y*y) * dA) + // + // We can compute these integrals by summing all the integrals + // for each triangle of the polygon. To evaluate the integral + // for a single triangle, we make a change of variables to + // the (u,v) coordinates of the triangle: + // x = x0 + e1x * u + e2x * v + // y = y0 + e1y * u + e2y * v + // where 0 <= u && 0 <= v && u + v <= 1. + // + // We integrate u from [0,1-v] and then v from [0,1]. + // We also need to use the Jacobian of the transformation: + // D = cross(e1, e2) + // + // Simplification: triangle centroid = (1/3) * (p1 + p2 + p3) + // + // The rest of the derivation is handled by computer algebra. + + var count = poly.VertexCount; + DebugTools.Assert(count >= 3); + + Vector2 center = new(0.0f, 0.0f); + float area = 0.0f; + float I = 0.0f; + + // Get a reference point for forming triangles. + // Use the first vertex to reduce round-off errors. + var s = poly.Vertices[0]; + + const float k_inv3 = 1.0f / 3.0f; + + for (var i = 0; i < count; ++i) + { + // Triangle vertices. + var e1 = poly.Vertices[i] - s; + var e2 = i + 1 < count ? poly.Vertices[i+1] - s : poly.Vertices[0] - s; + + float D = Vector2Helpers.Cross(e1, e2); + + float triangleArea = 0.5f * D; + area += triangleArea; + + // Area weighted centroid + center += (e1 + e2) * triangleArea * k_inv3; + + float ex1 = e1.X, ey1 = e1.Y; + float ex2 = e2.X, ey2 = e2.Y; + + float intx2 = ex1*ex1 + ex2*ex1 + ex2*ex2; + float inty2 = ey1*ey1 + ey2*ey1 + ey2*ey2; + + I += (0.25f * k_inv3 * D) * (intx2 + inty2); + } + + // Total mass + data.Mass = density * area; + + // Center of mass + DebugTools.Assert(area > float.Epsilon); + center *= 1.0f / area; + data.Center = center + s; + + // Inertia tensor relative to the local origin (point s). + data.I = density * I; + + // Shift to center of mass then to original body origin. + data.I += data.Mass * (Vector2.Dot(data.Center, data.Center) - Vector2.Dot(center, center)); + break; + default: + throw new NotImplementedException($"Cannot get MassData for {shape} as it's not implemented!"); + } + } + public void SetRadius( EntityUid uid, string fixtureId, Fixture fixture, IPhysShape shape, float radius, - FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null) { - if (MathHelper.CloseTo(shape.Radius, radius) || !Resolve(uid, ref manager, ref body, ref xform)) + if (MathHelper.CloseTo(shape.Radius, radius) || !Resolve(uid, ref body, ref xform)) return; shape.Radius = radius; @@ -34,7 +299,7 @@ public void SetRadius( _lookup.CreateProxies(uid, fixtureId, fixture, xform, body); } - _fixtures.FixtureUpdate(uid, manager: manager, body: body); + FixtureUpdate(uid, body: body); } #region Circle @@ -46,12 +311,11 @@ public void SetPositionRadius( PhysShapeCircle shape, Vector2 position, float radius, - FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null) { if ((MathHelper.CloseTo(shape.Radius, radius) && shape.Position.EqualsApprox(position)) || - !Resolve(uid, ref manager, ref body, ref xform)) + !Resolve(uid, ref body, ref xform)) return; shape.Position = position; @@ -65,7 +329,7 @@ public void SetPositionRadius( _lookup.CreateProxies(uid, fixtureId, fixture, xform, body); } - Dirty(uid, manager); + Dirty(uid, body); } public void SetPosition( @@ -74,11 +338,10 @@ public void SetPosition( Fixture fixture, PhysShapeCircle circle, Vector2 position, - FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null) { - if (circle.Position.EqualsApprox(position) || !Resolve(uid, ref manager, ref body, ref xform)) + if (circle.Position.EqualsApprox(position) || !Resolve(uid, ref body, ref xform)) return; circle.Position = position; @@ -91,7 +354,7 @@ public void SetPosition( _lookup.CreateProxies(uid, fixtureId, fixture, xform, body); } - Dirty(uid, manager); + Dirty(uid, body); } #endregion @@ -107,11 +370,10 @@ public void SetVertices( Vector2 vertex1, Vector2 vertex2, Vector2 vertex3, - FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null) { - if (!Resolve(uid, ref manager, ref body, ref xform)) + if (!Resolve(uid, ref body, ref xform)) return; edge.Vertex0 = vertex0; @@ -127,7 +389,7 @@ public void SetVertices( _lookup.CreateProxies(uid, fixtureId, fixture, xform, body); } - _fixtures.FixtureUpdate(uid, manager: manager, body: body); + FixtureUpdate(uid, body: body); } #endregion @@ -140,11 +402,10 @@ public void SetVertices( Fixture fixture, PolygonShape poly, Vector2[] vertices, - FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null) { - if (!Resolve(uid, ref manager, ref body, ref xform)) + if (!Resolve(uid, ref body, ref xform)) return; poly.Set(vertices, vertices.Length); @@ -157,7 +418,7 @@ public void SetVertices( _lookup.CreateProxies(uid, fixtureId, fixture, xform, body); } - _fixtures.FixtureUpdate(uid, manager: manager, body: body); + FixtureUpdate(uid, body: body); } #endregion diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Velocities.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Velocities.cs index c607ad5c991..593b6e93843 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Velocities.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Velocities.cs @@ -217,8 +217,6 @@ private void HandleParentChangeVelocity(EntityUid uid, PhysicsComponent physics, // I guess the question becomes, what do you do with conservation of momentum in that case. I guess its the job // of the teleporter to select a velocity at the after the parent has changed. - FixturesComponent? manager = null; - // for the new velocities (that need to be updated), we can just use the existing function: var (newLinear, newAngular) = GetMapVelocities(uid, physics, xform); @@ -227,8 +225,8 @@ private void HandleParentChangeVelocity(EntityUid uid, PhysicsComponent physics, { // no previous parent --> simple // Old velocity + (old velocity - new velocity) - SetLinearVelocity(uid, physics.LinearVelocity * 2 - newLinear, manager: manager, body: physics); - SetAngularVelocity(uid, physics.AngularVelocity * 2 - newAngular, manager: manager, body: physics); + SetLinearVelocity(uid, physics.LinearVelocity * 2 - newLinear, body: physics); + SetAngularVelocity(uid, physics.AngularVelocity * 2 - newAngular, body: physics); return; } @@ -264,7 +262,7 @@ private void HandleParentChangeVelocity(EntityUid uid, PhysicsComponent physics, // Finally we can update the Velocities. linear velocity is already in terms of map-coordinates, so no // world-rotation is required - SetLinearVelocity(uid, physics.LinearVelocity + oldLinear - newLinear, manager: manager, body: physics); - SetAngularVelocity(uid, physics.AngularVelocity + oldAngular - newAngular, manager: manager, body: physics); + SetLinearVelocity(uid, physics.LinearVelocity + oldLinear - newLinear, body: physics); + SetAngularVelocity(uid, physics.AngularVelocity + oldAngular - newAngular, body: physics); } } diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.cs index 848f44fbccc..fbcb238228e 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.cs @@ -67,7 +67,6 @@ public abstract partial class SharedPhysicsSystem : EntitySystem public bool MetricsEnabled { get; protected set; } - private EntityQuery _fixturesQuery; protected EntityQuery PhysicsQuery; private EntityQuery _xformQuery; private EntityQuery _anchorQuery; @@ -100,7 +99,6 @@ public override void Initialize() _angularVelocityIndex = 10; - _fixturesQuery = GetEntityQuery(); PhysicsQuery = GetEntityQuery(); _xformQuery = GetEntityQuery(); _anchorQuery = GetEntityQuery(); @@ -117,21 +115,13 @@ public override void Initialize() SubscribeLocalEvent(OnPhysicsHandleState); InitializeIsland(); InitializeContacts(); + InitializeFixturesChange(); Subs.CVar(_cfg, CVars.AutoClearForces, OnAutoClearChange); Subs.CVar(_cfg, CVars.NetTickrate, UpdateSubsteps, true); Subs.CVar(_cfg, CVars.TargetMinimumTickrate, UpdateSubsteps, true); } - private void OnPhysicsShutdown(EntityUid uid, PhysicsComponent component, ComponentShutdown args) - { - SetCanCollide(uid, false, false, body: component); - DebugTools.Assert(!component.Awake); - - if (LifeStage(uid) <= EntityLifeStage.MapInitialized) - RemComp(uid); - } - private void OnCollisionChange(ref CollisionChangeEvent ev) { var uid = ev.BodyUid; @@ -256,10 +246,9 @@ private void OnGridAdd(GridAddEvent ev) return; var body = EnsureComp(guid); - var manager = EnsureComp(guid); - SetCanCollide(guid, true, manager: manager, body: body); - SetBodyType(guid, BodyType.Static, manager: manager, body: body); + SetCanCollide(guid, true, body: body); + SetBodyType(guid, BodyType.Static, body: body); } public override void Shutdown() diff --git a/Robust.UnitTesting/RobustUnitTest.cs b/Robust.UnitTesting/RobustUnitTest.cs index 15df76fe6c3..09c3297aef3 100644 --- a/Robust.UnitTesting/RobustUnitTest.cs +++ b/Robust.UnitTesting/RobustUnitTest.cs @@ -53,7 +53,6 @@ public abstract partial class RobustUnitTest typeof(PhysicsComponent), typeof(PhysicsMapComponent), typeof(BroadphaseComponent), - typeof(FixturesComponent), typeof(JointComponent), typeof(GridTreeComponent), typeof(MovedGridsComponent), @@ -122,7 +121,6 @@ public void BaseSetup() // and it was like this when I found it. systems.LoadExtraSystemType(); - systems.LoadExtraSystemType(); systems.LoadExtraSystemType(); systems.LoadExtraSystemType(); diff --git a/Robust.UnitTesting/Server/RobustServerSimulation.cs b/Robust.UnitTesting/Server/RobustServerSimulation.cs index 806ac4f3535..7d674c925c8 100644 --- a/Robust.UnitTesting/Server/RobustServerSimulation.cs +++ b/Robust.UnitTesting/Server/RobustServerSimulation.cs @@ -303,7 +303,6 @@ public ISimulation InitializeInstance() compFactory.RegisterClass(); compFactory.RegisterClass(); compFactory.RegisterClass(); - compFactory.RegisterClass(); compFactory.RegisterClass(); compFactory.RegisterClass(); compFactory.RegisterClass(); @@ -330,7 +329,6 @@ public ISimulation InitializeInstance() entitySystemMan.LoadExtraSystemType(); entitySystemMan.LoadExtraSystemType(); entitySystemMan.LoadExtraSystemType(); - entitySystemMan.LoadExtraSystemType(); entitySystemMan.LoadExtraSystemType(); entitySystemMan.LoadExtraSystemType(); entitySystemMan.LoadExtraSystemType(); diff --git a/Robust.UnitTesting/Shared/EntityLookup_Test.cs b/Robust.UnitTesting/Shared/EntityLookup_Test.cs index b33d37e4b90..36b6c1fca15 100644 --- a/Robust.UnitTesting/Shared/EntityLookup_Test.cs +++ b/Robust.UnitTesting/Shared/EntityLookup_Test.cs @@ -60,7 +60,7 @@ private EntityUid GetPhysicsEntity(IEntityManager entManager, MapCoordinates spa { var ent = entManager.SpawnEntity(null, spawnPos); var physics = entManager.AddComponent(ent); - entManager.System().TryCreateFixture(ent, new PhysShapeCircle(0.35f, Vector2.Zero), "fix1"); + entManager.System().TryCreateFixture(ent, new PhysShapeCircle(0.35f, Vector2.Zero), "fix1"); entManager.System().SetCanCollide(ent, true, body: physics); return ent; } diff --git a/Robust.UnitTesting/Shared/Map/GridFixtures_Tests.cs b/Robust.UnitTesting/Shared/Map/GridFixtures_Tests.cs index 2e997c3b291..aab7a67c67b 100644 --- a/Robust.UnitTesting/Shared/Map/GridFixtures_Tests.cs +++ b/Robust.UnitTesting/Shared/Map/GridFixtures_Tests.cs @@ -31,7 +31,7 @@ public void TestGridFixtureDeletion() var entManager = server.Resolve(); var grid = server.Resolve().CreateGridEntity(map.MapId); var mapSystem = entManager.System(); - var fixtures = entManager.GetComponent(grid); + var fixtures = entManager.GetComponent(grid); mapSystem.SetTiles(grid, new List<(Vector2i GridIndices, Tile Tile)>() { @@ -41,12 +41,12 @@ public void TestGridFixtureDeletion() (Vector2i.Up, new Tile(1)), }); - Assert.That(fixtures.FixtureCount, Is.EqualTo(2)); + Assert.That(fixtures.Fixtures.Count, Is.EqualTo(2)); Assert.That(grid.Comp.LocalAABB.Equals(new Box2(0f, 0f, 3f, 2f))); mapSystem.SetTile(grid, Vector2i.Up, Tile.Empty); - Assert.That(fixtures.FixtureCount, Is.EqualTo(1)); + Assert.That(fixtures.Fixtures.Count, Is.EqualTo(1)); Assert.That(grid.Comp.LocalAABB.Equals(new Box2(0f, 0f, 3f, 1f))); } @@ -68,33 +68,32 @@ await server.WaitAssertion(() => // Should be nothing if grid empty Assert.That(entManager.TryGetComponent(grid, out PhysicsComponent? gridBody)); - Assert.That(entManager.TryGetComponent(grid, out FixturesComponent? manager)); - Assert.That(manager!.FixtureCount, Is.EqualTo(0)); + Assert.That(gridBody!.Fixtures.Count, Is.EqualTo(0)); Assert.That(gridBody!.BodyType, Is.EqualTo(BodyType.Static)); // 1 fixture if we only ever update the 1 chunk mapSystem.SetTile(grid, Vector2i.Zero, new Tile(1)); - Assert.That(manager.FixtureCount, Is.EqualTo(1)); + Assert.That(gridBody.Fixtures.Count, Is.EqualTo(1)); // Also should only be a single tile. - var bounds = manager.Fixtures.First().Value.Shape.ComputeAABB(new Transform(Vector2.Zero, (float) Angle.Zero.Theta), 0); + var bounds = gridBody.Fixtures.First().Value.Shape.ComputeAABB(new Transform(Vector2.Zero, (float) Angle.Zero.Theta), 0); // Poly probably has Box2D's radius added to it so won't be a unit square Assert.That(MathHelper.CloseToPercent(Box2.Area(bounds), 1.0f, 0.1f)); // Now do 2 tiles (same chunk) mapSystem.SetTile(grid, new Vector2i(0, 1), new Tile(1)); - Assert.That(manager.FixtureCount, Is.EqualTo(1)); - bounds = manager.Fixtures.First().Value.Shape.ComputeAABB(new Transform(Vector2.Zero, (float) Angle.Zero.Theta), 0); + Assert.That(gridBody.Fixtures.Count, Is.EqualTo(1)); + bounds = gridBody.Fixtures.First().Value.Shape.ComputeAABB(new Transform(Vector2.Zero, (float) Angle.Zero.Theta), 0); // Even if we add a new tile old fixture should stay the same if they don't connect. Assert.That(MathHelper.CloseToPercent(Box2.Area(bounds), 2.0f, 0.1f)); // If we add a new chunk should be 2 now mapSystem.SetTile(grid, new Vector2i(0, -1), new Tile(1)); - Assert.That(manager.FixtureCount, Is.EqualTo(2)); + Assert.That(gridBody.Fixtures.Count, Is.EqualTo(2)); - physSystem.SetLinearVelocity(grid, Vector2.One, manager: manager, body: gridBody); + physSystem.SetLinearVelocity(grid, Vector2.One, body: gridBody); Assert.That(gridBody.LinearVelocity.Length, Is.EqualTo(0f)); }); } diff --git a/Robust.UnitTesting/Shared/Physics/BroadphaseNetworkingTest.cs b/Robust.UnitTesting/Shared/Physics/BroadphaseNetworkingTest.cs index 61248c07e55..0a086881d79 100644 --- a/Robust.UnitTesting/Shared/Physics/BroadphaseNetworkingTest.cs +++ b/Robust.UnitTesting/Shared/Physics/BroadphaseNetworkingTest.cs @@ -40,7 +40,6 @@ public async Task TestBroadphaseNetworking() var confMan = server.ResolveDependency(); var cPlayerMan = client.ResolveDependency(); var sPlayerMan = server.ResolveDependency(); - var fixturesSystem = sEntMan.EntitySysManager.GetEntitySystem(); var physicsSystem = sEntMan.EntitySysManager.GetEntitySystem(); var mapSystem = sEntMan.EntitySysManager.GetEntitySystem(); @@ -80,7 +79,7 @@ await server.WaitPost(() => var shape = new PolygonShape(); shape.SetAsBox(0.5f, 0.5f); var fixture = new Fixture(shape, 0, 0, true); - fixturesSystem.CreateFixture(player, "fix1", fixture, body: physics, xform: xform); + physicsSystem.CreateFixture(player, "fix1", fixture, body: physics, xform: xform); physicsSystem.SetCanCollide(player, true, body: physics); physicsSystem.SetBodyType(player, BodyType.Dynamic); Assert.That(physics.CanCollide); diff --git a/Robust.UnitTesting/Shared/Physics/Broadphase_Test.cs b/Robust.UnitTesting/Shared/Physics/Broadphase_Test.cs index 85b1be0f659..4876571312b 100644 --- a/Robust.UnitTesting/Shared/Physics/Broadphase_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/Broadphase_Test.cs @@ -60,7 +60,6 @@ public void ReparentBroadphase() var sim = RobustServerSimulation.NewSimulation().InitializeInstance(); var entManager = sim.Resolve(); var mapManager = sim.Resolve(); - var fixturesSystem = entManager.EntitySysManager.GetEntitySystem(); var physicsSystem = entManager.EntitySysManager.GetEntitySystem(); var mapSys = entManager.System(); var xformSys = entManager.System(); @@ -84,7 +83,7 @@ public void ReparentBroadphase() var shape = new PolygonShape(); shape.SetAsBox(0.5f, 0.5f); var fixture = new Fixture(shape, 0, 0, true); - fixturesSystem.CreateFixture(ent, "fix1", fixture, body: physics, xform: xform); + physicsSystem.CreateFixture(ent, "fix1", fixture, body: physics, xform: xform); physicsSystem.SetCanCollide(ent, true, body: physics); Assert.That(physics.CanCollide); @@ -197,7 +196,6 @@ public void EntMapChangeRecursiveUpdate() var lookup = system.GetEntitySystem(); var xforms = system.GetEntitySystem(); var physSystem = system.GetEntitySystem(); - var fixtures = system.GetEntitySystem(); var mapSys = entManager.System(); // setup maps @@ -222,12 +220,11 @@ public void EntMapChangeRecursiveUpdate() var child = entManager.SpawnEntity(null, new EntityCoordinates(parent, Vector2.Zero)); var childXform = entManager.GetComponent(child); var childBody = entManager.AddComponent(child); - var childFixtures = entManager.GetComponent(child); // enable collision for the child var shape = new PolygonShape(); shape.SetAsBox(0.5f, 0.5f); - fixtures.CreateFixture(child, "fix1", new Fixture(shape, 0, 0, false), body: childBody, xform: childXform); + physSystem.CreateFixture(child, "fix1", new Fixture(shape, 0, 0, false), body: childBody, xform: childXform); physSystem.SetCanCollide(child, true, body: childBody); Assert.That(childBody.CanCollide); @@ -243,7 +240,7 @@ public void EntMapChangeRecursiveUpdate() Assert.That(lookup.FindBroadphase(child), Is.EqualTo(broadphase)); Assert.That(parentXform.Broadphase == new BroadphaseData(map, default, false, false)); Assert.That(childXform.Broadphase == new BroadphaseData(map, map, true, true)); - Assert.That(physMap.MoveBuffer.ContainsKey(childFixtures.Fixtures.First().Value.Proxies.First())); + Assert.That(physMap.MoveBuffer.ContainsKey(childBody.Fixtures.First().Value.Proxies.First())); var otherPhysMap = entManager.GetComponent(otherMap); Assert.That(otherPhysMap.MoveBuffer.Count == 0); }; @@ -278,7 +275,7 @@ public void EntMapChangeRecursiveUpdate() Assert.That(lookup.FindBroadphase(child), Is.EqualTo(broadphase)); Assert.That(parentXform.Broadphase == new BroadphaseData(grid, default, false, false)); Assert.That(childXform.Broadphase == new BroadphaseData(grid, map, true, true)); - Assert.That(physMap.MoveBuffer.ContainsKey(childFixtures.Fixtures.First().Value.Proxies.First())); + Assert.That(physMap.MoveBuffer.ContainsKey(childBody.Fixtures.First().Value.Proxies.First())); var otherPhysMap = entManager.GetComponent(otherMap); Assert.That(otherPhysMap.MoveBuffer.Count == 0); }; @@ -326,7 +323,6 @@ public void BroadphaseRecursiveNullspaceUpdate() var xformSystem = system.GetEntitySystem(); var physSystem = system.GetEntitySystem(); var lookup = system.GetEntitySystem(); - var fixtures = system.GetEntitySystem(); var (mapUid, mapId) = sim.CreateMap(); var mapBroadphase = entManager.GetComponent(mapUid); @@ -343,7 +339,7 @@ public void BroadphaseRecursiveNullspaceUpdate() var shape = new PolygonShape(); shape.SetAsBox(0.5f, 0.5f); - fixtures.CreateFixture(child1, "fix1", new Fixture(shape, 0, 0, false), body: child1Body, xform: child1Xform); + physSystem.CreateFixture(child1, "fix1", new Fixture(shape, 0, 0, false), body: child1Body, xform: child1Xform); physSystem.SetCanCollide(child1, true, body: child1Body); Assert.That(child1Body.CanCollide); diff --git a/Robust.UnitTesting/Shared/Physics/Collision_Test.cs b/Robust.UnitTesting/Shared/Physics/Collision_Test.cs index ebee07a62bc..ad9b49edf96 100644 --- a/Robust.UnitTesting/Shared/Physics/Collision_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/Collision_Test.cs @@ -44,20 +44,19 @@ public void TestHardCollidable() var sim = RobustServerSimulation.NewSimulation().InitializeInstance(); var entManager = sim.Resolve(); - var fixtures = entManager.System(); var physics = entManager.System(); var map = sim.CreateMap(); var bodyAUid = entManager.SpawnAttachedTo(null, new EntityCoordinates(map.Uid, Vector2.Zero)); var bodyBUid = entManager.SpawnAttachedTo(null, new EntityCoordinates(map.Uid, Vector2.Zero)); - var bodyA = entManager.AddComponent(bodyAUid); - var bodyB = entManager.AddComponent(bodyBUid); + entManager.AddComponent(bodyAUid); + entManager.AddComponent(bodyBUid); Assert.That(!physics.IsHardCollidable(bodyAUid, bodyBUid)); - fixtures.CreateFixture(bodyAUid, "fix1", new Fixture(new PhysShapeCircle(0.5f), 1, 1, true)); - fixtures.CreateFixture(bodyBUid, "fix1", new Fixture(new PhysShapeCircle(0.5f), 1, 1, true)); + physics.CreateFixture(bodyAUid, "fix1", new Fixture(new PhysShapeCircle(0.5f), 1, 1, true)); + physics.CreateFixture(bodyBUid, "fix1", new Fixture(new PhysShapeCircle(0.5f), 1, 1, true)); Assert.That(physics.IsHardCollidable(bodyAUid, bodyBUid)); } @@ -98,7 +97,7 @@ public void TestCollision() const float mass = 4.0f * hx * hy; var inertia = (mass / 3.0f) * (hx * hx + hy * hy) + mass * Vector2.Dot(center, center); - var massData1 = FixtureSystem.GetMassData(polygon1, 1f); + var massData1 = SharedPhysicsSystem.GetMassData(polygon1, 1f); Assert.That(MathF.Abs(massData1.Center.X - center.X), Is.LessThan(absTol + relTol * Math.Abs(center.X))); Assert.That(MathF.Abs(massData1.Center.Y - center.Y), Is.LessThan(absTol + relTol * Math.Abs(center.Y))); @@ -106,7 +105,7 @@ public void TestCollision() // Assert.That(MathF.Abs(massData1.Mass - mass), Is.LessThan(20.0f * (absTol + relTol * mass))); // Assert.That(MathF.Abs(massData1.I - inertia), Is.LessThan(40.0f * (absTol + relTol * inertia))); - var massData2 = FixtureSystem.GetMassData(polygon2, 1f); + var massData2 = SharedPhysicsSystem.GetMassData(polygon2, 1f); Assert.That(MathF.Abs(massData2.Center.X - center.X), Is.LessThan(absTol + relTol * Math.Abs(center.X))); Assert.That(MathF.Abs(massData2.Center.Y - center.Y), Is.LessThan(absTol + relTol * Math.Abs(center.Y))); @@ -123,7 +122,6 @@ public void CrossMapContacts() var sim = RobustServerSimulation.NewSimulation().InitializeInstance(); var entManager = sim.Resolve(); var mapManager = sim.Resolve(); - var fixtures = entManager.System(); var physics = entManager.System(); var xformSystem = entManager.System(); var mapId = sim.CreateMap().MapId; @@ -137,8 +135,8 @@ public void CrossMapContacts() var body2 = entManager.AddComponent(ent2); physics.SetBodyType(ent2, BodyType.Dynamic, body: body2); - fixtures.CreateFixture(ent1, "fix1", new Fixture(new PhysShapeCircle(1f), 1, 0, true), body: body1); - fixtures.CreateFixture(ent2, "fix1", new Fixture(new PhysShapeCircle(1f), 0, 1, true), body: body2); + physics.CreateFixture(ent1, "fix1", new Fixture(new PhysShapeCircle(1f), 1, 0, true), body: body1); + physics.CreateFixture(ent2, "fix1", new Fixture(new PhysShapeCircle(1f), 0, 1, true), body: body2); physics.WakeBody(ent1, body: body1); physics.WakeBody(ent2, body: body2); diff --git a/Robust.UnitTesting/Shared/Physics/FixtureShape_Test.cs b/Robust.UnitTesting/Shared/Physics/FixtureShape_Test.cs index 8413454861f..e4a7ddbb3ef 100644 --- a/Robust.UnitTesting/Shared/Physics/FixtureShape_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/FixtureShape_Test.cs @@ -1,6 +1,7 @@ using System; using System.Numerics; using NUnit.Framework; +using Robust.Server.GameObjects; using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Collision.Shapes; @@ -9,15 +10,15 @@ namespace Robust.UnitTesting.Shared.Physics { [TestFixture] - [TestOf(typeof(FixtureSystem))] + [TestOf(typeof(SharedPhysicsSystem))] public sealed class FixtureShape_Test : RobustUnitTest { - private FixtureSystem _shapeManager = default!; + private SharedPhysicsSystem _shapeManager = default!; [OneTimeSetUp] public void Setup() { - _shapeManager = new FixtureSystem(); + _shapeManager = new PhysicsSystem(); } [Test] diff --git a/Robust.UnitTesting/Shared/Physics/Fixtures_Test.cs b/Robust.UnitTesting/Shared/Physics/Fixtures_Test.cs index 9c25a17c596..56afec987cc 100644 --- a/Robust.UnitTesting/Shared/Physics/Fixtures_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/Fixtures_Test.cs @@ -22,7 +22,6 @@ public void SetDensity() var entManager = sim.Resolve(); var mapManager = sim.Resolve(); var sysManager = sim.Resolve(); - var fixturesSystem = sysManager.GetEntitySystem(); var physicsSystem = sysManager.GetEntitySystem(); var map = sim.CreateMap().MapId; @@ -30,7 +29,7 @@ public void SetDensity() var body = entManager.AddComponent(ent); physicsSystem.SetBodyType(ent, BodyType.Dynamic, body: body); var fixture = new Fixture(); - fixturesSystem.CreateFixture(ent, "fix1", fixture); + physicsSystem.CreateFixture(ent, "fix1", fixture); physicsSystem.SetDensity(ent, "fix1", fixture, 10f); Assert.That(fixture.Density, Is.EqualTo(10f)); diff --git a/Robust.UnitTesting/Shared/Physics/GridMovement_Test.cs b/Robust.UnitTesting/Shared/Physics/GridMovement_Test.cs index 0dddfa2facf..312962504da 100644 --- a/Robust.UnitTesting/Shared/Physics/GridMovement_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/GridMovement_Test.cs @@ -24,7 +24,6 @@ public async Task TestFindGridContacts() // Checks that FindGridContacts succesfully overlaps a grid + map broadphase physics body var systems = server.ResolveDependency(); - var fixtureSystem = systems.GetEntitySystem(); var mapManager = server.ResolveDependency(); var entManager = server.ResolveDependency(); var physSystem = systems.GetEntitySystem(); @@ -38,16 +37,16 @@ await server.WaitAssertion(() => // Setup 1 body on grid, 1 body off grid, and assert that it's all gucci. mapSystem.SetTile(grid, Vector2i.Zero, new Tile(1)); - var fixtures = entManager.GetComponent(grid); - Assert.That(fixtures.FixtureCount, Is.EqualTo(1)); + var fixtures = entManager.GetComponent(grid); + Assert.That(fixtures.Fixtures.Count, Is.EqualTo(1)); var onGrid = entManager.SpawnEntity(null, new EntityCoordinates(grid, 0.5f, 0.5f )); var onGridBody = entManager.AddComponent(onGrid); physSystem.SetBodyType(onGrid, BodyType.Dynamic, body: onGridBody); var shapeA = new PolygonShape(); shapeA.SetAsBox(0.5f, 0.5f); - fixtureSystem.CreateFixture(onGrid, "fix1", new Fixture(shapeA, 1, 0, false), body: onGridBody); - Assert.That(fixtureSystem.GetFixtureCount(onGrid), Is.EqualTo(1)); + physSystem.CreateFixture(onGrid, "fix1", new Fixture(shapeA, 1, 0, false), body: onGridBody); + Assert.That(onGridBody.Fixtures.Count, Is.EqualTo(1)); Assert.That(entManager.GetComponent(onGrid).ParentUid, Is.EqualTo(grid.Owner)); physSystem.WakeBody(onGrid, body: onGridBody); Assert.That(onGridBody.Awake); @@ -57,8 +56,8 @@ await server.WaitAssertion(() => physSystem.SetBodyType(offGrid, BodyType.Dynamic, body: offGridBody); var shapeB = new PolygonShape(); shapeB.SetAsBox(0.5f, 0.5f); - fixtureSystem.CreateFixture(offGrid, "fix1", new Fixture(shapeB, 0, 1, false), body: offGridBody); - Assert.That(fixtureSystem.GetFixtureCount(offGrid), Is.EqualTo(1)); + physSystem.CreateFixture(offGrid, "fix1", new Fixture(shapeB, 0, 1, false), body: offGridBody); + Assert.That(offGridBody.Fixtures.Count, Is.EqualTo(1)); Assert.That(entManager.GetComponent(offGrid).ParentUid, Is.Not.EqualTo((grid.Owner))); physSystem.WakeBody(offGrid, body: offGridBody); Assert.That(offGridBody.Awake); diff --git a/Robust.UnitTesting/Shared/Physics/JointDeletion_Test.cs b/Robust.UnitTesting/Shared/Physics/JointDeletion_Test.cs index 099ad25694f..7e226ed752c 100644 --- a/Robust.UnitTesting/Shared/Physics/JointDeletion_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/JointDeletion_Test.cs @@ -23,11 +23,9 @@ public async Task JointDeletionTest() await server.WaitIdleAsync(); var entManager = server.ResolveDependency(); - var mapManager = server.ResolveDependency(); var susManager = server.ResolveDependency(); var jointSystem = susManager.GetEntitySystem(); var broadphase = susManager.GetEntitySystem(); - var fixSystem = susManager.GetEntitySystem(); var physicsSystem = susManager.GetEntitySystem(); DistanceJoint joint = default!; @@ -45,18 +43,16 @@ await server.WaitPost(() => body1 = entManager.AddComponent(ent1); body2 = entManager.AddComponent(ent2); - var manager1 = entManager.EnsureComponent(ent1); - var manager2 = entManager.EnsureComponent(ent2); entManager.AddComponent(ent2); - physicsSystem.SetBodyType(ent1, BodyType.Dynamic, manager: manager1, body: body1); - physicsSystem.SetBodyType(ent2, BodyType.Dynamic, manager: manager2, body: body2); - physicsSystem.SetCanCollide(ent1, true, manager: manager1, body: body1); - physicsSystem.SetCanCollide(ent2, true, manager: manager2, body: body2); + physicsSystem.SetBodyType(ent1, BodyType.Dynamic, body: body1); + physicsSystem.SetBodyType(ent2, BodyType.Dynamic, body: body2); + physicsSystem.SetCanCollide(ent1, true, body: body1); + physicsSystem.SetCanCollide(ent2, true, body: body2); var shape = new PolygonShape(); shape.SetAsBox(0.5f, 0.5f); - fixSystem.CreateFixture(ent2, "fix1", new Fixture(shape, 0, 0, false), manager: manager2, body: body2); + physicsSystem.CreateFixture(ent2, "fix1", new Fixture(shape, 0, 0, false), body: body2); joint = jointSystem.CreateDistanceJoint(ent1, ent2, id: "distance-joint"); joint.CollideConnected = false; diff --git a/Robust.UnitTesting/Shared/Physics/Joints_Test.cs b/Robust.UnitTesting/Shared/Physics/Joints_Test.cs index cd68b31bf9a..8f14a3fa09c 100644 --- a/Robust.UnitTesting/Shared/Physics/Joints_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/Joints_Test.cs @@ -66,7 +66,6 @@ public void JointsCollidableTest() var server = factory.InitializeInstance(); var entManager = server.Resolve(); var mapManager = server.Resolve(); - var fixtureSystem = entManager.EntitySysManager.GetEntitySystem(); var jointSystem = entManager.EntitySysManager.GetEntitySystem(); var broadphaseSystem = entManager.EntitySysManager.GetEntitySystem(); var physicsSystem = server.Resolve().GetEntitySystem(); @@ -77,14 +76,12 @@ public void JointsCollidableTest() var ent2 = entManager.SpawnEntity(null, new MapCoordinates(Vector2.Zero, mapId)); var body1 = entManager.AddComponent(ent1); var body2 = entManager.AddComponent(ent2); - var manager1 = entManager.EnsureComponent(ent1); - var manager2 = entManager.EnsureComponent(ent2); - physicsSystem.SetBodyType(ent1, BodyType.Dynamic, manager: manager1, body: body1); - physicsSystem.SetBodyType(ent2, BodyType.Dynamic, manager: manager2, body: body2); + physicsSystem.SetBodyType(ent1, BodyType.Dynamic, body: body1); + physicsSystem.SetBodyType(ent2, BodyType.Dynamic, body: body2); - fixtureSystem.CreateFixture(ent1, "fix1", new Fixture(new PhysShapeCircle(0.1f), 1, 1, false), manager: manager1, body: body1); - fixtureSystem.CreateFixture(ent2, "fix1", new Fixture(new PhysShapeCircle(0.1f), 1, 1, false), manager: manager2, body: body2); + physicsSystem.CreateFixture(ent1, "fix1", new Fixture(new PhysShapeCircle(0.1f), 1, 1, false), body: body1); + physicsSystem.CreateFixture(ent2, "fix1", new Fixture(new PhysShapeCircle(0.1f), 1, 1, false), body: body2); var joint = jointSystem.CreateDistanceJoint(ent1, ent2); Assert.That(joint.CollideConnected, Is.EqualTo(true)); diff --git a/Robust.UnitTesting/Shared/Physics/PhysicsComponent_Test.cs b/Robust.UnitTesting/Shared/Physics/PhysicsComponent_Test.cs index 291329efe72..bc4abddff92 100644 --- a/Robust.UnitTesting/Shared/Physics/PhysicsComponent_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/PhysicsComponent_Test.cs @@ -24,9 +24,6 @@ public async Task TestPointLinearImpulse() var server = StartServer(); await server.WaitIdleAsync(); var entManager = server.ResolveDependency(); - var mapManager = server.ResolveDependency(); - var fixtureSystem = server.ResolveDependency() - .GetEntitySystem(); var physicsSystem = server.ResolveDependency() .GetEntitySystem(); @@ -37,7 +34,7 @@ await server.WaitAssertion(() => var box = entManager.AddComponent(boxEnt); var poly = new PolygonShape(); poly.SetAsBox(0.5f, 0.5f); - fixtureSystem.CreateFixture(boxEnt, "fix1", new Fixture(poly, 0, 0, false), body: box); + physicsSystem.CreateFixture(boxEnt, "fix1", new Fixture(poly, 0, 0, false), body: box); physicsSystem.SetFixedRotation(boxEnt, false, body: box); physicsSystem.SetBodyType(boxEnt, BodyType.Dynamic, body: box); Assert.That(box.InvI, Is.GreaterThan(0f)); diff --git a/Robust.UnitTesting/Shared/Physics/PhysicsMap_Test.cs b/Robust.UnitTesting/Shared/Physics/PhysicsMap_Test.cs index 5f3bbbb3572..731c8c4dabf 100644 --- a/Robust.UnitTesting/Shared/Physics/PhysicsMap_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/PhysicsMap_Test.cs @@ -26,7 +26,6 @@ public void RecursiveMapChange() var mapManager = sim.Resolve(); var system = entManager.EntitySysManager; var physSystem = system.GetEntitySystem(); - var fixtureSystem = system.GetEntitySystem(); var xformSystem = system.GetEntitySystem(); var mapId = sim.CreateMap().MapId; @@ -43,7 +42,7 @@ public void RecursiveMapChange() physSystem.SetBodyType(parent, BodyType.Dynamic); physSystem.SetSleepingAllowed(parent, parentBody, false); - fixtureSystem.CreateFixture(parent, "fix1", new Fixture(new PhysShapeCircle(0.5f), 0, 0, false), body: parentBody); + physSystem.CreateFixture(parent, "fix1", new Fixture(new PhysShapeCircle(0.5f), 0, 0, false), body: parentBody); physSystem.WakeBody(parent); Assert.That(physicsMap.AwakeBodies, Does.Contain(parentBody)); @@ -52,7 +51,7 @@ public void RecursiveMapChange() physSystem.SetBodyType(child, BodyType.Dynamic); physSystem.SetSleepingAllowed(child, childBody, false); - fixtureSystem.CreateFixture(child, "fix1", new Fixture(new PhysShapeCircle(0.5f), 0, 0, false), body: childBody); + physSystem.CreateFixture(child, "fix1", new Fixture(new PhysShapeCircle(0.5f), 0, 0, false), body: childBody); physSystem.WakeBody(child, body: childBody); Assert.That(physicsMap.AwakeBodies, Does.Contain(childBody)); diff --git a/Robust.UnitTesting/Shared/Physics/RayCast_Test.cs b/Robust.UnitTesting/Shared/Physics/RayCast_Test.cs index d33317166e7..d710c0953b6 100644 --- a/Robust.UnitTesting/Shared/Physics/RayCast_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/RayCast_Test.cs @@ -133,7 +133,7 @@ private void Setup(ISimulation sim, out MapId mapId) var physics = entManager.AddComponent(wall); var poly = new PolygonShape(); poly.SetAsBox(Box2.UnitCentered); - entManager.System().CreateFixture(wall, "fix1", new Fixture(poly, 1, 1, true)); + entManager.System().CreateFixture(wall, "fix1", new Fixture(poly, 1, 1, true)); entManager.System().SetCanCollide(wall, true, body: physics); Assert.That(physics.CanCollide); diff --git a/Robust.UnitTesting/Shared/Physics/Stack_Test.cs b/Robust.UnitTesting/Shared/Physics/Stack_Test.cs index 9412df334bb..21595d50bad 100644 --- a/Robust.UnitTesting/Shared/Physics/Stack_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/Stack_Test.cs @@ -52,9 +52,7 @@ public async Task TestBoxStack() await server.WaitIdleAsync(); var entityManager = server.ResolveDependency(); - var mapManager = server.ResolveDependency(); var entitySystemManager = server.ResolveDependency(); - var fixtureSystem = entitySystemManager.GetEntitySystem(); var physSystem = entitySystemManager.GetEntitySystem(); var gravSystem = entitySystemManager.GetEntitySystem(); MapId mapId; @@ -71,15 +69,14 @@ await server.WaitPost(() => var groundUid = entityManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); var ground = entityManager.AddComponent(groundUid); - var groundManager = entityManager.EnsureComponent(groundUid); var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0)); - fixtureSystem.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 1, 1, true), manager: groundManager, body: ground); + physSystem.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 1, 1, true), body: ground); var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10)); - fixtureSystem.CreateFixture(groundUid, "fix2", new Fixture(vertical, 1, 1, true), manager: groundManager, body: ground); + physSystem.CreateFixture(groundUid, "fix2", new Fixture(vertical, 1, 1, true), body: ground); - physSystem.WakeBody(groundUid, manager: groundManager, body: ground); + physSystem.WakeBody(groundUid, body: ground); var xs = new[] { @@ -95,9 +92,8 @@ await server.WaitPost(() => var boxUid = entityManager.SpawnEntity(null, new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 2.1f * i), mapId)); var box = entityManager.AddComponent(boxUid); - var manager = entityManager.EnsureComponent(boxUid); - physSystem.SetBodyType(boxUid, BodyType.Dynamic, manager: manager, body: box); + physSystem.SetBodyType(boxUid, BodyType.Dynamic, body: box); var poly = new PolygonShape(0.001f); poly.Set(new List() { @@ -107,8 +103,8 @@ await server.WaitPost(() => new(-0.5f, -0.5f), }); - fixtureSystem.CreateFixture(boxUid, "fix1", new Fixture(poly, 1, 1, true), manager: manager, body: box); - physSystem.WakeBody(boxUid, manager: manager, body: box); + physSystem.CreateFixture(boxUid, "fix1", new Fixture(poly, 1, 1, true), body: box); + physSystem.WakeBody(boxUid, body: box); bodies[j * rowCount + i] = box; } @@ -157,9 +153,7 @@ public async Task TestCircleStack() await server.WaitIdleAsync(); var entityManager = server.ResolveDependency(); - var mapManager = server.ResolveDependency(); var entitySystemManager = server.ResolveDependency(); - var fixtureSystem = entitySystemManager.GetEntitySystem(); var physSystem = entitySystemManager.GetEntitySystem(); var gravSystem = entitySystemManager.GetEntitySystem(); MapId mapId; @@ -176,15 +170,14 @@ await server.WaitPost(() => var groundUid = entityManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); var ground = entityManager.AddComponent(groundUid); - var groundManager = entityManager.EnsureComponent(groundUid); var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0)); - fixtureSystem.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 1, 1, true), manager: groundManager, body: ground); + physSystem.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 1, 1, true), body: ground); var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10)); - fixtureSystem.CreateFixture(groundUid, "fix2", new Fixture(vertical, 1, 1, true), manager: groundManager, body: ground); + physSystem.CreateFixture(groundUid, "fix2", new Fixture(vertical, 1, 1, true), body: ground); - physSystem.WakeBody(groundUid, manager: groundManager, body: ground); + physSystem.WakeBody(groundUid, body: ground); var xs = new[] { @@ -202,13 +195,12 @@ await server.WaitPost(() => var circleUid = entityManager.SpawnEntity(null, new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 1.1f * i), mapId)); var circle = entityManager.AddComponent(circleUid); - var manager = entityManager.EnsureComponent(circleUid); physSystem.SetLinearDamping(circleUid, circle, 0.05f); - physSystem.SetBodyType(circleUid, BodyType.Dynamic, manager: manager, body: circle); + physSystem.SetBodyType(circleUid, BodyType.Dynamic, body: circle); shape = new PhysShapeCircle(0.5f); - fixtureSystem.CreateFixture(circleUid, "fix1", new Fixture(shape, 1, 1, true), manager: manager, body: circle); - physSystem.WakeBody(circleUid, manager: manager, body: circle); + physSystem.CreateFixture(circleUid, "fix1", new Fixture(shape, 1, 1, true), body: circle); + physSystem.WakeBody(circleUid, body: circle); bodies[j * rowCount + i] = circle; } From 09743a7a3b1a21ae198b844493d744b8fb8f46ab Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Sun, 22 Dec 2024 14:14:46 +1100 Subject: [PATCH 2/3] Fixes --- .../EntityManager.ComponentDeltas.cs | 19 ++++ .../Systems/SharedGridFixtureSystem.cs | 9 +- .../Components/PhysicsComponentState.cs | 88 ++++++++++--------- Robust.Shared/Physics/Dynamics/Fixture.cs | 10 +-- .../Systems/SharedPhysicsSystem.Components.cs | 61 +++++++++---- .../Systems/SharedPhysicsSystem.Fixtures.cs | 6 +- .../SharedPhysicsSystem.FixturesChange.cs | 40 --------- .../Physics/Systems/SharedPhysicsSystem.cs | 9 +- .../Shared/Physics/CollisionWake_Test.cs | 1 - .../Shared/Physics/MapVelocity_Test.cs | 1 - 10 files changed, 126 insertions(+), 118 deletions(-) delete mode 100644 Robust.Shared/Physics/Systems/SharedPhysicsSystem.FixturesChange.cs diff --git a/Robust.Shared/GameObjects/EntityManager.ComponentDeltas.cs b/Robust.Shared/GameObjects/EntityManager.ComponentDeltas.cs index b560262d0ed..0d9ffc3c052 100644 --- a/Robust.Shared/GameObjects/EntityManager.ComponentDeltas.cs +++ b/Robust.Shared/GameObjects/EntityManager.ComponentDeltas.cs @@ -1,3 +1,4 @@ +using System; using Robust.Shared.Timing; namespace Robust.Shared.GameObjects; @@ -22,6 +23,24 @@ public uint GetModifiedFields(IComponentDelta delta, GameTick fromTick) return fields; } + public int GetNetworkedFieldIndex(IComponentDelta delta, string fieldName) + { + var compReg = ComponentFactory.GetRegistration(delta); + + return GetNetworkedFieldIndex(compReg, fieldName); + } + + public int GetNetworkedFieldIndex(ComponentRegistration compReg, string fieldName) + { + if (!compReg.NetworkedFieldLookup.TryGetValue(fieldName, out var idx)) + { + throw new InvalidOperationException( + $"Tried to get networked field index for component {compReg.Type} that doesn't implement it"); + } + + return idx; + } + public void DirtyField(EntityUid uid, IComponentDelta comp, string fieldName, MetaDataComponent? metadata = null) { var compReg = ComponentFactory.GetRegistration(comp); diff --git a/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs b/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs index 9cd19a5602f..e77a033a44a 100644 --- a/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs @@ -119,19 +119,12 @@ private bool UpdateFixture(EntityUid uid, MapChunk chunk, List rectangles // on the grid (e.g. mass) which we want to preserve. var newFixtures = new ValueList<(string Id, Fixture Fixture)>(); - Span vertices = stackalloc Vector2[4]; - foreach (var rectangle in rectangles) { var bounds = ((Box2) rectangle.Translated(origin)).Enlarged(_fixtureEnlargement); var poly = new PolygonShape(); - vertices[0] = bounds.BottomLeft; - vertices[1] = bounds.BottomRight; - vertices[2] = bounds.TopRight; - vertices[3] = bounds.TopLeft; - - poly.Set(vertices, 4); + poly.SetAsBox(bounds); #pragma warning disable CS0618 var newFixture = new Fixture( diff --git a/Robust.Shared/Physics/Components/PhysicsComponentState.cs b/Robust.Shared/Physics/Components/PhysicsComponentState.cs index d1c6e988b28..aaf3eab70ff 100644 --- a/Robust.Shared/Physics/Components/PhysicsComponentState.cs +++ b/Robust.Shared/Physics/Components/PhysicsComponentState.cs @@ -8,72 +8,92 @@ namespace Robust.Shared.Physics.Components; /// -/// Average use-case of only linear velocity update +/// No chunky fixture updates /// [Serializable, NetSerializable] -public record struct PhysicsLinearVelocityDeltaState : IComponentDeltaState +public record struct PhysicsSlimDeltaState : IComponentDeltaState { + public bool CanCollide; + public bool SleepingAllowed; + public bool FixedRotation; + public BodyStatus Status; + public Vector2 LinearVelocity; + public float AngularVelocity; + public BodyType BodyType; + + public float Friction; + public float LinearDamping; + public float AngularDamping; + + public Vector2 Force; + public float Torque; public void ApplyToFullState(PhysicsComponentState fullState) { - fullState.LinearVelocity = LinearVelocity; + fullState.Slim = this; } public PhysicsComponentState CreateNewFullState(PhysicsComponentState fullState) { var copy = new PhysicsComponentState(fullState) { - LinearVelocity = LinearVelocity, + Slim = this }; return copy; } } + /// -/// 2nd-most typical usecase of just velocity updates +/// Average use-case of only linear velocity update /// [Serializable, NetSerializable] -public record struct PhysicsVelocityDeltaState : IComponentDeltaState +public record struct PhysicsLinearVelocityDeltaState : IComponentDeltaState { public Vector2 LinearVelocity; - public float AngularVelocity; public void ApplyToFullState(PhysicsComponentState fullState) { - fullState.LinearVelocity = LinearVelocity; - fullState.AngularVelocity = AngularVelocity; + fullState.Slim.LinearVelocity = LinearVelocity; } public PhysicsComponentState CreateNewFullState(PhysicsComponentState fullState) { - var copy = new PhysicsComponentState(fullState) - { - LinearVelocity = LinearVelocity, - AngularVelocity = AngularVelocity - }; + var copy = new PhysicsComponentState(fullState); + copy.Slim.LinearVelocity = LinearVelocity; return copy; } } +/// +/// 2nd-most typical usecase of just velocity updates +/// [Serializable, NetSerializable] -public sealed class PhysicsComponentState : IComponentState +public record struct PhysicsVelocityDeltaState : IComponentDeltaState { - public bool CanCollide; - public bool SleepingAllowed; - public bool FixedRotation; - public BodyStatus Status; - public Vector2 LinearVelocity; public float AngularVelocity; - public BodyType BodyType; - public float Friction; - public float LinearDamping; - public float AngularDamping; + public void ApplyToFullState(PhysicsComponentState fullState) + { + fullState.Slim.LinearVelocity = LinearVelocity; + fullState.Slim.AngularVelocity = AngularVelocity; + } - public Vector2 Force; - public float Torque; + public PhysicsComponentState CreateNewFullState(PhysicsComponentState fullState) + { + var copy = new PhysicsComponentState(fullState); + copy.Slim.LinearVelocity = LinearVelocity; + copy.Slim.AngularVelocity = AngularVelocity; + return copy; + } +} + +[Serializable, NetSerializable] +public sealed class PhysicsComponentState : IComponentState +{ + public PhysicsSlimDeltaState Slim; public Dictionary Fixtures = new(); @@ -81,21 +101,7 @@ public PhysicsComponentState() {} public PhysicsComponentState(PhysicsComponentState existing) { - CanCollide = existing.CanCollide; - SleepingAllowed = existing.SleepingAllowed; - FixedRotation = existing.FixedRotation; - Status = existing.Status; - - LinearVelocity = existing.LinearVelocity; - AngularVelocity = existing.AngularVelocity; - BodyType = existing.BodyType; - - Friction = existing.Friction; - LinearDamping = existing.LinearDamping; - AngularDamping = existing.AngularDamping; - - Force = existing.Force; - Torque = existing.Torque; + Slim = existing.Slim; foreach (var (key, value) in existing.Fixtures) { diff --git a/Robust.Shared/Physics/Dynamics/Fixture.cs b/Robust.Shared/Physics/Dynamics/Fixture.cs index 93f0f2fbad9..2ac432f3ade 100644 --- a/Robust.Shared/Physics/Dynamics/Fixture.cs +++ b/Robust.Shared/Physics/Dynamics/Fixture.cs @@ -117,12 +117,7 @@ void ISerializationHooks.AfterDeserialization() { var bounds = aabb.LocalBounds; var poly = new PolygonShape(); - Span verts = stackalloc Vector2[4]; - verts[0] = bounds.BottomLeft; - verts[1] = bounds.BottomRight; - verts[2] = bounds.TopRight; - verts[3] = bounds.TopLeft; - poly.Set(verts, 4); + poly.SetAsBox(bounds); Shape = poly; } } @@ -151,8 +146,7 @@ public Fixture() public Fixture(Fixture fixture) { - var thisFix = new Fixture(); - fixture.CopyTo(thisFix); + fixture.CopyTo(this); } /// diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs index cba83ed6ae8..0e7e9319a58 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs @@ -127,7 +127,7 @@ private void OnPhysicsGetState(EntityUid uid, PhysicsComponent component, ref Co } } - args.State = new PhysicsComponentState + var slim = new PhysicsSlimDeltaState() { CanCollide = component.CanCollide, SleepingAllowed = component.SleepingAllowed, @@ -142,6 +142,26 @@ private void OnPhysicsGetState(EntityUid uid, PhysicsComponent component, ref Co Force = component.Force, Torque = component.Torque, }; + + // Check if we even need to add fixtures + if (component.LastModifiedFields[_fixtureFieldIndex] < args.FromTick) + { + args.State = slim; + return; + } + + var fixtures = new Dictionary(component.Fixtures.Count); + + foreach (var (id, fix) in component.Fixtures) + { + fixtures.Add(id, new(fix)); + } + + args.State = new PhysicsComponentState + { + Slim = slim, + Fixtures = fixtures, + }; } private void OnPhysicsHandleState(EntityUid uid, PhysicsComponent component, ref ComponentHandleState args) @@ -160,21 +180,13 @@ private void OnPhysicsHandleState(EntityUid uid, PhysicsComponent component, ref SetLinearVelocity(uid, velocityState.LinearVelocity, body: component); SetAngularVelocity(uid, velocityState.AngularVelocity, body: component); } + else if (args.Current is PhysicsSlimDeltaState slim) + { + ApplySlimState(uid, component, slim); + } else if (args.Current is PhysicsComponentState newState) { - SetSleepingAllowed(uid, component, newState.SleepingAllowed); - SetFixedRotation(uid, newState.FixedRotation, body: component); - SetCanCollide(uid, newState.CanCollide, body: component); - component.BodyStatus = newState.Status; - - SetLinearVelocity(uid, newState.LinearVelocity, body: component); - SetAngularVelocity(uid, newState.AngularVelocity, body: component); - SetBodyType(uid, newState.BodyType, component); - SetFriction(uid, component, newState.Friction); - SetLinearDamping(uid, component, newState.LinearDamping); - SetAngularDamping(uid, component, newState.AngularDamping); - component.Force = newState.Force; - component.Torque = newState.Torque; + ApplySlimState(uid, component, newState.Slim); // Fixtures foreach (var fixture in component.Fixtures.Values) @@ -250,6 +262,23 @@ private void OnPhysicsHandleState(EntityUid uid, PhysicsComponent component, ref } } + private void ApplySlimState(EntityUid uid, PhysicsComponent component, PhysicsSlimDeltaState newState) + { + SetSleepingAllowed(uid, component, newState.SleepingAllowed); + SetFixedRotation(uid, newState.FixedRotation, body: component); + SetCanCollide(uid, newState.CanCollide, body: component); + component.BodyStatus = newState.Status; + + SetLinearVelocity(uid, newState.LinearVelocity, body: component); + SetAngularVelocity(uid, newState.AngularVelocity, body: component); + SetBodyType(uid, newState.BodyType, component); + SetFriction(uid, component, newState.Friction); + SetLinearDamping(uid, component, newState.LinearDamping); + SetAngularDamping(uid, component, newState.AngularDamping); + component.Force = newState.Force; + component.Torque = newState.Torque; + } + #endregion private bool IsMoveable(PhysicsComponent body) @@ -654,7 +683,6 @@ public void SetBodyStatus(EntityUid uid, PhysicsComponent body, BodyStatus statu public bool SetCanCollide( EntityUid uid, bool value, - bool dirty = true, bool force = false, PhysicsComponent? body = null) { @@ -686,13 +714,16 @@ public bool SetCanCollide( body.CanCollide = value; if (!value) + { SetAwake((uid, body), false); + } if (body.Initialized) { var ev = new CollisionChangeEvent(uid, body, value); RaiseLocalEvent(ref ev); } + DirtyField(uid, body, nameof(PhysicsComponent.CanCollide)); return value; } diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Fixtures.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Fixtures.cs index 7d8cca0c6e6..b7a14673539 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Fixtures.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Fixtures.cs @@ -72,7 +72,7 @@ internal void CreateFixture( // Don't need to dirty here as we'll just manually call it after (we 100% need to call it). FixtureUpdate(uid, false, body: body); // Don't need to ResetMassData as FixtureUpdate already does it. - Dirty(uid, body); + DirtyField(uid, body, nameof(PhysicsComponent.Fixtures)); } // TODO: Set newcontacts to true. @@ -195,7 +195,9 @@ public void FixtureUpdate(EntityUid uid, bool dirty = true, bool resetMass = tru } if (dirty) - Dirty(uid, body); + { + DirtyField(uid, body, nameof(PhysicsComponent.Fixtures)); + } } public void SetDensity(EntityUid uid, string fixtureId, Fixture fixture, float value, bool update = true, PhysicsComponent? body = null) diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.FixturesChange.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.FixturesChange.cs deleted file mode 100644 index 59ba343baef..00000000000 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.FixturesChange.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Robust.Shared.GameObjects; -using Robust.Shared.Physics.Components; - -namespace Robust.Shared.Physics.Systems; - -public abstract partial class SharedPhysicsSystem -{ - private void InitializeFixturesChange() - { - SubscribeLocalEvent(OnChangeStartup); - SubscribeLocalEvent(OnChangeShutdown); - } - - private void OnChangeStartup(Entity ent, ref ComponentStartup args) - { - foreach (var (id, fixture) in ent.Comp.Fixtures) - { - TryCreateFixture(ent.Owner, - fixture.Shape, - id, - fixture.Density, - fixture.Hard, - fixture.CollisionLayer, - fixture.CollisionMask, - fixture.Friction, - fixture.Restitution); - } - - // TODO: Fixture creation should be handling this. - WakeBody(ent.Owner); - } - - private void OnChangeShutdown(Entity ent, ref ComponentShutdown args) - { - foreach (var id in ent.Comp.Fixtures.Keys) - { - DestroyFixture(ent.Owner, id); - } - } -} diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.cs index fbcb238228e..eee3d2c5539 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.cs @@ -75,6 +75,7 @@ public abstract partial class SharedPhysicsSystem : EntitySystem private ComponentRegistration _physicsReg = default!; private byte _angularVelocityIndex; + private byte _fixtureFieldIndex; public override void Initialize() { @@ -87,6 +88,7 @@ public override void Initialize() nameof(PhysicsComponent.CanCollide), nameof(PhysicsComponent.BodyStatus), nameof(PhysicsComponent.BodyType), + nameof(PhysicsComponent.Fixtures), nameof(PhysicsComponent.SleepingAllowed), nameof(PhysicsComponent.FixedRotation), nameof(PhysicsComponent.Friction), @@ -97,7 +99,11 @@ public override void Initialize() nameof(PhysicsComponent.AngularVelocity), nameof(PhysicsComponent.LinearVelocity)); - _angularVelocityIndex = 10; + _fixtureFieldIndex = 3; + _angularVelocityIndex = 11; + + DebugTools.Assert(EntityManager.GetNetworkedFieldIndex(_physicsReg, nameof(PhysicsComponent.AngularVelocity)) == _angularVelocityIndex); + DebugTools.Assert(EntityManager.GetNetworkedFieldIndex(_physicsReg, nameof(PhysicsComponent.Fixtures)) == _fixtureFieldIndex); PhysicsQuery = GetEntityQuery(); _xformQuery = GetEntityQuery(); @@ -115,7 +121,6 @@ public override void Initialize() SubscribeLocalEvent(OnPhysicsHandleState); InitializeIsland(); InitializeContacts(); - InitializeFixturesChange(); Subs.CVar(_cfg, CVars.AutoClearForces, OnAutoClearChange); Subs.CVar(_cfg, CVars.NetTickrate, UpdateSubsteps, true); diff --git a/Robust.UnitTesting/Shared/Physics/CollisionWake_Test.cs b/Robust.UnitTesting/Shared/Physics/CollisionWake_Test.cs index 126108f8e00..8218532bd25 100644 --- a/Robust.UnitTesting/Shared/Physics/CollisionWake_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/CollisionWake_Test.cs @@ -20,7 +20,6 @@ public sealed class CollisionWake_Test : RobustIntegrationTest - type: Transform - type: Physics bodyType: Dynamic - - type: Fixtures fixtures: fix1: shape: diff --git a/Robust.UnitTesting/Shared/Physics/MapVelocity_Test.cs b/Robust.UnitTesting/Shared/Physics/MapVelocity_Test.cs index a843a7ac035..692597bb627 100644 --- a/Robust.UnitTesting/Shared/Physics/MapVelocity_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/MapVelocity_Test.cs @@ -20,7 +20,6 @@ public sealed class MapVelocity_Test : RobustIntegrationTest components: - type: Physics bodyType: Dynamic - - type: Fixtures fixtures: fix1: shape: From ffbd93d281bd25627047b3cdd4aa417ae845f2c6 Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Sun, 22 Dec 2024 14:58:42 +1100 Subject: [PATCH 3/3] cancollide --- Robust.Shared/Containers/SharedContainerSystem.Insert.cs | 2 +- Robust.Shared/GameObjects/Systems/CollisionWakeSystem.cs | 9 ++++----- .../Physics/Systems/SharedPhysicsSystem.Components.cs | 6 +++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Robust.Shared/Containers/SharedContainerSystem.Insert.cs b/Robust.Shared/Containers/SharedContainerSystem.Insert.cs index 8e7dab6feb5..1d0df6454e3 100644 --- a/Robust.Shared/Containers/SharedContainerSystem.Insert.cs +++ b/Robust.Shared/Containers/SharedContainerSystem.Insert.cs @@ -202,7 +202,7 @@ private void RecursivelyUpdatePhysics(Entity entity, bool checkTerminating = true, bool dirty = true) + internal void UpdateCanCollide(Entity entity, bool checkTerminating = true) { if (_query.TryGetComponent(entity, out var wakeComp)) - UpdateCanCollide(entity.Owner, wakeComp, entity.Comp, checkTerminating: checkTerminating, dirty: dirty); + UpdateCanCollide(entity.Owner, wakeComp, entity.Comp, checkTerminating: checkTerminating); } internal void UpdateCanCollide( @@ -80,8 +80,7 @@ internal void UpdateCanCollide( CollisionWakeComponent component, PhysicsComponent? body = null, TransformComponent? xform = null, - bool checkTerminating = true, - bool dirty = true) + bool checkTerminating = true) { if (!component.Enabled) return; @@ -99,7 +98,7 @@ internal void UpdateCanCollide( (TryComp(uid, out JointComponent? jointComponent) && jointComponent.JointCount > 0) || xform.GridUid == null; - _physics.SetCanCollide(uid, canCollide, dirty, body: body); + _physics.SetCanCollide(uid, canCollide, body: body); } } } diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs index 0e7e9319a58..8e14af0a8a2 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs @@ -47,7 +47,7 @@ private void OnPhysicsInit(EntityUid uid, PhysicsComponent component, ComponentI if (component.CanCollide && (_containerSystem.IsEntityOrParentInContainer(uid) || xform.MapID == MapId.Nullspace)) { - SetCanCollide(uid, false, false, body: component); + SetCanCollide(uid, false, body: component); } if (component.CanCollide) @@ -81,7 +81,7 @@ private void OnPhysicsInit(EntityUid uid, PhysicsComponent component, ComponentI private void OnPhysicsShutdown(EntityUid uid, PhysicsComponent component, ComponentShutdown args) { DestroyContacts(component); - SetCanCollide(uid, false, false, body: component); + SetCanCollide(uid, false, body: component); DebugTools.Assert(!component.Awake); } @@ -593,7 +593,7 @@ public void SetAwake(Entity ent, bool value, bool updateSleepT // Update wake system last, if sleeping but still colliding. if (!value && body.CanCollide) - _wakeSystem.UpdateCanCollide(ent, checkTerminating: false, dirty: false); + _wakeSystem.UpdateCanCollide(ent, checkTerminating: false); if (updateSleepTime) SetSleepTime(body, 0);