Cliquez le lien ci-dessous pour retourner au sommaire des "tutorials pour créer une DLL Orbiter"
http://orbiter.dansteph.com/forum/index.php?topic=6335.msg95352#msg95352
Compiler une DLL avec un panel 2DUn panel 2d dans Orbiter ca peut ressembler a ca: (ici en exemple celui du DGIV en 3 parties)
Très cool mais le problème est que si vous avez essayé de compiler les exemples "deltaGlider" ou "shuttleA" pour voir comment faire un panel 2d vous devez avoir eu pleins d'erreurs de compilation.
La cause en est que VC++ ne contient pas nativement une gestion des ressources comme les bitmaps qui sont incluse et compilée dans la DLL et que part defaut la declaration des bibliotheque est minimale ce qui fait échouer la compilation , nous allons solutionner tout ca.
Attention!!!: 1- Ce tuto ne vous expliquera pas comment faire une panel 2d Orbiter de A à Z mais il vous permettra de compiler les exemples, un exemple qui compile c'est la clé pour un bon travail de detective, pouvoir modifier et comprendre comment marchent les choses. Ceci dit je vous donnerais deux trois pistes à la fin de ce tuto pour expliquer au moins le principe des panels 2d d'Orbiter.
2-Il est déconseillé de commencer par ce tuto, je part du principe que vous avez
compris les autres tuto et déjà examiné l'exemple de projet DLL.
Compilation d'une DLL avec panel 2dNous allons compiler le projet SHuttleA qui est le plus simple à comprendre. Le Deltaglider lui est deja sacrement toufu et plus difficile à suivre.
1-Cliquez sur "OrbiterSDK/Samples/ShuttleA/ShuttleaA.dsw" cliquez "oui pour tout" à convertir le projet.
2-Dans "outils/Options/projet et solutions/répértoire de VC++" sélectionnez en haut "Afficher les répértoire pour: fichier include" puis
rajouter une chemin vers "...windows server2003 R2/include/
MFC" (même chose que nous avons fait dans le tout premier tuto sauf que la nous incluons le repertoire "include/MFC" au lieu de "include"
Ceci ne sera a faire qu'une seul fois pour tout les projets.
3-Dans "Projet/Propriété de ShuttleA/Editeur de liens/Entrée" copiez collez la ligne ci-dessous dans le champ "dépendances supplémentaires"
user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.libCeci sera a faire pour tout les projet (qui utilisent un panel) n'oubliez pas de faire ca pour debug et release
4-VC++ express contrairement a la version payante ne contient pas d'editeur de ressources mais il en existe de très bon externe en freeware dont un qui s'appelle "resedit" et ce télécharge depuis cette page:
http://www.resedit.net/Dans l'exemple ShuttleA il vous servira à editer les ressources bitmap qui ce trouvent dans le fichier ShuttleA.rc
et "ressource.h" si vous voulez inclure des ressources dans votre propre projet il créera deux fichiers un "*.rc" et un "ressource.h" il ne faudra pas oublier d'inclure le "ressources.h" dans votre projet. (projet/ajouter un élément existant)
Testez la compilation, ca doit marcher.
Le principe du panel 2d dans OrbiterJe vous donne ci-dessous les grandes lignes qui vous aiderons à comprendre le principe, pour le détail il faudra faire le fameux travail de detective sur les exemples existant (shuttlea deltaglider) Dans le texte ci-dessous je vous donne en
jaune les mot clé des fonctions ou variables concernée par l'explication pour que vous puissiez suivre les explications tout en regardant le code du ShuttleA.
Dans le fichier *.rc sera déclaré la bitmap qui sera le fond du panel, cette bitmap ainsi que d'autres éventuelle seront compilée et
incluse dans la DLL, pas besoin donc de fournir les fichier bitmap en plus avec votre addon.
Notez que VC++ ne comprend QUE le format bmp.
Un exemple de bitmap, le fond du panel du haut du ShuttleA: (taille réduite)
Autres exemples: les bouttons du panel du DGIV: (taille réel)
Ces bitmaps doivent etre en format 256 couleur bmp, la couleur qui devra etre transparente dans Orbiter etant le blanc complet (RGB= 255,255,255)
Dans Orbiter vous pourrez declarer des zones sur ce panel, soit des zones simplement réactive au clique souris (bouton non animés), soit des zones ou vous pourrez ecrire du texte (affichage données) soit des zones ou vous allez copier des bout d'autres images bmp (bouton animé etc etc) soit un mélange des trois. Ceci ce fait a l'aide de la fonctions orbiter "
oapiRegisterPanelArea"
NOTE:Il faut etre precautioneux avec cette fonction, un parametre "
PANEL_REDRAW_ALWAYS" signifie que la zone sera rafraichie à chaque image, si vous copiez un bmp de 400x400 pixels ca fera 9 millions de pixels à copier chaque seconde ! (à 60FPS) Comme GDI32 la librairie windows 32 bits utilisée par Orbiter pour les graphisme 2d n'est pas super performante votre panel risque d'etre un "Bouffe framerate" pour votre addon.
"
PANEL_REDRAW_ALWAYS" est donc plutot reservé au zones dynamique qui changent à chaque image comme l'affichage de l'altitude, quantité de fuel etc etc.
Pour afficher un bouton sur lequel on peut cliquer par exemple il vaut mieux utiliser "PANEL_REDRAW_MOUSE" qui rafraichira la zone qu'après un clique souris ou
"PANEL_REDRAW_USER" qui nous permet grace a
"oapiTriggerPanelRedrawArea" d'avoir un control manuel sur le rafraichissement. (cas d'un bouton pressé qui allumerais une lumiere sur une autre zone par exemple)
Note sur la taille du fond du panel et les bitmapsQuel taille pour cette énorme bitmap ? C'est un compromis et ca dépend de votre projet, plus elle est grosse plus l'affichage sera lent sur de petite machine évidemment. Mais si vous la faite trop petite les gens avec un bon ordinateur et une résolution plus élevée aurons des "trous" à gauche et à droite et verrons l'espace.
En 2007 j'ai passé les fond du DGIV en 1280 de largeur mais je n'affiche les instrument que sur les 1184 pixels du millieux. Comme cela les gens en 1280 n'ont pas de trous et ceux qui tournent en 1184 voient encore tout les instruments. J'ai eu quelques demandes pour la passer en 1600 mais cette résolution (ou plus haute) est encore
"rare" et la pénalisation des performances sur les ordi moyen est vraiment conséquente.
Ceci dis ca dépend aussi de ce que vous y mettez, 1280x400 pour mettre 5 boutons... hem.
Pour les autres bitmap qui contiennent les boutons, lumieres et autre éléments ca dépend encore de votre panel.
Comme nous avons besoin des coordonées des éléments pour pouvoir les copier sur le panel principal il est plus facile de bien ranger les même éléments avec juste un offset x ou y que de les eparpiller sur la bitmap.
Exemple: Un bouton rotatif de 30 pixel de large qui peut avoir trois état par exemple, si vous ranger chaque image horizontalement à la suite vous pourrez faire en une ligne (en pseudo code) AfficheBouton(EtatBouton*30,0)
ou EtaBouton est 0 1 ou 2, si vous éparpillez les images sur la bitmap vous devrez ecrire les coordonnées pour chaque état bonjour la complication du code.
Donc pour les autres bitmap: assez grande pour que vous puissiez bien organiser vos éléments graphique... Mais pas trop grande
L'exemple ShuttleA décortiquéPréparation des ressources GDI:Pour avoir un panel dans un addons orbiter aura besoin d'un certain nombre de ressources, les bitmaps evidemment mais aussi le handle de la DLL (pointeur sur la mémoire dll) et des polices qui seront utilisée pour marquer du texte sur le panel, comme ces ressources sont communes a toutes les instances de votre addon les exemples orbiter les rangent dans la seule structure globale du projet
GDIParams g_Param; GDIParams est une structure et g_Param la variable pour acceder à cette structure (pour une aide sur les structures regardez l'exemple de projet)
En tout premier Orbiter copie donc le handle de la DLL dans cette structure et crée de polices (fonts) qui seront utlisée par les affichage de texte. Ceci ce fait dans la fonction
InitModule Comme vous le voyez le ShuttleA utilise une polices Arial (voir aide windows "
CreateFont") et des "stylos" (
CreatePen) et "brosse" (
CreateSolidBrush) qui servent à tracer des traits.
Stylos, brosse et affichage de texte sont faite via la librairie graphique de window GDI32 il existe quantité de site qui parlent de cette librarie googlez avec "GDI32 C++ tutorial" pour avoir plus d'aide sinon suivez les exemples du ShuttleA.
NOTE: Les ressources police, pen et brushes doivent absolument etre detruite a la fin du programme sinon vous aurez des "memory leak" (mémoire allouée et jamais libérée) la destruction de ces ressources ce fait dans
ExitModule ne pas oubliez quand vous declarer une ressource dans
InitModule de modifier aussi
ExitModule pour que cette ressource soit libérée.
La handle "
hDLL" lui sert a une fonction "
LoadBitmap" qu'orbiter utilise pour charger le panel.
Préparation des ressources BITMAP:Comme dis precedement les bitmaps sont incluse dans le projet par les fichiers ressource.h et *.rc.
La ou les bitmap(s) principales du fond (vous pouvez en avoir plusieurs "panel du haut" et "panel principal" par exemple) ) sera directement affichée par Orbiter grace aux fonction "
HBITMAP hBmp = LoadBitmap" et "
oapiRegisterPanelBackground"
Par contre pour les autres bitmaps qui contiennent uniquement des bout qui serons copiée sur le fond (bouton, lumiere etc etc) Orbiter necessite qu'elle soient incluse dans des "surfaces" ces surface sont crées dans la fonction "
InitPanel"
Exemple:
srf[0] = oapiCreateSurface (LOADBMP (IDB_SLIDER1));Prend la bitmap déclarée dans ressource.h avec le nom "IDB_SLIDER1" et crée une surface avec dont le handle sera gardé par la variable srf[0].
Attention: comme pour les polices vous devez absolument liberer ces surfaces sous peine de memory leak ceci ce fait avec "
oapiDestroySurface" dans "
ReleaseSurfaces"
Attention2: comme ces surface sont crée pour chaque instance de vessel qui a un panel les handle des surfaces ne sont PAS globale. Les variables srf[] sont declarée dans la classe.
Silence on tourne:Ouf, la vous avez tout, maintenant il est temps de faire tourner tout ca.
Dans orbiter le traitement du panel ce fera a l'aide de trois fonctions callback:
1-
clbkLoadPanel Appellée par Orbiter quand l'initialisation du panel doit ce faire, quand on switch exterieur/interieur ou quand on change de panel "haut/bas" par exemple. Cette fonction donne dans la variable "id" le no du panel à afficher.
Notez comme la fonction "
ReleaseSurface" est appellée avant l'initialisation du panel et comme la fonction "InitPanel" qui crée les surfaces est appelée à la fin.
Notez aussi "
oapiRegisterMFD" qui declare des MFD (avec coordonnées dans "
MFDSPEC")
Pour les autres fonctions regardez dans la doc orbiter.
2-
clbkPanelMouseEvent Cette fonction est appellée automatiquement quand l'utilisateur clique sur une zone définie avec "
PANEL_MOUSE_LBDOWN" c'est ici que vous procedez au changement induit par le clique sur la zone, allumage d'un systeme ou d'un réacteur, changement variables animations, extinction d'une variable qui vous fait consommer l'electricité d'un systeme (virtuel) etc etc.
3-
clbkPanelRedrawEvent Dans cette fonction sera fait l'affichage proprement dis sur le panel du fond, soit par copie de petit bouts d'autres bitmap (boutons etc etc) soit par dessin direct via des fonctions GDI (lignes, boites colorée) soit par ecriture direct de texte (
TextOut)
En parametre de cette fonction est fourni le nom de la zone concernée par l'affichage (nom déclaré dans
oapiRegisterPanelArea) et le handle du carré concernée sur le panel du fond. Ce handle servira aux fonctions GDI ou au fonctions de copie de bitmap.
Pour
l'affichage de texte regardez par exemple la fonction
RedrawPanel_Fuelstatus.
Cette fonction est appellée depuis
clbkPanelRedrawEvent et affiche en texte les chiffres du fuel.
En tout premier il nous faut obtenir le handle DC (Draw Context) de la surface avec "
HDC hDC = oapiGetDC(surf);", ce handle sert à toute les fonctions GDI . En deuxieme nous selectionnons avec
SelectObject la police que nous allons utiliser en l'occurence la police arial créée dans
InitModule En troisieme nous allons choisir la couleur RGB du texte avec
SetTextColor (hDC, RGB(224,224,224)); ici un blanc/grisé. En dernier nous voulons que le fond du texte laisse apparaitre le panel en dessous ceci ce fait avec
SetBkMode (hDC, TRANSPARENT);Ensuite vient l'affichage proprement dis avec des fonctions
TextOut. Notez qu'un rectangle est aussi tracé avec la fonction "
Rectangle" de GDI.
Important: quand on a un handle DC (oapiGetDC) il faut
toujours le liberer quand on n'en a plus besoin
notez donc à la fin de la fonction RedrawPanel_Fuelstatus la fonction
oapiReleaseDC (surf, hDC); qui libère le handle.
Le caneva d'un affichage de texte à donc toujours ce schema (pseudo code)
// préparation
oapiGetDC(SurfaceDestination) // obtient le handle DC de la surface destination
SelectObject(NotrePolice) // selectionne la police a utiliser
SetBkMode(TRANSPARENT) // l'affichage du fond de la police laissera apparaitre le fond
// affiche le(s) texte(s)
SetTextColor(RGB) // selectionne la couleur du texte
TextOut // affiche le texte
SetTextColor(RGB) // selectionne une autre couleurs de texte
TextOut // affiche un autre texte
// fin de cette affichage
oapiReleaseDC // libere le handle DC
Pour la
copie de bitmap, une image vaut des centaines de mots, le principe est simple:
La copie de bitmap ce fait avec la fonction Orbiter
oapiBlt avec en parametre le handle de la surface destination (fond) le handle de la surface d'une autre bitmap les coordonnées du carré source à copier et les coordonnées ou vous voulez la copier sur la destination.
Le truc très chiant c'est qu'un pixel de différence dans la taille source et destination vous donnera une bouillie de pixel, il faut être précis. (ne pas copier une zone de 21 pixel sur une destination de 20 pixel)
Il faut bien comprendre une chose importante: Si vous ne mettez pas une zone en "PANEL_REDRAW_ALWAYS" la bitmap du fond est
comme un tableau noir qui n'est jamais effacé Ce que vous copiez ou ecrivez dessus le restera toujours.
Il serais suicidaire pour le framerate de réafficher à chaque image une zone qui ne change pas ou très peu, si vous avez un bouton déclarer le en raffraichissement manuel ou en "
PANEL_REDRAW_MOUSE" (raffraichi après un clique souris) Comprennez bien qu'en interne le rafraichissement d'une zone déclenche des centaines de lignes qu'une simple test de votre coté pourrais éviter. (GDI est notoirement lent)
Exemple: si vous avez un compas directionnel declarez le en rafraichissement manuel (..REDRAW_USER) enregistez le dernier heading affiché et comparez le avec la nouvelle valeurs, si elle n'a pas changée ne déclencher pas le réaffichage si il a changée déclenchez un raffraichissement avec
oapiTriggerPanelRedrawAreaMessage modifié ( 18-07-2007 17:24 )