From dcd86cb873da933741c6fe145efc9e92f7f91622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 3 Sep 2023 20:34:40 +0200 Subject: [PATCH] Use golden angle to generate unique, distinct DDTeam colors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DDTeam colors were previously generated in HSL by taking the team index and multiplying it by 360/64° to calculate the hue, which results in team colors being evenly distributed over the entire color range like a rainbow. However, this causes colors of adjacent teams to be very similar and therefore hard to distinguish. Now, the hue is calculated by multiplying the team index with the golden angle (~137.50776°) and taking the modulo 360° of that. Due to the properties of the golden angle, this can generate never repeating sequences of unique colors where the adjacent colors are very distinct. Duplicate code is reduced by adding the `CGameClient::GetDDTeamColor` function. --- src/game/client/components/chat.cpp | 2 +- src/game/client/components/killmessages.cpp | 15 ++++++--------- src/game/client/components/nameplates.cpp | 2 +- src/game/client/components/scoreboard.cpp | 2 +- src/game/client/components/spectator.cpp | 2 +- src/game/client/gameclient.cpp | 8 ++++++++ src/game/client/gameclient.h | 1 + 7 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index b5c875595..17791ed96 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -977,7 +977,7 @@ void CChat::OnPrepareLines() else if(m_aLines[r].m_NameColor == TEAM_SPECTATORS) NameColor = ColorRGBA(0.75f, 0.5f, 0.75f, 1.f); else if(m_aLines[r].m_ClientID >= 0 && g_Config.m_ClChatTeamColors && m_pClient->m_Teams.Team(m_aLines[r].m_ClientID)) - NameColor = color_cast(ColorHSLA(m_pClient->m_Teams.Team(m_aLines[r].m_ClientID) / 64.0f, 1.0f, 0.75f)); + NameColor = m_pClient->GetDDTeamColor(m_pClient->m_Teams.Team(m_aLines[r].m_ClientID), 0.75f); else NameColor = ColorRGBA(0.8f, 0.8f, 0.8f, 1.f); diff --git a/src/game/client/components/killmessages.cpp b/src/game/client/components/killmessages.cpp index 212c7293c..7c4a69af3 100644 --- a/src/game/client/components/killmessages.cpp +++ b/src/game/client/components/killmessages.cpp @@ -278,21 +278,18 @@ void CKillMessages::OnRender() float x = StartX; - ColorRGBA TColor(1.f, 1.f, 1.f, 1.f); - ColorRGBA TOutlineColor(0.f, 0.f, 0.f, 0.3f); - // render victim name x -= m_aKillmsgs[r].m_VictimTextWidth; + ColorRGBA TextColor; if(m_aKillmsgs[r].m_VictimID >= 0 && g_Config.m_ClChatTeamColors && m_aKillmsgs[r].m_VictimDDTeam) - { - TColor = color_cast(ColorHSLA(m_aKillmsgs[r].m_VictimDDTeam / 64.0f, 1.0f, 0.75f)); - TColor.a = 1.f; - } + TextColor = m_pClient->GetDDTeamColor(m_aKillmsgs[r].m_VictimDDTeam, 0.75f); + else + TextColor = TextRender()->DefaultTextColor(); CreateKillmessageNamesIfNotCreated(m_aKillmsgs[r]); if(m_aKillmsgs[r].m_VictimTextContainerIndex.Valid()) - TextRender()->RenderTextContainer(m_aKillmsgs[r].m_VictimTextContainerIndex, TColor, TOutlineColor, x, y + (46.f - 36.f) / 2.f); + TextRender()->RenderTextContainer(m_aKillmsgs[r].m_VictimTextContainerIndex, TextColor, TextRender()->DefaultTextOutlineColor(), x, y + (46.f - 36.f) / 2.f); // render victim tee x -= 24.0f; @@ -380,7 +377,7 @@ void CKillMessages::OnRender() x -= m_aKillmsgs[r].m_KillerTextWidth; if(m_aKillmsgs[r].m_KillerTextContainerIndex.Valid()) - TextRender()->RenderTextContainer(m_aKillmsgs[r].m_KillerTextContainerIndex, TColor, TOutlineColor, x, y + (46.f - 36.f) / 2.f); + TextRender()->RenderTextContainer(m_aKillmsgs[r].m_KillerTextContainerIndex, TextColor, TextRender()->DefaultTextOutlineColor(), x, y + (46.f - 36.f) / 2.f); } y += 46.0f; diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp index ca08c6946..521cce7f2 100644 --- a/src/game/client/components/nameplates.cpp +++ b/src/game/client/components/nameplates.cpp @@ -144,7 +144,7 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP float tw = m_aNamePlates[ClientID].m_NameTextWidth; if(g_Config.m_ClNameplatesTeamcolors && m_pClient->m_Teams.Team(ClientID)) - rgb = color_cast(ColorHSLA(m_pClient->m_Teams.Team(ClientID) / 64.0f, 1.0f, 0.75f)); + rgb = m_pClient->GetDDTeamColor(m_pClient->m_Teams.Team(ClientID), 0.75f); ColorRGBA TColor; ColorRGBA TOutlineColor; diff --git a/src/game/client/components/scoreboard.cpp b/src/game/client/components/scoreboard.cpp index b84f07159..5643f57a4 100644 --- a/src/game/client/components/scoreboard.cpp +++ b/src/game/client/components/scoreboard.cpp @@ -355,7 +355,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch if(DDTeam != TEAM_FLOCK) { - ColorRGBA Color = color_cast(ColorHSLA(DDTeam / 64.0f, 1.0f, 0.5f, 0.5f)); + const ColorRGBA Color = m_pClient->GetDDTeamColor(DDTeam).WithAlpha(0.5f); int Corners = 0; if(OldDDTeam != DDTeam) Corners |= IGraphics::CORNER_TL | IGraphics::CORNER_TR; diff --git a/src/game/client/components/spectator.cpp b/src/game/client/components/spectator.cpp index 21f6e7914..0f5d28d40 100644 --- a/src/game/client/components/spectator.cpp +++ b/src/game/client/components/spectator.cpp @@ -401,7 +401,7 @@ void CSpectator::OnRender() if(DDTeam != TEAM_FLOCK) { - ColorRGBA Color = color_cast(ColorHSLA(DDTeam / 64.0f, 1.0f, 0.5f, 0.5f)); + const ColorRGBA Color = m_pClient->GetDDTeamColor(DDTeam).WithAlpha(0.5f); int Corners = 0; if(OldDDTeam != DDTeam) Corners |= IGraphics::CORNER_TL | IGraphics::CORNER_TR; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 72c1d8591..ae72bcdf9 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -765,6 +765,14 @@ bool CGameClient::Predict() const return !m_Snap.m_SpecInfo.m_Active && m_Snap.m_pLocalCharacter; } +ColorRGBA CGameClient::GetDDTeamColor(int DDTeam, float Lightness) const +{ + // Use golden angle to generate unique colors with distinct adjacent colors. + // The first DDTeam (team 1) gets angle 0°, i.e. red hue. + const float Hue = std::fmod((DDTeam - 1) * (137.50776f / 360.0f), 1.0f); + return color_cast(ColorHSLA(Hue, 1.0f, Lightness)); +} + void CGameClient::OnRelease() { // release all systems diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 8e4056959..d758e07a5 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -530,6 +530,7 @@ public: bool Predict() const; bool PredictDummy() { return g_Config.m_ClPredictDummy && Client()->DummyConnected() && m_Snap.m_LocalClientID >= 0 && m_PredictedDummyID >= 0 && !m_aClients[m_PredictedDummyID].m_Paused; } const CTuningParams *GetTuning(int i) { return &m_aTuningList[i]; } + ColorRGBA GetDDTeamColor(int DDTeam, float Lightness = 0.5f) const; CGameWorld m_GameWorld; CGameWorld m_PredictedWorld;