Draw HUD status bar element with shader and bounding box?

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Draw HUD status bar element with shader and bounding box?

Postby argv » Tue Oct 03, 2017 5:57 pm

I want to use ZScript to draw its HUD bars with a horizontal skew proportional to the height of the bar, regardless of how much of the bar is filled. I've put together this web-based demonstration (warning: doesn't show correctly in Microsoft IE/Edge) to show you what I have and what I want. The middle bar shows what I want; the bottom bar shows what I have.

Aetherius draws bars like I want on its HUD, by drawing each 1-pixel-wide sliver of the bar with a separate draw call. This is very slow. I'd like to help with that by skewing them with a shader instead, which should be much faster.

But I've got a problem. Currently, an ordinary texture can be drawn on the status bar, a shader can be attached to it via GLDEFS, and it can skew the texture just fine—but the horizontal skew is proportional to the scaled width of the texture, not the height! The result is like the bottom bar in the above demonstration.

This is my shader:

Code: Select allExpand view
const mat2 shearMatrix = mat2(1, 20./220., 0, 1);
const vec4 trans = vec4(0., 0., 0., 0.);

vec4 Process(vec4 color) {
   vec2 xy = gl_TexCoord[0].st;
   xy -= .5;
   xy.x *= 1. + (20. / 220.);
   xy *= shearMatrix;
   xy += .5;
   if (xy.x < 0. || xy.x > 1.)
      return trans * color;
      return getTexel(xy) * color;

I have it attached to a 1×20 texture named “HPBFILNV” in GLDEFS. I'm drawing it from a ZScript BaseStatusBar implementation like so:

Code: Select allExpand view
SetSize(0, 1440, 1080);

int value = CPlayer.Health;
int MaxValue = CPlayer.mo.GetMaxHealth(true);
int MaxFillWidth = 200;
TextureID Fill = TexMan.CheckForTexture("HPBFILNV", TexMan.Type_Any);
Vector2 Pos = (250, -175);

double scale = double(value) / double(MaxValue);
double underScale = min(scale, 1.0) * MaxFillWidth;

DrawTexture(Fill, Pos, DrawFlags, scale: (underScale, 1));

As you can see, the 1×20 texture is scaled horizontally to fill desired rectangular area. Then the shader is run over it to produce the (incorrectly) skewed bar.

Does anyone have any idea how to make it skew like I want? Is it possible, under the current constraints of GZDoom custom texture shaders?
Joined: 30 Aug 2016

Re: Draw HUD status bar element with shader and bounding box

Postby Gutawer » Thu Oct 05, 2017 9:43 am

I'd imagine your issue is because the shader's coordinates are between 0 and 1, so shearing the vector is going to be fucked up once the texture is scaled. Ideally you'd need to scale the texture coordinates by the scaled size of the texture (then scale back to get it in range 0-1), but I'm not entirely sure if that's possible.
However, if all you want to do is draw a sheared gradient, here's a different approach - in a for loop, create a vector which keeps track of the starting position of the current line of pixels, and for every loop, draw the line of pixels using the HUD Fill function, then subtract 1 from the x counter (to go back 1 x coordinate, for the shear effect), and subtract 1 from the y counter to go down one pixel. Then, draw the next line, and repeat until you've drawn the entire bar - the width of the lines of pixels will represent how much of the total bar is drawn. For the gradient effect, you can fairly easily interpolate between two colors. It wouldn't be as fast as a shear on the GPU, but it has another advantage of being software-compatible, and it shouldn't be that slow - for example, if you have an 8 pixel tall bar, you only need to call Fill 8 times.
User avatar
User Accounts Assistant
Joined: 16 Apr 2016
Discord: Gutawer#3431

Re: Draw HUD status bar element with shader and bounding box

Postby argv » Fri Oct 06, 2017 4:36 pm

Brilliant! That'd be 20 draw calls! It's, yeah, not as fast as drawing the whole thing with a shader, but 20 draw calls is a lot better than 100+. Also, no texture, which I assume will improve performance a little. Thanks!
Joined: 30 Aug 2016

Return to Scripting

Who is online

Users browsing this forum: Majestic-12 [Bot] and 0 guests