NLS Engine  v0.1
The Next Logical Step in game engine design.
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Defines
ModelRenderable.cpp
Go to the documentation of this file.
00001 
00008 #include "ModelRenderable.h"
00009 
00010 #include <cassert>
00011 
00012 #include <d3d9.h>
00013 #include <d3dx9tex.h>
00014 #include <boost/foreach.hpp>
00015 #include <boost/lexical_cast.hpp>
00016 
00017 #include <EngineConfig.h>
00018 
00019 #include "../../../sharedbase/Entity.h"
00020 #include "../../../sharedbase/QuatMath.h"
00021 #include "../../../sharedbase/EventLogger.h"
00022 #include "../../../sharedbase/Envelope.h"
00023 
00024 #include "../../RenderModule.h"
00025 
00026 #include "../../material/BasicMaterial.h"
00027 #include "../../texture/BasicTexture.h"
00028 
00029 #include "../../Materials.h"
00030 #include "../../Textures.h"
00031 
00032 namespace GraphicsCore {
00033   MeshCacheMap ModelComponent::MeshCache;
00034 }
00035 
00036 namespace GraphicsCore {
00037   ModelComponent::ModelComponent(EntitySPTR owner, ModuleInterface* renderModule) :
00038     RenderableComponent(owner, renderModule),
00039     mesh(nullptr),
00040     materialCount(0)
00041     {
00042   } //end of constructor
00043 
00044   ModelComponent::~ModelComponent() {
00045     long index;
00046     
00047     index = this->materialCount;
00048     while (--index >= 0) {
00049       this->RemoveMaterial(index);
00050       this->RemoveTexture(index);
00051     }
00052   } //end of destructor
00053   
00054   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00055   void ModelComponent::Render(LPDIRECT3DDEVICE9 d3d_device) {
00056     if (this->mesh != NULL) {
00057       //loop through the number of materials for the mesh
00058       for (unsigned long i = 0; i < this->materialCount; ++i) {
00059 
00060         //set the material for the mesh subset
00061         this->meshMaterials.at(i)->Apply(d3d_device);
00062         if (i < this->meshTextures.size()) {
00063           d3d_device->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
00064           this->meshTextures.at(i)->Apply(d3d_device);
00065         }
00066 
00067         //Draw the current mesh subset
00068         this->mesh->DrawSubset(i);
00069 
00070       } //end for loop
00071     }
00072   } //end Render()
00073 
00074   
00075   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00076   bool ModelComponent::InsertMaterial(unsigned int index, EnvelopeSPTR envelope) {
00077     MaterialInterface* newMaterial = nullptr;
00078     
00079     // Make sure we're not higher than the model can handle.
00080     if (index < this->materialCount) {
00081       newMaterial = MaterialsSystemInterface::CreateMaterial(envelope);
00082       
00083       // Make sure a new material was created!
00084       if (newMaterial != nullptr) {
00085         // Remove any pre-existing material
00086         this->RemoveMaterial(index); // Return value doesn't need to be tested: if there was no material, we can continue.  If there was, this will remove it and we can continue.
00087         
00088         // Place the new material
00089         if (index >= this->meshMaterials.size()) {
00090           assert(index == this->meshMaterials.size()); // If this fails then we are trying to insert way too far ahead.
00091           this->meshMaterials.push_back(newMaterial);
00092         }
00093         else {
00094           this->meshMaterials.at(index) = newMaterial;
00095         }
00096         
00097         return true;
00098       }
00099       else {
00100         LOG(LOG_PRIORITY::INFO, "Material could not be created; not replacing existing material.");
00101       }
00102     }
00103     else {
00104       LOG(LOG_PRIORITY::INFO, "Material index out of bounds: this ModelComponent only supports " + boost::lexical_cast < std::string > (this->materialCount) + " materials at indices 0 through " + boost::lexical_cast < std::string > (this->materialCount - 1) + "." );
00105     }
00106     
00107     return false;
00108   }
00109 
00110   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00111   bool ModelComponent::RemoveMaterial(unsigned int index) {
00112     // Make sure we're not higher than the model can handle.
00113     if (index < this->materialCount) {
00114       // Only do removals if the index actually exists in the list.  If it doesn't, then it didn't really need removal, as it doesn't exist!
00115       if (index < this->meshMaterials.size()) {
00116         
00117         MaterialsSystemInterface::DestroyMaterial(this->meshMaterials.at(index));
00118         
00119         this->meshMaterials.at(index) = nullptr;
00120       }
00121       
00122       return true;
00123     }
00124     else {
00125       LOG(LOG_PRIORITY::INFO, "Material index of '" + boost::lexical_cast < std::string > (index) + "' out of bounds: this ModelComponent only supports " + boost::lexical_cast < std::string > (this->materialCount) + " materials at indices 0 through " + boost::lexical_cast < std::string > (this->materialCount - 1) + "." );
00126     }
00127     
00128     return false;
00129   }
00130 
00131 
00132   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00133   bool ModelComponent::InsertTexture(unsigned int index, EnvelopeSPTR envelope) {
00134     TextureInterface* newTexture = nullptr;
00135     
00136     // Make sure we're not higher than the model can handle.
00137     if (index < this->materialCount) {
00138       newTexture = TexturesSystemInterface::CreateTexture(envelope, this->GetRenderModule());
00139       
00140       // Make sure a new texture was created!
00141       if (newTexture != nullptr) {
00142         // Remove any pre-existing texture
00143         this->RemoveTexture(index); // Return value doesn't need to be tested: if there was no texture, we can continue.  If there was, this will remove it and we can continue.
00144         
00145         // Place the new texture
00146         if (index >= this->meshTextures.size()) {
00147           assert(index == this->meshTextures.size()); // If this fails then we are trying to insert way too far ahead.
00148           this->meshTextures.push_back(newTexture);
00149         }
00150         else {
00151           this->meshTextures.at(index) = newTexture;
00152         }
00153         
00154         return true;
00155       }
00156       else {
00157         LOG(LOG_PRIORITY::INFO, "Texture could not be created; not replacing existing texture.");
00158       }
00159     }
00160     else {
00161       LOG(LOG_PRIORITY::INFO, "Texture index out of bounds: this ModelComponent only supports " + boost::lexical_cast < std::string > (this->materialCount) + " textures at indices 0 through " + boost::lexical_cast < std::string > (this->materialCount - 1) + "." );
00162     }
00163     
00164     return false;
00165   }
00166 
00167   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00168   bool ModelComponent::RemoveTexture(unsigned int index) {
00169     // Make sure we're not higher than the model can handle.
00170     if (index < this->materialCount) {
00171       // Only do removals if the index actually exists in the list.  If it doesn't, then it didn't really need removal, as it doesn't exist!
00172       if (index < this->meshTextures.size()) {
00173         
00174         TexturesSystemInterface::DestroyTexture(this->meshTextures.at(index));
00175         
00176         this->meshTextures.at(index) = nullptr;
00177       }
00178       
00179       return true;
00180     }
00181     else {
00182       LOG(LOG_PRIORITY::INFO, "Texture index of '" + boost::lexical_cast < std::string > (index) + "' out of bounds: this ModelComponent only supports " + boost::lexical_cast < std::string > (this->materialCount) + " textures at indices 0 through " + boost::lexical_cast < std::string > (this->materialCount - 1) + "." );
00183     }
00184     
00185     return false;
00186   }
00187 
00188 
00189   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00190   bool ModelComponent::LoadMeshFromFile(const std::string& file) {
00191     LPD3DXBUFFER materialBuffer;
00192     LPD3DXMESH mesh;
00193     
00194     RenderModule* renderModule = this->GetRenderModule();
00195 
00196     // Verify that we actually have a pointer to a RenderModule
00197     assert(renderModule != nullptr);
00198     
00199     MeshCacheMap::const_iterator mesh_cache_it = ModelComponent::MeshCache.find(file);
00200     if (mesh_cache_it != ModelComponent::MeshCache.end()) {
00201       LOG(LOG_PRIORITY::FLOW, "Mesh cache hit on file '" + file + "'!");
00202       
00203       this->materialCount = mesh_cache_it->second->materialCount;
00204       materialBuffer = mesh_cache_it->second->materialBuffer;
00205       mesh =  mesh_cache_it->second->mesh;
00206     }
00207     else {
00208       LOG(LOG_PRIORITY::FLOW, "Mesh cache miss on file '" + file + "', loading from disk and storing in cache.");
00209       HRESULT hr(renderModule->MeshFactory(NLS_ENGINE_DATA_PATH + file, D3DXMESH_SYSTEMMEM, NULL, &materialBuffer, NULL, &(this->materialCount), &mesh));
00210       
00211       ModelComponent::MeshCache.insert(MeshCacheMapValue(file, std::shared_ptr<MeshData>(new MeshData(mesh, this->materialCount, materialBuffer))));
00212       
00213       if (FAILED(hr)) {
00214         LOG(LOG_PRIORITY::INFO, "Error loading mesh '" + file + "' from disk!");
00215         return false;
00216       }
00217     }
00218     
00219     SetMaterials(materialBuffer);
00220     
00221     this->mesh = mesh;
00222     
00223     return true;
00224   } //end LoadMeshFromFile()
00225 
00226   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00227   // Good info at http://www.toymaker.info/Games/html/load_x_simply.html
00228   void ModelComponent::SetMaterials(LPD3DXBUFFER materialBuffer) {
00229     //pointer to the material buffer within the mesh
00230     D3DXMATERIAL* file_materials = (D3DXMATERIAL*) materialBuffer->GetBufferPointer();
00231 
00232     //Loop through the materials in the mesh and create a D3DMATERIAL for each one
00233     for (unsigned long index = 0; index < this->materialCount; ++index) {
00234       // Copy the materials.
00235       {
00236         // Override the Ambient color with the Diffuse color.  This is a workaround for invalid ambient values in the mesh file spec.
00237         file_materials[index].MatD3D.Ambient = file_materials[index].MatD3D.Diffuse;
00238         
00239         EnvelopeSPTR material(new Envelope());
00240         material->msgid = ID_MATERIAL_BASIC_USING_MAT;
00241         material->AddData(file_materials[index].MatD3D);
00242         
00243         this->InsertMaterial(index, material);
00244       }
00245       
00246       // Bring in the textures
00247       {
00248         EnvelopeSPTR texture(new Envelope());
00249         texture->msgid = ID_TEXTURE_BASIC;
00250         
00251         if (file_materials[index].pTextureFilename) {
00252           texture->AddData(ID_TEXTURE_PARAM_FILE);
00253           texture->AddData(std::string(file_materials[index].pTextureFilename));
00254         }
00255         
00256         this->InsertTexture(index, texture);
00257       }
00258     } //end for loop
00259   }
00260 }