如何加载三角片面的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;
}