343 {
344 std::ifstream file(path);
345 if (!file.is_open()) {
346 error = "could not open file";
347 return false;
348 }
349
350 std::vector<std::array<float, 3>> positions;
351 std::vector<std::array<float, 2>> texcoords;
352 std::vector<std::array<float, 3>> normals;
353 OBJLoadResult loaded;
354 loaded.object_name = std::filesystem::path(path).stem().string();
355 std::string current_object = loaded.object_name;
356 std::string current_material;
357 std::string line;
358 std::size_t line_number = 0;
359
360 while (std::getline(file, line)) {
361 ++line_number;
363 if (line.empty()) {
364 continue;
365 }
366
367 std::istringstream stream(line);
368 std::string tag;
369 stream >> tag;
370 if (tag == "v") {
371 std::array<float, 3> position{};
372 if (!(stream >> position[0] >> position[1] >> position[2]) || !std::isfinite(position[0]) || !std::isfinite(position[1]) || !std::isfinite(position[2])) {
373 error = "invalid vertex at line " + std::to_string(line_number);
374 return false;
375 }
376 positions.push_back(position);
377 } else if (tag == "vt") {
378 std::array<float, 2> texcoord{};
379 if (!(stream >> texcoord[0] >> texcoord[1]) || !std::isfinite(texcoord[0]) || !std::isfinite(texcoord[1])) {
380 error = "invalid texture coordinate at line " + std::to_string(line_number);
381 return false;
382 }
383 texcoords.push_back(texcoord);
384 } else if (tag == "vn") {
385 std::array<float, 3> normal{};
386 if (!(stream >> normal[0] >> normal[1] >> normal[2]) || !std::isfinite(normal[0]) || !std::isfinite(normal[1]) || !std::isfinite(normal[2])) {
387 error = "invalid normal at line " + std::to_string(line_number);
388 return false;
389 }
390 normals.push_back(normal);
391 } else if (tag == "o") {
392 std::string name;
393 std::getline(stream, name);
395 if (!name.empty()) {
396 current_object = name;
397 loaded.object_name = std::move(name);
398 }
399 } else if (tag == "mtllib") {
400 std::string library;
401 std::getline(stream, library);
403 if (!library.empty() && loaded.material_library_path.empty()) {
404 loaded.material_library_path =
resolve_obj_path(std::filesystem::path(path).parent_path(), library);
405 }
406 } else if (tag == "usemtl") {
407 std::getline(stream, current_material);
409 } else if (tag == "f") {
410 std::vector<OBJVertex> face;
411 std::string token;
412 while (stream >> token) {
415 error = "malformed face token '" + token + "' at line " + std::to_string(line_number);
416 return false;
417 }
418
420 if (position_index < 0 || position_index >= static_cast<int>(positions.size())) {
421 error = "face position index out of range at line " + std::to_string(line_number);
422 return false;
423 }
424
426 vertex.
position = positions[
static_cast<std::size_t
>(position_index)];
427 if (index.texcoord != 0) {
429 if (texcoord_index < 0 || texcoord_index >= static_cast<int>(texcoords.size())) {
430 error = "face texture index out of range at line " + std::to_string(line_number);
431 return false;
432 }
433 vertex.texcoord = texcoords[static_cast<std::size_t>(texcoord_index)];
434 }
435 if (index.normal != 0) {
437 if (normal_index < 0 || normal_index >= static_cast<int>(normals.size())) {
438 error = "face normal index out of range at line " + std::to_string(line_number);
439 return false;
440 }
441 }
442 face.push_back(vertex);
443 }
444
445 if (face.size() < 3) {
446 error = "face has fewer than three vertices at line " + std::to_string(line_number);
447 return false;
448 }
450 }
451 }
452
453 if (loaded.triangles.empty()) {
454 error = "no geometry found";
455 return false;
456 }
457 if (!loaded.material_library_path.empty() && !
load_mtl_file(loaded.material_library_path, loaded.materials, error)) {
458 return false;
459 }
460
461 result = std::move(loaded);
462 return true;
463 }
bool parse_obj_face_token(const std::string &token, OBJIndex &index)
bool load_mtl_file(const std::string &path, std::vector< OBJMaterial > &materials, std::string &error)
void triangulate_obj_face(const std::vector< OBJVertex > &face, const std::string &material_name, const std::string &object_name, std::vector< OBJTriangle > &triangles)
int resolve_obj_index(int index, const std::vector< T > &values)
std::array< float, 3 > position