//////////////////////////////////////////////////////////////////////////// // // Crytek Engine Source File. // Copyright (C), Crytek Studios, 2001. // ------------------------------------------------------------------------- // File name: ObjectManager.cpp // Version: v1.00 // Created: 10/10/2001 by Timur. // Compilers: Visual C++ 6.0 // Description: ObjectManager implementation. // ------------------------------------------------------------------------- // History: // //////////////////////////////////////////////////////////////////////////// #include "StdAfx.h" #include "ObjectManager.h" #include "..\DisplaySettings.h" #include "Util\Crc32.h" #include "TagPoint.h" #include "TagComment.h" //#include "StatObj.h" #include "Entity.h" #include "Group.h" #include "Volume.h" #include "SoundObject.h" #include "ShapeObject.h" #include "AIPoint.h" #include "BrushObject.h" #include "CameraObject.h" #include "AIAnchor.h" #include "AreaBox.h" #include "AreaSphere.h" #include "WaterShapeObject.h" #include "VisAreaShapeObject.h" #include "ProtEntityObject.h" #include "PrefabObject.h" #include "Settings.h" #include "Viewport.h" #include "GizmoManager.h" #include "ObjectLayerManager.h" #include "AxisGizmo.h" #include /*! * Class Description used for object templates. * This description filled from Xml template files. */ class CXMLObjectClassDesc : public CObjectClassDesc { public: CObjectClassDesc* superType; CString type; CString category; CString fileSpec; GUID guid; public: REFGUID ClassID() { return guid; } ObjectType GetObjectType() { return superType->GetObjectType(); }; const char* ClassName() { return type; }; const char* Category() { return category; }; CRuntimeClass* GetRuntimeClass() { return superType->GetRuntimeClass(); }; const char* GetFileSpec() { if (!fileSpec.IsEmpty()) return fileSpec; else return superType->GetFileSpec(); }; virtual int GameCreationOrder() { return superType->GameCreationOrder(); }; }; ////////////////////////////////////////////////////////////////////////// //! Undo New Object class CUndoBaseObjectNew : public IUndoObject { public: CUndoBaseObjectNew( CBaseObject *obj ) { m_object = obj; } protected: virtual int GetSize() { return sizeof(*this); }; // Return size of xml state. virtual const char* GetDescription() { return "New BaseObject"; }; virtual void Undo( bool bUndo ) { if (bUndo) { m_redo = new CXmlNode("Redo"); // Save current object state. CObjectArchive ar(GetIEditor()->GetObjectManager(),m_redo,false); ar.bUndo = true; m_object->Serialize( ar ); } // Delete this object. GetIEditor()->DeleteObject( m_object ); } virtual void Redo() { if (m_redo) { IObjectManager *pObjMan = GetIEditor()->GetObjectManager(); CObjectArchive ar( pObjMan,m_redo,true ); ar.bUndo = true; ar.LoadObject( m_redo,m_object ); pObjMan->SelectObject( m_object ); } } private: CBaseObjectPtr m_object; XmlNodeRef m_redo; }; ////////////////////////////////////////////////////////////////////////// //! Undo Delete Object class CUndoBaseObjectDelete : public IUndoObject { public: CUndoBaseObjectDelete( CBaseObject *obj ) { m_object = obj; // Save current object state. m_undo = new CXmlNode("Undo"); CObjectArchive ar(GetIEditor()->GetObjectManager(),m_undo,false); ar.bUndo = true; m_object->Serialize( ar ); } protected: virtual int GetSize() { return sizeof(*this); }; // Return size of xml state. virtual const char* GetDescription() { return "Delete BaseObject"; }; virtual void Undo( bool bUndo ) { IObjectManager *pObjMan = GetIEditor()->GetObjectManager(); CObjectArchive ar( pObjMan,m_undo,true ); ar.bUndo = true; ar.LoadObject( m_undo,m_object ); pObjMan->SelectObject( m_object ); } virtual void Redo() { // Delete this object. GetIEditor()->DeleteObject( m_object ); } private: CBaseObjectPtr m_object; XmlNodeRef m_undo; }; ////////////////////////////////////////////////////////////////////////// //! Undo Select Object class CUndoBaseObjectSelect : public IUndoObject { public: CUndoBaseObjectSelect( CBaseObject *obj ) { assert( obj != 0 ); m_object = obj; m_bUndoSelect = obj->IsSelected(); } protected: virtual void Release() { delete this; }; virtual int GetSize() { return sizeof(*this); }; // Return size of xml state. virtual const char* GetDescription() { return "Select Object"; }; virtual void Undo( bool bUndo ) { if (bUndo) { m_bRedoSelect = m_object->IsSelected(); } if (m_bUndoSelect) GetIEditor()->GetObjectManager()->SelectObject(m_object); else GetIEditor()->GetObjectManager()->UnselectObject(m_object); } virtual void Redo() { if (m_bRedoSelect) GetIEditor()->GetObjectManager()->SelectObject(m_object); else GetIEditor()->GetObjectManager()->UnselectObject(m_object); } private: CBaseObjectPtr m_object; bool m_bUndoSelect; bool m_bRedoSelect; }; ////////////////////////////////////////////////////////////////////////// // CObjectManager implementation. ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// CObjectManager::CObjectManager() { m_currSelection = &m_defaultSelection; m_currEditObject = 0; m_selectCallback = 0; m_createGameObjects = true; m_bSingleSelection = false; m_nLastSelCount=0; m_bGenUniqObjectNames = true; m_bSelectionChanged = false; m_bVisibleObjectValid = true; m_lastHideMask = 0; m_pLoadProgress = 0; m_totalObjectsToLoad = 0; m_loadedObjects = 0; // Creates objects layers manager. m_pLayerManager = new CObjectLayerManager( this ); // Creates gizmo manager. m_gizmoManager = new CGizmoManager; // Register default classes. CClassFactory *cf = CClassFactory::Instance(); cf->RegisterClass( new CTagPointClassDesc ); cf->RegisterClass( new CRespawnPointClassDesc ); cf->RegisterClass( new CTagCommentClassDesc ); //cf->RegisterClass( new CStaticObjectClassDesc ); //cf->RegisterClass( new CBuildingClassDesc ); cf->RegisterClass( new CEntityClassDesc ); cf->RegisterClass( new CSimpleEntityClassDesc ); cf->RegisterClass( new CGroupClassDesc ); cf->RegisterClass( new CVolumeClassDesc ); cf->RegisterClass( new CSoundObjectClassDesc ); cf->RegisterClass( new CShapeObjectClassDesc ); cf->RegisterClass( new CAIPathObjectClassDesc ); cf->RegisterClass( new CAIForbiddenAreaObjectClassDesc ); cf->RegisterClass( new CAINavigationModifierObjectClassDesc ); cf->RegisterClass( new CAIOcclusionPlaneObjectClassDesc ); cf->RegisterClass( new CAIPointClassDesc ); cf->RegisterClass( new CBrushObjectClassDesc ); cf->RegisterClass( new CCameraObjectClassDesc ); cf->RegisterClass( new CCameraObjectTargetClassDesc ); cf->RegisterClass( new CAIAnchorClassDesc ); cf->RegisterClass( new CAreaBoxClassDesc ); cf->RegisterClass( new CAreaSphereClassDesc ); cf->RegisterClass( new CWaterShapeObjectClassDesc ); cf->RegisterClass( new CVisAreaShapeObjectClassDesc ); cf->RegisterClass( new CPortalShapeObjectClassDesc ); cf->RegisterClass( new COccluderShapeObjectClassDesc ); cf->RegisterClass( new CProtEntityObjectClassDesc ); cf->RegisterClass( new CPrefabObjectClassDesc ); LoadRegistry(); } ////////////////////////////////////////////////////////////////////////// CObjectManager::~CObjectManager() { SaveRegistry(); DeleteAllObjects(); if (m_gizmoManager) delete m_gizmoManager; if (m_pLayerManager) delete m_pLayerManager; } void CObjectManager::SaveRegistry() { } void CObjectManager::LoadRegistry() { } ////////////////////////////////////////////////////////////////////////// CBaseObject* CObjectManager::NewObject( CObjectClassDesc *cls,CBaseObject *prev,const CString &file ) { ASSERT( cls != 0 ); CRuntimeClass *rtClass = cls->GetRuntimeClass(); ASSERT( rtClass->IsDerivedFrom(RUNTIME_CLASS(CBaseObject)) ); if (prev) { // Both current and previous object must be of same type. ASSERT( cls == prev->GetClassDesc() ); } // Suspend undo operations when initialzing object. GetIEditor()->SuspendUndo(); CBaseObjectPtr obj = (CBaseObject*)rtClass->CreateObject(); obj->SetIEditor( GetIEditor() ); obj->SetClassDesc( cls ); obj->SetLayer( m_pLayerManager->GetCurrentLayer() ); obj->m_objectManager = this; CoCreateGuid( &obj->m_guid ); // generate uniq GUID for this object. GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( obj ); if (obj->Init( GetIEditor(),prev,file )) { if (obj->GetName().IsEmpty()) { obj->SetName( GenUniqObjectName( cls->ClassName() ) );; } /* // Check if object Init function changed its name. if (objName.Compare(obj->GetName()) != 0) { // Object changed name. we must make sure it unique and generate new id. obj->SetName( GenUniqObjectName(obj->GetName()) ); // Generate new unique id for this object. int id = Crc32Gen::GetCRC32(obj->GetName()); obj->SetId(id); } */ // Create game object itself. obj->CreateGameObject(); if (!AddObject( obj )) obj = 0; } else { obj = 0; } GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( NULL ); GetIEditor()->ResumeUndo(); if (obj != 0 && GetIEditor()->IsUndoRecording()) GetIEditor()->RecordUndo( new CUndoBaseObjectNew(obj) ); return obj; } ////////////////////////////////////////////////////////////////////////// CBaseObject* CObjectManager::NewObject( CObjectArchive &ar,CBaseObject *pUndoObject,bool bMakeNewId ) { // Suspend undo operations when initialzing object. CUndoSuspend undoSuspender; XmlNodeRef objNode = ar.node; // Load all objects from XML. CString typeName; GUID id = GUID_NULL; if (!objNode->getAttr( "Type",typeName )) return 0; if (!objNode->getAttr( "Id",id )) { // Make new ID for object that doesnt have if. CoCreateGuid( &id ); } if (bMakeNewId) { // Make new guid for this object. GUID newId; CoCreateGuid( &newId ); ar.RemapID( id,newId ); // Mark this id remaped. id = newId; } CBaseObjectPtr pObject; if (pUndoObject) { // if undoing restore object pointer. pObject = pUndoObject; } else { // New object creation. CObjectClassDesc *cls = FindClass( typeName ); if (!cls) { CLogFile::FormatLine( "Error: RuntimeClass %s not registered",(const char*)typeName ); return 0; } CRuntimeClass *rtClass = cls->GetRuntimeClass(); assert( rtClass->IsDerivedFrom(RUNTIME_CLASS(CBaseObject)) ); pObject = (CBaseObject*)rtClass->CreateObject(); pObject->SetIEditor( GetIEditor() ); pObject->SetClassDesc( cls ); pObject->m_objectManager = this; pObject->m_guid = id; pObject->SetLayer( m_pLayerManager->GetCurrentLayer() ); // @FIXME: Make sure this id not taken. CBaseObject *obj = FindObject(pObject->GetId()); if (obj) { // If id is taken. CString objName; objNode->getAttr( "Name",objName ); CString error; error.Format( _T("Object with ID %s (%s) already exist in Object Manager\r\nWhen trying to create object: %s\r\nNew Object Ignored."),GuidUtil::ToString(obj->GetId()),(const char*)obj->GetName(),(const char*)objName ); CLogFile::WriteLine( error ); MessageBox( AfxGetMainWnd()->GetSafeHwnd(),error,_T("Object Id Collision"),MB_OK|MB_ICONWARNING ); return 0; //CoCreateGuid( &pObject->m_guid ); // generate uniq GUID for this object. } } GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( pObject ); if (!pObject->Init( GetIEditor(),0,"" )) { GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( NULL ); return 0; } if (!AddObject( pObject )) { GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( NULL ); return 0; } pObject->Serialize( ar ); GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( NULL ); if (!pObject->GetLayer()) { // Cannot be. assert(0); } if (pObject != 0 && pUndoObject == 0) { // If new object with no undo, record it. GetIEditor()->RecordUndo( new CUndoBaseObjectNew(pObject) ); } m_loadedObjects++; if (m_pLoadProgress && m_totalObjectsToLoad > 0) m_pLoadProgress->Step( (m_loadedObjects*100)/m_totalObjectsToLoad ); return pObject; } ////////////////////////////////////////////////////////////////////////// CBaseObject* CObjectManager::NewObject( const CString &typeName,CBaseObject *prev,const CString &file ) { CObjectClassDesc *cls = FindClass( typeName ); if (!cls) { GetIEditor()->GetSystem()->GetILog()->Log( "Warning: RuntimeClass %s not registered",(const char*)typeName ); return 0; } CBaseObject *pObject = NewObject( cls,prev,file ); return pObject; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::DeleteObject( CBaseObject *obj ) { if (m_currEditObject == obj) EndEditParams(); if (!obj) return; // If object already deleted. if (obj->CheckFlags(OBJFLAG_DELETED)) return; // Check if object is a group then delete all childs. if (obj->IsKindOf(RUNTIME_CLASS(CGroup))) { ((CGroup*)obj)->DeleteAllChilds(); } // This will detach all childs and store Undo for each detachment (So they can be restored with Undo) //obj->DetachAll(); // Must be after object DetachAll to support restoring Parent/Child relations. if (CUndo::IsRecording()) { // Store undo for all child objects. for (int i = 0; i < obj->GetChildCount(); i++) { if (!obj->GetChild(i)->CheckFlags(OBJFLAG_PREFAB)) obj->GetChild(i)->StoreUndo("DeleteParent"); } CUndo::Record( new CUndoBaseObjectDelete(obj) ); } GetIEditor()->SuspendUndo(); //! If object is child remove it from parent. obj->DetachThis(); // Release game resources. obj->Done(); NotifyObjectListeners( obj,CBaseObject::ON_DELETE ); RemoveObject( obj ); GetIEditor()->ResumeUndo(); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::DeleteAllObjects() { EndEditParams(); ClearSelection(); int i; InvalidateVisibleList(); // Delete all selection groups. std::vector sel; stl::map_to_vector( m_selections,sel ); for (i = 0; i < sel.size(); i++) { delete sel[i]; } m_selections.clear(); std::vector objectsHolder; GetAllObjects( objectsHolder ); for (i = 0; i < objectsHolder.size(); i++) { objectsHolder[i]->Done(); } // Clear map. m_objects.clear(); //! Delete object instances. objectsHolder.clear(); // Clear name map. m_nameNumbersMap.clear(); } CBaseObject* CObjectManager::CloneObject( CBaseObject *obj ) { ASSERT( obj ); //CRuntimeClass *cls = obj->GetRuntimeClass(); //CBaseObject *clone = (CBaseObject*)cls->CreateObject(); //clone->CloneCopy( obj ); CBaseObject *clone = NewObject( obj->GetClassDesc(),obj ); return clone; } ////////////////////////////////////////////////////////////////////////// CBaseObject* CObjectManager::FindObject( REFGUID guid ) const { return stl::find_in_map( m_objects,guid,(CBaseObject*)0 ); } ////////////////////////////////////////////////////////////////////////// CBaseObject* CObjectManager::FindObject( const CString &sName ) const { for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *pObj = it->second; if (stricmp(pObj->GetName(), sName)==0) return pObj; } return NULL; } ////////////////////////////////////////////////////////////////////////// bool CObjectManager::AddObject( CBaseObject *obj ) { CBaseObjectPtr p = stl::find_in_map( m_objects,obj->GetId(),0 ); if (p) { CErrorRecord err; err.error.Format( "New Object %s have Duplicate GUID %s, New Object Ignored",(const char*)obj->GetName(),GuidUtil::ToString(obj->GetId()) ); err.severity = CErrorRecord::ESEVERITY_ERROR; err.pObject = obj; err.flags = CErrorRecord::FLAG_OBJECTID; GetIEditor()->GetErrorReport()->ReportError(err); return false; } m_objects[obj->GetId()] = obj; InvalidateVisibleList(); return true; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::RemoveObject( CBaseObject *obj ) { assert( obj != 0 ); InvalidateVisibleList(); m_objects.erase(obj->GetId()); // Remove this object from selection groups. m_currSelection->RemoveObject(obj); std::vector sel; stl::map_to_vector( m_selections,sel ); for (int i = 0; i < sel.size(); i++) { sel[i]->RemoveObject(obj); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::GetAllObjects( std::vector &objects ) const { objects.clear(); objects.reserve( m_objects.size() ); for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it) { objects.push_back( it->second ); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::ChangeObjectId( CBaseObject *obj,int newId ) { /* assert( obj ); CBaseObjectPtr pObject = obj; CBaseObjectPtr p; if (m_objects.Find(obj->GetId(),p)) { // If object is already added to object list, change it in the map. m_objects.Erase( obj->GetId() ); m_objects[newId] = pObject; } pObject->SetId( newId ); */ } ////////////////////////////////////////////////////////////////////////// void CObjectManager::ChangeObjectName( CBaseObject *obj,const CString &newName ) { assert( obj ); if (newName != obj->GetName()) { /* // Check if this name already used. CBaseObject *pExisting = FindObject( newName ); if (pExisting) { // If id is taken. CString str; str.Format( "Duplicate Object Name: %s\r\nName change ignored",(const char*)newName ); AfxMessageBox( str,MB_OK|MB_ICONWARNING ); return; } */ obj->SetName(newName); /* // Change ID of object together with its name. int id = Crc32Gen::GetCRC32(obj->GetName()); ChangeObjectId( obj,id ); */ } } ////////////////////////////////////////////////////////////////////////// int CObjectManager::GetObjectCount() const { return m_objects.size(); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::GetObjects( CBaseObjectsArray &objects,CObjectLayer* layer ) { objects.clear(); objects.reserve( m_objects.size() ); for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { if (layer == 0 || it->second->GetLayer() == layer) objects.push_back( it->second ); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::GetCameras( std::vector &objects ) { objects.clear(); for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *object = it->second; if (object->IsKindOf(RUNTIME_CLASS(CCameraObject))) { // Only consider camera sources. if (object->IsLookAtTarget()) continue; objects.push_back( (CCameraObject*)object ); } } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::SendEvent( ObjectEvent event ) { for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *obj = it->second; if (obj->GetGroup()) continue; obj->OnEvent( event ); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::SendEvent( ObjectEvent event,const BBox &bounds ) { BBox box; for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *obj = it->second; if (obj->GetGroup()) continue; obj->GetBoundBox(box); if (bounds.IsIntersectBox(box)) obj->OnEvent( event ); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::Update() { HWND hFocusWnd = GetFocus(); if (m_currSelection->GetCount() == 0) { // Nothing selected. EndEditParams(); } else if (m_currSelection->GetCount() == 1) { if (!m_bSingleSelection) EndEditParams(); // Single object selecte. if (m_currEditObject != m_currSelection->GetObject(0)) { m_bSelectionChanged = false; CBaseObject *newSelObject = m_currSelection->GetObject(0); if (!m_currEditObject || (m_currEditObject->GetRuntimeClass() != newSelObject->GetRuntimeClass())) { // If old object and new objects are of different classes. EndEditParams(); } BeginEditParams( newSelObject,OBJECT_EDIT ); } } else if (m_currSelection->GetCount() > 1) { // Multiple objects are selected. if (m_bSelectionChanged) { m_bSelectionChanged = false; m_nLastSelCount=m_currSelection->GetCount(); EndEditParams(); bool bAllSameType = m_currSelection->SameObjectType(); m_currEditObject = m_currSelection->GetObject(0); m_currEditObject->BeginEditMultiSelParams( bAllSameType ); } } // Restore focus if it changed. if (hFocusWnd != GetFocus() && hFocusWnd) SetFocus( hFocusWnd ); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::HideObject( CBaseObject *obj,bool hide ) { assert( obj != 0 ); // Remove object from main object set and put it to hidden set. obj->SetHidden( hide ); InvalidateVisibleList(); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::UnhideAll() { for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *obj = it->second; obj->SetHidden(false); } InvalidateVisibleList(); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::FreezeObject( CBaseObject *obj,bool freeze ) { assert( obj != 0 ); // Remove object from main object set and put it to hidden set. obj->SetFrozen( freeze ); InvalidateVisibleList(); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::UnfreezeAll() { for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *obj = it->second; obj->SetFrozen(false); } InvalidateVisibleList(); } ////////////////////////////////////////////////////////////////////////// bool CObjectManager::SelectObject( CBaseObject *obj ) { assert( obj ); // Check if can be selected. if (!(obj->GetType() & gSettings.objectSelectMask)) return false; if (!obj->IsSelectable()) return false; if (m_selectCallback) { if (!m_selectCallback->OnSelectObject( obj )) return true; } /* if (GetIEditor()->IsUndoRecording() && !obj->IsSelected()) { GetIEditor()->RecordUndo( new CUndoBaseObjectSelect(obj) ); } */ m_currSelection->AddObject( obj ); SetObjectSelected( obj,true ); return true; } void CObjectManager::UnselectObject( CBaseObject *obj ) { /* if (GetIEditor()->IsUndoRecording() && obj->IsSelected()) { GetIEditor()->RecordUndo( new CUndoBaseObjectSelect(obj) ); } */ SetObjectSelected( obj,false ); m_currSelection->RemoveObject( obj ); } CSelectionGroup* CObjectManager::GetSelection( const CString &name ) const { CSelectionGroup *selection = stl::find_in_map( m_selections,name,(CSelectionGroup*)0 ); return selection; } void CObjectManager::NameSelection( const CString &name ) { if (m_currSelection->IsEmpty()) return; CSelectionGroup *selection = stl::find_in_map( m_selections,name,(CSelectionGroup*)0 ); if (selection) { ASSERT( selection != 0 ); // Check if trying to rename itself to the same name. if (selection == m_currSelection) return; m_selections.erase( name ); delete selection; } selection = new CSelectionGroup; selection->Copy( *m_currSelection ); selection->SetName( name ); m_selections[name] = selection; m_currSelection = selection; m_defaultSelection.RemoveAll(); } ////////////////////////////////////////////////////////////////////////// int CObjectManager::ClearSelection() { int numSel = m_currSelection->GetCount(); UnselectCurrent(); m_defaultSelection.RemoveAll(); m_currSelection = &m_defaultSelection; m_bSelectionChanged = true; return numSel; } ////////////////////////////////////////////////////////////////////////// int CObjectManager::InvertSelection() { int selCount = 0; // iterate all objects. for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *pObj = it->second; if (pObj->IsSelected()) UnselectObject( pObj ); else { if (SelectObject( pObj )) selCount++; } } return selCount; } void CObjectManager::SetSelection( const CString &name ) { CSelectionGroup *selection = stl::find_in_map( m_selections,name,(CSelectionGroup*)0 ); if (selection) { UnselectCurrent(); ASSERT( selection != 0 ); m_currSelection = selection; SelectCurrent(); } } void CObjectManager::RemoveSelection( const CString &name ) { CString selName = name; CSelectionGroup *selection = stl::find_in_map( m_selections,name,(CSelectionGroup*)0 ); if (selection) { if (selection == m_currSelection) { UnselectCurrent(); m_currSelection = &m_defaultSelection; m_defaultSelection.RemoveAll(); } delete selection; m_selections.erase( selName ); } } void CObjectManager::SelectCurrent() { for (int i = 0; i < m_currSelection->GetCount(); i++) { CBaseObject *obj = m_currSelection->GetObject(i); if (GetIEditor()->IsUndoRecording() && !obj->IsSelected()) GetIEditor()->RecordUndo( new CUndoBaseObjectSelect(obj) ); SetObjectSelected( obj,true ); } } void CObjectManager::UnselectCurrent() { // Make sure to unlock selection. GetIEditor()->LockSelection( false ); for (int i = 0; i < m_currSelection->GetCount(); i++) { CBaseObject *obj = m_currSelection->GetObject(i); if (GetIEditor()->IsUndoRecording() && obj->IsSelected()) GetIEditor()->RecordUndo( new CUndoBaseObjectSelect(obj) ); SetObjectSelected( obj,false ); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::Display( DisplayContext &dc ) { Vec3 org; FUNCTION_PROFILER( GetIEditor()->GetSystem(),PROFILE_EDITOR ); if (!m_bVisibleObjectValid || gSettings.objectHideMask != m_lastHideMask) { m_lastHideMask = gSettings.objectHideMask; UpdateVisibilityList(); } if (!dc.settings->IsDisplayHelpers()) { return; } CCamera camera = GetIEditor()->GetSystem()->GetViewCamera(); BBox bbox; if (dc.flags & DISPLAY_2D) { int numVis = m_visibleObjects.size(); for (int i = 0; i < numVis; i++) { CBaseObject *obj = m_visibleObjects[i]; obj->GetBoundBox( bbox ); if (dc.box.IsIntersectBox( bbox )) { obj->Display( dc ); } } } else { m_displayedObjects.resize(0); m_displayedObjects.reserve( m_visibleObjects.size() ); int numVis = m_visibleObjects.size(); for (int i = 0; i < numVis; i++) { CBaseObject *obj = m_visibleObjects[i]; obj->GetBoundBox( bbox ); if (camera.IsAABBVisibleFast( AABB(bbox.min,bbox.max) )) //if (camera.CheckOverlap(AABB(bbox.min,bbox.max)) != CULL_EXCLUSION) { m_displayedObjects.push_back( obj ); obj->Display( dc ); } } } if (m_gizmoManager) { m_gizmoManager->Display( dc ); } } void CObjectManager::BeginEditParams( CBaseObject *obj,int flags ) { ASSERT( obj != 0 ); if (obj == m_currEditObject) return; if (GetSelection()->GetCount() > 1) return; HWND hFocusWnd = GetFocus(); if (m_currEditObject) { //if (obj->GetClassDesc() != m_currEditObject->GetClassDesc()) if (!obj->IsSameClass(m_currEditObject)) EndEditParams( flags ); } m_currEditObject = obj; if (flags & OBJECT_CREATE) { // Unselect all other objects. ClearSelection(); // Select this object. SelectObject( obj ); } m_bSingleSelection = true; m_currEditObject->BeginEditParams( GetIEditor(),flags ); // Restore focus if it changed. if (hFocusWnd != GetFocus() && hFocusWnd) SetFocus( hFocusWnd ); } void CObjectManager::EndEditParams( int flags ) { if (m_currEditObject) { if (m_bSingleSelection) m_currEditObject->EndEditParams( GetIEditor() ); else m_currEditObject->EndEditMultiSelParams(); } m_bSingleSelection = false; m_currEditObject = 0; m_bSelectionChanged = false; } //! Select objects withing specified distance from givven position. int CObjectManager::SelectObjects( const BBox &box,bool bUnselect ) { int numSel = 0; BBox objBounds; for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *obj = it->second; if (obj->IsHidden()) continue; if (obj->IsFrozen()) continue; if (obj->GetGroup()) continue; obj->GetBoundBox( objBounds ); if (box.IsIntersectBox(objBounds)) { numSel++; if (!bUnselect) SelectObject( obj ); else UnselectObject( obj ); } // If its group. if (obj->GetRuntimeClass() == RUNTIME_CLASS(CGroup)) { numSel += ((CGroup*)obj)->SelectObjects( box,bUnselect ); } } return numSel; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::DeleteSelection() { // Make sure to unlock selection. GetIEditor()->LockSelection( false ); int i; std::vector objects; for (i = 0; i < m_currSelection->GetCount(); i++) { objects.push_back( m_currSelection->GetObject(i) ); } RemoveSelection( m_currSelection->GetName() ); m_currSelection = &m_defaultSelection; m_defaultSelection.RemoveAll(); // When deleting group, subobject of group must become selected. std::vector toselect; for (i = 0; i < objects.size(); i++) { DeleteObject( objects[i] ); } } /* //! Check if new hit object is better suitable for selection then former selected hit object. //! @return true if obj1 is better for selection. inline bool CompareHitObjects( CBaseObject *obj1,HitContext &hc1,CBaseObject *obj2,HitContext &hc2 ) { if (hc1.geometryHit && hc2.geometryHit) { if (hc1.dist < hc2.dist) return true; else return false; } if (!hc1.geometryHit && hc2.geometryHit) { return false; } } */ ////////////////////////////////////////////////////////////////////////// bool CObjectManager::HitTest( Vec3 &raySrc,Vec3 &rayDir,float distanceTollerance,ObjectHitInfo &hitInfo ) { FUNCTION_PROFILER( GetIEditor()->GetSystem(),PROFILE_EDITOR ); hitInfo.object = 0; hitInfo.distance = 0; hitInfo.axis = 0; HitContext hcOrg; hcOrg.view = hitInfo.view; if (hcOrg.view) { hcOrg.b2DViewport = hcOrg.view->GetType() != ET_ViewportCamera; } hcOrg.point2d = hitInfo.point2d; hcOrg.rayDir = GetNormalized(rayDir); hcOrg.raySrc = raySrc; hcOrg.distanceTollerance = distanceTollerance; HitContext hc = hcOrg; HitContext hcSelected; float mindist = FLT_MAX; if (!hitInfo.bIgnoreAxis) { // Test gizmos. if (m_gizmoManager->HitTest( hc )) { if (hc.axis != 0) { hitInfo.object = hc.object; hitInfo.axis = hc.axis; hitInfo.distance = hc.dist; return true; } } } BBox box; Vec3 tempPnt; // Only HitTest objects, that where previously Displayed. CBaseObject *selected = 0; int numVis = 0; if (hcOrg.b2DViewport) numVis = m_visibleObjects.size(); else numVis = m_displayedObjects.size(); for (int i = 0; i < numVis; i++) { CBaseObject *obj; if (hcOrg.b2DViewport) obj = m_visibleObjects[i]; else obj = m_displayedObjects[i]; if (obj->IsFrozen()) continue; //! Only check root objects. if (obj->GetGroup()) continue; obj->GetBoundBox( box ); if (hitInfo.camera) { if (!hitInfo.camera->IsAABBVisibleFast( AABB(box.min,box.max))) { continue; } } else { if (!box.IsIntersectBox(hitInfo.bounds)) continue; } // Fast bounding box check. if (!box.IsIntersectRay(raySrc,rayDir,tempPnt)) continue; hc = hcOrg; /* if (obj->IsSelected()) { if (!hitInfo.bIgnoreAxis) { int axis = obj->HitTestAxis( hc ); if (axis != 0) { hitInfo.object = obj; hitInfo.axis = axis; hitInfo.distance = hc.dist; return true; } } } */ ObjectType objType = obj->GetType(); // Check if this object type is masked for selection. if (!(objType & gSettings.objectSelectMask)) continue; if (obj->HitTest(hc)) { // Check if this object is nearest. if (hc.axis != 0) { hitInfo.object = obj; hitInfo.axis = hc.axis; hitInfo.distance = hc.dist; return true; } // Compare objects obj and selected. // CompareHitObjects( obj,hc,selected,hcSelected ); // If object is selected prefere unselected object. //if (hc.dist < mindist || (selected != 0 && selected->IsSelected())) if (hc.dist < mindist) { // If collided object specified, accept it, overwise take tested object itself. CBaseObject *hitObj = hc.object; if (!hitObj) hitObj = obj; hc.object = 0; // If object is selected prefere unselected object. //if (selected != 0 && !selected->IsSelected() && hitObj->IsSelected()) //continue; mindist = hc.dist; selected = hitObj; //hcSelected = hc; } } } if (selected) { hitInfo.object = selected; hitInfo.distance = mindist; return true; } return false; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::SelectObjectsInRect( CViewport *view,const CRect &rect,bool bSelect ) { // Ignore too small rectangles. if (rect.Width() < 1 || rect.Height() < 1) return; CUndo undo( "Select Object(s)" ); BBox box; HitContext hc; hc.view = view; hc.b2DViewport = view->GetType() != ET_ViewportCamera; hc.rect = rect; int numVis; if (hc.b2DViewport) numVis = m_visibleObjects.size(); else numVis = m_displayedObjects.size(); for (int i = 0; i < numVis; i++) { CBaseObject *pObj; if (hc.b2DViewport) pObj = m_visibleObjects[i]; else pObj = m_displayedObjects[i]; if (!pObj->IsSelectable()) continue; // Retrieve world space bound box. pObj->GetBoundBox( box ); // Check if object visible in viewport. if (!view->IsBoundsVisible(box)) continue; if (pObj->HitTestRect(hc)) { if (bSelect) SelectObject(pObj); else UnselectObject(pObj); } } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::RegisterObjectName( const CString &name ) { // Remove all numbers from the end of typename. CString typeName = name; int nameLen = typeName.GetLength(); int len = nameLen; while (len > 0 && isdigit(typeName[len-1])) len--; typeName = typeName.Left(len); int num = 0; if (len < nameLen) num = atoi((const char*)name+len) + 0; int lastNumber = stl::find_in_map( m_nameNumbersMap,typeName,0 ); int newLastNumber = MAX( num,lastNumber ); if (newLastNumber != lastNumber) m_nameNumbersMap[typeName] = newLastNumber; } ////////////////////////////////////////////////////////////////////////// CString CObjectManager::GenUniqObjectName( const CString &theTypeName ) { if (!m_bGenUniqObjectNames) return theTypeName; // Remove all numbers from the end of typename. CString typeName = theTypeName; int len = typeName.GetLength(); while (len > 0 && isdigit(typeName[len-1])) len--; typeName = typeName.Left(len); int lastNumber = stl::find_in_map( m_nameNumbersMap,typeName,0 ); lastNumber++; m_nameNumbersMap[typeName] = lastNumber; CString str; str.Format( "%s%d",(const char*)typeName,lastNumber ); /* CString tpName = typeName; char str[1024]; int num = 0; for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *obj = it->second; const char *name = it->second->GetName(); if (strncmp(name,tpName,len) == 0) { int n = atoi(name+len) + 1; num = MAX( num,n ); } } //sprintf( str,"%s%02d",(const char*)typeName,num ); sprintf( str,"%s%d",(const char*)typeName,num ); */ return str; } ////////////////////////////////////////////////////////////////////////// bool CObjectManager::EnableUniqObjectNames( bool bEnable ) { bool bPrev = m_bGenUniqObjectNames; m_bGenUniqObjectNames = bEnable; return bPrev; } ////////////////////////////////////////////////////////////////////////// CObjectClassDesc* CObjectManager::FindClass( const CString &className ) { IClassDesc *cls = CClassFactory::Instance()->FindClass( className ); if (cls != NULL && cls->SystemClassID() == ESYSTEM_CLASS_OBJECT) { return (CObjectClassDesc*)cls; } return 0; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::GetClassCategories( std::vector &categories ) { std::vector classes; CClassFactory::Instance()->GetClassesBySystemID( ESYSTEM_CLASS_OBJECT,classes ); std::set cset; for (int i = 0; i < classes.size(); i++) { const char *category = classes[i]->Category(); if (strlen(category) > 0) cset.insert( category ); } categories.clear(); categories.reserve( cset.size() ); for (std::set::iterator cit = cset.begin(); cit != cset.end(); ++cit) { categories.push_back( *cit ); } } void CObjectManager::GetClassTypes( const CString &category,std::vector &types ) { std::vector classes; CClassFactory::Instance()->GetClassesBySystemID( ESYSTEM_CLASS_OBJECT,classes ); for (int i = 0; i < classes.size(); i++) { const char* cat = classes[i]->Category(); if (stricmp(cat,category) == 0) { types.push_back( classes[i]->ClassName() ); } } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::RegisterClassTemplate( XmlNodeRef &templ ) { CString typeName = templ->getTag(); CString superTypeName; if (!templ->getAttr( "SuperType",superTypeName )) return; CObjectClassDesc *superType = FindClass( superTypeName ); if (!superType) return; CString category,fileSpec,initialName; templ->getAttr( "Category",category ); templ->getAttr( "File",fileSpec ); templ->getAttr( "Name",initialName ); CXMLObjectClassDesc *classDesc = new CXMLObjectClassDesc; classDesc->superType = superType; classDesc->type = typeName; classDesc->category = category; classDesc->fileSpec = fileSpec; CoCreateGuid( &classDesc->guid ); //classDesc->properties = templ->findChild( "Properties" ); CClassFactory::Instance()->RegisterClass( classDesc ); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::LoadClassTemplates( const CString &path ) { XmlParser parser; CString dir = Path::AddBackslash(path); std::vector files; CFileUtil::ScanDirectory( dir,"*.xml",files,false ); for (int k = 0; k < files.size(); k++) { // Construct the full filepath of the current file XmlNodeRef node = parser.parse( dir + files[k].filename ); if (node != 0 && node->isTag("ObjectTemplates")) { CString name; for (int i = 0; i < node->getChildCount(); i++) { RegisterClassTemplate( node->getChild(i) ); } } } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::Serialize( XmlNodeRef &xmlNode,bool bLoading,int flags ) { if (!xmlNode) return; bool bIgnoreExternal = flags & SERIALIZE_IGNORE_EXTERNAL; if (bLoading) { m_loadedObjects = 0; if (flags == SERIALIZE_ONLY_NOTSHARED) DeleteNotSharedObjects(); else if (flags == SERIALIZE_ONLY_SHARED) DeleteSharedObjects(); else DeleteAllObjects(); XmlNodeRef root = xmlNode->findChild( "Objects" ); int totalObjects = 0; if (root) root->getAttr( "NumObjects",totalObjects ); StartObjectsLoading( totalObjects ); // Load layers. CObjectArchive ar( this,xmlNode,true ); // Load layers. m_pLayerManager->Serialize( ar,bIgnoreExternal ); // Loading. if (root) { ar.node = root; LoadObjects( ar,false ); } EndObjectsLoading(); } else { // Saving. XmlNodeRef root = xmlNode->newChild( "Objects" ); CObjectArchive ar( this,root,false ); int totalObjects = m_objects.size(); root->setAttr( "NumObjects",totalObjects ); // Save all objects to XML. for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *obj = it->second; if (obj->GetGroup()) continue; if ((flags == SERIALIZE_ONLY_SHARED) && !obj->CheckFlags(OBJFLAG_SHARED)) continue; else if ((flags == SERIALIZE_ONLY_NOTSHARED) && obj->CheckFlags(OBJFLAG_SHARED)) continue; // Not save objects in prefabs. if (obj->CheckFlags(OBJFLAG_PREFAB)) continue; CObjectLayer *pLayer = obj->GetLayer(); if (pLayer->IsExternal() || pLayer->IsParentExternal()) continue; XmlNodeRef objNode = root->newChild( "Object" ); ar.node = objNode; obj->Serialize( ar ); } // Save layers. ar.node = xmlNode; m_pLayerManager->Serialize( ar,bIgnoreExternal ); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::LoadObjects( CObjectArchive &objectArchive,bool bSelect ) { XmlNodeRef objectsNode = objectArchive.node; int numObjects = objectsNode->getChildCount(); for (int i = 0; i < numObjects; i++) { objectArchive.node = objectsNode->getChild(i); CBaseObject *obj = objectArchive.LoadObject( objectsNode->getChild(i) ); if (obj && bSelect) SelectObject( obj ); } objectArchive.ResolveObjects(); InvalidateVisibleList(); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::Export( const CString &levelPath,XmlNodeRef &rootNode,bool onlyShared ) { // Clear export files. DeleteFile( levelPath+"TagPoints.ini" ); DeleteFile( levelPath+"Volumes.ini" ); // Save all objects to XML. for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *obj = it->second; CObjectLayer *pLayer = obj->GetLayer(); if (!pLayer->IsExportable()) continue; // Export Only shared objects. if ((obj->CheckFlags(OBJFLAG_SHARED) && onlyShared) || (!obj->CheckFlags(OBJFLAG_SHARED) && !onlyShared)) { obj->Export( levelPath,rootNode ); } } } void CObjectManager::DeleteNotSharedObjects() { std::vector objects; GetAllObjects( objects ); for (int i = 0; i < objects.size(); i++) { CBaseObject *obj = objects[i]; if (!obj->CheckFlags(OBJFLAG_SHARED)) { DeleteObject( obj ); } } } void CObjectManager::DeleteSharedObjects() { std::vector objects; GetAllObjects( objects ); for (int i = 0; i < objects.size(); i++) { CBaseObject *obj = objects[i]; if (obj->CheckFlags(OBJFLAG_SHARED)) { DeleteObject( obj ); } } } ////////////////////////////////////////////////////////////////////////// IObjectSelectCallback* CObjectManager::SetSelectCallback( IObjectSelectCallback* callback ) { IObjectSelectCallback* prev = m_selectCallback; m_selectCallback = callback; return prev; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::InvalidateVisibleList() { m_bVisibleObjectValid = false; m_visibleObjects.clear(); m_displayedObjects.clear(); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::UpdateVisibilityList() { m_visibleObjects.clear(); for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *obj = it->second; bool visible = obj->IsPotentiallyVisible(); obj->UpdateVisibility( visible ); if (visible) { // Prefabs are not added into visible list. if (!obj->CheckFlags(OBJFLAG_PREFAB)) m_visibleObjects.push_back( obj ); } } m_bVisibleObjectValid = true; } ////////////////////////////////////////////////////////////////////////// CBaseObject* CObjectManager::FindAnimNodeOwner( IAnimNode* node ) const { for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject *obj = it->second; if (obj->GetAnimNode() == node) { return obj; } } return 0; } ////////////////////////////////////////////////////////////////////////// bool CObjectManager::ConvertToType( CBaseObject *object,ObjectType objectType ) { if (objectType == OBJTYPE_BRUSH) { if (object->IsKindOf(RUNTIME_CLASS(CEntity))) { CUndo undo( "Convert To Brush" ); // Can convert Entity to brush. CBaseObjectPtr brushObject = GetIEditor()->NewObject( "Brush" ); if (!brushObject) return false; if (!brushObject->ConvertFromObject( object )) { // delete this object. DeleteObject( brushObject ); return false; } DeleteObject( object ); return true; } } else if (objectType == OBJTYPE_ENTITY) { if (object->IsKindOf(RUNTIME_CLASS(CBrushObject))) { CUndo undo( "Convert To SimpleEntity" ); // Can convert Entity to brush. CBaseObjectPtr ent = GetIEditor()->NewObject( "SimpleEntity" ); if (!ent) return false; if (!ent->ConvertFromObject( object )) { // delete this object. DeleteObject( ent ); return false; } DeleteObject( object ); return true; } } return false; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::SetObjectSelected( CBaseObject* pObject,bool bSelect ) { // Only select/unselect once. if ((pObject->IsSelected() && bSelect) || (!pObject->IsSelected() && !bSelect)) return; // Store selection undo. if (CUndo::IsRecording()) CUndo::Record( new CUndoBaseObjectSelect(pObject) ); pObject->SetSelected(bSelect); m_bSelectionChanged = true; if (bSelect) { if (CAxisGizmo::GetGlobalAxisGizmoCount() < gSettings.gizmo.axisGizmoMaxCount) { // Create axis gizmo for this object. m_gizmoManager->AddGizmo( new CAxisGizmo(pObject) ); } } if (bSelect) NotifyObjectListeners( pObject,CBaseObject::ON_SELECT ); else NotifyObjectListeners( pObject,CBaseObject::ON_UNSELECT ); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void CObjectManager::AddObjectEventListener( const EventCallback &cb ) { stl::push_back_unique( m_objectEventListeners,cb ); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::RemoveObjectEventListener( const EventCallback &cb ) { stl::find_and_erase( m_objectEventListeners,cb ); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::NotifyObjectListeners( CBaseObject *pObject,CBaseObject::EObjectListenerEvent event ) { std::list::iterator next; for (std::list::iterator it = m_objectEventListeners.begin(); it != m_objectEventListeners.end(); it = next) { next = it; ++next; // Call listener callback. (*it)( pObject,event ); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::StartObjectsLoading( int numObjects ) { if (m_pLoadProgress) return; m_pLoadProgress = new CWaitProgress( _T("Loading Objects") ); m_totalObjectsToLoad = numObjects; m_loadedObjects = 0; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::EndObjectsLoading() { if (m_pLoadProgress) delete m_pLoadProgress; m_pLoadProgress = 0; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::GatherUsedResources( CUsedResources &resources ) { CBaseObjectsArray objects; GetIEditor()->GetObjectManager()->GetObjects( objects ); for (int i = 0; i < objects.size(); i++) { CBaseObject *pObject = objects[i]; pObject->GatherUsedResources( resources ); } } ////////////////////////////////////////////////////////////////////////// IGizmoManager* CObjectManager::GetGizmoManager() { return m_gizmoManager; }