physx中加载obj创建mesh的方法

6 篇文章 0 订阅
本文详细介绍了如何将OBJ文件中的三角面数据加载到PhysX中,重点讲解了顶点信息和面信息的解析,以及在PhysX中创建PxTriangleMesh的过程。还提供了离线预烹饪方法以提高加载速度,包括加载顶点、构建索引数组、创建和保存cooked mesh数据,以及读取cooked mesh文件直接创建PxTriangleMesh。
摘要由CSDN通过智能技术生成

如何加载三角片面的obj文件到physx中

obj中的会在physx中的重要内容介绍

在obj文件中,physx加载PxTriangleMeshDesc最终要的两个数据,一个是顶点信息,一个是三角片面的三个顶点索引。

相对的obj中,这个顶点信息在obj文件中是每行v开头的后面的数据,分别代表 x,y,z。
例如v 3.419436 0.06810839 -1.137559 ,x= 3.419436, y=0.06810839 , z= -1.137559

面的信息是f打头的数据,数据如下:
f 1598/1598/1598 1597/1597/1597 1599/1599/1599
通过/分隔开的信息分别是顶点索引,贴图顶点索引,法线顶点索引。而我们只需要顶点索引即可。

注意:面的索引都是最小从1开始,所以在放入到physx中的时候,要将顶点数据的0号位置留空。 或者顶点0号位置不留空,但是所有的面索引值要减1。

obj加载方法

objLoader.h文件

#pragma once
#include <vector>
#include <iostream> 
#include <string>
#include <fstream>
#include <sstream>
#include <math.h>
#include <list>

class ObjLoader1
{
public:
	// 几何体顶点v
	struct vertices {
		float x;
		float y;
		float z;
	};
	// 面f
	struct face {
		unsigned int u; //顶点标号
		unsigned int v; // 贴图顶点标号
		unsigned int w;	// 法向量标号
	};
	ObjLoader1(std::string &filename);//读取函数
	~ObjLoader1() {
#if 0
		std::string filename = "test.obj.mesh";
		name += ".mesh";
		FILE * filefd = fopen(name.c_str(), "wb+");
		if (!filefd)
			return;
		int size = v.size();
		fwrite(&size, sizeof(int),1 , filefd);
		fwrite(&v[0], sizeof(struct vertices), size, filefd);

		size = f.size() * 3;;
		fwrite(&size, sizeof(int), 1, filefd);
		auto faceit = f.begin();
		for (int i = 0; i < f.size(); i++)
		{
			for (int j = 0; j < 3; j++) {
				fwrite(&(*faceit)[j].u, sizeof(unsigned int), 1, filefd);
			}
			faceit++;
		}

#endif
		v.clear();
		f.clear();
	}
	//void obj_parse_face(const string& szface, struct face facesrc);

public:
	std::vector<struct vertices> v;//存放顶点(x,y,z)坐标
	std::list<std::vector<struct face>> f;//存放面的三个顶点索引
	std::string name;
};


objLoader.cpp文件


#include <vector>
#include <iostream> 
#include <string>
#include <fstream>
#include <sstream>

#include <math.h>

#include "ObjLoader1.h"

using namespace std;




ObjLoader1::ObjLoader1(string &filename) {
#if 0
	//std::string testname = "test.obj.bak.mesh";
	name = filename;

	FILE * filefd = fopen((name + ".mesh").c_str(), "rb+");
	if (!filefd) {
		goto READ_DATA;
	}
	int size = v.size();
	fread(&size, sizeof(int),1, filefd);
	for (int i = 0; i < size; i++)
	{
		struct vertices vtmp;

		fread(&vtmp, sizeof(struct vertices), 1, filefd);
		v.push_back(vtmp);
		if (i % 10000 == 0)
			std::cout << "i:" << i << std::endl;
	}
	fread(&size, sizeof(int), 1, filefd);

	size /= 3;
	
	
	
	for (int i = 0; i < size; i++)
	{
		std::vector<struct face> vftmp;
		for (int j = 0; j < 3; j++) {
			struct face ftmp;
			fread(&ftmp.u, sizeof(unsigned int), 1, filefd);
			vftmp.push_back(ftmp);
		}
		f.push_back(vftmp);
		if (i % 10000 == 0)
			std::cout << "i:" << i << std::endl;
	}
	return;
#endif
READ_DATA:
	ifstream file(filename);
	string line;
	int count = 0;
	while (getline(file, line))
	{

		if (line.substr(0, 2) == "v ")
		{
			struct vertices vtmp = { 0 };


			count++;


			istringstream s(line.substr(2));
			// v -1.038081 -0.8783139 -2.667491
			s >> vtmp.x >> vtmp.y >> vtmp.z;
			if (vtmp.x == 0 || vtmp.y == 0 || vtmp.z == 0) {
				cout << "0:index:" << count << "line:" << line.substr(2) << endl;;
			}
				
			v.push_back(vtmp);
		}
		else if (line.substr(0, 1) == "f")
		{
			vector<struct face> vface;
		
			count++;

			//vector<struct face> vface;
			istringstream s(line.substr(2));
			string tmpdata[3];
			// f 25/25/25     28/28/28           26/26/26
			s >> tmpdata[0] >> tmpdata[1] >> tmpdata[2];
			for (int i = 0; i < 3; i ++)
			{
				struct face faceTmp;
				istringstream sdata(tmpdata[i]);
				string buffer;
				// sdata里面存储的   25/25/25
				int j = 0;
				while (getline(sdata, buffer, '/'))
				{
					// 得到的u 就是25
					faceTmp.u = atoi(buffer.c_str()) - 1;
					break;
				}
				vface.push_back(faceTmp);
			}
			f.push_back(vface);
		}
		if (count % 10000 == 0)
		{

			cout << "count : " << count << endl;
			count++;
		}
			
		
	}
	file.close();
	cout << "count : " << count << endl;
	cout << "v num: " << v.size() << endl;
	cout << "f num: " << f.size() << endl;
}

在physx中调用obj数据创建PxTriangleMesh

PxTriangleMesh* createOjbMesh(ObjLoader1 *objtmp1, int scale)
{

	const PxU32 numVertices = objtmp1->v.size();
	const PxU32 numTriangles = objtmp1->f.size();

	PxVec3* vertices = new PxVec3[numVertices];
	PxU32* indices = new PxU32[numTriangles * 3];

	// 加载顶点
	for (int i = 0; i < numVertices; ++i) {
		PxVec3 vectmp(objtmp1->v[i].x * scale, objtmp1->v[i].y * scale, objtmp1->v[i].z * scale);
		vertices[i] = vectmp;
	}
	//memcpy(vertices + 1, &objtmp->v[0], sizeof(PxVec3)* (numVertices));

	// 加载面
	auto faceIt = objtmp1->f.begin();
	for (int i = 0; i < numTriangles && faceIt != objtmp1->f.end(); faceIt++, ++i) {
		indices[i * 3 + 0] = (*faceIt)[0].u;
		indices[i * 3 + 1] = (*faceIt)[1].u;
		if ((*faceIt).size() >= 3)
			indices[i * 3 + 2] = (*faceIt)[2].u;
	}

	

	PxTriangleMeshDesc meshDesc1;
	meshDesc1.points.count = numVertices;
	meshDesc1.points.data = vertices;
	meshDesc1.points.stride = sizeof(PxVec3);

	meshDesc1.triangles.count = numTriangles;
	meshDesc1.triangles.data = indices;
	meshDesc1.triangles.stride = sizeof(PxU32) * 3;


	

	PxTriangleMesh* triMesh = gCooking->createTriangleMesh(meshDesc, gPhysics->getPhysicsInsertionCallback());
	
	return triMesh;
}

创建出mesh的actor

PxRigidActor* createObjMeshs(std::string name, PxVec3 &vec, int scale)
{

	ObjLoader1 objtmp1(name);
	PxTriangleMesh* mesh = createOjbMesh(&objtmp1, scale);

	// 创建出它的几何体
	PxTriangleMeshGeometry geom(mesh);
	// 创建网格面
	PxRigidStatic* TriangleMesh = gPhysics->createRigidStatic(PxTransform(vec));


	// 创建三角网格形状
	PxShape* shape = gPhysics->createShape(geom, *gMaterial);

	{
		// 设置厚度, 相当于多了一层 0.03厚的皮肤,也就是为了提前预判
		shape->setContactOffset(0.03f);
		// A negative rest offset helps to avoid jittering when the deformed mesh moves away from objects resting on it.
		// 允许穿透的厚度,当穿透指定的厚度后,就是发生弹开等动作 -0.02f 负数代表穿透后,正数代表穿透前
		shape->setRestOffset(-0.02f);
	}

	TriangleMesh->attachShape(*shape);
	shape->release();


	

	TriangleMesh->userData = new int;
	int testid = 88888888;
	memcpy(TriangleMesh->userData, &testid, sizeof(int));
	
	gScene->addActor(*TriangleMesh);

	

	return TriangleMesh;
}


加速加载cooking的方法

在进行创建mesh的时候,cooking是非常慢的,此时官方提供了一种离线的方式,就是提前将mesh给cooking出来,并将cooking好的数据写入到文件中,游戏中需要用到这个mesh的时候,直接去读取这个cooking文件,用这个数据创建mesh就好了,这个少了cooking的步骤,速度非常的快。

方法如下:

int writeMeshCookingFile(ObjLoader1 *objtmp1, int scale)
{
	if(!objtmp1)
		return -1;
	const PxU32 numVertices = objtmp1->v.size();
	const PxU32 numTriangles = objtmp1->f.size();

	PxVec3* vertices = new PxVec3[numVertices];
	PxU32* indices = new PxU32[numTriangles * 3];

	// 加载顶点
	for (int i = 0; i < numVertices; ++i) {
		PxVec3 vectmp(objtmp1->v[i].x * scale, objtmp1->v[i].y * scale, objtmp1->v[i].z * scale);
		vertices[i] = vectmp;
	}
	//memcpy(vertices + 1, &objtmp->v[0], sizeof(PxVec3)* (numVertices));

	// 加载面
	auto faceIt = objtmp1->f.begin();
	for (int i = 0; i < numTriangles && faceIt != objtmp1->f.end(); faceIt++, ++i) {
		indices[i * 3 + 0] = (*faceIt)[0].u;
		indices[i * 3 + 1] = (*faceIt)[1].u;
		if ((*faceIt).size() >= 3)
			indices[i * 3 + 2] = (*faceIt)[2].u;
	}


	PxTriangleMeshDesc meshDesc1;
	meshDesc1.points.count = numVertices;
	meshDesc1.points.data = vertices;
	meshDesc1.points.stride = sizeof(PxVec3);

	meshDesc1.triangles.count = numTriangles;
	meshDesc1.triangles.data = indices;
	meshDesc1.triangles.stride = sizeof(PxU32) * 3;



	PxDefaultMemoryOutputStream writeBuffer;
	PxTriangleMeshCookingResult::Enum result;
	bool status = gCooking->cookTriangleMesh(meshDesc, writeBuffer, &result);
	if (!status)
		return -1;

	delete[] vertices;
	delete[] indices;

	// 将流写入到文件
	FILE * filefd = fopen((objtmp->name + ".cooking").c_str(), "wb+");
	if (!filefd) {
		cout << "open cooking write fail" << endl;
		return -1;
	}

	int size = writeBuffer.getSize();
	fwrite(&size, sizeof(unsigned int), 1, filefd);

	fwrite(writeBuffer.getData(), sizeof(PxU8), size, filefd);
	fclose(filefd);
	return 0;
}

PxTriangleMesh * readCookingFile(std::string filename){
	// 读出流
	FILE * filefd = fopen((objtmp->name + ".cooking").c_str(), "rb+");
	if (!filefd) {
		cout << "open cooking read fail" << endl;
		return NULL;
	}
	int rsize = 0;
	fread(&rsize, sizeof(unsigned int), 1, filefd);
	std::cout << "rsize:" << rsize << std::endl;
	PxU8 *filebuff = new PxU8[rsize + 1];
	fread(filebuff, sizeof(PxU8), rsize, filefd);

	PxDefaultMemoryInputData readBuffer(filebuff, rsize);
	PxTriangleMesh * triangle = gPhysics->createTriangleMesh(readBuffer);
	delete[] filebuff;
	return triangle;


}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值