FORUM PS3, PSP & PS2 » Making Of » OSG, les rotations... le monde et plus si affinités

En ligne > Il y a 52 utilisateurs connus et inconnus. Pour voir la liste des connectés connus, cliquez ici

 Mot :   Pseudo :  
 
OSG, les rotations... le monde et plus si affinités
 
n°2139
Profil : Internaute
Posté le 29-07-2007 à 14:33:39   answer
 

Etant en vacances depuis quelques jours jusqu'à septembre, je me suis dit que j'allais faire ce que j'ai pas vraiment eu l'occasion de faire en cours jusqu'à présent: de la 3D (on a eu un cours théorique de synthèse d'image bien sympathique mais c'est pas avec ça que je vais pourvoir coder). Pour le coup je suis parti sur un jeu de simulation aérienne (même si pour l'instant mes avions sont des pavés, et que ca va etre très arcade). Je fais ca en C++ avec OSG (OpenSceneGraph: bibliothèque de gestion de graphe de scène qui se place en surcouche d'OpenGL)... et c'est la que commencent les problèmes :whistle:

 

OSG c'est très sympathique à l'usage, mais c'est documenté à l'arrache: y'a une doc html généré par doxygen sur laquelle il doit y avoir 70% des méthodes non commentées. Mais au moins y'a des tutoriaux. Donc pour ce qui est de la géométrie de base ça se passe plutôt bien, par contre j'ai deux problèmes: ma gestion du framerate et les déplacements.

 

Pour la gestion du framerate, en fait je pensais que OSG le gérait. Il le fait peut être... mais j'ai pas trouvé dans la doc. Donc j'ai cherché un peu dans les tutoriaux et je suis tombé là dessus:
http://www.cs.clemson.edu/~malloy/ [...] timer.html
A défaut de mieux j'ai utilisé ça en mettant le tout dans un singleton... avec un do while pour éviter des problèmes d'init. Mais le problème c'est qu'à moins que je sois fou ça reste de l'attente active et ça m'énerve. Est-ce que y'a un équivalent à wait dans OSG (genre le wait de SDL...)? Ca m'embêterait un peu de passer par un wait plus bas niveau parce que dans mes souvenirs ça devient dépendant du système et que pour le moment mon code est portable sans modifs...

 

Pour les déplacements ça fait deux jours que je suis dessus je comprends toujours pas... enfin il paraît que tout le monde galère au début mais quand même...
J'ai mon osg::Geode avec le modèle du vaisseau centré en 0 qui va dans la direction 0y.
Je cherche à faire des déplacements un peu à la Ace combat 3 (pas testé les plus récents):
- la rotation selon l'axe 0y fait seulement pivoter le vaisseau.
- les rotations selon les axes 0x et 0z dépendent de la rotation en 0y.

 

Donc j'ai balancé mon osg::Geode dans un osg::PositionAttitudeTransform chargé de faire les rotations et le déplacement. J'ai un vecteur qui représente la direction du vaisseau. Quand les touches haut ou bas sont enfoncées je le modifie et je recalcule les angles pour 0x et 0z en fonction de ce vecteur. Les problèmes c'est que d'une part même quand je touche seulement aux touches gauche et droite, au bout d'environ un demi-tour mon vaisseau arrete de tourner sur lui-même (je croyais avoir reglé le problème et que ça venait d'une mauvaise comprehension de ma part des quaternions mais apparemment c'est revenu automagiquement), et d'autre part que les rotations en 0x et 0z partent en freestyle (mais apparemment ca vient pas des rotations mais de la façon dont je mets à jour la direction puisque la direction m'a l'air d'etre correcte vis à vis des rotations). Bref si quelqu'un a une idée de ce qui déconne je suis preneur... voila en gros le code (disons qu'il est en travaux... j'ai rajoute un deuxième osg::PositionAttitudeTransform entre le premier et l'osg::Geode pour y mettre la rotation en 0y)

 
Code :
  1. void
  2. Ship::update()
  3. {
  4.   float  spd = speed_ * Framerate::instance().get_delay();
  5.   osg::Quat rot = transf_->getAttitude();
  6.   float         rot_x = rot.x() * rot.w();
  7.   float         rot_z = rot.z() * rot.w();
  8.   if (left_key)
  9.     rotation_ -= 1.0 * Framerate::instance().get_delay();
  10.   if (right_key)
  11.     rotation_ += 1.0 * Framerate::instance().get_delay();
  12.   if (up_key || down_key)
  13.   {
  14.     if (up_key)
  15.     {
  16.       direction_.x() -= cos(-cos(rot_z + M_PI / 2) + rotation_ + M_PI / 2)
  17. * Framerate::instance().get_delay();
  18.       direction_.z() += sin(sin(rot_x + M_PI / 2)rotation_ + M_PI / 2)
  19. * Framerate::instance().get_delay();
  20.     }
  21.     if (down_key)
  22.     {
  23.       direction_.x() += cos(-cos(rot_z + M_PI / 2)rotation_ + M_PI / 2)
  24. * Framerate::instance().get_delay();
  25.       direction_.z() -= sin(sin(rot_x + M_PI / 2)rotation_ + M_PI / 2)
  26. * Framerate::instance().get_delay();
  27.     }
  28.     direction_.normalize();
  29.   }
  30.   rot.makeRotate(osg::Vec3d(0, 1, 0), direction_);
  31.   orient_->setAttitude(osg::Quat(0, rotation_, 0, 1));
  32.   transf_->setAttitude(rot);
  33.   transf_->setPosition(transf_->getPosition() + direction_ * spd);
  34. }
   

Message cité 1 fois
Message édité par Skyfighter le 30-07-2007 à 12:27:45

---------------
1f u c4n r34d th1s u r34lly n33d t0 g37 l41d.
1f u c4n wr1t3 1t t00.
n°2140
Profil : Internaute
Posté le 30-07-2007 à 20:51:42   answer
 

Bon, je ne connais pas du tout OSG, mais je tente de répondre quand même :D
 

Skyfighter a écrit :

Pour la gestion du framerate, en fait je pensais que OSG le gérait. Il le fait peut être... mais j'ai pas trouvé dans la doc. Donc j'ai cherché un peu dans les tutoriaux et je suis tombé là dessus:
http://www.cs.clemson.edu/~malloy/ [...] timer.html
A défaut de mieux j'ai utilisé ça en mettant le tout dans un singleton... avec un do while pour éviter des problèmes d'init. Mais le problème c'est qu'à moins que je sois fou ça reste de l'attente active et ça m'énerve. Est-ce que y'a un équivalent à wait dans OSG (genre le wait de SDL...)? Ca m'embêterait un peu de passer par un wait plus bas niveau parce que dans mes souvenirs ça devient dépendant du système et que pour le moment mon code est portable sans modifs...

Alors, je ne joue plus trop sur PC, mais à l'époque où je le faisais, je ne pense pas que qui que ce soit ne se souciait de faire de l'attente passive...  En gros, on tournait à fond dans la boucle d'affichage, ce qui faisait un nombre d'images par secondes de fou si la scène n'était pas trop compliquée.  Après, si le système est configuré pour attendre la v-sync avant d'échanger les buffers (si tu fait du double-buffering), ça doit provoquer de l'attente passive le temps que le swap ait bien lieu (enfin en théorie OpenGl est asynchrone si je ne m'abuse, mais même dans ce cas, si tu vas plus vite que le rafraîchissement de ton écran, ça finit forcément par saturer -> attente passive).
 
Si tu as vraiment besoin d'attendre un timer ou le clavier par exemple, ce n'est pas vraiment à une bibliothèque graphique de te proposer ce genre de fonctions...  Par contre, tu dois pouvoir trouver des bibliothèques portables qui le permettent (genre SDL ?), et que tu peux utiliser en plus d'OSG.
 

Citation :

Je cherche à faire des déplacements un peu à la Ace combat 3 (pas testé les plus récents):
- la rotation selon l'axe 0y fait seulement pivoter le vaisseau.
- les rotations selon les axes 0x et 0z dépendent de la rotation en 0y.

Arf, problème pour moi là : je n'ai pas Ace Combat 3, et j'ai du mal à voir à quoi correspond cette distinction des rotations selon (Oy) d'une part, et (Ox) et (Oz) d'autre part (ok, l'une fait seulement pivoter le vaisseau, mais les autres font quoi ?  tout pivoter ?  Ça me paraît bizarre comme truc, va falloir que j'essaie AC3 un jour ^^)...
 

Citation :

Les problèmes c'est que d'une part même quand je touche seulement aux touches gauche et droite, au bout d'environ un demi-tour mon vaisseau arrete de tourner sur lui-même (je croyais avoir reglé le problème et que ça venait d'une mauvaise comprehension de ma part des quaternions mais apparemment c'est revenu automagiquement)

Bein c'est effectivement un problème avec les quaternions, je pense...  En gros, dans ce code :

Code :
  1. orient_->setAttitude(osg::Quat(0, rotation_, 0, 1));

n'est valable que quand ton angle "rotation_" est petit.  La formule générale serait plutôt l'une de ces deux là :

Code :
  1. orient_->setAttitude(osg::Quat( 0, sin(rotation_/2), 0, cos(rotation_/2) ));
  2. orient_->setAttitude(osg::Quat( rotation_, osg::Vec3d(0, 1, 0) ));

(les deux étant équivalentes, la première définit directement le quaternion à partir de ses 4 composantes, la seconde donne l'angle et le vecteur directeur de la rotation).  Quand rotation_ est petit, tu as bien sin(rotation_) ~ rotation/2, et cos(rotation_) ~ 1, ce qui fait que ton code marchait bien pour les petits angles.
 

Citation :

et d'autre part que les rotations en 0x et 0z partent en freestyle (mais apparemment ca vient pas des rotations mais de la façon dont je mets à jour la direction puisque la direction m'a l'air d'etre correcte vis à vis des rotations).

Oui, là tu as plusieurs problèmes :

Code :
  1. float         rot_x = rot.x() * rot.w();
  2. float         rot_z = rot.z() * rot.w();
  3. direction_.x() -= cos(-cos(rot_z + M_PI / 2) + rotation_ + M_PI / 2)
  4. * Framerate::instance().get_delay();
  5. direction_.z() += sin(sin(rot_x + M_PI / 2)rotation_ + M_PI / 2)
  6. * Framerate::instance().get_delay();

Tout d'abord, ton calcul de rot_x et rot_z (lignes 1-2) n'est pas bon (du moins si ton but est bien de trouver les angles selon x et z).  Les composantes x,y et z du quaternion définissent les coordonnées d'un vecteur directeur de l'axe de rotation, et non pas des angles (si les angles sont petits en revanche, ça marche à peu près).  Et normaliser en divisant par w n'a aucune raison d'aider.  Après, si tu veux absolument récupérer les angles, les formules sont un peu plus compliquées, et ça dépend de ce que tu veux exactement.  Si ce sont les angles d'Euler, ça donne ça (formules trouvées sur le net, je n'ai pas vérifié mais ça me semble correct à première vue) :
  tan(rot_x) = 2(xy + zw) / (w² + x² - y² - z²)
  sin(rot_y) = -2(xz - yw)
  tan(rot_z) =  2(xw + yz) / (w² - x² - y² + z²)  
 
Mais le plus génant je pense, ce sont les formules des lignes 4-7 par exemple.  Déjà avec des cos à l'intérieur des cos, on se dit qu'il y a un truc louche.  De plus, je ne pense pas que tu puisses obtenir une rotation simplement en ajoutant un cos/sin de quelque chose à ton vecteur (pense que le vecteur nul par exemple ne doit pas bouger après rotation).  Grosso modo, chaque composante du vecteur doit devenir une combinaison linéaire des composantes, ie pour tourner le vecteur (x,y,z), tu doit appliquer une formule de la forme x' = ax + by + cz, ou a,b, et c sont à déterminer en fonction de ta rotation (en général, c'est des sinus, cosinus ou des produit de sin et/ou cos), et pareil pour les autres coordonnées...
 
Cela dit, puisque tu utilises des quaternions, tu peux sans doute t'en servir pour tourner ton vecteur, ce qui doit être bien plus simple (et ça t'évite d'avoir à recalculer les angles de rotation rot_x et rot_z)...  En fait, j'aurais même tendance à faire le contraire de ce que tu fais, c'est à dire calculer d'abord la rotation (en multipliant les quaternions pour composer ton ancienne rotation avec celles produites par la pression des touches), puis, si tu en as besoin, calculer la direction en appliquant la rotation au vecteur direction initial.
 
Je t'aurais bien proposé un bout de code, mais j'ai vraiment du mal à voir à quoi correspondent tes rotations :( (et aussi direction_ d'ailleurs : tu dis que c'est la direction du vaisseau, mais il ne change pas quand tu presses haut/bas alors que le vaisseau est sencé tourner ?)


---------------
Beautés démoniaques, manipulant le monde dans l'ombre [...], on les appelait le clan de Marjoly, haïes, craintes et vénérées depuis la nuit des temps.
Mais même elles possédaient un défaut fatal...  elles étaient toutes d'une stupidité sans limite.
n°2141
Profil : Internaute
Posté le 30-07-2007 à 21:48:55   answer
 

Shao Onos a écrit :


Si tu as vraiment besoin d'attendre un timer ou le clavier par exemple, ce n'est pas vraiment à une bibliothèque graphique de te proposer ce genre de fonctions...  


Bein disons que OSG propose des timers que j'utilise pour la boucle d'attente (et que j'utiliserais volontier pour de la gestion clavier et cie) mais pas d'équivalent à SDL_wait à ma connaissance. En fait je voulais forcer le jeu à tourner à 60fps pour me simplifier la vie vis à vis des vitesses de déplacement mais plus je code plus je me rends compte que de toute façon je mets la fréquence d'affichage dans tous les calculs donc bon...
 

Citation :


Par contre, tu dois pouvoir trouver des bibliothèques portables qui le permettent (genre SDL ?), et que tu peux utiliser en plus d'OSG.


Je voulais justement essayer éviter la chose. Je sais que la SDL est sensé permettre de balancer de l'OpenGL et d'après ce que j'en ai lu on peut utiliser OSG dans SDL. Mais pour le coup faut faire la gestion de la fenêtre en SDL ce qui me paraît un peu extrême pour juste utiliser SDL_wait au final. Donc je commence par essayer de me renseigner pour voir si y'a pas déjà un équivalent portable dans OSG.
 
 

Citation :

Arf, problème pour moi là : je n'ai pas Ace Combat 3, et j'ai du mal à voir à quoi correspond cette distinction des rotations selon (Oy) d'une part, et (Ox) et (Oz) d'autre part (ok, l'une fait seulement pivoter le vaisseau, mais les autres font quoi ?  tout pivoter ?  Ça me paraît bizarre comme truc, va falloir que j'essaie AC3 un jour ^^)...


J'vais tenter de le réexpliquer pour que ce soit compréhensible ;)
Si on part sur du (0x, 0y, 0z), a l'init mon vaisceau à son centre en (0, 0, 0) et se dirige vers (0, 1, 0).
Quand j'appuie sur la fleche gauche ou la droite, je fais varier la rotation suivant l'axe 0y (dans le repère du vaisceau).
Quand j'appuie sur bas ou sur haut, je change la direction de l'appareil en prenant en compte la rotation en 0y.
 
 

Citation :


Bein c'est effectivement un problème avec les quaternions, je pense...  En gros, dans ce code :

Code :
  1. orient_->setAttitude(osg::Quat(0, rotation_, 0, 1));

n'est valable que quand ton angle "rotation_" est petit.  La formule générale serait plutôt l'une de ces deux là :

Code :
  1. orient_->setAttitude(osg::Quat( 0, sin(rotation_/2), 0, cos(rotation_/2) ));
  2. orient_->setAttitude(osg::Quat( rotation_, osg::Vec3d(0, 1, 0) ));


(les deux étant équivalentes, la première définit directement le quaternion à partir de ses 4 composantes, la seconde donne l'angle et le vecteur directeur de la rotation).  Quand rotation_ est petit, tu as bien sin(rotation_) ~ rotation/2, et cos(rotation_) ~ 1, ce qui fait que ton code marchait bien pour les petits angles.


Effectivement ça explique des choses... encore que j'ai du mal avec ta première formule. Faut que me trouve un bon tutorial sur les quaternions ^^;
 
 

Citation :


Mais le plus génant je pense, ce sont les formules des lignes 4-7 par exemple.  Déjà avec des cos à l'intérieur des cos, on se dit qu'il y a un truc louche.  De plus, je ne pense pas que tu puisses obtenir une rotation simplement en ajoutant un cos/sin de quelque chose à ton vecteur (pense que le vecteur nul par exemple ne doit pas bouger après rotation).  Grosso modo, chaque composante du vecteur doit devenir une combinaison linéaire des composantes, ie pour tourner le vecteur (x,y,z), tu doit appliquer une formule de la forme x' = ax + by + cz, ou a,b, et c sont à déterminer en fonction de ta rotation (en général, c'est des sinus, cosinus ou des produit de sin et/ou cos), et pareil pour les autres coordonnées...


Le cas du vecteur était pas sensé se présenter en fait. L'idée c'était que direction_ soit un vecteur normalisé que j'aie plus qu'à multiplié par ma vitesse de déplacement puis à additionner à la position courante. Mais clairement la formule de mise à jour est empiriquement en freestyle...
 
 

Citation :


Cela dit, puisque tu utilises des quaternions, tu peux sans doute t'en servir pour tourner ton vecteur, ce qui doit être bien plus simple (et ça t'évite d'avoir à recalculer les angles de rotation rot_x et rot_z)...  En fait, j'aurais même tendance à faire le contraire de ce que tu fais, c'est à dire calculer d'abord la rotation (en multipliant les quaternions pour composer ton ancienne rotation avec celles produites par la pression des touches), puis, si tu en as besoin, calculer la direction en appliquant la rotation au vecteur direction initial.


C'est ce que je faisais à la base. Seulement avec ma compréhension limitée des quaternions ça me donnait une direction fantaisiste :sweat: :D  
 

Citation :


Je t'aurais bien proposé un bout de code, mais j'ai vraiment du mal à voir à quoi correspondent tes rotations :( (et aussi direction_ d'ailleurs : tu dis que c'est la direction du vaisseau, mais il ne change pas quand tu presses haut/bas alors que le vaisseau est sencé tourner ?)


Si justement, direction_ est modifié que quand haut ou bas sont appuyés.


---------------
1f u c4n r34d th1s u r34lly n33d t0 g37 l41d.
1f u c4n wr1t3 1t t00.
n°2142
Profil : Internaute
Posté le 31-07-2007 à 00:28:28   answer
 

Skyfighter a écrit :

J'vais tenter de le réexpliquer pour que ce soit compréhensible ;)
Si on part sur du (0x, 0y, 0z), a l'init mon vaisceau à son centre en (0, 0, 0) et se dirige vers (0, 1, 0).
Quand j'appuie sur la fleche gauche ou la droite, je fais varier la rotation suivant l'axe 0y (dans le repère du vaisceau).
Quand j'appuie sur bas ou sur haut, je change la direction de l'appareil en prenant en compte la rotation en 0y.

Ah oui, je crois que j'ai compris :)
En gros, Oy est l'axe de roulis, et quand tu disais qu'une rotation selon Oy faisait seulement pivoter le vaisseau c'était dans le sens "ne change pas sa direction" ?  Je ne sais pas pourquoi, j'étais persuadé que c'était l'axe de lacet, et forcément je trouvais ça bizarre. :D
 
En fait, dans ce cas, je pense que tu n'as même pas à traiter de manière différente la rotation selon l'axe Oy des autres rotations.  Logiquement, même si tu tournes ton vecteur direction selon Oy (dans repère de l'avion), il ne changera pas de toute façon.  Bref, l'idée c'est de garder un quaternion pour la rotation en cours, et de le multiplier par la nouvelle rotation à chaque changement...  En reprenant ton code du début, ça donnerait un truc comme ça :

Code :
  1. void
  2. Ship::update()
  3. {
  4.   float  spd = speed_ * Framerate::instance().get_delay(),
  5.                  rotation_y = 1.0 * Framerate::instance().get_delay(),
  6.                  rotation_x = 1.0 * Framerate::instance().get_delay();
  7.   osg::Quat rot = transf_->getAttitude();
  8.   if (left_key)
  9.     rot *= osg::Quat( 0, -sin(rotation_y), 0, cos(rotation_y) );
  10.   if (right_key)
  11.     rot *= osg::Quat( 0,  sin(rotation_y), 0, cos(rotation_y) );
  12.   if (up_key)
  13.     rot *= osg::Quat( -sin(rotation_x), 0, 0, cos(rotation_x) );
  14.   if (down_key)
  15.     rot *= osg::Quat(  sin(rotation_x), 0, 0, cos(rotation_x) );
  16.   // Si nécessaire (à cause des erreurs d'arrondi) :
  17.   rot /= length(rot);
  18.   direction_ = rot * osg::Vec3d(0, 1, 0);
  19.   transf_->setAttitude(rot);
  20.   transf_->setPosition(transf_->getPosition() + direction_ * spd);
  21. }

Bon, j'ai normalisé rot en le divisant par sa longueur (en espérant que length fait bien ce qu'il faut), après ce n'est peut être pas nécessaire.  L'idée, c'est qu'un quaternion utilisé pour la rotation doit être unitaire, ce qui peut ne plus être le cas après les multiplications s'il y a eu des erreurs d'arrondi.  Maintenant, ça dépend de comment tout ça est implémenté, si ça se trouve le quaternion est automatiquement normalisé quand on l'utilise pour une rotation par exemple...
 
Sinon, toutes les rotations sont contenues dans le transf_, et il n'y a pas besoin du orient_ (à moins que ces deux rotations s'appliquent à deux choses différentes, auquel cas je n'ai pas tout compris).  Pour retrouver ta direction, il faut trouver le conjugué de ton vecteur d (pris comme un quaternion dont la composante en w est nul) par le quaternion r correspondant à la rotation : d' = r d r^-1.   Si je ne me plante pas l'opérateur * des quaternions appliqué sur un vecteur dans OSG semble effectuer la conjugaison, donc il suffit d'effectuer r*d (ou d est la direction initiale, ie. (0,1,0)) pour obtenir ton vecteur direction correctement orienté (sinon, tu peux le faire à la main, ie. avec un truc du genre  rot*osg::Quat(0,1,0,0)*rot.inverse()  et ignorer la composante w du résultat, mais c'est plus lourd ^^).
 
EDIT: Par contre, si tu appuis à la fois sur haut et gauche par exemple, ça va d'abord appliquer la rotation à gauche (selon Oy), puis celle vers le haut (selon Ox, enfin ça dépend de comment sont définis tes axes), ce qui est purement arbitraire.  Si les rotations élémentaires sont petites, ça ne pose pas de problème, sinon il faudrait faire un cas spécial avec une seule rotation selon un axe transversal je pense (enfin c'est du détail pour l'instant je pense, c'est comme le coup d'avancer dans la direction de l'avion sans tenir compte de la gravité et de la portance ;)).
 
EDIT2: Ah oui, bien sur rot doit être initialisé comme la rotation identité (rotation d'angle nul), ie par le quaternion (0,0,0,1), si tu ne veux pas commencer avec une rotation dès le départ.
 

Citation :

Effectivement ça explique des choses... encore que j'ai du mal avec ta première formule. Faut que me trouve un bon tutorial sur les quaternions ^^;

Bein il y a toute une théorie derrière, très intéressante si tu aimes bien l'algèbre :)
 
Pour ma formule, l'idée c'est que les composantes (x,y,z,w) d'un quaternion "de rotation" sont définies comme suit :
- (x,y,z) définit un vecteur directeur de l'axe de la rotation
- w vaut cos(angle/2)
- le quaternion doit être unitaire, ce qui implique que la longueur du vecteur directeur (x,y,z) vaut sin(angle/2)
Donc pour une rotation d'axe défini par le vecteur directeur unitaire (u,v,w), et d'angle a, le quaternion a les coordonnées (u sin(a/2), v sin(a/2), w sin(a/2), cos(a/2)).  C'est un peu comme avec les nombres complexes en 2D si tu veux : une rotation d'angle a est représentée par le complexe unitaire e^(ia), dont les coordonnées sont (cos(a), sin(a)).
 
Après pour retrouver les angles autour d'axes fixés (par exemple Ox, Oy et Oz) et non de l'axe de rotation lui même, il faut faire un peu de trigo ;)
 

Citation :

Si justement, direction_ est modifié que quand haut ou bas sont appuyés.

Oups, je voulais dire gauche/droite et non pas haut/bas ;)
Mais si j'ai bien compris ton mode de déplacement, c'est effectivement normal.


Message édité par Shao Onos le 31-07-2007 à 00:56:25

---------------
Beautés démoniaques, manipulant le monde dans l'ombre [...], on les appelait le clan de Marjoly, haïes, craintes et vénérées depuis la nuit des temps.
Mais même elles possédaient un défaut fatal...  elles étaient toutes d'une stupidité sans limite.
n°2143
Profil : Internaute
Posté le 31-07-2007 à 11:18:04   answer
 

J'ai bricolé un peu mon code ce matin... la nouvelle fournée donne ça:

Code :
  1. float  spd = speed_ * Framerate::instance().get_delay();
  2.   osg::Vec3d tmp = osg::Vec3d(0, 1, 0);
  3.   osg::Quat rot;
  4.   if (left_key)
  5.     rotation_.y() -= 1.0 * Framerate::instance().get_delay();
  6.   if (right_key)
  7.     rotation_.y() += 1.0 * Framerate::instance().get_delay();
  8.   if (up_key)
  9.   {
  10.     rotation_.z() += cos(M_PI / 2 + rotation_.y())
  11.       * Framerate::instance().get_delay();
  12.     rotation_.x() += sin(M_PI / 2 + rotation_.y())
  13.       * Framerate::instance().get_delay();
  14.   }
  15.   if (down_key)
  16.   {
  17.     rotation_.z() -= cos(M_PI / 2 + rotation_.y())
  18.       * Framerate::instance().get_delay();
  19.     rotation_.x() -= sin(M_PI / 2 + rotation_.y())
  20.       * Framerate::instance().get_delay();
  21.   }
  22.   rot.makeRotate(rotation_.x(), osg::Vec3d(1, 0, 0),
  23.   0, osg::Vec3d(0, 1, 0),
  24.   rotation_.z(), osg::Vec3d(0, 0, 1));
  25.   direction_ = rot * tmp;
  26.   transf_->setAttitude(osg::Quat(rotation_.y(), osg::Vec3d(0, 1, 0))
  27.         * rot);
  28.   transf_->setPosition(transf_->getPosition() + direction_ * spd);


Ca m'a l'air de marcher pas trop mal mais j'ai encore des comportements bizaroïdes sur les rotations selon 0x et 0z de temps en temps...
 

Citation :


En gros, Oy est l'axe de roulis, et quand tu disais qu'une rotation selon Oy faisait seulement pivoter le vaisseau c'était dans le sens "ne change pas sa direction" ?  Je ne sais pas pourquoi, j'étais persuadé que c'était l'axe de lacet, et forcément je trouvais ça bizarre. :D  


Voila, donc 0y axe de roulis, 0x axe de tangage et 0z axe de lacet dans mon cas :D  
 

Citation :


Sinon, toutes les rotations sont contenues dans le transf_, et il n'y a pas besoin du orient_ (à moins que ces deux rotations s'appliquent à deux choses différentes, auquel cas je n'ai pas tout compris).


C'est ce que je faisais au début... je l'ai remis comme ça maintenant. Mais ce qui m'embête c'est que je comprends pas trop comment je suis sensé gérer le quaternion. J'ai besoin dans un premier temps d'une rotation que sur 0x et 0z pour faire tourner mon vecteur de direction. Après il faut que j'ajoute la rotation en 0y pour le vaisseau. Le problème c'est que selon que je multiplie le quaternion d'origine à gauche ou à droite j'ai pas l'impression d'avoir le même résultat ce qui me paraît bizarre (du coup par rapport au code au dessus j'ai rajouté un makeRotate après la rotation du vecteur direction_ pour avoir la paix mais je trouve ça moche). Mais plus j'y pense plus je me dis qu'en fait comme j'ai un décor sans source de lumière et sans textures c'est juste la géométrie du décor qui joue avec mes nerfs...


---------------
1f u c4n r34d th1s u r34lly n33d t0 g37 l41d.
1f u c4n wr1t3 1t t00.
n°2144
Profil : Modérateur
Posté le 31-07-2007 à 16:24:02   answer
 

Skyfighter a écrit :

Bein disons que OSG propose des timers que j'utilise pour la boucle d'attente (et que j'utiliserais volontier pour de la gestion clavier et cie) mais pas d'équivalent à SDL_wait à ma connaissance. En fait je voulais forcer le jeu à tourner à 60fps pour me simplifier la vie vis à vis des vitesses de déplacement mais plus je code plus je me rends compte que de toute façon je mets la fréquence d'affichage dans tous les calculs donc bon...


Je ne pense pas que ce soit une bonne idée (je suis même sûr du contraire). Si tu rates un timer, ça va complètement fausser le tout.

 

Edit : ah oui, à propos, important : tu ne dois lire le timer qu'UNE FOIS par mise à jour de la physique. Le temps doit être figé durant le calcul, sinon tu vas avoir des surprises. Il faut lire le timer, le mettre dans une variable, et utiliser la variable ensuite. Si tu calcules une direction à l'instant t et une autre à l'instant t+quelques fractions de millisecondes, tu ne verras rien au début, mais tu risques d'avoir de très mauvaises surprises.

 

___________________________________________

 


Pour le reste, je n'ai pas tout lu, mais à vue de nez, tu n'utilises pas les quaternions correctement. J'ai l'impression que tu gardes des angles de rotation suivant x, y et z, que tu mets à jour avec les touches, puis que tu calcules le quaternion correspondant, que tu utilises pour calculer la matrice de rotation. Ca ne fonctionne PAS. Dès que tu t'approches des points problématiques (les pôles de la sphère), ça va merder.

 

Les quaternions ont été créés justement pour ne plus utiliser d'angles absolus de rotation. Ce qu'il faut faire, c'est concerver l'angle de ton appareil sous la forme d'un quaternion, créer un quaternion correspondant à la manoeuvre de ton avion, et *multiplier* les deux quaternions. C'est une représentation de l'espace des rotations.

 

Petit rappel, l'espace des rotations a trois dimensions, mais a une structure très particulière. On peut le représenter de différentes façons :
- angles d'Euler ou équivalent (ce que tu as l'air d'utiliser) : 3 valeurs, donc indépendantes, mais difficultés aux pôles
- quaternions : 4 valeurs, mais une relation liant ces quatres valeurs (d'où 4-1 = 3 dimensions)
- matrices de rotation : 9 valeurs, et 6 relations (3 pour les normes, 3 pour l'orthogonalité)

 

___________________________________________

 


Ceci dit, j'ai programmé de nombreux trucs de type simulateur de vol (spatial et aérien), et je n'ai personnellement jamais apprécié les quaternions. J'ai un système bien à moi (quoique probablement à d'autres ;) ) et qui fonctionne, basé sur la représentation matricielle.

 

Une position/orientation dans l'espace est définie par une matrice conforme 4X4 (ou de façon quasi-équivalente une matrice de rotation et une translation). Je vais prendre un exemple simple (sans physique solide) pour illustrer. Je transforme tous les mouvements dans le repère de l'avion (roulis, tangage, lacet, poussée) dans une matrice conforme. A noter que cette matrice est largement tabulable au besoin (ça tournait sur un pauvre TO8 ;) ). Je multiplie la matrice courante de position avec la matrice de mouvement.

 

Comme les quaternions, cependant, il y a une relation (en fait six) entre les 9 coefficients de la partie rotation pour que la matrice reste orthonormée. Avec les imprécisions des multiplications, il peut y avoir une dérive légère avec le temps. Du coup, je reprojetais (à l'occasion, ou chaque fois si nécessaire) la matrice qui fait office de matrice de rotation sur l'espace des matrices orthonormées. La solution la plus simple, en supposant que M=(u, v, w) :
- on normalise u
- on enlève à v sa composante suivant u, de sorte que u et v soit orthogonaux
- on normalise v
- on impose w=u^v

 

Maintenant, le meilleur : OpenGL est complètement prévu pour faire ces calculs plus vite que ton processeur, justement ;) Avec la pile OpenGL, tu peux effectuer toutes les transformations à coup de glRotate et glTranslate sans avoir à les programmer. Du coup, ça en devient réellement tout simple...

 

Même quand je faisais du tracking humain (avec des dizaines de degrés de libertés en chaîne), j'utilisais une variante de ce truc, ça marche vraiment bien, et ça m'a toujours semblé plus naturel que les quaternions. Après, c'est une question de feeling.

Message cité 1 fois
Message édité par Koren le 31-07-2007 à 16:26:10
n°2145
Profil : Internaute
Posté le 31-07-2007 à 19:45:49   answer
 

Koren a écrit :

Maintenant, le meilleur : OpenGL est complètement prévu pour faire ces calculs plus vite que ton processeur, justement ;) Avec la pile OpenGL, tu peux effectuer toutes les transformations à coup de glRotate et glTranslate sans avoir à les programmer. Du coup, ça en devient réellement tout simple...

Oui, sauf que tu dois toujours orthogonaliser de temps en temps si je ne m'abuse (je vois pas trop comment opengl t'en dispenser sachant qu'il accepte (à peu près) n'importe quoi comme matrice de transformation, et qu'il ne peut pas être à l'abri d'erreurs d'arrondi).  Enfin, ça reste plus rapide que de faire tous les calculs à la main, je le reconnais...
 
Cela dit, je pense que la seule véritable raison pour laquelle il utilise des quaternions ici, c'est que la bibliothèque qu'il utilise (OSG) semble n'utiliser que ça en natif...
 

Skyfighter a écrit :

C'est ce que je faisais au début... je l'ai remis comme ça maintenant. Mais ce qui m'embête c'est que je comprends pas trop comment je suis sensé gérer le quaternion. J'ai besoin dans un premier temps d'une rotation que sur 0x et 0z pour faire tourner mon vecteur de direction. Après il faut que j'ajoute la rotation en 0y pour le vaisseau. Le problème c'est que selon que je multiplie le quaternion d'origine à gauche ou à droite j'ai pas l'impression d'avoir le même résultat ce qui me paraît bizarre (du coup par rapport au code au dessus j'ai rajouté un makeRotate après la rotation du vecteur direction_ pour avoir la paix mais je trouve ça moche).

La multiplication des quaternions n'est pas commutative, sauf dans certains cas particulier (ex. deux rotations autour du même axe).  Si tu as l'impression qu'elle l'est, c'est que tu es sans doute tombé sur l'un de ces cas particuliers ;)
Si tu as deux quaternions q1 et q2, qui représentent deux rotations r1 et r2, le produit q1*q2 représente la composé r1 o r2 de tes deux rotations (ie. tu appliques r2, puis r1 dans cet ordre, ce qui est différent de r1 puis r2 en général).  Pour appliquer une rotation dans le repère local de ton avion, il faut effectuer cette rotation en premier (avant que le repère n'ait été changé par l' "ancienne" rotation).  Donc pour résumer :
- Si q représente la rotation en cours de ton avion (avant d'appuyer sur une touche)
- Si r représente la rotation autour de l'axe (Oy) (ou (Ox) ou (Oz), c'est selon) local à l'avion que tu veux "ajouter" à ta rotation en cours (suite à une pression sur gauche par exemple)
==> la nouvelle rotation pour ton avion sera  q' = q*r
 
Maintenant, je viens d'aller voir les sources de OSG (la doc étant complètement insuffisante), et l'opérateur * est défini à l'envers !!!
Donc en fait, dans ton programme, il faut calculer q' = r*q (et donc forcément le code que j'ai mis plus haut ne marche pas, ou plutôt multiplie dans le mauvais sens, ce qui implique une rotation dans le repère global et non dans le repère local)...
 
J'en ai profité pour faire un test avec un exemple tout simple (caméra fixe, avion qui fonce tout droit devant lui, gauche/droite modifient le roulis, et  haut/bas le tangage).   Je mets le code complet si tu veux voir ce que ça fait (mais bon, comme je ne connais pas OSG, c'est principalement du copier/coller de bouts de tutoriels modifiés jusqu'à ce que ça marche ^^), mais la partie qui t'intéresse est la méthode Scene::update (lignes 44 à 66).  En théorie le code compile sans warning, et j'ai fait l'édition de lien avec "-losgProducer -losg" sur un système Debian/sid (linux).
[EDIT : par contre, j'ai codé les pas de déplacement/rotation en dur et je ne les adapte pas au framerate, donc à modifier si ça va trop vite ou trop lentement]

Code :
  1. #include <cmath>
  2. #include <osgProducer/Viewer>
  3. #include <osg/Group>
  4. #include <osg/ShapeDrawable>
  5. #include <osg/PositionAttitudeTransform>
  6. bool up,down,left,right;
  7. class Scene {
  8. public:
  9.   Scene() { create_scene(); }
  10.   ~Scene() { }
  11.   void attach(osgProducer::Viewer& viewer) { viewer.setSceneData(_root); }
  12.   void update();
  13.  
  14. private:
  15.   void create_scene();
  16.   osg::Group *_root;
  17.   osg::PositionAttitudeTransform *_transf;
  18. };
  19. void Scene::create_scene() {
  20.   osg::Cone *cone = new osg::Cone(osg::Vec3d(0,0,0), 0.2, 1);
  21.   cone->setRotation(osg::Quat(-M_SQRT2/2,0,0,M_SQRT2/2));
  22.   osg::Box  *box1 = new osg::Box (osg::Vec3f(0,0.2,0), 1, 0.25, 0.05);
  23.   osg::Box  *box2 = new osg::Box (osg::Vec3f(0,-0.1,0.2), 0.05, 0.2, 0.2);
  24.   osg::ShapeDrawable *corps   = new osg::ShapeDrawable(cone);
  25.   osg::ShapeDrawable *ailes   = new osg::ShapeDrawable(box1);
  26.   osg::ShapeDrawable *aileron = new osg::ShapeDrawable(box2);
  27.   osg::Geode *avion = new osg::Geode();
  28.   avion -> addDrawable(corps);
  29.   avion -> addDrawable(ailes);
  30.   avion -> addDrawable(aileron);
  31.   _transf = new osg::PositionAttitudeTransform();
  32.   _transf->setPosition(osg::Vec3d(0, 0, 0));
  33.   _transf->addChild(avion);
  34.   _root = new osg::Group;
  35.   _root->addChild(_transf);
  36. }
  37. void Scene::update() {
  38.   float vitesse = 0.001,  v_roty = 0.001, v_rotx=0.001;
  39.   osg::Quat rotation = _transf->getAttitude();
  40.   osg::Vec3f position = _transf->getPosition();
  41.   float rotx, roty;
  42.   // Les rotations
  43.   rotx=roty=0;
  44.   if (left)  roty = -v_roty;
  45.   if (right) roty =  v_roty;
  46.   if (up)    rotx = -v_rotx;
  47.   if (down)  rotx =  v_rotx;
  48.   rotation = osg::Quat(roty, osg::Vec3d(0,1,0), rotx, osg::Vec3d(1,0,0), 0,osg::Vec3d(0,0,1))
  49.     * rotation;
  50.   rotation /= rotation.length();  // normalisation...  nécessaire ??
  51.   // Le déplacement
  52.   position += rotation * osg::Vec3d(0,1,0) * vitesse;
  53.   // La mise à jour de la transfo
  54.   _transf->setAttitude(rotation);
  55.   _transf->setPosition(position);
  56. }
  57. class KeyboardEventHandler : public osgGA::GUIEventHandler {
  58. public:
  59.   KeyboardEventHandler() {}
  60.   virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& ) {
  61.     switch(ea.getEventType()) {
  62.     case(osgGA::GUIEventAdapter::KEYDOWN):
  63.       switch(ea.getKey()) {
  64.       case osgGA::GUIEventAdapter::KEY_Left:
  65. left = true;
  66. return true;
  67.       case osgGA::GUIEventAdapter::KEY_Right:
  68. right= true;
  69. return true;
  70.       case osgGA::GUIEventAdapter::KEY_Up:
  71. up = true;
  72. return true;
  73.       case osgGA::GUIEventAdapter::KEY_Down:
  74. down= true;
  75. return true;
  76.       default:
  77. return false;
  78.       }
  79.     case(osgGA::GUIEventAdapter::KEYUP):
  80.       switch(ea.getKey()) {
  81.       case osgGA::GUIEventAdapter::KEY_Left:
  82. left = false;
  83. return true;
  84.       case osgGA::GUIEventAdapter::KEY_Right:
  85. right= false;
  86. return true;
  87.       case osgGA::GUIEventAdapter::KEY_Up:
  88. up = false;
  89. return true;
  90.       case osgGA::GUIEventAdapter::KEY_Down:
  91. down= false;
  92. return true;
  93.       default:
  94. return false;
  95.       }
  96.     default:
  97.       return false;
  98.     }
  99.   }
  100.   virtual void accept(osgGA::GUIEventHandlerVisitor& v) { v.visit(*this); }
  101. };
  102. int main(int argc, char **argv) {
  103.   // use an ArgumentParser object to manage the program arguments.
  104.   osg::ArgumentParser arguments(&argc,argv);
  105.  
  106.   // initialize the viewer.
  107.   osgProducer::Viewer viewer(arguments);
  108.   viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);
  109.   // get details on keyboard and mouse bindings used by the viewer.
  110.   viewer.getUsage(*arguments.getApplicationUsage());
  111.   // Create the scene
  112.   Scene* scene = new Scene;
  113.   scene->attach(viewer);
  114.   // Keyboard handler
  115.   up = down = left = right = false;
  116.   KeyboardEventHandler* keh = new KeyboardEventHandler;
  117.   viewer.getEventHandlerList().push_front(keh);
  118.    
  119.   // Display loop
  120.   viewer.realize();
  121.   while( !viewer.done() ) {
  122.     viewer.sync();
  123.     scene->update();
  124.     viewer.update();
  125.     viewer.frame();
  126.   }
  127.  
  128.   // Finish
  129.   viewer.sync();
  130.   viewer.cleanup_frame();
  131.   viewer.sync();
  132.   return 0;
  133. }

Message cité 1 fois
Message édité par Shao Onos le 31-07-2007 à 19:57:56

---------------
Beautés démoniaques, manipulant le monde dans l'ombre [...], on les appelait le clan de Marjoly, haïes, craintes et vénérées depuis la nuit des temps.
Mais même elles possédaient un défaut fatal...  elles étaient toutes d'une stupidité sans limite.
n°2146
Profil : Modérateur
Posté le 31-07-2007 à 20:48:46   answer
 

Shao Onos a écrit :

Oui, sauf que tu dois toujours orthogonaliser de temps en temps si je ne m'abuse


Yep, mais OpenGL dérive peu, et une fois par seconde, c'est largement suffisant. Il y a moyen de se débrouiller pour qu'il le fasse, mais c'est aussi simple de le faire faire par le processeur.

 

Edit : je ne sais pas si OSG n'accepte que les quaternions (ça me surprendrait) mais on peut construire un quaternion à partir de la matrice de rotation au besoin, semble-t-il).

 

OSG, ça va faire une ligne de plus dans le prochain CV ? ;)

Message cité 2 fois
Message édité par Koren le 31-07-2007 à 20:50:22
n°2147
Profil : Internaute
Posté le 31-07-2007 à 21:30:19   answer
 

Koren a écrit :

Edit : je ne sais pas si OSG n'accepte que les quaternions (ça me surprendrait) mais on peut construire un quaternion à partir de la matrice de rotation au besoin, semble-t-il).

Effectivement, il y a une classe MatrixTransform qui semble faire la même chose que PositionAttitudeTransform mais avec des matrices (j'ai pas regardé en détail cela dit).  C'est sans doute ce qu'il y a de mieux à utiliser effectivement...
 
Par contre, pour s'amuser à construire le quaternion à partir de la matrice dans ce genre de situation, faut vraiment ne pas supporter les quaternions ;) (ok, ça simplifie le déplacement, mais ça doit entraîner pas mal de surcoûts en contre-partie)


---------------
Beautés démoniaques, manipulant le monde dans l'ombre [...], on les appelait le clan de Marjoly, haïes, craintes et vénérées depuis la nuit des temps.
Mais même elles possédaient un défaut fatal...  elles étaient toutes d'une stupidité sans limite.
n°2148
Profil : Internaute
Posté le 31-07-2007 à 22:04:22   answer
 

Koren a écrit :


Edit : je ne sais pas si OSG n'accepte que les quaternions (ça me surprendrait) mais on peut construire un quaternion à partir de la matrice de rotation au besoin, semble-t-il).


OSG propose les quaternions, la représentation matricielle et de quoi faire des conversions. Après y'a tout une collection de méthodes pour générer des quaternions à partir des angles d'Euler. Mais comme ces fonctions sont justement jamais commentées dans la doc :whistle:
Par contre pour ce qui est de la caméra c'est purement de la matrice... j'avais galéré par mal dessus a début puis j'ai fini par trouver un tutorial où en gros ils expliquent qu'il suffit de placer un PositionAttitudeTransform à la position voulu en fils du noeud suivi par la caméra et de récupérer ses coordonnées dans le monde à coup de visitor.

 
Citation :


OSG, ça va faire une ligne de plus dans le prochain CV ? ;)


Ouais... en fait plus sérieusement je voulais utiliser une bibliothèque qui me gère uniquement le graphe de scène (pour pas me faire chier avec les rotations justement :whistle:) et les entrées pour faire par dessus une bibliothèque évènementielle (d'où aussi mon trip sur la synchronisation vis à vis du framerate). Fin d'année on a fait un peu de théorique sur les langages acteurs et à la rentrée on va attaquer les SMA. Donc c'est histoire de voir un peu toutes les problématiques de synchronisation planquées tout en faisant de la 3D...

 

Pis bon... DirectX fallait que je code sous Windows pour finir avec du code pas portable et Ogre je peux plus le voir depuis que j'ai vu que les noeuds du graphe de scène sont indexés sur des chaînes de caractères...

 
Citation :


Pour le reste, je n'ai pas tout lu, mais à vue de nez, tu n'utilises pas les quaternions correctement. J'ai l'impression que tu gardes des angles de rotation suivant x, y et z, que tu mets à jour avec les touches, puis que tu calcules le quaternion correspondant, que tu utilises pour calculer la matrice de rotation. Ca ne fonctionne PAS. Dès que tu t'approches des points problématiques (les pôles de la sphère), ça va merder.

 

Les quaternions ont été créés justement pour ne plus utiliser d'angles absolus de rotation. Ce qu'il faut faire, c'est concerver l'angle de ton appareil sous la forme d'un quaternion, créer un quaternion correspondant à la manoeuvre de ton avion, et *multiplier* les deux quaternions. C'est une représentation de l'espace des rotations.


Si tu veux on m'avait toujours dit de justement faire de l'absolu plutôt que du relatif parce que les erreurs d'arrondis sont sensés finir par te pourrir complètement la scène. Maintenant faut croire que ceux qui m'ont dit ça utilisent pas de quaternions :whistle:

 
Citation :


Si tu as deux quaternions q1 et q2, qui représentent deux rotations r1 et r2, le produit q1*q2 représente la composé r1 o r2 de tes deux rotations (ie. tu appliques r2, puis r1 dans cet ordre, ce qui est différent de r1 puis r2 en général).  Pour appliquer une rotation dans le repère local de ton avion, il faut effectuer cette rotation en premier (avant que le repère n'ait été changé par l' "ancienne" rotation).


Donc je suis pas fou :sol: :D
Merci pour l'explication je comprends déjà mieux mes problèmes de rotations ;)

Message cité 1 fois
Message édité par Skyfighter le 31-07-2007 à 22:05:29

---------------
1f u c4n r34d th1s u r34lly n33d t0 g37 l41d.
1f u c4n wr1t3 1t t00.
n°2149
Profil : Modérateur
Posté le 01-08-2007 à 01:44:51   answer
 

Skyfighter a écrit :

OSG propose les quaternions, la représentation matricielle et de quoi faire des conversions. Après y'a tout une collection de méthodes pour générer des quaternions à partir des angles d'Euler. Mais comme ces fonctions sont justement jamais commentées dans la doc :whistle:


C'est con, parce que tes commandes (lacet, tangage et roulis) sont EXACTEMENT les trois angles d'euler, donc tu dois pouvoir construire le quaternion de changement d'orientation sans aucun calcul (si tu ne te plantes pas sur les trois axes, mais bon, c'est faisable ;) )

 
Skyfighter a écrit :

Si tu veux on m'avait toujours dit de justement faire de l'absolu plutôt que du relatif parce que les erreurs d'arrondis sont sensés finir par te pourrir complètement la scène. Maintenant faut croire que ceux qui m'ont dit ça utilisent pas de quaternions :whistle:


Tu ne peux pas faire de l'absolu pour ce genre de choses, parce qu'à un moment, la jacobienne se trouve être non inversible, et du coup tu te retrouves dans des situations où deux positions infiniment proches en absolu ne sont pas absolument proche en relatif (c'est un peu difficile à expliquer avec les mains).

 

En l'occurrence, tu te fiches pas mal que les erreurs d'arrondis posent problème, tant que les contraintes (normalisation et orthogonalisation pour les matrices de rotation, ou relation des quaternions) sont vérifiées (et corrigées au besoin). Si ton appareil fait 1km dans un sens, trois loopings, et 1km dans l'autre sens, et se retrouve pas tout à fait à son point de départ, ce n'est pas un vrai problème. En général, tu pilotes par rapport à ton environnement, pas en intégrant les déplacements précédents (si tu vois ce que je veux dire).

 
Skyfighter a écrit :

Donc je suis pas fou :sol: :D
Merci pour l'explication je comprends déjà mieux mes problèmes de rotations ;)


Non, tu n'es pas fou... R(Ox, Pi/2) * R(Oy, Pi/2) * R(Ox, -Pi/2), ça ne donne pas R(Oy, Pi/2) mais R(Oz, +/-Pi/2)... La composition des rotations donne parfois des résultats bizarres si on s'y prend de travers.

 

J'avoue que ça me parait surprenant qu'ils aient programmé la rotation des quaternions à l' "envers", mais je pense que c'est comme pour OpenGL, tout dépend si tu considère que tu tourne le repère local ou le monde autour du repère...

 

Edit : et la doc d'OSG, c'est VRAIMENT de la merde, semble-t-il... Et tant que j'y suis, je ne te l'ai jamais dit, mais ta signature me désole tant je suis capable de la lire sans même un effort :mdr:


Message édité par Koren le 01-08-2007 à 01:46:28
n°2150
Profil : Internaute
Posté le 01-08-2007 à 10:31:20   answer
 

Shao Onos > Y'a un truc que j'ai du mal à comprendre dans ton code en fait. Tu fais jamais de rotation selon l'axe 0z pourtant ça m'a l'air de marcher quand je teste le code. Elle est faite plus ou moins toute seule par la multiplication des quaternions?

 


Koren a écrit :


Tu ne peux pas faire de l'absolu pour ce genre de choses, parce qu'à un moment, la jacobienne se trouve être non inversible, et du coup tu te retrouves dans des situations où deux positions infiniment proches en absolu ne sont pas absolument proche en relatif (c'est un peu difficile à expliquer avec les mains).


En gros, c'est les histoires d'interpolation foireuses qui font que d'un coup ton modèle contorsioniste quoi :??:
(me souviens de choses bizarres dans le genre sur le projet de motion capture sur lequel j'avais bossé... le gars qui s'occupait de la 3D a pas eu le temps de résoudre le problème ce qui fait que de temps en temps les pieds du modèle faisaient des tours complets sur eux-mêmes :D)

 
Citation :


En l'occurrence, tu te fiches pas mal que les erreurs d'arrondis posent problème, tant que les contraintes (normalisation et orthogonalisation pour les matrices de rotation, ou relation des quaternions) sont vérifiées (et corrigées au besoin). Si ton appareil fait 1km dans un sens, trois loopings, et 1km dans l'autre sens, et se retrouve pas tout à fait à son point de départ, ce n'est pas un vrai problème. En général, tu pilotes par rapport à ton environnement, pas en intégrant les déplacements précédents (si tu vois ce que je veux dire).


Je comprends l'idée... par contre je comprends plus comment j'arrivais à faire des sauts dans les premiers Tomb raider ;) :whistle:

 
Citation :


Edit : et la doc d'OSG, c'est VRAIMENT de la merde, semble-t-il...


J'appuie et j'enfonce, mais quelque part je préfère ça aux pointeurs cachés de DirectX. Pis bon... ça reste mieux documenté que la bibliothèque interne de mon dernier stage :whistle:

 
Citation :


Et tant que j'y suis, je ne te l'ai jamais dit, mais ta signature me désole tant je suis capable de la lire sans même un effort :mdr:


+1 vu que je l'ai écrite :cry:
faut que je la change... le problème c'est que les blagues de geeks classiques sont déjà sur-utilisées en général (il n'y a que 10/11 types de personnes dans le monde, tout ça). Après si je vais chercher dans les japanimes c'est pareil et les privates jokes avec les gens que je connais ça fera rire que moi. Repomper du bash ou du bashfr représente un intérêt limité aussi. Quoter du Douglas Adams ou du Tolkien... déjà vu et revu...
Je vais finir par écrire en l33tsp34k un aiku lui même étant une traducion en elfique d'une citation d'un anime de geek genre Battle Programmer Shirase ou Genshiken :sweat:

 

Message cité 1 fois
Message édité par Skyfighter le 01-08-2007 à 10:50:05

---------------
1f u c4n r34d th1s u r34lly n33d t0 g37 l41d.
1f u c4n wr1t3 1t t00.
n°2151
Profil : Modérateur
Posté le 01-08-2007 à 12:09:02   answer
 

Skyfighter a écrit :

Shao Onos > Y'a un truc que j'ai du mal à comprendre dans ton code en fait. Tu fais jamais de rotation selon l'axe 0z pourtant ça m'a l'air de marcher quand je teste le code. Elle est faite plus ou moins toute seule par la multiplication des quaternions?


Exactement (d'ailleurs, si tu regardes les angles d'Euler, c'est X, Y puis X, Z n'apparaît pas).
 

Skyfighter a écrit :

En gros, c'est les histoires d'interpolation foireuses qui font que d'un coup ton modèle contorsioniste quoi :??:
(me souviens de choses bizarres dans le genre sur le projet de motion capture sur lequel j'avais bossé... le gars qui s'occupait de la 3D a pas eu le temps de résoudre le problème ce qui fait que de temps en temps les pieds du modèle faisaient des tours complets sur eux-mêmes :D)


C'est exactement ça (j'avais d'ailleurs le même problème), et c'est le cauchemar des roboticiens, aussi.
 

Skyfighter a écrit :

par contre je comprends plus comment j'arrivais à faire des sauts dans les premiers Tomb raider ;) :whistle:


Euh, ah ? J'ai du manquer un truc, là :mdr:
 

Skyfighter a écrit :

J'appuie et j'enfonce, mais quelque part je préfère ça aux pointeurs cachés de DirectX. Pis bon... ça reste mieux documenté que la bibliothèque interne de mon dernier stage :whistle:


Je n'ai jamais fait de DirectX, mais tu ne donne pas très envie...
 

Skyfighter a écrit :

faut que je la change... le problème c'est que les blagues de geeks classiques sont déjà sur-utilisées en général (il n'y a que 10/11 types de personnes dans le monde, tout ça). Après si je vais chercher dans les japanimes c'est pareil et les privates jokes avec les gens que je connais ça fera rire que moi. Repomper du bash ou du bashfr représente un intérêt limité aussi. Quoter du Douglas Adams ou du Tolkien... déjà vu et revu...
Je vais finir par écrire en l33tsp34k un aiku lui même étant une traducion en elfique d'une citation d'un anime de geek genre Battle Programmer Shirase ou Genshiken :sweat:


Ca peut le faire...
 
Ca me fait sourire, le coup de bashfr, parce que j'y ai dégotté une citation paraît-il d'un boss de Nintendo (mais je n'ai pas pu vérifier, bien que je connaissais la citation) qui disait que l'on n'était pas influencé par les jeux vidéos, sinon depuis Pac-man, on serait tous en train de courir en rond dans des pièces sombres à gober des pillules magiques en écoutant de la musique répétitive. Et j'ai justement trouvé que ça ferait une belle signature ;)

n°2152
Profil : Internaute
Posté le 01-08-2007 à 13:54:05   answer
 

Koren a écrit :


Exactement (d'ailleurs, si tu regardes les angles d'Euler, c'est X, Y puis X, Z n'apparaît pas).


A la premièr lecture j'ai eu l'impression d'être enduit d'erreur :sweat:
Enfin en y réfléchissant vu que c'est la composée je comprends la logique de la chose. Par contre ce qui m'ennuit c'est que maintenant que la direction du vaisseau est bonne j'essaye de lui mettre une tourelle de tir. Pour le coup elle tourne selon 0x et 0z, mais avec ces histoires de conjugués ça tourne tout seul selon selon 0y aussi si je fais des multiplications de quaternions :sweat:
Enfin bon... avec un peu de bol l'opérateur + surchargé pour les quternions fait l'addition des rotations (oui je sais j'ai beaucoup d'espoir :D)...
 

Citation :


Euh, ah ? J'ai du manquer un truc, là :mdr:


Bein dans Tomb raider pour réussir un saut: tu t'approches du bord en marchant, tu t'orientes dans la direction du point de réception, tu fais un pas en arrière... tu cours puis tu sautes. Mais tout ça ça me semble intimement dépendant de la séquence des mouvements précédent le saut :whistle:  
 

Citation :


Ca me fait sourire, le coup de bashfr, parce que j'y ai dégotté une citation paraît-il d'un boss de Nintendo (mais je n'ai pas pu vérifier, bien que je connaissais la citation) qui disait que l'on n'était pas influencé par les jeux vidéos, sinon depuis Pac-man, on serait tous en train de courir en rond dans des pièces sombres à gober des pillules magiques en écoutant de la musique répétitive. Et j'ai justement trouvé que ça ferait une belle signature ;)


A ce compte là y'a des choses sympathiques sur bash... en traduction approximative ça donne ça:
"Y'a 5 ans mon pote était vierge, aujourd'hui il est marié et il a deux enfants. Vous allez le sortir un jour Duke Nukem Forever?" :D  


---------------
1f u c4n r34d th1s u r34lly n33d t0 g37 l41d.
1f u c4n wr1t3 1t t00.
n°2153
Profil : Modérateur
Posté le 01-08-2007 à 14:41:00   answer
 

Skyfighter a écrit :

Enfin bon... avec un peu de bol l'opérateur + surchargé pour les quternions fait l'addition des rotations (oui je sais j'ai beaucoup d'espoir :D)...


Je crois que c'est * qui était surchargé (et si tu les mets du bon côté, ça devrait effectivement fonctionner)
 

Skyfighter a écrit :

Bein dans Tomb raider pour réussir un saut: tu t'approches du bord en marchant, tu t'orientes dans la direction du point de réception, tu fais un pas en arrière... tu cours puis tu sautes. Mais tout ça ça me semble intimement dépendant de la séquence des mouvements précédent le saut :whistle:


Ah, entendu :) Mais ça ne dérive pas à ce point-là, de toute façon... C'est quelque chose comme du 0.00000000001 par pas de simulation, tu ne le verras que sur de très très longues séquences.
 

Skyfighter a écrit :

A ce compte là y'a des choses sympathiques sur bash... en traduction approximative ça donne ça:
"Y'a 5 ans mon pote était vierge, aujourd'hui il est marié et il a deux enfants. Vous allez le sortir un jour Duke Nukem Forever?" :D


Duke Nukem Whenever est une source inépuisable de plaisanteries, de toute façon...

n°2154
Profil : Internaute
Posté le 01-08-2007 à 16:23:30   answer
 

Skyfighter a écrit :

A la premièr lecture j'ai eu l'impression d'être enduit d'erreur :sweat:
Enfin en y réfléchissant vu que c'est la composée je comprends la logique de la chose. Par contre ce qui m'ennuit c'est que maintenant que la direction du vaisseau est bonne j'essaye de lui mettre une tourelle de tir. Pour le coup elle tourne selon 0x et 0z, mais avec ces histoires de conjugués ça tourne tout seul selon selon 0y aussi si je fais des multiplications de quaternions :sweat:
Enfin bon... avec un peu de bol l'opérateur + surchargé pour les quternions fait l'addition des rotations (oui je sais j'ai beaucoup d'espoir :D)...

Non, comme l'a dit koren, l'opérateur + risque de ne pas te servir à grand chose.  Si par "ajouter" des rotations, tu entends faire l'une puis l'autre, c'est juste une composition (opérateur * donc).
 
Dans le cas de ta tourelle de tir, le plus simple, c'est de profiter de la structure arborescente de la scène sous OSG.  En gros, tu peux utiliser quelque chose comme ça (hum, avec la gestion des fontes à pas fixe du forum, je sens que ça ne va pas être super lisible, mais bon on fait avec ce que l'on a ^^) :

Transformation avion
 +-- Groupe (avion + tourelle)
      +--  Corps/fuselage de ton avion  (un Geode)
      +--  Transformation tourelle
            + Tourelle (un Geode)

En fonctionnant ainsi, la transformation pour l'avion s'applique à tout le groupe (avion et tourelle), de sorte que quand ton avion avance/tourne, la tourelle le suit correctement.  Pour tourner la tourelle, il suffit alors de ne changer que la transformation de la tourelle, et ce dans le repère de l'avion (ie. sans chercher à savoir comment est positionné ton avion) : il s'agit probablement d'une simple rotation selon l'axe (Oz).
 
Il faut juste faire attention à définir la tourelle de sorte que l'axe de rotation coïncide avec l'axe (Oz).  Si tu veux positionner ta tourelle sur une aile, ou l'avancer/la reculer sur l'avion, il faut quand même la définir au centre, puis faire une translation en plus (avec un setPosition, à n'utiliser qu'une seule fois au début, puisque la tourelle ne se déplace pas sur l'avion ensuite)...
 
J'ai fait une version modifiée de mon code en mettant deux tourelles (une sur chaque aile).  J'ai utilisé le même Geode pour les deux tourelles, vu qu'elles sont identiques, par contre il y a bien deux transformations différentes.  Je mets juste le source de la classe Scene, le reste n'ayant pas changé (enfin si, dans la gestion des touches, je mets à jour t1l, t1r pour tourner à gauche/droite la première tourelle, et t2l, t2r pour tourner la seconde).
 
[EDIT : J'ai rajouté une 3ème tourelle arrière sur l'aileron de l'avion (oui je sais c'est ridicule, mais bon ^^), que l'on peut tourner autour de deux axes (lattitude et longitude, ie (Oz) de l'avion et (Ox) de la tourelle, ça me semble être le mode de contrôle le plus naturel, mais on peut changer ça au besoin), avec des limitations sur les angles maximaux autorisés...  Il faut activer t3l, t3r, t3u, ou t3d pour activer la rotation.]

Code :
  1. class Scene {
  2. public:
  3.   Scene() { create_scene(); }
  4.   ~Scene() { }
  5.   void attach(osgProducer::Viewer& viewer) { viewer.setSceneData(_root); }
  6.   void update();
  7.  
  8. private:
  9.   void create_scene();
  10.   osg::Group *_root;
  11.   osg::PositionAttitudeTransform *_transf;
  12.   osg::PositionAttitudeTransform *_transf1;
  13.   osg::PositionAttitudeTransform *_transf2;
  14.   osg::PositionAttitudeTransform *_transf3;
  15.   float angle1, angle2, angle3z, angle3x;
  16. };
  17. void Scene::create_scene() {
  18.   // On définit un Geode pour le fuselage (corps + ailes + ailerons)
  19.   osg::Geode *fuselage = new osg::Geode();
  20.   osg::Cone *cone = new osg::Cone(osg::Vec3d(0,0,0), 0.2, 1);
  21.   cone->setRotation(osg::Quat(-M_SQRT2/2,0,0,M_SQRT2/2));
  22.   osg::Box  *box1 = new osg::Box (osg::Vec3f(0,0.2,0), 1, 0.25, 0.05);
  23.   osg::Box  *box2 = new osg::Box (osg::Vec3f(0,-0.1,0.2), 0.05, 0.2, 0.2);
  24.   osg::ShapeDrawable *corps   = new osg::ShapeDrawable(cone);
  25.   osg::ShapeDrawable *ailes   = new osg::ShapeDrawable(box1);
  26.   osg::ShapeDrawable *aileron = new osg::ShapeDrawable(box2);
  27.   fuselage -> addDrawable(corps);
  28.   fuselage -> addDrawable(ailes);
  29.   fuselage -> addDrawable(aileron);
  30.   // On définit un Geode pour tourelle (centrée comme il faut)
  31.   osg::Geode *tourelle1 = new osg::Geode();
  32.   osg::Box *box3 = new osg::Box (osg::Vec3f(0,0.2,0), 0.1, 0.5, 0.1);
  33.   osg::ShapeDrawable * canon1 = new osg::ShapeDrawable(box3);
  34.   canon1 -> setColor(osg::Vec4d(1,0,0,0));
  35.   tourelle1 -> addDrawable(canon1);
  36.   // La même en bleu (tourelle arrière)...
  37.   osg::Geode *tourelle2 = new osg::Geode();
  38.   osg::ShapeDrawable * canon2 = new osg::ShapeDrawable(box3);
  39.   canon2 -> setColor(osg::Vec4d(0,0,1,0));
  40.   tourelle2 -> addDrawable(canon2);
  41.   // On produit deux objets transforms pour les tourelles
  42.   // On en profite pour les positioner comme il faut sur l'avion
  43.   _transf1 = new osg::PositionAttitudeTransform();
  44.   _transf1-> addChild(tourelle1);
  45.   _transf1->setPosition(osg::Vec3d(-0.4, 0.25, 0.05));
  46.   _transf2 = new osg::PositionAttitudeTransform();
  47.   _transf2-> addChild(tourelle1);
  48.   _transf2->setPosition(osg::Vec3d(0.4, 0.25, 0.05));
  49.   // Et un pour la tourelle arrière
  50.   _transf3 = new osg::PositionAttitudeTransform();
  51.   _transf3-> addChild(tourelle2);
  52.   _transf3->setPosition(osg::Vec3d(0, -0.15, 0.35));
  53.   _transf3->setAttitude(osg::Quat(0,0,1,0));
  54.   angle1=angle2=angle3x=0;
  55.   angle3z=M_PI_2;  // Il ne faut pas oublier de doubler les angles (cf. explications plus bas)
  56.   // On assemble le tout pour former un avion complet
  57.   osg::Group *avion = new osg::Group();
  58.   avion -> addChild(fuselage);
  59.   avion -> addChild(_transf1);
  60.   avion -> addChild(_transf2);
  61.   avion -> addChild(_transf3);
  62.   // On met notre avion dans un objet transform pour le manipuler
  63.   _transf = new osg::PositionAttitudeTransform();
  64.   _transf->addChild(avion);
  65.   // On créé la scène avec juste l'avion
  66.   _root = new osg::Group;
  67.   _root->addChild(_transf);
  68. }
  69. void Scene::update() {
  70.   float vitesse = 0.001,  v_roty = 0.001, v_rotx=0.001 , v_tourelles=0.001;
  71.   osg::Quat rotation = _transf->getAttitude();
  72.   osg::Vec3f position = _transf->getPosition();
  73.   float rotx, roty;
  74.   // Les rotations
  75.   rotx=roty=0;
  76.   if (left)  roty = -v_roty;
  77.   if (right) roty =  v_roty;
  78.   if (up)    rotx = -v_rotx;
  79.   if (down)  rotx =  v_rotx;
  80.   rotation = osg::Quat(roty, osg::Vec3d(0,1,0), rotx, osg::Vec3d(1,0,0), 0,osg::Vec3d(0,0,1))
  81.     * rotation;
  82.   rotation /= rotation.length();  // normalisation...  nécessaire ??
  83.   // Le déplacement
  84.   position += rotation * osg::Vec3d(0,1,0) * vitesse;
  85.   // La mise à jour de la transfo
  86.   _transf->setAttitude(rotation);
  87.   _transf->setPosition(position);
  88.   // Les tourelles des ailes
  89.   if (t1l) angle1 += v_tourelles;
  90.   if (t1r) angle1 -= v_tourelles;
  91.   if (t2l) angle2 += v_tourelles;
  92.   if (t2r) angle2 -= v_tourelles;
  93.   _transf1->setAttitude(osg::Quat(0,0,sin(angle1),cos(angle1)) );
  94.   _transf2->setAttitude(osg::Quat(0,0,sin(angle2),cos(angle2)) );
  95.   // La tourelle arrière
  96.   if (t3l) angle3z += v_tourelles; if(angle3z > 3*M_PI_4) angle3z=3*M_PI_4;
  97.   if (t3r) angle3z -= v_tourelles; if(angle3z < M_PI_4)   angle3z=  M_PI_4;
  98.   if (t3u) angle3x += v_tourelles; if(angle3x > M_PI_4) angle3x=M_PI_4;
  99.   if (t3d) angle3x -= v_tourelles; if(angle3x < 0)      angle3x=0;
  100.   _transf3->setAttitude(osg::Quat(sin(angle3x),0,0,cos(angle3x)) *
  101.   osg::Quat(0,0,sin(angle3z),cos(angle3z)));
  102. }

Pour tourner les tourelles, on peut utiliser principalement deux méthodes :
- Mémoriser l'angle, et effectuer une rotation "absolue" (sans tenir compte des anciennes valeurs de _transf1 et _transf2)
- Faire comme avec l'avion : multiplier l'ancien quaternion par celui représentant la rotation "relative"
Là, j'ai préféré utiliser la 1ere méthode, ça évite une multiplication de quaternions et la normalisation, et ça permet éventuellement d'imposer facilement des limitations sur l'angle par exemple.  Par contre, avec la formule que j'ai utilisé, l'angle "réel" des tourelles n'est pas angle1 ou angle2, mais le double (cf. les formules que j'avais mis au début).  Ce n'est pas génant, car ça revient à diviser les incréments par deux, mais c'est bon à savoir si tu veux limiter l'angle correctement (et ça explique que les tourelles font 2 tours quand l'avion n'en fait qu'un, alors que les incréments d'angle sont les mêmes).


Message édité par Shao Onos le 01-08-2007 à 17:38:24

---------------
Beautés démoniaques, manipulant le monde dans l'ombre [...], on les appelait le clan de Marjoly, haïes, craintes et vénérées depuis la nuit des temps.
Mais même elles possédaient un défaut fatal...  elles étaient toutes d'une stupidité sans limite.
n°2155
Profil : Internaute
Posté le 01-08-2007 à 17:39:53   answer
 

Shao Onos a écrit :


Dans le cas de ta tourelle de tir, le plus simple, c'est de profiter de la structure arborescente de la scène sous OSG.  En gros, tu peux utiliser quelque chose comme ça (hum, avec la gestion des fontes à pas fixe du forum, je sens que ça ne va pas être super lisible, mais bon on fait avec ce que l'on a ^^) :

Transformation avion
 +-- Groupe (avion + tourelle)
      +--  Corps/fuselage de ton avion  (un Geode)
      +--  Transformation tourelle
            + Tourelle (un Geode)


En fonctionnant ainsi, la transformation pour l'avion s'applique à tout le groupe (avion et tourelle), de sorte que quand ton avion avance/tourne, la tourelle le suit correctement.  Pour tourner la tourelle, il suffit alors de ne changer que la transformation de la tourelle, et ce dans le repère de l'avion (ie. sans chercher à savoir comment est positionné ton avion) : il s'agit probablement d'une simple rotation selon l'axe (Oz).
Il faut juste faire attention à définir la tourelle de sorte que l'axe de rotation coïncide avec l'axe (Oz).  Si tu veux positionner ta tourelle sur une aile, ou l'avancer/la reculer sur l'avion, il faut quand même la définir au centre, puis faire une translation en plus (avec un setPosition, à n'utiliser qu'une seule fois au début, puisque la tourelle ne se déplace pas sur l'avion ensuite)...


C'est ce que j'ai fait. Là où ça se complique c'est que c'est que ma tourelle ne bouge pas que sur un seul axe. En gros dans la scène j'ai deux caméras: l'un qui suit le vaisseau et l'autre que en quelque sorte une vue subjective de la tourelle avec un viseur. La tourelle pouvant tourner en 0y et 0x est controlée idéalement à la souris. Mais bon je devrais pouvoir me débrouiller faut que je joue un peu avec les quaternions pour bien me mettre dedans...

 
Citation :


Pour tourner les tourelles, on peut utiliser principalement deux méthodes :
- Mémoriser l'angle, et effectuer une rotation "absolue" (sans tenir compte des anciennes valeurs de _transf1 et _transf2)
- Faire comme avec l'avion : multiplier l'ancien quaternion par celui représentant la rotation "relative"


En fait étant donné qu'on utilise des quaternions, je pensais essayer une troisième solution: définir les valeurs limites des rotations par des quaternions et retrouver l'inclinaison à coup de slerp. Maintenant je m'embarque ptet dans un truc tordu...

 
Citation :


Là, j'ai préféré utiliser la 1ere méthode, ça évite une multiplication de quaternions et la normalisation, et ça permet éventuellement d'imposer facilement des limitations sur l'angle par exemple.  Par contre, avec la formule que j'ai utilisé, l'angle "réel" des tourelles n'est pas angle1 ou angle2, mais le double (cf. les formules que j'avais mis au début).  Ce n'est pas génant, car ça revient à diviser les incréments par deux, mais c'est bon à savoir si tu veux limiter l'angle correctement (et ça explique que les tourelles font 2 tours quand l'avion n'en fait qu'un, alors que les incréments d'angle sont les mêmes).


Bêtement question temps d'exécution, il vaudrait mieux faire quoi :??:

Message cité 1 fois
Message édité par Skyfighter le 01-08-2007 à 17:41:03

---------------
1f u c4n r34d th1s u r34lly n33d t0 g37 l41d.
1f u c4n wr1t3 1t t00.
n°2156
Profil : Internaute
Posté le 01-08-2007 à 18:05:13   answer