Salut à tous!
Je suis actuellement entrain de finir mon moteur de jeux que j'ai commencé il y a déjà, quelques années en vue de développez un jeux.
Ayant presque terminé le moteur graphique (à part quelque trucs plus compliqués comme par exemple le chargement de modèles 3D, la génération de terrain en 3D, l'éclairage et le shading par pixel, etc...)
Que je ne compte pas faire tout de suite car je compte rester en 3D isométrique simple pour le moment. (Donc le rendu d'images en 3D dans un monde en 2D)
Le truc c'est que la caméra et les sommets sont en 3D mais la caméra reste en vue du dessus et je me sert de la position z comme hauteur, et plus les objets sont bas en y, plus ils sont haut en z. (Je me sert du z pour générer la heightmap, ainsi que la normal map à l'aide d'un shader et du rendu multi-passe)
Car je veux un moteur généraliste qui utilise le maximum de techniques qui sont similaire pour la 3D ainsi que pour la 2D.
Je suis entrain de faire le moteur physique, alors, j'ai visité ce lien (en Anglais) avec des exemples en flash : ici
Voici un autre lien inspiré du précédent : http://www.codezealot.org/archives/55 pour avoir un moteur qui gère à la fois les collisions en 2D et en 3D à l'aide d'une seule et même technique.
Mais je sèche un peu, en effet, le théorème de SAT est un peu long, et de plus, il ne fonctionne pas pour tester par exemple si un point est dans un polygone, est encore moins avec les sphere et les ellipsoïdes ou là, ça devient carrément plus compliqué.
J'ai donc décidé d'optimiser SAT en ramenant tout les test à un test du style rayon shere et ce pour toutes les formes.
Bref, j'ai donc tenter de faire un algorithme qui projette toutes les normales et les sommets du polygone sur le vecteur entre le centre du polygone et un autre point quelconque, voici l'algorithme qui se charge de ça :
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 static Vec2f projectShapeOnVector(Vec3f vector, std::vector<Vec3f> vertices) { float max = 0.f, min = 0.f; if (vertices.size() > 0) { Vec3f v = vertices[0].projOnVector(vector); max = v.projOnAxis(vector); if (max < min) min = max; for (int i = 1; i < vertices.size(); i++) { Vec3f v = vertices[i].projOnVector(vector); float p = v.projOnAxis(vector); if (p < 0 && p < min) min = p; if (p > max) max = p; } } } return Vec2f(min, max); }Je redimensionne donc toutes les diagonales et les médianes du polygone suivant le cosinus de l'angle entre le vecteur d (vecteur entre le centre du polygone et un autre point) et la diagonale ou bien la médiane du polygone.
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 bool BoundingPolyhedron::isPointInside(Vec3f p) { Vec3f d = p - center; float c = d.magnitude(); std::vector<Vec3f> v; for (unsigned int i = 0; i < points.size(); i++) { v.push_back(points[i] - center); v.push_back(normals[i]); } Vec2f pr = Computer::projectShapeOnVector(d, v); float r = Math::abs(pr.x) + Math::abs(pr.y); std::cout<<"rsum : "<<r<<" c : "<<c<<std::endl; if (r != 0 && r - c < 0) return false; return true; }
Je projette ensuite tout les vecteurs redimensionnés sur l'axe d.
Je recherche le maximum (il doit toujours être > 0) ainsi que le minimum (il doit toujours être < 0) par rapport à tout les vecteurs projetés.
Je prend la somme des deux (en prenant les valeurs absolue) et je la test avec la distance entre le centre du polygone et l'autre point.
Et enfin je compare ces 2 valeurs comme si je faisait un test pour savoir si un point est dans une sphere.
La deuxième technique concerne l'intersection d'un rayon avec une sphere :
Et un autre pour l'intersection rayon, rayon :
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 bool BoundingSphere::intersects (Ray &ray) { Vec3f d1 = ray.getExt() - center; Vec3f d2 = ray.getOrig() - center; Vec3f di[3], di2[3]; float c = radius; di[0] = Vec3f(d1.x, 0, 0); di[1] = Vec3f(0, d1.y, 0); di[2] = Vec3f(0, 0, d1.z); di2[0] = Vec3f(d2.x, 0, 0); di2[1] = Vec3f(0, d2.y, 0); di2[2] = Vec3f(0, 0, d2.z); Vec3f di3[3]; di3[0] = Vec3f (radius, 0, 0); di3[1] = Vec3f (0, radius, 0); di3[2] = Vec3f (0, 0, radius); for (unsigned int i = 0; i < 3; i++) { float r1 = di[i].projOnAxis(di3[i]); float r2 = di2[i].projOnAxis(di3[i]); float rSum = r1 + r2; if (rSum != 0 && c - rSum < 0) return false; } return true; }
Voilà donc si quelqu'un peut corriger ou bien détecter des erreurs éventuelles je suis preneur, car, je sèche un peu. :/ (Je dois encore faire les tests entre sphere/polygone, polygone/polygone avec ma méthode de SAT optimisée, ainsi que ellipsoid/sphere, polygone et rayon (car la méthode de SAT doit faire beaucoup de tests sur beaucoup de normales surtout en 3D), mais je pense que j'ai trouvé comment géré cela en projetant les normales et vertex des 2 formes sur d et en faisant un test du genre collision entre 2 sphères.
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 float Ray::intersectsWhere (Ray &other) { Vec3f da = dir; Vec3f db = other.dir; Vec3f dc = other.orig - orig; if (dc.dot(da.cross(db)) != 0.f) // lines are not coplanar return -1; float s = dc.cross(db).dot2(da.cross(db)) / da.cross(db).magnSquared(); if (s < 0 || s > 1) { return -1; } return s; }
Les méthodes :
cross calcule le produis en croix entre 2 vecteur.
dot : calcule le produit scalaire entre 2 vecteurs normalisés. (de longueur 1)
normals : ce sont les médianes des vecteurs. (les vecteurs entre les milieux des côtés du polygone et le centre du polygone)
points : ce sont les diagonales des vecteurs. (les vecteurs entre les sommets du polygone et le contre du polygone)
dot2 : même chose que dot mais ne normalise pas les vecteurs.
projectOnVector : projette un vecteur "a" sur un autre vecteur "b". (= a.dot(b) * a)
projectOnAxis : projette un vecteur "a" sur un axe "b". (= a.dot(b) * a.longueur())
magnitude : calcule la longueur du vecteur.
magnSquared : calcule la longueur du vecteur mise au carré.
Voilà si quelqu'un pense pouvoir optimisé ou remarque une erreur dans ces algorithmes je suis preneur.
Car je galère un petit peut pour faire ça de manière simple et généraliste.
Partager