《基于MFC的OpenGL编程》Part 18 Reading objects from the OBJ File Format

《基于MFC的OpenGL编程》Part 18 Reading objects from the OBJ File Format

本文将介绍如何从Obj文件格式中创建3D对象,我们使用的是Nate Millerobj格式加载类。

This would be very useful to create large Virtual Reality applications as we could make use of the readily available 3D model files or make use of modeling tools to create these models and load them instead of creating them programatically. The .obj format is a very simple and popular format and files of other types such 3D Studio (.3ds) can be exported to this format or converted using tools such as 3D Exploration. This .obj loading code cannot read textures, it can only also read .mtl files in addition to the .obj file and thus make use of material data too.

1, Nate Millerobj文件加载类,其完整源代码可以从http://www.pobox.com/~ndr处下载。

《基于MFC的OpenGL编程》Part 18 Reading objects from the OBJ File Format《基于MFC的OpenGL编程》Part 18 Reading objects from the OBJ File FormatGlm头文件
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->/*
*Wavefront.objfileformatreader.
*
*author:NateRobins
*email:[email protected]
*www:
http://www.pobox.com/~ndr
*/
/*includes*/
#include
<GL/glut.h>
#ifndefM_PI
#defineM_PI3.14159265
#endif
/*defines*/
#defineGLM_NONE(0)/*renderwithonlyvertices*/
#defineGLM_FLAT(1<<0)/*renderwithfacetnormals*/
#defineGLM_SMOOTH(1<<1)/*renderwithvertexnormals*/
#defineGLM_TEXTURE(1<<2)/*renderwithtexturecoords*/
#defineGLM_COLOR(1<<3)/*renderwithcolors*/
#defineGLM_MATERIAL(1<<4)/*renderwithmaterials*/
/*structs*/

/*GLMmaterial:Structurethatdefinesamaterialinamodel.
*/
typedef
struct_GLMmaterial
{
char*name;/*nameofmaterial*/
GLfloatdiffuse[
4];/*diffusecomponent*/
GLfloatambient[
4];/*ambientcomponent*/
GLfloatspecular[
4];/*specularcomponent*/
GLfloatemmissive[
4];/*emmissivecomponent*/
GLfloatshininess;
/*specularexponent*/
}GLMmaterial;

/*GLMtriangle:Structurethatdefinesatriangleinamodel.
*/
typedef
struct{
GLuintvindices[
3];/*arrayoftrianglevertexindices*/
GLuintnindices[
3];/*arrayoftrianglenormalindices*/
GLuinttindices[
3];/*arrayoftriangletexcoordindices*/
GLuintfindex;
/*indexoftrianglefacetnormal*/
}GLMtriangle;

/*GLMgroup:Structurethatdefinesagroupinamodel.
*/
typedef
struct_GLMgroup{
char*name;/*nameofthisgroup*/
GLuintnumtriangles;
/*numberoftrianglesinthisgroup*/
GLuint
*triangles;/*arrayoftriangleindices*/
GLuintmaterial;
/*indextomaterialforgroup*/
struct_GLMgroup*next;/*pointertonextgroupinmodel*/
}GLMgroup;

/*GLMmodel:Structurethatdefinesamodel.
*/
typedef
struct{
char*pathname;/*pathtothismodel*/
char*mtllibname;/*nameofthemateriallibrary*/
GLuintnumvertices;
/*numberofverticesinmodel*/
GLfloat
*vertices;/*arrayofvertices*/
GLuintnumnormals;
/*numberofnormalsinmodel*/
GLfloat
*normals;/*arrayofnormals*/
GLuintnumtexcoords;
/*numberoftexcoordsinmodel*/
GLfloat
*texcoords;/*arrayoftexturecoordinates*/
GLuintnumfacetnorms;
/*numberoffacetnormsinmodel*/
GLfloat
*facetnorms;/*arrayoffacetnorms*/
GLuintnumtriangles;
/*numberoftrianglesinmodel*/
GLMtriangle
*triangles;/*arrayoftriangles*/
GLuintnummaterials;
/*numberofmaterialsinmodel*/
GLMmaterial
*materials;/*arrayofmaterials*/
GLuintnumgroups;
/*numberofgroupsinmodel*/
GLMgroup
*groups;/*linkedlistofgroups*/
GLfloatposition[
3];/*positionofthemodel*/
}GLMmodel;


/*publicfunctions*/

/*glmUnitize:"unitize"amodelbytranslatingittotheoriginand
*scalingittofitinaunitcubearoundtheorigin.Returnsthe
*scalefactorused.
*
*model-properlyinitializedGLMmodelstructure
*/
GLfloat
glmUnitize(GLMmodel
*model);

/*glmDimensions:Calculatesthedimensions(width,height,depth)of
*amodel.
*
*model-initializedGLMmodelstructure
*dimensions-arrayof3GLfloats(GLfloatdimensions[3])
*/
GLvoid
glmDimensions(GLMmodel
*model,GLfloat*dimensions);

/*glmScale:Scalesamodelbyagivenamount.
*
*model-properlyinitializedGLMmodelstructure
*scale-scalefactor(0.5=halfaslarge,2.0=twiceaslarge)
*/
GLvoid
glmScale(GLMmodel
*model,GLfloatscale);

/*glmReverseWinding:Reversethepolygonwindingforallpolygonsin
*thismodel.Defaultwindingiscounter-clockwise.Alsochanges
*thedirectionofthenormals.
*
*model-properlyinitializedGLMmodelstructure
*/
GLvoid
glmReverseWinding(GLMmodel
*model);

/*glmFacetNormals:Generatesfacetnormalsforamodel(bytakingthe
*crossproductofthetwovectorsderivedfromthesidesofeach
*triangle).Assumesacounter-clockwisewinding.
*
*model-initializedGLMmodelstructure
*/
GLvoid
glmFacetNormals(GLMmodel
*model);

/*glmVertexNormals:Generatessmoothvertexnormalsforamodel.
*Firstbuildsalistofallthetriangleseachvertexisin.Then
*loopsthrougheachvertexinthethelistaveragingallthefacet
*normalsofthetriangleseachvertexisin.Finally,setsthe
*normalindexinthetriangleforthevertextothegeneratedsmooth
*normal.Ifthedotproductofafacetnormalandthefacetnormal
*associatedwiththefirsttriangleinthelistoftrianglesthe
*currentvertexisinisgreaterthanthecosineoftheangle
*parametertothefunction,thatfacetnormalisnotaddedintothe
*averagenormalcalculationandthecorrespondingvertexisgiven
*thefacetnormal.Thistendstopreservehardedges.Theangleto
*usedependsonthemodel,but90degreesisusuallyagoodstart.
*
*model-initializedGLMmodelstructure
*angle-maximumangle(indegrees)tosmoothacross
*/
GLvoid
glmVertexNormals(GLMmodel
*model,GLfloatangle);

/*glmLinearTexture:Generatestexturecoordinatesaccordingtoa
*linearprojectionofthetexturemap.Itgeneratestheseby
*linearlymappingtheverticesontoasquare.
*
*model-pointertoinitializedGLMmodelstructure
*/
GLvoid
glmLinearTexture(GLMmodel
*model);

/*glmSpheremapTexture:Generatestexturecoordinatesaccordingtoa
*sphericalprojectionofthetexturemap.Sometimesreferredtoas
*spheremap,orreflectionmaptexturecoordinates.Itgenerates
*thesebyusingthenormaltocalculatewherethatvertexwouldmap
*ontoasphere.Sinceitisimpossibletomapsomethingflat
*perfectlyontosomethingspherical,thereisdistortionatthe
*poles.ThisparticularimplementationcausesthepolesalongtheX
*axistobedistorted.
*
*model-pointertoinitializedGLMmodelstructure
*/
GLvoid
glmSpheremapTexture(GLMmodel
*model);

/*glmDelete:DeletesaGLMmodelstructure.
*
*model-initializedGLMmodelstructure
*/
GLvoid
glmDelete(GLMmodel
*model);

/*glmReadOBJ:ReadsamodeldescriptionfromaWavefront.OBJfile.
*Returnsapointertothecreatedobjectwhichshouldbefree'dwith
*glmDelete().
*
*filename-nameofthefilecontainingtheWavefront.OBJformatdata.
*/
GLMmodel
*
glmReadOBJ(
char*filename);

/*glmWriteOBJ:WritesamodeldescriptioninWavefront.OBJformatto
*afile.
*
*model-initializedGLMmodelstructure
*filename-nameofthefiletowritetheWavefront.OBJformatdatato
*mode-abitwiseorofvaluesdescribingwhatiswrittentothefile
*GLM_NONE-writeonlyvertices
*GLM_FLAT-writefacetnormals
*GLM_SMOOTH-writevertexnormals
*GLM_TEXTURE-writetexturecoords
*GLM_FLATandGLM_SMOOTHshouldnotbothbespecified.
*/
GLvoid
glmWriteOBJ(GLMmodel
*model,char*filename,GLuintmode);

/*glmDraw:RendersthemodeltothecurrentOpenGLcontextusingthe
*modespecified.
*
*model-initializedGLMmodelstructure
*mode-abitwiseORofvaluesdescribingwhatistoberendered.
*GLM_NONE-renderwithonlyvertices
*GLM_FLAT-renderwithfacetnormals
*GLM_SMOOTH-renderwithvertexnormals
*GLM_TEXTURE-renderwithtexturecoords
*GLM_FLATandGLM_SMOOTHshouldnotbothbespecified.
*/
GLvoid
glmDraw(GLMmodel
*model,GLuintmode);

/*glmList:Generatesandreturnsadisplaylistforthemodelusing
*themodespecified.
*
*model-initializedGLMmodelstructure
*mode-abitwiseORofvaluesdescribingwhatistoberendered.
*GLM_NONE-renderwithonlyvertices
*GLM_FLAT-renderwithfacetnormals
*GLM_SMOOTH-renderwithvertexnormals
*GLM_TEXTURE-renderwithtexturecoords
*GLM_FLATandGLM_SMOOTHshouldnotbothbespecified.
*/
GLuint
glmList(GLMmodel
*model,GLuintmode);

/*glmWeld:eliminate(weld)vectorsthatarewithinanepsilonof
*eachother.
*
*model-initializedGLMmodelstructure
*epsilon-maximumdifferencebetweenvertices
*(0.00001isagoodstartforaunitizedmodel)
*
*/
GLvoid
glmWeld(GLMmodel
*model,GLfloatepsilon);

2, 在第17篇的基础上,CCY457OpenGLView类中加入下述变量,用来表示不同物体类型

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->GLuintm_MonitorList;//显示器
GLuintm_ChairList;//椅子
GLuintm_PotList;//花瓶
GLuintm_ComputerList;//计算机
intm_nObjectNo;

2, InitializeOpenGL函数中加入对LoadModelsFromFiles的调用

3, 绘制函数修改如下:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->voidCCY457OpenGLView::RenderScene()
{
//绘制函数
//PositionCamera
gluLookAt(m_PosX,m_PosY,m_PosZ,m_DirX,m_DirY,m_DirZ,0.0f,1.0f,0.0f);
//DrawtheScene
//Drawthefloor
//Drawtheground,wedomanualshadingtoadarkergreen
//inthebackgroundtogivetheillusionofdepth
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,m_Texture[
3]);
glBegin(GL_POLYGON);
glColor3ub(
0,255,0);
glTexCoord2f(
0.0f,0.0f);
glVertex3f(
-2.0f,0.0f,0.0f);
glTexCoord2f(
1.0f,0.0f);
glVertex3f(
2.0f,0.0f,0.0f);
glColor3ub(
0,100,0);
glTexCoord2f(
1.0f,1.0f);
glVertex3f(
2.0f,0.0f,-2.0f);
glTexCoord2f(
0.0f,1.0f);
glVertex3f(
-2.0f,0.0f,-2.0f);
glEnd();
glDisable(GL_TEXTURE_2D);
//DrawtheCube
//Savethematrixstateanddotherotations
glPushMatrix();
glTranslatef(
-1.0f,0.6f,-1.0f);
//Drawjetatneworientation,putlightincorrectposition
//beforerotatingthejet
glRotatef(m_xRot,1.0f,0.0f,0.0f);
glRotatef(m_yRot,
0.0f,1.0f,0.0f);
DrawCube(FALSE);
//Restoreoriginalmatrixstate
glPopMatrix();
//Getreadytodrawtheshadowandtheground
//Firstdisablelightingandsavetheprojectionstate
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glPushMatrix();
//Multiplybyshadowprojectionmatrix
glMultMatrixf((GLfloat*)m_ShadowMat);
glTranslatef(
-1.0f,0.6f,-1.0f);
glRotatef(m_xRot,
1.0f,0.0f,0.0f);
glRotatef(m_yRot,
0.0f,1.0f,0.0f);
//Passtruetoindicatedrawingshadow
DrawCube(TRUE);
//Restoretheprojectiontonormal
glPopMatrix();
//Restorelightingstatevariables
glEnable(GL_DEPTH_TEST);
//Drawthelightsource
glPushMatrix();
glTranslatef(
1.5f,1.5f,-1.0f);
glColor3ub(
255,255,0);
glutSolidSphere(
0.01f,10,10);
glPopMatrix();
glEnable(GL_LIGHTING);
}
voidCCY457OpenGLView::DrawCube(BOOLbShadow)
{
//Setmaterialcolor,noteweonlyhavetosettoblack
//fortheshadowonce
if(!bShadow)
{
switch(m_nObjectNo)
{
case0:glCallList(m_ChairList);
break;
case1:glCallList(m_PotList);
break;
case2:glCallList(m_ComputerList);
break;
case3:glCallList(m_MonitorList);
break;
case4:DrawCubeTex();
break;
}
}
else
{
glColor3ub(
0,0,0);
switch(m_nObjectNo)
{
case0:glCallList(m_ChairList);
break;
case1:glCallList(m_PotList);
break;
case2:glCallList(m_ComputerList);
break;
case3:glCallList(m_MonitorList);
break;
case4:DrawCubeNoTex();
break;
}
}
}

4, 加载Obj文件的具体实现代码:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->//LoadalltheModelsfromtheFilesoftype.obj
voidCCY457OpenGLView::LoadModelsFromFiles()
{
GLfloatscalefactor
=0.0;
//LoadComputerfromfile
GLMmodel*object1;
object1
=glmReadOBJ("models/computer.obj");
if(!scalefactor)
{
scalefactor
=glmUnitize(object1);
}
else
{
glmScale(object1,scalefactor);
}
glmScale(object1,
2.5);
/*buildadisplaylist*/
m_ComputerList
=glmList(object1,GLM_SMOOTH);
/*nukeit,wedon'tneeditanymore*/
glmDelete(object1);
//LoadChairFromFile
GLMmodel*object2;
scalefactor
=0.0;
object2
=glmReadOBJ("models/chair04.obj");
if(!scalefactor)
{
scalefactor
=glmUnitize(object2);
}
else
{
glmScale(object2,scalefactor);
}
glmScale(object2,
5.0);
/*buildadisplaylist*/
m_ChairList
=glmList(object2,GLM_SMOOTH);
/*nukeit,wedon'tneeditanymore*/
glmDelete(object2);
//LoadMonitorfromfile
GLMmodel*object5;
scalefactor
=0.0;
object5
=glmReadOBJ("models/samsung.obj");
if(!scalefactor)
{
scalefactor
=glmUnitize(object5);
}
else
{
glmScale(object5,scalefactor);
}
glmScale(object5,
0.5);
/*buildadisplaylist*/
m_MonitorList
=glmList(object5,GLM_SMOOTH);
/*nukeit,wedon'tneeditanymore*/
glmDelete(object5);
//LoadPhoneObjectfromfile
GLMmodel*object6;
scalefactor
=0.0;
object6
=glmReadOBJ("models/plant2.obj");
if(!scalefactor)
{
scalefactor
=glmUnitize(object6);
}
else
{
glmScale(object6,scalefactor);
}
glmScale(object6,
0.5);
/*buildadisplaylist*/
m_PotList
=glmList(object6,GLM_SMOOTH);
/*nukeit,wedon'tneeditanymore*/
glmDelete(object6);
}