#include "MyLoader.h"
#include <stdexcept>
#include <fstream>
#include <cmath>
#include <iostream>
#include "3dsId.h"
using namespace std;
MyLoader::MyLoader() : NowPos( 0 ),FileLength( 0 ), Version( 0 )
{}
//加载3DS文件
void MyLoader::LoadFile()
{
Chunk MyChunk;
ReadChunk( &MyChunk );
if( FileLength != MyChunk.Len )
throw runtime_error( "文件损坏" );
ReadChunk( &MyChunk );
Version = ReadNByte( 4 );
while( NowPos < FileLength )
{
ReadChunk( &MyChunk );
if( OBJECTINFO == MyChunk.ID )
LoadObjectInfo( MyChunk );
else
SkipNByte( MyChunk.Len - 6 );
}
ComputeNormals();
}
void MyLoader::LoadObjectInfo( const Chunk& MyChunk )
{
size_t BeginPos( NowPos - 6 );
Chunk TempChunk;
while( NowPos - BeginPos < MyChunk.Len )
{
ReadChunk( &TempChunk );
switch( TempChunk.ID )
{
case MATERIAL:
LoadMaterial( TempChunk );
break;
case OBJECT:
LoadObject( TempChunk );
break;
default:
SkipNByte( TempChunk.Len - 6 );
}
}
}
void MyLoader::LoadObject( const Chunk& MyChunk )
{
Object object;
object.Name = ReadString();
size_t BeginPos( NowPos - 7 - object.Name.size() );
Chunk ThisChunk;
while( NowPos - BeginPos < MyChunk.Len )
{
ReadChunk( &ThisChunk );
if( OBJECT_MESH == ThisChunk.ID )
LoadMesh( &object, ThisChunk );
else
SkipNByte( ThisChunk.Len - 6 );
}
MyModel.MyObject.push_back( object );
}
void MyLoader::LoadMesh( Object* object ,const Chunk& MyChunk )
{
size_t BeginPos( NowPos - 6 );
size_t End( MyChunk.Len );
Chunk ThisChunk;
while( NowPos - BeginPos < End )
{
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::LoadMaterial( const Chunk& MyChunk )
{
size_t BeginPos( NowPos - 6 );
Chunk TempChunk;
Material Here;
while( NowPos - BeginPos < MyChunk.Len )
{
ReadChunk( &TempChunk );
switch( TempChunk.ID )
{
case MATNAME:
Here.name = ReadString();
break;
case MAT_AMBIENT:
LoadColor( Here.ambientColor );
break;
case MAT_DIFFUSE:
LoadColor( Here.diffuseColor );
break;
case MAT_SPECULAR:
LoadColor( Here.specularColor );
break;
case MAT_SHININESS:
Here.shininess = LoadPercent();
break;
case MAT_TRANSPARENCY:
Here.transparency = LoadPercent();
break;
default:
SkipNByte( TempChunk.Len - 6 );
}
}
MyModel.ThisMaterial.push_back( Here );
}
float MyLoader::LoadPercent()
{
Chunk TempChunk;float Temp( -1 );
ReadChunk( &TempChunk );
switch( TempChunk.ID )
{
case INT_PERCENTAGE:
Temp = ReadNByte( 2 );
Temp /= 100.0;
break;
case FLOAT_PERCENTAGE:
Temp = ReadFloat();
break;
default:
SkipNByte( TempChunk.Len - 6 );
}
return Temp;
}
void MyLoader::LoadColor( float* color )
{
Chunk TempChunk;
ReadChunk( &TempChunk );
switch( TempChunk.ID )
{
case COLOR_F:
color[ 0 ] = ReadFloat();
color[ 1 ] = ReadFloat();
color[ 2 ] = ReadFloat();
break;
case COLOR_24:
color[ 0 ] = static_cast< float >( ReadNByte( 1 ) ) / 255.0;
color[ 1 ] = static_cast< float >( ReadNByte( 1 ) ) / 255.0;
color[ 2 ] = static_cast< float >( ReadNByte( 1 ) ) / 255.0;
break;
default:
SkipNByte( TempChunk.Len - 6 );
}
}
void MyLoader::LoadObjectMaterial( Object* object )
{
string Name = ReadString();
Material* ThisMaterial;
int Pos( -1 );
for( size_t i = 0; i != MyModel.ThisMaterial.size(); ++ i )
{
if( MyModel.ThisMaterial[ i ].name == Name )
Pos = i;
}
if( Pos == -1 )
{
cerr << "Waining: 没有找到该材质" << endl;
return;
}
size_t Sum = ReadNByte( 2 );
size_t Temp( 0 );
for( size_t i = 0; i != Sum; ++ i )
{
Temp = ReadNByte( 2 );
object->Faces[ Temp ].MaterialPos = Pos;
}
}
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;
}
Vertex MyLoader::Normalize( const Vertex& point )
{
float Magnitude = sqrt( point.x * point.x + point.y * point.y + point.z * point.z );
if( Magnitude == 0 )
Magnitude = 1;
Vertex Point( point );
Point.x /= (float)Magnitude;
Point.y /= (float)Magnitude;
Point.z /= (float)Magnitude;
return point;
}
void MyLoader::ComputeNormals()
{
for( size_t i = 0; i != MyModel.MyObject.size(); ++ i )
{
Object& TempObject = MyModel.MyObject[ i ];
for( size_t i = 0; i != TempObject.Faces.size(); ++ i )
{
Vertex Point1 = TempObject.Vertexs[ TempObject.Faces[ i ].Index[ 0 ] ];
Vertex Point2 = TempObject.Vertexs[ TempObject.Faces[ i ].Index[ 1 ] ];
Vertex Point3 = TempObject.Vertexs[ TempObject.Faces[ i ].Index[ 2 ] ];
Vertex Vector1 = Vectors( Point1, Point3 );
Vertex Vector2 = Vectors( Point3, Point2 );
Vertex Normal = Cross( Vector1, Vector2 );
TempObject.Faces[ i ].Normal = Normalize( Normal ); ;
}
}
}
/*
*功能:读取文件
*说明:打开文件前需要加locale::global以保证可以正确打开中文名称文件
*/
void MyLoader::ReadFile( const string& FileRoad )
{
locale::global( locale("") );
ifstream fin( FileRoad.c_str(), ifstream::binary );
locale::global( locale("C") );
if( !fin )
throw std::runtime_error( "打开文件失败" );
char Byte;
while( fin.get( Byte ) )
File += Byte;
fin.close();
FileLength = File.size();
}
/*
*功能:读取一个Chunk
*说明:此处修改了实参
*/
void MyLoader::ReadChunk( Chunk* MyChunk )
{
MyChunk->ID = ReadNByte( 2 );
MyChunk->Len = ReadNByte( 4 );
}
/*
*功能:检测行动会不会超出文件末端
*说明:暂无
*/
void MyLoader::Test( const size_t& Num )
{
if( Num > FileLength - NowPos )
throw std::runtime_error( "读取超出文件末端" );
}
/*
*功能:读取Num个字节转换为无符号数
*说明:由于没有找到合适的流函数,采用如下手段,此处请政府采取更好措施
*/
size_t MyLoader::ReadNByte( const size_t& Num )
{
Test( Num );
size_t Temp( 0 );size_t Next( 0 );
for( size_t i = 1; i <= Num; ++ i )
{
Next = static_cast< unsigned char >( File[ NowPos + Num - i ] );
if( Next > 255 )
Next = 0;
Temp = 256 * Temp + Next;
}
NowPos += Num;
return Temp;
}
/*
*功能:跳过Num个字节
*说明:暂无
*/
void MyLoader::SkipNByte( const size_t& Num )
{
Test( Num );
NowPos += Num;
}
/*
*功能:读取字节直到NULL,并以string返回
*说明:暂无
*/
std::string MyLoader::ReadString()
{
size_t BeginPos( NowPos );
string TempWord;
while( File[ NowPos ] != 0 )
{
TempWord += File[ NowPos ];
++ NowPos;
}
++ NowPos;
return TempWord;
}
/*
*功能:返回模型对象
*说明:此处以引用方式返回,容易造成外值被修改
*/
Model& MyLoader::GetModel()
{
return MyModel;
}
/*
*功能:加载图形所有的面(3ds max是以三角形面存储的)
*说明:面是以顶点索引的形式存储在文件中的,每三个索引是一个面,
* 但两组索引之间有一个两个字节的边界,需要跳过
*/
void MyLoader::LoadFaces( Object* ThisObject )
{
size_t Sum = ReadNByte( 2 );
Face face; size_t Temp( 0 );
for( size_t i = 0; i != Sum; ++ i )
{
for( size_t j = 0; j != 4; ++ j )
{
Temp = ReadNByte( 2 );
if( j < 3 )
face.Index[ j ] = Temp;
}
ThisObject->Faces.push_back( face );
}
}
/*
*功能:读取浮点型数据
*说明:由于没有找到合理的流函数,采用下面的良种手段使浮点数的读取能够
* 正确进行,此处需要政府进行规划
*/
GLfloat MyLoader::ReadFloat( )
{
/*
unsigned char* p = reinterpret