This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

 

  Another 3DS Loader/Viewer Class
  Submitted by



I know this is the 3rd 3DS file loader to be added to the Code Of The Day Collection but I figured I'd put in my two cents. This isn't the most efficient way to load a 3DS model but I find it a lot easier to read and debug. I have included my simple Texture class to support texturing of the models. I know it is crude and doesn't port well to non-Windows platforms so just replace it with your favorite texture class and have fun! Feel free to use this code in any project. I just ask that you let me know if you decide to use it. This code stands as is but I would like to know of any bugs so that I can address them in later releases. I have a mostly working Direct3D 8.0 port of this code (haven't finished the texture part) if anyone is interested.

Description from file:
// This is a simple class for loading and viewing
// 3D Studio model files (.3ds). It supports models
// with multiple objects. It also supports multiple
// textures per object. It does not support the animation
// for 3D Studio models b/c there are simply too many
// ways for an artist to animate a 3D Studio model and
// I didn't want to impose huge limitations on the artists.
// However, I have imposed a limitation on how the models are
// textured:
// 1) Every faces must be assigned a material
// 2) If you want the face to be textured assign the
//    texture to the Diffuse Color map
// 3) The texture must be supported by the GLTexture class
//    which only supports bitmap and targa right now
// 4) The texture must be located in the same directory as
//    the model

// Support for non-textured faces is done by reading the color
// from the material's diffuse color.

// Some models have problems loading even if you follow all of
// the restrictions I have stated and I don't know why. If you
// can import the 3D Studio file into Milkshape 3D 
// (http://www.swissquake.ch/chumbalum-soft) and then export it
// to a new 3D Studio file, it seems to fix many of the problems
// but there is a limit on the number of faces and vertices Milkshape 3D
// can read.

Usage:
Model_3DS m;

m.Load("model.3ds"); // Load the model m.Draw(); // Renders the model to the screen // If you want to show the model's normals m.shownormals = true;

// If the model is not going to be lit then set the lit // variable to false. It defaults to true. m.lit = false;

// You can disable the rendering of the model m.visible = false;

// You can move and rotate the model like this: m.rot.x = 90.0f; m.rot.y = 30.0f; m.rot.z = 0.0f;

m.pos.x = 10.0f; m.pos.y = 0.0f; m.pos.z = 0.0f;

// If you want to move or rotate individual objects m.Objects[0].rot.x = 90.0f; m.Objects[0].rot.y = 30.0f; m.Objects[0].rot.z = 0.0f;

m.Objects[0].pos.x = 10.0f; m.Objects[0].pos.y = 0.0f; m.Objects[0].pos.z = 0.0f;

Currently browsing [3ds-loadview.zip] (13,248 bytes) - [GLTexture.cpp] - (12,272 bytes)

//////////////////////////////////////////////////////////////////////
//
// OpenGL Texture Class
// by: Matthew Fairfax
//
// GLTexture.cpp: implementation of the GLTexture class.
// This class loads a texture file and prepares it
// to be used in OpenGL. It can open a bitmap or a
// targa file. The min filter is set to mipmap b/c
// they look better and the performance cost on
// modern video cards in negligible. I leave all of
// the texture management to the application. I have
// included the ability to load the texture from a
// Visual Studio resource. The bitmap's id must be
// be surrounded by quotation marks (i.e. "Texture.bmp").
// The targa files must be in a resource type of "TGA"
// (including the quotes). The targa's id must be
// surrounded by quotation marks (i.e. "Texture.tga").
//
// Usage:
// GLTexture tex;
// GLTexture tex1;
// GLTexture tex3;
//
// tex.Load("texture.bmp"); // Loads a bitmap
// tex.Use();				// Binds the bitmap for use
// 
// tex1.LoadFromResource("texture.tga"); // Loads a targa
// tex1.Use();				 // Binds the targa for use
//
// // You can also build a texture with a single color and use it
// tex3.BuildColorTexture(255, 0, 0);	// Builds a solid red texture
// tex3.Use();				 // Binds the targa for use
//
//////////////////////////////////////////////////////////////////////

#include "GLTexture.h"

#include <stdio.h> #include <string.h> #include <stdlib.h>

////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// GLTexture::GLTexture() {

}

GLTexture::~GLTexture() {

}

void GLTexture::Load(char *name) { // make the texture name all lower case texturename = strlwr(strdup(name));

// strip "'s if (strstr(texturename, "\"")) texturename = strtok(texturename, "\"");

// check the file extension to see what type of texture if(strstr(texturename, ".bmp")) LoadBMP(texturename); if(strstr(texturename, ".tga")) LoadTGA(texturename); }

void GLTexture::LoadFromResource(char *name) { // make the texture name all lower case texturename = strlwr(strdup(name));

// check the file extension to see what type of texture if(strstr(texturename, ".bmp")) LoadBMPResource(name); if(strstr(texturename, ".tga")) LoadTGAResource(name); }

void GLTexture::Use() { glEnable(GL_TEXTURE_2D); // Enable texture mapping glBindTexture(GL_TEXTURE_2D, texture[0]); // Bind the texture as the current one }

void GLTexture::LoadBMP(char *name) { // Create a place to store the texture AUX_RGBImageRec *TextureImage[1];

// Set the pointer to NULL memset(TextureImage,0,sizeof(void *)*1);

// Load the bitmap and assign our pointer to it TextureImage[0] = auxDIBImageLoad(name);

// Just in case we want to use the width and height later width = TextureImage[0]->sizeX; height = TextureImage[0]->sizeY;

// Generate the OpenGL texture id glGenTextures(1, &texture[0]);

// Bind this texture to its id glBindTexture(GL_TEXTURE_2D, texture[0]);

// Use mipmapping filter glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

// Generate the mipmaps gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);

// Cleanup if (TextureImage[0]) { if (TextureImage[0]->data) free(TextureImage[0]->data);

free(TextureImage[0]); } }

void GLTexture::LoadTGA(char *name) { GLubyte TGAheader[12] = {0,0,2,0,0,0,0,0,0,0,0,0};// Uncompressed TGA header GLubyte TGAcompare[12]; // Used to compare TGA header GLubyte header[6]; // First 6 useful bytes of the header GLuint bytesPerPixel; // Holds the number of bytes per pixel used GLuint imageSize; // Used to store the image size GLuint temp; // Temporary variable GLuint type = GL_RGBA; // Set the default type to RBGA (32 BPP) GLubyte *imageData; // Image data (up to 32 Bits) GLuint bpp; // Image color depth in bits per pixel. FILE *file = fopen(name, "rb"); // Open the TGA file // Load the file and perform checks if(file == NULL || // Does file exist? fread(TGAcompare,1,sizeof(TGAcompare),file) != sizeof(TGAcompare) || // Are there 12 bytes to read? memcmp(TGAheader,TGAcompare,sizeof(TGAheader)) != 0 || // Is it the right format? fread(header,1,sizeof(header),file) != sizeof(header)) // If so then read the next 6 header bytes { if (file == NULL) // If the file didn't exist then return return; else { fclose(file); // If something broke then close the file and return return; } }

// Determine the TGA width and height (highbyte*256+lowbyte) width = header[1] * 256 + header[0]; height = header[3] * 256 + header[2]; // Check to make sure the targa is valid and is 24 bit or 32 bit if(width <=0 || // Is the width less than or equal to zero height <=0 || // Is the height less than or equal to zero (header[4] != 24 && header[4] != 32)) // Is it 24 or 32 bit? { fclose(file); // If anything didn't check out then close the file and return return; }

bpp = header[4]; // Grab the bits per pixel bytesPerPixel = bpp / 8; // Divide by 8 to get the bytes per pixel imageSize = width * height * bytesPerPixel; // Calculate the memory required for the data // Allocate the memory for the image data imageData = new GLubyte[imageSize];

// Make sure the data is allocated write and load it if(imageData == NULL || // Does the memory storage exist? fread(imageData, 1, imageSize, file) != imageSize) // Does the image size match the memory reserved? { if(imageData != NULL) // Was the image data loaded free(imageData); // If so, then release the image data fclose(file); // Close the file return; }

// Loop through the image data and swap the 1st and 3rd bytes (red and blue) for(GLuint i = 0; i < int(imageSize); i += bytesPerPixel) { temp = imageData[i]; imageData[i] = imageData[i + 2]; imageData[i + 2] = temp; }

// We are done with the file so close it fclose(file);

// Set the type if (bpp == 24) type = GL_RGB; // Generate the OpenGL texture id glGenTextures(1, &texture[0]);

// Bind this texture to its id glBindTexture(GL_TEXTURE_2D, texture[0]);

// Use mipmapping filter glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

// Generate the mipmaps gluBuild2DMipmaps(GL_TEXTURE_2D, type, width, height, type, GL_UNSIGNED_BYTE, imageData);

// Cleanup free(imageData); }

void GLTexture::LoadBMPResource(char *name) { // Find the bitmap in the bitmap resources HRSRC hrsrc = FindResource(0, name, RT_BITMAP);

// If you can't find it then return if (hrsrc==0) return;

// Load the bitmap HGLOBAL resource = LoadResource(0, hrsrc); // If you can't load it then return if (resource==0) return;

// Load it into the buffer void *buffer = LockResource(resource);

// Cast it into a bitmap BITMAP *bmp = (BITMAP*)buffer;

// Get the height and width for future use width = bmp->bmWidth; height = bmp->bmHeight;

// Reverse the blue colot bit and the red color bit unsigned char *ptr = (unsigned char *)buffer+sizeof(BITMAPINFO)+2; unsigned char temp;

for (int i = 0; i < width*height; i++) { temp = ptr[i*3]; ptr[i*3] = ptr[i*3+2]; ptr[i*3+2] = temp; }

// Generate the OpenGL texture id glGenTextures(1, &texture[0]);

// Bind this texture to its id glBindTexture(GL_TEXTURE_2D, texture[0]);

// Use mipmapping filter glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

// Generate the mipmaps gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)buffer+sizeof(BITMAPINFO)+2); //gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, bmp->bmBits); // Cleanup free(buffer); free(bmp); }

void GLTexture::LoadTGAResource(char *name) { // struct to cast the resource into struct TGAstruct { GLubyte TGAcompare[12]; // Used to compare TGA header GLubyte header[6]; // First 6 useful bytes of the header };

GLubyte TGAheader[12] = {0,0,2,0,0,0,0,0,0,0,0,0};// Uncompressed TGA header GLuint bytesPerPixel; // Holds the number of bytes per pixel used GLuint imageSize; // Used to store the image size GLuint temp; // Temporary variable GLuint type = GL_RGBA; // Set the default type to RBGA (32 BPP) GLubyte *imageData; // Image data (up to 32 Bits) GLuint bpp; // Image color depth in bits per pixel. // Find the targa in the "TGA" resources HRSRC hrsrc = FindResource(0, name, "TGA");

// If you can't find it then return if (hrsrc==0) return;

// Load the targa HGLOBAL resource = LoadResource(0, hrsrc);

// If you can't load it then return if (resource==0) return;

// Load it into the buffer void *buffer = LockResource(resource);

// Cast it into the targa struct TGAstruct *top = (TGAstruct*)buffer;

// Make sure it checks out against our comparison header if (memcmp(TGAheader,top,sizeof(TGAheader)) != 0) return;

// Determine the TGA width and height (highbyte*256+lowbyte) width = top->header[1] * 256 + top->header[0]; height = top->header[3] * 256 + top->header[2]; // Check to make sure the targa is valid and is 24 bit or 32 bit if(width <=0 || // Is the width less than or equal to zero height <=0 || // Is the height less than or equal to zero (top->header[4] != 24 && top->header[4] != 32)) // Is it 24 or 32 bit? { // If anything didn't check out then close the file and return return; }

bpp = top->header[4]; // Grab the bits per pixel bytesPerPixel = bpp / 8; // Divide by 8 to get the bytes per pixel imageSize = width * height * bytesPerPixel; // Calculate the memory required for the data // Allocate the memory for the image data imageData = new GLubyte[imageSize];

// Load the data in memcpy(imageData, (GLubyte*)buffer+18, imageSize);

// Loop through the image data and swap the 1st and 3rd bytes (red and blue) for(GLuint i = 0; i < int(imageSize); i += bytesPerPixel) { temp = imageData[i]; imageData[i] = imageData[i + 2]; imageData[i + 2] = temp; }

// Set the type if (bpp == 24) type = GL_RGB; // Generate the OpenGL texture id glGenTextures(1, &texture[0]);

// Bind this texture to its id glBindTexture(GL_TEXTURE_2D, texture[0]);

// Use mipmapping filter glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

// Generate the mipmaps gluBuild2DMipmaps(GL_TEXTURE_2D, type, width, height, type, GL_UNSIGNED_BYTE, imageData);

// Cleanup free(imageData); free(buffer); free(top); }

void GLTexture::BuildColorTexture(unsigned char r, unsigned char g, unsigned char b) { unsigned char data[12]; // a 2x2 texture at 24 bits // Store the data for(int i = 0; i < 12; i += 3) { data[i] = r; data[i+1] = g; data[i+2] = b; }

// Generate the OpenGL texture id glGenTextures(1, &texture[0]);

// Bind this texture to its id glBindTexture(GL_TEXTURE_2D, texture[0]);

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

// Use mipmapping filter glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

// Generate the texture gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 2, 2, GL_RGB, GL_UNSIGNED_BYTE, data); }

Currently browsing [3ds-loadview.zip] (13,248 bytes) - [GLTexture.h] - (2,548 bytes)

//////////////////////////////////////////////////////////////////////
//
// OpenGL Texture Class
// by: Matthew Fairfax
//
// GLTexture.h: interface for the GLTexture class.
// This class loads a texture file and prepares it
// to be used in OpenGL. It can open a bitmap or a
// targa file. The min filter is set to mipmap b/c
// they look better and the performance cost on
// modern video cards in negligible. I leave all of
// the texture management to the application. I have
// included the ability to load the texture from a
// Visual Studio resource. The bitmap's id must be
// be surrounded by quotation marks (i.e. "Texture.bmp").
// The targa files must be in a resource type of "TGA"
// (including the quotes). The targa's id must be
// surrounded by quotation marks (i.e. "Texture.tga").
//
// Usage:
// GLTexture tex;
// GLTexture tex1;
// GLTexture tex3;
//
// tex.Load("texture.bmp"); // Loads a bitmap
// tex.Use();				// Binds the bitmap for use
// 
// tex1.LoadFromResource("texture.tga"); // Loads a targa
// tex1.Use();				 // Binds the targa for use
//
// // You can also build a texture with a single color and use it
// tex3.BuildColorTexture(255, 0, 0);	// Builds a solid red texture
// tex3.Use();				 // Binds the targa for use
//
//////////////////////////////////////////////////////////////////////

#ifndef GLTEXTURE_H
#define GLTEXTURE_H

#include <windows.h> // Header File For Windows #include <gl\gl.h> // Header File For The OpenGL32 Library #include <gl\glu.h> // Header File For The GLu32 Library #include <gl\glaux.h> // Header File For The Glaux Library class GLTexture { public: char *texturename; // The textures name unsigned int texture[1]; // OpenGL's number for the texture int width; // Texture's width int height; // Texture's height void Use(); // Binds the texture for use void BuildColorTexture(unsigned char r, unsigned char g, unsigned char b); // Sometimes we want a texture of uniform color void LoadTGAResource(char *name); // Load a targa from the resources void LoadBMPResource(char *name); // Load a bitmap from the resources void LoadFromResource(char *name); // Load the texture from a resource void LoadTGA(char *name); // Loads a targa file void LoadBMP(char *name); // Loads a bitmap file void Load(char *name); // Load the texture GLTexture(); // Constructor virtual ~GLTexture(); // Destructor };

#endif GLTEXTURE_H

Currently browsing [3ds-loadview.zip] (13,248 bytes) - [Model_3DS.cpp] - (31,867 bytes)

//////////////////////////////////////////////////////////////////////
//
// 3D Studio Model Class
// by: Matthew Fairfax
//
// Model_3DS.cpp: implementation of the Model_3DS class.
// This is a simple class for loading and viewing
// 3D Studio model files (.3ds). It supports models
// with multiple objects. It also supports multiple
// textures per object. It does not support the animation
// for 3D Studio models b/c there are simply too many
// ways for an artist to animate a 3D Studio model and
// I didn't want to impose huge limitations on the artists.
// However, I have imposed a limitation on how the models are
// textured:
// 1) Every faces must be assigned a material
// 2) If you want the face to be textured assign the
//    texture to the Diffuse Color map
// 3) The texture must be supported by the GLTexture class
//    which only supports bitmap and targa right now
// 4) The texture must be located in the same directory as
//    the model
//
// Support for non-textured faces is done by reading the color
// from the material's diffuse color.
//
// Some models have problems loading even if you follow all of
// the restrictions I have stated and I don't know why. If you
// can import the 3D Studio file into Milkshape 3D 
// (http://www.swissquake.ch/chumbalum-soft) and then export it
// to a new 3D Studio file. This seems to fix many of the problems
// but there is a limit on the number of faces and vertices Milkshape 3D
// can read.
//
// Usage:
// Model_3DS m;
//
// m.Load("model.3ds"); // Load the model
// m.Draw();			// Renders the model to the screen
//
// // If you want to show the model's normals
// m.shownormals = true;
//
// // If the model is not going to be lit then set the lit
// // variable to false. It defaults to true.
// m.lit = false;
//
// // You can disable the rendering of the model
// m.visible = false;
// 
// // You can move and rotate the model like this:
// m.rot.x = 90.0f;
// m.rot.y = 30.0f;
// m.rot.z = 0.0f;
//
// m.pos.x = 10.0f;
// m.pos.y = 0.0f;
// m.pos.z = 0.0f;
//
// // If you want to move or rotate individual objects
// m.Objects[0].rot.x = 90.0f;
// m.Objects[0].rot.y = 30.0f;
// m.Objects[0].rot.z = 0.0f;
//
// m.Objects[0].pos.x = 10.0f;
// m.Objects[0].pos.y = 0.0f;
// m.Objects[0].pos.z = 0.0f;
//
//////////////////////////////////////////////////////////////////////

// This is used to generate a warning from the compiler
#define _QUOTE(x) # x
#define QUOTE(x) _QUOTE(x)
#define __FILE__LINE__ __FILE__ "(" QUOTE(__LINE__) ") : "
#define warn( x )  message( __FILE__LINE__ #x "\n" ) 

// You need to uncomment this if you are using MFC #pragma warn( You need to uncomment this if you are using MFC ) //#include "stdafx.h" #include "Model_3DS.h"

#include <math.h> // Header file for the math library #include <gl\gl.h> // Header file for the OpenGL32 library // The chunk's id numbers #define MAIN3DS 0x4D4D #define MAIN_VERS 0x0002 #define EDIT3DS 0x3D3D #define MESH_VERS 0x3D3E #define OBJECT 0x4000 #define TRIG_MESH 0x4100 #define VERT_LIST 0x4110 #define FACE_DESC 0x4120 #define FACE_MAT 0x4130 #define TEX_VERTS 0x4140 #define SMOOTH_GROUP 0x4150 #define LOCAL_COORDS 0x4160 #define MATERIAL 0xAFFF #define MAT_NAME 0xA000 #define MAT_AMBIENT 0xA010 #define MAT_DIFFUSE 0xA020 #define MAT_SPECULAR 0xA030 #define SHINY_PERC 0xA040 #define SHINY_STR_PERC 0xA041 #define TRANS_PERC 0xA050 #define TRANS_FOFF_PERC 0xA052 #define REF_BLUR_PERC 0xA053 #define RENDER_TYPE 0xA100 #define SELF_ILLUM 0xA084 #define MAT_SELF_ILPCT 0xA08A #define WIRE_THICKNESS 0xA087 #define MAT_TEXMAP 0xA200 #define MAT_MAPNAME 0xA300 #define ONE_UNIT 0x0100 #define KEYF3DS 0xB000 #define FRAMES 0xB008 #define MESH_INFO 0xB002 #define HIER_POS 0xB030 #define HIER_FATHER 0xB010 #define PIVOT_PT 0xB013 #define TRACK00 0xB020 #define TRACK01 0xB021 #define TRACK02 0xB022 #define COLOR_RGB 0x0010 #define COLOR_TRU 0x0011 #define COLOR_TRUG 0x0012 #define COLOR_RGBG 0x0013 #define PERC_INT 0x0030 #define PERC_FLOAT 0x0031

////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// Model_3DS::Model_3DS() { // Initialization // Don't show the normals by default shownormals = false;

// The model is lit by default lit = true;

// The model is visible by default visible = true;

// Set up the default position pos.x = 0.0f; pos.y = 0.0f; pos.z = 0.0f; // Set up the default rotation rot.x = 0.0f; rot.y = 0.0f; rot.z = 0.0f;

// Set up the path path = new char[80]; sprintf(path, "");

// Zero out our counters for MFC numObjects = 0; numMaterials = 0;

// Set the scale to one scale = 1.0f; }

Model_3DS::~Model_3DS() {

}

void Model_3DS::Load(char *name) { // holds the main chunk header ChunkHeader main;

// strip "'s if (strstr(name, "\"")) name = strtok(name, "\"");

// Find the path if (strstr(name, "/") || strstr(name, "\\")) { // Holds the name of the model minus the path char *temp;

// Find the name without the path if (strstr(name, "/")) temp = strrchr(name, '/'); else temp = strrchr(name, '\\');

// Allocate space for the path path = new char[strlen(name)-strlen(temp)+1];

// Get a pointer to the end of the path and name char *src = name + strlen(name) - 1;

// Back up until a \ or the start while (src != path && !((*(src-1)) == '\\' || (*(src-1)) == '/')) src--;

// Copy the path into path memcpy (path, name, src-name); path[src-name] = 0; }

// Load the file bin3ds = fopen(name,"rb");

// Make sure we are at the beginning fseek(bin3ds, 0, SEEK_SET);

// Load the Main Chunk's header fread(&main.id,sizeof(main.id),1,bin3ds); fread(&main.len,sizeof(main.len),1,bin3ds);

// Start Processing MainChunkProcessor(main.len, ftell(bin3ds));

// Don't need the file anymore so close it fclose(bin3ds);

// Calculate the vertex normals CalculateNormals();

// For future reference modelname = name;

// Find the total number of faces and vertices totalFaces = 0; totalVerts = 0;

for (int i = 0; i < numObjects; i ++) { totalFaces += Objects[i].numFaces/3; totalVerts += Objects[i].numVerts; }

// If the object doesn't have any texcoords generate some for (int k = 0; k < numObjects; k++) { if (Objects[k].numTexCoords == 0) { // Set the number of texture coords Objects[k].numTexCoords = Objects[k].numVerts;

// Allocate an array to hold the texture coordinates Objects[k].TexCoords = new GLfloat[Objects[k].numTexCoords * 2];

// Make some texture coords for (int m = 0; m < Objects[k].numTexCoords; m++) { Objects[k].TexCoords[2*m] = Objects[k].Vertexes[3*m]; Objects[k].TexCoords[2*m+1] = Objects[k].Vertexes[3*m+1]; } } }

// Let's build simple colored textures for the materials w/o a texture for (int j = 0; j < numMaterials; j++) { if (Materials[j].textured == false) { unsigned char r = Materials[j].color.r; unsigned char g = Materials[j].color.g; unsigned char b = Materials[j].color.b; Materials[j].tex.BuildColorTexture(r, g, b); Materials[j].textured = true; } } }

void Model_3DS::Draw() { if (visible) { glPushMatrix();

// Move the model glTranslatef(pos.x, pos.y, pos.z);

// Rotate the model glRotatef(rot.x, 1.0f, 0.0f, 0.0f); glRotatef(rot.y, 0.0f, 1.0f, 0.0f); glRotatef(rot.z, 0.0f, 0.0f, 1.0f);

glScalef(scale, scale, scale);

// Loop through the objects for (int i = 0; i < numObjects; i++) { // Enable texture coordiantes, normals, and vertices arrays if (Objects[i].textured) glEnableClientState(GL_TEXTURE_COORD_ARRAY); if (lit) glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);

// Point them to the objects arrays if (Objects[i].textured) glTexCoordPointer(2, GL_FLOAT, 0, Objects[i].TexCoords); if (lit) glNormalPointer(GL_FLOAT, 0, Objects[i].Normals); glVertexPointer(3, GL_FLOAT, 0, Objects[i].Vertexes);

// Loop through the faces as sorted by material and draw them for (int j = 0; j < Objects[i].numMatFaces; j ++) { // Use the material's texture Materials[Objects[i].MatFaces[j].MatIndex].tex.Use();

glPushMatrix();

// Move the model glTranslatef(Objects[i].pos.x, Objects[i].pos.y, Objects[i].pos.z);

// Rotate the model //glRotatef(Objects[i].rot.x, 1.0f, 0.0f, 0.0f); //glRotatef(Objects[i].rot.y, 0.0f, 1.0f, 0.0f); //glRotatef(Objects[i].rot.z, 0.0f, 0.0f, 1.0f); glRotatef(Objects[i].rot.z, 0.0f, 0.0f, 1.0f); glRotatef(Objects[i].rot.y, 0.0f, 1.0f, 0.0f); glRotatef(Objects[i].rot.x, 1.0f, 0.0f, 0.0f);

// Draw the faces using an index to the vertex array glDrawElements(GL_TRIANGLES, Objects[i].MatFaces[j].numSubFaces, GL_UNSIGNED_SHORT, Objects[i].MatFaces[j].subFaces);

glPopMatrix(); }

// Show the normals? if (shownormals) { // Loop through the vertices and normals and draw the normal for (int k = 0; k < Objects[i].numVerts * 3; k += 3) { // Disable texturing glDisable(GL_TEXTURE_2D); // Disbale lighting if the model is lit if (lit) glDisable(GL_LIGHTING); // Draw the normals blue glColor3f(0.0f, 0.0f, 1.0f);

// Draw a line between the vertex and the end of the normal glBegin(GL_LINES); glVertex3f(Objects[i].Vertexes[k], Objects[i].Vertexes[k+1], Objects[i].Vertexes[k+2]); glVertex3f(Objects[i].Vertexes[k]+Objects[i].Normals[k], Objects[i].Vertexes[k+1]+Objects[i].Normals[k+1], Objects[i].Vertexes[k+2]+Objects[i].Normals[k+2]); glEnd();

// Reset the color to white glColor3f(1.0f, 1.0f, 1.0f); // If the model is lit then renable lighting if (lit) glEnable(GL_LIGHTING); } } }

glPopMatrix(); } }

void Model_3DS::CalculateNormals() { // Let's build some normals for (int i = 0; i < numObjects; i++) { for (int g = 0; g < Objects[i].numVerts; g++) { // Reduce each vert's normal to unit float length; Vector unit;

unit.x = Objects[i].Normals[g*3]; unit.y = Objects[i].Normals[g*3+1]; unit.z = Objects[i].Normals[g*3+2];

length = (float)sqrt((unit.x*unit.x) + (unit.y*unit.y) + (unit.z*unit.z));

if (length == 0.0f) length = 1.0f;

unit.x /= length; unit.y /= length; unit.z /= length;

Objects[i].Normals[g*3] = unit.x; Objects[i].Normals[g*3+1] = unit.y; Objects[i].Normals[g*3+2] = unit.z; } } }

void Model_3DS::MainChunkProcessor(long length, long findex) { ChunkHeader h;

// move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

while (ftell(bin3ds) < (findex + length - 6)) { fread(&h.id,sizeof(h.id),1,bin3ds); fread(&h.len,sizeof(h.len),1,bin3ds);

switch (h.id) { // This is the mesh information like vertices, faces, and materials case EDIT3DS : EditChunkProcessor(h.len, ftell(bin3ds)); break; // I left this in case anyone gets very ambitious case KEYF3DS : //KeyFrameChunkProcessor(h.len, ftell(bin3ds)); break; default : break; }

fseek(bin3ds, (h.len - 6), SEEK_CUR); }

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::EditChunkProcessor(long length, long findex) { ChunkHeader h;

// move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

// First count the number of Objects and Materials while (ftell(bin3ds) < (findex + length - 6)) { fread(&h.id,sizeof(h.id),1,bin3ds); fread(&h.len,sizeof(h.len),1,bin3ds);

switch (h.id) { case OBJECT : numObjects++; break; case MATERIAL : numMaterials++; break; default : break; }

fseek(bin3ds, (h.len - 6), SEEK_CUR); }

// Now load the materials if (numMaterials > 0) { Materials = new Material[numMaterials];

// Material is set to untextured until we find otherwise for (int d = 0; d < numMaterials; d++) Materials[d].textured = false;

fseek(bin3ds, findex, SEEK_SET);

int i = 0;

while (ftell(bin3ds) < (findex + length - 6)) { fread(&h.id,sizeof(h.id),1,bin3ds); fread(&h.len,sizeof(h.len),1,bin3ds);

switch (h.id) { case MATERIAL : MaterialChunkProcessor(h.len, ftell(bin3ds), i); i++; break; default : break; }

fseek(bin3ds, (h.len - 6), SEEK_CUR); } }

// Load the Objects (individual meshes in the whole model) if (numObjects > 0) { Objects = new Object[numObjects];

// Set the textured variable to false until we find a texture for (int k = 0; k < numObjects; k++) Objects[k].textured = false;

// Zero the objects position and rotation for (int m = 0; m < numObjects; m++) { Objects[m].pos.x = 0.0f; Objects[m].pos.y = 0.0f; Objects[m].pos.z = 0.0f;

Objects[m].rot.x = 0.0f; Objects[m].rot.y = 0.0f; Objects[m].rot.z = 0.0f; }

// Zero out the number of texture coords for (int n = 0; n < numObjects; n++) Objects[n].numTexCoords = 0;

fseek(bin3ds, findex, SEEK_SET);

int j = 0;

while (ftell(bin3ds) < (findex + length - 6)) { fread(&h.id,sizeof(h.id),1,bin3ds); fread(&h.len,sizeof(h.len),1,bin3ds);

switch (h.id) { case OBJECT : ObjectChunkProcessor(h.len, ftell(bin3ds), j); j++; break; default : break; }

fseek(bin3ds, (h.len - 6), SEEK_CUR); } }

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::MaterialChunkProcessor(long length, long findex, int matindex) { ChunkHeader h;

// move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

while (ftell(bin3ds) < (findex + length - 6)) { fread(&h.id,sizeof(h.id),1,bin3ds); fread(&h.len,sizeof(h.len),1,bin3ds);

switch (h.id) { case MAT_NAME : // Loads the material's names MaterialNameChunkProcessor(h.len, ftell(bin3ds), matindex); break; case MAT_AMBIENT : //ColorChunkProcessor(h.len, ftell(bin3ds)); break; case MAT_DIFFUSE : DiffuseColorChunkProcessor(h.len, ftell(bin3ds), matindex); break; case MAT_SPECULAR : //ColorChunkProcessor(h.len, ftell(bin3ds)); case MAT_TEXMAP : // Finds the names of the textures of the material and loads them TextureMapChunkProcessor(h.len, ftell(bin3ds), matindex); break; default : break; }

fseek(bin3ds, (h.len - 6), SEEK_CUR); }

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::MaterialNameChunkProcessor(long length, long findex, int matindex) { // move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

// Read the material's name for (int i = 0; i < 80; i++) { Materials[matindex].name[i] = fgetc(bin3ds); if (Materials[matindex].name[i] == 0) { Materials[matindex].name[i] = NULL; break; } }

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::DiffuseColorChunkProcessor(long length, long findex, int matindex) { ChunkHeader h;

// move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

while (ftell(bin3ds) < (findex + length - 6)) { fread(&h.id,sizeof(h.id),1,bin3ds); fread(&h.len,sizeof(h.len),1,bin3ds);

// Determine the format of the color and load it switch (h.id) { case COLOR_RGB : // A rgb float color chunk FloatColorChunkProcessor(h.len, ftell(bin3ds), matindex); break; case COLOR_TRU : // A rgb int color chunk IntColorChunkProcessor(h.len, ftell(bin3ds), matindex); break; case COLOR_RGBG : // A rgb gamma corrected float color chunk FloatColorChunkProcessor(h.len, ftell(bin3ds), matindex); break; case COLOR_TRUG : // A rgb gamma corrected int color chunk IntColorChunkProcessor(h.len, ftell(bin3ds), matindex); break; default : break; }

fseek(bin3ds, (h.len - 6), SEEK_CUR); }

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::FloatColorChunkProcessor(long length, long findex, int matindex) { float r; float g; float b;

// move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

fread(&r,sizeof(r),1,bin3ds); fread(&g,sizeof(g),1,bin3ds); fread(&b,sizeof(b),1,bin3ds);

Materials[matindex].color.r = (unsigned char)(r*255.0f); Materials[matindex].color.g = (unsigned char)(r*255.0f); Materials[matindex].color.b = (unsigned char)(r*255.0f); Materials[matindex].color.a = 255;

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::IntColorChunkProcessor(long length, long findex, int matindex) { unsigned char r; unsigned char g; unsigned char b;

// move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

fread(&r,sizeof(r),1,bin3ds); fread(&g,sizeof(g),1,bin3ds); fread(&b,sizeof(b),1,bin3ds);

Materials[matindex].color.r = r; Materials[matindex].color.g = g; Materials[matindex].color.b = b; Materials[matindex].color.a = 255;

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::TextureMapChunkProcessor(long length, long findex, int matindex) { ChunkHeader h;

// move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

while (ftell(bin3ds) < (findex + length - 6)) { fread(&h.id,sizeof(h.id),1,bin3ds); fread(&h.len,sizeof(h.len),1,bin3ds);

switch (h.id) { case MAT_MAPNAME: // Read the name of texture in the Diffuse Color map MapNameChunkProcessor(h.len, ftell(bin3ds), matindex); break; default : break; }

fseek(bin3ds, (h.len - 6), SEEK_CUR); }

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::MapNameChunkProcessor(long length, long findex, int matindex) { char name[80];

// move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

// Read the name of the texture for (int i = 0; i < 80; i++) { name[i] = fgetc(bin3ds); if (name[i] == 0) { name[i] = NULL; break; } }

// Load the name and indicate that the material has a texture char fullname[80]; sprintf(fullname, "%s%s", path, name); Materials[matindex].tex.Load(fullname); Materials[matindex].textured = true;

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::ObjectChunkProcessor(long length, long findex, int objindex) { ChunkHeader h;

// move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

// Load the object's name for (int i = 0; i < 80; i++) { Objects[objindex].name[i] = fgetc(bin3ds); if (Objects[objindex].name[i] == 0) { Objects[objindex].name[i] = NULL; break; } }

while (ftell(bin3ds) < (findex + length - 6)) { fread(&h.id,sizeof(h.id),1,bin3ds); fread(&h.len,sizeof(h.len),1,bin3ds);

switch (h.id) { case TRIG_MESH : // Process the triangles of the object TriangularMeshChunkProcessor(h.len, ftell(bin3ds), objindex); break; default : break; }

fseek(bin3ds, (h.len - 6), SEEK_CUR); }

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::TriangularMeshChunkProcessor(long length, long findex, int objindex) { ChunkHeader h;

// move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

while (ftell(bin3ds) < (findex + length - 6)) { fread(&h.id,sizeof(h.id),1,bin3ds); fread(&h.len,sizeof(h.len),1,bin3ds);

switch (h.id) { case VERT_LIST : // Load the vertices of the onject VertexListChunkProcessor(h.len, ftell(bin3ds), objindex); break; case LOCAL_COORDS : //LocalCoordinatesChunkProcessor(h.len, ftell(bin3ds)); break; case TEX_VERTS : // Load the texture coordinates for the vertices TexCoordsChunkProcessor(h.len, ftell(bin3ds), objindex); Objects[objindex].textured = true; break; default : break; }

fseek(bin3ds, (h.len - 6), SEEK_CUR); }

// After we have loaded the vertices we can load the faces fseek(bin3ds, findex, SEEK_SET);

while (ftell(bin3ds) < (findex + length - 6)) { fread(&h.id,sizeof(h.id),1,bin3ds); fread(&h.len,sizeof(h.len),1,bin3ds);

switch (h.id) { case FACE_DESC : // Load the faces of the object FacesDescriptionChunkProcessor(h.len, ftell(bin3ds), objindex); break; default : break; }

fseek(bin3ds, (h.len - 6), SEEK_CUR); }

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::VertexListChunkProcessor(long length, long findex, int objindex) { unsigned short numVerts;

// move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

// Read the number of vertices of the object fread(&numVerts,sizeof(numVerts),1,bin3ds);

// Allocate arrays for the vertices and normals Objects[objindex].Vertexes = new GLfloat[numVerts * 3]; Objects[objindex].Normals = new GLfloat[numVerts * 3];

// Assign the number of vertices for future use Objects[objindex].numVerts = numVerts;

// Zero out the normals array for (int j = 0; j < numVerts * 3; j++) Objects[objindex].Normals[j] = 0.0f;

// Read the vertices, switching the y and z coordinates and changing the sign of the z coordinate for (int i = 0; i < numVerts * 3; i+=3) { fread(&Objects[objindex].Vertexes[i],sizeof(GLfloat),1,bin3ds); fread(&Objects[objindex].Vertexes[i+2],sizeof(GLfloat),1,bin3ds); fread(&Objects[objindex].Vertexes[i+1],sizeof(GLfloat),1,bin3ds);

// Change the sign of the z coordinate Objects[objindex].Vertexes[i+2] = -Objects[objindex].Vertexes[i+2]; }

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::TexCoordsChunkProcessor(long length, long findex, int objindex) { // The number of texture coordinates unsigned short numCoords;

// move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

// Read the number of coordinates fread(&numCoords,sizeof(numCoords),1,bin3ds);

// Allocate an array to hold the texture coordinates Objects[objindex].TexCoords = new GLfloat[numCoords * 2];

// Set the number of texture coords Objects[objindex].numTexCoords = numCoords;

// Read teh texture coordiantes into the array for (int i = 0; i < numCoords * 2; i+=2) { fread(&Objects[objindex].TexCoords[i],sizeof(GLfloat),1,bin3ds); fread(&Objects[objindex].TexCoords[i+1],sizeof(GLfloat),1,bin3ds); }

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::FacesDescriptionChunkProcessor(long length, long findex, int objindex) { ChunkHeader h; unsigned short numFaces; // The number of faces in the object unsigned short vertA; // The first vertex of the face unsigned short vertB; // The second vertex of the face unsigned short vertC; // The third vertex of the face unsigned short flags; // The winding order flags long subs; // Holds our place in the file int numMatFaces = 0; // The number of different materials // move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

// Read the number of faces fread(&numFaces,sizeof(numFaces),1,bin3ds);

// Allocate an array to hold the faces Objects[objindex].Faces = new GLushort[numFaces * 3]; // Store the number of faces Objects[objindex].numFaces = numFaces * 3;

// Read the faces into the array for (int i = 0; i < numFaces * 3; i+=3) { // Read the vertices of the face fread(&vertA,sizeof(vertA),1,bin3ds); fread(&vertB,sizeof(vertB),1,bin3ds); fread(&vertC,sizeof(vertC),1,bin3ds); fread(&flags,sizeof(flags),1,bin3ds);

// Place them in the array Objects[objindex].Faces[i] = vertA; Objects[objindex].Faces[i+1] = vertB; Objects[objindex].Faces[i+2] = vertC;

// Calculate the face's normal Vector n; Vertex v1; Vertex v2; Vertex v3;

v1.x = Objects[objindex].Vertexes[vertA*3]; v1.y = Objects[objindex].Vertexes[vertA*3+1]; v1.z = Objects[objindex].Vertexes[vertA*3+2]; v2.x = Objects[objindex].Vertexes[vertB*3]; v2.y = Objects[objindex].Vertexes[vertB*3+1]; v2.z = Objects[objindex].Vertexes[vertB*3+2]; v3.x = Objects[objindex].Vertexes[vertC*3]; v3.y = Objects[objindex].Vertexes[vertC*3+1]; v3.z = Objects[objindex].Vertexes[vertC*3+2];

// calculate the normal float u[3], v[3];

// V2 - V3; u[0] = v2.x - v3.x; u[1] = v2.y - v3.y; u[2] = v2.z - v3.z;

// V2 - V1; v[0] = v2.x - v1.x; v[1] = v2.y - v1.y; v[2] = v2.z - v1.z;

n.x = (u[1]*v[2] - u[2]*v[1]); n.y = (u[2]*v[0] - u[0]*v[2]); n.z = (u[0]*v[1] - u[1]*v[0]);

// Add this normal to its verts' normals Objects[objindex].Normals[vertA*3] += n.x; Objects[objindex].Normals[vertA*3+1] += n.y; Objects[objindex].Normals[vertA*3+2] += n.z; Objects[objindex].Normals[vertB*3] += n.x; Objects[objindex].Normals[vertB*3+1] += n.y; Objects[objindex].Normals[vertB*3+2] += n.z; Objects[objindex].Normals[vertC*3] += n.x; Objects[objindex].Normals[vertC*3+1] += n.y; Objects[objindex].Normals[vertC*3+2] += n.z; }

// Store our current file position subs = ftell(bin3ds);

// Check to see how many materials the faces are split into while (ftell(bin3ds) < (findex + length - 6)) { fread(&h.id,sizeof(h.id),1,bin3ds); fread(&h.len,sizeof(h.len),1,bin3ds);

switch (h.id) { case FACE_MAT : //FacesMaterialsListChunkProcessor(h.len, ftell(bin3ds), objindex); numMatFaces++; break; default : break; }

fseek(bin3ds, (h.len - 6), SEEK_CUR); }

// Split the faces up according to their materials if (numMatFaces > 0) { // Allocate an array to hold the lists of faces divided by material Objects[objindex].MatFaces = new MaterialFaces[numMatFaces]; // Store the number of material faces Objects[objindex].numMatFaces = numMatFaces;

fseek(bin3ds, subs, SEEK_SET);

int j = 0;

// Split the faces up while (ftell(bin3ds) < (findex + length - 6)) { fread(&h.id,sizeof(h.id),1,bin3ds); fread(&h.len,sizeof(h.len),1,bin3ds);

switch (h.id) { case FACE_MAT : // Process the faces and split them up FacesMaterialsListChunkProcessor(h.len, ftell(bin3ds), objindex, j); j++; break; default : break; }

fseek(bin3ds, (h.len - 6), SEEK_CUR); } }

// move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

void Model_3DS::FacesMaterialsListChunkProcessor(long length, long findex, int objindex, int subfacesindex) { char name[80]; // The material's name unsigned short numEntries; // The number of faces associated with this material unsigned short Face; // Holds the faces as they are read int material; // An index to the Materials array for this material // move the file pointer to the beginning of the main // chunk's data findex + the size of the header fseek(bin3ds, findex, SEEK_SET);

// Read the material's name for (int i = 0; i < 80; i++) { name[i] = fgetc(bin3ds); if (name[i] == 0) { name[i] = NULL; break; } }

// Faind the material's index in the Materials array for (material = 0; material < numMaterials; material++) { if (strcmp(name, Materials[material].name) == 0) break; }

// Store this value for later so that we can find the material Objects[objindex].MatFaces[subfacesindex].MatIndex = material;

// Read the number of faces associated with this material fread(&numEntries,sizeof(numEntries),1,bin3ds);

// Allocate an array to hold the list of faces associated with this material Objects[objindex].MatFaces[subfacesindex].subFaces = new GLushort[numEntries * 3]; // Store this number for later use Objects[objindex].MatFaces[subfacesindex].numSubFaces = numEntries * 3;

// Read the faces into the array for (i = 0; i < numEntries * 3; i+=3) { // read the face fread(&Face,sizeof(Face),1,bin3ds); // Add the face's vertices to the list Objects[objindex].MatFaces[subfacesindex].subFaces[i] = Objects[objindex].Faces[Face * 3]; Objects[objindex].MatFaces[subfacesindex].subFaces[i+1] = Objects[objindex].Faces[Face * 3 + 1]; Objects[objindex].MatFaces[subfacesindex].subFaces[i+2] = Objects[objindex].Faces[Face * 3 + 2]; } // move the file pointer back to where we got it so // that the ProcessChunk() which we interrupted will read // from the right place fseek(bin3ds, findex, SEEK_SET); }

Currently browsing [3ds-loadview.zip] (13,248 bytes) - [Model_3DS.h] - (7,702 bytes)

//////////////////////////////////////////////////////////////////////
//
// 3D Studio Model Class
// by: Matthew Fairfax
//
// Model_3DS.h: interface for the Model_3DS class.
// This is a simple class for loading and viewing
// 3D Studio model files (.3ds). It supports models
// with multiple objects. It also supports multiple
// textures per object. It does not support the animation
// for 3D Studio models b/c there are simply too many
// ways for an artist to animate a 3D Studio model and
// I didn't want to impose huge limitations on the artists.
// However, I have imposed a limitation on how the models are
// textured:
// 1) Every faces must be assigned a material
// 2) If you want the face to be textured assign the
//    texture to the Diffuse Color map
// 3) The texture must be supported by the GLTexture class
//    which only supports bitmap and targa right now
// 4) The texture must be located in the same directory as
//    the model
//
// Support for non-textured faces is done by reading the color
// from the material's diffuse color.
//
// Some models have problems loading even if you follow all of
// the restrictions I have stated and I don't know why. If you
// can import the 3D Studio file into Milkshape 3D 
// (http://www.swissquake.ch/chumbalum-soft) and then export it
// to a new 3D Studio file. This seems to fix many of the problems
// but there is a limit on the number of faces and vertices Milkshape 3D
// can read.
//
// Usage:
// Model_3DS m;
//
// m.Load("model.3ds"); // Load the model
// m.Draw();			// Renders the model to the screen
//
// // If you want to show the model's normals
// m.shownormals = true;
//
// // If the model is not going to be lit then set the lit
// // variable to false. It defaults to true.
// m.lit = false;
//
// // You can disable the rendering of the model
// m.visible = false;
// 
// // You can move and rotate the model like this:
// m.rot.x = 90.0f;
// m.rot.y = 30.0f;
// m.rot.z = 0.0f;
//
// m.pos.x = 10.0f;
// m.pos.y = 0.0f;
// m.pos.z = 0.0f;
//
// // If you want to move or rotate individual objects
// m.Objects[0].rot.x = 90.0f;
// m.Objects[0].rot.y = 30.0f;
// m.Objects[0].rot.z = 0.0f;
//
// m.Objects[0].pos.x = 10.0f;
// m.Objects[0].pos.y = 0.0f;
// m.Objects[0].pos.z = 0.0f;
//
//////////////////////////////////////////////////////////////////////

#ifndef MODEL_3DS_H
#define MODEL_3DS_H

// I decided to use my GLTexture class b/c adding all of its functions // Would have greatly bloated the model class's code // Just replace this with your favorite texture class #include "GLTexture.h"

#include <stdio.h>

class Model_3DS { public: // A VERY simple vector struct // I could have included a complex class but I wanted the model class to stand alone struct Vector { float x; float y; float z; };

// Vertex struct to make code easier to read in places struct Vertex { float x; float y; float z; };

// Color struct holds the diffuse color of the material struct Color4i { unsigned char r; unsigned char g; unsigned char b; unsigned char a; };

// Holds the material info // TODO: add color support for non textured polys struct Material { char name[80]; // The material's name GLTexture tex; // The texture (this is the only outside reference in this class) bool textured; // whether or not it is textured Color4i color; };

// Every chunk in the 3ds file starts with this struct struct ChunkHeader { unsigned short id; // The chunk's id unsigned long len; // The lenght of the chunk };

// I sort the mesh by material so that I won't have to switch textures a great deal struct MaterialFaces { unsigned short *subFaces; // Index to our vertex array of all the faces that use this material int numSubFaces; // The number of faces int MatIndex; // An index to our materials };

// The 3ds file can be made up of several objects struct Object { char name[80]; // The object name float *Vertexes; // The array of vertices float *Normals; // The array of the normals for the vertices float *TexCoords; // The array of texture coordinates for the vertices unsigned short *Faces; // The array of face indices int numFaces; // The number of faces int numMatFaces; // The number of differnet material faces int numVerts; // The number of vertices int numTexCoords; // The number of vertices bool textured; // True: the object has textures MaterialFaces *MatFaces; // The faces are divided by materials Vector pos; // The position to move the object to Vector rot; // The angles to rotate the object };

char *modelname; // The name of the model char *path; // The path of the model int numObjects; // Total number of objects in the model int numMaterials; // Total number of materials in the model int totalVerts; // Total number of vertices in the model int totalFaces; // Total number of faces in the model bool shownormals; // True: show the normals Material *Materials; // The array of materials Object *Objects; // The array of objects in the model Vector pos; // The position to move the model to Vector rot; // The angles to rotate the model float scale; // The size you want the model scaled to bool lit; // True: the model is lit bool visible; // True: the model gets rendered void Load(char *name); // Loads a model void Draw(); // Draws the model FILE *bin3ds; // The binary 3ds file Model_3DS(); // Constructor virtual ~Model_3DS(); // Destructor private: void IntColorChunkProcessor(long length, long findex, int matindex); void FloatColorChunkProcessor(long length, long findex, int matindex); // Processes the Main Chunk that all the other chunks exist is void MainChunkProcessor(long length, long findex); // Processes the model's info void EditChunkProcessor(long length, long findex); // Processes the model's materials void MaterialChunkProcessor(long length, long findex, int matindex); // Processes the names of the materials void MaterialNameChunkProcessor(long length, long findex, int matindex); // Processes the material's diffuse color void DiffuseColorChunkProcessor(long length, long findex, int matindex); // Processes the material's texture maps void TextureMapChunkProcessor(long length, long findex, int matindex); // Processes the names of the textures and load the textures void MapNameChunkProcessor(long length, long findex, int matindex); // Processes the model's geometry void ObjectChunkProcessor(long length, long findex, int objindex); // Processes the triangles of the model void TriangularMeshChunkProcessor(long length, long findex, int objindex); // Processes the vertices of the model and loads them void VertexListChunkProcessor(long length, long findex, int objindex); // Processes the texture cordiantes of the vertices and loads them void TexCoordsChunkProcessor(long length, long findex, int objindex); // Processes the faces of the model and loads the faces void FacesDescriptionChunkProcessor(long length, long findex, int objindex); // Processes the materials of the faces and splits them up by material void FacesMaterialsListChunkProcessor(long length, long findex, int objindex, int subfacesindex);

// Calculates the normals of the vertices by averaging // the normals of the faces that use that vertex void CalculateNormals(); };

#endif MODEL_3DS_H

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

 

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.