libmoldeo (Moldeo 1.0 Core)  1.0
libmoldeo is the group of objects and functions that executes the basic operations of Moldeo 1.0 Platform.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
mo3ds.cpp
Go to the documentation of this file.
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
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