sandpypi/Sand.ChunkPrototype/ChunkVisualTracker.cs

161 lines
5.5 KiB
C#

namespace Sand.ChunkPrototype;
internal static class ChunkVisualTracker
{
public static long RenderDirtyPages(
byte[] destination,
int worldWidth,
IReadOnlyDictionary<ChunkCoord, ChunkCellPage> cellPages,
IReadOnlyDictionary<ChunkCoord, ChunkFieldPage> fieldPages,
bool enableWindVisuals,
bool enablePressureVisuals)
{
long bytesTouched = 0;
foreach (var (coord, page) in cellPages)
{
if (!page.HasVisualDirty)
{
continue;
}
bytesTouched += RenderPage(destination, worldWidth, coord, page, fieldPages.GetValueOrDefault(coord), enableWindVisuals, enablePressureVisuals);
page.ClearVisualDirty();
}
foreach (var (coord, fieldPage) in fieldPages)
{
if (cellPages.ContainsKey(coord))
{
continue;
}
bytesTouched += RenderFieldOnlyPage(destination, worldWidth, coord, fieldPage, enableWindVisuals, enablePressureVisuals);
}
return bytesTouched;
}
private static long RenderPage(
byte[] destination,
int worldWidth,
ChunkCoord coord,
ChunkCellPage page,
ChunkFieldPage? fieldPage,
bool enableWindVisuals,
bool enablePressureVisuals)
{
long bytesTouched = 0;
for (var localY = page.VisualDirtyMinRow; localY <= page.VisualDirtyMaxRow; localY++)
{
for (var localX = page.VisualDirtyMinCol; localX <= page.VisualDirtyMaxCol; localX++)
{
var particle = page[localX, localY];
var worldX = (coord.X * page.Width) + localX;
var worldY = (coord.Y * page.Height) + localY;
var rgbaIndex = ((worldY * worldWidth) + worldX) * 4;
if (particle.TypeId != 0)
{
destination[rgbaIndex] = particle.R;
destination[rgbaIndex + 1] = particle.G;
destination[rgbaIndex + 2] = particle.B;
destination[rgbaIndex + 3] = 255;
bytesTouched += 4;
continue;
}
var color = ResolveFieldColor(fieldPage, localX, localY, enableWindVisuals, enablePressureVisuals);
destination[rgbaIndex] = color.R;
destination[rgbaIndex + 1] = color.G;
destination[rgbaIndex + 2] = color.B;
destination[rgbaIndex + 3] = 255;
bytesTouched += 4;
}
}
return bytesTouched;
}
private static long RenderFieldOnlyPage(
byte[] destination,
int worldWidth,
ChunkCoord coord,
ChunkFieldPage page,
bool enableWindVisuals,
bool enablePressureVisuals)
{
if (!enableWindVisuals && !enablePressureVisuals)
{
return 0;
}
long bytesTouched = 0;
foreach (var (localX, localY, cell) in page.EnumerateActiveCells())
{
var color = ResolveFieldColor(cell, enableWindVisuals, enablePressureVisuals);
var worldX = (coord.X * page.Width) + localX;
var worldY = (coord.Y * page.Height) + localY;
var rgbaIndex = ((worldY * worldWidth) + worldX) * 4;
destination[rgbaIndex] = color.R;
destination[rgbaIndex + 1] = color.G;
destination[rgbaIndex + 2] = color.B;
destination[rgbaIndex + 3] = 255;
bytesTouched += 4;
}
return bytesTouched;
}
private static (byte R, byte G, byte B) ResolveFieldColor(ChunkFieldPage? fieldPage, int localX, int localY, bool enableWindVisuals, bool enablePressureVisuals)
{
if (fieldPage is null || !fieldPage.TryGetCell(localX, localY, out var cell))
{
return (0, 0, 0);
}
return ResolveFieldColor(cell, enableWindVisuals, enablePressureVisuals);
}
private static (byte R, byte G, byte B) ResolveFieldColor(FieldCellData cell, bool enableWindVisuals, bool enablePressureVisuals)
{
byte r = 0;
byte g = 0;
byte b = 0;
if (enablePressureVisuals)
{
var pressure = MathF.Abs(cell.Pressure);
if (pressure > 0.08f)
{
var tint = (byte)Math.Clamp((int)(pressure * 32f), 10, 140);
if (cell.Pressure >= 0f)
{
r = (byte)Math.Max((int)r, 26 + tint);
g = (byte)Math.Max((int)g, 18 + (tint / 3));
b = (byte)Math.Max((int)b, 24);
}
else
{
r = (byte)Math.Max((int)r, 18);
g = (byte)Math.Max((int)g, 24 + (tint / 4));
b = (byte)Math.Max((int)b, 30 + tint);
}
}
}
if (enableWindVisuals)
{
var totalFieldX = cell.WindX + cell.ForceX;
var totalFieldY = cell.WindY + cell.ForceY;
var windStrength = MathF.Sqrt((totalFieldX * totalFieldX) + (totalFieldY * totalFieldY));
if (windStrength > 0.08f)
{
var tint = (byte)Math.Clamp((int)(windStrength * 40f), 10, 120);
r = (byte)Math.Max((int)r, 16 + (tint / 3));
g = (byte)Math.Max((int)g, 24 + (tint / 2));
b = (byte)Math.Max((int)b, 32 + tint);
}
}
return (r, g, b);
}
}