2332 lines
59 KiB
C++
2332 lines
59 KiB
C++
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Crytek Engine Source File.
|
|
// Copyright (C), Crytek Studios, 2001.
|
|
// -------------------------------------------------------------------------
|
|
// File name: StatObj.cpp
|
|
// Version: v1.00
|
|
// Created: 10/10/2001 by Timur.
|
|
// Compilers: Visual C++ 6.0
|
|
// Description: StaticObject implementation.
|
|
// -------------------------------------------------------------------------
|
|
// History:
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "StdAfx.h"
|
|
#include "Entity.h"
|
|
#include "EntityPanel.h"
|
|
#include "PanelTreeBrowser.h"
|
|
#include "PropertiesPanel.h"
|
|
#include "Viewport.h"
|
|
#include "Settings.h"
|
|
#include "LineGizmo.h"
|
|
#include "Settings.h"
|
|
|
|
#include <I3DEngine.h>
|
|
#include <IAgent.h>
|
|
#include <IMovieSystem.h>
|
|
#include <IEntitySystem.h>
|
|
#include <EntityDesc.h>
|
|
|
|
#include "EntityPrototype.h"
|
|
#include "Material\MaterialManager.h"
|
|
|
|
#include "BrushObject.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CBase implementation.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
IMPLEMENT_DYNCREATE(CEntity,CBaseObject)
|
|
IMPLEMENT_DYNCREATE(CSimpleEntity,CEntity)
|
|
|
|
int CEntity::m_rollupId = 0;
|
|
CEntityPanel* CEntity::m_panel = 0;
|
|
float CEntity::m_helperScale = 1;
|
|
|
|
namespace
|
|
{
|
|
int s_propertiesPanelIndex = 0;
|
|
CPropertiesPanel* s_propertiesPanel = 0;
|
|
|
|
int s_propertiesPanelIndex2 = 0;
|
|
CPropertiesPanel* s_propertiesPanel2 = 0;
|
|
|
|
// Prevent OnPropertyChange to be executed when loading many properties at one time.
|
|
static bool s_ignorePropertiesUpdate = false;
|
|
|
|
CPanelTreeBrowser* s_treePanel = NULL;
|
|
int s_treePanelId = 0;
|
|
|
|
struct AutoReleaseAll
|
|
{
|
|
AutoReleaseAll() {};
|
|
~AutoReleaseAll()
|
|
{
|
|
delete s_propertiesPanel;
|
|
delete s_propertiesPanel2;
|
|
delete s_treePanel;
|
|
}
|
|
} s_autoReleaseAll;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CEntity::CEntity()
|
|
{
|
|
m_entity = 0;
|
|
m_loadFailed = false;
|
|
|
|
m_script = 0;
|
|
|
|
m_visualObject = 0;
|
|
|
|
m_box.min.Set(0,0,0);
|
|
m_box.max.Set(0,0,0);
|
|
|
|
m_proximityRadius = 0;
|
|
m_innerRadius = 0;
|
|
m_outerRadius = 0;
|
|
|
|
m_animNode = 0;
|
|
m_displayBBox = true;
|
|
m_entityId = 0;
|
|
m_visible = true;
|
|
m_bCalcPhysics = true;
|
|
//m_staticEntity = false;
|
|
|
|
m_bEntityXfromValid = false;
|
|
ZeroStruct( m_materialGUID );
|
|
|
|
SetColor( RGB(255,255,0) );
|
|
|
|
// Init Variables.
|
|
mv_castShadows = false;
|
|
mv_selfShadowing = false;
|
|
mv_recvShadowMaps = true;
|
|
mv_castShadowMaps = true;
|
|
mv_castLightmap = false;
|
|
mv_recvLightmap = false;
|
|
mv_hiddenInGame = false;
|
|
mv_ratioLOD = 100;
|
|
mv_ratioViewDist = 100;
|
|
mv_UpdateVisLevel = eUT_Always;
|
|
mv_notOnLowSpec = false;
|
|
|
|
AddVariable( mv_castShadows,"CastShadows",_T("CastShadowVolume"),functor(*this,&CEntity::OnRenderFlagsChange) );
|
|
AddVariable( mv_selfShadowing,"SelfShadowing",functor(*this,&CEntity::OnRenderFlagsChange) );
|
|
AddVariable( mv_castShadowMaps,"CastShadowMaps",functor(*this,&CEntity::OnRenderFlagsChange) );
|
|
AddVariable( mv_recvShadowMaps,"RecvShadowMaps",functor(*this,&CEntity::OnRenderFlagsChange) );
|
|
AddVariable( mv_castLightmap,"PreCalcShadows",_T("CastLightmap"),functor(*this,&CEntity::OnRenderFlagsChange) );
|
|
AddVariable( mv_recvLightmap,"ReceiveLightmap",functor(*this,&CEntity::OnRenderFlagsChange) );
|
|
AddVariable( mv_ratioLOD,"LodRatio",functor(*this,&CEntity::OnRenderFlagsChange) );
|
|
AddVariable( mv_ratioViewDist,"ViewDistRatio",functor(*this,&CEntity::OnRenderFlagsChange) );
|
|
AddVariable( mv_notOnLowSpec,"SkipOnLowSpec" );
|
|
AddVariable( mv_hiddenInGame,"HiddenInGame" );
|
|
// AddVariable( mv_UpdateVisLevel, "UpdateVisLevel", functor(*this,&CEntity::OnRenderFlagsChange) ); // waiting comboboxes support
|
|
mv_ratioLOD.SetLimits( 0,255 );
|
|
mv_ratioViewDist.SetLimits( 0,255 );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::Done()
|
|
{
|
|
DeleteEntity();
|
|
UnloadScript();
|
|
|
|
if (m_trackGizmo)
|
|
{
|
|
RemoveGizmo( m_trackGizmo );
|
|
m_trackGizmo = 0;
|
|
}
|
|
if (m_animNode)
|
|
{
|
|
m_animNode->UnregisterCallback(this);
|
|
GetIEditor()->GetMovieSystem()->RemoveNode( m_animNode );
|
|
m_animNode = 0;
|
|
}
|
|
ReleaseEventTargets();
|
|
|
|
CBaseObject::Done();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CEntity::Init( IEditor *ie,CBaseObject *prev,const CString &file )
|
|
{
|
|
CBaseObject::Init( ie,prev,file );
|
|
|
|
if (IsCreateGameObjects())
|
|
{
|
|
m_animNode = CreateAnimNode();
|
|
if (m_animNode)
|
|
{
|
|
m_animNode->SetName( GetName() );
|
|
m_animNode->RegisterCallback(this);
|
|
}
|
|
|
|
m_trackGizmo = new CTrackGizmo;
|
|
m_trackGizmo->SetAnimNode( m_animNode );
|
|
AddGizmo( m_trackGizmo );
|
|
}
|
|
|
|
if (prev)
|
|
{
|
|
CEntity *pe = (CEntity*)prev;
|
|
m_pMaterial = pe->m_pMaterial;
|
|
m_materialGUID = pe->m_materialGUID;
|
|
// Clone Properties.
|
|
if (pe->m_properties)
|
|
{
|
|
m_properties = CloneProperties(pe->m_properties);
|
|
}
|
|
if (pe->m_properties2)
|
|
{
|
|
m_properties2 = CloneProperties(pe->m_properties2);
|
|
}
|
|
// When cloning entity, do not get properties from script.
|
|
LoadScript( pe->GetEntityClass(),false,false );
|
|
SpawnEntity();
|
|
UpdatePropertyPanel();
|
|
}
|
|
else if (!file.IsEmpty())
|
|
{
|
|
SetUniqName( file );
|
|
m_entityClass = file;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
IAnimNode* CEntity::CreateAnimNode()
|
|
{
|
|
//GUID guid = GetId();
|
|
//int nodeId = Crc32Gen::GetCRC32( (const char*)&guid,sizeof(guid),0xffffffff );
|
|
//int entityId = Crc32Gen::GetCRC32( GetId();
|
|
int nodeId = GetId().Data1;
|
|
return GetIEditor()->GetMovieSystem()->CreateNode( ANODE_ENTITY,nodeId );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CEntity::IsSameClass( CBaseObject *obj )
|
|
{
|
|
if (GetClassDesc() == obj->GetClassDesc())
|
|
{
|
|
CEntity *ent = (CEntity*)obj;
|
|
return GetScript() == ent->GetScript();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CEntity::ConvertFromObject( CBaseObject *object )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::SetLookAt( CBaseObject *target )
|
|
{
|
|
CBaseObject::SetLookAt(target);
|
|
if (m_animNode)
|
|
{
|
|
IAnimNode *trgAnimNode = 0;
|
|
if (target)
|
|
trgAnimNode = target->GetAnimNode();
|
|
m_animNode->SetTarget( trgAnimNode );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
IPhysicalEntity* CEntity::GetCollisionEntity() const
|
|
{
|
|
// Returns physical object of entity.
|
|
if (m_entity)
|
|
return m_entity->GetPhysics();
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::GetBoundBox( BBox &box )
|
|
{
|
|
box = m_box;
|
|
box.Transform( GetWorldTM() );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::GetLocalBounds( BBox &box )
|
|
{
|
|
box = m_box;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CEntity::HitTestEntity( HitContext &hc,bool &bHavePhysics )
|
|
{
|
|
bHavePhysics = true;
|
|
IPhysicalWorld *pPhysWorld = GetIEditor()->GetSystem()->GetIPhysicalWorld();
|
|
// Test 3D viewport.
|
|
IPhysicalEntity *physic = 0;
|
|
|
|
ICryCharInstance *pCharacter = m_entity->GetCharInterface()->GetCharacter(0);
|
|
if (pCharacter)
|
|
{
|
|
physic = pCharacter->GetCharacterPhysics();
|
|
if (physic)
|
|
{
|
|
int type = physic->GetType();
|
|
if (type == PE_NONE || type == PE_PARTICLE || type == PE_ROPE || type == PE_SOFT)
|
|
physic = 0;
|
|
else if (physic->GetStatus( &pe_status_nparts() ) == 0)
|
|
physic = 0;
|
|
}
|
|
if (physic)
|
|
{
|
|
ray_hit hit;
|
|
int col = pPhysWorld->RayTraceEntity( physic,hc.raySrc,hc.rayDir*10000.0f,&hit );
|
|
if (col <= 0)
|
|
return false;
|
|
hc.dist = hit.dist;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
physic = m_entity->GetPhysics();
|
|
if (physic)
|
|
{
|
|
int type = physic->GetType();
|
|
if (type == PE_NONE || type == PE_PARTICLE || type == PE_ROPE || type == PE_SOFT)
|
|
physic = 0;
|
|
else if (physic->GetStatus( &pe_status_nparts() ) == 0)
|
|
physic = 0;
|
|
}
|
|
// Now if box intersected try real geometry ray test.
|
|
if (physic)
|
|
{
|
|
ray_hit hit;
|
|
int col = pPhysWorld->RayTraceEntity( physic,hc.raySrc,hc.rayDir*10000.0f,&hit );
|
|
if (col <= 0)
|
|
return false;
|
|
hc.dist = hit.dist;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
bHavePhysics = false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CEntity::HitTest( HitContext &hc )
|
|
{
|
|
if (!hc.b2DViewport)
|
|
{
|
|
// Test 3D viewport.
|
|
if (m_entity)
|
|
{
|
|
bool bHavePhysics = false;
|
|
if (HitTestEntity( hc,bHavePhysics ))
|
|
return true;
|
|
if (bHavePhysics)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if (m_visualObject)
|
|
{
|
|
Matrix44 tm = GetWorldTM();
|
|
float sz = m_helperScale * gSettings.gizmo.helpersScale;
|
|
tm.ScaleMatRow( Vec3(sz,sz,sz) );
|
|
primitives::ray aray; aray.origin = hc.raySrc; aray.dir = hc.rayDir*10000.0f;
|
|
|
|
IGeomManager *pGeomMgr = GetIEditor()->GetSystem()->GetIPhysicalWorld()->GetGeomManager();
|
|
IGeometry *pRay = pGeomMgr->CreatePrimitive(primitives::ray::type, &aray);
|
|
geom_world_data gwd;
|
|
gwd.R.extract_from4x4T( tm, gwd.offset,gwd.scale);
|
|
geom_contact *pcontacts = 0;
|
|
int col = (m_visualObject->GetPhysGeom() && m_visualObject->GetPhysGeom()->pGeom) ? m_visualObject->GetPhysGeom()->pGeom->Intersect(pRay, &gwd,0, 0, pcontacts) : 0;
|
|
pGeomMgr->DestroyGeometry(pRay);
|
|
if (col > 0)
|
|
{
|
|
if (pcontacts)
|
|
hc.dist = pcontacts[col-1].t;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
else if (m_entity)
|
|
{
|
|
float dist = FLT_MAX;
|
|
bool bHaveHit = false;
|
|
CEntityObject entobj;
|
|
IPhysicalWorld *physWorld = GetIEditor()->GetSystem()->GetIPhysicalWorld();
|
|
int numobj = m_entity->GetNumObjects();
|
|
if (numobj == 0)
|
|
{
|
|
hc.weakHit = true;
|
|
return true;
|
|
}
|
|
vector origin = hc.raySrc;
|
|
vector dir = hc.rayDir*10000.0f;
|
|
Matrix tm;
|
|
GetMatrix( tm );
|
|
tm.Transpose();
|
|
for (int i = 0; i < numobj; i++)
|
|
{
|
|
m_entity->GetEntityObject(i,entobj);
|
|
if (entobj.object && entobj.object->GetPhysGeom())
|
|
{
|
|
ray_hit hit;
|
|
int col = physWorld->RayTraceGeometry( entobj.object->GetPhysGeom(),(float*)tm.m_values,origin,dir,&hit );
|
|
if (col > 0)
|
|
{
|
|
dist = __min(dist,hit.dist);
|
|
bHaveHit = true;
|
|
}
|
|
}
|
|
}
|
|
if (bHaveHit)
|
|
{
|
|
hc.dist = dist;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
*/
|
|
|
|
float hitEpsilon = hc.view->GetScreenScaleFactor( GetWorldPos() ) * 0.01f;
|
|
float hitDist;
|
|
|
|
float fScale = GetScale().x;
|
|
BBox boxScaled;
|
|
boxScaled.min = m_box.min*fScale;
|
|
boxScaled.max = m_box.max*fScale;
|
|
|
|
Matrix44 invertWTM = GetWorldTM();
|
|
invertWTM.Invert44();
|
|
//Vec3 xformedRaySrc = invertWTM*hc.raySrc;
|
|
//Vec3 xformedRayDir = GetNormalized( invertWTM*hc.rayDir );
|
|
|
|
|
|
Vec3 xformedRaySrc = invertWTM.TransformPointOLD(hc.raySrc);
|
|
Vec3 xformedRayDir = invertWTM.TransformVectorOLD(hc.rayDir);
|
|
xformedRayDir.Normalize();
|
|
|
|
/*
|
|
if (m_staticEntity)
|
|
{
|
|
// Collided with bbox of entity.
|
|
hc.dist = GetDistance(xformedRaySrc,pntContact);
|
|
return true;
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
Vec3 intPnt;
|
|
// Check intersection with bbox edges.
|
|
if (boxScaled.RayEdgeIntersection( xformedRaySrc,xformedRayDir,hitEpsilon,hitDist,intPnt ))
|
|
{
|
|
hc.dist = GetDistance(xformedRaySrc,intPnt);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CEntity::MouseCreateCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags )
|
|
{
|
|
if (event == eMouseMove || event == eMouseLDown)
|
|
{
|
|
Vec3 pos;
|
|
// Rise Entity above ground on Bounding box ammount.
|
|
if (GetIEditor()->GetAxisConstrains() != AXIS_TERRAIN)
|
|
{
|
|
pos = view->MapViewToCP(point);
|
|
}
|
|
else
|
|
{
|
|
// Snap to terrain.
|
|
bool hitTerrain;
|
|
pos = view->ViewToWorld( point,&hitTerrain );
|
|
if (hitTerrain)
|
|
{
|
|
pos.z = GetIEditor()->GetTerrainElevation(pos.x,pos.y);
|
|
pos.z = pos.z - m_box.min.z;
|
|
}
|
|
pos = view->SnapToGrid(pos);
|
|
}
|
|
SetPos( pos );
|
|
|
|
if (event == eMouseLDown)
|
|
return MOUSECREATE_OK;
|
|
return MOUSECREATE_CONTINUE;
|
|
}
|
|
return CBaseObject::MouseCreateCallback( view,event,point,flags );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CEntity::CreateGameObject()
|
|
{
|
|
if (!m_script)
|
|
{
|
|
if (!m_entityClass.IsEmpty())
|
|
LoadScript( m_entityClass,true );
|
|
}
|
|
if (!m_entity)
|
|
{
|
|
if (!m_entityClass.IsEmpty())
|
|
SpawnEntity();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CEntity::LoadScript( const CString &entityClass,bool bForceReload,bool bGetScriptProperties,XmlNodeRef xmlProperties,XmlNodeRef xmlProperties2 )
|
|
{
|
|
if (entityClass == m_entityClass && m_script != 0 && !bForceReload)
|
|
return true;
|
|
m_entityClass = entityClass;
|
|
m_loadFailed = false;
|
|
|
|
UnloadScript();
|
|
|
|
if (!IsCreateGameObjects())
|
|
return false;
|
|
|
|
//HACK
|
|
// Special case to ignore static entities.
|
|
if (entityClass == "StaticEntity")
|
|
{
|
|
OnLoadFailed();
|
|
return false;
|
|
}
|
|
|
|
m_script = CEntityScriptRegistry::Instance()->Find( m_entityClass );
|
|
if (!m_script)
|
|
{
|
|
OnLoadFailed();
|
|
return false;
|
|
}
|
|
|
|
// Load script if its not loaded yet.
|
|
if (!m_script->IsValid())
|
|
{
|
|
if (!m_script->Load())
|
|
{
|
|
OnLoadFailed();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Make visual editor object for this entity.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
if (!m_script->GetVisualObject().IsEmpty())
|
|
{
|
|
//m_entity->LoadObject( 0,m_script->GetVisualObject(),1 );
|
|
//m_entity->DrawObject( 0,ETY_DRAW_NORMAL );
|
|
m_visualObject = GetIEditor()->Get3DEngine()->MakeObject( m_script->GetVisualObject() );
|
|
if (m_visualObject)
|
|
m_visualObject->SetShaderTemplate( EFT_USER_FIRST+1,"s_ObjectColor",NULL );
|
|
}
|
|
|
|
bool bUpdateUI = false;
|
|
// Create Entity properties from Script properties..
|
|
if (bGetScriptProperties && m_prototype == NULL && m_script->GetProperties() != NULL)
|
|
{
|
|
bUpdateUI = true;
|
|
CVarBlockPtr oldProperties = m_properties;
|
|
m_properties = CloneProperties( m_script->GetProperties() );
|
|
|
|
if (xmlProperties)
|
|
{
|
|
s_ignorePropertiesUpdate = true;
|
|
m_properties->Serialize( xmlProperties,true );
|
|
s_ignorePropertiesUpdate = false;
|
|
}
|
|
else if (oldProperties)
|
|
{
|
|
// If we had propertied before copy thier values to new script.
|
|
s_ignorePropertiesUpdate = true;
|
|
m_properties->CopyValuesByName(oldProperties);
|
|
s_ignorePropertiesUpdate = false;
|
|
}
|
|
}
|
|
|
|
// Create Entity properties from Script properties..
|
|
if (bGetScriptProperties && m_script->GetProperties2() != NULL)
|
|
{
|
|
bUpdateUI = true;
|
|
CVarBlockPtr oldProperties = m_properties2;
|
|
m_properties2 = CloneProperties( m_script->GetProperties2() );
|
|
|
|
if (xmlProperties2)
|
|
{
|
|
s_ignorePropertiesUpdate = true;
|
|
m_properties2->Serialize( xmlProperties2,true );
|
|
s_ignorePropertiesUpdate = false;
|
|
}
|
|
else if (oldProperties)
|
|
{
|
|
// If we had propertied before copy thier values to new script.
|
|
s_ignorePropertiesUpdate = true;
|
|
m_properties2->CopyValuesByName(oldProperties);
|
|
s_ignorePropertiesUpdate = false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::SpawnEntity()
|
|
{
|
|
if (!m_script)
|
|
return;
|
|
|
|
// Do not spawn second time.
|
|
if (m_entity)
|
|
return;
|
|
|
|
m_loadFailed = false;
|
|
|
|
EntityClassId ClassId = m_script->GetClsId();
|
|
|
|
//CLogFile::FormatLine( "Loading Entity %s (%s)",(const char*)entityClass,(const char*)GetName() );
|
|
|
|
if (m_entityId != 0)
|
|
{
|
|
if (GetIEditor()->GetSystem()->GetIEntitySystem()->IsIDUsed( m_entityId ))
|
|
m_entityId = 0;
|
|
}
|
|
|
|
CEntityDesc ed;
|
|
ed.ClassId = ClassId;
|
|
ed.name = (const char*)GetName();
|
|
ed.pos = GetWorldPos();
|
|
ed.angles = GetAngles();
|
|
ed.id = m_entityId;
|
|
if (ed.id == 0)
|
|
ed.id = -1; // Tells to Entity system to genrate new static id.
|
|
|
|
IEntitySystem *pEntitySystem = GetIEditor()->GetSystem()->GetIEntitySystem();
|
|
// Spawn Entity but not initialize it.
|
|
m_entity = pEntitySystem->SpawnEntity( ed,false );
|
|
if (m_entity)
|
|
{
|
|
m_entityId = m_entity->GetId();
|
|
|
|
// Bind to parent.
|
|
BindToParent();
|
|
BindIEntityChilds();
|
|
|
|
m_entity->Hide( !m_visible );
|
|
|
|
//m_script->SetCurrentProperties( this );
|
|
m_script->SetEventsTable( this );
|
|
|
|
// Mark this entity non destroyable.
|
|
m_entity->SetDestroyable(false);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// If have material, assign it to the entity.
|
|
if (m_pMaterial)
|
|
m_pMaterial->AssignToEntity( m_entity );
|
|
|
|
// Force transformation on entity.
|
|
XFormGameEntity();
|
|
|
|
if (m_properties != NULL)
|
|
{
|
|
m_script->SetProperties( m_entity,m_properties,false );
|
|
}
|
|
if (m_properties2 != NULL)
|
|
{
|
|
m_script->SetProperties2( m_entity,m_properties2,false );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Now initialize entity.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
if (!pEntitySystem->InitEntity( m_entity,ed ))
|
|
{
|
|
m_entity = 0;
|
|
OnLoadFailed();
|
|
return;
|
|
}
|
|
|
|
// Update render flags of entity (Must be after InitEntity).
|
|
OnRenderFlagsChange(0);
|
|
|
|
if (!m_physicsState.IsEmpty())
|
|
{
|
|
m_entity->SetPhysicsState( m_physicsState );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Check if needs to display bbox for this entity.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
m_bCalcPhysics = true;
|
|
if (m_entity->GetPhysics() != 0)
|
|
{
|
|
m_displayBBox = false;
|
|
if (m_entity->GetPhysics()->GetType() == PE_SOFT)
|
|
{
|
|
m_bCalcPhysics = false;
|
|
//! Ignore entity being updated from physics.
|
|
m_entity->SetFlags(ETY_FLAG_IGNORE_PHYSICS_UPDATE);
|
|
}
|
|
}
|
|
else
|
|
m_displayBBox = true;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Calculate entity bounding box.
|
|
CalcBBox();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Assign entity pointer to animation node.
|
|
if (m_animNode)
|
|
m_animNode->SetEntity(m_entity);
|
|
}
|
|
else
|
|
{
|
|
OnLoadFailed();
|
|
}
|
|
|
|
//?
|
|
UpdatePropertyPanel();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::DeleteEntity()
|
|
{
|
|
if (m_entity)
|
|
{
|
|
UnbindIEntity();
|
|
m_entity->SetDestroyable(true);
|
|
GetIEditor()->GetSystem()->GetIEntitySystem()->RemoveEntity( m_entity->GetId(),true );
|
|
}
|
|
m_entity = 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::UnloadScript()
|
|
{
|
|
if (m_entity)
|
|
{
|
|
DeleteEntity();
|
|
}
|
|
if (m_visualObject)
|
|
GetIEditor()->Get3DEngine()->ReleaseObject(m_visualObject);
|
|
m_visualObject = 0;
|
|
m_script = 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::XFormGameEntity()
|
|
{
|
|
if (!m_entity)
|
|
return;
|
|
|
|
m_entity->SetPos( GetPos(),false );
|
|
m_entity->SetAngles( GetAngles() );
|
|
m_entity->SetScale( GetScale().x );
|
|
// Make sure this entity is succesfully registered in correct sectors after loading.
|
|
m_entity->ForceRegisterInSectors();
|
|
|
|
/*
|
|
const Matrix &tm = GetWorldTM();
|
|
//m_entity->SetPos( Vec3(tm[3][0],tm[3][1],tm[3][2]) );
|
|
Quat rotate(tm);
|
|
//m_entity->SetAngles( rotate.GetEulerAngles()*180.0f/PI );
|
|
m_bEntityXfromValid = true;
|
|
*/
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::CalcBBox()
|
|
{
|
|
if (m_entity)
|
|
{
|
|
// Get Local bounding box of entity.
|
|
m_entity->GetLocalBBox( m_box.min,m_box.max );
|
|
|
|
if (m_box.IsEmpty())
|
|
m_displayBBox = true;
|
|
|
|
if (m_visualObject)
|
|
{
|
|
Vec3 minp = m_visualObject->GetBoxMin()*m_helperScale*gSettings.gizmo.helpersScale;
|
|
Vec3 maxp = m_visualObject->GetBoxMax()*m_helperScale*gSettings.gizmo.helpersScale;
|
|
m_box.Add( minp );
|
|
m_box.Add( maxp );
|
|
}
|
|
float minSize = 0.1f;
|
|
if (fabs(m_box.max.x-m_box.min.x)+fabs(m_box.max.y-m_box.min.y)+fabs(m_box.max.z-m_box.min.z) < minSize)
|
|
{
|
|
m_box.min = -Vec3( minSize,minSize,minSize );
|
|
m_box.max = Vec3( minSize,minSize,minSize );
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::SetName( const CString &name )
|
|
{
|
|
if (name == GetName())
|
|
return;
|
|
|
|
CBaseObject::SetName( name );
|
|
if (m_entity)
|
|
m_entity->SetName( (const char*)GetName() );
|
|
if (m_animNode)
|
|
{
|
|
m_animNode->SetName( name );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::SetPos( const Vec3d &pos )
|
|
{
|
|
if (IsVectorsEqual(GetPos(),pos))
|
|
return;
|
|
|
|
CBaseObject::SetPos( pos );
|
|
|
|
if (m_entity)
|
|
{
|
|
//m_entity->SetPos( GetWorldPos() );
|
|
m_entity->SetPos( GetPos(),false );
|
|
m_entity->ForceRegisterInSectors();
|
|
}
|
|
if (m_animNode)
|
|
{
|
|
m_animNode->SetPos( GetIEditor()->GetAnimation()->GetTime(),pos );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::SetAngles( const Vec3d &angles )
|
|
{
|
|
if (IsVectorsEqual(GetAngles(),angles))
|
|
return;
|
|
|
|
CBaseObject::SetAngles( angles );
|
|
|
|
if (m_entity)
|
|
{
|
|
m_entity->SetAngles( GetAngles() );
|
|
}
|
|
|
|
if (m_animNode)
|
|
{
|
|
m_rotate.SetRotationXYZ( angles*gf_PI/180.0f );
|
|
m_animNode->SetRotate( GetIEditor()->GetAnimation()->GetTime(),m_rotate );
|
|
}
|
|
}
|
|
|
|
void CEntity::SetScale( const Vec3d &scale )
|
|
{
|
|
if (IsVectorsEqual(GetScale(),scale))
|
|
return;
|
|
|
|
CBaseObject::SetScale( scale );
|
|
|
|
if (m_entity)
|
|
{
|
|
m_entity->SetScale( GetScale().x );
|
|
CalcBBox();
|
|
}
|
|
if (m_animNode)
|
|
{
|
|
m_animNode->SetScale( GetIEditor()->GetAnimation()->GetTime(),scale );
|
|
}
|
|
/*
|
|
if (m_entity)
|
|
m_entity->SetScale(scale);
|
|
*/
|
|
// CBaseObject::SetScale( scale );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::BeginEditParams( IEditor *ie,int flags )
|
|
{
|
|
CBaseObject::BeginEditParams( ie,flags );
|
|
|
|
if (m_properties2 != NULL)
|
|
{
|
|
if (!s_propertiesPanel2)
|
|
s_propertiesPanel2 = new CPropertiesPanel( AfxGetMainWnd() );
|
|
else
|
|
s_propertiesPanel2->DeleteVars();
|
|
s_propertiesPanel2->AddVars( m_properties2 );
|
|
if (!s_propertiesPanelIndex2)
|
|
s_propertiesPanelIndex2 = ie->AddRollUpPage( ROLLUP_OBJECTS,CString(GetTypeName()) + " Properties2",s_propertiesPanel2 );
|
|
}
|
|
|
|
if (!m_prototype)
|
|
{
|
|
if (m_properties != NULL)
|
|
{
|
|
if (!s_propertiesPanel)
|
|
s_propertiesPanel = new CPropertiesPanel( AfxGetMainWnd() );
|
|
else
|
|
s_propertiesPanel->DeleteVars();
|
|
s_propertiesPanel->AddVars( m_properties );
|
|
if (!s_propertiesPanelIndex)
|
|
s_propertiesPanelIndex = ie->AddRollUpPage( ROLLUP_OBJECTS,CString(GetTypeName()) + " Properties",s_propertiesPanel );
|
|
}
|
|
}
|
|
|
|
if (!m_panel && m_entity)
|
|
{
|
|
m_panel = new CEntityPanel(AfxGetMainWnd());
|
|
m_panel->Create( CEntityPanel::IDD,AfxGetMainWnd() );
|
|
m_rollupId = GetIEditor()->AddRollUpPage( ROLLUP_OBJECTS,m_entityClass,m_panel );
|
|
}
|
|
|
|
if (m_panel)
|
|
m_panel->SetEntity( this );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::EndEditParams( IEditor *ie )
|
|
{
|
|
if (m_rollupId != 0)
|
|
GetIEditor()->RemoveRollUpPage( ROLLUP_OBJECTS,m_rollupId );
|
|
m_rollupId = 0;
|
|
m_panel = 0;
|
|
|
|
if (s_propertiesPanelIndex != 0)
|
|
GetIEditor()->RemoveRollUpPage( ROLLUP_OBJECTS,s_propertiesPanelIndex );
|
|
s_propertiesPanelIndex = 0;
|
|
s_propertiesPanel = 0;
|
|
|
|
if (s_propertiesPanelIndex2 != 0)
|
|
GetIEditor()->RemoveRollUpPage( ROLLUP_OBJECTS,s_propertiesPanelIndex2 );
|
|
s_propertiesPanelIndex2 = 0;
|
|
s_propertiesPanel2 = 0;
|
|
|
|
CBaseObject::EndEditParams( GetIEditor() );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::UpdatePropertyPanel()
|
|
{
|
|
// If user interface opened reload properties.
|
|
if (s_propertiesPanel && m_properties != 0)
|
|
{
|
|
s_propertiesPanel->DeleteVars();
|
|
s_propertiesPanel->AddVars( m_properties );
|
|
}
|
|
|
|
// If user interface opened reload properties.
|
|
if (s_propertiesPanel2 && m_properties2 != 0)
|
|
{
|
|
s_propertiesPanel2->DeleteVars();
|
|
s_propertiesPanel2->AddVars( m_properties2 );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::BeginEditMultiSelParams( bool bAllOfSameType )
|
|
{
|
|
CBaseObject::BeginEditMultiSelParams(bAllOfSameType);
|
|
|
|
if (!bAllOfSameType)
|
|
return;
|
|
|
|
if (m_properties2 != NULL)
|
|
{
|
|
if (!s_propertiesPanel2)
|
|
s_propertiesPanel2 = new CPropertiesPanel( AfxGetMainWnd() );
|
|
else
|
|
s_propertiesPanel2->DeleteVars();
|
|
|
|
// Add all selected objects.
|
|
CSelectionGroup *grp = GetIEditor()->GetSelection();
|
|
for (int i = 0; i < grp->GetCount(); i++)
|
|
{
|
|
CEntity *ent = (CEntity*)grp->GetObject(i);
|
|
if (ent->m_properties2)
|
|
s_propertiesPanel2->AddVars( ent->m_properties2 );
|
|
}
|
|
if (!s_propertiesPanelIndex2)
|
|
s_propertiesPanelIndex2 = GetIEditor()->AddRollUpPage( ROLLUP_OBJECTS,CString(GetTypeName()) + " Properties",s_propertiesPanel2 );
|
|
}
|
|
|
|
if (m_properties != NULL && m_prototype == NULL)
|
|
{
|
|
if (!s_propertiesPanel)
|
|
s_propertiesPanel = new CPropertiesPanel( AfxGetMainWnd() );
|
|
else
|
|
s_propertiesPanel->DeleteVars();
|
|
|
|
// Add all selected objects.
|
|
CSelectionGroup *grp = GetIEditor()->GetSelection();
|
|
for (int i = 0; i < grp->GetCount(); i++)
|
|
{
|
|
CEntity *ent = (CEntity*)grp->GetObject(i);
|
|
CVarBlock *vb = ent->m_properties;
|
|
if (vb)
|
|
s_propertiesPanel->AddVars( vb );
|
|
}
|
|
if (!s_propertiesPanelIndex)
|
|
s_propertiesPanelIndex = GetIEditor()->AddRollUpPage( ROLLUP_OBJECTS,CString(GetTypeName()) + " Properties",s_propertiesPanel );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::EndEditMultiSelParams()
|
|
{
|
|
if (s_propertiesPanelIndex != 0)
|
|
GetIEditor()->RemoveRollUpPage( ROLLUP_OBJECTS,s_propertiesPanelIndex );
|
|
s_propertiesPanelIndex = 0;
|
|
s_propertiesPanel = 0;
|
|
|
|
if (s_propertiesPanelIndex2 != 0)
|
|
GetIEditor()->RemoveRollUpPage( ROLLUP_OBJECTS,s_propertiesPanelIndex2 );
|
|
s_propertiesPanelIndex2 = 0;
|
|
s_propertiesPanel2 = 0;
|
|
|
|
CBaseObject::EndEditMultiSelParams();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::SetSelected( bool bSelect )
|
|
{
|
|
CBaseObject::SetSelected( bSelect );
|
|
|
|
if (m_entity)
|
|
{
|
|
int flags = m_entity->GetRndFlags();
|
|
if (bSelect)
|
|
flags |= ERF_SELECTED;
|
|
else
|
|
flags &= ~ERF_SELECTED;
|
|
m_entity->SetRndFlags( flags );
|
|
}
|
|
if (m_animNode)
|
|
m_animNode->SetFlags( (bSelect) ? (m_animNode->GetFlags()|ANODE_FLAG_SELECTED) : (m_animNode->GetFlags()&(~ANODE_FLAG_SELECTED)) );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::OnPropertyChange( IVariable *var )
|
|
{
|
|
if (s_ignorePropertiesUpdate)
|
|
return;
|
|
|
|
if (m_script != 0 && m_entity != 0)
|
|
{
|
|
if (m_properties)
|
|
m_script->SetProperties( m_entity,m_properties,true );
|
|
if (m_properties2)
|
|
m_script->SetProperties2( m_entity,m_properties2,true );
|
|
// After change of properties bounding box of entity may change.
|
|
CalcBBox();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::Display( DisplayContext &dc )
|
|
{
|
|
if (!m_entity)
|
|
return;
|
|
|
|
//XFormEntity();
|
|
|
|
Matrix44 wtm = GetWorldTM();
|
|
|
|
COLORREF col = GetColor();
|
|
if (IsFrozen())
|
|
col = dc.GetFreezeColor();
|
|
|
|
//Vec3 scale = GetScale();
|
|
|
|
dc.PushMatrix( wtm );
|
|
|
|
if (IsSelected())
|
|
{
|
|
dc.SetSelectedColor( 0.5f );
|
|
//dc.renderer->Draw3dBBox( GetPos()-Vec3(m_radius,m_radius,m_radius),GetPos()+Vec3(m_radius,m_radius,m_radius));
|
|
dc.DrawWireBox( m_box.min,m_box.max );
|
|
}
|
|
else
|
|
{
|
|
if (m_displayBBox || (dc.flags & DISPLAY_2D))
|
|
{
|
|
dc.SetColor( col,0.3f );
|
|
dc.DrawWireBox( m_box.min,m_box.max );
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Only display solid BBox if visual object is associated with the entity.
|
|
if (m_displayBBox && m_visualObject)
|
|
{
|
|
|
|
dc.renderer->EnableDepthWrites(false);
|
|
dc.SetColor(col,0.05f);
|
|
dc.DrawSolidBox( m_box.min,m_box.max );
|
|
dc.renderer->EnableDepthWrites(true);
|
|
}
|
|
*/
|
|
|
|
/*
|
|
if (m_entity && m_proximityRadius >= 0)
|
|
{
|
|
float r = m_proximityRadius;
|
|
dc.SetColor( 1,1,0,1 );
|
|
dc.DrawWireBox( GetPos()-Vec3(r,r,r),GetPos()+Vec3(r,r,r) );
|
|
}
|
|
*/
|
|
|
|
// Draw radiuses if present and object selected.
|
|
if (gSettings.viewports.bAlwaysShowRadiuses || IsSelected())
|
|
{
|
|
float fScale = GetScale().x; // Ignore matrix scale.
|
|
if (fScale == 0) fScale = 1;
|
|
if (m_innerRadius > 0)
|
|
{
|
|
dc.SetColor( 0,1,0,0.3f );
|
|
dc.DrawWireSphere( Vec3(0,0,0),m_innerRadius/fScale );
|
|
}
|
|
if (m_outerRadius > 0)
|
|
{
|
|
dc.SetColor( 1,1,0,0.8f );
|
|
dc.DrawWireSphere( Vec3(0,0,0),m_outerRadius/fScale );
|
|
}
|
|
}
|
|
|
|
dc.PopMatrix();
|
|
|
|
// Entities themself are rendered by 3DEngine.
|
|
|
|
if (m_visualObject)
|
|
{
|
|
/*
|
|
float fScale = dc.view->GetScreenScaleFactor(wtm.GetTranslation()) * 0.04f;
|
|
wtm[0][0] *= fScale; wtm[0][1] *= fScale; wtm[0][2] *= fScale;
|
|
wtm[1][0] *= fScale; wtm[1][1] *= fScale; wtm[1][2] *= fScale;
|
|
wtm[2][0] *= fScale; wtm[2][1] *= fScale; wtm[2][2] *= fScale;
|
|
*/
|
|
Matrix44 tm(wtm);
|
|
float sz = m_helperScale*gSettings.gizmo.helpersScale;
|
|
tm.ScaleMatRow( Vec3(sz,sz,sz) );
|
|
|
|
SRendParams rp;
|
|
if (IsSelected())
|
|
rp.vColor = Rgb2Vec(dc.GetSelectedColor());
|
|
else
|
|
rp.vColor = Rgb2Vec(col);
|
|
rp.dwFObjFlags |= FOB_TRANS_MASK;
|
|
rp.fAlpha = 1;
|
|
rp.nDLightMask = GetIEditor()->Get3DEngine()->GetLightMaskFromPosition(wtm.GetTranslationOLD(),1.f) & 0xFFFF;
|
|
rp.pMatrix = &tm;
|
|
m_visualObject->Render( rp,Vec3(zero),0 );
|
|
}
|
|
|
|
if (IsSelected())
|
|
{
|
|
if (m_entity)
|
|
{
|
|
IAIObject *pAIObj = m_entity->GetAI();
|
|
if (pAIObj)
|
|
DrawAIInfo( dc,pAIObj );
|
|
}
|
|
}
|
|
|
|
/*
|
|
if ((dc.flags & DISPLAY_LINKS) && !m_eventTargets.empty())
|
|
{
|
|
DrawTargets(dc);
|
|
}
|
|
*/
|
|
|
|
DrawDefault(dc,col);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::DrawAIInfo( DisplayContext &dc,IAIObject *aiObj )
|
|
{
|
|
assert( aiObj );
|
|
IPuppet *pPuppet;
|
|
if (aiObj->CanBeConvertedTo( AIOBJECT_PUPPET,(void**)&pPuppet))
|
|
{
|
|
AgentParameters &ap = pPuppet->GetPuppetParameters();
|
|
|
|
// Draw ranges.
|
|
bool bTerrainCircle = false;
|
|
Vec3 wp = GetWorldPos();
|
|
float z = GetIEditor()->GetTerrainElevation( wp.x,wp.y );
|
|
if (fabs(wp.z-z) < 5)
|
|
bTerrainCircle = true;
|
|
|
|
dc.SetColor( RGB(0,255,0) );
|
|
if (bTerrainCircle)
|
|
dc.DrawTerrainCircle( wp,ap.m_fSoundRange,0.2f );
|
|
else
|
|
dc.DrawCircle( wp,ap.m_fSoundRange );
|
|
|
|
dc.SetColor( RGB(255,255,0) );
|
|
if (bTerrainCircle)
|
|
dc.DrawTerrainCircle( wp,ap.m_fCommRange,0.2f );
|
|
else
|
|
dc.DrawCircle( wp,ap.m_fCommRange );
|
|
|
|
dc.SetColor( RGB(255,0,0) );
|
|
if (bTerrainCircle)
|
|
dc.DrawTerrainCircle( wp,ap.m_fSightRange,0.2f );
|
|
else
|
|
dc.DrawCircle( wp,ap.m_fSightRange );
|
|
|
|
dc.SetColor( RGB(255/2,0,0) );
|
|
if (bTerrainCircle)
|
|
dc.DrawTerrainCircle( wp,ap.m_fSightRange/2,0.2f );
|
|
else
|
|
dc.DrawCircle( wp,ap.m_fSightRange/2 );
|
|
|
|
dc.SetColor( RGB(0,0,255) );
|
|
if (bTerrainCircle)
|
|
dc.DrawTerrainCircle( wp,ap.m_fAttackRange,0.2f );
|
|
else
|
|
dc.DrawCircle( wp,ap.m_fAttackRange );
|
|
|
|
//dc.SetColor( 0,1,0,0.3f );
|
|
//dc.DrawWireSphere( Vec3(0,0,0),m_innerRadius );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::DrawTargets( DisplayContext &dc )
|
|
{
|
|
/*
|
|
BBox box;
|
|
for (int i = 0; i < m_eventTargets.size(); i++)
|
|
{
|
|
CBaseObject *target = m_eventTargets[i].target;
|
|
if (!target)
|
|
return;
|
|
|
|
dc.SetColor( 0.8f,0.4f,0.4f,1 );
|
|
GetBoundBox( box );
|
|
Vec3 p1 = 0.5f*Vec3(box.max+box.min);
|
|
target->GetBoundBox( box );
|
|
Vec3 p2 = 0.5f*Vec3(box.max+box.min);
|
|
|
|
dc.DrawLine( p1,p2 );
|
|
|
|
Vec3 p3 = 0.5f*(p2+p1);
|
|
|
|
if (!(dc.flags & DISPLAY_HIDENAMES))
|
|
{
|
|
float col[4] = { 0.8f,0.4f,0.4f,1 };
|
|
dc.renderer->DrawLabelEx( p3+Vec3(0,0,0.3f),1.2f,col,true,true,m_eventTargets[i].event );
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::Serialize( CObjectArchive &ar )
|
|
{
|
|
CBaseObject::Serialize( ar );
|
|
XmlNodeRef xmlNode = ar.node;
|
|
if (ar.bLoading)
|
|
{
|
|
m_entityId = 0;
|
|
|
|
m_physicsState = "";
|
|
// Load
|
|
CString entityClass = m_entityClass;
|
|
m_loadFailed = false;
|
|
|
|
if (!m_prototype)
|
|
xmlNode->getAttr( "EntityClass",entityClass );
|
|
xmlNode->getAttr( "EntityId",m_entityId );
|
|
xmlNode->getAttr( "PhysicsState",m_physicsState );
|
|
|
|
ZeroStruct(m_materialGUID);
|
|
if (xmlNode->getAttr( "MaterialGUID",m_materialGUID ))
|
|
{
|
|
m_pMaterial = (CMaterial*)GetIEditor()->GetMaterialManager()->FindItem( m_materialGUID );
|
|
if (!m_pMaterial)
|
|
{
|
|
CErrorRecord err;
|
|
err.error.Format( "Material %s for Entity %s not found,",GuidUtil::ToString(m_materialGUID),(const char*)GetName() );
|
|
err.pObject = this;
|
|
err.severity = CErrorRecord::ESEVERITY_WARNING;
|
|
ar.ReportError(err);
|
|
//Warning( "Material %s for Entity %s not found,",GuidUtil::ToString(m_materialGUID),(const char*)GetName() );
|
|
}
|
|
else
|
|
{
|
|
if (m_pMaterial->GetParent())
|
|
SetMaterial( m_pMaterial->GetParent() );
|
|
m_pMaterial->SetUsed();
|
|
}
|
|
UpdateMaterialInfo();
|
|
}
|
|
else
|
|
m_pMaterial = 0;
|
|
|
|
// Load Event Targets.
|
|
ReleaseEventTargets();
|
|
XmlNodeRef eventTargets = xmlNode->findChild( "EventTargets" );
|
|
if (eventTargets)
|
|
{
|
|
for (int i = 0; i < eventTargets->getChildCount(); i++)
|
|
{
|
|
XmlNodeRef eventTarget = eventTargets->getChild(i);
|
|
CEntityEventTarget et;
|
|
et.target = 0;
|
|
GUID targetId = GUID_NULL;
|
|
eventTarget->getAttr( "TargetId",targetId );
|
|
eventTarget->getAttr( "Event",et.event );
|
|
eventTarget->getAttr( "SourceEvent",et.sourceEvent );
|
|
m_eventTargets.push_back( et );
|
|
if (targetId != GUID_NULL)
|
|
ar.SetResolveCallback( this,targetId,functor(*this,&CEntity::ResolveEventTarget),i );
|
|
}
|
|
}
|
|
|
|
XmlNodeRef propsNode;
|
|
XmlNodeRef props2Node = xmlNode->findChild("Properties2");
|
|
if (!m_prototype)
|
|
{
|
|
propsNode = xmlNode->findChild("Properties");
|
|
}
|
|
|
|
bool bLoaded = LoadScript( entityClass,!ar.bUndo,true,propsNode,props2Node );
|
|
if (ar.bUndo)
|
|
{
|
|
SpawnEntity();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Saving.
|
|
if (!m_entityClass.IsEmpty() && m_prototype == NULL)
|
|
xmlNode->setAttr( "EntityClass",m_entityClass );
|
|
|
|
if (m_entityId != 0)
|
|
xmlNode->setAttr( "EntityId",m_entityId );
|
|
|
|
if (!GuidUtil::IsEmpty(m_materialGUID))
|
|
{
|
|
xmlNode->setAttr( "MaterialGUID",m_materialGUID );
|
|
}
|
|
|
|
if (!m_physicsState.IsEmpty())
|
|
xmlNode->setAttr( "PhysicsState",m_physicsState );
|
|
|
|
if (!m_prototype)
|
|
{
|
|
//! Save properties.
|
|
if (m_properties)
|
|
{
|
|
XmlNodeRef propsNode = xmlNode->newChild("Properties");
|
|
m_properties->Serialize( propsNode,ar.bLoading );
|
|
}
|
|
}
|
|
|
|
//! Save properties.
|
|
if (m_properties2)
|
|
{
|
|
XmlNodeRef propsNode = xmlNode->newChild("Properties2");
|
|
m_properties2->Serialize( propsNode,ar.bLoading );
|
|
}
|
|
|
|
// Save Event Targets.
|
|
if (!m_eventTargets.empty())
|
|
{
|
|
XmlNodeRef eventTargets = xmlNode->newChild( "EventTargets" );
|
|
for (int i = 0; i < m_eventTargets.size(); i++)
|
|
{
|
|
CEntityEventTarget &et = m_eventTargets[i];
|
|
GUID targetId = GUID_NULL;
|
|
if (et.target != 0)
|
|
targetId = et.target->GetId();
|
|
|
|
XmlNodeRef eventTarget = eventTargets->newChild( "EventTarget" );
|
|
eventTarget->setAttr( "TargetId",targetId );
|
|
eventTarget->setAttr( "Event",et.event );
|
|
eventTarget->setAttr( "SourceEvent",et.sourceEvent );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
XmlNodeRef CEntity::Export( const CString &levelPath,XmlNodeRef &xmlNode )
|
|
{
|
|
if (m_loadFailed)
|
|
return 0;
|
|
|
|
//@HACK
|
|
//[Timur] This is the worst ever Hack! unfortunatly Have to be here because of our fabolus game programmers, sigh.
|
|
if (m_entityClass == "DynamicLight")
|
|
{
|
|
// If dynamic light and casts lightmap do not export this.
|
|
if (mv_castLightmap)
|
|
return 0;
|
|
}
|
|
|
|
// Export entities to entities.ini
|
|
XmlNodeRef objNode = CBaseObject::Export( levelPath,xmlNode );
|
|
|
|
objNode->setTag( "Entity" );
|
|
objNode->setAttr( "EntityClass",m_entityClass );
|
|
objNode->setAttr( "EntityId",m_entityId );
|
|
|
|
if (m_pMaterial)
|
|
{
|
|
objNode->setAttr( "Material",m_pMaterial->GetFullName() );
|
|
}
|
|
|
|
if (!m_physicsState.IsEmpty())
|
|
objNode->setAttr( "PhysicsState",m_physicsState );
|
|
|
|
objNode->setAttr( "Layer",GetLayer()->GetName() );
|
|
|
|
// Store parent entity.
|
|
if (GetParent())
|
|
{
|
|
if (GetParent()->IsKindOf( RUNTIME_CLASS(CEntity) ))
|
|
{
|
|
CEntity *parentEntity = (CEntity*)GetParent();
|
|
if (parentEntity)
|
|
objNode->setAttr( "ParentId",parentEntity->GetEntityId() );
|
|
|
|
Vec3 pos = GetPos();
|
|
Vec3 angles = GetAngles();
|
|
Vec3 scale = GetScale();
|
|
|
|
// When exporting optimize for 0,0,0 condition.
|
|
|
|
// Export local coords.
|
|
if (!IsEquivalent(pos,Vec3(0,0,0),0))
|
|
objNode->setAttr( "Pos",pos );
|
|
else
|
|
objNode->delAttr( "Pos" );
|
|
|
|
// Export local angles.
|
|
if (!IsEquivalent(angles,Vec3(0,0,0),0))
|
|
objNode->setAttr( "Angles",angles );
|
|
else
|
|
objNode->delAttr( "Angles" );
|
|
|
|
// Export local scale.
|
|
if (!IsEquivalent(scale,Vec3(1,1,1),0))
|
|
objNode->setAttr( "Scale",scale );
|
|
else
|
|
objNode->delAttr( "Scale" );
|
|
}
|
|
}
|
|
|
|
// Export Event Targets.
|
|
if (!m_eventTargets.empty())
|
|
{
|
|
XmlNodeRef eventTargets = objNode->newChild( "EventTargets" );
|
|
for (int i = 0; i < m_eventTargets.size(); i++)
|
|
{
|
|
CEntityEventTarget &et = m_eventTargets[i];
|
|
|
|
int entityId = 0;
|
|
if (et.target)
|
|
{
|
|
if (et.target->IsKindOf( RUNTIME_CLASS(CEntity) ))
|
|
{
|
|
entityId = ((CEntity*)et.target)->GetEntityId();
|
|
}
|
|
}
|
|
|
|
XmlNodeRef eventTarget = eventTargets->newChild( "EventTarget" );
|
|
//eventTarget->setAttr( "Target",obj->GetName() );
|
|
eventTarget->setAttr( "Target",entityId );
|
|
eventTarget->setAttr( "Event",et.event );
|
|
eventTarget->setAttr( "SourceEvent",et.sourceEvent );
|
|
}
|
|
}
|
|
|
|
//! Export properties.
|
|
if (m_properties)
|
|
{
|
|
XmlNodeRef propsNode = objNode->newChild("Properties");
|
|
m_properties->Serialize( propsNode,false );
|
|
}
|
|
//! Export properties.
|
|
if (m_properties2)
|
|
{
|
|
XmlNodeRef propsNode = objNode->newChild("Properties2");
|
|
m_properties2->Serialize( propsNode,false );
|
|
}
|
|
|
|
return objNode;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::OnEvent( ObjectEvent event )
|
|
{
|
|
CBaseObject::OnEvent(event);
|
|
|
|
switch (event)
|
|
{
|
|
case EVENT_INGAME:
|
|
case EVENT_OUTOFGAME:
|
|
if (m_entity)
|
|
{
|
|
if (event == EVENT_INGAME)
|
|
{
|
|
if (!m_bCalcPhysics)
|
|
m_entity->ClearFlags(ETY_FLAG_IGNORE_PHYSICS_UPDATE);
|
|
// Entity must be hidden when going to game.
|
|
if (m_visible)
|
|
m_entity->Hide( mv_hiddenInGame );
|
|
}
|
|
else if (event == EVENT_OUTOFGAME)
|
|
{
|
|
// Entity must be returned to editor visibility state.
|
|
m_entity->Hide( !m_visible );
|
|
m_entity->SetGarbageFlag(false); // If marked as garbage, unmark it.
|
|
}
|
|
XFormGameEntity();
|
|
if (!m_physicsState.IsEmpty())
|
|
{
|
|
IPhysicalEntity *physic = m_entity->GetPhysics();
|
|
if (physic)
|
|
{
|
|
const char *str = m_physicsState;
|
|
physic->SetStateFromSnapshotTxt( const_cast<char*>(str),m_physicsState.GetLength() );
|
|
physic->PostSetStateFromSnapshot();
|
|
}
|
|
}
|
|
if (event == EVENT_OUTOFGAME)
|
|
{
|
|
if (!m_bCalcPhysics)
|
|
m_entity->SetFlags(ETY_FLAG_IGNORE_PHYSICS_UPDATE);
|
|
}
|
|
}
|
|
break;
|
|
case EVENT_REFRESH:
|
|
if (m_entity)
|
|
{
|
|
// force entity to be registered in terrain sectors again.
|
|
|
|
//-- little hack to force reregistration of entities
|
|
//<<FIXME>> when issue with registration in editor is resolved
|
|
Vec3d pos = GetPos();
|
|
pos.z+=1.f;
|
|
m_entity->SetPos( pos,false );
|
|
//----------------------------------------------------
|
|
|
|
XFormGameEntity();
|
|
}
|
|
break;
|
|
|
|
case EVENT_AFTER_LOAD:
|
|
if (m_entity)
|
|
{
|
|
// Force entities to register them-self in sectors.
|
|
// force entity to be registered in terrain sectors again.
|
|
XFormGameEntity();
|
|
BindToParent();
|
|
BindIEntityChilds();
|
|
if (m_script)
|
|
{
|
|
//m_script->SetCurrentProperties( this );
|
|
m_script->SetEventsTable( this );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EVENT_UNLOAD_GEOM:
|
|
case EVENT_UNLOAD_ENTITY:
|
|
if (m_script)
|
|
m_script = 0;
|
|
if (m_entity)
|
|
{
|
|
UnloadScript();
|
|
}
|
|
break;
|
|
|
|
case EVENT_RELOAD_ENTITY:
|
|
GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( this );
|
|
if (m_script)
|
|
m_script->Reload();
|
|
Reload();
|
|
break;
|
|
|
|
case EVENT_RELOAD_GEOM:
|
|
GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( this );
|
|
Reload();
|
|
break;
|
|
|
|
case EVENT_PHYSICS_GETSTATE:
|
|
AcceptPhysicsState();
|
|
break;
|
|
case EVENT_PHYSICS_RESETSTATE:
|
|
ResetPhysicsState();
|
|
break;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::Reload( bool bReloadScript )
|
|
{
|
|
if (!m_script || bReloadScript)
|
|
LoadScript( m_entityClass,true );
|
|
if (m_entity)
|
|
DeleteEntity();
|
|
SpawnEntity();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::UpdateVisibility( bool visible )
|
|
{
|
|
CBaseObject::UpdateVisibility(visible);
|
|
if (visible != m_visible)
|
|
{
|
|
if (!visible && m_entity)
|
|
{
|
|
m_entity->Hide(true);
|
|
// Unload entity.
|
|
//OnEvent(EVENT_UNLOAD_ENTITY);
|
|
}
|
|
if (visible && m_entity)
|
|
{
|
|
m_entity->Hide(false);
|
|
}
|
|
}
|
|
m_visible = visible;
|
|
};
|
|
|
|
/*
|
|
class StaticInit
|
|
{
|
|
public:
|
|
StaticInit()
|
|
{
|
|
Vec3 angles(20,11.51,112);
|
|
Vec3 a1,a2;
|
|
|
|
Matrix tm;
|
|
tm.Identity();
|
|
tm.RotateMatrix( angles );
|
|
quaternionf qq( angles.z*PI/180.0f,angles.y*PI/180.0f,angles.x*PI/180.0f );
|
|
|
|
float mtx[3][3];
|
|
qq.getmatrix_buf( (float*)mtx );
|
|
|
|
Quat q(tm);
|
|
Quat q1;
|
|
q1.SetEulerAngles( angles*PI/180.0f );
|
|
Matrix tm1;
|
|
q1.GetMatrix(tm1);
|
|
a1 = q.GetEulerAngles() * 180.0f/PI;
|
|
a2 = q1.GetEulerAngles() * 180.0f/PI;
|
|
}
|
|
};
|
|
StaticInit ss;
|
|
*/
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::OnNodeAnimated()
|
|
{
|
|
if (!m_animNode)
|
|
return;
|
|
|
|
Vec3 pos = m_animNode->GetPos();
|
|
Vec3 scale = m_animNode->GetScale();
|
|
Quat rotate = m_animNode->GetRotate();
|
|
|
|
|
|
//m_entity->SetPos( GetPos(),false );
|
|
//m_entity->SetAngles( GetAngles() );
|
|
//m_entity->SetScale( GetScale().x );
|
|
|
|
bool bModified = false;
|
|
if (!IsVectorsEqual(pos,GetPos()))
|
|
{
|
|
CBaseObject::SetPos( pos );
|
|
bModified = true;
|
|
if (m_entity)
|
|
m_entity->SetPos( GetPos(),false );
|
|
}
|
|
if (rotate.w != m_rotate.w || rotate.v.x != m_rotate.v.x || rotate.v.y != m_rotate.v.y || rotate.v.z != m_rotate.v.z)
|
|
{
|
|
m_rotate = rotate;
|
|
Vec3 angles = RAD2DEG(Ang3::GetAnglesXYZ(Matrix33(m_rotate)));
|
|
CBaseObject::SetAngles( angles );
|
|
bModified = true;
|
|
if (m_entity)
|
|
m_entity->SetAngles( GetAngles() );
|
|
}
|
|
if (!IsVectorsEqual(scale,GetScale()))
|
|
{
|
|
CBaseObject::SetScale( scale );
|
|
bModified = true;
|
|
if (m_entity)
|
|
m_entity->SetScale( GetScale().x );
|
|
}
|
|
//if (bModified)
|
|
//XFormGameEntity();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::InvalidateTM()
|
|
{
|
|
CBaseObject::InvalidateTM();
|
|
|
|
// If matrix changes.
|
|
if (m_trackGizmo)
|
|
{
|
|
//m_trackGizmo->SetMatrix( GetWorldTM() );
|
|
if (GetParent())
|
|
{
|
|
m_trackGizmo->SetMatrix( GetParent()->GetWorldTM() );
|
|
}
|
|
else
|
|
{
|
|
Matrix44 tm;
|
|
tm.SetIdentity();
|
|
m_trackGizmo->SetMatrix(tm);
|
|
}
|
|
}
|
|
|
|
if (m_entity && GetParent())
|
|
{
|
|
if (!m_entity->IsBound())
|
|
{
|
|
m_entity->ForceBindCalculation(true);
|
|
m_entity->SetParentLocale( GetParent()->GetWorldTM() );
|
|
XFormGameEntity();
|
|
}
|
|
}
|
|
m_bEntityXfromValid = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Attach new child node.
|
|
void CEntity::AttachChild( CBaseObject* child,bool bKeepPos )
|
|
{
|
|
CBaseObject::AttachChild( child,bKeepPos );
|
|
if (child && child->IsKindOf(RUNTIME_CLASS(CEntity)))
|
|
{
|
|
((CEntity*)child)->BindToParent();
|
|
}
|
|
}
|
|
|
|
//! Detach all childs of this node.
|
|
void CEntity::DetachAll( bool bKeepPos )
|
|
{
|
|
//@FIXME: Unbind all childs.
|
|
CBaseObject::DetachAll(bKeepPos);
|
|
}
|
|
|
|
// Detach this node from parent.
|
|
void CEntity::DetachThis( bool bKeepPos )
|
|
{
|
|
UnbindIEntity();
|
|
CBaseObject::DetachThis(bKeepPos);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::BindToParent()
|
|
{
|
|
if (!m_entity)
|
|
return;
|
|
|
|
CBaseObject *parent = GetParent();
|
|
if (parent)
|
|
{
|
|
if (parent->IsKindOf(RUNTIME_CLASS(CEntity)))
|
|
{
|
|
CEntity *parentEntity = (CEntity*)parent;
|
|
|
|
IEntity *ientParent = parentEntity->GetIEntity();
|
|
if (ientParent)
|
|
{
|
|
XFormGameEntity();
|
|
ientParent->Bind( m_entity->GetId(),0 );
|
|
XFormGameEntity();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_entity->ForceBindCalculation(true);
|
|
m_entity->SetParentLocale( GetParent()->GetWorldTM() );
|
|
XFormGameEntity();
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::BindIEntityChilds()
|
|
{
|
|
if (!m_entity)
|
|
return;
|
|
|
|
int numChilds = GetChildCount();
|
|
for (int i = 0; i < numChilds; i++)
|
|
{
|
|
CBaseObject *child = GetChild(i);
|
|
if (child && child->IsKindOf(RUNTIME_CLASS(CEntity)))
|
|
{
|
|
IEntity *ientChild = ((CEntity*)child)->GetIEntity();
|
|
if (ientChild)
|
|
{
|
|
((CEntity*)child)->XFormGameEntity();
|
|
m_entity->Bind( ientChild->GetId(),0 );
|
|
((CEntity*)child)->XFormGameEntity();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::UnbindIEntity()
|
|
{
|
|
if (!m_entity)
|
|
return;
|
|
|
|
m_entity->ForceBindCalculation(false);
|
|
|
|
CBaseObject *parent = GetParent();
|
|
if (parent && parent->IsKindOf(RUNTIME_CLASS(CEntity)))
|
|
{
|
|
CEntity *parentEntity = (CEntity*)parent;
|
|
|
|
IEntity *ientParent = parentEntity->GetIEntity();
|
|
if (ientParent)
|
|
{
|
|
//m_entity->ForceBindCalculation(false);
|
|
ientParent->Unbind( m_entity->GetId(),0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::PostClone( CBaseObject *pFromObject,CObjectCloneContext &ctx )
|
|
{
|
|
CBaseObject::PostClone( pFromObject,ctx );
|
|
|
|
CEntity *pFromEntity = (CEntity*)pFromObject;
|
|
// Clone event targets.
|
|
if (!pFromEntity->m_eventTargets.empty())
|
|
{
|
|
int numTargets = pFromEntity->m_eventTargets.size();
|
|
for (int i = 0; i < numTargets; i++)
|
|
{
|
|
CEntityEventTarget &et = pFromEntity->m_eventTargets[i];
|
|
CBaseObject *pClonedTarget = ctx.FindClone( et.target );
|
|
if (!pClonedTarget)
|
|
pClonedTarget = et.target; // If target not cloned, link to original target.
|
|
|
|
// Add cloned event.
|
|
AddEventTarget( pClonedTarget,et.event,et.sourceEvent,true );
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::ResolveEventTarget( CBaseObject *object,unsigned int index )
|
|
{
|
|
// Find targetid.
|
|
assert( index >= 0 && index < m_eventTargets.size() );
|
|
if (object)
|
|
object->AddEventListener( functor(*this,&CEntity::OnEventTargetEvent) );
|
|
m_eventTargets[index].target = object;
|
|
if (!m_eventTargets.empty() && m_script != 0)
|
|
m_script->SetEventsTable( this );
|
|
|
|
// Make line gizmo.
|
|
if (!m_eventTargets[index].pLineGizmo && object)
|
|
{
|
|
CLineGizmo *pLineGizmo = new CLineGizmo;
|
|
pLineGizmo->SetObjects( this,object );
|
|
pLineGizmo->SetColor( Vec3(0.8f,0.4f,0.4f),Vec3(0.8f,0.4f,0.4f) );
|
|
pLineGizmo->SetName( m_eventTargets[index].event );
|
|
AddGizmo( pLineGizmo );
|
|
m_eventTargets[index].pLineGizmo = pLineGizmo;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::ReleaseEventTargets()
|
|
{
|
|
while (!m_eventTargets.empty())
|
|
RemoveEventTarget( m_eventTargets.size()-1,false );
|
|
m_eventTargets.clear();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::OnEventTargetEvent( CBaseObject *target,int event )
|
|
{
|
|
// When event target is deleted.
|
|
if (event == CBaseObject::ON_DELETE)
|
|
{
|
|
// Find this target in events list and remove.
|
|
int numTargets = m_eventTargets.size();
|
|
for (int i = 0; i < numTargets; i++)
|
|
{
|
|
if (m_eventTargets[i].target == target)
|
|
{
|
|
RemoveEventTarget( i );
|
|
numTargets = m_eventTargets.size();
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CEntity::AddEventTarget( CBaseObject *target,const CString &event,const CString &sourceEvent,bool bUpdateScript )
|
|
{
|
|
StoreUndo( "Add EventTarget" );
|
|
CEntityEventTarget et;
|
|
et.target = target;
|
|
et.event = event;
|
|
et.sourceEvent = sourceEvent;
|
|
|
|
// Assign event target.
|
|
if (et.target)
|
|
et.target->AddEventListener( functor(*this,&CEntity::OnEventTargetEvent) );
|
|
|
|
if (target)
|
|
{
|
|
// Make line gizmo.
|
|
CLineGizmo *pLineGizmo = new CLineGizmo;
|
|
pLineGizmo->SetObjects( this,target );
|
|
pLineGizmo->SetColor( Vec3(0.8f,0.4f,0.4f),Vec3(0.8f,0.4f,0.4f) );
|
|
pLineGizmo->SetName( event );
|
|
AddGizmo( pLineGizmo );
|
|
et.pLineGizmo = pLineGizmo;
|
|
}
|
|
|
|
m_eventTargets.push_back( et );
|
|
|
|
// Update event table in script.
|
|
if (bUpdateScript && m_script != 0)
|
|
m_script->SetEventsTable( this );
|
|
|
|
return m_eventTargets.size()-1;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::RemoveEventTarget( int index,bool bUpdateScript )
|
|
{
|
|
if (index >= 0 && index < m_eventTargets.size())
|
|
{
|
|
StoreUndo( "Remove EventTarget" );
|
|
|
|
if (m_eventTargets[index].pLineGizmo)
|
|
{
|
|
RemoveGizmo( m_eventTargets[index].pLineGizmo );
|
|
}
|
|
|
|
if (m_eventTargets[index].target)
|
|
m_eventTargets[index].target->RemoveEventListener( functor(*this,&CEntity::OnEventTargetEvent) );
|
|
m_eventTargets.erase( m_eventTargets.begin()+index );
|
|
|
|
// Update event table in script.
|
|
if (bUpdateScript && m_script != 0 && m_entity != 0)
|
|
m_script->SetEventsTable( this );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::OnRenderFlagsChange( IVariable *var )
|
|
{
|
|
if (m_entity)
|
|
{
|
|
m_entity->SetRndFlags(ERF_CASTSHADOWVOLUME, mv_castShadows);
|
|
m_entity->SetRndFlags(ERF_SELFSHADOW, mv_selfShadowing);;
|
|
m_entity->SetRndFlags(ERF_CASTSHADOWMAPS, mv_castShadowMaps);
|
|
m_entity->SetRndFlags(ERF_RECVSHADOWMAPS, mv_recvShadowMaps);
|
|
m_entity->SetRndFlags(ERF_CASTSHADOWINTOLIGHTMAP,mv_castLightmap);
|
|
m_entity->SetRndFlags(ERF_USELIGHTMAPS,mv_recvLightmap);
|
|
m_entity->SetRndFlags(ERF_SELECTED,IsSelected());
|
|
|
|
m_entity->SetLodRatio(mv_ratioLOD);
|
|
m_entity->SetViewDistRatio(mv_ratioViewDist);
|
|
//m_entity->SetUpdateVisLevel((EEntityUpdateVisLevel)(int)mv_UpdateVisLevel);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::OnRadiusChange( IVariable *var )
|
|
{
|
|
var->Get(m_proximityRadius);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::OnInnerRadiusChange( IVariable *var )
|
|
{
|
|
var->Get(m_innerRadius);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::OnOuterRadiusChange( IVariable *var )
|
|
{
|
|
var->Get(m_outerRadius);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CVarBlock* CEntity::CloneProperties( CVarBlock *srcProperties )
|
|
{
|
|
assert( srcProperties );
|
|
|
|
CVarBlock *properties = srcProperties->Clone(true);
|
|
|
|
//@FIXME Hack to dispay radiuses of properties.
|
|
// wires properties from param block, to this entity internal variables.
|
|
IVariable *var = 0;
|
|
var = properties->FindVariable( "Radius",false );
|
|
if (var && (var->GetType() == IVariable::FLOAT || var->GetType() == IVariable::INT))
|
|
{
|
|
var->Get(m_proximityRadius);
|
|
var->AddOnSetCallback( functor(*this,&CEntity::OnRadiusChange) );
|
|
}
|
|
var = properties->FindVariable( "InnerRadius",false );
|
|
if (var && (var->GetType() == IVariable::FLOAT || var->GetType() == IVariable::INT))
|
|
{
|
|
var->Get(m_innerRadius);
|
|
var->AddOnSetCallback( functor(*this,&CEntity::OnInnerRadiusChange) );
|
|
}
|
|
var = properties->FindVariable( "OuterRadius",false );
|
|
if (var && (var->GetType() == IVariable::FLOAT || var->GetType() == IVariable::INT))
|
|
{
|
|
var->Get(m_outerRadius);
|
|
var->AddOnSetCallback( functor(*this,&CEntity::OnOuterRadiusChange) );
|
|
}
|
|
|
|
// Each property must have callback to our OnPropertyChange.
|
|
properties->AddOnSetCallback( functor(*this,&CEntity::OnPropertyChange) );
|
|
|
|
return properties;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::OnLoadFailed()
|
|
{
|
|
m_loadFailed = true;
|
|
|
|
CErrorRecord err;
|
|
err.error.Format( "Entity %s Failed to Spawn (Script: %s)",(const char*)GetName(),(const char*)m_entityClass );
|
|
err.pObject = this;
|
|
GetIEditor()->GetErrorReport()->ReportError(err);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::SetMaterial( CMaterial *mtl )
|
|
{
|
|
StoreUndo( "Assign Material" );
|
|
m_pMaterial = mtl;
|
|
if (m_pMaterial)
|
|
{
|
|
m_pMaterial->SetUsed();
|
|
m_materialGUID = m_pMaterial->GetGUID();
|
|
}
|
|
else
|
|
{
|
|
ZeroStruct(m_materialGUID);
|
|
}
|
|
UpdateMaterialInfo();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::UpdateMaterialInfo()
|
|
{
|
|
if (m_entity)
|
|
{
|
|
if (m_pMaterial)
|
|
m_pMaterial->AssignToEntity( m_entity );
|
|
else
|
|
m_entity->SetMaterial(0);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::AcceptPhysicsState()
|
|
{
|
|
if (m_entity)
|
|
{
|
|
//StoreUndo( "Accept Physics State" );
|
|
// [Anton] - StoreUndo sends EVENT_AFTER_LOAD, which forces position and angles to editor's position and
|
|
// angles, which are not updated with the physics value
|
|
Vec3 pos = m_entity->GetPos();
|
|
Vec3 angles = m_entity->GetAngles();
|
|
SetPos( pos );
|
|
SetAngles( angles );
|
|
IPhysicalEntity *physic = m_entity->GetPhysics();
|
|
if (physic && physic->GetType() == PE_ARTICULATED)
|
|
{
|
|
// Only get state snapshot for articulated characters.
|
|
char str[4096*4];
|
|
int len = physic->GetStateSnapshotTxt( str,sizeof(str) );
|
|
if (len > sizeof(str)-1)
|
|
len = sizeof(str)-1;
|
|
str[len] = 0;
|
|
m_physicsState = str;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::ResetPhysicsState()
|
|
{
|
|
if (m_entity)
|
|
{
|
|
//StoreUndo( "Reset Physics State" );
|
|
m_physicsState = "";
|
|
Reload();
|
|
}
|
|
}
|
|
|
|
void CEntity::SetHelperScale( float scale )
|
|
{
|
|
bool bChanged = m_helperScale != scale;
|
|
m_helperScale = scale;
|
|
if (bChanged)
|
|
{
|
|
CalcBBox();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Analyze errors for this object.
|
|
void CEntity::Validate( CErrorReport *report )
|
|
{
|
|
CBaseObject::Validate( report );
|
|
// Checks for invalid values in base object.
|
|
if (!GuidUtil::IsEmpty(m_materialGUID) && m_pMaterial == NULL)
|
|
{
|
|
CErrorRecord err;
|
|
err.error.Format( "Material %s for Entity %s not found,",GuidUtil::ToString(m_materialGUID),(const char*)GetName() );
|
|
err.pObject = this;
|
|
report->ReportError(err);
|
|
//Warning( "Material %s for Entity %s not found,",GuidUtil::ToString(m_materialGUID),(const char*)GetName() );
|
|
}
|
|
|
|
if (!m_entity)
|
|
{
|
|
CErrorRecord err;
|
|
err.error.Format( "Entity %s Failed to Spawn (Script: %s)",(const char*)GetName(),(const char*)m_entityClass );
|
|
err.pObject = this;
|
|
report->ReportError(err);
|
|
return;
|
|
}
|
|
|
|
int slot;
|
|
|
|
// Check Entity.
|
|
int numObj = m_entity->GetNumObjects();
|
|
for (slot = 0; slot < numObj; slot++)
|
|
{
|
|
CEntityObject obj;
|
|
if (!m_entity->GetEntityObject( slot,obj ))
|
|
continue;
|
|
|
|
if (obj.object != NULL && obj.object->IsDefaultObject())
|
|
{
|
|
//const char *filename = obj.object->GetFileName();
|
|
// File Not found.
|
|
CErrorRecord err;
|
|
err.error.Format( "Geometry File in Slot %d for Entity %s not found",slot,(const char*)GetName() );
|
|
//err.file = filename;
|
|
err.pObject = this;
|
|
err.flags = CErrorRecord::FLAG_NOFILE;
|
|
report->ReportError(err);
|
|
}
|
|
}
|
|
/*
|
|
IEntityCharacter *pIChar = m_entity->GetCharInterface();
|
|
pIChar->GetCharacter(0);
|
|
for (slot = 0; slot < MAX_ANIMATED_MODELS; slot++)
|
|
{
|
|
ICryCharInstance *pCharacter = pIChar->GetCharacter(slot);
|
|
pCharacter->IsDe
|
|
}
|
|
*/
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CEntity::GatherUsedResources( CUsedResources &resources )
|
|
{
|
|
CBaseObject::GatherUsedResources( resources );
|
|
if (m_properties)
|
|
m_properties->GatherUsedResources( resources );
|
|
if (m_properties2)
|
|
m_properties2->GatherUsedResources( resources );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CEntity::IsSimilarObject( CBaseObject *pObject )
|
|
{
|
|
if (pObject->GetClassDesc() == GetClassDesc() && pObject->GetRuntimeClass() == GetRuntimeClass())
|
|
{
|
|
CEntity *pEntity = (CEntity*)pObject;
|
|
if (m_entityClass == pEntity->m_entityClass &&
|
|
m_proximityRadius == pEntity->m_proximityRadius &&
|
|
m_innerRadius == pEntity->m_innerRadius &&
|
|
m_outerRadius == pEntity->m_outerRadius)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CSimple Entity.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CSimpleEntity::Init( IEditor *ie,CBaseObject *prev,const CString &file )
|
|
{
|
|
bool bRes = false;
|
|
if (file.IsEmpty())
|
|
{
|
|
bRes = CEntity::Init( ie,prev,"" );
|
|
}
|
|
else
|
|
{
|
|
bRes = CEntity::Init( ie,prev,"BasicEntity" );
|
|
LoadScript( m_entityClass );
|
|
SetGeometryFile( file );
|
|
}
|
|
|
|
return bRes;
|
|
}
|
|
|
|
void CSimpleEntity::SetGeometryFile( const CString &filename )
|
|
{
|
|
if (m_properties)
|
|
{
|
|
IVariable* pModelVar = m_properties->FindVariable( "object_Model" );
|
|
if (pModelVar)
|
|
{
|
|
pModelVar->Set( filename );
|
|
}
|
|
}
|
|
}
|
|
|
|
CString CSimpleEntity::GetGeometryFile() const
|
|
{
|
|
CString filename;
|
|
if (m_properties)
|
|
{
|
|
IVariable* pModelVar = m_properties->FindVariable( "object_Model" );
|
|
if (pModelVar)
|
|
{
|
|
pModelVar->Get( filename );
|
|
}
|
|
}
|
|
return filename;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CSimpleEntity::ConvertFromObject( CBaseObject *object )
|
|
{
|
|
CBaseObject::ConvertFromObject( object );
|
|
if (object->IsKindOf(RUNTIME_CLASS(CBrushObject)))
|
|
{
|
|
CBrushObject *pBrushObject = (CBrushObject*)object;
|
|
|
|
IStatObj *prefab = pBrushObject->GetPrefabGeom();
|
|
if (!prefab)
|
|
return false;
|
|
|
|
// Copy entity shadow parameters.
|
|
int rndFlags = pBrushObject->GetRenderFlags();
|
|
|
|
mv_castShadows = (rndFlags & ERF_CASTSHADOWVOLUME) != 0;
|
|
mv_selfShadowing = (rndFlags & ERF_SELFSHADOW) != 0;
|
|
mv_castShadowMaps = (rndFlags & ERF_CASTSHADOWMAPS) != 0;
|
|
mv_recvShadowMaps = (rndFlags & ERF_RECVSHADOWMAPS) != 0;
|
|
mv_castLightmap = (rndFlags & ERF_CASTSHADOWINTOLIGHTMAP) != 0;
|
|
mv_recvLightmap = (rndFlags & ERF_USELIGHTMAPS) != 0;
|
|
mv_ratioLOD = pBrushObject->GetRatioLod();
|
|
mv_ratioViewDist = pBrushObject->GetRatioViewDist();
|
|
|
|
LoadScript( "BasicEntity" );
|
|
SpawnEntity();
|
|
SetGeometryFile( prefab->GetFileName() );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSimpleEntity::BeginEditParams( IEditor *ie,int flags )
|
|
{
|
|
CEntity::BeginEditParams( ie,flags );
|
|
|
|
CString filename = GetGeometryFile();
|
|
|
|
if (gSettings.bGeometryBrowserPanel)
|
|
{
|
|
if (!filename.IsEmpty())
|
|
{
|
|
if (!s_treePanel)
|
|
{
|
|
s_treePanel = new CPanelTreeBrowser;
|
|
int flags = CPanelTreeBrowser::NO_DRAGDROP|CPanelTreeBrowser::NO_PREVIEW|CPanelTreeBrowser::SELECT_ONCLICK;
|
|
s_treePanel->Create( functor(*this,&CSimpleEntity::OnFileChange),GetClassDesc()->GetFileSpec(),AfxGetMainWnd(),flags );
|
|
}
|
|
if (s_treePanelId == 0)
|
|
s_treePanelId = GetIEditor()->AddRollUpPage( ROLLUP_OBJECTS,_T("Geometry"),s_treePanel,false );
|
|
}
|
|
|
|
if (s_treePanel)
|
|
{
|
|
s_treePanel->SetSelectCallback( functor(*this,&CSimpleEntity::OnFileChange) );
|
|
s_treePanel->SelectFile( filename );
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSimpleEntity::EndEditParams( IEditor *ie )
|
|
{
|
|
if (s_treePanelId != 0)
|
|
{
|
|
GetIEditor()->RemoveRollUpPage( ROLLUP_OBJECTS,s_treePanelId );
|
|
s_treePanelId = 0;
|
|
}
|
|
|
|
CEntity::EndEditParams( ie );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSimpleEntity::OnFileChange( CString filename )
|
|
{
|
|
CUndo undo("Entity Geom Modify");
|
|
StoreUndo( "Entity Geom Modify" );
|
|
SetGeometryFile( filename );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Analyze errors for this object.
|
|
void CSimpleEntity::Validate( CErrorReport *report )
|
|
{
|
|
CEntity::Validate( report );
|
|
|
|
// Checks if object loaded.
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CSimpleEntity::IsSimilarObject( CBaseObject *pObject )
|
|
{
|
|
if (pObject->GetClassDesc() == GetClassDesc() && pObject->GetRuntimeClass() == GetRuntimeClass())
|
|
{
|
|
CSimpleEntity *pEntity = (CSimpleEntity*)pObject;
|
|
if (GetGeometryFile() == pEntity->GetGeometryFile())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
} |