📄 3ds.cpp
字号:
//***********************************************************************//
// //
// - "Talk to me like I'm a 3 year old!" Programming Lessons - //
// //
// $Author: DigiBen digiben@gametutorials.com //
// //
// $Program: 3DS Loader //
// //
// $Description: Demonstrates how to load a .3ds file format //
// //
// $Date: 10/6/01 //
// //
//***********************************************************************//
#include "main.h"
#include "3ds.h"
#include <assert.h>
// Global
int gBuffer[50000] = {0}; // This is used to read past unwanted data
// This file handles all of the code needed to load a .3DS file.
// Basically, how it works is, you load a chunk, then you check
// the chunk ID. Depending on the chunk ID, you load the information
// that is stored in that chunk. If you do not want to read that information,
// you read past it. You know how many bytes to read past the chunk because
// every chunk stores the length in bytes of that chunk.
///////////////////////////////// CLOAD3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This constructor initializes the tChunk data
/////
///////////////////////////////// CLOAD3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
CLoad3DS::CLoad3DS()
{
m_FilePointer = NULL;
}
///////////////////////////////// IMPORT 3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This is called by the client to open the .3ds file, read it, then clean up
/////
///////////////////////////////// IMPORT 3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool CLoad3DS::Import3DS(t3DModel *pModel, char *strFileName)
{
char strMessage[255] = {0};
tChunk currentChunk = {0};
// Open the 3DS file
m_FilePointer = fopen(strFileName, "rb");
// Make sure we have a valid file pointer (we found the file)
if(!m_FilePointer)
{
sprintf(strMessage, "Unable to find the file: %s!", strFileName);
MessageBox(NULL, strMessage, "Error", MB_OK);
return false;
}
// Once we have the file open, we need to read the very first data chunk
// to see if it's a 3DS file. That way we don't read an invalid file.
// If it is a 3DS file, then the first chunk ID will be equal to PRIMARY (some hex num)
// Read the first chuck of the file to see if it's a 3DS file
ReadChunk(¤tChunk);
// Make sure this is a 3DS file
if (currentChunk.ID != PRIMARY)
{
sprintf(strMessage, "Unable to load PRIMARY chuck from file: %s!", strFileName);
MessageBox(NULL, strMessage, "Error", MB_OK);
return false;
}
// Now we actually start reading in the data. ProcessNextChunk() is recursive
// Begin loading objects, by calling this recursive function
ProcessNextChunk(pModel, ¤tChunk);
// After we have read the whole 3DS file, we want to calculate our own vertex normals.
ComputeNormals(pModel);
// Clean up after everything
CleanUp();
return true;
}
///////////////////////////////// CLEAN UP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function cleans up our allocated memory and closes the file
/////
///////////////////////////////// CLEAN UP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::CleanUp()
{
if (m_FilePointer) {
fclose(m_FilePointer); // Close the current file pointer
m_FilePointer = NULL;
}
}
///////////////////////////////// PROCESS NEXT CHUNK\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function reads the main sections of the .3DS file, then dives deeper with recursion
/////
///////////////////////////////// PROCESS NEXT CHUNK\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ProcessNextChunk(t3DModel *pModel, tChunk *pPreviousChunk)
{
t3DObject newObject = {0}; // This is used to add to our object list
tMaterialInfo newTexture = {0}; // This is used to add to our material list
tChunk currentChunk = {0}; // The current chunk to load
tChunk tempChunk = {0}; // A temp chunk for holding data
// Below we check our chunk ID each time we read a new chunk. Then, if
// we want to extract the information from that chunk, we do so.
// If we don't want a chunk, we just read past it.
// Continue to read the sub chunks until we have reached the length.
// After we read ANYTHING we add the bytes read to the chunk and then check
// check against the length.
while (pPreviousChunk->bytesRead < pPreviousChunk->length)
{
// Read next Chunk
ReadChunk(¤tChunk);
// Check the chunk ID
switch (currentChunk.ID)
{
case VERSION: // This holds the version of the file
// If the file was made in 3D Studio Max, this chunk has an int that
// holds the file version. Since there might be new additions to the 3DS file
// format in 4.0, we give a warning to that problem.
// However, if the file wasn't made by 3D Studio Max, we don't 100% what the
// version length will be so we'll simply ignore the value
// Read the file version and add the bytes read to our bytesRead variable
currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
// If the file version is over 3, give a warning that there could be a problem
if ((currentChunk.length - currentChunk.bytesRead == 4) && (gBuffer[0] > 0x03)) {
MessageBox(NULL, "This 3DS file is over version 3 so it may load incorrectly", "Warning", MB_OK);
}
break;
case OBJECTINFO: // This holds the version of the mesh
{
// This chunk holds the version of the mesh. It is also the head of the MATERIAL
// and OBJECT chunks. From here on we start reading in the material and object info.
// Read the next chunk
ReadChunk(&tempChunk);
// Get the version of the mesh
tempChunk.bytesRead += fread(gBuffer, 1, tempChunk.length - tempChunk.bytesRead, m_FilePointer);
// Increase the bytesRead by the bytes read from the last chunk
currentChunk.bytesRead += tempChunk.bytesRead;
// Go to the next chunk, which is the object has a texture, it should be MATERIAL, then OBJECT.
ProcessNextChunk(pModel, ¤tChunk);
break;
}
case MATERIAL: // This holds the material information
// This chunk is the header for the material info chunks
// Increase the number of materials
pModel->numOfMaterials++;
// Add a empty texture structure to our texture list.
// If you are unfamiliar with STL's "vector" class, all push_back()
// does is add a new node onto the list. I used the vector class
// so I didn't need to write my own link list functions.
pModel->pMaterials.push_back(newTexture);
// Proceed to the material loading function
ProcessNextMaterialChunk(pModel, ¤tChunk);
break;
case OBJECT: // This holds the name of the object being read
// This chunk is the header for the object info chunks. It also
// holds the name of the object.
// Increase the object count
pModel->numOfObjects++;
// Add a new tObject node to our list of objects (like a link list)
pModel->pObject.push_back(newObject);
// Initialize the object and all it's data members
memset(&(pModel->pObject[pModel->numOfObjects - 1]), 0, sizeof(t3DObject));
// Get the name of the object and store it, then add the read bytes to our byte counter.
currentChunk.bytesRead += GetString(pModel->pObject[pModel->numOfObjects - 1].strName);
// Now proceed to read in the rest of the object information
ProcessNextObjectChunk(pModel, &(pModel->pObject[pModel->numOfObjects - 1]), ¤tChunk);
break;
case EDITKEYFRAME:
// Because I wanted to make this a SIMPLE tutorial as possible, I did not include
// the key frame information. This chunk is the header for all the animation info.
// In a later tutorial this will be the subject and explained thoroughly.
//ProcessNextKeyFrameChunk(pModel, currentChunk);
// Read past this chunk and add the bytes read to the byte counter
currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
break;
default:
// If we didn't care about a chunk, then we get here. We still need
// to read past the unknown or ignored chunk and add the bytes read to the byte counter.
currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
break;
}
// Add the bytes read from the last chunk to the previous chunk passed in.
pPreviousChunk->bytesRead += currentChunk.bytesRead;
}
}
///////////////////////////////// PROCESS NEXT OBJECT CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function handles all the information about the objects in the file
/////
///////////////////////////////// PROCESS NEXT OBJECT CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ProcessNextObjectChunk(t3DModel *pModel, t3DObject *pObject, tChunk *pPreviousChunk)
{
// The current chunk to work with
tChunk currentChunk = {0};
// Continue to read these chunks until we read the end of this sub chunk
while (pPreviousChunk->bytesRead < pPreviousChunk->length)
{
// Read the next chunk
ReadChunk(¤tChunk);
// Check which chunk we just read
switch (currentChunk.ID)
{
case OBJECT_MESH: // This lets us know that we are reading a new object
// We found a new object, so let's read in it's info using recursion
ProcessNextObjectChunk(pModel, pObject, ¤tChunk);
break;
case OBJECT_VERTICES: // This is the objects vertices
ReadVertices(pObject, ¤tChunk);
break;
case OBJECT_FACES: // This is the objects face information
ReadVertexIndices(pObject, ¤tChunk);
break;
case OBJECT_MATERIAL: // This holds the material name that the object has
// This chunk holds the name of the material that the object has assigned to it.
// This could either be just a color or a texture map. This chunk also holds
// the faces that the texture is assigned to (In the case that there is multiple
// textures assigned to one object, or it just has a texture on a part of the object.
// Since most of my game objects just have the texture around the whole object, and
// they aren't multitextured, I just want the material name.
// We now will read the name of the material assigned to this object
ReadObjectMaterial(pModel, pObject, ¤tChunk);
break;
case OBJECT_UV: // This holds the UV texture coordinates for the object
// This chunk holds all of the UV coordinates for our object. Let's read them in.
ReadUVCoordinates(pObject, ¤tChunk);
break;
default:
// Read past the ignored or unknown chunks
currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
break;
}
// Add the bytes read from the last chunk to the previous chunk passed in.
pPreviousChunk->bytesRead += currentChunk.bytesRead;
}
}
///////////////////////////////// PROCESS NEXT MATERIAL CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function handles all the information about the material (Texture)
/////
///////////////////////////////// PROCESS NEXT MATERIAL CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ProcessNextMaterialChunk(t3DModel *pModel, tChunk *pPreviousChunk)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -