TD1 - Navigation 3D et interactions simples

1. Première scène 3D

Après avoir suivi le tutoriel conseillé pour la création de la scène, j’initialise bien un Renderer, une Scene, une Camera ainsi qu’un TrackballControls. Ces objets sont initialisés puis mis à jours dans la fonction animate. Ensuite pour générer les cubes, j’ai implementer la classe TexturedCube qui hérite de la classe THREE.Mesh. Le constructeur de cette classe prend en parametre soit une texture, ou bien un tableau de 6 textures qui seront appliquées à un cube de longueur 1. Enfin, afin d’éviter de devoir utiliser tout le temps ce constructeur et donc les textures, j’ai implémenté un “générateur de cube” qui vient stocké un cube et le clonera à la demande.

THREE.TexturedCube = function(textures) {
  if (textures) {
    let geometry = new THREE.BoxGeometry(1, 1, 1);
    let materials = [];
    
    if (!Array.isArray(textures)){
      materials = new THREE.MeshBasicMaterial( {map : textures});
    } else {
      for (let tex of textures) {
	materials.push(new THREE.MeshBasicMaterial( { map: tex }));
      }
    }

    THREE.Mesh.call(this, geometry, materials);
  } else {
    THREE.Mesh.call(this);
  }
};

THREE.TexturedCube.prototype = Object.create(THREE.Mesh.prototype);
function CubeGenerator (oa) {
  this.oa = oa;
}

CubeGenerator.prototype.setOA = function(texturedCube) {
  this.oa = texturedCube;

  return this;
};

CubeGenerator.prototype.getOne = function () {
  let the_one = this.oa.clone();

  let mesh_id = the_one.id;
  let mesh_geom = the_one.geometry;
  
  applyFaceColor(mesh_geom, mesh_id);

  return the_one;
};

2. Vue à la première personne

Ensuite, pour ajouter la vue à la première personne, j’ai utilisé (comme proposé) un PointerLockControls au lieu du TrackballControls que j’ai ajouté à la scène. En utilisant la PointerLock API et ce contrôleur on obtient bien une camera à la première personne.

Pour obtenir un déplacement à la première personne, il suffit juste de capturer les événements du clavier (keyup et keydown) pour mettre à jour la position de la camera.

Enfin, en utilisant une camera orthogonale et une scène contenant tous les objets du HUD (deux lignes blanches dans notre cas), on peut afficher en surimpression un réticule de visé. Pour ce faire, il nous a fallu mettre l’attribut autoClear du renderer à false pour pouvoir nettoyer l’affichage, effectuer le rendu de la scène, nettoyer le depth buffer et afficher le réticule.

3. Sélection d’un objet

Afin d’implémenter le color picking, il a fallut d’abord attribuer à chaque objet une couleur unique (à l’aide de son id). Ainsi, à chaque fois que l’on viendra cloner un cube à l’aide du CubeGenerator, on vient appeler la fonction applyFaceColor proposée. Néanmoins, cette solution ne fonctionnait pas. En effet, lors du clonage d’un THREE.Mesh, la géométrie du mesh cloné était réutilisée et non clonée. Ceci faisait que tous les cubes clonés à partir du cube originel avaient la même couleur unique. Pour résoudre cela, j’ai du surcharger la méthode clone de la classe THREE.Mesh tel que :

THREE.Mesh.prototype.clone = function () {
  return new this.constructor( this.geometry.clone(), this.material ).copy( this );
};

Ensuite on effectue un rendu de la scène en surchargeant le matériau d’affichage avec la couleur des faces pour chaque objet dans un buffer. Ainsi, il nous suffit à chaque clic de la souris (événement click) de venir lire dans ce buffer la couleur sous le réticule, en déduire l’id du cube visé et appliquer l’opération souhaitée.

Conclusion

L’ensemble du TD a été effectué et peut être testé directement en ligne en suivant le lien approprié.

Liens utiles

Sujet du TD

Demo du rendu final

Code source du rendu final

Archive contenant le code source