Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Nazara - module de terrain] DynaTerrain

Terrain dynamique & infini

Sujet résolu
7 janvier 2013 à 21:18:05

Bonjour à tous,

Je suis Overdrivr, 23 ans, et je viens présenter un module que je programme pour le moteur de jeu Nazara. C'est un moteur de jeu, dévellopé par Lynix, qui se veut facile d'accès à l'utilisateur mais aussi très puissant. A vous d'en juger, mais pour ma part je pense que c'est déjà sur une très bonne voie pour réaliser cet objectif ! :)

J'ai aussi contribué à ce moteur avec un module de génération de bruits, et je me remet le couvert pour un terrain dynamique et infini. Je le précise car NzNoise sera utilisé dans DynaTerrain pour générer des terrains de manière procédurale.

Dépôt Github (Fork) : https://github.com/Overdrivr/NazaraEngine
Dépôt Github Officiel : https://github.com/DigitalPulseSoftware/NazaraEngine

En savoir plus sur le projet

Genèse

Ceci est ma deuxième tentative (lien vers la première), j'ai réécrit de zéro le système complet, en partant sur de bien meilleures bases, et en utilisant cette fois-ci une approche de design logiciel (par opposition au bidouillage qu'était la première :) )

Généralités et avancement
DynaTerrain est un moteur de terrain. Mon but est de fournir à l'utilisateur, dans Nazara, un système avancé de terrain, capable de gérer des espaces bien plus importants que les terrains classiques, éditable localement et généré procéduralement ailleurs.

Là où un terrain classique pourra gérer au grand maximum quelques dizaines de km², DynaTerrain devrait être capable de gérer des étendues de la taille de la France à une résolution locale d'un mètre. C'est bien évidemment un ordre de grandeur, mais qui est proche de la réalité, voire en dessous.

DynaTerrain devra également être très simple à utiliser.

Utilisation
Le code minimal à appeler est très court :

//La classe héritée de NzHeightSource, dont l'utilisateur a rédéfini la méthode GetHeight(float x, float y)
MyHeightSource source;

//On utilise la configuration par défaut, pas besoin de s'embêter
NzTerrainQuadTreeConfiguration myConfig;

//Le terrain en lui même (en interne, c'est un quadtree)
NzTerrainQuadTree quadtree(myConfig,NzVector2f(0.f,0.f),&source);

//On l'initialise
quadtree.Initialize(NzVector2f(0.f,0.f));

//Le terrain est initialisé

/*...*/
//Dans la boucle principale
quadtree.Render();//Le terrain est dessiné... les modifications du maillage et le culling sont automatiquement effectués


La seule autre opération à effectuer est d'hériter la classe HeightSource, et de réimplémenter GetHeight (par exemple si on veut que notre terrain ait une forme sinusoidale ) :

float MyHeightSource::GetNoiseValue(float x, float y)
{
   return (std::sin(x/100.f)+1);
}


Ce système permet à l'utilisateur d'implémenter facilement n'importe quelle méthode pour récupérer la hauteur de manière procédurale. C'est très flexible, et ça n'impose aucune contrainte.

Pour les données manuelles, le chargement sera implémenté dans la classe mère NzHeightSource, l'utilisateur n'aura juste qu'à appeler dans le main :

MyHeightSource source;
source.LoadTerrainFile("terrain.hsd");


La magie de l'héritage :)

Fonctionnalités actuelles - Objectifs

Voici la liste des fonctionnalités, avec en vert celles implémentées et fonctionnelles :
  • Précision variable du terrain en fonction de la pente
  • Augmentation de la précision à proximité de la caméra
  • Gestion des transitions entre 2 niveaux différents de précision
  • Terrain dynamique
  • Terrain infini
  • Utilisation de sources procédurales (NzNoise)
  • Utilisation de sources manuelles (heightmap éditables par l'utilisateur)
  • Mélange des deux sources


Je vais détailler les points qui peuvent paraître compliqués :

Précision variable en fonction de la pente

Le principe est simple, plus la variation de pente d'une zone est importante (= flanc d'une montagne), plus cette zone doit être précise pour éviter des "étirements" de triangles trop importants. Cette technique permet à des reliefs abruptes de garder leur forme globale, et réduit les artéfacts visuels.

Démonstration :
Image utilisateur

Et en remplaçant simplement ma fonction de hauteur de test par une fonction de bruit de NzNoise :
Image utilisateur

On voit clairement que lorsque la variation de pente (c.a.d la dérivée de la pente) est plus importante, le maillage est plus précis (+ de triangles)

Image utilisateur

Des couleurs et des textures sont à venir, ne vous faîtes pas de soucis :p

Terrain dynamique ?

Un terrain dynamique est un terrain dynamique est un terrain dont la géométrie s'adapte en temps réel, par exemple pour être plus détaillé à proximité de la caméra. Un tel terrain permet d'afficher des étendues très très vastes avec une excellente résolution locale.
Pour celà, des blocs de terrains entiers seront "collés" les uns aux autres, pour faire en sorte que la caméra soit toujours entourée de relief lorsqu'elle se déplace.

A propos des sources de hauteur

Pour chaque point (x,y) du terrain, le programme a déterminé une hauteur. Cette hauteur, qui est quasiment différente pour chaque point, peut provenir de différentes sources.

Soit elles proviennent d'une carte de hauteur (heightmap), soit elles proviennent d'une fonction de bruit.
  • La heightmap permet à l'utilisateur d'éditer manuellement la hauteur d'un terrain à sa guise. C'est très utilisé dans les jeux et c'est très pratique. Néanmoins, il faut un certain temps pour dessiner une heightmap, la précision ainsi que l'occupation spatiale est limitée (pas de terrain infini).
  • A l'inverse, une fonction de bruit ne permet pas d'éditer manuellement la hauteur de chaque point, mais au contraire d'éditer l'aspect global du terrain assez finement. Et comme ce sont des fonctions définies pour toutes coordonnées (x,y), elles permettent de générer un terrain infini.


Le but de ce module est donc de combiner ces deux techniques, pour ne garder que les avantages, c'est à dire avoir un terrain infini ET localement éditable.

Cette méthode j'en suis convaincu, peut permettre de gagner un temps considérable pour créer un terrain, en modifiant manuellement les zones d'intérêt et en délégant le gros du travail aux fonctions de bruit, c'est à dire, votre ordinateur. L'architecture du module ayant été pensée pour ça à la base, cette fonctionnalité s'intègrera assez naturellement dans les mois à venir.


Merci de m'avoir lu jusqu'au bout ! :)
  • Partager sur Facebook
  • Partager sur Twitter
7 janvier 2013 à 21:49:21

Bonsoir,

Comme beaucoup, je suis le moteur Nazara en admirant le travail de titan abattu. Mais je suis encore plus enchanté de voir des contributeurs à un projet open-source naissant du Site du Zéro. Je ne peux qu'encourager ce genre d'initiative.

Toi qui ne programme pas le coeur du système et qui a donc dû apprendre le moteur et son architecture, serais-tu communiquer ton expérience en expliquant comment tu as procédé pour comprendre comment utiliser ce moteur sans documentation disponible ? Je serais très interessé de savoir.

En tout cas, je suivrai ton projet comme je suis celui de Nazara.

Edit : Cela dit, pourrais-tu éditer ton premier poste pour faire une petite mise en forme. Adapte celui présenté dans les règles mais sinon tu risques d'avoir des alertes des membres que nous recevrons. D'autres modérateurs pourraient modérer à la louche et fermer ton topic.
  • Partager sur Facebook
  • Partager sur Twitter
Si vous voulez me retrouver, rendez-vous sur ZesteDeSavoir.
Anonyme
7 janvier 2013 à 22:55:40

Bonjour !

Travaillant sur un générateur de carte dans le cadre de mon projet actuel, je trouve votre travail impressionnant, passionnant et utile à plus d'un titre ! :)

Il est dommage que je ne puisse tirer profit de cette librairie par rapport au langage "zombi" que j'utilise ( VB6 ), mais le système de bruit que j'ai pu lire dans votre précédent post est vraiment intéressant ... Et de toute les manières, créer un algorithme de génération de terrain ( 2D en ce qui me concerne ... ), est passionnant à réaliser !

Chapeau pour ce que vou... Tu fais ( je vouvoies un jeune de deux fois mon âge ... allez je te tutoies ^^ ), ta librairie vaut vraiment le coup ... Peut être qu'un jour elle me servira qui sait ... ^^

Cordialement.

V.

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
7 janvier 2013 à 23:08:04

Bien que ça semble évidant pour toi tu ne précises pas certaine évidence (moins évidente pour le lecteur). A moins que tu souhaites toucher qu'une partie des zéros. :euh:

Citation : OveRdrivR

Là où un terrain classique pourra gérer au grand maximum quelques dizaines de km², DynaTerrain devrait être capable de gérer des étendues de la taille de la France à une résolution locale d'un mètre. C'est bien évidemment un ordre de grandeur, mais qui est proche de la réalité, voire en dessous.



Si je comprend : Un terrain classique ne génère que quelques dizaine de km² car il atteint la limite des performances en mémoire, alors que ton système optimise l'utilisation de mémoire en détaillant plus les zones de la carte avec précision que d'autres endroit n'ayant pas besoin de cette précision ?
  • Partager sur Facebook
  • Partager sur Twitter
7 janvier 2013 à 23:23:48

Bonsoir,
La mise en forme devrait être nettement meilleure désormais, mais ne pas hésiter à m'indiquer si c'est encore un peu fouillis !

En tout cas, merci pour les encouragements, ça fait au coeur. :)

Je vais essayer d'expliquer ma façon de procéder pour programmer les modules. Tout d'abord, Nazara est véritablement intuitif, et ça se traduit de différentes façons. Premièrement, le moteur est facile à utiliser. Pour peu que tu aies déjà pratiqué OpenGL, utiliser le Renderer est un jeu d'enfant car il suit la même méthodologie. C'est juste encapsulé dans des classes de plus haut niveau. Par exemple, avec opengl il te faut charger tes shaders, les compiler, etc. Avec Nazara, c'est identique sauf que tout est déjà implémenté.

Bien évidemment, le meilleur moyen d'apprendre c'est de lire les exemples. Les noms des classes et des méthodes sont très explicites, ça se lit très bien. L’enchaînement des appels à chaque méthode est très logique. Du coup ça se comprend et ça se retient.

Ensuite, si tu passes côté développeur, ajouter un module n'est pas plus compliqué. A la racine du dépôt Github de Nazara, il y a un dossier appelé NazaraModuleTemplate. Comme son nom l'indique, ce dossier est un template de module, il contient tout les fichiers de base nécessaire à un module. Si tu créé un module appelé XXXX, il te suffit de remplacer ModuleName à chaque fois que ça apparait par XXXX.

Si tu vas dans ce dossier, il y a trois sous-dossiers :
  • build/scripts/module/modulename.lua : c'est le fichier script de premake. Il faut remplacer modulename par xxxx et rajouter sous "configuration" les dépendances aux autres modules. Si ton module dépend du renderer, tu rajoutes pour chacun des cas (debug et statique) 'NazaraRender-x-x' sous NazaraCore-x-x. Et c'est tout.
  • src/Nazara/ModuleName/ : le dossier ou tu mettras les fichiers sources de ton module. Modifier les noms comme précédemment.
  • include/Nazara/ModuleName/ : idem mais pour les headers.


Ensuite tu déplaces ces dossiers (build, include et src) à la racine, où ils fusionneront avec ceux déjà existants, et voilà, ton module est en place.

Egalement, un point essentiel à mon sens, c'est l'utilisation de premake. En exécutant le script pour ton IDE situé dans build/, premake détecte automatiquement les nouveaux fichiers et regénère le makefile adapté à ton IDE. Il va mettre tout les fichiers nécessaires dans un sous-dossier du nom de l'IDE. Dans mon cas, sous code::blocks, j'ai un workspace code::blocks automatiquement crée. Je l'ouvre avec code::blocks et lance la compilation. Recompiler Nazara prend 1 min montre en main, et ça c'est appréciable. CMake est très puissant, mais personnellement je préfère premake, pas d'installation à faire pour l'utiliser, c'est plus rapide, rien à configurer, tout les produits de la compilation (lib et dll) sont dans un dossier propre, l'arborescence n'est pas polluée par des fichiers de compilation.

Voilà en gros, j'essayerais de compléter à l'occasion. N'hésites pas à demander si des points sont obscurs !

@Vetouille : Merci ! On est bien d'accord, c'est vraiment passionnant ;)

@Hors-Sujet : Oui j'en suis conscient et je m'en excuse, le premier post est un peu long mais a besoin d'être affiné/complété. Ca viendra dans les jours qui suivent promis ;) En tout cas c'est ça tu as raison. Bien que certaines des optimisations que je décris peuvent y être également appliquées. Le vrai avantage, c'est de combiner procédural+heightmap.
  • Partager sur Facebook
  • Partager sur Twitter
24 janvier 2013 à 19:04:52

Je profite du retour des forums pour faire un peu le point.

J'ai eu relativement peu de temps pour cause d'examens, mais le module a quand même avancé. Le shader s'est notamment étoffé, il calcule désormais la pente de chaque pixel, ainsi que son altitude, et surtout prend en charge les....TEXTURES !

Afin de plaquer correctement les textures, les coordonnées de texture UV de chaque pixel sont calculées à la volée dans le shader. Pour être plus précis ce sont en fait les coordonnées UVW qui sont calculées selon les 3 plans xy, xz et yz. Le shader choisit le plan le plus approprié (ou un mélange des plans) pour obtenir le moins de déformation des textures lorsque la pente est très importante. Ceci s'appelle le triplanar texture mapping.

La démonstration sur un terrain définit par une courbe exponentielle :

Je précise bien, simplement avec une seule texture et des normales correctement orientées, le shader génère ça tout seul comme un grand. Pas besoin d'envoyer les coordonnées de texture des vertices à la carte graphique :) La zone de transition peut paraître un peu moche, mais pas d'inquiétudes à avoir, avec de "vraies" textures c'est invisible.

Le shader est également capable, en fonction de critères établis par l'utilisateur, de spécifier si il doit utiliser telle ou telle texture pour telle pente/altitude. Par exemple, si l'altitude est > à 500 f, il utilisera la texture de neige. Sinon, là où la pente est très forte, une texture de falaise, etc.. Je n'ai malheureusement pas de capture d'écran à vous montrer, car sincèrement mes textures de test sont particulièrement moches, je suis une bille avec toshop ^^ Je trouverais bien une âme dévouée pour m'aider pour ça...

Last but not least, la précision dynamique autour de la caméra est enfin implémentée. Ce n'est par contre que la moitié du travail qui a été effectuée, car il me faut maintenant diminuer la précision lorsque la caméra s'éloigne d'une zone. Rien de bien compliqué, juste bidouiller une liste de node, mais il faut le faire ! Ce système en démonstration :

Prochain objectif, finir cet aspect dynamique, puis pouvoir relier différents quadtree entre eux (terrain infini et une surprise à la clé) et nettoyer le code.

Une dernière image produite durant le développement que je trouve particulièrement chouette (pour les curieux -> normales/pixel)

J'en profite pour rappeler que mon module n'est qu'une fraction du projet opensource Nazara de Lynix, et que vous êtes tous les bienvenus pour nous aider à étendre le projet. Ca n'a rien de compliqué, et on se fera un plaisir d'aider qui que ce soit.

Bonne soirée à tous

  • Partager sur Facebook
  • Partager sur Twitter
4 février 2013 à 10:46:40

Un petit up pour dire que les "trous" normalement visibles à l'interface entre deux portions du terrain de précision différentes ont été éliminés. J'ai réussi à faire ça en adaptant le mesh aux interfaces de la manière suivante (à gauche, non adapté, à droite, adapté) :

Adaptation du mesh aux interfaces

En action ça donne ça :

Voila pour les dernières nouveautés !

  • Partager sur Facebook
  • Partager sur Twitter
4 février 2013 à 12:53:58

Salut OveRdrivR,

J'ai une question qui me taraude : développes tu à partir de Nazara ou bien c'est un code totalement nouveau (je parle par rapport aux mesh, etc) ? En outre, je suppose que c'est le moteur qui gère quasiment tout pour l'affichage des mailles et tout, non ?

Sinon, super boulot, typiquement le genre de truc qu'on recherche en tant que module.

Alex.

  • Partager sur Facebook
  • Partager sur Twitter
Tell me and I forget. Teach me and I remember. Involve me and I learn.
4 février 2013 à 17:51:25

C'est un plaisir à voir et à lire, vraiement vous avez bcp de talent.
  • Partager sur Facebook
  • Partager sur Twitter
Mon 1 er jeu en java Space Mercenaire
4 février 2013 à 18:26:25

@NDKa : Non, je n'ai aucun appel à OpenGL dans mon code, j'utilises les classes du module Renderer, qui fournissent une première couche d'abstraction sur le rendu. Dans le futur, une fois le manager de scène implémenté par Lynix fonctionnel, il me suffira de déclarer chacune des "portions" du terrain en tant que NzNode et de fournir une AABB correcte pour chaque portion, pour pouvoir bénéficier de toutes les fonctions du scène manager (culling, éclairage amélioré, etc.).

Merci à tous les deux pour les encouragements, ça motive beaucoup, j'espère vous voir profiter de ça et utiliser Nazara dans le futur ;) 

  • Partager sur Facebook
  • Partager sur Twitter
8 février 2013 à 11:51:01

Ca y est, la fusion dynamique de nodes est fonctionnelle. Faute de temps, plutôt que de faire une vidéo passablement utile, je pense plutôt diffuser une démo, ça sera plus interactif ;) J'ai encore 2-3 problèmes à corriger, on peut raisonnablement espérer que je la mettrais en ligne courant de la semaine prochaine.

Ensuite, refactorisation du code et nettoyage, puis ajout de diverses fonctionnalités, tel que la possibilité de lier des terrains entre eux pour créer un terrain infini, et enfin, la petite surprise comme promis.

PS : Je ne parviens pas à modifier le premier message, ça me casse les pieds. Du coup, la bannière que je réservais pour le 1er post va atterrir ici :

  • Partager sur Facebook
  • Partager sur Twitter
9 février 2013 à 17:16:30

Je m'occupe de la vidéo, je la posterais sur le topic de Nazara en même temps qu'une petite news ;)

Voici: http://www.youtube.com/watch?v=Mm2pXM0SeWA

-
Edité par Lynix 9 février 2013 à 21:07:27

  • Partager sur Facebook
  • Partager sur Twitter
Nazara Engine | Discord NaN ! (Chat dédié à la programmation) | Ma chaîne Twitch (développement)
9 février 2013 à 20:48:38

C'est chouette de voir des modules de ce genre arrivé sur le moteur Nazara. Cependant, en tant que graphiste comment je peut utilisé concrètement ce module ? Pour faire une heightmap aucun soucis mais comment l'utiliser ? La partie du terrain générer par du procédurale devient éditable par la suite ? En fait, pour moi la question serait plutôt "Comment je peut ajouter des modèles ou contrôlé les partie de ce terrain ?

En tout cas c'est intéressant.

  • Partager sur Facebook
  • Partager sur Twitter
9 février 2013 à 22:27:44

C'est une très bonne question, à laquelle je n'ai pas répondu dans le premier post en effet. Et bien tant qu'il n'y aura pas d'éditeur graphique, je compte intégrer une fonctionnalité assez simple : Dans dynaterrain, pendant l'execution (ou simplement en spécifiant une position donnée), il sera possible d'exporter sous forme de fichier image bmp la heightmap d'un zone autour de la position de la caméra (ou de la position donnée). De là il te sera possible de la modifier manuellement sous photoshop ou autre, puis de réimporter la heightmap sous DT, qui remplacera localement les valeurs procédurales par ces valeurs manuelles.

C'est évidemment une fonctionnalité qui manque d’interactivité, mais au fur et à mesure du développement de Nazara et l'arrivée de la GUI, ce sera progressivement remplacé par une édition directe.

PS : Un grand merci pour la vidéo Lynix ;)

  • Partager sur Facebook
  • Partager sur Twitter
13 février 2013 à 13:54:39

Ca y est, la démo est disponible !

J'en ai profité pour faire un nouveau topic propre, désormais c'est là bas que ça se passe : http://www.siteduzero.com/forum/sujet/nazara-dynaterrain

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
13 février 2013 à 17:57:07

Je ferme donc ici.

Bravo pour le travail accompli au passage, je suis ça de loin. ;)

  • Partager sur Facebook
  • Partager sur Twitter