//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: 
//
// $Workfile:     $
// $Date:         $
// $NoKeywords: $
//=============================================================================

#ifndef BUILDDISP_H
#define BUILDDISP_H

#ifdef _WIN32
#pragma once
#endif

#include "commonmacros.h"
#include "tier0/dbg.h"
#include "bspfile.h"
#include "mathlib.h"
#include "bumpvects.h"
#include "disp_common.h"
#include "bitvec.h"

#define DISP_ALPHA_PROP_DELTA		382.5f

class CCoreDispInfo;

struct CoreDispBBox_t
{
	Vector vMin, vMax;
};

//=========================================================================
//
// Surface Class - interfacing class (fill in with MapFace, dface_t, and 
//                                    msurface_t)
//
class CCoreDispSurface
{
public:

	enum { QUAD_POINT_COUNT = 4 };
	enum { MAX_CORNER_NEIGHBOR_COUNT = 16 };

	CCoreDispSurface();

	//=========================================================================
	//
	// initialization
	//
	void Init( void );

	//=========================================================================
	//
	// parent surface id - index to CMapFace, dface_t, or msurface_t
	//
	inline void SetHandle( int handle );
	inline int GetHandle( void );

	//=========================================================================
	//
	// vertex data - pos, normal, texture, lightmap, alpha, etc...
	//
	inline void SetPointCount( int count );
	inline int GetPointCount( void ) const;

	inline void SetPoint( int index, Vector const &pt );
	inline void GetPoint( int index, Vector& pt ) const;
	inline Vector const& GetPoint( int index ) const;

	inline void SetPointNormal( int index, Vector const &normal );
	inline void GetPointNormal( int index, Vector &normal );
	inline void SetTexCoord( int index, Vector2D const& texCoord );
	inline void GetTexCoord( int index, Vector2D& texCoord ) const;

	inline void SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord );
	inline void GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const;
	inline void SetLuxelCoords( int bumpIndex, Vector2D const coords[4] );
	inline void GetLuxelCoords( int bumpIndex, Vector2D coords[4] ) const;

	inline void SetAlpha( int index, float alpha );
	inline float GetAlpha( int const index );

	//=========================================================================
	//
	// utils
	//
	inline void GetNormal( Vector& normal );
	inline void SetFlags( int flag );
	inline int GetFlags( void );
	inline void SetContents( int contents );
	inline int GetContents( void );

	//=========================================================================
	//
	// create utils (texture axis not use anymore but here to support older maps)
	//
	inline void SetSAxis( Vector const &axis );
	inline void GetSAxis( Vector &axis );
	inline void SetTAxis( Vector const &axis );
	inline void GetTAxis( Vector &axis );

	inline void SetPointStartIndex( int index );
	inline int GetPointStartIndex( void );
	inline void SetPointStart( Vector const &pt );
	inline void GetPointStart( Vector &pt );

	// Used by the tools to set the neighbor data from the BSP file.
	void	SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] );

    void	GeneratePointStartIndexFromMappingAxes( Vector const &sAxis, Vector const &tAxis );
	int		GenerateSurfPointStartIndex( void );
	int		FindSurfPointStartIndex( void );
	void	AdjustSurfPointData( void );
	
	// Indexed by CORNER_ defines.
	CDispCornerNeighbors*		GetCornerNeighbors( int iCorner )		{ Assert( iCorner >= 0 && iCorner < ARRAYSIZE( m_CornerNeighbors ) ); return &m_CornerNeighbors[iCorner]; }
	const CDispCornerNeighbors*	GetCornerNeighbors( int iCorner ) const { Assert( iCorner >= 0 && iCorner < ARRAYSIZE( m_CornerNeighbors ) ); return &m_CornerNeighbors[iCorner]; }
	
	// Indexed by CORNER_ defines.
	int							GetCornerNeighborCount( int iCorner ) const				{ return GetCornerNeighbors( iCorner )->m_nNeighbors; }
	int							GetCornerNeighbor( int iCorner, int iNeighbor ) const	{ Assert( iNeighbor >= 0 && iNeighbor < GetCornerNeighbors(iCorner)->m_nNeighbors ); return GetCornerNeighbors( iCorner )->m_Neighbors[iNeighbor]; }
	
	CDispNeighbor*			GetEdgeNeighbor( int iEdge )		{ Assert( iEdge >= 0 && iEdge < ARRAYSIZE( m_EdgeNeighbors ) ); return &m_EdgeNeighbors[iEdge]; }
	const CDispNeighbor*	GetEdgeNeighbor( int iEdge ) const	{ Assert( iEdge >= 0 && iEdge < ARRAYSIZE( m_EdgeNeighbors ) ); return &m_EdgeNeighbors[iEdge]; }


protected:
	
	int			m_Index;																// parent face (CMapFace, dface_t, msurface_t) index "handle"
	
	int			m_PointCount;															// number of points in the face (should be 4!)
	Vector		m_Points[QUAD_POINT_COUNT];												// points
	Vector		m_Normals[QUAD_POINT_COUNT];											// normals at points
	Vector2D	m_TexCoords[QUAD_POINT_COUNT];											// texture coordinates at points
	Vector2D	m_LuxelCoords[NUM_BUMP_VECTS+1][QUAD_POINT_COUNT];						// lightmap coordinates at points
	float		m_Alphas[QUAD_POINT_COUNT];												// alpha at points

	// Straight from the BSP file.	
	CDispNeighbor			m_EdgeNeighbors[4];
	CDispCornerNeighbors	m_CornerNeighbors[4];

    int			m_Flags;																// surface flags - inherited from the "parent" face
	int			m_Contents;																// contents flags - inherited from the "parent" face

	Vector		sAxis;																	// used to generate start disp orientation (old method)
	Vector		tAxis;																	// used to generate start disp orientation (old method)
	int			m_PointStartIndex;														// index to the starting point -- for saving starting point
	Vector		m_PointStart;															// starting point used to determine the orientation of the displacement map on the surface
};


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetHandle( int handle )
{
	m_Index = handle;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispSurface::GetHandle( void )
{
	return m_Index;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetPointCount( int count )
{
	// quad only -- currently!
	if( count != 4 )
		return;
	m_PointCount = count;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispSurface::GetPointCount( void ) const
{
	return m_PointCount;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetPoint( int index, Vector const &pt )
{
	Assert( index >= 0 );
	Assert( index < QUAD_POINT_COUNT );
	VectorCopy( pt, m_Points[index] );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::GetPoint( int index, Vector &pt ) const
{
	Assert( index >= 0 );
	Assert( index < QUAD_POINT_COUNT );
	VectorCopy( m_Points[index], pt );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline Vector const& CCoreDispSurface::GetPoint( int index ) const
{
	Assert( index >= 0 );
	Assert( index < QUAD_POINT_COUNT );
	return m_Points[index];
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetPointNormal( int index, Vector const &normal )
{
	Assert( index >= 0 );
	Assert( index < QUAD_POINT_COUNT );
	VectorCopy( normal, m_Normals[index] );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::GetPointNormal( int index, Vector& normal )
{
	Assert( index >= 0 );
	Assert( index < QUAD_POINT_COUNT );
	VectorCopy( m_Normals[index], normal );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetTexCoord( int index, Vector2D const& texCoord )
{
	Assert( index >= 0 );
	Assert( index < QUAD_POINT_COUNT );
	Vector2DCopy( texCoord, m_TexCoords[index] );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::GetTexCoord( int index, Vector2D& texCoord ) const
{
	Assert( index >= 0 );
	Assert( index < QUAD_POINT_COUNT );
	Vector2DCopy( m_TexCoords[index], texCoord );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord )
{
	Assert( index >= 0 );
	Assert( index < QUAD_POINT_COUNT );
	Assert( bumpIndex >= 0 );
	Assert( bumpIndex < NUM_BUMP_VECTS + 1 );
	Vector2DCopy( luxelCoord, m_LuxelCoords[bumpIndex][index] );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const
{
	Assert( index >= 0 );
	Assert( index < QUAD_POINT_COUNT );
	Assert( bumpIndex >= 0 );
	Assert( bumpIndex < NUM_BUMP_VECTS + 1 );
	Vector2DCopy( m_LuxelCoords[bumpIndex][index], luxelCoord );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetLuxelCoords( int bumpIndex, Vector2D const luxelCoords[4] )
{
	Assert( bumpIndex >= 0 );
	Assert( bumpIndex < NUM_BUMP_VECTS + 1 );
	for( int i=0; i < 4; i++ )
		Vector2DCopy( luxelCoords[i], m_LuxelCoords[bumpIndex][i] );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::GetLuxelCoords( int bumpIndex, Vector2D luxelCoords[4] ) const
{
	Assert( bumpIndex >= 0 );
	Assert( bumpIndex < NUM_BUMP_VECTS + 1 );
	for( int i=0; i < 4; i++ )
		Vector2DCopy( m_LuxelCoords[bumpIndex][i], luxelCoords[i] );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetAlpha( int index, float alpha )
{
	Assert( index >= 0 );
	Assert( index < QUAD_POINT_COUNT );
	m_Alphas[index] = alpha;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline float CCoreDispSurface::GetAlpha( int const index )
{
	Assert( index >= 0 );
	Assert( index < QUAD_POINT_COUNT );
	return m_Alphas[index];
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetFlags( int flag )
{
	m_Flags = flag;
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispSurface::GetFlags( void )
{
	return m_Flags;
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetContents( int contents )
{
	m_Contents = contents;
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispSurface::GetContents( void )
{
	return m_Contents;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetSAxis( Vector const &axis )
{
	VectorCopy( axis, sAxis );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::GetSAxis( Vector& axis )
{
	VectorCopy( sAxis, axis );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetTAxis( Vector const &axis )
{
	VectorCopy( axis, tAxis );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::GetTAxis( Vector& axis )
{
	VectorCopy( tAxis, axis );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetPointStartIndex( int index )
{
	Assert( index >= 0 );
	Assert( index < QUAD_POINT_COUNT );
	m_PointStartIndex = index;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispSurface::GetPointStartIndex( void )
{
	return m_PointStartIndex;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::SetPointStart( Vector const& pt )
{
	VectorCopy( pt, m_PointStart );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::GetPointStart( Vector& pt )
{
	VectorCopy( m_PointStart, pt );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispSurface::GetNormal( Vector& normal )
{
	//
	// calculate the displacement surface normal
	//
	Vector tmp[2];
	VectorSubtract( m_Points[1], m_Points[0], tmp[0] );
	VectorSubtract( m_Points[3], m_Points[0], tmp[1] );
	CrossProduct( tmp[1], tmp[0], normal );
	VectorNormalize( normal );
}


//=========================================================================
//
// Node Class (for displacement quad-tree)
//
class CCoreDispNode
{
public:

	enum { MAX_NEIGHBOR_NODE_COUNT = 4 };
	enum { MAX_NEIGHBOR_VERT_COUNT = 8 };
	enum { MAX_SURF_AT_NODE_COUNT = 8 };

	//=========================================================================
	//
	// Initialization
	//
	void Init( void );

	//=========================================================================
	//
	//
	//
	inline void SetBoundingBox( Vector const& bMin, Vector const& bMax );
	inline void GetBoundingBox( Vector& bMin, Vector& bMax );

	inline void SetErrorTerm( float errorTerm );
	inline float GetErrorTerm( void );

	inline void SetNeighborNodeIndex( int dir, int index );
	inline int GetNeighborNodeIndex( int dir );

	inline void SetCenterVertIndex( int index );
	inline int GetCenterVertIndex( void );
	inline void SetNeighborVertIndex( int dir, int index );
	inline int GetNeighborVertIndex( int dir );

	inline void SetTriBoundingBox( int index, Vector const& bMin, Vector const& bMax );
	inline void GetTriBoundingBox( int index, Vector& bMin, Vector& bMax );
	inline void SetTriPlane( int index, Vector const& normal, float dist );
	inline void GetTriPlane( int index, cplane_t *plane );

	inline void SetRayBoundingBox( int index, Vector const& bMin, Vector const& bMax );
	inline void GetRayBoundingBox( int index, Vector& bMin, Vector& bMax );

	//=========================================================================
	//
	// Node Functions (friend functions)
	//
	friend int GetNodeLevel( int index );
	friend int GetNodeCount( int power );
	friend int GetNodeParent( int index );
	friend int GetNodeChild( int power, int index, int direction );
	friend int GetNodeNeighborNode( int power, int index, int direction, int level );
	friend int GetNodeNeighborNodeFromNeighborSurf( int power, int index, int direction, int level, int neighborOrient );
	friend int GetNodeMinNodeAtLevel( int level );

	friend void GetDispNodeTriVerts( CCoreDispInfo *pDisp, int nodeIndex, int triIndex, float *v1, float *v2, float *v3 );

	friend void GetComponentsFromNodeIndex( int index, int *x, int *y );
	friend int GetNodeIndexFromComponents( int x, int y );

protected:	
		
	Vector		m_BBox[2];											// displacement node bounding box (take into account size of children)
	float		m_ErrorTerm;										// LOD error term (the "precision" of the representation of the surface at this node's level)
	int			m_VertIndex;										// the node's vertex index (center vertex of node)
	int			m_NeighborVertIndices[MAX_NEIGHBOR_VERT_COUNT];		// all other vertex indices in node (maximally creates 8 trianglar surfaces)
	Vector		m_SurfBBoxes[MAX_SURF_AT_NODE_COUNT][2];			// surface bounding boxes - old method
	cplane_t	m_SurfPlanes[MAX_SURF_AT_NODE_COUNT];				// surface plane info - old method

	Vector		m_RayBBoxes[4][2];									// bounding boxes for ray traces
};


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispNode::SetBoundingBox( Vector const& bMin, Vector const& bMax )
{
	VectorCopy( bMin, m_BBox[0] );
	VectorCopy( bMax, m_BBox[1] );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispNode::GetBoundingBox( Vector& bMin, Vector& bMax )
{
	VectorCopy( m_BBox[0], bMin );
	VectorCopy( m_BBox[1], bMax );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispNode::SetErrorTerm( float errorTerm )
{
	m_ErrorTerm = errorTerm;
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline float CCoreDispNode::GetErrorTerm( void )
{
	return m_ErrorTerm;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispNode::SetCenterVertIndex( int index )
{
	m_VertIndex = index;
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispNode::GetCenterVertIndex( void )
{
	return m_VertIndex;
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispNode::SetNeighborVertIndex( int dir, int index )
{
	Assert( dir >= 0 );
	Assert( dir < MAX_NEIGHBOR_VERT_COUNT );
	m_NeighborVertIndices[dir] = index;
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispNode::GetNeighborVertIndex( int dir )
{
	Assert( dir >= 0 );
	Assert( dir < MAX_NEIGHBOR_VERT_COUNT );
	return m_NeighborVertIndices[dir];
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispNode::SetTriBoundingBox( int index, Vector const& bMin, Vector const& bMax )
{
	Assert( index >= 0 );
	Assert( index < MAX_SURF_AT_NODE_COUNT );
	VectorCopy( bMin, m_SurfBBoxes[index][0] );
	VectorCopy( bMax, m_SurfBBoxes[index][1] );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispNode::GetTriBoundingBox( int index, Vector& bMin, Vector& bMax )
{
	Assert( index >= 0 );
	Assert( index < MAX_SURF_AT_NODE_COUNT );
	VectorCopy( m_SurfBBoxes[index][0], bMin );
	VectorCopy( m_SurfBBoxes[index][1], bMax );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispNode::SetTriPlane( int index, Vector const &normal, float dist )
{
	Assert( index >= 0 );
	Assert( index < MAX_SURF_AT_NODE_COUNT );
	VectorCopy( normal, m_SurfPlanes[index].normal );
	m_SurfPlanes[index].dist = dist;
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispNode::GetTriPlane( int index, cplane_t *plane )
{
	Assert( index >= 0 );
	Assert( index < MAX_SURF_AT_NODE_COUNT );
	VectorCopy( m_SurfPlanes[index].normal, plane->normal );
	plane->dist = m_SurfPlanes[index].dist;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispNode::SetRayBoundingBox( int index, Vector const &bMin, Vector const &bMax )
{
	Assert( index >= 0 );
	Assert( index < 4 );
	VectorCopy( bMin, m_RayBBoxes[index][0] );
	VectorCopy( bMax, m_RayBBoxes[index][1] );
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispNode::GetRayBoundingBox( int index, Vector& bMin, Vector& bMax )
{
	Assert( index >= 0 );
	Assert( index < 4 );
	VectorCopy( m_RayBBoxes[index][0], bMin );
	VectorCopy( m_RayBBoxes[index][1], bMax );
}

	
//=============================================================================
//
// CCoreInfoBuilder - the primary data necessay to derive a displacement surface
//                    used by WorldCraft (CMapFace, CMapDisp), VRAD (dface_t, ddispinto_t),
//                    and the engine (msurface_t, CDispInfo)
//

struct CoreDispVert_t
{
	Vector			m_FieldVector;						// displacement vector field
	float			m_FieldDistance;					// the distances along the displacement vector normal

	Vector			m_SubdivNormal;
	Vector			m_SubdivPos;						// used the create curvature of displacements

	// generated displacement surface data
	Vector			m_Vert;								// displacement surface vertices
	Vector			m_FlatVert;
	Vector			m_Normal;							// displacement surface normals
	Vector			m_TangentS;							// use in calculating the tangent space axes
	Vector			m_TangentT;							// use in calculating the tangent space axes
	Vector2D		m_TexCoord;							// displacement surface texture coordinates
	Vector2D		m_LuxelCoords[NUM_BUMP_VECTS+1];	// displacement surface lightmap coordinates

	// additional per-vertex data
	float			m_Alpha;							// displacement alpha values (per displacement vertex)
};

// New, need to use this at the node level
#define	COREDISPTRI_TAG_WALKABLE				(1<<0)
#define COREDISPTRI_TAG_FORCE_WALKABLE_BIT		(1<<1)
#define COREDISPTRI_TAG_FORCE_WALKABLE_VAL		(1<<2)
#define COREDISPTRI_TAG_BUILDABLE				(1<<3)
#define COREDISPTRI_TAG_FORCE_BUILDABLE_BIT		(1<<4)
#define COREDISPTRI_TAG_FORCE_BUILDABLE_VAL		(1<<5)

struct CoreDispTri_t
{
	unsigned short  m_iIndex[3];						// the three indices that make up a triangle
	unsigned short	m_uiTags;							// walkable, buildable, etc.
};

class CCoreDispInfo : public CDispUtilsHelper
{
public:

	//
	// tree and displacement surface directions
	//
	enum { WEST      = 0,
		   NORTH     = 1,
		   EAST      = 2,
		   SOUTH     = 3,
		   SOUTHWEST = 4,
		   SOUTHEAST = 5,
		   NORTHWEST = 6,
		   NORTHEAST = 7 };

#if 0
	//
	// building parameters
	//
	enum { BUILD_NORMALS      = 0x1,
		   BUILD_TEXCOORDS    = 0x2,
		   BUILD_LIGHTCOORDS  = 0x4,
		   BUILD_LODTREE      = 0x8,
		   BUILD_COLLISION    = 0x10,
		   BUILD_TANGENTSPACE = 0x20 };
#endif

	//
	// surface info flags
	//
	enum { SURF_BUMPED             = 0x1,  
		   SURF_NOCOLLISION		   = 0x2,
		   SURF_RAY_COLLISION_ONLY = 0x4 };

	enum { MAX_DISP_POWER = MAX_MAP_DISP_POWER };
	enum { MAX_VERT_COUNT = MAX_DISPVERTS };
	enum { MAX_NODE_COUNT = 85 };


// Convert from a CDispUtilsHelper.
public:
	
	static CCoreDispInfo*			FromDispUtils( CDispUtilsHelper *p )	{ return (CCoreDispInfo*)p; }


// CDispUtilsHelper implementation.
public:

	virtual CDispNeighbor*			GetEdgeNeighbor( int index );
	virtual CDispCornerNeighbors*	GetCornerNeighbors( int index );
	virtual const CPowerInfo* 		GetPowerInfo() const;
	virtual CDispUtilsHelper*		GetDispUtilsByIndex( int index );


public:

	//=========================================================================
	//
	// Creation/Destruction
	//
	CCoreDispInfo();
	~CCoreDispInfo();

	void InitSurf( int parentIndex, Vector points[4], Vector normals[4],
		           Vector2D texCoords[4], Vector2D lightCoords[4][4], int contents, int flags,
				   bool bGenerateSurfPointStart, Vector& startPoint, 
				   bool bHasMappingAxes, Vector& uAxis, Vector& vAxis );

	void InitDispInfo( int power, int minTess, float smoothingAngle, 
		               float *alphas, Vector *dispVectorField, float *dispDistances );

	// This just unpacks the contents of the verts into arrays and calls InitDispInfo.
	void InitDispInfo( int power, int minTess, float smoothingAngle, const CDispVert *pVerts, const CDispTri *pTris );
					   
//	bool Create( int creationFlags );
	bool Create( void );
	bool CreateWithoutLOD( void );

	//=========================================================================
	//
	// Parameter "Wrappers"
	//
	CCoreDispSurface*		GetSurface()		{ return &m_Surf; }
	const CCoreDispSurface*	GetSurface() const	{ return &m_Surf; }
	
	inline CCoreDispNode *GetNode( int index );

	inline void SetPower( int power );
	inline int GetPower( void ) const;
	inline int GetPostSpacing( void );
	inline int GetWidth( void );
	inline int GetHeight( void );
	inline int GetSize( void ) const;

	// Use this disp as a CDispUtils.
	void SetDispUtilsHelperInfo( CCoreDispInfo *pListBase, int listSize );

	void SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] ) { GetSurface()->SetNeighborData( edgeNeighbors, cornerNeighbors ); }

	// Get a corner point. Indexed by the CORNER_ defines.
	const CVertIndex&	GetCornerPointIndex( int index ) const	{ return GetPowerInfo()->GetCornerPointIndex( index ); }
	const Vector&		GetCornerPoint( int index ) const		{ return GetVert( VertIndexToInt( GetCornerPointIndex( index ) ) ); }

	inline void SetVert( int index, Vector const& vert );
	inline void GetVert( int index, Vector& vert ) const;
	
	inline const Vector& GetVert( int index ) const;
	inline const Vector& GetVert( const CVertIndex &index ) const;

	inline void GetFlatVert( int index, Vector& vert );
	
	inline void GetNormal( int index, Vector& normal ) const;
	inline const Vector& GetNormal( int index ) const;
	inline const Vector& GetNormal( const CVertIndex &index ) const;
	inline void SetNormal( int index, Vector const& normal );
	inline void SetNormal( const CVertIndex &index, Vector const& normal );
	
	inline void GetTangentS( int index, Vector& tangentS ) const;
	inline void GetTangentT( int index, Vector& tangentT ) const;
	inline void SetTexCoord( int index, Vector2D const& texCoord );
	inline void GetTexCoord( int index, Vector2D& texCoord ) const;
	
	inline void SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord );
	inline void GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const;

	inline void SetAlpha( int index, float alpha );
	inline float GetAlpha( int index );

	int GetTriCount( void );
	void GetTriIndices( int iTri, unsigned short &v1, unsigned short &v2, unsigned short &v3 );
	void SetTriIndices( int iTri, unsigned short v1, unsigned short v2, unsigned short v3 );
	void GetTriPos( int iTri, Vector &v1, Vector &v2, Vector &v3 );
	inline void SetTriTag( int iTri, unsigned short nTag )		{ m_pTris[iTri].m_uiTags |= nTag; }
	inline void ResetTriTag( int iTri, unsigned short nTag )	{ m_pTris[iTri].m_uiTags &= ~nTag; }
	inline void ToggleTriTag( int iTri, unsigned short nTag )   { m_pTris[iTri].m_uiTags ^= nTag; }
	inline bool IsTriTag( int iTri, unsigned short nTag )       { return ( ( m_pTris[iTri].m_uiTags & nTag ) != 0 ); }
	inline unsigned short GetTriTagValue( int iTri )			{ return m_pTris[iTri].m_uiTags; }
	inline void SetTriTagValue( int iTri, unsigned short nVal ) { m_pTris[iTri].m_uiTags = nVal; }

	bool IsTriWalkable( int iTri );
	bool IsTriBuildable( int iTri );

	inline void SetElevation( float elevation );
	inline float GetElevation( void );

	inline void ResetFieldVectors( void );
	inline void SetFieldVector( int index, Vector const &v );
	inline void GetFieldVector( int index, Vector& v );
	inline void ResetFieldDistances( void );
	inline void SetFieldDistance( int index, float dist );
	inline float GetFieldDistance( int index );

	inline void ResetSubdivPositions( void );
	inline void SetSubdivPosition( int ndx, Vector const &v );
	inline void GetSubdivPosition( int ndx, Vector& v );

	inline void ResetSubdivNormals( void );
	inline void SetSubdivNormal( int ndx, Vector const &v );
	inline void GetSubdivNormal( int ndx, Vector &v );

	inline void SetRenderIndexCount( int count );
	inline int GetRenderIndexCount( void );
	inline void SetRenderIndex( int index, int triIndex );
	inline int GetRenderIndex( int index );

	inline CoreDispVert_t *GetDispVert( int iVert )					{ return &m_pVerts[iVert]; }
	inline CoreDispVert_t *GetDispVertList();
	inline unsigned short *GetRenderIndexList( void );

	inline void SetTouched( bool touched );
	inline bool IsTouched( void );

	void CalcDispSurfCoords( bool bLightMap, int lightmapID );
	void GetPositionOnSurface( float u, float v, Vector &vPos, Vector *pNormal, float *pAlpha );

	void BaseFacePlaneToDispUV( Vector const &planePt, Vector2D &dispUV );
	void DispUVToSurf( Vector2D const &dispUV, Vector &surfPt );
	void DispUVToSurfWithAttribs( Vector2D const &dispUV, Vector &surfPt, Vector &normal, float &alpha );
	bool SurfToBaseFacePlane( Vector const &surfPt, Vector &planePt );

	const CDispCornerNeighbors*	GetCornerNeighbors( int iCorner ) const	{ return GetSurface()->GetCornerNeighbors( iCorner ); }
	const CDispNeighbor*	GetEdgeNeighbor( int iEdge ) const	{ return GetSurface()->GetEdgeNeighbor( iEdge ); }

	CBitVec<MAX_DISPVERTS>&			GetAllowedVerts()		{ return m_AllowedVerts; }
	const CBitVec<MAX_DISPVERTS>&	GetAllowedVerts() const	{ return m_AllowedVerts; }

	//=========================================================================
	//
	// friend functions
	//
	friend void SmoothNeighboringDispSurfNormals( CCoreDispInfo *pCoreDispInfoList, int listSize );

private:
																				// be changed to match the paint normal next pass)
	// LOD/collision node data
	CCoreDispNode		*m_Nodes;		// LOD quad-tree nodes

	float				m_Elevation;	// distance along the subdivision normal (should

	// defines the size of the displacement surface
	int					m_Power;		// "size" of the displacement map

	// base surface data
	CCoreDispSurface	m_Surf;			// surface containing displacement data
	                                    // be changed to match the paint normal next pass)
	// Vertex data..
	CoreDispVert_t		*m_pVerts;

	// Triangle data..
	CoreDispTri_t		*m_pTris;

	// render specific data
	int					m_RenderIndexCount;		// number of indices used in rendering
	unsigned short		*m_RenderIndices;		// rendering index list (list of triangles)
	int					m_RenderCounter;		// counter to verify surfaces are renderered/collided with only once per frame

	// utility data
	bool				m_bTouched;				// touched flag
	CCoreDispInfo		*m_pNext;				// used for chaining

	// The list that this disp is in (used for CDispUtils::IHelper implementation).
	CCoreDispInfo		*m_pListBase;
	int					m_ListSize;

	CBitVec<MAX_DISPVERTS>	m_AllowedVerts;		// Built in VBSP. Defines which verts are allowed to exist based on what the neighbors are.


	//=========================================================================
	//
	// Creation Functions
	//

	void GenerateDispSurf( void );
	void GenerateDispSurfNormals( void );
	void GenerateDispSurfTangentSpaces( void );
	bool DoesEdgeExist( int indexRow, int indexCol, int direction, int postSpacing );
	void CalcNormalFromEdges( int indexRow, int indexCol, bool bIsEdge[4], Vector& normal );
	void CalcDispSurfAlphas( void );
	void GenerateLODTree( void );
	void CalcVertIndicesAtNodes( int nodeIndex );
	int GetNodeVertIndexFromParentIndex( int level, int parentVertIndex, int direction );
	void CalcNodeInfo( int nodeIndex, int terminationLevel );
	void CalcNeighborVertIndicesAtNode( int nodeIndex, int level );
	void CalcNeighborNodeIndicesAtNode( int nodeIndex, int level );
	void CalcErrorTermAtNode( int nodeIndex, int level );
	float GetMaxErrorFromChildren( int nodeIndex, int level );
	void CalcBoundingBoxAtNode( int nodeIndex );
	void CalcMinMaxBoundingBoxAtNode( int nodeIndex, Vector& bMin, Vector& bMax );
	void CalcTriSurfInfoAtNode( int nodeIndex );
	void CalcTriSurfIndices( int nodeIndex, int indices[8][3] );
	void CalcTriSurfBoundingBoxes( int nodeIndex, int indices[8][3] );
	void CalcRayBoundingBoxes( int nodeIndex, int indices[8][3] );
	void CalcTriSurfPlanes( int nodeIndex, int indices[8][3] );
	void GenerateCollisionData( void );
	void GenerateCollisionSurface( void );

	void CreateBoundingBoxes( CoreDispBBox_t *pBBox, int count );

	void BuildTriTLtoBR( int ndx );
	void BuildTriBLtoTR( int ndx );

	void InitTris( void );
	void CreateTris( void );
};


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetPower( int power )
{
	m_Power = power;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispInfo::GetPower( void ) const
{
	return m_Power;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispInfo::GetPostSpacing( void )
{
	return ( ( 1 << m_Power ) + 1 );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispInfo::GetWidth( void )
{
	return ( ( 1 << m_Power ) + 1 );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispInfo::GetHeight( void )
{
	return ( ( 1 << m_Power ) + 1 );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispInfo::GetSize( void ) const
{
    return ( ( ( 1 << m_Power ) + 1 ) * ( ( 1 << m_Power ) + 1 ) );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetVert( int index, Vector const &vert )
{
	Assert( index >= 0 );
	Assert( index < MAX_VERT_COUNT );
	VectorCopy( vert, m_pVerts[index].m_Vert );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::GetVert( int index, Vector& vert ) const
{
	Assert( index >= 0 );
	Assert( index < MAX_VERT_COUNT );
	VectorCopy( m_pVerts[index].m_Vert, vert );
}


inline const Vector& CCoreDispInfo::GetVert( int index ) const
{
	Assert( index >= 0 );
	Assert( index < MAX_VERT_COUNT );
	return m_pVerts[index].m_Vert;
}

inline const Vector& CCoreDispInfo::GetVert( const CVertIndex &index ) const
{
	return GetVert( VertIndexToInt( index ) );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::GetFlatVert( int index, Vector& vert )
{
	Assert( index >= 0 );
	Assert( index < MAX_VERT_COUNT );
	VectorCopy( m_pVerts[index].m_FlatVert, vert );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetNormal( int index, Vector const &normal )
{
	Assert( index >= 0 );
	Assert( index < MAX_VERT_COUNT );
	VectorCopy( normal, m_pVerts[index].m_Normal );
}

	
inline void CCoreDispInfo::SetNormal( const CVertIndex &index, Vector const &normal )
{
	SetNormal( VertIndexToInt( index ), normal );
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::GetNormal( int index, Vector& normal ) const
{
	Assert( index >= 0 );
	Assert( index < MAX_VERT_COUNT );
	VectorCopy( m_pVerts[index].m_Normal, normal );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline const Vector& CCoreDispInfo::GetNormal( int index ) const
{
	Assert( index >= 0 );
	Assert( index < MAX_VERT_COUNT );
	return m_pVerts[index].m_Normal;
}


inline const Vector& CCoreDispInfo::GetNormal( const CVertIndex &index ) const
{
	return GetNormal( VertIndexToInt( index ) );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::GetTangentS( int index, Vector& tangentS ) const
{
	Assert( index >= 0 );
	Assert( index < GetSize() );
	VectorCopy( m_pVerts[index].m_TangentS, tangentS );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::GetTangentT( int index, Vector& tangentT ) const
{
	Assert( index >= 0 );
	Assert( index < GetSize() );
	VectorCopy( m_pVerts[index].m_TangentT, tangentT );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetTexCoord( int index, Vector2D const& texCoord )
{
	Assert( index >= 0 );
	Assert( index < GetSize() );
	Vector2DCopy( texCoord, m_pVerts[index].m_TexCoord );
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::GetTexCoord( int index, Vector2D& texCoord ) const
{
	Assert( index >= 0 );
	Assert( index < GetSize() );
	Vector2DCopy( m_pVerts[index].m_TexCoord, texCoord );
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord )
{
	Assert( index >= 0 );
	Assert( index < GetSize() );
	Assert( bumpIndex >= 0 );
	Assert( bumpIndex < NUM_BUMP_VECTS + 1 );
	Vector2DCopy( luxelCoord, m_pVerts[index].m_LuxelCoords[bumpIndex] );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const 
{
	Assert( index >= 0 );
	Assert( index < MAX_VERT_COUNT );
	Assert( bumpIndex >= 0 );
	Assert( bumpIndex < NUM_BUMP_VECTS + 1 );
	Vector2DCopy( m_pVerts[index].m_LuxelCoords[bumpIndex], luxelCoord );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetAlpha( int index, float alpha )
{
	Assert( index >= 0 );
	Assert( index < MAX_VERT_COUNT );
	m_pVerts[index].m_Alpha = alpha;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline float CCoreDispInfo::GetAlpha( int index )
{
	Assert( index >= 0 );
	Assert( index < MAX_VERT_COUNT );
	return m_pVerts[index].m_Alpha;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetElevation( float elevation )
{
	m_Elevation = elevation;
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline float CCoreDispInfo::GetElevation( void )
{
	return m_Elevation;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::ResetFieldVectors( void )
{
//	Vector normal;
//	m_Surf.GetNormal( normal );

	int size = GetSize();
	for( int i = 0; i < size; i++ )
	{
		m_pVerts[i].m_FieldVector.Init();
//		m_FieldVectors[i] = normal;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetFieldVector( int index, Vector const &v )
{
	Assert( index >= 0 );
	Assert( index < MAX_VERT_COUNT );
	VectorCopy( v, m_pVerts[index].m_FieldVector ); 
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::GetFieldVector( int index, Vector& v )
{
	Assert( index >= 0 );
	Assert( index < MAX_VERT_COUNT );
	VectorCopy( m_pVerts[index].m_FieldVector, v ); 
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::ResetSubdivPositions( void )
{
	int size = GetSize();
	for( int i = 0; i < size; i++ )
	{
		m_pVerts[i].m_SubdivPos.Init();
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetSubdivPosition( int ndx, Vector const &v )
{
	Assert( ndx >= 0 );
	Assert( ndx < MAX_VERT_COUNT );
	m_pVerts[ndx].m_SubdivPos = v;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::GetSubdivPosition( int ndx, Vector& v )
{
	Assert( ndx >= 0 );
	Assert( ndx < MAX_VERT_COUNT );
	v = m_pVerts[ndx].m_SubdivPos;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::ResetSubdivNormals( void )
{
	Vector normal;
	m_Surf.GetNormal( normal );

	int size = GetSize();
	for( int i = 0; i < size; i++ )
	{
		m_pVerts[i].m_SubdivNormal = normal;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetSubdivNormal( int ndx, Vector const &v )
{
	Assert( ndx >= 0 );
	Assert( ndx < MAX_VERT_COUNT );
	m_pVerts[ndx].m_SubdivNormal = v;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::GetSubdivNormal( int ndx, Vector &v )
{
	Assert( ndx >= 0 );
	Assert( ndx < MAX_VERT_COUNT );
	v = m_pVerts[ndx].m_SubdivNormal;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::ResetFieldDistances( void )
{
	int size = GetSize();
	for( int i = 0; i < size; i++ )
	{
		m_pVerts[i].m_FieldDistance = 0.0f;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetFieldDistance( int index, float dist )
{
	Assert( index >= 0 );
	Assert( index < GetSize() );
	m_pVerts[index].m_FieldDistance = dist;
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline float CCoreDispInfo::GetFieldDistance( int index )
{
	Assert( index >= 0 );
	Assert( index < GetSize() );
	return m_pVerts[index].m_FieldDistance;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetRenderIndexCount( int count )
{
	m_RenderIndexCount = count;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispInfo::GetRenderIndexCount( void )
{
	return m_RenderIndexCount;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetRenderIndex( int index, int triIndex )
{
	Assert( index >= 0 );
	Assert( index < ( MAX_VERT_COUNT*2*3) );
	m_RenderIndices[index] = triIndex;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int CCoreDispInfo::GetRenderIndex( int index )
{
	Assert( index >= 0 );
	Assert( index < ( MAX_VERT_COUNT*2*3) );
	return m_RenderIndices[index];
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline CoreDispVert_t *CCoreDispInfo::GetDispVertList()
{
	return m_pVerts;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline unsigned short *CCoreDispInfo::GetRenderIndexList( void )
{
	return &m_RenderIndices[0];
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CCoreDispInfo::SetTouched( bool touched )
{
	m_bTouched = touched;
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline bool CCoreDispInfo::IsTouched( void )
{
	return m_bTouched;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline CCoreDispNode *CCoreDispInfo::GetNode( int index )
{
	Assert( index >= 0 );
	Assert( index < MAX_NODE_COUNT );
	return &m_Nodes[index];
}



#endif // BUILDDISP_H
