Tutorial 4 : les plugins
Dans ce tutorial, nous allns apprendre à écrire un plugin pour RenderWare Graphics.
Un plugin est une extension de quelque chose qui existe déjà dans RenderWare Graphics au contraire des toolkits qui se servent de RenderWare Graphics.
Ici nous allons étendre RpAtomic pour lui ajouter un nom mais aussi pour le sauvegarder dans un fichier .DFF.
Initialisation du plugin
Ajout d'un nom
La première chose à faire est d'ajouter les fichiers RpAName.h et RpAName.c au projet.
Voici le premier code qu'il faut ajouter à RpAName.h
| #ifndef _RPANAME_H_ #define _RPANAME_H_ #ifdef __cplusplus #ifdef __cplusplus #endif /*_RPANAME_H_*/ |
Voici maintenant le code qu'il faut ajouter pour RpAName.c.
| #include <rwcore.h> #include <rpworld.h> #include "RpAName.h" // ID pour le plugin static RwInt32 rpExtensionOffset = -1; //fonctions pour le plugin RpAName //initialisation du plugin /* return
the offset */ |
Cette fonction nous permet d'enregistrer le plugin ainsi que
d'indiquer qu'il faut réserver de la place pour un char * en plus dans
la structure du plugin. Pour que chaque plugin possède un numéro
unique, il faut un ID de Vendor fournit par RenderWare et un ID de plugin.
On voit aussi qu'il faut une fonction rpConstructor et une fonction rpDestrcutor.
Ces 2 fonctions permettent de construire et de détruite chaque Atomic
créé. L'Offset obtenu par la fonction est stockée par
une variable car elle sera nécessaire par le plugin.
Voici les fonctions rpConstructor et rpDestructor
| /*
Local functions for the atomic name plugin */ static void * rpConstructor(void *atom, RwInt32 offset __RWUNUSED__, RwInt32 size __RWUNUSED__) { RwDebugSendMessage(rwDEBUGMESSAGE, /* success
*/ static void * RwDebugSendMessage(rwDEBUGMESSAGE, /*
success */ |
Ces 2 fonctions prennent 3 paramètres. Dans ces fonctions on fait appel à RwDebugSendMessage. Cette fonction permet d'écrire un message dans la fenêtre d'Output du debugger entre autres.
Pour que le plugin fonctionnne, il faut l'attahcer à l'application. Donc voici ce qu'il faut ajouter dans AttachPlugin après avoir inclu RpAName.h dans main.c:
if(!RpAtomicNameInitialize()) { return FALSE; } |
Maintenant il faut compiler en Debug et voir ce que dit le debuggeur lorsqu'on ajoute un atomic et lorsqu'on détruit l'application. On remarque que lorsqu'on appelle RpClumpStreamRead, RenderWare Graphics appelle RpAtomicCreate qui lui appelle le rpConstructor définit ci-dessus.
On va ajouter une macro pour récupere le char* à partir d'un pointer sur un atomic
| /*
Given an atomic, return a pointer to the extension */ #define RPEXTFROMATOMIC(a)\ ((char **)(((RwUInt8 *)(a)) + rpExtensionOffset)) |
Il faut maintenant initialiser le char* contenu dans le nouvel atomic que nous avons créé. Nous allons pour cela utiliser RwMalloc et RwFree. Ici, seul RwFree apparait car ce n'est pas dans le constructeur que l'on met le nom de l'atomic.
| static
void * rpConstructor(void *atom, RwInt32 offset __RWUNUSED__, RwInt32 size __RWUNUSED__) { if(rpExtensionOffset > 0) { char ** extData = RPEXTFROMATOMIC(atom); //l'atomic n'a pas de nom pour l'instant *extData=NULL; } /* success
*/ static void * /* success
*/ |
Le but de notre plugin est d'ajouter un nom à l'atomic. Pour l'instant, on peut détruire le nom mais on ne peut y accéder n'y le mettre en place. Dans cette partie on va voir comment réaliser ça.
| //fonction
pour mettre en place le nom de l'atomic const char * RpAtomicNameSetName(RpAtomic * atom,const char * name) { RwInt32 len = rwstrlen(name); if(rpExtensionOffset>0 && len>0) { char **extData = RPEXTFROMATOMIC(atom); if(*extData) //s'il y a deja un nom { RwFree(*extData); } *extData = RwMalloc(len+1,rwID_NAOBJECT); rwstrcpy(extData,name); return name; const char * |
Il faut ensuite mettre les entêtes des fonctions dans RpAName.h
| extern const char * RpAtomicNameGetName(RpAtomic
* atom);
extern const char * RpAtomicNameSetName(RpAtomic * atom,const char * name); |
Maintenant qu'on a crée les fonctions, il faut s'en servir... Pour cela on va afficher le nom contenu dans l'atomic sélectionné dans DisplayOnScreenInfo()
| if
(ShowPos) { if (PickedAtomic) { RwFrame *f = RpAtomicGetFrame(PickedAtomic); RwMatrix *m = RwFrameGetLTM(f); RwChar * s = RpAtomicNameGetName(PickedAtomic); RsSprintf(caption, RWSTRING("<%s>:%4.2f
%4.2f %4.2f"), RsCharsetPrint( Charset, caption,
0, 2, rsPRINTPOSTOPRIGHT ); |
Ainsi on peut voir que pour l'instant aucun des atomic n'a de nom.
Pour laisser le pickedAtomic persistant, il faut commenter la ligne indiquant que le PickedAtomic devient NULL.
| default: //PickedAtomic = NULL; break; } |
On va définir un tableau indiquant des noms qu'on peut donner aux atomic
| #define NUMNAMES 6 static const RwChar *AtomicNames[NUMNAMES] = { "Cube", "Banana", "Georges", "Toto", "Julie", "Auberon" }; static RwInt32 SelectedName = 0; |
On va définir des entrées dans le menu pour utiliser ces noms.
| static RwBool InitializeMenu(void) { static RwChar fpsLabel[] = RWSTRING("FPS_F"); static RwChar nearLabel[] = RWSTRING("Near Clip"); static RwChar farLabel[] = RWSTRING("Far Clip"); static RwChar showPosLabel[] = RWSTRING("Show Position"); static RwChar namesLabel[] = RWSTRING("Name Choice_N"); if( MenuOpen(TRUE, &ForegroundColor,
&BackgroundColor) ) MenuAddEntryReal(nearLabel,
&NearClip, NULL, 0.1f, 10.0f, 0.1f); MenuAddEntryInt(namesLabel,&SelectedName,NULL,0,NUMNAMES-1,1,AtomicNames); return TRUE; return FALSE; |
On compile et on exécute. Maintenant, il est possible de choisir le nom mais il n'est toujours pas affecté à l'atomic... On va encore ajouter une entrée au menu. Cette fois ce sera un trigger qui sera appelé (une fonction).
static RwBool if(
MenuOpen(TRUE, &ForegroundColor, &BackgroundColor) ) MenuAddEntryReal(nearLabel,
&NearClip, NULL, 0.1f, 10.0f, 0.1f); MenuAddEntryInt(namesLabel,&SelectedName,NULL,0,NUMNAMES-1,1,AtomicNames); return
TRUE; return
FALSE; |
Il ne reste plus qu'à définir le trigger assignNameTrigger (dans main.c).
| /** trigger pour mettre à jour le nom de l'atomic */ static RwBool assignNameTrigger(RwBool check) { if (PickedAtomic && !check) { RpAtomicNameSetName(PickedAtomic, AtomicNames[SelectedName]); } return TRUE; } |
On compile, on exécute, on place des objets et on leur donne des noms!!

Nous avons vu comment nommer les objets. Nous allons voir maintenant comment lire et sauvegarder ces nouveaux paramètres!! Tutorial4 sérialisation