diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Bumper/BumperColliderInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Bumper/BumperColliderInspector.cs index bcc483637..6168e070e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Bumper/BumperColliderInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Bumper/BumperColliderInspector.cs @@ -28,6 +28,7 @@ public class BumperColliderInspector : ColliderInspector 0) { var hitTime = dTime; // begin time search from now ... until delta ends @@ -63,9 +67,21 @@ internal void Simulate(ref PhysicsState state, in AABB playfieldBounds, ref Nati continue; } - // hit testing + // init contacts and event + ball.CollisionEvent.ClearCollider(hitTime); // search upto current hit time + + // hit testing (overlappingColliders is cleared in broad phase) PhysicsStaticBroadPhase.FindOverlaps(in state.Octree, in ball, ref overlappingColliders); - PhysicsStaticNarrowPhase.FindNextCollision(hitTime, ref ball, ref overlappingColliders, ref _contacts, ref state); + PhysicsStaticNarrowPhase.FindNextCollision(ref state.Colliders, ref ball, ref overlappingColliders, ref _contacts, ref state); + + PhysicsStaticBroadPhase.FindOverlaps(in kineticOctree, in ball, ref overlappingColliders); + PhysicsStaticNarrowPhase.FindNextCollision(ref state.KinematicColliders, ref ball, ref overlappingColliders, ref _contacts, ref state); + + // no negative time allowed + if (ball.CollisionEvent.HitTime < 0) { + ball.CollisionEvent.ClearCollider(); + } + PhysicsDynamicBroadPhase.FindOverlaps(in ballOctree, in ball, ref overlappingColliders, ref state.Balls); PhysicsDynamicNarrowPhase.FindNextCollision(ref ball, ref overlappingColliders, ref _contacts, ref state); @@ -87,7 +103,7 @@ internal void Simulate(ref PhysicsState state, in AABB playfieldBounds, ref Nati using (var enumerator = state.FlipperStates.GetEnumerator()) { while (enumerator.MoveNext()) { ref var flipperState = ref enumerator.Current.Value; - FlipperDisplacementPhysics.UpdateDisplacement(flipperState.ItemId, ref flipperState.Movement, + FlipperDisplacementPhysics.UpdateDisplacement(enumerator.Current.Key, ref flipperState.Movement, ref flipperState.Tricks, in flipperState.Static, hitTime, ref state.EventQueue); } } @@ -95,7 +111,7 @@ internal void Simulate(ref PhysicsState state, in AABB playfieldBounds, ref Nati using (var enumerator = state.GateStates.GetEnumerator()) { while (enumerator.MoveNext()) { ref var gateState = ref enumerator.Current.Value; - GateDisplacementPhysics.UpdateDisplacement(gateState.ItemId, ref gateState.Movement, in gateState.Static, + GateDisplacementPhysics.UpdateDisplacement(enumerator.Current.Key, ref gateState.Movement, in gateState.Static, hitTime, ref state.EventQueue); } } @@ -103,7 +119,7 @@ internal void Simulate(ref PhysicsState state, in AABB playfieldBounds, ref Nati using (var enumerator = state.PlungerStates.GetEnumerator()) { while (enumerator.MoveNext()) { ref var plungerState = ref enumerator.Current.Value; - PlungerDisplacementPhysics.UpdateDisplacement(plungerState.ItemId, ref plungerState.Movement, ref plungerState.Collider, + PlungerDisplacementPhysics.UpdateDisplacement(enumerator.Current.Key, ref plungerState.Movement, ref plungerState.Collider, in plungerState.Static, hitTime, ref state.EventQueue); } } @@ -111,7 +127,7 @@ internal void Simulate(ref PhysicsState state, in AABB playfieldBounds, ref Nati using (var enumerator = state.SpinnerStates.GetEnumerator()) { while (enumerator.MoveNext()) { ref var spinnerState = ref enumerator.Current.Value; - SpinnerDisplacementPhysics.UpdateDisplacement(spinnerState.ItemId, ref spinnerState.Movement, in spinnerState.Static, + SpinnerDisplacementPhysics.UpdateDisplacement(enumerator.Current.Key, ref spinnerState.Movement, in spinnerState.Static, hitTime, ref state.EventQueue); } } @@ -128,7 +144,7 @@ internal void Simulate(ref PhysicsState state, in AABB playfieldBounds, ref Nati // dynamic collision PhysicsDynamicCollision.Collide(hitTime, ref ball, ref state); - // static collision + // static & kinematic collision PhysicsStaticCollision.Collide(hitTime, ref ball, ref state); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsEngine.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsEngine.cs index b6fb6007c..3598dc65a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsEngine.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsEngine.cs @@ -45,6 +45,9 @@ public class PhysicsEngine : MonoBehaviour [NonSerialized] private InsideOfs _insideOfs; [NonSerialized] private NativeOctree _octree; [NonSerialized] private NativeColliders _colliders; + [NonSerialized] private NativeColliders _kinematicColliders; + [NonSerialized] private NativeColliders _kinematicCollidersAtIdentity; + [NonSerialized] private NativeParallelHashMap _kinematicColliderLookups; [NonSerialized] private NativeArray _physicsEnv = new(1, Allocator.Persistent); [NonSerialized] private NativeQueue _eventQueue = new(Allocator.Persistent); @@ -67,6 +70,8 @@ public class PhysicsEngine : MonoBehaviour #region Transforms [NonSerialized] private readonly Dictionary _transforms = new(); + [NonSerialized] private NativeParallelHashMap _kinematicTransforms = new(0, Allocator.Persistent); + [NonSerialized] private NativeParallelHashMap _updatedKinematicTransforms = new(0, Allocator.Persistent); [NonSerialized] private readonly Dictionary _skinnedMeshRenderers = new(); #endregion @@ -75,6 +80,8 @@ public class PhysicsEngine : MonoBehaviour [NonSerialized] private readonly List _scheduledActions = new(); [NonSerialized] private Player _player; + [NonSerialized] private IKinematicColliderComponent[] _kinematicColliderComponents; + private static ulong NowUsec => (ulong)(Time.timeAsDouble * 1000000); @@ -166,6 +173,7 @@ private void Awake() _player = GetComponentInParent(); _insideOfs = new InsideOfs(Allocator.Persistent); _physicsEnv[0] = new PhysicsEnv(NowUsec, GetComponentInChildren(), GravityStrength); + _kinematicColliderComponents = GetComponentsInChildren(); } private void Start() @@ -175,15 +183,27 @@ private void Start() var colliderItems = GetComponentsInChildren(); Debug.Log($"Found {colliderItems.Length} collidable items."); var colliders = new ColliderReference(Allocator.TempJob); + var kinematicColliders = new ColliderReference(Allocator.TempJob, true); foreach (var colliderItem in colliderItems) { if (!colliderItem.IsCollidable) { _disabledCollisionItems.Add(colliderItem.ItemId); } - colliderItem.GetColliders(_player, ref colliders, 0); + colliderItem.GetColliders(_player, ref colliders, ref kinematicColliders, 0); } // allocate colliders _colliders = new NativeColliders(ref colliders, Allocator.Persistent); + _kinematicColliders = new NativeColliders(ref kinematicColliders, Allocator.Persistent); + + // get kinetic collider matrices + foreach (var coll in _kinematicColliderComponents) { + _kinematicTransforms[coll.ItemId] = coll.TransformationMatrix; + } + _kinematicColliderLookups = kinematicColliders.CreateLookup(Allocator.Persistent); + + // create identity kinematic colliders + kinematicColliders.TransformToIdentity(_kinematicTransforms); + _kinematicCollidersAtIdentity = new NativeColliders(ref kinematicColliders, Allocator.Persistent); // create octree var elapsedMs = sw.Elapsed.TotalMilliseconds; @@ -209,6 +229,21 @@ private void Start() private void Update() { + // check for updated kinematic transforms + _updatedKinematicTransforms.Clear(); + foreach (var coll in _kinematicColliderComponents) { + if (!coll.IsKinematic) { // kinematic enabled? + continue; + } + var lastTransformationMatrix = _kinematicTransforms[coll.ItemId]; + var currTransformationMatrix = coll.TransformationMatrix; + if (lastTransformationMatrix.Equals(currTransformationMatrix)) { + continue; + } + _updatedKinematicTransforms.Add(coll.ItemId, currTransformationMatrix); + _kinematicTransforms[coll.ItemId] = currTransformationMatrix; + } + // prepare job var events = _eventQueue.AsParallelWriter(); var updatePhysics = new PhysicsUpdateJob { @@ -217,6 +252,10 @@ private void Update() PhysicsEnv = _physicsEnv, Octree = _octree, Colliders = _colliders, + KinematicColliders = _kinematicColliders, + KinematicCollidersAtIdentity = _kinematicCollidersAtIdentity, + KinematicColliderLookups = _kinematicColliderLookups, + UpdatedKinematicTransforms = _updatedKinematicTransforms, InsideOfs = _insideOfs, Events = events, Balls = _ballStates, @@ -236,8 +275,9 @@ private void Update() }; var env = _physicsEnv[0]; - var state = new PhysicsState(ref env, ref _octree, ref _colliders, ref events, ref _insideOfs, ref _ballStates, - ref _bumperStates, ref _dropTargetStates, ref _flipperStates, ref _gateStates, + var state = new PhysicsState(ref env, ref _octree, ref _colliders, ref _kinematicColliders, + ref _kinematicCollidersAtIdentity, ref _updatedKinematicTransforms, ref _kinematicColliderLookups, ref events, + ref _insideOfs, ref _ballStates, ref _bumperStates, ref _dropTargetStates, ref _flipperStates, ref _gateStates, ref _hitTargetStates, ref _kickerStates, ref _plungerStates, ref _spinnerStates, ref _surfaceStates, ref _triggerStates, ref _disabledCollisionItems, ref _swapBallCollisionHandling); @@ -402,6 +442,15 @@ private void OnDestroy() } _triggerStates.Dispose(); _disabledCollisionItems.Dispose(); + _kinematicTransforms.Dispose(); + _updatedKinematicTransforms.Dispose(); + using (var enumerator = _kinematicColliderLookups.GetEnumerator()) { + while (enumerator.MoveNext()) { + enumerator.Current.Value.Dispose(); + } + } + _kinematicColliderLookups.Dispose(); + } #endregion @@ -417,5 +466,16 @@ public ScheduledAction(ulong scheduleAt, Action action) Action = action; } } + + public ICollider[] GetKinematicColliders(int itemId) + { + ref var colliderIds = ref _kinematicColliderLookups.GetValueByRef(itemId); + var colliders = new ICollider[colliderIds.Length]; + for (var i = 0; i < colliderIds.Length; i++) { + var colliderId = colliderIds[i]; + colliders[i] = _kinematicColliders[colliderId]; + } + return colliders; + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsKinematicBroadPhase.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsKinematicBroadPhase.cs new file mode 100644 index 000000000..04b30ead8 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsKinematicBroadPhase.cs @@ -0,0 +1,55 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using NativeTrees; +using Unity.Collections; +using Unity.Profiling; +using VisualPinball.Unity.Collections; + +namespace VisualPinball.Unity +{ + public static class PhysicsKinematicBroadPhase + { + private static readonly ProfilerMarker PerfMarkerBallOctree = new("CreateKinematicOctree"); + + internal static void TransformColliders(ref PhysicsState state) + { + using var enumerator = state.UpdatedKinematicTransforms.GetEnumerator(); + while (enumerator.MoveNext()) { + ref var matrix = ref enumerator.Current.Value; + var itemId = enumerator.Current.Key; + + ref var colliderLookups = ref state.KinematicColliderLookups.GetValueByRef(itemId); + for (var i = 0; i < colliderLookups.Length; i++) { + state.Transform(colliderLookups[i], matrix); + } + } + } + + internal static NativeOctree CreateOctree(ref NativeColliders kinematicColliders, in AABB playfieldBounds) + { + PerfMarkerBallOctree.Begin(); + var octree = new NativeOctree(playfieldBounds, 1024, 10, Allocator.TempJob); + + for (var i = 0; i < kinematicColliders.Length; i++) { + octree.Insert(i, kinematicColliders.GetAabb(i)); + } + + PerfMarkerBallOctree.End(); + return octree; + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsKinematicBroadPhase.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsKinematicBroadPhase.cs.meta new file mode 100644 index 000000000..522a1e5da --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsKinematicBroadPhase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 04ece8618aa24f0c8bc5a4437ae4a7b9 +timeCreated: 1699026328 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsState.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsState.cs index c7a631bb9..44b21cd96 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsState.cs @@ -16,6 +16,7 @@ using NativeTrees; using Unity.Collections; +using Unity.Mathematics; using VisualPinball.Engine.VPT; using VisualPinball.Unity.Collections; @@ -26,6 +27,11 @@ internal struct PhysicsState internal PhysicsEnv Env; internal NativeOctree Octree; internal NativeColliders Colliders; + internal NativeColliders KinematicColliders; + internal NativeColliders KinematicCollidersAtIdentity; + internal NativeParallelHashMap UpdatedKinematicTransforms; + internal NativeParallelHashMap KinematicColliderLookups; + internal NativeQueue.ParallelWriter EventQueue; internal InsideOfs InsideOfs; internal NativeParallelHashMap Balls; @@ -43,7 +49,10 @@ internal struct PhysicsState internal bool SwapBallCollisionHandling; public PhysicsState(ref PhysicsEnv env, ref NativeOctree octree, ref NativeColliders colliders, - ref NativeQueue.ParallelWriter eventQueue, ref InsideOfs insideOfs, ref NativeParallelHashMap balls, + ref NativeColliders kinematicColliders, ref NativeColliders kinematicCollidersAtIdentity, + ref NativeParallelHashMap updatedKinematicTransforms, + ref NativeParallelHashMap kinematicColliderLookups, ref NativeQueue.ParallelWriter eventQueue, + ref InsideOfs insideOfs, ref NativeParallelHashMap balls, ref NativeParallelHashMap bumperStates, ref NativeParallelHashMap dropTargetStates, ref NativeParallelHashMap flipperStates, ref NativeParallelHashMap gateStates, ref NativeParallelHashMap hitTargetStates, ref NativeParallelHashMap kickerStates, @@ -54,6 +63,10 @@ public PhysicsState(ref PhysicsEnv env, ref NativeOctree octree, ref Native Env = env; Octree = octree; Colliders = colliders; + KinematicColliders = kinematicColliders; + KinematicCollidersAtIdentity = kinematicCollidersAtIdentity; + UpdatedKinematicTransforms = updatedKinematicTransforms; + KinematicColliderLookups = kinematicColliderLookups; EventQueue = eventQueue; InsideOfs = insideOfs; Balls = balls; @@ -71,10 +84,10 @@ public PhysicsState(ref PhysicsEnv env, ref NativeOctree octree, ref Native SwapBallCollisionHandling = swapBallCollisionHandling; } - internal ref ColliderHeader GetColliderHeader(int colliderId) => ref Colliders.GetHeader(colliderId); - internal ColliderType GetColliderType(int colliderId) => Colliders.GetHeader(colliderId).Type; + internal ref ColliderHeader GetColliderHeader(ref NativeColliders colliders, int colliderId) => ref colliders.GetHeader(colliderId); + internal ColliderType GetColliderType(ref NativeColliders colliders, int colliderId) => colliders.GetHeader(colliderId).Type; - internal bool IsColliderActive(int colliderId) => !DisabledCollisionItems.Contains(Colliders.GetItemId(colliderId)); + internal bool IsColliderActive(ref NativeColliders colliders, int colliderId) => !DisabledCollisionItems.Contains(colliders.GetItemId(colliderId)); #region States @@ -88,7 +101,6 @@ public PhysicsState(ref PhysicsEnv env, ref NativeOctree octree, ref Native internal ref KickerState GetKickerState(int colliderId) => ref KickerStates.GetValueByRef(Colliders.GetItemId(colliderId)); - internal bool HasDropTargetState(int colliderId) => DropTargetStates.ContainsKey(Colliders.GetItemId(colliderId)); internal bool HasHitTargetState(int colliderId) => HitTargetStates.ContainsKey(Colliders.GetItemId(colliderId)); @@ -97,7 +109,7 @@ public PhysicsState(ref PhysicsEnv env, ref NativeOctree octree, ref Native internal ref HitTargetState GetHitTargetState(int colliderId) => ref HitTargetStates.GetValueByRef(Colliders.GetItemId(colliderId)); - internal ref BumperState GetBumperState(int colliderId) => ref BumperStates.GetValueByRef(Colliders.GetItemId(colliderId)); + internal ref BumperState GetBumperState(int colliderId, ref NativeColliders col) => ref BumperStates.GetValueByRef(col.GetItemId(colliderId)); internal ref GateState GetGateState(int colliderId) => ref GateStates.GetValueByRef(Colliders.GetItemId(colliderId)); @@ -105,75 +117,99 @@ public PhysicsState(ref PhysicsEnv env, ref NativeOctree octree, ref Native #endregion + #region Transform + + internal void Transform(int colliderId, float4x4 matrix) + { + switch (GetColliderType(ref KinematicColliders, colliderId)) + { + case ColliderType.Bumper: + case ColliderType.Circle: + KinematicColliders.Circle(colliderId).Transform(KinematicCollidersAtIdentity.Circle(colliderId), matrix); + break; + case ColliderType.Point: + KinematicColliders.Point(colliderId).Transform(KinematicCollidersAtIdentity.Point(colliderId), matrix); + break; + case ColliderType.Line3D: + KinematicColliders.Line3D(colliderId).Transform(KinematicCollidersAtIdentity.Line3D(colliderId), matrix); + break; + case ColliderType.Triangle: + KinematicColliders.Triangle(colliderId).Transform(KinematicCollidersAtIdentity.Triangle(colliderId), matrix); + break; + } + } + + #endregion + #region Hit Test - internal float HitTest(int colliderId, ref BallState ball, ref CollisionEventData newCollEvent, ref NativeList contacts, ref PhysicsState state) + internal float HitTest(ref NativeColliders colliders, int colliderId, ref BallState ball, ref CollisionEventData newCollEvent, ref NativeList contacts) { - if (IsInactiveDropTarget(colliderId)) { + if (IsInactiveDropTarget(ref colliders, colliderId)) { return -1f; } - switch (GetColliderType(colliderId)) { + switch (GetColliderType(ref colliders, colliderId)) { case ColliderType.Bumper: case ColliderType.Circle: - return Colliders.Circle(colliderId).HitTest(ref newCollEvent, ref state.InsideOfs, in ball, + return colliders.Circle(colliderId).HitTest(ref newCollEvent, ref InsideOfs, in ball, ball.CollisionEvent.HitTime); case ColliderType.Gate: - return Colliders.Gate(colliderId).HitTest(ref newCollEvent, ref state.InsideOfs, in ball, + return colliders.Gate(colliderId).HitTest(ref newCollEvent, ref InsideOfs, in ball, ball.CollisionEvent.HitTime); case ColliderType.Line: - return Colliders.Line(colliderId).HitTest(ref newCollEvent, ref state.InsideOfs, in ball, + return colliders.Line(colliderId).HitTest(ref newCollEvent, ref InsideOfs, in ball, ball.CollisionEvent.HitTime); case ColliderType.LineZ: - return Colliders.LineZ(colliderId).HitTest(ref newCollEvent, in ball, ball.CollisionEvent.HitTime); + return colliders.LineZ(colliderId).HitTest(ref newCollEvent, in ball, ball.CollisionEvent.HitTime); case ColliderType.Line3D: - return Colliders.Line3D(colliderId).HitTest(ref newCollEvent, in ball, ball.CollisionEvent.HitTime); + return colliders.Line3D(colliderId).HitTest(ref newCollEvent, in ball, ball.CollisionEvent.HitTime); case ColliderType.LineSlingShot: - return Colliders.LineSlingShot(colliderId).HitTest(ref newCollEvent, ref state.InsideOfs, in ball, ball.CollisionEvent.HitTime); + return colliders.LineSlingShot(colliderId).HitTest(ref newCollEvent, ref InsideOfs, in ball, ball.CollisionEvent.HitTime); case ColliderType.Point: - return Colliders.Point(colliderId).HitTest(ref newCollEvent, in ball, ball.CollisionEvent.HitTime); + return colliders.Point(colliderId).HitTest(ref newCollEvent, in ball, ball.CollisionEvent.HitTime); case ColliderType.Plane: - return Colliders.Plane(colliderId).HitTest(ref newCollEvent, in ball, ball.CollisionEvent.HitTime); + return colliders.Plane(colliderId).HitTest(ref newCollEvent, in ball, ball.CollisionEvent.HitTime); case ColliderType.Spinner: - return Colliders.Spinner(colliderId).HitTest(ref newCollEvent, ref state.InsideOfs, in ball, + return colliders.Spinner(colliderId).HitTest(ref newCollEvent, ref InsideOfs, in ball, ball.CollisionEvent.HitTime); case ColliderType.Triangle: - return Colliders.Triangle(colliderId).HitTest(ref newCollEvent, in state.InsideOfs, in ball, + return colliders.Triangle(colliderId).HitTest(ref newCollEvent, in InsideOfs, in ball, ball.CollisionEvent.HitTime); case ColliderType.KickerCircle: case ColliderType.TriggerCircle: - return Colliders.Circle(colliderId).HitTestBasicRadius(ref newCollEvent, ref state.InsideOfs, in ball, + return colliders.Circle(colliderId).HitTestBasicRadius(ref newCollEvent, ref InsideOfs, in ball, ball.CollisionEvent.HitTime, false, false, false); case ColliderType.TriggerLine: - return Colliders.Line(colliderId).HitTestBasic(ref newCollEvent, ref state.InsideOfs, in ball, + return colliders.Line(colliderId).HitTestBasic(ref newCollEvent, ref InsideOfs, in ball, ball.CollisionEvent.HitTime, false, false, false); case ColliderType.Flipper: - ref var flipperState = ref state.GetFlipperState(colliderId); - return Colliders.Flipper(colliderId).HitTest(ref newCollEvent, ref state.InsideOfs, ref flipperState.Hit, + ref var flipperState = ref GetFlipperState(colliderId); + return colliders.Flipper(colliderId).HitTest(ref newCollEvent, ref InsideOfs, ref flipperState.Hit, in flipperState.Movement, in flipperState.Tricks, in flipperState.Static, in ball, ball.CollisionEvent.HitTime); case ColliderType.Plunger: - ref var plungerState = ref state.GetPlungerState(colliderId); - return Colliders.Plunger(colliderId).HitTest(ref newCollEvent, ref state.InsideOfs, ref plungerState.Movement, + ref var plungerState = ref GetPlungerState(colliderId); + return colliders.Plunger(colliderId).HitTest(ref newCollEvent, ref InsideOfs, ref plungerState.Movement, in plungerState.Collider, in plungerState.Static, in ball, ball.CollisionEvent.HitTime); } return -1f; } - private bool IsInactiveDropTarget(int colliderId) + private bool IsInactiveDropTarget(ref NativeColliders colliders, int colliderId) { - if (Colliders.GetItemType(colliderId) == ItemType.HitTarget && HasDropTargetState(colliderId)) { + if (colliders.GetItemType(colliderId) == ItemType.HitTarget && HasDropTargetState(colliderId)) { ref var dropTargetState = ref GetDropTargetState(colliderId); if (dropTargetState.Animation.IsDropped || dropTargetState.Animation.MoveAnimation) { // QUICKFIX so that DT is not triggered twice return true; diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsStaticCollision.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsStaticCollision.cs index b8432e7e5..082f6ccb0 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsStaticCollision.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsStaticCollision.cs @@ -25,64 +25,71 @@ internal static class PhysicsStaticCollision { internal static void Collide(float hitTime, ref BallState ball, ref PhysicsState state) { - // find balls with hit objects and minimum time if (ball.CollisionEvent.ColliderId < 0 || ball.CollisionEvent.HitTime > hitTime) { return; } + if (ball.CollisionEvent.IsKinematic) { + Collide(ref state.KinematicColliders, ref ball, ref state); + } else { + Collide(ref state.Colliders, ref ball, ref state); + } + } + + private static void Collide(ref NativeColliders colliders, ref BallState ball, ref PhysicsState state) + { var colliderId = ball.CollisionEvent.ColliderId; - var collHeader = state.GetColliderHeader(colliderId); - if (CollidesWithItem(ref collHeader, ref ball, ref state)) { + var collHeader = state.GetColliderHeader(ref colliders, colliderId); + if (CollidesWithItem(ref colliders, ref collHeader, ref ball, ref state)) { return; } - ref var cols = ref state.Colliders; - switch (state.GetColliderType(colliderId)) { + switch (state.GetColliderType(ref colliders, colliderId)) { case ColliderType.Circle: - ref var circleCollider = ref cols.Circle(colliderId); + ref var circleCollider = ref colliders.Circle(colliderId); circleCollider.Collide(ref ball, in ball.CollisionEvent, ref state.Env.Random); break; case ColliderType.Plane: - ref var planeCollider = ref cols.Plane(colliderId); + ref var planeCollider = ref colliders.Plane(colliderId); planeCollider.Collide(ref ball, in ball.CollisionEvent, ref state.Env.Random); break; case ColliderType.Line: - ref var lineCollider = ref cols.Line(colliderId); + ref var lineCollider = ref colliders.Line(colliderId); lineCollider.Collide(ref ball, ref state.EventQueue, in ball.CollisionEvent, ref state.Env.Random); break; case ColliderType.LineZ: - ref var lineZCollider = ref cols.LineZ(colliderId); + ref var lineZCollider = ref colliders.LineZ(colliderId); lineZCollider.Collide(ref ball, ref state.EventQueue, in ball.CollisionEvent, ref state.Env.Random); break; case ColliderType.Triangle: - ref var triangleCollider = ref cols.Triangle(colliderId); + ref var triangleCollider = ref colliders.Triangle(colliderId); triangleCollider.Collide(ref ball, ref state.EventQueue, in ball.CollisionEvent, ref state.Env.Random); break; case ColliderType.Line3D: - ref var line3DCollider = ref cols.Line3D(colliderId); + ref var line3DCollider = ref colliders.Line3D(colliderId); line3DCollider.Collide(ref ball, ref state.EventQueue, in ball.CollisionEvent, ref state.Env.Random); break; case ColliderType.Point: - ref var pointCollider = ref cols.Point(colliderId); + ref var pointCollider = ref colliders.Point(colliderId); pointCollider.Collide(ref ball, ref state.EventQueue, in ball.CollisionEvent, ref state.Env.Random); break; case ColliderType.Bumper: - ref var bumperState = ref state.GetBumperState(colliderId); + ref var bumperState = ref state.GetBumperState(colliderId, ref colliders); BumperCollider.Collide(ref ball, ref state.EventQueue, ref ball.CollisionEvent, ref bumperState.RingAnimation, ref bumperState.SkirtAnimation, in collHeader, in bumperState.Static, ref state.Env.Random); break; case ColliderType.Flipper: ref var flipperState = ref state.GetFlipperState(colliderId); - ref var flipperCollider = ref cols.Flipper(colliderId); + ref var flipperCollider = ref colliders.Flipper(colliderId); flipperCollider.Collide(ref ball, ref ball.CollisionEvent, ref flipperState.Movement, ref state.EventQueue, in ball.Id, in flipperState.Tricks, in flipperState.Static, in flipperState.Velocity, in flipperState.Hit, state.Env.TimeMsec @@ -97,7 +104,7 @@ internal static void Collide(float hitTime, ref BallState ball, ref PhysicsState case ColliderType.LineSlingShot: ref var surfaceState = ref state.GetSurfaceState(colliderId); - ref var surfaceCollider = ref cols.LineSlingShot(colliderId); + ref var surfaceCollider = ref colliders.LineSlingShot(colliderId); surfaceCollider.Collide(ref ball, ref state.EventQueue, in surfaceState.Slingshot, in ball.CollisionEvent, ref state.Env.Random); break; @@ -128,16 +135,14 @@ internal static void Collide(float hitTime, ref BallState ball, ref PhysicsState ball.CollisionEvent.ClearCollider(); } - private static bool CollidesWithItem(ref ColliderHeader collHeader, ref BallState ball, ref PhysicsState state) + private static bool CollidesWithItem(ref NativeColliders colliders, ref ColliderHeader collHeader, ref BallState ball, ref PhysicsState state) { - ref var cols = ref state.Colliders; - // hit target var colliderId = ball.CollisionEvent.ColliderId; if (collHeader.ItemType == ItemType.HitTarget) { var normal = collHeader.Type == ColliderType.Triangle - ? cols.Triangle(colliderId).Normal() + ? colliders.Triangle(colliderId).Normal() : ball.CollisionEvent.HitNormal; if (state.HasDropTargetState(colliderId)) { diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsStaticNarrowPhase.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsStaticNarrowPhase.cs index 246dd1f8b..211b16a7d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsStaticNarrowPhase.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsStaticNarrowPhase.cs @@ -26,7 +26,7 @@ public static class PhysicsStaticNarrowPhase private static readonly ProfilerMarker PerfMarkerNarrowPhase = new("NarrowPhase"); internal static void FindNextCollision( - float hitTime, + ref NativeColliders colliders, ref BallState ball, ref NativeParallelHashSet overlappingColliders, ref NativeList contacts, @@ -35,35 +35,28 @@ ref PhysicsState state { PerfMarkerNarrowPhase.Begin(); - // init contacts and event - ball.CollisionEvent.ClearCollider(hitTime); // search upto current hit time - using (var enumerator = overlappingColliders.GetEnumerator()) { while (enumerator.MoveNext()) { var overlappingColliderId = enumerator.Current; - if (!state.IsColliderActive(overlappingColliderId)) { + if (!state.IsColliderActive(ref colliders, overlappingColliderId)) { continue; } var newCollEvent = new CollisionEventData(); - var newTime = state.HitTest(overlappingColliderId, ref ball, ref newCollEvent, ref contacts, ref state); - SaveCollisions(ref ball, ref newCollEvent, ref contacts, overlappingColliderId, newTime); + var newTime = state.HitTest(ref colliders, overlappingColliderId, ref ball, ref newCollEvent, ref contacts); + SaveCollisions(ref ball, ref newCollEvent, ref contacts, overlappingColliderId, newTime, colliders.KinematicColliders); } } - // no negative time allowed - if (ball.CollisionEvent.HitTime < 0) { - ball.CollisionEvent.ClearCollider(); - } PerfMarkerNarrowPhase.End(); } private static void SaveCollisions(ref BallState ball, ref CollisionEventData newCollEvent, - ref NativeList contacts, int colliderId, float newTime) + ref NativeList contacts, int colliderId, float newTime, bool isKinematic) { var validHit = newTime >= 0f && !Math.Sign(newTime) && newTime <= ball.CollisionEvent.HitTime; if (newCollEvent.IsContact || validHit) { // todo why newCollEvent.IsContact? it's not in vpx source - newCollEvent.SetCollider(colliderId); + newCollEvent.SetCollider(colliderId, isKinematic); newCollEvent.HitTime = newTime; if (newCollEvent.IsContact) { // remember all contacts? contacts.Add(new ContactBufferElement(ball.Id, newCollEvent)); diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsUpdateJob.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsUpdateJob.cs index f247a812c..105c10b91 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsUpdateJob.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsUpdateJob.cs @@ -17,6 +17,7 @@ using Unity.Burst; using Unity.Collections; using Unity.Jobs; +using Unity.Mathematics; using VisualPinball.Engine.Common; namespace VisualPinball.Unity @@ -34,6 +35,10 @@ internal struct PhysicsUpdateJob : IJob public NativeArray PhysicsEnv; public NativeOctree Octree; public NativeColliders Colliders; + public NativeColliders KinematicColliders; + public NativeColliders KinematicCollidersAtIdentity; + public NativeParallelHashMap UpdatedKinematicTransforms; + public NativeParallelHashMap KinematicColliderLookups; public InsideOfs InsideOfs; public NativeQueue.ParallelWriter Events; public AABB PlayfieldBounds; @@ -55,8 +60,9 @@ internal struct PhysicsUpdateJob : IJob public void Execute() { var env = PhysicsEnv[0]; - var state = new PhysicsState(ref env, ref Octree, ref Colliders, ref Events, ref InsideOfs, ref Balls, - ref BumperStates, ref DropTargetStates, ref FlipperStates, ref GateStates, + var state = new PhysicsState(ref env, ref Octree, ref Colliders, ref KinematicColliders, + ref KinematicCollidersAtIdentity, ref UpdatedKinematicTransforms, ref KinematicColliderLookups, ref Events, + ref InsideOfs, ref Balls, ref BumperStates, ref DropTargetStates, ref FlipperStates, ref GateStates, ref HitTargetStates, ref KickerStates, ref PlungerStates, ref SpinnerStates, ref SurfaceStates, ref TriggerStates, ref DisabledCollisionItems, ref SwapBallCollisionHandling); var cycle = new PhysicsCycle(Allocator.Temp); diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/CircleCollider.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/CircleCollider.cs index bd18a8a48..85e8743c9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/CircleCollider.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/CircleCollider.cs @@ -30,8 +30,8 @@ public int Id public ColliderHeader Header; - public readonly float2 Center; - public readonly float Radius; + public float2 Center; + public float Radius; private readonly float _zHigh; private readonly float _zLow; @@ -221,5 +221,12 @@ public void Collide(ref BallState ball, in CollisionEventData collEvent, ref Ran { BallCollider.Collide3DWall(ref ball, in Header.Material, in collEvent, in collEvent.HitNormal, ref random); } + + public void Transform(CircleCollider circle, float4x4 matrix) + { + var size = matrix.GetScale(); + Center = math.mul(matrix, new float4(circle.Center, 0f, 1f)).xy; + Radius = size.x / 2 * BumperComponent.DataMeshScale; + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/ColliderReference.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/ColliderReference.cs index 29e1bc729..5ca5b4aa2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/ColliderReference.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/ColliderReference.cs @@ -16,6 +16,7 @@ using System; using Unity.Collections; +using Unity.Mathematics; using VisualPinball.Unity.Collections; namespace VisualPinball.Unity @@ -35,9 +36,12 @@ public struct ColliderReference : IDisposable internal NativeList TriangleColliders; internal NativeList PlaneColliders; - public NativeList Lookup; + public NativeList Lookups; // collider id -> collider type + index within collider type list - public ColliderReference(Allocator allocator) + public readonly bool KinematicColliders; // if set, populate _itemIdToColliderIds + private NativeParallelHashMap> _itemIdToColliderIds; + + public ColliderReference(Allocator allocator, bool kinematicColliders = false) { CircleColliders = new NativeList(allocator); FlipperColliders = new NativeList(allocator); @@ -51,7 +55,10 @@ public ColliderReference(Allocator allocator) SpinnerColliders = new NativeList(allocator); TriangleColliders = new NativeList(allocator); PlaneColliders = new NativeList(allocator); - Lookup = new NativeList(allocator); + Lookups = new NativeList(allocator); + + KinematicColliders = kinematicColliders; + _itemIdToColliderIds = new NativeParallelHashMap>(0, allocator); } public void Dispose() @@ -68,19 +75,57 @@ public void Dispose() SpinnerColliders.Dispose(); TriangleColliders.Dispose(); PlaneColliders.Dispose(); + using (var enumerator = _itemIdToColliderIds.GetEnumerator()) { + while (enumerator.MoveNext()) { + enumerator.Current.Value.Dispose(); + } + } + _itemIdToColliderIds.Dispose(); } - public int Count => Lookup.Length; + public int Count => Lookups.Length; public ICollider this[int i] => LookupCollider(i); + public void TransformToIdentity(NativeParallelHashMap itemIdToTransformationMatrix) + { + using var enumerator = _itemIdToColliderIds.GetEnumerator(); + while (enumerator.MoveNext()) { + var itemId = enumerator.Current.Key; + ref var colliderIds = ref enumerator.Current.Value; + foreach (var colliderId in colliderIds) { + var matrix = itemIdToTransformationMatrix[itemId]; + var lookup = Lookups[colliderId]; + switch (lookup.Type) { + case ColliderType.Bumper: + case ColliderType.Circle: + ref var circleCollider = ref CircleColliders.GetElementAsRef(lookup.Index); + circleCollider.Transform(CircleColliders[lookup.Index], math.inverse(matrix)); + break; + case ColliderType.Point: + ref var pointCollider = ref PointColliders.GetElementAsRef(lookup.Index); + pointCollider.Transform(PointColliders[lookup.Index], math.inverse(matrix)); + break; + case ColliderType.Line3D: + ref var line3DCollider = ref Line3DColliders.GetElementAsRef(lookup.Index); + line3DCollider.Transform(Line3DColliders[lookup.Index], math.inverse(matrix)); + break; + case ColliderType.Triangle: + ref var triangleCollider = ref TriangleColliders.GetElementAsRef(lookup.Index); + triangleCollider.Transform(TriangleColliders[lookup.Index], math.inverse(matrix)); + break; + } + } + } + } + private ICollider LookupCollider(int i) { - if (i < 0 || i >= Lookup.Length) { + if (i < 0 || i >= Lookups.Length) { throw new IndexOutOfRangeException($"Invalid index {i} when looking up collider."); } - ref var lookup = ref Lookup.GetElementAsRef(i); + ref var lookup = ref Lookups.GetElementAsRef(i); switch (lookup.Type) { case ColliderType.Circle: return CircleColliders.GetElementAsRef(lookup.Index); case ColliderType.Flipper: return FlipperColliders.GetElementAsRef(lookup.Index); @@ -100,98 +145,122 @@ private ICollider LookupCollider(int i) #region Add + private void TrackReference(int itemId, int colliderId) + { + if (!KinematicColliders) { + return; + } + + if (!_itemIdToColliderIds.ContainsKey(itemId)) { + _itemIdToColliderIds[itemId] = new NativeList(Allocator.Temp); + } + _itemIdToColliderIds[itemId].Add(colliderId); + } + internal int Add(CircleCollider collider) { - collider.Id = Lookup.Length; - Lookup.Add(new ColliderLookup(ColliderType.Circle, CircleColliders.Length)); + collider.Id = Lookups.Length; + TrackReference(collider.Header.ItemId, collider.Header.Id); + Lookups.Add(new ColliderLookup(ColliderType.Circle, CircleColliders.Length)); CircleColliders.Add(collider); return collider.Id; } internal int Add(FlipperCollider collider) { - collider.Id = Lookup.Length; - Lookup.Add(new ColliderLookup(ColliderType.Flipper, FlipperColliders.Length)); + collider.Id = Lookups.Length; + TrackReference(collider.Header.ItemId, collider.Header.Id); + Lookups.Add(new ColliderLookup(ColliderType.Flipper, FlipperColliders.Length)); FlipperColliders.Add(collider); return collider.Id; } internal int Add(GateCollider collider) { - collider.Id = Lookup.Length; - Lookup.Add(new ColliderLookup(ColliderType.Gate, GateColliders.Length)); + collider.Id = Lookups.Length; + TrackReference(collider.Header.ItemId, collider.Header.Id); + Lookups.Add(new ColliderLookup(ColliderType.Gate, GateColliders.Length)); GateColliders.Add(collider); return collider.Id; } internal int Add(Line3DCollider collider) { - collider.Id = Lookup.Length; - Lookup.Add(new ColliderLookup(ColliderType.Line3D, Line3DColliders.Length)); + collider.Id = Lookups.Length; + TrackReference(collider.Header.ItemId, collider.Header.Id); + Lookups.Add(new ColliderLookup(ColliderType.Line3D, Line3DColliders.Length)); Line3DColliders.Add(collider); return collider.Id; } internal int Add(LineSlingshotCollider collider) { - collider.Id = Lookup.Length; - Lookup.Add(new ColliderLookup(ColliderType.LineSlingShot, LineSlingshotColliders.Length)); + collider.Id = Lookups.Length; + TrackReference(collider.Header.ItemId, collider.Header.Id); + Lookups.Add(new ColliderLookup(ColliderType.LineSlingShot, LineSlingshotColliders.Length)); LineSlingshotColliders.Add(collider); return collider.Id; } internal int Add(LineCollider collider) { - collider.Id = Lookup.Length; - Lookup.Add(new ColliderLookup(ColliderType.Line, LineColliders.Length)); + collider.Id = Lookups.Length; + TrackReference(collider.Header.ItemId, collider.Header.Id); + Lookups.Add(new ColliderLookup(ColliderType.Line, LineColliders.Length)); LineColliders.Add(collider); return collider.Id; } internal int Add(LineZCollider collider) { - collider.Id = Lookup.Length; - Lookup.Add(new ColliderLookup(ColliderType.LineZ, LineZColliders.Length)); + collider.Id = Lookups.Length; + TrackReference(collider.Header.ItemId, collider.Header.Id); + Lookups.Add(new ColliderLookup(ColliderType.LineZ, LineZColliders.Length)); LineZColliders.Add(collider); return collider.Id; } internal int Add(PlungerCollider collider) { - collider.Id = Lookup.Length; - Lookup.Add(new ColliderLookup(ColliderType.Plunger, PlungerColliders.Length)); + collider.Id = Lookups.Length; + TrackReference(collider.Header.ItemId, collider.Header.Id); + Lookups.Add(new ColliderLookup(ColliderType.Plunger, PlungerColliders.Length)); PlungerColliders.Add(collider); return collider.Id; } internal int Add(PointCollider collider) { - collider.Id = Lookup.Length; - Lookup.Add(new ColliderLookup(ColliderType.Point, PointColliders.Length)); + collider.Id = Lookups.Length; + TrackReference(collider.Header.ItemId, collider.Header.Id); + Lookups.Add(new ColliderLookup(ColliderType.Point, PointColliders.Length)); PointColliders.Add(collider); return collider.Id; } internal int Add(SpinnerCollider collider) { - collider.Id = Lookup.Length; - Lookup.Add(new ColliderLookup(ColliderType.Spinner, SpinnerColliders.Length)); + collider.Id = Lookups.Length; + TrackReference(collider.Header.ItemId, collider.Header.Id); + Lookups.Add(new ColliderLookup(ColliderType.Spinner, SpinnerColliders.Length)); SpinnerColliders.Add(collider); return collider.Id; } internal int Add(TriangleCollider collider) { - collider.Id = Lookup.Length; - Lookup.Add(new ColliderLookup(ColliderType.Triangle, TriangleColliders.Length)); + collider.Id = Lookups.Length; + TrackReference(collider.Header.ItemId, collider.Header.Id); + Lookups.Add(new ColliderLookup(ColliderType.Triangle, TriangleColliders.Length)); TriangleColliders.Add(collider); return collider.Id; } internal int Add(PlaneCollider collider) { - collider.Id = Lookup.Length; - Lookup.Add(new ColliderLookup(ColliderType.Plane, PlaneColliders.Length)); + collider.Id = Lookups.Length; + TrackReference(collider.Header.ItemId, collider.Header.Id); + Lookups.Add(new ColliderLookup(ColliderType.Plane, PlaneColliders.Length)); PlaneColliders.Add(collider); return collider.Id; } @@ -200,9 +269,9 @@ internal int Add(PlaneCollider collider) public ICollider[] ToArray() { - var array = new ICollider[Lookup.Length]; - for (var i = 0; i < Lookup.Length; i++) { - var lookup = Lookup[i]; + var array = new ICollider[Lookups.Length]; + for (var i = 0; i < Lookups.Length; i++) { + var lookup = Lookups[i]; switch (lookup.Type) { case ColliderType.Circle: array[i] = CircleColliders[lookup.Index]; @@ -244,5 +313,15 @@ public ICollider[] ToArray() } return array; } + + public NativeParallelHashMap CreateLookup(Allocator allocator) + { + var lookup = new NativeParallelHashMap(_itemIdToColliderIds.Count(), allocator); + using var enumerator = _itemIdToColliderIds.GetEnumerator(); + while (enumerator.MoveNext()) { + lookup.Add(enumerator.Current.Key, new NativeColliderIds(enumerator.Current.Value, allocator)); + } + return lookup; + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/Line3DCollider.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/Line3DCollider.cs index ebbf1ec4a..77745a7f6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/Line3DCollider.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/Line3DCollider.cs @@ -37,20 +37,25 @@ public int Id // these are all used when casting this to LineZCollider, // so the order is important too. - // ReSharper disable once NotAccessedField.Local - private readonly float2 _xy; - // ReSharper disable once NotAccessedField.Local - private readonly float _zLow; - // ReSharper disable once NotAccessedField.Local - private readonly float _zHigh; - private readonly float3x3 _matrix; + private float2 _xy; + private float _zLow; + private float _zHigh; + private float3x3 _matrix; + + // these are just so we can recompute the matrix when the collider is transformed. + private float3 _v1; + private float3 _v2; public ColliderBounds Bounds { get; private set; } public Line3DCollider(float3 v1, float3 v2, ColliderInfo info) : this() { Header.Init(info, ColliderType.Line3D); + SetByVectors(v1, v2); + } + private void SetByVectors(float3 v1, float3 v2) + { var vLine = math.normalize(v2 - v1); // Axis of rotation to make 3D cylinder a cylinder along the z-axis @@ -89,6 +94,9 @@ public Line3DCollider(float3 v1, float3 v2, ColliderInfo info) : this() math.min(v1.z, v2.z), math.max(v1.z, v2.z) )); + + _v1 = v1; + _v2 = v2; } #region Narrowphase @@ -132,5 +140,13 @@ public void Collide(ref BallState ball, ref NativeQueue.ParallelWrite } public override string ToString() => $"Line3DCollider[{Header.ItemId}] ({_xy.x}/{_xy.y} | {_zLow} -> {_zHigh})"; + + public void Transform(Line3DCollider line3D, float4x4 matrix) + { + SetByVectors( + math.mul(matrix, new float4(line3D._v1, 1f)).xyz, + math.mul(matrix, new float4(line3D._v2, 1f)).xyz + ); + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/PointCollider.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/PointCollider.cs index 94f6b3956..2110c4be7 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/PointCollider.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/PointCollider.cs @@ -30,7 +30,7 @@ public int Id public ColliderHeader Header; - public readonly float3 P; + public float3 P; public ColliderBounds Bounds => new ColliderBounds(Header.ItemId, Header.Id, new Aabb( P.x, @@ -139,5 +139,10 @@ public void Collide(ref BallState ball, ref NativeQueue.ParallelWrit #endregion public override string ToString() => $"PointCollider[{Header.ItemId}] ({P.x}/{P.y}/{P.z})"; + + public void Transform(PointCollider point, float4x4 matrix) + { + P = math.mul(matrix, new float4(point.P, 1f)).xyz; + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/TriangleCollider.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/TriangleCollider.cs index 0959028ea..7a9d2ecbf 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/TriangleCollider.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collider/TriangleCollider.cs @@ -31,10 +31,10 @@ public int Id public ColliderHeader Header; - public readonly float3 Rgv0; - public readonly float3 Rgv1; - public readonly float3 Rgv2; - private readonly float3 _normal; + public float3 Rgv0; + public float3 Rgv1; + public float3 Rgv2; + private float3 _normal; public float3 Normal() => _normal; @@ -182,5 +182,13 @@ public void Collide(ref BallState ball, ref NativeQueue.ParallelWrite } #endregion + + public void Transform(TriangleCollider triangle, float4x4 matrix) + { + Rgv0 = math.mul(matrix, new float4(triangle.Rgv0, 1f)).xyz; + Rgv1 = math.mul(matrix, new float4(triangle.Rgv1, 1f)).xyz; + Rgv2 = math.mul(matrix, new float4(triangle.Rgv2, 1f)).xyz; + _normal = math.normalizesafe(math.cross(Rgv2 - Rgv0, Rgv1 - Rgv0)); + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/CollisionEventData.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/CollisionEventData.cs index 7e9ad88da..eb332dda6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/CollisionEventData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/CollisionEventData.cs @@ -29,12 +29,14 @@ internal struct CollisionEventData public bool IsContact; public int ColliderId; + public bool IsKinematic; public int BallId; - public void SetCollider(int colliderId) + public void SetCollider(int colliderId, bool isKinematic) { // it's either collider id (for static colliders) or ball id (for dynamic colliders) ColliderId = colliderId; + IsKinematic = isKinematic; BallId = 0; } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/ContactPhysics.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/ContactPhysics.cs index f1523ccac..d43c0d46d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/ContactPhysics.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/ContactPhysics.cs @@ -22,9 +22,13 @@ internal static void Update(ref ContactBufferElement contact, ref BallState ball { ref var collEvent = ref contact.CollEvent; if (collEvent.ColliderId > -1) { // collide with static collider - var collHeader = state.GetColliderHeader(collEvent.ColliderId); + var collHeader = collEvent.IsKinematic + ? ref state.GetColliderHeader(ref state.KinematicColliders, collEvent.ColliderId) + : ref state.GetColliderHeader(ref state.Colliders, collEvent.ColliderId); if (collHeader.Type == ColliderType.Flipper) { - ref var flipperCollider = ref state.Colliders.Flipper(collEvent.ColliderId); + var flipperCollider = collEvent.IsKinematic + ? ref state.KinematicColliders.Flipper(collEvent.ColliderId) + : ref state.Colliders.Flipper(collEvent.ColliderId); ref var flipperState = ref state.GetFlipperState(collEvent.ColliderId); flipperCollider.Contact(ref ball, ref flipperState.Movement, in collEvent, in flipperState.Static, in flipperState.Velocity, hitTime, in state.Env.Gravity); @@ -32,7 +36,9 @@ internal static void Update(ref ContactBufferElement contact, ref BallState ball Collider.Contact(in collHeader, ref ball, in collEvent, hitTime, in state.Env.Gravity); } } else if (collEvent.BallId != 0) { // collide with ball - ref var collHeader = ref state.GetColliderHeader(contact.CollEvent.ColliderId); + var collHeader = collEvent.IsKinematic + ? ref state.GetColliderHeader(ref state.KinematicColliders, contact.CollEvent.ColliderId) + : ref state.GetColliderHeader(ref state.Colliders, contact.CollEvent.ColliderId); BallCollider.HandleStaticContact(ref ball, in collEvent, collHeader.Material.Friction, hitTime, state.Env.Gravity); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeColliderIds.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeColliderIds.cs new file mode 100644 index 000000000..fbc8c9570 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeColliderIds.cs @@ -0,0 +1,39 @@ +using System; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; + +namespace VisualPinball.Unity +{ + public unsafe struct NativeColliderIds : IDisposable + { + public readonly int Length; + private void* _buffer; + + private readonly Allocator _allocator; + + public NativeColliderIds(NativeList colliderIds, Allocator allocator) + { + _allocator = allocator; + Length = colliderIds.Length; + long size = UnsafeUtility.SizeOf() * Length; + _buffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(_buffer, colliderIds.GetUnsafePtr(), size); + } + + public int this[int index] + { + get { + if (index < 0 || index >= Length) { + throw new IndexOutOfRangeException(); + } + return UnsafeUtility.ReadArrayElement(_buffer, index); + } + } + + public void Dispose() + { + UnsafeUtility.Free(_buffer, _allocator); + _buffer = null; + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeColliderIds.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeColliderIds.cs.meta new file mode 100644 index 000000000..b56bb2ba9 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeColliderIds.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1b636e0d45f0474c961c74de3cfd8b11 +timeCreated: 1699020556 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeCollider.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeColliders.cs similarity index 96% rename from VisualPinball.Unity/VisualPinball.Unity/Physics/NativeCollider.cs rename to VisualPinball.Unity/VisualPinball.Unity/Physics/NativeColliders.cs index 73e628c51..c221c73ae 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeCollider.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeColliders.cs @@ -1,581 +1,584 @@ -// Visual Pinball Engine -// Copyright (C) 2023 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// ReSharper disable InconsistentNaming - -using System.Diagnostics; -using System; -using Unity.Burst; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Collections; -using Unity.Profiling; -using VisualPinball.Engine.VPT; - -namespace VisualPinball.Unity -{ - [NativeContainer] - [NativeContainerSupportsMinMaxWriteRestriction] - [DebuggerDisplay("Length = {Length}")] - [DebuggerTypeProxy(typeof(NativeCollidersDebugView))] - public unsafe struct NativeColliders : IDisposable - { - #region Members - - private static readonly ProfilerMarker PerfMarker = new("NativeColliders Allocation"); - - public int Length => m_Length; - - /// - /// An array that links the collider IDs (the key) to the position in the respective collider buffer. - /// - [NativeDisableUnsafePtrRestriction] private void* m_LookupBuffer; - [NativeDisableUnsafePtrRestriction] private void* m_CircleColliderBuffer; - [NativeDisableUnsafePtrRestriction] private void* m_FlipperColliderBuffer; - [NativeDisableUnsafePtrRestriction] private void* m_GateColliderBuffer; - [NativeDisableUnsafePtrRestriction] private void* m_Line3DColliderBuffer; - [NativeDisableUnsafePtrRestriction] private void* m_LineSlingshotColliderBuffer; - [NativeDisableUnsafePtrRestriction] private void* m_LineColliderBuffer; - [NativeDisableUnsafePtrRestriction] private void* m_LineZColliderBuffer; - [NativeDisableUnsafePtrRestriction] private void* m_PlungerColliderBuffer; - [NativeDisableUnsafePtrRestriction] private void* m_PointColliderBuffer; - [NativeDisableUnsafePtrRestriction] private void* m_SpinnerColliderBuffer; - [NativeDisableUnsafePtrRestriction] private void* m_TriangleColliderBuffer; - [NativeDisableUnsafePtrRestriction] private void* m_PlaneColliderBuffer; - - private readonly Allocator m_AllocatorLabel; - - private int m_Length; // must be here, and called like that. - -#if ENABLE_UNITY_COLLECTIONS_CHECKS - internal int m_MinIndex; - internal int m_MaxIndex; - internal AtomicSafetyHandle m_Safety; - internal static readonly SharedStatic s_staticSafetyId = SharedStatic.GetOrCreate(); -#endif - - #endregion - - #region Constructor / Allocation - - public NativeColliders(ref ColliderReference colRef, Allocator allocator) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - // Native allocation is only valid for Temp, Job and Persistent - if (allocator <= Allocator.None) { - throw new ArgumentException("Allocator must be Temp, TempJob or Persistent", nameof(allocator)); - } -#endif - PerfMarker.Begin(); - - m_Length = colRef.Lookup.Length; - - long size = UnsafeUtility.SizeOf() * colRef.Lookup.Length; - m_LookupBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_LookupBuffer, colRef.Lookup.GetUnsafePtr(), size); - - size = UnsafeUtility.SizeOf() * colRef.FlipperColliders.Length; - m_FlipperColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_FlipperColliderBuffer, colRef.FlipperColliders.GetUnsafePtr(), size); - - size = UnsafeUtility.SizeOf() * colRef.CircleColliders.Length; - m_CircleColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_CircleColliderBuffer, colRef.CircleColliders.GetUnsafePtr(), size); - - size = UnsafeUtility.SizeOf() * colRef.GateColliders.Length; - m_GateColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_GateColliderBuffer, colRef.GateColliders.GetUnsafePtr(), size); - - size = UnsafeUtility.SizeOf() * colRef.Line3DColliders.Length; - m_Line3DColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_Line3DColliderBuffer, colRef.Line3DColliders.GetUnsafePtr(), size); - - size = UnsafeUtility.SizeOf() * colRef.LineSlingshotColliders.Length; - m_LineSlingshotColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_LineSlingshotColliderBuffer, colRef.LineSlingshotColliders.GetUnsafePtr(), size); - - size = UnsafeUtility.SizeOf() * colRef.LineColliders.Length; - m_LineColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_LineColliderBuffer, colRef.LineColliders.GetUnsafePtr(), size); - - size = UnsafeUtility.SizeOf() * colRef.LineZColliders.Length; - m_LineZColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_LineZColliderBuffer, colRef.LineZColliders.GetUnsafePtr(), size); - - size = UnsafeUtility.SizeOf() * colRef.PlungerColliders.Length; - m_PlungerColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_PlungerColliderBuffer, colRef.PlungerColliders.GetUnsafePtr(), size); - - size = UnsafeUtility.SizeOf() * colRef.PointColliders.Length; - m_PointColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_PointColliderBuffer, colRef.PointColliders.GetUnsafePtr(), size); - - size = UnsafeUtility.SizeOf() * colRef.SpinnerColliders.Length; - m_SpinnerColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_SpinnerColliderBuffer, colRef.SpinnerColliders.GetUnsafePtr(), size); - - size = UnsafeUtility.SizeOf() * colRef.TriangleColliders.Length; - m_TriangleColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_TriangleColliderBuffer, colRef.TriangleColliders.GetUnsafePtr(), size); - - size = UnsafeUtility.SizeOf() * colRef.PlaneColliders.Length; - m_PlaneColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(m_PlaneColliderBuffer, colRef.PlaneColliders.GetUnsafePtr(), size); - - m_AllocatorLabel = allocator; - - PerfMarker.End(); - -#if ENABLE_UNITY_COLLECTIONS_CHECKS - m_MinIndex = 0; - m_MaxIndex = m_Length - 1; - m_Safety = CollectionHelper.CreateSafetyHandle(allocator); - CollectionHelper.SetStaticSafetyId(ref m_Safety, ref s_staticSafetyId.Data); -#endif - } - - #endregion - - #region Collider Access - - internal ref CircleCollider Circle(int colliderId) - { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (lookup.Type != ColliderType.Circle) { - throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up circle collider."); - } -#endif - return ref UnsafeUtility.ArrayElementAsRef(m_CircleColliderBuffer, lookup.Index); - } - - internal ref FlipperCollider Flipper(int colliderId) - { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (lookup.Type != ColliderType.Flipper) { - throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up flipper collider."); - } -#endif - return ref UnsafeUtility.ArrayElementAsRef(m_FlipperColliderBuffer, lookup.Index); - } - - internal ref GateCollider Gate(int colliderId) - { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (lookup.Type != ColliderType.Gate) { - throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up gate collider."); - } -#endif - return ref UnsafeUtility.ArrayElementAsRef(m_GateColliderBuffer, lookup.Index); - } - - internal ref Line3DCollider Line3D(int colliderId) - { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (lookup.Type != ColliderType.Line3D) { - throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up line3d collider."); - } -#endif - return ref UnsafeUtility.ArrayElementAsRef(m_Line3DColliderBuffer, lookup.Index); - } - - internal ref LineCollider Line(int colliderId) - { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (lookup.Type != ColliderType.Line) { - throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up line collider."); - } -#endif - return ref UnsafeUtility.ArrayElementAsRef(m_LineColliderBuffer, lookup.Index); - } - - internal ref LineSlingshotCollider LineSlingShot(int colliderId) - { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (lookup.Type != ColliderType.LineSlingShot) { - throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up line slingshot collider."); - } -#endif - return ref UnsafeUtility.ArrayElementAsRef(m_LineSlingshotColliderBuffer, lookup.Index); - } - - internal ref LineZCollider LineZ(int colliderId) - { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (lookup.Type != ColliderType.LineZ) { - throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up line-z collider."); - } -#endif - return ref UnsafeUtility.ArrayElementAsRef(m_LineZColliderBuffer, lookup.Index); - } - - internal ref PlaneCollider Plane(int colliderId) - { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (lookup.Type != ColliderType.Plane) { - throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up plane collider."); - } -#endif - return ref UnsafeUtility.ArrayElementAsRef(m_PlaneColliderBuffer, lookup.Index); - } - - internal ref PlungerCollider Plunger(int colliderId) - { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (lookup.Type != ColliderType.Plunger) { - throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up plunger collider."); - } -#endif - return ref UnsafeUtility.ArrayElementAsRef(m_PlungerColliderBuffer, lookup.Index); - } - - internal ref PointCollider Point(int colliderId) - { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (lookup.Type != ColliderType.Point) { - throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up point collider."); - } -#endif - return ref UnsafeUtility.ArrayElementAsRef(m_PointColliderBuffer, lookup.Index); - } - - internal ref SpinnerCollider Spinner(int colliderId) - { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (lookup.Type != ColliderType.Spinner) { - throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up spinner collider."); - } -#endif - return ref UnsafeUtility.ArrayElementAsRef(m_SpinnerColliderBuffer, lookup.Index); - } - - internal ref TriangleCollider Triangle(int colliderId) - { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (lookup.Type != ColliderType.Triangle) { - throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up triangle collider."); - } -#endif - return ref UnsafeUtility.ArrayElementAsRef(m_TriangleColliderBuffer, lookup.Index); - } - - #endregion - - #region Collection Access - - public ICollider this[int index] - { - get - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - // If the container is currently not allowed to read from the buffer - // then this will throw an exception. - // This handles all cases, from already disposed containers - // to safe multithreaded access. - AtomicSafetyHandle.CheckReadAndThrow(m_Safety); - - // Perform out of range checks based on - // the NativeContainerSupportsMinMaxWriteRestriction policy - if (index < m_MinIndex || index > m_MaxIndex) - FailOutOfRangeError(index); -#endif - if (index < 0 || index >= m_Length) { - throw new IndexOutOfRangeException($"Invalid index {index} when looking up collider."); - } - - var lookup = UnsafeUtility.ReadArrayElement(m_LookupBuffer, index); - switch (lookup.Type) { - case ColliderType.Circle: return UnsafeUtility.ReadArrayElement(m_CircleColliderBuffer, lookup.Index); - case ColliderType.Flipper: return UnsafeUtility.ReadArrayElement(m_FlipperColliderBuffer, lookup.Index); - case ColliderType.Gate: return UnsafeUtility.ReadArrayElement(m_GateColliderBuffer, lookup.Index); - case ColliderType.Line3D: return UnsafeUtility.ReadArrayElement(m_Line3DColliderBuffer, lookup.Index); - case ColliderType.LineSlingShot: return UnsafeUtility.ReadArrayElement(m_LineSlingshotColliderBuffer, lookup.Index); - case ColliderType.Line: return UnsafeUtility.ReadArrayElement(m_LineColliderBuffer, lookup.Index); - case ColliderType.LineZ: return UnsafeUtility.ReadArrayElement(m_LineZColliderBuffer, lookup.Index); - case ColliderType.Plunger: return UnsafeUtility.ReadArrayElement(m_PlungerColliderBuffer, lookup.Index); - case ColliderType.Point: return UnsafeUtility.ReadArrayElement(m_PointColliderBuffer, lookup.Index); - case ColliderType.Spinner: return UnsafeUtility.ReadArrayElement(m_SpinnerColliderBuffer, lookup.Index); - case ColliderType.Triangle: return UnsafeUtility.ReadArrayElement(m_TriangleColliderBuffer, lookup.Index); - case ColliderType.Plane: return UnsafeUtility.ReadArrayElement(m_PlaneColliderBuffer, lookup.Index); - } - throw new ArgumentException($"Unknown lookup type."); - } - - set - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - // If the container is currently not allowed to write to the buffer - // then this will throw an exception. - // This handles all cases, from already disposed containers - // to safe multithreaded access. - AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); - - // Perform out of range checks based on - // the NativeContainerSupportsMinMaxWriteRestriction policy - if (index < m_MinIndex || index > m_MaxIndex) - FailOutOfRangeError(index); -#endif - - if (index < 0 || index >= m_Length) { - throw new IndexOutOfRangeException($"Invalid index {index} when looking up collider."); - } - - var lookup = UnsafeUtility.ReadArrayElement(m_LookupBuffer, index); - switch (lookup.Type) { - case ColliderType.Circle: - UnsafeUtility.WriteArrayElement(m_CircleColliderBuffer, lookup.Index, (CircleCollider)value); - break; - case ColliderType.Flipper: - UnsafeUtility.WriteArrayElement(m_FlipperColliderBuffer, lookup.Index, (FlipperCollider)value); - break; - case ColliderType.Gate: - UnsafeUtility.WriteArrayElement(m_GateColliderBuffer, lookup.Index, (GateCollider)value); - break; - case ColliderType.Line: - UnsafeUtility.WriteArrayElement(m_LineColliderBuffer, lookup.Index, (LineCollider)value); - break; - case ColliderType.Line3D: - UnsafeUtility.WriteArrayElement(m_Line3DColliderBuffer, lookup.Index, (Line3DCollider)value); - break; - case ColliderType.LineSlingShot: - UnsafeUtility.WriteArrayElement(m_LineSlingshotColliderBuffer, lookup.Index, (LineSlingshotCollider)value); - break; - case ColliderType.LineZ: - UnsafeUtility.WriteArrayElement(m_LineZColliderBuffer, lookup.Index, (LineZCollider)value); - break; - case ColliderType.Plane: - UnsafeUtility.WriteArrayElement(m_PlaneColliderBuffer, lookup.Index, (PlaneCollider)value); - break; - case ColliderType.Plunger: - UnsafeUtility.WriteArrayElement(m_PlungerColliderBuffer, lookup.Index, (PlungerCollider)value); - break; - case ColliderType.Point: - UnsafeUtility.WriteArrayElement(m_PointColliderBuffer, lookup.Index, (PointCollider)value); - break; - case ColliderType.Spinner: - UnsafeUtility.WriteArrayElement(m_SpinnerColliderBuffer, lookup.Index, (SpinnerCollider)value); - break; - case ColliderType.Triangle: - UnsafeUtility.WriteArrayElement(m_TriangleColliderBuffer, lookup.Index, (TriangleCollider)value); - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - } - - #endregion - - #region Disposition - - public void Dispose() - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - CollectionHelper.DisposeSafetyHandle(ref m_Safety); -#endif - - UnsafeUtility.Free(m_LookupBuffer, m_AllocatorLabel); - UnsafeUtility.Free(m_CircleColliderBuffer, m_AllocatorLabel); - UnsafeUtility.Free(m_FlipperColliderBuffer, m_AllocatorLabel); - UnsafeUtility.Free(m_GateColliderBuffer, m_AllocatorLabel); - UnsafeUtility.Free(m_Line3DColliderBuffer, m_AllocatorLabel); - UnsafeUtility.Free(m_LineSlingshotColliderBuffer, m_AllocatorLabel); - UnsafeUtility.Free(m_LineColliderBuffer, m_AllocatorLabel); - UnsafeUtility.Free(m_LineZColliderBuffer, m_AllocatorLabel); - UnsafeUtility.Free(m_PlungerColliderBuffer, m_AllocatorLabel); - UnsafeUtility.Free(m_PointColliderBuffer, m_AllocatorLabel); - UnsafeUtility.Free(m_SpinnerColliderBuffer, m_AllocatorLabel); - UnsafeUtility.Free(m_TriangleColliderBuffer, m_AllocatorLabel); - UnsafeUtility.Free(m_PlaneColliderBuffer, m_AllocatorLabel); - - m_LookupBuffer = null; - m_CircleColliderBuffer = null; - m_FlipperColliderBuffer = null; - m_GateColliderBuffer = null; - m_Line3DColliderBuffer = null; - m_LineSlingshotColliderBuffer = null; - m_LineColliderBuffer = null; - m_LineZColliderBuffer = null; - m_PlungerColliderBuffer = null; - m_PointColliderBuffer = null; - m_SpinnerColliderBuffer = null; - m_TriangleColliderBuffer = null; - m_PlaneColliderBuffer = null; - m_Length = 0; - } - -#if ENABLE_UNITY_COLLECTIONS_CHECKS - private void FailOutOfRangeError(int index) - { - if (index < Length && (m_MinIndex != 0 || m_MaxIndex != Length - 1)) - throw new IndexOutOfRangeException(string.Format( - "Index {0} is out of restricted IJobParallelFor range [{1}...{2}] in ReadWriteBuffer.\n" + - "ReadWriteBuffers are restricted to only read & write the element at the job index. " + - "You can use double buffering strategies to avoid race conditions due to " + - "reading & writing in parallel to the same elements from a job.", - index, m_MinIndex, m_MaxIndex)); - - throw new IndexOutOfRangeException($"Index {index} is out of range of '{Length}' Length."); - } -#endif - - #endregion - - #region Collider Data - - public Aabb GetAabb(int index) - { - if (index < 0 || index >= m_Length) { - throw new IndexOutOfRangeException($"Invalid index {index} when looking up collider."); - } - var lookup = UnsafeUtility.ReadArrayElement(m_LookupBuffer, index); - switch (lookup.Type) { - case ColliderType.Circle: return UnsafeUtility.ArrayElementAsRef(m_CircleColliderBuffer, lookup.Index).Bounds.Aabb; - case ColliderType.Flipper: return UnsafeUtility.ArrayElementAsRef(m_FlipperColliderBuffer, lookup.Index).Bounds.Aabb; - case ColliderType.Gate: return UnsafeUtility.ArrayElementAsRef(m_GateColliderBuffer, lookup.Index).Bounds.Aabb; - case ColliderType.Line3D: return UnsafeUtility.ArrayElementAsRef(m_Line3DColliderBuffer, lookup.Index).Bounds.Aabb; - case ColliderType.LineSlingShot: return UnsafeUtility.ArrayElementAsRef(m_LineSlingshotColliderBuffer, lookup.Index).Bounds.Aabb; - case ColliderType.Line: return UnsafeUtility.ArrayElementAsRef(m_LineColliderBuffer, lookup.Index).Bounds.Aabb; - case ColliderType.LineZ: return UnsafeUtility.ArrayElementAsRef(m_LineZColliderBuffer, lookup.Index).Bounds.Aabb; - case ColliderType.Plunger: return UnsafeUtility.ArrayElementAsRef(m_PlungerColliderBuffer, lookup.Index).Bounds.Aabb; - case ColliderType.Point: return UnsafeUtility.ArrayElementAsRef(m_PointColliderBuffer, lookup.Index).Bounds.Aabb; - case ColliderType.Spinner: return UnsafeUtility.ArrayElementAsRef(m_SpinnerColliderBuffer, lookup.Index).Bounds.Aabb; - case ColliderType.Triangle: return UnsafeUtility.ArrayElementAsRef(m_TriangleColliderBuffer, lookup.Index).Bounds.Aabb; - case ColliderType.Plane: return UnsafeUtility.ArrayElementAsRef(m_PlaneColliderBuffer, lookup.Index).Bounds.Aabb; - } - throw new ArgumentException($"Unknown lookup type."); - } - - public int GetItemId(int index) => GetHeader(index).ItemId; - - public ItemType GetItemType(int index) => GetHeader(index).ItemType; - - public ref ColliderHeader GetHeader(int index) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (index < 0 || index >= m_Length) { - throw new IndexOutOfRangeException($"Invalid index {index} when looking up collider."); - } -#endif - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, index); - switch (lookup.Type) { - case ColliderType.Circle: return ref UnsafeUtility.ArrayElementAsRef(m_CircleColliderBuffer, lookup.Index).Header; - case ColliderType.Flipper: return ref UnsafeUtility.ArrayElementAsRef(m_FlipperColliderBuffer, lookup.Index).Header; - case ColliderType.Gate: return ref UnsafeUtility.ArrayElementAsRef(m_GateColliderBuffer, lookup.Index).Header; - case ColliderType.Line3D: return ref UnsafeUtility.ArrayElementAsRef(m_Line3DColliderBuffer, lookup.Index).Header; - case ColliderType.LineSlingShot: return ref UnsafeUtility.ArrayElementAsRef(m_LineSlingshotColliderBuffer, lookup.Index).Header; - case ColliderType.Line: return ref UnsafeUtility.ArrayElementAsRef(m_LineColliderBuffer, lookup.Index).Header; - case ColliderType.LineZ: return ref UnsafeUtility.ArrayElementAsRef(m_LineZColliderBuffer, lookup.Index).Header; - case ColliderType.Plunger: return ref UnsafeUtility.ArrayElementAsRef(m_PlungerColliderBuffer, lookup.Index).Header; - case ColliderType.Point: return ref UnsafeUtility.ArrayElementAsRef(m_PointColliderBuffer, lookup.Index).Header; - case ColliderType.Spinner: return ref UnsafeUtility.ArrayElementAsRef(m_SpinnerColliderBuffer, lookup.Index).Header; - case ColliderType.Triangle: return ref UnsafeUtility.ArrayElementAsRef(m_TriangleColliderBuffer, lookup.Index).Header; - case ColliderType.Plane: return ref UnsafeUtility.ArrayElementAsRef(m_PlaneColliderBuffer, lookup.Index).Header; - } - throw new ArgumentException($"Unknown lookup type."); - } - - #endregion - - #region Debug - - public ICollider[] ToArray() - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - AtomicSafetyHandle.CheckReadAndThrow(m_Safety); -#endif - var array = new ICollider[Length]; - for (var i = 0; i < Length; i++) { - ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, i); - switch (lookup.Type) { - case ColliderType.Circle: - array[i] = UnsafeUtility.ReadArrayElement(m_CircleColliderBuffer, lookup.Index); - break; - case ColliderType.Flipper: - array[i] = UnsafeUtility.ReadArrayElement(m_FlipperColliderBuffer, lookup.Index); - break; - case ColliderType.Gate: - array[i] = UnsafeUtility.ReadArrayElement(m_GateColliderBuffer, lookup.Index); - break; - case ColliderType.Line3D: - array[i] = UnsafeUtility.ReadArrayElement(m_Line3DColliderBuffer, lookup.Index); - break; - case ColliderType.LineSlingShot: - array[i] = UnsafeUtility.ReadArrayElement(m_LineSlingshotColliderBuffer, lookup.Index); - break; - case ColliderType.Line: - array[i] = UnsafeUtility.ReadArrayElement(m_LineColliderBuffer, lookup.Index); - break; - case ColliderType.LineZ: - array[i] = UnsafeUtility.ReadArrayElement(m_LineZColliderBuffer, lookup.Index); - break; - case ColliderType.Plunger: - array[i] = UnsafeUtility.ReadArrayElement(m_PlungerColliderBuffer, lookup.Index); - break; - case ColliderType.Point: - array[i] = UnsafeUtility.ReadArrayElement(m_PointColliderBuffer, lookup.Index); - break; - case ColliderType.Spinner: - array[i] = UnsafeUtility.ReadArrayElement(m_SpinnerColliderBuffer, lookup.Index); - break; - case ColliderType.Triangle: - array[i] = UnsafeUtility.ReadArrayElement(m_TriangleColliderBuffer, lookup.Index); - break; - case ColliderType.Plane: - array[i] = UnsafeUtility.ReadArrayElement(m_PlaneColliderBuffer, lookup.Index); - break; - } - } - return array; - } - - #endregion - } - - public readonly struct ColliderLookup - { - public readonly ColliderType Type; - public readonly int Index; - - public ColliderLookup(ColliderType type, int index) - { - Type = type; - Index = index; - } - } - - // Visualizes the colliders in the C# debugger - internal sealed class NativeCollidersDebugView - { - private NativeColliders _nativeColliders; - - public NativeCollidersDebugView(NativeColliders nativeColliders) - { - _nativeColliders = nativeColliders; - } - public ICollider[] Colliders => _nativeColliders.ToArray(); - } -} +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// ReSharper disable InconsistentNaming + +using System.Diagnostics; +using System; +using Unity.Burst; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Collections; +using Unity.Profiling; +using VisualPinball.Engine.VPT; + +namespace VisualPinball.Unity +{ + [NativeContainer] + [NativeContainerSupportsMinMaxWriteRestriction] + [DebuggerDisplay("Length = {Length}")] + [DebuggerTypeProxy(typeof(NativeCollidersDebugView))] + public unsafe struct NativeColliders : IDisposable + { + #region Members + + private static readonly ProfilerMarker PerfMarker = new("NativeColliders Allocation"); + + public int Length => m_Length; + public bool KinematicColliders => m_KinematicColliders; + + /// + /// An array that links the collider IDs (the key) to the position in the respective collider buffer. + /// + [NativeDisableUnsafePtrRestriction] private void* m_LookupBuffer; + [NativeDisableUnsafePtrRestriction] private void* m_CircleColliderBuffer; + [NativeDisableUnsafePtrRestriction] private void* m_FlipperColliderBuffer; + [NativeDisableUnsafePtrRestriction] private void* m_GateColliderBuffer; + [NativeDisableUnsafePtrRestriction] private void* m_Line3DColliderBuffer; + [NativeDisableUnsafePtrRestriction] private void* m_LineSlingshotColliderBuffer; + [NativeDisableUnsafePtrRestriction] private void* m_LineColliderBuffer; + [NativeDisableUnsafePtrRestriction] private void* m_LineZColliderBuffer; + [NativeDisableUnsafePtrRestriction] private void* m_PlungerColliderBuffer; + [NativeDisableUnsafePtrRestriction] private void* m_PointColliderBuffer; + [NativeDisableUnsafePtrRestriction] private void* m_SpinnerColliderBuffer; + [NativeDisableUnsafePtrRestriction] private void* m_TriangleColliderBuffer; + [NativeDisableUnsafePtrRestriction] private void* m_PlaneColliderBuffer; + + private readonly Allocator m_AllocatorLabel; + private readonly bool m_KinematicColliders; + + private int m_Length; // must be here, and called like that. + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + internal int m_MinIndex; + internal int m_MaxIndex; + internal AtomicSafetyHandle m_Safety; + internal static readonly SharedStatic s_staticSafetyId = SharedStatic.GetOrCreate(); +#endif + + #endregion + + #region Constructor / Allocation + + public NativeColliders(ref ColliderReference colRef, Allocator allocator) + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + // Native allocation is only valid for Temp, Job and Persistent + if (allocator <= Allocator.None) { + throw new ArgumentException("Allocator must be Temp, TempJob or Persistent", nameof(allocator)); + } +#endif + PerfMarker.Begin(); + + m_Length = colRef.Lookups.Length; + m_KinematicColliders = colRef.KinematicColliders; + + long size = UnsafeUtility.SizeOf() * colRef.Lookups.Length; + m_LookupBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_LookupBuffer, colRef.Lookups.GetUnsafePtr(), size); + + size = UnsafeUtility.SizeOf() * colRef.FlipperColliders.Length; + m_FlipperColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_FlipperColliderBuffer, colRef.FlipperColliders.GetUnsafePtr(), size); + + size = UnsafeUtility.SizeOf() * colRef.CircleColliders.Length; + m_CircleColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_CircleColliderBuffer, colRef.CircleColliders.GetUnsafePtr(), size); + + size = UnsafeUtility.SizeOf() * colRef.GateColliders.Length; + m_GateColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_GateColliderBuffer, colRef.GateColliders.GetUnsafePtr(), size); + + size = UnsafeUtility.SizeOf() * colRef.Line3DColliders.Length; + m_Line3DColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_Line3DColliderBuffer, colRef.Line3DColliders.GetUnsafePtr(), size); + + size = UnsafeUtility.SizeOf() * colRef.LineSlingshotColliders.Length; + m_LineSlingshotColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_LineSlingshotColliderBuffer, colRef.LineSlingshotColliders.GetUnsafePtr(), size); + + size = UnsafeUtility.SizeOf() * colRef.LineColliders.Length; + m_LineColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_LineColliderBuffer, colRef.LineColliders.GetUnsafePtr(), size); + + size = UnsafeUtility.SizeOf() * colRef.LineZColliders.Length; + m_LineZColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_LineZColliderBuffer, colRef.LineZColliders.GetUnsafePtr(), size); + + size = UnsafeUtility.SizeOf() * colRef.PlungerColliders.Length; + m_PlungerColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_PlungerColliderBuffer, colRef.PlungerColliders.GetUnsafePtr(), size); + + size = UnsafeUtility.SizeOf() * colRef.PointColliders.Length; + m_PointColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_PointColliderBuffer, colRef.PointColliders.GetUnsafePtr(), size); + + size = UnsafeUtility.SizeOf() * colRef.SpinnerColliders.Length; + m_SpinnerColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_SpinnerColliderBuffer, colRef.SpinnerColliders.GetUnsafePtr(), size); + + size = UnsafeUtility.SizeOf() * colRef.TriangleColliders.Length; + m_TriangleColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_TriangleColliderBuffer, colRef.TriangleColliders.GetUnsafePtr(), size); + + size = UnsafeUtility.SizeOf() * colRef.PlaneColliders.Length; + m_PlaneColliderBuffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(m_PlaneColliderBuffer, colRef.PlaneColliders.GetUnsafePtr(), size); + + m_AllocatorLabel = allocator; + + PerfMarker.End(); + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + m_MinIndex = 0; + m_MaxIndex = m_Length - 1; + m_Safety = CollectionHelper.CreateSafetyHandle(allocator); + CollectionHelper.SetStaticSafetyId(ref m_Safety, ref s_staticSafetyId.Data); +#endif + } + + #endregion + + #region Collider Access + + internal ref CircleCollider Circle(int colliderId) + { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lookup.Type != ColliderType.Circle) { + throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up circle collider."); + } +#endif + return ref UnsafeUtility.ArrayElementAsRef(m_CircleColliderBuffer, lookup.Index); + } + + internal ref FlipperCollider Flipper(int colliderId) + { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lookup.Type != ColliderType.Flipper) { + throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up flipper collider."); + } +#endif + return ref UnsafeUtility.ArrayElementAsRef(m_FlipperColliderBuffer, lookup.Index); + } + + internal ref GateCollider Gate(int colliderId) + { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lookup.Type != ColliderType.Gate) { + throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up gate collider."); + } +#endif + return ref UnsafeUtility.ArrayElementAsRef(m_GateColliderBuffer, lookup.Index); + } + + internal ref Line3DCollider Line3D(int colliderId) + { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lookup.Type != ColliderType.Line3D) { + throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up line3d collider."); + } +#endif + return ref UnsafeUtility.ArrayElementAsRef(m_Line3DColliderBuffer, lookup.Index); + } + + internal ref LineCollider Line(int colliderId) + { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lookup.Type != ColliderType.Line) { + throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up line collider."); + } +#endif + return ref UnsafeUtility.ArrayElementAsRef(m_LineColliderBuffer, lookup.Index); + } + + internal ref LineSlingshotCollider LineSlingShot(int colliderId) + { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lookup.Type != ColliderType.LineSlingShot) { + throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up line slingshot collider."); + } +#endif + return ref UnsafeUtility.ArrayElementAsRef(m_LineSlingshotColliderBuffer, lookup.Index); + } + + internal ref LineZCollider LineZ(int colliderId) + { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lookup.Type != ColliderType.LineZ) { + throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up line-z collider."); + } +#endif + return ref UnsafeUtility.ArrayElementAsRef(m_LineZColliderBuffer, lookup.Index); + } + + internal ref PlaneCollider Plane(int colliderId) + { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lookup.Type != ColliderType.Plane) { + throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up plane collider."); + } +#endif + return ref UnsafeUtility.ArrayElementAsRef(m_PlaneColliderBuffer, lookup.Index); + } + + internal ref PlungerCollider Plunger(int colliderId) + { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lookup.Type != ColliderType.Plunger) { + throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up plunger collider."); + } +#endif + return ref UnsafeUtility.ArrayElementAsRef(m_PlungerColliderBuffer, lookup.Index); + } + + internal ref PointCollider Point(int colliderId) + { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lookup.Type != ColliderType.Point) { + throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up point collider."); + } +#endif + return ref UnsafeUtility.ArrayElementAsRef(m_PointColliderBuffer, lookup.Index); + } + + internal ref SpinnerCollider Spinner(int colliderId) + { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lookup.Type != ColliderType.Spinner) { + throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up spinner collider."); + } +#endif + return ref UnsafeUtility.ArrayElementAsRef(m_SpinnerColliderBuffer, lookup.Index); + } + + internal ref TriangleCollider Triangle(int colliderId) + { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, colliderId); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (lookup.Type != ColliderType.Triangle) { + throw new ArgumentException($"Invalid collider type {lookup.Type} when looking up triangle collider."); + } +#endif + return ref UnsafeUtility.ArrayElementAsRef(m_TriangleColliderBuffer, lookup.Index); + } + + #endregion + + #region Collection Access + + public ICollider this[int index] + { + get + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + // If the container is currently not allowed to read from the buffer + // then this will throw an exception. + // This handles all cases, from already disposed containers + // to safe multithreaded access. + AtomicSafetyHandle.CheckReadAndThrow(m_Safety); + + // Perform out of range checks based on + // the NativeContainerSupportsMinMaxWriteRestriction policy + if (index < m_MinIndex || index > m_MaxIndex) + FailOutOfRangeError(index); +#endif + if (index < 0 || index >= m_Length) { + throw new IndexOutOfRangeException($"Invalid index {index} when looking up collider."); + } + + var lookup = UnsafeUtility.ReadArrayElement(m_LookupBuffer, index); + switch (lookup.Type) { + case ColliderType.Circle: return UnsafeUtility.ReadArrayElement(m_CircleColliderBuffer, lookup.Index); + case ColliderType.Flipper: return UnsafeUtility.ReadArrayElement(m_FlipperColliderBuffer, lookup.Index); + case ColliderType.Gate: return UnsafeUtility.ReadArrayElement(m_GateColliderBuffer, lookup.Index); + case ColliderType.Line3D: return UnsafeUtility.ReadArrayElement(m_Line3DColliderBuffer, lookup.Index); + case ColliderType.LineSlingShot: return UnsafeUtility.ReadArrayElement(m_LineSlingshotColliderBuffer, lookup.Index); + case ColliderType.Line: return UnsafeUtility.ReadArrayElement(m_LineColliderBuffer, lookup.Index); + case ColliderType.LineZ: return UnsafeUtility.ReadArrayElement(m_LineZColliderBuffer, lookup.Index); + case ColliderType.Plunger: return UnsafeUtility.ReadArrayElement(m_PlungerColliderBuffer, lookup.Index); + case ColliderType.Point: return UnsafeUtility.ReadArrayElement(m_PointColliderBuffer, lookup.Index); + case ColliderType.Spinner: return UnsafeUtility.ReadArrayElement(m_SpinnerColliderBuffer, lookup.Index); + case ColliderType.Triangle: return UnsafeUtility.ReadArrayElement(m_TriangleColliderBuffer, lookup.Index); + case ColliderType.Plane: return UnsafeUtility.ReadArrayElement(m_PlaneColliderBuffer, lookup.Index); + } + throw new ArgumentException($"Unknown lookup type."); + } + + set + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + // If the container is currently not allowed to write to the buffer + // then this will throw an exception. + // This handles all cases, from already disposed containers + // to safe multithreaded access. + AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); + + // Perform out of range checks based on + // the NativeContainerSupportsMinMaxWriteRestriction policy + if (index < m_MinIndex || index > m_MaxIndex) + FailOutOfRangeError(index); +#endif + + if (index < 0 || index >= m_Length) { + throw new IndexOutOfRangeException($"Invalid index {index} when looking up collider."); + } + + var lookup = UnsafeUtility.ReadArrayElement(m_LookupBuffer, index); + switch (lookup.Type) { + case ColliderType.Circle: + UnsafeUtility.WriteArrayElement(m_CircleColliderBuffer, lookup.Index, (CircleCollider)value); + break; + case ColliderType.Flipper: + UnsafeUtility.WriteArrayElement(m_FlipperColliderBuffer, lookup.Index, (FlipperCollider)value); + break; + case ColliderType.Gate: + UnsafeUtility.WriteArrayElement(m_GateColliderBuffer, lookup.Index, (GateCollider)value); + break; + case ColliderType.Line: + UnsafeUtility.WriteArrayElement(m_LineColliderBuffer, lookup.Index, (LineCollider)value); + break; + case ColliderType.Line3D: + UnsafeUtility.WriteArrayElement(m_Line3DColliderBuffer, lookup.Index, (Line3DCollider)value); + break; + case ColliderType.LineSlingShot: + UnsafeUtility.WriteArrayElement(m_LineSlingshotColliderBuffer, lookup.Index, (LineSlingshotCollider)value); + break; + case ColliderType.LineZ: + UnsafeUtility.WriteArrayElement(m_LineZColliderBuffer, lookup.Index, (LineZCollider)value); + break; + case ColliderType.Plane: + UnsafeUtility.WriteArrayElement(m_PlaneColliderBuffer, lookup.Index, (PlaneCollider)value); + break; + case ColliderType.Plunger: + UnsafeUtility.WriteArrayElement(m_PlungerColliderBuffer, lookup.Index, (PlungerCollider)value); + break; + case ColliderType.Point: + UnsafeUtility.WriteArrayElement(m_PointColliderBuffer, lookup.Index, (PointCollider)value); + break; + case ColliderType.Spinner: + UnsafeUtility.WriteArrayElement(m_SpinnerColliderBuffer, lookup.Index, (SpinnerCollider)value); + break; + case ColliderType.Triangle: + UnsafeUtility.WriteArrayElement(m_TriangleColliderBuffer, lookup.Index, (TriangleCollider)value); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + #endregion + + #region Disposition + + public void Dispose() + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + CollectionHelper.DisposeSafetyHandle(ref m_Safety); +#endif + + UnsafeUtility.Free(m_LookupBuffer, m_AllocatorLabel); + UnsafeUtility.Free(m_CircleColliderBuffer, m_AllocatorLabel); + UnsafeUtility.Free(m_FlipperColliderBuffer, m_AllocatorLabel); + UnsafeUtility.Free(m_GateColliderBuffer, m_AllocatorLabel); + UnsafeUtility.Free(m_Line3DColliderBuffer, m_AllocatorLabel); + UnsafeUtility.Free(m_LineSlingshotColliderBuffer, m_AllocatorLabel); + UnsafeUtility.Free(m_LineColliderBuffer, m_AllocatorLabel); + UnsafeUtility.Free(m_LineZColliderBuffer, m_AllocatorLabel); + UnsafeUtility.Free(m_PlungerColliderBuffer, m_AllocatorLabel); + UnsafeUtility.Free(m_PointColliderBuffer, m_AllocatorLabel); + UnsafeUtility.Free(m_SpinnerColliderBuffer, m_AllocatorLabel); + UnsafeUtility.Free(m_TriangleColliderBuffer, m_AllocatorLabel); + UnsafeUtility.Free(m_PlaneColliderBuffer, m_AllocatorLabel); + + m_LookupBuffer = null; + m_CircleColliderBuffer = null; + m_FlipperColliderBuffer = null; + m_GateColliderBuffer = null; + m_Line3DColliderBuffer = null; + m_LineSlingshotColliderBuffer = null; + m_LineColliderBuffer = null; + m_LineZColliderBuffer = null; + m_PlungerColliderBuffer = null; + m_PointColliderBuffer = null; + m_SpinnerColliderBuffer = null; + m_TriangleColliderBuffer = null; + m_PlaneColliderBuffer = null; + m_Length = 0; + } + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + private void FailOutOfRangeError(int index) + { + if (index < Length && (m_MinIndex != 0 || m_MaxIndex != Length - 1)) + throw new IndexOutOfRangeException(string.Format( + "Index {0} is out of restricted IJobParallelFor range [{1}...{2}] in ReadWriteBuffer.\n" + + "ReadWriteBuffers are restricted to only read & write the element at the job index. " + + "You can use double buffering strategies to avoid race conditions due to " + + "reading & writing in parallel to the same elements from a job.", + index, m_MinIndex, m_MaxIndex)); + + throw new IndexOutOfRangeException($"Index {index} is out of range of '{Length}' Length."); + } +#endif + + #endregion + + #region Collider Data + + public Aabb GetAabb(int index) + { + if (index < 0 || index >= m_Length) { + throw new IndexOutOfRangeException($"Invalid index {index} when looking up collider."); + } + var lookup = UnsafeUtility.ReadArrayElement(m_LookupBuffer, index); + switch (lookup.Type) { + case ColliderType.Circle: return UnsafeUtility.ArrayElementAsRef(m_CircleColliderBuffer, lookup.Index).Bounds.Aabb; + case ColliderType.Flipper: return UnsafeUtility.ArrayElementAsRef(m_FlipperColliderBuffer, lookup.Index).Bounds.Aabb; + case ColliderType.Gate: return UnsafeUtility.ArrayElementAsRef(m_GateColliderBuffer, lookup.Index).Bounds.Aabb; + case ColliderType.Line3D: return UnsafeUtility.ArrayElementAsRef(m_Line3DColliderBuffer, lookup.Index).Bounds.Aabb; + case ColliderType.LineSlingShot: return UnsafeUtility.ArrayElementAsRef(m_LineSlingshotColliderBuffer, lookup.Index).Bounds.Aabb; + case ColliderType.Line: return UnsafeUtility.ArrayElementAsRef(m_LineColliderBuffer, lookup.Index).Bounds.Aabb; + case ColliderType.LineZ: return UnsafeUtility.ArrayElementAsRef(m_LineZColliderBuffer, lookup.Index).Bounds.Aabb; + case ColliderType.Plunger: return UnsafeUtility.ArrayElementAsRef(m_PlungerColliderBuffer, lookup.Index).Bounds.Aabb; + case ColliderType.Point: return UnsafeUtility.ArrayElementAsRef(m_PointColliderBuffer, lookup.Index).Bounds.Aabb; + case ColliderType.Spinner: return UnsafeUtility.ArrayElementAsRef(m_SpinnerColliderBuffer, lookup.Index).Bounds.Aabb; + case ColliderType.Triangle: return UnsafeUtility.ArrayElementAsRef(m_TriangleColliderBuffer, lookup.Index).Bounds.Aabb; + case ColliderType.Plane: return UnsafeUtility.ArrayElementAsRef(m_PlaneColliderBuffer, lookup.Index).Bounds.Aabb; + } + throw new ArgumentException($"Unknown lookup type."); + } + + public int GetItemId(int index) => GetHeader(index).ItemId; + + public ItemType GetItemType(int index) => GetHeader(index).ItemType; + + public ref ColliderHeader GetHeader(int index) + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (index < 0 || index >= m_Length) { + throw new IndexOutOfRangeException($"Invalid index {index} when looking up collider."); + } +#endif + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, index); + switch (lookup.Type) { + case ColliderType.Circle: return ref UnsafeUtility.ArrayElementAsRef(m_CircleColliderBuffer, lookup.Index).Header; + case ColliderType.Flipper: return ref UnsafeUtility.ArrayElementAsRef(m_FlipperColliderBuffer, lookup.Index).Header; + case ColliderType.Gate: return ref UnsafeUtility.ArrayElementAsRef(m_GateColliderBuffer, lookup.Index).Header; + case ColliderType.Line3D: return ref UnsafeUtility.ArrayElementAsRef(m_Line3DColliderBuffer, lookup.Index).Header; + case ColliderType.LineSlingShot: return ref UnsafeUtility.ArrayElementAsRef(m_LineSlingshotColliderBuffer, lookup.Index).Header; + case ColliderType.Line: return ref UnsafeUtility.ArrayElementAsRef(m_LineColliderBuffer, lookup.Index).Header; + case ColliderType.LineZ: return ref UnsafeUtility.ArrayElementAsRef(m_LineZColliderBuffer, lookup.Index).Header; + case ColliderType.Plunger: return ref UnsafeUtility.ArrayElementAsRef(m_PlungerColliderBuffer, lookup.Index).Header; + case ColliderType.Point: return ref UnsafeUtility.ArrayElementAsRef(m_PointColliderBuffer, lookup.Index).Header; + case ColliderType.Spinner: return ref UnsafeUtility.ArrayElementAsRef(m_SpinnerColliderBuffer, lookup.Index).Header; + case ColliderType.Triangle: return ref UnsafeUtility.ArrayElementAsRef(m_TriangleColliderBuffer, lookup.Index).Header; + case ColliderType.Plane: return ref UnsafeUtility.ArrayElementAsRef(m_PlaneColliderBuffer, lookup.Index).Header; + } + throw new ArgumentException($"Unknown lookup type."); + } + + #endregion + + #region Debug + + public ICollider[] ToArray() + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + AtomicSafetyHandle.CheckReadAndThrow(m_Safety); +#endif + var array = new ICollider[Length]; + for (var i = 0; i < Length; i++) { + ref var lookup = ref UnsafeUtility.ArrayElementAsRef(m_LookupBuffer, i); + switch (lookup.Type) { + case ColliderType.Circle: + array[i] = UnsafeUtility.ReadArrayElement(m_CircleColliderBuffer, lookup.Index); + break; + case ColliderType.Flipper: + array[i] = UnsafeUtility.ReadArrayElement(m_FlipperColliderBuffer, lookup.Index); + break; + case ColliderType.Gate: + array[i] = UnsafeUtility.ReadArrayElement(m_GateColliderBuffer, lookup.Index); + break; + case ColliderType.Line3D: + array[i] = UnsafeUtility.ReadArrayElement(m_Line3DColliderBuffer, lookup.Index); + break; + case ColliderType.LineSlingShot: + array[i] = UnsafeUtility.ReadArrayElement(m_LineSlingshotColliderBuffer, lookup.Index); + break; + case ColliderType.Line: + array[i] = UnsafeUtility.ReadArrayElement(m_LineColliderBuffer, lookup.Index); + break; + case ColliderType.LineZ: + array[i] = UnsafeUtility.ReadArrayElement(m_LineZColliderBuffer, lookup.Index); + break; + case ColliderType.Plunger: + array[i] = UnsafeUtility.ReadArrayElement(m_PlungerColliderBuffer, lookup.Index); + break; + case ColliderType.Point: + array[i] = UnsafeUtility.ReadArrayElement(m_PointColliderBuffer, lookup.Index); + break; + case ColliderType.Spinner: + array[i] = UnsafeUtility.ReadArrayElement(m_SpinnerColliderBuffer, lookup.Index); + break; + case ColliderType.Triangle: + array[i] = UnsafeUtility.ReadArrayElement(m_TriangleColliderBuffer, lookup.Index); + break; + case ColliderType.Plane: + array[i] = UnsafeUtility.ReadArrayElement(m_PlaneColliderBuffer, lookup.Index); + break; + } + } + return array; + } + + #endregion + } + + public readonly struct ColliderLookup + { + public readonly ColliderType Type; + public readonly int Index; + + public ColliderLookup(ColliderType type, int index) + { + Type = type; + Index = index; + } + } + + // Visualizes the colliders in the C# debugger + internal sealed class NativeCollidersDebugView + { + private NativeColliders _nativeColliders; + + public NativeCollidersDebugView(NativeColliders nativeColliders) + { + _nativeColliders = nativeColliders; + } + public ICollider[] Colliders => _nativeColliders.ToArray(); + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeCollider.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeColliders.cs.meta similarity index 97% rename from VisualPinball.Unity/VisualPinball.Unity/Physics/NativeCollider.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity/Physics/NativeColliders.cs.meta index e941cb236..ef37beb17 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeCollider.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/NativeColliders.cs.meta @@ -1,3 +1,3 @@ -fileFormatVersion: 2 -guid: 264d4febde7b4f19955044bba6cbd6d6 +fileFormatVersion: 2 +guid: 264d4febde7b4f19955044bba6cbd6d6 timeCreated: 1698263847 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs index 1a3dc9149..9b2bca9c6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs @@ -15,7 +15,6 @@ // along with this program. If not, see . using System; -using System.Collections.Generic; using UnityEngine; using VisualPinball.Engine.VPT.Bumper; @@ -72,11 +71,17 @@ void IApiCoil.OnCoil(bool enabled) protected override bool FireHitEvents => ColliderComponent.HitEvent; protected override float HitThreshold => ColliderComponent.Threshold; - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { var height = MainComponent.PositionZ; - colliders.Add(new CircleCollider(MainComponent.Position, MainComponent.Radius, height, - height + MainComponent.HeightScale, GetColliderInfo(), ColliderType.Bumper)); + if (ColliderComponent.IsKinematic) { + kinematicColliders.Add(new CircleCollider(MainComponent.Position, MainComponent.Radius, height, + height + MainComponent.HeightScale, GetColliderInfo(), ColliderType.Bumper)); + } else { + colliders.Add(new CircleCollider(MainComponent.Position, MainComponent.Radius, height, + height + MainComponent.HeightScale, GetColliderInfo(), ColliderType.Bumper)); + } } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperCollider.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperCollider.cs index 25847bf6e..3f81e2774 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperCollider.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperCollider.cs @@ -26,9 +26,6 @@ public static void Collide(ref BallState ball, ref NativeQueue.Parall ref CollisionEventData collEvent, ref BumperRingAnimationState ringState, ref BumperSkirtAnimationState skirtState, in ColliderHeader collHeader, in BumperStaticState state, ref Random random) { - // todo - // if (!m_enabled) return; - var dot = math.dot(collEvent.HitNormal, ball.Velocity); // needs to be computed before Collide3DWall()! var material = collHeader.Material; BallCollider.Collide3DWall(ref ball, in material, in collEvent, in collEvent.HitNormal, ref random); // reflect ball from wall diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperColliderComponent.cs index a3f19df37..34b60b586 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperColliderComponent.cs @@ -16,13 +16,14 @@ // ReSharper disable InconsistentNaming +using Unity.Mathematics; using UnityEngine; using VisualPinball.Engine.VPT.Bumper; namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Collision/Bumper Collider")] - public class BumperColliderComponent : ColliderComponent + public class BumperColliderComponent : ColliderComponent, IKinematicColliderComponent { #region Data @@ -41,6 +42,17 @@ public class BumperColliderComponent : ColliderComponent _isKinematic; + public int ItemId => MainComponent.gameObject.GetInstanceID(); + public float4x4 TransformationMatrix => MainComponent.TransformationMatrix; + #endregion protected override IApiColliderGenerator InstantiateColliderApi(Player player, PhysicsEngine physicsEngine) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs index c71ece42a..ccc1de0d2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs @@ -77,7 +77,8 @@ public class BumperComponent : MainRenderableComponent, private const string BaseMeshName = "bumper.base"; private const string CapMeshName = "bumper.cap"; private const string RingMeshName = "bumper.ring"; - private const float DataMeshScale = 100f; + + public const float DataMeshScale = 100f; public const string SocketSwitchItem = "socket_switch"; @@ -146,6 +147,15 @@ public override void UpdateTransforms() t.localEulerAngles = new Vector3(0, Orientation, 0); } + public float4x4 TransformationMatrix { + get { + var scaleMatrix = float4x4.Scale(new float3(Radius * 2f, Radius * 2f, HeightScale) / DataMeshScale); + var transMatrix = float4x4.Translate(new float3(Position.x, Position.y, PositionZ)); + var rotMatrix = float4x4.RotateZ(math.radians(Orientation)); + return math.mul(transMatrix, math.mul(rotMatrix, scaleMatrix)); + } + } + #endregion #region Conversion @@ -348,7 +358,6 @@ internal BumperState CreateState() } : default; return new BumperState( - collComponent ? gameObject.GetInstanceID() : 0, skirtAnimComponent ? skirtAnimComponent.gameObject.GetInstanceID() : 0, ringAnimComponent ? ringAnimComponent.gameObject.GetInstanceID() : 0, staticData, diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperState.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperState.cs index 1e2de8b9f..0013b549c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperState.cs @@ -18,17 +18,15 @@ namespace VisualPinball.Unity { internal struct BumperState { - internal readonly int ItemId; internal readonly int SkirtItemId; internal int RingItemId; internal BumperStaticState Static; internal BumperRingAnimationState RingAnimation; internal BumperSkirtAnimationState SkirtAnimation; - public BumperState(int itemId, int skirtItemId, int ringItemId, BumperStaticState @static, + public BumperState(int skirtItemId, int ringItemId, BumperStaticState @static, BumperRingAnimationState ringAnimation, BumperSkirtAnimationState skirtAnimation) { - ItemId = itemId; SkirtItemId = skirtItemId; RingItemId = ringItemId; Static = @static; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/CollidableApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/CollidableApi.cs index 6f6ec848c..fcbfa695c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/CollidableApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/CollidableApi.cs @@ -41,14 +41,14 @@ protected CollidableApi(GameObject go, Player player, PhysicsEngine physicsEngin protected virtual bool FireHitEvents => false; protected virtual float HitThreshold => 0; - protected abstract void CreateColliders(ref ColliderReference colliders, float margin); + protected abstract void CreateColliders(ref ColliderReference colliders, ref ColliderReference kinematicColliders, float margin); - void IApiColliderGenerator.CreateColliders(ref ColliderReference colliders, float margin) + void IApiColliderGenerator.CreateColliders(ref ColliderReference colliders, ref ColliderReference kinematicColliders, float margin) { if (!ColliderComponent) { return; } - CreateColliders(ref colliders, margin); + CreateColliders(ref colliders, ref kinematicColliders, margin); } ColliderInfo IApiColliderGenerator.GetColliderInfo() => GetColliderInfo(MainComponent.ItemType); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs index e03a448f0..7b2f3f0fb 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs @@ -26,6 +26,7 @@ using UnityEditor; using UnityEngine; using UnityEngine.Profiling; +using UnityEngine.UIElements; using VisualPinball.Engine.VPT; using VisualPinball.Engine.VPT.Flipper; using Mesh = UnityEngine.Mesh; @@ -70,6 +71,8 @@ public abstract class ColliderComponent : SubComponent this is IKinematicColliderComponent { IsKinematic: true }; + private void OnDrawGizmos() { Profiler.BeginSample("ItemColliderComponent.OnDrawGizmosSelected"); @@ -136,28 +142,48 @@ private void OnDrawGizmos() var generateColliders = ShowAabbs || showColliders && !HasCachedColliders; if (generateColliders) { - var api = InstantiateColliderApi(player, null); - var colliders = new ColliderReference(Allocator.TempJob); - api.CreateColliders(ref colliders, 0.1f); - if (showColliders) { - _colliderMesh = GenerateColliderMesh(ref colliders); - _collidersDirty = false; - } + if (Application.isPlaying && IsKinematic) { + + if (!_physicsEngine) { + _physicsEngine = GetComponentInParent(); + } + + var colliders = _physicsEngine.GetKinematicColliders(MainComponent.gameObject.GetInstanceID()); + if (showColliders) { + _colliderMesh = GenerateColliderMesh(colliders); + _collidersDirty = false; + } + + } else { + var api = InstantiateColliderApi(player, null); + var colliders = new ColliderReference(Allocator.TempJob); + var kinematicColliders = new ColliderReference(Allocator.TempJob, true); + api.CreateColliders(ref colliders, ref kinematicColliders, 0.1f); + + if (showColliders) { + _colliderMesh = IsKinematic + ? GenerateColliderMesh(ref kinematicColliders) + : GenerateColliderMesh(ref colliders); + _collidersDirty = false; + } - if (ShowAabbs) { - for (var i = 0; i < colliders.Count; i++) { - var col = colliders[i]; - DrawAabb(col.Bounds.Aabb, i == SelectedCollider); + if (ShowAabbs) { + for (var i = 0; i < colliders.Count; i++) { + var col = colliders[i]; + DrawAabb(col.Bounds.Aabb, i == SelectedCollider); + } } + colliders.Dispose(); + } - colliders.Dispose(); } if (ShowColliderOctree) { var api = InstantiateColliderApi(player, null); var colliders = new ColliderReference(Allocator.TempJob); - api.CreateColliders(ref colliders, 0.1f); + var kinematicColliders = new ColliderReference(Allocator.TempJob, true); + api.CreateColliders(ref colliders, ref kinematicColliders, 0.1f); var playfieldBounds = GetComponentInChildren().Bounds; var octree = new NativeOctree(playfieldBounds, 32, 10, Allocator.Persistent); @@ -174,7 +200,9 @@ private void OnDrawGizmos() if (showColliders) { - var color = Color.green; + var color = Application.isPlaying && IsKinematic + ? Color.magenta + : IsKinematic ? new Color(0, 1, 1) : Color.green; Handles.color = color; color.a = 0.3f; Gizmos.color = color; @@ -244,6 +272,70 @@ private Mesh GenerateColliderMesh(ref ColliderReference colliders) }; } + private Mesh GenerateColliderMesh(IEnumerable colliders) + { + var color = Color.magenta; + Handles.color = color; + color.a = 0.3f; + Gizmos.color = color; + var vertices = new List(); + var normals = new List(); + var indices = new List(); + _nonMeshColliders.Clear(); + + foreach (var coll in colliders) { + + if (coll is CircleCollider circleCollider) { + AddCollider(circleCollider, vertices, normals, indices); + } + + if (coll is FlipperCollider) { + AddFlipperCollider(vertices, normals, indices); + } + + if (coll is GateCollider gateCollider) { + AddCollider(gateCollider.LineSeg0, vertices, normals, indices); + AddCollider(gateCollider.LineSeg1, vertices, normals, indices); + } + + if (coll is LineCollider lineCollider) { + AddCollider(lineCollider, vertices, normals, indices); + } + + if (coll is LineSlingshotCollider lineSlingshotCollider) { + AddCollider(lineSlingshotCollider, vertices, normals, indices); + } + + if (coll is LineZCollider lineZCollider) { + _nonMeshColliders.Add(lineZCollider); + } + + if (coll is PlungerCollider plungerCollider) { + AddCollider(plungerCollider.LineSegBase, vertices, normals, indices); + AddCollider(plungerCollider.JointBase0, vertices, normals, indices); + AddCollider(plungerCollider.JointBase1, vertices, normals, indices); + } + + if (coll is SpinnerCollider spinnerCollider) { + AddCollider(spinnerCollider.LineSeg0, vertices, normals, indices); + AddCollider(spinnerCollider.LineSeg1, vertices, normals, indices); + } + + if (coll is TriangleCollider triangleCollider) { + AddCollider(triangleCollider, vertices, normals, indices); + } + } + + // todo Line3DCollider + + return new Mesh { + name = $"{name} (debug collider)", + vertices = vertices.ToArray(), + triangles = indices.ToArray(), + normals = normals.ToArray() + }; + } + private void DrawNonMeshColliders() { foreach (var col in _nonMeshColliders) { @@ -434,10 +526,11 @@ private static void DrawAabb(Aabb aabb, bool isSelected) #endregion - void ICollidableComponent.GetColliders(Player player, ref ColliderReference colliders, float margin) - { - InstantiateColliderApi(player, null).CreateColliders(ref colliders, margin); - } + void ICollidableComponent.GetColliders(Player player, ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) + => InstantiateColliderApi(player, null) + .CreateColliders(ref colliders, ref kinematicColliders, margin); + int ICollidableComponent.ItemId => MainComponent.gameObject.GetInstanceID(); bool ICollidableComponent.IsCollidable => isActiveAndEnabled; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs index 27c95e65a..25cae1a33 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs @@ -221,7 +221,8 @@ void IApiCollidable.OnCollide(int ballId, float hit) #region Collider Generation - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { var height = MainComponent.PositionZ; var baseRadius = math.max(MainComponent.BaseRadius, 0.01f); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index 83bfc2756..3ee02ad77 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -506,7 +506,6 @@ internal FlipperState CreateState() // vpx physics var d = GetMaterialData(colliderComponent); var state = new FlipperState( - gameObject.GetInstanceID(), d, GetMovementData(d), GetVelocityData(d), diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperState.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperState.cs index eab2654d0..adc9e6ae3 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperState.cs @@ -18,7 +18,6 @@ namespace VisualPinball.Unity { internal struct FlipperState { - internal readonly int ItemId; internal FlipperStaticData Static; internal FlipperMovementState Movement; internal FlipperVelocityData Velocity; @@ -26,11 +25,10 @@ internal struct FlipperState internal FlipperTricksData Tricks; internal SolenoidState Solenoid; - public FlipperState(int itemId, FlipperStaticData @static, FlipperMovementState movement, + public FlipperState(FlipperStaticData @static, FlipperMovementState movement, FlipperVelocityData velocity, FlipperHitData hit, FlipperTricksData tricks, SolenoidState solenoid) { - ItemId = itemId; Static = @static; Movement = movement; Velocity = velocity; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateApi.cs index a4f28e686..e5957e884 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateApi.cs @@ -97,7 +97,8 @@ public void Lift(float speed, float angleDeg) #region Collider Generation - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { var colliderGenerator = new GateColliderGenerator(this, MainComponent, ColliderComponent); colliderGenerator.GenerateColliders(MainComponent.PositionZ, ref colliders); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateComponent.cs index 53d9e21b7..5cb9ec8f6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateComponent.cs @@ -312,7 +312,6 @@ internal GateState CreateState() } : default; return new GateState( - collComponent ? gameObject.GetInstanceID() : 0, wireComponent ? wireComponent.gameObject.GetInstanceID() : 0, staticData, movementData diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateState.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateState.cs index 4e017f528..7242111e7 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateState.cs @@ -18,14 +18,12 @@ namespace VisualPinball.Unity { internal struct GateState { - internal readonly int ItemId; internal readonly int WireItemId; internal GateStaticState Static; internal GateMovementState Movement; - public GateState(int itemId, int wireItemId, GateStaticState @static, GateMovementState movement) + public GateState(int wireItemId, GateStaticState @static, GateMovementState movement) { - ItemId = itemId; WireItemId = wireItemId; Static = @static; Movement = movement; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs index 66bc87543..1764216ce 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs @@ -102,7 +102,8 @@ private void SetIsDropped(bool isDropped) protected override bool FireHitEvents => true; protected override float HitThreshold => ColliderComponent.Threshold; - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { var colliderGenerator = new DropTargetColliderGenerator(this, MainComponent, MainComponent); colliderGenerator.GenerateColliders(MainComponent.PlayfieldHeight, ref colliders); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetComponent.cs index 03ac170e3..39d88c32e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetComponent.cs @@ -168,7 +168,6 @@ internal DropTargetState CreateState() } : default; return new DropTargetState( - colliderComponent && animationComponent ? gameObject.GetInstanceID() : 0, animationComponent ? animationComponent.gameObject.GetInstanceID() : 0, staticData, animationData diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetState.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetState.cs index b5434468e..96cd8a424 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetState.cs @@ -18,14 +18,12 @@ namespace VisualPinball.Unity { internal struct DropTargetState { - internal readonly int ItemId; internal readonly int AnimatedItemId; internal DropTargetStaticState Static; internal DropTargetAnimationState Animation; - public DropTargetState(int itemId, int animatedItemId, DropTargetStaticState @static, DropTargetAnimationState animation) + public DropTargetState(int animatedItemId, DropTargetStaticState @static, DropTargetAnimationState animation) { - ItemId = itemId; AnimatedItemId = animatedItemId; Static = @static; Animation = animation; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetApi.cs index ae3818139..65b8c2b21 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetApi.cs @@ -59,7 +59,8 @@ internal HitTargetApi(GameObject go, Player player, PhysicsEngine physicsEngine) protected override bool FireHitEvents => true; protected override float HitThreshold => ColliderComponent.Threshold; - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { var colliderGenerator = new HitTargetColliderGenerator(this, MainComponent, MainComponent); colliderGenerator.GenerateColliders(ref colliders); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetComponent.cs index 2034b65a4..9f98de683 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetComponent.cs @@ -144,7 +144,6 @@ internal HitTargetState CreateState() } : default; return new HitTargetState( - hitTargetColliderComponent && hitTargetAnimationComponent ? gameObject.GetInstanceID() : 0, hitTargetAnimationComponent ? hitTargetAnimationComponent.gameObject.GetInstanceID() : 0, staticData, animationData diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetState.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetState.cs index 5938e876f..72f5f1897 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetState.cs @@ -18,14 +18,12 @@ namespace VisualPinball.Unity { internal struct HitTargetState { - internal readonly int ItemId; internal readonly int AnimatedItemId; internal HitTargetStaticData Static; internal HitTargetAnimationData Animation; - public HitTargetState(int itemId, int animatedItemId, HitTargetStaticData @static, HitTargetAnimationData animation) + public HitTargetState(int animatedItemId, HitTargetStaticData @static, HitTargetAnimationData animation) { - ItemId = itemId; AnimatedItemId = animatedItemId; Static = @static; Animation = animation; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/IApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/IApi.cs index 74f3816ad..9db4834e1 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/IApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/IApi.cs @@ -41,8 +41,9 @@ public interface IApiColliderGenerator /// Create colliders and add them to the provided list. /// /// List to add colliders to. + /// List to add kinematic colliders to. /// - void CreateColliders(ref ColliderReference colliders, float margin); + void CreateColliders(ref ColliderReference colliders, ref ColliderReference kinematicColliders, float margin); /// /// Computes collider info based on the component data. diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/ICollidableComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/ICollidableComponent.cs index 3367c18ea..76e47e4fb 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/ICollidableComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/ICollidableComponent.cs @@ -18,7 +18,7 @@ namespace VisualPinball.Unity { public interface ICollidableComponent { - internal void GetColliders(Player player, ref ColliderReference colliders, float margin); + internal void GetColliders(Player player, ref ColliderReference colliders, ref ColliderReference kinematicColliders, float margin); internal int ItemId { get; } internal bool IsCollidable { get; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/IKinematicColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/IKinematicColliderComponent.cs new file mode 100644 index 000000000..203149523 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/IKinematicColliderComponent.cs @@ -0,0 +1,27 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using Unity.Mathematics; + +namespace VisualPinball.Unity +{ + public interface IKinematicColliderComponent + { + public bool IsKinematic { get; } + public int ItemId { get; } + public float4x4 TransformationMatrix { get; } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/IKinematicColliderComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/IKinematicColliderComponent.cs.meta new file mode 100644 index 000000000..7fcfa17d4 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/IKinematicColliderComponent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 953c3c1b3d4c4aea9d41b5043345c551 +timeCreated: 1699050846 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs index 94b41e856..b78b51141 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs @@ -224,7 +224,8 @@ private void KickXYZ(float angle, float speed, float inclination, float x, float #region Collider Generation - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { var height = MainComponent.PositionZ; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs index 8d2cd03b7..872819dde 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs @@ -280,7 +280,6 @@ internal KickerState CreateState() : new ColliderMeshData(KickerHitMesh.Vertices, Radius, new float3(Center.x, Center.y, height), Allocator.Persistent); return new KickerState( - colliderComponent ? colliderComponent.gameObject.GetInstanceID() : 0, staticData, new KickerCollisionState(), meshData diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerState.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerState.cs index c18df1806..d4ffabf60 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerState.cs @@ -20,14 +20,12 @@ namespace VisualPinball.Unity { internal struct KickerState : IDisposable { - internal readonly int ItemId; internal KickerStaticState Static; internal KickerCollisionState Collision; internal ColliderMeshData CollisionMesh; - public KickerState(int itemId, KickerStaticState @static, KickerCollisionState collision, ColliderMeshData collisionMesh) + public KickerState(KickerStaticState @static, KickerCollisionState collision, ColliderMeshData collisionMesh) { - ItemId = itemId; Static = @static; Collision = collision; CollisionMesh = collisionMesh; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/MetalWireGuide/MetalWireGuideApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/MetalWireGuide/MetalWireGuideApi.cs index 2a26c3aba..9b04d3b79 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/MetalWireGuide/MetalWireGuideApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/MetalWireGuide/MetalWireGuideApi.cs @@ -43,7 +43,8 @@ internal MetalWireGuideApi(GameObject go, Player player, PhysicsEngine physicsEn protected override bool FireHitEvents => ColliderComponent.HitEvent; protected override float HitThreshold => 2.0f; // hard coded threshold for now - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { var colliderGenerator = new MetalWireGuideColliderGenerator(this, new MetalWireGuideMeshGenerator(MainComponent)); colliderGenerator.GenerateColliders(MainComponent.PlayfieldHeight, ColliderComponent.HitHeight, MainComponent.Bendradius, MainComponent.PlayfieldDetailLevel, ref colliders, margin); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Playfield/PlayfieldApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Playfield/PlayfieldApi.cs index 79121ef15..d5fed05ef 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Playfield/PlayfieldApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Playfield/PlayfieldApi.cs @@ -29,7 +29,8 @@ internal PlayfieldApi(GameObject go, Player player, PhysicsEngine physicsEngine) #region Collider Generation - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { var info = ((IApiColliderGenerator)this).GetColliderInfo(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs index 7ca9c3cd2..328d9f430 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs @@ -142,7 +142,8 @@ private IApiCoil Coil(string deviceItem) #region Collider Generation - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { colliders.Add(new PlungerCollider(MainComponent, ColliderComponent, GetColliderInfo())); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerComponent.cs index b7d9867b3..6bee71fe9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerComponent.cs @@ -303,7 +303,6 @@ internal PlungerState CreateState() }; return new PlungerState( - gameObject.GetInstanceID(), new PlungerStaticState { MomentumXfer = collComponent.MomentumXfer, ScatterVelocity = collComponent.ScatterVelocity, diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerState.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerState.cs index 66acf2347..2d7a4b008 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerState.cs @@ -18,16 +18,14 @@ namespace VisualPinball.Unity { internal struct PlungerState { - internal readonly int ItemId; internal PlungerStaticState Static; internal PlungerColliderState Collider; internal PlungerMovementState Movement; internal PlungerVelocityState Velocity; internal PlungerAnimationState Animation; - public PlungerState(int itemId, PlungerStaticState @static, PlungerColliderState collider, PlungerMovementState movement, PlungerVelocityState velocity, PlungerAnimationState animation) + public PlungerState(PlungerStaticState @static, PlungerColliderState collider, PlungerMovementState movement, PlungerVelocityState velocity, PlungerAnimationState animation) { - ItemId = itemId; Static = @static; Collider = collider; Movement = movement; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveApi.cs index af5a106f1..ecea5bf90 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveApi.cs @@ -43,10 +43,14 @@ internal PrimitiveApi(GameObject go, Player player, PhysicsEngine physicsEngine) protected override bool FireHitEvents => ColliderComponent.HitEvent; protected override float HitThreshold => ColliderComponent.Threshold; - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, ref ColliderReference kinematicColliders, float margin) { var colliderGenerator = new PrimitiveColliderGenerator(this, MainComponent, MainComponent); - colliderGenerator.GenerateColliders(ColliderComponent.CollisionReductionFactor, ref colliders); + if (ColliderComponent._isKinematic) { + colliderGenerator.GenerateColliders(ColliderComponent.CollisionReductionFactor, ref kinematicColliders); + } else { + colliderGenerator.GenerateColliders(ColliderComponent.CollisionReductionFactor, ref colliders); + } } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveColliderComponent.cs index d24b10d64..b30d404d4 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveColliderComponent.cs @@ -16,14 +16,14 @@ // ReSharper disable InconsistentNaming -using System.Collections.Generic; +using Unity.Mathematics; using UnityEngine; using VisualPinball.Engine.VPT.Primitive; namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Collision/Primitive Collider")] - public class PrimitiveColliderComponent : ColliderComponent + public class PrimitiveColliderComponent : ColliderComponent, IKinematicColliderComponent { #region Data @@ -57,6 +57,17 @@ public class PrimitiveColliderComponent : ColliderComponent _isKinematic; + public int ItemId => MainComponent.gameObject.GetInstanceID(); + public float4x4 TransformationMatrix => MainComponent.TransformationMatrix; + #endregion public override PhysicsMaterialData PhysicsMaterialData => GetPhysicsMaterialData(Elasticity, ElasticityFalloff, Friction, Scatter, OverwritePhysics); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveComponent.cs index 90a555e0f..85ad3bd4a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Primitive/PrimitiveComponent.cs @@ -95,6 +95,22 @@ public float2 RotatedPosition { } } + public float4x4 TransformationMatrix { + get { + var scaleMatrix = float4x4.Scale(Size); + var transMatrix = float4x4.Translate(new float3(Position.x, Position.y, Position.z + PlayfieldHeight)); + var rotTransMatrix = math.mul( + float4x4.EulerZYX(math.radians(Rotation)), + float4x4.Translate(Translation) + ); + rotTransMatrix = math.mul( + float4x4.EulerZYX(math.radians(ObjectRotation)), + rotTransMatrix + ); + return math.mul(transMatrix, math.mul(rotTransMatrix, scaleMatrix)); + } + } + #endregion #region Conversion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ramp/RampApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ramp/RampApi.cs index 66752d069..3e93b9e0f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ramp/RampApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ramp/RampApi.cs @@ -61,7 +61,8 @@ void IApi.OnDestroy() protected override bool FireHitEvents => ColliderComponent.HitEvent; protected override float HitThreshold => ColliderComponent.Threshold; - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { var colliderGenerator = new RampColliderGenerator(this, MainComponent, ColliderComponent); colliderGenerator.GenerateColliders(MainComponent.PlayfieldHeight, ref colliders, margin); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Rubber/RubberApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Rubber/RubberApi.cs index 2d92b9260..b4c0b0d2c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Rubber/RubberApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Rubber/RubberApi.cs @@ -43,7 +43,8 @@ internal RubberApi(GameObject go, Player player, PhysicsEngine physicsEngine) : protected override bool FireHitEvents => ColliderComponent.HitEvent; protected override float HitThreshold => 2.0f; // hard coded threshold for now - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { var colliderGenerator = new RubberColliderGenerator(this, new RubberMeshGenerator(MainComponent)); colliderGenerator.GenerateColliders(MainComponent.PlayfieldHeight, ColliderComponent.HitHeight, MainComponent.PlayfieldDetailLevel, ref colliders, margin); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerApi.cs index ccc5f7940..9019cfec0 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerApi.cs @@ -82,7 +82,8 @@ public SpinnerApi(GameObject go, Player player, PhysicsEngine physicsEngine) : b protected override bool FireHitEvents => true; - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { var colliderGenerator = new SpinnerColliderGenerator(this, MainComponent); colliderGenerator.GenerateColliders(MainComponent.HeightOnPlayfield, ref colliders); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerComponent.cs index d7fe56f65..f3e3718df 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerComponent.cs @@ -273,7 +273,6 @@ internal SpinnerState CreateState() } : default; return new SpinnerState( - collComponent ? gameObject.GetInstanceID() : 0, animComponent ? animComponent.gameObject.GetInstanceID() : 0, staticData, movementData diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerState.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerState.cs index 4fed8ca9d..4ce440e9e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerState.cs @@ -18,14 +18,12 @@ namespace VisualPinball.Unity { internal struct SpinnerState { - internal readonly int ItemId; internal readonly int AnimationItemId; internal SpinnerStaticState Static; internal SpinnerMovementState Movement; - public SpinnerState(int itemId, int animationItemId, SpinnerStaticState @static, SpinnerMovementState movement) + public SpinnerState(int animationItemId, SpinnerStaticState @static, SpinnerMovementState movement) { - ItemId = itemId; AnimationItemId = animationItemId; Static = @static; Movement = movement; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceApi.cs index 25f804dac..9ad5844a6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceApi.cs @@ -47,7 +47,8 @@ internal SurfaceApi(GameObject go, Player player, PhysicsEngine physicsEngine) : protected override bool FireHitEvents => true; protected override float HitThreshold => ColliderComponent.Threshold; - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { if (MainComponent.DragPoints.Length == 0) { return; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceComponent.cs index ee7dde8f3..f1f7d031a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceComponent.cs @@ -219,7 +219,7 @@ internal SurfaceState CreateState() return default; } - return new SurfaceState(gameObject.GetInstanceID(), new LineSlingshotState { + return new SurfaceState(new LineSlingshotState { IsDisabled = false, Threshold = collComponent.SlingshotThreshold, }); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceState.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceState.cs index 4213e4550..7d7c497b9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceState.cs @@ -18,12 +18,10 @@ namespace VisualPinball.Unity { internal struct SurfaceState { - internal readonly int ItemId; internal LineSlingshotState Slingshot; - public SurfaceState(int itemId, LineSlingshotState slingshot) + public SurfaceState(LineSlingshotState slingshot) { - ItemId = itemId; Slingshot = slingshot; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerApi.cs index 4e053be94..a867681e5 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerApi.cs @@ -63,7 +63,8 @@ internal TriggerApi(GameObject go, Player player, PhysicsEngine physicsEngine) : protected override bool FireHitEvents => true; - protected override void CreateColliders(ref ColliderReference colliders, float margin) + protected override void CreateColliders(ref ColliderReference colliders, + ref ColliderReference kinematicColliders, float margin) { var meshComponent = GameObject.GetComponent(); var colliderGenerator = new TriggerColliderGenerator(this, MainComponent, ColliderComponent, meshComponent); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs index 315d34357..2205b7d63 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs @@ -261,12 +261,11 @@ internal TriggerState CreateState() if (collComponent.ForFlipper == null) { return new TriggerState( - gameObject.GetInstanceID(), animComponent ? animComponent.gameObject.GetInstanceID() : 0, new TriggerStaticState { - AnimSpeed = animComponent.AnimSpeed, + AnimSpeed = animComponent ? animComponent.AnimSpeed : 0, Radius = collComponent.HitCircleRadius, - Shape = meshComponent.Shape, + Shape = meshComponent ? meshComponent.Shape : 0, TableScaleZ = 1f }, new TriggerMovementState(), @@ -275,7 +274,6 @@ internal TriggerState CreateState() } return new TriggerState( - gameObject.GetInstanceID(), new TriggerStaticState { AnimSpeed = 0, Radius = collComponent.HitCircleRadius, diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerState.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerState.cs index ef9acff93..afeddbf20 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerState.cs @@ -20,7 +20,6 @@ namespace VisualPinball.Unity { internal struct TriggerState : IDisposable { - internal readonly int ItemId; internal readonly int AnimatedItemId; internal TriggerStaticState Static; internal TriggerMovementState Movement; @@ -30,9 +29,8 @@ internal struct TriggerState : IDisposable /// /// Default trigger usage. /// - public TriggerState(int itemId, int animatedItemId, TriggerStaticState @static, TriggerMovementState movement, TriggerAnimationState animation) + public TriggerState(int animatedItemId, TriggerStaticState @static, TriggerMovementState movement, TriggerAnimationState animation) { - ItemId = itemId; AnimatedItemId = animatedItemId; Static = @static; Movement = movement; @@ -43,9 +41,8 @@ public TriggerState(int itemId, int animatedItemId, TriggerStaticState @static, /// /// Flipper correction usage. /// - public TriggerState(int itemId, TriggerStaticState @static, FlipperCorrectionState flipperCorrection) + public TriggerState(TriggerStaticState @static, FlipperCorrectionState flipperCorrection) { - ItemId = itemId; AnimatedItemId = 0; Static = @static; Movement = default;