这个代码不算成熟,但是可以读取一般的3DS文件了,还有重现时物体材质没有精心设计,这里请阅读的朋友自行设计吧。这个例子的目的是给刚学的朋友提供一个例子,代码如下:
3dsId.h
#ifndef _3DS_ID_H_
#define _3DS_ID_H_
const GLsizei PRIMARY = 0x4D4D;
const GLsizei MAINOBJECT = 0x3D3D; // 网格对象的版本号
const GLsizei EDITKEYFRAME = 0xB000; // 所有关键帧信息的头部
const GLsizei MATERIAL = 0xAFFF; // 保存纹理信息
const GLsizei OBJECT = 0x4000; // 保存对象的面、顶点等信息
const GLsizei MATNAME = 0xA000; // 保存材质名称
const GLsizei OBJECT_MESH = 0x4100; // 新的网格对象
const GLsizei OBJECT_VERTICES = 0x4110; // 对象顶点
const GLsizei OBJECT_FACES = 0x4120; // 对象的面
const GLsizei OBJECT_MATERIAL = 0x4130; // 对象的材质
const GLsizei MAT_AMBIENT = 0xa010;
const GLsizei MAT_DIFFUSE = 0xa020;
const GLsizei MAT_SPECULAR = 0xa030;
const GLsizei MAT_SHININESS = 0xa040;
const GLsizei MAT_TRANSPARENCY = 0xa050;
const GLsizei INT_PERCENTAGE = 0x0030;
const GLsizei FLOAT_PERCENTAGE = 0x0031;
const GLsizei COLOR_F = 0x0010;
const GLsizei COLOR_24 = 0x0011;
#endif
MyModel.h
#ifndef __MY_MODEL_H__
#define __MY_MODEL_H__
#include <gl\glut.h>
#include <vector>
#include <string>
struct Vertex
{
GLfloat x;
GLfloat y;
GLfloat z;
};
struct Face
{
GLushort Index[3];
GLushort MaterialPos;
Vertex Normal;
};
struct Chunk
{
GLushort ID;
GLuint Len;
};
struct Material
{
std::string name;
GLfloat ambientColor[3];
GLfloat diffuseColor[3];
GLfloat specularColor[3];
GLfloat emissiveColor[3];
GLfloat shininess;
GLfloat transparency;
};
struct Object
{
std::string Name;
std::vector< Vertex > Vertexs;
std::vector< Face > Faces;
};
struct Model
{
std::vector< Object > MyObject;
std::vector< Material > MyMaterial;
};
#endif
MyLoader.h
#ifndef __3DS_LOADER_H__
#define __3DS_LOADER_H__
#include <fstream>
#include "MyModel.h"
class MyLoader
{
public:
MyLoader();
void OpenFile( const std::string& );
void LoadFile();
void CloseFile();
const Model& GetModel();
private:
void LoadModel( const Chunk& );
void LoadMaterial( const Chunk& );
void LoadColor( float* );
void LoadPercent( float* );
void LoadObject( const Chunk& );
void LoadVertex( Object* const& );
void LoadFaces( Object* );
void LoadObjectMaterial( Object* );
void LoadMesh( const Chunk& MyChunk );
private:
Vertex Vectors( const Vertex&, const Vertex& );
Vertex Cross( const Vertex&, const Vertex& );
void Normalize( Vertex* Point );
void ComputeNormals();
private:
void ReadChunk( Chunk* MyChunk );
void ReadGLfloat( GLfloat* );
void ReadGLushort( GLushort* );
void ReadGLuint( GLuint* );
void ReadGLubyte( GLubyte* );
void SkipNByte( const size_t& );
std::string ReadString();
private:
size_t NowPos;
size_t FileLength;
size_t Version;
Model MyModel;
std::ifstream FileReader;
};
#endif
MyLoader.cpp
#include "MyLoader.h"
#include <stdexcept>
#include <cmath>
#include "3dsId.h"
using namespace std;
MyLoader::MyLoader() : NowPos( 0 ),FileLength( 0 ), Version( 0 )
{}
/*
*功能:读取文件
*说明:打开文件前需要加locale::global以保证可以正确打开中文名称文件,
* 另外需要以二进制方式打开,以保证所有文件能够正确读取
*/
void MyLoader::OpenFile( const string& FileRoad )
{
locale::global( locale("") );
FileReader.open( FileRoad.c_str(), ifstream::binary );
locale::global( locale("C") );
if( !FileReader )
throw std::runtime_error( "打开文件失败" );;
}
/*
*功能:关闭文件流
*说明:暂无
*/
void MyLoader::CloseFile()
{
FileReader.close();
}
/*
*功能:加载3DS文件
*说明:其他所有辅助函数都是为实现此功能,实现加载头3DS文件的有用信息
*/
void MyLoader::LoadFile()
{
Chunk MyChunk;
ReadChunk( &MyChunk );
if( PRIMARY != MyChunk.ID )
throw runtime_error( "文件损坏" );
FileLength = MyChunk.Len;
ReadChunk( &MyChunk );
ReadGLuint( &Version );
while( NowPos < FileLength )
{
ReadChunk( &MyChunk );
if( MAINOBJECT == MyChunk.ID )
LoadModel( MyChunk );
else
SkipNByte( MyChunk.Len - 6 );
}
ComputeNormals();
}
/*
*功能:加载模型
*说明:加载模型的有用数据
*/
void MyLoader::LoadModel( const Chunk& MyChunk )
{
size_t BeginPos( NowPos - 6 );
Chunk TempChunk;
while( NowPos - BeginPos != MyChunk.Len )
{
ReadChunk( &TempChunk );
switch( TempChunk.ID )
{
case OBJECT:
LoadObject( TempChunk );
break;
case MATERIAL:
LoadMaterial( TempChunk );
break;
default:
SkipNByte( TempChunk.Len - 6 );
}
}
}
/*
*功能:加载所有对象
*功能:此处仅仅提取网个对象
*/
void MyLoader::LoadObject( const Chunk& MyChunk )
{
Object object;
object.Name = ReadString();
MyModel.MyObject.push_back( object );
Chunk ThisChunk;
size_t BeginPos( NowPos - 7 - object.Name.size() );
while( NowPos - BeginPos != MyChunk.Len )
{
ReadChunk( &ThisChunk );
if( OBJECT_MESH == ThisChunk.ID )
LoadMesh( ThisChunk );
else
SkipNByte( ThisChunk.Len - 6 );
}
}
/*
*功能:加载网格对象
*说明:网格对象包括顶点,面,关联材质库的材质位置等
*/
void MyLoader::LoadMesh( const Chunk& MyChunk )
{
Object &object = MyModel.MyObject[ MyModel.MyObject.size() - 1 ];
size_t BeginPos( NowPos - 6 );
Chunk ThisChunk;
while( NowPos - BeginPos != MyChunk.Len )
{
ReadChunk( &ThisChunk );
switch( ThisChunk.ID )
{
case OBJECT_VERTICES: //顶点
LoadVertex( &object );
break;
case OBJECT_FACES: //面
LoadFaces( &object );
break;
case OBJECT_MATERIAL: //材质
LoadObjectMaterial( &object );
break;
default: //跳过不需要的块
SkipNByte( ThisChunk.Len - 6 );
}
}
}
/*
*功能:加载Object的关联的材质列表
*说明:加载的不是直接的材质,加载的是材质在材质数组中的位置
*/
void MyLoader::LoadObjectMaterial( Object* object )
{
string Name = ReadString();
int Pos( -1 );
for( size_t i = 0; i != MyModel.MyMaterial.size(); ++ i )
{
if( MyModel.MyMaterial[ i ].name == Name )
Pos = i;
}
if( Pos == -1 )
throw runtime_error( "没有找到该材质" );
GLushort Sum( 0 ); GLushort FacePos( 0 );
ReadGLushort( &Sum );
for( size_t i = 0; i != Sum; ++ i )
{
ReadGLushort( &FacePos );
object->Faces[ FacePos ].MaterialPos = Pos;
}
}
/*
*功能:加载模型所需所有材质
*说明:模型所需要的所有材质都在此加载入材质库
*/
void MyLoader::LoadMaterial( const Chunk& MyChunk )
{
Chunk TempChunk;
Material material;
size_t BeginPos( NowPos - 6 );
while( NowPos - BeginPos < MyChunk.Len )
{
ReadChunk( &TempChunk );
switch( TempChunk.ID )
{
case MATNAME: //材质名称
material.name = ReadString();
break;
case MAT_AMBIENT: //材质Ambient
LoadColor( material.ambientColor );
break;
case MAT_DIFFUSE: //材质Diffuse
LoadColor( material.diffuseColor );
break;
case MAT_SPECULAR: //材质Specular
LoadColor( material.specularColor );
break;
case MAT_SHININESS: //材质Shininess
LoadPercent( &material.shininess );
break;
case MAT_TRANSPARENCY: //材质透明度
LoadPercent( &material.transparency );
break;
default:
SkipNByte( TempChunk.Len - 6 );
}
}
MyModel.MyMaterial.push_back( material );
}
/*
*功能:加载材质颜色
*说明:3DS文件的材质颜色存储有两种形式,一种是(0~255)的整数形式存储,
* 占用一个字节;另一种是(0.0~1.0)的浮点型存储,占用四个字节
*/
void MyLoader::LoadColor( float* color )
{
Chunk TempChunk;
ReadChunk( &TempChunk );
switch( TempChunk.ID )
{
case COLOR_F:
ReadGLfloat( &color[ 0 ] );
ReadGLfloat( &color[ 1 ] );
ReadGLfloat( &color[ 2 ] );
break;
case COLOR_24:
GLubyte Byte;
for( size_t i = 0; i != 3; ++ i )
{
ReadGLubyte( &Byte );
color[ i ] = Byte / 255.0;
}
break;
default:
SkipNByte( TempChunk.Len - 6 );
}
}
/*
*功能:加载百分数
*说明:材质的Shininess和透明度是一个百分数,但是百分数有两种
* 一种但用两个字节,存储的是百分数的分子整数部分;另一种
* 是占用四字节,是一个浮点数
*/
void MyLoader::LoadPercent( GLfloat* Temp )
{
Chunk TempChunk;
ReadChunk( &TempChunk );
switch( TempChunk.ID )
{
case INT_PERCENTAGE: //Int型 百分数
GLushort Percent;
ReadGLushort( &Percent );
*Temp = Percent / 100.0;
break;
case FLOAT_PERCENTAGE: //Float型 百分数
ReadGLfloat( Temp );
break;
default:
SkipNByte( TempChunk.Len - 6 );
}
}
/*
*功能:解释两顶点为一矢量
*说明:暂无
*/
Vertex MyLoader::Vectors( const Vertex& lPoint, const Vertex& rPoint )
{
Vertex Point;
Point.x = lPoint.x - rPoint.x;
Point.y = lPoint.y - rPoint.y;
Point.z = lPoint.z - rPoint.z;
return Point;
}
/*
*功能:计算两矢量的叉积
*说明:计算平面法向量
*/
Vertex MyLoader::Cross( const Vertex& lPoint, const Vertex& rPoint )
{
Vertex Point;
Point.x = lPoint.y * rPoint.z - lPoint.z * rPoint.y;
Point.y = lPoint.z * rPoint.x - lPoint.x * rPoint.z;
Point.z = lPoint.x * rPoint.y - lPoint.y * rPoint.x;
return Point;
}
/*
*功能:单位化矢量
*说明:计算光照所用
*/
void MyLoader::Normalize( Vertex* point )
{
float Magnitude = sqrt( point->x * point->x + point->y * point->y + point->z * point->z );
if( 0 == Magnitude )
Magnitude = 1;
point->x /= Magnitude;
point->y /= Magnitude;
point->z /= Magnitude;
}
/*
*功能:为所有平面计算法向量
*说明:暂无
*/
void MyLoader::ComputeNormals()
{
for( size_t i = 0; i != MyModel.MyObject.size(); ++ i )
{
Object& object = MyModel.MyObject[ i ];
for( size_t j = 0; j != MyModel.MyObject[ i ].Faces.size(); ++ j )
{
Face& face = object.Faces[ j ];
const Vertex &Point1 = object.Vertexs[ face.Index[ 0 ] ];
const Vertex &Point2 = object.Vertexs[ face.Index[ 1 ] ];
const Vertex &Point3 = object.Vertexs[ face.Index[ 2 ] ];
face.Normal = Cross( Vectors( Point1, Point3 ), Vectors( Point3, Point2 ) );
Normalize( &face.Normal );
}
}
}
/*
*功能:返回模型对象
*说明:此处以引用方式返回,容易造成外值被修改
*/
const Model& MyLoader::GetModel()
{
return MyModel;
}
/*
*功能:加载图形所有的面(3ds max是以三角形面存储的)
*说明:面是以顶点索引的形式存储在文件中的,每三个索引是一个面,
* 但两组索引之间有一个两个字节的边界,需要跳过
*/
void MyLoader::LoadFaces( Object* ThisObject )
{
GLushort Sum( 0 );
ReadGLushort( &Sum );
Face face; GLushort Temp( 0 );
for( size_t i = 0; i != Sum; ++ i )
{
for( size_t j = 0; j != 4; ++ j )
{
ReadGLushort( &Temp );
if( j < 3 )
face.Index[ j ] = Temp;
}
ThisObject->Faces.push_back( face );
}
}
/*
*功能:加载顶点信息
*说明:此处需要注意的3DS max的坐标系统与OpenGL不同,需要
* 将Y,Z轴交换并且交换后Z轴取反
*/
void MyLoader::LoadVertex( Object* const& ThisObject )
{
GLushort Sum( 0 );
ReadGLushort( &Sum );
Vertex Point;
float Num( 0 );float distence( 0 );
for( size_t i = 0; i != Sum; ++ i )
{
ReadGLfloat( &Point.x );
ReadGLfloat( &Point.z );
ReadGLfloat( &Point.y );
Point.z *= -1;
ThisObject->Vertexs.push_back( Point );
}
}
/*
*功能:读取名称类型数据
*说明:暂无
*/
void MyLoader::ReadChunk( Chunk* MyChunk )
{
ReadGLushort( &MyChunk->ID );
ReadGLuint( &MyChunk->Len );
}
void MyLoader::ReadGLubyte( GLubyte* Ubyte )
{
FileReader.read( reinterpret_cast< char* >( Ubyte ), sizeof( GLubyte ) );
NowPos += sizeof( GLubyte );
}
void MyLoader::ReadGLushort( GLushort* Ushort )
{
FileReader.read( reinterpret_cast< char* >( Ushort ), sizeof( GLushort ) );
NowPos += sizeof( GLushort );
}
void MyLoader::ReadGLuint( GLuint* Uint )
{
FileReader.read( reinterpret_cast< char* >( Uint ), sizeof( GLuint ) );
NowPos += sizeof( GLuint );
}
void MyLoader::ReadGLfloat( GLfloat* Float )
{
FileReader.read( reinterpret_cast< char* >( Float ), sizeof( GLfloat ) );
NowPos += sizeof( GLfloat );
}
std::string MyLoader::ReadString()
{
char alpha; string TempWord;
while( FileReader.get( alpha ), alpha != 0 )
TempWord += alpha;
NowPos += TempWord.size() + 1;
return TempWord;
}
void MyLoader::SkipNByte( const size_t& Num )
{
FileReader.seekg( Num, ifstream::cur );
NowPos += Num;
}
main.cpp
#include "MyLoader.h"
#include <iostream>
using namespace std;
MyLoader Loader;
static GLfloat Spin = 0;
GLint MyList;
void BuildList()
{
MyList = glGenLists( 1 );
glNewList( MyList, GL_COMPILE );
Model TempModel = Loader.GetModel();
for( size_t i = 0; i != TempModel.MyObject.size(); ++ i )
{
const Object& object = TempModel.MyObject[i];
glBegin( GL_TRIANGLES );
for(int j = 0; j != object.Faces.size(); j++)
{
const Face& ThisFace = object.Faces[ j ];
const Material& MyMaterial = TempModel.MyMaterial[ ThisFace.MaterialPos ];
glNormal3f( ThisFace.Normal.x, ThisFace.Normal.y, ThisFace.Normal.z );
glColor4f( MyMaterial.diffuseColor[ 0 ], MyMaterial.diffuseColor[ 1 ],
MyMaterial.diffuseColor[ 2 ], MyMaterial.transparency );
if( MyMaterial.transparency )
glEnable( GL_BLEND );
for( size_t k = 0; k != 3; ++ k )
{
size_t index = object.Faces[ j ].Index[ k ];
glVertex3f( object.Vertexs[ index ].x, object.Vertexs[ index ].y, object.Vertexs[ index ].z );
}
if( MyMaterial.transparency )
glDisable( GL_BLEND );
}
glEnd();
}
glEndList();
}
void Init()
{
try
{
Loader.OpenFile( "Car.3DS" );
Loader.LoadFile();
Loader.CloseFile();
}
catch( runtime_error e )
{
cout << e.what() << endl;
}
catch(...)
{
cout << "未知错误" << endl;
}
glShadeModel( GL_SMOOTH );
glClearColor( 0.0, 0.0, 0.0, 0.0 );
glClearDepth( 1.0 );
GLfloat Ambient[4] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat Diffuse[4] = { 0.7, 0.7, 0.7, 1.0 };
GLfloat Position[4] = { 0.0, 0.0, -1.0, 1.0 };
GLfloat GlobalLight[4] = { 0.1, 0.1, 0.1, 1.0 };
glLightfv( GL_LIGHT0, GL_AMBIENT, Ambient );
glLightfv( GL_LIGHT0, GL_DIFFUSE, Diffuse );
glLightfv( GL_LIGHT0, GL_POSITION, Position );
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, GlobalLight );
glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glBlendFunc( GL_SRC_ALPHA, GL_SRC_ALPHA );
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
glEnable( GL_COLOR_MATERIAL );
glEnable( GL_DEPTH_TEST );
glEnable( GL_CULL_FACE );
BuildList();
}
void SpinDisplay()
{
if( ++ Spin > 360 )
Spin -= 360;
glutPostRedisplay();
}
void Display()
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glLoadIdentity();
gluLookAt( 0., 0.0, 200.0 , 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 );
glRotatef( Spin, 0.0, 1.0, 0.0 );
glCallList( MyList );
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int high )
{
if ( high == 0 )
high = 1;
glViewport( 0, 0, static_cast< GLsizei >( width ), static_cast< GLsizei >( high ) );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 45.0, static_cast< GLdouble >( width ) / static_cast< GLdouble >( high ), 0.1, 1000.0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
}
int main( int argc, char* argv[] )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH );
glutInitWindowSize( 800, 600 );
glutInitWindowPosition( 100, 100 );
glutCreateWindow( "MySystem" );
Init();
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glutIdleFunc( SpinDisplay );
glutMainLoop();
return 0;
}
(转帖自:http://hi.baidu.com/girlkoo/blog/item/321bf4ae0cdb2dc57cd92a5a.html
)