Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Sprays Importer/Gallery + fixed empty spray + NeoUI Texture/Image button + stb libraries #773

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
13 changes: 13 additions & 0 deletions mp/src/game/client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ target_include_directories(client
${CMAKE_SOURCE_DIR}/game/shared/neo/weapons
${CMAKE_SOURCE_DIR}/public
${CMAKE_SOURCE_DIR}/thirdparty/sixensesdk/include
${CMAKE_SOURCE_DIR}/vendor
game_controls
hl2
hl2/elements
Expand Down Expand Up @@ -1488,6 +1489,16 @@ target_sources_grouped(
neo/neo_fixup_glshaders.h
)

target_sources_grouped(
TARGET client
NAME "Source Files\\NEO_Vendor"
FILES
neo/vendor_impl.cpp
${CMAKE_SOURCE_DIR}/vendor/stb_image.h
${CMAKE_SOURCE_DIR}/vendor/stb_image_resize2.h
${CMAKE_SOURCE_DIR}/vendor/stb_dxt.h
)

target_sources_grouped(
TARGET client
NAME "Source Files\\NEO\\Game_Controls"
Expand Down Expand Up @@ -1522,6 +1533,8 @@ target_sources_grouped(
neo/ui/neo_loading.h
neo/ui/neo_ui.cpp
neo/ui/neo_ui.h
neo/ui/neo_utils.cpp
neo/ui/neo_utils.h
neo/ui/IOverrideInterface.h
neo/ui/OverrideInterface.cpp
neo/ui/neo_hud_ammo.cpp
Expand Down
26 changes: 25 additions & 1 deletion mp/src/game/client/c_te_playerdecal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,35 @@ IMaterial *CreateTempMaterialForPlayerLogo( int iPlayerIndex, player_info_t *inf

if ( !filesystem->FileExists( fulltexname ) )
{
#ifdef NEO
if (engine->GetLocalPlayer() == iPlayerIndex)
{
// NEO NOTE (nullsystem): Limit copying spray.vtf over for local player
engine->CopyLocalFile("materials/vgui/logos/spray.vtf", fulltexname);
}
#endif

char custname[ 512 ];
Q_snprintf( custname, sizeof( custname ), "download/user_custom/%c%c/%s.dat", logohex[0], logohex[1], logohex );
// it may have been downloaded but not copied under materials folder
if ( !filesystem->FileExists( custname ) )
if (!filesystem->FileExists(custname))
{
#ifdef NEO
if (engine->GetLocalPlayer() == iPlayerIndex)
{
if (!engine->CopyLocalFile(fulltexname, custname))
{
return nullptr;
}
}
else
{
return nullptr;
}
#else
return NULL; // not downloaded yet
#endif
}

// copy from download folder to materials/temp folder
// this is done since material system can access only materials/*.vtf files
Expand Down
283 changes: 281 additions & 2 deletions mp/src/game/client/neo/ui/neo_root.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
#include "tier1/interface.h"
#include <ctime>
#include "ui/neo_loading.h"
#include "ui/neo_utils.h"

#include <vgui/IInput.h>
#include <vgui_controls/Controls.h>
#include <stb_image.h>

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
Expand Down Expand Up @@ -498,6 +500,7 @@ void CNeoRoot::OnMainLoop(const NeoUI::Mode eMode)
&CNeoRoot::MainLoopMapList, // STATE_MAPLIST
&CNeoRoot::MainLoopServerDetails, // STATE_SERVERDETAILS
&CNeoRoot::MainLoopPlayerList, // STATE_PLAYERLIST
&CNeoRoot::MainLoopSprayPicker, // STATE_SPRAYPICKER

&CNeoRoot::MainLoopPopup, // STATE_KEYCAPTURE
&CNeoRoot::MainLoopPopup, // STATE_CONFIRMSETTINGS
Expand Down Expand Up @@ -1189,6 +1192,99 @@ void CNeoRoot::MainLoopMapList(const MainLoopParam param)
NeoUI::EndContext();
}

void CNeoRoot::MainLoopSprayPicker(const MainLoopParam param)
{
struct SprayInfo
{
char szPath[MAX_PATH];
char szVtf[MAX_PATH];
};
static CUtlVector<SprayInfo> vecStaticSprays;
if (m_bSprayGalleryRefresh)
{
vecStaticSprays.Purge();
FileFindHandle_t findHdl;
for (const char *pszFilename = filesystem->FindFirst("materials/vgui/logos/ui/*.vmt", &findHdl);
pszFilename;
pszFilename = filesystem->FindNext(findHdl))
{
// spray.vmt is only used for currently applying spray
if (V_strcmp(pszFilename, "spray.vmt") == 0)
{
continue;
}

SprayInfo sprayInfo = {};

V_sprintf_safe(sprayInfo.szPath, "vgui/logos/ui/%s", pszFilename);
sprayInfo.szPath[V_strlen(sprayInfo.szPath) - sizeof("vmt")] = '\0';

V_sprintf_safe(sprayInfo.szVtf, "materials/vgui/logos/%s", pszFilename);
sprayInfo.szVtf[V_strlen(sprayInfo.szVtf) - sizeof("vmt")] = '\0';
V_strcat_safe(sprayInfo.szVtf, ".vtf");

vecStaticSprays.AddToTail(sprayInfo);
}
filesystem->FindClose(findHdl);

m_bSprayGalleryRefresh = false;
}
const int iGalleryRows = g_iRowsInScreen / 4;
const int iNormTall = g_uiCtx.iRowTall;
const int iCellTall = iNormTall * 4;

const int iTallTotal = iNormTall * (g_iRowsInScreen + 2);
g_uiCtx.dPanel.wide = g_iRootSubPanelWide;
g_uiCtx.dPanel.x = (param.wide / 2) - (g_iRootSubPanelWide / 2);
g_uiCtx.dPanel.y = (param.tall / 2) - (iTallTotal / 2);
g_uiCtx.dPanel.tall = iNormTall * (g_iRowsInScreen + 1);
g_uiCtx.bgColor = COLOR_NEOPANELFRAMEBG;
NeoUI::BeginContext(&g_uiCtx, param.eMode, L"Pick spray", "CtxSprayPicker");
{
g_uiCtx.iRowTall = iCellTall;
NeoUI::BeginSection(true);
{
static constexpr int COLS_IN_ROW = 5;
for (int i = 0; i < vecStaticSprays.Count(); i += COLS_IN_ROW)
{
NeoUI::BeginHorizontal(g_uiCtx.dPanel.wide / COLS_IN_ROW);
{
for (int j = 0; j < COLS_IN_ROW && (i + j) < vecStaticSprays.Count(); ++j)
{
const auto &sprayInfo = vecStaticSprays.Element(i + j);
if (NeoUI::ButtonTexture(sprayInfo.szPath).bPressed)
{
m_eFileIOMode = FILEIODLGMODE_SPRAY;
OnFileSelected(sprayInfo.szVtf);
m_state = STATE_SETTINGS;
}
}
}
NeoUI::EndHorizontal();
}
}
NeoUI::EndSection();
g_uiCtx.iRowTall = iNormTall;
g_uiCtx.dPanel.y += g_uiCtx.dPanel.tall;
g_uiCtx.dPanel.tall = g_uiCtx.iRowTall;
NeoUI::BeginSection();
{
NeoUI::SwapFont(NeoUI::FONT_NTHORIZSIDES);
NeoUI::BeginHorizontal(g_uiCtx.dPanel.wide / 5);
{
if (NeoUI::Button(L"Back (ESC)").bPressed || NeoUI::Bind(KEY_ESCAPE))
{
m_state = STATE_SETTINGS;
}
}
NeoUI::EndHorizontal();
NeoUI::SwapFont(NeoUI::FONT_NTNORMAL);
}
NeoUI::EndSection();
}
NeoUI::EndContext();
}

void CNeoRoot::MainLoopServerDetails(const MainLoopParam param)
{
const auto *gameServer = &m_serverBrowser[m_iServerBrowserTab].m_filteredServers[m_iSelectedServer];
Expand Down Expand Up @@ -1540,8 +1636,191 @@ void CNeoRoot::ReadNewsFile(CUtlBuffer &buf)

void CNeoRoot::OnFileSelected(const char *szFullpath)
{
((m_ns.crosshair.eFileIOMode == vgui::FOD_OPEN) ?
&ImportCrosshair : &ExportCrosshair)(&m_ns.crosshair.info, szFullpath);
switch (m_eFileIOMode)
{
case FILEIODLGMODE_CROSSHAIR:
((m_ns.crosshair.eFileIOMode == vgui::FOD_OPEN) ?
&ImportCrosshair : &ExportCrosshair)(&m_ns.crosshair.info, szFullpath);
break;
case FILEIODLGMODE_SPRAY:
{
// Ensure the directories are there to write to
filesystem->CreateDirHierarchy("materials/vgui/logos");
filesystem->CreateDirHierarchy("materials/vgui/logos/ui");

CUtlBuffer sprayBuffer(0, 0, 0);

if (V_striEndsWith(szFullpath, ".vtf"))
{
// Check if we can just direct copy over (is DXT1 and 256x256) or will have
// to go through reprocessing like a PNG/JPEG format would
CUtlBuffer buf(0, 0, CUtlBuffer::READ_ONLY);
if (!filesystem->ReadFile(szFullpath, nullptr, buf))
{
return;
}

IVTFTexture *pVTFTexture = CreateVTFTexture();
if (pVTFTexture->Unserialize(buf))
{
const bool bDirectCopy = (pVTFTexture->Width() == pVTFTexture->Height() &&
(pVTFTexture->Format() == IMAGE_FORMAT_DXT1) ||
(pVTFTexture->Format() == IMAGE_FORMAT_DXT5));
if (bDirectCopy)
{
if (!engine->CopyLocalFile(szFullpath, "materials/vgui/logos/spray.vtf"))
{
Msg("ERROR: Cannot copy spray's vtf to: %s", "materials/vgui/logos/spray.vtf");
}
}
else
{
pVTFTexture->ConvertImageFormat(IMAGE_FORMAT_RGBA8888, false);
uint8 *data = NeoUtils::CropScaleTo256(pVTFTexture->ImageData(0, 0, 0),
pVTFTexture->Width(), pVTFTexture->Height());
NeoUtils::SerializeVTFDXTSprayToBuffer(&sprayBuffer, data);
free(data);
}
}
DestroyVTFTexture(pVTFTexture);
}
else
{
int width, height, channels;
uint8 *data = stbi_load(szFullpath, &width, &height, &channels, 0);
if (!data || width <= 0 || height <= 0)
{
return;
}

// Convert to RGBA
if (channels == 3)
{
uint8 *rgbaData = reinterpret_cast<uint8 *>(calloc(width * height, sizeof(uint8) * 4));

ImageLoader::ConvertImageFormat(data, IMAGE_FORMAT_RGB888,
rgbaData, IMAGE_FORMAT_RGBA8888,
width, height);

stbi_image_free(data);
data = rgbaData;
channels = 4;
}

{
uint8 *newData = NeoUtils::CropScaleTo256(data, width, height);
free(data);
data = newData;
width = NeoUtils::SPRAY_WH;
height = NeoUtils::SPRAY_WH;
}

NeoUtils::SerializeVTFDXTSprayToBuffer(&sprayBuffer, data);
free(data);
}

char szBaseFName[MAX_PATH] = {};
{
const char *pLastSlash = V_strrchr(szFullpath, '/');
const char *pszBaseName = pLastSlash ? pLastSlash + 1 : szFullpath;
int iSzLen = V_strlen(pszBaseName);
const char *pszDot = strchr(pszBaseName, '.');
if (pszDot)
{
iSzLen = pszDot - pszBaseName;
}
V_sprintf_safe(szBaseFName, "%.*s", iSzLen, pszBaseName);
}
char szByBaseName[MAX_PATH];
V_sprintf_safe(szByBaseName, "materials/vgui/logos/%s.vtf", szBaseFName);

if (sprayBuffer.Size() > 0)
{
if (!filesystem->WriteFile("materials/vgui/logos/spray.vtf", nullptr, sprayBuffer))
{
Msg("ERROR: Cannot save spray's vtf to: %s", "materials/vgui/logos/spray.vtf");
}
if (!filesystem->WriteFile(szByBaseName, nullptr, sprayBuffer))
{
Msg("ERROR: Cannot save spray's vtf to: %s", szByBaseName);
}
}
else if (V_strcmp(szFullpath, szByBaseName) != 0)
{
if (!engine->CopyLocalFile(szFullpath, szByBaseName))
{
Msg("ERROR: Cannot copy spray's vtf to: %s", szByBaseName);
}
}

// Generate the vmt files, one for spraying, one under "ui" to display in GUI
for (const char *pszBaseName : {"spray", static_cast<const char *>(szBaseFName)})
{
if (pszBaseName[0] == '\0')
{
continue;
}

char szStrBuffer[1024];
{
V_sprintf_safe(szStrBuffer, R"VMT(
LightmappedGeneric
{
"$basetexture" "vgui/logos/%s"
"$translucent" "1"
"$decal" "1"
"$decalscale" "0.250"
}
)VMT", pszBaseName);

CUtlBuffer bufVmt(0, 0, CUtlBuffer::TEXT_BUFFER);
bufVmt.PutString(szStrBuffer);

char szOutPath[MAX_PATH];
V_sprintf_safe(szOutPath, "materials/vgui/logos/%s.vmt", pszBaseName);
if (!filesystem->WriteFile(szOutPath, nullptr, bufVmt))
{
Msg("ERROR: Cannot save spray's vmt to: %s", szOutPath);
}
}

{
V_sprintf_safe(szStrBuffer, R"VMT(
"UnlitGeneric"
{
// Original shader: BaseTimesVertexColorAlphaBlendNoOverbright
"$translucent" 1
"$basetexture" "VGUI/logos/%s"
"$vertexcolor" 1
"$vertexalpha" 1
"$no_fullbright" 1
"$ignorez" 1
}
)VMT", pszBaseName);

CUtlBuffer bufVmt(0, 0, CUtlBuffer::TEXT_BUFFER);
bufVmt.PutString(szStrBuffer);

char szOutPath[MAX_PATH];
V_sprintf_safe(szOutPath, "materials/vgui/logos/ui/%s.vmt", pszBaseName);
if (!filesystem->WriteFile(szOutPath, nullptr, bufVmt))
{
Msg("ERROR: Cannot save spray's vmt to: %s", szOutPath);
}
}
}

// Reapply the cl_logofile ConVar, update the texture to the new spray
ConVarRef("cl_logofile").SetValue("materials/vgui/logos/spray.vtf");
engine->ClientCmd_Unrestricted("cl_logofile materials/vgui/logos/spray.vtf");

NeoUI::ResetTextures();
materials->ReloadMaterials("vgui/logo");
break;
}
default:
break;
}
}

// NEO NOTE (nullsystem): NeoRootCaptureESC is so that ESC keybinds can be recognized by non-root states, but root
Expand Down
Loading