Impl. resetting surface with neat visual effect
All checks were successful
Build and Publish WASM version of demo / Build-And-Deploy (push) Successful in 21s

This commit is contained in:
Stephen Seo 2023-08-21 14:37:50 +09:00
parent 530920c811
commit d8b81f29cc
6 changed files with 380 additions and 123 deletions

View file

@ -18,7 +18,8 @@ SOURCES = \
src/3d_helpers.cc \ src/3d_helpers.cc \
src/raymath.cc \ src/raymath.cc \
src/ems.cc \ src/ems.cc \
src/walker.cc src/walker.cc \
src/surface_triangle.cc
HEADERS = \ HEADERS = \
src/game.h \ src/game.h \
@ -27,7 +28,8 @@ HEADERS = \
src/screen_trunner.h \ src/screen_trunner.h \
src/3d_helpers.h \ src/3d_helpers.h \
src/ems.h \ src/ems.h \
src/walker.h src/walker.h \
src/surface_triangle.h
OBJECTS = $(addprefix ${OBJDIR}/,$(subst .cc,.cc.o,${SOURCES})) OBJECTS = $(addprefix ${OBJDIR}/,$(subst .cc,.cc.o,${SOURCES}))

View file

@ -21,6 +21,7 @@ TRunnerScreen::TRunnerScreen(std::weak_ptr<ScreenStack> stack)
: Screen(stack), : Screen(stack),
surface(), surface(),
surface_bbs(), surface_bbs(),
surface_triangles(),
// NOLINTBEGIN(bugprone-integer-division) // NOLINTBEGIN(bugprone-integer-division)
walkers{Walker{(float)(SURFACE_UNIT_WIDTH / 4) - SURFACE_X_OFFSET, walkers{Walker{(float)(SURFACE_UNIT_WIDTH / 4) - SURFACE_X_OFFSET,
(float)(SURFACE_UNIT_HEIGHT / 4) - SURFACE_Y_OFFSET, true}, (float)(SURFACE_UNIT_HEIGHT / 4) - SURFACE_Y_OFFSET, true},
@ -42,6 +43,8 @@ TRunnerScreen::TRunnerScreen(std::weak_ptr<ScreenStack> stack)
TEMP_cube_model(LoadModel("res/test_cube.obj")), TEMP_cube_model(LoadModel("res/test_cube.obj")),
TEMP_cube_texture(LoadTexture("res/test_cube_texture.png")), TEMP_cube_texture(LoadTexture("res/test_cube_texture.png")),
TEMP_matrix(get_identity_matrix()), TEMP_matrix(get_identity_matrix()),
bgRenderTexture(),
fgRenderTexture(),
camera_pos{0.0F, 4.0F, 4.0F}, camera_pos{0.0F, 4.0F, 4.0F},
camera_target{0.0F, 0.0F, 0.0F}, camera_target{0.0F, 0.0F, 0.0F},
mouse_hit{0.0F, 0.0F, 0.0F}, mouse_hit{0.0F, 0.0F, 0.0F},
@ -50,7 +53,9 @@ TRunnerScreen::TRunnerScreen(std::weak_ptr<ScreenStack> stack)
controlled_walker_idx(std::nullopt), controlled_walker_idx(std::nullopt),
left_text_width(MeasureText("Left", BUTTON_FONT_SIZE)), left_text_width(MeasureText("Left", BUTTON_FONT_SIZE)),
right_text_width(MeasureText("Right", BUTTON_FONT_SIZE)), right_text_width(MeasureText("Right", BUTTON_FONT_SIZE)),
forward_text_width(MeasureText("Forward", BUTTON_FONT_SIZE)) { forward_text_width(MeasureText("Forward", BUTTON_FONT_SIZE)),
reset_surface_text_width(MeasureText("Reset Surface", BUTTON_FONT_SIZE)),
surface_reset_anim_timer(0.0F) {
#ifndef NDEBUG #ifndef NDEBUG
std::cout << "idx_hit initialized to " << idx_hit << std::endl; std::cout << "idx_hit initialized to " << idx_hit << std::endl;
#endif #endif
@ -63,17 +68,32 @@ TRunnerScreen::TRunnerScreen(std::weak_ptr<ScreenStack> stack)
// Initialize surface. // Initialize surface.
generate_surface(); generate_surface();
// Set up render textures
bgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
fgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
#ifndef NDEBUG #ifndef NDEBUG
std::cout << "Screen finished init.\n"; std::cout << "Screen finished init.\n";
#endif #endif
} }
TRunnerScreen::~TRunnerScreen() { TRunnerScreen::~TRunnerScreen() {
UnloadRenderTexture(fgRenderTexture);
UnloadRenderTexture(bgRenderTexture);
UnloadTexture(TEMP_cube_texture); UnloadTexture(TEMP_cube_texture);
UnloadModel(TEMP_cube_model); UnloadModel(TEMP_cube_model);
} }
bool TRunnerScreen::update(float dt) { bool TRunnerScreen::update(float dt) {
if (IsWindowResized()) {
UnloadRenderTexture(fgRenderTexture);
UnloadRenderTexture(bgRenderTexture);
bgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
fgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
}
if (controlled_walker_idx.has_value()) { if (controlled_walker_idx.has_value()) {
auto walker_body_pos = auto walker_body_pos =
walkers[controlled_walker_idx.value()].get_body_pos(); walkers[controlled_walker_idx.value()].get_body_pos();
@ -89,138 +109,162 @@ bool TRunnerScreen::update(float dt) {
camera_pos = walkers[controlled_walker_idx.value()].get_body_pos() + offset; camera_pos = walkers[controlled_walker_idx.value()].get_body_pos() + offset;
} }
if (controlled_walker_idx.has_value() && IsMouseButtonDown(0)) { if (!flags.test(0)) {
// Check if clicked on button. if (controlled_walker_idx.has_value() && IsMouseButtonDown(0)) {
if (!walkers[controlled_walker_idx.value()].player_is_turning_left() && // Check if clicked on button.
GetTouchX() >= 0 && GetTouchX() <= left_text_width && if (!walkers[controlled_walker_idx.value()].player_is_turning_left() &&
GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE && GetTouchX() >= 0 && GetTouchX() <= left_text_width &&
GetTouchY() <= GetScreenHeight()) { GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE &&
walkers[controlled_walker_idx.value()].player_turn_left(); GetTouchY() <= GetScreenHeight()) {
goto post_check_click; walkers[controlled_walker_idx.value()].player_turn_left();
} else if (!walkers[controlled_walker_idx.value()]
.player_is_turning_right() &&
GetTouchX() >= left_text_width &&
GetTouchX() <= left_text_width + right_text_width &&
GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE &&
GetTouchY() <= GetScreenHeight()) {
walkers[controlled_walker_idx.value()].player_turn_right();
goto post_check_click;
} else if (!walkers[controlled_walker_idx.value()]
.player_is_going_forward()) {
if (int width_mid =
(left_text_width + right_text_width) / 2 - forward_text_width / 2;
GetTouchX() >= width_mid &&
GetTouchX() <= width_mid + forward_text_width &&
GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE * 2 &&
GetTouchY() <= GetScreenHeight() - BUTTON_FONT_SIZE) {
walkers[controlled_walker_idx.value()].player_go_forward();
goto post_check_click; goto post_check_click;
} } else if (!walkers[controlled_walker_idx.value()]
} .player_is_turning_right() &&
} else if (IsMouseButtonReleased(0)) { GetTouchX() >= left_text_width &&
if (controlled_walker_idx.has_value()) { GetTouchX() <= left_text_width + right_text_width &&
walkers[controlled_walker_idx.value()].player_idle(); GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE &&
goto post_check_click; GetTouchY() <= GetScreenHeight()) {
} walkers[controlled_walker_idx.value()].player_turn_right();
} goto post_check_click;
} else if (!walkers[controlled_walker_idx.value()]
if (IsMouseButtonPressed(0)) { .player_is_going_forward()) {
float press_x = GetTouchX(); if (int width_mid = (left_text_width + right_text_width) / 2 -
float press_y = GetTouchY(); forward_text_width / 2;
Ray ray = GetMouseRay(Vector2{press_x, press_y}, camera); GetTouchX() >= width_mid &&
#ifndef NDEBUG GetTouchX() <= width_mid + forward_text_width &&
std::cout << "X: " << press_x << ", Y: " << press_y << std::endl; GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE * 2 &&
std::cout << "Ray pos: " << ray.position.x << ", " << ray.position.y << ", " GetTouchY() <= GetScreenHeight() - BUTTON_FONT_SIZE) {
<< ray.position.z << " Ray dir: " << ray.direction.x << ", " walkers[controlled_walker_idx.value()].player_go_forward();
<< ray.direction.y << ", " << ray.direction.z << std::endl; goto post_check_click;
#endif
// Check if clicked on a Walker.
for (unsigned int idx = 0; idx < walkers.size(); ++idx) {
if (GetRayCollisionBox(ray, walkers[idx].get_body_bb()).hit) {
if (controlled_walker_idx.has_value()) {
walkers[controlled_walker_idx.value()].set_player_controlled(false);
} }
controlled_walker_idx = idx; }
walkers[controlled_walker_idx.value()].set_player_controlled(true); } else if (IsMouseButtonReleased(0)) {
if (controlled_walker_idx.has_value()) {
idx_hit = SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT; walkers[controlled_walker_idx.value()].player_idle();
goto post_check_click; goto post_check_click;
} }
} }
// Check if clicked on ground. if (IsMouseButtonPressed(0)) {
for (unsigned int idx = 0; idx < SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT; float press_x = GetTouchX();
++idx) { float press_y = GetTouchY();
int x = idx % SURFACE_UNIT_WIDTH;
int y = idx / SURFACE_UNIT_WIDTH;
float xf = (float)(x)-SURFACE_X_OFFSET;
float zf = (float)(y)-SURFACE_Y_OFFSET;
const auto &current = surface[idx].value(); // Check if clicked on reset surface.
Vector3 nw{xf - 0.5F, current.nw, zf - 0.5F}; if (!flags.test(0) &&
Vector3 ne{xf + 0.5F, current.ne, zf - 0.5F}; press_x >= GetScreenWidth() - reset_surface_text_width &&
Vector3 sw{xf - 0.5F, current.sw, zf + 0.5F}; press_y <= BUTTON_FONT_SIZE) {
Vector3 se{xf + 0.5F, current.se, zf + 0.5F}; generate_surface_with_triangles();
goto post_check_click;
}
const auto on_collide_fn = [this, idx, xf, zf, Ray ray = GetMouseRay(Vector2{press_x, press_y}, camera);
&current](const auto &collision) {
this->idx_hit = idx;
#ifndef NDEBUG #ifndef NDEBUG
std::cout << "idx_hit set to " << idx_hit << std::endl; std::cout << "X: " << press_x << ", Y: " << press_y << std::endl;
std::cout << "Ray pos: " << ray.position.x << ", " << ray.position.y
<< ", " << ray.position.z << " Ray dir: " << ray.direction.x
<< ", " << ray.direction.y << ", " << ray.direction.z
<< std::endl;
#endif #endif
this->mouse_hit = collision;
this->camera_target.x = xf; // Check if clicked on a Walker.
this->camera_target.y = for (unsigned int idx = 0; idx < walkers.size(); ++idx) {
(current.nw + current.ne + current.sw + current.se) / 4.0F; if (GetRayCollisionBox(ray, walkers[idx].get_body_bb()).hit) {
this->camera_target.z = zf; if (controlled_walker_idx.has_value()) {
if (idx != SURFACE_UNIT_WIDTH / 2 + walkers[controlled_walker_idx.value()].set_player_controlled(false);
(SURFACE_UNIT_HEIGHT / 2) * SURFACE_UNIT_WIDTH) { }
this->camera_pos = (Vector3Normalize(this->camera_target) * 4.0F) + controlled_walker_idx = idx;
this->camera_target; walkers[controlled_walker_idx.value()].set_player_controlled(true);
this->camera_pos.y += 4.0F;
} else { idx_hit = SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT;
this->camera_pos.x = 0.0F;
this->camera_pos.y = this->camera_target.y + 4.0F;
this->camera_pos.z = 0.0F;
}
this->camera_target.y += 1.0F;
if (this->controlled_walker_idx.has_value()) {
this->walkers[this->controlled_walker_idx.value()]
.set_player_controlled(false);
this->controlled_walker_idx = std::nullopt;
}
};
if (GetRayCollisionBox(ray, surface_bbs[idx]).hit) {
if (auto collision = GetRayCollisionTriangle(ray, nw, sw, ne);
collision.hit) {
on_collide_fn(collision.point);
goto post_check_click;
} else if (auto collision = GetRayCollisionTriangle(ray, ne, sw, se);
collision.hit) {
on_collide_fn(collision.point);
goto post_check_click; goto post_check_click;
} }
} }
// Check if clicked on ground.
for (unsigned int idx = 0; idx < SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT;
++idx) {
int x = idx % SURFACE_UNIT_WIDTH;
int y = idx / SURFACE_UNIT_WIDTH;
float xf = (float)(x)-SURFACE_X_OFFSET;
float zf = (float)(y)-SURFACE_Y_OFFSET;
const auto &current = surface[idx].value();
Vector3 nw{xf - 0.5F, current.nw, zf - 0.5F};
Vector3 ne{xf + 0.5F, current.ne, zf - 0.5F};
Vector3 sw{xf - 0.5F, current.sw, zf + 0.5F};
Vector3 se{xf + 0.5F, current.se, zf + 0.5F};
const auto on_collide_fn = [this, idx, xf, zf,
&current](const auto &collision) {
this->idx_hit = idx;
#ifndef NDEBUG
std::cout << "idx_hit set to " << idx_hit << std::endl;
#endif
this->mouse_hit = collision;
this->camera_target.x = xf;
this->camera_target.y =
(current.nw + current.ne + current.sw + current.se) / 4.0F;
this->camera_target.z = zf;
if (idx != SURFACE_UNIT_WIDTH / 2 +
(SURFACE_UNIT_HEIGHT / 2) * SURFACE_UNIT_WIDTH) {
this->camera_pos = (Vector3Normalize(this->camera_target) * 4.0F) +
this->camera_target;
this->camera_pos.y += 4.0F;
} else {
this->camera_pos.x = 0.0F;
this->camera_pos.y = this->camera_target.y + 4.0F;
this->camera_pos.z = 0.0F;
}
this->camera_target.y += 1.0F;
if (this->controlled_walker_idx.has_value()) {
this->walkers[this->controlled_walker_idx.value()]
.set_player_controlled(false);
this->controlled_walker_idx = std::nullopt;
}
};
if (GetRayCollisionBox(ray, surface_bbs[idx]).hit) {
if (auto collision = GetRayCollisionTriangle(ray, nw, sw, ne);
collision.hit) {
on_collide_fn(collision.point);
goto post_check_click;
} else if (auto collision = GetRayCollisionTriangle(ray, ne, sw, se);
collision.hit) {
on_collide_fn(collision.point);
goto post_check_click;
}
}
}
} }
} }
post_check_click: post_check_click:
if (flags.test(0)) {
surface_reset_anim_timer += dt;
if (surface_reset_anim_timer > SURFACE_RESET_TIME) {
flags.reset(0);
} else {
for (auto &tri : surface_triangles) {
tri.update(dt);
}
}
}
camera_to_targets(dt); camera_to_targets(dt);
for (auto &walker : walkers) { for (auto &walker : walkers) {
walker.update(dt, surface_bbs, SURFACE_UNIT_WIDTH, SURFACE_UNIT_HEIGHT); walker.update(flags.test(0) ? 0.0F : dt, surface_bbs, SURFACE_UNIT_WIDTH,
SURFACE_UNIT_HEIGHT);
} }
return false; return false;
} }
bool TRunnerScreen::draw() { bool TRunnerScreen::draw() {
BeginDrawing(); BeginTextureMode(bgRenderTexture);
ClearBackground(PixelToColor(Pixel::PIXEL_SKY)); ClearBackground(PixelToColor(Pixel::PIXEL_SKY));
BeginMode3D(camera); BeginMode3D(camera);
@ -237,12 +281,20 @@ bool TRunnerScreen::draw() {
: Color{(unsigned char)(200 + ox * 2), : Color{(unsigned char)(200 + ox * 2),
(unsigned char)(150 + oy * 2), 20, 255}; (unsigned char)(150 + oy * 2), 20, 255};
const auto &current = surface[idx].value(); const auto &current = surface[idx].value();
DrawTriangle3D(Vector3{xf - 0.5F, current.nw, zf - 0.5F}, float reset_y_offset = 0.0F;
Vector3{xf - 0.5F, current.sw, zf + 0.5F}, if (flags.test(0)) {
Vector3{xf + 0.5F, current.ne, zf - 0.5F}, color); reset_y_offset = (1.0F - std::sin(surface_reset_anim_timer /
DrawTriangle3D(Vector3{xf + 0.5F, current.se, zf + 0.5F}, SURFACE_RESET_TIME * PI / 2.0F)) *
Vector3{xf + 0.5F, current.ne, zf - 0.5F}, -SURFACE_RESET_Y_OFFSET;
Vector3{xf - 0.5F, current.sw, zf + 0.5F}, color); }
DrawTriangle3D(Vector3{xf - 0.5F, current.nw + reset_y_offset, zf - 0.5F},
Vector3{xf - 0.5F, current.sw + reset_y_offset, zf + 0.5F},
Vector3{xf + 0.5F, current.ne + reset_y_offset, zf - 0.5F},
color);
DrawTriangle3D(Vector3{xf + 0.5F, current.se + reset_y_offset, zf + 0.5F},
Vector3{xf + 0.5F, current.ne + reset_y_offset, zf - 0.5F},
Vector3{xf - 0.5F, current.sw + reset_y_offset, zf + 0.5F},
color);
} }
for (auto &walker : walkers) { for (auto &walker : walkers) {
@ -250,7 +302,7 @@ bool TRunnerScreen::draw() {
} }
// TODO DEBUG // TODO DEBUG
if (!controlled_walker_idx.has_value()) { if (!controlled_walker_idx.has_value() && !flags.test(0)) {
DrawLine3D(Vector3{0.0F, 3.0F, 0.0F}, mouse_hit, BLACK); DrawLine3D(Vector3{0.0F, 3.0F, 0.0F}, mouse_hit, BLACK);
for (const auto &walker : walkers) { for (const auto &walker : walkers) {
@ -262,29 +314,77 @@ bool TRunnerScreen::draw() {
} }
EndMode3D(); EndMode3D();
EndTextureMode();
BeginTextureMode(fgRenderTexture);
ClearBackground(Color{0, 0, 0, 0});
if (flags.test(0)) {
BeginMode3D(camera);
for (unsigned int idx = 0; idx < SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT;
++idx) {
int x = idx % SURFACE_UNIT_WIDTH;
int y = idx / SURFACE_UNIT_WIDTH;
int ox = x - SURFACE_UNIT_WIDTH / 2;
int oy = y - SURFACE_UNIT_HEIGHT / 2;
Color color = idx == idx_hit
? RAYWHITE
: Color{(unsigned char)(200 + ox * 2),
(unsigned char)(150 + oy * 2), 20, 255};
if (surface_reset_anim_timer < SURFACE_RESET_TIME_TRI_DRAW) {
unsigned char alpha =
((1.0F - surface_reset_anim_timer / SURFACE_RESET_TIME_TRI_DRAW) *
255.0F);
surface_triangles.at(x * 2 + y * SURFACE_UNIT_WIDTH * 2)
.draw(Color{color.r, color.g, color.b, alpha});
surface_triangles.at(x * 2 + 1 + y * SURFACE_UNIT_WIDTH * 2)
.draw(Color{color.r, color.g, color.b, alpha});
}
}
EndMode3D();
}
if (controlled_walker_idx.has_value()) { if (controlled_walker_idx.has_value()) {
int total_width = 0; int total_width = 0;
DrawRectangle(0, GetScreenHeight() - BUTTON_FONT_SIZE, left_text_width, DrawRectangle(0, GetScreenHeight() - BUTTON_FONT_SIZE, left_text_width,
BUTTON_FONT_SIZE, Color{255, 255, 255, 120}); BUTTON_FONT_SIZE, Color{255, 255, 255, 180});
DrawText("Left", 0, GetScreenHeight() - BUTTON_FONT_SIZE, BUTTON_FONT_SIZE, DrawText("Left", 0, GetScreenHeight() - BUTTON_FONT_SIZE, BUTTON_FONT_SIZE,
BLACK); BLACK);
total_width += left_text_width; total_width += left_text_width;
DrawRectangle(total_width, GetScreenHeight() - BUTTON_FONT_SIZE, DrawRectangle(total_width, GetScreenHeight() - BUTTON_FONT_SIZE,
right_text_width, BUTTON_FONT_SIZE, right_text_width, BUTTON_FONT_SIZE,
Color{255, 255, 255, 120}); Color{255, 255, 255, 180});
DrawText("Right", total_width, GetScreenHeight() - BUTTON_FONT_SIZE, DrawText("Right", total_width, GetScreenHeight() - BUTTON_FONT_SIZE,
BUTTON_FONT_SIZE, BLACK); BUTTON_FONT_SIZE, BLACK);
total_width = (total_width + right_text_width) / 2 - forward_text_width / 2; total_width = (total_width + right_text_width) / 2 - forward_text_width / 2;
DrawRectangle(total_width, GetScreenHeight() - BUTTON_FONT_SIZE * 2, DrawRectangle(total_width, GetScreenHeight() - BUTTON_FONT_SIZE * 2,
forward_text_width, BUTTON_FONT_SIZE, forward_text_width, BUTTON_FONT_SIZE,
Color{255, 255, 255, 120}); Color{255, 255, 255, 180});
DrawText("Forward", total_width, GetScreenHeight() - BUTTON_FONT_SIZE * 2, DrawText("Forward", total_width, GetScreenHeight() - BUTTON_FONT_SIZE * 2,
BUTTON_FONT_SIZE, BLACK); BUTTON_FONT_SIZE, BLACK);
} }
if (!flags.test(0)) {
DrawRectangle(GetScreenWidth() - reset_surface_text_width, 0,
reset_surface_text_width, BUTTON_FONT_SIZE,
Color{255, 255, 255, 180});
DrawText("Reset Surface", GetScreenWidth() - reset_surface_text_width, 0,
BUTTON_FONT_SIZE, BLACK);
}
EndTextureMode();
BeginDrawing();
DrawTextureRec(
bgRenderTexture.texture,
Rectangle{0, 0, (float)GetScreenWidth(), (float)-GetScreenHeight()},
{0, 0}, WHITE);
DrawTextureRec(
fgRenderTexture.texture,
Rectangle{0, 0, (float)GetScreenWidth(), (float)-GetScreenHeight()},
{0, 0}, WHITE);
EndDrawing(); EndDrawing();
return false; return false;
@ -496,3 +596,10 @@ void TRunnerScreen::generate_surface() {
} }
} }
} }
void TRunnerScreen::generate_surface_with_triangles() {
surface_triangles = surface_to_triangles(surface, SURFACE_UNIT_WIDTH);
generate_surface();
surface_reset_anim_timer = 0.0F;
flags.set(0);
}

View file

@ -13,6 +13,7 @@
// local includes // local includes
#include "common_constants.h" #include "common_constants.h"
#include "surface_triangle.h"
#include "walker.h" #include "walker.h"
constexpr float POS_VALUE_INC_RATE = 0.2F; constexpr float POS_VALUE_INC_RATE = 0.2F;
@ -22,8 +23,16 @@ constexpr float SURFACE_HEIGHT_INTERVAL = 0.7F;
constexpr int BUTTON_FONT_SIZE = 30; constexpr int BUTTON_FONT_SIZE = 30;
constexpr float SURFACE_RESET_TIME = 4.0F;
constexpr float SURFACE_RESET_TIME_TRI_DRAW = 3.0F;
constexpr float SURFACE_RESET_Y_OFFSET = 40.0F;
class TRunnerScreen : public Screen { class TRunnerScreen : public Screen {
public: public:
struct SurfaceUnit {
float nw, ne, sw, se;
};
TRunnerScreen(std::weak_ptr<ScreenStack> stack); TRunnerScreen(std::weak_ptr<ScreenStack> stack);
~TRunnerScreen() override; ~TRunnerScreen() override;
@ -47,24 +56,26 @@ class TRunnerScreen : public Screen {
PIXEL_WHITE PIXEL_WHITE
}; };
struct SurfaceUnit {
float nw, ne, sw, se;
};
static Color PixelToColor(Pixel p); static Color PixelToColor(Pixel p);
std::array<std::optional<SurfaceUnit>, std::array<std::optional<SurfaceUnit>,
SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT> SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT>
surface; surface;
std::array<BoundingBox, SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT> surface_bbs; std::array<BoundingBox, SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT> surface_bbs;
std::array<SurfaceTriangle, SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT * 2>
surface_triangles;
std::array<Walker, 4> walkers; std::array<Walker, 4> walkers;
Camera3D camera; Camera3D camera;
/*
* 0 - resetting surface
*/
std::bitset<64> flags; std::bitset<64> flags;
Model TEMP_cube_model; Model TEMP_cube_model;
Texture2D TEMP_cube_texture; Texture2D TEMP_cube_texture;
Matrix TEMP_matrix; Matrix TEMP_matrix;
RenderTexture2D bgRenderTexture;
RenderTexture2D fgRenderTexture;
Vector3 camera_pos; Vector3 camera_pos;
Vector3 camera_target; Vector3 camera_target;
Vector3 mouse_hit; Vector3 mouse_hit;
@ -73,9 +84,12 @@ class TRunnerScreen : public Screen {
const int left_text_width; const int left_text_width;
const int right_text_width; const int right_text_width;
const int forward_text_width; const int forward_text_width;
const int reset_surface_text_width;
float surface_reset_anim_timer;
void camera_to_targets(float dt); void camera_to_targets(float dt);
void generate_surface(); void generate_surface();
void generate_surface_with_triangles();
}; };
#endif #endif

60
src/surface_triangle.cc Normal file
View file

@ -0,0 +1,60 @@
#include "surface_triangle.h"
// third party includes
#include <raylib.h>
#include <raymath.h>
// local includes
#include "3d_helpers.h"
#include "ems.h"
SurfaceTriangle::SurfaceTriangle()
: triangle_coords{Vector3{0.5F, 0.0F, -0.5F}, Vector3{-0.5F, 0.0F, -0.5F},
Vector3{-0.5F, 0.0F, 0.5F}},
triangle_pos{0.0F, 0.0F, 0.0F},
rotate_axis{0.0F, 1.0F, 0.0F},
pos_move_dir{0.0F, 1.0F, 0.0F},
rotation(0.0F),
move_amount(0.0F) {}
SurfaceTriangle::SurfaceTriangle(Vector3 a, Vector3 b, Vector3 c, Vector3 pos)
: triangle_coords{a, b, c},
triangle_pos{pos},
rotate_axis{call_js_get_random() * 2.0F - 1.0F,
call_js_get_random() * 2.0F - 1.0F,
call_js_get_random() * 2.0F - 1.0F},
pos_move_dir{call_js_get_random() * 2.0F - 1.0F,
call_js_get_random() * 2.0F - 1.0F,
call_js_get_random() * 2.0F - 1.0F},
rotation(0.0F),
move_amount(0.0F) {
if (FloatEquals(rotate_axis.x, 0.0F) && FloatEquals(rotate_axis.x, 0.0F) &&
FloatEquals(rotate_axis.x, 0.0F)) {
rotate_axis.x = 1.0F;
}
if (FloatEquals(pos_move_dir.x, 0.0F) && FloatEquals(pos_move_dir.x, 0.0F) &&
FloatEquals(pos_move_dir.x, 0.0F)) {
pos_move_dir.x = 1.0F;
}
rotate_axis = Vector3Normalize(rotate_axis);
pos_move_dir = Vector3Normalize(pos_move_dir);
}
void SurfaceTriangle::update(float dt) {
rotation += dt * SURFACE_TRIANGLE_ROTATION_RATE;
move_amount += dt * SURFACE_TRIANGLE_MOVE_RATE;
}
void SurfaceTriangle::draw(Color color) {
const auto mat =
MatrixRotate(rotate_axis, rotation) *
MatrixTranslate(triangle_pos.x + pos_move_dir.x * move_amount,
triangle_pos.y + pos_move_dir.y * move_amount,
triangle_pos.z + pos_move_dir.z * move_amount);
Vector3 a = mat * triangle_coords[0];
Vector3 b = mat * triangle_coords[1];
Vector3 c = mat * triangle_coords[2];
DrawTriangle3D(a, b, c, color);
}

72
src/surface_triangle.h Normal file
View file

@ -0,0 +1,72 @@
#ifndef JUMPARTIFACT_DOT_COM_DEMO_0_SURFACE_TRIANGLE_H_
#define JUMPARTIFACT_DOT_COM_DEMO_0_SURFACE_TRIANGLE_H_
// standard library includes
#include <array>
// third party includes
#include <raylib.h>
// local includes
#include "common_constants.h"
constexpr float SURFACE_TRIANGLE_ROTATION_RATE = 0.4F;
constexpr float SURFACE_TRIANGLE_MOVE_RATE = 1.0F;
struct SurfaceTriangle {
SurfaceTriangle();
SurfaceTriangle(Vector3 a, Vector3 b, Vector3 c, Vector3 pos);
std::array<Vector3, 3> triangle_coords;
Vector3 triangle_pos;
Vector3 rotate_axis;
Vector3 pos_move_dir;
float rotation;
float move_amount;
void update(float dt);
void draw(Color color);
};
template <typename SurfaceUnitOptT, std::size_t ASize>
extern std::array<SurfaceTriangle, ASize * 2> surface_to_triangles(
const std::array<SurfaceUnitOptT, ASize> &surface,
const std::size_t width) {
std::array<SurfaceTriangle, ASize * 2> triangles;
for (std::size_t idx = 0; idx < ASize; ++idx) {
std::size_t x = idx % width;
std::size_t y = idx / width;
float posx = ((float)x) - SURFACE_X_OFFSET;
float posz = ((float)y) - SURFACE_Y_OFFSET;
std::size_t toffset = x * 2 + y * width * 2;
if (!surface[idx].has_value()) {
triangles.at(toffset) = SurfaceTriangle();
triangles.at(toffset + 1) = SurfaceTriangle();
continue;
}
const auto &surface_unit = surface[idx].value();
triangles.at(toffset) = SurfaceTriangle(
Vector3{0.5F, surface_unit.ne, -0.5F},
Vector3{-0.5F, surface_unit.nw, -0.5F},
Vector3{-0.5F, surface_unit.sw, 0.5F},
Vector3{posx,
(surface_unit.ne + surface_unit.nw + surface_unit.sw) / 3.0F,
posz});
triangles.at(toffset + 1) = SurfaceTriangle(
Vector3{0.5F, surface_unit.ne, -0.5F},
Vector3{-0.5F, surface_unit.sw, 0.5F},
Vector3{0.5F, surface_unit.se, 0.5F},
Vector3{posx,
(surface_unit.ne + surface_unit.sw + surface_unit.se) / 3.0F,
posz});
}
return triangles;
}
#endif

View file

@ -15,7 +15,8 @@ SOURCES = \
../src/screen_trunner.cc \ ../src/screen_trunner.cc \
../src/3d_helpers.cc \ ../src/3d_helpers.cc \
../src/raymath.cc \ ../src/raymath.cc \
../src/walker.cc ../src/walker.cc \
../src/surface_triangle.cc
HEADERS = \ HEADERS = \
../src/ems.h \ ../src/ems.h \
@ -24,7 +25,8 @@ HEADERS = \
../src/screen_test.h \ ../src/screen_test.h \
../src/screen_trunner.h \ ../src/screen_trunner.h \
../src/3d_helpers.h \ ../src/3d_helpers.h \
../src/walker.h ../src/walker.h \
../src/surface_triangle.h
OBJECTS = $(addprefix ${OBJDIR}/,$(subst ..,PREVDIR,$(subst .cc,.cc.o,${SOURCES}))) OBJECTS = $(addprefix ${OBJDIR}/,$(subst ..,PREVDIR,$(subst .cc,.cc.o,${SOURCES})))