[C++ SAMPFUNCS] ImGui Overlay

waitback

New member
Joined
Dec 14, 2021
Messages
1
Reaction score
2
Location
Slovakia
Hi there!

I've started working on a new project and in the process made a class to handle my ImGui windows so I thought I'll share it with you as this forum helped me a lot back when I was starting. Hopefully, this thread will do the same for someone else. The class is fairly easy to use and much cleaner than hard-writing everything into the present callback. It's implemented with SAMPFUNCS, but you can easily adapt it for any SDK. I've also put there an example on how to use this (main.cpp).

main.cpp
C++:
#include "SAMPFUNCS_API.h"
#include "game_api.h"
#include "ImGuiOverlay.h"

SAMPFUNCS* SF = new SAMPFUNCS();
ImGuiOverlay* IGO = nullptr;

void CALLBACK mainloop()
{
    static bool initialized = false;
    if (!initialized)
    {
        if (GAME == nullptr)
            return;
        if (GAME->GetSystemState() != eSystemState::GS_PLAYING_GAME)
            return;
        if (!SF->getSAMP()->IsInitialized())
            return;

        initialized = true;

        //-- USAGE DEMONSTRATION:
        IGO = new ImGuiOverlay();
        
        IGO->add_window("ImGui Overlay", [](){
            ImGui::Text("Hello ImGui!");
        });

        IGO->m_Show[0] = true;

        auto render_content = []() {
            ImGui::Text("Hello another window!");
        };

        IGO->add_window("Another window", render_content, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoInputs);
        IGO->m_Show[1] = true;
    }
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReasonForCall, LPVOID lpReserved)
{
    if (dwReasonForCall == DLL_PROCESS_ATTACH)
    {
        SF->initPlugin(mainloop, hModule);
    }
    return TRUE;
}


ImGuiOverlay.h
C++:
#pragma once

#include <Windows.h>
#include <functional>
#include <string>

#include "imgui.h"

class ImGuiOverlay
{
    static bool CALLBACK WndProcHandler(HWND hwd, UINT msg, WPARAM wParam, LPARAM lParam);
    static bool CALLBACK Present(CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion);

    static inline std::vector<std::function<void()>> m_RenderContent;
    static inline std::vector<ImGuiWindowFlags> m_Flags;
public:
    ImGuiOverlay();

    static size_t add_window(std::string title, std::function<void()> render_content, ImGuiWindowFlags flags = 0);

    static inline std::vector<std::string> m_Title;
    static inline std::vector<bool> m_Show;
};

extern ImGuiOverlay* IGO;


ImGuiOverlay.cpp
C++:
#include "ImGuiOverlay.h"

#include "SAMPFUNCS_API.h"
#include "game_api.h"

#include "imgui_impl_win32.h"
#include "imgui_impl_dx9.h"
#include "imgui_internal.h"

extern SAMPFUNCS* SF;
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

bool CALLBACK ImGuiOverlay::WndProcHandler(HWND hwd, UINT msg, WPARAM wParam, LPARAM lParam) {
    ImGui_ImplWin32_WndProcHandler(hwd, msg, wParam, lParam);
    return true;
}

bool CALLBACK ImGuiOverlay::Present(CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion) {
    if (SF->getRender()->CanDraw())
    {
        ImGuiIO& io = ImGui::GetIO();
        ImGui_ImplDX9_NewFrame();
        ImGui_ImplWin32_NewFrame();
        ImGui::NewFrame();

        for (size_t i = 0; i < m_Show.size(); i++) {
            if (m_Show[i]) {
                ImGui::Begin(m_Title[i].c_str(), 0, m_Flags[i]);
                m_RenderContent[i]();
                ImGui::End();
            }
        }

        ImGui::EndFrame();
        if (SUCCEEDED(SF->getRender()->BeginRender()))
        {
            ImGui::Render();
            ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
            SF->getRender()->EndRender();
        }
    }
    return true;
}

size_t ImGuiOverlay::add_window(std::string title, std::function<void()> render_content, ImGuiWindowFlags flags) {
    m_Show.push_back(false);
    m_Title.push_back(title);
    m_RenderContent.push_back(render_content);
    m_Flags.push_back(flags);

    return m_Show.size() - 1;
}

ImGuiOverlay::ImGuiOverlay() {
    auto pd3dDevice = SF->getRender()->getD3DDevice();

    ImGui::CreateContext();
    ImGui_ImplWin32_Init(FindWindowA(NULL, "GTA:SA:MP"));
    ImGui_ImplDX9_Init(pd3dDevice);

    SF->getRender()->registerD3DCallback(D3DMETHOD_PRESENT, Present);
    SF->getGame()->registerWndProcCallback(SFGame::MEDIUM_CB_PRIORITY, WndProcHandler);
}
 
Top