#ifndef MODEL_3DS_H
#define MODEL_3DS_H
// I decided to use my CIMAGE 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 "BaseCode/Image.h"
#include "BaseCode/Math/math.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
CIMAGE 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 *Tangents; // 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
// 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.numFaces/3;
totalVerts += Objects.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.textured)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if (lit)
{
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
}
glEnableClientState(GL_VERTEX_ARRAY);
// Point them to the objects arrays
if (Objects.textured)
glTexCoordPointer(2, GL_FLOAT, 0, Objects.TexCoords);
if (lit)
{
glNormalPointer(GL_FLOAT, 0, Objects.Normals);
glColorPointer(3, GL_FLOAT, 0, Objects.Tangents);
}
glVertexPointer(3, GL_FLOAT, 0, Objects.Vertexes);
// Loop through the faces as sorted by material and draw them
for (int j = 0; j < Objects.numMatFaces; j ++)
{
// Use the material's texture
Materials[Objects.MatFaces[j].MatIndex].tex.Bind();
glPushMatrix();
// Move the model
glTranslatef(Objects.pos.x, Objects.pos.y, Objects.pos.z);
// Rotate the model
//glRotatef(Objects.rot.x, 1.0f, 0.0f, 0.0f);
//glRotatef(Objects.rot.y, 0.0f, 1.0f, 0.0f);
//glRotatef(Objects.rot.z, 0.0f, 0.0f, 1.0f);
glRotatef(Objects.rot.z, 0.0f, 0.0f, 1.0f);
glRotatef(Objects.rot.y, 0.0f, 1.0f, 0.0f);
glRotatef(Objects.rot.x, 1.0f, 0.0f, 0.0f);
// Draw the faces using an index to the vertex array
glDrawElements(GL_TRIANGLES, Objects.MatFaces[j].numSubFaces, GL_UNSIGNED_SHORT, Objects.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.numVerts * 2; 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.Vertexes[k], Objects.Vertexes[k+1], Objects.Vertexes[k+2]);
glVertex3f(Objects.Vertexes[k]+Objects.Normals[k]*3.0, Objects.Vertexes[k+1]+Objects.Normals[k+1]*3.0, Objects.Vertexes[k+2]+Objects.Normals[k+2]*3.0);
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);
}
for (int k = 0; k < Objects.numVerts * 2; 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.Vertexes[k],Objects.Vertexes[k+1], Objects.Vertexes[k+2]);
glVertex3f(Objects.Vertexes[k]+Objects.Tangents[k]*2.0, Objects.Vertexes[k+1]+Objects.Tangents[k+1]*2.0, Objects.Vertexes[k+2]+Objects.Tangents[k+2]*2.0);
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);
}
for (int k = 0; k < Objects.numVerts * 2; 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);
vec3 Normal = vec3(Objects.Normals[k], Objects.Normals[k+1], Objects.Normals[k+2]);
vec3 Tangent = vec3(Objects.Tangents[k], Objects.Tangents[k+1], Objects.Tangents[k+2]);
vec3 BiNormal = cross(Normal,Tangent);
// Draw a line between the vertex and the end of the normal
glBegin(GL_LINES);
glVertex3f(Objects.Vertexes[k],Objects.Vertexes[k+1], Objects.Vertexes[k+2]);
glVertex3f(Objects.Vertexes[k]+BiNormal.x, Objects.Vertexes[k+1]+BiNormal.y, Objects.Vertexes[k+2]+BiNormal.z);
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.numVerts; g++)
{
vec3 Pos1 = vec3(Objects.Vertexes[g*3],
Objects.Vertexes[g*3+1],
Objects.Vertexes[g*3+2]);
for (int i2 = 0; i2 < numObjects; i2++)
{
for (int g2 = 0; g2 < Objects[i2].numVerts; g2++)
{
vec3 Pos2 = vec3(Objects[i2].Vertexes[g2*3],
Objects[i2].Vertexes[g2*3+1],
Objects[i2].Vertexes[g2*3+2]);
if (Pos1 == Pos2)
{
Objects.Normals[g*3] += Objects[i2].Normals[g2*3];
Objects.Normals[g*3+1] += Objects[i2].Normals[g2*3+1];
Objects.Normals[g*3+2] += Objects[i2].Normals[g2*3+2];
Objects[i2].Normals[g2*3] = Objects.Normals[g*3];
Objects[i2].Normals[g2*3+1] = Objects.Normals[g*3+1];
Objects[i2].Normals[g2*3+2] = Objects.Normals[g*3+2];
Objects.Tangents[g*3] += Objects[i2].Tangents[g2*3];
Objects.Tangents[g*3+1] += Objects[i2].Tangents[g2*3+1];
Objects.Tangents[g*3+2] += Objects[i2].Tangents[g2*3+2];
Objects[i2].Tangents[g2*3] = Objects.Tangents[g*3];
Objects[i2].Tangents[g2*3+1] = Objects.Tangents[g*3+1];
Objects[i2].Tangents[g2*3+2] = Objects.Tangents[g*3+2];
}
}
}
}
}
for (int i = 0; i < numObjects; i++)
{
for (int g = 0; g < Objects.numVerts; g++)
{
// Reduce each vert's normal to unit
float length;
Vector unit;
unit.x = Objects.Normals[g*3];
unit.y = Objects.Normals[g*3+1];
unit.z = Objects.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.Normals[g*3] = unit.x;
Objects.Normals[g*3+1] = unit.y;
Objects.Normals[g*3+2] = unit.z;
unit.x = Objects.Tangents[g*3];
unit.y = Objects.Tangents[g*3+1];
unit.z = Objects.Tangents[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.Tangents[g*3] = unit.x;
Objects.Tangents[g*3+1] = unit.y;
Objects.Tangents[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 = fgetc(bin3ds);
if (Materials[matindex].name == 0)
{
Materials[matindex].name = 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 = fgetc(bin3ds);
if (name == 0)
{
name = 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 = fgetc(bin3ds);
if (Objects[objindex].name == 0)
{
Objects[objindex].name = 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];
Objects[objindex].Tangents = 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;
Objects[objindex].Tangents[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,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,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 = 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;
vec3 P1,P2,P3;
vec2 UV1,UV2,UV3;
P1.x = Objects[objindex].Vertexes[vertA*3];
P1.y = Objects[objindex].Vertexes[vertA*3+1];
P1.z = Objects[objindex].Vertexes[vertA*3+2];
P2.x = Objects[objindex].Vertexes[vertB*3];
P2.y = Objects[objindex].Vertexes[vertB*3+1];
P2.z = Objects[objindex].Vertexes[vertB*3+2];
P3.x = Objects[objindex].Vertexes[vertC*3];
P3.y = Objects[objindex].Vertexes[vertC*3+1];
P3.z = Objects[objindex].Vertexes[vertC*3+2];
UV1.x = Objects[objindex].TexCoords[vertA*2];
UV1.y = Objects[objindex].TexCoords[vertA*2+1];
UV2.x = Objects[objindex].TexCoords[vertB*2];
UV2.y = Objects[objindex].TexCoords[vertB*2+1];
UV3.x = Objects[objindex].TexCoords[vertC*2];
UV3.y = Objects[objindex].TexCoords[vertC*2+1];
vec3 Edge1 = P2 - P1;
vec3 Edge2 = P3 - P1;
vec2 Edge1uv = UV2 - UV1;
vec2 Edge2uv = UV3 - UV1;
vec3 tangent;
float cp = Edge1uv.y * Edge2uv.x - Edge1uv.x * Edge2uv.y;
if ( cp != 0.0f ) {
float mul = 1.0f / cp;
tangent = (Edge1 * -Edge2uv.y + Edge2 * Edge1uv.y) * mul;
}
// Add this normal to its verts' normals
Objects[objindex].Tangents[vertA*3] += tangent.x;
Objects[objindex].Tangents[vertA*3+1] += tangent.y;
Objects[objindex].Tangents[vertA*3+2] += tangent.z;
Objects[objindex].Tangents[vertB*3] += tangent.x;
Objects[objindex].Tangents[vertB*3+1] += tangent.y;
Objects[objindex].Tangents[vertB*3+2] += tangent.z;
Objects[objindex].Tangents[vertC*3] += tangent.x;
Objects[objindex].Tangents[vertC*3+1] += tangent.y;
Objects[objindex].Tangents[vertC*3+2] += tangent.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 = fgetc(bin3ds);
if (name == 0)
{
name = 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 = 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);
}
3ds loader problem (crashes)
im having some trouble loading 3ds models... when i try to load a model I get an error and the program crashes... the loader when i try to load very simple models ive created quickly.. something like a textured box...
any ideas what might be causing it?
there error is something like "Debug assertion" and "Expression: _CrtCheckMemory"
im using the following loader code..
as a sidenote how would i modify the loader to load bumpmaps aswell..
if u dont know how to fix this maybe u could suggest a better loader... i dont really feel there is any reason to crate an own loader.. due to how much time it requires and that there should be some good free ones out there...
ive tried lib3ds... but ive never got that to work...
[Edited by - Dragon_Strike on May 17, 2007 12:16:18 PM]
First of all, copy the exact error, but not after finding out on which line exactly it occurs. Second of all, do something about your spelling.
This is just friendly advice so you get more chance on a more useful answer.
This is just friendly advice so you get more chance on a more useful answer.
Quote:Original post by Subotron
First of all, copy the exact error, but not after finding out on which line exactly it occurs. Second of all, do something about your spelling.
This is just friendly advice so you get more chance on a more useful answer.
it says... file: dbgheap.c
line: 346
and yea.. ill work on my spelling...
The error sounds like you are trying to access a null pointer or write to invalid memory. Does it crash when you load or when you draw the file? Can you run the application in debug mode and find the exact line that causes the exception? That will make it much easier to work out the cause.
Quote:Original post by weasalmongler
The error sounds like you are trying to access a null pointer or write to invalid memory. Does it crash when you load or when you draw the file? Can you run the application in debug mode and find the exact line that causes the exception? That will make it much easier to work out the cause.
its when loading the file.. i cant get the line... i get a new window with the error and then the program shuts down.. i dont get any line where the error occurred even in debug mode...
im troubleshooting now and trying to find about where in the load sequence it crashes...
this is what ive got...
Load->MainChunkProcessor->EditChunkProcessor->ObjectChunkProcessor->TriangularMeshChunkProcessor->
VertexListChunkProcessor (however i get the error after closing the program when i disable this one)
thats as far as i get right now...
Load->MainChunkProcessor->EditChunkProcessor->ObjectChunkProcessor->TriangularMeshChunkProcessor->
VertexListChunkProcessor (however i get the error after closing the program when i disable this one)
thats as far as i get right now...
If it is crashing in VertexListChunkProcessor then make sure that objindex always remains in a valid range for the number of objects actually created. In the debugger set breakpoints in a few places and examine what the values of different variables are, make sure they are all correct. The code you've posted is quite long and is difficult to debug without running it.
well as far as ive been able to debug the problems seems to occur here..
Objects[objindex].Vertexes = new GLfloat[numVerts * 3];
Objects[objindex].Normals = new GLfloat[numVerts * 3];
Objects[objindex].Tangents = new GLfloat[numVerts * 3];
but i dont understand how...
anyways.. im giving up on this... ive tried for a few hours now... but i dont think im good enough to be able to solve this problem..
ill have to search around for another loader...
thx alot for the help anyway...
Objects[objindex].Vertexes = new GLfloat[numVerts * 3];
Objects[objindex].Normals = new GLfloat[numVerts * 3];
Objects[objindex].Tangents = new GLfloat[numVerts * 3];
but i dont understand how...
anyways.. im giving up on this... ive tried for a few hours now... but i dont think im good enough to be able to solve this problem..
ill have to search around for another loader...
thx alot for the help anyway...
I'm sure that the problem is that your objindex does not lie in the [0, numObjects-1] range. That would lead to the program trying to write to invalid memory and cause the crash. You just need to work out why objindex isn't the right value.
Sorry to hear you're giving up. Best of luck with it in future.
Sorry to hear you're giving up. Best of luck with it in future.
well on second thought.. if i give up with this i have to give up with 3d programming.. .since this is an important part...
so the hell with it.. ill write my own loader...
ive just begun with it but ive alrdy encountered a problem.. the model doesnt draw properly... im just trying to load a simple box and draw its polygons.. but it gets messed up... i just get to planes facing each other.. they are not connected for some reason
i hope its not to much code to read through.. i have no idea what could be wrong
EDIT:::
i seem to have solved it...
which brings me to the next question.. wher can i find some documentation on more advanced loading? models consisting of several objects... loading the texturenames (diffusemap, bumpmap etc...)..
[Edited by - Dragon_Strike on May 17, 2007 12:19:45 PM]
so the hell with it.. ill write my own loader...
ive just begun with it but ive alrdy encountered a problem.. the model doesnt draw properly... im just trying to load a simple box and draw its polygons.. but it gets messed up... i just get to planes facing each other.. they are not connected for some reason
i hope its not to much code to read through.. i have no idea what could be wrong
EDIT:::
i seem to have solved it...
which brings me to the next question.. wher can i find some documentation on more advanced loading? models consisting of several objects... loading the texturenames (diffusemap, bumpmap etc...)..
[Edited by - Dragon_Strike on May 17, 2007 12:19:45 PM]
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement