Tutorial 1

Comme la plupart des codeurs, j'aime bien entré dans le tas et voir dessuite ce qu'on peut obtenir d'une nouvelle API. C'est ce que nous allons voir avec ces tutoriaux basés (copiés???) sur les tutoriaux vendus dans le package RenderWare Graphics®.

Nous allons donc voir dans ce premier tutorial comment charger un cube et comment tourner autour.

Compilation du projet tutorial 1
Lecture d'un objet
Affichage du cube
Destruction du cube

Compilation du projet tutorial 1

La première chose à faire est de compiler le projet tutorial 1. Ici 9 configurations s'offrent à nous : 3 pour directX 8, 3 pour DirectX 9 et 3 pour OpenGL. Les 3 configurations sont Debug, Metric et Release. Nous allons compiler en OpenGL Debug.

Une fenêtre RenderWare est créée. De plus il est possible d'afficher le nombre d'image par seconde avec une pression sur 'F'. Nous allons voir comment tout cela marche au fur et à mesure de ces tutoriaux.

En fait c'est dans main.c que sont gérés les événements et ce n'est pas RenderWare Graphics qui les gère. Dnas main.c se trouvent aussi les fonctions de création du monde, de la caméra, d'initialisation mais aussi de terminaison. Mais on verra cela plus tard.

Lecture d'un objet

Nous allons voir comment lire un objet dff (un cube en l'occurence)(dff= RenderWare Dive File Format) et comment l'afficher. Pour cela, on va créer 2 nouveaux fichiers et on va modifier le contrôleur d'événement du clavier.

#include <rwcore.h>
#include <rpworld.h>

#include "skeleton.h"
#include "utils.h"

RpClump *
DffLoad(RwChar *filename)
{
   RwStream *stream = NULL;
   RpClump *clump = NULL;
   RwChar *pathName;

   /* Open stream */
   pathName = RsPathnameCreate(filename);
   stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, pathName);
   if (stream)
   {
      /* Find clump chunk */
      if (RwStreamFindChunk(stream, rwID_CLUMP, NULL, NULL))
      {
         /* Load clump */
         clump = RpClumpStreamRead(stream);
      }

      /* close the stream */
      RwStreamClose( stream, NULL );
   }
   RsPathnameDestroy(pathName);

   return (clump);
}

Voici le code d'utils.c. Et maintenant le code d'utils.h :

#ifndef UTILS
#define UTILS

/* Function prototypes */
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */

extern RpClump *DffLoad(RwChar *filename);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* UTILS */

En ajoutant, les fichiers au projet et en compilant, il ne devrait pas y avoir d'erreur.

Maintenant, regardons la fonction AttachInputDevices(void) qui se trouve dans events.c.

/* AttachInputDevices function */
RwBool
AttachInputDevices(void)
{
   RsInputDeviceAttach(rsKEYBOARD, KeyboardHandler);

   RsInputDeviceAttach(rsMOUSE, MouseHandler);

return TRUE;
}

On remarque pour chaque device que sont le clavier et le souris, il y a un gestionnaire d'événements. Nous allons regarder le gestionnaire du clavier car c'est lui qui va nous permettre de lire le cube.

static RsEventStatus
KeyboardHandler(RsEvent event, void *param)
{
   /* Let the menu system have a look-in first... */
   if( MenuKeyboardHandler(event, param) == rsEVENTPROCESSED )
   {
      return rsEVENTPROCESSED;
   }
   /* ...then the application events, if necessary... */
   switch( event )
   {
   case rsKEYDOWN:
   {
      return HandleKeyDown((RsKeyStatus *)param);
   }
   case rsKEYUP:
   {
      return HandleKeyUp((RsKeyStatus *)param);
   }
   default:
   {
      return rsEVENTNOTPROCESSED;
   }
}
}
/* End of KeyboardHandler function */

2 nouveaux gestionnaires sont apparus : un s'occupe de l'appui des touches, l'autre du relachement. Celui qui nous intéresse, c'est le gestionnaire de l'appui.

static RsEventStatus
HandleKeyDown(RsKeyStatus *keyStatus)
{
   switch( keyStatus->keyCharCode )
   {

   case rsUP:
   {
      /* CURSOR-UP... */
      return rsEVENTPROCESSED;
   }

   case rsDOWN:
   {
      /* CURSOR-DOWN... */
      return rsEVENTPROCESSED;
   }

Ce n'est que le début de la fonction. Mais ici, on peut voir que rien n'est fait. Nous allons donc ajouter une action lors de l'ppui sur la touche F1.

//ajout pour l'appui sur F1
case rsF1:
{
   RpClump * clump= DffLoad("models/cube.dff");
   if(clump)
   {
      RpWorldAddClump(World,clump);
   }
   return rsEVENTPROCESSED;
}

Lors de l'appui sur F1, on crée un clump en lisant le fichier cube.dff. Si le clump est créé alors on l'ajoute au monde. Pour que le monde soit accessible, il faut l'ajouter dans utils.h.

//pour qu'on puisse avoir connaissance du monde
extern RpWorld *World;

Compilation, exécution, appui sur F1:

Super!!! Le même écran que tout à l'heure. En fait, l'objet et la caméra sont placés à l'origine tout les deux. De plus, on ne rend pas l'objet... Ca marche beaucoup moins bien du coup!!

Affichage du cube

Pour afficher le cube, il va falloir le mettre devant la caméra. On faire ça dans le gestionnaire du clavier:

case rsF1:
{
   RpClump * clump= DffLoad("models/cube.dff");
   if(clump)
   {
      //vecteur 3D
      RwV3d v = {0.0f, 0.0f, 5.0f}; // on se déplace de 5 selon Z
      RwFrameTranslate(RpClumpGetFrame(clump), &v, rwCOMBINEREPLACE); //translation effective
      RpWorldAddClump(World,clump);
   }
   return rsEVENTPROCESSED;
}

Voici donc comment éloigné notre cube de 5 unités sur l'axe des Z de la caméra. Le flag rwCOMBINEREPLACE permet d'écraser la matrice de transformation courante du cube. Nous allons maintenant rendre le cube. Pour cela, il faut se rendre (...) dans la fonction Render() de main.c. Une fois ceci fait, voici la nouvelle fonction Render():

static void
Render(void)
{
   RwCameraClear(Camera, &BackgroundColor, rwCAMERACLEARZ|rwCAMERACLEARIMAGE);

   if( RwCameraBeginUpdate(Camera) )
   {
   if( MenuGetStatus() != HELPMODE )
   {
      /*
      * Scene rendering here...
      */

      //rendu effective de la scène
      RpWorldRender(World);

      DisplayOnScreenInfo(Camera);
   }

On recompile, on exécute, on appui sur F1:

Victoire notre premier objet est affiché dans un fenêtre RenderWare!!

On a lu un objet on l'a affiché. Mais maintenant il faut le détuire!!

Destruction du cube

Comme indiqué plus haut, la fonction d'initialisation et de destruction se trouvent dans main.c.

/**
Destruction d'un clump
*/

static RpClump *
_destroyClumpCB(RpClump *c, void *d)
{
   RpWorldRemoveClump(World, c);
   RpClumpDestroy(c);
   return c;
}

Voici la fonction qui détruit 1 clump. Il faut rajouter ceci dans Terminate3D:

static void
Terminate3D(void)
{

   //Destruction de tous les clump du World
   RpWorldForAllClumps(World, _destroyClumpCB, NULL);

   #ifdef RWMETRICS

Dans cette première partie du premier tutorial, nous avons vu comment lire un objet .dff et comment l'afficher à l'écran. Nous allons voir comment ajouter de la lumière