mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 13:48:12 +00:00
Demos: Add Tubes :^)
This commit is contained in:
parent
16ca9ec762
commit
9dfe50170e
10 changed files with 557 additions and 0 deletions
4
Base/res/apps/Tubes.af
Normal file
4
Base/res/apps/Tubes.af
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[App]
|
||||||
|
Name=Tubes
|
||||||
|
Executable=/bin/Tubes
|
||||||
|
Category=Demos
|
BIN
Base/res/icons/16x16/app-tubes.png
Normal file
BIN
Base/res/icons/16x16/app-tubes.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 171 B |
BIN
Base/res/icons/32x32/app-tubes.png
Normal file
BIN
Base/res/icons/32x32/app-tubes.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
|
@ -9,5 +9,6 @@ add_subdirectory(ModelGallery)
|
||||||
add_subdirectory(Mouse)
|
add_subdirectory(Mouse)
|
||||||
add_subdirectory(Screensaver)
|
add_subdirectory(Screensaver)
|
||||||
add_subdirectory(Starfield)
|
add_subdirectory(Starfield)
|
||||||
|
add_subdirectory(Tubes)
|
||||||
add_subdirectory(VirGLDemo)
|
add_subdirectory(VirGLDemo)
|
||||||
add_subdirectory(WidgetGallery)
|
add_subdirectory(WidgetGallery)
|
||||||
|
|
13
Userland/Demos/Tubes/CMakeLists.txt
Normal file
13
Userland/Demos/Tubes/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
serenity_component(
|
||||||
|
Tubes
|
||||||
|
TARGETS Tubes
|
||||||
|
)
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
Shapes.cpp
|
||||||
|
Tubes.cpp
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
serenity_app(Tubes ICON app-tubes)
|
||||||
|
target_link_libraries(Tubes LibGUI LibCore LibGfx LibGL LibMain)
|
82
Userland/Demos/Tubes/Shapes.cpp
Normal file
82
Userland/Demos/Tubes/Shapes.cpp
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/Array.h>
|
||||||
|
#include <Demos/Tubes/Shapes.h>
|
||||||
|
#include <LibGL/GL/gl.h>
|
||||||
|
#include <LibGfx/Vector3.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
constexpr u8 sphere_number_of_segments = 4;
|
||||||
|
constexpr u8 tube_number_of_segments = 12;
|
||||||
|
|
||||||
|
void draw_sphere()
|
||||||
|
{
|
||||||
|
// Draw a sphere by drawing a cube with many segments with normalized coordinates
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
auto draw_segment = [](Array<DoubleVector3, 4> corners, Optional<int> flip_a, Optional<int> flip_b, Optional<int> swap_a, Optional<int> swap_b) {
|
||||||
|
for (DoubleVector3& corner : corners) {
|
||||||
|
if (flip_a.has_value())
|
||||||
|
corner[flip_a.value()] *= -1;
|
||||||
|
if (flip_b.has_value())
|
||||||
|
corner[flip_b.value()] *= -1;
|
||||||
|
if (swap_a.has_value() && swap_b.has_value())
|
||||||
|
swap(corner[swap_a.value()], corner[swap_b.value()]);
|
||||||
|
|
||||||
|
glNormal3d(corner.x(), corner.y(), corner.z());
|
||||||
|
glVertex3d(corner.x(), corner.y(), corner.z());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
double const segment_size = 2. / sphere_number_of_segments;
|
||||||
|
for (int y = 0; y < sphere_number_of_segments; y++) {
|
||||||
|
for (int x = 0; x < sphere_number_of_segments; x++) {
|
||||||
|
DoubleVector3 bottomleft = { -1. + x * segment_size, -1. + y * segment_size, 1. };
|
||||||
|
DoubleVector3 bottomright = { -1. + (x + 1) * segment_size, -1. + y * segment_size, 1. };
|
||||||
|
DoubleVector3 topright = { -1. + (x + 1) * segment_size, -1. + (y + 1) * segment_size, 1. };
|
||||||
|
DoubleVector3 topleft = { -1. + x * segment_size, -1. + (y + 1) * segment_size, 1. };
|
||||||
|
|
||||||
|
Array<DoubleVector3, 4> normalized_corners = {
|
||||||
|
bottomleft.normalized(),
|
||||||
|
bottomright.normalized(),
|
||||||
|
topright.normalized(),
|
||||||
|
topleft.normalized(),
|
||||||
|
};
|
||||||
|
|
||||||
|
draw_segment(normalized_corners, {}, {}, {}, {}); // front face
|
||||||
|
draw_segment(normalized_corners, 0, 2, {}, {}); // back face
|
||||||
|
draw_segment(normalized_corners, 2, {}, 0, 2); // left face
|
||||||
|
draw_segment(normalized_corners, 0, {}, 0, 2); // right face
|
||||||
|
draw_segment(normalized_corners, 1, {}, 1, 2); // top face
|
||||||
|
draw_segment(normalized_corners, 2, {}, 1, 2); // bottom face
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_tube()
|
||||||
|
{
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
double const segment_angle = 2 * M_PI / tube_number_of_segments;
|
||||||
|
double last_x = 0.;
|
||||||
|
double last_y = 1.;
|
||||||
|
for (int i = 1; i <= tube_number_of_segments; ++i) {
|
||||||
|
double angle = i * segment_angle;
|
||||||
|
double segment_x = sin(angle);
|
||||||
|
double segment_y = cos(angle);
|
||||||
|
|
||||||
|
glNormal3d(last_x, last_y, 0.);
|
||||||
|
glVertex3d(last_x, last_y, 0.);
|
||||||
|
glNormal3d(segment_x, segment_y, 0.);
|
||||||
|
glVertex3d(segment_x, segment_y, 0.);
|
||||||
|
glVertex3d(segment_x, segment_y, -2.);
|
||||||
|
glNormal3d(last_x, last_y, 0.);
|
||||||
|
glVertex3d(last_x, last_y, -2.);
|
||||||
|
|
||||||
|
last_x = segment_x;
|
||||||
|
last_y = segment_y;
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
}
|
12
Userland/Demos/Tubes/Shapes.h
Normal file
12
Userland/Demos/Tubes/Shapes.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Types.h>
|
||||||
|
|
||||||
|
void draw_sphere();
|
||||||
|
void draw_tube();
|
326
Userland/Demos/Tubes/Tubes.cpp
Normal file
326
Userland/Demos/Tubes/Tubes.cpp
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/NumericLimits.h>
|
||||||
|
#include <AK/Random.h>
|
||||||
|
#include <Demos/Tubes/Shapes.h>
|
||||||
|
#include <Demos/Tubes/Tubes.h>
|
||||||
|
#include <LibGL/GLContext.h>
|
||||||
|
#include <LibGUI/Application.h>
|
||||||
|
#include <LibGUI/Event.h>
|
||||||
|
#include <LibGUI/Painter.h>
|
||||||
|
#include <LibGUI/Widget.h>
|
||||||
|
#include <LibGfx/Bitmap.h>
|
||||||
|
|
||||||
|
constexpr size_t grid_resolution = 15;
|
||||||
|
constexpr int mouse_max_distance_move = 10;
|
||||||
|
constexpr int reset_every_ticks = 900;
|
||||||
|
constexpr double rotation_range = 35.;
|
||||||
|
constexpr u8 tube_maximum_count = 12;
|
||||||
|
constexpr u8 tube_minimum_count = 3;
|
||||||
|
constexpr double tube_movement_per_tick = .25;
|
||||||
|
constexpr double tube_relative_thickness = .6;
|
||||||
|
constexpr int tube_travel_max_stretch = 6;
|
||||||
|
|
||||||
|
static double random_double()
|
||||||
|
{
|
||||||
|
return get_random<u32>() / static_cast<double>(NumericLimits<u32>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int random_int(int min, int max)
|
||||||
|
{
|
||||||
|
return min + round_to<int>(random_double() * (max - min));
|
||||||
|
}
|
||||||
|
|
||||||
|
static IntVector4 tube_rotation_for_direction(Direction direction)
|
||||||
|
{
|
||||||
|
switch (direction) {
|
||||||
|
case Direction::XPositive:
|
||||||
|
return { 0, 1, 0, -90 };
|
||||||
|
case Direction::XNegative:
|
||||||
|
return { 0, 1, 0, 90 };
|
||||||
|
case Direction::YPositive:
|
||||||
|
return { 1, 0, 0, 90 };
|
||||||
|
case Direction::YNegative:
|
||||||
|
return { 1, 0, 0, -90 };
|
||||||
|
case Direction::ZPositive:
|
||||||
|
return { 0, 1, 0, 180 };
|
||||||
|
case Direction::ZNegative:
|
||||||
|
return { 0, 0, 0, 0 };
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static IntVector3 vector_for_direction(Direction direction)
|
||||||
|
{
|
||||||
|
switch (direction) {
|
||||||
|
case Direction::XPositive:
|
||||||
|
return { 1, 0, 0 };
|
||||||
|
case Direction::XNegative:
|
||||||
|
return { -1, 0, 0 };
|
||||||
|
case Direction::YPositive:
|
||||||
|
return { 0, 1, 0 };
|
||||||
|
case Direction::YNegative:
|
||||||
|
return { 0, -1, 0 };
|
||||||
|
case Direction::ZPositive:
|
||||||
|
return { 0, 0, 1 };
|
||||||
|
case Direction::ZNegative:
|
||||||
|
return { 0, 0, -1 };
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tubes::Tubes(int interval)
|
||||||
|
: m_grid(MUST(FixedArray<u8>::try_create(grid_resolution * grid_resolution * grid_resolution)))
|
||||||
|
{
|
||||||
|
start_timer(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tubes::choose_new_direction_for_tube(Tube& tube)
|
||||||
|
{
|
||||||
|
// Find all possible directions
|
||||||
|
Vector<Direction, 6> possible_directions;
|
||||||
|
for (int i = 1; i <= 6; ++i) {
|
||||||
|
auto direction = static_cast<Direction>(i);
|
||||||
|
auto direction_vector = vector_for_direction(direction);
|
||||||
|
auto check_position = tube.position + direction_vector;
|
||||||
|
if (is_valid_grid_position(check_position) && get_grid(check_position) == 0)
|
||||||
|
possible_directions.append(direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If tube is stuck, kill it :^(
|
||||||
|
if (possible_directions.is_empty()) {
|
||||||
|
tube.direction = Direction::None;
|
||||||
|
tube.active = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove our old direction if we have other options available
|
||||||
|
Direction const old_direction = tube.direction;
|
||||||
|
if (possible_directions.size() >= 2 && possible_directions.contains_slow(old_direction))
|
||||||
|
possible_directions.remove_all_matching([&old_direction](Direction const& item) { return item == old_direction; });
|
||||||
|
|
||||||
|
// Select a random new direction
|
||||||
|
tube.direction = possible_directions[random_int(0, static_cast<int>(possible_directions.size()) - 1)];
|
||||||
|
|
||||||
|
// Determine how far we can go in this direction
|
||||||
|
auto direction_vector = vector_for_direction(tube.direction);
|
||||||
|
int max_stretch = random_int(1, tube_travel_max_stretch);
|
||||||
|
IntVector3 new_target = tube.position;
|
||||||
|
while (max_stretch-- > 0) {
|
||||||
|
new_target += direction_vector;
|
||||||
|
if (!is_valid_grid_position(new_target) || get_grid(new_target) != 0)
|
||||||
|
break;
|
||||||
|
set_grid(new_target, 1);
|
||||||
|
tube.target_position = new_target;
|
||||||
|
}
|
||||||
|
tube.progress_to_target = 0.;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> Tubes::create_buffer(Gfx::IntSize size)
|
||||||
|
{
|
||||||
|
m_bitmap = TRY(Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRx8888, size));
|
||||||
|
m_gl_context = GL::create_context(*m_bitmap);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 Tubes::get_grid(IntVector3 position)
|
||||||
|
{
|
||||||
|
return m_grid[position.z() * grid_resolution * grid_resolution + position.y() * grid_resolution + position.x()];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Tubes::is_valid_grid_position(Gfx::IntVector3 position)
|
||||||
|
{
|
||||||
|
return position.x() >= 0
|
||||||
|
&& position.x() < static_cast<int>(grid_resolution)
|
||||||
|
&& position.y() >= 0
|
||||||
|
&& position.y() < static_cast<int>(grid_resolution)
|
||||||
|
&& position.z() >= 0
|
||||||
|
&& position.z() < static_cast<int>(grid_resolution);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tubes::set_grid(IntVector3 position, u8 value)
|
||||||
|
{
|
||||||
|
m_grid[position.z() * grid_resolution * grid_resolution + position.y() * grid_resolution + position.x()] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tubes::mousemove_event(GUI::MouseEvent& event)
|
||||||
|
{
|
||||||
|
if (m_mouse_origin.is_null()) {
|
||||||
|
m_mouse_origin = event.position();
|
||||||
|
} else if (event.position().distance_from(m_mouse_origin) > mouse_max_distance_move) {
|
||||||
|
GUI::Application::the()->quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tubes::mousedown_event(GUI::MouseEvent&)
|
||||||
|
{
|
||||||
|
GUI::Application::the()->quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tubes::keydown_event(GUI::KeyEvent&)
|
||||||
|
{
|
||||||
|
GUI::Application::the()->quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tubes::paint_event(GUI::PaintEvent& event)
|
||||||
|
{
|
||||||
|
GUI::Painter painter(*this);
|
||||||
|
painter.add_clip_rect(event.rect());
|
||||||
|
painter.blit(rect().location(), *m_bitmap, m_bitmap->rect());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tubes::reset_tubes()
|
||||||
|
{
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// Random rotation
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glPopMatrix();
|
||||||
|
glPushMatrix();
|
||||||
|
glRotated((random_double() - .5) * 2 * rotation_range, 0., 1., 0.);
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
|
||||||
|
// Clear grid
|
||||||
|
m_grid.fill_with(0);
|
||||||
|
|
||||||
|
// Create new set of tubes
|
||||||
|
auto free_grid_position = [&]() {
|
||||||
|
for (;;) {
|
||||||
|
IntVector3 position = {
|
||||||
|
random_int(0, grid_resolution - 1),
|
||||||
|
random_int(0, grid_resolution - 1),
|
||||||
|
random_int(0, grid_resolution - 1),
|
||||||
|
};
|
||||||
|
if (get_grid(position) != 0)
|
||||||
|
continue;
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
m_tubes.clear_with_capacity();
|
||||||
|
int tube_count = random_int(tube_minimum_count, tube_maximum_count);
|
||||||
|
while (tube_count-- > 0) {
|
||||||
|
Tube new_tube = {
|
||||||
|
.color = {
|
||||||
|
random_double(),
|
||||||
|
random_double(),
|
||||||
|
random_double(),
|
||||||
|
},
|
||||||
|
.position = free_grid_position(),
|
||||||
|
};
|
||||||
|
choose_new_direction_for_tube(new_tube);
|
||||||
|
m_tubes.append(new_tube);
|
||||||
|
set_grid(new_tube.position, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tubes::setup_view()
|
||||||
|
{
|
||||||
|
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||||
|
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
double const zoom = .25;
|
||||||
|
auto const half_aspect_ratio = static_cast<double>(m_bitmap->width()) / m_bitmap->height() * zoom;
|
||||||
|
glFrustum(-half_aspect_ratio, half_aspect_ratio, -zoom, zoom, .5, 10.);
|
||||||
|
glTranslated(0., 0., -2.);
|
||||||
|
glPushMatrix();
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
|
||||||
|
// Set up lighting
|
||||||
|
glEnable(GL_LIGHTING);
|
||||||
|
glEnable(GL_LIGHT0);
|
||||||
|
GLfloat light_ambient[] { .0f, .0f, .0f, 1.f };
|
||||||
|
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
|
||||||
|
GLfloat light_diffuse[] { 1.f, 1.f, 1.f, 1.f };
|
||||||
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
|
||||||
|
GLfloat light_specular[] { 1.f, 1.f, 1.f, 1.f };
|
||||||
|
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
|
||||||
|
GLfloat light_position[] { .5f, 1.f, .5f, 0.f };
|
||||||
|
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
|
||||||
|
|
||||||
|
GLfloat mat_specular[] { 1.f, 1.f, 1.f, 1.f };
|
||||||
|
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
|
||||||
|
glMaterialf(GL_FRONT, GL_SHININESS, 8.f);
|
||||||
|
|
||||||
|
// Adapt the vertex color as ambient and diffuse colors
|
||||||
|
glEnable(GL_COLOR_MATERIAL);
|
||||||
|
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
|
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
glEnable(GL_NORMALIZE);
|
||||||
|
glShadeModel(GL_SMOOTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tubes::timer_event(Core::TimerEvent&)
|
||||||
|
{
|
||||||
|
update_tubes();
|
||||||
|
m_gl_context->present();
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tubes::update_tubes()
|
||||||
|
{
|
||||||
|
if (++m_ticks % reset_every_ticks == 0)
|
||||||
|
reset_tubes();
|
||||||
|
|
||||||
|
double const primitive_size = 2.; // our tubes and spheres are 1 in diameter, so object size is 2
|
||||||
|
double const grid_width = 2.;
|
||||||
|
double const grid_scale = 1. / grid_resolution;
|
||||||
|
double const primitive_scale = 1. / primitive_size;
|
||||||
|
double const tube_length_scale = tube_movement_per_tick * primitive_size;
|
||||||
|
double const tube_thickness_scale = tube_relative_thickness * primitive_scale;
|
||||||
|
for (auto& tube : m_tubes) {
|
||||||
|
if (!tube.active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
glColor3d(tube.color.x(), tube.color.y(), tube.color.z());
|
||||||
|
glPushMatrix();
|
||||||
|
|
||||||
|
auto pos = tube.position;
|
||||||
|
glTranslated(
|
||||||
|
pos.x() * grid_scale * grid_width - (grid_width / 2.),
|
||||||
|
pos.y() * grid_scale * grid_width - (grid_width / 2.),
|
||||||
|
pos.z() * grid_scale * grid_width - (grid_width / 2.));
|
||||||
|
glScaled(grid_scale, grid_scale, grid_scale);
|
||||||
|
|
||||||
|
// Draw sphere if we're at the start or a corner
|
||||||
|
if (tube.progress_to_target == 0.) {
|
||||||
|
glPushMatrix();
|
||||||
|
glScaled(tube_thickness_scale, tube_thickness_scale, tube_thickness_scale);
|
||||||
|
draw_sphere();
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw tube at the current position
|
||||||
|
glPushMatrix();
|
||||||
|
auto direction_vector = vector_for_direction(tube.direction);
|
||||||
|
auto distance_to_target = (tube.target_position - tube.position).length<double>();
|
||||||
|
auto movement_magnitude = tube.progress_to_target * (distance_to_target - tube_movement_per_tick) / distance_to_target * grid_width;
|
||||||
|
glTranslated(
|
||||||
|
direction_vector.x() * movement_magnitude,
|
||||||
|
direction_vector.y() * movement_magnitude,
|
||||||
|
direction_vector.z() * movement_magnitude);
|
||||||
|
auto tube_rotation = tube_rotation_for_direction(tube.direction);
|
||||||
|
glRotated(tube_rotation.w(), tube_rotation.x(), tube_rotation.y(), tube_rotation.z());
|
||||||
|
glScaled(tube_thickness_scale, tube_thickness_scale, primitive_scale * tube_length_scale);
|
||||||
|
|
||||||
|
draw_tube();
|
||||||
|
glPopMatrix();
|
||||||
|
|
||||||
|
// Move towards target
|
||||||
|
if (tube.progress_to_target >= distance_to_target) {
|
||||||
|
tube.position = tube.target_position;
|
||||||
|
choose_new_direction_for_tube(tube);
|
||||||
|
} else {
|
||||||
|
tube.progress_to_target = min(tube.progress_to_target + tube_movement_per_tick, distance_to_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
}
|
64
Userland/Demos/Tubes/Tubes.h
Normal file
64
Userland/Demos/Tubes/Tubes.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/FixedArray.h>
|
||||||
|
#include <AK/Vector.h>
|
||||||
|
#include <LibGUI/Widget.h>
|
||||||
|
#include <LibGL/GLContext.h>
|
||||||
|
#include <LibGfx/Vector3.h>
|
||||||
|
|
||||||
|
enum class Direction : u8 {
|
||||||
|
None = 0,
|
||||||
|
XPositive = 1,
|
||||||
|
XNegative = 2,
|
||||||
|
YPositive = 3,
|
||||||
|
YNegative = 4,
|
||||||
|
ZPositive = 5,
|
||||||
|
ZNegative = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Tube {
|
||||||
|
bool active { true };
|
||||||
|
DoubleVector3 color;
|
||||||
|
IntVector3 position;
|
||||||
|
Direction direction { Direction::None };
|
||||||
|
IntVector3 target_position { 0, 0, 0 };
|
||||||
|
double progress_to_target { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
class Tubes final : public GUI::Widget {
|
||||||
|
C_OBJECT(Tubes)
|
||||||
|
public:
|
||||||
|
virtual ~Tubes() override = default;
|
||||||
|
|
||||||
|
ErrorOr<void> create_buffer(Gfx::IntSize);
|
||||||
|
void reset_tubes();
|
||||||
|
void setup_view();
|
||||||
|
void update_tubes();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Tubes(int);
|
||||||
|
|
||||||
|
void choose_new_direction_for_tube(Tube&);
|
||||||
|
u8 get_grid(IntVector3);
|
||||||
|
bool is_valid_grid_position(IntVector3);
|
||||||
|
void set_grid(IntVector3, u8 value);
|
||||||
|
|
||||||
|
virtual void paint_event(GUI::PaintEvent&) override;
|
||||||
|
virtual void timer_event(Core::TimerEvent&) override;
|
||||||
|
virtual void keydown_event(GUI::KeyEvent&) override;
|
||||||
|
virtual void mousedown_event(GUI::MouseEvent& event) override;
|
||||||
|
virtual void mousemove_event(GUI::MouseEvent& event) override;
|
||||||
|
|
||||||
|
RefPtr<Gfx::Bitmap> m_bitmap;
|
||||||
|
FixedArray<u8> m_grid;
|
||||||
|
OwnPtr<GL::GLContext> m_gl_context;
|
||||||
|
Gfx::IntPoint m_mouse_origin;
|
||||||
|
u64 m_ticks { 0 };
|
||||||
|
Vector<Tube> m_tubes;
|
||||||
|
};
|
55
Userland/Demos/Tubes/main.cpp
Normal file
55
Userland/Demos/Tubes/main.cpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Demos/Tubes/Tubes.h>
|
||||||
|
#include <LibCore/ArgsParser.h>
|
||||||
|
#include <LibCore/System.h>
|
||||||
|
#include <LibGUI/Application.h>
|
||||||
|
#include <LibGUI/Icon.h>
|
||||||
|
#include <LibGUI/Window.h>
|
||||||
|
#include <LibMain/Main.h>
|
||||||
|
|
||||||
|
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
{
|
||||||
|
TRY(Core::System::pledge("stdio recvfd sendfd rpath unix prot_exec"));
|
||||||
|
|
||||||
|
unsigned refresh_rate = 12;
|
||||||
|
|
||||||
|
Core::ArgsParser args_parser;
|
||||||
|
args_parser.set_general_help("Screensaver rendering colorful moving tubes using LibGL");
|
||||||
|
args_parser.add_option(refresh_rate, "Refresh rate", "rate", 'r', "milliseconds");
|
||||||
|
args_parser.parse(arguments);
|
||||||
|
|
||||||
|
auto app = TRY(GUI::Application::try_create(arguments));
|
||||||
|
|
||||||
|
TRY(Core::System::pledge("stdio recvfd sendfd rpath prot_exec"));
|
||||||
|
|
||||||
|
auto app_icon = GUI::Icon::default_icon("app-tubes"sv);
|
||||||
|
auto window = TRY(GUI::Window::try_create());
|
||||||
|
|
||||||
|
window->set_double_buffering_enabled(true);
|
||||||
|
window->set_title("Tubes");
|
||||||
|
window->set_resizable(false);
|
||||||
|
window->set_frameless(true);
|
||||||
|
window->set_fullscreen(true);
|
||||||
|
window->set_minimizable(false);
|
||||||
|
window->set_icon(app_icon.bitmap_for_size(16));
|
||||||
|
window->update();
|
||||||
|
|
||||||
|
auto tubes_widget = TRY(window->try_set_main_widget<Tubes>(refresh_rate));
|
||||||
|
tubes_widget->set_fill_with_background_color(false);
|
||||||
|
tubes_widget->set_override_cursor(Gfx::StandardCursor::Hidden);
|
||||||
|
window->show();
|
||||||
|
|
||||||
|
TRY(tubes_widget->create_buffer(window->size()));
|
||||||
|
tubes_widget->setup_view();
|
||||||
|
tubes_widget->reset_tubes();
|
||||||
|
|
||||||
|
window->move_to_front();
|
||||||
|
window->set_cursor(Gfx::StandardCursor::Hidden);
|
||||||
|
|
||||||
|
return app->exec();
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue