[DirectX] D3DXLoadMeshFromX et vertex buffers

D3DXLoadMeshFromX et vertex buffers [DirectX] - C++ - Programmation

Marsh Posté le 19-01-2006 à 19:30:13    

Bonjour,
 
J'essaye de calculer un arbre de boites AABB pour un debut de moteur 3D.
Je charge mes modeles a partir de fichiers .X avec la fonction D3DXLoadMeshFromX().
 
Mon but est ensuite de pouvoir acceder aux coordonnées de chaque triangle (face ?) un apres l'autre.
 
Jusqu'a present, je clone le mesh vers un format FVF_XYZ (pour pouvoir le lire directement avec "D3DXVECTOR3 * pVertex;" )
 
Ensuite j'apelle une fonction d'optimisation du mesh (je suis pas sur que ca serve a grand chose).
 
et enfin je lock le vertex buffer pour y acceder et je procede au traitement (creation des boites a partir des coordonnées du vertex buffer).
 
ca donne ca :  
 

Code :
  1. m_Mesh->CloneMeshFVF(D3DXMESH_SYSTEMMEM, D3DFVF_XYZ, pd3dDevice,&m_MeshFVFNormal);
  2.  DWORD * pVertexAdjacents    =new DWORD[m_MeshFVFNormal->GetNumFaces()*3];
  3.  DWORD * pVertexAdjacentsOut =new DWORD[m_MeshFVFNormal->GetNumFaces()*3];
  4.  m_MeshFVFNormal->GenerateAdjacency(0.01f,pVertexAdjacents);
  5.  m_MeshFVFNormal->OptimizeInplace(D3DXMESHOPT_ATTRSORT|D3DXMESHOPT_COMPACT, pVertexAdjacents, pVertexAdjacentsOut, NULL, NULL);
  6.  //D3DXSimplifyMesh(m_MeshFVFNormal,  pVertexAdjacentsOut,  NULL, NULL, m_MeshFVFNormal->GetNumVertices()/2, D3DXMESHSIMP_VERTEX, &m_MeshSimple);
  7.  // Ouverture du Vertex Buffer pour aller chercher les vertices du mesh (pour construire l'AABBTree)
  8.  D3DXVECTOR3 * pVertex;
  9.  D3DXVECTOR3 * pIndex;
  10.  m_MeshFVFNormal->LockVertexBuffer(D3DLOCK_READONLY, (LPVOID*)&pVertex);
  11.  //m_MeshFVFNormal->LockIndexBuffer(D3DLOCK_READONLY,(LPVOID*)&pIndex);
  12.  m_ArbreAABB->Construire(pVertex, m_MeshFVFNormal->GetNumVertices());
  13.  //m_MeshFVFNormal->UnlockIndexBuffer();
  14.  m_MeshFVFNormal->UnlockVertexBuffer();


 
Alors le probleme c'est que j'ai l'impression de mal lire le vertex buffer dans ma fonction "Construire".
J'ai en eefet des vertices avec des coordonnées mais lorsque j'essaye de prendre les 3 premieres pour tester le triangle, j'ai des problemes de coordonnées pour ce triangle.
 
J'ai donc l'impression que les 3 premiers vertices du vertex buffer ne sont pas forcement les 3sommets du premier triangle... exact ?
 
D'ou l'interet des index buffers ?
 
quand on charge un fichier .x, il y a des données charhées dans l'index buffer ?
 
Merci bcp de m'aider, je commence a saturer et j'ai du mal a trouver sur le net une representation de ce qu'il y a vraiment dans un vertex buffer et indexbuffer apres chargement d'un .X ;)

Reply

Marsh Posté le 19-01-2006 à 19:30:13   

Reply

Marsh Posté le 20-01-2006 à 01:22:12    

les fichier .X, je m'en sers pas donc je peux pas trop te dire.
 
ce que je peux te dire c'est quand dans le VertexBuffer tu as les données par vertex , soit généralement 'n' floats ou entiers 32bits ou groupes de 4 entiers 8bits pour la position du vertex,normale, coordoonées textures, indice de matrice/poids pour le skinning (par exemple ce n'est pas une obligation).
 
la carte 3D "fetch" le vertex qui est pointé par la valeur 16 ou 32bits dans l'indexbuffer auquel elle ajoute le offset d'indice.
 
généralement il y a toujours un indexbuffer, car c'est l'indice qui est la clé dans le cache post-T&L, ie tu n'as le rendement max géométrique de la carte qu'avec un indexbuffer, donc généralement y'a pas de vertexbuffer sans indexbuffer.  
 
a savoir aussi que depuis Dx9 tu peut mélanger des vertexs de formats différents dans le même VertexBuffer (le VertexDeclaration servant à décrire la structure des vertexs)
 
tu auras toutes les réponses a tes questions là:
 
http://msdn.microsoft.com/library/ [...] uffers.asp

Reply

Marsh Posté le 20-01-2006 à 01:35:28    

ha oui et fait gaffe: tu peux facilement provoquer un écran bleu avec les indexbuffer/vertexbuffer en déclenchant une exception matérielle au niveau de la carte 3D:
en écrivant hors espace locké, en préparant l'indexbuffer pour que le GPU sortes hors du vertexbuffer, en faisant un DrawIndexedPrimitive avec dépassement d'Indexbuffer ou un offset de vertex foireux :D
 
en gros c'est le seul cas où tu viander une machine sans l'os ne puisse rien faire si la carte se vomit dessus et que le driver se vautre sans rien pouvoir faire. (en OpenGl c'est aussi possible d'après ce que j'ai pu voir)


Message édité par bjone le 20-01-2006 à 01:35:54
Reply

Marsh Posté le 20-01-2006 à 17:40:20    

ok, merci pour ta reponse.
 
le principe du vertex/index buffers, je pense l'avoir bien compris. Cpendant, je ne savais pas si le chargement d'un fichier X remplissait uniqueent le vertex buffer (auquel ca j'aurais pu lire direct dedans). Mais d'apres tes conseils il est normal que j'ai des triangles foireux puisque je lis direct dedans et donc j'ai les bons sommets mais pas les bon triangles.
 
Je vais donc essayer de lire le vertex buffer a partir de l'index buffer et je vous tiens au courant... :)
 
merci, si vous aves des conseils, je suis toujours preneur ;)

Reply

Marsh Posté le 20-01-2006 à 18:20:19    

si tu veux comprendre comment ça marche, il faut que ce soit toi qui remplisse le couple vertexbuffer/indexbuffer.
 
les fichiers .X sont là pour avoir quelque chose de vite fait.

Reply

Marsh Posté le 20-01-2006 à 19:19:49    

Je pense que tu as raison mais ce sera ma prochaine etape (creation d'un terrain) ;)
 
D'ailleurs on ne trouve quasiment que ça sur le net...
 
Imaginons que j'ai un joli mesh qui subit une collision et que j'aimerais le déformer. Faut-il modifier les données du vertex buffer et/ou de l'index buffer ou faut il proceder autrement ?
C'est juste pour voir comment ca marche, je demande pas de details techniques ;)
 
merci, je vous tiens au courant de l'avancement.  


Message édité par Bouteille le 20-01-2006 à 19:21:25
Reply

Marsh Posté le 20-01-2006 à 20:01:19    

oui tu actualises le vertexbuffer (et l'indexbuffer si tu as besoin de créer ou de supprimmer de la géométrie)
 
après ça dépends de ta déformation: on peut en faire dans le vertexshader, mais si c'est pour tordre une carosserrie de voiture après une collision avec un sapin, il faut vraiment actualiser le vertexbuffer ou utiliser une autre géométrie (modèles destructibles avec dégats pré-modélisés).

Reply

Marsh Posté le 20-01-2006 à 21:54:59    

ok, merci de ton aide

Reply

Marsh Posté le 20-01-2006 à 22:04:51    

Juste une derniere petite question apres je retourne a mon code ;)
 
Comment savoir lors de la lecture de l'index buffer si je dois interpreter les coordonnées comme strips ou comme des triangles ? Les deux peuvent-ils coexister dans un VB ?
 
merci :D


Message édité par Bouteille le 20-01-2006 à 22:32:41
Reply

Marsh Posté le 20-01-2006 à 22:48:31    

c'est indépendant.
 
c'est à toi de savoir ce qu'il y a effectivement.
 
le problème c'est que la tu tournes le truc dans le mauvais sens:
tu essayes de réinterpréter à la main les structures du D3DXMesh.
 
évidemment quand tu remplis un VertexBuffer et un IndexBuffer, c'est à toi de savoir pourquoi tu l'a remplis comme ça.
 
---
 
sinon oui le VB et l'IB sont des ressources géométriques, et donc c'est indépendant du type de primitive connectant les vertexs.
 
tu peux parfaitement avoir une portion de strip de triangles, une portions de triangles indépendants, une portion de liste de lignes, une portion de point sprites...

Reply

Marsh Posté le 20-01-2006 à 22:48:31   

Reply

Marsh Posté le 20-01-2006 à 22:48:58    

Me revoila apres un test et je dis WOW !
 
merci ca m'avance enormement, l'utilisation des index buffers semble corriger mes problemes meme si ca fonctionne pas a 100%.
 
Je procede a une copie des vertex dans un tableau avant de le passer a ma fonction... est ce envisageable ou c'est vraiment trop lourd ?
 
ca donne ca :  
 

Code :
  1. // clonage du vertex buffer format XYZ
  2.  m_Mesh->CloneMeshFVF(D3DXMESH_SYSTEMMEM, D3DFVF_XYZ, pd3dDevice,&m_MeshFVFNormal);
  3.  DWORD * pVertexAdjacents    =new DWORD[m_MeshFVFNormal->GetNumFaces()*3];
  4.  DWORD * pVertexAdjacentsOut =new DWORD[m_MeshFVFNormal->GetNumFaces()*3];
  5.  m_MeshFVFNormal->GenerateAdjacency(0.01f,pVertexAdjacents);
  6.  m_MeshFVFNormal->OptimizeInplace(D3DXMESHOPT_ATTRSORT|D3DXMESHOPT_COMPACT, pVertexAdjacents, pVertexAdjacentsOut, NULL, NULL);
  7.  //D3DXSimplifyMesh(m_MeshFVFNormal,  pVertexAdjacentsOut,  NULL, NULL, m_MeshFVFNormal->GetNumVertices()/2, D3DXMESHSIMP_VERTEX, &m_MeshSimple);
  8.  // Ouverture du Vertex Buffer pour aller chercher les vertices du mesh (pour construire l'AABBTree)
  9.  D3DXVECTOR3 * pVertex;
  10.  WORD * pIndex;
  11.  m_MeshFVFNormal->LockVertexBuffer(D3DLOCK_READONLY, (LPVOID*)&pVertex);
  12.  m_MeshFVFNormal->LockIndexBuffer(D3DLOCK_READONLY,(LPVOID*)&pIndex);
  13.  D3DXVECTOR3 * Vertices=new D3DXVECTOR3[m_MeshFVFNormal->GetNumFaces()*3];
  14.  for (int z = m_MeshFVFNormal->GetNumFaces()*3;z--;)
  15.  {
  16.   Vertices[z]=pVertex[pIndex[z]]; // la copie dans un tableau
  17.  }
  18.  m_MeshFVFNormal->UnlockIndexBuffer();
  19.  m_MeshFVFNormal->UnlockVertexBuffer();
  20.  m_ArbreAABB->Construire(Vertices, m_MeshFVFNormal->GetNumFaces()*3); // utilisation du tableau dans une fonction recursive (Construire)


Message édité par Bouteille le 20-01-2006 à 23:42:21
Reply

Marsh Posté le 20-01-2006 à 23:02:04    

alors pour ce genre de trucs:
 
1) déjà le truc des Mesh de D3DX, c'est une assistance, si tu commençes a chercher les perfs, il vaux mieux créer tes propres objets de modèles si tu veux un contrôle fin.
 
2) pour l'emploi des VertexBuffers,IndexBuffers,Surfaces (textures, rendertargets), c'est dépendant de leur pool mémoire utilisé à la création, ainsi que les attributs/hints d'accès.
 
en gros si tu veux pouvoir manipuler un vertexbuffer, ou autre ressource,  
il faut que tu lises ça:
http://msdn.microsoft.com/library/ [...] 3dpool.asp
http://msdn.microsoft.com/library/ [...] DUSAGE.asp
 
en gros suivant comment est crée la ressource, tu auras la possibilité au pas de relire ou ré-écrire dedans, etc, et l'impact sur les perfs.
 
---
 
pour expliquer:
si ta ressource en explicitement en ram vidéo, un accès aléatoire (par pointeur avec un algo qui lit ou écrit de manière non séquentielle) sera lent, donc une copie en ram est recommandée.
 
si ta ressource et en D3DPOOL_MANAGED, alors le runtime maintiens une copie en ram système, et fait la copie en ram vidéo à la demande.
en gros dans ce cas si tu lockes la ressource en lecture seule, tu devrais obtenir un pointeur sur la copie maintenue en ram système, et donc une deuxième copie comme tu fais devrait être superflue.
 
dans ton cas tu lockes en lecture seule, avec l'objet Mesh du D3DX, a priori je penses que toutes les ressources sont en D3DPOOL_MANAGED, donc il est problable que la recopie ne soit pas nécessaire.
 
une fois plus, à cause de l'emploi du Mesh du D3DX, tu n'as pas forcément un contrôle fin, où toutes les garanties de comportement, donc je te garanties rien, regarde dans le SDK ce qu'ils en disent...

Reply

Marsh Posté le 20-01-2006 à 23:20:32    

--- en gros pour préciser pour ta culture personnelle, perfs vu du CPU:
 
en ram vidéo:
la lecture est lente, l'écriture moins, un accès non séquentiel ça doit être castastrophique.
because passage par l'agp.
 
en ram "agp":
(mémoire système projetée par le gart dans l'espace d'adressage du GPU), c'est moins pire, mais normalement l'espace mémoire n'est pas caché (ie tu ne profites pas du cache CPU, car le contrôle de cohérence vers le cache CPU serait tueur en perfs).
 
par contre je sais pas si sur un Athlon 64 l'espace mémoire AGP est caché car tout les accès mémoire passent par le CPU, contrairement où une topologie avec chipset imposent de matraquer le FSB pour savoir si le cache a des données plus récentes (actualisées par le cpu).
 
en ram système:  
a fond les ballons, cache CPU actif....
 
--- vu du GPU:
 
ram vidéo: à fond les ballons.
 
ram agp: c'est plus lent, peut être la possiblité d'écrire vu que dans l'espace d'adressage du GPU, remontées des ressources sans forcément de consommation de mémoire vidéo (ie pas besoin de faire une eviction de ressources en ram vidéo)
 
ram système: pas visible depuis le GPU, le driver doit copier/mettre la ressource en ram agp ou la transférer par dma en ram vidéo (en faisant une éviction d'une ressource considérer comme trop ancienne)
 
---
 
et de manière générale, les écritures dans une ressources de par le CPU peut provoquer un stall du GPU (attente car recopie actualisation de la ressource), et le CPU peut aussi faire un stall si tu attends une ressource pour la réutiliser.
 
et aussi de manière générale, comme tu peux voir, ce qui est le mieux pour l'un n'est forcément le mieux pour l'autre.
 
---
 
dans le cas d'une carte PCI Express, ça doit être à des années lumières mieux dans certains cas de figures.


Message édité par bjone le 20-01-2006 à 23:23:13
Reply

Marsh Posté le 20-01-2006 à 23:45:48    

cool, merci de tes reponses rapides...  
 
Pour l'instant jusqu'à ce que j'obtienne mon arbre de boites je vais rester comme ca : solution de facilité (au moins je sais que j'obtiens un bon tableau avec de bonnes coordonnées).
Apres je reflechirais pour les performances parce que c'est vrai que c'est un peu long à charger ce programme qui n'affiche que 2 modèles de quelques centaines de triangles ;)

Reply

Marsh Posté le 21-01-2006 à 00:44:01    

le chargement n'est pas forcément un problème, ce qui compte c'est d'être efficace une fois dans la boucle du moteur.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed