|
|
|
using System;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Numerics;
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
using gaemstone.ECS;
|
|
|
|
using gaemstone.Flecs;
|
|
|
|
using Silk.NET.Input;
|
|
|
|
using static gaemstone.Client.Components.InputComponents;
|
|
|
|
using static gaemstone.Client.Systems.Windowing;
|
|
|
|
|
|
|
|
namespace gaemstone.Client.Systems;
|
|
|
|
|
|
|
|
[Module]
|
|
|
|
[DependsOn<gaemstone.Client.Components.InputComponents>]
|
|
|
|
[DependsOn<gaemstone.Client.Systems.Windowing>]
|
|
|
|
public class InputManager
|
|
|
|
{
|
|
|
|
[Component, Path("InputContext"), Proxy<IInputContext>] public struct ContextProxy { }
|
|
|
|
|
|
|
|
[Component, Path("MouseImpl" ), Proxy<IMouse >] public struct MouseProxy { }
|
|
|
|
[Component, Path("KeyboardImpl"), Proxy<IKeyboard>] public struct KeyboardProxy { }
|
|
|
|
[Component, Path("GamepadImpl" ), Proxy<IGamepad >] public struct GamepadProxy { }
|
|
|
|
|
|
|
|
[System<SystemPhase.OnLoad>]
|
|
|
|
public static void Initialize(Universe universe,
|
|
|
|
[Game] GameWindow window, [Source<Input>, Not] IInputContext _)
|
|
|
|
{
|
|
|
|
var input = universe.LookupByTypeOrThrow<Input>();
|
|
|
|
var context = window.Handle.CreateInput();
|
|
|
|
input.Set(context);
|
|
|
|
|
|
|
|
// TODO: Add device names as documentation names to these entities.
|
|
|
|
|
|
|
|
foreach (var impl in context.Mice.Take(1))
|
|
|
|
input.LookupChildOrThrow("Mouse").Set(impl);
|
|
|
|
foreach (var impl in context.Keyboards.Take(1))
|
|
|
|
input.LookupChildOrThrow("Keyboard").Set(impl);
|
|
|
|
foreach (var impl in context.Gamepads)
|
|
|
|
input.NewChild("Gamepad" + impl.Index).Add<Gamepad>().Set(impl).Build();
|
|
|
|
|
|
|
|
// TODO: Should we even support joysticks?
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Observer<ObserverEvent.OnAdd>]
|
|
|
|
[Expression("CursorCapturedBy(Input, *)")]
|
|
|
|
public static void OnCursorCaptured(Universe universe)
|
|
|
|
=> universe.LookupByTypeOrThrow<Mouse>().GetOrThrow<IMouse>()
|
|
|
|
.Cursor.CursorMode = CursorMode.Raw;
|
|
|
|
|
|
|
|
[Observer<ObserverEvent.OnRemove>]
|
|
|
|
[Expression("CursorCapturedBy(Input, *)")]
|
|
|
|
public static void OnCursorReleased(Universe universe)
|
|
|
|
=> universe.LookupByTypeOrThrow<Mouse>().GetOrThrow<IMouse>()
|
|
|
|
.Cursor.CursorMode = CursorMode.Normal;
|
|
|
|
|
|
|
|
|
|
|
|
[System<SystemPhase.OnLoad>]
|
|
|
|
public static void ProcessMouse(TimeSpan delta, EntityRef mouse, IMouse impl)
|
|
|
|
{
|
|
|
|
var isCaptured = mouse.Parent!.Has<CursorCapturedBy, Core.Any>();
|
|
|
|
ref var position = ref mouse.NewChild("Position").Build().GetMut<RawValue2D>();
|
|
|
|
ref var posDelta = ref mouse.NewChild("Delta" ).Build().GetMut<RawValue2D>();
|
|
|
|
posDelta = impl.Position - position;
|
|
|
|
if (isCaptured) impl.Position = position;
|
|
|
|
else position = impl.Position;
|
|
|
|
|
|
|
|
Update1D(delta, mouse.NewChild("Wheel").Build(), impl.ScrollWheels[0].Y);
|
|
|
|
|
|
|
|
var buttons = mouse.NewChild("Buttons").Build();
|
|
|
|
foreach (var button in impl.SupportedButtons)
|
|
|
|
Update1D(delta, buttons.NewChild(button.ToString()).Build(),
|
|
|
|
impl.IsButtonPressed(button) ? 1 : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
[System<SystemPhase.OnLoad>]
|
|
|
|
public static void ProcessKeyboard(TimeSpan delta, EntityRef keyboard, IKeyboard impl)
|
|
|
|
{
|
|
|
|
foreach (var key in impl.SupportedKeys) {
|
|
|
|
var entity = keyboard.NewChild(key.ToString()).Build();
|
|
|
|
Update1D(delta, entity, impl.IsKeyPressed(key) ? 1 : 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[System<SystemPhase.OnLoad>]
|
|
|
|
public static void ProcessGamepad(TimeSpan delta, EntityRef gamepad, IGamepad impl)
|
|
|
|
{
|
|
|
|
var buttons = gamepad.NewChild("Buttons").Build();
|
|
|
|
foreach (var button in impl.Buttons)
|
|
|
|
Update1D(delta, buttons.NewChild(button.Name.ToString()).Build(), button.Pressed ? 1 : 0);
|
|
|
|
foreach (var trigger in impl.Triggers)
|
|
|
|
Update1D(delta, gamepad.NewChild("Trigger" + trigger.Index).Build(), trigger.Position);
|
|
|
|
foreach (var thumbstick in impl.Thumbsticks)
|
|
|
|
Update2D(delta, gamepad.NewChild("Thumbstick" + thumbstick.Index).Build(), new(thumbstick.X, thumbstick.Y));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private const float ActivationThreshold = 0.90f;
|
|
|
|
private const float DeactivationThreshold = 0.75f;
|
|
|
|
|
|
|
|
private static void Update1D(TimeSpan delta, EntityRef entity, float current)
|
|
|
|
{
|
|
|
|
entity.GetMut<RawValue1D>() = current;
|
|
|
|
if (current >= ActivationThreshold) {
|
|
|
|
ref var active = ref entity.GetRefOrNull<Active>();
|
|
|
|
if (Unsafe.IsNullRef(ref active)) {
|
|
|
|
entity.Set(new Active());
|
|
|
|
entity.Add<Activated>();
|
|
|
|
} else active.Duration += delta;
|
|
|
|
} else if (current <= DeactivationThreshold)
|
|
|
|
entity.Remove<Active>();
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void Update2D(TimeSpan delta, EntityRef entity, Vector2 current)
|
|
|
|
{
|
|
|
|
entity.GetMut<RawValue2D>() = current;
|
|
|
|
var magnitude = current.Length();
|
|
|
|
if (magnitude >= ActivationThreshold) {
|
|
|
|
ref var active = ref entity.GetRefOrNull<Active>();
|
|
|
|
if (Unsafe.IsNullRef(ref active)) {
|
|
|
|
entity.Set(new Active());
|
|
|
|
entity.Add<Activated>();
|
|
|
|
} else active.Duration += delta;
|
|
|
|
} else if (magnitude <= DeactivationThreshold)
|
|
|
|
entity.Remove<Active>();
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: flecs calls OnAdd observers repeatedly as the entity has other things added to it.
|
|
|
|
// So at least for now we need to add Activated manually, until this changes.
|
|
|
|
// [Observer<ObserverEvent.OnAdd>]
|
|
|
|
// public static void OnActiveAdded(EntityRef entity, Active _)
|
|
|
|
// => entity.Add<Activated>();
|
|
|
|
|
|
|
|
[Observer<ObserverEvent.OnRemove>]
|
|
|
|
public static void OnActiveRemoved(EntityRef entity, Active _)
|
|
|
|
=> entity.Add<Deactivated>();
|
|
|
|
|
|
|
|
[System<SystemPhase.PostFrame>]
|
|
|
|
public static void ClearDeActivated(EntityRef entity, [Or] Activated _1, [Or] Deactivated _2)
|
|
|
|
=> entity.Remove<Activated>().Remove<Deactivated>();
|
|
|
|
}
|