Dev #4 – Divine Drive Addendum

Hello everyone.

I hope you’re having a great day.

I wanted to quickly revisit the Divine Drive post from yesterday. I briefly mentioned using image filtering to render the Divine Drive on the fly.

Back on track, I think I’d like to make more entries in the sprite sheet to get more variations I can use for the wheel. Or maybe I could do something like a conical gradient and filter it. Not sure. I’m leaving that for next time because it’s late and I am tired of looking at glorified pie charts.

-Some Bozo 2024

I actually took on this task on stream yesterday (check me out on Twitch) and managed to get it working. Here’s how it looks now (animated to drain automatically)

I used a conical gradient with image filtering to achieve this effect. Here’s the source file:

When the renderer draws the Divine Drive, it starts by filtering this source image using a high-pass filter. The threshold value is based on the percentage of available power or capacity (depending on which portion it is drawing). So, for example, if you have 50% of your power left, the left half of the Drive will be white.

Threshold = (1 – chargePercent) * 255

Any value in the source image above this threshold will appear as a specific Set Color (which is white for the available power and a mid-gray for exhausted power).

If your remaining capacity is low enough, the output will be tinted red.

Here is the code that handles rendering a portion of the drive.

// -- Draw the capacity of the drive --
filter->setThresholdColor(IEColorF(capacityLeftPortion, capacityLeftPortion, capacityLeftPortion, 0.01));
filter->setSetColor(IEColorF(0.25, 0.25 * (!isRedTint), 0.25 * (!isRedTint), 1));
filter->setAlphaMode(ALPHA_1);

if (capacLeft != lastCapacityAmount || !lastCapacity) {

	imageFilterResult_t res = filter->filter(*dDrive2);

	if (res.code != FilterFailure) {
		if (lastCapacity) {
			lastCapacity->destroy();
			delete lastCapacity;
		}
		lastCapacity       = res.result;
		lastCapacityAmount = capacLeft;
	}
}

graphics->renderer->drawImage(imgRct, *lastCapacity );

Due to performance concerns, we actually cache the last image that was drawn (since it is unlikely you will be using power on any given frame) to avoid having to redraw it. Additionally, to save extra time, we update the Set Color of the threshold function instead of tinting afterwards because the tint function is slow.

Now, drawing the Divine Drive only takes 60us on my setup, as opposed to about 140us. Additionally, it uses far less memory ( 6.25kB * (2 buffers + 4 symbols) = 37.5 as opposed to the (8 images X * 40 wide * 4 images Y * 40 height * 4 bytes per pixel = 200kB), and now has a number of states only limited by the size of the circle itself as well as the 255 discrete values each pixel can be.

The only thing we have left to do is implement the glow when you have full power.

This system may be useful in implementing a health bar using the same process. Maybe we’ll take a look at that in the next devblog.

Thanks for reading.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *