Полезные сниппеты и функции

chromiusj

Известный
Модератор
6,013
4,331
Описание:
Рендер линии в 3D пространстве без артефактов, когда одна из точек линии уходит за камеру.

Принцип работы:
1) Обе точки видны на экране: линия рисуется напрямую через renderDrawLine
2) Ключевой случай: одна точка впереди, другая сзади. Нарисовать такую линию нельзя: проекция точки за камерой даёт «вывернутые» координаты и линию будет косоёбить уводить в разные стороны. Поэтому функция используя бинарный поиск (12 итераций, точность ~1/4096) ищет вдоль отрезка ту точку, которая лежит прямо на границе видимости (z > 0) и использует её вместо настоящей
3) Обе точки за экраном: ничего не рисуем, тут всё логично

Код:
Lua:
local function clipToNearPlane(fx, fy, fz, bx, by, bz)
    local sx, sy
    local left, right = 0.0, 1.0
    for _ = 1, 12 do
        local mid = (left + right) * 0.5
        local _, cx, cy, cz = convert3DCoordsToScreenEx(
            fx + (bx - fx) * mid,
            fy + (by - fy) * mid,
            fz + (bz - fz) * mid
        )
        if cz > 0 then
            left, sx, sy = mid, cx, cy
        else
            right = mid
        end
    end
    return sx, sy
end

function renderDrawLine3D(x1, y1, z1, x2, y2, z2, thickness, color)
    local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1)
    local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2)
    local v1, v2 = sz1 > 0, sz2 > 0

    if not v1 and not v2 then
        return false
    end

    if not v2 then
        sx2, sy2 = clipToNearPlane(x1, y1, z1, x2, y2, z2)
    elseif not v1 then
        sx1, sy1 = clipToNearPlane(x2, y2, z2, x1, y1, z1)
    end

    if not sx1 or not sx2 then
        return false
    end

    renderDrawLine(sx1, sy1, sx2, sy2, thickness, color)
    return true, sx1, sy1, sx2, sy2
end

Пример:
Lua:
function onD3DPresent()
    local x1, y1, z1 = 1806.59, -2547.78, 14
    local x2, y2, z2 = 1806.59, -2567.78, 14

    -- Улучшенный рендер
    renderDrawLine3D(x1, y1, z1, x2, y2, z2, 5, 0xFF00FF00)

    x1 = x1 + 1
    x2 = x2 + 1

    -- Обычный рендер
    local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1)
    local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2)
    if sz1 > 0 and sz2 > 0 then
        renderDrawLine(sx1, sy1, sx2, sy2, 5, 0xFFFF0000)
    end
end

Демонстрация:
Красная линия - обычный рендер
Зелёная линия - улучшенный рендер

Можно заметить, как зелёная линия стабильно отрисовывается под разными углами камеры, в то время как красная нет

Lua:
local NEAR_PLANE_EPSILON = 1e-3
local function clipToNearPlane(fx, fy, fz, wf, bx, by, bz, wb)
    local dw = wf - wb
    if dw == 0 then return nil, nil end
    local t = math.max(0.0, math.min(1.0, (wf - NEAR_PLANE_EPSILON) / dw))
    local _, sx, sy = convert3DCoordsToScreenEx(
        fx + (bx - fx) * t,
        fy + (by - fy) * t,
        fz + (bz - fz) * t
    )
    return sx, sy
end

local function renderDrawLine3D(x1, y1, z1, x2, y2, z2, thickness, color)
    local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1)
    local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2)

    if sz1 <= 0 and sz2 <= 0 then return false end

    if sz2 <= 0 then
        sx2, sy2 = clipToNearPlane(x1, y1, z1, sz1, x2, y2, z2, sz2)
    elseif sz1 <= 0 then
        sx1, sy1 = clipToNearPlane(x2, y2, z2, sz2, x1, y1, z1, sz1)
    end

    if not sx1 or not sx2 then return false end

    renderDrawLine(sx1, sy1, sx2, sy2, thickness, color)
    return true, sx1, sy1, sx2, sy2
end
 

kyrtion

Известный
1,397
539
Lua:
local NEAR_PLANE_EPSILON = 1e-3
local function clipToNearPlane(fx, fy, fz, wf, bx, by, bz, wb)
    local dw = wf - wb
    if dw == 0 then return nil, nil end
    local t = math.max(0.0, math.min(1.0, (wf - NEAR_PLANE_EPSILON) / dw))
    local _, sx, sy = convert3DCoordsToScreenEx(
        fx + (bx - fx) * t,
        fy + (by - fy) * t,
        fz + (bz - fz) * t
    )
    return sx, sy
end

local function renderDrawLine3D(x1, y1, z1, x2, y2, z2, thickness, color)
    local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1)
    local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2)

    if sz1 <= 0 and sz2 <= 0 then return false end

    if sz2 <= 0 then
        sx2, sy2 = clipToNearPlane(x1, y1, z1, sz1, x2, y2, z2, sz2)
    elseif sz1 <= 0 then
        sx1, sy1 = clipToNearPlane(x2, y2, z2, sz2, x1, y1, z1, sz1)
    end

    if not sx1 or not sx2 then return false end

    renderDrawLine(sx1, sy1, sx2, sy2, thickness, color)
    return true, sx1, sy1, sx2, sy2
end
Предлагаю кэшировать функции ради +производительность
(или я переборщил)

Lua:
local math_min, math_max = math.min, math.max
local convert3DCoordsToScreenEx = convert3DCoordsToScreenEx
local renderDrawLine = renderDrawLine
local NEAR_PLANE_EPSILON = 1e-3

local function clipToNearPlane(fx, fy, fz, wf, bx, by, bz, wb)
    local dw = wf - wb
    if dw == 0 then return nil, nil end
    local t = math_max(0.0, math_min(1.0, (wf - NEAR_PLANE_EPSILON) / dw))
    local _, sx, sy = convert3DCoordsToScreenEx(
        fx + (bx - fx) * t,
        fy + (by - fy) * t,
        fz + (bz - fz) * t
    )
    return sx, sy
end

local function renderDrawLine3D(x1, y1, z1, x2, y2, z2, thickness, color)
    local _, sx1, sy1, sz1 = convert3DCoordsToScreenEx(x1, y1, z1)
    local _, sx2, sy2, sz2 = convert3DCoordsToScreenEx(x2, y2, z2)

    if sz1 <= 0 and sz2 <= 0 then return false end

    if sz2 <= 0 then
        sx2, sy2 = clipToNearPlane(x1, y1, z1, sz1, x2, y2, z2, sz2)
    elseif sz1 <= 0 then
        sx1, sy1 = clipToNearPlane(x2, y2, z2, sz2, x1, y1, z1, sz1)
    end

    if not sx1 or not sx2 then return false end

    renderDrawLine(sx1, sy1, sx2, sy2, thickness, color)
    return true, sx1, sy1, sx2, sy2
end
 

vegas

Известный
Проверенный
563
526
Описание: Позволяет создавать несколько одинаковых хуков для samp/arizona events
Lua:
local handler = (function()
    local this = {
        list = {}
    }
    function this:update_require(lib, hook)
        require(lib)[hook] = function(...)
            local arguments = {...}
            local index = 0
            for _, execute in pairs(self.list[lib][hook].handlers) do
                index = index + 1
                local return_arguments = execute(table.unpack(arguments))
                if return_arguments == false then
                    return false
                end
                if return_arguments ~= nil and return_arguments ~= true then
                    arguments = return_arguments
                end
                if index == self.list[lib][hook].count then
                    return arguments
                end
            end
        end
    end
    function this:new(lib, hook, execute)
        if not self.list[lib] then
            self.list[lib] = {}
        end
    
        if not self.list[lib][hook] then
            self.list[lib][hook] = {handlers = {}, last_id = 0, count = 0}
        end
        self.list[lib][hook].count = self.list[lib][hook].count + 1
        self.list[lib][hook].last_id = self.list[lib][hook].last_id + 1
        self.list[lib][hook].handlers[self.list[lib][hook].last_id] = execute
        self:update_require(lib, hook)
        return (function()
            local this_ = {
                index = self.list[lib][hook].last_id,
            }
            function this_:remove()
                this.list[lib][hook].count = this.list[lib][hook].count - 1
                this.list[lib][hook].handlers[self.index] = nil
                this:update_require(lib, hook)
            end
            return this_
        end)()
    end
    
    return this
end)()
Пример: Первые 2 хука увидят событие и изменят значение, 3 хук увидит, ничего не изменив но удалит 4 хук который не должен был отправить клиенту событие,, 5 хук создается внутри 3 и возвращает значение с его изменением
Lua:
handler1 = handler:new('samp.events', 'onGivePlayerMoney', function(money)
    print('handler1')
    return {money + 1}
end)
handler2 = handler:new('samp.events', 'onGivePlayerMoney', function(money)
    print('handler2')
    return {money + 1}
end)
handler3 = handler:new('samp.events', 'onGivePlayerMoney', function(money)
    print('handler3')
    handler4:remove()
    handler5 = handler:new('samp.events', 'onGivePlayerMoney', function(money)
        print('handler5')
        return {money + 200}
    end)
end)
handler4 = handler:new('samp.events', 'onGivePlayerMoney', function(money)
    print('handler4')
    return false
end)
 
Последнее редактирование:

pathtohell

Участник
16
76
Описание: Обёртка над imgui.Begin, которая плавно меняет прозрачность окна в зависимости от расстояния курсора. При наведении — полная непрозрачность, при удалении — плавное затухание по smoothstep-кривой. Все параметры опциональны.

demo.gif


Код:
Lua:
local states = {}

--- @param name string
--- @param open userdata|nil imgui.new.bool pointer, nil = no close button
--- @param flags number|nil
--- @param minAlpha number|nil default 0.25
--- @param maxAlpha number|nil default 1.0
--- @param fadeDist number|nil default 200.0
--- @param fadeIn number|nil default 14.0
--- @param fadeOut number|nil default 6.0
--- @return boolean visible, boolean show
function imgui.BeginAutoOpacity(name, open, flags, minAlpha, maxAlpha, fadeDist, fadeIn, fadeOut)
    minAlpha = minAlpha or 0.25
    maxAlpha = maxAlpha or 1.0
    fadeDist = fadeDist or 200.0
    fadeIn = fadeIn or 14.0
    fadeOut = fadeOut or 6.0

    local s = states[name] or { alpha = maxAlpha }
    states[name] = s

    local style = imgui.GetStyle()
    local prevAlpha = style.Alpha
    style.Alpha = s.alpha

    local visible, show = imgui.Begin(name, open, flags)

    if not visible then
        style.Alpha = prevAlpha
        imgui.End()
        if open and not open[0] then states[name] = nil end
        return false, show
    end

    local io = imgui.GetIO()
    local wp = imgui.GetWindowPos()
    local ws = imgui.GetWindowSize()
    local mx = io.MousePos.x
    local my = io.MousePos.y
    local dx = math.max(wp.x - mx, 0, mx - (wp.x + ws.x))
    local dy = math.max(wp.y - my, 0, my - (wp.y + ws.y))
    local dist = math.sqrt(dx * dx + dy * dy)

    local target = maxAlpha
    if not imgui.IsWindowHovered() then
        local t = math.min(dist / fadeDist, 1.0)
        target = maxAlpha - (maxAlpha - minAlpha) * (t * t * (3 - 2 * t))
    end

    local speed = (target > s.alpha) and fadeIn or fadeOut
    local delta = math.min(io.DeltaTime * speed, 1.0)
    s.alpha = math.max(minAlpha, math.min(maxAlpha, s.alpha + (target - s.alpha) * delta))

    return true, show
end

function imgui.EndAutoOpacity()
    imgui.GetStyle().Alpha = 1.0
    imgui.End()
end

Пример использования:
Lua:
local mainWindowOpen = imgui.new.bool(true)

imgui.OnFrame(function() return mainWindowOpen[0] end, function()
    if imgui.BeginAutoOpacity("Main Window", mainWindowOpen) then
        imgui.Text("Hello!")
    end
    imgui.EndAutoOpacity()
end)