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:
parent
3287709df9
commit
343e66b816
7 changed files with 13657 additions and 9899 deletions
BIN
Base/home/anon/Documents/3D Models/teapot.bmp
Normal file
BIN
Base/home/anon/Documents/3D Models/teapot.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 MiB |
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue