161 lines
5.5 KiB
C#
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);
|
|
}
|
|
}
|