source-engine/hammer/dispsubdiv.cpp

1129 lines
35 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include <stdafx.h>
#include "DispSubdiv.h"
#include "MapDisp.h"
#include "UtlLinkedList.h"
#include "utlvector.h"
#include "GlobalFunctions.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//=============================================================================
//
// Editable Displacement Subdivision Mesh Implementation
//
class CEditDispSubdivMesh : public IEditDispSubdivMesh
{
public: // functions
void Init( void );
void Shutdown( void );
void AddDispTo( CMapDisp *pDisp );
void GetDispFrom( CMapDisp *pDisp );
void DoCatmullClarkSubdivision( void );
public: // typedefs, enums, structs
enum { EDITDISP_QUADSIZE = 4 }; // should be in mapdisp (general define)
private: // typedefs, enums, structs
typedef int SubdivPointHandle_t;
typedef int SubdivEdgeHandle_t;
typedef int SubdivQuadHandle_t;
enum { NUM_SUBDIV_LEVELS = 4 }; // number of subdivision levels
enum
{
SUBDIV_DISPPOINTS = 512,
SUBDIV_DISPEDGES = 1024,
SUBDIV_DISPQUADS = 512
};
enum
{
SUBDIV_POINTORDINARY = 0,
SUBDIV_POINTCORNER = 1,
SUBDIV_POINTCREASE = 2
};
struct SubdivPoint_t
{
Vector m_vPoint;
Vector m_vNormal;
Vector m_vNewPoint;
Vector m_vNewNormal;
unsigned short m_uType;
unsigned short m_uValence;
SubdivEdgeHandle_t m_EdgeHandles[EDITDISP_QUADSIZE*4];
};
struct SubdivEdge_t
{
Vector m_vNewEdgePoint;
Vector m_vNewEdgeNormal;
SubdivPointHandle_t m_PointHandles[2];
SubdivQuadHandle_t m_QuadHandles[2];
float m_flSharpness;
bool m_bActive;
};
struct SubdivQuad_t
{
// generated
Vector m_vCentroid; // quad center
Vector m_vNormal; // quad normal
// linkage
SubdivQuadHandle_t m_ndxParent; // parent quad index
SubdivQuadHandle_t m_ndxChild[EDITDISP_QUADSIZE]; // chilren (4 of them) indices
// quad data
SubdivPointHandle_t m_PointHandles[EDITDISP_QUADSIZE]; // point indices - unique list
SubdivEdgeHandle_t m_EdgeHandles[EDITDISP_QUADSIZE]; // edge indices - unique list
// disp/quad mapping
EditDispHandle_t m_EditDispHandle;
short m_Level; // level of quad in the hierarchy (tree)
short m_QuadIndices[EDITDISP_QUADSIZE]; // quad indices (in the X x X displacement surface)
};
private: // functions
SubdivPoint_t *GetPoint( SubdivPointHandle_t ptHandle );
SubdivEdge_t *GetEdge( SubdivEdgeHandle_t edgeHandle );
SubdivQuad_t *GetQuad( SubdivQuadHandle_t quadHandle );
void Point_Init( SubdivPointHandle_t ptHandle );
void Point_CalcNewPoint( SubdivPointHandle_t ptHandle );
void Point_PointOrdinary( SubdivPoint_t *pPoint );
void Point_PointCorner( SubdivPoint_t *pPoint );
void Point_PointCrease( SubdivPoint_t *pPoint );
void Edge_Init( SubdivEdgeHandle_t edgeHandle );
void Edge_CalcNewPoint( SubdivEdgeHandle_t edgeHandle );
void Quad_Init( SubdivQuadHandle_t quadHandle );
void Quad_CalcCentroid( SubdivQuadHandle_t quadHandle );
void Quad_CalcNormal( SubdivQuadHandle_t quadHandle );
bool CompareSubdivPoints( Vector const &pt1, Vector const &pt2, float flTolerance );
bool CompareSubdivEdges( SubdivPointHandle_t ptEdge0Handle0, SubdivPointHandle_t ptEdge0Handle1,
SubdivPointHandle_t ptEdge1Handle0, SubdivPointHandle_t ptEdge1Handle1 );
SubdivPointHandle_t BuildSubdivPoint( Vector const &vPoint, Vector const &vNormal );
SubdivEdgeHandle_t BuildSubdivEdge( int ndxEdge, SubdivQuadHandle_t quadHandle,
SubdivQuadHandle_t parentHandle, int ndxChild );
SubdivQuadHandle_t BuildSubdivQuad( int ndxChild, SubdivQuadHandle_t parentHandle );
void CatmullClarkSubdivision( void );
void UpdateSubdivisionHierarchy( int ndxLevel );
private: // variables
CUtlLinkedList<SubdivPoint_t, SubdivPointHandle_t> m_Points;
CUtlLinkedList<SubdivEdge_t, SubdivEdgeHandle_t> m_Edges;
CUtlLinkedList<SubdivQuad_t, SubdivQuadHandle_t> m_Quads;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
IEditDispSubdivMesh *CreateEditDispSubdivMesh( void )
{
return new CEditDispSubdivMesh;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void DestroyEditDispSubdivMesh( IEditDispSubdivMesh **pSubdivMesh )
{
if ( *pSubdivMesh )
{
delete *pSubdivMesh;
*pSubdivMesh = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEditDispSubdivMesh::SubdivPoint_t *CEditDispSubdivMesh::GetPoint( SubdivPointHandle_t ptHandle )
{
if ( !m_Points.IsValidIndex( ptHandle ) )
return NULL;
return &m_Points.Element( ptHandle );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEditDispSubdivMesh::SubdivEdge_t *CEditDispSubdivMesh::GetEdge( SubdivEdgeHandle_t edgeHandle )
{
if ( !m_Edges.IsValidIndex( edgeHandle ) )
return NULL;
return &m_Edges.Element( edgeHandle );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEditDispSubdivMesh::SubdivQuad_t *CEditDispSubdivMesh::GetQuad( SubdivQuadHandle_t quadHandle )
{
if ( !m_Quads.IsValidIndex( quadHandle ) )
return NULL;
return &m_Quads.Element( quadHandle );
}
//=============================================================================
//
// Subdivision Edit Displacement Point Functions
//
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::Point_Init( SubdivPointHandle_t ptHandle )
{
SubdivPoint_t *pPoint = GetPoint( ptHandle );
if ( pPoint )
{
VectorClear( pPoint->m_vPoint );
VectorClear( pPoint->m_vNormal );
VectorClear( pPoint->m_vNewPoint );
VectorClear( pPoint->m_vNewNormal );
pPoint->m_uType = (unsigned short)-1;
pPoint->m_uValence = 0;
for ( int ndxEdge = 0; ndxEdge < ( EDITDISP_QUADSIZE*2 ); ndxEdge++ )
{
pPoint->m_EdgeHandles[ndxEdge] = m_Edges.InvalidIndex();
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::Point_CalcNewPoint( SubdivPointHandle_t ptHandle )
{
// get the point to act on
SubdivPoint_t *pPoint = GetPoint( ptHandle );
if ( !pPoint )
return;
switch ( pPoint->m_uType )
{
case SUBDIV_POINTORDINARY: { Point_PointOrdinary( pPoint ); break; }
case SUBDIV_POINTCORNER: { Point_PointCorner( pPoint ); break; }
case SUBDIV_POINTCREASE: { Point_PointCrease( pPoint ); break; }
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::Point_PointOrdinary( SubdivPoint_t *pPoint )
{
//
// accumulate the edge data and multiply by the valence (coincident edge)
// ratio (squared)
//
Vector edgeAccumPoint( 0.0f, 0.0f, 0.0f );
Vector edgeAccumNormal( 0.0f, 0.0f, 0.0f );
for ( int ndxEdge = 0; ndxEdge < pPoint->m_uValence; ndxEdge++ )
{
SubdivEdge_t *pEdge = GetEdge( pPoint->m_EdgeHandles[ndxEdge] );
if ( pEdge )
{
VectorAdd( edgeAccumPoint, pEdge->m_vNewEdgePoint, edgeAccumPoint );
VectorAdd( edgeAccumNormal, pEdge->m_vNewEdgeNormal, edgeAccumNormal );
}
}
float ratio = 1.0f / ( float )( pPoint->m_uValence * pPoint->m_uValence );
VectorScale( edgeAccumPoint, ratio, edgeAccumPoint );
VectorScale( edgeAccumNormal, ratio, edgeAccumNormal );
//
// accumlate the centroid data from all neighboring quads and multiply by
// the valence (coincident edge) ratio (squared)
//
int quadListCount = 0;
SubdivQuadHandle_t quadList[32];
for ( int ndxEdge = 0; ndxEdge < pPoint->m_uValence; ndxEdge++ )
{
SubdivEdge_t *pEdge = GetEdge( pPoint->m_EdgeHandles[ndxEdge] );
if ( pEdge )
{
for ( int ndxQuad = 0; ndxQuad < 2; ndxQuad++ )
{
if ( pEdge->m_QuadHandles[ndxQuad] != m_Quads.InvalidIndex() )
{
int ndxList;
for ( ndxList = 0; ndxList < quadListCount; ndxList++ )
{
if( pEdge->m_QuadHandles[ndxQuad] == quadList[ndxList] )
break;
}
if( ndxList == quadListCount )
{
quadList[quadListCount] = pEdge->m_QuadHandles[ndxQuad];
quadListCount++;
}
}
}
}
}
Vector centroidAccum( 0.0f, 0.0f, 0.0f );
for ( int ndxQuad = 0; ndxQuad < quadListCount; ndxQuad++ )
{
SubdivQuadHandle_t quadHandle = quadList[ndxQuad];
Quad_CalcCentroid( quadHandle );
SubdivQuad_t *pQuad = GetQuad( quadHandle );
VectorAdd( centroidAccum, pQuad->m_vCentroid, centroidAccum );
}
VectorScale( centroidAccum, ratio, centroidAccum );
//
//
//
ratio = ( ( float )pPoint->m_uValence - 2.0f ) / ( float )pPoint->m_uValence;
VectorScale( pPoint->m_vPoint, ratio, pPoint->m_vNewPoint );
VectorAdd( pPoint->m_vNewPoint, edgeAccumPoint, pPoint->m_vNewPoint );
VectorAdd( pPoint->m_vNewPoint, centroidAccum, pPoint->m_vNewPoint );
VectorScale( pPoint->m_vNormal, ratio, pPoint->m_vNewNormal );
VectorAdd( pPoint->m_vNewNormal, edgeAccumNormal, pPoint->m_vNewNormal );
VectorAdd( pPoint->m_vNewNormal, centroidAccum, pPoint->m_vNewNormal );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::Point_PointCorner( SubdivPoint_t *pPoint )
{
VectorCopy( pPoint->m_vPoint, pPoint->m_vNewPoint );
VectorCopy( pPoint->m_vNormal, pPoint->m_vNewNormal );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::Point_PointCrease( SubdivPoint_t *pPoint )
{
//
// accumulate the edge data and multiply by the valence (coincident edge)
// ratio (squared)
//
Vector edgeAccumPoint( 0.0f, 0.0f, 0.0f );
Vector edgeAccumNormal( 0.0f, 0.0f, 0.0f );
for ( int ndxEdge = 0; ndxEdge < pPoint->m_uValence; ndxEdge++ )
{
SubdivEdge_t *pEdge = GetEdge( pPoint->m_EdgeHandles[ndxEdge] );
if ( pEdge && ( pEdge->m_flSharpness > 0.0f ) )
{
VectorAdd( edgeAccumPoint, pEdge->m_vNewEdgePoint, edgeAccumPoint );
VectorAdd( edgeAccumNormal, pEdge->m_vNewEdgeNormal, edgeAccumNormal );
}
}
//
//
//
VectorScale( pPoint->m_vPoint, 6.0f, pPoint->m_vNewPoint );
VectorAdd( pPoint->m_vNewPoint, edgeAccumPoint, pPoint->m_vNewPoint );
VectorScale( pPoint->m_vNewPoint, 0.125f, pPoint->m_vNewPoint );
VectorScale( pPoint->m_vNormal, 6.0f, pPoint->m_vNewNormal );
VectorAdd( pPoint->m_vNewNormal, edgeAccumNormal, pPoint->m_vNewNormal );
VectorScale( pPoint->m_vNewNormal, 0.125f, pPoint->m_vNewNormal );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::Edge_Init( SubdivEdgeHandle_t edgeHandle )
{
SubdivEdge_t *pEdge = GetEdge( edgeHandle );
if ( pEdge )
{
VectorClear( pEdge->m_vNewEdgePoint );
VectorClear( pEdge->m_vNewEdgeNormal );
pEdge->m_flSharpness = 1.0f;
pEdge->m_bActive = false;
for ( int ndx = 0; ndx < 2; ndx++ )
{
pEdge->m_PointHandles[ndx] = m_Points.InvalidIndex();
pEdge->m_QuadHandles[ndx] = m_Quads.InvalidIndex();
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::Edge_CalcNewPoint( SubdivEdgeHandle_t edgeHandle )
{
SubdivEdge_t *pEdge = GetEdge( edgeHandle );
if ( !pEdge )
return;
if ( !pEdge->m_bActive )
return;
//
// get edge points
//
SubdivPoint_t *pPoint0 = GetPoint( pEdge->m_PointHandles[0] );
SubdivPoint_t *pPoint1 = GetPoint( pEdge->m_PointHandles[1] );
if ( !pPoint0 || !pPoint1 )
return;
//
// calculate the "sharp" new edge point
//
Vector vSharpPoint( 0.0f, 0.0f, 0.0f );
VectorAdd( pPoint0->m_vPoint, pPoint1->m_vPoint, vSharpPoint );
VectorScale( vSharpPoint, 0.5f, vSharpPoint );
Vector vSharpNormal( 0.0f, 0.0f, 0.0f );
VectorAdd( pPoint0->m_vNormal, pPoint1->m_vNormal, vSharpNormal );
VectorNormalize( vSharpNormal );
//
// calculate the "smooth" new edge point (if necessary)
//
Vector vSmoothPoint( 0.0f, 0.0f, 0.0f );
Vector vSmoothNormal( 0.0f, 0.0f, 0.0f );
if ( ( pEdge->m_QuadHandles[1] != m_Edges.InvalidIndex() ) && ( pEdge->m_flSharpness != 1.0f ) )
{
Quad_CalcCentroid( pEdge->m_QuadHandles[0] );
Quad_CalcCentroid( pEdge->m_QuadHandles[1] );
Quad_CalcNormal( pEdge->m_QuadHandles[0] );
Quad_CalcNormal( pEdge->m_QuadHandles[1] );
SubdivQuad_t *pQuad0 = GetQuad( pEdge->m_QuadHandles[0] );
SubdivQuad_t *pQuad1 = GetQuad( pEdge->m_QuadHandles[1] );
VectorAdd( pPoint0->m_vPoint, pPoint1->m_vPoint, vSmoothPoint );
VectorAdd( vSmoothPoint, pQuad0->m_vCentroid, vSmoothPoint );
VectorAdd( vSmoothPoint, pQuad1->m_vCentroid, vSmoothPoint );
VectorScale( vSmoothPoint, 0.25f, vSmoothPoint );
VectorAdd( pPoint0->m_vNormal, pPoint1->m_vNormal, vSmoothNormal );
VectorAdd( vSmoothNormal, pQuad0->m_vNormal, vSmoothNormal );
VectorAdd( vSmoothNormal, pQuad1->m_vNormal, vSmoothNormal );
VectorNormalize( vSmoothNormal );
}
else
{
pEdge->m_flSharpness = 1.0f;
Quad_CalcCentroid( pEdge->m_QuadHandles[0] );
Quad_CalcNormal( pEdge->m_QuadHandles[0] );
}
//
// calculate the new edge point
//
// ( 1 - edge(sharpness) ) * vSmooth + edge(sharpness) * vSharp
//
VectorScale( vSmoothPoint, ( 1.0f - pEdge->m_flSharpness ), vSmoothPoint );
VectorScale( vSharpPoint, pEdge->m_flSharpness, vSharpPoint );
VectorAdd( vSmoothPoint, vSharpPoint, pEdge->m_vNewEdgePoint );
VectorScale( vSmoothNormal, ( 1.0f - pEdge->m_flSharpness ), vSmoothNormal );
VectorScale( vSharpNormal, pEdge->m_flSharpness, vSharpNormal );
VectorAdd( vSmoothNormal, vSharpNormal, pEdge->m_vNewEdgeNormal );
VectorNormalize( pEdge->m_vNewEdgeNormal );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::Quad_Init( SubdivQuadHandle_t quadHandle )
{
SubdivQuad_t *pQuad = GetQuad( quadHandle );
if ( pQuad )
{
VectorClear( pQuad->m_vCentroid );
VectorClear( pQuad->m_vNormal );
pQuad->m_ndxParent = m_Quads.InvalidIndex();
pQuad->m_EditDispHandle = EDITDISPHANDLE_INVALID;
pQuad->m_Level = -1;
for ( int ndx = 0; ndx < EDITDISP_QUADSIZE; ndx++ )
{
pQuad->m_ndxChild[ndx] = m_Quads.InvalidIndex();
pQuad->m_PointHandles[ndx] = m_Points.InvalidIndex();
pQuad->m_EdgeHandles[ndx] = m_Edges.InvalidIndex();
pQuad->m_QuadIndices[ndx] = -1;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::Quad_CalcCentroid( SubdivQuadHandle_t quadHandle )
{
SubdivQuad_t *pQuad = GetQuad( quadHandle );
if ( pQuad )
{
VectorClear( pQuad->m_vCentroid );
for ( int ndxPt = 0; ndxPt < EDITDISP_QUADSIZE; ndxPt++ )
{
SubdivPoint_t *pPoint = GetPoint( pQuad->m_PointHandles[ndxPt] );
VectorAdd( pQuad->m_vCentroid, pPoint->m_vPoint, pQuad->m_vCentroid );
}
VectorScale( pQuad->m_vCentroid, 0.25f, pQuad->m_vCentroid );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::Quad_CalcNormal( SubdivQuadHandle_t quadHandle )
{
SubdivQuad_t *pQuad = GetQuad( quadHandle );
if ( pQuad )
{
SubdivPoint_t *pPoints[3];
Vector edges[2];
pPoints[0] = GetPoint( pQuad->m_PointHandles[0] );
pPoints[1] = GetPoint( pQuad->m_PointHandles[1] );
pPoints[2] = GetPoint( pQuad->m_PointHandles[2] );
VectorSubtract( pPoints[1]->m_vPoint, pPoints[0]->m_vPoint, edges[0] );
VectorSubtract( pPoints[2]->m_vPoint, pPoints[0]->m_vPoint, edges[1] );
CrossProduct( edges[1], edges[0], pQuad->m_vNormal );
VectorNormalize( pQuad->m_vNormal );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEditDispSubdivMesh::CompareSubdivPoints( Vector const &pt1, Vector const &pt2,
float flTolerance )
{
for ( int axis = 0 ; axis < 3 ; axis++ )
{
if ( fabs( pt1[axis] - pt2[axis] ) > flTolerance )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEditDispSubdivMesh::CompareSubdivEdges( SubdivPointHandle_t ptEdge0Handle0,
SubdivPointHandle_t ptEdge0Handle1,
SubdivPointHandle_t ptEdge1Handle0,
SubdivPointHandle_t ptEdge1Handle1 )
{
if ( ( ( ptEdge0Handle0 == ptEdge1Handle0 ) && ( ptEdge0Handle1 == ptEdge1Handle1 ) ) ||
( ( ptEdge0Handle0 == ptEdge1Handle1 ) && ( ptEdge0Handle1 == ptEdge1Handle0 ) ) )
return true;
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEditDispSubdivMesh::SubdivPointHandle_t CEditDispSubdivMesh::BuildSubdivPoint( Vector const &vPoint,
Vector const &vPointNormal )
{
//
// build a "unique" point
//
SubdivPointHandle_t ptHandle;
for ( ptHandle = m_Points.Head(); ptHandle != m_Points.InvalidIndex();
ptHandle = m_Points.Next( ptHandle ) )
{
SubdivPoint_t *pPoint = GetPoint( ptHandle );
if ( pPoint )
{
// compare (positions)
if ( CompareSubdivPoints( vPoint, pPoint->m_vPoint, 0.1f ) )
return ptHandle;
}
}
ptHandle = m_Points.AddToTail();
Point_Init( ptHandle );
SubdivPoint_t *pPoint = GetPoint( ptHandle );
VectorCopy( vPoint, pPoint->m_vPoint );
VectorCopy( vPointNormal, pPoint->m_vNormal );
return ptHandle;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEditDispSubdivMesh::SubdivEdgeHandle_t CEditDispSubdivMesh::BuildSubdivEdge( int ndxEdge, SubdivQuadHandle_t quadHandle,
SubdivQuadHandle_t parentHandle, int ndxChild )
{
// get the quad
SubdivQuad_t *pQuad = GetQuad( quadHandle );
if ( !pQuad )
return m_Edges.InvalidIndex();
//
// define a unique edge (m_PointHandlesX2, m_QuadHandle)
//
SubdivEdgeHandle_t edgeHandle;
for ( edgeHandle = m_Edges.Head(); edgeHandle != m_Edges.InvalidIndex();
edgeHandle = m_Edges.Next( edgeHandle ) )
{
SubdivEdge_t *pEdge = GetEdge( edgeHandle );
if ( pEdge )
{
// compare (point handles)
if ( CompareSubdivEdges( pQuad->m_PointHandles[ndxEdge], pQuad->m_PointHandles[(ndxEdge+1)%4],
pEdge->m_PointHandles[0], pEdge->m_PointHandles[1] ) )
{
// check to see if the quad is quad 0 or 1 (or if it needs to be quad 1)
if ( ( pEdge->m_QuadHandles[0] != quadHandle ) &&
( pEdge->m_QuadHandles[1] == m_Quads.InvalidIndex() ) )
{
pEdge->m_QuadHandles[1] = quadHandle;
pEdge->m_flSharpness = 0.0f; // smooth edge (between two subdiv quads)
}
return edgeHandle;
}
}
}
edgeHandle = m_Edges.AddToTail();
Edge_Init( edgeHandle );
SubdivEdge_t *pEdge = GetEdge( edgeHandle );
pEdge->m_PointHandles[0] = pQuad->m_PointHandles[ndxEdge];
pEdge->m_PointHandles[1] = pQuad->m_PointHandles[(ndxEdge+1)%4];
pEdge->m_QuadHandles[0] = quadHandle;
pEdge->m_bActive = true;
// extra data for children (get edge sharpness from parent or
// it may be an internal edge and its sharpness will be 0)
if( ndxChild != -1 )
{
if ( ( ndxEdge == ndxChild ) || ( ndxEdge == ( (ndxChild+3)%4 ) ) )
{
SubdivQuad_t *pParentQuad = GetQuad( parentHandle );
if ( pParentQuad )
{
SubdivEdge_t *pParentEdge = GetEdge( pParentQuad->m_EdgeHandles[ndxEdge] );
pEdge->m_flSharpness = pParentEdge->m_flSharpness;
}
}
else
{
pEdge->m_flSharpness = 0.0f;
}
}
return edgeHandle;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEditDispSubdivMesh::SubdivQuadHandle_t CEditDispSubdivMesh::BuildSubdivQuad( int ndxChild,
SubdivQuadHandle_t parentHandle )
{
// get parent quad
SubdivQuad_t *pParentQuad = GetQuad( parentHandle );
if( !pParentQuad )
return m_Quads.InvalidIndex();
// allocate a new quad
SubdivQuadHandle_t quadHandle = m_Quads.AddToTail();
Quad_Init( quadHandle );
SubdivQuad_t *pQuad = GetQuad( quadHandle );
pQuad->m_ndxParent = parentHandle;
pQuad->m_EditDispHandle = pParentQuad->m_EditDispHandle;
pQuad->m_Level = pParentQuad->m_Level + 1;
switch ( ndxChild )
{
case 0:
{
// displacement quad indices
pQuad->m_QuadIndices[0] = pParentQuad->m_QuadIndices[0];
pQuad->m_QuadIndices[1] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[1] ) * 0.5f;
pQuad->m_QuadIndices[2] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[2] ) * 0.5f;
pQuad->m_QuadIndices[3] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[3] ) * 0.5f;
// new verts
SubdivEdge_t *pEdge0 = GetEdge( pParentQuad->m_EdgeHandles[0] );
SubdivEdge_t *pEdge3 = GetEdge( pParentQuad->m_EdgeHandles[3] );
if ( pEdge0 && pEdge3 )
{
pQuad->m_PointHandles[0] = pParentQuad->m_PointHandles[0];
pQuad->m_PointHandles[1] = BuildSubdivPoint( pEdge0->m_vNewEdgePoint, pEdge0->m_vNewEdgeNormal );
pQuad->m_PointHandles[2] = BuildSubdivPoint( pParentQuad->m_vCentroid, pParentQuad->m_vNormal );
pQuad->m_PointHandles[3] = BuildSubdivPoint( pEdge3->m_vNewEdgePoint, pEdge3->m_vNewEdgeNormal );
}
break;
}
case 1:
{
// displacement quad indices
pQuad->m_QuadIndices[0] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[1] ) * 0.5f;
pQuad->m_QuadIndices[1] = pParentQuad->m_QuadIndices[1];
pQuad->m_QuadIndices[2] = ( pParentQuad->m_QuadIndices[1] + pParentQuad->m_QuadIndices[2] ) * 0.5f;
pQuad->m_QuadIndices[3] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[2] ) * 0.5f;
// new verts
SubdivEdge_t *pEdge0 = GetEdge( pParentQuad->m_EdgeHandles[0] );
SubdivEdge_t *pEdge1 = GetEdge( pParentQuad->m_EdgeHandles[1] );
if ( pEdge0 && pEdge1 )
{
pQuad->m_PointHandles[0] = BuildSubdivPoint( pEdge0->m_vNewEdgePoint, pEdge0->m_vNewEdgeNormal );
pQuad->m_PointHandles[1] = pParentQuad->m_PointHandles[1];
pQuad->m_PointHandles[2] = BuildSubdivPoint( pEdge1->m_vNewEdgePoint, pEdge1->m_vNewEdgeNormal );
pQuad->m_PointHandles[3] = BuildSubdivPoint( pParentQuad->m_vCentroid, pParentQuad->m_vNormal );
}
break;
}
case 2:
{
// displacement quad indices
pQuad->m_QuadIndices[0] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[2] ) * 0.5f;
pQuad->m_QuadIndices[1] = ( pParentQuad->m_QuadIndices[1] + pParentQuad->m_QuadIndices[2] ) * 0.5f;
pQuad->m_QuadIndices[2] = pParentQuad->m_QuadIndices[2];
pQuad->m_QuadIndices[3] = ( pParentQuad->m_QuadIndices[2] + pParentQuad->m_QuadIndices[3] ) * 0.5f;
// new verts
SubdivEdge_t *pEdge1 = GetEdge( pParentQuad->m_EdgeHandles[1] );
SubdivEdge_t *pEdge2 = GetEdge( pParentQuad->m_EdgeHandles[2] );
if ( pEdge1 && pEdge2 )
{
pQuad->m_PointHandles[0] = BuildSubdivPoint( pParentQuad->m_vCentroid, pParentQuad->m_vNormal );
pQuad->m_PointHandles[1] = BuildSubdivPoint( pEdge1->m_vNewEdgePoint, pEdge1->m_vNewEdgeNormal );
pQuad->m_PointHandles[2] = pParentQuad->m_PointHandles[2];
pQuad->m_PointHandles[3] = BuildSubdivPoint( pEdge2->m_vNewEdgePoint, pEdge2->m_vNewEdgeNormal );
}
break;
}
case 3:
{
// displacement quad indices
pQuad->m_QuadIndices[0] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[3] ) * 0.5f;
pQuad->m_QuadIndices[1] = ( pParentQuad->m_QuadIndices[0] + pParentQuad->m_QuadIndices[2] ) * 0.5f;
pQuad->m_QuadIndices[2] = ( pParentQuad->m_QuadIndices[2] + pParentQuad->m_QuadIndices[3] ) * 0.5f;
pQuad->m_QuadIndices[3] = pParentQuad->m_QuadIndices[3];
// new verts
SubdivEdge_t *pEdge2 = GetEdge( pParentQuad->m_EdgeHandles[2] );
SubdivEdge_t *pEdge3 = GetEdge( pParentQuad->m_EdgeHandles[3] );
if ( pEdge2 && pEdge3 )
{
pQuad->m_PointHandles[0] = BuildSubdivPoint( pEdge3->m_vNewEdgePoint, pEdge3->m_vNewEdgeNormal );
pQuad->m_PointHandles[1] = BuildSubdivPoint( pParentQuad->m_vCentroid, pParentQuad->m_vNormal );
pQuad->m_PointHandles[2] = BuildSubdivPoint( pEdge2->m_vNewEdgePoint, pEdge2->m_vNewEdgeNormal );
pQuad->m_PointHandles[3] = pParentQuad->m_PointHandles[3];
}
break;
}
}
//
// buidl new quad edges
//
for ( int ndxEdge = 0; ndxEdge < 4; ndxEdge++ )
{
pQuad->m_EdgeHandles[ndxEdge] = BuildSubdivEdge( ndxEdge, quadHandle, parentHandle, ndxChild );
}
return quadHandle;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::Init( void )
{
// ensure capacity on all lists
IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
if( !pDispMgr )
return;
int selectCount = pDispMgr->SelectCount();
m_Points.EnsureCapacity( SUBDIV_DISPPOINTS * selectCount );
m_Edges.EnsureCapacity( SUBDIV_DISPEDGES * selectCount );
m_Quads.EnsureCapacity( SUBDIV_DISPQUADS * selectCount );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::Shutdown( void )
{
// clear all lists
m_Points.Purge();
m_Edges.Purge();
m_Quads.Purge();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::AddDispTo( CMapDisp *pDisp )
{
// add a quad to the subdivision mesh
SubdivQuadHandle_t quadHandle = m_Quads.AddToTail();
Quad_Init( quadHandle );
SubdivQuad_t *pQuad = &m_Quads.Element( quadHandle );
// this is the parent!
pQuad->m_ndxParent = m_Quads.InvalidIndex();
pQuad->m_EditDispHandle = pDisp->GetEditHandle();
pQuad->m_Level = 0;
//
// get displacement data
//
int dispWidth = pDisp->GetWidth();
int dispHeight = pDisp->GetHeight();
//
// setup mapping between the displacement size and initial quad indices
//
pQuad->m_QuadIndices[0] = 0;
pQuad->m_QuadIndices[1] = dispWidth * ( dispHeight - 1 );
pQuad->m_QuadIndices[2] = ( dispWidth * dispHeight ) - 1;
pQuad->m_QuadIndices[3] = ( dispWidth - 1 );
//
// find point normals and neighbors -- "smooth"
// NOTE: this is slow -- should write a faster version (is offline process, do later)
//
IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
if( !pDispMgr )
return;
Vector vPoints[4];
Vector vPointNormals[4];
for( int ndxPt = 0; ndxPt < EDITDISP_QUADSIZE; ndxPt++ )
{
// get the base face normal of all surfaces touching this point!
pDisp->GetSurfNormal( vPointNormals[ndxPt] );
// get the point to compare to neighbors
pDisp->GetSurfPoint( ndxPt, vPoints[ndxPt] );
int count = pDispMgr->SelectCount();
for( int ndxSelect = 0; ndxSelect < count; ndxSelect++ )
{
CMapDisp *pSelectDisp = pDispMgr->GetFromSelect( ndxSelect );
if( !pSelectDisp || ( pSelectDisp == pDisp ) )
continue;
for( int ndxPt2 = 0; ndxPt2 < EDITDISP_QUADSIZE; ndxPt2++ )
{
Vector vPoint;
pSelectDisp->GetSurfPoint( ndxPt2, vPoint );
if( CompareSubdivPoints( vPoints[ndxPt], vPoint, 0.01f ) )
{
Vector vNormal;
pSelectDisp->GetSurfNormal( vNormal );
VectorAdd( vPointNormals[ndxPt], vNormal, vPointNormals[ndxPt] );
}
}
}
VectorNormalize( vPointNormals[ndxPt] );
}
// build subdivision points
for( int ndxPt = 0; ndxPt < EDITDISP_QUADSIZE; ndxPt++ )
{
pQuad->m_PointHandles[ndxPt] = BuildSubdivPoint( vPoints[ndxPt], vPointNormals[ndxPt] );
}
// build subdivision edges
for( int ndxEdge = 0; ndxEdge < EDITDISP_QUADSIZE; ndxEdge++ )
{
pQuad->m_EdgeHandles[ndxEdge] = BuildSubdivEdge( ndxEdge, quadHandle, m_Quads.InvalidIndex(), -1 );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::GetDispFrom( CMapDisp *pDisp )
{
//
// find the parent quad with the id of the displacement
//
for ( SubdivQuadHandle_t quadHandle = m_Quads.Head(); quadHandle != m_Quads.InvalidIndex();
quadHandle = m_Quads.Next( quadHandle ) )
{
SubdivQuad_t *pQuad = GetQuad( quadHandle );
if ( pQuad )
{
// find children quads that "belong" to this displacement
if( pQuad->m_EditDispHandle != pDisp->GetEditHandle() )
continue;
// get the data at the appropriate level -- (based on the size of the displacement)
if ( pQuad->m_Level != pDisp->GetPower() )
continue;
//
// fill in subdivision positions and normals
//
for ( int ndxPt = 0; ndxPt < 4; ndxPt++ )
{
SubdivPoint_t *pPoint = GetPoint( pQuad->m_PointHandles[ndxPt] );
if ( pPoint )
{
Vector vFlatVert, vSubVert;
pDisp->GetFlatVert( pQuad->m_QuadIndices[ndxPt], vFlatVert );
VectorSubtract( pPoint->m_vPoint, vFlatVert, vSubVert );
pDisp->UpdateVertPositionForSubdiv( pQuad->m_QuadIndices[ndxPt], vSubVert );
pDisp->SetSubdivNormal( pQuad->m_QuadIndices[ndxPt], pPoint->m_vNormal );
}
}
}
}
// tell the dispalcemet to update itself
pDisp->UpdateData();
// reset subdivision/subdivided flags
pDisp->SetReSubdivision( false );
pDisp->SetSubdivided( true );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::DoCatmullClarkSubdivision( void )
{
for ( int ndxLevel = 0; ndxLevel < NUM_SUBDIV_LEVELS; ndxLevel++ )
{
// subdivide
CatmullClarkSubdivision();
// update the subdivision hierarchy (tree)
UpdateSubdivisionHierarchy( ndxLevel );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::CatmullClarkSubdivision( void )
{
//
// step 1: calculate the "new edge points" for all edges
//
for ( SubdivEdgeHandle_t edgeHandle = m_Edges.Head(); edgeHandle != m_Edges.InvalidIndex();
edgeHandle = m_Edges.Next( edgeHandle ) )
{
Edge_CalcNewPoint( edgeHandle );
}
//
// step 2: calculate the valence and edge list
//
for ( SubdivPointHandle_t ptHandle = m_Points.Head(); ptHandle != m_Points.InvalidIndex();
ptHandle = m_Points.Next( ptHandle ) )
{
for ( SubdivEdgeHandle_t edgeHandle = m_Edges.Head(); edgeHandle != m_Edges.InvalidIndex();
edgeHandle = m_Edges.Next( edgeHandle ) )
{
SubdivEdge_t *pEdge = GetEdge( edgeHandle );
if ( !pEdge->m_bActive )
continue;
if ( ( ptHandle == pEdge->m_PointHandles[0] ) || ( ptHandle == pEdge->m_PointHandles[1] ) )
{
SubdivPoint_t *pPoint = GetPoint( ptHandle );
if ( pPoint->m_uValence < ( EDITDISP_QUADSIZE*4 ) )
{
pPoint->m_EdgeHandles[pPoint->m_uValence] = edgeHandle;
pPoint->m_uValence++;
}
}
}
}
//
// step 3: determine the point's Type (Oridinary, Corner, Crease)
//
for ( SubdivPointHandle_t ptHandle = m_Points.Head(); ptHandle != m_Points.InvalidIndex(); ptHandle = m_Points.Next( ptHandle ) )
{
SubdivPoint_t *pPoint = GetPoint( ptHandle );
if ( pPoint )
{
int sharpCount = 0;
int sharpThreshold = pPoint->m_uValence - 1;
bool bHasNeighbors = false;
// initialize as oridinary -- determine otherwise
pPoint->m_uType = SUBDIV_POINTORDINARY;
for ( int ndxEdge = 0; ndxEdge < pPoint->m_uValence; ndxEdge++ )
{
SubdivEdge_t *pEdge = GetEdge( pPoint->m_EdgeHandles[ndxEdge] );
if ( pEdge )
{
if ( pEdge->m_flSharpness > 0.0f )
{
sharpCount++;
}
if ( pEdge->m_QuadHandles[1] != m_Quads.InvalidIndex() )
{
bHasNeighbors = true;
}
}
}
if ( !bHasNeighbors || ( sharpCount >= sharpThreshold ) )
{
pPoint->m_uType = SUBDIV_POINTCORNER;
}
else if( sharpCount > 1 )
{
pPoint->m_uType = SUBDIV_POINTCREASE;
}
}
}
//
// step 4: calculate the "new points" for all points
//
for ( SubdivPointHandle_t ptHandle = m_Points.Head(); ptHandle != m_Points.InvalidIndex(); ptHandle = m_Points.Next( ptHandle ) )
{
Point_CalcNewPoint( ptHandle );
}
//
// step 5: copy all "new point" data to point data
//
for ( SubdivPointHandle_t ptHandle = m_Points.Head(); ptHandle != m_Points.InvalidIndex(); ptHandle = m_Points.Next( ptHandle ) )
{
SubdivPoint_t *pPoint = GetPoint( ptHandle );
VectorCopy( pPoint->m_vNewPoint, pPoint->m_vPoint );
VectorCopy( pPoint->m_vNewNormal, pPoint->m_vNewNormal );
VectorClear( pPoint->m_vNewPoint );
VectorClear( pPoint->m_vNewNormal );
// reset valence
pPoint->m_uValence = 0;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEditDispSubdivMesh::UpdateSubdivisionHierarchy( int ndxLevel )
{
int quadCount = m_Quads.Count();
SubdivQuadHandle_t quadHandle = m_Quads.Head();
int ndxQuad = 0;
while ( ( quadHandle != m_Quads.InvalidIndex() ) && ( ndxQuad < quadCount ) )
{
SubdivQuad_t *pQuad = GetQuad( quadHandle );
if ( pQuad )
{
// skip parent quads
if ( pQuad->m_ndxChild[0] != m_Quads.InvalidIndex() )
{
ndxQuad++;
quadHandle = m_Quads.Next( quadHandle );
continue;
}
for( int ndxChild = 0; ndxChild < 4; ndxChild++ )
{
pQuad->m_ndxChild[ndxChild] = BuildSubdivQuad( ndxChild, quadHandle );
}
// de-activate all edges (children's edges are active now!)
for ( int ndxEdge = 0; ndxEdge < 4; ndxEdge++ )
{
SubdivEdge_t *pEdge = GetEdge( pQuad->m_EdgeHandles[ndxEdge] );
if ( pEdge )
{
pEdge->m_bActive = false;
}
}
}
ndxQuad++;
quadHandle = m_Quads.Next( quadHandle );
}
}