(call_js_get_random() * radius));
}
+std::array<Vector3, 4> get_quad_from_start_end(Vector3 start, Vector3 end,
+ Vector3 normal, float width) {
+ std::array<Vector3, 4> 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};
}
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<std::tuple<Vector3, int>> positions;
std::tuple<Vector3, int> 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();
next_pos + Vector3Normalize(coll.point - next_pos) * line_max_length;
}
- cylinders.push_back(Cylinder{
+ end_points.push_back(EndPoint{
.next_idx = std::get<int>(next),
.point = coll.point,
.mdir = Vector3Normalize(Vector3{call_js_get_random() * 2.0F - 1.0F,
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});
}
}
}
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<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);
+ DrawTriangle3D(quad[0], quad[1], quad[2], color);
+ DrawTriangle3D(quad[0], quad[2], quad[3], color);
}
}
}
// third party includes
#include <raylib.h>
-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:
/// 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<Cylinder> cylinders;
+ std::vector<EndPoint> end_points;
Vector3 center;
float radius;
float lifetime;