Jump to content

Startup Loading Window Library


Recommended Posts

  • Premium

If anyone had bought the VegaS' client optimization, they know that the loading of the motion files, depending on the loading vector's size, can take a bit of time, especially if you have 3642 files to load (totally random number 😗)

N.B. I won't display even a single byte of the VegaS system's code. I would be happy to assist you if you are a customer of his and have purchased his client optimization (I will confirm with him, but I won't offer support for leaked stuff).

 

Alrright, so, at first I thought to simply create a new "HWND" and work on that one, but, since it would run on the same instance as the client, it wouldn't be just as smooth. So just initialize a different instance and load the window on that instance? Sure, but, also no, too much of an hassle.

A simpler solution would be to start a new app with the client who would receive the progress of the loading and show a progress bar or something, just to inform the player that the client is actually opening.

Technically the project can be used for just anything else, like a separate menu integrated into the client or something, although the framework I used is not intended to fully work with DX8, so there might be some glitches considering what you wanna do with it.

 

Now, let's dive into it, because it's not just a plug-and-play thing. Let's start by saying the app is made using a framework called ImGui:

This is the hidden content, please

What's need to be done?

Let's start with the name and the size (in this case 1280x800) of the main Application. You can find it on LoadingWindowHandler.cpp:

m_HWND = ::CreateWindow(m_WCEX.lpszClassName, _T("Dear ImGui DirectX8 Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, m_WCEX.hInstance, NULL);

If you've had any experience on Win32 APIs, do not do this:

SetWindowLong(hWnd, GWL_STYLE, 0);

It breaks the text. If there is a fix, I haven't found one, yet.

 

Second thing, ImGui stores the position and the size of the child windows inside a .ini file, called imgui.ini

For example, in my case, I have:

[Window][MainWindow]
Pos=98,165
Size=1266,750
Collapsed=0

(declared in imgui.cpp line 4556)

and then:

[Window][TopWindow]
Pos=0,0
Size=1265,762
Collapsed=0

Which is the window where we're gonna render the background, text and progressbar. Obviously, depending on your image size, these settings should be changed. We can also edit the size of the "TopWindow" actually running the application and resize it there.

To do that, we can just de-comment:

#define TEST_PROGRESS_BAR

in LoadingWindowHandler.h

After that, we need to change the Configuration Type:

spacer.png

 

then we can open the application and move the window:

spacer.png

Or resize it:

spacer.png

Once closed, the application will have changed the settings in the ini file:

[Window][MainWindow]
Pos=98,165
Size=1266,750
Collapsed=0

[Window][TopWindow]
Pos=-78,-120
Size=967,524
Collapsed=0

This way you can resize/move the window according to your background image.

It is also possible to change the name of the ini file in imgui.cpp

IniFilename = "imgui.ini";

To change the background image you want to edit the directory and name here:

std::string filepath = cwd.string() + "\\movie\\splash.bmp";

From Microsoft docs the formats supported are: .bmp, .dds, .dib, .hdr, .jpg, .pfm, .png, .ppm, and .tga.

 

How are the positions of the ProgressBar, Text and background handled? Well, let's start from the background:

        ImGui::SetCursorPosY(0);
        auto size = ImGui::GetContentRegionAvail();
        ImGui::Image((void*)SplashImage, ImVec2(size.x, size.y));

ImGui::SetCursorPosY(0); is like saying "go to the Y coord 0", then the rest is to "resize" the background image to the Area of the "TopWindow".

 

The style of the ProgressBar is in LoadingWindowDesigner.cpp:

#include "LoadingWindowDesigner.h"
#include <ImGui/imgui.h>


namespace LoadingWindowDesigner
{
    void RenderUI(float progress, std::string loading_text)
    {
        auto window_size = ImGui::GetWindowSize();

        ImGui::SetCursorPosY(window_size.y - 30);
        ImGui::Text(loading_text.c_str());
        ImGui::ProgressBar(progress, ImVec2(window_size.x - 15.0f, 5.0f), "");
    }
}

Again, with SetCursorPosY we are saying to go specific Y coords, in this case at the bottom of the window minus 30 pixels, that's where it's gonna write the text, then we draw the ProgressBar.

The numbers inside ImVec2 are the dimensions of the ProgressBar. There's a third parameter where you could set an overlay text to the ProgressBar. If you wanna change the Font, well:

This is the hidden content, please

If you wanna change the color of the moving bar, you can go to imgui_widgets.cpp and change this line:

RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_ButtonHovered), 0.0f, fraction, style.FrameRounding);

The background instead is on this line:

RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);

The colors specified are the ones with the index: ImGuiCol_ButtonHovered and ImGuiCol_FrameBg.

All this stuff can be seen by downloading the framework from the github repository and launching a project from the examples folder. The ImGui Demo window will show every possible option that you can then check on the source code (even the colors, that I found playing around with the demo window).

One thing you'd notice from the window, is that there are some weird borders, and we can delete those (in my example, they actually fit pretty well with the background, so I left them. To do that just de-comment the other define:

//#define LOADING_WINDOW_NOBORDERS

P.S. There might be a few pixel modification on the ini file to do afterwards on the "TopWindow", noticeable when opening the client, because of the removal of the borders (if you did). Usually the correction is around 8px, which is the default padding of the framework, if I am not mistaken. You could also adjust the padding).

 

 

So, we chose a background image, set the window size accordingly, positioned the text and rendered the progressbar as we wanted. And for now, we are done with the library (remember to change the configuration type to .lib and comment:

#define TEST_PROGRESS_BAR

Now compile the library both in Debug and Release mode, and add them to the lib folder of the client. After that open LinkerLibraries.h and add:

#ifdef _DEBUG
#pragma comment(lib, "LoadingWindowLib_d.lib")
#else
#pragma comment(lib, "LoadingWindowLib.lib")
#endif

then in StdAfx.h we add:

#include "LoadingWindowMain.h"

Then add on your include folder:

  • LoadingWindowDesigner.h
  • LoadingWindowHandler.h
  • LoadingWindowMain.h

You also need to add the ImGui folder (only the header files):

spacer.png

 

Now if you have linking/compile errors, that's because your DirectX folder or your include directory, on the Project Settings, are different. To fix it you need to change the path in "Additional Include Properties" in the lib project so that your #include <directxheader> directive is the same as in the client.

After confirming that everything works, how to use it:

	auto LoadingWindow = std::thread(LoadingWindow::InitializeLibrary);
	LoadingWindow.detach();
	CPythonApplication *app = new CPythonApplication();
	app->Initialize(hInstance);

This will start a new thread running the library.

How to change the progress percentage and the text? Let's say you have a number of executions:

	
    static auto EXECUTION_TIMES = 100000;
    float progress_adder = (1.0f - progress)/static_cast<float>(EXECUTION_TIMES);

the "number" we are gonna add to the progress bar is gonna be his total (1.0f) minus the actual progress in that moment (if 0, you could just do 1.0f/static_cast<float>(EXECUTION_TIMES). Then on the loop of your loading stuff function you can change the text before the loop, or inside (if you wanna show something for every execution) and the actual progress:

LoadingWindow::UpdateProgressText("Executing tasks...");
for (auto i = 0; i < EXECUTION_TIMES; ++i)
{
	progress += progress_adder;
	// call a function here
	LoadingWindow::UpdateProgress(progress);
}

Always remember that the progress is a float and the "percentage" goes from 0.0f to 1.0f

Now, in theory, the library should close itself when it gets to 1.0:

        if (m_Progress >= 1.0f)
            m_Done = true;

but, at least in my case, it's also possible that it ends with a number such as 0.9999628 or something like that, so we can just call the CleanUpLibrary method to just close it when we want it.

And that's it.

 

Result: https://metin2.download/video/ZVh2zEqrn4hPd86qjkGv3bgk7WLHE4jv/.mp4

 

N.B. Despite working, if you intend to use this framework for other stuff, like some external menus to open from the client or w/e you have in mind, the DX8 version is super glitchy when there are two windows open:

This is the hidden content, please
and if you think you can use a different DirectX version, I am sorry to disappoint you:

spacer.png

As you can see, when trying to load the texture, it's calling the "AddRef()" method:

bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
{
    ImGuiIO& io = ImGui::GetIO();
    IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");

    // Setup backend capabilities flags
    ImGui_ImplDX9_Data* bd = IM_NEW(ImGui_ImplDX9_Data)();
    io.BackendRendererUserData = (void*)bd;
    io.BackendRendererName = "imgui_impl_dx9";
    io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.

    bd->pd3dDevice = device;
    bd->pd3dDevice->AddRef(); //<-----------

    return true;
}

but, the client calls the DX8 APIs, not DX9, ergo no DirectX different than 8.1

Can you use a different framework? Sure, you may always use a similar approach with another similar GUI App, like the CefWebBrowser, but on a different thread, because the webbrowser runs on the same instance of the client:

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
#ifdef _DEBUG
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_CRT_DF | _CRTDBG_LEAK_CHECK_DF );
	//_CrtSetBreakAlloc( 110247 ); 
#endif

	LocaleService_LoadConfig("locale.cfg");
	SetDefaultCodePage(LocaleService_GetCodePage());

	CefWebBrowser_Startup(hInstance); //<-------------

 

 

Credits:

  • ocornuti, the owner of ImGui's repository
  • KsaNL for providing a base for ImGui on DX8
  • Ikarus for dealing with my mental illness during this mini-project

 

Project Download:

This is the hidden content, please
 (or
This is the hidden content, please
)

Edited by Metin2 Dev
Core X - External 2 Internal
  • Metin2 Dev 92
  • kekw 2
  • Eyes 3
  • Not Good 1
  • Think 1
  • Scream 2
  • Good 10
  • Love 1
  • Love 29
Link to comment
Share on other sites

  • Forum Moderator

Cartoon Yes GIF by SpongeBob SquarePants

Edited by Metin2 Dev
Core X - External 2 Internal
  • Metin2 Dev 2
Link to comment
Share on other sites

  • Premium

Updated project:

  • Now it opens at the center of the screen
  • Set some defines for a window flag, open at the center and the testing of the application
  • Better testing of the progress bar (with define, now only the change of the configuration type is needed)
  • Changed optimization settings (it fucks with the linking, didn't notice since VisualStudio sets this option by default)
Link to comment
Share on other sites

  • 3 weeks later...

Announcements



×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.