Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/game/client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1674,6 +1674,7 @@ set(UNITY_SOURCE_NEO_UI
neo/ui/neo_scoreboard.cpp
neo/ui/neo_hud_context_hint.cpp
neo/ui/neo_spec_player_by_hud.cpp
neo/ui/neo_avatar.cpp
)

set_source_files_properties(
Expand Down Expand Up @@ -1715,6 +1716,7 @@ target_sources_grouped(
neo/ui/neo_hud_worldpos_marker_generic.h
neo/ui/neo_scoreboard.h
neo/ui/neo_hud_context_hint.h
neo/ui/neo_avatar.h
${UNITY_SOURCE_NEO_UI}
)

Expand Down
170 changes: 170 additions & 0 deletions src/game/client/neo/ui/neo_avatar.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#include "neo_avatar.h"

#include "cbase.h"
#include <vgui/ISurface.h>
#include <vgui_controls/Controls.h>
#include "VGuiMatSurface/IMatSystemSurface.h"

struct CacheAvatarValue
{
int normal;
int dead;
};

struct CacheAvatarKey
{
CSteamID m_SteamID;
int m_iAvatar = 0;

bool operator<(const CacheAvatarKey &rhs) const
{
return m_SteamID.ConvertToUint64() < rhs.m_SteamID.ConvertToUint64()
|| (m_SteamID.ConvertToUint64() == rhs.m_SteamID.ConvertToUint64()
&& m_iAvatar < rhs.m_iAvatar);
}
};

static inline CUtlMap<CacheAvatarKey, CacheAvatarValue> gAvatarImageCache;
static inline bool gAvatarImageCacheInit = false;

void NeoAvatar::SetSteamID(const CSteamID &steamID)
{
if (steamID == m_SteamID)
{
return;
}
m_SteamID = steamID;
m_iTextureID = 0;
m_iTextureDeadID = 0;
m_flPrevLoadAttempt = 0.0f;
}

void NeoAvatar::Fetch(const int iAvatarWH)
{
if (!m_SteamID.IsValid()
|| !SteamFriends()
|| !SteamUtils()
|| (m_iTextureID > 0 && m_iTexForAvatarWH == iAvatarWH)
|| (m_flPrevLoadAttempt + 1.0f) > gpGlobals->realtime)
{
return;
}

if (!SteamFriends()->RequestUserInformation(m_SteamID, false))
{
int iAvatar = 0;
if (iAvatarWH <= 32)
{
iAvatar = SteamFriends()->GetSmallFriendAvatar(m_SteamID);
}
else if (iAvatarWH <= 64)
{
iAvatar = SteamFriends()->GetMediumFriendAvatar(m_SteamID);
}
else
{
iAvatar = SteamFriends()->GetLargeFriendAvatar(m_SteamID);
}
m_iTexForAvatarWH = iAvatarWH;

if (iAvatar > 0)
{
if (!gAvatarImageCacheInit)
{
SetDefLessFunc(gAvatarImageCache);
gAvatarImageCacheInit = true;
}

int iTexIndex = gAvatarImageCache.Find(CacheAvatarKey{m_SteamID, iAvatar});
if (iTexIndex == gAvatarImageCache.InvalidIndex())
{
uint32 u32wide = 0, u32tall = 0;
if (SteamUtils()->GetImageSize(iAvatar, &u32wide, &u32tall)
&& u32wide > 0 && u32tall > 0)
{
const int wide = u32wide;
const int tall = u32tall;
const int destBufferSize = wide * tall * 4;
byte *rgbaBuf = (byte *)stackalloc(destBufferSize);
if (SteamUtils()->GetImageRGBA(iAvatar, rgbaBuf, destBufferSize))
{
// Create normal avatar from RGBA without edits
m_iTextureID = vgui::surface()->CreateNewTextureID(true);
g_pMatSystemSurface->DrawSetTextureRGBAEx2(m_iTextureID, rgbaBuf, wide, tall, IMAGE_FORMAT_RGBA8888, true);

// Create dead avatar from RGBA with redness edits
for (int offset = 0; offset < (wide * tall * 4); offset += 4)
{
constexpr float brightness = 0.5f;
constexpr float contrast = 1.5f;

float r = (rgbaBuf + offset)[0] / 255.0f;
float g = (rgbaBuf + offset)[1] / 255.0f;
float b = (rgbaBuf + offset)[2] / 255.0f;

// Contrast
r = Clamp((r - 0.5f) * contrast + 0.5f, 0.0f, 1.0f);
g = Clamp((g - 0.5f) * contrast + 0.5f, 0.0f, 1.0f);
b = Clamp((b - 0.5f) * contrast + 0.5f, 0.0f, 1.0f);

// Convert to grayscale - Luminosity, then gradient from black -> red -> white
const float gray = 0.3f * r + 0.59f * g + 0.11f * b;
if (gray < 0.5)
{
r = gray * 2.0f;
g = 0;
b = 0;
}
else {
r = 1.0f;
g = (gray - 0.5f) * 2.0f;
b = (gray - 0.5f) * 2.0f;
}

// Brightness
r = Clamp(r * brightness, 0.0f, 1.0f);
g = Clamp(g * brightness, 0.0f, 1.0f);
b = Clamp(b * brightness, 0.0f, 1.0f);

(rgbaBuf + offset)[0] = r * 255;
(rgbaBuf + offset)[1] = g * 255;
(rgbaBuf + offset)[2] = b * 255;
}

m_iTextureDeadID = vgui::surface()->CreateNewTextureID(true);
g_pMatSystemSurface->DrawSetTextureRGBAEx2(m_iTextureDeadID, rgbaBuf, wide, tall, IMAGE_FORMAT_RGBA8888, true);

// Add textures to global cache
iTexIndex = gAvatarImageCache.Insert(CacheAvatarKey{m_SteamID, iAvatar});
gAvatarImageCache[iTexIndex].normal = m_iTextureID;
gAvatarImageCache[iTexIndex].dead = m_iTextureDeadID;
}
stackfree(rgbaBuf);
}
}
else
{
m_iTextureID = gAvatarImageCache[iTexIndex].normal;
m_iTextureDeadID = gAvatarImageCache[iTexIndex].dead;
}
}
}

if (m_iTextureID == 0)
{
m_flPrevLoadAttempt = gpGlobals->realtime;
}
}

void NeoAvatar::Paint(const int x, const int y, const int widetall,
const bool bDead) const
{
if (m_iTextureID == 0)
{
return;
}
vgui::surface()->DrawSetTexture(bDead ? m_iTextureDeadID : m_iTextureID);
vgui::surface()->DrawSetColor(COLOR_WHITE);
vgui::surface()->DrawTexturedRect(x, y, x + widetall, y + widetall);
}

18 changes: 18 additions & 0 deletions src/game/client/neo/ui/neo_avatar.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include "steam/steam_api.h"

struct NeoAvatar
{
CSteamID m_SteamID;
int m_iTextureID = 0;
int m_iTextureDeadID = 0;
int m_iTexForAvatarWH = 0;
float m_flPrevLoadAttempt = 0.0f;

void SetSteamID(const CSteamID &steamID);
void Fetch(const int iAvatarWH);
void Paint(const int x, const int y, const int widetall,
const bool bDead = false) const;
};

51 changes: 10 additions & 41 deletions src/game/client/neo/ui/neo_hud_killer_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ CNEOHud_KillerInfo::CNEOHud_KillerInfo(const char *pszName, vgui::Panel *parent)
}
SetVisible(false);
CommonSetupHUD();

m_avatar32.SetAvatarSize(32, 32);
m_avatar64.SetAvatarSize(64, 64);
m_avatar184.SetAvatarSize(184, 184);
}

void CNEOHud_KillerInfo::ApplySchemeSettings(vgui::IScheme *pScheme)
Expand Down Expand Up @@ -77,18 +73,10 @@ void CNEOHud_KillerInfo::UpdateStateForNeoHudElementDraw()
&& (gpGlobals->curtime < (const_cast<C_NEO_Player *>(pLocalPlayer)->GetDeathTime() + DEATH_ANIMATION_TIME));

const CSteamID steamID = GetSteamIDForPlayerIndex(g_neoKillerInfos.iEntIndex);
if (steamID.IsValid())
{
m_avatar32.SetAvatarSteamID(steamID, k_EAvatarSize32x32);
m_avatar64.SetAvatarSteamID(steamID, k_EAvatarSize64x64);
m_avatar184.SetAvatarSteamID(steamID, k_EAvatarSize184x184);
}
else
{
m_avatar32.ClearAvatarSteamID();
m_avatar64.ClearAvatarSteamID();
m_avatar184.ClearAvatarSteamID();
}
m_avatar.SetSteamID(
(steamID.IsValid() && (CNEOScoreBoard::ShowAvatars()))
? steamID : CSteamID{});
m_avatar.Fetch(184);
}

void CNEOHud_KillerInfo::DrawNeoHudElement()
Expand Down Expand Up @@ -116,28 +104,12 @@ void CNEOHud_KillerInfo::DrawNeoHudElement()

const bool bSuicideKill = (iKillerEntIndex == pLocalPlayer->entindex()) ||
(iKillerEntIndex == NEO_ENVIRON_KILLED);

CAvatarImage *pAvatarImg = nullptr;
if (CNEOScoreBoard::ShowAvatars())
{
if (m_avatar184.IsValid())
{
pAvatarImg = &m_avatar184;
}
else if (m_avatar64.IsValid())
{
pAvatarImg = &m_avatar64;
}
else if (m_avatar32.IsValid())
{
pAvatarImg = &m_avatar32;
}
}
const bool bValidAvatar = m_avatar.m_iTextureID > 0;

// NEO NOTE (nullsystem): Similar sizing to how the scoreboard popup card is
const int iMargin = wide / 384;
const int iAvatarWT = pAvatarImg ? (((tall / 35) * 3) - (iMargin * 2)) : 0;
const int iTextOffsetX = pAvatarImg ? (iAvatarWT + (2 * iMargin)) : iMargin;
const int iAvatarWT = bValidAvatar ? (((tall / 35) * 3) - (iMargin * 2)) : 0;
const int iTextOffsetX = bValidAvatar ? (iAvatarWT + (2 * iMargin)) : iMargin;

wchar_t wszText[256] = {};

Expand All @@ -161,7 +133,7 @@ void CNEOHud_KillerInfo::DrawNeoHudElement()
vgui::surface()->GetTextSize(m_hFontNormal, L"A", iNormalWide, iNormalTall);

const int iBoxWide = Max(wide / 4, iTextOffsetX + iTitleWide + iMargin);
const int iBoxHeight = (pAvatarImg)
const int iBoxHeight = (bValidAvatar)
? (2 * iMargin) + iAvatarWT
: iMargin + iTitleTall + iNormalTall + iMargin;

Expand All @@ -173,12 +145,9 @@ void CNEOHud_KillerInfo::DrawNeoHudElement()
};
DrawNeoHudRoundedBox(rect.x0, rect.y0, rect.x1, rect.y1, COLOR_BLACK_TRANSPARENT);

if (pAvatarImg)
if (bValidAvatar)
{
pAvatarImg->m_bDeadAvatar = false;
pAvatarImg->SetPos(rect.x0 + iMargin, rect.y0 + iMargin);
pAvatarImg->SetSize(iAvatarWT, iAvatarWT);
pAvatarImg->Paint();
m_avatar.Paint(rect.x0 + iMargin, rect.y0 + iMargin, iAvatarWT);
}

vgui::surface()->DrawSetTextFont(m_hFontTitle);
Expand Down
6 changes: 2 additions & 4 deletions src/game/client/neo/ui/neo_hud_killer_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#include <hudelement.h>
#include <vgui_controls/Panel.h>
#include <vgui_avatarimage.h>
#include "neo_avatar.h"

class CNEOHud_KillerInfo : public CNEOHud_ChildElement, public CHudElement, public vgui::Panel
{
Expand All @@ -28,8 +28,6 @@ class CNEOHud_KillerInfo : public CNEOHud_ChildElement, public CHudElement, publ

vgui::HFont m_hFontNormal;
vgui::HFont m_hFontTitle;
CAvatarImage m_avatar32;
CAvatarImage m_avatar64;
CAvatarImage m_avatar184;
NeoAvatar m_avatar = {};
bool m_bPlayerShownHud = false;
};
1 change: 0 additions & 1 deletion src/game/client/neo/ui/neo_hud_round_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "c_neo_player.h"
#include "c_team.h"
#include "c_playerresource.h"
#include "vgui_avatarimage.h"
#include "neo_scoreboard.h"
#include "neo_hud_spectator_overlay.h"

Expand Down
Loading