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 :

Affichage de terrain, besoin d'optimisation !


Sujet :

OpenGL

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Inscrit en
    Septembre 2006
    Messages
    39
    Détails du profil
    Informations forums :
    Inscription : Septembre 2006
    Messages : 39
    Par défaut Affichage de terrain, besoin d'optimisation !
    Bonjour,
    Je débute en C/OpenGL, mon projet est d'afficher un terrain à partir d'un fichier de relevé d'altitude et d'y faire se déplacer une caméra.
    Le fichier contient 2044x2065 altitudes soit autant de vertexs pour l'afficher.

    J'ai réussi à l'afficher en utilisant une liste d'affichage:

    le problème est que la fluidité n'est pas très bonne (11fps avec un PM Dothan 1,7ghz + x700 128Mo).

    1) J'aurais besoin de vos conseils pour améliorer la fluidité.

    2) Il faudrait que l'altitude de chaque point soit modifiable en temps réel, ça ne me semble pas compatible avec les listes d'affichage (que l'on ne peut pas modifier) quelle solution me proposez-vous ?

    3) J'ai entendu parler de glVertexArray() qui pourrait eventuellement replacer une liste d'affichage pour ce qui est de de la fluidité (et en plus je devrais pouvoir modifier à tout moment le terrain non ?) seulement, je n'ai aucune idée de comment l'utiliser / la mettre en oeuvre

    4) ATITrayTool m'indique que j'ai 120Mo de mémoire vidéo de disponible / 128Mo la liste d'affichage est donc dans la mémoire principale, est-ce normal ?

    voici l'essentiel du code (demandez-moi plus de détail si ça ne vous suffit pas)
    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
     
    /*LES VARIABLES*/
    GLint* carte // c'est un tableau 1 dimension qui contient toutes les altitudes du fichier (chargé à l'aide d'une procedure)
    GLint TailleX //nombre de colonnes du fichier (ici = 2044)
    GLint TailleY // nombre de lignes du fichier (ici = 2065)
    GLint cellsize //distance entre 2 altitudes (ici = 50)
    GLint Terrain //identifiant de ma liste d'affichage
     
    /*LES FONCTIONS*/
    void affichage(void){
       (gestion de la caméra avec gluLookAt)
       glCallList(Terrain);
    }
     
    void GenererTerrain(void){
            GLfloat alt=0;
    	GLfloat altmax=AltitudeMax(); // + grande altitude dans le tableau carte
    	GLfloat altcoul=0;
    	GLint cote=cellsize;
     
    	if (glIsList(Terrain))
               glDeleteLists(Terrain,1);
           Terrain=glGenLists(1);
     
    	glNewList(Terrain,GL_COMPILE);	
     
    		for(int l=1;l<TailleY;l++){
    			glBegin(GL_TRIANGLE_STRIP);
     
    			for(int i=0;i<TailleX;i++)
    			{
    				alt=carte[((l-1)*TailleX)+i];//Vertex haut	
    				altcoul=alt/altmax;
    				glColor3f(altcoul,altcoul,altcoul);
    				glVertex3f(i*cote,-(l-1)*cote,alt);
     
    				alt=carte[(l*TailleX)+i];//Vertex bas
    				altcoul=alt/altmax;
    				glColor3f(altcoul,altcoul,altcoul);
    				glVertex3f(i*cote,-l*cote,alt);
     
    			}
    			glEnd();
    	}
    	glEndList();
    }
     
    int main(int argc, char** argv){
       (...)
       glutDisplayFunc(affichage);
       GenererTerrain();
       (...)
       glutMainLoop();
    }
    et pour finir, merci beaucoup

  2. #2
    Expert confirmé

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Par défaut
    Serait-ce possible d'avoir tout le code ainsi que le fichier d'entrée pour pouvoir faire des tests et voir la source du problème (s'il y en a un ?)

    Jc

  3. #3
    Membre Expert
    Avatar de shenron666
    Homme Profil pro
    avancé
    Inscrit en
    Avril 2005
    Messages
    2 582
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : avancé

    Informations forums :
    Inscription : Avril 2005
    Messages : 2 582
    Par défaut
    Citation Envoyé par vinpowful
    1) J'aurais besoin de vos conseils pour améliorer la fluidité.

    2) Il faudrait que l'altitude de chaque point soit modifiable en temps réel, ça ne me semble pas compatible avec les listes d'affichage (que l'on ne peut pas modifier) quelle solution me proposez-vous ?

    3) J'ai entendu parler de glVertexArray() qui pourrait eventuellement replacer une liste d'affichage pour ce qui est de de la fluidité (et en plus je devrais pouvoir modifier à tout moment le terrain non ?) seulement, je n'ai aucune idée de comment l'utiliser / la mettre en oeuvre
    les vertex array peuvent te donner de bien meilleures perfs
    qui plus est, elles peuvent être dynamique et donc tu peux changer les coordonnées sans (trop) perdre en perfs

    tu peux aller voir ici : http://www.codesampler.com/oglsrc.htm
    les deux derniers liens de la page 1 et surtout le premier de la page 2 t'intéresseront
    Tutoriels OpenGL
    Je ne répondrai à aucune question en MP
    - Si c'est simple tu dis que c'est compliqué et tu le fait
    - Si c'est compliqué tu dis que c'est simple et tu le sous-traite ou le fait faire par un stagiaire.

  4. #4
    Membre averti
    Inscrit en
    Septembre 2006
    Messages
    39
    Détails du profil
    Informations forums :
    Inscription : Septembre 2006
    Messages : 39
    Par défaut
    Voici le code que j'utilise actuellement (j'ai changé la procedure de création de la liste d'affichage afin de pouvoir spécifier le pas entre les éléments du tableau à lire, d'ailleurs le fait de passer le pas de 1 à 2, fait passer le frame rate de 11-12 fps à 62-69 fps (!!)).
    Autrement le problème avec le fichier d'entrée c'est qu'il appartient à mon école et puis il est un peu lourd (18 Mos)
    shenron666, merci pour les liens je vais les étudier (surtout quand j'aurais finis mes examens ).

    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
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
     
    #define _CRT_SECURE_NO_DEPRECATE		//désactive les warnings sur la fonction fopen
    #include <iostream>				
    #include <math.h>
    #include <GL/glut.h>
     
    //déclaration des fonctions
    void affichage(void);
    void clavier(unsigned char touche, int x, int y);
    void souris(int bouton, int etat, int x, int y);
    void fleches(int key, int x, int y);
    void repos();
    bool ChargerFichierDat(const char* nom);
    void GenererTerrain2bis(int pas);
    GLint AltitudeMin(void);
    GLint AltitudeMax(void);
    GLfloat CalculerPas(char direction);
     
    //variables globales
    GLfloat ex=0,ey=0,ez=0,angle=90;
    GLint TailleX,TailleY,xllcorner,yllcorner,cellsize,nodata;
    GLint Terrain=0;
    GLint* carte=NULL;
     
    int main(int argc, char** argv){
     
    	printf("argv[0]:%s\n",argv[0]);
    	/*Programme*/
    	if(ChargerFichierDat("indre0.dat") == false)
    		std::cerr<<"Erreur.";
    	printf("\nDonnees\n");		
    	printf("\nAltitude Min:%i\n",AltitudeMin());
    	printf("\nAltitude Max:%i\n",AltitudeMax());
    	printf("\nPasX:%f\n",CalculerPas('X'));
    	printf("\nPasY:%f\n",CalculerPas('Y'));
    	printf("\nPasZ:%f\n",CalculerPas('Z'));
     
    	/*Initialisation des coordonnées*/
    	ex=(TailleX-1)*cellsize/2;
    	ey=-(TailleY-1)*cellsize/2;
    	ez=TailleX+TailleY*cellsize;
     
    	/*GLUT->Création de la fenêtre*/
    	glutInit(&argc,argv);
    	glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH);
    	glutInitWindowPosition(0,0);
    	glutInitWindowSize(700,700);
    	glutCreateWindow("glut");
     
    	/*OpenGL->Initialisation*/
    	glClearColor(0.0,0.5,1.0,0.0);
    	glPointSize(2);	
    	glMatrixMode(GL_PROJECTION);
    	glLoadIdentity();
    	gluPerspective(65,1,0.1,1032500);
     
     
    	//CULLING
    	glEnable(GL_CULL_FACE);		
    	glCullFace(GL_BACK);		
    	glFrontFace(GL_CCW);		
    	//
    	glEnable(GL_DEPTH_TEST);
     
     
    	/*GLUT->définition des fonctions de rappel*/
    	glutDisplayFunc(affichage);
    	glutKeyboardFunc(clavier);
    	glutMouseFunc(souris);
    	glutSpecialFunc(fleches);
    	glutIdleFunc(repos);
     
    	//methode d'affichage avec liste d'affichage
    	printf("Generation du terrain...\n");
    	GenererTerrain2bis(1); //paramètre pas à 1, précision maximale
    	printf("Ok\n");
    	//
    	/*GLUT->Boucle principale*/
    	glutMainLoop();
     
    	/*Sortie du programme*/
    	free(carte);
    	if (glIsList(Terrain))
        glDeleteLists(Terrain,1);
    }
     
    void affichage(void){
    	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
     
    	glPushMatrix();		
    		gluLookAt(ex, ey+ez/tan(angle), ez, //oeil (le calcul en fonction de l'angle est faux pour l'instant, j'y travaillerais après la fluidité :))
    		 ex, ey, AltitudeMin(),		//point de visée
    		 0.0f, 1.0f, 0.0f);		//"up"
     
    		glCallList(Terrain);
     
    	glPopMatrix();		
    	glFlush();
    	glutSwapBuffers();
    }
    void clavier(unsigned char touche,int x, int y){
    	switch(touche)
    	{
    	case 'p':/*affichage plein*/
    		glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
    		glutPostRedisplay();
    		break;
    	case 'f':/*affichage fil de fer*/
    		glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);		
    		glutPostRedisplay();
    		break;
    	case 's':/*affichage sommets*/
    		glPolygonMode(GL_FRONT_AND_BACK,GL_POINT);
    		glutPostRedisplay();
    		break;
    	case 'h':/*ez++*/
    		ez+=CalculerPas('Z');
    		printf("\nez:%f\n",ez);
    		glutPostRedisplay();
    		break;
    	case 'b':
    		ez-=CalculerPas('Z');
    		printf("\nez:%f\n",ez);
    		glutPostRedisplay();
    		break;
    	case 'q':/*quitter le programme*/
    		exit(0);
    	}
    }
    void souris(int bouton, int etat, int x, int y){
    	if (bouton == GLUT_LEFT_BUTTON && etat == GLUT_DOWN) {
    		ez-=10;	
    		printf("\nez:%f\n",ez);
    		glutPostRedisplay();
    	}	
    }
    void fleches(int key, int x, int y){
    	switch(key)
    	{
    	case GLUT_KEY_UP:
    		ey+=CalculerPas('Y');
    		printf("\ney:%f\n",ey);
    		glutPostRedisplay();
    		break;
    	case GLUT_KEY_DOWN:
    		ey-=CalculerPas('Y');
    		printf("\ney:%f\n",ey);
    		glutPostRedisplay();
    		break;
    	case GLUT_KEY_LEFT:
    		ex-=CalculerPas('X');
    		printf("\nex:%f\n",ex);
    		glutPostRedisplay();
    		break;
    	case GLUT_KEY_RIGHT:
    		ex+=CalculerPas('X');
    		printf("\nex:%f\n",ex);
    		glutPostRedisplay();
    		break;
    	case GLUT_KEY_PAGE_DOWN:
    		angle-=10;
    		printf("\nangle:%f\n",angle);
    		glutPostRedisplay();
    		break;
    	case GLUT_KEY_PAGE_UP:
    		angle+=10;
    		printf("\nangle:%f\n",angle);
    		glutPostRedisplay();
    		break;
    	}
    }
    void repos(){
    	glutPostRedisplay();
    }
    bool ChargerFichierDat(const char* nom){
    	printf("Ouverture du fichier %s ...\n",nom);
    	FILE* pFichier= NULL;
    	pFichier=fopen(nom,"rb");
     
    	if (!pFichier)
    	{
    	std::cerr << "Erreur dans l'ouverture du fichier.";
    	return false;
    	}
    	printf("Fichier %s ouvert, lecture de l'en-tete...\n",nom);
     
    	//lecture de l'entête 
    	fread(&TailleX ,sizeof(GLint),1,pFichier);printf("Dimension X: %i\n",TailleX); // fread(buffer, taille en octet du bloc, nombre de bloc, idFichier)
    	fread(&TailleY,sizeof(GLint),1,pFichier);printf("Dimension Y: %i\n",TailleY);
    	fread(&xllcorner,sizeof(GLint),1,pFichier);printf("xllcorner: %i\n",xllcorner);
    	fread(&yllcorner,sizeof(GLint),1,pFichier);printf("yllcorner: %i\n",yllcorner);
    	fread(&cellsize,sizeof(GLint),1,pFichier);printf("cellsize: %i\n",cellsize);
    	fread(&nodata,sizeof(GLint),1,pFichier);printf("Valeur neutre: %i\n",nodata);
     
    	//allocation de la mémoire pour la carte
    	printf("Allocation d'un tableau de %ix%i elements de %i octets (%f Ko)...\n",TailleX,TailleY,sizeof(GLint),(GLfloat)TailleX*TailleY*sizeof(GLint)/1024);
    	carte=(GLint*)malloc(TailleX*TailleY*sizeof(GLint));
    	printf("Allocation reussie.\n");
     
    	//lecture des données
    	printf("Lecture des donnees...\n");
    	fre=fread (carte, sizeof(GLint), TailleX*TailleY, pFichier); // On copie dans le tableau carte un nombre n de "taille d'un GLentier" octets.
    	printf("Lecture terminee.\n");
    	fclose (pFichier); // On n'a plus besoin de ce fichier, on le ferme
    	printf("Fichier %s clos.",nom);
    	return true;
    }
     
     
     
    void GenererTerrain2bis(int pas){//utilisation d'une liste d'affichage et affichage d'un point sur pas.
    	GLfloat alt=0;
    	GLfloat altmax=AltitudeMax();
    	GLfloat altcoul=0;
    	GLfloat cote=cellsize;
     
    	if (glIsList(Terrain))
        glDeleteLists(Terrain,1);
    	Terrain=glGenLists(1);
     
    	glNewList(Terrain,GL_COMPILE);
     
    		for(int l=pas;l<TailleY;l+=pas){
    			glBegin(GL_TRIANGLE_STRIP);
     
    			for(int i=0;i<TailleX;i+=pas)
    			{
    				//Vertex haut
    				alt=carte[((l-pas)*TailleX)+i];
    				altcoul=alt/altmax;
    				glColor3f(altcoul,altcoul,altcoul);
    				glVertex3f(i*cote,-(l-pas)*cote,alt);
     
    				//Vertex bas
    				alt=carte[(l*TailleX)+i];
    				altcoul=alt/altmax;
    				glColor3f(altcoul,altcoul,altcoul);
    				glVertex3f(i*cote,-l*cote,alt);
    			}
     
    			glEnd();
    	}
    	glEndList();
    }
     
    GLfloat CalculerPas(char direction){
    	switch(direction){
    		case 'X':
    			return (TailleX*cellsize)/100.0;
    		case 'Y':
    			return (TailleY*cellsize)/100.0;
    		case 'Z':
    			return ((TailleX+TailleY)*cellsize)/400.0*tan(65.0); // :)
    	}
    }
    /*ok*/GLint AltitudeMin(void){	
    	int indice=0;
    	GLint min=nodata;
    	for(int i=0;i<TailleX*TailleY;i++){ //on cherche la première valeur != nodata
    		if(carte[i] != nodata){			
    			min=carte[i];
    			indice=i+1;
    			break;
    		}
    	}
    	if(min == nodata) return min;
    	for(indice;indice<TailleX*TailleY;indice++){
    		if((carte[indice]<min) && (carte[indice]!=nodata)) min=carte[indice];
    	}
    	return min;	
    }
    /*ok*/GLint AltitudeMax(void){	
    	int indice=0;
    	GLint max=nodata;
    	for(int i=0;i<TailleX*TailleY;i++){ //on cherche la première valeur != nodata
    		if(carte[i] != nodata){			
    			max=carte[i];
    			indice=i+1;
    			break;
    		}
    	}	
    	if(max == nodata) return max;
    	for(indice;indice<TailleX*TailleY;indice++){
    		if((carte[indice]>max) && (carte[indice]!=nodata)) max=carte[indice];
    	}
    	return max;	
    }

  5. #5
    Membre averti
    Inscrit en
    Septembre 2006
    Messages
    39
    Détails du profil
    Informations forums :
    Inscription : Septembre 2006
    Messages : 39
    Par défaut
    je viens de remarquer que j'ai pus gagner 1 fps en évitant l'appel à la fonction AltitudeMin() dans la fonction Affichage() (sur la ligne gluLookAt)

  6. #6
    Rédacteur
    Avatar de bafman
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    2 574
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Novembre 2003
    Messages : 2 574
    Par défaut
    effectivement, altitudeMin et altitudeMax peuvent être précalculé lors des changement dans la heigt map, pas la peine de les recalculer à chaque frame
    * Il est infiniment plus simple de faire rapidement un code qui marche que de faire un code rapide qui marche
    * pour faciliter les recherches, n'oubliez pas de voter pour les réponses pertinentes
    Mes articles

  7. #7
    Membre averti
    Inscrit en
    Septembre 2006
    Messages
    39
    Détails du profil
    Informations forums :
    Inscription : Septembre 2006
    Messages : 39
    Par défaut
    bonjour,
    ça y est j'ai enfin le temps de m'y remettre !
    j'ai testé la méthode avec un vertex array, bon déjà l'affichage n'est pas bon mais c'est sûrement à cause d'une petite erreur dans ma boucle d'affichage, mais j'ai tout de même testé en chargeant ma carte, j'obtiens 5fps ;

    J'ai regardé l'occupation mémoire de ma carte graphique lors de cette méthode, et elle est identique à celle que j'ai constatée lorsque j'utilisais les listes d'affichages (de 120 Mo je tombe à 116 Mo). Pourquoi est-ce mon tableau de vertex n'est pas stocké dans la mémoire vidéo ?? (j'estime que le tableau des coordonnées doit peser près de 50Mo: 2044*2065 points * 4 octets (GLint) * 3 coordonnées...)

    Bref, en attendant, j'aimerais bien avoir une caméra libre style fps, j'ai regardé dans vos archives, j'ai bien trouvé quelque chose mais le forumeur utilisait des classes... ce n'est pas possible d'obtenir quelque chose de bien juste en utilisant les glRotate et glTranslate ??

    voici ce que j'ai actuellement:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);	
     
    	glMatrixMode( GL_MODELVIEW );		
    	glLoadIdentity();
     
    	glRotatef(-alpha,1,0,0);
    	glRotatef(-beta,0,0,1);	
    	glTranslatef(-midx,midy,-midz+ez);
     
    	glCallList(Terrain);
     
    	glFlush();
    	glutSwapBuffers();
    La caméra pivote bien sur elle même comme dans le mode flyby des FPS, mais le problème, c'est que la flèche haut, qui fait varier ez, ne fait que se rapprocher la caméra du terrain quelque soit le point de vue (alors que si je regarde parallèlement au terrain, il faudrait que la caméra avance au lieu de s'enfoncer dedant)

    edit: midx, midy sont les coordonnées du milieu de la carte, midz une distance raisonnable afin de voir la carte en entier (et ces variables sont fixes). alpha et beta sont mes angles de vue modifiés selon les 2 axes de ma souris.
    Merci beaucoup

Discussions similaires

  1. Requête assez lente, besoin d'optimisation
    Par Marco94 dans le forum Langage SQL
    Réponses: 3
    Dernier message: 25/02/2011, 15h48
  2. Besoin d'optimiser un explorateur d'images trop lent
    Par josh38 dans le forum AWT/Swing
    Réponses: 0
    Dernier message: 14/09/2008, 17h01
  3. Besoin d'optimiser une lecture de fichier
    Par BakaOnigiri dans le forum Entrée/Sortie
    Réponses: 8
    Dernier message: 19/02/2008, 10h12
  4. Requete Multi jointure lourde, besoin d'optimisation
    Par Kijer dans le forum Requêtes
    Réponses: 13
    Dernier message: 22/10/2007, 16h43
  5. [DOM] Besoin d'optimiser le parcours d'un fichier XML
    Par stardeus dans le forum Format d'échange (XML, JSON...)
    Réponses: 19
    Dernier message: 08/04/2007, 17h04

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