Improve streamer effect implementation

This commit is contained in:
Sezz 2025-03-02 14:07:43 +11:00
parent 44128eda43
commit 1e143c6e0a
5 changed files with 253 additions and 163 deletions

View file

@ -5,6 +5,7 @@
#include "Game/effects/effects.h" #include "Game/effects/effects.h"
#include "Game/items.h" #include "Game/items.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Renderer/RendererEnums.h"
#include "Specific/clock.h" #include "Specific/clock.h"
using namespace TEN::Math; using namespace TEN::Math;
@ -27,25 +28,36 @@ namespace TEN::Effects::Streamer
if (Color.w > 0.0f) if (Color.w > 0.0f)
Color.w = EaseInOutSine(0.0f, OpacityMax, Life / LifeMax); Color.w = EaseInOutSine(0.0f, OpacityMax, Life / LifeMax);
// TODO: Not working. // TODO: Not working. Make it work. -- Sezz 2025.03.02
// Update orientation. // Update orientation.
Orientation.SetAngle(Orientation.GetAngle() + Rotation); Orientation.SetAngle(Orientation.GetAngle() + Rotation);
// Update vertices. // Update vertices.
TransformVertices(Velocity, ScaleRate); TransformVertices(Velocity, ExpRate);
// Update life. // Update life.
Life -= 1.0f; Life -= 1.0f;
} }
void Streamer::StreamerSegment::TransformVertices(float vel, float scaleRate) void Streamer::StreamerSegment::TransformVertices(float vel, float expRate)
{ {
// Apply expansion. // Apply expansion/contraction.
if (scaleRate != 0.0f) if (expRate != 0.0f)
{ {
auto dir = Orientation.ToDirection(); float distSqr = Vector3::DistanceSquared(Vertices[0], Vertices[1]);
Vertices[0] = Geometry::TranslatePoint(Vertices[0], -dir, scaleRate); if (expRate < 0.0f && distSqr <= SQUARE(abs(expRate)))
Vertices[1] = Geometry::TranslatePoint(Vertices[1], dir, scaleRate); {
auto center = (Vertices[0] + Vertices[1]) / 2;
Vertices[0] =
Vertices[1] = center;
}
else
{
auto dir = Orientation.ToDirection();
Vertices[0] = Geometry::TranslatePoint(Vertices[0], -dir, expRate);
Vertices[1] = Geometry::TranslatePoint(Vertices[1], dir, expRate);
}
} }
// Apply directional velocity. // Apply directional velocity.
@ -57,8 +69,34 @@ namespace TEN::Effects::Streamer
} }
} }
Streamer::Streamer(StreamerFeatherType featherType, BlendMode blendMode)
{
_featherType = featherType;
_blendMode = blendMode;
}
const std::vector<Streamer::StreamerSegment>& Streamer::GetSegments() const
{
return _segments;
}
StreamerFeatherType Streamer::GetFeatherType() const
{
return _featherType;
}
BlendMode Streamer::GetBlendMode() const
{
return _blendMode;
}
bool Streamer::IsBroken() const
{
return _isBroken;
}
void Streamer::AddSegment(const Vector3& pos, const Vector3& dir, short orient, const Vector4& color, void Streamer::AddSegment(const Vector3& pos, const Vector3& dir, short orient, const Vector4& color,
float width, float life, float vel, float scaleRate, short rot, int flags, unsigned int segmentCount) float width, float life, float vel, float expRate, short rot, unsigned int segmentCount)
{ {
constexpr auto FADE_IN_COEFF = 3.0f; constexpr auto FADE_IN_COEFF = 3.0f;
@ -76,61 +114,66 @@ namespace TEN::Effects::Streamer
segment.LifeMax = lifeMax; segment.LifeMax = lifeMax;
segment.OpacityMax = opacityMax; segment.OpacityMax = opacityMax;
segment.Velocity = vel; segment.Velocity = vel;
segment.ScaleRate = scaleRate; segment.ExpRate = expRate;
segment.Rotation = rot; segment.Rotation = rot;
segment.Flags = flags;
segment.InitializeVertices(pos, width); segment.InitializeVertices(pos, width);
} }
void Streamer::Update() void Streamer::Update()
{ {
if (Segments.empty()) if (_segments.empty())
return; return;
// If streamer was broken, set bool flag to track it. // Set flag to track if streamer was broken.
const auto& newestSegment = Segments.back(); const auto& newestSegment = _segments.back();
if (newestSegment.Life != newestSegment.LifeMax) if (newestSegment.Life != newestSegment.LifeMax)
IsBroken = true; _isBroken = true;
// Update segments. // Update segments.
for (auto& segment : Segments) for (auto& segment : _segments)
segment.Update(); segment.Update();
ClearInactiveEffects(Segments); ClearInactiveEffects(_segments);
} }
Streamer::StreamerSegment& Streamer::GetNewSegment() Streamer::StreamerSegment& Streamer::GetNewSegment()
{ {
TENAssert(Segments.size() <= SEGMENT_COUNT_MAX, "Streamer segment count overflow."); TENAssert(_segments.size() <= SEGMENT_COUNT_MAX, "Streamer segment count overflow.");
// Clear oldest segment if vector is full. // Clear oldest segment if vector is full.
if (Segments.size() == SEGMENT_COUNT_MAX) if (_segments.size() == SEGMENT_COUNT_MAX)
Segments.erase(Segments.begin()); _segments.erase(_segments.begin());
// Add and return new segment. // Add and return new segment.
return Segments.emplace_back(); return _segments.emplace_back();
} }
void StreamerModule::AddStreamer(int tag, const Vector3& pos, const Vector3& dir, short orient, const Vector4& color, const std::unordered_map<int, std::vector<Streamer>>& StreamerGroup::GetPools() const
float width, float life, float vel, float scaleRate, short rot, int flags)
{ {
TENAssert(Pools.size() <= POOL_COUNT_MAX, "Streamer pool count overflow."); return _pools;
}
void StreamerGroup::AddStreamer(int tag, const Vector3& pos, const Vector3& dir, short orient, const Vector4& color,
float width, float life, float vel, float expRate, short rot,
StreamerFeatherType featherType, BlendMode blendMode)
{
TENAssert(_pools.size() <= POOL_COUNT_MAX, "Streamer pool count overflow.");
// Return early if pool map is full and element with tag key doesn't already exist. // Return early if pool map is full and element with tag key doesn't already exist.
if (Pools.size() == POOL_COUNT_MAX && !Pools.count(tag)) if (_pools.size() == POOL_COUNT_MAX && !_pools.count(tag))
return; return;
// Get and extend streamer with new segment. // Get new streamer iteration or extend existing streamer iteration with new segment.
auto& streamer = GetStreamer(tag); auto& streamer = GetStreamerIteration(tag, featherType, blendMode);
streamer.AddSegment(pos, dir, orient, color, width, life, vel, scaleRate, rot, flags, (unsigned int)streamer.Segments.size()); streamer.AddSegment(pos, dir, orient, color, width, life, vel, expRate, rot, (unsigned int)streamer.GetSegments().size());
} }
void StreamerModule::Update() void StreamerGroup::Update()
{ {
if (Pools.empty()) if (_pools.empty())
return; return;
for (auto& [tag, pool] : Pools) for (auto& [tag, pool] : _pools)
{ {
for (auto& streamer : pool) for (auto& streamer : pool)
streamer.Update(); streamer.Update();
@ -141,15 +184,15 @@ namespace TEN::Effects::Streamer
ClearInactivePools(); ClearInactivePools();
} }
std::vector<Streamer>& StreamerModule::GetPool(int tag) std::vector<Streamer>& StreamerGroup::GetPool(int tag)
{ {
// Get pool at tag key. // Get pool at tag key.
Pools.insert({ tag, {} }); _pools.insert({ tag, {} });
auto& pool = Pools.at(tag); auto& pool = _pools.at(tag);
return pool; return pool;
} }
Streamer& StreamerModule::GetStreamer(int tag) Streamer& StreamerGroup::GetStreamerIteration(int tag, StreamerFeatherType featherType, BlendMode blendMode)
{ {
auto& pool = GetPool(tag); auto& pool = GetPool(tag);
TENAssert(pool.size() <= STREAMER_COUNT_MAX, "Streamer pool size overflow."); TENAssert(pool.size() <= STREAMER_COUNT_MAX, "Streamer pool size overflow.");
@ -158,7 +201,7 @@ namespace TEN::Effects::Streamer
if (!pool.empty()) if (!pool.empty())
{ {
auto& streamer = pool.back(); auto& streamer = pool.back();
if (!streamer.IsBroken) if (!streamer.IsBroken())
return streamer; return streamer;
} }
@ -167,17 +210,17 @@ namespace TEN::Effects::Streamer
pool.erase(pool.begin()); pool.erase(pool.begin());
// Add and return new streamer iteration. // Add and return new streamer iteration.
return pool.emplace_back(); return pool.emplace_back(Streamer(featherType, blendMode));
} }
void StreamerModule::ClearInactivePools() void StreamerGroup::ClearInactivePools()
{ {
for (auto it = Pools.begin(); it != Pools.end();) for (auto it = _pools.begin(); it != _pools.end();)
{ {
const auto& pool = it->second; const auto& pool = it->second;
if (pool.empty()) if (pool.empty())
{ {
it = Pools.erase(it); it = _pools.erase(it);
continue; continue;
} }
@ -185,43 +228,49 @@ namespace TEN::Effects::Streamer
} }
} }
void StreamerModule::ClearInactiveStreamers(int tag) void StreamerGroup::ClearInactiveStreamers(int tag)
{ {
auto& pool = Pools.at(tag); auto& pool = _pools.at(tag);
pool.erase( pool.erase(
std::remove_if( std::remove_if(
pool.begin(), pool.end(), pool.begin(), pool.end(),
[](const auto& streamer) [](const auto& streamer)
{ {
return streamer.Segments.empty(); return streamer.GetSegments().empty();
}), }),
pool.end()); pool.end());
} }
void StreamerEffectController::Spawn(int itemNumber, int tag, const Vector3& pos, const Vector3& direction, short orient, const Vector4& color, const std::unordered_map<int, StreamerGroup>& StreamerEffectController::GetGroups() const
float width, float life, float vel, float scaleRate, short rot, int flags)
{ {
TENAssert(Modules.size() <= MODULE_COUNT_MAX, "Streamer module count overflow."); return _groups;
}
// Return early if module map is full and element with itemNumber key doesn't already exist. void StreamerEffectController::Spawn(int itemNumber, int tag, const Vector3& pos, const Vector3& direction, short orient, const Vector4& color,
if (Modules.size() == MODULE_COUNT_MAX && !Modules.count(itemNumber)) float width, float life, float vel, float expRate, short rot,
StreamerFeatherType featherType, BlendMode blendMode)
{
TENAssert(_groups.size() <= GROUP_COUNT_MAX, "Streamer group count overflow.");
// Return early if group map is full and element with itemNumber key doesn't already exist.
if (_groups.size() == GROUP_COUNT_MAX && !_groups.count(itemNumber))
return; return;
// Get module and extend streamer within pool. // Get group and extend streamer within pool.
auto& module = GetModule(itemNumber); auto& group = GetGroup(itemNumber);
module.AddStreamer(tag, pos, direction, orient, color, width, life, vel, scaleRate, rot, flags); group.AddStreamer(tag, pos, direction, orient, color, width, life, vel, expRate, rot, featherType, blendMode);
} }
void StreamerEffectController::Update() void StreamerEffectController::Update()
{ {
if (Modules.empty()) if (_groups.empty())
return; return;
for (auto& [itemNumber, module] : Modules) for (auto& [itemNumber, group] : _groups)
module.Update(); group.Update();
ClearInactiveModules(); ClearInactiveGroups();
} }
void StreamerEffectController::Clear() void StreamerEffectController::Clear()
@ -229,22 +278,22 @@ namespace TEN::Effects::Streamer
*this = {}; *this = {};
} }
StreamerModule& StreamerEffectController::GetModule(int itemNumber) StreamerGroup& StreamerEffectController::GetGroup(int itemNumber)
{ {
// Get module at itemNumber key. // Get group at itemNumber key.
Modules.insert({ itemNumber, {} }); _groups.insert({ itemNumber, {} });
auto& module = Modules.at(itemNumber); auto& group = _groups.at(itemNumber);
return module; return group;
} }
void StreamerEffectController::ClearInactiveModules() void StreamerEffectController::ClearInactiveGroups()
{ {
for (auto it = Modules.begin(); it != Modules.end();) for (auto it = _groups.begin(); it != _groups.end();)
{ {
const auto& module = it->second; const auto& group = it->second;
if (module.Pools.empty()) if (group.GetPools().empty())
{ {
it = Modules.erase(it); it = _groups.erase(it);
continue; continue;
} }

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "Math/Math.h" #include "Math/Math.h"
#include "Renderer/RendererEnums.h"
using namespace TEN::Math; using namespace TEN::Math;
@ -7,11 +9,12 @@ struct ItemInfo;
namespace TEN::Effects::Streamer namespace TEN::Effects::Streamer
{ {
enum class StreamerFlags enum class StreamerFeatherType
{ {
FadeLeft = 1 << 0, None,
FadeRight = 1 << 1, Center,
BlendModeAdditive = 1 << 2 Left,
Right
}; };
class Streamer class Streamer
@ -21,32 +24,29 @@ namespace TEN::Effects::Streamer
static constexpr auto SEGMENT_COUNT_MAX = 128; static constexpr auto SEGMENT_COUNT_MAX = 128;
public:
struct StreamerSegment struct StreamerSegment
{ {
static constexpr auto VERTEX_COUNT = 2; static constexpr auto VERTEX_COUNT = 2;
AxisAngle Orientation = AxisAngle::Identity; std::array<Vector3, VERTEX_COUNT> Vertices = {};
Vector4 Color = Vector4::Zero; AxisAngle Orientation = AxisAngle::Identity; // TODO: Interpolate?
Vector4 Color = Vector4::Zero;
float Life = 0.0f; float Life = 0.0f;
float LifeMax = 0.0f; float LifeMax = 0.0f;
float OpacityMax = 0.0f; float OpacityMax = 0.0f;
float Velocity = 0.0f; float Velocity = 0.0f;
float ScaleRate = 0.0f; float ExpRate = 0.0f;
short Rotation = 0; short Rotation = 0;
int Flags = 0;
std::array<Vector3, VERTEX_COUNT> Vertices = {}; Vector4 PrevColor = Vector4::Zero;
Vector4 PrevColor = Vector4::Zero;
std::array<Vector3, VERTEX_COUNT> PrevVertices = {}; std::array<Vector3, VERTEX_COUNT> PrevVertices = {};
void InitializeVertices(const Vector3& pos, float width); void InitializeVertices(const Vector3& pos, float width);
void Update(); void Update();
private: private:
void TransformVertices(float vel, float scaleRate); void TransformVertices(float vel, float expRate);
void StoreInterpolationData() void StoreInterpolationData()
{ {
@ -58,13 +58,32 @@ namespace TEN::Effects::Streamer
// Fields // Fields
bool IsBroken = false; std::vector<StreamerSegment> _segments = {};
std::vector<StreamerSegment> Segments = {};
StreamerFeatherType _featherType = StreamerFeatherType::None;
BlendMode _blendMode = BlendMode::AlphaBlend;
bool _isBroken = false;
public:
// Constructors
Streamer(StreamerFeatherType featherType, BlendMode blendMode);
// Getters
const std::vector<StreamerSegment>& GetSegments() const;
StreamerFeatherType GetFeatherType() const;
BlendMode GetBlendMode() const;
// Inquirers
bool IsBroken() const;
// Utilities // Utilities
void AddSegment(const Vector3& pos, const Vector3& dir, short orient, const Vector4& color, void AddSegment(const Vector3& pos, const Vector3& dir, short orient, const Vector4& color,
float width, float life, float vel, float scaleRate, short rot, int flags, unsigned int segmentCount); float width, float life, float vel, float expRate, short rot, unsigned int segmentCount);
void Update(); void Update();
private: private:
@ -73,30 +92,36 @@ namespace TEN::Effects::Streamer
StreamerSegment& GetNewSegment(); StreamerSegment& GetNewSegment();
}; };
class StreamerModule class StreamerGroup
{ {
private: private:
// Constants // Constants
static constexpr auto POOL_COUNT_MAX = 8; static constexpr auto POOL_COUNT_MAX = 64;
static constexpr auto STREAMER_COUNT_MAX = 8; static constexpr auto STREAMER_COUNT_MAX = 4;
// Fields
std::unordered_map<int, std::vector<Streamer>> _pools = {}; // Key = tag.
public: public:
// Members
std::unordered_map<int, std::vector<Streamer>> Pools = {}; // Key = tag. // Getters
const std::unordered_map<int, std::vector<Streamer>>& GetPools() const;
// Utilities // Utilities
void AddStreamer(int tag, const Vector3& pos, const Vector3& dir, short orient, const Vector4& color, void AddStreamer(int tag, const Vector3& pos, const Vector3& dir, short orient, const Vector4& color,
float width, float life, float vel, float scaleRate, short rot, int flags); float width, float life, float vel, float expRate, short rot,
StreamerFeatherType featherType, BlendMode blendMode);
void Update(); void Update();
private: private:
// Helpers // Helpers
std::vector<Streamer>& GetPool(int tag); std::vector<Streamer>& GetPool(int tag);
Streamer& GetStreamer(int tag); Streamer& GetStreamerIteration(int tag, StreamerFeatherType featherType, BlendMode blendMode);
void ClearInactivePools(); void ClearInactivePools();
void ClearInactiveStreamers(int tag); void ClearInactiveStreamers(int tag);
}; };
@ -106,25 +131,30 @@ namespace TEN::Effects::Streamer
private: private:
// Constants // Constants
static constexpr auto MODULE_COUNT_MAX = 64; static constexpr auto GROUP_COUNT_MAX = 64;
public:
// Fields // Fields
std::unordered_map<int, StreamerModule> Modules = {}; // Key = item number. std::unordered_map<int, StreamerGroup> _groups = {}; // Key = item number.
public:
// Getters
const std::unordered_map<int, StreamerGroup>& GetGroups() const;
// Utilities // Utilities
void Spawn(int itemNumber, int tag, const Vector3& pos, const Vector3& dir, short orient, const Vector4& color, void Spawn(int itemNumber, int tag, const Vector3& pos, const Vector3& dir, short orient, const Vector4& color,
float width, float life, float vel, float scaleRate, short rot, int flags = 0); float width, float life, float vel, float expRate, short rot,
StreamerFeatherType featherType = StreamerFeatherType::None, BlendMode blendMode = BlendMode::AlphaBlend);
void Update(); void Update();
void Clear(); void Clear();
private: private:
// Helpers // Helpers
StreamerModule& GetModule(int itemNumber); StreamerGroup& GetGroup(int itemNumber);
void ClearInactiveModules(); void ClearInactiveGroups();
}; };
extern StreamerEffectController StreamerEffect; extern StreamerEffectController StreamerEffect;

View file

@ -38,12 +38,11 @@ namespace TEN::Entities::TR4
static void SpawnWraithTails(const ItemInfo& item) static void SpawnWraithTails(const ItemInfo& item)
{ {
constexpr auto OFFSET = Vector3(0.0f, -10.0f, -50.0f); constexpr auto OFFSET = Vector3(0.0f, -10.0f, -50.0f);
constexpr auto WIDTH = 8.0f; constexpr auto WIDTH = 8.0f;
constexpr auto LIFE_MAX = 0.5f; constexpr auto LIFE_MAX = 0.5f;
constexpr auto VEL = 4.0f; constexpr auto VEL = 4.0f;
constexpr auto SCALE_RATE = 1.0f; constexpr auto EXP_RATE = 1.0f;
constexpr auto FLAGS = (int)StreamerFlags::FadeRight;
enum class TailTag enum class TailTag
{ {
@ -83,19 +82,22 @@ namespace TEN::Entities::TR4
StreamerEffect.Spawn( StreamerEffect.Spawn(
item.Index, (int)TailTag::First, item.Index, (int)TailTag::First,
pos, direction0, orient2D, color, pos, direction0, orient2D, color,
WIDTH, LIFE_MAX, VEL, SCALE_RATE, 0, FLAGS); WIDTH, LIFE_MAX, VEL, EXP_RATE, 0,
StreamerFeatherType::Center, BlendMode::Additive);
// Spawn second tail. // Spawn second tail.
StreamerEffect.Spawn( StreamerEffect.Spawn(
item.Index, (int)TailTag::Second, item.Index, (int)TailTag::Second,
pos, direction1, orient2D, color, pos, direction1, orient2D, color,
WIDTH, LIFE_MAX, VEL, SCALE_RATE, 0, FLAGS); WIDTH, LIFE_MAX, VEL, EXP_RATE, 0,
StreamerFeatherType::Center, BlendMode::Additive);
// Spawn third tail. // Spawn third tail.
StreamerEffect.Spawn( StreamerEffect.Spawn(
item.Index, (int)TailTag::Third, item.Index, (int)TailTag::Third,
pos, direction2, orient2D, color, pos, direction2, orient2D, color,
WIDTH, LIFE_MAX, VEL, SCALE_RATE, 0, FLAGS); WIDTH, LIFE_MAX, VEL, EXP_RATE, 0,
StreamerFeatherType::Center, BlendMode::Additive);
} }
static void WraithWallEffect(Vector3i pos, short yRot, int objectNumber) static void WraithWallEffect(Vector3i pos, short yRot, int objectNumber)

View file

@ -355,11 +355,11 @@ namespace TEN::Entities::Vehicles
void SpawnVehicleWake(const ItemInfo& vehicleItem, const Vector3& relOffset, int waterHeight, bool isUnderwater) void SpawnVehicleWake(const ItemInfo& vehicleItem, const Vector3& relOffset, int waterHeight, bool isUnderwater)
{ {
constexpr auto COLOR = Vector4(0.75f); constexpr auto COLOR = Vector4(0.75f);
constexpr auto LIFE_MAX = 2.5f; constexpr auto LIFE_MAX = 2.5f;
constexpr auto VEL_ABS = 4.0f; constexpr auto VEL_ABS = 4.0f;
constexpr auto SCALE_RATE_ON_WATER = 6.0f; constexpr auto EXP_RATE_ON_WATER = 6.0f;
constexpr auto SCALE_RATE_UNDERWATER = 1.5f; constexpr auto EXP_RATE_UNDERWATER = 1.5f;
// Vehicle is out of water; return early. // Vehicle is out of water; return early.
if (waterHeight == NO_HEIGHT) if (waterHeight == NO_HEIGHT)
@ -373,23 +373,25 @@ namespace TEN::Entities::Vehicles
// Determine key parameters. // Determine key parameters.
auto positions = GetVehicleWakePositions(vehicleItem, relOffset, waterHeight, isUnderwater, isMovingForward); auto positions = GetVehicleWakePositions(vehicleItem, relOffset, waterHeight, isUnderwater, isMovingForward);
auto direction = -vehicleItem.Pose.Orientation.ToDirection(); auto dir = -vehicleItem.Pose.Orientation.ToDirection();
short orient2D = isUnderwater ? vehicleItem.Pose.Orientation.z : 0; short orient2D = isUnderwater ? vehicleItem.Pose.Orientation.z : 0;
float life = isUnderwater ? (LIFE_MAX / 2) : LIFE_MAX; float life = isUnderwater ? (LIFE_MAX / 2) : LIFE_MAX;
float vel = isMovingForward ? VEL_ABS : -VEL_ABS; float vel = isMovingForward ? VEL_ABS : -VEL_ABS;
float scaleRate = isUnderwater ? SCALE_RATE_UNDERWATER : SCALE_RATE_ON_WATER; float expRate = isUnderwater ? EXP_RATE_UNDERWATER : EXP_RATE_ON_WATER;
// Spawn left wake. // Spawn left wake.
StreamerEffect.Spawn( StreamerEffect.Spawn(
vehicleItem.Index, (int)tagLeft, vehicleItem.Index, (int)tagLeft,
positions.first, direction, orient2D, COLOR, positions.first, dir, orient2D, COLOR,
0.0f, life, vel, scaleRate, 0, (int)StreamerFlags::FadeLeft); 0.0f, life, vel, expRate, 0,
StreamerFeatherType::Right, BlendMode::Additive);
// Spawn right wake. // Spawn right wake.
StreamerEffect.Spawn( StreamerEffect.Spawn(
vehicleItem.Index, (int)tagRight, vehicleItem.Index, (int)tagRight,
positions.second, direction, orient2D, COLOR, positions.second, dir, orient2D, COLOR,
0.0f, life, vel, scaleRate, 0, (int)StreamerFlags::FadeRight); 0.0f, life, vel, expRate, 0,
StreamerFeatherType::Left, BlendMode::Additive);
} }
void HandleVehicleSpeedometer(float vel, float velMax) void HandleVehicleSpeedometer(float vel, float velMax)

View file

@ -112,65 +112,72 @@ namespace TEN::Renderer
void Renderer::PrepareStreamers(RenderView& view) void Renderer::PrepareStreamers(RenderView& view)
{ {
constexpr auto DEFAULT_BLEND_MODE = BlendMode::Additive; for (const auto& [itemNumber, group] : StreamerEffect.GetGroups())
for (const auto& [itemNumber, module] : StreamerEffect.Modules)
{ {
for (const auto& [tag, pool] : module.Pools) for (const auto& [tag, pool] : group.GetPools())
{ {
for (const auto& streamer : pool) for (const auto& streamer : pool)
{ {
for (int i = 0; i < streamer.Segments.size(); i++) for (int i = 0; i < streamer.GetSegments().size(); i++)
{ {
const auto& segment = streamer.Segments[i]; const auto& segment = streamer.GetSegments()[i];
const auto& prevSegment = streamer.Segments[std::max(i - 1, 0)]; const auto& prevSegment = streamer.GetSegments()[std::max(i - 1, 0)];
if (segment.Life <= 0.0f) if (segment.Life <= 0.0f)
continue; continue;
// Determine blend mode.
auto blendMode = DEFAULT_BLEND_MODE;
if (segment.Flags & (int)StreamerFlags::BlendModeAdditive)
blendMode = BlendMode::AlphaBlend;
if (segment.Flags & (int)StreamerFlags::FadeLeft) auto vertex0 = Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], GetInterpolationFactor());
auto vertex1 = Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], GetInterpolationFactor());
auto prevVertex0 = Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], GetInterpolationFactor());
auto prevVertex1 = Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], GetInterpolationFactor());
auto color = Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor());
auto prevColor = Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor());
switch (streamer.GetFeatherType())
{ {
AddColoredQuad( default:
Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], GetInterpolationFactor()), case StreamerFeatherType::None:
Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], GetInterpolationFactor()), AddColoredQuad(
Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], GetInterpolationFactor()), vertex0, vertex1,
Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], GetInterpolationFactor()), prevVertex1,
Vector4::Zero, prevVertex0,
Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor()), color,
Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor()), color,
Vector4::Zero, prevColor,
blendMode, view); prevColor,
} streamer.GetBlendMode(), view);
else if (segment.Flags & (int)StreamerFlags::FadeRight) break;
{
AddColoredQuad( case StreamerFeatherType::Center:
Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], GetInterpolationFactor()), {
Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], GetInterpolationFactor()), auto center = (vertex0 + vertex1) / 2;
Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], GetInterpolationFactor()), auto prevCenter = (prevVertex0 + prevVertex1) / 2;
Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], GetInterpolationFactor()),
Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor()), AddColoredQuad(
Vector4::Zero, center, vertex1, prevVertex1, prevCenter,
Vector4::Zero, color, Vector4::Zero, Vector4::Zero, prevColor,
Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor()), streamer.GetBlendMode(), view);
blendMode, view); AddColoredQuad(
} vertex0, center, prevCenter, prevVertex0,
else Vector4::Zero, color, prevColor, Vector4::Zero,
{ streamer.GetBlendMode(), view);
AddColoredQuad( }
Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], GetInterpolationFactor()), break;
Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], GetInterpolationFactor()),
Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], GetInterpolationFactor()), case StreamerFeatherType::Left:
Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], GetInterpolationFactor()), AddColoredQuad(
Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor()), vertex0, vertex1, prevVertex1, prevVertex0,
Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor()), color, Vector4::Zero, Vector4::Zero, prevColor,
Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor()), streamer.GetBlendMode(), view);
Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor()), break;
blendMode, view);
case StreamerFeatherType::Right:
AddColoredQuad(
vertex0, vertex1, prevVertex1, prevVertex0,
Vector4::Zero, color, prevColor, Vector4::Zero,
streamer.GetBlendMode(), view);
break;
} }
} }
} }