Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/orxonox/branches/showroom/Showroom/3ds.cpp @ 1884

Last change on this file since 1884 was 1869, checked in by john, 21 years ago

Johns Showroom

File size: 36.0 KB
Line 
1//***********************************************************************//
2//                                                                                                                                               //
3//              - "Talk to me like I'm a 3 year old!" Programming Lessons -              //
4//                                                                       //
5//              $Author:                DigiBen         digiben@gametutorials.com                        //
6//                                                                                                                                               //
7//              $Program:               3DS Loader                                                                               //
8//                                                                                                                                               //
9//              $Description:   Demonstrates how to load a .3ds file format              //
10//                                                                                                                                               //
11//              $Date:                  10/6/01                                                                                  //
12//                                                                                                                                               //
13//***********************************************************************//
14
15#include "3ds.h"
16#include <assert.h>
17#include <math.h>
18
19// Global
20int gBuffer[50000] = {0};                                       // This is used to read past unwanted data
21
22// This file handles all of the code needed to load a .3DS file.
23// Basically, how it works is, you load a chunk, then you check
24// the chunk ID.  Depending on the chunk ID, you load the information
25// that is stored in that chunk.  If you do not want to read that information,
26// you read past it.  You know how many bytes to read past the chunk because
27// every chunk stores the length in bytes of that chunk.
28
29///////////////////////////////// CLOAD3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
30/////
31/////   This constructor initializes the tChunk data
32/////
33///////////////////////////////// CLOAD3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
34
35CLoad3ds::CLoad3ds()
36{
37        m_FilePointer = NULL;
38}
39
40///////////////////////////////// IMPORT 3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
41/////
42/////   This is called by the client to open the .3ds file, read it, then clean up
43/////
44///////////////////////////////// IMPORT 3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
45
46bool CLoad3ds::Import3DS(C3dModel *pModel, char *strFileName)
47{
48        char strMessage[255] = {0};
49        tChunk currentChunk = {0};
50
51        // Open the 3DS file
52        m_FilePointer = fopen(strFileName, "rb");
53
54        // Make sure we have a valid file pointer (we found the file)
55        if(!m_FilePointer) 
56        {
57                sprintf(strMessage, "Unable to find the file: %s!", strFileName);
58                MessageBox(NULL, strMessage, "Error", MB_OK);
59                return false;
60        }
61
62        // Once we have the file open, we need to read the very first data chunk
63        // to see if it's a 3DS file.  That way we don't read an invalid file.
64        // If it is a 3DS file, then the first chunk ID will be equal to PRIMARY (some hex num)
65
66        // Read the first chuck of the file to see if it's a 3DS file
67        ReadChunk(&currentChunk);
68
69        // Make sure this is a 3DS file
70        if (currentChunk.ID != PRIMARY)
71        {
72                sprintf(strMessage, "Unable to load PRIMARY chuck from file: %s!", strFileName);
73                MessageBox(NULL, strMessage, "Error", MB_OK);
74                return false;
75        }
76
77        // Now we actually start reading in the data.  ProcessNextChunk() is recursive
78
79        // Begin loading objects, by calling this recursive function
80        ProcessNextChunk(pModel, &currentChunk);
81
82        // After we have read the whole 3DS file, we want to calculate our own vertex normals.
83        ComputeNormals(pModel);
84
85        // Clean up after everything
86        CleanUp();
87
88        return true;
89}
90
91///////////////////////////////// CLEAN UP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
92/////
93/////   This function cleans up our allocated memory and closes the file
94/////
95///////////////////////////////// CLEAN UP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
96
97void CLoad3ds::CleanUp()
98{
99        if (m_FilePointer) {
100                fclose(m_FilePointer);                                  // Close the current file pointer
101                m_FilePointer = NULL;
102        }
103}
104
105
106///////////////////////////////// PROCESS NEXT CHUNK\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
107/////
108/////   This function reads the main sections of the .3DS file, then dives deeper with recursion
109/////
110///////////////////////////////// PROCESS NEXT CHUNK\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
111
112void CLoad3ds::ProcessNextChunk(C3dModel *pModel, tChunk *pPreviousChunk)
113{
114        t3dObject newObject = {0};                                      // This is used to add to our object list
115        tMaterialInfo newTexture = {0};                         // This is used to add to our material list
116
117        tChunk currentChunk = {0};                                      // The current chunk to load
118        tChunk tempChunk = {0};                                         // A temp chunk for holding data               
119
120        // Below we check our chunk ID each time we read a new chunk.  Then, if
121        // we want to extract the information from that chunk, we do so.
122        // If we don't want a chunk, we just read past it. 
123
124        // Continue to read the sub chunks until we have reached the length.
125        // After we read ANYTHING we add the bytes read to the chunk and then check
126        // check against the length.
127        while (pPreviousChunk->bytesRead < pPreviousChunk->length)
128        {
129                // Read next Chunk
130                ReadChunk(&currentChunk);
131
132                // Check the chunk ID
133                switch (currentChunk.ID)
134                {
135                case VERSION:                                                   // This holds the version of the file
136                       
137                        // If the file was made in 3D Studio Max, this chunk has an int that
138                        // holds the file version.  Since there might be new additions to the 3DS file
139                        // format in 4.0, we give a warning to that problem.
140                        // However, if the file wasn't made by 3D Studio Max, we don't 100% what the
141                        // version length will be so we'll simply ignore the value
142
143                        // Read the file version and add the bytes read to our bytesRead variable
144                        currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
145
146                        // If the file version is over 3, give a warning that there could be a problem
147                        if ((currentChunk.length - currentChunk.bytesRead == 4) && (gBuffer[0] > 0x03)) {
148                                MessageBox(NULL, "This 3DS file is over version 3 so it may load incorrectly", "Warning", MB_OK);
149                        }
150                        break;
151
152                case OBJECTINFO:                                                // This holds the version of the mesh
153                        {       
154                        // This chunk holds the version of the mesh.  It is also the head of the MATERIAL
155                        // and OBJECT chunks.  From here on we start reading in the material and object info.
156
157                        // Read the next chunk
158                        ReadChunk(&tempChunk);
159
160                        // Get the version of the mesh
161                        tempChunk.bytesRead += fread(gBuffer, 1, tempChunk.length - tempChunk.bytesRead, m_FilePointer);
162
163                        // Increase the bytesRead by the bytes read from the last chunk
164                        currentChunk.bytesRead += tempChunk.bytesRead;
165
166                        // Go to the next chunk, which is the object has a texture, it should be MATERIAL, then OBJECT.
167                        ProcessNextChunk(pModel, &currentChunk);
168                        break;
169                }
170                case MATERIAL:                                                  // This holds the material information
171
172                        // This chunk is the header for the material info chunks
173
174                        // Increase the number of materials
175                        pModel->numOfMaterials++;
176
177                        // Add a empty texture structure to our texture list.
178                        // If you are unfamiliar with STL's "vector" class, all push_back()
179                        // does is add a new node onto the list.  I used the vector class
180                        // so I didn't need to write my own link list functions. 
181                        pModel->pMaterials.push_back(newTexture);
182
183                        // Proceed to the material loading function
184                        ProcessNextMaterialChunk(pModel, &currentChunk);
185                        break;
186
187                case OBJECT:                                                    // This holds the name of the object being read
188                               
189                        // This chunk is the header for the object info chunks.  It also
190                        // holds the name of the object.
191
192                        // Increase the object count
193                        pModel->numOfObjects++;
194               
195                        // Add a new tObject node to our list of objects (like a link list)
196                        pModel->pObject.push_back(newObject);
197                       
198                        // Initialize the object and all it's data members
199                        memset(&(pModel->pObject[pModel->numOfObjects - 1]), 0, sizeof(t3dObject));
200
201                        // Get the name of the object and store it, then add the read bytes to our byte counter.
202                        currentChunk.bytesRead += GetString(pModel->pObject[pModel->numOfObjects - 1].strName);
203                       
204                        // Now proceed to read in the rest of the object information
205                        ProcessNextObjectChunk(pModel, &(pModel->pObject[pModel->numOfObjects - 1]), &currentChunk);
206                        break;
207
208                case EDITKEYFRAME:
209
210                        // Because I wanted to make this a SIMPLE tutorial as possible, I did not include
211                        // the key frame information.  This chunk is the header for all the animation info.
212                        // In a later tutorial this will be the subject and explained thoroughly.
213
214                        //ProcessNextKeyFrameChunk(pModel, currentChunk);
215
216                        // Read past this chunk and add the bytes read to the byte counter
217                        currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
218                        break;
219
220                default: 
221                       
222                        // If we didn't care about a chunk, then we get here.  We still need
223                        // to read past the unknown or ignored chunk and add the bytes read to the byte counter.
224                        currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
225                        break;
226                }
227
228                // Add the bytes read from the last chunk to the previous chunk passed in.
229                pPreviousChunk->bytesRead += currentChunk.bytesRead;
230        }
231}
232
233
234///////////////////////////////// PROCESS NEXT OBJECT CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
235/////
236/////   This function handles all the information about the objects in the file
237/////
238///////////////////////////////// PROCESS NEXT OBJECT CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
239
240void CLoad3ds::ProcessNextObjectChunk(C3dModel *pModel, t3dObject *pObject, tChunk *pPreviousChunk)
241{
242        // The current chunk to work with
243        tChunk currentChunk = {0};
244
245        // Continue to read these chunks until we read the end of this sub chunk
246        while (pPreviousChunk->bytesRead < pPreviousChunk->length)
247        {
248                // Read the next chunk
249                ReadChunk(&currentChunk);
250
251                // Check which chunk we just read
252                switch (currentChunk.ID)
253                {
254                case OBJECT_MESH:                                       // This lets us know that we are reading a new object
255               
256                        // We found a new object, so let's read in it's info using recursion
257                        ProcessNextObjectChunk(pModel, pObject, &currentChunk);
258                        break;
259
260                case OBJECT_VERTICES:                           // This is the objects vertices
261                        ReadVertices(pObject, &currentChunk);
262                        break;
263
264                case OBJECT_FACES:                                      // This is the objects face information
265                        ReadVertexIndices(pObject, &currentChunk);
266                        break;
267
268                case OBJECT_MATERIAL:                           // This holds the material name that the object has
269                       
270                        // This chunk holds the name of the material that the object has assigned to it.
271                        // This could either be just a color or a texture map.  This chunk also holds
272                        // the faces that the texture is assigned to (In the case that there is multiple
273                        // textures assigned to one object, or it just has a texture on a part of the object.
274                        // Since most of my game objects just have the texture around the whole object, and
275                        // they aren't multitextured, I just want the material name.
276
277                        // We now will read the name of the material assigned to this object
278                        ReadObjectMaterial(pModel, pObject, &currentChunk);                     
279                        break;
280
281                case OBJECT_UV:                                         // This holds the UV texture coordinates for the object
282
283                        // This chunk holds all of the UV coordinates for our object.  Let's read them in.
284                        ReadUVCoordinates(pObject, &currentChunk);
285                        break;
286
287                default: 
288
289                        // Read past the ignored or unknown chunks
290                        currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
291                        break;
292                }
293
294                // Add the bytes read from the last chunk to the previous chunk passed in.
295                pPreviousChunk->bytesRead += currentChunk.bytesRead;
296        }
297}
298
299
300///////////////////////////////// PROCESS NEXT MATERIAL CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
301/////
302/////   This function handles all the information about the material (Texture)
303/////
304///////////////////////////////// PROCESS NEXT MATERIAL CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
305
306void CLoad3ds::ProcessNextMaterialChunk(C3dModel *pModel, tChunk *pPreviousChunk)
307{
308        // The current chunk to work with
309        tChunk currentChunk = {0};
310
311        // Continue to read these chunks until we read the end of this sub chunk
312        while (pPreviousChunk->bytesRead < pPreviousChunk->length)
313        {
314                // Read the next chunk
315                ReadChunk(&currentChunk);
316
317                // Check which chunk we just read in
318                switch (currentChunk.ID)
319                {
320                case MATNAME:                                                   // This chunk holds the name of the material
321                       
322                        // Here we read in the material name
323                        currentChunk.bytesRead += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strName, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
324                        break;
325
326                case MATDIFFUSE:                                                // This holds the R G B color of our object
327                        ReadColorChunk(&(pModel->pMaterials[pModel->numOfMaterials - 1]), &currentChunk);
328                        break;
329               
330                case MATMAP:                                                    // This is the header for the texture info
331                       
332                        // Proceed to read in the material information
333                        ProcessNextMaterialChunk(pModel, &currentChunk);
334                        break;
335
336                case MATMAPFILE:                                                // This stores the file name of the material
337
338                        // Here we read in the material's file name
339                        currentChunk.bytesRead += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strFile, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
340                        break;
341               
342                default: 
343
344                        // Read past the ignored or unknown chunks
345                        currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
346                        break;
347                }
348
349                // Add the bytes read from the last chunk to the previous chunk passed in.
350                pPreviousChunk->bytesRead += currentChunk.bytesRead;
351        }
352}
353
354///////////////////////////////// READ CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
355/////
356/////   This function reads in a chunk ID and it's length in bytes
357/////
358///////////////////////////////// READ CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
359
360void CLoad3ds::ReadChunk(tChunk *pChunk)
361{
362        // This reads the chunk ID which is 2 bytes.
363        // The chunk ID is like OBJECT or MATERIAL.  It tells what data is
364        // able to be read in within the chunks section. 
365        pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer);
366
367        // Then, we read the length of the chunk which is 4 bytes.
368        // This is how we know how much to read in, or read past.
369        pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer);
370}
371
372///////////////////////////////// GET STRING \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
373/////
374/////   This function reads in a string of characters
375/////
376///////////////////////////////// GET STRING \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
377
378int CLoad3ds::GetString(char *pBuffer)
379{
380        int index = 0;
381
382        // Read 1 byte of data which is the first letter of the string
383        fread(pBuffer, 1, 1, m_FilePointer);
384
385        // Loop until we get NULL
386        while (*(pBuffer + index++) != 0) {
387
388                // Read in a character at a time until we hit NULL.
389                fread(pBuffer + index, 1, 1, m_FilePointer);
390        }
391
392        // Return the string length, which is how many bytes we read in (including the NULL)
393        return strlen(pBuffer) + 1;
394}
395
396
397///////////////////////////////// READ COLOR \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
398/////
399/////   This function reads in the RGB color data
400/////
401///////////////////////////////// READ COLOR \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
402
403void CLoad3ds::ReadColorChunk(tMaterialInfo *pMaterial, tChunk *pChunk)
404{
405        tChunk tempChunk = {0};
406
407        // Read the color chunk info
408        ReadChunk(&tempChunk);
409
410        // Read in the R G B color (3 bytes - 0 through 255)
411        tempChunk.bytesRead += fread(pMaterial->color, 1, tempChunk.length - tempChunk.bytesRead, m_FilePointer);
412
413        // Add the bytes read to our chunk
414        pChunk->bytesRead += tempChunk.bytesRead;
415}
416
417
418///////////////////////////////// READ VERTEX INDECES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
419/////
420/////   This function reads in the indices for the vertex array
421/////
422///////////////////////////////// READ VERTEX INDECES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
423
424void CLoad3ds::ReadVertexIndices(t3dObject *pObject, tChunk *pPreviousChunk)
425{
426        unsigned short index = 0;                                       // This is used to read in the current face index
427
428        // In order to read in the vertex indices for the object, we need to first
429        // read in the number of them, then read them in.  Remember,
430        // we only want 3 of the 4 values read in for each face.  The fourth is
431        // a visibility flag for 3D Studio Max that doesn't mean anything to us.
432
433        // Read in the number of faces that are in this object (int)
434        pPreviousChunk->bytesRead += fread(&pObject->iNumOfFaces, 1, 2, m_FilePointer);
435
436        // Alloc enough memory for the faces and initialize the structure
437        pObject->pFaces = new tFace [pObject->iNumOfFaces];
438        memset(pObject->pFaces, 0, sizeof(tFace) * pObject->iNumOfFaces);
439
440        // Go through all of the faces in this object
441        for(int i = 0; i < pObject->iNumOfFaces; i++)
442        {
443                // Next, we read in the A then B then C index for the face, but ignore the 4th value.
444                // The fourth value is a visibility flag for 3D Studio Max, we don't care about this.
445                for(int j = 0; j < 4; j++)
446                {
447                        // Read the first vertice index for the current face
448                        pPreviousChunk->bytesRead += fread(&index, 1, sizeof(index), m_FilePointer);
449
450                        if(j < 3)
451                        {
452                                // Store the index in our face structure.
453                                pObject->pFaces[i].vertIndex[j] = index;
454                        }
455                }
456        }
457}
458
459
460///////////////////////////////// READ UV COORDINATES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
461/////
462/////   This function reads in the UV coordinates for the object
463/////
464///////////////////////////////// READ UV COORDINATES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
465
466void CLoad3ds::ReadUVCoordinates(t3dObject *pObject, tChunk *pPreviousChunk)
467{
468        // In order to read in the UV indices for the object, we need to first
469        // read in the amount there are, then read them in.
470
471        // Read in the number of UV coordinates there are (int)
472        pPreviousChunk->bytesRead += fread(&pObject->iNumTexVertex, 1, 2, m_FilePointer);
473
474        // Allocate memory to hold the UV coordinates
475        pObject->pTexVerts = new CVector2 [pObject->iNumTexVertex];
476
477        // Read in the texture coodinates (an array 2 float)
478        pPreviousChunk->bytesRead += fread(pObject->pTexVerts, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
479}
480
481
482///////////////////////////////// READ VERTICES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
483/////
484/////   This function reads in the vertices for the object
485/////
486///////////////////////////////// READ VERTICES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
487
488void CLoad3ds::ReadVertices(t3dObject *pObject, tChunk *pPreviousChunk)
489{
490        // Like most chunks, before we read in the actual vertices, we need
491        // to find out how many there are to read in.  Once we have that number
492        // we then fread() them into our vertice array.
493
494        // Read in the number of vertices (int)
495        pPreviousChunk->bytesRead += fread(&(pObject->iNumOfVerts), 1, 2, m_FilePointer);
496
497        // Allocate the memory for the verts and initialize the structure
498        pObject->pVerts = new CVector3 [pObject->iNumOfVerts];
499        memset(pObject->pVerts, 0, sizeof(CVector3) * pObject->iNumOfVerts);
500
501        // Read in the array of vertices (an array of 3 floats)
502        pPreviousChunk->bytesRead += fread(pObject->pVerts, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
503
504        // Now we should have all of the vertices read in.  Because 3D Studio Max
505        // Models with the Z-Axis pointing up (strange and ugly I know!), we need
506        // to flip the y values with the z values in our vertices.  That way it
507        // will be normal, with Y pointing up.  If you prefer to work with Z pointing
508        // up, then just delete this next loop.  Also, because we swap the Y and Z
509        // we need to negate the Z to make it come out correctly.
510
511        // Go through all of the vertices that we just read and swap the Y and Z values
512        for(int i = 0; i < pObject->iNumOfVerts; i++)
513        {
514                // Store off the Y value
515                float fTempY = pObject->pVerts[i].y;
516
517                // Set the Y value to the Z value
518                pObject->pVerts[i].y = pObject->pVerts[i].z;
519
520                // Set the Z value to the Y value,
521                // but negative Z because 3D Studio max does the opposite.
522                pObject->pVerts[i].z = -fTempY;
523        }
524}
525
526
527///////////////////////////////// READ OBJECT MATERIAL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
528/////
529/////   This function reads in the material name assigned to the object and sets the materialID
530/////
531///////////////////////////////// READ OBJECT MATERIAL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
532
533void CLoad3ds::ReadObjectMaterial(C3dModel *pModel, t3dObject *pObject, tChunk *pPreviousChunk)
534{
535        char strMaterial[255] = {0};                    // This is used to hold the objects material name
536
537        // *What is a material?*  - A material is either the color or the texture map of the object.
538        // It can also hold other information like the brightness, shine, etc... Stuff we don't
539        // really care about.  We just want the color, or the texture map file name really.
540
541        // Here we read the material name that is assigned to the current object.
542        // strMaterial should now have a string of the material name, like "Material #2" etc..
543        pPreviousChunk->bytesRead += GetString(strMaterial);
544
545        // Now that we have a material name, we need to go through all of the materials
546        // and check the name against each material.  When we find a material in our material
547        // list that matches this name we just read in, then we assign the materialID
548        // of the object to that material index.  You will notice that we passed in the
549        // model to this function.  This is because we need the number of textures.
550        // Yes though, we could have just passed in the model and not the object too.
551
552        // Go through all of the textures
553        for(int i = 0; i < pModel->numOfMaterials; i++)
554        {
555                // If the material we just read in matches the current texture name
556                if(strcmp(strMaterial, pModel->pMaterials[i].strName) == 0)
557                {
558                        // Set the material ID to the current index 'i' and stop checking
559                        pObject->materialID = i;
560
561                        // Now that we found the material, check if it's a texture map.
562                        // If the strFile has a string length of 1 and over it's a texture
563                        if(strlen(pModel->pMaterials[i].strFile) > 0) {
564
565                                // Set the object's flag to say it has a texture map to bind.
566                                pObject->bHasTexture = true;
567                        }       
568                        break;
569                }
570                else
571                {
572                        // Set the ID to -1 to show there is no material for this object
573                        pObject->materialID = -1;
574                }
575        }
576
577        // Read past the rest of the chunk since we don't care about shared vertices
578        // You will notice we subtract the bytes already read in this chunk from the total length.
579        pPreviousChunk->bytesRead += fread(gBuffer, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
580}                       
581
582// *Note*
583//
584// Below are some math functions for calculating vertex normals.  We want vertex normals
585// because it makes the lighting look really smooth and life like.  You probably already
586// have these functions in the rest of your engine, so you can delete these and call
587// your own.  I wanted to add them so I could show how to calculate vertex normals.
588
589//////////////////////////////  Math Functions  ////////////////////////////////*
590
591// This computes the magnitude of a normal.   (magnitude = sqrt(x^2 + y^2 + z^2)
592#define Mag(Normal) (sqrt(Normal.x*Normal.x + Normal.y*Normal.y + Normal.z*Normal.z))
593
594// This calculates a vector between 2 points and returns the result
595CVector3 Vector(CVector3 vPoint1, CVector3 vPoint2)
596{
597        CVector3 vVector;                                                       // The variable to hold the resultant vector
598
599        vVector.x = vPoint1.x - vPoint2.x;                      // Subtract point1 and point2 x's
600        vVector.y = vPoint1.y - vPoint2.y;                      // Subtract point1 and point2 y's
601        vVector.z = vPoint1.z - vPoint2.z;                      // Subtract point1 and point2 z's
602
603        return vVector;                                                         // Return the resultant vector
604}
605
606// This adds 2 vectors together and returns the result
607CVector3 AddVector(CVector3 vVector1, CVector3 vVector2)
608{
609        CVector3 vResult;                                                       // The variable to hold the resultant vector
610       
611        vResult.x = vVector2.x + vVector1.x;            // Add Vector1 and Vector2 x's
612        vResult.y = vVector2.y + vVector1.y;            // Add Vector1 and Vector2 y's
613        vResult.z = vVector2.z + vVector1.z;            // Add Vector1 and Vector2 z's
614
615        return vResult;                                                         // Return the resultant vector
616}
617
618// This divides a vector by a single number (scalar) and returns the result
619CVector3 DivideVectorByScaler(CVector3 vVector1, float Scaler)
620{
621        CVector3 vResult;                                                       // The variable to hold the resultant vector
622       
623        vResult.x = vVector1.x / Scaler;                        // Divide Vector1's x value by the scaler
624        vResult.y = vVector1.y / Scaler;                        // Divide Vector1's y value by the scaler
625        vResult.z = vVector1.z / Scaler;                        // Divide Vector1's z value by the scaler
626
627        return vResult;                                                         // Return the resultant vector
628}
629
630// This returns the cross product between 2 vectors
631CVector3 Cross(CVector3 vVector1, CVector3 vVector2)
632{
633        CVector3 vCross;                                                                // The vector to hold the cross product
634                                                                                                // Get the X value
635        vCross.x = ((vVector1.y * vVector2.z) - (vVector1.z * vVector2.y));
636                                                                                                // Get the Y value
637        vCross.y = ((vVector1.z * vVector2.x) - (vVector1.x * vVector2.z));
638                                                                                                // Get the Z value
639        vCross.z = ((vVector1.x * vVector2.y) - (vVector1.y * vVector2.x));
640
641        return vCross;                                                          // Return the cross product
642}
643
644// This returns the normal of a vector
645CVector3 Normalize(CVector3 vNormal)
646{
647        double Magnitude;                                                       // This holds the magitude                     
648
649        Magnitude = Mag(vNormal);                                       // Get the magnitude
650
651        vNormal.x /= (float)Magnitude;                          // Divide the vector's X by the magnitude
652        vNormal.y /= (float)Magnitude;                          // Divide the vector's Y by the magnitude
653        vNormal.z /= (float)Magnitude;                          // Divide the vector's Z by the magnitude
654
655        return vNormal;                                                         // Return the normal
656}
657
658///////////////////////////////// COMPUTER NORMALS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
659/////
660/////   This function computes the normals and vertex normals of the objects
661/////
662///////////////////////////////// COMPUTER NORMALS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
663
664void CLoad3ds::ComputeNormals(C3dModel *pModel)
665{
666        CVector3 vVector1, vVector2, vNormal, vPoly[3];
667
668        // If there are no objects, we can skip this part
669        if(pModel->numOfObjects <= 0)
670                return;
671
672        // What are vertex normals?  And how are they different from other normals?
673        // Well, if you find the normal to a triangle, you are finding a "Face Normal".
674        // If you give OpenGL a face normal for lighting, it will make your object look
675        // really flat and not very round.  If we find the normal for each vertex, it makes
676        // the smooth lighting look.  This also covers up blocky looking objects and they appear
677        // to have more polygons than they do.    Basically, what you do is first
678        // calculate the face normals, then you take the average of all the normals around each
679        // vertex.  It's just averaging.  That way you get a better approximation for that vertex.
680
681        // Go through each of the objects to calculate their normals
682        for(int index = 0; index < pModel->numOfObjects; index++)
683        {
684                // Get the current object
685                t3dObject *pObject = &(pModel->pObject[index]);
686
687                // Here we allocate all the memory we need to calculate the normals
688                CVector3 *pNormals              = new CVector3 [pObject->iNumOfFaces];
689                CVector3 *pTempNormals  = new CVector3 [pObject->iNumOfFaces];
690                pObject->pNormals               = new CVector3 [pObject->iNumOfVerts];
691
692                // Go though all of the faces of this object
693                for(int i=0; i < pObject->iNumOfFaces; i++)
694                {                                                                                               
695                        // To cut down LARGE code, we extract the 3 points of this face
696                        vPoly[0] = pObject->pVerts[pObject->pFaces[i].vertIndex[0]];
697                        vPoly[1] = pObject->pVerts[pObject->pFaces[i].vertIndex[1]];
698                        vPoly[2] = pObject->pVerts[pObject->pFaces[i].vertIndex[2]];
699
700                        // Now let's calculate the face normals (Get 2 vectors and find the cross product of those 2)
701
702                        vVector1 = Vector(vPoly[0], vPoly[2]);          // Get the vector of the polygon (we just need 2 sides for the normal)
703                        vVector2 = Vector(vPoly[2], vPoly[1]);          // Get a second vector of the polygon
704
705                        vNormal  = Cross(vVector1, vVector2);           // Return the cross product of the 2 vectors (normalize vector, but not a unit vector)
706                        pTempNormals[i] = vNormal;                                      // Save the un-normalized normal for the vertex normals
707                        vNormal  = Normalize(vNormal);                          // Normalize the cross product to give us the polygons normal
708
709                        pNormals[i] = vNormal;                                          // Assign the normal to the list of normals
710                }
711
712                //////////////// Now Get The Vertex Normals /////////////////
713
714                CVector3 vSum = {0.0, 0.0, 0.0};
715                CVector3 vZero = vSum;
716                int shared=0;
717
718                for (int i = 0; i < pObject->iNumOfVerts; i++)                  // Go through all of the vertices
719                {
720                        for (int j = 0; j < pObject->iNumOfFaces; j++)  // Go through all of the triangles
721                        {                                                                                               // Check if the vertex is shared by another face
722                                if (pObject->pFaces[j].vertIndex[0] == i || 
723                                        pObject->pFaces[j].vertIndex[1] == i || 
724                                        pObject->pFaces[j].vertIndex[2] == i)
725                                {
726                                        vSum = AddVector(vSum, pTempNormals[j]);// Add the un-normalized normal of the shared face
727                                        shared++;                                                               // Increase the number of shared triangles
728                                }
729                        }     
730                       
731                        // Get the normal by dividing the sum by the shared.  We negate the shared so it has the normals pointing out.
732                        pObject->pNormals[i] = DivideVectorByScaler(vSum, float(-shared));
733
734                        // Normalize the normal for the final vertex normal
735                        pObject->pNormals[i] = Normalize(pObject->pNormals[i]); 
736
737                        vSum = vZero;                                                                   // Reset the sum
738                        shared = 0;                                                                             // Reset the shared
739                }
740       
741                // Free our memory and start over on the next object
742                delete [] pTempNormals;
743                delete [] pNormals;
744        }
745}
746
747
748/////////////////////////////////////////////////////////////////////////////////
749//
750// * QUICK NOTES *
751//
752// This was a HUGE amount of knowledge and probably the largest tutorial yet!
753// In the next tutorial we will show you how to load a text file format called .obj.
754// This is the most common 3D file format that almost ANY 3D software will import.
755//
756// Once again I should point out that the coordinate system of OpenGL and 3DS Max are different.
757// Since 3D Studio Max Models with the Z-Axis pointing up (strange and ugly I know! :),
758// we need to flip the y values with the z values in our vertices.  That way it
759// will be normal, with Y pointing up.  Also, because we swap the Y and Z we need to negate
760// the Z to make it come out correctly.  This is also explained and done in ReadVertices().
761//
762// CHUNKS: What is a chunk anyway?
763//
764// "The chunk ID is a unique code which identifies the type of data in this chunk
765// and also may indicate the existence of subordinate chunks. The chunk length indicates
766// the length of following data to be associated with this chunk. Note, this may
767// contain more data than just this chunk. If the length of data is greater than that
768// needed to fill in the information for the chunk, additional subordinate chunks are
769// attached to this chunk immediately following any data needed for this chunk, and
770// should be parsed out. These subordinate chunks may themselves contain subordinate chunks.
771// Unfortunately, there is no indication of the length of data, which is owned by the current
772// chunk, only the total length of data attached to the chunk, which means that the only way
773// to parse out subordinate chunks is to know the exact format of the owning chunk. On the
774// other hand, if a chunk is unknown, the parsing program can skip the entire chunk and
775// subordinate chunks in one jump. " - Jeff Lewis (werewolf@worldgate.com)
776//
777// In a short amount of words, a chunk is defined this way:
778// 2 bytes - Stores the chunk ID (OBJECT, MATERIAL, PRIMARY, etc...)
779// 4 bytes - Stores the length of that chunk.  That way you know when that
780//           chunk is done and there is a new chunk.
781//
782// So, to start reading the 3DS file, you read the first 2 bytes of it, then
783// the length (using fread()).  It should be the PRIMARY chunk, otherwise it isn't
784// a .3DS file. 
785//
786// Below is a list of the order that you will find the chunks and all the know chunks.
787// If you go to www.wosit.org you can find a few documents on the 3DS file format.
788// You can also take a look at the 3DS Format.rtf that is included with this tutorial.
789//
790//
791//
792//      MAIN3DS  (0x4D4D)
793//     |
794//     +--EDIT3DS  (0x3D3D)
795//     |  |
796//     |  +--EDIT_MATERIAL (0xAFFF)
797//     |  |  |
798//     |  |  +--MAT_NAME01 (0xA000) (See mli Doc)
799//     |  |
800//     |  +--EDIT_CONFIG1  (0x0100)
801//     |  +--EDIT_CONFIG2  (0x3E3D)
802//     |  +--EDIT_VIEW_P1  (0x7012)
803//     |  |  |
804//     |  |  +--TOP            (0x0001)
805//     |  |  +--BOTTOM         (0x0002)
806//     |  |  +--LEFT           (0x0003)
807//     |  |  +--RIGHT          (0x0004)
808//     |  |  +--FRONT          (0x0005)
809//     |  |  +--BACK           (0x0006)
810//     |  |  +--USER           (0x0007)
811//     |  |  +--CAMERA         (0xFFFF)
812//     |  |  +--LIGHT          (0x0009)
813//     |  |  +--DISABLED       (0x0010) 
814//     |  |  +--BOGUS          (0x0011)
815//     |  |
816//     |  +--EDIT_VIEW_P2  (0x7011)
817//     |  |  |
818//     |  |  +--TOP            (0x0001)
819//     |  |  +--BOTTOM         (0x0002)
820//     |  |  +--LEFT           (0x0003)
821//     |  |  +--RIGHT          (0x0004)
822//     |  |  +--FRONT          (0x0005)
823//     |  |  +--BACK           (0x0006)
824//     |  |  +--USER           (0x0007)
825//     |  |  +--CAMERA         (0xFFFF)
826//     |  |  +--LIGHT          (0x0009)
827//     |  |  +--DISABLED       (0x0010) 
828//     |  |  +--BOGUS          (0x0011)
829//     |  |
830//     |  +--EDIT_VIEW_P3  (0x7020)
831//     |  +--EDIT_VIEW1    (0x7001)
832//     |  +--EDIT_BACKGR   (0x1200)
833//     |  +--EDIT_AMBIENT  (0x2100)
834//     |  +--EDIT_OBJECT   (0x4000)
835//     |  |  |
836//     |  |  +--OBJ_TRIMESH   (0x4100)     
837//     |  |  |  |
838//     |  |  |  +--TRI_VERTEXL          (0x4110)
839//     |  |  |  +--TRI_VERTEXOPTIONS    (0x4111)
840//     |  |  |  +--TRI_MAPPINGCOORS     (0x4140)
841//     |  |  |  +--TRI_MAPPINGSTANDARD  (0x4170)
842//     |  |  |  +--TRI_FACEL1           (0x4120)
843//     |  |  |  |  |
844//     |  |  |  |  +--TRI_SMOOTH            (0x4150)   
845//     |  |  |  |  +--TRI_MATERIAL          (0x4130)
846//     |  |  |  |
847//     |  |  |  +--TRI_LOCAL            (0x4160)
848//     |  |  |  +--TRI_VISIBLE          (0x4165)
849//     |  |  |
850//     |  |  +--OBJ_LIGHT    (0x4600)
851//     |  |  |  |
852//     |  |  |  +--LIT_OFF              (0x4620)
853//     |  |  |  +--LIT_SPOT             (0x4610)
854//     |  |  |  +--LIT_UNKNWN01         (0x465A)
855//     |  |  |
856//     |  |  +--OBJ_CAMERA   (0x4700)
857//     |  |  |  |
858//     |  |  |  +--CAM_UNKNWN01         (0x4710)
859//     |  |  |  +--CAM_UNKNWN02         (0x4720) 
860//     |  |  |
861//     |  |  +--OBJ_UNKNWN01 (0x4710)
862//     |  |  +--OBJ_UNKNWN02 (0x4720)
863//     |  |
864//     |  +--EDIT_UNKNW01  (0x1100)
865//     |  +--EDIT_UNKNW02  (0x1201)
866//     |  +--EDIT_UNKNW03  (0x1300)
867//     |  +--EDIT_UNKNW04  (0x1400)
868//     |  +--EDIT_UNKNW05  (0x1420)
869//     |  +--EDIT_UNKNW06  (0x1450)
870//     |  +--EDIT_UNKNW07  (0x1500)
871//     |  +--EDIT_UNKNW08  (0x2200)
872//     |  +--EDIT_UNKNW09  (0x2201)
873//     |  +--EDIT_UNKNW10  (0x2210)
874//     |  +--EDIT_UNKNW11  (0x2300)
875//     |  +--EDIT_UNKNW12  (0x2302)
876//     |  +--EDIT_UNKNW13  (0x2000)
877//     |  +--EDIT_UNKNW14  (0xAFFF)
878//     |
879//     +--KEYF3DS (0xB000)
880//        |
881//        +--KEYF_UNKNWN01 (0xB00A)
882//        +--............. (0x7001) ( viewport, same as editor )
883//        +--KEYF_FRAMES   (0xB008)
884//        +--KEYF_UNKNWN02 (0xB009)
885//        +--KEYF_OBJDES   (0xB002)
886//           |
887//           +--KEYF_OBJHIERARCH  (0xB010)
888//           +--KEYF_OBJDUMMYNAME (0xB011)
889//           +--KEYF_OBJUNKNWN01  (0xB013)
890//           +--KEYF_OBJUNKNWN02  (0xB014)
891//           +--KEYF_OBJUNKNWN03  (0xB015) 
892//           +--KEYF_OBJPIVOT     (0xB020) 
893//           +--KEYF_OBJUNKNWN04  (0xB021) 
894//           +--KEYF_OBJUNKNWN05  (0xB022) 
895//
896// Once you know how to read chunks, all you have to know is the ID you are looking for
897// and what data is stored after that ID.  You need to get the file format for that.
898// I can give it to you if you want, or you can go to www.wosit.org for several versions.
899// Because this is a proprietary format, it isn't a official document.
900//
901// I know there was a LOT of information blown over, but it is too much knowledge for
902// one tutorial.  In the animation tutorial that I eventually will get to, some of
903// the things explained here will be explained in more detail.  I do not claim that
904// this is the best .3DS tutorial, or even a GOOD one :)  But it is a good start, and there
905// isn't much code out there that is simple when it comes to reading .3DS files.
906// So far, this is the best I have seen.  That is why I made it :)
907//
908// I would like to thank www.wosit.org and Terry Caton (tcaton@umr.edu) for his help on this.
909//
910// Let me know if this helps you out!
911//
912//
913// Ben Humphrey (DigiBen)
914// Game Programmer
915// DigiBen@GameTutorials.com
916// Co-Web Host of www.GameTutorials.com
917//
918//
Note: See TracBrowser for help on using the repository browser.