DirectX OBJ model loading and rendering, directxobj Model

Source: Internet
Author: User

DirectX OBJ model loading and rendering, directxobj Model

In the previous DirectX example, the model I used is. x file. DirectX has a method D3DXLoadMeshFromX that can be loaded. x model, but there is a problem here ,. the x file cannot be opened in a text editor to view the structure. Here I will demonstrate how to parse it. obj model.

First, let's take a look. parts and structures of the obj model. A complete obj model consists of three parts: the obj model file, mtl Material file, and texture map; the obj and mtl files can be opened in a text editor. Open the obj file first and you can see the following content:

v -3.000767 2.993211 2.014205v -3.000767 -0.006789 2.014205v -2.750767 2.993211 2.014205v -2.750767 -0.006789 2.014205v -2.750767 2.993211 2.014205v -2.750767 -0.006789 2.014205v -2.750767 2.993211 -1.985795v -2.750767 -0.006789 -1.985795v -2.750767 2.993211 -1.985795vt 0.948633 0.500977vt 0.948633 0.000977vt 0.998633 0.500977vt 0.998633 0.000977vt 0.000000 0.500000vt 0.000000 0.000000vt 1.000000 0.500000vt 1.000000 0.000000vt 1.000000 0.501343vt 0.000000 0.501343vt 1.000000 0.438843vn 0.000000 0.000000 1.000000vn 1.000000 0.000000 0.000000vn 0.000000 0.000000 -1.000000vn -1.000000 0.000000 0.000000vn 0.000000 1.000000 0.000000vn 0.000000 -1.000000 0.000000vn -0.000000 -0.707107 -0.707107vn 0.000000 0.707107 0.707107vn -0.000000 0.707107 -0.707106g Leftusemtl woods 1f 1/1/1 2/2/1 3/3/1f 2/2/1 4/4/1 3/3/1s 2f 5/5/2 6/6/2 7/7/2f 6/6/2 8/8/2 7/7/2s 1f 9/3/3 10/4/3 11/1/3f 10/4/3 12/2/3 11/1/3s 2f 13/7/4 14/8/4 15/5/4f 14/8/4 16/6/4 15/5/4s 3f 17/9/5 18/10/5 19/11/5f 18/10/5 20/12/5 19/11/5f 21/10/6 22/9/6 23/12/6f 22/9/6 24/11/6 23/12/6

Let me explain the field:

First, v and the last three values represent the xyz coordinate values of a vertex;

Vt and the last two or three values represent the texture coordinate uv (w) of the vertex );

Vn and the last three values represent the normal vectors of vertices;

G indicates a group of faces;

Usemtl indicates the name of the material in the mtl file used in this group;

F and the last three sets of values indicate the index values of the three vertices/textures/normal in the preceding v, vt, and vn sets.


Open the mtl file and you will see:

newmtl woodillum 2Kd 0.800000 0.800000 0.800000Ka 0.200000 0.200000 0.200000Ks 0.000000 0.000000 0.000000Ke 0.000000 0.000000 0.000000Ns 0.000000map_Ka house/house.bmpmap_Kd house/house.bmp

Newmtl is followed by the value corresponding to the obj file usemtl;

In order to use only map_Kd in simple code, it indicates the texture name used by the diffuse reflection;

Others are lighting attributes. The default material lighting attributes are used in the code;


To parse the model, first parse the material file and put the material name and texture name into an array, in this way, when parsing obj, you can find the subscript of the corresponding material array by using the material name to find the corresponding texture:

Void MtlObj: getLineNum () {ifstream infile (path. c_str (); // open the specified file string sline; // each row while (getline (infile, sline )) {// read if (sline [0] = 'n' & sline [1] = 'E') from the specified file row by row. // newmtlmtlNum ++ ;} infile. close ();} void MtlObj: readfile () {getLineNum (); names = new string [mtlNum]; textures = new string [mtlNum]; int n = 0; int t = 0; ifstream infile (path. c_str (); // open the string sline of the specified file; // string value, name, texture; while (getline (infile , Sline) {// read if (sline! = "") {Istringstream ins (sline); ins> value; if (value = "newmtl") {ins> name; names [n] = name; n ++;} else if (value = "map_Kd") {ins> texture; textures [t] = texture; t ++ ;}} infile. close () ;}int MtlObj: getIndexByName (string name) {int index =-1; for (int I = 0; I <mtlNum; I ++) {if (names [I] = name) {index = I; break ;}} return index ;}

After preparing the material information, parse the obj file:

Void ModelObj: getLineNum () {ifstream infile (path. c_str (); // open the specified file string sline; // each row while (getline (infile, sline )) {// read if (sline [0] = 'V') {if (sline [1] = 'n') vnNum ++ from a specified file row by row; else if (sline [1] = 'T') vtNum ++; elsevNum ++;} if (sline [0] = 'F') fNum ++ ;} infile. close (); ifstream ifile (path. c_str (); string value, um, group, face; mtArr = new string [fNum]; groupArr = new int [fNum]; groupNum = 0; int fi = 0; while (getline (ifile, sline) {istringstream ins (sline); ins >>value; if (value = "usemtl") {ins> um; int mtlId = mtl-> getIndexByName (um); groupMtlMap. insert (pair <int, int> (groupNum, mtlId);} else if (value = "g") {ins> group; groupNum ++ ;} else if (value = "f") {ins> face; mtArr [fi] = um; groupArr [fi] = groupNum; fi ++;} ifile. close ();}
Find the id of the material in the previous material array by the material name. groupMtlMap is required to save the surface group id and Material id, in this way, you can find the corresponding texture map through the surface group id during rendering.

After obtaining the basic information, read the detailed content of the file:

Void ModelObj: readfile () {getLineNum (); vertices = new NormalTexVertex [fNum * 3]; indices = new int [fNum * 3]; // new two-dimensional array vArr = new float * [vNum]; for (int I = 0; I <vNum; I ++) vArr [I] = new float [3]; vnArr = new float * [vnNum]; for (int I = 0; I <vnNum; I ++) vnArr [I] = new float [3]; vtArr = new float * [vtNum]; for (int I = 0; I <vtNum; I ++) vtArr [I] = new float [3]; fvArr = new int * [fNum]; ftArr = new int * [fNum]; fnArr = new int * [fNum]; for (int I = 0; I <fNum; I ++) {fvArr [I] = new int [3]; ftArr [I] = new int [3]; fnArr [I] = new int [3];} ifstream infile (path. c_str (); string sline; // int ii = 0, tt = 0, jj = 0, kk = 0; std: string s1; float f2, f3, f4; while (getline (infile, sline) {if (sline [0] = 'V') {if (sline [1] = 'n ') {// vnistringstream ins (sline); ins> s1> f2> f3> f4; vnArr [ii] [0] = f2; vnArr [ii] [1] = f3; vnArr [ii] [2] = f4; ii ++;} else if (sline [1] = 'T ') {// vtistringstream ins (sline); ins> S1> f2> f3> f4; vtArr [tt] [0] = f2; vtArr [tt] [1] = 1-f3; vtArr [tt] [2] = f4; tt ++;} else {// vistringstream ins (sline); ins> s1> f2> f3> f4; vArr [jj] [0] = f2; vArr [jj] [1] = f3; vArr [jj] [2] = f4; jj ++ ;}} if (sline [0] = 'F') {// storage plane istringstream in (sline); float a; in> s1; // remove fint I, k; for (I = 0; I <3; I ++) {in> s1; // retrieve the first vertex and normal index a = 0; for (k = 0; s1 [k]! = '/'; K ++) a = a * 10 + (s1 [k]-48); fvArr [kk] [I] = a; a = 0; for (k = k + 1; s1 [k]! = '/'; K ++) a = a * 10 + (s1 [k]-48); ftArr [kk] [I] = a; a = 0; for (k = k + 1; s1 [k]; k ++) a = a * 10 + (s1 [k]-48 ); fnArr [kk] [I] = a;} kk ++;} infile. close ();}
This requires three times the number of faces, because n triangles have n * 3 vertices, because the normal and texture coordinate data may not be shared between vertices, therefore, different Triangles must be separated from each other in the same position;

Because DirectX's texture coordinate axis v is down and the model's v coordinate axis is up, the read texture coordinate v must be 1-v to be correctly rendered by DirectX, so there is a similar practice like vtArr [tt] [1] = 1-f3;

The index pointer is required during rendering. The size is the number of models x 3;


Then assemble triangles through various indexes:

void ModelObj::initTriangles() {for (int i=0;i<fNum;i++) {int v1Index=i*3;int v2Index=i*3+1;int v3Index=i*3+2;vertices[v1Index].x=vArr[fvArr[i][0]-1][0];vertices[v1Index].y=vArr[fvArr[i][0]-1][1];vertices[v1Index].z=vArr[fvArr[i][0]-1][2];vertices[v1Index].nx=vnArr[fnArr[i][0]-1][0];vertices[v1Index].ny=vnArr[fnArr[i][0]-1][1];vertices[v1Index].nz=vnArr[fnArr[i][0]-1][2];vertices[v1Index].u=vtArr[ftArr[i][0]-1][0];vertices[v1Index].v=vtArr[ftArr[i][0]-1][1];vertices[v2Index].x=vArr[fvArr[i][1]-1][0];vertices[v2Index].y=vArr[fvArr[i][1]-1][1];vertices[v2Index].z=vArr[fvArr[i][1]-1][2];vertices[v2Index].nx=vnArr[fnArr[i][1]-1][0];vertices[v2Index].ny=vnArr[fnArr[i][1]-1][1];vertices[v2Index].nz=vnArr[fnArr[i][1]-1][2];vertices[v2Index].u=vtArr[ftArr[i][1]-1][0];vertices[v2Index].v=vtArr[ftArr[i][1]-1][1];vertices[v3Index].x=vArr[fvArr[i][2]-1][0];vertices[v3Index].y=vArr[fvArr[i][2]-1][1];vertices[v3Index].z=vArr[fvArr[i][2]-1][2];vertices[v3Index].nx=vnArr[fnArr[i][2]-1][0];vertices[v3Index].ny=vnArr[fnArr[i][2]-1][1];vertices[v3Index].nz=vnArr[fnArr[i][2]-1][2];vertices[v3Index].u=vtArr[ftArr[i][2]-1][0];vertices[v3Index].v=vtArr[ftArr[i][2]-1][1];indices[i*3]=v1Index;indices[i*3+1]=v2Index;indices[i*3+2]=v3Index;}clearTriangles();}

After the Assembly is complete, clear the memory used to read the file:

void ModelObj::clearTriangles() {for(int i=0;i<vNum;i++)delete[] *(vArr+i);for(int i=0;i<vnNum;i++)delete[] *(vnArr+i);for(int i=0;i<vtNum;i++)delete[] *(vtArr+i);for(int i=0;i<fNum;i++) {delete[] *(fvArr+i);delete[] *(ftArr+i);delete[] *(fnArr+i);}delete[] vArr;delete[] vnArr;delete[] vtArr;delete[] fvArr;delete[] ftArr;delete[] fnArr;delete[] mtArr;}

Create a Grid Object and insert the data into it:

ObjModel::ObjModel(ModelObj* obj) {objLoader=obj;D3DXCreateMeshFVF(objLoader->fNum,objLoader->fNum*3,D3DXMESH_MANAGED,normalTexVertFvf,d3d,&mesh);initVertices();initTextures();DWORD* aAdjacency=new DWORD[objLoader->fNum*3];mesh->GenerateAdjacency(0.001,aAdjacency);mesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT|D3DXMESHOPT_VERTEXCACHE,aAdjacency,NULL,NULL,NULL);delete[] aAdjacency;}void ObjModel::initVertices() {NormalTexVertex* vertices=NULL;mesh->LockVertexBuffer(0,(void**)&vertices);for(int i=0;i<objLoader->fNum*3;i++)vertices[i]=objLoader->vertices[i];mesh->UnlockVertexBuffer();WORD* indices=NULL;mesh->LockIndexBuffer(0,(void**)&indices);for(int i=0;i<objLoader->fNum*3;i++)indices[i]=objLoader->indices[i];mesh->UnlockIndexBuffer();DWORD* attributes=NULL;mesh->LockAttributeBuffer(0,&attributes);for(int i=0;i<objLoader->fNum;i++)attributes[i]=objLoader->groupArr[i]-1;mesh->UnlockAttributeBuffer();}

Then, use the previous material array to create the required texture object:
void ObjModel::initTextures() {objLoader->mtl->getLength(mtlNum);textures=new LPDIRECT3DTEXTURE9[mtlNum];for(int i=0;i<mtlNum;i++) {string texFile=TEX_PATH+objLoader->mtl->textures[i];D3DXCreateTextureFromFile(d3d,texFile.c_str(),&textures[i]);}}

Now the data is ready for rendering:

void ObjModel::render() {for(DWORD i=0;i<(DWORD)objLoader->groupNum;i++) {map<int,int>::iterator itor=objLoader->groupMtlMap.find((int)(i+1));int mtlId=itor->second;d3d->SetTexture(1,textures[mtlId]);mesh->DrawSubset(i);}}


Finally, create and render the object:

void initObjModel() {objLoader=new ModelObj(MODEL_TANK,MTL_TANK);objModel=new ObjModel(objLoader);}void renderObjModel() {objModel->render();}void releaseObjModel() {delete objModel;delete objLoader;}

I imported a tank model and the final effect is as follows:




The model loader code has been written. Click to download it.


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.