267 lines
8.3 KiB
C#
267 lines
8.3 KiB
C#
namespace Sand.Core;
|
|
|
|
public sealed partial class SandSimulation
|
|
{
|
|
public void Clear()
|
|
{
|
|
Array.Clear(TypeId);
|
|
Array.Clear(Temperature);
|
|
Array.Clear(BurnTime);
|
|
Array.Clear(Burning);
|
|
Array.Clear(SparkTime);
|
|
Array.Clear(Lifetime);
|
|
Array.Clear(_pressureDuration);
|
|
Array.Clear(_cellAge);
|
|
Array.Clear(_integrity);
|
|
Array.Clear(_windFieldX);
|
|
Array.Clear(_windFieldY);
|
|
Array.Clear(_forceFieldX);
|
|
Array.Clear(_forceFieldY);
|
|
Array.Clear(_airPressure);
|
|
Array.Clear(_processedFrame);
|
|
Array.Clear(_explosionFrame);
|
|
Array.Clear(_rgbBuffer);
|
|
Array.Clear(_rgbaBuffer);
|
|
ParticleCount = 0;
|
|
Frame = 0;
|
|
_activeStepToken = 0;
|
|
_boundsDirty = false;
|
|
_staleOccupiedBoundsFrames = 0;
|
|
_hasOccupiedBounds = false;
|
|
_fieldBoundsDirty = false;
|
|
_hasActiveFieldBounds = false;
|
|
_hasDirtyVisualBounds = false;
|
|
_fullVisualDirty = true;
|
|
RefreshSettingsState();
|
|
}
|
|
|
|
public void RefreshSettingsState()
|
|
{
|
|
if (_settings.OuterWall && _idWall != 0)
|
|
{
|
|
for (var x = 0; x < Width; x++)
|
|
{
|
|
ForceSetCell(x, 0, _idWall);
|
|
ForceSetCell(x, Height - 1, _idWall);
|
|
}
|
|
|
|
for (var y = 0; y < Height; y++)
|
|
{
|
|
ForceSetCell(0, y, _idWall);
|
|
ForceSetCell(Width - 1, y, _idWall);
|
|
}
|
|
}
|
|
else if (_idWall != 0)
|
|
{
|
|
ClearBoundaryWalls();
|
|
}
|
|
|
|
RecomputeOccupiedBounds();
|
|
MarkFullVisualDirty();
|
|
}
|
|
|
|
public ushort GetTypeIdAtCell(int x, int y) => TypeId[x, y];
|
|
|
|
public float GetTemperatureAtCell(int x, int y) => Temperature[x, y];
|
|
|
|
public (float X, float Y) GetWindAtCell(int x, int y) => (_windFieldX[x, y], _windFieldY[x, y]);
|
|
|
|
public (float X, float Y) GetForceAtCell(int x, int y) => (_forceFieldX[x, y], _forceFieldY[x, y]);
|
|
|
|
public float GetAirPressureAtCell(int x, int y) => _airPressure[x, y];
|
|
|
|
public void ApplyWindBrushAtPixel(int centerX, int centerY, int brushRadius, float forceX, float forceY)
|
|
{
|
|
ApplyDirectionalBrush(centerX, centerY, brushRadius, forceX, forceY, _windTool, _windFieldX, _windFieldY);
|
|
}
|
|
|
|
public void ApplyAirBrushAtPixel(int centerX, int centerY, int brushRadius, float forceX, float forceY)
|
|
{
|
|
ApplyDirectionalBrush(centerX, centerY, brushRadius, forceX, forceY, _airTool, _windFieldX, _windFieldY, addPressure: true);
|
|
}
|
|
|
|
public void ApplyGravityBrushAtPixel(int centerX, int centerY, int brushRadius, float strength)
|
|
{
|
|
ApplyRadialForceBrushAtPixel(centerX, centerY, brushRadius, MathF.Abs(strength), inward: true, _gravityTool);
|
|
}
|
|
|
|
public void ApplyRepulsorBrushAtPixel(int centerX, int centerY, int brushRadius, float strength)
|
|
{
|
|
ApplyRadialForceBrushAtPixel(centerX, centerY, brushRadius, MathF.Abs(strength), inward: false, _repulsorTool);
|
|
}
|
|
|
|
public void CreateParticleAtPixel(int x, int y, string particleId)
|
|
{
|
|
var gridX = x / ParticleSize;
|
|
var gridY = y / ParticleSize;
|
|
if (!TryNormalizeCoordinate(ref gridX, ref gridY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!_library.TryGetTypeId(particleId.ToLowerInvariant(), out var typeId))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_settings.OuterWall && typeId != _idWall && IsBoundary(gridX, gridY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (TypeId[gridX, gridY] != 0)
|
|
{
|
|
if (typeId == _idSpark)
|
|
{
|
|
var targetId = TypeId[gridX, gridY];
|
|
if (_conductivity[targetId] > 0.5f && SparkTime[gridX, gridY] == 0)
|
|
{
|
|
SparkTime[gridX, gridY] = 4;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
SetCell(gridX, gridY, typeId, markProcessed: false, clearDynamics: true);
|
|
}
|
|
|
|
public void CreateParticleCircle(int centerX, int centerY, int brushRadius, string particleId)
|
|
{
|
|
for (var dx = -brushRadius; dx <= brushRadius; dx++)
|
|
{
|
|
for (var dy = -brushRadius; dy <= brushRadius; dy++)
|
|
{
|
|
if ((dx * dx) + (dy * dy) > brushRadius * brushRadius)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CreateParticleAtPixel(centerX + (dx * ParticleSize), centerY + (dy * ParticleSize), particleId);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void CreateParticlePourAtPixel(int centerX, int centerY, int brushRadius, string particleId, int maxParticles, int seed)
|
|
{
|
|
var gridCenterX = centerX / ParticleSize;
|
|
var gridCenterY = centerY / ParticleSize;
|
|
if (!_library.TryGetTypeId(particleId.ToLowerInvariant(), out var typeId))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var radiusSq = brushRadius * brushRadius;
|
|
var diameter = (brushRadius * 2) + 1;
|
|
var maxCells = diameter * diameter;
|
|
var attempts = Math.Max(maxParticles * 6, maxCells);
|
|
var placed = 0;
|
|
|
|
for (var i = 0; i < attempts && placed < maxParticles; i++)
|
|
{
|
|
var hashX = Hash(gridCenterX + (i * 17), gridCenterY - (i * 31), seed + i);
|
|
var hashY = Hash(gridCenterX - (i * 29), gridCenterY + (i * 13), seed - i);
|
|
var dx = (int)(hashX % (uint)diameter) - brushRadius;
|
|
var dy = (int)(hashY % (uint)diameter) - brushRadius;
|
|
if ((dx * dx) + (dy * dy) > radiusSq)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var x = gridCenterX + dx;
|
|
var y = gridCenterY + dy;
|
|
if (!TryNormalizeCoordinate(ref x, ref y))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (_settings.OuterWall && typeId != _idWall && IsBoundary(x, y))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (TypeId[x, y] != 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
SetCell(x, y, typeId, markProcessed: false, clearDynamics: true);
|
|
placed++;
|
|
}
|
|
}
|
|
|
|
public void ClearParticleCircle(int centerX, int centerY, int brushRadius)
|
|
{
|
|
for (var dx = -brushRadius; dx <= brushRadius; dx++)
|
|
{
|
|
for (var dy = -brushRadius; dy <= brushRadius; dy++)
|
|
{
|
|
if ((dx * dx) + (dy * dy) > brushRadius * brushRadius)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var gridX = (centerX / ParticleSize) + dx;
|
|
var gridY = (centerY / ParticleSize) + dy;
|
|
if (!TryNormalizeCoordinate(ref gridX, ref gridY))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (_settings.OuterWall && IsBoundary(gridX, gridY))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (TypeId[gridX, gridY] != 0)
|
|
{
|
|
ResetCell(gridX, gridY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ClearParticlePourAtPixel(int centerX, int centerY, int brushRadius, int maxParticles, int seed)
|
|
{
|
|
var gridCenterX = centerX / ParticleSize;
|
|
var gridCenterY = centerY / ParticleSize;
|
|
var radiusSq = brushRadius * brushRadius;
|
|
var diameter = (brushRadius * 2) + 1;
|
|
var maxCells = diameter * diameter;
|
|
var attempts = Math.Max(maxParticles * 6, maxCells);
|
|
var cleared = 0;
|
|
|
|
for (var i = 0; i < attempts && cleared < maxParticles; i++)
|
|
{
|
|
var hashX = Hash(gridCenterX + (i * 17), gridCenterY - (i * 31), seed + i);
|
|
var hashY = Hash(gridCenterX - (i * 29), gridCenterY + (i * 13), seed - i);
|
|
var dx = (int)(hashX % (uint)diameter) - brushRadius;
|
|
var dy = (int)(hashY % (uint)diameter) - brushRadius;
|
|
if ((dx * dx) + (dy * dy) > radiusSq)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var x = gridCenterX + dx;
|
|
var y = gridCenterY + dy;
|
|
if (!TryNormalizeCoordinate(ref x, ref y))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (_settings.OuterWall && IsBoundary(x, y))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (TypeId[x, y] == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ResetCell(x, y);
|
|
cleared++;
|
|
}
|
|
}
|
|
|
|
}
|