⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 subdivsurf.cpp

📁 <B>DirectX9.0 3D游戏编程</B>
💻 CPP
字号:
/********************************************************************
 *         Advanced 3D Game Programming using DirectX 9.0			*
 ********************************************************************
 * copyright (c) 2003 by Peter A Walsh and Adrian Perez				*
 * See license.txt for modification and distribution information	*
 ********************************************************************/

#include "stdafx.h"
#include "SubDivSurf.h"

#include <algorithm>
using namespace std;

cSubDivSurf::cSubDivSurf( const char* filename )
{
	/**
	 * Nullify all of our dynamic data structures
	 */
	m_d3dTriList = NULL;

	m_pVertexBuffer = NULL;

	/**
	 * Load the model
	 */
	cModel model( filename );

	/**
	 * For the purposes of the sample, unitize the object.
	 * production code probably wouldn't want this.
	 */
	model.Scale( 1.f / model.GenRadius() );

	m_nVerts = model.NumVerts();
	m_nTris = model.NumTris();
	m_nEdges = m_nTris * 3 / 2;

	m_pVList = new sVert[ m_nVerts ];
	m_pEList = new sEdge[ m_nEdges ];
	m_pTList = new sTriangle[ m_nTris ];

	int i;

	// Load the vertices
	for( i=0; i<m_nVerts; i++ )
	{
		//m_pVList[i].m_vert = *(model.VertData() + i);
		CopyMemory( &m_pVList[i].m_vert, (model.VertData() + i), sizeof( sVertex ) );
		
		m_pVList[i].m_index = i;
	}

	// Load the triangles (simultaneously load the edges)
	int currEdge = 0;
	for( i=0; i<m_nTris; i++ )
	{
		sTri* pTri = (sTri*) (model.TriData() + i);
		int ind[3];

		ind[0] = pTri->v[0];
		ind[1] = pTri->v[1];
		ind[2] = pTri->v[2];

		m_pTList[i].Init( &m_pVList[ind[0]], &m_pVList[ind[1]], &m_pVList[ind[2]] );

		// only do it one way to avoid duplicates
		if( ind[0] > ind[1] )
			m_pEList[currEdge++].Init( &m_pVList[ind[0]], &m_pVList[ind[1]] );
		if( ind[1] > ind[2] )
			m_pEList[currEdge++].Init( &m_pVList[ind[1]], &m_pVList[ind[2]] );
		if( ind[2] > ind[0] )
			m_pEList[currEdge++].Init( &m_pVList[ind[2]], &m_pVList[ind[0]] );
	}

	CalcNormals();

	GenD3DData();
}



cSubDivSurf::~cSubDivSurf()
{
	/**
	 * Annihilate all of our dynamic data
	 */
	if( m_pVertexBuffer )
		m_pVertexBuffer->Release();
	m_pVertexBuffer = NULL;

	delete [] m_pVList;
	delete [] m_pEList;
	delete [] m_pTList;

	delete [] m_d3dTriList;
}



void cSubDivSurf::Draw( matrix4& mat )
{
	LPDIRECT3DDEVICE9 lpDevice = Graphics()->GetDevice();
	Graphics()->SetWorldMatrix( mat );

	HRESULT hr;

	// The index buffer
	LPDIRECT3DINDEXBUFFER9 pIndexBuffer = 0;

	// Create the index buffer
	lpDevice->CreateIndexBuffer(
		m_nTris * 3 * sizeof( WORD ),	// Size in bytes of buffer
		D3DUSAGE_WRITEONLY,				// Will only be writing to the buffer
		D3DFMT_INDEX16,					// 16 bit indices
		D3DPOOL_DEFAULT,				// Default memory pooling
		&pIndexBuffer,					// Address of the buffer pointer
		NULL );							// Reserved. set to NULL

	// Pointer to the index buffer data
	WORD* pData = 0;

	// Lock the index buffer
	pIndexBuffer->Lock( 0, 0, (void**)&pData, 0 );

	// Copy the index data into the index buffer
	CopyMemory( pData, m_d3dTriList, m_nTris * 3 * sizeof( WORD ) );

	// Unlock the index buffer
	pIndexBuffer->Unlock();

	// Tell Direct3D to use the index buffer
	lpDevice->SetIndices( pIndexBuffer );
	
	// Attach the vertex buffer to rendering stream 0
	lpDevice->SetStreamSource( 0, m_pVertexBuffer, 0, sizeof( sVertex ) );

	// Draw the primitive
	hr = lpDevice->DrawIndexedPrimitive(
		D3DPT_TRIANGLELIST,
		0,
		0,
		m_nVerts,
		0,
		m_nTris );

	if( FAILED( hr ) )
	{
		DP0("[cSubDivSurf::Draw]: DrawIndexedPrimitive failed!\n");
	}

	pIndexBuffer->Release();
}


/**
 * This is where the meat of the subdivision work is done.
 * Depending on the valence of the two endpoints of each edge,
 * the code will generate the new edge value
 */
void cSubDivSurf::GenNewVertLocs()
{
	for( int i=0; i<m_nEdges; i++ )
	{
		int val0 = m_pEList[i].m_v[0]->Valence();
		int val1 = m_pEList[i].m_v[1]->Valence();

		point3 loc;

		/**
		 * CASE 1: both vertices are of valence == 6
		 * Use the butterfly scheme
		 */
		if( val0 == 6 && val1 == 6 )
		{
			loc = m_pEList[i].CalcButterfly();
		}

		/**
		 * CASE 2: one of the vertices are of valence == 6
		 * Calculate the k-vertex for the non-6 vertex
		 */
		else if( val0 == 6 && val1 != 6 )
		{
			loc = m_pEList[i].CalcKVert(1,0);
		}

		else if( val0 != 6 && val1 == 6 )
		{
			loc = m_pEList[i].CalcKVert(0,1);
		}

		/**
		 * CASE 3: neither of the vertices are of valence == 6
		 * Calculate the k-vertex for each of them, and average
		 * the result
		 */
		else 
		{
			loc = ( m_pEList[i].CalcKVert(1,0) + 
				    m_pEList[i].CalcKVert(0,1) ) / 2.f;
		}

		m_pEList[i].m_newVLoc.m_vert = sVertex( 
			loc , point3::Zero );

		/**
		 * Assign the new vertex an index (this is useful later,
		 * when we start throwing vertex pointers around.  We
		 * could have implemented everything with indices, but
		 * the code would be much harder to read.  An extra dword
		 * per vertex is a small price to pay.)
		 */
		m_pEList[i].m_newVLoc.m_index = i + m_nVerts;
	}
}


eResult cSubDivSurf::Subdivide()
{

	/**
	 * We know how many components our subdivided model will have, calc them
	 */
	int nNewEdges = 2*m_nEdges + 3*m_nTris;
	int nNewVerts = m_nVerts + m_nEdges;
	int nNewTris = 4*m_nTris;

	/**
	 * If the model will have too many triangles (d3d can only handle 2^16), return
	 */
	if( nNewVerts >= 65536 || nNewTris >= 65536)
	{
		return resFalse;
	}

	/**
	 * Find the location of the new vertices.  Most of the hard work
	 * is done here.
	 */
	GenNewVertLocs();

	int i;
	sVert* inner[3]; // the vertices on the 3 edges (order: 0..1, 1..2, 2..0)


	// Allocate space for the subdivided data
	sVert* pNewVerts = new sVert[ nNewVerts ];
	sEdge* pNewEdges = new sEdge[ nNewEdges ];
	sTriangle* pNewTris = new sTriangle[ nNewTris ];


	//==========--------------------------  Step 1: Fill the vertex list
	// First batch - the original vertices
	for( i=0; i<m_nVerts; i++ )
	{
		pNewVerts[i].m_index = i;
		pNewVerts[i].m_vert = m_pVList[i].m_vert;
	}
	// Second batch - vertices from each edge
	for( i=0; i<m_nEdges; i++ )
	{
		pNewVerts[m_nVerts + i].m_index = m_nVerts + i;
		pNewVerts[m_nVerts + i].m_vert = m_pEList[i].m_newVLoc.m_vert;
	}

	//==========--------------------------  Step 2: Fill in the edge list
	int currEdge = 0;
	// First batch - the 2 edges that are spawned by each original edge
	for( i=0; i<m_nEdges; i++ )
	{
		pNewEdges[currEdge++].Init( 
			&pNewVerts[m_pEList[i].m_v[0]->m_index],
			&pNewVerts[m_pEList[i].m_newVLoc.m_index] );
		pNewEdges[currEdge++].Init( 
			&pNewVerts[m_pEList[i].m_v[1]->m_index],
			&pNewVerts[m_pEList[i].m_newVLoc.m_index] );
	}
	// Second batch - the 3 inner edges spawned by each original triangle
	for( i=0; i<m_nTris; i++ )
	{
		// find the inner 3 vertices of this triangle 
		// ( the new vertex of each of the triangles' edges )
		inner[0] = &m_pTList[i].m_v[0]->GetEdge( m_pTList[i].m_v[1] )->m_newVLoc;
		inner[1] = &m_pTList[i].m_v[1]->GetEdge( m_pTList[i].m_v[2] )->m_newVLoc;
		inner[2] = &m_pTList[i].m_v[2]->GetEdge( m_pTList[i].m_v[0] )->m_newVLoc;

		pNewEdges[currEdge++].Init( 
			&pNewVerts[inner[0]->m_index],
			&pNewVerts[inner[1]->m_index] );
		pNewEdges[currEdge++].Init( 
			&pNewVerts[inner[1]->m_index],
			&pNewVerts[inner[2]->m_index] );
		pNewEdges[currEdge++].Init( 
			&pNewVerts[inner[2]->m_index],
			&pNewVerts[inner[0]->m_index] );
	}


	//==========--------------------------  Step 3: Fill in the triangle list
	int currTri = 0;
	for( i=0; i<m_nTris; i++ )
	{
		// find the inner vertices
		inner[0] = &m_pTList[i].m_v[0]->GetEdge( m_pTList[i].m_v[1] )->m_newVLoc;
		inner[1] = &m_pTList[i].m_v[1]->GetEdge( m_pTList[i].m_v[2] )->m_newVLoc;
		inner[2] = &m_pTList[i].m_v[2]->GetEdge( m_pTList[i].m_v[0] )->m_newVLoc;

		// 0, inner0, inner2
		pNewTris[currTri++].Init( 
			&pNewVerts[m_pTList[i].m_v[0]->m_index],
			&pNewVerts[inner[0]->m_index], 
			&pNewVerts[inner[2]->m_index] ); 

		// 1, inner1, inner0
		pNewTris[currTri++].Init( 
			&pNewVerts[m_pTList[i].m_v[1]->m_index],
			&pNewVerts[inner[1]->m_index], 
			&pNewVerts[inner[0]->m_index] ); 

		// 2, inner2, inner1
		pNewTris[currTri++].Init( 
			&pNewVerts[m_pTList[i].m_v[2]->m_index],
			&pNewVerts[inner[2]->m_index], 
			&pNewVerts[inner[1]->m_index] ); 

		// inner0, inner1, inner2
		pNewTris[currTri++].Init( 
			&pNewVerts[inner[0]->m_index], 
			&pNewVerts[inner[1]->m_index], 
			&pNewVerts[inner[2]->m_index] ); 
	}

	//==========--------------------------  Step 4: Housekeeping

	// Swap out the old data sets for the new ones.
	
	delete [] m_pVList;
	delete [] m_pEList;
	delete [] m_pTList;

	m_nVerts = nNewVerts;
	m_nEdges = nNewEdges;
	m_nTris = nNewTris;

	m_pVList = pNewVerts;
	m_pEList = pNewEdges;
	m_pTList = pNewTris;

	// Calculate the vertex normals of the new mesh using face normal averaging
	CalcNormals();

	//==========--------------------------  Step 5: Make arrays so we can send the triangles in one batch

	delete [] m_d3dTriList;
	if( m_pVertexBuffer )
		m_pVertexBuffer->Release();
	m_pVertexBuffer = NULL;

	GenD3DData();

	return resAllGood;
}

point3 cSubDivSurf::sEdge::CalcButterfly()
{
	point3 out = point3::Zero;

	sVert* other[2];
	other[0] = GetOtherVert( m_v[0], m_v[1], NULL );
	other[1] = GetOtherVert( m_v[0], m_v[1], other[0] );

	// two main ones
	out += (1.f/2.f) * m_v[0]->m_vert.loc;
	out += (1.f/2.f) * m_v[1]->m_vert.loc;

	// top/bottom ones
	out += (1.f/8.f) * other[0]->m_vert.loc;
	out += (1.f/8.f) * other[1]->m_vert.loc;

	// outside 4 verts
	out += (-1.f/16.f) * GetOtherVert( other[0], m_v[0], m_v[1] )->m_vert.loc;
	out += (-1.f/16.f) * GetOtherVert( other[0], m_v[1], m_v[0] )->m_vert.loc;
	out += (-1.f/16.f) * GetOtherVert( other[1], m_v[0], m_v[1] )->m_vert.loc;
	out += (-1.f/16.f) * GetOtherVert( other[1], m_v[1], m_v[0] )->m_vert.loc;

	return out;
}

point3 cSubDivSurf::sEdge::CalcKVert(int prim, int sec)
{
	int valence = m_v[prim]->Valence();

	point3 out = point3::Zero;

	out += (3.f / 4.f) * m_v[prim]->m_vert.loc;

	if( valence < 3 )
		assert( false );

	else if( valence == 3 )
	{
		for( int i=0; i<m_v[prim]->m_edgeList.size(); i++ )
		{
			sVert* pOther = m_v[prim]->m_edgeList[i]->Other( m_v[prim] );
			if( pOther == m_v[sec] )
				out += (5.f/12.f) * pOther->m_vert.loc;
			else
				out += (-1.f/12.f) * pOther->m_vert.loc;
		}
	}

	else if( valence == 4 )
	{
		out += (3.f/8.f) * m_v[sec]->m_vert.loc;

		sVert* pTemp = GetOtherVert( m_v[0], m_v[1], NULL );
		// get the one after it
		sVert* pOther = GetOtherVert( m_v[prim], pTemp, m_v[sec] );

		out += (-1.f/8.f) * pOther->m_vert.loc;
	}

	else // valence >= 5 
	{
		sVert* pCurr = m_v[sec];
		sVert* pLast = NULL;
		sVert* pTemp;
		for( int i=0; i< valence; i++ )
		{
			float weight = 
				((1.f/4.f) + (float)cos( 2 * PI * (float)i / (float)valence ) + 
				 (1.f/2.f) * (float)cos(4*PI*(float)i/(float)valence)) / (float)valence;

			out += weight * pCurr->m_vert.loc;

			pTemp = GetOtherVert( m_v[prim], pCurr, pLast );
			pLast = pCurr;
			pCurr = pTemp;
		}
	}
	return out;
}


void cSubDivSurf::CalcNormals()
{
	int i;

	// reset all vertex normals
	for( i=0; i<m_nVerts; i++ )
	{
		m_pVList[i].m_vert.norm = point3::Zero;
	}

	// find all triangle normals
	for( i=0; i<m_nTris; i++ )
	{
		m_pTList[i].m_normal = plane3(
			m_pTList[i].m_v[0]->m_vert.loc,
			m_pTList[i].m_v[1]->m_vert.loc,
			m_pTList[i].m_v[2]->m_vert.loc).n;

		// add the normal to each vertex
		m_pTList[i].m_v[0]->m_vert.norm += m_pTList[i].m_normal;
		m_pTList[i].m_v[1]->m_vert.norm += m_pTList[i].m_normal;
		m_pTList[i].m_v[2]->m_vert.norm += m_pTList[i].m_normal;
	}

	// reset all vertex normals
	for( i=0; i<m_nVerts; i++ )
	{
		m_pVList[i].m_vert.norm.Normalize();
	}

}

void cSubDivSurf::GenD3DData()
{

	/**
	 * Create a vertex buffer
	 */

	HRESULT hr;
	hr = Graphics()->GetDevice()->CreateVertexBuffer(
		m_nVerts * sizeof( sVertex ),
		D3DUSAGE_WRITEONLY,
		D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1,
		D3DPOOL_DEFAULT,
		&m_pVertexBuffer,
		0);

    if( FAILED( hr ))
	{
        throw cGameError("Vertex Buffer creation failed!\n");
	}

	m_d3dTriList = new sTri[ m_nTris ];

	sVertex* pVert;

	// Lock the vertex buffer
	hr = m_pVertexBuffer->Lock(
		0,
		0,
		(void**)&pVert,
		0 );

	if( FAILED( hr ))
	{
		throw cGameError("VB Lock failed\n");
	}

	int i;

	// Copy data into the buffer
	for( i=0; i<m_nVerts; i++ )
	{
		*pVert++ = m_pVList[i].m_vert;
	}
	m_pVertexBuffer->Unlock();

	for( i=0; i<m_nTris; i++ )
	{
		m_d3dTriList[i].v[0] = m_pTList[i].m_v[0]->m_index;
		m_d3dTriList[i].v[1] = m_pTList[i].m_v[1]->m_index;
		m_d3dTriList[i].v[2] = m_pTList[i].m_v[2]->m_index;
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -