Loupe

Gestion des inputs avec HoloLens et C++

Après cette brève introduction au développement 3D avec HoloLens, concentrons-nous sur une partie importante et, finalement, simple comme bonjour: la gestion des entrées utilisateur avec HoloLens.

L’inputs manager

Créons une classe qui s’occupera pour nous de récupérer les évènements, tels que le clic (ou pincé).

Afin d’intercepter les entrées utilisateur et d’en récupérer les informations associées (position du curseur du HoloLens lors du clic par exemple), les APIs HoloLens nous fournissent une classe appelée SpatialInteractionManager qui va nous permettre de nous abonner à tous les évènements disponibles.

Chaque évènement nous fournit un état (_state) qui nous permettra d’accéder aux informations associées.

Pour finir, notre instance _coordinatesSystem n’est déclarée ici que parce que nous l’utiliserons afin de récupérer la position du curseur dans notre environnement.

class CInputHandler
{
    public:
    //! Constructor
    CInputHandler();

    //! Destructor (empty)
    virtual ~CInputHandler();

    //! Sets the coordinates system
    inline void SetSpatialCoordinatesSystem(Windows::Perception::Spatial::SpatialCoordinateSystem^ coordinatesSystem)
    {
        _coordinatesSystem = coordinatesSystem;
    }

    private:
    //! Holographic
    Windows::UI::Input::Spatial::SpatialInteractionManager^ _manager;
    Windows::UI::Input::Spatial::SpatialInteractionSourceState^ _state = nullptr;
    Windows::Foundation::EventRegistrationToken _pressedEventToken;
    Windows::Perception::Spatial:: SpatialCoordinateSystem^ _coordinatesSystem = nullptr;

    //! Events
    void OnPressed(Windows::UI::Input::Spatial::SpatialInteractionManager^ sender, Windows::UI::Input::Spatial::SpatialInteractionSourceEventArgs^ args);

    //! Utils functions
    void _getCursorPosition(math::vector3f& vec);
};

Le constructeur

Le constructeur peut nous permettre de nous abonner à ces différents évènements. Regardons de plus près la méthode à suivre:

//! Constructor
CInputHandler::CInputHandler()
{
    //! Initialize
    _manager = SpatialInteractionManager::GetForCurrentView();

    //! Bind events
    _pressedEventToken = _manager->SourcePressed
            += ref new TypedEventHandler<SpatialInteractionManager^, SpatialInteractionSourceEventArgs^>(bind(&CInputHandler::OnPressed, this, _1, _2));
}

 

Dans un premier temps, il s’agit de récupérer une instance de SpatialInteractionManager en appelant la méthode statique GetForCurrentView(), vue courante qui est en fait notre instance de IFrameworkViewSource :(https://msdn.microsoft.com/library/windows/apps/hh700482).

Puis, dans un deuxième temps, il ne nous reste plus qu’à nous abonner aux évènements (ici le clic) en utilisant les TypedEventHandler (https://msdn.microsoft.com/fr-fr/library/windows/apps/br225997.aspx). Finalement nous récupérons le Token renvoyé par l’opérateur += qui contient la référence du delegate.

Note: Les arguments _1 et _2 sont juste des placeholders utilisés pour définir les positions des arguments dans la fonction bind. Plus d’informations ici : http://en.cppreference.com/w/cpp/utility/functional/placeholders

A partir de ce moment-là, à chaque clic, notre méthode OnPressed sera appelée par l’instance de SpatialInteractionManager.

La méthode OnPressed

Cette méthode prend deux arguments : un pointeur vers l’objet source (sender) et un pointeur vers les informations liées à l’évènement (args).

C’est l’argument args qui nous permettra de récupérer l’état de l’évènement et, au final, nous permettre de récupérer la position du curseur à partir du clic:

//! On source pressed (right click equivalence)
void CInputHandler::OnPressed(SpatialInteractionManager^ sender, SpatialInteractionSourceEventArgs^ args)
{
       math::vector3f cursorPosition;
       
       _state = args->State;
       _getCursorPosition(cursorPosition);

       std::cout << cursorPosition.X << ", " << cursorPosition.y << ", " << cursorPosition.Z << std::endl;
}

Dans cet exemple, la classe vector3f correspond à un vecteur 3D qui contient les membres à virgule flottante X, Y, et Z et qui ici correspond à la position du curseur.

La méthode _getCursorPosition (ci-dessous) nous permet de récupérer la position du curseur en complétant l’objet cursorPosition.

Récupérer la position du curseur

Récupérer la position du curseur revient à déterminer “où et jusqu’où” l’utilisateur regarde, c’est-à-dire le regard. En anglais, cela correspond au fameux “gaze” :  https://developer.microsoft.com/en-us/windows/holographic/gaze

La méthode consiste à récupérer deux informations essentielles : la position du HoloLens dans l’espace (soit la tête de l’utilisateur) et la direction dans laquelle il regarde, ces deux informations essentielles qui sont des vecteurs 3D. Une fois ces deux vecteurs additionnés, le résultat a une fâcheuse tendance à nous donner la position du curseur et ça tombe bien, c’est exactement ce que l’on veut !

void CInputHandler::_getPositionPosition(vector3f& vec)
{
    // Get pointer pose (head direction and position in space)
    auto position = _state->TryGetPointerPose(_coordinatesSystem);
       
    if (position != nullptr)
    {
        const float3 headPosition = position->Head->Position;
        const float3 headDirection = position->Head->ForwardDirection;

        static const float distanceFromUser = 2.f;
        const float3 gaze = headPosition + (distanceFromUser * headDirection);

        vec.X = gaze.x;
        vec.Y = gaze.y;
        vec.Z = gaze.z;
    }
}

La formule suivante:

float3 gaze = headPosition + headDirection;

Revient à dire que l’utilisateur regarde toujours un point situé à 1 mètre de lui (si l’on part du principe que l’on ne regarde jamais dans la vide). C’est-à-dire:

float3 gaze = headPosition + (1.f * headDirection);

Dans l’exemple ci-dessus, on part du principe que l’utilisateur regarde toujours un point situé à 2 mètres de lui (float distanceFromUser = 2.f). Afin de récupérer la position réelle du curseur, il nous faut récupérer les informations liées à l’analyse de l’environnement par l’HoloLens (Spatial Mapping : https://developer.microsoft.com/en-us/windows/holographic/spatial_mapping). Récupérer et utiliser ces informations nécessite un peu plus d’efforts mais sera présenté prochainement.

Conclusion

Gérer les entrées utilisateurs avec un HoloLens ne change finalement en rien par rapport à une application UWP classique. Les différences ici sont seulement les informations liées aux évènements et les usages qui en sont faits. C’est-à-dire qu’au lieu d’appeler une méthode sur un service lorsque l’utilisateur clique, ici nous additionnons deux vecteurs pour récupérer la position d’un curseur Sourire

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus