IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

OpenGL Discussion :

[ JOGL ] Camera Quaternion


Sujet :

OpenGL

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Inscrit en
    Mai 2009
    Messages
    114
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 114
    Par défaut [ JOGL ] Camera Quaternion
    Bonjour,

    Je met en place une camera quaternion. J'ai suivi le tutoriel Nehe et un autre site sur le net. La caméra qq soit el mvt de souris ne réagit pas correctement. diagonale droite vers le bas...

    Voici mon Quaternion
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    public class Quaternion {
     
        private float w;
        private float x;
        private float y;
        private float z;
     
        /** Constructeur */
        public Quaternion() {
            w = 1.0f;
            x = 0.0f;
            y = 0.0f;
            z = 0.0f;
        }
     
        /**
         * Transforme un quaternion en un quaternion représentant une rotation quelconque dans l'espace
         */
        public void createFromAxisAngle(float x, float y, float z, float degrees) {
     
            // First we want to convert the degrees to radians
            // since the angle is assumed to be in radians
            float angle = (degrees / 180.0f) * (float) Math.PI;
     
            // Here we calculate the sin( theta / 2) once for optimization
            float result = (float) Math.sin(angle / 2.0f);
     
            // Calcualte the w value by cos( theta / 2 )
            this.w = (float) Math.cos(angle / 2.0f);
     
            // Calculate the x, y and z of the quaternion
            this.x = x * result;
            this.y = y * result;
            this.z = z * result;
            normaliser();
        }
     
        /**
         * Normalise le quaternion courant
         */
        public void normaliser() {
            float norme = norme();
            // On ne normalise pas le vecteur nul
            if (norme != 0.0f) {
                w = w / norme;
                x = x / norme;
                y = y / norme;
                z = z / norme;
            }
        }
     
        /**
         * Renvoie la norme du quaternion courant
         */
        public float norme() {
            return w * w + x * x + y * y + z * z;
        }
     
        /**
         * Pour créer à partir d'un quaternion une matrice qui peut être utilisée par OpenGL
         */
        public void createMatrix(float[] matrix) {
            // float[] matrix = new float[16];
     
            // First row
            matrix[0] = 1.0f - 2.0f * (y * y + z * z);
            matrix[1] = 2.0f * (x * y + z * w);
            matrix[2] = 2.0f * (x * z - y * w);
            matrix[3] = 0.0f;
     
            // Second row
            matrix[4] = 2.0f * (x * y - z * w);
            matrix[5] = 1.0f - 2.0f * (x * x + z * z);
            matrix[6] = 2.0f * (z * y + x * w);
            matrix[7] = 0.0f;
     
            // Third row
            matrix[8] = 2.0f * (x * z + y * w);
            matrix[9] = 2.0f * (y * z - x * w);
            matrix[10] = 1.0f - 2.0f * (x * x + y * y);
            matrix[11] = 0.0f;
     
            // Fourth row
            matrix[12] = 0.0f;
            matrix[13] = 0.0f;
            matrix[14] = 0.0f;
            matrix[15] = 1.0f;
     
            // Now pMatrix[] is a 4x4 homogeneous matrix that can be applied to an OpenGL Matrix
            // return matrix;
        }
     
        /** Pour multiplier 2 quaternions */
        public Quaternion operator(Quaternion q) {
            Quaternion resultat = new Quaternion();
     
            resultat.w = w * q.w - x * q.x - y * q.y - z * q.z;
            resultat.x = w * q.x + x * q.w + y * q.z - z * q.y;
            resultat.y = w * q.y + y * q.w + z * q.x - x * q.z;
            resultat.z = w * q.z + z * q.w + x * q.y - y * q.x;
     
            return resultat;
        }
    ma camera
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    public class CameraQuaternions extends AbstractCamera {
     
        // Coefficient maximum pour la variation du pitch
        public float maxPitchRate;
     
        // Coefficient maximum pour la variation du heading
        public float maxHeadingRate;
     
        // Coefficient maximum pour la variation de vitesse
        public float maxForwardVelocity;
     
        // Angle de rotation du heading
        public float headingDegrees;
     
        // Angle de rotation du pitch
        public float pitchDegrees;
     
        // Valeur de la vitesse
        public float forwardVelocity;
     
        // Quaternions pour effectuer les rotations
        Quaternion qHeading;
        Quaternion qPitch;
     
        // Le vecteur de la direction de la camera.
        Vector directionVector;
     
        /** Constructeur de la classe Camera */
        public CameraQuaternions() {
            qHeading = new Quaternion();
            qPitch = new Quaternion();
     
            position = new Position(-3, 7, 15);
            directionVector = new Vector();
     
            maxPitchRate = 1.0f;
            maxHeadingRate = 1.0f;
            maxForwardVelocity = 1.0f;
            headingDegrees = 0.0f;
            pitchDegrees = 0.0f;
            forwardVelocity = 0.0f;
        }
     
        /**
         * Pour définir la perspective, c'est à dire la façon dont on voit le monde C'est ainsi que l'on "déplace"
         * la caméra
         */
        @Override
        public void setPerspective(GL2 gl) {
            // Quaternions
            float[] matrice = new float[16];
            Quaternion q = new Quaternion();
     
            // Make the Quaternions that will represent our rotations
            qPitch.createFromAxisAngle(1.0f, 0.0f, 0.0f, pitchDegrees);
            qHeading.createFromAxisAngle(0.0f, 1.0f, 0.0f, headingDegrees);
     
            // Combine the pitch and heading rotations and store the results in q
            q = qPitch.operator(qHeading);
            // matrice =
            q.createMatrix(matrice);
     
            // Let OpenGL set our new prespective on the world!
            // XXX : Cette opération est beaucoup plus couteuse que des appels à
            // glRotatef et glTranslatef
            gl.glMultMatrixf(matrice, 0);
     
            // Create a matrix from the pitch Quaternion and get the j vector
            // for our direction.
            // matrice =
            qPitch.createMatrix(matrice);
            directionVector.setJ(matrice[9]);
     
            // Combine the heading and pitch rotations and make a matrix to get
            // the i and j vectors for our direction.
            q = qHeading.operator(qPitch);
            // matrice =
            q.createMatrix(matrice);
            directionVector.setI(matrice[8]);
            directionVector.setK(matrice[10]);
     
            // Scale the direction by our speed.
            directionVector = directionVector.multiplierScalaire(forwardVelocity);
     
            // XXX
            // Remise à zéro de la vitesse :
            // - Cette classe représente à l'origine une caméra à la Wing Commander,
            // c'est à dire que l'on dirige la vue à la souris, et une impulsion
            // au clavier active les gazs, on avance alors tout seul (comme un
            // avion)
            // - Pour une caméra a la quake, ne pas appuyer sur le clavier
            // correspond à ne pas avancer
            //
            // Remettre cette variable à zéro en permanence est un pure bricolage
            // pour obtenir un comportement quake-like d'une caméra Wing Commander
            // De toute façon, elle reste "non straffante"
            forwardVelocity = 0.0f;
     
            // Increment our position by the vector
            position.setX(position.getX() + directionVector.getI());
            position.setY(position.getY() + directionVector.getJ());
            position.setZ(position.getZ() + directionVector.getK());
     
            // Translate to our new position.
            gl.glTranslatef(-position.getX(), -position.getY(), position.getZ());
        }
     
        /** Pour changer la direction */
        private void changePitch(float degrees) {
     
            // Notre pitch vaut moins que le maximum rate
            // donc on peut l'incrémenter
            if (Math.abs(degrees) < Math.abs(maxPitchRate)) {
                pitchDegrees += degrees;
            }
     
            // Sinon notre pitch vaut plus que le maximum rate
            // donc on peut seulement incrémenter le pitch
            // de ce maximum rate
            else {
                if (degrees < 0.0f) {
                    pitchDegrees -= maxPitchRate;
                }
                else {
                    pitchDegrees += maxPitchRate;
                }
            }
     
            // On ne veut pas que notre pitch "explose"
            if (pitchDegrees > 360.0f) {
                pitchDegrees -= 360.0f;
            }
            else {
                if (pitchDegrees < -360.0f) {
                    pitchDegrees += 360.0f;
                }
            }
        }
     
        /** Pour changer le cap */
        private void changeHeading(float degrees) {
            // Notre heading vaut moins que le maximum rate
            // donc on peut l'incrémenter
            // MAIS on doit vérifier si nous sommes renversés
            if (Math.abs(degrees) < Math.abs(maxHeadingRate)) {
                if ((pitchDegrees > 90.0f && pitchDegrees < 270.0f)
                        || (pitchDegrees < -90.0f && pitchDegrees > -270.0f)) {
                    headingDegrees -= degrees;
                }
                else {
                    headingDegrees += degrees;
                }
            }
     
            // Notre heading est plus grand que le maximum rate
            // donc on peut seulement incrémenter le heading
            // de ce maximum rate
            else {
                if (degrees < 0.0f) {
                    if ((pitchDegrees > 90.0f && pitchDegrees < 270.0f)
                            || (pitchDegrees < -90.0f && pitchDegrees > -270.0f)) {
     
                        // Normalement ici on décremente, mais vu que nous sommes
                        // renversés, on incrémente
                        headingDegrees += maxHeadingRate;
                    }
                    else {
                        headingDegrees -= maxHeadingRate;
                    }
                }
                else {
                    if ((pitchDegrees > 90.0f && pitchDegrees < 270.0f)
                            || (pitchDegrees < -90.0f && pitchDegrees > -270.0f)) {
                        headingDegrees -= maxHeadingRate;
                    }
                    else {
                        headingDegrees += maxHeadingRate;
                    }
                }
            }
     
            // On ne veut pas que notre heading "explose"
            if (headingDegrees > 360.0f) {
                headingDegrees -= 360.0f;
            }
            else {
                if (headingDegrees < -360.0f) {
                    headingDegrees += 360.0f;
                }
            }
        }
     
        /** Pour changer la vitesse */
        private void changeVelocity(float vel) {
     
            // Notre vitesse vaut moins que le maximum rate
            // donc on peut l'incrémenter
            if (Math.abs(vel) < Math.abs(maxForwardVelocity)) {
                forwardVelocity += vel;
            }
     
            // Notre vitesse est plus grande que le maximum rate
            // donc on peut seulement l'incrémenter de ce maximum rate
            else {
                if (vel < 0.0f) {
                    forwardVelocity -= -maxForwardVelocity;
                }
                else {
                    forwardVelocity += maxForwardVelocity;
                }
            }
        }
     
        public void moveForwards() {
            changeVelocity(0.5f);
        }
     
        public void moveBackwards() {
            changeVelocity(-0.5f);
        }
     
        public void moveRight() {
            changeHeading(5.0f);
        }
     
        public void strafeLeft() {
            changeHeading(-5.0f);
        }
     
        public void moveUpwards() {
     
        }
     
        public void moveDownwards() {
     
        }
     
        @Override
        public void setLook(float delta, boolean isPitch) {
            if (isPitch) {
                changePitch(delta);
                return;
            }
     
            changeHeading(delta);
        }
    }
    et mon mouseListener
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    public class Mouse implements MouseMoveListener {
     
        private static Logger logger = Logger.getLogger(Mouse.class.getName());
     
        Robot robot;
     
        public Mouse() {
            try {
                robot = new Robot();
            }
            catch (Exception e) {
                logger.error("Echec à l'initialisation du robot");
            }
        }
     
        public void mouseMove(MouseEvent e) {
            // deltaMouse mesure de combien le curseur de souris s'éloigne du centre
            // de l'écran
            float deltaMouse = 0.0f;
     
            // On récupère le component qui communique avec le listener car on a
            // besoin de connaître les dimensions de la fenêtre
            Rectangle canvas = e.display.getActiveShell().getBounds();
     
            // On cherche le centre de la zone opengl :
            // Coordonnée extérieure gauche de la frame + épaisseur de la bordure = on est
            // au bord gauche de la zone opengl
            // + la largeur de la zone opengl / 2 = le milieu de la zone opengl
            int centerX = canvas.width / 2;
            int centerY = canvas.height / 2;
     
            // XXX
            // Explication du +1.0f : un nombre empirique
            // Selon le gestionnaire de fenêtre :
            //
            // Pour Windows XP / KDE3 / Fvwm2 :
            // - si l'origine de la fenêtre est en (1,1) il y a une différence de 1
            // pixel entre le calcul du centre et celui des coordonnées de la souris
            // Donc on ajoute 1
            // - si la fenêtre est n'importe où ailleurs on ne touche à rien
            //
            // Pour Fluxbox :
            // On constate sur Fluxbox (un window manager de Linux) un comportement
            // particulier.
            // - si l'origine de la fenêtre est en (1,1) il ne faut rien rajouter
            // - si la fenêtre est n'importe où ailleurs : il y a une différence de 1 pixel
            // entre le calcul du centre et celui des coordonnées de la souris
            // Donc on ajoute 1
     
            // CODE WINDOWS_XP KDE3 FVWM2
            int mouseX = (canvas.x + e.x);
            int mouseY = (canvas.y + e.y);
     
            logger.info(String.format("canvas pos [%s, %s]", canvas.x, canvas.y));
            logger.info(String.format("mouse  pos [%s, %s]", e.x, e.y));
     
            logger.info(String.format("mouseX,Y  pos [%s, %s]", mouseX, mouseY));
     
            if (canvas.x == 1 && canvas.y == 1) {
                mouseX += 1.0f;
                mouseY += 1.0f;
            }
            // FIN CODE WINDOWS_XP KDE3 FVWM2
     
            if (mouseX < centerX) {
                deltaMouse = (centerX - mouseX);
                logger.info(String.format("mouseX < centerX [%s, %s]", mouseX, centerX));
                logger.info(String.format("deltaMouse X [%s]", deltaMouse));
                CameraManager.getInstance().getCamera().setLook(-0.2f * deltaMouse, false);
            }
            else if (mouseX > centerX) {
                deltaMouse = (mouseX - centerX);
                logger.info(String.format("mouseX > centerX [%s, %s]", mouseX, centerX));
                logger.info(String.format("deltaMouse X [%s]", deltaMouse));
                CameraManager.getInstance().getCamera().setLook(0.2f * deltaMouse, false);
            }
     
            if (mouseY < centerY) {
                deltaMouse = (centerY - mouseY);
                logger.info(String.format("mouseY < centerY [%s, %s]", mouseY, centerY));
                logger.info(String.format("deltaMouse Y [%s]", deltaMouse));
                CameraManager.getInstance().getCamera().setLook(-0.2f * deltaMouse, true);
            }
            else if (mouseY > centerY) {
                deltaMouse = (mouseY - centerY);
                logger.info(String.format("mouseY > centerY [%s, %s]", mouseY, centerY));
                logger.info(String.format("deltaMouse Y [%s]", deltaMouse));
                CameraManager.getInstance().getCamera().setLook(0.2f * deltaMouse, true);
            }
     
            mouseX = centerX + canvas.x;
            mouseY = centerY + canvas.y;
     
            // Le curseur de souris doit rester au centre de l'écran
            robot.mouseMove(mouseX, mouseY);
        }
    }
    J'ai vérifier ligne à ligne sans succés. SI qq'un est en mesure de m'aider
    Thx

  2. #2
    Membre confirmé
    Inscrit en
    Mai 2009
    Messages
    114
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 114
    Par défaut
    Une bonne piste :
    if (mouseX < centerX) et if (mouseY < centerY) n'arrive jamais...

  3. #3
    Expert confirmé

    Profil pro
    Fabricant et casseur d'avions
    Inscrit en
    Avril 2004
    Messages
    3 817
    Détails du profil
    Informations personnelles :
    Localisation : France, Tarn (Midi Pyrénées)

    Informations professionnelles :
    Activité : Fabricant et casseur d'avions
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2004
    Messages : 3 817
    Par défaut
    Salut,

    Citation Envoyé par gladek Voir le message
    Je met en place une camera quaternion. J'ai suivi le tutoriel Nehe et un autre site sur le net.
    Té, ça me fait penser que je n'ai toujours pas fini mes tutos sur mon moteur graphique avec jogl... argh!

    Citation Envoyé par gladek Voir le message
    La caméra qq soit el mvt de souris ne réagit pas correctement.
    ça veut dire quoi exactement "ne réagit pas correctement"?

    Citation Envoyé par gladek Voir le message
    Une bonne piste :
    if (mouseX < centerX) et if (mouseY < centerY) n'arrive jamais...
    J'aurais tendance à penser la même chose... genre les coordonnées mouse et center qui ne sont pas avec la même origine...

    Etant donné que tu as un joli fichier log, on pourrait avoir la trace quand ça déconne?
    "Errare humanum est, sed perseverare diabolicum"

    Ma page sur DVP.com

  4. #4
    Membre confirmé
    Inscrit en
    Mai 2009
    Messages
    114
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 114
    Par défaut
    le pb provient des coordonnées dans le mouseMove(MouseEvent e).
    MouseEvent donne les coordonnés dans la canvas, et Robot replace la souris dans le repere de l'ecran...
    Il faut également prendre en considération les bordures su shell(SWT+canvas)...

  5. #5
    Membre confirmé
    Inscrit en
    Mai 2009
    Messages
    114
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 114
    Par défaut
    Voila la correction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
        public void mouseMove(MouseEvent e) {
            // On récupère le component qui communique avec le listener car on a
            // besoin de connaître les dimensions de la fenêtre
            // 800x600
            Rectangle window = e.display.getActiveShell().getBounds();
            // 792x573
            Rectangle canvas = e.display.getActiveShell().getClientArea();
            int borderWidth = (window.width - canvas.width) / 2;
            int borderHeight = window.height - canvas.height - borderWidth;
     
            // On cherche le centre de la zone opengl :
            // Coordonnée extérieure gauche de la frame + épaisseur de la bordure = on est
            // au bord gauche de la zone opengl
            // + la largeur de la zone opengl / 2 = le milieu de la zone opengl
            centerX = window.x + canvas.width / 2;
            centerY = window.y + canvas.height / 2;
     
            mouseX = window.x + borderWidth + e.x;
            mouseY = window.y + borderHeight + e.y;
     
            // deltaMouse mesure de combien le curseur de souris s'éloigne du centre
            // de l'écran
            deltaMouse = 0.0f;
            if (mouseX < centerX) {
                deltaMouse = (centerX - mouseX);
                CameraManager.getInstance().getCamera().setLook(-0.2f * deltaMouse, false);
            }
            else if (mouseX > centerX) {
                deltaMouse = (mouseX - centerX);
                CameraManager.getInstance().getCamera().setLook(0.2f * deltaMouse, false);
            }
     
            if (mouseY < centerY) {
                deltaMouse = (centerY - mouseY);
                CameraManager.getInstance().getCamera().setLook(-0.2f * deltaMouse, true);
            }
            else if (mouseY > centerY) {
                deltaMouse = (mouseY - centerY);
                CameraManager.getInstance().getCamera().setLook(0.2f * deltaMouse, true);
            }
     
            // Le curseur de souris doit rester au centre de l'écran
            robot.mouseMove(centerX, centerY);
        }
    J'aimerai etre un peu plus sur du calcul de borderHeight.
    Je me demande si il y a un écart de +/- 1...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Camera (eviter les quaternions)
    Par Glosialabolas dans le forum OpenGL
    Réponses: 21
    Dernier message: 21/02/2006, 20h54
  2. [Debutant Java JOGL] problème glVertexPointer()
    Par Stef784ever dans le forum OpenGL
    Réponses: 2
    Dernier message: 21/04/2004, 17h43
  3. Camera First Person Shooter
    Par Martin dans le forum OpenGL
    Réponses: 6
    Dernier message: 15/01/2004, 04h37
  4. Camera - Directx3D
    Par oxor3 dans le forum DirectX
    Réponses: 3
    Dernier message: 20/09/2003, 00h47
  5. Texte // camera
    Par logramme dans le forum DirectX
    Réponses: 1
    Dernier message: 10/08/2002, 21h05

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo