This commit is contained in:
2024-12-16 04:28:45 +03:00
parent ef922486eb
commit dbe7aebd4f
57 changed files with 895 additions and 374 deletions

View File

@@ -9,6 +9,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DoomDeathmatch\DoomDeathmatch.csproj" />
<ProjectReference Include="..\Engine\Engine.csproj"/>
</ItemGroup>

View File

@@ -37,7 +37,8 @@ const vec3 ConsoleColorVec3[16] = vec3[](
vec3(1.0, 1.0, 1.0) // White
);
vec3 f(vec3 x) {
// RGB to LAB conversion (simplified approximation)
vec3 rgb2lab(vec3 x) {
const float epsilon = 0.008856;
const float k = 903.3;
vec3 fx;
@@ -49,10 +50,8 @@ vec3 f(vec3 x) {
// Perceptually weighted color difference (CIEDE2000-inspired)
float perceptualColorDistance(vec3 color1, vec3 color2) {
// RGB to LAB conversion (simplified approximation)
vec3 lab1 = f(color1);
vec3 lab2 = f(color2);
vec3 lab1 = rgb2lab(color1);
vec3 lab2 = rgb2lab(color2);
// Compute LAB-like color difference with perceptual weighting
float deltaL = lab1.x - lab2.x;
@@ -98,7 +97,6 @@ float interleavedGradientNoise(vec2 pixel) {
}
uniform sampler2D uInputTexture;
uniform vec2 uResolution;
layout (location = 0) in vec2 iUV;
layout (location = 0) out vec4 FragColor;
@@ -114,9 +112,9 @@ void main() {
// Output with high precision color mapping
FragColor = vec4(
luminance, // Red: Perceptual luminance
float(colorIndex) / 15.0, // Green: Normalized color index
0.0, // Blue: Unused
1.0 // Alpha
luminance, // Red: Perceptual luminance
float(colorIndex) / 15.0, // Green: Normalized color index
0.0, // Blue: Unused
1.0 // Alpha
);
}

View File

@@ -34,13 +34,10 @@ public sealed class ConsoleFastOutput : IDisposable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteChar(char parCharacter, int parX, int parY, ConsoleColor parForeground, ConsoleColor parBackground)
{
if (parX < 0 || parX >= _width || parY < 0 || parY >= _height)
return;
var index = parX + parY * _width;
ref var charInfo = ref _buffer[index];
charInfo.Char.UnicodeChar = parCharacter;
charInfo.Attributes = (short)((int)parForeground | ((int)parBackground << 4));
charInfo.Attributes = (short)((ushort)parForeground | ((ushort)parBackground << 4));
}
public void Flush()
@@ -60,7 +57,7 @@ public sealed class ConsoleFastOutput : IDisposable
_width = parWidth;
_height = parHeight;
_buffer = new WindowsFFI.CharInfo[parWidth * parHeight];
_buffer = new WindowsFFI.CharInfo[_width * _height];
_bufferSize = new WindowsFFI.Coord((short)_width, (short)_height);
}

View File

@@ -4,126 +4,98 @@ namespace PresenterConsole;
public class ConsoleInputHandler : IInputHandler
{
private readonly HashSet<ConsoleKey> _currentKeys = [];
private readonly HashSet<ConsoleKey> _previousKeys = [];
private ConsoleModifiers _currentModifiers;
private ConsoleModifiers _previousModifiers;
private readonly bool[] _currentKeys = new bool[256];
private readonly bool[] _previousKeys = new bool[256];
public void Update(double parDeltaTime)
{
// Save previous state
_previousKeys.Clear();
foreach (var key in _currentKeys)
for (var i = 0; i < _currentKeys.Length; i++)
{
_previousKeys.Add(key);
}
_previousModifiers = _currentModifiers;
// Clear current state
_currentKeys.Clear();
_currentModifiers = 0;
// Read keys
while (Console.KeyAvailable)
{
var keyInfo = Console.ReadKey(intercept: true);
_currentKeys.Add(keyInfo.Key);
_currentModifiers |= keyInfo.Modifiers;
_previousKeys[i] = _currentKeys[i];
_currentKeys[i] = (WindowsFFI.GetAsyncKeyState(i) & 0x8000) != 0;
}
}
public bool IsKeyPressed(KeyCode keyCode)
public bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode)
{
if (IsModifierKey(keyCode))
return IsModifierActive(keyCode);
return _currentKeys.Contains(ConvertToConsoleKey(keyCode));
var consoleKey = ConvertToConsoleKey(parKeyboardButtonCode);
return _currentKeys[consoleKey];
}
public bool IsKeyJustPressed(KeyCode keyCode)
public bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode)
{
if (IsModifierKey(keyCode))
return IsModifierActive(keyCode) && !WasModifierActive(keyCode);
var consoleKey = ConvertToConsoleKey(keyCode);
return _currentKeys.Contains(consoleKey) && !_previousKeys.Contains(consoleKey);
var consoleKey = ConvertToConsoleKey(parKeyboardButtonCode);
return _currentKeys[consoleKey] && !_previousKeys[consoleKey];
}
public bool IsKeyRepeat(KeyCode keyCode)
public bool IsMouseButtonPressed(MouseButtonCode parButtonCode)
{
if (IsModifierKey(keyCode))
return IsModifierActive(keyCode) && WasModifierActive(keyCode);
var consoleKey = ConvertToConsoleKey(keyCode);
return _currentKeys.Contains(consoleKey) && _previousKeys.Contains(consoleKey);
var consoleKey = ConvertToConsoleKey(parButtonCode);
return _currentKeys[consoleKey];
}
public bool IsMouseButtonPressed(MouseButton button)
public bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode)
{
return false;
var consoleKey = ConvertToConsoleKey(parButtonCode);
return _currentKeys[consoleKey] && !_previousKeys[consoleKey];
}
public bool IsMouseButtonJustPressed(MouseButton button)
{
return false;
}
private static int ConvertToConsoleKey(MouseButtonCode parMouseButtonCode) =>
parMouseButtonCode switch
{
MouseButtonCode.Left => 0x01,
MouseButtonCode.Right => 0x02,
MouseButtonCode.Middle => 0x04,
_ => throw new ArgumentOutOfRangeException(nameof(parMouseButtonCode), parMouseButtonCode, null)
};
private static bool IsModifierKey(KeyCode keyCode) => keyCode is KeyCode.Ctrl or KeyCode.Backspace or KeyCode.Shift;
private bool IsModifierActive(KeyCode keyCode) => keyCode switch
{
KeyCode.Ctrl => (_currentModifiers & ConsoleModifiers.Control) != 0,
KeyCode.Alt => (_currentModifiers & ConsoleModifiers.Alt) != 0,
KeyCode.Shift => (_currentModifiers & ConsoleModifiers.Shift) != 0,
_ => false
};
private bool WasModifierActive(KeyCode keyCode) => keyCode switch
{
KeyCode.Ctrl => (_previousModifiers & ConsoleModifiers.Control) != 0,
KeyCode.Alt => (_previousModifiers & ConsoleModifiers.Alt) != 0,
KeyCode.Shift => (_previousModifiers & ConsoleModifiers.Shift) != 0,
_ => false
};
private static ConsoleKey ConvertToConsoleKey(KeyCode keyCode) => keyCode switch
{
KeyCode.A => ConsoleKey.A,
KeyCode.B => ConsoleKey.B,
KeyCode.C => ConsoleKey.C,
KeyCode.D => ConsoleKey.D,
KeyCode.E => ConsoleKey.E,
KeyCode.F => ConsoleKey.F,
KeyCode.G => ConsoleKey.G,
KeyCode.H => ConsoleKey.H,
KeyCode.I => ConsoleKey.I,
KeyCode.J => ConsoleKey.J,
KeyCode.K => ConsoleKey.K,
KeyCode.L => ConsoleKey.L,
KeyCode.M => ConsoleKey.M,
KeyCode.N => ConsoleKey.N,
KeyCode.O => ConsoleKey.O,
KeyCode.P => ConsoleKey.P,
KeyCode.Q => ConsoleKey.Q,
KeyCode.R => ConsoleKey.R,
KeyCode.S => ConsoleKey.S,
KeyCode.T => ConsoleKey.T,
KeyCode.U => ConsoleKey.U,
KeyCode.V => ConsoleKey.V,
KeyCode.W => ConsoleKey.W,
KeyCode.X => ConsoleKey.X,
KeyCode.Y => ConsoleKey.Y,
KeyCode.Z => ConsoleKey.Z,
KeyCode.Enter => ConsoleKey.Enter,
KeyCode.Escape => ConsoleKey.Escape,
KeyCode.Space => ConsoleKey.Spacebar,
KeyCode.Tab => ConsoleKey.Tab,
KeyCode.Backspace => ConsoleKey.Backspace,
KeyCode.Up => ConsoleKey.UpArrow,
KeyCode.Down => ConsoleKey.DownArrow,
KeyCode.Left => ConsoleKey.LeftArrow,
KeyCode.Right => ConsoleKey.RightArrow,
_ => throw new ArgumentOutOfRangeException(nameof(keyCode), $"No mapping defined for {keyCode}")
};
private static int ConvertToConsoleKey(KeyboardButtonCode parKeyboardButtonCode) =>
parKeyboardButtonCode switch
{
KeyboardButtonCode.A => 0x41,
KeyboardButtonCode.B => 0x42,
KeyboardButtonCode.C => 0x43,
KeyboardButtonCode.D => 0x44,
KeyboardButtonCode.E => 0x45,
KeyboardButtonCode.F => 0x46,
KeyboardButtonCode.G => 0x47,
KeyboardButtonCode.H => 0x48,
KeyboardButtonCode.I => 0x49,
KeyboardButtonCode.J => 0x4A,
KeyboardButtonCode.K => 0x4B,
KeyboardButtonCode.L => 0x4C,
KeyboardButtonCode.M => 0x4D,
KeyboardButtonCode.N => 0x4E,
KeyboardButtonCode.O => 0x4F,
KeyboardButtonCode.P => 0x50,
KeyboardButtonCode.Q => 0x51,
KeyboardButtonCode.R => 0x52,
KeyboardButtonCode.S => 0x53,
KeyboardButtonCode.T => 0x54,
KeyboardButtonCode.U => 0x55,
KeyboardButtonCode.V => 0x56,
KeyboardButtonCode.W => 0x57,
KeyboardButtonCode.X => 0x58,
KeyboardButtonCode.Y => 0x59,
KeyboardButtonCode.Z => 0x5A,
KeyboardButtonCode.Enter => 0x0D,
KeyboardButtonCode.Escape => 0x1B,
KeyboardButtonCode.Space => 0x20,
KeyboardButtonCode.Tab => 0x09,
KeyboardButtonCode.Backspace => 0x08,
KeyboardButtonCode.Up => 0x26,
KeyboardButtonCode.Down => 0x28,
KeyboardButtonCode.Left => 0x25,
KeyboardButtonCode.Right => 0x27,
KeyboardButtonCode.Shift => 0x10,
KeyboardButtonCode.Ctrl => 0x11,
KeyboardButtonCode.Alt => 0x12,
KeyboardButtonCode.Delete => 0x2E,
KeyboardButtonCode.Insert => 0x2D,
KeyboardButtonCode.Home => 0x24,
KeyboardButtonCode.End => 0x23,
KeyboardButtonCode.PageUp => 0x21,
KeyboardButtonCode.PageDown => 0x22,
_ => throw new ArgumentOutOfRangeException(nameof(parKeyboardButtonCode), parKeyboardButtonCode, null)
};
}

View File

@@ -2,7 +2,6 @@
using Engine.Graphics;
using Engine.Graphics.Buffer;
using Engine.Graphics.Framebuffer;
using Engine.Graphics.Pixel;
using Engine.Graphics.Shader;
using Engine.Graphics.Texture;
using OpenTK.Graphics.OpenGL;
@@ -14,14 +13,10 @@ namespace PresenterConsole;
public class ConsolePresenter : IPresenter
{
public bool IsExiting => false;
public int Width => Console.WindowWidth;
public int Height => Console.WindowHeight;
public int Width { get; private set; } = 2;
public int Height { get; private set; } = 1;
public event Action<ResizeEventArgs>? Resize;
private int _cachedWidth;
private int _cachedHeight;
private readonly Engine.Engine _engine;
private readonly Framebuffer _framebuffer;
private readonly Engine.Graphics.Shader.Program _asciiProgram;
private Image<AsciiPixel>? _asciiImage;
@@ -31,14 +26,13 @@ public class ConsolePresenter : IPresenter
private readonly VertexArray _vertexArray;
private readonly ConsoleFastOutput _consoleOutput;
private static readonly char[] LIGHTMAP = " .:-+=#%@".Reverse().ToArray();
private static readonly char[] LIGHTMAP = " .,:;=*#%@".Reverse().ToArray();
public ConsolePresenter(Engine.Engine parEngine)
public ConsolePresenter()
{
_engine = parEngine;
_asciiProgram = ProgramLoader.LoadFromSource(Resource.ShaderResource.Ascii);
_framebuffer = Framebuffer.Builder(Width, Height)
_framebuffer = Framebuffer.Builder(Width / 2, Height)
.AddColorAttachment<AsciiPixel>()
.Build();
@@ -63,9 +57,6 @@ public class ConsolePresenter : IPresenter
_framebuffer.Bind();
GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
openglTexture.BindUnit();
_asciiProgram.Bind();
@@ -94,10 +85,10 @@ public class ConsolePresenter : IPresenter
for (var x = 0; x < parImage.Width; x++)
{
var pixel = parImage[y, x];
var lightnessIndex = (int)(pixel.Luminance / 255.0f * (LIGHTMAP.Length - 1));
var colorIndex = (int)(pixel.Color / 255.0f * 15.0f);
_consoleOutput.WriteChar(LIGHTMAP[lightnessIndex], (short)x, (short)y, 0,
(ConsoleColor)colorIndex);
var lightnessIndex = (byte)(pixel.Luminance / 255.0f * (LIGHTMAP.Length - 1));
var colorIndex = (ConsoleColor)(pixel.Color / 255.0f * 15.0f);
_consoleOutput.WriteChar(LIGHTMAP[lightnessIndex], 2 * x, y, 0, colorIndex);
_consoleOutput.WriteChar(LIGHTMAP[lightnessIndex], 2 * x + 1, y, 0, colorIndex);
}
}
@@ -106,14 +97,20 @@ public class ConsolePresenter : IPresenter
public void Update(double parDeltaTime)
{
if (_cachedWidth != Width || _cachedHeight != Height)
}
public void Render()
{
var consoleWidth = Console.WindowWidth;
var consoleHeight = Console.WindowHeight;
if (Width != consoleWidth || Height != consoleHeight)
{
_cachedWidth = Width;
_cachedHeight = Height;
Width = consoleWidth;
Height = consoleHeight;
Resize?.Invoke(new ResizeEventArgs(Width, Height));
Resize?.Invoke(new ResizeEventArgs(Width / 2, Height));
_framebuffer.Resize(Width, Height);
_framebuffer.Resize(Width / 2, Height);
_consoleOutput.Resize(Width, Height);
}
}

View File

@@ -1,6 +1,4 @@
// See https://aka.ms/new-console-template for more information
using Serilog.Events;
using Serilog.Events;
namespace PresenterConsole;
@@ -12,9 +10,12 @@ internal static class Program
.Headless()
.LogToFile(true, "log.txt")
.LogLevel(LogEventLevel.Debug)
.Presenter(parEngine => new ConsolePresenter(parEngine))
.Presenter(_ => new ConsolePresenter())
.InputHandler(_ => new ConsoleInputHandler())
.Build();
DoomDeathmatch.DoomDeathmatch.Initialize(engine);
engine.Run();
}
}

View File

@@ -60,4 +60,11 @@ public static partial class WindowsFFI
Coord parDwBufferCoord,
ref SmallRect parLpWriteRegion
);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "GetKeyboardState")]
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool GetKeyboardState(byte[] parKeyboardState);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "GetAsyncKeyState")]
public static partial short GetAsyncKeyState(int parKeyCode);
}