blob: da308498bcda44f79b5cab2f20ed26ab7d86aa91 [file] [log] [blame]
#ifndef RTOBJREADER_CXX
#define RTOBJREADER_CXX
#include "RTTL/common/MapOptions.hxx"
// For extended specs see
// http://www.martinreddy.net/gfx/3d/OBJ.spec
// http://www.fileformat.info/format/wavefrontobj/
namespace RTTL {
inline static
bool getInt3(char*& token, int& in, int& tn, int& nn) {
// Parse differently formated triplets like
// n0
// n0/n1/n2
// n0//n2
// Missing entries are assigned -1.
token += strcspn(token, " \t\n");
token += strspn (token, " \t\n");
if (token[0] == 0) {
err:
in = tn = nn = -1;
return false;
}
in = atoi(token);
token += strcspn(token, "/ \t\n");
if (token[0] == '/') {
// Either i/t/n or i//n
if (token[1] == '/') {
// it is i//n (no texture index)
tn = -1;
nn = atoi(token + 2);
return true;
}
// it is i/t/n
tn = atoi(++token);
token = strchr(token, '/');
if (token == 0) goto err;
nn = atoi(++token);
} else {
tn = nn = -1;
}
return true;
}
template<int N>
inline static
RTVec_t<N, float> getVec(char* token) {
// Read a vector form a string
RTVec_t<N, float> v;
#pragma unroll(3)
for (int i = 0; i < N; i++) {
token += strcspn(token, " \t\n");
token += strspn (token, " \t\n");
v[i] = atof(token);
}
return v;
}
template<long long mesh_descriptor>
inline static
bool readObj(string fileName, RTTriangleMesh<mesh_descriptor>& mesh) {
bool merge_triangles_to_quads = options.defined("merge_quads");
bool store_quads = merge_triangles_to_quads? true : options.defined("quads");
const int LINE_SIZE = 1000;
char line[LINE_SIZE];
FILE* file = fopen(fileName.c_str(), "rt");
if (!file) {
perror(fileName.c_str());
return false;
}
// First phase : only scan the file to compute sizes (false).
// Second phase : actually parse the file (true).
bool parse = false;
do {
// Counters of processed
// vertices, normals, textures, triangles
int nvv = -1, nvn = -1, nvt = -1, nt = -1;
// Read lines one by one.
while (fgets(line, LINE_SIZE, file)) {
int ll;
// check for continuation
while (line[ll = (strlen(line)-2)] == '\\') {
if (ll >= LINE_SIZE-8) {
printf("The following line in %s is too long:\n%s\n", fileName.c_str(), line);
return false;
}
fgets(line+ll, LINE_SIZE - ll, file);
}
char* token = line + strspn(line, " \t\n");
if (!token) continue;
if (token[0] == 'v') {
if (token[1] == ' ') {
// Vertex
nvv++;
if (!parse) continue;
mesh.addVertexPosition(getVec<3>(token));
} else if (token[1] == 'n') {
// Normal
nvn++;
if (!parse) continue;
mesh.addVertexNormal(getVec<3>(token));
} else if (token[1] == 't') {
// Texture
nvt++;
if (!parse) continue;
mesh.addVertexTexture1(getVec<2>(token));
}
} else if (token[0] == 'f' && strchr(" \t", token[1])) {
// Face: tri.v[i]/texture/normal
int t0, n0;
int t1, n1;
int t2, n2;
RTVec3i tri;
getInt3(token, tri.x, t0, n0);
if (getInt3(token, tri.y, t1, n1) == false) goto err;
if (getInt3(token, tri.z, t2, n2) == false) goto err;
if (store_quads) {
} else {
int ntriangles = 1;
extract_triangles_from_polygon:
ntriangles++;
nt++;
if (parse) {
// Store triangle 0/1/2
int ti = mesh.addTriangle(tri);
// Assign the 2nd vertex to the 1st (and then read new 2nd one).
tri.y = tri.z; t1 = t2; n1 = n2;
}
if (getInt3(token, tri.z, t2, n2)) {
goto extract_triangles_from_polygon;
}
}
} else if (!parse) {
// Read the next line while counting # of entities (first phase).
continue;
} else if (token[0] == '#') {
// Comment
continue;
} else if (token[0] == 'g' && strchr(" \t", token[1])) {
// Named group -- ignore it for now.
continue;
} else if (!strncmp(token, "mtllib", 6)) {
// Mtllib
} else if (!strncmp(token, "usemtl", 6)) {
// Usemtl
} else {
// Unsupported -- treat it as a comment.
continue;
}
}
// Go for second scan and actually read data (if parse was false)
// or break out of the loop.
rewind(file);
if (!parse) {
// Prepare for the second phase (actually parsing the file).
nvv++; nvn++; nvt++; nt++;
int nvv0 = mesh.numberOfVertices() ; if (nvv) mesh.setNumberOfVertices (nvv0 + nvv);
int nvn0 = mesh.numberOfVertexNormals() ; if (nvn) mesh.setNumberOfVertexNormals (nvn0 + nvn);
int nvt0 = mesh.numberOfTextureCoordinates1(); if (nvt) mesh.setNumberOfTextureCoordinates1(nvt0 + nvt);
int nt0 = mesh.numberOfTriangles() ; if (nt ) mesh.setNumberOfTriangles (nt0 + nt);
nvv = nvv0 - 1;
nvn = nvn0 - 1;
nvt = nvt0 - 1;
nt = nt0 - 1;
}
} while (parse = !parse);
fclose(file);
return true;
err:
printf("Errors parsing file %s\n", fileName.c_str());
return false;
}
};
#endif