1924 lines
46 KiB
C++
1924 lines
46 KiB
C++
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Crytek Engine Source File.
|
|
// Copyright (C), Crytek Studios, 2001.
|
|
// -------------------------------------------------------------------------
|
|
// File name: BaseObject.cpp
|
|
// Version: v1.00
|
|
// Created: 10/10/2001 by Timur.
|
|
// Compilers: Visual C++ 6.0
|
|
// Description: CBaseObject implementation.
|
|
// -------------------------------------------------------------------------
|
|
// History:
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "StdAfx.h"
|
|
#include "BaseObject.h"
|
|
#include "ObjectManager.h"
|
|
#include "Group.h"
|
|
#include "..\Viewport.h"
|
|
#include "..\ObjectPanel.h"
|
|
#include "..\PropertiesPanel.h"
|
|
#include "..\DisplaySettings.h"
|
|
#include "..\Settings.h"
|
|
|
|
#include "GizmoManager.h"
|
|
|
|
#include <IMovieSystem.h>
|
|
#include <ITimer.h>
|
|
|
|
#define AXIS_SIZE 0.15f
|
|
|
|
#define LINK_COLOR_PARENT RGB(0,255,255)
|
|
#define LINK_COLOR_CHILD RGB(0,0,255)
|
|
|
|
#define INVALID_POSITION_EPSILON 100000
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CBase implementation.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
IMPLEMENT_DYNAMIC(CBaseObject,CObject);
|
|
|
|
namespace {
|
|
int s_rollupIndex = 0;
|
|
CObjectPanel* s_objectPanel = 0;
|
|
|
|
int s_varsIndex = 0;
|
|
CPropertiesPanel* s_varsPanel = 0;
|
|
|
|
//! Which axis to highlight.
|
|
int s_highlightAxis = 0;
|
|
|
|
struct AutoReleaseAll
|
|
{
|
|
AutoReleaseAll() {};
|
|
~AutoReleaseAll()
|
|
{
|
|
delete s_objectPanel;
|
|
delete s_varsPanel;
|
|
}
|
|
} s_autoReleaseAll;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Undo object for CBaseObject.
|
|
class CUndoBaseObject : public IUndoObject
|
|
{
|
|
public:
|
|
CUndoBaseObject( CBaseObject *obj,const char *undoDescription )
|
|
{
|
|
// Stores the current state of this object.
|
|
assert( obj != 0 );
|
|
m_undoDescription = undoDescription;
|
|
m_object = obj;
|
|
m_redo = 0;
|
|
m_undo = new CXmlNode("Undo");
|
|
CObjectArchive ar(GetIEditor()->GetObjectManager(),m_undo,false );
|
|
ar.bUndo = true;
|
|
obj->Serialize( ar );
|
|
}
|
|
protected:
|
|
virtual int GetSize()
|
|
{
|
|
/*
|
|
CString m_undoStr,m_redoStr;
|
|
if (m_undo)
|
|
m_undoStr = m_undo->getXML();
|
|
if (m_redo)
|
|
m_redoStr = m_redo->getXML();
|
|
return sizeof(CUndoBaseObject) + m_undoStr.GetLength() + m_redoStr.GetLength() + m_undoDescription.GetLength();
|
|
*/
|
|
return sizeof(CUndoBaseObject);
|
|
}
|
|
virtual const char* GetDescription() { return m_undoDescription; };
|
|
|
|
virtual void Undo( bool bUndo )
|
|
{
|
|
GetIEditor()->SuspendUndo();
|
|
if (bUndo)
|
|
{
|
|
m_redo = new CXmlNode("Redo");
|
|
// Save current object state.
|
|
CObjectArchive ar(GetIEditor()->GetObjectManager(),m_redo,false );
|
|
ar.bUndo = true;
|
|
m_object->Serialize( ar );
|
|
}
|
|
// Undo object state.
|
|
CObjectArchive ar(GetIEditor()->GetObjectManager(),m_undo,true );
|
|
ar.bUndo = true;
|
|
m_object->Serialize( ar );
|
|
GetIEditor()->ResumeUndo();
|
|
}
|
|
virtual void Redo()
|
|
{
|
|
GetIEditor()->SuspendUndo();
|
|
CObjectArchive ar(GetIEditor()->GetObjectManager(),m_redo,true );
|
|
ar.bUndo = true;
|
|
m_object->Serialize( ar );
|
|
GetIEditor()->ResumeUndo();
|
|
}
|
|
|
|
private:
|
|
CString m_undoDescription;
|
|
CBaseObjectPtr m_object;
|
|
XmlNodeRef m_undo;
|
|
XmlNodeRef m_redo;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Undo object for CBaseObject.
|
|
class CUndoBaseObjectPos : public IUndoObject
|
|
{
|
|
public:
|
|
CUndoBaseObjectPos( CBaseObject *obj,const char *undoDescription )
|
|
{
|
|
// Stores the current state of this object.
|
|
assert( obj != 0 );
|
|
m_undoDescription = undoDescription;
|
|
m_object = obj;
|
|
|
|
ZeroStruct(m_redo);
|
|
m_undo.pos = obj->GetPos();
|
|
m_undo.angles = obj->GetAngles();
|
|
m_undo.scale = obj->GetScale();
|
|
m_undo.color = obj->GetColor();
|
|
m_undo.area = obj->GetArea();
|
|
}
|
|
protected:
|
|
virtual int GetSize() { return sizeof(*this); }
|
|
virtual const char* GetDescription() { return m_undoDescription; };
|
|
|
|
virtual void Undo( bool bUndo )
|
|
{
|
|
if (bUndo)
|
|
{
|
|
m_redo.pos = m_object->GetPos();
|
|
m_redo.angles = m_object->GetAngles();
|
|
m_redo.scale = m_object->GetScale();
|
|
m_redo.color = m_object->GetColor();
|
|
m_redo.area = m_object->GetArea();
|
|
}
|
|
m_object->SetPos(m_undo.pos);
|
|
m_object->SetAngles(m_undo.angles);
|
|
m_object->SetScale(m_undo.scale);
|
|
m_object->SetColor(m_undo.color);
|
|
m_object->SetArea(m_undo.area);
|
|
}
|
|
virtual void Redo()
|
|
{
|
|
m_object->SetPos(m_redo.pos);
|
|
m_object->SetAngles(m_redo.angles);
|
|
m_object->SetScale(m_redo.scale);
|
|
m_object->SetColor(m_redo.color);
|
|
m_object->SetArea(m_redo.area);
|
|
}
|
|
private:
|
|
CBaseObjectPtr m_object;
|
|
CString m_undoDescription;
|
|
struct StateStruct { Vec3 pos,angles,scale; COLORREF color; float area; };
|
|
StateStruct m_undo;
|
|
StateStruct m_redo;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
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*) NULL );
|
|
return pTarget;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CBaseObject implementation.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBaseObject::CBaseObject()
|
|
{
|
|
m_pos(0,0,0);
|
|
m_angles(0,0,0);
|
|
m_scale(1,1,1);
|
|
|
|
m_color = RGB(255,255,255);
|
|
|
|
m_group = 0;
|
|
|
|
m_flags = 0;
|
|
m_flattenArea = 0;
|
|
m_ie = 0;
|
|
|
|
m_numRefs = 0;
|
|
m_guid = GUID_NULL;
|
|
|
|
m_layer = 0;
|
|
|
|
m_parent = 0;
|
|
|
|
m_lookat = 0;
|
|
m_lookatSource = 0;
|
|
|
|
m_bMatrixInWorldSpace = false;
|
|
m_bMatrixValid = false;
|
|
m_ie = 0;
|
|
|
|
m_nextListener = m_eventListeners.end();
|
|
}
|
|
|
|
void CBaseObject::SetClassDesc( CObjectClassDesc *classDesc )
|
|
{
|
|
m_classDesc = classDesc;
|
|
}
|
|
|
|
//! Initialize Object.
|
|
bool CBaseObject::Init( IEditor *ie,CBaseObject *prev,const CString &file )
|
|
{
|
|
SetFlags( m_flags&(~OBJFLAG_DELETED) );
|
|
|
|
m_ie = ie;
|
|
if (prev != 0)
|
|
{
|
|
// Same layer.
|
|
SetLayer( prev->GetLayer() );
|
|
|
|
SetUniqName( prev->GetName() );
|
|
SetPos( prev->GetPos() );
|
|
SetAngles( prev->GetAngles() );
|
|
SetScale( prev->GetScale() );
|
|
SetArea( prev->GetArea() );
|
|
SetColor( prev->GetColor() );
|
|
SetMaterial( prev->GetMaterial() );
|
|
|
|
/*
|
|
// Clone important flags.
|
|
if (prev->CheckFlags(OBJFLAG_SHARED))
|
|
SetFlags(OBJFLAG_SHARED);
|
|
else
|
|
CheckFlags(OBJFLAG_SHARED);
|
|
*/
|
|
|
|
// Copy all basic variables.
|
|
CopyVariableValues(prev);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBaseObject::~CBaseObject()
|
|
{
|
|
for (Childs::iterator c = m_childs.begin(); c != m_childs.end(); c++)
|
|
{
|
|
CBaseObject* child = *c;
|
|
child->m_parent = 0;
|
|
}
|
|
m_childs.clear();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::Done()
|
|
{
|
|
DetachAll();
|
|
|
|
SetLookAt(0);
|
|
if (m_lookatSource)
|
|
{
|
|
m_lookatSource->SetLookAt(0);
|
|
}
|
|
SetFlags( m_flags|OBJFLAG_DELETED );
|
|
|
|
NotifyListeners( CBaseObject::ON_DELETE );
|
|
m_eventListeners.clear();
|
|
|
|
m_group = 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetName( const CString &name )
|
|
{
|
|
if (name == m_name)
|
|
return;
|
|
|
|
StoreUndo( "Name" );
|
|
|
|
m_name = name;
|
|
GetObjectManager()->RegisterObjectName( name );
|
|
SetModified();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetUniqName( const CString &name )
|
|
{
|
|
if (name == m_name)
|
|
return;
|
|
SetName( GetObjectManager()->GenUniqObjectName(name) );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
const CString& CBaseObject::GetName() const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IsSameClass( CBaseObject *obj )
|
|
{
|
|
return GetClassDesc() == obj->GetClassDesc();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetPos( const Vec3d &pos )
|
|
{
|
|
if (IsVectorsEqual(m_pos,pos))
|
|
return;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Check if position is bad.
|
|
if (fabs(pos.x) > INVALID_POSITION_EPSILON ||
|
|
fabs(pos.y) > INVALID_POSITION_EPSILON ||
|
|
fabs(pos.z) > INVALID_POSITION_EPSILON)
|
|
{
|
|
Log( "Invalid Position = %f,%f,%f",pos.x,pos.y,pos.z );
|
|
Warning( "Object %s, SetPos called with invalid position.\r\nPosition change ignored.\r\nCheck Log for more info.",(const char*)GetName() );
|
|
return;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
StoreUndo( "Position",true );
|
|
|
|
m_pos = pos;
|
|
m_height = pos.z - GetIEditor()->GetTerrainElevation(pos.x,pos.y);
|
|
if (m_bMatrixValid)
|
|
InvalidateTM();
|
|
SetModified();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetAngles( const Vec3d &angles )
|
|
{
|
|
if (IsVectorsEqual(m_angles,angles))
|
|
return;
|
|
|
|
StoreUndo( "Angles",true );
|
|
m_angles = angles;
|
|
if (m_bMatrixValid)
|
|
InvalidateTM();
|
|
SetModified();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetScale( const Vec3d &scale )
|
|
{
|
|
if (IsVectorsEqual(m_scale,scale))
|
|
return;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Check if position is bad.
|
|
if (scale.x < 0.01 || scale.y < 0.01 || scale.z < 0.01)
|
|
{
|
|
Log( "Invalid Scale = %f,%f,%f",scale.x,scale.y,scale.z );
|
|
Warning( "Object %s, SetScale called with invalid scale.\r\nScale change ignored.\r\nCheck Log for more info.",(const char*)GetName() );
|
|
return;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
StoreUndo( "Scale",true );
|
|
m_scale = scale;
|
|
if (m_bMatrixValid)
|
|
InvalidateTM();
|
|
SetModified();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetColor( COLORREF color )
|
|
{
|
|
if (color == m_color)
|
|
return;
|
|
|
|
StoreUndo( "Color",true );
|
|
|
|
m_color = color;
|
|
SetModified();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetArea( float area )
|
|
{
|
|
if (m_flattenArea == area)
|
|
return;
|
|
|
|
StoreUndo( "Area",true );
|
|
|
|
m_flattenArea = area;
|
|
SetModified();
|
|
};
|
|
|
|
/*
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::GetWorldBounds( BBox &box )
|
|
{
|
|
GetLocalBounds( box );
|
|
m_box = box;
|
|
|
|
Vec3d v[8];
|
|
v[0] = Vec3d(box.min.x,box.min.y,box.min.z);
|
|
v[1] = Vec3d(box.min.x,box.min.y,box.max.z);
|
|
v[2] = Vec3d(box.min.x,box.max.y,box.min.z);
|
|
v[3] = Vec3d(box.min.x,box.max.y,box.max.z);
|
|
v[4] = Vec3d(box.max.x,box.min.y,box.min.z);
|
|
v[5] = Vec3d(box.max.x,box.min.y,box.max.z);
|
|
v[6] = Vec3d(box.max.x,box.max.y,box.min.z);
|
|
v[7] = Vec3d(box.max.x,box.max.y,box.max.z);
|
|
|
|
box.min.Set(FLT_MAX,FLT_MAX,FLT_MAX);
|
|
box.max.Set(FLT_MIN,FLT_MIN,FLT_MIN);
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
box.min.x = std::min(box.min.x,v[i].x);
|
|
box.min.y = std::min(box.min.y,v[i].y);
|
|
box.min.z = std::min(box.min.z,v[i].z);
|
|
|
|
box.max.x = std::max(box.max.x,v[i].x);
|
|
box.max.y = std::max(box.max.y,v[i].y);
|
|
box.max.z = std::max(box.max.z,v[i].z);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::GetLocalBounds( BBox &box )
|
|
{
|
|
box = m_box;
|
|
}
|
|
*/
|
|
|
|
//! Get bounding box of object in world coordinate space.
|
|
void CBaseObject::GetBoundBox( BBox &box )
|
|
{
|
|
// Transform local bounding box into world space.
|
|
GetLocalBounds( box );
|
|
box.Transform( GetWorldTM() );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::GetLocalBounds( BBox &box )
|
|
{
|
|
box.min.Set(0,0,0);
|
|
box.max.Set(0,0,0);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::BeginEditParams( IEditor *ie,int flags )
|
|
{
|
|
if (!s_objectPanel)
|
|
{
|
|
s_objectPanel = new CObjectPanel(AfxGetMainWnd());
|
|
s_objectPanel->Create( CObjectPanel::IDD,AfxGetMainWnd() );
|
|
s_rollupIndex = ie->AddRollUpPage( ROLLUP_OBJECTS,GetTypeName(),s_objectPanel );
|
|
}
|
|
|
|
if (GetVarBlock())
|
|
{
|
|
if (!s_varsPanel)
|
|
s_varsPanel = new CPropertiesPanel( AfxGetMainWnd() );
|
|
else
|
|
s_varsPanel->DeleteVars();
|
|
s_varsPanel->AddVars( GetVarBlock() );
|
|
if (!s_varsIndex)
|
|
s_varsIndex = ie->AddRollUpPage( ROLLUP_OBJECTS,CString(GetTypeName()) + " Params",s_varsPanel );
|
|
}
|
|
|
|
assert( s_objectPanel != 0 );
|
|
if (s_objectPanel)
|
|
{
|
|
CObjectPanel::SParams uip;
|
|
uip.name = m_name;
|
|
uip.color = m_color;
|
|
uip.area = m_flattenArea;
|
|
//uip.flatten = CheckFlags(OBJFLAG_FLATTEN);
|
|
//uip.shared = CheckFlags(OBJFLAG_SHARED);
|
|
uip.layer = m_layer->GetName();
|
|
uip.helperScale = GetHelperScale();
|
|
s_objectPanel->SetParams( this,uip );
|
|
}
|
|
|
|
SetFlags( OBJFLAG_EDITING );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::EndEditParams( IEditor *ie )
|
|
{
|
|
OnUIUpdate();
|
|
|
|
ClearFlags( OBJFLAG_EDITING );
|
|
|
|
if (s_rollupIndex != 0)
|
|
ie->RemoveRollUpPage( ROLLUP_OBJECTS,s_rollupIndex );
|
|
s_rollupIndex = 0;
|
|
s_objectPanel = 0;
|
|
|
|
if (s_varsIndex != 0)
|
|
ie->RemoveRollUpPage( ROLLUP_OBJECTS,s_varsIndex );
|
|
s_varsIndex = 0;
|
|
s_varsPanel = 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::BeginEditMultiSelParams( bool bAllOfSameType )
|
|
{
|
|
if (!s_objectPanel)
|
|
{
|
|
s_objectPanel = new CObjectPanel(AfxGetMainWnd());
|
|
s_objectPanel->Create( CObjectPanel::IDD,AfxGetMainWnd() );
|
|
s_rollupIndex = m_ie->AddRollUpPage( ROLLUP_OBJECTS,GetTypeName(),s_objectPanel );
|
|
s_objectPanel->SetMultiSelect( true );
|
|
}
|
|
|
|
if (bAllOfSameType)
|
|
{
|
|
if (GetVarBlock())
|
|
{
|
|
if (!s_varsPanel)
|
|
s_varsPanel = new CPropertiesPanel( AfxGetMainWnd() );
|
|
else
|
|
s_varsPanel->DeleteVars();
|
|
|
|
// Add all selected objects.
|
|
CSelectionGroup *grp = GetIEditor()->GetSelection();
|
|
for (int i = 0; i < grp->GetCount(); i++)
|
|
{
|
|
CVarBlock *vb = grp->GetObject(i)->GetVarBlock();
|
|
if (vb)
|
|
s_varsPanel->AddVars( vb );
|
|
}
|
|
if (!s_varsIndex)
|
|
s_varsIndex = GetIEditor()->AddRollUpPage( ROLLUP_OBJECTS,CString(GetTypeName()) + " Params",s_varsPanel );
|
|
}
|
|
|
|
/*
|
|
if (m_params != 0 && GetPropertiesTemplate() != 0)
|
|
{
|
|
if (!s_paramsPanel)
|
|
{
|
|
s_paramsPanel = new CPropertiesPanel( AfxGetMainWnd() );
|
|
s_paramsPanel->SetMultiSelect(true);
|
|
s_paramsPanel->SetObject( this ); // Have to be before AddRollup.
|
|
s_paramsIndex = m_ie->AddRollUpPage( ROLLUP_OBJECTS,GetTypeName() + " Properties",s_paramsPanel );
|
|
}
|
|
else
|
|
{
|
|
s_paramsPanel->SetMultiSelect(true);
|
|
s_paramsPanel->SetObject( this );
|
|
}
|
|
}
|
|
*/
|
|
|
|
}
|
|
SetFlags( OBJFLAG_EDITING );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::EndEditMultiSelParams()
|
|
{
|
|
ClearFlags( OBJFLAG_EDITING );
|
|
|
|
if (s_rollupIndex != 0)
|
|
m_ie->RemoveRollUpPage( ROLLUP_OBJECTS,s_rollupIndex );
|
|
s_rollupIndex = 0;
|
|
s_objectPanel = 0;
|
|
|
|
if (s_varsIndex != 0)
|
|
m_ie->RemoveRollUpPage( ROLLUP_OBJECTS,s_varsIndex );
|
|
s_varsIndex = 0;
|
|
s_varsPanel = 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::OnUIUpdate()
|
|
{
|
|
if (s_objectPanel && !s_objectPanel->IsMultiSelect())
|
|
{
|
|
CObjectPanel::SParams uip;
|
|
s_objectPanel->GetParams( uip );
|
|
|
|
if (uip.area != m_flattenArea)
|
|
SetArea( uip.area );
|
|
|
|
/*
|
|
// First flags.
|
|
if (uip.flatten)
|
|
SetFlags(OBJFLAG_FLATTEN);
|
|
else
|
|
ClearFlags(OBJFLAG_FLATTEN);
|
|
*/
|
|
|
|
SetHelperScale( uip.helperScale );
|
|
|
|
//Timur[9/11/2002]
|
|
/*
|
|
if (uip.shared)
|
|
SetFlags(OBJFLAG_SHARED);
|
|
else
|
|
ClearFlags(OBJFLAG_SHARED);
|
|
*/
|
|
|
|
if (uip.name != m_name)
|
|
{
|
|
// This may also change object id.
|
|
GetObjectManager()->ChangeObjectName( this,uip.name );
|
|
}
|
|
if (uip.color != m_color )
|
|
SetColor( uip.color );
|
|
|
|
CObjectLayer *pLayer = GetObjectManager()->GetLayersManager()->FindLayerByName(uip.layer);
|
|
if (pLayer != m_layer)
|
|
SetLayer( pLayer );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::UpdateEditParams()
|
|
{
|
|
if (s_objectPanel && s_objectPanel->GetObject() == this)
|
|
{
|
|
// If its current object update its panel.
|
|
CObjectPanel::SParams uip;
|
|
uip.name = m_name;
|
|
uip.color = m_color;
|
|
uip.area = m_flattenArea;
|
|
//uip.flatten = CheckFlags(OBJFLAG_FLATTEN);
|
|
//uip.shared = CheckFlags(OBJFLAG_SHARED);
|
|
uip.layer = m_layer->GetName();
|
|
uip.helperScale = GetHelperScale();
|
|
s_objectPanel->SetParams( this,uip );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetModified()
|
|
{
|
|
/* Not needed! causes refresh of UI everytime object is moved.
|
|
if (CheckFlags(OBJFLAG_EDITING) && s_objectPanel != 0)
|
|
{
|
|
CObjectPanel::SParams uip;
|
|
uip.name = m_name;
|
|
uip.color = m_color;
|
|
uip.area = m_flattenArea;
|
|
uip.flatten = CheckFlags(OBJFLAG_FLATTEN);
|
|
uip.shared = CheckFlags(OBJFLAG_SHARED);
|
|
uip.layer = m_layer;
|
|
s_objectPanel->SetParams( uip );
|
|
}
|
|
*/
|
|
}
|
|
|
|
void CBaseObject::DrawDefault( DisplayContext &dc,COLORREF labelColor )
|
|
{
|
|
// Draw
|
|
if (dc.flags & DISPLAY_LINKS)
|
|
{
|
|
if (GetParent() && !(GetParent()->GetType() == OBJTYPE_GROUP) && !CheckFlags(OBJFLAG_PREFAB))
|
|
{
|
|
// Draw link between parent and child.
|
|
Vec3 parentPos = GetParent()->GetWorldPos();
|
|
dc.DrawLine( parentPos,GetWorldPos(),LINK_COLOR_PARENT,LINK_COLOR_CHILD );
|
|
}
|
|
}
|
|
|
|
if (dc.flags & DISPLAY_BBOX)
|
|
{
|
|
BBox box;
|
|
GetBoundBox( box );
|
|
dc.SetColor( Vec3(1,1,1) );
|
|
dc.DrawWireBox( box.min,box.max );
|
|
}
|
|
|
|
if (IsHighlighted())
|
|
{
|
|
DrawHighlight( dc );
|
|
}
|
|
|
|
if (IsSelected())
|
|
{
|
|
DrawArea( dc );
|
|
// Draw Axis.
|
|
//DrawAxis( dc,Vec3(0,0,0),AXIS_SIZE );
|
|
}
|
|
|
|
if (!(dc.flags & DISPLAY_HIDENAMES))
|
|
{
|
|
// Check if our group is not closed.
|
|
if (!m_group || m_group->IsOpen())
|
|
{
|
|
BBox box;
|
|
GetBoundBox( box );
|
|
Vec3 p = GetWorldPos();
|
|
p.z = box.max.z + 0.2f;
|
|
if ((dc.flags & DISPLAY_2D) && labelColor == RGB(255,255,255))
|
|
{
|
|
labelColor = RGB(0,0,0);
|
|
}
|
|
DrawLabel( dc,p,labelColor );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CBaseObject::DrawLabel( DisplayContext &dc,const Vec3 &pos,COLORREF labelColor )
|
|
{
|
|
float camDist = GetDistance(dc.camera->GetPos(), pos );
|
|
float maxDist = dc.settings->GetLabelsDistance();
|
|
if (camDist < dc.settings->GetLabelsDistance())
|
|
{
|
|
float range = maxDist / 2.0f;
|
|
Vec3 c = Rgb2Vec(labelColor);
|
|
if (IsSelected())
|
|
c = Rgb2Vec(dc.GetSelectedColor());
|
|
float col[4] = { c.x,c.y,c.z,1 };
|
|
if (camDist > range)
|
|
{
|
|
col[3] = col[3] * (1.0f - (camDist - range)/range);
|
|
}
|
|
dc.SetColor( col[0],col[1],col[2],col[3] );
|
|
//dc.renderer->DrawLabelEx( GetPos()+Vec3(0,0,1),1.0f,col,true,true,"%s",(const char*)GetName() );
|
|
//dc.renderer->DrawLabelEx( pos,1.0f,col,true,true,"%s",(const char*)GetName() );
|
|
dc.DrawTextLabel( pos,1,GetName() );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DrawHighlight( DisplayContext &dc )
|
|
{
|
|
//static int sizeRunner = 0;
|
|
BBox box;
|
|
GetLocalBounds( box );
|
|
dc.SetColor( RGB(255,120,0),0.8f );
|
|
//float s = 0.05f * sin( GetIEditor()->GetSystem()->GetITimer()->GetCurrTime() );
|
|
float s = 0.01f;
|
|
dc.PushMatrix( GetWorldTM() );
|
|
dc.DrawWireBox( box.min-Vec3(s,s,s),box.max+Vec3(s,s,s) );
|
|
dc.PopMatrix();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DrawAxis( DisplayContext &dc,const Vec3 &pos,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.renderer->SetMaterialColor( 0,1,0,1 );
|
|
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 );
|
|
|
|
/*
|
|
// Draw area radius.
|
|
dc.renderer->SetPolygonMode(R_WIREFRAME_MODE);
|
|
dc.renderer->SetMaterialColor( 0,1,1,0.2f );
|
|
//dc.renderer->DrawBall( areaPos,area );
|
|
dc.renderer->SetPolygonMode(R_SOLID_MODE);
|
|
*/
|
|
}
|
|
}
|
|
/*
|
|
void CBaseObject::GetMatrix( Matrix &tm,bool noScale )
|
|
{
|
|
tm.Identity();
|
|
tm.RotateMatrix( GetAngles() );
|
|
|
|
if (!noScale)
|
|
{
|
|
Vec3 s = GetScale();
|
|
if (s.x != 1 && s.y != 1 && s.z != 1)
|
|
{
|
|
Matrix tms;
|
|
tms.Identity();
|
|
tms[0][0] = s.x; tms[1][0] = 0; tms[2][0] = 0;
|
|
tms[0][1] = 0; tms[1][1] = s.y; tms[2][1] = 0;
|
|
tms[0][2] = 0; tms[1][2] = 0; tms[2][2] = s.z;
|
|
tm = tm*tms;
|
|
}
|
|
}
|
|
Vec3 p = GetPos();
|
|
tm[3][0] = p.x;
|
|
tm[3][1] = p.y;
|
|
tm[3][2] = p.z;
|
|
}
|
|
*/
|
|
|
|
int CBaseObject::MouseCreateCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags )
|
|
{
|
|
if (event == eMouseMove || event == eMouseLDown)
|
|
{
|
|
Vec3 pos = view->MapViewToCP(point);
|
|
SetPos( pos );
|
|
|
|
if (event == eMouseMove)
|
|
return MOUSECREATE_CONTINUE;
|
|
if (event == eMouseLDown)
|
|
return MOUSECREATE_OK;
|
|
}
|
|
return MOUSECREATE_CONTINUE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::OnEvent( ObjectEvent event )
|
|
{
|
|
switch (event)
|
|
{
|
|
case EVENT_KEEP_HEIGHT:
|
|
{
|
|
float h = m_height;
|
|
float newz = m_ie->GetTerrainElevation( m_pos.x,m_pos.y ) + m_height;
|
|
SetPos( Vec3(m_pos.x,m_pos.y,newz) );
|
|
m_height = h;
|
|
}
|
|
break;
|
|
|
|
case EVENT_AFTER_LOAD:
|
|
// Attach this to parent object.
|
|
//ResolveParent();
|
|
//ResolveLookAt();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetShared( bool bShared )
|
|
{
|
|
//Timur[9/11/2002]
|
|
/*
|
|
if (!GetGroup())
|
|
{
|
|
if (bShared)
|
|
SetFlags(OBJFLAG_SHARED);
|
|
else
|
|
ClearFlags(OBJFLAG_SHARED);
|
|
}
|
|
*/
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetHidden( bool bHidden )
|
|
{
|
|
if (CheckFlags(OBJFLAG_HIDDEN) != bHidden)
|
|
{
|
|
StoreUndo( "Hide Object" );
|
|
if (bHidden)
|
|
SetFlags(OBJFLAG_HIDDEN);
|
|
else
|
|
ClearFlags(OBJFLAG_HIDDEN);
|
|
|
|
GetObjectManager()->InvalidateVisibleList();
|
|
UpdateVisibility( !IsHidden() );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetFrozen( bool bFrozen )
|
|
{
|
|
if (CheckFlags(OBJFLAG_FROZEN) != bFrozen)
|
|
{
|
|
StoreUndo( "Freeze Object" );
|
|
if (bFrozen)
|
|
SetFlags(OBJFLAG_FROZEN);
|
|
else
|
|
ClearFlags(OBJFLAG_FROZEN);
|
|
}
|
|
if (bFrozen && IsSelected())
|
|
{
|
|
// If frozen must be unselected.
|
|
SetSelected(false);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
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 );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Returns true if object hidden.
|
|
bool CBaseObject::IsHidden() const
|
|
{
|
|
return (CheckFlags(OBJFLAG_HIDDEN)) ||
|
|
(!m_layer->IsVisible()) ||
|
|
(gSettings.objectHideMask & GetType());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Returns true if object frozen.
|
|
bool CBaseObject::IsFrozen() const
|
|
{
|
|
return CheckFlags(OBJFLAG_FROZEN) || m_layer->IsFrozen();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IsSelectable() const
|
|
{
|
|
// Not selectable if hidden.
|
|
if (IsHidden())
|
|
return false;
|
|
|
|
// Not selectable if frozen.
|
|
if (IsFrozen())
|
|
return false;
|
|
|
|
// Not selectable if in closed group.
|
|
CGroup* group = GetGroup();
|
|
if (group)
|
|
{
|
|
if (!group->IsOpen())
|
|
return false;
|
|
}
|
|
// Parts of prefab cannot be selected.
|
|
if (CheckFlags(OBJFLAG_PREFAB))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::Serialize( CObjectArchive &ar )
|
|
{
|
|
XmlNodeRef xmlNode = ar.node;
|
|
|
|
if (ar.bLoading)
|
|
{
|
|
// Loading.
|
|
|
|
int flags = 0;
|
|
int oldFlags = m_flags;
|
|
///xmlNode->getAttr( "Id",m_id );
|
|
|
|
CObjectLayer *pLayer = 0;
|
|
// Find layer name.
|
|
CString layerName;
|
|
if (xmlNode->getAttr( "Layer",layerName ))
|
|
{
|
|
pLayer = GetObjectManager()->GetLayersManager()->FindLayerByName(layerName);
|
|
}
|
|
|
|
CString name = m_name;
|
|
Vec3 pos = m_pos,angles=m_angles,scale=m_scale;
|
|
COLORREF color = m_color;
|
|
float flattenArea = m_flattenArea;
|
|
|
|
GUID parentId = GUID_NULL;
|
|
GUID lookatId = GUID_NULL;
|
|
|
|
xmlNode->getAttr( "Name",name );
|
|
xmlNode->getAttr( "Pos",pos );
|
|
xmlNode->getAttr( "Angles",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 );
|
|
|
|
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
|
|
|
|
ar.SetResolveCallback( this,parentId,functor(*this,&CBaseObject::ResolveParent) );
|
|
ar.SetResolveCallback( this,lookatId,functor(*this,&CBaseObject::SetLookAt) );
|
|
|
|
if (name != m_name)
|
|
{
|
|
// This may change object id.
|
|
SetName( name );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Check if position is bad.
|
|
if (fabs(pos.x) > INVALID_POSITION_EPSILON ||
|
|
fabs(pos.y) > INVALID_POSITION_EPSILON ||
|
|
fabs(pos.z) > INVALID_POSITION_EPSILON)
|
|
{
|
|
// File Not found.
|
|
CErrorRecord err;
|
|
err.error.Format( "Object %s have invalid position (%f,%f,%f)",(const char*)GetName(),pos.x,pos.y,pos.z );
|
|
err.pObject = this;
|
|
err.severity = CErrorRecord::ESEVERITY_WARNING;
|
|
GetIEditor()->GetErrorReport()->ReportError(err);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool bMatrixWasValid = m_bMatrixValid;
|
|
m_bMatrixValid = false;
|
|
SetPos( pos );
|
|
SetAngles( angles );
|
|
SetScale( scale );
|
|
m_bMatrixValid = bMatrixWasValid;
|
|
|
|
SetColor( color );
|
|
SetArea( flattenArea );
|
|
SetFrozen( bFrozen );
|
|
SetHidden( bHidden );
|
|
|
|
if (pLayer)
|
|
SetLayer( pLayer );
|
|
|
|
InvalidateTM();
|
|
SetModified();
|
|
|
|
if (ar.bUndo)
|
|
{
|
|
// If we are selected update UI Panel.
|
|
UpdateEditParams();
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
// Saving.
|
|
|
|
// This attributed only readed by ObjectManager.
|
|
xmlNode->setAttr( "Type",GetTypeName() );
|
|
|
|
if (m_layer)
|
|
xmlNode->setAttr( "Layer",m_layer->GetName() );
|
|
|
|
xmlNode->setAttr( "Id",m_guid );
|
|
xmlNode->setAttr( "Name",GetName() );
|
|
|
|
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() );
|
|
|
|
if (!IsEquivalent(GetAngles(),Vec3(0,0,0),0))
|
|
xmlNode->setAttr( "Angles",GetAngles() );
|
|
|
|
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 );
|
|
}
|
|
|
|
// Serialize variables after default entity parameters.
|
|
CVarObject::Serialize( xmlNode,ar.bLoading );
|
|
}
|
|
|
|
XmlNodeRef CBaseObject::Export( const CString &levelPath,XmlNodeRef &xmlNode )
|
|
{
|
|
XmlNodeRef objNode = xmlNode->newChild( "Object" );
|
|
|
|
objNode->setAttr( "Type",GetTypeName() );
|
|
|
|
objNode->setAttr( "Name",GetName() );
|
|
|
|
Vec3 pos,angles,scale;
|
|
if (m_parent)
|
|
{
|
|
// Export world coordinates.
|
|
AffineParts ap;
|
|
ap.SpectralDecompose( GetWorldTM() );
|
|
|
|
pos = ap.pos;
|
|
angles = RAD2DEG(Ang3::GetAnglesXYZ(Matrix33(ap.rot)));
|
|
scale = ap.scale;
|
|
}
|
|
else
|
|
{
|
|
pos = m_pos;
|
|
angles = m_angles;
|
|
scale = m_scale;
|
|
}
|
|
|
|
if (!IsEquivalent(pos,Vec3(0,0,0),0))
|
|
objNode->setAttr( "Pos",pos );
|
|
|
|
if (!IsEquivalent(angles,Vec3(0,0,0),0))
|
|
objNode->setAttr( "Angles",angles );
|
|
|
|
if (!IsEquivalent(scale,Vec3(1,1,1),0))
|
|
objNode->setAttr( "Scale",scale );
|
|
|
|
// Save variables.
|
|
CVarObject::Serialize( objNode,false );
|
|
|
|
return objNode;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBaseObject* CBaseObject::FindObject( REFGUID id ) const
|
|
{
|
|
return GetObjectManager()->FindObject( id );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::StoreUndo( const char *UndoDescription,bool minimal )
|
|
{
|
|
if (m_ie != 0 && m_ie->IsUndoRecording())
|
|
{
|
|
if (minimal)
|
|
m_ie->RecordUndo( new CUndoBaseObjectPos(this,UndoDescription) );
|
|
else
|
|
m_ie->RecordUndo( new CUndoBaseObject(this,UndoDescription) );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IsCreateGameObjects() const
|
|
{
|
|
return GetObjectManager()->IsCreateGameObjects();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetLayer( CObjectLayer *layer )
|
|
{
|
|
if (layer == m_layer)
|
|
return;
|
|
assert( layer != 0 );
|
|
StoreUndo( "Set Layer" );
|
|
m_layer = layer;
|
|
|
|
// Set layer for all childs.
|
|
for (int i = 0; i < m_childs.size(); i++)
|
|
{
|
|
m_childs[i]->SetLayer(layer);
|
|
}
|
|
|
|
// If object have target, target must also go into this layer.
|
|
if (m_lookat)
|
|
{
|
|
m_lookat->SetLayer(layer);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::HitTestRect( HitContext &hc )
|
|
{
|
|
BBox box;
|
|
// Retrieve world space bound box.
|
|
GetBoundBox( box );
|
|
|
|
// transform all 8 vertices into view space.
|
|
CPoint p[8] =
|
|
{
|
|
hc.view->WorldToView(Vec3d(box.min.x,box.min.y,box.min.z)),
|
|
hc.view->WorldToView(Vec3d(box.min.x,box.max.y,box.min.z)),
|
|
hc.view->WorldToView(Vec3d(box.max.x,box.min.y,box.min.z)),
|
|
hc.view->WorldToView(Vec3d(box.max.x,box.max.y,box.min.z)),
|
|
hc.view->WorldToView(Vec3d(box.min.x,box.min.y,box.max.z)),
|
|
hc.view->WorldToView(Vec3d(box.min.x,box.max.y,box.max.z)),
|
|
hc.view->WorldToView(Vec3d(box.max.x,box.min.y,box.max.z)),
|
|
hc.view->WorldToView(Vec3d(box.max.x,box.max.y,box.max.z))
|
|
};
|
|
|
|
CRect objrc,temprc;
|
|
|
|
objrc.left = 10000;
|
|
objrc.top = 10000;
|
|
objrc.right = -10000;
|
|
objrc.bottom = -10000;
|
|
// find new min/max values
|
|
for(int i=0; i<8; i++)
|
|
{
|
|
objrc.left = min(objrc.left,p[i].x);
|
|
objrc.right = max(objrc.right,p[i].x);
|
|
objrc.top = min(objrc.top,p[i].y);
|
|
objrc.bottom = max(objrc.bottom,p[i].y);
|
|
}
|
|
if (objrc.IsRectEmpty())
|
|
{
|
|
// Make objrc at least of size 1.
|
|
objrc.bottom += 1;
|
|
objrc.right += 1;
|
|
}
|
|
if (temprc.IntersectRect( objrc,hc.rect ))
|
|
{
|
|
hc.object = this;
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
/*
|
|
// Return false if point lies outside selection rectangle.
|
|
for(int i=0; i<8; i++)
|
|
{
|
|
if (p[i].x < hc.rect.left || p[i].x > hc.rect.right)
|
|
return false;
|
|
if (p[i].y < hc.rect.top || p[i].y > hc.rect.bottom)
|
|
return false;
|
|
}
|
|
hc.object = this;
|
|
return true;
|
|
*/
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CBaseObject::HitTestAxis( HitContext &hc )
|
|
{
|
|
return 0;
|
|
if (hc.distanceTollerance != 0)
|
|
return 0;
|
|
|
|
bool bWorldSpace = GetIEditor()->GetReferenceCoordSys() == COORDS_WORLD;
|
|
|
|
s_highlightAxis = 0;
|
|
|
|
Vec3 x(AXIS_SIZE,0,0);
|
|
Vec3 y(0,AXIS_SIZE,0);
|
|
Vec3 z(0,0,AXIS_SIZE);
|
|
|
|
Matrix44 tm = GetWorldTM();
|
|
Vec3 org = tm.TransformPointOLD( Vec3(0,0,0) );
|
|
if (!bWorldSpace)
|
|
{
|
|
tm.NoScale();
|
|
//CHANGED_BY_IVO
|
|
x = tm.TransformVectorOLD(x);
|
|
y = tm.TransformVectorOLD(y);
|
|
z = tm.TransformVectorOLD(z);
|
|
}
|
|
|
|
float camDist = GetDistance( GetIEditor()->GetViewerPos(), org );
|
|
x = x * camDist;
|
|
y = y * camDist;
|
|
z = z * camDist;
|
|
|
|
float hitDist = 0.01f * camDist;
|
|
|
|
Vec3 p = tm.TransformPointOLD( Vec3(0,0,0) );
|
|
Vec3 np1,np2,np3;
|
|
Vec3 rayTrg = hc.raySrc + hc.rayDir*10000.0f;
|
|
float d1 = RayToLineDistance( hc.raySrc,rayTrg,p,p+x,np1 );
|
|
float d2 = RayToLineDistance( hc.raySrc,rayTrg,p,p+y,np2 );
|
|
float d3 = RayToLineDistance( hc.raySrc,rayTrg,p,p+z,np3 );
|
|
|
|
if (d1 < hitDist && d1 < d2 && d1 < d3)
|
|
{
|
|
// X-Axis.
|
|
s_highlightAxis = AXIS_X;
|
|
hc.dist = GetDistance(hc.raySrc,np1);
|
|
}
|
|
else if (d2 < hitDist && d2 < d1 && d2 < d3)
|
|
{
|
|
// Y-Axis.
|
|
s_highlightAxis = AXIS_Y;
|
|
hc.dist = GetDistance(hc.raySrc,np2);
|
|
}
|
|
else if (d3 < hitDist && d3 < d1 && d3 < d2)
|
|
{
|
|
// Z-Axis.
|
|
s_highlightAxis = AXIS_Z;
|
|
hc.dist = GetDistance(hc.raySrc,np3);
|
|
}
|
|
|
|
return s_highlightAxis;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBaseObject* CBaseObject::GetChild( int i ) const
|
|
{
|
|
assert( i >= 0 && i < m_childs.size() );
|
|
return m_childs[i];
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IsChildOf( CBaseObject *node )
|
|
{
|
|
CBaseObject *p = m_parent;
|
|
while (p && p != node) {
|
|
p = p->m_parent;
|
|
}
|
|
if (p == node)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::AttachChild( CBaseObject* child,bool bKeepPos )
|
|
{
|
|
assert( child );
|
|
if (!child)
|
|
return;
|
|
|
|
child->StoreUndo( "Attach Child" );
|
|
|
|
Matrix44 childTM;
|
|
if (bKeepPos)
|
|
{
|
|
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.
|
|
|
|
//Timur[14/11/2003] Removed change of layer for child object.
|
|
// child->SetLayer( GetLayer() );
|
|
|
|
//Timur[9/11/2002]
|
|
/*
|
|
// All child object must have same sharing flag.
|
|
if (CheckFlags(OBJFLAG_SHARED))
|
|
child->SetFlags(OBJFLAG_SHARED);
|
|
else
|
|
child->ClearFlags(OBJFLAG_SHARED);
|
|
*/
|
|
|
|
if (bKeepPos)
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Keep old world space transformation.
|
|
child->SetWorldTM( childTM );
|
|
}
|
|
//else
|
|
child->InvalidateTM();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DetachAll( bool bKeepPos )
|
|
{
|
|
while (!m_childs.empty())
|
|
{
|
|
CBaseObject* child = *m_childs.begin();
|
|
child->DetachThis(bKeepPos);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::DetachThis( bool bKeepPos )
|
|
{
|
|
if (m_parent)
|
|
{
|
|
StoreUndo( "DetachThis" );
|
|
|
|
Matrix44 worldTM;
|
|
|
|
if (bKeepPos)
|
|
{
|
|
worldTM = GetWorldTM();
|
|
}
|
|
|
|
// 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 = 0;
|
|
parent->RemoveChild( this );
|
|
|
|
if (bKeepPos)
|
|
{
|
|
// Keep old world space transformation.
|
|
SetLocalTM( worldTM );
|
|
}
|
|
else
|
|
InvalidateTM();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
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 );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::ResolveParent( CBaseObject *parent )
|
|
{
|
|
if (parent != m_parent)
|
|
{
|
|
if (parent)
|
|
parent->AttachChild( this,false );
|
|
else
|
|
DetachThis(false);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::CalcLocalTM( Matrix44 &tm ) const
|
|
{
|
|
tm.SetIdentity();
|
|
|
|
if (m_lookat)
|
|
{
|
|
Vec3 pos = m_pos;
|
|
|
|
if (m_parent)
|
|
{
|
|
// Get our world position.
|
|
pos = m_parent->GetWorldTM().TransformPointOLD(pos);
|
|
}
|
|
|
|
// Calculate local TM matrix differently.
|
|
tm = MatrixFromVector( m_lookat->GetWorldPos() - pos,Vec3(0,0,-1),0 ); // Positive Z up.
|
|
// Translate matrix.
|
|
tm.SetTranslationOLD(pos);
|
|
}
|
|
else
|
|
{
|
|
tm=Matrix44::CreateRotationZYX(-m_angles*gf_DEGTORAD)*tm; //NOTE: angles in radians and negated
|
|
|
|
tm=Matrix33::CreateScale( Vec3d(m_scale.x,m_scale.y,m_scale.z) ) * tm;
|
|
|
|
// Translate matrix.
|
|
tm.SetTranslationOLD(m_pos);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
const Matrix44& CBaseObject::GetLocalTM() const
|
|
{
|
|
if (!m_bMatrixInWorldSpace && m_bMatrixValid)
|
|
{
|
|
return m_worldTM;
|
|
}
|
|
CalcLocalTM( m_worldTM );
|
|
m_bMatrixInWorldSpace = false;
|
|
m_bMatrixValid = true;
|
|
|
|
if (m_lookat)
|
|
m_bMatrixInWorldSpace = true;
|
|
|
|
return m_worldTM;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
const Matrix44& CBaseObject::GetWorldTM() const
|
|
{
|
|
if (!m_bMatrixValid)
|
|
{
|
|
m_worldTM = GetLocalTM();
|
|
}
|
|
if (!m_bMatrixInWorldSpace)
|
|
{
|
|
CBaseObject *parent = GetParent();
|
|
if (parent)
|
|
{
|
|
m_worldTM = m_worldTM * parent->GetWorldTM();
|
|
}
|
|
m_bMatrixInWorldSpace = true;
|
|
}
|
|
return m_worldTM;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::InvalidateTM()
|
|
{
|
|
bool bMatrixWasValid = m_bMatrixValid;
|
|
|
|
m_bMatrixInWorldSpace = false;
|
|
m_bMatrixValid = false;
|
|
|
|
// If matrix was valid, ivalidate all childs.
|
|
if (bMatrixWasValid)
|
|
{
|
|
if (m_lookatSource)
|
|
m_lookatSource->InvalidateTM();
|
|
|
|
// Invalidate matrices off all child objects.
|
|
for (int i = 0; i < m_childs.size(); i++)
|
|
{
|
|
if (m_childs[i] != 0 && m_childs[i]->m_bMatrixValid)
|
|
{
|
|
m_childs[i]->InvalidateTM();
|
|
}
|
|
}
|
|
NotifyListeners( ON_TRANSFORM );
|
|
}
|
|
|
|
// Notify parent that we were modified.
|
|
if (m_parent)
|
|
{
|
|
m_parent->OnChildModified();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetLocalTM( const Matrix44 &tm )
|
|
{
|
|
if (m_lookat)
|
|
{
|
|
SetPos( tm.GetTranslationOLD() );
|
|
// Calculate local TM matrix differently.
|
|
CalcLocalTM(m_worldTM);
|
|
|
|
m_bMatrixInWorldSpace = true;
|
|
m_bMatrixValid = true;
|
|
}
|
|
else
|
|
{
|
|
bool bMatrixWasValid = m_bMatrixValid;
|
|
m_bMatrixValid = false;
|
|
m_bMatrixInWorldSpace = false;
|
|
|
|
m_worldTM = tm;
|
|
AffineParts affineParts;
|
|
affineParts.SpectralDecompose(m_worldTM);
|
|
Vec3 angles = RAD2DEG(Ang3::GetAnglesXYZ(Matrix33(affineParts.rot)));
|
|
|
|
if (!IsVectorsEqual(m_pos,affineParts.pos))
|
|
SetPos( affineParts.pos );
|
|
|
|
if (!IsVectorsEqual(m_angles,angles))
|
|
SetAngles( angles );
|
|
|
|
if (!IsVectorsEqual(m_scale,affineParts.scale))
|
|
SetScale( affineParts.scale );
|
|
|
|
// Only now invalidate matrix.
|
|
m_bMatrixValid = bMatrixWasValid;
|
|
InvalidateTM();
|
|
}
|
|
|
|
/*
|
|
Matrix m(tm);
|
|
m.NoScale();
|
|
Quat q(m);
|
|
SetAngles( q.GetEulerAngles()*180.0f/PI );
|
|
m_worldTM = tm;
|
|
}
|
|
*/
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::SetWorldTM( const Matrix44 &tm )
|
|
{
|
|
if (GetParent())
|
|
{
|
|
Matrix44 invParentTM = GetParent()->GetWorldTM();
|
|
invParentTM.Invert44();
|
|
Matrix44 localTM = tm * invParentTM;
|
|
SetLocalTM( localTM );
|
|
}
|
|
else
|
|
{
|
|
SetLocalTM( tm );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::UpdateVisibility( bool visible )
|
|
{
|
|
/*
|
|
for (int i = 0; i < m_childs.size(); i++)
|
|
{
|
|
m_childs[i]->UpdateVisibility( visible );
|
|
}
|
|
*/
|
|
if (visible)
|
|
m_flags |= OBJFLAG_VISIBLE;
|
|
else
|
|
m_flags &= ~OBJFLAG_VISIBLE;
|
|
|
|
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( "Change LookAt" );
|
|
|
|
if (m_lookat)
|
|
{
|
|
// Unbind current lookat.
|
|
m_lookat->m_lookatSource = 0;
|
|
}
|
|
m_lookat = target;
|
|
if (m_lookat)
|
|
{
|
|
m_lookat->m_lookatSource = this;
|
|
}
|
|
|
|
InvalidateTM();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IsLookAtTarget() const
|
|
{
|
|
return m_lookatSource != 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::AddEventListener( const EventCallback &cb )
|
|
{
|
|
if (std::find(m_eventListeners.begin(),m_eventListeners.end(),cb) == m_eventListeners.end())
|
|
m_eventListeners.push_back(cb);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::RemoveEventListener( const EventCallback &cb )
|
|
{
|
|
std::list<EventCallback>::iterator it = std::find(m_eventListeners.begin(),m_eventListeners.end(),cb);
|
|
if (it != m_eventListeners.end())
|
|
{
|
|
m_nextListener = m_eventListeners.erase(it);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::NotifyListeners( EObjectListenerEvent event )
|
|
{
|
|
for (std::list<EventCallback>::iterator it = m_eventListeners.begin(); it != m_eventListeners.end(); it = m_nextListener)
|
|
{
|
|
m_nextListener = it;
|
|
m_nextListener++;
|
|
// Call listener callback.
|
|
(*it)( this,event );
|
|
}
|
|
m_nextListener = m_eventListeners.end();
|
|
/*
|
|
std::list<EventCallback>::iterator next;
|
|
for (std::list<EventCallback>::iterator it = m_eventListeners.begin(); it != m_eventListeners.end(); it = next)
|
|
{
|
|
next = it;
|
|
++next;
|
|
// Call listener callback.
|
|
(*it)( this,event );
|
|
}
|
|
*/
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::ConvertFromObject( CBaseObject *object )
|
|
{
|
|
SetLocalTM( object->GetLocalTM() );
|
|
SetName( object->GetName() );
|
|
SetLayer( object->GetLayer() );
|
|
SetColor( object->GetColor() );
|
|
m_flattenArea = object->m_flattenArea;
|
|
if (object->GetParent())
|
|
{
|
|
object->GetParent()->AttachChild( this );
|
|
}
|
|
SetMaterial( object->GetMaterial() );
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IsPotentiallyVisible() const
|
|
{
|
|
if (!m_layer->IsVisible())
|
|
return false;
|
|
if (CheckFlags(OBJFLAG_HIDDEN))
|
|
return false;
|
|
if (gSettings.objectHideMask & GetType())
|
|
return false;
|
|
|
|
CGroup *pGroup = GetGroup();
|
|
if (pGroup)
|
|
{
|
|
if (!pGroup->IsPotentiallyVisible())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Analyze errors for this object.
|
|
void CBaseObject::Validate( CErrorReport *report )
|
|
{
|
|
// Checks for invalid values in base object.
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Check if position is bad.
|
|
if (fabs(m_pos.x) > INVALID_POSITION_EPSILON ||
|
|
fabs(m_pos.y) > INVALID_POSITION_EPSILON ||
|
|
fabs(m_pos.z) > INVALID_POSITION_EPSILON)
|
|
{
|
|
// File Not found.
|
|
CErrorRecord err;
|
|
err.error.Format( "Object %s have invalid position (%f,%f,%f)",(const char*)GetName(),m_pos.x,m_pos.y,m_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.Format( "Object %s have invalid scale (%f,%f,%f)",(const char*)GetName(),m_scale.x,m_scale.y,m_scale.z );
|
|
err.pObject = this;
|
|
report->ReportError(err);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
Vec3 CBaseObject::GetWorldAngles() const
|
|
{
|
|
if (m_scale == Vec3(1,1,1))
|
|
{
|
|
Quat q = Quat( GetTransposed44(GetWorldTM()) );
|
|
Vec3 angles = RAD2DEG(Ang3::GetAnglesXYZ(Matrix33(q)));
|
|
return angles;
|
|
}
|
|
else
|
|
{
|
|
Matrix44 tm = GetWorldTM();
|
|
tm.NoScale();
|
|
Quat q = Quat(tm);
|
|
Vec3 angles = RAD2DEG(Ang3::GetAnglesXYZ(Matrix33(q)));
|
|
return angles;
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::PostClone( CBaseObject *pFromObject,CObjectCloneContext &ctx )
|
|
{
|
|
CBaseObject *pFromParent = pFromObject->GetParent();
|
|
if (pFromParent)
|
|
{
|
|
CBaseObject *pChildParent = ctx.FindClone( pFromParent );
|
|
if (pChildParent)
|
|
pChildParent->AttachChild( this,false );
|
|
else
|
|
pFromParent->AttachChild( this,false );
|
|
}
|
|
for (int i = 0; i < pFromObject->GetChildCount(); i++)
|
|
{
|
|
CBaseObject *pChildObject = pFromObject->GetChild(i);
|
|
CBaseObject *pClonedChild = GetObjectManager()->CloneObject( pChildObject );
|
|
ctx.AddClone( pChildObject,pClonedChild );
|
|
}
|
|
for (int i = 0; i < pFromObject->GetChildCount(); i++)
|
|
{
|
|
CBaseObject *pChildObject = pFromObject->GetChild(i);
|
|
CBaseObject *pClonedChild = ctx.FindClone( pChildObject );
|
|
if (pClonedChild)
|
|
{
|
|
pClonedChild->PostClone( pChildObject,ctx );
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::OnChangeGUID( REFGUID newGUID )
|
|
{
|
|
m_guid = newGUID;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBaseObject::GatherUsedResources( CUsedResources &resources )
|
|
{
|
|
if (GetVarBlock())
|
|
GetVarBlock()->GatherUsedResources( resources );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBaseObject::IsSimilarObject( CBaseObject *pObject )
|
|
{
|
|
if (pObject->GetClassDesc() == GetClassDesc() && pObject->GetRuntimeClass() == GetRuntimeClass())
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
} |