1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 06:17:35 +00:00

3DFileViewer: Support textured models

Models that contain UV co-ordinates are now supported,
and will display with a texture wrapped around it, provided
a `bmp` with the same name as the object is in the same
directory as the 3D Model.
This commit is contained in:
Jesse Buhagiar 2021-05-23 23:57:53 +10:00 committed by Ali Mohammad Pur
parent 3287709df9
commit 343e66b816
7 changed files with 13657 additions and 9899 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 MiB

File diff suppressed because it is too large Load diff

View file

@ -16,9 +16,18 @@ struct Vertex {
GLfloat z; GLfloat z;
}; };
struct TexCoord {
GLfloat u;
GLfloat v;
};
// A triangle defines a single "face" of a mesh // A triangle defines a single "face" of a mesh
struct Triangle { struct Triangle {
GLuint a; GLuint a;
GLuint b; GLuint b;
GLuint c; GLuint c;
GLuint tex_coord_index0;
GLuint tex_coord_index1;
GLuint tex_coord_index2;
}; };

View file

@ -22,8 +22,9 @@ const Color colors[] {
Color::White Color::White
}; };
Mesh::Mesh(Vector<Vertex> vertices, Vector<Triangle> triangles) Mesh::Mesh(Vector<Vertex> vertices, Vector<TexCoord> tex_coords, Vector<Triangle> triangles)
: m_vertex_list(move(vertices)) : m_vertex_list(move(vertices))
, m_tex_coords(move(tex_coords))
, m_triangle_list(move(triangles)) , m_triangle_list(move(triangles))
{ {
} }
@ -74,18 +75,27 @@ void Mesh::draw()
m_vertex_list.at(m_triangle_list[i].a).y, m_vertex_list.at(m_triangle_list[i].a).y,
m_vertex_list.at(m_triangle_list[i].a).z); m_vertex_list.at(m_triangle_list[i].a).z);
if (is_textured())
glTexCoord2f(m_tex_coords.at(m_triangle_list[i].tex_coord_index0).u, 1.0f - m_tex_coords.at(m_triangle_list[i].tex_coord_index0).v);
// Vertex 2 // Vertex 2
glVertex3f( glVertex3f(
m_vertex_list.at(m_triangle_list[i].b).x, m_vertex_list.at(m_triangle_list[i].b).x,
m_vertex_list.at(m_triangle_list[i].b).y, m_vertex_list.at(m_triangle_list[i].b).y,
m_vertex_list.at(m_triangle_list[i].b).z); m_vertex_list.at(m_triangle_list[i].b).z);
if (is_textured())
glTexCoord2f(m_tex_coords.at(m_triangle_list[i].tex_coord_index1).u, 1.0f - m_tex_coords.at(m_triangle_list[i].tex_coord_index1).v);
// Vertex 3 // Vertex 3
glVertex3f( glVertex3f(
m_vertex_list.at(m_triangle_list[i].c).x, m_vertex_list.at(m_triangle_list[i].c).x,
m_vertex_list.at(m_triangle_list[i].c).y, m_vertex_list.at(m_triangle_list[i].c).y,
m_vertex_list.at(m_triangle_list[i].c).z); m_vertex_list.at(m_triangle_list[i].c).z);
if (is_textured())
glTexCoord2f(m_tex_coords.at(m_triangle_list[i].tex_coord_index2).u, 1.0f - m_tex_coords.at(m_triangle_list[i].tex_coord_index2).v);
glEnd(); glEnd();
} }
} }

View file

@ -16,7 +16,7 @@ class Mesh : public RefCounted<Mesh> {
public: public:
Mesh() = delete; Mesh() = delete;
Mesh(Vector<Vertex> vertices, Vector<Triangle> triangles); Mesh(Vector<Vertex> vertices, Vector<TexCoord> tex_coords, Vector<Triangle> triangles);
size_t vertex_count() const { return m_vertex_list.size(); } size_t vertex_count() const { return m_vertex_list.size(); }
@ -24,7 +24,10 @@ public:
void draw(); void draw();
bool is_textured() const { return m_tex_coords.size() > 0; }
private: private:
Vector<Vertex> m_vertex_list; Vector<Vertex> m_vertex_list;
Vector<TexCoord> m_tex_coords;
Vector<Triangle> m_triangle_list; Vector<Triangle> m_triangle_list;
}; };

View file

@ -12,6 +12,7 @@
RefPtr<Mesh> WavefrontOBJLoader::load(Core::File& file) RefPtr<Mesh> WavefrontOBJLoader::load(Core::File& file)
{ {
Vector<Vertex> vertices; Vector<Vertex> vertices;
Vector<TexCoord> tex_coords;
Vector<Triangle> triangles; Vector<Triangle> triangles;
dbgln("Wavefront: Loading {}...", file.name()); dbgln("Wavefront: Loading {}...", file.name());
@ -20,8 +21,26 @@ RefPtr<Mesh> WavefrontOBJLoader::load(Core::File& file)
for (auto line = file.line_begin(); !line.at_end(); ++line) { for (auto line = file.line_begin(); !line.at_end(); ++line) {
auto object_line = *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;
}
// FIXME: Parse texture coordinates and vertex normals // FIXME: Parse texture coordinates and vertex normals
if (object_line.starts_with("vt") || object_line.starts_with("vn")) { if (object_line.starts_with("vn")) {
continue; continue;
} }
@ -33,10 +52,9 @@ RefPtr<Mesh> WavefrontOBJLoader::load(Core::File& file)
return nullptr; return nullptr;
} }
vertices.append( vertices.append({ static_cast<GLfloat>(atof(String(vertex_line.at(1)).characters())),
{ 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(2)).characters())), static_cast<GLfloat>(atof(String(vertex_line.at(3)).characters())) });
static_cast<GLfloat>(atof(String(vertex_line.at(3)).characters())) });
} }
// This line describes a face (a collection of 3 vertices, aka a triangle) // This line describes a face (a collection of 3 vertices, aka a triangle)
else if (object_line.starts_with("f")) { else if (object_line.starts_with("f")) {
@ -46,19 +64,30 @@ RefPtr<Mesh> WavefrontOBJLoader::load(Core::File& file)
return nullptr; return nullptr;
} }
GLuint vert_index[3];
GLuint tex_coord_index[3];
if (object_line.contains("/")) { if (object_line.contains("/")) {
for (int i = 1; i <= 3; ++i) { for (int i = 1; i <= 3; ++i) {
face_line.at(i) = face_line.at(i).split_view("/").at(0); vert_index[i - 1] = face_line.at(i).split_view("/").at(0).to_uint().value_or(1);
tex_coord_index[i - 1] = face_line.at(i).split_view("/").at(1).to_uint().value_or(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;
} }
// Create a new triangle // Create a new triangle
triangles.append( triangles.append(
{ { vert_index[0] - 1,
face_line.at(1).to_uint().value() - 1, vert_index[1] - 1,
face_line.at(2).to_uint().value() - 1, vert_index[2] - 1,
face_line.at(3).to_uint().value() - 1, tex_coord_index[0] - 1,
}); tex_coord_index[1] - 1,
tex_coord_index[2] - 1 });
} }
} }
@ -68,5 +97,5 @@ RefPtr<Mesh> WavefrontOBJLoader::load(Core::File& file)
} }
dbgln("Wavefront: Done."); dbgln("Wavefront: Done.");
return adopt_ref(*new Mesh(vertices, triangles)); return adopt_ref(*new Mesh(vertices, tex_coords, triangles));
} }

View file

@ -137,8 +137,26 @@ bool GLContextWidget::load(const String& filename)
return false; return false;
} }
// Determine whether or not a texture for this model resides within the same directory
StringBuilder builder;
builder.append(filename.split('.').at(0));
builder.append(".bmp");
// Attempt to open the texture file from disk
auto texture_image = Gfx::Bitmap::load_from_file(builder.string_view());
GLuint tex;
glGenTextures(1, &tex);
if (texture_image) {
// Upload texture data to the GL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture_image->width(), texture_image->height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_image->scanline(0));
} else {
dbgln("3DFileViewer: Couldn't load texture for {}", filename);
}
m_mesh = new_mesh; m_mesh = new_mesh;
dbgln("3DFileViewer: mesh has {} triangles.", m_mesh->triangle_count()); dbgln("3DFileViewer: mesh has {} triangles.", m_mesh->triangle_count());
return true; return true;
} }