Julien Chaumond commited on
Commit
9d520b3
1 Parent(s): 430db42

Boloss (.dae)

Browse files

* .dae model import
* programmatic animations using Tween.js
* custom promise-based anim system.

index.html CHANGED
@@ -2,13 +2,14 @@
2
  <html>
3
  <head>
4
  <meta charset=utf-8>
5
- <title>My first three.js app</title>
6
  <style>
7
  body { margin: 0; }
8
  canvas { width: 100%; height: 100% }
9
  </style>
10
  </head>
11
  <body>
 
12
  <script src="node_modules/three/build/three.js"></script>
13
  <script src="node_modules/three/examples/js/libs/stats.min.js"></script>
14
  <script src="node_modules/three/examples/js/controls/OrbitControls.js"></script>
@@ -19,6 +20,7 @@
19
  <script src="node_modules/three/examples/js/loaders/FBXLoader.js"></script>
20
  <script src="node_modules/three/examples/js/loaders/ColladaLoader.js"></script>
21
  <script src="dist/lib/Log.js"></script>
 
22
  <script src="dist/post/index.js"></script>
23
  </body>
24
  </html>
 
2
  <html>
3
  <head>
4
  <meta charset=utf-8>
5
+ <title>Evolution of intelligence at Hugging Face</title>
6
  <style>
7
  body { margin: 0; }
8
  canvas { width: 100%; height: 100% }
9
  </style>
10
  </head>
11
  <body>
12
+ <script src="node_modules/@tweenjs/tween.js/src/Tween.js"></script>
13
  <script src="node_modules/three/build/three.js"></script>
14
  <script src="node_modules/three/examples/js/libs/stats.min.js"></script>
15
  <script src="node_modules/three/examples/js/controls/OrbitControls.js"></script>
 
20
  <script src="node_modules/three/examples/js/loaders/FBXLoader.js"></script>
21
  <script src="node_modules/three/examples/js/loaders/ColladaLoader.js"></script>
22
  <script src="dist/lib/Log.js"></script>
23
+ <script src="dist/lib/Utils.js"></script>
24
  <script src="dist/post/index.js"></script>
25
  </body>
26
  </html>
models/boloss/Boloss-3d v10.dae ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7d13723b5089e59ae3c1a271b63ca7ce0e16e0594b76583db4d26bc4622f775c
3
+ size 43686
package-lock.json CHANGED
@@ -4,6 +4,16 @@
4
  "lockfileVersion": 1,
5
  "requires": true,
6
  "dependencies": {
 
 
 
 
 
 
 
 
 
 
7
  "three": {
8
  "version": "0.103.0",
9
  "resolved": "https://registry.npmjs.org/three/-/three-0.103.0.tgz",
 
4
  "lockfileVersion": 1,
5
  "requires": true,
6
  "dependencies": {
7
+ "@tweenjs/tween.js": {
8
+ "version": "17.3.0",
9
+ "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-17.3.0.tgz",
10
+ "integrity": "sha512-SPkhNj9/wGfbdX2C3B3KhttLQ4iesd+Ny8Dv1RnqF1MFUIqsZz/OJVLzJEHSEl7zheNx70dvqrwfbCFDQ0sWBw=="
11
+ },
12
+ "@types/tween.js": {
13
+ "version": "17.2.0",
14
+ "resolved": "https://registry.npmjs.org/@types/tween.js/-/tween.js-17.2.0.tgz",
15
+ "integrity": "sha512-mOsqurEtFEzwgkVc/jDVE2XrjZBYTbrmDUyCr9GXmnfc6q5otokxFtKvSY/B21zgz9LVRIvRTawKczjKi57wrA=="
16
+ },
17
  "three": {
18
  "version": "0.103.0",
19
  "resolved": "https://registry.npmjs.org/three/-/three-0.103.0.tgz",
package.json CHANGED
@@ -9,6 +9,8 @@
9
  "author": "",
10
  "license": "ISC",
11
  "dependencies": {
 
 
12
  "three": "^0.103.0"
13
  }
14
  }
 
9
  "author": "",
10
  "license": "ISC",
11
  "dependencies": {
12
+ "@tweenjs/tween.js": "^17.3.0",
13
+ "@types/tween.js": "^17.2.0",
14
  "three": "^0.103.0"
15
  }
16
  }
post-compile.sh CHANGED
@@ -1,3 +1,3 @@
1
  #!/bin/bash
2
  echo "post-compile-tweaks"
3
- tail -n +4 dist/index.js > dist/post/index.js
 
1
  #!/bin/bash
2
  echo "post-compile-tweaks"
3
+ tail -n +5 dist/index.js > dist/post/index.js
src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
  import * as THREE from 'three';
 
2
 
3
  const scene = new THREE.Scene();
4
  scene.background = new THREE.Color(
@@ -98,12 +99,35 @@ class Assets {
98
  );
99
  });
100
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  }
102
- class Utils {
103
- static boundingBox(o: THREE.Object3D): [THREE.Vector3, THREE.Vector3] {
104
  const bbox = new THREE.Box3().setFromObject(o);
105
- /// vv Just unpack it for easier console logging.
106
- return [bbox.min, bbox.max];
 
 
 
 
 
 
 
107
  }
108
  }
109
  (async () => {
@@ -114,20 +138,14 @@ class Utils {
114
  scene.add(gridHelper);
115
  const axesHelper = new THREE.AxesHelper(50);
116
  scene.add(axesHelper);
117
-
118
- const cube = new THREE.Mesh(
119
- new THREE.BoxGeometry(1, 1, 1),
120
- new THREE.MeshBasicMaterial({ color: 0x00ff00 })
121
- );
122
- cube.position.x = 7;
123
- scene.add(cube);
124
-
125
 
126
  {
127
  const egg = await Assets.loadEgg();
128
  c.log(egg);
129
  egg.scale.setScalar(.2);
130
  egg.rotateX(-Math.PI / 2);
 
131
  const box = new THREE.BoxHelper(egg);
132
  scene.add(box);
133
  scene.add(egg);
@@ -141,6 +159,7 @@ class Utils {
141
  c.log(egg);
142
  egg.scale.setScalar(100);
143
  egg.position.x = -10;
 
144
  egg.remove(egg.getObjectByName('Camera')!);
145
  scene.add(egg);
146
  // c.log(Utils.boundingBox(egg));
@@ -165,18 +184,101 @@ class Utils {
165
  const box = new THREE.BoxHelper(container, new THREE.Color('green'));
166
  scene.add(box);
167
  }
168
-
169
- // const geometry = new THREE.CylinderBufferGeometry( 0, 10, 30, 4, 1 );
170
- // const material = new THREE.MeshPhongMaterial( { color: 0xffffff, flatShading: true } );
171
- // for ( let i = 0; i < 500; i ++ ) {
172
- // const mesh = new THREE.Mesh( geometry, material );
173
- // mesh.position.x = Math.random() * 1600 - 800;
174
- // mesh.position.y = 0;
175
- // mesh.position.z = Math.random() * 1600 - 800;
176
- // mesh.updateMatrix();
177
- // mesh.matrixAutoUpdate = false;
178
- // scene.add( mesh );
179
- // }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  })();
181
 
182
  /**
@@ -197,6 +299,7 @@ function render() {
197
  }
198
  function animate() {
199
  requestAnimationFrame(animate);
 
200
  render();
201
  stats.update();
202
  }
 
1
  import * as THREE from 'three';
2
+ import * as TWEEN from '@tweenjs/tween.js';
3
 
4
  const scene = new THREE.Scene();
5
  scene.background = new THREE.Color(
 
99
  );
100
  });
101
  }
102
+ static loadBoloss(): Promise<{
103
+ animations: THREE.AnimationClip[];
104
+ scene: THREE.Group;
105
+ }> {
106
+ /// In Dae/Collada: did not manage to get
107
+ /// either the anims or the texture.
108
+ return new Promise((resolve, reject) => {
109
+ const loader: THREE.AnyLoader = new (<any>THREE).ColladaLoader();
110
+ loader.load(
111
+ `models/boloss/Boloss-3d v10.dae`,
112
+ (collada) => {
113
+ resolve(collada);
114
+ }
115
+ );
116
+ });
117
+ }
118
  }
119
+ class TUtils {
120
+ static boundingBox(o: THREE.Object3D): THREE.Box3 {
121
  const bbox = new THREE.Box3().setFromObject(o);
122
+ return bbox;
123
+ }
124
+ static flushYZero(o: THREE.Object3D) {
125
+ o.position.y = -(this.boundingBox(o)).min.y;
126
+ }
127
+ static perform(tween: TWEEN.Tween): Promise<void> {
128
+ return new Promise(resolve => {
129
+ tween.onComplete(resolve).start();
130
+ });
131
  }
132
  }
133
  (async () => {
 
138
  scene.add(gridHelper);
139
  const axesHelper = new THREE.AxesHelper(50);
140
  scene.add(axesHelper);
141
+
 
 
 
 
 
 
 
142
 
143
  {
144
  const egg = await Assets.loadEgg();
145
  c.log(egg);
146
  egg.scale.setScalar(.2);
147
  egg.rotateX(-Math.PI / 2);
148
+ TUtils.flushYZero(egg);
149
  const box = new THREE.BoxHelper(egg);
150
  scene.add(box);
151
  scene.add(egg);
 
159
  c.log(egg);
160
  egg.scale.setScalar(100);
161
  egg.position.x = -10;
162
+ TUtils.flushYZero(egg);
163
  egg.remove(egg.getObjectByName('Camera')!);
164
  scene.add(egg);
165
  // c.log(Utils.boundingBox(egg));
 
184
  const box = new THREE.BoxHelper(container, new THREE.Color('green'));
185
  scene.add(box);
186
  }
187
+ {
188
+ const boloss = (await Assets.loadBoloss()).scene;
189
+ c.log(boloss);
190
+ boloss.position.x = 34;
191
+ TUtils.flushYZero(boloss);
192
+ scene.add(boloss);
193
+ const box = new THREE.BoxHelper(boloss, new THREE.Color('blue'));
194
+ scene.add(box);
195
+ /// Anims like in AudioBoloss
196
+ const rootModel = boloss.getObjectByName(`SketchUp`)!;
197
+ const pupilL = boloss.getObjectByName(`Pupil-left`)!;
198
+ const pupilR = boloss.getObjectByName(`Pupil-right`)!;
199
+ const pupils = new THREE.Group();
200
+ pupils.add(pupilL, pupilR);
201
+ rootModel.add(pupils);
202
+ (async () => {
203
+ while (true) {
204
+ const translatePupil = new TWEEN.Tween(pupils.position)
205
+ .to({ x: "-1", y: "-1" }, 200)
206
+ .easing(TWEEN.Easing.Quadratic.Out)
207
+ ;
208
+ const translatePupilRev = new TWEEN.Tween(pupils.position)
209
+ .to({ x: "+1", y: "+1" }, 200)
210
+ .easing(TWEEN.Easing.Quadratic.Out)
211
+ ;
212
+ await TUtils.perform(translatePupil);
213
+ await Utils.wait(4, 1);
214
+ await TUtils.perform(translatePupilRev);
215
+ await Utils.wait(8, 3);
216
+ }
217
+ })();
218
+ const eyebrowL = boloss.getObjectByName(`Eyebrow-left`)!;
219
+ const eyebrowR = boloss.getObjectByName(`Eyebrow-right`)!;
220
+ const eyebrows = new THREE.Group();
221
+ eyebrows.add(eyebrowL, eyebrowR);
222
+ rootModel.add(eyebrows);
223
+ (async () => {
224
+ while (true) {
225
+ const scaleEyebrow = new TWEEN.Tween(eyebrows.scale)
226
+ .to({ x: 1.08, y: 1.08, z: 1.08 }, 100)
227
+ .easing(TWEEN.Easing.Quadratic.InOut)
228
+ ;
229
+ const scaleEyebrowRev = new TWEEN.Tween(eyebrows.scale)
230
+ .to({ x: 1, y: 1, z: 1 }, 100)
231
+ .easing(TWEEN.Easing.Quadratic.InOut)
232
+ ;
233
+ await Utils.wait(6, 6);
234
+ await TUtils.perform(scaleEyebrow);
235
+ await TUtils.perform(scaleEyebrowRev);
236
+ await Utils.wait(0.14);
237
+ await TUtils.perform(scaleEyebrow);
238
+ await TUtils.perform(scaleEyebrowRev);
239
+ }
240
+ })();
241
+ (async () => {
242
+ while (true) {
243
+ const angle = Utils.randomFloat(-0.2, 0.3);
244
+ const dummyL = new THREE.Object3D();
245
+ dummyL.rotateOnAxis(new THREE.Vector3(0, 1, 0.8), angle);
246
+ const dummyR = new THREE.Object3D();
247
+ dummyR.rotateOnAxis(new THREE.Vector3(0, -1, -0.8), angle);
248
+ /// ^^ exact same result as keeping the same vector and negating the angle.
249
+ const rotateBrowL = new TWEEN.Tween(eyebrowL.rotation)
250
+ .to({
251
+ x: dummyL.rotation.x,
252
+ y: dummyL.rotation.y,
253
+ z: dummyL.rotation.z,
254
+ }, 300)
255
+ ;
256
+ const rotateBrowR = new TWEEN.Tween(eyebrowR.rotation)
257
+ .to({
258
+ x: dummyR.rotation.x,
259
+ y: dummyR.rotation.y,
260
+ z: dummyR.rotation.z,
261
+ }, 300)
262
+ ;
263
+ await Promise.all([
264
+ TUtils.perform(rotateBrowL),
265
+ TUtils.perform(rotateBrowR),
266
+ ]);
267
+ await Utils.wait(1, 1);
268
+ await Promise.all([
269
+ TUtils.perform(
270
+ new TWEEN.Tween(eyebrowL.rotation).to({ x: 0, y: 0, z: 0 }, 300)
271
+ ),
272
+ TUtils.perform(
273
+ new TWEEN.Tween(eyebrowR.rotation).to({ x: 0, y: 0, z: 0 }, 300)
274
+ ),
275
+ ]);
276
+ await Utils.wait(1, 1);
277
+ /// ^^ not the exact same behavior as in AudioBoloss (all waits are actually randoms there.)
278
+ }
279
+ })();
280
+ }
281
+
282
  })();
283
 
284
  /**
 
299
  }
300
  function animate() {
301
  requestAnimationFrame(animate);
302
+ TWEEN.update();
303
  render();
304
  stats.update();
305
  }
src/lib/Utils.ts ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ class Utils {
3
+ /**
4
+ * "Real" modulo (always >= 0), not remainder.
5
+ */
6
+ static mod(a: number, n: number): number {
7
+ return ((a % n) + n) % n;
8
+ }
9
+
10
+ /**
11
+ * Return a random integer between min and max (upper bound is exclusive).
12
+ */
13
+ static randomInt(maxOrMin: number, max?: number): number {
14
+ return (max)
15
+ ? maxOrMin + Math.floor(Math.random() * (max - maxOrMin))
16
+ : Math.floor(Math.random() * maxOrMin);
17
+ }
18
+ static randomFloat(maxOrMin: number, max?: number): number {
19
+ return (max)
20
+ ? maxOrMin + (Math.random() * (max - maxOrMin))
21
+ : Math.random() * maxOrMin;
22
+ }
23
+
24
+ /**
25
+ * Clamp a val to [min, max]
26
+ */
27
+ static clamp(val: number, min: number, max: number): number {
28
+ return Math.min(Math.max(min, val), max);
29
+ }
30
+
31
+ /**
32
+ * Returns a promise that will resolve after the specified time
33
+ * @param ms Number of ms to wait
34
+ */
35
+ static delay(ms: number) {
36
+ return new Promise((resolve, reject) => {
37
+ setTimeout(() => resolve(), ms);
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Compatibility with iOS' SCNAction.wait()
43
+ */
44
+ static wait(duration: number, range: number = 0) {
45
+ return this.delay(
46
+ duration * 1_000
47
+ - range * 1_000 / 2
48
+ + this.randomInt(range * 1_000)
49
+ );
50
+ }
51
+ }