コード例 #1
0
ファイル: Model.cpp プロジェクト: TheRealMJP/SamplePattern
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;
}
コード例 #2
0
ファイル: DXUTMesh.cpp プロジェクト: n-n-n/Flow2D
//-----------------------------------------------------------------------------
// 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;
}
コード例 #3
0
ファイル: Model.cpp プロジェクト: larrson/metashader
	//------------------------------------------------------------------------------------------
	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;
	}
コード例 #4
0
ファイル: WaterDemo.cpp プロジェクト: erickterri/3dlearn
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();
}
コード例 #5
0
ファイル: main.cpp プロジェクト: galek/Asylum_Tutorials
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;
}
コード例 #6
0
ファイル: D3D9Mesh.cpp プロジェクト: artemeliy/inf4715
////////////////////////////////////////////////////////////////////////
///
/// 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;
}