/3d2d-imgui

Immediate mode 3D2D UI for Garry's Mod

Primary LanguageLuaMIT LicenseMIT

3D2D Immediate Mode GUI for Garry's Mod

3D2D panels made simple.

USE IMGUI INSTEAD

Development of this library has been stopped and you should use IMGUI library. It has a more flexible API and fixes many of the fundamental design issues this library has had from the beginning, especially related to heap memory usage. Here's a small comparison of equivalent code rendering 30 clickable buttons in each library:

tdui used 	24.4609375	 kbytes of heap memory
imgui used 	0.21484375	 kbytes of heap memory

Example

example

local tdui = include("tdui.lua") -- tdui.lua should be in same folder and AddCSLuaFile'd

local p
hook.Add("PostDrawTranslucentRenderables", "Paint3D2DUI", function(bDrawingSkybox, bDrawingDepth)
    -- This is required so that TDUI isn't drawn twice (which would break input)
    if bDrawingDepth then return end

    -- Create a 3D2D-IMGUI instance and cache it
    -- Note: if drawing TDUI inside a ENT:Draw(), you should cache the
    --       panel to the entity instance (self) instead of a local variable.
    --       That way there will be one panel per entity.
    p = p or tdui.Create()

    -- Draw a rectangle (x, y, w, h, [fill_color], [outline_color])
    p:Rect(-320, 0, 640, 600, _, Color(255, 255, 255))

    -- Draw a line of text (text, font, x, y, [color], [halign], [valign])
    -- Note: text is implicitly horizontally centered
    p:Text("Hello there!", "!Roboto@100", 0, 20)

    -- Draw a button (text, font, x, y, w, h, [color])
    -- Return value is boolean indicating whether left mouse or +use was pressed during this frame
    if p:Button("Say hi", "DermaLarge", -200, 160, 400, 100) then
        RunConsoleCommand("say", "hi!")
    end

    -- Draws a simple crosshair cursor at current mouse position
    p:Cursor()

    -- Renders all the queued draw commands at given 3D location (this one's near gm_construct wall)
    p:Render(Vector(980, -83, -79), Angle(0, 0, 0), 0.1)
end)

Installation

Copy tdui.lua into a folder in your addon and make sure it is AddCSLuaFiled.

Usage

Panel creation (should be called only once):

local p = tdui.Create()

Drawing components (should be called in a drawing hook, eg. ENT:Draw() or PostDrawTranslucentRenderables):

p:Rect(x, y, w, h, [fill_color], [outline_color])

p:Mat(material, x, y, w, h)

-- Same vertices layout as surface.DrawPoly
p:Polygon(vertices, color, material)

-- Note: horizontally aligned to center by default
p:Text(text, font, x, y, [color], [halign], [valign], [scissor_rect])

local isMouseOrUseDown = p:Button(text, font, x, y, w, h, [color])
if isMouseOrUseDown then
 	-- this is only called once per press
	print "Hello!"
end

-- Note: because TDUI attempts to be as stateless as possible, you need to pass
-- the current slider value to the slider function. The fraction returned by the
-- function is the new slider value.
-- Fraction is a numeric value between 0 and 1. TDUI Slider has no concept of "min" and "max"
-- values. You'll need to calculate those yourself.
local frac = p:Slider(gFrac, x, y, w, h)
if frac ~= gFrac then
	print "Slider value changed!"
	gFrac = frac
end

-- You can pass normal GMod fonts to the font parameter of p:Text and p:Button
-- but it also accepts some special formats that you can use:

p:Text("Hello world", "!Roboto@18", 0, 0)
-- This kind of syntax automatically creates and caches a font based on "Roboto"
-- typeface at size 18. The caching is pretty efficient so this can be used even
-- in finished projects, not just during development.

p:Cursor()

Configuration:

-- Draws and accepts input through walls
p:SetIgnoreZ(true)

-- Scales (multiplies) all UI elements by this value. This can be used during development
-- to test different UI scales or if you're lazy and don't want to scale values by hand.
-- This method also scales fonts that use the special !Typeface@Size format
p:SetUIScale(10)

-- Adds this tdui to list of tduis that are checked when the player presses +use bind
-- If said bind is pressed and user is hovering a TDUI on the list, the bind is blocked.
-- This is useful for eg. TDUIs inside cars, so that player doesn't leave car upon pressing +use
-- on tdui.
p:BlockUseBind()

Rendering (should be called in same drawing hook as drawing components):

p:Render(pos, angles, scale)

Tips

Code is mostly self-documenting. If there's a part you don't understand, feel free to post an issue.

Cache the panel. Use a local variable for hooks and self.Panel = self.Panel or tdui.CreatePanel() for ENT:Draw().

Make the scale parameter to p:Render as small as possible (eg. 0.1) and compensate by either scaling UI elements manually or by using p:SetUIScale. This makes the elements look sharper.

Don't be afraid to use the y-axis as the horizontal center point. 3D2D-IMGUI supports negative coordinates and the example uses them.

If you need to make eg. scrolling text, p:Text() accepts scissor_rect as the last parameter. It should be a table containing x, y, x2, y2.