]> git.seodisparate.com - jumpartifact.com_demo_0/commitdiff
Impl. shader to improve ElectricityEffect
authorStephen Seo <seo.disparate@gmail.com>
Wed, 30 Aug 2023 08:38:11 +0000 (17:38 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Wed, 30 Aug 2023 08:38:11 +0000 (17:38 +0900)
src/electricity_effect.cc
src/electricity_effect.h
src/screen_trunner.cc

index 0a160e992cf2ac9adfa2384250c9f6513b7b5b33..03b0615f7d15191394e928894743d8f42fcf2ca3 100644 (file)
@@ -1,8 +1,23 @@
 #include "electricity_effect.h"
 
+#ifndef NDEBUG
+#define DEBUG_PRINT_VEC2(v2)                                           \
+  do {                                                                 \
+    std::cout << "Loading " << #v2 << " with value " << (v2).x << ", " \
+              << (v2).y << std::endl;                                  \
+  } while (false);
+#define DEBUG_PRINT_FLOAT(f)                                             \
+  do {                                                                   \
+    std::cout << "Loading " << #f << " with value " << (f) << std::endl; \
+  } while (false);
+#endif
+
 // standard library includes
 #include <queue>
 #include <tuple>
+#ifndef NDEBUG
+#include <iostream>
+#endif
 
 // third party includes
 #include <raylib.h>
@@ -12,6 +27,8 @@
 #include "3d_helpers.h"
 #include "ems.h"
 
+std::optional<Shader> ElectricityEffect::shader = std::nullopt;
+
 ElectricityEffect::ElectricityEffect(Vector3 center, float radius,
                                      int line_count, float lifetime)
     : end_points(),
@@ -70,6 +87,11 @@ ElectricityEffect::ElectricityEffect(Vector3 center, float radius,
       positions.push({coll.point, end_points.size() - 1});
     }
   }
+
+  // Update shader height if not initialized.
+  if (!shader.has_value()) {
+    update_shader_height();
+  }
 }
 
 bool ElectricityEffect::update(float dt) {
@@ -88,16 +110,154 @@ bool ElectricityEffect::update(float dt) {
   return timer >= lifetime;
 }
 
-void ElectricityEffect::draw(Color color, Vector3 camera_pos) {
+void ElectricityEffect::draw(Color color, Camera *camera) {
   float ratio = timer < lifetime ? (1.0F - timer / lifetime) : 0.0F;
 
+  // Update shader values common to all quads.
+  {
+    int colDiffuse = GetShaderLocation(shader.value(), "colDiffuse");
+    Vector4 normalizedColor = ColorNormalize(color);
+    SetShaderValue(shader.value(), colDiffuse, &normalizedColor,
+                   SHADER_UNIFORM_VEC4);
+  }
+
   for (const auto &end_point : end_points) {
     if (end_point.next_idx >= 0) {
       std::array<Vector3, 4> quad = get_quad_from_start_end(
           end_point.point, end_points[end_point.next_idx].point,
-          camera_pos - end_point.point, QUAD_MAX_WIDTH * ratio);
+          camera->position - end_point.point, QUAD_MAX_WIDTH * ratio);
+      Vector2 q0 = GetWorldToScreen(quad[0], *camera);
+      Vector2 q1 = GetWorldToScreen(quad[3], *camera);
+      Vector2 q2 = GetWorldToScreen(quad[1], *camera);
+      Vector2 q3 = GetWorldToScreen(quad[2], *camera);
+      q1 = Vector2Subtract(q0, q1);
+      q3 = Vector2Subtract(q2, q3);
+
+      // Update shader values.
+      update_shader_sides(q0, q1, q2, q3, Vector2Distance(q0, q2));
+
+      // Draw with shader.
+      BeginShaderMode(shader.value());
       DrawTriangle3D(quad[0], quad[1], quad[2], color);
       DrawTriangle3D(quad[0], quad[2], quad[3], color);
+      EndShaderMode();
     }
   }
 }
+
+Shader ElectricityEffect::get_shader() {
+  if (!shader.has_value()) {
+    init_shader();
+  }
+  return shader.value();
+}
+
+void ElectricityEffect::cleanup_shader() {
+  if (shader.has_value()) {
+    UnloadShader(shader.value());
+    shader.reset();
+  }
+}
+
+void ElectricityEffect::update_shader_height() {
+  if (!shader.has_value()) {
+    init_shader();
+  }
+  int uniform_loc = GetShaderLocation(shader.value(), "screen_height");
+  float height = GetScreenHeight();
+  DEBUG_PRINT_FLOAT(height);
+  SetShaderValue(shader.value(), uniform_loc, &height, SHADER_UNIFORM_FLOAT);
+}
+
+void ElectricityEffect::update_shader_sides(Vector2 a, Vector2 adir, Vector2 b,
+                                            Vector2 bdir, float width) {
+  if (!shader.has_value()) {
+    init_shader();
+  }
+  int uniform_loc = GetShaderLocation(shader.value(), "sidePosA");
+  DEBUG_PRINT_VEC2(a);
+  SetShaderValue(shader.value(), uniform_loc, &a, SHADER_UNIFORM_VEC2);
+  uniform_loc = GetShaderLocation(shader.value(), "sideDirA");
+  DEBUG_PRINT_VEC2(adir);
+  SetShaderValue(shader.value(), uniform_loc, &adir, SHADER_UNIFORM_VEC2);
+  uniform_loc = GetShaderLocation(shader.value(), "sidePosB");
+  DEBUG_PRINT_VEC2(b);
+  SetShaderValue(shader.value(), uniform_loc, &b, SHADER_UNIFORM_VEC2);
+  uniform_loc = GetShaderLocation(shader.value(), "sideDirB");
+  DEBUG_PRINT_VEC2(bdir);
+  SetShaderValue(shader.value(), uniform_loc, &bdir, SHADER_UNIFORM_VEC2);
+  uniform_loc = GetShaderLocation(shader.value(), "width");
+  DEBUG_PRINT_FLOAT(width);
+  SetShaderValue(shader.value(), uniform_loc, &width, SHADER_UNIFORM_FLOAT);
+}
+
+void ElectricityEffect::init_shader() {
+  // Set up electricity shader.
+  // Vertex shader is exactly the same as Raylib's default vertex shader.
+  shader = LoadShaderFromMemory(
+      // vertex
+      "#version 100                       \n"
+      "attribute vec3 vertexPosition;     \n"
+      "attribute vec2 vertexTexCoord;     \n"
+      "attribute vec4 vertexColor;        \n"
+      "varying vec2 fragTexCoord;         \n"
+      "varying vec4 fragColor;            \n"
+      "uniform mat4 mvp;                  \n"
+      "void main()                        \n"
+      "{                                  \n"
+      "    fragTexCoord = vertexTexCoord; \n"
+      "    fragColor = vertexColor;       \n"
+      "    gl_Position = mvp*vec4(vertexPosition, 1.0); \n"
+      "}                                  \n",
+
+      // fragment
+      "#version 100                       \n"
+      "precision mediump float;           \n"
+      "varying vec2 fragTexCoord;         \n"
+      "varying vec4 fragColor;            \n"
+      "uniform sampler2D texture0;        \n"
+      "uniform vec4 colDiffuse;           \n"
+      "uniform float screen_height;       \n"
+      "uniform float width;             \n"
+      "uniform vec2 sidePosA;           \n"
+      "uniform vec2 sideDirA;           \n"
+      "uniform vec2 sidePosB;           \n"
+      "uniform vec2 sideDirB;           \n"
+      "float dot_get_alpha(vec2 pos, vec2 dir, vec2 point) { \n"
+      "  return ((dir.x * point.x + dir.y * point.y)   \n"
+      "             - (dir.x * pos.x + dir.y * pos.y)) \n"
+      "           / (dir.x * dir.x + dir.y * dir.y);   \n"
+      "}                                  \n"
+      "vec2 closest_point(vec2 pos, vec2 dir, vec2 point) { \n"
+      "  return pos + dir * dot_get_alpha(pos, dir, point); \n"
+      "}                                  \n"
+      "void main()                        \n"
+      "{                                  \n"
+      "    vec4 texelColor = texture2D(texture0, fragTexCoord); \n"
+      "    vec4 color = texelColor*colDiffuse*fragColor;      \n"
+      "    vec2 pos = gl_FragCoord.xy;    \n"
+      "    pos.y = screen_height - pos.y; \n"
+      "    vec2 closest = closest_point(sidePosA, sideDirA, pos); \n"
+      "    float distA = distance(pos, closest); \n"
+      "    closest = closest_point(sidePosB, sideDirB, pos); \n"
+      "    float distB = distance(pos, closest); \n"
+      "    float min_dist = width * 0.3;  \n"
+      "    if (min_dist < 0.00001) {      \n"
+      "        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); \n"
+      "    } else if (distA < distB) {    \n"
+      "        if (distA < min_dist) {    \n"
+      "            float lerpVal = distA / min_dist; \n"
+      "            gl_FragColor = color * (1.0 - lerpVal) \n"
+      "                    + vec4(1.0, 1.0, 1.0, 1.0) * lerpVal; \n"
+      "        } else {                   \n"
+      "            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); \n"
+      "        }                          \n"
+      "    } else if (distB < min_dist) { \n"
+      "        float lerpVal = distB / min_dist; \n"
+      "        gl_FragColor = color * (1.0 - lerpVal) \n"
+      "                + vec4(1.0, 1.0, 1.0, 1.0) * lerpVal; \n"
+      "    } else {                       \n"
+      "        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); \n"
+      "    }                              \n"
+      "}                                  \n");
+}
index da49892efa58002043308bd355bd927534f86c6d..2f3b6deacdb46e6161bc7d8bf4f969db7fba0931 100644 (file)
@@ -2,6 +2,7 @@
 #define JUMPARTIFACT_DOT_COM_DEMO_0_ELECTRICITY_EFFECT_H_
 
 // standard library includes
+#include <optional>
 #include <vector>
 
 // third party includes
@@ -21,7 +22,13 @@ class ElectricityEffect {
   /// Returns true if lifetime ended.
   bool update(float dt);
   /// Assumes draw mode is active.
-  void draw(Color color, Vector3 camera_pos);
+  void draw(Color color, Camera *camera);
+
+  static Shader get_shader();
+  static void cleanup_shader();
+  static void update_shader_height();
+  static void update_shader_sides(Vector2 a, Vector2 adir, Vector2 b,
+                                  Vector2 bdir, float width);
 
  private:
   struct EndPoint {
@@ -29,11 +36,14 @@ class ElectricityEffect {
     Vector3 point, mdir;
   };
 
+  static std::optional<Shader> shader;
   std::vector<EndPoint> end_points;
   Vector3 center;
   float radius;
   float lifetime;
   float timer;
+
+  static void init_shader();
 };
 
 #endif
index 2c2ce5d298b22a8f7724704d8cf882212ab24ef6..bb6e6258e6b2eccedb0cad435150bd1214191871 100644 (file)
@@ -76,12 +76,17 @@ TRunnerScreen::TRunnerScreen(std::weak_ptr<ScreenStack> stack)
   bgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
   fgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
 
+  // Initialize ElectricityEffect shader.
+  ElectricityEffect::update_shader_height();
+
 #ifndef NDEBUG
   std::cout << "Screen finished init.\n";
 #endif
 }
 
 TRunnerScreen::~TRunnerScreen() {
+  ElectricityEffect::cleanup_shader();
+
   UnloadRenderTexture(fgRenderTexture);
   UnloadRenderTexture(bgRenderTexture);
 
@@ -96,6 +101,8 @@ bool TRunnerScreen::update(float dt, bool is_resized) {
 
     bgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
     fgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
+
+    ElectricityEffect::update_shader_height();
   }
 
   if (flags.test(1)) {
@@ -365,7 +372,7 @@ bool TRunnerScreen::draw(RenderTexture *render_texture) {
   }
 
   for (auto &ee : electricityEffects) {
-    ee.draw(GREEN, camera.position);
+    ee.draw(GREEN, &camera);
   }
 
   for (auto &se : sparkEffects) {