Initial commit

This commit is contained in:
Thomas Forgione 2020-01-08 22:02:48 +01:00
commit 40db946939
43 changed files with 88131 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
assets

79
README.md Normal file
View File

@ -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`.
![](https://gitea.tforgione.fr/tforgione/tp-interaction/src/branch/master/assets/scheme.svg)
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))` |
| ... | ... |

BIN
assets/eye.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

66
assets/scheme.svg Normal file
View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="360" height="360" version="1.1">
<g id="Coord_LatLong" transform="translate(159.5, 195.5) scale(1,-1)">
<path d="M -143,0 A 143,62 0 1 0 143,0" style="stroke:#808080;stroke-width:1;stroke-dasharray:6,6;fill:none;"/>
<path d="M 0,143 A 143,60 -90 1 1 0,-143" transform="rotate(13)" style="stroke:#808080;stroke-dasharray:6,6;stroke-width:1;fill:none;"/>
<path d="M -125,0 H 186" transform="rotate(-135)" style="stroke:#000000;stroke-width:1;fill:none;"/>
<path d="M 0,-2.5 L 4,-3 L 2,4 L 0,12 L -2,4 L -4,-3 L 0,-2.5 Z" transform="rotate(-225) translate(0,186)" style="stroke:none;fill:#000000;"/>
<path d="M -155,0 H 188" transform="rotate(-10.6)" style="stroke:#000000;stroke-width:1;fill:none;"/>
<path d="M 0,-2.5 L 4,-3 L 2,4 L 0,12 L -2,4 L -4,-3 L 0,-2.5 Z" transform="rotate(-10.6) translate(188,0) rotate(270)" style="stroke:none;fill:#000000;"/>
<path d="M 0,-163 V 177" style="stroke:#000000;stroke-width:1;fill:none;"/>
<path d="M 0,-2.5 L 4,-3 L 2,4 L 0,12 L -2,4 L -4,-3 L 0,-2.5 Z" transform="translate(0,177)" style="stroke:none;fill:#000000;"/>
<circle cx="0" cy="0" r="143" style="stroke:#000000;stroke-width:2;fill:none;"/>
<path d="M 0,-143 A 143,60 -90 1 1 0,143" transform="rotate(13)" style="stroke:#000000;stroke-width:1;fill:none;"/>
<path d="M -143,0 A 143,62 0 1 1 143,0" style="stroke:#000000;stroke-width:1;fill:none;"/>
<path d="M 0,0 L -56.88,-56.88 A 143,62 0 0 1 66.73,-54.84 L 0,0 Z" style="stroke:none;fill:#204a87;opacity:0.2;"/>
<path d="M 0,0 L 52.68,-68.44 A 143,60 -90 0 1 53.71,63.72 L 0,0 Z" transform="rotate(13)" style="stroke:none;fill:#f57900;opacity:0.3;"/>
<path d="M 0,0 L 38,74.17" style="stroke:#4e9a06;stroke-width:2;fill:none;"/>
<path d="M 0,0 L -10,3 L -10,-3 L 0,0 Z" transform="translate(38, 74.17) rotate(63)" style="stroke:#4e9a06;stroke-width:2;fill:#4e9a06;"/>
<path d="M 52.68,-68.44 A 143,60 -90 0 1 53.71,63.72" transform="rotate(13)" style="stroke:#f57900;stroke-width:2;fill:none;"/>
<path d="M 0,0 L -10,3 L -10,-3 L 0,0 Z" transform="translate(38, 74.17) rotate(112)" style="stroke:#f57900;stroke-width:2;fill:#f57900;"/>
<path d="M -56.88,-56.88 A 143,62 0 0 1 62,-55.87" style="stroke:#204a87;stroke-width:2;fill:none;"/>
<path d="M 0,0 L -10,3 L -10,-3 L 0,0 Z" transform="translate(66.73,-54.84) rotate(11)" style="stroke:#204a87;stroke-width:2;fill:#204a87;"/>
<circle cx="38" cy="74.17" r="4" style="stroke:none;fill:#5c3566;"/>
<circle cx="0" cy="0" r="2" style="stroke:none;fill:#000000;"/>
<text transform="scale(1,-1)" x="6" y="-44" xml:space="preserve" style="font-size:28px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke:none;font-family:DejaVu Serif;-inkscape-font-specification:DejaVu Serif Italic"><tspan x="6" y="-44">ρ</tspan>
</text>
<text transform="scale(1,-1)" x="64" y="-10" xml:space="preserve" style="font-size:28px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke:none;font-family:DejaVu Serif;-inkscape-font-specification:DejaVu Serif Italic"><tspan x="64" y="-10">δ</tspan>
</text>
<text transform="scale(1,-1)" x="-22" y="90" xml:space="preserve" style="font-size:28px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke:none;font-family:DejaVu Serif;-inkscape-font-specification:DejaVu Serif Italic"><tspan x="-22" y="90">θ</tspan>
</text>
<text transform="scale(1,-1)" x="45" y="-78" xml:space="preserve" style="font-size:28px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke:none;font-family:DejaVu Serif;-inkscape-font-specification:DejaVu Serif Italic"><tspan x="45" y="-78">P</tspan>
</text>
<g transform="translate(-127,-154) scale(1,-1)">
<text x="0" y="0" xml:space="preserve" style="font-size:32px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke:none;font-family:DejaVu Serif;-inkscape-font-specification:DejaVu Serif Italic"><tspan x="0" y="0">z</tspan>
</text>
</g>
<g transform="translate(167,-65) scale(1,-1)">
<text x="0" y="0" xml:space="preserve" style="font-size:32px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke:none;font-family:DejaVu Serif;-inkscape-font-specification:DejaVu Serif Italic"><tspan x="0" y="0">x</tspan>
</text>
</g>
<g transform="translate(10,170) scale(1,-1)">
<text x="0" y="0" xml:space="preserve" style="font-size:32px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;stroke:none;font-family:DejaVu Serif;-inkscape-font-specification:DejaVu Serif Italic"><tspan x="0" y="0">y</tspan>
</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -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

84882
assets/windfall/Windfall.obj Normal file

File diff suppressed because it is too large Load Diff

BIN
assets/windfall/a_iwa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
assets/windfall/a_kusa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
assets/windfall/a_tuta.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
assets/windfall/m_ki12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
assets/windfall/m_ki13.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
assets/windfall/m_ren15.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
assets/windfall/m_ren16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
assets/windfall/water.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
assets/windfall/wood01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
assets/windfall/wood04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
assets/windfall/wood06.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

20
index.html Normal file
View File

@ -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>

83
js/Bookmark.js Normal file
View File

@ -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);
}
}
}

532
js/MTLLoader.js Normal file
View File

@ -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;
}
};

797
js/OBJLoader.js Normal file
View File

@ -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 ] );
},
addVertexLine: function ( a ) {
var src = this.vertices;
var dst = this.object.geometry.vertices;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
},
addNormal: function ( a, b, c ) {
var src = this.normals;
var dst = this.object.geometry.normals;
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 ] );
},
addColor: function ( a, b, c ) {
var src = this.colors;
var dst = this.object.geometry.colors;
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 ] );
},
addUV: function ( a, b, c ) {
var src = this.uvs;
var dst = this.object.geometry.uvs;
dst.push( src[ a + 0 ], src[ a + 1 ] );
dst.push( src[ b + 0 ], src[ b + 1 ] );
dst.push( src[ c + 0 ], src[ c + 1 ] );
},
addUVLine: function ( a ) {
var src = this.uvs;
var dst = this.object.geometry.uvs;
dst.push( src[ a + 0 ], src[ a + 1 ] );
},
addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) {
var vLen = this.vertices.length;
var ia = this.parseVertexIndex( a, vLen );
var ib = this.parseVertexIndex( b, vLen );
var ic = this.parseVertexIndex( c, vLen );
this.addVertex( ia, ib, ic );
if ( this.colors.length > 0 ) {
this.addColor( ia, ib, ic );
}
if ( ua !== undefined && ua !== '' ) {
var uvLen = this.uvs.length;
ia = this.parseUVIndex( ua, uvLen );
ib = this.parseUVIndex( ub, uvLen );
ic = this.parseUVIndex( uc, uvLen );
this.addUV( ia, ib, ic );
}
if ( na !== undefined && na !== '' ) {
// Normals are many times the same. If so, skip function call and parseInt.
var nLen = this.normals.length;
ia = this.parseNormalIndex( na, nLen );
ib = na === nb ? ia : this.parseNormalIndex( nb, nLen );
ic = na === nc ? ia : this.parseNormalIndex( nc, nLen );
this.addNormal( ia, ib, ic );
}
},
addPointGeometry: function ( vertices ) {
this.object.geometry.type = 'Points';
var vLen = this.vertices.length;
for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
this.addVertexPoint( this.parseVertexIndex( vertices[ vi ], vLen ) );
}
},
addLineGeometry: function ( vertices, uvs ) {
this.object.geometry.type = 'Line';
var vLen = this.vertices.length;
var uvLen = this.uvs.length;
for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) );
}
for ( var uvi = 0, l = uvs.length; uvi < l; uvi ++ ) {
this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) );
}
}
};
state.startObject( '', false );
return state;
}
//
function OBJLoader( manager ) {
THREE.Loader.call( this, manager );
this.materials = null;
}
OBJLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), {
constructor: OBJLoader,
load: function ( url, onLoad, onProgress, onError ) {
var scope = this;
var loader = new THREE.FileLoader( scope.manager );
loader.setPath( this.path );
loader.load( url, function ( text ) {
onLoad( scope.parse( text ) );
}, onProgress, onError );
},
setMaterials: function ( materials ) {
this.materials = materials;
return this;
},
parse: function ( text ) {
console.time( 'OBJLoader' );
var state = new ParserState();
if ( text.indexOf( '\r\n' ) !== - 1 ) {
// This is faster than String.split with regex that splits on both
text = text.replace( /\r\n/g, '\n' );
}
if ( text.indexOf( '\\\n' ) !== - 1 ) {
// join lines separated by a line continuation character (\)
text = text.replace( /\\\n/g, '' );
}
var lines = text.split( '\n' );
var line = '', lineFirstChar = '';
var lineLength = 0;
var result = [];
// Faster to just trim left side of the line. Use if available.
var trimLeft = ( typeof ''.trimLeft === 'function' );
for ( var i = 0, l = lines.length; i < l; i ++ ) {
line = lines[ i ];
line = trimLeft ? line.trimLeft() : line.trim();
lineLength = line.length;
if ( lineLength === 0 ) continue;
lineFirstChar = line.charAt( 0 );
// @todo invoke passed in handler if any
if ( lineFirstChar === '#' ) continue;
if ( lineFirstChar === 'v' ) {
var data = line.split( /\s+/ );
switch ( data[ 0 ] ) {
case 'v':
state.vertices.push(
parseFloat( data[ 1 ] ),
parseFloat( data[ 2 ] ),
parseFloat( data[ 3 ] )
);
if ( data.length >= 7 ) {
state.colors.push(
parseFloat( data[ 4 ] ),
parseFloat( data[ 5 ] ),
parseFloat( data[ 6 ] )
);
}
break;
case 'vn':
state.normals.push(
parseFloat( data[ 1 ] ),
parseFloat( data[ 2 ] ),
parseFloat( data[ 3 ] )
);
break;
case 'vt':
state.uvs.push(
parseFloat( data[ 1 ] ),
parseFloat( data[ 2 ] )
);
break;
}
} else if ( lineFirstChar === 'f' ) {
var lineData = line.substr( 1 ).trim();
var vertexData = lineData.split( /\s+/ );
var faceVertices = [];
// Parse the face vertex data into an easy to work with format
for ( var j = 0, jl = vertexData.length; j < jl; j ++ ) {
var vertex = vertexData[ j ];
if ( vertex.length > 0 ) {
var vertexParts = vertex.split( '/' );
faceVertices.push( vertexParts );
}
}
// Draw an edge between the first vertex and all subsequent vertices to form an n-gon
var v1 = faceVertices[ 0 ];
for ( var j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) {
var v2 = faceVertices[ j ];
var v3 = faceVertices[ j + 1 ];
state.addFace(
v1[ 0 ], v2[ 0 ], v3[ 0 ],
v1[ 1 ], v2[ 1 ], v3[ 1 ],
v1[ 2 ], v2[ 2 ], v3[ 2 ]
);
}
} else if ( lineFirstChar === 'l' ) {
var lineParts = line.substring( 1 ).trim().split( " " );
var lineVertices = [], lineUVs = [];
if ( line.indexOf( "/" ) === - 1 ) {
lineVertices = lineParts;
} else {
for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) {
var parts = lineParts[ li ].split( "/" );
if ( parts[ 0 ] !== "" ) lineVertices.push( parts[ 0 ] );
if ( parts[ 1 ] !== "" ) lineUVs.push( parts[ 1 ] );
}
}
state.addLineGeometry( lineVertices, lineUVs );
} else if ( lineFirstChar === 'p' ) {
var lineData = line.substr( 1 ).trim();
var pointData = lineData.split( " " );
state.addPointGeometry( pointData );
} else if ( ( result = object_pattern.exec( line ) ) !== null ) {
// o object_name
// or
// g group_name
// WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
// var name = result[ 0 ].substr( 1 ).trim();
var name = ( " " + result[ 0 ].substr( 1 ).trim() ).substr( 1 );
state.startObject( name );
} else if ( material_use_pattern.test( line ) ) {
// material
state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
} else if ( material_library_pattern.test( line ) ) {
// mtl file
state.materialLibraries.push( line.substring( 7 ).trim() );
} else if ( map_use_pattern.test( line ) ) {
// the line is parsed but ignored since the loader assumes textures are defined MTL files
// (according to https://www.okino.com/conv/imp_wave.htm, 'usemap' is the old-style Wavefront texture reference method)
console.warn( 'THREE.OBJLoader: Rendering identifier "usemap" not supported. Textures must be defined in MTL files.' );
} else if ( lineFirstChar === 's' ) {
result = line.split( ' ' );
// smooth shading
// @todo Handle files that have varying smooth values for a set of faces inside one geometry,
// but does not define a usemtl for each face set.
// This should be detected and a dummy material created (later MultiMaterial and geometry groups).
// This requires some care to not create extra material on each smooth value for "normal" obj files.
// where explicit usemtl defines geometry groups.
// Example asset: examples/models/obj/cerberus/Cerberus.obj
/*
* http://paulbourke.net/dataformats/obj/
* or
* http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
*
* From chapter "Grouping" Syntax explanation "s group_number":
* "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
* Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
* surfaces, smoothing groups are either turned on or off; there is no difference between values greater
* than 0."
*/
if ( result.length > 1 ) {
var value = result[ 1 ].trim().toLowerCase();
state.object.smooth = ( value !== '0' && value !== 'off' );
} else {
// ZBrush can produce "s" lines #11707
state.object.smooth = true;
}
var material = state.object.currentMaterial();
if ( material ) material.smooth = state.object.smooth;
} else {
// Handle null terminated files without exception
if ( line === '\0' ) continue;
throw new Error( 'THREE.OBJLoader: Unexpected line: "' + line + '"' );
}
}
state.finalize();
var container = new THREE.Group();
container.materialLibraries = [].concat( state.materialLibraries );
for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
var object = state.objects[ i ];
var geometry = object.geometry;
var materials = object.materials;
var isLine = ( geometry.type === 'Line' );
var isPoints = ( geometry.type === 'Points' );
var hasVertexColors = false;
// Skip o/g line declarations that did not follow with any faces
if ( geometry.vertices.length === 0 ) continue;
var buffergeometry = new THREE.BufferGeometry();
buffergeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( geometry.vertices, 3 ) );
if ( geometry.normals.length > 0 ) {
buffergeometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( geometry.normals, 3 ) );
} else {
buffergeometry.computeVertexNormals();
}
if ( geometry.colors.length > 0 ) {
hasVertexColors = true;
buffergeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( geometry.colors, 3 ) );
}
if ( geometry.uvs.length > 0 ) {
buffergeometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( geometry.uvs, 2 ) );
}
// Create materials
var createdMaterials = [];
for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
var sourceMaterial = materials[ mi ];
var material = undefined;
if ( this.materials !== null ) {
material = this.materials.create( sourceMaterial.name );
// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
var materialLine = new THREE.LineBasicMaterial();
THREE.Material.prototype.copy.call( materialLine, material );
materialLine.color.copy( material.color );
material = materialLine;
} else if ( isPoints && material && ! ( material instanceof THREE.PointsMaterial ) ) {
var materialPoints = new THREE.PointsMaterial( { size: 10, sizeAttenuation: false } );
THREE.Material.prototype.copy.call( materialPoints, material );
materialPoints.color.copy( material.color );
materialPoints.map = material.map;
material = materialPoints;
}
}
if ( ! material ) {
if ( isLine ) {
material = new THREE.LineBasicMaterial();
} else if ( isPoints ) {
material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
} else {
material = new THREE.MeshPhongMaterial();
}
material.name = sourceMaterial.name;
}
material.flatShading = sourceMaterial.smooth ? false : true;
material.vertexColors = hasVertexColors ? THREE.VertexColors : THREE.NoColors;
createdMaterials.push( material );
}
// Create mesh
var mesh;
if ( createdMaterials.length > 1 ) {
for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
var sourceMaterial = materials[ mi ];
buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
}
if ( isLine ) {
mesh = new THREE.LineSegments( buffergeometry, createdMaterials );
} else if ( isPoints ) {
mesh = new THREE.Points( buffergeometry, createdMaterials );
} else {
mesh = new THREE.Mesh( buffergeometry, createdMaterials );
}
} else {
if ( isLine ) {
mesh = new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] );
} else if ( isPoints ) {
mesh = new THREE.Points( buffergeometry, createdMaterials[ 0 ] );
} else {
mesh = new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] );
}
}
mesh.name = object.name;
container.add( mesh );
}
console.timeEnd( 'OBJLoader' );
return container;
}
} );
return OBJLoader;
} )();

283
js/main.js Normal file
View File

@ -0,0 +1,283 @@
class Controls {
constructor(object) {
// object is the object controlled by the controls, in our case, the camera.
this.object = object;
// 3D coordinates where the camera is looking at
this.target = new THREE.Vector3(0, 0, 0);
// Unit vectors of the local camera frame
// forward is in the z-direction
// left point towards the x-direction
// (a vector towards y is not necessary)
// in the direction of the angles
this.forward = new THREE.Vector3(0,0,0);
this.left = new THREE.Vector3(0,0,0);
// speed at which the camera will translate
this.speed = 50;
// speed at which the camera will rotate
this.sensitivity = 0.004;
// angles of the camera
this.angles = {
delta: 0,
theta: 0,
};
// the position of the camera is this.object.position
this.vectorsFromAngles();
// currently pressed keys
this.keys = {
up: false,
down: false,
left: false,
right: false,
};
// whether the mouse is being pressed or not
this.mouseClicked = false;
// whether there is a transition running or not
// if not, this.t is NaN
// otherwise, this.t is in [0, 1]
this.t = NaN;
// calls this.keyChange(true) when a key is pressed
document.addEventListener('keydown', this.keyChange(true));
// calls this.keyChange(false) when a key is released
document.addEventListener('keyup', this.keyChange(false));
// calls this.mouseDown(e) when a mouse button is pressed
document.addEventListener('mousedown', (e) => this.mouseDown(e));
// sets this.mouseClicked to false when a mouse button is released
document.addEventListener('mouseup', () => this.mouseClicked = false);
// calls this.mouseMove(e) when the mouse is moved
document.addEventListener('mousemove', (e) => this.mouseMove(e));
}
// rotates the camera to make it look to its target.
look() {
this.object.lookAt(this.target);
}
// updates this.forward, this.left and this.target
// from this.angles.delta and this.angles.theta
// this.forward is the vector moving direction
// this.left is the vector moving sideways to the left
// this.target is the point that the camera is looking
vectorsFromAngles() {
// TODO Part 1
}
// computes this.angles.delta and this.angles.theta from this.forward
// (may be useless)
anglesFromVectors() {
// TODO Part 2
}
// updates this.keys when a key is pressed or released
keyChange(setting) {
return (event) => {
switch (event.keyCode) {
case 37: case 81: this.keys.left = setting; break;
case 38: case 90: this.keys.up = setting; break;
case 39: case 68: this.keys.right = setting; break;
case 40: case 83: this.keys.down = setting; break;
case 13:
console.log("Position:", this.object.position);
console.log("Target:", this.target);
break;
}
};
}
// sets mouseClicked to true, and searches for a bookmark being clicked
mouseDown(event) {
this.mouseClicked = true;
let mouse = {
x: ( event.clientX / window.innerWidth ) * 2 - 1,
y: - ( event.clientY / window.innerHeight ) * 2 + 1,
};
// update the picking ray with the camera and mouse position
raycaster.setFromCamera(mouse, camera);
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects(scene.children, true);
if (this.testIntersection(intersects)) {
this.bookmarkClicked(intersects[0].object.parent);
}
}
// tests that a bookmark is clicked or not
//
// this function filters out other objects of the scene.
testIntersection(intersects) {
if (intersects.length === 0) {
return;
}
if (intersects[0].object === undefined) {
return;
}
if (! (intersects[0].object.parent instanceof Bookmark)) {
return;
}
if (this.object.position.distanceTo(intersects[0].object.parent.position) < 10) {
return;
}
this.bookmarkClicked(intersects[0].object.parent);
}
// initializes the paramters for the transition to a bookmark.
bookmarkClicked(bookmark) {
// TODO Part 2
}
// changes this.angles depending of event.movementX and event.movementY
//
// event.movementX indicates how many pixels the mouse has moved to the right
// event.movementY indicates how many piels the mouse has moved to the bottom
// both those values can be negative to indicate a motion in the opposite direction
mouseMove(event) {
// TODO Part 1
}
// called at every frame
update() {
// No transition : use keyboard and mouse to update the camera
if (isNaN(this.t)) {
// TODO Part 1
if (this.keys.up) {
// ...
}
} else {
// Transition to bookmark
// TODO Part 2
}
// update the camera parameters
this.look();
}
}
let container, controls;
let camera, scene, renderer;
let raycaster = new THREE.Raycaster();
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 100, 50000);
camera.position.y = 1000;
camera.position.z = 1000;
scene = new THREE.Scene();
let ambientLight = new THREE.AmbientLight(0xcccccc, 0.4);
scene.add(ambientLight);
let pointLight = new THREE.PointLight(0xffffff, 0.8);
camera.add(pointLight);
scene.add(camera);
let geometry = new THREE.PlaneGeometry(20000, 20000);
for (let index = 0; index < geometry.faceVertexUvs[0].length; index++) {
geometry.faceVertexUvs[0][index][0].multiplyScalar(10);
geometry.faceVertexUvs[0][index][1].multiplyScalar(10);
geometry.faceVertexUvs[0][index][2].multiplyScalar(10);
}
let texture = new THREE.TextureLoader().load('assets/windfall/water.png');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
let material = new THREE.MeshBasicMaterial({map: texture});
let sea = new THREE.Mesh(geometry, material);
sea.rotation.x = -Math.PI / 2;
scene.add(sea);
// controls
controls = new Controls(camera);
// model
let directory = "assets/windfall/";
let modelName = "Windfall";
let onProgress = function (xhr) {
if (xhr.lengthComputable) {
let percentComplete = xhr.loaded / xhr.total * 100;
console.log(Math.round(percentComplete, 2) + '% downloaded');
}
};
let onError = function () { };
let manager = new THREE.LoadingManager();
new THREE.MTLLoader(manager)
.setPath(directory)
.load(modelName + ".mtl", function (materials) {
materials.preload();
new THREE.OBJLoader(manager)
.setMaterials(materials)
.setPath(directory)
.load(modelName + ".obj", function (object) {
scene.add(object);
}, onProgress, onError);
});
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color(0x87ceeb));
container.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false);
// Add a bookmark to the scene
// let position = { x: -3089.6612461510463, y: 3944.8226721499614, z: -2409.0350261969947 };
// let target = { x: -3088.665295409939, y: 3944.824672148628, z: -2409.124904566436 };
// let bookmark = new Bookmark(position, target);
// scene.add(bookmark);
}
function animate() {
requestAnimationFrame(animate);
controls.update();
for (let child of scene.children) {
if (child instanceof Bookmark) {
child.update(camera);
}
}
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}

1034
js/three.min.js vendored Normal file

File diff suppressed because one or more lines are too long