tfVector, simplifiez vous la vie

Écrit le 03/07/2003 par *OgGiZ*
Dernière mise à jour : 06/02/2006

Commençons par écrire un constructeur « null ». C'est à dire que si nous faisons juste un new tfVector () que XYZ soit un vecteur nul.

tfVector :: tfVector (void)
{
    this->x = 0.0f;
    this->y = 0.0f;
    this->z = 0.0f;
}

Maintenant occupons-nous de faire un constructeur pour 3 float (rien de bien compliqué !)

tfVector :: tfVector (float x, float y, float z)
{
    this->x = x;
    this->y = y;
    this->z = z;
}

Déjà un peu plus complexe, créer un vecteur copie d'un autre vecteur :] Mais en gros c'est assez simple ! faut pas se casser la tête.

tfVector :: tfVector (tfVector& v)
{
    this->x = v.x;
    this->y = v.y;
    this->z = v.z;
}

Encore un constructeur sauf qu'ici on spécifie un Array[3]. Très utile sous son chtit moteur 3D avec des typedef float vec3_t[3] etc !

tfVector :: tfVector (float p[3])
{
    this->x = p[0];
    this->y = p[1];
    this->z = p[2];
}

Ici c'est un peu l'inverse ! On demande au prog de stocker XYZ dans un array de 3 float (cf vec3_t)

void tfVector :: CopyToArray (float point[3])
{
    point[0] = this->x;
    point[1] = this->y;
    point[2] = this->z;
}

La Magnitude, c'est quoi ce nom barbare ? c'est tout simplement la longueur de notre vecteur ! En math, cette longueur est définie par racine carrée de x²+y²+z². Le type retourné est un double, castons donc un peu !

float tfVector :: Magnitude (void)
{
    return (float) sqrt (x*x + y*y + z*z);
}

Aïe là ça devient more complex! C'est la même chose que vue précédemment sauf qu'ici nous allons tout projeter sur un plan 2D XY ! Bref Z = 0. La formule devient donc racine² de x²+y² :)

float tfVector :: Magnitude2D (void)
{
    return (float) sqrt (x*x + y*y);
}

Pour comprendre ce qu'est un vecteur normalisé il faut connaître sa définition de magnitude ! En fait un vecteur normalisé c'est juste un vecteur de longueur unitaire. Pour normaliser un vecteur nous allons donc diviser chacun de ses termes par sa taille. (Attention de bien vérifier que length != 0 !!!)

void tfVector :: Normalize (void)
{
    float length;

    length = Magnitude ();
    
    if (length)
    {
        this->x /= length;
        this->y /= length;
        this->z /= length;
    }
}

Bon ici y'a rien a comprendre ! Soit vous connaissez vos formules compliquées de rotation dans l'espace soit vous essayez de comprendre et d'assimiler ! La seule chose qu'on peut dire c'est que l'array angles est au format classique PITCH YAW ROLL.

void tfVector :: Rotate (float angles[3])
{
    float point[3];
    CopyToArray (point);

    this->x = cos (angles[0])*cos (angles[1])*point[0] - cos (angles[0])*sin (angles[1])*point[1] + sin (angles[0])*point[2];
    this->y = (sin (angles[0])*sin (angles[2]) + cos (angles[2])*sin (angles[1]))*point[0] + (-sin (angles[2])*sin (angles[0]) + cos (angles[2])*cos (angles[1]))*point[1] - sin (angles[2])*cos (angles[0])*point[2];
    this->z = (-cos (angles[2])*sin (angles[0]) + sin (angles[2])*sin (angles[1]))*point[0] + (cos (angles[2])*sin (angles[0]) + sin (angles[2])*cos (angles[1]))*point[1] + cos (angles[2])*cos (angles[0])*point[2];
}

Oui je vous dis oui tout de suite, c'est bien le code d'Half-Life retapé ce que vous avez là ! on récupère dans angles[3] les angles PITCH et YAW définis par notre vecteur. (dans HL on vous demande un point forward, ben ici le point forward c'est XYZ !)

void tfVector :: GetAngles (float angles[3])
{
    if (this->x == 0 && this->y == 0)
    {
        angles[1] = 0;

        if (this->z > 0)
            angles[0] = (float)PI*0.5f;
        else
            angles[0] = (float)PI*1.5f;
    }
    else
    {
        angles[1] = atan2 (this->y, this->x);

        if (angles[1] < 0)
            angles[1] += (float)PI*2;

        angles[0] = atan2 (this->z, Magnitude2D ());
    }
}

Ca simplifie souvent du code en le mettant comme fonction :) juste dire que vecteur = -vecteur nous retourne notre vecteur :) (ça fait beaucoup de vecteur sur une phrase !)

void tfVector :: Reverse ()
{
    this->x = -this->x;
    this->y = -this->y;
    this->z = -this->z;
}

Bon voici ici encore des mathz qui ne sont compréhensibles que si vous connaissez bien le sujet ! Sachez juste que pour un angles[3] on peut définir les 3 axes suivant l'angle ! A savoir le vecteur qui pointe dans la direction de l'angle (forward), celui qui va vers la droite perpendiculairement à notre forward et enfin celui qui va vers le haut de la même façon.

void tfVector :: SetForward (float angles[3])
{
    this->x = cos (angles[0])*cos (angles[1]);
    this->y = cos (angles[0])*sin (angles[1]);
    this->z = -sin (angles[0]);
}

void tfVector :: SetRight (float angles[3])
{
    this->x = -sin (angles[2])*sin (angles[0])*cos (angles[1]) + cos (angles[2])*sin (angles[1]);
    this->y = -sin (angles[2])*sin (angles[0])*sin (angles[1]) - cos (angles[2])*cos (angles[1]);
    this->z = -sin (angles[2])*cos (angles[0]);
}

void tfVector :: SetUp (float angles[3])
{
    this->x = cos (angles[2])*sin (angles[0])*cos (angles[1]) + sin (angles[2])*sin(angles[1]);
    this->y = cos (angles[2])*sin (angles[0])*sin(angles[1]) - sin (angles[2])*cos (angles[1]);
    this->z = cos (angles[2])*cos (angles[0]);
}

Bien entendu il existe aussi la gauche, le bas et le backward ! Ce sont juste les inverses des autres vecteurs.

void tfVector :: SetBackward (float angles[3])
{
    SetForward (angles);
    Reverse ();
}

void tfVector :: SetLeft (float angles[3])
{
    SetRight (angles);
    Reverse ();
}

void tfVector :: SetDown (float angles[3])
{
    SetUp (angles);
    Reverse ();
}

On commence ici avec quelque chose de plus abordable, l'opération Vecteur1 += Vecteur2. Pour se faire, on doit juste ajouter X du vecteur2 à notre vecteur1, etc. pour Y et Z.

tfVector& tfVector :: operator+= (tfVector u)
{
    this->x += u.x;
    this->y += u.y;
    this->z += u.z;

    return *this;
}

Idem pour le -= sauf qu'ici on retire bien vecteur2 bien entendu :)

tfVector& tfVector :: operator-= (tfVector u)
{
    this->x -= u.x;
    this->y -= u.y;
    this->z -= u.z;

    return *this;
}

La multiplication scalaire ! Par un nombre réel quoi. il suffit de multiplier XYZ par s. Ca va « allonger » notre vecteur (ou le rétrécir si 0 < s < 1)

tfVector& tfVector :: operator*= (float s)
{
    this->x *= s;
    this->y *= s;
    this->z *= s;

    return *this;
}

Idem sauf qu'ici on va le rétrécir de s (ou l'allonger si 0 < s < 1 mais bon ça fait un peu compliqué !)

tfVector& tfVector :: operator/= (float s)
{
    this->x /= s;
    this->y /= s;
    this->z /= s;

    return *this;
}

Idem que la fonction inverse sauf qu'ici si on note -vecteur1 C++ va comprendre.

tfVector tfVector :: operator- (void)
{
    return tfVector (-this->x, -this->y, -this->z);
}

Compare XYZ de notre vecteur courrant et du vecteur u.

bool tfVector :: operator== (tfVector& u)
{
    return this->x == u.x && this->y == u.y && this->z == u.z;
}

Ca semble zarb mais tout ce qu'on fait c'est regarder si l'opérator == renvoit false !

bool tfVector :: operator!= (tfVector& u)
{
    return !(*this == u);
}

On regarde si la magnitude est plus grande ou égale que celle du vecteur u.

bool tfVector :: operator>= (tfVector& u)
{
    return Magnitude () >= u.Magnitude ();
}

bool tfVector :: operator> (tfVector& u)
{
    return Magnitude () > u.Magnitude ();
}

Idem mais plus petit !

bool tfVector :: operator<= (tfVector& u)
{
    return Magnitude () <= u.Magnitude ();
}

bool tfVector :: operator< (tfVector& u)
{
    return Magnitude () < u.Magnitude ();
}

&= ouchla ça ce voit pas souvent ça ! C'est juste une petite astuce pour voir si les magnitude sont égale car == est déjà utilisé !

bool tfVector :: operator&= (tfVector& u)
{
    return Magnitude () == u.Magnitude ();
}

ATTENTION ICI ON EST PLUS DANS LA CLASSE !!!

Au cas ou on note vecteur3 = vecteur1 + vecteur2

tfVector operator+ (tfVector u, tfVector v)
{
    return tfVector (u.x+v.x, u.y+v.y, u.z+v.z);
}

tfVector operator- (tfVector u, tfVector v)
{
    return tfVector (u.x-v.x, u.y-v.y, u.z-v.z);
}

CROSS PRODUCT : Le produit vectoriel ! Hyper important ! Il permet de trouver un troisième vecteur perpendiculaire aux deux autres ! Par exemple (0,1,0)^(1,0,0) = (0,0,1), etc. mais c'est surtout pratique pour calculer la normale d'un plan (mais ça on en reparlera plus tard dans un autre tutorial !)

tfVector operator^ (tfVector u, tfVector v)
{
    return tfVector (u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x);
}

DOT PRODUCT : Important ça, le v*u. Le produit scalaire. Il est défini soit par ||a||*||b||*cos (a) soit par x1*x2 + y1*y2 + z1*z2. Il correspond à la projection d'un vecteur A sur un vecteur B. Prenez une feuille de papier et dessinez 2 vecteurs de la même origine. Définissez C comme étant le point obtenu en traçant une droite perpendiculaire à B avec comme origine l'extrémité de A. le produit scalaire correspond à la distance OC (O = origine). Cela permet de savoir si un vecteur est perpendiculaire à une autre (u*v = 0) ou si ils "regardent" dans des direction opposées (u*v < 0). Egalement pratique pour faire un aim dans les jeux vidéos ! Si on prend 2 vecteurs unitaires correspondants à l'angle de vue du joueur et à l'angle entre le joueur 1 et le joueur 2 et qu'on prend le produit scalaire et que celui ci est compris entre (par exemple) 0.9 et 1, et bien on sait que le joueur1 vise le joueur2 (ou que son curseur est tout près !)

float operator* (tfVector u, tfVector v)
{
    return (u.x*v.x + u.y*v.y + u.z*v.z);
}

C'est juste au cas ou on écrit tfVector = tfVector*float. Ca fait un scale.

tfVector operator* (tfVector u, float s)
{
    return tfVector (u.x*s, u.y*s, u.z*s);
}

Surtout pas oublier le rigolo qui irait écrire float*vecteur ;)

tfVector operator* (float s, tfVector u)
{
    return tfVector (u.x*s, u.y*s, u.z*s);
}

Idem pour / sauf qu'on ne peut pas diviser un float par un vecteur !

tfVector operator/ (tfVector u, float s)
{
    return tfVector (u.x/s, u.y/s, u.z/s);
}

C'est quoi ce truc de fou me direz-vous ? Il correspond à la matrice définie par Ux, Uy, Uz, Vx, Vy, Vz, Wx, Wy, Wz. Ca sert évidement à plein de choses mais je l'ai juste taper là à titre informatif (comme ça vous savez que ça existe !)

float TripleScalarProduct (tfVector u, tfVector v, tfVector w)
{
    return u*(v^w);
}

J'allais oublier le fichier .h avec pleins de définitions utiles :

#ifndef __VECTOR_H__
#define __VECTOR_H__

#pragma warning (disable: 4244)

#define    PI    3.14159265358979323846
#define TO_RAD(a)    (a*PI/180)
#define TO_DEG(a)    (a*180/PI)

class tfVector {
public:
    tfVector (void);
    tfVector (float x, float y, float z);
    tfVector (tfVector& v);
    tfVector (float p[3]);

    float Magnitude (void);
    float Magnitude2D (void);
    void Normalize (void);
    void Reverse (void);

    void GetAngles (float angles[3]);
    void SetRight (float angles[3]);
    void SetUp (float angles[3]);
    void SetLeft (float angles[3]);
    void SetDown (float angles[3]);
    void SetForward (float angles[3]);
    void SetBackward (float angles[3]);

    tfVector& operator+= (tfVector u); /* Will add u vector to the current one */
    tfVector& operator-= (tfVector u); /* Will substract u vector */
    tfVector& operator*= (float s); /* Will multiply each elements by s */
    tfVector& operator/= (float s); /* will divide each elements by s */

    tfVector operator- (void);/* return the inverse */
    bool operator== (tfVector& u); /* return true if xyz is equal */
    bool operator!= (tfVector& u); /* return true if xyz is different */

    bool operator&= (tfVector& u); /* Mangitude Equals Comparaison*/
    bool operator<= (tfVector& u); /* Mangitude Comparaison */
    bool operator< (tfVector& u); /* Mangitude Comparaison */
    bool operator>= (tfVector& u); /* Mangitude Comparaison */
    bool operator> (tfVector& u); /* Mangitude Comparaison */

    void CopyToArray (float point[3]);
    void Rotate (float angles[3]);

    float x;
    float y;
    float z;
};

tfVector operator+ (tfVector u, tfVector v); /* u+v */
tfVector operator- (tfVector u, tfVector v); /* u-v */
tfVector operator^ (tfVector u, tfVector v); /* normal of u and v */
float operator* (tfVector u, tfVector v); /* dot product */
tfVector operator* (tfVector u, float s); /* u*s */
tfVector operator* (float s, tfVector u); /* s*u */
tfVector operator/ (tfVector u, float s); /* u/s */
float TripleScalarProduct (tfVector u, tfVector v, tfVector w); /* u*(v^w) */

#endif