mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-05-07 11:13:53 +03:00
327 lines
9 KiB
C++
327 lines
9 KiB
C++
#include "framework.h"
|
|
#include "Game/effects/lightning.h"
|
|
|
|
#include "Game/effects/effects.h"
|
|
#include "Game/people.h"
|
|
#include "Math/Math.h"
|
|
#include "Specific/setup.h"
|
|
|
|
using namespace TEN::Math;
|
|
|
|
namespace TEN::Effects::ElectricArc
|
|
{
|
|
static constexpr auto HELICAL_LASER_LIFE_MAX = 18.0f;
|
|
|
|
std::vector<ElectricArc> ElectricArcs = {};
|
|
std::vector<HelicalLaser> HelicalLasers = {};
|
|
|
|
std::array<Vector3, ELECTRIC_ARC_KNOTS_SIZE> ElectricArcKnots = {};
|
|
std::array<Vector3, ELECTRIC_ARC_BUFFER_SIZE> ElectricArcBuffer = {};
|
|
|
|
// BIG TODO: Make a family of Bezier, B-Spline, and Catmull-Rom curve classes.
|
|
|
|
// More standard version. Adopt this in place of the one below.
|
|
static Vector3 CatmullRomSpline(float alpha, const std::array<Vector3, 4>& knots)
|
|
{
|
|
auto point1 = knots[1] + ((knots[2] - knots[0]) * (1 / 6.0f));
|
|
auto point2 = knots[2];
|
|
auto point3 = knots[2] + ((knots[3] - knots[1]) * (-1 / 6.0f));
|
|
auto point4 = knots[3];
|
|
|
|
auto spline = ((point2 * 2) + (point3 - point1) * alpha) +
|
|
(((point1 * 2) - (point2 * 5) + (point3 * 4) - point4) * SQUARE(alpha)) +
|
|
(((point1 * -1) + (point2 * 3) - (point3 * 3) + point4) * CUBE(alpha));
|
|
return spline;
|
|
}
|
|
|
|
// 4-point Catmull-Rom spline interpolation.
|
|
// Function takes reference to array of knots and
|
|
// calculates using subset of 4 determined alpha value.
|
|
static Vector3 ElectricArcSpline(const std::array<Vector3, ELECTRIC_ARC_KNOTS_SIZE>& knots, float alpha)
|
|
{
|
|
alpha *= ELECTRIC_ARC_KNOTS_SIZE - 3;
|
|
|
|
int span = alpha;
|
|
if (span >= (ELECTRIC_ARC_KNOTS_SIZE - 3))
|
|
span = ELECTRIC_ARC_KNOTS_SIZE - 4;
|
|
|
|
float something = alpha - span;
|
|
|
|
// Determine subset of 4 knots.
|
|
const auto& knot0 = knots[span];
|
|
const auto& knot1 = knots[span + 1];
|
|
const auto& knot2 = knots[span + 2];
|
|
const auto& knot3 = knots[span + 3];
|
|
|
|
auto point1 = knot1 + (knot1 / 2) - (knot2 / 2) - knot2 + (knot3 / 2) + ((-knot0 - Vector3::One) / 2);
|
|
auto ret = point1 * something;
|
|
auto point2 = ret + Vector3(2.0f) * knot2 - 2 * knot1 - (knot1 / 2) - (knot3 / 2) + knot0;
|
|
ret = point2 * something;
|
|
auto point3 = ret + (knot2 / 2) + ((-knot0 - Vector3::One) / 2);
|
|
ret = point3 * something;
|
|
|
|
return (ret + knot1);
|
|
}
|
|
|
|
// TODO: Pass const Vector4& for color.
|
|
void SpawnElectricArc(const Vector3& origin, const Vector3& target, float amplitude, byte r, byte g, byte b, float life, int flags, float width, unsigned int numSegments)
|
|
{
|
|
auto arc = ElectricArc();
|
|
|
|
arc.pos1 = origin;
|
|
arc.pos2 = ((origin * 3) + target) / 4;
|
|
arc.pos3 = ((target * 3) + origin) / 4;
|
|
arc.pos4 = target;
|
|
arc.flags = flags;
|
|
|
|
for (int i = 0; i < arc.interpolation.size(); i++)
|
|
{
|
|
if (arc.flags & LI_MOVEEND || i < (arc.interpolation.size() - 1))
|
|
{
|
|
arc.interpolation[i] = Vector3(
|
|
fmod(Random::GenerateInt(), amplitude),
|
|
fmod(Random::GenerateInt(), amplitude),
|
|
fmod(Random::GenerateInt(), amplitude)) -
|
|
Vector3(amplitude/ 2);
|
|
}
|
|
else
|
|
{
|
|
arc.interpolation[i] = Vector3::Zero;
|
|
}
|
|
}
|
|
|
|
arc.r = r;
|
|
arc.g = g;
|
|
arc.b = b;
|
|
arc.life = life;
|
|
arc.segments = numSegments;
|
|
arc.amplitude = amplitude;
|
|
arc.width = width;
|
|
|
|
ElectricArcs.push_back(arc);
|
|
}
|
|
|
|
void SpawnElectricArcGlow(const Vector3& pos, float scale, byte r, byte g, byte b)
|
|
{
|
|
auto& spark = *GetFreeParticle();
|
|
|
|
spark.on = true;
|
|
spark.spriteIndex = Objects[ID_MISC_SPRITES].meshIndex;
|
|
spark.blendMode = BLEND_MODES::BLENDMODE_ADDITIVE;
|
|
spark.x = pos.x;
|
|
spark.y = pos.y;
|
|
spark.z = pos.z;
|
|
spark.xVel = 0;
|
|
spark.yVel = 0;
|
|
spark.zVel = 0;
|
|
spark.sR = r;
|
|
spark.sG = g;
|
|
spark.sB = b;
|
|
spark.dR = r;
|
|
spark.dG = g;
|
|
spark.dB = b;
|
|
spark.life = 4;
|
|
spark.sLife = 4;
|
|
spark.colFadeSpeed = 2;
|
|
spark.fadeToBlack = 0;
|
|
spark.scalar = 3;
|
|
spark.maxYvel = 0;
|
|
spark.gravity = 0;
|
|
spark.sSize =
|
|
spark.dSize =
|
|
spark.size = scale + Random::GenerateInt(0, 4);
|
|
spark.flags = SP_DEF | SP_SCALE;
|
|
}
|
|
|
|
void SpawnHelicalLaser(const Vector3& origin, const Vector3& target)
|
|
{
|
|
static constexpr auto SEGMENTS_NUM_MAX = 128;
|
|
static constexpr auto COLOR = Vector4(0.0f, 0.375f, 1.0f, 1.0f);
|
|
static constexpr auto LENGTH_MAX = BLOCK(4);
|
|
static constexpr auto ROTATION = ANGLE(-10.0f);
|
|
|
|
static constexpr auto ELECTRIC_ARC_FLAGS = LI_THININ | LI_THINOUT;
|
|
|
|
auto laser = HelicalLaser();
|
|
|
|
laser.NumSegments = SEGMENTS_NUM_MAX;
|
|
laser.Origin = origin;
|
|
laser.Target = target;
|
|
laser.Orientation2D = Random::GenerateAngle();
|
|
laser.LightPosition = origin;
|
|
laser.Color = COLOR;
|
|
laser.Life = HELICAL_LASER_LIFE_MAX;
|
|
laser.Radius = 0.0f;
|
|
laser.Length = 0.0f;
|
|
laser.LengthEnd = LENGTH_MAX;
|
|
laser.Opacity = 1.0f;
|
|
laser.Rotation = ROTATION;
|
|
|
|
HelicalLasers.push_back(laser);
|
|
|
|
SpawnElectricArc(origin, target, 1, 0, laser.Color.x * UCHAR_MAX, laser.Color.z * UCHAR_MAX, 20, ELECTRIC_ARC_FLAGS, 19, 5);
|
|
SpawnElectricArc(origin, target, 1, 110, 255, 250, 20, ELECTRIC_ARC_FLAGS, 4, 5);
|
|
SpawnElectricArcGlow(laser.LightPosition, 0, 0, (laser.Color.x / 2) * UCHAR_MAX, (laser.Color.z / 2) * UCHAR_MAX);
|
|
}
|
|
|
|
void UpdateHelicalLasers()
|
|
{
|
|
static constexpr auto LIFE_START_FADING = HELICAL_LASER_LIFE_MAX / 2;
|
|
static constexpr auto LENGTH_LERP_ALPHA = 0.25f;
|
|
|
|
// No active effects; return early.
|
|
if (HelicalLasers.empty())
|
|
return;
|
|
|
|
for (auto& laser : HelicalLasers)
|
|
{
|
|
// Set to despawn.
|
|
laser.Life -= 1.0f;
|
|
if (laser.Life <= 0.0f)
|
|
continue;
|
|
|
|
// Update length.
|
|
laser.Length = Lerp(laser.Length, laser.LengthEnd, LENGTH_LERP_ALPHA);
|
|
|
|
// Update radius.
|
|
laser.Radius += 1 / 8.0f;
|
|
|
|
// Update opacity.
|
|
float alpha = laser.Life / LIFE_START_FADING;
|
|
laser.Opacity = Lerp(0.0f, 1.0f, alpha);
|
|
|
|
// Update orientation.
|
|
laser.Orientation2D += laser.Rotation;
|
|
}
|
|
|
|
// Despawn inactive effects.
|
|
HelicalLasers.erase(
|
|
std::remove_if(
|
|
HelicalLasers.begin(), HelicalLasers.end(),
|
|
[](const HelicalLaser& laser) { return (laser.Life <= 0.0f); }), HelicalLasers.end());
|
|
}
|
|
|
|
void UpdateElectricArcs()
|
|
{
|
|
// No active effects; return early.
|
|
if (ElectricArcs.empty())
|
|
return;
|
|
|
|
for (auto& arc : ElectricArcs)
|
|
{
|
|
// Set to despawn.
|
|
if (arc.life <= 0.0f)
|
|
continue;
|
|
|
|
// If/when this behaviour is changed, modify AddLightningArc accordingly.
|
|
arc.life -= 2.0f;
|
|
if (arc.life > 0.0f)
|
|
{
|
|
// TODO: Find a better way to do this.
|
|
auto* posPtr = (Vector3*)&arc.pos2;
|
|
for (auto& interpPos : arc.interpolation)
|
|
{
|
|
*posPtr += interpPos * 2;
|
|
interpPos -= (interpPos / 16);
|
|
|
|
posPtr++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Despawn inactive effects.
|
|
ElectricArcs.erase(
|
|
std::remove_if(
|
|
ElectricArcs.begin(), ElectricArcs.end(),
|
|
[](const ElectricArc& arc) { return (arc.life <= 0.0f); }), ElectricArcs.end());
|
|
}
|
|
|
|
void CalculateElectricArcSpline(const ElectricArc& arc, const std::array<Vector3, ELECTRIC_ARC_KNOTS_SIZE>& knots, std::array<Vector3, ELECTRIC_ARC_BUFFER_SIZE>& buffer)
|
|
{
|
|
int bufferIndex = 0;
|
|
|
|
buffer[bufferIndex] = knots[0];
|
|
bufferIndex++;
|
|
|
|
// Splined arc.
|
|
if (arc.flags & LI_SPLINE)
|
|
{
|
|
float interpStep = 1.0f / ((arc.segments * 3) - 1);
|
|
float alpha = interpStep;
|
|
|
|
if (((arc.segments * 3) - 2) > 0)
|
|
{
|
|
for (int i = (arc.segments * 3) - 2; i > 0; i--)
|
|
{
|
|
auto spline = ElectricArcSpline(knots, alpha);
|
|
auto sphere = BoundingSphere(Vector3::Zero, 8.0f);
|
|
auto offset = Random::GeneratePointInSphere(sphere);
|
|
|
|
buffer[bufferIndex] = spline + offset;
|
|
|
|
alpha += interpStep;
|
|
bufferIndex++;
|
|
}
|
|
}
|
|
}
|
|
// Straight arc.
|
|
else
|
|
{
|
|
int numSegments = (arc.segments * 3) - 1;
|
|
|
|
auto deltaPos = (knots[knots.size() - 1] - knots[0]) / numSegments;
|
|
auto pos = knots[0] + deltaPos + Vector3(
|
|
fmod(Random::GenerateInt(), arc.amplitude * 2),
|
|
fmod(Random::GenerateInt(), arc.amplitude * 2),
|
|
fmod(Random::GenerateInt(), arc.amplitude * 2)) -
|
|
Vector3(arc.amplitude);
|
|
|
|
if (((arc.segments * 3) - 2) > 0)
|
|
{
|
|
for (int i = (arc.segments * 3) - 2; i > 0; i--)
|
|
{
|
|
buffer[bufferIndex] = pos;
|
|
bufferIndex++;
|
|
|
|
pos += deltaPos + Vector3(
|
|
fmod(Random::GenerateInt(), arc.amplitude * 2),
|
|
fmod(Random::GenerateInt(), arc.amplitude * 2),
|
|
fmod(Random::GenerateInt(), arc.amplitude * 2)) -
|
|
Vector3(arc.amplitude);
|
|
}
|
|
}
|
|
}
|
|
|
|
buffer[bufferIndex] = knots[5];
|
|
}
|
|
|
|
void CalculateHelixSpline(const HelicalLaser& laser, std::array<Vector3, ELECTRIC_ARC_KNOTS_SIZE>& knots, std::array<Vector3, ELECTRIC_ARC_BUFFER_SIZE>& buffer)
|
|
{
|
|
int bufferIndex = 0;
|
|
|
|
buffer[bufferIndex] = knots[0];
|
|
bufferIndex++;
|
|
|
|
auto origin = knots[0];
|
|
auto target = knots[1];
|
|
auto direction = target - origin;
|
|
direction.Normalize();
|
|
|
|
float stepLength = laser.Length / laser.NumSegments;
|
|
float radiusStep = laser.Radius;
|
|
|
|
auto refPoint = Geometry::RotatePoint(Vector3::Right, EulerAngles(direction));
|
|
auto axisAngle = AxisAngle(direction, laser.Orientation2D);
|
|
|
|
for (int i = 0; i < laser.NumSegments; i++)
|
|
{
|
|
axisAngle.SetAngle(axisAngle.GetAngle() + ANGLE(25.0f));
|
|
auto pos = Geometry::RotatePoint(refPoint * (radiusStep * i), axisAngle);
|
|
|
|
buffer[bufferIndex] = origin + Geometry::TranslatePoint(pos, axisAngle.GetAxis(), stepLength * i);
|
|
bufferIndex++;
|
|
}
|
|
|
|
buffer[bufferIndex] = knots[1];
|
|
}
|
|
}
|