Гайд Работаем с DrawList в mimgui

chapo

чопа сребдс // @moujeek
Автор темы
Модератор
8,868
11,576
Привет, в этой теме покажу как рисовать примитивы через DrawList в mimgui.

С помощью DrawList вы сможете выводить на экран такие штуки как:
  • круг
  • заполненный круг
  • квадрат
  • заполненный квадрат
  • квадрат заполненный градиентом
  • линии и т.д.
1644749108296.png


Начало работы
Для начала работы с DrawList нам необходимо вызвать следующую функцию:
Lua:
local dl = imgui.GetWindowDrawList()
Теперь нам нужно перевести координаты "курсора" ImGui окна в экранные и записать их в переменную (в моем случае переменная "p"), для этого используем:
Lua:
local p = imgui.GetCursorScreenPos()
(!) Если вы хотите изменить положение элемента, то функцию SetCursorPos нужно вызывать до GetCursorScreenPos, например:
Lua:
imgui.SetCursorPos(imgui.ImVec2(5, 35))
local p = imgui.GetCursorScreenPos()

Примеры:
Lua:
dl:AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness = 1.0f);
dl:AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ~0, float thickness = 1.0f);   // a: upper-left, b: lower-right, rounding_corners_flags: 4-bits corresponding to which corner to round
dl:AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ~0);                     // a: upper-left, b: lower-right
dl:AddRectFilledMultiColor(const ImVec2& a, const ImVec2& b, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left);
dl:AddQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col, float thickness = 1.0f);
dl:AddQuadFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col);
dl:AddTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col, float thickness = 1.0f);
dl:AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col);
dl:AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12, float thickness = 1.0f);
dl:AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12);
dl:AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL);
dl:AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL);
dl:AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,1), ImU32 col = 0xFFFFFFFF);
dl:AddImageQuad(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,0), const ImVec2& uv_c = ImVec2(1,1), const ImVec2& uv_d = ImVec2(0,1), ImU32 col = 0xFFFFFFFF);
dl:AddPolyline(const ImVec2* points, const int num_points, ImU32 col, bool closed, float thickness, bool anti_aliased);
dl:AddConvexPolyFilled(const ImVec2* points, const int num_points, ImU32 col, bool anti_aliased);
dl:AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments = 0);
Пример отрисовки квадратов:
Lua:
--==[ БЕЗ ЗАЛИВКИ ]==--
imgui.SetCursorPos(imgui.ImVec2(5, 85))
local p = imgui.GetCursorScreenPos()
--[[
    AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ~0, float thickness = 1.0f)
    Параметры:
    const ImVec2& a - левый верхний угол
    const ImVec2& b - правый нижний угол
    ImU32 col, - цвет в формате U32 (0xAABBGGRR)
    float rounding - радиус закругления
    int rounding_corners_flags - закругляемые углы
    float thickness - толщина
]]
dl:AddRect(p, imgui.ImVec2(p.x + 30, p.y + 30), 0xFF0000ff)

--==[ C ЗАЛИВКОЙ ]==--
imgui.SetCursorPos(imgui.ImVec2(5 + 35, 85))
local p = imgui.GetCursorScreenPos()
dl:AddRectFilled(p, imgui.ImVec2(p.x + 30, p.y + 30), 0xFF0000ff) -- функция принимает такие же аргументы как и AddRect (выше)

Результат:
1644750066784.png
Lua:
--[[
    AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12, float thickness = 1.0f);
    Параметры:
    const ImVec2& centre -- положение (центр)
    float radius - радиус
    ImU32 col - цвет в формате U32
    int num_segments, - кол-во полигонов (углов у круга (че блять))
    float thickness - толщина обводки
]]

--==[ БЕЗ ЗАЛИВКИ ]==--
imgui.SetCursorPos(imgui.ImVec2(20, 50))
local p = imgui.GetCursorScreenPos()
dl:AddCircle(p, 15, 0xFF0000ff)

--==[ С ЗАЛИВКОЙ ]==--
imgui.SetCursorPos(imgui.ImVec2(20 + 35, 50))
local p = imgui.GetCursorScreenPos()
dl:AddCircleFilled(p, 15, 0xFF0000ff) -- от AddCircle отличается только отсутствием 5 аргумента (толщины)

Результат:
1644750319302.png

Отрисовка элементов вне окна:
так же в mimgui можно рисовать вне окна, для этого заменяем imgui.GetWindowDrawList() на imgui.GetBackgroundDrawList()
Пример:
Lua:
local backgroundDraw = imgui.OnFrame(
    function() return true end,
    function(self)
        self.HideCursor = true
        local dl = imgui.GetBackgroundDrawList()
        dl:AddRectFilled(imgui.ImVec2(800, 500), imgui.ImVec2(1000, 600), 0xFF0000ff, 20, 1 + 8)
        --[[
            в качестве положения в переменной Vec2 нужно использовать координаты экрана, вот более простой пример:
                local pos = imgui.ImVec2(800, 500) -- 800 - положение по X, 500 - положение по Y
                local size = imgui.ImVec2(200, 100) -- 200 - размер по X, 100 - размер по Y
                dl:AddRectFilled(pos, imgui.ImVec2(pos.x + size.x, pos.y + size.y), 0xFF0000ff, 20, 1 + 8)
        ]]
    end
)

Результат:
1644750628835.png


(!) Что бы получить цвет формата U32 из Vec4 можно использовать следующие функции:
  • ImGui - imgui.GetColorU32(vec_color) -- вернет 0xFF0000FF
  • MImGui - imgui.GetColorU32Vec4(vec_color) -- вернет 0xFF0000FF
  • Пример:
    Lua:
    local vec_color = imgui.ImVec4(1, 0, 0, 1)
    print(imgui.GetColorU32Vec4(vec_color))
    -->> 0xFF0000FF
 

Hatiko

Известный
Проверенный
1,502
620
Так был уже гайд, при чем недавно был создан. Что этот, что тот урезанный, особо толку нету инфы, кроме как круг с квадратом нарисовать.
 

Hatiko

Известный
Проверенный
1,502
620
drawlist в мимгуе разве как-то отличается от дравлиста в обычном имгуе?
В основном одинаково. давно ещё ковырялся, вроде бы как новые методы есть несколько штук , но они не совсем для "примитивов" нужны,
 

ChаtGPT

Активный
371
93
Всем хуй, решил залить сюда небольшой гайд по каналам дравлистов, полезен точно будет.
Каналы в DrawList позволяют разделить рисование примитивов на несколько частей. Как в фотошопе слои изображения отобразить на переднем, или заднем плане.

Пример без использования каналов:
Lua:
imgui.Begin('Main Window', renderWindow)
    local DL = imgui.GetWindowDrawList()
    
    DL:AddRectFilled(imgui.ImVec2(20.0, 20.0), imgui.ImVec2(120.0, 120.0), 0xFFFFFFFF) -- Белый заполненный квадрат
    DL:AddRectFilled(imgui.ImVec2(0.0, 0.0), imgui.ImVec2(100.0, 100.0), 0xFF0000FF) -- Красный заполненный квадрат
imgui.End()
1715175027943.png


В этом примере, примитивы рисуются по очереди, сначала рисуется белый квадрат, затем красный, который рисуется поверх белого квадрата.

Пример с использованием каналов:
В этом примере мы сделаем так, чтобы сначала рисовался красный квадрат, а затем белый:
Lua:
imgui.Begin('Main Window', renderWindow)
    local DL = imgui.GetWindowDrawList()

    -- Создаём 2 канала для внепорядкового рисования
    DL:ChannelsSplit(2)

    -- Он должен быть нарисован ПЕРЕД красным квадратом
    DL:ChannelsSetCurrent(1) -- Канал 1
    DL:AddRectFilled(imgui.ImVec2(20.0, 20.0), imgui.ImVec2(120.0, 120.0), 0xFFFFFFFF) -- Белый заполненный квадрат

    -- Он должен быть нарисован ПОСЛЕ белого квадрата
    DL:ChannelsSetCurrent(0) -- Канал 0
    DL:AddRectFilled(imgui.ImVec2(0.0, 0.0), imgui.ImVec2(100.0, 100.0), 0xFF0000FF) -- Красный заполненный квадрат

    DL:ChannelsMerge() -- Объединение всех каналов, в результате чего белый квадрат должны оказаться над красным
imgui.End()
1715175855490.png


Когда drawlist рисует эти примитивы, он сначала рисует канал 0, а затем канал 1. Поэтому, квадрат из канала 0 будет нарисован под квадратом из канала 1.

И зачем это нужно? Я приведу пример, зачем мне это понадобилось. Мне необходимо было реализовать авторазмер для imgui.BeginChild(), чтобы размер дочернего окна соответствовал содержимому внутри него. В текущей версии imgui это сделать невозможно. Я отказался от использования дочернего окна и создал примитив(DL:AddRectFilled). Когда мне нужно было залить фон этого примитива, я столкнулся с проблемой, что он отображается поверх всех элементов, поскольку примитив рисуется последним. Переместить примитив до рендера элементов imgui невозможно, потому что для авторазмера сначала нужно получить размер содержимого и только потом строить примитив. Вот именно здесь мне идеально подошли канали. Я отложил рендеринг imgui-элементов(которые до этого рендерились первыми), чтобы сначала отобразился примитив, а затем уже элементы imgui (кнопки, текст, чекбоксы и т.д.).

Говнокод, ну и похуй.:
local imgui = require 'mimgui'
local encoding = require 'encoding'
encoding.default = 'CP1251'
u8 = encoding.UTF8

local renderWindow = imgui.new.bool(true)
local groupCount = 0
local p = imgui.new.float(10.0)

imgui.OnInitialize(function()
    imgui.GetIO().IniFilename = nil
end)

local newFrame = imgui.OnFrame(
    function() return renderWindow[0] end,
    function(player)
        local resX, resY = getScreenResolution()
        local sizeX, sizeY = 300, 200
        imgui.SetNextWindowPos(imgui.ImVec2(resX / 3, resY / 2), imgui.Cond.FirstUseEver, imgui.ImVec2(0.5, 0.5))
        imgui.SetNextWindowSize(imgui.ImVec2(sizeX, sizeY), imgui.Cond.FirstUseEver)
        imgui.Begin('Main Window', renderWindow)

            local DL = imgui.GetWindowDrawList()
            if imgui.Button("Create Group") then groupCount = groupCount + 1 end
            imgui.SameLine()
            if imgui.Button('Clear') then groupCount = 0 end

            imgui.SliderFloat("padding", p, 0.0, 100.0)
            local padding = imgui.ImVec2(p[0], p[0])

            for i = 1, groupCount do
                DL:ChannelsSplit(2)
                local min = imgui.GetCursorScreenPos()
                imgui.SetCursorScreenPos(min + padding)
                DL:ChannelsSetCurrent(1)
                imgui.BeginGroup()
                    imgui.Button("Group " .. i)
                imgui.EndGroup()
                local max = imgui.GetItemRectMax() + padding
                imgui.SetCursorScreenPos(min)
                DL:ChannelsSetCurrent(0)
                DL:AddRectFilled(min, max, -1)
                imgui.Dummy(max - min)
                DL:ChannelsMerge()
                imgui.SameLine()
            end
        imgui.End()
    end
)

function main()
    while not isSampAvailable() do wait(0) end
    sampRegisterChatCommand('mimgui', function()
        renderWindow[0] = not renderWindow[0]
    end)
    wait(-1)
end

Инфу нашёл ЗДЕСЬ