!B (Sandbox) (CE-21795) Importing models with multisubmaterials via fbx switches...
[CRYENGINE.git] / Code / CryEngine / CrySchematyc2 / BaseEnv / Utils / BaseEnv_SpatialIndex.cpp
blob970c4829545fe5cfdca6994feba911b36f722476
1 // Copyright 2001-2019 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "StdAfx.h"
4 #include "BaseEnv/Utils/BaseEnv_SpatialIndex.h"
6 #include <CryRenderer/IRenderAuxGeom.h>
8 SERIALIZATION_ENUM_BEGIN_NESTED(SchematycBaseEnv, ESpatialVolumeShape, "Spatial volume shape")
9 SERIALIZATION_ENUM(SchematycBaseEnv::ESpatialVolumeShape::Box, "Box", "Box")
10 SERIALIZATION_ENUM(SchematycBaseEnv::ESpatialVolumeShape::Sphere, "Sphere", "Sphere")
11 SERIALIZATION_ENUM(SchematycBaseEnv::ESpatialVolumeShape::Point, "Point", "Point")
12 SERIALIZATION_ENUM_END()
14 namespace SchematycBaseEnv
16 const uint32 CSpatialIndex::s_volumeIdxMask = 0x0000ffff;
17 const uint32 CSpatialIndex::s_volumeSaltShift = 16;
18 const uint32 CSpatialIndex::s_volumeSaltMask = 0xffff0000;
20 CSpatialVolumeBounds::CSpatialVolumeBounds()
21 : m_shape(ESpatialVolumeShape::None)
24 CSpatialVolumeBounds::CSpatialVolumeBounds(const OBB& rhs)
25 : m_shape(ESpatialVolumeShape::Box)
27 new (m_storage) OBB(rhs);
30 CSpatialVolumeBounds::CSpatialVolumeBounds(const Sphere& rhs)
31 : m_shape(ESpatialVolumeShape::Sphere)
33 new (m_storage) Sphere(rhs);
36 CSpatialVolumeBounds::CSpatialVolumeBounds(const Vec3& rhs)
37 : m_shape(ESpatialVolumeShape::Point)
39 new (m_storage) Vec3(rhs);
42 CSpatialVolumeBounds::CSpatialVolumeBounds(const CSpatialVolumeBounds& rhs)
43 : m_shape(rhs.m_shape)
45 Copy(rhs);
48 CSpatialVolumeBounds::~CSpatialVolumeBounds()
50 Release();
53 CSpatialVolumeBounds CSpatialVolumeBounds::CreateOBB(const Matrix34& volumeTM, const Vec3& pos, const Vec3& size, const Matrix33& rot)
55 const QuatT transform = QuatT(volumeTM) * QuatT(Quat(rot), pos);
56 return CSpatialVolumeBounds(OBB::CreateOBB(Matrix33(transform.q), size*0.5f, transform.q.GetInverted() * transform.t));
59 CSpatialVolumeBounds CSpatialVolumeBounds::CreateSphere(const Matrix34& volumeTM, const Vec3& pos, float radius)
61 return CSpatialVolumeBounds(Sphere(volumeTM.TransformPoint(pos), radius));
64 CSpatialVolumeBounds CSpatialVolumeBounds::CreatePoint(const Matrix34& volumeTM, const Vec3& pos)
66 return CSpatialVolumeBounds(volumeTM.TransformPoint(pos));
69 ESpatialVolumeShape CSpatialVolumeBounds::GetShape() const
71 return m_shape;
74 const OBB& CSpatialVolumeBounds::AsBox() const
76 CRY_ASSERT(m_shape == ESpatialVolumeShape::Box);
77 return *reinterpret_cast<const OBB*>(m_storage);
80 const Sphere& CSpatialVolumeBounds::AsSphere() const
82 CRY_ASSERT(m_shape == ESpatialVolumeShape::Sphere);
83 return *reinterpret_cast<const Sphere*>(m_storage);
86 const Vec3& CSpatialVolumeBounds::AsPoint() const
88 CRY_ASSERT(m_shape == ESpatialVolumeShape::Point);
89 return *reinterpret_cast<const Vec3*>(m_storage);
92 AABB CSpatialVolumeBounds::CalculateAABB() const
94 switch(m_shape)
96 case ESpatialVolumeShape::Box:
98 return AABB::CreateAABBfromOBB(Vec3(ZERO), AsBox());
100 case ESpatialVolumeShape::Sphere:
102 return AABB(AsSphere().center, AsSphere().radius);
104 case ESpatialVolumeShape::Point:
106 return AABB(AsPoint(), AsPoint());
109 return AABB(0.0f);
112 bool CSpatialVolumeBounds::Overlap(const CSpatialVolumeBounds& rhs) const
114 switch(m_shape)
116 case ESpatialVolumeShape::Box:
118 switch(rhs.m_shape)
120 case ESpatialVolumeShape::Box:
122 return Overlap::OBB_OBB(Vec3(ZERO), AsBox(), Vec3(ZERO), rhs.AsBox());
124 case ESpatialVolumeShape::Sphere:
126 return Overlap::Sphere_OBB(rhs.AsSphere(), AsBox());
128 case ESpatialVolumeShape::Point:
130 return Overlap::Point_OBB(rhs.AsPoint(), Vec3(ZERO), AsBox());
133 break;
135 case ESpatialVolumeShape::Sphere:
137 switch(rhs.m_shape)
139 case ESpatialVolumeShape::Box:
141 return Overlap::Sphere_OBB(AsSphere(), rhs.AsBox());
143 case ESpatialVolumeShape::Sphere:
145 return Overlap::Sphere_Sphere(AsSphere(), rhs.AsSphere());
147 case ESpatialVolumeShape::Point:
149 return Overlap::Point_Sphere(rhs.AsPoint(), AsSphere());
152 break;
154 case ESpatialVolumeShape::Point:
156 switch(rhs.m_shape)
158 case ESpatialVolumeShape::Box:
160 return Overlap::Point_OBB(AsPoint(), Vec3(ZERO), rhs.AsBox());
162 case ESpatialVolumeShape::Sphere:
164 return Overlap::Point_Sphere(AsPoint(), rhs.AsSphere());
166 // N.B. No case for ESpatialVolumeShape::Point because we should never need to detect overlapping points.
168 break;
171 return false;
174 void CSpatialVolumeBounds::operator = (const CSpatialVolumeBounds& rhs)
176 Release();
177 Copy(rhs);
180 void CSpatialVolumeBounds::Copy(const CSpatialVolumeBounds& rhs)
182 m_shape = rhs.m_shape;
183 switch(rhs.m_shape)
185 case ESpatialVolumeShape::Box:
187 new (m_storage) OBB(rhs.AsBox());
188 break;
190 case ESpatialVolumeShape::Sphere:
192 new (m_storage) Sphere(rhs.AsSphere());
193 break;
195 case ESpatialVolumeShape::Point:
197 new (m_storage) Vec3(rhs.AsPoint());
198 break;
203 void CSpatialVolumeBounds::Release()
205 switch(m_shape)
207 case ESpatialVolumeShape::Box:
209 reinterpret_cast<const OBB*>(m_storage)->~OBB();
210 break;
212 case ESpatialVolumeShape::Sphere:
214 reinterpret_cast<const Sphere*>(m_storage)->~Sphere();
215 break;
217 case ESpatialVolumeShape::Point:
219 reinterpret_cast<const Vec3*>(m_storage)->~Vec3();
220 break;
225 SSpatialVolumeTagName::SSpatialVolumeTagName() {}
227 SSpatialVolumeTagName::SSpatialVolumeTagName(const string& _value)
228 : value(_value)
231 bool SSpatialVolumeTagName::operator == (const SSpatialVolumeTagName& rhs) const
233 return value == rhs.value;
236 bool Serialize(Serialization::IArchive& archive, SSpatialVolumeTagName& value, const char* szName, const char* szLabel)
238 /*const Serialization::StringList& tagNames = g_pGame->GetSchematycEnv().GetEntityDetectionVolumeSystem().GetTagNames();
239 if(archive.isInput())
241 Serialization::StringListValue temp(tagNames, 0);
242 archive(temp, szName, szLabel);
243 value.value = temp.c_str();
245 else if(archive.isOutput())
247 const int pos = tagNames.find(value.value.c_str());
248 archive(Serialization::StringListValue(tagNames, pos), szName, szLabel);
250 return true;
253 SSpatialVolumeParams::SSpatialVolumeParams()
254 : tags(ESpatialVolumeTags::None)
255 , monitorTags(ESpatialVolumeTags::None)
256 , entityId(0)
259 SSpatialVolumeParams::SSpatialVolumeParams(const CSpatialVolumeBounds& _bounds, ESpatialVolumeTags _tags, ESpatialVolumeTags _monitorTags, EntityId _entityId)
260 : bounds(_bounds)
261 , tags(_tags)
262 , monitorTags(_monitorTags)
263 , entityId(_entityId)
266 void CSpatialIndex::SSettings::Serialize(Serialization::IArchive& archive)
268 archive(userTags, "userTags", "+User Tags");
269 if(archive.isInput())
271 RefreshTags();
275 void CSpatialIndex::SSettings::RefreshTags()
277 tagNames.clear();
278 tagValues.clear();
280 const Serialization::EnumDescription& tagsEnumDescription = Serialization::getEnumDescription<ESpatialVolumeTags>();
281 UserTags::const_iterator itUserTag = userTags.begin();
282 UserTags::const_iterator itEndUserTag = userTags.end();
283 for(int tagValue = static_cast<int>(ESpatialVolumeTags::Begin); tagValue != static_cast<int>(ESpatialVolumeTags::End); tagValue <<= 1)
285 const int tagIdx = tagsEnumDescription.indexByValue(tagValue);
286 if(tagIdx != -1)
288 tagNames.push_back(tagsEnumDescription.labelByIndex(tagIdx));
289 tagValues.push_back(tagValue);
291 else if(itUserTag != itEndUserTag)
293 tagNames.push_back(*itUserTag);
294 tagValues.push_back(tagValue);
295 ++ itUserTag;
300 CSpatialIndex::SVolume::SVolume()
301 : monitorTags(ESpatialVolumeTags::None)
302 , entityId(0)
305 CSpatialIndex::SBroadPhaseVolume::SBroadPhaseVolume()
306 : tags(ESpatialVolumeTags::None)
309 CSpatialIndex::CSpatialIndex()
310 : m_pSettings(new SSettings())
311 , m_salt(0)
313 m_pSettings->RefreshTags();
314 gEnv->pSchematyc2->GetEnvRegistry().RegisterSettings("spatial_index_settings", m_pSettings);
316 m_volumes.reserve(256);
317 m_broadPhaseVolumes.reserve(256);
318 m_freeVolumes.reserve(64);
319 m_broadPhaseResults.reserve(64);
320 m_monitorResults.reserve(16);
321 m_eventQueue.reserve(64);
324 SpatialVolumeId CSpatialIndex::CreateVolume(const SSpatialVolumeParams& params, const SpatialIndexEventCallback& eventCallback)
326 uint32 volumeIdx;
327 if(m_freeVolumes.empty())
329 const uint32 volumeCount = m_volumes.size();
330 if(volumeCount > s_volumeIdxMask)
332 return SpatialVolumeId();
334 else
336 volumeIdx = volumeCount;
337 m_volumes.push_back(SVolume());
338 m_broadPhaseVolumes.push_back(SBroadPhaseVolume());
341 else
343 volumeIdx = m_freeVolumes.back();
344 m_freeVolumes.pop_back();
347 SVolume& volume = m_volumes[volumeIdx];
348 volume.bounds = params.bounds;
349 volume.monitorTags = params.monitorTags;
350 volume.id = GenerateVolumeId(volumeIdx);
351 volume.entityId = params.entityId;
352 volume.eventCallback = eventCallback;
354 volume.monitorCache.clear();
355 if(params.monitorTags != ESpatialVolumeTags::None)
357 volume.monitorCache.reserve(16);
360 SBroadPhaseVolume& broadPhaseVolume = m_broadPhaseVolumes[volumeIdx];
361 broadPhaseVolume.tags = params.tags;
362 broadPhaseVolume.aabb = params.bounds.CalculateAABB();
364 return volume.id;
367 void CSpatialIndex::DestroyVolume(const SpatialVolumeId& id)
369 if(id != SpatialVolumeId::s_invalid)
371 const uint32 volumeCount = m_volumes.size();
372 const uint32 volumeIdx = VolumeIdxFromId(id);
373 CRY_ASSERT(volumeIdx < volumeCount);
374 if(volumeIdx < volumeCount)
376 SVolume& volume = m_volumes[volumeIdx];
377 CRY_ASSERT(volume.id == id);
378 if(volume.id == id)
380 volume.bounds = CSpatialVolumeBounds();
381 volume.monitorTags = ESpatialVolumeTags::None;
382 volume.eventCallback = SpatialIndexEventCallback();
383 m_broadPhaseVolumes[volumeIdx].tags = ESpatialVolumeTags::None;
384 m_freeVolumes.push_back(volumeIdx);
390 void CSpatialIndex::UpdateVolumeBounds(const SpatialVolumeId& id, const CSpatialVolumeBounds& bounds)
392 if(id != SpatialVolumeId::s_invalid)
394 CRY_PROFILE_FUNCTION(PROFILE_GAME);
396 const uint32 volumeCount = m_volumes.size();
397 const uint32 volumeIdx = VolumeIdxFromId(id);
398 CRY_ASSERT(volumeIdx < volumeCount);
399 if(volumeIdx < volumeCount)
401 SVolume& volume = m_volumes[volumeIdx];
402 CRY_ASSERT(volume.id == id);
403 if(volume.id == id)
405 volume.bounds = bounds;
406 m_broadPhaseVolumes[volumeIdx].aabb = bounds.CalculateAABB();
412 void CSpatialIndex::SetVolumeTags(const SpatialVolumeId& id, ESpatialVolumeTags tags, bool bValue)
414 if(id != SpatialVolumeId::s_invalid)
416 const uint32 volumeCount = m_volumes.size();
417 const uint32 volumeIdx = VolumeIdxFromId(id);
418 CRY_ASSERT(volumeIdx < volumeCount);
419 if(volumeIdx < volumeCount)
421 SVolume& volume = m_volumes[volumeIdx];
422 CRY_ASSERT(volume.id == id);
423 if(volume.id == id)
425 if(bValue)
427 m_broadPhaseVolumes[volumeIdx].tags |= tags;
429 else
431 m_broadPhaseVolumes[volumeIdx].tags &= ~tags;
438 void CSpatialIndex::SetVolumeMonitorTags(const SpatialVolumeId& id, ESpatialVolumeTags tags, bool bValue)
440 if(id != SpatialVolumeId::s_invalid)
442 const uint32 volumeCount = m_volumes.size();
443 const uint32 volumeIdx = VolumeIdxFromId(id);
444 CRY_ASSERT(volumeIdx < volumeCount);
445 if(volumeIdx < volumeCount)
447 SVolume& volume = m_volumes[volumeIdx];
448 CRY_ASSERT(volume.id == id);
449 if(volume.id == id)
451 if(bValue)
453 volume.monitorTags |= tags;
455 else
457 volume.monitorTags &= ~tags;
464 SSpatialVolumeParams CSpatialIndex::GetVolumeParams(const SpatialVolumeId& id) const
466 if(id != SpatialVolumeId::s_invalid)
468 const uint32 volumeCount = m_volumes.size();
469 const uint32 volumeIdx = VolumeIdxFromId(id);
470 CRY_ASSERT(volumeIdx < volumeCount);
471 if(volumeIdx < volumeCount)
473 const SVolume& volume = m_volumes[volumeIdx];
474 CRY_ASSERT(volume.id == id);
475 if(volume.id == id)
477 SSpatialVolumeParams volumeParams;
478 volumeParams.bounds = volume.bounds;
479 volumeParams.tags = m_broadPhaseVolumes[volumeIdx].tags;
480 volumeParams.monitorTags = volume.monitorTags;
481 volumeParams.entityId = volume.entityId;
482 return volumeParams;
486 return SSpatialVolumeParams();
489 void CSpatialIndex::Query(const SpatialVolumeId& id, ESpatialVolumeTags tags, SpatialVolumeIds& results)
491 CRY_PROFILE_FUNCTION(PROFILE_GAME);
492 if(id != SpatialVolumeId::s_invalid)
494 const uint32 volumeCount = m_volumes.size();
495 const uint32 volumeIdx = VolumeIdxFromId(id);
496 CRY_ASSERT(volumeIdx < volumeCount);
497 if(volumeIdx < volumeCount)
499 SVolume& volume = m_volumes[volumeIdx];
500 CRY_ASSERT(volume.id == id);
501 if(volume.id == id)
503 // Perform broad phase query.
504 m_broadPhaseResults.clear();
505 BroadPhaseQuery(volumeIdx, tags, m_broadPhaseResults);
506 // Perform narrow phase tests.
507 for(uint32 otherVolumeIdx : m_broadPhaseResults)
509 SVolume& otherVolume = m_volumes[otherVolumeIdx];
510 if(volume.bounds.Overlap(otherVolume.bounds))
512 results.push_back(otherVolume.id);
515 // Sort results by index.
516 std::sort(results.begin(), results.end(), SCompareVolumeIds());
522 void CSpatialIndex::Update()
524 CRY_PROFILE_FUNCTION(PROFILE_GAME);
525 UpdateMonitoredVolumes();
526 ProcessEventQueue(m_eventQueue);
527 /*if(EnvCVars::sc_DebugDrawDetectionVolumes == 1)
529 DebugDraw();
533 ESpatialVolumeTags CSpatialIndex::CreateTag(const SSpatialVolumeTagName& tagName) const
535 const int tagIdx = m_pSettings->tagNames.find(tagName.value.c_str());
536 if(tagIdx != Serialization::StringList::npos)
538 return static_cast<ESpatialVolumeTags>(m_pSettings->tagValues[tagIdx]);
540 return ESpatialVolumeTags::None;
543 ESpatialVolumeTags CSpatialIndex::CreateTags(const SpatialVolumeTagNames& tagNames) const
545 ESpatialVolumeTags result = ESpatialVolumeTags::None;
546 for(const SSpatialVolumeTagName& tagName : tagNames)
548 result |= CreateTag(tagName);
550 return result;
553 const Serialization::StringList& CSpatialIndex::GetTagNames() const
555 return m_pSettings->tagNames;
558 const SpatialVolumeTagValues& CSpatialIndex::GetTagValues() const
560 return m_pSettings->tagValues;
563 SpatialVolumeId CSpatialIndex::GenerateVolumeId(uint32 volumeIdx)
565 const uint32 salt = m_salt ++;
566 const SpatialVolumeId volumeId((volumeIdx & s_volumeIdxMask) | ((salt << s_volumeSaltShift) & s_volumeSaltMask));
567 CRY_ASSERT(VolumeIdxFromId(volumeId) == volumeIdx);
568 return volumeId;
571 uint32 CSpatialIndex::VolumeIdxFromId(const SpatialVolumeId& id) const
573 return id.GetValue() & s_volumeIdxMask;
576 inline bool CSpatialIndex::CompareVolumeIds(const SpatialVolumeId& lhs, const SpatialVolumeId& rhs) const
578 return (lhs.GetValue() & s_volumeIdxMask) < (rhs.GetValue() & s_volumeIdxMask);
581 void CSpatialIndex::BroadPhaseQuery(uint32 volumeIdx, ESpatialVolumeTags tags, VolumeIdxs& results) const
583 const SBroadPhaseVolume broadPhaseVolume = m_broadPhaseVolumes[volumeIdx];
584 for(uint32 otherVolumeIdx = 0, volumeCount = m_volumes.size(); otherVolumeIdx < volumeCount; ++ otherVolumeIdx)
586 if(otherVolumeIdx != volumeIdx)
588 const SBroadPhaseVolume& otherBroadPhaseVolume = m_broadPhaseVolumes[otherVolumeIdx];
589 if((tags & otherBroadPhaseVolume.tags) != 0)
591 if(Overlap::AABB_AABB(broadPhaseVolume.aabb, otherBroadPhaseVolume.aabb))
593 results.push_back(otherVolumeIdx);
600 void CSpatialIndex::UpdateMonitoredVolumes()
602 CRY_PROFILE_FUNCTION(PROFILE_GAME);
603 // Update monitored volumes.
604 for(SVolume& volume : m_volumes)
606 // Query for overlapping volumes?
607 m_monitorResults.clear();
608 if(volume.monitorTags != ESpatialVolumeTags::None)
610 Query(volume.id, volume.monitorTags, m_monitorResults);
612 // Check for volumes leaving monitored volume.
613 for(uint32 cacheVolumeIdx = 0, cacheVolumeCount = volume.monitorCache.size(); cacheVolumeIdx < cacheVolumeCount; )
615 const SpatialVolumeId cacheVolumeId = volume.monitorCache[cacheVolumeIdx];
618 SpatialVolumeIds::iterator itLowerBoundVolume = std::lower_bound(m_monitorResults.begin(), m_monitorResults.end(), cacheVolumeId, SCompareVolumeIds());
620 if((itLowerBoundVolume == m_monitorResults.end()) || (*itLowerBoundVolume != cacheVolumeId))
622 SSpatialIndexEvent event;
623 event.id = ESpatialIndexEventId::Leaving;
624 event.volumeIds[0] = volume.id;
625 event.volumeIds[1] = cacheVolumeId;
626 m_eventQueue.push_back(event);
628 volume.monitorCache.erase(volume.monitorCache.begin() + cacheVolumeIdx);
629 -- cacheVolumeCount;
631 else
633 ++ cacheVolumeIdx;
636 // Check for volumes entering monitored volume.
637 for(uint32 newVolumeIdx = 0, newVolumeCount = m_monitorResults.size(); newVolumeIdx < newVolumeCount; ++ newVolumeIdx)
639 const SpatialVolumeId newVolumeId = m_monitorResults[newVolumeIdx];
640 SpatialVolumeIds::iterator itLowerBoundVolume = std::lower_bound(volume.monitorCache.begin(), volume.monitorCache.end(), newVolumeId);
641 if((itLowerBoundVolume == volume.monitorCache.end()) || (*itLowerBoundVolume != newVolumeId))
643 volume.monitorCache.insert(itLowerBoundVolume, newVolumeId);
645 SSpatialIndexEvent event;
646 event.id = ESpatialIndexEventId::Entering;
647 event.volumeIds[0] = volume.id;
648 event.volumeIds[1] = newVolumeId;
649 m_eventQueue.push_back(event);
655 void CSpatialIndex::ProcessEventQueue(EventQueue& eventQueue) const
657 CRY_PROFILE_FUNCTION(PROFILE_GAME);
658 // Send out all events in queue. N.B. This is performed after the update logic in order to reduce cache misses.
659 for(const SSpatialIndexEvent& event : eventQueue)
661 const SVolume& volume = m_volumes[VolumeIdxFromId(event.volumeIds[0])];
662 if(volume.eventCallback)
664 volume.eventCallback(event);
667 eventQueue.clear();
670 void CSpatialIndex::DebugDraw() const
672 IRenderAuxGeom& renderAuxGeom = *gEnv->pRenderer->GetIRenderAuxGeom();
673 for(const SVolume& volume : m_volumes)
675 ColorB color = ColorB(0, 150, 0);
676 if(!volume.monitorCache.empty())
678 color = ColorB(150, 0, 0);
680 else if(volume.monitorTags != ESpatialVolumeTags::None)
682 color = ColorB(150, 150, 0);
684 switch(volume.bounds.GetShape())
686 case ESpatialVolumeShape::Box:
688 renderAuxGeom.DrawOBB(volume.bounds.AsBox(), Matrix34(IDENTITY), false, color, eBBD_Faceted);
689 break;
691 case ESpatialVolumeShape::Sphere:
693 SAuxGeomRenderFlags prevRenderFlags = renderAuxGeom.GetRenderFlags();
694 renderAuxGeom.SetRenderFlags(e_Def3DPublicRenderflags | e_AlphaBlended);
695 renderAuxGeom.DrawSphere(volume.bounds.AsSphere().center, volume.bounds.AsSphere().radius, ColorB(color.r, color.g, color.b, 64), false);
696 renderAuxGeom.SetRenderFlags(prevRenderFlags);
697 break;
699 case ESpatialVolumeShape::Point:
701 renderAuxGeom.DrawSphere(volume.bounds.AsPoint(), 0.1f, color, false);
702 break;