You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2722 lines
80 KiB
C++
2722 lines
80 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
|
|
#include "EditorDefs.h"
|
|
|
|
#include "ObjectManager.h"
|
|
|
|
// Qt
|
|
#include <QMessageBox>
|
|
|
|
// Editor
|
|
#include "Settings.h"
|
|
#include "DisplaySettings.h"
|
|
#include "EntityObject.h"
|
|
#include "Viewport.h"
|
|
#include "GizmoManager.h"
|
|
#include "AxisGizmo.h"
|
|
#include "GameEngine.h"
|
|
#include "WaitProgress.h"
|
|
#include "Util/Image.h"
|
|
#include "ObjectManagerLegacyUndo.h"
|
|
#include "Include/HitContext.h"
|
|
#include "EditMode/DeepSelection.h"
|
|
#include "Plugins/ComponentEntityEditorPlugin/Objects/ComponentEntityObject.h"
|
|
|
|
#include <AzCore/Console/Console.h>
|
|
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
|
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
|
|
|
|
AZ_CVAR_EXTERNED(bool, ed_visibility_logTiming);
|
|
|
|
/*!
|
|
* 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:
|
|
virtual ~CXMLObjectClassDesc() = default;
|
|
REFGUID ClassID() override
|
|
{
|
|
return guid;
|
|
}
|
|
ObjectType GetObjectType() override { return superType->GetObjectType(); };
|
|
QString ClassName() override { return type; };
|
|
QString Category() override { return category; };
|
|
QObject* CreateQObject() const override { return superType->CreateQObject(); }
|
|
QString GetTextureIcon() override { return superType->GetTextureIcon(); };
|
|
QString GetFileSpec() override
|
|
{
|
|
if (!fileSpec.isEmpty())
|
|
{
|
|
return fileSpec;
|
|
}
|
|
else
|
|
{
|
|
return superType->GetFileSpec();
|
|
}
|
|
};
|
|
int GameCreationOrder() override { return superType->GameCreationOrder(); };
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CObjectManager implementation.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CObjectManager* g_pObjectManager = nullptr;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
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_pLoadProgress(nullptr)
|
|
, m_loadedObjects(0)
|
|
, m_totalObjectsToLoad(0)
|
|
, 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);
|
|
LoadRegistry();
|
|
|
|
AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CObjectManager::~CObjectManager()
|
|
{
|
|
AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
|
|
|
|
m_bExiting = true;
|
|
SaveRegistry();
|
|
DeleteAllObjects();
|
|
|
|
delete m_gizmoManager;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::RegisterObjectClasses()
|
|
{
|
|
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);
|
|
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 = nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
obj = nullptr;
|
|
}
|
|
GetIEditor()->GetErrorReport()->SetCurrentValidatorObject(nullptr);
|
|
}
|
|
|
|
GetIEditor()->ResumeUndo();
|
|
|
|
if (obj != nullptr && 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();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
if (!objNode->getAttr("Type", typeName))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (!objNode->getAttr("Id", id))
|
|
{
|
|
// Make new ID for object that doesn't have if.
|
|
id = AZ::Uuid::CreateRandom();
|
|
}
|
|
|
|
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 nullptr;
|
|
}
|
|
|
|
pObject = qobject_cast<CBaseObject*>(cls->CreateQObject());
|
|
assert(pObject);
|
|
pObject->SetClassDesc(cls);
|
|
pObject->m_guid = id;
|
|
|
|
pObject->InitVariables();
|
|
|
|
QString objName;
|
|
objNode->getAttr("Name", objName);
|
|
pObject->m_name = objName;
|
|
|
|
CBaseObject* obj = FindObject(pObject->GetId());
|
|
if (obj)
|
|
{
|
|
// 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").arg(pObject->m_name, obj->GetName());
|
|
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 nullptr;
|
|
//CoCreateGuid( &pObject->m_guid ); // generate uniq GUID for this object.
|
|
}
|
|
}
|
|
|
|
GetIEditor()->GetErrorReport()->SetCurrentValidatorObject(pObject);
|
|
if (!pObject->Init(GetIEditor(), nullptr, ""))
|
|
{
|
|
GetIEditor()->GetErrorReport()->SetCurrentValidatorObject(nullptr);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!AddObject(pObject))
|
|
{
|
|
GetIEditor()->GetErrorReport()->SetCurrentValidatorObject(nullptr);
|
|
return nullptr;
|
|
}
|
|
|
|
//pObject->Serialize( ar );
|
|
|
|
GetIEditor()->GetErrorReport()->SetCurrentValidatorObject(nullptr);
|
|
|
|
if (pObject != nullptr && pUndoObject == nullptr)
|
|
{
|
|
// 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 nullptr;
|
|
}
|
|
CBaseObject* pObject = NewObject(cls, prev, file, newObjectName);
|
|
return pObject;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::DeleteObject(CBaseObject* obj)
|
|
{
|
|
AZ_PROFILE_FUNCTION(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);
|
|
|
|
// 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));
|
|
}
|
|
|
|
AABB objAAB;
|
|
obj->GetBoundBox(objAAB);
|
|
GetIEditor()->GetGameEngine()->OnAreaModified(objAAB);
|
|
|
|
obj->Done();
|
|
|
|
NotifyObjectListeners(obj, CBaseObject::ON_DELETE);
|
|
|
|
RemoveObject(obj);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::DeleteSelection(CSelectionGroup* pSelection)
|
|
{
|
|
AZ_PROFILE_FUNCTION(Editor);
|
|
if (pSelection == nullptr)
|
|
{
|
|
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(Editor);
|
|
|
|
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_animatedAttachedEntities.clear();
|
|
}
|
|
|
|
CBaseObject* CObjectManager::CloneObject(CBaseObject* obj)
|
|
{
|
|
AZ_PROFILE_FUNCTION(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*)nullptr);
|
|
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(), nullptr);
|
|
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 (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)
|
|
{
|
|
assert(obj != 0);
|
|
|
|
InvalidateVisibleList();
|
|
|
|
// Handle removing object from type-specific containers if needed
|
|
{
|
|
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::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, "%s", 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);
|
|
|
|
// Add new name to map
|
|
const AZ::Crc32 nameCrc(newName.toUtf8().data(), newName.count(), true);
|
|
m_objectsByName[nameCrc] = obj;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CObjectManager::GetObjectCount() const
|
|
{
|
|
return static_cast<int>(m_objects.size());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::GetObjects(CBaseObjectsArray& 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::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::SendEvent(ObjectEvent event)
|
|
{
|
|
if (event == EVENT_RELOAD_ENTITY)
|
|
{
|
|
m_bInReloading = true;
|
|
}
|
|
|
|
for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
|
|
{
|
|
CBaseObject* obj = it->second;
|
|
obj->OnEvent(event);
|
|
}
|
|
|
|
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;
|
|
obj->GetBoundBox(box);
|
|
if (bounds.IsIntersectBox(box))
|
|
{
|
|
obj->OnEvent(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::Update()
|
|
{
|
|
if (m_bSkipObjectUpdate)
|
|
{
|
|
return;
|
|
}
|
|
|
|
QWidget* prevActiveWindow = QApplication::activeWindow();
|
|
|
|
// Restore focus if it changed.
|
|
if (prevActiveWindow && QApplication::activeWindow() != prevActiveWindow)
|
|
{
|
|
prevActiveWindow->setFocus();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
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 == nullptr)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
for (std::set<CEntityObject*>::iterator it = s.begin(), end = s.end(); it != end; ++it)
|
|
{
|
|
SelectObject(*it);
|
|
}
|
|
}
|
|
|
|
void CObjectManager::UnselectObject(CBaseObject* 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(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*)nullptr);
|
|
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*)nullptr);
|
|
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(nullptr);
|
|
|
|
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 = nullptr;
|
|
|
|
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(Editor);
|
|
|
|
// 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(Editor);
|
|
|
|
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(Editor);
|
|
CSelectionGroup* selection = stl::find_in_map(m_selections, name, (CSelectionGroup*)nullptr);
|
|
if (selection)
|
|
{
|
|
UnselectCurrent();
|
|
assert(selection != 0);
|
|
m_currSelection = selection;
|
|
SelectCurrent();
|
|
}
|
|
}
|
|
|
|
void CObjectManager::RemoveSelection(const QString& name)
|
|
{
|
|
AZ_PROFILE_FUNCTION(Editor);
|
|
|
|
QString selName = name;
|
|
CSelectionGroup* selection = stl::find_in_map(m_selections, name, (CSelectionGroup*)nullptr);
|
|
if (selection)
|
|
{
|
|
if (selection == m_currSelection)
|
|
{
|
|
UnselectCurrent();
|
|
m_currSelection = &m_defaultSelection;
|
|
m_defaultSelection.RemoveAll();
|
|
}
|
|
delete selection;
|
|
m_selections.erase(selName);
|
|
}
|
|
}
|
|
|
|
void CObjectManager::SelectCurrent()
|
|
{
|
|
AZ_PROFILE_FUNCTION(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(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(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();
|
|
}
|
|
|
|
if (dc.settings->IsDisplayHelpers())
|
|
{
|
|
// 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([[maybe_unused]] DisplayContext& dc)
|
|
{
|
|
AZ_Assert(false, "CObjectManager::ForceUpdateVisibleObjectCache is legacy/deprecated and should not be used.");
|
|
}
|
|
|
|
void CObjectManager::FindDisplayableObjects([[maybe_unused]] DisplayContext& dc, [[maybe_unused]] bool bDisplay)
|
|
{
|
|
AZ_Assert(false, "CObjectManager::FindDisplayableObjects is legacy/deprecated and should not be used.");
|
|
}
|
|
|
|
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;
|
|
|
|
// 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([[maybe_unused]] int flags)
|
|
{
|
|
m_bSingleSelection = false;
|
|
m_currEditObject = nullptr;
|
|
//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(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;
|
|
}
|
|
|
|
obj->GetBoundBox(objBounds);
|
|
if (box.IsIntersectBox(objBounds))
|
|
{
|
|
numSel++;
|
|
if (!bUnselect)
|
|
{
|
|
SelectObject(obj);
|
|
}
|
|
else
|
|
{
|
|
UnselectObject(obj);
|
|
}
|
|
}
|
|
}
|
|
return numSel;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CObjectManager::MoveObjects(const AABB& box, const Vec3& offset, ImageRotationDegrees rotation, [[maybe_unused]] 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::DeleteSelection()
|
|
{
|
|
AZ_PROFILE_FUNCTION(Editor);
|
|
|
|
// Make sure to unlock selection.
|
|
GetIEditor()->LockSelection(false);
|
|
|
|
CSelectionGroup objects;
|
|
for (int 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(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;
|
|
}
|
|
}
|
|
|
|
return (bSelectionHelperHit || obj->HitTest(hc));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CObjectManager::HitTest([[maybe_unused]] HitContext& hitInfo)
|
|
{
|
|
AZ_Assert(false, "CObjectManager::HitTest is legacy/deprecated and should not be used.");
|
|
return false;
|
|
}
|
|
|
|
void CObjectManager::FindObjectsInRect(
|
|
[[maybe_unused]] CViewport* view, [[maybe_unused]] const QRect& rect, [[maybe_unused]] std::vector<GUID>& guids)
|
|
{
|
|
AZ_Assert(false, "CObjectManager::FindObjectsInRect is legacy/deprecated and should not be used.");
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::SelectObjectsInRect(
|
|
[[maybe_unused]] CViewport* view, [[maybe_unused]] const QRect& rect, [[maybe_unused]] bool bSelect)
|
|
{
|
|
AZ_Assert(false, "CObjectManager::SelectObjectsInRect is legacy/deprecated and should not be used.");
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
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 != nullptr && cls->SystemClassID() == ESYSTEM_CLASS_OBJECT)
|
|
{
|
|
return (CObjectClassDesc*)cls;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
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(static_cast<int>(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 != nullptr && 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);
|
|
|
|
CObjectArchive ar(this, xmlNode, true);
|
|
|
|
// 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;
|
|
|
|
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;
|
|
}
|
|
|
|
XmlNodeRef objNode = root->newChild("Object");
|
|
ar.node = objNode;
|
|
obj->Serialize(ar);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::LoadObjects(CObjectArchive& objectArchive, bool bSelect)
|
|
{
|
|
m_bLoadingObjects = 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();
|
|
|
|
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;
|
|
// 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;
|
|
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')
|
|
m_visibleObjects.push_back(obj);
|
|
}
|
|
|
|
m_isUpdateVisibilityList = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
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(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() < 1 /*legacy axisGizmoMaxCount*/)
|
|
{
|
|
// Create axis gizmo for this object.
|
|
m_gizmoManager->AddGizmo(new CAxisGizmo(pObject));
|
|
}
|
|
}
|
|
|
|
if (bSelect)
|
|
{
|
|
NotifyObjectListeners(pObject, CBaseObject::ON_SELECT);
|
|
}
|
|
else
|
|
{
|
|
NotifyObjectListeners(pObject, CBaseObject::ON_UNSELECT);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::HideTransformManipulators()
|
|
{
|
|
m_gizmoManager->DeleteAllTransformManipulators();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::AddObjectEventListener(EventListener* listener)
|
|
{
|
|
stl::push_back_unique(m_objectEventListeners, listener);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::RemoveObjectEventListener(EventListener* listener)
|
|
{
|
|
stl::find_and_erase(m_objectEventListeners, listener);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::NotifyObjectListeners(CBaseObject* pObject, CBaseObject::EObjectListenerEvent event)
|
|
{
|
|
std::list<EventListener*>::iterator next;
|
|
for (std::list<EventListener*>::iterator it = m_objectEventListeners.begin(); it != m_objectEventListeners.end(); it = next)
|
|
{
|
|
next = it;
|
|
++next;
|
|
// Call listener callback.
|
|
(*it)->OnObjectEvent(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 = nullptr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectManager::GatherUsedResources(CUsedResources& resources)
|
|
{
|
|
CBaseObjectsArray objects;
|
|
GetIEditor()->GetObjectManager()->GetObjects(objects);
|
|
|
|
for (int i = 0; i < objects.size(); i++)
|
|
{
|
|
CBaseObject* pObject = objects[i];
|
|
pObject->GatherUsedResources(resources);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
IGizmoManager* CObjectManager::GetGizmoManager()
|
|
{
|
|
return m_gizmoManager;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
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::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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
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 (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 (pObj->HitTestRect(hc))
|
|
{
|
|
if (bSelect)
|
|
{
|
|
SelectObject(pObj);
|
|
}
|
|
else
|
|
{
|
|
UnselectObject(pObj);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CObjectManager::OnEditorModeActivated(
|
|
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
|
|
{
|
|
if (mode == AzToolsFramework::ViewportEditorMode::Component)
|
|
{
|
|
// 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(static_cast<int>(i)));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CObjectManager::OnEditorModeDeactivated(
|
|
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
|
|
{
|
|
if (mode == AzToolsFramework::ViewportEditorMode::Component)
|
|
{
|
|
// 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();
|
|
CBaseObjectsArray objects;
|
|
pObjMgr->GetObjects(objects);
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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."));
|
|
|
|
|
|
}
|
|
}
|
|
} // namespace AzToolsFramework
|