!I (1670414, 1670415, 1670416, 1670424, 1670431):
[CRYENGINE.git] / Code / Sandbox / EditorQt / Objects / AreaBox.cpp
blob73868fd16ef6bd21db87159697ec6f3bc26df533
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "StdAfx.h"
4 #include "AreaBox.h"
5 #include "Viewport.h"
6 #include <Preferences/ViewportPreferences.h>
7 #include "Controls/PropertiesPanel.h"
8 #include "Objects/ObjectLoader.h"
9 #include "Objects/InspectorWidgetCreator.h"
10 #include <CryEntitySystem/IEntity.h>
12 #include <Serialization/Decorators/EntityLink.h>
13 #include <Serialization/Decorators/EditorActionButton.h>
14 #include <Serialization/Decorators/EditToolButton.h>
16 #include "PickObjectTool.h"
18 REGISTER_CLASS_DESC(CAreaBoxClassDesc);
20 class AreaTargetTool : public CPickObjectTool
22 DECLARE_DYNCREATE(AreaTargetTool)
24 struct SAreaTargetPicker : IPickObjectCallback
26 SAreaTargetPicker()
30 void OnPick(CBaseObject* pObj) override
32 if (m_owner)
34 CUndo undo("Add target link");
35 m_owner->AddEntity(pObj);
39 void OnCancelPick() override
43 CAreaObjectBase* m_owner;
46 public:
47 AreaTargetTool()
48 : CPickObjectTool(&m_picker)
52 virtual void SetUserData(const char* key, void* userData) override
54 m_picker.m_owner = static_cast<CAreaObjectBase*>(userData);
57 private:
58 SAreaTargetPicker m_picker;
61 IMPLEMENT_DYNCREATE(AreaTargetTool, CPickObjectTool)
63 //////////////////////////////////////////////////////////////////////////
64 CAreaObjectBase::CAreaObjectBase()
66 m_entities.RegisterEventCallback(functor(*this, &CAreaObjectBase::OnObjectEvent));
69 //////////////////////////////////////////////////////////////////////////
70 CAreaObjectBase::~CAreaObjectBase()
72 m_entities.UnregisterEventCallback(functor(*this, &CAreaObjectBase::OnObjectEvent));
75 //////////////////////////////////////////////////////////////////////////
76 void CAreaObjectBase::SerializeEntityTargets(Serialization::IArchive& ar, bool bMultiEdit)
78 std::vector<Serialization::EntityTarget> links;
79 for (size_t i = 0; i < m_entities.GetCount(); i++)
81 links.emplace_back(m_entities.Get(i)->GetId(), (LPCTSTR)m_entities.Get(i)->GetName());
84 ar(links, "attached_entities", "!Attached Entities");
85 if (ar.isInput())
87 size_t linkCount = links.size();
88 bool linksChanged = m_entities.GetCount() != linkCount;
90 if (!linksChanged)
92 for (size_t i = 0; i < m_entities.GetCount(); i++)
94 if (m_entities.Get(i)->GetId() != links[i].guid)
96 linksChanged = true;
101 if (linksChanged)
103 while (!m_entities.IsEmpty())
105 RemoveEntity(0);
108 for (size_t i = 0; i < links.size(); i++)
110 CBaseObject* obj = GetIEditorImpl()->GetObjectManager()->FindObject(links[i].guid);
111 if (obj)
113 AddEntity(obj);
119 if (ar.isEdit())
121 if (ar.openBlock("linktools", "Target Tools"))
123 Serialization::SEditToolButton pickToolButton("");
124 pickToolButton.SetToolClass(RUNTIME_CLASS(AreaTargetTool), nullptr, this);
126 ar(pickToolButton, "picker", "^Pick");
127 ar(Serialization::ActionButton([ = ] {
128 CUndo undo("Clear entity links");
129 while (!m_entities.IsEmpty())
131 RemoveEntity(0);
133 }), "picker", "^Clear");
135 ar.closeBlock();
140 void CAreaObjectBase::CreateInspectorWidgets(CInspectorWidgetCreator& creator)
142 CEntityObject::CreateInspectorWidgets(creator);
144 creator.AddPropertyTree<CAreaObjectBase>("Area", &CAreaObjectBase::SerializeEntityTargets);
147 //////////////////////////////////////////////////////////////////////////
148 void CAreaObjectBase::PostClone(CBaseObject* pFromObject, CObjectCloneContext& ctx)
150 __super::PostClone(pFromObject, ctx);
152 CAreaObjectBase const* const pFrom = static_cast<CAreaObjectBase*>(pFromObject);
153 // Clone event targets.
154 if (!pFrom->m_entities.IsEmpty())
156 size_t const numEntities = pFrom->m_entities.GetCount();
158 for (size_t i = 0; i < numEntities; i++)
160 CBaseObject* const __restrict pBaseObject = pFrom->m_entities.Get(i);
161 CBaseObject* const __restrict pClonedBaseObject = ctx.FindClone(pBaseObject);
163 if (pClonedBaseObject != nullptr)
165 m_entities.Add(pClonedBaseObject);
167 else
169 m_entities.Add(pBaseObject);
175 //////////////////////////////////////////////////////////////////////////
176 void CAreaObjectBase::OnObjectEvent(CBaseObject* const pBaseObject, int const event)
178 if (event == OBJECT_ON_PREDELETE)
180 if (pBaseObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
182 CEntityObject const* const pEntityObject = static_cast<CEntityObject*>(pBaseObject);
184 if (pEntityObject != nullptr && pEntityObject->GetIEntity() != nullptr)
186 OnEntityRemoved(pEntityObject->GetIEntity());
192 //////////////////////////////////////////////////////////////////////////
193 void CAreaObjectBase::AddEntity(CBaseObject* const pBaseObject)
195 CRY_ASSERT(pBaseObject != nullptr);
196 StoreUndo("Add Entity");
197 m_entities.Add(pBaseObject);
199 if (pBaseObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
201 OnEntityAdded(static_cast<CEntityObject*>(pBaseObject)->GetIEntity());
204 UpdateUIVars();
207 //////////////////////////////////////////////////////////////////////////
208 void CAreaObjectBase::RemoveEntity(size_t const index)
210 CRY_ASSERT(index >= 0 && index < m_entities.GetCount());
212 if (index < m_entities.GetCount())
214 StoreUndo("Remove Entity");
215 CBaseObject* const pBaseObject = m_entities.Get(index);
216 CEntityObject const* const pEntity = GetEntity(index);
218 if (pEntity != nullptr)
220 OnEntityRemoved(pEntity->GetIEntity());
223 m_entities.Remove(pBaseObject);
225 UpdateUIVars();
229 //////////////////////////////////////////////////////////////////////////
230 CEntityObject* CAreaObjectBase::GetEntity(size_t const index)
232 CRY_ASSERT(index >= 0 && index < m_entities.GetCount());
233 CBaseObject* const pBaseObject = m_entities.Get(index);
235 if (pBaseObject != nullptr && pBaseObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
237 return static_cast<CEntityObject*>(pBaseObject);
240 return nullptr;
243 //////////////////////////////////////////////////////////////////////////
244 void CAreaObjectBase::Serialize(CObjectArchive& ar)
246 __super::Serialize(ar);
248 XmlNodeRef xmlNode = ar.node;
249 if (ar.bLoading)
251 m_entities.Clear();
252 XmlNodeRef const entities = xmlNode->findChild("Entities");
254 if (entities)
256 int const numChildren = entities->getChildCount();
258 for (int i = 0; i < numChildren; ++i)
260 XmlNodeRef const ent = entities->getChild(i);
261 CryGUID entityId;
263 if (ent->getAttr("Id", entityId))
265 ar.SetResolveCallback(this, entityId, functor(m_entities, &CSafeObjectsArray::Add));
270 else
272 if (!m_entities.IsEmpty())
274 XmlNodeRef const nodes = xmlNode->newChild("Entities");
275 size_t const numEntities = m_entities.GetCount();
277 for (size_t i = 0; i < numEntities; i++)
279 XmlNodeRef entNode = nodes->newChild("Entity");
281 if (m_entities.Get(i) != nullptr)
283 entNode->setAttr("Id", m_entities.Get(i)->GetId());
290 //////////////////////////////////////////////////////////////////////////
291 void CAreaObjectBase::DrawEntityLinks(DisplayContext& dc)
293 if (!m_entities.IsEmpty())
295 Vec3 const vcol = Vec3(1.0f, 1.0f, 1.0f);
296 size_t const numEntities = m_entities.GetCount();
298 for (size_t i = 0; i < numEntities; ++i)
300 CBaseObject const* const pBaseObject = GetEntity(i);
302 if (pBaseObject != nullptr)
304 dc.DrawLine(GetWorldPos(), pBaseObject->GetWorldPos(), ColorF(vcol.x, vcol.y, vcol.z, 0.7f), ColorF(1, 1, 1, 0.7f));
310 //////////////////////////////////////////////////////////////////////////
311 // CBase implementation.
312 //////////////////////////////////////////////////////////////////////////
313 IMPLEMENT_DYNCREATE(CAreaBox, CEntityObject)
315 //////////////////////////////////////////////////////////////////////////
316 CAreaBox::CAreaBox()
317 : m_innerFadeDistance(0.0f)
319 m_areaId = -1;
320 m_edgeWidth = 0;
321 mv_width = 4;
322 mv_length = 4;
323 mv_height = 1;
324 mv_groupId = 0;
325 mv_priority = 0;
326 mv_filled = false;
327 mv_displaySoundInfo = false;
328 m_entityClass = "AreaBox";
330 // Resize for 6 sides
331 m_abObstructSound.resize(6);
333 m_box.min = Vec3(-mv_width / 2, -mv_length / 2, 0);
334 m_box.max = Vec3(mv_width / 2, mv_length / 2, mv_height);
336 m_bIgnoreGameUpdate = 1;
338 m_pOwnSoundVarBlock = new CVarBlock;
341 //////////////////////////////////////////////////////////////////////////
342 void CAreaBox::InitVariables()
344 if (m_pVarObject == nullptr)
346 m_pVarObject = stl::make_unique<CVarObject>();
349 m_pVarObject->AddVariable(m_innerFadeDistance, "InnerFadeDistance", functor(*this, &CAreaBox::OnAreaChange));
350 m_pVarObject->AddVariable(m_areaId, "AreaId", functor(*this, &CAreaBox::OnAreaChange));
351 m_pVarObject->AddVariable(m_edgeWidth, "FadeInZone", functor(*this, &CAreaBox::OnSizeChange));
352 m_pVarObject->AddVariable(mv_width, "Width", functor(*this, &CAreaBox::OnSizeChange));
353 m_pVarObject->AddVariable(mv_length, "Length", functor(*this, &CAreaBox::OnSizeChange));
354 m_pVarObject->AddVariable(mv_height, "Height", functor(*this, &CAreaBox::OnSizeChange));
355 m_pVarObject->AddVariable(mv_groupId, "GroupId", functor(*this, &CAreaBox::OnAreaChange));
356 m_pVarObject->AddVariable(mv_priority, "Priority", functor(*this, &CAreaBox::OnAreaChange));
357 m_pVarObject->AddVariable(mv_filled, "DisplayFilled");
358 m_pVarObject->AddVariable(mv_displaySoundInfo, "DisplaySoundInfo", functor(*this, &CAreaBox::OnSoundParamsChange));
361 //////////////////////////////////////////////////////////////////////////
362 void CAreaBox::Done()
364 m_pOwnSoundVarBlock->DeleteAllVariables();
366 CEntityObject::Done();
369 //////////////////////////////////////////////////////////////////////////
370 bool CAreaBox::Init(CBaseObject* prev, const string& file)
372 m_bIgnoreGameUpdate = 1;
374 SetColor(RGB(0, 0, 255));
375 bool res = CEntityObject::Init(prev, file);
377 if (m_pEntity != nullptr)
379 m_pEntity->GetOrCreateComponent<IEntityAreaComponent>();
382 if (prev)
384 m_abObstructSound = ((CAreaBox*)prev)->m_abObstructSound;
385 CRY_ASSERT(m_abObstructSound.size() == 6);
388 m_bIgnoreGameUpdate = 0;
390 return res;
393 //////////////////////////////////////////////////////////////////////////
394 void CAreaBox::GetLocalBounds(AABB& box)
396 box = m_box;
399 //////////////////////////////////////////////////////////////////////////
400 bool CAreaBox::HitTest(HitContext& hc)
402 Vec3 p;
403 /*BBox box;
404 box = m_box;
405 box.Transform( GetWorldTM() );
406 float tr = hc.distanceTollerance/2;
407 box.min -= Vec3(tr,tr,tr);
408 box.max += Vec3(tr,tr,tr);
409 if (box.IsIntersectRay(hc.raySrc,hc.rayDir,p ))
411 hc.dist = Vec3(hc.raySrc - p).Length();
412 return true;
414 Matrix34 invertWTM = GetWorldTM();
415 Vec3 worldPos = invertWTM.GetTranslation();
416 invertWTM.Invert();
418 Vec3 xformedRaySrc = invertWTM.TransformPoint(hc.raySrc);
419 Vec3 xformedRayDir = invertWTM.TransformVector(hc.rayDir).GetNormalized();
421 float epsilonDist = max(.1f, hc.view->GetScreenScaleFactor(worldPos) * 0.01f);
422 epsilonDist *= max(0.0001f, min(invertWTM.GetColumn0().GetLength(), min(invertWTM.GetColumn1().GetLength(), invertWTM.GetColumn2().GetLength())));
423 float hitDist;
425 float tr = hc.distanceTolerance / 2 + 1;
426 AABB box;
427 box.min = m_box.min - Vec3(tr + epsilonDist, tr + epsilonDist, tr + epsilonDist);
428 box.max = m_box.max + Vec3(tr + epsilonDist, tr + epsilonDist, tr + epsilonDist);
430 if (Intersect::Ray_AABB(xformedRaySrc, xformedRayDir, box, p))
432 if (Intersect::Ray_AABBEdge(xformedRaySrc, xformedRayDir, m_box, epsilonDist, hitDist, p))
434 hc.dist = xformedRaySrc.GetDistance(p);
435 return true;
438 return false;
441 //////////////////////////////////////////////////////////////////////////
442 void CAreaBox::CreateInspectorWidgets(CInspectorWidgetCreator& creator)
444 CAreaObjectBase::CreateInspectorWidgets(creator);
446 creator.AddPropertyTree<CAreaBox>("Area Box", &CAreaBox::SerializeProperties);
449 void CAreaBox::SerializeProperties(Serialization::IArchive& ar, bool bMultiEdit)
451 m_pVarObject->SerializeVariable(&m_innerFadeDistance, ar);
452 m_pVarObject->SerializeVariable(&m_areaId, ar);
453 m_pVarObject->SerializeVariable(&m_edgeWidth, ar);
454 m_pVarObject->SerializeVariable(&mv_width, ar);
455 m_pVarObject->SerializeVariable(&mv_length, ar);
456 m_pVarObject->SerializeVariable(&mv_height, ar);
457 m_pVarObject->SerializeVariable(&mv_groupId, ar);
458 m_pVarObject->SerializeVariable(&mv_priority, ar);
459 m_pVarObject->SerializeVariable(&mv_filled, ar);
460 m_pVarObject->SerializeVariable(&mv_displaySoundInfo, ar);
462 if (!m_abObstructSound.empty() && !m_bIgnoreGameUpdate && mv_displaySoundInfo && !bMultiEdit)
464 if (!ar.isInput() || m_pOwnSoundVarBlock->IsEmpty())
466 if (!m_pOwnSoundVarBlock->IsEmpty())
467 m_pOwnSoundVarBlock->DeleteAllVariables();
468 // If the shape hasn't got a height subtract 2 for roof and floor
469 CRY_ASSERT(m_abObstructSound.size() == 6);
470 size_t const nVarCount = m_abObstructSound.size() - ((mv_height > 0.0f) ? 0 : 2);
471 for (size_t i = 0; i < nVarCount; ++i)
473 CVariable<bool>* const pvTemp = new CVariable<bool>;
474 pvTemp->Set(m_abObstructSound[i]);
475 pvTemp->AddOnSetCallback(functor(*this, &CAreaBox::OnPointChange));
477 string cTemp;
478 cTemp.Format("Side:%d", i + 1);
480 pvTemp->SetName(cTemp);
481 m_pOwnSoundVarBlock->AddVariable(pvTemp);
485 ar(*m_pOwnSoundVarBlock, "SoundObstruction", "|Sound Obstruction");
489 //////////////////////////////////////////////////////////////////////////
490 void CAreaBox::Reload(bool bReloadScript /* = false */)
492 __super::Reload(bReloadScript);
494 // During reloading the entity+proxies get completely removed.
495 // Hence we need to completely recreate the proxy and update all data to it.
496 UpdateGameArea();
499 //////////////////////////////////////////////////////////////////////////
500 void CAreaBox::OnAreaChange(IVariable* pVariable)
502 if (m_bIgnoreGameUpdate == 0 && m_pEntity != nullptr)
504 IEntityAreaComponent* pArea = m_pEntity->GetOrCreateComponent<IEntityAreaComponent>();
506 if (pArea)
508 pArea->SetID(m_areaId);
509 pArea->SetGroup(mv_groupId);
510 pArea->SetPriority(mv_priority);
511 pArea->SetInnerFadeDistance(m_innerFadeDistance);
516 //////////////////////////////////////////////////////////////////////////
517 void CAreaBox::OnSizeChange(IVariable* pVariable)
519 Vec3 size(0, 0, 0);
520 size.x = mv_width;
521 size.y = mv_length;
522 size.z = mv_height;
524 m_box.min = -size / 2;
525 m_box.max = size / 2;
526 // Make volume position bottom of bounding box.
527 m_box.min.z = 0;
528 m_box.max.z = size.z;
530 InvalidateWorldBox();
532 if (m_bIgnoreGameUpdate == 0 && m_pEntity != nullptr)
534 IEntityAreaComponent* pArea = m_pEntity->GetOrCreateComponent<IEntityAreaComponent>();
536 if (pArea)
538 pArea->SetBox(m_box.min, m_box.max, nullptr, 0);
539 pArea->SetProximity(m_edgeWidth);
544 //////////////////////////////////////////////////////////////////////////
545 void CAreaBox::Display(DisplayContext& dc)
547 if (!gViewportDebugPreferences.showAreaObjectHelper)
548 return;
550 if (!mv_displaySoundInfo)
552 COLORREF wireColor, solidColor;
553 float wireOffset = 0;
554 float alpha = 0.3f;
555 if (IsSelected())
557 wireColor = dc.GetSelectedColor();
558 solidColor = GetColor();
559 wireOffset = -0.1f;
561 else
563 wireColor = GetColor();
564 solidColor = GetColor();
567 //dc.renderer->SetCullMode( R_CULL_DISABLE );
569 dc.PushMatrix(GetWorldTM());
570 AABB box = m_box;
572 bool bFrozen = IsFrozen();
574 if (bFrozen)
575 dc.SetFreezeColor();
576 //////////////////////////////////////////////////////////////////////////
577 if (!bFrozen)
578 dc.SetColor(solidColor, alpha);
580 if (IsSelected())
582 dc.DepthWriteOff();
583 dc.DrawSolidBox(box.min, box.max);
584 dc.DepthWriteOn();
587 if (!bFrozen)
588 dc.SetColor(wireColor, 1);
590 if (IsSelected())
592 dc.SetLineWidth(3.0f);
593 dc.DrawWireBox(box.min, box.max);
594 dc.SetLineWidth(0);
596 else
597 dc.DrawWireBox(box.min, box.max);
598 if (m_edgeWidth)
600 float fFadeScale = m_edgeWidth / 200.0f;
601 AABB InnerBox = box;
602 Vec3 EdgeDist = InnerBox.max - InnerBox.min;
603 InnerBox.min.x += EdgeDist.x * fFadeScale;
604 InnerBox.max.x -= EdgeDist.x * fFadeScale;
605 InnerBox.min.y += EdgeDist.y * fFadeScale;
606 InnerBox.max.y -= EdgeDist.y * fFadeScale;
607 InnerBox.min.z += EdgeDist.z * fFadeScale;
608 InnerBox.max.z -= EdgeDist.z * fFadeScale;
609 dc.DrawWireBox(InnerBox.min, InnerBox.max);
611 if (mv_filled)
613 dc.SetAlpha(0.2f);
614 dc.DrawSolidBox(box.min, box.max);
616 //////////////////////////////////////////////////////////////////////////
618 dc.PopMatrix();
620 DrawEntityLinks(dc);
621 DrawDefault(dc);
623 else
625 ColorB const oObstructionFilled(255, 0, 0, 120);
626 ColorB const oObstructionFilledNotSelected(255, 0, 0, 30);
627 ColorB const oObstructionNotFilled(255, 0, 0, 10);
628 ColorB const oNoObstructionFilled(0, 255, 0, 120);
629 ColorB const oNoObstructionFilledNotSelected(0, 255, 0, 30);
630 ColorB const oNoObstructionNotFilled(0, 255, 0, 10);
632 COLORREF oWireColor;
633 if (IsSelected())
634 oWireColor = dc.GetSelectedColor();
635 else
636 oWireColor = GetColor();
638 dc.SetColor(oWireColor);
640 dc.PushMatrix(GetWorldTM());
642 if (mv_height > 0.0f)
643 dc.DrawWireBox(m_box.min, m_box.max);
645 Vec3 const v3BoxMin = m_box.min;
646 Vec3 const v3BoxMax = m_box.max;
648 // Now build the 6 segments
649 float const fLength = v3BoxMax.x - v3BoxMin.x;
650 float const fWidth = v3BoxMax.y - v3BoxMin.y;
651 float const fHeight = v3BoxMax.z - v3BoxMin.z;
653 Vec3 const v0(v3BoxMin);
654 Vec3 const v1(Vec3(v3BoxMin.x + fLength, v3BoxMin.y, v3BoxMin.z));
655 Vec3 const v2(Vec3(v3BoxMin.x + fLength, v3BoxMin.y + fWidth, v3BoxMin.z));
656 Vec3 const v3(Vec3(v3BoxMin.x, v3BoxMin.y + fWidth, v3BoxMin.z));
657 Vec3 const v4(Vec3(v3BoxMin.x, v3BoxMin.y, v3BoxMin.z + fHeight));
658 Vec3 const v5(Vec3(v3BoxMin.x + fLength, v3BoxMin.y, v3BoxMin.z + fHeight));
659 Vec3 const v6(Vec3(v3BoxMin.x + fLength, v3BoxMin.y + fWidth, v3BoxMin.z + fHeight));
660 Vec3 const v7(Vec3(v3BoxMin.x, v3BoxMin.y + fWidth, v3BoxMin.z + fHeight));
662 // Describe the box faces
663 SBoxFace const aoBoxFaces[6] =
665 SBoxFace(&v0, &v4, &v5, &v1),
666 SBoxFace(&v1, &v5, &v6, &v2),
667 SBoxFace(&v2, &v6, &v7, &v3),
668 SBoxFace(&v3, &v7, &v4, &v0),
669 SBoxFace(&v4, &v5, &v6, &v7),
670 SBoxFace(&v0, &v1, &v2, &v3)
673 // Draw each side
674 unsigned int const nSideCount = ((mv_height > 0.0f) ? 6 : 4);
675 for (unsigned int i = 0; i < nSideCount; ++i)
677 if (mv_height == 0.0f)
679 if (IsSelected())
681 if (m_abObstructSound[i])
682 dc.SetColor(oObstructionFilled);
683 else
684 dc.SetColor(oNoObstructionFilled);
686 else
687 dc.SetColor(oWireColor);
689 dc.DrawLine(*(aoBoxFaces[i].pP1), *(aoBoxFaces[i].pP4));
692 if (mv_height > 0.0f)
694 // Manipulate the color so it looks a little thicker and redder
695 if (mv_filled || gViewportPreferences.fillSelectedShapes)
697 ColorB const nColor = dc.GetColor();
699 if (m_abObstructSound[i])
700 dc.SetColor(IsSelected() ? oObstructionFilled : oObstructionFilledNotSelected);
701 else
702 dc.SetColor(IsSelected() ? oNoObstructionFilled : oNoObstructionFilledNotSelected);
704 dc.CullOff();
705 dc.DepthWriteOff();
707 dc.DrawQuad(*(aoBoxFaces[i].pP1), *(aoBoxFaces[i].pP2), *(aoBoxFaces[i].pP3), *(aoBoxFaces[i].pP4));
709 dc.DepthWriteOn();
710 dc.CullOn();
711 dc.SetColor(nColor);
713 else if (IsSelected())
715 ColorB const nColor = dc.GetColor();
717 if (m_abObstructSound[i])
718 dc.SetColor(oObstructionNotFilled);
719 else
720 dc.SetColor(oNoObstructionNotFilled);
722 dc.CullOff();
723 dc.DepthWriteOff();
725 dc.DrawQuad(*(aoBoxFaces[i].pP1), *(aoBoxFaces[i].pP2), *(aoBoxFaces[i].pP3), *(aoBoxFaces[i].pP4));
727 dc.DepthWriteOn();
728 dc.CullOn();
729 dc.SetColor(nColor);
734 //////////////////////////////////////////////////////////////////////////
735 dc.PopMatrix();
737 DrawEntityLinks(dc);
738 DrawDefault(dc);
742 //////////////////////////////////////////////////////////////////////////
743 void CAreaBox::InvalidateTM(int nWhyFlags)
745 CEntityObject::InvalidateTM(nWhyFlags);
748 //////////////////////////////////////////////////////////////////////////
749 void CAreaBox::Serialize(CObjectArchive& ar)
751 m_bIgnoreGameUpdate = 1;
752 __super::Serialize(ar);
753 m_bIgnoreGameUpdate = 0;
754 CVarBlockPtr soundVarBlock = new CVarBlock;
756 XmlNodeRef xmlNode = ar.node;
757 if (ar.bLoading)
759 AABB box;
760 xmlNode->getAttr("BoxMin", box.min);
761 xmlNode->getAttr("BoxMax", box.max);
763 SetAreaId(m_areaId);
764 SetBox(box);
766 else
768 // Saving.
769 // xmlNode->setAttr( "AreaId",m_areaId );
770 xmlNode->setAttr("BoxMin", m_box.min);
771 xmlNode->setAttr("BoxMax", m_box.max);
774 if (ar.bLoading)
776 for (size_t i = 0; i < ((mv_height > 0.0f) ? 6 : 4); ++i)
778 CVariable<bool>* const pvTemp = new CVariable<bool>;
780 stack_string cTemp;
781 cTemp.Format("Side%d", i + 1);
783 // And add each to the block
784 soundVarBlock->AddVariable(pvTemp, cTemp);
787 // Now read in the data
788 XmlNodeRef pSoundDataNode = xmlNode->findChild("SoundData");
789 if (pSoundDataNode)
791 // Make sure we stay backwards compatible and also find the old "Side:" formatting
792 // This can go out once every level got saved with the new formatting
793 char const* const pcTemp = pSoundDataNode->getAttr("Side:1");
794 if (pcTemp && pcTemp[0])
796 // We have the old formatting
797 soundVarBlock->DeleteAllVariables();
799 for (size_t i = 0; i < ((mv_height > 0.0f) ? 6 : 4); ++i)
801 CVariable<bool>* const pvTempOld = new CVariable<bool>;
803 stack_string cTempOld;
804 cTempOld.Format("Side:%d", i + 1);
806 // And add each to the block
807 soundVarBlock->AddVariable(pvTempOld, cTempOld);
811 soundVarBlock->Serialize(pSoundDataNode, true);
814 // Copy the data to the "bools" list
815 // First clear the remains out
816 m_abObstructSound.clear();
818 unsigned int const nVarCount = soundVarBlock->GetNumVariables();
819 for (unsigned int i = 0; i < nVarCount; ++i)
821 IVariable const* const pTemp = soundVarBlock->GetVariable(i);
822 if (pTemp)
824 bool bTemp = false;
825 pTemp->Get(bTemp);
826 m_abObstructSound.push_back(bTemp);
828 if (i >= nVarCount - 2)
830 // Here check for backwards compatibility reasons if "ObstructRoof" and "ObstructFloor" still exists
831 bool bTemp = false;
832 if (i == nVarCount - 2)
834 if (xmlNode->getAttr("ObstructRoof", bTemp))
835 m_abObstructSound[i] = bTemp;
837 else
839 if (xmlNode->getAttr("ObstructFloor", bTemp))
840 m_abObstructSound[i] = bTemp;
846 // Since the var block is a static object clear it for the next object to be empty
847 soundVarBlock->DeleteAllVariables();
849 else
851 XmlNodeRef pSoundDataNode = xmlNode->newChild("SoundData");
852 if (pSoundDataNode)
854 // TODO: What if mv_height or/and mv_width or/and mv_length == 0 ??
855 // For now just serialize all data
856 // First clear the remains out
857 soundVarBlock->DeleteAllVariables();
859 // Create the variables
860 CRY_ASSERT(m_abObstructSound.size() == 6);
862 size_t nIndex = 0;
863 tSoundObstruction::const_iterator Iter(m_abObstructSound.begin());
864 tSoundObstruction::const_iterator const IterEnd(m_abObstructSound.end());
866 for (; Iter != IterEnd; ++Iter)
868 bool const bObstructed = (bool)(*Iter);
870 CVariable<bool>* const pvTemp = new CVariable<bool>;
871 pvTemp->Set(bObstructed);
872 stack_string cTemp;
873 cTemp.Format("Side%d", ++nIndex);
875 // And add each to the block
876 soundVarBlock->AddVariable(pvTemp, cTemp);
879 soundVarBlock->Serialize(pSoundDataNode, false);
880 soundVarBlock->DeleteAllVariables();
885 //////////////////////////////////////////////////////////////////////////
886 XmlNodeRef CAreaBox::Export(const string& levelPath, XmlNodeRef& xmlNode)
888 XmlNodeRef objNode = CEntityObject::Export(levelPath, xmlNode);
889 return objNode;
892 //////////////////////////////////////////////////////////////////////////
893 void CAreaBox::SetAreaId(int nAreaId)
895 m_areaId = nAreaId;
897 if (m_bIgnoreGameUpdate == 0 && m_pEntity != nullptr)
899 IEntityAreaComponent* pArea = m_pEntity->GetOrCreateComponent<IEntityAreaComponent>();
901 if (pArea)
903 pArea->SetID(m_areaId);
908 //////////////////////////////////////////////////////////////////////////
909 int CAreaBox::GetAreaId()
911 return m_areaId;
914 //////////////////////////////////////////////////////////////////////////
915 void CAreaBox::SetBox(AABB box)
917 m_box = box;
919 if (m_bIgnoreGameUpdate == 0 && m_pEntity != nullptr)
921 IEntityAreaComponent* pArea = m_pEntity->GetOrCreateComponent<IEntityAreaComponent>();
923 if (pArea)
925 pArea->SetBox(m_box.min, m_box.max, nullptr, 0);
930 //////////////////////////////////////////////////////////////////////////
931 AABB CAreaBox::GetBox()
933 return m_box;
936 //////////////////////////////////////////////////////////////////////////
937 void CAreaBox::PostLoad(CObjectArchive& ar)
939 __super::PostLoad(ar);
940 UpdateGameArea();
941 UpdateAttachedEntities();
944 //////////////////////////////////////////////////////////////////////////
945 bool CAreaBox::CreateGameObject()
947 bool const bSuccess = __super::CreateGameObject();
949 if (bSuccess)
951 UpdateGameArea();
954 return bSuccess;
957 //////////////////////////////////////////////////////////////////////////
958 void CAreaBox::OnEntityAdded(IEntity const* const pIEntity)
960 if (m_bIgnoreGameUpdate == 0 && m_pEntity != nullptr)
962 IEntityAreaComponent* pArea = m_pEntity->GetOrCreateComponent<IEntityAreaComponent>();
964 if (pArea)
966 pArea->AddEntity(pIEntity->GetId());
971 //////////////////////////////////////////////////////////////////////////
972 void CAreaBox::OnEntityRemoved(IEntity const* const pIEntity)
974 if (m_bIgnoreGameUpdate == 0 && m_pEntity != nullptr)
976 IEntityAreaComponent* pArea = m_pEntity->GetOrCreateComponent<IEntityAreaComponent>();
978 if (pArea)
980 pArea->RemoveEntity(pIEntity->GetId());
985 //////////////////////////////////////////////////////////////////////////
986 void CAreaBox::UpdateGameArea()
988 if (m_bIgnoreGameUpdate == 0 && m_pEntity != nullptr)
990 IEntityAreaComponent* pArea = m_pEntity->GetOrCreateComponent<IEntityAreaComponent>();
992 if (pArea)
994 bool abObstructSound[6] = { false };
996 size_t nIndex = 0;
997 tSoundObstruction::const_iterator Iter(m_abObstructSound.begin());
998 tSoundObstruction::const_iterator const IterEnd(m_abObstructSound.end());
1000 for (; Iter != IterEnd; ++Iter)
1002 // Here we "unpack" the data! (1 bit*nPointsCount to 1 byte*nPointsCount)
1003 bool const bObstructed = (bool)(*Iter);
1004 abObstructSound[nIndex] = bObstructed;
1005 ++nIndex;
1008 UpdateSoundPanelParams();
1009 pArea->SetBox(m_box.min, m_box.max, &abObstructSound[0], 6);
1010 pArea->SetProximity(m_edgeWidth);
1011 pArea->SetID(m_areaId);
1012 pArea->SetGroup(mv_groupId);
1013 pArea->SetPriority(mv_priority);
1014 pArea->SetInnerFadeDistance(m_innerFadeDistance);
1019 //////////////////////////////////////////////////////////////////////////
1020 void CAreaBox::UpdateAttachedEntities()
1022 if (m_bIgnoreGameUpdate == 0 && m_pEntity != nullptr)
1024 IEntityAreaComponent* pArea = m_pEntity->GetOrCreateComponent<IEntityAreaComponent>();
1026 if (pArea)
1028 pArea->RemoveEntities();
1029 size_t const numEntities = GetEntityCount();
1031 for (size_t i = 0; i < numEntities; ++i)
1033 CEntityObject* const pEntity = GetEntity(i);
1035 if (pEntity != nullptr && pEntity->GetIEntity() != nullptr)
1037 pArea->AddEntity(pEntity->GetIEntity()->GetId());
1044 //////////////////////////////////////////////////////////////////////////
1045 void CAreaBox::OnSoundParamsChange(IVariable* var)
1047 if (!m_bIgnoreGameUpdate && mv_displaySoundInfo)
1049 // Refresh inspector
1050 UpdateUIVars();
1054 //////////////////////////////////////////////////////////////////////////
1055 void CAreaBox::OnPointChange(IVariable* var)
1057 CRY_ASSERT(m_abObstructSound.size() == 6);
1058 size_t const numVariables = static_cast<size_t>(m_pOwnSoundVarBlock->GetNumVariables());
1060 for (size_t i = 0; i < numVariables; ++i)
1062 if (m_pOwnSoundVarBlock->GetVariable(i) == var)
1064 if (m_pEntity != nullptr)
1066 IEntityAreaComponent* const pArea = m_pEntity->GetOrCreateComponent<IEntityAreaComponent>();
1068 if (pArea)
1070 bool bValue = false;
1071 var->Get(bValue);
1072 pArea->SetSoundObstructionOnAreaFace(i, bValue);
1073 m_abObstructSound[i] = bValue;
1077 break;
1082 //////////////////////////////////////////////////////////////////////////
1083 void CAreaBox::UpdateSoundPanelParams()
1085 if (!m_bIgnoreGameUpdate && mv_displaySoundInfo)
1087 UpdateUIVars();