]> git.seodisparate.com - jumpartifact.com_demo_0/commitdiff
Impl. resetting surface with neat visual effect
authorStephen Seo <seo.disparate@gmail.com>
Mon, 21 Aug 2023 05:37:50 +0000 (14:37 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Mon, 21 Aug 2023 05:37:50 +0000 (14:37 +0900)
Makefile
src/screen_trunner.cc
src/screen_trunner.h
src/surface_triangle.cc [new file with mode: 0644]
src/surface_triangle.h [new file with mode: 0644]
wasm_build/Makefile

index 58a313cdf23080556ce8f2e24df43d256ca06987..f64f735ca4191758a27bed6dfa7f3ccf7f1a47aa 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -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}))
 
index 94cf314d714eee560e19d1ba49f4fc1f3914c03a..24f55300f5db8b3b3679aa4950ac7040a593e799 100644 (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,120 +109,132 @@ bool TRunnerScreen::update(float dt) {
     camera_pos = walkers[controlled_walker_idx.value()].get_body_pos() + offset;
   }
 
-  if (controlled_walker_idx.has_value() && IsMouseButtonDown(0)) {
-    // Check if clicked on button.
-    if (!walkers[controlled_walker_idx.value()].player_is_turning_left() &&
-        GetTouchX() >= 0 && GetTouchX() <= left_text_width &&
-        GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE &&
-        GetTouchY() <= GetScreenHeight()) {
-      walkers[controlled_walker_idx.value()].player_turn_left();
-      goto post_check_click;
-    } 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();
+  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() &&
+          GetTouchX() >= 0 && GetTouchX() <= left_text_width &&
+          GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE &&
+          GetTouchY() <= GetScreenHeight()) {
+        walkers[controlled_walker_idx.value()].player_turn_left();
+        goto post_check_click;
+      } 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;
+        }
+      }
+    } else if (IsMouseButtonReleased(0)) {
+      if (controlled_walker_idx.has_value()) {
+        walkers[controlled_walker_idx.value()].player_idle();
         goto post_check_click;
       }
     }
-  } else if (IsMouseButtonReleased(0)) {
-    if (controlled_walker_idx.has_value()) {
-      walkers[controlled_walker_idx.value()].player_idle();
-      goto post_check_click;
-    }
-  }
 
-  if (IsMouseButtonPressed(0)) {
-    float press_x = GetTouchX();
-    float press_y = GetTouchY();
-    Ray ray = GetMouseRay(Vector2{press_x, press_y}, camera);
+    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 << "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
 
-    // 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);
+      // 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);
 
-        idx_hit = SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT;
+          idx_hit = SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT;
 
-        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;
+      // 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;
+          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;
+          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;
+          }
         }
       }
     }
@@ -210,17 +242,29 @@ 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);
+}
index 92888e8a26b8f454b1bd1a2ea2c9a6fc63a87d51..8b460422fe933a82fbbfe3e02baa5994e3ca79a3 100644 (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
diff --git a/src/surface_triangle.cc b/src/surface_triangle.cc
new file mode 100644 (file)
index 0000000..8f9d91c
--- /dev/null
@@ -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);
+}
diff --git a/src/surface_triangle.h b/src/surface_triangle.h
new file mode 100644 (file)
index 0000000..b13539e
--- /dev/null
@@ -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
index 92d1f8f0057f7d5fcdf000b143ff2e9bae9f4257..2c4f795c856c8a18b845580557fe42405ad28e96 100644 (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})))