Github源工程:https://github.com/ColorGalaxy/UE4-Batch-Draw-Mesh-And-OpenGL-Get-Model-Data
😊觉得赞,记得点Star⭐
目录
一、功能需求
想必你肯定会问我一个问题,UE4直接导入模型不好么?
哈哈,前提是在做毕设时,导师提供的只有顶点与面索引数据,没有模型。
下文详细介绍了毕设开发中的难点,涉及三篇其他文章。
后来学习了《LearnOpenGL模型加载》一章后,能够通过Assimp库读取obj模型并在窗口中绘制了。
因此十分好奇,我能否也可以经由C++输出与毕设相同格式的模型顶点与面片数据集。
二、成果
1.输出的CSV表格在MeshData文件夹下,比如这是个简单的三棱锥(方便调试查找BUG)输出的点面数据集。
2. 3ds max建一个茶壶,通过Opengl窗口绘制显示,然后输出点面数据道CSV表格,最后导入UE4中绘制。
三、环境配置
我将该项目所需的第三方库文件GLFW、GLAD、ASSIMP、stb_image.h均放在了项目中,并设置了相对路径防止路径丢失。
若有疑问或想尝试,也可去LearnOpenGL网址学习自行配置。
四、详细步骤
4.1 Max制作三棱锥并处理
用自带的工具栏新建简单的三棱锥,转换为可编辑多边形,可以看到顶点、边还是很多。
通过边删除、点去除、焊接、边界封口,去掉多余的边、顶点,转化成一个只包含四个顶点,四个面的简单三棱锥用于测试。
导出为obj,使用VS工程,设置模型路径,输出模型的顶点、面索引数据。
4.2 核心代码
如何读取模型,学习《LearnOpenGL模型加载》,此处不再赘述,主要对重复顶点数据去除、索引更新、打印CSV做说明。
4.2.1 传入结构体数据
Debug模式可以看到三棱锥的点面数据,有很多顶点是重复的,并且与面索引是一一对应的。
UE4中使用Procedural Mesh绘制只需要传入不同的顶点,因此简化数据,进行顶点去重、更新索引。
在Mesh.h的构造器中,将Vertex结构体中的position传入到MeshInfoCSV.h文件中进行处理,面索引是自动递增的,直接传入i值,然后调用Export函数打印。
struct Vertex//Assimp读数据的结构体
{
glm::vec3 position;
glm::vec3 normal;
glm::vec2 texCoords;
};
struct outData//自定义进行去重操作的结构体
{
float verticesPos_X;
float verticesPos_Y;
float verticesPos_Z;
unsigned int index;
unsigned int initialIndex;
};
Mesh(vector<Vertex> vertices, vector<unsigned int> indices, vector<Texture> textures)
{
this->vertices = vertices;
this->indices = indices;
this->textures = textures;
#ifdef EXPORT_MODEL_DATA
vector<outData> allInfo;
outData info;
for (int i = 0; i < vertices.size(); i++)
{
info.verticesPos_X = vertices[i].position.x;
info.verticesPos_Y = vertices[i].position.y;
info.verticesPos_Z = vertices[i].position.z;
info.index = i;
info.initialIndex = i;
allInfo.push_back(info);
}
MeshInfoCSV exportTxt(allInfo);
exportTxt.ExportVertices();
exportTxt.ExportTriangles();
#endif
setupMesh();
}
4.2.2 顶点去重、更新索引
思想:将所有的顶点坐标从小到大进行排序。面索引更新的原则是遍历所有顶点结构体成员时,若当前项与后一项顶点重复,allInfo结构体中去除后一项,并根据记录后一项的初始面索引,找到需要更新的新索引数组的下标,更新其值为当前项的顶点序号;若后一项不重复,就将顶点序号增加,再更新后一项的新索引。
最终得到的结果就是不同的顶点坐标结构体allInfo与更新的面索引数组indices。
MeshInfoCSV(vector<outData> allInfo)
{
this->allInfo = allInfo;
//initial indices
for (int i = 0; i < allInfo.size(); i++)
this->indices.push_back(i);
OptimizeData();
}
//remove duplicate vertex and modify indices
void OptimizeData()
{
//sort by position's value for remove the duplicate index
sort(allInfo.begin(), allInfo.end(), cmpByPosition);
//compare with previous data
//将重复的顶点去掉,更新面片的索引下标为不重复的顶点下标(内存优化)
if (allInfo.size() > 1)
{
int newIndex = 0;
allInfo[0].index = newIndex;
indices[allInfo[0].initialIndex] = newIndex;
int len = allInfo.size();
for (int i = 0; i < len - 1; i++)
{
if (allInfo[newIndex].verticesPos_X == allInfo[newIndex + 1].verticesPos_X
&& allInfo[newIndex].verticesPos_Y == allInfo[newIndex + 1].verticesPos_Y
&& allInfo[newIndex].verticesPos_Z == allInfo[newIndex + 1].verticesPos_Z)
{//update repetitive vertex's indexa
allInfo[newIndex + 1].index = newIndex;
indices[allInfo[newIndex + 1].initialIndex] = newIndex;
allInfo.erase(allInfo.begin() + newIndex + 1);
}
else
{
newIndex++;
indices[allInfo[newIndex].initialIndex] = newIndex;
allInfo[newIndex].index = newIndex;
}
}
}
}
4.2.3 输出本地CSV文件
fopen会提示不安全,要用fopen_s打开文件,使用w+模式可以覆盖原文件内容。
void ExportVertices()
{
int i;
FILE *fp;
//please keep close excel when print to file
fopen_s(&fp,"MeshData/OpenglVertices.csv", "w+");//model has one mesh
fprintf(fp, ",MeshID,Vertice_X,Vertice_Y,Vertice_Z\n");
for (i = 0; i < allInfo.size(); i++)
{
fprintf(fp, "%d,1,%.2f,%.2f,%.2f\n", i+1,allInfo[i].verticesPos_X, allInfo[i].verticesPos_Y, allInfo[i].verticesPos_Z);
}
fprintf(fp, "%d,0,0,0,0\n",i+1);
fclose(fp);
cout << "Vertices successfully print to csv excel!" << endl;
}
打印面索引的顺序必须是indices[i], indices[i+2], indices[i+1],因为OpenGL在渲染图元的时候,逆时针顶点所定义的三角形将会被处理为正向三角形;在UE4中是顺时针顺序排列才是正向的,否则会造成三棱锥外表面不可见,内表面可见的结果。
void ExportTriangles()
{
int i;
FILE *fp;
fopen_s(&fp, "MeshData/OpenglTriangles.csv", "w+");//model has one mesh
fprintf(fp, ",MeshID,Vertice1,Vertice2,Vertice3\n");
for (i = 0; i < indices.size(); i+=3)
{
//Vertex order must be clockwise in ue4 draw
fprintf(fp, "%d,1,%d,%d,%d\n", i/3 + 1, indices[i], indices[i+2], indices[i+1]);
}
fprintf(fp, "%d,0,0,0,0\n", i/3 + 1);
fclose(fp);
cout << "Triangles successfully print to csv excel!" << endl;
}
4.3 UE4绘制
得到顶点、三角面索引CSV文件后,结合该文章【UE4 C++】由点面数据,批量绘制ProceduralMesh并转化为StaticMesh资产就能在UE4中绘制得到模型了。目前该VS项目仅适用于单个模型(只包含一个Mesh)的数据集输出,不能同时传入多个模型。