source-engine/game/client/imgui/imgui_impl_source.cpp
2024-12-30 18:08:40 +05:00

278 lines
8.8 KiB
C++

/*********************************************************************************
* MIT License
*
* Copyright (c) 2023 Strata Source Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*********************************************************************************/
#include "imgui_impl_source.h"
#include "KeyValues.h"
#include "materialsystem/imesh.h"
#include "materialsystem/itexture.h"
#include "imgui/imgui.h"
#include "imgui/imgui_internal.h"
#include "pixelwriter.h"
#include "filesystem.h"
#include "vgui/ISystem.h"
#include "vgui_controls/Controls.h"
#include "tier0/memdbgon.h"
static IMaterial *g_pFontMat = nullptr;
class CDearImGuiFontTextureRegenerator : public ITextureRegenerator
{
public:
CDearImGuiFontTextureRegenerator() = default;
// Inherited from ITextureRegenerator
void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect ) override
{
ImGuiIO &io = ImGui::GetIO();
unsigned char *pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32( &pixels, &width, &height );
Assert( pVTFTexture->Width() == width );
Assert( pVTFTexture->Height() == height );
// if we ever use freetype for font loading, this should do format conversion instead
memcpy( pVTFTexture->ImageData(), pixels, 4ULL * width * height );
}
void Release() override
{
delete this;
}
};
void ImGui_ImplSource_SetupRenderState( IMatRenderContext *ctx, ImDrawData *draw_data )
{
// Apply imgui's display dimensions
ctx->Viewport( draw_data->DisplayPos.x, draw_data->DisplayPos.y, draw_data->DisplaySize.x, draw_data->DisplaySize.y );
// Setup orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
ctx->MatrixMode( MATERIAL_PROJECTION );
ctx->LoadIdentity();
ctx->Scale( 1, -1, 1 );
float L = draw_data->DisplayPos.x + 0.5f;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f;
float T = draw_data->DisplayPos.y + 0.5f;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y + 0.5f;
ctx->Ortho( L, T, R, B, 0.f, 1.f );
ctx->MatrixMode( MATERIAL_VIEW );
ctx->LoadIdentity();
}
void ImGui_ImplSource_RenderDrawData( ImDrawData *draw_data )
{
// Avoid rendering when minimized
if ( draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f )
return;
CMatRenderContextPtr ctx( materials );
ctx->MatrixMode( MATERIAL_VIEW );
ctx->PushMatrix();
ctx->MatrixMode( MATERIAL_PROJECTION );
ctx->PushMatrix();
ImGui_ImplSource_SetupRenderState( ctx, draw_data );
// Render command lists
ImVec2 clip_off = draw_data->DisplayPos;
for ( int n = 0; n < draw_data->CmdListsCount; n++ )
{
const ImDrawList *cmd_list = draw_data->CmdLists[n];
const ImDrawIdx *idx_buffer = cmd_list->IdxBuffer.Data;
// Draw the mesh
for ( int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++ )
{
const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[cmd_i];
if ( pcmd->UserCallback != nullptr )
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if ( pcmd->UserCallback == ImDrawCallback_ResetRenderState )
ImGui_ImplSource_SetupRenderState( ctx, draw_data );
else
pcmd->UserCallback( cmd_list, pcmd );
}
else
{
if ( pcmd->GetTexID() )
{
Vector2D clipmin = { pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y };
Vector2D clipmax = { pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y };
// Avoid rendering completely clipped draws
if ( clipmax.x <= clipmin.x || clipmax.y <= clipmin.y )
continue;
ctx->SetScissorRect( clipmin.x, clipmin.y, clipmax.x, clipmax.y, true );
IMesh *mesh = ctx->GetDynamicMesh( false, nullptr, nullptr, static_cast<IMaterial*>( pcmd->GetTexID() ) );
CMeshBuilder mb;
mb.Begin( mesh, MATERIAL_TRIANGLES, cmd_list->VtxBuffer.Size, pcmd->ElemCount );
const ImDrawVert *vtx_src = cmd_list->VtxBuffer.Data;
for ( int i = 0; i < cmd_list->VtxBuffer.Size; i++ )
{
mb.Position3f( Vector2DExpand( vtx_src->pos ), 0 );
mb.Color4ubv( reinterpret_cast<const unsigned char*>( &vtx_src->col ) );
mb.TexCoord2fv( 0, &vtx_src->uv.x );
mb.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 1>();
vtx_src++;
}
static_cast<CIndexBuilder &>( mb ).FastIndexList( idx_buffer + pcmd->IdxOffset, 0, pcmd->ElemCount );
mb.End( false, true );
ctx->SetScissorRect( clipmin.x, clipmin.y, clipmax.x, clipmax.y, false );
}
}
}
}
ctx->MatrixMode( MATERIAL_PROJECTION );
ctx->PopMatrix();
ctx->MatrixMode( MATERIAL_VIEW );
ctx->PopMatrix();
}
bool ImGui_ImplSource_Init()
{
// Setup backend capabilities flags
ImGuiIO &io = ImGui::GetIO();
ImGuiPlatformIO &platform_io = ImGui::GetPlatformIO();
io.BackendPlatformName = "source";
io.BackendRendererName = "imgui_impl_source";
io.BackendFlags = ImGuiBackendFlags_None;
platform_io.Platform_SetClipboardTextFn = []( void *, const char *c )
{
vgui::system()->SetClipboardText( c, V_strlen( c ) );
};
platform_io.Platform_GetClipboardTextFn = []( void *ctx ) -> const char *
{
auto &g = *static_cast<ImGuiContext *>( ctx );
g.ClipboardHandlerData.clear();
auto len = vgui::system()->GetClipboardTextCount();
if ( !len )
return nullptr;
g.ClipboardHandlerData.resize( len );
vgui::system()->GetClipboardText( 0, g.ClipboardHandlerData.Data, g.ClipboardHandlerData.Size );
return g.ClipboardHandlerData.Data;
};
ImGui_ImplSource_CreateDeviceObjects();
return true;
}
void ImGui_ImplSource_Shutdown()
{
ImGui_ImplSource_InvalidateDeviceObjects();
}
static bool ImGui_ImplSource_CreateFontsTexture()
{
if ( g_pFontMat )
return true;
// Build texture atlas
ImGuiIO &io = ImGui::GetIO();
int width, height;
unsigned char* pixels;
io.Fonts->GetTexDataAsRGBA32( &pixels, &width, &height );
// Create a material for the texture
ITexture *fonttex = g_pMaterialSystem->CreateProceduralTexture( "imgui_font", TEXTURE_GROUP_OTHER, width, height, IMAGE_FORMAT_RGBA8888, TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_POINTSAMPLE | TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_SINGLECOPY | TEXTUREFLAGS_NOLOD );
fonttex->SetTextureRegenerator( new CDearImGuiFontTextureRegenerator );
fonttex->Download();
KeyValues *vmt = new KeyValues( "UnlitGeneric" );
vmt->SetString( "$basetexture", "imgui_font" );
vmt->SetInt( "$nocull", 1 );
vmt->SetInt( "$vertexcolor", 1 );
vmt->SetInt( "$vertexalpha", 1 );
vmt->SetInt( "$translucent", 1 );
g_pFontMat = materials->CreateMaterial( "imgui_font_mat", vmt );
g_pFontMat->AddRef();
// Store our identifier
io.Fonts->SetTexID( g_pFontMat );
return true;
}
bool ImGui_ImplSource_CreateDeviceObjects()
{
return ImGui_ImplSource_CreateFontsTexture();
}
void ImGui_ImplSource_InvalidateDeviceObjects()
{
if ( g_pFontMat )
{
g_pFontMat->DecrementReferenceCount();
g_pFontMat = nullptr;
}
}
// The following functions are declared in imconfig_source.h and must not be renamed
ImFileHandle ImFileOpen( const char *filename, const char *mode )
{
Assert( g_pFullFileSystem );
return g_pFullFileSystem->Open( filename, mode );
}
bool ImFileClose( ImFileHandle f )
{
if ( f == nullptr )
return false;
Assert( g_pFullFileSystem );
g_pFullFileSystem->Close( f );
return true;
}
uint64 ImFileGetSize( ImFileHandle f )
{
Assert( g_pFullFileSystem && f );
return g_pFullFileSystem->Size( f );
}
uint64 ImFileRead( void *data, uint64 sz, uint64 count, ImFileHandle f )
{
Assert( g_pFullFileSystem && f );
return g_pFullFileSystem->Read( data, sz * count, f );
}
uint64 ImFileWrite( const void *data, uint64 sz, uint64 count, ImFileHandle f )
{
Assert( g_pFullFileSystem && f );
return g_pFullFileSystem->Write( data, sz * count, f );
}