[Windows Phone 7.5] Tutoriel sur la réalité augmentée, partie 3: afficher les données augmentées à l’écran

Cet article est le troisième et dernier d’une série qui constitue une introduction à la réalité augmentée avec Windows Phone 7.5 (Mango). Voici les liens vers les articles précédents:

 

Dans le dernier article, nous avons appris à récupérer la position de l’utilisateur. Il faut aussi récupérer la position GPS de ces points d’intérêt, mais ce n’est pas le but de cet article que de détailler les API de foursquare. Une fois qu’on a ces coordonnées, il faut commencer par calculer la direction de ces derniers par rapport à la position de l’utilisateur et du téléphone, afin de savoir si on doit les afficher ou pas à l’écran quand l’utilisateur pointe son téléphone dans une direction ou une autre :

 foreach (Venue v in ViewModel.Venues)
{
    // Calcul du cap de la Venue en fonction de la position du téléphone
    double Bearing = Math.Round(ARHelper.CalculateBearing(v.Location, ViewModel.MyLocation ), 0);

    // Calcul de la position de la Venue en fonction de l'angle, et de la distance, dans le repère XNA
    Vector3 RelativeVenuePosition = ARHelper.AngleToVector(Bearing, (WCSRadius * v.Distance / Radius));

    AddVenue(RelativeVenuePosition, v);
}

 

Le code ci-dessus calcul pour chaque point d’intérêt le cap en fonction de la position de l’utilisateur, et instancie un Vector3 représentant les coordonnées du point d’intérêt relativement à l’utilisateur. C’est de ce point dont on se servira par la suite pour calculer s’il doit être affiché ou pas !

 

Vous remarquerez que le bout de code ci-dessus utilise quelques méthodes qui ne sont pas dans le framework. Ce sont des méthodes développées pour ne pas charger le code en calculs mathématiques de position : en voici le code :

 public static class ARHelper
{
    public static double CalculateBearing(GeoCoordinate Venue, GeoCoordinate MyPosition)
    {
        ARHelper.DegreeToRadian(MyPosition.Latitude - Venue.Latitude);

        double num1 = ARHelper.DegreeToRadian(MyPosition.Longitude - Venue.Longitude);
        double num2 = ARHelper.DegreeToRadian(Venue.Latitude);
        double num3 = ARHelper.DegreeToRadian(MyPosition.Latitude);
        double angle = Math.Atan2(Math.Sin(num1) * Math.Cos(num3), Math.Cos(num2) 
            * Math.Sin(num3) - Math.Sin(num2) * Math.Cos(num3) * Math.Cos(num1));
            
        return ARHelper.RadianToDegree(angle) + 180.0;
    }

    public static Vector3 AngleToVector(double inAngle, double inRadius)
    {
        double num = ARHelper.DegreeToRadian(inAngle - 90.0);
        return new Vector3((float)Math.Round(inRadius * Math.Cos(num)), 0.0f, (float)Math.Round(inRadius * Math.Sin(num)));
    }

    public static double DegreeToRadian(double angle)
    {
        return 3.14159265358979 * angle / 180.0;
    }

    public static double RadianToDegree(double angle)
    {
        return angle * 57.2957795130823;
    }
}

 

Projeter les points d’intérêt sur l’écran

Il ne reste plus qu’à afficher les points à l’écran ! Comme dit plus tôt, on va rafraichir l’affichage à chaque changement de position du téléphone on va donc écrire le restant du code dans la méthode qui sert de handler pour l’évènement de changement de position du spatial framework. C’est ici qu’on va faire tous les calculs de projection des points sur l’écran, en fonction de leur position relative qu’on a calculé plus tôt. On va donc retrouver toutes nos matrices… Allons-y petit à petit :

D’abord, on calcule la position du téléphone dans l’espace :

 Matrix attitude = Matrix.CreateRotationX(MathHelper.PiOver2) * motionReading.Attitude.RotationMatrix;

 

Le telephone étant tenu horizontalement, il faut appliquer une rotation e 90° sur l’axe des X sur la matrice de position u téléphone qui nous est renvoyée directement par le Spatial Framework.

Ensuite, pour chacun des points, il va falloir créer une matrice qui décrit sa position en 3D dans le repère utilisé pour faire la projection :

 

 Matrix world = Matrix.CreateWorld(points[i], Vector3.UnitZ, Vector3.UnitX);

 

Il ne reste plus qu’à projeter le point sur l’écran:

 Vector3 projected = viewport.Project(Vector3.Zero, projection, view, world * attitude);

 

 

Une fois la projection faite, si le point est dans le champ de vision on les affiche, sinon on les masque:

 if (projected.Z > 1 || projected.Z < 0)
{
    AugmentedLabels[i].Visibility = System.Windows.Visibility.Collapsed;
}
else
{
    AugmentedLabels[i].Visibility = System.Windows.Visibility.Visible;

    // Centrage du contrôle sur le point
    TranslateTransform tt = new TranslateTransform();
    tt.X = projected.X - (AugmentedLabels[i].RenderSize.Width / 2);
    tt.Y = projected.Y - (AugmentedLabels[i].RenderSize.Height / 2);
    AugmentedLabels[i].RenderTransform = tt;
}

 

Ici le tableau AugmentedLabels contient les éléments graphiques à afficher (en l’occurrence, un UserControl avec une icône, un nom, et une distance.

Et voilà ! Nous savons maintenant comment marche la réalité augmentée en mode « viewfinder » dans Windows Phone 7.5.

Pour ceux qui souhaiteraient industrialiser leurs développements, il est possible de dériver un petit framework de tout ça… ou alors d’en utiliser un existant, comme celui-ci : Geo Augmente Reality Toolkit.

 

Vous pouvez retrouver l’intégralité de ce tutorial au format word ainsi que l’application d’exemple sur le skydrive suivant:

 

 

Happy coding !