Cube.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*
  2. * Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibCore/ArgsParser.h>
  7. #include <LibCore/ElapsedTimer.h>
  8. #include <LibGUI/Application.h>
  9. #include <LibGUI/Icon.h>
  10. #include <LibGUI/Label.h>
  11. #include <LibGUI/Menu.h>
  12. #include <LibGUI/Menubar.h>
  13. #include <LibGUI/Painter.h>
  14. #include <LibGUI/Widget.h>
  15. #include <LibGUI/Window.h>
  16. #include <LibGfx/Bitmap.h>
  17. #include <LibGfx/Matrix4x4.h>
  18. #include <LibGfx/Vector3.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <unistd.h>
  22. const int WIDTH = 200;
  23. const int HEIGHT = 200;
  24. static bool flag_hide_window_frame = false;
  25. class Cube final : public GUI::Widget {
  26. C_OBJECT(Cube)
  27. public:
  28. virtual ~Cube() override;
  29. void set_stat_label(RefPtr<GUI::Label> l) { m_stats = l; };
  30. void set_show_window_frame(bool);
  31. bool show_window_frame() const { return m_show_window_frame; }
  32. Function<void(GUI::ContextMenuEvent&)> on_context_menu_request;
  33. protected:
  34. virtual void context_menu_event(GUI::ContextMenuEvent& event) override
  35. {
  36. if (on_context_menu_request)
  37. on_context_menu_request(event);
  38. }
  39. private:
  40. Cube();
  41. RefPtr<Gfx::Bitmap> m_bitmap;
  42. RefPtr<GUI::Label> m_stats;
  43. virtual void paint_event(GUI::PaintEvent&) override;
  44. virtual void timer_event(Core::TimerEvent&) override;
  45. int m_accumulated_time;
  46. int m_cycles;
  47. int m_phase;
  48. bool m_show_window_frame { true };
  49. };
  50. Cube::Cube()
  51. {
  52. m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { WIDTH, HEIGHT });
  53. m_accumulated_time = 0;
  54. m_cycles = 0;
  55. m_phase = 0;
  56. stop_timer();
  57. start_timer(20);
  58. }
  59. Cube::~Cube()
  60. {
  61. }
  62. void Cube::paint_event(GUI::PaintEvent& event)
  63. {
  64. GUI::Painter painter(*this);
  65. painter.add_clip_rect(event.rect());
  66. painter.draw_scaled_bitmap(rect(), *m_bitmap, m_bitmap->rect());
  67. }
  68. void Cube::timer_event(Core::TimerEvent&)
  69. {
  70. Core::ElapsedTimer timer;
  71. timer.start();
  72. const FloatVector3 vertices[8] {
  73. { -1, -1, -1 },
  74. { -1, 1, -1 },
  75. { 1, 1, -1 },
  76. { 1, -1, -1 },
  77. { -1, -1, 1 },
  78. { -1, 1, 1 },
  79. { 1, 1, 1 },
  80. { 1, -1, 1 },
  81. };
  82. #define QUAD(a, b, c, d) a, b, c, c, d, a
  83. const int indices[] {
  84. QUAD(0, 1, 2, 3),
  85. QUAD(7, 6, 5, 4),
  86. QUAD(4, 5, 1, 0),
  87. QUAD(3, 2, 6, 7),
  88. QUAD(1, 5, 6, 2),
  89. QUAD(0, 3, 7, 4),
  90. };
  91. const Color colors[] {
  92. Color::Red,
  93. Color::Red,
  94. Color::Green,
  95. Color::Green,
  96. Color::Blue,
  97. Color::Blue,
  98. Color::Magenta,
  99. Color::Magenta,
  100. Color::White,
  101. Color::White,
  102. Color::Yellow,
  103. Color::Yellow,
  104. };
  105. FloatVector3 transformed_vertices[8];
  106. static float angle = 0;
  107. angle += 0.02f;
  108. auto matrix = Gfx::translation_matrix(FloatVector3(0, 0, 1.5f))
  109. * Gfx::rotation_matrix(FloatVector3(1, 0, 0), angle * 1.17356641f)
  110. * Gfx::rotation_matrix(FloatVector3(0, 1, 0), angle * 0.90533273f)
  111. * Gfx::rotation_matrix(FloatVector3(0, 0, 1), angle);
  112. for (int i = 0; i < 8; i++) {
  113. transformed_vertices[i] = transform_point(matrix, vertices[i]);
  114. }
  115. GUI::Painter painter(*m_bitmap);
  116. if (m_show_window_frame)
  117. painter.fill_rect_with_gradient(Gfx::Orientation::Vertical, m_bitmap->rect(), Gfx::Color::White, Gfx::Color::Blue);
  118. else
  119. painter.clear_rect(m_bitmap->rect(), Gfx::Color::Transparent);
  120. auto to_point = [](const FloatVector3& v) {
  121. return Gfx::IntPoint(v.x(), v.y());
  122. };
  123. for (size_t i = 0; i < sizeof(indices) / sizeof(indices[0]) / 3; i++) {
  124. auto a = transformed_vertices[indices[i * 3]];
  125. auto b = transformed_vertices[indices[i * 3 + 1]];
  126. auto c = transformed_vertices[indices[i * 3 + 2]];
  127. auto normal = (b - a).cross(c - a);
  128. normal.normalize();
  129. // Perspective projection
  130. a.set_x(WIDTH / 2 + a.x() / (1 + a.z() * 0.35f) * WIDTH / 3);
  131. a.set_y(HEIGHT / 2 - a.y() / (1 + a.z() * 0.35f) * WIDTH / 3);
  132. b.set_x(WIDTH / 2 + b.x() / (1 + b.z() * 0.35f) * WIDTH / 3);
  133. b.set_y(HEIGHT / 2 - b.y() / (1 + b.z() * 0.35f) * WIDTH / 3);
  134. c.set_x(WIDTH / 2 + c.x() / (1 + c.z() * 0.35f) * WIDTH / 3);
  135. c.set_y(HEIGHT / 2 - c.y() / (1 + c.z() * 0.35f) * WIDTH / 3);
  136. float winding = (b.x() - a.x()) * (c.y() - a.y()) - (b.y() - a.y()) * (c.x() - a.x());
  137. if (winding < 0)
  138. continue;
  139. float shade = 0.5f + normal.y() * 0.5f;
  140. auto color = colors[i];
  141. color.set_red(color.red() * shade);
  142. color.set_green(color.green() * shade);
  143. color.set_blue(color.blue() * shade);
  144. painter.draw_triangle(to_point(a), to_point(b), to_point(c), color);
  145. }
  146. if ((m_cycles % 50) == 0) {
  147. dbgln("{} total cycles. finished 50 in {} ms, avg {} ms", m_cycles, m_accumulated_time, m_accumulated_time / 50);
  148. m_stats->set_text(String::formatted("{} ms", m_accumulated_time / 50));
  149. m_accumulated_time = 0;
  150. }
  151. update();
  152. m_accumulated_time += timer.elapsed();
  153. m_cycles++;
  154. }
  155. void Cube::set_show_window_frame(bool show)
  156. {
  157. if (show == m_show_window_frame)
  158. return;
  159. m_show_window_frame = show;
  160. m_stats->set_visible(m_show_window_frame);
  161. auto& w = *window();
  162. w.set_frameless(!m_show_window_frame);
  163. w.set_has_alpha_channel(!m_show_window_frame);
  164. w.set_alpha_hit_threshold(m_show_window_frame ? 0 : 1);
  165. }
  166. int main(int argc, char** argv)
  167. {
  168. auto app = GUI::Application::construct(argc, argv);
  169. if (pledge("stdio recvfd sendfd rpath", nullptr) < 0) {
  170. perror("pledge");
  171. return 1;
  172. }
  173. if (unveil("/res", "r") < 0) {
  174. perror("unveil");
  175. return 1;
  176. }
  177. if (unveil(nullptr, nullptr) < 0) {
  178. perror("unveil");
  179. return 1;
  180. }
  181. Core::ArgsParser parser;
  182. parser.set_general_help("Create a window with a spinning cube.");
  183. parser.add_option(flag_hide_window_frame, "Hide window frame", "hide-window", 'h');
  184. parser.parse(argc, argv);
  185. auto window = GUI::Window::construct();
  186. window->set_double_buffering_enabled(true);
  187. window->set_title("Cube");
  188. window->set_resizable(false);
  189. window->resize(WIDTH, HEIGHT);
  190. window->set_has_alpha_channel(true);
  191. window->set_alpha_hit_threshold(1);
  192. auto& cube = window->set_main_widget<Cube>();
  193. auto& time = cube.add<GUI::Label>();
  194. time.set_relative_rect({ 0, 4, 40, 10 });
  195. time.move_by({ window->width() - time.width(), 0 });
  196. cube.set_stat_label(time);
  197. auto app_icon = GUI::Icon::default_icon("app-cube");
  198. window->set_icon(app_icon.bitmap_for_size(16));
  199. auto menubar = GUI::Menubar::construct();
  200. auto& file_menu = menubar->add_menu("&File");
  201. auto show_window_frame_action = GUI::Action::create_checkable("Show Window &Frame", [&](auto& action) {
  202. cube.set_show_window_frame(action.is_checked());
  203. });
  204. cube.set_show_window_frame(!flag_hide_window_frame);
  205. show_window_frame_action->set_checked(cube.show_window_frame());
  206. file_menu.add_action(move(show_window_frame_action));
  207. file_menu.add_separator();
  208. file_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); }));
  209. auto& help_menu = menubar->add_menu("&Help");
  210. help_menu.add_action(GUI::CommonActions::make_about_action("Cube Demo", app_icon, window));
  211. window->set_menubar(move(menubar));
  212. cube.on_context_menu_request = [&](auto& event) {
  213. file_menu.popup(event.screen_position());
  214. };
  215. window->show();
  216. return app->exec();
  217. }