- Notifications
You must be signed in to change notification settings - Fork0
Immediate mode 3D2D UI for Garry's Mod
License
Pdzly/imgui-1
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Immediate mode 3D2D UI for Garry's Mod. See some real-world useshere.
- Creating clickable buttons or tracking mouse in an area requires one line of code
- Visibility optimizations to prevent UI from being rendered if it's not visible
- Only a wrapper for
cam.Start3D2D
so allsurface
library functions are still usable - Error handling to prevent clients from crashing if a Lua error happens
Placeimgui.lua
somewhere in your addon or gamemode (eg.myaddon/lua/myaddon/imgui.lua
) and make sure it isAddCSLuaFile
d and available to your clientside rendering code.
Using with entities:
-- 3D2D UI should be rendered in translucent pass, so this should be either TRANSLUCENT or BOTHENT.RenderGroup=RENDERGROUP_TRANSLUCENTfunctionENT:DrawTranslucent()-- While you can of course use the imgui.Start3D2D function for entities, IMGUI has some special syntax-- This function automatically calls LocalToWorld and LocalToWorldAngles respectively on position and anglesifimgui.Entity3D2D(self,Vector(0,0,50),Angle(0,90,90),0.1)then-- render thingsimgui.End3D2D()endend
Using with gamemode rendering hooks:
localimgui=include("imgui.lua")-- imgui.lua should be in same folder and AddCSLuaFile'dhook.Add("PostDrawTranslucentRenderables","PaintIMGUI",function(bDrawingSkybox,bDrawingDepth)-- Don't render during depth passifbDrawingDepththenreturnend-- Starts the 3D2D context at given position, angle and scale.-- First 3 arguments are equivalent to cam.Start3D2D arguments.-- Fourth argument is the distance at which the UI panel won't be rendered anymore-- Fifth argument is the distance at which the UI will start fading away-- Function returns boolean indicating whether we should proceed with the rendering, hence the if statement-- These specific coordinates are for gm_construct at next to spawnifimgui.Start3D2D(Vector(980,-83,-79),Angle(0,270,90),0.1,200,150)then-- This is a regular 3D2D context, so you can use normal surface functions to draw thingssurface.SetDrawColor(255,127,0)surface.DrawRect(0,0,100,20)-- The main priority of the library is providing interactable panels-- This creates a clickable text button at x=0, y=30 with width=100, height=25-- The first argument is text to render inside button-- The second argument is special font syntax, that dynamically creates font "Roboto" at size 24-- The special syntax is just for convinience; you can use normal Garry's Mod font names in place-- The third, fourth, fith and sixth arguments are for x, y, width and height-- The seventh argument is the border width (optional)-- The last 3 arguments are for color, hover color, and press color (optional)ifimgui.xTextButton("Foo bar","!Roboto@24",0,30,100,25,1,Color(255,255,255),Color(0,0,255),Color(255,0,0))then-- the xTextButton function returns true, if user clicked on this area during this frameprint("yay, we were clicked :D")end-- End the 3D2D contextimgui.End3D2D()endend)
Prefer entities over global hooks, since they work better withSource engine visiblity optimizations.
Setting thedeveloper
convar to non-zero value draws a debugging panel on top of each IMGUI panel you draw. It shows few pieces of useful information:
- The mouse coordinates or the reason why input is not enabled at the moment
- World position of the TDUI panel, our distance from it and the distance at which the interface is hidden
- World angles of the TDUI panel, the dot product between eye position and the panel and the angle between eye position and the panel
- How many milliseconds did we spend on rendering this UI per frame (averaged over 100 frames/renders)
If you wish to hide the developer panel even withdeveloper
cvar, setimgui.DisableDeveloperMode = true
right after importing the library.
Start 3D2D context (callscam.Start3D2D
internally)
imgui.Start3D2D(pos,angles,scale,distanceHide,distanceFadeStart)
Start 3D2D context on an entity. Equivalent toimgui.Start3D2D
, except a)pos
/angles
are automatically transformed from local entity coordinates into world coordinates and b) the entity will be ignored in mouse input collision checks.
imgui.Entity3D2D(ent,lpos,lang,scale,distanceHide,distanceFadeStart)
Ends 3D2D Context
imgui.End3D2D()
Retrieves cursor position in 3D2D space.mx
/my
are null if player is not looking at the interface
localmx,my=imgui.CursorPos()
Whether player's 3D2D cursor is within given bounds
localhovering=imgui.IsHovering(x,y,w,h)
Whether player is currently pressing
localpressing=imgui.IsPressing()
Whether player is pressed during this frame. This is guaranteed to only be called once per click
localpressed=imgui.IsPressed()
Draws a rectangle button without any text or content
localwasPressed=imgui.xButton(x,y,w,h,borderWidth,borderClr,hoverClr,pressColor)
Draws a button with text inside. Thefont
parameter is passed throughimgui.xFont
, so special font syntax is supported.The text is automatically centered within the button.
localwasPressed=imgui.xTextButton(text,font,x,y,w,h,borderWidth,color,hoverClr,pressColor)
Draws a cursor IF the cursor is within given bounds. Note:x
,y
,w
,h
should be the bounds for your whole IMGUI interface, eg.0, 0, 512, 512
if you draw into the 3d2d space within those bounds.
imgui.xCursor(x,y,w,h)
Retrieves font name usable for Garry's Mod functions based on parameter. SeeSpecial font API section below
localfontName=imgui.xFont("!Roboto@24")
Expands the entity's render bounds to cover the whole rectangle passed as 3D2D coordinates. Note:x
,y
,w
,h
should be the bounds for your whole IMGUI interface, eg.0, 0, 512, 512
if you draw into the 3d2d space within those bounds.(only usable insideimgui.Entity3D2D
block, beforeimgui.End3D2D
call)
imgui.ExpandRenderBoundsFromRect(x,y,w,h)
One of the goals of the library is to not crash the client even if something throws an error during the 3D2D context. This is achieved by halting all rendering library-wide if we detect attempt at starting a new 3D2D context without never having calledEnd3D2D
in between.
The recovery protocol triggers automatically if you try to nest 3D2D contexts due to error or any other reason. You'll be notified with a[IMGUI] Starting a new IMGUI context when previous one is still rendering. Shutting down rendering pipeline to prevent crashes..
message in console if that happens. The only way to recover from this error state is to re-initialize the whole library to reset the internal global state of IMGUI. There is no programmatic way to do this at the moment, but you can re-save theimgui.lua
to trigger Lua autorefresh on it, or just reimport it.
IMGUI comes with a simplified method for creating fonts. If you use the built-in functions, such asimgui.xTextButton
, the passed font argument will automatically go through the font syntax parser, but you can also access it directly withimgui.xFont
.
Here's an example of usingimgui.xFont
for drawing normal text:
-- Draw 'Foo bar' using Roboto font at font size 30 at 0, 0draw.SimpleText("Foo bar",imgui.xFont("!Roboto@30"),0,0)
You'll probably want to network data from client to server on button press or other clientside action at some point. IMGUI doesn't support this by itself, butnetdata is a pretty good fit for entity-based UIs.
Here's an example entity with IMGUI + netdata:
ENT.Type="anim"ifSERVERthenfunctionENT:Initialize()self:SetModel("models/props_phx/construct/glass/glass_plate1x1.mdl")self:PhysicsInit(SOLID_VPHYSICS)self.Greeters= {}endfunctionENT:ReceiveNetAction(cl)table.insert(self.Greeters,cl:Nick())self:NetDataUpdate()endfunctionENT:NetDataWrite()net.WriteTable(self.Greeters)endendifCLIENTthenENT.RenderGroup=RENDERGROUP_BOTHfunctionENT:NetDataRead()self.Greeters=net.ReadTable()endfunctionENT:DrawTranslucent()localimgui=resort.bar.imguiifimgui.Entity3D2D(self,Vector(0,0,3.5),Angle(0,0,0),0.1)thenifimgui.xTextButton("Say hello","!Roboto@24",-100,-200,200,50,1)thenself:StartNetAction()net.SendToServer()endifself.Greetersthenfori,nickinpairs(self.Greeters)dodraw.SimpleText(nick,"DermaLarge",0,-160+i*25,nil,TEXT_ALIGN_CENTER)endendimgui.End3D2D()endendend