Initial commit
@ -0,0 +1 @@
|
||||
assets
|
@ -0,0 +1,79 @@
|
||||
# TP Interaction
|
||||
|
||||
Ce TP a pour but de vous faire coder des interactions avec un modèle 3D. Pour
|
||||
cela, vous implémenterez en Javascript des interactions avec la souris et avec
|
||||
les touches du clavier qui permettent de changer de point de vue, c'est à dire,
|
||||
de mettre à jour la position et l'orientation de la caméra (Partie 1), et
|
||||
ensuite d'enregistrer de tels points de vues pour permettre une navigation plus
|
||||
haut niveau (Partie 2).
|
||||
|
||||
Ce TP sera fait pendant les séances consacrées (un TP jeudi 9, un TP jeudi 16).
|
||||
|
||||
### Cloner le repository
|
||||
|
||||
```
|
||||
git clone https://gitea.tforgione.fr/tforgione/tp-interaction
|
||||
```
|
||||
|
||||
### Lancer le server
|
||||
|
||||
```
|
||||
python -m SimpleHTTPServer
|
||||
```
|
||||
|
||||
Vous pouvez ensuite aller sur la page [localhost:8000](http://localhost:8000)
|
||||
pour lancer l'interface. Pour voir la console vous pouvez cliquer sur `F12`,
|
||||
ce qui vous permettra de debugger plus facilement.
|
||||
|
||||
### Sujet
|
||||
|
||||
#### Partie 1 : interaction clavier / souris
|
||||
|
||||
L'objectif de cette premiere partie est d'implémenter les interactions type
|
||||
*free fly camera*. Nous vous proposons de mettre à jour les interactions avec
|
||||
la souris pour tourner la caméra et avec le clavier pour la deplacer.
|
||||
|
||||
L'utilisateur peut appuyer sur les flèches du clavier pour déplacer la caméra :
|
||||
- flèche en haut (ou touche z) pour avancer
|
||||
- flèche du bas (ou touche s) pour reculer
|
||||
- flèche de gauche (ou touche q) pour se translater vers la gauche
|
||||
- flèche de droite (ou touche d) pour se translater vers la droite
|
||||
|
||||
Ces directions seront définies à partir de l'orientation de la caméra, donnée
|
||||
par 2 angles `theta` et `delta`.
|
||||
|
||||

|
||||
|
||||
Vous pouvez trouver des exemples de coordonnées sphériques sur
|
||||
[wikipedia](https://fr.wikipedia.org/wiki/Coordonn%C3%A9es_sph%C3%A9riques).
|
||||
|
||||
**Attention** : dans THREE.js, l'axe des y est celui qui va vers le haut.
|
||||
|
||||
Pour cette partie, vous aurez besoin de compléter les *TODO Part 1* du fichier
|
||||
`js/main.js`.
|
||||
|
||||
#### Partie 2 : interaction via point de vue
|
||||
|
||||
L'objectif de cette deuxième partie est d'utiliser les points de vue
|
||||
enregistrés pour faciliter l'interaction. Pour commencer, vous pouvez
|
||||
décommenter les lignes 236 à 239 du fichier `js/main.js` pour faire apparaitre
|
||||
un point de vue. Vous devez ensuite compléter les *TODO Part 2* pour permettre
|
||||
à un utilisateur de cliquer sur un point de vue pour s'y déplacer.
|
||||
|
||||
### Les vecteurs dans THREE.js
|
||||
|
||||
Ce TP utilise le moteur de rendu THREE.js. Puisque javascript ne permet pas la
|
||||
surcharge d'opérateurs, il est impossible d'écrire `x + y` quand `x` et `y`
|
||||
sont des vecteurs de THREE.js. Ansi, THREE.js définit des méthodes pour
|
||||
effectuer ces opérations :
|
||||
|
||||
| Avec opérateurs | En THREE.js |
|
||||
|:----:|:---:|
|
||||
| `a += b` | `a.add(b)` |
|
||||
| `a -= b` | `a.sub(b)` |
|
||||
| `a *= l` | `a.multiplyScalar(l)` |
|
||||
| `a = b` | `a.copy(b)` |
|
||||
| `a + b` | `a.clone().add(b)` |
|
||||
| `a = b + c` | `a.copy(b.clone().add(c))` |
|
||||
| ... | ... |
|
||||
|
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 5.4 KiB |
@ -0,0 +1,354 @@
|
||||
# Blender MTL File: 'None'
|
||||
# Material Count: 32
|
||||
|
||||
newmtl m0lambea_v_x_v_x
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd a_tuta.png
|
||||
|
||||
newmtl m10lambert12_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_siba04.png
|
||||
|
||||
newmtl m11lambert14_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd rotennuno85.png
|
||||
|
||||
newmtl m12lambert15_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd wood04.png
|
||||
|
||||
newmtl m13lambert2_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd a_kusa.png
|
||||
|
||||
newmtl m14lambert6_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_yuka10.png
|
||||
|
||||
newmtl m15lambert74_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd a_iwa.png
|
||||
|
||||
newmtl m16lambert75_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd rotennuno4.png
|
||||
|
||||
newmtl m17lambert84_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_kabe33.png
|
||||
|
||||
newmtl m18lambert85_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_ren15.png
|
||||
|
||||
newmtl m19lambert8_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_miti05.png
|
||||
|
||||
newmtl m1lambert100_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_ki12.png
|
||||
|
||||
newmtl m20lambert92_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_yane08.png
|
||||
|
||||
newmtl m21lambert94_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_ki13.png
|
||||
|
||||
newmtl m22lambert95_v_x_v_x
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd a_kusa_yane.png
|
||||
|
||||
newmtl m23lambert98_v_x_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd gakkou03.png
|
||||
|
||||
newmtl m24lambert98_v_x_v_x
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd gakkou03.png
|
||||
|
||||
newmtl m25lambert99_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_mado08.png
|
||||
|
||||
newmtl m26lambertaa_v_v_x
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd a_kysakiwa.png
|
||||
|
||||
newmtl m27lambv_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_mado02.png
|
||||
|
||||
newmtl m28test_lambert105_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_leaf01.png
|
||||
|
||||
newmtl m29test_lambert48_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd k_taru04.png
|
||||
|
||||
newmtl m2lambert101_v_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_komono11.png
|
||||
|
||||
newmtl m30test_lambert49_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd k_taru05.png
|
||||
|
||||
newmtl m31lambert12_v_v_2_
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_siba04.png
|
||||
|
||||
newmtl m3lambert102_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_ren16.png
|
||||
|
||||
newmtl m4lambert103_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_kabe38.png
|
||||
|
||||
newmtl m5lambert104_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_leaf05.png
|
||||
|
||||
newmtl m6lambert107_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd m_leaf09.png
|
||||
|
||||
newmtl m7lambert10_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd wood01.png
|
||||
|
||||
newmtl m8lambert115_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd rotennuno70.png
|
||||
|
||||
newmtl m9lambert11_v_v
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd wood06.png
|
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 590 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 99 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 639 B |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 115 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 91 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 3.4 KiB |
@ -0,0 +1,20 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>TP</title>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body {
|
||||
overflow: hidden;
|
||||
margin: 0px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script src="js/three.min.js"></script>
|
||||
<script src="js/OBJLoader.js"></script>
|
||||
<script src="js/MTLLoader.js"></script>
|
||||
<script src="js/Bookmark.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,83 @@
|
||||
class Bookmark extends THREE.Object3D {
|
||||
constructor(position, target, scale = 500.0) {
|
||||
super();
|
||||
|
||||
position = new THREE.Vector3().copy(position);
|
||||
target = new THREE.Vector3().copy(target);
|
||||
|
||||
this.position.copy(position);
|
||||
this.target = new THREE.Vector3().copy(target);
|
||||
|
||||
this.t = 0;
|
||||
|
||||
let material = new THREE.LineBasicMaterial({
|
||||
color: 0xffffff,
|
||||
linewidth: 3,
|
||||
});
|
||||
let geometry = new THREE.Geometry();
|
||||
|
||||
let direction = this.target.clone().sub(position).normalize().multiplyScalar(this.length());
|
||||
|
||||
geometry.vertices.push(new THREE.Vector3(0.0, -1000000.0, 0.0));
|
||||
geometry.vertices.push(new THREE.Vector3(0.0, 0.0, 0.0));
|
||||
|
||||
this.line = new THREE.Line(geometry, material);
|
||||
this.add(this.line);
|
||||
|
||||
this.numberOfPoints = 20;
|
||||
this.createCircle();
|
||||
this._shining = false;
|
||||
|
||||
this.t = 0;
|
||||
}
|
||||
|
||||
createCircle() {
|
||||
let map = new THREE.TextureLoader().load("assets/eye.png");
|
||||
let material = new THREE.SpriteMaterial({
|
||||
map: map,
|
||||
color: 0xfffffff,
|
||||
transparent: true,
|
||||
});
|
||||
let geometry = new THREE.Geometry();
|
||||
this.circle = new THREE.Sprite(material);
|
||||
this.add(this.circle);
|
||||
}
|
||||
|
||||
length() {
|
||||
return 25 * (Math.cos(this.t) + 5);
|
||||
}
|
||||
|
||||
get shining() {
|
||||
return this._shining;
|
||||
}
|
||||
|
||||
set shining(other) {
|
||||
this._shining = other;
|
||||
if (!other) {
|
||||
this.circle.material.color = new THREE.Color(1, 1, 1);
|
||||
this.line.material.color = new THREE.Color(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
update(camera) {
|
||||
// super.update(camera);
|
||||
|
||||
/// assert(camera instanceof THREE.Camera);
|
||||
let distance = this.localToWorld(this.position.clone()).distanceTo(camera.position);
|
||||
this.t += 0.05;
|
||||
let l = this.length();
|
||||
this.circle.scale.x = l * Math.log(1 + distance);
|
||||
this.circle.scale.y = l * Math.log(1 + distance);
|
||||
this.circle.scale.z = l * Math.log(1 + distance);
|
||||
this.line.geometry.vertices[1].y = -this.circle.scale.x / 2;
|
||||
this.line.geometry.verticesNeedUpdate = true;
|
||||
|
||||
if (this.shining) {
|
||||
let value = (1 + Math.cos(Math.PI / 2 + this.t * 2)) / 2;
|
||||
this.circle.material.color = new THREE.Color(1 - value, value, 0);
|
||||
this.line.material.color = new THREE.Color(1 - value, value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,532 @@
|
||||
/**
|
||||
* Loads a Wavefront .mtl file specifying materials
|
||||
*
|
||||
* @author angelxuanchang
|
||||
*/
|
||||
|
||||
THREE.MTLLoader = function ( manager ) {
|
||||
|
||||
THREE.Loader.call( this, manager );
|
||||
|
||||
};
|
||||
|
||||
THREE.MTLLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), {
|
||||
|
||||
constructor: THREE.MTLLoader,
|
||||
|
||||
/**
|
||||
* Loads and parses a MTL asset from a URL.
|
||||
*
|
||||
* @param {String} url - URL to the MTL file.
|
||||
* @param {Function} [onLoad] - Callback invoked with the loaded object.
|
||||
* @param {Function} [onProgress] - Callback for download progress.
|
||||
* @param {Function} [onError] - Callback for download errors.
|
||||
*
|
||||
* @see setPath setResourcePath
|
||||
*
|
||||
* @note In order for relative texture references to resolve correctly
|
||||
* you must call setResourcePath() explicitly prior to load.
|
||||
*/
|
||||
load: function ( url, onLoad, onProgress, onError ) {
|
||||
|
||||
var scope = this;
|
||||
|
||||
var path = ( this.path === '' ) ? THREE.LoaderUtils.extractUrlBase( url ) : this.path;
|
||||
|
||||
var loader = new THREE.FileLoader( this.manager );
|
||||
loader.setPath( this.path );
|
||||
loader.load( url, function ( text ) {
|
||||
|
||||
onLoad( scope.parse( text, path ) );
|
||||
|
||||
}, onProgress, onError );
|
||||
|
||||
},
|
||||
|
||||
setMaterialOptions: function ( value ) {
|
||||
|
||||
this.materialOptions = value;
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses a MTL file.
|
||||
*
|
||||
* @param {String} text - Content of MTL file
|
||||
* @return {THREE.MTLLoader.MaterialCreator}
|
||||
*
|
||||
* @see setPath setResourcePath
|
||||
*
|
||||
* @note In order for relative texture references to resolve correctly
|
||||
* you must call setResourcePath() explicitly prior to parse.
|
||||
*/
|
||||
parse: function ( text, path ) {
|
||||
|
||||
var lines = text.split( '\n' );
|
||||
var info = {};
|
||||
var delimiter_pattern = /\s+/;
|
||||
var materialsInfo = {};
|
||||
|
||||
for ( var i = 0; i < lines.length; i ++ ) {
|
||||
|
||||
var line = lines[ i ];
|
||||
line = line.trim();
|
||||
|
||||
if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
|
||||
|
||||
// Blank line or comment ignore
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
var pos = line.indexOf( ' ' );
|
||||
|
||||
var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line;
|
||||
key = key.toLowerCase();
|
||||
|
||||
var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : '';
|
||||
value = value.trim();
|
||||
|
||||
if ( key === 'newmtl' ) {
|
||||
|
||||
// New material
|
||||
|
||||
info = { name: value };
|
||||
materialsInfo[ value ] = info;
|
||||
|
||||
} else {
|
||||
|
||||
if ( key === 'ka' || key === 'kd' || key === 'ks' || key === 'ke' ) {
|
||||
|
||||
var ss = value.split( delimiter_pattern, 3 );
|
||||
info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ];
|
||||
|
||||
} else {
|
||||
|
||||
info[ key ] = value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var materialCreator = new THREE.MTLLoader.MaterialCreator( this.resourcePath || path, this.materialOptions );
|
||||
materialCreator.setCrossOrigin( this.crossOrigin );
|
||||
materialCreator.setManager( this.manager );
|
||||
materialCreator.setMaterials( materialsInfo );
|
||||
return materialCreator;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
/**
|
||||
* Create a new THREE.MTLLoader.MaterialCreator
|
||||
* @param baseUrl - Url relative to which textures are loaded
|
||||
* @param options - Set of options on how to construct the materials
|
||||
* side: Which side to apply the material
|
||||
* THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide
|
||||
* wrap: What type of wrapping to apply for textures
|
||||
* THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
|
||||
* normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
|
||||
* Default: false, assumed to be already normalized
|
||||
* ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
|
||||
* Default: false
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
THREE.MTLLoader.MaterialCreator = function ( baseUrl, options ) {
|
||||
|
||||
this.baseUrl = baseUrl || '';
|
||||
this.options = options;
|
||||
this.materialsInfo = {};
|
||||
this.materials = {};
|
||||
this.materialsArray = [];
|
||||
this.nameLookup = {};
|
||||
|
||||
this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide;
|
||||
this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping;
|
||||
|
||||
};
|
||||
|
||||
THREE.MTLLoader.MaterialCreator.prototype = {
|
||||
|
||||
constructor: THREE.MTLLoader.MaterialCreator,
|
||||
|
||||
crossOrigin: 'anonymous',
|
||||
|
||||
setCrossOrigin: function ( value ) {
|
||||
|
||||
this.crossOrigin = value;
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
setManager: function ( value ) {
|
||||
|
||||
this.manager = value;
|
||||
|
||||
},
|
||||
|
||||
setMaterials: function ( materialsInfo ) {
|
||||
|
||||
this.materialsInfo = this.convert( materialsInfo );
|
||||
this.materials = {};
|
||||
this.materialsArray = [];
|
||||
this.nameLookup = {};
|
||||
|
||||
},
|
||||
|
||||
convert: function ( materialsInfo ) {
|
||||
|
||||
if ( ! this.options ) return materialsInfo;
|
||||
|
||||
var converted = {};
|
||||
|
||||
for ( var mn in materialsInfo ) {
|
||||
|
||||
// Convert materials info into normalized form based on options
|
||||
|
||||
var mat = materialsInfo[ mn ];
|
||||
|
||||
var covmat = {};
|
||||
|
||||
converted[ mn ] = covmat;
|
||||
|
||||
for ( var prop in mat ) {
|
||||
|
||||
var save = true;
|
||||
var value = mat[ prop ];
|
||||
var lprop = prop.toLowerCase();
|
||||
|
||||
switch ( lprop ) {
|
||||
|
||||
case 'kd':
|
||||
case 'ka':
|
||||
case 'ks':
|
||||
|
||||
// Diffuse color (color under white light) using RGB values
|
||||
|
||||
if ( this.options && this.options.normalizeRGB ) {
|
||||
|
||||
value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ];
|
||||
|
||||
}
|
||||
|
||||
if ( this.options && this.options.ignoreZeroRGBs ) {
|
||||
|
||||
if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) {
|
||||
|
||||
// ignore
|
||||
|
||||
save = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if ( save ) {
|
||||
|
||||
covmat[ lprop ] = value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return converted;
|
||||
|
||||
},
|
||||
|
||||
preload: function () {
|
||||
|
||||
for ( var mn in this.materialsInfo ) {
|
||||
|
||||
this.create( mn );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
getIndex: function ( materialName ) {
|
||||
|
||||
return this.nameLookup[ materialName ];
|
||||
|
||||
},
|
||||
|
||||
getAsArray: function () {
|
||||
|
||||
var index = 0;
|
||||
|
||||
for ( var mn in this.materialsInfo ) {
|
||||
|
||||
this.materialsArray[ index ] = this.create( mn );
|
||||
this.nameLookup[ mn ] = index;
|
||||
index ++;
|
||||
|
||||
}
|
||||
|
||||
return this.materialsArray;
|
||||
|
||||
},
|
||||
|
||||
create: function ( materialName ) {
|
||||
|
||||
if ( this.materials[ materialName ] === undefined ) {
|
||||
|
||||
this.createMaterial_( materialName );
|
||||
|
||||
}
|
||||
|
||||
return this.materials[ materialName ];
|
||||
|
||||
},
|
||||
|
||||
createMaterial_: function ( materialName ) {
|
||||
|
||||
// Create material
|
||||
|
||||
var scope = this;
|
||||
var mat = this.materialsInfo[ materialName ];
|
||||
var params = {
|
||||
|
||||
name: materialName,
|
||||
side: this.side
|
||||
|
||||
};
|
||||
|
||||
function resolveURL( baseUrl, url ) {
|
||||
|
||||
if ( typeof url !== 'string' || url === '' )
|
||||
return '';
|
||||
|
||||
// Absolute URL
|
||||
if ( /^https?:\/\//i.test( url ) ) return url;
|
||||
|
||||
return baseUrl + url;
|
||||
|
||||
}
|
||||
|
||||
function setMapForType( mapType, value ) {
|
||||
|
||||
if ( params[ mapType ] ) return; // Keep the first encountered texture
|
||||
|
||||
var texParams = scope.getTextureParams( value, params );
|
||||
var map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) );
|
||||
|
||||
map.repeat.copy( texParams.scale );
|
||||
map.offset.copy( texParams.offset );
|
||||
|
||||
map.wrapS = scope.wrap;
|
||||
map.wrapT = scope.wrap;
|
||||
|
||||
params[ mapType ] = map;
|
||||
|
||||
}
|
||||
|
||||
for ( var prop in mat ) {
|
||||
|
||||
var value = mat[ prop ];
|
||||
var n;
|
||||
|
||||
if ( value === '' ) continue;
|
||||
|
||||
switch ( prop.toLowerCase() ) {
|
||||
|
||||
// Ns is material specular exponent
|
||||
|
||||
case 'kd':
|
||||
|
||||
// Diffuse color (color under white light) using RGB values
|
||||
|
||||
params.color = new THREE.Color().fromArray( value );
|
||||
|
||||
break;
|
||||
|
||||
case 'ks':
|
||||
|
||||
// Specular color (color when light is reflected from shiny surface) using RGB values
|
||||
params.specular = new THREE.Color().fromArray( value );
|
||||
|
||||
break;
|
||||
|
||||
case 'ke':
|
||||
|
||||
// Emissive using RGB values
|
||||
params.emissive = new THREE.Color().fromArray( value );
|
||||
|
||||
break;
|
||||
|
||||
case 'map_kd':
|
||||
|
||||
// Diffuse texture map
|
||||
|
||||
setMapForType( "map", value );
|
||||
|
||||
break;
|
||||
|
||||
case 'map_ks':
|
||||
|
||||
// Specular map
|
||||
|
||||
setMapForType( "specularMap", value );
|
||||
|
||||
break;
|
||||
|
||||
case 'map_ke':
|
||||
|
||||
// Emissive map
|
||||
|
||||
setMapForType( "emissiveMap", value );
|
||||
|
||||
break;
|
||||
|
||||
case 'norm':
|
||||
|
||||
setMapForType( "normalMap", value );
|
||||
|
||||
break;
|
||||
|
||||
case 'map_bump':
|
||||
case 'bump':
|
||||
|
||||
// Bump texture map
|
||||
|
||||
setMapForType( "bumpMap", value );
|
||||
|
||||
break;
|
||||
|
||||
case 'map_d':
|
||||
|
||||
// Alpha map
|
||||
|
||||
setMapForType( "alphaMap", value );
|
||||
params.transparent = true;
|
||||
|
||||
break;
|
||||
|
||||
case 'ns':
|
||||
|
||||
// The specular exponent (defines the focus of the specular highlight)
|
||||
// A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
|
||||
|
||||
params.shininess = parseFloat( value );
|
||||
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
n = parseFloat( value );
|
||||
|
||||
if ( n < 1 ) {
|
||||
|
||||
params.opacity = n;
|
||||
params.transparent = true;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'tr':
|
||||
n = parseFloat( value );
|
||||
|
||||
if ( this.options && this.options.invertTrProperty ) n = 1 - n;
|
||||
|
||||
if ( n > 0 ) {
|
||||
|
||||
params.opacity = 1 - n;
|
||||
params.transparent = true;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.materials[ materialName ] = new THREE.MeshPhongMaterial( params );
|
||||
return this.materials[ materialName ];
|
||||
|
||||
},
|
||||
|
||||
getTextureParams: function ( value, matParams ) {
|
||||
|
||||
var texParams = {
|
||||
|
||||
scale: new THREE.Vector2( 1, 1 ),
|
||||
offset: new THREE.Vector2( 0, 0 )
|
||||
|
||||
};
|
||||
|
||||
var items = value.split( /\s+/ );
|
||||
var pos;
|
||||
|
||||
pos = items.indexOf( '-bm' );
|
||||
|
||||
if ( pos >= 0 ) {
|
||||
|
||||
matParams.bumpScale = parseFloat( items[ pos + 1 ] );
|
||||
items.splice( pos, 2 );
|
||||
|
||||
}
|
||||
|
||||
pos = items.indexOf( '-s' );
|
||||
|
||||
if ( pos >= 0 ) {
|
||||
|
||||
texParams.scale.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
|
||||
items.splice( pos, 4 ); // we expect 3 parameters here!
|
||||
|
||||
}
|
||||
|
||||
pos = items.indexOf( '-o' );
|
||||
|
||||
if ( pos >= 0 ) {
|
||||
|
||||
texParams.offset.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
|
||||
items.splice( pos, 4 ); // we expect 3 parameters here!
|
||||
|
||||
}
|
||||
|
||||
texParams.url = items.join( ' ' ).trim();
|
||||
return texParams;
|
||||
|
||||
},
|
||||
|
||||
loadTexture: function ( url, mapping, onLoad, onProgress, onError ) {
|
||||
|
||||
var texture;
|
||||
var manager = ( this.manager !== undefined ) ? this.manager : THREE.DefaultLoadingManager;
|
||||
var loader = manager.getHandler( url );
|
||||
|
||||
if ( loader === null ) {
|
||||
|
||||
loader = new THREE.TextureLoader( manager );
|
||||
|
||||
}
|
||||
|
||||
if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin );
|
||||
texture = loader.load( url, onLoad, onProgress, onError );
|
||||
|
||||
if ( mapping !== undefined ) texture.mapping = mapping;
|
||||
|
||||
return texture;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -0,0 +1,797 @@
|
||||
/**
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
*/
|
||||
|
||||
THREE.OBJLoader = ( function () {
|
||||
|
||||
// o object_name | g group_name
|
||||
var object_pattern = /^[og]\s*(.+)?/;
|
||||
// mtllib file_reference
|
||||
var material_library_pattern = /^mtllib /;
|
||||
// usemtl material_name
|
||||
var material_use_pattern = /^usemtl /;
|
||||
// usemap map_name
|
||||
var map_use_pattern = /^usemap /;
|
||||
|
||||
function ParserState() {
|
||||
|
||||
var state = {
|
||||
objects: [],
|
||||
object: {},
|
||||
|
||||
vertices: [],
|
||||
normals: [],
|
||||
colors: [],
|
||||
uvs: [],
|
||||
|
||||
materialLibraries: [],
|
||||
|
||||
startObject: function ( name, fromDeclaration ) {
|
||||
|
||||
// If the current object (initial from reset) is not from a g/o declaration in the parsed
|
||||
// file. We need to use it for the first parsed g/o to keep things in sync.
|
||||
if ( this.object && this.object.fromDeclaration === false ) {
|
||||
|
||||
this.object.name = name;
|
||||
this.object.fromDeclaration = ( fromDeclaration !== false );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
var previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined );
|
||||
|
||||
if ( this.object && typeof this.object._finalize === 'function' ) {
|
||||
|
||||
this.object._finalize( true );
|
||||
|
||||
}
|
||||
|
||||
this.object = {
|
||||
name: name || '',
|
||||
fromDeclaration: ( fromDeclaration !== false ),
|
||||
|
||||
geometry: {
|
||||
vertices: [],
|
||||
normals: [],
|
||||
colors: [],
|
||||
uvs: []
|
||||
},
|
||||
materials: [],
|
||||
smooth: true,
|
||||
|
||||
startMaterial: function ( name, libraries ) {
|
||||
|
||||
var previous = this._finalize( false );
|
||||
|
||||
// New usemtl declaration overwrites an inherited material, except if faces were declared
|
||||
// after the material, then it must be preserved for proper MultiMaterial continuation.
|
||||
if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) {
|
||||
|
||||
this.materials.splice( previous.index, 1 );
|
||||
|
||||
}
|
||||
|
||||
var material = {
|
||||
index: this.materials.length,
|
||||
name: name || '',
|
||||
mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ),
|
||||
smooth: ( previous !== undefined ? previous.smooth : this.smooth ),
|
||||
groupStart: ( previous !== undefined ? previous.groupEnd : 0 ),
|
||||
groupEnd: - 1,
|
||||
groupCount: - 1,
|
||||
inherited: false,
|
||||
|
||||
clone: function ( index ) {
|
||||
|
||||
var cloned = {
|
||||
index: ( typeof index === 'number' ? index : this.index ),
|
||||
name: this.name,
|
||||
mtllib: this.mtllib,
|
||||
smooth: this.smooth,
|
||||
groupStart: 0,
|
||||
groupEnd: - 1,
|
||||
groupCount: - 1,
|
||||
inherited: false
|
||||
};
|
||||
cloned.clone = this.clone.bind( cloned );
|
||||
return cloned;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
this.materials.push( material );
|
||||
|
||||
return material;
|
||||
|
||||
},
|
||||
|
||||
currentMaterial: function () {
|
||||
|
||||
if ( this.materials.length > 0 ) {
|
||||
|
||||
return this.materials[ this.materials.length - 1 ];
|
||||
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
},
|
||||
|
||||
_finalize: function ( end ) {
|
||||
|
||||
var lastMultiMaterial = this.currentMaterial();
|
||||
if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) {
|
||||
|
||||
lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
|
||||
lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
|
||||
lastMultiMaterial.inherited = false;
|
||||
|
||||
}
|
||||
|
||||
// Ignore objects tail materials if no face declarations followed them before a new o/g started.
|
||||
if ( end && this.materials.length > 1 ) {
|
||||
|
||||
for ( var mi = this.materials.length - 1; mi >= 0; mi -- ) {
|
||||
|
||||
if ( this.materials[ mi ].groupCount <= 0 ) {
|
||||
|
||||
this.materials.splice( mi, 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Guarantee at least one empty material, this makes the creation later more straight forward.
|
||||
if ( end && this.materials.length === 0 ) {
|
||||
|
||||
this.materials.push( {
|
||||
name: '',
|
||||
smooth: this.smooth
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
return lastMultiMaterial;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// Inherit previous objects material.
|
||||
// Spec tells us that a declared material must be set to all objects until a new material is declared.
|
||||
// If a usemtl declaration is encountered while this new object is being parsed, it will
|
||||
// overwrite the inherited material. Exception being that there was already face declarations
|
||||
// to the inherited material, then it will be preserved for proper MultiMaterial continuation.
|
||||
|
||||
if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) {
|
||||
|
||||
var declared = previousMaterial.clone( 0 );
|
||||
declared.inherited = true;
|
||||
this.object.materials.push( declared );
|
||||
|
||||
}
|
||||
|
||||
this.objects.push( this.object );
|
||||
|
||||
},
|
||||
|
||||
finalize: function () {
|
||||
|
||||
if ( this.object && typeof this.object._finalize === 'function' ) {
|
||||
|
||||
this.object._finalize( true );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
parseVertexIndex: function ( value, len ) {
|
||||
|
||||
var index = parseInt( value, 10 );
|
||||
return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
|
||||
|
||||
},
|
||||
|
||||
parseNormalIndex: function ( value, len ) {
|
||||
|
||||
var index = parseInt( value, 10 );
|
||||
return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
|
||||
|
||||
},
|
||||
|
||||
parseUVIndex: function ( value, len ) {
|
||||
|
||||
var index = parseInt( value, 10 );
|
||||
return ( index >= 0 ? index - 1 : index + len / 2 ) * 2;
|
||||
|
||||
},
|
||||
|
||||
addVertex: function ( a, b, c ) {
|
||||
|
||||
var src = this.vertices;
|
||||
var dst = this.object.geometry.vertices;
|
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
|
||||
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
|
||||
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
|
||||
|
||||
},
|
||||
|
||||
addVertexPoint: function ( a ) {
|
||||
|
||||
var src = this.vertices;
|
||||
var dst = this.object.geometry.vertices;
|
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
|
||||