1218 lines
29 KiB
C++
1218 lines
29 KiB
C++
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Crytek Engine Source File.
|
|
// Copyright (C), Crytek Studios, 2001.
|
|
// -------------------------------------------------------------------------
|
|
// File name: ShapeObject.cpp
|
|
// Version: v1.00
|
|
// Created: 10/10/2001 by Timur.
|
|
// Compilers: Visual C++ 6.0
|
|
// Description: CShapeObject implementation.
|
|
// -------------------------------------------------------------------------
|
|
// History:
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "StdAfx.h"
|
|
#include "ShapeObject.h"
|
|
#include "..\ShapePanel.h"
|
|
#include "..\Viewport.h"
|
|
#include "Util\Triangulate.h"
|
|
|
|
#include <I3DEngine.h>
|
|
#include <IAISystem.h>
|
|
#include <IGame.h>
|
|
#include <IMarkers.h>
|
|
|
|
#include <vector>
|
|
#include "IEntitySystem.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CBase implementation.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
IMPLEMENT_DYNCREATE(CShapeObject,CBaseObject)
|
|
IMPLEMENT_DYNCREATE(CAIForbiddenAreaObject,CShapeObject)
|
|
IMPLEMENT_DYNCREATE(CAIPathObject,CShapeObject)
|
|
IMPLEMENT_DYNCREATE(CAINavigationModifierObject,CShapeObject)
|
|
IMPLEMENT_DYNCREATE(CAIOcclusionPlaneObject,CShapeObject)
|
|
|
|
#define RAY_DISTANCE 100000.0f
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CShapeObject::m_rollupId = 0;
|
|
CShapePanel* CShapeObject::m_panel = 0;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CShapeObject::CShapeObject()
|
|
{
|
|
m_bForce2D = false;
|
|
mv_closed = false;
|
|
mv_areaId = 0;
|
|
mv_groupId = 0;
|
|
mv_width = 0;
|
|
mv_height = 0;
|
|
mv_displayFilled = false;
|
|
|
|
m_bbox.min = m_bbox.max = Vec3(0,0,0);
|
|
m_selectedPoint = -1;
|
|
m_lowestHeight = 0;
|
|
m_IArea = 0;
|
|
m_bIgnoreGameUpdate = true;
|
|
m_bAreaModified = true;
|
|
m_bDisplayFilledWhenSelected = false;
|
|
|
|
AddVariable( mv_width,"Width",functor(*this,&CShapeObject::OnShapeChange) );
|
|
AddVariable( mv_height,"Height",functor(*this,&CShapeObject::OnShapeChange) );
|
|
AddVariable( mv_areaId,"AreaId",functor(*this,&CShapeObject::OnShapeChange) );
|
|
AddVariable( mv_groupId,"GroupId",functor(*this,&CShapeObject::OnShapeChange) );
|
|
AddVariable( mv_closed,"Closed",functor(*this,&CShapeObject::OnShapeChange) );
|
|
AddVariable( mv_displayFilled,"DisplayFilled" );
|
|
|
|
SetColor( Vec2Rgb(Vec3(0,0.8f,1)) );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::Done()
|
|
{
|
|
m_entities.Clear();
|
|
m_points.clear();
|
|
UpdateGameArea(true);
|
|
CBaseObject::Done();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CShapeObject::Init( IEditor *ie,CBaseObject *prev,const CString &file )
|
|
{
|
|
m_ie = ie;
|
|
|
|
m_bIgnoreGameUpdate = true;
|
|
|
|
bool res = CBaseObject::Init( ie,prev,file );
|
|
|
|
if (prev)
|
|
{
|
|
m_points = ((CShapeObject*)prev)->m_points;
|
|
m_bIgnoreGameUpdate = false;
|
|
CalcBBox();
|
|
}
|
|
|
|
m_bIgnoreGameUpdate = false;
|
|
|
|
return res;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CShapeObject::CreateGameObject()
|
|
{
|
|
return true;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::SetName( const CString &name )
|
|
{
|
|
CBaseObject::SetName( name );
|
|
m_bAreaModified = true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::InvalidateTM()
|
|
{
|
|
CBaseObject::InvalidateTM();
|
|
m_bAreaModified = true;
|
|
// CalcBBox();
|
|
|
|
if (!IsOnlyUpdateOnUnselect() && !m_bIgnoreGameUpdate)
|
|
UpdateGameArea();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::GetBoundBox( BBox &box )
|
|
{
|
|
box = m_bbox;
|
|
box.Transform( GetWorldTM() );
|
|
float s = 1.0f;
|
|
box.min -= Vec3(s,s,s);
|
|
box.max += Vec3(s,s,s);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::GetLocalBounds( BBox &box )
|
|
{
|
|
box = m_bbox;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CShapeObject::HitTest( HitContext &hc )
|
|
{
|
|
// First check if ray intersect our bounding box.
|
|
float tr = hc.distanceTollerance/2 + SHAPE_CLOSE_DISTANCE;
|
|
|
|
// Find intersection of line with zero Z plane.
|
|
float minDist = FLT_MAX;
|
|
Vec3 intPnt;
|
|
//GetNearestEdge( hc.raySrc,hc.rayDir,p1,p2,minDist,intPnt );
|
|
|
|
bool bWasIntersection = false;
|
|
Vec3 ip;
|
|
Vec3 rayLineP1 = hc.raySrc;
|
|
Vec3 rayLineP2 = hc.raySrc + hc.rayDir*RAY_DISTANCE;
|
|
const Matrix44 &wtm = GetWorldTM();
|
|
|
|
for (int i = 0; i < m_points.size(); i++)
|
|
{
|
|
int j = (i < m_points.size()-1) ? i+1 : 0;
|
|
|
|
if (!mv_closed && j == 0 && i != 0)
|
|
continue;
|
|
|
|
Vec3 pi = wtm.TransformPointOLD(m_points[i]);
|
|
Vec3 pj = wtm.TransformPointOLD(m_points[j]);
|
|
|
|
float d = 0;
|
|
if (RayToLineDistance( rayLineP1,rayLineP2,pi,pj,d,ip ))
|
|
{
|
|
if (d < minDist)
|
|
{
|
|
bWasIntersection = true;
|
|
minDist = d;
|
|
intPnt = ip;
|
|
}
|
|
}
|
|
|
|
if (mv_height > 0)
|
|
{
|
|
if (RayToLineDistance( rayLineP1,rayLineP2,pi+Vec3(0,0,mv_height),pj+Vec3(0,0,mv_height),d,intPnt ))
|
|
{
|
|
if (d < minDist)
|
|
{
|
|
bWasIntersection = true;
|
|
minDist = d;
|
|
intPnt = ip;
|
|
}
|
|
}
|
|
if (RayToLineDistance( rayLineP1,rayLineP2,pi,pi+Vec3(0,0,mv_height),d,intPnt ))
|
|
{
|
|
if (d < minDist)
|
|
{
|
|
bWasIntersection = true;
|
|
minDist = d;
|
|
intPnt = ip;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
float fShapeCloseDistance = SHAPE_CLOSE_DISTANCE*hc.view->GetScreenScaleFactor(intPnt) * 0.01f;
|
|
|
|
if (bWasIntersection && minDist < fShapeCloseDistance+hc.distanceTollerance)
|
|
{
|
|
hc.dist = GetDistance(hc.raySrc,intPnt);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::BeginEditParams( IEditor *ie,int flags )
|
|
{
|
|
m_ie = ie;
|
|
CBaseObject::BeginEditParams( ie,flags );
|
|
|
|
if (!m_panel)
|
|
{
|
|
m_panel = new CShapePanel;
|
|
m_rollupId = ie->AddRollUpPage( ROLLUP_OBJECTS,"Shape Parameters",m_panel );
|
|
}
|
|
if (m_panel)
|
|
m_panel->SetShape( this );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::EndEditParams( IEditor *ie )
|
|
{
|
|
if (m_rollupId != 0)
|
|
{
|
|
ie->RemoveRollUpPage( ROLLUP_OBJECTS,m_rollupId );
|
|
CalcBBox();
|
|
}
|
|
m_rollupId = 0;
|
|
m_panel = 0;
|
|
|
|
CBaseObject::EndEditParams( ie );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CShapeObject::MouseCreateCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags )
|
|
{
|
|
if (event == eMouseMove || event == eMouseLDown || event == eMouseLDblClick)
|
|
{
|
|
Vec3 pos = view->MapViewToCP(point);
|
|
|
|
bool firstTime = false;
|
|
if (m_points.size() < 2)
|
|
{
|
|
SetPos( pos );
|
|
}
|
|
|
|
pos.z += SHAPE_Z_OFFSET;
|
|
|
|
if (m_points.size() == 0)
|
|
{
|
|
InsertPoint( -1,Vec3(0,0,0) );
|
|
firstTime = true;
|
|
}
|
|
else
|
|
{
|
|
SetPoint( m_points.size()-1,pos - GetWorldPos() );
|
|
}
|
|
|
|
if (event == eMouseLDblClick)
|
|
{
|
|
if (m_points.size() > 3)
|
|
{
|
|
m_points.pop_back(); // Remove last uneeded point.
|
|
EndCreation();
|
|
return MOUSECREATE_OK;
|
|
}
|
|
else
|
|
return MOUSECREATE_ABORT;
|
|
}
|
|
|
|
if (event == eMouseLDown)
|
|
{
|
|
Vec3 vlen = Vec3(pos.x,pos.y,0) - Vec3(GetWorldPos().x,GetWorldPos().y,0);
|
|
if (m_points.size() > 2 && vlen.Length() < SHAPE_CLOSE_DISTANCE)
|
|
{
|
|
EndCreation();
|
|
return MOUSECREATE_OK;
|
|
}
|
|
if (GetPointCount() >= GetMaxPoints())
|
|
{
|
|
//m_points.pop_back(); // Remove last uneeded point.
|
|
EndCreation();
|
|
return MOUSECREATE_OK;
|
|
}
|
|
|
|
InsertPoint( -1,pos-GetWorldPos() );
|
|
}
|
|
return MOUSECREATE_CONTINUE;
|
|
}
|
|
return CBaseObject::MouseCreateCallback( view,event,point,flags );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::EndCreation()
|
|
{
|
|
SetClosed(true);
|
|
if (m_panel)
|
|
m_panel->SetShape(this);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::Display( DisplayContext &dc )
|
|
{
|
|
//dc.renderer->EnableDepthTest(false);
|
|
|
|
const Matrix44 &wtm = GetWorldTM();
|
|
COLORREF col;
|
|
|
|
float fPointSize = 0.5f;
|
|
|
|
bool bPointSelected = false;
|
|
if (m_selectedPoint >= 0 && m_selectedPoint < m_points.size())
|
|
{
|
|
bPointSelected = true;
|
|
}
|
|
|
|
if (m_points.size() > 1)
|
|
{
|
|
if ((IsSelected() || IsHighlighted()) && !bPointSelected)
|
|
{
|
|
col = dc.GetSelectedColor();
|
|
dc.SetColor( col );
|
|
}
|
|
else
|
|
{
|
|
if (IsFrozen())
|
|
dc.SetFreezeColor();
|
|
else
|
|
dc.SetColor( GetColor() );
|
|
col = GetColor();
|
|
}
|
|
|
|
for (int i = 0; i < m_points.size(); i++)
|
|
{
|
|
int j = (i < m_points.size()-1) ? i+1 : 0;
|
|
|
|
Vec3 p0 = wtm.TransformPointOLD(m_points[i]);
|
|
//dc.renderer->DrawBall( p0,0.8f );
|
|
|
|
float fScale = fPointSize*dc.view->GetScreenScaleFactor(p0) * 0.01f;
|
|
Vec3 sz(fScale,fScale,fScale);
|
|
dc.DrawWireBox( p0-sz,p0+sz );
|
|
|
|
if (!mv_closed && j == 0 && i != 0)
|
|
continue;
|
|
|
|
Vec3 p1 = wtm.TransformPointOLD(m_points[j]);
|
|
dc.DrawLine( p0,p1 );
|
|
//DrawTerrainLine( dc,pos+m_points[i],pos+m_points[j] );
|
|
|
|
if (mv_height != 0)
|
|
{
|
|
BBox box = m_bbox;
|
|
box.Transform( GetWorldTM() );
|
|
m_lowestHeight = box.min.z;
|
|
// Draw second capping shape from above.
|
|
float z = m_lowestHeight + mv_height;
|
|
dc.DrawLine( p0,Vec3(p0.x,p0.y,z) );
|
|
dc.DrawLine( Vec3(p0.x,p0.y,z),Vec3(p1.x,p1.y,z) );
|
|
}
|
|
}
|
|
|
|
// Draw selected point.
|
|
if (bPointSelected)
|
|
{
|
|
dc.SetSelectedColor( 0.5f );
|
|
|
|
Vec3 selPos = wtm.TransformPointOLD(m_points[m_selectedPoint]);
|
|
//dc.renderer->DrawBall( selPos,1.0f );
|
|
float fScale = fPointSize*dc.view->GetScreenScaleFactor(selPos) * 0.01f;
|
|
Vec3 sz(fScale,fScale,fScale);
|
|
dc.DrawWireBox( selPos-sz,selPos+sz );
|
|
|
|
DrawAxis( dc,m_points[m_selectedPoint],0.15f );
|
|
}
|
|
}
|
|
|
|
if (!m_entities.IsEmpty())
|
|
{
|
|
Vec3 vcol = Rgb2Vec(col);
|
|
int num = m_entities.GetCount();
|
|
for (int i = 0; i < num; i++)
|
|
{
|
|
CBaseObject *obj = m_entities.Get(i);
|
|
if (!obj)
|
|
continue;
|
|
int p1,p2;
|
|
float dist;
|
|
Vec3 intPnt;
|
|
GetNearestEdge( obj->GetWorldPos(),p1,p2,dist,intPnt );
|
|
dc.DrawLine( intPnt,obj->GetWorldPos(),CFColor(vcol.x,vcol.y,vcol.z,0.7f),CFColor(1,1,1,0.7f) );
|
|
}
|
|
}
|
|
|
|
if (mv_closed && !IsFrozen())
|
|
{
|
|
if (mv_displayFilled || (IsSelected() && m_bDisplayFilledWhenSelected) || IsHighlighted())
|
|
{
|
|
if (IsHighlighted())
|
|
dc.SetColor( GetColor(),0.1f );
|
|
else
|
|
dc.SetColor( GetColor(),0.3f );
|
|
static std::vector<Vec3> tris;
|
|
tris.resize(0);
|
|
tris.reserve( m_points.size()*3 );
|
|
if (CTriangulate::Process( m_points,tris ))
|
|
{
|
|
for (int i = 0; i < tris.size(); i += 3)
|
|
{
|
|
dc.DrawTri( wtm.TransformPointOLD(tris[i]),wtm.TransformPointOLD(tris[i+1]),wtm.TransformPointOLD(tris[i+2]) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//dc.renderer->EnableDepthTest(true);
|
|
|
|
DrawDefault(dc,GetColor());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::DrawTerrainLine( DisplayContext &dc,const Vec3 &p1,const Vec3 &p2 )
|
|
{
|
|
float len = (p2-p1).Length();
|
|
int steps = len/2;
|
|
if (steps <= 1)
|
|
{
|
|
dc.DrawLine( p1,p2 );
|
|
return;
|
|
}
|
|
Vec3 pos1 = p1;
|
|
Vec3 pos2 = p1;
|
|
for (int i = 0; i < steps-1; i++)
|
|
{
|
|
pos2 = p1 + (1.0f/i)*(p2-p1);
|
|
pos2.z = dc.engine->GetTerrainElevation(pos2.x,pos2.y);
|
|
dc.SetColor( i*2,0,0,1 );
|
|
dc.DrawLine( pos1,pos2 );
|
|
pos1 = pos2;
|
|
}
|
|
//dc.DrawLine( pos2,p2 );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::Serialize( CObjectArchive &ar )
|
|
{
|
|
m_bIgnoreGameUpdate = true;
|
|
CBaseObject::Serialize( ar );
|
|
m_bIgnoreGameUpdate = false;
|
|
|
|
XmlNodeRef xmlNode = ar.node;
|
|
if (ar.bLoading)
|
|
{
|
|
m_bAreaModified = true;
|
|
m_bIgnoreGameUpdate = true;
|
|
m_entities.Clear();
|
|
|
|
GUID entityId;
|
|
if (xmlNode->getAttr( "EntityId",entityId ))
|
|
{
|
|
// For Backward compatability.
|
|
//m_entities.push_back(entityId);
|
|
ar.SetResolveCallback( this,entityId,functor(m_entities,&CSafeObjectsArray::Add) );
|
|
}
|
|
|
|
// Load Entities.
|
|
m_points.clear();
|
|
XmlNodeRef points = xmlNode->findChild( "Points" );
|
|
if (points)
|
|
{
|
|
for (int i = 0; i < points->getChildCount(); i++)
|
|
{
|
|
XmlNodeRef pnt = points->getChild(i);
|
|
Vec3 pos;
|
|
pnt->getAttr( "Pos",pos );
|
|
m_points.push_back(pos);
|
|
}
|
|
}
|
|
XmlNodeRef entities = xmlNode->findChild( "Entities" );
|
|
if (entities)
|
|
{
|
|
for (int i = 0; i < entities->getChildCount(); i++)
|
|
{
|
|
XmlNodeRef ent = entities->getChild(i);
|
|
GUID entityId;
|
|
if (ent->getAttr( "Id",entityId ))
|
|
{
|
|
//m_entities.push_back(id);
|
|
ar.SetResolveCallback( this,entityId,functor(m_entities,&CSafeObjectsArray::Add) );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ar.bUndo)
|
|
{
|
|
// Update game area only in undo mode.
|
|
m_bIgnoreGameUpdate = false;
|
|
}
|
|
CalcBBox();
|
|
UpdateGameArea();
|
|
|
|
// Update UI.
|
|
if (m_panel && m_panel->GetShape() == this)
|
|
m_panel->SetShape(this);
|
|
}
|
|
else
|
|
{
|
|
// Saving.
|
|
// Save Points.
|
|
if (!m_points.empty())
|
|
{
|
|
XmlNodeRef points = xmlNode->newChild( "Points" );
|
|
for (int i = 0; i < m_points.size(); i++)
|
|
{
|
|
XmlNodeRef pnt = points->newChild( "Point" );
|
|
pnt->setAttr( "Pos",m_points[i] );
|
|
}
|
|
}
|
|
// Save Entities.
|
|
if (!m_entities.IsEmpty())
|
|
{
|
|
XmlNodeRef nodes = xmlNode->newChild( "Entities" );
|
|
for (int i = 0; i < m_entities.GetCount(); i++)
|
|
{
|
|
XmlNodeRef entNode = nodes->newChild( "Entity" );
|
|
if (m_entities.Get(i))
|
|
entNode->setAttr( "Id",m_entities.Get(i)->GetId() );
|
|
}
|
|
}
|
|
}
|
|
m_bIgnoreGameUpdate = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
XmlNodeRef CShapeObject::Export( const CString &levelPath,XmlNodeRef &xmlNode )
|
|
{
|
|
XmlNodeRef objNode = CBaseObject::Export( levelPath,xmlNode );
|
|
|
|
// Export position in world space.
|
|
Matrix44 wtmNoScale = GetWorldTM();
|
|
wtmNoScale.NoScale();
|
|
|
|
//QUAT_CHANGED_BY_IVO
|
|
//Quat q(wtmNoScale);
|
|
//Quat q = CovertMatToQuat<float>( GetTransposed44(wtmNoScale) );
|
|
Quat q = Quat( GetTransposed44(wtmNoScale) );
|
|
|
|
Vec3 worldPos = wtmNoScale.GetTranslationOLD();
|
|
Vec3 worldAngles = Ang3::GetAnglesXYZ(Matrix33(q)) * 180.0f/gf_PI;
|
|
|
|
//if (worldPos != Vec3(0,0,0))
|
|
if (!IsEquivalent(worldPos,Vec3(0,0,0)))
|
|
objNode->setAttr( "Pos",worldPos );
|
|
|
|
//if (worldAngles != Vec3(0,0,0))
|
|
if (!IsEquivalent(worldAngles,Vec3(0,0,0),VEC_EPSILON))
|
|
objNode->setAttr( "Angles",worldAngles );
|
|
|
|
const Matrix44 &wtm = GetWorldTM();
|
|
// Export Points
|
|
if (!m_points.empty())
|
|
{
|
|
XmlNodeRef points = objNode->newChild( "Points" );
|
|
for (int i = 0; i < m_points.size(); i++)
|
|
{
|
|
XmlNodeRef pnt = points->newChild( "Point" );
|
|
pnt->setAttr( "Pos",wtm.TransformPointOLD(m_points[i]) );
|
|
}
|
|
}
|
|
|
|
// Export Entities.
|
|
if (!m_entities.IsEmpty())
|
|
{
|
|
XmlNodeRef nodes = objNode->newChild( "Entities" );
|
|
for (int i = 0; i < m_entities.GetCount(); i++)
|
|
{
|
|
XmlNodeRef entNode = nodes->newChild( "Entity" );
|
|
CBaseObject *obj = m_entities.Get(i);
|
|
CString name;
|
|
if (obj)
|
|
name = obj->GetName();
|
|
entNode->setAttr( "Name",name );
|
|
}
|
|
}
|
|
|
|
return objNode;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::CalcBBox()
|
|
{
|
|
if (m_points.empty())
|
|
return;
|
|
|
|
// Reposition shape, so that shape object position is in the middle of the shape.
|
|
Vec3 center = m_points[0];
|
|
if (center.x != 0 || center.y != 0 || center.z != 0)
|
|
{
|
|
// May not work correctly if shape is transformed.
|
|
for (int i = 0; i < m_points.size(); i++)
|
|
{
|
|
m_points[i] -= center;
|
|
}
|
|
const Matrix44 <m = GetLocalTM();
|
|
SetPos( GetPos() + ltm.TransformVectorOLD(center) );
|
|
}
|
|
|
|
// First point must always be 0,0,0.
|
|
m_bbox.Reset();
|
|
for (int i = 0; i < m_points.size(); i++)
|
|
{
|
|
m_bbox.Add( m_points[i] );
|
|
}
|
|
if (m_bbox.min.x > m_bbox.max.x)
|
|
{
|
|
m_bbox.min = m_bbox.max = Vec3(0,0,0);
|
|
}
|
|
BBox box = m_bbox;
|
|
box.Transform( GetWorldTM() );
|
|
m_lowestHeight = box.min.z;
|
|
|
|
m_bbox.max.z = max( m_bbox.max.z,mv_height );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::SetSelected( bool bSelect )
|
|
{
|
|
bool bWasSelected = IsSelected();
|
|
CBaseObject::SetSelected( bSelect );
|
|
if (!bSelect && bWasSelected)
|
|
{
|
|
// When unselecting shape, update area in game.
|
|
if (m_bAreaModified)
|
|
UpdateGameArea();
|
|
m_bAreaModified = false;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CShapeObject::InsertPoint( int index,const Vec3 &point )
|
|
{
|
|
if (GetPointCount() >= GetMaxPoints())
|
|
return GetPointCount()-1;
|
|
int newindex;
|
|
StoreUndo( "Insert Point" );
|
|
|
|
m_bAreaModified = true;
|
|
|
|
if (index < 0 || index >= m_points.size())
|
|
{
|
|
m_points.push_back( point );
|
|
newindex = m_points.size()-1;
|
|
}
|
|
else
|
|
{
|
|
m_points.insert( m_points.begin()+index,point );
|
|
newindex = index;
|
|
}
|
|
SetPoint( newindex,point );
|
|
CalcBBox();
|
|
return newindex;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::RemovePoint( int index )
|
|
{
|
|
if ((index >= 0 || index < m_points.size()) && m_points.size() > 3)
|
|
{
|
|
m_bAreaModified = true;
|
|
StoreUndo( "Remove Point" );
|
|
m_points.erase( m_points.begin()+index );
|
|
CalcBBox();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::PostClone( CBaseObject *pFromObject,CObjectCloneContext &ctx )
|
|
{
|
|
CBaseObject::PostClone( pFromObject,ctx );
|
|
|
|
CShapeObject *pFromShape = (CShapeObject*)pFromObject;
|
|
// Clone event targets.
|
|
if (!pFromShape->m_entities.IsEmpty())
|
|
{
|
|
int numEntities = pFromShape->m_entities.GetCount();
|
|
for (int i = 0; i < numEntities; i++)
|
|
{
|
|
CBaseObject *pTarget = pFromShape->m_entities.Get(i);
|
|
CBaseObject *pClonedTarget = ctx.FindClone( pTarget );
|
|
if (!pClonedTarget)
|
|
pClonedTarget = pTarget; // If target not cloned, link to original target.
|
|
|
|
// Add cloned event.
|
|
if (pClonedTarget)
|
|
AddEntity( pClonedTarget );
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::AddEntity( CBaseObject *entity )
|
|
{
|
|
assert( entity );
|
|
|
|
m_bAreaModified = true;
|
|
|
|
StoreUndo( "Add Entity" );
|
|
m_entities.Add( entity );
|
|
UpdateGameArea();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::RemoveEntity( int index )
|
|
{
|
|
assert( index >= 0 && index < m_entities.GetCount() );
|
|
StoreUndo( "Remove Entity" );
|
|
|
|
m_bAreaModified = true;
|
|
|
|
if (index < m_entities.GetCount())
|
|
m_entities.Remove( m_entities.Get(index) );
|
|
UpdateGameArea();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBaseObject* CShapeObject::GetEntity( int index )
|
|
{
|
|
assert( index >= 0 && index < m_entities.GetCount() );
|
|
return m_entities.Get(index);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::SetClosed( bool bClosed )
|
|
{
|
|
StoreUndo( "Set Closed" );
|
|
mv_closed = bClosed;
|
|
|
|
m_bAreaModified = true;
|
|
|
|
if (m_IArea && !mv_closed)
|
|
{
|
|
GetIEditor()->GetGame()->GetAreaManager()->DeleteArea( m_IArea );
|
|
m_IArea = 0;
|
|
}
|
|
if (!m_IArea && mv_closed)
|
|
{
|
|
UpdateGameArea();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CShapeObject::GetAreaId()
|
|
{
|
|
return mv_areaId;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::SelectPoint( int index )
|
|
{
|
|
m_selectedPoint = index;
|
|
}
|
|
|
|
void CShapeObject::SetPoint( int index,const Vec3 &pos )
|
|
{
|
|
Vec3 p = pos;
|
|
if (m_bForce2D)
|
|
{
|
|
if (!m_points.empty())
|
|
p.z = m_points[0].z; // Keep original Z.
|
|
}
|
|
if (index >= 0 && index < m_points.size())
|
|
{
|
|
m_points[index] = p;
|
|
}
|
|
m_bAreaModified = true;
|
|
if (!IsOnlyUpdateOnUnselect())
|
|
UpdateGameArea();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CShapeObject::GetNearestPoint( const Vec3 &raySrc,const Vec3 &rayDir,float &distance )
|
|
{
|
|
int index = -1;
|
|
float minDist = FLT_MAX;
|
|
Vec3 rayLineP1 = raySrc;
|
|
Vec3 rayLineP2 = raySrc + rayDir*RAY_DISTANCE;
|
|
Vec3 intPnt;
|
|
const Matrix44 &wtm = GetWorldTM();
|
|
for (int i = 0; i < m_points.size(); i++)
|
|
{
|
|
float d = PointToLineDistance( rayLineP1,rayLineP2,wtm.TransformPointOLD(m_points[i]),intPnt );
|
|
if (d < minDist)
|
|
{
|
|
minDist = d;
|
|
index = i;
|
|
}
|
|
}
|
|
distance = minDist;
|
|
return index;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::GetNearestEdge( const Vec3 &pos,int &p1,int &p2,float &distance,Vec3 &intersectPoint )
|
|
{
|
|
p1 = -1;
|
|
p2 = -1;
|
|
float minDist = FLT_MAX;
|
|
Vec3 intPnt;
|
|
|
|
const Matrix44 &wtm = GetWorldTM();
|
|
|
|
for (int i = 0; i < m_points.size(); i++)
|
|
{
|
|
int j = (i < m_points.size()-1) ? i+1 : 0;
|
|
|
|
if (!mv_closed && j == 0 && i != 0)
|
|
continue;
|
|
|
|
float d = PointToLineDistance( wtm.TransformPointOLD(m_points[i]),wtm.TransformPointOLD(m_points[j]),pos,intPnt );
|
|
if (d < minDist)
|
|
{
|
|
minDist = d;
|
|
p1 = i;
|
|
p2 = j;
|
|
intersectPoint = intPnt;
|
|
}
|
|
}
|
|
distance = minDist;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CShapeObject::RayToLineDistance( const Vec3 &rayLineP1,const Vec3 &rayLineP2,const Vec3 &pi,const Vec3 &pj,float &distance,Vec3 &intPnt )
|
|
{
|
|
Vec3 pa,pb;
|
|
float ua,ub;
|
|
if (!LineLineIntersect( pi,pj, rayLineP1,rayLineP2, pa,pb,ua,ub ))
|
|
return false;
|
|
|
|
// If before ray origin.
|
|
if (ub < 0)
|
|
return false;
|
|
|
|
float d = 0;
|
|
if (ua < 0)
|
|
d = PointToLineDistance( rayLineP1,rayLineP2,pi,intPnt );
|
|
else if (ua > 1)
|
|
d = PointToLineDistance( rayLineP1,rayLineP2,pj,intPnt );
|
|
else
|
|
{
|
|
intPnt = rayLineP1 + ub*(rayLineP2-rayLineP1);
|
|
d = (pb-pa).Length();
|
|
}
|
|
distance = d;
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::GetNearestEdge( const Vec3 &raySrc,const Vec3 &rayDir,int &p1,int &p2,float &distance,Vec3 &intersectPoint )
|
|
{
|
|
p1 = -1;
|
|
p2 = -1;
|
|
float minDist = FLT_MAX;
|
|
Vec3 intPnt;
|
|
Vec3 rayLineP1 = raySrc;
|
|
Vec3 rayLineP2 = raySrc + rayDir*RAY_DISTANCE;
|
|
|
|
const Matrix44 &wtm = GetWorldTM();
|
|
|
|
for (int i = 0; i < m_points.size(); i++)
|
|
{
|
|
int j = (i < m_points.size()-1) ? i+1 : 0;
|
|
|
|
if (!mv_closed && j == 0 && i != 0)
|
|
continue;
|
|
|
|
Vec3 pi = wtm.TransformPointOLD(m_points[i]);
|
|
Vec3 pj = wtm.TransformPointOLD(m_points[j]);
|
|
|
|
float d = 0;
|
|
if (!RayToLineDistance( rayLineP1,rayLineP2,pi,pj,d,intPnt ))
|
|
continue;
|
|
|
|
if (d < minDist)
|
|
{
|
|
minDist = d;
|
|
p1 = i;
|
|
p2 = j;
|
|
intersectPoint = intPnt;
|
|
}
|
|
}
|
|
distance = minDist;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::UpdateGameArea( bool bRemove )
|
|
{
|
|
if (m_bIgnoreGameUpdate)
|
|
return;
|
|
|
|
if (!IsCreateGameObjects())
|
|
return;
|
|
|
|
if (bRemove)
|
|
{
|
|
if (m_IArea)
|
|
{
|
|
GetIEditor()->GetGame()->GetAreaManager()->DeleteArea( m_IArea );
|
|
m_IArea = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (m_IArea && !mv_closed)
|
|
{
|
|
GetIEditor()->GetGame()->GetAreaManager()->DeleteArea( m_IArea );
|
|
m_IArea = 0;
|
|
}
|
|
|
|
if (!mv_closed)
|
|
return;
|
|
|
|
const Matrix44 &wtm = GetWorldTM();
|
|
|
|
BBox box = m_bbox;
|
|
box.Transform(wtm);
|
|
m_lowestHeight = box.min.z;
|
|
|
|
std::vector<Vec3> points;
|
|
if (GetPointCount() > 0)
|
|
{
|
|
points.resize(GetPointCount());
|
|
for (int i = 0; i < GetPointCount(); i++)
|
|
{
|
|
points[i] = wtm.TransformPointOLD( GetPoint(i) );
|
|
}
|
|
}
|
|
|
|
if (m_IArea)
|
|
{
|
|
m_IArea->SetPoints( &points[0],points.size() );
|
|
}
|
|
else if (GetPointCount() > 2)
|
|
{
|
|
std::vector<string> entitiesName;
|
|
entitiesName.clear();
|
|
|
|
m_IArea = GetIEditor()->GetGame()->GetAreaManager()->CreateArea( &points[0], points.size(), entitiesName, mv_areaId, mv_groupId, mv_width );
|
|
}
|
|
|
|
if (m_IArea)
|
|
{
|
|
m_IArea->SetID(mv_areaId);
|
|
m_IArea->SetProximity(mv_width);
|
|
m_IArea->SetVSize(mv_height);
|
|
m_IArea->SetVOrigin(m_lowestHeight);
|
|
m_IArea->SetGroup(mv_groupId);
|
|
|
|
m_IArea->ClearEntities();
|
|
for (int i = 0; i < GetEntityCount(); i++)
|
|
{
|
|
CBaseObject *obj = GetEntity(i);
|
|
if (obj)
|
|
m_IArea->AddEntity( obj->GetName() );
|
|
}
|
|
}
|
|
m_bAreaModified = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::OnShapeChange( IVariable *var )
|
|
{
|
|
m_bAreaModified = true;
|
|
if (!m_bIgnoreGameUpdate)
|
|
UpdateGameArea();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CShapeObject::OnEvent( ObjectEvent event )
|
|
{
|
|
if (event == EVENT_AFTER_LOAD)
|
|
{
|
|
// After loading Update game structure.
|
|
UpdateGameArea();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CShapeObject::HitTestRect( HitContext &hc )
|
|
{
|
|
BBox box;
|
|
// Retrieve world space bound box.
|
|
GetBoundBox( box );
|
|
|
|
// Project all edges to viewport.
|
|
const Matrix44 &wtm = GetWorldTM();
|
|
for (int i = 0; i < m_points.size(); i++)
|
|
{
|
|
int j = (i < m_points.size()-1) ? i+1 : 0;
|
|
if (!mv_closed && j == 0 && i != 0)
|
|
continue;
|
|
|
|
Vec3 p0 = wtm.TransformPointOLD(m_points[i]);
|
|
Vec3 p1 = wtm.TransformPointOLD(m_points[j]);
|
|
|
|
CPoint pnt0 = hc.view->WorldToView( p0 );
|
|
CPoint pnt1 = hc.view->WorldToView( p1 );
|
|
|
|
// See if pnt0 to pnt1 line intersects with rectangle.
|
|
// check see if one point is inside rect and other outside, or both inside.
|
|
bool in0 = hc.rect.PtInRect(pnt0);
|
|
bool in1 = hc.rect.PtInRect(pnt0);
|
|
if ((in0 && in1) || (in0 && !in1) || (!in0 && in1))
|
|
{
|
|
hc.object = this;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
/*
|
|
|
|
// transform all 8 vertices into world space
|
|
CPoint p[8] =
|
|
{
|
|
hc.view->WorldToView(Vec3d(box.min.x,box.min.y,box.min.z)),
|
|
hc.view->WorldToView(Vec3d(box.min.x,box.max.y,box.min.z)),
|
|
hc.view->WorldToView(Vec3d(box.max.x,box.min.y,box.min.z)),
|
|
hc.view->WorldToView(Vec3d(box.max.x,box.max.y,box.min.z)),
|
|
hc.view->WorldToView(Vec3d(box.min.x,box.min.y,box.max.z)),
|
|
hc.view->WorldToView(Vec3d(box.min.x,box.max.y,box.max.z)),
|
|
hc.view->WorldToView(Vec3d(box.max.x,box.min.y,box.max.z)),
|
|
hc.view->WorldToView(Vec3d(box.max.x,box.max.y,box.max.z))
|
|
};
|
|
|
|
CRect objrc,temprc;
|
|
|
|
objrc.left = 10000;
|
|
objrc.top = 10000;
|
|
objrc.right = -10000;
|
|
objrc.bottom = -10000;
|
|
// find new min/max values
|
|
for(int i=0; i<8; i++)
|
|
{
|
|
objrc.left = min(objrc.left,p[i].x);
|
|
objrc.right = max(objrc.right,p[i].x);
|
|
objrc.top = min(objrc.top,p[i].y);
|
|
objrc.bottom = max(objrc.bottom,p[i].y);
|
|
}
|
|
if (objrc.IsRectEmpty())
|
|
{
|
|
// Make objrc at least of size 1.
|
|
objrc.bottom += 1;
|
|
objrc.right += 1;
|
|
}
|
|
if (temprc.IntersectRect( objrc,hc.rect ))
|
|
{
|
|
hc.object = this;
|
|
return true;
|
|
}
|
|
return false;
|
|
*/
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CAIForbiddenAreaObject
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CAIForbiddenAreaObject::CAIForbiddenAreaObject()
|
|
{
|
|
m_bDisplayFilledWhenSelected = true;
|
|
SetColor( RGB(255,0,0) );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CAIForbiddenAreaObject::UpdateGameArea( bool bRemove )
|
|
{
|
|
if (m_bIgnoreGameUpdate)
|
|
return;
|
|
if (!IsCreateGameObjects())
|
|
return;
|
|
|
|
IAISystem *ai = GetIEditor()->GetSystem()->GetAISystem();
|
|
if (ai)
|
|
{
|
|
ai->DeletePath( GetName() );
|
|
ai->CreatePath( GetName() , AREATYPE_FORBIDDEN);
|
|
|
|
|
|
if (bRemove)
|
|
return;
|
|
|
|
const Matrix44 &wtm = GetWorldTM();
|
|
for (int i = 0; i < GetPointCount(); i++)
|
|
{
|
|
ai->AddPointToPath( wtm.TransformPointOLD(GetPoint(i)),GetName(),AREATYPE_FORBIDDEN );
|
|
}
|
|
}
|
|
m_bAreaModified = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CAIPathObject.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CAIPathObject::CAIPathObject()
|
|
{
|
|
SetColor( RGB(180,180,180) );
|
|
}
|
|
|
|
void CAIPathObject::UpdateGameArea( bool bRemove )
|
|
{
|
|
if (m_bIgnoreGameUpdate)
|
|
return;
|
|
if (!IsCreateGameObjects())
|
|
return;
|
|
|
|
IAISystem *ai = GetIEditor()->GetSystem()->GetAISystem();
|
|
if (ai)
|
|
{
|
|
ai->DeletePath( GetName() );
|
|
ai->CreatePath( GetName() );
|
|
|
|
if (bRemove)
|
|
return;
|
|
|
|
const Matrix44 &wtm = GetWorldTM();
|
|
for (int i = 0; i < GetPointCount(); i++)
|
|
{
|
|
ai->AddPointToPath( wtm.TransformPointOLD(GetPoint(i)),GetName() );
|
|
}
|
|
}
|
|
m_bAreaModified = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CAINavigationModifierObject
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CAINavigationModifierObject::CAINavigationModifierObject()
|
|
{
|
|
m_bDisplayFilledWhenSelected = true;
|
|
SetColor( RGB(24,128,231) );
|
|
}
|
|
|
|
void CAINavigationModifierObject::UpdateGameArea( bool bRemove )
|
|
{
|
|
if (m_bIgnoreGameUpdate)
|
|
return;
|
|
if (!IsCreateGameObjects())
|
|
return;
|
|
|
|
IAISystem *ai = GetIEditor()->GetSystem()->GetAISystem();
|
|
if (ai)
|
|
{
|
|
ai->DeletePath( GetName() );
|
|
ai->CreatePath( GetName(),AREATYPE_NAVIGATIONMODIFIER,GetHeight());
|
|
|
|
if (bRemove)
|
|
return;
|
|
|
|
const Matrix44 &wtm = GetWorldTM();
|
|
for (int i = 0; i < GetPointCount(); i++)
|
|
{
|
|
ai->AddPointToPath( wtm.TransformPointOLD(GetPoint(i)),GetName(),AREATYPE_NAVIGATIONMODIFIER );
|
|
}
|
|
}
|
|
m_bAreaModified = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CAIOclussionPlaneObject
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CAIOcclusionPlaneObject::CAIOcclusionPlaneObject()
|
|
{
|
|
m_bDisplayFilledWhenSelected = true;
|
|
SetColor( RGB(24,90,231) );
|
|
m_bForce2D = true;
|
|
mv_closed = true;
|
|
mv_displayFilled = true;
|
|
}
|
|
|
|
void CAIOcclusionPlaneObject::UpdateGameArea( bool bRemove )
|
|
{
|
|
if (m_bIgnoreGameUpdate)
|
|
return;
|
|
if (!IsCreateGameObjects())
|
|
return;
|
|
|
|
IAISystem *ai = GetIEditor()->GetSystem()->GetAISystem();
|
|
if (ai)
|
|
{
|
|
ai->DeletePath( GetName() );
|
|
ai->CreatePath( GetName(),AREATYPE_OCCLUSION_PLANE,GetHeight());
|
|
|
|
if (bRemove)
|
|
return;
|
|
|
|
const Matrix44 &wtm = GetWorldTM();
|
|
for (int i = 0; i < GetPointCount(); i++)
|
|
{
|
|
ai->AddPointToPath( wtm.TransformPointOLD(GetPoint(i)),GetName(),AREATYPE_OCCLUSION_PLANE );
|
|
}
|
|
}
|
|
m_bAreaModified = false;
|
|
} |