.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DoomDeathmatch\DoomDeathmatch.csproj" />
|
||||
<ProjectReference Include="..\Engine\Engine.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user