#include "Mesh.h" #include "Renderer.h" #include "Services.h" #include "ResourceManager.h" #include "Material.h" #include "VertexFormats.h" /*---------------------------------------------------- * Constructor: default material *----------------------------------------------------*/ Mesh::Mesh() : Resource("null", "null") { mMesh = 0; } /*---------------------------------------------------- * Constructor * @name - file name of the .x model * @path - path to the file *----------------------------------------------------*/ Mesh::Mesh(const std::string& name, const std::string& path) : Resource(name, path) { mMesh = 0; LoadXFile(); } /*---------------------------------------------------- * Mesh constructor: * Takes the name and path of the model. * Also takes the name of the texture and Mtrl to use * ***There must be a one:one ratio of materials and texture names *----------------------------------------------------*/ Mesh::Mesh(const std::string& name, const std::string& path, const std::string& textureName, D3DMATERIAL9& material) : Resource(name, path) { //get the material manager from the services manager MaterialRCManagerPtr mtrlManager = boost::static_pointer_cast>(Services::GetService(MaterialResService)); //create/get the material MaterialPtr mtrl = mtrlManager->Add(textureName, path); mtrl->SetMaterial(material); //add it to our list mMaterials.push_back(mtrl); mMesh = 0; LoadXFile(); } /*---------------------------------------------------- * Release the mesh *----------------------------------------------------*/ Mesh::~Mesh() { //release the d3d mesh mMesh = 0; //remove the materials from the manager MaterialRCManagerPtr mtrlManager = boost::static_pointer_cast>(Services::GetService(MaterialResService)); for(int i = 0; i < mMaterials.size(); i++) { mtrlManager->Remove(mMaterials[i]); } mMaterials.clear(); } /*---------------------------------------------------- * Set a material on a mesh. * Should be called only when the mesh has one material *----------------------------------------------------*/ void Mesh::SetMaterial(const std::string& name, const std::string& path) { if(mMaterials.size() > 0) // we have a material { mMaterials[0]->LoadTexture(name, path); } else //else create a new material { //get the material manager from the services manager MaterialRCManagerPtr mtrlManager = boost::static_pointer_cast>(Services::GetService(MaterialResService)); //create/get the material MaterialPtr mtrl = mtrlManager->Add(name, path); //add it to our list mMaterials.push_back(mtrl); } } /*---------------------------------------------------- * Draw the mesh subset *----------------------------------------------------*/ void Mesh::Draw(int subset) { HR(mMesh->DrawSubset(subset)); } /*---------------------------------------------------- * Load an .x model *----------------------------------------------------*/ void Mesh::LoadXFile() { // Step 1: Load the .x file from file into a system memory mesh. RendererPtr renderer = boost::static_pointer_cast(Services::GetService(RendererService)); ID3DXMeshPtr meshSys = 0; ID3DXBufferPtr adjBuffer = 0; ID3DXBufferPtr mtrlBuffer = 0; DWORD numMtrls = 0; HR(D3DXLoadMeshFromX(GetFileName().c_str(), D3DXMESH_SYSTEMMEM, renderer->GetDevice(), &adjBuffer, &mtrlBuffer, 0, &numMtrls, &meshSys)); // Step 2: Find out if the mesh already has normal info? D3DVERTEXELEMENT9 elems[MAX_FVF_DECL_SIZE]; HR(meshSys->GetDeclaration(elems)); bool hasNormals = false; D3DVERTEXELEMENT9 term = D3DDECL_END(); for(int i = 0; i < MAX_FVF_DECL_SIZE; ++i) { // Did we reach D3DDECL_END() {0xFF,0,D3DDECLTYPE_UNUSED, 0,0,0}? if(elems[i].Stream == 0xff ) break; if( elems[i].Type == D3DDECLTYPE_FLOAT3 && elems[i].Usage == D3DDECLUSAGE_NORMAL && elems[i].UsageIndex == 0 ) { hasNormals = true; break; } } // Step 3: Change vertex format to VertexPNT. D3DVERTEXELEMENT9 elements[64]; UINT numElements = 0; VertexPNT::Decl->GetDeclaration(elements, &numElements); ID3DXMeshPtr temp = 0; HR(meshSys->CloneMesh(D3DXMESH_SYSTEMMEM, elements, renderer->GetDevice(), &temp)); meshSys = 0; meshSys = temp; // Step 4: If the mesh did not have normals, generate them. if( hasNormals == false) HR(D3DXComputeNormals(meshSys, 0)); // Step 5: Optimize the mesh. HR(meshSys->Optimize(D3DXMESH_MANAGED | D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE, (DWORD*)adjBuffer->GetBufferPointer(), 0, 0, 0, &mMesh)); meshSys = 0; // Done w/ system mesh. adjBuffer = 0; // Done w/ buffer. // Step 6: Extract the materials and load the textures. MaterialRCManagerPtr mtrlManager = boost::static_pointer_cast>(Services::GetService(MaterialResService)); if( mtrlBuffer != 0 && numMtrls != 0 ) { D3DXMATERIAL* d3dxmtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer(); for(DWORD i = 0; i < numMtrls; ++i) { // Save the ith material. Note that the MatD3D property does not have an ambient // value set when its loaded, so just set it to the diffuse value. D3DMATERIAL9 m; m.Ambient = d3dxmtrls[i].MatD3D.Diffuse; m.Diffuse = d3dxmtrls[i].MatD3D.Diffuse; m.Specular = d3dxmtrls[i].MatD3D.Specular; m.Power = d3dxmtrls[i].MatD3D.Power; //mtrls.push_back( m ); // Check if the ith material has an associative texture MaterialPtr mtrl; if( d3dxmtrls[i].pTextureFilename != 0 ) { //add a reference to the mtrl manager //if the material with the texture name exists the mtrl manager //will just return the existing material. //If not, it will create a new material mtrl = mtrlManager->Add(d3dxmtrls[i].pTextureFilename, GetPath()); } else { // No texture for the ith subset mtrl = mtrlManager->Add(GetName() + "_mtrl", "null"); } //set the d3d material mtrl->SetMaterial(m); //add the material to the list mMaterials.push_back(mtrl); } } mtrlBuffer = 0; // done w/ buffer }