16 std::ifstream file(filename);
17 if (!file.is_open()) {
18 std::cerr <<
"Error opening file: " << filename << std::endl;
19 std::exit(EXIT_FAILURE);
22 std::string mtlFilename;
23 std::vector<vec3> positions;
24 std::vector<vec2> texCoords;
25 std::vector<vec3> colors;
27 std::string currentMaterial;
29 float minX = std::numeric_limits<float>::max(), maxX = std::numeric_limits<float>::lowest();
30 float minY = std::numeric_limits<float>::max(), maxY = std::numeric_limits<float>::lowest();
31 float minZ = std::numeric_limits<float>::max(), maxZ = std::numeric_limits<float>::lowest();
33 while (std::getline(file, line)) {
34 std::istringstream iss(line);
40 std::cerr <<
"Error: Empty " << type <<
" line in file: " << filename << std::endl;
41 std::exit(EXIT_FAILURE);
44 if (type ==
"mtllib") {
46 }
else if (type ==
"usemtl") {
47 iss >> currentMaterial;
48 }
else if (type ==
"v") {
50 if (!(iss >> x >> y >> z)) {
51 std::cerr <<
"Error: Bad " << type <<
" line in file: " << filename << std::endl;
52 std::exit(EXIT_FAILURE);
55 positions.push_back(pos);
57 minX = std::min(x, minX);
58 minY = std::min(y, minY);
59 minZ = std::min(z, minZ);
61 maxX = std::max(x, maxX);
62 maxY = std::max(y, maxY);
63 maxZ = std::max(z, maxZ);
64 }
else if (type ==
"vt") {
66 if (!(iss >> u >> v)) {
67 std::cerr <<
"Error: Bad " << type <<
" line in file: " << filename << std::endl;
68 std::exit(EXIT_FAILURE);
70 texCoords.push_back(
vec2(u, v));
71 }
else if (type ==
"vc") {
73 if (!(iss >> r >> g >> b)) {
74 std::cerr <<
"Error: Bad " << type <<
" line in file: " << filename << std::endl;
75 std::exit(EXIT_FAILURE);
77 colors.push_back(
vec3(r, g, b));
78 }
else if (type ==
"f") {
79 std::vector<int> vertexIndices;
80 std::vector<int> texCoordIndices;
82 std::string vertexData;
83 while (iss >> vertexData) {
84 std::istringstream vertexDataStream(vertexData);
87 while (std::getline(vertexDataStream, index,
'/')) {
88 if (i == 0 && !index.empty()) {
89 vertexIndices.push_back(std::stoi(index) - 1);
90 }
else if (i == 1 && !index.empty()) {
91 texCoordIndices.push_back(std::stoi(index) - 1);
97 if (vertexIndices.size() < 3) {
98 std::cerr <<
"Error: Bad " << type <<
" line in file: " << filename << std::endl;
99 std::exit(EXIT_FAILURE);
102 for (
size_t j = 0; j < vertexIndices.size() - 2; ++j) {
103 std::vector<int> triangleIndices = {0,
static_cast<int>(j + 1),
static_cast<int>(j + 2)};
105 for (
int index : triangleIndices) {
108 if (positions.empty())
111 vertex.
pos = positions[vertexIndices[index]];
113 if (!texCoords.empty()) {
114 vertex.
texCoord = texCoords[texCoordIndices[index]];
116 vertex.
texCoord =
vec2((vertex.
pos.
x - minX) / (maxX - minX), 1.0f - ((vertex.
pos.
y - minY) / (maxY - minY)));
119 if (!colors.empty()) {
120 vertex.
color = colors[vertexIndices[index]];
136 modelCentroid =
vec3((minX + maxX) / 2.f, (minY + maxY) / 2.f, (minZ + maxZ) / 2.f);
138 if (!mtlFilename.empty()) {
161 if (positions.empty()) {
162 std::cerr <<
"Error: The OBJ file does not contain vertex." << std::endl;
163 std::exit(EXIT_FAILURE);
167 std::cerr <<
"Error: The OBJ file does not contain face." << std::endl;
168 std::exit(EXIT_FAILURE);
173 std::filesystem::path objPath(objFilePath);
174 std::filesystem::path mtlPath = objPath.parent_path() / mtlFilename;
176 std::ifstream file(mtlPath);
177 if (!file.is_open()) {
178 std::cout <<
"Warning: MTL file not found: " << mtlPath << std::endl;
184 std::unordered_map<std::string, Material> materials;
185 auto mat = materials.end();
187 while (std::getline(file, line)) {
188 std::istringstream iss(line);
192 if (type ==
"newmtl") {
196 auto [it, mode] = materials.insert_or_assign(name,
Material{});
199 std::clog <<
"Warning: replaced old material " << name << std::endl;
201 }
else if (type ==
"Ka") {
202 if (mat == materials.end()) {
203 std::cerr <<
"Error: Material not defined before " << type <<
" in file: " << mtlFilename << std::endl;
204 std::exit(EXIT_FAILURE);
206 if (!(iss >> mat->second.ambient.x >> mat->second.ambient.y >> mat->second.ambient.z)) {
207 std::cerr <<
"Error: Bad " << type <<
" line in file: " << mtlFilename << std::endl;
208 std::exit(EXIT_FAILURE);
210 }
else if (type ==
"Kd") {
211 if (mat == materials.end()) {
212 std::cerr <<
"Error: Material not defined before " << type <<
" in file: " << mtlFilename << std::endl;
213 std::exit(EXIT_FAILURE);
215 if (!(iss >> mat->second.diffuse.x >> mat->second.diffuse.y >> mat->second.diffuse.z)) {
216 std::cerr <<
"Error: Bad " << type <<
" line in file: " << mtlFilename << std::endl;
217 std::exit(EXIT_FAILURE);
219 }
else if (type ==
"Ks") {
220 if (mat == materials.end()) {
221 std::cerr <<
"Error: Material not defined before " << type <<
" in file: " << mtlFilename << std::endl;
222 std::exit(EXIT_FAILURE);
224 if (!(iss >> mat->second.specular.x >> mat->second.specular.y >> mat->second.specular.z)) {
225 std::cerr <<
"Error: Bad " << type <<
" line in file: " << mtlFilename << std::endl;
226 std::exit(EXIT_FAILURE);
228 }
else if (type ==
"d") {
229 if (mat == materials.end()) {
230 std::cerr <<
"Error: Material not defined before " << type <<
" in file: " << mtlFilename << std::endl;
231 std::exit(EXIT_FAILURE);
233 if (!(iss >> mat->second.dissolve)) {
234 std::cerr <<
"Error: Bad " << type <<
" line in file: " << mtlFilename << std::endl;
235 std::exit(EXIT_FAILURE);
241 if (mat == materials.end()) {
245 if (!vertex.material_name.empty()) {
247 const auto&[ambient, diffuse, specular, dissolve] = materials.at(vertex.material_name);
249 vertex.color = diffuse;
250 vertex.ambientColor = ambient;
251 vertex.specularColor = specular;
252 vertex.dissolveFactor = dissolve;
253 }
catch (
const std::out_of_range&) {}