Created
June 24, 2023 19:28
-
-
Save L-e-x-o-n/1d585fa46c5827501407b081d54f2aa8 to your computer and use it in GitHub Desktop.
Draws the spherical commblast range onto the ground updated for comupdate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function widget:GetInfo() | |
return { | |
name = "Commblast Range GL4", | |
desc = "Draws the spherical commblast range onto the ground", | |
author = "Beherith", --Updated for commander update by Lexon | |
date = "2022.08.27", | |
license = "Lua code: GNU GPL, v2 or later, Shader GLSL code: (c) Beherith ([email protected])", | |
layer = -1, | |
enabled = true, | |
} | |
end | |
local commblastSphereShader = nil | |
local glTexture = gl.Texture | |
local glCulling = gl.Culling | |
local glDepthTest = gl.DepthTest | |
local spGetUnitDefID = Spring.GetUnitDefID | |
local spGetUnitPosition = Spring.GetUnitPosition | |
local spGetGroundHeight = Spring.GetGroundHeight | |
local spGetUnitsInCylinder = Spring.GetUnitsInCylinder | |
local spIsGUIHidden = Spring.IsGUIHidden | |
local spIsSphereInView = Spring.IsSphereInView | |
local spGetMyTeamID = Spring.GetMyTeamID | |
local diag = math.diag | |
---- Config stuff ------------------ | |
local commanders = {} -- keyed as {unitID : {draw = bool, oldopacity = 0.0, newopacity = 1.0}} | |
local commDefIds = { [UnitDefNames['corcom'].id] = true, [UnitDefNames['armcom'].id] = true} | |
local blastRadius = 350 -- com explosion | |
local showOnEnemyDistance = 370 | |
local fadeInDistance = 160 | |
---- GL4 Config stuff ---------------- | |
local shaderConfig = { | |
FULLRADIUS = 500, | |
SPHERESEGMENTS = 8, | |
BLASTRADIUS = blastRadius, | |
OPACITYMULTIPLIER = 0.75, | |
TEAMCOLORED = 1, | |
BLASTDMG = 5000, | |
} | |
---- Object intersection test http://www.realtimerendering.com/intersections.html | |
-- Shaders | |
local vsSrc = [[ | |
#version 420 | |
#extension GL_ARB_uniform_buffer_object : require | |
#extension GL_ARB_shader_storage_buffer_object : require | |
#extension GL_ARB_shading_language_420pack: require | |
// This shader is (c) Beherith ([email protected]) | |
#line 5000 | |
layout (location = 0) in vec4 position; // l w rot and maxalpha | |
layout (location = 1) in vec3 normals; | |
layout (location = 2) in vec2 uvs; | |
layout (location = 3) in vec4 params_alphastart_alphaend_gameframe; | |
layout (location = 4) in uvec4 instData; | |
//__ENGINEUNIFORMBUFFERDEFS__ | |
//__DEFINES__ | |
layout(std140, binding = 0) readonly buffer MatrixBuffer { | |
mat4 mat[]; | |
}; | |
struct SUniformsBuffer { | |
uint composite; // u8 drawFlag; u8 unused1; u16 id; | |
uint unused2; | |
uint unused3; | |
uint unused4; | |
float maxHealth; | |
float health; | |
float unused5; | |
float unused6; | |
vec4 speed; | |
vec4[5] userDefined; //can't use float[20] because float in arrays occupies 4 * float space | |
}; | |
layout(std140, binding=1) readonly buffer UniformsBuffer { | |
SUniformsBuffer uni[]; | |
}; | |
#line 10000 | |
#define SNORM2NORM(value) (value * 0.5 + 0.5) | |
out DataVS { | |
flat vec3 v_centerpos; // xyz and radius? | |
flat vec4 v_teamcolor; // red or teamcolor, and alpha modifier | |
noperspective vec2 v_screenUV; | |
}; | |
out float hp; | |
void main() | |
{ | |
uint teamIndex = (instData.z & 0x000000FFu); //leftmost ubyte is teamIndex | |
vec4 teamCol = teamColor[teamIndex]; | |
mat4 worldMatrix = mat[instData.x]; | |
// if the unit is not visible, then just transform the sphere to 0 | |
if ((uni[instData.y].composite & 0x00001fu) == 0u ) worldMatrix = mat4(0.0); | |
v_centerpos = worldMatrix[3].xyz; | |
vec4 worldPos = vec4(1.0); | |
worldPos.xyz = position.xyz * FULLRADIUS; | |
worldPos = worldMatrix * worldPos; | |
gl_Position = cameraViewProj * worldPos; | |
v_screenUV = SNORM2NORM(gl_Position.xy / gl_Position.w); | |
vec3 camPos = cameraViewInv[3].xyz; | |
// TODO: | |
// modulate alpha based on time from params_alpha_health | |
float time = timeInfo.x + timeInfo.w ; | |
float alphasmooth = clamp((time - params_alphastart_alphaend_gameframe.z) / 15.0, 0.0, 1.0); | |
alphasmooth = mix(params_alphastart_alphaend_gameframe.x, params_alphastart_alphaend_gameframe.y, alphasmooth); | |
// modulate alpha based on health | |
float damagedness = 1.0 - clamp( uni[instData.y].health/ uni[instData.y].maxHealth, 0, 1); | |
// modulate alpha based on distance from camera | |
float distanceToCamera = length(camPos - worldMatrix[3].xyz); | |
distanceToCamera = clamp((distanceToCamera -2000)/1000,0,1); | |
v_teamcolor.rgb = vec3(1.0, 0.0, 0.0); | |
#if (TEAMCOLORED == 1) | |
v_teamcolor.rgb = teamCol.rgb; | |
#endif | |
hp = uni[instData.y].health; | |
v_teamcolor.a = alphasmooth + damagedness - distanceToCamera; | |
} | |
]] | |
local fsSrc = [[ | |
#version 420 | |
#extension GL_ARB_uniform_buffer_object : require | |
#extension GL_ARB_shading_language_420pack: require | |
// This shader is (c) Beherith ([email protected]) | |
//__ENGINEUNIFORMBUFFERDEFS__ | |
//__DEFINES__ | |
#line 30000 | |
uniform float iconDistance; | |
in DataVS { | |
flat vec3 v_centerpos; // xyz and radius? | |
flat vec4 v_teamcolor; // red or teamcolor, and alpha modifier | |
noperspective vec2 v_screenUV; | |
}; | |
uniform sampler2D mapDepths; | |
out vec4 fragColor; | |
float distancetocomm = 0; | |
float lineblast(float radius, float width, float strength) { | |
float linestrength = clamp(width - abs(distancetocomm - radius),0,1); | |
return linestrength * strength; | |
} | |
in float hp; | |
#line 31000 | |
void main(void) | |
{ | |
float mapdepth = texture(mapDepths, v_screenUV).x; | |
// Transform screen-space depth to world-space position | |
vec4 mapWorldPos = vec4( vec3(v_screenUV.xy * 2.0 - 1.0, mapdepth), 1.0); | |
mapWorldPos = cameraViewProjInv * mapWorldPos; | |
mapWorldPos.xyz = mapWorldPos.xyz / mapWorldPos.w; // YAAAY this works! | |
vec3 mapToComm = v_centerpos.xyz - mapWorldPos.xyz; | |
distancetocomm = length(mapToComm); | |
float radius = BLASTRADIUS - ((hp * BLASTRADIUS) / BLASTDMG); | |
if (distancetocomm > BLASTRADIUS) fragColor.a = 0.0 ; | |
fragColor.rgba = vec4(1.0, 0.0, 0.0, 0.0); | |
fragColor.rgb = v_teamcolor.rgb; | |
fragColor.a += lineblast(radius, 2, 0.55); | |
fragColor.a *= OPACITYMULTIPLIER; | |
fragColor.a *= v_teamcolor.a; | |
} | |
]] | |
local luaShaderDir = "LuaUI/Widgets/Include/" | |
local LuaShader = VFS.Include(luaShaderDir.."LuaShader.lua") | |
VFS.Include(luaShaderDir.."instancevbotable.lua") | |
local commblastSphereVBO | |
local function initFogGL4(shaderConfig, DPATname) | |
if commblastSphereShader then | |
commblastSphereShader:Finalize() | |
end | |
local engineUniformBufferDefs = LuaShader.GetEngineUniformBufferDefs() | |
vsSrc = vsSrc:gsub("//__ENGINEUNIFORMBUFFERDEFS__", engineUniformBufferDefs) | |
vsSrc = vsSrc:gsub("FULLRADIUS", shaderConfig.FULLRADIUS) | |
vsSrc = vsSrc:gsub("TEAMCOLORED", shaderConfig.TEAMCOLORED) | |
fsSrc = fsSrc:gsub("//__ENGINEUNIFORMBUFFERDEFS__", engineUniformBufferDefs) | |
fsSrc = fsSrc:gsub("BLASTRADIUS", shaderConfig.BLASTRADIUS) | |
fsSrc = fsSrc:gsub("OPACITYMULTIPLIER", shaderConfig.OPACITYMULTIPLIER) | |
fsSrc = fsSrc:gsub("BLASTDMG", shaderConfig.BLASTDMG) | |
commblastSphereShader = LuaShader( | |
{ | |
vertex = vsSrc:gsub("//__DEFINES__", "#define MYGRAVITY " .. tostring(Game.gravity + 0.1)), | |
fragment = fsSrc:gsub("//__DEFINES__", "#define USE_STIPPLE " .. tostring(0)), | |
uniformInt = { | |
mapDepths = 0, | |
}, | |
uniformFloat = { | |
fadeDistance = 300000, | |
}, | |
shaderConfig = shaderConfig | |
}, | |
"Commblast Range GL4" | |
) | |
commblastSphereShader:Initialize() | |
if not commblastSphereShader then | |
Spring.Echo("Error: Commblast Range GL4 shader not initialized") | |
widgetHandler:RemoveWidget() | |
return | |
end | |
local sphereVBO, _, sphereIndexVBO, _ = makeSphereVBO(shaderConfig.SPHERESEGMENTS, shaderConfig.SPHERESEGMENTS/2, 1) | |
DrawPrimitiveAtUnitVBO = makeInstanceVBOTable( | |
{ | |
{id = 3, name = 'params_alphastart_alphaend_gameframe', size = 4}, | |
{id = 4, name = 'instData', type = GL.UNSIGNED_INT, size = 4}, | |
}, | |
64, -- maxelements | |
DPATname .. "VBO", -- name, | |
4 --unitIDattribID | |
) | |
DrawPrimitiveAtUnitVBO:makeVAOandAttach(sphereVBO,DrawPrimitiveAtUnitVBO.instanceVBO, sphereIndexVBO) | |
return DrawPrimitiveAtUnitVBO, commblastSphereShader | |
end | |
local cacheTable = {0,0,0,0,0,0,0,0} | |
local function AddBlastSphere(unitID, noUpload, oldopacity, newopacity, gameframe) | |
--Spring.Echo("Added a unit") | |
cacheTable[1] = oldopacity | |
cacheTable[2] = newopacity | |
cacheTable[3] = gameframe | |
return pushElementInstance(commblastSphereVBO, | |
cacheTable, | |
unitID, true, noUpload, unitID) | |
end | |
function widget:RecvLuaMsg(msg, playerID) | |
--Spring.Echo("widget:RecvLuaMsg",msg) | |
if msg:sub(1,18) == 'LobbyOverlayActive' then | |
chobbyInterface = (msg:sub(1,19) == 'LobbyOverlayActive1') | |
end | |
end | |
function widget:DrawWorldPreUnit() | |
if chobbyInterface then return end | |
if spIsGUIHidden() then return end | |
if commblastSphereShader.shaderObj ~= nil and commblastSphereVBO.usedElements > 0 then | |
--Spring.Echo(commblastSphereVBO.usedElements) | |
glCulling(GL.FRONT) | |
glDepthTest(GL.LEQUAL) | |
glDepthTest(false) | |
gl.DepthMask(false) | |
gl.Texture(0, "$map_gbuffer_zvaltex") | |
commblastSphereShader:Activate() | |
commblastSphereVBO:Draw() | |
commblastSphereShader:Deactivate() | |
glTexture(0, false) | |
glCulling(false) | |
glDepthTest(false) | |
end | |
end | |
local myTeamID | |
--- Look how easy api_unit_tracker is to use! | |
function widget:VisibleUnitAdded(unitID, unitDefID, unitTeam) | |
--Spring.Echo("VisibleUnitAdded", unitID, unitDefID, unitTeam) | |
if commDefIds[unitDefID] and unitTeam == myTeamID then | |
commanders[unitID] = {draw = false, oldopacity = 0.0, newopacity = 0.0} | |
end | |
end | |
function widget:VisibleUnitsChanged(extVisibleUnits, extNumVisibleUnits) | |
clearInstanceTable(commblastSphereVBO) -- clear all instances | |
for unitID, unitDefID in pairs(extVisibleUnits) do | |
widget:VisibleUnitAdded(unitID, unitDefID) | |
end | |
end | |
function widget:VisibleUnitRemoved(unitID) -- remove the corresponding ground plate if it exists | |
commanders[unitID] = nil | |
if commblastSphereVBO.instanceIDtoIndex[unitID] then | |
popElementInstance(commblastSphereVBO, unitID) | |
end | |
end | |
function widget:GameFrame(n) | |
-- This is where we update the alphas of the spheres | |
-- also based on health! | |
-- we gotta do this every 15 frames, and do interpolation in-shader for optimum efficiency | |
-- check com movement, also try to draw as little as possible! | |
-- api_unit_tracker will take care of removing invisible commanders for us | |
-- but we have to add/update them here manually! | |
if n%15 ~= 0 then return end | |
-- in the first pass, identify which ones we want to draw, and set their new opacities accordingly | |
-- do not draw if any: | |
-- comm above ground | |
-- not in view | |
-- no enemies in range | |
for unitID, params in pairs(commanders) do | |
local x, y, z = spGetUnitPosition(unitID) | |
local draw = false | |
local newopacity = 0 | |
if x then | |
local groundHeight = spGetGroundHeight(x,z) | |
draw = true | |
if y - groundHeight > 10 then | |
draw = false | |
elseif not spIsSphereInView(x,y,z,blastRadius) then | |
draw = false | |
end | |
-- Spring.Echo(draw, groundHeight) | |
if draw then | |
draw = false | |
local nearbyEnemyUnits = spGetUnitsInCylinder(x, z, showOnEnemyDistance + fadeInDistance, Spring.ENEMY_UNITS) | |
if nearbyEnemyUnits then | |
for _, v in pairs(nearbyEnemyUnits) do | |
if commDefIds[spGetUnitDefID(v)] then | |
local ex,ey,ez = spGetUnitPosition(v) | |
if ex then | |
local distance = diag(x-ex, y-ey, z-ez) | |
if distance < (showOnEnemyDistance + fadeInDistance) then | |
draw = true | |
newopacity = 1.0 - math.min(1.0, math.max(0.0, (distance - showOnEnemyDistance) / fadeInDistance)) | |
end | |
end | |
end | |
break | |
end | |
end | |
end | |
end | |
params.draw = draw | |
params.oldopacity = params.newopacity | |
params.newopacity = newopacity | |
end | |
clearInstanceTable(commblastSphereVBO) | |
local gameframe = Spring.GetGameFrame() | |
for unitID, params in pairs(commanders) do | |
if params.draw then | |
--Spring.Echo("Drawing blastsphere for", unitID, params.draw,params.newopacity, params.oldopacity) | |
AddBlastSphere(unitID, true, params.oldopacity, params.newopacity, gameframe) | |
end | |
end | |
uploadAllElements(commblastSphereVBO) | |
end | |
function widget:Initialize() | |
myTeamID = spGetMyTeamID() | |
commblastSphereVBO, commblastSphereShader = initFogGL4(shaderConfig, "commblastSpheres") | |
--Spring.Echo(Spring.HaveShadows(),"advshad",Spring.HaveAdvShading()) | |
if WG['unittrackerapi'] and WG['unittrackerapi'].visibleUnits then | |
widget:VisibleUnitsChanged(WG['unittrackerapi'].visibleUnits, nil) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment