From 10fc9e312ac8e61a44812c03ceb88ccc9e4044fb Mon Sep 17 00:00:00 2001 From: Dylan Perks Date: Mon, 16 Dec 2024 14:47:08 +0000 Subject: [PATCH] Update proposal to add 'virtual pointers' --- .../Proposal - Multi-Backend Input.md | 85 +++++++++++++------ 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/documentation/proposals/Proposal - Multi-Backend Input.md b/documentation/proposals/Proposal - Multi-Backend Input.md index ad3f56e5ec..dd5f8e8f64 100644 --- a/documentation/proposals/Proposal - Multi-Backend Input.md +++ b/documentation/proposals/Proposal - Multi-Backend Input.md @@ -277,8 +277,9 @@ public readonly record struct ConnectionEvent(IInputDevice Device, long Timestam public readonly record struct KeyChangedEvent(IKeyboard Keyboard, long Timestamp, Button Key, Button Previous, bool IsRepeat, KeyModifiers Modifiers); public readonly record struct KeyCharEvent(IKeyboard Keyboard, long Timestamp, char? Character); public readonly record struct ButtonChangedEvent(IButtonDevice Device, long Timestamp, Button Button, Button Previous) where T : struct, Enum; -public readonly record struct PointChangedEvent(IPointer Pointer, long Timestamp, TargetPoint? OldPoint, TargetPoint Point); +public readonly record struct PointChangedEvent(IPointer Pointer, long Timestamp, TargetPoint? OldPoint, TargetPoint? NewPoint); public readonly record struct PointerGripChangedEvent(IPointer Pointer, long Timestamp, float GripPressure, float Delta); +public readonly record struct PointerTargetChangedEvent(IPointer Pointer, long Timestamp, IPointerTarget Target, bool IsAdded, Box3F OldBounds, Box3F NewBounds); public readonly record struct MouseScrollEvent(IMouse Mouse, long Timestamp, TargetPoint Point, Vector2 WheelPosition, Vector2 Delta); public readonly record struct PointerClickEvent(IPointer Pointer, long Timestamp, TargetPoint Point, MouseButton Button); public readonly record struct JoystickHatMoveEvent(IJoystick Joystick, long Timestamp, Vector2 Value, Vector2 Delta); @@ -372,23 +373,13 @@ A target is defined as follows: public interface IPointerTarget { /// - /// The minimum position of points on this target, where represents the lack - /// of a lower bound on a particular axis and 0 represents an unused axis if is - /// also 0 for that axis. + /// The boundary in which positions of points on this target shall fall. For , + /// shall represent the lack of a lower bound on a particular axis. For + /// For , shall represent the lack of a lower bound + /// on a particular axis. 0 represents an unused axis that axis is 0 on both + /// and . /// - Vector3 MinPosition { get; } - - /// - /// The maximum position of points on this target, where represents the lack - /// of an upper bound on a particular axis and 0 represents an unused axis if is - /// also 0 for that axis. - /// - Vector3 MaxPosition { get; } - - /// - /// An optional name describing the target. - /// - string? Name { get; } + Box3F Bounds { get; } /// /// Gets the number of points with which the given pointer is pointing at this target. @@ -415,6 +406,10 @@ public interface IPointerTarget } ``` +**FUTURE IMPROVEMENT:** This interface could be expanded to provide rotation of the target itself as well, representing +a full `Transform` structure for the space. At this time, this was not deemed necessary for inclusion, but should be a +trivial extension to add in the future. + **INFORMATIVE TEXT**: Furthermore, it is our eventual goal to be able to support considering VR hands as pointer devices through raycasting. Such a future proposal will involve a way to create a child target within the bounds of this target a `IPointerTarget` from that which represents the 3D world (i.e. the entire VR world is a target, and the point @@ -445,6 +440,12 @@ public enum TargetPointFlags /// /// Represents a point on a target at which a pointer is pointing. /// +/// +/// An integral identifier for the point. This point must be the only point for the device currently pointing at a +/// target with this identifier at any given time. If this point ceases to point at the target, then the identifier +/// becomes free for another device point. This means that this identifier can just be an index, but may be globally +/// unique depending on the backend's capabilities. +/// /// Flags describing the state of the point. /// The absolute position on the target at which the pointer is pointing. /// @@ -452,24 +453,26 @@ public enum TargetPointFlags /// (e.g. due to the target being infinitely large a.k.a. "unbounded"), then this property shall have a value of /// default. /// -/// -/// The angle at which the pointer is pointing at the point on the target. An identity quaternion shall be interpreted -/// as the point directly perpendicular to and facing towards the target. This shall carry an identity quaternion if -/// there is no orientation available. +/// +/// A ray representing the distance and angle at which the pointer is pointing at the point on the target. A ray with an +/// orientation equivalent to an identity quaternion shall be interpreted as the point directly perpendicular to and +/// facing towards the target, with this being the default value should this information be unavailable. If distance +/// information is unavailable, this shall be equivalent to a default vector. /// -/// The distance of the pointer from the point the pointer is pointing at. /// /// The pressure applied to the point on the target by the pointer, between 0.0 representing the minimum amount /// of pressure and 1.0 representing the maximum amount of pressure. This shall be 1.0 if such data is /// unavailable but the point is otherwise valid. /// +/// The pointer being pointed at. public readonly record struct TargetPoint( + int Id, TargetPointFlags Flags, Vector3 Position, Vector3 NormalizedPosition, - Quaternion Orientation, - Vector3 Distance, - float Pressure + Ray3F Pointer, + float Pressure, + IPointerTarget Target ) { public bool IsValid => (Flags & Flags.PointingAtTarget) != Flags.NotPointingAtTarget; } @@ -480,11 +483,9 @@ The `PointerState` shall be defined as follows: public class PointerState { public ButtonReadOnlyList Buttons { get; } - public InputReadOnlyList Points { get; } + public InputReadOnlyList Points { get; } public float GripPressure { get; } } - -public readonly record struct PointerStatePoint(IPointerTarget Target, TargetPoint Point); ``` `Points` represents the `TargetPoint`s this pointer is pointing at on its "native targets" i.e. that which is enumerated @@ -501,15 +502,43 @@ The handler for pointer inputs shall be defined as follows: ```cs public interface IPointerInputHandler : IButtonInputHandler { + void HandleTargetChanged(PointerTargetChangedEvent @event); void HandlePointChanged(PointChangedEvent @event); void HandleGripChanged(PointerGripChangedEvent @event); } ``` +`HandleTargetChanged` must be called when properties on an `IPointerTarget` within `IPointer.Targets` changes, or when +an `IPointerTarget` is added or removed to/from `IPointer.Targets`. `IsAdded` shall be `true` if it has been added, +`false` if it has been removed. + `HandlePointChanged` must be called when a point within `PointerState.Points` changes. `HandleGripChanged` must be called when `PointerState.GripPressure` changes. +These device interfaces and related APIs are designed to mirror physical hardware that the user uses to point at a +target. However, there are many cases where applications would work better with an abstraction that creates "virtual +pointers" for each point, rather that the points being spread across many logical devices. For this we propose the +following addendum to the `Pointers` class: + +```cs +public partial class Pointers +{ + public IReadOnlyList Points { get; } +} + +public readonly record struct ContextPoint(IPointer Device, TargetPoint Point, ButtonReadOnlyList Buttons, float GripPressure) +{ + public int Id { get; } +} +``` + +`Id` shall be an identifier that mixes `Point.Id` and `Device.Id` in a way that ensures the identifier is unique across +the whole context. + +**FUTURE IMPROVEMENT:** The `Pointers` class is also expected to be the site of gesture recognition when proposed in the +future. + `PointerButton` shall be defined as follows: ```cs public enum PointerButton