Expeditions: Samurai veut révolutionner le RPG tactique avec une aventure entièrement jouable en coop
J´ai fais le tour des API et libs de développement graphique aujourd´hui et en lisant les tutoriels venant avec le SDK de Direct X j´ai décidé d´essayer de faire un pong, question de voir si je pouvais arriver à produire quelque chose.
J´ai donc créé un framework de base, un renderer, une structure de points contenant deux triangles pour formé un rectangle et une fonction qui applique une matrice de translation à ma device D3D.
Ça fonctionne, j´ai réussi à faire un paddle qui bouge de haut en bas avec la matrice.
Le problème, c´est que la matrice est appliquée au contexte en entier, elle affecte tout les polygones en même temps.
Quelqu´un sait si le problème peut se régler vite fait bien fait ou si c´est une grosse tranche de théorie ici ? Je vois vraiment pas comment je pourrais dissocier deux rectangles avec le programme actuel.
Voici ma fonction de translation si jamais quelqu´un à le courage de m´aider
void WorldMatrixTranslate(float x, float y , float z)
{
D3DXMATRIXA16 matWorld; / / Créé la matrice
D3DXMatrixTranslation(& , x , y , z); / / Effectue la translation ( c´est facile quand même, déjà tout fait)
g_pd3dDevice->SetTransform(D3DTS_WORLD, &); / / Effectue la transformation sur mon contexte d3d ( donc tous mes polygones)
}
Oh ! Et j´ai essayé de créer plusieurs contextes d3d déjà mais... ça a planté
.
pourquoi ne pas simplement changer les coordoonées de points de ton objet ? ( et non pas toucher a l´ensemble du monde).
pour chacun de tes objets, comme dis lapintade, tu as la position ; il te suffit de changer la matrice de positionnement D3DTS_WORLD avec les bonnes valeurs avant le rendu de chacun de tes objets indépendants.
l´idée de base étant en pseudo-code:
Object::render()
empiler la matrice de transfo
dessiner l´objet
pour chaque sous-objet
sous-objet->render()
depiler la matrice de transfo
RenderAll()
pile de matrice = une matrice identité
pour chaque objet de la scene
objet->render()
l´utilisation d´une pile de matrices te permet de faire des objets composés et des objets simples
mer... les tabulations sont pas passées :/
Object::render()
. .empiler la matrice de transfo
. .dessiner l´objet
. .pour chaque sous-objet
. ...sous-objet->render()
. .depiler la matrice de transfo
RenderAll()
. .pile de matrice = une matrice identité
. .pour chaque objet de la scene
. ...objet->render()
voilà ![]()
Hmmm je vais mettre ça dans la catégorie " c´est une grosse tranche de théorie ici" ( voir mon premier msg) alors
.
Je peux aussi changer manuellement la position des points qui formes les vertices si je les réinitialise entre chaque rendu... c´est envisageable ou dégoutant comme solution ? ![]()
C´est envisageable ( ça marcherait), mais dégoutant ; )
dans l´idéal, tu considères chacun de tes modèles 3D ( importé d´un fichier ou fait à la main dans le code), comme défini autour du point origine O(0,0,0). Ensuite tu associes à ce modèle une position 3D dans ta scène : tu peux alors adapter la matrice de transformation, à chaque rendu, pour l´afficher à l´endroit souhaité.
Si tu testes la méthode consistant à modifier la position des vertices, tu remarqueras que tu plomberas les perfs de ton appli : en effet, tu auras à locker ton LPDIRECT3DVERTEXBUFFER9, qui est une opération relativement couteuse ( tu devrais utiliser un D3DLOCK_DISCARD car tu changes TOUTES les données du buffer, et ça, ça empeche la carte graphique de continuer ses opérations en parallèle car les données ne sont plus valides : c´est un joli pipeline stall)
Le mieux, c´est donc d´utiliser un LPDIRECT3DVERTEXBUFFER9 en association à un LPDIRECT3DINDEXBUFFER9 ( gain mémoire à considérer), le tout créé en D3DPOOL_DEFAULT ou D3DPOOL_MANAGED ( si tu comptes locker les buffers, déconseillé sauf si indispensable ; dans le SDK y´a un bon exemple de LOCK qui ne fait pas trop chuter les perfs, car les données sont remplacées dans de nouvelles zones mémoire ce qui permet à la carte graphique de continuer à dessiner : l´application n´est donc pas bloquée pendant que tu fais tes modifications).
Tes buffers, c´est la forme géométrique ; tu rajoutes des informations, comme un D3DVECTOR3 pour le position et un autre pour l´orientation ( à débattre...), et tu as un bel objet avec tout ce qu´il faut
pour le rendu, tu construis la matrice D3DTS_WORLD avec l´info de positionnement de ton objet, tu l´appliques, et ensuite tu mets les vertex/index stream pour dessiner, et c´est tout bon.
De manière simpliste, tu peux faire ça sous la forme d´une class Object, qui contient tes buffers, tes infos de positions, et dont la méthode render() modifie la matrice WORLD.
edit : le coup de la pile de matrice, tu peux t´en passer dans un premier temps, ce n´est utile que pour des objets composés, c-a-d où la position d´un objet est défini par rapport à un autre ( un bras par exemple, la main est définie par rapport au poignet, lui meme connecté à l´avant-bras, lui meme connecté au coude, lui meme-connecté...)
De plus la modif est mineure au niveau du code pour prendre en compte un telle pile de matrices, dont tu pourras toujours la rajouter tres facilement en temps utile.
J´ai passé ma journée à faire le tour des fichiers d´aide, j´ai sérieusement besoin d´un exemple pour faire un mesh ( de façon à faire drawsubset) avec un vertexbuffer. 4 heures de travail et j´ai pas avancé ! Si j´attrape l´imbécile qui a écri la documentation de direct x...
l´idée primaire des D3DXMESH, c´est de faciliter l´import d´objets créés sous des modeleurs ; en ce sens, pour faire des modifs dessus ou en créer un complètement avec du code, c´est un peu le merdier... Note bien au passage que ça fait partie de D3DX, donc c´est une facilité que tu peux choisir d´ignorer complètement, en substituant par ton propre systeme.
perso pour des structures 3D générées par le code, j´utilise des classes d´objets maison, qui sont mieux adaptées à mes besoins. Pour les meshes, faut chopper les buffers, les locker, tester les subsets, etc. c´est un merdier sans nom... A toi de voir si tu persistes dans cette voie ; moi je trouve que ça complique et que ça allourdi pas mal, dans la mesure ou les mesh contiennent pas mal de truc dont je ne me sert pas.
Bon... et bien j´ai essayé un autre truc qui n´a pas marché non plus... merci de ton aide LGV mais je pense que je vais passer du côté d´OpenGL.
J´aime bien DirectX, un kit très puissant et plus complet que celui d´OGL à tout point de vue. Mais côté documentation, c´est très cruel, je suppose qu´il faut un cour ou un livre de référence pour bien s´en tirer ( du genre de ça peut-être: http://www.amazon.com/exec/obidos/tg/detail/-/0735616531/qid=1088298983/sr=8-6/ref=pd_ka_6/102-7009204-1176101?v=glance&s=books&n=507846 ) .
Je suis prêt à admettre que je débute dans le domaine et que je mange peut-être de trop grosses bouchés mais tout de même, c´est décevant de pas arriver à faire un truc aussi simple ( quel genre de truc de masochiste offre un exemple qui permet de facilement gérer un ensemble de vertices groupées en me laissant sans la moindre idée de comment les dissocier sans contourner le problème de façon innéficace? la frustration qui s´exprime ici
) ...
ouaip, j´en conviens, avant de tirer qqch de la doc ou des exemples, il faut déjà beaucoup expérimenter... Mais apres c´est un vrai plaisir. Si tu fais l´effort de poursuivre un peu, d´ici qq mois tu verras que c´est pas mal intuitif en fait, DirectX. Maintenant c´est vrai qu´il faut avoir envie de galérer, au début :/
les meshes, c´est déjà bien avancé, essaye p-e de te contenter de buffers ( vertex/index) dans un premier temps, de jouer avec les paramètres de rendus, etc. Et petit à petit de faire des effets plus complexes, ou de commencer à architecturer la surcouche que tu créeras autour de l´API. D´autant plus que quand tu auras à gérer des buffers dynamiques, tu n´utiliseras pas des meshes en général. Avec les 6 tuts du SDK, dès que tu sais 1) créer ta fenetre 2) créer un device 3) creer un vertex buffer et le remplir 4) changer des params du device 5) rendre la scène, ( soit qq centaines de lignes au total, grand max) tu as déjà de quoi expérimenter pas mal. Et cette base, c´est ce que te permettras de faire OpenGL, qui lui ne fourni pas d´extensions telles que les meshes ( y´a rien niveau archi, SD, ni maths d´ailleurs... faudra te faire tes lib :
vectors/matrices/quaternions/interpolateurs/etc.).
Changer ou pas maintenant... à méditer, en tout cas à terme il est interessant de tester un peu les deux, DirectX et OpenGL.
Y´a un truc vraiment pas clair avec le remplissage des buffers... Tu vas peut-être pouvoir m´aider, j´ai l´impression que ça peut me déprendre.
- Mon objectif ici est toujours de faire le rendu de 2 polygones contrôlés indépendemment l´un de l´autre -
Premièrement, j´ai un tableau de vertices:
CUSTOMVERTEX vertices[] =
{
{ -2.0f, -2.0f, 0.0f, 0xffff0000, },
{ -2.0f, 2.0f, 0.0f, 0xff0000ff, },
{ 2.0f, 2.0f, 0.0f, 0xff0000ff, },
};
Ensuite je créé mon vertexbuffer avec
g_pd3dDevice->CreateVertexBuffer(...);
g_pd3dDevice étant ici de type LPDIRECT3DDEVICE9 et le buffer étant assigné à un LPDIRECT3DVERTEXBUFFER9 nommé g_pVB ( je suis pas mal à la lettre la nomenclature du guide du sdk comme tu peux voir...)
Ensuite je remplis les buffers comme dans le tutoriel
VOID* pVertices1;
if( FAILED( g_pVB->Lock( 0, sizeof(vertices), ( void**)&1, D3DLOCK_DISCARD ) ) )
return E_FAIL;
memcpy( pVertices1, vertices, sizeof(vertices) ) ;
g_pVB->Unlock();
Ici je suis un peu perdu. Je barre le vertex buffer pour pouvoir écrire dedans ( jusqu´ici ça va). Je lock de 0 jusqu´a la taille de mes vertices ( ce qui est logique), je fais de pVertices1 un pointeur vers les informations retournées par le buffer et je précise le type de barrure.
Pour écrire dans le buffer, je fais une copie en mémoire dont la destion est le pointeur vers mon buffer ? ! Je pige pas pourquoi je suis en train d´envoyer mes vertices dans un pointeur qui n´est plus utilisé à aucune autre ligne de code...
Je suppose que mon problème est là, si je peux remplir deux buffers, je peux affecté les deux morceaux à des transformations matricielles world différentes ?
Comme tu sais déjà, les mesh ( et donc le drawsubset) ça a pas été un succès... Et pour les " class d´objets maison", j´aimerais bien en faire une mais en partant j´arrive pas à faire le rendu correctement, difficile d´implémenter ça en POO.
Je viens d´utiliser un second vertex buffer LPDIRECT3DVERTEXBUFFER9 plein de géométrie différente et le résultat et le même...
il faut voir que ton LPDIRECT3DVERTEXBUFFER9 est une zone mémoire qui existe " qq part" dans le système ( selon le D3DPOOL_ utilisé à la création, le buffer peut se trouver dans la mémoire de la carte graphique, en mémoire centrale du PC, ou alors se balader ici et là durant l´exécution...) ; le LOCK sert donc à assurer que le buffer se trouve dans une zone où tu peux avoir accès, autrement dit en mémoire centrale : ça veut dire que si le buffer ne s´y trouve pas, il y est transféré. Puisque là c´est le remplissage initial, on s´en fout un peu, mais il faut savoir que c´est une opération couteuse.
Ensuite, tu lock en DISCARD, ça veut dire qu´à partir de ce moment, tu dis au systeme que les infos qui étaient dans le buffer ne sont plus valides ( ce qui est vrai, puisque avant il n´avait pas été initialisé).
Enfin, le LOCK t´initialises un pointeur ( pVertices1 daons ton cas) vers la zone du buffer : tu peux alors l´utiliser pour lire ou écrire DANS le buffer en question ; tu ne peux rien faire d´autre avec, c´est pourquoi tu ne te sers plus par la suite de ce pointeur. Les infos maintenant contenue dans le buffer seront utilisées pour le rendu lorsque tu fais un SetStreamSource() sur ton D3DDevice : tu lui dis d´utiliser les infos du LPD3DVERTEXBUFFER9, autrement celle que tu as remplies en lockant ce buffer.
voici les différentes étapes pastées depuis un bout de code pour des particules :
création du buffer ( à chaque perte du device) :
if ( FAILED(hr =
pDevice->CreateVertexBuffer(m_nNbParticles*sizeof(
ParticleVertex), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY | D3DUSAGE_POINTS, ParticleVertex::FVF, D3DPOOL_DEFAULT, &_pVB, 0)))
{
return DXTRACE_ERR("CreateVertexBuffer", hr);
}
------------------------
release du buffer ( à chaque perte du device)
SAFE_RELEASE(m_pVB);
------------------------
remplissage du buffer ( à chaque frame vu mes infos changent entre chaque image) :
ParticleVertex *p_vertex;
if ( FAILED(hr = m_pVB->Lock(0, 0, reinterpret_cast<VOID **>(&_vertex), D3DLOCK_DISCARD)))
{
return DXTRACE_ERR("Lock", hr);
}
// copy position and color of all active particles in the vertex buffer
m_nNbActiveParticles = 0;
for ( UINT index = 0; index < m_nNbParticles; ++index)
{
if ( !m_pParticles[index].isDead())
{
p_vertex[m_nNbActiveParticles].position = m_pParticles[index].position();
p_vertex[m_nNbActiveParticles].diffuse = m_pParticles[index].color();
++m_nNbActiveParticles;
}
}
if ( FAILED(hr = m_pVB->Unlock()))
{
return DXTRACE_ERR("Unlock", hr);
}
note que dans mon cas je préfère remplir directement le buffer via le pointeur retourné par le LOCK plutôt que de faire un memcpy
----------------------
rendu :
pDevice->SetStreamSource(0, m_pVB, 0, sizeof(ParticleVertex));
pDevice->SetFVF(ParticleVertex::FVF);
pDevice->DrawPrimitive(D3DPT_POINTLIST, 0, m_nNbActiveParticles);
ici on retrouve le SetStreamSource, donc les infos du buffers sont utilisées pour le rendu, bien qu´on ne se serve plus du pointeur qui a servi à locker
en espérant que ça t´éclaire un peu ![]()
ParticleVertex *p_vertex;
if ( FAILED(hr = m_pVB->Lock(0, 0, reinterpret_cast<VOID **>(&_vertex), D3DLOCK_DISCARD)))
{
return DXTRACE_ERR("Lock", hr);
}
/ / copy position and color of all active particles in the vertex buffer
m_nNbActiveParticles = 0;
for ( UINT index = 0; index < m_nNbParticles; ++index)
{
if ( ! m_pParticles[index].isDead())
{
p_vertex[m_nNbActiveParticles].position = m_pParticles[index].position();
p_vertex[m_nNbActiveParticles].diffuse = m_pParticles[index].color();
++m_nNbActiveParticles;
}
}
if ( FAILED(hr = m_pVB->Unlock()))
{
return DXTRACE_ERR("Unlock", hr);
}
Quand je lis ca, je suis heureux d´utiliser Open GL ![]()
lag-it : LOL ; ) bah, c´est... " différent" ![]()
Oui. Microsoft ( bon politique à part
) font de bonnes choses parfois ( Visual C++ ) , mais je n´ai jamais aimé leur style de programmation ( à part la notation hongroise qui est pratique ) que ce soit dans l´API win32 ou DiretX ( les MFC sont un peut mieux malgrès le fait que tout ne soit pas Objet ) .
Les reinterpret_cast<VOID **> ![]()
ben le reinterpret_cast<VOID **>() c´est quand meme un " standard" au sens pratique courante quand tu veux faire une fonction qui modifie un pointeur de type générique ; pour avoir pas mal utilisé DirectX, je pense que le seul truc un peu chiant qu´on puisse reprocher, c´est le côté programmation COM, avec les interfaces qu´il faut créer et " releaser", sinon c´est plutôt pas mal je trouve.
Win32, c´est un style, c´est certain, et MFC je crois que tlm est d´accord : c´est VRAIMENT mal gaulé ; ) ( faudrait tester les GUI en C#, parait que c´est bcp mieux). Bon, ce n´est pas non plus le thème du topic, donc on va éviter de polluer ; )
wxWindows ( wxWidgets maintenant ) et GTK ( payant sous windows ) remplacent très bien, mais oui évitons ![]()
Oh mais polluez ! c´est plutôt intéressant comme pollution
. Merci du coup de main, je vais voir à déchiffrer tout ça.
Juste par curiosité, LGV, comment est-ce que t´as procédé pour apprendre à utiliser l´API ? Jamais je peux croire que tu t´en tires avec la documentation de MS ?