In my previous post I have described how the gradient helped to solve few issues when creating skins for the GUI system. This time I would like to explain how the gradient is calculated.
Previous generation GUI system was really primitive when it comes to drawing shapes. The biggest problem was probably calculating the color gradients. Because the shapes were created by raw GL_POLYGON data, each point parameters should be specified. This means for each point I had to write its UV coordinates, color value and position. Calculating the color was one of the most problematical parts of this process.
Let’s for example say I have a simple rectangle with 4 points.

Now If I wanted to make a good looking gradient color, I had to manually calculate each point. For such simple rectangle this is not hard.
For example let's pick the gradient top color
RED=RGBA{1.0,0.0,0.0,1.0} and bottom with 0.0 alpha
RED={1.0,0.0,0.0,0.0}I can easily calculate the 4 points of the shape:
Point1 = RGBA{1.0,0.0,0.0,1.0}
Point2 = RGBA{1.0,0.0,0.0,1.0}
Point3 = RGBA{1.0,0.0,0.0,0.0}
Point4 = RGBA{1.0,0.0,0.0,0.0}
The result is something like this:

Usually however more complex shapes are used to define appearance of the GUI widgets. Take a simple rectangle with an arc on one of the edges. This makes calculation difficult especially when some day you would like to change the vertical gradient color to horizontal or even to linear gradient with angular rotation. Calculating colors now is really the job you would want to do the least.

The new GUI system tries to solve this problem, by calculating the colors automatically. Because the drawing of shapes is abstracted (user is using lines, circles, rectangles not the GL_POLYGON points directly) it is possible for the GUI system to calculate the points.
The only thing that user needs to define is the initial 2 gradient colors (start, end) and gradient type or angular rotation. The GUI system would do everything else.
I would like to describe this in more detail, how the vertex colors are calculated.
I am using 2 basic points for this. These points hold the initial color values and also the position relative to each other. This makes it possible to create different gradient widths and heights.

The downside is that because we are dealing with vertex points we loose some color values, depending on the number of total vertexes. To better illustrate this imagine the circular gradient that is applied to simple 4 vertex rectangle. Because there are only 4 points it is not possible to describe the transition between them the same way as per pixel calculated image. The only way to improve this is to increase vertex size on the sides and with the shape itself. Another way is to apply texture . In the new GUI system however I decided not to use spherical gradient calculation because of these issues.
So right now I have only 3 gradient types – vertical , horizontal and angular.
The Vertical gradient type is one of the most simplest.
Each shape is created from line. Each line has 2 points. Basically the function loops and calculates color for each point.
Before starting to calculate, we need to get 2 base gradient points – the most top left and bottom right points of the shape.
The position of these points is relative to the maximum size of the shape so I use the shape left,top,width,height values for this.
//here are positions of the gradient pointsRenderVertex gradientPos1 = GetVertexPosition( left , top , width , height , gradientPoint1 );
RenderVertex gradientPos2 = GetVertexPosition( left , top , width , height , gradientPoint2 );
As a result I get absolute position x,y on the screen of each point
When I have these 2 points I can find the distance between them in absolute coordinates:
//total horizontal distance between gradient points
distv = (gradientPos2.Y - gradientPos1.Y) ;
//ignore the sign value just incase
distv = fabs( distv );

And when I got this, I can calculate the relative distance between the first top left points and the current point I am calculating:
//now find the position of the point between them
if( distv == 0.0f )
{
posv = 0.0f;
}
else
{
posv = (guiPoint.Y - gradientPos1.Y ) / distv;
}

Now when I have this posv I can apply it and find the result color interpolation:
//calculate the color value now with simple interpolation
result.R = gradColor1.R * (1.0f - posv) + gradColor2.R * posv;
result.G = gradColor1.G * (1.0f - posv) + gradColor2.G * posv;
result.B = gradColor1.B * (1.0f - posv) + gradColor2.B * posv;
result.A = gradColor1.A * (1.0f - posv) + gradColor2.A * posv;
The same way it works with the horizontal gradient type. This time however instead of using Height we use Width of the shape, but the process itself is exactly the same.

A more complicated is the angular gradient calculation. For this we need additional parameter – angle in degrees. The user specifies the angle within range 0...360.
We start the same way when calculatin vertical or horizontal gradient, but calculate both distances:
//Find horizontal position first
//total horizontal distance between gradient points
disth = (gradientPos2.X - gradientPos1.X) ;
//ignore the sign value just incase
disth = fabs( disth );
//now find the position of the point between gradient points
if( disth == 0.0f )
{
posh = 0.0f;
}
else
{
posh = (guiPoint.X - gradientPos1.X ) / disth;
}
//Now find vertical position
//total vertical distance between gradient points
distv = (gradientPos2.Y - gradientPos1.Y) ;
//ignore the sign value just incase
distv = fabs( distv );
//now find the position of the point between gradient points
if( distv == 0.0f )
{
posv = 0.0f;
}
else
{
posv = (guiPoint.Y - gradientPos1.Y ) / distv;
}
The only thing left to do, is to calculate the rotated distances:
//make the angle within requested range
fangle = fabs(angle);
if( fangle > 360.0f)
{
fangle = 0.0f;
}
//the gltriangle size is 2Pi
fangle = angle * 2;
// 0 .. 90
if( (fangle >= 0)&&(fangle < 180)
//now rotate the point
posRot = posh * trig.GLTriangle( fangle + 180 ) + posv * trig.GLTriangle( fangle );
}
else
// 90 .. 180
if( (fangle >= 180)&&(fangle < 360)
//now rotate the point
posRot = (1.0f - posh) * trig.GLTriangle( fangle + 180 ) + posv * trig.GLTriangle( fangle );
}
else
// 180 .. 270
if( (fangle >= 360)&&(fangle < 540)
//now rotate the point
posRot = (1.0f - posh) * trig.GLTriangle( fangle + 180 ) + (1.0f - posv) * trig.GLTriangle( fangle );
}
else
// 270 .. 0
if( (fangle >= 540) )
{
//now rotate the point
posRot = posh * trig.GLTriangle( fangle + 180 ) + (1.0f - posv) * trig.GLTriangle( fangle );
}
The trig.GLTriangle is a precalculated table of cos and sin values. I also change the sign depending on what angle is currently selected.
With the posRot value I can get the required color value:
result.R = gradColor1.R * (1.0f - posRot) + gradColor2.R * posRot;
result.G = gradColor1.G * (1.0f - posRot) + gradColor2.G * posRot;
result.B = gradColor1.B * (1.0f - posRot) + gradColor2.B * posRot;
result.A = gradColor1.A * (1.0f - posRot) + gradColor2.A * posRot;
That's it.

This is only a small feature of the new GUI system, however it makes a big difference when creating GUI skins, because it takes significiant portion of the work from the designer.