HUD Tutorial (Flight Director)

From OrbiterWiki
Jump to: navigation, search

This tutorial explains how to add a flight director icon into the HUD of your vessel. The example code is from the Sirius SDHLV.

For doing so, you need three error angles for the projection: Pitch error, yaw error and bank error. You have to define them somehow in your vessel, either as member variables or have a member object in your vessel which does the calculations.

The starting point for any HUD modification is in the callback function clbkDrawHUD(). Here, you can enter your own code, or call the callback function of VESSEL2 for using the default HUD drawing. For keeping the code in the function understandable, i recommend delegating the actual drawing into other member functions or child objects.

The callback uses three parameters:

iMode
The current HUD mode. You can also define your own HUD modes, but remember that new standard HUD modes might get implemented in Orbiter in the future.
pHPS
The HUD paint specification is a structure which contains all information you need to know about the screen size.
hdc
The device context handle is neccessary for using the windows painting functions.

If you delegate the information to other functions or objects, you only need the parameters pHPS and hdc, iMode is optional.

In the Sirius, the callback function looks like that:

void SDLV110::clbkDrawHUD(int iMode, const HUDPAINTSPEC* pHPS, HDC hdc)
{
	VESSEL2::clbkDrawHUD(iMode, pHPS, hdc);
	HUD_DrawVesselState(pHPS, hdc);
	HUD_DrawLast3Events(pHPS, hdc);
	
	switch(iStructState)
	{
	case 0:
	case 1:
		HUD_DrawSSMEState(pHPS, hdc);
		break;
	case 2:
		HUD_DrawStage2RCS(pHPS, hdc);
		break;
	}
	switch(iMode)
	{
	case HUD_SURFACE:
		HUD_DrawFlightDirector(pHPS, hdc);
		break;
	case HUD_NONE:
		break;
	}

}

Depending on current vehicle configuration (iStructState) and the HUD mode, different drawing functions get called for painting the icons and information.

The interesting function for our example is HUD_DrawFlightDirector(), which contains the drawing code for the flight director symbol.

The function all together looks like that:

void SDLV110::HUD_DrawFlightDirector(const HUDPAINTSPEC* pHPS, HDC hdc)
{
	int iX = pHPS->CX + (int)(fYawError * DEG * pHPS->Scale);
	int iY = pHPS->CY - (int)(fPitchError * DEG * pHPS->Scale);
	int iR = pHPS->Markersize / 2;
	Ellipse(hdc, iX - iR, iY - iR, iX + iR, iY + iR);
	//two lines for indicating Roll Error
	int iX1 = (int)(1.0 * pHPS->Markersize * cos(fBankError));
	int iX2 = (int)(0.5 * pHPS->Markersize * cos(fBankError));
	int iY1 = (int)(1.0 * pHPS->Markersize * sin(fBankError));
	int iY2 = (int)(0.5 * pHPS->Markersize * sin(fBankError));
	MoveToEx(hdc, iX + iX1, iY + iY1, NULL);
	LineTo(hdc, iX + iX2, iY + iY2);

	MoveToEx(hdc, iX - iX1, iY - iY1, NULL);
	LineTo(hdc, iX - iX2, iY - iY2);

	//Top Marker for indicating Roll Error
	iX1 = (int)(0.8 * pHPS->Markersize * sin(fBankError));
	iX2 = (int)(0.5 * pHPS->Markersize * sin(fBankError));
	iY1 = (int)(0.8 * pHPS->Markersize * cos(fBankError));
	iY2 = (int)(0.5 * pHPS->Markersize * cos(fBankError));
	MoveToEx(hdc, iX + iX1, iY - iY1, NULL);
	LineTo(hdc, iX + iX2, iY - iY2);
}

Lets take a closer look at a few operations:

	int iX = pHPS->CX + (int)(fYawError * DEG * pHPS->Scale);
	int iY = pHPS->CY - (int)(fPitchError * DEG * pHPS->Scale);
	int iR = pHPS->Markersize / 2;

Here, we calculate the center position (iX, iY) and the size of the symbol (iR). pHPS->Scale tells us how many pixels on the screen make one degree on the display (depending on zoom factor). With pHPS->Markersize we get the standard size of a symbol on the HUD (the Flight path indicator) in pixels.

With this data, we can use the windows paint functions for drawing the symbol.

	//two lines for indicating Roll Error
	int iX1 = (int)(1.0 * pHPS->Markersize * cos(fBankError));
	int iX2 = (int)(0.5 * pHPS->Markersize * cos(fBankError));
	int iY1 = (int)(1.0 * pHPS->Markersize * sin(fBankError));
	int iY2 = (int)(0.5 * pHPS->Markersize * sin(fBankError));
	MoveToEx(hdc, iX + iX1, iY + iY1, NULL);
	LineTo(hdc, iX + iX2, iY + iY2);

	MoveToEx(hdc, iX - iX1, iY - iY1, NULL);
	LineTo(hdc, iX - iX2, iY - iY2);

Here, we draw the two "wing" lines. The math behind it is simple trigonometry, if you are not good friends with the trigometric functions, this is the right moment to refresh your knowledge - such functions will get used in orbiter very often, and it really simplifies writing code when you can work effectivly with them.

Windows has the following logic behind its line drawing algorithm: You move a virtual "turtle" at a position and let it draw lines. The end point of each line can also be used as start of a new line, so for drawing polylines, you only need to move the "turtle" to the starting point and draw the lines in sequence.