Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Moteur 3D] hARMful

Ensemble de bibliothèques C++/OpenGL

    2 avril 2020 à 0:13:04

    Bonjour/Bonsoir à toutes et à tous,

    Présentation

    Je m'appelle Denis et je suis né en mai 1987. J'ai suivi un parcours scientifique au lycée en m'orientant vers la biologie pour la spécialité du bac puis dans mes études, en licence et en master recherche. Au cours du master, j'ai acquis quelques connaissances en programmation (Java et PHP), ce qui m'a aidé dans la suite de mon cursus. En plus de tout ça, j'ai commencé au lycée à faire des modèles et des animations en 3D avec Blender. J'ai continué mes projets en licence et en master. Un autre bon point pour la suite de mes études.

    En effet, faute de pouvoir trouver une thèse, je me suis réorienté vers une école d'ingénieurs en informatique. Je suis rentré en 3ème année, l'école n'acceptant que des étudiants au niveau d'au moins bac+2 (on n'y fait que les 3 dernières années de la formation). Avant la rentrée dans ce monde "nouveau", j'ai beaucoup étudié le langage C++ pendant les vacances scolaires et j'ai très rapidement aimé ce langage. J'en avais entendu parlé comme étant un langage plus "bas niveau" lorsque j'étais à la fac, je ne voulais donc pas être largué.
    La première année n'a pas été facile. Le langage d'assembleur m'a donné des sueurs, les mathématiques me laissaient en PLS... J'ai malgré tout réussi à passer tous les examens du premier coup !
    L'année suivante était aussi plus intéressante en contenu : nous pouvions avoir un aperçu de nos spécialisations, prévues pour la dernière année. Je me suis tout naturellement tourné vers le multimédia et la 3D. C'était vraiment sympa et nous avons même pu développer un jeu comme projet d'école.
    Enfin, la troisième année a continué sur cette lancée. Ce qui était assez drôle, c'était d'avoir des cours de Blender. A la fin des TP, nous devions faire une courte animation d'un poulpe. La prof avait remarqué que je connaissais (un peu) le soft, donc j'ai pas pu m'en tenir à l'énoncé...
    A la fin de mon stage de fin d'études, j'ai été l'un des majors de promo. Il y en avait un pour chacune des spécialités. Jolie revanche pour quelqu'un qui captait rien en maths et qui faisait de la biologie 3 ans auparavant. :D

    Je suis maintenant ingénieur logiciel 3D temps réel, depuis plus de 5 ans. C'est pourtant quand j'étais encore étudiant que j'ai commencé le projet que je viens vous présenter.

    hARMful banner

    En savoir plus sur le projet

    Génèse

    Quand j'étais sur les bancs de l'école, je faisais souvent de petits projets persos sans grand intérêt. Je voulais juste pratiquer pour m'améliorer en plus des cours et des TPs. Seulement, ces projets me demandaient souvent de récupérer des fichiers pour des choses que j'avais déjà codées... C'était un peu embêtant de ne pas avoir ça sous forme de bibliothèque facilement réutilisable. Il m'est alors venu l'idée d'englober ces différentes fonctionnalités, principalement du réseau à l'époque, dans une bibliothèque. Puis j'ai voulu tester un système de combats de type RPG (en mode console) dans lequel les "personnages" avaient des caractères différents, impactant leur comportement en groupe : l'altruiste soigne énormément ses potes, le téméraire attaque et se fiche d'être en bonne santé, l'égoïste ne soigne personne d'autre que lui-même, etc. Je m'étais alors amusé à voir comment se comportaient ce petit "monde" en groupe, avec l'envie d'avancer vers de la vraie intelligence artificielle (au sens deep learning). Je n'ai pas atteint le stade du deep learning, mais l'idée est toujours dans les cartons ! J'ai emballé tout ce code, y compris un système d'inventaire, dans une bibliothèque que j'avais appelée DISDAIN (pour Damage + Inventory Systems and Dynamic Artificial Intelligence Network).

    D'un autre côté, j'ai découvert le premier Raspberry Pi à cet instant. J'ai voulu tester la puce graphique avec un moteur 3D existant. J'ai tenté de compiler OGRE dessus... échec. Mon choix est alors allé vers Irrlicht et j'ai pu parvenir à le faire fonctionner (je ne sais pas maintenant mais à l'époque, il n'était pas prévu pour compiler clé en main sur le RPi). Simplement le mal était fait : toute cette galère m'avait donné l'idée de faire un moteur de jeu sur Raspberry Pi...
    DISDAIN compilait bien sur le Raspi. Il fallait continuer ! J'ai ensuite voulu ajouter des logs et d'autres utilitaires pratiques à ma première lib. Ainsi est apparue DOOM (Debugging and Oriented-Object Modules). J'ai ensuite commencé à développer une autre bibliothèque, cette fois dédiée aux mathématiques : MIND (Mathematics Interface to New Developments).

    Le projet hARMful était né. Les différentes bibliothèques qui le composent seraient compilables sur un Raspberry Pi en limitant le nombre de dépendances tierces (car potentiellement non disponibles pour le Raspi).
    hARMful (Handy, Adapted to Raspi Multimedia and Fast Utilities Libraries) est un acronyme mettant aussi en valeur le processeur ARM du petit ordinateur.

    Pour le logo du projet, je me suis basé sur d'une part une chose qui me tient à coeur dans mes développements en général : la simplicité. Donc, comme pour beaucoup de programmes liés à la 3D, un cube représente cette simplicité. Mais un cube simple n'est pas suffisant. Il em faut quelque chose de dangereux pour respecter le nom de "harmful" ("dangereux" en anglais). J'ai donc choisi un animal qui représente les deux choses : une méduse-boîte. En plus de ça, pour le design du logo, j'ai choisi un style qui rappelle également un microprocesseur avec les tentacules.

    Lien vers le code source du projet : https://github.com/dcarlus/hARMful

    Généralités et avancement

    Depuis cette époque, le code a subi un refactoring profond. DISDAIN, la "première vengeresse", n'est plus... Jusqu'au jour où elle reviendra !
    DOOM a été épurée (le code n'est plus à jour, il faudra faire une passe).
    MIND a été quasiment toute recodée. Avant elle ne tournait qu'en CPU de façon très classique. Maintenant, elle supporte exclusivement SSE2 (jusqu'à SSE4.2) pour les processeurs Intel et AMD. Le projet n'est plus directement lié au Raspberry Pi même si le portage sur la plateforme reste dans les cartons. Pour ça, il faudra d'abord assurer le support de NEON pour les processeurs ARM en général.

    DOOM et surtout MIND ont fait l'objet d'une longue série de tests unitaires. MIND est couverte à 100% dans le but d'écarter le maximum d'erreurs de calculs venant d'elle.
    Une autre bibliothèque est apparue, il s'agit de SPITE (Saving and Parsing Images, Texts and Extra). Son nom l'indique, elle sert à lire des fichiers d'images et de texte. L'écriture sera bientôt gérée.


    Depuis l'année dernière, j'ai commencé la bibliothèque qui nous intéresse tout particulièrement ici : l'affichage 3D opéré par HOPE (Handy OpenGL and Physics Engines). C'est donc du rendu en OpenGL 4.6.
    La v1.0 du moteur de rendu est sortie il y a quelques semaines, après plus d'un an de développement rien que pour HOPE. Je précise qu'il s'agit - à ce jour - uniquement d'un moteur de rendu. Ne vous attendez pas à voir un jeu réalisé avec car ce n'est pas du tout possible ! Je ne gère même pas encore les événements du clavier... Mon but premier, c'est le rendu et tout particulièrement le rendu PBR - j'y reviendrai. Le reste viendra un peu plus tard.

    Cette v1.0 n'offre pas énormément de contenu en terme de features. Voici une petite vidéo qui précise la nature des principales features disponibles actuellement.
    A noter que le moteur compile sous GNU/Linux (g++) et Windows (Visual Studio 2019). Les indications de build sont présentes dans le README du projet, vidéos à l'appui.

    Vidéo de démo de la v1.0

    La v1.0 pose essentiellement les bases de l'architecture du moteur 3D. J'ai déjà une base pour des effets complémentaires pour le rendu (HDRI, DoF, ...). Il y a aussi énormément de code qui me sert et qui me servira encore beaucoup pour la suite du projet telle que la gestion des framebuffers.
    Le moteur se base sur un système d'entités-composants pour l'arbre de scène. Il n'est pas du tout optimisé mais des pistes sont déjà prévues pour améliorer l'ensemble (static batching et frustum culling par exemple).
    Un second arbre, le framegraph, est utilisé pour définir le rendu. Ce graphe permet de gérer de façon simple les différentes passes de rendu, mais aussi le viewport, la caméra utilisée, les effets visuels, ... Ce graphe est bien sûr dynamique et on peut le modifier à tout moment en cours d'exécution. En terme d'architecture et donc de développement, c'est un sacré allié. Pour l'ambient occlusion par exemple, je définis un sous-arbre constitué de noeuds déjà codés et je n'ai quasiment que des shaders à développer. La structure du code elle-même pour faire cet effet est énormément simplifiée.

    Depuis la release de la v1.0, j'ai déjà sorti 2 patchs pour corriger des bugs importants et divers problèmes d'OpenGL.
    Un troisième est dans les cartons pour corriger un autre gros bug lié aux ombres. Depuis que le projet compile sous Windows, je peux me servir à la fois d'un bon débogueur (Visual Studio) en combinaison avec NSight Graphics. Ce sera très utile car sous Linux, je me sers uniquement de Atom pour coder - ce qui est pour moi la façon la plus optimale de coder accompagné du fidèle terminal ^^. Mais je dois bien l'avouer, pour déboguer c'est plus embêtant.

    Exemple de scène

    Pour la suite du projet, j'ai déjà planifié différentes release faisant un peu penser à des Epics - même si la définition est subjective - si on parlait gestion de projet.

    • v1.1: je souhaite m'attaquer aux shaders pour du rendu basé sur la physique (PBR) ainsi que de l'illumination basée sur des images (IBL). Si vous ne voyez pas de quoi il s'agit, voici un exemple de rendu PBR avec Blender et son moteur temps-réel EEVEE : https://www.youtube.com/watch?v=nxrwx7nmS5A
    • v1.2: ici, je m'occuperai d'améliorer l'ombrage[^1] (jusqu'ici uniquement possible pour les lampes directionnelles) et ajouter des matériaux transparents (OIT, pour "order-independent transparency").
    • v1.3: phase #1 de l'amélioration des performances avec un "static batching" sur le scene tree avant de lancer la boucle principale.
    • v1.4: ajout des inputs pour contrôler les objets au clavier, souris, manette ; et intégration du moteur physique.
    • v1.5: support des meshs animés (squelettes) et systèmes de particules dont l'instanciation de meshes.
    • v1.6: phase #2 de performances (octree, frustum culling).
    • Pour la suite, sans réfléchir encore aux priorités : illumination globale, ground-truth ambient occlusion, système de scripting, utilisation de NEON (ARM), Vulkan...

    [^1] J'ai déjà amélioré la qualité des ombres, mais ça ne fait pas partie de la v1.0 !

    A noter que le moteur s'utilise uniquement sous forme de code. Je développe en sources fermées une GUI mais elle n'est pas ma priorité pour le moment.

    Merci d'avoir lu tout ce pavé !

    Portez-vous bien ! :)

    -
    Edité par AntiXeon 2 avril 2020 à 0:36:58

    • Partager sur Facebook
    • Partager sur Twitter
      8 avril 2020 à 12:27:19

      Bonjour à tous,

      Comme je l'avais expliqué dans le premier post, je travaille sur un patch pour corriger quelques bugs. J'ai passé quelques jours spécifiquement sur les ombres afin de faire une première passe et en corriger de gros défauts évidents. Il en reste encore mais pour l'heure, ça me semble déjà beaucoup mieux. Le rendu me paraît pour le moment assez suffisant pour pouvoir aller vers du rendu physiquement réaliste sans que ça ne choque trop les rétines.

      Voici une vidéo pour conclure cette première phase de traitement des ombres. Il y en aura encore d'autres dans la vie du projet.

      https://www.youtube.com/watch?v=BIaklMRR1RQ

      • Partager sur Facebook
      • Partager sur Twitter
        21 mai 2020 à 18:17:31

        Bonjour,

        Le projet est toujours en développement. :lol:

        Après avoir finalisé la correction de quelques bugs, j'ai finalement fait une transition assez brutale vers une v2.0 toujours en préparation. Elle remplace la v1.1 initialement prévue mais incluera tout de même les matériaux PBR. Une première étape a d'ailleurs été faite et les matériaux eux-mêmes sont donc gérés.


        Modèle : https://3dmodelhaven.com/model/?c=furniture&m=WoodenTable_02

        J'aimerais ajouter d'autres algorithmes pour améliorer le rendu de certaines surfaces comme le clear coat, particulièrement adapté pour le rendu de voitures (peinture métallisée).

        En ce moment, je travaille sur l'éclairage ambiant (IBL). Avant de pouvoir l'intégrer au rendu, je dois gérer les différentes textures qui serviront. J'ai déjà de quoi charger une HDR (radiance map) et je parviens à convertir un panorama 360° vers une cubemap. Par contre, il me faut générer deux autres images : l'irradiance map (pour l'éclairage ambiant) et la specular map (pour les reflets).

        Pour générer ces images, je ne vais pas intégrer les calculs dans le moteur lui-même, ni la conversion panorama vers cubemap comme je l'avais prévu au départ. Je développe un outil en ligne de commande qui fait ces calculs d'un côté et de l'autre le moteur chargera directement les images générées.

        Il existe bien des outils qui font déjà ce genre de chose comme cmftStudio ou IBLBaker. Avec cmftStudio, j'ai des outputs qui correspondent à ce que je souhaite avoir (fichier .hdr) mais il est assez bogué : les images contiennent des artefacts qui se voient donc au rendu. Quant à IBLBaker, les fichiers en sortie ne me conviennent pas (format .dds que je ne sais pas encore lire).
        Enfin, un outil simple et rapide me convient mieux plutôt que ces logiciels avec une UI "compliquée" pour juste générer des images.

        Je me dis que ce genre d'outil pourrait aussi intéresser d'autres personnes, il sera donc publié sur GitHub une fois bien avancé !

        La génération de l'irradiance map commence à fonctionner. Petite anecdote : je me sers du moteur lui-même pour les générer.
        Au niveau du code, il y a encore à faire pas du nettoyage et des arrangements, le plus gros est en place ! Les images sont générées indépendamment les unes des autres, je pense garder cette séparation pour éviter un découpage qui pourrait allonger le chargement des cubemaps.

        Le panorama HDR convertit en cubemap (https://hdrihaven.com/hdri/?c=outdoor&h=palermo_sidewalk) :

        L'irradiance map correspondante (assemblée à la main pour vérifier les raccordements) :

        -
        Edité par AntiXeon 21 mai 2020 à 18:23:58

        • Partager sur Facebook
        • Partager sur Twitter
          26 mai 2020 à 11:12:19

          Bonjour,

          Je reviens avec une version déjà utilisable du logiciel incredIBL, celui qui convertit un panorama 360° (HDR) en cubemaps adaptées à l'image-based rendering (IBL).

          Le dépôt GitHub se trouve ici : https://github.com/dcarlus/incredIBL

          Comme expliqué dans le "LisezMoi", c'est un outil qui se lance en ligne de commande avec quelques paramètres pour ajuster les fichiers de sortie (taille des images, nombre de spéculaires, etc). Un exemple est illustré en bas de la page du projet pour montrer tout ce qui est produit.

          Les cubemaps fonctionnent (je crois) uniquement avec des images dont la taille est une puissance de 2. C'est pourquoi, quand on rentre une taille pour par exemple l'environment map, elle sera modifiée pour la puissance de 2 la plus proche. Par exemple, si on donne "--envmap 967", les images correspondantes seront en  réalité d'une taille de 1024x1024.

          Comme la génération des images se base sur le GPU (en fait j'utilise les shaders de LearnOpenGL tel que mentionné dans leur code source), les calculs sont très rapides y compris pour des images en entrée de 8k ou 16k. Les latences sont dues principalement aux lecture et écriture des fichiers.

          Un truc que j'aimerais ajouter est un format de fichier spécifique nommé HEM (hARMful environment map) pour mon moteur. Il servirait à englober toutes les images en un seul fichier. Ce n'est pas un format propriétaire, loin de là l'idée ! Le but est de faciliter le partage et l'utilisation de l'IBL sans avoir 40 fichiers. Je vais faire un essai avec un zip/tar des images et en le "rebadgeant" en .hem.

          Une autre chose à promouvoir pour compléter la solution liée à l'IBL, c'est la génération de table de recherche de BRDF (BRDF look-up table). Mais il existe déjà des solutions qui fonctionnent très bien pour ça sur GitHub, je pourrai éventuellement en faire un fork si je veux ajouter des choses comme un terme dédié à la BRDF de tissus ( https://google.github.io/filament/Filament.html#lighting/imagebasedlights/cloth ).

          EDIT : J'ai ajouté l'export du format personnalisé ".hem". C'est une archive .tar qui contient les mêmes données compressées au format ".hdr". La compression des images se fait en mémoire et donc limite les accès au disque. Au final avec ce format, il n'y a qu'une lecture de fichier (celui en entrée) et une écriture de l'archive complète.

          Voici une vidéo de présentation : https://www.youtube.com/watch?v=zoz6vdJ_wik

          :)

          -
          Edité par AntiXeon 30 mai 2020 à 1:10:26

          • Partager sur Facebook
          • Partager sur Twitter
            3 juin 2020 à 11:56:01

            Hello,

            J'ai ajouté de quoi calculer la BRDF look-up table au programme (optionnel). Le calcul étant un peu long, j'ai recouru au multithreading et ça donne un bon coup de fouet. Le programme utilise autant de threads que disponibles sur le CPU, j'ai pas mis d'option pour ça...

            L'image a une taille en puissance de 2 uniquement. Je la découpe en autant de carrés de 32x32 pixels que nécessaire et j'alloue autant de threads que possible. Sur un Intel i7 4 cœurs HT (donc 2 threads par cœur), si l'image est composée de 4 portions, il y aura 4 threads. S'il y a 256 portions, il y aura 8 threads qui tourneront en boucle jusqu'à compléter l'image entièrement.
            Les threads piochent dans une stack de portions jusqu'à épuisement de son contenu.

            L'image peut avoir une taille minimale de 32x32 pixels (ce qui fera tourner un seul thread). Il n'y a pas de taille maximale même si elle fait généralement 512 voire 1024 pixels de côtés.

            -
            Edité par AntiXeon 3 juin 2020 à 11:58:43

            • Partager sur Facebook
            • Partager sur Twitter
              12 juin 2020 à 21:02:24

              Bonsoir !

              J'ai de premiers résultats pour du rendu PBR avec l'ensemble des données générées avec l'outil dont j'ai parlé au-dessus. Voici quelques rendus à chaud, bien que des détails soient encore à finaliser.

              D'autres images ici (pour ne pas trop polluer/alourdir la page) :

              https://dcarlus.github.io/hARMful/assets/images/hARMful_big.jpg
              https://dcarlus.github.io/hARMful/assets/images/renders/pbr/walther.jpg
              https://dcarlus.github.io/hARMful/assets/images/renders/pbr/mustang.jpg

              Pour la dernière, le rendu n'est pas terrible. C'est normal car Assimp ne supporte pas le rendu PBR autrement qu'avec le format glTF. Or, je n'aime pas ce format car il est vraiment trop lourd en plus d'être du texte donc très long à charger de base. Quand ça arrive, le moteur utilise ce qu'il peut de Assimp (= couleur de diffus) et le reste est mis en valeurs par défaut. Un peu comme le fait Unity, mais Unity fait ça mieux évidemment !
              Donc le chrome du parechoc apparaît peu flatteur, la peinture est horrible... Je voulais malgré tout faire une comparaison avec mon "premier" rendu (https://www.youtube.com/watch?v=WdcBg3hA-xQ).

              Pour les autres objets, c'est pas beaucoup mieux. C'est un système où il faut respecter une certaine structure de dossier pour placer les différentes textures (albedo, normal, ...) puisque Assimp ne sait pas les trouver. Il me faudra faire un éditeur pour qu'il soit possible de créer des matériaux compatibles avec le moteur.

              Il y a des bugs mineurs à corriger encore, comme les reflets qui tremblent un peu en bougeant la caméra.
              Quand le rendu sera débogué, je pense que je continuerai sur des choses un peu plus légères pour faire une "pause". J'aimerais ajouter des effets de post-prod comme du bloom. Ca donne une image bien plus jolie.
              Parmi les autres choses que je voudrais faire en terme de shading : parallax mapping, subsurface scattering, clear coat et anisotropie. :D

              A bientôt !

              -
              Edité par AntiXeon 12 juin 2020 à 21:08:35

              • Partager sur Facebook
              • Partager sur Twitter
                15 juin 2020 à 15:37:12

                C'est du lourd tous ça ;)

                le projet à un discord ?

                • Partager sur Facebook
                • Partager sur Twitter
                  15 juin 2020 à 18:04:59

                  Hello ! Merci ! :)

                  Non, j'ai juste une chaîne Youtube pour faire des vidéos quand une nouvelle feature fonctionne à peu près. Jusqu'à présent, l'aspect communauté n'est pas vraiment développé.

                  Je trouve que le moteur n'a pas encore beaucoup d'intérêt pour un Discord et tout. En fait, c'est un projet à la base plutôt pour apprendre à faire des trucs en OpenGL/GLSL sur le temps libre, donc orienté plutôt shaders/rendu. Alors je sais pas si ça peut vraiment motiver des gens au point de mettre un Discord. :euh:

                  Je te cache pas que j'aimerais beaucoup avoir déjà un truc où on peut interagir plus (contrôles, moteur physique...). Là, ça serait déjà plus plaisant pour qu'une communauté se forme et commence à tester avec des mini jeux.

                  C'est d'ailleurs pour ça qu'il a encore de grosses lacunes. Il est très mal optimisé car j'ai fait aucun effort là-dessus. Donc une petite scène va vite ramer et un jeu ne pourrait même pas tourner correctement...
                  J'ai des idées pour améliorer les perfs mais le rendu est prioritaire encore maintenant. Quand les grosses bases seront posées, là je pense que j'irai sur un gros refactoring du système (au sens Entité-Composant-Système) afin d'optimiser le moteur. Ensuite, j'ajouterai des contrôles et le moteur physique Bullet ! :D

                  Oui, je sais vendre mes projets. Faudrait que je me convertisse dans le marketing ! :p

                  -
                  Edité par AntiXeon 15 juin 2020 à 18:09:49

                  • Partager sur Facebook
                  • Partager sur Twitter
                    24 juin 2020 à 11:07:22

                    Salut,

                    Je reviens avec une nouvelle vidéo pour présenter le rendu PBR du moteur.

                    https://www.youtube.com/watch?v=TOFYPtxJ3Vw

                    C'est la première démo pour la V2.0 du moteur. Il n'y a absolument rien de définitif, la V2.0 devrait sortir fin automne/début hiver 2020. J'aimerais la sortir en décembre 2020 au plus tard.

                    C'est donc une première version avec des shaders de base pour ce rendu. Je compte en ajouter d'autres pour différentes surfaces : "subsurface scattering" pour la peau par exemple, "clear coat" pour les peintures métallisées, rendu amélioré des tissus, ...

                    Avant ça, je vais revoir en profondeur le fonctionnement interne du moteur. Je pensais m'attaquer à cette partie dans plusieurs mois lors d'une grosse phase d'optimisation mais finalement je bouscule un peu ces plans. Il me semble plus intéressant de faire les modifications maintenant pour pérenniser la structure du moteur. De plus, en optimisant plus tôt, je pourrai proposer des interactions utilisateur aussi plus tôt (= faire quelque chose de "jouable" avec le moteur).

                    Au programme, principalement du multithreading et le static batching (pour limiter le nombre de drawcalls). Avant de lancer la boucle principale de rendu, le programme va optimiser l'arbre de scène et précalculer/regrouper les données pouvant l'être. C'est grosso modo la ligne que je me donne à cet instant où je n'ai que des objets physiquement statiques.

                    :)

                    -
                    Edité par AntiXeon 24 juin 2020 à 11:12:18

                    • Partager sur Facebook
                    • Partager sur Twitter
                      27 juin 2020 à 23:34:17

                      Bon boulot ! J'adore le logo
                      • Partager sur Facebook
                      • Partager sur Twitter
                        28 juin 2020 à 18:20:23

                        Coucou Crevette,

                        Merci bien ! En éternel insatisfait, je vois plein de défauts à corriger au plus vite. ^^

                        A commencer par l'architecture. Le principe de ce projet est de partir d'une solution naïve et d'explorer ensuite des solutions plus complexes quand ça se révèle nécessaire. Pour l'architecture du moteur de rendu, j'ai franchi ce cap. J'étais basé sur une approche 100% arbre (de scène) avec des noeuds portant des composants. Ca fait le taf mais c'est très très loin d'être optimal. Je précise que c'est pas une découverte et que c'est parfaitement assumé. C'est ma façon de travailler pour mieux comprendre les enjeux de ce qui est communément établi. J'ai ce besoin d'expérimenter de mon côté pour me rendre compte et comprendre pourquoi c'est de la m****, pourquoi les gens font comme ça et pas autrement. :D

                        Déjà, les performances sont nulles. C'était quelque chose de connu dès le départ (j'avais prévu des phases d'optimisation avant même la sortie de la V1.0), ce n'est pas une surprise qui tombe du ciel tout à coup. Cependant, j'ai un peu changé mon fusil d'épaule et j'ai commencé à revoir mon planning il y quelques semaines pour mettre en place le static batching plus tôt que prévu. Puis, j'ai pensé que multithreader le prétraitement sur CPU serait encore un gros plus, en particulier avec la possibilité de développer un "thread pool" (j'en ai codé un dans l'outil incredIBL et sa place est plutôt dans la bibliothèque). Le premier but de ce projet était pour moi d'arriver à du rendu un peu plus joli que du simple Phong. Or là, ça commence à arriver même s'il faut encore énormément de boulot.
                        Le fait d'optimiser les performances côté CPU me laissera le champ libre pour commencer à rendre le moteur exploitable sur des scènes plus grosses, l'interaction, la physique et les animations. Ce n'est pas un détail et je ne suis pas sûr de continuer uniquement sur du rendu pour les prochaines versions. En clair, j'aimerais distiller ici et là le moteur physique et les interactions avec l'utilisateur au lieu d'y consacrer des versions spécifiques et tardives.

                        Bon, quand on commence à regrouper tout ça, on se dit autant mettre les deux pieds dans le plat !
                        Donc j'ai commencé à designer une architecture Entité-Composant-Système. Je me suis un peu documenté sur le sujet et je m'en suis inspiré pour faire quelque chose qui me convienne aussi. J'ai introduit en plus l'idée d'une machine à états pour les différents systèmes, certains devant s'exécuter successivement avant d'autres (inputs, réception réseau et IA, suivi de la physique), ou certains pouvant s'exécuter en parallèle après ces changements dans la scène (rendu, son, envoi réseau). Avant de mettre en place cette architecture, je voudrais la tester car c'est la première fois que je fais cet exercice donc il y a pas mal d'inconnues.
                        Il se trouve que j'ai commencé à apprendre le langage Rust. Je pensais me lancer dans un petit jeu 2D (avec SDL2) après la sortie de la V2.0 du moteur, histoire de faire un petit break et de produire quelque chose de plus concret. Je me dis que c'est peut-être l'occasion de justement apprendre Rust en implémentant une telle architecture dans un petit projet similaire. Le but n'est pas de faire le jeu complet immédiatement mais au moins tester quelques bribes de l'architecture pour en voir les plus gros défauts et les corriger. Une fois cette étape faite, je pourrai intégrer l'architecture dans le moteur sans avoir à tout modifier 40 fois.

                        En fait, le passage de V1.x à V2.x s'accompagne de modifications assez radicales dans l'utilisation du moteur. J'aimerais conserver autant que possible ce qui aura été fait dans dans la V2.0, sans devoir refaire de profonds changements invalidant totalement les projets. D'où ce besoin d'assurer la bonne architecture et la remettre le moins possible en cause.

                        Donc pour résumer ce pavé : le projet sera indirectement "en pause" pendant quelques temps pour préparer la nouvelle architecture. :p

                        • Partager sur Facebook
                        • Partager sur Twitter
                          28 juin 2020 à 22:15:05

                          Tu n'avait

                          pas d'ECS ?
                          Comment fesait tu ton rendu ? Un SceneGraph (arbre) de tes objet ?
                          Avec l'ECS tout ca deviens beaucoup plus rapide : tu manipule une liste d'identifiant ( int ) plutot qu'une liste d'objet.

                          Dans mon cas mon ECS ressemble a ca :

                          using Entity = std::size_t;

                          using Ctype = std::string;
                          
                          class base_component {
                          private:
                              Ctype type;
                              Entity owner;
                          }


                          et le store :

                          using Box = std::map<Entity, std::unique_ptr<BComponent>>;
                          
                          		class CBox {
                          		public:
                          			void Add(std::unique_ptr<BComponent> comp)
                          			{
                          				auto owner = comp->Owner();
                          				Components_.emplace(owner, std::move(comp));
                          			}
                          
                          			BComponent* Get(Entity owner)
                          			{
                          				auto found = Components_.find(owner);
                          
                          				if (found != Components_.end())
                          				{
                          					return found->second.get();
                          				}
                          
                          				return nullptr;
                          			}
                          
                          			void Remove(Entity owner)
                          			{
                          				Components_.erase(owner);
                          			}
                          		private:
                          			Box Components_;
                          		};
                          
                          		using Boxes = std::map<CType, CBox>;
                          
                          		class Store {
                          		public:
                          			Store() {};
                          
                          			template<class T>
                          			T* Add(std::unique_ptr<BComponent> comp)
                          			{
                          				auto t = comp->Type();
                          				auto box = Boxes_.find(t);
                          
                          				// add the box, it does not exist yet
                          				if (box == Boxes_.end())
                          				{
                          					CBox new_box{};
                          					Boxes_.emplace(t, std::move(new_box));
                          					box = Boxes_.find(t);
                          				}
                          
                          				auto owner = comp->Owner();
                          				box->second.Add(std::move(comp));
                          
                          				return static_cast<T*>(box->second.Get(owner));
                          			}
                          
                          		private:
                          			Boxes Boxes_;
                          		};

                          Mon 'Store' a donc cette forme : std::map< Component_type , std::map< Owner , Component>>.
                          En gros : 1 boite par type de composant. 1 composant par entité.

                          Quasi le systeme entier a access au store. Ainsi il suffit de :

                          auto const Position = store.Get<Position*>(owner);
                          if(Position != nullptr)
                             Position.x = ..
                             Position.y = ..
                             Position.z = .. 

                          Est-ce le meilleur design ? Aucune idée , mais c'etait le plus simple pour moi pour conserver et recuperer un composant.

                          A partir de la ton scene graph peut etre composé que d'entités.

                          • Partager sur Facebook
                          • Partager sur Twitter
                            29 juin 2020 à 0:47:12

                            J'avais un ECS mais il n'a pas la meilleure architecture. J'ai du mal à l'appeler ECS mais ça y correspond. Tu peux te faire une idée de l'architecture avec Qt3D dont je m'étais inspiré partiellement. Je pense encore garder une part de cette façon de faire, en particulier le framegraph que je trouve puissant, bien que cette partie sera légèrement revue.
                            Le Frostbite Engine utilise aussi un framegraph par exemple, très probablement différemment de ma gestion, mais il en utilise un.
                            https://www.ea.com/frostbite/news/framegraph-extensible-rendering-architecture-in-frostbite

                            Pour l'instant, je ne montre pas le résultat de l'architecture remodelée, je le ferai quand elle sera définitive. Là je pourrai vraiment parler d'ECS. Je vais bien utiliser des ID pour les entités et les composants, ainsi que des systèmes et des événements pour les communications internes au moteur.

                            Jusqu'à présent je me passais d'ID car je gérais ça à l'aide de pointeurs comme je n'avais pas de gestion d'événements. Or, ce manque d'événements était aussi une grosse limitation de l'architecture actuelle.

                            -
                            Edité par AntiXeon 29 juin 2020 à 0:49:50

                            • Partager sur Facebook
                            • Partager sur Twitter

                            [Moteur 3D] hARMful

                            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                            • Editeur
                            • Markdown