//////////////////////////////////////////////////////////////////////////// // // Crytek Engine Source File. // Copyright (C), Crytek Studios, 2001. // ------------------------------------------------------------------------- // File name: Group.cpp // Version: v1.00 // Created: 10/10/2001 by Timur. // Compilers: Visual C++ 6.0 // Description: CGroup implementation. // ------------------------------------------------------------------------- // History: // //////////////////////////////////////////////////////////////////////////// #include "StdAfx.h" #include "Group.h" #include "ObjectManager.h" #include "..\Viewport.h" #include "..\DisplaySettings.h" int CGroup::s_groupGeomMergeId = 0; ////////////////////////////////////////////////////////////////////////// // CBase implementation. ////////////////////////////////////////////////////////////////////////// IMPLEMENT_DYNCREATE(CGroup,CBaseObject) /* ////////////////////////////////////////////////////////////////////////// // class CUndoGroupChildsObject : public IUndoObject { public: CUndoGroupChildsObject( CGroup *grp,const char *undoDescription ) { // Stores the current state of this object. assert( grp != 0 ); m_undoDescription = undoDescription; m_objectId = grp->GetId(); for (int i = 0; i < grp->GetChildCount(); i++) m_undo.push_back( grp->GetChild(i)->GetId() ); } protected: virtual void Release() { delete this; }; virtual int GetSize() { return sizeof(*this) + m_undo.size()*sizeof(int)+ m_redo.size()*sizeof(int) + m_undoDescription.GetLength(); } virtual const char* GetDescription() { return m_undoDescription; }; virtual void Undo( bool bUndo ) { CGroup *grp = (CGroup*)GetIEditor()->GetObjectManager()->FindObject( m_objectId ); if (!grp || grp->GetType() != OBJTYPE_GROUP) return; GetIEditor()->SuspendUndo(); if (bUndo) { m_redo.clear(); // Save current object state. for (int i = 0; i < grp->GetChildCount(); i++) m_redo.push_back( grp->GetChild(i)->GetId() ); } grp->DetachAll(); // Undo object state. for (int i = 0; i < m_undo.size(); i++) { CBaseObject *obj = GetIEditor()->GetObjectManager()->FindObject( m_undo[i] ); if (obj) grp->AddChild( obj ); } GetIEditor()->ResumeUndo(); } virtual void Redo() { CGroup *grp = (CGroup*)GetIEditor()->GetObjectManager()->FindObject( m_objectId ); if (!grp || grp->GetType() != OBJTYPE_GROUP) return; GetIEditor()->SuspendUndo(); grp->RemoveAllChilds(); // Redo object state. for (int i = 0; i < m_redo.size(); i++) { CBaseObject *obj = GetIEditor()->GetObjectManager()->FindObject( m_redo[i] ); if (obj) grp->AddChild( obj ); } GetIEditor()->ResumeUndo(); } private: CString m_undoDescription; int m_objectId; std::vector m_undo; std::vector m_redo; }; */ ////////////////////////////////////////////////////////////////////////// CGroup::CGroup() { m_opened = false; m_bAlwaysDrawBox = true; m_ignoreChildModify = false; m_bbox.min = m_bbox.max = Vec3(0,0,0); m_bBBoxValid = false; SetColor( RGB(0,255,0) ); // Green m_geomMergeId = ++s_groupGeomMergeId; AddVariable( mv_mergeStaticGeom,"MergeGeom",_T("Merge Static Geometry"),functor(*this,&CGroup::OnMergeStaticGeom) ); } inline void RecursivelyGetAllChilds( CBaseObject *obj,std::vector &childs ) { for (int i = 0; i < obj->GetChildCount(); i++) { CBaseObject *c = obj->GetChild(i); childs.push_back(c); RecursivelyGetAllChilds( c,childs ); } } ////////////////////////////////////////////////////////////////////////// void CGroup::Done() { DeleteAllChilds(); CBaseObject::Done(); } ////////////////////////////////////////////////////////////////////////// void CGroup::DeleteAllChilds() { std::vector childs; RecursivelyGetAllChilds( this,childs ); for (int i = 0; i < childs.size(); i++) { GetObjectManager()->DeleteObject(childs[i]); } } ////////////////////////////////////////////////////////////////////////// bool CGroup::Init( IEditor *ie,CBaseObject *prev,const CString &file ) { m_ie = ie; bool res = CBaseObject::Init( ie,prev,file ); if (prev) { InvalidateBBox(); } return res; } ////////////////////////////////////////////////////////////////////////// void CGroup::AttachChild( CBaseObject* child,bool bKeepPos ) { CBaseObject::AttachChild( child,bKeepPos ); // Set Group pointer of all non group child objects to this group. RecursivelySetGroup( child,this ); InvalidateBBox(); } ////////////////////////////////////////////////////////////////////////// void CGroup::RemoveChild( CBaseObject *child ) { // Set Group pointer of all non group child objects to parent group if present. RecursivelySetGroup( child,GetGroup() ); CBaseObject::RemoveChild( child ); InvalidateBBox(); } ////////////////////////////////////////////////////////////////////////// void CGroup::RecursivelySetGroup( CBaseObject *object,CGroup *pGroup ) { object->SetGroup(pGroup); if (object->GetType() != OBJTYPE_GROUP) { int numChilds = object->GetChildCount(); for (int i = 0; i < numChilds; i++) { RecursivelySetGroup( object->GetChild(i),pGroup ); } } else { if (pGroup) { CGroup *pChildGroup = (CGroup*)object; if (!pGroup->IsOpen()) pChildGroup->Close(); } } } ////////////////////////////////////////////////////////////////////////// void CGroup::RecursivelySetFlags( CBaseObject *object,int flags ) { object->SetFlags( flags ); int numChilds = object->GetChildCount(); for (int i = 0; i < numChilds; i++) { RecursivelySetFlags( object->GetChild(i),flags ); } } ////////////////////////////////////////////////////////////////////////// void CGroup::RecursivelySetLayer( CBaseObject *object,CObjectLayer *pLayer ) { object->SetLayer( pLayer ); int numChilds = object->GetChildCount(); for (int i = 0; i < numChilds; i++) { RecursivelySetLayer( object->GetChild(i),pLayer ); } } ////////////////////////////////////////////////////////////////////////// void CGroup::GetBoundBox( BBox &box ) { if (!m_bBBoxValid) CalcBoundBox(); box = m_bbox; box.Transform( GetWorldTM() ); } ////////////////////////////////////////////////////////////////////////// void CGroup::GetLocalBounds( BBox &box ) { if (!m_bBBoxValid) CalcBoundBox(); box = m_bbox; } ////////////////////////////////////////////////////////////////////////// bool CGroup::HitTest( HitContext &hc ) { bool selected = false; if (m_opened) { selected = HitTestChilds( hc ); } if (!selected) { Vec3 p; Matrix44 invertWTM = GetWorldTM(); Vec3 worldPos = invertWTM.GetTranslationOLD(); invertWTM.Invert44(); Vec3 xformedRaySrc = invertWTM.TransformPointOLD(hc.raySrc); Vec3 xformedRayDir = GetNormalized( invertWTM.TransformVectorOLD(hc.rayDir) ); float epsilonDist = hc.view->GetScreenScaleFactor( worldPos ) * 0.01f; float hitDist; float tr = hc.distanceTollerance/2 + 1; BBox box; box.min = m_bbox.min - Vec3(tr+epsilonDist,tr+epsilonDist,tr+epsilonDist); box.max = m_bbox.max + Vec3(tr+epsilonDist,tr+epsilonDist,tr+epsilonDist); if (box.IsIntersectRay( xformedRaySrc,xformedRayDir,p )) { if (m_bbox.RayEdgeIntersection( xformedRaySrc,xformedRayDir,epsilonDist,hitDist,p )) { hc.dist = GetDistance(xformedRaySrc,p); return true; } else { // Check if any childs of closed group selected. if (!m_opened) { if (HitTestChilds( hc )) { hc.object = this; return true; } } } } } return selected; } ////////////////////////////////////////////////////////////////////////// bool CGroup::HitTestChilds( HitContext &hcOrg ) { float mindist = FLT_MAX; //uint hideMask = GetIEditor()->GetDisplaySettings()->GetObjectHideMask(); HitContext hc; CBaseObject *selected = 0; int numChilds = GetChildCount(); for (int i = 0; i < numChilds; i++) { CBaseObject *obj = GetChild(i); if (obj->IsHidden()) continue; if (obj->IsFrozen()) continue; hc = hcOrg; if (obj->IsSelected()) { int axis = obj->HitTestAxis( hc ); if (axis != 0) { hcOrg.object = obj; hcOrg.axis = axis; hcOrg.dist = hc.dist; return true; } } if (obj->HitTest(hc)) { if (hc.dist < mindist) { mindist = hc.dist; // If collided object specified, accept it, overwise take tested object itself. if (hc.object) selected = hc.object; else selected = obj; hc.object = 0; } } } if (selected) { hcOrg.object = selected; hcOrg.dist = mindist; return true; } return false; } ////////////////////////////////////////////////////////////////////////// void CGroup::BeginEditParams( IEditor *ie,int flags ) { m_ie = ie; CBaseObject::BeginEditParams( ie,flags ); } ////////////////////////////////////////////////////////////////////////// void CGroup::EndEditParams( IEditor *ie ) { CBaseObject::EndEditParams( ie ); } void CGroup::SetPos( const Vec3d &pos ) { CBaseObject::SetPos(pos); } void CGroup::SetAngles( const Vec3d &angles ) { CBaseObject::SetAngles(angles); } void CGroup::SetScale( const Vec3d &scale ) { CBaseObject::SetScale(scale); } ////////////////////////////////////////////////////////////////////////// void CGroup::Display( DisplayContext &dc ) { bool hideNames = dc.flags & DISPLAY_HIDENAMES; DrawDefault(dc,GetColor()); if (!IsHighlighted()) { dc.PushMatrix( GetWorldTM() ); if (IsSelected()) { dc.SetSelectedColor(); dc.DrawWireBox( m_bbox.min,m_bbox.max ); } else { if (m_bAlwaysDrawBox) { if (IsFrozen()) dc.SetFreezeColor(); else dc.SetColor( GetColor() ); dc.DrawWireBox( m_bbox.min,m_bbox.max ); } } dc.PopMatrix(); } /* uint hideMask = GetIEditor()->GetDisplaySettings()->GetObjectHideMask(); CObjectManager *objMan = GetObjectManager(); int numChilds = GetChildCount(); for (int i = 0; i < numChilds; i++) { CBaseObject *obj = GetChild(i); if (!m_opened) { dc.flags |= DISPLAY_HIDENAMES; } // Check if object hidden. if (obj->IsHidden()) { obj->UpdateVisibility(false); continue; } obj->Display( dc ); obj->UpdateVisibility(true); } if (hideNames) dc.flags |= DISPLAY_HIDENAMES; else dc.flags &= ~DISPLAY_HIDENAMES; */ } ////////////////////////////////////////////////////////////////////////// void CGroup::Serialize( CObjectArchive &ar ) { CBaseObject::Serialize( ar ); if (ar.bLoading) { // Loading. ar.node->getAttr( "Opened",m_opened ); if (!ar.bUndo) SerializeChilds( ar ); } else { ar.node->setAttr( "Opened",m_opened ); if (!ar.bUndo) SerializeChilds( ar ); } } ////////////////////////////////////////////////////////////////////////// void CGroup::SerializeChilds( CObjectArchive &ar ) { XmlNodeRef xmlNode = ar.node; if (ar.bLoading) { // Loading. DetachAll(); // Loading. XmlNodeRef childsRoot = xmlNode->findChild( "Objects" ); if (childsRoot) { // Load all childs from XML archive. ar.LoadObjects( childsRoot ); InvalidateBBox(); } } else { if (GetChildCount() > 0) { // Saving. XmlNodeRef root = xmlNode->newChild( "Objects" ); ar.node = root; // Save all child objects to XML. int num = GetChildCount(); for (int i = 0; i < num; i++) { CBaseObject *obj = GetChild(i); ar.SaveObject( obj ); } } } ar.node = xmlNode; } ////////////////////////////////////////////////////////////////////////// XmlNodeRef CGroup::Export( const CString &levelPath,XmlNodeRef &xmlNode ) { /* // Saving. XmlNodeRef root( "Objects" ); xmlNode->addChild( root ); // Save all child objects to XML. for (int i = 0; i < m_childs.size(); i++) { CBaseObject *obj = m_childs[i]; XmlNodeRef objNode( "Object" ); objNode->setAttr( "Type",obj->GetTypeName() ); root->addChild( objNode ); obj->Serialize( objNode,false ); } */ return CBaseObject::Export( levelPath,xmlNode ); } ////////////////////////////////////////////////////////////////////////// void CGroup::Ungroup() { std::vector childs; for (int i = 0; i < GetChildCount(); i++) { childs.push_back(GetChild(i)); } DetachAll(); // Select ungrouped objects. for (int i = 0; i < childs.size(); i++) { GetObjectManager()->SelectObject( childs[i] ); } } ////////////////////////////////////////////////////////////////////////// void CGroup::Open() { if (!m_opened) { } m_opened = true; } ////////////////////////////////////////////////////////////////////////// void CGroup::Close() { if (m_opened) { } m_opened = false; } ////////////////////////////////////////////////////////////////////////// void CGroup::RecursivelyGetBoundBox( CBaseObject *object,BBox &box ) { BBox b; object->GetBoundBox( b ); box.Add(b.min); box.Add(b.max); int numChilds = object->GetChildCount(); for (int i = 0; i < numChilds; i++) { RecursivelyGetBoundBox( object->GetChild(i),box ); } } ////////////////////////////////////////////////////////////////////////// void CGroup::CalcBoundBox() { Matrix44 ltm = GetLocalTM(); Matrix44 tm; tm.SetIdentity(); SetWorldTM(tm); // Calc local bounds box.. BBox box; box.Reset(); int numChilds = GetChildCount(); for (int i = 0; i < numChilds; i++) { RecursivelyGetBoundBox( GetChild(i),box ); } if (numChilds == 0) { box.min = Vec3(-1,-1,-1); box.max = Vec3(1,1,1); } SetLocalTM(ltm); m_bbox = box; m_bBBoxValid = true; } ////////////////////////////////////////////////////////////////////////// void CGroup::OnChildModified() { if (m_ignoreChildModify) return; InvalidateBBox(); } //! Select objects withing specified distance from givven position. int CGroup::SelectObjects( const BBox &box,bool bUnselect ) { int numSel = 0; BBox objBounds; uint hideMask = GetIEditor()->GetDisplaySettings()->GetObjectHideMask(); int num = GetChildCount(); for (int i = 0; i < num; ++i) { CBaseObject *obj = GetChild(i); if (obj->IsHidden()) continue; if (obj->IsFrozen()) continue; if (obj->GetGroup()) continue; obj->GetBoundBox( objBounds ); if (box.IsIntersectBox(objBounds)) { numSel++; if (!bUnselect) GetObjectManager()->SelectObject( obj ); else GetObjectManager()->UnselectObject( obj ); } // If its group. if (obj->GetRuntimeClass() == RUNTIME_CLASS(CGroup)) { numSel += ((CGroup*)obj)->SelectObjects( box,bUnselect ); } } return numSel; } ////////////////////////////////////////////////////////////////////////// void CGroup::OnEvent( ObjectEvent event ) { CBaseObject::OnEvent(event); int i; switch (event) { case EVENT_DBLCLICK: if (IsOpen()) { int numChilds = GetChildCount(); for (i = 0; i < numChilds; i++) { GetObjectManager()->SelectObject( GetChild(i) ); } } break; default: { int numChilds = GetChildCount(); for (int i = 0; i < numChilds; i++) { GetChild(i)->OnEvent( event ); } } break; } }; ////////////////////////////////////////////////////////////////////////// void CGroup::OnMergeStaticGeom( IVariable *pVar ) { } ////////////////////////////////////////////////////////////////////////// int CGroup::GetGeomMergeId() const { if (mv_mergeStaticGeom) { return m_geomMergeId; } return 0; }