I am reading Dear Imgui library and I learn by changing it a bit. I am stuck with window closing. My window theoretically is closed, widgets do not work, but graphics still render.
Every gui window - Window
- has a class named RenderData. It is a class which has variables, objects like vector array of vertices, vector array of elements, vector array of DrawCmd (DrawCmd is a struct which has data per draw call - One DrawCmd = one opengl draw call). Window class also has methods that are responsible for adding data in proper way that user gave us. For example FillRect(Vector2 pos, Vector2 size, Color color);
adds to the vector of vertices, data in way that after giving it to the vbo and call draw call it can render rectangle.
It also has the method that clears resources
void RenderData::Free()
{
vtxData.clear();
drawCmd.clear();
vtxBuffer.clear();
elemBuffer.clear();
currVtxIndex = 0;
}
The GUIRenderer
class is responsible for providing this data to the buffer
It has vector of pointers on RenderData, vbo, ebo and vao.
Methods are:
- Create()
- creates vbo, ebo, vao and shaders
- AddRenderData(RenderData* render)'
- it push_back render
which is in our gui window
- RenderPresent();
- sets up camera, blends, adds vbo attrtibutes, goes through renderData.size() and adds data from current renderData to the vbo and ebo, goes through drawCmd and call draw call. It looks like this:
void GUIRenderer::RenderPresent()
{
windowSize = window.getWindowSize();
setRenderViewport(0, 0, static_cast<uint>(windowSize.w), static_cast<uint>(windowSize.h));
camera.Projection(0.0f, windowSize.w, windowSize.h, 0.0f);
camera.Update();
shader->UseProgram();
shader->UniformMatrix4fv(0, camera.getCameraMatrix());
EnableBlendFunc(Blend_Source_Alpha, Blend_One_Minus_Source_Alpha);
vao = vao->Create();
BindBuffer();
//These are my functions that adds and enables vbo attributes
AddAttribute(0, 3, FLOAT, alse, sizeof(Vertex), (const void*)offsetof(Vertex, position));
AddAttribute(1, 4, UNSIGNED_BYTE, true, sizeof(Vertex), (const void*)offsetof(Vertex, color));
for (uint i = 0; i < renderData.size(); ++i) {
const RenderData* currentRender = renderData[i];
const uint* elemBufferOffset = nullptr;
vbo->BindBuffer();
vbo->AddData(currentRender->vtxBuffer.size() * sizeof(Vertex), currentRender->vtxBuffer.data());
ebo->BindBuffer();
ebo->AddData(currentRender->elemBuffer.size() * sizeof(uint), currentRender->elemBuffer.data());
for (uint j = 0; j < currentRender->drawCmd.size(); ++j) {
const DrawCmd drawCmd = currentRender->drawCmd[j];
if(drawCmd.shader) drawCmd .shader->UseProgram();
DrawElements(drawCall.elemCount, elemBufferOffset);
if(drawCmd.shader) drawCmd .shader->UnuseProgram();
elemBufferOffset += drawCmd .elemCount;
}
}
DisableBlendFunc();
}
klasa GUI posiada metodę, która obsługuje tą klasę, to znaczy dodaje renderData, które są w oknach do tablicy, która jest w GUIRenderer. Ta metoda nazywa się GUI::RenderPresent()
GUI class has a method that adds renderDatas which are in our gui windows to vector of render data pointers in GUIRenderer. It is called GUI::RenderPresent()
void GUI::RenderPresent()
{
for(const auto& window : context->window) {
guiRenderer.AddRenderData(window->renderer);
}
guiRenderer.RenderPresent();
}
Problem jak już na początku wspomniałem polega na tym, że po "zamknięciu" okna grafika tego okna nadal jest renderowana
The problem as I mentioned at the beginning is that, after "closing" the window, graphics of this window still renders If we go deeper in to the Dear ImGui we can see that context Class has 3 the most importants objects
ImVector<ImGuiWindow*> Windows;
ImVector<ImGuiWindow*> CurrentWindowStack;
ImGuiStorage WindowsById;
CurrentWindowStack
the size of this is restarted to 0 every iteration through main loop, it is push_back in Begin(...);
and pop in End()
WindowsById
it is a some kind of storage for windows based on name/id(hash)
if we want to create a new window (so it is not in WindowsById
yet) we have to create it and push it back to Windows
and also to the storage(WindowsById
). Before we do this, we have to check if WindowById
does not have our window. If it has, we do not need to create it, we just need to use it again.
moja metoda wygląda bardzo podobnie z pewnymi zmianami (dla uproszczenia będzie wyglądać prawie dokładnie identycznie)
my method is very simillar but it has some changes (for simplicity it will look the same, without bullshits)
void GUI::New()
{
currentWindowStack.resize(0);
}
void GUI::Begin(const std::string& name)
{
Window* window = context->FindWindowByName(name);
if (!window) {
// if there is no window with this name/id(hash) - create it and at to the storage (`WindowById`)
window = context->CreateGUIWindow(name);
}
context->currentWindowStack.push_back(window);
context->currentWindow = window;
window->active = true;
RenderData* winRenderer = window->renderer;
winRenderer->Free();
//it adds default values to the drawCmd vector array, it just adds one draw call
winRenderer->AddDrawCall();
uint backgroundColor = 0x80000000;
winRenderer->FillRect(Rect(window->position, window->size), backgroundColor);
}
void GUI::End()
{
Window* window = context->currentWindow;
context->currentWindowStack.pop_back();
context->currentWindow = context->currentWindowStack.empty() ? nullptr : context->currentWindowStack.back();
}
int main()
{
//... sdl window
//... opengl context
GUI gui;
bool openWindow = true;
while(!quit) {
bool pressed = false;
if(keyDown(KEY_b) && !pressed) {
openWindow = !open;
pressed = true;
}
gui.New();
if(openWindow ) {
gui.Begin("Test");
gui.Button("TestButton");
gui.End();
}
gui.RenderPresent();
}
return 0;
}
when openWindow
is true, button should work and also everything should render - and it is o
but when openWindow
is false, button should not work (and it does not, so it is ok) and nothing should be rendered - but it is rendered - window background, window's widgets.
Why the graphics is rendered even if it should not? Why does it work in Dear imGui and not in my application? What should I change to fix it?
Begin
sets widnow->active
to be true, but you never reset it to false and never use it.
You have to read closely.
Dear Imgui's NewFrame()
method sets all windows' active to false and Render
checks if window is active, and if it is, it adds to the vector of drawlist.
So, New()
should look like this:
void GUI::New()
{
currentWindowStack.resize(0);
for(const auto& window : context.window) {
window->active = false;
}
}
and RenderPresent()
should look like this:
void GUI::RenderPresent()
{
for(const auto& window : context->window) {
if(window->active) {
guiRenderer.AddRenderData(window->renderer);
}
}
guiRenderer.RenderPresent();
}
User contributions licensed under CC BY-SA 3.0