diff options
Diffstat (limited to 'Userland/Demos')
-rw-r--r-- | Userland/Demos/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Demos/GLTeapot/CMakeLists.txt | 8 | ||||
-rw-r--r-- | Userland/Demos/GLTeapot/Common.h | 23 | ||||
-rw-r--r-- | Userland/Demos/GLTeapot/Mesh.cpp | 42 | ||||
-rw-r--r-- | Userland/Demos/GLTeapot/Mesh.h | 29 | ||||
-rw-r--r-- | Userland/Demos/GLTeapot/MeshLoader.h | 20 | ||||
-rw-r--r-- | Userland/Demos/GLTeapot/WavefrontOBJLoader.cpp | 59 | ||||
-rw-r--r-- | Userland/Demos/GLTeapot/WavefrontOBJLoader.h | 21 | ||||
-rw-r--r-- | Userland/Demos/GLTeapot/main.cpp | 126 |
9 files changed, 329 insertions, 0 deletions
diff --git a/Userland/Demos/CMakeLists.txt b/Userland/Demos/CMakeLists.txt index 2239368dac..0d150f24bc 100644 --- a/Userland/Demos/CMakeLists.txt +++ b/Userland/Demos/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(CatDog) add_subdirectory(Cube) add_subdirectory(Eyes) add_subdirectory(Fire) +add_subdirectory(GLTeapot) add_subdirectory(LibGfxDemo) add_subdirectory(LibGfxScaleDemo) add_subdirectory(Mouse) diff --git a/Userland/Demos/GLTeapot/CMakeLists.txt b/Userland/Demos/GLTeapot/CMakeLists.txt new file mode 100644 index 0000000000..ff6420a889 --- /dev/null +++ b/Userland/Demos/GLTeapot/CMakeLists.txt @@ -0,0 +1,8 @@ +set(SOURCES + Mesh.cpp + WavefrontOBJLoader.cpp + main.cpp +) + +serenity_app(GLTeapot ICON app-teapot) +target_link_libraries(GLTeapot LibGUI LibGL) diff --git a/Userland/Demos/GLTeapot/Common.h b/Userland/Demos/GLTeapot/Common.h new file mode 100644 index 0000000000..feee346014 --- /dev/null +++ b/Userland/Demos/GLTeapot/Common.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibGL/GL/gl.h> + +// Point in 3D space +struct Vertex { + GLfloat x; + GLfloat y; + GLfloat z; +}; + +// A triangle defines a single "face" of a mesh +struct Triangle { + Vertex a; + Vertex b; + Vertex c; +}; diff --git a/Userland/Demos/GLTeapot/Mesh.cpp b/Userland/Demos/GLTeapot/Mesh.cpp new file mode 100644 index 0000000000..89329186a4 --- /dev/null +++ b/Userland/Demos/GLTeapot/Mesh.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibGL/GL/gl.h> +#include <LibGfx/Color.h> +#include <stdlib.h> + +#include "Mesh.h" + +const Color colors[] { + Color::Red, + Color::Green, + Color::Blue, + Color::Blue, + Color::Magenta, + Color::White, + Color::Yellow, +}; + +void Mesh::draw() +{ + u32 color_index = 0; + Color cur_color; + + for (const auto& triangle : m_triangle_list) { + cur_color = colors[color_index]; + + glBegin(GL_TRIANGLES); + glColor4ub(cur_color.red(), cur_color.green(), cur_color.blue(), 255); + + glVertex3f(triangle.a.x, triangle.a.y, triangle.a.z); // Vertex 1 + glVertex3f(triangle.b.x, triangle.b.y, triangle.b.z); // Vertex 2 + glVertex3f(triangle.c.x, triangle.c.y, triangle.c.z); // Vertex 3 + + glEnd(); + + color_index = ((color_index + 1) % 7); + } +} diff --git a/Userland/Demos/GLTeapot/Mesh.h b/Userland/Demos/GLTeapot/Mesh.h new file mode 100644 index 0000000000..f0e2808f08 --- /dev/null +++ b/Userland/Demos/GLTeapot/Mesh.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/RefCounted.h> +#include <AK/Vector.h> + +#include "Common.h" + +// NOTE: We don't support indexed +class Mesh : public RefCounted<Mesh> { +public: + Mesh() = delete; + Mesh(const Vector<Triangle>& triangles) + : m_triangle_list(triangles) + { + } + + void draw(); + + size_t triangle_count() const { return m_triangle_list.size(); } + +private: + Vector<Triangle> m_triangle_list; +}; diff --git a/Userland/Demos/GLTeapot/MeshLoader.h b/Userland/Demos/GLTeapot/MeshLoader.h new file mode 100644 index 0000000000..fd62d49e64 --- /dev/null +++ b/Userland/Demos/GLTeapot/MeshLoader.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/String.h> + +#include "Common.h" +#include "Mesh.h" + +class MeshLoader { +public: + MeshLoader() { } + virtual ~MeshLoader() { } + + virtual RefPtr<Mesh> load(const String& fname) = 0; +}; diff --git a/Userland/Demos/GLTeapot/WavefrontOBJLoader.cpp b/Userland/Demos/GLTeapot/WavefrontOBJLoader.cpp new file mode 100644 index 0000000000..677770ff03 --- /dev/null +++ b/Userland/Demos/GLTeapot/WavefrontOBJLoader.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "WavefrontOBJLoader.h" +#include <LibCore/File.h> +#include <stdlib.h> + +RefPtr<Mesh> WavefrontOBJLoader::load(const String& fname) +{ + auto obj_file_or_error = Core::File::open(fname, Core::IODevice::OpenMode::ReadOnly); + Vector<Vertex> vertices; + Vector<Triangle> triangles; + + dbgln("Wavefront: Loading {}...", fname); + + if (obj_file_or_error.is_error()) + return nullptr; + + // Start reading file line by line + for (auto line = obj_file_or_error.value()->line_begin(); !line.at_end(); ++line) { + auto object_line = *line; + + // 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())) }); + } + // This line describes a face (a collection of 3 vertices, aka a triangle) + else 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; + } + + // Create a new triangle + triangles.append( + { + vertices.at(face_line.at(1).to_uint().value() - 1), + vertices.at(face_line.at(2).to_uint().value() - 1), + vertices.at(face_line.at(3).to_uint().value() - 1), + }); + } + } + + dbgln("Wavefront: Done."); + return adopt_ref(*new Mesh(triangles)); +} diff --git a/Userland/Demos/GLTeapot/WavefrontOBJLoader.h b/Userland/Demos/GLTeapot/WavefrontOBJLoader.h new file mode 100644 index 0000000000..92ef601577 --- /dev/null +++ b/Userland/Demos/GLTeapot/WavefrontOBJLoader.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/RefCounted.h> +#include <AK/RefPtr.h> + +#include "Mesh.h" +#include "MeshLoader.h" + +class WavefrontOBJLoader : public MeshLoader { +public: + WavefrontOBJLoader() { } + ~WavefrontOBJLoader() override { } + + RefPtr<Mesh> load(const String& fname) override; +}; diff --git a/Userland/Demos/GLTeapot/main.cpp b/Userland/Demos/GLTeapot/main.cpp new file mode 100644 index 0000000000..0107db298f --- /dev/null +++ b/Userland/Demos/GLTeapot/main.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibCore/ElapsedTimer.h> +#include <LibGL/GL/gl.h> +#include <LibGL/GLContext.h> +#include <LibGUI/Application.h> +#include <LibGUI/Icon.h> +#include <LibGUI/Painter.h> +#include <LibGUI/Widget.h> +#include <LibGUI/Window.h> +#include <LibGfx/Bitmap.h> +#include <unistd.h> + +#include "Mesh.h" +#include "MeshLoader.h" +#include "WavefrontOBJLoader.h" + +static constexpr u16 RENDER_WIDTH = 640; +static constexpr u16 RENDER_HEIGHT = 480; + +class GLContextWidget final : public GUI::Widget { + C_OBJECT(GLContextWidget) +public: +private: + GLContextWidget() + { + m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { RENDER_WIDTH, RENDER_HEIGHT }); + m_context = GL::create_context(*m_bitmap); + + start_timer(20); + + GL::make_context_current(m_context); + glFrontFace(GL_CW); + glEnable(GL_CULL_FACE); + + // Set projection matrix + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-0.5, 0.5, -0.5, 0.5, 1, 1500); + + // Load the teapot + auto mesh_loader = adopt_own(*new WavefrontOBJLoader()); + m_teapot = mesh_loader->load("/res/gl/teapot.obj"); + + dbgln("GLTeapot: teapot mesh has {} triangles.", m_teapot->triangle_count()); + } + + virtual void paint_event(GUI::PaintEvent&) override; + virtual void timer_event(Core::TimerEvent&) override; + +private: + RefPtr<Mesh> m_teapot; + RefPtr<Gfx::Bitmap> m_bitmap; + OwnPtr<GL::GLContext> m_context; +}; + +void GLContextWidget::paint_event(GUI::PaintEvent& event) +{ + GUI::Painter painter(*this); + painter.add_clip_rect(event.rect()); + + /* Blit it! */ + painter.draw_scaled_bitmap(event.rect(), *m_bitmap, m_bitmap->rect()); +} + +void GLContextWidget::timer_event(Core::TimerEvent&) +{ + static float angle = 0.0f; + + angle -= 0.01f; + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + auto matrix = FloatMatrix4x4::translate(FloatVector3(0, 0, -8.5)) + * FloatMatrix4x4::rotate(FloatVector3(1, 0, 0), angle) + * FloatMatrix4x4::rotate(FloatVector3(0, 1, 0), 0.0f) + * FloatMatrix4x4::rotate(FloatVector3(0, 0, 1), angle); + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf((float*)matrix.elements()); + + m_teapot->draw(); + + m_context->present(); + update(); +} + +int main(int argc, char** argv) +{ + auto app = GUI::Application::construct(argc, argv); + + if (pledge("stdio recvfd sendfd rpath", nullptr) < 0) { + perror("pledge"); + return 1; + } + + if (unveil("/res", "r") < 0) { + perror("unveil"); + return 1; + } + + if (unveil(nullptr, nullptr) < 0) { + perror("unveil"); + return 1; + } + + // Construct the main window + auto window = GUI::Window::construct(); + auto app_icon = GUI::Icon::default_icon("app-teapot"); + + window->set_icon(app_icon.bitmap_for_size(16)); + window->set_title("GLTeapot"); + window->resize(640, 480); + window->set_resizable(false); + window->set_double_buffering_enabled(true); + window->set_main_widget<GLContextWidget>(); + + window->show(); + + return app->exec(); +} |