AGX Dynamics 2.42.3.0
Loading...
Searching...
No Matches
CollisionGroupManager.h
Go to the documentation of this file.
1/*
2Copyright 2007-2025. Algoryx Simulation AB.
3
4All AGX source code, intellectual property, documentation, sample code,
5tutorials, scene files and technical white papers, are copyrighted, proprietary
6and confidential material of Algoryx Simulation AB. You may not download, read,
7store, distribute, publish, copy or otherwise disseminate, use or expose this
8material unless having a written signed agreement with Algoryx Simulation AB, or having been
9advised so by Algoryx Simulation AB for a time limited evaluation, or having purchased a
10valid commercial license from Algoryx Simulation AB.
11
12Algoryx Simulation AB disclaims all responsibilities for loss or damage caused
13from using this software, unless otherwise stated in written agreements with
14Algoryx Simulation AB.
15*/
16
17#ifndef AGXCOLLIDE_COLLISIONGROUPHANDLER_H
18#define AGXCOLLIDE_COLLISIONGROUPHANDLER_H
19
20#include <agx/HashSet.h>
21#include <agx/AtomicValue.h>
22#include <agx/Component.h>
26
27namespace agxCollide
28{
31 {
32 public:
34
35 public:
36 // typedef agx::Vector< agx::UInt32 > GroupIdVector;
42
44
45 public:
47
53 agx::Physics::CollisionGroupSetPtr set, agx::UInt32 groupId, bool unreferenceOldSet = true);
54
60 agx::Physics::CollisionGroupSetPtr set, agx::Physics::CollisionGroupSetPtr set2, bool unreferenceOldSet = true);
61
67 agx::Physics::CollisionGroupSetPtr set, agx::UInt32 groupId, bool unreferenceOldSet = true);
68
75
76
81
85 void setEnableCollisions(agx::UInt32 groupId1, agx::UInt32 groupId2, bool flag);
86
91 bool canSetsCollide(agx::UInt32 set1, agx::UInt32 set2) const;
92
93 // Wrapper
95
96
101 bool canGroupsCollide(agx::UInt32 group1, agx::UInt32 group2) const;
102
107
109 static bool hasGroup(agx::Physics::CollisionGroupSetPtr set, const agx::Name& groupName);
111
112 const CollisionHash& getDisabledGroupCollisions() const;
113 const CollisionHash& getDisabledSetCollisions() const;
114
116
117 // Not thread safe
118 bool hasGroup(const agx::Name& groupName) const;
119 bool hasGroup(agx::UInt32 groupId) const;
120
121 // Not thread safe
125
127
130
133
135 void reset();
137
138 void internal_restore(agxStream::InputArchive& in);
139
141
142 protected:
144
145 private:
146 friend class Space;
147 agx::UInt32 generateNameId();
148 void setUniqueIdCounter(agx::UInt32 id);
149 void setNameIdCounter(agx::UInt32 id);
150 agx::Physics::CollisionGroupPtr restoreGroup(const agx::Name& groupName, agx::UInt32 id);
151
152
153 private:
154 agx::Physics::CollisionGroupSetPtr getUnarySet(agx::UInt32 groupId);
155 void internal_unreferenceSet(agx::Physics::CollisionGroupSetPtr set);
156 bool internal_canCollide(const GroupIdHash& groups1, const GroupIdHash& groups2);
157 void removeInactiveSets();
158 void registerNewSet(agx::Physics::CollisionGroupSetPtr set);
159
160 class GroupSetCache
161 {
162 public:
163 class Bucket
164 {
165 public:
166 enum State
167 {
170 VALID
171 };
172
174
175 AGX_FORCE_INLINE Bucket() : state(INACTIVE)
176 {
177 }
178
180 {
181 groupSet = b.groupSet;
182 state = b.state;
183
184 return *this;
185 }
186
188 { return state >= ACTIVE; }
189
191 {
192 // prefetch( &state, agx::NTA );
193 return state == VALID;
194 }
195
196 private:
197 friend class GroupSetCache;
198 agx::UInt8 state;
199 };
200
201
202 public:
203 GroupSetCache() :
204 m_size(0), m_capacity(0), m_buckets(nullptr),
205 m_maxProbeLength(0)
206 {
207 this->rebuild(0);
208 }
209
210 void clear()
211 {
212 deallocateBuckets();
213 m_size = 0;
214 m_capacity = 0;
215 m_buckets = nullptr;
216 m_maxProbeLength = 0;
217 rebuild(0);
218 }
219
220 ~GroupSetCache()
221 {
222 deallocateBuckets();
223 }
224
225
227 {
228 agx::UInt32 bucketIndex = findBucket(&group, 1);
229 return bucketIndex != agx::InvalidIndex ? m_buckets[bucketIndex].groupSet : agx::Physics::CollisionGroupSetPtr();
230 }
231
233 {
234 agxAssert(set && set.groups().size() + 1 < 1024);
235 agx::Physics::CollisionGroupPtr localGroups[1024];
236 size_t numGroups = 0;
237
238 // Build extended list, insert group in already sorted vector
239 const GroupVector& groups = set.groups();
240 size_t i = 0;
241 for (; i < groups.size() && groups[i].id() < group.id(); ++i)
242 localGroups[numGroups++] = groups[i];
243
244 localGroups[numGroups++] = group;
245
246 for (; i < groups.size(); ++i)
247 localGroups[numGroups++] = groups[i];
248
249
250 agx::UInt32 bucketIndex = findBucket(localGroups, numGroups);
251 return bucketIndex != agx::InvalidIndex ? m_buckets[bucketIndex].groupSet : agx::Physics::CollisionGroupSetPtr();
252 }
253
255 {
256 agxAssert(set && set.groups().size() - 1 < 1024);
257 agx::Physics::CollisionGroupPtr localGroups[1024];
258 size_t numGroups = 0;
259
260 // Build reduced list
261 const GroupVector& groups = set.groups();
262 for (size_t i = 0; i < groups.size(); ++i)
263 {
264 if (groups[i].id() == group.id())
265 continue;
266
267 localGroups[numGroups++] = groups[i];
268 }
269
270 agx::UInt32 bucketIndex = findBucket(localGroups, numGroups);
271 return bucketIndex != agx::InvalidIndex ? m_buckets[bucketIndex].groupSet : agx::Physics::CollisionGroupSetPtr();
272 }
273
275 {
276 if (!set)
278
279 const GroupVector& groups = set.groups();
280
281 agx::UInt32 bucketIndex = findBucket(groups.begin(), groups.size());
282 return bucketIndex != agx::InvalidIndex ? m_buckets[bucketIndex].groupSet : agx::Physics::CollisionGroupSetPtr();
283 }
284
286 {
287 agxAssert(set);
288 const GroupVector& groups = set.groups();
289
290 agx::UInt32 hashValue = calculateHash(groups.begin(), groups.size());
291
292 Bucket *bucket;
293 Bucket *targetBucket = 0;
294 size_t offset = 0;
295 // std::cout << "Insert " << cellIndex << " with id " << cellId << std::endl;
296
297 /* Check if we should grow and rebuild hash table */
298 if (m_size >= (size_t)((float)m_capacity * AGX_HASH_SET_GROW_THRESHOLD))
299 this->rebuild((size_t)((float)(m_capacity+1) * AGX_HASH_SET_GROW_FACTOR));
300
301 /* Iterate until target bucket is found */
302 bool goon = true;
303 do
304 {
305 bucket = &m_buckets[(hashValue + offset * offset) % m_capacity];
306
307 /* Check if overwriting previous insertion with same key */
308 if (bucket->isValid() && compareGroups(groups.begin(), groups.size(), bucket->groupSet))
309 {
310 targetBucket = bucket;
311 break;
312 }
313
314 /* Tag first free slot */
315 if (!bucket->isValid() && !targetBucket)
316 targetBucket = bucket;
317
318 /* If we have found a free bucket we can stop iterating when we are sure that the key was not previously inserted */
319 if (targetBucket && offset >= m_maxProbeLength)
320 break;
321
322 /* Go to next bucket */
323 offset++;
324
325 /* Resize and rebuild table if it takes too long to find a free slot */
327 {
328 this->rebuild((size_t)((float)m_capacity * AGX_HASH_SET_GROW_FACTOR));
329 return this->insert(set);
330 }
331 } while (goon);
332
333 agxAssert(targetBucket);
334
335 if (offset > m_maxProbeLength)
336 m_maxProbeLength = (agx::UInt16)offset;
337
338 targetBucket->groupSet = set;
339
340 /* Check if inserting a new value rather than overwriting an old entry */
341 if (!targetBucket->isValid())
342 {
343 m_size++;
344 targetBucket->state = Bucket::VALID;
345 }
346 }
347
348 bool contains(agx::Physics::CollisionGroupSetPtr set) const
349 {
350 return find(set).isValid();
351 }
352
353
355 {
356 agxAssert(set);
357 const GroupVector& groups = set.groups();
358
359 agx::UInt32 bucketIndex = findBucket(groups.begin(), groups.size());
360 agxAssert(bucketIndex != agx::InvalidIndex);
361
362 Bucket *bucket = &m_buckets[bucketIndex];
363 agxAssert(bucket->isValid() && compareGroups(groups.begin(), groups.size(), bucket->groupSet));
364
365 m_size--;
366
367 // Bucket does not become inactive since it might be in the middle of a probing chain
368 bucket->state = Bucket::ACTIVE;
369 }
370
371 private:
372
373 AGX_FORCE_INLINE agx::UInt32 findBucket(const agx::Physics::CollisionGroupPtr *localGroups, size_t numGroups) const
374 {
375 agxAssert(m_buckets);
376 if (m_buckets) {
377
378 agx::UInt32 hashValue = calculateHash(localGroups, numGroups);
379
380 size_t index = hashValue % m_capacity;
381 const Bucket *bucket = &m_buckets[index];
382
383 /* First test is outside for-loop, hopefully getting better branch prediction */
384 if (bucket->isValid() && compareGroups(localGroups, numGroups, bucket->groupSet))
385 return (agx::UInt32)index;
386
387
388 for (size_t offset = 1; bucket->isActive() && offset <= m_maxProbeLength; offset++)
389 {
390 index = (hashValue + offset * offset) % m_capacity;
391 bucket = &m_buckets[index];
392
393 if (bucket->isValid() && compareGroups(localGroups, numGroups, bucket->groupSet))
394 return (agx::UInt32)index;
395 }
396 }
397
398 /* Not found */
399 return agx::InvalidIndex;
400 }
401
402 AGX_FORCE_INLINE agx::UInt32 calculateHash(const agx::Physics::CollisionGroupPtr *localGroups, size_t numGroups) const
403 {
404 // Calculate hash
405 agx::UInt32 hashValue = 0;
406 for (size_t i = 0; i < numGroups; ++i)
407 hashValue = agx::hash(hashValue, agx::hash(localGroups[i].id()));
408
409 return hashValue;
410 }
411
412
413 AGX_FORCE_INLINE bool compareGroups(const agx::Physics::CollisionGroupPtr *localGroups, size_t numGroups, agx::Physics::CollisionGroupSetPtr set) const
414 {
415 agxAssert(set);
416 const GroupVector& groups = set.groups();
417
418 if (groups.size() != numGroups)
419 return false;
420
421 for (size_t i = 0; i < groups.size(); ++i)
422 {
423 if (groups[i].id() != localGroups[i].id())
424 return false;
425 }
426
427 return true;
428 }
429
430 void rebuild(size_t size)
431 {
432 size = std::max( size, size_t(AGX_HASH_SET_MIN_SIZE) );
433 //LOGGER_DEBUG() << "HashTable: Rebuilding and resizing from " << numBuckets() << " to " << agx::nextPrime((int)(numBuckets() * resizeFactor)) << " buckets.\n" << LOGGER_END();
434 agxAssert(size >= m_size);
435 size_t oldNumBuckets = m_capacity;
436 Bucket *oldBuckets = m_buckets;
437
438 m_capacity = agx::nextPrime((int)size);
439 m_buckets = (Bucket *)m_allocator.allocateBytes(m_capacity * sizeof(Bucket));
440 for (size_t i = 0; i < m_capacity; ++i)
441 ::new (&m_buckets[i]) Bucket();
442
443 m_size = 0;
444 m_maxProbeLength = 0;
445
446 for (size_t i = 0; i < oldNumBuckets; i++)
447 {
448 Bucket& bucket = oldBuckets[i];
449
450 if (bucket.isValid())
451 this->insert(bucket.groupSet);
452 }
453
454 m_allocator.deallocateBytes((void *)oldBuckets, oldNumBuckets * sizeof(Bucket));
455 }
456
457 void deallocateBuckets()
458 {
459 m_allocator.deallocateBytes(m_buckets, m_capacity * sizeof(Bucket));
460 }
461
462 private:
463 size_t m_size;
464 size_t m_capacity;
465 Bucket *m_buckets;
466
467 agx::UInt16 m_maxProbeLength;
468 agx::ByteAllocator m_allocator;
469 };
470
471
472 private:
473 agx::AtomicValue m_nameIdCounter;
474 agx::AtomicValue m_uniqueIdCounter;
475 agx::Mutex m_mutex;
476 GroupSetCache m_groupSetCache;
477 agxData::EntityStorageRef m_collisionGroupSetStorage;
478
479 CollisionHash m_disabledSetCollisions;
480 CollisionHash m_disabledGroupCollisions;
481
483
484 agxData::EntityStorageRef m_collisionGroupStorage;
485
487 CollisionGroupTable m_collisionGroupTable;
488
489 typedef agx::HashVector<agx::Name, agx::UInt32> CollisionGroupNameTable;
490 CollisionGroupNameTable m_collisionGroupNameTable;
491 };
492
493
494 AGXPHYSICS_EXPORT std::ostream& operator << ( std::ostream& output, const agx::Physics::CollisionGroupSetPtr& set );
495 AGXPHYSICS_EXPORT std::ostream& operator << ( std::ostream& output, const agx::Physics::CollisionGroupPtr& group );
496
497
498
499
500 /* Implementation */
501
503 {
504 // std::cout << "canSetsCollide " << set1 << " : " << set2 << " => " << !m_disabledSetCollisions.contains(agx::buildHashKey(set1, set2)) << std::endl;
505 return !m_disabledSetCollisions.contains(agx::buildHashKey(set1, set2));
506 }
507
509 {
510 agxAssert(set1);
511 agxAssert(set2);
512 agxAssert(set1.getStorage() == m_collisionGroupSetStorage);
513 agxAssert(set2.getStorage() == m_collisionGroupSetStorage);
514 return this->canSetsCollide((agx::UInt32)set1.getId(), (agx::UInt32)set2.getId());
515 }
516
518 {
519 return !m_disabledGroupCollisions.contains(agx::buildHashKey(group1, group2));
520 }
521
524
525}
526
527
528#endif /* AGXCOLLIDE_COLLISIONGROUPHANDLER_H */
#define AGX_DECLARE_POINTER_TYPES(type)
Definition: Referenced.h:254
#define AGXSTREAM_DECLARE_SERIALIZABLE(T)
Use this in a Serializable class to add the required methods Important: Use full namespace in the dec...
Definition: Serializable.h:207
#define AGXPHYSICS_EXPORT
#define AGX_HASH_SET_MIN_SIZE
Definition: agx/HashSet.h:63
#define AGX_HASH_SET_MAX_PROBE_LENGTH
Definition: agx/HashSet.h:70
#define AGX_HASH_SET_GROW_FACTOR
Definition: agx/HashSet.h:67
#define AGX_HASH_SET_GROW_THRESHOLD
Definition: agx/HashSet.h:65
#define m_size
Definition: agx/Vector.h:429
#define m_capacity
Definition: agx/Vector.h:430
agx::Physics::CollisionGroupSetPtr getExtendedSet(agx::Physics::CollisionGroupSetPtr set, agx::UInt32 groupId, bool unreferenceOldSet=true)
Extend an existing group set by adding an additional group (can be nullptr pointer)
const agxData::EntityStorage * getCollisionGroupSetStorage() const
const CollisionHash & getDisabledGroupCollisions() const
agx::Physics::CollisionGroupPtr getGroup(const agx::Name &groupName)
const agxData::EntityStorage * getCollisionGroupStorage() const
static void printSet(const agx::Physics::CollisionGroupSetPtr &set)
static bool hasGroup(agx::Physics::CollisionGroupSetPtr set, agx::UInt32 groupId)
void unreferenceSet(agx::Physics::CollisionGroupSetPtr set)
Unreference a set when it is no longer used.
bool canSetsCollide(agx::UInt32 set1, agx::UInt32 set2) const
static bool hasGroup(agx::Physics::CollisionGroupSetPtr set, const agx::Name &groupName)
agx::HashSet< agx::Physics::CollisionGroupPtr > GroupHash
bool canGroupsCollide(agx::UInt32 group1, agx::UInt32 group2) const
void setEnableCollisions(agx::UInt32 groupId1, agx::UInt32 groupId2, bool flag)
Enable/disable collisions between two groups.
agx::Vector< agx::Physics::CollisionGroupPtr > GroupVector
static bool verify(agx::Physics::CollisionGroupSetPtr set)
agx::HashSet< agx::UInt32 > GroupIdHash
agx::Vector< agx::Physics::CollisionGroupSetPtr > GroupSetVector
agx::Physics::CollisionGroupSetPtr getExtendedSetFromAddedSet(agx::Physics::CollisionGroupSetPtr set, agx::Physics::CollisionGroupSetPtr set2, bool unreferenceOldSet=true)
Extend an existing group set by adding an additional group set.
const CollisionHash & getDisabledSetCollisions() const
static CollisionGroupManager * defaultManager()
agx::Physics::CollisionGroupPtr getGroup(agx::UInt32 groupId)
agx::Physics::CollisionGroupSetPtr getMatchingSet(agx::Physics::CollisionGroupSetPtr set)
Get the matching set stored in another CollisionGroupManager NOTE: The old set must be manually unref...
agx::HashSet< agx::Physics::CollisionGroupSetPtr > GroupSetHash
bool hasGroup(agx::UInt32 groupId) const
SymmetricCollisionGroupVector getDisabledCollisionGroupPairs() const
bool hasGroup(const agx::Name &groupName) const
agx::HashSet< agx::UInt64 > CollisionHash
agx::Physics::CollisionGroupSetPtr getReducedSet(agx::Physics::CollisionGroupSetPtr set, agx::UInt32 groupId, bool unreferenceOldSet=true)
Reduce an existing group set by removing a group.
agx::Vector< agx::SymmetricPair< agx::Physics::CollisionGroupPtr > > SymmetricCollisionGroupVector
agx::UInt32 getGroupId(const agx::Name &groupName)
This class contains all Geometries and performs Broad Phase and Narrow Phase collision detection to c...
Definition: Space.h:86
agx::Index getId() const
Definition: EntityPtr.h:273
agxData::EntityStorage * getStorage() const
Definition: EntityPtr.h:272
Data storage for a collection of entity instances of a specified EntityModel.
Definition: EntityStorage.h:73
This class is an abstract base class for all classes that can be stored and retrieved from an Archive...
Definition: Serializable.h:44
Byte allocator.
Definition: Allocator.h:55
A component is an object containing other objects, enabling hierarchical structuring.
Definition: Component.h:39
bool contains(const KeyT &key) const
Boolean query to test existence of an element.
Definition: agx/HashSet.h:314
This class is a combined container which has the find complexity of a HashTable, deterministic iterat...
Definition: HashVector.h:41
Inheritance with partial specialization due to bug with ref_ptr containers.
Representation of a name string.
Definition: Name.h:33
Pointer to a entity instance of type Physics.CollisionGroup.
AGXPHYSICS_EXPORT agx::UInt32 & id()
Pointer to a entity instance of type Physics.CollisionGroupSet.
AGXPHYSICS_EXPORT agx::Vector< agx::Physics::CollisionGroupPtr > & groups()
Templated vector class.
Definition: agx/Vector.h:53
#define agxAssert(expr)
Definition: debug.h:143
#define DOXYGEN_END_INTERNAL_BLOCK()
Definition: macros.h:89
#define DOXYGEN_START_INTERNAL_BLOCK()
Definition: macros.h:88
#define AGX_FORCE_INLINE
Definition: macros.h:58
This namespace consists of a set of classes for handling geometric intersection tests including boole...
std::ostream & operator<<(std::ostream &output, const BoundingAABB &bound)
Definition: BoundingAABB.h:224
This namespace contain classes for streaming classes into archives, ASCII, binary for storage (serial...
std::pair< agx::Name, agx::Real > Bucket
The agx namespace contains the dynamics/math part of the AGX Dynamics API.
UInt32 hash(const T &key)
Definition: HashFunction.h:165
uint16_t UInt16
Definition: Integer.h:31
std::mutex Mutex
Definition: Referenced.h:99
uint32_t UInt32
Definition: Integer.h:32
AGXCORE_EXPORT int nextPrime(int n)
AGXCORE_EXPORT agx::UInt64 buildHashKey(agx::UInt32 id1, agx::UInt32 id2)
Definition: HashFunction.h:42
uint8_t UInt8
Definition: Integer.h:30
AGXCORE_EXPORT const InvalidIndexStruct InvalidIndex