This is the part that I was really hoping for some guidance on.
Oh... I see.
The theory is not that hard : somehow, we need to tell Orbiter to detects when clicks happens, and notify us.
One part of the job é to define that "clickable" areas and tell Orbiter about then. Another part of the job is to handle the event that Orbiter raises to us when that specific area is clicked.
The tedious part is to know what parts of the screen should "hold" the desired clickable area...
Going back to the Orbiter's API manual, searching the PDF for "mouse" I found this:
Code:
7.54.1.2 OAPIFUNC void oapiVCRegisterArea (int id, const RECT & tgtrect, int draw_event, int mouse_event, int bkmode, SURFHANDLE tgt)
Define an active area in a virtual cockpit. Active areas can be repainted. This function is similar to oapiRegisterPanelArea.
Parameters:
id area identifier
tgtrect bounding box of the active area in the target texture (pixels) draw_event redraw condition (see draw events)
mouse_event mouse event ( see mouse events)
bkmode background mode (see bkmodes)
tgt target texture to be updated
So now I have something to search for on Delta Glider sources:
Code:
oapiVCRegisterArea (AID_MFD1_LBUTTONS, _R(112, 214, 255, 224), PANEL_REDRAW_USER, PANEL_MOUSE_LBDOWN|PANEL_MOUSE_LBPRESSED|PANEL_MOUSE_ONREPLAY, PANEL_MAP_BACKGROUND, tex1);
oapiVCSetAreaClickmode_Quadrilateral (AID_MFD1_LBUTTONS, _V(-0.2301,1.1592,7.3322), _V(-0.2161,1.1592,7.3322), _V(-0.2301,1.0302,7.2852), _V(-0.2161,1.0302,7.2852));
oapiVCRegisterArea (AID_MFD1_RBUTTONS, _R(112, 224, 255, 234), PANEL_REDRAW_USER, PANEL_MOUSE_LBDOWN|PANEL_MOUSE_LBPRESSED|PANEL_MOUSE_ONREPLAY, PANEL_MAP_BACKGROUND, tex1);
oapiVCSetAreaClickmode_Quadrilateral (AID_MFD1_RBUTTONS, _V(-0.023942,1.1592,7.3322), _V(-0.009927,1.1592,7.3322), _V(-0.023942,1.0302,7.2852), _V(-0.009927,1.0302,7.2852));
....
oapiVCRegisterArea (AID_MFD1_PWR, PANEL_REDRAW_NEVER, PANEL_MOUSE_LBDOWN|PANEL_MOUSE_ONREPLAY);
oapiVCSetAreaClickmode_Spherical (AID_MFD1_PWR, _V(-0.1914,1.009,7.2775), 0.01);
oapiVCRegisterArea (AID_MFD1_SEL, PANEL_REDRAW_NEVER, PANEL_MOUSE_LBDOWN|PANEL_MOUSE_ONREPLAY);
oapiVCSetAreaClickmode_Spherical (AID_MFD1_SEL, _V(-0.0670,1.009,7.2775), 0.01);
oapiVCRegisterArea (AID_MFD1_MNU, PANEL_REDRAW_NEVER, PANEL_MOUSE_LBDOWN|PANEL_MOUSE_ONREPLAY);
oapiVCSetAreaClickmode_Spherical (AID_MFD1_MNU, _V(-0.0485,1.009,7.2775), 0.01);
The API definition for that calls are:
Code:
/**
* \brief Define an active area in a virtual cockpit. Active areas can be repainted. This function is
* similar to oapiRegisterPanelArea.
* \param id area identifier
* \param tgtrect bounding box of the active area in the target texture (pixels)
* \param draw_event redraw condition (see \ref register_p_a "draw events")
* \param mouse_event mouse event ( see \ref panel_mouse "mouse events")
* \param bkmode background mode (see \ref register_p_a "bkmodes")
* \param tgt target texture to be updated
* \note The target texture can be retrieved from a mesh by using the
* oapiGetTextureHandle() method. Dynamic textures must be marked with flag "D" in the mesh file.
* \note Redraw events can be used not only to update mesh textures dynamically,
* but also to animate mesh groups, or edit mesh vertices or texture coordinates.
* \note If no dynamic texture repaints are required during redraw events, use the
* alternative version of oapiVCRegisterArea() instead.
* \note To define a mouse-sensitive volume in the virtual cockpit, use one of the
* \a oapiVCSetAreaClickmode_XXX functions.
*/
OAPIFUNC void oapiVCRegisterArea (int id, const RECT &tgtrect, int draw_event, int mouse_event, int bkmode, SURFHANDLE tgt);
And pressing F12 on RECT, we have:
Code:
typedef struct tagRECT
{
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;
The process is not different from any other control (as buttons, levers, sliders, etc), as everything else is defined together at the same place.
As we can see, we use TWO calls to define a control: the oapiVCRegisterArea where we, well, register a UNIQUE NUMBER (we use defines to that, press F2 on the AID_MFD1_LBUTTONS symbol and see for yourself) and tells Orbiter what to (and who to do it) when this happens, and oapiVCSetAreaClickmode_Quadrilateral or oapiVCSetAreaClickmode_Spherical, where we define the geometric bounds in which the click for that identifier should be monitored. As the name says, you can define a quadrilateral or a sphere to define such boundings.
The boring part of the thing is exactly this: to draw imaginary quadrilaterals and spheres over the respective mesh's group. As usual, we don't address the group directly, we address the surface. On the example, the variable tex1
pinpoints the surface. Searching for tex1:
Code:
SURFHANDLE tex1 = oapiGetTextureHandle (vcmesh_tpl, 16);
And pressing F12 over oapiGetTextureHandle:
Code:
/**
* \brief Retrieve a surface handle for a mesh texture.
* \param hMesh mesh handle
* \param texidx texture index (>=1)
* \return surface handle
* \note This function can be used for dynamically updating textures during the simulation.
* \note the texture index is given by the order in which the textures appear in the
* texture list at the end of the mesh file.
* \note Important: Any textures which are to be dynamically modified should be
* listed with the "D" flag ("dynamic") in the mesh file. This causes Orbiter to
* decompress the texture when it is loaded. Blitting operations to compressed
* surfaces is very inefficient on most graphics hardware.
*/
OAPIFUNC SURFHANDLE oapiGetTextureHandle (MESHHANDLE hMesh, DWORD texidx);
.
So we now that we are aiming the 16th texture from the mesh those handle is on vcmesh_tpl. Searching for vcmesh_tpl I found:
Code:
SetMeshVisibilityMode (AddMesh (vcmesh_tpl = oapiLoadMeshGlobal ("DG\\DeltaGliderCockpit")), MESHVIS_VC);
So, again, we are back to the DeltaGliderCockpit (not a surprise).
The 16th texture define in this mesh is:
Code:
TEXTURES 19
DG\DGMK4_4.dds
DG\DGMK4_2.dds
DG\DGMK4_3.dds
DG\PSNGR2.dds
DG\PSNGR1.dds
DG\PSNGR3.dds
DG\PSNGR4.dds
DG\DGPILOT1.dds
DG\DGIP_01.dds
DG\DGIP_04.dds
DG\DGIP_02.dds
DG\DGIP_03.dds
DG\DGIP_05.dds
DG\DGIP_06.dds D
DG\DG_VC2.dds
DG\DG_VC1.dds D ; This ONE!
Not surprisingly, it's a dynamic texture - one that is redrawn in runtime.
Searching for all the groups the uses TEXTURE 16, we get 12 (yes, twelve!) groups that use it. Things are becoming a little messy. :/
This happens because is bad for performance to switching textures while drawing the mesh. So we group them ordering by texture to minimize the texture switching. One way to save texture switching is to have a one big texture shared by many groups, and then each group chooses the part of it to be used. This is where that 2 last numbers from the vertex definition goes: you don't have to use only 0 and 1, you can use floats. So you can use one texture to paint many surfaces. One can divide a texture by 4 and:
surfaces a uses 0 0 ; 0 0.5 ; 0.5 0 ; 0.5 0.5
surfaces b uses 0.5 0 ; 0.5 0.5 ; 1 0 ; 1 0.5
surfaces c uses 0 0.5 ; 0 1 ; 0.5 0.5 ; 0.5 1
surfaces d uses 0.5 0.5 ; 0.5 1 ; 1 0.5 ; 1 1
The coordinates is normalized. So, 0 means the first point (from left to right, or to bottom to top), and 1.0 means the last one. 0.5 means WIDTH*0.5 or HEIGHT*0.5 . And so on.
Use squared paper to draw the points above. You will understand easier this way.
Problem is, we still don't know what of the 12 groups is the one we want. =/
Fortunately, there's a easy way: open the DG cockpit on 3D modelling tool and go hunting the left MFD buttons. I use blender, and useD
this article to navigate inside the mesh. Doing this, I found that the group that we want is called ORBGroup3 on the Blender. Bad news, Blender gives this kind of name when there's no label on the group. But at least I know that we are searching the 3rd group on the mesh as I know that Blender calls ORBGroup1 the first group, and so on.
So, I could find this:
Code:
MATERIAL 0 ; 1
TEXTURE 16 ; 9
FLAG 4
GEOM 60 30 ; DG4_3D2 2
-0.230072 1.148224 7.328184 0.000000 0.342346 -0.939574 0.4375 0.8828
-0.216058 1.148224 7.328184 0.000000 0.342346 -0.939574 0.5313 0.8828
-0.216058 1.15591 7.330984 0.000000 0.342346 -0.939574 0.5313 0.8438
-0.230072 1.15591 7.330984 0.000000 0.342346 -0.939574 0.4375 0.8438
-0.230072 1.125724 7.319994 0.000000 0.342267 -0.939603 0.5313 0.8828
-0.216058 1.125724 7.319994 0.000000 0.342267 -0.939603 0.6250 0.8828
-0.216058 1.13341 7.322794 0.000000 0.342267 -0.939603 0.6250 0.8438
-0.230072 1.13341 7.322794 0.000000 0.342267 -0.939603 0.5313 0.8438
-0.230072 1.103234 7.311814 0.000000 0.341310 -0.939951 0.6250 0.8828
-0.216058 1.103234 7.311814 0.000000 0.341310 -0.939951 0.7188 0.8828
-0.216058 1.11091 7.314604 0.000000 0.341310 -0.939951 0.7188 0.8438
-0.230072 1.11091 7.314604 0.000000 0.341310 -0.939951 0.6250 0.8438
-0.230072 1.080734 7.303624 0.000000 0.340832 -0.940124 0.7188 0.8828
-0.216058 1.080734 7.303624 0.000000 0.340832 -0.940124 0.8125 0.8828
-0.216058 1.08841 7.306414 0.000000 0.340832 -0.940124 0.8125 0.8438
-0.230072 1.08841 7.306414 0.000000 0.340832 -0.940124 0.7188 0.8438
-0.230072 1.058234 7.295434 0.000000 0.342267 -0.939603 0.8125 0.8828
-0.216058 1.058234 7.295434 0.000000 0.342267 -0.939603 0.9063 0.8828
-0.216058 1.06591 7.298224 0.000000 0.342267 -0.939603 0.9063 0.8438
-0.230072 1.06591 7.298224 0.000000 0.342267 -0.939603 0.8125 0.8438
-0.230072 1.035734 7.287244 0.000000 0.342267 -0.939603 0.9063 0.8828
-0.216058 1.035734 7.287244 0.000000 0.342267 -0.939603 1 0.8828
-0.216058 1.04341 7.290034 0.000000 0.342267 -0.939603 1 0.8438
-0.230072 1.04341 7.290034 0.000000 0.342267 -0.939603 0.9063 0.8438
-0.023942 1.148224 7.328184 0.000000 0.342346 -0.939574 0.4375 0.9219
-0.009927 1.148224 7.328184 0.000000 0.342346 -0.939574 0.5313 0.9219
-0.009927 1.15591 7.330984 0.000000 0.342346 -0.939574 0.5313 0.8828
-0.023942 1.15591 7.330984 0.000000 0.342346 -0.939574 0.4375 0.8828
-0.023942 1.125724 7.319994 0.000000 0.342267 -0.939603 0.5313 0.9219
-0.009927 1.125724 7.319994 0.000000 0.342267 -0.939603 0.6250 0.9219
-0.009927 1.13341 7.322794 0.000000 0.342267 -0.939603 0.6250 0.8828
-0.023942 1.13341 7.322794 0.000000 0.342267 -0.939603 0.5313 0.8828
-0.023942 1.103234 7.311814 0.000000 0.341310 -0.939951 0.6250 0.9219
-0.009927 1.103234 7.311814 0.000000 0.341310 -0.939951 0.7188 0.9219
-0.009927 1.11091 7.314604 0.000000 0.341310 -0.939951 0.7188 0.8828
-0.023942 1.11091 7.314604 0.000000 0.341310 -0.939951 0.6250 0.8828
-0.023942 1.080734 7.303624 0.000000 0.340832 -0.940124 0.7188 0.9219
-0.009927 1.080734 7.303624 0.000000 0.340832 -0.940124 0.8125 0.9219
-0.009927 1.08841 7.306414 0.000000 0.340832 -0.940124 0.8125 0.8828
-0.023942 1.08841 7.306414 0.000000 0.340832 -0.940124 0.7188 0.8828
-0.023942 1.058234 7.295434 0.000000 0.342267 -0.939603 0.8125 0.9219
-0.009927 1.058234 7.295434 0.000000 0.342267 -0.939603 0.9063 0.9219
-0.009927 1.06591 7.298224 0.000000 0.342267 -0.939603 0.9063 0.8828
-0.023942 1.06591 7.298224 0.000000 0.342267 -0.939603 0.8125 0.8828
-0.023942 1.035734 7.287244 0.000000 0.342267 -0.939603 0.9063 0.9219
-0.009927 1.035734 7.287244 0.000000 0.342267 -0.939603 1 0.9219
-0.009927 1.04341 7.290034 0.000000 0.342267 -0.939603 1 0.8828
-0.023942 1.04341 7.290034 0.000000 0.342267 -0.939603 0.9063 0.8828
-0.198383 1.006021 7.276421 0.000000 0.342274 -0.939600 0.90625 0.7148
-0.184369 1.006021 7.276421 0.000000 0.342274 -0.939600 0.9961 0.7148
-0.184369 1.013785 7.279249 0.000000 0.342274 -0.939600 0.9961 0.6719
-0.198383 1.013785 7.279249 0.000000 0.342274 -0.939600 0.90625 0.6719
-0.074045 1.006021 7.276421 0.000000 0.342274 -0.939600 0.90625 0.75
-0.060031 1.006021 7.276421 0.000000 0.342274 -0.939600 0.9961 0.75
-0.060031 1.013785 7.279249 0.000000 0.342274 -0.939600 0.9961 0.7070
-0.074045 1.013785 7.279249 0.000000 0.342274 -0.939600 0.90625 0.7070
-0.055507 1.006021 7.276422 0.000000 0.342274 -0.939600 0.90625 0.7852
-0.041493 1.006021 7.276422 0.000000 0.342274 -0.939600 0.9961 0.7852
-0.041493 1.013785 7.279249 0.000000 0.342274 -0.939600 0.9961 0.7422
-0.055507 1.013785 7.279249 0.000000 0.342274 -0.939600 0.90625 0.7422
1 0 2
3 2 0
5 4 6
7 6 4
9 8 10
11 10 8
13 12 14
15 14 12
17 16 18
19 18 16
21 20 22
23 22 20
25 24 26
27 26 24
29 28 30
31 30 28
33 32 34
35 34 32
37 36 38
39 38 36
41 40 42
43 42 40
45 44 46
47 46 44
49 48 50
51 50 48
53 52 54
55 54 52
57 56 58
59 58 56
These vertices define all the buttons for the left MFD, and the UV coordinates on each vertex a UV position on the texture where the triangle will fetch the image.
This is a lot of data. What we need from them? Basically, we need the top/left and bottom/right vertices of the surfaces (so we can define that quadrilateral in which the clicks will be detected) and the texture coordinates where to draw the button labels (that so are painted on the mesh).
A quick way to do so is using a spreadsheet:
Code:
X Y Z U V
MIN: -0.230072 1.006021 7.276421 0.4375 0.4375
MAX: -0.009927 1.15591 7.330984 1 0.9219
All the buttons are inside a cube defined by [-0.230072, 1.15591, 7.330984] to [0.009927, 1.006021, 7.276421]. Note that the left MFD is at left of the 0,0,0 (center of the local mesh). So -0.23 is farther to the left than -0.009 .
Visually:
However, if you go to the code up in this post, you will notice that the MFD buttons were registered in 5 steps:
1) A quadrilateral for AID_MFD1_LBUTTONS
2) A quadrilateral for AID_MFD1_RBUTTONS
3) Three sphericals for AID_MFD1_PWR, AID_MFD1_SEL, AID_MFD1_MNU
I marked these in orange on the picture.
So, you need to find the boundaries for the orange markings I did on my last screenshot. The best way to do that is using 3d Modeller as Blender or Mesh Wizard, but you can try to use a Spreadsheet. Copy and paste the vertices data into a spreadsheet and reorder the vertices to find the interesting points:
Order by Y, the two lowest values belongs to the PWR, SEL and MNU buttons. Reserve them.
Order by X. The two lowest values belongs to the LBUTTONS. Reserve them.
The remainings belongs to the RBUTTONS.
I made a Google Docs Spreadsheet
here with these data already munched.
Let's focus for now on the PWR, SEL and MNU buttons, since they are selected individually and their textures are not dynamic.
We already defined and registered them. Now we must handle the events that Orbiter send us when a click is detected on the designed area. Searching for AID_MFD1_PWR, I got to this:
Code:
// --------------------------------------------------------------
// Respond to virtual cockpit mouse event
// --------------------------------------------------------------
bool DeltaGlider::clbkVCMouseEvent (int id, int event, VECTOR3 &p)
{
switch (id) {
case AID_MFD1_PWR:
oapiToggleMFD_on (MFD_LEFT);
return true;
case AID_MFD1_SEL:
oapiSendMFDKey (MFD_LEFT, OAPI_KEY_F1);
return true;
case AID_MFD1_MNU:
oapiSendMFDKey (MFD_LEFT, OAPI_KEY_GRAVE);
return true;
}
return false;
}
This is a callback - a special kind of function where Orbiter calls us, and not vice-versa. When orbiter calls this callback, it's because some registered area was clicked. The id gives us the Area ID (AID_* as we define them). Event is the kind of event: left button down, left button UP, etc. See the API PDF for this function to details. This function should returns true if the event was handled, and returns false if not (so orbiter knows it must try this event with someone else's handler).
What I suggest to do is to create a mesh group on your already made Mesh with the group on this article, write the code to register and handle the PWR, SEL and MNU buttons and play around a bit.
The function buttons are a bit messy to deal, because:
1) They are registered as a group, and then the handler must discover (by analysing the event data) the mouse position in which the mouse event was detected to infer in which button it happened.
2) The buttons surface are painted with a dynamic texture, that changes as you change the MFD. You must detect these changes in order to redraw the correct labels on the dynamic texture Orbiter uses to paint the buttons.
It's better to make to work and understand the simplest buttons first.
We will be back to this soon.
Merry Xmas!
---------- Post added at 08:40 AM ---------- Previous post was at 08:19 AM ----------
In time, create also a squared surface near the MFD, and paint it with the texture you will use to paint the buttons. Use the whole texture.
You will need this to see what's happening on the texture. It will help to understand what's happening.