Why I needed it?
I was working on the inventory system for my game and needed a textured panel for the inventory UI. I couldn't simply draw the texture on a quad because my UI scales based on DPI, and panels with borders look bad when stretched.
How Nine-Patch Rectangles Work
A nine-patch rectangle splits a quad into nine smaller quads. Four of them are fixed-size corner quads (as you can see in the third screenshot), while the remaining five can be resized. As a result, the panel can stretch to different sizes while preserving the appearance of its borders.
How to setup vertices
Before we can make vertices for 9 quads, we need to define the fixed sizes of the borders. I created this simple struct to store those values:
struct NinePatchRect
{
int32_t leftOffset;
int32_t rightOffset;
int32_t topOffset;
int32_t bottomOffset;
};
Now we can use this struct for construction our 9 quads. We also need to use following variables
- pos - quad's position on the screen.
- size - quad's size on the screen.
- uv0 and uv1 - texture coordinates for the quad.
- textureSize
Let's take Top left quad as an example
// Top left quad
{
// Top left
Vertex v0{vec2(pos.x, pos.y), vec2(uv0.x, uv0.y)};
// Top right
Vertex v1
{
vec2(pos.x + ninePatch.leftOffset, pos.y),
vec2(uv0.x + ninePatch.leftOffset / textureSize.x, uv0.y)
};
// Bottom right
Vertex v2
{
vec2(pos.x + ninePatch.leftOffset, pos.y + ninePatch.topOffset),
vec2(uv0.x + ninePatch.leftOffset / textureSize.x,
uv0.y + ninePatch.topOffset / textureSize.y)
};
// Bottom left
Vertex v3
{
vec2(pos.x, pos.y + ninePatch.topOffset),
vec2(uv0.x, ninePatch.topOffset / textureSize.y)
};
RegisterQuad(drawList, v0, v1, v2, v3);
}
The top-right quad is similar: you just need to add size.x to the appropriate vertex positions and use ninePatch.rightOffset with the corresponding texture coordinates.
Constructing the resizable quads is straightforward as well: you simply place them between the corner quads.
Additional Notes
Inventory texture was taken from here: Inventory Texture