mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Gridtree ECS (#4109)
This commit is contained in:
@@ -7,6 +7,7 @@ using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -119,6 +120,86 @@ public abstract partial class SharedMapSystem
|
||||
SubscribeLocalEvent<MapGridComponent, ComponentInit>(OnGridInit);
|
||||
SubscribeLocalEvent<MapGridComponent, ComponentStartup>(OnGridStartup);
|
||||
SubscribeLocalEvent<MapGridComponent, ComponentShutdown>(OnGridRemove);
|
||||
SubscribeLocalEvent<MapGridComponent, MoveEvent>(OnGridMove);
|
||||
}
|
||||
|
||||
public void OnGridBoundsChange(EntityUid uid, MapGridComponent component)
|
||||
{
|
||||
// Just MapLoader things.
|
||||
if (component.MapProxy == DynamicTree.Proxy.Free) return;
|
||||
|
||||
var xform = EntityManager.GetComponent<TransformComponent>(uid);
|
||||
var aabb = GetWorldAABB(uid, component, xform);
|
||||
|
||||
if (TryComp<GridTreeComponent>(xform.MapUid, out var gridTree))
|
||||
{
|
||||
gridTree.Tree.MoveProxy(component.MapProxy, in aabb, Vector2.Zero);
|
||||
}
|
||||
|
||||
if (TryComp<MovedGridsComponent>(xform.MapUid, out var movedGrids))
|
||||
{
|
||||
movedGrids.MovedGrids.Add(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGridMove(EntityUid uid, MapGridComponent component, ref MoveEvent args)
|
||||
{
|
||||
if (args.ParentChanged)
|
||||
{
|
||||
OnParentChange(uid, component, ref args);
|
||||
return;
|
||||
}
|
||||
|
||||
// Just maploader / test things
|
||||
if (component.MapProxy == DynamicTree.Proxy.Free) return;
|
||||
|
||||
var xform = args.Component;
|
||||
var aabb = GetWorldAABB(uid, component, xform);
|
||||
|
||||
if (TryComp<GridTreeComponent>(xform.MapUid, out var gridTree))
|
||||
{
|
||||
gridTree.Tree.MoveProxy(component.MapProxy, in aabb, Vector2.Zero);
|
||||
}
|
||||
|
||||
if (TryComp<MovedGridsComponent>(xform.MapUid, out var movedGrids))
|
||||
{
|
||||
movedGrids.MovedGrids.Add(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnParentChange(EntityUid uid, MapGridComponent component, ref MoveEvent args)
|
||||
{
|
||||
if (EntityManager.HasComponent<MapComponent>(uid))
|
||||
return;
|
||||
|
||||
var lifestage = EntityManager.GetComponent<MetaDataComponent>(uid).EntityLifeStage;
|
||||
|
||||
// oh boy
|
||||
// Want gridinit to handle this hence specialcase those situations.
|
||||
// oh boy oh boy, its even worse now.
|
||||
// transform now raises parent change events on startup, because container code is a POS.
|
||||
if (lifestage < EntityLifeStage.Initialized || args.Component.LifeStage == ComponentLifeStage.Starting)
|
||||
return;
|
||||
|
||||
// Make sure we cleanup old map for moved grid stuff.
|
||||
var mapId = args.Component.MapID;
|
||||
var oldMap = args.OldPosition.ToMap(EntityManager, _transform);
|
||||
|
||||
// y'all need jesus
|
||||
if (oldMap.MapId == mapId) return;
|
||||
|
||||
if (component.MapProxy != DynamicTree.Proxy.Free && TryComp<MovedGridsComponent>(MapManager.GetMapEntityId(oldMap.MapId), out var oldMovedGrids))
|
||||
{
|
||||
oldMovedGrids.MovedGrids.Remove(uid);
|
||||
RemoveGrid(uid, component, MapManager.GetMapEntityId(oldMap.MapId));
|
||||
}
|
||||
|
||||
DebugTools.Assert(component.MapProxy == DynamicTree.Proxy.Free);
|
||||
if (TryComp<MovedGridsComponent>(MapManager.GetMapEntityId(mapId), out var newMovedGrids))
|
||||
{
|
||||
newMovedGrids.MovedGrids.Add(uid);
|
||||
AddGrid(uid, component, mapId);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGridHandleState(EntityUid uid, MapGridComponent component, ref ComponentHandleState args)
|
||||
@@ -312,6 +393,23 @@ public abstract partial class SharedMapSystem
|
||||
if (xform.MapUid != null && xform.MapUid != uid)
|
||||
_transform.SetParent(uid, xform, xform.MapUid.Value, xformQuery);
|
||||
|
||||
if (!HasComp<MapComponent>(uid))
|
||||
{
|
||||
var aabb = GetWorldAABB(uid, component);
|
||||
|
||||
if (TryComp<GridTreeComponent>(xform.MapUid, out var gridTree))
|
||||
{
|
||||
var proxy = gridTree.Tree.CreateProxy(in aabb, (uid, component));
|
||||
DebugTools.Assert(component.MapProxy == DynamicTree.Proxy.Free);
|
||||
component.MapProxy = proxy;
|
||||
}
|
||||
|
||||
if (TryComp<MovedGridsComponent>(xform.MapUid, out var movedGrids))
|
||||
{
|
||||
movedGrids.MovedGrids.Add(uid);
|
||||
}
|
||||
}
|
||||
|
||||
var msg = new GridInitializeEvent(uid);
|
||||
RaiseLocalEvent(uid, msg, true);
|
||||
}
|
||||
@@ -324,6 +422,12 @@ public abstract partial class SharedMapSystem
|
||||
|
||||
private void OnGridRemove(EntityUid uid, MapGridComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (TryComp<TransformComponent>(uid, out var xform) && xform.MapUid != null)
|
||||
{
|
||||
RemoveGrid(uid, component, xform.MapUid.Value);
|
||||
}
|
||||
|
||||
component.MapProxy = DynamicTree.Proxy.Free;
|
||||
RaiseLocalEvent(uid, new GridRemovalEvent(uid), true);
|
||||
|
||||
if (uid == EntityUid.Invalid)
|
||||
@@ -334,4 +438,51 @@ public abstract partial class SharedMapSystem
|
||||
|
||||
MapManager.DeleteGrid(uid);
|
||||
}
|
||||
|
||||
private Box2 GetWorldAABB(EntityUid uid, MapGridComponent grid, TransformComponent? xform = null)
|
||||
{
|
||||
if (!Resolve(uid, ref xform))
|
||||
return new Box2();
|
||||
|
||||
var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform, GetEntityQuery<TransformComponent>());
|
||||
var aabb = grid.LocalAABB.Translated(worldPos);
|
||||
|
||||
return new Box2Rotated(aabb, worldRot, worldPos).CalcBoundingBox();
|
||||
}
|
||||
|
||||
private void AddGrid(EntityUid uid, MapGridComponent grid, MapId mapId)
|
||||
{
|
||||
DebugTools.Assert(!EntityManager.HasComponent<MapComponent>(uid));
|
||||
var aabb = GetWorldAABB(uid, grid);
|
||||
|
||||
if (!TryComp<TransformComponent>(uid, out var xform))
|
||||
return;
|
||||
|
||||
if (TryComp<GridTreeComponent>(xform.MapUid, out var gridTree))
|
||||
{
|
||||
var proxy = gridTree.Tree.CreateProxy(in aabb, (uid, grid));
|
||||
DebugTools.Assert(grid.MapProxy == DynamicTree.Proxy.Free);
|
||||
grid.MapProxy = proxy;
|
||||
}
|
||||
|
||||
if (TryComp<MovedGridsComponent>(xform.MapUid, out var movedGrids))
|
||||
{
|
||||
movedGrids.MovedGrids.Add(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveGrid(EntityUid uid, MapGridComponent grid, EntityUid mapUid)
|
||||
{
|
||||
if (grid.MapProxy != DynamicTree.Proxy.Free && TryComp<GridTreeComponent>(mapUid, out var gridTree))
|
||||
{
|
||||
gridTree.Tree.DestroyProxy(grid.MapProxy);
|
||||
}
|
||||
|
||||
grid.MapProxy = DynamicTree.Proxy.Free;
|
||||
|
||||
if (TryComp<MovedGridsComponent>(mapUid, out var movedGrids))
|
||||
{
|
||||
movedGrids.MovedGrids.Remove(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,9 @@ public abstract partial class SharedMapSystem
|
||||
|
||||
private void OnMapInit(EntityUid uid, MapComponent component, ComponentInit args)
|
||||
{
|
||||
EnsureComp<GridTreeComponent>(uid);
|
||||
EnsureComp<MovedGridsComponent>(uid);
|
||||
|
||||
var msg = new MapChangedEvent(uid, component.MapId, true);
|
||||
RaiseLocalEvent(uid, msg, true);
|
||||
}
|
||||
|
||||
13
Robust.Shared/Map/Components/GridTreeComponent.cs
Normal file
13
Robust.Shared/Map/Components/GridTreeComponent.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.Map.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class GridTreeComponent : Component
|
||||
{
|
||||
[ViewVariables]
|
||||
public readonly B2DynamicTree<(EntityUid Uid, MapGridComponent Grid)> Tree = new();
|
||||
}
|
||||
@@ -155,7 +155,7 @@ namespace Robust.Shared.Map.Components
|
||||
|
||||
// TODO: Move this to the component when we combine.
|
||||
_entMan.EntitySysManager.GetEntitySystem<SharedPhysicsSystem>().WakeBody(Owner);
|
||||
_mapManager.OnGridBoundsChange(Owner, this);
|
||||
_entMan.EntitySysManager.GetEntitySystem<SharedMapSystem>().OnGridBoundsChange(Owner, this);
|
||||
system?.RegenerateCollision(Owner, chunkRectangles, removedChunks);
|
||||
}
|
||||
|
||||
|
||||
16
Robust.Shared/Map/Components/MovedGridsComponent.cs
Normal file
16
Robust.Shared/Map/Components/MovedGridsComponent.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.Map.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Stores what grids moved in a tick.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class MovedGridsComponent : Component
|
||||
{
|
||||
[ViewVariables]
|
||||
public HashSet<EntityUid> MovedGrids = new();
|
||||
}
|
||||
@@ -26,16 +26,6 @@ namespace Robust.Shared.Map
|
||||
/// </summary>
|
||||
bool SuppressOnTileChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the set of grids that have moved on this map in this tick.
|
||||
/// </summary>
|
||||
HashSet<EntityUid> GetMovedGrids(MapId mapId);
|
||||
|
||||
/// <summary>
|
||||
/// Clear the set of grids that have moved on this map in this tick.
|
||||
/// </summary>
|
||||
void ClearMovedGrids(MapId mapId);
|
||||
|
||||
/// <summary>
|
||||
/// Starts up the map system.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.Map
|
||||
@@ -19,6 +18,5 @@ namespace Robust.Shared.Map
|
||||
MapId CreateMap(MapId? mapId, EntityUid euid);
|
||||
|
||||
void RemoveMapId(MapId mapId);
|
||||
void OnGridBoundsChange(EntityUid uid, MapGridComponent grid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,196 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Map;
|
||||
|
||||
internal partial class MapManager
|
||||
{
|
||||
// TODO: Move IMapManager stuff to the system
|
||||
private Dictionary<MapId, B2DynamicTree<(EntityUid Uid, MapGridComponent Grid)>> _gridTrees = new();
|
||||
|
||||
private Dictionary<MapId, HashSet<EntityUid>> _movedGrids = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grids that have moved this tick until broadphase has run.
|
||||
/// </summary>
|
||||
/// <param name="mapId"></param>
|
||||
/// <returns></returns>
|
||||
public HashSet<EntityUid> GetMovedGrids(MapId mapId)
|
||||
{
|
||||
// Temporary until this is moved to SharedPhysicsMapComponent
|
||||
if (!_movedGrids.TryGetValue(mapId, out var moved))
|
||||
{
|
||||
Logger.ErrorS("map", $"Unable to get moved grids for {mapId}");
|
||||
moved = new HashSet<EntityUid>();
|
||||
DebugTools.Assert(false);
|
||||
}
|
||||
|
||||
return moved;
|
||||
}
|
||||
|
||||
public void ClearMovedGrids(MapId mapId)
|
||||
{
|
||||
if (!_movedGrids.TryGetValue(mapId, out var moved))
|
||||
{
|
||||
Logger.ErrorS("map", $"Unable to clear moved grids for {mapId}");
|
||||
return;
|
||||
}
|
||||
|
||||
moved.Clear();
|
||||
}
|
||||
|
||||
private void StartupGridTrees()
|
||||
{
|
||||
// Needs to be done on mapmanager startup because the eventbus will clear on shutdown
|
||||
// (and mapmanager initialize doesn't run upon connecting to a server every time).
|
||||
EntityManager.EventBus.SubscribeEvent<GridInitializeEvent>(EventSource.Local, this, OnGridInit);
|
||||
EntityManager.EventBus.SubscribeEvent<GridRemovalEvent>(EventSource.Local, this, OnGridRemove);
|
||||
EntityManager.EventBus.SubscribeLocalEvent<MapGridComponent, MoveEvent>(OnGridMove);
|
||||
EntityManager.EventBus.SubscribeLocalEvent<MapGridComponent, EntParentChangedMessage>(OnGridParentChange);
|
||||
}
|
||||
|
||||
private void ShutdownGridTrees()
|
||||
{
|
||||
EntityManager.EventBus.UnsubscribeEvent<GridInitializeEvent>(EventSource.Local, this);
|
||||
EntityManager.EventBus.UnsubscribeEvent<GridRemovalEvent>(EventSource.Local, this);
|
||||
EntityManager.EventBus.UnsubscribeLocalEvent<MapGridComponent, MoveEvent>();
|
||||
EntityManager.EventBus.UnsubscribeLocalEvent<MapGridComponent, EntParentChangedMessage>();
|
||||
|
||||
DebugTools.Assert(_gridTrees.Count == 0);
|
||||
DebugTools.Assert(_movedGrids.Count == 0);
|
||||
}
|
||||
|
||||
private void OnMapCreatedGridTree(MapEventArgs e)
|
||||
{
|
||||
if (e.Map == MapId.Nullspace) return;
|
||||
|
||||
_gridTrees.Add(e.Map, new B2DynamicTree<(EntityUid Uid, MapGridComponent Grid)>());
|
||||
_movedGrids.Add(e.Map, new HashSet<EntityUid>());
|
||||
}
|
||||
|
||||
public void RemoveMapId(MapId mapId)
|
||||
{
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
_mapEntities.Remove(mapId);
|
||||
_gridTrees.Remove(mapId);
|
||||
_movedGrids.Remove(mapId);
|
||||
}
|
||||
|
||||
private Box2 GetWorldAABB(EntityUid uid, MapGridComponent grid, TransformComponent? xform = null)
|
||||
{
|
||||
xform ??= EntityManager.GetComponent<TransformComponent>(uid);
|
||||
|
||||
var (worldPos, worldRot) = xform.GetWorldPositionRotation();
|
||||
|
||||
return new Box2Rotated(grid.LocalAABB, worldRot).CalcBoundingBox().Translated(worldPos);
|
||||
}
|
||||
|
||||
private void OnGridInit(GridInitializeEvent args)
|
||||
{
|
||||
if (EntityManager.HasComponent<MapComponent>(args.EntityUid))
|
||||
return;
|
||||
|
||||
var xform = EntityManager.GetComponent<TransformComponent>(args.EntityUid);
|
||||
var mapId = xform.MapID;
|
||||
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
var grid = EntityManager.GetComponent<MapGridComponent>(args.EntityUid);
|
||||
AddGrid(args.EntityUid, grid, mapId);
|
||||
}
|
||||
|
||||
private void AddGrid(EntityUid uid, MapGridComponent grid, MapId mapId)
|
||||
{
|
||||
DebugTools.Assert(!EntityManager.HasComponent<MapComponent>(uid));
|
||||
var aabb = GetWorldAABB(uid, grid);
|
||||
var proxy = _gridTrees[mapId].CreateProxy(in aabb, (uid, grid));
|
||||
DebugTools.Assert(grid.MapProxy == DynamicTree.Proxy.Free);
|
||||
grid.MapProxy = proxy;
|
||||
|
||||
_movedGrids[mapId].Add(uid);
|
||||
}
|
||||
|
||||
private void OnGridRemove(GridRemovalEvent args)
|
||||
{
|
||||
if (EntityManager.HasComponent<MapComponent>(args.EntityUid))
|
||||
return;
|
||||
|
||||
var xform = EntityManager.GetComponent<TransformComponent>(args.EntityUid);
|
||||
|
||||
// Can't check for free proxy because DetachParentToNull gets called first woo!
|
||||
if (xform.MapID == MapId.Nullspace) return;
|
||||
|
||||
var grid = EntityManager.GetComponent<MapGridComponent>(args.EntityUid);
|
||||
RemoveGrid(args.EntityUid, grid, xform.MapID);
|
||||
}
|
||||
|
||||
private void RemoveGrid(EntityUid uid, MapGridComponent grid, MapId mapId)
|
||||
{
|
||||
_gridTrees[mapId].DestroyProxy(grid.MapProxy);
|
||||
_movedGrids[mapId].Remove(uid);
|
||||
grid.MapProxy = DynamicTree.Proxy.Free;
|
||||
}
|
||||
|
||||
private void OnGridMove(EntityUid uid, MapGridComponent component, ref MoveEvent args)
|
||||
{
|
||||
// Just maploader / test things
|
||||
if (component.MapProxy == DynamicTree.Proxy.Free) return;
|
||||
|
||||
var xform = args.Component;
|
||||
var aabb = GetWorldAABB(uid, component, xform);
|
||||
_gridTrees[xform.MapID].MoveProxy(component.MapProxy, in aabb, Vector2.Zero);
|
||||
_movedGrids[xform.MapID].Add(uid);
|
||||
}
|
||||
|
||||
private void OnGridParentChange(EntityUid uid, MapGridComponent component, ref EntParentChangedMessage args)
|
||||
{
|
||||
if (EntityManager.HasComponent<MapComponent>(uid))
|
||||
return;
|
||||
|
||||
var lifestage = EntityManager.GetComponent<MetaDataComponent>(uid).EntityLifeStage;
|
||||
|
||||
// oh boy
|
||||
// Want gridinit to handle this hence specialcase those situations.
|
||||
// oh boy oh boy, its even worse now.
|
||||
// transform now raises parent change events on startup, because container code is a POS.
|
||||
if (lifestage < EntityLifeStage.Initialized || args.Transform.LifeStage == ComponentLifeStage.Starting)
|
||||
return;
|
||||
|
||||
// Make sure we cleanup old map for moved grid stuff.
|
||||
var mapId = args.Transform.MapID;
|
||||
|
||||
// y'all need jesus
|
||||
if (args.OldMapId == mapId) return;
|
||||
|
||||
if (component.MapProxy != DynamicTree.Proxy.Free && _movedGrids.TryGetValue(args.OldMapId, out var oldMovedGrids))
|
||||
{
|
||||
oldMovedGrids.Remove(uid);
|
||||
RemoveGrid(uid, component, args.OldMapId);
|
||||
}
|
||||
|
||||
DebugTools.Assert(component.MapProxy == DynamicTree.Proxy.Free);
|
||||
if (_movedGrids.TryGetValue(mapId, out var newMovedGrids))
|
||||
{
|
||||
newMovedGrids.Add(uid);
|
||||
AddGrid(uid, component, mapId);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnGridBoundsChange(EntityUid uid, MapGridComponent grid)
|
||||
{
|
||||
// Just MapLoader things.
|
||||
if (grid.MapProxy == DynamicTree.Proxy.Free) return;
|
||||
|
||||
var xform = EntityManager.GetComponent<TransformComponent>(uid);
|
||||
var aabb = GetWorldAABB(uid, grid, xform);
|
||||
_gridTrees[xform.MapID].MoveProxy(grid.MapProxy, in aabb, Vector2.Zero);
|
||||
_movedGrids[xform.MapID].Add(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,8 +144,6 @@ internal partial class MapManager
|
||||
// Yeah this sucks but I just want to save maps for now, deal.
|
||||
if (raiseEvent)
|
||||
{
|
||||
var args = new MapEventArgs(mapId);
|
||||
OnMapCreatedGridTree(args);
|
||||
var ev = new MapChangedEvent(newMapEntity, mapId, true);
|
||||
EntityManager.EventBus.RaiseLocalEvent(newMapEntity, ev, true);
|
||||
}
|
||||
@@ -246,7 +244,6 @@ internal partial class MapManager
|
||||
}
|
||||
|
||||
var args = new MapEventArgs(actualId);
|
||||
OnMapCreatedGridTree(args);
|
||||
return actualId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,21 +22,23 @@ internal partial class MapManager
|
||||
|
||||
public void FindGridsIntersecting(MapId mapId, Box2 worldAABB, GridCallback callback, bool approx = false)
|
||||
{
|
||||
if (!_gridTrees.TryGetValue(mapId, out var gridTree))
|
||||
if (!_mapEntities.TryGetValue(mapId, out var mapEnt) ||
|
||||
!EntityManager.TryGetComponent<GridTreeComponent>(mapEnt, out var gridTree))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var physicsQuery = EntityManager.GetEntityQuery<PhysicsComponent>();
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
var xformSystem = EntityManager.System<SharedTransformSystem>();
|
||||
var state = (worldAABB, gridTree, callback, approx, physicsQuery, xformQuery, xformSystem);
|
||||
var state = (worldAABB, gridTree.Tree, callback, approx, physicsQuery, xformQuery, xformSystem);
|
||||
|
||||
gridTree.Query(ref state,
|
||||
gridTree.Tree.Query(ref state,
|
||||
static (ref (Box2 worldAABB,
|
||||
B2DynamicTree<(EntityUid Uid, MapGridComponent Grid)> gridTree,
|
||||
GridCallback callback,
|
||||
bool approx,
|
||||
EntityQuery<PhysicsComponent> physicsQuery,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<PhysicsComponent> physicsQuery, EntityQuery<TransformComponent> xformQuery,
|
||||
SharedTransformSystem xformSystem) tuple,
|
||||
DynamicTree.Proxy proxy) =>
|
||||
{
|
||||
@@ -61,15 +63,17 @@ internal partial class MapManager
|
||||
|
||||
public void FindGridsIntersecting<TState>(MapId mapId, Box2 worldAABB, ref TState state, GridCallback<TState> callback, bool approx = false)
|
||||
{
|
||||
if (!_gridTrees.TryGetValue(mapId, out var gridTree))
|
||||
if (!_mapEntities.TryGetValue(mapId, out var mapEnt) ||
|
||||
!EntityManager.TryGetComponent<GridTreeComponent>(mapEnt, out var gridTree))
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
var physicsQuery = EntityManager.GetEntityQuery<PhysicsComponent>();
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
var xformSystem = EntityManager.System<SharedTransformSystem>();
|
||||
var state2 = (state, worldAABB, gridTree, callback, approx, physicsQuery, xformQuery, xformSystem);
|
||||
var state2 = (state, worldAABB, gridTree.Tree, callback, approx, physicsQuery, xformQuery, xformSystem);
|
||||
|
||||
gridTree.Query(ref state2, static (ref (
|
||||
gridTree.Tree.Query(ref state2, static (ref (
|
||||
TState state,
|
||||
Box2 worldAABB,
|
||||
B2DynamicTree<(EntityUid Uid, MapGridComponent Grid)> gridTree,
|
||||
@@ -141,8 +145,6 @@ internal partial class MapManager
|
||||
[Obsolete("Use the FindGridsIntersecting callback")]
|
||||
public IEnumerable<MapGridComponent> FindGridsIntersecting(MapId mapId, Box2 worldAabb, bool approx = false)
|
||||
{
|
||||
if (!_gridTrees.ContainsKey(mapId)) return Enumerable.Empty<MapGridComponent>();
|
||||
|
||||
var grids = new List<MapGridComponent>();
|
||||
var state = grids;
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@ internal partial class MapManager : IMapManagerInternal, IEntityEventSubscriber
|
||||
|
||||
Logger.DebugS("map", "Starting...");
|
||||
|
||||
StartupGridTrees();
|
||||
|
||||
DebugTools.Assert(!GridExists(EntityUid.Invalid));
|
||||
}
|
||||
|
||||
@@ -59,7 +57,6 @@ internal partial class MapManager : IMapManagerInternal, IEntityEventSubscriber
|
||||
{
|
||||
EntityManager.DeleteEntity(mapComp.Owner);
|
||||
}
|
||||
ShutdownGridTrees();
|
||||
|
||||
#if DEBUG
|
||||
DebugTools.Assert(!GridExists(EntityUid.Invalid));
|
||||
|
||||
@@ -6,397 +6,396 @@ using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Robust.Shared.Physics.Dynamics.Joints
|
||||
namespace Robust.Shared.Physics.Dynamics.Joints;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
internal sealed class WeldJointState : JointState
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
internal sealed class WeldJointState : JointState
|
||||
public float Stiffness { get; internal set; }
|
||||
public float Damping { get; internal set; }
|
||||
public float Bias { get; internal set; }
|
||||
|
||||
public override Joint GetJoint()
|
||||
{
|
||||
public float Stiffness { get; internal set; }
|
||||
public float Damping { get; internal set; }
|
||||
public float Bias { get; internal set; }
|
||||
|
||||
public override Joint GetJoint()
|
||||
{
|
||||
return new WeldJoint(this);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class WeldJoint : Joint, IEquatable<WeldJoint>
|
||||
{
|
||||
// Shared
|
||||
private float _gamma;
|
||||
private Vector3 _impulse;
|
||||
|
||||
// Temporary
|
||||
private int _indexA;
|
||||
private int _indexB;
|
||||
private Vector2 _rA;
|
||||
private Vector2 _rB;
|
||||
private Vector2 _localCenterA;
|
||||
private Vector2 _localCenterB;
|
||||
private float _invMassA;
|
||||
private float _invMassB;
|
||||
private float _invIA;
|
||||
private float _invIB;
|
||||
private Matrix33 _mass;
|
||||
|
||||
// Settable
|
||||
|
||||
[DataField("stiffness")]
|
||||
public float Stiffness;
|
||||
|
||||
[DataField("damping")]
|
||||
public float Damping;
|
||||
|
||||
[DataField("bias")]
|
||||
public float Bias;
|
||||
|
||||
/// <summary>
|
||||
/// The bodyB angle minus bodyA angle in the reference state (radians).
|
||||
/// </summary>
|
||||
[DataField("referenceAngle")]
|
||||
public float ReferenceAngle;
|
||||
|
||||
/// <summary>
|
||||
/// Used for Serv3 reasons
|
||||
/// </summary>
|
||||
public WeldJoint() {}
|
||||
|
||||
internal WeldJoint(EntityUid bodyA, EntityUid bodyB, Vector2 anchorA, Vector2 anchorB, float referenceAngle) : base(bodyA, bodyB)
|
||||
{
|
||||
LocalAnchorA = anchorA;
|
||||
LocalAnchorB = anchorB;
|
||||
ReferenceAngle = referenceAngle;
|
||||
}
|
||||
|
||||
internal WeldJoint(EntityUid bodyAUid, EntityUid bodyBUid) : base(bodyAUid, bodyBUid) {}
|
||||
|
||||
internal WeldJoint(WeldJointState state) : base(state)
|
||||
{
|
||||
Stiffness = state.Stiffness;
|
||||
Damping = state.Damping;
|
||||
Bias = state.Bias;
|
||||
}
|
||||
|
||||
public override JointType JointType => JointType.Weld;
|
||||
public override JointState GetState()
|
||||
{
|
||||
var weldJointState = new WeldJointState();
|
||||
|
||||
base.GetState(weldJointState);
|
||||
return weldJointState;
|
||||
}
|
||||
|
||||
public override Vector2 GetReactionForce(float invDt)
|
||||
{
|
||||
var P = new Vector2(_impulse.X, _impulse.Y);
|
||||
return P * invDt;
|
||||
}
|
||||
|
||||
public override float GetReactionTorque(float invDt)
|
||||
{
|
||||
return invDt * _impulse.Z;
|
||||
}
|
||||
|
||||
internal override void InitVelocityConstraints(
|
||||
in SolverData data,
|
||||
in SharedPhysicsSystem.IslandData island,
|
||||
PhysicsComponent bodyA,
|
||||
PhysicsComponent bodyB,
|
||||
Vector2[] positions,
|
||||
float[] angles,
|
||||
Vector2[] linearVelocities,
|
||||
float[] angularVelocities)
|
||||
{
|
||||
var offset = island.Offset;
|
||||
_indexA = bodyA.IslandIndex[island.Index];
|
||||
_indexB = bodyB.IslandIndex[island.Index];
|
||||
_localCenterA = bodyA.LocalCenter;
|
||||
_localCenterB = bodyB.LocalCenter;
|
||||
_invMassA = bodyA.InvMass;
|
||||
_invMassB = bodyB.InvMass;
|
||||
_invIA = bodyA.InvI;
|
||||
_invIB = bodyB.InvI;
|
||||
|
||||
float aA = angles[_indexA];
|
||||
var vA = linearVelocities[offset + _indexA];
|
||||
float wA = angularVelocities[offset + _indexA];
|
||||
|
||||
float aB = angles[_indexB];
|
||||
var vB = linearVelocities[offset + _indexB];
|
||||
float wB = angularVelocities[offset + _indexB];
|
||||
|
||||
Quaternion2D qA = new(aA), qB = new(aB);
|
||||
|
||||
_rA = Transform.Mul(qA, LocalAnchorA - _localCenterA);
|
||||
_rB = Transform.Mul(qB, LocalAnchorB - _localCenterB);
|
||||
|
||||
// J = [-I -r1_skew I r2_skew]
|
||||
// [ 0 -1 0 1]
|
||||
// r_skew = [-ry; rx]
|
||||
|
||||
// Matlab
|
||||
// K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB]
|
||||
// [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB]
|
||||
// [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB]
|
||||
|
||||
float mA = _invMassA, mB = _invMassB;
|
||||
float iA = _invIA, iB = _invIB;
|
||||
|
||||
Matrix33 K;
|
||||
K.EX.X = mA + mB + _rA.Y * _rA.Y * iA + _rB.Y * _rB.Y * iB;
|
||||
K.EY.X = -_rA.Y * _rA.X * iA - _rB.Y * _rB.X * iB;
|
||||
K.EZ.X = -_rA.Y * iA - _rB.Y * iB;
|
||||
K.EX.Y = K.EY.X;
|
||||
K.EY.Y = mA + mB + _rA.X * _rA.X * iA + _rB.X * _rB.X * iB;
|
||||
K.EZ.Y = _rA.X * iA + _rB.X * iB;
|
||||
K.EX.Z = K.EZ.X;
|
||||
K.EY.Z = K.EZ.Y;
|
||||
K.EZ.Z = iA + iB;
|
||||
|
||||
if (Stiffness > 0.0f)
|
||||
{
|
||||
K.GetInverse22(ref _mass);
|
||||
|
||||
float invM = iA + iB;
|
||||
|
||||
float C = aB - aA - ReferenceAngle;
|
||||
|
||||
// Damping coefficient
|
||||
float d = Damping;
|
||||
|
||||
// Spring stiffness
|
||||
float k = Stiffness;
|
||||
|
||||
// magic formulas
|
||||
float h = data.FrameTime;
|
||||
_gamma = h * (d + h * k);
|
||||
_gamma = _gamma != 0.0f ? 1.0f / _gamma : 0.0f;
|
||||
Bias = C * h * k * _gamma;
|
||||
|
||||
invM += _gamma;
|
||||
_mass.EZ.Z = invM != 0.0f ? 1.0f / invM : 0.0f;
|
||||
}
|
||||
else if (K.EZ.Z == 0.0f)
|
||||
{
|
||||
K.GetInverse22(ref _mass);
|
||||
_gamma = 0.0f;
|
||||
Bias = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
K.GetSymInverse33(ref _mass);
|
||||
_gamma = 0.0f;
|
||||
Bias = 0.0f;
|
||||
}
|
||||
|
||||
if (data.WarmStarting)
|
||||
{
|
||||
// Scale impulses to support a variable time step.
|
||||
_impulse *= data.DtRatio;
|
||||
|
||||
var P = new Vector2(_impulse.X, _impulse.Y);
|
||||
|
||||
vA -= P * mA;
|
||||
wA -= iA * (Vector2.Cross(_rA, P) + _impulse.Z);
|
||||
|
||||
vB += P * mB;
|
||||
wB += iB * (Vector2.Cross(_rB, P) + _impulse.Z);
|
||||
}
|
||||
else
|
||||
{
|
||||
_impulse = Vector3.Zero;
|
||||
}
|
||||
|
||||
linearVelocities[offset + _indexA] = vA;
|
||||
angularVelocities[offset + _indexA] = wA;
|
||||
linearVelocities[offset + _indexB] = vB;
|
||||
angularVelocities[offset + _indexB] = wB;
|
||||
}
|
||||
|
||||
internal override void SolveVelocityConstraints(
|
||||
in SolverData data,
|
||||
in SharedPhysicsSystem.IslandData island,
|
||||
Vector2[] linearVelocities,
|
||||
float[] angularVelocities)
|
||||
{
|
||||
var offset = island.Offset;
|
||||
var vA = linearVelocities[offset + _indexA];
|
||||
float wA = angularVelocities[offset + _indexA];
|
||||
var vB = linearVelocities[offset + _indexB];
|
||||
float wB = angularVelocities[offset + _indexB];
|
||||
|
||||
float mA = _invMassA, mB = _invMassB;
|
||||
float iA = _invIA, iB = _invIB;
|
||||
|
||||
if (Stiffness > 0.0f)
|
||||
{
|
||||
float Cdot2 = wB - wA;
|
||||
|
||||
float impulse2 = -_mass.EZ.Z * (Cdot2 + Bias + _gamma * _impulse.Z);
|
||||
_impulse.Z += impulse2;
|
||||
|
||||
wA -= iA * impulse2;
|
||||
wB += iB * impulse2;
|
||||
|
||||
var Cdot1 = vB + Vector2.Cross(wB, _rB) - vA - Vector2.Cross(wA, _rA);
|
||||
|
||||
var impulse1 = -_mass.Mul22(Cdot1);
|
||||
_impulse.X += impulse1.X;
|
||||
_impulse.Y += impulse1.Y;
|
||||
|
||||
var P = impulse1;
|
||||
|
||||
vA -= P * mA;
|
||||
wA -= iA * Vector2.Cross(_rA, P);
|
||||
|
||||
vB += P * mB;
|
||||
wB += iB * Vector2.Cross(_rB, P);
|
||||
}
|
||||
else
|
||||
{
|
||||
var Cdot1 = vB + Vector2.Cross(wB, _rB) - vA - Vector2.Cross(wA, _rA);
|
||||
float Cdot2 = wB - wA;
|
||||
var Cdot = new Vector3(Cdot1.X, Cdot1.Y, Cdot2);
|
||||
|
||||
var impulse = -_mass.Mul(Cdot);
|
||||
_impulse += impulse;
|
||||
|
||||
var P = new Vector2(impulse.X, impulse.Y);
|
||||
|
||||
vA -= P * mA;
|
||||
wA -= iA * (Vector2.Cross(_rA, P) + impulse.Z);
|
||||
|
||||
vB += P * mB;
|
||||
wB += iB * (Vector2.Cross(_rB, P) + impulse.Z);
|
||||
}
|
||||
|
||||
linearVelocities[offset + _indexA] = vA;
|
||||
angularVelocities[offset + _indexA] = wA;
|
||||
linearVelocities[offset + _indexB] = vB;
|
||||
angularVelocities[offset + _indexB] = wB;
|
||||
}
|
||||
|
||||
internal override bool SolvePositionConstraints(
|
||||
in SolverData data,
|
||||
Vector2[] positions,
|
||||
float[] angles)
|
||||
{
|
||||
var cA = positions[_indexA];
|
||||
float aA = angles[_indexA];
|
||||
var cB = positions[_indexB];
|
||||
float aB = angles[_indexB];
|
||||
|
||||
Quaternion2D qA = new(aA), qB = new(aB);
|
||||
|
||||
float mA = _invMassA, mB = _invMassB;
|
||||
float iA = _invIA, iB = _invIB;
|
||||
|
||||
var rA = Transform.Mul(qA, LocalAnchorA - _localCenterA);
|
||||
var rB = Transform.Mul(qB, LocalAnchorB - _localCenterB);
|
||||
|
||||
float positionError, angularError;
|
||||
|
||||
Matrix33 K;
|
||||
K.EX.X = mA + mB + rA.Y * rA.Y * iA + rB.Y * rB.Y * iB;
|
||||
K.EY.X = -rA.Y * rA.X * iA - rB.Y * rB.X * iB;
|
||||
K.EZ.X = -rA.Y * iA - rB.Y * iB;
|
||||
K.EX.Y = K.EY.X;
|
||||
K.EY.Y = mA + mB + rA.X * rA.X * iA + rB.X * rB.X * iB;
|
||||
K.EZ.Y = rA.X * iA + rB.X * iB;
|
||||
K.EX.Z = K.EZ.X;
|
||||
K.EY.Z = K.EZ.Y;
|
||||
K.EZ.Z = iA + iB;
|
||||
|
||||
if (Stiffness > 0.0f)
|
||||
{
|
||||
var C1 = cB + rB - cA - rA;
|
||||
|
||||
positionError = C1.Length;
|
||||
angularError = 0.0f;
|
||||
|
||||
var P = -K.Solve22(C1);
|
||||
|
||||
cA -= P * mA;
|
||||
aA -= iA * Vector2.Cross(rA, P);
|
||||
|
||||
cB += P * mB;
|
||||
aB += iB * Vector2.Cross(rB, P);
|
||||
}
|
||||
else
|
||||
{
|
||||
var C1 = cB + rB - cA - rA;
|
||||
float C2 = aB - aA - ReferenceAngle;
|
||||
|
||||
positionError = C1.Length;
|
||||
angularError = Math.Abs(C2);
|
||||
|
||||
Vector3 C = new(C1.X, C1.Y, C2);
|
||||
|
||||
Vector3 impulse;
|
||||
if (K.EZ.Z > 0.0f)
|
||||
{
|
||||
impulse = -K.Solve33(C);
|
||||
}
|
||||
else
|
||||
{
|
||||
var impulse2 = -K.Solve22(C1);
|
||||
impulse = new Vector3(impulse2.X, impulse2.Y, 0.0f);
|
||||
}
|
||||
|
||||
var P = new Vector2(impulse.X, impulse.Y);
|
||||
|
||||
cA -= P * mA;
|
||||
aA -= iA * (Vector2.Cross(rA, P) + impulse.Z);
|
||||
|
||||
cB += P * mB;
|
||||
aB += iB * (Vector2.Cross(rB, P) + impulse.Z);
|
||||
}
|
||||
|
||||
positions[_indexA] = cA;
|
||||
angles[_indexA]= aA;
|
||||
positions[_indexB] = cB;
|
||||
angles[_indexB] = aB;
|
||||
|
||||
return positionError <= PhysicsConstants.LinearSlop && angularError <= PhysicsConstants.AngularSlop;
|
||||
}
|
||||
|
||||
public override Joint Clone(EntityUid uidA, EntityUid uidB)
|
||||
{
|
||||
var weld = new WeldJoint(uidA, uidB, LocalAnchorA, LocalAnchorB, ReferenceAngle)
|
||||
{
|
||||
Enabled = Enabled,
|
||||
Bias = Bias,
|
||||
Damping = Damping,
|
||||
Stiffness = Stiffness,
|
||||
_impulse = _impulse,
|
||||
Breakpoint = Breakpoint
|
||||
};
|
||||
return weld;
|
||||
}
|
||||
|
||||
public override void CopyTo(Joint original)
|
||||
{
|
||||
if (original is not WeldJoint weld)
|
||||
return;
|
||||
|
||||
weld.Enabled = Enabled;
|
||||
weld.Bias = Bias;
|
||||
weld.Damping = Damping;
|
||||
weld.Stiffness = Stiffness;
|
||||
weld._impulse = _impulse;
|
||||
weld.Breakpoint = Breakpoint;
|
||||
}
|
||||
|
||||
public bool Equals(WeldJoint? other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
if (!base.Equals(other)) return false;
|
||||
|
||||
return Stiffness.Equals(other.Stiffness) &&
|
||||
Damping.Equals(other.Damping) &&
|
||||
Bias.Equals(other.Bias);
|
||||
}
|
||||
return new WeldJoint(this);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class WeldJoint : Joint, IEquatable<WeldJoint>
|
||||
{
|
||||
// Shared
|
||||
private float _gamma;
|
||||
private Vector3 _impulse;
|
||||
|
||||
// Temporary
|
||||
private int _indexA;
|
||||
private int _indexB;
|
||||
private Vector2 _rA;
|
||||
private Vector2 _rB;
|
||||
private Vector2 _localCenterA;
|
||||
private Vector2 _localCenterB;
|
||||
private float _invMassA;
|
||||
private float _invMassB;
|
||||
private float _invIA;
|
||||
private float _invIB;
|
||||
private Matrix33 _mass;
|
||||
|
||||
// Settable
|
||||
|
||||
[DataField("stiffness")]
|
||||
public float Stiffness;
|
||||
|
||||
[DataField("damping")]
|
||||
public float Damping;
|
||||
|
||||
[DataField("bias")]
|
||||
public float Bias;
|
||||
|
||||
/// <summary>
|
||||
/// The bodyB angle minus bodyA angle in the reference state (radians).
|
||||
/// </summary>
|
||||
[DataField("referenceAngle")]
|
||||
public float ReferenceAngle;
|
||||
|
||||
/// <summary>
|
||||
/// Used for Serv3 reasons
|
||||
/// </summary>
|
||||
public WeldJoint() {}
|
||||
|
||||
internal WeldJoint(EntityUid bodyA, EntityUid bodyB, Vector2 anchorA, Vector2 anchorB, float referenceAngle) : base(bodyA, bodyB)
|
||||
{
|
||||
LocalAnchorA = anchorA;
|
||||
LocalAnchorB = anchorB;
|
||||
ReferenceAngle = referenceAngle;
|
||||
}
|
||||
|
||||
internal WeldJoint(EntityUid bodyAUid, EntityUid bodyBUid) : base(bodyAUid, bodyBUid) {}
|
||||
|
||||
internal WeldJoint(WeldJointState state) : base(state)
|
||||
{
|
||||
Stiffness = state.Stiffness;
|
||||
Damping = state.Damping;
|
||||
Bias = state.Bias;
|
||||
}
|
||||
|
||||
public override JointType JointType => JointType.Weld;
|
||||
public override JointState GetState()
|
||||
{
|
||||
var weldJointState = new WeldJointState();
|
||||
|
||||
base.GetState(weldJointState);
|
||||
return weldJointState;
|
||||
}
|
||||
|
||||
public override Vector2 GetReactionForce(float invDt)
|
||||
{
|
||||
var P = new Vector2(_impulse.X, _impulse.Y);
|
||||
return P * invDt;
|
||||
}
|
||||
|
||||
public override float GetReactionTorque(float invDt)
|
||||
{
|
||||
return invDt * _impulse.Z;
|
||||
}
|
||||
|
||||
internal override void InitVelocityConstraints(
|
||||
in SolverData data,
|
||||
in SharedPhysicsSystem.IslandData island,
|
||||
PhysicsComponent bodyA,
|
||||
PhysicsComponent bodyB,
|
||||
Vector2[] positions,
|
||||
float[] angles,
|
||||
Vector2[] linearVelocities,
|
||||
float[] angularVelocities)
|
||||
{
|
||||
var offset = island.Offset;
|
||||
_indexA = bodyA.IslandIndex[island.Index];
|
||||
_indexB = bodyB.IslandIndex[island.Index];
|
||||
_localCenterA = bodyA.LocalCenter;
|
||||
_localCenterB = bodyB.LocalCenter;
|
||||
_invMassA = bodyA.InvMass;
|
||||
_invMassB = bodyB.InvMass;
|
||||
_invIA = bodyA.InvI;
|
||||
_invIB = bodyB.InvI;
|
||||
|
||||
float aA = angles[_indexA];
|
||||
var vA = linearVelocities[offset + _indexA];
|
||||
float wA = angularVelocities[offset + _indexA];
|
||||
|
||||
float aB = angles[_indexB];
|
||||
var vB = linearVelocities[offset + _indexB];
|
||||
float wB = angularVelocities[offset + _indexB];
|
||||
|
||||
Quaternion2D qA = new(aA), qB = new(aB);
|
||||
|
||||
_rA = Transform.Mul(qA, LocalAnchorA - _localCenterA);
|
||||
_rB = Transform.Mul(qB, LocalAnchorB - _localCenterB);
|
||||
|
||||
// J = [-I -r1_skew I r2_skew]
|
||||
// [ 0 -1 0 1]
|
||||
// r_skew = [-ry; rx]
|
||||
|
||||
// Matlab
|
||||
// K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB]
|
||||
// [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB]
|
||||
// [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB]
|
||||
|
||||
float mA = _invMassA, mB = _invMassB;
|
||||
float iA = _invIA, iB = _invIB;
|
||||
|
||||
Matrix33 K;
|
||||
K.EX.X = mA + mB + _rA.Y * _rA.Y * iA + _rB.Y * _rB.Y * iB;
|
||||
K.EY.X = -_rA.Y * _rA.X * iA - _rB.Y * _rB.X * iB;
|
||||
K.EZ.X = -_rA.Y * iA - _rB.Y * iB;
|
||||
K.EX.Y = K.EY.X;
|
||||
K.EY.Y = mA + mB + _rA.X * _rA.X * iA + _rB.X * _rB.X * iB;
|
||||
K.EZ.Y = _rA.X * iA + _rB.X * iB;
|
||||
K.EX.Z = K.EZ.X;
|
||||
K.EY.Z = K.EZ.Y;
|
||||
K.EZ.Z = iA + iB;
|
||||
|
||||
if (Stiffness > 0.0f)
|
||||
{
|
||||
K.GetInverse22(ref _mass);
|
||||
|
||||
float invM = iA + iB;
|
||||
|
||||
float C = aB - aA - ReferenceAngle;
|
||||
|
||||
// Damping coefficient
|
||||
float d = Damping;
|
||||
|
||||
// Spring stiffness
|
||||
float k = Stiffness;
|
||||
|
||||
// magic formulas
|
||||
float h = data.FrameTime;
|
||||
_gamma = h * (d + h * k);
|
||||
_gamma = _gamma != 0.0f ? 1.0f / _gamma : 0.0f;
|
||||
Bias = C * h * k * _gamma;
|
||||
|
||||
invM += _gamma;
|
||||
_mass.EZ.Z = invM != 0.0f ? 1.0f / invM : 0.0f;
|
||||
}
|
||||
else if (K.EZ.Z == 0.0f)
|
||||
{
|
||||
K.GetInverse22(ref _mass);
|
||||
_gamma = 0.0f;
|
||||
Bias = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
K.GetSymInverse33(ref _mass);
|
||||
_gamma = 0.0f;
|
||||
Bias = 0.0f;
|
||||
}
|
||||
|
||||
if (data.WarmStarting)
|
||||
{
|
||||
// Scale impulses to support a variable time step.
|
||||
_impulse *= data.DtRatio;
|
||||
|
||||
var P = new Vector2(_impulse.X, _impulse.Y);
|
||||
|
||||
vA -= P * mA;
|
||||
wA -= iA * (Vector2.Cross(_rA, P) + _impulse.Z);
|
||||
|
||||
vB += P * mB;
|
||||
wB += iB * (Vector2.Cross(_rB, P) + _impulse.Z);
|
||||
}
|
||||
else
|
||||
{
|
||||
_impulse = Vector3.Zero;
|
||||
}
|
||||
|
||||
linearVelocities[offset + _indexA] = vA;
|
||||
angularVelocities[offset + _indexA] = wA;
|
||||
linearVelocities[offset + _indexB] = vB;
|
||||
angularVelocities[offset + _indexB] = wB;
|
||||
}
|
||||
|
||||
internal override void SolveVelocityConstraints(
|
||||
in SolverData data,
|
||||
in SharedPhysicsSystem.IslandData island,
|
||||
Vector2[] linearVelocities,
|
||||
float[] angularVelocities)
|
||||
{
|
||||
var offset = island.Offset;
|
||||
var vA = linearVelocities[offset + _indexA];
|
||||
float wA = angularVelocities[offset + _indexA];
|
||||
var vB = linearVelocities[offset + _indexB];
|
||||
float wB = angularVelocities[offset + _indexB];
|
||||
|
||||
float mA = _invMassA, mB = _invMassB;
|
||||
float iA = _invIA, iB = _invIB;
|
||||
|
||||
if (Stiffness > 0.0f)
|
||||
{
|
||||
float Cdot2 = wB - wA;
|
||||
|
||||
float impulse2 = -_mass.EZ.Z * (Cdot2 + Bias + _gamma * _impulse.Z);
|
||||
_impulse.Z += impulse2;
|
||||
|
||||
wA -= iA * impulse2;
|
||||
wB += iB * impulse2;
|
||||
|
||||
var Cdot1 = vB + Vector2.Cross(wB, _rB) - vA - Vector2.Cross(wA, _rA);
|
||||
|
||||
var impulse1 = -_mass.Mul22(Cdot1);
|
||||
_impulse.X += impulse1.X;
|
||||
_impulse.Y += impulse1.Y;
|
||||
|
||||
var P = impulse1;
|
||||
|
||||
vA -= P * mA;
|
||||
wA -= iA * Vector2.Cross(_rA, P);
|
||||
|
||||
vB += P * mB;
|
||||
wB += iB * Vector2.Cross(_rB, P);
|
||||
}
|
||||
else
|
||||
{
|
||||
var Cdot1 = vB + Vector2.Cross(wB, _rB) - vA - Vector2.Cross(wA, _rA);
|
||||
float Cdot2 = wB - wA;
|
||||
var Cdot = new Vector3(Cdot1.X, Cdot1.Y, Cdot2);
|
||||
|
||||
var impulse = -_mass.Mul(Cdot);
|
||||
_impulse += impulse;
|
||||
|
||||
var P = new Vector2(impulse.X, impulse.Y);
|
||||
|
||||
vA -= P * mA;
|
||||
wA -= iA * (Vector2.Cross(_rA, P) + impulse.Z);
|
||||
|
||||
vB += P * mB;
|
||||
wB += iB * (Vector2.Cross(_rB, P) + impulse.Z);
|
||||
}
|
||||
|
||||
linearVelocities[offset + _indexA] = vA;
|
||||
angularVelocities[offset + _indexA] = wA;
|
||||
linearVelocities[offset + _indexB] = vB;
|
||||
angularVelocities[offset + _indexB] = wB;
|
||||
}
|
||||
|
||||
internal override bool SolvePositionConstraints(
|
||||
in SolverData data,
|
||||
Vector2[] positions,
|
||||
float[] angles)
|
||||
{
|
||||
var cA = positions[_indexA];
|
||||
float aA = angles[_indexA];
|
||||
var cB = positions[_indexB];
|
||||
float aB = angles[_indexB];
|
||||
|
||||
Quaternion2D qA = new(aA), qB = new(aB);
|
||||
|
||||
float mA = _invMassA, mB = _invMassB;
|
||||
float iA = _invIA, iB = _invIB;
|
||||
|
||||
var rA = Transform.Mul(qA, LocalAnchorA - _localCenterA);
|
||||
var rB = Transform.Mul(qB, LocalAnchorB - _localCenterB);
|
||||
|
||||
float positionError, angularError;
|
||||
|
||||
Matrix33 K;
|
||||
K.EX.X = mA + mB + rA.Y * rA.Y * iA + rB.Y * rB.Y * iB;
|
||||
K.EY.X = -rA.Y * rA.X * iA - rB.Y * rB.X * iB;
|
||||
K.EZ.X = -rA.Y * iA - rB.Y * iB;
|
||||
K.EX.Y = K.EY.X;
|
||||
K.EY.Y = mA + mB + rA.X * rA.X * iA + rB.X * rB.X * iB;
|
||||
K.EZ.Y = rA.X * iA + rB.X * iB;
|
||||
K.EX.Z = K.EZ.X;
|
||||
K.EY.Z = K.EZ.Y;
|
||||
K.EZ.Z = iA + iB;
|
||||
|
||||
if (Stiffness > 0.0f)
|
||||
{
|
||||
var C1 = cB + rB - cA - rA;
|
||||
|
||||
positionError = C1.Length;
|
||||
angularError = 0.0f;
|
||||
|
||||
var P = -K.Solve22(C1);
|
||||
|
||||
cA -= P * mA;
|
||||
aA -= iA * Vector2.Cross(rA, P);
|
||||
|
||||
cB += P * mB;
|
||||
aB += iB * Vector2.Cross(rB, P);
|
||||
}
|
||||
else
|
||||
{
|
||||
var C1 = cB + rB - cA - rA;
|
||||
float C2 = aB - aA - ReferenceAngle;
|
||||
|
||||
positionError = C1.Length;
|
||||
angularError = Math.Abs(C2);
|
||||
|
||||
Vector3 C = new(C1.X, C1.Y, C2);
|
||||
|
||||
Vector3 impulse;
|
||||
if (K.EZ.Z > 0.0f)
|
||||
{
|
||||
impulse = -K.Solve33(C);
|
||||
}
|
||||
else
|
||||
{
|
||||
var impulse2 = -K.Solve22(C1);
|
||||
impulse = new Vector3(impulse2.X, impulse2.Y, 0.0f);
|
||||
}
|
||||
|
||||
var P = new Vector2(impulse.X, impulse.Y);
|
||||
|
||||
cA -= P * mA;
|
||||
aA -= iA * (Vector2.Cross(rA, P) + impulse.Z);
|
||||
|
||||
cB += P * mB;
|
||||
aB += iB * (Vector2.Cross(rB, P) + impulse.Z);
|
||||
}
|
||||
|
||||
positions[_indexA] = cA;
|
||||
angles[_indexA]= aA;
|
||||
positions[_indexB] = cB;
|
||||
angles[_indexB] = aB;
|
||||
|
||||
return positionError <= PhysicsConstants.LinearSlop && angularError <= PhysicsConstants.AngularSlop;
|
||||
}
|
||||
|
||||
public override Joint Clone(EntityUid uidA, EntityUid uidB)
|
||||
{
|
||||
var weld = new WeldJoint(uidA, uidB, LocalAnchorA, LocalAnchorB, ReferenceAngle)
|
||||
{
|
||||
Enabled = Enabled,
|
||||
Bias = Bias,
|
||||
Damping = Damping,
|
||||
Stiffness = Stiffness,
|
||||
_impulse = _impulse,
|
||||
Breakpoint = Breakpoint
|
||||
};
|
||||
return weld;
|
||||
}
|
||||
|
||||
public override void CopyTo(Joint original)
|
||||
{
|
||||
if (original is not WeldJoint weld)
|
||||
return;
|
||||
|
||||
weld.Enabled = Enabled;
|
||||
weld.Bias = Bias;
|
||||
weld.Damping = Damping;
|
||||
weld.Stiffness = Stiffness;
|
||||
weld._impulse = _impulse;
|
||||
weld.Breakpoint = Breakpoint;
|
||||
}
|
||||
|
||||
public bool Equals(WeldJoint? other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
if (!base.Equals(other)) return false;
|
||||
|
||||
return Stiffness.Equals(other.Stiffness) &&
|
||||
Damping.Equals(other.Damping) &&
|
||||
Bias.Equals(other.Bias);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,8 @@ namespace Robust.Shared.Physics.Systems
|
||||
internal void FindNewContacts(PhysicsMapComponent component, MapId mapId)
|
||||
{
|
||||
var moveBuffer = component.MoveBuffer;
|
||||
var movedGrids = _mapManager.GetMovedGrids(mapId);
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
var movedGrids = Comp<MovedGridsComponent>(mapUid).MovedGrids;
|
||||
var gridMoveBuffer = new Dictionary<FixtureProxy, Box2>();
|
||||
|
||||
var broadphaseQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
@@ -265,7 +266,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
ArrayPool<List<FixtureProxy>>.Shared.Return(contactBuffer);
|
||||
ArrayPool<(FixtureProxy Proxy, Box2 AABB)>.Shared.Return(pMoveBuffer);
|
||||
moveBuffer.Clear();
|
||||
_mapManager.ClearMovedGrids(mapId);
|
||||
movedGrids.Clear();
|
||||
}
|
||||
|
||||
private void HandleGridCollisions(
|
||||
|
||||
@@ -177,6 +177,16 @@ namespace Robust.UnitTesting
|
||||
compFactory.RegisterClass<JointComponent>();
|
||||
}
|
||||
|
||||
if (!compFactory.AllRegisteredTypes.Contains(typeof(GridTreeComponent)))
|
||||
{
|
||||
compFactory.RegisterClass<GridTreeComponent>();
|
||||
}
|
||||
|
||||
if (!compFactory.AllRegisteredTypes.Contains(typeof(MovedGridsComponent)))
|
||||
{
|
||||
compFactory.RegisterClass<MovedGridsComponent>();
|
||||
}
|
||||
|
||||
if (!compFactory.AllRegisteredTypes.Contains(typeof(JointRelayTargetComponent)))
|
||||
{
|
||||
compFactory.RegisterClass<JointRelayTargetComponent>();
|
||||
|
||||
@@ -270,6 +270,8 @@ namespace Robust.UnitTesting.Server
|
||||
compFactory.RegisterClass<MapLightComponent>();
|
||||
compFactory.RegisterClass<PhysicsComponent>();
|
||||
compFactory.RegisterClass<JointComponent>();
|
||||
compFactory.RegisterClass<GridTreeComponent>();
|
||||
compFactory.RegisterClass<MovedGridsComponent>();
|
||||
compFactory.RegisterClass<JointRelayTargetComponent>();
|
||||
compFactory.RegisterClass<BroadphaseComponent>();
|
||||
compFactory.RegisterClass<ContainerManagerComponent>();
|
||||
|
||||
Reference in New Issue
Block a user