3dmax에서 모델링한 후 여러가지 포맷으로 익스포트하여 OpenGL에서 사용가능하게 적절히 쓸 수 있다.
아래코드는 .ASE 파일포맷으로 익스포트된 모델정보를 로드하여 관리하는 코드이다.
기본적으로 VERTEX_LIST, FACE_LIST를 가져오고 텍스쳐가 있다면 UVLIST(TVERTLIST)와 TFACELIST도 읽어와 텍스쳐매핑을 한다.
노멀이나 컬러는 아래코드만 이해하면 금방 읽어올수 있을것이다. 추후 업데이트할지도?
사실 Tface list와 Tvertex list정보로 새로운 Vertex list를 만드는데 상당히 애먹었는데,,
Tface list의 인덱스의 새로운 버텍스가 Face list의 인덱스의 기존 버텍스값을 가진다는.. 간단한 원리이다.
(이때 새로운 버텍스는 최적화고 뭐고 그냥 NUMTVERTEX만큼이면 된다.)
그릴때는 텍스쳐가 있다면 변환된 새로운 Vertex list를 GL_VERTEX_ARRAY로 지정해주고, 인덱스는 TFace list를 사용하면 된다.
#pragma once
#include <fstream>
#include <GL/glut.h>
#include <GL/GLU.H>
#include <GL/GL.H>
#include <GL/glaux.h>
#include <string.h>
#include <vector>
using namespace std;
struct MeshVertex{
GLfloat x;
GLfloat y;
GLfloat z;
};
struct MeshIndex{
GLuint a;
GLuint b;
GLuint c;
};
struct MeshUV{
GLfloat u;
GLfloat v;
};
class C3DMMaterial{
public:
C3DMMaterial(){
texrec = NULL;
texid = NULL;
}
~C3DMMaterial(){}
void SetTextureID(GLuint* TexID){
texid = TexID;
}
bool LoadBmp(const char* FileName){
if( texid == NULL )
return false;
WCHAR wbuf[256];
MultiByteToWideChar(CP_ACP, NULL, FileName, -1, wbuf, strlen(FileName)+1);
texrec = auxDIBImageLoad( wbuf );
if( texrec == NULL )
return false;
glBindTexture(GL_TEXTURE_2D, *texid);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0,3, texrec->sizeX, texrec->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texrec->data);
return true;
}
void BindMaterial(){
glBindTexture(GL_TEXTURE_2D, *texid);
}
private:
AUX_RGBImageRec* texrec;
GLuint* texid;
};
//3dmax geometry object
class C3DMGeomObject{
public:
C3DMGeomObject(){
meshNumVertex = meshNumFace = meshNumTvertex = 0;
meshVertexList = NULL;
meshFaceList = NULL;
meshUVList = NULL;
meshTfaceList = NULL;
meshCvtVertexList = NULL;
material_ref = -1;
}
~C3DMGeomObject(){
ClearGeomObject();
}
void InitMeshVertexList(int numVertex, MeshVertex* VertexList){
meshNumVertex = numVertex;
meshVertexList = VertexList;
}
void InitMeshFaceList(int numFace, MeshIndex* FaceList){
meshNumFace = numFace;
meshFaceList = FaceList;
}
void InitMeshTexList(int numTvert, MeshUV* TvertList, MeshIndex* TfaceList){
meshNumTvertex = numTvert;
meshUVList = TvertList;
meshTfaceList = TfaceList;
//converting
meshCvtVertexList = new MeshVertex[meshNumTvertex];
memset(meshCvtVertexList, 0, sizeof(MeshVertex)*meshNumTvertex);
for(int i=0; i<meshNumFace; i++){
meshCvtVertexList[ meshTfaceList[i].a ] = meshVertexList[ meshFaceList[i].a ];
meshCvtVertexList[ meshTfaceList[i].b ] = meshVertexList[ meshFaceList[i].b ];
meshCvtVertexList[ meshTfaceList[i].c ] = meshVertexList[ meshFaceList[i].c ];
}
//텍스쳐 on/off를 위해 기존 버텍스어레이는 삭제하지 않음
}
void DrawGeomObject(C3DMMaterial* Material = NULL){
if( Material != NULL ){
glEnable(GL_TEXTURE_2D);
Material->BindMaterial();
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, meshUVList);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, meshCvtVertexList);
glDrawElements(GL_TRIANGLES, meshNumFace*3, GL_UNSIGNED_INT, meshTfaceList);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
}
else{
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, meshVertexList);
glDrawElements(GL_TRIANGLES, meshNumFace*3, GL_UNSIGNED_INT, meshFaceList);
glDisableClientState(GL_VERTEX_ARRAY);
}
}
GLint GetMaterialRef(){
return material_ref;
}
void SetMaterialRef(GLint refNo){
material_ref = refNo;
}
void ClearGeomObject(){
if( meshVertexList != NULL )
delete [] meshVertexList;
if( meshFaceList != NULL )
delete [] meshFaceList;
if( meshUVList != NULL )
delete [] meshUVList;
if( meshTfaceList != NULL )
delete [] meshTfaceList;
if( meshCvtVertexList != NULL )
delete [] meshCvtVertexList;
}
private:
int meshNumVertex;
int meshNumFace;
int meshNumTvertex;
MeshVertex* meshVertexList;
MeshIndex* meshFaceList;
MeshUV* meshUVList; //tvertex
MeshIndex* meshTfaceList;
MeshVertex* meshCvtVertexList; //converted vertex list
GLint material_ref;
};
class C3DMObject{
public:
C3DMObject(){
material_count = 0;
material_list = NULL;
texid_arr = NULL;
geomObjs.clear();
}
~C3DMObject(){
if( material_list != NULL )
delete [] material_list;
if( texid_arr != NULL )
delete [] texid_arr;
for(vector<C3DMGeomObject*>::iterator itr = geomObjs.begin(); itr != geomObjs.end(); itr++){
delete (*itr);
}
geomObjs.clear();
}
//파일 형식이 잘못되었을 경우는 고려하지 않음
bool ObjectFromASE(const char* FileName){
ifstream fin(FileName);
if( !fin.is_open() )
return false;
char linebuf[BUFSIZ];
char dummy[128];
while( !fin.eof() ){
fin.getline(linebuf, BUFSIZ);
sscanf(linebuf, "%s", dummy);
if( !strcmp(dummy, "*MATERIAL_LIST") ){
parseMATERIAL_LIST(fin);
}
else if( !strcmp(dummy, "*GEOMOBJECT") ){
parseGEOMOBJECT(fin);
}
}
return true;
}
void DrawObject(){
for(vector<C3DMGeomObject*>::iterator itr = geomObjs.begin(); itr != geomObjs.end(); itr++){
(*itr)->DrawGeomObject( getMaterial((*itr)->GetMaterialRef()) );
}
}
private:
bool parseMATERIAL_LIST(ifstream& fin){
char linebuf[BUFSIZ];
char dummy[128];
while( !fin.eof() ){
fin.getline(linebuf, BUFSIZ);
sscanf(linebuf, "%s", dummy);
//in *MATERIAL_LIST
if( !strcmp(dummy, "*MATERIAL_COUNT") ){
sscanf(linebuf, "%s %d", dummy, &material_count); //*MATERIAL_COUNT [count]
if( material_count == 0 )
break;//end *MATERIAL_LIST
texid_arr = new GLuint[material_count];
glGenTextures(material_count, texid_arr); //generate textures
material_list = new C3DMMaterial[material_count]; //create material list
}
else if( !strcmp(dummy, "*MATERIAL") ){
int mat_no = 0;
sscanf(linebuf, "%s %d", dummy, &mat_no); //*MATERIAL [No.]
while( !fin.eof() ){
fin.getline(linebuf, BUFSIZ);
sscanf(linebuf, "%s", dummy);
if( !strcmp(dummy, "*BITMAP") ){
sscanf(linebuf, "%s %s", dummy, dummy );//scan again
_splitpath(dummy, NULL, NULL, dummy, NULL );
strcat(dummy, ".bmp");
material_list[mat_no].SetTextureID(&texid_arr[mat_no]);
if( material_list[mat_no].LoadBmp(dummy) == false ){
return false;
}
break;//end *MATERIAL
}
}
}
else if( !strcmp(linebuf, "}") ){
break;//end *MATERIAL_LIST
}
}
return true;
}
bool parseGEOMOBJECT(ifstream& fin){
char linebuf[BUFSIZ];
char dummy[128];
C3DMGeomObject* tmpGeomObj = new C3DMGeomObject();
while( !fin.eof() ){
fin.getline(linebuf, BUFSIZ);
sscanf(linebuf, "%s", dummy);
if( !strcmp(dummy, "*MESH") ){
int meshNumVertex = 0;
MeshVertex* VertexList = NULL;
int meshNumFace = 0;
MeshIndex* FaceList = NULL;
int meshNumTvert = 0;
MeshUV* TvertexList = NULL;
MeshIndex* TfaceList = NULL;
while( !fin.eof() ){
fin.getline(linebuf, BUFSIZ);
sscanf(linebuf, "%s", dummy);
if( !strcmp(dummy, "*MESH_NUMVERTEX") ){
sscanf(linebuf, "%s %d", dummy, &meshNumVertex);
}
else if( !strcmp(dummy, "*MESH_NUMFACES") ){
sscanf(linebuf, "%s %d", dummy, &meshNumFace);
}
else if( !strcmp(dummy, "*MESH_VERTEX_LIST") ){
VertexList = new MeshVertex[meshNumVertex];
memset(VertexList, 0, sizeof(MeshVertex)*meshNumVertex);
for(int i=0; i<meshNumVertex; i++){
fin.getline(linebuf, BUFSIZ);
//*MESH_VERTEX [index] [x] [y] [z]
sscanf(linebuf, "%s %s %f %f %f", dummy, dummy, &VertexList[i].x, &VertexList[i].y, &VertexList[i].z);
}
tmpGeomObj->InitMeshVertexList(meshNumVertex, VertexList);
}
else if( !strcmp(dummy, "*MESH_FACE_LIST") ){
FaceList = new MeshIndex[meshNumFace];
memset(FaceList, 0, sizeof(MeshIndex)*meshNumFace);
for(int i=0; i<meshNumFace; i++){
fin.getline(linebuf, BUFSIZ);
//*MESH_FACE [index]: A: [a] B:[b] C: [c] ...
sscanf(linebuf, "%s %s %s%d %s%d %s%d", dummy, dummy, dummy,&FaceList[i].a, dummy,&FaceList[i].b, dummy,&FaceList[i].c);
}
tmpGeomObj->InitMeshFaceList(meshNumFace, FaceList);
}
else if( !strcmp(dummy, "*MESH_NUMTVERTEX") ){
sscanf(linebuf, "%s %d", dummy, &meshNumTvert);
}
else if( !strcmp(dummy, "*MESH_TVERTLIST") ){
TvertexList = new MeshUV[meshNumTvert];
memset(TvertexList, 0, sizeof(MeshUV)*meshNumTvert);
for(int i=0; i<meshNumTvert; i++){
fin.getline(linebuf, BUFSIZ);
//*MESH_TVERT [index] [u] [v] [?]
sscanf(linebuf, "%s %s %f %f", dummy, dummy, &TvertexList[i].u, &TvertexList[i].v);
}
}
else if( !strcmp(dummy, "*MESH_NUMTVFACES") ){
int tmpTvFace = 0;
sscanf(linebuf, "%s %d", dummy, &tmpTvFace);
if( tmpTvFace != meshNumFace )
return false; //invalid file contents
}
else if( !strcmp(dummy, "*MESH_TFACELIST") ){
TfaceList = new MeshIndex[meshNumFace];
memset(TfaceList, 0, sizeof(MeshIndex)*meshNumFace);
for(int i=0; i<meshNumFace; i++){
fin.getline(linebuf, BUFSIZ);
//*MESH_TFACE [index] [tvert a] [tvert b] [tvert c]
sscanf(linebuf, "%s %s %d %d %d", dummy, dummy, &TfaceList[i].a, &TfaceList[i].b, &TfaceList[i].c);
}
tmpGeomObj->InitMeshTexList(meshNumTvert, TvertexList, TfaceList);
}
else if( !strcmp(linebuf, "\t}") ){
break;
}
}
}//end *MESH
else if( !strcmp(dummy, "*MATERIAL_REF") ){
int refno = -1;
sscanf(linebuf, "%s %d", dummy, &refno);
tmpGeomObj->SetMaterialRef(refno);
}
else if( !strcmp(linebuf, "}") ){
break;
}
}
geomObjs.push_back(tmpGeomObj);
return true;
}
C3DMMaterial* getMaterial(GLint refNo){
if( refNo < 0 || refNo >= material_count )
return NULL;
return &material_list[refNo];
}
private:
GLint material_count;
C3DMMaterial* material_list;
GLuint* texid_arr;
vector<C3DMGeomObject*> geomObjs; //지오메트리는 몇개인지 안알려줘서 벡터로 처리
};
ps. 3dmax의 submaterial? 과 텍스쳐가 아닌 material에 대한 처리는 어떻게해야할지 분석중..
ps2. 주석에도 있지만,, 파일형식 잘못되었을 때 등 크리티컬한 에러처리를 전혀 하지않음. 그냥 공부용으로만 사용
'Graphics Note' 카테고리의 다른 글
[Mathematics] quaternion(사원수) (1) | 2013.07.10 |
---|---|
[Mathematics] matrices for transformation (0) | 2013.07.09 |
[Mathematics] complex number (5) | 2013.07.01 |
유리같은 셰이딩 (0) | 2013.06.25 |
Directx DXUT Library build & include (0) | 2013.03.30 |