General Question Does anyone have a full copy of the code from this post by martins?

Xyon

Puts the Fun in Dysfunctional
Administrator
Moderator
Orbiter Contributor
Addon Developer
Webmaster
GFX Staff
Beta Tester
Joined
Aug 9, 2009
Messages
6,920
Reaction score
787
Points
203
Location
10.0.0.1
Website
www.orbiter-radio.co.uk
Preferred Pronouns
she/her
I was hoping to start with a small example and build up, rather than searching through 5k lines of code and trying to find anything that's there.

Which element, specifically, are you looking for code on? The Panel, or an MFD, or the specific integration between MFD and 2D panel? I have some code... somewhere... from following that masterclass, but I would need to dig deeply to find it.
 

thepenguin

Flying Penguin
Addon Developer
Joined
Jul 27, 2013
Messages
220
Reaction score
1
Points
16
Location
Earth-Moon Lagrange Point (L4)
Which element, specifically, are you looking for code on? The Panel, or an MFD, or the specific integration between MFD and 2D panel? I have some code... somewhere... from following that masterclass, but I would need to dig deeply to find it.

I've made it this far:
picture.php


I'm trying to get the buttons working and integrated with the MFD/Panel combionation.
 

Lisias

Space Traveller Wanna-be
Joined
May 31, 2015
Messages
346
Reaction score
3
Points
18
Website
www.youtube.com
I was hoping to start with a small example and build up, rather than searching through 5k lines of code and trying to find anything that's there.

Oh, I see.

Unfortunately, I didn't started up with the Classes - I got my hands on nice vessel from my hookie times and learnt on the hard (but funnier =P) way.

Showing a MFD is easy - you just tell Orbiter to draw the MFD screen on a Surface (as it were a Dynamic Texture). Can be any surface. Any one at all.

Using the Delta Glider as reference, open Meshes/DG/deltaglidercockpit.msh using a text editor (try Notepad++, very good) and search for all GEOM . Every single piece usable on this mesh is there with names.

From the Orbiter's API (that you need to read anyway), I know that a function called oapiRegisterMFD is the one you need to, well, register a MFD into the vessel. Searching the source code, I found:

Code:
		oapiRegisterMFD (MFD_LEFT,  mfds_left);
		oapiRegisterMFD (MFD_RIGHT, mfds_right);

Nice. So in DeltaGlider source code, the variables mfds_left and mfds_right holds the Mesh's surfaces where the MFDs is drawn.
Searching for everybody that references these variables, I got into this:

Code:
bool DeltaGlider::clbkLoadVC (int id)
{
	static VCMFDSPEC mfds_left  = {1, MESHGRP_VC_LMFDDISP};
	static VCMFDSPEC mfds_right = {1, MESHGRP_VC_RMFDDISP};
	static VCHUDSPEC huds = {1, MESHGRP_VC_HUDDISP, {0,1.462,7.09}, 0.15};
	SURFHANDLE tex1 = oapiGetTextureHandle (vcmesh_tpl, 16);
	SURFHANDLE tex2 = oapiGetTextureHandle (vcmesh_tpl, 18);
	SURFHANDLE tex3 = oapiGetTextureHandle (vcmesh_tpl, 14);

Good. Now we know that the mfds are drawn on a surface defined by groups called MESHGRP_VC_LMFDDISP and MESHGRP_VC_RMFDDISP from the Mesh index #1.

Also from the PDF, I learnt that a function called oapiLoadMeshGlobal is used to load meshes to be used, so another search got me:

Code:
	SetMeshVisibilityMode (AddMesh (exmesh_tpl = oapiLoadMeshGlobal (ScramVersion() ? "DG\\deltaglider" : "DG\\deltaglider_ns")), MESHVIS_EXTERNAL);
	SetMeshVisibilityMode (AddMesh (vcmesh_tpl = oapiLoadMeshGlobal ("DG\\DeltaGliderCockpit")), MESHVIS_VC);

It's not the best practice "guessing" the index from the known behaviour from the API - I know that the first AddMesh returns a index 0, the second, 1, etc. I would define variables to hold these information (safer, as if someone adds meshes alter, the variable will reflect the change instead of break the code). Anyway, we know for sure now the mesh and the group index - MESHGRP_VC_RMFDDISP and MESHGRP_VC_RMFDDISP - pressing F2 on these symbols, we get 109 and 110 as the group indices used by MFD.

Happyfully, the guy that generated the mesh added a comment on each GEOM tag to make easier to locate the groups by index. Notepad++ also helps, as the "Search all" lists all the GEOM lines together. :)

So, I got the 109 and 110th groups from the cockpit mesh:

Code:
LABEL instrument _09
MATERIAL 19
TEXTURE 0
GEOM 4 2	; instrument 109
-0.204963 1.017834 7.281795 0.000000 0.342025 -0.939691 0 1
-0.035036 1.017834 7.281795 0.000000 0.342025 -0.939691 1 1
-0.204963 1.177514 7.339914 0.000000 0.342025 -0.939691 0 0
-0.035036 1.177514 7.339914 0.000000 0.342025 -0.939691 1 0
1 0 3
2 3 0
MATERIAL 19
TEXTURE 0
GEOM 4 2	; instrument 110
0.035036 1.017834 7.281795 0.000000 0.342025 -0.939691 0 1
0.204963 1.017834 7.281795 0.000000 0.342025 -0.939691 1 1
0.035036 1.177514 7.339914 0.000000 0.342025 -0.939691 0 0
0.204963 1.177514 7.339914 0.000000 0.342025 -0.939691 1 0
1 0 3
2 3 0

From 3DModel.pdf (another obligatory reading), we know that the first trio of numbers is the vertex coordinates (left hand style), the second trio is a normal used for lightning and are not of our concerning now. The last two numbers are important, as they define the texture coordinates that will be drawn on that point. 0,0 is the lower left corner, 1,1 is the upper right.

And that's it. I suggest you to fool a little with the mesh and with the code to understand how this works. Try to change the texture coordinates, or to change the numbers on the surface definition to see what happens.

Then draw the MFD into another surface just for fun - try the HUD, of even the helmet's visor - a carefully crafted transparency would allow you to paint a MFD on a helmet's corner, and then you would have a Helmet HUD.

The hard part is to make the buttons "clickable" so you can control the MFD. But this can talked in a second moment.
 

Lisias

Space Traveller Wanna-be
Joined
May 31, 2015
Messages
346
Reaction score
3
Points
18
Website
www.youtube.com
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.
 
Last edited:

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,829
Reaction score
2,085
Points
203
Location
between the planets
also, the font graphics for blitting would be appreciated a lot

Ah, the hassle of creating a font bitmap, when it would be so much more easy to just let the computer do it by himself. I have a pretty capable font class for the UI for IMS2 that handles creation of the bitmap and blitting, so I might as well share it (complete code below).

Basically it abstracts the whole font blitting to a more intuitive level where you can just create a font with a typeface, size and some colors, and then just tell it to print some text to a SURFHANDLE. It can handle non-monotype fonts, so no more messy business with manually hardcoding lookup tables.

Usage is as straight forward as I could make it:

Code:
//create a white arial font on balck background with a height of 16 pixels:
GUI_font *newFont = new GUI_font(16, "Arial", true, GUI_SMALL_DEFAULT_FONT, GUI_COLOR(255, 255, 255), GUI_COLOR(0, 0, 0));

//define a rectangle on the target surface to print to. This just serves as a clipping rect, to make sure the text doesn't draw outside the borders
RECT printablerect = _R(0, 0, 100, 30);

//print a text to the hypothetical SURFHANDLE mysurface, centered in printablerect:
newFont->Print(mysurface, "Hello World!", printablerect.right / 2, printablerect.bottom / 2, printablerect, false, T_CENTER, V_CENTER);

And that's about it. The font also supports an alternative highlight styling if anybody should need it, but mostly you don't.

Anyways, the code:


The header, GUI_font.h
Code:
#pragma once

//font class to handle text output

typedef enum {T_RIGHT, T_LEFT, T_CENTER} GUI_TEXT_POS;				//Text positions for print functions
typedef enum {V_TOP, V_BOTTOM, V_CENTER} GUI_TEXT_VERTICAL_POS;	//vertical alignment of text


//struct for handling colors
struct GUI_COLOR
{
	int r;
	int g;
	int b;
	GUI_COLOR(){};
	GUI_COLOR(int red, int green, int blue)
	{
		r = red;
		g = green;
		b = blue;
	};
	bool operator==(GUI_COLOR c)
	{
		if (r == c.r && g == c.g && b == c.b)
		{
			return true;
		}
		return false;
	};
	bool operator !=(GUI_COLOR c)
	{
		if (r == c.r && g == c.g && b == c.b)
		{
			return false;
		}
		return true;
	}
};


class GUI_font				
{
public:
	/* creates the desired font
	 * height: The desired font height (size) in pixel
	 * face: the typeface name of the font (e.g. "Arial")
	 * proportional: flag for proportional/fixed pitch font 
	 * !!see orbiter api reference of oapiCreateFont() for more explanation on the above 3, as well as style below!!
	 * _id: unique identifier for this font (intended to be used if the font is handled by some kind of font manager)
	 * _color: The text color of the font
	 * _bgcolor: The color of the background the font is drawn on
	 * _hilightcolor: an alternate color in which the font should appear when hilighted. if not defined, no hilight font will be reated
	 * hilightbg: an alternate background color for the highlighted font. If undefined, the normal background color will be used
	 * style: font decoration style (bold, italic, underlined etc)
	 */
	GUI_font(int height, string face, bool proportional, int _id, GUI_COLOR _color, GUI_COLOR _bgcolor, 
		GUI_COLOR _hilightcolor = GUI_COLOR(0, 0, 0), GUI_COLOR _hilightbg = GUI_COLOR(0,0,0), FontStyle style = FONT_NORMAL);

	~GUI_font();

	/* prints a string to the target surface at the desired coordinates
	 * tgt: the SURFHANDLE to draw the text on
	 * text: Reference to a string containing the text to be drawn
	 * _x, _y: x and y coordinates on the target surface. Exact interpretation depends on tpos and vpos
	 * elementpos: a clipping rect. Any text falling outside of this rectangle won't be drawn.
	 * highlight: if true, the text will be printed using the highlighted font
	 * tpos: text justification. can either be T_LEFT, T_RIGHT or T_CENTER
	 * vpos: vertical alignement of the text. can either be V_TOP, V_BOTTOM or V_CENTER
	 *
	 *NOITES:
	 * where exactly the text is printed depends on tpos and vpos.
	 * T_LEFT means that _x is the left-hand coordinate, and the text should be drawn from there to the right
	 * T_RIGHT means that _x is the right-hand coordinate, and the text should be drawn from there to the left
	 * T_CENTER means that _x is the center coordinate, and the text should be drawn evenly towards the right and the left
	 * V_TOP means that _y is the top coordinate, and that the text should be drawn below it
	 * V_BOTTOM means that _y is the bottom coordinate, and that the text should be drawn above it (this is NOT the baseline. There is no baseline support, sorry)
	 * V_CENTER means that _y is the vertical center of the text, and the top of the text is height / 2 above it.
	 */
	void Print(SURFHANDLE tgt, std::string &text, int _x, int _y, RECT &elementPos, bool highlight = false, GUI_TEXT_POS tpos = T_LEFT, GUI_TEXT_VERTICAL_POS vpos = V_TOP);
	
	/* returns the id of thisfont
	*/
	int GetId();

	/* returns the text size of this font (pixel)
	*/
	int GetfHeight();
	
	/*returns the AVERAGE width of a character in this font
	*/
	int GetfWidth(){ return fwidth; };
	
	/* returns the total width of the passed string when printed in this font (pixel)
	*/
	int GetTextWidth(string &text);

private:
	int id;
	int fheight;			//height of a symbol
	int fwidth;				//average width of a symbol
	int width;				//width of the texture
	SURFHANDLE src;			//source surface
	bool hasHighlightFont;	//whether or not this font has a variant for highlights
	GUI_COLOR color;
	GUI_COLOR bgcolor;
	GUI_COLOR hilightcolor;
	GUI_COLOR hilightbg;

	int charpos[94];		//stores character positions in the bitmap
	
	/* this function allocates a texture surface and draws the
	 * font to it using a sketchpad. 
	 */
	void createFont(Font *font);
};

The actual code:

Code:
#include "GUI_font.h"

GUI_font::GUI_font(int height, string face, bool proportional, int _id, GUI_COLOR _color, GUI_COLOR _bgcolor, GUI_COLOR _hilightcolor, GUI_COLOR _hilightbg, FontStyle style)
{
	fheight = height;
	id = _id;
	hilightbg = _hilightbg;
	hilightcolor = _hilightcolor;
	bgcolor = _bgcolor;
	color = _color;

	if (hilightbg == GUI_COLOR(0, 0, 0))
	{
		hilightbg = bgcolor;
	}
	if (hilightcolor == GUI_COLOR(0, 0, 0))
	{
		hasHighlightFont = false;
	}
	else
	{
		hasHighlightFont = true;
	}

	Font *font = oapiCreateFont(fheight, proportional, (char*)face.data(), style);
	createFont(font);
}

GUI_font::~GUI_font()
{
	oapiDestroySurface(src);
}



void GUI_font::createFont(Font *font)
{
	
	//create character lookup table
	int curpos = 0;
	//we need to create a dummy surface in order to get a sketchpad in order to get the width of individual characters
	SURFHANDLE dummy = oapiCreateSurfaceEx(10, 10, OAPISURFACE_SKETCHPAD);
	Sketchpad *skp = oapiGetSketchpad(dummy);
	skp->SetFont(font);
	//run through all characters from 0 to 94 and note how wide they are
	for (int i = 0; i < 94; ++i)
	{
		charpos[i] = curpos;
		const char c = i + 32;
		curpos += skp->GetTextWidth(&c, 1);
	}
	oapiReleaseSketchpad(skp);
	oapiDestroySurface(dummy);
	width = curpos;
	
	//the average width of a symbol is the total width of the texture divided by the number of symbols
	fwidth = width / 94;

	//now create the actual surface on which the font is going to be
	int height = fheight;
	//if the font has a highlighted version, we need twice the size, because we have to print it twice
	if (hasHighlightFont) height += fheight;
	
	SURFHANDLE tgt = oapiCreateSurfaceEx(width, height, OAPISURFACE_SKETCHPAD);
	
	//fill the font background color
	oapiColourFill(tgt, oapiGetColour(bgcolor.r, bgcolor.g, bgcolor.b), 0, 0, width, fheight);
	//if we have a hilight version of the font, we need another row with different background color
	if (hasHighlightFont)
	{
		oapiColourFill(tgt, oapiGetColour(hilightbg.r, hilightbg.g, hilightbg.b), 0, fheight, width, fheight);
	}

	//everything's set up, now we only need to write all the characters to the right place
	skp = oapiGetSketchpad(tgt);
	skp->SetFont(font);
	skp->SetTextColor(GUI_Draw::getDwordColor(color));

	for (int i = 0; i < 94; ++i)
	{
		const char c = i + 32;
		skp->Text(charpos[i], 0, &c, 1);
		if (hasHighlightFont)
		{
			skp->SetTextColor(GUI_Draw::getDwordColor(hilightcolor));
			skp->Text(charpos[i], fheight, &c, 1);
			skp->SetTextColor(GUI_Draw::getDwordColor(color));
		}
	}
	//clean up and assign the newly created surface as source
	oapiReleaseSketchpad(skp);
	oapiReleaseFont(font);
	src = tgt;

}


void GUI_font::Print(SURFHANDLE tgt, std::string &text, int _x, int _y, RECT &elementPos, bool highlight, GUI_TEXT_POS tpos, GUI_TEXT_VERTICAL_POS vpos)
{

	int textwidth = GetTextWidth(text);
	int srcy = 0;

	int startx = _x;					//starting position relative to panel

	if (tpos == T_RIGHT)
	{
		startx = max(_x - textwidth, elementPos.left);
	}
	else if (tpos == T_CENTER)
	{
		startx = max(_x - (textwidth / 2), elementPos.left);
	}

	if (vpos == V_BOTTOM)
	{
		_y -= fheight;
	}
	else if (vpos == V_CENTER)
	{
		_y -= fheight / 2;
	}

	if (highlight && hasHighlightFont)
	{
		srcy += fheight;
	}

	int tgtoffset = 0;
	for (UINT i = 0; i < text.length(); ++i)
	{
		//int srcx = srcX + (text.c_str()[i] - 32) * fwidth;
		int ch = text.c_str()[i] - 32;
		int srcx = charpos[ch];
		int charwidth = width;
		if (ch < 93)
		{
			charwidth = charpos[ch + 1];
		}
		charwidth -= srcx;
		int tgtx = startx + tgtoffset;
		tgtoffset += charwidth;

		if (tgtx < elementPos.left || tgtx + charwidth > elementPos.right)
			//making sure function doesn't print beyond elements boundaries
		{
			break;
		}
		oapiBlt(tgt, src, tgtx, _y, srcx, srcy, charwidth, fheight);
	}
}

//returns the length of the text in pixels
int GUI_font::GetTextWidth(string &text)
{
	int width = 0;
	for (string::iterator it = text.begin(); it != text.end(); ++it)
	{
		int ch = *it - 32;
		int charwidth = width;
		if (ch < 93)
		{
			charwidth = charpos[ch + 1];
		}
		charwidth -= charpos[ch];
		width += charwidth;
	}
	return width;
}


int GUI_font::GetId()
{
	return id;
}



int GUI_font::GetfHeight()
{
	return fheight;
}
 
Last edited:

gattispilot

Addon Developer
Addon Developer
Joined
Oct 17, 2007
Messages
8,564
Reaction score
2,526
Points
203
Location
Dallas, TX
Thanks. Looks good. Now just to get the dynamic texture for button:)
 
Top