Contenu mis en avant
Dépliage

Plugin Cinema 4D

Alvéole

Plugin Cinema 4D

Projecteur UV

Plugin Cinema 4D

 

Description

Réalisation d'un hologramme 3d basé sur le principe de réflexion d'image sur une pyramide transparente.

Projet réalisé avec Cédric Bitsch.

Rendu 3D en OpenGL.

Modèle 3D

Guêpe modélisée dans Cinema 4D.

Export 3D en Python

Pour exporter notre guêpe nous avons utilisé un script Python dans Cinema 4D afin d’obtenir les coordonnées des points

et des polygones sous forme de tableaux en langage C.

Hiérarchie des objets

Chaque partie du corps de la guêpe est indépendante des autres, ce qui permet d’animer l’insecte.

Nous avons utilisé une méthode hiérarchique pour lier les éléments entre eux (méthode push / pop).

    glPushMatrix();
    dessineObjet(&obj_Guepe);
    
    
    
    dessineObjet(&obj_Corps); // A un enfant
    
    glPushMatrix();
    
    dessineObjet(&obj_Tete); // A un enfant
    
    glPushMatrix();
    dessineObjet(&obj_AntenneG); // A un suivant
    glPopMatrix();
    
    glPushMatrix();
    dessineObjet(&obj_AntenneD); // Suivant
    glPopMatrix();
    
    glPopMatrix();
    
    glPushMatrix();
    dessineObjet(&obj_Cul); // Parent, Suivant
    glPopMatrix();
    
    glPushMatrix();
    dessineObjet(&obj_PatteD1); // Suivant
    glPopMatrix();
    
    glPushMatrix();
    dessineObjet(&obj_PatteD2); // Suivant
    glPopMatrix();
    
    glPushMatrix();
    dessineObjet(&obj_PatteD3); // Suivant
    glPopMatrix();
    
    glPushMatrix();
    dessineObjet(&obj_PatteG1); // Suivant
    glPopMatrix();
    
    glPushMatrix();
    dessineObjet(&obj_PatteG2); // Suivant
    glPopMatrix();
    
    glPushMatrix();
    dessineObjet(&obj_PatteG3); // Suivant
    glPopMatrix();
    
    glPushMatrix();
    dessineObjet(&obj_AileD1); // Suivant
    glPopMatrix();
    
    glPushMatrix();
    dessineObjet(&obj_AileD2); // Suivant
    glPopMatrix();
    
    glPushMatrix();
    dessineObjet(&obj_AileG1); // Suivant
    glPopMatrix();
    
    glPushMatrix();
    dessineObjet(&obj_AileG2); // Suivant
    glPopMatrix();
    
    
    glPopMatrix();
Rotation des membres

Nous avons créé une rotation automatique des antennes et des pattes afin de créer un effet de réalisme.

Nous avons utilisé 2 rotations dans 2 axes différents, chacune animée d’un mouvement sinusoïdal décalé afin d’obtenir un mouvement circulaire.

Mouvements au sol

Au sol notre guêpe adapte l’allure du mouvement de ses pattes suivant la vitesse de déplacement.

Nous avons un système de rotation identique à celui des antennes.

        // Animation pattes
            float vitesse = sqrt(_obj->vit.x * _obj->vit.x + _obj->vit.z * _obj->vit.z);
            vitesse *= 50;
    
            vitesse *= 1. - limite(_obj->posAbs.y, 0.0, 1.0);;
    
            if (_obj->posAbs.y < 0.1) {
    
                // Patte avant gauche
    
                obj_PatteD1.rotcos += 0.1 * vitesse;
                limCos(&obj_PatteD1.rotcos);
                obj_PatteD1.angle = cos(obj_PatteD1.rotcos) * 20;
                obj_PatteD1.rotation = {0., 1., 0.};
    
                obj_PatteD1.rotcos2 += 0.1 * vitesse;
                limCos(&obj_PatteD1.rotcos2);
                obj_PatteD1.angle2 = cos(obj_PatteD1.rotcos2 + PIs2) * 20;
                obj_PatteD1.rotation2 = {0., 0., 1.};
    
                // Patte avant droite
    
                obj_PatteG1.rotcos += -0.1 * vitesse;
                limCos(&obj_PatteG1.rotcos);
                obj_PatteG1.angle = cos(obj_PatteG1.rotcos) * 20;
                obj_PatteG1.rotation = {0., 1., 0.};
    
                obj_PatteG1.rotcos2 += -0.1 * vitesse;
                limCos(&obj_PatteG1.rotcos2);
                obj_PatteG1.angle2 = cos(obj_PatteG1.rotcos2 + PIs2) * 20;
                obj_PatteG1.rotation2 = {0., 0., 1.};
    
                // Patte milieu gauche
    
                obj_PatteD2.rotcos += 0.1 * vitesse;
                limCos(&obj_PatteD2.rotcos);
                obj_PatteD2.angle = cos(obj_PatteD2.rotcos + PIs2) * 20;
                obj_PatteD2.rotation = {0., 1., 0.};
    
                obj_PatteD2.rotcos2 += 0.1 * vitesse;
                limCos(&obj_PatteD2.rotcos2);
                obj_PatteD2.angle2 = cos(obj_PatteD2.rotcos2) * 20;
                obj_PatteD2.rotation2 = {0., 0.5, 1.};
    
                // Patte milieu droite
    
                obj_PatteG2.rotcos += -0.1 * vitesse;
                limCos(&obj_PatteG2.rotcos);
                obj_PatteG2.angle = cos(obj_PatteG2.rotcos + 1.7) * 20;
                obj_PatteG2.rotation = {0., 1., 0.};
    
                obj_PatteG2.rotcos2 += 0.1 * vitesse;
                limCos(&obj_PatteG2.rotcos2);
                obj_PatteG2.angle2 = cos(obj_PatteG2.rotcos2 + PIs2) * 20;
                obj_PatteG2.rotation2 = {0., 0., 1.};
    
                // Patte arrère gauche
    
                obj_PatteD3.rotcos += 0.1 * vitesse;
                limCos(&obj_PatteD3.rotcos);
                obj_PatteD3.angle = cos(obj_PatteD3.rotcos) * 20;
                obj_PatteD3.rotation = {0., 1., 0.};
    
                obj_PatteD3.rotcos2 += 0.1 * vitesse;
                limCos(&obj_PatteD3.rotcos2);
                obj_PatteD3.angle2 = cos(obj_PatteD3.rotcos2 + PIs2) * 20;
                obj_PatteD3.rotation2 = {1., 0., 0.};
    
                // Patte arrère droite
    
                obj_PatteG3.rotcos += -0.1 * vitesse;
                limCos(&obj_PatteG3.rotcos);
                obj_PatteG3.angle = cos(obj_PatteG3.rotcos) * 20;
                obj_PatteG3.rotation = {0., 1., 0.};
    
                obj_PatteG3.rotcos2 += 0.1 * vitesse;
                limCos(&obj_PatteG3.rotcos2);
                obj_PatteG3.angle2 = cos(obj_PatteG3.rotcos2 + PIs2) * 20;
                obj_PatteG3.rotation2 = {1., 0., 0.};
            }
}
Animation en vol

Lors de son envol nous avons un mouvement de repli des pattes.

Nous avons également un mouvement des ailes basé sur le même principe d’accélération que pour le mouvement au sol.

Lumières

Nous avons créé 2 lumières :

- La première est une lumière ambiante (couleur froide).

- La seconde est un projecteur (couleur chaude) qui met en relief la guêpe.

Caméra

Nous avons 4 caméras :

- La première pour la vue pleine écran qui tourne autour de la guêpe.

- Les 3 autres sont réservé pour afficher l’hologramme.

Vecteur calculeCam(int ax, int ay) {
    Vecteur _c = {1,1,1};
    _c.x = 2 * sin(ax * 3.14 / 180.) * cos(ay * 3.14 / 180.) + cameraPos.x;
    _c.y = 2 * sin(ay * 3.14 / 180.) + cameraPos.y;
    _c.z = 2 * cos(ax * 3.14 / 180.) * cos(ay * 3.14 / 180.) + cameraPos.z;
    return _c;
}
    
Vecteur calculeU(Vecteur camPos, Vecteur ciblePos, int idCam) {
    
    Vecteur _v1 = vecteurMoins(camPos, ciblePos);
    Vecteur _v2 = vecteurMoins(camPos, ciblePos); _v2.y = 0;
    Vecteur _u;
    
    if (idCam == 2) { // Milieu
        Vecteur _w = produitVectoriel(_v1, _v2);
        _u = produitVectoriel(_v1, _w);
        _u = vecteurProduit(_u, -1);
    
        if (camPos.y - ciblePos.y < 0) _u = vecteurProduit(_u, -1);
    }
    
    else if (idCam == 1) { // Gauche
        _u = produitVectoriel(_v1, _v2);
        _u = vecteurProduit(_u, -1);
    
        if (camPos.y - ciblePos.y < 0) _u = vecteurProduit(_u, -1);
    }
    
    else if (idCam == 3) { // Droite
        _u = produitVectoriel(_v1, _v2);
    
        if (camPos.y - ciblePos.y < 0) _u = vecteurProduit(_u, -1);
    }
    
    else if (idCam == 4) { // Gauche
        Vecteur _w = produitVectoriel(_v1, _v2);
        _u = produitVectoriel(_v1, _w);
    
        if (camPos.y - ciblePos.y < 0) _u = vecteurProduit(_u, -1);
    }
    
    return _u;
}
    
// [...]
    
                gluPerspective(60., 1920. / 1080. , 0.01, 40.);
                Vecteur camPos = calculeCam(anglex, angley);
                Vecteur _u = calculeU(camPos, obj_Guepe.posAbs, 4);
    
                gluLookAt(camPos.x, camPos.y, camPos.z, // Position de la caméra
                        obj_Guepe.posAbs.x, obj_Guepe.posAbs.y, obj_Guepe.posAbs.z, // Position où la cam regarde
                        _u.x, _u.y, _u.z); // Vecteur haut
Textures

Nous avons trois textures :

- Une pour la guêpe.

- Une pour le sol.

- Une pour les arbres.

Nous avons utilisé une fonction qui lit les fichiers bmp pour importer les textures dans GLUT.

GLuint LoadTexture(const char * filename, int width, int height) {
    GLuint texture;
    unsigned char * data;
    FILE* file;
    
    file = fopen(filename, "rb");
    
    if (file == NULL) return 0;
    
    data = (unsigned char *)malloc(width * height * 3);
    //int size = fseek(file,);
    fread(data, width * height * 3, 1, file);
    fclose(file);
    
    for (int i = 0; i < width * height ; ++i) {
        int index = i*3;
        unsigned char B,R;
        B = data[index];
        R = data[index+2];
    
        data[index] = R;
        data[index+2] = B;
    }
    
    glGenTextures( 1, &texture );
    glBindTexture( GL_TEXTURE_2D, texture );
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST );
    
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_REPEAT );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_REPEAT );
    gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height,GL_RGB, GL_UNSIGNED_BYTE, data );
    
    free( data );
    
    return texture;
}
Normales

Nous avons calculé les normales des sommets manuellement.

- La normale d’un sommet = moyenne des normales des polygones autours du sommet.

- La normale d’un polygone = produit vectoriel de 2 côtés adjacents.

Les normales permettent d’avoir un rendu plus lisse de l’objet face à la lumière.

void calculeNormaleObjet(Objet* _obj) {
    Vecteur (*_normales)[4] = _obj->normales;
    Vecteur *_p = _obj->points;
    int (*_f)[4] = _obj->polys;
    int nbPolys = _obj->nbpolys;
    int nbPts = _obj->nbpts;
    int i;
    Vecteur tabNorPts[nbPts];
    
    for (i = 0 ; i < nbPts ; i++) {
        tabNorPts[i] = {0.0, 0.0, 0.0};
    }
    
    for (i = 0 ; i < nbPolys ; i++) {
        Vecteur normalePoly = calculeNormalePol(_p[_f[i][0]], _p[_f[i][1]], _p[_f[i][2]], _p[_f[i][3]]);
        tabNorPts[_f[i][0]] = vecteurPlus(tabNorPts[_f[i][0]], normalePoly);
        tabNorPts[_f[i][1]] = vecteurPlus(tabNorPts[_f[i][1]], normalePoly);
        tabNorPts[_f[i][2]] = vecteurPlus(tabNorPts[_f[i][2]], normalePoly);
        tabNorPts[_f[i][3]] = vecteurPlus(tabNorPts[_f[i][3]], normalePoly);
    }
    
    for (i = 0 ; i < nbPts ; i++) {
        tabNorPts[i] = vecteurNormaliser(tabNorPts[i]);
    }
    
    for (i = 0 ; i < nbPolys ; i++) {
        _normales[i][0] = tabNorPts[_f[i][0]];
        _normales[i][1] = tabNorPts[_f[i][1]];
        _normales[i][2] = tabNorPts[_f[i][2]];
        _normales[i][3] = tabNorPts[_f[i][3]];
    }
    
    free(tabNorPts);
}
Commandes

Flèches directionnelles : Avancer, reculer, pivoter à droite, pivoter à gauche.

Espace ou + : Grand saut

C : Petit saut

H ou * : Vue hologramme / vue 3d classique

/ : Attaque dard

Q : Quitter

Difficultés rencontrées
Push et pop

Nous avons rencontré une difficulté en voulant créer automatiquement la génération des push et des pop.

Nous voulions gérer la mise en place des différentes parties de notre guêpe dans une fonction.

Cette fonction aurait été très bénéfique car nous aurions pu intégrer dans notre projet n’importe

quel « robot » sans avoir à gérer manuellement les push et les pop.

Nous avions commencé a créer un système de hiérarchie entre les différents éléments de la guêpe grâce à un système de liste chainé.

obj_Guepe.enfant = &obj_Corps;
obj_Corps.parent = &obj_Guepe;
obj_Corps.enfant = &obj_Tete;
 
obj_Tete.parent = &obj_Corps;
obj_Tete.enfant = &obj_AntenneG;
obj_Tete.suivant = &obj_PatteD1;
 
obj_AntenneG.parent = &obj_Tete;
obj_AntenneG.suivant = &obj_AntenneD;
 
obj_AntenneD.parent = &obj_Tete;
obj_AntenneD.precedent = &obj_AntenneG;
 
obj_PatteD1.parent = &obj_Corps;
obj_PatteD1.precedent = &obj_Tete;
obj_PatteD1.suivant = &obj_PatteD2;
 
obj_PatteD2.parent = &obj_Corps;
obj_PatteD2.precedent = &obj_PatteD1;
obj_PatteD2.suivant = &obj_PatteD3;
 
obj_PatteD3.parent = &obj_Corps;
obj_PatteD3.precedent = &obj_PatteD2;
obj_PatteD3.suivant = &obj_Cul;
Disposition des écrans pour la création de l’hologramme

Une des parties les plus délicates du projet a été la mise en place des 3 vues pour générer l’hologramme.

Nous avons rencontré des difficultés sur l’aménagement et l'angle des vues. Au final nous avons réussi à figer les vues et à obtenir un très bel hologramme bien que les 3 écrans se superposent légèrement. Nous nous sommes aidé d'un logiciel 3D pour simuler les triangles et les réflexions sur les faces du plexiglas.

Support en bois

Nous avons réalisé un socle en bois avec une ouverture à l’arrière afin d’insérer l’ordinateur portable.

Nous avons créé un logo représentant notre guêpe.

Le support est totalement rétractable pour le rangement et le déplacement.

Polyméthacrylate de méthyle

Nous avons opté pour un hologramme avec 3 plans de plexiglas.

Nous avons 1 pièce centrale et 2 pièces pour les cotés. Nous avons remarqué que la réflexion de l'écran se faisait des deux côtés du plexiglas, ce qui dédoublait l'image. Une feuille de plexiglas très fine permet de réduire ce dédoublement mais la rend parfois trop souple.

person
create
Les plus récents
keyboard_arrow_down