Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2028 was 1901, checked in by patrick, 21 years ago

ported showroom to linux - haleluja

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