1010 lines
25 KiB
C++
1010 lines
25 KiB
C++
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Crytek Engine Source File.
|
|
// Copyright (C), Crytek Studios, 2001.
|
|
// -------------------------------------------------------------------------
|
|
// File name: BrushObject.cpp
|
|
// Version: v1.00
|
|
// Created: 10/10/2001 by Timur.
|
|
// Compilers: Visual C++ 6.0
|
|
// Description: CBrushObject implementation.
|
|
// -------------------------------------------------------------------------
|
|
// History:
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "StdAfx.h"
|
|
#include "BrushObject.h"
|
|
|
|
#include "ObjectManager.h"
|
|
#include "..\Viewport.h"
|
|
|
|
#include "..\Brush\BrushPanel.h"
|
|
#include "..\Brush\Brush.h"
|
|
#include "PanelTreeBrowser.h"
|
|
|
|
#include "Entity.h"
|
|
#include "EdMesh.h"
|
|
#include "Material\Material.h"
|
|
#include "Material\MaterialManager.h"
|
|
#include "Settings.h"
|
|
|
|
#include <I3Dengine.h>
|
|
#include <IEntitySystem.h>
|
|
|
|
#define MIN_BOUNDS_SIZE 0.01f
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CBase implementation.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
IMPLEMENT_DYNCREATE(CBrushObject,CBaseObject)
|
|
|
|
namespace
|
|
{
|
|
CBrushPanel* s_brushPanel = NULL;
|
|
int s_brushPanelId = 0;
|
|
|
|
CPanelTreeBrowser s_treePanel;
|
|
CPanelTreeBrowser* s_treePanelPtr = NULL;
|
|
int s_treePanelId = 0;
|
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBrushObject::CBrushObject()
|
|
{
|
|
m_prefabGeom = 0;
|
|
m_indoor = 0;
|
|
m_engineNode = 0;
|
|
|
|
m_renderFlags = 0;
|
|
|
|
AddVariable( mv_prefabName,"Prefab",functor(*this,&CBrushObject::OnPrefabChange),IVariable::DT_OBJECT );
|
|
|
|
// Init Variables.
|
|
mv_outdoor = false;
|
|
mv_castShadows = false;
|
|
mv_selfShadowing = false;
|
|
mv_castShadowMaps = false;
|
|
mv_recvShadowMaps = true;
|
|
mv_castLightmap = true;
|
|
mv_recvLightmap = true;
|
|
mv_hideable = false;
|
|
mv_ratioLOD = 100;
|
|
mv_ratioViewDist = 100;
|
|
mv_excludeFromTriangulation = false;
|
|
mv_lightmapQuality = 1;
|
|
mv_lightmapQuality.SetLimits( 0,100 );
|
|
|
|
ZeroStruct(m_materialGUID);
|
|
|
|
static CString sVarName_OutdoorOnly = "OutdoorOnly";
|
|
static CString sVarName_CastShadows = "CastShadows";
|
|
static CString sVarName_CastShadows2 = _T("CastShadowVolume");
|
|
static CString sVarName_SelfShadowing = "SelfShadowing";
|
|
static CString sVarName_CastShadowMaps = "CastShadowMaps";
|
|
static CString sVarName_RecvShadowMaps = "RecvShadowMaps";
|
|
static CString sVarName_CastLightmap = "CastLightmap";
|
|
static CString sVarName_ReceiveLightmap = "ReceiveLightmap";
|
|
static CString sVarName_Hideable = "Hideable";
|
|
static CString sVarName_LodRatio = "LodRatio";
|
|
static CString sVarName_ViewDistRatio = "ViewDistRatio";
|
|
static CString sVarName_NotTriangulate = "NotTriangulate";
|
|
static CString sVarName_LightmapQuality = "LightmapQuality";
|
|
|
|
ReserveNumVariables( 11 );
|
|
AddVariable( mv_outdoor,sVarName_OutdoorOnly,functor(*this, &CBrushObject::OnRenderVarChange) );
|
|
AddVariable( mv_castShadows,sVarName_CastShadows,sVarName_CastShadows2,functor(*this, &CBrushObject::OnRenderVarChange) );
|
|
AddVariable( mv_selfShadowing,sVarName_SelfShadowing,functor(*this, &CBrushObject::OnRenderVarChange) );
|
|
AddVariable( mv_castShadowMaps,sVarName_CastShadowMaps,functor(*this, &CBrushObject::OnRenderVarChange) );
|
|
AddVariable( mv_recvShadowMaps,sVarName_RecvShadowMaps,functor(*this, &CBrushObject::OnRenderVarChange) );
|
|
|
|
AddVariable( mv_castLightmap,sVarName_CastLightmap,functor(*this, &CBrushObject::OnRenderVarChange) );
|
|
AddVariable( mv_recvLightmap,sVarName_ReceiveLightmap,functor(*this, &CBrushObject::OnRenderVarChange) );
|
|
AddVariable( mv_hideable,sVarName_Hideable,functor(*this, &CBrushObject::OnRenderVarChange) );
|
|
AddVariable( mv_ratioLOD,sVarName_LodRatio,functor(*this, &CBrushObject::OnRenderVarChange) );
|
|
AddVariable( mv_ratioViewDist,sVarName_ViewDistRatio,functor(*this, &CBrushObject::OnRenderVarChange) );
|
|
AddVariable( mv_excludeFromTriangulation,sVarName_NotTriangulate,functor(*this, &CBrushObject::OnRenderVarChange) );
|
|
AddVariable( mv_lightmapQuality,sVarName_LightmapQuality );
|
|
|
|
mv_ratioLOD.SetLimits( 0,255 );
|
|
mv_ratioViewDist.SetLimits( 0,255 );
|
|
|
|
m_bIgnoreNodeUpdate = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::Done()
|
|
{
|
|
if (m_engineNode)
|
|
{
|
|
GetIEditor()->Get3DEngine()->DeleteEntityRender(m_engineNode);
|
|
m_engineNode = 0;
|
|
}
|
|
// Release Mesh.
|
|
m_prefabGeom = 0;
|
|
|
|
//free brush.
|
|
m_brush = 0;
|
|
|
|
CBaseObject::Done();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBrushObject::Init( IEditor *ie,CBaseObject *prev,const CString &file )
|
|
{
|
|
SetColor( RGB(255,255,255) );
|
|
|
|
if (IsCreateGameObjects())
|
|
{
|
|
if (prev)
|
|
{
|
|
CBrushObject *brushObj = (CBrushObject*)prev;
|
|
if (brushObj->GetBrush())
|
|
{
|
|
SetBrush( brushObj->GetBrush()->Clone(true) );
|
|
}
|
|
}
|
|
else if (!file.IsEmpty())
|
|
{
|
|
// Create brush from prefab.
|
|
mv_prefabName = file;
|
|
|
|
CString name = Path::GetFileName( file );
|
|
SetUniqName( name );
|
|
}
|
|
//m_indoor->AddBrush( this );
|
|
}
|
|
|
|
// Must be after SetBrush call.
|
|
bool res = CBaseObject::Init( ie,prev,file );
|
|
|
|
if (prev)
|
|
{
|
|
CBrushObject *brushObj = (CBrushObject*)prev;
|
|
// Copy material GUID.
|
|
m_materialGUID = brushObj->m_materialGUID;
|
|
m_bbox = brushObj->m_bbox;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBrushObject::CreateGameObject()
|
|
{
|
|
if (!m_engineNode)
|
|
{
|
|
m_engineNode = GetIEditor()->Get3DEngine()->CreateEntityRender();
|
|
m_engineNode->SetEditorObjectId( GetId().Data1 );
|
|
OnRenderVarChange(0);
|
|
UpdateEngineNode();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::BeginEditParams( IEditor *ie,int flags )
|
|
{
|
|
CBaseObject::BeginEditParams( ie,flags );
|
|
|
|
if (!s_brushPanel)
|
|
{
|
|
s_brushPanel = new CBrushPanel;
|
|
s_brushPanelId = ie->AddRollUpPage( ROLLUP_OBJECTS,_T("Brush Parameters"),s_brushPanel );
|
|
}
|
|
|
|
if (gSettings.bGeometryBrowserPanel)
|
|
{
|
|
CString prefabName = mv_prefabName;
|
|
if (!prefabName.IsEmpty())
|
|
{
|
|
if (!s_treePanelPtr)
|
|
{
|
|
s_treePanelPtr = &s_treePanel;
|
|
//s_treePanel = new CPanelTreeBrowser;
|
|
int flags = CPanelTreeBrowser::NO_DRAGDROP|CPanelTreeBrowser::NO_PREVIEW|CPanelTreeBrowser::SELECT_ONCLICK;
|
|
s_treePanelPtr->Create( functor(*this, &CBrushObject::OnFileChange),GetClassDesc()->GetFileSpec(),AfxGetMainWnd(),flags );
|
|
}
|
|
if (s_treePanelId == 0)
|
|
s_treePanelId = GetIEditor()->AddRollUpPage( ROLLUP_OBJECTS,_T("Prefab"),s_treePanelPtr,false );
|
|
}
|
|
|
|
if (s_treePanelPtr)
|
|
{
|
|
s_treePanelPtr->SetSelectCallback( functor(*this, &CBrushObject::OnFileChange) );
|
|
s_treePanelPtr->SelectFile( prefabName );
|
|
}
|
|
}
|
|
|
|
if (s_brushPanel)
|
|
s_brushPanel->SetBrush( this );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::EndEditParams( IEditor *ie )
|
|
{
|
|
CBaseObject::EndEditParams( ie );
|
|
|
|
if (s_treePanelId != 0)
|
|
{
|
|
GetIEditor()->RemoveRollUpPage( ROLLUP_OBJECTS,s_treePanelId );
|
|
s_treePanelId = 0;
|
|
}
|
|
|
|
if (s_brushPanel)
|
|
{
|
|
ie->RemoveRollUpPage( ROLLUP_OBJECTS,s_brushPanelId );
|
|
s_brushPanel = 0;
|
|
s_brushPanelId = 0;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::BeginEditMultiSelParams( bool bAllOfSameType )
|
|
{
|
|
CBaseObject::BeginEditMultiSelParams( bAllOfSameType );
|
|
if (bAllOfSameType)
|
|
{
|
|
if (!s_brushPanel)
|
|
{
|
|
s_brushPanel = new CBrushPanel;
|
|
s_brushPanelId = GetIEditor()->AddRollUpPage( ROLLUP_OBJECTS,_T("Brush Parameters"),s_brushPanel );
|
|
}
|
|
if (s_brushPanel)
|
|
s_brushPanel->SetBrush(0);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::EndEditMultiSelParams()
|
|
{
|
|
CBaseObject::EndEditMultiSelParams();
|
|
if (s_brushPanel)
|
|
{
|
|
GetIEditor()->RemoveRollUpPage( ROLLUP_OBJECTS,s_brushPanelId );
|
|
s_brushPanel = 0;
|
|
s_brushPanelId = 0;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::OnFileChange( CString filename )
|
|
{
|
|
CUndo undo("Brush Prefab Modify");
|
|
StoreUndo( "Brush Prefab Modify" );
|
|
mv_prefabName = filename;
|
|
if (m_brush)
|
|
m_brush->ResetToPrefabSize();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::SetScale( const Vec3d &scale )
|
|
{
|
|
// Ignore scale;
|
|
CBaseObject::SetScale( scale );
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::SetSelected( bool bSelect )
|
|
{
|
|
CBaseObject::SetSelected( bSelect );
|
|
|
|
if (m_engineNode)
|
|
{
|
|
if (bSelect)
|
|
m_renderFlags |= ERF_SELECTED;
|
|
else
|
|
m_renderFlags &= ~ERF_SELECTED;
|
|
m_engineNode->SetRndFlags( m_renderFlags );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::GetBoundBox( BBox &box )
|
|
{
|
|
box = m_bbox;
|
|
box.Transform( GetWorldTM() );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::GetLocalBounds( BBox &box )
|
|
{
|
|
box = m_bbox;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CBrushObject::MouseCreateCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags )
|
|
{
|
|
if (event == eMouseMove || event == eMouseLDown)
|
|
{
|
|
Vec3 pos = view->MapViewToCP( point );
|
|
SetPos( pos );
|
|
if (event == eMouseLDown)
|
|
return MOUSECREATE_OK;
|
|
return MOUSECREATE_CONTINUE;
|
|
}
|
|
return CBaseObject::MouseCreateCallback( view,event,point,flags );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::Display( DisplayContext &dc )
|
|
{
|
|
if (dc.flags & DISPLAY_2D)
|
|
{
|
|
int flags = 0;
|
|
|
|
if (IsSelected())
|
|
{
|
|
dc.SetLineWidth(2);
|
|
|
|
flags = 1;
|
|
//dc.SetSelectedColor();
|
|
dc.SetColor( RGB(225,0,0) );
|
|
}
|
|
else
|
|
{
|
|
flags = 0;
|
|
dc.SetColor( GetColor() );
|
|
}
|
|
|
|
dc.PushMatrix( GetWorldTM() );
|
|
dc.DrawWireBox( m_bbox.min,m_bbox.max );
|
|
dc.PopMatrix();
|
|
//if (m_brush)
|
|
//dc.view->DrawBrush( dc,m_brush,GetWorldTM(),flags );
|
|
|
|
if (IsSelected())
|
|
dc.SetLineWidth(1);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
if (m_brush != 0 && m_indoor != 0)
|
|
{
|
|
if (!(m_brush->m_flags&BRF_RE_VALID))
|
|
{
|
|
m_indoor->UpdateObject( m_brush->GetIndoorGeom() );
|
|
m_indoor->RecalcBounds();
|
|
}
|
|
Vec3 invCamSrc = dc.camera->GetPos();
|
|
invCamSrc = m_invertTM.TransformPoint(invCamSrc);
|
|
|
|
m_brush->Render( dc.renderer,invCamSrc )
|
|
}
|
|
*/
|
|
|
|
if (IsSelected())
|
|
{
|
|
dc.SetSelectedColor();
|
|
dc.PushMatrix( GetWorldTM() );
|
|
dc.DrawWireBox( m_bbox.min,m_bbox.max );
|
|
//dc.SetColor( RGB(255,255,0),0.1f ); // Yellow selected color.
|
|
//dc.DrawSolidBox( m_bbox.min,m_bbox.max );
|
|
dc.PopMatrix();
|
|
}
|
|
|
|
|
|
DrawDefault( dc );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
XmlNodeRef CBrushObject::Export( const CString &levelPath,XmlNodeRef &xmlNode )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::Serialize( CObjectArchive &ar )
|
|
{
|
|
XmlNodeRef xmlNode = ar.node;
|
|
m_bIgnoreNodeUpdate = true;
|
|
CBaseObject::Serialize( ar );
|
|
m_bIgnoreNodeUpdate = false;
|
|
if (ar.bLoading)
|
|
{
|
|
ZeroStruct(m_materialGUID);
|
|
if (ar.node->getAttr( "MaterialGUID",m_materialGUID ))
|
|
{
|
|
m_pMaterial = (CMaterial*)GetIEditor()->GetMaterialManager()->FindItem( m_materialGUID );
|
|
if (!m_pMaterial)
|
|
{
|
|
CErrorRecord err;
|
|
err.error.Format( "Material %s for Brush %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 Brush %s not found,",GuidUtil::ToString(m_materialGUID),(const char*)GetName() );
|
|
}
|
|
else
|
|
{
|
|
if (m_pMaterial->GetParent())
|
|
SetMaterial( m_pMaterial->GetParent() );
|
|
m_pMaterial->SetUsed();
|
|
}
|
|
//if (m_engineNode)
|
|
//UpdateEngineNode();
|
|
}
|
|
else
|
|
m_pMaterial = 0;
|
|
|
|
/*
|
|
if (ar.bUndo)
|
|
{
|
|
OnPrefabChange(0);
|
|
}
|
|
*/
|
|
if (!m_prefabGeom)
|
|
{
|
|
CString mesh = mv_prefabName;
|
|
if (!mesh.IsEmpty())
|
|
CreateBrushFromPrefab( mesh );
|
|
}
|
|
|
|
if (!m_prefabGeom)
|
|
{
|
|
XmlNodeRef brushNode = xmlNode->findChild( "Brush" );
|
|
if (brushNode)
|
|
{
|
|
SBrush *brush = m_brush;
|
|
if (!brush)
|
|
brush = new SBrush;
|
|
|
|
if (!m_prefabGeom)
|
|
brush->Serialize( brushNode,ar.bLoading );
|
|
|
|
if (!m_brush)
|
|
SetBrush( brush );
|
|
}
|
|
}
|
|
UpdateEngineNode();
|
|
}
|
|
else
|
|
{
|
|
ar.node->setAttr( "RndFlags",m_renderFlags );
|
|
if (!GuidUtil::IsEmpty(m_materialGUID))
|
|
{
|
|
ar.node->setAttr( "MaterialGUID",m_materialGUID );
|
|
}
|
|
if (m_brush)
|
|
{
|
|
if (!m_prefabGeom)
|
|
{
|
|
XmlNodeRef brushNode = xmlNode->newChild( "Brush" );
|
|
m_brush->Serialize( brushNode,ar.bLoading );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBrushObject::HitTest( HitContext &hc )
|
|
{
|
|
Vec3 pnt;
|
|
|
|
Vec3 raySrc = hc.raySrc;
|
|
Vec3 rayDir = hc.rayDir;
|
|
WorldToLocalRay( raySrc,rayDir );
|
|
|
|
if (m_bbox.IsIntersectRay( raySrc,rayDir,pnt ))
|
|
{
|
|
if (hc.b2DViewport)
|
|
{
|
|
// World space distance.
|
|
hc.dist = GetDistance(hc.raySrc, GetWorldTM().TransformPointOLD(pnt));
|
|
return true;
|
|
}
|
|
|
|
IPhysicalEntity *physics = 0;
|
|
|
|
if (m_engineNode)
|
|
{
|
|
physics = m_engineNode->GetPhysics();
|
|
if (physics)
|
|
{
|
|
if (physics->GetStatus( &pe_status_nparts() ) == 0)
|
|
physics = 0;
|
|
}
|
|
}
|
|
|
|
if (physics)
|
|
{
|
|
vectorr origin = hc.raySrc;
|
|
vectorr dir = hc.rayDir*10000.0f;
|
|
ray_hit hit;
|
|
int col = GetIEditor()->GetSystem()->GetIPhysicalWorld()->RayTraceEntity( physics,origin,dir,&hit );
|
|
if (col <= 0)
|
|
return false;
|
|
|
|
// World space distance.
|
|
hc.dist = hit.dist;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// World space distance.
|
|
hc.dist = GetDistance(hc.raySrc, GetWorldTM().TransformPointOLD(pnt));
|
|
return true;
|
|
|
|
/*
|
|
// No physics collision.
|
|
SBrushFace *face = m_brush->Ray( raySrc,rayDir,&dist );
|
|
//SBrushFace *face = m_brush->Ray( hc.raySrc,hc.rayDir,&dist );
|
|
if (face)
|
|
{
|
|
hc.dist = dist;
|
|
return true;
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CBrushObject::HitTestAxis( HitContext &hc )
|
|
{
|
|
//@HACK Temporary hack.
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::SetBrush( SBrush *brush )
|
|
{
|
|
if (m_brush == brush)
|
|
return;
|
|
|
|
/*
|
|
if (m_brush)
|
|
{
|
|
m_indoor->RemoveObject( m_brush->GetIndoorGeom() );
|
|
}
|
|
*/
|
|
|
|
m_brush = brush;
|
|
if (m_brush)
|
|
{
|
|
m_brush->SetMatrix( GetWorldTM() );
|
|
UpdateEngineNode();
|
|
m_bbox = m_brush->m_bounds;
|
|
}
|
|
|
|
// Add brush indoor geometry to indoors.
|
|
//m_indoor->AddObject( m_brush->GetIndoorGeom() );
|
|
}
|
|
|
|
//! Retrieve brush assigned to object.
|
|
SBrush* CBrushObject::GetBrush()
|
|
{
|
|
return m_brush;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Invalidates cached transformation matrix.
|
|
void CBrushObject::InvalidateTM()
|
|
{
|
|
CBaseObject::InvalidateTM();
|
|
|
|
if (m_brush)
|
|
{
|
|
m_brush->SetMatrix( GetWorldTM() );
|
|
}
|
|
if (m_engineNode)
|
|
UpdateEngineNode(true);
|
|
|
|
m_invertTM = GetWorldTM();
|
|
m_invertTM.Invert44();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::WorldToLocalRay( Vec3 &raySrc,Vec3 &rayDir )
|
|
{
|
|
raySrc = m_invertTM.TransformPointOLD( raySrc );
|
|
rayDir = GetNormalized( m_invertTM.TransformVectorOLD( rayDir ) );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::SelectBrushSide( const Vec3 &raySrc,const Vec3 &rayDir,bool shear )
|
|
{
|
|
if (!m_brush)
|
|
return;
|
|
|
|
m_subSelection.Clear();
|
|
|
|
Vec3 rSrc = raySrc;
|
|
Vec3 rDir = rayDir;
|
|
Vec3 rTrg;
|
|
WorldToLocalRay( rSrc,rDir );
|
|
rTrg = rSrc + rDir*32768.0f;
|
|
|
|
m_brush->SelectSide( rSrc,rDir,shear,m_subSelection );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::MoveSelectedPoints( const Vec3 &worldOffset )
|
|
{
|
|
if (!m_brush)
|
|
return;
|
|
|
|
// Store undo.
|
|
StoreUndo( "Stretch Brush" );
|
|
|
|
BBox prevBounds = m_brush->m_bounds;
|
|
|
|
std::vector<Vec3> prevPoints;
|
|
prevPoints.resize( m_subSelection.points.size() );
|
|
|
|
const Matrix44 &tm = GetWorldTM();
|
|
|
|
//CHANGED_BY_IVO
|
|
Vec3 ofs = m_invertTM.TransformVectorOLD( worldOffset );
|
|
|
|
for (int i = 0; i < m_subSelection.points.size(); i++)
|
|
{
|
|
Vec3 pnt = *m_subSelection.points[i];
|
|
prevPoints[i] = pnt; // Remember previous point.
|
|
pnt = m_invertTM.TransformPointOLD(pnt) + ofs;
|
|
*m_subSelection.points[i] = tm.TransformPointOLD(pnt);
|
|
}
|
|
|
|
if (m_brush->BuildSolid(false))
|
|
{
|
|
// Now optimize brush if it correctly created.
|
|
// Now move brush. to make position center of the brush again.
|
|
//Vec3 halfSize = (m_brush->m_bounds.max - m_brush->m_bounds.min)/2;
|
|
Vec3 prevMid = (prevBounds.max + prevBounds.min)/2;
|
|
Vec3 mid = (m_brush->m_bounds.max + m_brush->m_bounds.min)/2;
|
|
Vec3 ofs = mid - prevMid;
|
|
m_brush->Move( -ofs );
|
|
|
|
SetPos( GetPos() + GetWorldTM().TransformVectorOLD(ofs) );
|
|
|
|
m_bbox = m_brush->m_bounds;
|
|
}
|
|
else
|
|
{
|
|
// Restore previous brush.
|
|
for (int i = 0; i < m_subSelection.points.size(); i++)
|
|
{
|
|
*m_subSelection.points[i] = prevPoints[i];
|
|
}
|
|
m_brush->BuildSolid(false);
|
|
CLogFile::WriteLine( "Invalid brush, operation aborted." );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::OnPrefabChange( IVariable *var )
|
|
{
|
|
// Load new prefab model.
|
|
CString objName = mv_prefabName;
|
|
|
|
CreateBrushFromPrefab( objName );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::CreateBrushFromPrefab( const char *meshFilname )
|
|
{
|
|
if (m_prefabGeom)
|
|
{
|
|
if (m_prefabGeom->IsSameObject(meshFilname))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
GetIEditor()->GetErrorReport()->SetCurrentFile( meshFilname );
|
|
m_prefabGeom = CEdMesh::LoadMesh( meshFilname,false );
|
|
GetIEditor()->GetErrorReport()->SetCurrentFile( "" );
|
|
if (m_prefabGeom)
|
|
{
|
|
if (m_brush)
|
|
{
|
|
// If brush already created, assign it with geometry and update IEntityRender
|
|
m_brush->SetPrefabGeom( m_prefabGeom );
|
|
m_brush->ResetToPrefabSize();
|
|
m_bbox = m_brush->m_bounds;
|
|
}
|
|
else
|
|
{
|
|
m_prefabGeom->GetBounds( m_bbox );
|
|
//[Timur] Do not create real brush now.
|
|
// If brush not yet created, make a new brush.
|
|
//SBrush *brush = new SBrush;
|
|
//brush->SetPrefabGeom( m_prefabGeom );
|
|
//SetBrush( brush );
|
|
}
|
|
UpdateEngineNode();
|
|
}
|
|
else if (m_engineNode)
|
|
{
|
|
// Remove this object from engine node.
|
|
m_engineNode->SetEntityStatObj( 0,0,0 );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::ResetToPrefabSize()
|
|
{
|
|
if (m_brush)
|
|
{
|
|
m_brush->ResetToPrefabSize();
|
|
m_bbox = m_brush->m_bounds;
|
|
UpdateEngineNode();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::ReloadPrefabGeometry()
|
|
{
|
|
if (m_prefabGeom)
|
|
{
|
|
m_prefabGeom->ReloadGeometry();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::OnRenderVarChange( IVariable *var )
|
|
{
|
|
UpdateEngineNode();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
IPhysicalEntity* CBrushObject::GetCollisionEntity() const
|
|
{
|
|
// Returns physical object of entity.
|
|
if (m_engineNode)
|
|
return m_engineNode->GetPhysics();
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBrushObject::ConvertFromObject( CBaseObject *object )
|
|
{
|
|
CBaseObject::ConvertFromObject( object );
|
|
if (object->IsKindOf(RUNTIME_CLASS(CEntity)))
|
|
{
|
|
CEntity *entity = (CEntity*)object;
|
|
IEntity *pIEntity = entity->GetIEntity();
|
|
if (!pIEntity)
|
|
return false;
|
|
|
|
IStatObj *prefab = pIEntity->GetIStatObj(0);
|
|
if (!prefab)
|
|
return false;
|
|
|
|
// Copy entity shadow parameters.
|
|
mv_castShadows = entity->IsCastShadow();
|
|
mv_selfShadowing = entity->IsSelfShadowing();
|
|
mv_castShadowMaps = entity->IsCastShadowMaps();
|
|
mv_recvShadowMaps = entity->IsRecvShadowMaps();
|
|
mv_castLightmap = entity->IsCastLightmap();
|
|
mv_recvLightmap = entity->IsRecvLightmap();
|
|
mv_ratioLOD = entity->GetRatioLod();
|
|
mv_ratioViewDist = entity->GetRatioViewDist();
|
|
|
|
mv_prefabName = prefab->GetFileName();
|
|
}
|
|
/*
|
|
if (object->IsKindOf(RUNTIME_CLASS(CStaticObject)))
|
|
{
|
|
CStaticObject *pStatObject = (CStaticObject*)object;
|
|
mv_hideable = pStatObject->IsHideable();
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::UpdateEngineNode( bool bOnlyTransform )
|
|
{
|
|
if (m_bIgnoreNodeUpdate)
|
|
return;
|
|
|
|
if (!m_engineNode)
|
|
return;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Set brush render flags.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
m_renderFlags = 0;
|
|
if (mv_outdoor)
|
|
m_renderFlags |= ERF_OUTDOORONLY;
|
|
if (mv_castShadows)
|
|
m_renderFlags |= ERF_CASTSHADOWVOLUME;
|
|
if (mv_selfShadowing)
|
|
m_renderFlags |= ERF_SELFSHADOW;
|
|
if (mv_castShadowMaps)
|
|
m_renderFlags |= ERF_CASTSHADOWMAPS;
|
|
if (mv_recvShadowMaps)
|
|
m_renderFlags |= ERF_RECVSHADOWMAPS;
|
|
|
|
if (mv_castLightmap)
|
|
m_renderFlags |= ERF_CASTSHADOWINTOLIGHTMAP;
|
|
if (mv_recvLightmap)
|
|
m_renderFlags |= ERF_USELIGHTMAPS;
|
|
if (mv_hideable)
|
|
m_renderFlags |= ERF_HIDABLE;
|
|
if (mv_excludeFromTriangulation)
|
|
m_renderFlags |= ERF_EXCLUDE_FROM_TRIANGULATION;
|
|
|
|
if (IsSelected())
|
|
m_renderFlags |= ERF_SELECTED;
|
|
|
|
int flags = GetRenderFlags();
|
|
m_engineNode->SetRndFlags( m_renderFlags );
|
|
|
|
m_engineNode->SetViewDistRatio( mv_ratioViewDist );
|
|
m_engineNode->SetLodRatio( mv_ratioLOD );
|
|
|
|
if (m_prefabGeom)
|
|
{
|
|
Matrix44 tm = GetBrushMatrix();
|
|
m_engineNode->SetEntityStatObj( 0,m_prefabGeom->GetGeometry(),&tm );
|
|
}
|
|
|
|
// Fast exit if only transformation needs to be changed.
|
|
if (bOnlyTransform)
|
|
return;
|
|
|
|
if (m_pMaterial)
|
|
{
|
|
m_pMaterial->AssignToEntity( m_engineNode );
|
|
}
|
|
else
|
|
{
|
|
// Reset all material settings for this node.
|
|
m_engineNode->SetMaterial(0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
Matrix44 CBrushObject::GetBrushMatrix() const
|
|
{
|
|
if (m_prefabGeom == NULL || m_brush == NULL)
|
|
{
|
|
return GetWorldTM();
|
|
}
|
|
BBox box;
|
|
m_prefabGeom->GetBounds( box );
|
|
Vec3 omin = box.min;
|
|
Vec3 omax = box.max;
|
|
if (omax.x - omin.x == 0)
|
|
omax.x = omin.x + MIN_BOUNDS_SIZE;
|
|
if (omax.y - omin.y == 0)
|
|
omax.y = omin.y + MIN_BOUNDS_SIZE;
|
|
if (omax.z - omin.z == 0)
|
|
omax.z = omin.z + MIN_BOUNDS_SIZE;
|
|
|
|
Matrix44 mov1;
|
|
Matrix44 mov2;
|
|
mov1.SetIdentity();
|
|
mov2.SetIdentity();
|
|
mov1.SetTranslationOLD( -omin );
|
|
mov2.SetTranslationOLD( m_brush->m_bounds.min );
|
|
|
|
Vec3 bb = (m_brush->m_bounds.max - m_brush->m_bounds.min);
|
|
Vec3 ob = (omax - omin);
|
|
Vec3 scl;
|
|
scl.x = bb.x / ob.x;
|
|
scl.y = bb.y / ob.y;
|
|
scl.z = bb.z / ob.z;
|
|
|
|
Matrix44 stm;
|
|
stm.SetIdentity();
|
|
|
|
stm=Matrix33::CreateScale( Vec3d(scl.x,scl.y,scl.z) )*stm;
|
|
|
|
Matrix44 tm = mov1 * stm * mov2;
|
|
tm = tm * GetWorldTM();
|
|
|
|
return tm;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
IStatObj* CBrushObject::GetPrefabGeom() const
|
|
{
|
|
if (!m_prefabGeom)
|
|
return 0;
|
|
return m_prefabGeom->GetGeometry();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::UpdateVisibility( bool visible )
|
|
{
|
|
CBaseObject::UpdateVisibility( visible );
|
|
if (m_engineNode)
|
|
{
|
|
if (!visible)
|
|
m_renderFlags |= ERF_HIDDEN;
|
|
else
|
|
m_renderFlags &= ~ERF_HIDDEN;
|
|
m_engineNode->SetRndFlags( m_renderFlags );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::SetMaterial( CMaterial *mtl )
|
|
{
|
|
StoreUndo( "Assign Material" );
|
|
if (m_prefabGeom)
|
|
m_prefabGeom->SetMaterial( mtl );
|
|
m_pMaterial = mtl;
|
|
if (m_pMaterial)
|
|
{
|
|
m_pMaterial->SetUsed();
|
|
m_materialGUID = m_pMaterial->GetGUID();
|
|
}
|
|
else
|
|
{
|
|
ZeroStruct(m_materialGUID);
|
|
}
|
|
if (m_engineNode)
|
|
UpdateEngineNode();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CMaterial* CBrushObject::GetMaterial() const
|
|
{
|
|
/*
|
|
if (!m_pMaterial)
|
|
{
|
|
if (m_prefabGeom != NULL && m_prefabGeom->GetMaterial())
|
|
return m_prefabGeom->GetMaterial();
|
|
}
|
|
*/
|
|
return m_pMaterial;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBrushObject::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 Brush %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_prefabGeom)
|
|
{
|
|
CString file = mv_prefabName;
|
|
CErrorRecord err;
|
|
err.error.Format( "No Geometry for Brush %s",(const char*)file,(const char*)GetName() );
|
|
err.file = file;
|
|
err.pObject = this;
|
|
report->ReportError(err);
|
|
}
|
|
else if (m_prefabGeom->IsDefaultObject())
|
|
{
|
|
CString file = mv_prefabName;
|
|
CErrorRecord err;
|
|
err.error.Format( "Geometry file %s for Brush %s Failed to Load",(const char*)file,(const char*)GetName() );
|
|
err.file = file;
|
|
err.pObject = this;
|
|
report->ReportError(err);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBrushObject::IsSimilarObject( CBaseObject *pObject )
|
|
{
|
|
if (pObject->GetClassDesc() == GetClassDesc() && GetRuntimeClass() == pObject->GetRuntimeClass())
|
|
{
|
|
CBrushObject *pBrush = (CBrushObject*)pObject;
|
|
if ((CString)mv_prefabName == (CString)pBrush->mv_prefabName)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|