268 lines
7.0 KiB
C++
268 lines
7.0 KiB
C++
// Adapted from https://raw.githubusercontent.com/tamato/simple-obj-loader/master/objloader.cpp
|
|
#include "objloader.hpp"
|
|
|
|
#include <yolo/yolo.hpp>
|
|
|
|
#include <iostream>
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#include <cstring>
|
|
#include <assert.h>
|
|
#include <map>
|
|
|
|
namespace inferno::scene {
|
|
|
|
struct FaceVert
|
|
{
|
|
FaceVert() : vert(-1), norm(-1), coord(-1) {}
|
|
|
|
int vert;
|
|
int norm;
|
|
int coord;
|
|
};
|
|
|
|
struct vert_less {
|
|
bool operator() (const FaceVert& lhs, const FaceVert& rhs) const
|
|
{
|
|
// handle any size mesh
|
|
if (lhs.vert != rhs.vert) return (lhs.vert<rhs.vert);
|
|
if (lhs.norm != rhs.norm) return (lhs.norm<rhs.norm);
|
|
if (lhs.coord!=rhs.coord) return (lhs.coord<rhs.coord);
|
|
return false;
|
|
// the following breaks down on very large meshes
|
|
// const unsigned long prime1 = 73856093;
|
|
// const unsigned long prime2 = 19349663;
|
|
// const unsigned long prime3 = 83492791;
|
|
// unsigned long lh = (lhs.vert * prime1) ^ (lhs.norm * prime2) ^ (lhs.coord * prime3);
|
|
// unsigned long rh = (rhs.vert * prime1) ^ (rhs.norm * prime2) ^ (rhs.coord * prime3);
|
|
// return lh < rh;
|
|
}
|
|
};
|
|
|
|
ObjLoader::ObjLoader()
|
|
: mTexCoordLayers(1)
|
|
{
|
|
|
|
}
|
|
|
|
ObjLoader::~ObjLoader()
|
|
{
|
|
|
|
}
|
|
|
|
void ObjLoader::load(std::filesystem::path file)
|
|
{
|
|
if (!std::filesystem::exists(file))
|
|
{
|
|
yolo::error("OBJ File does not exist at ", file.string());
|
|
return;
|
|
}
|
|
|
|
std::ifstream inf;
|
|
inf.open(file.c_str(), std::ios_base::in);
|
|
if (!inf.is_open())
|
|
{
|
|
yolo::error("Failed to open OBJ file ", file.string());
|
|
return;
|
|
}
|
|
|
|
mPositions.clear();
|
|
mNormals.clear();
|
|
mTexCoords.clear();
|
|
mFaces.clear();
|
|
|
|
char *delims = " \n\r";
|
|
const unsigned int CHARACTER_COUNT = 500;
|
|
char line[CHARACTER_COUNT] = {0};
|
|
|
|
std::vector<glm::vec3> verts;
|
|
std::vector<glm::vec3> norms;
|
|
std::vector<glm::vec2> texcoords;
|
|
|
|
std::map<FaceVert, int, vert_less> uniqueverts;
|
|
unsigned int vert_count = 0;
|
|
|
|
while (inf.good())
|
|
{
|
|
memset( (void*)line, 0, CHARACTER_COUNT);
|
|
inf.getline(line, CHARACTER_COUNT);
|
|
if (inf.eof()) break;
|
|
|
|
char *token = strtok(line, delims);
|
|
if (token == NULL || token[0] == '#' || token[0] == '$')
|
|
continue;
|
|
|
|
// verts look like:
|
|
// v float float float
|
|
if (strcmp(token, "v") == 0)
|
|
{
|
|
float x=0, y=0, z=0, w=1;
|
|
sscanf(line+2, "%f %f %f %f", &x, &y, &z, &w);
|
|
verts.push_back( glm::vec3(x/w,y/w,z/w) );
|
|
}
|
|
// normals:
|
|
// nv float float float
|
|
else if (strcmp(token, "vn") == 0) {
|
|
|
|
float x=0, y=0, z=0;
|
|
sscanf(line+3, "%f %f %f", &x, &y, &z);
|
|
norms.push_back( glm::vec3(x,y,z) );
|
|
}
|
|
// texcoords:
|
|
// vt float float
|
|
else if (strcmp(token, "vt") == 0)
|
|
{
|
|
float x=0, y=0, z=0;
|
|
sscanf(line+3, "%f %f %f", &x, &y, &z);
|
|
texcoords.push_back( glm::vec2(x, y) );
|
|
}
|
|
|
|
// keep track of smoothing groups
|
|
// s [number|off]
|
|
else if (strcmp(token, "s") == 0)
|
|
{
|
|
|
|
}
|
|
|
|
// faces start with:
|
|
// f
|
|
else if (strcmp(token, "f") == 0)
|
|
{
|
|
|
|
std::vector<int> vindices;
|
|
std::vector<int> nindices;
|
|
std::vector<int> tindices;
|
|
|
|
// fill out a triangle from the line, it could have 3 or 4 edges
|
|
char *lineptr = line + 2;
|
|
while (lineptr[0] != 0) {
|
|
while (lineptr[0] == ' ') ++lineptr;
|
|
|
|
int vi=0, ni=0, ti=0;
|
|
if (sscanf(lineptr, "%d/%d/%d", &vi, &ni, &ti) == 3) {
|
|
vindices.push_back(vi-1);
|
|
nindices.push_back(ni-1);
|
|
tindices.push_back(ti-1);
|
|
}
|
|
else
|
|
if (sscanf(lineptr, "%d//%d", &vi, &ni) == 2) {
|
|
vindices.push_back(vi-1);
|
|
nindices.push_back(ni-1);
|
|
}
|
|
else
|
|
if (sscanf(lineptr, "%d/%d", &vi, &ti) == 2) {
|
|
vindices.push_back(vi-1);
|
|
tindices.push_back(ti-1);
|
|
}
|
|
else
|
|
if (sscanf(lineptr, "%d", &vi) == 1) {
|
|
vindices.push_back(vi-1);
|
|
}
|
|
|
|
while(lineptr[0] != ' ' && lineptr[0] != 0) ++lineptr;
|
|
}
|
|
|
|
// being that some exporters can export either 3 or 4 sided polygon's
|
|
// convert what ever was exported into triangles
|
|
for (size_t i=1; i<vindices.size()-1; ++i)
|
|
{
|
|
Face face;
|
|
FaceVert tri;
|
|
|
|
tri.vert = vindices[0];
|
|
if (!nindices.empty())
|
|
tri.norm = nindices[0];
|
|
if (!tindices.empty())
|
|
tri.norm = tindices[0];
|
|
|
|
if (uniqueverts.count(tri) == 0)
|
|
uniqueverts[tri] = vert_count++;
|
|
face.a = uniqueverts[tri];
|
|
|
|
tri.vert = vindices[i];
|
|
if (!nindices.empty())
|
|
tri.norm = nindices[i];
|
|
if (!tindices.empty())
|
|
tri.norm = tindices[i];
|
|
|
|
if (uniqueverts.count(tri) == 0)
|
|
uniqueverts[tri] = vert_count++;
|
|
face.b = uniqueverts[tri];
|
|
|
|
tri.vert = vindices[i+1];
|
|
if (!nindices.empty())
|
|
tri.norm = nindices[i+1];
|
|
if (!tindices.empty())
|
|
tri.norm = tindices[i+1];
|
|
|
|
if (uniqueverts.count(tri) == 0)
|
|
uniqueverts[tri] = vert_count++;
|
|
face.c = uniqueverts[tri];
|
|
mFaces.push_back(face);
|
|
}
|
|
}
|
|
}
|
|
inf.close();
|
|
|
|
mPositions.resize(vert_count);
|
|
if (norms.size() > 0)
|
|
mNormals.resize(vert_count);
|
|
if (texcoords.size() > 0)
|
|
mTexCoords.resize(vert_count);
|
|
|
|
std::map<FaceVert, int, vert_less>::iterator iter;
|
|
for (iter = uniqueverts.begin(); iter != uniqueverts.end(); ++iter)
|
|
{
|
|
|
|
mPositions[iter->second] = verts[iter->first.vert];
|
|
|
|
if ( norms.size() > 0 )
|
|
{
|
|
mNormals[iter->second] = norms[iter->first.norm];
|
|
}
|
|
|
|
if ( texcoords.size() > 0)
|
|
{
|
|
mTexCoords[iter->second] = texcoords[iter->first.coord];
|
|
}
|
|
}
|
|
}
|
|
|
|
int ObjLoader::getIndexCount()
|
|
{
|
|
return (int)mFaces.size() * 3;
|
|
}
|
|
|
|
int ObjLoader::getVertCount()
|
|
{
|
|
return (int)mPositions.size();
|
|
}
|
|
|
|
const uint32_t* ObjLoader::getFaces()
|
|
{
|
|
return (const uint32_t*)&mFaces[0];
|
|
}
|
|
|
|
const float* ObjLoader::getPositions()
|
|
{
|
|
return (const float*)&mPositions[0];
|
|
}
|
|
|
|
const float* ObjLoader::getNormals()
|
|
{
|
|
return (const float*)&mNormals[0];
|
|
}
|
|
|
|
uint32_t ObjLoader::getTexCoordLayers()
|
|
{
|
|
return mTexCoordLayers;
|
|
}
|
|
|
|
const float* ObjLoader::getTexCoords(int multiTexCoordLayer)
|
|
{
|
|
assert(multiTexCoordLayer < mTexCoordLayers);
|
|
return (const float*)&mTexCoords[0];
|
|
}
|
|
|
|
}
|