NLS Engine  v0.1
The Next Logical Step in game engine design.
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Defines
RenderableHelicalTube.cpp
Go to the documentation of this file.
00001 
00012 #include "RenderableHelicalTube.h"
00013 
00014 // System Library Includes
00015 #include <d3dx9.h>
00016 #include <cmath>
00017 #include <vector>
00018 #include <windows.h>
00019 
00020 // Application Library Includes
00021 #include <boost/foreach.hpp>
00022 #include <EngineConfig.h>
00023 
00024 // Local Includes
00025 #include "../../../sharedbase/EventLogger.h"
00026 #include "../../material/MaterialInterface.h"
00027 #include "../../texture/TextureInterface.h"
00028 
00029 // Forward Declarations
00030 
00031 // Typedefs and data structures
00032 namespace GraphicsCore {
00033   struct HelixVertex {
00034     D3DXVECTOR3 pos;
00035     D3DXVECTOR3 norm;
00036     D3DXVECTOR2 texcoord;
00037   };
00038 }
00039 
00040 // Constants
00041 namespace GraphicsCore {
00042   static const float PI        = 3.1415926535897932384626433832795f;
00043   static const float TWO_PI    = 6.283185307179586476925286766559f;
00044   
00045   static const unsigned long HELIX_VERTEX_FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
00046 }
00047 
00048 // Class methods
00049 namespace GraphicsCore {
00050   
00051   RenderableHelicalTube::RenderableHelicalTube(EntitySPTR owner, ModuleInterface* renderModule, LPDIRECT3DDEVICE9 d3dDevice) :
00052       RenderableComponent(owner, renderModule) {
00053     
00054     this->d3dDevice = d3dDevice;
00055     
00056     this->material = nullptr;
00057     
00058     this->texture = nullptr;
00059     
00060     this->SetLength(100.0);
00061     this->SetRadius(1.0);
00062     this->SetPitch(5.0);
00063     this->SetSegmentCount(24);
00064     
00065     this->mesh = nullptr;
00066   }
00067   
00068   RenderableHelicalTube::~RenderableHelicalTube() {
00069     this->RemoveMaterial(0);
00070     
00071     this->RemoveTexture(0);
00072   }
00073   
00074   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00075   bool RenderableHelicalTube::SetLength(float length) {
00076     if (length > 0.0 && length < 2000000) { // sanity check
00077       this->length = length;
00078       
00079       return true;
00080     }
00081     
00082     return false;
00083   }
00084   
00085   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00086   bool RenderableHelicalTube::SetRadius(float radius) {
00087     if (radius > 0.0 && radius < 2000000) { // sanity check
00088       this->radius = radius;
00089       
00090       return true;
00091     }
00092     
00093     return false;
00094   }
00095   
00096   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00097   bool RenderableHelicalTube::SetPitch(float pitch) {
00098     if (pitch > 0.0 && pitch < 2000000) { // sanity check
00099       this->pitch = pitch;
00100       
00101       return true;
00102     }
00103     
00104     return false;
00105   }
00106   
00107   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00108   bool RenderableHelicalTube::SetSegmentCount(unsigned int segments) {
00109     if (segments < 1000000) { // sanity check
00110       this->numberOfSegments = segments;
00111       
00112       return true;
00113     }
00114     
00115     return false;
00116   }
00117   
00118   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00119   void RenderableHelicalTube::Render(LPDIRECT3DDEVICE9 pd3dDevice) {
00120     if (this->mesh != nullptr) {
00121       if (this->material != nullptr) {
00122         this->material->Apply(pd3dDevice);
00123       }
00124       
00125       if (this->texture != nullptr) {
00126         this->texture->Apply(pd3dDevice);
00127       }
00128       
00129       this->mesh->DrawSubset(0);
00130     }
00131   }
00132   
00133   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00134   bool RenderableHelicalTube::InsertMaterial(unsigned int index, EnvelopeSPTR envelope) {
00135     MaterialInterface* newMaterial = nullptr;
00136     
00137     // Helical tube only has one material.
00138     if (index == 0) {
00139       newMaterial = MaterialsSystemInterface::CreateMaterial(envelope);
00140       
00141       // Make sure a new material was created!
00142       if (newMaterial != nullptr) {
00143         // Remove any pre-existing material
00144         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.
00145         
00146         // Place the new material
00147         this->material = newMaterial;
00148         
00149         return true;
00150       }
00151       else {
00152         LOG(LOG_PRIORITY::INFO, "Material could not be created; not replacing existing material.");
00153       }
00154     }
00155     else {
00156       LOG(LOG_PRIORITY::INFO, "Material index out of bounds: RenderableHelicalTube only supports 1 material at index 0." );
00157     }
00158     
00159     return false;
00160   }
00161   
00162   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00163   bool RenderableHelicalTube::RemoveMaterial(unsigned int index) {
00164     // Helical tube only has one material.
00165     if (index == 0) {
00166       MaterialsSystemInterface::DestroyMaterial(this->material);
00167       
00168       this->material = nullptr;
00169       
00170       return true;
00171     }
00172     else {
00173       LOG(LOG_PRIORITY::INFO, "Material index out of bounds: RenderableHelicalTube only supports 1 material at index 0." );
00174     }
00175     
00176     return false;
00177   }
00178   
00179   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00180   bool RenderableHelicalTube::InsertTexture(unsigned int index, EnvelopeSPTR envelope) {
00181     TextureInterface* newTexture = nullptr;
00182     
00183     // Helical tube only has one texture.
00184     if (index == 0) {
00185       newTexture = TexturesSystemInterface::CreateTexture(envelope, this->GetRenderModule());
00186       
00187       // Make sure a new texture was created!
00188       if (newTexture != nullptr) {
00189         // Remove any pre-existing texture
00190         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.
00191         
00192         // Place the new texture
00193         this->texture = newTexture;
00194         
00195         return true;
00196       }
00197       else {
00198         LOG(LOG_PRIORITY::INFO, "Texture could not be created; not replacing existing texture.");
00199       }
00200     }
00201     else {
00202       LOG(LOG_PRIORITY::INFO, "Texture index out of bounds: RenderableHelicalTube only supports 1 texture at index 0." );
00203     }
00204     
00205     return false;
00206   }
00207   
00208   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00209   bool RenderableHelicalTube::RemoveTexture(unsigned int index) {
00210     // Helical tube only has one texture.
00211     if (index == 0) {
00212       TexturesSystemInterface::DestroyTexture(this->texture);
00213       
00214       this->texture = nullptr;
00215       
00216       return true;
00217     }
00218     else {
00219       LOG(LOG_PRIORITY::INFO, "Texture index out of bounds: RenderableHelicalTube only supports 1 texture at index 0." );
00220     }
00221     
00222     return false;
00223   }
00224   
00225   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00226   void RenderableHelicalTube::UpdateGeometry() {
00227     std::vector<HelixVertex> vertices;
00228     std::vector<unsigned short> indices;
00229     
00230     // Create the vertices
00231     {
00232       HelixVertex vertex;
00233       float cur_angle, cur_length;
00234       
00235       float angle_step = TWO_PI / this->numberOfSegments;
00236       float length_step = this->pitch / (TWO_PI * this->numberOfSegments);
00237       
00238       float uv_repeat = 1.0f; // *BUG: the texture reversal that the below edge split operation is supposed to fix still occurs if this is higher than 1.
00239       
00240       float u_step = uv_repeat / (this->radius * TWO_PI); // Set to keep the texture square realative to the v.
00241       float v_step = uv_repeat / TWO_PI;
00242       
00243       cur_angle = 0.0;
00244       cur_length = 0.0;
00245       
00246       // Go the full length, plus whatever it takes to finish the turn.
00247       while (cur_length < this->length || cur_angle < TWO_PI) {
00248         while (cur_angle > TWO_PI) {
00249           // Correct the angle.
00250           cur_angle -= TWO_PI;
00251         }
00252         vertex.texcoord.x = cur_length * u_step;
00253         vertex.texcoord.y = cur_angle * v_step;
00254         
00255         cur_length += length_step;
00256         
00257         vertex.pos.x = this->radius * cos(cur_angle);
00258         vertex.pos.y = this->radius * sin(cur_angle);
00259         vertex.pos.z = cur_length;
00260         
00261         vertex.norm.x = -vertex.pos.x;
00262         vertex.norm.y = -vertex.pos.y;
00263         vertex.norm.z = 0.0;
00264         D3DXVec3Normalize(&vertex.norm, &vertex.norm);
00265         
00266         vertices.push_back(vertex);
00267         
00268         // Are we on a boundary vertex?
00269         if (cur_angle + angle_step >= TWO_PI) {
00270           // Add a duplicate vertex to create an edge split for the start of the next series with the corrected UV coordinate.
00271           // This is to work around the texture reversals that happen when the V coordinate goes from near 1.0 to near 0.0 in a single step.
00272           vertex.texcoord.y -= 1.0f;
00273           
00274           vertices.push_back(vertex);
00275         }
00276         
00277         cur_angle += angle_step;
00278       }
00279       
00280       if (vertices.size() < 3) {
00281         LOG(LOG_PRIORITY::ERR, "Parameters must be wrong because there are less than 3 vertices!");
00282         return;
00283       }
00284     }
00285     
00286     // Create the indices for the index buffer
00287     {
00288       unsigned short vertex_index = vertices.size();
00289       
00290       unsigned short row_count = this->numberOfSegments + 1;
00291       
00292       //*BUG: This is generating degenerate extra triangles at the split seam created above by the duplicate vertices.
00293       while (--vertex_index >= row_count) {
00294         // Triangle 1
00295         indices.push_back(vertex_index - row_count);
00296         indices.push_back(vertex_index - 1);
00297         indices.push_back(vertex_index);
00298         
00299         // Triangle 2
00300         indices.push_back(vertex_index - row_count + 1);
00301         indices.push_back(vertex_index - row_count);
00302         indices.push_back(vertex_index);
00303       }
00304     }
00305     
00306     // Build the mesh
00307     {
00308       // Create the mesh itself
00309       {
00310         // *NOTE: I'm not sure if creating a mesh over a previous mesh is correct or if the old one should be deleted somehow first... ~Ricky
00311         if (FAILED(D3DXCreateMeshFVF(
00312           indices.size() / 3, // Number of faces
00313           vertices.size(),    // Number of vertices
00314           D3DXMESH_MANAGED,   // Options
00315           HELIX_VERTEX_FVF,   // FVF
00316           this->d3dDevice,
00317           &this->mesh
00318         ))) {
00319           std::string err = "Mesh could not be initialized! " + std::string((this->mesh == nullptr) ? "Mesh was null, not sure what happened..." : "Mesh wasn't null.  Possible issue with needing to delete mesh before recreating.");
00320           LOG(LOG_PRIORITY::ERR, err);
00321           return;
00322         }
00323         
00324       }
00325       
00326       
00327       // Fill the vertex buffer
00328       {
00329         void* vertexBuffer;
00330         if (FAILED(this->mesh->LockVertexBuffer(D3DLOCK_DISCARD, (void**)&vertexBuffer))) {
00331           LOG(LOG_PRIORITY::ERR, "Vertex buffer could not be locked!");
00332           return;
00333         }
00334         
00335         memcpy(vertexBuffer, &vertices.at(0), sizeof(HelixVertex) * vertices.size()); // *NOTE: This may not be the best technique, as memcpy with std::vector is a hazardous operation.
00336         
00337         this->mesh->UnlockVertexBuffer();
00338       }
00339       
00340       // Fill the index buffer
00341       {
00342         void* indexBuffer;
00343         if (FAILED(this->mesh->LockIndexBuffer(0, &indexBuffer))) {
00344           LOG(LOG_PRIORITY::ERR, "Index buffer could not be locked!");
00345           return;
00346         }
00347         
00348         memcpy(indexBuffer, &indices.at(0), sizeof(unsigned short) * indices.size()); // *NOTE: This may not be the best technique, as memcpy with std::vector is a hazardous operation.
00349         
00350         this->mesh->UnlockIndexBuffer();
00351       }
00352     }
00353     
00354     // Optimize the mesh
00355     // HACK: I'll write the mesh optimization line when I feel like it! ~Ricky
00356     // May also want to D3DXConvertMeshSubsetToSingleStrip
00357     
00358     
00359     // Send the mesh to disk for analysis - only when debugging the above algorithms
00360     //D3DXSaveMeshToX("../tube.x", this->mesh, NULL, NULL, NULL, 0, D3DXF_FILEFORMAT_TEXT);
00361   }
00362 }