我相信

要让未来的自己,喜欢自己的模样。

OpenGL读取3DS文件示例(C++语言编写)

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

posted on 2012-04-19 22:30 sarah 阅读(3170) 评论(0)  编辑 收藏 引用 所属分类: C++


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


My Links

Blog Stats

常用链接

留言簿

随笔档案(5)

文章分类(7)

文章档案(8)

BLOG

最新随笔

搜索

最新评论

阅读排行榜

评论排行榜