56904d1799
* Generic Cleanup Removals: * Unused cvars,member variables and defines * Windows media center edition support routines * CSystem - removed debug_GetCallStackRaw/GetRootWindowMessageHandler/ UnloadDLL/ShutdownModuleLibraries Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Fix CryMessageBox return values in Windows build Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Remove named selection group and a few smaller unused functionalities Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Remove ObjectManager export functionality + 2 deprecated functions Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Remove object legacy freeze/hide support from ObjectManager Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Remove ObjectManager duplicate name dection, as well as object renaming Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Remove ObjectManager serialization and selection callbacks Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * ObjectManager - remove MoveObjects/HitTestObject/EndEditParams and related members/functions Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Remove a bunch of unused clone related functionality + misc Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * ObjectManager - misc removals Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * ObjectManager - more removals Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Remove unused object selection/retrieval in ObjectManager and LoadObjects in ObjectLoader Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * CBaseObject - remove unused material layers mask support Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * More CBaseObject cleanups. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * CBaseObject - remove SubObj functions and IMouseCreateCallback Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Remove unused procedural floor management, helper scale and tags. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * CBaseObject - remove more unused methods. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Removals in multiple places. CBaseObject - unused `OBJTYPE_DUMMY` flag removed, member `IsSameClass` CObjectArchive removed unused methods/members CObjectManager removed unused `Update` method Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * More removals IDisplayViewport - HitTestLine/GetGridStep/setHitcontext/ GetConstructionPlane Unused Cry_Matrix44 template specializations. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * O3DE.exe Project-Centric "Open Editor" fix (#5852) * The O3DE.exe Open Editor button now attempts to open the Editor in the build directory of the project being opened. If their is no Editor within the build directory of the Project, it uses the Editor.exe in the current O3DE.exe executable directory if it exists Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Engine .gitignore now ignores the build directory if placed in the AutomatedTesting project Previously it was just ignoring a `[Bb]uild` directory if it was directly within the engine root. This change matches the behavior of the project templates. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Renamed the ProjectUtils GetEditorDirectory function to GetEditorExecutablePath Added a platform specific implementation for retrieving the path to the Editor executable in the GetEditorExectuablePath function. It first attempts to locate the Editor via checking the project build directory for an Editor executable before falling back to checking the binary directory of the currently running O3DE executable. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Correct the MacOS GetEditorExecutablePath to return the Editor path Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Adding missing C++20 std::erase implementations (#5735) There were already implementations for std::erase_if. This adds the counterpart AZStd::erase versions resolves #5734 Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * More removals and refactors `ShowMessage` and `CryMessageBox` return void now Simplify code in `CSystem::WarningV` Remove unused `CryGetTicksPerSec` Remove unused WinBase functionality Replace `nanosleep` call in WinBase with `std::this_thread::sleep_for` Remove unused Win32Wrapper routines Remove unused IFunctorBase.h and IEntityObjectListener.h Fix VectorAndArray.cpp compilation Use QMessageBox instead of CryMessageBox in the editor. Remove empty ArchiveVars platform specific files Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Fix test code. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Remove an unused function Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * CBaseObject and undo description removals. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * A bunch of removals * Remove IRenderNode * Remove editor's KDTree * Remove StatObjBus, InstanceStatObjEventBus::ReleaseData is redundant ( same functionality in the only available handler is triggered by `OnCryEditorCloseScene`) * Remove CExportManager::AddStatObj/AddMeshes/AddMesh, IExportManager::ExportSingleStatObj * Remove CIconManager/IIconManager::GetObject * Remove CBaseObject::IntersectRayMesh * Remove IIndexedMesh and related structs. * Unused IUndoObject::GetDescription and all derived implementations. * Unused CUndoBaseLibrary/CUndoBaseLibraryManager * Unused Matrix34_tpl typedefs * Legacy Xml classes cleanup Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Remove unused `Vec3ToVector3D` helper Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * fix - remove unused static variable Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
2414 lines
68 KiB
C++
2414 lines
68 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
|
|
*
|
|
*/
|
|
|
|
|
|
// Description : CBaseObject implementation.
|
|
|
|
|
|
#include "EditorDefs.h"
|
|
|
|
#include "BaseObject.h"
|
|
|
|
// Azframework
|
|
#include <AzFramework/Terrain/TerrainDataRequestBus.h> // for AzFramework::Terrain::TerrainDataRequests
|
|
|
|
// AzToolsFramework
|
|
#include <AzToolsFramework/API/ComponentEntityObjectBus.h> // for ComponentEntityObjectRequestBus
|
|
|
|
// Editor
|
|
#include "Settings.h"
|
|
#include "Viewport.h"
|
|
#include "DisplaySettings.h"
|
|
#include "Undo/Undo.h"
|
|
#include "UsedResources.h"
|
|
#include "GizmoManager.h"
|
|
#include "Include/IIconManager.h"
|
|
#include "Objects/SelectionGroup.h"
|
|
#include "Objects/ObjectManager.h"
|
|
#include "ViewManager.h"
|
|
#include "IEditorImpl.h"
|
|
#include "GameEngine.h"
|
|
// To use the Andrew's algorithm in order to make convex hull from the points, this header is needed.
|
|
#include "Util/GeometryUtil.h"
|
|
|
|
namespace {
|
|
QColor kLinkColorParent = QColor(0, 255, 255);
|
|
QColor kLinkColorChild = QColor(0, 0, 255);
|
|
QColor kLinkColorGray = QColor(128, 128, 128);
|
|
}
|
|
|
|
extern CObjectManager* g_pObjectManager;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Undo object for CBaseObject.
|
|
class CUndoBaseObject
|
|
: public IUndoObject
|
|
{
|
|
public:
|
|
CUndoBaseObject(CBaseObject* pObj);
|
|
|
|
protected:
|
|
int GetSize() override { return sizeof(*this); }
|
|
QString GetObjectName() override;
|
|
|
|
void Undo(bool bUndo) override;
|
|
void Redo() override;
|
|
|
|
protected:
|
|
GUID m_guid;
|
|
XmlNodeRef m_undo;
|
|
XmlNodeRef m_redo;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Undo object for CBaseObject that only stores its transform, color, area and minSpec
|
|
class CUndoBaseObjectMinimal
|
|
: public IUndoObject
|
|
{
|
|
public:
|
|
CUndoBaseObjectMinimal(CBaseObject* obj, int flags);
|
|
|
|
protected:
|
|
int GetSize() override { return sizeof(*this); }
|
|
QString GetObjectName() override;
|
|
|
|
void Undo(bool bUndo) override;
|
|
void Redo() override;
|
|
|
|
private:
|
|
struct StateStruct
|
|
{
|
|
Vec3 pos;
|
|
Quat rotate;
|
|
Vec3 scale;
|
|
QColor color;
|
|
float area;
|
|
int minSpec;
|
|
};
|
|
|
|
void SetTransformsFromState(CBaseObject* pObject, const StateStruct& state, bool bUndo);
|
|
|
|
GUID m_guid;
|
|
StateStruct m_undoState;
|
|
StateStruct m_redoState;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Undo object for attach/detach changes
|
|
class CUndoAttachBaseObject
|
|
: public IUndoObject
|
|
{
|
|
public:
|
|
CUndoAttachBaseObject(CBaseObject* pAttachedObject, bool bKeepPos, bool bAttach)
|
|
: m_attachedObjectGUID(pAttachedObject->GetId())
|
|
, m_parentObjectGUID(pAttachedObject->GetParent()->GetId())
|
|
, m_bKeepPos(bKeepPos)
|
|
, m_bAttach(bAttach) {}
|
|
|
|
void Undo([[maybe_unused]] bool bUndo) override
|
|
{
|
|
if (m_bAttach)
|
|
{
|
|
Detach();
|
|
}
|
|
else
|
|
{
|
|
Attach();
|
|
}
|
|
}
|
|
|
|
void Redo() override
|
|
{
|
|
if (m_bAttach)
|
|
{
|
|
Attach();
|
|
}
|
|
else
|
|
{
|
|
Detach();
|
|
}
|
|
}
|
|
|
|
private:
|
|
void Attach()
|
|
{
|
|
CObjectManager* pObjectManager = static_cast<CObjectManager*>(GetIEditor()->GetObjectManager());
|
|
CBaseObject* pObject = pObjectManager->FindObject(m_attachedObjectGUID);
|
|
CBaseObject* pParentObject = pObjectManager->FindObject(m_parentObjectGUID);
|
|
|
|
if (pObject && pParentObject)
|
|
{
|
|
pParentObject->AttachChild(pObject, m_bKeepPos);
|
|
}
|
|
}
|
|
|
|
void Detach()
|
|
{
|
|
CObjectManager* pObjectManager = static_cast<CObjectManager*>(GetIEditor()->GetObjectManager());
|
|
CBaseObject* pObject = pObjectManager->FindObject(m_attachedObjectGUID);
|
|
|
|
if (pObject)
|
|
{
|
|
pObject->DetachThis(m_bKeepPos);
|
|
}
|
|
}
|
|
|
|
int GetSize() override { return sizeof(CUndoAttachBaseObject); }
|
|
|
|
GUID m_attachedObjectGUID;
|
|
GUID m_parentObjectGUID;
|
|
bool m_bKeepPos;
|
|
bool m_bAttach;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CUndoBaseObject::CUndoBaseObject(CBaseObject* obj)
|
|
{
|
|
// Stores the current state of this object.
|
|
assert(obj != 0);
|
|
m_guid = obj->GetId();
|
|
|
|
m_redo = nullptr;
|
|
m_undo = XmlHelpers::CreateXmlNode("Undo");
|
|
CObjectArchive ar(GetIEditor()->GetObjectManager(), m_undo, false);
|
|
ar.bUndo = true;
|
|
obj->Serialize(ar);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
QString CUndoBaseObject::GetObjectName()
|
|
{
|
|
if (CBaseObject* obj = GetIEditor()->GetObjectManager()->FindObject(m_guid))
|
|
{
|
|
return obj->GetName();
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoBaseObject::Undo(bool bUndo)
|
|
{
|
|
CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(m_guid);
|
|
if (!pObject)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetIEditor()->SuspendUndo();
|
|
|
|
if (bUndo)
|
|
{
|
|
m_redo = XmlHelpers::CreateXmlNode("Redo");
|
|
// Save current object state.
|
|
CObjectArchive ar(GetIEditor()->GetObjectManager(), m_redo, false);
|
|
ar.bUndo = true;
|
|
pObject->Serialize(ar);
|
|
}
|
|
|
|
// Undo object state.
|
|
CObjectArchive ar(GetIEditor()->GetObjectManager(), m_undo, true);
|
|
ar.bUndo = true;
|
|
pObject->Serialize(ar);
|
|
|
|
GetIEditor()->ResumeUndo();
|
|
|
|
using namespace AzToolsFramework;
|
|
ComponentEntityObjectRequestBus::Event(pObject, &ComponentEntityObjectRequestBus::Events::UpdatePreemptiveUndoCache);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoBaseObject::Redo()
|
|
{
|
|
CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(m_guid);
|
|
if (!pObject)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetIEditor()->SuspendUndo();
|
|
|
|
CObjectArchive ar(GetIEditor()->GetObjectManager(), m_redo, true);
|
|
ar.bUndo = true;
|
|
|
|
pObject->Serialize(ar);
|
|
|
|
GetIEditor()->ResumeUndo();
|
|
|
|
using namespace AzToolsFramework;
|
|
ComponentEntityObjectRequestBus::Event(pObject, &ComponentEntityObjectRequestBus::Events::UpdatePreemptiveUndoCache);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CUndoBaseObjectMinimal::CUndoBaseObjectMinimal(CBaseObject* pObj, [[maybe_unused]] int flags)
|
|
{
|
|
// Stores the current state of this object.
|
|
assert(pObj != nullptr);
|
|
m_guid = pObj->GetId();
|
|
|
|
ZeroStruct(m_redoState);
|
|
|
|
m_undoState.pos = pObj->GetPos();
|
|
m_undoState.rotate = pObj->GetRotation();
|
|
m_undoState.scale = pObj->GetScale();
|
|
m_undoState.color = pObj->GetColor();
|
|
m_undoState.area = pObj->GetArea();
|
|
m_undoState.minSpec = pObj->GetMinSpec();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
QString CUndoBaseObjectMinimal::GetObjectName()
|
|
{
|
|
CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(m_guid);
|
|
if (!pObject)
|
|
{
|
|
return QString();
|
|
}
|
|
|
|
return pObject->GetName();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoBaseObjectMinimal::Undo(bool bUndo)
|
|
{
|
|
CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(m_guid);
|
|
if (!pObject)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (bUndo)
|
|
{
|
|
m_redoState.pos = pObject->GetPos();
|
|
m_redoState.scale = pObject->GetScale();
|
|
m_redoState.rotate = pObject->GetRotation();
|
|
m_redoState.color = pObject->GetColor();
|
|
m_redoState.area = pObject->GetArea();
|
|
m_redoState.minSpec = pObject->GetMinSpec();
|
|
}
|
|
|
|
SetTransformsFromState(pObject, m_undoState, bUndo);
|
|
|
|
pObject->ChangeColor(m_undoState.color);
|
|
pObject->SetArea(m_undoState.area);
|
|
pObject->SetMinSpec(m_undoState.minSpec, false);
|
|
|
|
using namespace AzToolsFramework;
|
|
ComponentEntityObjectRequestBus::Event(pObject, &ComponentEntityObjectRequestBus::Events::UpdatePreemptiveUndoCache);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoBaseObjectMinimal::Redo()
|
|
{
|
|
CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(m_guid);
|
|
if (!pObject)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetTransformsFromState(pObject, m_redoState, true);
|
|
|
|
pObject->ChangeColor(m_redoState.color);
|
|
pObject->SetArea(m_redoState.area);
|
|
pObject->SetMinSpec(m_redoState.minSpec, false);
|
|
|
|
using namespace AzToolsFramework;
|
|
ComponentEntityObjectRequestBus::Event(pObject, &ComponentEntityObjectRequestBus::Events::UpdatePreemptiveUndoCache);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CUndoBaseObjectMinimal::SetTransformsFromState(CBaseObject* pObject, const StateStruct& state, bool bUndo)
|
|
{
|
|
uint32 flags = eObjectUpdateFlags_Undo;
|
|
if (!bUndo)
|
|
{
|
|
flags |= eObjectUpdateFlags_UserInputUndo;
|
|
}
|
|
|
|
pObject->SetPos(state.pos, flags);
|
|
pObject->SetScale(state.scale, flags);
|
|
pObject->SetRotation(state.rotate, flags);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CObjectCloneContext::AddClone(CBaseObject* pFromObject, CBaseObject* pToObject)
|
|
{
|
|
m_objectsMap[pFromObject] = pToObject;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBaseObject* CObjectCloneContext::FindClone(CBaseObject* pFromObject)
|
|
{
|
|
CBaseObject* pTarget = stl::find_in_map(m_objectsMap, pFromObject, (CBaseObject*) nullptr);
|
|
return pTarget;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
GUID CObjectCloneContext::ResolveClonedID(REFGUID guid)
|
|
{
|
|
CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(guid);
|
|
CBaseObject* pClonedTarget = FindClone(pObject);
|
|
if (!pClonedTarget)
|
|
{
|
|
pClonedTarget = pObject; // If target not cloned, link to original target.
|
|
}
|
|
if (pClonedTarget)
|
|
{
|
|
return pClonedTarget->GetId();
|
|
}
|
|
return GUID_NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CBaseObject implementation.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBaseObject::CBaseObject()
|
|
: m_pos(0, 0, 0)
|
|
, m_rotate(IDENTITY)
|
|
, m_scale(1, 1, 1)
|
|
, m_guid(GUID_NULL)
|
|
, m_flags(0)
|
|
, m_nTextureIcon(0)
|
|
, m_color(QColor(255, 255, 255))
|
|
, m_worldTM(IDENTITY)
|
|
, m_lookat(nullptr)
|
|
, m_lookatSource(nullptr)
|
|
, m_flattenArea(0.f)
|
|
, m_classDesc(nullptr)
|
|
, m_numRefs(0)
|
|
, m_parent(nullptr)
|
|
, m_bInSelectionBox(false)
|
|
, m_pTransformDelegate(nullptr)
|
|
, m_bMatrixInWorldSpace(false)
|
|
, m_bMatrixValid(false)
|
|
, m_bWorldBoxValid(false)
|
|
, m_nMinSpec(0)
|
|
, m_vDrawIconPos(0, 0, 0)
|
|
, m_nIconFlags(0)
|
|
{
|
|
m_worldBounds.min.Set(0, 0, 0);
|
|
m_worldBounds.max.Set(0, 0, 0);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
IObjectManager* CBaseObject::GetObjectManager() const
|
|
{
|
|
return g_pObjectManager;
|
|
};
|
|
|
|
|
|
void CBaseObject::SetClassDesc(CObjectClassDesc* classDesc)
|
|
{
|
|
m_classDesc = classDesc;
|
|
}
|
|
|
|
//! Initialize Object.
|
|
bool CBaseObject::Init([[maybe_unused]] IEditor* ie, CBaseObject* prev, [[maybe_unused]] const QString& file)
|
|
{
|
|
SetFlags(m_flags & (~OBJFLAG_DELETED));
|
|
|
|
if (prev != nullptr)
|
|
{
|
|
SetUniqueName(prev->GetName());
|
|
SetLocalTM(prev->GetPos(), prev->GetRotation(), prev->GetScale());
|
|
SetArea(prev->GetArea());
|
|
SetColor(prev->GetColor());
|
|
SetMinSpec(prev->GetMinSpec(), false);
|
|
|
|
// Copy all basic variables.
|
|
EnableUpdateCallbacks(false);
|
|
CopyVariableValues(prev);
|
|
EnableUpdateCallbacks(true);
|
|
OnSetValues();
|
|
}
|
|
|
|
m_nTextureIcon = m_classDesc->GetTextureIconId();
|
|
if (m_classDesc->RenderTextureOnTop())
|
|
{
|
|
SetFlags(OBJFLAG_SHOW_ICONONTOP);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBaseObject::~CBaseObject()
|
|
{
|
|
for (Childs::iterator c = m_childs.begin(); c != m_childs.end(); c++)
|
|
{
|
|
CBaseObject* child = *c;
|
|
child->m_parent = nullptr;
|
|
}
|
|
m_childs.clear();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::Done()
|
|
{
|
|
DetachThis();
|
|
|
|
// From children
|
|
DetachAll();
|
|
|
|
SetLookAt(nullptr);
|
|
if (m_lookatSource)
|
|
{
|
|
m_lookatSource->SetLookAt(nullptr);
|
|
}
|
|
SetFlags(m_flags | OBJFLAG_DELETED);
|
|
|
|
NotifyListeners(CBaseObject::ON_DELETE);
|
|
m_eventListeners.clear();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetName(const QString& name)
|
|
{
|
|
if (name == m_name)
|
|
{
|
|
return;
|
|
}
|
|
|
|
StoreUndo();
|
|
|
|
// Notification is expensive and not required if this is during construction.
|
|
bool notify = (!m_name.isEmpty());
|
|
|
|
m_name = name;
|
|
GetObjectManager()->RegisterObjectName(name);
|
|
SetModified(false);
|
|
|
|
if (notify)
|
|
{
|
|
NotifyListeners(ON_RENAME);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetUniqueName(const QString& name)
|
|
{
|
|
SetName(GetObjectManager()->GenerateUniqueObjectName(name));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::GenerateUniqueName()
|
|
{
|
|
if (m_classDesc)
|
|
{
|
|
SetUniqueName(m_classDesc->ClassName());
|
|
}
|
|
else
|
|
{
|
|
SetUniqueName("Object");
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
const QString& CBaseObject::GetName() const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::SetPos(const Vec3& pos, int flags)
|
|
{
|
|
const Vec3 currentPos = GetPos();
|
|
|
|
bool equal = false;
|
|
if (flags & eObjectUpdateFlags_MoveTool) // very sensitive in case of the move tool
|
|
{
|
|
equal = IsVectorsEqual(currentPos, pos, 0.0f);
|
|
}
|
|
else // less sensitive for others
|
|
{
|
|
equal = IsVectorsEqual(currentPos, pos);
|
|
}
|
|
|
|
if (equal)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Check if position is bad.
|
|
if (fabs(pos.x) > AZ::Constants::MaxFloatBeforePrecisionLoss ||
|
|
fabs(pos.y) > AZ::Constants::MaxFloatBeforePrecisionLoss ||
|
|
fabs(pos.z) > AZ::Constants::MaxFloatBeforePrecisionLoss ||
|
|
!_finite(pos.x) || !_finite(pos.y) || !_finite(pos.z))
|
|
{
|
|
CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_WARNING,
|
|
"Object %s, SetPos called with invalid position: (%f,%f,%f)",
|
|
GetName().toUtf8().data(), pos.x, pos.y, pos.z);
|
|
return false;
|
|
}
|
|
|
|
OnBeforeAreaChange();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
const bool bPositionDelegated = m_pTransformDelegate && m_pTransformDelegate->IsPositionDelegated();
|
|
if (m_pTransformDelegate && (flags & eObjectUpdateFlags_Animated) == 0)
|
|
{
|
|
m_pTransformDelegate->SetTransformDelegatePos(pos);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
if (!bPositionDelegated && (flags & eObjectUpdateFlags_RestoreUndo) == 0 && (flags & eObjectUpdateFlags_Animated) == 0)
|
|
{
|
|
StoreUndo(true, flags);
|
|
}
|
|
|
|
if (!bPositionDelegated)
|
|
{
|
|
m_pos = pos;
|
|
}
|
|
|
|
if (!(flags & eObjectUpdateFlags_DoNotInvalidate))
|
|
{
|
|
InvalidateTM(flags | eObjectUpdateFlags_PositionChanged);
|
|
}
|
|
|
|
SetModified(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::SetRotation(const Quat& rotate, int flags)
|
|
{
|
|
const Quat currentRotate = GetRotation();
|
|
|
|
if (currentRotate.w == rotate.w && currentRotate.v.x == rotate.v.x
|
|
&& currentRotate.v.y == rotate.v.y && currentRotate.v.z == rotate.v.z)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (flags & eObjectUpdateFlags_ScaleTool)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
OnBeforeAreaChange();
|
|
|
|
const bool bRotationDelegated = m_pTransformDelegate && m_pTransformDelegate->IsRotationDelegated();
|
|
if (m_pTransformDelegate && (flags & eObjectUpdateFlags_Animated) == 0)
|
|
{
|
|
m_pTransformDelegate->SetTransformDelegateRotation(rotate);
|
|
}
|
|
|
|
if (!bRotationDelegated && (flags & eObjectUpdateFlags_RestoreUndo) == 0 && (flags & eObjectUpdateFlags_Animated) == 0)
|
|
{
|
|
StoreUndo(true, flags);
|
|
}
|
|
|
|
if (!bRotationDelegated)
|
|
{
|
|
m_rotate = rotate;
|
|
}
|
|
|
|
if (m_bMatrixValid && !(flags & eObjectUpdateFlags_DoNotInvalidate))
|
|
{
|
|
InvalidateTM(flags | eObjectUpdateFlags_RotationChanged);
|
|
}
|
|
|
|
SetModified(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::SetScale(const Vec3& scale, int flags)
|
|
{
|
|
if (IsVectorsEqual(GetScale(), scale))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Check if scale is bad.
|
|
if (scale.x < 0.01f || scale.y < 0.01f || scale.z < 0.01f)
|
|
{
|
|
CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_WARNING, "Object %s, SetScale called with invalid scale: (%f,%f,%f)", GetName().toUtf8().data(), scale.x, scale.y, scale.z);
|
|
return false;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
OnBeforeAreaChange();
|
|
|
|
const bool bScaleDelegated = m_pTransformDelegate && m_pTransformDelegate->IsScaleDelegated();
|
|
if (m_pTransformDelegate && (flags & eObjectUpdateFlags_Animated) == 0)
|
|
{
|
|
m_pTransformDelegate->SetTransformDelegateScale(scale);
|
|
}
|
|
|
|
if (!bScaleDelegated && (flags & eObjectUpdateFlags_RestoreUndo) == 0 && (flags & eObjectUpdateFlags_Animated) == 0)
|
|
{
|
|
StoreUndo(true, flags);
|
|
}
|
|
|
|
if (!bScaleDelegated)
|
|
{
|
|
m_scale = scale;
|
|
}
|
|
|
|
if (m_bMatrixValid && !(flags & eObjectUpdateFlags_DoNotInvalidate))
|
|
{
|
|
InvalidateTM(flags | eObjectUpdateFlags_ScaleChanged);
|
|
}
|
|
|
|
SetModified(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
const Vec3 CBaseObject::GetPos() const
|
|
{
|
|
if (!m_pTransformDelegate)
|
|
{
|
|
return m_pos;
|
|
}
|
|
|
|
return m_pTransformDelegate->GetTransformDelegatePos(m_pos);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
const Quat CBaseObject::GetRotation() const
|
|
{
|
|
if (!m_pTransformDelegate)
|
|
{
|
|
return m_rotate;
|
|
}
|
|
|
|
return m_pTransformDelegate->GetTransformDelegateRotation(m_rotate);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
const Vec3 CBaseObject::GetScale() const
|
|
{
|
|
if (!m_pTransformDelegate)
|
|
{
|
|
return m_scale;
|
|
}
|
|
|
|
return m_pTransformDelegate->GetTransformDelegateScale(m_scale);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::ChangeColor(const QColor& color)
|
|
{
|
|
if (color == m_color)
|
|
{
|
|
return;
|
|
}
|
|
|
|
StoreUndo(true);
|
|
|
|
SetColor(color);
|
|
SetModified(false);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetColor(const QColor& color)
|
|
{
|
|
m_color = color;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetArea(float area)
|
|
{
|
|
if (m_flattenArea == area)
|
|
{
|
|
return;
|
|
}
|
|
|
|
StoreUndo(true);
|
|
|
|
m_flattenArea = area;
|
|
SetModified(false);
|
|
};
|
|
|
|
//! Get bounding box of object in world coordinate space.
|
|
void CBaseObject::GetBoundBox(AABB& box)
|
|
{
|
|
if (!m_bWorldBoxValid)
|
|
{
|
|
GetLocalBounds(m_worldBounds);
|
|
if (!m_worldBounds.IsReset() && !m_worldBounds.IsEmpty())
|
|
{
|
|
m_worldBounds.SetTransformedAABB(GetWorldTM(), m_worldBounds);
|
|
m_bWorldBoxValid = true;
|
|
}
|
|
}
|
|
box = m_worldBounds;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::GetLocalBounds(AABB& box)
|
|
{
|
|
box.min.Set(0, 0, 0);
|
|
box.max.Set(0, 0, 0);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetModified(bool)
|
|
{
|
|
}
|
|
|
|
void CBaseObject::DrawDefault(DisplayContext& dc, const QColor& labelColor)
|
|
{
|
|
Vec3 wp = GetWorldPos();
|
|
|
|
bool bDisplaySelectionHelper = false;
|
|
if (!CanBeDrawn(dc, bDisplaySelectionHelper))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Draw link between parent and child.
|
|
if (dc.flags & DISPLAY_LINKS)
|
|
{
|
|
if (GetParent())
|
|
{
|
|
dc.DrawLine(GetParentAttachPointWorldTM().GetTranslation(), wp, IsFrozen() ? kLinkColorGray : kLinkColorParent, IsFrozen() ? kLinkColorGray : kLinkColorChild);
|
|
}
|
|
size_t nChildCount = GetChildCount();
|
|
for (size_t i = 0; i < nChildCount; ++i)
|
|
{
|
|
const CBaseObject* pChild = GetChild(i);
|
|
dc.DrawLine(pChild->GetParentAttachPointWorldTM().GetTranslation(), pChild->GetWorldPos(), pChild->IsFrozen() ? kLinkColorGray : kLinkColorParent, pChild->IsFrozen() ? kLinkColorGray : kLinkColorChild);
|
|
}
|
|
}
|
|
|
|
// Draw Bounding box
|
|
if (dc.flags & DISPLAY_BBOX)
|
|
{
|
|
AABB box;
|
|
GetBoundBox(box);
|
|
dc.SetColor(Vec3(1, 1, 1));
|
|
dc.DrawWireBox(box.min, box.max);
|
|
}
|
|
|
|
if (IsHighlighted())
|
|
{
|
|
DrawHighlight(dc);
|
|
}
|
|
|
|
if (IsSelected())
|
|
{
|
|
DrawArea(dc);
|
|
|
|
CSelectionGroup* pSelection = GetObjectManager()->GetSelection();
|
|
|
|
// If the number of selected object is over 2, the merged boundbox should be used to render the measurement axis.
|
|
if (!pSelection || (pSelection && pSelection->GetCount() == 1))
|
|
{
|
|
DrawDimensions(dc);
|
|
}
|
|
}
|
|
|
|
if (bDisplaySelectionHelper)
|
|
{
|
|
DrawSelectionHelper(dc, wp, labelColor, 1.0f);
|
|
}
|
|
else if (!(dc.flags & DISPLAY_HIDENAMES))
|
|
{
|
|
DrawLabel(dc, wp, labelColor);
|
|
}
|
|
|
|
SetDrawTextureIconProperties(dc, wp);
|
|
DrawTextureIcon(dc, wp);
|
|
DrawWarningIcons(dc, wp);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DrawDimensions(DisplayContext&, AABB*)
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DrawSelectionHelper(DisplayContext& dc, const Vec3& pos, const QColor& labelColor, [[maybe_unused]] float alpha)
|
|
{
|
|
DrawLabel(dc, pos, labelColor);
|
|
|
|
dc.SetColor(GetColor());
|
|
if (IsHighlighted() || IsSelected() || IsInSelectionBox())
|
|
{
|
|
dc.SetColor(dc.GetSelectedColor());
|
|
}
|
|
|
|
uint32 nPrevState = dc.GetState();
|
|
dc.DepthTestOff();
|
|
float r = dc.view->GetScreenScaleFactor(pos) * 0.006f;
|
|
dc.DrawWireBox(pos - Vec3(r, r, r), pos + Vec3(r, r, r));
|
|
dc.SetState(nPrevState);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetDrawTextureIconProperties(DisplayContext& dc, const Vec3& pos, float alpha, int texIconFlags)
|
|
{
|
|
if (gSettings.viewports.bShowIcons || gSettings.viewports.bShowSizeBasedIcons)
|
|
{
|
|
if (IsHighlighted())
|
|
{
|
|
dc.SetColor(QColor(255, 120, 0), 0.8f * alpha);
|
|
}
|
|
else if (IsSelected())
|
|
{
|
|
dc.SetSelectedColor(alpha);
|
|
}
|
|
else if (IsFrozen())
|
|
{
|
|
dc.SetFreezeColor();
|
|
}
|
|
else
|
|
{
|
|
dc.SetColor(QColor(255, 255, 255), alpha);
|
|
}
|
|
|
|
m_vDrawIconPos = pos;
|
|
|
|
int nIconFlags = texIconFlags;
|
|
if (CheckFlags(OBJFLAG_SHOW_ICONONTOP))
|
|
{
|
|
Vec3 objectPos = GetWorldPos();
|
|
|
|
AABB box;
|
|
GetBoundBox(box);
|
|
m_vDrawIconPos.z = (m_vDrawIconPos.z - objectPos.z) + box.max.z;
|
|
nIconFlags |= DisplayContext::TEXICON_ALIGN_BOTTOM;
|
|
}
|
|
m_nIconFlags = nIconFlags;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DrawTextureIcon(DisplayContext& dc, [[maybe_unused]] const Vec3& pos, [[maybe_unused]] float alpha)
|
|
{
|
|
if (m_nTextureIcon && (gSettings.viewports.bShowIcons || gSettings.viewports.bShowSizeBasedIcons))
|
|
{
|
|
dc.DrawTextureLabel(GetTextureIconDrawPos(), OBJECT_TEXTURE_ICON_SIZEX, OBJECT_TEXTURE_ICON_SIZEY, GetTextureIcon(), GetTextureIconFlags());
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DrawWarningIcons(DisplayContext& dc, const Vec3&)
|
|
{
|
|
if (gSettings.viewports.bShowIcons || gSettings.viewports.bShowSizeBasedIcons)
|
|
{
|
|
const int warningIconSizeX = OBJECT_TEXTURE_ICON_SIZEX / 2;
|
|
const int warningIconSizeY = OBJECT_TEXTURE_ICON_SIZEY / 2;
|
|
|
|
const int iconOffsetX = m_nTextureIcon ? (-OBJECT_TEXTURE_ICON_SIZEX / 2) : 0;
|
|
const int iconOffsetY = m_nTextureIcon ? (-OBJECT_TEXTURE_ICON_SIZEY / 2) : 0;
|
|
|
|
if (gSettings.viewports.bShowScaleWarnings)
|
|
{
|
|
const EScaleWarningLevel scaleWarningLevel = GetScaleWarningLevel();
|
|
|
|
if (scaleWarningLevel != eScaleWarningLevel_None)
|
|
{
|
|
dc.SetColor(QColor(255, scaleWarningLevel == eScaleWarningLevel_RescaledNonUniform ? 50 : 255, 50), 1.0f);
|
|
dc.DrawTextureLabel(GetTextureIconDrawPos(), warningIconSizeX, warningIconSizeY,
|
|
GetIEditor()->GetIconManager()->GetIconTexture(eIcon_ScaleWarning), GetTextureIconFlags(),
|
|
-warningIconSizeX / 2, iconOffsetX - (warningIconSizeY / 2));
|
|
}
|
|
}
|
|
|
|
if (gSettings.viewports.bShowRotationWarnings)
|
|
{
|
|
const ERotationWarningLevel rotationWarningLevel = GetRotationWarningLevel();
|
|
if (rotationWarningLevel != eRotationWarningLevel_None)
|
|
{
|
|
dc.SetColor(QColor(255, rotationWarningLevel == eRotationWarningLevel_RotatedNonRectangular ? 50 : 255, 50), 1.0f);
|
|
dc.DrawTextureLabel(GetTextureIconDrawPos(), warningIconSizeX, warningIconSizeY,
|
|
GetIEditor()->GetIconManager()->GetIconTexture(eIcon_RotationWarning), GetTextureIconFlags(),
|
|
warningIconSizeX / 2, iconOffsetY - (warningIconSizeY / 2));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DrawLabel(DisplayContext& dc, const Vec3& pos, const QColor& lC, float alpha, float size)
|
|
{
|
|
QColor labelColor = lC;
|
|
|
|
AABB box;
|
|
GetBoundBox(box);
|
|
|
|
//p.z = box.max.z + 0.2f;
|
|
if ((dc.flags & DISPLAY_2D) && labelColor == QColor(255, 255, 255))
|
|
{
|
|
labelColor = QColor(0, 0, 0);
|
|
}
|
|
|
|
if (dc.flags & DISPLAY_SELECTION_HELPERS)
|
|
{
|
|
Vec3 c(static_cast<f32>(labelColor.redF()), static_cast<f32>(labelColor.greenF()), static_cast<f32>(labelColor.redF()));
|
|
if (IsSelected())
|
|
{
|
|
c = Vec3(static_cast<f32>(dc.GetSelectedColor().redF()), static_cast<f32>(dc.GetSelectedColor().greenF()), static_cast<f32>(dc.GetSelectedColor().blueF()));
|
|
}
|
|
|
|
float col[4] = { c.x, c.y, c.z, 1 };
|
|
if (dc.flags & DISPLAY_SELECTION_HELPERS)
|
|
{
|
|
if (IsHighlighted())
|
|
{
|
|
c = Vec3(static_cast<f32>(dc.GetSelectedColor().redF()), static_cast<f32>(dc.GetSelectedColor().greenF()), static_cast<f32>(dc.GetSelectedColor().blueF()));
|
|
}
|
|
col[0] = c.x;
|
|
col[1] = c.y;
|
|
col[2] = c.z;
|
|
}
|
|
|
|
dc.SetColor(col[0], col[1], col[2], col[3] * alpha);
|
|
dc.DrawTextLabel(pos, size, GetName().toUtf8().data());
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DrawHighlight(DisplayContext& dc)
|
|
{
|
|
if (!m_nTextureIcon)
|
|
{
|
|
AABB box;
|
|
GetLocalBounds(box);
|
|
|
|
dc.PushMatrix(GetWorldTM());
|
|
dc.DrawWireBox(box.min, box.max);
|
|
dc.SetLineWidth(1);
|
|
dc.PopMatrix();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DrawBudgetUsage(DisplayContext& dc, const QColor& color)
|
|
{
|
|
AABB box;
|
|
GetLocalBounds(box);
|
|
|
|
dc.SetColor(color);
|
|
|
|
dc.PushMatrix(GetWorldTM());
|
|
dc.DrawWireBox(box.min, box.max);
|
|
dc.PopMatrix();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DrawAxis([[maybe_unused]] DisplayContext& dc, [[maybe_unused]] const Vec3& pos, [[maybe_unused]] float size)
|
|
{
|
|
/*
|
|
dc.renderer->EnableDepthTest(false);
|
|
Vec3 x(size,0,0);
|
|
Vec3 y(0,size,0);
|
|
Vec3 z(0,0,size);
|
|
|
|
bool bWorldSpace = false;
|
|
if (dc.flags & DISPLAY_WORLDSPACEAXIS)
|
|
bWorldSpace = true;
|
|
|
|
Matrix tm = GetWorldTM();
|
|
Vec3 org = tm.TransformPoint( pos );
|
|
|
|
if (!bWorldSpace)
|
|
{
|
|
tm.NoScale();
|
|
x = tm.TransformVector(x);
|
|
y = tm.TransformVector(y);
|
|
z = tm.TransformVector(z);
|
|
}
|
|
|
|
float fScreenScale = dc.view->GetScreenScaleFactor(org);
|
|
x = x * fScreenScale;
|
|
y = y * fScreenScale;
|
|
z = z * fScreenScale;
|
|
|
|
float col[4] = { 1,1,1,1 };
|
|
float hcol[4] = { 1,0,0,1 };
|
|
dc.renderer->DrawLabelEx( org+x,1.2f,col,true,true,"X" );
|
|
dc.renderer->DrawLabelEx( org+y,1.2f,col,true,true,"Y" );
|
|
dc.renderer->DrawLabelEx( org+z,1.2f,col,true,true,"Z" );
|
|
|
|
Vec3 colX(1,0,0),colY(0,1,0),colZ(0,0,1);
|
|
if (s_highlightAxis)
|
|
{
|
|
float col[4] = { 1,0,0,1 };
|
|
if (s_highlightAxis == 1)
|
|
{
|
|
colX.Set(1,1,0);
|
|
dc.renderer->DrawLabelEx( org+x,1.2f,col,true,true,"X" );
|
|
}
|
|
if (s_highlightAxis == 2)
|
|
{
|
|
colY.Set(1,1,0);
|
|
dc.renderer->DrawLabelEx( org+y,1.2f,col,true,true,"Y" );
|
|
}
|
|
if (s_highlightAxis == 3)
|
|
{
|
|
colZ.Set(1,1,0);
|
|
dc.renderer->DrawLabelEx( org+z,1.2f,col,true,true,"Z" );
|
|
}
|
|
}
|
|
|
|
x = x * 0.8f;
|
|
y = y * 0.8f;
|
|
z = z * 0.8f;
|
|
float fArrowScale = fScreenScale * 0.07f;
|
|
dc.SetColor( colX );
|
|
dc.DrawArrow( org,org+x,fArrowScale );
|
|
dc.SetColor( colY );
|
|
dc.DrawArrow( org,org+y,fArrowScale );
|
|
dc.SetColor( colZ );
|
|
dc.DrawArrow( org,org+z,fArrowScale );
|
|
|
|
//dc.DrawLine( org,org+x,colX,colX );
|
|
//dc.DrawLine( org,org+y,colY,colY );
|
|
//dc.DrawLine( org,org+z,colZ,colZ );
|
|
|
|
dc.renderer->EnableDepthTest(true);
|
|
///dc.SetColor( 0,1,1,1 );
|
|
//dc.DrawLine( p,p+dc.view->m_constructionPlane.m_normal*10.0f );
|
|
*/
|
|
}
|
|
|
|
void CBaseObject::DrawArea(DisplayContext& dc)
|
|
{
|
|
float area = m_flattenArea;
|
|
if (area > 0)
|
|
{
|
|
dc.SetColor(QColor(5, 5, 255), 1.f); // make it different color from the AI sight radius
|
|
Vec3 wp = GetWorldPos();
|
|
float z = GetIEditor()->GetTerrainElevation(wp.x, wp.y);
|
|
if (fabs(wp.z - z) < 5)
|
|
{
|
|
dc.DrawTerrainCircle(wp, area, 0.2f);
|
|
}
|
|
else
|
|
{
|
|
dc.DrawCircle(wp, area);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::CanBeDrawn(const DisplayContext& dc, bool& outDisplaySelectionHelper) const
|
|
{
|
|
bool bResult = true;
|
|
outDisplaySelectionHelper = false;
|
|
|
|
if (dc.flags & DISPLAY_SELECTION_HELPERS)
|
|
{
|
|
// Check if this object type is masked for selection.
|
|
if ((GetType() & gSettings.objectSelectMask) && !IsFrozen())
|
|
{
|
|
if (IsSkipSelectionHelper())
|
|
{
|
|
return bResult;
|
|
}
|
|
if (CanBeHightlighted())
|
|
{
|
|
outDisplaySelectionHelper = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Object helpers should not be displayed when object is not for selection.
|
|
bResult = false;
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::OnEvent(ObjectEvent event)
|
|
{
|
|
switch (event)
|
|
{
|
|
case EVENT_CONFIG_SPEC_CHANGE:
|
|
UpdateVisibility(!IsHidden());
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetHidden(bool bHidden, bool bAnimated)
|
|
{
|
|
if (CheckFlags(OBJFLAG_HIDDEN) != bHidden)
|
|
{
|
|
if (!bAnimated)
|
|
{
|
|
StoreUndo();
|
|
}
|
|
|
|
if (bHidden)
|
|
{
|
|
SetFlags(OBJFLAG_HIDDEN);
|
|
}
|
|
else
|
|
{
|
|
ClearFlags(OBJFLAG_HIDDEN);
|
|
}
|
|
|
|
UpdateVisibility(!IsHidden());
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetFrozen(bool bFrozen)
|
|
{
|
|
if (CheckFlags(OBJFLAG_FROZEN) != bFrozen)
|
|
{
|
|
StoreUndo();
|
|
if (bFrozen)
|
|
{
|
|
SetFlags(OBJFLAG_FROZEN);
|
|
}
|
|
else
|
|
{
|
|
ClearFlags(OBJFLAG_FROZEN);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetHighlight(bool bHighlight)
|
|
{
|
|
if (bHighlight)
|
|
{
|
|
SetFlags(OBJFLAG_HIGHLIGHT);
|
|
}
|
|
else
|
|
{
|
|
ClearFlags(OBJFLAG_HIGHLIGHT);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetSelected(bool bSelect)
|
|
{
|
|
if (bSelect)
|
|
{
|
|
SetFlags(OBJFLAG_SELECTED);
|
|
NotifyListeners(ON_SELECT);
|
|
|
|
//CLogFile::FormatLine( "Selected: %s, ID=%u",(const char*)m_name,m_id );
|
|
}
|
|
else
|
|
{
|
|
ClearFlags(OBJFLAG_SELECTED);
|
|
NotifyListeners(ON_UNSELECT);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IsHiddenBySpec() const
|
|
{
|
|
if (!gSettings.bApplyConfigSpecInEditor)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return (m_nMinSpec != 0 && gSettings.editorConfigSpec != 0 && m_nMinSpec > static_cast<uint32>(gSettings.editorConfigSpec));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Returns true if object hidden.
|
|
bool CBaseObject::IsHidden() const
|
|
{
|
|
return (CheckFlags(OBJFLAG_HIDDEN)) ||
|
|
(m_classDesc && (gSettings.objectHideMask & GetType()));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Returns true if object frozen.
|
|
bool CBaseObject::IsFrozen() const
|
|
{
|
|
return CheckFlags(OBJFLAG_FROZEN);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IsSelectable() const
|
|
{
|
|
// Not selectable if frozen.
|
|
return IsFrozen() ? false : true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::Serialize(CObjectArchive& ar)
|
|
{
|
|
XmlNodeRef xmlNode = ar.node;
|
|
|
|
ITransformDelegate* pTransformDelegate = m_pTransformDelegate;
|
|
m_pTransformDelegate = nullptr;
|
|
|
|
if (ar.bLoading)
|
|
{
|
|
// Loading.
|
|
int flags = 0;
|
|
int oldFlags = m_flags;
|
|
|
|
QString name = m_name;
|
|
QString mtlName;
|
|
|
|
Vec3 pos = m_pos;
|
|
Vec3 scale = m_scale;
|
|
Quat quat = m_rotate;
|
|
Ang3 angles(0, 0, 0);
|
|
uint32 nMinSpec = m_nMinSpec;
|
|
|
|
QColor color = m_color;
|
|
float flattenArea = m_flattenArea;
|
|
|
|
GUID parentId = GUID_NULL;
|
|
GUID lookatId = GUID_NULL;
|
|
|
|
xmlNode->getAttr("Name", name);
|
|
xmlNode->getAttr("Pos", pos);
|
|
if (!xmlNode->getAttr("Rotate", quat))
|
|
{
|
|
// Backward compatibility.
|
|
if (xmlNode->getAttr("Angles", angles))
|
|
{
|
|
angles = DEG2RAD(angles);
|
|
//angles.z += gf_PI/2;
|
|
quat.SetRotationXYZ(angles);
|
|
}
|
|
}
|
|
|
|
xmlNode->getAttr("Scale", scale);
|
|
xmlNode->getAttr("ColorRGB", color);
|
|
xmlNode->getAttr("FlattenArea", flattenArea);
|
|
xmlNode->getAttr("Flags", flags);
|
|
xmlNode->getAttr("Parent", parentId);
|
|
xmlNode->getAttr("LookAt", lookatId);
|
|
xmlNode->getAttr("Material", mtlName);
|
|
xmlNode->getAttr("MinSpec", nMinSpec);
|
|
|
|
if (nMinSpec <= CONFIG_VERYHIGH_SPEC) // Ignore invalid values.
|
|
{
|
|
m_nMinSpec = nMinSpec;
|
|
}
|
|
|
|
bool bHidden = flags & OBJFLAG_HIDDEN;
|
|
bool bFrozen = flags & OBJFLAG_FROZEN;
|
|
|
|
m_flags = flags;
|
|
m_flags &= ~OBJFLAG_PERSISTMASK;
|
|
m_flags |= (oldFlags) & (~OBJFLAG_PERSISTMASK);
|
|
//SetFlags( flags & OBJFLAG_PERSISTMASK );
|
|
m_flags &= ~OBJFLAG_SHARED; // clear shared flag
|
|
m_flags &= ~OBJFLAG_DELETED; // clear deleted flag
|
|
|
|
if (ar.bUndo)
|
|
{
|
|
DetachThis(false);
|
|
}
|
|
|
|
if (name != m_name)
|
|
{
|
|
// This may change object id.
|
|
SetName(name);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Check if position is bad.
|
|
if (fabs(pos.x) > AZ::Constants::MaxFloatBeforePrecisionLoss ||
|
|
fabs(pos.y) > AZ::Constants::MaxFloatBeforePrecisionLoss ||
|
|
fabs(pos.z) > AZ::Constants::MaxFloatBeforePrecisionLoss)
|
|
{
|
|
// File Not found.
|
|
CErrorRecord err;
|
|
err.error = QStringLiteral("Object %1 have invalid position (%2,%3,%4)").arg(GetName()).arg(pos.x).arg(pos.y).arg(pos.z);
|
|
err.pObject = this;
|
|
err.severity = CErrorRecord::ESEVERITY_WARNING;
|
|
GetIEditor()->GetErrorReport()->ReportError(err);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!ar.bUndo)
|
|
{
|
|
SetLocalTM(pos, quat, scale);
|
|
}
|
|
else
|
|
{
|
|
SetLocalTM(pos, quat, scale, eObjectUpdateFlags_Undo);
|
|
}
|
|
|
|
SetColor(color);
|
|
SetArea(flattenArea);
|
|
SetFrozen(bFrozen);
|
|
SetHidden(bHidden);
|
|
|
|
ar.SetResolveCallback(this, parentId, [this](CBaseObject* parent) { ResolveParent(parent); });
|
|
ar.SetResolveCallback(this, lookatId, [this](CBaseObject* target) { SetLookAt(target); });
|
|
|
|
InvalidateTM(0);
|
|
SetModified(false);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
}
|
|
else
|
|
{
|
|
// Saving.
|
|
|
|
// This attributed only readed by ObjectManager.
|
|
xmlNode->setAttr("Type", GetTypeName().toUtf8().data());
|
|
|
|
xmlNode->setAttr("Id", m_guid);
|
|
|
|
xmlNode->setAttr("Name", GetName().toUtf8().data());
|
|
|
|
if (m_parent)
|
|
{
|
|
xmlNode->setAttr("Parent", m_parent->GetId());
|
|
}
|
|
|
|
if (m_lookat)
|
|
{
|
|
xmlNode->setAttr("LookAt", m_lookat->GetId());
|
|
}
|
|
|
|
if (!IsEquivalent(GetPos(), Vec3(0, 0, 0), 0))
|
|
{
|
|
xmlNode->setAttr("Pos", GetPos());
|
|
}
|
|
|
|
xmlNode->setAttr("Rotate", m_rotate);
|
|
|
|
if (!IsEquivalent(GetScale(), Vec3(1, 1, 1), 0))
|
|
{
|
|
xmlNode->setAttr("Scale", GetScale());
|
|
}
|
|
|
|
xmlNode->setAttr("ColorRGB", GetColor());
|
|
|
|
if (GetArea() != 0)
|
|
{
|
|
xmlNode->setAttr("FlattenArea", GetArea());
|
|
}
|
|
|
|
int flags = m_flags & OBJFLAG_PERSISTMASK;
|
|
if (flags != 0)
|
|
{
|
|
xmlNode->setAttr("Flags", flags);
|
|
}
|
|
|
|
if (m_nMinSpec != 0)
|
|
{
|
|
xmlNode->setAttr("MinSpec", (uint32)m_nMinSpec);
|
|
}
|
|
}
|
|
|
|
// Serialize variables after default entity parameters.
|
|
CVarObject::Serialize(xmlNode, ar.bLoading);
|
|
|
|
m_pTransformDelegate = pTransformDelegate;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
XmlNodeRef CBaseObject::Export([[maybe_unused]] const QString& levelPath, XmlNodeRef& xmlNode)
|
|
{
|
|
XmlNodeRef objNode = xmlNode->newChild("Object");
|
|
|
|
objNode->setAttr("Type", GetTypeName().toUtf8().data());
|
|
objNode->setAttr("Name", GetName().toUtf8().data());
|
|
|
|
Vec3 pos, scale;
|
|
Quat rotate;
|
|
if (m_parent)
|
|
{
|
|
// Export world coordinates.
|
|
AffineParts ap;
|
|
ap.SpectralDecompose(GetWorldTM());
|
|
|
|
pos = ap.pos;
|
|
rotate = ap.rot;
|
|
scale = ap.scale;
|
|
}
|
|
else
|
|
{
|
|
pos = m_pos;
|
|
rotate = m_rotate;
|
|
scale = m_scale;
|
|
}
|
|
|
|
if (!IsEquivalent(pos, Vec3(0, 0, 0), 0))
|
|
{
|
|
objNode->setAttr("Pos", pos);
|
|
}
|
|
|
|
if (!rotate.IsIdentity())
|
|
{
|
|
objNode->setAttr("Rotate", rotate);
|
|
}
|
|
|
|
if (!IsEquivalent(scale, Vec3(0, 0, 0), 0))
|
|
{
|
|
objNode->setAttr("Scale", scale);
|
|
}
|
|
|
|
if (m_nMinSpec != 0)
|
|
{
|
|
objNode->setAttr("MinSpec", (uint32)m_nMinSpec);
|
|
}
|
|
|
|
// Save variables.
|
|
CVarObject::Serialize(objNode, false);
|
|
|
|
return objNode;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBaseObject* CBaseObject::FindObject(REFGUID id) const
|
|
{
|
|
return GetObjectManager()->FindObject(id);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::StoreUndo(bool minimal, int flags)
|
|
{
|
|
// Don't use Sandbox undo for AZ entities, except for the move & scale tools, which rely on it.
|
|
const bool isGizmoTool = 0 != (flags & (eObjectUpdateFlags_MoveTool | eObjectUpdateFlags_ScaleTool | eObjectUpdateFlags_UserInput));
|
|
if (!isGizmoTool && 0 != (m_flags & OBJFLAG_DONT_SAVE))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (CUndo::IsRecording())
|
|
{
|
|
if (minimal)
|
|
{
|
|
CUndo::Record(new CUndoBaseObjectMinimal(this, flags));
|
|
}
|
|
else
|
|
{
|
|
CUndo::Record(new CUndoBaseObject(this));
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
QString CBaseObject::GetTypeName() const
|
|
{
|
|
QString className = m_classDesc->ClassName();
|
|
QString subClassName = strstr(className.toUtf8().data(), "::");
|
|
if (subClassName.isEmpty())
|
|
{
|
|
return className;
|
|
}
|
|
|
|
QString name;
|
|
name.append(className.midRef(0, className.length() - subClassName.length()));
|
|
return name;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IntersectRectBounds(const AABB& bbox)
|
|
{
|
|
AABB aabb;
|
|
GetBoundBox(aabb);
|
|
|
|
return aabb.IsIntersectBox(bbox);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IntersectRayBounds(const Ray& ray)
|
|
{
|
|
Vec3 tmpPnt;
|
|
AABB aabb;
|
|
GetBoundBox(aabb);
|
|
|
|
return Intersect::Ray_AABB(ray, aabb, tmpPnt);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
namespace
|
|
{
|
|
using Edge2D = std::pair<Vec2, Vec2>;
|
|
}
|
|
bool IsIncludePointsInConvexHull(Edge2D* pEdgeArray0, int nEdgeArray0Size, Edge2D* pEdgeArray1, int nEdgeArray1Size)
|
|
{
|
|
if (!pEdgeArray0 || !pEdgeArray1 || nEdgeArray0Size <= 0 || nEdgeArray1Size <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static const float kPointEdgeMaxInsideDistance(0.05f);
|
|
|
|
bool bInside(true);
|
|
for (int i = 0; i < nEdgeArray0Size; ++i)
|
|
{
|
|
const Vec2& v(pEdgeArray0[i].first);
|
|
bInside = true;
|
|
for (int k = 0; k < nEdgeArray1Size; ++k)
|
|
{
|
|
Vec2 v0 = pEdgeArray1[k].first;
|
|
Vec2 v1 = pEdgeArray1[k].second;
|
|
Vec3 direction(v1.x - v0.x, v1.y - v0.y, 0);
|
|
Vec3 up(0, 0, 1);
|
|
Vec3 z = up.Cross(direction);
|
|
Vec2 normal;
|
|
normal.x = z.x;
|
|
normal.y = z.y;
|
|
normal.Normalize();
|
|
float distance = -normal.Dot(v0);
|
|
if (normal.Dot(v) + distance > kPointEdgeMaxInsideDistance)
|
|
{
|
|
bInside = false;
|
|
break;
|
|
}
|
|
}
|
|
if (bInside)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return bInside;
|
|
}
|
|
|
|
void ModifyConvexEdgeDirection(Edge2D* pEdgeArray, int nEdgeArraySize)
|
|
{
|
|
if (!pEdgeArray || nEdgeArraySize < 2)
|
|
{
|
|
return;
|
|
}
|
|
Vec3 v0(pEdgeArray[0].first.x - pEdgeArray[0].second.x, pEdgeArray[0].first.y - pEdgeArray[0].second.y, 0);
|
|
Vec3 v1(pEdgeArray[1].second.x - pEdgeArray[1].first.x, pEdgeArray[1].second.y - pEdgeArray[1].first.y, 0);
|
|
Vec3 vCross = v0.Cross(v1);
|
|
if (vCross.z < 0)
|
|
{
|
|
for (int i = 0; i < nEdgeArraySize; ++i)
|
|
{
|
|
std::swap(pEdgeArray[i].first, pEdgeArray[i].second);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CBaseObject::HitTestRectBounds(HitContext& hc, const AABB& box)
|
|
{
|
|
if (hc.bUseSelectionHelpers)
|
|
{
|
|
if (IsSkipSelectionHelper())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static const int kNumberOfBoundBoxPt(8);
|
|
|
|
// transform all 8 vertices into view space.
|
|
QPoint p[kNumberOfBoundBoxPt] =
|
|
{
|
|
hc.view->WorldToView(Vec3(box.min.x, box.min.y, box.min.z)),
|
|
hc.view->WorldToView(Vec3(box.min.x, box.max.y, box.min.z)),
|
|
hc.view->WorldToView(Vec3(box.max.x, box.min.y, box.min.z)),
|
|
hc.view->WorldToView(Vec3(box.max.x, box.max.y, box.min.z)),
|
|
hc.view->WorldToView(Vec3(box.min.x, box.min.y, box.max.z)),
|
|
hc.view->WorldToView(Vec3(box.min.x, box.max.y, box.max.z)),
|
|
hc.view->WorldToView(Vec3(box.max.x, box.min.y, box.max.z)),
|
|
hc.view->WorldToView(Vec3(box.max.x, box.max.y, box.max.z))
|
|
};
|
|
|
|
QRect objrc;
|
|
objrc.setLeft(10000);
|
|
objrc.setTop(10000);
|
|
objrc.setRight(-10000);
|
|
objrc.setBottom(-10000);
|
|
|
|
// find new min/max values
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
objrc.setLeft(min(objrc.left(), p[i].x()));
|
|
objrc.setRight(max(objrc.right(), p[i].x()));
|
|
objrc.setTop(min(objrc.top(), p[i].y()));
|
|
objrc.setBottom(max(objrc.bottom(), p[i].y()));
|
|
}
|
|
if (objrc.isEmpty())
|
|
{
|
|
// Make objrc at least of size 1.
|
|
objrc.moveBottomRight(objrc.bottomRight() + QPoint(1, 1));
|
|
}
|
|
|
|
if (hc.rect.contains(objrc.topLeft()) &&
|
|
hc.rect.contains(objrc.bottomLeft()) &&
|
|
hc.rect.contains(objrc.topRight()) &&
|
|
hc.rect.contains(objrc.bottomRight()))
|
|
{
|
|
hc.object = this;
|
|
return true;
|
|
}
|
|
|
|
if (objrc.intersects(hc.rect))
|
|
{
|
|
AABB localAABB;
|
|
GetLocalBounds(localAABB);
|
|
CBaseObject* pOldObj = hc.object;
|
|
hc.object = this;
|
|
if (localAABB.IsEmpty())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
const int kMaxSizeOfEdgeList0(4);
|
|
Edge2D edgelist0[kMaxSizeOfEdgeList0] = {
|
|
Edge2D(Vec2(static_cast<f32>(hc.rect.left()), static_cast<f32>(hc.rect.top())), Vec2(static_cast<f32>(hc.rect.right()), static_cast<f32>(hc.rect.top()))),
|
|
Edge2D(Vec2(static_cast<f32>(hc.rect.right()), static_cast<f32>(hc.rect.top())), Vec2(static_cast<f32>(hc.rect.right()), static_cast<f32>(hc.rect.bottom()))),
|
|
Edge2D(Vec2(static_cast<f32>(hc.rect.right()), static_cast<f32>(hc.rect.bottom())), Vec2(static_cast<f32>(hc.rect.left()), static_cast<f32>(hc.rect.bottom()))),
|
|
Edge2D(Vec2(static_cast<f32>(hc.rect.left()), static_cast<f32>(hc.rect.bottom())), Vec2(static_cast<f32>(hc.rect.left()), static_cast<f32>(hc.rect.top())))
|
|
};
|
|
|
|
const int kMaxSizeOfEdgeList1(8);
|
|
Edge2D edgelist1[kMaxSizeOfEdgeList1];
|
|
int nEdgeList1Count(kMaxSizeOfEdgeList1);
|
|
|
|
const Matrix34& worldTM(GetWorldTM());
|
|
OBB obb = OBB::CreateOBBfromAABB(Matrix33(worldTM), localAABB);
|
|
Vec3 ax = obb.m33.GetColumn0() * obb.h.x;
|
|
Vec3 ay = obb.m33.GetColumn1() * obb.h.y;
|
|
Vec3 az = obb.m33.GetColumn2() * obb.h.z;
|
|
QPoint obb_p[kMaxSizeOfEdgeList1] =
|
|
{
|
|
hc.view->WorldToView(-ax - ay - az + worldTM.GetTranslation()),
|
|
hc.view->WorldToView(-ax - ay + az + worldTM.GetTranslation()),
|
|
hc.view->WorldToView(-ax + ay - az + worldTM.GetTranslation()),
|
|
hc.view->WorldToView(-ax + ay + az + worldTM.GetTranslation()),
|
|
hc.view->WorldToView(ax - ay - az + worldTM.GetTranslation()),
|
|
hc.view->WorldToView(ax - ay + az + worldTM.GetTranslation()),
|
|
hc.view->WorldToView(ax + ay - az + worldTM.GetTranslation()),
|
|
hc.view->WorldToView(ax + ay + az + worldTM.GetTranslation())
|
|
};
|
|
|
|
std::vector<Vec3> pointsForRegion1;
|
|
pointsForRegion1.reserve(kMaxSizeOfEdgeList1);
|
|
for (int i = 0; i < kMaxSizeOfEdgeList1; ++i)
|
|
{
|
|
pointsForRegion1.push_back(Vec3(static_cast<f32>(obb_p[i].x()), static_cast<f32>(obb_p[i].y()), 0.0f));
|
|
}
|
|
|
|
std::vector<Vec3> convexHullForRegion1;
|
|
ConvexHull2D(convexHullForRegion1, pointsForRegion1);
|
|
nEdgeList1Count = static_cast<int>(convexHullForRegion1.size());
|
|
if (nEdgeList1Count < 3 || nEdgeList1Count > kMaxSizeOfEdgeList1)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
for (int i = 0; i < nEdgeList1Count; ++i)
|
|
{
|
|
int iNextI = (i + 1) % nEdgeList1Count;
|
|
edgelist1[i] = Edge2D(Vec2(convexHullForRegion1[i].x, convexHullForRegion1[i].y), Vec2(convexHullForRegion1[iNextI].x, convexHullForRegion1[iNextI].y));
|
|
}
|
|
|
|
ModifyConvexEdgeDirection(edgelist0, kMaxSizeOfEdgeList0);
|
|
ModifyConvexEdgeDirection(edgelist1, nEdgeList1Count);
|
|
|
|
bool bInside = IsIncludePointsInConvexHull(edgelist0, kMaxSizeOfEdgeList0, edgelist1, nEdgeList1Count);
|
|
if (!bInside)
|
|
{
|
|
bInside = IsIncludePointsInConvexHull(edgelist1, nEdgeList1Count, edgelist0, kMaxSizeOfEdgeList0);
|
|
}
|
|
if (!bInside)
|
|
{
|
|
hc.object = pOldObj;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::HitTestRect(HitContext& hc)
|
|
{
|
|
AZ_PROFILE_FUNCTION(Editor);
|
|
|
|
AABB box;
|
|
|
|
if (hc.bUseSelectionHelpers)
|
|
{
|
|
if (IsSkipSelectionHelper())
|
|
{
|
|
return false;
|
|
}
|
|
box.min = GetWorldPos();
|
|
box.max = box.min;
|
|
}
|
|
else
|
|
{
|
|
// Retrieve world space bound box.
|
|
GetBoundBox(box);
|
|
}
|
|
|
|
bool bHit = HitTestRectBounds(hc, box);
|
|
m_bInSelectionBox = bHit;
|
|
if (bHit)
|
|
{
|
|
hc.object = this;
|
|
}
|
|
return bHit;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBaseObject* CBaseObject::GetChild(size_t const i) const
|
|
{
|
|
assert(i < m_childs.size());
|
|
return m_childs[i];
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::AttachChild(CBaseObject* child, bool bKeepPos)
|
|
{
|
|
Matrix34 childTM;
|
|
ITransformDelegate* pTransformDelegate;
|
|
ITransformDelegate* pChildTransformDelegate;
|
|
|
|
{
|
|
CScopedSuspendUndo suspendUndo;
|
|
|
|
assert(child);
|
|
if (!child || child == GetLookAt())
|
|
{
|
|
return;
|
|
}
|
|
|
|
child->NotifyListeners(bKeepPos ? ON_PREATTACHEDKEEPXFORM : ON_PREATTACHED);
|
|
|
|
pTransformDelegate = m_pTransformDelegate;
|
|
pChildTransformDelegate = child->m_pTransformDelegate;
|
|
SetTransformDelegate(nullptr);
|
|
child->SetTransformDelegate(nullptr);
|
|
|
|
if (bKeepPos)
|
|
{
|
|
child->InvalidateTM(0);
|
|
childTM = child->GetWorldTM();
|
|
}
|
|
|
|
// If not already attached to this node.
|
|
if (child->m_parent == this)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Add to child list first to make sure node not get deleted while reattaching.
|
|
m_childs.push_back(child);
|
|
if (child->m_parent)
|
|
{
|
|
child->DetachThis(bKeepPos); // Detach node if attached to other parent.
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
child->m_parent = this; // Assign this node as parent to child node.
|
|
}
|
|
|
|
{
|
|
CScopedSuspendUndo suspendUndo;
|
|
|
|
{
|
|
if (bKeepPos)
|
|
{
|
|
child->SetWorldTM(childTM);
|
|
}
|
|
|
|
child->InvalidateTM(0);
|
|
}
|
|
|
|
m_pTransformDelegate = pTransformDelegate;
|
|
child->m_pTransformDelegate = pChildTransformDelegate;
|
|
|
|
child->NotifyListeners(ON_ATTACHED);
|
|
|
|
NotifyListeners(ON_CHILDATTACHED);
|
|
}
|
|
|
|
if (CUndo::IsRecording())
|
|
{
|
|
CUndo::Record(new CUndoAttachBaseObject(child, bKeepPos, true));
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DetachAll(bool bKeepPos)
|
|
{
|
|
while (!m_childs.empty())
|
|
{
|
|
CBaseObject* child = *m_childs.begin();
|
|
child->DetachThis(bKeepPos);
|
|
NotifyListeners(ON_CHILDDETACHED);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DetachThis(bool bKeepPos)
|
|
{
|
|
if (m_parent)
|
|
{
|
|
if (CUndo::IsRecording())
|
|
{
|
|
CUndo::Record(new CUndoAttachBaseObject(this, bKeepPos, false));
|
|
}
|
|
|
|
Matrix34 worldTM;
|
|
ITransformDelegate* pTransformDelegate;
|
|
|
|
{
|
|
CScopedSuspendUndo suspendUndo;
|
|
NotifyListeners(bKeepPos ? ON_PREDETACHEDKEEPXFORM : ON_PREDETACHED);
|
|
|
|
pTransformDelegate = m_pTransformDelegate;
|
|
SetTransformDelegate(nullptr);
|
|
|
|
if (bKeepPos)
|
|
{
|
|
ITransformDelegate* pParentTransformDelegate = m_parent->m_pTransformDelegate;
|
|
m_parent->SetTransformDelegate(nullptr);
|
|
worldTM = GetWorldTM();
|
|
m_parent->SetTransformDelegate(pParentTransformDelegate);
|
|
}
|
|
}
|
|
|
|
{
|
|
CScopedSuspendUndo suspendUndo;
|
|
|
|
// Copy parent to temp var, erasing child from parent may delete this node if child referenced only from parent.
|
|
CBaseObject* parent = m_parent;
|
|
m_parent = nullptr;
|
|
parent->RemoveChild(this);
|
|
|
|
if (bKeepPos)
|
|
{
|
|
// Keep old world space transformation.
|
|
SetWorldTM(worldTM);
|
|
}
|
|
|
|
SetTransformDelegate(pTransformDelegate);
|
|
|
|
NotifyListeners(ON_DETACHED);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::RemoveChild(CBaseObject* node)
|
|
{
|
|
Childs::iterator it = std::find(m_childs.begin(), m_childs.end(), node);
|
|
if (it != m_childs.end())
|
|
{
|
|
m_childs.erase(it);
|
|
NotifyListeners(ON_CHILDDETACHED);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::ResolveParent(CBaseObject* parent)
|
|
{
|
|
// even though parent is same as m_parent, adding the member to the parent must be done.
|
|
if (parent)
|
|
{
|
|
parent->AddMember(this, false);
|
|
}
|
|
else
|
|
{
|
|
DetachThis(false);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::CalcLocalTM(Matrix34& tm) const
|
|
{
|
|
tm.SetIdentity();
|
|
|
|
if (m_lookat)
|
|
{
|
|
Vec3 pos = GetPos();
|
|
|
|
if (m_parent)
|
|
{
|
|
// Get our world position.
|
|
pos = GetParentAttachPointWorldTM().TransformPoint(pos);
|
|
}
|
|
|
|
// Calculate local TM matrix differently.
|
|
Vec3 lookatPos = m_lookat->GetWorldPos();
|
|
if (lookatPos == pos) // if target at object pos
|
|
{
|
|
tm.SetTranslation(pos);
|
|
}
|
|
else
|
|
{
|
|
tm = Matrix34(Matrix33::CreateRotationVDir((lookatPos - pos).GetNormalized()), pos);
|
|
}
|
|
if (m_parent)
|
|
{
|
|
Matrix34 invParentTM = m_parent->GetWorldTM();
|
|
invParentTM.Invert();
|
|
tm = invParentTM * tm;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tm = Matrix34::Create(GetScale(), GetRotation(), GetPos());
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
const Matrix34& CBaseObject::GetWorldTM() const
|
|
{
|
|
if (!m_bMatrixValid)
|
|
{
|
|
m_worldTM = GetLocalTM();
|
|
m_bMatrixValid = true;
|
|
m_bMatrixInWorldSpace = false;
|
|
m_bWorldBoxValid = false;
|
|
}
|
|
if (!m_bMatrixInWorldSpace)
|
|
{
|
|
CBaseObject* parent = GetParent();
|
|
if (parent)
|
|
{
|
|
m_worldTM = GetParentAttachPointWorldTM() * m_worldTM;
|
|
}
|
|
m_bMatrixInWorldSpace = true;
|
|
m_bWorldBoxValid = false;
|
|
}
|
|
return m_worldTM;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
Matrix34 CBaseObject::GetParentAttachPointWorldTM() const
|
|
{
|
|
CBaseObject* pParent = GetParent();
|
|
if (pParent)
|
|
{
|
|
return pParent->GetWorldTM();
|
|
}
|
|
|
|
return Matrix34(IDENTITY);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::InvalidateTM([[maybe_unused]] int flags)
|
|
{
|
|
bool bMatrixWasValid = m_bMatrixValid;
|
|
|
|
m_bMatrixInWorldSpace = false;
|
|
m_bMatrixValid = false;
|
|
m_bWorldBoxValid = false;
|
|
|
|
// If matrix was valid, invalidate all childs.
|
|
if (bMatrixWasValid)
|
|
{
|
|
if (m_lookatSource)
|
|
{
|
|
m_lookatSource->InvalidateTM(eObjectUpdateFlags_ParentChanged);
|
|
}
|
|
|
|
// Invalidate matrices off all child objects.
|
|
for (int i = 0; i < m_childs.size(); i++)
|
|
{
|
|
if (m_childs[i] != nullptr && m_childs[i]->m_bMatrixValid)
|
|
{
|
|
m_childs[i]->InvalidateTM(eObjectUpdateFlags_ParentChanged);
|
|
}
|
|
}
|
|
NotifyListeners(ON_TRANSFORM);
|
|
|
|
// Notify parent that we were modified.
|
|
if (m_parent)
|
|
{
|
|
m_parent->OnChildModified();
|
|
}
|
|
}
|
|
|
|
if (m_pTransformDelegate)
|
|
{
|
|
m_pTransformDelegate->MatrixInvalidated();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetLocalTM(const Vec3& pos, const Quat& rotate, const Vec3& scale, int flags)
|
|
{
|
|
const bool b1 = SetPos(pos, flags | eObjectUpdateFlags_DoNotInvalidate);
|
|
const bool b2 = SetRotation(rotate, flags | eObjectUpdateFlags_DoNotInvalidate);
|
|
const bool b3 = SetScale(scale, flags | eObjectUpdateFlags_DoNotInvalidate);
|
|
|
|
if (b1 || b2 || b3 || flags == eObjectUpdateFlags_Animated)
|
|
{
|
|
flags = (b1) ? (flags | eObjectUpdateFlags_PositionChanged) : (flags & (~eObjectUpdateFlags_PositionChanged));
|
|
flags = (b2) ? (flags | eObjectUpdateFlags_RotationChanged) : (flags & (~eObjectUpdateFlags_RotationChanged));
|
|
flags = (b3) ? (flags | eObjectUpdateFlags_ScaleChanged) : (flags & (~eObjectUpdateFlags_ScaleChanged));
|
|
InvalidateTM(flags);
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetLocalTM(const Matrix34& tm, int flags)
|
|
{
|
|
if (m_lookat)
|
|
{
|
|
bool b1 = SetPos(tm.GetTranslation(), eObjectUpdateFlags_DoNotInvalidate);
|
|
flags = (b1) ? (flags | eObjectUpdateFlags_PositionChanged) : (flags & (~eObjectUpdateFlags_PositionChanged));
|
|
InvalidateTM(flags);
|
|
}
|
|
else
|
|
{
|
|
AffineParts affineParts;
|
|
affineParts.SpectralDecompose(tm);
|
|
SetLocalTM(affineParts.pos, affineParts.rot, affineParts.scale, flags);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetWorldPos(const Vec3& pos, int flags)
|
|
{
|
|
if (GetParent())
|
|
{
|
|
Matrix34 invParentTM = GetParentAttachPointWorldTM();
|
|
invParentTM.Invert();
|
|
Vec3 posLocal = invParentTM * pos;
|
|
SetPos(posLocal, flags);
|
|
}
|
|
else
|
|
{
|
|
SetPos(pos, flags);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetWorldTM(const Matrix34& tm, int flags)
|
|
{
|
|
if (GetParent())
|
|
{
|
|
Matrix34 invParentTM = GetParentAttachPointWorldTM();
|
|
invParentTM.Invert();
|
|
Matrix34 localTM = invParentTM * tm;
|
|
SetLocalTM(localTM, flags);
|
|
}
|
|
else
|
|
{
|
|
SetLocalTM(tm, flags);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::UpdateVisibility(bool bVisible)
|
|
{
|
|
if (bVisible == CheckFlags(OBJFLAG_INVISIBLE))
|
|
{
|
|
if (IObjectManager* objectManager = GetObjectManager())
|
|
{
|
|
objectManager->InvalidateVisibleList();
|
|
}
|
|
|
|
if (!bVisible)
|
|
{
|
|
m_flags |= OBJFLAG_INVISIBLE;
|
|
}
|
|
else
|
|
{
|
|
m_flags &= ~OBJFLAG_INVISIBLE;
|
|
}
|
|
|
|
NotifyListeners(ON_VISIBILITY);
|
|
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::AddGizmo(CGizmo* gizmo)
|
|
{
|
|
GetObjectManager()->GetGizmoManager()->AddGizmo(gizmo);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::RemoveGizmo(CGizmo* gizmo)
|
|
{
|
|
GetObjectManager()->GetGizmoManager()->RemoveGizmo(gizmo);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetLookAt(CBaseObject* target)
|
|
{
|
|
if (m_lookat == target)
|
|
{
|
|
return;
|
|
}
|
|
|
|
StoreUndo();
|
|
|
|
if (m_lookat)
|
|
{
|
|
// Unbind current lookat.
|
|
m_lookat->m_lookatSource = nullptr;
|
|
}
|
|
m_lookat = target;
|
|
if (m_lookat)
|
|
{
|
|
m_lookat->m_lookatSource = this;
|
|
}
|
|
|
|
InvalidateTM(0);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IsLookAtTarget() const
|
|
{
|
|
return m_lookatSource != nullptr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::AddEventListener(EventListener* listener)
|
|
{
|
|
if (find(m_eventListeners.begin(), m_eventListeners.end(), listener) == m_eventListeners.end())
|
|
{
|
|
m_eventListeners.push_back(listener);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::RemoveEventListener(EventListener* listener)
|
|
{
|
|
std::vector<EventListener*>::iterator listenerFound = find(m_eventListeners.begin(), m_eventListeners.end(), listener);
|
|
if (listenerFound != m_eventListeners.end())
|
|
{
|
|
(*listenerFound) = nullptr;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool IsBaseObjectEventCallbackNULL(CBaseObject::EventListener* listener) { return listener == nullptr; }
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::NotifyListeners(EObjectListenerEvent event)
|
|
{
|
|
for (auto it = m_eventListeners.begin(); it != m_eventListeners.end(); ++it)
|
|
{
|
|
// Call listener callback.
|
|
if (*it)
|
|
{
|
|
(*it)->OnObjectEvent(this, event);
|
|
}
|
|
}
|
|
|
|
m_eventListeners.erase(remove_if(m_eventListeners.begin(), m_eventListeners.end(), IsBaseObjectEventCallbackNULL), std::end(m_eventListeners));
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::ConvertFromObject(CBaseObject* object)
|
|
{
|
|
SetLocalTM(object->GetLocalTM());
|
|
SetName(object->GetName());
|
|
SetColor(object->GetColor());
|
|
m_flattenArea = object->m_flattenArea;
|
|
if (object->GetParent())
|
|
{
|
|
object->GetParent()->AttachChild(this);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IsPotentiallyVisible() const
|
|
{
|
|
if (CheckFlags(OBJFLAG_HIDDEN))
|
|
{
|
|
return false;
|
|
}
|
|
if (gSettings.objectHideMask & GetType())
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Analyze errors for this object.
|
|
void CBaseObject::Validate(IErrorReport* report)
|
|
{
|
|
// Checks for invalid values in base object.
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Check if position is bad.
|
|
const Vec3 pos = GetPos();
|
|
|
|
if (fabs(pos.x) > AZ::Constants::MaxFloatBeforePrecisionLoss ||
|
|
fabs(pos.y) > AZ::Constants::MaxFloatBeforePrecisionLoss ||
|
|
fabs(pos.z) > AZ::Constants::MaxFloatBeforePrecisionLoss)
|
|
{
|
|
// File Not found.
|
|
CErrorRecord err;
|
|
err.error = QStringLiteral("Object %1 have invalid position (%2,%3,%4)").arg(GetName()).arg(pos.x).arg(pos.y).arg(pos.z);
|
|
err.pObject = this;
|
|
report->ReportError(err);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
float minScale = 0.01f;
|
|
float maxScale = 1000.0f;
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Check if position is bad.
|
|
if (m_scale.x < minScale || m_scale.x > maxScale ||
|
|
m_scale.y < minScale || m_scale.y > maxScale ||
|
|
m_scale.z < minScale || m_scale.z > maxScale)
|
|
{
|
|
// File Not found.
|
|
CErrorRecord err;
|
|
err.error = QStringLiteral("Object %1 have invalid scale (%2,%3,%4)").arg(GetName()).arg(m_scale.x).arg(m_scale.y).arg(m_scale.z);
|
|
err.pObject = this;
|
|
report->ReportError(err);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::GatherUsedResources(CUsedResources& resources)
|
|
{
|
|
if (GetVarBlock())
|
|
{
|
|
GetVarBlock()->GatherUsedResources(resources);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IsSimilarObject(CBaseObject* pObject)
|
|
{
|
|
if (pObject->GetClassDesc() == GetClassDesc() && pObject->metaObject() == metaObject())
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetMinSpec(uint32 nSpec, bool bSetChildren)
|
|
{
|
|
m_nMinSpec = nSpec;
|
|
UpdateVisibility(!IsHidden());
|
|
|
|
// Set min spec for all childs.
|
|
if (bSetChildren)
|
|
{
|
|
for (int i = static_cast<int>(m_childs.size()) - 1; i >= 0; --i)
|
|
{
|
|
m_childs[i]->SetMinSpec(nSpec, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
EScaleWarningLevel CBaseObject::GetScaleWarningLevel() const
|
|
{
|
|
EScaleWarningLevel scaleWarningLevel = eScaleWarningLevel_None;
|
|
|
|
const float kScaleThreshold = 0.001f;
|
|
|
|
if (fabs(m_scale.x - 1.0f) > kScaleThreshold
|
|
|| fabs(m_scale.y - 1.0f) > kScaleThreshold
|
|
|| fabs(m_scale.z - 1.0f) > kScaleThreshold)
|
|
{
|
|
if (fabs(m_scale.x - m_scale.y) < kScaleThreshold
|
|
&& fabs(m_scale.y - m_scale.z) < kScaleThreshold)
|
|
{
|
|
scaleWarningLevel = eScaleWarningLevel_Rescaled;
|
|
}
|
|
else
|
|
{
|
|
scaleWarningLevel = eScaleWarningLevel_RescaledNonUniform;
|
|
}
|
|
}
|
|
|
|
return scaleWarningLevel;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
ERotationWarningLevel CBaseObject::GetRotationWarningLevel() const
|
|
{
|
|
ERotationWarningLevel rotationWarningLevel = eRotationWarningLevel_None;
|
|
|
|
const float kRotationThreshold = 0.01f;
|
|
|
|
Ang3 eulerRotation = Ang3(GetRotation());
|
|
|
|
if (fabs(eulerRotation.x) > kRotationThreshold
|
|
|| fabs(eulerRotation.y) > kRotationThreshold
|
|
|| fabs(eulerRotation.z) > kRotationThreshold)
|
|
{
|
|
const float xRotationMod = fabs(fmod(eulerRotation.x, gf_PI / 2.0f));
|
|
const float yRotationMod = fabs(fmod(eulerRotation.y, gf_PI / 2.0f));
|
|
const float zRotationMod = fabs(fmod(eulerRotation.z, gf_PI / 2.0f));
|
|
|
|
if ((xRotationMod < kRotationThreshold || xRotationMod > (gf_PI / 2.0f - kRotationThreshold))
|
|
&& (yRotationMod < kRotationThreshold || yRotationMod > (gf_PI / 2.0f - kRotationThreshold))
|
|
&& (zRotationMod < kRotationThreshold || zRotationMod > (gf_PI / 2.0f - kRotationThreshold)))
|
|
{
|
|
rotationWarningLevel = eRotationWarningLevel_Rotated;
|
|
}
|
|
else
|
|
{
|
|
rotationWarningLevel = eRotationWarningLevel_RotatedNonRectangular;
|
|
}
|
|
}
|
|
|
|
return rotationWarningLevel;
|
|
}
|
|
|
|
bool CBaseObject::IsSkipSelectionHelper() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool CBaseObject::CanBeHightlighted() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Matrix33 CBaseObject::GetWorldRotTM() const
|
|
{
|
|
if (GetParent())
|
|
{
|
|
return GetParent()->GetWorldRotTM() * Matrix33(GetRotation());
|
|
}
|
|
return Matrix33(GetRotation());
|
|
}
|
|
|
|
Matrix33 CBaseObject::GetWorldScaleTM() const
|
|
{
|
|
if (GetParent())
|
|
{
|
|
return GetParent()->GetWorldScaleTM() * Matrix33::CreateScale(GetScale());
|
|
}
|
|
return Matrix33::CreateScale(GetScale());
|
|
}
|
|
|
|
void CBaseObject::SetTransformDelegate(ITransformDelegate* pTransformDelegate)
|
|
{
|
|
m_pTransformDelegate = pTransformDelegate;
|
|
InvalidateTM(0);
|
|
}
|
|
|
|
void CBaseObject::AddMember(CBaseObject* pMember, bool bKeepPos /*=true */)
|
|
{
|
|
AttachChild(pMember, bKeepPos);
|
|
}
|
|
|
|
void CBaseObject::OnBeforeAreaChange()
|
|
{
|
|
AABB aabb;
|
|
GetBoundBox(aabb);
|
|
GetIEditor()->GetGameEngine()->OnAreaModified(aabb);
|
|
}
|
|
|
|
#include <Objects/moc_BaseObject.cpp>
|