-
-
Notifications
You must be signed in to change notification settings - Fork 407
Expand file tree
/
Copy pathStarSystemGenerator.cpp
More file actions
1943 lines (1671 loc) · 74.2 KB
/
Copy pathStarSystemGenerator.cpp
File metadata and controls
1943 lines (1671 loc) · 74.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright © 2008-2026 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
#include "StarSystemGenerator.h"
#include "AtmosphereParameters.h"
#include "Factions.h"
#include "Galaxy.h"
#include "Json.h"
#include "Lang.h"
#include "Pi.h"
#include "Sector.h"
#include "gameconsts.h"
#include "core/Log.h"
#include "core/macros.h"
#include "galaxy/Economy.h"
#include "lua/LuaNameGen.h"
#include "profiler/Profiler.h"
#include <functional>
static const fixed SUN_MASS_TO_EARTH_MASS = fixed(332998, 1); // XXX Duplication from StarSystem.cpp
// if binary stars have separation s, planets can have stable
// orbits at (0.5 * s * SAFE_DIST_FROM_BINARY)
static const fixed SAFE_DIST_FROM_BINARY = fixed(5, 1);
// very crudely
static const fixed AU_SOL_RADIUS = fixed(305, 65536);
static const fixed AU_EARTH_RADIUS = fixed(3, 65536); // XXX Duplication from StarSystem.cpp
static const double CELSIUS = 273.15;
static const fixed ONEEUMASS = fixed::FromDouble(1);
static const fixed TWOHUNDREDEUMASSES = fixed::FromDouble(200.0);
// Estimate "prototype" body radius from sphere-density formula:
// Convert 1 Earth Mass to a volume in cubic megameters (10^18 * m^3) then premultiply by 3/4pi
static const fixedf<48> EARTH_MASS_TO_VOL_MM3 = fixedf<48>(25946, 100); // 259.46 = (3 / EARTH_DENSITY / 4*PI) * 5.9742
// Convert a distance in megameters to earth radii
static const fixedf<48> MM_TO_EARTH_RAD = fixedf<48>(15678, 100000);
// max surface gravity for a permanent human settlement
static const double MAX_SETTLEMENT_SURFACE_GRAVITY = 50; // m/s2 .. roughly 5 g
static const Uint32 POLIT_SEED = 0x1234abcd;
static const Uint32 POLIT_SALT = 0x8732abdf;
const fixed StarSystemLegacyGeneratorBase::starMetallicities[] = {
fixed(1, 1), // GRAVPOINT - for planets that orbit them
fixed(9, 10), // brown dwarf
fixed(5, 10), // white dwarf
fixed(7, 10), // M0
fixed(6, 10), // K0
fixed(5, 10), // G0
fixed(4, 10), // F0
fixed(3, 10), // A0
fixed(2, 10), // B0
fixed(1, 10), // O5
fixed(8, 10), // M0 Giant
fixed(65, 100), // K0 Giant
fixed(55, 100), // G0 Giant
fixed(4, 10), // F0 Giant
fixed(3, 10), // A0 Giant
fixed(2, 10), // B0 Giant
fixed(1, 10), // O5 Giant
fixed(9, 10), // M0 Super Giant
fixed(7, 10), // K0 Super Giant
fixed(6, 10), // G0 Super Giant
fixed(4, 10), // F0 Super Giant
fixed(3, 10), // A0 Super Giant
fixed(2, 10), // B0 Super Giant
fixed(1, 10), // O5 Super Giant
fixed(1, 1), // M0 Hyper Giant
fixed(7, 10), // K0 Hyper Giant
fixed(6, 10), // G0 Hyper Giant
fixed(4, 10), // F0 Hyper Giant
fixed(3, 10), // A0 Hyper Giant
fixed(2, 10), // B0 Hyper Giant
fixed(1, 10), // O5 Hyper Giant
fixed(1, 1), // M WF
fixed(8, 10), // B WF
fixed(6, 10), // O WF
fixed(1, 1), // S BH Blackholes, give them high metallicity,
fixed(1, 1), // IM BH so any rocks that happen to be there
fixed(1, 1) // SM BH may be mining hotspots. FUN :)
};
const StarSystemLegacyGeneratorBase::StarTypeInfo StarSystemLegacyGeneratorBase::starTypeInfo[] = {
{ {}, {},
0, 0 },
{ //Brown Dwarf
{ 2, 8 }, { 10, 30 },
1000, 2000 },
{ //white dwarf
{ 20, 100 }, { 1, 2 },
4000, 40000 },
{ //M
{ 10, 47 }, { 30, 60 },
2000, 3500 },
{ //K
{ 50, 78 }, { 60, 100 },
3500, 5000 },
{ //G
{ 80, 110 }, { 80, 120 },
5000, 6000 },
{ //F
{ 115, 170 }, { 110, 150 },
6000, 7500 },
{ //A
{ 180, 320 }, { 120, 220 },
7500, 10000 },
{ //B
{ 200, 300 }, { 120, 290 },
10000, 30000 },
{ //O
{ 300, 400 }, { 200, 310 },
30000, 60000 },
{ //M Giant
{ 60, 357 }, { 2000, 5000 },
2500, 3500 },
{ //K Giant
{ 125, 500 }, { 1500, 3000 },
3500, 5000 },
{ //G Giant
{ 200, 800 }, { 1000, 2000 },
5000, 6000 },
{ //F Giant
{ 250, 900 }, { 800, 1500 },
6000, 7500 },
{ //A Giant
{ 400, 1000 }, { 600, 1000 },
7500, 10000 },
{ //B Giant
{ 500, 1000 }, { 600, 1000 },
10000, 30000 },
{ //O Giant
{ 600, 1200 }, { 600, 1000 },
30000, 60000 },
{ //M Super Giant
{ 1050, 5000 }, { 7000, 15000 },
2500, 3500 },
{ //K Super Giant
{ 1100, 5000 }, { 5000, 9000 },
3500, 5000 },
{ //G Super Giant
{ 1200, 5000 }, { 4000, 8000 },
5000, 6000 },
{ //F Super Giant
{ 1500, 6000 }, { 3500, 7000 },
6000, 7500 },
{ //A Super Giant
{ 2000, 8000 }, { 3000, 6000 },
7500, 10000 },
{ //B Super Giant
{ 3000, 9000 }, { 2500, 5000 },
10000, 30000 },
{ //O Super Giant
{ 5000, 10000 }, { 2000, 4000 },
30000, 60000 },
{ //M Hyper Giant
{ 5000, 15000 }, { 20000, 40000 },
2500, 3500 },
{ //K Hyper Giant
{ 5000, 17000 }, { 17000, 25000 },
3500, 5000 },
{ //G Hyper Giant
{ 5000, 18000 }, { 14000, 20000 },
5000, 6000 },
{ //F Hyper Giant
{ 5000, 19000 }, { 12000, 17500 },
6000, 7500 },
{ //A Hyper Giant
{ 5000, 20000 }, { 10000, 15000 },
7500, 10000 },
{ //B Hyper Giant
{ 5000, 23000 }, { 6000, 10000 },
10000, 30000 },
{ //O Hyper Giant
{ 10000, 30000 }, { 4000, 7000 },
30000, 60000 },
{ // M WF
{ 2000, 5000 }, { 2500, 5000 },
25000, 35000 },
{ // B WF
{ 2000, 7500 }, { 2500, 5000 },
35000, 45000 },
{ // O WF
{ 2000, 10000 }, { 2500, 5000 },
45000, 60000 },
{ // S BH
{ 20, 2000 }, { 0, 0 }, // XXX black holes are < 1 Sol radii big; this is clamped to a non-zero value later
10, 24 },
{ // IM BH
{ 900000, 1000000 }, { 100, 500 },
1, 10 },
{ // SM BH
{ 2000000, 5000000 }, { 10000, 20000 },
10, 24 }
};
bool StarSystemFromSectorGenerator::Apply(Random &rng, RefCountedPtr<Galaxy> galaxy, const Sector *sec, RefCountedPtr<StarSystem::GeneratorAPI> system, GalaxyGenerator::StarSystemConfig *config)
{
PROFILE_SCOPED()
assert(system->GetPath().systemIndex < sec->m_systems.size());
const Sector::System &secSys = sec->m_systems[system->GetPath().systemIndex];
system->SetPosition(secSys.GetPosition());
system->SetFaction(galaxy->GetFactions()->GetNearestClaimant(&secSys));
system->SetSeed(secSys.GetSeed());
system->SetName(secSys.GetName());
system->SetOtherNames(secSys.GetOtherNames());
system->SetExplored(secSys.GetExplored(), secSys.GetExploredTime());
return true;
}
void StarSystemLegacyGeneratorBase::PickAtmosphere(SystemBody *sbody)
{
/* Alpha value isn't real alpha. in the shader fog depth is determined
* by density*alpha, so that we can have very dense atmospheres
* without having them a big stinking solid color obscuring everything
These are our atmosphere colours, for terrestrial planets we use m_atmosOxidizing
for some variation to atmosphere colours
*/
switch (sbody->GetType()) {
case SystemBody::TYPE_PLANET_GAS_GIANT:
sbody->m_atmosColor = Color(255, 255, 255, 3);
// NOTE: realistic generation for gas giant atmospheres needed elsewhere
// sbody->m_atmosDensity = 14.0;
break;
case SystemBody::TYPE_PLANET_ASTEROID:
sbody->m_atmosColor = Color::BLANK;
break;
default:
case SystemBody::TYPE_PLANET_TERRESTRIAL:
double r = 0, g = 0, b = 0;
double atmo = sbody->GetAtmosOxidizing();
if (sbody->GetVolatileGas() > 0.001) {
if (atmo > 0.95) {
// o2
r = 1.0f + ((0.95f - atmo) * 15.0f);
g = 0.95f + ((0.95f - atmo) * 10.0f);
b = atmo * atmo * atmo * atmo * atmo;
} else if (atmo > 0.7) {
// co2
r = atmo + 0.05f;
g = 1.0f + (0.7f - atmo);
b = 0.8f;
} else if (atmo > 0.65) {
// co
r = 1.0f + (0.65f - atmo);
g = 0.8f;
b = atmo + 0.25f;
} else if (atmo > 0.55) {
// ch4
r = 1.0f + ((0.55f - atmo) * 5.0);
g = 0.35f - ((0.55f - atmo) * 5.0);
b = 0.4f;
} else if (atmo > 0.3) {
// h
r = 1.0f;
g = 1.0f;
b = 1.0f;
} else if (atmo > 0.2) {
// he
r = 1.0f;
g = 1.0f;
b = 1.0f;
} else if (atmo > 0.15) {
// ar
r = 0.5f - ((0.15f - atmo) * 5.0);
g = 0.0f;
b = 0.5f + ((0.15f - atmo) * 5.0);
} else if (atmo > 0.1) {
// s
r = 0.8f - ((0.1f - atmo) * 4.0);
g = 1.0f;
b = 0.5f - ((0.1f - atmo) * 10.0);
} else {
// n
r = 1.0f;
g = 1.0f;
b = 1.0f;
}
sbody->m_atmosColor = Color(r * 255, g * 255, b * 255, 255);
} else {
sbody->m_atmosColor = Color::BLANK;
}
//Output("| Atmosphere :\n| red : [%f] \n| green : [%f] \n| blue : [%f] \n", r, g, b);
//Output("-------------------------------\n");
break;
/*default:
sbody->m_atmosColor = Color(0.6f, 0.6f, 0.6f, 1.0f);
sbody->m_atmosDensity = m_body->m_volatileGas.ToDouble();
break;*/
}
}
static const unsigned char RANDOM_RING_COLORS[][4] = {
{ 156, 122, 98, 217 }, // jupiter-like
{ 156, 122, 98, 217 }, // saturn-like
{ 181, 173, 174, 217 }, // neptune-like
{ 130, 122, 98, 217 }, // uranus-like
{ 207, 122, 98, 217 } // brown dwarf-like
};
void StarSystemLegacyGeneratorBase::PickRings(SystemBodyData *sbody, bool forceRings)
{
sbody->m_rings.minRadius = fixed();
sbody->m_rings.maxRadius = fixed();
sbody->m_rings.baseColor = Color(255, 255, 255, 255);
bool bHasRings = forceRings;
if (!bHasRings) {
Random ringRng(sbody->m_seed + 965467);
// today's forecast:
if (sbody->m_type == SystemBody::TYPE_PLANET_GAS_GIANT) {
// 50% chance of rings
bHasRings = ringRng.Double() < 0.5;
} else if (sbody->m_type == SystemBody::TYPE_PLANET_TERRESTRIAL) {
// 10% chance of rings
bHasRings = ringRng.Double() < 0.1;
}
/*else if (sbody->m_type == SystemBody::TYPE_PLANET_ASTEROID)
{
// 1:10 (10%) chance of rings
bHasRings = ringRng.Double() < 0.1;
}*/
}
if (bHasRings) {
Random ringRng(sbody->m_seed + 965467);
// today's forecast: 50% chance of rings
double rings_die = ringRng.Double();
if (forceRings || (rings_die < 0.5)) {
const unsigned char *const baseCol = RANDOM_RING_COLORS[ringRng.Int32(COUNTOF(RANDOM_RING_COLORS))];
sbody->m_rings.baseColor.r = Clamp(baseCol[0] + ringRng.Int32(-20, 20), 0, 255);
sbody->m_rings.baseColor.g = Clamp(baseCol[1] + ringRng.Int32(-20, 20), 0, 255);
sbody->m_rings.baseColor.b = Clamp(baseCol[2] + ringRng.Int32(-20, 10), 0, 255);
sbody->m_rings.baseColor.a = Clamp(baseCol[3] + ringRng.Int32(-5, 5), 0, 255);
// from wikipedia: http://en.wikipedia.org/wiki/Roche_limit
// basic Roche limit calculation assuming a rigid satellite
// d = R (2 p_M / p_m)^{1/3}
//
// where R is the radius of the primary, p_M is the density of
// the primary and p_m is the density of the satellite
//
// I assume a satellite density of 500 kg/m^3
// (which Wikipedia says is an average comet density)
//
// also, I can't be bothered to think about unit conversions right now,
// so I'm going to ignore the real density of the primary and take it as 1100 kg/m^3
// (note: density of Saturn is ~687, Jupiter ~1,326, Neptune ~1,638, Uranus ~1,318)
//
// This gives: d = 1.638642 * R
fixed innerMin = fixed(110, 100);
fixed innerMax = fixed(145, 100);
fixed outerMin = fixed(150, 100);
fixed outerMax = fixed(168642, 100000);
sbody->m_rings.minRadius = innerMin + (innerMax - innerMin) * ringRng.Fixed();
sbody->m_rings.maxRadius = outerMin + (outerMax - outerMin) * ringRng.Fixed();
}
}
}
/*
* http://en.wikipedia.org/wiki/Hill_sphere
*/
fixedf<48> StarSystemLegacyGeneratorBase::CalcHillRadius(SystemBody *sbody) const
{
PROFILE_SCOPED()
// high-precision for working with very small numbers
// system distances are not expected to be larger than 32k AU
using fixedp = fixedf<48>;
if (sbody->GetSuperType() <= SystemBody::SUPERTYPE_STAR) {
return fixedp();
} else {
// playing with precision since these numbers get small
// masses in earth masses
fixed mprimary = sbody->GetParent()->GetMassInEarths();
fixedp a = sbody->GetSemiMajorAxisAsFixed();
fixedp e = sbody->GetEccentricityAsFixed();
fixedp pe = a * (fixedp(1, 1) - e); // periapsis in higher precision
fixedp mass_ratio = sbody->GetMassAsFixed() / (fixed(3, 1) * mprimary);
return pe * fixedp::CubeRootOf(mass_ratio);
//fixed hr = semiMajorAxis*(fixed(1,1) - eccentricity) *
// fixedcuberoot(mass / (3*mprimary));
}
}
void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtr<StarSystem::GeneratorAPI> system, SystemBody *parent,
const std::vector<CustomSystemBody *> &children, int *outHumanInfestedness)
{
PROFILE_SCOPED()
// gravpoints have no mass, but we sum the masses of its children instead
if (parent->GetType() == SystemBody::TYPE_GRAVPOINT) {
parent->m_mass = fixed(0);
// parent gravpoint mass = sum of masses of its children
for (const auto *child : children) {
// FIXME: this implicitly requires that gravpoint children have a precomputed mass sum
// gravpoint mass calculation needs a first-pass over the body data to properly sum masses before orbits are applied
// this function can only handle a single incorrect-mass gravpoint with no gravpoint children as written
if (child->bodyData.m_type >= SystemBody::TYPE_GRAVPOINT && child->bodyData.m_type <= SystemBody::TYPE_STAR_MAX)
parent->m_mass += child->bodyData.m_mass;
else
parent->m_mass += child->bodyData.m_mass / SUN_MASS_TO_EARTH_MASS;
}
}
for (std::vector<CustomSystemBody *>::const_iterator i = children.begin(); i != children.end(); ++i) {
const CustomSystemBody *csbody = *i;
SystemBody *kid = system->NewBody();
kid->m_parent = parent;
kid->m_isCustomBody = true;
// Copy all system body parameters from the custom system body
*kid = csbody->bodyData;
kid->SetOrbitFromParameters();
kid->SetAtmFromParameters();
if (kid->GetType() != SystemBody::TYPE_STARPORT_SURFACE) {
if (kid->GetSuperType() == SystemBody::SUPERTYPE_STARPORT) {
fixed lowestOrbit = fixed().FromDouble(parent->CalcAtmosphereParams().atmosRadius + 500000.0 / EARTH_RADIUS);
if (kid->GetOrbit().GetSemiMajorAxis() < lowestOrbit.ToDouble()) {
Error("%s's orbit is too close to its parent (%.2f/%.2f)", kid->m_name.c_str(), kid->GetOrbit().GetSemiMajorAxis(), lowestOrbit.ToFloat());
}
} else {
if (kid->GetOrbit().GetSemiMajorAxis() < 1.2 * parent->GetRadius()) {
Error("%s's orbit is too close to its parent", kid->m_name.c_str());
}
}
}
if (kid->GetSuperType() == SystemBody::SUPERTYPE_STARPORT) {
(*outHumanInfestedness)++;
system->AddSpaceStation(kid);
}
parent->m_children.push_back(kid);
PickAtmosphere(kid);
CustomGetKidsOf(system, kid, csbody->children, outHumanInfestedness);
}
}
bool StarSystemCustomGenerator::ApplyToSystem(Random &rng, RefCountedPtr<StarSystem::GeneratorAPI> system, const CustomSystem *customSys)
{
system->SetCustom(true, false);
system->SetSeed(customSys->seed);
system->SetNumStars(customSys->numStars);
system->SetPosition(customSys->pos);
system->SetOtherNames(customSys->other_names);
if (customSys->name.length() > 0) system->SetName(customSys->name);
if (customSys->shortDesc.length() > 0) system->SetShortDesc(customSys->shortDesc);
if (customSys->longDesc.length() > 0) system->SetLongDesc(customSys->longDesc);
SysPolit sysPolit;
sysPolit.govType = customSys->govType;
sysPolit.lawlessness = customSys->lawlessness;
system->SetSysPolit(sysPolit);
if (customSys->IsRandom())
return false;
system->SetCustom(true, true);
const CustomSystemBody *csbody = customSys->sBody;
SystemBody *rootBody = system->NewBody();
*rootBody = csbody->bodyData;
rootBody->m_parent = 0;
rootBody->m_isCustomBody = true;
system->SetRootBody(rootBody);
int humanInfestedness = 0;
CustomGetKidsOf(system, rootBody, csbody->children, &humanInfestedness);
unsigned countedStars = 0;
for (RefCountedPtr<SystemBody> b : system->GetBodies()) {
if (b->GetSuperType() == SystemBody::SUPERTYPE_STAR) {
++countedStars;
system->AddStar(b.Get());
}
}
(void) countedStars;
assert(countedStars == system->GetNumStars());
return true;
}
bool StarSystemCustomGenerator::Apply(Random &rng, RefCountedPtr<Galaxy> galaxy, const Sector *sec, RefCountedPtr<StarSystem::GeneratorAPI> system, GalaxyGenerator::StarSystemConfig *config)
{
PROFILE_SCOPED()
system->SetCustom(false, false);
// No system entry in the Sector, may be a "new" custom system from the Editor
if (system->GetPath().systemIndex >= sec->m_systems.size())
return true;
if (const CustomSystem *customSys = sec->m_systems[system->GetPath().systemIndex].GetCustomSystem())
config->isCustomOnly = ApplyToSystem(rng, system, customSys);
return true;
}
/*
* star_radius in sol radii
* star_temp in kelvin,
* object_dist in AU
* return energy per unit area in solar constants (1362 W/m^2 )
*/
static fixed calcEnergyPerUnitAreaAtDist(fixed star_radius, int star_temp, fixed object_dist)
{
// energy = boltzmann * T^4 * 4 * PI * r^2
// energy_per_m2 = energy / ( 4 * PI * dist^2 )
// drop 4*PI because it directly cancels
// energy_per_m2 is later divided by the boltzmann constant so drop that too
fixed temp = star_temp * fixed(1, 5778); //normalize to Sun's temperature
const fixed total_solar_emission =
temp * temp * temp * temp * star_radius * star_radius;
return total_solar_emission / (object_dist * object_dist); //return value in solar consts (overflow prevention)
}
//helper function, get branch of system tree from body all the way to the system's root and write it to path
static void getPathToRoot(const SystemBody *body, std::vector<const SystemBody *> &path)
{
while (body) {
path.push_back(body);
body = body->GetParent();
}
}
int StarSystemRandomGenerator::CalcSurfaceTemp(const SystemBody *primary, fixed distToPrimary, fixed albedo, fixed greenhouse)
{
PROFILE_SCOPED()
// accumulator seeded with current primary
fixed energy_per_meter2 = calcEnergyPerUnitAreaAtDist(primary->m_radius, primary->m_averageTemp, distToPrimary);
fixed dist;
// find the other stars which aren't our parent star
for (auto *s : primary->GetStarSystem()->GetStars()) {
if (s != primary) {
//get branches from body and star to system root
std::vector<const SystemBody *> first_to_root;
std::vector<const SystemBody *> second_to_root;
getPathToRoot(primary, first_to_root);
getPathToRoot(&(*s), second_to_root);
std::vector<const SystemBody *>::reverse_iterator fit = first_to_root.rbegin();
std::vector<const SystemBody *>::reverse_iterator sit = second_to_root.rbegin();
// keep tracing both branches from system's root until they diverge
while (sit != second_to_root.rend() && fit != first_to_root.rend() && (*sit) == (*fit)) {
++sit;
++fit;
}
if (sit == second_to_root.rend()) --sit;
if (fit == first_to_root.rend()) --fit; //oops! one of the branches ends at lca, backtrack
//planet is around one part of coorbiting pair, star is another.
if ((*fit)->IsCoOrbitalWith(*sit)) {
dist = ((*fit)->GetOrbMaxAsFixed() + (*fit)->GetOrbMinAsFixed()) >> 1; //binaries don't have fully initialized smaxes
} else if ((*sit)->IsCoOrbital()) {
//star is part of binary around which planet is (possibly indirectly) orbiting
bool inverted_ancestry = false;
for (const SystemBody *body = (*sit); body; body = body->GetParent())
if (body == (*fit)) {
inverted_ancestry = true; //ugly hack due to function being static taking planet's primary rather than being called from actual planet
break;
}
if (inverted_ancestry) //primary is star's ancestor! Don't try to take its orbit (could probably be a gravpoint check at this point, but paranoia)
{
dist = distToPrimary;
} else {
dist = ((*fit)->GetOrbMaxAsFixed() + (*fit)->GetOrbMinAsFixed()) >> 1; //simplified to planet orbiting stationary star
}
} else if ((*fit)->IsCoOrbital()) //planet is around one part of coorbiting pair, star isn't coorbiting with it
{
//simplified to star orbiting stationary planet. neither is part of any binaries - hooray!
dist = ((*sit)->GetOrbMaxAsFixed() + (*sit)->GetOrbMinAsFixed()) >> 1;
} else {
//avg of conjunction and opposition dist
dist = (((*sit)->GetSemiMajorAxisAsFixed() - (*fit)->GetSemiMajorAxisAsFixed()).Abs() + ((*sit)->GetSemiMajorAxisAsFixed() + (*fit)->GetSemiMajorAxisAsFixed()));
dist >>= 1;
}
}
energy_per_meter2 += calcEnergyPerUnitAreaAtDist(s->m_radius, s->m_averageTemp, dist);
}
/*
// Can't use this version as pow() is nowhere near deterministic across multiple platforms and compilers
// Luckily pow(x, 0.25) can be expressed as two successive sqrt operations
// bond albedo, not geometric
static double CalcSurfaceTemp(double star_radius, double star_temp, double object_dist, double albedo, double greenhouse)
{
const double energy_per_meter2 = calcEnergyPerUnitAreaAtDist(star_radius, star_temp, object_dist);
const double surface_temp = pow(energy_per_meter2*(1-albedo)/(4*(1-greenhouse)*boltzman_const), 0.25);
return surface_temp;
}
*/
const fixed surface_temp_pow4 = energy_per_meter2 * (1 - albedo) / (1 - greenhouse);
return (279 * int(isqrt(isqrt((surface_temp_pow4.v))))) >> (fixed::FRAC / 4); //multiplied by 279 to convert from Earth's temps to Kelvin
}
/*
* For moons distance from star is not orbMin, orbMax.
*/
const SystemBody *StarSystemRandomGenerator::FindStarAndTrueOrbitalRange(const SystemBody *planet, fixed &orbMin_, fixed &orbMax_) const
{
const SystemBody *star = planet->GetParent();
assert(star);
/* while not found star yet.. */
while (star->GetSuperType() > SystemBody::SUPERTYPE_STAR) {
planet = star;
star = star->GetParent();
}
orbMin_ = planet->GetOrbMinAsFixed();
orbMax_ = planet->GetOrbMaxAsFixed();
return star;
}
/**
* In this and following functions, we attempt to capture even a fraction of the
* true majesty of the infinite cosmos.
*
* System generation is based primarily around a mass-based metric, where the
* total mass of a primary body determines the maximum mass of its individual
* satellites.
*
* The area of a body's "orbital slice" (between itself and any neighboring
* bodies) informs the mass of a body to avoid obviously-unnatural
* configurations with high-mass bodies orbiting so closely as to perturb each
* others' orbits beyond what Pioneer can simulate.
*
* A series of post-processing factors are applied to this initial maximum mass
* value to curve the mass factor over the entire Hill Sphere of the parent
* body. This reduces the incidence of "gas giant spam" and produces more
* perceptually-realistic arrangements of body types and masses.
*
* Body radius and type is inferred from the mass of the body and applied based
* on a set of density heuristics (in PickPlanetType), and remaining body
* parameters are set to complete body generation.
*/
void StarSystemRandomGenerator::PickPlanetType(SystemBody *sbody, Random &rand)
{
PROFILE_SCOPED()
fixed albedo;
fixed greenhouse;
fixed minDistToStar, maxDistToStar, averageDistToStar;
const SystemBody *star = FindStarAndTrueOrbitalRange(sbody, minDistToStar, maxDistToStar);
averageDistToStar = (minDistToStar + maxDistToStar) >> 1;
// first calculate blackbody temp (no greenhouse effect, zero albedo)
sbody->m_averageTemp = CalcSurfaceTemp(star, averageDistToStar, albedo, greenhouse);
// We get some more fractional bits for small bodies otherwise we can easily end up with 0 radius which breaks stuff elsewhere
//
// AndyC - Updated to use the empirically gathered data from this site:
// https://phl.upr.edu/library/labnotes/standard-mass-radius-relation-for-exoplanets
// but we still limit at the lowest end
if (sbody->GetMassAsFixed() <= fixed(1, 1)) {
// We know the upper bound on this value is +259.9, so 16 integer bits is fine.
fixedf<48> vol = fixedf<48>(sbody->GetMassAsFixed()) * EARTH_MASS_TO_VOL_MM3;
// If the cube-root continues to underflow, pre-shift vol left by 6 bits, then post-shift the result by 2 bits.
// sbody->m_radius = (fixedf<48>::CubeRootOf(vol << 6) >> 2) * MM_TO_EARTH_RAD;
sbody->m_radius = fixedf<48>::CubeRootOf(vol) * MM_TO_EARTH_RAD;
} else if (sbody->GetMassAsFixed() < ONEEUMASS) {
// smaller than 1 Earth mass is almost certainly a rocky body
sbody->m_radius = fixed::FromDouble(pow(sbody->GetMassAsFixed().ToDouble(), 0.3));
} else if (sbody->GetMassAsFixed() < TWOHUNDREDEUMASSES) {
// from 1 EU to 200 they transition from Earth-like rocky bodies, through Ocean worlds and on to Gas Giants
sbody->m_radius = fixed::FromDouble(pow(sbody->GetMassAsFixed().ToDouble(), 0.5));
} else {
// Anything bigger than 200 EU masses is a Gas Giant or bigger but the density changes to decrease from here on up...
sbody->m_radius = fixed::FromDouble(22.6 * (1.0 / pow(sbody->GetMassAsFixed().ToDouble(), double(0.0886))));
}
// randomize radius or the surface gravity will be consistently 1.0 g for planets in the 1 - 200 EU range.
sbody->m_radius = fixed::FromDouble(sbody->GetRadiusAsFixed().ToDouble() * ( 1.2 - (0.4 * rand.Double()))); // +/-20% radius
// enforce minimum size of 10km
sbody->m_radius = std::max(sbody->GetRadiusAsFixed(), fixed(1, 630));
if (sbody->GetParent()->GetType() <= SystemBody::TYPE_STAR_MAX) {
// get it from the table now rather than setting it on stars/gravpoints as
// currently nothing else needs them to have metallicity
sbody->m_metallicity = starMetallicities[sbody->GetParent()->GetType()] * rand.Fixed();
} else {
// this assumes the parent's parent is a star/gravpoint, which is currently always true
sbody->m_metallicity = starMetallicities[sbody->GetParent()->GetParent()->GetType()] * rand.Fixed();
}
// harder to be volcanic when you are tiny (you cool down)
sbody->m_volcanicity = std::min(fixed(1, 1), sbody->GetMassAsFixed()) * rand.Fixed();
sbody->m_atmosOxidizing = rand.Fixed();
sbody->m_life = fixed();
sbody->m_volatileGas = fixed();
sbody->m_volatileLiquid = fixed();
sbody->m_volatileIces = fixed();
// pick body type
if (sbody->GetMassAsFixed() > 317 * 13) {
// more than 13 jupiter masses can fuse deuterium - is a brown dwarf
sbody->m_type = SystemBody::TYPE_BROWN_DWARF;
sbody->m_averageTemp = sbody->GetAverageTemp() + rand.Int32(starTypeInfo[sbody->GetType()].tempMin, starTypeInfo[sbody->GetType()].tempMax);
// prevent mass exceeding 65 jupiter masses or so, when it becomes a star
// XXX since TYPE_BROWN_DWARF is supertype star, mass is now in
// solar masses. what a fucking mess
sbody->m_mass = std::min(sbody->GetMassAsFixed(), fixed(317 * 65, 1)) / SUN_MASS_TO_EARTH_MASS;
//Radius is too high as it now uses the planetary calculations to work out radius (Cube root of mass)
// So tell it to use the star data instead:
sbody->m_radius = fixed(rand.Int32(starTypeInfo[sbody->GetType()].radius[0], starTypeInfo[sbody->GetType()].radius[1]), 100);
sbody->GenerateStarColor();
} else if (sbody->GetMassAsFixed() > 6) {
sbody->m_type = SystemBody::TYPE_PLANET_GAS_GIANT;
// Generate a random "surface" density for gas giants roughly fitted to real-life estimation of Jupiter at "cloud deck" level
// The bounds are derived from real-world density-at-1-bar data for the outer gas giants with an
// approximation factor for density at cloud deck level of `3e * density @ 1 bar`
sbody->m_volatileGas = rand.NormFixed(fixed(1050, 1000), fixed(8000, 1000)).Abs();
// Most gas giant atmospheres contain an incredibly small or negligible proportion of oxidizing elements / water ice
sbody->m_atmosOxidizing = rand.NormFixed(fixed(0, 1), fixed(300, 1000)).Abs();
} else if (sbody->GetMassAsFixed() > fixed(1, 12000)) {
sbody->m_type = SystemBody::TYPE_PLANET_TERRESTRIAL;
fixed amount_volatiles = fixed(2, 1) * rand.Fixed();
if (rand.Int32(3)) amount_volatiles *= sbody->GetMassAsFixed();
// total atmosphere loss
if (rand.Fixed() > sbody->GetMassAsFixed()) amount_volatiles = fixed();
//Output("Amount volatiles: %f\n", amount_volatiles.ToFloat());
// fudge how much of the volatiles are in which state
greenhouse = fixed();
albedo = fixed();
// CO2 sublimation
if (sbody->GetAverageTemp() > 195)
greenhouse += amount_volatiles * fixed(1, 3);
else
albedo += fixed(2, 6);
// H2O liquid
if (sbody->GetAverageTemp() > 273)
greenhouse += amount_volatiles * fixed(1, 5);
else
albedo += fixed(3, 6);
// H2O boils
if (sbody->GetAverageTemp() > 373) greenhouse += amount_volatiles * fixed(1, 3);
if (greenhouse > fixed(7, 10)) { // never reach 1, but 1/(1-greenhouse) still grows
greenhouse *= greenhouse;
greenhouse *= greenhouse;
greenhouse = greenhouse / (greenhouse + fixed(32, 311));
}
sbody->m_averageTemp = CalcSurfaceTemp(star, averageDistToStar, albedo, greenhouse);
const fixed proportion_gas = sbody->GetAverageTemp() / (fixed(100, 1) + sbody->GetAverageTemp());
sbody->m_volatileGas = proportion_gas * amount_volatiles;
const fixed proportion_liquid = (fixed(1, 1) - proportion_gas) * (sbody->GetAverageTemp() / (fixed(50, 1) + sbody->GetAverageTemp()));
sbody->m_volatileLiquid = proportion_liquid * amount_volatiles;
const fixed proportion_ices = fixed(1, 1) - (proportion_gas + proportion_liquid);
sbody->m_volatileIces = proportion_ices * amount_volatiles;
//Output("temp %dK, gas:liquid:ices %f:%f:%f\n", averageTemp, proportion_gas.ToFloat(),
// proportion_liquid.ToFloat(), proportion_ices.ToFloat());
if ((sbody->GetVolatileLiquidAsFixed() > fixed()) &&
(sbody->GetAverageTemp() > CELSIUS - 60) &&
(sbody->GetAverageTemp() < CELSIUS + 200)) {
// try for life
int minTemp = CalcSurfaceTemp(star, maxDistToStar, albedo, greenhouse);
int maxTemp = CalcSurfaceTemp(star, minDistToStar, albedo, greenhouse);
if ((minTemp > CELSIUS - 10) && (minTemp < CELSIUS + 90) && //removed explicit checks for star type (also BD and WD seem to have slight chance of having life around them)
(maxTemp > CELSIUS - 10) && (maxTemp < CELSIUS + 90)) //TODO: ceiling based on actual boiling point on the planet, not in 1atm
{
fixed maxMass, lifeMult, allowedMass(1, 2);
allowedMass += 2;
//find the most massive star, mass is tied to lifespan
//this automagically eliminates O, B and so on from consideration
//handy calculator: http://www.asc-csa.gc.ca/eng/educators/resources/astronomy/module2/calculator.asp
//system could have existed long enough for life to form (based on Sol)
for (auto *s : sbody->GetStarSystem()->GetStars()) {
maxMass = maxMass < s->GetMassAsFixed() ? s->GetMassAsFixed() : maxMass;
}
if (maxMass < allowedMass) {
lifeMult = allowedMass - maxMass;
}
sbody->m_life = lifeMult * rand.Fixed();
}
}
} else {
sbody->m_type = SystemBody::TYPE_PLANET_ASTEROID;
}
// Tidal lock for planets close to their parents:
// http://en.wikipedia.org/wiki/Tidal_locking
//
// Formula: time ~ semiMajorAxis^6 * radius / mass / parentMass^2
//
// compared to Earth's Moon
static fixed MOON_TIDAL_LOCK = fixed(6286, 1);
fixed invTidalLockTime = fixed(1, 1);
// fine-tuned not to give overflows, order of evaluation matters!
if (sbody->GetParent()->GetType() <= SystemBody::TYPE_STAR_MAX) {
invTidalLockTime /= (sbody->GetSemiMajorAxisAsFixed() * sbody->GetSemiMajorAxisAsFixed());
invTidalLockTime *= sbody->GetMassAsFixed();
invTidalLockTime /= (sbody->GetSemiMajorAxisAsFixed() * sbody->GetSemiMajorAxisAsFixed());
invTidalLockTime *= sbody->GetParent()->GetMassAsFixed() * sbody->GetParent()->GetMassAsFixed();
invTidalLockTime /= sbody->GetRadiusAsFixed();
invTidalLockTime /= (sbody->GetSemiMajorAxisAsFixed() * sbody->GetSemiMajorAxisAsFixed()) * MOON_TIDAL_LOCK;
} else {
invTidalLockTime /= (sbody->GetSemiMajorAxisAsFixed() * sbody->GetSemiMajorAxisAsFixed()) * SUN_MASS_TO_EARTH_MASS;
invTidalLockTime *= sbody->GetMassAsFixed();
invTidalLockTime /= (sbody->GetSemiMajorAxisAsFixed() * sbody->GetSemiMajorAxisAsFixed()) * SUN_MASS_TO_EARTH_MASS;
invTidalLockTime *= sbody->GetParent()->GetMassAsFixed() * sbody->GetParent()->GetMassAsFixed();
invTidalLockTime /= sbody->GetRadiusAsFixed();
invTidalLockTime /= (sbody->GetSemiMajorAxisAsFixed() * sbody->GetSemiMajorAxisAsFixed()) * MOON_TIDAL_LOCK;
}
//Output("tidal lock of %s: %.5f, a %.5f R %.4f mp %.3f ms %.3f\n", name.c_str(),
// invTidalLockTime.ToFloat(), semiMajorAxis.ToFloat(), radius.ToFloat(), parent->mass.ToFloat(), mass.ToFloat());
if (invTidalLockTime > 10) { // 10x faster than Moon, no chance not to be tidal-locked
sbody->m_rotationPeriod = fixed(int(round(sbody->GetOrbit().Period())), 3600 * 24);
sbody->m_axialTilt = sbody->GetInclinationAsFixed();
} else if (invTidalLockTime > fixed(1, 100)) { // rotation speed changed in favour of tidal lock
// XXX: there should be some chance the satellite was captured only recently and ignore this
// I'm omitting that now, I do not want to change the Universe by additional rand call.
fixed lambda = invTidalLockTime / (fixed(1, 20) + invTidalLockTime);
sbody->m_rotationPeriod = (1 - lambda) * sbody->GetRotationPeriodAsFixed() + lambda * sbody->GetOrbit().Period() / 3600 / 24;
sbody->m_axialTilt = (1 - lambda) * sbody->GetAxialTiltAsFixed() + lambda * sbody->GetInclinationAsFixed();
} // else .. nothing happens to the satellite
sbody->SetAtmFromParameters();
PickAtmosphere(sbody);
PickRings(sbody);
}
static fixed mass_from_disk_area(fixed a, fixed b, fixed max)
{
// so, density of the disk with distance from star goes like so: 1 - x/discMax
//
// ---
// ---
// --- <- zero at discMax
//
// Which turned into a disc becomes 2*pi*x - (2*pi*x*x)/discMax
// Integral of which is: pi*x*x - (2/(3*discMax))*pi*x*x*x
//
// Because get_disc_density divides total_mass by
// mass_from_disk_area(0, discMax, discMax) to find density, the
// constant factors (pi) in this equation drop out.
//
b = (b > max ? max : b);
assert(b >= a);
assert(a <= max);
assert(b <= max);
assert(a >= 0);
fixed one_over_3max = fixed(2, 1) / (3 * max);
// We have to avoid overflow of fixed-point numbers
// Find a representation that doesn't calculate x*x*x
// m = 2/(3 * discMax)
// a' = a^2 - m * a^3
// a' a^-2 = a^-2 ( a^2 - m a^3 ) -> a^2 a^-2 - m a^3 a^-2
// a' a^-2 = 1 - m a
// return (b * b - one_over_3max * b * b * b) -
// (a * a - one_over_3max * a * a * a);
fixed one_max_a = fixed(1, 1) - one_over_3max * a;
fixed one_max_b = fixed(1, 1) - one_over_3max * b;
return (b * b * one_max_b) - (a * a * one_max_a);
}
static fixed get_disc_density(SystemBody *primary, fixed discMin, fixed discMax, fixed percentOfPrimaryMass)
{
discMax = std::max(discMax, discMin + fixed(1, 100)); // avoid divide-by-zero
fixed total = mass_from_disk_area(discMin, discMax, discMax);
return primary->GetMassInEarths() * percentOfPrimaryMass / total;
}
static inline bool test_overlap(const fixed &x1, const fixed &x2, const fixed &y1, const fixed &y2)
{
return (x1 >= y1 && x1 <= y2) ||
(x2 >= y1 && x2 <= y2) ||
(y1 >= x1 && y1 <= x2) ||
(y2 >= x1 && y2 <= x2);
}
fixed StarSystemRandomGenerator::CalcBodySatelliteShellDensity(Random &rand, SystemBody *primary, fixed &discMin, fixed &discMax)
{
SystemBody::BodySuperType parentSuperType = primary->GetSuperType();
if (parentSuperType <= SystemBody::SUPERTYPE_STAR) {
if (primary->GetType() == SystemBody::TYPE_GRAVPOINT) {
/* around a binary */
if (primary->HasChildren())
discMin = primary->m_children[0]->m_orbMax * SAFE_DIST_FROM_BINARY;
/* empty gravpoint, should only be encountered while creating custom system */
else
discMin = fixed(1, 1);
} else {
/* correct thing is roche limit, but lets ignore that because
* it depends on body densities and gives some strange results */
discMin = 4 * primary->GetRadiusAsFixed() * AU_SOL_RADIUS;
}
if (primary->GetType() == SystemBody::TYPE_BROWN_DWARF) {
// Increase the minimum radius around brown dwarf stars
discMin = 100 * primary->GetRadiusAsFixed() * AU_SOL_RADIUS;
}
if (primary->GetType() == SystemBody::TYPE_WHITE_DWARF) {
// white dwarfs will have started as stars < 8 solar
// masses or so, so pick discMax according to that
// We give it a larger discMin because it used to be a much larger star
discMin = 1000 * primary->GetRadiusAsFixed() * AU_SOL_RADIUS;
discMax = 100 * rand.NFixed(2); // rand-splitting again
discMax *= fixed::SqrtOf(fixed(1, 2) + fixed(8, 1) * rand.Fixed());
} else {
discMax = 100 * rand.NormFixed().Abs() * fixed::SqrtOf(primary->GetMassAsFixed());
}
// having limited discMin by bin-separation/fake roche, and
// discMax by some relation to star mass, we can now compute
// disc density
fixed discDensity = get_disc_density(primary, discMin, discMax, fixed(1, 100));
// Avoid very small, dense stars creating unnatural amounts of gas giants surrounding
discDensity *= std::min(primary->GetRadiusAsFixed() / primary->GetMassAsFixed(), fixed(1,1));
// NOTE: limits applied here scale the density distribution function so
// that bodies are naturally of low mass at the binary/trinary limit
if ((parentSuperType == SystemBody::SUPERTYPE_STAR) && (primary->m_parent)) {
// limit planets out to 10% distance to star's binary companion
discMax = std::min(discMax, primary->m_orbMin * fixed(1, 10));
}
/* in trinary and quaternary systems don't bump into other pair... */
StarSystem *system = primary->GetStarSystem();
if (system->GetNumStars() >= 3) {
discMax = std::min(discMax, fixed(5, 100) * system->GetRootBody()->GetChildren()[0]->m_orbMin);
}
return discDensity;
} else {
fixed primary_rad = primary->GetRadiusAsFixed() * AU_EARTH_RADIUS;
discMin = 4 * primary_rad;
discMax = fixed(5000, 1);
// use hill radius to find max size of moon system. for stars botch it.
// And use planets orbit around its primary as a scaler to a moon's orbit
// assume satellites only exist max 1/10th of L1 distance
// generated value should be well within precision limits
// NOTE: this is opinionated and serves to limit "useless moons" for
// gameplay purposes instead of fully representing reality
fixedf<48> hillSphereRad = CalcHillRadius(primary) * fixedf<48>(1, 4);
discMax = std::min(discMax, fixed(hillSphereRad));
return get_disc_density(primary, discMin, discMax, fixed(1, 500));
}
}
void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtr<StarSystem::GeneratorAPI> system, SystemBody *primary, Random &rand)
{
PROFILE_SCOPED()
SystemBody::BodySuperType parentSuperType = primary->GetSuperType();
// NOTE: using a consistent seed value here as body shell density should be immutable across multiple invocations
const SystemPath &path = system->GetPath();
Random rng { BODY_SATELLITE_SALT, primary->GetSeed(), uint32_t(path.sectorX), uint32_t(path.sectorY), uint32_t(path.sectorZ), UNIVERSE_SEED };
fixed discMin;
fixed discMax;
fixed discDensity = CalcBodySatelliteShellDensity(rng, primary, discMin, discMax);
if (discMin > discMax || discDensity <= 0)
return; // can't make planets here, outside of Hill radius
// Output("Around %s: Range %f -> %f AU, Density %g\n", primary->GetName().c_str(), discMin.ToDouble(), discMax.ToDouble(), discDensity.ToDouble());