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