Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Huge speed-up for loading meshes with loadObj function. #51

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 29 additions & 22 deletions Library/include/graphics/OpenGLDataStructs.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,12 @@ namespace sf
glm::vec3 pos; //Vertex attrib 0
glm::vec3 normal; //Vertex attrib 1

Vertex()
{
pos = glm::vec3(0.f);
normal = glm::vec3(0.f);
}
Vertex() : pos(0.f), normal(0.f)
{}

friend bool operator==(const Vertex& lhs, const Vertex& rhs)
{
if(lhs.pos == rhs.pos
&& lhs.normal == rhs.normal)
return true;
return false;
return lhs.pos == rhs.pos && lhs.normal == rhs.normal;
};
};

Expand All @@ -139,21 +133,35 @@ namespace sf
glm::vec2 uv; //Vertex attrib 2
glm::vec3 tangent; //Vertex attrib 3

TexturableVertex()
{
uv = glm::vec3(0.f);
tangent = glm::vec3(0.f);
}
TexturableVertex() : Vertex(), uv(0.f), tangent(0.f)
{}

friend bool operator==(const TexturableVertex& lhs, const TexturableVertex& rhs)
{
if(lhs.pos == rhs.pos
&& lhs.normal == rhs.normal
&& lhs.uv == rhs.uv)
return true;
return false;
return lhs.pos == rhs.pos && lhs.normal == rhs.normal
&& lhs.uv == rhs.uv && lhs.tangent == rhs.tangent;
};
};

// Hash function for Vertex
struct VertexHash {
std::size_t operator()(const Vertex& v) const {
std::size_t posHash = std::hash<float>()(v.pos.x) ^ (std::hash<float>()(v.pos.y) << 1) ^ (std::hash<float>()(v.pos.z) << 2);
std::size_t normalHash = std::hash<float>()(v.normal.x) ^ (std::hash<float>()(v.normal.y) << 1) ^ (std::hash<float>()(v.normal.z) << 2);
return posHash ^ (normalHash << 1);
}
};

// Hash function for TexturableVertex
struct TexturableVertexHash {
std::size_t operator()(const TexturableVertex& v) const {
std::size_t posHash = std::hash<float>()(v.pos.x) ^ (std::hash<float>()(v.pos.y) << 1) ^ (std::hash<float>()(v.pos.z) << 2);
std::size_t normalHash = std::hash<float>()(v.normal.x) ^ (std::hash<float>()(v.normal.y) << 1) ^ (std::hash<float>()(v.normal.z) << 2);
std::size_t uvHash = std::hash<float>()(v.uv.x) ^ (std::hash<float>()(v.uv.y) << 1);
std::size_t tangentHash = std::hash<float>()(v.tangent.x) ^ (std::hash<float>()(v.tangent.y) << 1) ^ (std::hash<float>()(v.tangent.z) << 2);
return posHash ^ (normalHash << 1) ^ (uvHash << 2) ^ (tangentHash << 3);
}
};

//! A structure containing single face data.
struct Face
Expand All @@ -162,9 +170,8 @@ namespace sf

friend bool operator==(const Face& lhs, const Face& rhs)
{
if(lhs.vertexID[0] == rhs.vertexID[0] && lhs.vertexID[1] == rhs.vertexID[1] && lhs.vertexID[2] == rhs.vertexID[2])
return true;
return false;
return lhs.vertexID[0] == rhs.vertexID[0] && lhs.vertexID[1] == rhs.vertexID[1]
&& lhs.vertexID[2] == rhs.vertexID[2];
};
};

Expand Down
121 changes: 57 additions & 64 deletions Library/src/utils/GeometryFileUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#include "core/SimulationApp.h"
#include "utils/SystemUtil.hpp"

#include <unordered_map>

namespace sf
{

Expand Down Expand Up @@ -65,7 +67,7 @@ Mesh* LoadOBJ(const std::string& path, GLfloat scale)
std::vector<glm::vec3> normals;
std::vector<glm::vec2> uvs;
Mesh* mesh_ = nullptr;
int64_t start = GetTimeInMicroseconds();
const int64_t start = GetTimeInMicroseconds();

//Read vertices
while(fgets(line, 1024, file))
Expand Down Expand Up @@ -95,9 +97,9 @@ Mesh* LoadOBJ(const std::string& path, GLfloat scale)
}
fseek(file, 0, SEEK_SET); //Go back to beginning of file

size_t genVStart = positions.size();
bool hasNormals = normals.size() > 0;
bool hasUVs = uvs.size() > 0;
const std::size_t genVStart = positions.size();
const bool hasNormals = normals.size() > 0;
const bool hasUVs = uvs.size() > 0;

#ifdef DEBUG
printf("Vertices: %ld Normals: %ld\n", genVStart, normals.size());
Expand All @@ -107,13 +109,9 @@ Mesh* LoadOBJ(const std::string& path, GLfloat scale)
{
TexturableMesh* mesh = new TexturableMesh;

//Initialize positions
for(size_t i=0; i<positions.size(); ++i)
{
TexturableVertex v;
v.pos = positions[i];
mesh->vertices.push_back(v);
}
mesh->vertices.reserve(std::max(positions.size(), normals.size()));

std::unordered_map<TexturableVertex, GLuint, TexturableVertexHash> vertexCache;

//Read faces
while(fgets(line, 1024, file))
Expand All @@ -125,37 +123,36 @@ Mesh* LoadOBJ(const std::string& path, GLfloat scale)
unsigned int vID[3];
unsigned int uvID[3];
unsigned int nID[3];
sscanf(line, "f %u/%u/%u %u/%u/%u %u/%u/%u\n", &vID[0], &uvID[0], &nID[0], &vID[1], &uvID[1], &nID[1], &vID[2], &uvID[2], &nID[2]);

if(hasNormals)
{
sscanf(line, "f %u/%u/%u %u/%u/%u %u/%u/%u\n", &vID[0], &uvID[0], &nID[0], &vID[1], &uvID[1], &nID[1], &vID[2], &uvID[2], &nID[2]);
}
else
{
sscanf(line, "f %u/%u %u/%u %u/%u\n", &vID[0], &uvID[0], &vID[1], &uvID[1], &vID[2], &uvID[2]);
}

for(short unsigned int i=0; i<3; ++i)
{
TexturableVertex v = mesh->vertices[vID[i]-1]; //Vertex from previously read pool

if(glm::length2(v.normal) == 0.f) //Is it a fresh vertex?
TexturableVertex v;
v.pos = positions[vID[i]-1];
v.uv = uvs[uvID[i]-1];
if(hasNormals)
{
mesh->vertices[vID[i]-1].normal = normals[nID[i]-1];
mesh->vertices[vID[i]-1].uv = uvs[uvID[i]-1];
face.vertexID[i] = vID[i]-1;
v.normal = normals[nID[i]-1];
}
else if((v.normal == normals[nID[i]-1]) && (v.uv == uvs[uvID[i]-1])) //Does it have the same normal and UV?

const auto it = vertexCache.find(v);
if(it != vertexCache.end())
{
face.vertexID[i] = vID[i]-1;
}
else //Otherwise search the generated pool
face.vertexID[i] = it->second;
}
else
{
v.normal = normals[nID[i]-1];
v.uv = uvs[uvID[i]-1];

std::vector<TexturableVertex>::iterator it;
if((it = std::find(mesh->vertices.begin()+genVStart, mesh->vertices.end(), v)) != mesh->vertices.end()) //If vertex exists
{
face.vertexID[i] = (GLuint)(it - mesh->vertices.begin());
}
else
{
mesh->vertices.push_back(v);
face.vertexID[i] = (GLuint)mesh->vertices.size()-1;
}
mesh->vertices.push_back(std::move(v));
face.vertexID[i] = (GLuint)mesh->vertices.size() - 1;
vertexCache[v] = face.vertexID[i];
}
}

Expand All @@ -167,12 +164,19 @@ Mesh* LoadOBJ(const std::string& path, GLfloat scale)
{
PlainMesh* mesh = new PlainMesh;

//Initialize positions
for(size_t i=0; i<positions.size(); ++i)
mesh->vertices.reserve(std::max(positions.size(), normals.size()));

std::unordered_map<Vertex, GLuint, VertexHash> vertexCache;

if (!hasNormals)
{
Vertex v;
v.pos = positions[i];
mesh->vertices.push_back(v);
//Initialize positions
for(size_t i=0; i<positions.size(); ++i)
{
Vertex v;
v.pos = positions[i];
mesh->vertices.push_back(std::move(v));
}
}

//Read faces
Expand All @@ -191,31 +195,20 @@ Mesh* LoadOBJ(const std::string& path, GLfloat scale)

for(short unsigned int i=0; i<3; ++i)
{
Vertex v = mesh->vertices[vID[i]-1]; //Vertex from previously read pool

if(glm::length2(v.normal) == 0.f) //Is it a fresh vertex?
{
mesh->vertices[vID[i]-1].normal = normals[nID[i]-1];
face.vertexID[i] = vID[i]-1;
}
else if(v.normal == normals[nID[i]-1]) //Does it have the same normal?
Vertex v;
v.pos = positions[vID[i]-1];
v.normal = normals[nID[i]-1];

const auto it = vertexCache.find(v);
if(it != vertexCache.end())
{
face.vertexID[i] = vID[i]-1;
}
else //Otherwise search the generated pool
face.vertexID[i] = it->second;
}
else
{
v.normal = normals[nID[i]-1];

std::vector<Vertex>::iterator it;
if((it = std::find(mesh->vertices.begin()+genVStart, mesh->vertices.end(), v)) != mesh->vertices.end()) //If vertex exists
{
face.vertexID[i] = (GLuint)(it - mesh->vertices.begin());
}
else
{
mesh->vertices.push_back(v);
face.vertexID[i] = (GLuint)mesh->vertices.size()-1;
}
mesh->vertices.push_back(std::move(v));
face.vertexID[i] = (GLuint)mesh->vertices.size() - 1;
vertexCache[v] = face.vertexID[i];
}
}
}
Expand All @@ -235,7 +228,7 @@ Mesh* LoadOBJ(const std::string& path, GLfloat scale)
}
fclose(file);

int64_t end = GetTimeInMicroseconds();
const int64_t end = GetTimeInMicroseconds();

#ifdef DEBUG
printf("Loaded: %ld Generated: %ld\n", genVStart, mesh_->getNumOfVertices()-genVStart);
Expand Down