/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "ObjectManager.h" #include "../DisplaySettings.h" #include "TagPoint.h" #include "TagComment.h" #include "EntityObject.h" #include "AIWave.h" #include "Group.h" #include "Volume.h" #include "SoundObject.h" #include "ShapeObject.h" #include "ParticleEffectObject.h" #include "AiPoint.h" #include "BrushObject.h" #include "CloudObject.h" #include "CloudGroup.h" #include "DecalObject.h" #include "DistanceCloudObject.h" #include "GravityVolumeObject.h" #include "GeomEntity.h" #include "ActorEntity.h" #include "SimpleEntity.h" #include "MiscEntities.h" #include "NullEditTool.h" #include "SmartObjectHelperObject.h" #include "CameraObject.h" #include "AIAnchor.h" #include "AIReinforcementSpot.h" #include "SmartObject.h" #include "AreaBox.h" #include "AreaSphere.h" #include "WaterShapeObject.h" #include "VisAreaShapeObject.h" #include "ProtEntityObject.h" #include "PrefabObject.h" #include "Prefabs/PrefabManager.h" #include "SequenceObject.h" #include "RopeObject.h" #include "CharAttachHelper.h" #include "EnvironmentProbeObject.h" #include "AICoverSurface.h" #include "RefPicture.h" #include "Viewport.h" #include "GizmoManager.h" #include "ObjectLayerManager.h" #include "AxisGizmo.h" #include "ObjectPhysicsManager.h" #include "EditMode/ObjectMode.h" #include "IAgent.h" #include <IEntitySystem.h> #include "Geometry/EdMesh.h" #include "Material/MaterialManager.h" #include "IAIObject.h" #include "../EditMode/DeepSelection.h" #include "Objects/EnvironmentProbeObject.h" #include "GameEngine.h" #include <IRemoteCommand.h> #include "TrackView/TrackViewSequence.h" #include "TrackView/TrackViewSequenceManager.h" #include "Util/GuidUtil.h" #include <AzCore/Debug/Profiler.h> #include <AzCore/Math/Uuid.h> #include <AzFramework/Entity/EntityDebugDisplayBus.h> #include <AzToolsFramework/API/ToolsApplicationAPI.h> #include <AzToolsFramework/API/ComponentEntityObjectBus.h> #include <QMessageBox> #include <AzToolsFramework/Metrics/LyEditorMetricsBus.h> #include <LegacyEntityConversion/LegacyEntityConversion.h> #include <Plugins/ComponentEntityEditorPlugin/Objects/ComponentEntityObject.h> #include "ObjectManagerLegacyUndo.h" #include <AzCore/RTTI/BehaviorContext.h> /*! * Class Description used for object templates. * This description filled from Xml template files. */ class CXMLObjectClassDesc : public CObjectClassDesc { public: CObjectClassDesc* superType; QString type; QString category; QString fileSpec; GUID guid; public: REFGUID ClassID() { return guid; } ObjectType GetObjectType() { return superType->GetObjectType(); }; QString ClassName() { return type; }; QString Category() { return category; }; QObject* CreateQObject() const override { return superType->CreateQObject(); } QString GetTextureIcon() { return superType->GetTextureIcon(); }; QString GetFileSpec() { if (!fileSpec.isEmpty()) { return fileSpec; } else { return superType->GetFileSpec(); } }; virtual int GameCreationOrder() { return superType->GameCreationOrder(); }; }; void CBaseObjectsCache::AddObject(CBaseObject* object) { m_objects.push_back(object); if (object->GetType() == OBJTYPE_AZENTITY) { auto componentEntityObject = static_cast<CComponentEntityObject*>(object); m_entityIds.push_back(componentEntityObject->GetAssociatedEntityId()); } } ////////////////////////////////////////////////////////////////////////// // CObjectManager implementation. ////////////////////////////////////////////////////////////////////////// CObjectManager* g_pObjectManager = 0; ////////////////////////////////////////////////////////////////////////// CObjectManager::CObjectManager() : m_lastHideMask(0) , m_maxObjectViewDistRatio(0.00001f) , m_currSelection(&m_defaultSelection) , m_nLastSelCount(0) , m_bSelectionChanged(false) , m_selectCallback(nullptr) , m_currEditObject(nullptr) , m_bSingleSelection(false) , m_createGameObjects(true) , m_bGenUniqObjectNames(true) , m_gizmoManager(new CGizmoManager()) , m_pLayerManager(new CObjectLayerManager(this)) , m_pLoadProgress(nullptr) , m_loadedObjects(0) , m_totalObjectsToLoad(0) , m_pPhysicsManager(new CObjectPhysicsManager()) , m_bExiting(false) , m_isUpdateVisibilityList(false) , m_currentHideCount(CBaseObject::s_invalidHiddenID) , m_bInReloading(false) , m_bSkipObjectUpdate(false) , m_bLevelExporting(false) { g_pObjectManager = this; RegisterObjectClasses(); m_objectsByName.reserve(1024); m_converter.reset(aznew AZ::LegacyConversion::Converter()); LoadRegistry(); AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect( AzToolsFramework::GetEntityContextId()); } ////////////////////////////////////////////////////////////////////////// CObjectManager::~CObjectManager() { AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect(); m_converter.reset(); m_bExiting = true; SaveRegistry(); DeleteAllObjects(); delete m_gizmoManager; delete m_pLayerManager; delete m_pPhysicsManager; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::RegisterObjectClasses() { // Register default classes. CClassFactory* cf = CClassFactory::Instance(); cf->RegisterClass(new CTemplateObjectClassDesc<CTagPoint>("StdTagPoint", "", "Editor/ObjectIcons/TagPoint.bmp", OBJTYPE_TAGPOINT)); cf->RegisterClass(new CTemplateObjectClassDesc<CTagComment>("Comment", "Misc", "", OBJTYPE_TAGPOINT)); cf->RegisterClass(new CTemplateObjectClassDesc<CEntityObject>("StdEntity", "", "", OBJTYPE_ENTITY, 200, "*EntityClass")); cf->RegisterClass(new CTemplateObjectClassDesc<CSimpleEntity>("SimpleEntity", "", "", OBJTYPE_ENTITY, 202, "*.cgf;*.chr;*.cga;*.cdf")); cf->RegisterClass(new CTemplateObjectClassDesc<CGeomEntity>("GeomEntity", "Geom Entity", "", OBJTYPE_ENTITY, 201, "*.cgf;*.chr;*.cga;*.cdf")); cf->RegisterClass(new CTemplateObjectClassDesc<CActorEntity>("ActorEntity", "Actor Entity", "", OBJTYPE_ENTITY, 201, "*.prototype")); cf->RegisterClass(new CTemplateObjectClassDesc<CParticleEffectObject>("ParticleEntity", "Particle Entity", "", OBJTYPE_ENTITY, 200, "*ParticleEffects")); cf->RegisterClass(new CTemplateObjectClassDesc<CGroup>("Group", "", "", OBJTYPE_GROUP)); cf->RegisterClass(new CTemplateObjectClassDesc<CVolume>("StdVolume", "", "", OBJTYPE_VOLUME, 15)); cf->RegisterClass(new CTemplateObjectClassDesc<CSoundObject>("StdSoundObject", "", "", OBJTYPE_TAGPOINT)); cf->RegisterClass(new CTemplateObjectClassDesc<CShapeObject>("Shape", "Area", "", OBJTYPE_SHAPE, 50)); cf->RegisterClass(new CTemplateObjectClassDesc<CAIPathObject>("AIPath", "AI", "", OBJTYPE_SHAPE, 50)); cf->RegisterClass(new CTemplateObjectClassDesc<CAIShapeObject>("AIShape", "AI", "", OBJTYPE_SHAPE, 50)); cf->RegisterClass(new CTemplateObjectClassDesc<CAIOcclusionPlaneObject>("AIHorizontalOcclusionPlane", "AI", "", OBJTYPE_SHAPE, 50)); cf->RegisterClass(new CTemplateObjectClassDesc<CAIPerceptionModifierObject>("AIPerceptionModifier", "AI", "", OBJTYPE_SHAPE, 50)); cf->RegisterClass(new CTemplateObjectClassDesc<CAITerritoryObject>("Entity::AITerritory", "", "", OBJTYPE_SHAPE, 50)); cf->RegisterClass(new CTemplateObjectClassDesc<CAIWaveObject>("Entity::AIWave", "", "", OBJTYPE_ENTITY, 51)); cf->RegisterClass(new CTemplateObjectClassDesc<CAIPoint>("AIPoint", "AI", "", OBJTYPE_AIPOINT, 110)); cf->RegisterClass(new CTemplateObjectClassDesc<CAIAnchor>("AIAnchor", "AI", "", OBJTYPE_AIPOINT, 111)); cf->RegisterClass(new CTemplateObjectClassDesc<CAIReinforcementSpot>("AIReinforcementSpot", "AI", "", OBJTYPE_AIPOINT, 111)); cf->RegisterClass(new CTemplateObjectClassDesc<CAICoverSurface>("CoverSurface", "AI", "", OBJTYPE_AIPOINT, 151)); cf->RegisterClass(new CTemplateObjectClassDesc<CSmartObject>("SmartObject", "AI", "", OBJTYPE_AIPOINT, 111)); cf->RegisterClass(new CTemplateObjectClassDesc<CGameShapeObject>("GameVolume", "Custom", "", OBJTYPE_SHAPE, 50)); cf->RegisterClass(new CTemplateObjectClassDesc<CGameShapeLedgeObject>("Ledge", "Custom", "", OBJTYPE_SHAPE, 50)); cf->RegisterClass(new CTemplateObjectClassDesc<CGameShapeLedgeStaticObject>("LedgeStatic", "Custom", "", OBJTYPE_SHAPE, 50)); cf->RegisterClass(new CTemplateObjectClassDesc<CNavigationAreaObject>("NavigationArea", "AI", "", OBJTYPE_SHAPE, 50)); cf->RegisterClass(new CTemplateObjectClassDesc<CBrushObject>("Brush", "Brush", "", OBJTYPE_BRUSH, 150, "Objects/*.cgf")); cf->RegisterClass(new CTemplateObjectClassDesc<CCameraObject>("Camera", "Misc", "", OBJTYPE_ENTITY, 202)); cf->RegisterClass(new CTemplateObjectClassDesc<CCameraObjectTarget>("CameraTarget", "", "", OBJTYPE_ENTITY, 202)); cf->RegisterClass(new CTemplateObjectClassDesc<CAreaBox>("AreaBox", "Area", "", OBJTYPE_VOLUME, 52)); cf->RegisterClass(new CTemplateObjectClassDesc<CAreaSphere>("AreaSphere", "Area", "", OBJTYPE_VOLUME, 51)); cf->RegisterClass(new CTemplateObjectClassDesc<CWaterShapeObject>("WaterVolume", "Area", "", OBJTYPE_VOLUME, 16)); cf->RegisterClass(new CTemplateObjectClassDesc<CVisAreaShapeObject>("VisArea", "Area", "", OBJTYPE_VOLUME, 10)); cf->RegisterClass(new CTemplateObjectClassDesc<CPortalShapeObject>("Portal", "Area", "", OBJTYPE_VOLUME, 11)); cf->RegisterClass(new CTemplateObjectClassDesc<COccluderPlaneObject>("OccluderPlane", "Area", "", OBJTYPE_VOLUME, 12)); cf->RegisterClass(new CTemplateObjectClassDesc<COccluderAreaObject>("OccluderArea", "Area", "", OBJTYPE_VOLUME, 12)); cf->RegisterClass(new CTemplateObjectClassDesc<CProtEntityObject>("EntityArchetype", "Archetype Entity", "", OBJTYPE_ENTITY, 205, "*EntityArchetype")); cf->RegisterClass(new CTemplateObjectClassDesc<CPrefabObject>(PREFAB_OBJECT_CLASS_NAME, CATEGORY_PREFABS, "Editor/ObjectIcons/prefab.bmp", OBJTYPE_PREFAB, 210, "*Prefabs")); cf->RegisterClass(new CTemplateObjectClassDesc<CCloudObject>("CloudVolume", "", "", OBJTYPE_CLOUD, 150)); cf->RegisterClass(new CTemplateObjectClassDesc<CCloudGroup>("Cloud", "", "", OBJTYPE_CLOUD)); cf->RegisterClass(new CTemplateObjectClassDesc<CSmartObjectHelperObject>("SmartObjectHelper", "", "", OBJTYPE_OTHER)); cf->RegisterClass(new CTemplateObjectClassDesc<CDecalObject>("Decal", "Misc", "Editor/ObjectIcons/Decal.bmp", OBJTYPE_DECAL, 150)); cf->RegisterClass(new CTemplateObjectClassDesc<CSequenceObject>("SequenceObject", "", "Editor/ObjectIcons/sequence.bmp", OBJTYPE_OTHER, 950)); cf->RegisterClass(new CTemplateObjectClassDesc<CGravityVolumeObject>("GravityVolume", "Misc", "", OBJTYPE_OTHER, 50)); cf->RegisterClass(new CTemplateObjectClassDesc<CDistanceCloudObject>("DistanceCloud", "Misc", "Editor/ObjectIcons/Clouds.bmp", OBJTYPE_DISTANCECLOUD, 200)); #if ENABLE_CRY_PHYSICS cf->RegisterClass(new CTemplateObjectClassDesc<CRopeObject>("Rope", "Misc", "Editor/ObjectIcons/rope.bmp", OBJTYPE_OTHER, 300)); #endif cf->RegisterClass(new CTemplateObjectClassDesc<CCharacterAttachHelperObject>("CharAttachHelper", "Misc", "Editor/ObjectIcons/Magnet.bmp", OBJTYPE_ENTITY, 200)); cf->RegisterClass(new CTemplateObjectClassDesc<CEnvironementProbeObject>("EnvironmentProbe", "Misc", "Editor/ObjectIcons/environmentProbe.bmp", OBJTYPE_ENTITY, 202)); cf->RegisterClass(new CTemplateObjectClassDesc<CRefPicture>("ReferencePicture", "Misc", "", OBJTYPE_REFPICTURE)); #if ENABLE_CRY_PHYSICS cf->RegisterClass(new CTemplateObjectClassDesc<CConstraintEntity>("Entity::Constraint", "", "", OBJTYPE_ENTITY, 203, "*.cgf;*.chr;*.cga;*.cdf")); cf->RegisterClass(new CTemplateObjectClassDesc<CWindAreaEntity>("Entity::WindArea", "", "", OBJTYPE_ENTITY, 203, "*.cgf;*.chr;*.cga;*.cdf")); #endif // ENABLE_CRY_PHYSICS cf->RegisterClass(new CTemplateObjectClassDesc<CNavigationSeedPoint>("NavigationSeedPoint", "AI", "", OBJTYPE_TAGPOINT)); #if defined(USE_GEOM_CACHES) cf->RegisterClass(new CTemplateObjectClassDesc<CGeomCacheEntity>("Entity::GeomCache", "", "", OBJTYPE_GEOMCACHE, 204, "*.cax")); #endif LoadRegistry(); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::SaveRegistry() { } void CObjectManager::LoadRegistry() { } ////////////////////////////////////////////////////////////////////////// CBaseObject* CObjectManager::NewObject(CObjectClassDesc* cls, CBaseObject* prev, const QString& file, const char* newObjectName) { // Suspend undo operations when initializing object. GetIEditor()->SuspendUndo(); CBaseObjectPtr obj; { obj = qobject_cast<CBaseObject*>(cls->CreateQObject()); obj->SetClassDesc(cls); CObjectLayer* destLayer = obj->SupportsLayers() ? m_pLayerManager->GetCurrentLayer() : m_pLayerManager->FindLayerByName("Main"); obj->SetLayer(destLayer); obj->InitVariables(); obj->m_guid = AZ::Uuid::CreateRandom(); // generate uniq GUID for this object. GetIEditor()->GetErrorReport()->SetCurrentValidatorObject(obj); if (obj->Init(GetIEditor(), prev, file)) { if ((newObjectName)&&(newObjectName[0])) { obj->SetName(newObjectName); } else { if (obj->GetName().isEmpty()) { obj->GenerateUniqueName(); } } // 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()) { // AZ entity creations are handled through the AZ undo system. if (obj->GetType() != OBJTYPE_AZENTITY) { GetIEditor()->RecordUndo(new CUndoBaseObjectNew(obj)); // check for script entities const char* scriptClassName = ""; CEntityObject* entityObj = qobject_cast<CEntityObject*>(obj); QByteArray entityClass; // Leave it outside of the if. Otherwise buffer is deleted. if (entityObj) { entityClass = entityObj->GetEntityClass().toUtf8(); scriptClassName = entityClass.data(); } using namespace AzToolsFramework; EditorMetricsEventsBus::Broadcast(&EditorMetricsEventsBus::Events::LegacyEntityCreated, cls->ClassName().toUtf8().data(), scriptClassName); } } return obj; } ////////////////////////////////////////////////////////////////////////// CBaseObject* CObjectManager::NewObject(CObjectArchive& ar, CBaseObject* pUndoObject, bool bMakeNewId) { XmlNodeRef objNode = ar.node; // Load all objects from XML. QString typeName; GUID id = GUID_NULL; GUID idInPrefab = GUID_NULL; if (!objNode->getAttr("Type", typeName)) { return 0; } if (!objNode->getAttr("Id", id)) { // Make new ID for object that doesn't have if. id = AZ::Uuid::CreateRandom(); } idInPrefab = id; if (bMakeNewId) { // Make new guid for this object. GUID newId = AZ::Uuid::CreateRandom(); ar.RemapID(id, newId); // Mark this id remapped. id = newId; } CBaseObjectPtr pObject; if (pUndoObject) { // if undoing restore object pointer. pObject = pUndoObject; } else { // New object creation. // Suspend undo operations when initializing object. CUndoSuspend undoSuspender; QString entityClass; if (objNode->getAttr("EntityClass", entityClass)) { typeName = typeName + "::" + entityClass; } CObjectClassDesc* cls = FindClass(typeName); if (!cls) { CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "RuntimeClass %s not registered", typeName.toUtf8().data()); return 0; } pObject = qobject_cast<CBaseObject*>(cls->CreateQObject()); assert(pObject); pObject->SetClassDesc(cls); pObject->m_guid = id; CObjectLayer* destLayer = pObject->SupportsLayers() ? m_pLayerManager->GetCurrentLayer() : m_pLayerManager->FindLayerByName("Main"); pObject->SetLayer(destLayer); pObject->InitVariables(); QString objName; objNode->getAttr("Name", objName); pObject->m_name = objName; pObject->SetIdInPrefab(idInPrefab); CBaseObject* obj = FindObject(pObject->GetId()); if (obj) { QString layerName; if (obj->GetLayer()) { layerName = " [" + obj->GetLayer()->GetName() + "]"; } // If id is taken. QString error; error = QObject::tr("[Error] Object %1 already exists in the Object Manager and has been deleted as it is a duplicate of object %2 in layer %3.").arg(pObject->m_name, obj->GetName(), layerName); CLogFile::WriteLine(error.toUtf8().data()); if (!GetIEditor()->IsInTestMode() && !GetIEditor()->IsInLevelLoadTestMode()) { CErrorRecord errorRecord; errorRecord.pObject = obj; errorRecord.count = 1; errorRecord.severity = CErrorRecord::ESEVERITY_ERROR; errorRecord.error = error; errorRecord.description = "Possible duplicate objects being loaded, potential fix is to remove duplicate objects from level files."; GetIEditor()->GetErrorReport()->ReportError(errorRecord); } 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); assert(pObject->GetLayer()); if (pObject != 0 && pUndoObject == 0) { // If new object with no undo, record it. if (CUndo::IsRecording()) { 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 QString& typeName, CBaseObject* prev, const QString& file, const char* newObjectName) { // [9/22/2009 evgeny] If it is "Entity", figure out if a CEntity subclass is actually needed QString fullName = typeName + "::" + file; CObjectClassDesc* cls = FindClass(fullName); if (!cls) { cls = FindClass(typeName); } if (!cls) { GetIEditor()->GetSystem()->GetILog()->Log("Warning: RuntimeClass %s (as well as %s) not registered", typeName.toUtf8().data(), fullName.toUtf8().data()); return 0; } CBaseObject* pObject = NewObject(cls, prev, file, newObjectName); return pObject; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::DeleteObject(CBaseObject* obj) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); if (m_currEditObject == obj) { EndEditParams(); } if (!obj) { return; } // If object already deleted. if (obj->CheckFlags(OBJFLAG_DELETED)) { return; } NotifyObjectListeners(obj, CBaseObject::ON_PREDELETE); obj->NotifyListeners(CBaseObject::ON_PREDELETE); // Check if object is a group then delete all childs. if (qobject_cast<CGroup*>(obj)) { ((CGroup*)obj)->DeleteAllMembers(); } else if (qobject_cast<CAITerritoryObject*>(obj)) { FindAndRenameProperty2("aiterritory_Territory", obj->GetName(), "<None>"); } else if (qobject_cast<CAIWaveObject*>(obj)) { FindAndRenameProperty2("aiwave_Wave", obj->GetName(), "<None>"); } // Must be after object DetachAll to support restoring Parent/Child relations. // AZ entity deletions are handled through the AZ undo system. if (CUndo::IsRecording() && obj->GetType() != OBJTYPE_AZENTITY) { // Store undo for all child objects. for (int i = 0; i < obj->GetChildCount(); i++) { obj->GetChild(i)->StoreUndo("DeleteParent"); } CUndo::Record(new CUndoBaseObjectDelete(obj)); } OnObjectModified(obj, true, false); AABB objAAB; obj->GetBoundBox(objAAB); GetIEditor()->GetGameEngine()->OnAreaModified(objAAB); // Release game resources. CBaseObject* pParent = obj->GetParent(); obj->Done(); if (pParent) { if (qobject_cast<CGroup*>(pParent) && !qobject_cast<CPrefabObject*>(pParent)) { ((CGroup*)pParent)->Sync(); AABB aabb; ((CGroup*)pParent)->GetBoundBox(aabb); ((CGroup*)pParent)->UpdatePivot(aabb.min); } else { pParent->UpdateGroup(); } } NotifyObjectListeners(obj, CBaseObject::ON_DELETE); RemoveObject(obj); RefreshEntitiesAssignedToSelectedTnW(); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::DeleteSelection(CSelectionGroup* pSelection) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); if (pSelection == NULL) { return; } // if the object contains an entity which has link, the link information should be recorded for undo separately. p if (CUndo::IsRecording()) { for (int i = 0, iSize(pSelection->GetCount()); i < iSize; ++i) { CBaseObject* pObj = pSelection->GetObject(i); if (!qobject_cast<CEntityObject*>(pObj)) { continue; } CEntityObject* pEntity = (CEntityObject*)pObj; if (pEntity->GetEntityLinkCount() <= 0) { continue; } CEntityObject::StoreUndoEntityLink(pSelection); break; } } AzToolsFramework::EntityIdList selectedComponentEntities; for (int i = 0, iObjSize(pSelection->GetCount()); i < iObjSize; i++) { CBaseObject* object = pSelection->GetObject(i); // AZ::Entity deletion is handled through AZ undo system (DeleteSelected bus call below). if (object->GetType() != OBJTYPE_AZENTITY) { DeleteObject(object); } else { AZ::EntityId id; EBUS_EVENT_ID_RESULT(id, object, AzToolsFramework::ComponentEntityObjectRequestBus, GetAssociatedEntityId); if (id.IsValid()) { selectedComponentEntities.push_back(id); } } } // Delete AZ (component) entities. if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { EBUS_EVENT(AzToolsFramework::ToolsApplicationRequests::Bus, DeleteEntities, selectedComponentEntities); } else { EBUS_EVENT(AzToolsFramework::ToolsApplicationRequests::Bus, DeleteEntitiesAndAllDescendants, selectedComponentEntities); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::DeleteAllObjects() { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); GetIEditor()->GetPrefabManager()->SetSkipPrefabUpdate(true); EndEditParams(); ClearSelection(); int i; InvalidateVisibleList(); // Delete all selection groups. std::vector<CSelectionGroup*> sel; stl::map_to_vector(m_selections, sel); for (i = 0; i < sel.size(); i++) { delete sel[i]; } m_selections.clear(); TBaseObjects objectsHolder; GetAllObjects(objectsHolder); // Clear map. Need to do this before deleting objects in case someone tries to get object list during shutdown. m_objects.clear(); m_objectsByName.clear(); for (i = 0; i < objectsHolder.size(); i++) { objectsHolder[i]->Done(); } //! Delete object instances. objectsHolder.clear(); // Clear name map. m_nameNumbersMap.clear(); m_aiTerritoryObjects.clear(); m_aiWaveObjects.clear(); m_animatedAttachedEntities.clear(); RefreshEntitiesAssignedToSelectedTnW(); GetIEditor()->GetPrefabManager()->SetSkipPrefabUpdate(false); } CBaseObject* CObjectManager::CloneObject(CBaseObject* obj) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); 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 { CBaseObject* result = stl::find_in_map(m_objects, guid, (CBaseObject*)0); return result; } ////////////////////////////////////////////////////////////////////////// CBaseObject* CObjectManager::FindObject(const QString& sName) const { const AZ::Crc32 crc(sName.toUtf8().data(), sName.toUtf8().count(), true); auto iter = m_objectsByName.find(crc); if (iter != m_objectsByName.end()) { return iter->second; } return nullptr; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::FindObjectsOfType(ObjectType type, std::vector<CBaseObject*>& result) { result.clear(); CBaseObjectsArray objects; GetObjects(objects); for (size_t i = 0, n = objects.size(); i < n; ++i) { if (objects[i]->GetType() == type) { result.push_back(objects[i]); } } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::FindObjectsOfType(const QMetaObject* pClass, std::vector<CBaseObject*>& result) { result.clear(); CBaseObjectsArray objects; GetObjects(objects); for (size_t i = 0, n = objects.size(); i < n; ++i) { CBaseObject* pObject = objects[i]; if (pObject->metaObject() == pClass) { result.push_back(pObject); } } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::FindObjectsInAABB(const AABB& aabb, std::vector<CBaseObject*>& result) const { result.clear(); CBaseObjectsArray objects; GetObjects(objects); for (size_t i = 0, n = objects.size(); i < n; ++i) { CBaseObject* pObject = objects[i]; AABB aabbObj; pObject->GetBoundBox(aabbObj); if (aabb.IsIntersectBox(aabbObj)) { result.push_back(pObject); } } } ////////////////////////////////////////////////////////////////////////// bool CObjectManager::AddObject(CBaseObject* obj) { CBaseObjectPtr p = stl::find_in_map(m_objects, obj->GetId(), 0); if (p) { CErrorRecord err; err.error = QObject::tr("New Object %1 has Duplicate GUID %2, New Object Ignored").arg(obj->GetName()).arg(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; // Handle adding object to type-specific containers if needed { if (CAITerritoryObject* territoryObj = qobject_cast<CAITerritoryObject*>(obj)) { m_aiTerritoryObjects.insert(territoryObj); } else if (CAIWaveObject* waveObj = qobject_cast<CAIWaveObject*>(obj)) { m_aiWaveObjects.insert(waveObj); } else if (CEntityObject* entityObj = qobject_cast<CEntityObject*>(obj)) { CEntityObject::EAttachmentType attachType = entityObj->GetAttachType(); if (attachType == CEntityObject::EAttachmentType::eAT_GeomCacheNode || attachType == CEntityObject::EAttachmentType::eAT_CharacterBone) { m_animatedAttachedEntities.insert(entityObj); } } } const AZ::Crc32 nameCrc(obj->GetName().toUtf8().data(), obj->GetName().toUtf8().count(), true); m_objectsByName[nameCrc] = obj; RegisterObjectName(obj->GetName()); InvalidateVisibleList(); NotifyObjectListeners(obj, CBaseObject::ON_ADD); return true; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::RemoveObject(CBaseObject* obj) { AzToolsFramework::EditorMetricsEventBusSelectionChangeHelper selectionChangeMetricsHelper; assert(obj != 0); InvalidateVisibleList(); // Handle removing object from type-specific containers if needed { if (CAITerritoryObject* territoryObj = qobject_cast<CAITerritoryObject*>(obj)) { m_aiTerritoryObjects.erase(territoryObj); } else if (CAIWaveObject* waveObj = qobject_cast<CAIWaveObject*>(obj)) { m_aiWaveObjects.erase(waveObj); } else if (CEntityObject* entityObj = qobject_cast<CEntityObject*>(obj)) { m_animatedAttachedEntities.erase(entityObj); } } // Remove this object from selection groups. m_currSelection->RemoveObject(obj); std::vector<CSelectionGroup*> sel; stl::map_to_vector(m_selections, sel); for (int i = 0; i < sel.size(); i++) { sel[i]->RemoveObject(obj); } m_objectsByName.erase(AZ::Crc32(obj->GetName().toUtf8().data(), obj->GetName().toUtf8().count(), true)); // Need to erase this last since it is a smart pointer and can end up deleting the object if it is the last reference to it being kept m_objects.erase(obj->GetId()); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::GetAllObjects(TBaseObjects& 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(REFGUID oldGuid, REFGUID newGuid) { Objects::iterator it = m_objects.find(oldGuid); if (it != m_objects.end()) { CBaseObjectPtr pRemappedObject = (*it).second; pRemappedObject->SetId(newGuid); m_objects.erase(it); m_objects.insert(std::make_pair(newGuid, pRemappedObject)); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::NotifyPrefabObjectChanged(CBaseObject* pObject) { NotifyObjectListeners(pObject, CBaseObject::ON_PREFAB_CHANGED); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::ShowDuplicationMsgWarning(CBaseObject* obj, const QString& newName, bool bShowMsgBox) const { CBaseObject* pExisting = FindObject(newName); if (pExisting) { QString sRenameWarning = QObject::tr("%1 \"%2\" was NOT renamed to \"%3\" because %4 with the same name already exists.") .arg(obj->GetClassDesc()->ClassName()) .arg(obj->GetName()) .arg(newName) .arg(pExisting->GetClassDesc()->ClassName() ); // If id is taken. CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_WARNING, sRenameWarning.toUtf8().data()); if (bShowMsgBox) { QMessageBox::critical(QApplication::activeWindow(), QString(), sRenameWarning); } } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::ChangeObjectName(CBaseObject* obj, const QString& newName) { assert(obj); if (newName != obj->GetName()) { if (IsDuplicateObjectName(newName)) { return; } // Remove previous name from map const AZ::Crc32 oldNameCrc(obj->GetName().toUtf8().data(), obj->GetName().count(), true); m_objectsByName.erase(oldNameCrc); obj->SetName(newName); // Make sure object name edit field is updated in the object properties panel. obj->UpdateEditParams(); // Add new name to map const AZ::Crc32 nameCrc(newName.toUtf8().data(), newName.count(), true); m_objectsByName[nameCrc] = obj; } } ////////////////////////////////////////////////////////////////////////// int CObjectManager::GetObjectCount() const { return m_objects.size(); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::GetObjects(CBaseObjectsArray& objects, const CObjectLayer* layer) const { objects.clear(); objects.reserve(m_objects.size()); for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it) { if (layer == 0 || it->second->GetLayer() == layer) { objects.push_back(it->second); } } } void CObjectManager::GetObjects(DynArray<CBaseObject*>& objects, const CObjectLayer* layer) const { CBaseObjectsArray objectArray; GetObjects(objectArray, layer); objects.clear(); for (int i = 0, iCount(objectArray.size()); i < iCount; ++i) { objects.push_back(objectArray[i]); } } void CObjectManager::GetObjects(CBaseObjectsArray& objects, BaseObjectFilterFunctor const& filter) const { objects.clear(); objects.reserve(m_objects.size()); for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it) { assert(it->second); if (filter.first(*it->second, filter.second)) { objects.push_back(it->second); } } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::GetCameras(std::vector<CCameraObject*>& objects) { objects.clear(); for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject* object = it->second; if (qobject_cast<CCameraObject*>(object)) { // Only consider camera sources. if (object->IsLookAtTarget()) { continue; } objects.push_back((CCameraObject*)object); } } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::SendEvent(ObjectEvent event) { if (event == EVENT_RELOAD_ENTITY) { m_bInReloading = true; } if (event == EVENT_INGAME || event == EVENT_OUTOFGAME || event == EVENT_UNLOAD_ENTITY || event == EVENT_RELOAD_ENTITY) { GetIEditor()->GetPrefabManager()->SetSkipPrefabUpdate(true); } for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject* obj = it->second; if (obj->GetGroup() && event != EVENT_PRE_EXPORT) { continue; } obj->OnEvent(event); } if (event == EVENT_INGAME || event == EVENT_OUTOFGAME || event == EVENT_UNLOAD_ENTITY || event == EVENT_RELOAD_ENTITY) { GetIEditor()->GetPrefabManager()->SetSkipPrefabUpdate(false); } if (event == EVENT_RELOAD_ENTITY) { m_bInReloading = false; GetIEditor()->Notify(eNotify_OnReloadTrackView); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::SendEvent(ObjectEvent event, const AABB& bounds) { AABB 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() { if (m_bSkipObjectUpdate) { return; } QWidget* prevActiveWindow = QApplication::activeWindow(); CheckAndFixSelection(); // Restore focus if it changed. if (prevActiveWindow && QApplication::activeWindow() != prevActiveWindow) { prevActiveWindow->setFocus(); } m_pPhysicsManager->Update(); if (!GetIEditor()->IsNewViewportInteractionModelEnabled()) { UpdateAttachedEntities(); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::HideObject(CBaseObject* obj, bool hide) { assert(obj != 0); if (hide) { obj->SetHidden(hide, ++m_currentHideCount); } else { obj->SetHidden(false); } InvalidateVisibleList(); } void CObjectManager::ShowLastHiddenObject() { uint64 mostRecentID = CBaseObject::s_invalidHiddenID; CBaseObject* mostRecentObject = nullptr; for (auto it : m_objects) { CBaseObject* obj = it.second; uint64 hiddenID = obj->GetHideOrder(); if (hiddenID > mostRecentID) { mostRecentID = hiddenID; mostRecentObject = obj; } } if (mostRecentObject != nullptr) { mostRecentObject->SetHidden(false); } } ////////////////////////////////////////////////////////////////////////// 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, bool bUseMask) { assert(obj); if (obj == NULL) { return false; } // Check if can be selected. if (bUseMask && (!(obj->GetType() & gSettings.objectSelectMask))) { return false; } if (m_selectCallback) { if (!m_selectCallback->OnSelectObject(obj)) { return true; } } AzToolsFramework::EditorMetricsEventBusSelectionChangeHelper selectionChangeMetricsHelper; m_currSelection->AddObject(obj); // while in ComponentMode we never explicitly change selection (the entity will always be selected). // this check is to handle the case where an undo or redo action has occurred and // the entity has been destroyed and recreated as part of the deserialization step. // we want the internal state to stay consistent but do not want to notify other systems of the change. if (AzToolsFramework::ComponentModeFramework::InComponentMode()) { obj->SetSelected(true); } else { SetObjectSelected(obj, true); GetIEditor()->Notify(eNotify_OnSelectionChange); } return true; } void CObjectManager::SelectEntities(std::set<CEntityObject*>& s) { AzToolsFramework::EditorMetricsEventBusSelectionChangeHelper selectionChangeMetricsHelper; for (std::set<CEntityObject*>::iterator it = s.begin(), end = s.end(); it != end; ++it) { SelectObject(*it); } } void CObjectManager::UnselectObject(CBaseObject* obj) { AzToolsFramework::EditorMetricsEventBusSelectionChangeHelper selectionChangeMetricsHelper; // while in ComponentMode we never explicitly change selection (the entity will always be selected). // this check is to handle the case where an undo or redo action has occurred and // the entity has been destroyed and recreated as part of the deserialization step. // we want the internal state to stay consistent but do not want to notify other systems of the change. if (AzToolsFramework::ComponentModeFramework::InComponentMode()) { obj->SetSelected(false); } else { SetObjectSelected(obj, false); } m_currSelection->RemoveObject(obj); } CSelectionGroup* CObjectManager::GetSelection(const QString& name) const { CSelectionGroup* selection = stl::find_in_map(m_selections, name, (CSelectionGroup*)0); return selection; } void CObjectManager::GetNameSelectionStrings(QStringList& names) { for (TNameSelectionMap::iterator it = m_selections.begin(); it != m_selections.end(); ++it) { names.push_back(it->first); } } void CObjectManager::NameSelection(const QString& 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(); } void CObjectManager::SerializeNameSelection(XmlNodeRef& rootNode, bool bLoading) { if (!rootNode) { return; } _smart_ptr<CSelectionGroup> tmpGroup(0); QString selRootStr("NameSelection"); QString selNodeStr("NameSelectionNode"); QString selNodeNameStr("name"); QString idStr("id"); QString objAttrStr("obj"); XmlNodeRef startNode = rootNode->findChild(selRootStr.toUtf8().data()); if (bLoading) { m_selections.erase(m_selections.begin(), m_selections.end()); if (startNode) { for (int selNodeNo = 0; selNodeNo < startNode->getChildCount(); ++selNodeNo) { XmlNodeRef selNode = startNode->getChild(selNodeNo); tmpGroup = new CSelectionGroup; for (int objIDNodeNo = 0; objIDNodeNo < selNode->getChildCount(); ++objIDNodeNo) { GUID curID = GUID_NULL; XmlNodeRef idNode = selNode->getChild(objIDNodeNo); if (!idNode->getAttr(idStr.toUtf8().data(), curID)) { continue; } if (curID != GUID_NULL) { if (GetIEditor()->GetObjectManager()->FindObject(curID)) { tmpGroup->AddObject(GetIEditor()->GetObjectManager()->FindObject(curID)); } } } if (tmpGroup->GetCount() > 0) { QString nameStr; if (!selNode->getAttr(selNodeNameStr.toUtf8().data(), nameStr)) { continue; } tmpGroup->SetName(nameStr); m_selections[nameStr] = tmpGroup; } } } } else { startNode = rootNode->newChild(selRootStr.toUtf8().data()); CSelectionGroup* objSelection = 0; for (TNameSelectionMap::iterator it = m_selections.begin(); it != m_selections.end(); ++it) { XmlNodeRef selectionNameNode = startNode->newChild(selNodeStr.toUtf8().data()); selectionNameNode->setAttr(selNodeNameStr.toUtf8().data(), it->first.toUtf8().data()); objSelection = it->second; if (!objSelection) { continue; } if (objSelection->GetCount() == 0) { continue; } for (int i = 0; i < objSelection->GetCount(); ++i) { if (objSelection->GetObject(i)) { XmlNodeRef objNode = selectionNameNode->newChild(objAttrStr.toUtf8().data()); objNode->setAttr(idStr.toUtf8().data(), GuidUtil::ToString(objSelection->GetObject(i)->GetId())); } } } } } ////////////////////////////////////////////////////////////////////////// int CObjectManager::ClearSelection() { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); AzToolsFramework::EditorMetricsEventBusSelectionChangeHelper selectionChangeMetricsHelper; // Make sure to unlock selection. GetIEditor()->LockSelection(false); int numSel = m_currSelection->GetCount(); // Handle Undo/Redo of Component Entities bool isUndoRecording = GetIEditor()->IsUndoRecording(); if (isUndoRecording) { m_processingBulkSelect = true; GetIEditor()->RecordUndo(new CUndoBaseObjectClearSelection(*m_currSelection)); } // Handle legacy entities separately so the selection group can be cleared safely. // This prevents every AzEntity from being removed one by one from a vector. m_currSelection->RemoveAllExceptLegacySet(); // Kick off Deselect for Legacy Entities for (CBaseObjectPtr legacyObject : m_currSelection->GetLegacyObjects()) { if (isUndoRecording && legacyObject->IsSelected()) { GetIEditor()->RecordUndo(new CUndoBaseObjectSelect(legacyObject)); } SetObjectSelected(legacyObject, false); } // Legacy set is cleared m_defaultSelection.RemoveAll(); m_currSelection = &m_defaultSelection; m_bSelectionChanged = true; // Unselect all component entities as one bulk operation instead of individually AzToolsFramework::ToolsApplicationRequestBus::Broadcast( &AzToolsFramework::ToolsApplicationRequests::SetSelectedEntities, AzToolsFramework::EntityIdList()); m_processingBulkSelect = false; if (!m_bExiting) { GetIEditor()->Notify(eNotify_OnSelectionChange); } return numSel; } ////////////////////////////////////////////////////////////////////////// int CObjectManager::InvertSelection() { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); AzToolsFramework::EditorMetricsEventBusSelectionChangeHelper selectionChangeMetricsHelper; 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 QString& name) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); CSelectionGroup* selection = stl::find_in_map(m_selections, name, (CSelectionGroup*)0); if (selection) { AzToolsFramework::EditorMetricsEventBusSelectionChangeHelper selectionChangeMetricsHelper; UnselectCurrent(); assert(selection != 0); m_currSelection = selection; SelectCurrent(); } } void CObjectManager::RemoveSelection(const QString& name) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); AzToolsFramework::EditorMetricsEventBusSelectionChangeHelper selectionChangeMetricsHelper; QString 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); } } //! Checks the state of the current selection and fixes it if necessary - Used when AZ Code modifies the selection void CObjectManager::CheckAndFixSelection() { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); bool bObjectMode = qobject_cast<CObjectMode*>(GetIEditor()->GetEditTool()) != nullptr; AzToolsFramework::EditorMetricsEventBusSelectionChangeHelper selectionChangeMetricsHelper; if (m_currSelection->GetCount() == 0) { // Nothing selected. EndEditParams(); if (bObjectMode) { GetIEditor()->ShowTransformManipulator(false); } } else if (m_currSelection->GetCount() == 1) { if (!m_bSingleSelection) { EndEditParams(); } CBaseObject* newSelObject = m_currSelection->GetObject(0); // Single object selected. if (m_currEditObject != m_currSelection->GetObject(0)) { m_bSelectionChanged = false; if (!m_currEditObject || (m_currEditObject->metaObject() != newSelObject->metaObject())) { // If old object and new objects are of different classes. EndEditParams(); } if (GetIEditor()->GetEditTool() && GetIEditor()->GetEditTool()->IsUpdateUIPanel()) { BeginEditParams(newSelObject, OBJECT_EDIT); } //AfxGetMainWnd()->SetFocus(); } } else if (m_currSelection->GetCount() > 1) { // Multiple objects are selected. if (m_bSelectionChanged && bObjectMode) { m_bSelectionChanged = false; m_nLastSelCount = m_currSelection->GetCount(); EndEditParams(); bool bAllSameType = m_currSelection->SameObjectType(); if (bAllSameType && m_currSelection->GetObject(0)->GetType() == OBJTYPE_SOLID) { m_currEditObject = m_currSelection->GetObject(m_nLastSelCount - 1); } else { m_currEditObject = m_currSelection->GetObject(0); } m_currEditObject->BeginEditMultiSelParams(bAllSameType); } } } void CObjectManager::SelectCurrent() { AzToolsFramework::EditorMetricsEventBusSelectionChangeHelper selectionChangeMetricsHelper; AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); 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() { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); // Make sure to unlock selection. GetIEditor()->LockSelection(false); // Unselect all component entities as one bulk operation instead of individually AzToolsFramework::EntityIdList selectedEntities; EBUS_EVENT(AzToolsFramework::ToolsApplicationRequests::Bus, SetSelectedEntities, selectedEntities); 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) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); int currentHideMask = GetIEditor()->GetDisplaySettings()->GetObjectHideMask(); if (m_lastHideMask != currentHideMask) { // a setting has changed which may cause the set of currently visible objects to change, so invalidate the serial number // so that viewports and anyone else that needs to update settings knows it has to. m_lastHideMask = currentHideMask; ++m_visibilitySerialNumber; } // the object manager itself has a visibility list, so it also has to update its cache when the serial has changed if (m_visibilitySerialNumber != m_lastComputedVisibility) { m_lastComputedVisibility = m_visibilitySerialNumber; UpdateVisibilityList(); } bool viewIsDirty = dc.settings->IsDisplayHelpers(); // displaying helpers require computing all the bound boxes and things anyway. if (!viewIsDirty) { if (CBaseObjectsCache* cache = dc.view->GetVisibleObjectsCache()) { // if the current rendering viewport has an out-of-date cache serial number, it needs to be refreshed too. // views set their cache empty when they indicate they need to force a refresh. if ((cache->GetObjectCount() == 0) || (cache->GetSerialNumber() != m_visibilitySerialNumber)) { viewIsDirty = true; } } } if (viewIsDirty) { FindDisplayableObjects(dc, true); // this also actually draws the helpers. // Also broadcast for anyone else that needs to draw global debug to do so now AzFramework::DebugDisplayEventBus::Broadcast(&AzFramework::DebugDisplayEvents::DrawGlobalDebugInfo); } if (m_gizmoManager) { m_gizmoManager->Display(dc); } } void CObjectManager::ForceUpdateVisibleObjectCache(DisplayContext& dc) { FindDisplayableObjects(dc, false); } void CObjectManager::FindDisplayableObjects(DisplayContext& dc, bool bDisplay) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); CBaseObjectsCache* pDispayedViewObjects = dc.view->GetVisibleObjectsCache(); if (!pDispayedViewObjects) { return; } pDispayedViewObjects->SetSerialNumber(m_visibilitySerialNumber); // update viewport to be latest serial number const CCamera& camera = GetIEditor()->GetSystem()->GetViewCamera(); AABB bbox; bbox.min.zero(); bbox.max.zero(); pDispayedViewObjects->ClearObjects(); pDispayedViewObjects->Reserve(m_visibleObjects.size()); CEditTool* pEditTool = GetIEditor()->GetEditTool(); const bool newViewportInteractionModelEnabled = GetIEditor()->IsNewViewportInteractionModelEnabled(); 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)) { pDispayedViewObjects->AddObject(obj); if (bDisplay && dc.settings->IsDisplayHelpers() && (gSettings.viewports.nShowFrozenHelpers || !obj->IsFrozen())) { if (!newViewportInteractionModelEnabled) { obj->Display(dc); } if (pEditTool) { pEditTool->DrawObjectHelpers(obj, dc); } } } } } else { CSelectionGroup* pSelection = GetSelection(); if (pSelection && pSelection->GetCount() > 1) { AABB mergedAABB; mergedAABB.Reset(); bool bAllSolids = true; for (int i = 0, iCount(pSelection->GetCount()); i < iCount; ++i) { CBaseObject* pObj(pSelection->GetObject(i)); if (pObj == NULL) { continue; } AABB aabb; pObj->GetBoundBox(aabb); mergedAABB.Add(aabb); if (bAllSolids && pObj->GetType() != OBJTYPE_SOLID) { bAllSolids = false; } } if (!bAllSolids) { pSelection->GetObject(0)->CBaseObject::DrawDimensions(dc, &mergedAABB); } else { pSelection->GetObject(0)->DrawDimensions(dc, &mergedAABB); } } int numVis = m_visibleObjects.size(); for (int i = 0; i < numVis; i++) { CBaseObject* obj = m_visibleObjects[i]; if (obj && obj->IsInCameraView(camera)) { // Check if object is too far. float visRatio = obj->GetCameraVisRatio(camera); if (visRatio > m_maxObjectViewDistRatio || (dc.flags & DISPLAY_SELECTION_HELPERS) || obj->IsSelected()) { pDispayedViewObjects->AddObject(obj); if (bDisplay && dc.settings->IsDisplayHelpers() && (gSettings.viewports.nShowFrozenHelpers || !obj->IsFrozen()) && !obj->CheckFlags(OBJFLAG_HIDE_HELPERS)) { if (!newViewportInteractionModelEnabled) { obj->Display(dc); } if (pEditTool) { pEditTool->DrawObjectHelpers(obj, dc); } } } } } } } void CObjectManager::BeginEditParams(CBaseObject* obj, int flags) { assert(obj != 0); if (obj == m_currEditObject) { return; } if (GetSelection()->GetCount() > 1) { return; } QWidget* prevActiveWindow = QApplication::activeWindow(); 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, false); } m_bSingleSelection = true; m_currEditObject->BeginEditParams(GetIEditor(), flags); // Restore focus if it changed. // OBJECT_EDIT is used by the EntityOutliner when items are selected. Using it here to prevent shifting focus to the EntityInspector on select. if (!(flags & OBJECT_EDIT) && prevActiveWindow && QApplication::activeWindow() != prevActiveWindow) { prevActiveWindow->setFocus(); } } 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; // don't need to clear for ungroup } //! Select objects within specified distance from given position. int CObjectManager::SelectObjects(const AABB& box, bool bUnselect) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); int numSel = 0; AABB objBounds; for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject* obj = it->second; if (obj->IsHidden()) { continue; } if (obj->GetGroup()) { continue; } obj->GetBoundBox(objBounds); if (box.IsIntersectBox(objBounds)) { numSel++; if (!bUnselect) { SelectObject(obj); } else { UnselectObject(obj); } } // If its group. if (qobject_cast<CGroup*>(obj)) { numSel += ((CGroup*)obj)->SelectObjects(box, bUnselect); } } return numSel; } ////////////////////////////////////////////////////////////////////////// int CObjectManager::MoveObjects(const AABB& box, const Vec3& offset, ImageRotationDegrees rotation, bool bIsCopy) { AABB objBounds; Vec3 src = (box.min + box.max) / 2; Vec3 dst = src + offset; float alpha = 0.0f; switch (rotation) { case ImageRotationDegrees::Rotate90: alpha = gf_halfPI; break; case ImageRotationDegrees::Rotate180: alpha = gf_PI; break; case ImageRotationDegrees::Rotate270: alpha = gf_PI + gf_halfPI; break; default: break; } float cosa = cos(alpha); float sina = sin(alpha); for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject* obj = it->second; if (obj->GetParent()) { continue; } if (obj->GetGroup()) { continue; } obj->GetBoundBox(objBounds); if (box.IsIntersectBox(objBounds)) { if (rotation == ImageRotationDegrees::Rotate0) { obj->SetPos(obj->GetPos() - src + dst); } else { Vec3 pos = obj->GetPos() - src; Vec3 newPos(pos); newPos.x = cosa * pos.x - sina * pos.y; newPos.y = sina * pos.x + cosa * pos.y; obj->SetPos(newPos + dst); Quat q; obj->SetRotation(q.CreateRotationZ(alpha) * obj->GetRotation()); } } } return 0; } bool CObjectManager::IsObjectDeletionAllowed(CBaseObject* pObject) { if (!pObject) { return false; } // Test AI object against AI/Physics activation uint32 flags = GetIEditor()->GetDisplaySettings()->GetSettings(); if ((flags & SETTINGS_PHYSICS) != 0) { if (qobject_cast<CEntityObject*>(pObject)) { CEntityObject* pEntityObj = (CEntityObject*)pObject; if (pEntityObj) { IEntity* pIEntity = pEntityObj->GetIEntity(); if (pIEntity) { if (pIEntity->HasAI()) { QMessageBox::critical(QApplication::activeWindow(), QString(), QObject::tr("AI object %1 cannot be deleted when AI/Physics mode is activated.").arg(pObject->GetName())); return false; } } } } } return true; }; ////////////////////////////////////////////////////////////////////////// void CObjectManager::DeleteSelection() { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); AzToolsFramework::EditorMetricsEventBusSelectionChangeHelper selectionChangeMetricsHelper; // Make sure to unlock selection. GetIEditor()->LockSelection(false); GUID bID = GUID_NULL; int i; CSelectionGroup objects; for (i = 0; i < m_currSelection->GetCount(); i++) { // Check condition(s) if object could be deleted if (!IsObjectDeletionAllowed(m_currSelection->GetObject(i))) { return; } objects.AddObject(m_currSelection->GetObject(i)); } RemoveSelection(m_currSelection->GetName()); m_currSelection = &m_defaultSelection; m_defaultSelection.RemoveAll(); DeleteSelection(&objects); } ////////////////////////////////////////////////////////////////////////// bool CObjectManager::HitTestObject(CBaseObject* obj, HitContext& hc) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); if (obj->IsFrozen()) { return false; } if (obj->IsHidden()) { return false; } // This object is rejected by deep selection. if (obj->CheckFlags(OBJFLAG_NO_HITTEST)) { return false; } ObjectType objType = obj->GetType(); // Check if this object type is masked for selection. if (!(objType & gSettings.objectSelectMask)) { return false; } const bool bSelectionHelperHit = obj->HitHelperTest(hc); if (hc.bUseSelectionHelpers && !bSelectionHelperHit) { return false; } if (!bSelectionHelperHit) { // Fast checking. if (hc.camera && !obj->IsInCameraView(*hc.camera)) { return false; } else if (hc.bounds && !obj->IntersectRectBounds(*hc.bounds)) { return false; } // Do 2D space testing. if (hc.nSubObjFlags == 0) { Ray ray(hc.raySrc, hc.rayDir); if (!obj->IntersectRayBounds(ray)) { return false; } } else if (!obj->HitTestRect(hc)) { return false; } CEditTool* pEditTool = GetIEditor()->GetEditTool(); if (pEditTool && pEditTool->HitTest(obj, hc)) { return true; } } return (bSelectionHelperHit || obj->HitTest(hc)); } ////////////////////////////////////////////////////////////////////////// bool CObjectManager::HitTest(HitContext& hitInfo) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); hitInfo.object = nullptr; hitInfo.dist = FLT_MAX; hitInfo.axis = 0; hitInfo.manipulatorMode = 0; HitContext hcOrg = hitInfo; if (hcOrg.view) { hcOrg.view->GetPerpendicularAxis(0, &hcOrg.b2DViewport); } hcOrg.rayDir = hcOrg.rayDir.GetNormalized(); HitContext hc = hcOrg; float mindist = FLT_MAX; if (!hitInfo.bIgnoreAxis && !hc.bUseSelectionHelpers) { // Test gizmos. if (m_gizmoManager->HitTest(hc)) { if (hc.axis != 0) { hitInfo.object = hc.object; hitInfo.gizmo = hc.gizmo; hitInfo.axis = hc.axis; hitInfo.manipulatorMode = hc.manipulatorMode; hitInfo.dist = hc.dist; return true; } } } if (hitInfo.bOnlyGizmo) { return false; } // Only HitTest objects, that where previously Displayed. CBaseObjectsCache* pDispayedViewObjects = hitInfo.view->GetVisibleObjectsCache(); const bool iconsPrioritized = true; // Force icons to always be prioritized over other things you hit. Can change to be a configurable option in the future. CBaseObject* selected = 0; const char* name = nullptr; bool iconHit = false; int numVis = pDispayedViewObjects->GetObjectCount(); for (int i = 0; i < numVis; i++) { CBaseObject* obj = pDispayedViewObjects->GetObject(i); //! Only check root objects. //! One exception: a child should be checked, if belonged group is opened. if (obj->GetGroup()) { if (!obj->GetGroup()->IsOpen()) { continue; } } if (obj == hitInfo.pExcludedObject) { continue; } if (HitTestObject(obj, hc)) { if (m_selectCallback && !m_selectCallback->CanSelectObject(obj)) { continue; } // Check if this object is nearest. if (hc.axis != 0) { hitInfo.object = obj; hitInfo.axis = hc.axis; hitInfo.dist = hc.dist; return true; } // When prioritizing icons, we don't allow non-icon hits to beat icon hits if (iconsPrioritized && iconHit && !hc.iconHit) { continue; } if (hc.dist < mindist || (!iconHit && hc.iconHit)) { if (hc.iconHit) { iconHit = true; } mindist = hc.dist; name = hc.name; selected = obj; } // Clear the object pointer if an object was hit, not just if the collision // was closer than any previous. Not all paths from HitTestObject set the object pointer and so you could get // an object from a previous (rejected) result but with collision information about a closer hit. hc.object = nullptr; hc.iconHit = false; // If use deep selection if (hitInfo.pDeepSelection) { hitInfo.pDeepSelection->AddObject(hc.dist, obj); } } } if (selected) { hitInfo.object = selected; hitInfo.dist = mindist; hitInfo.name = name; hitInfo.iconHit = iconHit; return true; } return false; } void CObjectManager::FindObjectsInRect(CViewport* view, const QRect& rect, std::vector<GUID>& guids) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); if (rect.width() < 1 || rect.height() < 1) { return; } HitContext hc; hc.view = view; hc.b2DViewport = view->GetType() != ET_ViewportCamera; hc.rect = rect; hc.bUseSelectionHelpers = view->GetAdvancedSelectModeFlag(); guids.clear(); CBaseObjectsCache* pDispayedViewObjects = view->GetVisibleObjectsCache(); int numVis = pDispayedViewObjects->GetObjectCount(); for (int i = 0; i < numVis; ++i) { CBaseObject* pObj = pDispayedViewObjects->GetObject(i); HitTestObjectAgainstRect(pObj, view, hc, guids); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::SelectObjectsInRect(CViewport* view, const QRect& rect, bool bSelect) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); // Ignore too small rectangles. if (rect.width() < 1 || rect.height() < 1) { return; } CUndo undo("Select Object(s)"); HitContext hc; hc.view = view; hc.b2DViewport = view->GetType() != ET_ViewportCamera; hc.rect = rect; hc.bUseSelectionHelpers = view->GetAdvancedSelectModeFlag(); bool isUndoRecording = GetIEditor()->IsUndoRecording(); if (isUndoRecording) { m_processingBulkSelect = true; } CBaseObjectsCache* displayedViewObjects = view->GetVisibleObjectsCache(); int numVis = displayedViewObjects->GetObjectCount(); // Tracking the previous selection allows proper undo/redo functionality of additional // selections (CTRL + drag select) AZStd::unordered_set<const CBaseObject*> previousSelection; for (int i = 0; i < numVis; ++i) { CBaseObject* object = displayedViewObjects->GetObject(i); if (object->IsSelected()) { previousSelection.insert(object); } else { // This will update m_currSelection SelectObjectInRect(object, view, hc, bSelect); // Legacy undo/redo does not go through the Ebus system and must be done individually if (isUndoRecording && object->GetType() != OBJTYPE_AZENTITY) { GetIEditor()->RecordUndo(new CUndoBaseObjectSelect(object, true)); } } } if (isUndoRecording && m_currSelection) { // Component Entities can handle undo/redo in bulk due to Ebuses GetIEditor()->RecordUndo(new CUndoBaseObjectBulkSelect(previousSelection, *m_currSelection)); } m_processingBulkSelect = false; } ////////////////////////////////////////////////////////////////////////// uint16 FindPossibleObjectNameNumber(std::set<uint16>& numberSet) { const int LIMIT = 65535; size_t nSetSize = numberSet.size(); for (uint16 i = 1; i < LIMIT; ++i) { uint16 candidateNumber = (i + nSetSize) % LIMIT; if (numberSet.find(candidateNumber) == numberSet.end()) { numberSet.insert(candidateNumber); return candidateNumber; } } return 0; } void CObjectManager::RegisterObjectName(const QString& name) { // Remove all numbers from the end of typename. QString typeName = name; int nameLen = typeName.length(); int len = nameLen; while (len > 0 && typeName[len - 1].isDigit()) { len--; } typeName = typeName.left(len); uint16 num = 1; if (len < nameLen) { num = (uint16)atoi((const char*)name.toUtf8().data() + len) + 0; } NameNumbersMap::iterator iNameNumber = m_nameNumbersMap.find(typeName); if (iNameNumber == m_nameNumbersMap.end()) { std::set<uint16> numberSet; numberSet.insert(num); m_nameNumbersMap[typeName] = numberSet; } else { std::set<uint16>& numberSet = iNameNumber->second; numberSet.insert(num); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::UpdateRegisterObjectName(const QString& name) { // Remove all numbers from the end of typename. QString typeName = name; int nameLen = typeName.length(); int len = nameLen; while (len > 0 && typeName[len - 1].isDigit()) { len--; } typeName = typeName.left(len); uint16 num = 1; if (len < nameLen) { num = (uint16)atoi((const char*)name.toUtf8().data() + len) + 0; } NameNumbersMap::iterator it = m_nameNumbersMap.find(typeName); if (it != m_nameNumbersMap.end()) { if (it->second.end() != it->second.find(num)) { it->second.erase(num); if (it->second.empty()) { m_nameNumbersMap.erase(it); } } } } ////////////////////////////////////////////////////////////////////////// QString CObjectManager::GenerateUniqueObjectName(const QString& theTypeName) { if (!m_bGenUniqObjectNames) { return theTypeName; } QString typeName = theTypeName; const int subIndex = theTypeName.indexOf("::"); if (subIndex != -1 && subIndex > typeName.length() - 2) { typeName.remove(0, subIndex + 2); } // Remove all numbers from the end of typename. int len = typeName.length(); while (len > 0 && typeName[len - 1].isDigit()) { len--; } typeName = typeName.left(len); NameNumbersMap::iterator ii = m_nameNumbersMap.find(typeName); uint16 lastNumber = 1; if (ii != m_nameNumbersMap.end()) { lastNumber = FindPossibleObjectNameNumber(ii->second); } else { std::set<uint16> numberSet; numberSet.insert(lastNumber); m_nameNumbersMap[typeName] = numberSet; } QString str = QStringLiteral("%1%2").arg(typeName).arg(lastNumber); return str; } ////////////////////////////////////////////////////////////////////////// bool CObjectManager::EnableUniqObjectNames(bool bEnable) { bool bPrev = m_bGenUniqObjectNames; m_bGenUniqObjectNames = bEnable; return bPrev; } ////////////////////////////////////////////////////////////////////////// CObjectClassDesc* CObjectManager::FindClass(const QString& className) { IClassDesc* cls = CClassFactory::Instance()->FindClass(className.toUtf8().data()); if (cls != NULL && cls->SystemClassID() == ESYSTEM_CLASS_OBJECT) { return (CObjectClassDesc*)cls; } return 0; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::GetClassCategories(QStringList& categories) { std::vector<IClassDesc*> classes; CClassFactory::Instance()->GetClassesBySystemID(ESYSTEM_CLASS_OBJECT, classes); std::set<QString> cset; for (int i = 0; i < classes.size(); i++) { QString category = classes[i]->Category(); if (!category.isEmpty()) { cset.insert(category); } } categories.clear(); categories.reserve(cset.size()); for (std::set<QString>::iterator cit = cset.begin(); cit != cset.end(); ++cit) { categories.push_back(*cit); } } void CObjectManager::GetClassCategoryToolClassNamePairs(std::vector< std::pair<QString, QString> >& categoryToolClassNamePairs) { std::vector<IClassDesc*> classes; CClassFactory::Instance()->GetClassesBySystemID(ESYSTEM_CLASS_OBJECT, classes); std::set< std::pair<QString, QString> > cset; for (int i = 0; i < classes.size(); i++) { QString category = classes[i]->Category(); QString toolClassName = ((CObjectClassDesc*)classes[i])->GetToolClassName(); if (!category.isEmpty()) { cset.insert(std::pair<QString, QString>(category, toolClassName)); } } categoryToolClassNamePairs.clear(); categoryToolClassNamePairs.reserve(cset.size()); for (std::set< std::pair<QString, QString> >::iterator cit = cset.begin(); cit != cset.end(); ++cit) { categoryToolClassNamePairs.push_back(*cit); } } void CObjectManager::GetClassTypes(const QString& category, QStringList& types) { std::vector<IClassDesc*> classes; CClassFactory::Instance()->GetClassesBySystemID(ESYSTEM_CLASS_OBJECT, classes); for (int i = 0; i < classes.size(); i++) { QString cat = classes[i]->Category(); if (QString::compare(cat, category, Qt::CaseInsensitive) == 0 && classes[i]->IsEnabled()) { types.push_back(classes[i]->ClassName()); } } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::RegisterClassTemplate(const XmlNodeRef& templ) { QString typeName = templ->getTag(); QString superTypeName; if (!templ->getAttr("SuperType", superTypeName)) { return; } CObjectClassDesc* superType = FindClass(superTypeName); if (!superType) { return; } QString 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; classDesc->guid = AZ::Uuid::CreateRandom(); CClassFactory::Instance()->RegisterClass(classDesc); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::LoadClassTemplates(const QString& path) { QString dir = Path::AddPathSlash(path); IFileUtil::FileArray 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 = XmlHelpers::LoadXmlFromFile((dir + files[k].filename).toUtf8().data()); if (node != 0 && node->isTag("ObjectTemplates")) { QString name; for (int i = 0; i < node->getChildCount(); i++) { RegisterClassTemplate(node->getChild(i)); } } } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::RegisterCVars() { REGISTER_CVAR2("AxisHelperHitRadius", &m_axisHelperHitRadius, 20, VF_DEV_ONLY, "Adjust the hit radius used for axis helpers, like the transform gizmo."); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::Serialize(XmlNodeRef& xmlNode, bool bLoading, int flags) { if (!xmlNode) { return; } 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); // Loading. if (root) { ar.node = root; LoadObjects(ar, false); } EndObjectsLoading(); } else { // Saving. XmlNodeRef root = xmlNode->newChild("Objects"); CObjectArchive ar(this, root, false); // Save all objects to XML. for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject* obj = it->second; // Not save objects in prefabs or groups. if (obj->GetGroup() || obj->CheckFlags(OBJFLAG_PREFAB)) { continue; } if (obj->CheckFlags(OBJFLAG_DONT_SAVE)) { continue; } if ((flags == SERIALIZE_ONLY_SHARED) && !obj->CheckFlags(OBJFLAG_SHARED)) { continue; } else if ((flags == SERIALIZE_ONLY_NOTSHARED) && obj->CheckFlags(OBJFLAG_SHARED)) { continue; } CObjectLayer* pLayer = obj->GetLayer(); if (pLayer->IsExternal()) { continue; } XmlNodeRef objNode = root->newChild("Object"); ar.node = objNode; obj->Serialize(ar); } // Save layers. ar.node = xmlNode; m_pLayerManager->Serialize(ar); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::LoadObjects(CObjectArchive& objectArchive, bool bSelect) { m_bLoadingObjects = true; GetIEditor()->GetPrefabManager()->SetSkipPrefabUpdate(true); 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); } } EndObjectsLoading(); // End progress bar, here, Resolve objects have his own. objectArchive.ResolveObjects(); InvalidateVisibleList(); GetIEditor()->GetPrefabManager()->SetSkipPrefabUpdate(false); m_bLoadingObjects = false; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::Export(const QString& levelPath, XmlNodeRef& rootNode, bool onlyShared) { // Clear export files. QFile::remove(QStringLiteral("%1TagPoints.ini").arg(levelPath)); QFile::remove(QStringLiteral("%1Volumes.ini").arg(levelPath)); // 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::ExportEntities(XmlNodeRef& rootNode) { // 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; } if (qobject_cast<CEntityObject*>(obj)) { obj->Export("", rootNode); } } } void CObjectManager::DeleteNotSharedObjects() { TBaseObjects 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() { TBaseObjects 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() { if (m_isUpdateVisibilityList) { return; } ++m_visibilitySerialNumber; m_visibleObjects.clear(); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::UpdateVisibilityList() { m_isUpdateVisibilityList = true; m_visibleObjects.clear(); bool isInIsolationMode = false; AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(isInIsolationMode, &AzToolsFramework::ToolsApplicationRequestBus::Events::IsEditorInIsolationMode); for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject* obj = it->second; bool visible = obj->IsPotentiallyVisible(); // entities not isolated in Isolation Mode will be invisible bool isObjectIsolated = obj->IsIsolated(); visible = visible && (!isInIsolationMode || isObjectIsolated); obj->UpdateVisibility(visible); // when the new viewport interaction model is enabled we always want to add objects // in the view (frustum) to the visible objects list so we can draw feedback for // entities being hidden in the viewport when selected in the entity outliner // (EditorVisibleEntityDataCache must be populated even if entities are 'hidden') if (visible || GetIEditor()->IsNewViewportInteractionModelEnabled()) { // Prefabs are not added into visible list. if (!obj->CheckFlags(OBJFLAG_PREFAB)) { m_visibleObjects.push_back(obj); } } } m_isUpdateVisibilityList = false; } ////////////////////////////////////////////////////////////////////////// CBaseObject* CObjectManager::FindAnimNodeOwner(CTrackViewAnimNode* pNode) const { CEntityObject* entityObject = nullptr; if (pNode) { entityObject = pNode->GetNodeEntity(false); if (!entityObject) { // Find owner entity. IEntity* pIEntity = pNode->GetEntity(); if (pIEntity) { // Find owner editor entity. entityObject = CEntityObject::FindFromEntityId(pIEntity->GetId()); } // If we haven't found the entityObject using the legacy CryEntity methods, finally try searching AZ Entities if (!entityObject && pNode->GetAzEntityId().IsValid()) { EBUS_EVENT_ID_RESULT(entityObject, pNode->GetAzEntityId(), AzToolsFramework::ComponentEntityEditorRequestBus, GetSandboxObject); } } } return entityObject; } ////////////////////////////////////////////////////////////////////////// bool CObjectManager::ConvertToType(CBaseObject* pObject, const QString& typeName) { QString message = QString("Convert ") + pObject->GetName() + " to " + typeName; CUndo undo(message.toUtf8().data()); CBaseObjectPtr pNewObject = GetIEditor()->NewObject(typeName.toUtf8().data()); if (pNewObject) { if (pNewObject->ConvertFromObject(pObject)) { DeleteObject(pObject); return true; } DeleteObject(pNewObject); } Log((message + " is failed.").toUtf8().data()); return false; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::SetObjectSelected(CBaseObject* pObject, bool bSelect) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); // Only select/unselect once. if ((pObject->IsSelected() && bSelect) || (!pObject->IsSelected() && !bSelect)) { return; } // Store selection undo. if (CUndo::IsRecording() && !m_processingBulkSelect) { CUndo::Record(new CUndoBaseObjectSelect(pObject)); } pObject->SetSelected(bSelect); m_bSelectionChanged = true; if (bSelect && !GetIEditor()->GetTransformManipulator()) { 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); } if (qobject_cast<CAITerritoryObject*>(pObject) || qobject_cast<CAIWaveObject*>(pObject)) { RefreshEntitiesAssignedToSelectedTnW(); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::HideTransformManipulators() { m_gizmoManager->DeleteAllTransformManipulators(); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// 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<EventCallback>::iterator next; for (std::list<EventCallback>::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("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, CObjectLayer* pLayer) { CBaseObjectsArray objects; GetIEditor()->GetObjectManager()->GetObjects(objects, pLayer); for (int i = 0; i < objects.size(); i++) { CBaseObject* pObject = objects[i]; pObject->GatherUsedResources(resources); } } ////////////////////////////////////////////////////////////////////////// IGizmoManager* CObjectManager::GetGizmoManager() { return m_gizmoManager; } ////////////////////////////////////////////////////////////////////////// IStatObj* CObjectManager::GetGeometryFromObject(CBaseObject* pObject) { assert(pObject); if (qobject_cast<CBrushObject*>(pObject)) { CBrushObject* pBrushObj = (CBrushObject*)pObject; return pBrushObj->GetIStatObj(); } if (qobject_cast<CEntityObject*>(pObject)) { CEntityObject* pEntityObj = (CEntityObject*)pObject; if (pEntityObj->GetIEntity()) { IEntity* pGameEntity = pEntityObj->GetIEntity(); for (int i = 0; pGameEntity != NULL && i < pGameEntity->GetSlotCount(); i++) { if (pGameEntity->GetStatObj(i)) { return pGameEntity->GetStatObj(i); } } } } return 0; } ////////////////////////////////////////////////////////////////////////// ICharacterInstance* CObjectManager::GetCharacterFromObject(CBaseObject* pObject) { assert(pObject); if (qobject_cast<CEntityObject*>(pObject)) { CEntityObject* pEntityObj = (CEntityObject*)pObject; if (pEntityObj->GetIEntity()) { IEntity* pGameEntity = pEntityObj->GetIEntity(); for (int i = 0; pGameEntity != NULL && i < pGameEntity->GetSlotCount(); i++) { if (pGameEntity->GetCharacter(i)) { return pGameEntity->GetCharacter(i); } } } } return 0; } #if ENABLE_CRY_PHYSICS ////////////////////////////////////////////////////////////////////////// CBaseObject* CObjectManager::FindPhysicalObjectOwner(IPhysicalEntity* pPhysicalEntity) { if (!pPhysicalEntity) { return 0; } int itype = pPhysicalEntity->GetiForeignData(); switch (itype) { case PHYS_FOREIGN_ID_ROPE: { IRopeRenderNode* pRenderNode = (IRopeRenderNode*)pPhysicalEntity->GetForeignData(itype); if (pRenderNode) { EntityId id = (EntityId)pRenderNode->GetEntityOwner(); CEntityObject* pEntity = CEntityObject::FindFromEntityId(id); return pEntity; } } break; case PHYS_FOREIGN_ID_ENTITY: { IEntity* pIEntity = gEnv->pEntitySystem ? gEnv->pEntitySystem->GetEntityFromPhysics(pPhysicalEntity) : nullptr; if (pIEntity) { return CEntityObject::FindFromEntityId(pIEntity->GetId()); } } break; case PHYS_FOREIGN_ID_STATIC: { IRopeRenderNode* pRenderNode = (IRopeRenderNode*)pPhysicalEntity->GetForeignData(itype); if (pRenderNode) { // Find brush who created this render node. } } } return 0; } #endif // ENABLE_CRY_PHYSICS ////////////////////////////////////////////////////////////////////////// void CObjectManager::OnObjectModified(CBaseObject* pObject, bool bDelete, bool boModifiedTransformOnly) { if (!m_bLoadingObjects) { if (qobject_cast<CEntityObject*>(pObject)) { if (qobject_cast<CAITerritoryObject*>(pObject) != nullptr || qobject_cast<CAIWaveObject*>(pObject) != nullptr) { RefreshEntitiesAssignedToSelectedTnW(); } else { CEntityObject* pEntity = static_cast<CEntityObject*>(pObject); // Editor's class CEntity IEntity* pIEntity = pEntity->GetIEntity(); // CryEntitySystem's interface IEntity if (pIEntity) { IAIObject* pAIObject = pIEntity->GetAI(); if (pAIObject && pAIObject->IsAgent()) { RefreshEntitiesAssignedToSelectedTnW(); } } } } } if (IRenderNode* pRenderNode = pObject->GetEngineNode()) { GetIEditor()->Get3DEngine()->OnObjectModified(pRenderNode, pRenderNode->GetRndFlags()); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::UnregisterNoExported() { I3DEngine* p3DEngine = GetIEditor()->Get3DEngine(); for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject* pObj = it->second; CObjectLayer* pLayer = pObj->GetLayer(); if (pLayer && !pLayer->IsExportable()) { IRenderNode* pRenderNode = pObj->GetEngineNode(); if (pRenderNode && pRenderNode->GetEntityStatObj()) { p3DEngine->UnRegisterEntityAsJob(pRenderNode); } } } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::RegisterNoExported() { I3DEngine* p3DEngine = GetIEditor()->Get3DEngine(); for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject* pObj = it->second; CObjectLayer* pLayer = pObj->GetLayer(); if (pLayer && !pLayer->IsExportable()) { IRenderNode* pRenderNode = pObj->GetEngineNode(); if (pRenderNode && pRenderNode->GetEntityStatObj()) { p3DEngine->RegisterEntity(pRenderNode); } } } } ////////////////////////////////////////////////////////////////////////// bool CObjectManager::IsEntityAssignedToSelectedTerritory(CEntityObject* pEntity) { return m_setEntitiesAssignedToSelectedTerritory.find(pEntity) != m_setEntitiesAssignedToSelectedTerritory.end(); } ////////////////////////////////////////////////////////////////////////// bool CObjectManager::IsEntityAssignedToSelectedWave(CEntityObject* pEntity) { return m_setEntitiesAssignedToSelectedWave.find(pEntity) != m_setEntitiesAssignedToSelectedWave.end(); } ////////////////////////////////////////////////////////////////////////// bool CObjectManager::IsLightClass(CBaseObject* pObject) { if (qobject_cast<CEntityObject*>(pObject)) { CEntityObject* pEntity = (CEntityObject*)pObject; if (pEntity) { if (pEntity->GetEntityClass().compare(CLASS_LIGHT) == 0) { return TRUE; } if (pEntity->GetEntityClass().compare(CLASS_RIGIDBODY_LIGHT) == 0) { return TRUE; } if (pEntity->GetEntityClass().compare(CLASS_DESTROYABLE_LIGHT) == 0) { return TRUE; } } } return FALSE; } ////////////////////////////////////////////////////////////////////////// void CObjectManager::RefreshEntitiesAssignedToSelectedTnW() { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor); m_setEntitiesAssignedToSelectedTerritory.clear(); m_setEntitiesAssignedToSelectedWave.clear(); // No need to refresh entities assigned to selected waves/territories when there are no waves/territories if (m_aiTerritoryObjects.empty() && m_aiWaveObjects.empty()) { return; } std::vector<CAITerritoryObject*> vSelectedTerritories; std::set<QString> setSelectedTerritories; std::set<QString> setSelectedWaves; std::map<CEntityObject*, QString> mapEntityTerritories; std::vector<CEntityObject*> vEntitiesWithAutoAssignment; std::map<CEntityObject*, QString> mapEntityWaves; QString sEntityTerritory; QString sEntityWave; CBaseObjectsArray objects; { AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::Editor, "CObjectManager::RefreshEntitiesAssignedToSelectedTnW:GetObjects"); GetObjects(objects); } // First clarify relationships between Entities (e.g. Grunts) and AI Territories & Waves { AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::Editor, "CObjectManager::RefreshEntitiesAssignedToSelectedTnW:ClarifyRelationships"); for (size_t i = 0, n = objects.size(); i < n; ++i) { CBaseObject* pObject = objects[i]; if (!qobject_cast<CEntityObject*>(pObject)) { continue; } CEntityObject* pEntity = static_cast<CEntityObject*>(pObject); if (pEntity->IsHidden() || pEntity->IsHiddenBySpec() || pEntity->IsFrozen()) { continue; } if (qobject_cast<CAITerritoryObject*>(pEntity)) { if (pEntity->IsSelected()) { vSelectedTerritories.push_back(static_cast<CAITerritoryObject*>(pEntity)); setSelectedTerritories.insert(pEntity->GetName()); } } else if (qobject_cast<CAIWaveObject*>(pEntity)) { if (pEntity->IsSelected()) { setSelectedWaves.insert(pEntity->GetName()); } } else { // Associate Entities (e.g. Grunts) with their Territories and Waves CVarBlock* pProperties2 = pEntity->GetProperties2(); if (pProperties2) { IVariable* pVarTerritory = pProperties2->FindVariable("aiterritory_Territory"); if (pVarTerritory) { pVarTerritory->Get(sEntityTerritory); if (!sEntityTerritory.isEmpty() && (sEntityTerritory != "<None>")) { #ifndef USE_SIMPLIFIED_AI_TERRITORY_SHAPE if (sEntityTerritory == "<Auto>") { vEntitiesWithAutoAssignment.push_back(pEntity); } else #endif { mapEntityTerritories.insert(std::make_pair(pEntity, sEntityTerritory)); IVariable* pVarWave = pProperties2->FindVariable("aiwave_Wave"); if (pVarWave) { pVarWave->Get(sEntityWave); if (!sEntityWave.isEmpty() && (sEntityWave != "<None>")) { mapEntityWaves.insert(std::make_pair(pEntity, sEntityWave)); } } } } } } } } } // Now figure out what to select { AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::Editor, "CObjectManager::RefreshEntitiesAssignedToSelectedTnW:DetermineSelection"); for (std::map<CEntityObject*, QString>::iterator it = mapEntityTerritories.begin(), end = mapEntityTerritories.end(); it != end; ++it) { CEntityObject* pEntity = it->first; const QString& sTerritoryName = it->second; if (setSelectedTerritories.find(sTerritoryName) != setSelectedTerritories.end()) { m_setEntitiesAssignedToSelectedTerritory.insert(pEntity); } } for (std::vector<CAITerritoryObject*>::iterator it = vSelectedTerritories.begin(), end = vSelectedTerritories.end(); it != end; ++it) { CAITerritoryObject* territory = *it; const Matrix34& tm = territory->GetWorldTM(); std::vector<Vec3> territoryPoints; for (int i = 0, n = territory->GetPointCount(); i < n; ++i) { Vec3 point = tm.TransformPoint(territory->GetPoint(i)); territoryPoints.push_back(point); } for (std::vector<CEntityObject*>::iterator it2 = vEntitiesWithAutoAssignment.begin(), end = vEntitiesWithAutoAssignment.end(); it2 != end; ++it2) { CEntityObject* pEntity = *it2; const Vec3& pos = pEntity->GetPos(); float height = territory->GetHeight(); float h = pos.z - territory->GetPos().z; if ((height < 0.01f) || ((0.0f < h) && (h < height))) { if (Overlap::Point_Polygon2D(pos, territoryPoints)) { m_setEntitiesAssignedToSelectedTerritory.insert(pEntity); } } } } for (std::map<CEntityObject*, QString>::iterator it = mapEntityWaves.begin(), end = mapEntityWaves.end(); it != end; ++it) { CEntityObject* pEntity = it->first; const QString& sWaveName = it->second; if (setSelectedWaves.find(sWaveName) != setSelectedWaves.end()) { m_setEntitiesAssignedToSelectedWave.insert(pEntity); } } } } size_t CObjectManager::NumberOfAssignedEntities() { std::set<CEntityObject*> result; std::set_union( m_setEntitiesAssignedToSelectedTerritory.begin(), m_setEntitiesAssignedToSelectedTerritory.end(), m_setEntitiesAssignedToSelectedWave.begin(), m_setEntitiesAssignedToSelectedWave.end(), inserter(result, result.begin())); return result.size(); } void CObjectManager::SelectAssignedEntities() { // Memorize what to select before call to ClearSelection() std::set<CEntityObject*> t = m_setEntitiesAssignedToSelectedTerritory; std::set<CEntityObject*> w = m_setEntitiesAssignedToSelectedWave; ClearSelection(); SelectEntities(t); SelectEntities(w); } void CObjectManager::FindAndRenameProperty2(const char* property2Name, const QString& oldValue, const QString& newValue) { CBaseObjectsArray objects; GetObjects(objects); for (size_t i = 0, n = objects.size(); i < n; ++i) { CBaseObject* pObject = objects[i]; if (qobject_cast<CEntityObject*>(pObject)) { CEntityObject* pEntity = static_cast<CEntityObject*>(pObject); CVarBlock* pProperties2 = pEntity->GetProperties2(); if (pProperties2) { IVariable* pVariable = pProperties2->FindVariable(property2Name); if (pVariable) { QString sValue; pVariable->Get(sValue); if (sValue == oldValue) { pEntity->StoreUndo("Rename Property2"); pVariable->Set(newValue); // Special case #ifdef USE_SIMPLIFIED_AI_TERRITORY_SHAPE if (strcmp(property2Name, "aiterritory_Territory") == 0 && ((newValue == "<None>") || (newValue != oldValue))) #else if (strcmp(property2Name, "aiterritory_Territory") == 0 && ((newValue == "<Auto>") || (newValue == "<None>") || (newValue != oldValue))) #endif { IVariable* pVariableWave = pProperties2->FindVariable("aiwave_Wave"); if (pVariableWave) { pVariableWave->Set("<None>"); } } } } } } } } void CObjectManager::FindAndRenameProperty2If(const char* property2Name, const QString& oldValue, const QString& newValue, const char* otherProperty2Name, const QString& otherValue) { CBaseObjectsArray objects; GetObjects(objects); for (size_t i = 0, n = objects.size(); i < n; ++i) { CBaseObject* pObject = objects[i]; if (qobject_cast<CEntityObject*>(pObject)) { CEntityObject* pEntity = static_cast<CEntityObject*>(pObject); CVarBlock* pProperties2 = pEntity->GetProperties2(); if (pProperties2) { IVariable* pVariable = pProperties2->FindVariable(property2Name); IVariable* pOtherVariable = pProperties2->FindVariable(otherProperty2Name); if (pVariable && pOtherVariable) { QString sValue; pVariable->Get(sValue); QString sOtherValue; pOtherVariable->Get(sOtherValue); if ((sValue == oldValue) && (sOtherValue == otherValue)) { pEntity->StoreUndo("Rename Property2 If"); pVariable->Set(newValue); // Special case #ifdef USE_SIMPLIFIED_AI_TERRITORY_SHAPE if ((strcmp(property2Name, "aiterritory_Territory") == 0) && (newValue == "<None>")) #else if ((strcmp(property2Name, "aiterritory_Territory") == 0) && ((newValue == "<Auto>") || (newValue == "<None>"))) #endif { IVariable* pVariableWave = pProperties2->FindVariable("aiwave_Wave"); if (pVariableWave) { pVariableWave->Set("<None>"); } } } } } } } } void CObjectManager::ResolveMissingObjects() { enum { eInit = 0, eNoForAll = QMessageBox::NoToAll, eNo = QMessageBox::No, eYesForAll = QMessageBox::YesToAll, eYes = QMessageBox::Yes }; typedef std::map<QString, QString> LocationMap; LocationMap locationMap; int locationState = eInit; bool isUpdated = false; Log("Resolving missed objects..."); for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject* obj = it->second; CGeomEntity* pGeomEntity = 0; CSimpleEntity* pSimpleEntity = 0; CBrushObject* pBrush = 0; IEntity* pEntity = 0; QString geometryFile; IVariable* pModelVar = 0; if (qobject_cast<CGeomEntity*>(obj)) { IEntity* pEntityObj = ((CGeomEntity*)obj)->GetIEntity(); if (pEntityObj && pEntityObj->GetStatObj(0) && pEntityObj->GetStatObj(0)->IsDefaultObject()) { pGeomEntity = (CGeomEntity*)obj; geometryFile = pGeomEntity->GetGeometryFile(); } } else if (qobject_cast<CSimpleEntity*>(obj)) { IEntity* pEntityObj = ((CSimpleEntity*)obj)->GetIEntity(); if (pEntityObj && pEntityObj->GetStatObj(0) && pEntityObj->GetStatObj(0)->IsDefaultObject()) { pSimpleEntity = (CSimpleEntity*)obj; geometryFile = pSimpleEntity->GetGeometryFile(); } } else if (qobject_cast<CBrushObject*>(obj)) { CBrushObject* pBrushObj = (CBrushObject*)obj; if (pBrushObj->GetGeometry() && ((CEdMesh*)pBrushObj->GetGeometry())->IsDefaultObject()) { pBrush = (CBrushObject*)obj; geometryFile = pBrush->GetGeometryFile(); } } else if (qobject_cast<CEntityObject*>(obj)) { IEntity* pEntityObj = ((CEntityObject*)obj)->GetIEntity(); if (pEntityObj && pEntityObj->GetStatObj(0) && pEntityObj->GetStatObj(0)->IsDefaultObject()) { CVarBlock* pVars = ((CEntityObject*)obj)->GetProperties(); if (pVars) { for (int i = 0; i < pVars->GetNumVariables(); i++) { pModelVar = pVars->GetVariable(i); if (pModelVar && pModelVar->GetDataType() == IVariable::DT_FILE) { pModelVar->Get(geometryFile); QString ext = PathUtil::GetExt(geometryFile.toUtf8().data()); if (QString::compare(ext, CRY_GEOMETRY_FILE_EXT, Qt::CaseInsensitive) == 0 || QString::compare(ext, CRY_SKEL_FILE_EXT, Qt::CaseInsensitive) == 0 || QString::compare(ext, CRY_CHARACTER_DEFINITION_FILE_EXT, Qt::CaseInsensitive) == 0 || QString::compare(ext, CRY_ANIM_GEOMETRY_FILE_EXT, Qt::CaseInsensitive) == 0) { if (!gEnv->pCryPak->IsFileExist(geometryFile.toUtf8().data())) { pEntity = pEntityObj; break; } } } } } } } if (!pGeomEntity && !pSimpleEntity && !pBrush && !pEntity) { continue; } int nKey = 0; QString newFilename = stl::find_in_map(locationMap, geometryFile, QString("")); if (newFilename != "") { if (pGeomEntity) { pGeomEntity->SetGeometryFile(newFilename); } else if (pSimpleEntity) { pSimpleEntity->SetGeometryFile(newFilename); } else if (pBrush) { pBrush->CreateBrushFromMesh(newFilename.toUtf8().data()); if (pBrush->GetGeometry() && !((CEdMesh*)pBrush->GetGeometry())->IsDefaultObject()) { pBrush->SetGeometryFile(newFilename); } } else if (pEntity) { pModelVar->Set(newFilename); } Log("%s: %s <- %s", obj->GetName().toUtf8().constData(), geometryFile.toUtf8().constData(), newFilename.toUtf8().constData()); continue; } if (locationState == eNoForAll) { continue; } if (locationState != eYesForAll) // Skip, if "Yes for All" pressed before { QString mes = QObject::tr("Geometry file for object \"%1\" is missing/removed. \r\nFile: %2\r\nAttempt to locate this file?").arg(obj->GetName(), geometryFile); nKey = QMessageBox::question(QApplication::activeWindow(), QObject::tr("Object missing"), mes, QMessageBox::NoToAll | QMessageBox::No | QMessageBox::Yes | QMessageBox::YesToAll); if (nKey == eNoForAll && locationMap.size() == 0) { break; } if (nKey == eNoForAll || nKey == eYesForAll) { locationState = nKey; } } if (nKey == eYes || locationState == eYesForAll) { IFileUtil::FileArray cFiles; QString filemask = PathUtil::GetFile(geometryFile.toUtf8().data()); CFileUtil::ScanDirectory(Path::GetEditingGameDataFolder().c_str(), filemask, cFiles, true); if (cFiles.size()) { QString newFilename = cFiles[0].filename; if (pGeomEntity) { pGeomEntity->SetGeometryFile(newFilename); } else if (pSimpleEntity) { pSimpleEntity->SetGeometryFile(newFilename); } else if (pBrush) { pBrush->CreateBrushFromMesh(newFilename.toUtf8().data()); if (pBrush->GetGeometry() && !((CEdMesh*)pBrush->GetGeometry())->IsDefaultObject()) { pBrush->SetGeometryFile(newFilename); } } else if (pEntity) { pModelVar->Set(newFilename); } locationMap[geometryFile] = newFilename; Log("%s: %s <- %s", obj->GetName().toUtf8().constData(), geometryFile.toUtf8().constData(), newFilename.toUtf8().constData()); isUpdated = true; } else { GetIEditor()->GetSystem()->GetILog()->LogWarning("Can't resolve object: %s: %s", obj->GetName().toUtf8().constData(), geometryFile.toUtf8().constData()); } } } if (isUpdated) { GetIEditor()->SetModifiedFlag(); } else { Log("No objects has been resolved."); } ResolveMissingMaterials(); } void CObjectManager::ResolveMissingMaterials() { enum { eInit = 0, eNoForAll = QMessageBox::NoToAll, eNo = QMessageBox::No, eYesForAll = QMessageBox::YesToAll, eYes = QMessageBox::Yes }; typedef std::map<QString, QString> LocationMap; LocationMap locationMap; int locationState = eInit; bool isUpdated = false; QString oldFilename; Log("Resolving missed materials..."); for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it) { CBaseObject* obj = it->second; CMaterial* pMat = obj->GetMaterial(); if (pMat && pMat->GetMatInfo() && pMat->GetMatInfo()->IsDefault()) { oldFilename = pMat->GetFilename(); } else { continue; } int nKey = 0; QString newFilename = stl::find_in_map(locationMap, oldFilename, QString("")); if (newFilename != "") { CMaterial* pNewMaterial = GetIEditor()->GetMaterialManager()->LoadMaterial(newFilename); if (pNewMaterial) { obj->SetMaterial(pNewMaterial); Log("%s: %s <- %s", pMat->GetName().toUtf8().constData(), oldFilename.toUtf8().constData(), newFilename.toUtf8().constData()); } continue; } if (locationState == eNoForAll) { continue; } if (locationState != eYesForAll) // Skip, if "Yes for All" pressed before { QString mes = QObject::tr("Material for object \"%1\" is missing/removed. \r\nFile: %2\r\nAttempt to locate this file?").arg(obj->GetName(), oldFilename); nKey = QMessageBox::question(QApplication::activeWindow(), QObject::tr("Material missing"), mes, QMessageBox::NoToAll | QMessageBox::No | QMessageBox::Yes | QMessageBox::YesToAll); if (nKey == eNoForAll && locationMap.size() == 0) { break; } if (nKey == eNoForAll || nKey == eYesForAll) { locationState = nKey; } } if (nKey == eYes || locationState == eYesForAll) { IFileUtil::FileArray cFiles; QString filemask = PathUtil::GetFile(oldFilename.toUtf8().data()); CFileUtil::ScanDirectory(Path::GetEditingGameDataFolder().c_str(), filemask, cFiles, true); if (cFiles.size()) { QString newFilename = cFiles[0].filename; CMaterial* pNewMaterial = GetIEditor()->GetMaterialManager()->LoadMaterial(newFilename); if (pNewMaterial) { obj->SetMaterial(pNewMaterial); locationMap[oldFilename] = newFilename; Log("%s: %s <- %s", pMat->GetName().toUtf8().constData(), oldFilename.toUtf8().constData(), newFilename.toUtf8().constData()); isUpdated = true; } } else { GetIEditor()->GetSystem()->GetILog()->LogWarning("Can't resolve material: %s: %s", pMat->GetName().toUtf8().constData(), oldFilename.toUtf8().constData()); } } } if (isUpdated) { GetIEditor()->SetModifiedFlag(); } else { Log("No materials has been resolved."); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::UpdateAttachedEntities() { for (CEntityObject* attachedEntityObj : m_animatedAttachedEntities) { attachedEntityObj->UpdateTransform(); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::AssignLayerIDsToRenderNodes() { m_pLayerManager->AssignLayerIDsToRenderNodes(); } ////////////////////////////////////////////////////////////////////////// void CObjectManager::HitTestObjectAgainstRect(CBaseObject* pObj, CViewport* view, HitContext hc, std::vector<GUID>& guids) { if (!pObj->IsSelectable()) { return; } AABB box; // Retrieve world space bound box. pObj->GetBoundBox(box); // Check if object visible in viewport. if (!view->IsBoundsVisible(box)) { return; } if (qobject_cast<CGroup*>(pObj)) { CGroup* pGroup = static_cast<CGroup*>(pObj); // If the group is open check children if (pGroup->IsOpen()) { for (int i = 0; i < pGroup->GetChildCount(); ++i) { HitTestObjectAgainstRect(pGroup->GetChild(i), view, hc, guids); } } } if (pObj->HitTestRect(hc)) { stl::push_back_unique(guids, pObj->GetId()); } } ////////////////////////////////////////////////////////////////////////// void CObjectManager::SelectObjectInRect(CBaseObject* pObj, CViewport* view, HitContext hc, bool bSelect) { if (!pObj->IsSelectable()) { return; } AABB box; // Retrieve world space bound box. pObj->GetBoundBox(box); // Check if object visible in viewport. if (!view->IsBoundsVisible(box)) { return; } if (qobject_cast<CGroup*>(pObj)) { CGroup* pGroup = static_cast<CGroup*>(pObj); // If the group is open check children if (pGroup->IsOpen()) { for (int i = 0; i < pGroup->GetChildCount(); ++i) { SelectObjectInRect(pGroup->GetChild(i), view, hc, bSelect); } } } if (pObj->HitTestRect(hc)) { if (bSelect) { SelectObject(pObj); } else { UnselectObject(pObj); } } } void CObjectManager::EnteredComponentMode(const AZStd::vector<AZ::Uuid>& /*componentModeTypes*/) { // provide an EditTool that does nothing. // note: will hide rotation gizmo when active (CRotateTool) GetIEditor()->SetEditTool(new NullEditTool()); // hide current gizmo for entity (translate/rotate/scale) IGizmoManager* gizmoManager = GetGizmoManager(); const size_t gizmoCount = static_cast<size_t>(gizmoManager->GetGizmoCount()); for (size_t i = 0; i < gizmoCount; ++i) { gizmoManager->RemoveGizmo(gizmoManager->GetGizmoByIndex(i)); } } void CObjectManager::LeftComponentMode(const AZStd::vector<AZ::Uuid>& /*componentModeTypes*/) { // return to default EditTool (in whatever transform mode is set) GetIEditor()->SetEditTool(nullptr); // show translate/rotate/scale gizmo again if (IGizmoManager* gizmoManager = GetGizmoManager()) { if (CBaseObject* selectedObject = GetIEditor()->GetSelectedObject()) { gizmoManager->AddGizmo(new CAxisGizmo(selectedObject)); } } } ////////////////////////////////////////////////////////////////////////// namespace { AZStd::vector<AZStd::string> PyGetAllObjects() { IObjectManager* pObjMgr = GetIEditor()->GetObjectManager(); CObjectLayer* pLayer = NULL; CBaseObjectsArray objects; pObjMgr->GetObjects(objects, pLayer); int count = pObjMgr->GetObjectCount(); AZStd::vector<AZStd::string> result; for (int i = 0; i < count; ++i) { result.push_back(objects[i]->GetName().toUtf8().data()); } return result; } std::vector<std::string> PyGetAllLayers() { CObjectLayerManager* pLayerMgr = GetIEditor()->GetObjectManager()->GetLayersManager(); std::vector<std::string> result; std::vector<CObjectLayer*> layers; pLayerMgr->GetLayers(layers); for (size_t i = 0; i < layers.size(); ++i) { result.push_back(layers[i]->GetName().toUtf8().data()); } return result; } AZStd::vector<AZStd::string> PyGetNamesOfSelectedObjects() { CSelectionGroup* pSel = GetIEditor()->GetSelection(); AZStd::vector<AZStd::string> result; const int selectionCount = pSel->GetCount(); result.reserve(selectionCount); for (int i = 0; i < selectionCount; i++) { result.push_back(pSel->GetObject(i)->GetName().toUtf8().data()); } return result; } void PySelectObject(const char* objName) { CUndo undo("Select Object"); CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(objName); if (pObject) { GetIEditor()->GetObjectManager()->SelectObject(pObject); } } void PyUnselectObjects(const AZStd::vector<AZStd::string>& names) { CUndo undo("Unselect Objects"); std::vector<CBaseObject*> pBaseObjects; for (int i = 0; i < names.size(); i++) { if (!GetIEditor()->GetObjectManager()->FindObject(names[i].c_str())) { throw std::logic_error((QString("\"") + names[i].c_str() + "\" is an invalid entity.").toUtf8().data()); } pBaseObjects.push_back(GetIEditor()->GetObjectManager()->FindObject(names[i].c_str())); } for (int i = 0; i < pBaseObjects.size(); i++) { GetIEditor()->GetObjectManager()->UnselectObject(pBaseObjects[i]); } } void PySelectObjects(const AZStd::vector<AZStd::string>& names) { CUndo undo("Select Objects"); CBaseObject* pObject; for (size_t i = 0; i < names.size(); ++i) { pObject = GetIEditor()->GetObjectManager()->FindObject(names[i].c_str()); if (!pObject) { throw std::logic_error((QString("\"") + names[i].c_str() + "\" is an invalid entity.").toUtf8().data()); } GetIEditor()->GetObjectManager()->SelectObject(pObject); } } bool PyIsObjectHidden(const char* objName) { CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(objName); if (!pObject) { throw std::logic_error((QString("\"") + objName + "\" is an invalid object name.").toUtf8().data()); } return pObject->IsHidden(); } void PyHideAllObjects() { CBaseObjectsArray baseObjects; GetIEditor()->GetObjectManager()->GetObjects(baseObjects); if (baseObjects.size() <= 0) { throw std::logic_error("Objects not found."); } CUndo undo("Hide All Objects"); for (int i = 0; i < baseObjects.size(); i++) { GetIEditor()->GetObjectManager()->HideObject(baseObjects[i], true); } } void PyUnHideAllObjects() { CUndo undo("Unhide All Objects"); GetIEditor()->GetObjectManager()->UnhideAll(); } void PyHideObject(const char* objName) { CUndo undo("Hide Object"); CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(objName); if (pObject) { GetIEditor()->GetObjectManager()->HideObject(pObject, true); } } void PyUnhideObject(const char* objName) { CUndo undo("Unhide Object"); CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(objName); if (pObject) { GetIEditor()->GetObjectManager()->HideObject(pObject, false); } } void PyFreezeObject(const char* objName) { CUndo undo("Freeze Object"); CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(objName); if (pObject) { GetIEditor()->GetObjectManager()->FreezeObject(pObject, true); } } void PyUnfreezeObject(const char* objName) { CUndo undo("Unfreeze Object"); CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(objName); if (pObject) { GetIEditor()->GetObjectManager()->FreezeObject(pObject, false); } } bool PyIsObjectFrozen(const char* objName) { CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(objName); if (!pObject) { throw std::logic_error((QString("\"") + objName + "\" is an invalid object name.").toUtf8().data()); } return pObject->IsFrozen(); } void PyDeleteObject(const char* objName) { CUndo undo("Delete Object"); CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(objName); if (pObject) { GetIEditor()->GetObjectManager()->DeleteObject(pObject); } } int PyClearSelection() { CUndo undo("Clear Selection"); return GetIEditor()->GetObjectManager()->ClearSelection(); } void PyDeleteSelected() { CUndo undo("Delete Selected Object"); GetIEditor()->GetObjectManager()->DeleteSelection(); } int PyGetNumSelectedObjects() { if (CSelectionGroup* pGroup = GetIEditor()->GetObjectManager()->GetSelection()) { return pGroup->GetCount(); } return 0; } AZ::Vector3 PyGetSelectionCenter() { if (CSelectionGroup* pGroup = GetIEditor()->GetObjectManager()->GetSelection()) { if (pGroup->GetCount() == 0) { throw std::runtime_error("Nothing selected"); } const Vec3 center = pGroup->GetCenter(); return AZ::Vector3(center.x, center.y, center.z); } throw std::runtime_error("Nothing selected"); } AZ::Aabb PyGetSelectionAABB() { if (CSelectionGroup* pGroup = GetIEditor()->GetObjectManager()->GetSelection()) { if (pGroup->GetCount() == 0) { throw std::runtime_error("Nothing selected"); } const AABB aabb = pGroup->GetBounds(); AZ::Aabb result; result.Set( AZ::Vector3( aabb.min.x, aabb.min.y, aabb.min.z ), AZ::Vector3( aabb.max.x, aabb.max.y, aabb.max.z ) ); return result; } throw std::runtime_error("Nothing selected"); } AZ::Vector3 PyGetObjectPosition(const char* pName) { CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(pName); if (!pObject) { throw std::logic_error((QString("\"") + pName + "\" is an invalid object.").toUtf8().data()); } Vec3 position = pObject->GetPos(); return AZ::Vector3(position.x, position.y, position.z); } AZ::Vector3 PyGetWorldObjectPosition(const char* pName) { CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(pName); if (!pObject) { throw std::logic_error((QString("\"") + pName + "\" is an invalid object.").toUtf8().data()); } Vec3 position = pObject->GetWorldPos(); return AZ::Vector3(position.x, position.y, position.z); } void PySetObjectPosition(const char* pName, float fValueX, float fValueY, float fValueZ) { CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(pName); if (!pObject) { throw std::logic_error((QString("\"") + pName + "\" is an invalid object.").toUtf8().data()); } CUndo undo("Set Object Base Position"); pObject->SetPos(Vec3(fValueX, fValueY, fValueZ)); } AZ::Vector3 PyGetObjectRotation(const char* pName) { CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(pName); if (!pObject) { throw std::logic_error((QString("\"") + pName + "\" is an invalid object.").toUtf8().data()); } Ang3 ang = RAD2DEG(Ang3(pObject->GetRotation())); return AZ::Vector3(ang.x, ang.y, ang.z); } void PySetObjectRotation(const char* pName, float fValueX, float fValueY, float fValueZ) { CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(pName); if (!pObject) { throw std::logic_error((QString("\"") + pName + "\" is an invalid object.").toUtf8().data()); } CUndo undo("Set Object Rotation"); pObject->SetRotation(Quat(DEG2RAD(Ang3(fValueX, fValueY, fValueZ)))); } AZ::Vector3 PyGetObjectScale(const char* pName) { CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(pName); if (!pObject) { throw std::logic_error((QString("\"") + pName + "\" is an invalid object.").toUtf8().data()); } Vec3 scaleVec3 = pObject->GetScale(); return AZ::Vector3(scaleVec3.x, scaleVec3.y, scaleVec3.z); } void PySetObjectScale(const char* pName, float fValueX, float fValueY, float fValueZ) { CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(pName); if (!pObject) { throw std::logic_error((QString("\"") + pName + "\" is an invalid object.").toUtf8().data()); } CUndo undo("Set Object Scale"); pObject->SetScale(Vec3(fValueX, fValueY, fValueZ)); } void PyRenameObject(const char* pOldName, const char* pNewName) { CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(pOldName); if (!pObject) { throw std::runtime_error("Could not find object"); } if (strcmp(pNewName, "") == 0 || GetIEditor()->GetObjectManager()->FindObject(pNewName)) { throw std::runtime_error("Invalid object name."); } CUndo undo("Rename object"); pObject->SetName(pNewName); } } namespace AzToolsFramework { void ObjectManagerFuncsHandler::Reflect(AZ::ReflectContext* context) { if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context)) { // this will put these methods into the 'azlmbr.legacy.general' module auto addLegacyGeneral = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder) { methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Category, "Legacy/Editor") ->Attribute(AZ::Script::Attributes::Module, "legacy.general"); }; addLegacyGeneral(behaviorContext->Method("get_all_objects", PyGetAllObjects, nullptr, "Gets the list of names of all objects in the whole level.")); addLegacyGeneral(behaviorContext->Method("get_names_of_selected_objects", PyGetNamesOfSelectedObjects, nullptr, "Get the name from selected object/objects.")); addLegacyGeneral(behaviorContext->Method("select_object", PySelectObject, nullptr, "Selects a specified object.")); addLegacyGeneral(behaviorContext->Method("unselect_objects", PyUnselectObjects, nullptr, "Unselects a list of objects.")); addLegacyGeneral(behaviorContext->Method("select_objects", PySelectObjects, nullptr, "Selects a list of objects.")); addLegacyGeneral(behaviorContext->Method("get_num_selected", PyGetNumSelectedObjects, nullptr, "Returns the number of selected objects.")); addLegacyGeneral(behaviorContext->Method("clear_selection", PyClearSelection, nullptr, "Clears selection.")); addLegacyGeneral(behaviorContext->Method("get_selection_center", PyGetSelectionCenter, nullptr, "Returns the center point of the selection group.")); addLegacyGeneral(behaviorContext->Method("get_selection_aabb", PyGetSelectionAABB, nullptr, "Returns the aabb of the selection group.")); addLegacyGeneral(behaviorContext->Method("hide_object", PyHideObject, nullptr, "Hides a specified object.")); addLegacyGeneral(behaviorContext->Method("is_object_hidden", PyIsObjectHidden, nullptr, "Checks if object is hidden and returns a bool value.")); addLegacyGeneral(behaviorContext->Method("unhide_object", PyUnhideObject, nullptr, "Unhides a specified object.")); addLegacyGeneral(behaviorContext->Method("hide_all_objects", PyHideAllObjects, nullptr, "Hides all objects.")); addLegacyGeneral(behaviorContext->Method("unhide_all_objects", PyUnHideAllObjects, nullptr, "Unhides all objects.")); addLegacyGeneral(behaviorContext->Method("freeze_object", PyFreezeObject, nullptr, "Freezes a specified object.")); addLegacyGeneral(behaviorContext->Method("is_object_frozen", PyIsObjectFrozen, nullptr, "Checks if object is frozen and returns a bool value.")); addLegacyGeneral(behaviorContext->Method("unfreeze_object", PyUnfreezeObject, nullptr, "Unfreezes a specified object.")); addLegacyGeneral(behaviorContext->Method("delete_object", PyDeleteObject, nullptr, "Deletes a specified object.")); addLegacyGeneral(behaviorContext->Method("delete_selected", PyDeleteSelected, nullptr, "Deletes selected object(s).")); addLegacyGeneral(behaviorContext->Method("get_position", PyGetObjectPosition, nullptr, "Gets the position of an object.")); addLegacyGeneral(behaviorContext->Method("set_position", PySetObjectPosition, nullptr, "Sets the position of an object.")); addLegacyGeneral(behaviorContext->Method("get_rotation", PyGetObjectRotation, nullptr, "Gets the rotation of an object.")); addLegacyGeneral(behaviorContext->Method("set_rotation", PySetObjectRotation, nullptr, "Sets the rotation of an object.")); addLegacyGeneral(behaviorContext->Method("get_scale", PyGetObjectScale, nullptr, "Gets the scale of an object.")); addLegacyGeneral(behaviorContext->Method("set_scale", PySetObjectScale, nullptr, "Sets the scale of an object.")); addLegacyGeneral(behaviorContext->Method("rename_object", PyRenameObject, nullptr, "Renames object with oldObjectName to newObjectName.")); } } }