Skip to content

Commit

Permalink
Sprays - Initial commit
Browse files Browse the repository at this point in the history
* Integrate stb_image.h library
* rought draft
* fixes NeotokyoRebuild#602
  • Loading branch information
nullsystem committed Sep 29, 2024
1 parent bc8e217 commit fbaab6c
Show file tree
Hide file tree
Showing 9 changed files with 8,278 additions and 2 deletions.
9 changes: 9 additions & 0 deletions mp/src/game/client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,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 @@ -1486,6 +1487,14 @@ 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
)

target_sources_grouped(
TARGET client
NAME "Source Files\\NEO\\Game_Controls"
Expand Down
52 changes: 50 additions & 2 deletions mp/src/game/client/neo/ui/neo_root.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1502,8 +1502,56 @@ 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:
{
char szRetTexPath[VTF_PATH_MAX] = {};
uint8 *data = NeoUI::ConvertToVTF(&szRetTexPath, szFullpath);

char szRetVtfPath[PATH_MAX];
V_sprintf_safe(szRetVtfPath, "%s.vtf", szRetTexPath);

ConVarRef("cl_logofile").SetValue(szRetVtfPath);
if (m_ns.general.iTexIdSpray > 0)
{
vgui::surface()->DeleteTextureByID(m_ns.general.iTexIdSpray);
}
#if 1
m_ns.general.iTexIdSpray = vgui::surface()->CreateNewTextureID(true);
//vgui::surface()->DrawSetTextureFile(m_ns.general.iTexIdSpray, szRetTexPath, false, false);
vgui::surface()->DrawSetTextureRGBA(m_ns.general.iTexIdSpray, data, 256, 256, false, false);
free(data);
#else
CUtlBuffer buf(0, 0, CUtlBuffer::READ_ONLY);
if (filesystem->ReadFile(szRetFullPath, nullptr, buf))
{
IVTFTexture *pVTFTexture = CreateVTFTexture();
if (pVTFTexture->Unserialize(buf))
{
const auto width = pVTFTexture->Width();
const auto height = pVTFTexture->Height();
const auto depth = pVTFTexture->Depth();
const auto format = pVTFTexture->Format();
const auto flags = pVTFTexture->Flags();
const auto mip = pVTFTexture->MipCount();
const auto face = pVTFTexture->FaceCount();
const auto frame = pVTFTexture->FrameCount();
}
//DestroyVTFTexture(pVTFTexture);
//stbi_image_free(data);
}
#endif
break;
}
default:
break;
}
}

// NEO NOTE (nullsystem): NeoRootCaptureESC is so that ESC keybinds can be recognized by non-root states, but root
Expand Down
8 changes: 8 additions & 0 deletions mp/src/game/client/neo/ui/neo_root.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,14 @@ class CNeoRoot : public vgui::EditablePanel, public CGameEventListener
void ReadNewsFile(CUtlBuffer &buf);
bool m_bShowBrowserLabel = false;

enum FileIODialogMode
{
FILEIODLGMODE_CROSSHAIR = 0,
FILEIODLGMODE_SPRAY,

FILEIODLGMODE__TOTAL,
};
FileIODialogMode m_eFileIOMode;
vgui::FileOpenDialog *m_pFileIODialog = nullptr;
MESSAGE_FUNC_CHARPTR(OnFileSelected, "FileSelected", fullpath);
};
Expand Down
40 changes: 40 additions & 0 deletions mp/src/game/client/neo/ui/neo_root_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,17 @@ void NeoSettingsRestore(NeoSettings *ns, const NeoSettings::Keys::Flags flagsKey
}
}
}

if (pGeneral->iTexIdSpray > 0)
{
vgui::surface()->DeleteTextureByID(pGeneral->iTexIdSpray);
}
const char *szLogo = ConVarRef("cl_logofile").GetString();
if (szLogo)
{
pGeneral->iTexIdSpray = vgui::surface()->CreateNewTextureID();
vgui::surface()->DrawSetTextureFile(pGeneral->iTexIdSpray, szLogo, false, false);
}
}
{
NeoSettings::Keys *pKeys = &ns->keys;
Expand Down Expand Up @@ -628,6 +639,34 @@ void NeoSettings_General(NeoSettings *ns)
NeoUI::RingBoxBool(L"Show position", &pGeneral->bShowPos);
NeoUI::RingBox(L"Show FPS", SHOWFPS_LABELS, ARRAYSIZE(SHOWFPS_LABELS), &pGeneral->iShowFps);
NeoUI::RingBox(L"Download filter", DLFILTER_LABELS, ARRAYSIZE(DLFILTER_LABELS), &pGeneral->iDlFilter);

NeoUI::Pad();
if (NeoUI::Button(L"", L"Import spray").bPressed)
{
if (g_pNeoRoot->m_pFileIODialog)
{
g_pNeoRoot->m_pFileIODialog->MarkForDeletion();
}
g_pNeoRoot->m_pFileIODialog = new vgui::FileOpenDialog(g_pNeoRoot, "Import spray", vgui::FOD_OPEN);
g_pNeoRoot->m_eFileIOMode = CNeoRoot::FILEIODLGMODE_SPRAY;
g_pNeoRoot->m_pFileIODialog->AddFilter("*.jpg;*.jpeg;*.png;*.vtf", "Images (JPEG, PNG, VTF)", true);
g_pNeoRoot->m_pFileIODialog->AddFilter("*.jpg;*.jpeg", "JPEG Image", false);
g_pNeoRoot->m_pFileIODialog->AddFilter("*.png", "PNG Image", false);
g_pNeoRoot->m_pFileIODialog->AddFilter("*.vtf", "VTF Image", false);
g_pNeoRoot->m_pFileIODialog->DoModal();
}
NeoUI::Pad();

if (pGeneral->iTexIdSpray > 0)
{
vgui::surface()->DrawSetTexture(pGeneral->iTexIdSpray);
vgui::surface()->DrawSetColor(COLOR_WHITE);
vgui::surface()->DrawTexturedRect(
g_uiCtx.dPanel.x + g_uiCtx.iLayoutX,
g_uiCtx.dPanel.y + g_uiCtx.iLayoutY,
g_uiCtx.dPanel.x + g_uiCtx.iLayoutX + 256,
g_uiCtx.dPanel.y + g_uiCtx.iLayoutY + 256);
}
}

void NeoSettings_Keys(NeoSettings *ns)
Expand Down Expand Up @@ -786,6 +825,7 @@ void NeoSettings_Crosshair(NeoSettings *ns)
g_pNeoRoot->m_pFileIODialog->MarkForDeletion();
}
pCrosshair->eFileIOMode = bPresImport ? vgui::FOD_OPEN : vgui::FOD_SAVE;
g_pNeoRoot->m_eFileIOMode = CNeoRoot::FILEIODLGMODE_CROSSHAIR;
g_pNeoRoot->m_pFileIODialog = new vgui::FileOpenDialog(g_pNeoRoot,
bPresImport ? "Import crosshair" : "Export crosshair",
pCrosshair->eFileIOMode);
Expand Down
2 changes: 2 additions & 0 deletions mp/src/game/client/neo/ui/neo_root_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ struct NeoSettings
bool bShowPos;
int iShowFps;
int iDlFilter;

int iTexIdSpray;
};

struct Keys
Expand Down
175 changes: 175 additions & 0 deletions mp/src/game/client/neo/ui/neo_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#include <vgui/ISurface.h>
#include <vgui/IInput.h>
#include <vgui_controls/Controls.h>
#include <filesystem.h>

#include "stb_image.h"

using namespace vgui;

Expand Down Expand Up @@ -1020,4 +1023,176 @@ void OpenURL(const char *szBaseUrl, const char *szPath)
system(syscmd);
}

uint8 *ConvertToVTF(char (*szRetTexPath)[VTF_PATH_MAX], const char *szFullpath)
{
if (!szFullpath)
{
return nullptr;
}

// TODO: See: CViewRender::WriteSaveGameScreenshotOfSize
int width, height, stride;
uint8 *data = stbi_load(szFullpath, &width, &height, &stride, 0);
if (!data || width <= 0 || height <= 0)
{
return nullptr;
}

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

#if 0
const int iSrcEnd = width * height * stride;

for (int srcOffset = 0, dstOffset = 0;
srcOffset < iSrcEnd;
srcOffset += stride, dstOffset += 4)
{
V_memcpy(rgbaData + dstOffset, data + srcOffset, stride);
(rgbaData + dstOffset)[4] = 255;
}
#else
bool bConverted = ImageLoader::ConvertImageFormat(data, IMAGE_FORMAT_RGB888,
rgbaData, IMAGE_FORMAT_RGBA8888,
width, height);
#endif

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

// Crop to 256x256
if (width != 256 || height != 256)
{
uint8 *croppedData = (uint8 *)(calloc(256 * 256, sizeof(uint8) * stride));

#if 1
const int dstYLines = 256 * stride;
const int dstYEnd = dstYLines * 256;
const int dstXOffset = (width < 256) ? (256 - width) : 0;
const int dstYOffset = (height < 256) ? (256 - height) : 0;
const int dstOffsetStart = (dstYOffset * dstYLines) + (dstXOffset * stride);

const int srcYLines = width * stride;
const int srcYEnd = srcYLines * height;
const int srcXOffset = (width > 256) ? ((width / 2) - 128) : 0;
const int srcYOffset = (height > 256) ? ((height / 2) - 128) : 0;
const int srcOffsetStart = (srcYOffset * srcYLines) + (srcXOffset * stride);

const int xTakeMem = ((width > 256) ? 256 : width) * stride;

for (int srcOffset = srcOffsetStart, dstOffset = dstOffsetStart;
(srcOffset < srcYEnd) && (dstOffset < dstYEnd);
srcOffset += srcYLines, dstOffset += dstYLines)
{
V_memcpy(croppedData + dstOffset, data + srcOffset, xTakeMem);
}
#else
ImageLoader::ResampleInfo_t info;
info.m_pSrc = data;
info.m_pDest = croppedData;
info.m_nSrcWidth = width;
info.m_nSrcHeight = height;
info.m_nSrcDepth = 1;
info.m_nDestWidth = 256;
info.m_nDestHeight = 256;
info.m_nDestDepth = 1;
info.m_flSrcGamma = 2.2f;
info.m_flDestGamma = 2.2f;
info.m_flAlphaThreshhold = 1.0f;
info.m_flAlphaHiFreqThreshhold = 1.0f;
info.m_nFlags = ImageLoader::RESAMPLE_NORMALMAP;
bool bResampled = ResampleRGBA8888(info);
#endif

stbi_image_free(data);
data = croppedData;
width = 256;
height = 256;
}

const char *pLastSlash = V_strrchr(szFullpath, '/');
const char *pszBaseName = pLastSlash ? pLastSlash + 1 : szFullpath;
char *pszDot = strchr((char *)(pszBaseName), '.');
if (pszDot)
{
*pszDot = '\0';
}
V_sprintf_safe(*szRetTexPath, "materials/vgui/logos/%s", pszBaseName);
filesystem->CreateDirHierarchy("materials/vgui/logos");
char szFullFilePath[PATH_MAX];

// Create and initialize a 256x256 VTF texture
CUtlBuffer buffer;
bool bWriteResult = false;
IVTFTexture *pVTFTexture = CreateVTFTexture();
const int nFlags = TEXTUREFLAGS_SRGB | TEXTUREFLAGS_ONEBITALPHA; // | TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD;
if (pVTFTexture->Init(256, 256, 1, IMAGE_FORMAT_RGBA8888, nFlags, 1))
{
pVTFTexture->InitLowResImage(16, 16, IMAGE_FORMAT_RGBA8888);

uint8 *pDstData = pVTFTexture->ImageData();
V_memcpy(pDstData, data, width * height * 4);

pVTFTexture->ConvertImageFormat(IMAGE_FORMAT_DEFAULT, false);
pVTFTexture->GenerateMipmaps();
pVTFTexture->ConstructLowResImage();
pVTFTexture->ConvertImageFormat(IMAGE_FORMAT_DXT1, false);

// Allocate output buffer
const int iFileSize = pVTFTexture->FileSize() * 2;
void *pVTF = malloc(iFileSize);
buffer.SetExternalBuffer(pVTF, iFileSize, 0);

// Serialize to the buffer
bWriteResult = pVTFTexture->Serialize(buffer);

// Free the VTF texture
DestroyVTFTexture(pVTFTexture);
}
else
{
bWriteResult = false;
}

//stbi_image_free(data);

if (!bWriteResult)
{
return nullptr;
}

V_sprintf_safe(szFullFilePath, "%s.vtf", *szRetTexPath);
if (!filesystem->WriteFile(szFullFilePath, nullptr, buffer))
{
// TODO ERROR
}

// TODO: Windows use back-slash
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);

V_sprintf_safe(szFullFilePath, "%s.vmt", *szRetTexPath);
if (!filesystem->WriteFile(szFullFilePath, nullptr, bufVmt))
{
// TODO ERROR
}

return data;
}

} // namespace NeoUI
3 changes: 3 additions & 0 deletions mp/src/game/client/neo/ui/neo_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,7 @@ void SliderU8(const wchar_t *wszLeftLabel, uint8 *ucValue, const uint8 iMin, con
void TextEdit(const wchar_t *wszLeftLabel, wchar_t *wszText, const int iMaxSize);
bool Bind(const ButtonCode_t eCode);
void OpenURL(const char *szBaseUrl, const char *szPath);

#define VTF_PATH_MAX (512)
uint8 *ConvertToVTF(char (*szRetTexPath)[VTF_PATH_MAX], const char *szFullpath);
}
3 changes: 3 additions & 0 deletions mp/src/game/client/neo/vendor_impl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

Loading

0 comments on commit fbaab6c

Please sign in to comment.