Commit c8912c2ba1e131892b650bd7ccf3249603ec1d12
Merge branch 'main_dev' into 'main'
Main dev See merge request yunteng/thingskit-view!316
Showing
27 changed files
with
4861 additions
and
17 deletions
Too many changes to show.
To preserve performance only 27 of 234 files are displayed.
Dockerfile
0 → 100644
1 | -import path from 'path' | |
1 | +import path, { resolve } from 'path' | |
2 | 2 | import { BuildOptions } from 'vite' |
3 | 3 | export const OUTPUT_DIR = 'dist' |
4 | 4 | |
... | ... | @@ -13,6 +13,10 @@ export const brotliSize = false |
13 | 13 | |
14 | 14 | // 分包 |
15 | 15 | export const rollupOptions: BuildOptions['rollupOptions'] = { |
16 | + input: { | |
17 | + main: resolve(__dirname, '../index.html'), | |
18 | + editor: resolve(__dirname, '../editor/index.html'), | |
19 | + }, | |
16 | 20 | output: { |
17 | 21 | chunkFileNames: 'static/js/[name]-[hash].js', |
18 | 22 | entryFileNames: 'static/js/[name]-[hash].js', | ... | ... |
1 | -import { createHtmlPlugin } from 'vite-plugin-html' | |
1 | +import { HtmlTagDescriptor, Plugin } from 'vite' | |
2 | +// import { createHtmlPlugin } from 'vite-plugin-html' | |
2 | 3 | |
3 | 4 | const GLOB_CONFIG_FILE_NAME = '_app.config.js' |
4 | 5 | |
5 | -export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) { | |
6 | +export function configHtmlPlugin(env: ViteEnv, isBuild: boolean): Plugin { | |
6 | 7 | const { VITE_GLOB_APP_TITLE, VITE_GLOB_PUBLIC_PATH } = env |
7 | 8 | const getAppConfigSrc = () => { |
8 | 9 | const path = VITE_GLOB_PUBLIC_PATH |
9 | 10 | return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${Date.now()}` |
10 | 11 | } |
11 | 12 | |
12 | - const htmlPlugin = createHtmlPlugin({ | |
13 | - minify: isBuild, | |
14 | - inject: { | |
15 | - data: { | |
16 | - title: VITE_GLOB_APP_TITLE, | |
17 | - }, | |
18 | - tags: isBuild ? [ | |
19 | - { | |
20 | - tag: 'script', | |
21 | - attrs: { src: getAppConfigSrc() } | |
22 | - } | |
23 | - ] : [] | |
13 | + // const htmlPlugin = createHtmlPlugin({ | |
14 | + // minify: isBuild, | |
15 | + // inject: { | |
16 | + // data: { | |
17 | + // title: VITE_GLOB_APP_TITLE, | |
18 | + // }, | |
19 | + // tags: isBuild ? [ | |
20 | + // { | |
21 | + // tag: 'script', | |
22 | + // attrs: { src: getAppConfigSrc() } | |
23 | + // } | |
24 | + // ] : [] | |
25 | + // } | |
26 | + // }) | |
27 | + | |
28 | + | |
29 | + | |
30 | + const tags:HtmlTagDescriptor[] = [ | |
31 | + { | |
32 | + tag: 'title', | |
33 | + children: VITE_GLOB_APP_TITLE | |
24 | 34 | } |
25 | - }) | |
35 | + ] | |
26 | 36 | |
27 | - return htmlPlugin | |
37 | + if (isBuild) { | |
38 | + tags.push( | |
39 | + { | |
40 | + tag: 'script', | |
41 | + attrs: { src: getAppConfigSrc() } | |
42 | + } | |
43 | + ) | |
44 | + } | |
45 | + return { | |
46 | + name: 'html-plugin', | |
47 | + transformIndexHtml: (code, ctx) => { | |
48 | + | |
49 | + return { | |
50 | + html: code, | |
51 | + tags | |
52 | + } | |
53 | + }, | |
54 | + | |
55 | + } | |
28 | 56 | } | ... | ... |
default.conf
0 → 100644
1 | +server { | |
2 | + listen 80; | |
3 | + listen [::]:80; | |
4 | + server_name 192.168.1.48; | |
5 | + #charset koi8-r; | |
6 | + #access_log /var/log/nginx/host.access.log main; | |
7 | + | |
8 | + root /usr/share/nginx/html; | |
9 | + index index.html index.htm; | |
10 | + try_files $uri $uri/ /index.html; | |
11 | + | |
12 | + | |
13 | + | |
14 | + location /api/ { | |
15 | + proxy_pass http://192.168.1.48:8080; | |
16 | + proxy_http_version 1.1; | |
17 | + proxy_set_header Upgrade $http_upgrade; | |
18 | + proxy_set_header Connection "Upgrade"; | |
19 | + proxy_set_header Host $host; | |
20 | + proxy_cache_bypass $http_upgrade; | |
21 | + } | |
22 | + | |
23 | + location /yt/ { | |
24 | + proxy_pass http://192.168.1.48:8080; | |
25 | + proxy_http_version 1.1; | |
26 | + proxy_set_header Upgrade $http_upgrade; | |
27 | + proxy_set_header Connection "Upgrade"; | |
28 | + proxy_set_header Host $host; | |
29 | + proxy_cache_bypass $http_upgrade; | |
30 | + } | |
31 | + | |
32 | + location /upload/ { | |
33 | + proxy_pass http://192.168.1.48:8080; | |
34 | + proxy_http_version 1.1; | |
35 | + proxy_set_header Upgrade $http_upgrade; | |
36 | + proxy_set_header Connection "Upgrade"; | |
37 | + proxy_set_header Host $host; | |
38 | + proxy_cache_bypass $http_upgrade; | |
39 | + } | |
40 | + | |
41 | + #error_page 404 /404.html; | |
42 | + | |
43 | + # redirect server error pages to the static page /50x.html | |
44 | + # | |
45 | + error_page 500 502 503 504 /50x.html; | |
46 | + location = /50x.html { | |
47 | + root /usr/share/nginx/html; | |
48 | + } | |
49 | +} | |
\ No newline at end of file | ... | ... |
editor/.eslintrc.json
0 → 100644
editor/css/main.css
0 → 100644
1 | +:root { | |
2 | + color-scheme: light dark; | |
3 | +} | |
4 | + | |
5 | +[hidden] { | |
6 | + display: none !important; | |
7 | +} | |
8 | + | |
9 | +body { | |
10 | + font-family: Helvetica, Arial, sans-serif; | |
11 | + font-size: 14px; | |
12 | + margin: 0; | |
13 | + overflow: hidden; | |
14 | +} | |
15 | + | |
16 | +hr { | |
17 | + border: 0; | |
18 | + border-top: 1px solid #ccc; | |
19 | +} | |
20 | + | |
21 | +button { | |
22 | + position: relative; | |
23 | +} | |
24 | + | |
25 | +input { | |
26 | + vertical-align: middle; | |
27 | +} | |
28 | + | |
29 | + input[type="color"]::-webkit-color-swatch-wrapper { | |
30 | + padding: 0; | |
31 | + } | |
32 | + input[type="color"]::-webkit-color-swatch { | |
33 | + border: none; | |
34 | + } | |
35 | + | |
36 | +textarea { | |
37 | + tab-size: 4; | |
38 | + white-space: pre; | |
39 | + word-wrap: normal; | |
40 | +} | |
41 | + | |
42 | + textarea.success { | |
43 | + border-color: #8b8 !important; | |
44 | + } | |
45 | + | |
46 | + textarea.fail { | |
47 | + border-color: #f00 !important; | |
48 | + background-color: rgba(255,0,0,0.05); | |
49 | + } | |
50 | + | |
51 | +textarea, input { outline: none; } /* osx */ | |
52 | + | |
53 | +.Panel { | |
54 | + -moz-user-select: none; | |
55 | + -webkit-user-select: none; | |
56 | + -ms-user-select: none; | |
57 | + | |
58 | + /* No support for these yet */ | |
59 | + -o-user-select: none; | |
60 | + user-select: none; | |
61 | +} | |
62 | + | |
63 | +.TabbedPanel { | |
64 | + -moz-user-select: none; | |
65 | + -webkit-user-select: none; | |
66 | + -ms-user-select: none; | |
67 | + | |
68 | + /* No support for these yet */ | |
69 | + -o-user-select: none; | |
70 | + user-select: none; | |
71 | + position: relative; | |
72 | + display: block; | |
73 | + width: 100%; | |
74 | + min-width: 335px; | |
75 | +} | |
76 | + | |
77 | +.TabbedPanel .Tabs { | |
78 | + position: relative; | |
79 | + z-index: 1; /** Above .Panels **/ | |
80 | + display: block; | |
81 | + width: 100%; | |
82 | + white-space: pre; | |
83 | + overflow: hidden; | |
84 | + overflow-x: auto; | |
85 | +} | |
86 | + | |
87 | + .TabbedPanel .Tabs::-webkit-scrollbar { | |
88 | + height: 5px; | |
89 | + background: #eee; | |
90 | + } | |
91 | + .TabbedPanel .Tabs::-webkit-scrollbar-thumb { | |
92 | + background: #08f3; | |
93 | + } | |
94 | + .TabbedPanel .Tabs:hover::-webkit-scrollbar-thumb { | |
95 | + background: #08f; | |
96 | + cursor: ew-resize; | |
97 | + } | |
98 | + | |
99 | + .TabbedPanel .Tabs .Tab { | |
100 | + padding: 10px 9px; | |
101 | + text-transform: uppercase; | |
102 | + } | |
103 | + | |
104 | + .TabbedPanel .Panels { | |
105 | + position: absolute; | |
106 | + top: 40px; | |
107 | + display: block; | |
108 | + width: 100%; | |
109 | + } | |
110 | + | |
111 | +/* Listbox */ | |
112 | +.Listbox { | |
113 | + color: #444; | |
114 | + background-color: #fff; | |
115 | + padding: 0; | |
116 | + width: 100%; | |
117 | + min-height: 180px; | |
118 | + font-size: 12px; | |
119 | + cursor: default; | |
120 | + overflow: auto; | |
121 | +} | |
122 | + | |
123 | +.Listbox .ListboxItem { | |
124 | + padding: 6px; | |
125 | + color: #666; | |
126 | + white-space: nowrap; | |
127 | +} | |
128 | + | |
129 | +.Listbox .ListboxItem.active { | |
130 | + background-color: rgba(0, 0, 0, 0.04); | |
131 | +} | |
132 | + | |
133 | +/* CodeMirror */ | |
134 | + | |
135 | +.CodeMirror { | |
136 | + | |
137 | + position: absolute !important; | |
138 | + top: 37px; | |
139 | + width: 100% !important; | |
140 | + height: calc(100% - 37px) !important; | |
141 | + | |
142 | +} | |
143 | + | |
144 | + .CodeMirror .errorLine { | |
145 | + | |
146 | + background: rgba(255,0,0,0.25); | |
147 | + | |
148 | + } | |
149 | + | |
150 | + .CodeMirror .esprima-error { | |
151 | + | |
152 | + color: #f00; | |
153 | + text-align: right; | |
154 | + padding: 0 20px; | |
155 | + | |
156 | + } | |
157 | + | |
158 | +/* outliner */ | |
159 | + | |
160 | +#outliner .opener { | |
161 | + display: inline-block; | |
162 | + width: 14px; | |
163 | + height: 14px; | |
164 | + margin: 0px 4px; | |
165 | + vertical-align: top; | |
166 | + text-align: center; | |
167 | +} | |
168 | + | |
169 | + #outliner .opener.open:after { | |
170 | + content: '−'; | |
171 | + } | |
172 | + | |
173 | + #outliner .opener.closed:after { | |
174 | + content: '+'; | |
175 | + } | |
176 | + | |
177 | +#outliner .option { | |
178 | + | |
179 | + border: 1px solid transparent; | |
180 | + | |
181 | +} | |
182 | + | |
183 | +#outliner .option.drag { | |
184 | + | |
185 | + border: 1px dashed #999; | |
186 | + | |
187 | +} | |
188 | + | |
189 | +#outliner .option.dragTop { | |
190 | + | |
191 | + border-top: 1px dashed #999; | |
192 | + | |
193 | +} | |
194 | + | |
195 | +#outliner .option.dragBottom { | |
196 | + | |
197 | + border-bottom: 1px dashed #999; | |
198 | + | |
199 | +} | |
200 | + | |
201 | +#outliner .type { | |
202 | + display: inline-block; | |
203 | + width: 14px; | |
204 | + height: 14px; | |
205 | + color: #ddd; | |
206 | + text-align: center; | |
207 | +} | |
208 | + | |
209 | +#outliner .type:after { | |
210 | + content: '●'; | |
211 | +} | |
212 | + | |
213 | +/* */ | |
214 | + | |
215 | +#outliner .Scene { | |
216 | + color: #8888dd; | |
217 | +} | |
218 | + | |
219 | +#outliner .Camera { | |
220 | + color: #dd8888; | |
221 | +} | |
222 | + | |
223 | +#outliner .Light { | |
224 | + color: #dddd88; | |
225 | +} | |
226 | + | |
227 | +/* */ | |
228 | + | |
229 | +#outliner .Object3D { | |
230 | + color: #aaaaee; | |
231 | +} | |
232 | + | |
233 | +#outliner .Mesh { | |
234 | + color: #8888ee; | |
235 | +} | |
236 | + | |
237 | +#outliner .Line { | |
238 | + color: #88ee88; | |
239 | +} | |
240 | + | |
241 | +#outliner .LineSegments { | |
242 | + color: #88ee88; | |
243 | +} | |
244 | + | |
245 | +#outliner .Points { | |
246 | + color: #ee8888; | |
247 | +} | |
248 | + | |
249 | +/* */ | |
250 | + | |
251 | +#outliner .Geometry { | |
252 | + color: #aaeeaa; | |
253 | +} | |
254 | + | |
255 | +#outliner .Material { | |
256 | + color: #eeaaee; | |
257 | +} | |
258 | + | |
259 | +/* */ | |
260 | + | |
261 | +#outliner .Script:after { | |
262 | + content: '◎' | |
263 | +} | |
264 | + | |
265 | +/* */ | |
266 | + | |
267 | +button { | |
268 | + color: #555; | |
269 | + background-color: #ddd; | |
270 | + border: 0px; | |
271 | + margin: 0px; /* GNOME Web */ | |
272 | + padding: 5px 8px; | |
273 | + font-size: 12px; | |
274 | + text-transform: uppercase; | |
275 | + cursor: pointer; | |
276 | + outline: none; | |
277 | +} | |
278 | + | |
279 | + button:hover { | |
280 | + background-color: #fff; | |
281 | + } | |
282 | + | |
283 | + button.selected { | |
284 | + background-color: #fff; | |
285 | + } | |
286 | + | |
287 | +input, textarea { | |
288 | + border: 1px solid transparent; | |
289 | + color: #444; | |
290 | +} | |
291 | + | |
292 | +input.Number { | |
293 | + color: #08f!important; | |
294 | + font-size: 12px; | |
295 | + border: 0px; | |
296 | + padding: 2px; | |
297 | +} | |
298 | + | |
299 | +select { | |
300 | + color: #666; | |
301 | + background-color: #ddd; | |
302 | + border: 0px; | |
303 | + text-transform: uppercase; | |
304 | + cursor: pointer; | |
305 | + outline: none; | |
306 | +} | |
307 | + | |
308 | + select:hover { | |
309 | + background-color: #fff; | |
310 | + } | |
311 | + | |
312 | +/* UI */ | |
313 | + | |
314 | +#resizer { | |
315 | + position: absolute; | |
316 | + z-index: 2; /* Above #sidebar */ | |
317 | + top: 32px; | |
318 | + right: 350px; | |
319 | + width: 5px; | |
320 | + bottom: 0px; | |
321 | + transform: translatex(2.5px); | |
322 | + cursor: col-resize; | |
323 | +} | |
324 | + | |
325 | + #resizer:hover { | |
326 | + background-color: #08f8; | |
327 | + transition-property: background-color; | |
328 | + transition-delay: 0.1s; | |
329 | + transition-duration: 0.2s; | |
330 | + } | |
331 | + | |
332 | + #resizer:active { | |
333 | + background-color: #08f; | |
334 | + } | |
335 | + | |
336 | +#viewport { | |
337 | + position: absolute; | |
338 | + top: 32px; | |
339 | + left: 0; | |
340 | + right: 350px; | |
341 | + bottom: 0; | |
342 | +} | |
343 | + | |
344 | + #viewport .Text { | |
345 | + text-shadow: 1px 1px 0 rgba(0,0,0,0.25); | |
346 | + pointer-events: none; | |
347 | + } | |
348 | + | |
349 | +#script { | |
350 | + position: absolute; | |
351 | + top: 32px; | |
352 | + left: 0; | |
353 | + right: 350px; | |
354 | + bottom: 0; | |
355 | + opacity: 0.9; | |
356 | +} | |
357 | + | |
358 | +#player { | |
359 | + position: absolute; | |
360 | + top: 32px; | |
361 | + left: 0; | |
362 | + right: 350px; | |
363 | + bottom: 0; | |
364 | +} | |
365 | + | |
366 | +#menubar { | |
367 | + position: absolute; | |
368 | + width: 100%; | |
369 | + height: 32px; | |
370 | + background: #eee; | |
371 | + padding: 0; | |
372 | + margin: 0; | |
373 | + right: 0; | |
374 | + top: 0; | |
375 | +} | |
376 | + | |
377 | + #menubar .menu { | |
378 | + float: left; | |
379 | + cursor: pointer; | |
380 | + padding-right: 8px; | |
381 | + } | |
382 | + | |
383 | + #menubar .menu.right { | |
384 | + float: right; | |
385 | + cursor: auto; | |
386 | + padding-right: 0; | |
387 | + text-align: right; | |
388 | + } | |
389 | + | |
390 | + #menubar .menu .title { | |
391 | + display: inline-block; | |
392 | + color: #888; | |
393 | + margin: 0; | |
394 | + padding: 8px; | |
395 | + line-height: 16px; | |
396 | + } | |
397 | + | |
398 | + #menubar .menu .key { | |
399 | + position: absolute; | |
400 | + right: 10px; | |
401 | + color: #ccc; | |
402 | + border: 1px solid #ccc; | |
403 | + border-radius: 4px; | |
404 | + font-size: 9px; | |
405 | + padding: 2px 4px; | |
406 | + right: 10px; | |
407 | + pointer-events: none; | |
408 | + } | |
409 | + | |
410 | + #menubar .menu .options { | |
411 | + position: fixed; | |
412 | + z-index: 1; /* higher than resizer */ | |
413 | + display: none; | |
414 | + padding: 5px 0; | |
415 | + background: #eee; | |
416 | + min-width: 150px; | |
417 | + max-height: calc(100vh - 80px); | |
418 | + overflow: auto; | |
419 | + } | |
420 | + | |
421 | + #menubar .menu:hover .options { | |
422 | + display: block; | |
423 | + box-shadow: 0 10px 10px -5px #00000033; | |
424 | + } | |
425 | + | |
426 | + #menubar .menu .options hr { | |
427 | + border-color: #ddd; | |
428 | + } | |
429 | + | |
430 | + #menubar .menu .options .option { | |
431 | + color: #666; | |
432 | + background-color: transparent; | |
433 | + padding: 5px 10px; | |
434 | + margin: 0 !important; | |
435 | + } | |
436 | + | |
437 | + #menubar .menu .options .option:hover { | |
438 | + color: #fff; | |
439 | + background-color: #08f; | |
440 | + } | |
441 | + | |
442 | + #menubar .menu .options .option:not(.submenu-title):active { | |
443 | + color: #666; | |
444 | + background: transparent; | |
445 | + } | |
446 | + | |
447 | + #menubar .menu .options .option.toggle::before { | |
448 | + | |
449 | + content: ' '; | |
450 | + display: inline-block; | |
451 | + width: 16px; | |
452 | + | |
453 | + } | |
454 | + | |
455 | + #menubar .menu .options .option.toggle-on::before { | |
456 | + | |
457 | + content: '✔'; | |
458 | + font-size: 12px; | |
459 | + | |
460 | + } | |
461 | + | |
462 | + #menubar .submenu-title::after { | |
463 | + content: '⏵'; | |
464 | + float: right; | |
465 | + } | |
466 | + | |
467 | + #menubar .menu .options .inactive { | |
468 | + color: #bbb; | |
469 | + background-color: transparent; | |
470 | + padding: 5px 10px; | |
471 | + margin: 0 !important; | |
472 | + cursor: not-allowed; | |
473 | + } | |
474 | + | |
475 | + | |
476 | + | |
477 | +#sidebar { | |
478 | + position: absolute; | |
479 | + right: 0; | |
480 | + top: 32px; | |
481 | + bottom: 0; | |
482 | + width: 350px; | |
483 | + background: #eee; | |
484 | + overflow: auto; | |
485 | + overflow-x: hidden; | |
486 | +} | |
487 | + | |
488 | + #sidebar .Panel { | |
489 | + color: #888; | |
490 | + padding: 10px; | |
491 | + border-top: 1px solid #ccc; | |
492 | + } | |
493 | + | |
494 | + #sidebar .Panel.collapsed { | |
495 | + margin-bottom: 0; | |
496 | + } | |
497 | + | |
498 | + #sidebar .Row { | |
499 | + display: flex; | |
500 | + align-items: center; | |
501 | + min-height: 24px; | |
502 | + margin-bottom: 10px; | |
503 | + } | |
504 | + | |
505 | + #sidebar .Row .Label { | |
506 | + | |
507 | + width: 120px; | |
508 | + | |
509 | + } | |
510 | + | |
511 | +#tabs { | |
512 | + background-color: #ddd; | |
513 | + border-top: 1px solid #ccc; | |
514 | +} | |
515 | + | |
516 | + #tabs span { | |
517 | + color: #aaa; | |
518 | + border-right: 1px solid #ccc; | |
519 | + padding: 10px; | |
520 | + } | |
521 | + | |
522 | + #tabs span.selected { | |
523 | + color: #888; | |
524 | + background-color: #eee; | |
525 | + } | |
526 | + | |
527 | +#toolbar { | |
528 | + position: absolute; | |
529 | + left: 10px; | |
530 | + top: 42px; | |
531 | + width: 32px; | |
532 | + background: #eee; | |
533 | + text-align: center; | |
534 | +} | |
535 | + | |
536 | + #toolbar button, #toolbar input { | |
537 | + height: 32px; | |
538 | + } | |
539 | + | |
540 | + #toolbar button img { | |
541 | + width: 16px; | |
542 | + opacity: 0.5; | |
543 | + } | |
544 | + | |
545 | +.Outliner { | |
546 | + color: #444; | |
547 | + background-color: #fff; | |
548 | + padding: 0; | |
549 | + width: 100%; | |
550 | + height: 180px; | |
551 | + font-size: 12px; | |
552 | + cursor: default; | |
553 | + overflow: auto; | |
554 | + resize: vertical; | |
555 | + outline: none !important; | |
556 | +} | |
557 | + | |
558 | + .Outliner .option { | |
559 | + padding: 4px; | |
560 | + color: #666; | |
561 | + white-space: nowrap; | |
562 | + } | |
563 | + | |
564 | + .Outliner .option:hover { | |
565 | + background-color: rgba(0,0,0,0.02); | |
566 | + } | |
567 | + | |
568 | + .Outliner .option.active { | |
569 | + background-color: rgba(0,0,0,0.04); | |
570 | + } | |
571 | + | |
572 | + | |
573 | +.TabbedPanel .Tabs { | |
574 | + background-color: #ddd; | |
575 | + border-top: 1px solid #ccc; | |
576 | +} | |
577 | + | |
578 | + .TabbedPanel .Tab { | |
579 | + color: #aaa; | |
580 | + border-right: 1px solid #ccc; | |
581 | + } | |
582 | + | |
583 | + .TabbedPanel .Tab.selected { | |
584 | + color: #888; | |
585 | + background-color: #eee; | |
586 | + } | |
587 | + | |
588 | +.Listbox { | |
589 | + color: #444; | |
590 | + background-color: #fff; | |
591 | +} | |
592 | + | |
593 | +.Panel { | |
594 | + color: #888; | |
595 | +} | |
596 | + | |
597 | +/* */ | |
598 | + | |
599 | +@media all and ( max-width: 600px ) { | |
600 | + | |
601 | + #resizer { | |
602 | + display: none; | |
603 | + } | |
604 | + | |
605 | + #menubar .menu .options { | |
606 | + max-height: calc(100% - 80px); | |
607 | + } | |
608 | + | |
609 | + #menubar .menu.right { | |
610 | + display: none; | |
611 | + } | |
612 | + | |
613 | + #viewport { | |
614 | + left: 0; | |
615 | + right: 0; | |
616 | + top: 32px; | |
617 | + height: calc(100% - 352px); | |
618 | + } | |
619 | + | |
620 | + #script { | |
621 | + left: 0; | |
622 | + right: 0; | |
623 | + top: 32px; | |
624 | + height: calc(100% - 352px); | |
625 | + } | |
626 | + | |
627 | + #player { | |
628 | + left: 0; | |
629 | + right: 0; | |
630 | + top: 32px; | |
631 | + height: calc(100% - 352px); | |
632 | + } | |
633 | + | |
634 | + #sidebar { | |
635 | + left: 0; | |
636 | + width: 100%; | |
637 | + top: calc(100% - 320px); | |
638 | + bottom: 0; | |
639 | + } | |
640 | + | |
641 | +} | |
642 | + | |
643 | +/* DARK MODE */ | |
644 | + | |
645 | +@media ( prefers-color-scheme: dark ) { | |
646 | + | |
647 | + button { | |
648 | + color: #aaa; | |
649 | + background-color: #222; | |
650 | + } | |
651 | + | |
652 | + button:hover { | |
653 | + color: #ccc; | |
654 | + background-color: #444; | |
655 | + } | |
656 | + | |
657 | + button.selected { | |
658 | + color: #fff; | |
659 | + background-color: #08f; | |
660 | + } | |
661 | + | |
662 | + input, textarea { | |
663 | + background-color: #222; | |
664 | + border: 1px solid transparent; | |
665 | + color: #888; | |
666 | + } | |
667 | + | |
668 | + select { | |
669 | + color: #aaa; | |
670 | + background-color: #222; | |
671 | + } | |
672 | + | |
673 | + select:hover { | |
674 | + color: #ccc; | |
675 | + background-color: #444; | |
676 | + } | |
677 | + | |
678 | + /* UI */ | |
679 | + | |
680 | + #menubar { | |
681 | + background: #111; | |
682 | + } | |
683 | + | |
684 | + #menubar .menu .key { | |
685 | + color: #444; | |
686 | + border-color: #444; | |
687 | + } | |
688 | + | |
689 | + #menubar .menu .options { | |
690 | + background: #111; | |
691 | + } | |
692 | + | |
693 | + #menubar .menu .options hr { | |
694 | + border-color: #222; | |
695 | + } | |
696 | + | |
697 | + #menubar .menu .options .option { | |
698 | + color: #888; | |
699 | + } | |
700 | + | |
701 | + #menubar .menu .options .inactive { | |
702 | + color: #444; | |
703 | + } | |
704 | + | |
705 | + #sidebar { | |
706 | + background-color: #111; | |
707 | + } | |
708 | + | |
709 | + #sidebar .Panel { | |
710 | + border-top: 1px solid #222; | |
711 | + } | |
712 | + | |
713 | + #sidebar .Panel.Material canvas { | |
714 | + border: solid 1px #5A5A5A; | |
715 | + } | |
716 | + | |
717 | + #tabs { | |
718 | + background-color: #1b1b1b; | |
719 | + border-top: 1px solid #222; | |
720 | + } | |
721 | + | |
722 | + #tabs span { | |
723 | + color: #555; | |
724 | + border-right: 1px solid #222; | |
725 | + } | |
726 | + | |
727 | + #tabs span.selected { | |
728 | + background-color: #111; | |
729 | + } | |
730 | + | |
731 | + #toolbar { | |
732 | + background-color: #111; | |
733 | + } | |
734 | + | |
735 | + #toolbar img { | |
736 | + filter: invert(1); | |
737 | + } | |
738 | + | |
739 | + .Outliner { | |
740 | + background: #222; | |
741 | + } | |
742 | + | |
743 | + .Outliner .option { | |
744 | + color: #999; | |
745 | + } | |
746 | + | |
747 | + .Outliner .option:hover { | |
748 | + background-color: rgba(21,60,94,0.5); | |
749 | + } | |
750 | + | |
751 | + .Outliner .option.active { | |
752 | + background-color: rgba(21,60,94,1); | |
753 | + } | |
754 | + | |
755 | + .TabbedPanel .Tabs { | |
756 | + background-color: #1b1b1b; | |
757 | + border-top: 1px solid #222; | |
758 | + } | |
759 | + | |
760 | + .TabbedPanel .Tabs::-webkit-scrollbar { | |
761 | + background: #111; | |
762 | + } | |
763 | + | |
764 | + .TabbedPanel .Tab { | |
765 | + color: #555; | |
766 | + border-right: 1px solid #222; | |
767 | + } | |
768 | + | |
769 | + .TabbedPanel .Tab.selected { | |
770 | + color: #888; | |
771 | + background-color: #111; | |
772 | + } | |
773 | + | |
774 | + .Listbox { | |
775 | + color: #888; | |
776 | + background: #222; | |
777 | + } | |
778 | + | |
779 | + .Listbox .ListboxItem:hover { | |
780 | + background-color: rgba(21,60,94,0.5); | |
781 | + } | |
782 | + | |
783 | + .Listbox .ListboxItem.active { | |
784 | + background-color: rgba(21,60,94,1); | |
785 | + } | |
786 | + | |
787 | +} | |
788 | + | |
789 | +/* Temporary Chrome fix (#24794) */ | |
790 | + | |
791 | +[draggable="true"] { | |
792 | + transform: translate(0, 0); | |
793 | + z-index: 0; | |
794 | +} | ... | ... |
1 | +How to implement additional commands for undo/redo functionality? | |
2 | +=== | |
3 | + | |
4 | +### Basics ### | |
5 | + | |
6 | +After evaluating different design patterns for undo/redo we decided to use the [command-pattern](http://en.wikipedia.org/wiki/Command_pattern) for implementing undo/redo functionality in the three.js-editor. | |
7 | + | |
8 | +This means that every action is encapsulated in a command-object which contains all the relevant information to restore the previous state. | |
9 | + | |
10 | +In our implementation we store the old and the new state separately (we don't store the complete state but rather the attribute and value which has changed). | |
11 | +It would also be possible to only store the difference between the old and the new state. | |
12 | + | |
13 | +**Before implementing your own command you should look if you can't reuse one of the already existing ones.** | |
14 | + | |
15 | +For numbers, strings or booleans the Set...ValueCommand-commands can be used. | |
16 | +Then there are separate commands for: | |
17 | +- setting a color property (THREE.Color) | |
18 | +- setting maps (THREE.Texture) | |
19 | +- setting geometries | |
20 | +- setting materials | |
21 | +- setting position, rotation and scale | |
22 | + | |
23 | +### Template for new commands ### | |
24 | + | |
25 | +Every command needs a constructor. In the constructor | |
26 | + | |
27 | +```javascript | |
28 | + | |
29 | +function DoSomethingCommand( editor ) { | |
30 | + | |
31 | + Command.call( this, editor ); // Required: Call default constructor | |
32 | + | |
33 | + this.type = 'DoSomethingCommand'; // Required: has to match the object-name! | |
34 | + this.name = 'Set/Do/Update Something'; // Required: description of the command, used in Sidebar.History | |
35 | + | |
36 | + // TODO: store all the relevant information needed to | |
37 | + // restore the old and the new state | |
38 | + | |
39 | +} | |
40 | +``` | |
41 | + | |
42 | +And as part of the prototype you need to implement four functions | |
43 | +- **execute:** which is also used for redo | |
44 | +- **undo:** which reverts the changes made by 'execute' | |
45 | +- **toJSON:** which serializes the command so that the undo/redo-history can be preserved across a browser refresh | |
46 | +- **fromJSON:** which deserializes the command | |
47 | + | |
48 | +```javascript | |
49 | +DoSomethingCommand.prototype = { | |
50 | + | |
51 | + execute: function () { | |
52 | + | |
53 | + // TODO: apply changes to 'object' to reach the new state | |
54 | + | |
55 | + }, | |
56 | + | |
57 | + undo: function () { | |
58 | + | |
59 | + // TODO: restore 'object' to old state | |
60 | + | |
61 | + }, | |
62 | + | |
63 | + toJSON: function () { | |
64 | + | |
65 | + var output = Command.prototype.toJSON.call( this ); // Required: Call 'toJSON'-method of prototype 'Command' | |
66 | + | |
67 | + // TODO: serialize all the necessary information as part of 'output' (JSON-format) | |
68 | + // so that it can be restored in 'fromJSON' | |
69 | + | |
70 | + return output; | |
71 | + | |
72 | + }, | |
73 | + | |
74 | + fromJSON: function ( json ) { | |
75 | + | |
76 | + Command.prototype.fromJSON.call( this, json ); // Required: Call 'fromJSON'-method of prototype 'Command' | |
77 | + | |
78 | + // TODO: restore command from json | |
79 | + | |
80 | + } | |
81 | + | |
82 | +}; | |
83 | + | |
84 | +``` | |
85 | + | |
86 | +### Executing a command ### | |
87 | + | |
88 | +To execute a command we need an instance of the main editor-object. The editor-object functions as the only entry point through which all commands have to go to be added as part of the undo/redo-history. | |
89 | +On **editor** we then call **.execute(...)*** with the new command-object which in turn calls **history.execute(...)** and adds the command to the undo-stack. | |
90 | + | |
91 | +```javascript | |
92 | + | |
93 | +editor.execute( new DoSomethingCommand() ); | |
94 | + | |
95 | +``` | |
96 | + | |
97 | +### Updatable commands ### | |
98 | + | |
99 | +Some commands are also **updatable**. By default a command is not updatable. Making a command updatable means that you | |
100 | +have to implement a fifth function 'update' as part of the prototype. In it only the 'new' state gets updated while the old one stays the same. | |
101 | + | |
102 | +Here as an example is the update-function of **SetColorCommand**: | |
103 | + | |
104 | +```javascript | |
105 | +update: function ( cmd ) { | |
106 | + | |
107 | + this.newValue = cmd.newValue; | |
108 | + | |
109 | +}, | |
110 | + | |
111 | +``` | |
112 | + | |
113 | +#### List of updatable commands | |
114 | + | |
115 | +- SetColorCommand | |
116 | +- SetGeometryCommand | |
117 | +- SetMaterialColorCommand | |
118 | +- SetMaterialValueCommand | |
119 | +- SetPositionCommand | |
120 | +- SetRotationCommand | |
121 | +- SetScaleCommand | |
122 | +- SetValueCommand | |
123 | +- SetScriptValueCommand | |
124 | + | |
125 | +The idea behind 'updatable commands' is that two commands of the same type which occur | |
126 | +within a short period of time should be merged into one. | |
127 | +**For example:** Dragging with your mouse over the x-position field in the sidebar | |
128 | +leads to hundreds of minor changes to the x-position. | |
129 | +The user expectation is not to undo every single change that happened while they dragged | |
130 | +the mouse cursor but rather to go back to the position before they started to drag their mouse. | |
131 | + | |
132 | +When editing a script the changes are also merged into one undo-step. | ... | ... |
1 | +Writing unit tests for undo-redo commands | |
2 | +=== | |
3 | + | |
4 | +### Overview ### | |
5 | + | |
6 | +Writing unit tests for undo/redo commands is easy. | |
7 | +The main idea to simulate a scene, execute actions and perform undo and redo. | |
8 | +Following steps are required. | |
9 | + | |
10 | +1. Create a new unit test file | |
11 | +2. Include the new command and the unit test file in the editor's test suite | |
12 | +3. Write the test | |
13 | +4. Execute the test | |
14 | + | |
15 | +Each of the listed steps will now be described in detail. | |
16 | + | |
17 | +### 1. Create a new unit test file ### | |
18 | + | |
19 | +Create a new file in path `test/unit/editor/TestDoSomethingCommand.js`. | |
20 | + | |
21 | +### 2. Include the new command in the editor test suite ### | |
22 | + | |
23 | +Navigate to the editor test suite `test/unit/unittests_editor.html` and open it. | |
24 | +Within the file, go to the `<!-- command object classes -->` and include the new command: | |
25 | + | |
26 | +```html | |
27 | +// <!-- command object classes --> | |
28 | +//... | |
29 | +<script src="../../editor/js/commands/AddScriptCommand.js"></script> | |
30 | +<script src="../../editor/js/commands/DoSomethingCommand.js"></script> // add this line | |
31 | +<script src="../../editor/js/commands/MoveObjectCommand.js"></script> | |
32 | +//... | |
33 | +``` | |
34 | + | |
35 | +It is recommended to keep the script inclusions in alphabetical order, if possible. | |
36 | + | |
37 | +Next, in the same file, go to `<!-- Undo-Redo tests -->` and include the test file for the new command: | |
38 | + | |
39 | +```html | |
40 | +// <!-- Undo-Redo tests --> | |
41 | +//... | |
42 | +<script src="editor/TestAddScriptCommand.js"></script> | |
43 | +<script src="editor/TestDoSomethingCommand.js"></script> // add this line | |
44 | +<script src="editor/TestMoveObjectCommand.js"></script> | |
45 | +//... | |
46 | +``` | |
47 | + | |
48 | +Again, keeping the alphabetical order is recommended. | |
49 | + | |
50 | +### 3. Write the test ### | |
51 | + | |
52 | +#### Template #### | |
53 | + | |
54 | +Open the unit test file `test/unit/editor/TestDoSomethingCommand.js` and paste following code: | |
55 | + | |
56 | +```javascript | |
57 | +module( "DoSomethingCommand" ); | |
58 | + | |
59 | +test("Test DoSomethingCommand (Undo and Redo)", function() { | |
60 | + | |
61 | + var editor = new Editor(); | |
62 | + | |
63 | + var box = aBox( 'Name your box' ); | |
64 | + | |
65 | + // other available objects from "CommonUtilities.js" | |
66 | + // var sphere = aSphere( 'Name your sphere' ); | |
67 | + // var pointLight = aPointLight( 'Name your pointLight' ); | |
68 | + // var perspectiveCamera = aPerspectiveCamera( 'Name your perspectiveCamera' ); | |
69 | + | |
70 | + // in most cases you'll need to add the object to work with | |
71 | + editor.execute( new AddObjectCommand( editor, box ) ); | |
72 | + | |
73 | + | |
74 | + // your test begins here... | |
75 | + | |
76 | + | |
77 | +} ); | |
78 | +``` | |
79 | + | |
80 | +The predefined code is just meant to ease the development, you do not have to stick with it. | |
81 | +However, the test should cover at least one `editor.execute()`, one `editor.undo()` and one `editor.redo()` call. | |
82 | + | |
83 | +Best practice is to call `editor.execute( new DoSomethingCommand( {custom parameters} ) )` **twice**. Since you'll have to do one undo (go one step back), it is recommended to have a custom state for comparison. Try to avoid assertions `ok()` against default values. | |
84 | + | |
85 | +#### Assertions #### | |
86 | +After performing `editor.execute()` twice, you can do your first assertion to check whether the executes are done correctly. | |
87 | + | |
88 | +Next, you perform `editor.undo()` and check if the last action was undone. | |
89 | + | |
90 | +Finally, perform `editor.redo()` and verify if the values are as expected. | |
91 | + | |
92 | +### 4. Execute the test ### | |
93 | + | |
94 | +Open the editor's unit test suite `test/unit/unittests_editor.html` in your browser and check the results from the test framework. | ... | ... |
editor/examples/arkanoid.app.json
0 → 100644
1 | +{ | |
2 | + "metadata": { | |
3 | + "type": "App" | |
4 | + }, | |
5 | + "project": { | |
6 | + "shadows": true, | |
7 | + "vr": false | |
8 | + }, | |
9 | + "camera": { | |
10 | + "metadata": { | |
11 | + "version": 4.5, | |
12 | + "type": "Object", | |
13 | + "generator": "Object3D.toJSON" | |
14 | + }, | |
15 | + "object": { | |
16 | + "uuid": "0C0DD0AD-3A7F-4ECD-A9FE-CECD97D5CBD9", | |
17 | + "type": "PerspectiveCamera", | |
18 | + "name": "Camera", | |
19 | + "layers": 1, | |
20 | + "matrix": [0.939236,0,-0.343272,0,-0.147782,0.902586,-0.404351,0,0.309832,0.430511,0.847741,0,11.713146,19.228675,40.388679,1], | |
21 | + "fov": 50, | |
22 | + "zoom": 1, | |
23 | + "near": 0.1, | |
24 | + "far": 100000, | |
25 | + "focus": 10, | |
26 | + "aspect": 1.428977, | |
27 | + "filmGauge": 35, | |
28 | + "filmOffset": 0 | |
29 | + } | |
30 | + }, | |
31 | + "scene": { | |
32 | + "metadata": { | |
33 | + "version": 4.5, | |
34 | + "type": "Object", | |
35 | + "generator": "Object3D.toJSON" | |
36 | + }, | |
37 | + "geometries": [ | |
38 | + { | |
39 | + "uuid": "BBEE74D1-E43D-4C32-A9F3-4656E78C26F3", | |
40 | + "type": "PlaneGeometry", | |
41 | + "width": 30, | |
42 | + "height": 40, | |
43 | + "widthSegments": 1, | |
44 | + "heightSegments": 1 | |
45 | + }, | |
46 | + { | |
47 | + "uuid": "C1722F5F-89AD-45D8-B78C-D1D34AF2A012", | |
48 | + "type": "BoxGeometry", | |
49 | + "width": 2, | |
50 | + "height": 1, | |
51 | + "depth": 1, | |
52 | + "widthSegments": 1, | |
53 | + "heightSegments": 1, | |
54 | + "depthSegments": 1 | |
55 | + }, | |
56 | + { | |
57 | + "uuid": "327EFFCF-649C-4EF3-86D4-B422C5A86E89", | |
58 | + "type": "CylinderGeometry", | |
59 | + "radiusTop": 0.5, | |
60 | + "radiusBottom": 0.5, | |
61 | + "height": 2, | |
62 | + "radialSegments": 32, | |
63 | + "heightSegments": 1, | |
64 | + "openEnded": false | |
65 | + }, | |
66 | + { | |
67 | + "uuid": "0791211B-BB02-4E57-82B5-64C05DE92B39", | |
68 | + "type": "SphereGeometry", | |
69 | + "radius": 0.5, | |
70 | + "widthSegments": 32, | |
71 | + "heightSegments": 16, | |
72 | + "phiStart": 0, | |
73 | + "phiLength": 6.28, | |
74 | + "thetaStart": 0, | |
75 | + "thetaLength": 3.14 | |
76 | + }, | |
77 | + { | |
78 | + "uuid": "73F12A47-9EA7-47FD-BCF3-89B8219B2626", | |
79 | + "type": "BoxGeometry", | |
80 | + "width": 2, | |
81 | + "height": 1, | |
82 | + "depth": 1, | |
83 | + "widthSegments": 1, | |
84 | + "heightSegments": 1, | |
85 | + "depthSegments": 1 | |
86 | + }, | |
87 | + { | |
88 | + "uuid": "3BDEB9FB-BDD4-44AD-8A47-008BED1C8982", | |
89 | + "type": "CylinderGeometry", | |
90 | + "radiusTop": 0.5, | |
91 | + "radiusBottom": 0.5, | |
92 | + "height": 2, | |
93 | + "radialSegments": 32, | |
94 | + "heightSegments": 1, | |
95 | + "openEnded": false | |
96 | + }], | |
97 | + "materials": [ | |
98 | + { | |
99 | + "uuid": "2F69AF3A-DDF5-4BBA-87B5-80159F90DDBF", | |
100 | + "type": "MeshPhongMaterial", | |
101 | + "color": 86015, | |
102 | + "emissive": 0, | |
103 | + "specular": 1118481, | |
104 | + "shininess": 30, | |
105 | + "depthFunc": 3, | |
106 | + "depthTest": true, | |
107 | + "depthWrite": true | |
108 | + }, | |
109 | + { | |
110 | + "uuid": "D98FC4D1-169E-420A-92EA-20E55009A46D", | |
111 | + "type": "MeshBasicMaterial", | |
112 | + "color": 63744, | |
113 | + "depthFunc": 3, | |
114 | + "depthTest": true, | |
115 | + "depthWrite": true, | |
116 | + "wireframe": true | |
117 | + }, | |
118 | + { | |
119 | + "uuid": "3B9DE64D-E1C8-4C24-9F73-3A9E10E3E655", | |
120 | + "type": "MeshPhongMaterial", | |
121 | + "color": 16777215, | |
122 | + "emissive": 0, | |
123 | + "specular": 1118481, | |
124 | + "shininess": 30, | |
125 | + "depthFunc": 3, | |
126 | + "depthTest": true, | |
127 | + "depthWrite": true | |
128 | + }, | |
129 | + { | |
130 | + "uuid": "043B208C-1F83-42C6-802C-E0E35621C27C", | |
131 | + "type": "MeshPhongMaterial", | |
132 | + "color": 16777215, | |
133 | + "emissive": 0, | |
134 | + "specular": 1118481, | |
135 | + "shininess": 30, | |
136 | + "depthFunc": 3, | |
137 | + "depthTest": true, | |
138 | + "depthWrite": true | |
139 | + }, | |
140 | + { | |
141 | + "uuid": "40EC9BDA-91C0-4671-937A-2BCB6DA7EEBB", | |
142 | + "type": "MeshBasicMaterial", | |
143 | + "color": 63744, | |
144 | + "depthFunc": 3, | |
145 | + "depthTest": true, | |
146 | + "depthWrite": true, | |
147 | + "wireframe": true | |
148 | + }], | |
149 | + "object": { | |
150 | + "uuid": "31517222-A9A7-4EAF-B5F6-60751C0BABA3", | |
151 | + "type": "Scene", | |
152 | + "name": "Scene", | |
153 | + "layers": 1, | |
154 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1], | |
155 | + "children": [ | |
156 | + { | |
157 | + "uuid": "EBBB1E63-6318-4752-AE2E-440A4E0B3EF3", | |
158 | + "type": "Mesh", | |
159 | + "name": "Ground", | |
160 | + "layers": 1, | |
161 | + "matrix": [1,0,0,0,0,0.000796,-1,0,0,1,0.000796,0,0,0,0,1], | |
162 | + "geometry": "BBEE74D1-E43D-4C32-A9F3-4656E78C26F3", | |
163 | + "material": "2F69AF3A-DDF5-4BBA-87B5-80159F90DDBF" | |
164 | + }, | |
165 | + { | |
166 | + "uuid": "6EE2E764-43E0-48E0-85F2-E0C8823C20DC", | |
167 | + "type": "DirectionalLight", | |
168 | + "name": "DirectionalLight 1", | |
169 | + "layers": 1, | |
170 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,10,20,15,1], | |
171 | + "color": 16777215, | |
172 | + "intensity": 1, | |
173 | + "shadow": { | |
174 | + "camera": { | |
175 | + "uuid": "3BC010F7-9766-4087-BA04-1D4FD7721ABA", | |
176 | + "type": "OrthographicCamera", | |
177 | + "layers": 1, | |
178 | + "zoom": 1, | |
179 | + "left": -5, | |
180 | + "right": 5, | |
181 | + "top": 5, | |
182 | + "bottom": -5, | |
183 | + "near": 0.5, | |
184 | + "far": 500 | |
185 | + } | |
186 | + } | |
187 | + }, | |
188 | + { | |
189 | + "uuid": "38219749-1E67-45F2-AB15-E64BA0940CAD", | |
190 | + "type": "Mesh", | |
191 | + "name": "Brick", | |
192 | + "layers": 1, | |
193 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0.5,0,1], | |
194 | + "geometry": "C1722F5F-89AD-45D8-B78C-D1D34AF2A012", | |
195 | + "material": "D98FC4D1-169E-420A-92EA-20E55009A46D", | |
196 | + "children": [ | |
197 | + { | |
198 | + "uuid": "711A5955-8F17-4A8B-991A-7604D27E6FA0", | |
199 | + "type": "Mesh", | |
200 | + "name": "Cylinder", | |
201 | + "layers": 1, | |
202 | + "matrix": [0.000795,0.000795,1,0,-1.000001,-0.000001,0.000795,0,0.000001,-1.000001,0.000795,0,0,0,0,1], | |
203 | + "geometry": "327EFFCF-649C-4EF3-86D4-B422C5A86E89", | |
204 | + "material": "3B9DE64D-E1C8-4C24-9F73-3A9E10E3E655" | |
205 | + }] | |
206 | + }, | |
207 | + { | |
208 | + "uuid": "18FFA67C-F893-4E7A-8A76-8D996DEBE0C6", | |
209 | + "type": "Mesh", | |
210 | + "name": "Ball", | |
211 | + "layers": 1, | |
212 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0.5,3.55,1], | |
213 | + "geometry": "0791211B-BB02-4E57-82B5-64C05DE92B39", | |
214 | + "material": "043B208C-1F83-42C6-802C-E0E35621C27C" | |
215 | + }, | |
216 | + { | |
217 | + "uuid": "6D660D49-39B8-40C3-95F6-E4E007AA8D79", | |
218 | + "type": "Mesh", | |
219 | + "name": "Paddle", | |
220 | + "layers": 1, | |
221 | + "matrix": [2,0,0,0,0,1,0,0,0,0,1,0,0,0.5,15.95,1], | |
222 | + "geometry": "73F12A47-9EA7-47FD-BCF3-89B8219B2626", | |
223 | + "material": "40EC9BDA-91C0-4671-937A-2BCB6DA7EEBB", | |
224 | + "children": [ | |
225 | + { | |
226 | + "uuid": "4F5F884C-9E1B-45E6-8F1E-4D538A46D8CB", | |
227 | + "type": "Mesh", | |
228 | + "name": "Cylinder", | |
229 | + "layers": 1, | |
230 | + "matrix": [0.000795,0.000795,1,0,-1.000001,-0.000001,0.000795,0,0.000001,-1.000001,0.000795,0,0,0,0,1], | |
231 | + "geometry": "3BDEB9FB-BDD4-44AD-8A47-008BED1C8982", | |
232 | + "material": "3B9DE64D-E1C8-4C24-9F73-3A9E10E3E655" | |
233 | + }] | |
234 | + }, | |
235 | + { | |
236 | + "uuid": "B0BEAF69-8B5D-4D87-ADCA-FDE83A02762D", | |
237 | + "type": "PointLight", | |
238 | + "name": "PointLight 2", | |
239 | + "layers": 1, | |
240 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-11.65,6.949,-20.682,1], | |
241 | + "color": 16777215, | |
242 | + "intensity": 1, | |
243 | + "distance": 0, | |
244 | + "decay": 1, | |
245 | + "shadow": { | |
246 | + "camera": { | |
247 | + "uuid": "2F0DA21A-EFB8-4E9A-83C5-A601D6113780", | |
248 | + "type": "PerspectiveCamera", | |
249 | + "layers": 1, | |
250 | + "fov": 90, | |
251 | + "zoom": 1, | |
252 | + "near": 0.5, | |
253 | + "far": 500, | |
254 | + "focus": 10, | |
255 | + "aspect": 1, | |
256 | + "filmGauge": 35, | |
257 | + "filmOffset": 0 | |
258 | + } | |
259 | + } | |
260 | + }], | |
261 | + "background": 11184810 | |
262 | + } | |
263 | + }, | |
264 | + "scripts": { | |
265 | + "6D660D49-39B8-40C3-95F6-E4E007AA8D79": [ | |
266 | + { | |
267 | + "name": "User", | |
268 | + "source": "function pointermove( event ) {\n\n\tthis.position.x = ( event.clientX / player.width ) * 30 - 15;\n\n}\n\n// function update( event ) {}" | |
269 | + }], | |
270 | + "31517222-A9A7-4EAF-B5F6-60751C0BABA3": [ | |
271 | + { | |
272 | + "name": "Game Logic", | |
273 | + "source": "var ball = this.getObjectByName( 'Ball' );\n\nvar direction = new THREE.Vector3();\ndirection.x = Math.random() - 0.5;\ndirection.z = - 0.5;\ndirection.normalize();\n\nvar speed = new THREE.Vector3();\n\n//\n\nvar group = new THREE.Group();\nthis.add( group );\n\nvar paddle = this.getObjectByName( 'Paddle' );\npaddle.material.visible = false;\ngroup.add( paddle );\n\nvar brick = this.getObjectByName( 'Brick' );\n\nfor ( var j = 0; j < 8; j ++ ) {\n\n\tvar material = new THREE.MeshPhongMaterial( { color: Math.random() * 0xffffff } );\n\n\tfor ( var i = 0; i < 12; i ++ ) {\n\t\t\n\t\tvar object = brick.clone();\n\t\tobject.position.x = i * 2.2 - 12;\n\t\tobject.position.z = j * 1.4 - 12;\n\t\tgroup.add( object );\n\n\t\tvar cylinder = object.getObjectByName( 'Cylinder' );\n\t\tcylinder.material = material;\n\n\t}\n\t\n}\n\nbrick.visible = false;\nbrick.material.visible = false;\n\n//\n\nvar raycaster = new THREE.Raycaster();\n\nfunction update( event ) {\n\t\n\tif ( ball.position.x < - 15 || ball.position.x > 15 ) direction.x = - direction.x;\n\tif ( ball.position.z < - 20 || ball.position.z > 20 ) direction.z = - direction.z;\n\n\tball.position.x = Math.max( - 15, Math.min( 15, ball.position.x ) );\n\tball.position.z = Math.max( - 20, Math.min( 20, ball.position.z ) );\n\t\t\n\traycaster.set( ball.position, direction );\n\t\n\tvar intersections = raycaster.intersectObjects( group.children );\n\t\n\tif ( intersections.length > 0 ) {\n\t\n\t\tvar intersection = intersections[ 0 ];\n\t\t\n\t\tif ( intersection.distance < 0.5 ) {\n\t\t\t\n\t\t\tif ( intersection.object !== paddle ) {\n\n\t\t\t\tgroup.remove( intersection.object );\n\t\t\t\t\n\t\t\t}\n\t\t\t\n\t\t\tdirection.reflect( intersection.face.normal );\n\t\t\t\n\t\t}\n\t\t\n\t}\n\n\tball.position.add( speed.copy( direction ).multiplyScalar( event.delta / 40 ) );\n\t\n}" | |
274 | + }] | |
275 | + } | |
276 | +} | ... | ... |
editor/examples/camera.app.json
0 → 100644
1 | +{ | |
2 | + "metadata": { | |
3 | + "type": "App" | |
4 | + }, | |
5 | + "project": { | |
6 | + "shadows": true, | |
7 | + "vr": false | |
8 | + }, | |
9 | + "camera": { | |
10 | + "metadata": { | |
11 | + "version": 4.5, | |
12 | + "type": "Object", | |
13 | + "generator": "Object3D.toJSON" | |
14 | + }, | |
15 | + "object": { | |
16 | + "uuid": "60EBAF60-53DA-47B0-A028-8FC031B708F6", | |
17 | + "type": "PerspectiveCamera", | |
18 | + "name": "Camera", | |
19 | + "layers": 1, | |
20 | + "matrix": [0.970041,0,-0.242943,0,-0.048226,0.980099,-0.192562,0,0.238108,0.198509,0.950736,0,1.548,1.29,6.18,1], | |
21 | + "fov": 50, | |
22 | + "zoom": 1, | |
23 | + "near": 0.1, | |
24 | + "far": 100000, | |
25 | + "focus": 10, | |
26 | + "aspect": 1.428977, | |
27 | + "filmGauge": 35, | |
28 | + "filmOffset": 0 | |
29 | + } | |
30 | + }, | |
31 | + "scene": { | |
32 | + "metadata": { | |
33 | + "version": 4.5, | |
34 | + "type": "Object", | |
35 | + "generator": "Object3D.toJSON" | |
36 | + }, | |
37 | + "geometries": [ | |
38 | + { | |
39 | + "uuid": "6D90C4BE-EBA6-4E21-8F54-7CFDAA61F30B", | |
40 | + "type": "PlaneGeometry", | |
41 | + "width": 10, | |
42 | + "height": 10, | |
43 | + "widthSegments": 1, | |
44 | + "heightSegments": 1 | |
45 | + }, | |
46 | + { | |
47 | + "uuid": "D3008B2A-ACDD-43CC-87F7-4F942607D21A", | |
48 | + "type": "BoxGeometry", | |
49 | + "width": 1, | |
50 | + "height": 1, | |
51 | + "depth": 1, | |
52 | + "widthSegments": 1, | |
53 | + "heightSegments": 1, | |
54 | + "depthSegments": 1 | |
55 | + }, | |
56 | + { | |
57 | + "uuid": "F482ACD4-013A-49CF-AE0F-C9FF4ADAE409", | |
58 | + "type": "CylinderGeometry", | |
59 | + "radiusTop": 0, | |
60 | + "radiusBottom": 0.4, | |
61 | + "height": 0.75, | |
62 | + "radialSegments": 4, | |
63 | + "heightSegments": 1, | |
64 | + "openEnded": false | |
65 | + }, | |
66 | + { | |
67 | + "uuid": "51CDDCED-BC71-4B1B-A485-725B6A48204B", | |
68 | + "type": "IcosahedronGeometry", | |
69 | + "radius": 0.4, | |
70 | + "detail": 2 | |
71 | + }], | |
72 | + "materials": [ | |
73 | + { | |
74 | + "uuid": "4AE8130E-B6A8-47BC-ACCF-060973C74044", | |
75 | + "type": "MeshPhongMaterial", | |
76 | + "color": 16777215, | |
77 | + "emissive": 0, | |
78 | + "specular": 1118481, | |
79 | + "shininess": 30, | |
80 | + "depthFunc": 3, | |
81 | + "depthTest": true, | |
82 | + "depthWrite": true | |
83 | + }, | |
84 | + { | |
85 | + "uuid": "B5943856-E404-45D9-A427-4774202C2CD0", | |
86 | + "type": "MeshPhongMaterial", | |
87 | + "color": 37119, | |
88 | + "emissive": 0, | |
89 | + "specular": 1118481, | |
90 | + "shininess": 30, | |
91 | + "depthFunc": 3, | |
92 | + "depthTest": true, | |
93 | + "depthWrite": true | |
94 | + }, | |
95 | + { | |
96 | + "uuid": "3F872310-2067-4BE4-9250-5B3F4E43797E", | |
97 | + "type": "MeshPhongMaterial", | |
98 | + "color": 15859456, | |
99 | + "emissive": 0, | |
100 | + "specular": 1118481, | |
101 | + "shininess": 30, | |
102 | + "depthFunc": 3, | |
103 | + "depthTest": true, | |
104 | + "depthWrite": true | |
105 | + }, | |
106 | + { | |
107 | + "uuid": "E1826901-7922-4584-A25D-6D487E2C9BBD", | |
108 | + "type": "MeshPhongMaterial", | |
109 | + "color": 16711680, | |
110 | + "emissive": 0, | |
111 | + "specular": 1118481, | |
112 | + "shininess": 30, | |
113 | + "depthFunc": 3, | |
114 | + "depthTest": true, | |
115 | + "depthWrite": true | |
116 | + }], | |
117 | + "object": { | |
118 | + "uuid": "3741222A-BD8F-401C-A5D2-5A907E891896", | |
119 | + "type": "Scene", | |
120 | + "name": "Scene", | |
121 | + "layers": 1, | |
122 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1], | |
123 | + "children": [ | |
124 | + { | |
125 | + "uuid": "B7CBBC6F-EC26-49B5-8D0D-67D9C535924B", | |
126 | + "type": "Group", | |
127 | + "name": "Dummy", | |
128 | + "layers": 1, | |
129 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,1,4,1], | |
130 | + "children": [ | |
131 | + { | |
132 | + "uuid": "60B69C58-4201-43FD-815E-AD2EDFBBD0CE", | |
133 | + "type": "PerspectiveCamera", | |
134 | + "name": "PerspectiveCamera", | |
135 | + "layers": 1, | |
136 | + "matrix": [-1,0,0,0,0,1,0,0,0,0,-1,0,0,0,0,1], | |
137 | + "fov": 50, | |
138 | + "zoom": 1, | |
139 | + "near": 0.1, | |
140 | + "far": 100, | |
141 | + "focus": 10, | |
142 | + "aspect": 1, | |
143 | + "filmGauge": 35, | |
144 | + "filmOffset": 0 | |
145 | + }] | |
146 | + }, | |
147 | + { | |
148 | + "uuid": "A460C230-DC88-4A8F-A3FB-AA0FE735F3ED", | |
149 | + "type": "Mesh", | |
150 | + "name": "Plane", | |
151 | + "layers": 1, | |
152 | + "matrix": [1,0,0,0,0,0.040785,-0.999168,0,0,0.999168,0.040785,0,0,-0.5,0,1], | |
153 | + "geometry": "6D90C4BE-EBA6-4E21-8F54-7CFDAA61F30B", | |
154 | + "material": "4AE8130E-B6A8-47BC-ACCF-060973C74044" | |
155 | + }, | |
156 | + { | |
157 | + "uuid": "26DAAD69-725D-43B7-AF9D-990A99DEF8C5", | |
158 | + "type": "Mesh", | |
159 | + "name": "Box", | |
160 | + "layers": 1, | |
161 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1], | |
162 | + "geometry": "D3008B2A-ACDD-43CC-87F7-4F942607D21A", | |
163 | + "material": "B5943856-E404-45D9-A427-4774202C2CD0" | |
164 | + }, | |
165 | + { | |
166 | + "uuid": "AAAFF2D6-4725-4AFC-A9FE-26419B11011F", | |
167 | + "type": "Mesh", | |
168 | + "name": "Cylinder", | |
169 | + "layers": 1, | |
170 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-1.3,-0.15,0,1], | |
171 | + "geometry": "F482ACD4-013A-49CF-AE0F-C9FF4ADAE409", | |
172 | + "material": "3F872310-2067-4BE4-9250-5B3F4E43797E" | |
173 | + }, | |
174 | + { | |
175 | + "uuid": "B855E267-A266-4098-ACD6-6A1FDE7B88BA", | |
176 | + "type": "Mesh", | |
177 | + "name": "Icosahedron", | |
178 | + "layers": 1, | |
179 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,1.3,-0.1,0,1], | |
180 | + "geometry": "51CDDCED-BC71-4B1B-A485-725B6A48204B", | |
181 | + "material": "E1826901-7922-4584-A25D-6D487E2C9BBD" | |
182 | + }, | |
183 | + { | |
184 | + "uuid": "E2939A7B-5E40-438A-8C1B-32126FBC6892", | |
185 | + "type": "PointLight", | |
186 | + "name": "PointLight 1", | |
187 | + "layers": 1, | |
188 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-0.939,1.271,-1.143,1], | |
189 | + "color": 9474221, | |
190 | + "intensity": 0.75, | |
191 | + "distance": 0, | |
192 | + "decay": 1, | |
193 | + "shadow": { | |
194 | + "camera": { | |
195 | + "uuid": "EFF42F46-1E27-4B36-B9D9-CF7D879D258E", | |
196 | + "type": "PerspectiveCamera", | |
197 | + "layers": 1, | |
198 | + "fov": 90, | |
199 | + "zoom": 1, | |
200 | + "near": 0.5, | |
201 | + "far": 500, | |
202 | + "focus": 10, | |
203 | + "aspect": 1, | |
204 | + "filmGauge": 35, | |
205 | + "filmOffset": 0 | |
206 | + } | |
207 | + } | |
208 | + }, | |
209 | + { | |
210 | + "uuid": "3412781E-27CC-43C3-A5DB-54C0C8E42ED6", | |
211 | + "type": "PointLight", | |
212 | + "name": "PointLight 2", | |
213 | + "layers": 1, | |
214 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0.881,0.083,1.254,1], | |
215 | + "color": 12773063, | |
216 | + "intensity": 1, | |
217 | + "distance": 0, | |
218 | + "decay": 1, | |
219 | + "shadow": { | |
220 | + "camera": { | |
221 | + "uuid": "81E800FE-E8A7-4A9E-AFAA-4F04FD56AFE4", | |
222 | + "type": "PerspectiveCamera", | |
223 | + "layers": 1, | |
224 | + "fov": 90, | |
225 | + "zoom": 1, | |
226 | + "near": 0.5, | |
227 | + "far": 500, | |
228 | + "focus": 10, | |
229 | + "aspect": 1, | |
230 | + "filmGauge": 35, | |
231 | + "filmOffset": 0 | |
232 | + } | |
233 | + } | |
234 | + }], | |
235 | + "background": 11184810 | |
236 | + } | |
237 | + }, | |
238 | + "scripts": { | |
239 | + "60B69C58-4201-43FD-815E-AD2EDFBBD0CE": [ | |
240 | + { | |
241 | + "name": "Player Camera", | |
242 | + "source": "player.setCamera( this );" | |
243 | + }], | |
244 | + "B7CBBC6F-EC26-49B5-8D0D-67D9C535924B": [ | |
245 | + { | |
246 | + "name": "Orbit", | |
247 | + "source": "function update( event ) {\n\n\tvar time = event.time * 0.001;\n\n\tthis.position.x = Math.sin( time ) * 4;\n\tthis.position.z = Math.cos( time ) * 4;\n\tthis.lookAt( scene.position );\n\n}" | |
248 | + }] | |
249 | + } | |
250 | +} | ... | ... |
editor/examples/particles.app.json
0 → 100644
1 | +{ | |
2 | + "metadata": { | |
3 | + "type": "App" | |
4 | + }, | |
5 | + "project": { | |
6 | + "shadows": true, | |
7 | + "vr": false | |
8 | + }, | |
9 | + "camera": { | |
10 | + "metadata": { | |
11 | + "version": 4.5, | |
12 | + "type": "Object", | |
13 | + "generator": "Object3D.toJSON" | |
14 | + }, | |
15 | + "object": { | |
16 | + "uuid": "056199EB-6985-481B-97CC-A57FB7C87809", | |
17 | + "type": "PerspectiveCamera", | |
18 | + "name": "Camera", | |
19 | + "layers": 1, | |
20 | + "matrix": [0.707107,0,-0.707107,0,-0.235702,0.942809,-0.235702,0,0.666667,0.333333,0.666667,0,4.182,2.091,4.182,1], | |
21 | + "fov": 50, | |
22 | + "zoom": 1, | |
23 | + "near": 0.1, | |
24 | + "far": 100000, | |
25 | + "focus": 10, | |
26 | + "aspect": 0.666193, | |
27 | + "filmGauge": 35, | |
28 | + "filmOffset": 0 | |
29 | + } | |
30 | + }, | |
31 | + "scene": { | |
32 | + "metadata": { | |
33 | + "version": 4.5, | |
34 | + "type": "Object", | |
35 | + "generator": "Object3D.toJSON" | |
36 | + }, | |
37 | + "geometries": [ | |
38 | + { | |
39 | + "uuid": "C3C0CE7D-10B8-43FC-8F74-011CC6E57800", | |
40 | + "type": "PlaneGeometry", | |
41 | + "width": 100, | |
42 | + "height": 100, | |
43 | + "widthSegments": 1, | |
44 | + "heightSegments": 1 | |
45 | + }], | |
46 | + "materials": [ | |
47 | + { | |
48 | + "uuid": "3A9449D2-62DB-4BB4-ABBD-6F3F9D46DE1A", | |
49 | + "type": "MeshStandardMaterial", | |
50 | + "color": 5465019, | |
51 | + "roughness": 1, | |
52 | + "metalness": 0, | |
53 | + "emissive": 0, | |
54 | + "depthFunc": 3, | |
55 | + "depthTest": true, | |
56 | + "depthWrite": true | |
57 | + }, | |
58 | + { | |
59 | + "uuid": "F5361474-F5F1-412F-8D99-3699B868092D", | |
60 | + "type": "SpriteMaterial", | |
61 | + "color": 16777215, | |
62 | + "transparent": true, | |
63 | + "depthFunc": 3, | |
64 | + "depthTest": true, | |
65 | + "depthWrite": true | |
66 | + }], | |
67 | + "object": { | |
68 | + "uuid": "3741222A-BD8F-401C-A5D2-5A907E891896", | |
69 | + "type": "Scene", | |
70 | + "name": "Scene", | |
71 | + "layers": 1, | |
72 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1], | |
73 | + "children": [ | |
74 | + { | |
75 | + "uuid": "05B57416-1BE5-4A96-BB05-9D9CD112D52B", | |
76 | + "type": "Mesh", | |
77 | + "name": "Ground", | |
78 | + "layers": 1, | |
79 | + "matrix": [1,0,0,0,0,0.000796,-1,0,0,1,0.000796,0,0,-0.5,0,1], | |
80 | + "geometry": "C3C0CE7D-10B8-43FC-8F74-011CC6E57800", | |
81 | + "material": "3A9449D2-62DB-4BB4-ABBD-6F3F9D46DE1A" | |
82 | + }, | |
83 | + { | |
84 | + "uuid": "0A3CB873-07E6-4EEB-830B-68192504111B", | |
85 | + "type": "Sprite", | |
86 | + "name": "Particle", | |
87 | + "layers": 1, | |
88 | + "matrix": [0.04,0,0,0,0,0.04,0,0,0,0,0.04,0,0,0,0,1], | |
89 | + "material": "F5361474-F5F1-412F-8D99-3699B868092D" | |
90 | + }, | |
91 | + { | |
92 | + "uuid": "40E5CDA4-0E39-4265-9293-3E9EC3207F61", | |
93 | + "type": "PointLight", | |
94 | + "name": "PointLight", | |
95 | + "layers": 1, | |
96 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,1.183,0,1], | |
97 | + "color": 16777215, | |
98 | + "intensity": 1, | |
99 | + "distance": 0, | |
100 | + "decay": 1, | |
101 | + "shadow": { | |
102 | + "camera": { | |
103 | + "uuid": "B6D3493E-E5C9-4D65-9E26-BB788D127BE1", | |
104 | + "type": "PerspectiveCamera", | |
105 | + "layers": 1, | |
106 | + "fov": 90, | |
107 | + "zoom": 1, | |
108 | + "near": 0.5, | |
109 | + "far": 500, | |
110 | + "focus": 10, | |
111 | + "aspect": 1, | |
112 | + "filmGauge": 35, | |
113 | + "filmOffset": 0 | |
114 | + } | |
115 | + } | |
116 | + }], | |
117 | + "background": 2171689, | |
118 | + "fog": { | |
119 | + "type": "Fog", | |
120 | + "color": 2171688, | |
121 | + "near": 1, | |
122 | + "far": 50 | |
123 | + } | |
124 | + } | |
125 | + }, | |
126 | + "scripts": { | |
127 | + "3741222A-BD8F-401C-A5D2-5A907E891896": [ | |
128 | + { | |
129 | + "name": "Fountain", | |
130 | + "source": "var original = this.getObjectByName( 'Particle' );\n\nvar particles = [];\n\nfor ( var i = 0; i < 100; i ++ ) {\n\n\tvar particle = original.clone();\n\tparticle.userData.velocity = new THREE.Vector3();\n\tthis.add( particle );\n\n\tparticles.push( particle );\n\n}\n\nfunction update( event ) {\n\t\n\tvar particle = particles.shift();\n\tparticles.push( particle );\n\t\t\n\tvar velocity = particle.userData.velocity;\n\tvelocity.x = Math.random() * 0.1 - 0.05;\n\tvelocity.y = Math.random() * 0.1 + 0.1;\n\tvelocity.z = Math.random() * 0.1 - 0.05;\n\n\tfor ( var i = 0; i < particles.length; i ++ ) {\n\n\t\tvar particle = particles[ i ];\n\n\t\tvar velocity = particle.userData.velocity;\n\n\t\tvelocity.y -= 0.0098;\n\n\t\tparticle.position.add( velocity );\n\n\t\tif ( particle.position.y < 0 ) {\n\n\t\t\tparticle.position.y = 0;\n\n\t\t\tvelocity.y = - velocity.y;\n\t\t\tvelocity.multiplyScalar( 0.6 );\n\n\t\t}\n\n\t}\n\n}" | |
131 | + }] | |
132 | + } | |
133 | +} | ... | ... |
editor/examples/pong.app.json
0 → 100644
1 | +{ | |
2 | + "metadata": { | |
3 | + "type": "App" | |
4 | + }, | |
5 | + "project": { | |
6 | + "shadows": true, | |
7 | + "vr": false | |
8 | + }, | |
9 | + "camera": { | |
10 | + "metadata": { | |
11 | + "version": 4.5, | |
12 | + "type": "Object", | |
13 | + "generator": "Object3D.toJSON" | |
14 | + }, | |
15 | + "object": { | |
16 | + "uuid": "4AC7ADED-CC22-4B16-8218-2E0A0C38C8F8", | |
17 | + "type": "PerspectiveCamera", | |
18 | + "name": "Camera", | |
19 | + "layers": 1, | |
20 | + "matrix": [0.952212,0,-0.305438,0,-0.17743,0.813973,-0.553142,0,0.248618,0.580902,0.775075,0,1.865,4.357,5.813,1], | |
21 | + "fov": 50, | |
22 | + "zoom": 1, | |
23 | + "near": 0.1, | |
24 | + "far": 100000, | |
25 | + "focus": 10, | |
26 | + "aspect": 1.428977, | |
27 | + "filmGauge": 35, | |
28 | + "filmOffset": 0 | |
29 | + } | |
30 | + }, | |
31 | + "scene": { | |
32 | + "metadata": { | |
33 | + "version": 4.5, | |
34 | + "type": "Object", | |
35 | + "generator": "Object3D.toJSON" | |
36 | + }, | |
37 | + "geometries": [ | |
38 | + { | |
39 | + "uuid": "490CEBA3-6A25-4BE1-B517-C5FB11A5D18A", | |
40 | + "type": "PlaneGeometry", | |
41 | + "width": 6, | |
42 | + "height": 4, | |
43 | + "widthSegments": 1, | |
44 | + "heightSegments": 1 | |
45 | + }, | |
46 | + { | |
47 | + "uuid": "D9A92F2D-2F08-4851-99C7-12D8D1CA13C7", | |
48 | + "type": "BoxGeometry", | |
49 | + "width": 0.1, | |
50 | + "height": 0.1, | |
51 | + "depth": 0.1, | |
52 | + "widthSegments": 1, | |
53 | + "heightSegments": 1, | |
54 | + "depthSegments": 1 | |
55 | + }, | |
56 | + { | |
57 | + "uuid": "5E63B8CF-E225-4ABC-994A-4D06BD4E21EB", | |
58 | + "type": "BoxGeometry", | |
59 | + "width": 0.2, | |
60 | + "height": 0.2, | |
61 | + "depth": 1, | |
62 | + "widthSegments": 1, | |
63 | + "heightSegments": 1, | |
64 | + "depthSegments": 1 | |
65 | + }, | |
66 | + { | |
67 | + "uuid": "D61532B4-24C3-4BC4-B56B-7245E8163E09", | |
68 | + "type": "BoxGeometry", | |
69 | + "width": 0.2, | |
70 | + "height": 0.2, | |
71 | + "depth": 1, | |
72 | + "widthSegments": 1, | |
73 | + "heightSegments": 1, | |
74 | + "depthSegments": 1 | |
75 | + }], | |
76 | + "materials": [ | |
77 | + { | |
78 | + "uuid": "7EDF7C08-6325-418A-BBAB-89341C694730", | |
79 | + "type": "MeshPhongMaterial", | |
80 | + "color": 16777215, | |
81 | + "emissive": 0, | |
82 | + "specular": 16777215, | |
83 | + "shininess": 30, | |
84 | + "depthFunc": 3, | |
85 | + "depthTest": true, | |
86 | + "depthWrite": true | |
87 | + }, | |
88 | + { | |
89 | + "uuid": "B1CAF098-FE36-45E1-BEBE-8D6AC04821CC", | |
90 | + "type": "MeshPhongMaterial", | |
91 | + "color": 16711680, | |
92 | + "emissive": 0, | |
93 | + "specular": 1118481, | |
94 | + "shininess": 30, | |
95 | + "depthFunc": 3, | |
96 | + "depthTest": true, | |
97 | + "depthWrite": true | |
98 | + }, | |
99 | + { | |
100 | + "uuid": "FBDBE66D-B613-4741-802D-5AE1DE07DE46", | |
101 | + "type": "MeshPhongMaterial", | |
102 | + "color": 2752767, | |
103 | + "emissive": 0, | |
104 | + "specular": 1118481, | |
105 | + "shininess": 30, | |
106 | + "depthFunc": 3, | |
107 | + "depthTest": true, | |
108 | + "depthWrite": true | |
109 | + }], | |
110 | + "object": { | |
111 | + "uuid": "31517222-A9A7-4EAF-B5F6-60751C0BABA3", | |
112 | + "type": "Scene", | |
113 | + "name": "Scene", | |
114 | + "layers": 1, | |
115 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1], | |
116 | + "children": [ | |
117 | + { | |
118 | + "uuid": "B47D0BFC-D63A-4CBB-985E-9C4DBDF086E4", | |
119 | + "type": "Mesh", | |
120 | + "name": "Ground", | |
121 | + "layers": 1, | |
122 | + "matrix": [1,0,0,0,0,0.000796,-1,0,0,1,0.000796,0,0,-0.1,0,1], | |
123 | + "geometry": "490CEBA3-6A25-4BE1-B517-C5FB11A5D18A", | |
124 | + "material": "7EDF7C08-6325-418A-BBAB-89341C694730" | |
125 | + }, | |
126 | + { | |
127 | + "uuid": "CE13E58A-4E8B-4F72-9E2E-7DE57C58F989", | |
128 | + "type": "Mesh", | |
129 | + "name": "Ball", | |
130 | + "layers": 1, | |
131 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1], | |
132 | + "geometry": "D9A92F2D-2F08-4851-99C7-12D8D1CA13C7", | |
133 | + "material": "B1CAF098-FE36-45E1-BEBE-8D6AC04821CC" | |
134 | + }, | |
135 | + { | |
136 | + "uuid": "2AAEA3AA-EC45-492B-B450-10473D1EC6C5", | |
137 | + "type": "Mesh", | |
138 | + "name": "Pad 1", | |
139 | + "layers": 1, | |
140 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-2.4,0,0,1], | |
141 | + "geometry": "5E63B8CF-E225-4ABC-994A-4D06BD4E21EB", | |
142 | + "material": "FBDBE66D-B613-4741-802D-5AE1DE07DE46" | |
143 | + }, | |
144 | + { | |
145 | + "uuid": "F1DD46A7-6584-4A37-BC76-852C3911077E", | |
146 | + "type": "Mesh", | |
147 | + "name": "Pad 2", | |
148 | + "layers": 1, | |
149 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,2.4,0,0,1], | |
150 | + "geometry": "D61532B4-24C3-4BC4-B56B-7245E8163E09", | |
151 | + "material": "FBDBE66D-B613-4741-802D-5AE1DE07DE46" | |
152 | + }, | |
153 | + { | |
154 | + "uuid": "C62AAE9F-9E51-46A5-BD2B-71BA804FC0B3", | |
155 | + "type": "DirectionalLight", | |
156 | + "name": "DirectionalLight", | |
157 | + "layers": 1, | |
158 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,1,2,1.5,1], | |
159 | + "color": 16777215, | |
160 | + "intensity": 1, | |
161 | + "shadow": { | |
162 | + "camera": { | |
163 | + "uuid": "2CF1F42A-8992-4E8D-8D94-7CC20979344C", | |
164 | + "type": "OrthographicCamera", | |
165 | + "layers": 1, | |
166 | + "zoom": 1, | |
167 | + "left": -5, | |
168 | + "right": 5, | |
169 | + "top": 5, | |
170 | + "bottom": -5, | |
171 | + "near": 0.5, | |
172 | + "far": 500 | |
173 | + } | |
174 | + } | |
175 | + }], | |
176 | + "background": 11184810 | |
177 | + } | |
178 | + }, | |
179 | + "scripts": { | |
180 | + "31517222-A9A7-4EAF-B5F6-60751C0BABA3": [ | |
181 | + { | |
182 | + "name": "Game logic", | |
183 | + "source": "var ball = this.getObjectByName( 'Ball' );\n\nvar position = ball.position;\n\nvar velocity = new THREE.Vector3();\n\nvar direction = new THREE.Vector3();\ndirection.x = Math.random() - 0.5;\ndirection.z = Math.random() - 0.5;\ndirection.normalize().multiplyScalar( 0.1 );\n\nvar pad1 = this.getObjectByName( 'Pad 1' );\nvar pad2 = this.getObjectByName( 'Pad 2' );\n\nvar raycaster = new THREE.Raycaster();\nvar objects = [ pad1, pad2 ];\n\n//\n\nfunction pointermove( event ) {\n\n\tpad1.position.z = ( event.clientX / player.width ) * 3 - 1.5;\n\tpad2.position.z = - pad1.position.z;\n\n}\n\nfunction update( event ) {\n\t\n\tif ( position.x < -3 || position.x > 3 ) direction.x = - direction.x;\n\tif ( position.z < -2 || position.z > 2 ) direction.z = - direction.z;\n\t\n\tposition.x = Math.max( - 3, Math.min( 3, position.x ) );\n\tposition.z = Math.max( - 2, Math.min( 2, position.z ) );\n\t\n\traycaster.set( position, direction );\n\t\n\tvar intersections = raycaster.intersectObjects( objects );\n\t\n\tif ( intersections.length > 0 ) {\n\n\t\tvar intersection = intersections[ 0 ];\n\t\t\n\t\tif ( intersection.distance < 0.1 ) {\n\t\t\t\n\t\t\tdirection.reflect( intersection.face.normal );\n\t\t\t\n\t\t}\n\t\t\n\t}\n\n\tposition.add( velocity.copy( direction ).multiplyScalar( event.delta / 20 ) );\n\n}" | |
184 | + }] | |
185 | + } | |
186 | +} | ... | ... |
editor/examples/shaders.app.json
0 → 100755
1 | +{ | |
2 | + "metadata": { | |
3 | + "type": "App" | |
4 | + }, | |
5 | + "project": { | |
6 | + "shadows": true, | |
7 | + "vr": false | |
8 | + }, | |
9 | + "camera": { | |
10 | + "metadata": { | |
11 | + "version": 4.5, | |
12 | + "type": "Object", | |
13 | + "generator": "Object3D.toJSON" | |
14 | + }, | |
15 | + "object": { | |
16 | + "uuid": "4AC7ADED-CC22-4B16-8218-2E0A0C38C8F8", | |
17 | + "type": "PerspectiveCamera", | |
18 | + "name": "Camera", | |
19 | + "layers": 1, | |
20 | + "matrix": [0.605503,0,-0.795843,0,-0.261526,0.944464,-0.198978,0,0.751645,0.328615,0.571876,0,2.571484,1.124239,1.956469,1], | |
21 | + "fov": 50, | |
22 | + "zoom": 1, | |
23 | + "near": 0.1, | |
24 | + "far": 10000, | |
25 | + "focus": 10, | |
26 | + "aspect": 1.428977, | |
27 | + "filmGauge": 35, | |
28 | + "filmOffset": 0 | |
29 | + } | |
30 | + }, | |
31 | + "scene": { | |
32 | + "metadata": { | |
33 | + "version": 4.5, | |
34 | + "type": "Object", | |
35 | + "generator": "Object3D.toJSON" | |
36 | + }, | |
37 | + "geometries": [ | |
38 | + { | |
39 | + "uuid": "EA781333-F3AE-470D-9110-A9724FCB42AA", | |
40 | + "type": "IcosahedronGeometry", | |
41 | + "radius": 1, | |
42 | + "detail": 24 | |
43 | + }], | |
44 | + "materials": [ | |
45 | + { | |
46 | + "uuid": "50ED51F1-DEA4-4B61-8082-BF41609E8C27", | |
47 | + "type": "ShaderMaterial", | |
48 | + "depthFunc": 3, | |
49 | + "depthTest": true, | |
50 | + "depthWrite": true, | |
51 | + "wireframe": true, | |
52 | + "uniforms": { | |
53 | + "time": { | |
54 | + "value": 0 | |
55 | + } | |
56 | + }, | |
57 | + "vertexShader": "uniform float time;\nvarying vec3 vPosition;\nvoid main() {\n\tvPosition = position;\n\tvPosition.x += sin( time + vPosition.z * 4.0 ) / 4.0;\n\tvPosition.y += cos( time + vPosition.z * 4.0 ) / 4.0;\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( vPosition, 1.0 );\n}", | |
58 | + "fragmentShader": "varying vec3 vPosition;\nvoid main() {\n\tgl_FragColor = vec4( vPosition * 2.0, 1.0 );\n}" | |
59 | + }], | |
60 | + "object": { | |
61 | + "uuid": "5FC9ACA9-2A93-474D-AA32-FACC76551914", | |
62 | + "type": "Scene", | |
63 | + "name": "Scene", | |
64 | + "layers": 1, | |
65 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1], | |
66 | + "children": [ | |
67 | + { | |
68 | + "uuid": "FC7B6CF2-6386-4F47-9CE6-8ADB9FCA6E1F", | |
69 | + "type": "Mesh", | |
70 | + "name": "Icosahedron 1", | |
71 | + "layers": 1, | |
72 | + "matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1], | |
73 | + "geometry": "EA781333-F3AE-470D-9110-A9724FCB42AA", | |
74 | + "material": "50ED51F1-DEA4-4B61-8082-BF41609E8C27" | |
75 | + }], | |
76 | + "background": 11184810 | |
77 | + } | |
78 | + }, | |
79 | + "scripts": { | |
80 | + "FC7B6CF2-6386-4F47-9CE6-8ADB9FCA6E1F": [ | |
81 | + { | |
82 | + "name": "", | |
83 | + "source": "function update( event ) {\n\n\tthis.material.uniforms.time.value = event.time / 500.0;\n\n}" | |
84 | + }] | |
85 | + } | |
86 | +} | ... | ... |
editor/images/icon.png
0 → 100644
7.11 KB
editor/images/icon.xcf
0 → 100644
No preview for this file type
editor/images/rotate.svg
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<svg version="1.1" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"> | |
3 | + <path d="m247.8 474.12c61.315 0 112.09-26.692 147.65-59.495l-1.6947 75.58c-0.17697 10.509 8.2063 19.178 18.721 19.349h0.32318c10.364 0 18.855-8.3198 19.033-18.721l2.5382-126.18c0.0827-5.1592-1.9294-10.135-5.5843-13.784-3.6492-3.6492-8.5987-5.6603-13.784-5.5718l-126.73 2.5383c-10.509 0.18371-18.885 8.8469-18.708 19.362 0.17794 10.401 8.6689 18.714 19.032 18.714h0.32991l83.195-1.7957c-29.35 27.815-72.656 51.93-124.33 51.93-105.91 0-177.05-91.548-177.05-177.05-0.31704-10.248-6.2951-19.251-18.532-19.479-12.237-0.22764-19.196 8.1831-19.543 19.486 0 103.89 86.446 215.13 215.13 215.13zm4.3538-435.25c-61.315 0-112.09 26.692-147.65 59.495l1.6947-75.579c0.17698-10.509-8.2063-19.178-18.721-19.349h-0.32317c-10.364 0-18.855 8.3198-19.033 18.721l-2.5383 126.18c-0.08272 5.1592 1.9294 10.135 5.5844 13.784 3.6492 3.6492 8.5987 5.6603 13.784 5.5719l126.73-2.5383c10.509-0.18371 18.885-8.8469 18.708-19.362-0.17794-10.401-8.6689-18.714-19.032-18.714h-0.32991l-83.195 1.7957c29.35-27.815 72.656-51.93 124.33-51.93 105.91 0 177.05 91.548 177.05 177.05 0.31703 10.248 6.2951 19.251 18.532 19.479 12.237 0.22763 19.196-8.1831 19.543-19.486 0-103.89-86.446-215.13-215.13-215.13z"/> | |
4 | +</svg> | ... | ... |
editor/images/scale.svg
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<svg version="1.1" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"> | |
3 | + <path d="m450.25 44.182-133.13 1.1699c-9.691 0.076-17.482 7.9849-17.406 17.67 0.076 9.638 27.155 17.4 17.535 17.4h0.1289l90.35-0.84375-116.88 116.87c-6.85 6.844-6.85 17.949 0 24.793 3.425 3.425 7.9075 5.1387 12.396 5.1387 4.483 0 8.9734-1.7137 12.398-5.1387l116.87-116.88-0.84179 90.344c-0.07 9.685 7.7193 17.594 17.404 17.67h0.12891c9.62 0 17.459-7.7624 17.535-17.4l1.168-133.13c1.3e-4 -0.01637-2e-3 -0.03246-2e-3 -0.04883 3e-3 -0.51471-0.0239-1.0297-0.0664-1.543-4e-3 -0.05325-9.6e-4 -0.10695-6e-3 -0.16016-2.4e-4 -0.0027-2e-3 -0.0051-2e-3 -0.0078-0.26224-2.8345-1.2132-5.6145-2.8516-8.0742-0.31876-0.47858-0.66235-0.94631-1.0332-1.3984-0.36831-0.45029-0.76079-0.88383-1.1758-1.2988-0.41658-0.41735-0.85218-0.81156-1.3047-1.1816-0.24176-0.19804-0.49656-0.36957-0.7461-0.55273-0.21514-0.15762-0.42293-0.32538-0.64453-0.47266-0.35614-0.23723-0.72494-0.4459-1.0938-0.6543-0.12574-0.07096-0.24567-0.15292-0.37304-0.2207-0.42481-0.22631-0.86059-0.42442-1.2988-0.61328-0.0824-0.03554-0.16117-0.07901-0.24414-0.11328-0.42706-0.17629-0.86181-0.32571-1.2988-0.4668-0.10761-0.03483-0.21201-0.07858-0.32031-0.11133-0.37604-0.11341-0.75724-0.19952-1.1387-0.28711-0.18044-0.04161-0.35715-0.09494-0.53906-0.13086-0.30867-0.06068-0.62071-0.09471-0.93164-0.13867-0.25679-0.03651-0.51062-0.0862-0.76953-0.11133-0.56319-0.0544-1.128-0.08394-1.6934-0.08398zm-387.45 255.62c-9.62 0-17.459 7.7663-17.535 17.404l-1.1699 133.12c-1.22e-4 0.0164 2e-3 0.0325 2e-3 0.0488-0.0025 0.51395 0.02395 1.0285 0.06641 1.541 0.0046 0.0557 0.0026 0.11233 0.0078 0.16797 0.31299 3.3855 1.6059 6.695 3.8809 9.4727 0.3666 0.44761 0.75628 0.88287 1.1738 1.3008h2e-3c0.20731 0.20728 0.42966 0.38925 0.64453 0.58398 0.21795 0.19755 0.42764 0.40626 0.6543 0.5918 0.29789 0.24417 0.61028 0.46004 0.91992 0.68164 0.15919 0.11373 0.31004 0.23952 0.47266 0.34766 0.37454 0.24954 0.76185 0.46983 1.1504 0.6875 0.10897 0.061 0.21208 0.1328 0.32227 0.1914 0.38726 0.20616 0.78506 0.38353 1.1836 0.5586 0.12552 0.0552 0.24628 0.12161 0.37305 0.17382 0.36908 0.15187 0.74642 0.27673 1.123 0.40235 0.16645 0.0556 0.32796 0.12318 0.49609 0.17383 0.46956 0.14111 0.94417 0.25454 1.4219 0.35547 0.07285 0.0154 0.14372 0.0382 0.2168 0.0527 0.4682 0.0927 0.94102 0.15666 1.4141 0.21094 0.09821 0.0113 0.19446 0.0313 0.29297 0.041 0.57076 0.0558 1.6783-0.48578 1.7168 0.0859h0.13477l133.13-1.1699c9.685-0.07 17.476-7.9831 17.4-17.662-0.076-9.69-8.1259-17.387-17.67-17.404l-90.338 0.83996 116.87-116.87c6.844-6.844 6.85-17.943 0-24.793s-17.955-6.85-24.799 0l-116.87 116.86 0.84375-90.344c0.076-9.679-7.7154-17.586-17.4-17.662z"/> | |
4 | +</svg> | ... | ... |
editor/images/translate.svg
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<svg version="1.1" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"> | |
3 | + <path d="m256 0c-1.2452 0-2.4586 0.12791-3.6387 0.35156-0.0659 0.012567-0.13349 0.018008-0.19922 0.03125-0.57894 0.11595-1.146 0.26357-1.7051 0.42969-0.047 0.01399-0.0957 0.022767-0.14258 0.037109-1.8151 0.55436-3.5159 1.3682-5.0625 2.3965-0.0312 0.020703-0.0646 0.037706-0.0957 0.058594-0.32576 0.21931-0.63201 0.46307-0.94336 0.70117-0.70605 0.53787-1.3906 1.1059-2.0234 1.7422l-75.18 75.828c-7.563 7.628-7.5048 19.937 0.11718 27.5 7.628 7.557 19.941 7.5048 27.498-0.11719l41.932-42.291v169.89h-169.88l42.295-41.926c7.622-7.563 7.6782-19.87 0.11523-27.498s-19.876-7.6802-27.498-0.11719l-75.828 75.18c-0.00297 3e-3 -0.00485 7e-3 -0.00781 0.01-2.2463 2.2302-3.9501 5.0041-4.8984 8.1094-0.004708 0.0154-0.007049 0.0315-0.011719 0.0469-0.17839 0.58935-0.3326 1.1892-0.45508 1.8008-0.013248 0.0657-0.018677 0.13332-0.03125 0.19922-0.10655 0.56223-0.19883 1.1288-0.25586 1.707-0.062671 0.63522-0.095706 1.2794-0.095706 1.9314s0.033035 1.2962 0.095703 1.9316c0.057025 0.57824 0.14931 1.1448 0.25586 1.707 0.012572 0.0659 0.018003 0.13349 0.03125 0.19922 0.11595 0.57894 0.26357 1.146 0.42969 1.7051 0.014761 0.0496 0.023909 0.10093 0.039062 0.15039 0.56503 1.8473 1.396 3.5783 2.4512 5.1465 0.35511 0.52775 0.73293 1.0381 1.1367 1.5274 0.00265 3e-3 0.00516 7e-3 0.00781 0.01 0.067158 0.0815 0.14629 0.15391 0.21484 0.23438 0.33891 0.39681 0.68535 0.78801 1.0547 1.1562 0.01376 0.0137 0.025257 0.0293 0.039063 0.043l75.826 75.18c3.791 3.753 8.7375 5.6328 13.689 5.6328 5.003 0 10.007-1.9189 13.805-5.7559 7.563-7.628 7.5048-19.935-0.11719-27.498l-42.289-41.926h169.89v169.89l-41.926-42.289c-7.557-7.635-19.87-7.667-27.498-0.12305-7.622 7.563-7.6802 19.872-0.11719 27.5l75.18 75.826c3e-3 3e-3 7e-3 5e-3 0.01 8e-3 1.3458 1.3556 2.8885 2.5158 4.584 3.4297 0.0637 0.0343 0.13128 0.0602 0.19532 0.0937 0.50006 0.26239 1.0086 0.50958 1.5332 0.72852 0.0287 0.012 0.0591 0.0194 0.0879 0.0312 0.55148 0.22706 1.1139 0.43292 1.6894 0.60937 0.13059 0.0401 0.26501 0.0662 0.39649 0.10352 0.48459 0.13741 0.97149 0.26916 1.4707 0.36914 0.0657 0.0132 0.13332 0.0187 0.19922 0.0312 1.1798 0.22361 2.3932 0.35152 3.6384 0.35152 1.8194 0 3.572-0.26909 5.2422-0.73633 0.15543-0.0435 0.31454-0.0738 0.46875-0.12109 1.1011-0.33792 2.1539-0.78025 3.1621-1.2988 0.11472-0.0588 0.23407-0.10685 0.34765-0.16797 0.37259-0.20111 0.72848-0.42812 1.0859-0.65234 0.1868-0.11676 0.38143-0.22052 0.56445-0.34375 0.10467-0.0707 0.19953-0.15388 0.30274-0.22657 0.93571-0.65673 1.8271-1.3857 2.6445-2.209l75.18-75.826c7.556-7.628 7.503-19.937-0.125-27.5-7.635-7.557-19.941-7.505-27.498 0.12305l-41.932 42.299v-169.9h169.89l-42.293 41.926c-7.628 7.563-7.68 19.87-0.12305 27.498 3.804 3.837 8.8076 5.7559 13.811 5.7559 4.945 0 9.8984-1.8808 13.689-5.6328l75.826-75.18c0.0141-0.014 0.0269-0.0289 0.041-0.043 0.2515-0.2508 0.48121-0.52292 0.71875-0.78711 0.18447-0.20534 0.38168-0.40105 0.55664-0.61328 0.404-0.48955 0.78339-0.9993 1.1387-1.5274 0.0249-0.0371 0.0437-0.0779 0.0684-0.11523 0.32713-0.4935 0.63802-0.99972 0.91992-1.5234 0.0324-0.0604 0.0561-0.12484 0.0879-0.18555 0.26306-0.50133 0.51109-1.0112 0.73047-1.5371 0.0125-0.0301 0.0208-0.0617 0.0332-0.0918 0.22497-0.54707 0.42834-1.105 0.60352-1.6758 0.0473-0.15419 0.0776-0.31334 0.12109-0.46875 0.13019-0.46537 0.25974-0.93139 0.35547-1.4102 0.0128-0.0637 0.0171-0.12948 0.0293-0.19336 0.22362-1.1801 0.35153-2.3934 0.35153-3.6386 0-1.3125-0.13302-2.5925-0.38086-3.832-4e-4 -2e-3 -2e-3 -4e-3 -2e-3 -6e-3 -0.0955-0.47674-0.22387-0.94085-0.35352-1.4043-0.0435-0.15541-0.0738-0.31456-0.12109-0.46875-0.18478-0.60207-0.39663-1.192-0.63672-1.7676-5.6e-4 -1e-3 -1e-3 -3e-3 -2e-3 -4e-3 -0.24542-0.58801-0.51657-1.1617-0.81641-1.7188-2e-3 -3e-3 -4e-3 -6e-3 -6e-3 -0.01-0.30165-0.55963-0.62921-1.1039-0.98242-1.6289-8e-4 -1e-3 -1e-3 -3e-3 -2e-3 -4e-3 -0.35459-0.52676-0.73364-1.035-1.1367-1.5234-0.17496-0.21223-0.37217-0.40794-0.55664-0.61328-0.23754-0.26419-0.46725-0.53631-0.71875-0.78711-0.0141-0.014-0.0269-0.029-0.041-0.043l-75.826-75.18c-7.635-7.563-19.943-7.5048-27.5 0.11718-7.557 7.628-7.505 19.935 0.12305 27.498l42.295 41.932h-169.89v-169.89l41.926 42.289c3.804 3.837 8.8076 5.7559 13.811 5.7559 4.945 0 9.8955-1.8738 13.688-5.6328 7.628-7.563 7.68-19.872 0.12304-27.5l-75.18-75.826c-0.0137-0.013805-0.0292-0.025303-0.043-0.039063-0.44175-0.44306-0.90413-0.86662-1.3867-1.2656-5e-3 -0.00382-9e-3 -0.0079-0.0137-0.011719-3.3608-2.7735-7.6688-4.4395-12.367-4.4395z"/> | |
4 | +</svg> | ... | ... |
editor/index.html
0 → 100644
1 | +<!DOCTYPE html> | |
2 | +<html lang="en"> | |
3 | + <head> | |
4 | + <title>ThingsKit 3D Model Editor</title> | |
5 | + <meta charset="utf-8" /> | |
6 | + <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" /> | |
7 | + </head> | |
8 | + | |
9 | + <body> | |
10 | + <link rel="stylesheet" href="css/main.css" /> | |
11 | + <link rel="stylesheet" href="./js/libs/spin/spin.css"> | |
12 | + <link rel="stylesheet" href="js/libs/codemirror/codemirror.css" /> | |
13 | + <link rel="stylesheet" href="js/libs/codemirror/theme/monokai.css" /> | |
14 | + <link rel="stylesheet" href="js/libs/codemirror/addon/dialog.css" /> | |
15 | + <link rel="stylesheet" href="js/libs/codemirror/addon/show-hint.css" /> | |
16 | + <link rel="stylesheet" href="js/libs/codemirror/addon/tern.css" /> | |
17 | + <script type="module" src="./jsm/libs/draco/draco_encoder.js"></script> | |
18 | + <script type="module" src="./js/libs/codemirror/codemirror.js"></script> | |
19 | + <script type="module" src="./js/libs/codemirror/mode/javascript.js"></script> | |
20 | + <script type="module" src="./js/libs/codemirror/mode/glsl.js"></script> | |
21 | + <script type="module" src="./js/libs/esprima.js"></script> | |
22 | + <script type="module" src="./js/libs/jsonlint.js"></script> | |
23 | + <script type="module" src="./js/libs/ffmpeg/ffmpeg.min.js"></script> | |
24 | + <script type="module" src="./js/libs/codemirror/addon/dialog.js"></script> | |
25 | + <script type="module" src="./js/libs/codemirror/addon/show-hint.js"></script> | |
26 | + <script type="module" src="./js/libs/codemirror/addon/tern.js"></script> | |
27 | + <script type="module" src="./js/libs/acorn/acorn.js"></script> | |
28 | + <script type="module" src="./js/libs/acorn/acorn_loose.js"></script> | |
29 | + <script type="module" src="./js/libs/acorn/walk.js"></script> | |
30 | + <script type="module" src="./js/libs/ternjs/polyfill.js"></script> | |
31 | + <script type="module" src="./js/libs/ternjs/signal.js"></script> | |
32 | + <script type="module" src="./js/libs/ternjs/tern.js"></script> | |
33 | + <script type="module" src="./js/libs/ternjs/def.js"></script> | |
34 | + <script type="module" src="./js/libs/ternjs/comment.js"></script> | |
35 | + <script type="module" src="./js/libs/ternjs/infer.js"></script> | |
36 | + <script type="module" src="./js/libs/ternjs/doc_comment.js"></script> | |
37 | + <script type="module" src="./js/libs/tern-threejs/threejs.js"></script> | |
38 | + <script type="module" src="./js/libs/signals.min.js"></script> | |
39 | + <script type="module" src="./main.js"></script> | |
40 | + </body> | |
41 | +</html> | ... | ... |
editor/js/Command.js
0 → 100644
1 | +/** | |
2 | + * @param editor pointer to main editor object used to initialize | |
3 | + * each command object with a reference to the editor | |
4 | + * @constructor | |
5 | + */ | |
6 | + | |
7 | +class Command { | |
8 | + | |
9 | + constructor( editor ) { | |
10 | + | |
11 | + this.id = - 1; | |
12 | + this.inMemory = false; | |
13 | + this.updatable = false; | |
14 | + this.type = ''; | |
15 | + this.name = ''; | |
16 | + this.editor = editor; | |
17 | + | |
18 | + } | |
19 | + | |
20 | + toJSON() { | |
21 | + | |
22 | + const output = {}; | |
23 | + output.type = this.type; | |
24 | + output.id = this.id; | |
25 | + output.name = this.name; | |
26 | + return output; | |
27 | + | |
28 | + } | |
29 | + | |
30 | + fromJSON( json ) { | |
31 | + | |
32 | + this.inMemory = true; | |
33 | + this.type = json.type; | |
34 | + this.id = json.id; | |
35 | + this.name = json.name; | |
36 | + | |
37 | + } | |
38 | + | |
39 | +} | |
40 | + | |
41 | +export { Command }; | ... | ... |
editor/js/Config.js
0 → 100644
1 | +function Config() { | |
2 | + | |
3 | + const name = 'threejs-editor'; | |
4 | + | |
5 | + const userLanguage = navigator.language.split( '-' )[ 0 ]; | |
6 | + | |
7 | + const suggestedLanguage = [ 'fr', 'ja', 'zh', 'ko' ].includes( userLanguage ) ? userLanguage : 'en'; | |
8 | + | |
9 | + const storage = { | |
10 | + 'language': suggestedLanguage, | |
11 | + | |
12 | + 'autosave': true, | |
13 | + | |
14 | + 'project/title': '', | |
15 | + 'project/editable': false, | |
16 | + 'project/vr': false, | |
17 | + | |
18 | + 'project/renderer/antialias': true, | |
19 | + 'project/renderer/shadows': true, | |
20 | + 'project/renderer/shadowType': 1, // PCF | |
21 | + 'project/renderer/toneMapping': 0, // NoToneMapping | |
22 | + 'project/renderer/toneMappingExposure': 1, | |
23 | + | |
24 | + 'settings/history': false, | |
25 | + | |
26 | + 'settings/shortcuts/translate': 'w', | |
27 | + 'settings/shortcuts/rotate': 'e', | |
28 | + 'settings/shortcuts/scale': 'r', | |
29 | + 'settings/shortcuts/undo': 'z', | |
30 | + 'settings/shortcuts/focus': 'f' | |
31 | + }; | |
32 | + | |
33 | + if ( window.localStorage[ name ] === undefined ) { | |
34 | + | |
35 | + window.localStorage[ name ] = JSON.stringify( storage ); | |
36 | + | |
37 | + } else { | |
38 | + | |
39 | + const data = JSON.parse( window.localStorage[ name ] ); | |
40 | + | |
41 | + for ( const key in data ) { | |
42 | + | |
43 | + storage[ key ] = data[ key ]; | |
44 | + | |
45 | + } | |
46 | + | |
47 | + } | |
48 | + | |
49 | + return { | |
50 | + | |
51 | + getKey: function ( key ) { | |
52 | + | |
53 | + return storage[ key ]; | |
54 | + | |
55 | + }, | |
56 | + | |
57 | + setKey: function () { // key, value, key, value ... | |
58 | + | |
59 | + for ( let i = 0, l = arguments.length; i < l; i += 2 ) { | |
60 | + | |
61 | + storage[ arguments[ i ] ] = arguments[ i + 1 ]; | |
62 | + | |
63 | + } | |
64 | + | |
65 | + window.localStorage[ name ] = JSON.stringify( storage ); | |
66 | + | |
67 | + console.log( '[' + /\d\d\:\d\d\:\d\d/.exec( new Date() )[ 0 ] + ']', 'Saved config to LocalStorage.' ); | |
68 | + | |
69 | + }, | |
70 | + | |
71 | + clear: function () { | |
72 | + | |
73 | + delete window.localStorage[ name ]; | |
74 | + | |
75 | + } | |
76 | + | |
77 | + }; | |
78 | + | |
79 | +} | |
80 | + | |
81 | +export { Config }; | ... | ... |
editor/js/Editor.js
0 → 100644
1 | +import * as THREE from 'three'; | |
2 | + | |
3 | +import { Config } from './Config.js'; | |
4 | +import { Loader } from './Loader.js'; | |
5 | +import { History as _History } from './History.js'; | |
6 | +import { Strings } from './Strings.js'; | |
7 | +import { Storage as _Storage } from './Storage.js'; | |
8 | +import { Selector } from './Selector.js'; | |
9 | + | |
10 | +var _DEFAULT_CAMERA = new THREE.PerspectiveCamera(50, 1, 0.01, 1000); | |
11 | +_DEFAULT_CAMERA.name = 'Camera'; | |
12 | +_DEFAULT_CAMERA.position.set(0, 5, 10); | |
13 | +_DEFAULT_CAMERA.lookAt(new THREE.Vector3()); | |
14 | + | |
15 | +function Editor() { | |
16 | + | |
17 | + const Signal = signals.Signal; // eslint-disable-line no-undef | |
18 | + | |
19 | + this.signals = { | |
20 | + | |
21 | + // script | |
22 | + | |
23 | + editScript: new Signal(), | |
24 | + | |
25 | + // player | |
26 | + | |
27 | + startPlayer: new Signal(), | |
28 | + stopPlayer: new Signal(), | |
29 | + | |
30 | + // xr | |
31 | + | |
32 | + enterXR: new Signal(), | |
33 | + offerXR: new Signal(), | |
34 | + leaveXR: new Signal(), | |
35 | + | |
36 | + // notifications | |
37 | + | |
38 | + editorCleared: new Signal(), | |
39 | + | |
40 | + savingStarted: new Signal(), | |
41 | + savingFinished: new Signal(), | |
42 | + | |
43 | + transformModeChanged: new Signal(), | |
44 | + snapChanged: new Signal(), | |
45 | + spaceChanged: new Signal(), | |
46 | + rendererCreated: new Signal(), | |
47 | + rendererUpdated: new Signal(), | |
48 | + rendererDetectKTX2Support: new Signal(), | |
49 | + | |
50 | + sceneBackgroundChanged: new Signal(), | |
51 | + sceneEnvironmentChanged: new Signal(), | |
52 | + sceneFogChanged: new Signal(), | |
53 | + sceneFogSettingsChanged: new Signal(), | |
54 | + sceneGraphChanged: new Signal(), | |
55 | + sceneRendered: new Signal(), | |
56 | + | |
57 | + cameraChanged: new Signal(), | |
58 | + cameraResetted: new Signal(), | |
59 | + | |
60 | + geometryChanged: new Signal(), | |
61 | + | |
62 | + objectSelected: new Signal(), | |
63 | + objectFocused: new Signal(), | |
64 | + | |
65 | + objectAdded: new Signal(), | |
66 | + objectChanged: new Signal(), | |
67 | + objectRemoved: new Signal(), | |
68 | + | |
69 | + cameraAdded: new Signal(), | |
70 | + cameraRemoved: new Signal(), | |
71 | + | |
72 | + helperAdded: new Signal(), | |
73 | + helperRemoved: new Signal(), | |
74 | + | |
75 | + materialAdded: new Signal(), | |
76 | + materialChanged: new Signal(), | |
77 | + materialRemoved: new Signal(), | |
78 | + | |
79 | + scriptAdded: new Signal(), | |
80 | + scriptChanged: new Signal(), | |
81 | + scriptRemoved: new Signal(), | |
82 | + | |
83 | + windowResize: new Signal(), | |
84 | + | |
85 | + showHelpersChanged: new Signal(), | |
86 | + refreshSidebarObject3D: new Signal(), | |
87 | + refreshSidebarEnvironment: new Signal(), | |
88 | + historyChanged: new Signal(), | |
89 | + | |
90 | + viewportCameraChanged: new Signal(), | |
91 | + viewportShadingChanged: new Signal(), | |
92 | + | |
93 | + intersectionsDetected: new Signal(), | |
94 | + | |
95 | + pathTracerUpdated: new Signal(), | |
96 | + | |
97 | + }; | |
98 | + | |
99 | + this.config = new Config(); | |
100 | + this.history = new _History(this); | |
101 | + this.selector = new Selector(this); | |
102 | + this.storage = new _Storage(); | |
103 | + this.strings = new Strings(this.config); | |
104 | + | |
105 | + this.loader = new Loader(this); | |
106 | + | |
107 | + this.camera = _DEFAULT_CAMERA.clone(); | |
108 | + | |
109 | + this.scene = new THREE.Scene(); | |
110 | + this.scene.name = 'Scene'; | |
111 | + | |
112 | + this.sceneHelpers = new THREE.Scene(); | |
113 | + this.sceneHelpers.add(new THREE.HemisphereLight(0xffffff, 0x888888, 2)); | |
114 | + | |
115 | + this.object = {}; | |
116 | + this.geometries = {}; | |
117 | + this.materials = {}; | |
118 | + this.textures = {}; | |
119 | + this.scripts = {}; | |
120 | + | |
121 | + this.materialsRefCounter = new Map(); // tracks how often is a material used by a 3D object | |
122 | + | |
123 | + this.mixer = new THREE.AnimationMixer(this.scene); | |
124 | + | |
125 | + this.selected = null; | |
126 | + this.helpers = {}; | |
127 | + | |
128 | + this.cameras = {}; | |
129 | + | |
130 | + this.viewportCamera = this.camera; | |
131 | + this.viewportShading = 'default'; | |
132 | + | |
133 | + this.addCamera(this.camera); | |
134 | + | |
135 | +} | |
136 | + | |
137 | +Editor.prototype = { | |
138 | + | |
139 | + setScene: function (scene) { | |
140 | + | |
141 | + this.scene.uuid = scene.uuid; | |
142 | + this.scene.name = scene.name; | |
143 | + | |
144 | + this.scene.background = scene.background; | |
145 | + this.scene.environment = scene.environment; | |
146 | + this.scene.fog = scene.fog; | |
147 | + this.scene.backgroundBlurriness = scene.backgroundBlurriness; | |
148 | + this.scene.backgroundIntensity = scene.backgroundIntensity; | |
149 | + | |
150 | + this.scene.userData = JSON.parse(JSON.stringify(scene.userData)); | |
151 | + | |
152 | + // avoid render per object | |
153 | + | |
154 | + this.signals.sceneGraphChanged.active = false; | |
155 | + | |
156 | + while (scene.children.length > 0) { | |
157 | + | |
158 | + this.addObject(scene.children[0]); | |
159 | + | |
160 | + } | |
161 | + | |
162 | + this.signals.sceneGraphChanged.active = true; | |
163 | + this.signals.sceneGraphChanged.dispatch(); | |
164 | + | |
165 | + }, | |
166 | + | |
167 | + // | |
168 | + | |
169 | + addObject: function (object, parent, index) { | |
170 | + | |
171 | + var scope = this; | |
172 | + | |
173 | + object.traverse(function (child) { | |
174 | + | |
175 | + if (child.geometry !== undefined) scope.addGeometry(child.geometry); | |
176 | + if (child.material !== undefined) scope.addMaterial(child.material); | |
177 | + | |
178 | + scope.addCamera(child); | |
179 | + scope.addHelper(child); | |
180 | + | |
181 | + }); | |
182 | + | |
183 | + if (parent === undefined) { | |
184 | + | |
185 | + this.scene.add(object); | |
186 | + | |
187 | + } else { | |
188 | + | |
189 | + parent.children.splice(index, 0, object); | |
190 | + object.parent = parent; | |
191 | + | |
192 | + } | |
193 | + | |
194 | + this.signals.objectAdded.dispatch(object); | |
195 | + this.signals.sceneGraphChanged.dispatch(); | |
196 | + | |
197 | + }, | |
198 | + | |
199 | + nameObject: function (object, name) { | |
200 | + | |
201 | + object.name = name; | |
202 | + this.signals.sceneGraphChanged.dispatch(); | |
203 | + | |
204 | + }, | |
205 | + | |
206 | + removeObject: function (object) { | |
207 | + | |
208 | + if (object.parent === null) return; // avoid deleting the camera or scene | |
209 | + | |
210 | + var scope = this; | |
211 | + | |
212 | + object.traverse(function (child) { | |
213 | + | |
214 | + scope.removeCamera(child); | |
215 | + scope.removeHelper(child); | |
216 | + | |
217 | + if (child.material !== undefined) scope.removeMaterial(child.material); | |
218 | + | |
219 | + }); | |
220 | + | |
221 | + object.parent.remove(object); | |
222 | + | |
223 | + this.signals.objectRemoved.dispatch(object); | |
224 | + this.signals.sceneGraphChanged.dispatch(); | |
225 | + | |
226 | + }, | |
227 | + | |
228 | + addGeometry: function (geometry) { | |
229 | + | |
230 | + this.geometries[geometry.uuid] = geometry; | |
231 | + | |
232 | + }, | |
233 | + | |
234 | + setGeometryName: function (geometry, name) { | |
235 | + | |
236 | + geometry.name = name; | |
237 | + this.signals.sceneGraphChanged.dispatch(); | |
238 | + | |
239 | + }, | |
240 | + | |
241 | + addMaterial: function (material) { | |
242 | + | |
243 | + if (Array.isArray(material)) { | |
244 | + | |
245 | + for (var i = 0, l = material.length; i < l; i++) { | |
246 | + | |
247 | + this.addMaterialToRefCounter(material[i]); | |
248 | + | |
249 | + } | |
250 | + | |
251 | + } else { | |
252 | + | |
253 | + this.addMaterialToRefCounter(material); | |
254 | + | |
255 | + } | |
256 | + | |
257 | + this.signals.materialAdded.dispatch(); | |
258 | + | |
259 | + }, | |
260 | + | |
261 | + addMaterialToRefCounter: function (material) { | |
262 | + | |
263 | + var materialsRefCounter = this.materialsRefCounter; | |
264 | + | |
265 | + var count = materialsRefCounter.get(material); | |
266 | + | |
267 | + if (count === undefined) { | |
268 | + | |
269 | + materialsRefCounter.set(material, 1); | |
270 | + this.materials[material.uuid] = material; | |
271 | + | |
272 | + } else { | |
273 | + | |
274 | + count++; | |
275 | + materialsRefCounter.set(material, count); | |
276 | + | |
277 | + } | |
278 | + | |
279 | + }, | |
280 | + | |
281 | + removeMaterial: function (material) { | |
282 | + | |
283 | + if (Array.isArray(material)) { | |
284 | + | |
285 | + for (var i = 0, l = material.length; i < l; i++) { | |
286 | + | |
287 | + this.removeMaterialFromRefCounter(material[i]); | |
288 | + | |
289 | + } | |
290 | + | |
291 | + } else { | |
292 | + | |
293 | + this.removeMaterialFromRefCounter(material); | |
294 | + | |
295 | + } | |
296 | + | |
297 | + this.signals.materialRemoved.dispatch(); | |
298 | + | |
299 | + }, | |
300 | + | |
301 | + removeMaterialFromRefCounter: function (material) { | |
302 | + | |
303 | + var materialsRefCounter = this.materialsRefCounter; | |
304 | + | |
305 | + var count = materialsRefCounter.get(material); | |
306 | + count--; | |
307 | + | |
308 | + if (count === 0) { | |
309 | + | |
310 | + materialsRefCounter.delete(material); | |
311 | + delete this.materials[material.uuid]; | |
312 | + | |
313 | + } else { | |
314 | + | |
315 | + materialsRefCounter.set(material, count); | |
316 | + | |
317 | + } | |
318 | + | |
319 | + }, | |
320 | + | |
321 | + getMaterialById: function (id) { | |
322 | + | |
323 | + var material; | |
324 | + var materials = Object.values(this.materials); | |
325 | + | |
326 | + for (var i = 0; i < materials.length; i++) { | |
327 | + | |
328 | + if (materials[i].id === id) { | |
329 | + | |
330 | + material = materials[i]; | |
331 | + break; | |
332 | + | |
333 | + } | |
334 | + | |
335 | + } | |
336 | + | |
337 | + return material; | |
338 | + | |
339 | + }, | |
340 | + | |
341 | + setMaterialName: function (material, name) { | |
342 | + | |
343 | + material.name = name; | |
344 | + this.signals.sceneGraphChanged.dispatch(); | |
345 | + | |
346 | + }, | |
347 | + | |
348 | + addTexture: function (texture) { | |
349 | + | |
350 | + this.textures[texture.uuid] = texture; | |
351 | + | |
352 | + }, | |
353 | + | |
354 | + // | |
355 | + | |
356 | + addCamera: function (camera) { | |
357 | + | |
358 | + if (camera.isCamera) { | |
359 | + | |
360 | + this.cameras[camera.uuid] = camera; | |
361 | + | |
362 | + this.signals.cameraAdded.dispatch(camera); | |
363 | + | |
364 | + } | |
365 | + | |
366 | + }, | |
367 | + | |
368 | + removeCamera: function (camera) { | |
369 | + | |
370 | + if (this.cameras[camera.uuid] !== undefined) { | |
371 | + | |
372 | + delete this.cameras[camera.uuid]; | |
373 | + | |
374 | + this.signals.cameraRemoved.dispatch(camera); | |
375 | + | |
376 | + } | |
377 | + | |
378 | + }, | |
379 | + | |
380 | + // | |
381 | + | |
382 | + addHelper: function () { | |
383 | + | |
384 | + var geometry = new THREE.SphereGeometry(2, 4, 2); | |
385 | + var material = new THREE.MeshBasicMaterial({ color: 0xff0000, visible: false }); | |
386 | + | |
387 | + return function (object, helper) { | |
388 | + | |
389 | + if (helper === undefined) { | |
390 | + | |
391 | + if (object.isCamera) { | |
392 | + | |
393 | + helper = new THREE.CameraHelper(object); | |
394 | + | |
395 | + } else if (object.isPointLight) { | |
396 | + | |
397 | + helper = new THREE.PointLightHelper(object, 1); | |
398 | + | |
399 | + } else if (object.isDirectionalLight) { | |
400 | + | |
401 | + helper = new THREE.DirectionalLightHelper(object, 1); | |
402 | + | |
403 | + } else if (object.isSpotLight) { | |
404 | + | |
405 | + helper = new THREE.SpotLightHelper(object); | |
406 | + | |
407 | + } else if (object.isHemisphereLight) { | |
408 | + | |
409 | + helper = new THREE.HemisphereLightHelper(object, 1); | |
410 | + | |
411 | + } else if (object.isSkinnedMesh) { | |
412 | + | |
413 | + helper = new THREE.SkeletonHelper(object.skeleton.bones[0]); | |
414 | + | |
415 | + } else if (object.isBone === true && object.parent && object.parent.isBone !== true) { | |
416 | + | |
417 | + helper = new THREE.SkeletonHelper(object); | |
418 | + | |
419 | + } else { | |
420 | + | |
421 | + // no helper for this object type | |
422 | + return; | |
423 | + | |
424 | + } | |
425 | + | |
426 | + const picker = new THREE.Mesh(geometry, material); | |
427 | + picker.name = 'picker'; | |
428 | + picker.userData.object = object; | |
429 | + helper.add(picker); | |
430 | + | |
431 | + } | |
432 | + | |
433 | + this.sceneHelpers.add(helper); | |
434 | + this.helpers[object.id] = helper; | |
435 | + | |
436 | + this.signals.helperAdded.dispatch(helper); | |
437 | + | |
438 | + }; | |
439 | + | |
440 | + }(), | |
441 | + | |
442 | + removeHelper: function (object) { | |
443 | + | |
444 | + if (this.helpers[object.id] !== undefined) { | |
445 | + | |
446 | + var helper = this.helpers[object.id]; | |
447 | + helper.parent.remove(helper); | |
448 | + helper.dispose(); | |
449 | + | |
450 | + delete this.helpers[object.id]; | |
451 | + | |
452 | + this.signals.helperRemoved.dispatch(helper); | |
453 | + | |
454 | + } | |
455 | + | |
456 | + }, | |
457 | + | |
458 | + // | |
459 | + | |
460 | + addScript: function (object, script) { | |
461 | + | |
462 | + if (this.scripts[object.uuid] === undefined) { | |
463 | + | |
464 | + this.scripts[object.uuid] = []; | |
465 | + | |
466 | + } | |
467 | + | |
468 | + this.scripts[object.uuid].push(script); | |
469 | + | |
470 | + this.signals.scriptAdded.dispatch(script); | |
471 | + | |
472 | + }, | |
473 | + | |
474 | + removeScript: function (object, script) { | |
475 | + | |
476 | + if (this.scripts[object.uuid] === undefined) return; | |
477 | + | |
478 | + var index = this.scripts[object.uuid].indexOf(script); | |
479 | + | |
480 | + if (index !== - 1) { | |
481 | + | |
482 | + this.scripts[object.uuid].splice(index, 1); | |
483 | + | |
484 | + } | |
485 | + | |
486 | + this.signals.scriptRemoved.dispatch(script); | |
487 | + | |
488 | + }, | |
489 | + | |
490 | + getObjectMaterial: function (object, slot) { | |
491 | + | |
492 | + var material = object.material; | |
493 | + | |
494 | + if (Array.isArray(material) && slot !== undefined) { | |
495 | + | |
496 | + material = material[slot]; | |
497 | + | |
498 | + } | |
499 | + | |
500 | + return material; | |
501 | + | |
502 | + }, | |
503 | + | |
504 | + setObjectMaterial: function (object, slot, newMaterial) { | |
505 | + | |
506 | + if (Array.isArray(object.material) && slot !== undefined) { | |
507 | + | |
508 | + object.material[slot] = newMaterial; | |
509 | + | |
510 | + } else { | |
511 | + | |
512 | + object.material = newMaterial; | |
513 | + | |
514 | + } | |
515 | + | |
516 | + }, | |
517 | + | |
518 | + setViewportCamera: function (uuid) { | |
519 | + | |
520 | + this.viewportCamera = this.cameras[uuid]; | |
521 | + this.signals.viewportCameraChanged.dispatch(); | |
522 | + | |
523 | + }, | |
524 | + | |
525 | + setViewportShading: function (value) { | |
526 | + | |
527 | + this.viewportShading = value; | |
528 | + this.signals.viewportShadingChanged.dispatch(); | |
529 | + | |
530 | + }, | |
531 | + | |
532 | + // | |
533 | + | |
534 | + select: function (object) { | |
535 | + | |
536 | + this.selector.select(object); | |
537 | + | |
538 | + }, | |
539 | + | |
540 | + selectById: function (id) { | |
541 | + | |
542 | + if (id === this.camera.id) { | |
543 | + | |
544 | + this.select(this.camera); | |
545 | + return; | |
546 | + | |
547 | + } | |
548 | + | |
549 | + this.select(this.scene.getObjectById(id)); | |
550 | + | |
551 | + }, | |
552 | + | |
553 | + selectByUuid: function (uuid) { | |
554 | + | |
555 | + var scope = this; | |
556 | + | |
557 | + this.scene.traverse(function (child) { | |
558 | + | |
559 | + if (child.uuid === uuid) { | |
560 | + | |
561 | + scope.select(child); | |
562 | + | |
563 | + } | |
564 | + | |
565 | + }); | |
566 | + | |
567 | + }, | |
568 | + | |
569 | + deselect: function () { | |
570 | + | |
571 | + this.selector.deselect(); | |
572 | + | |
573 | + }, | |
574 | + | |
575 | + focus: function (object) { | |
576 | + | |
577 | + if (object !== undefined) { | |
578 | + | |
579 | + this.signals.objectFocused.dispatch(object); | |
580 | + | |
581 | + } | |
582 | + | |
583 | + }, | |
584 | + | |
585 | + focusById: function (id) { | |
586 | + | |
587 | + this.focus(this.scene.getObjectById(id)); | |
588 | + | |
589 | + }, | |
590 | + | |
591 | + clear: function () { | |
592 | + | |
593 | + this.history.clear(); | |
594 | + this.storage.clear(); | |
595 | + | |
596 | + this.camera.copy(_DEFAULT_CAMERA); | |
597 | + this.signals.cameraResetted.dispatch(); | |
598 | + | |
599 | + this.scene.name = 'Scene'; | |
600 | + this.scene.userData = {}; | |
601 | + this.scene.background = null; | |
602 | + this.scene.environment = null; | |
603 | + this.scene.fog = null; | |
604 | + | |
605 | + var objects = this.scene.children; | |
606 | + | |
607 | + this.signals.sceneGraphChanged.active = false; | |
608 | + | |
609 | + while (objects.length > 0) { | |
610 | + | |
611 | + this.removeObject(objects[0]); | |
612 | + | |
613 | + } | |
614 | + | |
615 | + this.signals.sceneGraphChanged.active = true; | |
616 | + | |
617 | + this.geometries = {}; | |
618 | + this.materials = {}; | |
619 | + this.textures = {}; | |
620 | + this.scripts = {}; | |
621 | + | |
622 | + this.materialsRefCounter.clear(); | |
623 | + | |
624 | + this.animations = {}; | |
625 | + this.mixer.stopAllAction(); | |
626 | + | |
627 | + this.deselect(); | |
628 | + | |
629 | + this.signals.editorCleared.dispatch(); | |
630 | + | |
631 | + }, | |
632 | + | |
633 | + // | |
634 | + | |
635 | + fromJSON: async function (json) { | |
636 | + | |
637 | + var loader = new THREE.ObjectLoader(); | |
638 | + var camera = await loader.parseAsync(json.camera); | |
639 | + | |
640 | + const existingUuid = this.camera.uuid; | |
641 | + const incomingUuid = camera.uuid; | |
642 | + | |
643 | + // copy all properties, including uuid | |
644 | + this.camera.copy(camera); | |
645 | + this.camera.uuid = incomingUuid; | |
646 | + | |
647 | + delete this.cameras[existingUuid]; // remove old entry [existingUuid, this.camera] | |
648 | + this.cameras[incomingUuid] = this.camera; // add new entry [incomingUuid, this.camera] | |
649 | + | |
650 | + this.signals.cameraResetted.dispatch(); | |
651 | + | |
652 | + this.history.fromJSON(json.history); | |
653 | + this.scripts = json.scripts; | |
654 | + | |
655 | + this.setScene(await loader.parseAsync(json.scene)); | |
656 | + | |
657 | + if (json.environment === 'ModelViewer') { | |
658 | + | |
659 | + this.signals.sceneEnvironmentChanged.dispatch(json.environment); | |
660 | + this.signals.refreshSidebarEnvironment.dispatch(); | |
661 | + | |
662 | + } | |
663 | + | |
664 | + }, | |
665 | + | |
666 | + toJSON: function () { | |
667 | + | |
668 | + // scripts clean up | |
669 | + | |
670 | + var scene = this.scene; | |
671 | + var scripts = this.scripts; | |
672 | + | |
673 | + for (var key in scripts) { | |
674 | + | |
675 | + var script = scripts[key]; | |
676 | + | |
677 | + if (script.length === 0 || scene.getObjectByProperty('uuid', key) === undefined) { | |
678 | + | |
679 | + delete scripts[key]; | |
680 | + | |
681 | + } | |
682 | + | |
683 | + } | |
684 | + | |
685 | + // honor modelviewer environment | |
686 | + | |
687 | + let environment = null; | |
688 | + | |
689 | + if (this.scene.environment !== null && this.scene.environment.isRenderTargetTexture === true) { | |
690 | + | |
691 | + environment = 'ModelViewer'; | |
692 | + | |
693 | + } | |
694 | + | |
695 | + // | |
696 | + | |
697 | + return { | |
698 | + | |
699 | + metadata: {}, | |
700 | + project: { | |
701 | + shadows: this.config.getKey('project/renderer/shadows'), | |
702 | + shadowType: this.config.getKey('project/renderer/shadowType'), | |
703 | + toneMapping: this.config.getKey('project/renderer/toneMapping'), | |
704 | + toneMappingExposure: this.config.getKey('project/renderer/toneMappingExposure') | |
705 | + }, | |
706 | + camera: this.viewportCamera.toJSON(), | |
707 | + scene: this.scene.toJSON(), | |
708 | + scripts: this.scripts, | |
709 | + history: this.history.toJSON(), | |
710 | + environment: environment | |
711 | + | |
712 | + }; | |
713 | + | |
714 | + }, | |
715 | + | |
716 | + objectByUuid: function (uuid) { | |
717 | + | |
718 | + return this.scene.getObjectByProperty('uuid', uuid, true); | |
719 | + | |
720 | + }, | |
721 | + | |
722 | + execute: function (cmd, optionalName) { | |
723 | + this.history.execute(cmd, optionalName); | |
724 | + console.log(this) | |
725 | + this.saveTooltip?.show?.() | |
726 | + }, | |
727 | + | |
728 | + undo: function () { | |
729 | + | |
730 | + this.history.undo(); | |
731 | + | |
732 | + }, | |
733 | + | |
734 | + redo: function () { | |
735 | + | |
736 | + this.history.redo(); | |
737 | + | |
738 | + }, | |
739 | + | |
740 | + utils: { | |
741 | + | |
742 | + save: save, | |
743 | + saveArrayBuffer: saveArrayBuffer, | |
744 | + saveString: saveString, | |
745 | + formatNumber: formatNumber | |
746 | + | |
747 | + } | |
748 | + | |
749 | +}; | |
750 | + | |
751 | +const link = document.createElement('a'); | |
752 | + | |
753 | +function save(blob, filename) { | |
754 | + | |
755 | + if (link.href) { | |
756 | + | |
757 | + URL.revokeObjectURL(link.href); | |
758 | + | |
759 | + } | |
760 | + | |
761 | + link.href = URL.createObjectURL(blob); | |
762 | + link.download = filename || 'data.json'; | |
763 | + link.dispatchEvent(new MouseEvent('click')); | |
764 | + | |
765 | +} | |
766 | + | |
767 | +function saveArrayBuffer(buffer, filename) { | |
768 | + | |
769 | + save(new Blob([buffer], { type: 'application/octet-stream' }), filename); | |
770 | + | |
771 | +} | |
772 | + | |
773 | +function saveString(text, filename) { | |
774 | + | |
775 | + save(new Blob([text], { type: 'text/plain' }), filename); | |
776 | + | |
777 | +} | |
778 | + | |
779 | +function formatNumber(number) { | |
780 | + | |
781 | + return new Intl.NumberFormat('en-us', { useGrouping: true }).format(number); | |
782 | + | |
783 | +} | |
784 | + | |
785 | +export { Editor }; | ... | ... |
editor/js/EditorControls.js
0 → 100644
1 | +import * as THREE from 'three'; | |
2 | + | |
3 | +class EditorControls extends THREE.EventDispatcher { | |
4 | + | |
5 | + constructor( object, domElement ) { | |
6 | + | |
7 | + super(); | |
8 | + | |
9 | + // API | |
10 | + | |
11 | + this.enabled = true; | |
12 | + this.center = new THREE.Vector3(); | |
13 | + this.panSpeed = 0.002; | |
14 | + this.zoomSpeed = 0.1; | |
15 | + this.rotationSpeed = 0.005; | |
16 | + | |
17 | + // internals | |
18 | + | |
19 | + var scope = this; | |
20 | + var vector = new THREE.Vector3(); | |
21 | + var delta = new THREE.Vector3(); | |
22 | + var box = new THREE.Box3(); | |
23 | + | |
24 | + var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2 }; | |
25 | + var state = STATE.NONE; | |
26 | + | |
27 | + var center = this.center; | |
28 | + var normalMatrix = new THREE.Matrix3(); | |
29 | + var pointer = new THREE.Vector2(); | |
30 | + var pointerOld = new THREE.Vector2(); | |
31 | + var spherical = new THREE.Spherical(); | |
32 | + var sphere = new THREE.Sphere(); | |
33 | + | |
34 | + var pointers = []; | |
35 | + var pointerPositions = {}; | |
36 | + | |
37 | + // events | |
38 | + | |
39 | + var changeEvent = { type: 'change' }; | |
40 | + | |
41 | + this.focus = function ( target ) { | |
42 | + | |
43 | + var distance; | |
44 | + | |
45 | + box.setFromObject( target ); | |
46 | + | |
47 | + if ( box.isEmpty() === false ) { | |
48 | + | |
49 | + box.getCenter( center ); | |
50 | + distance = box.getBoundingSphere( sphere ).radius; | |
51 | + | |
52 | + } else { | |
53 | + | |
54 | + // Focusing on an Group, AmbientLight, etc | |
55 | + | |
56 | + center.setFromMatrixPosition( target.matrixWorld ); | |
57 | + distance = 0.1; | |
58 | + | |
59 | + } | |
60 | + | |
61 | + delta.set( 0, 0, 1 ); | |
62 | + delta.applyQuaternion( object.quaternion ); | |
63 | + delta.multiplyScalar( distance * 4 ); | |
64 | + | |
65 | + object.position.copy( center ).add( delta ); | |
66 | + | |
67 | + scope.dispatchEvent( changeEvent ); | |
68 | + | |
69 | + }; | |
70 | + | |
71 | + this.pan = function ( delta ) { | |
72 | + | |
73 | + var distance = object.position.distanceTo( center ); | |
74 | + | |
75 | + delta.multiplyScalar( distance * scope.panSpeed ); | |
76 | + delta.applyMatrix3( normalMatrix.getNormalMatrix( object.matrix ) ); | |
77 | + | |
78 | + object.position.add( delta ); | |
79 | + center.add( delta ); | |
80 | + | |
81 | + scope.dispatchEvent( changeEvent ); | |
82 | + | |
83 | + }; | |
84 | + | |
85 | + this.zoom = function ( delta ) { | |
86 | + | |
87 | + var distance = object.position.distanceTo( center ); | |
88 | + | |
89 | + delta.multiplyScalar( distance * scope.zoomSpeed ); | |
90 | + | |
91 | + if ( delta.length() > distance ) return; | |
92 | + | |
93 | + delta.applyMatrix3( normalMatrix.getNormalMatrix( object.matrix ) ); | |
94 | + | |
95 | + object.position.add( delta ); | |
96 | + | |
97 | + scope.dispatchEvent( changeEvent ); | |
98 | + | |
99 | + }; | |
100 | + | |
101 | + this.rotate = function ( delta ) { | |
102 | + | |
103 | + vector.copy( object.position ).sub( center ); | |
104 | + | |
105 | + spherical.setFromVector3( vector ); | |
106 | + | |
107 | + spherical.theta += delta.x * scope.rotationSpeed; | |
108 | + spherical.phi += delta.y * scope.rotationSpeed; | |
109 | + | |
110 | + spherical.makeSafe(); | |
111 | + | |
112 | + vector.setFromSpherical( spherical ); | |
113 | + | |
114 | + object.position.copy( center ).add( vector ); | |
115 | + | |
116 | + object.lookAt( center ); | |
117 | + | |
118 | + scope.dispatchEvent( changeEvent ); | |
119 | + | |
120 | + }; | |
121 | + | |
122 | + // | |
123 | + | |
124 | + function onPointerDown( event ) { | |
125 | + | |
126 | + if ( scope.enabled === false ) return; | |
127 | + | |
128 | + if ( pointers.length === 0 ) { | |
129 | + | |
130 | + domElement.setPointerCapture( event.pointerId ); | |
131 | + | |
132 | + domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove ); | |
133 | + domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp ); | |
134 | + | |
135 | + } | |
136 | + | |
137 | + // | |
138 | + | |
139 | + if ( isTrackingPointer( event ) ) return; | |
140 | + | |
141 | + // | |
142 | + | |
143 | + addPointer( event ); | |
144 | + | |
145 | + if ( event.pointerType === 'touch' ) { | |
146 | + | |
147 | + onTouchStart( event ); | |
148 | + | |
149 | + } else { | |
150 | + | |
151 | + onMouseDown( event ); | |
152 | + | |
153 | + } | |
154 | + | |
155 | + } | |
156 | + | |
157 | + function onPointerMove( event ) { | |
158 | + | |
159 | + if ( scope.enabled === false ) return; | |
160 | + | |
161 | + if ( event.pointerType === 'touch' ) { | |
162 | + | |
163 | + onTouchMove( event ); | |
164 | + | |
165 | + } else { | |
166 | + | |
167 | + onMouseMove( event ); | |
168 | + | |
169 | + } | |
170 | + | |
171 | + } | |
172 | + | |
173 | + function onPointerUp( event ) { | |
174 | + | |
175 | + removePointer( event ); | |
176 | + | |
177 | + switch ( pointers.length ) { | |
178 | + | |
179 | + case 0: | |
180 | + | |
181 | + domElement.releasePointerCapture( event.pointerId ); | |
182 | + | |
183 | + domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove ); | |
184 | + domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp ); | |
185 | + | |
186 | + break; | |
187 | + | |
188 | + case 1: | |
189 | + | |
190 | + var pointerId = pointers[ 0 ]; | |
191 | + var position = pointerPositions[ pointerId ]; | |
192 | + | |
193 | + // minimal placeholder event - allows state correction on pointer-up | |
194 | + onTouchStart( { pointerId: pointerId, pageX: position.x, pageY: position.y } ); | |
195 | + | |
196 | + break; | |
197 | + | |
198 | + } | |
199 | + | |
200 | + } | |
201 | + | |
202 | + // mouse | |
203 | + | |
204 | + function onMouseDown( event ) { | |
205 | + | |
206 | + if ( event.button === 0 ) { | |
207 | + | |
208 | + state = STATE.ROTATE; | |
209 | + | |
210 | + } else if ( event.button === 1 ) { | |
211 | + | |
212 | + state = STATE.ZOOM; | |
213 | + | |
214 | + } else if ( event.button === 2 ) { | |
215 | + | |
216 | + state = STATE.PAN; | |
217 | + | |
218 | + } | |
219 | + | |
220 | + pointerOld.set( event.clientX, event.clientY ); | |
221 | + | |
222 | + } | |
223 | + | |
224 | + function onMouseMove( event ) { | |
225 | + | |
226 | + pointer.set( event.clientX, event.clientY ); | |
227 | + | |
228 | + var movementX = pointer.x - pointerOld.x; | |
229 | + var movementY = pointer.y - pointerOld.y; | |
230 | + | |
231 | + if ( state === STATE.ROTATE ) { | |
232 | + | |
233 | + scope.rotate( delta.set( - movementX, - movementY, 0 ) ); | |
234 | + | |
235 | + } else if ( state === STATE.ZOOM ) { | |
236 | + | |
237 | + scope.zoom( delta.set( 0, 0, movementY ) ); | |
238 | + | |
239 | + } else if ( state === STATE.PAN ) { | |
240 | + | |
241 | + scope.pan( delta.set( - movementX, movementY, 0 ) ); | |
242 | + | |
243 | + } | |
244 | + | |
245 | + pointerOld.set( event.clientX, event.clientY ); | |
246 | + | |
247 | + } | |
248 | + | |
249 | + function onMouseUp() { | |
250 | + | |
251 | + state = STATE.NONE; | |
252 | + | |
253 | + } | |
254 | + | |
255 | + function onMouseWheel( event ) { | |
256 | + | |
257 | + if ( scope.enabled === false ) return; | |
258 | + | |
259 | + event.preventDefault(); | |
260 | + | |
261 | + // Normalize deltaY due to https://bugzilla.mozilla.org/show_bug.cgi?id=1392460 | |
262 | + scope.zoom( delta.set( 0, 0, event.deltaY > 0 ? 1 : - 1 ) ); | |
263 | + | |
264 | + } | |
265 | + | |
266 | + function contextmenu( event ) { | |
267 | + | |
268 | + event.preventDefault(); | |
269 | + | |
270 | + } | |
271 | + | |
272 | + this.dispose = function () { | |
273 | + | |
274 | + domElement.removeEventListener( 'contextmenu', contextmenu ); | |
275 | + domElement.removeEventListener( 'dblclick', onMouseUp ); | |
276 | + domElement.removeEventListener( 'wheel', onMouseWheel ); | |
277 | + | |
278 | + domElement.removeEventListener( 'pointerdown', onPointerDown ); | |
279 | + | |
280 | + }; | |
281 | + | |
282 | + domElement.addEventListener( 'contextmenu', contextmenu ); | |
283 | + domElement.addEventListener( 'dblclick', onMouseUp ); | |
284 | + domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } ); | |
285 | + | |
286 | + domElement.addEventListener( 'pointerdown', onPointerDown ); | |
287 | + | |
288 | + // touch | |
289 | + | |
290 | + var touches = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; | |
291 | + var prevTouches = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; | |
292 | + | |
293 | + var prevDistance = null; | |
294 | + | |
295 | + function onTouchStart( event ) { | |
296 | + | |
297 | + trackPointer( event ); | |
298 | + | |
299 | + switch ( pointers.length ) { | |
300 | + | |
301 | + case 1: | |
302 | + touches[ 0 ].set( event.pageX, event.pageY, 0 ).divideScalar( window.devicePixelRatio ); | |
303 | + touches[ 1 ].set( event.pageX, event.pageY, 0 ).divideScalar( window.devicePixelRatio ); | |
304 | + break; | |
305 | + | |
306 | + case 2: | |
307 | + | |
308 | + var position = getSecondPointerPosition( event ); | |
309 | + | |
310 | + touches[ 0 ].set( event.pageX, event.pageY, 0 ).divideScalar( window.devicePixelRatio ); | |
311 | + touches[ 1 ].set( position.x, position.y, 0 ).divideScalar( window.devicePixelRatio ); | |
312 | + prevDistance = touches[ 0 ].distanceTo( touches[ 1 ] ); | |
313 | + break; | |
314 | + | |
315 | + } | |
316 | + | |
317 | + prevTouches[ 0 ].copy( touches[ 0 ] ); | |
318 | + prevTouches[ 1 ].copy( touches[ 1 ] ); | |
319 | + | |
320 | + } | |
321 | + | |
322 | + | |
323 | + function onTouchMove( event ) { | |
324 | + | |
325 | + trackPointer( event ); | |
326 | + | |
327 | + function getClosest( touch, touches ) { | |
328 | + | |
329 | + var closest = touches[ 0 ]; | |
330 | + | |
331 | + for ( var touch2 of touches ) { | |
332 | + | |
333 | + if ( closest.distanceTo( touch ) > touch2.distanceTo( touch ) ) closest = touch2; | |
334 | + | |
335 | + } | |
336 | + | |
337 | + return closest; | |
338 | + | |
339 | + } | |
340 | + | |
341 | + switch ( pointers.length ) { | |
342 | + | |
343 | + case 1: | |
344 | + touches[ 0 ].set( event.pageX, event.pageY, 0 ).divideScalar( window.devicePixelRatio ); | |
345 | + touches[ 1 ].set( event.pageX, event.pageY, 0 ).divideScalar( window.devicePixelRatio ); | |
346 | + scope.rotate( touches[ 0 ].sub( getClosest( touches[ 0 ], prevTouches ) ).multiplyScalar( - 1 ) ); | |
347 | + break; | |
348 | + | |
349 | + case 2: | |
350 | + | |
351 | + var position = getSecondPointerPosition( event ); | |
352 | + | |
353 | + touches[ 0 ].set( event.pageX, event.pageY, 0 ).divideScalar( window.devicePixelRatio ); | |
354 | + touches[ 1 ].set( position.x, position.y, 0 ).divideScalar( window.devicePixelRatio ); | |
355 | + var distance = touches[ 0 ].distanceTo( touches[ 1 ] ); | |
356 | + scope.zoom( delta.set( 0, 0, prevDistance - distance ) ); | |
357 | + prevDistance = distance; | |
358 | + | |
359 | + | |
360 | + var offset0 = touches[ 0 ].clone().sub( getClosest( touches[ 0 ], prevTouches ) ); | |
361 | + var offset1 = touches[ 1 ].clone().sub( getClosest( touches[ 1 ], prevTouches ) ); | |
362 | + offset0.x = - offset0.x; | |
363 | + offset1.x = - offset1.x; | |
364 | + | |
365 | + scope.pan( offset0.add( offset1 ) ); | |
366 | + | |
367 | + break; | |
368 | + | |
369 | + } | |
370 | + | |
371 | + prevTouches[ 0 ].copy( touches[ 0 ] ); | |
372 | + prevTouches[ 1 ].copy( touches[ 1 ] ); | |
373 | + | |
374 | + } | |
375 | + | |
376 | + function addPointer( event ) { | |
377 | + | |
378 | + pointers.push( event.pointerId ); | |
379 | + | |
380 | + } | |
381 | + | |
382 | + function removePointer( event ) { | |
383 | + | |
384 | + delete pointerPositions[ event.pointerId ]; | |
385 | + | |
386 | + for ( var i = 0; i < pointers.length; i ++ ) { | |
387 | + | |
388 | + if ( pointers[ i ] == event.pointerId ) { | |
389 | + | |
390 | + pointers.splice( i, 1 ); | |
391 | + return; | |
392 | + | |
393 | + } | |
394 | + | |
395 | + } | |
396 | + | |
397 | + } | |
398 | + | |
399 | + function isTrackingPointer( event ) { | |
400 | + | |
401 | + for ( var i = 0; i < pointers.length; i ++ ) { | |
402 | + | |
403 | + if ( pointers[ i ] == event.pointerId ) return true; | |
404 | + | |
405 | + } | |
406 | + | |
407 | + return false; | |
408 | + | |
409 | + } | |
410 | + | |
411 | + function trackPointer( event ) { | |
412 | + | |
413 | + var position = pointerPositions[ event.pointerId ]; | |
414 | + | |
415 | + if ( position === undefined ) { | |
416 | + | |
417 | + position = new THREE.Vector2(); | |
418 | + pointerPositions[ event.pointerId ] = position; | |
419 | + | |
420 | + } | |
421 | + | |
422 | + position.set( event.pageX, event.pageY ); | |
423 | + | |
424 | + } | |
425 | + | |
426 | + function getSecondPointerPosition( event ) { | |
427 | + | |
428 | + var pointerId = ( event.pointerId === pointers[ 0 ] ) ? pointers[ 1 ] : pointers[ 0 ]; | |
429 | + | |
430 | + return pointerPositions[ pointerId ]; | |
431 | + | |
432 | + } | |
433 | + | |
434 | + } | |
435 | + | |
436 | +} | |
437 | + | |
438 | +export { EditorControls }; | ... | ... |
editor/js/History.js
0 → 100644
1 | +import * as Commands from './commands/Commands.js'; | |
2 | + | |
3 | +class History { | |
4 | + | |
5 | + constructor( editor ) { | |
6 | + | |
7 | + this.editor = editor; | |
8 | + this.undos = []; | |
9 | + this.redos = []; | |
10 | + this.lastCmdTime = Date.now(); | |
11 | + this.idCounter = 0; | |
12 | + | |
13 | + this.historyDisabled = false; | |
14 | + this.config = editor.config; | |
15 | + | |
16 | + // signals | |
17 | + | |
18 | + const scope = this; | |
19 | + | |
20 | + this.editor.signals.startPlayer.add( function () { | |
21 | + | |
22 | + scope.historyDisabled = true; | |
23 | + | |
24 | + } ); | |
25 | + | |
26 | + this.editor.signals.stopPlayer.add( function () { | |
27 | + | |
28 | + scope.historyDisabled = false; | |
29 | + | |
30 | + } ); | |
31 | + | |
32 | + } | |
33 | + | |
34 | + execute( cmd, optionalName ) { | |
35 | + | |
36 | + const lastCmd = this.undos[ this.undos.length - 1 ]; | |
37 | + const timeDifference = Date.now() - this.lastCmdTime; | |
38 | + | |
39 | + const isUpdatableCmd = lastCmd && | |
40 | + lastCmd.updatable && | |
41 | + cmd.updatable && | |
42 | + lastCmd.object === cmd.object && | |
43 | + lastCmd.type === cmd.type && | |
44 | + lastCmd.script === cmd.script && | |
45 | + lastCmd.attributeName === cmd.attributeName; | |
46 | + | |
47 | + if ( isUpdatableCmd && cmd.type === 'SetScriptValueCommand' ) { | |
48 | + | |
49 | + // When the cmd.type is "SetScriptValueCommand" the timeDifference is ignored | |
50 | + | |
51 | + lastCmd.update( cmd ); | |
52 | + cmd = lastCmd; | |
53 | + | |
54 | + } else if ( isUpdatableCmd && timeDifference < 500 ) { | |
55 | + | |
56 | + lastCmd.update( cmd ); | |
57 | + cmd = lastCmd; | |
58 | + | |
59 | + } else { | |
60 | + | |
61 | + // the command is not updatable and is added as a new part of the history | |
62 | + | |
63 | + this.undos.push( cmd ); | |
64 | + cmd.id = ++ this.idCounter; | |
65 | + | |
66 | + } | |
67 | + | |
68 | + cmd.name = ( optionalName !== undefined ) ? optionalName : cmd.name; | |
69 | + cmd.execute(); | |
70 | + cmd.inMemory = true; | |
71 | + | |
72 | + if ( this.config.getKey( 'settings/history' ) ) { | |
73 | + | |
74 | + cmd.json = cmd.toJSON(); // serialize the cmd immediately after execution and append the json to the cmd | |
75 | + | |
76 | + } | |
77 | + | |
78 | + this.lastCmdTime = Date.now(); | |
79 | + | |
80 | + // clearing all the redo-commands | |
81 | + | |
82 | + this.redos = []; | |
83 | + this.editor.signals.historyChanged.dispatch( cmd ); | |
84 | + | |
85 | + } | |
86 | + | |
87 | + undo() { | |
88 | + | |
89 | + if ( this.historyDisabled ) { | |
90 | + | |
91 | + alert( this.editor.strings.getKey( 'prompt/history/forbid' ) ); | |
92 | + return; | |
93 | + | |
94 | + } | |
95 | + | |
96 | + let cmd = undefined; | |
97 | + | |
98 | + if ( this.undos.length > 0 ) { | |
99 | + | |
100 | + cmd = this.undos.pop(); | |
101 | + | |
102 | + if ( cmd.inMemory === false ) { | |
103 | + | |
104 | + cmd.fromJSON( cmd.json ); | |
105 | + | |
106 | + } | |
107 | + | |
108 | + } | |
109 | + | |
110 | + if ( cmd !== undefined ) { | |
111 | + | |
112 | + cmd.undo(); | |
113 | + this.redos.push( cmd ); | |
114 | + this.editor.signals.historyChanged.dispatch( cmd ); | |
115 | + | |
116 | + } | |
117 | + | |
118 | + return cmd; | |
119 | + | |
120 | + } | |
121 | + | |
122 | + redo() { | |
123 | + | |
124 | + if ( this.historyDisabled ) { | |
125 | + | |
126 | + alert( this.editor.strings.getKey( 'prompt/history/forbid' ) ); | |
127 | + return; | |
128 | + | |
129 | + } | |
130 | + | |
131 | + let cmd = undefined; | |
132 | + | |
133 | + if ( this.redos.length > 0 ) { | |
134 | + | |
135 | + cmd = this.redos.pop(); | |
136 | + | |
137 | + if ( cmd.inMemory === false ) { | |
138 | + | |
139 | + cmd.fromJSON( cmd.json ); | |
140 | + | |
141 | + } | |
142 | + | |
143 | + } | |
144 | + | |
145 | + if ( cmd !== undefined ) { | |
146 | + | |
147 | + cmd.execute(); | |
148 | + this.undos.push( cmd ); | |
149 | + this.editor.signals.historyChanged.dispatch( cmd ); | |
150 | + | |
151 | + } | |
152 | + | |
153 | + return cmd; | |
154 | + | |
155 | + } | |
156 | + | |
157 | + toJSON() { | |
158 | + | |
159 | + const history = {}; | |
160 | + history.undos = []; | |
161 | + history.redos = []; | |
162 | + | |
163 | + if ( ! this.config.getKey( 'settings/history' ) ) { | |
164 | + | |
165 | + return history; | |
166 | + | |
167 | + } | |
168 | + | |
169 | + // Append Undos to History | |
170 | + | |
171 | + for ( let i = 0; i < this.undos.length; i ++ ) { | |
172 | + | |
173 | + if ( this.undos[ i ].hasOwnProperty( 'json' ) ) { | |
174 | + | |
175 | + history.undos.push( this.undos[ i ].json ); | |
176 | + | |
177 | + } | |
178 | + | |
179 | + } | |
180 | + | |
181 | + // Append Redos to History | |
182 | + | |
183 | + for ( let i = 0; i < this.redos.length; i ++ ) { | |
184 | + | |
185 | + if ( this.redos[ i ].hasOwnProperty( 'json' ) ) { | |
186 | + | |
187 | + history.redos.push( this.redos[ i ].json ); | |
188 | + | |
189 | + } | |
190 | + | |
191 | + } | |
192 | + | |
193 | + return history; | |
194 | + | |
195 | + } | |
196 | + | |
197 | + fromJSON( json ) { | |
198 | + | |
199 | + if ( json === undefined ) return; | |
200 | + | |
201 | + for ( let i = 0; i < json.undos.length; i ++ ) { | |
202 | + | |
203 | + const cmdJSON = json.undos[ i ]; | |
204 | + const cmd = new Commands[ cmdJSON.type ]( this.editor ); // creates a new object of type "json.type" | |
205 | + cmd.json = cmdJSON; | |
206 | + cmd.id = cmdJSON.id; | |
207 | + cmd.name = cmdJSON.name; | |
208 | + this.undos.push( cmd ); | |
209 | + this.idCounter = ( cmdJSON.id > this.idCounter ) ? cmdJSON.id : this.idCounter; // set last used idCounter | |
210 | + | |
211 | + } | |
212 | + | |
213 | + for ( let i = 0; i < json.redos.length; i ++ ) { | |
214 | + | |
215 | + const cmdJSON = json.redos[ i ]; | |
216 | + const cmd = new Commands[ cmdJSON.type ]( this.editor ); // creates a new object of type "json.type" | |
217 | + cmd.json = cmdJSON; | |
218 | + cmd.id = cmdJSON.id; | |
219 | + cmd.name = cmdJSON.name; | |
220 | + this.redos.push( cmd ); | |
221 | + this.idCounter = ( cmdJSON.id > this.idCounter ) ? cmdJSON.id : this.idCounter; // set last used idCounter | |
222 | + | |
223 | + } | |
224 | + | |
225 | + // Select the last executed undo-command | |
226 | + this.editor.signals.historyChanged.dispatch( this.undos[ this.undos.length - 1 ] ); | |
227 | + | |
228 | + } | |
229 | + | |
230 | + clear() { | |
231 | + | |
232 | + this.undos = []; | |
233 | + this.redos = []; | |
234 | + this.idCounter = 0; | |
235 | + | |
236 | + this.editor.signals.historyChanged.dispatch(); | |
237 | + | |
238 | + } | |
239 | + | |
240 | + goToState( id ) { | |
241 | + | |
242 | + if ( this.historyDisabled ) { | |
243 | + | |
244 | + alert( this.editor.strings.getKey( 'prompt/history/forbid' ) ); | |
245 | + return; | |
246 | + | |
247 | + } | |
248 | + | |
249 | + this.editor.signals.sceneGraphChanged.active = false; | |
250 | + this.editor.signals.historyChanged.active = false; | |
251 | + | |
252 | + let cmd = this.undos.length > 0 ? this.undos[ this.undos.length - 1 ] : undefined; // next cmd to pop | |
253 | + | |
254 | + if ( cmd === undefined || id > cmd.id ) { | |
255 | + | |
256 | + cmd = this.redo(); | |
257 | + while ( cmd !== undefined && id > cmd.id ) { | |
258 | + | |
259 | + cmd = this.redo(); | |
260 | + | |
261 | + } | |
262 | + | |
263 | + } else { | |
264 | + | |
265 | + while ( true ) { | |
266 | + | |
267 | + cmd = this.undos[ this.undos.length - 1 ]; // next cmd to pop | |
268 | + | |
269 | + if ( cmd === undefined || id === cmd.id ) break; | |
270 | + | |
271 | + this.undo(); | |
272 | + | |
273 | + } | |
274 | + | |
275 | + } | |
276 | + | |
277 | + this.editor.signals.sceneGraphChanged.active = true; | |
278 | + this.editor.signals.historyChanged.active = true; | |
279 | + | |
280 | + this.editor.signals.sceneGraphChanged.dispatch(); | |
281 | + this.editor.signals.historyChanged.dispatch( cmd ); | |
282 | + | |
283 | + } | |
284 | + | |
285 | + enableSerialization( id ) { | |
286 | + | |
287 | + /** | |
288 | + * because there might be commands in this.undos and this.redos | |
289 | + * which have not been serialized with .toJSON() we go back | |
290 | + * to the oldest command and redo one command after the other | |
291 | + * while also calling .toJSON() on them. | |
292 | + */ | |
293 | + | |
294 | + this.goToState( - 1 ); | |
295 | + | |
296 | + this.editor.signals.sceneGraphChanged.active = false; | |
297 | + this.editor.signals.historyChanged.active = false; | |
298 | + | |
299 | + let cmd = this.redo(); | |
300 | + while ( cmd !== undefined ) { | |
301 | + | |
302 | + if ( ! cmd.hasOwnProperty( 'json' ) ) { | |
303 | + | |
304 | + cmd.json = cmd.toJSON(); | |
305 | + | |
306 | + } | |
307 | + | |
308 | + cmd = this.redo(); | |
309 | + | |
310 | + } | |
311 | + | |
312 | + this.editor.signals.sceneGraphChanged.active = true; | |
313 | + this.editor.signals.historyChanged.active = true; | |
314 | + | |
315 | + this.goToState( id ); | |
316 | + | |
317 | + } | |
318 | + | |
319 | +} | |
320 | + | |
321 | +export { History }; | ... | ... |
editor/js/Loader.js
0 → 100644
1 | +import * as THREE from 'three'; | |
2 | + | |
3 | +import { TGALoader } from 'three/addons/loaders/TGALoader.js'; | |
4 | + | |
5 | +import { AddObjectCommand } from './commands/AddObjectCommand.js'; | |
6 | + | |
7 | +import { LoaderUtils } from './LoaderUtils.js'; | |
8 | + | |
9 | +import { unzipSync, strFromU8 } from 'three/addons/libs/fflate.module.js'; | |
10 | + | |
11 | +function Loader( editor ) { | |
12 | + | |
13 | + const scope = this; | |
14 | + | |
15 | + this.texturePath = ''; | |
16 | + | |
17 | + this.loadItemList = function ( items ) { | |
18 | + | |
19 | + LoaderUtils.getFilesFromItemList( items, function ( files, filesMap ) { | |
20 | + | |
21 | + scope.loadFiles( files, filesMap ); | |
22 | + | |
23 | + } ); | |
24 | + | |
25 | + }; | |
26 | + | |
27 | + this.loadFiles = function ( files, filesMap ) { | |
28 | + | |
29 | + if ( files.length > 0 ) { | |
30 | + | |
31 | + filesMap = filesMap || LoaderUtils.createFilesMap( files ); | |
32 | + | |
33 | + const manager = new THREE.LoadingManager(); | |
34 | + manager.setURLModifier( function ( url ) { | |
35 | + | |
36 | + url = url.replace( /^(\.?\/)/, '' ); // remove './' | |
37 | + | |
38 | + const file = filesMap[ url ]; | |
39 | + | |
40 | + if ( file ) { | |
41 | + | |
42 | + console.log( 'Loading', url ); | |
43 | + | |
44 | + return URL.createObjectURL( file ); | |
45 | + | |
46 | + } | |
47 | + | |
48 | + return url; | |
49 | + | |
50 | + } ); | |
51 | + | |
52 | + manager.addHandler( /\.tga$/i, new TGALoader() ); | |
53 | + | |
54 | + for ( let i = 0; i < files.length; i ++ ) { | |
55 | + | |
56 | + scope.loadFile( files[ i ], manager ); | |
57 | + | |
58 | + } | |
59 | + | |
60 | + } | |
61 | + | |
62 | + }; | |
63 | + | |
64 | + this.loadFile = function ( file, manager ) { | |
65 | + | |
66 | + const filename = file.name; | |
67 | + const extension = filename.split( '.' ).pop().toLowerCase(); | |
68 | + | |
69 | + const reader = new FileReader(); | |
70 | + reader.addEventListener( 'progress', function ( event ) { | |
71 | + | |
72 | + const size = '(' + editor.utils.formatNumber( Math.floor( event.total / 1000 ) ) + ' KB)'; | |
73 | + const progress = Math.floor( ( event.loaded / event.total ) * 100 ) + '%'; | |
74 | + | |
75 | + console.log( 'Loading', filename, size, progress ); | |
76 | + | |
77 | + } ); | |
78 | + | |
79 | + switch ( extension ) { | |
80 | + | |
81 | + case '3dm': | |
82 | + | |
83 | + { | |
84 | + | |
85 | + reader.addEventListener( 'load', async function ( event ) { | |
86 | + | |
87 | + const contents = event.target.result; | |
88 | + | |
89 | + const { Rhino3dmLoader } = await import( 'three/addons/loaders/3DMLoader.js' ); | |
90 | + | |
91 | + const loader = new Rhino3dmLoader(); | |
92 | + loader.setLibraryPath( '../examples/jsm/libs/rhino3dm/' ); | |
93 | + loader.parse( contents, function ( object ) { | |
94 | + | |
95 | + object.name = filename; | |
96 | + | |
97 | + editor.execute( new AddObjectCommand( editor, object ) ); | |
98 | + | |
99 | + }, function ( error ) { | |
100 | + | |
101 | + console.error( error ); | |
102 | + | |
103 | + } ); | |
104 | + | |
105 | + }, false ); | |
106 | + reader.readAsArrayBuffer( file ); | |
107 | + | |
108 | + break; | |
109 | + | |
110 | + } | |
111 | + | |
112 | + case '3ds': | |
113 | + | |
114 | + { | |
115 | + | |
116 | + reader.addEventListener( 'load', async function ( event ) { | |
117 | + | |
118 | + const { TDSLoader } = await import( 'three/addons/loaders/TDSLoader.js' ); | |
119 | + | |
120 | + const loader = new TDSLoader(); | |
121 | + const object = loader.parse( event.target.result ); | |
122 | + | |
123 | + editor.execute( new AddObjectCommand( editor, object ) ); | |
124 | + | |
125 | + }, false ); | |
126 | + reader.readAsArrayBuffer( file ); | |
127 | + | |
128 | + break; | |
129 | + | |
130 | + } | |
131 | + | |
132 | + case '3mf': | |
133 | + | |
134 | + { | |
135 | + | |
136 | + reader.addEventListener( 'load', async function ( event ) { | |
137 | + | |
138 | + const { ThreeMFLoader } = await import( 'three/addons/loaders/3MFLoader.js' ); | |
139 | + | |
140 | + const loader = new ThreeMFLoader(); | |
141 | + const object = loader.parse( event.target.result ); | |
142 | + | |
143 | + editor.execute( new AddObjectCommand( editor, object ) ); | |
144 | + | |
145 | + }, false ); | |
146 | + reader.readAsArrayBuffer( file ); | |
147 | + | |
148 | + break; | |
149 | + | |
150 | + } | |
151 | + | |
152 | + case 'amf': | |
153 | + | |
154 | + { | |
155 | + | |
156 | + reader.addEventListener( 'load', async function ( event ) { | |
157 | + | |
158 | + const { AMFLoader } = await import( 'three/addons/loaders/AMFLoader.js' ); | |
159 | + | |
160 | + const loader = new AMFLoader(); | |
161 | + const amfobject = loader.parse( event.target.result ); | |
162 | + | |
163 | + editor.execute( new AddObjectCommand( editor, amfobject ) ); | |
164 | + | |
165 | + }, false ); | |
166 | + reader.readAsArrayBuffer( file ); | |
167 | + | |
168 | + break; | |
169 | + | |
170 | + } | |
171 | + | |
172 | + case 'dae': | |
173 | + | |
174 | + { | |
175 | + | |
176 | + reader.addEventListener( 'load', async function ( event ) { | |
177 | + | |
178 | + const contents = event.target.result; | |
179 | + | |
180 | + const { ColladaLoader } = await import( 'three/addons/loaders/ColladaLoader.js' ); | |
181 | + | |
182 | + const loader = new ColladaLoader( manager ); | |
183 | + const collada = loader.parse( contents ); | |
184 | + | |
185 | + collada.scene.name = filename; | |
186 | + | |
187 | + editor.execute( new AddObjectCommand( editor, collada.scene ) ); | |
188 | + | |
189 | + }, false ); | |
190 | + reader.readAsText( file ); | |
191 | + | |
192 | + break; | |
193 | + | |
194 | + } | |
195 | + | |
196 | + case 'drc': | |
197 | + | |
198 | + { | |
199 | + | |
200 | + reader.addEventListener( 'load', async function ( event ) { | |
201 | + | |
202 | + const contents = event.target.result; | |
203 | + | |
204 | + const { DRACOLoader } = await import( 'three/addons/loaders/DRACOLoader.js' ); | |
205 | + | |
206 | + const loader = new DRACOLoader(); | |
207 | + loader.setDecoderPath( '../examples/jsm/libs/draco/' ); | |
208 | + loader.parse( contents, function ( geometry ) { | |
209 | + | |
210 | + let object; | |
211 | + | |
212 | + if ( geometry.index !== null ) { | |
213 | + | |
214 | + const material = new THREE.MeshStandardMaterial(); | |
215 | + | |
216 | + object = new THREE.Mesh( geometry, material ); | |
217 | + object.name = filename; | |
218 | + | |
219 | + } else { | |
220 | + | |
221 | + const material = new THREE.PointsMaterial( { size: 0.01 } ); | |
222 | + material.vertexColors = geometry.hasAttribute( 'color' ); | |
223 | + | |
224 | + object = new THREE.Points( geometry, material ); | |
225 | + object.name = filename; | |
226 | + | |
227 | + } | |
228 | + | |
229 | + loader.dispose(); | |
230 | + editor.execute( new AddObjectCommand( editor, object ) ); | |
231 | + | |
232 | + } ); | |
233 | + | |
234 | + }, false ); | |
235 | + reader.readAsArrayBuffer( file ); | |
236 | + | |
237 | + break; | |
238 | + | |
239 | + } | |
240 | + | |
241 | + case 'fbx': | |
242 | + | |
243 | + { | |
244 | + | |
245 | + reader.addEventListener( 'load', async function ( event ) { | |
246 | + | |
247 | + const contents = event.target.result; | |
248 | + | |
249 | + const { FBXLoader } = await import( 'three/addons/loaders/FBXLoader.js' ); | |
250 | + | |
251 | + const loader = new FBXLoader( manager ); | |
252 | + const object = loader.parse( contents ); | |
253 | + | |
254 | + editor.execute( new AddObjectCommand( editor, object ) ); | |
255 | + | |
256 | + }, false ); | |
257 | + reader.readAsArrayBuffer( file ); | |
258 | + | |
259 | + break; | |
260 | + | |
261 | + } | |
262 | + | |
263 | + case 'glb': | |
264 | + | |
265 | + { | |
266 | + | |
267 | + reader.addEventListener( 'load', async function ( event ) { | |
268 | + | |
269 | + const contents = event.target.result; | |
270 | + | |
271 | + const loader = await createGLTFLoader(); | |
272 | + | |
273 | + loader.parse( contents, '', function ( result ) { | |
274 | + | |
275 | + const scene = result.scene; | |
276 | + scene.name = filename; | |
277 | + | |
278 | + scene.animations.push( ...result.animations ); | |
279 | + editor.execute( new AddObjectCommand( editor, scene ) ); | |
280 | + | |
281 | + loader.dracoLoader.dispose(); | |
282 | + loader.ktx2Loader.dispose(); | |
283 | + | |
284 | + } ); | |
285 | + | |
286 | + }, false ); | |
287 | + reader.readAsArrayBuffer( file ); | |
288 | + | |
289 | + break; | |
290 | + | |
291 | + } | |
292 | + | |
293 | + case 'gltf': | |
294 | + | |
295 | + { | |
296 | + | |
297 | + reader.addEventListener( 'load', async function ( event ) { | |
298 | + | |
299 | + const contents = event.target.result; | |
300 | + | |
301 | + const loader = await createGLTFLoader( manager ); | |
302 | + | |
303 | + loader.parse( contents, '', function ( result ) { | |
304 | + | |
305 | + const scene = result.scene; | |
306 | + scene.name = filename; | |
307 | + | |
308 | + scene.animations.push( ...result.animations ); | |
309 | + editor.execute( new AddObjectCommand( editor, scene ) ); | |
310 | + | |
311 | + loader.dracoLoader.dispose(); | |
312 | + loader.ktx2Loader.dispose(); | |
313 | + | |
314 | + } ); | |
315 | + | |
316 | + }, false ); | |
317 | + reader.readAsArrayBuffer( file ); | |
318 | + | |
319 | + break; | |
320 | + | |
321 | + } | |
322 | + | |
323 | + case 'js': | |
324 | + case 'json': | |
325 | + | |
326 | + { | |
327 | + | |
328 | + reader.addEventListener( 'load', function ( event ) { | |
329 | + | |
330 | + const contents = event.target.result; | |
331 | + | |
332 | + // 2.0 | |
333 | + | |
334 | + if ( contents.indexOf( 'postMessage' ) !== - 1 ) { | |
335 | + | |
336 | + const blob = new Blob( [ contents ], { type: 'text/javascript' } ); | |
337 | + const url = URL.createObjectURL( blob ); | |
338 | + | |
339 | + const worker = new Worker( url ); | |
340 | + | |
341 | + worker.onmessage = function ( event ) { | |
342 | + | |
343 | + event.data.metadata = { version: 2 }; | |
344 | + handleJSON( event.data ); | |
345 | + | |
346 | + }; | |
347 | + | |
348 | + worker.postMessage( Date.now() ); | |
349 | + | |
350 | + return; | |
351 | + | |
352 | + } | |
353 | + | |
354 | + // >= 3.0 | |
355 | + | |
356 | + let data; | |
357 | + | |
358 | + try { | |
359 | + | |
360 | + data = JSON.parse( contents ); | |
361 | + | |
362 | + } catch ( error ) { | |
363 | + | |
364 | + alert( error ); | |
365 | + return; | |
366 | + | |
367 | + } | |
368 | + | |
369 | + handleJSON( data ); | |
370 | + | |
371 | + }, false ); | |
372 | + reader.readAsText( file ); | |
373 | + | |
374 | + break; | |
375 | + | |
376 | + } | |
377 | + | |
378 | + case 'kmz': | |
379 | + | |
380 | + { | |
381 | + | |
382 | + reader.addEventListener( 'load', async function ( event ) { | |
383 | + | |
384 | + const { KMZLoader } = await import( 'three/addons/loaders/KMZLoader.js' ); | |
385 | + | |
386 | + const loader = new KMZLoader(); | |
387 | + const collada = loader.parse( event.target.result ); | |
388 | + | |
389 | + collada.scene.name = filename; | |
390 | + | |
391 | + editor.execute( new AddObjectCommand( editor, collada.scene ) ); | |
392 | + | |
393 | + }, false ); | |
394 | + reader.readAsArrayBuffer( file ); | |
395 | + | |
396 | + break; | |
397 | + | |
398 | + } | |
399 | + | |
400 | + case 'ldr': | |
401 | + case 'mpd': | |
402 | + | |
403 | + { | |
404 | + | |
405 | + reader.addEventListener( 'load', async function ( event ) { | |
406 | + | |
407 | + const { LDrawLoader } = await import( 'three/addons/loaders/LDrawLoader.js' ); | |
408 | + | |
409 | + const loader = new LDrawLoader(); | |
410 | + loader.setPath( '../../examples/models/ldraw/officialLibrary/' ); | |
411 | + loader.parse( event.target.result, function ( group ) { | |
412 | + | |
413 | + group.name = filename; | |
414 | + // Convert from LDraw coordinates: rotate 180 degrees around OX | |
415 | + group.rotation.x = Math.PI; | |
416 | + | |
417 | + editor.execute( new AddObjectCommand( editor, group ) ); | |
418 | + | |
419 | + } ); | |
420 | + | |
421 | + }, false ); | |
422 | + reader.readAsText( file ); | |
423 | + | |
424 | + break; | |
425 | + | |
426 | + } | |
427 | + | |
428 | + case 'md2': | |
429 | + | |
430 | + { | |
431 | + | |
432 | + reader.addEventListener( 'load', async function ( event ) { | |
433 | + | |
434 | + const contents = event.target.result; | |
435 | + | |
436 | + const { MD2Loader } = await import( 'three/addons/loaders/MD2Loader.js' ); | |
437 | + | |
438 | + const geometry = new MD2Loader().parse( contents ); | |
439 | + const material = new THREE.MeshStandardMaterial(); | |
440 | + | |
441 | + const mesh = new THREE.Mesh( geometry, material ); | |
442 | + mesh.mixer = new THREE.AnimationMixer( mesh ); | |
443 | + mesh.name = filename; | |
444 | + | |
445 | + mesh.animations.push( ...geometry.animations ); | |
446 | + editor.execute( new AddObjectCommand( editor, mesh ) ); | |
447 | + | |
448 | + }, false ); | |
449 | + reader.readAsArrayBuffer( file ); | |
450 | + | |
451 | + break; | |
452 | + | |
453 | + } | |
454 | + | |
455 | + case 'obj': | |
456 | + | |
457 | + { | |
458 | + | |
459 | + reader.addEventListener( 'load', async function ( event ) { | |
460 | + | |
461 | + const contents = event.target.result; | |
462 | + | |
463 | + const { OBJLoader } = await import( 'three/addons/loaders/OBJLoader.js' ); | |
464 | + | |
465 | + const object = new OBJLoader().parse( contents ); | |
466 | + object.name = filename; | |
467 | + | |
468 | + editor.execute( new AddObjectCommand( editor, object ) ); | |
469 | + | |
470 | + }, false ); | |
471 | + reader.readAsText( file ); | |
472 | + | |
473 | + break; | |
474 | + | |
475 | + } | |
476 | + | |
477 | + case 'pcd': | |
478 | + | |
479 | + { | |
480 | + | |
481 | + reader.addEventListener( 'load', async function ( event ) { | |
482 | + | |
483 | + const contents = event.target.result; | |
484 | + | |
485 | + const { PCDLoader } = await import( 'three/addons/loaders/PCDLoader.js' ); | |
486 | + | |
487 | + const points = new PCDLoader().parse( contents ); | |
488 | + points.name = filename; | |
489 | + | |
490 | + editor.execute( new AddObjectCommand( editor, points ) ); | |
491 | + | |
492 | + }, false ); | |
493 | + reader.readAsArrayBuffer( file ); | |
494 | + | |
495 | + break; | |
496 | + | |
497 | + } | |
498 | + | |
499 | + case 'ply': | |
500 | + | |
501 | + { | |
502 | + | |
503 | + reader.addEventListener( 'load', async function ( event ) { | |
504 | + | |
505 | + const contents = event.target.result; | |
506 | + | |
507 | + const { PLYLoader } = await import( 'three/addons/loaders/PLYLoader.js' ); | |
508 | + | |
509 | + const geometry = new PLYLoader().parse( contents ); | |
510 | + let object; | |
511 | + | |
512 | + if ( geometry.index !== null ) { | |
513 | + | |
514 | + const material = new THREE.MeshStandardMaterial(); | |
515 | + | |
516 | + object = new THREE.Mesh( geometry, material ); | |
517 | + object.name = filename; | |
518 | + | |
519 | + } else { | |
520 | + | |
521 | + const material = new THREE.PointsMaterial( { size: 0.01 } ); | |
522 | + material.vertexColors = geometry.hasAttribute( 'color' ); | |
523 | + | |
524 | + object = new THREE.Points( geometry, material ); | |
525 | + object.name = filename; | |
526 | + | |
527 | + } | |
528 | + | |
529 | + editor.execute( new AddObjectCommand( editor, object ) ); | |
530 | + | |
531 | + }, false ); | |
532 | + reader.readAsArrayBuffer( file ); | |
533 | + | |
534 | + break; | |
535 | + | |
536 | + } | |
537 | + | |
538 | + case 'stl': | |
539 | + | |
540 | + { | |
541 | + | |
542 | + reader.addEventListener( 'load', async function ( event ) { | |
543 | + | |
544 | + const contents = event.target.result; | |
545 | + | |
546 | + const { STLLoader } = await import( 'three/addons/loaders/STLLoader.js' ); | |
547 | + | |
548 | + const geometry = new STLLoader().parse( contents ); | |
549 | + const material = new THREE.MeshStandardMaterial(); | |
550 | + | |
551 | + const mesh = new THREE.Mesh( geometry, material ); | |
552 | + mesh.name = filename; | |
553 | + | |
554 | + editor.execute( new AddObjectCommand( editor, mesh ) ); | |
555 | + | |
556 | + }, false ); | |
557 | + | |
558 | + if ( reader.readAsBinaryString !== undefined ) { | |
559 | + | |
560 | + reader.readAsBinaryString( file ); | |
561 | + | |
562 | + } else { | |
563 | + | |
564 | + reader.readAsArrayBuffer( file ); | |
565 | + | |
566 | + } | |
567 | + | |
568 | + break; | |
569 | + | |
570 | + } | |
571 | + | |
572 | + case 'svg': | |
573 | + | |
574 | + { | |
575 | + | |
576 | + reader.addEventListener( 'load', async function ( event ) { | |
577 | + | |
578 | + const contents = event.target.result; | |
579 | + | |
580 | + const { SVGLoader } = await import( 'three/addons/loaders/SVGLoader.js' ); | |
581 | + | |
582 | + const loader = new SVGLoader(); | |
583 | + const paths = loader.parse( contents ).paths; | |
584 | + | |
585 | + // | |
586 | + | |
587 | + const group = new THREE.Group(); | |
588 | + group.name = filename; | |
589 | + group.scale.multiplyScalar( 0.1 ); | |
590 | + group.scale.y *= - 1; | |
591 | + | |
592 | + for ( let i = 0; i < paths.length; i ++ ) { | |
593 | + | |
594 | + const path = paths[ i ]; | |
595 | + | |
596 | + const material = new THREE.MeshBasicMaterial( { | |
597 | + color: path.color, | |
598 | + depthWrite: false | |
599 | + } ); | |
600 | + | |
601 | + const shapes = SVGLoader.createShapes( path ); | |
602 | + | |
603 | + for ( let j = 0; j < shapes.length; j ++ ) { | |
604 | + | |
605 | + const shape = shapes[ j ]; | |
606 | + | |
607 | + const geometry = new THREE.ShapeGeometry( shape ); | |
608 | + const mesh = new THREE.Mesh( geometry, material ); | |
609 | + | |
610 | + group.add( mesh ); | |
611 | + | |
612 | + } | |
613 | + | |
614 | + } | |
615 | + | |
616 | + editor.execute( new AddObjectCommand( editor, group ) ); | |
617 | + | |
618 | + }, false ); | |
619 | + reader.readAsText( file ); | |
620 | + | |
621 | + break; | |
622 | + | |
623 | + } | |
624 | + | |
625 | + case 'usdz': | |
626 | + | |
627 | + { | |
628 | + | |
629 | + reader.addEventListener( 'load', async function ( event ) { | |
630 | + | |
631 | + const contents = event.target.result; | |
632 | + | |
633 | + const { USDZLoader } = await import( 'three/addons/loaders/USDZLoader.js' ); | |
634 | + | |
635 | + const group = new USDZLoader().parse( contents ); | |
636 | + group.name = filename; | |
637 | + | |
638 | + editor.execute( new AddObjectCommand( editor, group ) ); | |
639 | + | |
640 | + }, false ); | |
641 | + reader.readAsArrayBuffer( file ); | |
642 | + | |
643 | + break; | |
644 | + | |
645 | + } | |
646 | + | |
647 | + case 'vox': | |
648 | + | |
649 | + { | |
650 | + | |
651 | + reader.addEventListener( 'load', async function ( event ) { | |
652 | + | |
653 | + const contents = event.target.result; | |
654 | + | |
655 | + const { VOXLoader, VOXMesh } = await import( 'three/addons/loaders/VOXLoader.js' ); | |
656 | + | |
657 | + const chunks = new VOXLoader().parse( contents ); | |
658 | + | |
659 | + const group = new THREE.Group(); | |
660 | + group.name = filename; | |
661 | + | |
662 | + for ( let i = 0; i < chunks.length; i ++ ) { | |
663 | + | |
664 | + const chunk = chunks[ i ]; | |
665 | + | |
666 | + const mesh = new VOXMesh( chunk ); | |
667 | + group.add( mesh ); | |
668 | + | |
669 | + } | |
670 | + | |
671 | + editor.execute( new AddObjectCommand( editor, group ) ); | |
672 | + | |
673 | + }, false ); | |
674 | + reader.readAsArrayBuffer( file ); | |
675 | + | |
676 | + break; | |
677 | + | |
678 | + } | |
679 | + | |
680 | + case 'vtk': | |
681 | + case 'vtp': | |
682 | + | |
683 | + { | |
684 | + | |
685 | + reader.addEventListener( 'load', async function ( event ) { | |
686 | + | |
687 | + const contents = event.target.result; | |
688 | + | |
689 | + const { VTKLoader } = await import( 'three/addons/loaders/VTKLoader.js' ); | |
690 | + | |
691 | + const geometry = new VTKLoader().parse( contents ); | |
692 | + const material = new THREE.MeshStandardMaterial(); | |
693 | + | |
694 | + const mesh = new THREE.Mesh( geometry, material ); | |
695 | + mesh.name = filename; | |
696 | + | |
697 | + editor.execute( new AddObjectCommand( editor, mesh ) ); | |
698 | + | |
699 | + }, false ); | |
700 | + reader.readAsArrayBuffer( file ); | |
701 | + | |
702 | + break; | |
703 | + | |
704 | + } | |
705 | + | |
706 | + case 'wrl': | |
707 | + | |
708 | + { | |
709 | + | |
710 | + reader.addEventListener( 'load', async function ( event ) { | |
711 | + | |
712 | + const contents = event.target.result; | |
713 | + | |
714 | + const { VRMLLoader } = await import( 'three/addons/loaders/VRMLLoader.js' ); | |
715 | + | |
716 | + const result = new VRMLLoader().parse( contents ); | |
717 | + | |
718 | + editor.execute( new AddObjectCommand( editor, result ) ); | |
719 | + | |
720 | + }, false ); | |
721 | + reader.readAsText( file ); | |
722 | + | |
723 | + break; | |
724 | + | |
725 | + } | |
726 | + | |
727 | + case 'xyz': | |
728 | + | |
729 | + { | |
730 | + | |
731 | + reader.addEventListener( 'load', async function ( event ) { | |
732 | + | |
733 | + const contents = event.target.result; | |
734 | + | |
735 | + const { XYZLoader } = await import( 'three/addons/loaders/XYZLoader.js' ); | |
736 | + | |
737 | + const geometry = new XYZLoader().parse( contents ); | |
738 | + | |
739 | + const material = new THREE.PointsMaterial(); | |
740 | + material.vertexColors = geometry.hasAttribute( 'color' ); | |
741 | + | |
742 | + const points = new THREE.Points( geometry, material ); | |
743 | + points.name = filename; | |
744 | + | |
745 | + editor.execute( new AddObjectCommand( editor, points ) ); | |
746 | + | |
747 | + }, false ); | |
748 | + reader.readAsText( file ); | |
749 | + | |
750 | + break; | |
751 | + | |
752 | + } | |
753 | + | |
754 | + case 'zip': | |
755 | + | |
756 | + { | |
757 | + | |
758 | + reader.addEventListener( 'load', function ( event ) { | |
759 | + | |
760 | + handleZIP( event.target.result ); | |
761 | + | |
762 | + }, false ); | |
763 | + reader.readAsArrayBuffer( file ); | |
764 | + | |
765 | + break; | |
766 | + | |
767 | + } | |
768 | + | |
769 | + default: | |
770 | + | |
771 | + console.error( 'Unsupported file format (' + extension + ').' ); | |
772 | + | |
773 | + break; | |
774 | + | |
775 | + } | |
776 | + | |
777 | + }; | |
778 | + | |
779 | + function handleJSON( data ) { | |
780 | + | |
781 | + if ( data.metadata === undefined ) { // 2.0 | |
782 | + | |
783 | + data.metadata = { type: 'Geometry' }; | |
784 | + | |
785 | + } | |
786 | + | |
787 | + if ( data.metadata.type === undefined ) { // 3.0 | |
788 | + | |
789 | + data.metadata.type = 'Geometry'; | |
790 | + | |
791 | + } | |
792 | + | |
793 | + if ( data.metadata.formatVersion !== undefined ) { | |
794 | + | |
795 | + data.metadata.version = data.metadata.formatVersion; | |
796 | + | |
797 | + } | |
798 | + | |
799 | + switch ( data.metadata.type.toLowerCase() ) { | |
800 | + | |
801 | + case 'buffergeometry': | |
802 | + | |
803 | + { | |
804 | + | |
805 | + const loader = new THREE.BufferGeometryLoader(); | |
806 | + const result = loader.parse( data ); | |
807 | + | |
808 | + const mesh = new THREE.Mesh( result ); | |
809 | + | |
810 | + editor.execute( new AddObjectCommand( editor, mesh ) ); | |
811 | + | |
812 | + break; | |
813 | + | |
814 | + } | |
815 | + | |
816 | + case 'geometry': | |
817 | + | |
818 | + console.error( 'Loader: "Geometry" is no longer supported.' ); | |
819 | + | |
820 | + break; | |
821 | + | |
822 | + case 'object': | |
823 | + | |
824 | + { | |
825 | + | |
826 | + const loader = new THREE.ObjectLoader(); | |
827 | + loader.setResourcePath( scope.texturePath ); | |
828 | + | |
829 | + loader.parse( data, function ( result ) { | |
830 | + | |
831 | + editor.execute( new AddObjectCommand( editor, result ) ); | |
832 | + | |
833 | + } ); | |
834 | + | |
835 | + break; | |
836 | + | |
837 | + } | |
838 | + | |
839 | + case 'app': | |
840 | + | |
841 | + editor.fromJSON( data ); | |
842 | + | |
843 | + break; | |
844 | + | |
845 | + } | |
846 | + | |
847 | + } | |
848 | + | |
849 | + async function handleZIP( contents ) { | |
850 | + | |
851 | + const zip = unzipSync( new Uint8Array( contents ) ); | |
852 | + | |
853 | + const manager = new THREE.LoadingManager(); | |
854 | + manager.setURLModifier( function ( url ) { | |
855 | + | |
856 | + const file = zip[ url ]; | |
857 | + | |
858 | + if ( file ) { | |
859 | + | |
860 | + console.log( 'Loading', url ); | |
861 | + | |
862 | + const blob = new Blob( [ file.buffer ], { type: 'application/octet-stream' } ); | |
863 | + return URL.createObjectURL( blob ); | |
864 | + | |
865 | + } | |
866 | + | |
867 | + return url; | |
868 | + | |
869 | + } ); | |
870 | + | |
871 | + // Poly | |
872 | + | |
873 | + if ( zip[ 'model.obj' ] && zip[ 'materials.mtl' ] ) { | |
874 | + | |
875 | + const { MTLLoader } = await import( 'three/addons/loaders/MTLLoader.js' ); | |
876 | + const { OBJLoader } = await import( 'three/addons/loaders/OBJLoader.js' ); | |
877 | + | |
878 | + const materials = new MTLLoader( manager ).parse( strFromU8( zip[ 'materials.mtl' ] ) ); | |
879 | + const object = new OBJLoader().setMaterials( materials ).parse( strFromU8( zip[ 'model.obj' ] ) ); | |
880 | + | |
881 | + editor.execute( new AddObjectCommand( editor, object ) ); | |
882 | + return; | |
883 | + | |
884 | + } | |
885 | + | |
886 | + // | |
887 | + | |
888 | + for ( const path in zip ) { | |
889 | + | |
890 | + const file = zip[ path ]; | |
891 | + | |
892 | + const extension = path.split( '.' ).pop().toLowerCase(); | |
893 | + | |
894 | + switch ( extension ) { | |
895 | + | |
896 | + case 'fbx': | |
897 | + | |
898 | + { | |
899 | + | |
900 | + const { FBXLoader } = await import( 'three/addons/loaders/FBXLoader.js' ); | |
901 | + | |
902 | + const loader = new FBXLoader( manager ); | |
903 | + const object = loader.parse( file.buffer ); | |
904 | + | |
905 | + editor.execute( new AddObjectCommand( editor, object ) ); | |
906 | + | |
907 | + break; | |
908 | + | |
909 | + } | |
910 | + | |
911 | + case 'glb': | |
912 | + | |
913 | + { | |
914 | + | |
915 | + const loader = await createGLTFLoader(); | |
916 | + | |
917 | + loader.parse( file.buffer, '', function ( result ) { | |
918 | + | |
919 | + const scene = result.scene; | |
920 | + | |
921 | + scene.animations.push( ...result.animations ); | |
922 | + editor.execute( new AddObjectCommand( editor, scene ) ); | |
923 | + | |
924 | + loader.dracoLoader.dispose(); | |
925 | + loader.ktx2Loader.dispose(); | |
926 | + | |
927 | + } ); | |
928 | + | |
929 | + break; | |
930 | + | |
931 | + } | |
932 | + | |
933 | + case 'gltf': | |
934 | + | |
935 | + { | |
936 | + | |
937 | + const loader = await createGLTFLoader( manager ); | |
938 | + | |
939 | + loader.parse( strFromU8( file ), '', function ( result ) { | |
940 | + | |
941 | + const scene = result.scene; | |
942 | + | |
943 | + scene.animations.push( ...result.animations ); | |
944 | + editor.execute( new AddObjectCommand( editor, scene ) ); | |
945 | + | |
946 | + loader.dracoLoader.dispose(); | |
947 | + loader.ktx2Loader.dispose(); | |
948 | + | |
949 | + } ); | |
950 | + | |
951 | + break; | |
952 | + | |
953 | + } | |
954 | + | |
955 | + } | |
956 | + | |
957 | + } | |
958 | + | |
959 | + } | |
960 | + | |
961 | + async function createGLTFLoader( manager ) { | |
962 | + | |
963 | + const { GLTFLoader } = await import( 'three/addons/loaders/GLTFLoader.js' ); | |
964 | + const { DRACOLoader } = await import( 'three/addons/loaders/DRACOLoader.js' ); | |
965 | + const { KTX2Loader } = await import( 'three/addons/loaders/KTX2Loader.js' ); | |
966 | + const { MeshoptDecoder } = await import( 'three/addons/libs/meshopt_decoder.module.js' ); | |
967 | + | |
968 | + const dracoLoader = new DRACOLoader(); | |
969 | + dracoLoader.setDecoderPath( '../examples/jsm/libs/draco/gltf/' ); | |
970 | + | |
971 | + const ktx2Loader = new KTX2Loader( manager ); | |
972 | + ktx2Loader.setTranscoderPath( '../examples/jsm/libs/basis/' ); | |
973 | + | |
974 | + editor.signals.rendererDetectKTX2Support.dispatch( ktx2Loader ); | |
975 | + | |
976 | + const loader = new GLTFLoader( manager ); | |
977 | + loader.setDRACOLoader( dracoLoader ); | |
978 | + loader.setKTX2Loader( ktx2Loader ); | |
979 | + loader.setMeshoptDecoder( MeshoptDecoder ); | |
980 | + | |
981 | + return loader; | |
982 | + | |
983 | + } | |
984 | + | |
985 | +} | |
986 | + | |
987 | +export { Loader }; | |
\ No newline at end of file | ... | ... |
editor/js/LoaderUtils.js
0 → 100644
1 | +const LoaderUtils = { | |
2 | + | |
3 | + createFilesMap: function ( files ) { | |
4 | + | |
5 | + const map = {}; | |
6 | + | |
7 | + for ( let i = 0; i < files.length; i ++ ) { | |
8 | + | |
9 | + const file = files[ i ]; | |
10 | + map[ file.name ] = file; | |
11 | + | |
12 | + } | |
13 | + | |
14 | + return map; | |
15 | + | |
16 | + }, | |
17 | + | |
18 | + getFilesFromItemList: function ( items, onDone ) { | |
19 | + | |
20 | + // TOFIX: setURLModifier() breaks when the file being loaded is not in root | |
21 | + | |
22 | + let itemsCount = 0; | |
23 | + let itemsTotal = 0; | |
24 | + | |
25 | + const files = []; | |
26 | + const filesMap = {}; | |
27 | + | |
28 | + function onEntryHandled() { | |
29 | + | |
30 | + itemsCount ++; | |
31 | + | |
32 | + if ( itemsCount === itemsTotal ) { | |
33 | + | |
34 | + onDone( files, filesMap ); | |
35 | + | |
36 | + } | |
37 | + | |
38 | + } | |
39 | + | |
40 | + function handleEntry( entry ) { | |
41 | + | |
42 | + if ( entry.isDirectory ) { | |
43 | + | |
44 | + const reader = entry.createReader(); | |
45 | + reader.readEntries( function ( entries ) { | |
46 | + | |
47 | + for ( let i = 0; i < entries.length; i ++ ) { | |
48 | + | |
49 | + handleEntry( entries[ i ] ); | |
50 | + | |
51 | + } | |
52 | + | |
53 | + onEntryHandled(); | |
54 | + | |
55 | + } ); | |
56 | + | |
57 | + } else if ( entry.isFile ) { | |
58 | + | |
59 | + entry.file( function ( file ) { | |
60 | + | |
61 | + files.push( file ); | |
62 | + | |
63 | + filesMap[ entry.fullPath.slice( 1 ) ] = file; | |
64 | + onEntryHandled(); | |
65 | + | |
66 | + } ); | |
67 | + | |
68 | + } | |
69 | + | |
70 | + itemsTotal ++; | |
71 | + | |
72 | + } | |
73 | + | |
74 | + for ( let i = 0; i < items.length; i ++ ) { | |
75 | + | |
76 | + const item = items[ i ]; | |
77 | + | |
78 | + if ( item.kind === 'file' ) { | |
79 | + | |
80 | + handleEntry( item.webkitGetAsEntry() ); | |
81 | + | |
82 | + } | |
83 | + | |
84 | + } | |
85 | + | |
86 | + } | |
87 | + | |
88 | +}; | |
89 | + | |
90 | +export { LoaderUtils }; | ... | ... |