【OpenGL C++ UE4】獲取模型頂點及面索引數據,並優化存儲結構供UE4繪製

Github源工程:https://github.com/ColorGalaxy/UE4-Batch-Draw-Mesh-And-OpenGL-Get-Model-Data  

😊覺得贊,記得點Star⭐

目錄

一、功能需求

二、成果

三、環境配置

四、詳細步驟

4.1 Max製作三棱錐並處理

4.2 核心代碼

4.2.1 傳入結構體數據

4.2.2 頂點去重、更新索引

4.2.3 輸出本地CSV文件

4.3 UE4繪製


一、功能需求

想必你肯定會問我一個問題,UE4直接導入模型不好麼?

哈哈,前提是在做畢設時,導師提供的只有頂點與面索引數據,沒有模型。

下文詳細介紹了畢設開發中的難點,涉及三篇其他文章。

【UE4 C++】由點面數據,批量繪製ProceduralMesh並轉化爲StaticMesh資產

後來學習了《LearnOpenGL模型加載》一章後,能夠通過Assimp庫讀取obj模型並在窗口中繪製了。

因此十分好奇,我能否也可以經由C++輸出與畢設相同格式的模型頂點與面片數據集

二、成果

1.輸出的CSV表格在MeshData文件夾下,比如這是個簡單的三棱錐(方便調試查找BUG)輸出的點面數據集。

Vertices
Triangles

2. 3ds max建一個茶壺,通過Opengl窗口繪製顯示,然後輸出點面數據道CSV表格,最後導入UE4中繪製。

3ds max中的茶壺
Opengl窗口繪製——線框顯示

 

UE4繪製的靜態網格體——線框顯示

三、環境配置

我將該項目所需的第三方庫文件GLFWGLADASSIMPstb_image.h均放在了項目中,並設置了相對路徑防止路徑丟失。

若有疑問或想嘗試,也可去LearnOpenGL網址學習自行配置。

四、詳細步驟

4.1 Max製作三棱錐並處理

用自帶的工具欄新建簡單的三棱錐,轉換爲可編輯多邊形,可以看到頂點、邊還是很多。

通過邊刪除、點去除、焊接、邊界封口,去掉多餘的邊、頂點,轉化成一個只包含四個頂點,四個面的簡單三棱錐用於測試。 

導出爲obj,使用VS工程,設置模型路徑,輸出模型的頂點、面索引數據。 

4.2 核心代碼

VS項目文件結構

如何讀取模型,學習《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],因爲要保證三個頂點是順時針順序排列,否則在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)的數據集輸出,不能同時傳入多個模型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章