summaryrefslogtreecommitdiff
path: root/Demos/Cube
diff options
context:
space:
mode:
authorStephan Unverwerth <s.unverwerth@gmx.de>2020-04-18 12:23:56 +0200
committerAndreas Kling <kling@serenityos.org>2020-04-18 12:28:54 +0200
commit1806592d5819212c494146bd478b4c034f33514d (patch)
tree94bee920d4644a2494f18bee4c964b4e2486c008 /Demos/Cube
parent2dcc38d7bf076753921ce3110c9cb75e344efed8 (diff)
downloadserenity-1806592d5819212c494146bd478b4c034f33514d.zip
Demos: Add 3D Cube Demo
This renders a spinning 3D cube and demonstrates the 3D math classes from LibGfx.
Diffstat (limited to 'Demos/Cube')
-rw-r--r--Demos/Cube/Cube.cpp213
-rw-r--r--Demos/Cube/Makefile8
2 files changed, 221 insertions, 0 deletions
diff --git a/Demos/Cube/Cube.cpp b/Demos/Cube/Cube.cpp
new file mode 100644
index 0000000000..8222eebcf9
--- /dev/null
+++ b/Demos/Cube/Cube.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@gmx.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibCore/ElapsedTimer.h>
+#include <LibGUI/Application.h>
+#include <LibGUI/Label.h>
+#include <LibGUI/Painter.h>
+#include <LibGUI/Widget.h>
+#include <LibGUI/Window.h>
+#include <LibGfx/Bitmap.h>
+#include <LibGfx/Matrix4x4.h>
+#include <LibGfx/Vector3.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+const int WIDTH = 200;
+const int HEIGHT = 200;
+
+class Cube final : public GUI::Widget {
+ C_OBJECT(Cube)
+public:
+ virtual ~Cube() override;
+ void set_stat_label(RefPtr<GUI::Label> l) { m_stats = l; };
+
+private:
+ Cube();
+ RefPtr<Gfx::Bitmap> m_bitmap;
+ RefPtr<GUI::Label> m_stats;
+
+ virtual void paint_event(GUI::PaintEvent&) override;
+ virtual void timer_event(Core::TimerEvent&) override;
+
+ int m_accumulated_time;
+ int m_cycles;
+ int m_phase;
+};
+
+Cube::Cube()
+{
+ m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { WIDTH, HEIGHT });
+
+ m_accumulated_time = 0;
+ m_cycles = 0;
+ m_phase = 0;
+
+ stop_timer();
+ start_timer(20);
+}
+
+Cube::~Cube()
+{
+}
+
+void Cube::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 Cube::timer_event(Core::TimerEvent&)
+{
+ Core::ElapsedTimer timer;
+ timer.start();
+
+ const FloatVector3 vertices[8] {
+ { -1, -1, -1 },
+ { -1, 1, -1 },
+ { 1, 1, -1 },
+ { 1, -1, -1 },
+ { -1, -1, 1 },
+ { -1, 1, 1 },
+ { 1, 1, 1 },
+ { 1, -1, 1 },
+ };
+
+#define QUAD(a, b, c, d) a, b, c, c, d, a
+
+ const int indices[] {
+ QUAD(0, 1, 2, 3),
+ QUAD(7, 6, 5, 4),
+ QUAD(4, 5, 1, 0),
+ QUAD(3, 2, 6, 7),
+ QUAD(1, 5, 6, 2),
+ QUAD(0, 3, 7, 4),
+ };
+
+ const Color colors[] {
+ Color::Red,
+ Color::Red,
+ Color::Green,
+ Color::Green,
+ Color::Blue,
+ Color::Blue,
+ Color::Magenta,
+ Color::Magenta,
+ Color::White,
+ Color::White,
+ Color::Yellow,
+ Color::Yellow,
+ };
+
+ FloatVector3 transformed_vertices[8];
+
+ static float angle = 0;
+ angle += 0.02f;
+
+ auto matrix = FloatMatrix4x4::translate(FloatVector3(0, 0, 1.5f))
+ * FloatMatrix4x4::rotate(FloatVector3(1, 0, 0), angle * 1.17356641f)
+ * FloatMatrix4x4::rotate(FloatVector3(0, 1, 0), angle * 0.90533273f)
+ * FloatMatrix4x4::rotate(FloatVector3(0, 0, 1), angle);
+
+ for (int i = 0; i < 8; i++) {
+ transformed_vertices[i] = matrix.transform_point(vertices[i]);
+ }
+
+ GUI::Painter painter(*m_bitmap);
+ painter.fill_rect_with_gradient(Gfx::Orientation::Vertical, painter.clip_rect(), Gfx::Color::White, Gfx::Color::Blue);
+
+ auto to_point = [](const FloatVector3& v) {
+ return Gfx::Point(v.x(), v.y());
+ };
+
+ for (size_t i = 0; i < sizeof(indices) / sizeof(indices[0]) / 3; i++) {
+ auto a = transformed_vertices[indices[i * 3]];
+ auto b = transformed_vertices[indices[i * 3 + 1]];
+ auto c = transformed_vertices[indices[i * 3 + 2]];
+ auto normal = (b - a).cross(c - a);
+ normal.normalize();
+
+ // Perspective projection
+ a.set_x(WIDTH / 2 + a.x() / (1 + a.z() * 0.35) * WIDTH / 3);
+ a.set_y(HEIGHT / 2 - a.y() / (1 + a.z() * 0.35) * WIDTH / 3);
+ b.set_x(WIDTH / 2 + b.x() / (1 + b.z() * 0.35) * WIDTH / 3);
+ b.set_y(HEIGHT / 2 - b.y() / (1 + b.z() * 0.35) * WIDTH / 3);
+ c.set_x(WIDTH / 2 + c.x() / (1 + c.z() * 0.35) * WIDTH / 3);
+ c.set_y(HEIGHT / 2 - c.y() / (1 + c.z() * 0.35) * WIDTH / 3);
+
+ float winding = (b.x() - a.x()) * (c.y() - a.y()) - (b.y() - a.y()) * (c.x() - a.x());
+ if (winding < 0)
+ continue;
+
+ float shade = 0.5f + normal.y() * 0.5f;
+ auto color = colors[i];
+ color.set_red(color.red() * shade);
+ color.set_green(color.green() * shade);
+ color.set_blue(color.blue() * shade);
+
+ painter.draw_triangle(to_point(a), to_point(b), to_point(c), color);
+ }
+
+ if ((m_cycles % 50) == 0) {
+ dbgprintf("%d total cycles. finished 50 in %d ms, avg %d ms\n", m_cycles, m_accumulated_time, m_accumulated_time / 50);
+ m_stats->set_text(String::format("%d ms", m_accumulated_time / 50));
+ m_accumulated_time = 0;
+ }
+
+ update();
+
+ m_accumulated_time += timer.elapsed();
+ m_cycles++;
+}
+
+int main(int argc, char** argv)
+{
+ GUI::Application app(argc, argv);
+
+ auto window = GUI::Window::construct();
+ window->set_double_buffering_enabled(true);
+ window->set_title("Cube");
+ window->set_resizable(false);
+ window->set_rect(100, 100, WIDTH, HEIGHT);
+
+ auto& cube = window->set_main_widget<Cube>();
+
+ auto& time = cube.add<GUI::Label>();
+ time.set_relative_rect({ 0, 4, 40, 10 });
+ time.move_by({ window->width() - time.width(), 0 });
+ time.set_foreground_color(Color::from_rgb(0x222222));
+ cube.set_stat_label(time);
+
+ window->show();
+ window->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-demo.png"));
+
+ return app.exec();
+}
diff --git a/Demos/Cube/Makefile b/Demos/Cube/Makefile
new file mode 100644
index 0000000000..28ea8daab0
--- /dev/null
+++ b/Demos/Cube/Makefile
@@ -0,0 +1,8 @@
+OBJS = \
+ Cube.o
+
+PROGRAM = Cube
+
+LIB_DEPS = GUI IPC Gfx Core
+
+include ../../Makefile.common