Accueil / Architecture
Architecture
Le code s’organise autour d’un Core central qui orchestre le chargement de scène, des plugins, le rendu, l’interface optionnelle et le mode cluster optionnel. L’architecture privilégie les interfaces (testabilité, extensibilité) et sépare nettement l’analyse, la possession des données et la logique de rendu.
Vue en couches
Chaque responsabilité vit dans un module dédié. Le tableau ci-dessous relie chaque module à son emplacement dans les sources et à son rôle.
| Module | Emplacement | Rôle |
|---|---|---|
| Point d’entrée | main.cpp, srcs/core/Core.* | Orchestration du cycle de vie complet. |
| Chargement / sauvegarde de scène | srcs/core/scene/SceneParser.*, builder/*, factory/*, SceneRegister.* | Lecture du JSON, construction typée, sérialisation inverse. |
| Données de scène | srcs/core/scene/Scene.*, bvh/* | Caméra, primitives, lumières, matériaux, structure d’accélération BVH. |
| Plugins | srcs/core/PluginLoader.*, srcs/common/IPlugin.hpp, IPluginLoader.hpp | Chargement dynamique des .so. |
| Renderers / UI | srcs/plugins/renderer/*, srcs/plugins/user_interface/* | Plugins de rendu et interface graphique. |
| Géométrie / lumières | srcs/plugins/primitive/*, srcs/plugins/light/* | Implémentations concrètes des primitives et des lumières. |
| Cluster (optionnel) | srcs/core/cluster/*, srcs/common/cluster/* | Rendu distribué client/serveur. |
| Outils & utilitaires | srcs/core/obj/*, srcs/core/utils/*, srcs/core/exceptions/*, srcs/common/* | Import OBJ, export PNG, surveillance de fichier, exceptions, maths. |
Diagramme des dépendances
Vue d’ensemble des dépendances principales à l’exécution :
main.cpp
│
▼
Core ───────────────► PluginLoader ───► plugins/*.so ──┬─► Renderer plugins (default, viewport)
│ └─► UI plugin (user_interface)
│
├─► SceneParser ──┬─► parseCamera
│ │ ├─► parseMaterials
│ │ └─► parseObjects ──► SceneObjectBuilder ──┬─► PrimitiveBuilder
│ │ └─► LightBuilder
│ ├─► ObjParser (import .obj)
│ └─► SceneBuilder ──► Scene ──► BVHNode / buildBvh()
│
├─► ISceneRenderer (actif) ──► Scene
├─► RenderExporter ──► render.png
├─► SceneRegister ──► export .json
├─► FileObserver ──► rechargement à chaud du fichier de scène
└─► ClusterModule (optionnel) ──► serveur / client ──► PacketFactory
Le Core
Le Core (srcs/core/Core.*) implémente ICoreAccess et coordonne
tout le cycle de vie. En six grandes étapes :
- Chargement des plugins —
loadPlugins()parcourt./plugins, puisloadRenderers()etloadUserInterface()récupèrent les instances par type. - Analyse & construction — le fichier
.jsonest analysé et la scène est construite via les builders (loadScene()/loadNewScene()). - Sélection du renderer actif — parmi
_defaultRenderer,_viewportRendereret le renderer de cluster interne. - Construction du BVH & rendu —
scene.buildBvh()puisrenderFrame()/startRendering(). - Export de l’image — via
RenderExportervers_renderOutputPath(par défautrender.png). - Boucle d’interface (optionnelle) — la boucle d’événements de l’UI tourne et le
FileObserverdéclenche le rechargement à chaud du fichier de scène.
Historique annuler / rétablir par instantané JSON. Le Core conserve un
vecteur _history d’instantanés (limité à HISTORY_LIMIT = 100) avec un
_historyIndex. Chaque mutation appelle historyCapture(), qui sérialise
l’état courant via snapshotScene() ; historyUndo() / historyRedo()
reconstruisent la scène à partir de l’instantané voulu avec restoreSnapshot(). Une
signature (historySignature()) évite d’enregistrer deux instantanés identiques
consécutifs.
Pendant un undo/redo, le drapeau _suppressWatcherReload
neutralise temporairement le FileObserver pour ne pas confondre une écriture interne avec
une modification externe du fichier.
Système de plugins
Les modules de rendu et d’interface sont des
bibliothèques partagées .so chargées à l’exécution depuis ./plugins.
Au démarrage, PluginLoader::load() appelle dlopen(path, RTLD_NOW) puis résout,
avec dlsym, deux symboles obligatoires (contrat C ABI) :
extern "C" IPlugin *create_plugin();
extern "C" void destroy_plugin(IPlugin *plugin);
Si l’un des deux manque, le dlopen est refermé (dlclose) et le plugin rejeté.
Sinon l’instance créée est stockée dans un PluginHandle { handle, instance, destroy } et
interrogeable par PluginType — actuellement RENDERER ou
USER_INTERFACE. À l’arrêt, unloadAll() détruit chaque instance puis
dlclose son .so (important pour éviter les fuites signalées par AddressSanitizer).
| Plugin | Type | Rôle |
|---|---|---|
renderer_default.so | RENDERER | Renderer « Default » — rendu complet de l’image. |
renderer_viewport.so | RENDERER | Renderer « Viewport » — rendu progressif pour l’interface. |
user_interface.so | USER_INTERFACE | Interface graphique SFML (viewport, panneaux, gizmos). |
Les primitives et lumières vivent dans srcs/plugins/… mais sont
actuellement compilées dans l’exécutable principal et instanciées via les
builders/factories — elles ne sont pas (encore) chargées comme .so dynamiques.
L’architecture reste néanmoins prête pour cette évolution (l’énumération PluginType
réserve d’ailleurs des entrées PRIMITIVE/LIGHT en commentaire).
Construction de la scène
La construction est volontairement séparée de l’analyse, afin de garder le parsing indépendant des détails de construction concrète :
scene.json
│
▼
SceneParser ──┬─► parseCamera
│ ├─► parseMaterials
│ └─► parseObjects ──► SceneObjectBuilder ──┬─► PrimitiveBuilder
│ └─► LightBuilder
▼
SceneBuilder ──► Scene (caméra + matériaux + objets, dispatch par ObjectType)
SceneObjectBuilder— répartiteur :withType(string)active le mode primitive,withType(LightType)le mode lumière ; les setters communs (nom, position…) sont transmis au builder interne actif, etbuild()renvoie unISceneObject*(IPrimitive*ouILight*).PrimitiveBuilder— construit les primitives concrètes (sphere,plane,cube,cylinder,cone,triangle,torus,tanglecube,fractal) à partir des champs analysés (position,rotation,scale,radius,height,size,vertex0..2,power,iterations,material…).LightBuilder— construitPointLightetDirectionalLight(nom, position/direction, intensité, couleur).SceneBuilder— agrège caméra, matériaux et objets : créeScene(camera, ambient, diffuse), enregistre les matériaux par nom, puis répartit selonObjectType(PRIMITIVE → addPrimitive,LIGHT → addLight).PrimitiveFactory/LightFactory— constructeurs par valeurs par défaut (createPrimitive(type),createLight(type)), utiles pour la création d’objets depuis l’éditeur, sans passer par le parsing.
SceneRegister effectue le chemin inverse (save path) : il
re-sérialise l’état de la scène en JSON (caméra, coefficients d’environnement, objets, matériaux),
permettant l’édition en aller-retour dans l’interface. Le cas particulier type = "object"
(maillage) est importé via ObjParser.
Pipeline de rendu
Une image est produite en appliquant la même séquence à chaque pixel :
- Rayon caméra — génération d’un rayon primaire
P(t) = O + t·Dpour le pixel(i, j), avec bornest ∈ [t_min, t_max]. - Intersection accélérée — test contre la géométrie, accéléré par le BVH construit
avant le rendu via
scene.buildBvh()(BVH global + BVH local par maillage). - Hit le plus proche — conservation du plus petit
tvalide (t, point d’impact, normale, matériau). - Ombrage — au point d’impact
H = O + t·D: calcul et orientation de la normaleN, lecture des paramètres du matériau. - Lumières & rayons d’ombre — pour chaque lumière, un rayon d’ombre décalé
(
H + ε·N) teste la visibilité ; si occlus, la contribution est ignorée, sinon on évalue diffus et spéculaire. - Accumulation — ambiant + contributions directes visibles, écrêtage dans la plage de sortie.
- Export PNG — une fois tous les pixels calculés, le tampon image est écrit sur disque (voir Outils et utilitaires).
Robustesse numérique : décalage ε pour les rayons
secondaires/d’ombre (évite l’acné d’ombre par auto-intersection), rejet des dénominateurs proches de zéro
dans les résolutions linéaires (plans/triangles), test cohérent t ∈ [t_min, t_max], et
normalisation des vecteurs utilisés dans les produits scalaires de l’ombrage.
Outils et utilitaires
Autour du pipeline, plusieurs utilitaires ciblés (dossiers srcs/core/obj/,
srcs/core/utils/ et srcs/core/exceptions/) :
ObjParser — import .obj
ObjParser (srcs/core/obj/ObjParser.hpp) lit un fichier Wavefront .obj
et alimente la liste d’objets de la scène. Il analyse les sommets (v), les normales
(vn) et les faces (f) pour produire des ObjTriangle (avec support
des normales lissées smooth), applique une transformation (withPosition,
withRotation, withSize), et expose les triangles bruts, les sommets et les
indices de faces. Le mode setEmitObjects(false) permet de récupérer la géométrie sans émettre
d’objets de scène. Toute erreur d’analyse lève ObjParserException.
RenderExporter — export PNG
RenderExporter::saveToFile(render, path) (srcs/core/utils/RenderExporter.hpp)
encode un Render (largeur, hauteur, pixels) en PNG écrit à la main :
génération de la signature PNG et des chunks IHDR/IDAT/IEND,
compression zlib « stored » (blocs non compressés), et calcul des sommes de contrôle
CRC32 et Adler32. L’image est écrite en truecolor RGB 8 bits, sans dépendance à
une bibliothèque d’image externe. Un échec d’ouverture du fichier lève
ExportRenderException.
FileObserver — rechargement à chaud
FileObserver (srcs/core/utils/FileObserver.hpp) surveille le fichier de scène en
mémorisant sa date de dernière modification (std::filesystem::file_time_type).
pollChanges() renvoie true lorsque le fichier a changé depuis le dernier appel ;
le Core l’interroge dans sa boucle pour recharger la scène automatiquement.
setFilePath() et reset() permettent de (re)cibler le fichier suivi.
Exceptions
Les erreurs métier sont modélisées par des exceptions dédiées (srcs/core/exceptions/),
toutes dérivées de std::exception :
| Exception | Levée pour |
|---|---|
LoadingSceneException | Chargement de scène — avec un ExceptionType (UNKNOWN_FILE, CANNOT_OPEN_FILE, INVALID_FILE_EXTENSION, WRONG_FILE_CONTENT, OTHER). |
BuildingObjectException | Échec de construction d’un objet de scène par les builders. |
ExportRenderException | Échec de l’export de l’image (ouverture/écriture du PNG). |
ObjParserException | Erreur d’analyse d’un fichier .obj (définie avec ObjParser). |
ColorException | Valeur de couleur invalide. |
VectorException | Opération vectorielle invalide (maths). |
Module cluster
Le ClusterModule (srcs/core/cluster/*, interface
srcs/common/cluster/*) fournit une infrastructure de rendu distribué :
un serveur découpe l’image en tuiles et les distribue à des clients connectés qui renvoient leurs pixels.
Il expose startServer(scene, port), joinCluster(address, port) et
leaveCluster() ; le rendu est coordonné par ClusterRenderCoordinator et
ClusterRenderer.
La communication passe par des paquets typés construits par PacketFactory
(identifiés par PacketID), par exemple :
| Paquet | Rôle |
|---|---|
ClientJoinRequest | Un client demande à rejoindre le cluster. |
ServerSceneData | Le serveur envoie la scène aux clients. |
ServerRenderRequest | Le serveur demande le rendu d’une tuile. |
ClientTileData | Un client renvoie les pixels d’une tuile. |
ServerRenderState | Le serveur diffuse l’état d’avancement du rendu. |
--server [PORT]lance l’interface et démarre le serveur (startServer).--connect IP PORTlance l’interface et rejoint un serveur (joinCluster).