// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Chaos/ExternalCollisionData.h" #include "Chaos/CollisionResolutionTypes.h" #include "Chaos/Framework/PhysicsProxy.h" class UPrimitiveComponent; namespace Chaos { // base class for data that requires time of creation to be recorded struct FTimeResource { FTimeResource() : TimeCreated(-TNumericLimits::Max()) {} FReal TimeCreated; }; typedef TArray FCollisionDataArray; typedef TArray FBreakingDataArray; typedef TArray FTrailingDataArray; typedef TArray FRemovalDataArray; typedef TArray FSleepingDataArray; typedef TArray FCrumblingDataArray; /* Common */ /* Maps PhysicsProxy to list of indices in events arrays * - for looking up say all collisions a particular physics object had this frame */ struct FIndicesByPhysicsProxy : public FTimeResource { FIndicesByPhysicsProxy() : PhysicsProxyToIndicesMap(TMap>()) {} void Reset() { PhysicsProxyToIndicesMap.Reset(); } TMap> PhysicsProxyToIndicesMap; // PhysicsProxy -> Indices in Events arrays }; /* Collision */ /* * All the collision events for one frame time stamped with the time for that frame */ struct FAllCollisionData : public FTimeResource { FAllCollisionData() : AllCollisionsArray(FCollisionDataArray()) {} void Reset() { AllCollisionsArray.Reset(); } FCollisionDataArray AllCollisionsArray; }; struct FCollisionEventData { FCollisionEventData() {} void Reset() { PhysicsProxyToCollisionIndices.PhysicsProxyToIndicesMap.Reset(); CollisionData.Reset(); } FAllCollisionData CollisionData; FIndicesByPhysicsProxy PhysicsProxyToCollisionIndices; }; /* Breaking */ /* * All the breaking events for one frame time stamped with the time for that frame */ struct FAllBreakingData : public FTimeResource { FAllBreakingData() : AllBreakingsArray(FBreakingDataArray()), bHasGlobalEvent(false) {} void Reset() { AllBreakingsArray.Reset(); bHasGlobalEvent = false; } FBreakingDataArray AllBreakingsArray; bool bHasGlobalEvent; }; struct FBreakingEventData { FBreakingEventData() {} void Reset() { BreakingData.Reset(); PhysicsProxyToBreakingIndices.PhysicsProxyToIndicesMap.Reset(); } FAllBreakingData BreakingData; FIndicesByPhysicsProxy PhysicsProxyToBreakingIndices; }; /* Trailing */ /* * All the trailing events for one frame time stamped with the time for that frame */ struct FAllTrailingData : FTimeResource { FAllTrailingData() : AllTrailingsArray(FTrailingDataArray()) {} void Reset() { AllTrailingsArray.Reset(); } FTrailingDataArray AllTrailingsArray; }; struct FTrailingEventData { FTrailingEventData() {} void Reset() { TrailingData.Reset(); PhysicsProxyToTrailingIndices.Reset(); } FAllTrailingData TrailingData; FIndicesByPhysicsProxy PhysicsProxyToTrailingIndices; }; /* Removal */ /* * All the removal events for one frame time stamped with the time for that frame */ struct FAllRemovalData : FTimeResource { FAllRemovalData() : AllRemovalArray(FRemovalDataArray()) {} void Reset() { AllRemovalArray.Reset(); } FRemovalDataArray AllRemovalArray; }; struct FRemovalEventData { FRemovalEventData() {} void Reset() { RemovalData.Reset(); PhysicsProxyToRemovalIndices.PhysicsProxyToIndicesMap.Reset(); } FAllRemovalData RemovalData; FIndicesByPhysicsProxy PhysicsProxyToRemovalIndices; }; struct FSleepingEventData { FSleepingEventData() {} void Reset() { SleepingData.Reset(); } FSleepingDataArray SleepingData; }; /* * All the crumbling events for one frame time stamped with the time for that frame */ struct FAllCrumblingData : public FTimeResource { FAllCrumblingData() : AllCrumblingsArray(FCrumblingDataArray()), bHasGlobalEvent(false) {} void Reset() { AllCrumblingsArray.Reset(); bHasGlobalEvent = false; } FCrumblingDataArray AllCrumblingsArray; bool bHasGlobalEvent; }; struct FCrumblingEventData { FCrumblingEventData() {} void Reset() { CrumblingData.Reset(); PhysicsProxyToCrumblingIndices.Reset(); } FORCEINLINE_DEBUGGABLE void Reserve(int32 Num) { CrumblingData.AllCrumblingsArray.Reserve(Num); } FORCEINLINE_DEBUGGABLE void SetTimeCreated(FReal TimeCreatedIn) { CrumblingData.TimeCreated = TimeCreatedIn; } FORCEINLINE_DEBUGGABLE void AddCrumbling(const FCrumblingData& CrumblingToAdd) { const int32 NewIndex = CrumblingData.AllCrumblingsArray.Emplace(CrumblingToAdd); TArray& Indices = PhysicsProxyToCrumblingIndices.PhysicsProxyToIndicesMap.FindOrAdd(CrumblingToAdd.Proxy); Indices.Add(NewIndex); } FAllCrumblingData CrumblingData; FIndicesByPhysicsProxy PhysicsProxyToCrumblingIndices; }; template bool IsEventDataEmpty(const PayloadType* Buffer) { if (!Buffer) { return false; } if constexpr (std::is_same_v) { return Buffer->CollisionData.AllCollisionsArray.IsEmpty(); } else if constexpr (std::is_same_v) { return Buffer->BreakingData.AllBreakingsArray.IsEmpty(); } else if constexpr (std::is_same_v) { return Buffer->TrailingData.AllTrailingsArray.IsEmpty(); } else if constexpr (std::is_same_v) { return Buffer->RemovalData.AllRemovalArray.IsEmpty(); } else if constexpr (std::is_same_v) { return Buffer->SleepingData.IsEmpty(); } else if constexpr (std::is_same_v) { return Buffer->CrumblingData.AllCrumblingsArray.IsEmpty(); } else { return false; } } template const TMap>* GetProxyToIndexMap(const PayloadType* Buffer) { if (!Buffer) { return nullptr; } if constexpr (std::is_same_v) { return &Buffer->PhysicsProxyToCollisionIndices.PhysicsProxyToIndicesMap; } else if constexpr (std::is_same_v) { return &Buffer->PhysicsProxyToBreakingIndices.PhysicsProxyToIndicesMap; } else if constexpr (std::is_same_v) { return nullptr; //&Buffer->PhysicsProxyToTrailingIndices.PhysicsProxyToIndicesMap; } else if constexpr (std::is_same_v) { return &Buffer->PhysicsProxyToRemovalIndices.PhysicsProxyToIndicesMap; } else if constexpr (std::is_same_v) { return nullptr; } else if constexpr (std::is_same_v) { return &Buffer->PhysicsProxyToCrumblingIndices.PhysicsProxyToIndicesMap; } else { return nullptr; } } // An index into a FCollisionEventData array of FCollidingDataobjects obtained from // the PhysicsProxyToIndicesMap for a specific proxy. // Indices held in FCollisionEventData::PhysicsProxyToCollisionIndices are encoded // to include the proxy index in the collision data structure (0 or 1). // This is useful if you collect a set of collision indices for a set of proxies, and // the store a flat list of FCollidingDataIndexes. In this case it may be expensive // to determine which of the proxies in the collision belongs to one of our proxies. class FCollidingDataIndex { public: FCollidingDataIndex() : ProxyIndex(0) , CollisionIndex(INDEX_NONE) { } // InProxyIndex: The index of the proxy in the FCollidingData (0 or 1) // InCollisionIndex: The index of the FCollidingData in the FCollisionEventData array FCollidingDataIndex(int32 InCollisionIndex, int32 InProxyIndex) : ProxyIndex(InProxyIndex) , CollisionIndex(InCollisionIndex) { check(InCollisionIndex >= 0); check(InProxyIndex >= 0); check(InProxyIndex <= 1); } // Reset to invalid state void Reset() { ProxyIndex = 0; CollisionIndex = INDEX_NONE; } // Is this index a valid index into the FCollidingData array? bool IsValid() const { return CollisionIndex >= 0; } // The index into the FCollidingData array int32 GetIndex() const { return CollisionIndex; } // The index of our body in the collision data. This will be zero or one. int32 GetProxyIndex() const { return ProxyIndex; } private: uint32 ProxyIndex : 1; int32 CollisionIndex : 31; }; // It wouldn't matter if it were larger, but just in case more flags are added and we do something unexpected... static_assert(sizeof(FCollidingDataIndex) == sizeof(int32), "FCollidingDataIndex should be same size as int32"); // Iterates over the FCollidingData objects for a set of proxies // // Usage: // for (FCollisionEventDataIterator It(MyProxyList, CollisionData); It; ++It) // { // Chaos::FCollidingDataIndex CollidingDataIndex = It.GetCurrentCollidingDataIndex(); // if (CollidingDataIndex.IsValid()) // { // // CollidingData is between one ot the proxies in MyProxyList and another (which in general may also be in MyProxyList!) // const Chaos::FCollidingData& CollidingData = CollisionEventData.CollisionData.AllCollisionsArray[CollidingDataIndex.GetIndex()]; // // Chaos::FVec3 Normal = (CollidingDataIndex.GetProxyIndex() == 0) ? CollidingData.Normal : -CollidingData.Normal // float Mass = (CollidingDataIndex.GetProxyIndex() == 0) ? CollidingData.Mass1 : CollidingData.Mass2; // // ... // }} // } // class FCollisionEventDataIterator { public: CHAOS_API FCollisionEventDataIterator(const TArrayView& InProxies, const Chaos::FCollisionEventData& InCollisionEventData); // Reset the iterator to point at the first FCollidingData for the first proxy that has any collisions. CHAOS_API void Reset(); // Move to the next collision. If we have reached the end of collisions for the current proxy, // move to the next proxy with collisions and select its first collision. CHAOS_API void Next(); // Get the index of the current collision in the CollisionEventData's CollidingData array // or INDEX_NONE if we have reached the end of the collisions for all proxies. CHAOS_API FCollidingDataIndex GetCurrentCollidingDataIndex() const; // Get the Proxy that the iterator is currently pointing to // or null if we have reached the end of the collisions for all proxies. CHAOS_API const IPhysicsProxyBase* GetCurrentProxy() const; // Have we reached the end of the collisions for all proxies? bool IsFinished() const { return (ProxyIndex >= Proxies.Num()); } // Returns true if we are still iterating (have not reached the end) operator bool() const { return !IsFinished(); } // Move to the next non-empty collision/proxy FCollisionEventDataIterator& operator++() { Next(); return *this; } // NOTE: Deliberately not providing a post-increment operator // because it is more expensive to copy than users might expect // (although not that bad really, so feel free to add one if // you really need it!) FCollisionEventDataIterator operator++(int) = delete; private: // Current Iteration state int32 ProxyIndex; int32 ProxyCollisionIndex; const TArray* ProxyCollisionIndices; // Set of proxies we care about TArrayView Proxies; // Collision data from the whole scene const Chaos::FCollisionEventData& CollisionEventData; }; }