opengl读取obj模型3

第三版本相较于前两个版本,添加了对 f 1//1 类型的支持(面 由点坐标和法向量组成),以及对mtl文件的读取。

准备工作,准备一个结构体来保存材质信息,然后由vector保存即可

struct mMaterial
{
	float Ns;     //shinness
	float Ka[3];
	float Kd[3];
	float Ks[3];
	string mTextureName;
	GLuint mTextureId;
};

ka,kd,ks即相应的环境光,漫反射及镜面反射的参数,mTextureName保存着该材质的名称,由mtl文件读入,mTextureId即纹理贴图。

objModel类新加入一个成员变量mtlName,用于存储材质名称,方便从vector中查询材质信息。


class objModel :public List
{
public:
	objModel() {}
	void loadObjModel(const char*);
	void print();
	void objDraw();
private:
	vector normal, texcoord, position;     //vector容器存储法线,纹理坐标及点的位置信息
	vector face;
	int vertnum;
	int coordnum;
	int nornum;
	std::string mtlName;
};

修改loadObjModel函数,使其能够对mtl文件进行读写。定义一个全局变量来存储mtl信息,std::vector mMtl;。

CreateTexture函数自己实现就好了。

void objModel::loadObjModel(const char* objFileName)
{
	int nFileSize = 0;
	unsigned char* fileContent = LoadFileContent(objFileName, nFileSize);    //读取文件内容
	if (fileContent == nullptr)     //文件为空
	{
		return;
	}
	objModel* tempModel = new objModel;
	stringstream ssFileContent((char*)fileContent);   //流读取文件内容
	string temp;       //接受无关信息
	char szoneLine[256];        //读取一行的数据
	while (!ssFileContent.eof())
	{
		memset(szoneLine, 0, 256);        //  每次循环初始化数组szoneLine
		ssFileContent.getline(szoneLine, 256);      //流读取一行

		if (strlen(szoneLine) > 0)       //该行不为空
		{
			if (szoneLine[0] == 'v')     //v开头的数据
			{
				stringstream ssOneLine(szoneLine);        //数据存储到流中 方便赋值
				if (szoneLine[1] == 't')       //纹理信息
				{
					ssOneLine >> temp;     //接受标识符 vt
					Float3 tempTexcoord;
					ssOneLine >> tempTexcoord.Data[0] >> tempTexcoord.Data[1];   //数据存入临时变量中
					tempModel->texcoord.push_back(tempTexcoord);         //存入容器
				}
				if (szoneLine[1] == 'n')            //法线信息
				{
					ssOneLine >> temp;      //接收标识符vn
					Float3 tempNormal;
					ssOneLine >> tempNormal.Data[0] >> tempNormal.Data[1] >> tempNormal.Data[2];
					tempModel->normal.push_back(tempNormal);
				}
				else                          //点的位置信息
				{
					ssOneLine >> temp;
					Float3 tempLocation;
					ssOneLine >> tempLocation.Data[0] >> tempLocation.Data[1] >> tempLocation.Data[2];
					tempModel->position.push_back(tempLocation);
				}
			}
			else if (szoneLine[0] == 'f')          //面信息
			{
				stringstream ssOneLine(szoneLine);     //流读取一行数据
				ssOneLine >> temp; //接收标识符f
				//    f信息    exp: f 1/1/1 2/2/2 3/3/3      位置索引/纹理索引/法线索引   三角面片 三个点构成一个面
				string vertexStr;   //接收流的内容
				Face tempFace;
				for (int i = 0; i < 3; ++i)         //每个面三个点
				{
					ssOneLine >> vertexStr;           //从流中读取点的索引信息
					size_t pos = vertexStr.find_first_of('/');       //找到第一个/的位置      //即找到点的位置信息
					string locIndexStr = vertexStr.substr(0, pos);       //赋值点的位置信息
					size_t pos2 = vertexStr.find_first_of('/', pos + 1);   //找到第二个/   即找到点的纹理坐标信息
					if (pos2 - pos == 1)
					{
						string norIndexStr = vertexStr.substr(pos2 + 1, vertexStr.length() - pos2 - 1);
						tempFace.vertex[i][0] = atoi(locIndexStr.c_str());        //将索引信息从 srting转换为 int     //位置索引信息赋值
						tempFace.vertex[i][2] = atoi(norIndexStr.c_str());
						tempModel->coordnum = 0;
					}
					else
					{
						string texIndexSrt = vertexStr.substr(pos + 1, pos2 - 1 - pos);       //赋值点的纹理坐标信息
						string norIndexSrt = vertexStr.substr(pos2 + 1, vertexStr.length() - 1 - pos2);   //赋值点的法线信息
						tempFace.vertex[i][0] = atoi(locIndexStr.c_str());        //将索引信息从 srting转换为 int     //位置索引信息赋值
						tempFace.vertex[i][1] = atoi(texIndexSrt.c_str());         //纹理坐标索引信息赋值
						tempFace.vertex[i][2] = atoi(norIndexSrt.c_str());         //法线信息赋值
					}
				}
				tempModel->face.push_back(tempFace);
			}
			else if (szoneLine[0] == '#')             //处理注释内容
			{
				stringstream ssOneLine(szoneLine);     //流读取一行数据
				ssOneLine >> temp;        //接收#
				string numtemp;
				while (ssOneLine)
				{
					ssOneLine >> temp;
					if (temp == "faces")
					{
						PushBack(tempModel);
						tempModel = new objModel;
						break;
					}
					else if (temp == "vertices")
					{
						tempModel->vertnum = atoi(numtemp.c_str());
						break;
					}
					else if (temp == "texture")
					{
						tempModel->coordnum = atoi(numtemp.c_str());
						break;
					}
					else if (temp == "vertex")
					{
						tempModel->nornum = atoi(numtemp.c_str());
						break;
					}
					numtemp = temp;

				}
			}
			else if (szoneLine[0] == 'u')
			{
				stringstream ssOneLine(szoneLine);
				ssOneLine >> temp >> temp;  //temp接收材质名
				tempModel->mtlName = temp;
			}
			else if (szoneLine[0] == 'm')       //处理材质库
			{
				mMaterial *tempMtl=new mMaterial;
				stringstream ssOneLine(szoneLine);
				ssOneLine >> temp;      //接受mtllib
				ssOneLine >> temp;      //接收材质名	
				
				string fileName = "Res/" + temp;
				const char* mtlFileName = fileName.c_str();
				fstream mtlFile;
				mtlFile.open(mtlFileName);
				if (mtlFile.fail())
					cout << "mtl file open fail" << std::endl;
				
				while (!mtlFile.eof())
				{
					string temp;
					mtlFile >> temp;
					if (temp == "newmtl")
					{
						mtlFile >> tempMtl->mTextureName;
					}
					else if (temp == "Ns")
					{
						mtlFile >> tempMtl->Ns;
					}
					else if (temp == "Ka")
					{
						mtlFile >> tempMtl->Ka[0] >> tempMtl->Ka[1] >> tempMtl->Ka[2];
					}
					else if (temp == "Kd")
					{
						mtlFile >> tempMtl->Kd[0] >> tempMtl->Kd[1] >> tempMtl->Kd[2];
					}
					else if (temp == "Ks")
					{
						mtlFile >> tempMtl->Ks[0] >> tempMtl->Ks[1] >> tempMtl->Ks[2];
					}
					else if (temp == "map_Kd")
					{
						mtlFile >> temp;
						size_t pos = temp.find_last_of('\\');
						temp = temp.substr(pos + 1, temp.length() - 1 - pos);
						string tempF = "Res/" + temp;
						const char* texFile = tempF.c_str();
						tempMtl->mTextureId = CreateTexture(texFile);
						mMtl.push_back(*tempMtl);
						tempMtl = new mMaterial;
					}
				}
			}
		}//end 该行非空
	}//end while
	delete tempModel;
	delete fileContent;
}

对照obj文件,及mtl文件,代码很容易理解了,不多赘述。

opengl读取obj模型3_第1张图片

需要注意的就是 代码中的 string fileName = "Res/" + temp;    。因为在我的项目中,所有的资源文件都保存在了Res文件下,所以我文件前缀为Res/,大家在使用代码的时候,根据需求自行修改即可,也可以将其作为变量传入函数,随时修改前缀即可。

下面进行objDraw的修改,使其能够处理材质信息。

void objModel::objDraw()
{
	glEnable(GL_TEXTURE_2D);
	if (position.size() != 0)
	{
		
		if (mtlName.length()!=0)
		{
			for (auto i = mMtl.begin(); i < mMtl.end(); ++i)
			{
				if (i->mTextureName == mtlName)
				{
					glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, i->Ka);
					glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, i->Kd);
					glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, i->Ks);
					glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, i->Ns);
					glBindTexture(GL_TEXTURE_2D, i->mTextureId);
					break;
				}
			}
			
		}
		glBegin(GL_TRIANGLES);
		if (coordnum == 0)
		{
			for (unsigned int i = 0; i < face.size(); ++i)
			{
				//第一个点的法线,位置信息
				glNormal3fv(normal[face[i].vertex[0][2] - 1 - nnum].Data);
				glVertex3fv(position[face[i].vertex[0][0] - 1 - vnum].Data);
				//第二个点的法线,位置信息
				glNormal3fv(normal[face[i].vertex[1][2] - 1 - nnum].Data);
				glVertex3fv(position[face[i].vertex[1][0] - 1 - vnum].Data);
				//第三个点的法线,位置信息
				glNormal3fv(normal[face[i].vertex[2][2] - 1 - nnum].Data);
				glVertex3fv(position[face[i].vertex[2][0] - 1 - vnum].Data);
			}
		}
		else
		{
			for (unsigned int i = 0; i < face.size(); ++i)         //循环遍历face信息
			{

				//第一个点的法线,纹理,位置信息
				glNormal3fv(normal[face[i].vertex[0][2] - 1 - nnum].Data);
				glTexCoord2fv(texcoord[face[i].vertex[0][1] - 1 - cnum].Data);
				glVertex3fv(position[face[i].vertex[0][0] - 1 - vnum].Data);
				//第二个点的法线,纹理,位置信息
				glNormal3fv(normal[face[i].vertex[1][2] - 1 - nnum].Data);
				glTexCoord2fv(texcoord[face[i].vertex[1][1] - 1 - cnum].Data);
				glVertex3fv(position[face[i].vertex[1][0] - 1 - vnum].Data);
				//第三个点的法线,纹理,位置信息
				glNormal3fv(normal[face[i].vertex[2][2] - 1 - nnum].Data);
				glTexCoord2fv(texcoord[face[i].vertex[2][1] - 1 - cnum].Data);
				glVertex3fv(position[face[i].vertex[2][0] - 1 - vnum].Data);
			}
		}
		glEnd();
	}
	vnum += vertnum;
	cnum += coordnum;
	nnum += nornum;//cout << "end model" << std::endl;
	if (Next() != nullptr)
	{
		Next()->objDraw();
	}
	else
		return;
}

相当简单的修改,从vector中查找该材质信息,进行设置即可。

效果图:

opengl读取obj模型3_第2张图片

没办法,建模功底差的要死,所以效果图很惨。但是代码是没问题的

你可能感兴趣的:(opengl读取obj模型3)