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.