//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Virtual mesh implementation.  Cached terrain collision model
//
//=============================================================================


#include "cbase.h"
#include "convert.h"
#include "ivp_surface_manager.hxx"
#include "ivp_surman_polygon.hxx"
#include "ivp_template_surbuild.hxx"
#include "ivp_compact_surface.hxx"
#include <ivp_compact_ledge.hxx>
#include <ivp_ray_solver.hxx>
#include <ivp_compact_ledge_solver.hxx>
#include "ivp_surbuild_pointsoup.hxx"
#include "ivp_surbuild_ledge_soup.hxx"
#include "physics_trace.h"
#include "collisionutils.h"
#include "datamanager.h"
#include "utlbuffer.h"
#include "ledgewriter.h"
#include "tier1/mempool.h"
#include "tier0/memdbgon.h"

class CPhysCollideVirtualMesh;

CTSPool< CUtlVector<CPhysCollideVirtualMesh *> > g_MeshFrameLocksPool;
CTHREADLOCALPTR(CUtlVector<CPhysCollideVirtualMesh *>) g_pMeshFrameLocks;

// This is the surfacemanager class for IVP that implements the required functions by layering CPhysCollideVirtualMesh
class IVP_SurfaceManager_VirtualMesh : public IVP_SurfaceManager
{
public:
	void add_reference_to_ledge(const IVP_Compact_Ledge *ledge);
	void remove_reference_to_ledge(const IVP_Compact_Ledge *ledge);
	void insert_all_ledges_hitting_ray(IVP_Ray_Solver *ray_solver, IVP_Real_Object *object);
	void get_radius_and_radius_dev_to_given_center(const IVP_U_Float_Point *center, IVP_FLOAT *radius, IVP_FLOAT *radius_deviation) const;
	virtual IVP_SURMAN_TYPE get_type() { return IVP_SURMAN_POLYGON; }
	
	// assume mesh is never a single triangle
	virtual const IVP_Compact_Ledge *get_single_convex() const;
	void get_mass_center(IVP_U_Float_Point *mass_center_out) const;
	void get_rotation_inertia( IVP_U_Float_Point *rotation_inertia_out ) const;
	void get_all_ledges_within_radius(const IVP_U_Point *observer_os, IVP_DOUBLE radius,
		const IVP_Compact_Ledge *root_ledge, IVP_Real_Object *other_object, const IVP_Compact_Ledge *other_reference_ledge,
		IVP_U_BigVector<IVP_Compact_Ledge> *resulting_ledges);

	void get_all_terminal_ledges(IVP_U_BigVector<IVP_Compact_Ledge> *resulting_ledges);
	IVP_SurfaceManager_VirtualMesh( CPhysCollideVirtualMesh *pMesh );
	virtual ~IVP_SurfaceManager_VirtualMesh();

private:
	CPhysCollideVirtualMesh *m_pMesh;
};

// These are the managed objects for the LRU of terrain collisions
// These get created/destroyed dynamically by a resourcemanager
// These contain the uncompressed collision models for each displacement patch
// The idea is to have only the necessary instances of these in memory at any given time - never all of them
class CMeshInstance
{
public:
	// resourcemanager
	static unsigned int EstimatedSize( const virtualmeshlist_t &list );
	static CMeshInstance *CreateResource( const virtualmeshlist_t &list );
	static unsigned int ComputeRootLedgeSize( const byte *pHull );
	void DestroyResource() { delete this; }
	unsigned int Size() { return m_memSize; }
	CMeshInstance *GetData() { return this; }
	const triangleledge_t	*GetLedges() { return (triangleledge_t *)m_pMemory; }
	inline int HullCount() { return m_hullCount; }
	const IVP_Compact_Ledge *GetOuterHull() { return (m_hullCount==1) ? (const IVP_Compact_Ledge *)(m_pMemory + m_hullOffset) : NULL; }
	int GetRootLedges( IVP_Compact_Ledge **pLedges, int outCount ) 
	{ 
		int hullOffset = m_hullOffset;
		int count = min(outCount, (int)m_hullCount);
		for ( int i = 0; i < count; i++ )
		{
			pLedges[i] = (IVP_Compact_Ledge *)(m_pMemory + hullOffset);
			hullOffset += sizeof(IVP_Compact_Ledge) + (sizeof(IVP_Compact_Triangle) * pLedges[i]->get_n_triangles());
		}
		return count;
	}

	// locals
	CMeshInstance() { m_pMemory = 0; }
	~CMeshInstance();

private:
	void Init( const virtualmeshlist_t &list );

	int		m_memSize;
	char	*m_pMemory;
	unsigned short m_hullOffset;
	byte	m_hullCount;
	byte	m_pad;
};

CMeshInstance::~CMeshInstance()
{
	if ( m_pMemory )
	{
		ivp_free_aligned( m_pMemory );
		m_pMemory = NULL;
	}
}

unsigned int CMeshInstance::EstimatedSize( const virtualmeshlist_t &list )
{
	int ledgeSize = sizeof(triangleledge_t) * list.triangleCount;
	int pointSize = sizeof(IVP_Compact_Poly_Point) * list.vertexCount;

	int hullSize = ComputeRootLedgeSize(list.pHull);
	return ledgeSize + pointSize + hullSize;
}

// computes the unpacked size of the array of root ledges
unsigned int CMeshInstance::ComputeRootLedgeSize( const byte *pData )
{
	if ( !pData )
		return 0;
	virtualmeshhull_t *pHeader = (virtualmeshhull_t *)pData;
	packedhull_t *pHull = (packedhull_t *)(pHeader+1);
	unsigned int size = pHeader->hullCount * sizeof(IVP_Compact_Ledge);
	for ( int i = 0; i < pHeader->hullCount; i++ )
	{
		size += sizeof(IVP_Compact_Triangle) * pHull[i].triangleCount;
	}
	return size;
}

CMeshInstance *CMeshInstance::CreateResource( const virtualmeshlist_t &list )
{
	CMeshInstance *pMesh = new CMeshInstance;
	pMesh->Init( list );
	return pMesh;
}


// flat memory footprint has triangleledges (ledge + 2 triangles for terrain), then has verts, then optional convex hull
void CMeshInstance::Init( const virtualmeshlist_t &list )
{
	int ledgeSize = sizeof(triangleledge_t) * list.triangleCount;
	int pointSize = sizeof(IVP_Compact_Poly_Point) * list.vertexCount;
	int memSize = ledgeSize + pointSize + ComputeRootLedgeSize(list.pHull);
	m_memSize = memSize;
	m_hullCount = 0;
	m_pMemory = (char *)ivp_malloc_aligned( memSize, 16 );
	Assert( (intp(m_pMemory) & 15) == 0 );	// make sure it is aligned
	IVP_Compact_Poly_Point *pPoints = (IVP_Compact_Poly_Point *)&m_pMemory[ledgeSize];
	triangleledge_t *pLedges = (triangleledge_t *) m_pMemory;
	memset( m_pMemory, 0, memSize );
	int i;

	for ( i = 0; i < list.vertexCount; i++ )
	{
		ConvertPositionToIVP( list.pVerts[i], pPoints[i] );
	}

	for ( i = 0; i < list.triangleCount; i++ )
	{
		Vector v0 = list.pVerts[list.indices[i*3+0]];
		Vector v1 = list.pVerts[list.indices[i*3+1]];
		Vector v2 = list.pVerts[list.indices[i*3+2]];
		Assert( v0 != v1 && v1 != v2 && v0 != v2 );
		CVPhysicsVirtualMeshWriter::InitTwoSidedTriangleLege( &pLedges[i], pPoints, list.indices[i*3+0], list.indices[i*3+1], list.indices[i*3+2], 0 );
	}
	Assert( list.triangleCount > 0 && list.triangleCount <= MAX_VIRTUAL_TRIANGLES );
	// if there's a hull, build it out too
	if ( list.pHull )
	{
		virtualmeshhull_t *pHeader = (virtualmeshhull_t *)list.pHull;
		m_hullCount = pHeader->hullCount;
		Assert( (ledgeSize + pointSize) < 65536 );
		m_hullOffset = ledgeSize + pointSize;
		byte *pMem = (byte *)m_pMemory + m_hullOffset;
#if _DEBUG
		int hullSize = CVPhysicsVirtualMeshWriter::UnpackLedgeListFromHull( pMem, pHeader, pPoints );
		Assert((m_hullOffset+hullSize)==memSize);
#else
		CVPhysicsVirtualMeshWriter::UnpackLedgeListFromHull( pMem, pHeader, pPoints );
#endif
	}
}

const int g_MeshSize = (2048 * 1024 * 4); // nillerusr: 2 MiB should be enough, old value causes problems in ep2
static CDataManager<CMeshInstance, virtualmeshlist_t, CMeshInstance *, CThreadFastMutex> g_MeshManager( g_MeshSize );
static int numIndices = 0, numTriangles = 0, numBaseTriangles = 0, numSplits = 0;
//-----------------------------------------------------------------------------
// Purpose: This allows for just-in-time procedural triangle soup data to be 
//			instanced & cached as IVP collision data (compact ledges)
//-----------------------------------------------------------------------------
// NOTE: This is the permanent in-memory representation.  It holds the compressed data
// and the parameters necessary to request the proxy geometry as needed
class CPhysCollideVirtualMesh : public CPhysCollide
{
public:
	// UNDONE: Unlike other CPhysCollide objects, operations the virtual mesh are
	// non-const because they may instantiate the cache.  This causes problems with the interface.
	// Maybe the cache stuff should be mutable, but it amounts to the same kind of
	// hackery to cast away const.
	
	// get a surface manager 
	virtual IVP_SurfaceManager *CreateSurfaceManager( short &collideType ) const
	{
		collideType = COLLIDE_VIRTUAL;
		// UNDONE: Figure out how to avoid this const_cast
		return new IVP_SurfaceManager_VirtualMesh(const_cast<CPhysCollideVirtualMesh *>(this));
	}
	virtual void GetAllLedges( IVP_U_BigVector<IVP_Compact_Ledge> &ledges ) const
	{
		const triangleledge_t *pLedges = const_cast<CPhysCollideVirtualMesh *>(this)->AddRef()->GetLedges();
		for ( int i = 0; i < m_ledgeCount; i++ )
		{
			ledges.add( const_cast<IVP_Compact_Ledge *>(&pLedges[i].ledge) );
		}
		const_cast<CPhysCollideVirtualMesh *>(this)->Release();
	}
	virtual unsigned int GetSerializationSize() const 
	{ 
		if ( !m_pHull )
			return 0; 
		return m_pHull->TotalSize();
	}

	virtual unsigned int SerializeToBuffer( char *pDest, bool bSwap = false ) const 
	{
		unsigned int size = GetSerializationSize();
		if ( size )
		{
			memcpy( pDest, m_pHull, size );
		}
		return size;
	}
	virtual int GetVCollideIndex() const { return 0; }
	virtual void SetMassCenter( const Vector &massCenter ) {Assert(0); }
	virtual Vector GetOrthographicAreas() const { return Vector(1,1,1);}

	Vector GetMassCenter() const;
	virtual float GetSphereRadius() const;
	float GetSphereRadiusIVP() const;
	void Init( const char *pBuffer, unsigned int size )
	{
	}
	void GetAllLedgesWithinRadius( const IVP_U_Point *observer_os, IVP_DOUBLE radius, IVP_U_BigVector<IVP_Compact_Ledge> *resulting_ledges, const IVP_Compact_Ledge *pRootLedge = NULL )
	{
		virtualmeshtrianglelist_t list;
		list.triangleCount = 0;
		Vector centerHL;
		ConvertPositionToHL( *observer_os, centerHL );
		float radiusHL = ConvertDistanceToHL(radius);
		m_params.pMeshEventHandler->GetTrianglesInSphere( m_params.userData, centerHL, radiusHL, &list );
		if ( list.triangleCount )
		{
			CMeshInstance *pMesh = AddRef();
			const triangleledge_t *pLedges = pMesh->GetLedges();
			FrameRelease();

			// If we have two root ledges, then each one contains half the triangles
			// only return triangles indexed under the root ledge being queried
			int minTriangle = 0;
			int maxTriangle = m_ledgeCount;
			if ( pMesh->HullCount() > 1 )
			{
				Assert(pMesh->HullCount()==2);
				IVP_Compact_Ledge *pRootNodes[2];
				pMesh->GetRootLedges( pRootNodes, 2 );
				int midTriangle = m_ledgeCount/2;
				if ( pRootLedge == pRootNodes[0] )
				{
					maxTriangle = midTriangle;
				}
				else
				{
					minTriangle = midTriangle;
				}
			}
			IVP_DOUBLE radiusSq = radius * radius;
			for ( int i = 0; i < list.triangleCount; i++ )
			{
				Assert( list.triangleIndices[i] < m_ledgeCount );
				if ( list.triangleIndices[i] < minTriangle || list.triangleIndices[i] >= maxTriangle )
					continue;

				const IVP_Compact_Ledge *ledge = &pLedges[list.triangleIndices[i]].ledge;
				Assert(ledge->get_n_triangles() == 2);
				const IVP_Compact_Triangle *triangle = ledge->get_first_triangle();
				IVP_DOUBLE qdist = IVP_CLS.calc_qlen_PF_F_space(ledge, triangle, observer_os);
				if ( qdist > radiusSq ) 
				{
					continue;
				}

				resulting_ledges->add( const_cast<IVP_Compact_Ledge *>(ledge) );
			}
		}
	}

	virtual void OutputDebugInfo() const
	{
		Msg("Virtual mesh!\n");
	}

	CPhysCollideVirtualMesh(const virtualmeshparams_t &params) : m_params(params), m_hMemory( INVALID_MEMHANDLE ), m_ledgeCount( 0 )
	{
		m_pHull = NULL;
		if ( params.buildOuterHull )
		{
			BuildBoundingLedge();
		}
	}

	virtual ~CPhysCollideVirtualMesh();

	// adds a lock on the collsion memory :: MUST CALL Release() or FrameRelease corresponding to this call!!!
	CMeshInstance *AddRef();

	void BuildBoundingLedge();
	static virtualmeshhull_t *CreateMeshBoundingHull( const virtualmeshlist_t &list );
	static void DestroyMeshBoundingHull(virtualmeshhull_t *pHull) { CVPhysicsVirtualMeshWriter::DestroyPackedHull(pHull); }
	static IVP_Compact_Surface *CreateBoundingSurfaceFromRange( const virtualmeshlist_t &list, int firstIndex, int indexCount );

	int GetRootLedges( IVP_Compact_Ledge **pLedges, int outCount )
	{
		int count = AddRef()->GetRootLedges(pLedges, outCount);
		FrameRelease();
		return count;
	}

	IVP_Compact_Ledge *GetBoundingLedge()
	{
		IVP_Compact_Ledge *pLedge = const_cast<IVP_Compact_Ledge *>(AddRef()->GetOuterHull());
		FrameRelease();
		return pLedge;
	}

	// releases a lock on the collision memory
	void Release();
	// Analagous to Release, but happens at the end of the frame
	void FrameRelease() 
	{ 
		CUtlVector<CPhysCollideVirtualMesh *> *pLocks = g_pMeshFrameLocks;
		if ( !pLocks )
		{
			g_pMeshFrameLocks = pLocks = g_MeshFrameLocksPool.GetObject();
			Assert( pLocks );
		}
		pLocks->AddToTail(this);	
	}
	inline void GetBounds( Vector &mins, Vector &maxs ) const
	{
		m_params.pMeshEventHandler->GetWorldspaceBounds( m_params.userData, &mins, &maxs );
	}

private:
	CMeshInstance *BuildLedges();

	virtualmeshparams_t m_params;
	virtualmeshhull_t *m_pHull;
	memhandle_t		m_hMemory;
	short			m_ledgeCount;
};

static void FlushFrameLocks()
{
	CUtlVector<CPhysCollideVirtualMesh *> *pLocks = g_pMeshFrameLocks;
	if ( pLocks )
	{
		for ( int i = 0; i < pLocks->Count(); i++ )
		{
			Assert( (*pLocks)[i] );
			(*pLocks)[i]->Release();
		}
		pLocks->RemoveAll();
		g_MeshFrameLocksPool.PutObject( g_pMeshFrameLocks );
		g_pMeshFrameLocks = NULL;
	}
}

void VirtualMeshPSI()
{
	FlushFrameLocks();
}


Vector CPhysCollideVirtualMesh::GetMassCenter() const
{
	Vector mins, maxs;
	GetBounds( mins, maxs );
	return 0.5 * (mins + maxs);
}

float CPhysCollideVirtualMesh::GetSphereRadius() const
{
	Vector mins, maxs;
	GetBounds( mins, maxs );
	Vector point = 0.5 * (mins+maxs);
	return (maxs - point).Length();
}

float CPhysCollideVirtualMesh::GetSphereRadiusIVP() const
{
	return ConvertDistanceToIVP( GetSphereRadius() );
}

static CThreadFastMutex s_BuildVirtualMeshMutex;
CMeshInstance *CPhysCollideVirtualMesh::AddRef()
{
	CMeshInstance *pMesh = g_MeshManager.LockResource( m_hMemory );
	if ( !pMesh )
	{
		s_BuildVirtualMeshMutex.Lock();
		pMesh = g_MeshManager.LockResource( m_hMemory );
		if ( !pMesh )
		{
			pMesh = BuildLedges();
		}
		s_BuildVirtualMeshMutex.Unlock();
	}
	Assert( pMesh );
	return pMesh;
}
 
void CPhysCollideVirtualMesh::Release()
{
	g_MeshManager.UnlockResource( m_hMemory );
}

CPhysCollideVirtualMesh::~CPhysCollideVirtualMesh()
{
	CVPhysicsVirtualMeshWriter::DestroyPackedHull(m_pHull);
	g_MeshManager.DestroyResource( m_hMemory );
}

CMeshInstance *CPhysCollideVirtualMesh::BuildLedges()
{
	virtualmeshlist_t list;
	m_params.pMeshEventHandler->GetVirtualMesh( m_params.userData, &list );
	if ( !list.pHull )
	{
		list.pHull = (byte *)m_pHull;
	}

	if ( list.triangleCount )
	{
		m_hMemory = g_MeshManager.CreateResource( list );
		m_ledgeCount = list.triangleCount;
		CMeshInstance *pMesh = g_MeshManager.LockResource( m_hMemory );

		Assert( g_MeshManager.AvailableSize() != 0 );

		return pMesh;
	}
	return NULL;
}

// build the outer ledge, split into two if necessary
void CPhysCollideVirtualMesh::BuildBoundingLedge()
{
	virtualmeshlist_t list;
	m_params.pMeshEventHandler->GetVirtualMesh( m_params.userData, &list );
	m_pHull = CreateMeshBoundingHull(list);
}

virtualmeshhull_t *CPhysCollideVirtualMesh::CreateMeshBoundingHull( const virtualmeshlist_t &list )
{
	virtualmeshhull_t *pHull = NULL;
	if ( list.triangleCount )
	{
		IVP_Compact_Surface *pSurface = CreateBoundingSurfaceFromRange( list, 0, list.indexCount );
		if ( pSurface )
		{
			const IVP_Compact_Ledge *pLedge = pSurface->get_compact_ledge_tree_root()->get_compact_hull();
			if ( CVPhysicsVirtualMeshWriter::LedgeCanBePacked(pLedge, list) )
			{
				pHull = CVPhysicsVirtualMeshWriter::CreatePackedHullFromLedges( list, &pLedge, 1 );
			}
			else
			{
				// too big to pack to 8-bits, split in two
				IVP_Compact_Surface *pSurface0 = CreateBoundingSurfaceFromRange( list, 0, list.indexCount/2 );
				IVP_Compact_Surface *pSurface1 = CreateBoundingSurfaceFromRange( list, list.indexCount/2, list.indexCount/2 );

				const IVP_Compact_Ledge *pLedges[2] = {pSurface0->get_compact_ledge_tree_root()->get_compact_hull(), pSurface1->get_compact_ledge_tree_root()->get_compact_hull()};
				pHull = CVPhysicsVirtualMeshWriter::CreatePackedHullFromLedges( list, pLedges, 2 );
				ivp_free_aligned(pSurface0);
				ivp_free_aligned(pSurface1);
			}
			ivp_free_aligned(pSurface);
		}
	}
	return pHull;
}

IVP_Compact_Surface *CPhysCollideVirtualMesh::CreateBoundingSurfaceFromRange( const virtualmeshlist_t &list, int firstIndex, int indexCount )
{
	Assert( list.triangleCount );
	IVP_U_Point triVerts[3];
	IVP_U_Vector<IVP_U_Point> triList;
	IVP_SurfaceBuilder_Ledge_Soup builder;
	triList.add( &triVerts[0] );
	triList.add( &triVerts[1] );
	triList.add( &triVerts[2] );
	int lastIndex = firstIndex + indexCount;
	int firstTriangle = firstIndex/3;
	int lastTriangle = lastIndex/3;
	for ( int i = firstTriangle; i < lastTriangle; i++ )
	{
		ConvertPositionToIVP( list.pVerts[list.indices[i*3+0]], triVerts[0] );
		ConvertPositionToIVP( list.pVerts[list.indices[i*3+1]], triVerts[1] );
		ConvertPositionToIVP( list.pVerts[list.indices[i*3+2]], triVerts[2] );
		IVP_Compact_Ledge *pLedge = IVP_SurfaceBuilder_Pointsoup::convert_pointsoup_to_compact_ledge( &triList );
		builder.insert_ledge( pLedge );
	}
	// build a convex hull of those verts
	IVP_Template_Surbuild_LedgeSoup params;
	params.build_root_convex_hull = IVP_TRUE;
	IVP_Compact_Surface *pSurface = builder.compile( &params );

#if _DEBUG
	const IVP_Compact_Ledgetree_Node *node = pSurface->get_compact_ledge_tree_root();
	IVP_Compact_Ledge *pLedge = const_cast<IVP_Compact_Ledge *>(node->get_compact_hull());	// we're going to write into client data on each vert before we throw this away
	Assert(pLedge && !pLedge->is_terminal());
#endif
	return pSurface;
}

CPhysCollide *CreateVirtualMesh( const virtualmeshparams_t &params )
{
	return new CPhysCollideVirtualMesh(params);
}

void DestroyVirtualMesh( CPhysCollide *pMesh )
{
	delete pMesh;
}

//-----------------------------------------------------------------------------
// IVP_SurfaceManager_VirtualMesh
// This hooks the underlying collision model to IVP's surfacemanager interface
//-----------------------------------------------------------------------------

IVP_SurfaceManager_VirtualMesh::IVP_SurfaceManager_VirtualMesh( CPhysCollideVirtualMesh *pMesh ) : m_pMesh(pMesh)
{
}

IVP_SurfaceManager_VirtualMesh::~IVP_SurfaceManager_VirtualMesh()
{
	FlushFrameLocks();
}

void IVP_SurfaceManager_VirtualMesh::add_reference_to_ledge(const IVP_Compact_Ledge *ledge)
{
	m_pMesh->AddRef();
}
void IVP_SurfaceManager_VirtualMesh::remove_reference_to_ledge(const IVP_Compact_Ledge *ledge)
{
	m_pMesh->Release();
}

// Implement the IVP raycast.  This is done by testing each triangle (front & back) - so it's slow
void IVP_SurfaceManager_VirtualMesh::insert_all_ledges_hitting_ray(IVP_Ray_Solver *ray_solver, IVP_Real_Object *object)
{
	IVP_Vector_of_Ledges_256 ledges;
	IVP_Ray_Solver_Os ray_solver_os( ray_solver, object);

	IVP_U_Point center(&ray_solver_os.ray_center_point);
	m_pMesh->GetAllLedgesWithinRadius( &center, ray_solver_os.ray_length * 0.5f, &ledges );

	for (int i=ledges.len()-1;i>=0;i--)
	{
		const IVP_Compact_Ledge *l = ledges.element_at(i);
		ray_solver_os.check_ray_against_compact_ledge_os(l);
	}
}

// Used to predict collision detection needs
void IVP_SurfaceManager_VirtualMesh::get_radius_and_radius_dev_to_given_center(const IVP_U_Float_Point *center, IVP_FLOAT *radius, IVP_FLOAT *radius_deviation) const
{
	// UNDONE: Check radius_deviation to see if there is a useful optimization to be made here
	*radius = m_pMesh->GetSphereRadiusIVP();
	*radius_deviation = *radius;
}

// get a single convex if appropriate
const IVP_Compact_Ledge *IVP_SurfaceManager_VirtualMesh::get_single_convex() const 
{ 
	return m_pMesh->GetBoundingLedge(); 
}

// get a mass center for objects using this collision rep
void IVP_SurfaceManager_VirtualMesh::get_mass_center(IVP_U_Float_Point *mass_center_out) const
{
	Vector center = m_pMesh->GetMassCenter();
	ConvertPositionToIVP( center, *mass_center_out );
}

//-----------------------------------------------------------------------------
// Purpose: Compute a diagonalized inertia tensor.
//-----------------------------------------------------------------------------
void IVP_SurfaceManager_VirtualMesh::get_rotation_inertia( IVP_U_Float_Point *rotation_inertia_out ) const
{
	// HACKHACK: No need for this because we only support static objects for now
	rotation_inertia_out->set(1,1,1);
}

//-----------------------------------------------------------------------------
// Purpose: Query ledges (triangles in this case) in sphere
//-----------------------------------------------------------------------------
void IVP_SurfaceManager_VirtualMesh::get_all_ledges_within_radius(const IVP_U_Point *observer_os, IVP_DOUBLE radius,
	const IVP_Compact_Ledge *root_ledge, IVP_Real_Object *other_object, const IVP_Compact_Ledge *other_reference_ledge,
	IVP_U_BigVector<IVP_Compact_Ledge> *resulting_ledges)
{
	if ( !root_ledge )
	{
		IVP_Compact_Ledge *pLedges[2];
		int count = m_pMesh->GetRootLedges( pLedges, ARRAYSIZE(pLedges) );
		if ( count )
		{
			for ( int i = 0; i < count; i++ )
			{
				resulting_ledges->add( pLedges[i] ); // return the recursive/virtual outer hull
			}
			return;
		}
	}
	m_pMesh->GetAllLedgesWithinRadius( observer_os, radius, resulting_ledges, root_ledge );
}

//-----------------------------------------------------------------------------
// Purpose: Query all of the ledges (triangles)
//-----------------------------------------------------------------------------
void IVP_SurfaceManager_VirtualMesh::get_all_terminal_ledges(IVP_U_BigVector<IVP_Compact_Ledge> *resulting_ledges)
{
	m_pMesh->GetAllLedges( *resulting_ledges );
}