我已经尝试了一个星期,试图找到一个合适的哈希函数来避免.obj文件中的重复顶点,但没有成功。我不知道如何在结构中循环(参见源代码(并获得正确的索引来索引无序映射。我不知道这是否是最好的方法,我很乐意接受任何有效的解决方案。提前感谢您的帮助。我们将不胜感激!!
源代码:
结构:
struct Face
{
struct VertexConfig
{
int i[3];
};
int numConfigs = 0;
VertexConfig configs[4];
};
类别:
class MeshResource
{
public:
vector<float> vertices;
vector<unsigned int> indices;
vector<float> normals;
vector<float> textureIndex;
std::shared_ptr<Material> material;
GLuint slot;
GLint components ;
GLenum type;
GLsizei stride , offset ;
GLboolean normalized = GL_FALSE;
GLuint numIndices, vao;
MeshResource() = default;
MeshResource(vector<float> vertices, vector<unsigned int> indices);
// TODO: Call cleanup from destructor
void SetupQuad();
void Draw();
void DrawMesh2();
void CreateCube(float width, float height, float depth);
void Cleanup();
unsigned int VAO, VBO, EBO;
void ParseIntoFloat(string line, std::vector<float>& vector);
void ParseIntoFloat2(string line, std::vector<float>& vector);
void LoadMeshData(const char* FileName);
void ParseFace(string line, int pos, std::vector<Face>& faces, bool& hasNormals, bool& hasUvs);
void Bind();
void SetupMesh();
private:
};
.obj解析器:
void MeshResource::LoadMeshData(const char* FileName)
{
std::ifstream file(FileName);
std::string line;
std::vector<float> vp;
std::vector<float> vt;
std::vector<float> vn;
std::vector<Face> faces;
bool hasNormals = false;
bool hasUvs = false;
while (std::getline(file, line))
{
//check for vertices
if (line.substr(0, 2) == "v ")
ParseIntoFloat(line, vp);
//check for texture
else if (line.substr(0, 2) == "vt")
ParseIntoFloat2(line, vt);
//check for Normals
else if (line.substr(0, 2) == "vn")
ParseIntoFloat(line, vn);
//check for faces
else if (line.substr(0, 2) == "f ")
MeshResource::ParseFace(line, 2, faces, hasNormals, hasUvs);
}
std::unordered_map<int, int> map;
int currentIndex = 0;
const int step = 1 + (int)hasUvs + (int)hasNormals;
const int posLength = vp.size();
const int uvLength = vt.size();
int counter = 0;
for (int i = 0; i < faces.size(); i++ )
{
counter++;
for (int j = 0; j < faces[i].numConfigs; j++)
{
int posIndex = i+j;
int uvIndex = i+j + 1;
int normalIndex = i+j + (int)hasUvs + 1;
int index = vertices.size() / 8;
int baseIndex_Position = (faces[i].configs[j].i[0] - 1) * 3;
vertices.push_back(vp[baseIndex_Position]);
vertices.push_back(vp[baseIndex_Position + 1]);
vertices.push_back(vp[baseIndex_Position + 2]);
int baseindex_Normals = (faces[i].configs[j].i[2] -1) * 3;
vertices.push_back(vn[baseindex_Normals]);
vertices.push_back(vn[baseindex_Normals + 1]);
vertices.push_back(vn[baseindex_Normals + 2]);
int baseIndex_Texture = (faces[i].configs[j].i[1] - 1) * 2;
vertices.push_back(vt[baseIndex_Texture]);
vertices.push_back(vt[baseIndex_Texture + 1]);
indices.push_back(currentIndex);
if (j == 2)
{
auto temp = indices[currentIndex];
indices[currentIndex] = indices[currentIndex-1];
indices[currentIndex - 1] = temp;
}
currentIndex++;
}
}
SetupMesh();
}
void MeshResource::ParseFace(string line, int pos, std::vector<Face>& faces, bool &hasNormals, bool &hasUvs)
{
Face f;
bool eol = false;
while (!eol)
{
bool eow = false;
pos = line.find(" ");
if (line[0] == 'f')
{
line = line.substr(line.find(" ") + 1);
continue;
}
pos = line.find(" ");
eol = pos == -1;
if (eol && f.numConfigs>=3)
break;
string word = line.substr(0,pos);
int counter = 0;
bool isOnLast = false;
for (size_t i = 0; i < 3; i++)
{
isOnLast = false;
int slashIndex = word.find('/');
bool notCurrent = false;
eow = slashIndex == -1;
string number;
if (eow)
{
slashIndex = word.length() - 1;
number = word.substr(0);
isOnLast = true;
}
else
{
number = word.substr(0,slashIndex);
word = word.substr(slashIndex+1);
if (slashIndex == 0)
notCurrent = true;
}
counter++;
if (slashIndex !=-1 && !notCurrent)
{
if (counter == 1)
f.configs[f.numConfigs].i[0] = std::stoi(number);
else if (counter == 2)
{
f.configs[f.numConfigs].i[1] = std::stoi(number);
hasNormals = true;
}
else if (counter == 3 && word.length() > 0)
{
f.configs[f.numConfigs].i[2] = std::stoi(number);
hasUvs = true;
}
}
if (isOnLast)
break;
}
f.numConfigs++;
pos = line.find(" ");
line = line.substr(pos + 1);
}
faces.push_back(f);
}
如果你错过了任何代码,请告诉我,我会提供的!再次感谢!/Filip
首先,要使用struct
来保存属于单个顶点的所有坐标。您可以创建自己的struct Vertex
,但我强烈建议使用GLM库的glm::vec3f
(假设它们是三维坐标(。这个库还提供了std::hash
的特殊化,因此您可以简单地创建一个从顶点到索引的无序映射,如下所示:
std::unordered_map<glm::vec3f, int> vertex_indices;
如果你想知道GLM是如何创建哈希专用化的,这样你就可以自己尝试,看看它的源代码。
因此,一旦你解析了一个顶点,你就可以检查它是否已经在地图中,或者插入它:
int index;
glm::vec3f vertex;
...
if (auto it = vertex_indices.find(vertex); it != vertex_indices.end()) {
// vertex already found
...
} else {
vertex_indices[vertex] = index;
}
现在的诀窍是:如果顶点已经存在,该怎么办?您需要一些方法来将OBJ文件中f行中的索引重新映射到已消除重复的索引集。您需要更多的数据结构来处理这个问题。
std::vector<vertex> vertices;
std::vector<int> deduplicated_indices;
std::unordered_map<glm::vec3f, int> vertex_indices;
int index = 0;
glm::vec3f vertex;
while (std::getline(...)) {
vertex = parseVertex(line);
if (auto it = vertex_indices.find(vertex); it != vertex_indices.end()) {
// vertex already found, map the file's index to our deduplicated index
deduplicated_indices.push_back(it->second);
} else {
// remember this vertex
int our_index = vertices.size();
vertices.push_back(vertex);
deduplicated_indices.push_back(our_index);
vertex_indices[vertex] = our_index;
}
}
之后,您将获得已消除重复的顶点的矢量,矢量deduplicated_indices
将从OBJ文件中的索引映射到vertices
中的索引。这样,当你读人脸时:
int configs[3];
// read indices from OBJ file into configs[]
...
// convert those indices to deduplicate indices
for (size_t i = 0; i < 3; ++i) {
f.configs[i] = deduplicated_indices[configs[i]];
}