Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

3dsloader.cc

Go to the documentation of this file.
00001 // I would like to thank www.wosit.org and Terry Caton (tcaton@umr.edu) for his help on this.
00002 // Let me know if this helps you out!
00003 // Ben Humphrey (DigiBen)
00004 // Game Programmer
00005 // DigiBen@GameTutorials.com
00006 // Co-Web Host of www.GameTutorials.com
00007 
00008 #include "3dsLoader.h"
00009 #include <iostream>
00010 
00018 CLoad3DS::CLoad3DS() {
00019     m_CurrentChunk = new tChunk; // Initialize and allocate our current chunk
00020     m_TempChunk = new tChunk;    // Initialize and allocate a temporary chunk
00021 }
00022 
00023 
00028 bool 
00029 CLoad3DS::import3DS(t3DModel *pModel, char *strFileName) {
00030 
00031     // Open the 3DS file
00032     m_FilePointer = fopen(strFileName, "rb");
00033 
00034     // Make sure we have a valid file pointer (we found the file)
00035     if(!m_FilePointer) {
00036         cout <<  "Unable to find the file: " << strFileName << "\n";
00037         cout.flush();
00038         return false;
00039     }
00040 
00041     // Once we have the file open, we need to read the very first data chunk
00042     // to see if it's a 3DS file. 
00043     // If it is a 3DS file, then the first chunk ID will be equal to PRIMARY (some hex num)
00044 
00045     // Read the first chuck of the file to see if it's a 3DS file
00046     readChunk(m_CurrentChunk);
00047 
00048     // Make sure this is a 3DS file
00049     if (m_CurrentChunk->ID != PRIMARY) {
00050         cout << "Unable to load PRIMARY chuck from file \n";
00051         cout.flush();
00052         return false;
00053     }
00054 
00055     // Now we actually start reading in the data.  ProcessNextChunk() is recursive
00056     // Begin loading objects, by calling this recursive function
00057     processNextChunk(pModel, m_CurrentChunk);
00058 
00059     // After we have read the whole 3DS file, we want to calculate our own vertex normals.
00060     computeNormals(pModel);
00061 
00062     // Clean up after everything
00063     cleanUp();
00064 
00065     return 0;
00066 }
00067 
00071 void 
00072 CLoad3DS::cleanUp() {
00073     fclose(m_FilePointer);      // Close the current file pointer
00074     delete m_CurrentChunk;      // Free the current chunk
00075     delete m_TempChunk;         // Free our temporary chunk
00076 }
00077 
00078 
00082 void 
00083 CLoad3DS::processNextChunk(t3DModel *pModel, tChunk *pPreviousChunk) {
00084     t3DObject newObject = {0};      // This is used to add to our object list
00085     tMaterialInfo newTexture = {0}; // This is used to add to our material list
00086     unsigned short version = 0;     // This will hold the file version
00087     int buffer[50000] = {0};        // This is used to read past unwanted data
00088 
00089     m_CurrentChunk = new tChunk;    // Allocate a new chunk             
00090 
00091     // Below we check our chunk ID each time we read a new chunk.  Then, if
00092     // we want to extract the information from that chunk, we do so.
00093     // If we don't want a chunk, we just read past it.  
00094     // Continue to read the sub chunks until we have reached the length.
00095     // After we read ANYTHING we add the bytes read to the chunk and then check
00096     // check against the length.
00097     while (pPreviousChunk->bytesRead < pPreviousChunk->length) {
00098 
00099         // Read next Chunk
00100         readChunk(m_CurrentChunk);
00101 
00102         // Check the chunk ID
00103         switch (m_CurrentChunk->ID)     {
00104             case VERSION:                           // This holds the version of the file
00105                 
00106                 // This chunk has an unsigned short that holds the file version.
00107                 // Since there might be new additions to the 3DS file format in 4.0,
00108                 // we give a warning to that problem.
00109 
00110                 // Read the file version and add the bytes read to our bytesRead variable
00111                 m_CurrentChunk->bytesRead += fread(&version, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00112 
00113                 // If the file version is over 3, give a warning that there could be a problem
00114                 if (version > 0x03)
00115                     cout << "This 3DS file is over version 3 so it may load incorrectly \n";
00116                 break;
00117 
00118             case OBJECTINFO:                        // This holds the version of the mesh
00119                 
00120                 // This chunk holds the version of the mesh.  It is also the head of the MATERIAL
00121                 // and OBJECT chunks.  From here on we start reading in the material and object info.
00122                 // Read the next chunk
00123                 readChunk(m_TempChunk);
00124 
00125                 // Get the version of the mesh
00126                 m_TempChunk->bytesRead += fread(&version, 1, m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
00127 
00128                 // Increase the bytesRead by the bytes read from the last chunk
00129                 m_CurrentChunk->bytesRead += m_TempChunk->bytesRead;
00130 
00131                 // Go to the next chunk, which is the object has a texture, it should be MATERIAL, then OBJECT.
00132                 processNextChunk(pModel, m_CurrentChunk);
00133                 break;
00134 
00135             case MATERIAL:                          // This holds the material information
00136 
00137                 // This chunk is the header for the material info chunks
00138                 // Increase the number of materials
00139                 pModel->numOfMaterials++;
00140 
00141                 // Add a empty texture structure to our texture list.
00142                 // If you are unfamiliar with STL's "vector" class, all push_back()
00143                 // does is add a new node onto the list.  I used the vector class
00144                 // so I didn't need to write my own link list functions.  
00145                 pModel->pMaterials.push_back(newTexture);
00146 
00147                 // Proceed to the material loading function
00148                 processNextMaterialChunk(pModel, m_CurrentChunk);
00149                 break;
00150 
00151             case OBJECT:                            // This holds the name of the object being read
00152                     
00153                 // This chunk is the header for the object info chunks.  It also
00154                 // holds the name of the object.
00155 
00156                 // Increase the object count
00157                 pModel->numOfObjects++;
00158             
00159                 // Add a new tObject node to our list of objects (like a link list)
00160                 pModel->pObject.push_back(newObject);
00161                 
00162                 // Initialize the object and all it's data members
00163                 memset(&(pModel->pObject[pModel->numOfObjects - 1]), 0, sizeof(t3DObject));
00164 
00165                 // Get the name of the object and store it, then add the read bytes to our byte counter.
00166                 m_CurrentChunk->bytesRead += getString(pModel->pObject[pModel->numOfObjects - 1].strName);
00167                 
00168                 // Now proceed to read in the rest of the object information
00169                 processNextObjectChunk(pModel, &(pModel->pObject[pModel->numOfObjects - 1]), m_CurrentChunk);
00170                 break;
00171 
00172             case EDITKEYFRAME:
00173 
00174                 // Because I wanted to make this a SIMPLE tutorial as possible, I did not include
00175                 // the key frame information.  This chunk is the header for all the animation info.
00176                 // In a later tutorial this will be the subject and explained thoroughly.
00177                 // processNextKeyFrameChunk(pModel, m_CurrentChunk);
00178 
00179                 // Read past this chunk and add the bytes read to the byte counter
00180                 m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00181                 break;
00182 
00183             default:            
00184                 // If we didn't care about a chunk, then we get here.  We still need
00185                 // to read past the unknown or ignored chunk and add the bytes read to the byte counter.
00186                 m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00187                 break;
00188         }
00189 
00190         // Add the bytes read from the last chunk to the previous chunk passed in.
00191         pPreviousChunk->bytesRead += m_CurrentChunk->bytesRead;
00192     }
00193 
00194     // Free the current chunk and set it back to the previous chunk (since it started that way)
00195     delete m_CurrentChunk;
00196     m_CurrentChunk = pPreviousChunk;
00197 }
00198 
00199 
00203 void 
00204 CLoad3DS::processNextObjectChunk(t3DModel *pModel, t3DObject *pObject, tChunk *pPreviousChunk) {
00205 
00206     int buffer[50000] = {0};                    // This is used to read past unwanted data
00207 
00208     // Allocate a new chunk to work with
00209     m_CurrentChunk = new tChunk;
00210 
00211     // Continue to read these chunks until we read the end of this sub chunk
00212     while (pPreviousChunk->bytesRead < pPreviousChunk->length) {
00213         
00214         // Read the next chunk
00215         readChunk(m_CurrentChunk);
00216 
00217         // Check which chunk we just read
00218         switch (m_CurrentChunk->ID) {
00219             case OBJECT_MESH:                   // This lets us know that we are reading a new object
00220             
00221                 // We found a new object, so let's read in it's info using recursion
00222                 processNextObjectChunk(pModel, pObject, m_CurrentChunk);
00223                 break;
00224 
00225             case OBJECT_VERTICES:               // This is the objects vertices
00226                 readVertices(pObject, m_CurrentChunk);
00227                 break;
00228 
00229             case OBJECT_FACES:                  // This is the objects face information
00230                 readVertexIndices(pObject, m_CurrentChunk);
00231                 break;
00232 
00233             case OBJECT_MATERIAL:               // This holds the material name that the object has
00234                 
00235                 // This chunk holds the name of the material that the object has assigned to it.
00236                 // This could either be just a color or a texture map.  This chunk also holds
00237                 // the faces that the texture is assigned to (In the case that there is multiple
00238                 // textures assigned to one object, or it just has a texture on a part of the object.
00239                 // Since most of my game objects just have the texture around the whole object, and 
00240                 // they aren't multitextured, I just want the material name.
00241                 // We now will read the name of the material assigned to this object
00242                 readObjectMaterial(pModel, pObject, m_CurrentChunk);            
00243                 break;
00244 
00245             case OBJECT_UV:                     // This holds the UV texture coordinates for the object
00246 
00247                 // This chunk holds all of the UV coordinates for our object.  Let's read them in.
00248                 readUVCoordinates(pObject, m_CurrentChunk);
00249                 break;
00250 
00251             default:  
00252 
00253                 // Read past the ignored or unknown chunks
00254                 m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00255                 break;
00256         }
00257 
00258         // Add the bytes read from the last chunk to the previous chunk passed in.
00259         pPreviousChunk->bytesRead += m_CurrentChunk->bytesRead;
00260     }
00261 
00262     // Free the current chunk and set it back to the previous chunk (since it started that way)
00263     delete m_CurrentChunk;
00264     m_CurrentChunk = pPreviousChunk;
00265 }
00266 
00267 
00271 void 
00272 CLoad3DS::processNextMaterialChunk(t3DModel *pModel, tChunk *pPreviousChunk) {
00273     int buffer[50000] = {0};                    // This is used to read past unwanted data
00274 
00275     // Allocate a new chunk to work with
00276     m_CurrentChunk = new tChunk;
00277 
00278     // Continue to read these chunks until we read the end of this sub chunk
00279     while (pPreviousChunk->bytesRead < pPreviousChunk->length)  {
00280         
00281         // Read the next chunk
00282         readChunk(m_CurrentChunk);
00283 
00284         // Check which chunk we just read in
00285         switch (pModel, m_CurrentChunk->ID) {
00286 
00287             case MATNAME:                           // This chunk holds the name of the material                
00288             
00289                 // Here we read in the material name
00290                 m_CurrentChunk->bytesRead += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strName, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00291                 break;
00292 
00293             case MATDIFFUSE:                        // This holds the R G B color of our object
00294                 readColorChunk(&(pModel->pMaterials[pModel->numOfMaterials - 1]), m_CurrentChunk);
00295                 break;
00296             
00297             case MATMAP:                            // This is the header for the texture info
00298                 
00299                 // Proceed to read in the material information
00300                 processNextMaterialChunk(pModel, m_CurrentChunk);
00301                 break;
00302 
00303             case MATMAPFILE:                        // This stores the file name of the material
00304 
00305                 // Here we read in the material's file name
00306                 m_CurrentChunk->bytesRead += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strFile, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00307                 break;
00308             
00309             default:  
00310 
00311                 // Read past the ignored or unknown chunks
00312                 m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00313                 break;
00314         }
00315 
00316         // Add the bytes read from the last chunk to the previous chunk passed in.
00317         pPreviousChunk->bytesRead += m_CurrentChunk->bytesRead;
00318     }
00319 
00320     // Free the current chunk and set it back to the previous chunk (since it started that way)
00321     delete m_CurrentChunk;
00322     m_CurrentChunk = pPreviousChunk;
00323 }
00324 
00325 
00329 void 
00330 CLoad3DS::readChunk(tChunk *pChunk) {
00331     // This reads the chunk ID which is 2 bytes.
00332     // The chunk ID is like OBJECT or MATERIAL.  It tells what data is
00333     // able to be read in within the chunks section.  
00334     pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer);
00335 
00336     // Then, we read the length of the chunk which is 4 bytes.
00337     // This is how we know how much to read in, or read past.
00338     pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer);
00339 }
00340 
00344 int 
00345 CLoad3DS::getString(char *pBuffer)
00346 {
00347     int index = 0;
00348 
00349     // Read 1 byte of data which is the first letter of the string
00350     fread(pBuffer, 1, 1, m_FilePointer);
00351 
00352     // Loop until we get NULL
00353     while (*(pBuffer + index++) != 0) {
00354 
00355         // Read in a character at a time until we hit NULL.
00356         fread(pBuffer + index, 1, 1, m_FilePointer);
00357     }
00358 
00359     // Return the string length, which is how many bytes we read in (including the NULL)
00360     return strlen(pBuffer) + 1;
00361 }
00362 
00363 
00367 void 
00368 CLoad3DS::readColorChunk(tMaterialInfo *pMaterial, tChunk *pChunk) {
00369     // Read the color chunk info
00370     readChunk(m_TempChunk);
00371 
00372     // Read in the R G B color (3 bytes - 0 through 255)
00373     m_TempChunk->bytesRead += fread(pMaterial->color, 1, m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
00374 
00375     // Add the bytes read to our chunk
00376     pChunk->bytesRead += m_TempChunk->bytesRead;
00377 }
00378 
00379 
00383 void 
00384 CLoad3DS::readVertexIndices(t3DObject *pObject, tChunk *pPreviousChunk) {
00385     unsigned short index = 0;                   // This is used to read in the current face index
00386 
00387     // In order to read in the vertex indices for the object, we need to first
00388     // read in the number of them, then read them in.  Remember,
00389     // we only want 3 of the 4 values read in for each face.  The fourth is
00390     // a visibility flag for 3D Studio Max that doesn't mean anything to us.
00391 
00392     // Read in the number of faces that are in this object (int)
00393     pPreviousChunk->bytesRead += fread(&pObject->numOfFaces, 1, 2, m_FilePointer);
00394 
00395     // Alloc enough memory for the faces and initialize the structure
00396     pObject->pFaces = new tFace [pObject->numOfFaces];
00397     memset(pObject->pFaces, 0, sizeof(tFace) * pObject->numOfFaces);
00398 
00399     // Go through all of the faces in this object
00400     for(int i = 0; i < pObject->numOfFaces; i++)
00401     {
00402         // Next, we read in the A then B then C index for the face, but ignore the 4th value.
00403         // The fourth value is a visibility flag for 3D Studio Max, we don't care about this.
00404         for(int j = 0; j < 4; j++)
00405         {
00406             // Read the first vertice index for the current face 
00407             pPreviousChunk->bytesRead += fread(&index, 1, sizeof(index), m_FilePointer);
00408 
00409             if(j < 3)
00410             {
00411                 // Store the index in our face structure.
00412                 pObject->pFaces[i].vertIndex[j] = index;
00413             }
00414         }
00415     }
00416 }
00417 
00418 
00422 void 
00423 CLoad3DS::readUVCoordinates(t3DObject *pObject, tChunk *pPreviousChunk) {
00424     // In order to read in the UV indices for the object, we need to first
00425     // read in the amount there are, then read them in.
00426     // Read in the number of UV coordinates there are (int)
00427     pPreviousChunk->bytesRead += fread(&pObject->numTexVertex, 1, 2, m_FilePointer);
00428 
00429     // Allocate memory to hold the UV coordinates
00430     pObject->pTexVerts = new tVector2 [pObject->numTexVertex];
00431 
00432     // Read in the texture coodinates (an array 2 float)
00433     pPreviousChunk->bytesRead += fread(pObject->pTexVerts, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
00434 }
00435 
00436 
00440 void 
00441 CLoad3DS::readVertices(t3DObject *pObject, tChunk *pPreviousChunk) {
00442     // Like most chunks, before we read in the actual vertices, we need
00443     // to find out how many there are to read in.  Once we have that number
00444     // we then fread() them into our vertice array.
00445 
00446     // Read in the number of vertices (int)
00447     pPreviousChunk->bytesRead += fread(&(pObject->numOfVerts), 1, 2, m_FilePointer);
00448 
00449     // Allocate the memory for the verts and initialize the structure
00450     pObject->pVerts = new tVector3 [pObject->numOfVerts];
00451     memset(pObject->pVerts, 0, sizeof(tVector3) * pObject->numOfVerts);
00452 
00453     // Read in the array of vertices (an array of 3 floats)
00454     pPreviousChunk->bytesRead += fread(pObject->pVerts, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
00455 }
00456 
00457 
00461 void 
00462 CLoad3DS::readObjectMaterial(t3DModel *pModel, t3DObject *pObject, tChunk *pPreviousChunk) {
00463     char strMaterial[255] = {0};            // This is used to hold the objects material name
00464     int buffer[50000] = {0};                // This is used to read past unwanted data
00465 
00466     // A material is either the color or the texture map of the object.
00467     // It can also hold other information like the brightness, shine, etc... Stuff we don't
00468     // really care about.  We just want the color, or the texture map file name really.
00469     // Here we read the material name that is assigned to the current object.
00470     // strMaterial should now have a string of the material name, like "Material #2" etc..
00471     pPreviousChunk->bytesRead += getString(strMaterial);
00472 
00473     // Now that we have a material name, we need to go through all of the materials
00474     // and check the name against each material.  When we find a material in our material
00475     // list that matches this name we just read in, then we assign the materialID
00476     // of the object to that material index.  You will notice that we passed in the
00477     // model to this function.  This is because we need the number of textures.
00478     // Yes though, we could have just passed in the model and not the object too.
00479 
00480     // Go through all of the textures
00481     for(int i = 0; i < pModel->numOfMaterials; i++)
00482     {
00483         // If the material we just read in matches the current texture name
00484         if(strcmp(strMaterial, pModel->pMaterials[i].strName) == 0)
00485         {
00486             // Set the material ID to the current index 'i' and stop checking
00487             pObject->materialID = i;
00488 
00489             // Now that we found the material, check if it's a texture map.
00490             // If the strFile has a string length of 1 and over it's a texture
00491             if(strlen(pModel->pMaterials[i].strFile) > 0) {
00492 
00493                 // Set the object's flag to say it has a texture map to bind.
00494                 pObject->bHasTexture = true;
00495             }   
00496             break;
00497         }
00498     }
00499 
00500     // Read past the rest of the chunk since we don't care about shared vertices
00501     // You will notice we subtract the bytes already read in this chunk from the total length.
00502     pPreviousChunk->bytesRead += fread(buffer, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
00503 }           
00504 
00505 
00506 // This computes the magnitude of a normal.   (magnitude = sqrt(x^2 + y^2 + z^2)
00507 #define MAG(Normal) (sqrt(Normal.x*Normal.x + Normal.y*Normal.y + Normal.z*Normal.z))
00508 
00512 tVector3 
00513 Vector(tVector3 vPoint1, tVector3 vPoint2) {
00514     tVector3 vVector;                           // The variable to hold the resultant vector
00515 
00516     vVector.x = vPoint1.x - vPoint2.x;          // Subtract point1 and point2 x's
00517     vVector.y = vPoint1.y - vPoint2.y;          // Subtract point1 and point2 y's
00518     vVector.z = vPoint1.z - vPoint2.z;          // Subtract point1 and point2 z's
00519 
00520     return vVector;                             // Return the resultant vector
00521 }
00522 
00526 tVector3 
00527 addVector(tVector3 vVector1, tVector3 vVector2) {
00528     tVector3 vResult;                   
00529     
00530     vResult.x = vVector2.x + vVector1.x; // Add Vector1 and Vector2 x's
00531     vResult.y = vVector2.y + vVector1.y; // Add Vector1 and Vector2 y's
00532     vResult.z = vVector2.z + vVector1.z; // Add Vector1 and Vector2 z's
00533 
00534     return vResult;                             // Return the resultant vector
00535 }
00536 
00540 tVector3 
00541 divideVectorByScaler(tVector3 vVector1, float scaler) {
00542     tVector3 vResult;                           
00543     
00544     vResult.x = vVector1.x / scaler;            
00545     vResult.y = vVector1.y / scaler;            
00546     vResult.z = vVector1.z / scaler;            
00547 
00548     return vResult;                             
00549 }
00550 
00554 tVector3 
00555 cross(tVector3 vVector1, tVector3 vVector2) {
00556     tVector3 vCross;    
00557                 
00558     vCross.x = ((vVector1.y * vVector2.z) - (vVector1.z * vVector2.y));             
00559     vCross.y = ((vVector1.z * vVector2.x) - (vVector1.x * vVector2.z));                                             
00560     vCross.z = ((vVector1.x * vVector2.y) - (vVector1.y * vVector2.x));
00561     return vCross;                              
00562 }
00563 
00567 tVector3 
00568 normalize(tVector3 vNormal) {
00569     double magnitude;                           // This holds the magitude          
00570 
00571     magnitude = MAG(vNormal);                   // Get the magnitude
00572 
00573     vNormal.x /= (float)magnitude;          
00574     vNormal.y /= (float)magnitude;          
00575     vNormal.z /= (float)magnitude;          
00576 
00577     return vNormal;                         
00578 }
00579 
00583 void 
00584 CLoad3DS::computeNormals(t3DModel *pModel) {
00585     tVector3 vVector1, vVector2, vNormal, vPoly[3];
00586 
00587     // If there are no objects, we can skip this part
00588     if(pModel->numOfObjects <= 0)
00589         return;
00590     
00591     // Go through each of the objects to calculate their normals
00592     for(int index = 0; index < pModel->numOfObjects; index++)
00593     {
00594         // Get the current object
00595         t3DObject *pObject = &(pModel->pObject[index]);
00596 
00597         // Here we allocate all the memory we need to calculate the normals
00598         tVector3 *pNormals      = new tVector3 [pObject->numOfFaces];
00599         tVector3 *pTempNormals  = new tVector3 [pObject->numOfFaces];
00600         pObject->pNormals       = new tVector3 [pObject->numOfVerts];
00601 
00602         // Go though all of the faces of this object
00603         for(int i=0; i < pObject->numOfFaces; i++)
00604         {                                               
00605             // To cut down LARGE code, we extract the 3 points of this face
00606             vPoly[0] = pObject->pVerts[pObject->pFaces[i].vertIndex[0]];
00607             vPoly[1] = pObject->pVerts[pObject->pFaces[i].vertIndex[1]];
00608             vPoly[2] = pObject->pVerts[pObject->pFaces[i].vertIndex[2]];
00609 
00610             // Now let's calculate the face normals (Get 2 vectors and find the cross product of those 2)
00611 
00612             vVector1 = Vector(vPoly[0], vPoly[2]);      // Get the vector of the polygon (we just need 2 sides for the normal)
00613             vVector2 = Vector(vPoly[2], vPoly[1]);      // Get a second vector of the polygon
00614 
00615             vNormal  = cross(vVector1, vVector2);       // Return the cross product of the 2 vectors (normalize vector, but not a unit vector)
00616             pTempNormals[i] = vNormal;                  // Save the un-normalized normal for the vertex normals
00617             vNormal  = normalize(vNormal);              // Normalize the cross product to give us the polygons normal
00618             
00619             pNormals[i] = vNormal;                      // Assign the normal to the list of normals
00620         }
00621 
00623 
00624         tVector3 vSum = {0.0, 0.0, 0.0};
00625         tVector3 vZero = vSum;
00626         int shared=0;
00627 
00628         for (i = 0; i < pObject->numOfVerts; i++)           // Go through all of the vertices
00629         {
00630             for (int j = 0; j < pObject->numOfFaces; j++)   // Go through all of the triangles
00631             {                                               // Check if the vertex is shared by another face
00632                 if (pObject->pFaces[j].vertIndex[0] == i || 
00633                     pObject->pFaces[j].vertIndex[1] == i || 
00634                     pObject->pFaces[j].vertIndex[2] == i)
00635                 {
00636                     vSum = addVector(vSum, pTempNormals[j]);// Add the un-normalized normal of the shared face
00637                     shared++;                               // Increase the number of shared triangles
00638                 }
00639             }      
00640             
00641             // Get the normal by dividing the sum by the shared.  We negate the shared so it has the normals pointing out.
00642             pObject->pNormals[i] = divideVectorByScaler(vSum, float(-shared));
00643 
00644             // Normalize the normal for the final vertex normal
00645             pObject->pNormals[i] = normalize(pObject->pNormals[i]); 
00646 
00647             vSum = vZero;                                   // Reset the sum
00648             shared = 0;                                     // Reset the shared
00649         }
00650     
00651         // Free our memory and start over on the next object
00652         delete [] pTempNormals;
00653         delete [] pNormals;
00654     }
00655 }

Anoid NG © Michael Westergaard, Martin Stig Stissing, Ronni Michael Laursen, and Kristian Bisgaard Lassen