/* ************************************************************************** */ /* */ /* ::: :::::::: */ /* ObjParser.cpp :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: ycontre +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/01/16 15:00:33 by tomoron #+# #+# */ /* Updated: 2025/01/18 21:07:12 by ycontre ### ########.fr */ /* */ /* ************************************************************************** */ #include "RT.hpp" ObjParser::ObjParser(std::string &filename) { _mat = 0; _file.open(filename); if(!_file.is_open()) throw std::runtime_error("OBJ : could not open object file"); } ObjParser::~ObjParser() { _file.close(); } glm::vec3 ObjParser::getVertex(std::stringstream &line) { glm::vec3 res; if(!(line >> res.x >> res.y >> res.z)) throw std::runtime_error("syntax error in obj file while parsing vertex"); return(res); } glm::vec2 ObjParser::getUV(std::stringstream &line) { glm::vec2 res; if(!(line >> res.x) || (!(line >> res.y) && !line.eof())) throw std::runtime_error("syntax error in obj file while parsing texture vertex"); return(res); } long int ObjParser::checkVertexIndex(int index, size_t size) { if((size_t)index > size || index == 0 || (index < 0 && (size_t)(-index) > size)) throw std::runtime_error("obj file error, invalid vertex index"); if(index < 0) index = size - index; return(index - 1); } int ObjParser::pointInTriangle(glm::vec3 pts[3], std::vector vertices, size_t cur) { glm::vec3 v0, v1, v2; float d00, d01, d11, d20, d21; float den; float u, v; for(size_t i = 0; i < vertices.size(); i++) { if(i == ((cur - 1) % vertices.size()) || i == cur || i == ((cur + 1) % vertices.size())) continue; v0 = pts[2] - pts[0]; v1 = pts[1] - pts[0]; v2 = vertices[i] - pts[0]; d00 = glm::dot(v0, v0); d01 = glm::dot(v0, v1); d11 = glm::dot(v1, v1); d20 = glm::dot(v2, v0); d21 = glm::dot(v2, v1); den = glm::dot(d00, d11) - glm::dot(d01, d01); u = (glm::dot(d11, d20) - glm::dot(d01, d21)) / den; v = (glm::dot(d00, d21) - glm::dot(d01, d20)) / den; if(u >= 0 && v >= 0 && (u + v) <= 1) return(1); } return(0); } bool ObjParser::addTriangleFromPolygon(std::vector &vertices, int inv) { glm::vec3 v1, v2 ,v3; float dot; for (size_t i = 0; i < vertices.size(); i++) { if(!i) v1 = vertices.back(); else v1 = vertices[i - 1]; v2 = vertices[i]; v3 = vertices[(i + 1) % vertices.size()]; if (inv) dot = glm::cross(v2 - v1, v2 - v3).z; else dot = glm::cross(v2 - v3, v2 - v1).z; if(dot <= 0) continue; glm::vec3 triangleVertices[3] = {v1, v2, v3}; if(pointInTriangle(triangleVertices, vertices, i)) continue; vertices.erase(vertices.begin() + i); addTriangle(v1, v2 ,v3); return(1); } return(0); } std::vector ObjParser::objSplit(std::string str, std::string delim) { std::vector res; while(str.find(delim) != std::string::npos) { res.push_back(str.substr(0, str.find(delim))); str.erase(0, str.find(delim) + 1); } res.push_back(str); return(res); } void ObjParser::getFaceVertices(std::vector &faceVertices, std::stringstream &line) { std::string el; std::vector sp; while(line >> el) { sp = objSplit(el, "/"); if(sp.size() > 3) std::runtime_error("OBJ : too many values in an element of a face"); if(sp.size() == 0) std::runtime_error("OBJ : wtf ?"); faceVertices.push_back(_vertices[checkVertexIndex(std::stoi(sp[0]), _vertices.size())]); } } void ObjParser::addFace(std::stringstream &line) { std::vector faceVertices; getFaceVertices(faceVertices, line); if(!line.eof()) throw std::runtime_error("OBJ : an error occured while paring face"); if(faceVertices.size() < 3) throw std::runtime_error("OBJ : face does not have enough vertices"); while(faceVertices.size() > 3) if (!addTriangleFromPolygon(faceVertices, 0)) if(!addTriangleFromPolygon(faceVertices, 1)) return ; if(!line.eof()) throw std::runtime_error("OBJ: an error occured while parsing face"); addTriangle(faceVertices[0], faceVertices[1], faceVertices[2]); } void ObjParser::addTriangle(glm::vec3 v1, glm::vec3 v2, glm::vec3 v3) { _triangles.push_back(Triangle(v1, v2, v3, _mat)); } void ObjParser::parseMtl(std::stringstream &input_line, Scene &scene) { std::string filename; std::ifstream file; std::string matName; std::string identifier; std::string line; Material *mat; input_line >> filename; file.open(filename); mat = 0; if(!file.is_open()) throw std::runtime_error("OBJ : could not open material file"); while(getline(file, line)) { if (line.empty() || line[0] == '#') continue; std::stringstream lineStream(line); lineStream >> identifier; if(identifier == "newmtl") { if(mat) { scene.addMaterial(mat); _matNames[matName] = scene.getMaterialData().size() - 1; } lineStream >> matName; if(matName.empty()) throw std::runtime_error("OBJ: syntax error in material file, missing material name"); mat = new Material; memset(mat, 0, sizeof(Material)); mat->metallic = 1.0f; continue; } if(!mat) throw std::runtime_error("OBJ: error in material file, material name not defined"); if(identifier == "Kd") { if(!(lineStream >> mat->color.x >> mat->color.y >> mat->color.z)) throw std::runtime_error("OBJ: syntax error while getting material color"); } else if(identifier == "Ns") { if(!(lineStream >> mat->roughness) || mat->roughness > 1000 || mat->roughness < 0) throw std::runtime_error("OBJ: syntax error while getting material softness"); mat->roughness /= 1000; } else if(identifier == "Ke") { float x, y, z; if(!(lineStream >> x >> y >> z)) throw std::runtime_error("OBJ: syntax error while getting material emission"); mat->emission = (x + y + z) / 3; } else if(identifier == "Ni") { if(!(lineStream >> mat->refraction)) throw std::runtime_error("OBJ: syntax error while getting material refraction"); } else std::cerr << "unsupported material setting : " << identifier << std::endl; } if(mat) { scene.addMaterial(mat); _matNames[matName] = scene.getMaterialData().size() - 1; } file.close(); } void ObjParser::parse(Scene &scene) { std::string line; std::string identifier; while (getline(_file, line)) { try{ if(line[0] == '#' || line.empty()) continue; std::stringstream lineStream(line); identifier = ""; lineStream >> identifier; if(identifier == "v") _vertices.push_back(getVertex(lineStream)); else if (identifier == "vt") _textureVertices.push_back(getUV(lineStream)); else if (identifier == "f") addFace(lineStream); else if (identifier == "mtllib") parseMtl(lineStream, scene); else if (identifier == "usemtl") { lineStream >> identifier; if(_matNames.find(identifier) == _matNames.end()) throw std::runtime_error("OBJ: invalid material name"); _mat = _matNames[identifier]; } } catch (std::exception &e) { std::cerr << line << std::endl; throw; } } scene.addBvh(_triangles); }