355 {
356 std::ifstream file(path);
357 if (!file.is_open()) {
358 error = "could not open file";
359 return false;
360 }
361
362 std::vector<std::array<float, 3>> positions;
363 std::vector<std::array<float, 2>> texcoords;
364 std::vector<std::array<float, 3>> normals;
365 OBJLoadResult loaded;
366 loaded.object_name = std::filesystem::path(path).stem().string();
367 std::string current_object = loaded.object_name;
368 std::string current_material;
369 std::string line;
370 std::size_t line_number = 0;
371
372 while (std::getline(file, line)) {
373 ++line_number;
375 if (line.empty()) {
376 continue;
377 }
378
379 std::istringstream stream(line);
380 std::string tag;
381 stream >> tag;
382 if (tag == "v") {
383 std::array<float, 3> position{};
384 if (!(stream >> position[0] >> position[1] >> position[2]) ||
385 !std::isfinite(position[0]) || !std::isfinite(position[1]) || !std::isfinite(position[2])) {
386 error = "invalid vertex at line " + std::to_string(line_number);
387 return false;
388 }
389 positions.push_back(position);
390 } else if (tag == "vt") {
391 std::array<float, 2> texcoord{};
392 if (!(stream >> texcoord[0] >> texcoord[1]) ||
393 !std::isfinite(texcoord[0]) || !std::isfinite(texcoord[1])) {
394 error = "invalid texture coordinate at line " + std::to_string(line_number);
395 return false;
396 }
397 texcoords.push_back(texcoord);
398 } else if (tag == "vn") {
399 std::array<float, 3> normal{};
400 if (!(stream >> normal[0] >> normal[1] >> normal[2]) ||
401 !std::isfinite(normal[0]) || !std::isfinite(normal[1]) || !std::isfinite(normal[2])) {
402 error = "invalid normal at line " + std::to_string(line_number);
403 return false;
404 }
405 normals.push_back(normal);
406 } else if (tag == "o") {
407 std::string name;
408 std::getline(stream, name);
410 if (!name.empty()) {
411 current_object = name;
412 loaded.object_name = std::move(name);
413 }
414 } else if (tag == "mtllib") {
415 std::string library;
416 std::getline(stream, library);
418 if (!library.empty() && loaded.material_library_path.empty()) {
419 loaded.material_library_path =
resolve_obj_path(std::filesystem::path(path).parent_path(), library);
420 }
421 } else if (tag == "usemtl") {
422 std::getline(stream, current_material);
424 } else if (tag == "f") {
425 std::vector<OBJVertex> face;
426 std::string token;
427 while (stream >> token) {
430 error = "malformed face token '" + token + "' at line " + std::to_string(line_number);
431 return false;
432 }
433
435 if (position_index < 0 || position_index >= static_cast<int>(positions.size())) {
436 error = "face position index out of range at line " + std::to_string(line_number);
437 return false;
438 }
439
441 vertex.
position = positions[
static_cast<std::size_t
>(position_index)];
442 if (index.texcoord != 0) {
444 if (texcoord_index < 0 || texcoord_index >= static_cast<int>(texcoords.size())) {
445 error = "face texture index out of range at line " + std::to_string(line_number);
446 return false;
447 }
448 vertex.texcoord = texcoords[static_cast<std::size_t>(texcoord_index)];
449 }
450 if (index.normal != 0) {
452 if (normal_index < 0 || normal_index >= static_cast<int>(normals.size())) {
453 error = "face normal index out of range at line " + std::to_string(line_number);
454 return false;
455 }
456 }
457 face.push_back(vertex);
458 }
459
460 if (face.size() < 3) {
461 error = "face has fewer than three vertices at line " + std::to_string(line_number);
462 return false;
463 }
465 }
466 }
467
468 if (loaded.triangles.empty()) {
469 error = "no geometry found";
470 return false;
471 }
472 if (!loaded.material_library_path.empty() &&
473 !
load_mtl_file(loaded.material_library_path, loaded.materials, error)) {
474 return false;
475 }
476
477 result = std::move(loaded);
478 return true;
479 }
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