Skip to content

Commit d116396

Browse files
committed
Remove particle speed limit, reduce steam effect volume more realistically
1 parent 086d502 commit d116396

File tree

5 files changed

+36
-56
lines changed

5 files changed

+36
-56
lines changed

Source/Documentation/Manual/physics.rst

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2474,7 +2474,6 @@ the one shown below::
24742474
24752475
Comment ( Advanced optional parameters. )
24762476
ORTSMaxParticles ( 2500 )
2477-
ORTSEmissionSpeedLimit ( 150m/s )
24782477
ORTSGraphic ( "smokemain.ace" )
24792478
ORTSGraphicAtlasLayout ( 4 4 )
24802479
)
@@ -2485,7 +2484,6 @@ the one shown below::
24852484
single: ORTSParticleDiameter
24862485
single: ORTSPositionVariation
24872486
single: ORTSInitialVelocityVariation
2488-
single: ORTSEmissionSpeedLimit
24892487
single: ORTSFinalVelocity
24902488
single: ORTSFinalVelocityVariation
24912489
single: ORTSLifespanMultiplier
@@ -2624,13 +2622,6 @@ and poor framerates:
26242622
the particle limit. Do NOT increase the limit if particles are already overlapping; it is often
26252623
better to simply reduce the number of particles emitted using ORTSRateMultiplier and/or other
26262624
parameters listed here.
2627-
- ``ORTSEmissionSpeedLimit ( v )`` -- Some particle emitters are driven by the simulated flow
2628-
rate of exhaust, which has to make some assumptions about the pressure and volume of exhaust.
2629-
This can lead to unrealistically high speed particles being emitted, so a speed limit can be
2630-
set (default units are meters per second) to keep particle speed reasonable. This is not a hard
2631-
limit, it is gradually enforced as the simulation calculates higher and higher exhaust speed.
2632-
The limit is also applied before considering the emitter velocity factor, so actual exhaust speed
2633-
will vary depending on other settings.
26342625
By default, the limit is 150 meters per second, which will be suitable in most cases.
26352626
- ``ORTSGraphic ( "tex" )`` -- Gives the name and path to the texture that should be used to render
26362627
particles from this emitter. The default texture is "smokemain.ace" for steam-type emitters and

Source/Orts.Simulation/Simulation/RollingStocks/MSTSSteamLocomotive.cs

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,9 @@ public class MSTSSteamLocomotive : MSTSLocomotive
396396

397397
#region Additional steam properties
398398
const float SpecificHeatCoalKJpKGpK = 1.26f; // specific heat of coal - kJ/kg/K
399-
const float SteamVaporSpecVolumeAt100DegC1BarM3pKG = 1.696f;
399+
const float SaturatedSteam1BarSpecificVolM3pKG = 1.694f; // specific volume of saturated steam at 1 bar (100 degC) in m^3 / kg
400+
const float SaturatedSteam5BarSpecificVolM3pKG = 0.375f; // specific volume of saturated steam at 5 bar (152 degC) in m^3 / kg
401+
const float SaturatedSteam10BarSpecificVolM3pKG = 0.194f; // specific volume of saturated steam at 10 bar (180 degC) in m^3 / kg
400402
float WaterHeatBTUpFT3; // Water heat in btu/ft3
401403
bool FusiblePlugIsBlown = false; // Fusible plug blown, due to lack of water in the boiler
402404
bool LocoIsOilBurner = false; // Used to identify if loco is oil burner
@@ -3260,35 +3262,35 @@ private void UpdateFX(float elapsedClockSeconds)
32603262
SanderSteamExhaustReverseVelocityMpS = Sander && SandingSystemType == SandingSystemTypes.Steam && Direction == Direction.Reverse ? (1.0f * SteamEffectsFactor) : 0.0f;
32613263
SanderSteamExhaustParticleDurationS = 1.0f;
32623264

3263-
Cylinders1SteamVolumeM3pS = CylinderCock1On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3264-
Cylinders2SteamVolumeM3pS = CylinderCock2On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3265+
Cylinders1SteamVolumeM3pS = CylinderCock1On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
3266+
Cylinders2SteamVolumeM3pS = CylinderCock2On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
32653267

3266-
Cylinders11SteamVolumeM3pS = CylinderCock11On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3267-
Cylinders12SteamVolumeM3pS = CylinderCock12On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3268-
Cylinders21SteamVolumeM3pS = CylinderCock21On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3269-
Cylinders22SteamVolumeM3pS = CylinderCock22On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3270-
Cylinders31SteamVolumeM3pS = CylinderCock31On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3271-
Cylinders32SteamVolumeM3pS = CylinderCock32On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3272-
Cylinders41SteamVolumeM3pS = CylinderCock41On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3273-
Cylinders42SteamVolumeM3pS = CylinderCock42On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3268+
Cylinders11SteamVolumeM3pS = CylinderCock11On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
3269+
Cylinders12SteamVolumeM3pS = CylinderCock12On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
3270+
Cylinders21SteamVolumeM3pS = CylinderCock21On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
3271+
Cylinders22SteamVolumeM3pS = CylinderCock22On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
3272+
Cylinders31SteamVolumeM3pS = CylinderCock31On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
3273+
Cylinders32SteamVolumeM3pS = CylinderCock32On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
3274+
Cylinders41SteamVolumeM3pS = CylinderCock41On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
3275+
Cylinders42SteamVolumeM3pS = CylinderCock42On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
32743276

32753277
Cylinder1ParticleDurationS = 1.0f;
32763278
Cylinder2ParticleDurationS = 1.0f;
32773279

32783280

3279-
CylinderSteamExhaust1SteamVolumeM3pS = throttle > 0.0 && CylinderSteamExhaust1On ? (cutoff * 10.0f * SteamEffectsFactor) : 0.0f;
3280-
CylinderSteamExhaust2SteamVolumeM3pS = throttle > 0.0 && CylinderSteamExhaust2On ? (cutoff * 10.0f * SteamEffectsFactor) : 0.0f;
3281-
CylinderSteamExhaust3SteamVolumeM3pS = throttle > 0.0 && CylinderSteamExhaust3On ? (cutoff * 10.0f * SteamEffectsFactor) : 0.0f;
3282-
CylinderSteamExhaust4SteamVolumeM3pS = throttle > 0.0 && CylinderSteamExhaust4On ? (cutoff * 10.0f * SteamEffectsFactor) : 0.0f;
3281+
CylinderSteamExhaust1SteamVolumeM3pS = throttle > 0.0 && CylinderSteamExhaust1On ? (cutoff * 2.5f * SteamEffectsFactor) : 0.0f;
3282+
CylinderSteamExhaust2SteamVolumeM3pS = throttle > 0.0 && CylinderSteamExhaust2On ? (cutoff * 2.5f * SteamEffectsFactor) : 0.0f;
3283+
CylinderSteamExhaust3SteamVolumeM3pS = throttle > 0.0 && CylinderSteamExhaust3On ? (cutoff * 2.5f * SteamEffectsFactor) : 0.0f;
3284+
CylinderSteamExhaust4SteamVolumeM3pS = throttle > 0.0 && CylinderSteamExhaust4On ? (cutoff * 2.5f * SteamEffectsFactor) : 0.0f;
32833285
CylinderSteamExhaustParticleDurationS = 1.0f;
32843286

3285-
CylinderSteamExhaust2_1SteamVolumeM3pS = throttle > 0.0 && CylinderSteamExhaust2_1On ? (cutoff * 10.0f * SteamEffectsFactor) : 0.0f;
3286-
CylinderSteamExhaust2_2SteamVolumeM3pS = throttle > 0.0 && CylinderSteamExhaust2_2On ? (cutoff * 10.0f * SteamEffectsFactor) : 0.0f;
3287+
CylinderSteamExhaust2_1SteamVolumeM3pS = throttle > 0.0 && CylinderSteamExhaust2_1On ? (cutoff * 2.5f * SteamEffectsFactor) : 0.0f;
3288+
CylinderSteamExhaust2_2SteamVolumeM3pS = throttle > 0.0 && CylinderSteamExhaust2_2On ? (cutoff * 2.5f * SteamEffectsFactor) : 0.0f;
32873289

3288-
Cylinders2_11SteamVolumeM3pS = CylinderCock2_11On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3289-
Cylinders2_12SteamVolumeM3pS = CylinderCock2_12On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3290-
Cylinders2_21SteamVolumeM3pS = CylinderCock2_21On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3291-
Cylinders2_22SteamVolumeM3pS = CylinderCock2_22On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.00f * SteamEffectsFactor) : 0.0f;
3290+
Cylinders2_11SteamVolumeM3pS = CylinderCock2_11On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
3291+
Cylinders2_12SteamVolumeM3pS = CylinderCock2_12On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
3292+
Cylinders2_21SteamVolumeM3pS = CylinderCock2_21On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
3293+
Cylinders2_22SteamVolumeM3pS = CylinderCock2_22On && CylinderCocksAreOpen && throttle > 0.0 && CylCockSteamUsageDisplayLBpS > 0.0 ? (1.0f * SteamEffectsFactor) : 0.0f;
32923294

32933295
// Booster Cylinder Steam Exhausts (automatic)
32943296
BoosterCylinderSteamExhaust01SteamVolumeM3pS = BoosterCylinderSteamExhaustOn && BoosterCylinderSteamExhaust01On ? (1.0f * BoosterSteamFraction) : 0.0f;
@@ -3337,7 +3339,7 @@ private void UpdateFX(float elapsedClockSeconds)
33373339

33383340

33393341
// Blowdown Steam Effects
3340-
BlowdownSteamVolumeM3pS = (BlowdownValveOpen && BlowdownSteamUsageLBpS > 0.0) ? Kg.FromLb(BlowdownSteamUsageLBpS) * SteamVaporSpecVolumeAt100DegC1BarM3pKG : 0;
3342+
BlowdownSteamVolumeM3pS = (BlowdownValveOpen && BlowdownSteamUsageLBpS > 0.0) ? Kg.FromLb(BlowdownSteamUsageLBpS) * SaturatedSteam10BarSpecificVolM3pKG : 0;
33413343
BlowdownParticleDurationS = 2.0f;
33423344

33433345
// Drainpipe Steam Effects
@@ -3376,7 +3378,7 @@ private void UpdateFX(float elapsedClockSeconds)
33763378

33773379
// Safety Valves Steam Effects
33783380

3379-
SafetyValvesSteamVolumeM3pS = SafetyIsOn ? Kg.FromLb(SafetyValveUsageLBpS) * SteamVaporSpecVolumeAt100DegC1BarM3pKG : 0;
3381+
SafetyValvesSteamVolumeM3pS = SafetyIsOn ? Kg.FromLb(SafetyValveUsageLBpS) * SaturatedSteam10BarSpecificVolM3pKG : 0;
33803382
SafetyValvesParticleDurationS = MathHelper.Clamp(3.0f / (AbsSpeedMpS / 4.0f), 0.1f, 3.0f);
33813383

33823384
// Smoke Stack Smoke Effects
@@ -3409,8 +3411,8 @@ private void UpdateFX(float elapsedClockSeconds)
34093411
{
34103412
float smokeVolumeVariationFactor = 1.0f * cutoff; // adjust smoke volume based upon throttle and cutoff settings
34113413

3412-
StackSteamVolumeM3pS = Kg.FromLb(CylinderSteamUsageLBpS + BlowerSteamUsageLBpS + RadiationSteamLossLBpS + CompSteamUsageLBpS + GeneratorSteamUsageLBpS) * smokeVolumeVariationFactor * SteamVaporSpecVolumeAt100DegC1BarM3pKG;
3413-
StackSteamVolumeM3pS = (StackSteamVolumeM3pS) / StackCount;
3414+
StackSteamVolumeM3pS = Kg.FromLb(CylinderSteamUsageLBpS + BlowerSteamUsageLBpS + RadiationSteamLossLBpS + CompSteamUsageLBpS + GeneratorSteamUsageLBpS) *
3415+
smokeVolumeVariationFactor * SaturatedSteam5BarSpecificVolM3pKG;
34143416
StackParticleDurationS = Throttlepercent + FireRatio;
34153417
}
34163418
else // when not exhausting
@@ -3419,8 +3421,8 @@ private void UpdateFX(float elapsedClockSeconds)
34193421
{
34203422
float smokeRestVolumeVariationFactor = 1.0f * cutoff; // adjust smoke volume based upon throttle and cutoff settings
34213423

3422-
StackSteamVolumeM3pS = Kg.FromLb(BlowerSteamUsageLBpS + RadiationSteamLossLBpS + CompSteamUsageLBpS + GeneratorSteamUsageLBpS) * smokeRestVolumeVariationFactor * SteamVaporSpecVolumeAt100DegC1BarM3pKG;
3423-
StackSteamVolumeM3pS = (StackSteamVolumeM3pS) / StackCount;
3424+
StackSteamVolumeM3pS = Kg.FromLb(BlowerSteamUsageLBpS + RadiationSteamLossLBpS + CompSteamUsageLBpS + GeneratorSteamUsageLBpS) *
3425+
smokeRestVolumeVariationFactor * SaturatedSteam5BarSpecificVolM3pKG;
34243426
StackParticleDurationS = Throttlepercent + FireRatio;
34253427
}
34263428
}
@@ -3429,8 +3431,8 @@ private void UpdateFX(float elapsedClockSeconds)
34293431
else // Legacy smoke implementation
34303432
{
34313433
float velocityRate = (float)Math.Sqrt(KPa.FromPSI(SteamReleasePressure_AtmPSI) * 1000 * 2 / WaterDensityAt100DegC1BarKGpM3);
3432-
StackSteamVolumeM3pS = Kg.FromLb(CylinderSteamUsageLBpS + BlowerSteamUsageLBpS + RadiationSteamLossLBpS + CompSteamUsageLBpS + GeneratorSteamUsageLBpS) * SteamVaporSpecVolumeAt100DegC1BarM3pKG;
3433-
StackSteamVolumeM3pS = (StackSteamVolumeM3pS) / StackCount;
3434+
StackSteamVolumeM3pS = Kg.FromLb(CylinderSteamUsageLBpS + BlowerSteamUsageLBpS + RadiationSteamLossLBpS + CompSteamUsageLBpS + GeneratorSteamUsageLBpS) *
3435+
SaturatedSteam5BarSpecificVolM3pKG;
34343436
StackParticleDurationS = Throttlepercent + FireRatio;
34353437
}
34363438

Source/Orts.Simulation/Simulation/RollingStocks/MSTSWagon.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4608,8 +4608,6 @@ public class ParticleEmitterData
46084608
public Vector3 FinalVelocityMpS = Vector3.Up; // Default final velocity is 1 m/s upward
46094609
public Vector3 FinalVelocityVariationMpS = new Vector3(0.75f);
46104610

4611-
public float SpeedLimitMpS = 150.0f;
4612-
46134611
public float NozzleDiameterM = 0.1f;
46144612
public float NozzleAreaM2 = -1; // If left at -1, will be initialized later
46154613

@@ -4704,7 +4702,6 @@ public ParticleEmitterData(STFReader stf)
47044702
FinalVelocityVariationMpS.Z = FinalVelocityVariationMpS.Y = FinalVelocityVariationMpS.X;
47054703
}
47064704
}),
4707-
new STFReader.TokenProcessor("ortsemissionspeedlimit", ()=>{ SpeedLimitMpS = stf.ReadFloatBlock(STFReader.UNITS.Speed, null); }),
47084705
new STFReader.TokenProcessor("ortsparticlediameter", ()=>{ NozzleDiameterM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); }),
47094706
new STFReader.TokenProcessor("ortslifespanmultiplier", ()=>{ LifetimeFactor = stf.ReadFloatBlock(STFReader.UNITS.None, null); }),
47104707
new STFReader.TokenProcessor("ortslifespanvariation", ()=>{ LifetimeVariationFactor = stf.ReadFloatBlock(STFReader.UNITS.None, null); }),

Source/RunActivity/Viewer3D/ParticleEmitter.cs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,6 @@ public void SetOutputRate(float particlespS)
136136
/// <param name="volumeM3pS">The cubic meter volume of particles to emit every second.</param>
137137
public void SetOutputVolumetric(float volumeM3pS)
138138
{
139-
// Volumetric flow rate is assumed to be at atmospheric pressure, but this can give unrealistically fast-moving exhaust
140-
// Limit exhaust speed to the 'speed limit' and calculate how 'compressed' the exhaust still is when emitter
141-
// This calculation gradually increases the 'compression' as calculated speed increases
142-
float factor = (volumeM3pS / EmissionHoleM2 + Emitter.EmitterData.SpeedLimitMpS) / Emitter.EmitterData.SpeedLimitMpS;
143-
144-
Emitter.CompressionFactor = factor;
145-
volumeM3pS /= factor;
146-
147139
Emitter.XNAInitialVelocity = Emitter.EmitterData.InitialVelocityFactor * volumeM3pS / EmissionHoleM2;
148140
Emitter.ParticlesPerSecond = EmitterData.RateFactor * volumeM3pS / ParticleVolumeM3;
149141

@@ -377,8 +369,6 @@ struct ParticleVertex
377369

378370
internal int SpriteCount;
379371

380-
internal float CompressionFactor = 1.0f;
381-
382372
internal WorldPosition WorldPosition;
383373

384374
internal MSTSWagonViewer CarViewer;
@@ -666,11 +656,11 @@ public void Update(float currentTime, ElapsedTime elapsedTime)
666656
finalVelocity.X += ParticleViewer.Viewer.Simulator.Weather.WindInstantaneousSpeedMpS * ParticleViewer.Viewer.Simulator.Weather.WindInstantaneousDirection.X * EmitterData.WindEffect;
667657
finalVelocity.Z += ParticleViewer.Viewer.Simulator.Weather.WindInstantaneousSpeedMpS * ParticleViewer.Viewer.Simulator.Weather.WindInstantaneousDirection.Y * EmitterData.WindEffect;
668658

669-
// Amount by which particles initially expand depends on change in particle speed; faster particles expand more due to 'high pressure' at exhaust
670-
float speedDeltaRatio = (float)Math.Sqrt(initialSpeed * CompressionFactor);
671-
float initialExpansion = speedDeltaRatio * EmitterData.InitialExpansionFactor;
659+
// Amount by which particles initially expand depends on particle speed; faster particles expand more due to 'high pressure' at exhaust
660+
float speedIntensity = (float)Math.Sqrt(initialSpeed);
661+
float initialExpansion = speedIntensity * EmitterData.InitialExpansionFactor;
672662
// Speed at which particles slow down depends on change in particle speed; faster particles slow down faster due to 'drag' from speed difference
673-
settling /= speedDeltaRatio / 5.0f + 1.0f; // Note: The / 5 is largely arbitrary, chosen to give results that look good
663+
settling /= speedIntensity / 5.0f + 1.0f; // Note: The / 5 is largely arbitrary, chosen to give results that look good
674664

675665
for (var j = 0; j < VerticesPerParticle; j++)
676666
{

Source/RunActivity/Viewer3D/RollingStock/MSTSSteamLocomotiveViewer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ public override void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime)
428428
foreach (var drawer in Stack)
429429
{
430430
Color_Value = car.SmokeColor.SmoothedValue;
431-
drawer.SetOutputVolumetric(car.StackSteamVolumeM3pS, car.StackParticleDurationS, new Color(Color_Value, Color_Value, Color_Value));
431+
drawer.SetOutputVolumetric(car.StackSteamVolumeM3pS / car.StackCount, car.StackParticleDurationS, new Color(Color_Value, Color_Value, Color_Value));
432432
}
433433

434434
foreach (var drawer in Whistle)

0 commit comments

Comments
 (0)