Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImGui::ImagePolygonButton #6713

Open
joramas opened this issue Aug 13, 2023 · 1 comment
Open

ImGui::ImagePolygonButton #6713

joramas opened this issue Aug 13, 2023 · 1 comment

Comments

@joramas
Copy link

joramas commented Aug 13, 2023

Discussed in https://github.com/ocornut/imgui/discussions/6712

Originally posted by joramas August 13, 2023
Hi Omar!
I was using the normal UVRect [ImVec2(0.0f, 0.0f) to ImVec2(1.0f, 1.0f)] to render an image to the ImGui::ImageButton. I changed my sprite sheets packing algorithm to a polygon for tight packing, and now, instead of quads, I got an array of vertices and indices for each image. I've implemented the following functions, so anyone might find it helpful:

// ===========================================
ImDrawList::PrimImageVerticesAndIndices (in imgui_draw.cpp)
ImDrawList::AddImagePolygon                   (in imgui_draw.cpp)
ImGui::ImagePolygonButton                       (in imgui_widgets.cpp)
// ===========================================

NOTE: I use TexturePacker for sprite_sheet creation, so I had to adjust the alignment in the image button quad, but aside from that, anyone should adapt it to its necessities as wanted. At the end, there is an image of how it looks.

void ImDrawList::PrimImageVerticesAndIndices(ImVec2 CenterP, ImVec2 WidgetSize, u16 VertexCount, vec2* Ps, vec2* UVs, u16 IndexCount, u16* Indices, vec2 MinOffset)
{
    ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;
    
    for(u16 IndexIndex = 0;
        IndexIndex < IndexCount;
        ++IndexIndex)
    {
        _IdxWritePtr[IndexIndex] = idx + Indices[IndexIndex];
    }
    
    ImU32 WhiteColor = 0xFFFFFFFF; // NOTE(jl): Image untinted
    ImVec2 HalfWidgetSize = ImVec2(0.5f*WidgetSize.x, 0.5f*WidgetSize.y);
    
    for(u16 VertexIndex = 0;
        VertexIndex < VertexCount;
        ++VertexIndex)
    {
        vec2 P = Ps[VertexIndex] + MinOffset; // NOTE(jl): Still in the 0 to 1 range
        P.y = 1.0f - P.y;         // NOTE(jl): Flip it
        
        // NOTE(jl): Back to pixels
        P.x *= WidgetSize.x;
        P.y *= WidgetSize.y;
        
        ImVec2 VertexP = CenterP + ImVec2(P.x, P.y) - ImVec2(HalfWidgetSize.x, HalfWidgetSize.y); 
        _VtxWritePtr[VertexIndex].pos = ImVec2(VertexP.x, VertexP.y);
        
        vec2 UV = UVs[VertexIndex];
        _VtxWritePtr[VertexIndex].uv = ImVec2(UV.x, UV.y);
        
        _VtxWritePtr[VertexIndex].col = WhiteColor;
    }
    
    _VtxWritePtr += VertexCount;
    _VtxCurrentIdx += VertexCount;
    _IdxWritePtr += IndexCount;
}

void ImDrawList::AddImagePolygon(ImTextureID user_texture_id, ImVec2 CenterP, ImVec2 ButtonSize, u16 VertexCount, vec2* Ps, vec2* UVs, u16 IndexCount, u16* Indices, vec2 MinOffset)
{
    const bool push_texture_id = user_texture_id != _CmdHeader.TextureId;
    if (push_texture_id)
        PushTextureID(user_texture_id);
    
    PrimReserve(IndexCount, VertexCount);
    PrimImageVerticesAndIndices(CenterP, ButtonSize, VertexCount, Ps, UVs, IndexCount, Indices, MinOffset);
    
    if (push_texture_id)
        PopTextureID();
}

bool ImGui::ImagePolygonButton(ImTextureID user_texture_id, const ImVec2& button_size, u16 VertexCount, vec2* Ps, vec2* UVs, u16 IndexCount, u16* Indices, vec2 MinOffset, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
{
    ImGuiContext& g = *GImGui;
    ImGuiWindow* window = g.CurrentWindow;
    if (window->SkipItems)
        return false;
    
    // Default to using texture ID as ID. User can still push string/integer prefixes.
    PushID((void*)(intptr_t)user_texture_id);
    const ImGuiID id = window->GetID("#image");
    PopID();
    
    const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : g.Style.FramePadding;
    
    // NOTE(jl): ImageButtonEx inlined
    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + button_size + padding * 2);
    ItemSize(bb);
    if (!ItemAdd(bb, id))
        return false;
    
    bool hovered, held;
    bool pressed = ButtonBehavior(bb, id, &hovered, &held);
    
    // Render
    const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
    RenderNavHighlight(bb, id);
    RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding));
    if (bg_col.w > 0.0f)
    {
        window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
    }
    
    float center_x = bb.Min.x + (bb.Max.x - bb.Min.x)*0.5f;
    float center_y = bb.Min.y + (bb.Max.y - bb.Min.y)*0.5f;
    ImVec2 CenterP(center_x, center_y);
    window->DrawList->AddImagePolygon(user_texture_id, CenterP, button_size, VertexCount, Ps, UVs, IndexCount, Indices, MinOffset);
    
    return pressed;
}

image_polygon_buttons

@ocornut
Copy link
Owner

ocornut commented Aug 14, 2023

Thank you for posting this.

Linking to #845 #2942 #4722 for visibility of similar techniques.

Please note that you shouldn't need to modify imgui files to implement this:

  • the ImGui::ImagePolygonButton() function can be declared and implemented in your own source file.
  • ImDrawList being a class instead of a namespace you cannot do it the same way, but you can call your function e.g. void ImDrawList_AddImagePolygon(ImDrawList* draw_list, ....) and it'll work.

Nowadays I regret making ImDrawList use methods instead of loose functions, as the later makes it slightly easier to extend. maybe something we will reconsider in 2.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants