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

View file

@ -21,6 +21,7 @@ TRunnerScreen::TRunnerScreen(std::weak_ptr<ScreenStack> stack)
: Screen(stack),
surface(),
surface_bbs(),
surface_triangles(),
// NOLINTBEGIN(bugprone-integer-division)
walkers{Walker{(float)(SURFACE_UNIT_WIDTH / 4) - SURFACE_X_OFFSET,
(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_texture(LoadTexture("res/test_cube_texture.png")),
TEMP_matrix(get_identity_matrix()),
bgRenderTexture(),
fgRenderTexture(),
camera_pos{0.0F, 4.0F, 4.0F},
camera_target{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),
left_text_width(MeasureText("Left", 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
std::cout << "idx_hit initialized to " << idx_hit << std::endl;
#endif
@ -63,17 +68,32 @@ TRunnerScreen::TRunnerScreen(std::weak_ptr<ScreenStack> stack)
// Initialize surface.
generate_surface();
// Set up render textures
bgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
fgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
#ifndef NDEBUG
std::cout << "Screen finished init.\n";
#endif
}
TRunnerScreen::~TRunnerScreen() {
UnloadRenderTexture(fgRenderTexture);
UnloadRenderTexture(bgRenderTexture);
UnloadTexture(TEMP_cube_texture);
UnloadModel(TEMP_cube_model);
}
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()) {
auto walker_body_pos =
walkers[controlled_walker_idx.value()].get_body_pos();
@ -89,6 +109,7 @@ bool TRunnerScreen::update(float dt) {
camera_pos = walkers[controlled_walker_idx.value()].get_body_pos() + offset;
}
if (!flags.test(0)) {
if (controlled_walker_idx.has_value() && IsMouseButtonDown(0)) {
// Check if clicked on button.
if (!walkers[controlled_walker_idx.value()].player_is_turning_left() &&
@ -107,8 +128,8 @@ bool TRunnerScreen::update(float dt) {
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;
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 &&
@ -127,12 +148,22 @@ bool TRunnerScreen::update(float dt) {
if (IsMouseButtonPressed(0)) {
float press_x = GetTouchX();
float press_y = GetTouchY();
// Check if clicked on reset surface.
if (!flags.test(0) &&
press_x >= GetScreenWidth() - reset_surface_text_width &&
press_y <= BUTTON_FONT_SIZE) {
generate_surface_with_triangles();
goto post_check_click;
}
Ray ray = GetMouseRay(Vector2{press_x, press_y}, camera);
#ifndef NDEBUG
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;
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
// Check if clicked on a Walker.
@ -207,20 +238,33 @@ bool TRunnerScreen::update(float dt) {
}
}
}
}
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);
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;
}
bool TRunnerScreen::draw() {
BeginDrawing();
BeginTextureMode(bgRenderTexture);
ClearBackground(PixelToColor(Pixel::PIXEL_SKY));
BeginMode3D(camera);
@ -237,12 +281,20 @@ bool TRunnerScreen::draw() {
: Color{(unsigned char)(200 + ox * 2),
(unsigned char)(150 + oy * 2), 20, 255};
const auto &current = surface[idx].value();
DrawTriangle3D(Vector3{xf - 0.5F, current.nw, zf - 0.5F},
Vector3{xf - 0.5F, current.sw, zf + 0.5F},
Vector3{xf + 0.5F, current.ne, zf - 0.5F}, color);
DrawTriangle3D(Vector3{xf + 0.5F, current.se, zf + 0.5F},
Vector3{xf + 0.5F, current.ne, zf - 0.5F},
Vector3{xf - 0.5F, current.sw, zf + 0.5F}, color);
float reset_y_offset = 0.0F;
if (flags.test(0)) {
reset_y_offset = (1.0F - std::sin(surface_reset_anim_timer /
SURFACE_RESET_TIME * PI / 2.0F)) *
-SURFACE_RESET_Y_OFFSET;
}
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) {
@ -250,7 +302,7 @@ bool TRunnerScreen::draw() {
}
// 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);
for (const auto &walker : walkers) {
@ -262,29 +314,77 @@ bool TRunnerScreen::draw() {
}
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()) {
int total_width = 0;
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,
BLACK);
total_width += left_text_width;
DrawRectangle(total_width, GetScreenHeight() - 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,
BUTTON_FONT_SIZE, BLACK);
total_width = (total_width + right_text_width) / 2 - forward_text_width / 2;
DrawRectangle(total_width, GetScreenHeight() - BUTTON_FONT_SIZE * 2,
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,
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();
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
#include "common_constants.h"
#include "surface_triangle.h"
#include "walker.h"
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 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 {
public:
struct SurfaceUnit {
float nw, ne, sw, se;
};
TRunnerScreen(std::weak_ptr<ScreenStack> stack);
~TRunnerScreen() override;
@ -47,24 +56,26 @@ class TRunnerScreen : public Screen {
PIXEL_WHITE
};
struct SurfaceUnit {
float nw, ne, sw, se;
};
static Color PixelToColor(Pixel p);
std::array<std::optional<SurfaceUnit>,
SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT>
surface;
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;
Camera3D camera;
/*
* 0 - resetting surface
*/
std::bitset<64> flags;
Model TEMP_cube_model;
Texture2D TEMP_cube_texture;
Matrix TEMP_matrix;
RenderTexture2D bgRenderTexture;
RenderTexture2D fgRenderTexture;
Vector3 camera_pos;
Vector3 camera_target;
Vector3 mouse_hit;
@ -73,9 +84,12 @@ class TRunnerScreen : public Screen {
const int left_text_width;
const int right_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 generate_surface();
void generate_surface_with_triangles();
};
#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/3d_helpers.cc \
../src/raymath.cc \
../src/walker.cc
../src/walker.cc \
../src/surface_triangle.cc
HEADERS = \
../src/ems.h \
@ -24,7 +25,8 @@ HEADERS = \
../src/screen_test.h \
../src/screen_trunner.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})))