Pow3r: stage 1 (#4208)

Co-authored-by: 20kdc <asdd2808@gmail.com>
This commit is contained in:
Pieter-Jan Briers
2021-07-04 18:11:52 +02:00
committed by GitHub
parent ea60a81fdf
commit 103bc19508
212 changed files with 8584 additions and 4426 deletions

16
Pow3r/LinqHelper.cs Normal file
View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
namespace Pow3r
{
public static class LinqHelper
{
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
foreach (var item in enumerable)
{
action(item);
}
}
}
}

21
Pow3r/Pow3r.csproj Normal file
View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ImGui.NET" Version="1.78.0" />
<PackageReference Include="OpenTK" Version="4.5.0" />
<PackageReference Include="Veldrid" Version="4.8.0" />
<PackageReference Include="Veldrid.SPIRV" Version="1.0.14" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Content.Server\Content.Server.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
</ItemGroup>
</Project>

127
Pow3r/Program.OpenGL.cs Normal file
View File

@@ -0,0 +1,127 @@
using System;
using System.Numerics;
using System.Text;
using System.Linq;
using ImGuiNET;
using OpenTK.Graphics.OpenGL;
using OpenTK.Windowing.GraphicsLibraryFramework;
namespace Pow3r
{
internal sealed unsafe partial class Program
{
private int _glFontTexture;
private void InitOpenGL()
{
if (GL.GetString(StringName.Extensions).Split(' ').Contains("GL_ARB_debug_output"))
GL.Arb.DebugMessageCallback(GLDebugCallbackDelegate, (nint) 0x0105);
GL.Enable(EnableCap.ScissorTest);
GL.Enable(EnableCap.Blend);
GL.BlendEquation(BlendEquationMode.FuncAdd);
GL.BlendFuncSeparate(
BlendingFactorSrc.SrcAlpha,
BlendingFactorDest.OneMinusSrcAlpha,
BlendingFactorSrc.One,
BlendingFactorDest.OneMinusSrcAlpha);
var io = ImGui.GetIO();
io.Fonts.GetTexDataAsRGBA32(out byte* pixels, out var width, out var height, out _);
_glFontTexture = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, _glFontTexture);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, PixelFormat.Bgra, PixelType.UnsignedByte, (nint) pixels);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Nearest);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Nearest);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
/*
GL.TextureParameter(_fontTexture, TextureParameterName.TextureSwizzleR, 1);
GL.TextureParameter(_fontTexture, TextureParameterName.TextureSwizzleG, 1);
GL.TextureParameter(_fontTexture, TextureParameterName.TextureSwizzleB, 1);
GL.TextureParameter(_fontTexture, TextureParameterName.TextureSwizzleA, (int) All.Red);*/
io.Fonts.SetTexID((nint) _glFontTexture);
io.Fonts.ClearTexData();
}
private void RenderOpenGL()
{
GLFW.GetFramebufferSize(_window.WindowPtr, out var fbW, out var fbH);
GL.Viewport(0, 0, fbW, fbH);
GL.Disable(EnableCap.ScissorTest);
GL.ClearColor(0, 0, 0, 1);
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Enable(EnableCap.ScissorTest);
GL.Enable(EnableCap.Texture2D);
var drawData = ImGui.GetDrawData();
var l = drawData.DisplayPos.X;
var r = drawData.DisplayPos.X + drawData.DisplaySize.X;
var t = drawData.DisplayPos.Y;
var b = drawData.DisplayPos.Y + drawData.DisplaySize.Y;
var matrix = Matrix4x4.CreateOrthographicOffCenter(l, r, b, t, -1, 1);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix((float*) &matrix);
var clipOff = drawData.DisplayPos;
var clipScale = drawData.FramebufferScale;
GL.EnableClientState(ArrayCap.VertexArray);
GL.EnableClientState(ArrayCap.TextureCoordArray);
GL.EnableClientState(ArrayCap.ColorArray);
for (var n = 0; n < drawData.CmdListsCount; n++)
{
var drawList = drawData.CmdListsRange[n];
for (var cmdI = 0; cmdI < drawList.CmdBuffer.Size; cmdI++)
{
var cmd = drawList.CmdBuffer[cmdI];
GL.BindTexture(TextureTarget.Texture2D, (uint) cmd.TextureId);
Vector4 clipRect = default;
clipRect.X = (cmd.ClipRect.X - clipOff.X) * clipScale.X;
clipRect.Y = (cmd.ClipRect.Y - clipOff.Y) * clipScale.Y;
clipRect.Z = (cmd.ClipRect.Z - clipOff.X) * clipScale.X;
clipRect.W = (cmd.ClipRect.W - clipOff.Y) * clipScale.Y;
GL.Scissor((int) clipRect.X, (int) (fbH - clipRect.W), (int) (clipRect.Z - clipRect.X),
(int) (clipRect.W - clipRect.Y));
IntPtr adjustedVB = drawList.VtxBuffer.Data + (nint) (sizeof(ImDrawVert) * cmd.VtxOffset);
GL.VertexPointer(2, VertexPointerType.Float, sizeof(ImDrawVert), adjustedVB);
GL.TexCoordPointer(2, TexCoordPointerType.Float, sizeof(ImDrawVert), adjustedVB + 8);
GL.ColorPointer(4, ColorPointerType.UnsignedByte, sizeof(ImDrawVert), adjustedVB + 16);
GL.DrawElements(PrimitiveType.Triangles, (int) cmd.ElemCount,
DrawElementsType.UnsignedShort,
drawList.IdxBuffer.Data + (nint) (cmd.IdxOffset * 2));
}
}
_window.SwapBuffers();
}
private static readonly DebugProcArb GLDebugCallbackDelegate = GLDebugCallback;
private static void GLDebugCallback(DebugSource source, DebugType type, int id, DebugSeverity severity,
int length, IntPtr message, IntPtr userParam)
{
var msg = Encoding.UTF8.GetString((byte*) message, length);
if (severity == DebugSeverity.DebugSeverityNotification)
return;
Console.WriteLine($"[{type}][{severity}] {source}: {msg}");
}
}
}

71
Pow3r/Program.SaveLoad.cs Normal file
View File

@@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using Content.Server.Power.Pow3r;
using static Content.Server.Power.Pow3r.PowerState;
namespace Pow3r
{
internal sealed partial class Program
{
private void LoadFromDisk()
{
if (!File.Exists("data.json"))
return;
var dat = JsonSerializer.Deserialize<DiskDat>(File.ReadAllBytes("data.json"), SerializerOptions);
if (dat == null)
return;
_paused = dat.Paused;
_nextId = dat.NextId;
_currentSolver = dat.Solver;
_state = new PowerState
{
Networks = dat.Networks.ToDictionary(n => n.Id, n => n),
Supplies = dat.Supplies.ToDictionary(s => s.Id, s => s),
Loads = dat.Loads.ToDictionary(l => l.Id, l => l),
Batteries = dat.Batteries.ToDictionary(b => b.Id, b => b)
};
_displayLoads = dat.Loads.ToDictionary(n => n.Id, _ => new DisplayLoad());
_displaySupplies = dat.Supplies.ToDictionary(n => n.Id, _ => new DisplaySupply());
_displayBatteries = dat.Batteries.ToDictionary(n => n.Id, _ => new DisplayBattery());
_displayNetworks = dat.Networks.ToDictionary(n => n.Id, _ => new DisplayNetwork());
RefreshLinks();
}
private void SaveToDisk()
{
var data = new DiskDat
{
Paused = _paused,
NextId = _nextId,
Solver = _currentSolver,
Loads = _state.Loads.Values.ToList(),
Batteries = _state.Batteries.Values.ToList(),
Networks = _state.Networks.Values.ToList(),
Supplies = _state.Supplies.Values.ToList()
};
File.WriteAllBytes("data.json", JsonSerializer.SerializeToUtf8Bytes(data, SerializerOptions));
}
private sealed class DiskDat
{
public bool Paused;
public int NextId;
public int Solver;
public List<Load> Loads;
public List<Network> Networks;
public List<Supply> Supplies;
public List<Battery> Batteries;
}
}
}

137
Pow3r/Program.Simulation.cs Normal file
View File

@@ -0,0 +1,137 @@
using System.Collections.Generic;
using System.Diagnostics;
using Content.Server.Power.Pow3r;
using static Content.Server.Power.Pow3r.PowerState;
namespace Pow3r
{
internal sealed partial class Program
{
private const int MaxTickData = 180;
private int _nextId = 1;
private PowerState _state = new();
private Network _linking;
private int _tickDataIdx;
private bool _paused;
private readonly string[] _solverNames =
{
nameof(GraphWalkSolver),
nameof(BatteryRampPegSolver),
nameof(NoOpSolver)
};
private readonly IPowerSolver[] _solvers = {
new GraphWalkSolver(),
new BatteryRampPegSolver(),
new NoOpSolver()
};
private int _currentSolver;
private readonly float[] _simTickTimes = new float[MaxTickData];
private readonly Queue<object> _remQueue = new();
private readonly Stopwatch _simStopwatch = new Stopwatch();
private NodeId AllocId()
{
return new(_nextId++);
}
private void Tick(float frameTime)
{
if (_paused)
return;
RunSingleStep(frameTime);
}
private void RunSingleStep(float frameTime)
{
_simStopwatch.Restart();
_tickDataIdx = (_tickDataIdx + 1) % MaxTickData;
_solvers[_currentSolver].Tick(frameTime, _state);
// Update tick history.
foreach (var load in _state.Loads.Values)
{
var displayLoad = _displayLoads[load.Id];
displayLoad.ReceivedPowerData[_tickDataIdx] = load.ReceivingPower;
}
foreach (var supply in _state.Supplies.Values)
{
var displaySupply = _displaySupplies[supply.Id];
displaySupply.SuppliedPowerData[_tickDataIdx] = supply.CurrentSupply;
}
foreach (var battery in _state.Batteries.Values)
{
var displayBattery = _displayBatteries[battery.Id];
displayBattery.StoredPowerData[_tickDataIdx] = battery.CurrentStorage;
displayBattery.ReceivingPowerData[_tickDataIdx] = battery.CurrentReceiving;
displayBattery.SuppliedPowerData[_tickDataIdx] = battery.CurrentSupply;
}
_simTickTimes[_tickDataIdx] = (float) _simStopwatch.Elapsed.TotalMilliseconds;
}
private void RunSingleStep()
{
RunSingleStep(1f/_tps);
}
// Link data is stored authoritatively on networks,
// but for easy access it is replicated into the linked components.
// This is updated here.
private void RefreshLinks()
{
foreach (var battery in _state.Batteries.Values)
{
battery.LinkedNetworkCharging = default;
battery.LinkedNetworkDischarging = default;
}
foreach (var load in _state.Loads.Values)
{
load.LinkedNetwork = default;
}
foreach (var supply in _state.Supplies.Values)
{
supply.LinkedNetwork = default;
}
foreach (var network in _state.Networks.Values)
{
foreach (var loadId in network.Loads)
{
var load = _state.Loads[loadId];
load.LinkedNetwork = network.Id;
}
foreach (var supplyId in network.Supplies)
{
var supply = _state.Supplies[supplyId];
supply.LinkedNetwork = network.Id;
}
foreach (var batteryId in network.BatteriesCharging)
{
var battery = _state.Batteries[batteryId];
battery.LinkedNetworkCharging = network.Id;
}
foreach (var batteryId in network.BatteriesDischarging)
{
var battery = _state.Batteries[batteryId];
battery.LinkedNetworkDischarging = network.Id;
}
}
}
}
}

459
Pow3r/Program.UI.cs Normal file
View File

@@ -0,0 +1,459 @@
using System;
using System.Collections.Generic;
using ImGuiNET;
using Robust.Shared.Maths;
using static ImGuiNET.ImGui;
using Color = System.Drawing.Color;
using Vector2 = System.Numerics.Vector2;
using RobustVec2 = Robust.Shared.Maths.Vector2;
using static Content.Server.Power.Pow3r.PowerState;
namespace Pow3r
{
internal sealed partial class Program
{
private bool _showDemo;
private Dictionary<NodeId, DisplayLoad> _displayLoads = new();
private Dictionary<NodeId, DisplayBattery> _displayBatteries = new();
private Dictionary<NodeId, DisplayNetwork> _displayNetworks = new();
private Dictionary<NodeId, DisplaySupply> _displaySupplies = new();
private void DoUI(float frameTime)
{
if (BeginMainMenuBar())
{
_showDemo ^= MenuItem("Demo");
EndMainMenuBar();
}
SetNextWindowSize(new Vector2(150, 200));
Begin("CreateButtons",
ImGuiWindowFlags.NoTitleBar |
ImGuiWindowFlags.NoCollapse |
ImGuiWindowFlags.NoResize);
if (Button("Generator"))
{
var id = AllocId();
_state.Supplies.Add(id, new Supply { Id = id });
_displaySupplies.Add(id, new DisplaySupply());
}
if (Button("Load"))
{
var id = AllocId();
_state.Loads.Add(id, new Load { Id = id });
_displayLoads.Add(id, new DisplayLoad());
}
if (Button("Network"))
{
var id = AllocId();
_state.Networks.Add(id, new Network { Id = id });
_displayNetworks.Add(id, new DisplayNetwork());
}
if (Button("Battery"))
{
var id = AllocId();
_state.Batteries.Add(id, new Battery { Id = id });
_displayBatteries.Add(id, new DisplayBattery());
}
Checkbox("Paused", ref _paused);
SliderInt("TPS", ref _tps, 1, 120);
SetNextItemWidth(-1);
Combo("", ref _currentSolver, _solverNames, _solverNames.Length);
if (Button("Single step"))
RunSingleStep();
End();
Begin("Simulating timing");
PlotLines("Tick time (ms)", ref _simTickTimes[0], MaxTickData, _tickDataIdx + 1,
$"{_simTickTimes[_tickDataIdx]:N2}",
0,
0.1f, new Vector2(250, 150));
End();
Begin("Frame timings");
PlotLines("Frame (ms)", ref _frameTimings[0], _frameTimings.Length, _frameTimeIdx + 1,
$"{_frameTimings[_frameTimeIdx]:N2}",
0,
33.333f, new Vector2(250, 150));
End();
{
Begin("Memory");
var heap = GC.GetTotalMemory(false);
Text($"Managed heap: {heap>>20} MiB");
End();
}
foreach (var network in _state.Networks.Values)
{
var displayNetwork = _displayNetworks[network.Id];
Begin($"Network {network.Id}##Gen{network.Id}");
Text($"Height: {network.Height}");
displayNetwork.CurrentWindowPos = CalcWindowCenter();
if (Button("Delete"))
{
_remQueue.Enqueue(network);
if (_linking == network)
{
_linking = null;
}
}
SameLine();
if (_linking != null)
{
if (_linking == network && Button("Cancel"))
{
_linking = null;
}
}
else
{
if (Button("Link..."))
{
_linking = network;
}
}
End();
}
foreach (var load in _state.Loads.Values)
{
var displayLoad = _displayLoads[load.Id];
Begin($"Load {load.Id}##Load{load.Id}");
Checkbox("Enabled", ref load.Enabled);
SliderFloat("Desired", ref load.DesiredPower, 0, 1000, "%.0f W");
displayLoad.CurrentWindowPos = CalcWindowCenter();
PlotLines("", ref displayLoad.ReceivedPowerData[0], MaxTickData, _tickDataIdx + 1,
$"Receiving: {load.ReceivingPower:N1} W",
0,
load.DesiredPower, new Vector2(250, 150));
if (Button("Delete"))
{
_remQueue.Enqueue(load);
}
SameLine();
if (_linking != null)
{
if (Button("Link"))
{
_linking.Loads.Add(load.Id);
_linking = null;
RefreshLinks();
}
}
else
{
if (load.LinkedNetwork != default && Button("Unlink"))
{
var net = _state.Networks[load.LinkedNetwork];
net.Loads.Remove(load.Id);
load.LinkedNetwork = default;
}
}
End();
}
foreach (var supply in _state.Supplies.Values)
{
var displaySupply = _displaySupplies[supply.Id];
Begin($"Generator {supply.Id}##Gen{supply.Id}");
Checkbox("Enabled", ref supply.Enabled);
SliderFloat("Available", ref supply.MaxSupply, 0, 1000, "%.0f W");
SliderFloat("Ramp", ref supply.SupplyRampRate, 0, 100, "%.0f W/s");
SliderFloat("Tolerance", ref supply.SupplyRampTolerance, 0, 100, "%.0f W");
displaySupply.CurrentWindowPos = CalcWindowCenter();
Text($"Ramp Position: {supply.SupplyRampPosition:N1}");
PlotLines("", ref displaySupply.SuppliedPowerData[0], MaxTickData, _tickDataIdx + 1,
$"Supply: {supply.CurrentSupply:N1} W",
0, supply.MaxSupply, new Vector2(250, 150));
if (Button("Delete"))
{
_remQueue.Enqueue(supply);
}
SameLine();
if (_linking != null)
{
if (Button("Link"))
{
_linking.Supplies.Add(supply.Id);
_linking = null;
RefreshLinks();
}
}
else
{
if (supply.LinkedNetwork != default && Button("Unlink"))
{
var net = _state.Networks[supply.LinkedNetwork];
net.Supplies.Remove(supply.Id);
supply.LinkedNetwork = default;
}
}
End();
}
foreach (var battery in _state.Batteries.Values)
{
var displayBattery = _displayBatteries[battery.Id];
Begin($"Battery {battery.Id}##Bat{battery.Id}");
Checkbox("Enabled", ref battery.Enabled);
SliderFloat("Capacity", ref battery.Capacity, 0, 100000, "%.0f J");
SliderFloat("Max charge rate", ref battery.MaxChargeRate, 0, 1000, "%.0f W");
SliderFloat("Max supply", ref battery.MaxSupply, 0, 1000, "%.0f W");
SliderFloat("Ramp", ref battery.SupplyRampRate, 0, 100, "%.0f W/s");
SliderFloat("Tolerance", ref battery.SupplyRampTolerance, 0, 100, "%.0f W");
var percent = 100 * battery.Efficiency;
SliderFloat("Efficiency", ref percent, 0, 100, "%.0f %%");
battery.Efficiency = percent / 100;
displayBattery.CurrentWindowPos = CalcWindowCenter();
SliderFloat("Ramp position", ref battery.SupplyRampPosition, 0, battery.MaxSupply, "%.0f W");
PlotLines("", ref displayBattery.SuppliedPowerData[0], MaxTickData, _tickDataIdx + 1,
$"OUT: {battery.CurrentSupply:N1} W",
0, battery.MaxSupply + 1000, new Vector2(250, 75));
PlotLines("", ref displayBattery.ReceivingPowerData[0], MaxTickData, _tickDataIdx + 1,
$"IN: {battery.CurrentReceiving:N1} W",
0, battery.MaxChargeRate + 1000, new Vector2(250, 75));
PlotLines("", ref displayBattery.StoredPowerData[0], MaxTickData, _tickDataIdx + 1,
$"Charge: {battery.CurrentStorage:N1} J",
0, battery.Capacity, new Vector2(250, 75));
if (Button("Delete"))
{
_remQueue.Enqueue(battery);
}
SameLine();
if (_linking != null)
{
if (battery.LinkedNetworkCharging == default && Button("Link as load"))
{
_linking.BatteriesCharging.Add(battery.Id);
_linking = null;
RefreshLinks();
}
else
{
SameLine();
if (battery.LinkedNetworkDischarging == default && Button("Link as supply"))
{
_linking.BatteriesDischarging.Add(battery.Id);
_linking = null;
RefreshLinks();
}
}
}
else
{
if (battery.LinkedNetworkCharging != default && Button("Unlink loading"))
{
var net = _state.Networks[battery.LinkedNetworkCharging];
net.BatteriesCharging.Remove(battery.Id);
battery.LinkedNetworkCharging = default;
}
else
{
SameLine();
if (battery.LinkedNetworkDischarging != default && Button("Unlink supplying"))
{
var net = _state.Networks[battery.LinkedNetworkDischarging];
net.BatteriesDischarging.Remove(battery.Id);
battery.LinkedNetworkDischarging = default;
}
}
}
if (Button("Empty"))
battery.CurrentStorage = 0;
SameLine();
if (Button("Fill"))
battery.CurrentStorage = battery.Capacity;
End();
}
var bgDrawList = GetBackgroundDrawList();
foreach (var network in _state.Networks.Values)
{
var displayNet = _displayNetworks[network.Id];
foreach (var supplyId in network.Supplies)
{
var supply = _displaySupplies[supplyId];
DrawArrowLine(bgDrawList, displayNet.CurrentWindowPos, supply.CurrentWindowPos, Color.LawnGreen);
}
foreach (var loadId in network.Loads)
{
var load = _displayLoads[loadId];
DrawArrowLine(bgDrawList, load.CurrentWindowPos, displayNet.CurrentWindowPos, Color.Red);
}
foreach (var batteryId in network.BatteriesCharging)
{
var battery = _displayBatteries[batteryId];
DrawArrowLine(bgDrawList, battery.CurrentWindowPos, displayNet.CurrentWindowPos, Color.Purple);
}
foreach (var batteryId in network.BatteriesDischarging)
{
var battery = _displayBatteries[batteryId];
DrawArrowLine(bgDrawList, displayNet.CurrentWindowPos, battery.CurrentWindowPos, Color.Cyan);
}
}
if (_showDemo)
{
ShowDemoWindow();
}
var reLink = false;
while (_remQueue.TryDequeue(out var item))
{
switch (item)
{
case Network n:
_state.Networks.Remove(n.Id);
_displayNetworks.Remove(n.Id);
reLink = true;
break;
case Supply s:
_state.Supplies.Remove(s.Id);
_state.Networks.Values.ForEach(n => n.Supplies.Remove(s.Id));
_displaySupplies.Remove(s.Id);
break;
case Load l:
_state.Loads.Remove(l.Id);
_state.Networks.Values.ForEach(n => n.Loads.Remove(l.Id));
_displayLoads.Remove(l.Id);
break;
case Battery b:
_state.Batteries.Remove(b.Id);
_state.Networks.Values.ForEach(n => n.BatteriesCharging.Remove(b.Id));
_state.Networks.Values.ForEach(n => n.BatteriesDischarging.Remove(b.Id));
_displayBatteries.Remove(b.Id);
break;
}
}
if (reLink)
RefreshLinks();
}
private void DrawArrowLine(ImDrawListPtr ptr, Vector2 a, Vector2 b, Color color)
{
// A: to
// B: from
const float wingLength = 15;
const float thickness = 3;
var cvtColor = CvtColor(color);
ptr.AddLine(a, b, cvtColor, thickness);
var angleA = Angle.FromDegrees(45);
var angleB = Angle.FromDegrees(-45);
var mid = (a + b) / 2;
var dir = -Vector2.Normalize(a - b);
var rVec = new RobustVec2(dir.X, dir.Y);
var wingADir = CvtVec(angleA.RotateVec(rVec));
var wingBDir = CvtVec(angleB.RotateVec(rVec));
var wingA = wingADir * wingLength + mid;
var wingB = wingBDir * wingLength + mid;
ptr.AddLine(mid, wingA, cvtColor, thickness);
ptr.AddLine(mid, wingB, cvtColor, thickness);
}
private static uint CvtColor(Color color)
{
return color.R | ((uint) color.G << 8) | ((uint) color.B << 16) | ((uint) color.A << 24);
}
private static Vector2 CalcWindowCenter()
{
return GetWindowPos() + GetWindowSize() / 2;
}
private static Vector2 CvtVec(RobustVec2 vec)
{
return new Vector2(vec.X, vec.Y);
}
private sealed class DisplayNetwork
{
public Vector2 CurrentWindowPos;
}
private sealed class DisplayBattery
{
public Vector2 CurrentWindowPos;
public readonly float[] ReceivingPowerData = new float[MaxTickData];
public readonly float[] SuppliedPowerData = new float[MaxTickData];
public readonly float[] StoredPowerData = new float[MaxTickData];
}
private sealed class DisplayLoad
{
public Vector2 CurrentWindowPos;
public readonly float[] ReceivedPowerData = new float[MaxTickData];
}
private sealed class DisplaySupply
{
public Vector2 CurrentWindowPos;
public readonly float[] SuppliedPowerData = new float[MaxTickData];
}
}
}

409
Pow3r/Program.Veldrid.cs Normal file
View File

@@ -0,0 +1,409 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using ImGuiNET;
using OpenTK.Windowing.GraphicsLibraryFramework;
using Veldrid;
using Veldrid.OpenGL;
using Veldrid.SPIRV;
using Veldrid.Vk;
namespace Pow3r
{
internal sealed unsafe partial class Program
{
private const string VDVertexShader = @"
#version 460
layout (location = 0) in vec2 Position;
layout (location = 1) in vec2 UV;
layout (location = 2) in vec4 Color;
layout (set = 0, binding = 0) uniform ProjMtx {
mat4 _ProjMtx;
};
layout (location = 0) out vec2 Frag_UV;
layout (location = 1) out vec4 Frag_Color;
// Converts a color from sRGB gamma to linear light gamma
vec4 toLinear(vec4 sRGB)
{
bvec3 cutoff = lessThan(sRGB.rgb, vec3(0.04045));
vec3 higher = pow((sRGB.rgb + vec3(0.055))/vec3(1.055), vec3(2.4));
vec3 lower = sRGB.rgb/vec3(12.92);
return vec4(mix(higher, lower, cutoff), sRGB.a);
}
void main()
{
Frag_UV = UV;
Frag_Color = toLinear(Color);
gl_Position = _ProjMtx * vec4(Position.xy,0,1);
}";
private const string VDFragmentShader = @"
#version 460
layout (location = 0) in vec2 Frag_UV;
layout (location = 1) in vec4 Frag_Color;
layout (set = 1, binding = 0) uniform texture2D Texture;
layout (set = 1, binding = 1) uniform sampler TextureSampler;
layout (location = 0) out vec4 Out_Color;
void main()
{
Out_Color = Frag_Color * texture(sampler2D(Texture, TextureSampler), Frag_UV.st);
}";
private VeldridRenderer _vdRenderer = VeldridRenderer.Vulkan;
private GraphicsDevice _vdGfxDevice;
private CommandList _vdCommandList;
private Pipeline _vdPipeline;
private Shader[] _vdShaders;
private ResourceSet _vdSetTexture;
private ResourceSet _vdSetProjMatrix;
private Texture _vdTexture;
private Sampler _vdSampler;
private DeviceBuffer _vdProjMatrixUniformBuffer;
private int _vdLastWidth;
private int _vdLastHeight;
private VdFencedDatum[] _fencedData = Array.Empty<VdFencedDatum>();
private void InitVeldrid()
{
var options = new GraphicsDeviceOptions
{
#if DEBUG
Debug = true,
#endif
HasMainSwapchain = true,
SyncToVerticalBlank = _vsync,
PreferStandardClipSpaceYDirection = true,
SwapchainSrgbFormat = true
};
GLFW.GetFramebufferSize(_window.WindowPtr, out var w, out var h);
var hwnd = GLFW.GetWin32Window(_window.WindowPtr);
var hinstance = GetModuleHandleA(null);
switch (_vdRenderer)
{
case VeldridRenderer.Vulkan:
_vdGfxDevice = GraphicsDevice.CreateVulkan(
options,
VkSurfaceSource.CreateWin32((nint) hinstance, hwnd),
(uint) w, (uint) h);
break;
case VeldridRenderer.D3D11:
_vdGfxDevice = GraphicsDevice.CreateD3D11(options, hwnd, (uint) w, (uint) h);
break;
case VeldridRenderer.OpenGL:
{
var platInfo = new OpenGLPlatformInfo(
(nint) _window.WindowPtr,
GLFW.GetProcAddress,
ptr => GLFW.MakeContextCurrent((Window*) ptr),
() => (nint) GLFW.GetCurrentContext(),
() => GLFW.MakeContextCurrent(null),
ptr => GLFW.DestroyWindow((Window*) ptr),
() => GLFW.SwapBuffers(_window.WindowPtr),
vsync => GLFW.SwapInterval(vsync ? 1 : 0));
_vdGfxDevice = GraphicsDevice.CreateOpenGL(options, platInfo, (uint) w, (uint) h);
break;
}
}
var factory = _vdGfxDevice.ResourceFactory;
_vdCommandList = factory.CreateCommandList();
_vdCommandList.Name = "Honk";
var vtxLayout = new VertexLayoutDescription(
new VertexElementDescription("Position", VertexElementFormat.Float2,
VertexElementSemantic.TextureCoordinate),
new VertexElementDescription("UV", VertexElementFormat.Float2, VertexElementSemantic.TextureCoordinate),
new VertexElementDescription("Color", VertexElementFormat.Byte4_Norm,
VertexElementSemantic.TextureCoordinate));
var vtxShaderDesc = new ShaderDescription(
ShaderStages.Vertex,
Encoding.UTF8.GetBytes(VDVertexShader),
"main");
var fragShaderDesc = new ShaderDescription(
ShaderStages.Fragment,
Encoding.UTF8.GetBytes(VDFragmentShader),
"main");
_vdShaders = factory.CreateFromSpirv(vtxShaderDesc, fragShaderDesc);
_vdShaders[0].Name = "VertexShader";
_vdShaders[1].Name = "FragmentShader";
var layoutTexture = factory.CreateResourceLayout(new ResourceLayoutDescription(
new ResourceLayoutElementDescription(
"Texture",
ResourceKind.TextureReadOnly,
ShaderStages.Fragment),
new ResourceLayoutElementDescription(
"TextureSampler",
ResourceKind.Sampler,
ShaderStages.Fragment)));
layoutTexture.Name = "LayoutTexture";
var layoutProjMatrix = factory.CreateResourceLayout(new ResourceLayoutDescription(
new ResourceLayoutElementDescription(
"ProjMtx",
ResourceKind.UniformBuffer,
ShaderStages.Vertex)));
layoutProjMatrix.Name = "LayoutProjMatrix";
var pipelineDesc = new GraphicsPipelineDescription(
new BlendStateDescription(
RgbaFloat.White,
new BlendAttachmentDescription(
true,
BlendFactor.SourceAlpha,
BlendFactor.InverseSourceAlpha,
BlendFunction.Add,
BlendFactor.One,
BlendFactor.InverseSourceAlpha,
BlendFunction.Add)
),
DepthStencilStateDescription.Disabled,
new RasterizerStateDescription(
FaceCullMode.None,
PolygonFillMode.Solid,
FrontFace.Clockwise,
depthClipEnabled: false,
scissorTestEnabled: true),
PrimitiveTopology.TriangleList,
new ShaderSetDescription(new[] {vtxLayout}, _vdShaders),
new[] {layoutProjMatrix, layoutTexture},
new OutputDescription(
null,
new OutputAttachmentDescription(PixelFormat.B8_G8_R8_A8_UNorm_SRgb))
);
_vdPipeline = factory.CreateGraphicsPipeline(pipelineDesc);
_vdPipeline.Name = "MainPipeline";
_vdProjMatrixUniformBuffer = factory.CreateBuffer(new BufferDescription(
(uint) sizeof(Matrix4x4),
BufferUsage.Dynamic | BufferUsage.UniformBuffer));
_vdProjMatrixUniformBuffer.Name = "_vdProjMatrixUniformBuffer";
_vdSetProjMatrix = factory.CreateResourceSet(new ResourceSetDescription(
layoutProjMatrix,
_vdProjMatrixUniformBuffer));
_vdSetProjMatrix.Name = "_vdSetProjMatrix";
var io = ImGui.GetIO();
io.Fonts.GetTexDataAsRGBA32(out byte* pixels, out var width, out var height, out _);
_vdTexture = factory.CreateTexture(TextureDescription.Texture2D(
(uint) width, (uint) height,
mipLevels: 1,
arrayLayers: 1,
PixelFormat.R8_G8_B8_A8_UNorm_SRgb,
TextureUsage.Sampled));
_vdTexture.Name = "MainTexture";
_vdSampler = factory.CreateSampler(SamplerDescription.Linear);
_vdSampler.Name = "MainSampler";
_vdGfxDevice.UpdateTexture(
_vdTexture,
(IntPtr) pixels,
(uint) (width * height * 4),
x: 0, y: 0, z: 0,
(uint) width, (uint) height, depth: 1,
mipLevel: 0,
arrayLayer: 0);
_vdSetTexture = factory.CreateResourceSet(new ResourceSetDescription(
layoutTexture,
_vdTexture,
_vdSampler));
_vdSetTexture.Name = "SetTexture";
io.Fonts.SetTexID((nint) 0);
io.Fonts.ClearTexData();
_vdGfxDevice.ResizeMainWindow((uint) w, (uint) h);
_vdGfxDevice.SwapBuffers();
}
private void RenderVeldrid()
{
GLFW.GetFramebufferSize(_window.WindowPtr, out var fbW, out var fbH);
if (_vdLastWidth != fbW && _vdLastHeight != fbH)
{
_vdGfxDevice.ResizeMainWindow((uint) fbW, (uint) fbH);
_vdLastWidth = fbW;
_vdLastHeight = fbH;
}
_vdCommandList.Begin();
_vdCommandList.SetFramebuffer(_vdGfxDevice.SwapchainFramebuffer);
_vdCommandList.SetViewport(0, new Viewport(0, 0, fbW, fbH, 0, 1));
_vdCommandList.ClearColorTarget(0, RgbaFloat.Black);
var factory = _vdGfxDevice.ResourceFactory;
var drawData = ImGui.GetDrawData();
ref var fencedData = ref GetFreeFencedData();
ref var vtxBuf = ref fencedData.VertexBuffer;
ref var idxBuf = ref fencedData.IndexBuffer;
var byteLenVtx = (uint) (sizeof(ImDrawVert) * drawData.TotalVtxCount);
if (fencedData.VertexBuffer == null || vtxBuf.SizeInBytes < byteLenVtx)
{
vtxBuf?.Dispose();
vtxBuf = factory.CreateBuffer(new BufferDescription(
byteLenVtx,
BufferUsage.VertexBuffer | BufferUsage.Dynamic));
vtxBuf.Name = "_vdVtxBuffer";
}
var byteLenIdx = (uint) (sizeof(ushort) * drawData.TotalIdxCount);
if (idxBuf == null || idxBuf.SizeInBytes < byteLenIdx)
{
idxBuf?.Dispose();
idxBuf = factory.CreateBuffer(new BufferDescription(
byteLenIdx,
BufferUsage.IndexBuffer | BufferUsage.Dynamic));
idxBuf.Name = "_vdIdxBuffer";
}
var vtxOffset = 0;
var idxOffset = 0;
var mappedVtxBuf = MappedToSpan(_vdGfxDevice.Map<ImDrawVert>(vtxBuf, MapMode.Write));
var mappedIdxBuf = MappedToSpan(_vdGfxDevice.Map<ushort>(idxBuf, MapMode.Write));
var l = drawData.DisplayPos.X;
var r = drawData.DisplayPos.X + drawData.DisplaySize.X;
var t = drawData.DisplayPos.Y;
var b = drawData.DisplayPos.Y + drawData.DisplaySize.Y;
var matrix = Matrix4x4.CreateOrthographicOffCenter(l, r, b, t, -1, 1);
var clipOff = drawData.DisplayPos;
var clipScale = drawData.FramebufferScale;
_vdCommandList.UpdateBuffer(_vdProjMatrixUniformBuffer, 0, ref matrix);
_vdCommandList.SetPipeline(_vdPipeline);
_vdCommandList.SetGraphicsResourceSet(0, _vdSetProjMatrix);
_vdCommandList.SetGraphicsResourceSet(1, _vdSetTexture);
_vdCommandList.SetVertexBuffer(0, vtxBuf);
_vdCommandList.SetIndexBuffer(idxBuf, IndexFormat.UInt16);
for (var n = 0; n < drawData.CmdListsCount; n++)
{
var drawList = drawData.CmdListsRange[n];
var drawVtx = new Span<ImDrawVert>((void*) drawList.VtxBuffer.Data, drawList.VtxBuffer.Size);
var drawIdx = new Span<ushort>((void*) drawList.IdxBuffer.Data, drawList.IdxBuffer.Size);
drawVtx.CopyTo(mappedVtxBuf[vtxOffset..]);
drawIdx.CopyTo(mappedIdxBuf[idxOffset..]);
for (var cmdI = 0; cmdI < drawList.CmdBuffer.Size; cmdI++)
{
var cmd = drawList.CmdBuffer[cmdI];
Vector4 clipRect = default;
clipRect.X = (cmd.ClipRect.X - clipOff.X) * clipScale.X;
clipRect.Y = (cmd.ClipRect.Y - clipOff.Y) * clipScale.Y;
clipRect.Z = (cmd.ClipRect.Z - clipOff.X) * clipScale.X;
clipRect.W = (cmd.ClipRect.W - clipOff.Y) * clipScale.Y;
_vdCommandList.SetScissorRect(
0,
(uint) clipRect.X,
(uint) clipRect.Y,
(uint) (clipRect.Z - clipRect.X),
(uint) (clipRect.W - clipRect.Y));
_vdCommandList.DrawIndexed(
cmd.ElemCount,
1,
(uint) (cmd.IdxOffset + idxOffset),
(int) (cmd.VtxOffset + vtxOffset),
0);
}
vtxOffset += drawVtx.Length;
idxOffset += drawIdx.Length;
}
_vdGfxDevice.Unmap(vtxBuf);
_vdGfxDevice.Unmap(idxBuf);
_vdCommandList.End();
_vdGfxDevice.SubmitCommands(_vdCommandList, fencedData.Fence);
_vdGfxDevice.SwapBuffers();
}
private ref VdFencedDatum GetFreeFencedData()
{
for (var i = 0; i < _fencedData.Length; i++)
{
ref var fenced = ref _fencedData[i];
if (fenced.Fence.Signaled)
{
fenced.Fence.Reset();
return ref fenced;
}
}
Array.Resize(ref _fencedData, _fencedData.Length + 1);
ref var slot = ref _fencedData[^1];
slot = new VdFencedDatum {Fence = _vdGfxDevice.ResourceFactory.CreateFence(false)};
return ref slot;
}
private static Span<T> MappedToSpan<T>(MappedResourceView<T> mapped) where T : struct
{
return MemoryMarshal.CreateSpan(ref mapped[0], mapped.Count);
}
[DllImport("kernel32.dll")]
private static extern void* GetModuleHandleA(byte* lpModuleName);
private struct VdFencedDatum
{
public Fence Fence;
public DeviceBuffer IndexBuffer;
public DeviceBuffer VertexBuffer;
}
private enum VeldridRenderer
{
Vulkan,
D3D11,
OpenGL
}
}
}

389
Pow3r/Program.cs Normal file
View File

@@ -0,0 +1,389 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using ImGuiNET;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.GraphicsLibraryFramework;
using ErrorCode = OpenTK.Windowing.GraphicsLibraryFramework.ErrorCode;
using Vector2 = System.Numerics.Vector2;
// ReSharper disable PossibleNullReferenceException
namespace Pow3r
{
internal sealed unsafe partial class Program
{
private Renderer _renderer = Renderer.Veldrid;
[UnmanagedCallersOnly]
private static byte* GetClipboardTextCallback(void* userData)
{
return GLFW.GetClipboardStringRaw((Window*) userData);
}
[UnmanagedCallersOnly]
private static void SetClipboardTextCallback(void* userData, byte* text)
{
GLFW.SetClipboardStringRaw((Window*) userData, text);
}
private static readonly GLFWCallbacks.ErrorCallback ErrorCallback = GlfwErrorCallback;
private static void GlfwErrorCallback(ErrorCode error, string description)
{
Console.WriteLine($"{error}: {description}");
}
private bool[] _mouseJustPressed = new bool[5];
private bool _fullscreen;
private int _monitorIdx;
private bool _vsync = true;
private GameWindow _window;
private readonly Stopwatch _stopwatch = new();
private readonly Cursor*[] _cursors = new Cursor*[9];
private readonly float[] _frameTimings = new float[180];
private int _frameTimeIdx = 0;
private int _tps = 60;
private void Run(string[] args)
{
for (var i = 0; i < args.Length; i++)
{
if (args[i] == "--renderer")
{
_renderer = Enum.Parse<Renderer>(args[++i]);
}
else if (args[i] == "--veldrid")
{
_vdRenderer = Enum.Parse<VeldridRenderer>(args[++i]);
}
else if (args[i] == "--fullscreen")
{
_fullscreen = true;
}
else if (args[i] == "--monitor-idx")
{
_monitorIdx = int.Parse(args[++i]);
}
else if (args[i] == "--no-vsync")
{
_vsync = false;
}
else if (args[i] == "--help")
{
Console.WriteLine("--renderer <Veldrid|OpenGL>");
Console.WriteLine("--veldrid <Vulkan|OpenGL|D3D11>");
Console.WriteLine("--no-vsync");
Console.WriteLine("--fullscreen");
Console.WriteLine("--monitor-idx");
Console.WriteLine("--help");
return;
}
else
{
Console.WriteLine($"unknown arg \"{args[i]}\"");
return;
}
}
Console.WriteLine($"Renderer: {_renderer}");
if (_renderer == Renderer.Veldrid)
Console.WriteLine($"Veldrid API: {_vdRenderer}");
Console.WriteLine($"Fullscreen: {_fullscreen}");
Console.WriteLine($"VSync: {_vsync}");
//NativeLibrary.Load("nvapi64.dll");
GLFW.Init();
GLFW.SetErrorCallback(ErrorCallback);
// var sw = Stopwatch.StartNew();
GLFW.WindowHint(WindowHintBool.SrgbCapable, true);
var windowSettings = new NativeWindowSettings
{
Size = (1280, 720),
WindowState = WindowState.Maximized,
StartVisible = false,
Title = "Pow3r"
};
var openGLBased = _renderer == Renderer.OpenGL ||
(_renderer == Renderer.Veldrid && _vdRenderer == VeldridRenderer.OpenGL);
if (openGLBased)
{
windowSettings.API = ContextAPI.OpenGL;
if (_renderer == Renderer.Veldrid)
{
windowSettings.Profile = ContextProfile.Core;
windowSettings.APIVersion = new Version(4, 6);
windowSettings.Flags = ContextFlags.ForwardCompatible;
}
else
{
windowSettings.Profile = ContextProfile.Any;
windowSettings.APIVersion = new Version(1, 5);
}
#if DEBUG
windowSettings.Flags |= ContextFlags.Debug;
#endif
}
else
{
windowSettings.API = ContextAPI.NoAPI;
}
_window = new GameWindow(GameWindowSettings.Default, windowSettings);
// Console.WriteLine(sw.ElapsedMilliseconds);
if (_fullscreen)
{
var monitors = GLFW.GetMonitors();
var monitor = monitors[_monitorIdx];
var monitorMode = GLFW.GetVideoMode(monitor);
GLFW.SetWindowMonitor(
_window.WindowPtr,
monitor,
0, 0,
monitorMode->Width,
monitorMode->Height,
monitorMode->RefreshRate);
}
if (openGLBased)
{
_window.VSync = _vsync ? VSyncMode.On : VSyncMode.Off;
}
var context = ImGui.CreateContext();
ImGui.SetCurrentContext(context);
ImGui.StyleColorsDark();
var io = ImGui.GetIO();
io.Fonts.AddFontDefault();
delegate* unmanaged<void*, byte*> getClipboardCallback = &GetClipboardTextCallback;
io.GetClipboardTextFn = (IntPtr) getClipboardCallback;
delegate* unmanaged<void*, byte*, void> setClipboardCallback = &SetClipboardTextCallback;
io.GetClipboardTextFn = (IntPtr) setClipboardCallback;
io.ClipboardUserData = (IntPtr) _window.WindowPtr;
io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset;
io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos;
io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors;
io.KeyMap[(int) ImGuiKey.Tab] = (int) Keys.Tab;
io.KeyMap[(int) ImGuiKey.LeftArrow] = (int) Keys.Left;
io.KeyMap[(int) ImGuiKey.RightArrow] = (int) Keys.Right;
io.KeyMap[(int) ImGuiKey.UpArrow] = (int) Keys.Up;
io.KeyMap[(int) ImGuiKey.DownArrow] = (int) Keys.Down;
io.KeyMap[(int) ImGuiKey.PageUp] = (int) Keys.PageUp;
io.KeyMap[(int) ImGuiKey.PageDown] = (int) Keys.PageDown;
io.KeyMap[(int) ImGuiKey.Home] = (int) Keys.Home;
io.KeyMap[(int) ImGuiKey.End] = (int) Keys.End;
io.KeyMap[(int) ImGuiKey.Delete] = (int) Keys.Delete;
io.KeyMap[(int) ImGuiKey.Backspace] = (int) Keys.Backspace;
io.KeyMap[(int) ImGuiKey.Enter] = (int) Keys.Enter;
io.KeyMap[(int) ImGuiKey.Escape] = (int) Keys.Escape;
io.KeyMap[(int) ImGuiKey.A] = (int) Keys.A;
io.KeyMap[(int) ImGuiKey.C] = (int) Keys.C;
io.KeyMap[(int) ImGuiKey.V] = (int) Keys.V;
io.KeyMap[(int) ImGuiKey.X] = (int) Keys.X;
io.KeyMap[(int) ImGuiKey.Y] = (int) Keys.Y;
io.KeyMap[(int) ImGuiKey.Z] = (int) Keys.Z;
_cursors[(int) ImGuiMouseCursor.Arrow] = GLFW.CreateStandardCursor(CursorShape.Arrow);
_cursors[(int) ImGuiMouseCursor.TextInput] = GLFW.CreateStandardCursor(CursorShape.IBeam);
_cursors[(int) ImGuiMouseCursor.ResizeNS] = GLFW.CreateStandardCursor(CursorShape.VResize);
_cursors[(int) ImGuiMouseCursor.ResizeEW] = GLFW.CreateStandardCursor(CursorShape.HResize);
_cursors[(int) ImGuiMouseCursor.Hand] = GLFW.CreateStandardCursor(CursorShape.Hand);
_cursors[(int) ImGuiMouseCursor.ResizeAll] = GLFW.CreateStandardCursor(CursorShape.Arrow);
_cursors[(int) ImGuiMouseCursor.ResizeNESW] = GLFW.CreateStandardCursor(CursorShape.Arrow);
_cursors[(int) ImGuiMouseCursor.ResizeNWSE] = GLFW.CreateStandardCursor(CursorShape.Arrow);
_cursors[(int) ImGuiMouseCursor.NotAllowed] = GLFW.CreateStandardCursor(CursorShape.Arrow);
InitRenderer();
_window.MouseDown += OnMouseDown;
_window.TextInput += WindowOnTextInput;
_window.MouseWheel += WindowOnMouseWheel;
_window.KeyDown += args => KeyCallback(args, true);
_window.KeyUp += args => KeyCallback(args, false);
_stopwatch.Start();
LoadFromDisk();
_window.IsVisible = true;
var lastTick = TimeSpan.Zero;
var lastFrame = TimeSpan.Zero;
var curTime = TimeSpan.Zero;
while (!GLFW.WindowShouldClose(_window.WindowPtr))
{
_window.ProcessEvents();
var tickSpan = TimeSpan.FromSeconds(1f / _tps);
while (curTime - lastTick > tickSpan)
{
lastTick += tickSpan;
Tick((float) tickSpan.TotalSeconds);
}
_frameTimeIdx = (_frameTimeIdx + 1) % _frameTimings.Length;
var dt = curTime - lastFrame;
lastFrame = curTime;
_frameTimings[_frameTimeIdx] = (float) dt.TotalMilliseconds;
FrameUpdate((float) dt.TotalSeconds);
Render();
curTime = _stopwatch.Elapsed;
}
SaveToDisk();
}
private static void KeyCallback(KeyboardKeyEventArgs obj, bool down)
{
var io = ImGui.GetIO();
if (obj.Key ==Keys.Unknown)
return;
var keyInt = (int) obj.Key;
io.KeysDown[keyInt] = down;
io.KeyCtrl = io.KeysDown[(int) Keys.LeftControl] || io.KeysDown[(int) Keys.RightControl];
io.KeyShift = io.KeysDown[(int) Keys.LeftShift] || io.KeysDown[(int) Keys.RightShift];
io.KeyAlt = io.KeysDown[(int) Keys.LeftAlt] || io.KeysDown[(int) Keys.RightAlt];
}
private static void WindowOnMouseWheel(MouseWheelEventArgs obj)
{
var io = ImGui.GetIO();
io.MouseWheelH += obj.OffsetX;
io.MouseWheel += obj.OffsetY;
}
private static void WindowOnTextInput(TextInputEventArgs obj)
{
var io = ImGui.GetIO();
io.AddInputCharacter((uint) obj.Unicode);
}
private void OnMouseDown(MouseButtonEventArgs obj)
{
var button = (int) obj.Button;
if (obj.IsPressed && button < _mouseJustPressed.Length)
_mouseJustPressed[button] = true;
}
private void FrameUpdate(float dt)
{
//var sw = Stopwatch.StartNew();
var io = ImGui.GetIO();
GLFW.GetFramebufferSize(_window.WindowPtr, out var fbW, out var fbH);
GLFW.GetWindowSize(_window.WindowPtr, out var wW, out var wH);
io.DisplaySize = new Vector2(wW, wH);
io.DisplayFramebufferScale = new Vector2(fbW / (float) wW, fbH / (float) wH);
io.DeltaTime = dt;
UpdateMouseState(io);
UpdateCursorState(io);
//Console.WriteLine($"INPUT: {sw.Elapsed.TotalMilliseconds}");
ImGui.NewFrame();
DoUI(dt);
}
private void UpdateCursorState(ImGuiIOPtr io)
{
var cursor = ImGui.GetMouseCursor();
if (cursor == ImGuiMouseCursor.None)
{
GLFW.SetInputMode(_window.WindowPtr, CursorStateAttribute.Cursor, CursorModeValue.CursorHidden);
}
else
{
GLFW.SetCursor(_window.WindowPtr, _cursors[(int) cursor]);
GLFW.SetInputMode(_window.WindowPtr, CursorStateAttribute.Cursor, CursorModeValue.CursorNormal);
}
}
private void UpdateMouseState(ImGuiIOPtr io)
{
for (var i = 0; i < io.MouseDown.Count; i++)
{
io.MouseDown[i] = _mouseJustPressed[i] ||
GLFW.GetMouseButton(_window.WindowPtr, (MouseButton) i) == InputAction.Press;
_mouseJustPressed[i] = false;
}
var oldMousePos = io.MousePos;
io.MousePos = new Vector2(float.PositiveInfinity, float.PositiveInfinity);
var focused = _window.IsFocused;
if (focused)
{
if (io.WantSetMousePos)
{
GLFW.SetCursorPos(_window.WindowPtr, oldMousePos.X, oldMousePos.Y);
}
else
{
GLFW.GetCursorPos(_window.WindowPtr, out var x, out var y);
io.MousePos = new Vector2((float) x, (float) y);
}
}
}
private void InitRenderer()
{
switch (_renderer)
{
case Renderer.OpenGL:
InitOpenGL();
break;
case Renderer.Veldrid:
InitVeldrid();
break;
}
}
private void Render()
{
ImGui.Render();
switch (_renderer)
{
case Renderer.OpenGL:
RenderOpenGL();
break;
case Renderer.Veldrid:
RenderVeldrid();
break;
}
}
private static void Main(string[] args)
{
new Program().Run(args);
}
public enum Renderer
{
OpenGL,
Veldrid
}
}
}