180 lines
7.7 KiB
C#
180 lines
7.7 KiB
C#
using Raylib_cs;
|
|
using Sand.Core;
|
|
using System.Numerics;
|
|
using System.Diagnostics;
|
|
|
|
namespace Sand.App;
|
|
|
|
internal static partial class SandApp
|
|
{
|
|
private const int ScreenWidth = 1280;
|
|
private const int ScreenHeight = 800;
|
|
private const int SidebarWidth = 220;
|
|
private const int SettingsWidth = 320;
|
|
private const int ParticleSize = 4;
|
|
|
|
public static void Run()
|
|
{
|
|
var state = CreateState();
|
|
|
|
Raylib.SetConfigFlags(ConfigFlags.ResizableWindow);
|
|
Raylib.InitWindow(ScreenWidth, ScreenHeight, $"Sand - C# Port ({state.BackendName})");
|
|
Raylib.SetTargetFPS(120);
|
|
|
|
var frameImage = Raylib.GenImageColor(state.SimWidth, state.SimHeight, Color.Black);
|
|
state.FrameTexture = Raylib.LoadTextureFromImage(frameImage);
|
|
Raylib.UnloadImage(frameImage);
|
|
|
|
while (!Raylib.WindowShouldClose())
|
|
{
|
|
var frameStart = Stopwatch.GetTimestamp();
|
|
Update(state);
|
|
Draw(state);
|
|
var frameEnd = Stopwatch.GetTimestamp();
|
|
state.LastAppFrameTimeMicroseconds = ToMicroseconds(frameStart, frameEnd);
|
|
state.LastAppOtherTimeMicroseconds = Math.Max(
|
|
0,
|
|
state.LastAppFrameTimeMicroseconds -
|
|
state.LastUpdateTimeMicroseconds -
|
|
state.LastFrameBuildCallTimeMicroseconds -
|
|
state.LastTextureUploadTimeMicroseconds -
|
|
state.LastDrawTimeMicroseconds);
|
|
}
|
|
|
|
Raylib.UnloadTexture(state.FrameTexture);
|
|
Raylib.CloseWindow();
|
|
}
|
|
|
|
private static AppState CreateState()
|
|
{
|
|
var contentRoot = Path.Combine(AppContext.BaseDirectory, "Content", "part");
|
|
var library = ParticleLibraryLoader.LoadFromDirectory(contentRoot);
|
|
var settings = new SimulationSettings
|
|
{
|
|
StorageMode = SimulationStorageMode.Dense,
|
|
PauseSim = false,
|
|
EnableDebug = true,
|
|
EnableFps = true,
|
|
EnableCursor = true,
|
|
EnableGasEffect = true,
|
|
};
|
|
|
|
var simWidth = (ScreenWidth - SidebarWidth) / ParticleSize;
|
|
var simHeight = ScreenHeight / ParticleSize;
|
|
var simulation = CreateSimulationBackend(simWidth, simHeight, library, settings);
|
|
var categories = BuildCategories(library, simulation.BackendName);
|
|
var currentCategory = "Solids";
|
|
|
|
return new AppState
|
|
{
|
|
Library = library,
|
|
Settings = settings,
|
|
Simulation = simulation,
|
|
Categories = categories,
|
|
CurrentCategory = currentCategory,
|
|
CurrentParticle = categories[currentCategory][0].Id,
|
|
BackendName = simulation.BackendName,
|
|
BrushRadius = 3,
|
|
SimWidth = simWidth,
|
|
SimHeight = simHeight,
|
|
UploadBuffer = new byte[simWidth * simHeight * 4],
|
|
LastWindDirection = new Vector2(1f, 0f),
|
|
SettingItems =
|
|
[
|
|
new SettingItem("Cursor", () => settings.EnableCursor, () => settings.EnableCursor = !settings.EnableCursor),
|
|
new SettingItem("Glow", () => settings.EnableGlow, () => settings.EnableGlow = !settings.EnableGlow),
|
|
new SettingItem("Gas FX", () => settings.EnableGasEffect, () => settings.EnableGasEffect = !settings.EnableGasEffect),
|
|
new SettingItem("Wind FX", () => settings.EnableWindVisuals, () => settings.EnableWindVisuals = !settings.EnableWindVisuals),
|
|
new SettingItem("Pressure FX", () => settings.EnablePressureVisuals, () => settings.EnablePressureVisuals = !settings.EnablePressureVisuals),
|
|
new SettingItem("Debug", () => settings.EnableDebug, () => settings.EnableDebug = !settings.EnableDebug),
|
|
new SettingItem("FPS", () => settings.EnableFps, () => settings.EnableFps = !settings.EnableFps),
|
|
new SettingItem("Temp FX", () => settings.EnableTempVisuals, () => settings.EnableTempVisuals = !settings.EnableTempVisuals),
|
|
new SettingItem("Wrap", () => settings.WrapParticles, () => settings.WrapParticles = !settings.WrapParticles),
|
|
new SettingItem("Outer Wall", () => settings.OuterWall, () => settings.OuterWall = !settings.OuterWall),
|
|
],
|
|
};
|
|
}
|
|
|
|
private static ISimulationBackend CreateSimulationBackend(int simWidth, int simHeight, IParticleLibrary library, SimulationSettings settings)
|
|
{
|
|
var backend = Environment.GetEnvironmentVariable("SAND_BACKEND");
|
|
var storageMode = settings.StorageMode;
|
|
if (string.Equals(backend, "chunk", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
storageMode = SimulationStorageMode.ChunkPrototype;
|
|
}
|
|
else if (string.Equals(backend, "dense", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
storageMode = SimulationStorageMode.Dense;
|
|
}
|
|
|
|
if (storageMode == SimulationStorageMode.ChunkPrototype)
|
|
{
|
|
return new ChunkPrototypeSimulationBackend(simWidth, simHeight, ParticleSize, library, settings);
|
|
}
|
|
|
|
return new CoreSimulationBackend(new SandSimulation(simWidth, simHeight, ParticleSize, library, settings));
|
|
}
|
|
|
|
private static Dictionary<string, List<ParticleDef>> BuildCategories(IParticleLibrary library, string backendName)
|
|
{
|
|
if (string.Equals(backendName, "chunk", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return BuildChunkPrototypeCategories(library);
|
|
}
|
|
|
|
var special = library.Definitions.Where(static d => d.IsSpecial).OrderBy(static d => d.Name).ToList();
|
|
if (!special.Any(static d => d.Id == "air"))
|
|
{
|
|
special.Add(new ParticleDef
|
|
{
|
|
Id = "air",
|
|
Name = "Air",
|
|
Kind = ParticleKind.Gas,
|
|
IsSpecial = true,
|
|
Color = new Rgb24(160, 210, 255),
|
|
});
|
|
}
|
|
|
|
special = special.OrderBy(static d => d.Name).ToList();
|
|
|
|
return new Dictionary<string, List<ParticleDef>>
|
|
{
|
|
["Solids"] = library.Definitions.Where(static d => d.Kind == ParticleKind.Solid && !d.IsSpecial).OrderBy(static d => d.Name).ToList(),
|
|
["Liquids"] = library.Definitions.Where(static d => d.Kind == ParticleKind.Liquid).OrderBy(static d => d.Name).ToList(),
|
|
["Gases"] = library.Definitions.Where(static d => d.Kind == ParticleKind.Gas).OrderBy(static d => d.Name).ToList(),
|
|
["Special"] = special,
|
|
};
|
|
}
|
|
|
|
private static Dictionary<string, List<ParticleDef>> BuildChunkPrototypeCategories(IParticleLibrary library)
|
|
{
|
|
var special = library.Definitions.Where(static d => d.IsSpecial).OrderBy(static d => d.Name).ToList();
|
|
if (!special.Any(static d => d.Id == "air"))
|
|
{
|
|
special.Add(new ParticleDef
|
|
{
|
|
Id = "air",
|
|
Name = "Air",
|
|
Kind = ParticleKind.Gas,
|
|
IsSpecial = true,
|
|
Color = new Rgb24(160, 210, 255),
|
|
});
|
|
}
|
|
|
|
special = special.OrderBy(static d => d.Name).ToList();
|
|
return new Dictionary<string, List<ParticleDef>>
|
|
{
|
|
["Solids"] = library.Definitions.Where(static d => d.Kind == ParticleKind.Solid && !d.IsSpecial).OrderBy(static d => d.Name).ToList(),
|
|
["Liquids"] = library.Definitions.Where(static d => d.Kind == ParticleKind.Liquid).OrderBy(static d => d.Name).ToList(),
|
|
["Gases"] = library.Definitions.Where(static d => d.Kind == ParticleKind.Gas).OrderBy(static d => d.Name).ToList(),
|
|
["Special"] = special,
|
|
};
|
|
}
|
|
|
|
private static long ToMicroseconds(long startTimestamp, long endTimestamp)
|
|
{
|
|
return (long)((endTimestamp - startTimestamp) * 1_000_000.0 / Stopwatch.Frequency);
|
|
}
|
|
}
|