这个代码不算成熟,但是可以读取一般的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 ) {} 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(); } 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 ); } } } 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 ); } 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 ); } } 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; } 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 ); } } 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; }
|