LoggingContactListener.h 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <Jolt/Physics/Collision/ContactListener.h>
  5. // Contact listener that just logs the calls made to it for later validation
  6. class LoggingContactListener : public ContactListener
  7. {
  8. public:
  9. // Contact callback type
  10. enum class EType
  11. {
  12. Validate,
  13. Add,
  14. Persist,
  15. Remove
  16. };
  17. // Entry written when a contact callback happens
  18. struct LogEntry
  19. {
  20. EType mType;
  21. BodyID mBody1;
  22. BodyID mBody2;
  23. ContactManifold mManifold;
  24. };
  25. virtual ValidateResult OnContactValidate(const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset, const CollideShapeResult &inCollisionResult) override
  26. {
  27. // Check contract that body 1 is dynamic or that body2 is not dynamic
  28. bool contract = inBody1.IsDynamic() || !inBody2.IsDynamic();
  29. CHECK(contract);
  30. lock_guard lock(mLogMutex);
  31. mLog.push_back({ EType::Validate, inBody1.GetID(), inBody2.GetID(), ContactManifold() });
  32. return ValidateResult::AcceptContact;
  33. }
  34. virtual void OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override
  35. {
  36. // Check contract that body 1 < body 2
  37. CHECK(inBody1.GetID() < inBody2.GetID());
  38. lock_guard lock(mLogMutex);
  39. SubShapeIDPair key(inBody1.GetID(), inManifold.mSubShapeID1, inBody2.GetID(), inManifold.mSubShapeID2);
  40. CHECK(mExistingContacts.insert(key).second); // Validate that contact does not exist yet
  41. mLog.push_back({ EType::Add, inBody1.GetID(), inBody2.GetID(), inManifold });
  42. }
  43. virtual void OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override
  44. {
  45. // Check contract that body 1 < body 2
  46. CHECK(inBody1.GetID() < inBody2.GetID());
  47. lock_guard lock(mLogMutex);
  48. SubShapeIDPair key(inBody1.GetID(), inManifold.mSubShapeID1, inBody2.GetID(), inManifold.mSubShapeID2);
  49. CHECK(mExistingContacts.find(key) != mExistingContacts.end()); // Validate that OnContactAdded was called
  50. mLog.push_back({ EType::Persist, inBody1.GetID(), inBody2.GetID(), inManifold });
  51. }
  52. virtual void OnContactRemoved(const SubShapeIDPair &inSubShapePair) override
  53. {
  54. // Check contract that body 1 < body 2
  55. CHECK(inSubShapePair.GetBody1ID() < inSubShapePair.GetBody2ID());
  56. lock_guard lock(mLogMutex);
  57. CHECK(mExistingContacts.erase(inSubShapePair) == 1); // Validate that OnContactAdded was called
  58. ContactManifold manifold;
  59. manifold.mSubShapeID1 = inSubShapePair.GetSubShapeID1();
  60. manifold.mSubShapeID2 = inSubShapePair.GetSubShapeID2();
  61. mLog.push_back({ EType::Remove, inSubShapePair.GetBody1ID(), inSubShapePair.GetBody2ID(), manifold });
  62. }
  63. void Clear()
  64. {
  65. mLog.clear();
  66. }
  67. size_t GetEntryCount() const
  68. {
  69. return mLog.size();
  70. }
  71. const LogEntry & GetEntry(size_t inIdx) const
  72. {
  73. return mLog[inIdx];
  74. }
  75. // Find first event with a particular type and involving two particular bodies
  76. int Find(EType inType, const BodyID &inBody1, const BodyID &inBody2) const
  77. {
  78. for (size_t i = 0; i < mLog.size(); ++i)
  79. {
  80. const LogEntry &e = mLog[i];
  81. if (e.mType == inType && ((e.mBody1 == inBody1 && e.mBody2 == inBody2) || (e.mBody1 == inBody2 && e.mBody2 == inBody1)))
  82. return int(i);
  83. }
  84. return -1;
  85. }
  86. // Check if event with a particular type and involving two particular bodies exists
  87. bool Contains(EType inType, const BodyID &inBody1, const BodyID &inBody2) const
  88. {
  89. return Find(inType, inBody1, inBody2) >= 0;
  90. }
  91. private:
  92. Mutex mLogMutex; // Callbacks are made from a thread, make sure we don't corrupt the log
  93. Array<LogEntry> mLog;
  94. UnorderedSet<SubShapeIDPair> mExistingContacts; // For validation purposes: the contacts that are currently active
  95. };