diff --git a/src/3d_helpers.cc b/src/3d_helpers.cc index 9d17173..4057a95 100644 --- a/src/3d_helpers.cc +++ b/src/3d_helpers.cc @@ -181,6 +181,21 @@ Vector3 from_edge_to_sphere_random(Vector3 center, Vector3 point, (call_js_get_random() * radius)); } +std::array get_quad_from_start_end(Vector3 start, Vector3 end, + Vector3 normal, float width) { + std::array quad; + Vector3 start_to_end = Vector3Normalize(end - start); + + // Normalize just in case "normal" isn't actually a true normal. + quad[0] = Vector3Normalize(Vector3CrossProduct(start_to_end, normal)); + quad[1] = start + quad[0] * (width / 2.0F); + quad[2] = end + quad[0] * (width / 2.0F); + quad[3] = end - quad[0] * (width / 2.0F); + quad[0] = start - quad[0] * (width / 2.0F); + + return quad; +} + Vector3 operator+(const Vector3 &a, const Vector3 &b) { return Vector3{a.x + b.x, a.y + b.y, a.z + b.z}; } diff --git a/src/3d_helpers.h b/src/3d_helpers.h index b4cfcd4..e0f0190 100644 --- a/src/3d_helpers.h +++ b/src/3d_helpers.h @@ -2,6 +2,7 @@ #define JUMPARTIFACT_DOT_COM_DEMO_0_3D_HELPERS_H_ // standard library includes +#include #include // third party includes @@ -30,6 +31,21 @@ extern std::optional ray_to_plane(const Ray &ray, const Ray &plane); extern Vector3 from_edge_to_sphere_random(Vector3 center, Vector3 point, float radius); +/* + * start side + * b -- a + * | | + * | | + * c -- d + * end side + * + * (Normal facing outwards from screen.) + */ +extern std::array get_quad_from_start_end(Vector3 start, + Vector3 end, + Vector3 normal, + float width); + // Unimplemented as this function isn't really needed and it exposes some // weirdness regarding column-major matrices. // extern Vector4 operator*(const Matrix &m, const Vector4 &v); diff --git a/src/electricity_effect.cc b/src/electricity_effect.cc index ea9435d..0a160e9 100644 --- a/src/electricity_effect.cc +++ b/src/electricity_effect.cc @@ -14,29 +14,29 @@ ElectricityEffect::ElectricityEffect(Vector3 center, float radius, int line_count, float lifetime) - : cylinders(), + : end_points(), center(center), radius(radius), lifetime(lifetime), timer(0.0F) { - cylinders.reserve(line_count); + end_points.reserve(line_count); - const float line_max_length = radius * CYLINDER_LINE_MAX_LENGTH_RATIO; + const float line_max_length = radius * QUAD_LINE_MAX_LENGTH_RATIO; - // Generate cylinders. + // Generate end_points. std::queue> positions; std::tuple next; Vector3 next_pos, dir; - for (unsigned int idx = 0; idx < CYLINDER_SPLIT_COUNT && line_count > 0; + for (unsigned int idx = 0; idx < QUAD_SPLIT_COUNT && line_count > 0; ++idx, --line_count) { - cylinders.push_back(Cylinder{.next_idx = -1, - .point = center, - .mdir = Vector3Normalize(Vector3{ - call_js_get_random() * 2.0F - 1.0F, - call_js_get_random() * 2.0F - 1.0F, - call_js_get_random() * 2.0F - 1.0F, - })}); - positions.push({center, cylinders.size() - 1}); + end_points.push_back(EndPoint{.next_idx = -1, + .point = center, + .mdir = Vector3Normalize(Vector3{ + call_js_get_random() * 2.0F - 1.0F, + call_js_get_random() * 2.0F - 1.0F, + call_js_get_random() * 2.0F - 1.0F, + })}); + positions.push({center, end_points.size() - 1}); } while (line_count-- > 0 && !positions.empty()) { next = positions.front(); @@ -57,7 +57,7 @@ ElectricityEffect::ElectricityEffect(Vector3 center, float radius, next_pos + Vector3Normalize(coll.point - next_pos) * line_max_length; } - cylinders.push_back(Cylinder{ + end_points.push_back(EndPoint{ .next_idx = std::get(next), .point = coll.point, .mdir = Vector3Normalize(Vector3{call_js_get_random() * 2.0F - 1.0F, @@ -65,9 +65,9 @@ ElectricityEffect::ElectricityEffect(Vector3 center, float radius, call_js_get_random() * 2.0F - 1.0F})}); dir = Vector3Normalize(center - coll.point); - coll.point = coll.point + dir * (radius * CYLINDER_EDGE_OFFSET); - for (unsigned int idx = 0; idx < CYLINDER_SPLIT_COUNT; ++idx) { - positions.push({coll.point, cylinders.size() - 1}); + coll.point = coll.point + dir * (radius * QUAD_EDGE_OFFSET); + for (unsigned int idx = 0; idx < QUAD_SPLIT_COUNT; ++idx) { + positions.push({coll.point, end_points.size() - 1}); } } } @@ -75,27 +75,29 @@ ElectricityEffect::ElectricityEffect(Vector3 center, float radius, bool ElectricityEffect::update(float dt) { timer += dt; - for (auto &cylinder : cylinders) { - cylinder.point = cylinder.point + cylinder.mdir * (dt * CYLINDER_MOVE_RATE); - if (Vector3Distance(cylinder.point, center) > radius) { - cylinder.point = - cylinder.point - cylinder.mdir * (dt * CYLINDER_MOVE_RATE); - cylinder.mdir = - from_edge_to_sphere_random(center, cylinder.point, radius); + for (auto &end_point : end_points) { + end_point.point = end_point.point + end_point.mdir * (dt * QUAD_MOVE_RATE); + if (Vector3Distance(end_point.point, center) > radius) { + end_point.point = + end_point.point - end_point.mdir * (dt * QUAD_MOVE_RATE); + end_point.mdir = + from_edge_to_sphere_random(center, end_point.point, radius); } } return timer >= lifetime; } -void ElectricityEffect::draw(Color color) { +void ElectricityEffect::draw(Color color, Vector3 camera_pos) { float ratio = timer < lifetime ? (1.0F - timer / lifetime) : 0.0F; - for (const auto &cylinder : cylinders) { - if (cylinder.next_idx >= 0) { - DrawCylinderEx(cylinder.point, cylinders.at(cylinder.next_idx).point, - CYLINDER_MAX_RADIUS * ratio, CYLINDER_MAX_RADIUS * ratio, - CYLINDER_SIDES, color); + for (const auto &end_point : end_points) { + if (end_point.next_idx >= 0) { + std::array 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); + DrawTriangle3D(quad[0], quad[1], quad[2], color); + DrawTriangle3D(quad[0], quad[2], quad[3], color); } } } diff --git a/src/electricity_effect.h b/src/electricity_effect.h index e82d63a..da49892 100644 --- a/src/electricity_effect.h +++ b/src/electricity_effect.h @@ -7,12 +7,11 @@ // third party includes #include -constexpr int CYLINDER_SPLIT_COUNT = 3; -constexpr int CYLINDER_SIDES = 3; -constexpr float CYLINDER_MAX_RADIUS = 0.03F; -constexpr float CYLINDER_EDGE_OFFSET = 0.01F; -constexpr float CYLINDER_LINE_MAX_LENGTH_RATIO = 0.85F; -constexpr float CYLINDER_MOVE_RATE = 0.05F; +constexpr int QUAD_SPLIT_COUNT = 3; +constexpr float QUAD_MAX_WIDTH = 0.06F; +constexpr float QUAD_EDGE_OFFSET = 0.01F; +constexpr float QUAD_LINE_MAX_LENGTH_RATIO = 0.85F; +constexpr float QUAD_MOVE_RATE = 0.05F; class ElectricityEffect { public: @@ -22,15 +21,15 @@ class ElectricityEffect { /// Returns true if lifetime ended. bool update(float dt); /// Assumes draw mode is active. - void draw(Color color); + void draw(Color color, Vector3 camera_pos); private: - struct Cylinder { + struct EndPoint { int next_idx; Vector3 point, mdir; }; - std::vector cylinders; + std::vector end_points; Vector3 center; float radius; float lifetime; diff --git a/src/screen_trunner.cc b/src/screen_trunner.cc index dd36e1a..2c2ce5d 100644 --- a/src/screen_trunner.cc +++ b/src/screen_trunner.cc @@ -72,7 +72,7 @@ TRunnerScreen::TRunnerScreen(std::weak_ptr stack) // Initialize surface. generate_surface(); - // Set up render textures + // Set up render textures. bgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight()); fgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight()); @@ -365,7 +365,7 @@ bool TRunnerScreen::draw(RenderTexture *render_texture) { } for (auto &ee : electricityEffects) { - ee.draw(GREEN); + ee.draw(GREEN, camera.position); } for (auto &se : sparkEffects) {