Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/orxonox/branches/john/src/3ds.cc @ 2035

Last change on this file since 2035 was 2035, checked in by john, 20 years ago

Diese Dateien werden für das Importieren von 3ds Modellen gebraucht.
3ds importiert die Daten.
3dUnit enthält die Daten.
3dsStructs definiert einige 3d Variablen

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