Compiler ses Shaders et ressources en XNB pour MonoGame avec Pipeline

Le Framework XNA proposait en son temps une génération du contenu à la volée lors de la compilation. C’était relativement pratique, mais aussi un peu ennoyant parfois car il n’était pas possible d’y mettre n’importe quoi. La principale difficulté quand on passe sur MonoGame est de construire ses assets au format XNB pour pouvoir les exploiter ensuite. Jusqu’à maintenant il fallait avoir recourt à plusieurs méthodes plus ou moins contraignantes pour les générer, de la VM Windows à l’installation du vieux SDK de Windows Phone, il y avait de quoi faire..

L’équipe du projet MonoGame travail depuis un moment sur plusieurs outils qui permettent de générer ces fichiers, ça a commencé par des outils en ligne de commande comme 2MGFX (génération de shaders), puis MGCB (Génération de XNB pour les images, fonts, modèles, etc…), puis ça a été au tour du plugin pour MonoDevelop (j’en ai parlé ici). Aujourd’hui un nouvel outil est arrivé, il est graphique et indépendant de tout IDE : Pipeline !

L'outil Pipeline
L’outil Pipeline

Disponible uniquement sous Windows pour le moment (un port xwt est en cours pour Linux et Mac Os X), cet outil va vous permettre de construire des XNB à partir de vos images (jpg, png, dds), spriteFont, XML, musiques, sons (Wave, MP3), modèles 3D (FBX, X) et shaders (FX).

Typiquement Pipeline permet de créer un nouveau projet de contenu ou d’en importer un existant depuis XNA. Dans tous les cas le projet final aura pour extension .mgcb. L’outil permet de spécifier un dossier de déploiement et c’est particulièrement pratique car vous pouvez spécifier le chemin de votre dossier de build, comme ça les actions de votre part sont minimes et vous pouvez enfin vous concentrer sur le développement et/ou la création d’assets, la génération devenant secondaire.

Il y a tout un tas d’options, on peut choisir le profil de construction (Reach ou HiRes), l’OS visé, c’est particulièrement intéressant si vous portez votre jeu sur plusieurs plateformes et il est même possible de compresser les assets ! Il en résultera un dossier Content beaucoup plus léger. J’attire votre attention sur le fait que tous les fichiers générés seront au format XNB, même les shaders. Ainsi si vous avez déjà utilisé 2MGFX, qui compilait en mgfxo, là ce n’est plus le cas (c’est donc exactement comme dans XNA, vous n’aurez pas d’extension à spécifier).

Mise en place

Vous avez plusieurs possibilités, la première est de récupérer un installeur de la version nightly de MonoGame pour Windows, ce package contiendra la dernière version du Framework de la branche develop, avec tous les outils qui vont bien, comme Pipeline.

La deuxième méthode (celle que j’utilise) consiste à cloner le dépôt de MonoGame et de compiler les sources depuis Visual Studio (ou tout autre IDE pour .Net). Si vous optez pour cette solution sachez qu’il est normal de ne pas voir de fichiers de solution à la racine du projet, il faut cliquer sur Protobuild.exe pour les générer 😉 De là il faudra ouvrir MonoGame.Framework.Windows.sln, vous mettre en mode Release et tout construire. Je vous recommande de vous faire un petit dossier Tools avec MGCB, 2MGFX et Pipeline.

Une fois que vous êtes en possession de ce fantastique outil, il ne vous reste plus qu’à intégrer un nouveau projet de type content à votre projet existant, ou démarrer un nouveau projet. Attention le projet de contenu n’apparaîtra pas dans Visual Studio.

Structure d'un projet
Structure d’un projet

Voici par exemple à quoi ressemble un de mes projet. J’ai un dossier global qui contient la solution du projet. Celle-ci référence les projets C3DE et C3DE.Demo. Le dossier C3DE.Content est le projet de contenu qui contient un fichier C3DE.mgcb et c’est ce fichier qui est utilisé par l’outil graphique Pipeline pour générer le contenu. Pour le créer c’est très facile, ouvrez Pipeline, faites File / New et choisissez un endroit où sauvegarder. De là copier vos ressources à côté du fichier et dans l’outils cliquez sur l’arbre pour ajouter du nouveau contenu (SpriteFont, XML) ou du contenu existant (typiquement vos images, musiques, etc…).

configFolderPour ma part, le dossier C3DE.Demo correspond au projet qui contient l’exécutable global du projet. Ainsi cet exécutable va vouloir taper dans un dossier Content. On peut en créer un dans le dossier bin/Debug|Release mais ce n’est pas pratique. Pour ne pas être obligé de copier manuellement les XNB à chaque fois, j’ai créé un dosier Content à la racine de ce dossier et j’ai ajouté ces quelques lignes dans le fichier du projet C3DE.Demo.csproj

 

<ItemGroup>
    <Content Include="Content\**\*.*">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
</ItemGroup>

Dernière chose, j’ai configuré mon projet de contenu avec Pipeline pour que les fichiers soient copiés dans ./C3DE.Demo/Content.

Copie des fichiers dans le dossier Content du projet actif
Copie des fichiers dans le dossier Content du projet actif

Ainsi à chaque fois que je reconstruis les assets avec Pipeline, les XNB sont automatiquement copiés dans le dossier Content de mon projet actif, de même quand je clic sur le bouton construire dans Visual Studio, ce dossier est automatiquement copié, avec son contenu à côté de l’exécutable.

Compiler des shaders pour MonoGame

Il y a des choses à savoir car porter des shaders de XNA 4.0 vers MonoGame peut ne pas être une partie de plaisir. De même, si vous écrivez vos effets à la main, il faudra garder en tête que ces derniers doivent être soignés afin d’être correctement compilés. Dans tous les cas l’outil Pipeline vous dira ce qui ne va pas et au pire, ça ne passera pas dans MonoGame (Si vous avez une erreur après un effect.apply() vous pouvez être quasiment sur que votre shader est défectueux).

Faites bien attention car la compilation du shader est agressive, si vous avez par exemple des paramètres qui ne sont pas utilisés (par exemple un AmbientIntensity) dans votre shader alors ceux-ci ne seront pas inclus dans le shader final. Ainsi si vous tentez de mettre une valeur à ce paramètre dans votre code, vous aurez alors une erreur. Soyez donc très vigilant sur ce point. Toutes les variables d’entrées du shader doivent être utilisées, sinon supprimez les et revoyez votre code.

Si vous développez pour Windows avec DirectX, il y a deux grosse choses à savoir. Primo dans la structure du VertexShader, il faut remplacer la sémantique POSITION[n] par SV_Position, c’est une nouveauté de DirectX 10 et c’est obligatoire. Je vous invite à vous rendre sur la page de documentation de Microsoft car il y a aussi d’autres sémantiques qui peuvent changer. Voici un exemple de structure d’entrée de VertexShader :

struct VertexShaderInput
{
#if SM4
	    float4 Position : SV_Position;
#else
	    float4 Position : POSITION0;
#endif
	    float4 Normal : NORMAL0;
	    float2 TextureCoordinate : TEXCOORD0;
};

Ensuite la compilation de la passe doit être défini avec un profil bien précis. Si vous déployez pour DirectX 10 minimum alors vous devez utiliser le profil [v|p]s_4_0_level_9_1, mais si vous voulez profiter de plus de fonctionnalités (et vous fermer quelques portes), il faut compiler en [v|p]s_4_0_level_9_3. Il faut savoir que certains shaders un peu chargés ne compileront pas en [v|p]s_4_0_level_9_1, il faudra passer en [v|p]s_4_0_level_9_3. Tous les profils disponibles sont consultables depuis ce lien. Voici un exemple de déclaration de technique qui fonctionne pour les cibles OpenGL et DirectX :

technique Textured
{
	    pass Pass1
	    {
#if SM4
        		VertexShader = compile vs_4_0_level_9_3 VertexShaderFunction();
		        PixelShader = compile ps_4_0_level_9_3 PixelShaderFunction();
#else
		        VertexShader = compile vs_3_0 VertexShaderFunction();
		        PixelShader = compile ps_3_0 PixelShaderFunction();
#endif
	    }
}

La directive de préprocesseur SM4 est définie quand vous compilez votre shader en mode DirectX.

Vous pourrez trouver plus d’informations sur la page dédiée aux shaders sur le site de MonoGame. Une dernière choses… n’appelez pas vos sampler, sampler 😉

Conclusion

Développez avec MonoGame est désormais beaucoup plus simple et facile, je pense qu’un jour nous seront au même niveau que XNA en terme de facilité de gestion des assets, voir supérieur car plusieurs plateformes seront prises en charge. Le port pour Linux et Mac sera une grande avancée, cela marquera la fin de la dépendance à Windows et annoncera aussi une nouvelle indépendance pour le Framework Open Source.

Si vous rencontrez des problèmes vous pouvez demander de l’aide sur les forums de la communauté MonoGame, ils sont très faciles et pratiques à utiliser, ça change de codeplex 😉 Sinon vous pouvez m’envoyer un petit mail ou laisser un commentaire.