Tutorial 3: interaction avec l'utilisateur avancée

Nous allons voir maintenant comment utiliser un menu pour aider l'utilisateur.

Translation selon Z
Mise en place du menu

Translation selon Z

Lors du dernier tutorial nous avons appris à deplacer des objets mais seulement selon les axes x et y. En effet, ce sont les axes selon lesquells se déplace la souris. Nous allons voir comment ajouter de la profondeur à nos déplacement.

Comme pour les autres opérations, nous allons ajouter les états nécessaires :

typedef enum {
   MNoOperation,
   MPickAndTranslateObject,
   MPickAndRotateObject,
   MPickAndZTranslateObject,
   MTranslateObject,
   MRotateObject,
   MZTranslateObject,

   MOUSEMOVEOPERATIONFORCEENUMSIZEINT = RWFORCEENUMSIZEINT
} MouseMoveOperation;

Maintenant on va indiquer que lorsqu'on appuie sur Ctrl, on veut faire une translation selon Z. On indique ceci dans le gestionnaire de clic gauche de la souris.

static RsEventStatus
HandleLeftButtonDown(RsMouseStatus *mouseStatus)
{
   /* Left mouse button down event handling... */

   if (mouseStatus->shift)
   {
      MouseMoveAction = MPickAndRotateObject;
   }
   else if (mouseStatus->control) //si on appuie sur la touche contrôle
   {
      MouseMoveAction = MPickAndZTranslateObject;
   }
   else
   {
      MouseMoveAction = MPickAndTranslateObject;
   }

   return rsEVENTPROCESSED;
}

Pour faire du code propre nous allons créer une foncion PickAtomic qui évitera un surplus de code dans la fonction Idle():

/**
fonction qui permet d'indiquer la prochaine opération à faire si on trouve un objet avec un clic gauche
*/

static void
PickAtomic(MouseMoveOperation nextOp)
{
   PickedAtomic = RwCameraPickAtomicOnPixel(Camera,&MousePos);
   if(PickedAtomic) // si on trouve quelque chose
   {
      //on récupère la BB
      AtomicGetBBox(PickedAtomic,&PickBox);
      //on sauvegarde
      OldPos=MousePos;
      //on indique l'action
      MouseMoveAction=nextOp;
   }
   else
   {
      MouseMoveAction = MNoOperation;
   }
}

Ensuite, il faut mettre les nouveaux événements en place dans Idle():

switch(MouseMoveAction)
{
case MPickAndTranslateObject :
   PickAtomic(MTranslateObject);
   break;

case MTranslateObject :
   TranslateAtomic();
   break;

case MPickAndRotateObject :
   PickAtomic(RotateObject);
   break;

case MRotateObject :
   RotateAtomic();
   break;

case MPickAndZTranslateObject:
   PickAtomic(MZTranslateObject);
   break;

case MZTranslateObject:
   ZTranslateAtomic();
   break;

default:
   PickedAtomic = NULL;
   break;
}

Il ne reste plus qu'à écrire la fonction ZTranslateAtomic()

static void
ZTranslateAtomic(void)
{
   RwReal dy;
   RwFrame *f;
   RwV3d at;

   f=RwCameraGetFrame(Camera);
   //deplacement
   dy=OldPos.y-MousePos.y;
   //on recupère le 'look at' vecteur celui dirigé selon Z
   at=*RwMatrixGetAt(RwFrameGetMatrix(f));
   //on indique la longeur du déplacement
   RwV3dScale(&at,&at,dy*0.01f);
   //on va réellement faire la translation
   f = RpAtomicGetFrame(PickedAtomic);
   //rwCOMBINEPOSTCONCAT indique qu'on fait l'opération apres toutes les autres
   RwFrameTranslate(f,&at,rwCOMBINEPOSTCONCAT);
   OldPos = MousePos;
}

On vient d'ajouter un degré de liberté à notre cube. On compile, on exécute, on appuie sur F1 puis on bouge la souris avec 'shift' ou 'ctrl'.

Mise en place du menu

Nous allons voir ici comment mettre en place un menu, comment s'en servir mais aussi quelques fonctions de la caméra.

Pour l'instant dans InitializeMenu(), ne se trouve qu'une seule commande qui sert à ajouter la ligne pour indiquer qu'il faut appuyer sur F pour afficher ou non le nombre de FPS (Frame Per Second).

Il faut aussi parler des plans de clipping de la caméra. Ces plans indique la distance la plus proche et la plus éloignée à laquelle les objets sont visibles pour la caméra. Par défaut le near clipping plane est situé à 0.1.

Nous allons modifier la fonction InitializeMenu() pour ajouter 2 lignes pour pouvoir faire varier la distance la plus éloignée et la distance la plus proche.

static RwBool
InitializeMenu(void)
{
   static RwChar fpsLabel[] = RWSTRING("FPS_F");
   static RwChar nearLabel[] = RWSTRING("Near Clip");
   static RwChar farLabel[] = RWSTRING("Far Clip");

   if( MenuOpen(TRUE, &ForegroundColor, &BackgroundColor) )
   {
      MenuAddEntryBool(fpsLabel, &FPSOn, NULL);
      MenuAddEntryReal(nearLabel, &NearClip,NULL,0.1f,10.0f,0.1f);
      MenuAddEntryReal(farLabel, &FarClip,NULL,10.0f,100.0f,1.0f);

      return TRUE;
   }

   return FALSE;
}

Nous avons donc ajouté 2 entrées au menu. Ces 2 entrées travaillent avec des Real. Il faut donc ajouter les deux variables que sont NearClip et FarClip.

static RwReal NearClip = 0.1f;
static RwReal FarClip = 30.0f;

Il faut maintenant indiquer qu'on changer les plans de clipping. Ceci est réalisé dans la fonction Render()

static void
Render(void)
{
   RwCameraSetNearClipPlane(Camera, NearClip);
   RwCameraSetFarClipPlane(Camera, FarClip);
   RwCameraClear(Camera, &BackgroundColor, rwCAMERACLEARZ|rwCAMERACLEARIMAGE);

   if( RwCameraBeginUpdate(Camera) )
   {

Tout est prêt. On compile, on exécute, on appuie sur F1 et puis on change les plans de clipping.

Nous allons voir dans la suite comment afficher la position d'un objet et comment faire du drag and drop pour ajouter des objets : Tutorial 3 suite