How to write a new widget:... callin?

How to write a new widget:... callin?

Discuss game development here, from a distinct game project to an accessible third-party mutator, down to the interaction and design of individual units if you like.

Moderator: Moderators

Post Reply
User avatar
Beherith
Posts: 5145
Joined: 26 Oct 2007, 16:21

How to write a new widget:... callin?

Post by Beherith »

I have a widget that does a bit of management of what units are visible at any given time. This is just a few hundred lines of code, that check wether they are truly drawn, and tracks them incrementally.

I would like this widget to implement a few new widget:VisibleUnitAdd, widget:VisibleUnitRemove, etc calls.

Does anyone have a bit of info, or maybe an example of how to do this correctly? As I have a good dozen widget that use these calls instead of repeating a ton of boilerplate code.
User avatar
Silentwings
Posts: 3720
Joined: 25 Oct 2008, 00:23

Re: How to write a new widget:... callin?

Post by Silentwings »

Seems like a clean one - https://github.com/spring/spring/commit ... 880d2d4dc2

Or did you mean add it as a "fake" lua-powered callin to the widgethandler without engine interaction?

But from your use case (which I don't completely follow) I wonder if you'd be better off with a callout e.g. Spring.GetVisible(Unit,Feature)s / Spring.(Get,Set)(Unit,Feature)Visibility
User avatar
Beherith
Posts: 5145
Joined: 26 Oct 2007, 16:21

Re: How to write a new widget:... callin?

Post by Beherith »

Thanks Silentwings, that is very good info for a gadget-side engine one. Ive written a similar callin into barspring for AllowUnitCaptureStep() .

But I needed a purely Lua widget-side one: Watch The Fort and Sprung/Sprunk assisted me in this one:

Code: Select all

Beyond-All-Reason.sdd\luaui\barwidgets.lua
  174:     'LanguageChanged', -- add to list of callins
 1639: function widgetHandler:LanguageChanged()
 1640:     for _, w in ipairs(self.LanguageChangedList) do
 1641:         w:LanguageChanged()

Trigger the callin from a widget:

Code: Select all

C:\Users\Peti\Documents\My Games\Spring\games\Beyond-All-Reason.sdd\luaui\Widgets\gui_language.lua
   54:         if Script.LuaUI('LanguageChanged') then
   55:             Script.LuaUI.LanguageChanged()

Handle the callin in another widget:

Code: Select all

C:\Users\Peti\Documents\My Games\Spring\games\Beyond-All-Reason.sdd\luaui\Widgets\gui_info.lua
 1561: function widget:LanguageChanged()
My use case is the following: for GL4 widgets, especially widgets that draw stuff at units, its best to have a vertex buffer object that always contains the things we need to draw, and is updated incrementally whenever new units enter los/get created or old units die/go out of los.

This way, the widgets dont have to do any work to do occlusion culling ever, its all done GPU side.

This whole new widget callin system was needed to reduce the 100 lines of boilerplate code on each widget which tracked the nasty edge cases of unitgiven, unittaken, the even nastier cases where a zombie unit (no longer alive but not invalid yet) could enter LOS, and all the management of rebuilding the table of valid visible units on PlayerChanged callins.

Using all of this stuff, making a widget that draws something at every single unit with 0 cpu overhead gets simplified to:

Code: Select all

function widget:GetInfo()
   return {
      name      = "API Unit Tracker Tester GL4",
      desc      = "Tracks visibleunitslist",
      author    = "Beherith",
      date      = "2022.03.01",
      license   = "GNU GPL, v2 or later",
      layer     = -88888888,
      enabled   = true
   }
end

local myvisibleUnits = {} -- table of unitID : unitDefID

local unitTrackerVBO = nil
local unitTrackerShader = nil
local luaShaderDir = "LuaUI/Widgets/Include/"
local texture = "luaui/images/solid.png"

local function initGL4()
	local DrawPrimitiveAtUnit = VFS.Include(luaShaderDir.."DrawPrimitiveAtUnit.lua")
	local InitDrawPrimitiveAtUnit = DrawPrimitiveAtUnit.InitDrawPrimitiveAtUnit
	local shaderConfig = DrawPrimitiveAtUnit.shaderConfig -- MAKE SURE YOU READ THE SHADERCONFIG TABLE in DrawPrimitiveAtUnit.lua
	shaderConfig.TRANSPARENCY = 0.5	
	shaderConfig.ANIMATION = 0
	shaderConfig.HEIGHTOFFSET = 3.99
	unitTrackerVBO, unitTrackerShader = InitDrawPrimitiveAtUnit(shaderConfig, "unitTrackerTester")
end


function widget:VisibleUnitAdded(unitID, unitDefID, unitTeam)
	Spring.Echo("widget:VisibleUnitAdded",unitID, unitDefID, unitTeam)
	local teamID = Spring.GetUnitTeam(unitID) or 0 
	local gf = Spring.GetGameFrame()
	myvisibleUnits[unitID] = unitDefID
	pushElementInstance(
		unitTrackerVBO, -- push into this Instance VBO Table
		{
			96, 96, 8, 8,  -- lengthwidthcornerheight
			teamID, -- teamID
			12, -- how many trianges should we make (2 = cornerrect)
			gf, 0, 0, 0, -- the gameFrame (for animations), and any other parameters one might want to add
			0, 1, 0, 1, -- These are our default UV atlas tranformations
			0, 0, 0, 0 -- these are just padding zeros, that will get filled in
		},
		unitID, -- this is the key inside the VBO TAble,
		true, -- update existing element
		nil, -- noupload, dont use unless you know what you are doing
		unitID -- last one should be UNITID?
	)
end

function widget:VisibleUnitsChanged(extVisibleUnits, extNumVisibleUnits)
	Spring.Echo("widget:VisibleUnitsChanged",extVisibleUnits, extNumVisibleUnits)
	clearInstanceTable(unitTrackerVBO)
	for unitID, unitDefID in pairs(extVisibleUnits) do 
		widget:VisibleUnitAdded(unitID, unitDefID, Spring.GetUnitTeam(unitID))
	end
end

function widget:VisibleUnitRemoved(unitID)
	Spring.Echo("widget:VisibleUnitRemoved",unitID)
	if unitTrackerVBO.instanceIDtoIndex[unitID] then 
		popElementInstance(unitTrackerVBO, unitID)
		myvisibleUnits[unitID] = nil
	end
end

function widget:DrawWorldPreUnit()
	if Spring.IsGUIHidden() then
		return
	end

	if unitTrackerVBO.usedElements > 0 then
		gl.Texture(0, texture)
		unitTrackerShader:Activate()
		unitTrackerShader:SetUniform("iconDistance", 99999) -- pass
		unitTrackerShader:SetUniform("addRadius", 0)
		gl.DepthTest(true)
		gl.DepthMask(false)
		unitTrackerVBO.VAO:DrawArrays(GL.POINTS, unitTrackerVBO.usedElements)
		unitTrackerShader:Deactivate()
		gl.Texture(0, false)
	end
end

function widget:Initialize()
	initGL4()
end
Edit: All of this is allowing us single digit LuaUI load even in heavy lategame situations, and most of that goes to UI element rendering :)
With all the bells and whistles enabled, like gpu rendered healthbars, paralyze effects, stenciled LOS and radar ranges, rank icons, AO plates for features, AO plates for units etc.
Post Reply

Return to “Game Development”