How to Integrate Custom Math Types like glm::vec2 with Dear ImGui

You can integrate glm::vec2 and other custom math types with Dear ImGui by providing conversion operators or wrapper functions that translate your vector types to ImGui's native ImVec2 struct, enabling seamless use without modifying the library source.

The Dear ImGui library (ocornut/imgui) uses its own lightweight ImVec2 structure for 2D coordinates, sizes, and layout calculations. When your application relies on GLM or another mathematics library, manually converting between vector types at every API call creates friction. Fortunately, the integration requires only zero-cost conversion patterns that live entirely in your codebase, leaving imgui.h and the core implementation untouched.

Understanding ImVec2 and API Conventions

ImGui's public API in imgui.h defines ImVec2 as a plain-old-data struct containing two public float members:

struct ImVec2 { float x, y; ImVec2() : x(0), y(0) {} ImVec2(float _x, float _y) : x(_x), _y(_y) {} };

API functions consume this type in two distinct patterns according to the source implementation in imgui.h:

  • By value or const reference for layout operations (e.g., SetCursorPos(const ImVec2& pos), SetNextWindowPos(const ImVec2& pos))
  • By pointer to float array for multi-component widgets (e.g., SliderFloat2(const char* label, float v[2], float v_min, float v_max))

Because ImVec2 contains no private members, virtual functions, or complex constructors, converting from glm::vec2 is a trivial copy of two floats that compilers optimize to a single instruction.

Method 1: Conversion Operators and Helper Functions

The simplest integration adds explicit conversion utilities in a header you control.

Implicit Conversion via Free Functions

Create a small bridging header (e.g., imgui_glm_bridge.h) that includes both libraries and defines inline converters:

#include <glm/vec2.hpp>
#include "imgui.h"

inline ImVec2 ToImVec(const glm::vec2& v) { 
    return ImVec2(v.x, v.y); 
}

inline glm::vec2 ToGlmVec(const ImVec2& v) { 
    return glm::vec2(v.x, v.y); 
}

With these helpers, you can pass GLM vectors directly to layout functions:

glm::vec2 buttonPos(150.0f, 75.0f);
ImGui::SetCursorPos(ToImVec(buttonPos));
ImGui::Button("Click Me");

Specializing for Your Math Library

If you can extend your math library's namespace without violating ODR, provide semantic conversion functions there for discoverability:

namespace glm {
    inline ImVec2 to_imvec(const vec2& v) { 
        return ImVec2(v.x, v.y); 
    }
}

Method 2: Pointer-Based Access for Multi-Component Widgets

Widgets editing multiple floats simultaneously, such as SliderFloat2, InputFloat2, and DragFloat2, expect a pointer to a contiguous array of floats rather than an ImVec2. GLM guarantees that vec2 stores its components contiguously in memory, allowing direct pointer access.

Use glm::value_ptr from <glm/gtc/type_ptr.hpp> to obtain the address of the first component:

#include <glm/vec2.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "imgui.h"

void EditVelocity(glm::vec2& velocity) {
    ImGui::SliderFloat2("Velocity", glm::value_ptr(velocity), -10.0f, 10.0f);
}

This pattern avoids any copying; ImGui writes directly into your glm::vec2 memory.

Method 3: Wrapper Functions for Type-Safe API

For large projects where you want ImGui calls to look native to your math types, write thin wrapper overloads that forward to the original API after conversion:

// imgui_wrappers.h
#pragma once
#include <glm/vec2.hpp>
#include <glm/vec4.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "imgui.h"

inline void SetNextWindowPos(const glm::vec2& pos, ImGuiCond cond = 0, 
                             const glm::vec2& pivot = glm::vec2(0,0)) {
    ImGui::SetNextWindowPos(ImVec2(pos.x, pos.y), cond, ImVec2(pivot.x, pivot.y));
}

inline bool SliderFloat2(const char* label, glm::vec2& v, 
                         float v_min, float v_max, 
                         const char* format = "%.3f") {
    return ImGui::SliderFloat2(label, glm::value_ptr(v), v_min, v_max, format);
}

inline bool ColorEdit4(const char* label, glm::vec4& color) {
    return ImGui::ColorEdit4(label, glm::value_ptr(color));
}

These wrappers eliminate boilerplate at call sites while maintaining full compatibility with the underlying ImGui implementation defined in imgui.h and imgui_internal.h.

Key Source Files and Implementation Details

Understanding where types are defined helps ensure your integration remains robust across updates:

  • imgui.h: Contains the ImVec2 and ImVec4 struct definitions and the public API surface (e.g., SetCursorPos, SetNextWindowSize). Functions here accept ImVec2 by value or const reference.
  • imgui_internal.h: Implements internal vector math utilities like ImMin, ImMax, and ImFloor that operate on ImVec2. While you typically do not call these directly, understanding their expectation of POD semantics confirms that conversion helpers should remain simple copies.
  • misc/cpp/imgui_stdlib.h: Demonstrates the official pattern for extending ImGui with support for standard library types (specifically std::string). Use this as a reference for how to structure your own imgui_glm.h extension header.
  • backends/imgui_impl_opengl3.h: Shows how ImVec2 values are passed to rendering backends, confirming that the struct layout must match exactly what the graphics API expects—reinforcing why keeping ImVec2 as the boundary type is safest.

Summary

  • ImVec2 is POD: Defined in imgui.h with public float x, y members, making it trivially convertible from any 2-float vector type.
  • Zero-cost conversion: Converting glm::vec2 to ImVec2 copies two floats and adds no runtime overhead.
  • Two integration patterns: Use conversion helpers (e.g., ToImVec) for layout functions accepting ImVec2, and glm::value_ptr for widget functions requiring raw float* arrays.
  • No source modifications: Keep all bridging code in your own headers to preserve binary compatibility and simplify updates to the ocornut/imgui repository.
  • Extensible to other types: The same patterns apply to glm::vec3, glm::vec4, and custom math structs with contiguous float storage.

Frequently Asked Questions

Can I replace ImVec2 with glm::vec2 inside ImGui's source code?

Modifying imgui.h to replace ImVec2 with glm::vec2 is strongly discouraged. Doing so breaks binary compatibility with the official library and complicates future updates. The supported approach is to provide conversion helpers in your own codebase while treating ImVec2 strictly as a boundary type at the API surface.

Do these conversion patterns add runtime overhead?

No. Converting glm::vec2 to ImVec2 involves copying two 32-bit floats, which modern compilers optimize into a single mov instruction or inline entirely. When using glm::value_ptr for functions like SliderFloat2, no copying occurs—ImGui writes directly into your vector's memory.

How do I integrate glm::vec3 or glm::vec4 for color editing?

Apply the same principles used for vec2. For functions accepting ImVec4 (such as ColorEdit4 or ColorPicker4), create a helper: inline ImVec4 ToImVec4(const glm::vec4& v) { return ImVec4(v.x, v.y, v.z, v.w); }. Alternatively, pass glm::value_ptr(vec4) to functions expecting a float[4] array, such as ColorEdit4(const char* label, float col[4]).

Where should I define my conversion utilities?

Place conversion helpers in a dedicated header file (e.g., imgui_glm.h or math_bridge.h) that includes both imgui.h and your math library headers. This isolation prevents pollution of either library's namespace and makes it trivial to swap math libraries or update ImGui versions without merge conflicts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →