summaryrefslogtreecommitdiff
path: root/Userland/Applications/3DFileViewer/WavefrontOBJLoader.cpp
blob: 4ef2a87c13b8920d877f9c811eff1475ef830c1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
 * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
 * Copyright (c) 2021, Mathieu Gaillard <gaillard.mathieu.39@gmail.com>
 * Copyright (c) 2021, Pedro Pereira <pmh.pereira@gmail.com>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include "WavefrontOBJLoader.h"
#include <LibCore/File.h>
#include <stdlib.h>

RefPtr<Mesh> WavefrontOBJLoader::load(Core::File& file)
{
    Vector<Vertex> vertices;
    Vector<Vertex> normals;
    Vector<TexCoord> tex_coords;
    Vector<Triangle> triangles;

    dbgln("Wavefront: Loading {}...", file.name());

    // Start reading file line by line
    for (auto line = file.line_begin(); !line.at_end(); ++line) {
        auto object_line = *line;

        // Ignore file comments
        if (object_line.starts_with("#"))
            continue;

        if (object_line.starts_with("vt")) {
            auto tex_coord_line = object_line.split_view(' ');
            if (tex_coord_line.size() != 3) {
                dbgln("Wavefront: Malformed TexCoord line. Aborting.");
                dbgln("{}", object_line);
                return nullptr;
            }

            tex_coords.append({ static_cast<GLfloat>(atof(String(tex_coord_line.at(1)).characters())),
                static_cast<GLfloat>(atof(String(tex_coord_line.at(2)).characters())) });

            continue;
        }

        if (object_line.starts_with("vn")) {
            auto normal_line = object_line.split_view(' ');
            if (normal_line.size() != 4) {
                dbgln("Wavefront: Malformed vertex normal line. Aborting.");
                return nullptr;
            }

            normals.append({ static_cast<GLfloat>(atof(String(normal_line.at(1)).characters())),
                static_cast<GLfloat>(atof(String(normal_line.at(2)).characters())),
                static_cast<GLfloat>(atof(String(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) {
                dbgln("Wavefront: Malformed vertex line. Aborting.");
                return nullptr;
            }

            vertices.append({ static_cast<GLfloat>(atof(String(vertex_line.at(1)).characters())),
                static_cast<GLfloat>(atof(String(vertex_line.at(2)).characters())),
                static_cast<GLfloat>(atof(String(vertex_line.at(3)).characters())) });

            continue;
        }

        // This line describes a face (a collection of 3 vertices, aka a triangle)
        if (object_line.starts_with("f")) {
            auto face_line = object_line.split_view(' ');
            if (face_line.size() != 4) {
                dbgln("Wavefront: Malformed face line. Aborting.");
                return nullptr;
            }

            GLuint vert_index[3];
            GLuint tex_coord_index[3];
            GLuint normal_index[3];
            if (object_line.contains("/")) {
                for (int i = 1; i <= 3; ++i) {
                    auto vertex_data = face_line.at(i).split_view("/", true);

                    vert_index[i - 1] = vertex_data.at(0).to_uint().value_or(1);
                    tex_coord_index[i - 1] = vertex_data.at(1).to_uint().value_or(1);

                    if (vertex_data.size() == 3)
                        normal_index[i - 1] = vertex_data.at(2).to_uint().value_or(1);
                    else
                        normal_index[i - 1] = 1;
                }
            } else {
                vert_index[0] = (face_line.at(1).to_uint().value_or(1));
                vert_index[1] = (face_line.at(2).to_uint().value_or(1));
                vert_index[2] = (face_line.at(3).to_uint().value_or(1));
                tex_coord_index[0] = 0;
                tex_coord_index[1] = 0;
                tex_coord_index[2] = 0;
                normal_index[0] = 0;
                normal_index[1] = 0;
                normal_index[2] = 0;
            }

            // Create a new triangle
            triangles.append(
                {
                    vert_index[0] - 1,
                    vert_index[1] - 1,
                    vert_index[2] - 1,
                    tex_coord_index[0] - 1,
                    tex_coord_index[1] - 1,
                    tex_coord_index[2] - 1,
                    normal_index[0] - 1,
                    normal_index[1] - 1,
                    normal_index[2] - 1,
                });
        }
    }

    if (vertices.is_empty()) {
        dbgln("Wavefront: Failed to read any data from 3D file: {}", file.name());
        return nullptr;
    }

    dbgln("Wavefront: Done.");
    return adopt_ref(*new Mesh(vertices, tex_coords, normals, triangles));
}