348 lines
17 KiB
C#
348 lines
17 KiB
C#
namespace Sand.Core;
|
|
|
|
public sealed partial class SandSimulation
|
|
{
|
|
private readonly ParticleLibrary _library;
|
|
private readonly SimulationSettings _settings;
|
|
private readonly byte[] _rgbBuffer;
|
|
private readonly byte[] _rgbaBuffer;
|
|
private readonly byte[] _kind;
|
|
private readonly byte[] _isStatic;
|
|
private readonly float[] _mass;
|
|
private readonly float[] _hardness;
|
|
private readonly byte[] _isMolten;
|
|
private readonly float[] _velocity;
|
|
private readonly float[] _conductivity;
|
|
private readonly byte[] _conductive;
|
|
private readonly float[] _flamability;
|
|
private readonly float[] _durability;
|
|
private readonly float[] _heatCapacity;
|
|
private readonly float[] _friction;
|
|
private readonly float[] _viscosity;
|
|
private readonly float[] _pressure;
|
|
private readonly byte[] _burningInit;
|
|
private readonly float[] _burnDuration;
|
|
private readonly float[] _burnTemperature;
|
|
private readonly float[] _burnRate;
|
|
private readonly float[] _initialTemperature;
|
|
private readonly ushort[] _evapTarget;
|
|
private readonly float[] _evapTemperature;
|
|
private readonly ushort[] _meltTarget;
|
|
private readonly float[] _meltTemperature;
|
|
private readonly ushort[] _solidifyTarget;
|
|
private readonly float[] _solidifyTemperature;
|
|
private readonly ushort[] _freezeTarget;
|
|
private readonly float[] _freezeTemperature;
|
|
private readonly float[] _defaultLifetime;
|
|
private readonly ushort[] _producesTarget;
|
|
private readonly ushort[] _producesOnDeathTarget;
|
|
private readonly float[] _heatEmission;
|
|
private readonly float[] _energyTransfer;
|
|
private readonly float[] _radius;
|
|
private readonly float[] _forceFalloff;
|
|
private readonly float[] _turbulence;
|
|
private readonly float[] _pressureResistance;
|
|
private readonly float[] _pressureTolerance;
|
|
private readonly float[] _pressureThreshold;
|
|
private readonly short[] _pressureThresholdDuration;
|
|
private readonly ushort[] _brokenTarget;
|
|
private readonly byte[] _explosive;
|
|
private readonly short[] _explosionRadius;
|
|
private readonly float[] _explosionForce;
|
|
private readonly Rgb24?[] _explosionColor;
|
|
private readonly byte[] _behaviorKind;
|
|
private readonly float[] _lifetimeMultiplier;
|
|
private readonly float[] _burnDecayPerStep;
|
|
private readonly float[] _ambientCoolingMultiplier;
|
|
private readonly float[] _neighborHeatTransferMultiplier;
|
|
private readonly float[] _upwardBias;
|
|
private readonly float[] _sideDriftBias;
|
|
private readonly float[] _fireSpreadChance;
|
|
private readonly float[] _smokeSpawnChance;
|
|
private readonly float[] _emberSpawnChance;
|
|
private readonly float[] _produceChance;
|
|
private readonly float[] _heatEmissionMultiplier;
|
|
private readonly float[] _energyTransferMultiplier;
|
|
private readonly float[] _pressureSensitivity;
|
|
private readonly float[] _pressureDecayMultiplier;
|
|
private readonly float[] _forceResponseMultiplier;
|
|
private readonly float[] _lateralFlowMultiplier;
|
|
private readonly float[] _diagonalFlowMultiplier;
|
|
private readonly float[] _phaseTransitionHysteresis;
|
|
private readonly float[] _minLifetimeTicks;
|
|
private readonly float[] _maxLifetimeTicks;
|
|
private readonly Rgb24[] _colorLut;
|
|
private readonly float[,] _windFieldX;
|
|
private readonly float[,] _windFieldY;
|
|
private readonly float[,] _forceFieldX;
|
|
private readonly float[,] _forceFieldY;
|
|
private readonly float[,] _airPressure;
|
|
private readonly float[,] _pressureDuration;
|
|
private readonly float[,] _cellAge;
|
|
private readonly float[,] _integrity;
|
|
private readonly ToolProfile _windTool;
|
|
private readonly ToolProfile _gravityTool;
|
|
private readonly ToolProfile _repulsorTool;
|
|
private readonly ToolProfile _airTool;
|
|
private readonly ushort _idFire;
|
|
private readonly ushort _idLava;
|
|
private readonly ushort _idWater;
|
|
private readonly ushort _idSand;
|
|
private readonly ushort _idWetSand;
|
|
private readonly ushort _idDirt;
|
|
private readonly ushort _idMud;
|
|
private readonly ushort _idStone;
|
|
private readonly ushort _idSteam;
|
|
private readonly ushort _idAcid;
|
|
private readonly ushort _idSpark;
|
|
private readonly ushort _idEnergy;
|
|
private readonly ushort _idWall;
|
|
private readonly ushort _idSmoke;
|
|
private readonly ushort _idGlass;
|
|
private readonly ushort _idBurningWood;
|
|
private readonly ushort _idEmber;
|
|
private readonly ushort _idPlasma;
|
|
private readonly ushort _idSnow;
|
|
private readonly ushort _idIce;
|
|
private readonly int[,] _processedFrame;
|
|
private readonly int[,] _explosionFrame;
|
|
private int _activeStepToken;
|
|
private bool _boundsDirty;
|
|
private int _staleOccupiedBoundsFrames;
|
|
private bool _hasOccupiedBounds;
|
|
private int _minOccupiedX;
|
|
private int _minOccupiedY;
|
|
private int _maxOccupiedX;
|
|
private int _maxOccupiedY;
|
|
private bool _fieldBoundsDirty;
|
|
private bool _hasActiveFieldBounds;
|
|
private int _minActiveFieldX;
|
|
private int _minActiveFieldY;
|
|
private int _maxActiveFieldX;
|
|
private int _maxActiveFieldY;
|
|
private bool _hasDirtyVisualBounds;
|
|
private bool _fullVisualDirty = true;
|
|
private int _minDirtyVisualX;
|
|
private int _minDirtyVisualY;
|
|
private int _maxDirtyVisualX;
|
|
private int _maxDirtyVisualY;
|
|
private bool _deferVisualDirtyTracking;
|
|
private uint _visualSettingsStamp = uint.MaxValue;
|
|
|
|
public SandSimulation(int width, int height, int particleSize, IParticleLibrary library, SimulationSettings? settings = null)
|
|
{
|
|
Width = width;
|
|
Height = height;
|
|
ParticleSize = particleSize;
|
|
_library = library as ParticleLibrary ?? throw new ArgumentException("Expected ParticleLibrary implementation.", nameof(library));
|
|
_settings = settings ?? new SimulationSettings();
|
|
|
|
TypeId = new ushort[width, height];
|
|
Temperature = new float[width, height];
|
|
BurnTime = new float[width, height];
|
|
Burning = new byte[width, height];
|
|
SparkTime = new short[width, height];
|
|
Lifetime = new float[width, height];
|
|
_pressureDuration = new float[width, height];
|
|
_cellAge = new float[width, height];
|
|
_windFieldX = new float[width, height];
|
|
_windFieldY = new float[width, height];
|
|
_forceFieldX = new float[width, height];
|
|
_forceFieldY = new float[width, height];
|
|
_airPressure = new float[width, height];
|
|
_processedFrame = new int[width, height];
|
|
_explosionFrame = new int[width, height];
|
|
_integrity = new float[width, height];
|
|
_rgbBuffer = new byte[width * height * 3];
|
|
_rgbaBuffer = new byte[width * height * 4];
|
|
FrameStats = new SimulationFrameStats();
|
|
var count = _library.Definitions.Count + 1;
|
|
_kind = new byte[count];
|
|
_isStatic = new byte[count];
|
|
_mass = new float[count];
|
|
_hardness = new float[count];
|
|
_isMolten = new byte[count];
|
|
_velocity = new float[count];
|
|
_conductivity = new float[count];
|
|
_conductive = new byte[count];
|
|
_flamability = new float[count];
|
|
_durability = new float[count];
|
|
_heatCapacity = new float[count];
|
|
_friction = new float[count];
|
|
_viscosity = new float[count];
|
|
_pressure = new float[count];
|
|
_burningInit = new byte[count];
|
|
_burnDuration = new float[count];
|
|
_burnTemperature = new float[count];
|
|
_burnRate = new float[count];
|
|
_initialTemperature = new float[count];
|
|
_evapTarget = new ushort[count];
|
|
_evapTemperature = Enumerable.Repeat(9999f, count).ToArray();
|
|
_meltTarget = new ushort[count];
|
|
_meltTemperature = Enumerable.Repeat(9999f, count).ToArray();
|
|
_solidifyTarget = new ushort[count];
|
|
_solidifyTemperature = Enumerable.Repeat(-9999f, count).ToArray();
|
|
_freezeTarget = new ushort[count];
|
|
_freezeTemperature = Enumerable.Repeat(-9999f, count).ToArray();
|
|
_defaultLifetime = new float[count];
|
|
_producesTarget = new ushort[count];
|
|
_producesOnDeathTarget = new ushort[count];
|
|
_heatEmission = new float[count];
|
|
_energyTransfer = new float[count];
|
|
_radius = new float[count];
|
|
_forceFalloff = new float[count];
|
|
_turbulence = new float[count];
|
|
_pressureResistance = new float[count];
|
|
_pressureTolerance = new float[count];
|
|
_pressureThreshold = new float[count];
|
|
_pressureThresholdDuration = new short[count];
|
|
_brokenTarget = new ushort[count];
|
|
_explosive = new byte[count];
|
|
_explosionRadius = new short[count];
|
|
_explosionForce = new float[count];
|
|
_explosionColor = new Rgb24?[count];
|
|
_behaviorKind = new byte[count];
|
|
_lifetimeMultiplier = Enumerable.Repeat(1f, count).ToArray();
|
|
_burnDecayPerStep = Enumerable.Repeat(1f, count).ToArray();
|
|
_ambientCoolingMultiplier = Enumerable.Repeat(1f, count).ToArray();
|
|
_neighborHeatTransferMultiplier = Enumerable.Repeat(1f, count).ToArray();
|
|
_upwardBias = new float[count];
|
|
_sideDriftBias = new float[count];
|
|
_fireSpreadChance = new float[count];
|
|
_smokeSpawnChance = new float[count];
|
|
_emberSpawnChance = new float[count];
|
|
_produceChance = new float[count];
|
|
_heatEmissionMultiplier = Enumerable.Repeat(1f, count).ToArray();
|
|
_energyTransferMultiplier = Enumerable.Repeat(1f, count).ToArray();
|
|
_pressureSensitivity = Enumerable.Repeat(1f, count).ToArray();
|
|
_pressureDecayMultiplier = Enumerable.Repeat(1f, count).ToArray();
|
|
_forceResponseMultiplier = Enumerable.Repeat(1f, count).ToArray();
|
|
_lateralFlowMultiplier = Enumerable.Repeat(1f, count).ToArray();
|
|
_diagonalFlowMultiplier = Enumerable.Repeat(1f, count).ToArray();
|
|
_phaseTransitionHysteresis = new float[count];
|
|
_minLifetimeTicks = new float[count];
|
|
_maxLifetimeTicks = new float[count];
|
|
_colorLut = new Rgb24[count];
|
|
|
|
for (ushort typeId = 1; typeId < count; typeId++)
|
|
{
|
|
var definition = _library.GetDefinition(typeId);
|
|
var runtime = ParticleRuntimeProfileBuilder.Build(definition);
|
|
_kind[typeId] = (byte)definition.Kind;
|
|
_isStatic[typeId] = definition.IsStatic ? (byte)1 : (byte)0;
|
|
_mass[typeId] = definition.Mass;
|
|
_hardness[typeId] = MathF.Max(0f, definition.Hardness);
|
|
_isMolten[typeId] = definition.Id == "lava" || definition.Id.StartsWith("molten_", StringComparison.Ordinal) ? (byte)1 : (byte)0;
|
|
_velocity[typeId] = definition.Velocity;
|
|
_conductivity[typeId] = definition.Conductivity;
|
|
_conductive[typeId] = definition.Conductive ? (byte)1 : (byte)0;
|
|
_flamability[typeId] = definition.Flamability;
|
|
_durability[typeId] = MathF.Max(1f, definition.Durability);
|
|
_heatCapacity[typeId] = MathF.Max(0.1f, definition.HeatCapacity);
|
|
_friction[typeId] = definition.Friction;
|
|
_viscosity[typeId] = definition.Viscosity;
|
|
_pressure[typeId] = definition.Pressure;
|
|
_burningInit[typeId] = definition.Burning ? (byte)1 : (byte)0;
|
|
_burnDuration[typeId] = definition.BurnDuration;
|
|
_burnTemperature[typeId] = definition.BurnTemperature;
|
|
_burnRate[typeId] = MathF.Max(0.05f, definition.BurnRate);
|
|
_initialTemperature[typeId] = definition.Temperature;
|
|
_evapTarget[typeId] = ResolveOptionalTypeId(definition.Evaporate);
|
|
_meltTarget[typeId] = ResolveOptionalTypeId(definition.Melt);
|
|
_solidifyTarget[typeId] = ResolveOptionalTypeId(definition.Solidify);
|
|
_freezeTarget[typeId] = ResolveOptionalTypeId(definition.Freeze);
|
|
_producesTarget[typeId] = ResolveOptionalTypeId(definition.Produces);
|
|
_producesOnDeathTarget[typeId] = ResolveOptionalTypeId(definition.ProducesOnDeath);
|
|
_heatEmission[typeId] = definition.HeatEmission;
|
|
_energyTransfer[typeId] = definition.EnergyTransfer;
|
|
_radius[typeId] = definition.Radius;
|
|
_forceFalloff[typeId] = definition.ForceFalloff <= 0f ? 1f : definition.ForceFalloff;
|
|
_turbulence[typeId] = definition.Turbulence;
|
|
_pressureResistance[typeId] = definition.PressureResistance;
|
|
_pressureTolerance[typeId] = definition.PressureTolerance;
|
|
_pressureThreshold[typeId] = definition.PressureThreshold;
|
|
_pressureThresholdDuration[typeId] = checked((short)Math.Clamp(definition.PressureThresholdDuration, 0, short.MaxValue));
|
|
_brokenTarget[typeId] = ResolveOptionalTypeId(definition.Broken);
|
|
_explosive[typeId] = definition.Explosive ? (byte)1 : (byte)0;
|
|
_explosionRadius[typeId] = checked((short)Math.Clamp(definition.ExplosionRadius, 0, short.MaxValue));
|
|
_explosionForce[typeId] = definition.ExplosionForce;
|
|
_explosionColor[typeId] = definition.ExplosionColor;
|
|
if (definition.EvaporateTemperature is not null) _evapTemperature[typeId] = definition.EvaporateTemperature.Value;
|
|
if (definition.MeltTemperature is not null) _meltTemperature[typeId] = definition.MeltTemperature.Value;
|
|
if (definition.SolidifyTemperature is not null) _solidifyTemperature[typeId] = definition.SolidifyTemperature.Value;
|
|
if (definition.FreezeTemperature is not null) _freezeTemperature[typeId] = definition.FreezeTemperature.Value;
|
|
_behaviorKind[typeId] = (byte)runtime.Balance.BehaviorKind;
|
|
_lifetimeMultiplier[typeId] = runtime.Balance.LifetimeMultiplier;
|
|
_burnDecayPerStep[typeId] = runtime.Balance.BurnDecayPerStep;
|
|
_ambientCoolingMultiplier[typeId] = runtime.Balance.AmbientCoolingMultiplier;
|
|
_neighborHeatTransferMultiplier[typeId] = runtime.Balance.NeighborHeatTransferMultiplier;
|
|
_upwardBias[typeId] = runtime.Balance.UpwardBias;
|
|
_sideDriftBias[typeId] = runtime.Balance.SideDriftBias;
|
|
_fireSpreadChance[typeId] = runtime.Balance.FireSpreadChance;
|
|
_smokeSpawnChance[typeId] = runtime.Balance.SmokeSpawnChance;
|
|
_emberSpawnChance[typeId] = runtime.Balance.EmberSpawnChance;
|
|
_produceChance[typeId] = runtime.Balance.ProduceChance;
|
|
_heatEmissionMultiplier[typeId] = runtime.Balance.HeatEmissionMultiplier;
|
|
_energyTransferMultiplier[typeId] = runtime.Balance.EnergyTransferMultiplier;
|
|
_pressureSensitivity[typeId] = runtime.Balance.PressureSensitivity;
|
|
_pressureDecayMultiplier[typeId] = runtime.Balance.PressureDecayMultiplier;
|
|
_forceResponseMultiplier[typeId] = runtime.Balance.ForceResponseMultiplier;
|
|
_lateralFlowMultiplier[typeId] = runtime.Balance.LateralFlowMultiplier;
|
|
_diagonalFlowMultiplier[typeId] = runtime.Balance.DiagonalFlowMultiplier;
|
|
_phaseTransitionHysteresis[typeId] = runtime.Balance.PhaseTransitionHysteresis;
|
|
_minLifetimeTicks[typeId] = runtime.Balance.MinLifetimeTicks;
|
|
_maxLifetimeTicks[typeId] = runtime.Balance.MaxLifetimeTicks;
|
|
_defaultLifetime[typeId] = ResolveDefaultLifetime(definition, runtime.Balance);
|
|
_colorLut[typeId] = definition.Color;
|
|
}
|
|
|
|
_idFire = ResolveOptionalTypeId("fire");
|
|
_idLava = ResolveOptionalTypeId("lava");
|
|
_idWater = ResolveOptionalTypeId("water");
|
|
_idSand = ResolveOptionalTypeId("sand");
|
|
_idWetSand = ResolveOptionalTypeId("wsand");
|
|
_idDirt = ResolveOptionalTypeId("dirt");
|
|
_idMud = ResolveOptionalTypeId("mud");
|
|
_idStone = ResolveOptionalTypeId("stone");
|
|
_idSteam = ResolveOptionalTypeId("steam");
|
|
_idAcid = ResolveOptionalTypeId("acid");
|
|
_idSpark = ResolveOptionalTypeId("spark");
|
|
_idEnergy = ResolveOptionalTypeId("energy");
|
|
_idWall = ResolveOptionalTypeId("wall");
|
|
_idSmoke = ResolveOptionalTypeId("smoke");
|
|
_idGlass = ResolveOptionalTypeId("glass");
|
|
_idBurningWood = ResolveOptionalTypeId("burning_wood");
|
|
_idEmber = ResolveOptionalTypeId("ember");
|
|
_idPlasma = ResolveOptionalTypeId("plasma");
|
|
_idSnow = ResolveOptionalTypeId("snow");
|
|
_idIce = ResolveOptionalTypeId("ice");
|
|
if (_idBurningWood != 0 && _producesOnDeathTarget[_idBurningWood] == 0 && _idEmber != 0)
|
|
{
|
|
_producesOnDeathTarget[_idBurningWood] = _idEmber;
|
|
}
|
|
_windTool = BuildToolProfile("wind", defaultStrength: 4f, fallbackRadiusCells: 4);
|
|
_gravityTool = BuildToolProfile("gravity_well", defaultStrength: 3.5f, fallbackRadiusCells: 6);
|
|
_repulsorTool = BuildToolProfile("repulsor", defaultStrength: 3f, fallbackRadiusCells: 5);
|
|
_airTool = new ToolProfile("air", 4, 3f, 1f, 0.1f, ["all"]);
|
|
|
|
Clear();
|
|
}
|
|
|
|
public int Width { get; }
|
|
public int Height { get; }
|
|
public int ParticleSize { get; }
|
|
public int Frame { get; private set; }
|
|
public int ParticleCount { get; private set; }
|
|
public ushort[,] TypeId { get; }
|
|
public float[,] Temperature { get; }
|
|
public float[,] BurnTime { get; }
|
|
public byte[,] Burning { get; }
|
|
public short[,] SparkTime { get; }
|
|
public float[,] Lifetime { get; }
|
|
public SimulationSettings Settings => _settings;
|
|
public SimulationFrameStats FrameStats { get; }
|
|
public ISimulationAccelerator? Accelerator { get; set; }
|
|
public float GetPressureDurationAtCell(int x, int y) => _pressureDuration[x, y];
|
|
}
|
|
|