This continues the tutorial from Part 2.
Lesson 6: Adding a switch (button, dial, etc.)
Apart from MFDs, the panel can contain many more active elements. A common class of elements has two or more discrete states (a switch that can be flipped up or down, a dial that can be turned to a set of discrete positions, a control light that can be on or off, etc.) Representative for all those objects, let’s add a 2-way switch now.
Active objects that have a number of discrete visual states are generally defined by one of a set of bitmaps representing the state. We can implement this in two ways: (a) blitting the bitmap displaying the required state into the background panel texture, or (b) putting a billboard texture group over the background mesh, and dynamically modifying the texture coordinates to point to a section of the texture containing an image of the required state.
There are advantages and disadvantages to both methods: (a) doesn’t increase the mesh complexity. Once the blitting operation is performed, the rendering process is as efficient as before. This method is best if the object state only changes very infrequently. (b) doesn’t require a blit, just a modification to the vertex texture coordinates. This method is best if the object state changes continuously (so that the overhead of the blitting operation becomes significant).
A switch usually changes its state only occasionally (on user input), so method (a) should be suitable and will be implemented here. I will discuss an example for method (b) in the next part.
As with the MFD button columns, the switch is defined as a class derived from PanelElement:
As before, we have the Redraw2D and ProcessMouse2D functions to deal with updating the visual state and responding to user interaction. They can be implemented as follows:
GetMySwitchState and SetMySwitchState are assumed to be implemented in MyVessel. In Redraw2D, btn_x and btn_y represent the target location of the button in the background texture. tx_x0 and tx_y0 are the locations of the button image in its “base state”, tx_w and tx_h are the width and height of the button in pixels. The images for the additional states are assumed to be in a horizontal row to the right of the base state, so that tx_x0 + switch_state*tx_dx picks out the required one.
In ProcessMouse2D, we flip the switch depending on whether the user clicks the top or bottom half of the switch. Returning true triggers a redraw event.
We add the switch as a new panel element in the constructor (after increasing the length of the pel array to 3):
and register the switch as an active element inside DefineMainPanel:
That’s it. The redraw and mouse event callback functions we have defined for the MFD button columns in the last section already take care of calling the appropriate MySwitch methods when needed.
Note: If you are implementing an entire row of switches, it is probably more efficient to define the row as a single panel element, rather than creating a new object for each individual switch.
The next part will implement a slider using mesh vertex transformations.
Lesson 6: Adding a switch (button, dial, etc.)
Apart from MFDs, the panel can contain many more active elements. A common class of elements has two or more discrete states (a switch that can be flipped up or down, a dial that can be turned to a set of discrete positions, a control light that can be on or off, etc.) Representative for all those objects, let’s add a 2-way switch now.
Active objects that have a number of discrete visual states are generally defined by one of a set of bitmaps representing the state. We can implement this in two ways: (a) blitting the bitmap displaying the required state into the background panel texture, or (b) putting a billboard texture group over the background mesh, and dynamically modifying the texture coordinates to point to a section of the texture containing an image of the required state.
There are advantages and disadvantages to both methods: (a) doesn’t increase the mesh complexity. Once the blitting operation is performed, the rendering process is as efficient as before. This method is best if the object state only changes very infrequently. (b) doesn’t require a blit, just a modification to the vertex texture coordinates. This method is best if the object state changes continuously (so that the overhead of the blitting operation becomes significant).
A switch usually changes its state only occasionally (on user input), so method (a) should be suitable and will be implemented here. I will discuss an example for method (b) in the next part.
As with the MFD button columns, the switch is defined as a class derived from PanelElement:
Code:
class MySwitch: public PanelElement {
public:
MySwitch (VESSEL3 *v);
bool Redraw2D (SURFHANDLE surf);
bool ProcessMouse2D (int event, int mx, int my);
private:
int switch_state;
};
Code:
MySwitch::MySwitch (VESSEL3 *v): PanelElement (v)
{
switch_state = 0;
// this is the base state as shown in the panel background
}
bool MySwitch::Redraw2D (SURFHANDLE surf)
{
int new_switch_state = GetMySwitchState();
if (new_switch_state != switch_state) {
switch_state = new_switch_state;
tx_x = tx_x0 + switch_state*tx_dx;
oapiBlt (surf, surf, btn_x, btn_y, tx_x, tx_y0, tx_w, tx_h);
}
return false;
}
bool MySwitch::ProcessMouse2D (int event, int mx, int my)
{
int curr_switch_state = GetMySwitchState();
int new_switch_state;
if (my < tx_h/2) new_switch_state = 0;
else new_switch_state = 1;
if (new_switch_state != curr_switch_state) {
SetMySwitchState (new_switch_state);
return true;
} else
return false;
}
In ProcessMouse2D, we flip the switch depending on whether the user clicks the top or bottom half of the switch. Returning true triggers a redraw event.
We add the switch as a new panel element in the constructor (after increasing the length of the pel array to 3):
Code:
MyVessel::MyVessel (...)
{
...
for (int i = 0; i < 2; i++)
pel[i] = new MFDButtonCol (this, i);
pel[2] = new MySwitch (this);
...
}
Code:
void MyVessel::DefineMainPanel (PANELHANDLE hPanel)
{
...
RegisterPanelArea (hPanel, AID_MYSWITCH, _R(btn_x, btn_y, tx_w, tx_h),
PANEL_REDRAW_MOUSE, PANEL_MOUSE_LBDOWN, panel2dtex, pel[2]);
...
}
Note: If you are implementing an entire row of switches, it is probably more efficient to define the row as a single panel element, rather than creating a new object for each individual switch.
The next part will implement a slider using mesh vertex transformations.