Exemplo n.º 1
0
MeshContainer* XFileLoader::CreateMeshContainer()
{
	HRESULT hr;

	MeshContainer* meshContainer = NULL;

	Graphics* graphics = Graphics::GetInstance();
	IDirect3DDevice9Ptr pD3DDevice = graphics->GetDirect3DDevice();

	if( !(m_pD3DMesh->GetFVF() & D3DFVF_NORMAL) )
    {
		LPD3DXMESH tmpMesh = NULL;

        // 柔軟な頂点フォーマット (FVF) コードを使ってメッシュのコピーを作成する
        hr = m_pD3DMesh->CloneMeshFVF(
            m_pD3DMesh->GetOptions(),
            m_pD3DMesh->GetFVF() | D3DFVF_NORMAL,
            pD3DDevice,
            &tmpMesh); // ←ここにコピー
        if(FAILED(hr))
        {
            goto exit;
        }
        // メッシュに含まれる各頂点の法線を計算して、設定する
        //D3DXComputeNormals( tmpMesh, reinterpret_cast<DWORD*>(pAdjacencyBuf->GetBufferPointer()) );
		D3DXComputeNormals( tmpMesh, NULL );

		m_pD3DMesh->Release();
		m_pD3DMesh = tmpMesh;
	}

	D3DVERTEXELEMENT9 pDecl[MAX_FVF_DECL_SIZE];
	hr = m_pD3DMesh->GetDeclaration(pDecl);
	if( FAILED(hr) )
	{
		goto exit;
	}

	DWORD vertexNum = m_pD3DMesh->GetNumVertices();
	DWORD faceNum = m_pD3DMesh->GetNumFaces();

	DWORD attrNum = 0;
	m_pD3DMesh->GetAttributeTable(NULL, &attrNum);
	
	DWORD size = m_pD3DMesh->GetNumBytesPerVertex();

	BYTE* pD3DVertice = NULL;
	m_pD3DMesh->LockVertexBuffer( 0,(LPVOID*)&pD3DVertice );

	sVertex* vertices = new sVertex[vertexNum];

	for( DWORD vertIdx = 0;vertIdx<vertexNum;vertIdx++ )
	{
		sVertex* vertex = &vertices[vertIdx];
		vertex->uv = D3DXVECTOR2(0.0f,0.0f);
		vertex->color = 0xFFFFFFFF;

		for( DWORD i=0;i<MAX_FVF_DECL_SIZE;i++ )
		{
			D3DVERTEXELEMENT9& decl = pDecl[i];

			if( decl.Stream==0xFF )
			{
				break;
			}

			switch( decl.Usage )
			{
			case D3DDECLUSAGE_POSITION:
				vertex->position = *(D3DXVECTOR3*)(pD3DVertice+vertIdx*size+decl.Offset);
				vertex->position = vertex->position * m_scale;
				break;
			case D3DDECLUSAGE_NORMAL:
				vertex->normal = *(D3DXVECTOR3*)(pD3DVertice+vertIdx*size+decl.Offset);
				break;
			case D3DDECLUSAGE_TEXCOORD:
				vertex->uv = *(D3DXVECTOR2*)(pD3DVertice+vertIdx*size+decl.Offset);
				break;
			case D3DDECLUSAGE_COLOR:
				vertex->color = *(DWORD*)(pD3DVertice+vertIdx*size+decl.Offset);
				break;
			}
		}
	}

	m_pD3DMesh->UnlockVertexBuffer();

	LPDIRECT3DINDEXBUFFER9 pIndexBuffer = NULL;
	m_pD3DMesh->GetIndexBuffer( &pIndexBuffer );
	D3DINDEXBUFFER_DESC desc;
	pIndexBuffer->GetDesc( &desc );
	pIndexBuffer->Release();

	DWORD* indices = new DWORD[faceNum*3];

	if( desc.Format==D3DFMT_INDEX16 )
	{
		WORD* pD3DIndices = NULL;
		m_pD3DMesh->LockIndexBuffer(0,(LPVOID*)&pD3DIndices );
		for( DWORD i=0;i<faceNum*3;i++ )
		{
			indices[i] = pD3DIndices[i];
		}
		m_pD3DMesh->UnlockIndexBuffer();
	}
	else
	{
		DWORD* pD3DIndices =NULL;
		m_pD3DMesh->LockIndexBuffer(0,(LPVOID*)&pD3DIndices );
		memcpy( indices,pD3DIndices,sizeof(DWORD)*faceNum*3 );
		m_pD3DMesh->UnlockIndexBuffer();
	}

	D3DXATTRIBUTERANGE *attrList = new D3DXATTRIBUTERANGE[attrNum];
	m_pD3DMesh->GetAttributeTable(attrList, &attrNum);

	meshContainer = new MeshContainer;

	meshContainer->pMesh = new Mesh;
	meshContainer->pMesh->Create( vertexNum,faceNum,attrNum );

	meshContainer->pMesh->SetVertices( vertices );

	meshContainer->pMesh->SetIndices( indices );

	meshContainer->pMesh->SetAttributeRanges( attrList );

	delete[] vertices;
	delete[] indices;
	delete[] attrList;

	meshContainer->materialNum = m_Materials;
	meshContainer->pMaterials = new sMaterial[m_Materials];

	D3DXMATERIAL* pD3DMaterials = (D3DXMATERIAL*)m_pMaterialBuf->GetBufferPointer();

	for( DWORD i=0;i<m_Materials;i++ )
	{
		sMaterial* pMaterial = &meshContainer->pMaterials[i];

		D3DXMATERIAL* pD3DMaterial = &pD3DMaterials[i];

		pMaterial->colorDiffuse = pD3DMaterial->MatD3D.Diffuse;

		pMaterial->colorSpecular.r = pD3DMaterial->MatD3D.Specular.r;
		pMaterial->colorSpecular.g = pD3DMaterial->MatD3D.Specular.g;
		pMaterial->colorSpecular.b = pD3DMaterial->MatD3D.Specular.b;
		pMaterial->colorSpecular.a = 0.0f;

		pMaterial->colorAmbient.r = pD3DMaterial->MatD3D.Diffuse.r;
		pMaterial->colorAmbient.g = pD3DMaterial->MatD3D.Diffuse.g;
		pMaterial->colorAmbient.b = pD3DMaterial->MatD3D.Diffuse.b;
		pMaterial->colorAmbient.a = 0.0f;

		pMaterial->colorEmissive.r = pD3DMaterial->MatD3D.Emissive.r;
		pMaterial->colorEmissive.g = pD3DMaterial->MatD3D.Emissive.g;
		pMaterial->colorEmissive.b = pD3DMaterial->MatD3D.Emissive.b;
		pMaterial->colorEmissive.a = 0.0f;

		pMaterial->specularPower = pD3DMaterial->MatD3D.Power;

		TCHAR path[MAX_PATH];
		_tcscpy_s( path,m_path.c_str() );

		tstring texFileName;
		tstring sphereFileName;

		if( pD3DMaterial->pTextureFilename && strlen(pD3DMaterial->pTextureFilename)>0 )
		{
			tstring filename = to_tstring(pD3DMaterial->pTextureFilename);

			tstring::size_type index = filename.find( _T("*") );
			if( index != tstring::npos )
			{
				sphereFileName = filename.substr( index+1 );
				PathAppend( path,sphereFileName.c_str() );
				sphereFileName = path;
				PathRemoveFileSpec( path );

				texFileName = filename.erase( index );
				PathAppend( path,texFileName.c_str() );
				texFileName = path;
				PathRemoveFileSpec( path );
			}
			else
			{
				texFileName = filename;
				PathAppend( path,texFileName.c_str() );
				texFileName = path;
				PathRemoveFileSpec( path );
			}

			tstring ext = PathFindExtension( texFileName.c_str() );

			if( ext == _T(".sph" ) || ext == _T(".spa") )
			{
				sphereFileName = texFileName;
				texFileName = _T("");
			}
		}

		if( !texFileName.empty() )
		{
			TexturePtr pTex = ResourceManager::GetInstance().GetResource<Texture>( texFileName );
			if( !pTex )
			{
				pTex = TexturePtr(new Texture);
				if( pTex->CreateFromFile( texFileName ) )
				{
					ResourceManager::GetInstance().AddResource( texFileName,pTex );
				}
				else
				{
					pTex.reset();
				}
			}

			if( pTex )
			{
				pMaterial->textureDiffuse = pTex;
			}
		}

		if( !sphereFileName.empty() )
		{
			TexturePtr pTex = ResourceManager::GetInstance().GetResource<Texture>( sphereFileName );
			if( !pTex )
			{
				pTex = TexturePtr(new Texture);
				if( pTex->CreateFromFile( sphereFileName ) )
				{
					ResourceManager::GetInstance().AddResource( sphereFileName,pTex );
				}
				else
				{
					pTex.reset();
				}
			}

			if( pTex )
			{
				pMaterial->textureSphere = pTex;
			}

			tstring ext = PathFindExtension( sphereFileName.c_str() );
			if( ext == _T(".sph" ) )
			{
				pMaterial->spheremap = eSPHEREMAP_MUL;
			}
			else if( ext == _T(".spa") )
			{
				pMaterial->spheremap = eSPHEREMAP_ADD;
			}
		}
	}

exit:
	if( m_pMaterialBuf )
	{
		m_pMaterialBuf->Release();
		m_pMaterialBuf = NULL;
	}
	if( m_pEffectInstancesBuf )
	{
		m_pEffectInstancesBuf->Release();
		m_pEffectInstancesBuf = NULL;
	}
	if( m_pAdjacencyBuf )
	{
		m_pAdjacencyBuf->Release();
		m_pAdjacencyBuf = NULL;
	}
	if( m_pD3DMesh )
	{
		m_pD3DMesh->Release();
		m_pD3DMesh = NULL;
	}
	return meshContainer;
}
Exemplo n.º 2
0
bool GStillEntity::checkIntersect ( const D3DXVECTOR4& vPos, /*世界坐标系中的点 */ const D3DXVECTOR4& vDir, /*世界坐标系中的向量 */ bool bInsectInfo /*是 裥枰鲎残畔?*/ )
{
    HRESULT hr = S_FALSE;

    //将Pos和Dir转换到物体本地坐标系中
    D3DXMATRIX matWorld = getTrans()->getLocalD3D();
    D3DXMatrixInverse ( &matWorld, NULL, &matWorld );

    D3DXVec4Transform ( ( D3DXVECTOR4 * ) &vDir, ( D3DXVECTOR4 * ) &vDir, &matWorld );
    D3DXVec3Normalize ( ( D3DXVECTOR3* ) &vDir, ( D3DXVECTOR3* ) &vDir );
    D3DXVec4Transform ( ( D3DXVECTOR4 * ) &vPos, ( D3DXVECTOR4 * ) &vPos, &matWorld );

    if ( mMeshForInsect == NULL )
    {
        recreateInsectMesh();
    }

    BOOL bHit = FALSE;

    hr = D3DXIntersect (
             mMeshForInsect, ( D3DXVECTOR3* ) &vPos, ( D3DXVECTOR3* ) &vDir, &bHit,
             &m_InsectInfo.dwFaceIndex, &m_InsectInfo.u, &m_InsectInfo.v, &m_InsectInfo.fDist,
             NULL, NULL
         );

    mNodeState.setBit ( eObjState_Picked, ( bool ) bHit );
    dDebugMsgBox ( hr, "碰撞失败!" );

    if ( FAILED ( hr ) )
    {
        return false;
    }

    if ( bInsectInfo && mNodeState[eObjState_Picked] )
    {
        D3DXVECTOR3 v[3];
        DWORD dwIndex[3];

        //先要获取索引缓冲区格式
        LPDIRECT3DINDEXBUFFER9 pI = NULL;
        mMeshForInsect->GetIndexBuffer ( &pI );

        D3DINDEXBUFFER_DESC indexDesc;
        dMemoryZero ( &indexDesc, sizeof ( D3DINDEXBUFFER_DESC ) );

        if ( pI != NULL )
        {
            pI->GetDesc ( &indexDesc );
        }

        if ( indexDesc.Format== D3DFMT_INDEX16 )
        {
            WORD *pIndexData16;

            hr = mMeshForInsect->LockIndexBuffer ( D3DLOCK_READONLY, ( void** ) &pIndexData16 );

            dwIndex[0] = pIndexData16[m_InsectInfo.dwFaceIndex * 3 + 0];
            dwIndex[1] = pIndexData16[m_InsectInfo.dwFaceIndex * 3 + 1];
            dwIndex[2] = pIndexData16[m_InsectInfo.dwFaceIndex * 3 + 2];
        }
        else
        {
            DWORD *pIndexData32;

            hr = mMeshForInsect->LockIndexBuffer ( D3DLOCK_READONLY, ( void** ) &pIndexData32 );

            dwIndex[0] = pIndexData32[m_InsectInfo.dwFaceIndex * 3 + 0];
            dwIndex[1] = pIndexData32[m_InsectInfo.dwFaceIndex * 3 + 1];
            dwIndex[2] = pIndexData32[m_InsectInfo.dwFaceIndex * 3 + 2];
        }


        mMeshForInsect->UnlockIndexBuffer();

        D3DXVECTOR3 *pVertexData;

        hr = mMeshForInsect->LockVertexBuffer ( D3DLOCK_READONLY, ( void** ) &pVertexData );

        v[0] = pVertexData[dwIndex[0]];
        v[1] = pVertexData[dwIndex[1]];
        v[2] = pVertexData[dwIndex[2]];

        mMeshForInsect->UnlockVertexBuffer();

        D3DXVECTOR4 vNormal ( ZEROFLOAT, ZEROFLOAT, ZEROFLOAT, ZEROFLOAT );
        D3DXVECTOR4 vHitPos ( ZEROFLOAT, ZEROFLOAT, ZEROFLOAT, ZEROFLOAT );

        D3DXVECTOR3 vTmp1, vTmp2;
        vTmp1 = v[1] - v[0];
        vTmp2 = v[2] - v[0];

        vHitPos = ( D3DXVECTOR4 ) v[0] + m_InsectInfo.u * ( D3DXVECTOR4 ) vTmp1 + m_InsectInfo.v * ( D3DXVECTOR4 ) vTmp2;
        vHitPos.w = 1;

        updateWorld ();

        D3DXVec4Transform ( &vHitPos, &vHitPos, &getTrans()->getLocalD3D() );
        m_InsectInfo.vHitPos = D3DXVECTOR3 ( vHitPos.x, vHitPos.y, vHitPos.z );

        D3DXVec3Cross ( ( D3DXVECTOR3* ) &vNormal, &vTmp1, &vTmp2 );
        vNormal.w = 0;


        D3DXVec4Transform ( &vNormal, &vNormal, &matWorld );
        D3DXVec3Normalize ( ( D3DXVECTOR3* ) &vNormal, ( D3DXVECTOR3* ) &vNormal );

        m_InsectInfo.vNormal = D3DXVECTOR3 ( vNormal.x, vNormal.y, vNormal.z );

    }

    return mNodeState[eObjState_Picked];

}
void CSampleRigidParticlesAndTerrain::InititialisePhysics()
{
	neV3 gravity; gravity.Set(0.0f, -8.0f, 0.0f);

	neSimulatorSizeInfo sizeInfo;

	sizeInfo.rigidParticleCount = NUMBER_OF_PARTICLES;
	sizeInfo.animatedBodiesCount = WALL_NUMBER;
	sizeInfo.geometriesCount = NUMBER_OF_PARTICLES * GEOMETRY_PER_BODY + WALL_NUMBER;

	{ //dont need any of these
		sizeInfo.rigidBodiesCount = 0;
		sizeInfo.constraintsCount = 0;
	}

	s32 totalBody = NUMBER_OF_PARTICLES + WALL_NUMBER;
	sizeInfo.overlappedPairsCount = totalBody * (totalBody - 1) / 2;

	sim = neSimulator::CreateSimulator(sizeInfo, &all, &gravity);

	neV3 position;

	position.SetZero();

	for (s32 j = 0; j < NUMBER_OF_PARTICLES; j++)
	{
		position.Set(0.0f, 2.0f * j + 20.0f, 0.0f);
		//position.Set(13.5f, 20.0f, 1.5f);

		MakeParticle(position, j);	
	}

	//SetUpTerrain

	terrainRender.SetGraphicMesh(L"model\\landscape2.x");
	terrainRender.SetDiffuseColor(D3DXCOLOR(0.1f,0.5f,0.1f,1.0f));

	LPD3DXMESH lpterrainD3Dmesh = terrainRender.mMesh.GetMesh();

	neTriangleMesh triMesh;

	triMesh.vertexCount = lpterrainD3Dmesh->GetNumVertices();

	triMesh.triangleCount = lpterrainD3Dmesh->GetNumFaces();	

	neV3 * verts = new neV3[triMesh.vertexCount];

	//
	DWORD dwFVF			= lpterrainD3Dmesh->GetFVF();
	DWORD dwOptions		= lpterrainD3Dmesh->GetOptions();
	DWORD dwNumFaces	= lpterrainD3Dmesh->GetNumFaces();
	DWORD dwNumVertices = lpterrainD3Dmesh->GetNumVertices();
	DWORD dwBytes		= lpterrainD3Dmesh->GetNumBytesPerVertex();

	LPDIRECT3DVERTEXBUFFER9 pVB;
	lpterrainD3Dmesh->GetVertexBuffer(&pVB);

	byte* pBuffer;
	pVB->Lock(0, 0, (void**)&pBuffer, 0);

	byte* pPointer = pBuffer;

	for (int i = 0;i< triMesh.vertexCount;i++)
	{
		if (dwFVF & D3DFVF_XYZ)
		{
			D3DVECTOR *d3dvector;
			d3dvector = (D3DVECTOR*)pPointer;
			verts[i].Set(d3dvector->x,d3dvector->y,d3dvector->z);
			pPointer += sizeof(D3DVECTOR);
		}
		//if (dwFVF & D3DFVF_NORMAL)
		//{
		//	//don't care the NORMAL data
		//	pPointer += sizeof(D3DVECTOR);
		//}
		//if (dwFVF & D3DFVF_TEX1)
		//{
		//	pPointer += 8;
		//}
		pPointer += dwBytes - sizeof(D3DVECTOR);
		

	}


	pVB->Unlock();
	pVB->Release();

	//

	triMesh.vertices = verts;

	neTriangle * tri = new neTriangle[triMesh.triangleCount];

	s32 * triindex = new s32[triMesh.triangleCount * 3];

	//

	LPDIRECT3DINDEXBUFFER9 pIB;
	lpterrainD3Dmesh->GetIndexBuffer(&pIB);

	D3DINDEXBUFFER_DESC kDesc;
	pIB->GetDesc(&kDesc);

	dwBytes = 0;
	if (kDesc.Format & D3DFMT_INDEX16)
	{
		dwBytes = 2 * sizeof(byte);
	}
	else if (kDesc.Format & D3DFMT_INDEX32)
	{
		dwBytes = 4 * sizeof(byte);
	}

	pIB->Lock(0, 0, (void**)&pBuffer, 0);

	pPointer = pBuffer;
	while ((pPointer - pBuffer) < kDesc.Size)
	{
		if (dwBytes == 2*sizeof(byte))
		{
			//16bit
			triindex[(pPointer-pBuffer)/dwBytes] = *((s16*)pPointer);
		}
		else if (dwBytes == 4*sizeof(byte))
		{
			//32bit
			triindex[(pPointer-pBuffer)/dwBytes] = *((s32*)pPointer);
		}
		pPointer += dwBytes;
	}

	pIB->Unlock();
	pIB->Release();


	

	//

	for (s32 i = 0; i < triMesh.triangleCount; i++)
	{
		tri[i].indices[0] = triindex[i * 3];
		tri[i].indices[1] = triindex[i * 3 + 1];
		tri[i].indices[2] = triindex[i * 3 + 2];
		tri[i].materialID = 0;
		tri[i].flag = neTriangle::NE_TRI_TRIANGLE;
		//tri[i].flag = neTriangle::NE_TRI_HEIGHT_MAP;
	}
	triMesh.triangles = tri;

	sim->SetTerrainMesh(&triMesh);

	//SetUpRoom

	ground = sim->CreateAnimatedBody();

	neGeometry * geom = ground->AddGeometry();	 

	geom->SetBoxSize(gFloor.boxSize);

	ground->UpdateBoundingInfo();

	ground->SetPos(gFloor.pos);

	groundRender.SetGraphicBox(gFloor.boxSize[0], gFloor.boxSize[1], gFloor.boxSize[2]);


}
Exemplo n.º 4
0
//------------------------------------------------------------------------------------------------
// Name:  XMesh
// Desc:  Constructs the subset geometry for a D3DXMesh
//------------------------------------------------------------------------------------------------
bool XMesh::buildGeometryFromD3DXMesh(LPD3DXMESH d3dxMesh, SubsetGeometry* subsetGeometry, DWORD subsets)
{
    // Check parameters
    if (APP_ERROR(!d3dxMesh || !subsetGeometry)("Invalid parameter to XMesh::buildGeometryFromD3DXMesh"))
        return false;

    // Add a reference to the mesh to counteract freeing it at the end
    d3dxMesh->AddRef();

    // Get the device
    LPDIRECT3DDEVICE9 pd3dDevice = NULL;
    d3dxMesh->GetDevice(&pd3dDevice);

    // If this mesh isn't already in the correct format, have D3D do the grunt work of
    // converting it.
    bool generate_normals = false; // Whether or not normals need to be generated for this mesh
    if ((d3dxMesh->GetFVF() != D3DFVF_GEOMETRYVERTEX) ||
        (D3DFMT_GEOMETRYINDEX == D3DFMT_INDEX32) && ((d3dxMesh->GetOptions() & D3DXMESH_32BIT) == 0))
    {
        // Holds the mesh when its converted to the correct format
        LPD3DXMESH pTemd3dxMesh = NULL;

        // Duplicate the loaded mesh into the format
        if (APP_ERROR(d3dxMesh->CloneMeshFVF(
                            D3DXMESH_SYSTEMMEM | ((D3DFMT_GEOMETRYINDEX == D3DFMT_INDEX32) ? D3DXMESH_32BIT : 0),
                            D3DFVF_GEOMETRYVERTEX, pd3dDevice, &pTemd3dxMesh))
                     ("XMesh couldn't convert the source geometry format")) {
            d3dxMesh->Release();
            pd3dDevice->Release();
			      return false;
        }

        // Generate normals if they didn't exist
        generate_normals = ((d3dxMesh->GetFVF()&D3DFVF_NORMAL)!=D3DFVF_NORMAL &&
                            (D3DFMT_GEOMETRYINDEX&D3DFVF_NORMAL)!=D3DFVF_NORMAL);

        // Use this mesh instead
        d3dxMesh->Release();
        d3dxMesh = pTemd3dxMesh;
    }

    // The mesh must have its attributes sorted before it can be converted to single strips
    {
        // Allocate an adjacency buffer
        DWORD faces = d3dxMesh->GetNumFaces();
        DWORD* pAdjacency = new DWORD[faces * 3];
		    bool failed = false;

        if (APP_ERROR(FAILED(d3dxMesh->GenerateAdjacency(ADJACENCY_EPSILON, pAdjacency)))("Unable to generate the mesh adjacency"))
          failed = true;

        { // Clean up "bowties" in the mesh that prevent lighting from being calculated correctly
          LPD3DXMESH cleaned_mesh = NULL;
          DWORD* cleaned_adjacency = new DWORD[faces * 3];
          LPD3DXBUFFER errors_and_warnings = NULL;
          if (!failed && APP_ERROR(FAILED(D3DXCleanMesh(D3DXCLEAN_BOWTIES,
                                                        d3dxMesh,
                                                        pAdjacency,
                                                        &cleaned_mesh,
                                                        cleaned_adjacency,
                                                        &errors_and_warnings)))
                                  ("Failed to clean mesh")) {
            failed = true;
            if (errors_and_warnings) {
              DEBUG_ERROR("Mesh cleaning error:  %s", (const char*)errors_and_warnings->GetBufferPointer());
            }
          }

          SAFE_RELEASE(errors_and_warnings);

          // If we successfully cleaned the mesh, use the new mesh and new set of
          // adjacencies.  Otherwise, just delete anything that was allocated and
          // keep the original.
          if (failed) {
            SAFE_DELETE_ARRAY(cleaned_adjacency);
            SAFE_RELEASE(cleaned_mesh);
          } else {
            SAFE_DELETE_ARRAY(pAdjacency);
            SAFE_RELEASE(d3dxMesh)
            pAdjacency = cleaned_adjacency;
            d3dxMesh = cleaned_mesh;
          }
        }

        // Compute mesh normals, if necessary
        if (!failed && generate_normals && APP_ERROR(FAILED(D3DXComputeNormals(d3dxMesh, pAdjacency)))("Couldn't generate mesh normals")) {
          failed = true;
        }

        // Optimize the mesh
        if (!failed && APP_ERROR(FAILED(d3dxMesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT,
                                                                  pAdjacency,
                                                                  NULL,
                                                                  NULL,
                                                                  NULL)))
                                 ("Couldn't optimize mesh attributes")) {
			    failed = true;
		    }

        // Get rid of the temporary adjacency buffer
        SAFE_DELETE_ARRAY(pAdjacency);

        // Return if there was an error
        if (failed) {
          SAFE_RELEASE(d3dxMesh);
          SAFE_RELEASE(pd3dDevice);
          return false;
        }
    }

    // Lock the vertex buffer
    GeometryVertex* pXVertices = NULL;
    if (APP_ERROR(d3dxMesh->LockVertexBuffer(D3DLOCK_READONLY, (VOID**)&pXVertices))("Couldn't lock source vertex buffer"))
    {
		// Erase this mesh
        d3dxMesh->Release();
        pd3dDevice->Release();

		// Failure
		return false;
    }

    // Iterate through all of the materials and copy vertex/index data, and assign material
    // information for the mesh.
    for (DWORD subset = 0; subset < subsets; subset++)
    {
        // Use D3DX to convert this subset into a nicely indexed form
        DWORD numStripIndices;
        LPDIRECT3DINDEXBUFFER9 pSubsetIB;
        if (APP_ERROR(D3DXConvertMeshSubsetToSingleStrip(d3dxMesh, subset, D3DXMESH_SYSTEMMEM, &pSubsetIB, &numStripIndices))("Couldn't convert mesh subset into indexable strip"))
        {
            // Erase any geometry we made
            DeallocateGeometry(subsetGeometry);

            // Get rid of the mesh
            d3dxMesh->UnlockVertexBuffer();
            d3dxMesh->Release();

            // Free our device
            pd3dDevice->Release();

            // Return the error
            return false;
        }

        D3DINDEXBUFFER_DESC desc;
        GeometryIndex* pXIndices = NULL;

        // Check the format of the indices and lock the strip index buffer
        if (APP_ERROR(pSubsetIB->GetDesc(&desc))("Couldn't get .X mesh IB desc") || (desc.Format != D3DFMT_GEOMETRYINDEX) ||
            APP_ERROR(pSubsetIB->Lock(0, 0, (VOID**)&pXIndices, D3DLOCK_READONLY))("Unable to lock the .X index buffer"))
        {
            // Erase any geometry we made
            DeallocateGeometry(subsetGeometry);

            // Get rid of the mesh
            pSubsetIB->Release();
            d3dxMesh->UnlockVertexBuffer();
            d3dxMesh->Release();

            // Free our device
            pd3dDevice->Release();

            // Error!
            return false;
        }

        // This table pairs an index from the .X file to an index in the buffer that
        // holds the vertices for this subset
        XIndicesTable xIndicesTable;

        // For each of the indices in the strip, puts its vertex ID into the indices
        // table.  Use the counter to determine which vertex this is.
        {
            GeometryIndex vertexCounter = 0;
            for (DWORD e = 0; e < numStripIndices; ++e)
            {
                // Insert the entry [x-mesh index, subset index] into the table
                XIndicesTableInsertResult result = xIndicesTable.insert(XIndicesEntry(pXIndices[e], vertexCounter));

                // If the result was successful (this isn't a duplicated X-mesh index) increment the vertex counter
                if (result.second)
                    vertexCounter++;
            }
        }

        // Grab the number of vertices this geometry uses
        DWORD numVertices = (DWORD)xIndicesTable.size();

        // This buffer holds all of the triangles in this subset
        TriangleList triangles;

        // This list keeps track of locations in the strip where the winding order changes.  This is necessary
        // because this next part will remove degenerate triangles from the list.
        std::set<size_t> windingChanges;

        // Generate the list of triangles from the strip provided
        for (DWORD t = 0; t < numStripIndices - 2; ++t)
        {
            // Build the triangle that will be added to the buffer
            // CHANGED July 25, 2008:  the winding order is wrong here
            //Triangle tri = { pXIndices[t + 0], pXIndices[t + 1], pXIndices[t + 2] };
            Triangle tri = { pXIndices[t + 0], pXIndices[t + 2], pXIndices[t + 1] };



            // Convert the triangle into subset-indices by using the lookup table
            // we generated before.
            tri.index[0] = xIndicesTable.find(tri.index[0])->second;
            tri.index[1] = xIndicesTable.find(tri.index[1])->second;
            tri.index[2] = xIndicesTable.find(tri.index[2])->second;

            // Check to make sure this triangle isn't degenerate.  If it is, we can just skip
            // this triangle entirely to simplify the geometry.
            if (tri.index[0] == tri.index[1] || tri.index[1] == tri.index[2] || tri.index[0] == tri.index[2])
            {
                // Try to find the winding in the list
                std::set<size_t>::iterator currentWinding = windingChanges.find(triangles.size());

                // Add this to the winding change list, or remove the change if it's already there
                if (currentWinding != windingChanges.end())
                    windingChanges.erase(currentWinding);
                else
                    windingChanges.insert(triangles.size());

                // Don't insert a triangle here
                continue;
            }

            // Add this triangle to the list
            triangles.push_back(tri);
        }

        // Calculate the number of indices we need for the buffer
        DWORD numGeometryIndices = (DWORD)(triangles.size() * 3);

        // Allocate the destination geometry
        Geometry* pGeometry = NULL;
        if (APP_ERROR(AllocateGeometry(numVertices, numGeometryIndices, &pGeometry))("Couldn't allocate geometry"))
        {
            // Erase any geometry we made
            DeallocateGeometry(subsetGeometry);

            // Get rid of the mesh
            pSubsetIB->Unlock();
            pSubsetIB->Release();
            d3dxMesh->UnlockVertexBuffer();
            d3dxMesh->Release();

            // Free our device
            pd3dDevice->Release();

            // Error!
            return false;
        }

        // Copy the vertices needed for this subset into the buffer
        GeometryVertex* pVertices = pGeometry->pVertices;
        for (XIndicesIterator i = xIndicesTable.begin(); i != xIndicesTable.end(); ++i)
        {
            GeometryVertex* pCurrentVertex = &pVertices[i->second];
            *pCurrentVertex = pXVertices[i->first];

            // Modify the vertex location to make this a unit mesh sitting on the X-Z plane
            pCurrentVertex->x = pCurrentVertex->x;
            pCurrentVertex->y = pCurrentVertex->y;
            pCurrentVertex->z = pCurrentVertex->z;

            //pVertices[i->second].color = D3DCOLOR_XRGB(255,255,255);
            // todo: enable color?
        }

        // Copy triangles into the indices buffer
        DWORD index = 0;
        GeometryIndex* pIndices = pGeometry->pIndices;
        DWORD windingOrder = 0;
        for (TriangleIterator t = triangles.begin(); t != triangles.end(); ++t)
        {
            // Find this index in the winding list
            if (windingChanges.find(index / 3) != windingChanges.end())
                windingOrder = 1 - windingOrder;

            // Alternate the winding order so that everything shows up correctly
            if ((index / 3) % 2 == windingOrder)
            {
                pIndices[index + 0] = t->index[0];
                pIndices[index + 1] = t->index[1];
                pIndices[index + 2] = t->index[2];
            }
            else
            {
                pIndices[index + 0] = t->index[1];
                pIndices[index + 1] = t->index[0];
                pIndices[index + 2] = t->index[2];
            }

            // Increment the index counter
            index += 3;
        }

        // Unlock and delete strip index buffer
        pSubsetIB->Unlock();
        pSubsetIB->Release();

        // Store the buffers in the main array
        std::pair<SubsetGeometry::iterator,bool> result =
            subsetGeometry->insert(SubsetGeometry::value_type(subset, pGeometry));

        if (APP_ERROR(!result.second)("Couldn't insert subset geometry into main array for .X mesh"))
        {
            // Get rid of this geometry
            DeallocateGeometry(pGeometry);
            DeallocateGeometry(subsetGeometry);

            // Erase the mesh
            d3dxMesh->UnlockVertexBuffer();
            d3dxMesh->Release();

            // Free our device
            pd3dDevice->Release();

            // Return error
            return false;
        }

        //DEBUG_MSG("Subset %i has %i vertices %i indices (%i polygons)\n", subset, numVertices, numGeometryIndices, numGeometryIndices / 3);
    }

    // Done with the DirectX mesh.  This will not erase the outside mesh.
    d3dxMesh->UnlockVertexBuffer();
    d3dxMesh->Release();

    // Free the device reference
    pd3dDevice->Release();

    // Success
    return true;
}