Merci d'avoir pris le temps de regarder tout ça ! Je vais répondre à tes remarques une par une.
1. Dans les toutes premières versions, j'utilisais Lua tel quel (appels de fonction C directement donc) et j'avais choisi tinyxml pour parser les fichiers XML.
Du côté de Lua, j'ai petit à petit développé des utilitaires pour simplifier son utilisation dans mon code (donc rien de généraliste), puis ça a fini par se transformer en la bibliothèque luapp qui est maintenant utilisée. Je ne connaissais pas de wrapper C++ à l'époque, donc c'était la voie naturelle.
Du côté de XML, j'ai fini par abandonner tinyxml. C'était il y a déjà quelques années, donc les raisons exactes ne me reviennent plus à l'esprit. Mais je me souviens en tout cas que je n'aimais pas les messages d'erreur qu'il sortait, et qu'il ne fournissait pas de moyen pour vérifier le contenu du fichier (ce qu'on appelle les DTDs, il me semble). Au lieu de repasser à une autre bibliothèque, j'ai eu envie de coder la mienne, pour voir si j'en étais capable et aussi pour m'amuser
Le point positif, c'est que je sais ce que cette lib a dans le ventre. En l’occurrence, le XML que j'utilise n'est semble-t-il pas standard, puisque je n'ai aucun support pour les blocs CDATA, et pas la nécessité de gérer plusieurs types d'encodage. De même, les fichiers de validation (.def) ne sont pas du tout standard, et suivent une syntaxe que j'ai complètement inventée mais qui me semble bien plus lisible que ce que j'ai pu voir pour du "vrai" DTD (c'est probablement moins complet, mais largement suffisant pour ce que j'en fait). Un exemple type de fichier .def :
On définit un type de bloc (~ une classe en C++, si on veut) :
<d[Toto]:Region name inherits="" b:virtual="false" b:setAllPoints="false" b:hidden="false">
<n[.,1]:Size>
<n[*]:AbsDimension n:x="0" n:y="0"/>
<n[*]:RelDimension n:x="1.0" n:y="1.0"/>
</Size>
<n[.,1]:Anchors>
<Anchor point="TOPLEFT" relativeTo="" relativePoint="">
<n[.,1]:Offset>
<n[*]:AbsDimension n:x="0" n:y="0"/>
<n[*]:RelDimension n:x="0.0" n:y="0.0"/>
</Offset>
</Anchor>
</Anchors>
<n[1,1]:Regions>
<l:Region/>
</Regions>
</Region>
Le "d[Toto]:" devant le nom du bloc signifie "pré-définition", et 'Toto' est le nom d'un bloc dont celui-ci va
hériter : si on attend quelque part dans le code un bloc "Toto", alors l'utilisateur pourra donner un bloc "Region" s'il le souhaite.
Les "b:" et "n:" devant le nom d'un attribut signifient qu'on attend respectivement un booléen ou un nombre (par défaut, tout est une chaîne de caractère). Si une valeur est précisée après un attribut, alors il s'agit d'une valeur par défaut, et l'attribut peut ne pas être fourni.
Le "n[X]:" devant le nom d'un sous-bloc précise combien de ces blocs on peut trouver : si X est de la forme "x,y" alors on peut le trouver au moins "x" fois et au maximum "y" fois (un point '.' veut dire "peu importe"), et si X est une étoile '*', alors seul un des blocs en '*' à ce niveau peut être présent.
Enfin, le "l:" devant le nom d'un sous-bloc signifie qu'on charge ("load") ce bloc parmi la liste des blocs pré-définis.
Pour finir le fichier de définition, il faut lui donner la structure du fichier XML, donc son bloc "root", par exemple :
<Ui>
<Include file/>
<Script file/>
<l:Frame/>
</Ui>
Comme tu le dis, ça m'a quand même pris pas mal de temps de coder ces deux bibliothèques utilitaires. Mais d'une c'est déjà fait, et de deux j'y gagne en maîtrise du code. Je sais ce que font ces bibliothèques, et elles ont presque été taillées pour l'usage que j'en fais. Rien ne manque, et il n'y a pas de code ou de fonctionnalité superflus.
Au niveau de la maintenance, je ne me suis pas posé la question. En principe, je ne devrais pas avoir besoin de revoir toute l'architecture de la chose, puisque les besoins étaient posés dès le départ. Maintenant, comme tu l'as fait remarqué (cf ce qui suit), il y a probablement quelques petits soucis ça et là qu'il faut corriger. Mais c'est parfois aussi le cas des bibliothèques plus répandues (même si j'avoue que ce doit être bien plus rare), et dans ces cas là on peut parfois attendre des mois avant que le rapport d'erreur soit lu, puis un mois pour que le correctif soit envoyé sur le dépôt SVN, puis encore un mois pour qu'il arrive dans une version distribuée. Puis combien de temps avant que cette version soit utilisée par la majorité des gens ?
Je trouve quand même la gestion des dépendances extrêmement pénible, autant pour le développeur qui doit faire avec les bugs et changements d'API non prévus, et autant pour les utilisateurs de la bibliothèque qui doivent installer et compiler une centaine de dépendances avant de pouvoir l'utiliser (sous Linux heureusement ce point est relativement négligeable, mais sous Windows...).
Bref : les dépendances, moins j'en ai, mieux je me porte. Même si ça signifie qu'il faut que je ré-invente la moitié de la roue si je n'ai pas besoin du carosse.
2. Là par contre tu as raison : si le contenu d'un bloc de code Lua contient un '<' (l'autre ne gêne pas le parser, mais ce n'est pas une excuse), alors le fichier XML n'est pas lisible. Mais je dois pouvoir régler ça sans avoir recours au bloc CDATA (qui est quand même moche je trouve...), en spécifiant dans mon fichier de définition que les blocs <OnUpdate> etc. ne contiennent que des données et pas d'autres blocs. C'est minimaliste et non standard, mais là encore, bien suffisant pour l'utilisation que j'en fais (à noter : dans World of Warcraft, les blocs de code Lua ne sont pas encadrés par des blocs CDATA non plus, je ne sais pas quelle astuce ils utilisent).
3. Là encore tu as raison. Si la SFML est inutilisable ici, c'est justement parce que la bibliothèque de GUI s'attend à avoir un retour du clavier "non formaté", et elle se charge elle même de traduire les touches en lettres selon la locale qui aura été choisie par l'utilisateur (et contrairement à ce que tu crois, les combinaisons style '^' + 'e' = 'ê' sont bien prises en charge
pas les 'Alt' + xxx par contre). Mais comme tu le dis, c'est s'embêter pour rien, puisqu'il vaut mieux que le système gère lui-même cette étape là. Étant donné qu'il n'y a pas de solution portable pour le faire (je n'utilisais pas la SFML au début), j'avais préféré opter pour une solution "à la main", mais portable.
Bref, de mon côté aussi, il y a effectivement du progrès à faire (mais j'aurai quand même besoin d'avoir l'input non formaté, puisque la bibliothèque propose aussi un système de réponse aux touches du clavier, le "key binding", qui lui doit être indépendant du layout utilisé).
4. Du point de vue du rendu, les seules fonctionnalités dont j'ai besoin sont :
- pouvoir dessiner un quadruplet de vertex arbitraires,
- pouvoir dessiner un ensemble de tels quadruplets d'un coup (pour le texte : pas obligatoire, mais améliore les performances),
- pouvoir charger en mémoire un fichier image et manipuler son contenu à la main,
- pouvoir charger et rasteriser un fichier de police .ttf dans une texture (là je pense que tout le monde utilise Freetype...),
- et avoir des surfaces de rendu intermédiaires dont on peut modifier les fonctions de blending ("render targets" : pour le caching, mais aussi pour quelques autres utilisations plus exotiques).
Il est bien sûr possible de faire tout ça en OpenGL sans trop se casser la tête, et c'est donc ce que j'ai fait. Avec la SFML 1.6, la classe Sprite était trop restrictive pour la tâche, j'avais donc rajouté un type "Quad" qui permettait de traiter le premier point (... que j'avais d'ailleurs présenté à l'époque sur le forum anglais :
click). Mais les VertexArrays semblent (d'après l'API) pouvoir jouer le même rôle, tout en permettant de remplir le deuxième point. Donc a priori de ce côté là, plus de problème !
Par contre j'ai vérifié dans l'API pour les render targets : j'ai besoin de
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
qui n'est pas disponible. En effet, pour rendre correctement des objets transparents avec les render targets, la seule solution
exacte est d'utiliser des couleurs en alpha-prémultiplié. Le blend mode que j'utilisais avant (et que tu utilises toi aussi à première vue)
glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
est imparfait. Il fonctionne plus ou moins, mais certains cas particuliers rendent extrêmement mal (le texte en petites tailles, bien souvent).
Utiliser de l'alpha-prémultiplié peut sembler un peu effrayant au départ, mais ça ne change pratiquement pas le framework de rendu. Il suffit juste, quand on charge une image, de la parcourir et de multiplier les canaux R, G et B de chaque pixel par le canal alpha. Sans texture, si on veut afficher une sprite rouge à moitié transparente, il suffit de lui donner la couleur (128, 0, 0, 128) au lieu de (255, 0, 0, 128). C'est le prix à payer. Mais on y gagne : la fonction de blending reste
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
et ce, que l'on dessine dans une render target ou à l'écran, et le rendu est
parfaitement identique à celui qu'on aurait sans render target. J'ai même lu quelque part que, en évitant une multiplication (GL_ONE au lieu de GL_SRC_ALPHA), on gagne en performance. Mais je n'ai rien mesuré de tel pour le moment.
Par contre j'ai pas encore réfléchi à comment on gérer les autres modes de blending (add, multiply, etc.) dans cette situation...
Voilà pour les réponses ! Autant je pense ne rien changer sur le point 1 sauf faire la correction en 2, autant le point 3 est clairement perfectible. Le point 4 dépend de toi
Merci pour ce retour !