Simulation N-Corps — Référence Physique¶
Vue d'ensemble¶
Le module N-corps (sous-dossier src/nbody/) implémente une simulation
gravitationnelle temps réel de jusqu'à 16 corps en orbite autour d'une étoile
centrale. Le moteur physique privilégie la stabilité long terme
(conservation de l'énergie sur des milliers de secondes simulées) en utilisant
des techniques éprouvées d'astrophysique numérique :
| Composant | Technique |
|---|---|
| Intégrateur | Velocity Verlet (symplectique, ordre 2) |
| Régularisation | Adoucissement de Plummer (par paire, adaptatif) |
| Conditions initiales | Formule de vitesse orbitale adoucie |
| Pas de temps | Fixe \(\frac{1}{120}\text{s}\), accumulateur borné |
Validé par un test automatisé de 1 200 s avec 0.002 % de dérive d'énergie.
Intégrateur Velocity Verlet¶
Velocity Verlet (aussi appelé Leapfrog kick-drift-kick) est un intégrateur symplectique : il préserve la structure géométrique des systèmes hamiltoniens, ce qui garantit que l'énergie totale oscille autour de la vraie valeur au lieu de dériver monotoniquement.
Algorithme (par pas fixe \(\Delta t\))¶
- Calculer les accélérations à partir des positions courantes : \(\mathbf{a}_\text{old}\)
- Mettre à jour les positions : $\(\mathbf{x} \leftarrow \mathbf{x} + \mathbf{v}\,\Delta t + \tfrac{1}{2}\,\mathbf{a}_\text{old}\,\Delta t^2\)$
- Calculer les accélérations à partir des nouvelles positions : \(\mathbf{a}_\text{new}\)
- Mettre à jour les vitesses : $\(\mathbf{v} \leftarrow \mathbf{v} + \tfrac{1}{2}(\mathbf{a}_\text{old} + \mathbf{a}_\text{new})\,\Delta t\)$
Implémenté dans integrate_step() dans src/nbody/nbody_physics.c.
Pourquoi le Symplectique est Important¶
Les méthodes non-symplectiques (Euler, RK4) accumulent une dérive séculaire d'énergie proportionnelle au nombre de pas. Sur 1 200 s à 120 Hz, cela représente 144 000 pas — suffisant pour que même de petites erreurs par pas fassent exploser ou effondrer le système. L'erreur bornée d'un intégrateur symplectique maintient les orbites qualitativement correctes indéfiniment.
Références :
- Intégrateur symplectique — Wikipédia
- Intégration leapfrog — Wikipedia
- Hairer, Lubich & Wanner, Geometric Numerical Integration (Springer, 2006)
Adoucissement de Plummer¶
Dans un champ gravitationnel pur en \(1/r^2\), deux masses ponctuelles passant très près subissent des forces arbitrairement grandes, menant à des effets de fronde numérique et des échanges chaotiques d'énergie. Le remède standard en astrophysique est l'adoucissement de Plummer :
La longueur d'adoucissement \(\varepsilon\) régularise la singularité en \(r = 0\), transformant le potentiel newtonien \(1/r\) en le potentiel fini de Plummer \(\Phi = -G\,M / \sqrt{r^2 + \varepsilon^2}\).
Adoucissement Adaptatif par Paire¶
Plutôt qu'un seul \(\varepsilon\) global, on adapte l'adoucissement aux rayons physiques de chaque paire :
Avec les constantes par défaut :
| Constante | Valeur | Rôle |
|---|---|---|
NBODY_SOFTENING_SQ |
0.25 | Plancher absolu (\(\varepsilon_\min = 0.5\)) |
NBODY_SOFTENING_FACTOR |
2.0 | Multiplicateur par paire sur les rayons combinés |
Ceci garantit que :
- Les gros corps (l'étoile, \(r = 1.5\)) bénéficient d'un fort adoucissement (\(\varepsilon \geq 3.6\)).
- Les petites paires (deux lunes) en reçoivent proportionnellement moins, gardant leur dynamique plus képlérienne.
- Aucune paire n'atteint jamais la région singulière.
Implémenté dans pair_softening_sq().
Références :
- Adoucissement gravitationnel — Wikipedia
- Dehnen & Aly, Improving convergence of N-body methods (MNRAS 425, 2012)
- Aarseth, Gravitational N-Body Simulations (Cambridge, 2003)
Vitesse Orbitale Adoucie¶
Une erreur courante est d'initialiser les vitesses d'orbite circulaire avec la formule de Kepler \(v = \sqrt{G\,M/r}\). C'est faux quand l'adoucissement est actif car le potentiel effectif est plus plat que \(1/r\).
La condition correcte d'orbite circulaire pour le potentiel de Plummer est :
Elle vient de l'équilibre entre l'accélération centripète \(v^2/r\) et l'accélération gravitationnelle adoucie \(G\,M\,r / (r^2 + \varepsilon^2)^{3/2}\).
Utiliser la formule non-adoucie surestime la vitesse orbitale, injectant un excès d'énergie cinétique et causant la spirale vers l'extérieur des corps.
Implémenté dans softened_orbital_vel().
Annulation de la Quantité de Mouvement¶
Après l'initialisation de tous les corps, la quantité de mouvement totale \(\mathbf{p} = \sum m_i \mathbf{v}_i\) est mise à zéro en soustrayant la vitesse du centre de masse de chaque corps :
Cela maintient le centre de masse immobile et empêche le système entier de dériver à travers la scène. L'opération est faite une seule fois à l'initialisation, jamais pendant la simulation (ce qui briserait la symplecticité).
Pas de Temps Fixe avec Accumulateur¶
Les pas de temps variables détruisent la propriété symplectique. La simulation utilise un \(\Delta t = 1/120\,\text{s}\) fixe avec un motif d'accumulateur standard :
accumulateur += wall_dt × time_scale
borner accumulateur à NBODY_MAX_ACCUMULATOR (1/10 s)
tant que accumulateur >= NBODY_FIXED_DT :
integrate_step(NBODY_FIXED_DT)
accumulateur -= NBODY_FIXED_DT
Le bornage maximum de l'accumulateur empêche un emballement de l'intégration après des pics de latence ou lors de la première frame. Le plafond de \(1/10\,\text{s}\) permet de supporter des framerates aussi bas que ~10 FPS sans perdre de temps simulé (jusqu'à 12 pas Verlet par frame pour \(N = 14\) corps — trivial en CPU).
Approches Testées et Rejetées¶
Au cours du développement, plusieurs techniques alternatives ont été testées et rejetées car elles brisaient l'invariant symplectique ou introduisaient une dissipation d'énergie inacceptable.
1. Collisions Élastiques¶
Idée : Quand deux corps se chevauchent (\(r < r_i + r_j\)), refléter leurs vitesses avec les formules de collision élastique.
Problème : La détection discrète de collision à pas de temps fixe introduit des impulsions non-réversibles dans le temps. Cela brise la symplecticité et cause une dérive séculaire d'énergie. En pratique, les corps gagnaient de l'énergie à chaque collision, menant à une explosion exponentielle.
Verdict : Supprimé entièrement. L'adoucissement de Plummer empêche le chevauchement sans nécessiter de gestion des collisions.
2. Clamp d'Énergie¶
Idée : Après chaque pas, mesurer l'énergie totale ; si elle dépasse un seuil, redimensionner toutes les vitesses pour restaurer l'énergie cible.
Problème : C'est une étape de projection qui ne dérive pas d'un hamiltonien. Elle détruit la structure symplectique et introduit un amortissement ou un pompage artificiel selon le signe de la correction.
Verdict : Supprimé.
3. Frein d'Échappement¶
Idée : Si un corps dépasse un seuil de distance, appliquer une force de traînée pour le ramener.
Problème : Force non-conservative → perte séculaire d'énergie → les orbites décroissent et le système s'effondre.
Verdict : Supprimé.
4. Ré-annulation de la Quantité de Mouvement par Pas¶
Idée : Soustraire la vitesse du centre de masse de tous les corps à chaque pas.
Problème : C'est une projection non-symplectique appliquée à chaque pas. Elle interfère avec la structure de l'espace des phases de l'intégrateur et cause une dérive descendante de l'énergie.
Verdict : La quantité de mouvement est annulée une seule fois à l'initialisation, jamais pendant la simulation.
5. XPBD (Extended Position-Based Dynamics)¶
Idée : Utiliser le solveur de contraintes XPBD (de la physique de jeux vidéo) pour imposer une distance minimale entre les corps, remplaçant l'adoucissement par une répulsion basée sur des contraintes.
Problème : XPBD est fondamentalement dissipatif par conception — il résout les contraintes en projetant les positions, ce qui ne conserve pas l'énergie. En test, cela a causé 21 % de perte d'énergie sur 300 s, les orbites ont décru rapidement et tous les corps se sont effondrés sur l'étoile.
XPBD est excellent pour la physique de jeux (ragdolls, tissus) où la dissipation est acceptable voire souhaitable, mais il est catastrophique pour les simulations gravitationnelles qui nécessitent la conservation de l'énergie.
Verdict : Rejeté après test. Verlet pur + fort adoucissement de Plummer est l'approche correcte pour la gravité N-corps.
Références :
- Müller et al., Detailed Rigid Body Simulation with Extended Position Based Dynamics (CGF 2020)
- Position Based Dynamics — Wikipedia
Potentiel de Confinement¶
Les corps qui s'éloignent trop de l'étoile centrale sont repoussés par un potentiel de rappel quadratique — un mur souple qui garde la simulation visuellement bornée sans clamper brutalement les positions.
Physique¶
Pour chaque corps \(i\) à distance \(r_i\) de l'étoile :
La force résultante est :
où \(r_\text{max}\) = NBODY_CONFINEMENT_RADIUS (25 unités) et \(k\) =
NBODY_CONFINEMENT_K (5.0).
Réaction de Newton (3ème loi) sur l'étoile centrale pour préserver le centre de masse :
Le potentiel est conservatif, donc l'intégrateur Velocity Verlet reste symplectique dans la zone de confinement.
Constantes¶
| Constante | Valeur | Rôle |
|---|---|---|
NBODY_CONFINEMENT_RADIUS |
25.0 | Distance du mur souple depuis l'étoile |
NBODY_CONFINEMENT_K |
5.0 | Raideur du ressort (\(k \cdot dt^2 \approx 0.0003\)) |
Amortissement Radial Proportionnel¶
Le potentiel de confinement seul provoque des oscillations des corps autour de la frontière (trajectoires ping-pong). Un amortissement radial proportionnel retire de l'énergie uniquement à la composante radiale sortante, et seulement dans la zone de confinement :
où \(\gamma\) = NBODY_CONFINEMENT_DAMPING (20.0).
- Radial uniquement : seule la composante \((\mathbf{v} \cdot \hat{\mathbf{r}})\) est amortie ; la vitesse tangentielle est préservée → moment cinétique conservé.
- Sortant uniquement : l'amortissement s'applique quand \(v_\text{radial} > 0\) (corps s'éloignant de l'étoile).
- Proportionnel au dépassement : près de la frontière (\(r \approx r_\text{max}\)), l'amortissement est négligeable ; loin au-delà, il est fort. Cela permet au système d'atteindre un équilibre où les orbites ne traversent plus la frontière et la dissipation d'énergie se stabilise.
- Conservation de la quantité de mouvement : l'impulsion est transférée à l'étoile centrale (\(\Delta v_\text{étoile} = -(m_i/m_0) \cdot \Delta v_i\)).
Indicateur de Stabilité¶
L'indicateur HUD utilise la dérive d'énergie signée pour distinguer l'amortissement de la divergence :
| Couleur | État | Condition | Affichage |
|---|---|---|---|
| Vert | Stable | $ | \text{dérive} |
| Jaune | Damping | \(\text{dérive}_\text{signée} < 0\) | E: XX% (retenue) |
| Rouge | Divergent | \(\text{dérive}_\text{signée} > 0\) | drift: X.XX% |
En mode Damping, l'affichage montre E: XX% = \(100 \times (1 - |\text{dérive}|)\),
représentant combien d'énergie le système possède encore. Cela évite le
confusant « drift: 349% » qui surviendrait avec la métrique absolue.
Effets Visuels de Shockwave¶
Quand un corps percute la frontière de confinement, un anneau d'onde de choc s'étend depuis le point d'impact, fournissant un retour visuel pour la zone de confinement autrement invisible.
Détection d'Impact¶
Chaque sous-pas d'intégration vérifie si la distance d'un corps à l'étoile
dépasse NBODY_CONFINEMENT_RADIUS avec une vitesse radiale sortante positive.
Les impacts sont enregistrés par corps dans des slots NBodyImpact — seule
la vitesse maximale de tous les sous-pas d'une frame est conservée,
dédupliquant naturellement les hits multiples de l'accumulateur à pas fixe
(~12 sous-pas/frame).
Rendu¶
Les ondes de choc sont rendues comme des quads alignés à l'écran en mélange additif (billboard) avec un motif procédural d'anneau en expansion dans le fragment shader :
- Profil de l'anneau : bords intérieurs/extérieurs
smoothstepautour d'un rayon central dépendant du temps - Émissif HDR : la couleur de sortie est multipliée par 4× pour interagir avec le bloom
- Atténuation : intensité = \((1 - \text{progression}^2)\), montée rapide, extinction lente
- Billboard : le quad fait face à la caméra via les vecteurs right/up extraits de la matrice vue
Jusqu'à SHOCKWAVE_MAX_ACTIVE (8) ondes de choc simultanées ; la plus
ancienne est évincée quand le buffer est plein. Durée = SHOCKWAVE_DURATION
(1.2 s), rayon max = SHOCKWAVE_MAX_RADIUS (6.0 unités monde).
Position dans le Pipeline¶
Les ondes de choc sont dessinées dans le FBO HDR, après les traînées N-corps et avant le post-processing. Le bloom amplifie naturellement le halo de l'anneau :
scene_render() → Skybox → Sphères → Traînées NBody → Shockwave VFX → Sondes
↓
postprocess_end() → Bloom → ...
Validation par Tests¶
La suite de tests automatisée (tests/test_nbody_stability.c) vérifie les
invariants physiques sur de longues simulations :
| Test | Ce qu'il vérifie | Seuil |
|---|---|---|
test_nbody_single_step_sanity |
Un pas n'explose pas | Positions finies |
test_nbody_energy_conservation |
Énergie bornée après boost initial | \(\Delta E / E_0 < 65\%\) |
test_nbody_paused_no_change |
Sim en pause parfaitement gelée | Identique bit à bit |
test_nbody_survives_dt_spikes |
Les pics de frame ne déstabilisent pas | Tous les corps \(< 50\) unités |
test_nbody_long_run_stability |
Invariants sur 1 200 s | Voir ci-dessous |
test_nbody_kinetic_energy_positive |
\(E_k > 0\) après init | Positif |
test_nbody_energy_drift_api |
API drift : \(E_0\) stocké, dérive \(\approx 0\) à \(t=0\) | dérive \(< 5\%\) après 10 s |
test_nbody_zero_gravity |
\(G = 0\) signifie aucune accélération | Vitesse inchangée après 120 pas |
Résultats de Stabilité Long Terme (1 200 s à 120 Hz = 144 000 pas)¶
| Métrique | Mesuré | Seuil |
|---|---|---|
| Dérive d'énergie \(\|\Delta E / E_0\|\) | 0.0017 % | < 5 % |
| Dérive du centre de masse | 0.001 | < 0.5 |
| Distance maximale d'un corps | 19.6 | < 50 |
| Nombre de corps | 7 / 7 | inchangé |
Référence des Constantes¶
Toutes les constantes sont définies dans include/nbody.h :
| Constante | Valeur | Description |
|---|---|---|
NBODY_MAX_BODIES |
16 | Nombre maximum de corps |
NBODY_DEFAULT_G |
1.0 | Constante gravitationnelle |
NBODY_SOFTENING_SQ |
0.25 | \(\varepsilon^2\) minimum |
NBODY_SOFTENING_FACTOR |
2.0 | Multiplicateur d'adoucissement par paire |
NBODY_FIXED_DT |
1/120 s | Pas de temps fixe d'intégration |
NBODY_MAX_ACCUMULATOR |
1/10 s | Temps physique accumulé maximum (supporte ~10 FPS) |
NBODY_CONFINEMENT_RADIUS |
25.0 | Distance du mur souple depuis l'étoile |
NBODY_CONFINEMENT_K |
5.0 | Raideur du ressort de confinement |
NBODY_CONFINEMENT_DAMPING |
20.0 | Coefficient de base d'amortissement radial |
SHOCKWAVE_MAX_ACTIVE |
8 | Nombre max d'ondes de choc simultanées |
SHOCKWAVE_DURATION |
1.2 s | Durée de vie d'un anneau |
SHOCKWAVE_MAX_RADIUS |
6.0 | Rayon max d'expansion (unités monde) |
SHOCKWAVE_MIN_VELOCITY |
0.2 | Vitesse min pour déclencher une onde |
TRAIL_DURATION_DEFAULT |
4.0 s | Durée de vie des traînées en secondes (ajustable 0.5–30 s) |
Contrôles en Temps Réel¶
Vitesse de Simulation (time_scale)¶
La vitesse de simulation est ajustable au clavier :
| Touche (US) | Touche (AZERTY) | Action |
|---|---|---|
. |
: |
Doubler la vitesse (max 64×) |
, |
; |
Diviser par 2 (min ⅛×) |
Le multiplicateur time_scale suit des puissances de 2 pour garantir que
1.0× est toujours atteignable :
Une notification overlay affiche la vitesse actuelle à chaque changement.
Inversion Temporelle¶
La direction du temps peut être inversée à l'exécution :
| Touche (US) | Touche (AZERTY) | Action |
|---|---|---|
Ctrl+Shift+G |
Ctrl+Shift+G |
Bascule la direction du temps (avant/arrière) |
Ceci exploite une propriété fondamentale de la gravité newtonienne : les équations du mouvement sont invariantes sous \(t \to -t\). Inverser le temps revient à négativer toutes les vitesses — le système retrace sa trajectoire en sens inverse.
Comme Velocity Verlet est à la fois symplectique et réversible dans le temps (\(\Phi_h^{-1} = \Phi_{-h}\)), la simulation inversée suit la même orbite avec une erreur d'énergie bornée. Après \(N\) pas en avant et \(N\) pas en arrière, le système revient à l'état initial (modulo l'arrondi flottant : \(\sim N \cdot \epsilon_{\text{machine}}\) d'erreur de position).
En marche arrière, le HUD affiche un indicateur ⏪ à côté de la lecture
d'énergie. Les contrôles de vitesse (, / .) ajustent la magnitude
sans affecter la direction temporelle.
Transition Progressive¶
L'inversion n'est pas instantanée : time_scale converge linéairement
vers sa cible au rythme de 3.0 unités/s (NBODY_TIME_SCALE_RATE). À
vitesse \(1\times\), la transition complète avant→arrière prend ~0.7 s :
Cela produit un effet naturel « rembobinage VHS » où les corps ralentissent, se figent brièvement, puis accélèrent en sens inverse. Des multiplicateurs plus élevés prennent proportionnellement plus de temps (ex. \(4\times\) → ~2.7 s).
Contrôle de la Gravité (gravity)¶
La constante gravitationnelle \(G\) est ajustable en temps réel avec le modificateur Shift :
| Touche (US) | Touche (AZERTY) | Action |
|---|---|---|
Shift+. |
Shift+: |
Doubler G (max 128) |
Shift+, |
Shift+; |
Diviser G par 2 (min 0.125, puis OFF) |
\(G\) suit des puissances de 2 à partir de la valeur courante :
- \(G = 0\) désactive la gravité : les corps suivent des trajectoires balistiques en ligne droite à leur vitesse courante.
- Quand \(G\) change, l'énergie de référence \(E_0\) est recalculée pour que l'indicateur de stabilité ne reflète que l'erreur d'intégration numérique, pas le changement intentionnel de paramètre physique.
Affichage HUD¶
Quand le mode N-corps est actif et l'overlay texte est activé (F1), deux
lignes sont affichées :
- Énergie & Gravité :
Ek: 77.1 J | G: 2.000— énergie cinétique \(E_k = \sum \tfrac{1}{2} m_i |\mathbf{v}_i|^2\) et valeur courante de \(G\). - Stabilité : indicateur coloré avec métrique contextuelle :
| Couleur | État | Métrique | Exemple |
|---|---|---|---|
| Vert | Stable | dérive% | Stability: Stable (drift: 0.12%) |
| Jaune | Damping | énergie retenue | Stability: Damping (E: 78%) |
| Rouge | Divergent | dérive% | Stability: Divergent (drift: 6.42%) |
La dérive est \(|E(t) - E_0| / |E_0|\). En mode Damping, l'énergie retenue est \(100 \times (1 - \text{dérive})\), montrant combien d'énergie le système possède encore (évite les valeurs de dérive >100% confusantes).
Stabilité de la Longueur des Traînées — Échantillonnage Temporel¶
La longueur des traînées est contrôlée par un paramètre de durée
(trail_duration, défaut 4.0 s, ajustable 0.5–30 s) plutôt que par un
nombre fixe de points. Chaque échantillon enregistré porte un timestamp
d'une horloge monotone de simulation :
sim_time += wall_dt × |time_scale|
sample_timer += wall_dt × |time_scale|
tant que sample_timer >= TRAIL_SAMPLE_INTERVAL :
pour chaque corps :
ring_push(position, sim_time) ← horodaté
sample_timer -= TRAIL_SAMPLE_INTERVAL
Le constructeur de ruban calcule ensuite l'âge par point :
Les points avec \(\text{age} > 1\) sont ignorés. La largeur et l'intensité utilisent ce facteur d'âge au lieu de l'index dans le ring buffer.
Pourquoi c'est important à bas FPS : À 15 FPS, seuls ~15 échantillons/sec
sont enregistrés (moins de positions uniques), mais chacun porte le bon
timestamp. Le ruban couvre toujours exactement trail_duration secondes de
trajectoire, donc la longueur des traînées est identique à 15 et 60 FPS.
À time_scale élevé (ex. 8×), 8 échantillons sont émis par frame,
préservant la fidélité spatiale.
Zones de Profilage¶
Le pipeline N-corps est instrumenté avec des zones de profilage CPU à grain
fin (visibles dans Tracy ou tout profileur compatible PROFILE_ZONE).
Zones CPU (PROFILE_ZONE — Thread Principal)¶
| Zone | Fichier | Ce qu'elle mesure |
|---|---|---|
NBody Physics |
app.c |
Wrapper top-level (parent de toutes les suivantes) |
NBody Verlet |
scene.c |
nbody_step() — gravité O(N²) + Velocity Verlet |
NBody Trail Sample |
scene.c |
trail_renderer_record() — écriture ring buffers |
NBody Instance Build |
scene.c |
nbody_write_instances() — génération matrices modèle |
NBody VBO Upload |
scene.c |
instanced_group_update() → glBufferSubData |
Trail Ribbon Build |
trail_renderer.c |
build_ribbon() × N corps — staging géométrie CPU |
Trail VBO Upload |
trail_renderer.c |
glBufferSubData — upload buffer GPU |
Trail Draw Calls |
trail_renderer.c |
glMultiDrawArrays — N strips batchés en 1 appel |
Zones GPU (GPU_STAGE_PROFILER — Contexte OpenGL)¶
| Zone | Fichier | Ce qu'elle mesure |
|---|---|---|
Instanced Render |
scene.c |
Draw call des sphères (partagé avec grille matériaux) |
NBody Trails |
scene.c |
Passe complète de rendu des traînées |
Cycle de Vie des Ressources¶
Lorsque le mode N-corps est basculé sur OFF, le moteur restaure la grille
de matériaux originale en appelant scene_init_instancing() une seconde fois.
Ce chemin de ré-initialisation doit d'abord libérer chaque ressource allouée
par l'appel précédent pour éviter les fuites de buffers GPU et de mémoire CPU :
/* Nettoyage avant ré-init (scene.c — branche N-body OFF) */
trail_renderer_cleanup(&scene->trail_renderer);
#ifdef USE_TRANSPARENT_BILLBOARDS
if (scene->billboard_instances) {
platform_aligned_free(scene->billboard_instances);
scene->billboard_instances = NULL;
}
billboard_sorter_cleanup(&scene->billboard_sorter);
#endif
instanced_group_cleanup(&scene->instanced_group);
billboard_group_cleanup(&scene->billboard_group);
scene_init_instancing(scene);
Le nettoyage reproduit ce que fait scene_cleanup() à la fermeture de
l'application. Sans cela, chaque bascule ON → OFF fuyait ~27 Ko (4 blocs :
instance VBO, billboard VBO, tableau d'instances aligné, trieur de sphères).
Validé avec just test-integration-valgrind-full — 0 bytes definitely lost.
Carte du Code¶
| Fichier | Rôle |
|---|---|
src/nbody/nbody.h |
API publique de la façade (seul fichier à inclure) |
src/nbody/nbody_types.h |
Structures de données (NBodySim, NBodyParticle) et constantes |
src/nbody/nbody_physics.c |
Cœur mathématique : Intégrateur Verlet et adoucissement |
src/nbody/nbody_diagnostics.c |
Mesures de stabilité (Hamiltonien, dérive d'énergie) |
src/nbody/nbody_preset.c |
Configuration initiale et constantes des astres (OrbiterDef) |
src/nbody/nbody_render.c |
Pont graphique : traduction vers SphereInstance |
tests/test_nbody_stability.c |
Suite de tests de stabilité (8 tests) |
tests/test_nbody_stability.c |
Suite de tests de stabilité (8 tests) |
include/trail_renderer.h |
Rendu des traînées (rubans visuels derrière les corps) |
src/trail_renderer.c |
Implémentation des traînées en rubans billboard |
src/app_input.c |
Raccourcis vitesse de simulation (, / .) et gravité (variantes Shift) |
src/app_ui.c |
Overlay HUD N-corps (énergie, gravité, stabilité) |
src/app_binding.c |
Enregistrement dans l'overlay d'aide F2 |
include/shockwave.h |
API et structures de données des ondes de choc |
src/shockwave.c |
Renderer des ondes de choc (init, emit, update, draw) |
shaders/shockwave.vert |
Vertex shader billboard (quad face caméra) |
shaders/shockwave.frag |
Fragment shader d'anneau procédural en expansion |
shaders/trail.vert |
Vertex shader des traînées |
shaders/trail.frag |
Fragment shader des traînées |