ID3DXMesh* Mesh::GenerateTangentFrame(ID3DXMesh* mesh, IDirect3DDevice9* d3d9Device) { // make sure we have a texture coordinate D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE]; DXCall(mesh->GetDeclaration(decl)); bool foundTexCoord = false; for (UINT i = 0; i < MAX_FVF_DECL_SIZE; ++i) { if (decl[i].Stream == 0xFF) break; else if(decl[i].Usage == D3DDECLUSAGE_TEXCOORD && decl[i].UsageIndex == 0) { foundTexCoord = true; break; } } _ASSERT(foundTexCoord); // Clone the mesh with a new declaration D3DVERTEXELEMENT9 newDecl[] = { { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 }, { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, { 0, 32, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0 }, { 0, 44, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0 }, D3DDECL_END() }; ID3DXMesh* clonedMesh = NULL; UINT options = D3DXMESH_MANAGED; if (indexType == Index32Bit) options |= D3DXMESH_32BIT; DXCall(mesh->CloneMesh(options, newDecl, d3d9Device, &clonedMesh)); mesh->Release(); // Calculate the tangent frame DXCall(D3DXComputeTangentFrameEx(clonedMesh, D3DDECLUSAGE_TEXCOORD, 0, D3DDECLUSAGE_BINORMAL, 0, D3DDECLUSAGE_TANGENT, 0, D3DDECLUSAGE_NORMAL, 0, D3DXTANGENT_CALCULATE_NORMALS | D3DXTANGENT_GENERATE_IN_PLACE, &adjacency[0], 0.01f, 0.25f, 0.01f, NULL, NULL)); return clonedMesh; }
//----------------------------------------------------------------------------- // Convert the mesh to the format specified by the given vertex declarations. //----------------------------------------------------------------------------- HRESULT CDXUTMesh::SetVertexDecl( LPDIRECT3DDEVICE9 pd3dDevice, const D3DVERTEXELEMENT9 *pDecl, bool bAutoComputeNormals, bool bAutoComputeTangents, bool bSplitVertexForOptimalTangents ) { LPD3DXMESH pTempMesh = NULL; if( m_pMesh ) { if( FAILED( m_pMesh->CloneMesh( m_pMesh->GetOptions(), pDecl, pd3dDevice, &pTempMesh ) ) ) { SAFE_RELEASE( pTempMesh ); return E_FAIL; } } // Check if the old declaration contains a normal. bool bHadNormal = false; bool bHadTangent = false; D3DVERTEXELEMENT9 aOldDecl[MAX_FVF_DECL_SIZE]; if( m_pMesh && SUCCEEDED( m_pMesh->GetDeclaration( aOldDecl ) ) ) { for( UINT index = 0; index < D3DXGetDeclLength( aOldDecl ); ++index ) { if( aOldDecl[index].Usage == D3DDECLUSAGE_NORMAL ) { bHadNormal = true; } if( aOldDecl[index].Usage == D3DDECLUSAGE_TANGENT ) { bHadTangent = true; } } } // Check if the new declaration contains a normal. bool bHaveNormalNow = false; bool bHaveTangentNow = false; D3DVERTEXELEMENT9 aNewDecl[MAX_FVF_DECL_SIZE]; if( pTempMesh && SUCCEEDED( pTempMesh->GetDeclaration( aNewDecl ) ) ) { for( UINT index = 0; index < D3DXGetDeclLength( aNewDecl ); ++index ) { if( aNewDecl[index].Usage == D3DDECLUSAGE_NORMAL ) { bHaveNormalNow = true; } if( aNewDecl[index].Usage == D3DDECLUSAGE_TANGENT ) { bHaveTangentNow = true; } } } SAFE_RELEASE( m_pMesh ); if( pTempMesh ) { m_pMesh = pTempMesh; if( !bHadNormal && bHaveNormalNow && bAutoComputeNormals ) { // Compute normals in case the meshes have them D3DXComputeNormals( m_pMesh, NULL ); } if( bHaveNormalNow && !bHadTangent && bHaveTangentNow && bAutoComputeTangents ) { ID3DXMesh* pNewMesh; HRESULT hr; DWORD *rgdwAdjacency = NULL; rgdwAdjacency = new DWORD[m_pMesh->GetNumFaces() * 3]; if( rgdwAdjacency == NULL ) return E_OUTOFMEMORY; V( m_pMesh->GenerateAdjacency(1e-6f,rgdwAdjacency) ); float fPartialEdgeThreshold; float fSingularPointThreshold; float fNormalEdgeThreshold; if( bSplitVertexForOptimalTangents ) { fPartialEdgeThreshold = 0.01f; fSingularPointThreshold = 0.25f; fNormalEdgeThreshold = 0.01f; } else { fPartialEdgeThreshold = -1.01f; fSingularPointThreshold = 0.01f; fNormalEdgeThreshold = -1.01f; } // Compute tangents, which are required for normal mapping hr = D3DXComputeTangentFrameEx( m_pMesh, D3DDECLUSAGE_TEXCOORD, 0, D3DDECLUSAGE_TANGENT, 0, D3DX_DEFAULT, 0, D3DDECLUSAGE_NORMAL, 0, 0, rgdwAdjacency, fPartialEdgeThreshold, fSingularPointThreshold, fNormalEdgeThreshold, &pNewMesh, NULL ); SAFE_DELETE_ARRAY( rgdwAdjacency ); if( FAILED(hr) ) return hr; SAFE_RELEASE( m_pMesh ); m_pMesh = pNewMesh; } } return S_OK; }
//------------------------------------------------------------------------------------------ HRESULT CModel::LoadFromFile( const std::string& i_strFilePath ) { HRESULT hr; // パスを保持 m_strFilePath = i_strFilePath; // 作成前に破棄 Destroy(); /// 作成 /// IDirect3DDevice9* pd3dDevice = CApp::GetInstance()->GetGraphicDevice()->GetD3DDevice(); V_RETURN( D3DXLoadMeshFromXA( m_strFilePath.c_str(), D3DXMESH_MANAGED, pd3dDevice, NULL, NULL, NULL, NULL, &m_pMesh)); // 法線&接線ベクトルを作成 { // 法線と接線をもつ頂点宣言を作成 D3DVERTEXELEMENT9 vertDecl[] = { { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 }, { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, { 0, 32, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0 }, { 0, 44, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0 }, D3DDECL_END() }; // 元のメッシュのコピー用(新頂点宣言を適用したもの)と接線生成後の格納用メッシュ LPD3DXMESH clonedMesh, newMesh; // 新しい頂点宣言をもつメッシュへコピー hr = m_pMesh->CloneMesh(D3DXMESH_VB_MANAGED, vertDecl, pd3dDevice, &clonedMesh); MY_ASSERT( SUCCEEDED(hr) ); // 法線をもつか bool bHasNormal = (m_pMesh->GetFVF() & D3DFVF_NORMAL) != 0; // 接線を生成 hr = D3DXComputeTangentFrameEx( clonedMesh, D3DDECLUSAGE_TEXCOORD, 0, D3DDECLUSAGE_TANGENT, 0, D3DDECLUSAGE_BINORMAL, 0 , bHasNormal ? D3DX_DEFAULT : D3DDECLUSAGE_NORMAL , 0 , bHasNormal ? 0 : D3DXTANGENT_CALCULATE_NORMALS , NULL, 0.01f, 0.25f, 0.01f, &newMesh, NULL ); MY_ASSERT( SUCCEEDED(hr) ); // メッシュを解放 m_pMesh->Release(); clonedMesh->Release(); // 接線の生成されたメッシュを保持 m_pMesh = newMesh; // 各バッファを取得 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE]; hr = m_pMesh->GetDeclaration( declaration ); MY_ASSERT( SUCCEEDED(hr) ); pd3dDevice->CreateVertexDeclaration( declaration, &m_pDeclaration ); hr = m_pMesh->GetVertexBuffer( &m_pVertexBuffer ); MY_ASSERT( SUCCEEDED(hr) ); hr = m_pMesh->GetIndexBuffer( &m_pIndexBuffer ); MY_ASSERT( SUCCEEDED(hr) ); } return S_OK; }
WaterDemo::WaterDemo(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP) : D3DApp(hInstance, winCaption, devType, requestedVP) { if(!checkDeviceCaps()) { MessageBox(0, "checkDeviceCaps() Failed", 0, 0); PostQuitMessage(0); } InitAllVertexDeclarations(); mLight.dirW = D3DXVECTOR3(0.0f, -2.0f, -1.0f); D3DXVec3Normalize(&mLight.dirW, &mLight.dirW); mLight.ambient = D3DXCOLOR(0.3f, 0.3f, 0.3f, 1.0f); mLight.diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); mLight.spec = D3DXCOLOR(0.7f, 0.7f, 0.7f, 1.0f); mGfxStats = new GfxStats(); mSky = new Sky("grassenvmap1024.dds", 10000.0f); D3DXMATRIX waterWorld; D3DXMatrixTranslation(&waterWorld, 0.0f, 2.0f, 0.0f); Mtrl waterMtrl; waterMtrl.ambient = D3DXCOLOR(0.26f, 0.23f, 0.3f, 0.90f); waterMtrl.diffuse = D3DXCOLOR(0.26f, 0.23f, 0.3f, 0.90f); waterMtrl.spec = 1.0f*WHITE; waterMtrl.specPower = 64.0f; Water::InitInfo waterInitInfo; waterInitInfo.dirLight = mLight; waterInitInfo.mtrl = waterMtrl; waterInitInfo.vertRows = 128; waterInitInfo.vertCols = 128; waterInitInfo.dx = 1.0f; waterInitInfo.dz = 1.0f; waterInitInfo.waveMapFilename0 = "wave0.dds"; waterInitInfo.waveMapFilename1 = "wave1.dds"; waterInitInfo.waveMapVelocity0 = D3DXVECTOR2(0.05f, 0.08f); waterInitInfo.waveMapVelocity1 = D3DXVECTOR2(-0.02f, 0.1f); waterInitInfo.texScale = 16.0f; waterInitInfo.toWorld = waterWorld; mWater = new Water(waterInitInfo); mWater->setEnvMap(mSky->getEnvMap()); ID3DXMesh* tempMesh = 0; LoadXFile("BasicColumnScene.x", &tempMesh, mSceneMtrls, mSceneTextures); // Get the vertex declaration for the NMapVertex. D3DVERTEXELEMENT9 elems[MAX_FVF_DECL_SIZE]; UINT numElems = 0; HR(NMapVertex::Decl->GetDeclaration(elems, &numElems)); // Clone the mesh to the NMapVertex format. ID3DXMesh* clonedTempMesh = 0; HR(tempMesh->CloneMesh(D3DXMESH_MANAGED, elems, gd3dDevice, &clonedTempMesh)); // Now use D3DXComputeTangentFrameEx to build the TNB-basis for each vertex // in the mesh. HR(D3DXComputeTangentFrameEx( clonedTempMesh, // Input mesh D3DDECLUSAGE_TEXCOORD, 0, // Vertex element of input tex-coords. D3DDECLUSAGE_BINORMAL, 0, // Vertex element to output binormal. D3DDECLUSAGE_TANGENT, 0, // Vertex element to output tangent. D3DDECLUSAGE_NORMAL, 0, // Vertex element to output normal. 0, // Options 0, // Adjacency 0.01f, 0.25f, 0.01f, // Thresholds for handling errors &mSceneMesh, // Output mesh 0)); // Vertex Remapping // Done with temps. ReleaseCOM(tempMesh); ReleaseCOM(clonedTempMesh); D3DXMatrixIdentity(&mSceneWorld); D3DXMatrixIdentity(&mSceneWorldInv); HR(D3DXCreateTextureFromFile(gd3dDevice, "floor_nmap.bmp", &mSceneNormalMaps[0])); HR(D3DXCreateTextureFromFile(gd3dDevice, "bricks_nmap.bmp", &mSceneNormalMaps[1])); HR(D3DXCreateTextureFromFile(gd3dDevice, "whitetex.dds", &mWhiteTex)); // Initialize camera. gCamera->pos().y = 7.0f; gCamera->pos().z = -30.0f; gCamera->setSpeed(10.0f); mGfxStats->addVertices(mSceneMesh->GetNumVertices()); mGfxStats->addTriangles(mSceneMesh->GetNumFaces()); mGfxStats->addVertices(mWater->getNumVertices()); mGfxStats->addTriangles(mWater->getNumTriangles()); mGfxStats->addVertices(mSky->getNumVertices()); mGfxStats->addTriangles(mSky->getNumTriangles()); buildFX(); onResetDevice(); }
HRESULT InitScene() { HRESULT hr; LPD3DXBUFFER errors = NULL; SetWindowText(hwnd, TITLE); if( FAILED(hr = D3DXLoadMeshFromXA("../media/meshes/sphere.X", D3DXMESH_MANAGED, device, NULL, NULL, NULL, NULL, &mesh)) ) { MYERROR("Could not load sphere"); return hr; } // generate tangent frame LPD3DXMESH newmesh = NULL; D3DVERTEXELEMENT9 decl[] = { { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, { 0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, { 0, 20, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 }, { 0, 32, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0 }, { 0, 44, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0 }, D3DDECL_END() }; if( FAILED(hr = mesh->CloneMesh(D3DXMESH_MANAGED, decl, device, &newmesh)) ) { MYERROR("Could not clone mesh"); return hr; } mesh->Release(); mesh = NULL; hr = D3DXComputeTangentFrameEx(newmesh, D3DDECLUSAGE_TEXCOORD, 0, D3DDECLUSAGE_TANGENT, 0, D3DDECLUSAGE_BINORMAL, 0, D3DDECLUSAGE_NORMAL, 0, 0, NULL, 0.01f, 0.25f, 0.01f, &mesh, NULL); newmesh->Release(); if( FAILED(hr) ) { MYERROR("Could not compute tangent frame"); return hr; } if( FAILED(hr = CreateChecker(device, 10, 10, 0xff7557a8, 0xffd8d8d8, &tex)) ) { MYERROR("Could not create texture"); return hr; } if( FAILED(hr = D3DXCreateTextureFromFileA(device, "../media/textures/brick_nh.dds", &normalmap)) ) { MYERROR("Could not load normalmap"); return hr; } hr = D3DXCreateEffectFromFileA(device, "../media/shaders/normal.fx", NULL, NULL, D3DXSHADER_DEBUG, NULL, &effect, &errors); if( FAILED(hr) ) { if( errors ) { char* str = (char*)errors->GetBufferPointer(); std::cout << str << "\n\n"; errors->Release(); } MYERROR("Could not create effect"); return hr; } D3DXVECTOR4 uv(3, 1, 0, 1); effect->SetVector("normuv", &uv); D3DXVECTOR3 eye(0, 0, -1.5f); D3DXVECTOR3 look(0, 0, 0); D3DXVECTOR3 up(0, 1, 0); D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI / 3, (float)screenwidth / (float)screenheight, 0.1f, 10); D3DXMatrixLookAtLH(&view, &eye, &look, &up); D3DXMatrixIdentity(&world); return S_OK; }
//////////////////////////////////////////////////////////////////////// /// /// Loads DirectX mesh file. /// /// @param filename Filename to load. /// /// @return success or failure. /// //////////////////////////////////////////////////////////////////////// const VCNBool VCNDXMesh::LoadFromFile( const VCNString& filename ) { VCND3D9* renderer = static_cast<VCND3D9*>( VCNRenderCore::GetInstance() ); LPDIRECT3DDEVICE9 d3dDevice = renderer->GetD3DDevice(); CComPtr<ID3DXMesh> systemMesh; CComPtr<ID3DXBuffer> materialBuffer; // Load the mesh from the specified file // DWORD numMaterials = 0; HRESULT hr = D3DXLoadMeshFromX(filename.c_str(), D3DXMESH_SYSTEMMEM, d3dDevice, NULL, &materialBuffer,NULL, &numMaterials, &systemMesh ); if ( FAILED(hr) ) return false; // Load materials // D3DXMATERIAL* d3dxMaterials = (D3DXMATERIAL*)materialBuffer->GetBufferPointer(); for (DWORD i = 0; i < numMaterials; ++i) { VCN_ASSERT_MSG( i == 0 || mMaterialID == kInvalidResID, VCNTXT("We do not support multiple material per meshes") ); // Create the texture if it exists - it may not if (d3dxMaterials[i].pTextureFilename) { VCNString texturePath = VCNTXT("Textures/"); texturePath += VCN_A2W(d3dxMaterials[i].pTextureFilename); // Check if the texture is already loaded VCNResID texID = kInvalidResID; VCND3D9Texture* resTexture = VCNResourceCore::GetInstance()->GetResource<VCND3D9Texture>(texturePath); if (!resTexture) { LPDIRECT3DTEXTURE9 d3dTexture = NULL; hr = D3DXCreateTextureFromFile(d3dDevice, texturePath.c_str(), &d3dTexture); VCN_ASSERT_MSG( SUCCEEDED(hr), _T("Can't load texture [%s]"), texturePath.c_str() ); resTexture = new VCND3D9Texture( d3dTexture ); resTexture->SetName( texturePath ); texID = VCNResourceCore::GetInstance()->AddResource( texturePath, resTexture ); } else { texID = resTexture->GetResourceID(); } VCNMaterial* material = new VCNMaterial(); material->SetName( VCNString(VCNTXT("material_dx_mesh_")) + filename ); material->SetAmbientColor( VCNColor((const VCNFloat*)&d3dxMaterials[i].MatD3D.Ambient) ); material->SetDiffuseColor( VCNColor((const VCNFloat*)&d3dxMaterials[i].MatD3D.Diffuse) ); material->SetSpecularColor( VCNColor((const VCNFloat*)&d3dxMaterials[i].MatD3D.Specular) ); material->SetSpecularPower( d3dxMaterials[i].MatD3D.Power ); VCNEffectParamSet& params = material->GetEffectParamSet(); params.SetEffectID( eidLitTextured ); params.AddResource( VCNTXT("DiffuseTexture"), texID ); // Add material as a resource. mMaterialID = VCNResourceCore::GetInstance()->AddResource( material->GetName(), material ); } } // Optimize the mesh if possible // const VCNUInt faceCount = systemMesh->GetNumFaces(); DWORD* adjac = new DWORD[faceCount*3]; hr = systemMesh->GenerateAdjacency(0.5f, adjac); hr = systemMesh->OptimizeInplace(D3DXMESHOPT_VERTEXCACHE, adjac, NULL, NULL, NULL); // Calculate Tangent and Binormal hr = D3DXComputeTangentFrameEx(systemMesh, D3DDECLUSAGE_TEXCOORD, 0, D3DDECLUSAGE_TANGENT, 0, D3DDECLUSAGE_BINORMAL, 0, D3DDECLUSAGE_NORMAL, 0, D3DXTANGENT_DONT_ORTHOGONALIZE | D3DXTANGENT_WEIGHT_BY_AREA, adjac, -1.01f, -0.01f, -1.01f, &systemMesh, NULL); delete [] adjac; // Load caches // const VCNUInt vertexCount = systemMesh->GetNumVertices(); const DWORD meshFVF = systemMesh->GetFVF(); const DWORD stride = D3DXGetFVFVertexSize( meshFVF ); const DWORD positionStride = D3DXGetFVFVertexSize( D3DFVF_XYZ ); const DWORD normalStride = D3DXGetFVFVertexSize( D3DFVF_NORMAL ); const DWORD diffuseStride = D3DXGetFVFVertexSize( D3DFVF_DIFFUSE ); const DWORD textureStride = D3DXGetFVFVertexSize( D3DFVF_TEX1 ); VCNFloat* vtPositionBufStart = new VCNFloat[vertexCount * kCacheStrides[VT_POSITION]]; VCNFloat* vtNormalBufStart = new VCNFloat[vertexCount * kCacheStrides[VT_LIGHTING]]; VCNFloat* vtTextureBufStart = new VCNFloat[vertexCount * kCacheStrides[VT_DIFFUSE_TEX_COORDS]]; VCNFloat* vtPositionBuf = vtPositionBufStart; VCNFloat* vtNormalBuf = vtNormalBufStart; VCNFloat* vtTextureBuf = vtTextureBufStart; BYTE* vbptr = NULL; systemMesh->LockVertexBuffer(D3DLOCK_READONLY, (LPVOID*)&vbptr); for(VCNUInt i = 0; i < vertexCount; ++i) { if ( meshFVF == (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1) ) { // Read position D3DXVECTOR3* pos = (D3DXVECTOR3*)vbptr; *vtPositionBuf = pos->x; vtPositionBuf++; *vtPositionBuf = pos->y; vtPositionBuf++; *vtPositionBuf = pos->z; vtPositionBuf++; // Read normal D3DXVECTOR3* normal = (D3DXVECTOR3*)(vbptr + positionStride); *vtNormalBuf = normal->x; vtNormalBuf++; *vtNormalBuf = normal->y; vtNormalBuf++; *vtNormalBuf = normal->z; vtNormalBuf++; // Set default diffuse color std::fill(vtNormalBuf, vtNormalBuf+3, 1.0f); vtNormalBuf += 3; float* texCoords = (float*)(vbptr + positionStride + normalStride); *vtTextureBuf = texCoords[0]; vtTextureBuf++; *vtTextureBuf = texCoords[1]; vtTextureBuf++; vbptr += stride; } else { VCN_ASSERT_FAIL( VCNTXT("Mesh FVF not supported [FVF(%d) stride = %d]"), meshFVF, stride ); } } systemMesh->UnlockVertexBuffer(); VCNResID positionCache = renderer->CreateCache(VT_POSITION, vtPositionBufStart, vertexCount * kCacheStrides[VT_POSITION]); VCNResID lightingCache = renderer->CreateCache(VT_LIGHTING, vtNormalBufStart, vertexCount * kCacheStrides[VT_LIGHTING]); VCNResID textureCache = renderer->CreateCache(VT_DIFFUSE_TEX_COORDS, vtTextureBufStart, vertexCount * kCacheStrides[VT_DIFFUSE_TEX_COORDS]); SetCacheID(VT_POSITION, positionCache); SetCacheID(VT_LIGHTING, lightingCache); SetCacheID(VT_DIFFUSE_TEX_COORDS, textureCache); delete [] vtPositionBufStart; delete [] vtNormalBufStart; delete [] vtTextureBufStart; VCNUShort* ibptr = NULL; VCNUShort* indices = new VCNUShort[faceCount * 3]; systemMesh->LockIndexBuffer(D3DLOCK_READONLY, (LPVOID*)&ibptr); for(VCNUInt i = 0; i < systemMesh->GetNumFaces(); i++) { indices[(i * 3) + 0] = *(ibptr++); indices[(i * 3) + 1] = *(ibptr++); indices[(i * 3) + 2] = *(ibptr++); } systemMesh->UnlockIndexBuffer(); VCNResID indexCacheID = renderer->CreateCache(VT_INDEX, indices, faceCount * 3 * kCacheStrides[VT_INDEX]); SetFaceCache(indexCacheID); SetFaceCount(faceCount); SetPrimitiveType(PT_TRIANGLELIST); delete [] indices; // Compute bounding sphere if ( !ComputeBoundingSphere(systemMesh) ) return false; return true; }