/* * Copyright (c) 2021, Jesse Buhagiar * Copyright (c) 2021, Mathieu Gaillard * Copyright (c) 2021, Pedro Pereira * * SPDX-License-Identifier: BSD-2-Clause */ #include "WavefrontOBJLoader.h" #include #include #include #include static inline GLuint get_index_value(StringView& representation) { return representation.to_uint().value_or(1) - 1; } ErrorOr> WavefrontOBJLoader::load(String const& filename, NonnullOwnPtr file) { auto buffered_file = TRY(Core::BufferedFile::create(move(file))); Vector vertices; Vector normals; Vector tex_coords; Vector triangles; dbgln("Wavefront: Loading {}...", filename); // Start reading file line by line auto buffer = TRY(ByteBuffer::create_uninitialized(PAGE_SIZE)); while (TRY(buffered_file->can_read_line())) { auto object_line = TRY(buffered_file->read_line(buffer)); // Ignore file comments if (object_line.starts_with('#')) continue; if (object_line.starts_with("vt"sv)) { auto tex_coord_line = object_line.split_view(' '); if (tex_coord_line.size() != 3) { return Error::from_string_literal("Wavefront: Malformed TexCoord line."); } tex_coords.append({ static_cast(atof(DeprecatedString(tex_coord_line.at(1)).characters())), static_cast(atof(DeprecatedString(tex_coord_line.at(2)).characters())) }); continue; } if (object_line.starts_with("vn"sv)) { auto normal_line = object_line.split_view(' '); if (normal_line.size() != 4) { return Error::from_string_literal("Wavefront: Malformed vertex normal line."); } normals.append({ static_cast(atof(DeprecatedString(normal_line.at(1)).characters())), static_cast(atof(DeprecatedString(normal_line.at(2)).characters())), static_cast(atof(DeprecatedString(normal_line.at(3)).characters())) }); continue; } // This line describes a vertex (a position in 3D space) if (object_line.starts_with('v')) { auto vertex_line = object_line.split_view(' '); if (vertex_line.size() != 4) { return Error::from_string_literal("Wavefront: Malformed vertex line."); } vertices.append({ static_cast(atof(DeprecatedString(vertex_line.at(1)).characters())), static_cast(atof(DeprecatedString(vertex_line.at(2)).characters())), static_cast(atof(DeprecatedString(vertex_line.at(3)).characters())) }); continue; } // This line describes a face (a collection of 3+ vertices, aka a triangle or polygon) if (object_line.starts_with('f')) { auto face_line = object_line.substring_view(2).split_view(' '); auto number_of_vertices = face_line.size(); if (number_of_vertices < 3) { return Error::from_string_literal("Wavefront: Malformed face line."); } auto vertex_indices = TRY(FixedArray::create(number_of_vertices)); auto tex_coord_indices = TRY(FixedArray::create(number_of_vertices)); auto normal_indices = TRY(FixedArray::create(number_of_vertices)); for (size_t i = 0; i < number_of_vertices; ++i) { auto vertex_parts = face_line.at(i).split_view('/', SplitBehavior::KeepEmpty); vertex_indices[i] = get_index_value(vertex_parts[0]); tex_coord_indices[i] = (vertex_parts.size() >= 2) ? get_index_value(vertex_parts[1]) : 0; normal_indices[i] = (vertex_parts.size() >= 3) ? get_index_value(vertex_parts[2]) : 0; } // Create a triangle for each part of the polygon for (size_t i = 0; i < number_of_vertices - 2; ++i) { triangles.append({ vertex_indices[0], vertex_indices[i + 1], vertex_indices[i + 2], tex_coord_indices[0], tex_coord_indices[i + 1], tex_coord_indices[i + 2], normal_indices[0], normal_indices[i + 1], normal_indices[i + 2], }); } } } if (vertices.is_empty()) { return Error::from_string_literal("Wavefront: Failed to read any data from 3D file"); } dbgln("Wavefront: Done."); return adopt_ref(*new Mesh(vertices, tex_coords, normals, triangles)); }