diff --git a/TombEngine/Game/effects/Streamer.cpp b/TombEngine/Game/effects/Streamer.cpp index a7266498f..0ab1f752e 100644 --- a/TombEngine/Game/effects/Streamer.cpp +++ b/TombEngine/Game/effects/Streamer.cpp @@ -5,6 +5,7 @@ #include "Game/effects/effects.h" #include "Game/items.h" #include "Math/Math.h" +#include "Renderer/RendererEnums.h" #include "Specific/clock.h" using namespace TEN::Math; @@ -27,25 +28,36 @@ namespace TEN::Effects::Streamer if (Color.w > 0.0f) Color.w = EaseInOutSine(0.0f, OpacityMax, Life / LifeMax); - // TODO: Not working. + // TODO: Not working. Make it work. -- Sezz 2025.03.02 // Update orientation. Orientation.SetAngle(Orientation.GetAngle() + Rotation); // Update vertices. - TransformVertices(Velocity, ScaleRate); + TransformVertices(Velocity, ExpRate); // Update life. Life -= 1.0f; } - void Streamer::StreamerSegment::TransformVertices(float vel, float scaleRate) + void Streamer::StreamerSegment::TransformVertices(float vel, float expRate) { - // Apply expansion. - if (scaleRate != 0.0f) + // Apply expansion/contraction. + if (expRate != 0.0f) { - auto dir = Orientation.ToDirection(); - Vertices[0] = Geometry::TranslatePoint(Vertices[0], -dir, scaleRate); - Vertices[1] = Geometry::TranslatePoint(Vertices[1], dir, scaleRate); + float distSqr = Vector3::DistanceSquared(Vertices[0], Vertices[1]); + if (expRate < 0.0f && distSqr <= SQUARE(abs(expRate))) + { + 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. @@ -57,8 +69,34 @@ namespace TEN::Effects::Streamer } } + Streamer::Streamer(StreamerFeatherType featherType, BlendMode blendMode) + { + _featherType = featherType; + _blendMode = blendMode; + } + + const std::vector& 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, - 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; @@ -76,61 +114,66 @@ namespace TEN::Effects::Streamer segment.LifeMax = lifeMax; segment.OpacityMax = opacityMax; segment.Velocity = vel; - segment.ScaleRate = scaleRate; + segment.ExpRate = expRate; segment.Rotation = rot; - segment.Flags = flags; segment.InitializeVertices(pos, width); } void Streamer::Update() { - if (Segments.empty()) + if (_segments.empty()) return; - // If streamer was broken, set bool flag to track it. - const auto& newestSegment = Segments.back(); + // Set flag to track if streamer was broken. + const auto& newestSegment = _segments.back(); if (newestSegment.Life != newestSegment.LifeMax) - IsBroken = true; + _isBroken = true; // Update segments. - for (auto& segment : Segments) + for (auto& segment : _segments) segment.Update(); - ClearInactiveEffects(Segments); + ClearInactiveEffects(_segments); } 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. - if (Segments.size() == SEGMENT_COUNT_MAX) - Segments.erase(Segments.begin()); + if (_segments.size() == SEGMENT_COUNT_MAX) + _segments.erase(_segments.begin()); // 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, - float width, float life, float vel, float scaleRate, short rot, int flags) + const std::unordered_map>& StreamerGroup::GetPools() const { - 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. - if (Pools.size() == POOL_COUNT_MAX && !Pools.count(tag)) + if (_pools.size() == POOL_COUNT_MAX && !_pools.count(tag)) return; - // Get and extend streamer with new segment. - auto& streamer = GetStreamer(tag); - streamer.AddSegment(pos, dir, orient, color, width, life, vel, scaleRate, rot, flags, (unsigned int)streamer.Segments.size()); + // Get new streamer iteration or extend existing streamer iteration with new segment. + auto& streamer = GetStreamerIteration(tag, featherType, blendMode); + 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; - for (auto& [tag, pool] : Pools) + for (auto& [tag, pool] : _pools) { for (auto& streamer : pool) streamer.Update(); @@ -141,15 +184,15 @@ namespace TEN::Effects::Streamer ClearInactivePools(); } - std::vector& StreamerModule::GetPool(int tag) + std::vector& StreamerGroup::GetPool(int tag) { // Get pool at tag key. - Pools.insert({ tag, {} }); - auto& pool = Pools.at(tag); + _pools.insert({ tag, {} }); + auto& pool = _pools.at(tag); return pool; } - Streamer& StreamerModule::GetStreamer(int tag) + Streamer& StreamerGroup::GetStreamerIteration(int tag, StreamerFeatherType featherType, BlendMode blendMode) { auto& pool = GetPool(tag); TENAssert(pool.size() <= STREAMER_COUNT_MAX, "Streamer pool size overflow."); @@ -158,7 +201,7 @@ namespace TEN::Effects::Streamer if (!pool.empty()) { auto& streamer = pool.back(); - if (!streamer.IsBroken) + if (!streamer.IsBroken()) return streamer; } @@ -167,17 +210,17 @@ namespace TEN::Effects::Streamer pool.erase(pool.begin()); // 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; if (pool.empty()) { - it = Pools.erase(it); + it = _pools.erase(it); 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( std::remove_if( pool.begin(), pool.end(), [](const auto& streamer) { - return streamer.Segments.empty(); + return streamer.GetSegments().empty(); }), pool.end()); } - void StreamerEffectController::Spawn(int itemNumber, int tag, const Vector3& pos, const Vector3& direction, short orient, const Vector4& color, - float width, float life, float vel, float scaleRate, short rot, int flags) + const std::unordered_map& StreamerEffectController::GetGroups() const { - 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. - if (Modules.size() == MODULE_COUNT_MAX && !Modules.count(itemNumber)) + void StreamerEffectController::Spawn(int itemNumber, int tag, const Vector3& pos, const Vector3& direction, short orient, const Vector4& color, + 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; - // Get module and extend streamer within pool. - auto& module = GetModule(itemNumber); - module.AddStreamer(tag, pos, direction, orient, color, width, life, vel, scaleRate, rot, flags); + // Get group and extend streamer within pool. + auto& group = GetGroup(itemNumber); + group.AddStreamer(tag, pos, direction, orient, color, width, life, vel, expRate, rot, featherType, blendMode); } void StreamerEffectController::Update() { - if (Modules.empty()) + if (_groups.empty()) return; - for (auto& [itemNumber, module] : Modules) - module.Update(); + for (auto& [itemNumber, group] : _groups) + group.Update(); - ClearInactiveModules(); + ClearInactiveGroups(); } void StreamerEffectController::Clear() @@ -229,22 +278,22 @@ namespace TEN::Effects::Streamer *this = {}; } - StreamerModule& StreamerEffectController::GetModule(int itemNumber) + StreamerGroup& StreamerEffectController::GetGroup(int itemNumber) { - // Get module at itemNumber key. - Modules.insert({ itemNumber, {} }); - auto& module = Modules.at(itemNumber); - return module; + // Get group at itemNumber key. + _groups.insert({ itemNumber, {} }); + auto& group = _groups.at(itemNumber); + 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; - if (module.Pools.empty()) + const auto& group = it->second; + if (group.GetPools().empty()) { - it = Modules.erase(it); + it = _groups.erase(it); continue; } diff --git a/TombEngine/Game/effects/Streamer.h b/TombEngine/Game/effects/Streamer.h index f08a7b9d6..fdfc3acfb 100644 --- a/TombEngine/Game/effects/Streamer.h +++ b/TombEngine/Game/effects/Streamer.h @@ -1,5 +1,7 @@ #pragma once + #include "Math/Math.h" +#include "Renderer/RendererEnums.h" using namespace TEN::Math; @@ -7,11 +9,12 @@ struct ItemInfo; namespace TEN::Effects::Streamer { - enum class StreamerFlags + enum class StreamerFeatherType { - FadeLeft = 1 << 0, - FadeRight = 1 << 1, - BlendModeAdditive = 1 << 2 + None, + Center, + Left, + Right }; class Streamer @@ -21,32 +24,29 @@ namespace TEN::Effects::Streamer static constexpr auto SEGMENT_COUNT_MAX = 128; - public: struct StreamerSegment { static constexpr auto VERTEX_COUNT = 2; - AxisAngle Orientation = AxisAngle::Identity; - Vector4 Color = Vector4::Zero; + std::array Vertices = {}; + AxisAngle Orientation = AxisAngle::Identity; // TODO: Interpolate? + Vector4 Color = Vector4::Zero; float Life = 0.0f; float LifeMax = 0.0f; float OpacityMax = 0.0f; float Velocity = 0.0f; - float ScaleRate = 0.0f; + float ExpRate = 0.0f; short Rotation = 0; - int Flags = 0; - std::array Vertices = {}; - - Vector4 PrevColor = Vector4::Zero; + Vector4 PrevColor = Vector4::Zero; std::array PrevVertices = {}; void InitializeVertices(const Vector3& pos, float width); void Update(); private: - void TransformVertices(float vel, float scaleRate); + void TransformVertices(float vel, float expRate); void StoreInterpolationData() { @@ -58,13 +58,32 @@ namespace TEN::Effects::Streamer // Fields - bool IsBroken = false; - std::vector Segments = {}; + std::vector _segments = {}; + + StreamerFeatherType _featherType = StreamerFeatherType::None; + BlendMode _blendMode = BlendMode::AlphaBlend; + bool _isBroken = false; + + + public: + // Constructors + + Streamer(StreamerFeatherType featherType, BlendMode blendMode); + + // Getters + + const std::vector& GetSegments() const; + StreamerFeatherType GetFeatherType() const; + BlendMode GetBlendMode() const; + + // Inquirers + + bool IsBroken() const; // Utilities 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(); private: @@ -73,30 +92,36 @@ namespace TEN::Effects::Streamer StreamerSegment& GetNewSegment(); }; - class StreamerModule + class StreamerGroup { private: // Constants - static constexpr auto POOL_COUNT_MAX = 8; - static constexpr auto STREAMER_COUNT_MAX = 8; + static constexpr auto POOL_COUNT_MAX = 64; + static constexpr auto STREAMER_COUNT_MAX = 4; + + // Fields + + std::unordered_map> _pools = {}; // Key = tag. public: - // Members - std::unordered_map> Pools = {}; // Key = tag. + // Getters + + const std::unordered_map>& GetPools() const; // Utilities 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(); private: // Helpers std::vector& GetPool(int tag); - Streamer& GetStreamer(int tag); + Streamer& GetStreamerIteration(int tag, StreamerFeatherType featherType, BlendMode blendMode); void ClearInactivePools(); void ClearInactiveStreamers(int tag); }; @@ -106,25 +131,30 @@ namespace TEN::Effects::Streamer private: // Constants - static constexpr auto MODULE_COUNT_MAX = 64; + static constexpr auto GROUP_COUNT_MAX = 64; - public: // Fields - std::unordered_map Modules = {}; // Key = item number. + std::unordered_map _groups = {}; // Key = item number. + + public: + // Getters + + const std::unordered_map& GetGroups() const; // Utilities 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 Clear(); private: // Helpers - StreamerModule& GetModule(int itemNumber); - void ClearInactiveModules(); + StreamerGroup& GetGroup(int itemNumber); + void ClearInactiveGroups(); }; extern StreamerEffectController StreamerEffect; diff --git a/TombEngine/Objects/TR4/Entity/Wraith.cpp b/TombEngine/Objects/TR4/Entity/Wraith.cpp index b843f6751..73996002a 100644 --- a/TombEngine/Objects/TR4/Entity/Wraith.cpp +++ b/TombEngine/Objects/TR4/Entity/Wraith.cpp @@ -38,12 +38,11 @@ namespace TEN::Entities::TR4 static void SpawnWraithTails(const ItemInfo& item) { - constexpr auto OFFSET = Vector3(0.0f, -10.0f, -50.0f); - constexpr auto WIDTH = 8.0f; - constexpr auto LIFE_MAX = 0.5f; - constexpr auto VEL = 4.0f; - constexpr auto SCALE_RATE = 1.0f; - constexpr auto FLAGS = (int)StreamerFlags::FadeRight; + constexpr auto OFFSET = Vector3(0.0f, -10.0f, -50.0f); + constexpr auto WIDTH = 8.0f; + constexpr auto LIFE_MAX = 0.5f; + constexpr auto VEL = 4.0f; + constexpr auto EXP_RATE = 1.0f; enum class TailTag { @@ -83,19 +82,22 @@ namespace TEN::Entities::TR4 StreamerEffect.Spawn( item.Index, (int)TailTag::First, 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. StreamerEffect.Spawn( item.Index, (int)TailTag::Second, 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. StreamerEffect.Spawn( item.Index, (int)TailTag::Third, 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) diff --git a/TombEngine/Objects/Utils/VehicleHelpers.cpp b/TombEngine/Objects/Utils/VehicleHelpers.cpp index dea5824ab..d225862e1 100644 --- a/TombEngine/Objects/Utils/VehicleHelpers.cpp +++ b/TombEngine/Objects/Utils/VehicleHelpers.cpp @@ -355,11 +355,11 @@ namespace TEN::Entities::Vehicles void SpawnVehicleWake(const ItemInfo& vehicleItem, const Vector3& relOffset, int waterHeight, bool isUnderwater) { - constexpr auto COLOR = Vector4(0.75f); - constexpr auto LIFE_MAX = 2.5f; - constexpr auto VEL_ABS = 4.0f; - constexpr auto SCALE_RATE_ON_WATER = 6.0f; - constexpr auto SCALE_RATE_UNDERWATER = 1.5f; + constexpr auto COLOR = Vector4(0.75f); + constexpr auto LIFE_MAX = 2.5f; + constexpr auto VEL_ABS = 4.0f; + constexpr auto EXP_RATE_ON_WATER = 6.0f; + constexpr auto EXP_RATE_UNDERWATER = 1.5f; // Vehicle is out of water; return early. if (waterHeight == NO_HEIGHT) @@ -373,23 +373,25 @@ namespace TEN::Entities::Vehicles // Determine key parameters. 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; float life = isUnderwater ? (LIFE_MAX / 2) : LIFE_MAX; 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. StreamerEffect.Spawn( vehicleItem.Index, (int)tagLeft, - positions.first, direction, orient2D, COLOR, - 0.0f, life, vel, scaleRate, 0, (int)StreamerFlags::FadeLeft); + positions.first, dir, orient2D, COLOR, + 0.0f, life, vel, expRate, 0, + StreamerFeatherType::Right, BlendMode::Additive); // Spawn right wake. StreamerEffect.Spawn( vehicleItem.Index, (int)tagRight, - positions.second, direction, orient2D, COLOR, - 0.0f, life, vel, scaleRate, 0, (int)StreamerFlags::FadeRight); + positions.second, dir, orient2D, COLOR, + 0.0f, life, vel, expRate, 0, + StreamerFeatherType::Left, BlendMode::Additive); } void HandleVehicleSpeedometer(float vel, float velMax) diff --git a/TombEngine/Renderer/RendererDrawEffect.cpp b/TombEngine/Renderer/RendererDrawEffect.cpp index 988e0b186..ebabe1a2d 100644 --- a/TombEngine/Renderer/RendererDrawEffect.cpp +++ b/TombEngine/Renderer/RendererDrawEffect.cpp @@ -112,65 +112,72 @@ namespace TEN::Renderer void Renderer::PrepareStreamers(RenderView& view) { - constexpr auto DEFAULT_BLEND_MODE = BlendMode::Additive; - - for (const auto& [itemNumber, module] : StreamerEffect.Modules) + for (const auto& [itemNumber, group] : StreamerEffect.GetGroups()) { - for (const auto& [tag, pool] : module.Pools) + for (const auto& [tag, pool] : group.GetPools()) { 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& prevSegment = streamer.Segments[std::max(i - 1, 0)]; + const auto& segment = streamer.GetSegments()[i]; + const auto& prevSegment = streamer.GetSegments()[std::max(i - 1, 0)]; if (segment.Life <= 0.0f) 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( - Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], GetInterpolationFactor()), - Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], GetInterpolationFactor()), - Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], GetInterpolationFactor()), - Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], GetInterpolationFactor()), - Vector4::Zero, - Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor()), - Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor()), - Vector4::Zero, - blendMode, view); - } - else if (segment.Flags & (int)StreamerFlags::FadeRight) - { - AddColoredQuad( - Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], GetInterpolationFactor()), - Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], GetInterpolationFactor()), - Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], GetInterpolationFactor()), - Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], GetInterpolationFactor()), - Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor()), - Vector4::Zero, - Vector4::Zero, - Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor()), - blendMode, view); - } - else - { - AddColoredQuad( - Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], GetInterpolationFactor()), - Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], GetInterpolationFactor()), - Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], GetInterpolationFactor()), - Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], GetInterpolationFactor()), - Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor()), - Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor()), - Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor()), - Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor()), - blendMode, view); + default: + case StreamerFeatherType::None: + AddColoredQuad( + vertex0, vertex1, + prevVertex1, + prevVertex0, + color, + color, + prevColor, + prevColor, + streamer.GetBlendMode(), view); + break; + + case StreamerFeatherType::Center: + { + auto center = (vertex0 + vertex1) / 2; + auto prevCenter = (prevVertex0 + prevVertex1) / 2; + + AddColoredQuad( + center, vertex1, prevVertex1, prevCenter, + color, Vector4::Zero, Vector4::Zero, prevColor, + streamer.GetBlendMode(), view); + AddColoredQuad( + vertex0, center, prevCenter, prevVertex0, + Vector4::Zero, color, prevColor, Vector4::Zero, + streamer.GetBlendMode(), view); + } + break; + + case StreamerFeatherType::Left: + AddColoredQuad( + vertex0, vertex1, prevVertex1, prevVertex0, + color, Vector4::Zero, Vector4::Zero, prevColor, + streamer.GetBlendMode(), view); + break; + + case StreamerFeatherType::Right: + AddColoredQuad( + vertex0, vertex1, prevVertex1, prevVertex0, + Vector4::Zero, color, prevColor, Vector4::Zero, + streamer.GetBlendMode(), view); + break; } } }