Initial commit.

This commit is contained in:
hal8174 2024-04-23 10:14:24 +02:00
commit d3bb49b3f5
1073 changed files with 484757 additions and 0 deletions

View file

@ -0,0 +1,715 @@
// Copyright 2009-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
#include "obj_loader.h"
// #include "texture.h"
namespace embree {
/*! Three-index vertex, indexing start at 0, -1 means invalid vertex. */
struct Vertex {
unsigned int v, vt, vn;
Vertex() {
};
Vertex(unsigned int v) : v(v), vt(v), vn(v) {
};
Vertex(unsigned int v, unsigned int vt, unsigned int vn) : v(v), vt(vt), vn(vn) {
};
};
struct Crease {
float w;
unsigned int a, b;
Crease() : w(0), a(-1), b(-1) {
};
Crease(float w, unsigned int a, unsigned int b) : w(w), a(a), b(b) {
};
};
static inline bool operator <(const Vertex& a, const Vertex& b) {
if (a.v != b.v) return a.v < b.v;
if (a.vn != b.vn) return a.vn < b.vn;
if (a.vt != b.vt) return a.vt < b.vt;
return false;
}
/*! Fill space at the end of the token with 0s. */
static inline const char* trimEnd(const char* token) {
if (*token == 0) return token;
char* pe = (char *) token;
while (*pe) pe++;
pe--;
while ((*pe == ' ' || *pe == '\t' || *pe == '\r') && pe >= token) *pe-- = 0;
return token;
}
/*! Determine if character is a separator. */
static inline bool isSep(const char c) {
return (c == ' ') || (c == '\t');
}
/*! Parse separator. */
static inline const char* parseSep(const char*& token) {
size_t sep = strspn(token, " \t");
if (!sep)
THROW_RUNTIME_ERROR("separator expected");
return token += sep;
}
/*! Parse optional separator. */
static inline const char* parseSepOpt(const char*& token) {
return token += strspn(token, " \t");
}
/*! Read float from a string. */
static inline float getFloat(const char*& token) {
token += strspn(token, " \t");
float n = (float) atof(token);
token += strcspn(token, " \t\r");
return n;
}
/*! Read int from a string. */
static inline int getInt(const char*& token) {
token += strspn(token, " \t");
int n = atoi(token);
token += strcspn(token, " \t\r");
return n;
}
/*! Read Vec2f from a string. */
static inline Vec2f getVec2f(const char*& token) {
float x = getFloat(token);
float y = getFloat(token);
return Vec2f(x, y);
}
/*! Read Vec3f from a string. */
static inline Vec3f getVec3f(const char*& token) {
float x = getFloat(token);
token += strspn(token, " \t");
if (*token == 0) return Vec3f(x);
float y = getFloat(token);
float z = getFloat(token);
return Vec3f(x, y, z);
}
/*! Read Vec3fa from a string. */
static inline Vec3fa getVec3fa(const char*& token) {
float x = getFloat(token);
token += strspn(token, " \t");
if (*token == 0) return Vec3f(x);
float y = getFloat(token);
float z = getFloat(token);
return Vec3fa(x, y, z);
}
class OBJLoader {
public:
/*! Constructor. */
OBJLoader(const FileName& fileName, const bool subdivMode, const bool combineIntoSingleObject);
/*! output model */
Ref<SceneGraph::GroupNode> group;
private:
/*! file to load */
FileName path;
/*! load only quads and ignore triangles */
bool subdivMode;
/*! Geometry buffer. */
avector<Vec3fa> v;
avector<Vec3fa> vn;
std::vector<Vec2f> vt;
std::vector<Crease> ec;
std::vector<std::vector<Vertex> > curGroup;
std::vector<avector<Vec3ff> > curGroupHair;
/*! Material handling. */
std::string curMaterialName;
Ref<SceneGraph::MaterialNode> curMaterial;
std::map<std::string, Ref<SceneGraph::MaterialNode> > material;
// std::map<std::string, std::shared_ptr<Texture>> textureMap;
private:
void loadMTL(const FileName& fileName);
unsigned int fix_v(int index);
unsigned int fix_vt(int index);
unsigned int fix_vn(int index);
void flushFaceGroup();
void flushTriGroup();
// void flushHairGroup();
Vertex getUInt3(const char*& token);
uint32_t getVertex(std::map<Vertex, uint32_t>& vertexMap, Ref<SceneGraph::TriangleMeshNode> mesh,
const Vertex& i);
// std::shared_ptr<Texture> loadTexture(const FileName& fname);
};
OBJLoader::OBJLoader(const FileName& fileName, const bool subdivMode, const bool combineIntoSingleObject)
: group(new SceneGraph::GroupNode), path(fileName.path()), subdivMode(subdivMode) {
/* open file */
std::ifstream cin;
cin.open(fileName.c_str());
if (!cin.is_open()) {
THROW_RUNTIME_ERROR("cannot open " + fileName.str());
return;
}
/* generate default material */
Ref<SceneGraph::MaterialNode> defaultMaterial = new OBJMaterial("default");
curMaterialName = "default";
curMaterial = defaultMaterial;
while (cin.peek() != -1) {
/* load next multiline */
std::string line;
std::getline(cin, line);
while (!line.empty() && line[line.size() - 1] == '\\') {
line[line.size() - 1] = ' ';
std::string next_line;
std::getline(cin, next_line);
if (next_line.empty()) break;
line += next_line;
}
const char* token = trimEnd(line.c_str() + strspn(line.c_str(), " \t"));
if (token[0] == 0) continue;
/*! parse position */
if (token[0] == 'v' && isSep(token[1])) {
v.push_back(getVec3f(token += 2));
continue;
}
/* parse normal */
if (token[0] == 'v' && token[1] == 'n' && isSep(token[2])) {
vn.push_back(getVec3f(token += 3));
continue;
}
/* parse texcoord */
if (token[0] == 'v' && token[1] == 't' && isSep(token[2])) {
vt.push_back(getVec2f(token += 3));
continue;
}
/*! parse face */
if (token[0] == 'f' && isSep(token[1])) {
parseSep(token += 1);
std::vector<Vertex> face;
while (token[0]) {
Vertex vtx = getUInt3(token);
face.push_back(vtx);
parseSepOpt(token);
}
curGroup.push_back(face);
continue;
}
/*! parse corona hair */
if (!strncmp(token, "hair", 4) && isSep(token[4])) {
throw std::runtime_error("corona hair is deimplemented");
}
/*! parse edge crease */
if (token[0] == 'e' && token[1] == 'c' && isSep(token[2])) {
parseSep(token += 2);
float w = getFloat(token);
parseSepOpt(token);
unsigned int a = fix_v(getInt(token));
parseSepOpt(token);
unsigned int b = fix_v(getInt(token));
parseSepOpt(token);
ec.push_back(Crease(w, a, b));
continue;
}
/*! use material */
if (!strncmp(token, "usemtl", 6) && isSep(token[6])) {
if (!combineIntoSingleObject) flushFaceGroup();
std::string name(parseSep(token += 6));
if (material.find(name) == material.end()) {
curMaterial = defaultMaterial;
curMaterialName = "default";
} else {
curMaterial = material[name];
curMaterialName = name;
}
continue;
}
/* load material library */
if (!strncmp(token, "mtllib", 6) && isSep(token[6])) {
loadMTL(path + std::string(parseSep(token += 6)));
continue;
}
// ignore unknown stuff
}
flushFaceGroup();
cin.close();
}
struct ExtObjMaterial {
public:
enum Type { NONE, MATTE, GLASS, METAL, METALLIC_PAINT };
Ref<SceneGraph::MaterialNode> select() const {
// std::shared_ptr<Texture> nulltex;
if (type == NONE) {
// return new OBJMaterial(d,map_d,Ka,map_Ka,Kd,map_Kd,Ks,map_Ks,Kt,map_Kt,Ns,map_Ns,map_Displ);
return new OBJMaterial(d, Ka, Kd, Ks, Kt, Ns);
} else if (type == MATTE) {
if (coat_eta != 1.0f)
return new MetallicPaintMaterial(Kd, zero, 0.0f, eta.x);
else
// return new OBJMaterial(1.0f,nulltex,zero,nulltex,Kd,map_Kd,Ks,nulltex,zero,nulltex,1.0f/(1E-6f+roughness),nulltex,nulltex);
return new OBJMaterial(1.0f, zero, Kd, Ks, zero, 1.0f / (1E-6f + roughness));
} else if (type == GLASS) {
return new ThinDielectricMaterial(Vec3fa(1.0f), eta.x, 0.1f);
} else if (type == METAL) {
if (roughness == 0.0f) {
if (eta == Vec3fa(1.0f) && k == Vec3fa(0.0f)) return new MirrorMaterial(Kd);
else return new MetalMaterial(Kd, eta, k);
} else return new MetalMaterial(Kd, eta, k, roughness);
} else if (type == METALLIC_PAINT) {
return new MetallicPaintMaterial(Kd, Ks, 0.0f, coat_eta);
}
return new MatteMaterial(Vec3fa(0.5f));
}
public:
Type type = NONE;
int illum = 0; /*< illumination model */
float d = 1.0f; /*< dissolve factor, 1=opaque, 0=transparent */
float Ns = 1.0f; /*< specular exponent */
float Ni = 1.0f; /*< optical density for the surface (index of refraction) */
Vec3fa Ka = zero; /*< ambient reflectivity */
Vec3fa Kd = one; /*< diffuse reflectivity */
Vec3fa Ks = zero; /*< specular reflectivity */
// Vec3fa Kt = zero; /*< transmission filter */
Vec3fa Kt = one; /*< transmission filter */
// ^ Because many textures were deleted
// std::shared_ptr<Texture> map_d; /*< d texture */
// std::shared_ptr<Texture> map_Ka; /*< Ka texture */
// std::shared_ptr<Texture> map_Kd; /*< Kd texture */
// std::shared_ptr<Texture> map_Ks; /*< Ks texture */
// std::shared_ptr<Texture> map_Kt; /*< Kt texture */
// std::shared_ptr<Texture> map_Ns; /*< Ns texture */
// std::shared_ptr<Texture> map_Displ; /*< Displ texture */
float roughness = zero;
// std::shared_ptr<Texture> roughnessMap;
float coat_eta = one;
float coat_roughness = zero;
// std::shared_ptr<Texture> coat_roughnessMap;
float bump = zero;
Vec3f eta = Vec3f(1.4f);
Vec3f k = Vec3f(3.0f);
};
// std::shared_ptr<Texture> OBJLoader::loadTexture(const FileName& fname)
// {
// if (textureMap.find(fname.str()) != textureMap.end())
// return textureMap[fname.str()];
// return std::shared_ptr<Texture>(Texture::load(path+fname));
// }
/* load material file */
void OBJLoader::loadMTL(const FileName& fileName) {
std::ifstream cin;
cin.open(fileName.c_str());
if (!cin.is_open()) {
std::cerr << "cannot open " << fileName.str() << std::endl;
return;
}
std::string name;
ExtObjMaterial cur;
while (cin.peek() != -1) {
/* load next multiline */
std::string line;
std::getline(cin, line);
while (!line.empty() && line[line.size() - 1] == '\\') {
line[line.size() - 1] = ' ';
std::string next_line;
std::getline(cin, next_line);
if (next_line.empty()) break;
line += next_line;
}
const char* token = trimEnd(line.c_str() + strspn(line.c_str(), " \t"));
if (token[0] == 0) continue; // ignore empty lines
if (token[0] == '#') continue; // ignore comments
if (!strncmp(token, "newmtl", 6)) {
if (name != "") {
material[name] = cur.select();
material[name]->name = name;
}
parseSep(token += 6);
name = token;
cur = ExtObjMaterial();
continue;
}
if (name == "")
THROW_RUNTIME_ERROR("invalid material file: newmtl expected first");
try {
if (!strncmp(token, "illum", 5)) {
parseSep(token += 5);
continue;
}
if (!strncmp(token, "d", 1)) {
parseSep(token += 1);
cur.d = getFloat(token);
continue;
}
if (!strncmp(token, "Ns", 2)) {
parseSep(token += 2);
cur.Ns = getFloat(token);
continue;
}
if (!strncmp(token, "Ni", 2)) {
parseSep(token += 2);
cur.Ni = getFloat(token);
continue;
}
// if (!strncmp(token, "map_d", 5) || !strncmp(token, "d_map", 5)) {
// parseSep(token += 5);
// cur.map_d = loadTexture(FileName(token));
// continue;
// }
// if (!strncmp(token, "map_Ka", 6) || !strncmp(token, "Ka_map", 6)) {
// parseSep(token += 6);
// cur.map_Ka = loadTexture(FileName(token));
// continue;
// }
// if (!strncmp(token, "map_Kd", 6) || !strncmp(token, "Kd_map", 6)) {
// parseSep(token += 6);
// cur.map_Kd = loadTexture(FileName(token));
// continue;
// }
// if (!strncmp(token, "map_Ks", 6) || !strncmp(token, "Ks_map", 6)) {
// parseSep(token += 6);
// cur.map_Ks = loadTexture(FileName(token));
// continue;
// }
// if (!strncmp(token, "map_Kt", 6) || !strncmp(token, "Kt_map", 6)) {
// parseSep(token += 6);
// cur.map_Kt = loadTexture(FileName(token));
// continue;
// }
// if (!strncmp(token, "map_Tf", 6) || !strncmp(token, "Tf_map", 6)) {
// parseSep(token += 6);
// cur.map_Kt = loadTexture(FileName(token));
// continue;
// }
// if (!strncmp(token, "map_Ns", 6) || !strncmp(token, "Ns_map", 6)) {
// parseSep(token += 6);
// cur.map_Ns = loadTexture(FileName(token));
// continue;
// }
// if (!strncmp(token, "map_Displ", 9) || !strncmp(token, "Displ_map", 9)) {
// parseSep(token += 9);
// cur.map_Displ = loadTexture(FileName(token));
// continue;
// }
if (!strncmp(token, "Ka", 2)) {
parseSep(token += 2);
cur.Ka = getVec3f(token);
continue;
}
if (!strncmp(token, "Kd", 2)) {
parseSep(token += 2);
cur.Kd = getVec3f(token);
continue;
}
if (!strncmp(token, "Ks", 2)) {
parseSep(token += 2);
cur.Ks = getVec3f(token);
continue;
}
if (!strncmp(token, "Kt", 2)) {
parseSep(token += 2);
cur.Kt = getVec3f(token);
continue;
}
if (!strncmp(token, "Tf", 2)) {
parseSep(token += 2);
cur.Kt = getVec3f(token);
continue;
}
/* extended OBJ */
if (!strncmp(token, "type", 4)) {
parseSep(token += 4);
std::string type = token;
if (type == "matte") cur.type = ExtObjMaterial::MATTE;
else if (type == "glass") cur.type = ExtObjMaterial::GLASS;
else if (type == "metal") cur.type = ExtObjMaterial::METAL;
else if (type == "metallicPaint") cur.type = ExtObjMaterial::METALLIC_PAINT;
else if (type == "principled") cur.type = ExtObjMaterial::NONE;
else if (type == "luminous") cur.type = ExtObjMaterial::NONE;
continue;
}
/* OSPRay principled material extensions */
if (!strncmp(token, "baseColor", 9)) {
parseSep(token += 9);
cur.Kd = getVec3f(token);
continue;
}
if (!strncmp(token, "ior", 3)) {
parseSep(token += 3);
cur.Ni = getFloat(token);
continue;
}
// if (!strncmp(token, "map_baseColor", 13)) { parseSep(token += 13); cur.map_Kd = loadTexture(FileName(token)); continue; }
// if (!strncmp(token, "map_specular", 12)) { parseSep(token += 12); cur.map_Ks = loadTexture(FileName(token)); continue; }
if (!strncmp(token, "map_normal.scale", 16)) {
parseSep(token += 16);
continue;
}
// if (!strncmp(token, "map_normal", 10)) { parseSep(token += 10); cur.map_Displ = loadTexture(FileName(token)); continue; }
if (!strncmp(token, "transmissionColor", 17)) {
parseSep(token += 17);
cur.Kt = getVec3f(token);
continue;
}
if (!strncmp(token, "transmission", 12)) {
parseSep(token += 12);
cur.Kt = Vec3f(getFloat(token));
continue;
}
// if (!strncmp(token, "roughnessMap", 12)) { parseSep(token += 12); cur.roughnessMap = loadTexture(FileName(token)); continue; }
if (!strncmp(token, "roughness", 9)) {
parseSep(token += 9);
cur.roughness = getFloat(token);
cur.Ns = 1.0f / (cur.roughness + 1E-6f);
continue;
}
// if (!strncmp(token, "colorMap", 8)) { parseSep(token += 8); cur.map_Kd = loadTexture(FileName(token)); continue; }
if (!strncmp(token, "color", 5)) {
parseSep(token += 5);
cur.Kd = getVec3f(token);
continue;
}
if (!strncmp(token, "coat.color", 10)) {
parseSep(token += 10);
cur.Kd = getVec3f(token);
continue;
}
if (!strncmp(token, "coat.eta", 8)) {
parseSep(token += 8);
cur.coat_eta = getFloat(token);
continue;
}
// if (!strncmp(token, "coat.roughnessMap",17)) { parseSep(token += 17); cur.coat_roughnessMap = loadTexture(FileName(token)); continue; }
if (!strncmp(token, "coat.roughness", 14)) {
parseSep(token += 14);
cur.coat_roughness = getFloat(token);
continue;
}
// if (!strncmp(token, "bumpMap", 7)) { parseSep(token += 7); cur.map_Displ = loadTexture(FileName(token)); continue; }
if (!strncmp(token, "bump", 4)) {
parseSep(token += 4);
cur.bump = getFloat(token);
continue;
}
if (!strncmp(token, "eta", 3)) {
parseSep(token += 3);
cur.eta = getVec3f(token);
continue;
}
if (!strncmp(token, "k", 1)) {
parseSep(token += 1);
cur.k = getVec3f(token);
continue;
}
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
}
if (name != "") {
material[name] = cur.select();
material[name]->name = name;
}
cin.close();
}
/*! handles relative indices and starts indexing from 0 */
unsigned int OBJLoader::fix_v(int index) {
return (index > 0 ? index - 1 : (index == 0 ? 0 : (int) v.size() + index));
}
unsigned int OBJLoader::fix_vt(int index) {
return (index > 0 ? index - 1 : (index == 0 ? 0 : (int) vt.size() + index));
}
unsigned int OBJLoader::fix_vn(int index) {
return (index > 0 ? index - 1 : (index == 0 ? 0 : (int) vn.size() + index));
}
/*! Parse differently formatted triplets like: n0, n0/n1/n2, n0//n2, n0/n1. */
/*! All indices are converted to C-style (from 0). Missing entries are assigned -1. */
Vertex OBJLoader::getUInt3(const char*& token) {
Vertex v(-1);
v.v = fix_v(atoi(token));
token += strcspn(token, "/ \t\r");
if (token[0] != '/') return (v);
token++;
// it is i//n
if (token[0] == '/') {
token++;
v.vn = fix_vn(atoi(token));
token += strcspn(token, " \t\r");
return (v);
}
// it is i/t/n or i/t
v.vt = fix_vt(atoi(token));
token += strcspn(token, "/ \t\r");
if (token[0] != '/') return (v);
token++;
// it is i/t/n
v.vn = fix_vn(atoi(token));
token += strcspn(token, " \t\r");
return (v);
}
uint32_t OBJLoader::getVertex(std::map<Vertex, uint32_t>& vertexMap, Ref<SceneGraph::TriangleMeshNode> mesh,
const Vertex& i) {
const std::map<Vertex, uint32_t>::iterator& entry = vertexMap.find(i);
if (entry != vertexMap.end()) return (entry->second);
if (i.v >= v.size()) std::cout << "WARNING: corrupted OBJ file" << std::endl;
else mesh->positions[0].push_back(v[i.v]);
if (i.vn != -1) {
while (mesh->normals[0].size() < mesh->positions[0].size()) mesh->normals[0].push_back(zero);
// some vertices might not had a normal
if (i.vn >= vn.size()) std::cout << "WARNING: corrupted OBJ file" << std::endl;
else mesh->normals[0][mesh->positions[0].size() - 1] = vn[i.vn];
}
if (i.vt != -1) {
while (mesh->texcoords.size() < mesh->positions[0].size()) mesh->texcoords.push_back(zero);
// some vertices might not had a texture coordinate
if (i.vt >= vt.size()) std::cout << "WARNING: corrupted OBJ file" << std::endl;
else mesh->texcoords[mesh->positions[0].size() - 1] = vt[i.vt];
}
return (vertexMap[i] = (unsigned int) (mesh->positions[0].size()) - 1);
}
void OBJLoader::flushFaceGroup() {
flushTriGroup();
// flushHairGroup();
}
/*! end current facegroup and append to mesh */
void OBJLoader::flushTriGroup() {
if (curGroup.empty()) return;
if (subdivMode) {
throw std::runtime_error("Deimplemented");
// Ref<SceneGraph::SubdivMeshNode> mesh = new SceneGraph::SubdivMeshNode(curMaterial,BBox1f(0,1),1);
// mesh->normals.resize(1);
// group->add(mesh.cast<SceneGraph::Node>());
// for (size_t i=0; i<v.size(); i++) mesh->positions[0].push_back(v[i]);
// for (size_t i=0; i<vn.size(); i++) mesh->normals[0].push_back(vn[i]);
// for (size_t i=0; i<vt.size(); i++) mesh->texcoords.push_back(vt[i]);
// for (size_t i=0; i<ec.size(); ++i) {
// assert(((size_t)ec[i].a < v.size()) && ((size_t)ec[i].b < v.size()));
// mesh->edge_creases.push_back(Vec2i(ec[i].a, ec[i].b));
// mesh->edge_crease_weights.push_back(ec[i].w);
// }
// for (size_t j=0; j<curGroup.size(); j++)
// {
// const std::vector<Vertex>& face = curGroup[j];
// mesh->verticesPerFace.push_back(int(face.size()));
// for (size_t i=0; i<face.size(); i++)
// mesh->position_indices.push_back(face[i].v);
// }
// if (mesh->normals[0].size() == 0)
// mesh->normals.clear();
// mesh->verify();
} else {
Ref<SceneGraph::TriangleMeshNode> mesh = new SceneGraph::TriangleMeshNode(curMaterial, BBox1f(0, 1), 1);
mesh->normals.resize(1);
group->add(mesh.cast<SceneGraph::Node>());
// merge three indices into one
std::map<Vertex, uint32_t> vertexMap;
for (size_t j = 0; j < curGroup.size(); j++) {
/* iterate over all faces */
const std::vector<Vertex>& face = curGroup[j];
/* triangulate the face with a triangle fan */
Vertex i0 = face[0], i1 = Vertex(-1), i2 = face[1];
for (size_t k = 2; k < face.size(); k++) {
i1 = i2;
i2 = face[k];
uint32_t v0, v1, v2;
v0 = getVertex(vertexMap, mesh, i0);
v1 = getVertex(vertexMap, mesh, i1);
v2 = getVertex(vertexMap, mesh, i2);
assert(v0 < mesh->numVertices());
assert(v1 < mesh->numVertices());
assert(v2 < mesh->numVertices());
mesh->triangles.push_back(SceneGraph::TriangleMeshNode::Triangle(v0, v1, v2));
}
}
/* there may be vertices without normals or texture coordinates, thus we have to make these arrays the same size here */
if (mesh->normals[0].size()) while (mesh->normals[0].size() < mesh->numVertices()) mesh->normals[0].
push_back(zero);
if (mesh->texcoords.size()) while (mesh->texcoords.size() < mesh->numVertices()) mesh->texcoords.
push_back(zero);
if (mesh->normals[0].size() == 0)
mesh->normals.clear();
mesh->verify();
}
curGroup.clear();
ec.clear();
}
Ref<SceneGraph::Node> loadOBJ(const FileName& fileName, const bool subdivMode, const bool combineIntoSingleObject) {
OBJLoader loader(fileName, subdivMode, combineIntoSingleObject);
return loader.group.cast<SceneGraph::Node>();
}
}