From 80e4355f8ac3a7a89089506b252712ebf0258dd9 Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Mon, 10 Jun 2024 10:44:07 +0200 Subject: [PATCH] remove mapbox code --- README.md | 4 - i3dm.export.sln | 2 - samples/bordeaux/Mapbox3DTiles.js | 55235 -------------- samples/bordeaux/index.html | 73 - samples/bordeaux/old/Mapbox3DTiles.js | 60425 ---------------- samples/bordeaux/old/index.html | 67 - samples/traffic_lights/mapbox/Box.glb | Bin 1664 -> 0 bytes .../traffic_lights/mapbox/Mapbox3DTiles.js | 60425 ---------------- samples/traffic_lights/mapbox/index.html | 67 - .../mapbox/output/tiles/0_0_5_9.i3dm | Bin 2728 -> 0 bytes .../mapbox/output/tiles/0_0_6_9.i3dm | Bin 1952 -> 0 bytes .../mapbox/output/tiles/0_0_8_9.i3dm | Bin 2152 -> 0 bytes .../mapbox/output/tiles/0_0_9_9.i3dm | Bin 1952 -> 0 bytes .../mapbox/output/tiles/0_1_0_3.i3dm | Bin 1952 -> 0 bytes .../mapbox/output/tiles/0_1_0_4.i3dm | Bin 4648 -> 0 bytes .../mapbox/output/tiles/0_1_0_6.i3dm | Bin 1952 -> 0 bytes .../mapbox/output/tiles/0_1_0_7.i3dm | Bin 2288 -> 0 bytes .../mapbox/output/tiles/0_1_0_8.i3dm | Bin 3104 -> 0 bytes .../mapbox/output/tiles/0_1_0_9.i3dm | Bin 1952 -> 0 bytes .../mapbox/output/tiles/0_1_1_2.i3dm | Bin 2664 -> 0 bytes .../mapbox/output/tiles/0_1_1_3.i3dm | Bin 10880 -> 0 bytes .../mapbox/output/tiles/0_1_1_4.i3dm | Bin 5312 -> 0 bytes .../mapbox/output/tiles/0_1_1_5.i3dm | Bin 1952 -> 0 bytes .../mapbox/output/tiles/0_1_1_6.i3dm | Bin 2200 -> 0 bytes .../mapbox/output/tiles/0_1_1_7.i3dm | Bin 4168 -> 0 bytes .../mapbox/output/tiles/0_1_1_8.i3dm | Bin 2008 -> 0 bytes .../mapbox/output/tiles/0_1_1_9.i3dm | Bin 2544 -> 0 bytes .../mapbox/output/tiles/0_1_2_2.i3dm | Bin 6856 -> 0 bytes .../mapbox/output/tiles/0_1_2_3.i3dm | Bin 11256 -> 0 bytes .../mapbox/output/tiles/0_1_2_4.i3dm | Bin 10336 -> 0 bytes .../mapbox/output/tiles/0_1_2_5.i3dm | Bin 4848 -> 0 bytes .../mapbox/output/tiles/0_1_2_6.i3dm | Bin 3928 -> 0 bytes .../mapbox/output/tiles/0_1_2_7.i3dm | Bin 2672 -> 0 bytes .../mapbox/output/tiles/0_1_2_8.i3dm | Bin 2712 -> 0 bytes .../mapbox/output/tiles/0_1_2_9.i3dm | Bin 2144 -> 0 bytes .../mapbox/output/tiles/0_1_3_1.i3dm | Bin 6368 -> 0 bytes .../mapbox/output/tiles/0_1_3_2.i3dm | Bin 9504 -> 0 bytes .../mapbox/output/tiles/0_1_3_3.i3dm | Bin 13864 -> 0 bytes .../mapbox/output/tiles/0_1_3_4.i3dm | Bin 17336 -> 0 bytes .../mapbox/output/tiles/0_1_3_5.i3dm | Bin 9704 -> 0 bytes .../mapbox/output/tiles/0_1_3_6.i3dm | Bin 2408 -> 0 bytes .../mapbox/output/tiles/0_1_3_7.i3dm | Bin 3448 -> 0 bytes .../mapbox/output/tiles/0_1_3_8.i3dm | Bin 10760 -> 0 bytes .../mapbox/output/tiles/0_1_3_9.i3dm | Bin 2608 -> 0 bytes .../mapbox/output/tiles/0_1_4_0.i3dm | Bin 2088 -> 0 bytes .../mapbox/output/tiles/0_1_4_1.i3dm | Bin 6168 -> 0 bytes .../mapbox/output/tiles/0_1_4_2.i3dm | Bin 10104 -> 0 bytes .../mapbox/output/tiles/0_1_4_3.i3dm | Bin 18080 -> 0 bytes .../mapbox/output/tiles/0_1_4_4.i3dm | Bin 26432 -> 0 bytes .../mapbox/output/tiles/0_1_4_5.i3dm | Bin 14440 -> 0 bytes .../mapbox/output/tiles/0_1_4_6.i3dm | Bin 5856 -> 0 bytes .../mapbox/output/tiles/0_1_4_7.i3dm | Bin 11328 -> 0 bytes .../mapbox/output/tiles/0_1_4_8.i3dm | Bin 15856 -> 0 bytes .../mapbox/output/tiles/0_1_4_9.i3dm | Bin 4888 -> 0 bytes .../mapbox/output/tiles/0_1_5_0.i3dm | Bin 3256 -> 0 bytes .../mapbox/output/tiles/0_1_5_1.i3dm | Bin 12448 -> 0 bytes .../mapbox/output/tiles/0_1_5_2.i3dm | Bin 20936 -> 0 bytes .../mapbox/output/tiles/0_1_5_3.i3dm | Bin 18176 -> 0 bytes .../mapbox/output/tiles/0_1_5_4.i3dm | Bin 17920 -> 0 bytes .../mapbox/output/tiles/0_1_5_5.i3dm | Bin 10704 -> 0 bytes .../mapbox/output/tiles/0_1_5_6.i3dm | Bin 3952 -> 0 bytes .../mapbox/output/tiles/0_1_5_7.i3dm | Bin 15880 -> 0 bytes .../mapbox/output/tiles/0_1_5_8.i3dm | Bin 9016 -> 0 bytes .../mapbox/output/tiles/0_1_5_9.i3dm | Bin 3792 -> 0 bytes .../mapbox/output/tiles/0_1_6_0.i3dm | Bin 8048 -> 0 bytes .../mapbox/output/tiles/0_1_6_1.i3dm | Bin 5336 -> 0 bytes .../mapbox/output/tiles/0_1_6_2.i3dm | Bin 15968 -> 0 bytes .../mapbox/output/tiles/0_1_6_3.i3dm | Bin 10880 -> 0 bytes .../mapbox/output/tiles/0_1_6_4.i3dm | Bin 11656 -> 0 bytes .../mapbox/output/tiles/0_1_6_5.i3dm | Bin 1952 -> 0 bytes .../mapbox/output/tiles/0_1_6_6.i3dm | Bin 2336 -> 0 bytes .../mapbox/output/tiles/0_1_6_7.i3dm | Bin 14032 -> 0 bytes .../mapbox/output/tiles/0_1_6_8.i3dm | Bin 17608 -> 0 bytes .../mapbox/output/tiles/0_1_6_9.i3dm | Bin 11128 -> 0 bytes .../mapbox/output/tiles/0_1_7_0.i3dm | Bin 3000 -> 0 bytes .../mapbox/output/tiles/0_1_7_1.i3dm | Bin 8664 -> 0 bytes .../mapbox/output/tiles/0_1_7_2.i3dm | Bin 12168 -> 0 bytes .../mapbox/output/tiles/0_1_7_3.i3dm | Bin 12552 -> 0 bytes .../mapbox/output/tiles/0_1_7_4.i3dm | Bin 18800 -> 0 bytes .../mapbox/output/tiles/0_1_7_5.i3dm | Bin 12496 -> 0 bytes .../mapbox/output/tiles/0_1_7_6.i3dm | Bin 6360 -> 0 bytes .../mapbox/output/tiles/0_1_7_7.i3dm | Bin 15288 -> 0 bytes .../mapbox/output/tiles/0_1_7_8.i3dm | Bin 17600 -> 0 bytes .../mapbox/output/tiles/0_1_7_9.i3dm | Bin 8192 -> 0 bytes .../mapbox/output/tiles/0_1_8_0.i3dm | Bin 3464 -> 0 bytes .../mapbox/output/tiles/0_1_8_1.i3dm | Bin 2928 -> 0 bytes .../mapbox/output/tiles/0_1_8_2.i3dm | Bin 12832 -> 0 bytes .../mapbox/output/tiles/0_1_8_3.i3dm | Bin 9888 -> 0 bytes .../mapbox/output/tiles/0_1_8_4.i3dm | Bin 14184 -> 0 bytes .../mapbox/output/tiles/0_1_8_5.i3dm | Bin 12176 -> 0 bytes .../mapbox/output/tiles/0_1_8_6.i3dm | Bin 14464 -> 0 bytes .../mapbox/output/tiles/0_1_8_7.i3dm | Bin 17360 -> 0 bytes .../mapbox/output/tiles/0_1_8_8.i3dm | Bin 20384 -> 0 bytes .../mapbox/output/tiles/0_1_8_9.i3dm | Bin 7328 -> 0 bytes .../mapbox/output/tiles/0_1_9_0.i3dm | Bin 2528 -> 0 bytes .../mapbox/output/tiles/0_1_9_1.i3dm | Bin 4816 -> 0 bytes .../mapbox/output/tiles/0_1_9_2.i3dm | Bin 12576 -> 0 bytes .../mapbox/output/tiles/0_1_9_3.i3dm | Bin 12680 -> 0 bytes .../mapbox/output/tiles/0_1_9_4.i3dm | Bin 17264 -> 0 bytes .../mapbox/output/tiles/0_1_9_5.i3dm | Bin 13440 -> 0 bytes .../mapbox/output/tiles/0_1_9_6.i3dm | Bin 8560 -> 0 bytes .../mapbox/output/tiles/0_1_9_7.i3dm | Bin 16960 -> 0 bytes .../mapbox/output/tiles/0_1_9_8.i3dm | Bin 22952 -> 0 bytes .../mapbox/output/tiles/0_1_9_9.i3dm | Bin 9600 -> 0 bytes .../mapbox/output/tiles/0_2_9_0.i3dm | Bin 4040 -> 0 bytes .../mapbox/output/tiles/1_0_0_7.i3dm | Bin 3408 -> 0 bytes .../mapbox/output/tiles/1_0_0_8.i3dm | Bin 4088 -> 0 bytes .../mapbox/output/tiles/1_0_0_9.i3dm | Bin 6272 -> 0 bytes .../mapbox/output/tiles/1_0_1_7.i3dm | Bin 2600 -> 0 bytes .../mapbox/output/tiles/1_0_1_8.i3dm | Bin 11056 -> 0 bytes .../mapbox/output/tiles/1_0_1_9.i3dm | Bin 5112 -> 0 bytes .../mapbox/output/tiles/1_0_2_7.i3dm | Bin 2264 -> 0 bytes .../mapbox/output/tiles/1_0_2_8.i3dm | Bin 20560 -> 0 bytes .../mapbox/output/tiles/1_0_2_9.i3dm | Bin 14264 -> 0 bytes .../mapbox/output/tiles/1_0_3_7.i3dm | Bin 1944 -> 0 bytes .../mapbox/output/tiles/1_0_3_8.i3dm | Bin 17488 -> 0 bytes .../mapbox/output/tiles/1_0_3_9.i3dm | Bin 18136 -> 0 bytes .../mapbox/output/tiles/1_0_4_7.i3dm | Bin 2224 -> 0 bytes .../mapbox/output/tiles/1_0_4_8.i3dm | Bin 12144 -> 0 bytes .../mapbox/output/tiles/1_0_4_9.i3dm | Bin 11704 -> 0 bytes .../mapbox/output/tiles/1_0_5_7.i3dm | Bin 2000 -> 0 bytes .../mapbox/output/tiles/1_0_5_8.i3dm | Bin 3856 -> 0 bytes .../mapbox/output/tiles/1_0_5_9.i3dm | Bin 2936 -> 0 bytes .../mapbox/output/tiles/1_0_6_7.i3dm | Bin 1944 -> 0 bytes .../mapbox/output/tiles/1_0_6_8.i3dm | Bin 2216 -> 0 bytes .../mapbox/output/tiles/1_0_6_9.i3dm | Bin 2584 -> 0 bytes .../mapbox/output/tiles/1_0_7_8.i3dm | Bin 4024 -> 0 bytes .../mapbox/output/tiles/1_0_7_9.i3dm | Bin 5232 -> 0 bytes .../mapbox/output/tiles/1_0_8_8.i3dm | Bin 7560 -> 0 bytes .../mapbox/output/tiles/1_0_8_9.i3dm | Bin 7528 -> 0 bytes .../mapbox/output/tiles/1_0_9_4.i3dm | Bin 3840 -> 0 bytes .../mapbox/output/tiles/1_0_9_5.i3dm | Bin 14592 -> 0 bytes .../mapbox/output/tiles/1_0_9_6.i3dm | Bin 12024 -> 0 bytes .../mapbox/output/tiles/1_0_9_8.i3dm | Bin 2976 -> 0 bytes .../mapbox/output/tiles/1_0_9_9.i3dm | Bin 2328 -> 0 bytes .../mapbox/output/tiles/1_1_0_0.i3dm | Bin 8952 -> 0 bytes .../mapbox/output/tiles/1_1_0_1.i3dm | Bin 11592 -> 0 bytes .../mapbox/output/tiles/1_1_0_2.i3dm | Bin 13848 -> 0 bytes .../mapbox/output/tiles/1_1_0_3.i3dm | Bin 24480 -> 0 bytes .../mapbox/output/tiles/1_1_0_4.i3dm | Bin 21752 -> 0 bytes .../mapbox/output/tiles/1_1_0_5.i3dm | Bin 19848 -> 0 bytes .../mapbox/output/tiles/1_1_0_6.i3dm | Bin 25120 -> 0 bytes .../mapbox/output/tiles/1_1_0_7.i3dm | Bin 13504 -> 0 bytes .../mapbox/output/tiles/1_1_0_8.i3dm | Bin 26104 -> 0 bytes .../mapbox/output/tiles/1_1_0_9.i3dm | Bin 11984 -> 0 bytes .../mapbox/output/tiles/1_1_1_0.i3dm | Bin 8432 -> 0 bytes .../mapbox/output/tiles/1_1_1_1.i3dm | Bin 6072 -> 0 bytes .../mapbox/output/tiles/1_1_1_2.i3dm | Bin 25288 -> 0 bytes .../mapbox/output/tiles/1_1_1_3.i3dm | Bin 18872 -> 0 bytes .../mapbox/output/tiles/1_1_1_4.i3dm | Bin 12368 -> 0 bytes .../mapbox/output/tiles/1_1_1_5.i3dm | Bin 21512 -> 0 bytes .../mapbox/output/tiles/1_1_1_6.i3dm | Bin 21288 -> 0 bytes .../mapbox/output/tiles/1_1_1_7.i3dm | Bin 20344 -> 0 bytes .../mapbox/output/tiles/1_1_1_8.i3dm | Bin 8952 -> 0 bytes .../mapbox/output/tiles/1_1_1_9.i3dm | Bin 7944 -> 0 bytes .../mapbox/output/tiles/1_1_2_0.i3dm | Bin 14664 -> 0 bytes .../mapbox/output/tiles/1_1_2_1.i3dm | Bin 12032 -> 0 bytes .../mapbox/output/tiles/1_1_2_2.i3dm | Bin 19952 -> 0 bytes .../mapbox/output/tiles/1_1_2_3.i3dm | Bin 22152 -> 0 bytes .../mapbox/output/tiles/1_1_2_4.i3dm | Bin 9536 -> 0 bytes .../mapbox/output/tiles/1_1_2_5.i3dm | Bin 23904 -> 0 bytes .../mapbox/output/tiles/1_1_2_6.i3dm | Bin 22344 -> 0 bytes .../mapbox/output/tiles/1_1_2_7.i3dm | Bin 23272 -> 0 bytes .../mapbox/output/tiles/1_1_2_8.i3dm | Bin 17976 -> 0 bytes .../mapbox/output/tiles/1_1_2_9.i3dm | Bin 13376 -> 0 bytes .../mapbox/output/tiles/1_1_3_0.i3dm | Bin 10552 -> 0 bytes .../mapbox/output/tiles/1_1_3_1.i3dm | Bin 9384 -> 0 bytes .../mapbox/output/tiles/1_1_3_2.i3dm | Bin 21992 -> 0 bytes .../mapbox/output/tiles/1_1_3_3.i3dm | Bin 21176 -> 0 bytes .../mapbox/output/tiles/1_1_3_4.i3dm | Bin 21544 -> 0 bytes .../mapbox/output/tiles/1_1_3_5.i3dm | Bin 36768 -> 0 bytes .../mapbox/output/tiles/1_1_3_6.i3dm | Bin 33560 -> 0 bytes .../mapbox/output/tiles/1_1_3_7.i3dm | Bin 41936 -> 0 bytes .../mapbox/output/tiles/1_1_3_8.i3dm | Bin 38160 -> 0 bytes .../mapbox/output/tiles/1_1_3_9.i3dm | Bin 32048 -> 0 bytes .../mapbox/output/tiles/1_1_4_0.i3dm | Bin 11256 -> 0 bytes .../mapbox/output/tiles/1_1_4_1.i3dm | Bin 12696 -> 0 bytes .../mapbox/output/tiles/1_1_4_2.i3dm | Bin 24520 -> 0 bytes .../mapbox/output/tiles/1_1_4_3.i3dm | Bin 29816 -> 0 bytes .../mapbox/output/tiles/1_1_4_4.i3dm | Bin 29736 -> 0 bytes .../mapbox/output/tiles/1_1_4_5.i3dm | Bin 38016 -> 0 bytes .../mapbox/output/tiles/1_1_4_6.i3dm | Bin 39752 -> 0 bytes .../mapbox/output/tiles/1_1_4_7.i3dm | Bin 36960 -> 0 bytes .../mapbox/output/tiles/1_1_4_8.i3dm | Bin 37552 -> 0 bytes .../mapbox/output/tiles/1_1_4_9.i3dm | Bin 29432 -> 0 bytes .../mapbox/output/tiles/1_1_5_0.i3dm | Bin 3120 -> 0 bytes .../mapbox/output/tiles/1_1_5_1.i3dm | Bin 20272 -> 0 bytes .../mapbox/output/tiles/1_1_5_2.i3dm | Bin 20408 -> 0 bytes .../mapbox/output/tiles/1_1_5_3.i3dm | Bin 22952 -> 0 bytes .../mapbox/output/tiles/1_1_5_4.i3dm | Bin 26560 -> 0 bytes .../mapbox/output/tiles/1_1_5_5.i3dm | Bin 30896 -> 0 bytes .../mapbox/output/tiles/1_1_5_6.i3dm | Bin 43640 -> 0 bytes .../mapbox/output/tiles/1_1_5_7.i3dm | Bin 49536 -> 0 bytes .../mapbox/output/tiles/1_1_5_8.i3dm | Bin 26320 -> 0 bytes .../mapbox/output/tiles/1_1_5_9.i3dm | Bin 4680 -> 0 bytes .../mapbox/output/tiles/1_1_6_0.i3dm | Bin 6712 -> 0 bytes .../mapbox/output/tiles/1_1_6_1.i3dm | Bin 13584 -> 0 bytes .../mapbox/output/tiles/1_1_6_2.i3dm | Bin 17944 -> 0 bytes .../mapbox/output/tiles/1_1_6_3.i3dm | Bin 16120 -> 0 bytes .../mapbox/output/tiles/1_1_6_4.i3dm | Bin 24200 -> 0 bytes .../mapbox/output/tiles/1_1_6_5.i3dm | Bin 35704 -> 0 bytes .../mapbox/output/tiles/1_1_6_6.i3dm | Bin 37296 -> 0 bytes .../mapbox/output/tiles/1_1_6_7.i3dm | Bin 26032 -> 0 bytes .../mapbox/output/tiles/1_1_6_8.i3dm | Bin 7464 -> 0 bytes .../mapbox/output/tiles/1_1_6_9.i3dm | Bin 13256 -> 0 bytes .../mapbox/output/tiles/1_1_7_0.i3dm | Bin 5280 -> 0 bytes .../mapbox/output/tiles/1_1_7_1.i3dm | Bin 10336 -> 0 bytes .../mapbox/output/tiles/1_1_7_2.i3dm | Bin 17904 -> 0 bytes .../mapbox/output/tiles/1_1_7_3.i3dm | Bin 18112 -> 0 bytes .../mapbox/output/tiles/1_1_7_4.i3dm | Bin 17616 -> 0 bytes .../mapbox/output/tiles/1_1_7_5.i3dm | Bin 20696 -> 0 bytes .../mapbox/output/tiles/1_1_7_6.i3dm | Bin 26672 -> 0 bytes .../mapbox/output/tiles/1_1_7_7.i3dm | Bin 10136 -> 0 bytes .../mapbox/output/tiles/1_1_7_8.i3dm | Bin 9200 -> 0 bytes .../mapbox/output/tiles/1_1_7_9.i3dm | Bin 25856 -> 0 bytes .../mapbox/output/tiles/1_1_8_0.i3dm | Bin 9600 -> 0 bytes .../mapbox/output/tiles/1_1_8_1.i3dm | Bin 6400 -> 0 bytes .../mapbox/output/tiles/1_1_8_2.i3dm | Bin 8920 -> 0 bytes .../mapbox/output/tiles/1_1_8_3.i3dm | Bin 16152 -> 0 bytes .../mapbox/output/tiles/1_1_8_4.i3dm | Bin 16384 -> 0 bytes .../mapbox/output/tiles/1_1_8_5.i3dm | Bin 20192 -> 0 bytes .../mapbox/output/tiles/1_1_8_6.i3dm | Bin 24072 -> 0 bytes .../mapbox/output/tiles/1_1_8_7.i3dm | Bin 15864 -> 0 bytes .../mapbox/output/tiles/1_1_8_8.i3dm | Bin 7376 -> 0 bytes .../mapbox/output/tiles/1_1_8_9.i3dm | Bin 10944 -> 0 bytes .../mapbox/output/tiles/1_1_9_0.i3dm | Bin 7376 -> 0 bytes .../mapbox/output/tiles/1_1_9_1.i3dm | Bin 3888 -> 0 bytes .../mapbox/output/tiles/1_1_9_2.i3dm | Bin 11232 -> 0 bytes .../mapbox/output/tiles/1_1_9_3.i3dm | Bin 12344 -> 0 bytes .../mapbox/output/tiles/1_1_9_4.i3dm | Bin 10816 -> 0 bytes .../mapbox/output/tiles/1_1_9_5.i3dm | Bin 21888 -> 0 bytes .../mapbox/output/tiles/1_1_9_6.i3dm | Bin 17552 -> 0 bytes .../mapbox/output/tiles/1_1_9_7.i3dm | Bin 15440 -> 0 bytes .../mapbox/output/tiles/1_1_9_8.i3dm | Bin 6064 -> 0 bytes .../mapbox/output/tiles/1_1_9_9.i3dm | Bin 2496 -> 0 bytes .../mapbox/output/tiles/1_2_0_0.i3dm | Bin 4440 -> 0 bytes .../mapbox/output/tiles/1_2_1_0.i3dm | Bin 2368 -> 0 bytes .../mapbox/output/tiles/1_2_1_5.i3dm | Bin 2008 -> 0 bytes .../mapbox/output/tiles/1_2_1_6.i3dm | Bin 2008 -> 0 bytes .../mapbox/output/tiles/1_2_1_7.i3dm | Bin 2464 -> 0 bytes .../mapbox/output/tiles/1_2_2_0.i3dm | Bin 9472 -> 0 bytes .../mapbox/output/tiles/1_2_2_1.i3dm | Bin 2320 -> 0 bytes .../mapbox/output/tiles/1_2_2_4.i3dm | Bin 3192 -> 0 bytes .../mapbox/output/tiles/1_2_2_5.i3dm | Bin 4424 -> 0 bytes .../mapbox/output/tiles/1_2_2_6.i3dm | Bin 2064 -> 0 bytes .../mapbox/output/tiles/1_2_3_0.i3dm | Bin 16304 -> 0 bytes .../mapbox/output/tiles/1_2_3_1.i3dm | Bin 2192 -> 0 bytes .../mapbox/output/tiles/1_2_3_3.i3dm | Bin 2080 -> 0 bytes .../mapbox/output/tiles/1_2_3_4.i3dm | Bin 6960 -> 0 bytes .../mapbox/output/tiles/1_2_3_5.i3dm | Bin 9432 -> 0 bytes .../mapbox/output/tiles/1_2_3_6.i3dm | Bin 4632 -> 0 bytes .../mapbox/output/tiles/1_2_4_0.i3dm | Bin 10288 -> 0 bytes .../mapbox/output/tiles/1_2_4_2.i3dm | Bin 3912 -> 0 bytes .../mapbox/output/tiles/1_2_4_3.i3dm | Bin 10056 -> 0 bytes .../mapbox/output/tiles/1_2_4_4.i3dm | Bin 9792 -> 0 bytes .../mapbox/output/tiles/1_2_4_5.i3dm | Bin 16456 -> 0 bytes .../mapbox/output/tiles/1_2_4_6.i3dm | Bin 6376 -> 0 bytes .../mapbox/output/tiles/1_2_5_0.i3dm | Bin 2216 -> 0 bytes .../mapbox/output/tiles/1_2_5_1.i3dm | Bin 3528 -> 0 bytes .../mapbox/output/tiles/1_2_5_2.i3dm | Bin 8120 -> 0 bytes .../mapbox/output/tiles/1_2_5_3.i3dm | Bin 9712 -> 0 bytes .../mapbox/output/tiles/1_2_5_4.i3dm | Bin 8832 -> 0 bytes .../mapbox/output/tiles/1_2_5_5.i3dm | Bin 13224 -> 0 bytes .../mapbox/output/tiles/1_2_5_6.i3dm | Bin 5984 -> 0 bytes .../mapbox/output/tiles/1_2_6_0.i3dm | Bin 16392 -> 0 bytes .../mapbox/output/tiles/1_2_6_1.i3dm | Bin 9640 -> 0 bytes .../mapbox/output/tiles/1_2_6_2.i3dm | Bin 9664 -> 0 bytes .../mapbox/output/tiles/1_2_6_3.i3dm | Bin 5528 -> 0 bytes .../mapbox/output/tiles/1_2_6_4.i3dm | Bin 3736 -> 0 bytes .../mapbox/output/tiles/1_2_6_5.i3dm | Bin 7480 -> 0 bytes .../mapbox/output/tiles/1_2_6_6.i3dm | Bin 2072 -> 0 bytes .../mapbox/output/tiles/1_2_7_0.i3dm | Bin 24576 -> 0 bytes .../mapbox/output/tiles/1_2_7_1.i3dm | Bin 19376 -> 0 bytes .../mapbox/output/tiles/1_2_7_2.i3dm | Bin 10096 -> 0 bytes .../mapbox/output/tiles/1_2_7_3.i3dm | Bin 15432 -> 0 bytes .../mapbox/output/tiles/1_2_7_4.i3dm | Bin 9392 -> 0 bytes .../mapbox/output/tiles/1_2_7_5.i3dm | Bin 4064 -> 0 bytes .../mapbox/output/tiles/1_2_8_0.i3dm | Bin 11760 -> 0 bytes .../mapbox/output/tiles/1_2_8_1.i3dm | Bin 12264 -> 0 bytes .../mapbox/output/tiles/1_2_8_2.i3dm | Bin 3992 -> 0 bytes .../mapbox/output/tiles/1_2_8_3.i3dm | Bin 9144 -> 0 bytes .../mapbox/output/tiles/1_2_8_4.i3dm | Bin 7160 -> 0 bytes .../mapbox/output/tiles/1_2_9_0.i3dm | Bin 6000 -> 0 bytes .../mapbox/output/tiles/1_2_9_1.i3dm | Bin 15344 -> 0 bytes .../mapbox/output/tiles/1_2_9_2.i3dm | Bin 11920 -> 0 bytes .../mapbox/output/tiles/1_2_9_3.i3dm | Bin 4904 -> 0 bytes .../mapbox/output/tiles/1_2_9_4.i3dm | Bin 2008 -> 0 bytes .../mapbox/output/tiles/2_0_0_2.i3dm | Bin 5816 -> 0 bytes .../mapbox/output/tiles/2_0_0_3.i3dm | Bin 5960 -> 0 bytes .../mapbox/output/tiles/2_0_0_4.i3dm | Bin 23888 -> 0 bytes .../mapbox/output/tiles/2_0_0_5.i3dm | Bin 26016 -> 0 bytes .../mapbox/output/tiles/2_0_0_6.i3dm | Bin 20144 -> 0 bytes .../mapbox/output/tiles/2_0_0_7.i3dm | Bin 12408 -> 0 bytes .../mapbox/output/tiles/2_0_0_8.i3dm | Bin 11512 -> 0 bytes .../mapbox/output/tiles/2_0_1_0.i3dm | Bin 2488 -> 0 bytes .../mapbox/output/tiles/2_0_1_1.i3dm | Bin 2192 -> 0 bytes .../mapbox/output/tiles/2_0_1_2.i3dm | Bin 8464 -> 0 bytes .../mapbox/output/tiles/2_0_1_3.i3dm | Bin 15040 -> 0 bytes .../mapbox/output/tiles/2_0_1_4.i3dm | Bin 9040 -> 0 bytes .../mapbox/output/tiles/2_0_1_5.i3dm | Bin 17832 -> 0 bytes .../mapbox/output/tiles/2_0_1_6.i3dm | Bin 31472 -> 0 bytes .../mapbox/output/tiles/2_0_1_7.i3dm | Bin 15488 -> 0 bytes .../mapbox/output/tiles/2_0_1_8.i3dm | Bin 11608 -> 0 bytes .../mapbox/output/tiles/2_0_1_9.i3dm | Bin 2920 -> 0 bytes .../mapbox/output/tiles/2_0_2_0.i3dm | Bin 3640 -> 0 bytes .../mapbox/output/tiles/2_0_2_1.i3dm | Bin 6592 -> 0 bytes .../mapbox/output/tiles/2_0_2_2.i3dm | Bin 4120 -> 0 bytes .../mapbox/output/tiles/2_0_2_3.i3dm | Bin 15240 -> 0 bytes .../mapbox/output/tiles/2_0_2_4.i3dm | Bin 12328 -> 0 bytes .../mapbox/output/tiles/2_0_2_5.i3dm | Bin 17424 -> 0 bytes .../mapbox/output/tiles/2_0_2_6.i3dm | Bin 11896 -> 0 bytes .../mapbox/output/tiles/2_0_2_7.i3dm | Bin 24680 -> 0 bytes .../mapbox/output/tiles/2_0_2_8.i3dm | Bin 14640 -> 0 bytes .../mapbox/output/tiles/2_0_3_0.i3dm | Bin 3992 -> 0 bytes .../mapbox/output/tiles/2_0_3_2.i3dm | Bin 15584 -> 0 bytes .../mapbox/output/tiles/2_0_3_3.i3dm | Bin 22344 -> 0 bytes .../mapbox/output/tiles/2_0_3_4.i3dm | Bin 8368 -> 0 bytes .../mapbox/output/tiles/2_0_3_5.i3dm | Bin 10688 -> 0 bytes .../mapbox/output/tiles/2_0_3_6.i3dm | Bin 13816 -> 0 bytes .../mapbox/output/tiles/2_0_3_7.i3dm | Bin 21256 -> 0 bytes .../mapbox/output/tiles/2_0_3_8.i3dm | Bin 17088 -> 0 bytes .../mapbox/output/tiles/2_0_4_2.i3dm | Bin 10856 -> 0 bytes .../mapbox/output/tiles/2_0_4_3.i3dm | Bin 16968 -> 0 bytes .../mapbox/output/tiles/2_0_4_4.i3dm | Bin 7824 -> 0 bytes .../mapbox/output/tiles/2_0_4_5.i3dm | Bin 9848 -> 0 bytes .../mapbox/output/tiles/2_0_4_6.i3dm | Bin 17528 -> 0 bytes .../mapbox/output/tiles/2_0_4_7.i3dm | Bin 11808 -> 0 bytes .../mapbox/output/tiles/2_0_4_8.i3dm | Bin 12616 -> 0 bytes .../mapbox/output/tiles/2_0_4_9.i3dm | Bin 3856 -> 0 bytes .../mapbox/output/tiles/2_0_5_2.i3dm | Bin 10560 -> 0 bytes .../mapbox/output/tiles/2_0_5_3.i3dm | Bin 15288 -> 0 bytes .../mapbox/output/tiles/2_0_5_4.i3dm | Bin 5432 -> 0 bytes .../mapbox/output/tiles/2_0_5_5.i3dm | Bin 4784 -> 0 bytes .../mapbox/output/tiles/2_0_5_6.i3dm | Bin 12528 -> 0 bytes .../mapbox/output/tiles/2_0_5_7.i3dm | Bin 11568 -> 0 bytes .../mapbox/output/tiles/2_0_5_8.i3dm | Bin 4608 -> 0 bytes .../mapbox/output/tiles/2_0_5_9.i3dm | Bin 2088 -> 0 bytes .../mapbox/output/tiles/2_0_6_3.i3dm | Bin 3488 -> 0 bytes .../mapbox/output/tiles/2_0_6_4.i3dm | Bin 4392 -> 0 bytes .../mapbox/output/tiles/2_0_6_6.i3dm | Bin 5848 -> 0 bytes .../mapbox/output/tiles/2_0_6_7.i3dm | Bin 2336 -> 0 bytes .../mapbox/output/tiles/2_1_0_0.i3dm | Bin 6120 -> 0 bytes .../mapbox/output/tiles/2_1_0_1.i3dm | Bin 8160 -> 0 bytes .../mapbox/output/tiles/2_1_0_2.i3dm | Bin 6512 -> 0 bytes .../mapbox/output/tiles/2_1_0_3.i3dm | Bin 10528 -> 0 bytes .../mapbox/output/tiles/2_1_0_4.i3dm | Bin 12168 -> 0 bytes .../mapbox/output/tiles/2_1_0_5.i3dm | Bin 20000 -> 0 bytes .../mapbox/output/tiles/2_1_0_6.i3dm | Bin 10288 -> 0 bytes .../mapbox/output/tiles/2_1_0_7.i3dm | Bin 10232 -> 0 bytes .../mapbox/output/tiles/2_1_0_8.i3dm | Bin 2528 -> 0 bytes .../mapbox/output/tiles/2_1_0_9.i3dm | Bin 2512 -> 0 bytes .../mapbox/output/tiles/2_1_1_0.i3dm | Bin 1944 -> 0 bytes .../mapbox/output/tiles/2_1_1_1.i3dm | Bin 5952 -> 0 bytes .../mapbox/output/tiles/2_1_1_2.i3dm | Bin 3712 -> 0 bytes .../mapbox/output/tiles/2_1_1_3.i3dm | Bin 5944 -> 0 bytes .../mapbox/output/tiles/2_1_1_4.i3dm | Bin 5872 -> 0 bytes .../mapbox/output/tiles/2_1_1_5.i3dm | Bin 7280 -> 0 bytes .../mapbox/output/tiles/2_1_1_6.i3dm | Bin 2392 -> 0 bytes .../mapbox/output/tiles/2_1_1_7.i3dm | Bin 2936 -> 0 bytes .../mapbox/output/tiles/2_1_1_9.i3dm | Bin 5528 -> 0 bytes .../mapbox/output/tiles/2_1_2_2.i3dm | Bin 2152 -> 0 bytes .../mapbox/output/tiles/2_1_2_3.i3dm | Bin 3200 -> 0 bytes .../mapbox/output/tiles/2_1_2_4.i3dm | Bin 3552 -> 0 bytes .../mapbox/output/tiles/2_1_2_5.i3dm | Bin 2688 -> 0 bytes .../mapbox/output/tiles/2_1_2_6.i3dm | Bin 6104 -> 0 bytes .../mapbox/output/tiles/2_1_2_7.i3dm | Bin 5848 -> 0 bytes .../mapbox/output/tiles/2_1_2_8.i3dm | Bin 2008 -> 0 bytes .../mapbox/output/tiles/2_1_2_9.i3dm | Bin 5128 -> 0 bytes .../mapbox/output/tiles/2_1_3_4.i3dm | Bin 3424 -> 0 bytes .../mapbox/output/tiles/2_1_3_5.i3dm | Bin 1944 -> 0 bytes .../mapbox/output/tiles/2_1_3_6.i3dm | Bin 5832 -> 0 bytes .../mapbox/output/tiles/2_1_3_7.i3dm | Bin 2392 -> 0 bytes .../mapbox/output/tiles/2_1_3_8.i3dm | Bin 5176 -> 0 bytes .../mapbox/output/tiles/2_1_3_9.i3dm | Bin 2984 -> 0 bytes .../mapbox/output/tiles/2_1_4_4.i3dm | Bin 3656 -> 0 bytes .../mapbox/output/tiles/2_1_4_5.i3dm | Bin 7528 -> 0 bytes .../mapbox/output/tiles/2_1_4_9.i3dm | Bin 2144 -> 0 bytes .../mapbox/output/tiles/2_1_5_3.i3dm | Bin 2640 -> 0 bytes .../mapbox/output/tiles/2_1_5_4.i3dm | Bin 8752 -> 0 bytes .../mapbox/output/tiles/2_1_5_5.i3dm | Bin 3848 -> 0 bytes .../mapbox/output/tiles/2_1_5_7.i3dm | Bin 2832 -> 0 bytes .../mapbox/output/tiles/2_1_5_8.i3dm | Bin 2528 -> 0 bytes .../mapbox/output/tiles/2_1_5_9.i3dm | Bin 2016 -> 0 bytes .../mapbox/output/tiles/2_1_6_2.i3dm | Bin 2408 -> 0 bytes .../mapbox/output/tiles/2_1_6_3.i3dm | Bin 6192 -> 0 bytes .../mapbox/output/tiles/2_1_6_4.i3dm | Bin 7904 -> 0 bytes .../mapbox/output/tiles/2_1_6_5.i3dm | Bin 1952 -> 0 bytes .../mapbox/output/tiles/2_1_6_8.i3dm | Bin 2472 -> 0 bytes .../mapbox/output/tiles/2_2_0_0.i3dm | Bin 17000 -> 0 bytes .../mapbox/output/tiles/2_2_0_1.i3dm | Bin 13128 -> 0 bytes .../mapbox/output/tiles/2_2_0_2.i3dm | Bin 11192 -> 0 bytes .../mapbox/output/tiles/2_2_0_3.i3dm | Bin 2504 -> 0 bytes .../mapbox/output/tiles/2_2_1_0.i3dm | Bin 17080 -> 0 bytes .../mapbox/output/tiles/2_2_1_1.i3dm | Bin 13400 -> 0 bytes .../mapbox/output/tiles/2_2_1_2.i3dm | Bin 3000 -> 0 bytes .../mapbox/output/tiles/2_2_1_6.i3dm | Bin 1944 -> 0 bytes .../mapbox/output/tiles/2_2_2_0.i3dm | Bin 9192 -> 0 bytes .../mapbox/output/tiles/2_2_2_1.i3dm | Bin 4720 -> 0 bytes .../mapbox/output/tiles/2_2_2_2.i3dm | Bin 2264 -> 0 bytes .../mapbox/output/tiles/2_2_2_3.i3dm | Bin 2952 -> 0 bytes .../mapbox/output/tiles/2_2_3_0.i3dm | Bin 2776 -> 0 bytes .../mapbox/output/tiles/2_2_3_1.i3dm | Bin 2280 -> 0 bytes .../mapbox/output/tiles/2_2_3_3.i3dm | Bin 2368 -> 0 bytes .../mapbox/output/tiles/2_2_5_1.i3dm | Bin 1944 -> 0 bytes .../mapbox/output/tiles/2_2_6_0.i3dm | Bin 2488 -> 0 bytes .../mapbox/output/tiles/2_2_6_1.i3dm | Bin 2584 -> 0 bytes .../traffic_lights/mapbox/output/tileset.json | 227 - .../mapbox/output/tileset_0_0.json | 207 - .../mapbox/output/tileset_0_1.json | 3687 - .../mapbox/output/tileset_0_2.json | 87 - .../mapbox/output/tileset_1_0.json | 1247 - .../mapbox/output/tileset_1_1.json | 4047 -- .../mapbox/output/tileset_1_2.json | 2127 - .../mapbox/output/tileset_2_0.json | 2207 - .../mapbox/output/tileset_2_1.json | 1927 - .../mapbox/output/tileset_2_2.json | 767 - src/Cesium/CesiumTransformer.cs | 1 - src/Cesium/Matrix.cs | 1 - src/EnuCalculator.cs | 32 +- src/ImplicitTiling.cs | 3 +- src/Options.cs | 8 - src/TileHandler.cs | 10 +- src/i3dm.export.csproj | 2 +- tests/RotationTests.cs | 25 - tests/TileHandlerTests.cs | 14 +- 425 files changed, 16 insertions(+), 192908 deletions(-) delete mode 100644 samples/bordeaux/Mapbox3DTiles.js delete mode 100644 samples/bordeaux/index.html delete mode 100644 samples/bordeaux/old/Mapbox3DTiles.js delete mode 100644 samples/bordeaux/old/index.html delete mode 100644 samples/traffic_lights/mapbox/Box.glb delete mode 100644 samples/traffic_lights/mapbox/Mapbox3DTiles.js delete mode 100644 samples/traffic_lights/mapbox/index.html delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_0_5_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_0_6_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_0_8_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_0_9_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_0_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_0_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_0_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_0_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_0_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_0_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_1_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_1_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_1_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_1_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_1_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_1_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_1_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_1_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_2_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_2_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_2_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_2_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_2_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_2_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_2_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_2_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_3_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_3_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_3_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_3_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_3_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_3_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_3_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_3_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_3_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_4_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_4_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_4_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_4_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_4_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_4_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_4_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_4_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_4_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_4_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_5_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_5_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_5_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_5_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_5_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_5_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_5_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_5_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_5_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_5_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_6_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_6_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_6_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_6_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_6_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_6_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_6_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_6_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_6_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_6_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_7_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_7_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_7_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_7_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_7_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_7_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_7_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_7_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_7_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_7_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_8_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_8_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_8_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_8_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_8_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_8_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_8_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_8_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_8_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_8_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_9_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_9_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_9_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_9_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_9_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_9_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_9_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_9_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_9_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_1_9_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/0_2_9_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_0_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_0_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_0_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_1_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_1_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_1_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_2_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_2_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_2_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_3_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_3_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_3_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_4_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_4_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_4_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_5_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_5_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_5_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_6_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_6_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_6_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_7_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_7_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_8_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_8_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_9_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_9_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_9_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_9_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_0_9_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_0_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_0_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_0_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_0_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_0_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_0_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_0_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_0_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_0_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_0_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_1_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_1_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_1_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_1_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_1_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_1_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_1_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_1_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_1_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_1_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_2_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_2_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_2_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_2_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_2_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_2_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_2_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_2_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_2_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_2_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_3_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_3_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_3_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_3_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_3_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_3_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_3_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_3_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_3_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_3_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_4_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_4_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_4_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_4_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_4_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_4_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_4_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_4_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_4_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_4_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_5_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_5_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_5_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_5_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_5_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_5_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_5_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_5_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_5_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_5_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_6_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_6_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_6_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_6_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_6_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_6_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_6_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_6_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_6_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_6_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_7_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_7_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_7_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_7_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_7_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_7_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_7_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_7_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_7_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_7_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_8_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_8_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_8_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_8_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_8_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_8_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_8_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_8_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_8_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_8_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_9_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_9_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_9_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_9_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_9_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_9_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_9_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_9_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_9_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_1_9_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_0_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_1_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_1_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_1_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_1_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_2_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_2_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_2_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_2_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_2_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_3_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_3_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_3_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_3_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_3_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_3_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_4_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_4_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_4_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_4_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_4_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_4_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_5_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_5_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_5_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_5_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_5_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_5_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_5_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_6_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_6_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_6_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_6_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_6_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_6_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_6_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_7_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_7_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_7_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_7_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_7_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_7_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_8_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_8_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_8_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_8_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_8_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_9_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_9_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_9_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_9_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/1_2_9_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_0_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_0_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_0_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_0_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_0_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_0_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_0_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_1_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_1_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_1_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_1_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_1_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_1_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_1_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_1_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_1_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_1_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_2_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_2_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_2_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_2_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_2_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_2_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_2_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_2_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_2_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_3_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_3_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_3_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_3_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_3_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_3_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_3_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_3_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_4_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_4_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_4_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_4_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_4_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_4_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_4_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_4_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_5_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_5_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_5_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_5_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_5_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_5_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_5_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_5_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_6_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_6_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_6_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_0_6_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_0_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_0_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_0_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_0_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_0_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_0_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_0_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_0_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_0_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_0_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_1_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_1_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_1_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_1_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_1_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_1_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_1_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_1_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_1_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_2_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_2_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_2_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_2_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_2_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_2_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_2_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_2_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_3_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_3_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_3_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_3_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_3_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_3_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_4_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_4_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_4_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_5_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_5_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_5_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_5_7.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_5_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_5_9.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_6_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_6_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_6_4.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_6_5.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_1_6_8.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_0_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_0_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_0_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_0_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_1_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_1_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_1_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_1_6.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_2_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_2_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_2_2.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_2_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_3_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_3_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_3_3.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_5_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_6_0.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tiles/2_2_6_1.i3dm delete mode 100644 samples/traffic_lights/mapbox/output/tileset.json delete mode 100644 samples/traffic_lights/mapbox/output/tileset_0_0.json delete mode 100644 samples/traffic_lights/mapbox/output/tileset_0_1.json delete mode 100644 samples/traffic_lights/mapbox/output/tileset_0_2.json delete mode 100644 samples/traffic_lights/mapbox/output/tileset_1_0.json delete mode 100644 samples/traffic_lights/mapbox/output/tileset_1_1.json delete mode 100644 samples/traffic_lights/mapbox/output/tileset_1_2.json delete mode 100644 samples/traffic_lights/mapbox/output/tileset_2_0.json delete mode 100644 samples/traffic_lights/mapbox/output/tileset_2_1.json delete mode 100644 samples/traffic_lights/mapbox/output/tileset_2_2.json delete mode 100644 tests/RotationTests.cs diff --git a/README.md b/README.md index 555568d..8af43e2 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,6 @@ The input table contains instance information like location (epsg:4326), binary The 3D tiles created by this tool are tested in Cesium JS. -MapBox GL JS (using https://github.com/Geodan/mapbox-3dtiles) is NOT supported at the moment. - Sample of trees in Cesium viewer using instanced 3D Tiles in Lyon: ![image](https://github.com/Geodan/i3dm.export/assets/538812/f67c6126-64be-42a9-9ee5-8f64c452c4aa) @@ -74,8 +72,6 @@ Tool parameters: -q: (optional - Default: "") Query to add to the where clauses (for example: -q "id<5460019"). Be sure to check indexes when using this option. --f, --format: (optional - default Cesium) Output format mapbox/cesium - --use_external_model: (optional - default false) Use external model instead of embedded model --use_scale_non_uniform: (optional - default false) Use column scale_non_uniform for scaling instances diff --git a/i3dm.export.sln b/i3dm.export.sln index 94f8836..dda68ec 100644 --- a/i3dm.export.sln +++ b/i3dm.export.sln @@ -7,8 +7,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{68333F7F-D9B ProjectSection(SolutionItems) = preProject docs\Box.glb = docs\Box.glb docs\getting_started.md = docs\getting_started.md - docs\index.html = docs\index.html - docs\Mapbox3DTiles.js = docs\Mapbox3DTiles.js README.md = README.md docs\screenshot.png = docs\screenshot.png docs\trees.png = docs\trees.png diff --git a/samples/bordeaux/Mapbox3DTiles.js b/samples/bordeaux/Mapbox3DTiles.js deleted file mode 100644 index a3096cc..0000000 --- a/samples/bordeaux/Mapbox3DTiles.js +++ /dev/null @@ -1,55235 +0,0 @@ -var Mapbox3DTiles = (function (exports) { - 'use strict'; - - /** - * @license - * Copyright 2010-2021 Three.js Authors - * SPDX-License-Identifier: MIT - */ - const REVISION = '132'; - const CullFaceNone = 0; - const CullFaceBack = 1; - const CullFaceFront = 2; - const PCFShadowMap = 1; - const PCFSoftShadowMap = 2; - const VSMShadowMap = 3; - const FrontSide = 0; - const BackSide = 1; - const DoubleSide = 2; - const FlatShading = 1; - const NoBlending = 0; - const NormalBlending = 1; - const AdditiveBlending = 2; - const SubtractiveBlending = 3; - const MultiplyBlending = 4; - const CustomBlending = 5; - const AddEquation = 100; - const SubtractEquation = 101; - const ReverseSubtractEquation = 102; - const MinEquation = 103; - const MaxEquation = 104; - const ZeroFactor = 200; - const OneFactor = 201; - const SrcColorFactor = 202; - const OneMinusSrcColorFactor = 203; - const SrcAlphaFactor = 204; - const OneMinusSrcAlphaFactor = 205; - const DstAlphaFactor = 206; - const OneMinusDstAlphaFactor = 207; - const DstColorFactor = 208; - const OneMinusDstColorFactor = 209; - const SrcAlphaSaturateFactor = 210; - const NeverDepth = 0; - const AlwaysDepth = 1; - const LessDepth = 2; - const LessEqualDepth = 3; - const EqualDepth = 4; - const GreaterEqualDepth = 5; - const GreaterDepth = 6; - const NotEqualDepth = 7; - const MultiplyOperation = 0; - const MixOperation = 1; - const AddOperation = 2; - const NoToneMapping = 0; - const LinearToneMapping = 1; - const ReinhardToneMapping = 2; - const CineonToneMapping = 3; - const ACESFilmicToneMapping = 4; - const CustomToneMapping = 5; - - const UVMapping = 300; - const CubeReflectionMapping = 301; - const CubeRefractionMapping = 302; - const EquirectangularReflectionMapping = 303; - const EquirectangularRefractionMapping = 304; - const CubeUVReflectionMapping = 306; - const CubeUVRefractionMapping = 307; - const RepeatWrapping = 1000; - const ClampToEdgeWrapping = 1001; - const MirroredRepeatWrapping = 1002; - const NearestFilter = 1003; - const NearestMipmapNearestFilter = 1004; - const NearestMipmapLinearFilter = 1005; - const LinearFilter = 1006; - const LinearMipmapNearestFilter = 1007; - const LinearMipmapLinearFilter = 1008; - const UnsignedByteType = 1009; - const ByteType = 1010; - const ShortType = 1011; - const UnsignedShortType = 1012; - const IntType = 1013; - const UnsignedIntType = 1014; - const FloatType = 1015; - const HalfFloatType = 1016; - const UnsignedShort4444Type = 1017; - const UnsignedShort5551Type = 1018; - const UnsignedShort565Type = 1019; - const UnsignedInt248Type = 1020; - const AlphaFormat = 1021; - const RGBFormat = 1022; - const RGBAFormat = 1023; - const LuminanceFormat = 1024; - const LuminanceAlphaFormat = 1025; - const RGBEFormat = RGBAFormat; - const DepthFormat = 1026; - const DepthStencilFormat = 1027; - const RedFormat = 1028; - const RedIntegerFormat = 1029; - const RGFormat = 1030; - const RGIntegerFormat = 1031; - const RGBIntegerFormat = 1032; - const RGBAIntegerFormat = 1033; - - const RGB_S3TC_DXT1_Format = 33776; - const RGBA_S3TC_DXT1_Format = 33777; - const RGBA_S3TC_DXT3_Format = 33778; - const RGBA_S3TC_DXT5_Format = 33779; - const RGB_PVRTC_4BPPV1_Format = 35840; - const RGB_PVRTC_2BPPV1_Format = 35841; - const RGBA_PVRTC_4BPPV1_Format = 35842; - const RGBA_PVRTC_2BPPV1_Format = 35843; - const RGB_ETC1_Format = 36196; - const RGB_ETC2_Format = 37492; - const RGBA_ETC2_EAC_Format = 37496; - const RGBA_ASTC_4x4_Format = 37808; - const RGBA_ASTC_5x4_Format = 37809; - const RGBA_ASTC_5x5_Format = 37810; - const RGBA_ASTC_6x5_Format = 37811; - const RGBA_ASTC_6x6_Format = 37812; - const RGBA_ASTC_8x5_Format = 37813; - const RGBA_ASTC_8x6_Format = 37814; - const RGBA_ASTC_8x8_Format = 37815; - const RGBA_ASTC_10x5_Format = 37816; - const RGBA_ASTC_10x6_Format = 37817; - const RGBA_ASTC_10x8_Format = 37818; - const RGBA_ASTC_10x10_Format = 37819; - const RGBA_ASTC_12x10_Format = 37820; - const RGBA_ASTC_12x12_Format = 37821; - const RGBA_BPTC_Format = 36492; - const SRGB8_ALPHA8_ASTC_4x4_Format = 37840; - const SRGB8_ALPHA8_ASTC_5x4_Format = 37841; - const SRGB8_ALPHA8_ASTC_5x5_Format = 37842; - const SRGB8_ALPHA8_ASTC_6x5_Format = 37843; - const SRGB8_ALPHA8_ASTC_6x6_Format = 37844; - const SRGB8_ALPHA8_ASTC_8x5_Format = 37845; - const SRGB8_ALPHA8_ASTC_8x6_Format = 37846; - const SRGB8_ALPHA8_ASTC_8x8_Format = 37847; - const SRGB8_ALPHA8_ASTC_10x5_Format = 37848; - const SRGB8_ALPHA8_ASTC_10x6_Format = 37849; - const SRGB8_ALPHA8_ASTC_10x8_Format = 37850; - const SRGB8_ALPHA8_ASTC_10x10_Format = 37851; - const SRGB8_ALPHA8_ASTC_12x10_Format = 37852; - const SRGB8_ALPHA8_ASTC_12x12_Format = 37853; - const LoopOnce = 2200; - const LoopRepeat = 2201; - const LoopPingPong = 2202; - const InterpolateDiscrete = 2300; - const InterpolateLinear = 2301; - const InterpolateSmooth = 2302; - const ZeroCurvatureEnding = 2400; - const ZeroSlopeEnding = 2401; - const WrapAroundEnding = 2402; - const NormalAnimationBlendMode = 2500; - const AdditiveAnimationBlendMode = 2501; - const TrianglesDrawMode = 0; - const TriangleStripDrawMode = 1; - const TriangleFanDrawMode = 2; - const LinearEncoding = 3000; - const sRGBEncoding = 3001; - const GammaEncoding = 3007; - const RGBEEncoding = 3002; - const LogLuvEncoding = 3003; - const RGBM7Encoding = 3004; - const RGBM16Encoding = 3005; - const RGBDEncoding = 3006; - const BasicDepthPacking = 3200; - const RGBADepthPacking = 3201; - const TangentSpaceNormalMap = 0; - const ObjectSpaceNormalMap = 1; - const KeepStencilOp = 7680; - const AlwaysStencilFunc = 519; - - const StaticDrawUsage = 35044; - const DynamicDrawUsage = 35048; - const GLSL3 = '300 es'; - - /** - * https://github.com/mrdoob/eventdispatcher.js/ - */ - - class EventDispatcher { - - addEventListener( type, listener ) { - - if ( this._listeners === undefined ) this._listeners = {}; - - const listeners = this._listeners; - - if ( listeners[ type ] === undefined ) { - - listeners[ type ] = []; - - } - - if ( listeners[ type ].indexOf( listener ) === - 1 ) { - - listeners[ type ].push( listener ); - - } - - } - - hasEventListener( type, listener ) { - - if ( this._listeners === undefined ) return false; - - const listeners = this._listeners; - - return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; - - } - - removeEventListener( type, listener ) { - - if ( this._listeners === undefined ) return; - - const listeners = this._listeners; - const listenerArray = listeners[ type ]; - - if ( listenerArray !== undefined ) { - - const index = listenerArray.indexOf( listener ); - - if ( index !== - 1 ) { - - listenerArray.splice( index, 1 ); - - } - - } - - } - - dispatchEvent( event ) { - - if ( this._listeners === undefined ) return; - - const listeners = this._listeners; - const listenerArray = listeners[ event.type ]; - - if ( listenerArray !== undefined ) { - - event.target = this; - - // Make a copy, in case listeners are removed while iterating. - const array = listenerArray.slice( 0 ); - - for ( let i = 0, l = array.length; i < l; i ++ ) { - - array[ i ].call( this, event ); - - } - - event.target = null; - - } - - } - - } - - const _lut = []; - - for ( let i = 0; i < 256; i ++ ) { - - _lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 ); - - } - - let _seed = 1234567; - - - const DEG2RAD = Math.PI / 180; - const RAD2DEG = 180 / Math.PI; - - // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 - function generateUUID() { - - const d0 = Math.random() * 0xffffffff | 0; - const d1 = Math.random() * 0xffffffff | 0; - const d2 = Math.random() * 0xffffffff | 0; - const d3 = Math.random() * 0xffffffff | 0; - const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' + - _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' + - _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] + - _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ]; - - // .toUpperCase() here flattens concatenated strings to save heap memory space. - return uuid.toUpperCase(); - - } - - function clamp( value, min, max ) { - - return Math.max( min, Math.min( max, value ) ); - - } - - // compute euclidian modulo of m % n - // https://en.wikipedia.org/wiki/Modulo_operation - function euclideanModulo( n, m ) { - - return ( ( n % m ) + m ) % m; - - } - - // Linear mapping from range to range - function mapLinear( x, a1, a2, b1, b2 ) { - - return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); - - } - - // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ - function inverseLerp( x, y, value ) { - - if ( x !== y ) { - - return ( value - x ) / ( y - x ); - - } else { - - return 0; - - } - - } - - // https://en.wikipedia.org/wiki/Linear_interpolation - function lerp( x, y, t ) { - - return ( 1 - t ) * x + t * y; - - } - - // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ - function damp( x, y, lambda, dt ) { - - return lerp( x, y, 1 - Math.exp( - lambda * dt ) ); - - } - - // https://www.desmos.com/calculator/vcsjnyz7x4 - function pingpong( x, length = 1 ) { - - return length - Math.abs( euclideanModulo( x, length * 2 ) - length ); - - } - - // http://en.wikipedia.org/wiki/Smoothstep - function smoothstep( x, min, max ) { - - if ( x <= min ) return 0; - if ( x >= max ) return 1; - - x = ( x - min ) / ( max - min ); - - return x * x * ( 3 - 2 * x ); - - } - - function smootherstep( x, min, max ) { - - if ( x <= min ) return 0; - if ( x >= max ) return 1; - - x = ( x - min ) / ( max - min ); - - return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); - - } - - // Random integer from interval - function randInt( low, high ) { - - return low + Math.floor( Math.random() * ( high - low + 1 ) ); - - } - - // Random float from interval - function randFloat( low, high ) { - - return low + Math.random() * ( high - low ); - - } - - // Random float from <-range/2, range/2> interval - function randFloatSpread( range ) { - - return range * ( 0.5 - Math.random() ); - - } - - // Deterministic pseudo-random float in the interval [ 0, 1 ] - function seededRandom( s ) { - - if ( s !== undefined ) _seed = s % 2147483647; - - // Park-Miller algorithm - - _seed = _seed * 16807 % 2147483647; - - return ( _seed - 1 ) / 2147483646; - - } - - function degToRad( degrees ) { - - return degrees * DEG2RAD; - - } - - function radToDeg( radians ) { - - return radians * RAD2DEG; - - } - - function isPowerOfTwo( value ) { - - return ( value & ( value - 1 ) ) === 0 && value !== 0; - - } - - function ceilPowerOfTwo( value ) { - - return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); - - } - - function floorPowerOfTwo( value ) { - - return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); - - } - - function setQuaternionFromProperEuler( q, a, b, c, order ) { - - // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles - - // rotations are applied to the axes in the order specified by 'order' - // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c' - // angles are in radians - - const cos = Math.cos; - const sin = Math.sin; - - const c2 = cos( b / 2 ); - const s2 = sin( b / 2 ); - - const c13 = cos( ( a + c ) / 2 ); - const s13 = sin( ( a + c ) / 2 ); - - const c1_3 = cos( ( a - c ) / 2 ); - const s1_3 = sin( ( a - c ) / 2 ); - - const c3_1 = cos( ( c - a ) / 2 ); - const s3_1 = sin( ( c - a ) / 2 ); - - switch ( order ) { - - case 'XYX': - q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 ); - break; - - case 'YZY': - q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 ); - break; - - case 'ZXZ': - q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 ); - break; - - case 'XZX': - q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 ); - break; - - case 'YXY': - q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 ); - break; - - case 'ZYZ': - q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 ); - break; - - default: - console.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order ); - - } - - } - - var MathUtils = /*#__PURE__*/Object.freeze({ - __proto__: null, - DEG2RAD: DEG2RAD, - RAD2DEG: RAD2DEG, - generateUUID: generateUUID, - clamp: clamp, - euclideanModulo: euclideanModulo, - mapLinear: mapLinear, - inverseLerp: inverseLerp, - lerp: lerp, - damp: damp, - pingpong: pingpong, - smoothstep: smoothstep, - smootherstep: smootherstep, - randInt: randInt, - randFloat: randFloat, - randFloatSpread: randFloatSpread, - seededRandom: seededRandom, - degToRad: degToRad, - radToDeg: radToDeg, - isPowerOfTwo: isPowerOfTwo, - ceilPowerOfTwo: ceilPowerOfTwo, - floorPowerOfTwo: floorPowerOfTwo, - setQuaternionFromProperEuler: setQuaternionFromProperEuler - }); - - class Vector2 { - - constructor( x = 0, y = 0 ) { - - this.x = x; - this.y = y; - - } - - get width() { - - return this.x; - - } - - set width( value ) { - - this.x = value; - - } - - get height() { - - return this.y; - - } - - set height( value ) { - - this.y = value; - - } - - set( x, y ) { - - this.x = x; - this.y = y; - - return this; - - } - - setScalar( scalar ) { - - this.x = scalar; - this.y = scalar; - - return this; - - } - - setX( x ) { - - this.x = x; - - return this; - - } - - setY( y ) { - - this.y = y; - - return this; - - } - - setComponent( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - } - - getComponent( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - default: throw new Error( 'index is out of range: ' + index ); - - } - - } - - clone() { - - return new this.constructor( this.x, this.y ); - - } - - copy( v ) { - - this.x = v.x; - this.y = v.y; - - return this; - - } - - add( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - - return this; - - } - - addScalar( s ) { - - this.x += s; - this.y += s; - - return this; - - } - - addVectors( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - - return this; - - } - - addScaledVector( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - - return this; - - } - - sub( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - - return this; - - } - - subScalar( s ) { - - this.x -= s; - this.y -= s; - - return this; - - } - - subVectors( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - - return this; - - } - - multiply( v ) { - - this.x *= v.x; - this.y *= v.y; - - return this; - - } - - multiplyScalar( scalar ) { - - this.x *= scalar; - this.y *= scalar; - - return this; - - } - - divide( v ) { - - this.x /= v.x; - this.y /= v.y; - - return this; - - } - - divideScalar( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - } - - applyMatrix3( m ) { - - const x = this.x, y = this.y; - const e = m.elements; - - this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; - this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; - - return this; - - } - - min( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - - return this; - - } - - max( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - - return this; - - } - - clamp( min, max ) { - - // assumes min < max, componentwise - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - - return this; - - } - - clampScalar( minVal, maxVal ) { - - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - - return this; - - } - - clampLength( min, max ) { - - const length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - } - - floor() { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - - return this; - - } - - ceil() { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - - return this; - - } - - round() { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - - return this; - - } - - roundToZero() { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - - return this; - - } - - negate() { - - this.x = - this.x; - this.y = - this.y; - - return this; - - } - - dot( v ) { - - return this.x * v.x + this.y * v.y; - - } - - cross( v ) { - - return this.x * v.y - this.y * v.x; - - } - - lengthSq() { - - return this.x * this.x + this.y * this.y; - - } - - length() { - - return Math.sqrt( this.x * this.x + this.y * this.y ); - - } - - manhattanLength() { - - return Math.abs( this.x ) + Math.abs( this.y ); - - } - - normalize() { - - return this.divideScalar( this.length() || 1 ); - - } - - angle() { - - // computes the angle in radians with respect to the positive x-axis - - const angle = Math.atan2( - this.y, - this.x ) + Math.PI; - - return angle; - - } - - distanceTo( v ) { - - return Math.sqrt( this.distanceToSquared( v ) ); - - } - - distanceToSquared( v ) { - - const dx = this.x - v.x, dy = this.y - v.y; - return dx * dx + dy * dy; - - } - - manhattanDistanceTo( v ) { - - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); - - } - - setLength( length ) { - - return this.normalize().multiplyScalar( length ); - - } - - lerp( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - - return this; - - } - - lerpVectors( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - - return this; - - } - - equals( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) ); - - } - - fromArray( array, offset = 0 ) { - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - - return this; - - } - - toArray( array = [], offset = 0 ) { - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - - return array; - - } - - fromBufferAttribute( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - - return this; - - } - - rotateAround( center, angle ) { - - const c = Math.cos( angle ), s = Math.sin( angle ); - - const x = this.x - center.x; - const y = this.y - center.y; - - this.x = x * c - y * s + center.x; - this.y = x * s + y * c + center.y; - - return this; - - } - - random() { - - this.x = Math.random(); - this.y = Math.random(); - - return this; - - } - - } - - Vector2.prototype.isVector2 = true; - - class Matrix3 { - - constructor() { - - this.elements = [ - - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 - - ]; - - if ( arguments.length > 0 ) { - - console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); - - } - - } - - set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { - - const te = this.elements; - - te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; - te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; - te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; - - return this; - - } - - identity() { - - this.set( - - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 - - ); - - return this; - - } - - copy( m ) { - - const te = this.elements; - const me = m.elements; - - te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; - te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; - te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; - - return this; - - } - - extractBasis( xAxis, yAxis, zAxis ) { - - xAxis.setFromMatrix3Column( this, 0 ); - yAxis.setFromMatrix3Column( this, 1 ); - zAxis.setFromMatrix3Column( this, 2 ); - - return this; - - } - - setFromMatrix4( m ) { - - const me = m.elements; - - this.set( - - me[ 0 ], me[ 4 ], me[ 8 ], - me[ 1 ], me[ 5 ], me[ 9 ], - me[ 2 ], me[ 6 ], me[ 10 ] - - ); - - return this; - - } - - multiply( m ) { - - return this.multiplyMatrices( this, m ); - - } - - premultiply( m ) { - - return this.multiplyMatrices( m, this ); - - } - - multiplyMatrices( a, b ) { - - const ae = a.elements; - const be = b.elements; - const te = this.elements; - - const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; - const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; - const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; - - const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; - const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; - const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; - - te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; - te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; - te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; - - te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; - te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; - te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; - - te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; - te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; - te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; - - return this; - - } - - multiplyScalar( s ) { - - const te = this.elements; - - te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; - te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; - te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; - - return this; - - } - - determinant() { - - const te = this.elements; - - const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], - d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], - g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; - - return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; - - } - - invert() { - - const te = this.elements, - - n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], - n12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ], - n13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ], - - t11 = n33 * n22 - n32 * n23, - t12 = n32 * n13 - n33 * n12, - t13 = n23 * n12 - n22 * n13, - - det = n11 * t11 + n21 * t12 + n31 * t13; - - if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 ); - - const detInv = 1 / det; - - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; - te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; - - te[ 3 ] = t12 * detInv; - te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; - te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; - - te[ 6 ] = t13 * detInv; - te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; - te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; - - return this; - - } - - transpose() { - - let tmp; - const m = this.elements; - - tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; - tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; - tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; - - return this; - - } - - getNormalMatrix( matrix4 ) { - - return this.setFromMatrix4( matrix4 ).invert().transpose(); - - } - - transposeIntoArray( r ) { - - const m = this.elements; - - r[ 0 ] = m[ 0 ]; - r[ 1 ] = m[ 3 ]; - r[ 2 ] = m[ 6 ]; - r[ 3 ] = m[ 1 ]; - r[ 4 ] = m[ 4 ]; - r[ 5 ] = m[ 7 ]; - r[ 6 ] = m[ 2 ]; - r[ 7 ] = m[ 5 ]; - r[ 8 ] = m[ 8 ]; - - return this; - - } - - setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) { - - const c = Math.cos( rotation ); - const s = Math.sin( rotation ); - - this.set( - sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, - - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, - 0, 0, 1 - ); - - return this; - - } - - scale( sx, sy ) { - - const te = this.elements; - - te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx; - te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy; - - return this; - - } - - rotate( theta ) { - - const c = Math.cos( theta ); - const s = Math.sin( theta ); - - const te = this.elements; - - const a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ]; - const a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ]; - - te[ 0 ] = c * a11 + s * a21; - te[ 3 ] = c * a12 + s * a22; - te[ 6 ] = c * a13 + s * a23; - - te[ 1 ] = - s * a11 + c * a21; - te[ 4 ] = - s * a12 + c * a22; - te[ 7 ] = - s * a13 + c * a23; - - return this; - - } - - translate( tx, ty ) { - - const te = this.elements; - - te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ]; - te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ]; - - return this; - - } - - equals( matrix ) { - - const te = this.elements; - const me = matrix.elements; - - for ( let i = 0; i < 9; i ++ ) { - - if ( te[ i ] !== me[ i ] ) return false; - - } - - return true; - - } - - fromArray( array, offset = 0 ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.elements[ i ] = array[ i + offset ]; - - } - - return this; - - } - - toArray( array = [], offset = 0 ) { - - const te = this.elements; - - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - - array[ offset + 3 ] = te[ 3 ]; - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; - array[ offset + 8 ] = te[ 8 ]; - - return array; - - } - - clone() { - - return new this.constructor().fromArray( this.elements ); - - } - - } - - Matrix3.prototype.isMatrix3 = true; - - let _canvas; - - class ImageUtils { - - static getDataURL( image ) { - - if ( /^data:/i.test( image.src ) ) { - - return image.src; - - } - - if ( typeof HTMLCanvasElement == 'undefined' ) { - - return image.src; - - } - - let canvas; - - if ( image instanceof HTMLCanvasElement ) { - - canvas = image; - - } else { - - if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - - _canvas.width = image.width; - _canvas.height = image.height; - - const context = _canvas.getContext( '2d' ); - - if ( image instanceof ImageData ) { - - context.putImageData( image, 0, 0 ); - - } else { - - context.drawImage( image, 0, 0, image.width, image.height ); - - } - - canvas = _canvas; - - } - - if ( canvas.width > 2048 || canvas.height > 2048 ) { - - console.warn( 'THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons', image ); - - return canvas.toDataURL( 'image/jpeg', 0.6 ); - - } else { - - return canvas.toDataURL( 'image/png' ); - - } - - } - - } - - let textureId = 0; - - class Texture extends EventDispatcher { - - constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding ) { - - super(); - - Object.defineProperty( this, 'id', { value: textureId ++ } ); - - this.uuid = generateUUID(); - - this.name = ''; - - this.image = image; - this.mipmaps = []; - - this.mapping = mapping; - - this.wrapS = wrapS; - this.wrapT = wrapT; - - this.magFilter = magFilter; - this.minFilter = minFilter; - - this.anisotropy = anisotropy; - - this.format = format; - this.internalFormat = null; - this.type = type; - - this.offset = new Vector2( 0, 0 ); - this.repeat = new Vector2( 1, 1 ); - this.center = new Vector2( 0, 0 ); - this.rotation = 0; - - this.matrixAutoUpdate = true; - this.matrix = new Matrix3(); - - this.generateMipmaps = true; - this.premultiplyAlpha = false; - this.flipY = true; - this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - - // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. - // - // Also changing the encoding after already used by a Material will not automatically make the Material - // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. - this.encoding = encoding; - - this.version = 0; - this.onUpdate = null; - - this.isRenderTargetTexture = false; - - } - - updateMatrix() { - - this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( source ) { - - this.name = source.name; - - this.image = source.image; - this.mipmaps = source.mipmaps.slice( 0 ); - - this.mapping = source.mapping; - - this.wrapS = source.wrapS; - this.wrapT = source.wrapT; - - this.magFilter = source.magFilter; - this.minFilter = source.minFilter; - - this.anisotropy = source.anisotropy; - - this.format = source.format; - this.internalFormat = source.internalFormat; - this.type = source.type; - - this.offset.copy( source.offset ); - this.repeat.copy( source.repeat ); - this.center.copy( source.center ); - this.rotation = source.rotation; - - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrix.copy( source.matrix ); - - this.generateMipmaps = source.generateMipmaps; - this.premultiplyAlpha = source.premultiplyAlpha; - this.flipY = source.flipY; - this.unpackAlignment = source.unpackAlignment; - this.encoding = source.encoding; - - return this; - - } - - toJSON( meta ) { - - const isRootObject = ( meta === undefined || typeof meta === 'string' ); - - if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { - - return meta.textures[ this.uuid ]; - - } - - const output = { - - metadata: { - version: 4.5, - type: 'Texture', - generator: 'Texture.toJSON' - }, - - uuid: this.uuid, - name: this.name, - - mapping: this.mapping, - - repeat: [ this.repeat.x, this.repeat.y ], - offset: [ this.offset.x, this.offset.y ], - center: [ this.center.x, this.center.y ], - rotation: this.rotation, - - wrap: [ this.wrapS, this.wrapT ], - - format: this.format, - type: this.type, - encoding: this.encoding, - - minFilter: this.minFilter, - magFilter: this.magFilter, - anisotropy: this.anisotropy, - - flipY: this.flipY, - - premultiplyAlpha: this.premultiplyAlpha, - unpackAlignment: this.unpackAlignment - - }; - - if ( this.image !== undefined ) { - - // TODO: Move to THREE.Image - - const image = this.image; - - if ( image.uuid === undefined ) { - - image.uuid = generateUUID(); // UGH - - } - - if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { - - let url; - - if ( Array.isArray( image ) ) { - - // process array of images e.g. CubeTexture - - url = []; - - for ( let i = 0, l = image.length; i < l; i ++ ) { - - // check cube texture with data textures - - if ( image[ i ].isDataTexture ) { - - url.push( serializeImage( image[ i ].image ) ); - - } else { - - url.push( serializeImage( image[ i ] ) ); - - } - - } - - } else { - - // process single image - - url = serializeImage( image ); - - } - - meta.images[ image.uuid ] = { - uuid: image.uuid, - url: url - }; - - } - - output.image = image.uuid; - - } - - if ( ! isRootObject ) { - - meta.textures[ this.uuid ] = output; - - } - - return output; - - } - - dispose() { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - transformUv( uv ) { - - if ( this.mapping !== UVMapping ) return uv; - - uv.applyMatrix3( this.matrix ); - - if ( uv.x < 0 || uv.x > 1 ) { - - switch ( this.wrapS ) { - - case RepeatWrapping: - - uv.x = uv.x - Math.floor( uv.x ); - break; - - case ClampToEdgeWrapping: - - uv.x = uv.x < 0 ? 0 : 1; - break; - - case MirroredRepeatWrapping: - - if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { - - uv.x = Math.ceil( uv.x ) - uv.x; - - } else { - - uv.x = uv.x - Math.floor( uv.x ); - - } - - break; - - } - - } - - if ( uv.y < 0 || uv.y > 1 ) { - - switch ( this.wrapT ) { - - case RepeatWrapping: - - uv.y = uv.y - Math.floor( uv.y ); - break; - - case ClampToEdgeWrapping: - - uv.y = uv.y < 0 ? 0 : 1; - break; - - case MirroredRepeatWrapping: - - if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { - - uv.y = Math.ceil( uv.y ) - uv.y; - - } else { - - uv.y = uv.y - Math.floor( uv.y ); - - } - - break; - - } - - } - - if ( this.flipY ) { - - uv.y = 1 - uv.y; - - } - - return uv; - - } - - set needsUpdate( value ) { - - if ( value === true ) this.version ++; - - } - - } - - Texture.DEFAULT_IMAGE = undefined; - Texture.DEFAULT_MAPPING = UVMapping; - - Texture.prototype.isTexture = true; - - function serializeImage( image ) { - - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || - ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - - // default images - - return ImageUtils.getDataURL( image ); - - } else { - - if ( image.data ) { - - // images of DataTexture - - return { - data: Array.prototype.slice.call( image.data ), - width: image.width, - height: image.height, - type: image.data.constructor.name - }; - - } else { - - console.warn( 'THREE.Texture: Unable to serialize Texture.' ); - return {}; - - } - - } - - } - - class Vector4 { - - constructor( x = 0, y = 0, z = 0, w = 1 ) { - - this.x = x; - this.y = y; - this.z = z; - this.w = w; - - } - - get width() { - - return this.z; - - } - - set width( value ) { - - this.z = value; - - } - - get height() { - - return this.w; - - } - - set height( value ) { - - this.w = value; - - } - - set( x, y, z, w ) { - - this.x = x; - this.y = y; - this.z = z; - this.w = w; - - return this; - - } - - setScalar( scalar ) { - - this.x = scalar; - this.y = scalar; - this.z = scalar; - this.w = scalar; - - return this; - - } - - setX( x ) { - - this.x = x; - - return this; - - } - - setY( y ) { - - this.y = y; - - return this; - - } - - setZ( z ) { - - this.z = z; - - return this; - - } - - setW( w ) { - - this.w = w; - - return this; - - } - - setComponent( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - case 3: this.w = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - } - - getComponent( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - case 3: return this.w; - default: throw new Error( 'index is out of range: ' + index ); - - } - - } - - clone() { - - return new this.constructor( this.x, this.y, this.z, this.w ); - - } - - copy( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.w = ( v.w !== undefined ) ? v.w : 1; - - return this; - - } - - add( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - this.z += v.z; - this.w += v.w; - - return this; - - } - - addScalar( s ) { - - this.x += s; - this.y += s; - this.z += s; - this.w += s; - - return this; - - } - - addVectors( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - this.w = a.w + b.w; - - return this; - - } - - addScaledVector( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - this.w += v.w * s; - - return this; - - } - - sub( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - this.w -= v.w; - - return this; - - } - - subScalar( s ) { - - this.x -= s; - this.y -= s; - this.z -= s; - this.w -= s; - - return this; - - } - - subVectors( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - this.w = a.w - b.w; - - return this; - - } - - multiply( v ) { - - this.x *= v.x; - this.y *= v.y; - this.z *= v.z; - this.w *= v.w; - - return this; - - } - - multiplyScalar( scalar ) { - - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - this.w *= scalar; - - return this; - - } - - applyMatrix4( m ) { - - const x = this.x, y = this.y, z = this.z, w = this.w; - const e = m.elements; - - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; - this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; - - return this; - - } - - divideScalar( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - } - - setAxisAngleFromQuaternion( q ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm - - // q is assumed to be normalized - - this.w = 2 * Math.acos( q.w ); - - const s = Math.sqrt( 1 - q.w * q.w ); - - if ( s < 0.0001 ) { - - this.x = 1; - this.y = 0; - this.z = 0; - - } else { - - this.x = q.x / s; - this.y = q.y / s; - this.z = q.z / s; - - } - - return this; - - } - - setAxisAngleFromRotationMatrix( m ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - let angle, x, y, z; // variables for result - const epsilon = 0.01, // margin to allow for rounding errors - epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees - - te = m.elements, - - m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], - m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], - m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - - if ( ( Math.abs( m12 - m21 ) < epsilon ) && - ( Math.abs( m13 - m31 ) < epsilon ) && - ( Math.abs( m23 - m32 ) < epsilon ) ) { - - // singularity found - // first check for identity matrix which must have +1 for all terms - // in leading diagonal and zero in other terms - - if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && - ( Math.abs( m13 + m31 ) < epsilon2 ) && - ( Math.abs( m23 + m32 ) < epsilon2 ) && - ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { - - // this singularity is identity matrix so angle = 0 - - this.set( 1, 0, 0, 0 ); - - return this; // zero angle, arbitrary axis - - } - - // otherwise this singularity is angle = 180 - - angle = Math.PI; - - const xx = ( m11 + 1 ) / 2; - const yy = ( m22 + 1 ) / 2; - const zz = ( m33 + 1 ) / 2; - const xy = ( m12 + m21 ) / 4; - const xz = ( m13 + m31 ) / 4; - const yz = ( m23 + m32 ) / 4; - - if ( ( xx > yy ) && ( xx > zz ) ) { - - // m11 is the largest diagonal term - - if ( xx < epsilon ) { - - x = 0; - y = 0.707106781; - z = 0.707106781; - - } else { - - x = Math.sqrt( xx ); - y = xy / x; - z = xz / x; - - } - - } else if ( yy > zz ) { - - // m22 is the largest diagonal term - - if ( yy < epsilon ) { - - x = 0.707106781; - y = 0; - z = 0.707106781; - - } else { - - y = Math.sqrt( yy ); - x = xy / y; - z = yz / y; - - } - - } else { - - // m33 is the largest diagonal term so base result on this - - if ( zz < epsilon ) { - - x = 0.707106781; - y = 0.707106781; - z = 0; - - } else { - - z = Math.sqrt( zz ); - x = xz / z; - y = yz / z; - - } - - } - - this.set( x, y, z, angle ); - - return this; // return 180 deg rotation - - } - - // as we have reached here there are no singularities so we can handle normally - - let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + - ( m13 - m31 ) * ( m13 - m31 ) + - ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize - - if ( Math.abs( s ) < 0.001 ) s = 1; - - // prevent divide by zero, should not happen if matrix is orthogonal and should be - // caught by singularity test above, but I've left it in just in case - - this.x = ( m32 - m23 ) / s; - this.y = ( m13 - m31 ) / s; - this.z = ( m21 - m12 ) / s; - this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); - - return this; - - } - - min( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - this.w = Math.min( this.w, v.w ); - - return this; - - } - - max( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - this.w = Math.max( this.w, v.w ); - - return this; - - } - - clamp( min, max ) { - - // assumes min < max, componentwise - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - this.z = Math.max( min.z, Math.min( max.z, this.z ) ); - this.w = Math.max( min.w, Math.min( max.w, this.w ) ); - - return this; - - } - - clampScalar( minVal, maxVal ) { - - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); - - return this; - - } - - clampLength( min, max ) { - - const length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - } - - floor() { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - this.w = Math.floor( this.w ); - - return this; - - } - - ceil() { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - this.w = Math.ceil( this.w ); - - return this; - - } - - round() { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - this.w = Math.round( this.w ); - - return this; - - } - - roundToZero() { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); - this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); - - return this; - - } - - negate() { - - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - this.w = - this.w; - - return this; - - } - - dot( v ) { - - return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; - - } - - lengthSq() { - - return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; - - } - - length() { - - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); - - } - - manhattanLength() { - - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); - - } - - normalize() { - - return this.divideScalar( this.length() || 1 ); - - } - - setLength( length ) { - - return this.normalize().multiplyScalar( length ); - - } - - lerp( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - this.w += ( v.w - this.w ) * alpha; - - return this; - - } - - lerpVectors( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - this.z = v1.z + ( v2.z - v1.z ) * alpha; - this.w = v1.w + ( v2.w - v1.w ) * alpha; - - return this; - - } - - equals( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); - - } - - fromArray( array, offset = 0 ) { - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - this.w = array[ offset + 3 ]; - - return this; - - } - - toArray( array = [], offset = 0 ) { - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - array[ offset + 3 ] = this.w; - - return array; - - } - - fromBufferAttribute( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - this.w = attribute.getW( index ); - - return this; - - } - - random() { - - this.x = Math.random(); - this.y = Math.random(); - this.z = Math.random(); - this.w = Math.random(); - - return this; - - } - - } - - Vector4.prototype.isVector4 = true; - - /* - In options, we can specify: - * Texture parameters for an auto-generated target texture - * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers - */ - class WebGLRenderTarget extends EventDispatcher { - - constructor( width, height, options = {} ) { - - super(); - - this.width = width; - this.height = height; - this.depth = 1; - - this.scissor = new Vector4( 0, 0, width, height ); - this.scissorTest = false; - - this.viewport = new Vector4( 0, 0, width, height ); - - this.texture = new Texture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); - this.texture.isRenderTargetTexture = true; - - this.texture.image = { width: width, height: height, depth: 1 }; - - this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; - this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; - this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; - - this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; - this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; - this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; - - } - - setTexture( texture ) { - - texture.image = { - width: this.width, - height: this.height, - depth: this.depth - }; - - this.texture = texture; - - } - - setSize( width, height, depth = 1 ) { - - if ( this.width !== width || this.height !== height || this.depth !== depth ) { - - this.width = width; - this.height = height; - this.depth = depth; - - this.texture.image.width = width; - this.texture.image.height = height; - this.texture.image.depth = depth; - - this.dispose(); - - } - - this.viewport.set( 0, 0, width, height ); - this.scissor.set( 0, 0, width, height ); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( source ) { - - this.width = source.width; - this.height = source.height; - this.depth = source.depth; - - this.viewport.copy( source.viewport ); - - this.texture = source.texture.clone(); - this.texture.image = { ...this.texture.image }; // See #20328. - - this.depthBuffer = source.depthBuffer; - this.stencilBuffer = source.stencilBuffer; - this.depthTexture = source.depthTexture; - - return this; - - } - - dispose() { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } - - WebGLRenderTarget.prototype.isWebGLRenderTarget = true; - - class WebGLMultipleRenderTargets extends WebGLRenderTarget { - - constructor( width, height, count ) { - - super( width, height ); - - const texture = this.texture; - - this.texture = []; - - for ( let i = 0; i < count; i ++ ) { - - this.texture[ i ] = texture.clone(); - - } - - } - - setSize( width, height, depth = 1 ) { - - if ( this.width !== width || this.height !== height || this.depth !== depth ) { - - this.width = width; - this.height = height; - this.depth = depth; - - for ( let i = 0, il = this.texture.length; i < il; i ++ ) { - - this.texture[ i ].image.width = width; - this.texture[ i ].image.height = height; - this.texture[ i ].image.depth = depth; - - } - - this.dispose(); - - } - - this.viewport.set( 0, 0, width, height ); - this.scissor.set( 0, 0, width, height ); - - return this; - - } - - copy( source ) { - - this.dispose(); - - this.width = source.width; - this.height = source.height; - this.depth = source.depth; - - this.viewport.set( 0, 0, this.width, this.height ); - this.scissor.set( 0, 0, this.width, this.height ); - - this.depthBuffer = source.depthBuffer; - this.stencilBuffer = source.stencilBuffer; - this.depthTexture = source.depthTexture; - - this.texture.length = 0; - - for ( let i = 0, il = source.texture.length; i < il; i ++ ) { - - this.texture[ i ] = source.texture[ i ].clone(); - - } - - return this; - - } - - } - - WebGLMultipleRenderTargets.prototype.isWebGLMultipleRenderTargets = true; - - class WebGLMultisampleRenderTarget extends WebGLRenderTarget { - - constructor( width, height, options ) { - - super( width, height, options ); - - this.samples = 4; - - } - - copy( source ) { - - super.copy.call( this, source ); - - this.samples = source.samples; - - return this; - - } - - } - - WebGLMultisampleRenderTarget.prototype.isWebGLMultisampleRenderTarget = true; - - class Quaternion { - - constructor( x = 0, y = 0, z = 0, w = 1 ) { - - this._x = x; - this._y = y; - this._z = z; - this._w = w; - - } - - static slerp( qa, qb, qm, t ) { - - console.warn( 'THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead.' ); - return qm.slerpQuaternions( qa, qb, t ); - - } - - static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { - - // fuzz-free, array-based Quaternion SLERP operation - - let x0 = src0[ srcOffset0 + 0 ], - y0 = src0[ srcOffset0 + 1 ], - z0 = src0[ srcOffset0 + 2 ], - w0 = src0[ srcOffset0 + 3 ]; - - const x1 = src1[ srcOffset1 + 0 ], - y1 = src1[ srcOffset1 + 1 ], - z1 = src1[ srcOffset1 + 2 ], - w1 = src1[ srcOffset1 + 3 ]; - - if ( t === 0 ) { - - dst[ dstOffset + 0 ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; - return; - - } - - if ( t === 1 ) { - - dst[ dstOffset + 0 ] = x1; - dst[ dstOffset + 1 ] = y1; - dst[ dstOffset + 2 ] = z1; - dst[ dstOffset + 3 ] = w1; - return; - - } - - if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { - - let s = 1 - t; - const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, - dir = ( cos >= 0 ? 1 : - 1 ), - sqrSin = 1 - cos * cos; - - // Skip the Slerp for tiny steps to avoid numeric problems: - if ( sqrSin > Number.EPSILON ) { - - const sin = Math.sqrt( sqrSin ), - len = Math.atan2( sin, cos * dir ); - - s = Math.sin( s * len ) / sin; - t = Math.sin( t * len ) / sin; - - } - - const tDir = t * dir; - - x0 = x0 * s + x1 * tDir; - y0 = y0 * s + y1 * tDir; - z0 = z0 * s + z1 * tDir; - w0 = w0 * s + w1 * tDir; - - // Normalize in case we just did a lerp: - if ( s === 1 - t ) { - - const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); - - x0 *= f; - y0 *= f; - z0 *= f; - w0 *= f; - - } - - } - - dst[ dstOffset ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; - - } - - static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { - - const x0 = src0[ srcOffset0 ]; - const y0 = src0[ srcOffset0 + 1 ]; - const z0 = src0[ srcOffset0 + 2 ]; - const w0 = src0[ srcOffset0 + 3 ]; - - const x1 = src1[ srcOffset1 ]; - const y1 = src1[ srcOffset1 + 1 ]; - const z1 = src1[ srcOffset1 + 2 ]; - const w1 = src1[ srcOffset1 + 3 ]; - - dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; - dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; - dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; - dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; - - return dst; - - } - - get x() { - - return this._x; - - } - - set x( value ) { - - this._x = value; - this._onChangeCallback(); - - } - - get y() { - - return this._y; - - } - - set y( value ) { - - this._y = value; - this._onChangeCallback(); - - } - - get z() { - - return this._z; - - } - - set z( value ) { - - this._z = value; - this._onChangeCallback(); - - } - - get w() { - - return this._w; - - } - - set w( value ) { - - this._w = value; - this._onChangeCallback(); - - } - - set( x, y, z, w ) { - - this._x = x; - this._y = y; - this._z = z; - this._w = w; - - this._onChangeCallback(); - - return this; - - } - - clone() { - - return new this.constructor( this._x, this._y, this._z, this._w ); - - } - - copy( quaternion ) { - - this._x = quaternion.x; - this._y = quaternion.y; - this._z = quaternion.z; - this._w = quaternion.w; - - this._onChangeCallback(); - - return this; - - } - - setFromEuler( euler, update ) { - - if ( ! ( euler && euler.isEuler ) ) { - - throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - - } - - const x = euler._x, y = euler._y, z = euler._z, order = euler._order; - - // http://www.mathworks.com/matlabcentral/fileexchange/ - // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ - // content/SpinCalc.m - - const cos = Math.cos; - const sin = Math.sin; - - const c1 = cos( x / 2 ); - const c2 = cos( y / 2 ); - const c3 = cos( z / 2 ); - - const s1 = sin( x / 2 ); - const s2 = sin( y / 2 ); - const s3 = sin( z / 2 ); - - switch ( order ) { - - case 'XYZ': - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - break; - - case 'YXZ': - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - break; - - case 'ZXY': - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - break; - - case 'ZYX': - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - break; - - case 'YZX': - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - break; - - case 'XZY': - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - break; - - default: - console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order ); - - } - - if ( update !== false ) this._onChangeCallback(); - - return this; - - } - - setFromAxisAngle( axis, angle ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - - // assumes axis is normalized - - const halfAngle = angle / 2, s = Math.sin( halfAngle ); - - this._x = axis.x * s; - this._y = axis.y * s; - this._z = axis.z * s; - this._w = Math.cos( halfAngle ); - - this._onChangeCallback(); - - return this; - - } - - setFromRotationMatrix( m ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - const te = m.elements, - - m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], - m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], - m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], - - trace = m11 + m22 + m33; - - if ( trace > 0 ) { - - const s = 0.5 / Math.sqrt( trace + 1.0 ); - - this._w = 0.25 / s; - this._x = ( m32 - m23 ) * s; - this._y = ( m13 - m31 ) * s; - this._z = ( m21 - m12 ) * s; - - } else if ( m11 > m22 && m11 > m33 ) { - - const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); - - this._w = ( m32 - m23 ) / s; - this._x = 0.25 * s; - this._y = ( m12 + m21 ) / s; - this._z = ( m13 + m31 ) / s; - - } else if ( m22 > m33 ) { - - const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); - - this._w = ( m13 - m31 ) / s; - this._x = ( m12 + m21 ) / s; - this._y = 0.25 * s; - this._z = ( m23 + m32 ) / s; - - } else { - - const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); - - this._w = ( m21 - m12 ) / s; - this._x = ( m13 + m31 ) / s; - this._y = ( m23 + m32 ) / s; - this._z = 0.25 * s; - - } - - this._onChangeCallback(); - - return this; - - } - - setFromUnitVectors( vFrom, vTo ) { - - // assumes direction vectors vFrom and vTo are normalized - - let r = vFrom.dot( vTo ) + 1; - - if ( r < Number.EPSILON ) { - - // vFrom and vTo point in opposite directions - - r = 0; - - if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { - - this._x = - vFrom.y; - this._y = vFrom.x; - this._z = 0; - this._w = r; - - } else { - - this._x = 0; - this._y = - vFrom.z; - this._z = vFrom.y; - this._w = r; - - } - - } else { - - // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 - - this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; - this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; - this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; - this._w = r; - - } - - return this.normalize(); - - } - - angleTo( q ) { - - return 2 * Math.acos( Math.abs( clamp( this.dot( q ), - 1, 1 ) ) ); - - } - - rotateTowards( q, step ) { - - const angle = this.angleTo( q ); - - if ( angle === 0 ) return this; - - const t = Math.min( 1, step / angle ); - - this.slerp( q, t ); - - return this; - - } - - identity() { - - return this.set( 0, 0, 0, 1 ); - - } - - invert() { - - // quaternion is assumed to have unit length - - return this.conjugate(); - - } - - conjugate() { - - this._x *= - 1; - this._y *= - 1; - this._z *= - 1; - - this._onChangeCallback(); - - return this; - - } - - dot( v ) { - - return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; - - } - - lengthSq() { - - return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; - - } - - length() { - - return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); - - } - - normalize() { - - let l = this.length(); - - if ( l === 0 ) { - - this._x = 0; - this._y = 0; - this._z = 0; - this._w = 1; - - } else { - - l = 1 / l; - - this._x = this._x * l; - this._y = this._y * l; - this._z = this._z * l; - this._w = this._w * l; - - } - - this._onChangeCallback(); - - return this; - - } - - multiply( q, p ) { - - if ( p !== undefined ) { - - console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); - return this.multiplyQuaternions( q, p ); - - } - - return this.multiplyQuaternions( this, q ); - - } - - premultiply( q ) { - - return this.multiplyQuaternions( q, this ); - - } - - multiplyQuaternions( a, b ) { - - // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm - - const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; - const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; - - this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; - this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; - this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; - this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; - - this._onChangeCallback(); - - return this; - - } - - slerp( qb, t ) { - - if ( t === 0 ) return this; - if ( t === 1 ) return this.copy( qb ); - - const x = this._x, y = this._y, z = this._z, w = this._w; - - // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ - - let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; - - if ( cosHalfTheta < 0 ) { - - this._w = - qb._w; - this._x = - qb._x; - this._y = - qb._y; - this._z = - qb._z; - - cosHalfTheta = - cosHalfTheta; - - } else { - - this.copy( qb ); - - } - - if ( cosHalfTheta >= 1.0 ) { - - this._w = w; - this._x = x; - this._y = y; - this._z = z; - - return this; - - } - - const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; - - if ( sqrSinHalfTheta <= Number.EPSILON ) { - - const s = 1 - t; - this._w = s * w + t * this._w; - this._x = s * x + t * this._x; - this._y = s * y + t * this._y; - this._z = s * z + t * this._z; - - this.normalize(); - this._onChangeCallback(); - - return this; - - } - - const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); - const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); - const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, - ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; - - this._w = ( w * ratioA + this._w * ratioB ); - this._x = ( x * ratioA + this._x * ratioB ); - this._y = ( y * ratioA + this._y * ratioB ); - this._z = ( z * ratioA + this._z * ratioB ); - - this._onChangeCallback(); - - return this; - - } - - slerpQuaternions( qa, qb, t ) { - - this.copy( qa ).slerp( qb, t ); - - } - - equals( quaternion ) { - - return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); - - } - - fromArray( array, offset = 0 ) { - - this._x = array[ offset ]; - this._y = array[ offset + 1 ]; - this._z = array[ offset + 2 ]; - this._w = array[ offset + 3 ]; - - this._onChangeCallback(); - - return this; - - } - - toArray( array = [], offset = 0 ) { - - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._w; - - return array; - - } - - fromBufferAttribute( attribute, index ) { - - this._x = attribute.getX( index ); - this._y = attribute.getY( index ); - this._z = attribute.getZ( index ); - this._w = attribute.getW( index ); - - return this; - - } - - _onChange( callback ) { - - this._onChangeCallback = callback; - - return this; - - } - - _onChangeCallback() {} - - } - - Quaternion.prototype.isQuaternion = true; - - class Vector3 { - - constructor( x = 0, y = 0, z = 0 ) { - - this.x = x; - this.y = y; - this.z = z; - - } - - set( x, y, z ) { - - if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) - - this.x = x; - this.y = y; - this.z = z; - - return this; - - } - - setScalar( scalar ) { - - this.x = scalar; - this.y = scalar; - this.z = scalar; - - return this; - - } - - setX( x ) { - - this.x = x; - - return this; - - } - - setY( y ) { - - this.y = y; - - return this; - - } - - setZ( z ) { - - this.z = z; - - return this; - - } - - setComponent( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - } - - getComponent( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - default: throw new Error( 'index is out of range: ' + index ); - - } - - } - - clone() { - - return new this.constructor( this.x, this.y, this.z ); - - } - - copy( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; - - return this; - - } - - add( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - this.z += v.z; - - return this; - - } - - addScalar( s ) { - - this.x += s; - this.y += s; - this.z += s; - - return this; - - } - - addVectors( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - - return this; - - } - - addScaledVector( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - - return this; - - } - - sub( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - - return this; - - } - - subScalar( s ) { - - this.x -= s; - this.y -= s; - this.z -= s; - - return this; - - } - - subVectors( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - - return this; - - } - - multiply( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); - return this.multiplyVectors( v, w ); - - } - - this.x *= v.x; - this.y *= v.y; - this.z *= v.z; - - return this; - - } - - multiplyScalar( scalar ) { - - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - - return this; - - } - - multiplyVectors( a, b ) { - - this.x = a.x * b.x; - this.y = a.y * b.y; - this.z = a.z * b.z; - - return this; - - } - - applyEuler( euler ) { - - if ( ! ( euler && euler.isEuler ) ) { - - console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - - } - - return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) ); - - } - - applyAxisAngle( axis, angle ) { - - return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) ); - - } - - applyMatrix3( m ) { - - const x = this.x, y = this.y, z = this.z; - const e = m.elements; - - this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; - this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; - this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; - - return this; - - } - - applyNormalMatrix( m ) { - - return this.applyMatrix3( m ).normalize(); - - } - - applyMatrix4( m ) { - - const x = this.x, y = this.y, z = this.z; - const e = m.elements; - - const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); - - this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; - this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; - this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; - - return this; - - } - - applyQuaternion( q ) { - - const x = this.x, y = this.y, z = this.z; - const qx = q.x, qy = q.y, qz = q.z, qw = q.w; - - // calculate quat * vector - - const ix = qw * x + qy * z - qz * y; - const iy = qw * y + qz * x - qx * z; - const iz = qw * z + qx * y - qy * x; - const iw = - qx * x - qy * y - qz * z; - - // calculate result * inverse quat - - this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; - this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; - this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; - - return this; - - } - - project( camera ) { - - return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); - - } - - unproject( camera ) { - - return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); - - } - - transformDirection( m ) { - - // input: THREE.Matrix4 affine matrix - // vector interpreted as a direction - - const x = this.x, y = this.y, z = this.z; - const e = m.elements; - - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; - - return this.normalize(); - - } - - divide( v ) { - - this.x /= v.x; - this.y /= v.y; - this.z /= v.z; - - return this; - - } - - divideScalar( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - } - - min( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - - return this; - - } - - max( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - - return this; - - } - - clamp( min, max ) { - - // assumes min < max, componentwise - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - this.z = Math.max( min.z, Math.min( max.z, this.z ) ); - - return this; - - } - - clampScalar( minVal, maxVal ) { - - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - - return this; - - } - - clampLength( min, max ) { - - const length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - } - - floor() { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - - return this; - - } - - ceil() { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - - return this; - - } - - round() { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - - return this; - - } - - roundToZero() { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); - - return this; - - } - - negate() { - - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - - return this; - - } - - dot( v ) { - - return this.x * v.x + this.y * v.y + this.z * v.z; - - } - - // TODO lengthSquared? - - lengthSq() { - - return this.x * this.x + this.y * this.y + this.z * this.z; - - } - - length() { - - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); - - } - - manhattanLength() { - - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); - - } - - normalize() { - - return this.divideScalar( this.length() || 1 ); - - } - - setLength( length ) { - - return this.normalize().multiplyScalar( length ); - - } - - lerp( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - - return this; - - } - - lerpVectors( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - this.z = v1.z + ( v2.z - v1.z ) * alpha; - - return this; - - } - - cross( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); - return this.crossVectors( v, w ); - - } - - return this.crossVectors( this, v ); - - } - - crossVectors( a, b ) { - - const ax = a.x, ay = a.y, az = a.z; - const bx = b.x, by = b.y, bz = b.z; - - this.x = ay * bz - az * by; - this.y = az * bx - ax * bz; - this.z = ax * by - ay * bx; - - return this; - - } - - projectOnVector( v ) { - - const denominator = v.lengthSq(); - - if ( denominator === 0 ) return this.set( 0, 0, 0 ); - - const scalar = v.dot( this ) / denominator; - - return this.copy( v ).multiplyScalar( scalar ); - - } - - projectOnPlane( planeNormal ) { - - _vector$c.copy( this ).projectOnVector( planeNormal ); - - return this.sub( _vector$c ); - - } - - reflect( normal ) { - - // reflect incident vector off plane orthogonal to normal - // normal is assumed to have unit length - - return this.sub( _vector$c.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); - - } - - angleTo( v ) { - - const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); - - if ( denominator === 0 ) return Math.PI / 2; - - const theta = this.dot( v ) / denominator; - - // clamp, to handle numerical problems - - return Math.acos( clamp( theta, - 1, 1 ) ); - - } - - distanceTo( v ) { - - return Math.sqrt( this.distanceToSquared( v ) ); - - } - - distanceToSquared( v ) { - - const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; - - return dx * dx + dy * dy + dz * dz; - - } - - manhattanDistanceTo( v ) { - - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); - - } - - setFromSpherical( s ) { - - return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); - - } - - setFromSphericalCoords( radius, phi, theta ) { - - const sinPhiRadius = Math.sin( phi ) * radius; - - this.x = sinPhiRadius * Math.sin( theta ); - this.y = Math.cos( phi ) * radius; - this.z = sinPhiRadius * Math.cos( theta ); - - return this; - - } - - setFromCylindrical( c ) { - - return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); - - } - - setFromCylindricalCoords( radius, theta, y ) { - - this.x = radius * Math.sin( theta ); - this.y = y; - this.z = radius * Math.cos( theta ); - - return this; - - } - - setFromMatrixPosition( m ) { - - const e = m.elements; - - this.x = e[ 12 ]; - this.y = e[ 13 ]; - this.z = e[ 14 ]; - - return this; - - } - - setFromMatrixScale( m ) { - - const sx = this.setFromMatrixColumn( m, 0 ).length(); - const sy = this.setFromMatrixColumn( m, 1 ).length(); - const sz = this.setFromMatrixColumn( m, 2 ).length(); - - this.x = sx; - this.y = sy; - this.z = sz; - - return this; - - } - - setFromMatrixColumn( m, index ) { - - return this.fromArray( m.elements, index * 4 ); - - } - - setFromMatrix3Column( m, index ) { - - return this.fromArray( m.elements, index * 3 ); - - } - - equals( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); - - } - - fromArray( array, offset = 0 ) { - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - - return this; - - } - - toArray( array = [], offset = 0 ) { - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - - return array; - - } - - fromBufferAttribute( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - - return this; - - } - - random() { - - this.x = Math.random(); - this.y = Math.random(); - this.z = Math.random(); - - return this; - - } - - } - - Vector3.prototype.isVector3 = true; - - const _vector$c = /*@__PURE__*/ new Vector3(); - const _quaternion$4 = /*@__PURE__*/ new Quaternion(); - - class Box3 { - - constructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) { - - this.min = min; - this.max = max; - - } - - set( min, max ) { - - this.min.copy( min ); - this.max.copy( max ); - - return this; - - } - - setFromArray( array ) { - - let minX = + Infinity; - let minY = + Infinity; - let minZ = + Infinity; - - let maxX = - Infinity; - let maxY = - Infinity; - let maxZ = - Infinity; - - for ( let i = 0, l = array.length; i < l; i += 3 ) { - - const x = array[ i ]; - const y = array[ i + 1 ]; - const z = array[ i + 2 ]; - - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; - - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; - - } - - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); - - return this; - - } - - setFromBufferAttribute( attribute ) { - - let minX = + Infinity; - let minY = + Infinity; - let minZ = + Infinity; - - let maxX = - Infinity; - let maxY = - Infinity; - let maxZ = - Infinity; - - for ( let i = 0, l = attribute.count; i < l; i ++ ) { - - const x = attribute.getX( i ); - const y = attribute.getY( i ); - const z = attribute.getZ( i ); - - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; - - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; - - } - - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); - - return this; - - } - - setFromPoints( points ) { - - this.makeEmpty(); - - for ( let i = 0, il = points.length; i < il; i ++ ) { - - this.expandByPoint( points[ i ] ); - - } - - return this; - - } - - setFromCenterAndSize( center, size ) { - - const halfSize = _vector$b.copy( size ).multiplyScalar( 0.5 ); - - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); - - return this; - - } - - setFromObject( object ) { - - this.makeEmpty(); - - return this.expandByObject( object ); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( box ) { - - this.min.copy( box.min ); - this.max.copy( box.max ); - - return this; - - } - - makeEmpty() { - - this.min.x = this.min.y = this.min.z = + Infinity; - this.max.x = this.max.y = this.max.z = - Infinity; - - return this; - - } - - isEmpty() { - - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); - - } - - getCenter( target ) { - - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); - - } - - getSize( target ) { - - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); - - } - - expandByPoint( point ) { - - this.min.min( point ); - this.max.max( point ); - - return this; - - } - - expandByVector( vector ) { - - this.min.sub( vector ); - this.max.add( vector ); - - return this; - - } - - expandByScalar( scalar ) { - - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); - - return this; - - } - - expandByObject( object ) { - - // Computes the world-axis-aligned bounding box of an object (including its children), - // accounting for both the object's, and children's, world transforms - - object.updateWorldMatrix( false, false ); - - const geometry = object.geometry; - - if ( geometry !== undefined ) { - - if ( geometry.boundingBox === null ) { - - geometry.computeBoundingBox(); - - } - - _box$3.copy( geometry.boundingBox ); - _box$3.applyMatrix4( object.matrixWorld ); - - this.union( _box$3 ); - - } - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - this.expandByObject( children[ i ] ); - - } - - return this; - - } - - containsPoint( point ) { - - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y || - point.z < this.min.z || point.z > this.max.z ? false : true; - - } - - containsBox( box ) { - - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y && - this.min.z <= box.min.z && box.max.z <= this.max.z; - - } - - getParameter( point, target ) { - - // This can potentially have a divide by zero if the box - // has a size dimension of 0. - - return target.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ), - ( point.z - this.min.z ) / ( this.max.z - this.min.z ) - ); - - } - - intersectsBox( box ) { - - // using 6 splitting planes to rule out intersections. - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y || - box.max.z < this.min.z || box.min.z > this.max.z ? false : true; - - } - - intersectsSphere( sphere ) { - - // Find the point on the AABB closest to the sphere center. - this.clampPoint( sphere.center, _vector$b ); - - // If that point is inside the sphere, the AABB and sphere intersect. - return _vector$b.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); - - } - - intersectsPlane( plane ) { - - // We compute the minimum and maximum dot product values. If those values - // are on the same side (back or front) of the plane, then there is no intersection. - - let min, max; - - if ( plane.normal.x > 0 ) { - - min = plane.normal.x * this.min.x; - max = plane.normal.x * this.max.x; - - } else { - - min = plane.normal.x * this.max.x; - max = plane.normal.x * this.min.x; - - } - - if ( plane.normal.y > 0 ) { - - min += plane.normal.y * this.min.y; - max += plane.normal.y * this.max.y; - - } else { - - min += plane.normal.y * this.max.y; - max += plane.normal.y * this.min.y; - - } - - if ( plane.normal.z > 0 ) { - - min += plane.normal.z * this.min.z; - max += plane.normal.z * this.max.z; - - } else { - - min += plane.normal.z * this.max.z; - max += plane.normal.z * this.min.z; - - } - - return ( min <= - plane.constant && max >= - plane.constant ); - - } - - intersectsTriangle( triangle ) { - - if ( this.isEmpty() ) { - - return false; - - } - - // compute box center and extents - this.getCenter( _center ); - _extents.subVectors( this.max, _center ); - - // translate triangle to aabb origin - _v0$2.subVectors( triangle.a, _center ); - _v1$7.subVectors( triangle.b, _center ); - _v2$3.subVectors( triangle.c, _center ); - - // compute edge vectors for triangle - _f0.subVectors( _v1$7, _v0$2 ); - _f1.subVectors( _v2$3, _v1$7 ); - _f2.subVectors( _v0$2, _v2$3 ); - - // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb - // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation - // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) - let axes = [ - 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, - _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, - - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 - ]; - if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { - - return false; - - } - - // test 3 face normals from the aabb - axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; - if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { - - return false; - - } - - // finally testing the face normal of the triangle - // use already existing triangle edge vectors here - _triangleNormal.crossVectors( _f0, _f1 ); - axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; - - return satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ); - - } - - clampPoint( point, target ) { - - return target.copy( point ).clamp( this.min, this.max ); - - } - - distanceToPoint( point ) { - - const clampedPoint = _vector$b.copy( point ).clamp( this.min, this.max ); - - return clampedPoint.sub( point ).length(); - - } - - getBoundingSphere( target ) { - - this.getCenter( target.center ); - - target.radius = this.getSize( _vector$b ).length() * 0.5; - - return target; - - } - - intersect( box ) { - - this.min.max( box.min ); - this.max.min( box.max ); - - // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. - if ( this.isEmpty() ) this.makeEmpty(); - - return this; - - } - - union( box ) { - - this.min.min( box.min ); - this.max.max( box.max ); - - return this; - - } - - applyMatrix4( matrix ) { - - // transform of empty box is an empty box. - if ( this.isEmpty() ) return this; - - // NOTE: I am using a binary pattern to specify all 2^3 combinations below - _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 - _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 - _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 - _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 - _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 - _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 - _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 - _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 - - this.setFromPoints( _points ); - - return this; - - } - - translate( offset ) { - - this.min.add( offset ); - this.max.add( offset ); - - return this; - - } - - equals( box ) { - - return box.min.equals( this.min ) && box.max.equals( this.max ); - - } - - } - - Box3.prototype.isBox3 = true; - - const _points = [ - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3() - ]; - - const _vector$b = /*@__PURE__*/ new Vector3(); - - const _box$3 = /*@__PURE__*/ new Box3(); - - // triangle centered vertices - - const _v0$2 = /*@__PURE__*/ new Vector3(); - const _v1$7 = /*@__PURE__*/ new Vector3(); - const _v2$3 = /*@__PURE__*/ new Vector3(); - - // triangle edge vectors - - const _f0 = /*@__PURE__*/ new Vector3(); - const _f1 = /*@__PURE__*/ new Vector3(); - const _f2 = /*@__PURE__*/ new Vector3(); - - const _center = /*@__PURE__*/ new Vector3(); - const _extents = /*@__PURE__*/ new Vector3(); - const _triangleNormal = /*@__PURE__*/ new Vector3(); - const _testAxis = /*@__PURE__*/ new Vector3(); - - function satForAxes( axes, v0, v1, v2, extents ) { - - for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { - - _testAxis.fromArray( axes, i ); - // project the aabb onto the seperating axis - const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); - // project all 3 vertices of the triangle onto the seperating axis - const p0 = v0.dot( _testAxis ); - const p1 = v1.dot( _testAxis ); - const p2 = v2.dot( _testAxis ); - // actual test, basically see if either of the most extreme of the triangle points intersects r - if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { - - // points of the projected triangle are outside the projected half-length of the aabb - // the axis is seperating and we can exit - return false; - - } - - } - - return true; - - } - - const _box$2 = /*@__PURE__*/ new Box3(); - const _v1$6 = /*@__PURE__*/ new Vector3(); - const _toFarthestPoint = /*@__PURE__*/ new Vector3(); - const _toPoint = /*@__PURE__*/ new Vector3(); - - class Sphere { - - constructor( center = new Vector3(), radius = - 1 ) { - - this.center = center; - this.radius = radius; - - } - - set( center, radius ) { - - this.center.copy( center ); - this.radius = radius; - - return this; - - } - - setFromPoints( points, optionalCenter ) { - - const center = this.center; - - if ( optionalCenter !== undefined ) { - - center.copy( optionalCenter ); - - } else { - - _box$2.setFromPoints( points ).getCenter( center ); - - } - - let maxRadiusSq = 0; - - for ( let i = 0, il = points.length; i < il; i ++ ) { - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); - - } - - this.radius = Math.sqrt( maxRadiusSq ); - - return this; - - } - - copy( sphere ) { - - this.center.copy( sphere.center ); - this.radius = sphere.radius; - - return this; - - } - - isEmpty() { - - return ( this.radius < 0 ); - - } - - makeEmpty() { - - this.center.set( 0, 0, 0 ); - this.radius = - 1; - - return this; - - } - - containsPoint( point ) { - - return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); - - } - - distanceToPoint( point ) { - - return ( point.distanceTo( this.center ) - this.radius ); - - } - - intersectsSphere( sphere ) { - - const radiusSum = this.radius + sphere.radius; - - return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); - - } - - intersectsBox( box ) { - - return box.intersectsSphere( this ); - - } - - intersectsPlane( plane ) { - - return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; - - } - - clampPoint( point, target ) { - - const deltaLengthSq = this.center.distanceToSquared( point ); - - target.copy( point ); - - if ( deltaLengthSq > ( this.radius * this.radius ) ) { - - target.sub( this.center ).normalize(); - target.multiplyScalar( this.radius ).add( this.center ); - - } - - return target; - - } - - getBoundingBox( target ) { - - if ( this.isEmpty() ) { - - // Empty sphere produces empty bounding box - target.makeEmpty(); - return target; - - } - - target.set( this.center, this.center ); - target.expandByScalar( this.radius ); - - return target; - - } - - applyMatrix4( matrix ) { - - this.center.applyMatrix4( matrix ); - this.radius = this.radius * matrix.getMaxScaleOnAxis(); - - return this; - - } - - translate( offset ) { - - this.center.add( offset ); - - return this; - - } - - expandByPoint( point ) { - - // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 - - _toPoint.subVectors( point, this.center ); - - const lengthSq = _toPoint.lengthSq(); - - if ( lengthSq > ( this.radius * this.radius ) ) { - - const length = Math.sqrt( lengthSq ); - const missingRadiusHalf = ( length - this.radius ) * 0.5; - - // Nudge this sphere towards the target point. Add half the missing distance to radius, - // and the other half to position. This gives a tighter enclosure, instead of if - // the whole missing distance were just added to radius. - - this.center.add( _toPoint.multiplyScalar( missingRadiusHalf / length ) ); - this.radius += missingRadiusHalf; - - } - - return this; - - } - - union( sphere ) { - - // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 - - // To enclose another sphere into this sphere, we only need to enclose two points: - // 1) Enclose the farthest point on the other sphere into this sphere. - // 2) Enclose the opposite point of the farthest point into this sphere. - - _toFarthestPoint.subVectors( sphere.center, this.center ).normalize().multiplyScalar( sphere.radius ); - - this.expandByPoint( _v1$6.copy( sphere.center ).add( _toFarthestPoint ) ); - this.expandByPoint( _v1$6.copy( sphere.center ).sub( _toFarthestPoint ) ); - - return this; - - } - - equals( sphere ) { - - return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - } - - const _vector$a = /*@__PURE__*/ new Vector3(); - const _segCenter = /*@__PURE__*/ new Vector3(); - const _segDir = /*@__PURE__*/ new Vector3(); - const _diff = /*@__PURE__*/ new Vector3(); - - const _edge1 = /*@__PURE__*/ new Vector3(); - const _edge2 = /*@__PURE__*/ new Vector3(); - const _normal$1 = /*@__PURE__*/ new Vector3(); - - class Ray { - - constructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) { - - this.origin = origin; - this.direction = direction; - - } - - set( origin, direction ) { - - this.origin.copy( origin ); - this.direction.copy( direction ); - - return this; - - } - - copy( ray ) { - - this.origin.copy( ray.origin ); - this.direction.copy( ray.direction ); - - return this; - - } - - at( t, target ) { - - return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); - - } - - lookAt( v ) { - - this.direction.copy( v ).sub( this.origin ).normalize(); - - return this; - - } - - recast( t ) { - - this.origin.copy( this.at( t, _vector$a ) ); - - return this; - - } - - closestPointToPoint( point, target ) { - - target.subVectors( point, this.origin ); - - const directionDistance = target.dot( this.direction ); - - if ( directionDistance < 0 ) { - - return target.copy( this.origin ); - - } - - return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - - } - - distanceToPoint( point ) { - - return Math.sqrt( this.distanceSqToPoint( point ) ); - - } - - distanceSqToPoint( point ) { - - const directionDistance = _vector$a.subVectors( point, this.origin ).dot( this.direction ); - - // point behind the ray - - if ( directionDistance < 0 ) { - - return this.origin.distanceToSquared( point ); - - } - - _vector$a.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - - return _vector$a.distanceToSquared( point ); - - } - - distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { - - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h - // It returns the min distance between the ray and the segment - // defined by v0 and v1 - // It can also set two optional targets : - // - The closest point on the ray - // - The closest point on the segment - - _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); - _segDir.copy( v1 ).sub( v0 ).normalize(); - _diff.copy( this.origin ).sub( _segCenter ); - - const segExtent = v0.distanceTo( v1 ) * 0.5; - const a01 = - this.direction.dot( _segDir ); - const b0 = _diff.dot( this.direction ); - const b1 = - _diff.dot( _segDir ); - const c = _diff.lengthSq(); - const det = Math.abs( 1 - a01 * a01 ); - let s0, s1, sqrDist, extDet; - - if ( det > 0 ) { - - // The ray and segment are not parallel. - - s0 = a01 * b1 - b0; - s1 = a01 * b0 - b1; - extDet = segExtent * det; - - if ( s0 >= 0 ) { - - if ( s1 >= - extDet ) { - - if ( s1 <= extDet ) { - - // region 0 - // Minimum at interior points of ray and segment. - - const invDet = 1 / det; - s0 *= invDet; - s1 *= invDet; - sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; - - } else { - - // region 1 - - s1 = segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } else { - - // region 5 - - s1 = - segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } else { - - if ( s1 <= - extDet ) { - - // region 4 - - s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } else if ( s1 <= extDet ) { - - // region 3 - - s0 = 0; - s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = s1 * ( s1 + 2 * b1 ) + c; - - } else { - - // region 2 - - s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } - - } else { - - // Ray and segment are parallel. - - s1 = ( a01 > 0 ) ? - segExtent : segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - if ( optionalPointOnRay ) { - - optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); - - } - - if ( optionalPointOnSegment ) { - - optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter ); - - } - - return sqrDist; - - } - - intersectSphere( sphere, target ) { - - _vector$a.subVectors( sphere.center, this.origin ); - const tca = _vector$a.dot( this.direction ); - const d2 = _vector$a.dot( _vector$a ) - tca * tca; - const radius2 = sphere.radius * sphere.radius; - - if ( d2 > radius2 ) return null; - - const thc = Math.sqrt( radius2 - d2 ); - - // t0 = first intersect point - entrance on front of sphere - const t0 = tca - thc; - - // t1 = second intersect point - exit point on back of sphere - const t1 = tca + thc; - - // test to see if both t0 and t1 are behind the ray - if so, return null - if ( t0 < 0 && t1 < 0 ) return null; - - // test to see if t0 is behind the ray: - // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, - // in order to always return an intersect point that is in front of the ray. - if ( t0 < 0 ) return this.at( t1, target ); - - // else t0 is in front of the ray, so return the first collision point scaled by t0 - return this.at( t0, target ); - - } - - intersectsSphere( sphere ) { - - return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); - - } - - distanceToPlane( plane ) { - - const denominator = plane.normal.dot( this.direction ); - - if ( denominator === 0 ) { - - // line is coplanar, return origin - if ( plane.distanceToPoint( this.origin ) === 0 ) { - - return 0; - - } - - // Null is preferable to undefined since undefined means.... it is undefined - - return null; - - } - - const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; - - // Return if the ray never intersects the plane - - return t >= 0 ? t : null; - - } - - intersectPlane( plane, target ) { - - const t = this.distanceToPlane( plane ); - - if ( t === null ) { - - return null; - - } - - return this.at( t, target ); - - } - - intersectsPlane( plane ) { - - // check if the ray lies on the plane first - - const distToPoint = plane.distanceToPoint( this.origin ); - - if ( distToPoint === 0 ) { - - return true; - - } - - const denominator = plane.normal.dot( this.direction ); - - if ( denominator * distToPoint < 0 ) { - - return true; - - } - - // ray origin is behind the plane (and is pointing behind it) - - return false; - - } - - intersectBox( box, target ) { - - let tmin, tmax, tymin, tymax, tzmin, tzmax; - - const invdirx = 1 / this.direction.x, - invdiry = 1 / this.direction.y, - invdirz = 1 / this.direction.z; - - const origin = this.origin; - - if ( invdirx >= 0 ) { - - tmin = ( box.min.x - origin.x ) * invdirx; - tmax = ( box.max.x - origin.x ) * invdirx; - - } else { - - tmin = ( box.max.x - origin.x ) * invdirx; - tmax = ( box.min.x - origin.x ) * invdirx; - - } - - if ( invdiry >= 0 ) { - - tymin = ( box.min.y - origin.y ) * invdiry; - tymax = ( box.max.y - origin.y ) * invdiry; - - } else { - - tymin = ( box.max.y - origin.y ) * invdiry; - tymax = ( box.min.y - origin.y ) * invdiry; - - } - - if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; - - // These lines also handle the case where tmin or tmax is NaN - // (result of 0 * Infinity). x !== x returns true if x is NaN - - if ( tymin > tmin || tmin !== tmin ) tmin = tymin; - - if ( tymax < tmax || tmax !== tmax ) tmax = tymax; - - if ( invdirz >= 0 ) { - - tzmin = ( box.min.z - origin.z ) * invdirz; - tzmax = ( box.max.z - origin.z ) * invdirz; - - } else { - - tzmin = ( box.max.z - origin.z ) * invdirz; - tzmax = ( box.min.z - origin.z ) * invdirz; - - } - - if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; - - if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; - - if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; - - //return point closest to the ray (positive side) - - if ( tmax < 0 ) return null; - - return this.at( tmin >= 0 ? tmin : tmax, target ); - - } - - intersectsBox( box ) { - - return this.intersectBox( box, _vector$a ) !== null; - - } - - intersectTriangle( a, b, c, backfaceCulling, target ) { - - // Compute the offset origin, edges, and normal. - - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h - - _edge1.subVectors( b, a ); - _edge2.subVectors( c, a ); - _normal$1.crossVectors( _edge1, _edge2 ); - - // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, - // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by - // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) - // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) - // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) - let DdN = this.direction.dot( _normal$1 ); - let sign; - - if ( DdN > 0 ) { - - if ( backfaceCulling ) return null; - sign = 1; - - } else if ( DdN < 0 ) { - - sign = - 1; - DdN = - DdN; - - } else { - - return null; - - } - - _diff.subVectors( this.origin, a ); - const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); - - // b1 < 0, no intersection - if ( DdQxE2 < 0 ) { - - return null; - - } - - const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); - - // b2 < 0, no intersection - if ( DdE1xQ < 0 ) { - - return null; - - } - - // b1+b2 > 1, no intersection - if ( DdQxE2 + DdE1xQ > DdN ) { - - return null; - - } - - // Line intersects triangle, check if ray does. - const QdN = - sign * _diff.dot( _normal$1 ); - - // t < 0, no intersection - if ( QdN < 0 ) { - - return null; - - } - - // Ray intersects triangle. - return this.at( QdN / DdN, target ); - - } - - applyMatrix4( matrix4 ) { - - this.origin.applyMatrix4( matrix4 ); - this.direction.transformDirection( matrix4 ); - - return this; - - } - - equals( ray ) { - - return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - } - - class Matrix4 { - - constructor() { - - this.elements = [ - - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ]; - - if ( arguments.length > 0 ) { - - console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); - - } - - } - - set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { - - const te = this.elements; - - te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; - te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; - te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; - te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; - - return this; - - } - - identity() { - - this.set( - - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - clone() { - - return new Matrix4().fromArray( this.elements ); - - } - - copy( m ) { - - const te = this.elements; - const me = m.elements; - - te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; - te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; - te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; - te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; - - return this; - - } - - copyPosition( m ) { - - const te = this.elements, me = m.elements; - - te[ 12 ] = me[ 12 ]; - te[ 13 ] = me[ 13 ]; - te[ 14 ] = me[ 14 ]; - - return this; - - } - - setFromMatrix3( m ) { - - const me = m.elements; - - this.set( - - me[ 0 ], me[ 3 ], me[ 6 ], 0, - me[ 1 ], me[ 4 ], me[ 7 ], 0, - me[ 2 ], me[ 5 ], me[ 8 ], 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - extractBasis( xAxis, yAxis, zAxis ) { - - xAxis.setFromMatrixColumn( this, 0 ); - yAxis.setFromMatrixColumn( this, 1 ); - zAxis.setFromMatrixColumn( this, 2 ); - - return this; - - } - - makeBasis( xAxis, yAxis, zAxis ) { - - this.set( - xAxis.x, yAxis.x, zAxis.x, 0, - xAxis.y, yAxis.y, zAxis.y, 0, - xAxis.z, yAxis.z, zAxis.z, 0, - 0, 0, 0, 1 - ); - - return this; - - } - - extractRotation( m ) { - - // this method does not support reflection matrices - - const te = this.elements; - const me = m.elements; - - const scaleX = 1 / _v1$5.setFromMatrixColumn( m, 0 ).length(); - const scaleY = 1 / _v1$5.setFromMatrixColumn( m, 1 ).length(); - const scaleZ = 1 / _v1$5.setFromMatrixColumn( m, 2 ).length(); - - te[ 0 ] = me[ 0 ] * scaleX; - te[ 1 ] = me[ 1 ] * scaleX; - te[ 2 ] = me[ 2 ] * scaleX; - te[ 3 ] = 0; - - te[ 4 ] = me[ 4 ] * scaleY; - te[ 5 ] = me[ 5 ] * scaleY; - te[ 6 ] = me[ 6 ] * scaleY; - te[ 7 ] = 0; - - te[ 8 ] = me[ 8 ] * scaleZ; - te[ 9 ] = me[ 9 ] * scaleZ; - te[ 10 ] = me[ 10 ] * scaleZ; - te[ 11 ] = 0; - - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; - - return this; - - } - - makeRotationFromEuler( euler ) { - - if ( ! ( euler && euler.isEuler ) ) { - - console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); - - } - - const te = this.elements; - - const x = euler.x, y = euler.y, z = euler.z; - const a = Math.cos( x ), b = Math.sin( x ); - const c = Math.cos( y ), d = Math.sin( y ); - const e = Math.cos( z ), f = Math.sin( z ); - - if ( euler.order === 'XYZ' ) { - - const ae = a * e, af = a * f, be = b * e, bf = b * f; - - te[ 0 ] = c * e; - te[ 4 ] = - c * f; - te[ 8 ] = d; - - te[ 1 ] = af + be * d; - te[ 5 ] = ae - bf * d; - te[ 9 ] = - b * c; - - te[ 2 ] = bf - ae * d; - te[ 6 ] = be + af * d; - te[ 10 ] = a * c; - - } else if ( euler.order === 'YXZ' ) { - - const ce = c * e, cf = c * f, de = d * e, df = d * f; - - te[ 0 ] = ce + df * b; - te[ 4 ] = de * b - cf; - te[ 8 ] = a * d; - - te[ 1 ] = a * f; - te[ 5 ] = a * e; - te[ 9 ] = - b; - - te[ 2 ] = cf * b - de; - te[ 6 ] = df + ce * b; - te[ 10 ] = a * c; - - } else if ( euler.order === 'ZXY' ) { - - const ce = c * e, cf = c * f, de = d * e, df = d * f; - - te[ 0 ] = ce - df * b; - te[ 4 ] = - a * f; - te[ 8 ] = de + cf * b; - - te[ 1 ] = cf + de * b; - te[ 5 ] = a * e; - te[ 9 ] = df - ce * b; - - te[ 2 ] = - a * d; - te[ 6 ] = b; - te[ 10 ] = a * c; - - } else if ( euler.order === 'ZYX' ) { - - const ae = a * e, af = a * f, be = b * e, bf = b * f; - - te[ 0 ] = c * e; - te[ 4 ] = be * d - af; - te[ 8 ] = ae * d + bf; - - te[ 1 ] = c * f; - te[ 5 ] = bf * d + ae; - te[ 9 ] = af * d - be; - - te[ 2 ] = - d; - te[ 6 ] = b * c; - te[ 10 ] = a * c; - - } else if ( euler.order === 'YZX' ) { - - const ac = a * c, ad = a * d, bc = b * c, bd = b * d; - - te[ 0 ] = c * e; - te[ 4 ] = bd - ac * f; - te[ 8 ] = bc * f + ad; - - te[ 1 ] = f; - te[ 5 ] = a * e; - te[ 9 ] = - b * e; - - te[ 2 ] = - d * e; - te[ 6 ] = ad * f + bc; - te[ 10 ] = ac - bd * f; - - } else if ( euler.order === 'XZY' ) { - - const ac = a * c, ad = a * d, bc = b * c, bd = b * d; - - te[ 0 ] = c * e; - te[ 4 ] = - f; - te[ 8 ] = d * e; - - te[ 1 ] = ac * f + bd; - te[ 5 ] = a * e; - te[ 9 ] = ad * f - bc; - - te[ 2 ] = bc * f - ad; - te[ 6 ] = b * e; - te[ 10 ] = bd * f + ac; - - } - - // bottom row - te[ 3 ] = 0; - te[ 7 ] = 0; - te[ 11 ] = 0; - - // last column - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; - - return this; - - } - - makeRotationFromQuaternion( q ) { - - return this.compose( _zero, q, _one ); - - } - - lookAt( eye, target, up ) { - - const te = this.elements; - - _z.subVectors( eye, target ); - - if ( _z.lengthSq() === 0 ) { - - // eye and target are in the same position - - _z.z = 1; - - } - - _z.normalize(); - _x.crossVectors( up, _z ); - - if ( _x.lengthSq() === 0 ) { - - // up and z are parallel - - if ( Math.abs( up.z ) === 1 ) { - - _z.x += 0.0001; - - } else { - - _z.z += 0.0001; - - } - - _z.normalize(); - _x.crossVectors( up, _z ); - - } - - _x.normalize(); - _y.crossVectors( _z, _x ); - - te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x; - te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y; - te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z; - - return this; - - } - - multiply( m, n ) { - - if ( n !== undefined ) { - - console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); - return this.multiplyMatrices( m, n ); - - } - - return this.multiplyMatrices( this, m ); - - } - - premultiply( m ) { - - return this.multiplyMatrices( m, this ); - - } - - multiplyMatrices( a, b ) { - - const ae = a.elements; - const be = b.elements; - const te = this.elements; - - const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; - const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; - const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; - const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; - - const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; - const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; - const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; - const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; - - te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; - te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; - te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; - te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; - - te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; - te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; - te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; - te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; - - te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; - te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; - te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; - te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; - - te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; - te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; - te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; - te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; - - return this; - - } - - multiplyScalar( s ) { - - const te = this.elements; - - te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; - te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; - te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; - te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; - - return this; - - } - - determinant() { - - const te = this.elements; - - const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; - const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; - const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; - const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; - - //TODO: make this more efficient - //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) - - return ( - n41 * ( - + n14 * n23 * n32 - - n13 * n24 * n32 - - n14 * n22 * n33 - + n12 * n24 * n33 - + n13 * n22 * n34 - - n12 * n23 * n34 - ) + - n42 * ( - + n11 * n23 * n34 - - n11 * n24 * n33 - + n14 * n21 * n33 - - n13 * n21 * n34 - + n13 * n24 * n31 - - n14 * n23 * n31 - ) + - n43 * ( - + n11 * n24 * n32 - - n11 * n22 * n34 - - n14 * n21 * n32 - + n12 * n21 * n34 - + n14 * n22 * n31 - - n12 * n24 * n31 - ) + - n44 * ( - - n13 * n22 * n31 - - n11 * n23 * n32 - + n11 * n22 * n33 - + n13 * n21 * n32 - - n12 * n21 * n33 - + n12 * n23 * n31 - ) - - ); - - } - - transpose() { - - const te = this.elements; - let tmp; - - tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; - tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; - tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; - - tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; - tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; - tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; - - return this; - - } - - setPosition( x, y, z ) { - - const te = this.elements; - - if ( x.isVector3 ) { - - te[ 12 ] = x.x; - te[ 13 ] = x.y; - te[ 14 ] = x.z; - - } else { - - te[ 12 ] = x; - te[ 13 ] = y; - te[ 14 ] = z; - - } - - return this; - - } - - invert() { - - // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm - const te = this.elements, - - n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ], - n12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ], - n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ], - n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ], - - t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, - t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, - t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, - t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; - - const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; - - if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); - - const detInv = 1 / det; - - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; - te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; - te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; - - te[ 4 ] = t12 * detInv; - te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; - te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; - te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; - - te[ 8 ] = t13 * detInv; - te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; - te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; - te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; - - te[ 12 ] = t14 * detInv; - te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; - te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; - te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; - - return this; - - } - - scale( v ) { - - const te = this.elements; - const x = v.x, y = v.y, z = v.z; - - te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; - te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; - te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; - te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; - - return this; - - } - - getMaxScaleOnAxis() { - - const te = this.elements; - - const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; - const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; - const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; - - return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); - - } - - makeTranslation( x, y, z ) { - - this.set( - - 1, 0, 0, x, - 0, 1, 0, y, - 0, 0, 1, z, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeRotationX( theta ) { - - const c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - 1, 0, 0, 0, - 0, c, - s, 0, - 0, s, c, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeRotationY( theta ) { - - const c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - c, 0, s, 0, - 0, 1, 0, 0, - - s, 0, c, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeRotationZ( theta ) { - - const c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - c, - s, 0, 0, - s, c, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeRotationAxis( axis, angle ) { - - // Based on http://www.gamedev.net/reference/articles/article1199.asp - - const c = Math.cos( angle ); - const s = Math.sin( angle ); - const t = 1 - c; - const x = axis.x, y = axis.y, z = axis.z; - const tx = t * x, ty = t * y; - - this.set( - - tx * x + c, tx * y - s * z, tx * z + s * y, 0, - tx * y + s * z, ty * y + c, ty * z - s * x, 0, - tx * z - s * y, ty * z + s * x, t * z * z + c, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeScale( x, y, z ) { - - this.set( - - x, 0, 0, 0, - 0, y, 0, 0, - 0, 0, z, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeShear( xy, xz, yx, yz, zx, zy ) { - - this.set( - - 1, yx, zx, 0, - xy, 1, zy, 0, - xz, yz, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - compose( position, quaternion, scale ) { - - const te = this.elements; - - const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; - const x2 = x + x, y2 = y + y, z2 = z + z; - const xx = x * x2, xy = x * y2, xz = x * z2; - const yy = y * y2, yz = y * z2, zz = z * z2; - const wx = w * x2, wy = w * y2, wz = w * z2; - - const sx = scale.x, sy = scale.y, sz = scale.z; - - te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; - te[ 1 ] = ( xy + wz ) * sx; - te[ 2 ] = ( xz - wy ) * sx; - te[ 3 ] = 0; - - te[ 4 ] = ( xy - wz ) * sy; - te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; - te[ 6 ] = ( yz + wx ) * sy; - te[ 7 ] = 0; - - te[ 8 ] = ( xz + wy ) * sz; - te[ 9 ] = ( yz - wx ) * sz; - te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; - te[ 11 ] = 0; - - te[ 12 ] = position.x; - te[ 13 ] = position.y; - te[ 14 ] = position.z; - te[ 15 ] = 1; - - return this; - - } - - decompose( position, quaternion, scale ) { - - const te = this.elements; - - let sx = _v1$5.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); - const sy = _v1$5.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); - const sz = _v1$5.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); - - // if determine is negative, we need to invert one scale - const det = this.determinant(); - if ( det < 0 ) sx = - sx; - - position.x = te[ 12 ]; - position.y = te[ 13 ]; - position.z = te[ 14 ]; - - // scale the rotation part - _m1$2.copy( this ); - - const invSX = 1 / sx; - const invSY = 1 / sy; - const invSZ = 1 / sz; - - _m1$2.elements[ 0 ] *= invSX; - _m1$2.elements[ 1 ] *= invSX; - _m1$2.elements[ 2 ] *= invSX; - - _m1$2.elements[ 4 ] *= invSY; - _m1$2.elements[ 5 ] *= invSY; - _m1$2.elements[ 6 ] *= invSY; - - _m1$2.elements[ 8 ] *= invSZ; - _m1$2.elements[ 9 ] *= invSZ; - _m1$2.elements[ 10 ] *= invSZ; - - quaternion.setFromRotationMatrix( _m1$2 ); - - scale.x = sx; - scale.y = sy; - scale.z = sz; - - return this; - - } - - makePerspective( left, right, top, bottom, near, far ) { - - if ( far === undefined ) { - - console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' ); - - } - - const te = this.elements; - const x = 2 * near / ( right - left ); - const y = 2 * near / ( top - bottom ); - - const a = ( right + left ) / ( right - left ); - const b = ( top + bottom ) / ( top - bottom ); - const c = - ( far + near ) / ( far - near ); - const d = - 2 * far * near / ( far - near ); - - te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; - te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; - - return this; - - } - - makeOrthographic( left, right, top, bottom, near, far ) { - - const te = this.elements; - const w = 1.0 / ( right - left ); - const h = 1.0 / ( top - bottom ); - const p = 1.0 / ( far - near ); - - const x = ( right + left ) * w; - const y = ( top + bottom ) * h; - const z = ( far + near ) * p; - - te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; - te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; - - return this; - - } - - equals( matrix ) { - - const te = this.elements; - const me = matrix.elements; - - for ( let i = 0; i < 16; i ++ ) { - - if ( te[ i ] !== me[ i ] ) return false; - - } - - return true; - - } - - fromArray( array, offset = 0 ) { - - for ( let i = 0; i < 16; i ++ ) { - - this.elements[ i ] = array[ i + offset ]; - - } - - return this; - - } - - toArray( array = [], offset = 0 ) { - - const te = this.elements; - - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - array[ offset + 3 ] = te[ 3 ]; - - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; - - array[ offset + 8 ] = te[ 8 ]; - array[ offset + 9 ] = te[ 9 ]; - array[ offset + 10 ] = te[ 10 ]; - array[ offset + 11 ] = te[ 11 ]; - - array[ offset + 12 ] = te[ 12 ]; - array[ offset + 13 ] = te[ 13 ]; - array[ offset + 14 ] = te[ 14 ]; - array[ offset + 15 ] = te[ 15 ]; - - return array; - - } - - } - - Matrix4.prototype.isMatrix4 = true; - - const _v1$5 = /*@__PURE__*/ new Vector3(); - const _m1$2 = /*@__PURE__*/ new Matrix4(); - const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 ); - const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 ); - const _x = /*@__PURE__*/ new Vector3(); - const _y = /*@__PURE__*/ new Vector3(); - const _z = /*@__PURE__*/ new Vector3(); - - const _matrix$1 = /*@__PURE__*/ new Matrix4(); - const _quaternion$3 = /*@__PURE__*/ new Quaternion(); - - class Euler { - - constructor( x = 0, y = 0, z = 0, order = Euler.DefaultOrder ) { - - this._x = x; - this._y = y; - this._z = z; - this._order = order; - - } - - get x() { - - return this._x; - - } - - set x( value ) { - - this._x = value; - this._onChangeCallback(); - - } - - get y() { - - return this._y; - - } - - set y( value ) { - - this._y = value; - this._onChangeCallback(); - - } - - get z() { - - return this._z; - - } - - set z( value ) { - - this._z = value; - this._onChangeCallback(); - - } - - get order() { - - return this._order; - - } - - set order( value ) { - - this._order = value; - this._onChangeCallback(); - - } - - set( x, y, z, order = this._order ) { - - this._x = x; - this._y = y; - this._z = z; - this._order = order; - - this._onChangeCallback(); - - return this; - - } - - clone() { - - return new this.constructor( this._x, this._y, this._z, this._order ); - - } - - copy( euler ) { - - this._x = euler._x; - this._y = euler._y; - this._z = euler._z; - this._order = euler._order; - - this._onChangeCallback(); - - return this; - - } - - setFromRotationMatrix( m, order = this._order, update = true ) { - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - const te = m.elements; - const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; - const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; - const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - - switch ( order ) { - - case 'XYZ': - - this._y = Math.asin( clamp( m13, - 1, 1 ) ); - - if ( Math.abs( m13 ) < 0.9999999 ) { - - this._x = Math.atan2( - m23, m33 ); - this._z = Math.atan2( - m12, m11 ); - - } else { - - this._x = Math.atan2( m32, m22 ); - this._z = 0; - - } - - break; - - case 'YXZ': - - this._x = Math.asin( - clamp( m23, - 1, 1 ) ); - - if ( Math.abs( m23 ) < 0.9999999 ) { - - this._y = Math.atan2( m13, m33 ); - this._z = Math.atan2( m21, m22 ); - - } else { - - this._y = Math.atan2( - m31, m11 ); - this._z = 0; - - } - - break; - - case 'ZXY': - - this._x = Math.asin( clamp( m32, - 1, 1 ) ); - - if ( Math.abs( m32 ) < 0.9999999 ) { - - this._y = Math.atan2( - m31, m33 ); - this._z = Math.atan2( - m12, m22 ); - - } else { - - this._y = 0; - this._z = Math.atan2( m21, m11 ); - - } - - break; - - case 'ZYX': - - this._y = Math.asin( - clamp( m31, - 1, 1 ) ); - - if ( Math.abs( m31 ) < 0.9999999 ) { - - this._x = Math.atan2( m32, m33 ); - this._z = Math.atan2( m21, m11 ); - - } else { - - this._x = 0; - this._z = Math.atan2( - m12, m22 ); - - } - - break; - - case 'YZX': - - this._z = Math.asin( clamp( m21, - 1, 1 ) ); - - if ( Math.abs( m21 ) < 0.9999999 ) { - - this._x = Math.atan2( - m23, m22 ); - this._y = Math.atan2( - m31, m11 ); - - } else { - - this._x = 0; - this._y = Math.atan2( m13, m33 ); - - } - - break; - - case 'XZY': - - this._z = Math.asin( - clamp( m12, - 1, 1 ) ); - - if ( Math.abs( m12 ) < 0.9999999 ) { - - this._x = Math.atan2( m32, m22 ); - this._y = Math.atan2( m13, m11 ); - - } else { - - this._x = Math.atan2( - m23, m33 ); - this._y = 0; - - } - - break; - - default: - - console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order ); - - } - - this._order = order; - - if ( update === true ) this._onChangeCallback(); - - return this; - - } - - setFromQuaternion( q, order, update ) { - - _matrix$1.makeRotationFromQuaternion( q ); - - return this.setFromRotationMatrix( _matrix$1, order, update ); - - } - - setFromVector3( v, order = this._order ) { - - return this.set( v.x, v.y, v.z, order ); - - } - - reorder( newOrder ) { - - // WARNING: this discards revolution information -bhouston - - _quaternion$3.setFromEuler( this ); - - return this.setFromQuaternion( _quaternion$3, newOrder ); - - } - - equals( euler ) { - - return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); - - } - - fromArray( array ) { - - this._x = array[ 0 ]; - this._y = array[ 1 ]; - this._z = array[ 2 ]; - if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; - - this._onChangeCallback(); - - return this; - - } - - toArray( array = [], offset = 0 ) { - - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._order; - - return array; - - } - - toVector3( optionalResult ) { - - if ( optionalResult ) { - - return optionalResult.set( this._x, this._y, this._z ); - - } else { - - return new Vector3( this._x, this._y, this._z ); - - } - - } - - _onChange( callback ) { - - this._onChangeCallback = callback; - - return this; - - } - - _onChangeCallback() {} - - } - - Euler.prototype.isEuler = true; - - Euler.DefaultOrder = 'XYZ'; - Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; - - class Layers { - - constructor() { - - this.mask = 1 | 0; - - } - - set( channel ) { - - this.mask = 1 << channel | 0; - - } - - enable( channel ) { - - this.mask |= 1 << channel | 0; - - } - - enableAll() { - - this.mask = 0xffffffff | 0; - - } - - toggle( channel ) { - - this.mask ^= 1 << channel | 0; - - } - - disable( channel ) { - - this.mask &= ~ ( 1 << channel | 0 ); - - } - - disableAll() { - - this.mask = 0; - - } - - test( layers ) { - - return ( this.mask & layers.mask ) !== 0; - - } - - } - - let _object3DId = 0; - - const _v1$4 = /*@__PURE__*/ new Vector3(); - const _q1 = /*@__PURE__*/ new Quaternion(); - const _m1$1 = /*@__PURE__*/ new Matrix4(); - const _target = /*@__PURE__*/ new Vector3(); - - const _position$3 = /*@__PURE__*/ new Vector3(); - const _scale$2 = /*@__PURE__*/ new Vector3(); - const _quaternion$2 = /*@__PURE__*/ new Quaternion(); - - const _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 ); - const _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 ); - const _zAxis = /*@__PURE__*/ new Vector3( 0, 0, 1 ); - - const _addedEvent = { type: 'added' }; - const _removedEvent = { type: 'removed' }; - - class Object3D extends EventDispatcher { - - constructor() { - - super(); - - Object.defineProperty( this, 'id', { value: _object3DId ++ } ); - - this.uuid = generateUUID(); - - this.name = ''; - this.type = 'Object3D'; - - this.parent = null; - this.children = []; - - this.up = Object3D.DefaultUp.clone(); - - const position = new Vector3(); - const rotation = new Euler(); - const quaternion = new Quaternion(); - const scale = new Vector3( 1, 1, 1 ); - - function onRotationChange() { - - quaternion.setFromEuler( rotation, false ); - - } - - function onQuaternionChange() { - - rotation.setFromQuaternion( quaternion, undefined, false ); - - } - - rotation._onChange( onRotationChange ); - quaternion._onChange( onQuaternionChange ); - - Object.defineProperties( this, { - position: { - configurable: true, - enumerable: true, - value: position - }, - rotation: { - configurable: true, - enumerable: true, - value: rotation - }, - quaternion: { - configurable: true, - enumerable: true, - value: quaternion - }, - scale: { - configurable: true, - enumerable: true, - value: scale - }, - modelViewMatrix: { - value: new Matrix4() - }, - normalMatrix: { - value: new Matrix3() - } - } ); - - this.matrix = new Matrix4(); - this.matrixWorld = new Matrix4(); - - this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; - this.matrixWorldNeedsUpdate = false; - - this.layers = new Layers(); - this.visible = true; - - this.castShadow = false; - this.receiveShadow = false; - - this.frustumCulled = true; - this.renderOrder = 0; - - this.animations = []; - - this.userData = {}; - - } - - onBeforeRender() {} - onAfterRender() {} - - applyMatrix4( matrix ) { - - if ( this.matrixAutoUpdate ) this.updateMatrix(); - - this.matrix.premultiply( matrix ); - - this.matrix.decompose( this.position, this.quaternion, this.scale ); - - } - - applyQuaternion( q ) { - - this.quaternion.premultiply( q ); - - return this; - - } - - setRotationFromAxisAngle( axis, angle ) { - - // assumes axis is normalized - - this.quaternion.setFromAxisAngle( axis, angle ); - - } - - setRotationFromEuler( euler ) { - - this.quaternion.setFromEuler( euler, true ); - - } - - setRotationFromMatrix( m ) { - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - this.quaternion.setFromRotationMatrix( m ); - - } - - setRotationFromQuaternion( q ) { - - // assumes q is normalized - - this.quaternion.copy( q ); - - } - - rotateOnAxis( axis, angle ) { - - // rotate object on axis in object space - // axis is assumed to be normalized - - _q1.setFromAxisAngle( axis, angle ); - - this.quaternion.multiply( _q1 ); - - return this; - - } - - rotateOnWorldAxis( axis, angle ) { - - // rotate object on axis in world space - // axis is assumed to be normalized - // method assumes no rotated parent - - _q1.setFromAxisAngle( axis, angle ); - - this.quaternion.premultiply( _q1 ); - - return this; - - } - - rotateX( angle ) { - - return this.rotateOnAxis( _xAxis, angle ); - - } - - rotateY( angle ) { - - return this.rotateOnAxis( _yAxis, angle ); - - } - - rotateZ( angle ) { - - return this.rotateOnAxis( _zAxis, angle ); - - } - - translateOnAxis( axis, distance ) { - - // translate object by distance along axis in object space - // axis is assumed to be normalized - - _v1$4.copy( axis ).applyQuaternion( this.quaternion ); - - this.position.add( _v1$4.multiplyScalar( distance ) ); - - return this; - - } - - translateX( distance ) { - - return this.translateOnAxis( _xAxis, distance ); - - } - - translateY( distance ) { - - return this.translateOnAxis( _yAxis, distance ); - - } - - translateZ( distance ) { - - return this.translateOnAxis( _zAxis, distance ); - - } - - localToWorld( vector ) { - - return vector.applyMatrix4( this.matrixWorld ); - - } - - worldToLocal( vector ) { - - return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() ); - - } - - lookAt( x, y, z ) { - - // This method does not support objects having non-uniformly-scaled parent(s) - - if ( x.isVector3 ) { - - _target.copy( x ); - - } else { - - _target.set( x, y, z ); - - } - - const parent = this.parent; - - this.updateWorldMatrix( true, false ); - - _position$3.setFromMatrixPosition( this.matrixWorld ); - - if ( this.isCamera || this.isLight ) { - - _m1$1.lookAt( _position$3, _target, this.up ); - - } else { - - _m1$1.lookAt( _target, _position$3, this.up ); - - } - - this.quaternion.setFromRotationMatrix( _m1$1 ); - - if ( parent ) { - - _m1$1.extractRotation( parent.matrixWorld ); - _q1.setFromRotationMatrix( _m1$1 ); - this.quaternion.premultiply( _q1.invert() ); - - } - - } - - add( object ) { - - if ( arguments.length > 1 ) { - - for ( let i = 0; i < arguments.length; i ++ ) { - - this.add( arguments[ i ] ); - - } - - return this; - - } - - if ( object === this ) { - - console.error( 'THREE.Object3D.add: object can\'t be added as a child of itself.', object ); - return this; - - } - - if ( object && object.isObject3D ) { - - if ( object.parent !== null ) { - - object.parent.remove( object ); - - } - - object.parent = this; - this.children.push( object ); - - object.dispatchEvent( _addedEvent ); - - } else { - - console.error( 'THREE.Object3D.add: object not an instance of THREE.Object3D.', object ); - - } - - return this; - - } - - remove( object ) { - - if ( arguments.length > 1 ) { - - for ( let i = 0; i < arguments.length; i ++ ) { - - this.remove( arguments[ i ] ); - - } - - return this; - - } - - const index = this.children.indexOf( object ); - - if ( index !== - 1 ) { - - object.parent = null; - this.children.splice( index, 1 ); - - object.dispatchEvent( _removedEvent ); - - } - - return this; - - } - - removeFromParent() { - - const parent = this.parent; - - if ( parent !== null ) { - - parent.remove( this ); - - } - - return this; - - } - - clear() { - - for ( let i = 0; i < this.children.length; i ++ ) { - - const object = this.children[ i ]; - - object.parent = null; - - object.dispatchEvent( _removedEvent ); - - } - - this.children.length = 0; - - return this; - - - } - - attach( object ) { - - // adds object as a child of this, while maintaining the object's world transform - - this.updateWorldMatrix( true, false ); - - _m1$1.copy( this.matrixWorld ).invert(); - - if ( object.parent !== null ) { - - object.parent.updateWorldMatrix( true, false ); - - _m1$1.multiply( object.parent.matrixWorld ); - - } - - object.applyMatrix4( _m1$1 ); - - this.add( object ); - - object.updateWorldMatrix( false, true ); - - return this; - - } - - getObjectById( id ) { - - return this.getObjectByProperty( 'id', id ); - - } - - getObjectByName( name ) { - - return this.getObjectByProperty( 'name', name ); - - } - - getObjectByProperty( name, value ) { - - if ( this[ name ] === value ) return this; - - for ( let i = 0, l = this.children.length; i < l; i ++ ) { - - const child = this.children[ i ]; - const object = child.getObjectByProperty( name, value ); - - if ( object !== undefined ) { - - return object; - - } - - } - - return undefined; - - } - - getWorldPosition( target ) { - - this.updateWorldMatrix( true, false ); - - return target.setFromMatrixPosition( this.matrixWorld ); - - } - - getWorldQuaternion( target ) { - - this.updateWorldMatrix( true, false ); - - this.matrixWorld.decompose( _position$3, target, _scale$2 ); - - return target; - - } - - getWorldScale( target ) { - - this.updateWorldMatrix( true, false ); - - this.matrixWorld.decompose( _position$3, _quaternion$2, target ); - - return target; - - } - - getWorldDirection( target ) { - - this.updateWorldMatrix( true, false ); - - const e = this.matrixWorld.elements; - - return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); - - } - - raycast() {} - - traverse( callback ) { - - callback( this ); - - const children = this.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].traverse( callback ); - - } - - } - - traverseVisible( callback ) { - - if ( this.visible === false ) return; - - callback( this ); - - const children = this.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].traverseVisible( callback ); - - } - - } - - traverseAncestors( callback ) { - - const parent = this.parent; - - if ( parent !== null ) { - - callback( parent ); - - parent.traverseAncestors( callback ); - - } - - } - - updateMatrix() { - - this.matrix.compose( this.position, this.quaternion, this.scale ); - - this.matrixWorldNeedsUpdate = true; - - } - - updateMatrixWorld( force ) { - - if ( this.matrixAutoUpdate ) this.updateMatrix(); - - if ( this.matrixWorldNeedsUpdate || force ) { - - if ( this.parent === null ) { - - this.matrixWorld.copy( this.matrix ); - - } else { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - - } - - this.matrixWorldNeedsUpdate = false; - - force = true; - - } - - // update children - - const children = this.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateMatrixWorld( force ); - - } - - } - - updateWorldMatrix( updateParents, updateChildren ) { - - const parent = this.parent; - - if ( updateParents === true && parent !== null ) { - - parent.updateWorldMatrix( true, false ); - - } - - if ( this.matrixAutoUpdate ) this.updateMatrix(); - - if ( this.parent === null ) { - - this.matrixWorld.copy( this.matrix ); - - } else { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - - } - - // update children - - if ( updateChildren === true ) { - - const children = this.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateWorldMatrix( false, true ); - - } - - } - - } - - toJSON( meta ) { - - // meta is a string when called from JSON.stringify - const isRootObject = ( meta === undefined || typeof meta === 'string' ); - - const output = {}; - - // meta is a hash used to collect geometries, materials. - // not providing it implies that this is the root object - // being serialized. - if ( isRootObject ) { - - // initialize meta obj - meta = { - geometries: {}, - materials: {}, - textures: {}, - images: {}, - shapes: {}, - skeletons: {}, - animations: {} - }; - - output.metadata = { - version: 4.5, - type: 'Object', - generator: 'Object3D.toJSON' - }; - - } - - // standard Object3D serialization - - const object = {}; - - object.uuid = this.uuid; - object.type = this.type; - - if ( this.name !== '' ) object.name = this.name; - if ( this.castShadow === true ) object.castShadow = true; - if ( this.receiveShadow === true ) object.receiveShadow = true; - if ( this.visible === false ) object.visible = false; - if ( this.frustumCulled === false ) object.frustumCulled = false; - if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; - if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; - - object.layers = this.layers.mask; - object.matrix = this.matrix.toArray(); - - if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; - - // object specific properties - - if ( this.isInstancedMesh ) { - - object.type = 'InstancedMesh'; - object.count = this.count; - object.instanceMatrix = this.instanceMatrix.toJSON(); - if ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON(); - - } - - // - - function serialize( library, element ) { - - if ( library[ element.uuid ] === undefined ) { - - library[ element.uuid ] = element.toJSON( meta ); - - } - - return element.uuid; - - } - - if ( this.isScene ) { - - if ( this.background ) { - - if ( this.background.isColor ) { - - object.background = this.background.toJSON(); - - } else if ( this.background.isTexture ) { - - object.background = this.background.toJSON( meta ).uuid; - - } - - } - - if ( this.environment && this.environment.isTexture ) { - - object.environment = this.environment.toJSON( meta ).uuid; - - } - - } else if ( this.isMesh || this.isLine || this.isPoints ) { - - object.geometry = serialize( meta.geometries, this.geometry ); - - const parameters = this.geometry.parameters; - - if ( parameters !== undefined && parameters.shapes !== undefined ) { - - const shapes = parameters.shapes; - - if ( Array.isArray( shapes ) ) { - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - - serialize( meta.shapes, shape ); - - } - - } else { - - serialize( meta.shapes, shapes ); - - } - - } - - } - - if ( this.isSkinnedMesh ) { - - object.bindMode = this.bindMode; - object.bindMatrix = this.bindMatrix.toArray(); - - if ( this.skeleton !== undefined ) { - - serialize( meta.skeletons, this.skeleton ); - - object.skeleton = this.skeleton.uuid; - - } - - } - - if ( this.material !== undefined ) { - - if ( Array.isArray( this.material ) ) { - - const uuids = []; - - for ( let i = 0, l = this.material.length; i < l; i ++ ) { - - uuids.push( serialize( meta.materials, this.material[ i ] ) ); - - } - - object.material = uuids; - - } else { - - object.material = serialize( meta.materials, this.material ); - - } - - } - - // - - if ( this.children.length > 0 ) { - - object.children = []; - - for ( let i = 0; i < this.children.length; i ++ ) { - - object.children.push( this.children[ i ].toJSON( meta ).object ); - - } - - } - - // - - if ( this.animations.length > 0 ) { - - object.animations = []; - - for ( let i = 0; i < this.animations.length; i ++ ) { - - const animation = this.animations[ i ]; - - object.animations.push( serialize( meta.animations, animation ) ); - - } - - } - - if ( isRootObject ) { - - const geometries = extractFromCache( meta.geometries ); - const materials = extractFromCache( meta.materials ); - const textures = extractFromCache( meta.textures ); - const images = extractFromCache( meta.images ); - const shapes = extractFromCache( meta.shapes ); - const skeletons = extractFromCache( meta.skeletons ); - const animations = extractFromCache( meta.animations ); - - if ( geometries.length > 0 ) output.geometries = geometries; - if ( materials.length > 0 ) output.materials = materials; - if ( textures.length > 0 ) output.textures = textures; - if ( images.length > 0 ) output.images = images; - if ( shapes.length > 0 ) output.shapes = shapes; - if ( skeletons.length > 0 ) output.skeletons = skeletons; - if ( animations.length > 0 ) output.animations = animations; - - } - - output.object = object; - - return output; - - // extract data from the cache hash - // remove metadata on each item - // and return as array - function extractFromCache( cache ) { - - const values = []; - for ( const key in cache ) { - - const data = cache[ key ]; - delete data.metadata; - values.push( data ); - - } - - return values; - - } - - } - - clone( recursive ) { - - return new this.constructor().copy( this, recursive ); - - } - - copy( source, recursive = true ) { - - this.name = source.name; - - this.up.copy( source.up ); - - this.position.copy( source.position ); - this.rotation.order = source.rotation.order; - this.quaternion.copy( source.quaternion ); - this.scale.copy( source.scale ); - - this.matrix.copy( source.matrix ); - this.matrixWorld.copy( source.matrixWorld ); - - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; - - this.layers.mask = source.layers.mask; - this.visible = source.visible; - - this.castShadow = source.castShadow; - this.receiveShadow = source.receiveShadow; - - this.frustumCulled = source.frustumCulled; - this.renderOrder = source.renderOrder; - - this.userData = JSON.parse( JSON.stringify( source.userData ) ); - - if ( recursive === true ) { - - for ( let i = 0; i < source.children.length; i ++ ) { - - const child = source.children[ i ]; - this.add( child.clone() ); - - } - - } - - return this; - - } - - } - - Object3D.DefaultUp = new Vector3( 0, 1, 0 ); - Object3D.DefaultMatrixAutoUpdate = true; - - Object3D.prototype.isObject3D = true; - - const _v0$1 = /*@__PURE__*/ new Vector3(); - const _v1$3 = /*@__PURE__*/ new Vector3(); - const _v2$2 = /*@__PURE__*/ new Vector3(); - const _v3$1 = /*@__PURE__*/ new Vector3(); - - const _vab = /*@__PURE__*/ new Vector3(); - const _vac = /*@__PURE__*/ new Vector3(); - const _vbc = /*@__PURE__*/ new Vector3(); - const _vap = /*@__PURE__*/ new Vector3(); - const _vbp = /*@__PURE__*/ new Vector3(); - const _vcp = /*@__PURE__*/ new Vector3(); - - class Triangle { - - constructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) { - - this.a = a; - this.b = b; - this.c = c; - - } - - static getNormal( a, b, c, target ) { - - target.subVectors( c, b ); - _v0$1.subVectors( a, b ); - target.cross( _v0$1 ); - - const targetLengthSq = target.lengthSq(); - if ( targetLengthSq > 0 ) { - - return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); - - } - - return target.set( 0, 0, 0 ); - - } - - // static/instance method to calculate barycentric coordinates - // based on: http://www.blackpawn.com/texts/pointinpoly/default.html - static getBarycoord( point, a, b, c, target ) { - - _v0$1.subVectors( c, a ); - _v1$3.subVectors( b, a ); - _v2$2.subVectors( point, a ); - - const dot00 = _v0$1.dot( _v0$1 ); - const dot01 = _v0$1.dot( _v1$3 ); - const dot02 = _v0$1.dot( _v2$2 ); - const dot11 = _v1$3.dot( _v1$3 ); - const dot12 = _v1$3.dot( _v2$2 ); - - const denom = ( dot00 * dot11 - dot01 * dot01 ); - - // collinear or singular triangle - if ( denom === 0 ) { - - // arbitrary location outside of triangle? - // not sure if this is the best idea, maybe should be returning undefined - return target.set( - 2, - 1, - 1 ); - - } - - const invDenom = 1 / denom; - const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; - const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; - - // barycentric coordinates must always sum to 1 - return target.set( 1 - u - v, v, u ); - - } - - static containsPoint( point, a, b, c ) { - - this.getBarycoord( point, a, b, c, _v3$1 ); - - return ( _v3$1.x >= 0 ) && ( _v3$1.y >= 0 ) && ( ( _v3$1.x + _v3$1.y ) <= 1 ); - - } - - static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { - - this.getBarycoord( point, p1, p2, p3, _v3$1 ); - - target.set( 0, 0 ); - target.addScaledVector( uv1, _v3$1.x ); - target.addScaledVector( uv2, _v3$1.y ); - target.addScaledVector( uv3, _v3$1.z ); - - return target; - - } - - static isFrontFacing( a, b, c, direction ) { - - _v0$1.subVectors( c, b ); - _v1$3.subVectors( a, b ); - - // strictly front facing - return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; - - } - - set( a, b, c ) { - - this.a.copy( a ); - this.b.copy( b ); - this.c.copy( c ); - - return this; - - } - - setFromPointsAndIndices( points, i0, i1, i2 ) { - - this.a.copy( points[ i0 ] ); - this.b.copy( points[ i1 ] ); - this.c.copy( points[ i2 ] ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( triangle ) { - - this.a.copy( triangle.a ); - this.b.copy( triangle.b ); - this.c.copy( triangle.c ); - - return this; - - } - - getArea() { - - _v0$1.subVectors( this.c, this.b ); - _v1$3.subVectors( this.a, this.b ); - - return _v0$1.cross( _v1$3 ).length() * 0.5; - - } - - getMidpoint( target ) { - - return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); - - } - - getNormal( target ) { - - return Triangle.getNormal( this.a, this.b, this.c, target ); - - } - - getPlane( target ) { - - return target.setFromCoplanarPoints( this.a, this.b, this.c ); - - } - - getBarycoord( point, target ) { - - return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); - - } - - getUV( point, uv1, uv2, uv3, target ) { - - return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); - - } - - containsPoint( point ) { - - return Triangle.containsPoint( point, this.a, this.b, this.c ); - - } - - isFrontFacing( direction ) { - - return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); - - } - - intersectsBox( box ) { - - return box.intersectsTriangle( this ); - - } - - closestPointToPoint( p, target ) { - - const a = this.a, b = this.b, c = this.c; - let v, w; - - // algorithm thanks to Real-Time Collision Detection by Christer Ericson, - // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., - // under the accompanying license; see chapter 5.1.5 for detailed explanation. - // basically, we're distinguishing which of the voronoi regions of the triangle - // the point lies in with the minimum amount of redundant computation. - - _vab.subVectors( b, a ); - _vac.subVectors( c, a ); - _vap.subVectors( p, a ); - const d1 = _vab.dot( _vap ); - const d2 = _vac.dot( _vap ); - if ( d1 <= 0 && d2 <= 0 ) { - - // vertex region of A; barycentric coords (1, 0, 0) - return target.copy( a ); - - } - - _vbp.subVectors( p, b ); - const d3 = _vab.dot( _vbp ); - const d4 = _vac.dot( _vbp ); - if ( d3 >= 0 && d4 <= d3 ) { - - // vertex region of B; barycentric coords (0, 1, 0) - return target.copy( b ); - - } - - const vc = d1 * d4 - d3 * d2; - if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { - - v = d1 / ( d1 - d3 ); - // edge region of AB; barycentric coords (1-v, v, 0) - return target.copy( a ).addScaledVector( _vab, v ); - - } - - _vcp.subVectors( p, c ); - const d5 = _vab.dot( _vcp ); - const d6 = _vac.dot( _vcp ); - if ( d6 >= 0 && d5 <= d6 ) { - - // vertex region of C; barycentric coords (0, 0, 1) - return target.copy( c ); - - } - - const vb = d5 * d2 - d1 * d6; - if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { - - w = d2 / ( d2 - d6 ); - // edge region of AC; barycentric coords (1-w, 0, w) - return target.copy( a ).addScaledVector( _vac, w ); - - } - - const va = d3 * d6 - d5 * d4; - if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { - - _vbc.subVectors( c, b ); - w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); - // edge region of BC; barycentric coords (0, 1-w, w) - return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC - - } - - // face region - const denom = 1 / ( va + vb + vc ); - // u = va * denom - v = vb * denom; - w = vc * denom; - - return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); - - } - - equals( triangle ) { - - return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); - - } - - } - - let materialId = 0; - - class Material extends EventDispatcher { - - #alphaTest = 0; - - constructor() { - - super(); - - Object.defineProperty( this, 'id', { value: materialId ++ } ); - - this.uuid = generateUUID(); - - this.name = ''; - this.type = 'Material'; - - this.fog = true; - - this.blending = NormalBlending; - this.side = FrontSide; - this.vertexColors = false; - - this.opacity = 1; - this.format = RGBAFormat; - this.transparent = false; - - this.blendSrc = SrcAlphaFactor; - this.blendDst = OneMinusSrcAlphaFactor; - this.blendEquation = AddEquation; - this.blendSrcAlpha = null; - this.blendDstAlpha = null; - this.blendEquationAlpha = null; - - this.depthFunc = LessEqualDepth; - this.depthTest = true; - this.depthWrite = true; - - this.stencilWriteMask = 0xff; - this.stencilFunc = AlwaysStencilFunc; - this.stencilRef = 0; - this.stencilFuncMask = 0xff; - this.stencilFail = KeepStencilOp; - this.stencilZFail = KeepStencilOp; - this.stencilZPass = KeepStencilOp; - this.stencilWrite = false; - - this.clippingPlanes = null; - this.clipIntersection = false; - this.clipShadows = false; - - this.shadowSide = null; - - this.colorWrite = true; - - this.precision = null; // override the renderer's default precision for this material - - this.polygonOffset = false; - this.polygonOffsetFactor = 0; - this.polygonOffsetUnits = 0; - - this.dithering = false; - - this.alphaToCoverage = false; - this.premultipliedAlpha = false; - - this.visible = true; - - this.toneMapped = true; - - this.userData = {}; - - this.version = 0; - - } - - get alphaTest() { - - return this.#alphaTest; - - } - - set alphaTest( value ) { - - if ( this.#alphaTest > 0 !== value > 0 ) { - - this.version ++; - - } - - this.#alphaTest = value; - - } - - onBuild( /* shaderobject, renderer */ ) {} - - onBeforeCompile( /* shaderobject, renderer */ ) {} - - customProgramCacheKey() { - - return this.onBeforeCompile.toString(); - - } - - setValues( values ) { - - if ( values === undefined ) return; - - for ( const key in values ) { - - const newValue = values[ key ]; - - if ( newValue === undefined ) { - - console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' ); - continue; - - } - - // for backward compatability if shading is set in the constructor - if ( key === 'shading' ) { - - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( newValue === FlatShading ) ? true : false; - continue; - - } - - const currentValue = this[ key ]; - - if ( currentValue === undefined ) { - - console.warn( 'THREE.' + this.type + ': \'' + key + '\' is not a property of this material.' ); - continue; - - } - - if ( currentValue && currentValue.isColor ) { - - currentValue.set( newValue ); - - } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { - - currentValue.copy( newValue ); - - } else { - - this[ key ] = newValue; - - } - - } - - } - - toJSON( meta ) { - - const isRoot = ( meta === undefined || typeof meta === 'string' ); - - if ( isRoot ) { - - meta = { - textures: {}, - images: {} - }; - - } - - const data = { - metadata: { - version: 4.5, - type: 'Material', - generator: 'Material.toJSON' - } - }; - - // standard Material serialization - data.uuid = this.uuid; - data.type = this.type; - - if ( this.name !== '' ) data.name = this.name; - - if ( this.color && this.color.isColor ) data.color = this.color.getHex(); - - if ( this.roughness !== undefined ) data.roughness = this.roughness; - if ( this.metalness !== undefined ) data.metalness = this.metalness; - - if ( this.sheenTint && this.sheenTint.isColor ) data.sheenTint = this.sheenTint.getHex(); - if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); - if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; - - if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); - if ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity; - if ( this.specularTint && this.specularTint.isColor ) data.specularTint = this.specularTint.getHex(); - if ( this.shininess !== undefined ) data.shininess = this.shininess; - if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat; - if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness; - - if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { - - data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; - - } - - if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { - - data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; - - } - - if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { - - data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; - data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); - - } - - if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; - if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; - if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; - - if ( this.lightMap && this.lightMap.isTexture ) { - - data.lightMap = this.lightMap.toJSON( meta ).uuid; - data.lightMapIntensity = this.lightMapIntensity; - - } - - if ( this.aoMap && this.aoMap.isTexture ) { - - data.aoMap = this.aoMap.toJSON( meta ).uuid; - data.aoMapIntensity = this.aoMapIntensity; - - } - - if ( this.bumpMap && this.bumpMap.isTexture ) { - - data.bumpMap = this.bumpMap.toJSON( meta ).uuid; - data.bumpScale = this.bumpScale; - - } - - if ( this.normalMap && this.normalMap.isTexture ) { - - data.normalMap = this.normalMap.toJSON( meta ).uuid; - data.normalMapType = this.normalMapType; - data.normalScale = this.normalScale.toArray(); - - } - - if ( this.displacementMap && this.displacementMap.isTexture ) { - - data.displacementMap = this.displacementMap.toJSON( meta ).uuid; - data.displacementScale = this.displacementScale; - data.displacementBias = this.displacementBias; - - } - - if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; - if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; - - if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; - if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; - if ( this.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid; - if ( this.specularTintMap && this.specularTintMap.isTexture ) data.specularTintMap = this.specularTintMap.toJSON( meta ).uuid; - - if ( this.envMap && this.envMap.isTexture ) { - - data.envMap = this.envMap.toJSON( meta ).uuid; - - if ( this.combine !== undefined ) data.combine = this.combine; - - } - - if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; - if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity; - if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio; - - if ( this.gradientMap && this.gradientMap.isTexture ) { - - data.gradientMap = this.gradientMap.toJSON( meta ).uuid; - - } - - if ( this.transmission !== undefined ) data.transmission = this.transmission; - if ( this.transmissionMap && this.transmissionMap.isTexture ) data.transmissionMap = this.transmissionMap.toJSON( meta ).uuid; - if ( this.thickness !== undefined ) data.thickness = this.thickness; - if ( this.thicknessMap && this.thicknessMap.isTexture ) data.thicknessMap = this.thicknessMap.toJSON( meta ).uuid; - if ( this.attenuationDistance !== undefined ) data.attenuationDistance = this.attenuationDistance; - if ( this.attenuationTint !== undefined ) data.attenuationTint = this.attenuationTint.getHex(); - - if ( this.size !== undefined ) data.size = this.size; - if ( this.shadowSide !== null ) data.shadowSide = this.shadowSide; - if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; - - if ( this.blending !== NormalBlending ) data.blending = this.blending; - if ( this.side !== FrontSide ) data.side = this.side; - if ( this.vertexColors ) data.vertexColors = true; - - if ( this.opacity < 1 ) data.opacity = this.opacity; - if ( this.format !== RGBAFormat ) data.format = this.format; - if ( this.transparent === true ) data.transparent = this.transparent; - - data.depthFunc = this.depthFunc; - data.depthTest = this.depthTest; - data.depthWrite = this.depthWrite; - data.colorWrite = this.colorWrite; - - data.stencilWrite = this.stencilWrite; - data.stencilWriteMask = this.stencilWriteMask; - data.stencilFunc = this.stencilFunc; - data.stencilRef = this.stencilRef; - data.stencilFuncMask = this.stencilFuncMask; - data.stencilFail = this.stencilFail; - data.stencilZFail = this.stencilZFail; - data.stencilZPass = this.stencilZPass; - - // rotation (SpriteMaterial) - if ( this.rotation && this.rotation !== 0 ) data.rotation = this.rotation; - - if ( this.polygonOffset === true ) data.polygonOffset = true; - if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; - if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; - - if ( this.linewidth && this.linewidth !== 1 ) data.linewidth = this.linewidth; - if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; - if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; - if ( this.scale !== undefined ) data.scale = this.scale; - - if ( this.dithering === true ) data.dithering = true; - - if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; - if ( this.alphaToCoverage === true ) data.alphaToCoverage = this.alphaToCoverage; - if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; - - if ( this.wireframe === true ) data.wireframe = this.wireframe; - if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; - if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap; - if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin; - - if ( this.flatShading === true ) data.flatShading = this.flatShading; - - if ( this.visible === false ) data.visible = false; - - if ( this.toneMapped === false ) data.toneMapped = false; - - if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData; - - // TODO: Copied from Object3D.toJSON - - function extractFromCache( cache ) { - - const values = []; - - for ( const key in cache ) { - - const data = cache[ key ]; - delete data.metadata; - values.push( data ); - - } - - return values; - - } - - if ( isRoot ) { - - const textures = extractFromCache( meta.textures ); - const images = extractFromCache( meta.images ); - - if ( textures.length > 0 ) data.textures = textures; - if ( images.length > 0 ) data.images = images; - - } - - return data; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( source ) { - - this.name = source.name; - - this.fog = source.fog; - - this.blending = source.blending; - this.side = source.side; - this.vertexColors = source.vertexColors; - - this.opacity = source.opacity; - this.format = source.format; - this.transparent = source.transparent; - - this.blendSrc = source.blendSrc; - this.blendDst = source.blendDst; - this.blendEquation = source.blendEquation; - this.blendSrcAlpha = source.blendSrcAlpha; - this.blendDstAlpha = source.blendDstAlpha; - this.blendEquationAlpha = source.blendEquationAlpha; - - this.depthFunc = source.depthFunc; - this.depthTest = source.depthTest; - this.depthWrite = source.depthWrite; - - this.stencilWriteMask = source.stencilWriteMask; - this.stencilFunc = source.stencilFunc; - this.stencilRef = source.stencilRef; - this.stencilFuncMask = source.stencilFuncMask; - this.stencilFail = source.stencilFail; - this.stencilZFail = source.stencilZFail; - this.stencilZPass = source.stencilZPass; - this.stencilWrite = source.stencilWrite; - - const srcPlanes = source.clippingPlanes; - let dstPlanes = null; - - if ( srcPlanes !== null ) { - - const n = srcPlanes.length; - dstPlanes = new Array( n ); - - for ( let i = 0; i !== n; ++ i ) { - - dstPlanes[ i ] = srcPlanes[ i ].clone(); - - } - - } - - this.clippingPlanes = dstPlanes; - this.clipIntersection = source.clipIntersection; - this.clipShadows = source.clipShadows; - - this.shadowSide = source.shadowSide; - - this.colorWrite = source.colorWrite; - - this.precision = source.precision; - - this.polygonOffset = source.polygonOffset; - this.polygonOffsetFactor = source.polygonOffsetFactor; - this.polygonOffsetUnits = source.polygonOffsetUnits; - - this.dithering = source.dithering; - - this.alphaTest = source.alphaTest; - this.alphaToCoverage = source.alphaToCoverage; - this.premultipliedAlpha = source.premultipliedAlpha; - - this.visible = source.visible; - - this.toneMapped = source.toneMapped; - - this.userData = JSON.parse( JSON.stringify( source.userData ) ); - - return this; - - } - - dispose() { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - set needsUpdate( value ) { - - if ( value === true ) this.version ++; - - } - - } - - Material.prototype.isMaterial = true; - - const _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, - 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, - 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, - 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, - 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, - 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, - 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, - 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, - 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, - 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, - 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, - 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, - 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, - 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, - 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, - 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, - 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, - 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, - 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, - 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, - 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, - 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, - 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, - 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; - - const _hslA = { h: 0, s: 0, l: 0 }; - const _hslB = { h: 0, s: 0, l: 0 }; - - function hue2rgb( p, q, t ) { - - if ( t < 0 ) t += 1; - if ( t > 1 ) t -= 1; - if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; - if ( t < 1 / 2 ) return q; - if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); - return p; - - } - - function SRGBToLinear( c ) { - - return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); - - } - - function LinearToSRGB( c ) { - - return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; - - } - - class Color { - - constructor( r, g, b ) { - - if ( g === undefined && b === undefined ) { - - // r is THREE.Color, hex or string - return this.set( r ); - - } - - return this.setRGB( r, g, b ); - - } - - set( value ) { - - if ( value && value.isColor ) { - - this.copy( value ); - - } else if ( typeof value === 'number' ) { - - this.setHex( value ); - - } else if ( typeof value === 'string' ) { - - this.setStyle( value ); - - } - - return this; - - } - - setScalar( scalar ) { - - this.r = scalar; - this.g = scalar; - this.b = scalar; - - return this; - - } - - setHex( hex ) { - - hex = Math.floor( hex ); - - this.r = ( hex >> 16 & 255 ) / 255; - this.g = ( hex >> 8 & 255 ) / 255; - this.b = ( hex & 255 ) / 255; - - return this; - - } - - setRGB( r, g, b ) { - - this.r = r; - this.g = g; - this.b = b; - - return this; - - } - - setHSL( h, s, l ) { - - // h,s,l ranges are in 0.0 - 1.0 - h = euclideanModulo( h, 1 ); - s = clamp( s, 0, 1 ); - l = clamp( l, 0, 1 ); - - if ( s === 0 ) { - - this.r = this.g = this.b = l; - - } else { - - const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); - const q = ( 2 * l ) - p; - - this.r = hue2rgb( q, p, h + 1 / 3 ); - this.g = hue2rgb( q, p, h ); - this.b = hue2rgb( q, p, h - 1 / 3 ); - - } - - return this; - - } - - setStyle( style ) { - - function handleAlpha( string ) { - - if ( string === undefined ) return; - - if ( parseFloat( string ) < 1 ) { - - console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); - - } - - } - - - let m; - - if ( m = /^((?:rgb|hsl)a?)\(([^\)]*)\)/.exec( style ) ) { - - // rgb / hsl - - let color; - const name = m[ 1 ]; - const components = m[ 2 ]; - - switch ( name ) { - - case 'rgb': - case 'rgba': - - if ( color = /^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { - - // rgb(255,0,0) rgba(255,0,0,0.5) - this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; - this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; - this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; - - handleAlpha( color[ 4 ] ); - - return this; - - } - - if ( color = /^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { - - // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) - this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; - this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; - this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; - - handleAlpha( color[ 4 ] ); - - return this; - - } - - break; - - case 'hsl': - case 'hsla': - - if ( color = /^\s*(\d*\.?\d+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { - - // hsl(120,50%,50%) hsla(120,50%,50%,0.5) - const h = parseFloat( color[ 1 ] ) / 360; - const s = parseInt( color[ 2 ], 10 ) / 100; - const l = parseInt( color[ 3 ], 10 ) / 100; - - handleAlpha( color[ 4 ] ); - - return this.setHSL( h, s, l ); - - } - - break; - - } - - } else if ( m = /^\#([A-Fa-f\d]+)$/.exec( style ) ) { - - // hex color - - const hex = m[ 1 ]; - const size = hex.length; - - if ( size === 3 ) { - - // #ff0 - this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; - this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; - this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; - - return this; - - } else if ( size === 6 ) { - - // #ff0000 - this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; - this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; - this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; - - return this; - - } - - } - - if ( style && style.length > 0 ) { - - return this.setColorName( style ); - - } - - return this; - - } - - setColorName( style ) { - - // color keywords - const hex = _colorKeywords[ style.toLowerCase() ]; - - if ( hex !== undefined ) { - - // red - this.setHex( hex ); - - } else { - - // unknown color - console.warn( 'THREE.Color: Unknown color ' + style ); - - } - - return this; - - } - - clone() { - - return new this.constructor( this.r, this.g, this.b ); - - } - - copy( color ) { - - this.r = color.r; - this.g = color.g; - this.b = color.b; - - return this; - - } - - copyGammaToLinear( color, gammaFactor = 2.0 ) { - - this.r = Math.pow( color.r, gammaFactor ); - this.g = Math.pow( color.g, gammaFactor ); - this.b = Math.pow( color.b, gammaFactor ); - - return this; - - } - - copyLinearToGamma( color, gammaFactor = 2.0 ) { - - const safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; - - this.r = Math.pow( color.r, safeInverse ); - this.g = Math.pow( color.g, safeInverse ); - this.b = Math.pow( color.b, safeInverse ); - - return this; - - } - - convertGammaToLinear( gammaFactor ) { - - this.copyGammaToLinear( this, gammaFactor ); - - return this; - - } - - convertLinearToGamma( gammaFactor ) { - - this.copyLinearToGamma( this, gammaFactor ); - - return this; - - } - - copySRGBToLinear( color ) { - - this.r = SRGBToLinear( color.r ); - this.g = SRGBToLinear( color.g ); - this.b = SRGBToLinear( color.b ); - - return this; - - } - - copyLinearToSRGB( color ) { - - this.r = LinearToSRGB( color.r ); - this.g = LinearToSRGB( color.g ); - this.b = LinearToSRGB( color.b ); - - return this; - - } - - convertSRGBToLinear() { - - this.copySRGBToLinear( this ); - - return this; - - } - - convertLinearToSRGB() { - - this.copyLinearToSRGB( this ); - - return this; - - } - - getHex() { - - return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; - - } - - getHexString() { - - return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); - - } - - getHSL( target ) { - - // h,s,l ranges are in 0.0 - 1.0 - - const r = this.r, g = this.g, b = this.b; - - const max = Math.max( r, g, b ); - const min = Math.min( r, g, b ); - - let hue, saturation; - const lightness = ( min + max ) / 2.0; - - if ( min === max ) { - - hue = 0; - saturation = 0; - - } else { - - const delta = max - min; - - saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); - - switch ( max ) { - - case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; - case g: hue = ( b - r ) / delta + 2; break; - case b: hue = ( r - g ) / delta + 4; break; - - } - - hue /= 6; - - } - - target.h = hue; - target.s = saturation; - target.l = lightness; - - return target; - - } - - getStyle() { - - return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; - - } - - offsetHSL( h, s, l ) { - - this.getHSL( _hslA ); - - _hslA.h += h; _hslA.s += s; _hslA.l += l; - - this.setHSL( _hslA.h, _hslA.s, _hslA.l ); - - return this; - - } - - add( color ) { - - this.r += color.r; - this.g += color.g; - this.b += color.b; - - return this; - - } - - addColors( color1, color2 ) { - - this.r = color1.r + color2.r; - this.g = color1.g + color2.g; - this.b = color1.b + color2.b; - - return this; - - } - - addScalar( s ) { - - this.r += s; - this.g += s; - this.b += s; - - return this; - - } - - sub( color ) { - - this.r = Math.max( 0, this.r - color.r ); - this.g = Math.max( 0, this.g - color.g ); - this.b = Math.max( 0, this.b - color.b ); - - return this; - - } - - multiply( color ) { - - this.r *= color.r; - this.g *= color.g; - this.b *= color.b; - - return this; - - } - - multiplyScalar( s ) { - - this.r *= s; - this.g *= s; - this.b *= s; - - return this; - - } - - lerp( color, alpha ) { - - this.r += ( color.r - this.r ) * alpha; - this.g += ( color.g - this.g ) * alpha; - this.b += ( color.b - this.b ) * alpha; - - return this; - - } - - lerpColors( color1, color2, alpha ) { - - this.r = color1.r + ( color2.r - color1.r ) * alpha; - this.g = color1.g + ( color2.g - color1.g ) * alpha; - this.b = color1.b + ( color2.b - color1.b ) * alpha; - - return this; - - } - - lerpHSL( color, alpha ) { - - this.getHSL( _hslA ); - color.getHSL( _hslB ); - - const h = lerp( _hslA.h, _hslB.h, alpha ); - const s = lerp( _hslA.s, _hslB.s, alpha ); - const l = lerp( _hslA.l, _hslB.l, alpha ); - - this.setHSL( h, s, l ); - - return this; - - } - - equals( c ) { - - return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); - - } - - fromArray( array, offset = 0 ) { - - this.r = array[ offset ]; - this.g = array[ offset + 1 ]; - this.b = array[ offset + 2 ]; - - return this; - - } - - toArray( array = [], offset = 0 ) { - - array[ offset ] = this.r; - array[ offset + 1 ] = this.g; - array[ offset + 2 ] = this.b; - - return array; - - } - - fromBufferAttribute( attribute, index ) { - - this.r = attribute.getX( index ); - this.g = attribute.getY( index ); - this.b = attribute.getZ( index ); - - if ( attribute.normalized === true ) { - - // assuming Uint8Array - - this.r /= 255; - this.g /= 255; - this.b /= 255; - - } - - return this; - - } - - toJSON() { - - return this.getHex(); - - } - - } - - Color.NAMES = _colorKeywords; - - Color.prototype.isColor = true; - Color.prototype.r = 1; - Color.prototype.g = 1; - Color.prototype.b = 1; - - /** - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * } - */ - - class MeshBasicMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.type = 'MeshBasicMaterial'; - - this.color = new Color( 0xffffff ); // emissive - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.setValues( parameters ); - - } - - copy( source ) { - - super.copy( source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - return this; - - } - - } - - MeshBasicMaterial.prototype.isMeshBasicMaterial = true; - - const _vector$9 = /*@__PURE__*/ new Vector3(); - const _vector2$1 = /*@__PURE__*/ new Vector2(); - - class BufferAttribute { - - constructor( array, itemSize, normalized ) { - - if ( Array.isArray( array ) ) { - - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - - } - - this.name = ''; - - this.array = array; - this.itemSize = itemSize; - this.count = array !== undefined ? array.length / itemSize : 0; - this.normalized = normalized === true; - - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; - - this.version = 0; - - } - - onUploadCallback() {} - - set needsUpdate( value ) { - - if ( value === true ) this.version ++; - - } - - setUsage( value ) { - - this.usage = value; - - return this; - - } - - copy( source ) { - - this.name = source.name; - this.array = new source.array.constructor( source.array ); - this.itemSize = source.itemSize; - this.count = source.count; - this.normalized = source.normalized; - - this.usage = source.usage; - - return this; - - } - - copyAt( index1, attribute, index2 ) { - - index1 *= this.itemSize; - index2 *= attribute.itemSize; - - for ( let i = 0, l = this.itemSize; i < l; i ++ ) { - - this.array[ index1 + i ] = attribute.array[ index2 + i ]; - - } - - return this; - - } - - copyArray( array ) { - - this.array.set( array ); - - return this; - - } - - copyColorsArray( colors ) { - - const array = this.array; - let offset = 0; - - for ( let i = 0, l = colors.length; i < l; i ++ ) { - - let color = colors[ i ]; - - if ( color === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); - color = new Color(); - - } - - array[ offset ++ ] = color.r; - array[ offset ++ ] = color.g; - array[ offset ++ ] = color.b; - - } - - return this; - - } - - copyVector2sArray( vectors ) { - - const array = this.array; - let offset = 0; - - for ( let i = 0, l = vectors.length; i < l; i ++ ) { - - let vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); - vector = new Vector2(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - - } - - return this; - - } - - copyVector3sArray( vectors ) { - - const array = this.array; - let offset = 0; - - for ( let i = 0, l = vectors.length; i < l; i ++ ) { - - let vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); - vector = new Vector3(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - - } - - return this; - - } - - copyVector4sArray( vectors ) { - - const array = this.array; - let offset = 0; - - for ( let i = 0, l = vectors.length; i < l; i ++ ) { - - let vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); - vector = new Vector4(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - array[ offset ++ ] = vector.w; - - } - - return this; - - } - - applyMatrix3( m ) { - - if ( this.itemSize === 2 ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector2$1.fromBufferAttribute( this, i ); - _vector2$1.applyMatrix3( m ); - - this.setXY( i, _vector2$1.x, _vector2$1.y ); - - } - - } else if ( this.itemSize === 3 ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$9.fromBufferAttribute( this, i ); - _vector$9.applyMatrix3( m ); - - this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); - - } - - } - - return this; - - } - - applyMatrix4( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$9.x = this.getX( i ); - _vector$9.y = this.getY( i ); - _vector$9.z = this.getZ( i ); - - _vector$9.applyMatrix4( m ); - - this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); - - } - - return this; - - } - - applyNormalMatrix( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$9.x = this.getX( i ); - _vector$9.y = this.getY( i ); - _vector$9.z = this.getZ( i ); - - _vector$9.applyNormalMatrix( m ); - - this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); - - } - - return this; - - } - - transformDirection( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$9.x = this.getX( i ); - _vector$9.y = this.getY( i ); - _vector$9.z = this.getZ( i ); - - _vector$9.transformDirection( m ); - - this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); - - } - - return this; - - } - - set( value, offset = 0 ) { - - this.array.set( value, offset ); - - return this; - - } - - getX( index ) { - - return this.array[ index * this.itemSize ]; - - } - - setX( index, x ) { - - this.array[ index * this.itemSize ] = x; - - return this; - - } - - getY( index ) { - - return this.array[ index * this.itemSize + 1 ]; - - } - - setY( index, y ) { - - this.array[ index * this.itemSize + 1 ] = y; - - return this; - - } - - getZ( index ) { - - return this.array[ index * this.itemSize + 2 ]; - - } - - setZ( index, z ) { - - this.array[ index * this.itemSize + 2 ] = z; - - return this; - - } - - getW( index ) { - - return this.array[ index * this.itemSize + 3 ]; - - } - - setW( index, w ) { - - this.array[ index * this.itemSize + 3 ] = w; - - return this; - - } - - setXY( index, x, y ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - - return this; - - } - - setXYZ( index, x, y, z ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - - return this; - - } - - setXYZW( index, x, y, z, w ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - this.array[ index + 3 ] = w; - - return this; - - } - - onUpload( callback ) { - - this.onUploadCallback = callback; - - return this; - - } - - clone() { - - return new this.constructor( this.array, this.itemSize ).copy( this ); - - } - - toJSON() { - - const data = { - itemSize: this.itemSize, - type: this.array.constructor.name, - array: Array.prototype.slice.call( this.array ), - normalized: this.normalized - }; - - if ( this.name !== '' ) data.name = this.name; - if ( this.usage !== StaticDrawUsage ) data.usage = this.usage; - if ( this.updateRange.offset !== 0 || this.updateRange.count !== - 1 ) data.updateRange = this.updateRange; - - return data; - - } - - } - - BufferAttribute.prototype.isBufferAttribute = true; - - class Uint16BufferAttribute extends BufferAttribute { - - constructor( array, itemSize, normalized ) { - - super( new Uint16Array( array ), itemSize, normalized ); - - } - - } - - class Uint32BufferAttribute extends BufferAttribute { - - constructor( array, itemSize, normalized ) { - - super( new Uint32Array( array ), itemSize, normalized ); - - } - - } - - class Float16BufferAttribute extends BufferAttribute { - - constructor( array, itemSize, normalized ) { - - super( new Uint16Array( array ), itemSize, normalized ); - - } - - } - - Float16BufferAttribute.prototype.isFloat16BufferAttribute = true; - - class Float32BufferAttribute extends BufferAttribute { - - constructor( array, itemSize, normalized ) { - - super( new Float32Array( array ), itemSize, normalized ); - - } - - } - - function arrayMax( array ) { - - if ( array.length === 0 ) return - Infinity; - - let max = array[ 0 ]; - - for ( let i = 1, l = array.length; i < l; ++ i ) { - - if ( array[ i ] > max ) max = array[ i ]; - - } - - return max; - - } - - let _id = 0; - - const _m1 = /*@__PURE__*/ new Matrix4(); - const _obj = /*@__PURE__*/ new Object3D(); - const _offset = /*@__PURE__*/ new Vector3(); - const _box$1$1 = /*@__PURE__*/ new Box3(); - const _boxMorphTargets = /*@__PURE__*/ new Box3(); - const _vector$8 = /*@__PURE__*/ new Vector3(); - - class BufferGeometry extends EventDispatcher { - - constructor() { - - super(); - - Object.defineProperty( this, 'id', { value: _id ++ } ); - - this.uuid = generateUUID(); - - this.name = ''; - this.type = 'BufferGeometry'; - - this.index = null; - this.attributes = {}; - - this.morphAttributes = {}; - this.morphTargetsRelative = false; - - this.groups = []; - - this.boundingBox = null; - this.boundingSphere = null; - - this.drawRange = { start: 0, count: Infinity }; - - this.userData = {}; - - } - - getIndex() { - - return this.index; - - } - - setIndex( index ) { - - if ( Array.isArray( index ) ) { - - this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); - - } else { - - this.index = index; - - } - - return this; - - } - - getAttribute( name ) { - - return this.attributes[ name ]; - - } - - setAttribute( name, attribute ) { - - this.attributes[ name ] = attribute; - - return this; - - } - - deleteAttribute( name ) { - - delete this.attributes[ name ]; - - return this; - - } - - hasAttribute( name ) { - - return this.attributes[ name ] !== undefined; - - } - - addGroup( start, count, materialIndex = 0 ) { - - this.groups.push( { - - start: start, - count: count, - materialIndex: materialIndex - - } ); - - } - - clearGroups() { - - this.groups = []; - - } - - setDrawRange( start, count ) { - - this.drawRange.start = start; - this.drawRange.count = count; - - } - - applyMatrix4( matrix ) { - - const position = this.attributes.position; - - if ( position !== undefined ) { - - position.applyMatrix4( matrix ); - - position.needsUpdate = true; - - } - - const normal = this.attributes.normal; - - if ( normal !== undefined ) { - - const normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - normal.applyNormalMatrix( normalMatrix ); - - normal.needsUpdate = true; - - } - - const tangent = this.attributes.tangent; - - if ( tangent !== undefined ) { - - tangent.transformDirection( matrix ); - - tangent.needsUpdate = true; - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - return this; - - } - - applyQuaternion( q ) { - - _m1.makeRotationFromQuaternion( q ); - - this.applyMatrix4( _m1 ); - - return this; - - } - - rotateX( angle ) { - - // rotate geometry around world x-axis - - _m1.makeRotationX( angle ); - - this.applyMatrix4( _m1 ); - - return this; - - } - - rotateY( angle ) { - - // rotate geometry around world y-axis - - _m1.makeRotationY( angle ); - - this.applyMatrix4( _m1 ); - - return this; - - } - - rotateZ( angle ) { - - // rotate geometry around world z-axis - - _m1.makeRotationZ( angle ); - - this.applyMatrix4( _m1 ); - - return this; - - } - - translate( x, y, z ) { - - // translate geometry - - _m1.makeTranslation( x, y, z ); - - this.applyMatrix4( _m1 ); - - return this; - - } - - scale( x, y, z ) { - - // scale geometry - - _m1.makeScale( x, y, z ); - - this.applyMatrix4( _m1 ); - - return this; - - } - - lookAt( vector ) { - - _obj.lookAt( vector ); - - _obj.updateMatrix(); - - this.applyMatrix4( _obj.matrix ); - - return this; - - } - - center() { - - this.computeBoundingBox(); - - this.boundingBox.getCenter( _offset ).negate(); - - this.translate( _offset.x, _offset.y, _offset.z ); - - return this; - - } - - setFromPoints( points ) { - - const position = []; - - for ( let i = 0, l = points.length; i < l; i ++ ) { - - const point = points[ i ]; - position.push( point.x, point.y, point.z || 0 ); - - } - - this.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) ); - - return this; - - } - - computeBoundingBox() { - - if ( this.boundingBox === null ) { - - this.boundingBox = new Box3(); - - } - - const position = this.attributes.position; - const morphAttributesPosition = this.morphAttributes.position; - - if ( position && position.isGLBufferAttribute ) { - - console.error( 'THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".', this ); - - this.boundingBox.set( - new Vector3( - Infinity, - Infinity, - Infinity ), - new Vector3( + Infinity, + Infinity, + Infinity ) - ); - - return; - - } - - if ( position !== undefined ) { - - this.boundingBox.setFromBufferAttribute( position ); - - // process morph attributes if present - - if ( morphAttributesPosition ) { - - for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - - const morphAttribute = morphAttributesPosition[ i ]; - _box$1$1.setFromBufferAttribute( morphAttribute ); - - if ( this.morphTargetsRelative ) { - - _vector$8.addVectors( this.boundingBox.min, _box$1$1.min ); - this.boundingBox.expandByPoint( _vector$8 ); - - _vector$8.addVectors( this.boundingBox.max, _box$1$1.max ); - this.boundingBox.expandByPoint( _vector$8 ); - - } else { - - this.boundingBox.expandByPoint( _box$1$1.min ); - this.boundingBox.expandByPoint( _box$1$1.max ); - - } - - } - - } - - } else { - - this.boundingBox.makeEmpty(); - - } - - if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { - - console.error( 'THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); - - } - - } - - computeBoundingSphere() { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new Sphere(); - - } - - const position = this.attributes.position; - const morphAttributesPosition = this.morphAttributes.position; - - if ( position && position.isGLBufferAttribute ) { - - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".', this ); - - this.boundingSphere.set( new Vector3(), Infinity ); - - return; - - } - - if ( position ) { - - // first, find the center of the bounding sphere - - const center = this.boundingSphere.center; - - _box$1$1.setFromBufferAttribute( position ); - - // process morph attributes if present - - if ( morphAttributesPosition ) { - - for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - - const morphAttribute = morphAttributesPosition[ i ]; - _boxMorphTargets.setFromBufferAttribute( morphAttribute ); - - if ( this.morphTargetsRelative ) { - - _vector$8.addVectors( _box$1$1.min, _boxMorphTargets.min ); - _box$1$1.expandByPoint( _vector$8 ); - - _vector$8.addVectors( _box$1$1.max, _boxMorphTargets.max ); - _box$1$1.expandByPoint( _vector$8 ); - - } else { - - _box$1$1.expandByPoint( _boxMorphTargets.min ); - _box$1$1.expandByPoint( _boxMorphTargets.max ); - - } - - } - - } - - _box$1$1.getCenter( center ); - - // second, try to find a boundingSphere with a radius smaller than the - // boundingSphere of the boundingBox: sqrt(3) smaller in the best case - - let maxRadiusSq = 0; - - for ( let i = 0, il = position.count; i < il; i ++ ) { - - _vector$8.fromBufferAttribute( position, i ); - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$8 ) ); - - } - - // process morph attributes if present - - if ( morphAttributesPosition ) { - - for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - - const morphAttribute = morphAttributesPosition[ i ]; - const morphTargetsRelative = this.morphTargetsRelative; - - for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) { - - _vector$8.fromBufferAttribute( morphAttribute, j ); - - if ( morphTargetsRelative ) { - - _offset.fromBufferAttribute( position, j ); - _vector$8.add( _offset ); - - } - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$8 ) ); - - } - - } - - } - - this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); - - if ( isNaN( this.boundingSphere.radius ) ) { - - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); - - } - - } - - } - - computeTangents() { - - const index = this.index; - const attributes = this.attributes; - - // based on http://www.terathon.com/code/tangent.html - // (per vertex tangents) - - if ( index === null || - attributes.position === undefined || - attributes.normal === undefined || - attributes.uv === undefined ) { - - console.error( 'THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)' ); - return; - - } - - const indices = index.array; - const positions = attributes.position.array; - const normals = attributes.normal.array; - const uvs = attributes.uv.array; - - const nVertices = positions.length / 3; - - if ( attributes.tangent === undefined ) { - - this.setAttribute( 'tangent', new BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); - - } - - const tangents = attributes.tangent.array; - - const tan1 = [], tan2 = []; - - for ( let i = 0; i < nVertices; i ++ ) { - - tan1[ i ] = new Vector3(); - tan2[ i ] = new Vector3(); - - } - - const vA = new Vector3(), - vB = new Vector3(), - vC = new Vector3(), - - uvA = new Vector2(), - uvB = new Vector2(), - uvC = new Vector2(), - - sdir = new Vector3(), - tdir = new Vector3(); - - function handleTriangle( a, b, c ) { - - vA.fromArray( positions, a * 3 ); - vB.fromArray( positions, b * 3 ); - vC.fromArray( positions, c * 3 ); - - uvA.fromArray( uvs, a * 2 ); - uvB.fromArray( uvs, b * 2 ); - uvC.fromArray( uvs, c * 2 ); - - vB.sub( vA ); - vC.sub( vA ); - - uvB.sub( uvA ); - uvC.sub( uvA ); - - const r = 1.0 / ( uvB.x * uvC.y - uvC.x * uvB.y ); - - // silently ignore degenerate uv triangles having coincident or colinear vertices - - if ( ! isFinite( r ) ) return; - - sdir.copy( vB ).multiplyScalar( uvC.y ).addScaledVector( vC, - uvB.y ).multiplyScalar( r ); - tdir.copy( vC ).multiplyScalar( uvB.x ).addScaledVector( vB, - uvC.x ).multiplyScalar( r ); - - tan1[ a ].add( sdir ); - tan1[ b ].add( sdir ); - tan1[ c ].add( sdir ); - - tan2[ a ].add( tdir ); - tan2[ b ].add( tdir ); - tan2[ c ].add( tdir ); - - } - - let groups = this.groups; - - if ( groups.length === 0 ) { - - groups = [ { - start: 0, - count: indices.length - } ]; - - } - - for ( let i = 0, il = groups.length; i < il; ++ i ) { - - const group = groups[ i ]; - - const start = group.start; - const count = group.count; - - for ( let j = start, jl = start + count; j < jl; j += 3 ) { - - handleTriangle( - indices[ j + 0 ], - indices[ j + 1 ], - indices[ j + 2 ] - ); - - } - - } - - const tmp = new Vector3(), tmp2 = new Vector3(); - const n = new Vector3(), n2 = new Vector3(); - - function handleVertex( v ) { - - n.fromArray( normals, v * 3 ); - n2.copy( n ); - - const t = tan1[ v ]; - - // Gram-Schmidt orthogonalize - - tmp.copy( t ); - tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); - - // Calculate handedness - - tmp2.crossVectors( n2, t ); - const test = tmp2.dot( tan2[ v ] ); - const w = ( test < 0.0 ) ? - 1.0 : 1.0; - - tangents[ v * 4 ] = tmp.x; - tangents[ v * 4 + 1 ] = tmp.y; - tangents[ v * 4 + 2 ] = tmp.z; - tangents[ v * 4 + 3 ] = w; - - } - - for ( let i = 0, il = groups.length; i < il; ++ i ) { - - const group = groups[ i ]; - - const start = group.start; - const count = group.count; - - for ( let j = start, jl = start + count; j < jl; j += 3 ) { - - handleVertex( indices[ j + 0 ] ); - handleVertex( indices[ j + 1 ] ); - handleVertex( indices[ j + 2 ] ); - - } - - } - - } - - computeVertexNormals() { - - const index = this.index; - const positionAttribute = this.getAttribute( 'position' ); - - if ( positionAttribute !== undefined ) { - - let normalAttribute = this.getAttribute( 'normal' ); - - if ( normalAttribute === undefined ) { - - normalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 ); - this.setAttribute( 'normal', normalAttribute ); - - } else { - - // reset existing normals to zero - - for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) { - - normalAttribute.setXYZ( i, 0, 0, 0 ); - - } - - } - - const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); - const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); - const cb = new Vector3(), ab = new Vector3(); - - // indexed elements - - if ( index ) { - - for ( let i = 0, il = index.count; i < il; i += 3 ) { - - const vA = index.getX( i + 0 ); - const vB = index.getX( i + 1 ); - const vC = index.getX( i + 2 ); - - pA.fromBufferAttribute( positionAttribute, vA ); - pB.fromBufferAttribute( positionAttribute, vB ); - pC.fromBufferAttribute( positionAttribute, vC ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - nA.fromBufferAttribute( normalAttribute, vA ); - nB.fromBufferAttribute( normalAttribute, vB ); - nC.fromBufferAttribute( normalAttribute, vC ); - - nA.add( cb ); - nB.add( cb ); - nC.add( cb ); - - normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z ); - normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z ); - normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z ); - - } - - } else { - - // non-indexed elements (unconnected triangle soup) - - for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) { - - pA.fromBufferAttribute( positionAttribute, i + 0 ); - pB.fromBufferAttribute( positionAttribute, i + 1 ); - pC.fromBufferAttribute( positionAttribute, i + 2 ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z ); - normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z ); - normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z ); - - } - - } - - this.normalizeNormals(); - - normalAttribute.needsUpdate = true; - - } - - } - - merge( geometry, offset ) { - - if ( ! ( geometry && geometry.isBufferGeometry ) ) { - - console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); - return; - - } - - if ( offset === undefined ) { - - offset = 0; - - console.warn( - 'THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. ' - + 'Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge.' - ); - - } - - const attributes = this.attributes; - - for ( const key in attributes ) { - - if ( geometry.attributes[ key ] === undefined ) continue; - - const attribute1 = attributes[ key ]; - const attributeArray1 = attribute1.array; - - const attribute2 = geometry.attributes[ key ]; - const attributeArray2 = attribute2.array; - - const attributeOffset = attribute2.itemSize * offset; - const length = Math.min( attributeArray2.length, attributeArray1.length - attributeOffset ); - - for ( let i = 0, j = attributeOffset; i < length; i ++, j ++ ) { - - attributeArray1[ j ] = attributeArray2[ i ]; - - } - - } - - return this; - - } - - normalizeNormals() { - - const normals = this.attributes.normal; - - for ( let i = 0, il = normals.count; i < il; i ++ ) { - - _vector$8.fromBufferAttribute( normals, i ); - - _vector$8.normalize(); - - normals.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); - - } - - } - - toNonIndexed() { - - function convertBufferAttribute( attribute, indices ) { - - const array = attribute.array; - const itemSize = attribute.itemSize; - const normalized = attribute.normalized; - - const array2 = new array.constructor( indices.length * itemSize ); - - let index = 0, index2 = 0; - - for ( let i = 0, l = indices.length; i < l; i ++ ) { - - if ( attribute.isInterleavedBufferAttribute ) { - - index = indices[ i ] * attribute.data.stride + attribute.offset; - - } else { - - index = indices[ i ] * itemSize; - - } - - for ( let j = 0; j < itemSize; j ++ ) { - - array2[ index2 ++ ] = array[ index ++ ]; - - } - - } - - return new BufferAttribute( array2, itemSize, normalized ); - - } - - // - - if ( this.index === null ) { - - console.warn( 'THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed.' ); - return this; - - } - - const geometry2 = new BufferGeometry(); - - const indices = this.index.array; - const attributes = this.attributes; - - // attributes - - for ( const name in attributes ) { - - const attribute = attributes[ name ]; - - const newAttribute = convertBufferAttribute( attribute, indices ); - - geometry2.setAttribute( name, newAttribute ); - - } - - // morph attributes - - const morphAttributes = this.morphAttributes; - - for ( const name in morphAttributes ) { - - const morphArray = []; - const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes - - for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) { - - const attribute = morphAttribute[ i ]; - - const newAttribute = convertBufferAttribute( attribute, indices ); - - morphArray.push( newAttribute ); - - } - - geometry2.morphAttributes[ name ] = morphArray; - - } - - geometry2.morphTargetsRelative = this.morphTargetsRelative; - - // groups - - const groups = this.groups; - - for ( let i = 0, l = groups.length; i < l; i ++ ) { - - const group = groups[ i ]; - geometry2.addGroup( group.start, group.count, group.materialIndex ); - - } - - return geometry2; - - } - - toJSON() { - - const data = { - metadata: { - version: 4.5, - type: 'BufferGeometry', - generator: 'BufferGeometry.toJSON' - } - }; - - // standard BufferGeometry serialization - - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) data.name = this.name; - if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; - - if ( this.parameters !== undefined ) { - - const parameters = this.parameters; - - for ( const key in parameters ) { - - if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; - - } - - return data; - - } - - // for simplicity the code assumes attributes are not shared across geometries, see #15811 - - data.data = { attributes: {} }; - - const index = this.index; - - if ( index !== null ) { - - data.data.index = { - type: index.array.constructor.name, - array: Array.prototype.slice.call( index.array ) - }; - - } - - const attributes = this.attributes; - - for ( const key in attributes ) { - - const attribute = attributes[ key ]; - - data.data.attributes[ key ] = attribute.toJSON( data.data ); - - } - - const morphAttributes = {}; - let hasMorphAttributes = false; - - for ( const key in this.morphAttributes ) { - - const attributeArray = this.morphAttributes[ key ]; - - const array = []; - - for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { - - const attribute = attributeArray[ i ]; - - array.push( attribute.toJSON( data.data ) ); - - } - - if ( array.length > 0 ) { - - morphAttributes[ key ] = array; - - hasMorphAttributes = true; - - } - - } - - if ( hasMorphAttributes ) { - - data.data.morphAttributes = morphAttributes; - data.data.morphTargetsRelative = this.morphTargetsRelative; - - } - - const groups = this.groups; - - if ( groups.length > 0 ) { - - data.data.groups = JSON.parse( JSON.stringify( groups ) ); - - } - - const boundingSphere = this.boundingSphere; - - if ( boundingSphere !== null ) { - - data.data.boundingSphere = { - center: boundingSphere.center.toArray(), - radius: boundingSphere.radius - }; - - } - - return data; - - } - - clone() { - - /* - // Handle primitives - - const parameters = this.parameters; - - if ( parameters !== undefined ) { - - const values = []; - - for ( const key in parameters ) { - - values.push( parameters[ key ] ); - - } - - const geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; - - } - - return new this.constructor().copy( this ); - */ - - return new BufferGeometry().copy( this ); - - } - - copy( source ) { - - // reset - - this.index = null; - this.attributes = {}; - this.morphAttributes = {}; - this.groups = []; - this.boundingBox = null; - this.boundingSphere = null; - - // used for storing cloned, shared data - - const data = {}; - - // name - - this.name = source.name; - - // index - - const index = source.index; - - if ( index !== null ) { - - this.setIndex( index.clone( data ) ); - - } - - // attributes - - const attributes = source.attributes; - - for ( const name in attributes ) { - - const attribute = attributes[ name ]; - this.setAttribute( name, attribute.clone( data ) ); - - } - - // morph attributes - - const morphAttributes = source.morphAttributes; - - for ( const name in morphAttributes ) { - - const array = []; - const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes - - for ( let i = 0, l = morphAttribute.length; i < l; i ++ ) { - - array.push( morphAttribute[ i ].clone( data ) ); - - } - - this.morphAttributes[ name ] = array; - - } - - this.morphTargetsRelative = source.morphTargetsRelative; - - // groups - - const groups = source.groups; - - for ( let i = 0, l = groups.length; i < l; i ++ ) { - - const group = groups[ i ]; - this.addGroup( group.start, group.count, group.materialIndex ); - - } - - // bounding box - - const boundingBox = source.boundingBox; - - if ( boundingBox !== null ) { - - this.boundingBox = boundingBox.clone(); - - } - - // bounding sphere - - const boundingSphere = source.boundingSphere; - - if ( boundingSphere !== null ) { - - this.boundingSphere = boundingSphere.clone(); - - } - - // draw range - - this.drawRange.start = source.drawRange.start; - this.drawRange.count = source.drawRange.count; - - // user data - - this.userData = source.userData; - - return this; - - } - - dispose() { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } - - BufferGeometry.prototype.isBufferGeometry = true; - - const _inverseMatrix$2 = /*@__PURE__*/ new Matrix4(); - const _ray$2 = /*@__PURE__*/ new Ray(); - const _sphere$3 = /*@__PURE__*/ new Sphere(); - - const _vA$1 = /*@__PURE__*/ new Vector3(); - const _vB$1 = /*@__PURE__*/ new Vector3(); - const _vC$1 = /*@__PURE__*/ new Vector3(); - - const _tempA = /*@__PURE__*/ new Vector3(); - const _tempB = /*@__PURE__*/ new Vector3(); - const _tempC = /*@__PURE__*/ new Vector3(); - - const _morphA = /*@__PURE__*/ new Vector3(); - const _morphB = /*@__PURE__*/ new Vector3(); - const _morphC = /*@__PURE__*/ new Vector3(); - - const _uvA$1 = /*@__PURE__*/ new Vector2(); - const _uvB$1 = /*@__PURE__*/ new Vector2(); - const _uvC$1 = /*@__PURE__*/ new Vector2(); - - const _intersectionPoint = /*@__PURE__*/ new Vector3(); - const _intersectionPointWorld = /*@__PURE__*/ new Vector3(); - - class Mesh extends Object3D { - - constructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) { - - super(); - - this.type = 'Mesh'; - - this.geometry = geometry; - this.material = material; - - this.updateMorphTargets(); - - } - - copy( source ) { - - super.copy( source ); - - if ( source.morphTargetInfluences !== undefined ) { - - this.morphTargetInfluences = source.morphTargetInfluences.slice(); - - } - - if ( source.morphTargetDictionary !== undefined ) { - - this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); - - } - - this.material = source.material; - this.geometry = source.geometry; - - return this; - - } - - updateMorphTargets() { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); - - if ( keys.length > 0 ) { - - const morphAttribute = morphAttributes[ keys[ 0 ] ]; - - if ( morphAttribute !== undefined ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - - const name = morphAttribute[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } - - } - - } - - } else { - - const morphTargets = geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - console.error( 'THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - } - - raycast( raycaster, intersects ) { - - const geometry = this.geometry; - const material = this.material; - const matrixWorld = this.matrixWorld; - - if ( material === undefined ) return; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _sphere$3.copy( geometry.boundingSphere ); - _sphere$3.applyMatrix4( matrixWorld ); - - if ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) return; - - // - - _inverseMatrix$2.copy( matrixWorld ).invert(); - _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); - - // Check boundingBox before continuing - - if ( geometry.boundingBox !== null ) { - - if ( _ray$2.intersectsBox( geometry.boundingBox ) === false ) return; - - } - - let intersection; - - if ( geometry.isBufferGeometry ) { - - const index = geometry.index; - const position = geometry.attributes.position; - const morphPosition = geometry.morphAttributes.position; - const morphTargetsRelative = geometry.morphTargetsRelative; - const uv = geometry.attributes.uv; - const uv2 = geometry.attributes.uv2; - const groups = geometry.groups; - const drawRange = geometry.drawRange; - - if ( index !== null ) { - - // indexed buffer geometry - - if ( Array.isArray( material ) ) { - - for ( let i = 0, il = groups.length; i < il; i ++ ) { - - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; - - const start = Math.max( group.start, drawRange.start ); - const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); - - for ( let j = start, jl = end; j < jl; j += 3 ) { - - const a = index.getX( j ); - const b = index.getX( j + 1 ); - const c = index.getX( j + 2 ); - - intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics - intersection.face.materialIndex = group.materialIndex; - intersects.push( intersection ); - - } - - } - - } - - } else { - - const start = Math.max( 0, drawRange.start ); - const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - - for ( let i = start, il = end; i < il; i += 3 ) { - - const a = index.getX( i ); - const b = index.getX( i + 1 ); - const c = index.getX( i + 2 ); - - intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics - intersects.push( intersection ); - - } - - } - - } - - } else if ( position !== undefined ) { - - // non-indexed buffer geometry - - if ( Array.isArray( material ) ) { - - for ( let i = 0, il = groups.length; i < il; i ++ ) { - - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; - - const start = Math.max( group.start, drawRange.start ); - const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); - - for ( let j = start, jl = end; j < jl; j += 3 ) { - - const a = j; - const b = j + 1; - const c = j + 2; - - intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics - intersection.face.materialIndex = group.materialIndex; - intersects.push( intersection ); - - } - - } - - } - - } else { - - const start = Math.max( 0, drawRange.start ); - const end = Math.min( position.count, ( drawRange.start + drawRange.count ) ); - - for ( let i = start, il = end; i < il; i += 3 ) { - - const a = i; - const b = i + 1; - const c = i + 2; - - intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics - intersects.push( intersection ); - - } - - } - - } - - } - - } else if ( geometry.isGeometry ) { - - console.error( 'THREE.Mesh.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - } - - Mesh.prototype.isMesh = true; - - function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { - - let intersect; - - if ( material.side === BackSide ) { - - intersect = ray.intersectTriangle( pC, pB, pA, true, point ); - - } else { - - intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point ); - - } - - if ( intersect === null ) return null; - - _intersectionPointWorld.copy( point ); - _intersectionPointWorld.applyMatrix4( object.matrixWorld ); - - const distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld ); - - if ( distance < raycaster.near || distance > raycaster.far ) return null; - - return { - distance: distance, - point: _intersectionPointWorld.clone(), - object: object - }; - - } - - function checkBufferGeometryIntersection( object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ) { - - _vA$1.fromBufferAttribute( position, a ); - _vB$1.fromBufferAttribute( position, b ); - _vC$1.fromBufferAttribute( position, c ); - - const morphInfluences = object.morphTargetInfluences; - - if ( morphPosition && morphInfluences ) { - - _morphA.set( 0, 0, 0 ); - _morphB.set( 0, 0, 0 ); - _morphC.set( 0, 0, 0 ); - - for ( let i = 0, il = morphPosition.length; i < il; i ++ ) { - - const influence = morphInfluences[ i ]; - const morphAttribute = morphPosition[ i ]; - - if ( influence === 0 ) continue; - - _tempA.fromBufferAttribute( morphAttribute, a ); - _tempB.fromBufferAttribute( morphAttribute, b ); - _tempC.fromBufferAttribute( morphAttribute, c ); - - if ( morphTargetsRelative ) { - - _morphA.addScaledVector( _tempA, influence ); - _morphB.addScaledVector( _tempB, influence ); - _morphC.addScaledVector( _tempC, influence ); - - } else { - - _morphA.addScaledVector( _tempA.sub( _vA$1 ), influence ); - _morphB.addScaledVector( _tempB.sub( _vB$1 ), influence ); - _morphC.addScaledVector( _tempC.sub( _vC$1 ), influence ); - - } - - } - - _vA$1.add( _morphA ); - _vB$1.add( _morphB ); - _vC$1.add( _morphC ); - - } - - if ( object.isSkinnedMesh ) { - - object.boneTransform( a, _vA$1 ); - object.boneTransform( b, _vB$1 ); - object.boneTransform( c, _vC$1 ); - - } - - const intersection = checkIntersection( object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint ); - - if ( intersection ) { - - if ( uv ) { - - _uvA$1.fromBufferAttribute( uv, a ); - _uvB$1.fromBufferAttribute( uv, b ); - _uvC$1.fromBufferAttribute( uv, c ); - - intersection.uv = Triangle.getUV( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); - - } - - if ( uv2 ) { - - _uvA$1.fromBufferAttribute( uv2, a ); - _uvB$1.fromBufferAttribute( uv2, b ); - _uvC$1.fromBufferAttribute( uv2, c ); - - intersection.uv2 = Triangle.getUV( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); - - } - - const face = { - a: a, - b: b, - c: c, - normal: new Vector3(), - materialIndex: 0 - }; - - Triangle.getNormal( _vA$1, _vB$1, _vC$1, face.normal ); - - intersection.face = face; - - } - - return intersection; - - } - - class BoxGeometry extends BufferGeometry { - - constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) { - - super(); - - this.type = 'BoxGeometry'; - - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; - - const scope = this; - - // segments - - widthSegments = Math.floor( widthSegments ); - heightSegments = Math.floor( heightSegments ); - depthSegments = Math.floor( depthSegments ); - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - let numberOfVertices = 0; - let groupStart = 0; - - // build each side of the box geometry - - buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px - buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx - buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py - buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny - buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz - buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { - - const segmentWidth = width / gridX; - const segmentHeight = height / gridY; - - const widthHalf = width / 2; - const heightHalf = height / 2; - const depthHalf = depth / 2; - - const gridX1 = gridX + 1; - const gridY1 = gridY + 1; - - let vertexCounter = 0; - let groupCount = 0; - - const vector = new Vector3(); - - // generate vertices, normals and uvs - - for ( let iy = 0; iy < gridY1; iy ++ ) { - - const y = iy * segmentHeight - heightHalf; - - for ( let ix = 0; ix < gridX1; ix ++ ) { - - const x = ix * segmentWidth - widthHalf; - - // set values to correct vector component - - vector[ u ] = x * udir; - vector[ v ] = y * vdir; - vector[ w ] = depthHalf; - - // now apply vector to vertex buffer - - vertices.push( vector.x, vector.y, vector.z ); - - // set values to correct vector component - - vector[ u ] = 0; - vector[ v ] = 0; - vector[ w ] = depth > 0 ? 1 : - 1; - - // now apply vector to normal buffer - - normals.push( vector.x, vector.y, vector.z ); - - // uvs - - uvs.push( ix / gridX ); - uvs.push( 1 - ( iy / gridY ) ); - - // counters - - vertexCounter += 1; - - } - - } - - // indices - - // 1. you need three indices to draw a single face - // 2. a single segment consists of two faces - // 3. so we need to generate six (2*3) indices per segment - - for ( let iy = 0; iy < gridY; iy ++ ) { - - for ( let ix = 0; ix < gridX; ix ++ ) { - - const a = numberOfVertices + ix + gridX1 * iy; - const b = numberOfVertices + ix + gridX1 * ( iy + 1 ); - const c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); - const d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - // increase counter - - groupCount += 6; - - } - - } - - // add a group to the geometry. this will ensure multi material support - - scope.addGroup( groupStart, groupCount, materialIndex ); - - // calculate new start value for groups - - groupStart += groupCount; - - // update total number of vertices - - numberOfVertices += vertexCounter; - - } - - } - - static fromJSON( data ) { - - return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); - - } - - } - - /** - * Uniform Utilities - */ - - function cloneUniforms( src ) { - - const dst = {}; - - for ( const u in src ) { - - dst[ u ] = {}; - - for ( const p in src[ u ] ) { - - const property = src[ u ][ p ]; - - if ( property && ( property.isColor || - property.isMatrix3 || property.isMatrix4 || - property.isVector2 || property.isVector3 || property.isVector4 || - property.isTexture || property.isQuaternion ) ) { - - dst[ u ][ p ] = property.clone(); - - } else if ( Array.isArray( property ) ) { - - dst[ u ][ p ] = property.slice(); - - } else { - - dst[ u ][ p ] = property; - - } - - } - - } - - return dst; - - } - - function mergeUniforms( uniforms ) { - - const merged = {}; - - for ( let u = 0; u < uniforms.length; u ++ ) { - - const tmp = cloneUniforms( uniforms[ u ] ); - - for ( const p in tmp ) { - - merged[ p ] = tmp[ p ]; - - } - - } - - return merged; - - } - - // Legacy - - const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; - - var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}"; - - var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}"; - - /** - * parameters = { - * defines: { "label" : "value" }, - * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, - * - * fragmentShader: , - * vertexShader: , - * - * wireframe: , - * wireframeLinewidth: , - * - * lights: - * } - */ - - class ShaderMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.type = 'ShaderMaterial'; - - this.defines = {}; - this.uniforms = {}; - - this.vertexShader = default_vertex; - this.fragmentShader = default_fragment; - - this.linewidth = 1; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; // set to use scene fog - this.lights = false; // set to use scene lights - this.clipping = false; // set to use user-defined clipping planes - - this.extensions = { - derivatives: false, // set to use derivatives - fragDepth: false, // set to use fragment depth values - drawBuffers: false, // set to use draw buffers - shaderTextureLOD: false // set to use shader texture LOD - }; - - // When rendered geometry doesn't include these attributes but the material does, - // use these default values in WebGL. This avoids errors when buffer data is missing. - this.defaultAttributeValues = { - 'color': [ 1, 1, 1 ], - 'uv': [ 0, 0 ], - 'uv2': [ 0, 0 ] - }; - - this.index0AttributeName = undefined; - this.uniformsNeedUpdate = false; - - this.glslVersion = null; - - if ( parameters !== undefined ) { - - if ( parameters.attributes !== undefined ) { - - console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); - - } - - this.setValues( parameters ); - - } - - } - - copy( source ) { - - super.copy( source ); - - this.fragmentShader = source.fragmentShader; - this.vertexShader = source.vertexShader; - - this.uniforms = cloneUniforms( source.uniforms ); - - this.defines = Object.assign( {}, source.defines ); - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - this.lights = source.lights; - this.clipping = source.clipping; - - this.extensions = Object.assign( {}, source.extensions ); - - this.glslVersion = source.glslVersion; - - return this; - - } - - toJSON( meta ) { - - const data = super.toJSON( meta ); - - data.glslVersion = this.glslVersion; - data.uniforms = {}; - - for ( const name in this.uniforms ) { - - const uniform = this.uniforms[ name ]; - const value = uniform.value; - - if ( value && value.isTexture ) { - - data.uniforms[ name ] = { - type: 't', - value: value.toJSON( meta ).uuid - }; - - } else if ( value && value.isColor ) { - - data.uniforms[ name ] = { - type: 'c', - value: value.getHex() - }; - - } else if ( value && value.isVector2 ) { - - data.uniforms[ name ] = { - type: 'v2', - value: value.toArray() - }; - - } else if ( value && value.isVector3 ) { - - data.uniforms[ name ] = { - type: 'v3', - value: value.toArray() - }; - - } else if ( value && value.isVector4 ) { - - data.uniforms[ name ] = { - type: 'v4', - value: value.toArray() - }; - - } else if ( value && value.isMatrix3 ) { - - data.uniforms[ name ] = { - type: 'm3', - value: value.toArray() - }; - - } else if ( value && value.isMatrix4 ) { - - data.uniforms[ name ] = { - type: 'm4', - value: value.toArray() - }; - - } else { - - data.uniforms[ name ] = { - value: value - }; - - // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far - - } - - } - - if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; - - data.vertexShader = this.vertexShader; - data.fragmentShader = this.fragmentShader; - - const extensions = {}; - - for ( const key in this.extensions ) { - - if ( this.extensions[ key ] === true ) extensions[ key ] = true; - - } - - if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; - - return data; - - } - - } - - ShaderMaterial.prototype.isShaderMaterial = true; - - class Camera extends Object3D { - - constructor() { - - super(); - - this.type = 'Camera'; - - this.matrixWorldInverse = new Matrix4(); - - this.projectionMatrix = new Matrix4(); - this.projectionMatrixInverse = new Matrix4(); - - } - - copy( source, recursive ) { - - super.copy( source, recursive ); - - this.matrixWorldInverse.copy( source.matrixWorldInverse ); - - this.projectionMatrix.copy( source.projectionMatrix ); - this.projectionMatrixInverse.copy( source.projectionMatrixInverse ); - - return this; - - } - - getWorldDirection( target ) { - - this.updateWorldMatrix( true, false ); - - const e = this.matrixWorld.elements; - - return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize(); - - } - - updateMatrixWorld( force ) { - - super.updateMatrixWorld( force ); - - this.matrixWorldInverse.copy( this.matrixWorld ).invert(); - - } - - updateWorldMatrix( updateParents, updateChildren ) { - - super.updateWorldMatrix( updateParents, updateChildren ); - - this.matrixWorldInverse.copy( this.matrixWorld ).invert(); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - } - - Camera.prototype.isCamera = true; - - class PerspectiveCamera extends Camera { - - constructor( fov = 50, aspect = 1, near = 0.1, far = 2000 ) { - - super(); - - this.type = 'PerspectiveCamera'; - - this.fov = fov; - this.zoom = 1; - - this.near = near; - this.far = far; - this.focus = 10; - - this.aspect = aspect; - this.view = null; - - this.filmGauge = 35; // width of the film (default in millimeters) - this.filmOffset = 0; // horizontal film offset (same unit as gauge) - - this.updateProjectionMatrix(); - - } - - copy( source, recursive ) { - - super.copy( source, recursive ); - - this.fov = source.fov; - this.zoom = source.zoom; - - this.near = source.near; - this.far = source.far; - this.focus = source.focus; - - this.aspect = source.aspect; - this.view = source.view === null ? null : Object.assign( {}, source.view ); - - this.filmGauge = source.filmGauge; - this.filmOffset = source.filmOffset; - - return this; - - } - - /** - * Sets the FOV by focal length in respect to the current .filmGauge. - * - * The default film gauge is 35, so that the focal length can be specified for - * a 35mm (full frame) camera. - * - * Values for focal length and film gauge must have the same unit. - */ - setFocalLength( focalLength ) { - - /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ - const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; - - this.fov = RAD2DEG * 2 * Math.atan( vExtentSlope ); - this.updateProjectionMatrix(); - - } - - /** - * Calculates the focal length from the current .fov and .filmGauge. - */ - getFocalLength() { - - const vExtentSlope = Math.tan( DEG2RAD * 0.5 * this.fov ); - - return 0.5 * this.getFilmHeight() / vExtentSlope; - - } - - getEffectiveFOV() { - - return RAD2DEG * 2 * Math.atan( - Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom ); - - } - - getFilmWidth() { - - // film not completely covered in portrait format (aspect < 1) - return this.filmGauge * Math.min( this.aspect, 1 ); - - } - - getFilmHeight() { - - // film not completely covered in landscape format (aspect > 1) - return this.filmGauge / Math.max( this.aspect, 1 ); - - } - - /** - * Sets an offset in a larger frustum. This is useful for multi-window or - * multi-monitor/multi-machine setups. - * - * For example, if you have 3x2 monitors and each monitor is 1920x1080 and - * the monitors are in grid like this - * - * +---+---+---+ - * | A | B | C | - * +---+---+---+ - * | D | E | F | - * +---+---+---+ - * - * then for each monitor you would call it like this - * - * const w = 1920; - * const h = 1080; - * const fullWidth = w * 3; - * const fullHeight = h * 2; - * - * --A-- - * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); - * --B-- - * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); - * --C-- - * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); - * --D-- - * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); - * --E-- - * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); - * --F-- - * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); - * - * Note there is no reason monitors have to be the same size or in a grid. - */ - setViewOffset( fullWidth, fullHeight, x, y, width, height ) { - - this.aspect = fullWidth / fullHeight; - - if ( this.view === null ) { - - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; - - } - - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; - - this.updateProjectionMatrix(); - - } - - clearViewOffset() { - - if ( this.view !== null ) { - - this.view.enabled = false; - - } - - this.updateProjectionMatrix(); - - } - - updateProjectionMatrix() { - - const near = this.near; - let top = near * Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom; - let height = 2 * top; - let width = this.aspect * height; - let left = - 0.5 * width; - const view = this.view; - - if ( this.view !== null && this.view.enabled ) { - - const fullWidth = view.fullWidth, - fullHeight = view.fullHeight; - - left += view.offsetX * width / fullWidth; - top -= view.offsetY * height / fullHeight; - width *= view.width / fullWidth; - height *= view.height / fullHeight; - - } - - const skew = this.filmOffset; - if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); - - this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); - - this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); - - } - - toJSON( meta ) { - - const data = super.toJSON( meta ); - - data.object.fov = this.fov; - data.object.zoom = this.zoom; - - data.object.near = this.near; - data.object.far = this.far; - data.object.focus = this.focus; - - data.object.aspect = this.aspect; - - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - - data.object.filmGauge = this.filmGauge; - data.object.filmOffset = this.filmOffset; - - return data; - - } - - } - - PerspectiveCamera.prototype.isPerspectiveCamera = true; - - const fov = 90, aspect = 1; - - class CubeCamera extends Object3D { - - constructor( near, far, renderTarget ) { - - super(); - - this.type = 'CubeCamera'; - - if ( renderTarget.isWebGLCubeRenderTarget !== true ) { - - console.error( 'THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter.' ); - return; - - } - - this.renderTarget = renderTarget; - - const cameraPX = new PerspectiveCamera( fov, aspect, near, far ); - cameraPX.layers = this.layers; - cameraPX.up.set( 0, - 1, 0 ); - cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); - this.add( cameraPX ); - - const cameraNX = new PerspectiveCamera( fov, aspect, near, far ); - cameraNX.layers = this.layers; - cameraNX.up.set( 0, - 1, 0 ); - cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); - this.add( cameraNX ); - - const cameraPY = new PerspectiveCamera( fov, aspect, near, far ); - cameraPY.layers = this.layers; - cameraPY.up.set( 0, 0, 1 ); - cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); - this.add( cameraPY ); - - const cameraNY = new PerspectiveCamera( fov, aspect, near, far ); - cameraNY.layers = this.layers; - cameraNY.up.set( 0, 0, - 1 ); - cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); - this.add( cameraNY ); - - const cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraPZ.layers = this.layers; - cameraPZ.up.set( 0, - 1, 0 ); - cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); - this.add( cameraPZ ); - - const cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraNZ.layers = this.layers; - cameraNZ.up.set( 0, - 1, 0 ); - cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); - this.add( cameraNZ ); - - } - - update( renderer, scene ) { - - if ( this.parent === null ) this.updateMatrixWorld(); - - const renderTarget = this.renderTarget; - - const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children; - - const currentXrEnabled = renderer.xr.enabled; - const currentRenderTarget = renderer.getRenderTarget(); - - renderer.xr.enabled = false; - - const generateMipmaps = renderTarget.texture.generateMipmaps; - - renderTarget.texture.generateMipmaps = false; - - renderer.setRenderTarget( renderTarget, 0 ); - renderer.render( scene, cameraPX ); - - renderer.setRenderTarget( renderTarget, 1 ); - renderer.render( scene, cameraNX ); - - renderer.setRenderTarget( renderTarget, 2 ); - renderer.render( scene, cameraPY ); - - renderer.setRenderTarget( renderTarget, 3 ); - renderer.render( scene, cameraNY ); - - renderer.setRenderTarget( renderTarget, 4 ); - renderer.render( scene, cameraPZ ); - - renderTarget.texture.generateMipmaps = generateMipmaps; - - renderer.setRenderTarget( renderTarget, 5 ); - renderer.render( scene, cameraNZ ); - - renderer.setRenderTarget( currentRenderTarget ); - - renderer.xr.enabled = currentXrEnabled; - - } - - } - - class CubeTexture extends Texture { - - constructor( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - - images = images !== undefined ? images : []; - mapping = mapping !== undefined ? mapping : CubeReflectionMapping; - format = format !== undefined ? format : RGBFormat; - - super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.flipY = false; - - } - - get images() { - - return this.image; - - } - - set images( value ) { - - this.image = value; - - } - - } - - CubeTexture.prototype.isCubeTexture = true; - - class WebGLCubeRenderTarget extends WebGLRenderTarget { - - constructor( size, options, dummy ) { - - if ( Number.isInteger( options ) ) { - - console.warn( 'THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )' ); - - options = dummy; - - } - - super( size, size, options ); - - options = options || {}; - - // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js) - // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, - // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. - - // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped - // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture - // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). - - this.texture = new CubeTexture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); - this.texture.isRenderTargetTexture = true; - - this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; - this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; - - this.texture._needsFlipEnvMap = false; - - } - - fromEquirectangularTexture( renderer, texture ) { - - this.texture.type = texture.type; - this.texture.format = RGBAFormat; // see #18859 - this.texture.encoding = texture.encoding; - - this.texture.generateMipmaps = texture.generateMipmaps; - this.texture.minFilter = texture.minFilter; - this.texture.magFilter = texture.magFilter; - - const shader = { - - uniforms: { - tEquirect: { value: null }, - }, - - vertexShader: /* glsl */` - - varying vec3 vWorldDirection; - - vec3 transformDirection( in vec3 dir, in mat4 matrix ) { - - return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); - - } - - void main() { - - vWorldDirection = transformDirection( position, modelMatrix ); - - #include - #include - - } - `, - - fragmentShader: /* glsl */` - - uniform sampler2D tEquirect; - - varying vec3 vWorldDirection; - - #include - - void main() { - - vec3 direction = normalize( vWorldDirection ); - - vec2 sampleUV = equirectUv( direction ); - - gl_FragColor = texture2D( tEquirect, sampleUV ); - - } - ` - }; - - const geometry = new BoxGeometry( 5, 5, 5 ); - - const material = new ShaderMaterial( { - - name: 'CubemapFromEquirect', - - uniforms: cloneUniforms( shader.uniforms ), - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader, - side: BackSide, - blending: NoBlending - - } ); - - material.uniforms.tEquirect.value = texture; - - const mesh = new Mesh( geometry, material ); - - const currentMinFilter = texture.minFilter; - - // Avoid blurred poles - if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter; - - const camera = new CubeCamera( 1, 10, this ); - camera.update( renderer, mesh ); - - texture.minFilter = currentMinFilter; - - mesh.geometry.dispose(); - mesh.material.dispose(); - - return this; - - } - - clear( renderer, color, depth, stencil ) { - - const currentRenderTarget = renderer.getRenderTarget(); - - for ( let i = 0; i < 6; i ++ ) { - - renderer.setRenderTarget( this, i ); - - renderer.clear( color, depth, stencil ); - - } - - renderer.setRenderTarget( currentRenderTarget ); - - } - - } - - WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; - - const _vector1 = /*@__PURE__*/ new Vector3(); - const _vector2 = /*@__PURE__*/ new Vector3(); - const _normalMatrix = /*@__PURE__*/ new Matrix3(); - - class Plane { - - constructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) { - - // normal is assumed to be normalized - - this.normal = normal; - this.constant = constant; - - } - - set( normal, constant ) { - - this.normal.copy( normal ); - this.constant = constant; - - return this; - - } - - setComponents( x, y, z, w ) { - - this.normal.set( x, y, z ); - this.constant = w; - - return this; - - } - - setFromNormalAndCoplanarPoint( normal, point ) { - - this.normal.copy( normal ); - this.constant = - point.dot( this.normal ); - - return this; - - } - - setFromCoplanarPoints( a, b, c ) { - - const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); - - // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? - - this.setFromNormalAndCoplanarPoint( normal, a ); - - return this; - - } - - copy( plane ) { - - this.normal.copy( plane.normal ); - this.constant = plane.constant; - - return this; - - } - - normalize() { - - // Note: will lead to a divide by zero if the plane is invalid. - - const inverseNormalLength = 1.0 / this.normal.length(); - this.normal.multiplyScalar( inverseNormalLength ); - this.constant *= inverseNormalLength; - - return this; - - } - - negate() { - - this.constant *= - 1; - this.normal.negate(); - - return this; - - } - - distanceToPoint( point ) { - - return this.normal.dot( point ) + this.constant; - - } - - distanceToSphere( sphere ) { - - return this.distanceToPoint( sphere.center ) - sphere.radius; - - } - - projectPoint( point, target ) { - - return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); - - } - - intersectLine( line, target ) { - - const direction = line.delta( _vector1 ); - - const denominator = this.normal.dot( direction ); - - if ( denominator === 0 ) { - - // line is coplanar, return origin - if ( this.distanceToPoint( line.start ) === 0 ) { - - return target.copy( line.start ); - - } - - // Unsure if this is the correct method to handle this case. - return null; - - } - - const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; - - if ( t < 0 || t > 1 ) { - - return null; - - } - - return target.copy( direction ).multiplyScalar( t ).add( line.start ); - - } - - intersectsLine( line ) { - - // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. - - const startSign = this.distanceToPoint( line.start ); - const endSign = this.distanceToPoint( line.end ); - - return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); - - } - - intersectsBox( box ) { - - return box.intersectsPlane( this ); - - } - - intersectsSphere( sphere ) { - - return sphere.intersectsPlane( this ); - - } - - coplanarPoint( target ) { - - return target.copy( this.normal ).multiplyScalar( - this.constant ); - - } - - applyMatrix4( matrix, optionalNormalMatrix ) { - - const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); - - const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); - - const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); - - this.constant = - referencePoint.dot( normal ); - - return this; - - } - - translate( offset ) { - - this.constant -= offset.dot( this.normal ); - - return this; - - } - - equals( plane ) { - - return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - } - - Plane.prototype.isPlane = true; - - const _sphere$2 = /*@__PURE__*/ new Sphere(); - const _vector$7 = /*@__PURE__*/ new Vector3(); - - class Frustum { - - constructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane() ) { - - this.planes = [ p0, p1, p2, p3, p4, p5 ]; - - } - - set( p0, p1, p2, p3, p4, p5 ) { - - const planes = this.planes; - - planes[ 0 ].copy( p0 ); - planes[ 1 ].copy( p1 ); - planes[ 2 ].copy( p2 ); - planes[ 3 ].copy( p3 ); - planes[ 4 ].copy( p4 ); - planes[ 5 ].copy( p5 ); - - return this; - - } - - copy( frustum ) { - - const planes = this.planes; - - for ( let i = 0; i < 6; i ++ ) { - - planes[ i ].copy( frustum.planes[ i ] ); - - } - - return this; - - } - - setFromProjectionMatrix( m ) { - - const planes = this.planes; - const me = m.elements; - const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; - const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; - const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; - const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; - - planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); - planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); - planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); - planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); - planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); - planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); - - return this; - - } - - intersectsObject( object ) { - - const geometry = object.geometry; - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _sphere$2.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); - - return this.intersectsSphere( _sphere$2 ); - - } - - intersectsSprite( sprite ) { - - _sphere$2.center.set( 0, 0, 0 ); - _sphere$2.radius = 0.7071067811865476; - _sphere$2.applyMatrix4( sprite.matrixWorld ); - - return this.intersectsSphere( _sphere$2 ); - - } - - intersectsSphere( sphere ) { - - const planes = this.planes; - const center = sphere.center; - const negRadius = - sphere.radius; - - for ( let i = 0; i < 6; i ++ ) { - - const distance = planes[ i ].distanceToPoint( center ); - - if ( distance < negRadius ) { - - return false; - - } - - } - - return true; - - } - - intersectsBox( box ) { - - const planes = this.planes; - - for ( let i = 0; i < 6; i ++ ) { - - const plane = planes[ i ]; - - // corner at max distance - - _vector$7.x = plane.normal.x > 0 ? box.max.x : box.min.x; - _vector$7.y = plane.normal.y > 0 ? box.max.y : box.min.y; - _vector$7.z = plane.normal.z > 0 ? box.max.z : box.min.z; - - if ( plane.distanceToPoint( _vector$7 ) < 0 ) { - - return false; - - } - - } - - return true; - - } - - containsPoint( point ) { - - const planes = this.planes; - - for ( let i = 0; i < 6; i ++ ) { - - if ( planes[ i ].distanceToPoint( point ) < 0 ) { - - return false; - - } - - } - - return true; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - } - - function WebGLAnimation() { - - let context = null; - let isAnimating = false; - let animationLoop = null; - let requestId = null; - - function onAnimationFrame( time, frame ) { - - animationLoop( time, frame ); - - requestId = context.requestAnimationFrame( onAnimationFrame ); - - } - - return { - - start: function () { - - if ( isAnimating === true ) return; - if ( animationLoop === null ) return; - - requestId = context.requestAnimationFrame( onAnimationFrame ); - - isAnimating = true; - - }, - - stop: function () { - - context.cancelAnimationFrame( requestId ); - - isAnimating = false; - - }, - - setAnimationLoop: function ( callback ) { - - animationLoop = callback; - - }, - - setContext: function ( value ) { - - context = value; - - } - - }; - - } - - function WebGLAttributes( gl, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - const buffers = new WeakMap(); - - function createBuffer( attribute, bufferType ) { - - const array = attribute.array; - const usage = attribute.usage; - - const buffer = gl.createBuffer(); - - gl.bindBuffer( bufferType, buffer ); - gl.bufferData( bufferType, array, usage ); - - attribute.onUploadCallback(); - - let type = 5126; - - if ( array instanceof Float32Array ) { - - type = 5126; - - } else if ( array instanceof Float64Array ) { - - console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' ); - - } else if ( array instanceof Uint16Array ) { - - if ( attribute.isFloat16BufferAttribute ) { - - if ( isWebGL2 ) { - - type = 5131; - - } else { - - console.warn( 'THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.' ); - - } - - } else { - - type = 5123; - - } - - } else if ( array instanceof Int16Array ) { - - type = 5122; - - } else if ( array instanceof Uint32Array ) { - - type = 5125; - - } else if ( array instanceof Int32Array ) { - - type = 5124; - - } else if ( array instanceof Int8Array ) { - - type = 5120; - - } else if ( array instanceof Uint8Array ) { - - type = 5121; - - } else if ( array instanceof Uint8ClampedArray ) { - - type = 5121; - - } - - return { - buffer: buffer, - type: type, - bytesPerElement: array.BYTES_PER_ELEMENT, - version: attribute.version - }; - - } - - function updateBuffer( buffer, attribute, bufferType ) { - - const array = attribute.array; - const updateRange = attribute.updateRange; - - gl.bindBuffer( bufferType, buffer ); - - if ( updateRange.count === - 1 ) { - - // Not using update ranges - - gl.bufferSubData( bufferType, 0, array ); - - } else { - - if ( isWebGL2 ) { - - gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, - array, updateRange.offset, updateRange.count ); - - } else { - - gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, - array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); - - } - - updateRange.count = - 1; // reset range - - } - - } - - // - - function get( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - - return buffers.get( attribute ); - - } - - function remove( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - - const data = buffers.get( attribute ); - - if ( data ) { - - gl.deleteBuffer( data.buffer ); - - buffers.delete( attribute ); - - } - - } - - function update( attribute, bufferType ) { - - if ( attribute.isGLBufferAttribute ) { - - const cached = buffers.get( attribute ); - - if ( ! cached || cached.version < attribute.version ) { - - buffers.set( attribute, { - buffer: attribute.buffer, - type: attribute.type, - bytesPerElement: attribute.elementSize, - version: attribute.version - } ); - - } - - return; - - } - - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - - const data = buffers.get( attribute ); - - if ( data === undefined ) { - - buffers.set( attribute, createBuffer( attribute, bufferType ) ); - - } else if ( data.version < attribute.version ) { - - updateBuffer( data.buffer, attribute, bufferType ); - - data.version = attribute.version; - - } - - } - - return { - - get: get, - remove: remove, - update: update - - }; - - } - - class PlaneGeometry extends BufferGeometry { - - constructor( width = 1, height = 1, widthSegments = 1, heightSegments = 1 ) { - - super(); - this.type = 'PlaneGeometry'; - - this.parameters = { - width: width, - height: height, - widthSegments: widthSegments, - heightSegments: heightSegments - }; - - const width_half = width / 2; - const height_half = height / 2; - - const gridX = Math.floor( widthSegments ); - const gridY = Math.floor( heightSegments ); - - const gridX1 = gridX + 1; - const gridY1 = gridY + 1; - - const segment_width = width / gridX; - const segment_height = height / gridY; - - // - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - for ( let iy = 0; iy < gridY1; iy ++ ) { - - const y = iy * segment_height - height_half; - - for ( let ix = 0; ix < gridX1; ix ++ ) { - - const x = ix * segment_width - width_half; - - vertices.push( x, - y, 0 ); - - normals.push( 0, 0, 1 ); - - uvs.push( ix / gridX ); - uvs.push( 1 - ( iy / gridY ) ); - - } - - } - - for ( let iy = 0; iy < gridY; iy ++ ) { - - for ( let ix = 0; ix < gridX; ix ++ ) { - - const a = ix + gridX1 * iy; - const b = ix + gridX1 * ( iy + 1 ); - const c = ( ix + 1 ) + gridX1 * ( iy + 1 ); - const d = ( ix + 1 ) + gridX1 * iy; - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - static fromJSON( data ) { - - return new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments ); - - } - - } - - var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif"; - - var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; - - var alphatest_fragment = "#ifdef USE_ALPHATEST\n\tif ( diffuseColor.a < alphaTest ) discard;\n#endif"; - - var alphatest_pars_fragment = "#ifdef USE_ALPHATEST\n\tuniform float alphaTest;\n#endif"; - - var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );\n\t#endif\n#endif"; - - var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; - - var begin_vertex = "vec3 transformed = vec3( position );"; - - var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif"; - - var bsdfs = "vec3 BRDF_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n}\nfloat V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( f0, f90, dotVH );\n\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( V * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotVH = saturate( dot( geometry.viewDir, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, 1.0, dotVH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie( float roughness, float NoH ) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max( 1.0 - cos2h, 0.0078125 );\n\treturn ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI );\n}\nfloat V_Neubelt( float NoV, float NoL ) {\n\treturn saturate( 1.0 / ( 4.0 * ( NoL + NoV - NoL * NoV ) ) );\n}\nvec3 BRDF_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif"; - - var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 ) * faceDirection;\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif"; - - var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#pragma unroll_loop_end\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t\tif ( clipped ) discard;\n\t#endif\n#endif"; - - var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif"; - - var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n#endif"; - - var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif"; - - var color_fragment = "#if defined( USE_COLOR_ALPHA )\n\tdiffuseColor *= vColor;\n#elif defined( USE_COLOR )\n\tdiffuseColor.rgb *= vColor;\n#endif"; - - var color_pars_fragment = "#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#elif defined( USE_COLOR )\n\tvarying vec3 vColor;\n#endif"; - - var color_pars_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvarying vec3 vColor;\n#endif"; - - var color_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvColor = vec4( 1.0 );\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor *= color;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif"; - - var common = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement( a ) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract( sin( sn ) * c );\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef USE_CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}"; - - var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_maxMipLevel 8.0\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_maxTileSize 256.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tfloat texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 );\n\t\tvec2 f = fract( uv );\n\t\tuv += 0.5 - f;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tif ( mipInt < cubeUV_maxMipLevel ) {\n\t\t\tuv.y += 2.0 * cubeUV_maxTileSize;\n\t\t}\n\t\tuv.y += filterInt * 2.0 * cubeUV_minTileSize;\n\t\tuv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize );\n\t\tuv *= texelSize;\n\t\tvec3 tl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x += texelSize;\n\t\tvec3 tr = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.y += texelSize;\n\t\tvec3 br = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x -= texelSize;\n\t\tvec3 bl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tvec3 tm = mix( tl, tr, f.x );\n\t\tvec3 bm = mix( bl, br, f.x );\n\t\treturn mix( tm, bm, f.y );\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif"; - - var defaultnormal_vertex = "vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif"; - - var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif"; - - var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias );\n#endif"; - - var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; - - var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif"; - - var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; - - var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = clamp( floor( D ) / 255.0, 0.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = cLogLuvM * value.rgb;\n\tXp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract( Le );\n\tvResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}"; - - var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif"; - - var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; - - var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif"; - - var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif"; - - var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif"; - - var fog_vertex = "#ifdef USE_FOG\n\tvFogDepth = - mvPosition.z;\n#endif"; - - var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float vFogDepth;\n#endif"; - - var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, vFogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; - - var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float vFogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif"; - - var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn texture2D( gradientMap, coord ).rgb;\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}"; - - var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel = texture2D( lightMap, vUv2 );\n\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tlightMapIrradiance *= PI;\n\t#endif\n\treflectedLight.indirectDiffuse += lightMapIrradiance;\n#endif"; - - var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif"; - - var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointLightInfo( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( - dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotLightInfo( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( - dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalLightInfo( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( - dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif"; - - var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\treturn irradiance;\n}\nfloat getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\t#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\tif ( cutoffDistance > 0.0 ) {\n\t\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\t}\n\t\treturn distanceFalloff;\n\t#else\n\t\tif ( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\t\treturn pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t\t}\n\t\treturn 1.0;\n\t#endif\n}\nfloat getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {\n\treturn smoothstep( coneCosine, penumbraCosine, angleCosine );\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tlight.color = directionalLight.color;\n\t\tlight.direction = directionalLight.direction;\n\t\tlight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tlight.color = pointLight.color;\n\t\tlight.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay );\n\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat angleCos = dot( light.direction, spotLight.direction );\n\t\tfloat spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\tif ( spotAttenuation > 0.0 ) {\n\t\t\tfloat lightDistance = length( lVector );\n\t\t\tlight.color = spotLight.color * spotAttenuation;\n\t\t\tlight.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\t\t} else {\n\t\t\tlight.color = vec3( 0.0 );\n\t\t\tlight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\treturn irradiance;\n\t}\n#endif"; - - var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\n\tvec3 getIBLIrradiance( const in GeometricContext geometry ) {\n\t\t#if defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n\tvec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) {\n\t\t#if defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 reflectVec;\n\t\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\t\treflectVec = reflect( - viewDir, normal );\n\t\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t\t#else\n\t\t\t\treflectVec = refract( - viewDir, normal, refractionRatio );\n\t\t\t#endif\n\t\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t\treturn envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n#endif"; - - var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;"; - - var lights_toon_pars_fragment = "varying vec3 vViewPosition;\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)"; - - var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;"; - - var lights_phong_pars_fragment = "varying vec3 vViewPosition;\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)"; - - var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness;\nmaterial.roughness = min( material.roughness, 1.0 );\n#ifdef IOR\n\t#ifdef SPECULAR\n\t\tfloat specularIntensityFactor = specularIntensity;\n\t\tvec3 specularTintFactor = specularTint;\n\t\t#ifdef USE_SPECULARINTENSITYMAP\n\t\t\tspecularIntensityFactor *= texture2D( specularIntensityMap, vUv ).a;\n\t\t#endif\n\t\t#ifdef USE_SPECULARTINTMAP\n\t\t\tspecularTintFactor *= specularTintMapTexelToLinear( texture2D( specularTintMap, vUv ) ).rgb;\n\t\t#endif\n\t\tmaterial.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor );\n\t#else\n\t\tfloat specularIntensityFactor = 1.0;\n\t\tvec3 specularTintFactor = vec3( 1.0 );\n\t\tmaterial.specularF90 = 1.0;\n\t#endif\n\tmaterial.specularColor = mix( min( pow2( ( ior - 1.0 ) / ( ior + 1.0 ) ) * specularTintFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.specularF90 = 1.0;\n#endif\n#ifdef USE_CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\tmaterial.clearcoatF0 = vec3( 0.04 );\n\tmaterial.clearcoatF90 = 1.0;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenTint = sheenTint;\n#endif"; - - var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat roughness;\n\tvec3 specularColor;\n\tfloat specularF90;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat clearcoat;\n\t\tfloat clearcoatRoughness;\n\t\tvec3 clearcoatF0;\n\t\tfloat clearcoatF90;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tvec3 sheenTint;\n\t#endif\n};\nvec3 clearcoatSpecular = vec3( 0.0 );\nvec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw;\n\treturn fab;\n}\nvec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) {\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\treturn specularColor * fab.x + specularF90 * fab.y;\n}\nvoid computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\tvec3 FssEss = specularColor * fab.x + specularF90 * fab.y;\n\tfloat Ess = fab.x + fab.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.roughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = dotNLcc * directLight.color;\n\t\tclearcoatSpecular += ccIrradiance * BRDF_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += irradiance * BRDF_Sheen( material.roughness, directLight.direction, geometry, material.sheenTint );\n\t#else\n\t\treflectedLight.directSpecular += irradiance * BRDF_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness );\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\t#endif\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tcomputeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; - - var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef USE_CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointLightInfo( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotLightInfo( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalLightInfo( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; - - var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel = texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getIBLIrradiance( geometry );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness );\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness );\n\t#endif\n#endif"; - - var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif"; - - var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif"; - - var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n\tvarying float vIsPerspective;\n#endif"; - - var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t\tvarying float vIsPerspective;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif"; - - var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif"; - - var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif"; - - var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif"; - - var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif"; - - var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tuniform mat3 uvTransform;\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; - - var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif"; - - var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; - - var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n#endif"; - - var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifndef USE_MORPHNORMALS\n\t\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\t\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; - - var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t#endif\n#endif"; - - var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;\n#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * faceDirection;\n\t\t\tbitangent = bitangent * faceDirection;\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;"; - - var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection );\n#endif"; - - var normal_pars_fragment = "#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif"; - - var normal_pars_vertex = "#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif"; - - var normal_vertex = "#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif"; - - var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tvec3 N = surf_norm;\n\t\tvec3 q1perp = cross( q1, N );\n\t\tvec3 q0perp = cross( N, q0 );\n\t\tvec3 T = q1perp * st0.x + q0perp * st1.x;\n\t\tvec3 B = q1perp * st0.y + q0perp * st1.y;\n\t\tfloat det = max( dot( T, T ), dot( B, B ) );\n\t\tfloat scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det );\n\t\treturn normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z );\n\t}\n#endif"; - - var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; - - var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection );\n\t#endif\n#endif"; - - var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif"; - - var output_fragment = "#ifdef OPAQUE\ndiffuseColor.a = 1.0;\n#endif\n#ifdef USE_TRANSMISSION\ndiffuseColor.a *= transmissionAlpha + 0.1;\n#endif\ngl_FragColor = vec4( outgoingLight, diffuseColor.a );"; - - var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) );\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w );\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}"; - - var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; - - var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 );\n#ifdef USE_INSTANCING\n\tmvPosition = instanceMatrix * mvPosition;\n#endif\nmvPosition = modelViewMatrix * mvPosition;\ngl_Position = projectionMatrix * mvPosition;"; - - var dithering_fragment = "#ifdef DITHERING\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif"; - - var dithering_pars_fragment = "#ifdef DITHERING\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif"; - - var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif"; - - var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; - - var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\tfloat hard_shadow = step( compare , distribution.x );\n\t\tif (hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif"; - - var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif"; - - var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0\n\t\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\tvec4 shadowWorldPosition;\n\t#endif\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n#endif"; - - var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}"; - - var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; - - var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform highp sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif"; - - var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif"; - - var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif"; - - var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif"; - - var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif"; - - var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif"; - - var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; - - var transmission_fragment = "#ifdef USE_TRANSMISSION\n\tfloat transmissionAlpha = 1.0;\n\tfloat transmissionFactor = transmission;\n\tfloat thicknessFactor = thickness;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\ttransmissionFactor *= texture2D( transmissionMap, vUv ).r;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tthicknessFactor *= texture2D( thicknessMap, vUv ).g;\n\t#endif\n\tvec3 pos = vWorldPosition;\n\tvec3 v = normalize( cameraPosition - pos );\n\tvec3 n = inverseTransformDirection( normal, viewMatrix );\n\tvec4 transmission = getIBLVolumeRefraction(\n\t\tn, v, roughnessFactor, material.diffuseColor, material.specularColor, material.specularF90,\n\t\tpos, modelMatrix, viewMatrix, projectionMatrix, ior, thicknessFactor,\n\t\tattenuationTint, attenuationDistance );\n\ttotalDiffuse = mix( totalDiffuse, transmission.rgb, transmissionFactor );\n\ttransmissionAlpha = transmission.a;\n#endif"; - - var transmission_pars_fragment = "#ifdef USE_TRANSMISSION\n\tuniform float transmission;\n\tuniform float thickness;\n\tuniform float attenuationDistance;\n\tuniform vec3 attenuationTint;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\tuniform sampler2D transmissionMap;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tuniform sampler2D thicknessMap;\n\t#endif\n\tuniform vec2 transmissionSamplerSize;\n\tuniform sampler2D transmissionSamplerMap;\n\tuniform mat4 modelMatrix;\n\tuniform mat4 projectionMatrix;\n\tvarying vec3 vWorldPosition;\n\tvec3 getVolumeTransmissionRay( vec3 n, vec3 v, float thickness, float ior, mat4 modelMatrix ) {\n\t\tvec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior );\n\t\tvec3 modelScale;\n\t\tmodelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) );\n\t\tmodelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) );\n\t\tmodelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) );\n\t\treturn normalize( refractionVector ) * thickness * modelScale;\n\t}\n\tfloat applyIorToRoughness( float roughness, float ior ) {\n\t\treturn roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 );\n\t}\n\tvec4 getTransmissionSample( vec2 fragCoord, float roughness, float ior ) {\n\t\tfloat framebufferLod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior );\n\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\treturn texture2DLodEXT( transmissionSamplerMap, fragCoord.xy, framebufferLod );\n\t\t#else\n\t\t\treturn texture2D( transmissionSamplerMap, fragCoord.xy, framebufferLod );\n\t\t#endif\n\t}\n\tvec3 applyVolumeAttenuation( vec3 radiance, float transmissionDistance, vec3 attenuationColor, float attenuationDistance ) {\n\t\tif ( attenuationDistance == 0.0 ) {\n\t\t\treturn radiance;\n\t\t} else {\n\t\t\tvec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance;\n\t\t\tvec3 transmittance = exp( - attenuationCoefficient * transmissionDistance );\t\t\treturn transmittance * radiance;\n\t\t}\n\t}\n\tvec4 getIBLVolumeRefraction( vec3 n, vec3 v, float roughness, vec3 diffuseColor, vec3 specularColor, float specularF90,\n\t\tvec3 position, mat4 modelMatrix, mat4 viewMatrix, mat4 projMatrix, float ior, float thickness,\n\t\tvec3 attenuationColor, float attenuationDistance ) {\n\t\tvec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix );\n\t\tvec3 refractedRayExit = position + transmissionRay;\n\t\tvec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );\n\t\tvec2 refractionCoords = ndcPos.xy / ndcPos.w;\n\t\trefractionCoords += 1.0;\n\t\trefractionCoords /= 2.0;\n\t\tvec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior );\n\t\tvec3 attenuatedColor = applyVolumeAttenuation( transmittedLight.rgb, length( transmissionRay ), attenuationColor, attenuationDistance );\n\t\tvec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness );\n\t\treturn vec4( ( 1.0 - F ) * attenuatedColor * diffuseColor, transmittedLight.a );\n\t}\n#endif"; - - var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )\n\tvarying vec2 vUv;\n#endif"; - - var uv_pars_vertex = "#ifdef USE_UV\n\t#ifdef UVS_VERTEX_ONLY\n\t\tvec2 vUv;\n\t#else\n\t\tvarying vec2 vUv;\n\t#endif\n\tuniform mat3 uvTransform;\n#endif"; - - var uv_vertex = "#ifdef USE_UV\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif"; - - var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif"; - - var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n\tuniform mat3 uv2Transform;\n#endif"; - - var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n#endif"; - - var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; - - var background_frag = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; - - var background_vert = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; - - var cube_frag = "#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; - - var cube_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; - - var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; - - var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}"; - - var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; - - var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; - - var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; - - var equirect_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; - - var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )\n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshmatcap_frag = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshmatcap_vert = "#define MATCAP\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; - - var meshnormal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}"; - - var meshnormal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; - - var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshphysical_frag = "#define STANDARD\n#ifdef PHYSICAL\n\t#define IOR\n\t#define SPECULAR\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef IOR\n\tuniform float ior;\n#endif\n#ifdef SPECULAR\n\tuniform float specularIntensity;\n\tuniform vec3 specularTint;\n\t#ifdef USE_SPECULARINTENSITYMAP\n\t\tuniform sampler2D specularIntensityMap;\n\t#endif\n\t#ifdef USE_SPECULARTINTMAP\n\t\tuniform sampler2D specularTintMap;\n\t#endif\n#endif\n#ifdef USE_CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheenTint;\n#endif\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;\n\tvec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;\n\t#include \n\tvec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\tvec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );\n\t\toutgoingLight = outgoingLight * ( 1.0 - clearcoat * Fcc ) + clearcoatSpecular * clearcoat;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshphysical_vert = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifdef USE_TRANSMISSION\n\tvarying vec3 vWorldPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n#ifdef USE_TRANSMISSION\n\tvWorldPosition = worldPosition.xyz;\n#endif\n}"; - - var meshtoon_frag = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshtoon_vert = "#define TOON\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; - - var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var points_vert = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}"; - - var shadow_vert = "#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var sprite_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var sprite_vert = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; - - const ShaderChunk = { - alphamap_fragment: alphamap_fragment, - alphamap_pars_fragment: alphamap_pars_fragment, - alphatest_fragment: alphatest_fragment, - alphatest_pars_fragment: alphatest_pars_fragment, - aomap_fragment: aomap_fragment, - aomap_pars_fragment: aomap_pars_fragment, - begin_vertex: begin_vertex, - beginnormal_vertex: beginnormal_vertex, - bsdfs: bsdfs, - bumpmap_pars_fragment: bumpmap_pars_fragment, - clipping_planes_fragment: clipping_planes_fragment, - clipping_planes_pars_fragment: clipping_planes_pars_fragment, - clipping_planes_pars_vertex: clipping_planes_pars_vertex, - clipping_planes_vertex: clipping_planes_vertex, - color_fragment: color_fragment, - color_pars_fragment: color_pars_fragment, - color_pars_vertex: color_pars_vertex, - color_vertex: color_vertex, - common: common, - cube_uv_reflection_fragment: cube_uv_reflection_fragment, - defaultnormal_vertex: defaultnormal_vertex, - displacementmap_pars_vertex: displacementmap_pars_vertex, - displacementmap_vertex: displacementmap_vertex, - emissivemap_fragment: emissivemap_fragment, - emissivemap_pars_fragment: emissivemap_pars_fragment, - encodings_fragment: encodings_fragment, - encodings_pars_fragment: encodings_pars_fragment, - envmap_fragment: envmap_fragment, - envmap_common_pars_fragment: envmap_common_pars_fragment, - envmap_pars_fragment: envmap_pars_fragment, - envmap_pars_vertex: envmap_pars_vertex, - envmap_physical_pars_fragment: envmap_physical_pars_fragment, - envmap_vertex: envmap_vertex, - fog_vertex: fog_vertex, - fog_pars_vertex: fog_pars_vertex, - fog_fragment: fog_fragment, - fog_pars_fragment: fog_pars_fragment, - gradientmap_pars_fragment: gradientmap_pars_fragment, - lightmap_fragment: lightmap_fragment, - lightmap_pars_fragment: lightmap_pars_fragment, - lights_lambert_vertex: lights_lambert_vertex, - lights_pars_begin: lights_pars_begin, - lights_toon_fragment: lights_toon_fragment, - lights_toon_pars_fragment: lights_toon_pars_fragment, - lights_phong_fragment: lights_phong_fragment, - lights_phong_pars_fragment: lights_phong_pars_fragment, - lights_physical_fragment: lights_physical_fragment, - lights_physical_pars_fragment: lights_physical_pars_fragment, - lights_fragment_begin: lights_fragment_begin, - lights_fragment_maps: lights_fragment_maps, - lights_fragment_end: lights_fragment_end, - logdepthbuf_fragment: logdepthbuf_fragment, - logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, - logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, - logdepthbuf_vertex: logdepthbuf_vertex, - map_fragment: map_fragment, - map_pars_fragment: map_pars_fragment, - map_particle_fragment: map_particle_fragment, - map_particle_pars_fragment: map_particle_pars_fragment, - metalnessmap_fragment: metalnessmap_fragment, - metalnessmap_pars_fragment: metalnessmap_pars_fragment, - morphnormal_vertex: morphnormal_vertex, - morphtarget_pars_vertex: morphtarget_pars_vertex, - morphtarget_vertex: morphtarget_vertex, - normal_fragment_begin: normal_fragment_begin, - normal_fragment_maps: normal_fragment_maps, - normal_pars_fragment: normal_pars_fragment, - normal_pars_vertex: normal_pars_vertex, - normal_vertex: normal_vertex, - normalmap_pars_fragment: normalmap_pars_fragment, - clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, - clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, - clearcoat_pars_fragment: clearcoat_pars_fragment, - output_fragment: output_fragment, - packing: packing, - premultiplied_alpha_fragment: premultiplied_alpha_fragment, - project_vertex: project_vertex, - dithering_fragment: dithering_fragment, - dithering_pars_fragment: dithering_pars_fragment, - roughnessmap_fragment: roughnessmap_fragment, - roughnessmap_pars_fragment: roughnessmap_pars_fragment, - shadowmap_pars_fragment: shadowmap_pars_fragment, - shadowmap_pars_vertex: shadowmap_pars_vertex, - shadowmap_vertex: shadowmap_vertex, - shadowmask_pars_fragment: shadowmask_pars_fragment, - skinbase_vertex: skinbase_vertex, - skinning_pars_vertex: skinning_pars_vertex, - skinning_vertex: skinning_vertex, - skinnormal_vertex: skinnormal_vertex, - specularmap_fragment: specularmap_fragment, - specularmap_pars_fragment: specularmap_pars_fragment, - tonemapping_fragment: tonemapping_fragment, - tonemapping_pars_fragment: tonemapping_pars_fragment, - transmission_fragment: transmission_fragment, - transmission_pars_fragment: transmission_pars_fragment, - uv_pars_fragment: uv_pars_fragment, - uv_pars_vertex: uv_pars_vertex, - uv_vertex: uv_vertex, - uv2_pars_fragment: uv2_pars_fragment, - uv2_pars_vertex: uv2_pars_vertex, - uv2_vertex: uv2_vertex, - worldpos_vertex: worldpos_vertex, - - background_frag: background_frag, - background_vert: background_vert, - cube_frag: cube_frag, - cube_vert: cube_vert, - depth_frag: depth_frag, - depth_vert: depth_vert, - distanceRGBA_frag: distanceRGBA_frag, - distanceRGBA_vert: distanceRGBA_vert, - equirect_frag: equirect_frag, - equirect_vert: equirect_vert, - linedashed_frag: linedashed_frag, - linedashed_vert: linedashed_vert, - meshbasic_frag: meshbasic_frag, - meshbasic_vert: meshbasic_vert, - meshlambert_frag: meshlambert_frag, - meshlambert_vert: meshlambert_vert, - meshmatcap_frag: meshmatcap_frag, - meshmatcap_vert: meshmatcap_vert, - meshnormal_frag: meshnormal_frag, - meshnormal_vert: meshnormal_vert, - meshphong_frag: meshphong_frag, - meshphong_vert: meshphong_vert, - meshphysical_frag: meshphysical_frag, - meshphysical_vert: meshphysical_vert, - meshtoon_frag: meshtoon_frag, - meshtoon_vert: meshtoon_vert, - points_frag: points_frag, - points_vert: points_vert, - shadow_frag: shadow_frag, - shadow_vert: shadow_vert, - sprite_frag: sprite_frag, - sprite_vert: sprite_vert - }; - - /** - * Uniforms library for shared webgl shaders - */ - - const UniformsLib = { - - common: { - - diffuse: { value: new Color( 0xffffff ) }, - opacity: { value: 1.0 }, - - map: { value: null }, - uvTransform: { value: new Matrix3() }, - uv2Transform: { value: new Matrix3() }, - - alphaMap: { value: null }, - alphaTest: { value: 0 } - - }, - - specularmap: { - - specularMap: { value: null }, - - }, - - envmap: { - - envMap: { value: null }, - flipEnvMap: { value: - 1 }, - reflectivity: { value: 1.0 }, // basic, lambert, phong - ior: { value: 1.5 }, // standard, physical - refractionRatio: { value: 0.98 }, - maxMipLevel: { value: 0 } - - }, - - aomap: { - - aoMap: { value: null }, - aoMapIntensity: { value: 1 } - - }, - - lightmap: { - - lightMap: { value: null }, - lightMapIntensity: { value: 1 } - - }, - - emissivemap: { - - emissiveMap: { value: null } - - }, - - bumpmap: { - - bumpMap: { value: null }, - bumpScale: { value: 1 } - - }, - - normalmap: { - - normalMap: { value: null }, - normalScale: { value: new Vector2( 1, 1 ) } - - }, - - displacementmap: { - - displacementMap: { value: null }, - displacementScale: { value: 1 }, - displacementBias: { value: 0 } - - }, - - roughnessmap: { - - roughnessMap: { value: null } - - }, - - metalnessmap: { - - metalnessMap: { value: null } - - }, - - gradientmap: { - - gradientMap: { value: null } - - }, - - fog: { - - fogDensity: { value: 0.00025 }, - fogNear: { value: 1 }, - fogFar: { value: 2000 }, - fogColor: { value: new Color( 0xffffff ) } - - }, - - lights: { - - ambientLightColor: { value: [] }, - - lightProbe: { value: [] }, - - directionalLights: { value: [], properties: { - direction: {}, - color: {} - } }, - - directionalLightShadows: { value: [], properties: { - shadowBias: {}, - shadowNormalBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - directionalShadowMap: { value: [] }, - directionalShadowMatrix: { value: [] }, - - spotLights: { value: [], properties: { - color: {}, - position: {}, - direction: {}, - distance: {}, - coneCos: {}, - penumbraCos: {}, - decay: {} - } }, - - spotLightShadows: { value: [], properties: { - shadowBias: {}, - shadowNormalBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - spotShadowMap: { value: [] }, - spotShadowMatrix: { value: [] }, - - pointLights: { value: [], properties: { - color: {}, - position: {}, - decay: {}, - distance: {} - } }, - - pointLightShadows: { value: [], properties: { - shadowBias: {}, - shadowNormalBias: {}, - shadowRadius: {}, - shadowMapSize: {}, - shadowCameraNear: {}, - shadowCameraFar: {} - } }, - - pointShadowMap: { value: [] }, - pointShadowMatrix: { value: [] }, - - hemisphereLights: { value: [], properties: { - direction: {}, - skyColor: {}, - groundColor: {} - } }, - - // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src - rectAreaLights: { value: [], properties: { - color: {}, - position: {}, - width: {}, - height: {} - } }, - - ltc_1: { value: null }, - ltc_2: { value: null } - - }, - - points: { - - diffuse: { value: new Color( 0xffffff ) }, - opacity: { value: 1.0 }, - size: { value: 1.0 }, - scale: { value: 1.0 }, - map: { value: null }, - alphaMap: { value: null }, - alphaTest: { value: 0 }, - uvTransform: { value: new Matrix3() } - - }, - - sprite: { - - diffuse: { value: new Color( 0xffffff ) }, - opacity: { value: 1.0 }, - center: { value: new Vector2( 0.5, 0.5 ) }, - rotation: { value: 0.0 }, - map: { value: null }, - alphaMap: { value: null }, - alphaTest: { value: 0 }, - uvTransform: { value: new Matrix3() } - - } - - }; - - const ShaderLib = { - - basic: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.meshbasic_vert, - fragmentShader: ShaderChunk.meshbasic_frag - - }, - - lambert: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) } - } - ] ), - - vertexShader: ShaderChunk.meshlambert_vert, - fragmentShader: ShaderChunk.meshlambert_frag - - }, - - phong: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - specular: { value: new Color( 0x111111 ) }, - shininess: { value: 30 } - } - ] ), - - vertexShader: ShaderChunk.meshphong_vert, - fragmentShader: ShaderChunk.meshphong_frag - - }, - - standard: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.roughnessmap, - UniformsLib.metalnessmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - roughness: { value: 1.0 }, - metalness: { value: 0.0 }, - envMapIntensity: { value: 1 } // temporary - } - ] ), - - vertexShader: ShaderChunk.meshphysical_vert, - fragmentShader: ShaderChunk.meshphysical_frag - - }, - - toon: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.gradientmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) } - } - ] ), - - vertexShader: ShaderChunk.meshtoon_vert, - fragmentShader: ShaderChunk.meshtoon_frag - - }, - - matcap: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.fog, - { - matcap: { value: null } - } - ] ), - - vertexShader: ShaderChunk.meshmatcap_vert, - fragmentShader: ShaderChunk.meshmatcap_frag - - }, - - points: { - - uniforms: mergeUniforms( [ - UniformsLib.points, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.points_vert, - fragmentShader: ShaderChunk.points_frag - - }, - - dashed: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.fog, - { - scale: { value: 1 }, - dashSize: { value: 1 }, - totalSize: { value: 2 } - } - ] ), - - vertexShader: ShaderChunk.linedashed_vert, - fragmentShader: ShaderChunk.linedashed_frag - - }, - - depth: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.displacementmap - ] ), - - vertexShader: ShaderChunk.depth_vert, - fragmentShader: ShaderChunk.depth_frag - - }, - - normal: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - { - opacity: { value: 1.0 } - } - ] ), - - vertexShader: ShaderChunk.meshnormal_vert, - fragmentShader: ShaderChunk.meshnormal_frag - - }, - - sprite: { - - uniforms: mergeUniforms( [ - UniformsLib.sprite, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.sprite_vert, - fragmentShader: ShaderChunk.sprite_frag - - }, - - background: { - - uniforms: { - uvTransform: { value: new Matrix3() }, - t2D: { value: null }, - }, - - vertexShader: ShaderChunk.background_vert, - fragmentShader: ShaderChunk.background_frag - - }, - /* ------------------------------------------------------------------------- - // Cube map shader - ------------------------------------------------------------------------- */ - - cube: { - - uniforms: mergeUniforms( [ - UniformsLib.envmap, - { - opacity: { value: 1.0 } - } - ] ), - - vertexShader: ShaderChunk.cube_vert, - fragmentShader: ShaderChunk.cube_frag - - }, - - equirect: { - - uniforms: { - tEquirect: { value: null }, - }, - - vertexShader: ShaderChunk.equirect_vert, - fragmentShader: ShaderChunk.equirect_frag - - }, - - distanceRGBA: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.displacementmap, - { - referencePosition: { value: new Vector3() }, - nearDistance: { value: 1 }, - farDistance: { value: 1000 } - } - ] ), - - vertexShader: ShaderChunk.distanceRGBA_vert, - fragmentShader: ShaderChunk.distanceRGBA_frag - - }, - - shadow: { - - uniforms: mergeUniforms( [ - UniformsLib.lights, - UniformsLib.fog, - { - color: { value: new Color( 0x00000 ) }, - opacity: { value: 1.0 } - }, - ] ), - - vertexShader: ShaderChunk.shadow_vert, - fragmentShader: ShaderChunk.shadow_frag - - } - - }; - - ShaderLib.physical = { - - uniforms: mergeUniforms( [ - ShaderLib.standard.uniforms, - { - clearcoat: { value: 0 }, - clearcoatMap: { value: null }, - clearcoatRoughness: { value: 0 }, - clearcoatRoughnessMap: { value: null }, - clearcoatNormalScale: { value: new Vector2( 1, 1 ) }, - clearcoatNormalMap: { value: null }, - sheenTint: { value: new Color( 0x000000 ) }, - transmission: { value: 0 }, - transmissionMap: { value: null }, - transmissionSamplerSize: { value: new Vector2() }, - transmissionSamplerMap: { value: null }, - thickness: { value: 0 }, - thicknessMap: { value: null }, - attenuationDistance: { value: 0 }, - attenuationTint: { value: new Color( 0x000000 ) }, - specularIntensity: { value: 0 }, - specularIntensityMap: { value: null }, - specularTint: { value: new Color( 1, 1, 1 ) }, - specularTintMap: { value: null }, - } - ] ), - - vertexShader: ShaderChunk.meshphysical_vert, - fragmentShader: ShaderChunk.meshphysical_frag - - }; - - function WebGLBackground( renderer, cubemaps, state, objects, premultipliedAlpha ) { - - const clearColor = new Color( 0x000000 ); - let clearAlpha = 0; - - let planeMesh; - let boxMesh; - - let currentBackground = null; - let currentBackgroundVersion = 0; - let currentTonemapping = null; - - function render( renderList, scene ) { - - let forceClear = false; - let background = scene.isScene === true ? scene.background : null; - - if ( background && background.isTexture ) { - - background = cubemaps.get( background ); - - } - - // Ignore background in AR - // TODO: Reconsider this. - - const xr = renderer.xr; - const session = xr.getSession && xr.getSession(); - - if ( session && session.environmentBlendMode === 'additive' ) { - - background = null; - - } - - if ( background === null ) { - - setClear( clearColor, clearAlpha ); - - } else if ( background && background.isColor ) { - - setClear( background, 1 ); - forceClear = true; - - } - - if ( renderer.autoClear || forceClear ) { - - renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); - - } - - if ( background && ( background.isCubeTexture || background.mapping === CubeUVReflectionMapping ) ) { - - if ( boxMesh === undefined ) { - - boxMesh = new Mesh( - new BoxGeometry( 1, 1, 1 ), - new ShaderMaterial( { - name: 'BackgroundCubeMaterial', - uniforms: cloneUniforms( ShaderLib.cube.uniforms ), - vertexShader: ShaderLib.cube.vertexShader, - fragmentShader: ShaderLib.cube.fragmentShader, - side: BackSide, - depthTest: false, - depthWrite: false, - fog: false - } ) - ); - - boxMesh.geometry.deleteAttribute( 'normal' ); - boxMesh.geometry.deleteAttribute( 'uv' ); - - boxMesh.onBeforeRender = function ( renderer, scene, camera ) { - - this.matrixWorld.copyPosition( camera.matrixWorld ); - - }; - - // enable code injection for non-built-in material - Object.defineProperty( boxMesh.material, 'envMap', { - - get: function () { - - return this.uniforms.envMap.value; - - } - - } ); - - objects.update( boxMesh ); - - } - - boxMesh.material.uniforms.envMap.value = background; - boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1; - - if ( currentBackground !== background || - currentBackgroundVersion !== background.version || - currentTonemapping !== renderer.toneMapping ) { - - boxMesh.material.needsUpdate = true; - - currentBackground = background; - currentBackgroundVersion = background.version; - currentTonemapping = renderer.toneMapping; - - } - - // push to the pre-sorted opaque render list - renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); - - } else if ( background && background.isTexture ) { - - if ( planeMesh === undefined ) { - - planeMesh = new Mesh( - new PlaneGeometry( 2, 2 ), - new ShaderMaterial( { - name: 'BackgroundMaterial', - uniforms: cloneUniforms( ShaderLib.background.uniforms ), - vertexShader: ShaderLib.background.vertexShader, - fragmentShader: ShaderLib.background.fragmentShader, - side: FrontSide, - depthTest: false, - depthWrite: false, - fog: false - } ) - ); - - planeMesh.geometry.deleteAttribute( 'normal' ); - - // enable code injection for non-built-in material - Object.defineProperty( planeMesh.material, 'map', { - - get: function () { - - return this.uniforms.t2D.value; - - } - - } ); - - objects.update( planeMesh ); - - } - - planeMesh.material.uniforms.t2D.value = background; - - if ( background.matrixAutoUpdate === true ) { - - background.updateMatrix(); - - } - - planeMesh.material.uniforms.uvTransform.value.copy( background.matrix ); - - if ( currentBackground !== background || - currentBackgroundVersion !== background.version || - currentTonemapping !== renderer.toneMapping ) { - - planeMesh.material.needsUpdate = true; - - currentBackground = background; - currentBackgroundVersion = background.version; - currentTonemapping = renderer.toneMapping; - - } - - - // push to the pre-sorted opaque render list - renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null ); - - } - - } - - function setClear( color, alpha ) { - - state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha ); - - } - - return { - - getClearColor: function () { - - return clearColor; - - }, - setClearColor: function ( color, alpha = 1 ) { - - clearColor.set( color ); - clearAlpha = alpha; - setClear( clearColor, clearAlpha ); - - }, - getClearAlpha: function () { - - return clearAlpha; - - }, - setClearAlpha: function ( alpha ) { - - clearAlpha = alpha; - setClear( clearColor, clearAlpha ); - - }, - render: render - - }; - - } - - function WebGLBindingStates( gl, extensions, attributes, capabilities ) { - - const maxVertexAttributes = gl.getParameter( 34921 ); - - const extension = capabilities.isWebGL2 ? null : extensions.get( 'OES_vertex_array_object' ); - const vaoAvailable = capabilities.isWebGL2 || extension !== null; - - const bindingStates = {}; - - const defaultState = createBindingState( null ); - let currentState = defaultState; - - function setup( object, material, program, geometry, index ) { - - let updateBuffers = false; - - if ( vaoAvailable ) { - - const state = getBindingState( geometry, program, material ); - - if ( currentState !== state ) { - - currentState = state; - bindVertexArrayObject( currentState.object ); - - } - - updateBuffers = needsUpdate( geometry, index ); - - if ( updateBuffers ) saveCache( geometry, index ); - - } else { - - const wireframe = ( material.wireframe === true ); - - if ( currentState.geometry !== geometry.id || - currentState.program !== program.id || - currentState.wireframe !== wireframe ) { - - currentState.geometry = geometry.id; - currentState.program = program.id; - currentState.wireframe = wireframe; - - updateBuffers = true; - - } - - } - - if ( object.isInstancedMesh === true ) { - - updateBuffers = true; - - } - - if ( index !== null ) { - - attributes.update( index, 34963 ); - - } - - if ( updateBuffers ) { - - setupVertexAttributes( object, material, program, geometry ); - - if ( index !== null ) { - - gl.bindBuffer( 34963, attributes.get( index ).buffer ); - - } - - } - - } - - function createVertexArrayObject() { - - if ( capabilities.isWebGL2 ) return gl.createVertexArray(); - - return extension.createVertexArrayOES(); - - } - - function bindVertexArrayObject( vao ) { - - if ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao ); - - return extension.bindVertexArrayOES( vao ); - - } - - function deleteVertexArrayObject( vao ) { - - if ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao ); - - return extension.deleteVertexArrayOES( vao ); - - } - - function getBindingState( geometry, program, material ) { - - const wireframe = ( material.wireframe === true ); - - let programMap = bindingStates[ geometry.id ]; - - if ( programMap === undefined ) { - - programMap = {}; - bindingStates[ geometry.id ] = programMap; - - } - - let stateMap = programMap[ program.id ]; - - if ( stateMap === undefined ) { - - stateMap = {}; - programMap[ program.id ] = stateMap; - - } - - let state = stateMap[ wireframe ]; - - if ( state === undefined ) { - - state = createBindingState( createVertexArrayObject() ); - stateMap[ wireframe ] = state; - - } - - return state; - - } - - function createBindingState( vao ) { - - const newAttributes = []; - const enabledAttributes = []; - const attributeDivisors = []; - - for ( let i = 0; i < maxVertexAttributes; i ++ ) { - - newAttributes[ i ] = 0; - enabledAttributes[ i ] = 0; - attributeDivisors[ i ] = 0; - - } - - return { - - // for backward compatibility on non-VAO support browser - geometry: null, - program: null, - wireframe: false, - - newAttributes: newAttributes, - enabledAttributes: enabledAttributes, - attributeDivisors: attributeDivisors, - object: vao, - attributes: {}, - index: null - - }; - - } - - function needsUpdate( geometry, index ) { - - const cachedAttributes = currentState.attributes; - const geometryAttributes = geometry.attributes; - - let attributesNum = 0; - - for ( const key in geometryAttributes ) { - - const cachedAttribute = cachedAttributes[ key ]; - const geometryAttribute = geometryAttributes[ key ]; - - if ( cachedAttribute === undefined ) return true; - - if ( cachedAttribute.attribute !== geometryAttribute ) return true; - - if ( cachedAttribute.data !== geometryAttribute.data ) return true; - - attributesNum ++; - - } - - if ( currentState.attributesNum !== attributesNum ) return true; - - if ( currentState.index !== index ) return true; - - return false; - - } - - function saveCache( geometry, index ) { - - const cache = {}; - const attributes = geometry.attributes; - let attributesNum = 0; - - for ( const key in attributes ) { - - const attribute = attributes[ key ]; - - const data = {}; - data.attribute = attribute; - - if ( attribute.data ) { - - data.data = attribute.data; - - } - - cache[ key ] = data; - - attributesNum ++; - - } - - currentState.attributes = cache; - currentState.attributesNum = attributesNum; - - currentState.index = index; - - } - - function initAttributes() { - - const newAttributes = currentState.newAttributes; - - for ( let i = 0, il = newAttributes.length; i < il; i ++ ) { - - newAttributes[ i ] = 0; - - } - - } - - function enableAttribute( attribute ) { - - enableAttributeAndDivisor( attribute, 0 ); - - } - - function enableAttributeAndDivisor( attribute, meshPerAttribute ) { - - const newAttributes = currentState.newAttributes; - const enabledAttributes = currentState.enabledAttributes; - const attributeDivisors = currentState.attributeDivisors; - - newAttributes[ attribute ] = 1; - - if ( enabledAttributes[ attribute ] === 0 ) { - - gl.enableVertexAttribArray( attribute ); - enabledAttributes[ attribute ] = 1; - - } - - if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { - - const extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' ); - - extension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute ); - attributeDivisors[ attribute ] = meshPerAttribute; - - } - - } - - function disableUnusedAttributes() { - - const newAttributes = currentState.newAttributes; - const enabledAttributes = currentState.enabledAttributes; - - for ( let i = 0, il = enabledAttributes.length; i < il; i ++ ) { - - if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { - - gl.disableVertexAttribArray( i ); - enabledAttributes[ i ] = 0; - - } - - } - - } - - function vertexAttribPointer( index, size, type, normalized, stride, offset ) { - - if ( capabilities.isWebGL2 === true && ( type === 5124 || type === 5125 ) ) { - - gl.vertexAttribIPointer( index, size, type, stride, offset ); - - } else { - - gl.vertexAttribPointer( index, size, type, normalized, stride, offset ); - - } - - } - - function setupVertexAttributes( object, material, program, geometry ) { - - if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) { - - if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) return; - - } - - initAttributes(); - - const geometryAttributes = geometry.attributes; - - const programAttributes = program.getAttributes(); - - const materialDefaultAttributeValues = material.defaultAttributeValues; - - for ( const name in programAttributes ) { - - const programAttribute = programAttributes[ name ]; - - if ( programAttribute.location >= 0 ) { - - let geometryAttribute = geometryAttributes[ name ]; - - if ( geometryAttribute === undefined ) { - - if ( name === 'instanceMatrix' && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; - if ( name === 'instanceColor' && object.instanceColor ) geometryAttribute = object.instanceColor; - - } - - if ( geometryAttribute !== undefined ) { - - const normalized = geometryAttribute.normalized; - const size = geometryAttribute.itemSize; - - const attribute = attributes.get( geometryAttribute ); - - // TODO Attribute may not be available on context restore - - if ( attribute === undefined ) continue; - - const buffer = attribute.buffer; - const type = attribute.type; - const bytesPerElement = attribute.bytesPerElement; - - if ( geometryAttribute.isInterleavedBufferAttribute ) { - - const data = geometryAttribute.data; - const stride = data.stride; - const offset = geometryAttribute.offset; - - if ( data && data.isInstancedInterleavedBuffer ) { - - for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - - enableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute ); - - } - - if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { - - geometry._maxInstanceCount = data.meshPerAttribute * data.count; - - } - - } else { - - for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - - enableAttribute( programAttribute.location + i ); - - } - - } - - gl.bindBuffer( 34962, buffer ); - - for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - - vertexAttribPointer( - programAttribute.location + i, - size / programAttribute.locationSize, - type, - normalized, - stride * bytesPerElement, - ( offset + ( size / programAttribute.locationSize ) * i ) * bytesPerElement - ); - - } - - } else { - - if ( geometryAttribute.isInstancedBufferAttribute ) { - - for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - - enableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute ); - - } - - if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { - - geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; - - } - - } else { - - for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - - enableAttribute( programAttribute.location + i ); - - } - - } - - gl.bindBuffer( 34962, buffer ); - - for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - - vertexAttribPointer( - programAttribute.location + i, - size / programAttribute.locationSize, - type, - normalized, - size * bytesPerElement, - ( size / programAttribute.locationSize ) * i * bytesPerElement - ); - - } - - } - - } else if ( materialDefaultAttributeValues !== undefined ) { - - const value = materialDefaultAttributeValues[ name ]; - - if ( value !== undefined ) { - - switch ( value.length ) { - - case 2: - gl.vertexAttrib2fv( programAttribute.location, value ); - break; - - case 3: - gl.vertexAttrib3fv( programAttribute.location, value ); - break; - - case 4: - gl.vertexAttrib4fv( programAttribute.location, value ); - break; - - default: - gl.vertexAttrib1fv( programAttribute.location, value ); - - } - - } - - } - - } - - } - - disableUnusedAttributes(); - - } - - function dispose() { - - reset(); - - for ( const geometryId in bindingStates ) { - - const programMap = bindingStates[ geometryId ]; - - for ( const programId in programMap ) { - - const stateMap = programMap[ programId ]; - - for ( const wireframe in stateMap ) { - - deleteVertexArrayObject( stateMap[ wireframe ].object ); - - delete stateMap[ wireframe ]; - - } - - delete programMap[ programId ]; - - } - - delete bindingStates[ geometryId ]; - - } - - } - - function releaseStatesOfGeometry( geometry ) { - - if ( bindingStates[ geometry.id ] === undefined ) return; - - const programMap = bindingStates[ geometry.id ]; - - for ( const programId in programMap ) { - - const stateMap = programMap[ programId ]; - - for ( const wireframe in stateMap ) { - - deleteVertexArrayObject( stateMap[ wireframe ].object ); - - delete stateMap[ wireframe ]; - - } - - delete programMap[ programId ]; - - } - - delete bindingStates[ geometry.id ]; - - } - - function releaseStatesOfProgram( program ) { - - for ( const geometryId in bindingStates ) { - - const programMap = bindingStates[ geometryId ]; - - if ( programMap[ program.id ] === undefined ) continue; - - const stateMap = programMap[ program.id ]; - - for ( const wireframe in stateMap ) { - - deleteVertexArrayObject( stateMap[ wireframe ].object ); - - delete stateMap[ wireframe ]; - - } - - delete programMap[ program.id ]; - - } - - } - - function reset() { - - resetDefaultState(); - - if ( currentState === defaultState ) return; - - currentState = defaultState; - bindVertexArrayObject( currentState.object ); - - } - - // for backward-compatilibity - - function resetDefaultState() { - - defaultState.geometry = null; - defaultState.program = null; - defaultState.wireframe = false; - - } - - return { - - setup: setup, - reset: reset, - resetDefaultState: resetDefaultState, - dispose: dispose, - releaseStatesOfGeometry: releaseStatesOfGeometry, - releaseStatesOfProgram: releaseStatesOfProgram, - - initAttributes: initAttributes, - enableAttribute: enableAttribute, - disableUnusedAttributes: disableUnusedAttributes - - }; - - } - - function WebGLBufferRenderer( gl, extensions, info, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - let mode; - - function setMode( value ) { - - mode = value; - - } - - function render( start, count ) { - - gl.drawArrays( mode, start, count ); - - info.update( count, mode, 1 ); - - } - - function renderInstances( start, count, primcount ) { - - if ( primcount === 0 ) return; - - let extension, methodName; - - if ( isWebGL2 ) { - - extension = gl; - methodName = 'drawArraysInstanced'; - - } else { - - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawArraysInstancedANGLE'; - - if ( extension === null ) { - - console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } - - } - - extension[ methodName ]( mode, start, count, primcount ); - - info.update( count, mode, primcount ); - - } - - // - - this.setMode = setMode; - this.render = render; - this.renderInstances = renderInstances; - - } - - function WebGLCapabilities( gl, extensions, parameters ) { - - let maxAnisotropy; - - function getMaxAnisotropy() { - - if ( maxAnisotropy !== undefined ) return maxAnisotropy; - - if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) { - - const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - - maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); - - } else { - - maxAnisotropy = 0; - - } - - return maxAnisotropy; - - } - - function getMaxPrecision( precision ) { - - if ( precision === 'highp' ) { - - if ( gl.getShaderPrecisionFormat( 35633, 36338 ).precision > 0 && - gl.getShaderPrecisionFormat( 35632, 36338 ).precision > 0 ) { - - return 'highp'; - - } - - precision = 'mediump'; - - } - - if ( precision === 'mediump' ) { - - if ( gl.getShaderPrecisionFormat( 35633, 36337 ).precision > 0 && - gl.getShaderPrecisionFormat( 35632, 36337 ).precision > 0 ) { - - return 'mediump'; - - } - - } - - return 'lowp'; - - } - - /* eslint-disable no-undef */ - const isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext ) || - ( typeof WebGL2ComputeRenderingContext !== 'undefined' && gl instanceof WebGL2ComputeRenderingContext ); - /* eslint-enable no-undef */ - - let precision = parameters.precision !== undefined ? parameters.precision : 'highp'; - const maxPrecision = getMaxPrecision( precision ); - - if ( maxPrecision !== precision ) { - - console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' ); - precision = maxPrecision; - - } - - const drawBuffers = isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ); - - const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; - - const maxTextures = gl.getParameter( 34930 ); - const maxVertexTextures = gl.getParameter( 35660 ); - const maxTextureSize = gl.getParameter( 3379 ); - const maxCubemapSize = gl.getParameter( 34076 ); - - const maxAttributes = gl.getParameter( 34921 ); - const maxVertexUniforms = gl.getParameter( 36347 ); - const maxVaryings = gl.getParameter( 36348 ); - const maxFragmentUniforms = gl.getParameter( 36349 ); - - const vertexTextures = maxVertexTextures > 0; - const floatFragmentTextures = isWebGL2 || extensions.has( 'OES_texture_float' ); - const floatVertexTextures = vertexTextures && floatFragmentTextures; - - const maxSamples = isWebGL2 ? gl.getParameter( 36183 ) : 0; - - return { - - isWebGL2: isWebGL2, - - drawBuffers: drawBuffers, - - getMaxAnisotropy: getMaxAnisotropy, - getMaxPrecision: getMaxPrecision, - - precision: precision, - logarithmicDepthBuffer: logarithmicDepthBuffer, - - maxTextures: maxTextures, - maxVertexTextures: maxVertexTextures, - maxTextureSize: maxTextureSize, - maxCubemapSize: maxCubemapSize, - - maxAttributes: maxAttributes, - maxVertexUniforms: maxVertexUniforms, - maxVaryings: maxVaryings, - maxFragmentUniforms: maxFragmentUniforms, - - vertexTextures: vertexTextures, - floatFragmentTextures: floatFragmentTextures, - floatVertexTextures: floatVertexTextures, - - maxSamples: maxSamples - - }; - - } - - function WebGLClipping( properties ) { - - const scope = this; - - let globalState = null, - numGlobalPlanes = 0, - localClippingEnabled = false, - renderingShadows = false; - - const plane = new Plane(), - viewNormalMatrix = new Matrix3(), - - uniform = { value: null, needsUpdate: false }; - - this.uniform = uniform; - this.numPlanes = 0; - this.numIntersection = 0; - - this.init = function ( planes, enableLocalClipping, camera ) { - - const enabled = - planes.length !== 0 || - enableLocalClipping || - // enable state of previous frame - the clipping code has to - // run another frame in order to reset the state: - numGlobalPlanes !== 0 || - localClippingEnabled; - - localClippingEnabled = enableLocalClipping; - - globalState = projectPlanes( planes, camera, 0 ); - numGlobalPlanes = planes.length; - - return enabled; - - }; - - this.beginShadows = function () { - - renderingShadows = true; - projectPlanes( null ); - - }; - - this.endShadows = function () { - - renderingShadows = false; - resetGlobalState(); - - }; - - this.setState = function ( material, camera, useCache ) { - - const planes = material.clippingPlanes, - clipIntersection = material.clipIntersection, - clipShadows = material.clipShadows; - - const materialProperties = properties.get( material ); - - if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { - - // there's no local clipping - - if ( renderingShadows ) { - - // there's no global clipping - - projectPlanes( null ); - - } else { - - resetGlobalState(); - - } - - } else { - - const nGlobal = renderingShadows ? 0 : numGlobalPlanes, - lGlobal = nGlobal * 4; - - let dstArray = materialProperties.clippingState || null; - - uniform.value = dstArray; // ensure unique state - - dstArray = projectPlanes( planes, camera, lGlobal, useCache ); - - for ( let i = 0; i !== lGlobal; ++ i ) { - - dstArray[ i ] = globalState[ i ]; - - } - - materialProperties.clippingState = dstArray; - this.numIntersection = clipIntersection ? this.numPlanes : 0; - this.numPlanes += nGlobal; - - } - - - }; - - function resetGlobalState() { - - if ( uniform.value !== globalState ) { - - uniform.value = globalState; - uniform.needsUpdate = numGlobalPlanes > 0; - - } - - scope.numPlanes = numGlobalPlanes; - scope.numIntersection = 0; - - } - - function projectPlanes( planes, camera, dstOffset, skipTransform ) { - - const nPlanes = planes !== null ? planes.length : 0; - let dstArray = null; - - if ( nPlanes !== 0 ) { - - dstArray = uniform.value; - - if ( skipTransform !== true || dstArray === null ) { - - const flatSize = dstOffset + nPlanes * 4, - viewMatrix = camera.matrixWorldInverse; - - viewNormalMatrix.getNormalMatrix( viewMatrix ); - - if ( dstArray === null || dstArray.length < flatSize ) { - - dstArray = new Float32Array( flatSize ); - - } - - for ( let i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { - - plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); - - plane.normal.toArray( dstArray, i4 ); - dstArray[ i4 + 3 ] = plane.constant; - - } - - } - - uniform.value = dstArray; - uniform.needsUpdate = true; - - } - - scope.numPlanes = nPlanes; - scope.numIntersection = 0; - - return dstArray; - - } - - } - - function WebGLCubeMaps( renderer ) { - - let cubemaps = new WeakMap(); - - function mapTextureMapping( texture, mapping ) { - - if ( mapping === EquirectangularReflectionMapping ) { - - texture.mapping = CubeReflectionMapping; - - } else if ( mapping === EquirectangularRefractionMapping ) { - - texture.mapping = CubeRefractionMapping; - - } - - return texture; - - } - - function get( texture ) { - - if ( texture && texture.isTexture && texture.isRenderTargetTexture === false ) { - - const mapping = texture.mapping; - - if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) { - - if ( cubemaps.has( texture ) ) { - - const cubemap = cubemaps.get( texture ).texture; - return mapTextureMapping( cubemap, texture.mapping ); - - } else { - - const image = texture.image; - - if ( image && image.height > 0 ) { - - const currentRenderTarget = renderer.getRenderTarget(); - - const renderTarget = new WebGLCubeRenderTarget( image.height / 2 ); - renderTarget.fromEquirectangularTexture( renderer, texture ); - cubemaps.set( texture, renderTarget ); - - renderer.setRenderTarget( currentRenderTarget ); - - texture.addEventListener( 'dispose', onTextureDispose ); - - return mapTextureMapping( renderTarget.texture, texture.mapping ); - - } else { - - // image not yet ready. try the conversion next frame - - return null; - - } - - } - - } - - } - - return texture; - - } - - function onTextureDispose( event ) { - - const texture = event.target; - - texture.removeEventListener( 'dispose', onTextureDispose ); - - const cubemap = cubemaps.get( texture ); - - if ( cubemap !== undefined ) { - - cubemaps.delete( texture ); - cubemap.dispose(); - - } - - } - - function dispose() { - - cubemaps = new WeakMap(); - - } - - return { - get: get, - dispose: dispose - }; - - } - - class OrthographicCamera extends Camera { - - constructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) { - - super(); - - this.type = 'OrthographicCamera'; - - this.zoom = 1; - this.view = null; - - this.left = left; - this.right = right; - this.top = top; - this.bottom = bottom; - - this.near = near; - this.far = far; - - this.updateProjectionMatrix(); - - } - - copy( source, recursive ) { - - super.copy( source, recursive ); - - this.left = source.left; - this.right = source.right; - this.top = source.top; - this.bottom = source.bottom; - this.near = source.near; - this.far = source.far; - - this.zoom = source.zoom; - this.view = source.view === null ? null : Object.assign( {}, source.view ); - - return this; - - } - - setViewOffset( fullWidth, fullHeight, x, y, width, height ) { - - if ( this.view === null ) { - - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; - - } - - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; - - this.updateProjectionMatrix(); - - } - - clearViewOffset() { - - if ( this.view !== null ) { - - this.view.enabled = false; - - } - - this.updateProjectionMatrix(); - - } - - updateProjectionMatrix() { - - const dx = ( this.right - this.left ) / ( 2 * this.zoom ); - const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); - const cx = ( this.right + this.left ) / 2; - const cy = ( this.top + this.bottom ) / 2; - - let left = cx - dx; - let right = cx + dx; - let top = cy + dy; - let bottom = cy - dy; - - if ( this.view !== null && this.view.enabled ) { - - const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; - const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; - - left += scaleW * this.view.offsetX; - right = left + scaleW * this.view.width; - top -= scaleH * this.view.offsetY; - bottom = top - scaleH * this.view.height; - - } - - this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); - - this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); - - } - - toJSON( meta ) { - - const data = super.toJSON( meta ); - - data.object.zoom = this.zoom; - data.object.left = this.left; - data.object.right = this.right; - data.object.top = this.top; - data.object.bottom = this.bottom; - data.object.near = this.near; - data.object.far = this.far; - - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - - return data; - - } - - } - - OrthographicCamera.prototype.isOrthographicCamera = true; - - class RawShaderMaterial extends ShaderMaterial { - - constructor( parameters ) { - - super( parameters ); - - this.type = 'RawShaderMaterial'; - - } - - } - - RawShaderMaterial.prototype.isRawShaderMaterial = true; - - const LOD_MIN = 4; - const LOD_MAX = 8; - const SIZE_MAX = Math.pow( 2, LOD_MAX ); - - // The standard deviations (radians) associated with the extra mips. These are - // chosen to approximate a Trowbridge-Reitz distribution function times the - // geometric shadowing function. These sigma values squared must match the - // variance #defines in cube_uv_reflection_fragment.glsl.js. - const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; - - const TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; - - // The maximum length of the blur for loop. Smaller sigmas will use fewer - // samples and exit early, but not recompile the shader. - const MAX_SAMPLES = 20; - - const ENCODINGS = { - [ LinearEncoding ]: 0, - [ sRGBEncoding ]: 1, - [ RGBEEncoding ]: 2, - [ RGBM7Encoding ]: 3, - [ RGBM16Encoding ]: 4, - [ RGBDEncoding ]: 5, - [ GammaEncoding ]: 6 - }; - - const _flatCamera = /*@__PURE__*/ new OrthographicCamera(); - const { _lodPlanes, _sizeLods, _sigmas } = /*@__PURE__*/ _createPlanes(); - const _clearColor = /*@__PURE__*/ new Color(); - let _oldTarget = null; - - // Golden Ratio - const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; - const INV_PHI = 1 / PHI; - - // Vertices of a dodecahedron (except the opposites, which represent the - // same axis), used as axis directions evenly spread on a sphere. - const _axisDirections = [ - /*@__PURE__*/ new Vector3( 1, 1, 1 ), - /*@__PURE__*/ new Vector3( - 1, 1, 1 ), - /*@__PURE__*/ new Vector3( 1, 1, - 1 ), - /*@__PURE__*/ new Vector3( - 1, 1, - 1 ), - /*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ), - /*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ), - /*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ), - /*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ), - /*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ), - /*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ) ]; - - /** - * This class generates a Prefiltered, Mipmapped Radiance Environment Map - * (PMREM) from a cubeMap environment texture. This allows different levels of - * blur to be quickly accessed based on material roughness. It is packed into a - * special CubeUV format that allows us to perform custom interpolation so that - * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap - * chain, it only goes down to the LOD_MIN level (above), and then creates extra - * even more filtered 'mips' at the same LOD_MIN resolution, associated with - * higher roughness levels. In this way we maintain resolution to smoothly - * interpolate diffuse lighting while limiting sampling computation. - * - * Paper: Fast, Accurate Image-Based Lighting - * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view - */ - - class PMREMGenerator { - - constructor( renderer ) { - - this._renderer = renderer; - this._pingPongRenderTarget = null; - - this._blurMaterial = _getBlurShader( MAX_SAMPLES ); - this._equirectShader = null; - this._cubemapShader = null; - - this._compileMaterial( this._blurMaterial ); - - } - - /** - * Generates a PMREM from a supplied Scene, which can be faster than using an - * image if networking bandwidth is low. Optional sigma specifies a blur radius - * in radians to be applied to the scene before PMREM generation. Optional near - * and far planes ensure the scene is rendered in its entirety (the cubeCamera - * is placed at the origin). - */ - fromScene( scene, sigma = 0, near = 0.1, far = 100 ) { - - _oldTarget = this._renderer.getRenderTarget(); - const cubeUVRenderTarget = this._allocateTargets(); - - this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget ); - if ( sigma > 0 ) { - - this._blur( cubeUVRenderTarget, 0, 0, sigma ); - - } - - this._applyPMREM( cubeUVRenderTarget ); - this._cleanup( cubeUVRenderTarget ); - - return cubeUVRenderTarget; - - } - - /** - * Generates a PMREM from an equirectangular texture, which can be either LDR - * (RGBFormat) or HDR (RGBEFormat). The ideal input image size is 1k (1024 x 512), - * as this matches best with the 256 x 256 cubemap output. - */ - fromEquirectangular( equirectangular ) { - - return this._fromTexture( equirectangular ); - - } - - /** - * Generates a PMREM from an cubemap texture, which can be either LDR - * (RGBFormat) or HDR (RGBEFormat). The ideal input cube size is 256 x 256, - * as this matches best with the 256 x 256 cubemap output. - */ - fromCubemap( cubemap ) { - - return this._fromTexture( cubemap ); - - } - - /** - * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during - * your texture's network fetch for increased concurrency. - */ - compileCubemapShader() { - - if ( this._cubemapShader === null ) { - - this._cubemapShader = _getCubemapShader(); - this._compileMaterial( this._cubemapShader ); - - } - - } - - /** - * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during - * your texture's network fetch for increased concurrency. - */ - compileEquirectangularShader() { - - if ( this._equirectShader === null ) { - - this._equirectShader = _getEquirectShader(); - this._compileMaterial( this._equirectShader ); - - } - - } - - /** - * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class, - * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on - * one of them will cause any others to also become unusable. - */ - dispose() { - - this._blurMaterial.dispose(); - - if ( this._cubemapShader !== null ) this._cubemapShader.dispose(); - if ( this._equirectShader !== null ) this._equirectShader.dispose(); - - for ( let i = 0; i < _lodPlanes.length; i ++ ) { - - _lodPlanes[ i ].dispose(); - - } - - } - - // private interface - - _cleanup( outputTarget ) { - - this._pingPongRenderTarget.dispose(); - this._renderer.setRenderTarget( _oldTarget ); - outputTarget.scissorTest = false; - _setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height ); - - } - - _fromTexture( texture ) { - - _oldTarget = this._renderer.getRenderTarget(); - const cubeUVRenderTarget = this._allocateTargets( texture ); - this._textureToCubeUV( texture, cubeUVRenderTarget ); - this._applyPMREM( cubeUVRenderTarget ); - this._cleanup( cubeUVRenderTarget ); - - return cubeUVRenderTarget; - - } - - _allocateTargets( texture ) { // warning: null texture is valid - - const params = { - magFilter: NearestFilter, - minFilter: NearestFilter, - generateMipmaps: false, - type: UnsignedByteType, - format: RGBEFormat, - encoding: _isLDR( texture ) ? texture.encoding : RGBEEncoding, - depthBuffer: false - }; - - const cubeUVRenderTarget = _createRenderTarget( params ); - cubeUVRenderTarget.depthBuffer = texture ? false : true; - this._pingPongRenderTarget = _createRenderTarget( params ); - return cubeUVRenderTarget; - - } - - _compileMaterial( material ) { - - const tmpMesh = new Mesh( _lodPlanes[ 0 ], material ); - this._renderer.compile( tmpMesh, _flatCamera ); - - } - - _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) { - - const fov = 90; - const aspect = 1; - const cubeCamera = new PerspectiveCamera( fov, aspect, near, far ); - const upSign = [ 1, - 1, 1, 1, 1, 1 ]; - const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ]; - const renderer = this._renderer; - - const originalAutoClear = renderer.autoClear; - const outputEncoding = renderer.outputEncoding; - const toneMapping = renderer.toneMapping; - renderer.getClearColor( _clearColor ); - - renderer.toneMapping = NoToneMapping; - renderer.outputEncoding = LinearEncoding; - renderer.autoClear = false; - - const backgroundMaterial = new MeshBasicMaterial( { - name: 'PMREM.Background', - side: BackSide, - depthWrite: false, - depthTest: false, - } ); - - const backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial ); - - let useSolidColor = false; - const background = scene.background; - - if ( background ) { - - if ( background.isColor ) { - - backgroundMaterial.color.copy( background ); - scene.background = null; - useSolidColor = true; - - } - - } else { - - backgroundMaterial.color.copy( _clearColor ); - useSolidColor = true; - - } - - for ( let i = 0; i < 6; i ++ ) { - - const col = i % 3; - if ( col == 0 ) { - - cubeCamera.up.set( 0, upSign[ i ], 0 ); - cubeCamera.lookAt( forwardSign[ i ], 0, 0 ); - - } else if ( col == 1 ) { - - cubeCamera.up.set( 0, 0, upSign[ i ] ); - cubeCamera.lookAt( 0, forwardSign[ i ], 0 ); - - } else { - - cubeCamera.up.set( 0, upSign[ i ], 0 ); - cubeCamera.lookAt( 0, 0, forwardSign[ i ] ); - - } - - _setViewport( cubeUVRenderTarget, - col * SIZE_MAX, i > 2 ? SIZE_MAX : 0, SIZE_MAX, SIZE_MAX ); - renderer.setRenderTarget( cubeUVRenderTarget ); - - if ( useSolidColor ) { - - renderer.render( backgroundBox, cubeCamera ); - - } - - renderer.render( scene, cubeCamera ); - - } - - backgroundBox.geometry.dispose(); - backgroundBox.material.dispose(); - - renderer.toneMapping = toneMapping; - renderer.outputEncoding = outputEncoding; - renderer.autoClear = originalAutoClear; - scene.background = background; - - } - - _textureToCubeUV( texture, cubeUVRenderTarget ) { - - const renderer = this._renderer; - - if ( texture.isCubeTexture ) { - - if ( this._cubemapShader == null ) { - - this._cubemapShader = _getCubemapShader(); - - } - - } else { - - if ( this._equirectShader == null ) { - - this._equirectShader = _getEquirectShader(); - - } - - } - - const material = texture.isCubeTexture ? this._cubemapShader : this._equirectShader; - const mesh = new Mesh( _lodPlanes[ 0 ], material ); - - const uniforms = material.uniforms; - - uniforms[ 'envMap' ].value = texture; - - if ( ! texture.isCubeTexture ) { - - uniforms[ 'texelSize' ].value.set( 1.0 / texture.image.width, 1.0 / texture.image.height ); - - } - - uniforms[ 'inputEncoding' ].value = ENCODINGS[ texture.encoding ]; - uniforms[ 'outputEncoding' ].value = ENCODINGS[ cubeUVRenderTarget.texture.encoding ]; - - _setViewport( cubeUVRenderTarget, 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX ); - - renderer.setRenderTarget( cubeUVRenderTarget ); - renderer.render( mesh, _flatCamera ); - - } - - _applyPMREM( cubeUVRenderTarget ) { - - const renderer = this._renderer; - const autoClear = renderer.autoClear; - renderer.autoClear = false; - - for ( let i = 1; i < TOTAL_LODS; i ++ ) { - - const sigma = Math.sqrt( _sigmas[ i ] * _sigmas[ i ] - _sigmas[ i - 1 ] * _sigmas[ i - 1 ] ); - - const poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ]; - - this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis ); - - } - - renderer.autoClear = autoClear; - - } - - /** - * This is a two-pass Gaussian blur for a cubemap. Normally this is done - * vertically and horizontally, but this breaks down on a cube. Here we apply - * the blur latitudinally (around the poles), and then longitudinally (towards - * the poles) to approximate the orthogonally-separable blur. It is least - * accurate at the poles, but still does a decent job. - */ - _blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) { - - const pingPongRenderTarget = this._pingPongRenderTarget; - - this._halfBlur( - cubeUVRenderTarget, - pingPongRenderTarget, - lodIn, - lodOut, - sigma, - 'latitudinal', - poleAxis ); - - this._halfBlur( - pingPongRenderTarget, - cubeUVRenderTarget, - lodOut, - lodOut, - sigma, - 'longitudinal', - poleAxis ); - - } - - _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) { - - const renderer = this._renderer; - const blurMaterial = this._blurMaterial; - - if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) { - - console.error( - 'blur direction must be either latitudinal or longitudinal!' ); - - } - - // Number of standard deviations at which to cut off the discrete approximation. - const STANDARD_DEVIATIONS = 3; - - const blurMesh = new Mesh( _lodPlanes[ lodOut ], blurMaterial ); - const blurUniforms = blurMaterial.uniforms; - - const pixels = _sizeLods[ lodIn ] - 1; - const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 ); - const sigmaPixels = sigmaRadians / radiansPerPixel; - const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES; - - if ( samples > MAX_SAMPLES ) { - - console.warn( `sigmaRadians, ${ - sigmaRadians}, is too large and will clip, as it requested ${ - samples} samples when the maximum is set to ${MAX_SAMPLES}` ); - - } - - const weights = []; - let sum = 0; - - for ( let i = 0; i < MAX_SAMPLES; ++ i ) { - - const x = i / sigmaPixels; - const weight = Math.exp( - x * x / 2 ); - weights.push( weight ); - - if ( i == 0 ) { - - sum += weight; - - } else if ( i < samples ) { - - sum += 2 * weight; - - } - - } - - for ( let i = 0; i < weights.length; i ++ ) { - - weights[ i ] = weights[ i ] / sum; - - } - - blurUniforms[ 'envMap' ].value = targetIn.texture; - blurUniforms[ 'samples' ].value = samples; - blurUniforms[ 'weights' ].value = weights; - blurUniforms[ 'latitudinal' ].value = direction === 'latitudinal'; - - if ( poleAxis ) { - - blurUniforms[ 'poleAxis' ].value = poleAxis; - - } - - blurUniforms[ 'dTheta' ].value = radiansPerPixel; - blurUniforms[ 'mipInt' ].value = LOD_MAX - lodIn; - blurUniforms[ 'inputEncoding' ].value = ENCODINGS[ targetIn.texture.encoding ]; - blurUniforms[ 'outputEncoding' ].value = ENCODINGS[ targetIn.texture.encoding ]; - - const outputSize = _sizeLods[ lodOut ]; - const x = 3 * Math.max( 0, SIZE_MAX - 2 * outputSize ); - const y = ( lodOut === 0 ? 0 : 2 * SIZE_MAX ) + 2 * outputSize * ( lodOut > LOD_MAX - LOD_MIN ? lodOut - LOD_MAX + LOD_MIN : 0 ); - - _setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize ); - renderer.setRenderTarget( targetOut ); - renderer.render( blurMesh, _flatCamera ); - - } - - } - - function _isLDR( texture ) { - - if ( texture === undefined || texture.type !== UnsignedByteType ) return false; - - return texture.encoding === LinearEncoding || texture.encoding === sRGBEncoding || texture.encoding === GammaEncoding; - - } - - function _createPlanes() { - - const _lodPlanes = []; - const _sizeLods = []; - const _sigmas = []; - - let lod = LOD_MAX; - - for ( let i = 0; i < TOTAL_LODS; i ++ ) { - - const sizeLod = Math.pow( 2, lod ); - _sizeLods.push( sizeLod ); - let sigma = 1.0 / sizeLod; - - if ( i > LOD_MAX - LOD_MIN ) { - - sigma = EXTRA_LOD_SIGMA[ i - LOD_MAX + LOD_MIN - 1 ]; - - } else if ( i == 0 ) { - - sigma = 0; - - } - - _sigmas.push( sigma ); - - const texelSize = 1.0 / ( sizeLod - 1 ); - const min = - texelSize / 2; - const max = 1 + texelSize / 2; - const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ]; - - const cubeFaces = 6; - const vertices = 6; - const positionSize = 3; - const uvSize = 2; - const faceIndexSize = 1; - - const position = new Float32Array( positionSize * vertices * cubeFaces ); - const uv = new Float32Array( uvSize * vertices * cubeFaces ); - const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); - - for ( let face = 0; face < cubeFaces; face ++ ) { - - const x = ( face % 3 ) * 2 / 3 - 1; - const y = face > 2 ? 0 : - 1; - const coordinates = [ - x, y, 0, - x + 2 / 3, y, 0, - x + 2 / 3, y + 1, 0, - x, y, 0, - x + 2 / 3, y + 1, 0, - x, y + 1, 0 - ]; - position.set( coordinates, positionSize * vertices * face ); - uv.set( uv1, uvSize * vertices * face ); - const fill = [ face, face, face, face, face, face ]; - faceIndex.set( fill, faceIndexSize * vertices * face ); - - } - - const planes = new BufferGeometry(); - planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) ); - planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) ); - planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) ); - _lodPlanes.push( planes ); - - if ( lod > LOD_MIN ) { - - lod --; - - } - - } - - return { _lodPlanes, _sizeLods, _sigmas }; - - } - - function _createRenderTarget( params ) { - - const cubeUVRenderTarget = new WebGLRenderTarget( 3 * SIZE_MAX, 3 * SIZE_MAX, params ); - cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; - cubeUVRenderTarget.texture.name = 'PMREM.cubeUv'; - cubeUVRenderTarget.scissorTest = true; - return cubeUVRenderTarget; - - } - - function _setViewport( target, x, y, width, height ) { - - target.viewport.set( x, y, width, height ); - target.scissor.set( x, y, width, height ); - - } - - function _getBlurShader( maxSamples ) { - - const weights = new Float32Array( maxSamples ); - const poleAxis = new Vector3( 0, 1, 0 ); - const shaderMaterial = new RawShaderMaterial( { - - name: 'SphericalGaussianBlur', - - defines: { 'n': maxSamples }, - - uniforms: { - 'envMap': { value: null }, - 'samples': { value: 1 }, - 'weights': { value: weights }, - 'latitudinal': { value: false }, - 'dTheta': { value: 0 }, - 'mipInt': { value: 0 }, - 'poleAxis': { value: poleAxis }, - 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] }, - 'outputEncoding': { value: ENCODINGS[ LinearEncoding ] } - }, - - vertexShader: _getCommonVertexShader(), - - fragmentShader: /* glsl */` - - precision mediump float; - precision mediump int; - - varying vec3 vOutputDirection; - - uniform sampler2D envMap; - uniform int samples; - uniform float weights[ n ]; - uniform bool latitudinal; - uniform float dTheta; - uniform float mipInt; - uniform vec3 poleAxis; - - ${ _getEncodings() } - - #define ENVMAP_TYPE_CUBE_UV - #include - - vec3 getSample( float theta, vec3 axis ) { - - float cosTheta = cos( theta ); - // Rodrigues' axis-angle rotation - vec3 sampleDirection = vOutputDirection * cosTheta - + cross( axis, vOutputDirection ) * sin( theta ) - + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); - - return bilinearCubeUV( envMap, sampleDirection, mipInt ); - - } - - void main() { - - vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); - - if ( all( equal( axis, vec3( 0.0 ) ) ) ) { - - axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); - - } - - axis = normalize( axis ); - - gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); - gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); - - for ( int i = 1; i < n; i++ ) { - - if ( i >= samples ) { - - break; - - } - - float theta = dTheta * float( i ); - gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); - gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); - - } - - gl_FragColor = linearToOutputTexel( gl_FragColor ); - - } - `, - - blending: NoBlending, - depthTest: false, - depthWrite: false - - } ); - - return shaderMaterial; - - } - - function _getEquirectShader() { - - const texelSize = new Vector2( 1, 1 ); - const shaderMaterial = new RawShaderMaterial( { - - name: 'EquirectangularToCubeUV', - - uniforms: { - 'envMap': { value: null }, - 'texelSize': { value: texelSize }, - 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] }, - 'outputEncoding': { value: ENCODINGS[ LinearEncoding ] } - }, - - vertexShader: _getCommonVertexShader(), - - fragmentShader: /* glsl */` - - precision mediump float; - precision mediump int; - - varying vec3 vOutputDirection; - - uniform sampler2D envMap; - uniform vec2 texelSize; - - ${ _getEncodings() } - - #include - - void main() { - - gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); - - vec3 outputDirection = normalize( vOutputDirection ); - vec2 uv = equirectUv( outputDirection ); - - vec2 f = fract( uv / texelSize - 0.5 ); - uv -= f * texelSize; - vec3 tl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb; - uv.x += texelSize.x; - vec3 tr = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb; - uv.y += texelSize.y; - vec3 br = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb; - uv.x -= texelSize.x; - vec3 bl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb; - - vec3 tm = mix( tl, tr, f.x ); - vec3 bm = mix( bl, br, f.x ); - gl_FragColor.rgb = mix( tm, bm, f.y ); - - gl_FragColor = linearToOutputTexel( gl_FragColor ); - - } - `, - - blending: NoBlending, - depthTest: false, - depthWrite: false - - } ); - - return shaderMaterial; - - } - - function _getCubemapShader() { - - const shaderMaterial = new RawShaderMaterial( { - - name: 'CubemapToCubeUV', - - uniforms: { - 'envMap': { value: null }, - 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] }, - 'outputEncoding': { value: ENCODINGS[ LinearEncoding ] } - }, - - vertexShader: _getCommonVertexShader(), - - fragmentShader: /* glsl */` - - precision mediump float; - precision mediump int; - - varying vec3 vOutputDirection; - - uniform samplerCube envMap; - - ${ _getEncodings() } - - void main() { - - gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); - gl_FragColor.rgb = envMapTexelToLinear( textureCube( envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ) ) ).rgb; - gl_FragColor = linearToOutputTexel( gl_FragColor ); - - } - `, - - blending: NoBlending, - depthTest: false, - depthWrite: false - - } ); - - return shaderMaterial; - - } - - function _getCommonVertexShader() { - - return /* glsl */` - - precision mediump float; - precision mediump int; - - attribute vec3 position; - attribute vec2 uv; - attribute float faceIndex; - - varying vec3 vOutputDirection; - - // RH coordinate system; PMREM face-indexing convention - vec3 getDirection( vec2 uv, float face ) { - - uv = 2.0 * uv - 1.0; - - vec3 direction = vec3( uv, 1.0 ); - - if ( face == 0.0 ) { - - direction = direction.zyx; // ( 1, v, u ) pos x - - } else if ( face == 1.0 ) { - - direction = direction.xzy; - direction.xz *= -1.0; // ( -u, 1, -v ) pos y - - } else if ( face == 2.0 ) { - - direction.x *= -1.0; // ( -u, v, 1 ) pos z - - } else if ( face == 3.0 ) { - - direction = direction.zyx; - direction.xz *= -1.0; // ( -1, v, -u ) neg x - - } else if ( face == 4.0 ) { - - direction = direction.xzy; - direction.xy *= -1.0; // ( -u, -1, v ) neg y - - } else if ( face == 5.0 ) { - - direction.z *= -1.0; // ( u, v, -1 ) neg z - - } - - return direction; - - } - - void main() { - - vOutputDirection = getDirection( uv, faceIndex ); - gl_Position = vec4( position, 1.0 ); - - } - `; - - } - - function _getEncodings() { - - return /* glsl */` - - uniform int inputEncoding; - uniform int outputEncoding; - - #include - - vec4 inputTexelToLinear( vec4 value ) { - - if ( inputEncoding == 0 ) { - - return value; - - } else if ( inputEncoding == 1 ) { - - return sRGBToLinear( value ); - - } else if ( inputEncoding == 2 ) { - - return RGBEToLinear( value ); - - } else if ( inputEncoding == 3 ) { - - return RGBMToLinear( value, 7.0 ); - - } else if ( inputEncoding == 4 ) { - - return RGBMToLinear( value, 16.0 ); - - } else if ( inputEncoding == 5 ) { - - return RGBDToLinear( value, 256.0 ); - - } else { - - return GammaToLinear( value, 2.2 ); - - } - - } - - vec4 linearToOutputTexel( vec4 value ) { - - if ( outputEncoding == 0 ) { - - return value; - - } else if ( outputEncoding == 1 ) { - - return LinearTosRGB( value ); - - } else if ( outputEncoding == 2 ) { - - return LinearToRGBE( value ); - - } else if ( outputEncoding == 3 ) { - - return LinearToRGBM( value, 7.0 ); - - } else if ( outputEncoding == 4 ) { - - return LinearToRGBM( value, 16.0 ); - - } else if ( outputEncoding == 5 ) { - - return LinearToRGBD( value, 256.0 ); - - } else { - - return LinearToGamma( value, 2.2 ); - - } - - } - - vec4 envMapTexelToLinear( vec4 color ) { - - return inputTexelToLinear( color ); - - } - `; - - } - - function WebGLCubeUVMaps( renderer ) { - - let cubeUVmaps = new WeakMap(); - - let pmremGenerator = null; - - function get( texture ) { - - if ( texture && texture.isTexture && texture.isRenderTargetTexture === false ) { - - const mapping = texture.mapping; - - const isEquirectMap = ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ); - const isCubeMap = ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping ); - - if ( isEquirectMap || isCubeMap ) { - - // equirect/cube map to cubeUV conversion - - if ( cubeUVmaps.has( texture ) ) { - - return cubeUVmaps.get( texture ).texture; - - } else { - - const image = texture.image; - - if ( ( isEquirectMap && image && image.height > 0 ) || ( isCubeMap && image && isCubeTextureComplete( image ) ) ) { - - const currentRenderTarget = renderer.getRenderTarget(); - - if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); - - const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture ) : pmremGenerator.fromCubemap( texture ); - cubeUVmaps.set( texture, renderTarget ); - - renderer.setRenderTarget( currentRenderTarget ); - - texture.addEventListener( 'dispose', onTextureDispose ); - - return renderTarget.texture; - - } else { - - // image not yet ready. try the conversion next frame - - return null; - - } - - } - - } - - } - - return texture; - - } - - function isCubeTextureComplete( image ) { - - let count = 0; - const length = 6; - - for ( let i = 0; i < length; i ++ ) { - - if ( image[ i ] !== undefined ) count ++; - - } - - return count === length; - - - } - - function onTextureDispose( event ) { - - const texture = event.target; - - texture.removeEventListener( 'dispose', onTextureDispose ); - - const cubemapUV = cubeUVmaps.get( texture ); - - if ( cubemapUV !== undefined ) { - - cubeUVmaps.delete( texture ); - cubemapUV.dispose(); - - } - - } - - function dispose() { - - cubeUVmaps = new WeakMap(); - - if ( pmremGenerator !== null ) { - - pmremGenerator.dispose(); - pmremGenerator = null; - - } - - } - - return { - get: get, - dispose: dispose - }; - - } - - function WebGLExtensions( gl ) { - - const extensions = {}; - - function getExtension( name ) { - - if ( extensions[ name ] !== undefined ) { - - return extensions[ name ]; - - } - - let extension; - - switch ( name ) { - - case 'WEBGL_depth_texture': - extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); - break; - - case 'EXT_texture_filter_anisotropic': - extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); - break; - - case 'WEBGL_compressed_texture_s3tc': - extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); - break; - - case 'WEBGL_compressed_texture_pvrtc': - extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); - break; - - default: - extension = gl.getExtension( name ); - - } - - extensions[ name ] = extension; - - return extension; - - } - - return { - - has: function ( name ) { - - return getExtension( name ) !== null; - - }, - - init: function ( capabilities ) { - - if ( capabilities.isWebGL2 ) { - - getExtension( 'EXT_color_buffer_float' ); - - } else { - - getExtension( 'WEBGL_depth_texture' ); - getExtension( 'OES_texture_float' ); - getExtension( 'OES_texture_half_float' ); - getExtension( 'OES_texture_half_float_linear' ); - getExtension( 'OES_standard_derivatives' ); - getExtension( 'OES_element_index_uint' ); - getExtension( 'OES_vertex_array_object' ); - getExtension( 'ANGLE_instanced_arrays' ); - - } - - getExtension( 'OES_texture_float_linear' ); - getExtension( 'EXT_color_buffer_half_float' ); - - }, - - get: function ( name ) { - - const extension = getExtension( name ); - - if ( extension === null ) { - - console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); - - } - - return extension; - - } - - }; - - } - - function WebGLGeometries( gl, attributes, info, bindingStates ) { - - const geometries = {}; - const wireframeAttributes = new WeakMap(); - - function onGeometryDispose( event ) { - - const geometry = event.target; - - if ( geometry.index !== null ) { - - attributes.remove( geometry.index ); - - } - - for ( const name in geometry.attributes ) { - - attributes.remove( geometry.attributes[ name ] ); - - } - - geometry.removeEventListener( 'dispose', onGeometryDispose ); - - delete geometries[ geometry.id ]; - - const attribute = wireframeAttributes.get( geometry ); - - if ( attribute ) { - - attributes.remove( attribute ); - wireframeAttributes.delete( geometry ); - - } - - bindingStates.releaseStatesOfGeometry( geometry ); - - if ( geometry.isInstancedBufferGeometry === true ) { - - delete geometry._maxInstanceCount; - - } - - // - - info.memory.geometries --; - - } - - function get( object, geometry ) { - - if ( geometries[ geometry.id ] === true ) return geometry; - - geometry.addEventListener( 'dispose', onGeometryDispose ); - - geometries[ geometry.id ] = true; - - info.memory.geometries ++; - - return geometry; - - } - - function update( geometry ) { - - const geometryAttributes = geometry.attributes; - - // Updating index buffer in VAO now. See WebGLBindingStates. - - for ( const name in geometryAttributes ) { - - attributes.update( geometryAttributes[ name ], 34962 ); - - } - - // morph targets - - const morphAttributes = geometry.morphAttributes; - - for ( const name in morphAttributes ) { - - const array = morphAttributes[ name ]; - - for ( let i = 0, l = array.length; i < l; i ++ ) { - - attributes.update( array[ i ], 34962 ); - - } - - } - - } - - function updateWireframeAttribute( geometry ) { - - const indices = []; - - const geometryIndex = geometry.index; - const geometryPosition = geometry.attributes.position; - let version = 0; - - if ( geometryIndex !== null ) { - - const array = geometryIndex.array; - version = geometryIndex.version; - - for ( let i = 0, l = array.length; i < l; i += 3 ) { - - const a = array[ i + 0 ]; - const b = array[ i + 1 ]; - const c = array[ i + 2 ]; - - indices.push( a, b, b, c, c, a ); - - } - - } else { - - const array = geometryPosition.array; - version = geometryPosition.version; - - for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { - - const a = i + 0; - const b = i + 1; - const c = i + 2; - - indices.push( a, b, b, c, c, a ); - - } - - } - - const attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); - attribute.version = version; - - // Updating index buffer in VAO now. See WebGLBindingStates - - // - - const previousAttribute = wireframeAttributes.get( geometry ); - - if ( previousAttribute ) attributes.remove( previousAttribute ); - - // - - wireframeAttributes.set( geometry, attribute ); - - } - - function getWireframeAttribute( geometry ) { - - const currentAttribute = wireframeAttributes.get( geometry ); - - if ( currentAttribute ) { - - const geometryIndex = geometry.index; - - if ( geometryIndex !== null ) { - - // if the attribute is obsolete, create a new one - - if ( currentAttribute.version < geometryIndex.version ) { - - updateWireframeAttribute( geometry ); - - } - - } - - } else { - - updateWireframeAttribute( geometry ); - - } - - return wireframeAttributes.get( geometry ); - - } - - return { - - get: get, - update: update, - - getWireframeAttribute: getWireframeAttribute - - }; - - } - - function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - let mode; - - function setMode( value ) { - - mode = value; - - } - - let type, bytesPerElement; - - function setIndex( value ) { - - type = value.type; - bytesPerElement = value.bytesPerElement; - - } - - function render( start, count ) { - - gl.drawElements( mode, count, type, start * bytesPerElement ); - - info.update( count, mode, 1 ); - - } - - function renderInstances( start, count, primcount ) { - - if ( primcount === 0 ) return; - - let extension, methodName; - - if ( isWebGL2 ) { - - extension = gl; - methodName = 'drawElementsInstanced'; - - } else { - - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawElementsInstancedANGLE'; - - if ( extension === null ) { - - console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } - - } - - extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); - - info.update( count, mode, primcount ); - - } - - // - - this.setMode = setMode; - this.setIndex = setIndex; - this.render = render; - this.renderInstances = renderInstances; - - } - - function WebGLInfo( gl ) { - - const memory = { - geometries: 0, - textures: 0 - }; - - const render = { - frame: 0, - calls: 0, - triangles: 0, - points: 0, - lines: 0 - }; - - function update( count, mode, instanceCount ) { - - render.calls ++; - - switch ( mode ) { - - case 4: - render.triangles += instanceCount * ( count / 3 ); - break; - - case 1: - render.lines += instanceCount * ( count / 2 ); - break; - - case 3: - render.lines += instanceCount * ( count - 1 ); - break; - - case 2: - render.lines += instanceCount * count; - break; - - case 0: - render.points += instanceCount * count; - break; - - default: - console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); - break; - - } - - } - - function reset() { - - render.frame ++; - render.calls = 0; - render.triangles = 0; - render.points = 0; - render.lines = 0; - - } - - return { - memory: memory, - render: render, - programs: null, - autoReset: true, - reset: reset, - update: update - }; - - } - - function numericalSort( a, b ) { - - return a[ 0 ] - b[ 0 ]; - - } - - function absNumericalSort( a, b ) { - - return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); - - } - - function WebGLMorphtargets( gl ) { - - const influencesList = {}; - const morphInfluences = new Float32Array( 8 ); - - const workInfluences = []; - - for ( let i = 0; i < 8; i ++ ) { - - workInfluences[ i ] = [ i, 0 ]; - - } - - function update( object, geometry, material, program ) { - - const objectInfluences = object.morphTargetInfluences; - - // When object doesn't have morph target influences defined, we treat it as a 0-length array - // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences - - const length = objectInfluences === undefined ? 0 : objectInfluences.length; - - let influences = influencesList[ geometry.id ]; - - if ( influences === undefined || influences.length !== length ) { - - // initialise list - - influences = []; - - for ( let i = 0; i < length; i ++ ) { - - influences[ i ] = [ i, 0 ]; - - } - - influencesList[ geometry.id ] = influences; - - } - - // Collect influences - - for ( let i = 0; i < length; i ++ ) { - - const influence = influences[ i ]; - - influence[ 0 ] = i; - influence[ 1 ] = objectInfluences[ i ]; - - } - - influences.sort( absNumericalSort ); - - for ( let i = 0; i < 8; i ++ ) { - - if ( i < length && influences[ i ][ 1 ] ) { - - workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; - workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; - - } else { - - workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; - workInfluences[ i ][ 1 ] = 0; - - } - - } - - workInfluences.sort( numericalSort ); - - const morphTargets = geometry.morphAttributes.position; - const morphNormals = geometry.morphAttributes.normal; - - let morphInfluencesSum = 0; - - for ( let i = 0; i < 8; i ++ ) { - - const influence = workInfluences[ i ]; - const index = influence[ 0 ]; - const value = influence[ 1 ]; - - if ( index !== Number.MAX_SAFE_INTEGER && value ) { - - if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) { - - geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] ); - - } - - if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) { - - geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] ); - - } - - morphInfluences[ i ] = value; - morphInfluencesSum += value; - - } else { - - if ( morphTargets && geometry.hasAttribute( 'morphTarget' + i ) === true ) { - - geometry.deleteAttribute( 'morphTarget' + i ); - - } - - if ( morphNormals && geometry.hasAttribute( 'morphNormal' + i ) === true ) { - - geometry.deleteAttribute( 'morphNormal' + i ); - - } - - morphInfluences[ i ] = 0; - - } - - } - - // GLSL shader uses formula baseinfluence * base + sum(target * influence) - // This allows us to switch between absolute morphs and relative morphs without changing shader code - // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) - const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; - - program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); - program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); - - } - - return { - - update: update - - }; - - } - - function WebGLObjects( gl, geometries, attributes, info ) { - - let updateMap = new WeakMap(); - - function update( object ) { - - const frame = info.render.frame; - - const geometry = object.geometry; - const buffergeometry = geometries.get( object, geometry ); - - // Update once per frame - - if ( updateMap.get( buffergeometry ) !== frame ) { - - geometries.update( buffergeometry ); - - updateMap.set( buffergeometry, frame ); - - } - - if ( object.isInstancedMesh ) { - - if ( object.hasEventListener( 'dispose', onInstancedMeshDispose ) === false ) { - - object.addEventListener( 'dispose', onInstancedMeshDispose ); - - } - - attributes.update( object.instanceMatrix, 34962 ); - - if ( object.instanceColor !== null ) { - - attributes.update( object.instanceColor, 34962 ); - - } - - } - - return buffergeometry; - - } - - function dispose() { - - updateMap = new WeakMap(); - - } - - function onInstancedMeshDispose( event ) { - - const instancedMesh = event.target; - - instancedMesh.removeEventListener( 'dispose', onInstancedMeshDispose ); - - attributes.remove( instancedMesh.instanceMatrix ); - - if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); - - } - - return { - - update: update, - dispose: dispose - - }; - - } - - class DataTexture2DArray extends Texture { - - constructor( data = null, width = 1, height = 1, depth = 1 ) { - - super( null ); - - this.image = { data, width, height, depth }; - - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; - - this.wrapR = ClampToEdgeWrapping; - - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; - - this.needsUpdate = true; - - } - - } - - DataTexture2DArray.prototype.isDataTexture2DArray = true; - - class DataTexture3D extends Texture { - - constructor( data = null, width = 1, height = 1, depth = 1 ) { - - // We're going to add .setXXX() methods for setting properties later. - // Users can still set in DataTexture3D directly. - // - // const texture = new THREE.DataTexture3D( data, width, height, depth ); - // texture.anisotropy = 16; - // - // See #14839 - - super( null ); - - this.image = { data, width, height, depth }; - - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; - - this.wrapR = ClampToEdgeWrapping; - - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; - - this.needsUpdate = true; - - } - - } - - DataTexture3D.prototype.isDataTexture3D = true; - - /** - * Uniforms of a program. - * Those form a tree structure with a special top-level container for the root, - * which you get by calling 'new WebGLUniforms( gl, program )'. - * - * - * Properties of inner nodes including the top-level container: - * - * .seq - array of nested uniforms - * .map - nested uniforms by name - * - * - * Methods of all nodes except the top-level container: - * - * .setValue( gl, value, [textures] ) - * - * uploads a uniform value(s) - * the 'textures' parameter is needed for sampler uniforms - * - * - * Static methods of the top-level container (textures factorizations): - * - * .upload( gl, seq, values, textures ) - * - * sets uniforms in 'seq' to 'values[id].value' - * - * .seqWithValue( seq, values ) : filteredSeq - * - * filters 'seq' entries with corresponding entry in values - * - * - * Methods of the top-level container (textures factorizations): - * - * .setValue( gl, name, value, textures ) - * - * sets uniform with name 'name' to 'value' - * - * .setOptional( gl, obj, prop ) - * - * like .set for an optional property of the object - * - */ - - const emptyTexture = new Texture(); - const emptyTexture2dArray = new DataTexture2DArray(); - const emptyTexture3d = new DataTexture3D(); - const emptyCubeTexture = new CubeTexture(); - - // --- Utilities --- - - // Array Caches (provide typed arrays for temporary by size) - - const arrayCacheF32 = []; - const arrayCacheI32 = []; - - // Float32Array caches used for uploading Matrix uniforms - - const mat4array = new Float32Array( 16 ); - const mat3array = new Float32Array( 9 ); - const mat2array = new Float32Array( 4 ); - - // Flattening for arrays of vectors and matrices - - function flatten( array, nBlocks, blockSize ) { - - const firstElem = array[ 0 ]; - - if ( firstElem <= 0 || firstElem > 0 ) return array; - // unoptimized: ! isNaN( firstElem ) - // see http://jacksondunstan.com/articles/983 - - const n = nBlocks * blockSize; - let r = arrayCacheF32[ n ]; - - if ( r === undefined ) { - - r = new Float32Array( n ); - arrayCacheF32[ n ] = r; - - } - - if ( nBlocks !== 0 ) { - - firstElem.toArray( r, 0 ); - - for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { - - offset += blockSize; - array[ i ].toArray( r, offset ); - - } - - } - - return r; - - } - - function arraysEqual( a, b ) { - - if ( a.length !== b.length ) return false; - - for ( let i = 0, l = a.length; i < l; i ++ ) { - - if ( a[ i ] !== b[ i ] ) return false; - - } - - return true; - - } - - function copyArray( a, b ) { - - for ( let i = 0, l = b.length; i < l; i ++ ) { - - a[ i ] = b[ i ]; - - } - - } - - // Texture unit allocation - - function allocTexUnits( textures, n ) { - - let r = arrayCacheI32[ n ]; - - if ( r === undefined ) { - - r = new Int32Array( n ); - arrayCacheI32[ n ] = r; - - } - - for ( let i = 0; i !== n; ++ i ) { - - r[ i ] = textures.allocateTextureUnit(); - - } - - return r; - - } - - // --- Setters --- - - // Note: Defining these methods externally, because they come in a bunch - // and this way their names minify. - - // Single scalar - - function setValueV1f( gl, v ) { - - const cache = this.cache; - - if ( cache[ 0 ] === v ) return; - - gl.uniform1f( this.addr, v ); - - cache[ 0 ] = v; - - } - - // Single float vector (from flat array or THREE.VectorN) - - function setValueV2f( gl, v ) { - - const cache = this.cache; - - if ( v.x !== undefined ) { - - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { - - gl.uniform2f( this.addr, v.x, v.y ); - - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - - } - - } else { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform2fv( this.addr, v ); - - copyArray( cache, v ); - - } - - } - - function setValueV3f( gl, v ) { - - const cache = this.cache; - - if ( v.x !== undefined ) { - - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { - - gl.uniform3f( this.addr, v.x, v.y, v.z ); - - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; - - } - - } else if ( v.r !== undefined ) { - - if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { - - gl.uniform3f( this.addr, v.r, v.g, v.b ); - - cache[ 0 ] = v.r; - cache[ 1 ] = v.g; - cache[ 2 ] = v.b; - - } - - } else { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform3fv( this.addr, v ); - - copyArray( cache, v ); - - } - - } - - function setValueV4f( gl, v ) { - - const cache = this.cache; - - if ( v.x !== undefined ) { - - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { - - gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); - - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; - cache[ 3 ] = v.w; - - } - - } else { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform4fv( this.addr, v ); - - copyArray( cache, v ); - - } - - } - - // Single matrix (from flat array or THREE.MatrixN) - - function setValueM2( gl, v ) { - - const cache = this.cache; - const elements = v.elements; - - if ( elements === undefined ) { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniformMatrix2fv( this.addr, false, v ); - - copyArray( cache, v ); - - } else { - - if ( arraysEqual( cache, elements ) ) return; - - mat2array.set( elements ); - - gl.uniformMatrix2fv( this.addr, false, mat2array ); - - copyArray( cache, elements ); - - } - - } - - function setValueM3( gl, v ) { - - const cache = this.cache; - const elements = v.elements; - - if ( elements === undefined ) { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniformMatrix3fv( this.addr, false, v ); - - copyArray( cache, v ); - - } else { - - if ( arraysEqual( cache, elements ) ) return; - - mat3array.set( elements ); - - gl.uniformMatrix3fv( this.addr, false, mat3array ); - - copyArray( cache, elements ); - - } - - } - - function setValueM4( gl, v ) { - - const cache = this.cache; - const elements = v.elements; - - if ( elements === undefined ) { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniformMatrix4fv( this.addr, false, v ); - - copyArray( cache, v ); - - } else { - - if ( arraysEqual( cache, elements ) ) return; - - mat4array.set( elements ); - - gl.uniformMatrix4fv( this.addr, false, mat4array ); - - copyArray( cache, elements ); - - } - - } - - // Single integer / boolean - - function setValueV1i( gl, v ) { - - const cache = this.cache; - - if ( cache[ 0 ] === v ) return; - - gl.uniform1i( this.addr, v ); - - cache[ 0 ] = v; - - } - - // Single integer / boolean vector (from flat array) - - function setValueV2i( gl, v ) { - - const cache = this.cache; - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform2iv( this.addr, v ); - - copyArray( cache, v ); - - } - - function setValueV3i( gl, v ) { - - const cache = this.cache; - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform3iv( this.addr, v ); - - copyArray( cache, v ); - - } - - function setValueV4i( gl, v ) { - - const cache = this.cache; - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform4iv( this.addr, v ); - - copyArray( cache, v ); - - } - - // Single unsigned integer - - function setValueV1ui( gl, v ) { - - const cache = this.cache; - - if ( cache[ 0 ] === v ) return; - - gl.uniform1ui( this.addr, v ); - - cache[ 0 ] = v; - - } - - // Single unsigned integer vector (from flat array) - - function setValueV2ui( gl, v ) { - - const cache = this.cache; - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform2uiv( this.addr, v ); - - copyArray( cache, v ); - - } - - function setValueV3ui( gl, v ) { - - const cache = this.cache; - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform3uiv( this.addr, v ); - - copyArray( cache, v ); - - } - - function setValueV4ui( gl, v ) { - - const cache = this.cache; - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform4uiv( this.addr, v ); - - copyArray( cache, v ); - - } - - - // Single texture (2D / Cube) - - function setValueT1( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.safeSetTexture2D( v || emptyTexture, unit ); - - } - - function setValueT3D1( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.setTexture3D( v || emptyTexture3d, unit ); - - } - - function setValueT6( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.safeSetTextureCube( v || emptyCubeTexture, unit ); - - } - - function setValueT2DArray1( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.setTexture2DArray( v || emptyTexture2dArray, unit ); - - } - - // Helper to pick the right setter for the singular case - - function getSingularSetter( type ) { - - switch ( type ) { - - case 0x1406: return setValueV1f; // FLOAT - case 0x8b50: return setValueV2f; // _VEC2 - case 0x8b51: return setValueV3f; // _VEC3 - case 0x8b52: return setValueV4f; // _VEC4 - - case 0x8b5a: return setValueM2; // _MAT2 - case 0x8b5b: return setValueM3; // _MAT3 - case 0x8b5c: return setValueM4; // _MAT4 - - case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 - - case 0x1405: return setValueV1ui; // UINT - case 0x8dc6: return setValueV2ui; // _VEC2 - case 0x8dc7: return setValueV3ui; // _VEC3 - case 0x8dc8: return setValueV4ui; // _VEC4 - - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1; - - case 0x8b5f: // SAMPLER_3D - case 0x8dcb: // INT_SAMPLER_3D - case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D - return setValueT3D1; - - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6; - - case 0x8dc1: // SAMPLER_2D_ARRAY - case 0x8dcf: // INT_SAMPLER_2D_ARRAY - case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY - case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW - return setValueT2DArray1; - - } - - } - - - // Array of scalars - - function setValueV1fArray( gl, v ) { - - gl.uniform1fv( this.addr, v ); - - } - - // Array of vectors (from flat array or array of THREE.VectorN) - - function setValueV2fArray( gl, v ) { - - const data = flatten( v, this.size, 2 ); - - gl.uniform2fv( this.addr, data ); - - } - - function setValueV3fArray( gl, v ) { - - const data = flatten( v, this.size, 3 ); - - gl.uniform3fv( this.addr, data ); - - } - - function setValueV4fArray( gl, v ) { - - const data = flatten( v, this.size, 4 ); - - gl.uniform4fv( this.addr, data ); - - } - - // Array of matrices (from flat array or array of THREE.MatrixN) - - function setValueM2Array( gl, v ) { - - const data = flatten( v, this.size, 4 ); - - gl.uniformMatrix2fv( this.addr, false, data ); - - } - - function setValueM3Array( gl, v ) { - - const data = flatten( v, this.size, 9 ); - - gl.uniformMatrix3fv( this.addr, false, data ); - - } - - function setValueM4Array( gl, v ) { - - const data = flatten( v, this.size, 16 ); - - gl.uniformMatrix4fv( this.addr, false, data ); - - } - - // Array of integer / boolean - - function setValueV1iArray( gl, v ) { - - gl.uniform1iv( this.addr, v ); - - } - - // Array of integer / boolean vectors (from flat array) - - function setValueV2iArray( gl, v ) { - - gl.uniform2iv( this.addr, v ); - - } - - function setValueV3iArray( gl, v ) { - - gl.uniform3iv( this.addr, v ); - - } - - function setValueV4iArray( gl, v ) { - - gl.uniform4iv( this.addr, v ); - - } - - // Array of unsigned integer - - function setValueV1uiArray( gl, v ) { - - gl.uniform1uiv( this.addr, v ); - - } - - // Array of unsigned integer vectors (from flat array) - - function setValueV2uiArray( gl, v ) { - - gl.uniform2uiv( this.addr, v ); - - } - - function setValueV3uiArray( gl, v ) { - - gl.uniform3uiv( this.addr, v ); - - } - - function setValueV4uiArray( gl, v ) { - - gl.uniform4uiv( this.addr, v ); - - } - - - // Array of textures (2D / Cube) - - function setValueT1Array( gl, v, textures ) { - - const n = v.length; - - const units = allocTexUnits( textures, n ); - - gl.uniform1iv( this.addr, units ); - - for ( let i = 0; i !== n; ++ i ) { - - textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] ); - - } - - } - - function setValueT6Array( gl, v, textures ) { - - const n = v.length; - - const units = allocTexUnits( textures, n ); - - gl.uniform1iv( this.addr, units ); - - for ( let i = 0; i !== n; ++ i ) { - - textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); - - } - - } - - // Helper to pick the right setter for a pure (bottom-level) array - - function getPureArraySetter( type ) { - - switch ( type ) { - - case 0x1406: return setValueV1fArray; // FLOAT - case 0x8b50: return setValueV2fArray; // _VEC2 - case 0x8b51: return setValueV3fArray; // _VEC3 - case 0x8b52: return setValueV4fArray; // _VEC4 - - case 0x8b5a: return setValueM2Array; // _MAT2 - case 0x8b5b: return setValueM3Array; // _MAT3 - case 0x8b5c: return setValueM4Array; // _MAT4 - - case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 - - case 0x1405: return setValueV1uiArray; // UINT - case 0x8dc6: return setValueV2uiArray; // _VEC2 - case 0x8dc7: return setValueV3uiArray; // _VEC3 - case 0x8dc8: return setValueV4uiArray; // _VEC4 - - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1Array; - - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6Array; - - } - - } - - // --- Uniform Classes --- - - function SingleUniform( id, activeInfo, addr ) { - - this.id = id; - this.addr = addr; - this.cache = []; - this.setValue = getSingularSetter( activeInfo.type ); - - // this.path = activeInfo.name; // DEBUG - - } - - function PureArrayUniform( id, activeInfo, addr ) { - - this.id = id; - this.addr = addr; - this.cache = []; - this.size = activeInfo.size; - this.setValue = getPureArraySetter( activeInfo.type ); - - // this.path = activeInfo.name; // DEBUG - - } - - PureArrayUniform.prototype.updateCache = function ( data ) { - - const cache = this.cache; - - if ( data instanceof Float32Array && cache.length !== data.length ) { - - this.cache = new Float32Array( data.length ); - - } - - copyArray( cache, data ); - - }; - - function StructuredUniform( id ) { - - this.id = id; - - this.seq = []; - this.map = {}; - - } - - StructuredUniform.prototype.setValue = function ( gl, value, textures ) { - - const seq = this.seq; - - for ( let i = 0, n = seq.length; i !== n; ++ i ) { - - const u = seq[ i ]; - u.setValue( gl, value[ u.id ], textures ); - - } - - }; - - // --- Top-level --- - - // Parser - builds up the property tree from the path strings - - const RePathPart = /(\w+)(\])?(\[|\.)?/g; - - // extracts - // - the identifier (member name or array index) - // - followed by an optional right bracket (found when array index) - // - followed by an optional left bracket or dot (type of subscript) - // - // Note: These portions can be read in a non-overlapping fashion and - // allow straightforward parsing of the hierarchy that WebGL encodes - // in the uniform names. - - function addUniform( container, uniformObject ) { - - container.seq.push( uniformObject ); - container.map[ uniformObject.id ] = uniformObject; - - } - - function parseUniform( activeInfo, addr, container ) { - - const path = activeInfo.name, - pathLength = path.length; - - // reset RegExp object, because of the early exit of a previous run - RePathPart.lastIndex = 0; - - while ( true ) { - - const match = RePathPart.exec( path ), - matchEnd = RePathPart.lastIndex; - - let id = match[ 1 ]; - const idIsIndex = match[ 2 ] === ']', - subscript = match[ 3 ]; - - if ( idIsIndex ) id = id | 0; // convert to integer - - if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { - - // bare name or "pure" bottom-level array "[0]" suffix - - addUniform( container, subscript === undefined ? - new SingleUniform( id, activeInfo, addr ) : - new PureArrayUniform( id, activeInfo, addr ) ); - - break; - - } else { - - // step into inner node / create it in case it doesn't exist - - const map = container.map; - let next = map[ id ]; - - if ( next === undefined ) { - - next = new StructuredUniform( id ); - addUniform( container, next ); - - } - - container = next; - - } - - } - - } - - // Root Container - - function WebGLUniforms( gl, program ) { - - this.seq = []; - this.map = {}; - - const n = gl.getProgramParameter( program, 35718 ); - - for ( let i = 0; i < n; ++ i ) { - - const info = gl.getActiveUniform( program, i ), - addr = gl.getUniformLocation( program, info.name ); - - parseUniform( info, addr, this ); - - } - - } - - WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) { - - const u = this.map[ name ]; - - if ( u !== undefined ) u.setValue( gl, value, textures ); - - }; - - WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { - - const v = object[ name ]; - - if ( v !== undefined ) this.setValue( gl, name, v ); - - }; - - - // Static interface - - WebGLUniforms.upload = function ( gl, seq, values, textures ) { - - for ( let i = 0, n = seq.length; i !== n; ++ i ) { - - const u = seq[ i ], - v = values[ u.id ]; - - if ( v.needsUpdate !== false ) { - - // note: always updating when .needsUpdate is undefined - u.setValue( gl, v.value, textures ); - - } - - } - - }; - - WebGLUniforms.seqWithValue = function ( seq, values ) { - - const r = []; - - for ( let i = 0, n = seq.length; i !== n; ++ i ) { - - const u = seq[ i ]; - if ( u.id in values ) r.push( u ); - - } - - return r; - - }; - - function WebGLShader( gl, type, string ) { - - const shader = gl.createShader( type ); - - gl.shaderSource( shader, string ); - gl.compileShader( shader ); - - return shader; - - } - - let programIdCount = 0; - - function addLineNumbers( string ) { - - const lines = string.split( '\n' ); - - for ( let i = 0; i < lines.length; i ++ ) { - - lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; - - } - - return lines.join( '\n' ); - - } - - function getEncodingComponents( encoding ) { - - switch ( encoding ) { - - case LinearEncoding: - return [ 'Linear', '( value )' ]; - case sRGBEncoding: - return [ 'sRGB', '( value )' ]; - case RGBEEncoding: - return [ 'RGBE', '( value )' ]; - case RGBM7Encoding: - return [ 'RGBM', '( value, 7.0 )' ]; - case RGBM16Encoding: - return [ 'RGBM', '( value, 16.0 )' ]; - case RGBDEncoding: - return [ 'RGBD', '( value, 256.0 )' ]; - case GammaEncoding: - return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ]; - case LogLuvEncoding: - return [ 'LogLuv', '( value )' ]; - default: - console.warn( 'THREE.WebGLProgram: Unsupported encoding:', encoding ); - return [ 'Linear', '( value )' ]; - - } - - } - - function getShaderErrors( gl, shader, type ) { - - const status = gl.getShaderParameter( shader, 35713 ); - const errors = gl.getShaderInfoLog( shader ).trim(); - - if ( status && errors === '' ) return ''; - - // --enable-privileged-webgl-extension - // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); - - return type.toUpperCase() + '\n\n' + errors + '\n\n' + addLineNumbers( gl.getShaderSource( shader ) ); - - } - - function getTexelDecodingFunction( functionName, encoding ) { - - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }'; - - } - - function getTexelEncodingFunction( functionName, encoding ) { - - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }'; - - } - - function getToneMappingFunction( functionName, toneMapping ) { - - let toneMappingName; - - switch ( toneMapping ) { - - case LinearToneMapping: - toneMappingName = 'Linear'; - break; - - case ReinhardToneMapping: - toneMappingName = 'Reinhard'; - break; - - case CineonToneMapping: - toneMappingName = 'OptimizedCineon'; - break; - - case ACESFilmicToneMapping: - toneMappingName = 'ACESFilmic'; - break; - - case CustomToneMapping: - toneMappingName = 'Custom'; - break; - - default: - console.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping ); - toneMappingName = 'Linear'; - - } - - return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; - - } - - function generateExtensions( parameters ) { - - const chunks = [ - ( parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '', - ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', - ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '', - ( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '' - ]; - - return chunks.filter( filterEmptyLine ).join( '\n' ); - - } - - function generateDefines( defines ) { - - const chunks = []; - - for ( const name in defines ) { - - const value = defines[ name ]; - - if ( value === false ) continue; - - chunks.push( '#define ' + name + ' ' + value ); - - } - - return chunks.join( '\n' ); - - } - - function fetchAttributeLocations( gl, program ) { - - const attributes = {}; - - const n = gl.getProgramParameter( program, 35721 ); - - for ( let i = 0; i < n; i ++ ) { - - const info = gl.getActiveAttrib( program, i ); - const name = info.name; - - let locationSize = 1; - if ( info.type === 35674 ) locationSize = 2; - if ( info.type === 35675 ) locationSize = 3; - if ( info.type === 35676 ) locationSize = 4; - - // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); - - attributes[ name ] = { - type: info.type, - location: gl.getAttribLocation( program, name ), - locationSize: locationSize - }; - - } - - return attributes; - - } - - function filterEmptyLine( string ) { - - return string !== ''; - - } - - function replaceLightNums( string, parameters ) { - - return string - .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) - .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) - .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) - .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) - .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) - .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) - .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) - .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); - - } - - function replaceClippingPlaneNums( string, parameters ) { - - return string - .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) - .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); - - } - - // Resolve Includes - - const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; - - function resolveIncludes( string ) { - - return string.replace( includePattern, includeReplacer ); - - } - - function includeReplacer( match, include ) { - - const string = ShaderChunk[ include ]; - - if ( string === undefined ) { - - throw new Error( 'Can not resolve #include <' + include + '>' ); - - } - - return resolveIncludes( string ); - - } - - // Unroll Loops - - const deprecatedUnrollLoopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; - const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g; - - function unrollLoops( string ) { - - return string - .replace( unrollLoopPattern, loopReplacer ) - .replace( deprecatedUnrollLoopPattern, deprecatedLoopReplacer ); - - } - - function deprecatedLoopReplacer( match, start, end, snippet ) { - - console.warn( 'WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.' ); - return loopReplacer( match, start, end, snippet ); - - } - - function loopReplacer( match, start, end, snippet ) { - - let string = ''; - - for ( let i = parseInt( start ); i < parseInt( end ); i ++ ) { - - string += snippet - .replace( /\[\s*i\s*\]/g, '[ ' + i + ' ]' ) - .replace( /UNROLLED_LOOP_INDEX/g, i ); - - } - - return string; - - } - - // - - function generatePrecision( parameters ) { - - let precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;'; - - if ( parameters.precision === 'highp' ) { - - precisionstring += '\n#define HIGH_PRECISION'; - - } else if ( parameters.precision === 'mediump' ) { - - precisionstring += '\n#define MEDIUM_PRECISION'; - - } else if ( parameters.precision === 'lowp' ) { - - precisionstring += '\n#define LOW_PRECISION'; - - } - - return precisionstring; - - } - - function generateShadowMapTypeDefine( parameters ) { - - let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; - - if ( parameters.shadowMapType === PCFShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; - - } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; - - } else if ( parameters.shadowMapType === VSMShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; - - } - - return shadowMapTypeDefine; - - } - - function generateEnvMapTypeDefine( parameters ) { - - let envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - - if ( parameters.envMap ) { - - switch ( parameters.envMapMode ) { - - case CubeReflectionMapping: - case CubeRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - break; - - case CubeUVReflectionMapping: - case CubeUVRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; - break; - - } - - } - - return envMapTypeDefine; - - } - - function generateEnvMapModeDefine( parameters ) { - - let envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; - - if ( parameters.envMap ) { - - switch ( parameters.envMapMode ) { - - case CubeRefractionMapping: - case CubeUVRefractionMapping: - - envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; - break; - - } - - } - - return envMapModeDefine; - - } - - function generateEnvMapBlendingDefine( parameters ) { - - let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; - - if ( parameters.envMap ) { - - switch ( parameters.combine ) { - - case MultiplyOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - break; - - case MixOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; - break; - - case AddOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; - break; - - } - - } - - return envMapBlendingDefine; - - } - - function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { - - // TODO Send this event to Three.js DevTools - // console.log( 'WebGLProgram', cacheKey ); - - const gl = renderer.getContext(); - - const defines = parameters.defines; - - let vertexShader = parameters.vertexShader; - let fragmentShader = parameters.fragmentShader; - - const shadowMapTypeDefine = generateShadowMapTypeDefine( parameters ); - const envMapTypeDefine = generateEnvMapTypeDefine( parameters ); - const envMapModeDefine = generateEnvMapModeDefine( parameters ); - const envMapBlendingDefine = generateEnvMapBlendingDefine( parameters ); - - - const gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; - - const customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters ); - - const customDefines = generateDefines( defines ); - - const program = gl.createProgram(); - - let prefixVertex, prefixFragment; - let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : ''; - - if ( parameters.isRawShaderMaterial ) { - - prefixVertex = [ - - customDefines - - ].filter( filterEmptyLine ).join( '\n' ); - - if ( prefixVertex.length > 0 ) { - - prefixVertex += '\n'; - - } - - prefixFragment = [ - - customExtensions, - customDefines - - ].filter( filterEmptyLine ).join( '\n' ); - - if ( prefixFragment.length > 0 ) { - - prefixFragment += '\n'; - - } - - } else { - - prefixVertex = [ - - generatePrecision( parameters ), - - '#define SHADER_NAME ' + parameters.shaderName, - - customDefines, - - parameters.instancing ? '#define USE_INSTANCING' : '', - parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', - - parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', - - '#define GAMMA_FACTOR ' + gammaFactorDefine, - - '#define MAX_BONES ' + parameters.maxBones, - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - - parameters.map ? '#define USE_MAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - - parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.specularIntensityMap ? '#define USE_SPECULARINTENSITYMAP' : '', - parameters.specularTintMap ? '#define USE_SPECULARTINTMAP' : '', - - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - - parameters.transmission ? '#define USE_TRANSMISSION' : '', - parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', - parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', - - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', - parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - - parameters.flatShading ? '#define FLAT_SHADED' : '', - - parameters.skinning ? '#define USE_SKINNING' : '', - parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', - - parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', - parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', - - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - - parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', - - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - - 'uniform mat4 modelMatrix;', - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform mat4 viewMatrix;', - 'uniform mat3 normalMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', - - '#ifdef USE_INSTANCING', - - ' attribute mat4 instanceMatrix;', - - '#endif', - - '#ifdef USE_INSTANCING_COLOR', - - ' attribute vec3 instanceColor;', - - '#endif', - - 'attribute vec3 position;', - 'attribute vec3 normal;', - 'attribute vec2 uv;', - - '#ifdef USE_TANGENT', - - ' attribute vec4 tangent;', - - '#endif', - - '#if defined( USE_COLOR_ALPHA )', - - ' attribute vec4 color;', - - '#elif defined( USE_COLOR )', - - ' attribute vec3 color;', - - '#endif', - - '#ifdef USE_MORPHTARGETS', - - ' attribute vec3 morphTarget0;', - ' attribute vec3 morphTarget1;', - ' attribute vec3 morphTarget2;', - ' attribute vec3 morphTarget3;', - - ' #ifdef USE_MORPHNORMALS', - - ' attribute vec3 morphNormal0;', - ' attribute vec3 morphNormal1;', - ' attribute vec3 morphNormal2;', - ' attribute vec3 morphNormal3;', - - ' #else', - - ' attribute vec3 morphTarget4;', - ' attribute vec3 morphTarget5;', - ' attribute vec3 morphTarget6;', - ' attribute vec3 morphTarget7;', - - ' #endif', - - '#endif', - - '#ifdef USE_SKINNING', - - ' attribute vec4 skinIndex;', - ' attribute vec4 skinWeight;', - - '#endif', - - '\n' - - ].filter( filterEmptyLine ).join( '\n' ); - - prefixFragment = [ - - customExtensions, - - generatePrecision( parameters ), - - '#define SHADER_NAME ' + parameters.shaderName, - - customDefines, - - '#define GAMMA_FACTOR ' + gammaFactorDefine, - - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - - parameters.map ? '#define USE_MAP' : '', - parameters.matcap ? '#define USE_MATCAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapTypeDefine : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.envMap ? '#define ' + envMapBlendingDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - - parameters.clearcoat ? '#define USE_CLEARCOAT' : '', - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.specularIntensityMap ? '#define USE_SPECULARINTENSITYMAP' : '', - parameters.specularTintMap ? '#define USE_SPECULARTINTMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - parameters.alphaTest ? '#define USE_ALPHATEST' : '', - - parameters.sheenTint ? '#define USE_SHEEN' : '', - parameters.transmission ? '#define USE_TRANSMISSION' : '', - parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', - parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', - - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', - parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - - parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', - - parameters.flatShading ? '#define FLAT_SHADED' : '', - - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', - - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - - parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', - - parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', - - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - - ( ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ) ? '#define TEXTURE_LOD_EXT' : '', - - 'uniform mat4 viewMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', - - ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', - ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below - ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', - - parameters.dithering ? '#define DITHERING' : '', - parameters.format === RGBFormat ? '#define OPAQUE' : '', - - ShaderChunk[ 'encodings_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below - parameters.map ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', - parameters.matcap ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '', - parameters.envMap ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', - parameters.emissiveMap ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', - parameters.specularTintMap ? getTexelDecodingFunction( 'specularTintMapTexelToLinear', parameters.specularTintMapEncoding ) : '', - parameters.lightMap ? getTexelDecodingFunction( 'lightMapTexelToLinear', parameters.lightMapEncoding ) : '', - getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ), - - parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', - - '\n' - - ].filter( filterEmptyLine ).join( '\n' ); - - } - - vertexShader = resolveIncludes( vertexShader ); - vertexShader = replaceLightNums( vertexShader, parameters ); - vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); - - fragmentShader = resolveIncludes( fragmentShader ); - fragmentShader = replaceLightNums( fragmentShader, parameters ); - fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); - - vertexShader = unrollLoops( vertexShader ); - fragmentShader = unrollLoops( fragmentShader ); - - if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { - - // GLSL 3.0 conversion for built-in materials and ShaderMaterial - - versionString = '#version 300 es\n'; - - prefixVertex = [ - '#define attribute in', - '#define varying out', - '#define texture2D texture' - ].join( '\n' ) + '\n' + prefixVertex; - - prefixFragment = [ - '#define varying in', - ( parameters.glslVersion === GLSL3 ) ? '' : 'out highp vec4 pc_fragColor;', - ( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor', - '#define gl_FragDepthEXT gl_FragDepth', - '#define texture2D texture', - '#define textureCube texture', - '#define texture2DProj textureProj', - '#define texture2DLodEXT textureLod', - '#define texture2DProjLodEXT textureProjLod', - '#define textureCubeLodEXT textureLod', - '#define texture2DGradEXT textureGrad', - '#define texture2DProjGradEXT textureProjGrad', - '#define textureCubeGradEXT textureGrad' - ].join( '\n' ) + '\n' + prefixFragment; - - } - - const vertexGlsl = versionString + prefixVertex + vertexShader; - const fragmentGlsl = versionString + prefixFragment + fragmentShader; - - // console.log( '*VERTEX*', vertexGlsl ); - // console.log( '*FRAGMENT*', fragmentGlsl ); - - const glVertexShader = WebGLShader( gl, 35633, vertexGlsl ); - const glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl ); - - gl.attachShader( program, glVertexShader ); - gl.attachShader( program, glFragmentShader ); - - // Force a particular attribute to index 0. - - if ( parameters.index0AttributeName !== undefined ) { - - gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); - - } else if ( parameters.morphTargets === true ) { - - // programs with morphTargets displace position out of attribute 0 - gl.bindAttribLocation( program, 0, 'position' ); - - } - - gl.linkProgram( program ); - - // check for link errors - if ( renderer.debug.checkShaderErrors ) { - - const programLog = gl.getProgramInfoLog( program ).trim(); - const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); - const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); - - let runnable = true; - let haveDiagnostics = true; - - if ( gl.getProgramParameter( program, 35714 ) === false ) { - - runnable = false; - - const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); - const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); - - console.error( - 'THREE.WebGLProgram: Shader Error ' + gl.getError() + ' - ' + - 'VALIDATE_STATUS ' + gl.getProgramParameter( program, 35715 ) + '\n\n' + - 'Program Info Log: ' + programLog + '\n' + - vertexErrors + '\n' + - fragmentErrors - ); - - } else if ( programLog !== '' ) { - - console.warn( 'THREE.WebGLProgram: Program Info Log:', programLog ); - - } else if ( vertexLog === '' || fragmentLog === '' ) { - - haveDiagnostics = false; - - } - - if ( haveDiagnostics ) { - - this.diagnostics = { - - runnable: runnable, - - programLog: programLog, - - vertexShader: { - - log: vertexLog, - prefix: prefixVertex - - }, - - fragmentShader: { - - log: fragmentLog, - prefix: prefixFragment - - } - - }; - - } - - } - - // Clean up - - // Crashes in iOS9 and iOS10. #18402 - // gl.detachShader( program, glVertexShader ); - // gl.detachShader( program, glFragmentShader ); - - gl.deleteShader( glVertexShader ); - gl.deleteShader( glFragmentShader ); - - // set up caching for uniform locations - - let cachedUniforms; - - this.getUniforms = function () { - - if ( cachedUniforms === undefined ) { - - cachedUniforms = new WebGLUniforms( gl, program ); - - } - - return cachedUniforms; - - }; - - // set up caching for attribute locations - - let cachedAttributes; - - this.getAttributes = function () { - - if ( cachedAttributes === undefined ) { - - cachedAttributes = fetchAttributeLocations( gl, program ); - - } - - return cachedAttributes; - - }; - - // free resource - - this.destroy = function () { - - bindingStates.releaseStatesOfProgram( this ); - - gl.deleteProgram( program ); - this.program = undefined; - - }; - - // - - this.name = parameters.shaderName; - this.id = programIdCount ++; - this.cacheKey = cacheKey; - this.usedTimes = 1; - this.program = program; - this.vertexShader = glVertexShader; - this.fragmentShader = glFragmentShader; - - return this; - - } - - function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ) { - - const programs = []; - - const isWebGL2 = capabilities.isWebGL2; - const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; - const floatVertexTextures = capabilities.floatVertexTextures; - const maxVertexUniforms = capabilities.maxVertexUniforms; - const vertexTextures = capabilities.vertexTextures; - - let precision = capabilities.precision; - - const shaderIDs = { - MeshDepthMaterial: 'depth', - MeshDistanceMaterial: 'distanceRGBA', - MeshNormalMaterial: 'normal', - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - MeshToonMaterial: 'toon', - MeshStandardMaterial: 'physical', - MeshPhysicalMaterial: 'physical', - MeshMatcapMaterial: 'matcap', - LineBasicMaterial: 'basic', - LineDashedMaterial: 'dashed', - PointsMaterial: 'points', - ShadowMaterial: 'shadow', - SpriteMaterial: 'sprite' - }; - - const parameterNames = [ - 'precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor', - 'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV', - 'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap', - 'objectSpaceNormalMap', 'tangentSpaceNormalMap', - 'clearcoat', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', - 'displacementMap', - 'specularMap', 'specularIntensityMap', 'specularTintMap', 'specularTintMapEncoding', 'roughnessMap', 'metalnessMap', 'gradientMap', - 'alphaMap', 'alphaTest', 'combine', 'vertexColors', 'vertexAlphas', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2', - 'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning', - 'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals', 'premultipliedAlpha', - 'numDirLights', 'numPointLights', 'numSpotLights', 'numHemiLights', 'numRectAreaLights', - 'numDirLightShadows', 'numPointLightShadows', 'numSpotLightShadows', - 'shadowMapEnabled', 'shadowMapType', 'toneMapping', 'physicallyCorrectLights', - 'doubleSided', 'flipSided', 'numClippingPlanes', 'numClipIntersection', 'depthPacking', 'dithering', 'format', - 'sheenTint', 'transmission', 'transmissionMap', 'thicknessMap' - ]; - - function getMaxBones( object ) { - - const skeleton = object.skeleton; - const bones = skeleton.bones; - - if ( floatVertexTextures ) { - - return 1024; - - } else { - - // default for when object is not specified - // ( for example when prebuilding shader to be used with multiple objects ) - // - // - leave some extra space for other uniforms - // - limit here is ANGLE's 254 max uniform vectors - // (up to 54 should be safe) - - const nVertexUniforms = maxVertexUniforms; - const nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); - - const maxBones = Math.min( nVertexMatrices, bones.length ); - - if ( maxBones < bones.length ) { - - console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); - return 0; - - } - - return maxBones; - - } - - } - - function getTextureEncodingFromMap( map ) { - - let encoding; - - if ( map && map.isTexture ) { - - encoding = map.encoding; - - } else if ( map && map.isWebGLRenderTarget ) { - - console.warn( 'THREE.WebGLPrograms.getTextureEncodingFromMap: don\'t use render targets as textures. Use their .texture property instead.' ); - encoding = map.texture.encoding; - - } else { - - encoding = LinearEncoding; - - } - - return encoding; - - } - - function getParameters( material, lights, shadows, scene, object ) { - - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; - - const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); - - const shaderID = shaderIDs[ material.type ]; - - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) - - const maxBones = object.isSkinnedMesh ? getMaxBones( object ) : 0; - - if ( material.precision !== null ) { - - precision = capabilities.getMaxPrecision( material.precision ); - - if ( precision !== material.precision ) { - - console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); - - } - - } - - let vertexShader, fragmentShader; - - if ( shaderID ) { - - const shader = ShaderLib[ shaderID ]; - - vertexShader = shader.vertexShader; - fragmentShader = shader.fragmentShader; - - } else { - - vertexShader = material.vertexShader; - fragmentShader = material.fragmentShader; - - } - - const currentRenderTarget = renderer.getRenderTarget(); - - const useAlphaTest = material.alphaTest > 0; - const useClearcoat = material.clearcoat > 0; - - const parameters = { - - isWebGL2: isWebGL2, - - shaderID: shaderID, - shaderName: material.type, - - vertexShader: vertexShader, - fragmentShader: fragmentShader, - defines: material.defines, - - isRawShaderMaterial: material.isRawShaderMaterial === true, - glslVersion: material.glslVersion, - - precision: precision, - - instancing: object.isInstancedMesh === true, - instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, - - supportsVertexTextures: vertexTextures, - outputEncoding: ( currentRenderTarget !== null ) ? getTextureEncodingFromMap( currentRenderTarget.texture ) : renderer.outputEncoding, - map: !! material.map, - mapEncoding: getTextureEncodingFromMap( material.map ), - matcap: !! material.matcap, - matcapEncoding: getTextureEncodingFromMap( material.matcap ), - envMap: !! envMap, - envMapMode: envMap && envMap.mapping, - envMapEncoding: getTextureEncodingFromMap( envMap ), - envMapCubeUV: ( !! envMap ) && ( ( envMap.mapping === CubeUVReflectionMapping ) || ( envMap.mapping === CubeUVRefractionMapping ) ), - lightMap: !! material.lightMap, - lightMapEncoding: getTextureEncodingFromMap( material.lightMap ), - aoMap: !! material.aoMap, - emissiveMap: !! material.emissiveMap, - emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap ), - bumpMap: !! material.bumpMap, - normalMap: !! material.normalMap, - objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, - tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, - - clearcoat: useClearcoat, - clearcoatMap: useClearcoat && !! material.clearcoatMap, - clearcoatRoughnessMap: useClearcoat && !! material.clearcoatRoughnessMap, - clearcoatNormalMap: useClearcoat && !! material.clearcoatNormalMap, - - displacementMap: !! material.displacementMap, - roughnessMap: !! material.roughnessMap, - metalnessMap: !! material.metalnessMap, - specularMap: !! material.specularMap, - specularIntensityMap: !! material.specularIntensityMap, - specularTintMap: !! material.specularTintMap, - specularTintMapEncoding: getTextureEncodingFromMap( material.specularTintMap ), - - alphaMap: !! material.alphaMap, - alphaTest: useAlphaTest, - - gradientMap: !! material.gradientMap, - - sheenTint: ( !! material.sheenTint && ( material.sheenTint.r > 0 || material.sheenTint.g > 0 || material.sheenTint.b > 0 ) ), - - transmission: material.transmission > 0, - transmissionMap: !! material.transmissionMap, - thicknessMap: !! material.thicknessMap, - - combine: material.combine, - - vertexTangents: ( !! material.normalMap && !! object.geometry && !! object.geometry.attributes.tangent ), - vertexColors: material.vertexColors, - vertexAlphas: material.vertexColors === true && !! object.geometry && !! object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4, - vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap || !! material.thicknessMap || !! material.specularIntensityMap || !! material.specularTintMap, - uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || material.transmission > 0 || !! material.transmissionMap || !! material.thicknessMap || !! material.specularIntensityMap || !! material.specularTintMap ) && !! material.displacementMap, - - fog: !! fog, - useFog: material.fog, - fogExp2: ( fog && fog.isFogExp2 ), - - flatShading: !! material.flatShading, - - sizeAttenuation: material.sizeAttenuation, - logarithmicDepthBuffer: logarithmicDepthBuffer, - - skinning: object.isSkinnedMesh === true && maxBones > 0, - maxBones: maxBones, - useVertexTexture: floatVertexTextures, - - morphTargets: !! object.geometry && !! object.geometry.morphAttributes.position, - morphNormals: !! object.geometry && !! object.geometry.morphAttributes.normal, - - numDirLights: lights.directional.length, - numPointLights: lights.point.length, - numSpotLights: lights.spot.length, - numRectAreaLights: lights.rectArea.length, - numHemiLights: lights.hemi.length, - - numDirLightShadows: lights.directionalShadowMap.length, - numPointLightShadows: lights.pointShadowMap.length, - numSpotLightShadows: lights.spotShadowMap.length, - - numClippingPlanes: clipping.numPlanes, - numClipIntersection: clipping.numIntersection, - - format: material.format, - dithering: material.dithering, - - shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, - shadowMapType: renderer.shadowMap.type, - - toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, - physicallyCorrectLights: renderer.physicallyCorrectLights, - - premultipliedAlpha: material.premultipliedAlpha, - - doubleSided: material.side === DoubleSide, - flipSided: material.side === BackSide, - - depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false, - - index0AttributeName: material.index0AttributeName, - - extensionDerivatives: material.extensions && material.extensions.derivatives, - extensionFragDepth: material.extensions && material.extensions.fragDepth, - extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, - extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, - - rendererExtensionFragDepth: isWebGL2 || extensions.has( 'EXT_frag_depth' ), - rendererExtensionDrawBuffers: isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ), - rendererExtensionShaderTextureLod: isWebGL2 || extensions.has( 'EXT_shader_texture_lod' ), - - customProgramCacheKey: material.customProgramCacheKey() - - }; - - return parameters; - - } - - function getProgramCacheKey( parameters ) { - - const array = []; - - if ( parameters.shaderID ) { - - array.push( parameters.shaderID ); - - } else { - - array.push( parameters.fragmentShader ); - array.push( parameters.vertexShader ); - - } - - if ( parameters.defines !== undefined ) { - - for ( const name in parameters.defines ) { - - array.push( name ); - array.push( parameters.defines[ name ] ); - - } - - } - - if ( parameters.isRawShaderMaterial === false ) { - - for ( let i = 0; i < parameterNames.length; i ++ ) { - - array.push( parameters[ parameterNames[ i ] ] ); - - } - - array.push( renderer.outputEncoding ); - array.push( renderer.gammaFactor ); - - } - - array.push( parameters.customProgramCacheKey ); - - return array.join(); - - } - - function getUniforms( material ) { - - const shaderID = shaderIDs[ material.type ]; - let uniforms; - - if ( shaderID ) { - - const shader = ShaderLib[ shaderID ]; - uniforms = UniformsUtils.clone( shader.uniforms ); - - } else { - - uniforms = material.uniforms; - - } - - return uniforms; - - } - - function acquireProgram( parameters, cacheKey ) { - - let program; - - // Check if code has been already compiled - for ( let p = 0, pl = programs.length; p < pl; p ++ ) { - - const preexistingProgram = programs[ p ]; - - if ( preexistingProgram.cacheKey === cacheKey ) { - - program = preexistingProgram; - ++ program.usedTimes; - - break; - - } - - } - - if ( program === undefined ) { - - program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); - programs.push( program ); - - } - - return program; - - } - - function releaseProgram( program ) { - - if ( -- program.usedTimes === 0 ) { - - // Remove from unordered set - const i = programs.indexOf( program ); - programs[ i ] = programs[ programs.length - 1 ]; - programs.pop(); - - // Free WebGL resources - program.destroy(); - - } - - } - - return { - getParameters: getParameters, - getProgramCacheKey: getProgramCacheKey, - getUniforms: getUniforms, - acquireProgram: acquireProgram, - releaseProgram: releaseProgram, - // Exposed for resource monitoring & error feedback via renderer.info: - programs: programs - }; - - } - - function WebGLProperties() { - - let properties = new WeakMap(); - - function get( object ) { - - let map = properties.get( object ); - - if ( map === undefined ) { - - map = {}; - properties.set( object, map ); - - } - - return map; - - } - - function remove( object ) { - - properties.delete( object ); - - } - - function update( object, key, value ) { - - properties.get( object )[ key ] = value; - - } - - function dispose() { - - properties = new WeakMap(); - - } - - return { - get: get, - remove: remove, - update: update, - dispose: dispose - }; - - } - - function painterSortStable( a, b ) { - - if ( a.groupOrder !== b.groupOrder ) { - - return a.groupOrder - b.groupOrder; - - } else if ( a.renderOrder !== b.renderOrder ) { - - return a.renderOrder - b.renderOrder; - - } else if ( a.program !== b.program ) { - - return a.program.id - b.program.id; - - } else if ( a.material.id !== b.material.id ) { - - return a.material.id - b.material.id; - - } else if ( a.z !== b.z ) { - - return a.z - b.z; - - } else { - - return a.id - b.id; - - } - - } - - function reversePainterSortStable( a, b ) { - - if ( a.groupOrder !== b.groupOrder ) { - - return a.groupOrder - b.groupOrder; - - } else if ( a.renderOrder !== b.renderOrder ) { - - return a.renderOrder - b.renderOrder; - - } else if ( a.z !== b.z ) { - - return b.z - a.z; - - } else { - - return a.id - b.id; - - } - - } - - - function WebGLRenderList( properties ) { - - const renderItems = []; - let renderItemsIndex = 0; - - const opaque = []; - const transmissive = []; - const transparent = []; - - const defaultProgram = { id: - 1 }; - - function init() { - - renderItemsIndex = 0; - - opaque.length = 0; - transmissive.length = 0; - transparent.length = 0; - - } - - function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { - - let renderItem = renderItems[ renderItemsIndex ]; - const materialProperties = properties.get( material ); - - if ( renderItem === undefined ) { - - renderItem = { - id: object.id, - object: object, - geometry: geometry, - material: material, - program: materialProperties.program || defaultProgram, - groupOrder: groupOrder, - renderOrder: object.renderOrder, - z: z, - group: group - }; - - renderItems[ renderItemsIndex ] = renderItem; - - } else { - - renderItem.id = object.id; - renderItem.object = object; - renderItem.geometry = geometry; - renderItem.material = material; - renderItem.program = materialProperties.program || defaultProgram; - renderItem.groupOrder = groupOrder; - renderItem.renderOrder = object.renderOrder; - renderItem.z = z; - renderItem.group = group; - - } - - renderItemsIndex ++; - - return renderItem; - - } - - function push( object, geometry, material, groupOrder, z, group ) { - - const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - - if ( material.transmission > 0.0 ) { - - transmissive.push( renderItem ); - - } else if ( material.transparent === true ) { - - transparent.push( renderItem ); - - } else { - - opaque.push( renderItem ); - - } - - } - - function unshift( object, geometry, material, groupOrder, z, group ) { - - const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - - if ( material.transmission > 0.0 ) { - - transmissive.unshift( renderItem ); - - } else if ( material.transparent === true ) { - - transparent.unshift( renderItem ); - - } else { - - opaque.unshift( renderItem ); - - } - - } - - function sort( customOpaqueSort, customTransparentSort ) { - - if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); - if ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable ); - if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); - - } - - function finish() { - - // Clear references from inactive renderItems in the list - - for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { - - const renderItem = renderItems[ i ]; - - if ( renderItem.id === null ) break; - - renderItem.id = null; - renderItem.object = null; - renderItem.geometry = null; - renderItem.material = null; - renderItem.program = null; - renderItem.group = null; - - } - - } - - return { - - opaque: opaque, - transmissive: transmissive, - transparent: transparent, - - init: init, - push: push, - unshift: unshift, - finish: finish, - - sort: sort - }; - - } - - function WebGLRenderLists( properties ) { - - let lists = new WeakMap(); - - function get( scene, renderCallDepth ) { - - let list; - - if ( lists.has( scene ) === false ) { - - list = new WebGLRenderList( properties ); - lists.set( scene, [ list ] ); - - } else { - - if ( renderCallDepth >= lists.get( scene ).length ) { - - list = new WebGLRenderList( properties ); - lists.get( scene ).push( list ); - - } else { - - list = lists.get( scene )[ renderCallDepth ]; - - } - - } - - return list; - - } - - function dispose() { - - lists = new WeakMap(); - - } - - return { - get: get, - dispose: dispose - }; - - } - - function UniformsCache() { - - const lights = {}; - - return { - - get: function ( light ) { - - if ( lights[ light.id ] !== undefined ) { - - return lights[ light.id ]; - - } - - let uniforms; - - switch ( light.type ) { - - case 'DirectionalLight': - uniforms = { - direction: new Vector3(), - color: new Color() - }; - break; - - case 'SpotLight': - uniforms = { - position: new Vector3(), - direction: new Vector3(), - color: new Color(), - distance: 0, - coneCos: 0, - penumbraCos: 0, - decay: 0 - }; - break; - - case 'PointLight': - uniforms = { - position: new Vector3(), - color: new Color(), - distance: 0, - decay: 0 - }; - break; - - case 'HemisphereLight': - uniforms = { - direction: new Vector3(), - skyColor: new Color(), - groundColor: new Color() - }; - break; - - case 'RectAreaLight': - uniforms = { - color: new Color(), - position: new Vector3(), - halfWidth: new Vector3(), - halfHeight: new Vector3() - }; - break; - - } - - lights[ light.id ] = uniforms; - - return uniforms; - - } - - }; - - } - - function ShadowUniformsCache() { - - const lights = {}; - - return { - - get: function ( light ) { - - if ( lights[ light.id ] !== undefined ) { - - return lights[ light.id ]; - - } - - let uniforms; - - switch ( light.type ) { - - case 'DirectionalLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; - - case 'SpotLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; - - case 'PointLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2(), - shadowCameraNear: 1, - shadowCameraFar: 1000 - }; - break; - - // TODO (abelnation): set RectAreaLight shadow uniforms - - } - - lights[ light.id ] = uniforms; - - return uniforms; - - } - - }; - - } - - - - let nextVersion = 0; - - function shadowCastingLightsFirst( lightA, lightB ) { - - return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 ); - - } - - function WebGLLights( extensions, capabilities ) { - - const cache = new UniformsCache(); - - const shadowCache = ShadowUniformsCache(); - - const state = { - - version: 0, - - hash: { - directionalLength: - 1, - pointLength: - 1, - spotLength: - 1, - rectAreaLength: - 1, - hemiLength: - 1, - - numDirectionalShadows: - 1, - numPointShadows: - 1, - numSpotShadows: - 1 - }, - - ambient: [ 0, 0, 0 ], - probe: [], - directional: [], - directionalShadow: [], - directionalShadowMap: [], - directionalShadowMatrix: [], - spot: [], - spotShadow: [], - spotShadowMap: [], - spotShadowMatrix: [], - rectArea: [], - rectAreaLTC1: null, - rectAreaLTC2: null, - point: [], - pointShadow: [], - pointShadowMap: [], - pointShadowMatrix: [], - hemi: [] - - }; - - for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); - - const vector3 = new Vector3(); - const matrix4 = new Matrix4(); - const matrix42 = new Matrix4(); - - function setup( lights, physicallyCorrectLights ) { - - let r = 0, g = 0, b = 0; - - for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); - - let directionalLength = 0; - let pointLength = 0; - let spotLength = 0; - let rectAreaLength = 0; - let hemiLength = 0; - - let numDirectionalShadows = 0; - let numPointShadows = 0; - let numSpotShadows = 0; - - lights.sort( shadowCastingLightsFirst ); - - // artist-friendly light intensity scaling factor - const scaleFactor = ( physicallyCorrectLights !== true ) ? Math.PI : 1; - - for ( let i = 0, l = lights.length; i < l; i ++ ) { - - const light = lights[ i ]; - - const color = light.color; - const intensity = light.intensity; - const distance = light.distance; - - const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; - - if ( light.isAmbientLight ) { - - r += color.r * intensity * scaleFactor; - g += color.g * intensity * scaleFactor; - b += color.b * intensity * scaleFactor; - - } else if ( light.isLightProbe ) { - - for ( let j = 0; j < 9; j ++ ) { - - state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); - - } - - } else if ( light.isDirectionalLight ) { - - const uniforms = cache.get( light ); - - uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); - - if ( light.castShadow ) { - - const shadow = light.shadow; - - const shadowUniforms = shadowCache.get( light ); - - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - - state.directionalShadow[ directionalLength ] = shadowUniforms; - state.directionalShadowMap[ directionalLength ] = shadowMap; - state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; - - numDirectionalShadows ++; - - } - - state.directional[ directionalLength ] = uniforms; - - directionalLength ++; - - } else if ( light.isSpotLight ) { - - const uniforms = cache.get( light ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - - uniforms.color.copy( color ).multiplyScalar( intensity * scaleFactor ); - uniforms.distance = distance; - - uniforms.coneCos = Math.cos( light.angle ); - uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); - uniforms.decay = light.decay; - - if ( light.castShadow ) { - - const shadow = light.shadow; - - const shadowUniforms = shadowCache.get( light ); - - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - - state.spotShadow[ spotLength ] = shadowUniforms; - state.spotShadowMap[ spotLength ] = shadowMap; - state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; - - numSpotShadows ++; - - } - - state.spot[ spotLength ] = uniforms; - - spotLength ++; - - } else if ( light.isRectAreaLight ) { - - const uniforms = cache.get( light ); - - // (a) intensity is the total visible light emitted - //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); - - // (b) intensity is the brightness of the light - uniforms.color.copy( color ).multiplyScalar( intensity ); - - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - - state.rectArea[ rectAreaLength ] = uniforms; - - rectAreaLength ++; - - } else if ( light.isPointLight ) { - - const uniforms = cache.get( light ); - - uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); - uniforms.distance = light.distance; - uniforms.decay = light.decay; - - if ( light.castShadow ) { - - const shadow = light.shadow; - - const shadowUniforms = shadowCache.get( light ); - - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - shadowUniforms.shadowCameraNear = shadow.camera.near; - shadowUniforms.shadowCameraFar = shadow.camera.far; - - state.pointShadow[ pointLength ] = shadowUniforms; - state.pointShadowMap[ pointLength ] = shadowMap; - state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; - - numPointShadows ++; - - } - - state.point[ pointLength ] = uniforms; - - pointLength ++; - - } else if ( light.isHemisphereLight ) { - - const uniforms = cache.get( light ); - - uniforms.skyColor.copy( light.color ).multiplyScalar( intensity * scaleFactor ); - uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity * scaleFactor ); - - state.hemi[ hemiLength ] = uniforms; - - hemiLength ++; - - } - - } - - if ( rectAreaLength > 0 ) { - - if ( capabilities.isWebGL2 ) { - - // WebGL 2 - - state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; - state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; - - } else { - - // WebGL 1 - - if ( extensions.has( 'OES_texture_float_linear' ) === true ) { - - state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; - state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; - - } else if ( extensions.has( 'OES_texture_half_float_linear' ) === true ) { - - state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; - state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; - - } else { - - console.error( 'THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.' ); - - } - - } - - } - - state.ambient[ 0 ] = r; - state.ambient[ 1 ] = g; - state.ambient[ 2 ] = b; - - const hash = state.hash; - - if ( hash.directionalLength !== directionalLength || - hash.pointLength !== pointLength || - hash.spotLength !== spotLength || - hash.rectAreaLength !== rectAreaLength || - hash.hemiLength !== hemiLength || - hash.numDirectionalShadows !== numDirectionalShadows || - hash.numPointShadows !== numPointShadows || - hash.numSpotShadows !== numSpotShadows ) { - - state.directional.length = directionalLength; - state.spot.length = spotLength; - state.rectArea.length = rectAreaLength; - state.point.length = pointLength; - state.hemi.length = hemiLength; - - state.directionalShadow.length = numDirectionalShadows; - state.directionalShadowMap.length = numDirectionalShadows; - state.pointShadow.length = numPointShadows; - state.pointShadowMap.length = numPointShadows; - state.spotShadow.length = numSpotShadows; - state.spotShadowMap.length = numSpotShadows; - state.directionalShadowMatrix.length = numDirectionalShadows; - state.pointShadowMatrix.length = numPointShadows; - state.spotShadowMatrix.length = numSpotShadows; - - hash.directionalLength = directionalLength; - hash.pointLength = pointLength; - hash.spotLength = spotLength; - hash.rectAreaLength = rectAreaLength; - hash.hemiLength = hemiLength; - - hash.numDirectionalShadows = numDirectionalShadows; - hash.numPointShadows = numPointShadows; - hash.numSpotShadows = numSpotShadows; - - state.version = nextVersion ++; - - } - - } - - function setupView( lights, camera ) { - - let directionalLength = 0; - let pointLength = 0; - let spotLength = 0; - let rectAreaLength = 0; - let hemiLength = 0; - - const viewMatrix = camera.matrixWorldInverse; - - for ( let i = 0, l = lights.length; i < l; i ++ ) { - - const light = lights[ i ]; - - if ( light.isDirectionalLight ) { - - const uniforms = state.directional[ directionalLength ]; - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); - - directionalLength ++; - - } else if ( light.isSpotLight ) { - - const uniforms = state.spot[ spotLength ]; - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); - - spotLength ++; - - } else if ( light.isRectAreaLight ) { - - const uniforms = state.rectArea[ rectAreaLength ]; - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - // extract local rotation of light to derive width/height half vectors - matrix42.identity(); - matrix4.copy( light.matrixWorld ); - matrix4.premultiply( viewMatrix ); - matrix42.extractRotation( matrix4 ); - - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - - uniforms.halfWidth.applyMatrix4( matrix42 ); - uniforms.halfHeight.applyMatrix4( matrix42 ); - - rectAreaLength ++; - - } else if ( light.isPointLight ) { - - const uniforms = state.point[ pointLength ]; - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - pointLength ++; - - } else if ( light.isHemisphereLight ) { - - const uniforms = state.hemi[ hemiLength ]; - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - uniforms.direction.transformDirection( viewMatrix ); - uniforms.direction.normalize(); - - hemiLength ++; - - } - - } - - } - - return { - setup: setup, - setupView: setupView, - state: state - }; - - } - - function WebGLRenderState( extensions, capabilities ) { - - const lights = new WebGLLights( extensions, capabilities ); - - const lightsArray = []; - const shadowsArray = []; - - function init() { - - lightsArray.length = 0; - shadowsArray.length = 0; - - } - - function pushLight( light ) { - - lightsArray.push( light ); - - } - - function pushShadow( shadowLight ) { - - shadowsArray.push( shadowLight ); - - } - - function setupLights( physicallyCorrectLights ) { - - lights.setup( lightsArray, physicallyCorrectLights ); - - } - - function setupLightsView( camera ) { - - lights.setupView( lightsArray, camera ); - - } - - const state = { - lightsArray: lightsArray, - shadowsArray: shadowsArray, - - lights: lights - }; - - return { - init: init, - state: state, - setupLights: setupLights, - setupLightsView: setupLightsView, - - pushLight: pushLight, - pushShadow: pushShadow - }; - - } - - function WebGLRenderStates( extensions, capabilities ) { - - let renderStates = new WeakMap(); - - function get( scene, renderCallDepth = 0 ) { - - let renderState; - - if ( renderStates.has( scene ) === false ) { - - renderState = new WebGLRenderState( extensions, capabilities ); - renderStates.set( scene, [ renderState ] ); - - } else { - - if ( renderCallDepth >= renderStates.get( scene ).length ) { - - renderState = new WebGLRenderState( extensions, capabilities ); - renderStates.get( scene ).push( renderState ); - - } else { - - renderState = renderStates.get( scene )[ renderCallDepth ]; - - } - - } - - return renderState; - - } - - function dispose() { - - renderStates = new WeakMap(); - - } - - return { - get: get, - dispose: dispose - }; - - } - - /** - * parameters = { - * - * opacity: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ - - class MeshDepthMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.type = 'MeshDepthMaterial'; - - this.depthPacking = BasicDepthPacking; - - this.map = null; - - this.alphaMap = null; - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; - - this.setValues( parameters ); - - } - - copy( source ) { - - super.copy( source ); - - this.depthPacking = source.depthPacking; - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - return this; - - } - - } - - MeshDepthMaterial.prototype.isMeshDepthMaterial = true; - - /** - * parameters = { - * - * referencePosition: , - * nearDistance: , - * farDistance: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: - * - * } - */ - - class MeshDistanceMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.type = 'MeshDistanceMaterial'; - - this.referencePosition = new Vector3(); - this.nearDistance = 1; - this.farDistance = 1000; - - this.map = null; - - this.alphaMap = null; - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.fog = false; - - this.setValues( parameters ); - - } - - copy( source ) { - - super.copy( source ); - - this.referencePosition.copy( source.referencePosition ); - this.nearDistance = source.nearDistance; - this.farDistance = source.farDistance; - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - return this; - - } - - } - - MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; - - var vsm_frag = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\nuniform float samples;\n#include \nvoid main() {\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 );\n\tfloat uvStart = samples <= 1.0 ? 0.0 : - 1.0;\n\tfor ( float i = 0.0; i < samples; i ++ ) {\n\t\tfloat uvOffset = uvStart + i * uvStride;\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean / samples;\n\tsquared_mean = squared_mean / samples;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; - - var vsm_vert = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; - - function WebGLShadowMap( _renderer, _objects, _capabilities ) { - - let _frustum = new Frustum(); - - const _shadowMapSize = new Vector2(), - _viewportSize = new Vector2(), - - _viewport = new Vector4(), - - _depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ), - _distanceMaterial = new MeshDistanceMaterial(), - - _materialCache = {}, - - _maxTextureSize = _capabilities.maxTextureSize; - - const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; - - const shadowMaterialVertical = new ShaderMaterial( { - - uniforms: { - shadow_pass: { value: null }, - resolution: { value: new Vector2() }, - radius: { value: 4.0 }, - samples: { value: 8.0 } - }, - - vertexShader: vsm_vert, - - fragmentShader: vsm_frag - - } ); - - const shadowMaterialHorizontal = shadowMaterialVertical.clone(); - shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; - - const fullScreenTri = new BufferGeometry(); - fullScreenTri.setAttribute( - 'position', - new BufferAttribute( - new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), - 3 - ) - ); - - const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); - - const scope = this; - - this.enabled = false; - - this.autoUpdate = true; - this.needsUpdate = false; - - this.type = PCFShadowMap; - - this.render = function ( lights, scene, camera ) { - - if ( scope.enabled === false ) return; - if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; - - if ( lights.length === 0 ) return; - - const currentRenderTarget = _renderer.getRenderTarget(); - const activeCubeFace = _renderer.getActiveCubeFace(); - const activeMipmapLevel = _renderer.getActiveMipmapLevel(); - - const _state = _renderer.state; - - // Set GL state for depth map. - _state.setBlending( NoBlending ); - _state.buffers.color.setClear( 1, 1, 1, 1 ); - _state.buffers.depth.setTest( true ); - _state.setScissorTest( false ); - - // render depth map - - for ( let i = 0, il = lights.length; i < il; i ++ ) { - - const light = lights[ i ]; - const shadow = light.shadow; - - if ( shadow === undefined ) { - - console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); - continue; - - } - - if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; - - _shadowMapSize.copy( shadow.mapSize ); - - const shadowFrameExtents = shadow.getFrameExtents(); - - _shadowMapSize.multiply( shadowFrameExtents ); - - _viewportSize.copy( shadow.mapSize ); - - if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { - - if ( _shadowMapSize.x > _maxTextureSize ) { - - _viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x ); - _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; - shadow.mapSize.x = _viewportSize.x; - - } - - if ( _shadowMapSize.y > _maxTextureSize ) { - - _viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y ); - _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; - shadow.mapSize.y = _viewportSize.y; - - } - - } - - if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - - const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; - - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + '.shadowMap'; - - shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - - shadow.camera.updateProjectionMatrix(); - - } - - if ( shadow.map === null ) { - - const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; - - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + '.shadowMap'; - - shadow.camera.updateProjectionMatrix(); - - } - - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - - const viewportCount = shadow.getViewportCount(); - - for ( let vp = 0; vp < viewportCount; vp ++ ) { - - const viewport = shadow.getViewport( vp ); - - _viewport.set( - _viewportSize.x * viewport.x, - _viewportSize.y * viewport.y, - _viewportSize.x * viewport.z, - _viewportSize.y * viewport.w - ); - - _state.viewport( _viewport ); - - shadow.updateMatrices( light, vp ); - - _frustum = shadow.getFrustum(); - - renderObject( scene, camera, shadow.camera, light, this.type ); - - } - - // do blur pass for VSM - - if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - - VSMPass( shadow, camera ); - - } - - shadow.needsUpdate = false; - - } - - scope.needsUpdate = false; - - _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); - - }; - - function VSMPass( shadow, camera ) { - - const geometry = _objects.update( fullScreenMesh ); - - // vertical pass - - shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; - shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; - shadowMaterialVertical.uniforms.radius.value = shadow.radius; - shadowMaterialVertical.uniforms.samples.value = shadow.blurSamples; - _renderer.setRenderTarget( shadow.mapPass ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); - - // horizontal pass - - shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; - shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; - shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; - shadowMaterialHorizontal.uniforms.samples.value = shadow.blurSamples; - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null ); - - } - - function getDepthMaterial( object, geometry, material, light, shadowCameraNear, shadowCameraFar, type ) { - - let result = null; - - const customMaterial = ( light.isPointLight === true ) ? object.customDistanceMaterial : object.customDepthMaterial; - - if ( customMaterial !== undefined ) { - - result = customMaterial; - - } else { - - result = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial; - - } - - if ( ( _renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 ) || - ( material.displacementMap && material.displacementScale !== 0 ) || - ( material.alphaMap && material.alphaTest > 0 ) ) { - - // in this case we need a unique material instance reflecting the - // appropriate state - - const keyA = result.uuid, keyB = material.uuid; - - let materialsForVariant = _materialCache[ keyA ]; - - if ( materialsForVariant === undefined ) { - - materialsForVariant = {}; - _materialCache[ keyA ] = materialsForVariant; - - } - - let cachedMaterial = materialsForVariant[ keyB ]; - - if ( cachedMaterial === undefined ) { - - cachedMaterial = result.clone(); - materialsForVariant[ keyB ] = cachedMaterial; - - } - - result = cachedMaterial; - - } - - result.visible = material.visible; - result.wireframe = material.wireframe; - - if ( type === VSMShadowMap ) { - - result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; - - } else { - - result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; - - } - - result.alphaMap = material.alphaMap; - result.alphaTest = material.alphaTest; - - result.clipShadows = material.clipShadows; - result.clippingPlanes = material.clippingPlanes; - result.clipIntersection = material.clipIntersection; - - result.displacementMap = material.displacementMap; - result.displacementScale = material.displacementScale; - result.displacementBias = material.displacementBias; - - result.wireframeLinewidth = material.wireframeLinewidth; - result.linewidth = material.linewidth; - - if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { - - result.referencePosition.setFromMatrixPosition( light.matrixWorld ); - result.nearDistance = shadowCameraNear; - result.farDistance = shadowCameraFar; - - } - - return result; - - } - - function renderObject( object, camera, shadowCamera, light, type ) { - - if ( object.visible === false ) return; - - const visible = object.layers.test( camera.layers ); - - if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { - - if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { - - object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - - const geometry = _objects.update( object ); - const material = object.material; - - if ( Array.isArray( material ) ) { - - const groups = geometry.groups; - - for ( let k = 0, kl = groups.length; k < kl; k ++ ) { - - const group = groups[ k ]; - const groupMaterial = material[ group.materialIndex ]; - - if ( groupMaterial && groupMaterial.visible ) { - - const depthMaterial = getDepthMaterial( object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); - - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); - - } - - } - - } else if ( material.visible ) { - - const depthMaterial = getDepthMaterial( object, geometry, material, light, shadowCamera.near, shadowCamera.far, type ); - - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); - - } - - } - - } - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - renderObject( children[ i ], camera, shadowCamera, light, type ); - - } - - } - - } - - function WebGLState( gl, extensions, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - function ColorBuffer() { - - let locked = false; - - const color = new Vector4(); - let currentColorMask = null; - const currentColorClear = new Vector4( 0, 0, 0, 0 ); - - return { - - setMask: function ( colorMask ) { - - if ( currentColorMask !== colorMask && ! locked ) { - - gl.colorMask( colorMask, colorMask, colorMask, colorMask ); - currentColorMask = colorMask; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( r, g, b, a, premultipliedAlpha ) { - - if ( premultipliedAlpha === true ) { - - r *= a; g *= a; b *= a; - - } - - color.set( r, g, b, a ); - - if ( currentColorClear.equals( color ) === false ) { - - gl.clearColor( r, g, b, a ); - currentColorClear.copy( color ); - - } - - }, - - reset: function () { - - locked = false; - - currentColorMask = null; - currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state - - } - - }; - - } - - function DepthBuffer() { - - let locked = false; - - let currentDepthMask = null; - let currentDepthFunc = null; - let currentDepthClear = null; - - return { - - setTest: function ( depthTest ) { - - if ( depthTest ) { - - enable( 2929 ); - - } else { - - disable( 2929 ); - - } - - }, - - setMask: function ( depthMask ) { - - if ( currentDepthMask !== depthMask && ! locked ) { - - gl.depthMask( depthMask ); - currentDepthMask = depthMask; - - } - - }, - - setFunc: function ( depthFunc ) { - - if ( currentDepthFunc !== depthFunc ) { - - if ( depthFunc ) { - - switch ( depthFunc ) { - - case NeverDepth: - - gl.depthFunc( 512 ); - break; - - case AlwaysDepth: - - gl.depthFunc( 519 ); - break; - - case LessDepth: - - gl.depthFunc( 513 ); - break; - - case LessEqualDepth: - - gl.depthFunc( 515 ); - break; - - case EqualDepth: - - gl.depthFunc( 514 ); - break; - - case GreaterEqualDepth: - - gl.depthFunc( 518 ); - break; - - case GreaterDepth: - - gl.depthFunc( 516 ); - break; - - case NotEqualDepth: - - gl.depthFunc( 517 ); - break; - - default: - - gl.depthFunc( 515 ); - - } - - } else { - - gl.depthFunc( 515 ); - - } - - currentDepthFunc = depthFunc; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( depth ) { - - if ( currentDepthClear !== depth ) { - - gl.clearDepth( depth ); - currentDepthClear = depth; - - } - - }, - - reset: function () { - - locked = false; - - currentDepthMask = null; - currentDepthFunc = null; - currentDepthClear = null; - - } - - }; - - } - - function StencilBuffer() { - - let locked = false; - - let currentStencilMask = null; - let currentStencilFunc = null; - let currentStencilRef = null; - let currentStencilFuncMask = null; - let currentStencilFail = null; - let currentStencilZFail = null; - let currentStencilZPass = null; - let currentStencilClear = null; - - return { - - setTest: function ( stencilTest ) { - - if ( ! locked ) { - - if ( stencilTest ) { - - enable( 2960 ); - - } else { - - disable( 2960 ); - - } - - } - - }, - - setMask: function ( stencilMask ) { - - if ( currentStencilMask !== stencilMask && ! locked ) { - - gl.stencilMask( stencilMask ); - currentStencilMask = stencilMask; - - } - - }, - - setFunc: function ( stencilFunc, stencilRef, stencilMask ) { - - if ( currentStencilFunc !== stencilFunc || - currentStencilRef !== stencilRef || - currentStencilFuncMask !== stencilMask ) { - - gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); - - currentStencilFunc = stencilFunc; - currentStencilRef = stencilRef; - currentStencilFuncMask = stencilMask; - - } - - }, - - setOp: function ( stencilFail, stencilZFail, stencilZPass ) { - - if ( currentStencilFail !== stencilFail || - currentStencilZFail !== stencilZFail || - currentStencilZPass !== stencilZPass ) { - - gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); - - currentStencilFail = stencilFail; - currentStencilZFail = stencilZFail; - currentStencilZPass = stencilZPass; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( stencil ) { - - if ( currentStencilClear !== stencil ) { - - gl.clearStencil( stencil ); - currentStencilClear = stencil; - - } - - }, - - reset: function () { - - locked = false; - - currentStencilMask = null; - currentStencilFunc = null; - currentStencilRef = null; - currentStencilFuncMask = null; - currentStencilFail = null; - currentStencilZFail = null; - currentStencilZPass = null; - currentStencilClear = null; - - } - - }; - - } - - // - - const colorBuffer = new ColorBuffer(); - const depthBuffer = new DepthBuffer(); - const stencilBuffer = new StencilBuffer(); - - let enabledCapabilities = {}; - - let xrFramebuffer = null; - let currentBoundFramebuffers = {}; - - let currentProgram = null; - - let currentBlendingEnabled = false; - let currentBlending = null; - let currentBlendEquation = null; - let currentBlendSrc = null; - let currentBlendDst = null; - let currentBlendEquationAlpha = null; - let currentBlendSrcAlpha = null; - let currentBlendDstAlpha = null; - let currentPremultipledAlpha = false; - - let currentFlipSided = null; - let currentCullFace = null; - - let currentLineWidth = null; - - let currentPolygonOffsetFactor = null; - let currentPolygonOffsetUnits = null; - - const maxTextures = gl.getParameter( 35661 ); - - let lineWidthAvailable = false; - let version = 0; - const glVersion = gl.getParameter( 7938 ); - - if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { - - version = parseFloat( /^WebGL (\d)/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 1.0 ); - - } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { - - version = parseFloat( /^OpenGL ES (\d)/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 2.0 ); - - } - - let currentTextureSlot = null; - let currentBoundTextures = {}; - - const scissorParam = gl.getParameter( 3088 ); - const viewportParam = gl.getParameter( 2978 ); - - const currentScissor = new Vector4().fromArray( scissorParam ); - const currentViewport = new Vector4().fromArray( viewportParam ); - - function createTexture( type, target, count ) { - - const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. - const texture = gl.createTexture(); - - gl.bindTexture( type, texture ); - gl.texParameteri( type, 10241, 9728 ); - gl.texParameteri( type, 10240, 9728 ); - - for ( let i = 0; i < count; i ++ ) { - - gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); - - } - - return texture; - - } - - const emptyTextures = {}; - emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); - emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); - - // init - - colorBuffer.setClear( 0, 0, 0, 1 ); - depthBuffer.setClear( 1 ); - stencilBuffer.setClear( 0 ); - - enable( 2929 ); - depthBuffer.setFunc( LessEqualDepth ); - - setFlipSided( false ); - setCullFace( CullFaceBack ); - enable( 2884 ); - - setBlending( NoBlending ); - - // - - function enable( id ) { - - if ( enabledCapabilities[ id ] !== true ) { - - gl.enable( id ); - enabledCapabilities[ id ] = true; - - } - - } - - function disable( id ) { - - if ( enabledCapabilities[ id ] !== false ) { - - gl.disable( id ); - enabledCapabilities[ id ] = false; - - } - - } - - function bindXRFramebuffer( framebuffer ) { - - if ( framebuffer !== xrFramebuffer ) { - - gl.bindFramebuffer( 36160, framebuffer ); - - xrFramebuffer = framebuffer; - - } - - } - - function bindFramebuffer( target, framebuffer ) { - - if ( framebuffer === null && xrFramebuffer !== null ) framebuffer = xrFramebuffer; // use active XR framebuffer if available - - if ( currentBoundFramebuffers[ target ] !== framebuffer ) { - - gl.bindFramebuffer( target, framebuffer ); - - currentBoundFramebuffers[ target ] = framebuffer; - - if ( isWebGL2 ) { - - // 36009 is equivalent to 36160 - - if ( target === 36009 ) { - - currentBoundFramebuffers[ 36160 ] = framebuffer; - - } - - if ( target === 36160 ) { - - currentBoundFramebuffers[ 36009 ] = framebuffer; - - } - - } - - return true; - - } - - return false; - - } - - function useProgram( program ) { - - if ( currentProgram !== program ) { - - gl.useProgram( program ); - - currentProgram = program; - - return true; - - } - - return false; - - } - - const equationToGL = { - [ AddEquation ]: 32774, - [ SubtractEquation ]: 32778, - [ ReverseSubtractEquation ]: 32779 - }; - - if ( isWebGL2 ) { - - equationToGL[ MinEquation ] = 32775; - equationToGL[ MaxEquation ] = 32776; - - } else { - - const extension = extensions.get( 'EXT_blend_minmax' ); - - if ( extension !== null ) { - - equationToGL[ MinEquation ] = extension.MIN_EXT; - equationToGL[ MaxEquation ] = extension.MAX_EXT; - - } - - } - - const factorToGL = { - [ ZeroFactor ]: 0, - [ OneFactor ]: 1, - [ SrcColorFactor ]: 768, - [ SrcAlphaFactor ]: 770, - [ SrcAlphaSaturateFactor ]: 776, - [ DstColorFactor ]: 774, - [ DstAlphaFactor ]: 772, - [ OneMinusSrcColorFactor ]: 769, - [ OneMinusSrcAlphaFactor ]: 771, - [ OneMinusDstColorFactor ]: 775, - [ OneMinusDstAlphaFactor ]: 773 - }; - - function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { - - if ( blending === NoBlending ) { - - if ( currentBlendingEnabled === true ) { - - disable( 3042 ); - currentBlendingEnabled = false; - - } - - return; - - } - - if ( currentBlendingEnabled === false ) { - - enable( 3042 ); - currentBlendingEnabled = true; - - } - - if ( blending !== CustomBlending ) { - - if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { - - if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { - - gl.blendEquation( 32774 ); - - currentBlendEquation = AddEquation; - currentBlendEquationAlpha = AddEquation; - - } - - if ( premultipliedAlpha ) { - - switch ( blending ) { - - case NormalBlending: - gl.blendFuncSeparate( 1, 771, 1, 771 ); - break; - - case AdditiveBlending: - gl.blendFunc( 1, 1 ); - break; - - case SubtractiveBlending: - gl.blendFuncSeparate( 0, 0, 769, 771 ); - break; - - case MultiplyBlending: - gl.blendFuncSeparate( 0, 768, 0, 770 ); - break; - - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; - - } - - } else { - - switch ( blending ) { - - case NormalBlending: - gl.blendFuncSeparate( 770, 771, 1, 771 ); - break; - - case AdditiveBlending: - gl.blendFunc( 770, 1 ); - break; - - case SubtractiveBlending: - gl.blendFunc( 0, 769 ); - break; - - case MultiplyBlending: - gl.blendFunc( 0, 768 ); - break; - - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; - - } - - } - - currentBlendSrc = null; - currentBlendDst = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; - - currentBlending = blending; - currentPremultipledAlpha = premultipliedAlpha; - - } - - return; - - } - - // custom blending - - blendEquationAlpha = blendEquationAlpha || blendEquation; - blendSrcAlpha = blendSrcAlpha || blendSrc; - blendDstAlpha = blendDstAlpha || blendDst; - - if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { - - gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); - - currentBlendEquation = blendEquation; - currentBlendEquationAlpha = blendEquationAlpha; - - } - - if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { - - gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); - - currentBlendSrc = blendSrc; - currentBlendDst = blendDst; - currentBlendSrcAlpha = blendSrcAlpha; - currentBlendDstAlpha = blendDstAlpha; - - } - - currentBlending = blending; - currentPremultipledAlpha = null; - - } - - function setMaterial( material, frontFaceCW ) { - - material.side === DoubleSide - ? disable( 2884 ) - : enable( 2884 ); - - let flipSided = ( material.side === BackSide ); - if ( frontFaceCW ) flipSided = ! flipSided; - - setFlipSided( flipSided ); - - ( material.blending === NormalBlending && material.transparent === false ) - ? setBlending( NoBlending ) - : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); - - depthBuffer.setFunc( material.depthFunc ); - depthBuffer.setTest( material.depthTest ); - depthBuffer.setMask( material.depthWrite ); - colorBuffer.setMask( material.colorWrite ); - - const stencilWrite = material.stencilWrite; - stencilBuffer.setTest( stencilWrite ); - if ( stencilWrite ) { - - stencilBuffer.setMask( material.stencilWriteMask ); - stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); - stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); - - } - - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - - material.alphaToCoverage === true - ? enable( 32926 ) - : disable( 32926 ); - - } - - // - - function setFlipSided( flipSided ) { - - if ( currentFlipSided !== flipSided ) { - - if ( flipSided ) { - - gl.frontFace( 2304 ); - - } else { - - gl.frontFace( 2305 ); - - } - - currentFlipSided = flipSided; - - } - - } - - function setCullFace( cullFace ) { - - if ( cullFace !== CullFaceNone ) { - - enable( 2884 ); - - if ( cullFace !== currentCullFace ) { - - if ( cullFace === CullFaceBack ) { - - gl.cullFace( 1029 ); - - } else if ( cullFace === CullFaceFront ) { - - gl.cullFace( 1028 ); - - } else { - - gl.cullFace( 1032 ); - - } - - } - - } else { - - disable( 2884 ); - - } - - currentCullFace = cullFace; - - } - - function setLineWidth( width ) { - - if ( width !== currentLineWidth ) { - - if ( lineWidthAvailable ) gl.lineWidth( width ); - - currentLineWidth = width; - - } - - } - - function setPolygonOffset( polygonOffset, factor, units ) { - - if ( polygonOffset ) { - - enable( 32823 ); - - if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { - - gl.polygonOffset( factor, units ); - - currentPolygonOffsetFactor = factor; - currentPolygonOffsetUnits = units; - - } - - } else { - - disable( 32823 ); - - } - - } - - function setScissorTest( scissorTest ) { - - if ( scissorTest ) { - - enable( 3089 ); - - } else { - - disable( 3089 ); - - } - - } - - // texture - - function activeTexture( webglSlot ) { - - if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1; - - if ( currentTextureSlot !== webglSlot ) { - - gl.activeTexture( webglSlot ); - currentTextureSlot = webglSlot; - - } - - } - - function bindTexture( webglType, webglTexture ) { - - if ( currentTextureSlot === null ) { - - activeTexture(); - - } - - let boundTexture = currentBoundTextures[ currentTextureSlot ]; - - if ( boundTexture === undefined ) { - - boundTexture = { type: undefined, texture: undefined }; - currentBoundTextures[ currentTextureSlot ] = boundTexture; - - } - - if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { - - gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); - - boundTexture.type = webglType; - boundTexture.texture = webglTexture; - - } - - } - - function unbindTexture() { - - const boundTexture = currentBoundTextures[ currentTextureSlot ]; - - if ( boundTexture !== undefined && boundTexture.type !== undefined ) { - - gl.bindTexture( boundTexture.type, null ); - - boundTexture.type = undefined; - boundTexture.texture = undefined; - - } - - } - - function compressedTexImage2D() { - - try { - - gl.compressedTexImage2D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( 'THREE.WebGLState:', error ); - - } - - } - - function texImage2D() { - - try { - - gl.texImage2D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( 'THREE.WebGLState:', error ); - - } - - } - - function texImage3D() { - - try { - - gl.texImage3D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( 'THREE.WebGLState:', error ); - - } - - } - - // - - function scissor( scissor ) { - - if ( currentScissor.equals( scissor ) === false ) { - - gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); - currentScissor.copy( scissor ); - - } - - } - - function viewport( viewport ) { - - if ( currentViewport.equals( viewport ) === false ) { - - gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); - currentViewport.copy( viewport ); - - } - - } - - // - - function reset() { - - // reset state - - gl.disable( 3042 ); - gl.disable( 2884 ); - gl.disable( 2929 ); - gl.disable( 32823 ); - gl.disable( 3089 ); - gl.disable( 2960 ); - gl.disable( 32926 ); - - gl.blendEquation( 32774 ); - gl.blendFunc( 1, 0 ); - gl.blendFuncSeparate( 1, 0, 1, 0 ); - - gl.colorMask( true, true, true, true ); - gl.clearColor( 0, 0, 0, 0 ); - - gl.depthMask( true ); - gl.depthFunc( 513 ); - gl.clearDepth( 1 ); - - gl.stencilMask( 0xffffffff ); - gl.stencilFunc( 519, 0, 0xffffffff ); - gl.stencilOp( 7680, 7680, 7680 ); - gl.clearStencil( 0 ); - - gl.cullFace( 1029 ); - gl.frontFace( 2305 ); - - gl.polygonOffset( 0, 0 ); - - gl.activeTexture( 33984 ); - - gl.bindFramebuffer( 36160, null ); - - if ( isWebGL2 === true ) { - - gl.bindFramebuffer( 36009, null ); - gl.bindFramebuffer( 36008, null ); - - } - - gl.useProgram( null ); - - gl.lineWidth( 1 ); - - gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); - gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); - - // reset internals - - enabledCapabilities = {}; - - currentTextureSlot = null; - currentBoundTextures = {}; - - xrFramebuffer = null; - currentBoundFramebuffers = {}; - - currentProgram = null; - - currentBlendingEnabled = false; - currentBlending = null; - currentBlendEquation = null; - currentBlendSrc = null; - currentBlendDst = null; - currentBlendEquationAlpha = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; - currentPremultipledAlpha = false; - - currentFlipSided = null; - currentCullFace = null; - - currentLineWidth = null; - - currentPolygonOffsetFactor = null; - currentPolygonOffsetUnits = null; - - currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); - currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); - - colorBuffer.reset(); - depthBuffer.reset(); - stencilBuffer.reset(); - - } - - return { - - buffers: { - color: colorBuffer, - depth: depthBuffer, - stencil: stencilBuffer - }, - - enable: enable, - disable: disable, - - bindFramebuffer: bindFramebuffer, - bindXRFramebuffer: bindXRFramebuffer, - - useProgram: useProgram, - - setBlending: setBlending, - setMaterial: setMaterial, - - setFlipSided: setFlipSided, - setCullFace: setCullFace, - - setLineWidth: setLineWidth, - setPolygonOffset: setPolygonOffset, - - setScissorTest: setScissorTest, - - activeTexture: activeTexture, - bindTexture: bindTexture, - unbindTexture: unbindTexture, - compressedTexImage2D: compressedTexImage2D, - texImage2D: texImage2D, - texImage3D: texImage3D, - - scissor: scissor, - viewport: viewport, - - reset: reset - - }; - - } - - function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { - - const isWebGL2 = capabilities.isWebGL2; - const maxTextures = capabilities.maxTextures; - const maxCubemapSize = capabilities.maxCubemapSize; - const maxTextureSize = capabilities.maxTextureSize; - const maxSamples = capabilities.maxSamples; - - const _videoTextures = new WeakMap(); - let _canvas; - - // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, - // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! - // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). - - let useOffscreenCanvas = false; - - try { - - useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' - && ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null; - - } catch ( err ) { - - // Ignore any errors - - } - - function createCanvas( width, height ) { - - // Use OffscreenCanvas when available. Specially needed in web workers - - return useOffscreenCanvas ? - new OffscreenCanvas( width, height ) : - document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - - } - - function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { - - let scale = 1; - - // handle case if texture exceeds max size - - if ( image.width > maxSize || image.height > maxSize ) { - - scale = maxSize / Math.max( image.width, image.height ); - - } - - // only perform resize if necessary - - if ( scale < 1 || needsPowerOfTwo === true ) { - - // only perform resize for certain image types - - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || - ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - - const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; - - const width = floor( scale * image.width ); - const height = floor( scale * image.height ); - - if ( _canvas === undefined ) _canvas = createCanvas( width, height ); - - // cube textures can't reuse the same canvas - - const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; - - canvas.width = width; - canvas.height = height; - - const context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, width, height ); - - console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); - - return canvas; - - } else { - - if ( 'data' in image ) { - - console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); - - } - - return image; - - } - - } - - return image; - - } - - function isPowerOfTwo$1( image ) { - - return isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ); - - } - - function textureNeedsPowerOfTwo( texture ) { - - if ( isWebGL2 ) return false; - - return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || - ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); - - } - - function textureNeedsGenerateMipmaps( texture, supportsMips ) { - - return texture.generateMipmaps && supportsMips && - texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; - - } - - function generateMipmap( target, texture, width, height, depth = 1 ) { - - _gl.generateMipmap( target ); - - const textureProperties = properties.get( texture ); - - textureProperties.__maxMipLevel = Math.log2( Math.max( width, height, depth ) ); - - } - - function getInternalFormat( internalFormatName, glFormat, glType ) { - - if ( isWebGL2 === false ) return glFormat; - - if ( internalFormatName !== null ) { - - if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; - - console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); - - } - - let internalFormat = glFormat; - - if ( glFormat === 6403 ) { - - if ( glType === 5126 ) internalFormat = 33326; - if ( glType === 5131 ) internalFormat = 33325; - if ( glType === 5121 ) internalFormat = 33321; - - } - - if ( glFormat === 6407 ) { - - if ( glType === 5126 ) internalFormat = 34837; - if ( glType === 5131 ) internalFormat = 34843; - if ( glType === 5121 ) internalFormat = 32849; - - } - - if ( glFormat === 6408 ) { - - if ( glType === 5126 ) internalFormat = 34836; - if ( glType === 5131 ) internalFormat = 34842; - if ( glType === 5121 ) internalFormat = 32856; - - } - - if ( internalFormat === 33325 || internalFormat === 33326 || - internalFormat === 34842 || internalFormat === 34836 ) { - - extensions.get( 'EXT_color_buffer_float' ); - - } - - return internalFormat; - - } - - // Fallback filters for non-power-of-2 textures - - function filterFallback( f ) { - - if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { - - return 9728; - - } - - return 9729; - - } - - // - - function onTextureDispose( event ) { - - const texture = event.target; - - texture.removeEventListener( 'dispose', onTextureDispose ); - - deallocateTexture( texture ); - - if ( texture.isVideoTexture ) { - - _videoTextures.delete( texture ); - - } - - info.memory.textures --; - - } - - function onRenderTargetDispose( event ) { - - const renderTarget = event.target; - - renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); - - deallocateRenderTarget( renderTarget ); - - } - - // - - function deallocateTexture( texture ) { - - const textureProperties = properties.get( texture ); - - if ( textureProperties.__webglInit === undefined ) return; - - _gl.deleteTexture( textureProperties.__webglTexture ); - - properties.remove( texture ); - - } - - function deallocateRenderTarget( renderTarget ) { - - const texture = renderTarget.texture; - - const renderTargetProperties = properties.get( renderTarget ); - const textureProperties = properties.get( texture ); - - if ( ! renderTarget ) return; - - if ( textureProperties.__webglTexture !== undefined ) { - - _gl.deleteTexture( textureProperties.__webglTexture ); - - info.memory.textures --; - - } - - if ( renderTarget.depthTexture ) { - - renderTarget.depthTexture.dispose(); - - } - - if ( renderTarget.isWebGLCubeRenderTarget ) { - - for ( let i = 0; i < 6; i ++ ) { - - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); - - } - - } else { - - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); - if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); - if ( renderTargetProperties.__webglColorRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer ); - if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); - - } - - if ( renderTarget.isWebGLMultipleRenderTargets ) { - - for ( let i = 0, il = texture.length; i < il; i ++ ) { - - const attachmentProperties = properties.get( texture[ i ] ); - - if ( attachmentProperties.__webglTexture ) { - - _gl.deleteTexture( attachmentProperties.__webglTexture ); - - info.memory.textures --; - - } - - properties.remove( texture[ i ] ); - - } - - } - - properties.remove( texture ); - properties.remove( renderTarget ); - - } - - // - - let textureUnits = 0; - - function resetTextureUnits() { - - textureUnits = 0; - - } - - function allocateTextureUnit() { - - const textureUnit = textureUnits; - - if ( textureUnit >= maxTextures ) { - - console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures ); - - } - - textureUnits += 1; - - return textureUnit; - - } - - // - - function setTexture2D( texture, slot ) { - - const textureProperties = properties.get( texture ); - - if ( texture.isVideoTexture ) updateVideoTexture( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - const image = texture.image; - - if ( image === undefined ) { - - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined' ); - - } else if ( image.complete === false ) { - - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); - - } else { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 3553, textureProperties.__webglTexture ); - - } - - function setTexture2DArray( texture, slot ) { - - const textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 35866, textureProperties.__webglTexture ); - - } - - function setTexture3D( texture, slot ) { - - const textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 32879, textureProperties.__webglTexture ); - - } - - function setTextureCube( texture, slot ) { - - const textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - uploadCubeTexture( textureProperties, texture, slot ); - return; - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); - - } - - const wrappingToGL = { - [ RepeatWrapping ]: 10497, - [ ClampToEdgeWrapping ]: 33071, - [ MirroredRepeatWrapping ]: 33648 - }; - - const filterToGL = { - [ NearestFilter ]: 9728, - [ NearestMipmapNearestFilter ]: 9984, - [ NearestMipmapLinearFilter ]: 9986, - - [ LinearFilter ]: 9729, - [ LinearMipmapNearestFilter ]: 9985, - [ LinearMipmapLinearFilter ]: 9987 - }; - - function setTextureParameters( textureType, texture, supportsMips ) { - - if ( supportsMips ) { - - _gl.texParameteri( textureType, 10242, wrappingToGL[ texture.wrapS ] ); - _gl.texParameteri( textureType, 10243, wrappingToGL[ texture.wrapT ] ); - - if ( textureType === 32879 || textureType === 35866 ) { - - _gl.texParameteri( textureType, 32882, wrappingToGL[ texture.wrapR ] ); - - } - - _gl.texParameteri( textureType, 10240, filterToGL[ texture.magFilter ] ); - _gl.texParameteri( textureType, 10241, filterToGL[ texture.minFilter ] ); - - } else { - - _gl.texParameteri( textureType, 10242, 33071 ); - _gl.texParameteri( textureType, 10243, 33071 ); - - if ( textureType === 32879 || textureType === 35866 ) { - - _gl.texParameteri( textureType, 32882, 33071 ); - - } - - if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { - - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); - - } - - _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) ); - - if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { - - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); - - } - - } - - if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) { - - const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - - if ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2 - if ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( 'OES_texture_half_float_linear' ) === false ) ) return; // verify extension for WebGL 1 only - - if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { - - _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); - properties.get( texture ).__currentAnisotropy = texture.anisotropy; - - } - - } - - } - - function initTexture( textureProperties, texture ) { - - if ( textureProperties.__webglInit === undefined ) { - - textureProperties.__webglInit = true; - - texture.addEventListener( 'dispose', onTextureDispose ); - - textureProperties.__webglTexture = _gl.createTexture(); - - info.memory.textures ++; - - } - - } - - function uploadTexture( textureProperties, texture, slot ) { - - let textureType = 3553; - - if ( texture.isDataTexture2DArray ) textureType = 35866; - if ( texture.isDataTexture3D ) textureType = 32879; - - initTexture( textureProperties, texture ); - - state.activeTexture( 33984 + slot ); - state.bindTexture( textureType, textureProperties.__webglTexture ); - - _gl.pixelStorei( 37440, texture.flipY ); - _gl.pixelStorei( 37441, texture.premultiplyAlpha ); - _gl.pixelStorei( 3317, texture.unpackAlignment ); - _gl.pixelStorei( 37443, 0 ); - - const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo$1( texture.image ) === false; - const image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); - - const supportsMips = isPowerOfTwo$1( image ) || isWebGL2, - glFormat = utils.convert( texture.format ); - - let glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - - setTextureParameters( textureType, texture, supportsMips ); - - let mipmap; - const mipmaps = texture.mipmaps; - - if ( texture.isDepthTexture ) { - - // populate depth texture with dummy data - - glInternalFormat = 6402; - - if ( isWebGL2 ) { - - if ( texture.type === FloatType ) { - - glInternalFormat = 36012; - - } else if ( texture.type === UnsignedIntType ) { - - glInternalFormat = 33190; - - } else if ( texture.type === UnsignedInt248Type ) { - - glInternalFormat = 35056; - - } else { - - glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D - - } - - } else { - - if ( texture.type === FloatType ) { - - console.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' ); - - } - - } - - // validation checks for WebGL 1 - - if ( texture.format === DepthFormat && glInternalFormat === 6402 ) { - - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { - - console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); - - texture.type = UnsignedShortType; - glType = utils.convert( texture.type ); - - } - - } - - if ( texture.format === DepthStencilFormat && glInternalFormat === 6402 ) { - - // Depth stencil textures need the DEPTH_STENCIL internal format - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - glInternalFormat = 34041; - - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedInt248Type ) { - - console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); - - texture.type = UnsignedInt248Type; - glType = utils.convert( texture.type ); - - } - - } - - // - - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); - - } else if ( texture.isDataTexture ) { - - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels - - if ( mipmaps.length > 0 && supportsMips ) { - - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else { - - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; - - } - - } else if ( texture.isCompressedTexture ) { - - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { - - if ( glFormat !== null ) { - - state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - - } else { - - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); - - } - - } else { - - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - } - - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else if ( texture.isDataTexture2DArray ) { - - state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; - - } else if ( texture.isDataTexture3D ) { - - state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; - - } else { - - // regular Texture (image, video, canvas) - - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels - - if ( mipmaps.length > 0 && supportsMips ) { - - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); - - } - - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else { - - state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); - textureProperties.__maxMipLevel = 0; - - } - - } - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - generateMipmap( textureType, texture, image.width, image.height ); - - } - - textureProperties.__version = texture.version; - - if ( texture.onUpdate ) texture.onUpdate( texture ); - - } - - function uploadCubeTexture( textureProperties, texture, slot ) { - - if ( texture.image.length !== 6 ) return; - - initTexture( textureProperties, texture ); - - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); - - _gl.pixelStorei( 37440, texture.flipY ); - _gl.pixelStorei( 37441, texture.premultiplyAlpha ); - _gl.pixelStorei( 3317, texture.unpackAlignment ); - _gl.pixelStorei( 37443, 0 ); - - const isCompressed = ( texture && ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ) ); - const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); - - const cubeImage = []; - - for ( let i = 0; i < 6; i ++ ) { - - if ( ! isCompressed && ! isDataTexture ) { - - cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); - - } else { - - cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; - - } - - } - - const image = cubeImage[ 0 ], - supportsMips = isPowerOfTwo$1( image ) || isWebGL2, - glFormat = utils.convert( texture.format ), - glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - - setTextureParameters( 34067, texture, supportsMips ); - - let mipmaps; - - if ( isCompressed ) { - - for ( let i = 0; i < 6; i ++ ) { - - mipmaps = cubeImage[ i ].mipmaps; - - for ( let j = 0; j < mipmaps.length; j ++ ) { - - const mipmap = mipmaps[ j ]; - - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { - - if ( glFormat !== null ) { - - state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - - } else { - - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); - - } - - } else { - - state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - } - - } - - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else { - - mipmaps = texture.mipmaps; - - for ( let i = 0; i < 6; i ++ ) { - - if ( isDataTexture ) { - - state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - - for ( let j = 0; j < mipmaps.length; j ++ ) { - - const mipmap = mipmaps[ j ]; - const mipmapImage = mipmap.image[ i ].image; - - state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); - - } - - } else { - - state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); - - for ( let j = 0; j < mipmaps.length; j ++ ) { - - const mipmap = mipmaps[ j ]; - - state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); - - } - - } - - } - - textureProperties.__maxMipLevel = mipmaps.length; - - } - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - // We assume images for cube map have the same size. - generateMipmap( 34067, texture, image.width, image.height ); - - } - - textureProperties.__version = texture.version; - - if ( texture.onUpdate ) texture.onUpdate( texture ); - - } - - // Render targets - - // Setup storage for target texture and bind it to correct framebuffer - function setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget ) { - - const glFormat = utils.convert( texture.format ); - const glType = utils.convert( texture.type ); - const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - - if ( textureTarget === 32879 || textureTarget === 35866 ) { - - state.texImage3D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null ); - - } else { - - state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - - } - - state.bindFramebuffer( 36160, framebuffer ); - _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( texture ).__webglTexture, 0 ); - state.bindFramebuffer( 36160, null ); - - } - - // Setup storage for internal depth/stencil buffers and bind to correct framebuffer - function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { - - _gl.bindRenderbuffer( 36161, renderbuffer ); - - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - - let glInternalFormat = 33189; - - if ( isMultisample ) { - - const depthTexture = renderTarget.depthTexture; - - if ( depthTexture && depthTexture.isDepthTexture ) { - - if ( depthTexture.type === FloatType ) { - - glInternalFormat = 36012; - - } else if ( depthTexture.type === UnsignedIntType ) { - - glInternalFormat = 33190; - - } - - } - - const samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - - } else { - - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); - - } - - _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); - - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - - if ( isMultisample ) { - - const samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height ); - - } else { - - _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); - - } - - - _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); - - } else { - - // Use the first texture for MRT so far - const texture = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture[ 0 ] : renderTarget.texture; - - const glFormat = utils.convert( texture.format ); - const glType = utils.convert( texture.type ); - const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - - if ( isMultisample ) { - - const samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - - } else { - - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); - - } - - } - - _gl.bindRenderbuffer( 36161, null ); - - } - - // Setup resources for a Depth Texture for a FBO (needs an extension) - function setupDepthTexture( framebuffer, renderTarget ) { - - const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); - if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); - - state.bindFramebuffer( 36160, framebuffer ); - - if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { - - throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); - - } - - // upload an empty depth texture with framebuffer size - if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || - renderTarget.depthTexture.image.width !== renderTarget.width || - renderTarget.depthTexture.image.height !== renderTarget.height ) { - - renderTarget.depthTexture.image.width = renderTarget.width; - renderTarget.depthTexture.image.height = renderTarget.height; - renderTarget.depthTexture.needsUpdate = true; - - } - - setTexture2D( renderTarget.depthTexture, 0 ); - - const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; - - if ( renderTarget.depthTexture.format === DepthFormat ) { - - _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); - - } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { - - _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); - - } else { - - throw new Error( 'Unknown depthTexture format' ); - - } - - } - - // Setup GL resources for a non-texture depth buffer - function setupDepthRenderbuffer( renderTarget ) { - - const renderTargetProperties = properties.get( renderTarget ); - - const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - - if ( renderTarget.depthTexture ) { - - if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); - - setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); - - } else { - - if ( isCube ) { - - renderTargetProperties.__webglDepthbuffer = []; - - for ( let i = 0; i < 6; i ++ ) { - - state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); - renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); - - } - - } else { - - state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); - renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); - - } - - } - - state.bindFramebuffer( 36160, null ); - - } - - // Set up GL resources for the render target - function setupRenderTarget( renderTarget ) { - - const texture = renderTarget.texture; - - const renderTargetProperties = properties.get( renderTarget ); - const textureProperties = properties.get( texture ); - - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - - if ( renderTarget.isWebGLMultipleRenderTargets !== true ) { - - textureProperties.__webglTexture = _gl.createTexture(); - textureProperties.__version = texture.version; - info.memory.textures ++; - - } - - const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); - const isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true ); - const isRenderTarget3D = texture.isDataTexture3D || texture.isDataTexture2DArray; - const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; - - // Handles WebGL2 RGBFormat fallback - #18858 - - if ( isWebGL2 && texture.format === RGBFormat && ( texture.type === FloatType || texture.type === HalfFloatType ) ) { - - texture.format = RGBAFormat; - - console.warn( 'THREE.WebGLRenderer: Rendering to textures with RGB format is not supported. Using RGBA format instead.' ); - - } - - // Setup framebuffer - - if ( isCube ) { - - renderTargetProperties.__webglFramebuffer = []; - - for ( let i = 0; i < 6; i ++ ) { - - renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - - } - - } else { - - renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); - - if ( isMultipleRenderTargets ) { - - if ( capabilities.drawBuffers ) { - - const textures = renderTarget.texture; - - for ( let i = 0, il = textures.length; i < il; i ++ ) { - - const attachmentProperties = properties.get( textures[ i ] ); - - if ( attachmentProperties.__webglTexture === undefined ) { - - attachmentProperties.__webglTexture = _gl.createTexture(); - - info.memory.textures ++; - - } - - } - - } else { - - console.warn( 'THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension.' ); - - } - - } else if ( isMultisample ) { - - if ( isWebGL2 ) { - - renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); - renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); - - _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer ); - - const glFormat = utils.convert( texture.format ); - const glType = utils.convert( texture.type ); - const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - const samples = getRenderTargetSamples( renderTarget ); - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - - state.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.framebufferRenderbuffer( 36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer ); - _gl.bindRenderbuffer( 36161, null ); - - if ( renderTarget.depthBuffer ) { - - renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); - - } - - state.bindFramebuffer( 36160, null ); - - - } else { - - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); - - } - - } - - } - - // Setup color buffer - - if ( isCube ) { - - state.bindTexture( 34067, textureProperties.__webglTexture ); - setTextureParameters( 34067, texture, supportsMips ); - - for ( let i = 0; i < 6; i ++ ) { - - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, texture, 36064, 34069 + i ); - - } - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - generateMipmap( 34067, texture, renderTarget.width, renderTarget.height ); - - } - - state.unbindTexture(); - - } else if ( isMultipleRenderTargets ) { - - const textures = renderTarget.texture; - - for ( let i = 0, il = textures.length; i < il; i ++ ) { - - const attachment = textures[ i ]; - const attachmentProperties = properties.get( attachment ); - - state.bindTexture( 3553, attachmentProperties.__webglTexture ); - setTextureParameters( 3553, attachment, supportsMips ); - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, 36064 + i, 3553 ); - - if ( textureNeedsGenerateMipmaps( attachment, supportsMips ) ) { - - generateMipmap( 3553, attachment, renderTarget.width, renderTarget.height ); - - } - - } - - state.unbindTexture(); - - } else { - - let glTextureType = 3553; - - if ( isRenderTarget3D ) { - - // Render targets containing layers, i.e: Texture 3D and 2d arrays - - if ( isWebGL2 ) { - - const isTexture3D = texture.isDataTexture3D; - glTextureType = isTexture3D ? 32879 : 35866; - - } else { - - console.warn( 'THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2.' ); - - } - - } - - state.bindTexture( glTextureType, textureProperties.__webglTexture ); - setTextureParameters( glTextureType, texture, supportsMips ); - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, 36064, glTextureType ); - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - generateMipmap( glTextureType, texture, renderTarget.width, renderTarget.height, renderTarget.depth ); - - } - - state.unbindTexture(); - - } - - // Setup depth and stencil buffers - - if ( renderTarget.depthBuffer ) { - - setupDepthRenderbuffer( renderTarget ); - - } - - } - - function updateRenderTargetMipmap( renderTarget ) { - - const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; - - const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; - - for ( let i = 0, il = textures.length; i < il; i ++ ) { - - const texture = textures[ i ]; - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - const target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553; - const webglTexture = properties.get( texture ).__webglTexture; - - state.bindTexture( target, webglTexture ); - generateMipmap( target, texture, renderTarget.width, renderTarget.height ); - state.unbindTexture(); - - } - - } - - } - - function updateMultisampleRenderTarget( renderTarget ) { - - if ( renderTarget.isWebGLMultisampleRenderTarget ) { - - if ( isWebGL2 ) { - - const width = renderTarget.width; - const height = renderTarget.height; - let mask = 16384; - - if ( renderTarget.depthBuffer ) mask |= 256; - if ( renderTarget.stencilBuffer ) mask |= 1024; - - const renderTargetProperties = properties.get( renderTarget ); - - state.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); - state.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); - - _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); - - state.bindFramebuffer( 36008, null ); - state.bindFramebuffer( 36009, renderTargetProperties.__webglMultisampledFramebuffer ); - - } else { - - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); - - } - - } - - } - - function getRenderTargetSamples( renderTarget ) { - - return ( isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ? - Math.min( maxSamples, renderTarget.samples ) : 0; - - } - - function updateVideoTexture( texture ) { - - const frame = info.render.frame; - - // Check the last frame we updated the VideoTexture - - if ( _videoTextures.get( texture ) !== frame ) { - - _videoTextures.set( texture, frame ); - texture.update(); - - } - - } - - // backwards compatibility - - let warnedTexture2D = false; - let warnedTextureCube = false; - - function safeSetTexture2D( texture, slot ) { - - if ( texture && texture.isWebGLRenderTarget ) { - - if ( warnedTexture2D === false ) { - - console.warn( 'THREE.WebGLTextures.safeSetTexture2D: don\'t use render targets as textures. Use their .texture property instead.' ); - warnedTexture2D = true; - - } - - texture = texture.texture; - - } - - setTexture2D( texture, slot ); - - } - - function safeSetTextureCube( texture, slot ) { - - if ( texture && texture.isWebGLCubeRenderTarget ) { - - if ( warnedTextureCube === false ) { - - console.warn( 'THREE.WebGLTextures.safeSetTextureCube: don\'t use cube render targets as textures. Use their .texture property instead.' ); - warnedTextureCube = true; - - } - - texture = texture.texture; - - } - - - setTextureCube( texture, slot ); - - } - - // - - this.allocateTextureUnit = allocateTextureUnit; - this.resetTextureUnits = resetTextureUnits; - - this.setTexture2D = setTexture2D; - this.setTexture2DArray = setTexture2DArray; - this.setTexture3D = setTexture3D; - this.setTextureCube = setTextureCube; - this.setupRenderTarget = setupRenderTarget; - this.updateRenderTargetMipmap = updateRenderTargetMipmap; - this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; - - this.safeSetTexture2D = safeSetTexture2D; - this.safeSetTextureCube = safeSetTextureCube; - - } - - function WebGLUtils( gl, extensions, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - function convert( p ) { - - let extension; - - if ( p === UnsignedByteType ) return 5121; - if ( p === UnsignedShort4444Type ) return 32819; - if ( p === UnsignedShort5551Type ) return 32820; - if ( p === UnsignedShort565Type ) return 33635; - - if ( p === ByteType ) return 5120; - if ( p === ShortType ) return 5122; - if ( p === UnsignedShortType ) return 5123; - if ( p === IntType ) return 5124; - if ( p === UnsignedIntType ) return 5125; - if ( p === FloatType ) return 5126; - - if ( p === HalfFloatType ) { - - if ( isWebGL2 ) return 5131; - - extension = extensions.get( 'OES_texture_half_float' ); - - if ( extension !== null ) { - - return extension.HALF_FLOAT_OES; - - } else { - - return null; - - } - - } - - if ( p === AlphaFormat ) return 6406; - if ( p === RGBFormat ) return 6407; - if ( p === RGBAFormat ) return 6408; - if ( p === LuminanceFormat ) return 6409; - if ( p === LuminanceAlphaFormat ) return 6410; - if ( p === DepthFormat ) return 6402; - if ( p === DepthStencilFormat ) return 34041; - if ( p === RedFormat ) return 6403; - - // WebGL2 formats. - - if ( p === RedIntegerFormat ) return 36244; - if ( p === RGFormat ) return 33319; - if ( p === RGIntegerFormat ) return 33320; - if ( p === RGBIntegerFormat ) return 36248; - if ( p === RGBAIntegerFormat ) return 36249; - - if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || - p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); - - if ( extension !== null ) { - - if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; - if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; - - } else { - - return null; - - } - - } - - if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || - p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - - if ( extension !== null ) { - - if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; - if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; - if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; - - } else { - - return null; - - } - - } - - if ( p === RGB_ETC1_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); - - if ( extension !== null ) { - - return extension.COMPRESSED_RGB_ETC1_WEBGL; - - } else { - - return null; - - } - - } - - if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_etc' ); - - if ( extension !== null ) { - - if ( p === RGB_ETC2_Format ) return extension.COMPRESSED_RGB8_ETC2; - if ( p === RGBA_ETC2_EAC_Format ) return extension.COMPRESSED_RGBA8_ETC2_EAC; - - } - - } - - if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || - p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || - p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || - p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || - p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format || - p === SRGB8_ALPHA8_ASTC_4x4_Format || p === SRGB8_ALPHA8_ASTC_5x4_Format || p === SRGB8_ALPHA8_ASTC_5x5_Format || - p === SRGB8_ALPHA8_ASTC_6x5_Format || p === SRGB8_ALPHA8_ASTC_6x6_Format || p === SRGB8_ALPHA8_ASTC_8x5_Format || - p === SRGB8_ALPHA8_ASTC_8x6_Format || p === SRGB8_ALPHA8_ASTC_8x8_Format || p === SRGB8_ALPHA8_ASTC_10x5_Format || - p === SRGB8_ALPHA8_ASTC_10x6_Format || p === SRGB8_ALPHA8_ASTC_10x8_Format || p === SRGB8_ALPHA8_ASTC_10x10_Format || - p === SRGB8_ALPHA8_ASTC_12x10_Format || p === SRGB8_ALPHA8_ASTC_12x12_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_astc' ); - - if ( extension !== null ) { - - // TODO Complete? - - return p; - - } else { - - return null; - - } - - } - - if ( p === RGBA_BPTC_Format ) { - - extension = extensions.get( 'EXT_texture_compression_bptc' ); - - if ( extension !== null ) { - - // TODO Complete? - - return p; - - } else { - - return null; - - } - - } - - if ( p === UnsignedInt248Type ) { - - if ( isWebGL2 ) return 34042; - - extension = extensions.get( 'WEBGL_depth_texture' ); - - if ( extension !== null ) { - - return extension.UNSIGNED_INT_24_8_WEBGL; - - } else { - - return null; - - } - - } - - } - - return { convert: convert }; - - } - - class ArrayCamera extends PerspectiveCamera { - - constructor( array = [] ) { - - super(); - - this.cameras = array; - - } - - } - - ArrayCamera.prototype.isArrayCamera = true; - - class Group extends Object3D { - - constructor() { - - super(); - - this.type = 'Group'; - - } - - } - - Group.prototype.isGroup = true; - - const _moveEvent = { type: 'move' }; - - class WebXRController { - - constructor() { - - this._targetRay = null; - this._grip = null; - this._hand = null; - - } - - getHandSpace() { - - if ( this._hand === null ) { - - this._hand = new Group(); - this._hand.matrixAutoUpdate = false; - this._hand.visible = false; - - this._hand.joints = {}; - this._hand.inputState = { pinching: false }; - - } - - return this._hand; - - } - - getTargetRaySpace() { - - if ( this._targetRay === null ) { - - this._targetRay = new Group(); - this._targetRay.matrixAutoUpdate = false; - this._targetRay.visible = false; - this._targetRay.hasLinearVelocity = false; - this._targetRay.linearVelocity = new Vector3(); - this._targetRay.hasAngularVelocity = false; - this._targetRay.angularVelocity = new Vector3(); - - } - - return this._targetRay; - - } - - getGripSpace() { - - if ( this._grip === null ) { - - this._grip = new Group(); - this._grip.matrixAutoUpdate = false; - this._grip.visible = false; - this._grip.hasLinearVelocity = false; - this._grip.linearVelocity = new Vector3(); - this._grip.hasAngularVelocity = false; - this._grip.angularVelocity = new Vector3(); - - } - - return this._grip; - - } - - dispatchEvent( event ) { - - if ( this._targetRay !== null ) { - - this._targetRay.dispatchEvent( event ); - - } - - if ( this._grip !== null ) { - - this._grip.dispatchEvent( event ); - - } - - if ( this._hand !== null ) { - - this._hand.dispatchEvent( event ); - - } - - return this; - - } - - disconnect( inputSource ) { - - this.dispatchEvent( { type: 'disconnected', data: inputSource } ); - - if ( this._targetRay !== null ) { - - this._targetRay.visible = false; - - } - - if ( this._grip !== null ) { - - this._grip.visible = false; - - } - - if ( this._hand !== null ) { - - this._hand.visible = false; - - } - - return this; - - } - - update( inputSource, frame, referenceSpace ) { - - let inputPose = null; - let gripPose = null; - let handPose = null; - - const targetRay = this._targetRay; - const grip = this._grip; - const hand = this._hand; - - if ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) { - - if ( targetRay !== null ) { - - inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); - - if ( inputPose !== null ) { - - targetRay.matrix.fromArray( inputPose.transform.matrix ); - targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); - - if ( inputPose.linearVelocity ) { - - targetRay.hasLinearVelocity = true; - targetRay.linearVelocity.copy( inputPose.linearVelocity ); - - } else { - - targetRay.hasLinearVelocity = false; - - } - - if ( inputPose.angularVelocity ) { - - targetRay.hasAngularVelocity = true; - targetRay.angularVelocity.copy( inputPose.angularVelocity ); - - } else { - - targetRay.hasAngularVelocity = false; - - } - - this.dispatchEvent( _moveEvent ); - - } - - } - - if ( hand && inputSource.hand ) { - - handPose = true; - - for ( const inputjoint of inputSource.hand.values() ) { - - // Update the joints groups with the XRJoint poses - const jointPose = frame.getJointPose( inputjoint, referenceSpace ); - - if ( hand.joints[ inputjoint.jointName ] === undefined ) { - - // The transform of this joint will be updated with the joint pose on each frame - const joint = new Group(); - joint.matrixAutoUpdate = false; - joint.visible = false; - hand.joints[ inputjoint.jointName ] = joint; - // ?? - hand.add( joint ); - - } - - const joint = hand.joints[ inputjoint.jointName ]; - - if ( jointPose !== null ) { - - joint.matrix.fromArray( jointPose.transform.matrix ); - joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); - joint.jointRadius = jointPose.radius; - - } - - joint.visible = jointPose !== null; - - } - - // Custom events - - // Check pinchz - const indexTip = hand.joints[ 'index-finger-tip' ]; - const thumbTip = hand.joints[ 'thumb-tip' ]; - const distance = indexTip.position.distanceTo( thumbTip.position ); - - const distanceToPinch = 0.02; - const threshold = 0.005; - - if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { - - hand.inputState.pinching = false; - this.dispatchEvent( { - type: 'pinchend', - handedness: inputSource.handedness, - target: this - } ); - - } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { - - hand.inputState.pinching = true; - this.dispatchEvent( { - type: 'pinchstart', - handedness: inputSource.handedness, - target: this - } ); - - } - - } else { - - if ( grip !== null && inputSource.gripSpace ) { - - gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); - - if ( gripPose !== null ) { - - grip.matrix.fromArray( gripPose.transform.matrix ); - grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); - - if ( gripPose.linearVelocity ) { - - grip.hasLinearVelocity = true; - grip.linearVelocity.copy( gripPose.linearVelocity ); - - } else { - - grip.hasLinearVelocity = false; - - } - - if ( gripPose.angularVelocity ) { - - grip.hasAngularVelocity = true; - grip.angularVelocity.copy( gripPose.angularVelocity ); - - } else { - - grip.hasAngularVelocity = false; - - } - - } - - } - - } - - } - - if ( targetRay !== null ) { - - targetRay.visible = ( inputPose !== null ); - - } - - if ( grip !== null ) { - - grip.visible = ( gripPose !== null ); - - } - - if ( hand !== null ) { - - hand.visible = ( handPose !== null ); - - } - - return this; - - } - - } - - class WebXRManager extends EventDispatcher { - - constructor( renderer, gl ) { - - super(); - - const scope = this; - const state = renderer.state; - - let session = null; - let framebufferScaleFactor = 1.0; - - let referenceSpace = null; - let referenceSpaceType = 'local-floor'; - - let pose = null; - let glBinding = null; - let glFramebuffer = null; - let glProjLayer = null; - let glBaseLayer = null; - let isMultisample = false; - let glMultisampledFramebuffer = null; - let glColorRenderbuffer = null; - let glDepthRenderbuffer = null; - let xrFrame = null; - let depthStyle = null; - let clearStyle = null; - - const controllers = []; - const inputSourcesMap = new Map(); - - // - - const cameraL = new PerspectiveCamera(); - cameraL.layers.enable( 1 ); - cameraL.viewport = new Vector4(); - - const cameraR = new PerspectiveCamera(); - cameraR.layers.enable( 2 ); - cameraR.viewport = new Vector4(); - - const cameras = [ cameraL, cameraR ]; - - const cameraVR = new ArrayCamera(); - cameraVR.layers.enable( 1 ); - cameraVR.layers.enable( 2 ); - - let _currentDepthNear = null; - let _currentDepthFar = null; - - // - - this.cameraAutoUpdate = true; - this.enabled = false; - - this.isPresenting = false; - - this.getController = function ( index ) { - - let controller = controllers[ index ]; - - if ( controller === undefined ) { - - controller = new WebXRController(); - controllers[ index ] = controller; - - } - - return controller.getTargetRaySpace(); - - }; - - this.getControllerGrip = function ( index ) { - - let controller = controllers[ index ]; - - if ( controller === undefined ) { - - controller = new WebXRController(); - controllers[ index ] = controller; - - } - - return controller.getGripSpace(); - - }; - - this.getHand = function ( index ) { - - let controller = controllers[ index ]; - - if ( controller === undefined ) { - - controller = new WebXRController(); - controllers[ index ] = controller; - - } - - return controller.getHandSpace(); - - }; - - // - - function onSessionEvent( event ) { - - const controller = inputSourcesMap.get( event.inputSource ); - - if ( controller ) { - - controller.dispatchEvent( { type: event.type, data: event.inputSource } ); - - } - - } - - function onSessionEnd() { - - inputSourcesMap.forEach( function ( controller, inputSource ) { - - controller.disconnect( inputSource ); - - } ); - - inputSourcesMap.clear(); - - _currentDepthNear = null; - _currentDepthFar = null; - - // restore framebuffer/rendering state - - state.bindXRFramebuffer( null ); - renderer.setRenderTarget( renderer.getRenderTarget() ); - - if ( glFramebuffer ) gl.deleteFramebuffer( glFramebuffer ); - if ( glMultisampledFramebuffer ) gl.deleteFramebuffer( glMultisampledFramebuffer ); - if ( glColorRenderbuffer ) gl.deleteRenderbuffer( glColorRenderbuffer ); - if ( glDepthRenderbuffer ) gl.deleteRenderbuffer( glDepthRenderbuffer ); - glFramebuffer = null; - glMultisampledFramebuffer = null; - glColorRenderbuffer = null; - glDepthRenderbuffer = null; - glBaseLayer = null; - glProjLayer = null; - glBinding = null; - session = null; - - // - - animation.stop(); - - scope.isPresenting = false; - - scope.dispatchEvent( { type: 'sessionend' } ); - - } - - this.setFramebufferScaleFactor = function ( value ) { - - framebufferScaleFactor = value; - - if ( scope.isPresenting === true ) { - - console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' ); - - } - - }; - - this.setReferenceSpaceType = function ( value ) { - - referenceSpaceType = value; - - if ( scope.isPresenting === true ) { - - console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' ); - - } - - }; - - this.getReferenceSpace = function () { - - return referenceSpace; - - }; - - this.getBaseLayer = function () { - - return glProjLayer !== null ? glProjLayer : glBaseLayer; - - }; - - this.getBinding = function () { - - return glBinding; - - }; - - this.getFrame = function () { - - return xrFrame; - - }; - - this.getSession = function () { - - return session; - - }; - - this.setSession = async function ( value ) { - - session = value; - - if ( session !== null ) { - - session.addEventListener( 'select', onSessionEvent ); - session.addEventListener( 'selectstart', onSessionEvent ); - session.addEventListener( 'selectend', onSessionEvent ); - session.addEventListener( 'squeeze', onSessionEvent ); - session.addEventListener( 'squeezestart', onSessionEvent ); - session.addEventListener( 'squeezeend', onSessionEvent ); - session.addEventListener( 'end', onSessionEnd ); - session.addEventListener( 'inputsourceschange', onInputSourcesChange ); - - const attributes = gl.getContextAttributes(); - - if ( attributes.xrCompatible !== true ) { - - await gl.makeXRCompatible(); - - } - - if ( session.renderState.layers === undefined ) { - - const layerInit = { - antialias: attributes.antialias, - alpha: attributes.alpha, - depth: attributes.depth, - stencil: attributes.stencil, - framebufferScaleFactor: framebufferScaleFactor - }; - - glBaseLayer = new XRWebGLLayer( session, gl, layerInit ); - - session.updateRenderState( { baseLayer: glBaseLayer } ); - - } else if ( gl instanceof WebGLRenderingContext ) { - - // Use old style webgl layer because we can't use MSAA - // WebGL2 support. - - const layerInit = { - antialias: true, - alpha: attributes.alpha, - depth: attributes.depth, - stencil: attributes.stencil, - framebufferScaleFactor: framebufferScaleFactor - }; - - glBaseLayer = new XRWebGLLayer( session, gl, layerInit ); - - session.updateRenderState( { layers: [ glBaseLayer ] } ); - - } else { - - isMultisample = attributes.antialias; - let depthFormat = null; - - - if ( attributes.depth ) { - - clearStyle = 256; - - if ( attributes.stencil ) clearStyle |= 1024; - - depthStyle = attributes.stencil ? 33306 : 36096; - depthFormat = attributes.stencil ? 35056 : 33190; - - } - - const projectionlayerInit = { - colorFormat: attributes.alpha ? 32856 : 32849, - depthFormat: depthFormat, - scaleFactor: framebufferScaleFactor - }; - - glBinding = new XRWebGLBinding( session, gl ); - - glProjLayer = glBinding.createProjectionLayer( projectionlayerInit ); - - glFramebuffer = gl.createFramebuffer(); - - session.updateRenderState( { layers: [ glProjLayer ] } ); - - if ( isMultisample ) { - - glMultisampledFramebuffer = gl.createFramebuffer(); - glColorRenderbuffer = gl.createRenderbuffer(); - gl.bindRenderbuffer( 36161, glColorRenderbuffer ); - gl.renderbufferStorageMultisample( - 36161, - 4, - 32856, - glProjLayer.textureWidth, - glProjLayer.textureHeight ); - state.bindFramebuffer( 36160, glMultisampledFramebuffer ); - gl.framebufferRenderbuffer( 36160, 36064, 36161, glColorRenderbuffer ); - gl.bindRenderbuffer( 36161, null ); - - if ( depthFormat !== null ) { - - glDepthRenderbuffer = gl.createRenderbuffer(); - gl.bindRenderbuffer( 36161, glDepthRenderbuffer ); - gl.renderbufferStorageMultisample( 36161, 4, depthFormat, glProjLayer.textureWidth, glProjLayer.textureHeight ); - gl.framebufferRenderbuffer( 36160, depthStyle, 36161, glDepthRenderbuffer ); - gl.bindRenderbuffer( 36161, null ); - - } - - state.bindFramebuffer( 36160, null ); - - } - - } - - referenceSpace = await session.requestReferenceSpace( referenceSpaceType ); - - animation.setContext( session ); - animation.start(); - - scope.isPresenting = true; - - scope.dispatchEvent( { type: 'sessionstart' } ); - - } - - }; - - function onInputSourcesChange( event ) { - - const inputSources = session.inputSources; - - // Assign inputSources to available controllers - - for ( let i = 0; i < controllers.length; i ++ ) { - - inputSourcesMap.set( inputSources[ i ], controllers[ i ] ); - - } - - // Notify disconnected - - for ( let i = 0; i < event.removed.length; i ++ ) { - - const inputSource = event.removed[ i ]; - const controller = inputSourcesMap.get( inputSource ); - - if ( controller ) { - - controller.dispatchEvent( { type: 'disconnected', data: inputSource } ); - inputSourcesMap.delete( inputSource ); - - } - - } - - // Notify connected - - for ( let i = 0; i < event.added.length; i ++ ) { - - const inputSource = event.added[ i ]; - const controller = inputSourcesMap.get( inputSource ); - - if ( controller ) { - - controller.dispatchEvent( { type: 'connected', data: inputSource } ); - - } - - } - - } - - // - - const cameraLPos = new Vector3(); - const cameraRPos = new Vector3(); - - /** - * Assumes 2 cameras that are parallel and share an X-axis, and that - * the cameras' projection and world matrices have already been set. - * And that near and far planes are identical for both cameras. - * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 - */ - function setProjectionFromUnion( camera, cameraL, cameraR ) { - - cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); - cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); - - const ipd = cameraLPos.distanceTo( cameraRPos ); - - const projL = cameraL.projectionMatrix.elements; - const projR = cameraR.projectionMatrix.elements; - - // VR systems will have identical far and near planes, and - // most likely identical top and bottom frustum extents. - // Use the left camera for these values. - const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); - const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); - const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; - const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; - - const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; - const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; - const left = near * leftFov; - const right = near * rightFov; - - // Calculate the new camera's position offset from the - // left camera. xOffset should be roughly half `ipd`. - const zOffset = ipd / ( - leftFov + rightFov ); - const xOffset = zOffset * - leftFov; - - // TODO: Better way to apply this offset? - cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); - camera.translateX( xOffset ); - camera.translateZ( zOffset ); - camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); - camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); - - // Find the union of the frustum values of the cameras and scale - // the values so that the near plane's position does not change in world space, - // although must now be relative to the new union camera. - const near2 = near + zOffset; - const far2 = far + zOffset; - const left2 = left - xOffset; - const right2 = right + ( ipd - xOffset ); - const top2 = topFov * far / far2 * near2; - const bottom2 = bottomFov * far / far2 * near2; - - camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); - - } - - function updateCamera( camera, parent ) { - - if ( parent === null ) { - - camera.matrixWorld.copy( camera.matrix ); - - } else { - - camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); - - } - - camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); - - } - - this.updateCamera = function ( camera ) { - - if ( session === null ) return; - - cameraVR.near = cameraR.near = cameraL.near = camera.near; - cameraVR.far = cameraR.far = cameraL.far = camera.far; - - if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { - - // Note that the new renderState won't apply until the next frame. See #18320 - - session.updateRenderState( { - depthNear: cameraVR.near, - depthFar: cameraVR.far - } ); - - _currentDepthNear = cameraVR.near; - _currentDepthFar = cameraVR.far; - - } - - const parent = camera.parent; - const cameras = cameraVR.cameras; - - updateCamera( cameraVR, parent ); - - for ( let i = 0; i < cameras.length; i ++ ) { - - updateCamera( cameras[ i ], parent ); - - } - - cameraVR.matrixWorld.decompose( cameraVR.position, cameraVR.quaternion, cameraVR.scale ); - - // update user camera and its children - - camera.position.copy( cameraVR.position ); - camera.quaternion.copy( cameraVR.quaternion ); - camera.scale.copy( cameraVR.scale ); - camera.matrix.copy( cameraVR.matrix ); - camera.matrixWorld.copy( cameraVR.matrixWorld ); - - const children = camera.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateMatrixWorld( true ); - - } - - // update projection matrix for proper view frustum culling - - if ( cameras.length === 2 ) { - - setProjectionFromUnion( cameraVR, cameraL, cameraR ); - - } else { - - // assume single camera setup (AR) - - cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); - - } - - }; - - this.getCamera = function () { - - return cameraVR; - - }; - - this.getFoveation = function () { - - if ( glProjLayer !== null ) { - - return glProjLayer.fixedFoveation; - - } - - if ( glBaseLayer !== null ) { - - return glBaseLayer.fixedFoveation; - - } - - return undefined; - - }; - - this.setFoveation = function ( foveation ) { - - // 0 = no foveation = full resolution - // 1 = maximum foveation = the edges render at lower resolution - - if ( glProjLayer !== null ) { - - glProjLayer.fixedFoveation = foveation; - - } - - if ( glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined ) { - - glBaseLayer.fixedFoveation = foveation; - - } - - }; - - // Animation Loop - - let onAnimationFrameCallback = null; - - function onAnimationFrame( time, frame ) { - - pose = frame.getViewerPose( referenceSpace ); - xrFrame = frame; - - if ( pose !== null ) { - - const views = pose.views; - - if ( glBaseLayer !== null ) { - - state.bindXRFramebuffer( glBaseLayer.framebuffer ); - - } - - let cameraVRNeedsUpdate = false; - - // check if it's necessary to rebuild cameraVR's camera list - - if ( views.length !== cameraVR.cameras.length ) { - - cameraVR.cameras.length = 0; - - cameraVRNeedsUpdate = true; - - } - - for ( let i = 0; i < views.length; i ++ ) { - - const view = views[ i ]; - - let viewport = null; - - if ( glBaseLayer !== null ) { - - viewport = glBaseLayer.getViewport( view ); - - } else { - - const glSubImage = glBinding.getViewSubImage( glProjLayer, view ); - - state.bindXRFramebuffer( glFramebuffer ); - - if ( glSubImage.depthStencilTexture !== undefined ) { - - gl.framebufferTexture2D( 36160, depthStyle, 3553, glSubImage.depthStencilTexture, 0 ); - - } - - gl.framebufferTexture2D( 36160, 36064, 3553, glSubImage.colorTexture, 0 ); - - viewport = glSubImage.viewport; - - } - - const camera = cameras[ i ]; - - camera.matrix.fromArray( view.transform.matrix ); - camera.projectionMatrix.fromArray( view.projectionMatrix ); - camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); - - if ( i === 0 ) { - - cameraVR.matrix.copy( camera.matrix ); - - } - - if ( cameraVRNeedsUpdate === true ) { - - cameraVR.cameras.push( camera ); - - } - - } - - if ( isMultisample ) { - - state.bindXRFramebuffer( glMultisampledFramebuffer ); - - if ( clearStyle !== null ) gl.clear( clearStyle ); - - } - - } - - // - - const inputSources = session.inputSources; - - for ( let i = 0; i < controllers.length; i ++ ) { - - const controller = controllers[ i ]; - const inputSource = inputSources[ i ]; - - controller.update( inputSource, frame, referenceSpace ); - - } - - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); - - if ( isMultisample ) { - - const width = glProjLayer.textureWidth; - const height = glProjLayer.textureHeight; - - state.bindFramebuffer( 36008, glMultisampledFramebuffer ); - state.bindFramebuffer( 36009, glFramebuffer ); - // Invalidate the depth here to avoid flush of the depth data to main memory. - gl.invalidateFramebuffer( 36008, [ depthStyle ] ); - gl.invalidateFramebuffer( 36009, [ depthStyle ] ); - gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, 16384, 9728 ); - // Invalidate the MSAA buffer because it's not needed anymore. - gl.invalidateFramebuffer( 36008, [ 36064 ] ); - state.bindFramebuffer( 36008, null ); - state.bindFramebuffer( 36009, null ); - - state.bindFramebuffer( 36160, glMultisampledFramebuffer ); - - } - - xrFrame = null; - - } - - const animation = new WebGLAnimation(); - - animation.setAnimationLoop( onAnimationFrame ); - - this.setAnimationLoop = function ( callback ) { - - onAnimationFrameCallback = callback; - - }; - - this.dispose = function () {}; - - } - - } - - function WebGLMaterials( properties ) { - - function refreshFogUniforms( uniforms, fog ) { - - uniforms.fogColor.value.copy( fog.color ); - - if ( fog.isFog ) { - - uniforms.fogNear.value = fog.near; - uniforms.fogFar.value = fog.far; - - } else if ( fog.isFogExp2 ) { - - uniforms.fogDensity.value = fog.density; - - } - - } - - function refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget ) { - - if ( material.isMeshBasicMaterial ) { - - refreshUniformsCommon( uniforms, material ); - - } else if ( material.isMeshLambertMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsLambert( uniforms, material ); - - } else if ( material.isMeshToonMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsToon( uniforms, material ); - - } else if ( material.isMeshPhongMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsPhong( uniforms, material ); - - } else if ( material.isMeshStandardMaterial ) { - - refreshUniformsCommon( uniforms, material ); - - if ( material.isMeshPhysicalMaterial ) { - - refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ); - - } else { - - refreshUniformsStandard( uniforms, material ); - - } - - } else if ( material.isMeshMatcapMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsMatcap( uniforms, material ); - - } else if ( material.isMeshDepthMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsDepth( uniforms, material ); - - } else if ( material.isMeshDistanceMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsDistance( uniforms, material ); - - } else if ( material.isMeshNormalMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsNormal( uniforms, material ); - - } else if ( material.isLineBasicMaterial ) { - - refreshUniformsLine( uniforms, material ); - - if ( material.isLineDashedMaterial ) { - - refreshUniformsDash( uniforms, material ); - - } - - } else if ( material.isPointsMaterial ) { - - refreshUniformsPoints( uniforms, material, pixelRatio, height ); - - } else if ( material.isSpriteMaterial ) { - - refreshUniformsSprites( uniforms, material ); - - } else if ( material.isShadowMaterial ) { - - uniforms.color.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - - } else if ( material.isShaderMaterial ) { - - material.uniformsNeedUpdate = false; // #15581 - - } - - } - - function refreshUniformsCommon( uniforms, material ) { - - uniforms.opacity.value = material.opacity; - - if ( material.color ) { - - uniforms.diffuse.value.copy( material.color ); - - } - - if ( material.emissive ) { - - uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); - - } - - if ( material.map ) { - - uniforms.map.value = material.map; - - } - - if ( material.alphaMap ) { - - uniforms.alphaMap.value = material.alphaMap; - - } - - if ( material.specularMap ) { - - uniforms.specularMap.value = material.specularMap; - - } - - if ( material.alphaTest > 0 ) { - - uniforms.alphaTest.value = material.alphaTest; - - } - - const envMap = properties.get( material ).envMap; - - if ( envMap ) { - - uniforms.envMap.value = envMap; - - uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; - - uniforms.reflectivity.value = material.reflectivity; - uniforms.ior.value = material.ior; - uniforms.refractionRatio.value = material.refractionRatio; - - const maxMipLevel = properties.get( envMap ).__maxMipLevel; - - if ( maxMipLevel !== undefined ) { - - uniforms.maxMipLevel.value = maxMipLevel; - - } - - } - - if ( material.lightMap ) { - - uniforms.lightMap.value = material.lightMap; - uniforms.lightMapIntensity.value = material.lightMapIntensity; - - } - - if ( material.aoMap ) { - - uniforms.aoMap.value = material.aoMap; - uniforms.aoMapIntensity.value = material.aoMapIntensity; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. displacementMap map - // 4. normal map - // 5. bump map - // 6. roughnessMap map - // 7. metalnessMap map - // 8. alphaMap map - // 9. emissiveMap map - // 10. clearcoat map - // 11. clearcoat normal map - // 12. clearcoat roughnessMap map - // 13. specular intensity map - // 14. specular tint map - // 15. transmission map - // 16. thickness map - - let uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.specularMap ) { - - uvScaleMap = material.specularMap; - - } else if ( material.displacementMap ) { - - uvScaleMap = material.displacementMap; - - } else if ( material.normalMap ) { - - uvScaleMap = material.normalMap; - - } else if ( material.bumpMap ) { - - uvScaleMap = material.bumpMap; - - } else if ( material.roughnessMap ) { - - uvScaleMap = material.roughnessMap; - - } else if ( material.metalnessMap ) { - - uvScaleMap = material.metalnessMap; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } else if ( material.emissiveMap ) { - - uvScaleMap = material.emissiveMap; - - } else if ( material.clearcoatMap ) { - - uvScaleMap = material.clearcoatMap; - - } else if ( material.clearcoatNormalMap ) { - - uvScaleMap = material.clearcoatNormalMap; - - } else if ( material.clearcoatRoughnessMap ) { - - uvScaleMap = material.clearcoatRoughnessMap; - - } else if ( material.specularIntensityMap ) { - - uvScaleMap = material.specularIntensityMap; - - } else if ( material.specularTintMap ) { - - uvScaleMap = material.specularTintMap; - - } else if ( material.transmissionMap ) { - - uvScaleMap = material.transmissionMap; - - } else if ( material.thicknessMap ) { - - uvScaleMap = material.thicknessMap; - - } - - if ( uvScaleMap !== undefined ) { - - // backwards compatibility - if ( uvScaleMap.isWebGLRenderTarget ) { - - uvScaleMap = uvScaleMap.texture; - - } - - if ( uvScaleMap.matrixAutoUpdate === true ) { - - uvScaleMap.updateMatrix(); - - } - - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - - } - - // uv repeat and offset setting priorities for uv2 - // 1. ao map - // 2. light map - - let uv2ScaleMap; - - if ( material.aoMap ) { - - uv2ScaleMap = material.aoMap; - - } else if ( material.lightMap ) { - - uv2ScaleMap = material.lightMap; - - } - - if ( uv2ScaleMap !== undefined ) { - - // backwards compatibility - if ( uv2ScaleMap.isWebGLRenderTarget ) { - - uv2ScaleMap = uv2ScaleMap.texture; - - } - - if ( uv2ScaleMap.matrixAutoUpdate === true ) { - - uv2ScaleMap.updateMatrix(); - - } - - uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix ); - - } - - } - - function refreshUniformsLine( uniforms, material ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - - } - - function refreshUniformsDash( uniforms, material ) { - - uniforms.dashSize.value = material.dashSize; - uniforms.totalSize.value = material.dashSize + material.gapSize; - uniforms.scale.value = material.scale; - - } - - function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.size.value = material.size * pixelRatio; - uniforms.scale.value = height * 0.5; - - if ( material.map ) { - - uniforms.map.value = material.map; - - } - - if ( material.alphaMap ) { - - uniforms.alphaMap.value = material.alphaMap; - - } - - if ( material.alphaTest > 0 ) { - - uniforms.alphaTest.value = material.alphaTest; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map - - let uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } - - if ( uvScaleMap !== undefined ) { - - if ( uvScaleMap.matrixAutoUpdate === true ) { - - uvScaleMap.updateMatrix(); - - } - - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - - } - - } - - function refreshUniformsSprites( uniforms, material ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.rotation.value = material.rotation; - - if ( material.map ) { - - uniforms.map.value = material.map; - - } - - if ( material.alphaMap ) { - - uniforms.alphaMap.value = material.alphaMap; - - } - - if ( material.alphaTest > 0 ) { - - uniforms.alphaTest.value = material.alphaTest; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map - - let uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } - - if ( uvScaleMap !== undefined ) { - - if ( uvScaleMap.matrixAutoUpdate === true ) { - - uvScaleMap.updateMatrix(); - - } - - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - - } - - } - - function refreshUniformsLambert( uniforms, material ) { - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - } - - function refreshUniformsPhong( uniforms, material ) { - - uniforms.specular.value.copy( material.specular ); - uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsToon( uniforms, material ) { - - if ( material.gradientMap ) { - - uniforms.gradientMap.value = material.gradientMap; - - } - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsStandard( uniforms, material ) { - - uniforms.roughness.value = material.roughness; - uniforms.metalness.value = material.metalness; - - if ( material.roughnessMap ) { - - uniforms.roughnessMap.value = material.roughnessMap; - - } - - if ( material.metalnessMap ) { - - uniforms.metalnessMap.value = material.metalnessMap; - - } - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - const envMap = properties.get( material ).envMap; - - if ( envMap ) { - - //uniforms.envMap.value = material.envMap; // part of uniforms common - uniforms.envMapIntensity.value = material.envMapIntensity; - - } - - } - - function refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ) { - - refreshUniformsStandard( uniforms, material ); - - uniforms.ior.value = material.ior; // also part of uniforms common - - if ( material.sheenTint ) uniforms.sheenTint.value.copy( material.sheenTint ); - - if ( material.clearcoat > 0 ) { - - uniforms.clearcoat.value = material.clearcoat; - uniforms.clearcoatRoughness.value = material.clearcoatRoughness; - - if ( material.clearcoatMap ) { - - uniforms.clearcoatMap.value = material.clearcoatMap; - - } - - if ( material.clearcoatRoughnessMap ) { - - uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; - - } - - if ( material.clearcoatNormalMap ) { - - uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); - uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; - - if ( material.side === BackSide ) { - - uniforms.clearcoatNormalScale.value.negate(); - - } - - } - - } - - if ( material.transmission > 0 ) { - - uniforms.transmission.value = material.transmission; - uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; - uniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height ); - - if ( material.transmissionMap ) { - - uniforms.transmissionMap.value = material.transmissionMap; - - } - - uniforms.thickness.value = material.thickness; - - if ( material.thicknessMap ) { - - uniforms.thicknessMap.value = material.thicknessMap; - - } - - uniforms.attenuationDistance.value = material.attenuationDistance; - uniforms.attenuationTint.value.copy( material.attenuationTint ); - - } - - uniforms.specularIntensity.value = material.specularIntensity; - uniforms.specularTint.value.copy( material.specularTint ); - - if ( material.specularIntensityMap ) { - - uniforms.specularIntensityMap.value = material.specularIntensityMap; - - } - - if ( material.specularTintMap ) { - - uniforms.specularTintMap.value = material.specularTintMap; - - } - - } - - function refreshUniformsMatcap( uniforms, material ) { - - if ( material.matcap ) { - - uniforms.matcap.value = material.matcap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsDepth( uniforms, material ) { - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsDistance( uniforms, material ) { - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - uniforms.referencePosition.value.copy( material.referencePosition ); - uniforms.nearDistance.value = material.nearDistance; - uniforms.farDistance.value = material.farDistance; - - } - - function refreshUniformsNormal( uniforms, material ) { - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - return { - refreshFogUniforms: refreshFogUniforms, - refreshMaterialUniforms: refreshMaterialUniforms - }; - - } - - function createCanvasElement() { - - const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - canvas.style.display = 'block'; - return canvas; - - } - - function WebGLRenderer( parameters = {} ) { - - const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), - _context = parameters.context !== undefined ? parameters.context : null, - - _alpha = parameters.alpha !== undefined ? parameters.alpha : false, - _depth = parameters.depth !== undefined ? parameters.depth : true, - _stencil = parameters.stencil !== undefined ? parameters.stencil : true, - _antialias = parameters.antialias !== undefined ? parameters.antialias : false, - _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, - _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, - _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default', - _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; - - let currentRenderList = null; - let currentRenderState = null; - - // render() can be called from within a callback triggered by another render. - // We track this so that the nested render call gets its list and state isolated from the parent render call. - - const renderListStack = []; - const renderStateStack = []; - - // public properties - - this.domElement = _canvas; - - // Debug configuration container - this.debug = { - - /** - * Enables error checking and reporting when shader programs are being compiled - * @type {boolean} - */ - checkShaderErrors: true - }; - - // clearing - - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; - - // scene graph - - this.sortObjects = true; - - // user-defined clipping - - this.clippingPlanes = []; - this.localClippingEnabled = false; - - // physically based shading - - this.gammaFactor = 2.0; // for backwards compatibility - this.outputEncoding = LinearEncoding; - - // physical lights - - this.physicallyCorrectLights = false; - - // tone mapping - - this.toneMapping = NoToneMapping; - this.toneMappingExposure = 1.0; - - // internal properties - - const _this = this; - - let _isContextLost = false; - - // internal state cache - - let _currentActiveCubeFace = 0; - let _currentActiveMipmapLevel = 0; - let _currentRenderTarget = null; - let _currentMaterialId = - 1; - - let _currentCamera = null; - - const _currentViewport = new Vector4(); - const _currentScissor = new Vector4(); - let _currentScissorTest = null; - - // - - let _width = _canvas.width; - let _height = _canvas.height; - - let _pixelRatio = 1; - let _opaqueSort = null; - let _transparentSort = null; - - const _viewport = new Vector4( 0, 0, _width, _height ); - const _scissor = new Vector4( 0, 0, _width, _height ); - let _scissorTest = false; - - // - - const _currentDrawBuffers = []; - - // frustum - - const _frustum = new Frustum(); - - // clipping - - let _clippingEnabled = false; - let _localClippingEnabled = false; - - // transmission - - let _transmissionRenderTarget = null; - - // camera matrices cache - - const _projScreenMatrix = new Matrix4(); - - const _vector3 = new Vector3(); - - const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; - - function getTargetPixelRatio() { - - return _currentRenderTarget === null ? _pixelRatio : 1; - - } - - // initialize - - let _gl = _context; - - function getContext( contextNames, contextAttributes ) { - - for ( let i = 0; i < contextNames.length; i ++ ) { - - const contextName = contextNames[ i ]; - const context = _canvas.getContext( contextName, contextAttributes ); - if ( context !== null ) return context; - - } - - return null; - - } - - try { - - const contextAttributes = { - alpha: _alpha, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer, - powerPreference: _powerPreference, - failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat - }; - - // event listeners must be registered before WebGL context is created, see #12753 - - _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); - - if ( _gl === null ) { - - const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ]; - - if ( _this.isWebGL1Renderer === true ) { - - contextNames.shift(); - - } - - _gl = getContext( contextNames, contextAttributes ); - - if ( _gl === null ) { - - if ( getContext( contextNames ) ) { - - throw new Error( 'Error creating WebGL context with your selected attributes.' ); - - } else { - - throw new Error( 'Error creating WebGL context.' ); - - } - - } - - } - - // Some experimental-webgl implementations do not have getShaderPrecisionFormat - - if ( _gl.getShaderPrecisionFormat === undefined ) { - - _gl.getShaderPrecisionFormat = function () { - - return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; - - }; - - } - - } catch ( error ) { - - console.error( 'THREE.WebGLRenderer: ' + error.message ); - throw error; - - } - - let extensions, capabilities, state, info; - let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; - let programCache, materials, renderLists, renderStates, clipping, shadowMap; - - let background, morphtargets, bufferRenderer, indexedBufferRenderer; - - let utils, bindingStates; - - function initGLContext() { - - extensions = new WebGLExtensions( _gl ); - - capabilities = new WebGLCapabilities( _gl, extensions, parameters ); - - extensions.init( capabilities ); - - utils = new WebGLUtils( _gl, extensions, capabilities ); - - state = new WebGLState( _gl, extensions, capabilities ); - - _currentDrawBuffers[ 0 ] = 1029; - - info = new WebGLInfo( _gl ); - properties = new WebGLProperties(); - textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); - cubemaps = new WebGLCubeMaps( _this ); - cubeuvmaps = new WebGLCubeUVMaps( _this ); - attributes = new WebGLAttributes( _gl, capabilities ); - bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); - geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); - objects = new WebGLObjects( _gl, geometries, attributes, info ); - morphtargets = new WebGLMorphtargets( _gl ); - clipping = new WebGLClipping( properties ); - programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ); - materials = new WebGLMaterials( properties ); - renderLists = new WebGLRenderLists( properties ); - renderStates = new WebGLRenderStates( extensions, capabilities ); - background = new WebGLBackground( _this, cubemaps, state, objects, _premultipliedAlpha ); - shadowMap = new WebGLShadowMap( _this, objects, capabilities ); - - bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); - indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); - - info.programs = programCache.programs; - - _this.capabilities = capabilities; - _this.extensions = extensions; - _this.properties = properties; - _this.renderLists = renderLists; - _this.shadowMap = shadowMap; - _this.state = state; - _this.info = info; - - } - - initGLContext(); - - // xr - - const xr = new WebXRManager( _this, _gl ); - - this.xr = xr; - - // API - - this.getContext = function () { - - return _gl; - - }; - - this.getContextAttributes = function () { - - return _gl.getContextAttributes(); - - }; - - this.forceContextLoss = function () { - - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.loseContext(); - - }; - - this.forceContextRestore = function () { - - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.restoreContext(); - - }; - - this.getPixelRatio = function () { - - return _pixelRatio; - - }; - - this.setPixelRatio = function ( value ) { - - if ( value === undefined ) return; - - _pixelRatio = value; - - this.setSize( _width, _height, false ); - - }; - - this.getSize = function ( target ) { - - return target.set( _width, _height ); - - }; - - this.setSize = function ( width, height, updateStyle ) { - - if ( xr.isPresenting ) { - - console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); - return; - - } - - _width = width; - _height = height; - - _canvas.width = Math.floor( width * _pixelRatio ); - _canvas.height = Math.floor( height * _pixelRatio ); - - if ( updateStyle !== false ) { - - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; - - } - - this.setViewport( 0, 0, width, height ); - - }; - - this.getDrawingBufferSize = function ( target ) { - - return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); - - }; - - this.setDrawingBufferSize = function ( width, height, pixelRatio ) { - - _width = width; - _height = height; - - _pixelRatio = pixelRatio; - - _canvas.width = Math.floor( width * pixelRatio ); - _canvas.height = Math.floor( height * pixelRatio ); - - this.setViewport( 0, 0, width, height ); - - }; - - this.getCurrentViewport = function ( target ) { - - return target.copy( _currentViewport ); - - }; - - this.getViewport = function ( target ) { - - return target.copy( _viewport ); - - }; - - this.setViewport = function ( x, y, width, height ) { - - if ( x.isVector4 ) { - - _viewport.set( x.x, x.y, x.z, x.w ); - - } else { - - _viewport.set( x, y, width, height ); - - } - - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); - - }; - - this.getScissor = function ( target ) { - - return target.copy( _scissor ); - - }; - - this.setScissor = function ( x, y, width, height ) { - - if ( x.isVector4 ) { - - _scissor.set( x.x, x.y, x.z, x.w ); - - } else { - - _scissor.set( x, y, width, height ); - - } - - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - - }; - - this.getScissorTest = function () { - - return _scissorTest; - - }; - - this.setScissorTest = function ( boolean ) { - - state.setScissorTest( _scissorTest = boolean ); - - }; - - this.setOpaqueSort = function ( method ) { - - _opaqueSort = method; - - }; - - this.setTransparentSort = function ( method ) { - - _transparentSort = method; - - }; - - // Clearing - - this.getClearColor = function ( target ) { - - return target.copy( background.getClearColor() ); - - }; - - this.setClearColor = function () { - - background.setClearColor.apply( background, arguments ); - - }; - - this.getClearAlpha = function () { - - return background.getClearAlpha(); - - }; - - this.setClearAlpha = function () { - - background.setClearAlpha.apply( background, arguments ); - - }; - - this.clear = function ( color, depth, stencil ) { - - let bits = 0; - - if ( color === undefined || color ) bits |= 16384; - if ( depth === undefined || depth ) bits |= 256; - if ( stencil === undefined || stencil ) bits |= 1024; - - _gl.clear( bits ); - - }; - - this.clearColor = function () { - - this.clear( true, false, false ); - - }; - - this.clearDepth = function () { - - this.clear( false, true, false ); - - }; - - this.clearStencil = function () { - - this.clear( false, false, true ); - - }; - - // - - this.dispose = function () { - - _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); - - renderLists.dispose(); - renderStates.dispose(); - properties.dispose(); - cubemaps.dispose(); - cubeuvmaps.dispose(); - objects.dispose(); - bindingStates.dispose(); - - xr.dispose(); - - xr.removeEventListener( 'sessionstart', onXRSessionStart ); - xr.removeEventListener( 'sessionend', onXRSessionEnd ); - - if ( _transmissionRenderTarget ) { - - _transmissionRenderTarget.dispose(); - _transmissionRenderTarget = null; - - } - - animation.stop(); - - }; - - // Events - - function onContextLost( event ) { - - event.preventDefault(); - - console.log( 'THREE.WebGLRenderer: Context Lost.' ); - - _isContextLost = true; - - } - - function onContextRestore( /* event */ ) { - - console.log( 'THREE.WebGLRenderer: Context Restored.' ); - - _isContextLost = false; - - const infoAutoReset = info.autoReset; - const shadowMapEnabled = shadowMap.enabled; - const shadowMapAutoUpdate = shadowMap.autoUpdate; - const shadowMapNeedsUpdate = shadowMap.needsUpdate; - const shadowMapType = shadowMap.type; - - initGLContext(); - - info.autoReset = infoAutoReset; - shadowMap.enabled = shadowMapEnabled; - shadowMap.autoUpdate = shadowMapAutoUpdate; - shadowMap.needsUpdate = shadowMapNeedsUpdate; - shadowMap.type = shadowMapType; - - } - - function onMaterialDispose( event ) { - - const material = event.target; - - material.removeEventListener( 'dispose', onMaterialDispose ); - - deallocateMaterial( material ); - - } - - // Buffer deallocation - - function deallocateMaterial( material ) { - - releaseMaterialProgramReferences( material ); - - properties.remove( material ); - - } - - - function releaseMaterialProgramReferences( material ) { - - const programs = properties.get( material ).programs; - - if ( programs !== undefined ) { - - programs.forEach( function ( program ) { - - programCache.releaseProgram( program ); - - } ); - - } - - } - - // Buffer rendering - - function renderObjectImmediate( object, program ) { - - object.render( function ( object ) { - - _this.renderBufferImmediate( object, program ); - - } ); - - } - - this.renderBufferImmediate = function ( object, program ) { - - bindingStates.initAttributes(); - - const buffers = properties.get( object ); - - if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); - if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); - if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); - if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); - - const programAttributes = program.getAttributes(); - - if ( object.hasPositions ) { - - _gl.bindBuffer( 34962, buffers.position ); - _gl.bufferData( 34962, object.positionArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.position.location ); - _gl.vertexAttribPointer( programAttributes.position.location, 3, 5126, false, 0, 0 ); - - } - - if ( object.hasNormals ) { - - _gl.bindBuffer( 34962, buffers.normal ); - _gl.bufferData( 34962, object.normalArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.normal.location ); - _gl.vertexAttribPointer( programAttributes.normal.location, 3, 5126, false, 0, 0 ); - - } - - if ( object.hasUvs ) { - - _gl.bindBuffer( 34962, buffers.uv ); - _gl.bufferData( 34962, object.uvArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.uv.location ); - _gl.vertexAttribPointer( programAttributes.uv.location, 2, 5126, false, 0, 0 ); - - } - - if ( object.hasColors ) { - - _gl.bindBuffer( 34962, buffers.color ); - _gl.bufferData( 34962, object.colorArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.color.location ); - _gl.vertexAttribPointer( programAttributes.color.location, 3, 5126, false, 0, 0 ); - - } - - bindingStates.disableUnusedAttributes(); - - _gl.drawArrays( 4, 0, object.count ); - - object.count = 0; - - }; - - this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { - - if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) - - const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); - - const program = setProgram( camera, scene, material, object ); - - state.setMaterial( material, frontFaceCW ); - - // - - let index = geometry.index; - const position = geometry.attributes.position; - - // - - if ( index === null ) { - - if ( position === undefined || position.count === 0 ) return; - - } else if ( index.count === 0 ) { - - return; - - } - - // - - let rangeFactor = 1; - - if ( material.wireframe === true ) { - - index = geometries.getWireframeAttribute( geometry ); - rangeFactor = 2; - - } - - if ( geometry.morphAttributes.position !== undefined || geometry.morphAttributes.normal !== undefined ) { - - morphtargets.update( object, geometry, material, program ); - - } - - bindingStates.setup( object, material, program, geometry, index ); - - let attribute; - let renderer = bufferRenderer; - - if ( index !== null ) { - - attribute = attributes.get( index ); - - renderer = indexedBufferRenderer; - renderer.setIndex( attribute ); - - } - - // - - const dataCount = ( index !== null ) ? index.count : position.count; - - const rangeStart = geometry.drawRange.start * rangeFactor; - const rangeCount = geometry.drawRange.count * rangeFactor; - - const groupStart = group !== null ? group.start * rangeFactor : 0; - const groupCount = group !== null ? group.count * rangeFactor : Infinity; - - const drawStart = Math.max( rangeStart, groupStart ); - const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; - - const drawCount = Math.max( 0, drawEnd - drawStart + 1 ); - - if ( drawCount === 0 ) return; - - // - - if ( object.isMesh ) { - - if ( material.wireframe === true ) { - - state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); - renderer.setMode( 1 ); - - } else { - - renderer.setMode( 4 ); - - } - - } else if ( object.isLine ) { - - let lineWidth = material.linewidth; - - if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material - - state.setLineWidth( lineWidth * getTargetPixelRatio() ); - - if ( object.isLineSegments ) { - - renderer.setMode( 1 ); - - } else if ( object.isLineLoop ) { - - renderer.setMode( 2 ); - - } else { - - renderer.setMode( 3 ); - - } - - } else if ( object.isPoints ) { - - renderer.setMode( 0 ); - - } else if ( object.isSprite ) { - - renderer.setMode( 4 ); - - } - - if ( object.isInstancedMesh ) { - - renderer.renderInstances( drawStart, drawCount, object.count ); - - } else if ( geometry.isInstancedBufferGeometry ) { - - const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount ); - - renderer.renderInstances( drawStart, drawCount, instanceCount ); - - } else { - - renderer.render( drawStart, drawCount ); - - } - - }; - - // Compile - - this.compile = function ( scene, camera ) { - - currentRenderState = renderStates.get( scene ); - currentRenderState.init(); - - renderStateStack.push( currentRenderState ); - - scene.traverseVisible( function ( object ) { - - if ( object.isLight && object.layers.test( camera.layers ) ) { - - currentRenderState.pushLight( object ); - - if ( object.castShadow ) { - - currentRenderState.pushShadow( object ); - - } - - } - - } ); - - currentRenderState.setupLights( _this.physicallyCorrectLights ); - - scene.traverse( function ( object ) { - - const material = object.material; - - if ( material ) { - - if ( Array.isArray( material ) ) { - - for ( let i = 0; i < material.length; i ++ ) { - - const material2 = material[ i ]; - - getProgram( material2, scene, object ); - - } - - } else { - - getProgram( material, scene, object ); - - } - - } - - } ); - - renderStateStack.pop(); - currentRenderState = null; - - }; - - // Animation Loop - - let onAnimationFrameCallback = null; - - function onAnimationFrame( time ) { - - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); - - } - - function onXRSessionStart() { - - animation.stop(); - - } - - function onXRSessionEnd() { - - animation.start(); - - } - - const animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); - - if ( typeof window !== 'undefined' ) animation.setContext( window ); - - this.setAnimationLoop = function ( callback ) { - - onAnimationFrameCallback = callback; - xr.setAnimationLoop( callback ); - - ( callback === null ) ? animation.stop() : animation.start(); - - }; - - xr.addEventListener( 'sessionstart', onXRSessionStart ); - xr.addEventListener( 'sessionend', onXRSessionEnd ); - - // Rendering - - this.render = function ( scene, camera ) { - - if ( camera !== undefined && camera.isCamera !== true ) { - - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; - - } - - if ( _isContextLost === true ) return; - - // update scene graph - - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - - // update camera matrices and frustum - - if ( camera.parent === null ) camera.updateMatrixWorld(); - - if ( xr.enabled === true && xr.isPresenting === true ) { - - if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); - - camera = xr.getCamera(); // use XR camera for rendering - - } - - // - if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget ); - - currentRenderState = renderStates.get( scene, renderStateStack.length ); - currentRenderState.init(); - - renderStateStack.push( currentRenderState ); - - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromProjectionMatrix( _projScreenMatrix ); - - _localClippingEnabled = this.localClippingEnabled; - _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); - - currentRenderList = renderLists.get( scene, renderListStack.length ); - currentRenderList.init(); - - renderListStack.push( currentRenderList ); - - projectObject( scene, camera, 0, _this.sortObjects ); - - currentRenderList.finish(); - - if ( _this.sortObjects === true ) { - - currentRenderList.sort( _opaqueSort, _transparentSort ); - - } - - // - - if ( _clippingEnabled === true ) clipping.beginShadows(); - - const shadowsArray = currentRenderState.state.shadowsArray; - - shadowMap.render( shadowsArray, scene, camera ); - - if ( _clippingEnabled === true ) clipping.endShadows(); - - // - - if ( this.info.autoReset === true ) this.info.reset(); - - // - - background.render( currentRenderList, scene ); - - // render scene - - currentRenderState.setupLights( _this.physicallyCorrectLights ); - - if ( camera.isArrayCamera ) { - - const cameras = camera.cameras; - - for ( let i = 0, l = cameras.length; i < l; i ++ ) { - - const camera2 = cameras[ i ]; - - renderScene( currentRenderList, scene, camera2, camera2.viewport ); - - } - - } else { - - renderScene( currentRenderList, scene, camera ); - - } - - // - - if ( _currentRenderTarget !== null ) { - - // resolve multisample renderbuffers to a single-sample texture if necessary - - textures.updateMultisampleRenderTarget( _currentRenderTarget ); - - // Generate mipmap if we're using any kind of mipmap filtering - - textures.updateRenderTargetMipmap( _currentRenderTarget ); - - } - - // - - if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); - - // Ensure depth buffer writing is enabled so it can be cleared on next render - - state.buffers.depth.setTest( true ); - state.buffers.depth.setMask( true ); - state.buffers.color.setMask( true ); - - state.setPolygonOffset( false ); - - // _gl.finish(); - - bindingStates.resetDefaultState(); - _currentMaterialId = - 1; - _currentCamera = null; - - renderStateStack.pop(); - - if ( renderStateStack.length > 0 ) { - - currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; - - } else { - - currentRenderState = null; - - } - - renderListStack.pop(); - - if ( renderListStack.length > 0 ) { - - currentRenderList = renderListStack[ renderListStack.length - 1 ]; - - } else { - - currentRenderList = null; - - } - - }; - - function projectObject( object, camera, groupOrder, sortObjects ) { - - if ( object.visible === false ) return; - - const visible = object.layers.test( camera.layers ); - - if ( visible ) { - - if ( object.isGroup ) { - - groupOrder = object.renderOrder; - - } else if ( object.isLOD ) { - - if ( object.autoUpdate === true ) object.update( camera ); - - } else if ( object.isLight ) { - - currentRenderState.pushLight( object ); - - if ( object.castShadow ) { - - currentRenderState.pushShadow( object ); - - } - - } else if ( object.isSprite ) { - - if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { - - if ( sortObjects ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - const geometry = objects.update( object ); - const material = object.material; - - if ( material.visible ) { - - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - - } - - } - - } else if ( object.isImmediateRenderObject ) { - - if ( sortObjects ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null ); - - } else if ( object.isMesh || object.isLine || object.isPoints ) { - - if ( object.isSkinnedMesh ) { - - // update skeleton only once in a frame - - if ( object.skeleton.frame !== info.render.frame ) { - - object.skeleton.update(); - object.skeleton.frame = info.render.frame; - - } - - } - - if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { - - if ( sortObjects ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - const geometry = objects.update( object ); - const material = object.material; - - if ( Array.isArray( material ) ) { - - const groups = geometry.groups; - - for ( let i = 0, l = groups.length; i < l; i ++ ) { - - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; - - if ( groupMaterial && groupMaterial.visible ) { - - currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); - - } - - } - - } else if ( material.visible ) { - - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - - } - - } - - } - - } - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - projectObject( children[ i ], camera, groupOrder, sortObjects ); - - } - - } - - function renderScene( currentRenderList, scene, camera, viewport ) { - - const opaqueObjects = currentRenderList.opaque; - const transmissiveObjects = currentRenderList.transmissive; - const transparentObjects = currentRenderList.transparent; - - currentRenderState.setupLightsView( camera ); - - if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, scene, camera ); - - if ( viewport ) state.viewport( _currentViewport.copy( viewport ) ); - - if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); - if ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera ); - if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); - - } - - function renderTransmissionPass( opaqueObjects, scene, camera ) { - - if ( _transmissionRenderTarget === null ) { - - const needsAntialias = _antialias === true && capabilities.isWebGL2 === true; - const renderTargetType = needsAntialias ? WebGLMultisampleRenderTarget : WebGLRenderTarget; - - _transmissionRenderTarget = new renderTargetType( 1024, 1024, { - generateMipmaps: true, - type: utils.convert( HalfFloatType ) !== null ? HalfFloatType : UnsignedByteType, - minFilter: LinearMipmapLinearFilter, - magFilter: NearestFilter, - wrapS: ClampToEdgeWrapping, - wrapT: ClampToEdgeWrapping - } ); - - } - - const currentRenderTarget = _this.getRenderTarget(); - _this.setRenderTarget( _transmissionRenderTarget ); - _this.clear(); - - // Turn off the features which can affect the frag color for opaque objects pass. - // Otherwise they are applied twice in opaque objects pass and transmission objects pass. - const currentToneMapping = _this.toneMapping; - _this.toneMapping = NoToneMapping; - - renderObjects( opaqueObjects, scene, camera ); - - _this.toneMapping = currentToneMapping; - - textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); - textures.updateRenderTargetMipmap( _transmissionRenderTarget ); - - _this.setRenderTarget( currentRenderTarget ); - - } - - function renderObjects( renderList, scene, camera ) { - - const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; - - for ( let i = 0, l = renderList.length; i < l; i ++ ) { - - const renderItem = renderList[ i ]; - - const object = renderItem.object; - const geometry = renderItem.geometry; - const material = overrideMaterial === null ? renderItem.material : overrideMaterial; - const group = renderItem.group; - - if ( object.layers.test( camera.layers ) ) { - - renderObject( object, scene, camera, geometry, material, group ); - - } - - } - - } - - function renderObject( object, scene, camera, geometry, material, group ) { - - object.onBeforeRender( _this, scene, camera, geometry, material, group ); - - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - - if ( object.isImmediateRenderObject ) { - - const program = setProgram( camera, scene, material, object ); - - state.setMaterial( material ); - - bindingStates.reset(); - - renderObjectImmediate( object, program ); - - } else { - - if ( material.transparent === true && material.side === DoubleSide ) { - - material.side = BackSide; - material.needsUpdate = true; - _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - - material.side = FrontSide; - material.needsUpdate = true; - _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - - material.side = DoubleSide; - - } else { - - _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - - } - - } - - object.onAfterRender( _this, scene, camera, geometry, material, group ); - - } - - function getProgram( material, scene, object ) { - - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - - const materialProperties = properties.get( material ); - - const lights = currentRenderState.state.lights; - const shadowsArray = currentRenderState.state.shadowsArray; - - const lightsStateVersion = lights.state.version; - - const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); - const programCacheKey = programCache.getProgramCacheKey( parameters ); - - let programs = materialProperties.programs; - - // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change - - materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; - materialProperties.fog = scene.fog; - materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment ); - - if ( programs === undefined ) { - - // new material - - material.addEventListener( 'dispose', onMaterialDispose ); - - programs = new Map(); - materialProperties.programs = programs; - - } - - let program = programs.get( programCacheKey ); - - if ( program !== undefined ) { - - // early out if program and light state is identical - - if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { - - updateCommonMaterialProperties( material, parameters ); - - return program; - - } - - } else { - - parameters.uniforms = programCache.getUniforms( material ); - - material.onBuild( parameters, _this ); - - material.onBeforeCompile( parameters, _this ); - - program = programCache.acquireProgram( parameters, programCacheKey ); - programs.set( programCacheKey, program ); - - materialProperties.uniforms = parameters.uniforms; - - } - - const uniforms = materialProperties.uniforms; - - if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { - - uniforms.clippingPlanes = clipping.uniform; - - } - - updateCommonMaterialProperties( material, parameters ); - - // store the light setup it was created for - - materialProperties.needsLights = materialNeedsLights( material ); - materialProperties.lightsStateVersion = lightsStateVersion; - - if ( materialProperties.needsLights ) { - - // wire up the material to this renderer's lighting state - - uniforms.ambientLightColor.value = lights.state.ambient; - uniforms.lightProbe.value = lights.state.probe; - uniforms.directionalLights.value = lights.state.directional; - uniforms.directionalLightShadows.value = lights.state.directionalShadow; - uniforms.spotLights.value = lights.state.spot; - uniforms.spotLightShadows.value = lights.state.spotShadow; - uniforms.rectAreaLights.value = lights.state.rectArea; - uniforms.ltc_1.value = lights.state.rectAreaLTC1; - uniforms.ltc_2.value = lights.state.rectAreaLTC2; - uniforms.pointLights.value = lights.state.point; - uniforms.pointLightShadows.value = lights.state.pointShadow; - uniforms.hemisphereLights.value = lights.state.hemi; - - uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; - uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; - uniforms.spotShadowMap.value = lights.state.spotShadowMap; - uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; - uniforms.pointShadowMap.value = lights.state.pointShadowMap; - uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; - // TODO (abelnation): add area lights shadow info to uniforms - - } - - const progUniforms = program.getUniforms(); - const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); - - materialProperties.currentProgram = program; - materialProperties.uniformsList = uniformsList; - - return program; - - } - - function updateCommonMaterialProperties( material, parameters ) { - - const materialProperties = properties.get( material ); - - materialProperties.outputEncoding = parameters.outputEncoding; - materialProperties.instancing = parameters.instancing; - materialProperties.skinning = parameters.skinning; - materialProperties.morphTargets = parameters.morphTargets; - materialProperties.morphNormals = parameters.morphNormals; - materialProperties.numClippingPlanes = parameters.numClippingPlanes; - materialProperties.numIntersection = parameters.numClipIntersection; - materialProperties.vertexAlphas = parameters.vertexAlphas; - materialProperties.vertexTangents = parameters.vertexTangents; - - } - - function setProgram( camera, scene, material, object ) { - - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - - textures.resetTextureUnits(); - - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; - const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding; - const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); - const vertexAlphas = material.vertexColors === true && !! object.geometry && !! object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4; - const vertexTangents = !! object.geometry && !! object.geometry.attributes.tangent; - const morphTargets = !! object.geometry && !! object.geometry.morphAttributes.position; - const morphNormals = !! object.geometry && !! object.geometry.morphAttributes.normal; - - const materialProperties = properties.get( material ); - const lights = currentRenderState.state.lights; - - if ( _clippingEnabled === true ) { - - if ( _localClippingEnabled === true || camera !== _currentCamera ) { - - const useCache = - camera === _currentCamera && - material.id === _currentMaterialId; - - // we might want to call this function with some ClippingGroup - // object instead of the material, once it becomes feasible - // (#8465, #8379) - clipping.setState( material, camera, useCache ); - - } - - } - - // - - let needsProgramChange = false; - - if ( material.version === materialProperties.__version ) { - - if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { - - needsProgramChange = true; - - } else if ( materialProperties.outputEncoding !== encoding ) { - - needsProgramChange = true; - - } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { - - needsProgramChange = true; - - } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { - - needsProgramChange = true; - - } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { - - needsProgramChange = true; - - } else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) { - - needsProgramChange = true; - - } else if ( materialProperties.envMap !== envMap ) { - - needsProgramChange = true; - - } else if ( material.fog && materialProperties.fog !== fog ) { - - needsProgramChange = true; - - } else if ( materialProperties.numClippingPlanes !== undefined && - ( materialProperties.numClippingPlanes !== clipping.numPlanes || - materialProperties.numIntersection !== clipping.numIntersection ) ) { - - needsProgramChange = true; - - } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { - - needsProgramChange = true; - - } else if ( materialProperties.vertexTangents !== vertexTangents ) { - - needsProgramChange = true; - - } else if ( materialProperties.morphTargets !== morphTargets ) { - - needsProgramChange = true; - - } else if ( materialProperties.morphNormals !== morphNormals ) { - - needsProgramChange = true; - - } - - } else { - - needsProgramChange = true; - materialProperties.__version = material.version; - - } - - // - - let program = materialProperties.currentProgram; - - if ( needsProgramChange === true ) { - - program = getProgram( material, scene, object ); - - } - - let refreshProgram = false; - let refreshMaterial = false; - let refreshLights = false; - - const p_uniforms = program.getUniforms(), - m_uniforms = materialProperties.uniforms; - - if ( state.useProgram( program.program ) ) { - - refreshProgram = true; - refreshMaterial = true; - refreshLights = true; - - } - - if ( material.id !== _currentMaterialId ) { - - _currentMaterialId = material.id; - - refreshMaterial = true; - - } - - if ( refreshProgram || _currentCamera !== camera ) { - - p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); - - if ( capabilities.logarithmicDepthBuffer ) { - - p_uniforms.setValue( _gl, 'logDepthBufFC', - 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - - } - - if ( _currentCamera !== camera ) { - - _currentCamera = camera; - - // lighting uniforms depend on the camera so enforce an update - // now, in case this material supports lights - or later, when - // the next material that does gets activated: - - refreshMaterial = true; // set to true on material change - refreshLights = true; // remains set until update done - - } - - // load material specific uniforms - // (shader material also gets them for the sake of genericity) - - if ( material.isShaderMaterial || - material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshStandardMaterial || - material.envMap ) { - - const uCamPos = p_uniforms.map.cameraPosition; - - if ( uCamPos !== undefined ) { - - uCamPos.setValue( _gl, - _vector3.setFromMatrixPosition( camera.matrixWorld ) ); - - } - - } - - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial ) { - - p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); - - } - - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial || - material.isShadowMaterial || - object.isSkinnedMesh ) { - - p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); - - } - - } - - // skinning uniforms must be set even if material didn't change - // auto-setting of texture unit for bone texture must go before other textures - // otherwise textures used for skinning can take over texture units reserved for other material textures - - if ( object.isSkinnedMesh ) { - - p_uniforms.setOptional( _gl, object, 'bindMatrix' ); - p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); - - const skeleton = object.skeleton; - - if ( skeleton ) { - - if ( capabilities.floatVertexTextures ) { - - if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture(); - - p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); - p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); - - } else { - - p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); - - } - - } - - } - - if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { - - materialProperties.receiveShadow = object.receiveShadow; - p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); - - } - - if ( refreshMaterial ) { - - p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); - - if ( materialProperties.needsLights ) { - - // the current material requires lighting info - - // note: all lighting uniforms are always set correctly - // they simply reference the renderer's state for their - // values - // - // use the current material's .needsUpdate flags to set - // the GL state when required - - markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); - - } - - // refresh uniforms common to several materials - - if ( fog && material.fog ) { - - materials.refreshFogUniforms( m_uniforms, fog ); - - } - - materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget ); - - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - - } - - if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { - - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - material.uniformsNeedUpdate = false; - - } - - if ( material.isSpriteMaterial ) { - - p_uniforms.setValue( _gl, 'center', object.center ); - - } - - // common matrices - - p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); - p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); - p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); - - return program; - - } - - // If uniforms are marked as clean, they don't need to be loaded to the GPU. - - function markUniformsLightsNeedsUpdate( uniforms, value ) { - - uniforms.ambientLightColor.needsUpdate = value; - uniforms.lightProbe.needsUpdate = value; - - uniforms.directionalLights.needsUpdate = value; - uniforms.directionalLightShadows.needsUpdate = value; - uniforms.pointLights.needsUpdate = value; - uniforms.pointLightShadows.needsUpdate = value; - uniforms.spotLights.needsUpdate = value; - uniforms.spotLightShadows.needsUpdate = value; - uniforms.rectAreaLights.needsUpdate = value; - uniforms.hemisphereLights.needsUpdate = value; - - } - - function materialNeedsLights( material ) { - - return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || - material.isMeshStandardMaterial || material.isShadowMaterial || - ( material.isShaderMaterial && material.lights === true ); - - } - - this.getActiveCubeFace = function () { - - return _currentActiveCubeFace; - - }; - - this.getActiveMipmapLevel = function () { - - return _currentActiveMipmapLevel; - - }; - - this.getRenderTarget = function () { - - return _currentRenderTarget; - - }; - - this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { - - _currentRenderTarget = renderTarget; - _currentActiveCubeFace = activeCubeFace; - _currentActiveMipmapLevel = activeMipmapLevel; - - if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { - - textures.setupRenderTarget( renderTarget ); - - } - - let framebuffer = null; - let isCube = false; - let isRenderTarget3D = false; - - if ( renderTarget ) { - - const texture = renderTarget.texture; - - if ( texture.isDataTexture3D || texture.isDataTexture2DArray ) { - - isRenderTarget3D = true; - - } - - const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; - - if ( renderTarget.isWebGLCubeRenderTarget ) { - - framebuffer = __webglFramebuffer[ activeCubeFace ]; - isCube = true; - - } else if ( renderTarget.isWebGLMultisampleRenderTarget ) { - - framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; - - } else { - - framebuffer = __webglFramebuffer; - - } - - _currentViewport.copy( renderTarget.viewport ); - _currentScissor.copy( renderTarget.scissor ); - _currentScissorTest = renderTarget.scissorTest; - - } else { - - _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); - _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); - _currentScissorTest = _scissorTest; - - } - - const framebufferBound = state.bindFramebuffer( 36160, framebuffer ); - - if ( framebufferBound && capabilities.drawBuffers ) { - - let needsUpdate = false; - - if ( renderTarget ) { - - if ( renderTarget.isWebGLMultipleRenderTargets ) { - - const textures = renderTarget.texture; - - if ( _currentDrawBuffers.length !== textures.length || _currentDrawBuffers[ 0 ] !== 36064 ) { - - for ( let i = 0, il = textures.length; i < il; i ++ ) { - - _currentDrawBuffers[ i ] = 36064 + i; - - } - - _currentDrawBuffers.length = textures.length; - - needsUpdate = true; - - } - - } else { - - if ( _currentDrawBuffers.length !== 1 || _currentDrawBuffers[ 0 ] !== 36064 ) { - - _currentDrawBuffers[ 0 ] = 36064; - _currentDrawBuffers.length = 1; - - needsUpdate = true; - - } - - } - - } else { - - if ( _currentDrawBuffers.length !== 1 || _currentDrawBuffers[ 0 ] !== 1029 ) { - - _currentDrawBuffers[ 0 ] = 1029; - _currentDrawBuffers.length = 1; - - needsUpdate = true; - - } - - } - - if ( needsUpdate ) { - - if ( capabilities.isWebGL2 ) { - - _gl.drawBuffers( _currentDrawBuffers ); - - } else { - - extensions.get( 'WEBGL_draw_buffers' ).drawBuffersWEBGL( _currentDrawBuffers ); - - } - - } - - } - - state.viewport( _currentViewport ); - state.scissor( _currentScissor ); - state.setScissorTest( _currentScissorTest ); - - if ( isCube ) { - - const textureProperties = properties.get( renderTarget.texture ); - _gl.framebufferTexture2D( 36160, 36064, 34069 + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); - - } else if ( isRenderTarget3D ) { - - const textureProperties = properties.get( renderTarget.texture ); - const layer = activeCubeFace || 0; - _gl.framebufferTextureLayer( 36160, 36064, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); - - } - - _currentMaterialId = - 1; // reset current material to ensure correct uniform bindings - - }; - - this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { - - if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - return; - - } - - let framebuffer = properties.get( renderTarget ).__webglFramebuffer; - - if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { - - framebuffer = framebuffer[ activeCubeFaceIndex ]; - - } - - if ( framebuffer ) { - - state.bindFramebuffer( 36160, framebuffer ); - - try { - - const texture = renderTarget.texture; - const textureFormat = texture.format; - const textureType = texture.type; - - if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); - return; - - } - - const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); - - if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! halfFloatSupportedByExt ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); - return; - - } - - if ( _gl.checkFramebufferStatus( 36160 ) === 36053 ) { - - // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) - - if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); - - } - - } else { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); - - } - - } finally { - - // restore framebuffer of current render target if necessary - - const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; - state.bindFramebuffer( 36160, framebuffer ); - - } - - } - - }; - - this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { - - const levelScale = Math.pow( 2, - level ); - const width = Math.floor( texture.image.width * levelScale ); - const height = Math.floor( texture.image.height * levelScale ); - - let glFormat = utils.convert( texture.format ); - - if ( capabilities.isWebGL2 ) { - - // Workaround for https://bugs.chromium.org/p/chromium/issues/detail?id=1120100 - // Not needed in Chrome 93+ - - if ( glFormat === 6407 ) glFormat = 32849; - if ( glFormat === 6408 ) glFormat = 32856; - - } - - textures.setTexture2D( texture, 0 ); - - _gl.copyTexImage2D( 3553, level, glFormat, position.x, position.y, width, height, 0 ); - - state.unbindTexture(); - - }; - - this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { - - const width = srcTexture.image.width; - const height = srcTexture.image.height; - const glFormat = utils.convert( dstTexture.format ); - const glType = utils.convert( dstTexture.type ); - - textures.setTexture2D( dstTexture, 0 ); - - // As another texture upload may have changed pixelStorei - // parameters, make sure they are correct for the dstTexture - _gl.pixelStorei( 37440, dstTexture.flipY ); - _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); - _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); - - if ( srcTexture.isDataTexture ) { - - _gl.texSubImage2D( 3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); - - } else { - - if ( srcTexture.isCompressedTexture ) { - - _gl.compressedTexSubImage2D( 3553, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); - - } else { - - _gl.texSubImage2D( 3553, level, position.x, position.y, glFormat, glType, srcTexture.image ); - - } - - } - - // Generate mipmaps only when copying level 0 - if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( 3553 ); - - state.unbindTexture(); - - }; - - this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { - - if ( _this.isWebGL1Renderer ) { - - console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' ); - return; - - } - - const width = sourceBox.max.x - sourceBox.min.x + 1; - const height = sourceBox.max.y - sourceBox.min.y + 1; - const depth = sourceBox.max.z - sourceBox.min.z + 1; - const glFormat = utils.convert( dstTexture.format ); - const glType = utils.convert( dstTexture.type ); - let glTarget; - - if ( dstTexture.isDataTexture3D ) { - - textures.setTexture3D( dstTexture, 0 ); - glTarget = 32879; - - } else if ( dstTexture.isDataTexture2DArray ) { - - textures.setTexture2DArray( dstTexture, 0 ); - glTarget = 35866; - - } else { - - console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' ); - return; - - } - - _gl.pixelStorei( 37440, dstTexture.flipY ); - _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); - _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); - - const unpackRowLen = _gl.getParameter( 3314 ); - const unpackImageHeight = _gl.getParameter( 32878 ); - const unpackSkipPixels = _gl.getParameter( 3316 ); - const unpackSkipRows = _gl.getParameter( 3315 ); - const unpackSkipImages = _gl.getParameter( 32877 ); - - const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image; - - _gl.pixelStorei( 3314, image.width ); - _gl.pixelStorei( 32878, image.height ); - _gl.pixelStorei( 3316, sourceBox.min.x ); - _gl.pixelStorei( 3315, sourceBox.min.y ); - _gl.pixelStorei( 32877, sourceBox.min.z ); - - if ( srcTexture.isDataTexture || srcTexture.isDataTexture3D ) { - - _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data ); - - } else { - - if ( srcTexture.isCompressedTexture ) { - - console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture.' ); - _gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data ); - - } else { - - _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image ); - - } - - } - - _gl.pixelStorei( 3314, unpackRowLen ); - _gl.pixelStorei( 32878, unpackImageHeight ); - _gl.pixelStorei( 3316, unpackSkipPixels ); - _gl.pixelStorei( 3315, unpackSkipRows ); - _gl.pixelStorei( 32877, unpackSkipImages ); - - // Generate mipmaps only when copying level 0 - if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); - - state.unbindTexture(); - - }; - - this.initTexture = function ( texture ) { - - textures.setTexture2D( texture, 0 ); - - state.unbindTexture(); - - }; - - this.resetState = function () { - - _currentActiveCubeFace = 0; - _currentActiveMipmapLevel = 0; - _currentRenderTarget = null; - - state.reset(); - bindingStates.reset(); - - }; - - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef - - } - - } - - class WebGL1Renderer extends WebGLRenderer {} - - WebGL1Renderer.prototype.isWebGL1Renderer = true; - - class Scene extends Object3D { - - constructor() { - - super(); - - this.type = 'Scene'; - - this.background = null; - this.environment = null; - this.fog = null; - - this.overrideMaterial = null; - - this.autoUpdate = true; // checked by the renderer - - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef - - } - - } - - copy( source, recursive ) { - - super.copy( source, recursive ); - - if ( source.background !== null ) this.background = source.background.clone(); - if ( source.environment !== null ) this.environment = source.environment.clone(); - if ( source.fog !== null ) this.fog = source.fog.clone(); - - if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); - - this.autoUpdate = source.autoUpdate; - this.matrixAutoUpdate = source.matrixAutoUpdate; - - return this; - - } - - toJSON( meta ) { - - const data = super.toJSON( meta ); - - if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); - - return data; - - } - - } - - Scene.prototype.isScene = true; - - class InterleavedBuffer { - - constructor( array, stride ) { - - this.array = array; - this.stride = stride; - this.count = array !== undefined ? array.length / stride : 0; - - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; - - this.version = 0; - - this.uuid = generateUUID(); - - } - - onUploadCallback() {} - - set needsUpdate( value ) { - - if ( value === true ) this.version ++; - - } - - setUsage( value ) { - - this.usage = value; - - return this; - - } - - copy( source ) { - - this.array = new source.array.constructor( source.array ); - this.count = source.count; - this.stride = source.stride; - this.usage = source.usage; - - return this; - - } - - copyAt( index1, attribute, index2 ) { - - index1 *= this.stride; - index2 *= attribute.stride; - - for ( let i = 0, l = this.stride; i < l; i ++ ) { - - this.array[ index1 + i ] = attribute.array[ index2 + i ]; - - } - - return this; - - } - - set( value, offset = 0 ) { - - this.array.set( value, offset ); - - return this; - - } - - clone( data ) { - - if ( data.arrayBuffers === undefined ) { - - data.arrayBuffers = {}; - - } - - if ( this.array.buffer._uuid === undefined ) { - - this.array.buffer._uuid = generateUUID(); - - } - - if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - - data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; - - } - - const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); - - const ib = new this.constructor( array, this.stride ); - ib.setUsage( this.usage ); - - return ib; - - } - - onUpload( callback ) { - - this.onUploadCallback = callback; - - return this; - - } - - toJSON( data ) { - - if ( data.arrayBuffers === undefined ) { - - data.arrayBuffers = {}; - - } - - // generate UUID for array buffer if necessary - - if ( this.array.buffer._uuid === undefined ) { - - this.array.buffer._uuid = generateUUID(); - - } - - if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - - data.arrayBuffers[ this.array.buffer._uuid ] = Array.prototype.slice.call( new Uint32Array( this.array.buffer ) ); - - } - - // - - return { - uuid: this.uuid, - buffer: this.array.buffer._uuid, - type: this.array.constructor.name, - stride: this.stride - }; - - } - - } - - InterleavedBuffer.prototype.isInterleavedBuffer = true; - - const _vector$6 = /*@__PURE__*/ new Vector3(); - - class InterleavedBufferAttribute { - - constructor( interleavedBuffer, itemSize, offset, normalized = false ) { - - this.name = ''; - - this.data = interleavedBuffer; - this.itemSize = itemSize; - this.offset = offset; - - this.normalized = normalized === true; - - } - - get count() { - - return this.data.count; - - } - - get array() { - - return this.data.array; - - } - - set needsUpdate( value ) { - - this.data.needsUpdate = value; - - } - - applyMatrix4( m ) { - - for ( let i = 0, l = this.data.count; i < l; i ++ ) { - - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); - - _vector$6.applyMatrix4( m ); - - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - - } - - return this; - - } - - applyNormalMatrix( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); - - _vector$6.applyNormalMatrix( m ); - - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - - } - - return this; - - } - - transformDirection( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); - - _vector$6.transformDirection( m ); - - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - - } - - return this; - - } - - setX( index, x ) { - - this.data.array[ index * this.data.stride + this.offset ] = x; - - return this; - - } - - setY( index, y ) { - - this.data.array[ index * this.data.stride + this.offset + 1 ] = y; - - return this; - - } - - setZ( index, z ) { - - this.data.array[ index * this.data.stride + this.offset + 2 ] = z; - - return this; - - } - - setW( index, w ) { - - this.data.array[ index * this.data.stride + this.offset + 3 ] = w; - - return this; - - } - - getX( index ) { - - return this.data.array[ index * this.data.stride + this.offset ]; - - } - - getY( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 1 ]; - - } - - getZ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 2 ]; - - } - - getW( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 3 ]; - - } - - setXY( index, x, y ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - - return this; - - } - - setXYZ( index, x, y, z ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - - return this; - - } - - setXYZW( index, x, y, z, w ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - this.data.array[ index + 3 ] = w; - - return this; - - } - - clone( data ) { - - if ( data === undefined ) { - - console.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data.' ); - - const array = []; - - for ( let i = 0; i < this.count; i ++ ) { - - const index = i * this.data.stride + this.offset; - - for ( let j = 0; j < this.itemSize; j ++ ) { - - array.push( this.data.array[ index + j ] ); - - } - - } - - return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); - - } else { - - if ( data.interleavedBuffers === undefined ) { - - data.interleavedBuffers = {}; - - } - - if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { - - data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); - - } - - return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); - - } - - } - - toJSON( data ) { - - if ( data === undefined ) { - - console.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data.' ); - - const array = []; - - for ( let i = 0; i < this.count; i ++ ) { - - const index = i * this.data.stride + this.offset; - - for ( let j = 0; j < this.itemSize; j ++ ) { - - array.push( this.data.array[ index + j ] ); - - } - - } - - // deinterleave data and save it as an ordinary buffer attribute for now - - return { - itemSize: this.itemSize, - type: this.array.constructor.name, - array: array, - normalized: this.normalized - }; - - } else { - - // save as true interlaved attribtue - - if ( data.interleavedBuffers === undefined ) { - - data.interleavedBuffers = {}; - - } - - if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { - - data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); - - } - - return { - isInterleavedBufferAttribute: true, - itemSize: this.itemSize, - data: this.data.uuid, - offset: this.offset, - normalized: this.normalized - }; - - } - - } - - } - - InterleavedBufferAttribute.prototype.isInterleavedBufferAttribute = true; - - /** - * parameters = { - * color: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * rotation: , - * sizeAttenuation: - * } - */ - - class SpriteMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.type = 'SpriteMaterial'; - - this.color = new Color( 0xffffff ); - - this.map = null; - - this.alphaMap = null; - - this.rotation = 0; - - this.sizeAttenuation = true; - - this.transparent = true; - - this.setValues( parameters ); - - } - - copy( source ) { - - super.copy( source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.rotation = source.rotation; - - this.sizeAttenuation = source.sizeAttenuation; - - return this; - - } - - } - - SpriteMaterial.prototype.isSpriteMaterial = true; - - let _geometry; - - const _intersectPoint = /*@__PURE__*/ new Vector3(); - const _worldScale = /*@__PURE__*/ new Vector3(); - const _mvPosition = /*@__PURE__*/ new Vector3(); - - const _alignedPosition = /*@__PURE__*/ new Vector2(); - const _rotatedPosition = /*@__PURE__*/ new Vector2(); - const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); - - const _vA = /*@__PURE__*/ new Vector3(); - const _vB = /*@__PURE__*/ new Vector3(); - const _vC = /*@__PURE__*/ new Vector3(); - - const _uvA = /*@__PURE__*/ new Vector2(); - const _uvB = /*@__PURE__*/ new Vector2(); - const _uvC = /*@__PURE__*/ new Vector2(); - - class Sprite extends Object3D { - - constructor( material ) { - - super(); - - this.type = 'Sprite'; - - if ( _geometry === undefined ) { - - _geometry = new BufferGeometry(); - - const float32Array = new Float32Array( [ - - 0.5, - 0.5, 0, 0, 0, - 0.5, - 0.5, 0, 1, 0, - 0.5, 0.5, 0, 1, 1, - - 0.5, 0.5, 0, 0, 1 - ] ); - - const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); - - _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); - _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); - _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); - - } - - this.geometry = _geometry; - this.material = ( material !== undefined ) ? material : new SpriteMaterial(); - - this.center = new Vector2( 0.5, 0.5 ); - - } - - raycast( raycaster, intersects ) { - - if ( raycaster.camera === null ) { - - console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); - - } - - _worldScale.setFromMatrixScale( this.matrixWorld ); - - _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); - this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); - - _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); - - if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { - - _worldScale.multiplyScalar( - _mvPosition.z ); - - } - - const rotation = this.material.rotation; - let sin, cos; - - if ( rotation !== 0 ) { - - cos = Math.cos( rotation ); - sin = Math.sin( rotation ); - - } - - const center = this.center; - - transformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - - _uvA.set( 0, 0 ); - _uvB.set( 1, 0 ); - _uvC.set( 1, 1 ); - - // check first triangle - let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint ); - - if ( intersect === null ) { - - // check second triangle - transformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - _uvB.set( 0, 1 ); - - intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint ); - if ( intersect === null ) { - - return; - - } - - } - - const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); - - if ( distance < raycaster.near || distance > raycaster.far ) return; - - intersects.push( { - - distance: distance, - point: _intersectPoint.clone(), - uv: Triangle.getUV( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ), - face: null, - object: this - - } ); - - } - - copy( source ) { - - super.copy( source ); - - if ( source.center !== undefined ) this.center.copy( source.center ); - - this.material = source.material; - - return this; - - } - - } - - Sprite.prototype.isSprite = true; - - function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { - - // compute position in camera space - _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); - - // to check if rotation is not zero - if ( sin !== undefined ) { - - _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); - _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); - - } else { - - _rotatedPosition.copy( _alignedPosition ); - - } - - - vertexPosition.copy( mvPosition ); - vertexPosition.x += _rotatedPosition.x; - vertexPosition.y += _rotatedPosition.y; - - // transform to world space - vertexPosition.applyMatrix4( _viewWorldMatrix ); - - } - - const _basePosition = /*@__PURE__*/ new Vector3(); - - const _skinIndex = /*@__PURE__*/ new Vector4(); - const _skinWeight = /*@__PURE__*/ new Vector4(); - - const _vector$5 = /*@__PURE__*/ new Vector3(); - const _matrix = /*@__PURE__*/ new Matrix4(); - - class SkinnedMesh extends Mesh { - - constructor( geometry, material ) { - - super( geometry, material ); - - this.type = 'SkinnedMesh'; - - this.bindMode = 'attached'; - this.bindMatrix = new Matrix4(); - this.bindMatrixInverse = new Matrix4(); - - } - - copy( source ) { - - super.copy( source ); - - this.bindMode = source.bindMode; - this.bindMatrix.copy( source.bindMatrix ); - this.bindMatrixInverse.copy( source.bindMatrixInverse ); - - this.skeleton = source.skeleton; - - return this; - - } - - bind( skeleton, bindMatrix ) { - - this.skeleton = skeleton; - - if ( bindMatrix === undefined ) { - - this.updateMatrixWorld( true ); - - this.skeleton.calculateInverses(); - - bindMatrix = this.matrixWorld; - - } - - this.bindMatrix.copy( bindMatrix ); - this.bindMatrixInverse.copy( bindMatrix ).invert(); - - } - - pose() { - - this.skeleton.pose(); - - } - - normalizeSkinWeights() { - - const vector = new Vector4(); - - const skinWeight = this.geometry.attributes.skinWeight; - - for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { - - vector.x = skinWeight.getX( i ); - vector.y = skinWeight.getY( i ); - vector.z = skinWeight.getZ( i ); - vector.w = skinWeight.getW( i ); - - const scale = 1.0 / vector.manhattanLength(); - - if ( scale !== Infinity ) { - - vector.multiplyScalar( scale ); - - } else { - - vector.set( 1, 0, 0, 0 ); // do something reasonable - - } - - skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); - - } - - } - - updateMatrixWorld( force ) { - - super.updateMatrixWorld( force ); - - if ( this.bindMode === 'attached' ) { - - this.bindMatrixInverse.copy( this.matrixWorld ).invert(); - - } else if ( this.bindMode === 'detached' ) { - - this.bindMatrixInverse.copy( this.bindMatrix ).invert(); - - } else { - - console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); - - } - - } - - boneTransform( index, target ) { - - const skeleton = this.skeleton; - const geometry = this.geometry; - - _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); - _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); - - _basePosition.fromBufferAttribute( geometry.attributes.position, index ).applyMatrix4( this.bindMatrix ); - - target.set( 0, 0, 0 ); - - for ( let i = 0; i < 4; i ++ ) { - - const weight = _skinWeight.getComponent( i ); - - if ( weight !== 0 ) { - - const boneIndex = _skinIndex.getComponent( i ); - - _matrix.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); - - target.addScaledVector( _vector$5.copy( _basePosition ).applyMatrix4( _matrix ), weight ); - - } - - } - - return target.applyMatrix4( this.bindMatrixInverse ); - - } - - } - - SkinnedMesh.prototype.isSkinnedMesh = true; - - class Bone extends Object3D { - - constructor() { - - super(); - - this.type = 'Bone'; - - } - - } - - Bone.prototype.isBone = true; - - class DataTexture extends Texture { - - constructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, encoding ) { - - super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.image = { data: data, width: width, height: height }; - - this.magFilter = magFilter; - this.minFilter = minFilter; - - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; - - this.needsUpdate = true; - - } - - } - - DataTexture.prototype.isDataTexture = true; - - const _offsetMatrix = /*@__PURE__*/ new Matrix4(); - const _identityMatrix = /*@__PURE__*/ new Matrix4(); - - class Skeleton { - - constructor( bones = [], boneInverses = [] ) { - - this.uuid = generateUUID(); - - this.bones = bones.slice( 0 ); - this.boneInverses = boneInverses; - this.boneMatrices = null; - - this.boneTexture = null; - this.boneTextureSize = 0; - - this.frame = - 1; - - this.init(); - - } - - init() { - - const bones = this.bones; - const boneInverses = this.boneInverses; - - this.boneMatrices = new Float32Array( bones.length * 16 ); - - // calculate inverse bone matrices if necessary - - if ( boneInverses.length === 0 ) { - - this.calculateInverses(); - - } else { - - // handle special case - - if ( bones.length !== boneInverses.length ) { - - console.warn( 'THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.' ); - - this.boneInverses = []; - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - this.boneInverses.push( new Matrix4() ); - - } - - } - - } - - } - - calculateInverses() { - - this.boneInverses.length = 0; - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - const inverse = new Matrix4(); - - if ( this.bones[ i ] ) { - - inverse.copy( this.bones[ i ].matrixWorld ).invert(); - - } - - this.boneInverses.push( inverse ); - - } - - } - - pose() { - - // recover the bind-time world matrices - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - const bone = this.bones[ i ]; - - if ( bone ) { - - bone.matrixWorld.copy( this.boneInverses[ i ] ).invert(); - - } - - } - - // compute the local matrices, positions, rotations and scales - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - const bone = this.bones[ i ]; - - if ( bone ) { - - if ( bone.parent && bone.parent.isBone ) { - - bone.matrix.copy( bone.parent.matrixWorld ).invert(); - bone.matrix.multiply( bone.matrixWorld ); - - } else { - - bone.matrix.copy( bone.matrixWorld ); - - } - - bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); - - } - - } - - } - - update() { - - const bones = this.bones; - const boneInverses = this.boneInverses; - const boneMatrices = this.boneMatrices; - const boneTexture = this.boneTexture; - - // flatten bone matrices to array - - for ( let i = 0, il = bones.length; i < il; i ++ ) { - - // compute the offset between the current and the original transform - - const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; - - _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); - _offsetMatrix.toArray( boneMatrices, i * 16 ); - - } - - if ( boneTexture !== null ) { - - boneTexture.needsUpdate = true; - - } - - } - - clone() { - - return new Skeleton( this.bones, this.boneInverses ); - - } - - computeBoneTexture() { - - // layout (1 matrix = 4 pixels) - // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) - // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) - // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) - // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) - // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) - - let size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix - size = ceilPowerOfTwo( size ); - size = Math.max( size, 4 ); - - const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel - boneMatrices.set( this.boneMatrices ); // copy current values - - const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); - - this.boneMatrices = boneMatrices; - this.boneTexture = boneTexture; - this.boneTextureSize = size; - - return this; - - } - - getBoneByName( name ) { - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - const bone = this.bones[ i ]; - - if ( bone.name === name ) { - - return bone; - - } - - } - - return undefined; - - } - - dispose( ) { - - if ( this.boneTexture !== null ) { - - this.boneTexture.dispose(); - - this.boneTexture = null; - - } - - } - - fromJSON( json, bones ) { - - this.uuid = json.uuid; - - for ( let i = 0, l = json.bones.length; i < l; i ++ ) { - - const uuid = json.bones[ i ]; - let bone = bones[ uuid ]; - - if ( bone === undefined ) { - - console.warn( 'THREE.Skeleton: No bone found with UUID:', uuid ); - bone = new Bone(); - - } - - this.bones.push( bone ); - this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) ); - - } - - this.init(); - - return this; - - } - - toJSON() { - - const data = { - metadata: { - version: 4.5, - type: 'Skeleton', - generator: 'Skeleton.toJSON' - }, - bones: [], - boneInverses: [] - }; - - data.uuid = this.uuid; - - const bones = this.bones; - const boneInverses = this.boneInverses; - - for ( let i = 0, l = bones.length; i < l; i ++ ) { - - const bone = bones[ i ]; - data.bones.push( bone.uuid ); - - const boneInverse = boneInverses[ i ]; - data.boneInverses.push( boneInverse.toArray() ); - - } - - return data; - - } - - } - - class InstancedBufferAttribute extends BufferAttribute { - - constructor( array, itemSize, normalized, meshPerAttribute = 1 ) { - - if ( typeof normalized === 'number' ) { - - meshPerAttribute = normalized; - - normalized = false; - - console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' ); - - } - - super( array, itemSize, normalized ); - - this.meshPerAttribute = meshPerAttribute; - - } - - copy( source ) { - - super.copy( source ); - - this.meshPerAttribute = source.meshPerAttribute; - - return this; - - } - - toJSON() { - - const data = super.toJSON(); - - data.meshPerAttribute = this.meshPerAttribute; - - data.isInstancedBufferAttribute = true; - - return data; - - } - - } - - InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; - - const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); - const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); - - const _instanceIntersects = []; - - const _mesh = /*@__PURE__*/ new Mesh(); - - class InstancedMesh extends Mesh { - - constructor( geometry, material, count ) { - - super( geometry, material ); - - this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 ); - this.instanceColor = null; - - this.count = count; - - this.frustumCulled = false; - - } - - copy( source ) { - - super.copy( source ); - - this.instanceMatrix.copy( source.instanceMatrix ); - - if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone(); - - this.count = source.count; - - return this; - - } - - getColorAt( index, color ) { - - color.fromArray( this.instanceColor.array, index * 3 ); - - } - - getMatrixAt( index, matrix ) { - - matrix.fromArray( this.instanceMatrix.array, index * 16 ); - - } - - raycast( raycaster, intersects ) { - - const matrixWorld = this.matrixWorld; - const raycastTimes = this.count; - - _mesh.geometry = this.geometry; - _mesh.material = this.material; - - if ( _mesh.material === undefined ) return; - - for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { - - // calculate the world matrix for each instance - - this.getMatrixAt( instanceId, _instanceLocalMatrix ); - - _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); - - // the mesh represents this single instance - - _mesh.matrixWorld = _instanceWorldMatrix; - - _mesh.raycast( raycaster, _instanceIntersects ); - - // process the result of raycast - - for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { - - const intersect = _instanceIntersects[ i ]; - intersect.instanceId = instanceId; - intersect.object = this; - intersects.push( intersect ); - - } - - _instanceIntersects.length = 0; - - } - - } - - setColorAt( index, color ) { - - if ( this.instanceColor === null ) { - - this.instanceColor = new InstancedBufferAttribute( new Float32Array( this.instanceMatrix.count * 3 ), 3 ); - - } - - color.toArray( this.instanceColor.array, index * 3 ); - - } - - setMatrixAt( index, matrix ) { - - matrix.toArray( this.instanceMatrix.array, index * 16 ); - - } - - updateMorphTargets() { - - } - - dispose() { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } - - InstancedMesh.prototype.isInstancedMesh = true; - - /** - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * linecap: "round", - * linejoin: "round" - * } - */ - - class LineBasicMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.type = 'LineBasicMaterial'; - - this.color = new Color( 0xffffff ); - - this.linewidth = 1; - this.linecap = 'round'; - this.linejoin = 'round'; - - this.setValues( parameters ); - - } - - - copy( source ) { - - super.copy( source ); - - this.color.copy( source.color ); - - this.linewidth = source.linewidth; - this.linecap = source.linecap; - this.linejoin = source.linejoin; - - return this; - - } - - } - - LineBasicMaterial.prototype.isLineBasicMaterial = true; - - const _start$1 = /*@__PURE__*/ new Vector3(); - const _end$1 = /*@__PURE__*/ new Vector3(); - const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); - const _ray$1 = /*@__PURE__*/ new Ray(); - const _sphere$1 = /*@__PURE__*/ new Sphere(); - - class Line extends Object3D { - - constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { - - super(); - - this.type = 'Line'; - - this.geometry = geometry; - this.material = material; - - this.updateMorphTargets(); - - } - - copy( source ) { - - super.copy( source ); - - this.material = source.material; - this.geometry = source.geometry; - - return this; - - } - - computeLineDistances() { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - // we assume non-indexed geometry - - if ( geometry.index === null ) { - - const positionAttribute = geometry.attributes.position; - const lineDistances = [ 0 ]; - - for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { - - _start$1.fromBufferAttribute( positionAttribute, i - 1 ); - _end$1.fromBufferAttribute( positionAttribute, i ); - - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += _start$1.distanceTo( _end$1 ); - - } - - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - - } else { - - console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - - } - - } else if ( geometry.isGeometry ) { - - console.error( 'THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - return this; - - } - - raycast( raycaster, intersects ) { - - const geometry = this.geometry; - const matrixWorld = this.matrixWorld; - const threshold = raycaster.params.Line.threshold; - const drawRange = geometry.drawRange; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _sphere$1.copy( geometry.boundingSphere ); - _sphere$1.applyMatrix4( matrixWorld ); - _sphere$1.radius += threshold; - - if ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return; - - // - - _inverseMatrix$1.copy( matrixWorld ).invert(); - _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); - - const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - const localThresholdSq = localThreshold * localThreshold; - - const vStart = new Vector3(); - const vEnd = new Vector3(); - const interSegment = new Vector3(); - const interRay = new Vector3(); - const step = this.isLineSegments ? 2 : 1; - - if ( geometry.isBufferGeometry ) { - - const index = geometry.index; - const attributes = geometry.attributes; - const positionAttribute = attributes.position; - - if ( index !== null ) { - - const start = Math.max( 0, drawRange.start ); - const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - - for ( let i = start, l = end - 1; i < l; i += step ) { - - const a = index.getX( i ); - const b = index.getX( i + 1 ); - - vStart.fromBufferAttribute( positionAttribute, a ); - vEnd.fromBufferAttribute( positionAttribute, b ); - - const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - - if ( distSq > localThresholdSq ) continue; - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - const distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } else { - - const start = Math.max( 0, drawRange.start ); - const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); - - for ( let i = start, l = end - 1; i < l; i += step ) { - - vStart.fromBufferAttribute( positionAttribute, i ); - vEnd.fromBufferAttribute( positionAttribute, i + 1 ); - - const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - - if ( distSq > localThresholdSq ) continue; - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - const distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } - - } else if ( geometry.isGeometry ) { - - console.error( 'THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - updateMorphTargets() { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); - - if ( keys.length > 0 ) { - - const morphAttribute = morphAttributes[ keys[ 0 ] ]; - - if ( morphAttribute !== undefined ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - - const name = morphAttribute[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } - - } - - } - - } else { - - const morphTargets = geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - console.error( 'THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - } - - } - - Line.prototype.isLine = true; - - const _start$2 = /*@__PURE__*/ new Vector3(); - const _end$2 = /*@__PURE__*/ new Vector3(); - - class LineSegments extends Line { - - constructor( geometry, material ) { - - super( geometry, material ); - - this.type = 'LineSegments'; - - } - - computeLineDistances() { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - // we assume non-indexed geometry - - if ( geometry.index === null ) { - - const positionAttribute = geometry.attributes.position; - const lineDistances = []; - - for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { - - _start$2.fromBufferAttribute( positionAttribute, i ); - _end$2.fromBufferAttribute( positionAttribute, i + 1 ); - - lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; - lineDistances[ i + 1 ] = lineDistances[ i ] + _start$2.distanceTo( _end$2 ); - - } - - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - - } else { - - console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - - } - - } else if ( geometry.isGeometry ) { - - console.error( 'THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - return this; - - } - - } - - LineSegments.prototype.isLineSegments = true; - - class LineLoop extends Line { - - constructor( geometry, material ) { - - super( geometry, material ); - - this.type = 'LineLoop'; - - } - - } - - LineLoop.prototype.isLineLoop = true; - - /** - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * - * size: , - * sizeAttenuation: - * - * } - */ - - class PointsMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.type = 'PointsMaterial'; - - this.color = new Color( 0xffffff ); - - this.map = null; - - this.alphaMap = null; - - this.size = 1; - this.sizeAttenuation = true; - - this.setValues( parameters ); - - } - - copy( source ) { - - super.copy( source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.size = source.size; - this.sizeAttenuation = source.sizeAttenuation; - - return this; - - } - - } - - PointsMaterial.prototype.isPointsMaterial = true; - - const _inverseMatrix = /*@__PURE__*/ new Matrix4(); - const _ray = /*@__PURE__*/ new Ray(); - const _sphere$4 = /*@__PURE__*/ new Sphere(); - const _position$2 = /*@__PURE__*/ new Vector3(); - - class Points extends Object3D { - - constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) { - - super(); - - this.type = 'Points'; - - this.geometry = geometry; - this.material = material; - - this.updateMorphTargets(); - - } - - copy( source ) { - - super.copy( source ); - - this.material = source.material; - this.geometry = source.geometry; - - return this; - - } - - raycast( raycaster, intersects ) { - - const geometry = this.geometry; - const matrixWorld = this.matrixWorld; - const threshold = raycaster.params.Points.threshold; - const drawRange = geometry.drawRange; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _sphere$4.copy( geometry.boundingSphere ); - _sphere$4.applyMatrix4( matrixWorld ); - _sphere$4.radius += threshold; - - if ( raycaster.ray.intersectsSphere( _sphere$4 ) === false ) return; - - // - - _inverseMatrix.copy( matrixWorld ).invert(); - _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); - - const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - const localThresholdSq = localThreshold * localThreshold; - - if ( geometry.isBufferGeometry ) { - - const index = geometry.index; - const attributes = geometry.attributes; - const positionAttribute = attributes.position; - - if ( index !== null ) { - - const start = Math.max( 0, drawRange.start ); - const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - - for ( let i = start, il = end; i < il; i ++ ) { - - const a = index.getX( i ); - - _position$2.fromBufferAttribute( positionAttribute, a ); - - testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); - - } - - } else { - - const start = Math.max( 0, drawRange.start ); - const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); - - for ( let i = start, l = end; i < l; i ++ ) { - - _position$2.fromBufferAttribute( positionAttribute, i ); - - testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); - - } - - } - - } else { - - console.error( 'THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - updateMorphTargets() { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); - - if ( keys.length > 0 ) { - - const morphAttribute = morphAttributes[ keys[ 0 ] ]; - - if ( morphAttribute !== undefined ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - - const name = morphAttribute[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } - - } - - } - - } else { - - const morphTargets = geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - console.error( 'THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - } - - } - - Points.prototype.isPoints = true; - - function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { - - const rayPointDistanceSq = _ray.distanceSqToPoint( point ); - - if ( rayPointDistanceSq < localThresholdSq ) { - - const intersectPoint = new Vector3(); - - _ray.closestPointToPoint( point, intersectPoint ); - intersectPoint.applyMatrix4( matrixWorld ); - - const distance = raycaster.ray.origin.distanceTo( intersectPoint ); - - if ( distance < raycaster.near || distance > raycaster.far ) return; - - intersects.push( { - - distance: distance, - distanceToRay: Math.sqrt( rayPointDistanceSq ), - point: intersectPoint, - index: index, - face: null, - object: object - - } ); - - } - - } - - class VideoTexture extends Texture { - - constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - - super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.format = format !== undefined ? format : RGBFormat; - - this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - - this.generateMipmaps = false; - - const scope = this; - - function updateVideo() { - - scope.needsUpdate = true; - video.requestVideoFrameCallback( updateVideo ); - - } - - if ( 'requestVideoFrameCallback' in video ) { - - video.requestVideoFrameCallback( updateVideo ); - - } - - } - - clone() { - - return new this.constructor( this.image ).copy( this ); - - } - - update() { - - const video = this.image; - const hasVideoFrameCallback = 'requestVideoFrameCallback' in video; - - if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { - - this.needsUpdate = true; - - } - - } - - } - - VideoTexture.prototype.isVideoTexture = true; - - class CompressedTexture extends Texture { - - constructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - - super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.image = { width: width, height: height }; - this.mipmaps = mipmaps; - - // no flipping for cube textures - // (also flipping doesn't work for compressed textures ) - - this.flipY = false; - - // can't generate mipmaps for compressed textures - // mips must be embedded in DDS files - - this.generateMipmaps = false; - - } - - } - - CompressedTexture.prototype.isCompressedTexture = true; - - class CanvasTexture extends Texture { - - constructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - - super( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.needsUpdate = true; - - } - - } - - CanvasTexture.prototype.isCanvasTexture = true; - - class DepthTexture extends Texture { - - constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { - - format = format !== undefined ? format : DepthFormat; - - if ( format !== DepthFormat && format !== DepthStencilFormat ) { - - throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); - - } - - if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; - if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; - - super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.image = { width: width, height: height }; - - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - - this.flipY = false; - this.generateMipmaps = false; - - } - - - } - - DepthTexture.prototype.isDepthTexture = true; - - const _v0 = new Vector3(); - const _v1$1 = new Vector3(); - const _normal = new Vector3(); - const _triangle = new Triangle(); - - class EdgesGeometry extends BufferGeometry { - - constructor( geometry, thresholdAngle ) { - - super(); - - this.type = 'EdgesGeometry'; - - this.parameters = { - thresholdAngle: thresholdAngle - }; - - thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; - - if ( geometry.isGeometry === true ) { - - console.error( 'THREE.EdgesGeometry no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - return; - - } - - const precisionPoints = 4; - const precision = Math.pow( 10, precisionPoints ); - const thresholdDot = Math.cos( DEG2RAD * thresholdAngle ); - - const indexAttr = geometry.getIndex(); - const positionAttr = geometry.getAttribute( 'position' ); - const indexCount = indexAttr ? indexAttr.count : positionAttr.count; - - const indexArr = [ 0, 0, 0 ]; - const vertKeys = [ 'a', 'b', 'c' ]; - const hashes = new Array( 3 ); - - const edgeData = {}; - const vertices = []; - for ( let i = 0; i < indexCount; i += 3 ) { - - if ( indexAttr ) { - - indexArr[ 0 ] = indexAttr.getX( i ); - indexArr[ 1 ] = indexAttr.getX( i + 1 ); - indexArr[ 2 ] = indexAttr.getX( i + 2 ); - - } else { - - indexArr[ 0 ] = i; - indexArr[ 1 ] = i + 1; - indexArr[ 2 ] = i + 2; - - } - - const { a, b, c } = _triangle; - a.fromBufferAttribute( positionAttr, indexArr[ 0 ] ); - b.fromBufferAttribute( positionAttr, indexArr[ 1 ] ); - c.fromBufferAttribute( positionAttr, indexArr[ 2 ] ); - _triangle.getNormal( _normal ); - - // create hashes for the edge from the vertices - hashes[ 0 ] = `${ Math.round( a.x * precision ) },${ Math.round( a.y * precision ) },${ Math.round( a.z * precision ) }`; - hashes[ 1 ] = `${ Math.round( b.x * precision ) },${ Math.round( b.y * precision ) },${ Math.round( b.z * precision ) }`; - hashes[ 2 ] = `${ Math.round( c.x * precision ) },${ Math.round( c.y * precision ) },${ Math.round( c.z * precision ) }`; - - // skip degenerate triangles - if ( hashes[ 0 ] === hashes[ 1 ] || hashes[ 1 ] === hashes[ 2 ] || hashes[ 2 ] === hashes[ 0 ] ) { - - continue; - - } - - // iterate over every edge - for ( let j = 0; j < 3; j ++ ) { - - // get the first and next vertex making up the edge - const jNext = ( j + 1 ) % 3; - const vecHash0 = hashes[ j ]; - const vecHash1 = hashes[ jNext ]; - const v0 = _triangle[ vertKeys[ j ] ]; - const v1 = _triangle[ vertKeys[ jNext ] ]; - - const hash = `${ vecHash0 }_${ vecHash1 }`; - const reverseHash = `${ vecHash1 }_${ vecHash0 }`; - - if ( reverseHash in edgeData && edgeData[ reverseHash ] ) { - - // if we found a sibling edge add it into the vertex array if - // it meets the angle threshold and delete the edge from the map. - if ( _normal.dot( edgeData[ reverseHash ].normal ) <= thresholdDot ) { - - vertices.push( v0.x, v0.y, v0.z ); - vertices.push( v1.x, v1.y, v1.z ); - - } - - edgeData[ reverseHash ] = null; - - } else if ( ! ( hash in edgeData ) ) { - - // if we've already got an edge here then skip adding a new one - edgeData[ hash ] = { - - index0: indexArr[ j ], - index1: indexArr[ jNext ], - normal: _normal.clone(), - - }; - - } - - } - - } - - // iterate over all remaining, unmatched edges and add them to the vertex array - for ( const key in edgeData ) { - - if ( edgeData[ key ] ) { - - const { index0, index1 } = edgeData[ key ]; - _v0.fromBufferAttribute( positionAttr, index0 ); - _v1$1.fromBufferAttribute( positionAttr, index1 ); - - vertices.push( _v0.x, _v0.y, _v0.z ); - vertices.push( _v1$1.x, _v1$1.y, _v1$1.z ); - - } - - } - - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - - } - - } - - /** - * Extensible curve object. - * - * Some common of curve methods: - * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) - * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) - * .getPoints(), .getSpacedPoints() - * .getLength() - * .updateArcLengths() - * - * This following curves inherit from THREE.Curve: - * - * -- 2D curves -- - * THREE.ArcCurve - * THREE.CubicBezierCurve - * THREE.EllipseCurve - * THREE.LineCurve - * THREE.QuadraticBezierCurve - * THREE.SplineCurve - * - * -- 3D curves -- - * THREE.CatmullRomCurve3 - * THREE.CubicBezierCurve3 - * THREE.LineCurve3 - * THREE.QuadraticBezierCurve3 - * - * A series of curves can be represented as a THREE.CurvePath. - * - **/ - - class Curve { - - constructor() { - - this.type = 'Curve'; - - this.arcLengthDivisions = 200; - - } - - // Virtual base class method to overwrite and implement in subclasses - // - t [0 .. 1] - - getPoint( /* t, optionalTarget */ ) { - - console.warn( 'THREE.Curve: .getPoint() not implemented.' ); - return null; - - } - - // Get point at relative position in curve according to arc length - // - u [0 .. 1] - - getPointAt( u, optionalTarget ) { - - const t = this.getUtoTmapping( u ); - return this.getPoint( t, optionalTarget ); - - } - - // Get sequence of points using getPoint( t ) - - getPoints( divisions = 5 ) { - - const points = []; - - for ( let d = 0; d <= divisions; d ++ ) { - - points.push( this.getPoint( d / divisions ) ); - - } - - return points; - - } - - // Get sequence of points using getPointAt( u ) - - getSpacedPoints( divisions = 5 ) { - - const points = []; - - for ( let d = 0; d <= divisions; d ++ ) { - - points.push( this.getPointAt( d / divisions ) ); - - } - - return points; - - } - - // Get total curve arc length - - getLength() { - - const lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; - - } - - // Get list of cumulative segment lengths - - getLengths( divisions = this.arcLengthDivisions ) { - - if ( this.cacheArcLengths && - ( this.cacheArcLengths.length === divisions + 1 ) && - ! this.needsUpdate ) { - - return this.cacheArcLengths; - - } - - this.needsUpdate = false; - - const cache = []; - let current, last = this.getPoint( 0 ); - let sum = 0; - - cache.push( 0 ); - - for ( let p = 1; p <= divisions; p ++ ) { - - current = this.getPoint( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; - - } - - this.cacheArcLengths = cache; - - return cache; // { sums: cache, sum: sum }; Sum is in the last element. - - } - - updateArcLengths() { - - this.needsUpdate = true; - this.getLengths(); - - } - - // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - - getUtoTmapping( u, distance ) { - - const arcLengths = this.getLengths(); - - let i = 0; - const il = arcLengths.length; - - let targetArcLength; // The targeted u distance value to get - - if ( distance ) { - - targetArcLength = distance; - - } else { - - targetArcLength = u * arcLengths[ il - 1 ]; - - } - - // binary search for the index with largest value smaller than target u distance - - let low = 0, high = il - 1, comparison; - - while ( low <= high ) { - - i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats - - comparison = arcLengths[ i ] - targetArcLength; - - if ( comparison < 0 ) { - - low = i + 1; - - } else if ( comparison > 0 ) { - - high = i - 1; - - } else { - - high = i; - break; - - // DONE - - } - - } - - i = high; - - if ( arcLengths[ i ] === targetArcLength ) { - - return i / ( il - 1 ); - - } - - // we could get finer grain at lengths, or use simple interpolation between two points - - const lengthBefore = arcLengths[ i ]; - const lengthAfter = arcLengths[ i + 1 ]; - - const segmentLength = lengthAfter - lengthBefore; - - // determine where we are between the 'before' and 'after' points - - const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; - - // add that fractional amount to t - - const t = ( i + segmentFraction ) / ( il - 1 ); - - return t; - - } - - // Returns a unit vector tangent at t - // In case any sub curve does not implement its tangent derivation, - // 2 points a small delta apart will be used to find its gradient - // which seems to give a reasonable approximation - - getTangent( t, optionalTarget ) { - - const delta = 0.0001; - let t1 = t - delta; - let t2 = t + delta; - - // Capping in case of danger - - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; - - const pt1 = this.getPoint( t1 ); - const pt2 = this.getPoint( t2 ); - - const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); - - tangent.copy( pt2 ).sub( pt1 ).normalize(); - - return tangent; - - } - - getTangentAt( u, optionalTarget ) { - - const t = this.getUtoTmapping( u ); - return this.getTangent( t, optionalTarget ); - - } - - computeFrenetFrames( segments, closed ) { - - // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf - - const normal = new Vector3(); - - const tangents = []; - const normals = []; - const binormals = []; - - const vec = new Vector3(); - const mat = new Matrix4(); - - // compute the tangent vectors for each segment on the curve - - for ( let i = 0; i <= segments; i ++ ) { - - const u = i / segments; - - tangents[ i ] = this.getTangentAt( u, new Vector3() ); - tangents[ i ].normalize(); - - } - - // select an initial normal vector perpendicular to the first tangent vector, - // and in the direction of the minimum tangent xyz component - - normals[ 0 ] = new Vector3(); - binormals[ 0 ] = new Vector3(); - let min = Number.MAX_VALUE; - const tx = Math.abs( tangents[ 0 ].x ); - const ty = Math.abs( tangents[ 0 ].y ); - const tz = Math.abs( tangents[ 0 ].z ); - - if ( tx <= min ) { - - min = tx; - normal.set( 1, 0, 0 ); - - } - - if ( ty <= min ) { - - min = ty; - normal.set( 0, 1, 0 ); - - } - - if ( tz <= min ) { - - normal.set( 0, 0, 1 ); - - } - - vec.crossVectors( tangents[ 0 ], normal ).normalize(); - - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - - - // compute the slowly-varying normal and binormal vectors for each segment on the curve - - for ( let i = 1; i <= segments; i ++ ) { - - normals[ i ] = normals[ i - 1 ].clone(); - - binormals[ i ] = binormals[ i - 1 ].clone(); - - vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - - if ( vec.length() > Number.EPSILON ) { - - vec.normalize(); - - const theta = Math.acos( clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); - - } - - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - - } - - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - - if ( closed === true ) { - - let theta = Math.acos( clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); - theta /= segments; - - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { - - theta = - theta; - - } - - for ( let i = 1; i <= segments; i ++ ) { - - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - - } - - } - - return { - tangents: tangents, - normals: normals, - binormals: binormals - }; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( source ) { - - this.arcLengthDivisions = source.arcLengthDivisions; - - return this; - - } - - toJSON() { - - const data = { - metadata: { - version: 4.5, - type: 'Curve', - generator: 'Curve.toJSON' - } - }; - - data.arcLengthDivisions = this.arcLengthDivisions; - data.type = this.type; - - return data; - - } - - fromJSON( json ) { - - this.arcLengthDivisions = json.arcLengthDivisions; - - return this; - - } - - } - - class EllipseCurve extends Curve { - - constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0 ) { - - super(); - - this.type = 'EllipseCurve'; - - this.aX = aX; - this.aY = aY; - - this.xRadius = xRadius; - this.yRadius = yRadius; - - this.aStartAngle = aStartAngle; - this.aEndAngle = aEndAngle; - - this.aClockwise = aClockwise; - - this.aRotation = aRotation; - - } - - getPoint( t, optionalTarget ) { - - const point = optionalTarget || new Vector2(); - - const twoPi = Math.PI * 2; - let deltaAngle = this.aEndAngle - this.aStartAngle; - const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; - - // ensures that deltaAngle is 0 .. 2 PI - while ( deltaAngle < 0 ) deltaAngle += twoPi; - while ( deltaAngle > twoPi ) deltaAngle -= twoPi; - - if ( deltaAngle < Number.EPSILON ) { - - if ( samePoints ) { - - deltaAngle = 0; - - } else { - - deltaAngle = twoPi; - - } - - } - - if ( this.aClockwise === true && ! samePoints ) { - - if ( deltaAngle === twoPi ) { - - deltaAngle = - twoPi; - - } else { - - deltaAngle = deltaAngle - twoPi; - - } - - } - - const angle = this.aStartAngle + t * deltaAngle; - let x = this.aX + this.xRadius * Math.cos( angle ); - let y = this.aY + this.yRadius * Math.sin( angle ); - - if ( this.aRotation !== 0 ) { - - const cos = Math.cos( this.aRotation ); - const sin = Math.sin( this.aRotation ); - - const tx = x - this.aX; - const ty = y - this.aY; - - // Rotate the point about the center of the ellipse. - x = tx * cos - ty * sin + this.aX; - y = tx * sin + ty * cos + this.aY; - - } - - return point.set( x, y ); - - } - - copy( source ) { - - super.copy( source ); - - this.aX = source.aX; - this.aY = source.aY; - - this.xRadius = source.xRadius; - this.yRadius = source.yRadius; - - this.aStartAngle = source.aStartAngle; - this.aEndAngle = source.aEndAngle; - - this.aClockwise = source.aClockwise; - - this.aRotation = source.aRotation; - - return this; - - } - - toJSON() { - - const data = super.toJSON(); - - data.aX = this.aX; - data.aY = this.aY; - - data.xRadius = this.xRadius; - data.yRadius = this.yRadius; - - data.aStartAngle = this.aStartAngle; - data.aEndAngle = this.aEndAngle; - - data.aClockwise = this.aClockwise; - - data.aRotation = this.aRotation; - - return data; - - } - - fromJSON( json ) { - - super.fromJSON( json ); - - this.aX = json.aX; - this.aY = json.aY; - - this.xRadius = json.xRadius; - this.yRadius = json.yRadius; - - this.aStartAngle = json.aStartAngle; - this.aEndAngle = json.aEndAngle; - - this.aClockwise = json.aClockwise; - - this.aRotation = json.aRotation; - - return this; - - } - - } - - EllipseCurve.prototype.isEllipseCurve = true; - - class ArcCurve extends EllipseCurve { - - constructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - super( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - - this.type = 'ArcCurve'; - - } - - } - - ArcCurve.prototype.isArcCurve = true; - - /** - * Centripetal CatmullRom Curve - which is useful for avoiding - * cusps and self-intersections in non-uniform catmull rom curves. - * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf - * - * curve.type accepts centripetal(default), chordal and catmullrom - * curve.tension is used for catmullrom which defaults to 0.5 - */ - - - /* - Based on an optimized c++ solution in - - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - - http://ideone.com/NoEbVM - - This CubicPoly class could be used for reusing some variables and calculations, - but for three.js curve use, it could be possible inlined and flatten into a single function call - which can be placed in CurveUtils. - */ - - function CubicPoly() { - - let c0 = 0, c1 = 0, c2 = 0, c3 = 0; - - /* - * Compute coefficients for a cubic polynomial - * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 - * such that - * p(0) = x0, p(1) = x1 - * and - * p'(0) = t0, p'(1) = t1. - */ - function init( x0, x1, t0, t1 ) { - - c0 = x0; - c1 = t0; - c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; - c3 = 2 * x0 - 2 * x1 + t0 + t1; - - } - - return { - - initCatmullRom: function ( x0, x1, x2, x3, tension ) { - - init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); - - }, - - initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { - - // compute tangents when parameterized in [t1,t2] - let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; - let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; - - // rescale tangents for parametrization in [0,1] - t1 *= dt1; - t2 *= dt1; - - init( x1, x2, t1, t2 ); - - }, - - calc: function ( t ) { - - const t2 = t * t; - const t3 = t2 * t; - return c0 + c1 * t + c2 * t2 + c3 * t3; - - } - - }; - - } - - // - - const tmp = new Vector3(); - const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); - - class CatmullRomCurve3 extends Curve { - - constructor( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) { - - super(); - - this.type = 'CatmullRomCurve3'; - - this.points = points; - this.closed = closed; - this.curveType = curveType; - this.tension = tension; - - } - - getPoint( t, optionalTarget = new Vector3() ) { - - const point = optionalTarget; - - const points = this.points; - const l = points.length; - - const p = ( l - ( this.closed ? 0 : 1 ) ) * t; - let intPoint = Math.floor( p ); - let weight = p - intPoint; - - if ( this.closed ) { - - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; - - } else if ( weight === 0 && intPoint === l - 1 ) { - - intPoint = l - 2; - weight = 1; - - } - - let p0, p3; // 4 points (p1 & p2 defined below) - - if ( this.closed || intPoint > 0 ) { - - p0 = points[ ( intPoint - 1 ) % l ]; - - } else { - - // extrapolate first point - tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); - p0 = tmp; - - } - - const p1 = points[ intPoint % l ]; - const p2 = points[ ( intPoint + 1 ) % l ]; - - if ( this.closed || intPoint + 2 < l ) { - - p3 = points[ ( intPoint + 2 ) % l ]; - - } else { - - // extrapolate last point - tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); - p3 = tmp; - - } - - if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { - - // init Centripetal / Chordal Catmull-Rom - const pow = this.curveType === 'chordal' ? 0.5 : 0.25; - let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); - let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); - let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); - - // safety check for repeated points - if ( dt1 < 1e-4 ) dt1 = 1.0; - if ( dt0 < 1e-4 ) dt0 = dt1; - if ( dt2 < 1e-4 ) dt2 = dt1; - - px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); - py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); - pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); - - } else if ( this.curveType === 'catmullrom' ) { - - px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); - py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); - pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); - - } - - point.set( - px.calc( weight ), - py.calc( weight ), - pz.calc( weight ) - ); - - return point; - - } - - copy( source ) { - - super.copy( source ); - - this.points = []; - - for ( let i = 0, l = source.points.length; i < l; i ++ ) { - - const point = source.points[ i ]; - - this.points.push( point.clone() ); - - } - - this.closed = source.closed; - this.curveType = source.curveType; - this.tension = source.tension; - - return this; - - } - - toJSON() { - - const data = super.toJSON(); - - data.points = []; - - for ( let i = 0, l = this.points.length; i < l; i ++ ) { - - const point = this.points[ i ]; - data.points.push( point.toArray() ); - - } - - data.closed = this.closed; - data.curveType = this.curveType; - data.tension = this.tension; - - return data; - - } - - fromJSON( json ) { - - super.fromJSON( json ); - - this.points = []; - - for ( let i = 0, l = json.points.length; i < l; i ++ ) { - - const point = json.points[ i ]; - this.points.push( new Vector3().fromArray( point ) ); - - } - - this.closed = json.closed; - this.curveType = json.curveType; - this.tension = json.tension; - - return this; - - } - - } - - CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; - - /** - * Bezier Curves formulas obtained from - * http://en.wikipedia.org/wiki/Bézier_curve - */ - - function CatmullRom( t, p0, p1, p2, p3 ) { - - const v0 = ( p2 - p0 ) * 0.5; - const v1 = ( p3 - p1 ) * 0.5; - const t2 = t * t; - const t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; - - } - - // - - function QuadraticBezierP0( t, p ) { - - const k = 1 - t; - return k * k * p; - - } - - function QuadraticBezierP1( t, p ) { - - return 2 * ( 1 - t ) * t * p; - - } - - function QuadraticBezierP2( t, p ) { - - return t * t * p; - - } - - function QuadraticBezier( t, p0, p1, p2 ) { - - return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + - QuadraticBezierP2( t, p2 ); - - } - - // - - function CubicBezierP0( t, p ) { - - const k = 1 - t; - return k * k * k * p; - - } - - function CubicBezierP1( t, p ) { - - const k = 1 - t; - return 3 * k * k * t * p; - - } - - function CubicBezierP2( t, p ) { - - return 3 * ( 1 - t ) * t * t * p; - - } - - function CubicBezierP3( t, p ) { - - return t * t * t * p; - - } - - function CubicBezier( t, p0, p1, p2, p3 ) { - - return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + - CubicBezierP3( t, p3 ); - - } - - class CubicBezierCurve extends Curve { - - constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) { - - super(); - - this.type = 'CubicBezierCurve'; - - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; - - } - - getPoint( t, optionalTarget = new Vector2() ) { - - const point = optionalTarget; - - const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) - ); - - return point; - - } - - copy( source ) { - - super.copy( source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); - - return this; - - } - - toJSON() { - - const data = super.toJSON(); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); - - return data; - - } - - fromJSON( json ) { - - super.fromJSON( json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); - - return this; - - } - - } - - CubicBezierCurve.prototype.isCubicBezierCurve = true; - - class CubicBezierCurve3 extends Curve { - - constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) { - - super(); - - this.type = 'CubicBezierCurve3'; - - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; - - } - - getPoint( t, optionalTarget = new Vector3() ) { - - const point = optionalTarget; - - const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), - CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) - ); - - return point; - - } - - copy( source ) { - - super.copy( source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); - - return this; - - } - - toJSON() { - - const data = super.toJSON(); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); - - return data; - - } - - fromJSON( json ) { - - super.fromJSON( json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); - - return this; - - } - - } - - CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; - - class LineCurve extends Curve { - - constructor( v1 = new Vector2(), v2 = new Vector2() ) { - - super(); - - this.type = 'LineCurve'; - - this.v1 = v1; - this.v2 = v2; - - } - - getPoint( t, optionalTarget = new Vector2() ) { - - const point = optionalTarget; - - if ( t === 1 ) { - - point.copy( this.v2 ); - - } else { - - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); - - } - - return point; - - } - - // Line curve is linear, so we can overwrite default getPointAt - getPointAt( u, optionalTarget ) { - - return this.getPoint( u, optionalTarget ); - - } - - getTangent( t, optionalTarget ) { - - const tangent = optionalTarget || new Vector2(); - - tangent.copy( this.v2 ).sub( this.v1 ).normalize(); - - return tangent; - - } - - copy( source ) { - - super.copy( source ); - - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - } - - toJSON() { - - const data = super.toJSON(); - - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - } - - fromJSON( json ) { - - super.fromJSON( json ); - - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - } - - } - - LineCurve.prototype.isLineCurve = true; - - class LineCurve3 extends Curve { - - constructor( v1 = new Vector3(), v2 = new Vector3() ) { - - super(); - - this.type = 'LineCurve3'; - this.isLineCurve3 = true; - - this.v1 = v1; - this.v2 = v2; - - } - getPoint( t, optionalTarget = new Vector3() ) { - - const point = optionalTarget; - - if ( t === 1 ) { - - point.copy( this.v2 ); - - } else { - - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); - - } - - return point; - - } - // Line curve is linear, so we can overwrite default getPointAt - getPointAt( u, optionalTarget ) { - - return this.getPoint( u, optionalTarget ); - - } - copy( source ) { - - super.copy( source ); - - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - } - toJSON() { - - const data = super.toJSON(); - - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - } - fromJSON( json ) { - - super.fromJSON( json ); - - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - } - - } - - class QuadraticBezierCurve extends Curve { - - constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) { - - super(); - - this.type = 'QuadraticBezierCurve'; - - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - - } - - getPoint( t, optionalTarget = new Vector2() ) { - - const point = optionalTarget; - - const v0 = this.v0, v1 = this.v1, v2 = this.v2; - - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ) - ); - - return point; - - } - - copy( source ) { - - super.copy( source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - } - - toJSON() { - - const data = super.toJSON(); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - } - - fromJSON( json ) { - - super.fromJSON( json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - } - - } - - QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; - - class QuadraticBezierCurve3 extends Curve { - - constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) { - - super(); - - this.type = 'QuadraticBezierCurve3'; - - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - - } - - getPoint( t, optionalTarget = new Vector3() ) { - - const point = optionalTarget; - - const v0 = this.v0, v1 = this.v1, v2 = this.v2; - - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ), - QuadraticBezier( t, v0.z, v1.z, v2.z ) - ); - - return point; - - } - - copy( source ) { - - super.copy( source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - } - - toJSON() { - - const data = super.toJSON(); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - } - - fromJSON( json ) { - - super.fromJSON( json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - } - - } - - QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; - - class SplineCurve extends Curve { - - constructor( points = [] ) { - - super(); - - this.type = 'SplineCurve'; - - this.points = points; - - } - - getPoint( t, optionalTarget = new Vector2() ) { - - const point = optionalTarget; - - const points = this.points; - const p = ( points.length - 1 ) * t; - - const intPoint = Math.floor( p ); - const weight = p - intPoint; - - const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; - const p1 = points[ intPoint ]; - const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; - const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - - point.set( - CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), - CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) - ); - - return point; - - } - - copy( source ) { - - super.copy( source ); - - this.points = []; - - for ( let i = 0, l = source.points.length; i < l; i ++ ) { - - const point = source.points[ i ]; - - this.points.push( point.clone() ); - - } - - return this; - - } - - toJSON() { - - const data = super.toJSON(); - - data.points = []; - - for ( let i = 0, l = this.points.length; i < l; i ++ ) { - - const point = this.points[ i ]; - data.points.push( point.toArray() ); - - } - - return data; - - } - - fromJSON( json ) { - - super.fromJSON( json ); - - this.points = []; - - for ( let i = 0, l = json.points.length; i < l; i ++ ) { - - const point = json.points[ i ]; - this.points.push( new Vector2().fromArray( point ) ); - - } - - return this; - - } - - } - - SplineCurve.prototype.isSplineCurve = true; - - var Curves = /*#__PURE__*/Object.freeze({ - __proto__: null, - ArcCurve: ArcCurve, - CatmullRomCurve3: CatmullRomCurve3, - CubicBezierCurve: CubicBezierCurve, - CubicBezierCurve3: CubicBezierCurve3, - EllipseCurve: EllipseCurve, - LineCurve: LineCurve, - LineCurve3: LineCurve3, - QuadraticBezierCurve: QuadraticBezierCurve, - QuadraticBezierCurve3: QuadraticBezierCurve3, - SplineCurve: SplineCurve - }); - - /** - * Port from https://github.com/mapbox/earcut (v2.2.2) - */ - - const Earcut = { - - triangulate: function ( data, holeIndices, dim = 2 ) { - - const hasHoles = holeIndices && holeIndices.length; - const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length; - let outerNode = linkedList( data, 0, outerLen, dim, true ); - const triangles = []; - - if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; - - let minX, minY, maxX, maxY, x, y, invSize; - - if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); - - // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox - if ( data.length > 80 * dim ) { - - minX = maxX = data[ 0 ]; - minY = maxY = data[ 1 ]; - - for ( let i = dim; i < outerLen; i += dim ) { - - x = data[ i ]; - y = data[ i + 1 ]; - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - - } - - // minX, minY and invSize are later used to transform coords into integers for z-order calculation - invSize = Math.max( maxX - minX, maxY - minY ); - invSize = invSize !== 0 ? 1 / invSize : 0; - - } - - earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); - - return triangles; - - } - - }; - - // create a circular doubly linked list from polygon points in the specified winding order - function linkedList( data, start, end, dim, clockwise ) { - - let i, last; - - if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { - - for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); - - } else { - - for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); - - } - - if ( last && equals( last, last.next ) ) { - - removeNode( last ); - last = last.next; - - } - - return last; - - } - - // eliminate colinear or duplicate points - function filterPoints( start, end ) { - - if ( ! start ) return start; - if ( ! end ) end = start; - - let p = start, - again; - do { - - again = false; - - if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { - - removeNode( p ); - p = end = p.prev; - if ( p === p.next ) break; - again = true; - - } else { - - p = p.next; - - } - - } while ( again || p !== end ); - - return end; - - } - - // main ear slicing loop which triangulates a polygon (given as a linked list) - function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { - - if ( ! ear ) return; - - // interlink polygon nodes in z-order - if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); - - let stop = ear, - prev, next; - - // iterate through ears, slicing them one by one - while ( ear.prev !== ear.next ) { - - prev = ear.prev; - next = ear.next; - - if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { - - // cut off the triangle - triangles.push( prev.i / dim ); - triangles.push( ear.i / dim ); - triangles.push( next.i / dim ); - - removeNode( ear ); - - // skipping the next vertex leads to less sliver triangles - ear = next.next; - stop = next.next; - - continue; - - } - - ear = next; - - // if we looped through the whole remaining polygon and can't find any more ears - if ( ear === stop ) { - - // try filtering points and slicing again - if ( ! pass ) { - - earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); - - // if this didn't work, try curing all small self-intersections locally - - } else if ( pass === 1 ) { - - ear = cureLocalIntersections( filterPoints( ear ), triangles, dim ); - earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); - - // as a last resort, try splitting the remaining polygon into two - - } else if ( pass === 2 ) { - - splitEarcut( ear, triangles, dim, minX, minY, invSize ); - - } - - break; - - } - - } - - } - - // check whether a polygon node forms a valid ear with adjacent nodes - function isEar( ear ) { - - const a = ear.prev, - b = ear, - c = ear.next; - - if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear - - // now make sure we don't have other points inside the potential ear - let p = ear.next.next; - - while ( p !== ear.prev ) { - - if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.next; - - } - - return true; - - } - - function isEarHashed( ear, minX, minY, invSize ) { - - const a = ear.prev, - b = ear, - c = ear.next; - - if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear - - // triangle bbox; min & max are calculated like this for speed - const minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), - minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), - maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), - maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); - - // z-order range for the current triangle bbox; - const minZ = zOrder( minTX, minTY, minX, minY, invSize ), - maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); - - let p = ear.prevZ, - n = ear.nextZ; - - // look for points inside the triangle in both directions - while ( p && p.z >= minZ && n && n.z <= maxZ ) { - - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; - - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; - - } - - // look for remaining points in decreasing z-order - while ( p && p.z >= minZ ) { - - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; - - } - - // look for remaining points in increasing z-order - while ( n && n.z <= maxZ ) { - - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; - - } - - return true; - - } - - // go through all polygon nodes and cure small local self-intersections - function cureLocalIntersections( start, triangles, dim ) { - - let p = start; - do { - - const a = p.prev, - b = p.next.next; - - if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { - - triangles.push( a.i / dim ); - triangles.push( p.i / dim ); - triangles.push( b.i / dim ); - - // remove two nodes involved - removeNode( p ); - removeNode( p.next ); - - p = start = b; - - } - - p = p.next; - - } while ( p !== start ); - - return filterPoints( p ); - - } - - // try splitting polygon into two and triangulate them independently - function splitEarcut( start, triangles, dim, minX, minY, invSize ) { - - // look for a valid diagonal that divides the polygon into two - let a = start; - do { - - let b = a.next.next; - while ( b !== a.prev ) { - - if ( a.i !== b.i && isValidDiagonal( a, b ) ) { - - // split the polygon in two by the diagonal - let c = splitPolygon( a, b ); - - // filter colinear points around the cuts - a = filterPoints( a, a.next ); - c = filterPoints( c, c.next ); - - // run earcut on each half - earcutLinked( a, triangles, dim, minX, minY, invSize ); - earcutLinked( c, triangles, dim, minX, minY, invSize ); - return; - - } - - b = b.next; - - } - - a = a.next; - - } while ( a !== start ); - - } - - // link every hole into the outer loop, producing a single-ring polygon without holes - function eliminateHoles( data, holeIndices, outerNode, dim ) { - - const queue = []; - let i, len, start, end, list; - - for ( i = 0, len = holeIndices.length; i < len; i ++ ) { - - start = holeIndices[ i ] * dim; - end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; - list = linkedList( data, start, end, dim, false ); - if ( list === list.next ) list.steiner = true; - queue.push( getLeftmost( list ) ); - - } - - queue.sort( compareX ); - - // process holes from left to right - for ( i = 0; i < queue.length; i ++ ) { - - eliminateHole( queue[ i ], outerNode ); - outerNode = filterPoints( outerNode, outerNode.next ); - - } - - return outerNode; - - } - - function compareX( a, b ) { - - return a.x - b.x; - - } - - // find a bridge between vertices that connects hole with an outer ring and and link it - function eliminateHole( hole, outerNode ) { - - outerNode = findHoleBridge( hole, outerNode ); - if ( outerNode ) { - - const b = splitPolygon( outerNode, hole ); - - // filter collinear points around the cuts - filterPoints( outerNode, outerNode.next ); - filterPoints( b, b.next ); - - } - - } - - // David Eberly's algorithm for finding a bridge between hole and outer polygon - function findHoleBridge( hole, outerNode ) { - - let p = outerNode; - const hx = hole.x; - const hy = hole.y; - let qx = - Infinity, m; - - // find a segment intersected by a ray from the hole's leftmost point to the left; - // segment's endpoint with lesser x will be potential connection point - do { - - if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { - - const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); - if ( x <= hx && x > qx ) { - - qx = x; - if ( x === hx ) { - - if ( hy === p.y ) return p; - if ( hy === p.next.y ) return p.next; - - } - - m = p.x < p.next.x ? p : p.next; - - } - - } - - p = p.next; - - } while ( p !== outerNode ); - - if ( ! m ) return null; - - if ( hx === qx ) return m; // hole touches outer segment; pick leftmost endpoint - - // look for points inside the triangle of hole point, segment intersection and endpoint; - // if there are no points found, we have a valid connection; - // otherwise choose the point of the minimum angle with the ray as connection point - - const stop = m, - mx = m.x, - my = m.y; - let tanMin = Infinity, tan; - - p = m; - - do { - - if ( hx >= p.x && p.x >= mx && hx !== p.x && - pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { - - tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential - - if ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) { - - m = p; - tanMin = tan; - - } - - } - - p = p.next; - - } while ( p !== stop ); - - return m; - - } - - // whether sector in vertex m contains sector in vertex p in the same coordinates - function sectorContainsSector( m, p ) { - - return area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0; - - } - - // interlink polygon nodes in z-order - function indexCurve( start, minX, minY, invSize ) { - - let p = start; - do { - - if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); - p.prevZ = p.prev; - p.nextZ = p.next; - p = p.next; - - } while ( p !== start ); - - p.prevZ.nextZ = null; - p.prevZ = null; - - sortLinked( p ); - - } - - // Simon Tatham's linked list merge sort algorithm - // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html - function sortLinked( list ) { - - let i, p, q, e, tail, numMerges, pSize, qSize, - inSize = 1; - - do { - - p = list; - list = null; - tail = null; - numMerges = 0; - - while ( p ) { - - numMerges ++; - q = p; - pSize = 0; - for ( i = 0; i < inSize; i ++ ) { - - pSize ++; - q = q.nextZ; - if ( ! q ) break; - - } - - qSize = inSize; - - while ( pSize > 0 || ( qSize > 0 && q ) ) { - - if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { - - e = p; - p = p.nextZ; - pSize --; - - } else { - - e = q; - q = q.nextZ; - qSize --; - - } - - if ( tail ) tail.nextZ = e; - else list = e; - - e.prevZ = tail; - tail = e; - - } - - p = q; - - } - - tail.nextZ = null; - inSize *= 2; - - } while ( numMerges > 1 ); - - return list; - - } - - // z-order of a point given coords and inverse of the longer side of data bbox - function zOrder( x, y, minX, minY, invSize ) { - - // coords are transformed into non-negative 15-bit integer range - x = 32767 * ( x - minX ) * invSize; - y = 32767 * ( y - minY ) * invSize; - - x = ( x | ( x << 8 ) ) & 0x00FF00FF; - x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; - x = ( x | ( x << 2 ) ) & 0x33333333; - x = ( x | ( x << 1 ) ) & 0x55555555; - - y = ( y | ( y << 8 ) ) & 0x00FF00FF; - y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; - y = ( y | ( y << 2 ) ) & 0x33333333; - y = ( y | ( y << 1 ) ) & 0x55555555; - - return x | ( y << 1 ); - - } - - // find the leftmost node of a polygon ring - function getLeftmost( start ) { - - let p = start, - leftmost = start; - do { - - if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; - p = p.next; - - } while ( p !== start ); - - return leftmost; - - } - - // check if a point lies within a convex triangle - function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { - - return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && - ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && - ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; - - } - - // check if a diagonal between two polygon nodes is valid (lies in polygon interior) - function isValidDiagonal( a, b ) { - - return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // dones't intersect other edges - ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible - ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors - equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case - - } - - // signed area of a triangle - function area( p, q, r ) { - - return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); - - } - - // check if two points are equal - function equals( p1, p2 ) { - - return p1.x === p2.x && p1.y === p2.y; - - } - - // check if two segments intersect - function intersects( p1, q1, p2, q2 ) { - - const o1 = sign( area( p1, q1, p2 ) ); - const o2 = sign( area( p1, q1, q2 ) ); - const o3 = sign( area( p2, q2, p1 ) ); - const o4 = sign( area( p2, q2, q1 ) ); - - if ( o1 !== o2 && o3 !== o4 ) return true; // general case - - if ( o1 === 0 && onSegment( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 - if ( o2 === 0 && onSegment( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 - if ( o3 === 0 && onSegment( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 - if ( o4 === 0 && onSegment( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 - - return false; - - } - - // for collinear points p, q, r, check if point q lies on segment pr - function onSegment( p, q, r ) { - - return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); - - } - - function sign( num ) { - - return num > 0 ? 1 : num < 0 ? - 1 : 0; - - } - - // check if a polygon diagonal intersects any polygon segments - function intersectsPolygon( a, b ) { - - let p = a; - do { - - if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && - intersects( p, p.next, a, b ) ) return true; - p = p.next; - - } while ( p !== a ); - - return false; - - } - - // check if a polygon diagonal is locally inside the polygon - function locallyInside( a, b ) { - - return area( a.prev, a, a.next ) < 0 ? - area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : - area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; - - } - - // check if the middle point of a polygon diagonal is inside the polygon - function middleInside( a, b ) { - - let p = a, - inside = false; - const px = ( a.x + b.x ) / 2, - py = ( a.y + b.y ) / 2; - do { - - if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && - ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) - inside = ! inside; - p = p.next; - - } while ( p !== a ); - - return inside; - - } - - // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; - // if one belongs to the outer ring and another to a hole, it merges it into a single ring - function splitPolygon( a, b ) { - - const a2 = new Node( a.i, a.x, a.y ), - b2 = new Node( b.i, b.x, b.y ), - an = a.next, - bp = b.prev; - - a.next = b; - b.prev = a; - - a2.next = an; - an.prev = a2; - - b2.next = a2; - a2.prev = b2; - - bp.next = b2; - b2.prev = bp; - - return b2; - - } - - // create a node and optionally link it with previous one (in a circular doubly linked list) - function insertNode( i, x, y, last ) { - - const p = new Node( i, x, y ); - - if ( ! last ) { - - p.prev = p; - p.next = p; - - } else { - - p.next = last.next; - p.prev = last; - last.next.prev = p; - last.next = p; - - } - - return p; - - } - - function removeNode( p ) { - - p.next.prev = p.prev; - p.prev.next = p.next; - - if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; - if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; - - } - - function Node( i, x, y ) { - - // vertex index in coordinates array - this.i = i; - - // vertex coordinates - this.x = x; - this.y = y; - - // previous and next vertex nodes in a polygon ring - this.prev = null; - this.next = null; - - // z-order curve value - this.z = null; - - // previous and next nodes in z-order - this.prevZ = null; - this.nextZ = null; - - // indicates whether this is a steiner point - this.steiner = false; - - } - - function signedArea( data, start, end, dim ) { - - let sum = 0; - for ( let i = start, j = end - dim; i < end; i += dim ) { - - sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); - j = i; - - } - - return sum; - - } - - class ShapeUtils { - - // calculate area of the contour polygon - - static area( contour ) { - - const n = contour.length; - let a = 0.0; - - for ( let p = n - 1, q = 0; q < n; p = q ++ ) { - - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; - - } - - return a * 0.5; - - } - - static isClockWise( pts ) { - - return ShapeUtils.area( pts ) < 0; - - } - - static triangulateShape( contour, holes ) { - - const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] - const holeIndices = []; // array of hole indices - const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] - - removeDupEndPts( contour ); - addContour( vertices, contour ); - - // - - let holeIndex = contour.length; - - holes.forEach( removeDupEndPts ); - - for ( let i = 0; i < holes.length; i ++ ) { - - holeIndices.push( holeIndex ); - holeIndex += holes[ i ].length; - addContour( vertices, holes[ i ] ); - - } - - // - - const triangles = Earcut.triangulate( vertices, holeIndices ); - - // - - for ( let i = 0; i < triangles.length; i += 3 ) { - - faces.push( triangles.slice( i, i + 3 ) ); - - } - - return faces; - - } - - } - - function removeDupEndPts( points ) { - - const l = points.length; - - if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { - - points.pop(); - - } - - } - - function addContour( vertices, contour ) { - - for ( let i = 0; i < contour.length; i ++ ) { - - vertices.push( contour[ i ].x ); - vertices.push( contour[ i ].y ); - - } - - } - - /** - * Creates extruded geometry from a path shape. - * - * parameters = { - * - * curveSegments: , // number of points on the curves - * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too - * depth: , // Depth to extrude the shape - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into the original shape bevel goes - * bevelSize: , // how far from shape outline (including bevelOffset) is bevel - * bevelOffset: , // how far from shape outline does bevel start - * bevelSegments: , // number of bevel layers - * - * extrudePath: // curve to extrude shape along - * - * UVGenerator: // object that provides UV generator functions - * - * } - */ - - class ExtrudeGeometry extends BufferGeometry { - - constructor( shapes, options ) { - - super(); - - this.type = 'ExtrudeGeometry'; - - this.parameters = { - shapes: shapes, - options: options - }; - - shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; - - const scope = this; - - const verticesArray = []; - const uvArray = []; - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - addShape( shape ); - - } - - // build geometry - - this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); - - this.computeVertexNormals(); - - // functions - - function addShape( shape ) { - - const placeholder = []; - - // options - - const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - const steps = options.steps !== undefined ? options.steps : 1; - let depth = options.depth !== undefined ? options.depth : 100; - - let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; - let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; - let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; - let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; - let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; - - const extrudePath = options.extrudePath; - - const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; - - // deprecated options - - if ( options.amount !== undefined ) { - - console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' ); - depth = options.amount; - - } - - // - - let extrudePts, extrudeByPath = false; - let splineTube, binormal, normal, position2; - - if ( extrudePath ) { - - extrudePts = extrudePath.getSpacedPoints( steps ); - - extrudeByPath = true; - bevelEnabled = false; // bevels not supported for path extrusion - - // SETUP TNB variables - - // TODO1 - have a .isClosed in spline? - - splineTube = extrudePath.computeFrenetFrames( steps, false ); - - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); - - binormal = new Vector3(); - normal = new Vector3(); - position2 = new Vector3(); - - } - - // Safeguards if bevels are not enabled - - if ( ! bevelEnabled ) { - - bevelSegments = 0; - bevelThickness = 0; - bevelSize = 0; - bevelOffset = 0; - - } - - // Variables initialization - - const shapePoints = shape.extractPoints( curveSegments ); - - let vertices = shapePoints.shape; - const holes = shapePoints.holes; - - const reverse = ! ShapeUtils.isClockWise( vertices ); - - if ( reverse ) { - - vertices = vertices.reverse(); - - // Maybe we should also check if holes are in the opposite direction, just to be safe ... - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - - if ( ShapeUtils.isClockWise( ahole ) ) { - - holes[ h ] = ahole.reverse(); - - } - - } - - } - - - const faces = ShapeUtils.triangulateShape( vertices, holes ); - - /* Vertices */ - - const contour = vertices; // vertices has all points but contour has only points of circumference - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - - vertices = vertices.concat( ahole ); - - } - - - function scalePt2( pt, vec, size ) { - - if ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' ); - - return vec.clone().multiplyScalar( size ).add( pt ); - - } - - const vlen = vertices.length, flen = faces.length; - - - // Find directions for point movement - - - function getBevelVec( inPt, inPrev, inNext ) { - - // computes for inPt the corresponding point inPt' on a new contour - // shifted by 1 unit (length of normalized vector) to the left - // if we walk along contour clockwise, this new contour is outside the old one - // - // inPt' is the intersection of the two lines parallel to the two - // adjacent edges of inPt at a distance of 1 unit on the left side. - - let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt - - // good reading for geometry algorithms (here: line-line intersection) - // http://geomalgorithms.com/a05-_intersect-1.html - - const v_prev_x = inPt.x - inPrev.x, - v_prev_y = inPt.y - inPrev.y; - const v_next_x = inNext.x - inPt.x, - v_next_y = inNext.y - inPt.y; - - const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); - - // check for collinear edges - const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - if ( Math.abs( collinear0 ) > Number.EPSILON ) { - - // not collinear - - // length of vectors for normalizing - - const v_prev_len = Math.sqrt( v_prev_lensq ); - const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); - - // shift adjacent points by unit vectors to the left - - const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); - const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); - - const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); - const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); - - // scaling factor for v_prev to intersection point - - const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / - ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - // vector from inPt to intersection point - - v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); - v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); - - // Don't normalize!, otherwise sharp corners become ugly - // but prevent crazy spikes - const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); - if ( v_trans_lensq <= 2 ) { - - return new Vector2( v_trans_x, v_trans_y ); - - } else { - - shrink_by = Math.sqrt( v_trans_lensq / 2 ); - - } - - } else { - - // handle special case of collinear edges - - let direction_eq = false; // assumes: opposite - - if ( v_prev_x > Number.EPSILON ) { - - if ( v_next_x > Number.EPSILON ) { - - direction_eq = true; - - } - - } else { - - if ( v_prev_x < - Number.EPSILON ) { - - if ( v_next_x < - Number.EPSILON ) { - - direction_eq = true; - - } - - } else { - - if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { - - direction_eq = true; - - } - - } - - } - - if ( direction_eq ) { - - // console.log("Warning: lines are a straight sequence"); - v_trans_x = - v_prev_y; - v_trans_y = v_prev_x; - shrink_by = Math.sqrt( v_prev_lensq ); - - } else { - - // console.log("Warning: lines are a straight spike"); - v_trans_x = v_prev_x; - v_trans_y = v_prev_y; - shrink_by = Math.sqrt( v_prev_lensq / 2 ); - - } - - } - - return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); - - } - - - const contourMovements = []; - - for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - - if ( j === il ) j = 0; - if ( k === il ) k = 0; - - // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) - - contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); - - } - - const holesMovements = []; - let oneHoleMovements, verticesMovements = contourMovements.concat(); - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - - oneHoleMovements = []; - - for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - - if ( j === il ) j = 0; - if ( k === il ) k = 0; - - // (j)---(i)---(k) - oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); - - } - - holesMovements.push( oneHoleMovements ); - verticesMovements = verticesMovements.concat( oneHoleMovements ); - - } - - - // Loop bevelSegments, 1 for the front, 1 for the back - - for ( let b = 0; b < bevelSegments; b ++ ) { - - //for ( b = bevelSegments; b > 0; b -- ) { - - const t = b / bevelSegments; - const z = bevelThickness * Math.cos( t * Math.PI / 2 ); - const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - - // contract shape - - for ( let i = 0, il = contour.length; i < il; i ++ ) { - - const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - - v( vert.x, vert.y, - z ); - - } - - // expand holes - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; - - for ( let i = 0, il = ahole.length; i < il; i ++ ) { - - const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - - v( vert.x, vert.y, - z ); - - } - - } - - } - - const bs = bevelSize + bevelOffset; - - // Back facing vertices - - for ( let i = 0; i < vlen; i ++ ) { - - const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, 0 ); - - } else { - - // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); - - normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); - - position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); - - v( position2.x, position2.y, position2.z ); - - } - - } - - // Add stepped vertices... - // Including front facing vertices - - for ( let s = 1; s <= steps; s ++ ) { - - for ( let i = 0; i < vlen; i ++ ) { - - const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, depth / steps * s ); - - } else { - - // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); - - normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); - - position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); - - v( position2.x, position2.y, position2.z ); - - } - - } - - } - - - // Add bevel segments planes - - //for ( b = 1; b <= bevelSegments; b ++ ) { - for ( let b = bevelSegments - 1; b >= 0; b -- ) { - - const t = b / bevelSegments; - const z = bevelThickness * Math.cos( t * Math.PI / 2 ); - const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - - // contract shape - - for ( let i = 0, il = contour.length; i < il; i ++ ) { - - const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, depth + z ); - - } - - // expand holes - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; - - for ( let i = 0, il = ahole.length; i < il; i ++ ) { - - const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, depth + z ); - - } else { - - v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); - - } - - } - - } - - } - - /* Faces */ - - // Top and bottom faces - - buildLidFaces(); - - // Sides faces - - buildSideFaces(); - - - ///// Internal functions - - function buildLidFaces() { - - const start = verticesArray.length / 3; - - if ( bevelEnabled ) { - - let layer = 0; // steps + 1 - let offset = vlen * layer; - - // Bottom faces - - for ( let i = 0; i < flen; i ++ ) { - - const face = faces[ i ]; - f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); - - } - - layer = steps + bevelSegments * 2; - offset = vlen * layer; - - // Top faces - - for ( let i = 0; i < flen; i ++ ) { - - const face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); - - } - - } else { - - // Bottom faces - - for ( let i = 0; i < flen; i ++ ) { - - const face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ] ); - - } - - // Top faces - - for ( let i = 0; i < flen; i ++ ) { - - const face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); - - } - - } - - scope.addGroup( start, verticesArray.length / 3 - start, 0 ); - - } - - // Create faces for the z-sides of the shape - - function buildSideFaces() { - - const start = verticesArray.length / 3; - let layeroffset = 0; - sidewalls( contour, layeroffset ); - layeroffset += contour.length; - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - sidewalls( ahole, layeroffset ); - - //, true - layeroffset += ahole.length; - - } - - - scope.addGroup( start, verticesArray.length / 3 - start, 1 ); - - - } - - function sidewalls( contour, layeroffset ) { - - let i = contour.length; - - while ( -- i >= 0 ) { - - const j = i; - let k = i - 1; - if ( k < 0 ) k = contour.length - 1; - - //console.log('b', i,j, i-1, k,vertices.length); - - for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { - - const slen1 = vlen * s; - const slen2 = vlen * ( s + 1 ); - - const a = layeroffset + j + slen1, - b = layeroffset + k + slen1, - c = layeroffset + k + slen2, - d = layeroffset + j + slen2; - - f4( a, b, c, d ); - - } - - } - - } - - function v( x, y, z ) { - - placeholder.push( x ); - placeholder.push( y ); - placeholder.push( z ); - - } - - - function f3( a, b, c ) { - - addVertex( a ); - addVertex( b ); - addVertex( c ); - - const nextIndex = verticesArray.length / 3; - const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); - - } - - function f4( a, b, c, d ) { - - addVertex( a ); - addVertex( b ); - addVertex( d ); - - addVertex( b ); - addVertex( c ); - addVertex( d ); - - - const nextIndex = verticesArray.length / 3; - const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 3 ] ); - - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); - addUV( uvs[ 3 ] ); - - } - - function addVertex( index ) { - - verticesArray.push( placeholder[ index * 3 + 0 ] ); - verticesArray.push( placeholder[ index * 3 + 1 ] ); - verticesArray.push( placeholder[ index * 3 + 2 ] ); - - } - - - function addUV( vector2 ) { - - uvArray.push( vector2.x ); - uvArray.push( vector2.y ); - - } - - } - - } - - toJSON() { - - const data = super.toJSON(); - - const shapes = this.parameters.shapes; - const options = this.parameters.options; - - return toJSON$1( shapes, options, data ); - - } - - static fromJSON( data, shapes ) { - - const geometryShapes = []; - - for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { - - const shape = shapes[ data.shapes[ j ] ]; - - geometryShapes.push( shape ); - - } - - const extrudePath = data.options.extrudePath; - - if ( extrudePath !== undefined ) { - - data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); - - } - - return new ExtrudeGeometry( geometryShapes, data.options ); - - } - - } - - const WorldUVGenerator = { - - generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { - - const a_x = vertices[ indexA * 3 ]; - const a_y = vertices[ indexA * 3 + 1 ]; - const b_x = vertices[ indexB * 3 ]; - const b_y = vertices[ indexB * 3 + 1 ]; - const c_x = vertices[ indexC * 3 ]; - const c_y = vertices[ indexC * 3 + 1 ]; - - return [ - new Vector2( a_x, a_y ), - new Vector2( b_x, b_y ), - new Vector2( c_x, c_y ) - ]; - - }, - - generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { - - const a_x = vertices[ indexA * 3 ]; - const a_y = vertices[ indexA * 3 + 1 ]; - const a_z = vertices[ indexA * 3 + 2 ]; - const b_x = vertices[ indexB * 3 ]; - const b_y = vertices[ indexB * 3 + 1 ]; - const b_z = vertices[ indexB * 3 + 2 ]; - const c_x = vertices[ indexC * 3 ]; - const c_y = vertices[ indexC * 3 + 1 ]; - const c_z = vertices[ indexC * 3 + 2 ]; - const d_x = vertices[ indexD * 3 ]; - const d_y = vertices[ indexD * 3 + 1 ]; - const d_z = vertices[ indexD * 3 + 2 ]; - - if ( Math.abs( a_y - b_y ) < Math.abs( a_x - b_x ) ) { - - return [ - new Vector2( a_x, 1 - a_z ), - new Vector2( b_x, 1 - b_z ), - new Vector2( c_x, 1 - c_z ), - new Vector2( d_x, 1 - d_z ) - ]; - - } else { - - return [ - new Vector2( a_y, 1 - a_z ), - new Vector2( b_y, 1 - b_z ), - new Vector2( c_y, 1 - c_z ), - new Vector2( d_y, 1 - d_z ) - ]; - - } - - } - - }; - - function toJSON$1( shapes, options, data ) { - - data.shapes = []; - - if ( Array.isArray( shapes ) ) { - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - - data.shapes.push( shape.uuid ); - - } - - } else { - - data.shapes.push( shapes.uuid ); - - } - - if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); - - return data; - - } - - class ShapeGeometry extends BufferGeometry { - - constructor( shapes, curveSegments = 12 ) { - - super(); - this.type = 'ShapeGeometry'; - - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - let groupStart = 0; - let groupCount = 0; - - // allow single and array values for "shapes" parameter - - if ( Array.isArray( shapes ) === false ) { - - addShape( shapes ); - - } else { - - for ( let i = 0; i < shapes.length; i ++ ) { - - addShape( shapes[ i ] ); - - this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support - - groupStart += groupCount; - groupCount = 0; - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - - // helper functions - - function addShape( shape ) { - - const indexOffset = vertices.length / 3; - const points = shape.extractPoints( curveSegments ); - - let shapeVertices = points.shape; - const shapeHoles = points.holes; - - // check direction of vertices - - if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { - - shapeVertices = shapeVertices.reverse(); - - } - - for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { - - const shapeHole = shapeHoles[ i ]; - - if ( ShapeUtils.isClockWise( shapeHole ) === true ) { - - shapeHoles[ i ] = shapeHole.reverse(); - - } - - } - - const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); - - // join vertices of inner and outer paths to a single array - - for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { - - const shapeHole = shapeHoles[ i ]; - shapeVertices = shapeVertices.concat( shapeHole ); - - } - - // vertices, normals, uvs - - for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { - - const vertex = shapeVertices[ i ]; - - vertices.push( vertex.x, vertex.y, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( vertex.x, vertex.y ); // world uvs - - } - - // incides - - for ( let i = 0, l = faces.length; i < l; i ++ ) { - - const face = faces[ i ]; - - const a = face[ 0 ] + indexOffset; - const b = face[ 1 ] + indexOffset; - const c = face[ 2 ] + indexOffset; - - indices.push( a, b, c ); - groupCount += 3; - - } - - } - - } - - toJSON() { - - const data = super.toJSON(); - - const shapes = this.parameters.shapes; - - return toJSON( shapes, data ); - - } - - static fromJSON( data, shapes ) { - - const geometryShapes = []; - - for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { - - const shape = shapes[ data.shapes[ j ] ]; - - geometryShapes.push( shape ); - - } - - return new ShapeGeometry( geometryShapes, data.curveSegments ); - - } - - } - - function toJSON( shapes, data ) { - - data.shapes = []; - - if ( Array.isArray( shapes ) ) { - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - - data.shapes.push( shape.uuid ); - - } - - } else { - - data.shapes.push( shapes.uuid ); - - } - - return data; - - } - - class WireframeGeometry extends BufferGeometry { - - constructor( geometry ) { - - super(); - this.type = 'WireframeGeometry'; - - if ( geometry.isGeometry === true ) { - - console.error( 'THREE.WireframeGeometry no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - return; - - } - - // buffer - - const vertices = []; - const edges = new Set(); - - // helper variables - - const start = new Vector3(); - const end = new Vector3(); - - if ( geometry.index !== null ) { - - // indexed BufferGeometry - - const position = geometry.attributes.position; - const indices = geometry.index; - let groups = geometry.groups; - - if ( groups.length === 0 ) { - - groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; - - } - - // create a data structure that contains all eges without duplicates - - for ( let o = 0, ol = groups.length; o < ol; ++ o ) { - - const group = groups[ o ]; - - const groupStart = group.start; - const groupCount = group.count; - - for ( let i = groupStart, l = ( groupStart + groupCount ); i < l; i += 3 ) { - - for ( let j = 0; j < 3; j ++ ) { - - const index1 = indices.getX( i + j ); - const index2 = indices.getX( i + ( j + 1 ) % 3 ); - - start.fromBufferAttribute( position, index1 ); - end.fromBufferAttribute( position, index2 ); - - if ( isUniqueEdge( start, end, edges ) === true ) { - - vertices.push( start.x, start.y, start.z ); - vertices.push( end.x, end.y, end.z ); - - } - - } - - } - - } - - } else { - - // non-indexed BufferGeometry - - const position = geometry.attributes.position; - - for ( let i = 0, l = ( position.count / 3 ); i < l; i ++ ) { - - for ( let j = 0; j < 3; j ++ ) { - - // three edges per triangle, an edge is represented as (index1, index2) - // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) - - const index1 = 3 * i + j; - const index2 = 3 * i + ( ( j + 1 ) % 3 ); - - start.fromBufferAttribute( position, index1 ); - end.fromBufferAttribute( position, index2 ); - - if ( isUniqueEdge( start, end, edges ) === true ) { - - vertices.push( start.x, start.y, start.z ); - vertices.push( end.x, end.y, end.z ); - - } - - } - - } - - } - - // build geometry - - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - - } - - } - - function isUniqueEdge( start, end, edges ) { - - const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; - const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge - - if ( edges.has( hash1 ) === true || edges.has( hash2 ) === true ) { - - return false; - - } else { - - edges.add( hash1, hash2 ); - return true; - - } - - } - - /** - * parameters = { - * color: - * } - */ - - class ShadowMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.type = 'ShadowMaterial'; - - this.color = new Color( 0x000000 ); - this.transparent = true; - - this.setValues( parameters ); - - } - - copy( source ) { - - super.copy( source ); - - this.color.copy( source.color ); - - return this; - - } - - } - - ShadowMaterial.prototype.isShadowMaterial = true; - - /** - * parameters = { - * color: , - * roughness: , - * metalness: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * roughnessMap: new THREE.Texture( ), - * - * metalnessMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * envMapIntensity: - * - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * flatShading: - * } - */ - - class MeshStandardMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.defines = { 'STANDARD': '' }; - - this.type = 'MeshStandardMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - this.roughness = 1.0; - this.metalness = 0.0; - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.roughnessMap = null; - - this.metalnessMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.envMapIntensity = 1.0; - - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.flatShading = false; - - this.setValues( parameters ); - - } - - copy( source ) { - - super.copy( source ); - - this.defines = { 'STANDARD': '' }; - - this.color.copy( source.color ); - this.roughness = source.roughness; - this.metalness = source.metalness; - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.roughnessMap = source.roughnessMap; - - this.metalnessMap = source.metalnessMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.envMapIntensity = source.envMapIntensity; - - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.flatShading = source.flatShading; - - return this; - - } - - } - - MeshStandardMaterial.prototype.isMeshStandardMaterial = true; - - /** - * parameters = { - * clearcoat: , - * clearcoatMap: new THREE.Texture( ), - * clearcoatRoughness: , - * clearcoatRoughnessMap: new THREE.Texture( ), - * clearcoatNormalScale: , - * clearcoatNormalMap: new THREE.Texture( ), - * - * ior: , - * reflectivity: , - * - * sheenTint: , - * - * transmission: , - * transmissionMap: new THREE.Texture( ), - * - * thickness: , - * thicknessMap: new THREE.Texture( ), - * attenuationDistance: , - * attenuationTint: , - * - * specularIntensity: , - * specularIntensityhMap: new THREE.Texture( ), - * specularTint: , - * specularTintMap: new THREE.Texture( ) - * } - */ - - class MeshPhysicalMaterial extends MeshStandardMaterial { - - #clearcoat = 0; - #transmission = 0; - - constructor( parameters ) { - - super(); - - this.defines = { - - 'STANDARD': '', - 'PHYSICAL': '' - - }; - - this.type = 'MeshPhysicalMaterial'; - - this.clearcoatMap = null; - this.clearcoatRoughness = 0.0; - this.clearcoatRoughnessMap = null; - this.clearcoatNormalScale = new Vector2( 1, 1 ); - this.clearcoatNormalMap = null; - - this.ior = 1.5; - - Object.defineProperty( this, 'reflectivity', { - get: function () { - - return ( clamp( 2.5 * ( this.ior - 1 ) / ( this.ior + 1 ), 0, 1 ) ); - - }, - set: function ( reflectivity ) { - - this.ior = ( 1 + 0.4 * reflectivity ) / ( 1 - 0.4 * reflectivity ); - - } - } ); - - this.sheenTint = new Color( 0x000000 ); - - this.transmission = 0.0; - this.transmissionMap = null; - - this.thickness = 0.01; - this.thicknessMap = null; - this.attenuationDistance = 0.0; - this.attenuationTint = new Color( 1, 1, 1 ); - - this.specularIntensity = 1.0; - this.specularIntensityMap = null; - this.specularTint = new Color( 1, 1, 1 ); - this.specularTintMap = null; - - this.setValues( parameters ); - - } - - get clearcoat() { - - return this.#clearcoat; - - } - - set clearcoat( value ) { - - if ( this.#clearcoat > 0 !== value > 0 ) { - - this.version ++; - - } - - this.#clearcoat = value; - - } - - get transmission() { - - return this.#transmission; - - } - - set transmission( value ) { - - if ( this.#transmission > 0 !== value > 0 ) { - - this.version ++; - - } - - this.#transmission = value; - - } - - - copy( source ) { - - super.copy( source ); - - this.defines = { - - 'STANDARD': '', - 'PHYSICAL': '' - - }; - - this.clearcoat = source.clearcoat; - this.clearcoatMap = source.clearcoatMap; - this.clearcoatRoughness = source.clearcoatRoughness; - this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; - this.clearcoatNormalMap = source.clearcoatNormalMap; - this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); - - this.ior = source.ior; - - this.sheenTint.copy( source.sheenTint ); - - this.transmission = source.transmission; - this.transmissionMap = source.transmissionMap; - - this.thickness = source.thickness; - this.thicknessMap = source.thicknessMap; - this.attenuationDistance = source.attenuationDistance; - this.attenuationTint.copy( source.attenuationTint ); - - this.specularIntensity = source.specularIntensity; - this.specularIntensityMap = source.specularIntensityMap; - this.specularTint.copy( source.specularTint ); - this.specularTintMap = source.specularTintMap; - - return this; - - } - - } - - MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; - - /** - * parameters = { - * color: , - * specular: , - * shininess: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.MultiplyOperation, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * flatShading: - * } - */ - - class MeshPhongMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.type = 'MeshPhongMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - this.specular = new Color( 0x111111 ); - this.shininess = 30; - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.flatShading = false; - - this.setValues( parameters ); - - } - - copy( source ) { - - super.copy( source ); - - this.color.copy( source.color ); - this.specular.copy( source.specular ); - this.shininess = source.shininess; - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.flatShading = source.flatShading; - - return this; - - } - - } - - MeshPhongMaterial.prototype.isMeshPhongMaterial = true; - - /** - * parameters = { - * color: , - * - * map: new THREE.Texture( ), - * gradientMap: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * wireframe: , - * wireframeLinewidth: , - * - * } - */ - - class MeshToonMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.defines = { 'TOON': '' }; - - this.type = 'MeshToonMaterial'; - - this.color = new Color( 0xffffff ); - - this.map = null; - this.gradientMap = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.alphaMap = null; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.setValues( parameters ); - - } - - copy( source ) { - - super.copy( source ); - - this.color.copy( source.color ); - - this.map = source.map; - this.gradientMap = source.gradientMap; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.alphaMap = source.alphaMap; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - return this; - - } - - } - - MeshToonMaterial.prototype.isMeshToonMaterial = true; - - /** - * parameters = { - * opacity: , - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * - * flatShading: - * } - */ - - class MeshNormalMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.type = 'MeshNormalMaterial'; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; - - this.flatShading = false; - - this.setValues( parameters ); - - } - - copy( source ) { - - super.copy( source ); - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - this.flatShading = source.flatShading; - - return this; - - } - - } - - MeshNormalMaterial.prototype.isMeshNormalMaterial = true; - - /** - * parameters = { - * color: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * } - */ - - class MeshLambertMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.type = 'MeshLambertMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.setValues( parameters ); - - } - - copy( source ) { - - super.copy( source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - return this; - - } - - } - - MeshLambertMaterial.prototype.isMeshLambertMaterial = true; - - /** - * parameters = { - * color: , - * opacity: , - * - * matcap: new THREE.Texture( ), - * - * map: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * flatShading: - * } - */ - - class MeshMatcapMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.defines = { 'MATCAP': '' }; - - this.type = 'MeshMatcapMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - - this.matcap = null; - - this.map = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.alphaMap = null; - - this.flatShading = false; - - this.setValues( parameters ); - - } - - - copy( source ) { - - super.copy( source ); - - this.defines = { 'MATCAP': '' }; - - this.color.copy( source.color ); - - this.matcap = source.matcap; - - this.map = source.map; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.alphaMap = source.alphaMap; - - this.flatShading = source.flatShading; - - return this; - - } - - } - - MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; - - /** - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * - * scale: , - * dashSize: , - * gapSize: - * } - */ - - class LineDashedMaterial extends LineBasicMaterial { - - constructor( parameters ) { - - super(); - - this.type = 'LineDashedMaterial'; - - this.scale = 1; - this.dashSize = 3; - this.gapSize = 1; - - this.setValues( parameters ); - - } - - copy( source ) { - - super.copy( source ); - - this.scale = source.scale; - this.dashSize = source.dashSize; - this.gapSize = source.gapSize; - - return this; - - } - - } - - LineDashedMaterial.prototype.isLineDashedMaterial = true; - - const AnimationUtils = { - - // same as Array.prototype.slice, but also works on typed arrays - arraySlice: function ( array, from, to ) { - - if ( AnimationUtils.isTypedArray( array ) ) { - - // in ios9 array.subarray(from, undefined) will return empty array - // but array.subarray(from) or array.subarray(from, len) is correct - return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); - - } - - return array.slice( from, to ); - - }, - - // converts an array to a specific type - convertArray: function ( array, type, forceClone ) { - - if ( ! array || // let 'undefined' and 'null' pass - ! forceClone && array.constructor === type ) return array; - - if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { - - return new type( array ); // create typed array - - } - - return Array.prototype.slice.call( array ); // create Array - - }, - - isTypedArray: function ( object ) { - - return ArrayBuffer.isView( object ) && - ! ( object instanceof DataView ); - - }, - - // returns an array by which times and values can be sorted - getKeyframeOrder: function ( times ) { - - function compareTime( i, j ) { - - return times[ i ] - times[ j ]; - - } - - const n = times.length; - const result = new Array( n ); - for ( let i = 0; i !== n; ++ i ) result[ i ] = i; - - result.sort( compareTime ); - - return result; - - }, - - // uses the array previously returned by 'getKeyframeOrder' to sort data - sortedArray: function ( values, stride, order ) { - - const nValues = values.length; - const result = new values.constructor( nValues ); - - for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { - - const srcOffset = order[ i ] * stride; - - for ( let j = 0; j !== stride; ++ j ) { - - result[ dstOffset ++ ] = values[ srcOffset + j ]; - - } - - } - - return result; - - }, - - // function for parsing AOS keyframe formats - flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { - - let i = 1, key = jsonKeys[ 0 ]; - - while ( key !== undefined && key[ valuePropertyName ] === undefined ) { - - key = jsonKeys[ i ++ ]; - - } - - if ( key === undefined ) return; // no data - - let value = key[ valuePropertyName ]; - if ( value === undefined ) return; // no data - - if ( Array.isArray( value ) ) { - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - values.push.apply( values, value ); // push all elements - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } else if ( value.toArray !== undefined ) { - - // ...assume THREE.Math-ish - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - value.toArray( values, values.length ); - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } else { - - // otherwise push as-is - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - values.push( value ); - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } - - }, - - subclip: function ( sourceClip, name, startFrame, endFrame, fps = 30 ) { - - const clip = sourceClip.clone(); - - clip.name = name; - - const tracks = []; - - for ( let i = 0; i < clip.tracks.length; ++ i ) { - - const track = clip.tracks[ i ]; - const valueSize = track.getValueSize(); - - const times = []; - const values = []; - - for ( let j = 0; j < track.times.length; ++ j ) { - - const frame = track.times[ j ] * fps; - - if ( frame < startFrame || frame >= endFrame ) continue; - - times.push( track.times[ j ] ); - - for ( let k = 0; k < valueSize; ++ k ) { - - values.push( track.values[ j * valueSize + k ] ); - - } - - } - - if ( times.length === 0 ) continue; - - track.times = AnimationUtils.convertArray( times, track.times.constructor ); - track.values = AnimationUtils.convertArray( values, track.values.constructor ); - - tracks.push( track ); - - } - - clip.tracks = tracks; - - // find minimum .times value across all tracks in the trimmed clip - - let minStartTime = Infinity; - - for ( let i = 0; i < clip.tracks.length; ++ i ) { - - if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { - - minStartTime = clip.tracks[ i ].times[ 0 ]; - - } - - } - - // shift all tracks such that clip begins at t=0 - - for ( let i = 0; i < clip.tracks.length; ++ i ) { - - clip.tracks[ i ].shift( - 1 * minStartTime ); - - } - - clip.resetDuration(); - - return clip; - - }, - - makeClipAdditive: function ( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { - - if ( fps <= 0 ) fps = 30; - - const numTracks = referenceClip.tracks.length; - const referenceTime = referenceFrame / fps; - - // Make each track's values relative to the values at the reference frame - for ( let i = 0; i < numTracks; ++ i ) { - - const referenceTrack = referenceClip.tracks[ i ]; - const referenceTrackType = referenceTrack.ValueTypeName; - - // Skip this track if it's non-numeric - if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue; - - // Find the track in the target clip whose name and type matches the reference track - const targetTrack = targetClip.tracks.find( function ( track ) { - - return track.name === referenceTrack.name - && track.ValueTypeName === referenceTrackType; - - } ); - - if ( targetTrack === undefined ) continue; - - let referenceOffset = 0; - const referenceValueSize = referenceTrack.getValueSize(); - - if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - - referenceOffset = referenceValueSize / 3; - - } - - let targetOffset = 0; - const targetValueSize = targetTrack.getValueSize(); - - if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - - targetOffset = targetValueSize / 3; - - } - - const lastIndex = referenceTrack.times.length - 1; - let referenceValue; - - // Find the value to subtract out of the track - if ( referenceTime <= referenceTrack.times[ 0 ] ) { - - // Reference frame is earlier than the first keyframe, so just use the first keyframe - const startIndex = referenceOffset; - const endIndex = referenceValueSize - referenceOffset; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); - - } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { - - // Reference frame is after the last keyframe, so just use the last keyframe - const startIndex = lastIndex * referenceValueSize + referenceOffset; - const endIndex = startIndex + referenceValueSize - referenceOffset; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); - - } else { - - // Interpolate to the reference value - const interpolant = referenceTrack.createInterpolant(); - const startIndex = referenceOffset; - const endIndex = referenceValueSize - referenceOffset; - interpolant.evaluate( referenceTime ); - referenceValue = AnimationUtils.arraySlice( interpolant.resultBuffer, startIndex, endIndex ); - - } - - // Conjugate the quaternion - if ( referenceTrackType === 'quaternion' ) { - - const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); - referenceQuat.toArray( referenceValue ); - - } - - // Subtract the reference value from all of the track values - - const numTimes = targetTrack.times.length; - for ( let j = 0; j < numTimes; ++ j ) { - - const valueStart = j * targetValueSize + targetOffset; - - if ( referenceTrackType === 'quaternion' ) { - - // Multiply the conjugate for quaternion track types - Quaternion.multiplyQuaternionsFlat( - targetTrack.values, - valueStart, - referenceValue, - 0, - targetTrack.values, - valueStart - ); - - } else { - - const valueEnd = targetValueSize - targetOffset * 2; - - // Subtract each value for all other numeric track types - for ( let k = 0; k < valueEnd; ++ k ) { - - targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; - - } - - } - - } - - } - - targetClip.blendMode = AdditiveAnimationBlendMode; - - return targetClip; - - } - - }; - - /** - * Abstract base class of interpolants over parametric samples. - * - * The parameter domain is one dimensional, typically the time or a path - * along a curve defined by the data. - * - * The sample values can have any dimensionality and derived classes may - * apply special interpretations to the data. - * - * This class provides the interval seek in a Template Method, deferring - * the actual interpolation to derived classes. - * - * Time complexity is O(1) for linear access crossing at most two points - * and O(log N) for random access, where N is the number of positions. - * - * References: - * - * http://www.oodesign.com/template-method-pattern.html - * - */ - - class Interpolant { - - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - this.parameterPositions = parameterPositions; - this._cachedIndex = 0; - - this.resultBuffer = resultBuffer !== undefined ? - resultBuffer : new sampleValues.constructor( sampleSize ); - this.sampleValues = sampleValues; - this.valueSize = sampleSize; - - this.settings = null; - this.DefaultSettings_ = {}; - - } - - evaluate( t ) { - - const pp = this.parameterPositions; - let i1 = this._cachedIndex, - t1 = pp[ i1 ], - t0 = pp[ i1 - 1 ]; - - validate_interval: { - - seek: { - - let right; - - linear_scan: { - - //- See http://jsperf.com/comparison-to-undefined/3 - //- slower code: - //- - //- if ( t >= t1 || t1 === undefined ) { - forward_scan: if ( ! ( t < t1 ) ) { - - for ( let giveUpAt = i1 + 2; ; ) { - - if ( t1 === undefined ) { - - if ( t < t0 ) break forward_scan; - - // after end - - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t, t0 ); - - } - - if ( i1 === giveUpAt ) break; // this loop - - t0 = t1; - t1 = pp[ ++ i1 ]; - - if ( t < t1 ) { - - // we have arrived at the sought interval - break seek; - - } - - } - - // prepare binary search on the right side of the index - right = pp.length; - break linear_scan; - - } - - //- slower code: - //- if ( t < t0 || t0 === undefined ) { - if ( ! ( t >= t0 ) ) { - - // looping? - - const t1global = pp[ 1 ]; - - if ( t < t1global ) { - - i1 = 2; // + 1, using the scan for the details - t0 = t1global; - - } - - // linear reverse scan - - for ( let giveUpAt = i1 - 2; ; ) { - - if ( t0 === undefined ) { - - // before start - - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); - - } - - if ( i1 === giveUpAt ) break; // this loop - - t1 = t0; - t0 = pp[ -- i1 - 1 ]; - - if ( t >= t0 ) { - - // we have arrived at the sought interval - break seek; - - } - - } - - // prepare binary search on the left side of the index - right = i1; - i1 = 0; - break linear_scan; - - } - - // the interval is valid - - break validate_interval; - - } // linear scan - - // binary search - - while ( i1 < right ) { - - const mid = ( i1 + right ) >>> 1; - - if ( t < pp[ mid ] ) { - - right = mid; - - } else { - - i1 = mid + 1; - - } - - } - - t1 = pp[ i1 ]; - t0 = pp[ i1 - 1 ]; - - // check boundary cases, again - - if ( t0 === undefined ) { - - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); - - } - - if ( t1 === undefined ) { - - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t0, t ); - - } - - } // seek - - this._cachedIndex = i1; - - this.intervalChanged_( i1, t0, t1 ); - - } // validate_interval - - return this.interpolate_( i1, t0, t, t1 ); - - } - - getSettings_() { - - return this.settings || this.DefaultSettings_; - - } - - copySampleValue_( index ) { - - // copies a sample value to the result buffer - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - offset = index * stride; - - for ( let i = 0; i !== stride; ++ i ) { - - result[ i ] = values[ offset + i ]; - - } - - return result; - - } - - // Template methods for derived classes: - - interpolate_( /* i1, t0, t, t1 */ ) { - - throw new Error( 'call to abstract method' ); - // implementations shall return this.resultBuffer - - } - - intervalChanged_( /* i1, t0, t1 */ ) { - - // empty - - } - - } - - // ALIAS DEFINITIONS - - Interpolant.prototype.beforeStart_ = Interpolant.prototype.copySampleValue_; - Interpolant.prototype.afterEnd_ = Interpolant.prototype.copySampleValue_; - - /** - * Fast and simple cubic spline interpolant. - * - * It was derived from a Hermitian construction setting the first derivative - * at each sample position to the linear slope between neighboring positions - * over their parameter interval. - */ - - class CubicInterpolant extends Interpolant { - - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - - this._weightPrev = - 0; - this._offsetPrev = - 0; - this._weightNext = - 0; - this._offsetNext = - 0; - - this.DefaultSettings_ = { - - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - - }; - - } - - intervalChanged_( i1, t0, t1 ) { - - const pp = this.parameterPositions; - let iPrev = i1 - 2, - iNext = i1 + 1, - - tPrev = pp[ iPrev ], - tNext = pp[ iNext ]; - - if ( tPrev === undefined ) { - - switch ( this.getSettings_().endingStart ) { - - case ZeroSlopeEnding: - - // f'(t0) = 0 - iPrev = i1; - tPrev = 2 * t0 - t1; - - break; - - case WrapAroundEnding: - - // use the other end of the curve - iPrev = pp.length - 2; - tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; - - break; - - default: // ZeroCurvatureEnding - - // f''(t0) = 0 a.k.a. Natural Spline - iPrev = i1; - tPrev = t1; - - } - - } - - if ( tNext === undefined ) { - - switch ( this.getSettings_().endingEnd ) { - - case ZeroSlopeEnding: - - // f'(tN) = 0 - iNext = i1; - tNext = 2 * t1 - t0; - - break; - - case WrapAroundEnding: - - // use the other end of the curve - iNext = 1; - tNext = t1 + pp[ 1 ] - pp[ 0 ]; - - break; - - default: // ZeroCurvatureEnding - - // f''(tN) = 0, a.k.a. Natural Spline - iNext = i1 - 1; - tNext = t0; - - } - - } - - const halfDt = ( t1 - t0 ) * 0.5, - stride = this.valueSize; - - this._weightPrev = halfDt / ( t0 - tPrev ); - this._weightNext = halfDt / ( tNext - t1 ); - this._offsetPrev = iPrev * stride; - this._offsetNext = iNext * stride; - - } - - interpolate_( i1, t0, t, t1 ) { - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - o1 = i1 * stride, o0 = o1 - stride, - oP = this._offsetPrev, oN = this._offsetNext, - wP = this._weightPrev, wN = this._weightNext, - - p = ( t - t0 ) / ( t1 - t0 ), - pp = p * p, - ppp = pp * p; - - // evaluate polynomials - - const sP = - wP * ppp + 2 * wP * pp - wP * p; - const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; - const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; - const sN = wN * ppp - wN * pp; - - // combine data linearly - - for ( let i = 0; i !== stride; ++ i ) { - - result[ i ] = - sP * values[ oP + i ] + - s0 * values[ o0 + i ] + - s1 * values[ o1 + i ] + - sN * values[ oN + i ]; - - } - - return result; - - } - - } - - class LinearInterpolant extends Interpolant { - - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - interpolate_( i1, t0, t, t1 ) { - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - offset1 = i1 * stride, - offset0 = offset1 - stride, - - weight1 = ( t - t0 ) / ( t1 - t0 ), - weight0 = 1 - weight1; - - for ( let i = 0; i !== stride; ++ i ) { - - result[ i ] = - values[ offset0 + i ] * weight0 + - values[ offset1 + i ] * weight1; - - } - - return result; - - } - - } - - /** - * - * Interpolant that evaluates to the sample value at the position preceeding - * the parameter. - */ - - class DiscreteInterpolant extends Interpolant { - - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - interpolate_( i1 /*, t0, t, t1 */ ) { - - return this.copySampleValue_( i1 - 1 ); - - } - - } - - class KeyframeTrack { - - constructor( name, times, values, interpolation ) { - - if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); - if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); - - this.name = name; - - this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); - this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); - - this.setInterpolation( interpolation || this.DefaultInterpolation ); - - } - - // Serialization (in static context, because of constructor invocation - // and automatic invocation of .toJSON): - - static toJSON( track ) { - - const trackType = track.constructor; - - let json; - - // derived classes can define a static toJSON method - if ( trackType.toJSON !== this.toJSON ) { - - json = trackType.toJSON( track ); - - } else { - - // by default, we assume the data can be serialized as-is - json = { - - 'name': track.name, - 'times': AnimationUtils.convertArray( track.times, Array ), - 'values': AnimationUtils.convertArray( track.values, Array ) - - }; - - const interpolation = track.getInterpolation(); - - if ( interpolation !== track.DefaultInterpolation ) { - - json.interpolation = interpolation; - - } - - } - - json.type = track.ValueTypeName; // mandatory - - return json; - - } - - InterpolantFactoryMethodDiscrete( result ) { - - return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); - - } - - InterpolantFactoryMethodLinear( result ) { - - return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); - - } - - InterpolantFactoryMethodSmooth( result ) { - - return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); - - } - - setInterpolation( interpolation ) { - - let factoryMethod; - - switch ( interpolation ) { - - case InterpolateDiscrete: - - factoryMethod = this.InterpolantFactoryMethodDiscrete; - - break; - - case InterpolateLinear: - - factoryMethod = this.InterpolantFactoryMethodLinear; - - break; - - case InterpolateSmooth: - - factoryMethod = this.InterpolantFactoryMethodSmooth; - - break; - - } - - if ( factoryMethod === undefined ) { - - const message = 'unsupported interpolation for ' + - this.ValueTypeName + ' keyframe track named ' + this.name; - - if ( this.createInterpolant === undefined ) { - - // fall back to default, unless the default itself is messed up - if ( interpolation !== this.DefaultInterpolation ) { - - this.setInterpolation( this.DefaultInterpolation ); - - } else { - - throw new Error( message ); // fatal, in this case - - } - - } - - console.warn( 'THREE.KeyframeTrack:', message ); - return this; - - } - - this.createInterpolant = factoryMethod; - - return this; - - } - - getInterpolation() { - - switch ( this.createInterpolant ) { - - case this.InterpolantFactoryMethodDiscrete: - - return InterpolateDiscrete; - - case this.InterpolantFactoryMethodLinear: - - return InterpolateLinear; - - case this.InterpolantFactoryMethodSmooth: - - return InterpolateSmooth; - - } - - } - - getValueSize() { - - return this.values.length / this.times.length; - - } - - // move all keyframes either forwards or backwards in time - shift( timeOffset ) { - - if ( timeOffset !== 0.0 ) { - - const times = this.times; - - for ( let i = 0, n = times.length; i !== n; ++ i ) { - - times[ i ] += timeOffset; - - } - - } - - return this; - - } - - // scale all keyframe times by a factor (useful for frame <-> seconds conversions) - scale( timeScale ) { - - if ( timeScale !== 1.0 ) { - - const times = this.times; - - for ( let i = 0, n = times.length; i !== n; ++ i ) { - - times[ i ] *= timeScale; - - } - - } - - return this; - - } - - // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. - // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values - trim( startTime, endTime ) { - - const times = this.times, - nKeys = times.length; - - let from = 0, - to = nKeys - 1; - - while ( from !== nKeys && times[ from ] < startTime ) { - - ++ from; - - } - - while ( to !== - 1 && times[ to ] > endTime ) { - - -- to; - - } - - ++ to; // inclusive -> exclusive bound - - if ( from !== 0 || to !== nKeys ) { - - // empty tracks are forbidden, so keep at least one keyframe - if ( from >= to ) { - - to = Math.max( to, 1 ); - from = to - 1; - - } - - const stride = this.getValueSize(); - this.times = AnimationUtils.arraySlice( times, from, to ); - this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); - - } - - return this; - - } - - // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable - validate() { - - let valid = true; - - const valueSize = this.getValueSize(); - if ( valueSize - Math.floor( valueSize ) !== 0 ) { - - console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); - valid = false; - - } - - const times = this.times, - values = this.values, - - nKeys = times.length; - - if ( nKeys === 0 ) { - - console.error( 'THREE.KeyframeTrack: Track is empty.', this ); - valid = false; - - } - - let prevTime = null; - - for ( let i = 0; i !== nKeys; i ++ ) { - - const currTime = times[ i ]; - - if ( typeof currTime === 'number' && isNaN( currTime ) ) { - - console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); - valid = false; - break; - - } - - if ( prevTime !== null && prevTime > currTime ) { - - console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); - valid = false; - break; - - } - - prevTime = currTime; - - } - - if ( values !== undefined ) { - - if ( AnimationUtils.isTypedArray( values ) ) { - - for ( let i = 0, n = values.length; i !== n; ++ i ) { - - const value = values[ i ]; - - if ( isNaN( value ) ) { - - console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); - valid = false; - break; - - } - - } - - } - - } - - return valid; - - } - - // removes equivalent sequential keys as common in morph target sequences - // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) - optimize() { - - // times or values may be shared with other tracks, so overwriting is unsafe - const times = AnimationUtils.arraySlice( this.times ), - values = AnimationUtils.arraySlice( this.values ), - stride = this.getValueSize(), - - smoothInterpolation = this.getInterpolation() === InterpolateSmooth, - - lastIndex = times.length - 1; - - let writeIndex = 1; - - for ( let i = 1; i < lastIndex; ++ i ) { - - let keep = false; - - const time = times[ i ]; - const timeNext = times[ i + 1 ]; - - // remove adjacent keyframes scheduled at the same time - - if ( time !== timeNext && ( i !== 1 || time !== times[ 0 ] ) ) { - - if ( ! smoothInterpolation ) { - - // remove unnecessary keyframes same as their neighbors - - const offset = i * stride, - offsetP = offset - stride, - offsetN = offset + stride; - - for ( let j = 0; j !== stride; ++ j ) { - - const value = values[ offset + j ]; - - if ( value !== values[ offsetP + j ] || - value !== values[ offsetN + j ] ) { - - keep = true; - break; - - } - - } - - } else { - - keep = true; - - } - - } - - // in-place compaction - - if ( keep ) { - - if ( i !== writeIndex ) { - - times[ writeIndex ] = times[ i ]; - - const readOffset = i * stride, - writeOffset = writeIndex * stride; - - for ( let j = 0; j !== stride; ++ j ) { - - values[ writeOffset + j ] = values[ readOffset + j ]; - - } - - } - - ++ writeIndex; - - } - - } - - // flush last keyframe (compaction looks ahead) - - if ( lastIndex > 0 ) { - - times[ writeIndex ] = times[ lastIndex ]; - - for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { - - values[ writeOffset + j ] = values[ readOffset + j ]; - - } - - ++ writeIndex; - - } - - if ( writeIndex !== times.length ) { - - this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); - this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); - - } else { - - this.times = times; - this.values = values; - - } - - return this; - - } - - clone() { - - const times = AnimationUtils.arraySlice( this.times, 0 ); - const values = AnimationUtils.arraySlice( this.values, 0 ); - - const TypedKeyframeTrack = this.constructor; - const track = new TypedKeyframeTrack( this.name, times, values ); - - // Interpolant argument to constructor is not saved, so copy the factory method directly. - track.createInterpolant = this.createInterpolant; - - return track; - - } - - } - - KeyframeTrack.prototype.TimeBufferType = Float32Array; - KeyframeTrack.prototype.ValueBufferType = Float32Array; - KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; - - /** - * A Track of Boolean keyframe values. - */ - class BooleanKeyframeTrack extends KeyframeTrack {} - - BooleanKeyframeTrack.prototype.ValueTypeName = 'bool'; - BooleanKeyframeTrack.prototype.ValueBufferType = Array; - BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; - BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; - BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - - /** - * A Track of keyframe values that represent color. - */ - class ColorKeyframeTrack extends KeyframeTrack {} - - ColorKeyframeTrack.prototype.ValueTypeName = 'color'; - - /** - * A Track of numeric keyframe values. - */ - class NumberKeyframeTrack extends KeyframeTrack {} - - NumberKeyframeTrack.prototype.ValueTypeName = 'number'; - - /** - * Spherical linear unit quaternion interpolant. - */ - - class QuaternionLinearInterpolant extends Interpolant { - - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - interpolate_( i1, t0, t, t1 ) { - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - alpha = ( t - t0 ) / ( t1 - t0 ); - - let offset = i1 * stride; - - for ( let end = offset + stride; offset !== end; offset += 4 ) { - - Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); - - } - - return result; - - } - - } - - /** - * A Track of quaternion keyframe values. - */ - class QuaternionKeyframeTrack extends KeyframeTrack { - - InterpolantFactoryMethodLinear( result ) { - - return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); - - } - - } - - QuaternionKeyframeTrack.prototype.ValueTypeName = 'quaternion'; - // ValueBufferType is inherited - QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; - QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - - /** - * A Track that interpolates Strings - */ - class StringKeyframeTrack extends KeyframeTrack {} - - StringKeyframeTrack.prototype.ValueTypeName = 'string'; - StringKeyframeTrack.prototype.ValueBufferType = Array; - StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; - StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; - StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - - /** - * A Track of vectored keyframe values. - */ - class VectorKeyframeTrack extends KeyframeTrack {} - - VectorKeyframeTrack.prototype.ValueTypeName = 'vector'; - - class AnimationClip { - - constructor( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) { - - this.name = name; - this.tracks = tracks; - this.duration = duration; - this.blendMode = blendMode; - - this.uuid = generateUUID(); - - // this means it should figure out its duration by scanning the tracks - if ( this.duration < 0 ) { - - this.resetDuration(); - - } - - } - - - static parse( json ) { - - const tracks = [], - jsonTracks = json.tracks, - frameTime = 1.0 / ( json.fps || 1.0 ); - - for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { - - tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); - - } - - const clip = new this( json.name, json.duration, tracks, json.blendMode ); - clip.uuid = json.uuid; - - return clip; - - } - - static toJSON( clip ) { - - const tracks = [], - clipTracks = clip.tracks; - - const json = { - - 'name': clip.name, - 'duration': clip.duration, - 'tracks': tracks, - 'uuid': clip.uuid, - 'blendMode': clip.blendMode - - }; - - for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { - - tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); - - } - - return json; - - } - - static CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) { - - const numMorphTargets = morphTargetSequence.length; - const tracks = []; - - for ( let i = 0; i < numMorphTargets; i ++ ) { - - let times = []; - let values = []; - - times.push( - ( i + numMorphTargets - 1 ) % numMorphTargets, - i, - ( i + 1 ) % numMorphTargets ); - - values.push( 0, 1, 0 ); - - const order = AnimationUtils.getKeyframeOrder( times ); - times = AnimationUtils.sortedArray( times, 1, order ); - values = AnimationUtils.sortedArray( values, 1, order ); - - // if there is a key at the first frame, duplicate it as the - // last frame as well for perfect loop. - if ( ! noLoop && times[ 0 ] === 0 ) { - - times.push( numMorphTargets ); - values.push( values[ 0 ] ); - - } - - tracks.push( - new NumberKeyframeTrack( - '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', - times, values - ).scale( 1.0 / fps ) ); - - } - - return new this( name, - 1, tracks ); - - } - - static findByName( objectOrClipArray, name ) { - - let clipArray = objectOrClipArray; - - if ( ! Array.isArray( objectOrClipArray ) ) { - - const o = objectOrClipArray; - clipArray = o.geometry && o.geometry.animations || o.animations; - - } - - for ( let i = 0; i < clipArray.length; i ++ ) { - - if ( clipArray[ i ].name === name ) { - - return clipArray[ i ]; - - } - - } - - return null; - - } - - static CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) { - - const animationToMorphTargets = {}; - - // tested with https://regex101.com/ on trick sequences - // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 - const pattern = /^([\w-]*?)([\d]+)$/; - - // sort morph target names into animation groups based - // patterns like Walk_001, Walk_002, Run_001, Run_002 - for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { - - const morphTarget = morphTargets[ i ]; - const parts = morphTarget.name.match( pattern ); - - if ( parts && parts.length > 1 ) { - - const name = parts[ 1 ]; - - let animationMorphTargets = animationToMorphTargets[ name ]; - - if ( ! animationMorphTargets ) { - - animationToMorphTargets[ name ] = animationMorphTargets = []; - - } - - animationMorphTargets.push( morphTarget ); - - } - - } - - const clips = []; - - for ( const name in animationToMorphTargets ) { - - clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); - - } - - return clips; - - } - - // parse the animation.hierarchy format - static parseAnimation( animation, bones ) { - - if ( ! animation ) { - - console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); - return null; - - } - - const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { - - // only return track if there are actually keys. - if ( animationKeys.length !== 0 ) { - - const times = []; - const values = []; - - AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); - - // empty keys are filtered out, so check again - if ( times.length !== 0 ) { - - destTracks.push( new trackType( trackName, times, values ) ); - - } - - } - - }; - - const tracks = []; - - const clipName = animation.name || 'default'; - const fps = animation.fps || 30; - const blendMode = animation.blendMode; - - // automatic length determination in AnimationClip. - let duration = animation.length || - 1; - - const hierarchyTracks = animation.hierarchy || []; - - for ( let h = 0; h < hierarchyTracks.length; h ++ ) { - - const animationKeys = hierarchyTracks[ h ].keys; - - // skip empty tracks - if ( ! animationKeys || animationKeys.length === 0 ) continue; - - // process morph targets - if ( animationKeys[ 0 ].morphTargets ) { - - // figure out all morph targets used in this track - const morphTargetNames = {}; - - let k; - - for ( k = 0; k < animationKeys.length; k ++ ) { - - if ( animationKeys[ k ].morphTargets ) { - - for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { - - morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; - - } - - } - - } - - // create a track for each morph target with all zero - // morphTargetInfluences except for the keys in which - // the morphTarget is named. - for ( const morphTargetName in morphTargetNames ) { - - const times = []; - const values = []; - - for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { - - const animationKey = animationKeys[ k ]; - - times.push( animationKey.time ); - values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); - - } - - tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); - - } - - duration = morphTargetNames.length * ( fps || 1.0 ); - - } else { - - // ...assume skeletal animation - - const boneName = '.bones[' + bones[ h ].name + ']'; - - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.position', - animationKeys, 'pos', tracks ); - - addNonemptyTrack( - QuaternionKeyframeTrack, boneName + '.quaternion', - animationKeys, 'rot', tracks ); - - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.scale', - animationKeys, 'scl', tracks ); - - } - - } - - if ( tracks.length === 0 ) { - - return null; - - } - - const clip = new this( clipName, duration, tracks, blendMode ); - - return clip; - - } - - resetDuration() { - - const tracks = this.tracks; - let duration = 0; - - for ( let i = 0, n = tracks.length; i !== n; ++ i ) { - - const track = this.tracks[ i ]; - - duration = Math.max( duration, track.times[ track.times.length - 1 ] ); - - } - - this.duration = duration; - - return this; - - } - - trim() { - - for ( let i = 0; i < this.tracks.length; i ++ ) { - - this.tracks[ i ].trim( 0, this.duration ); - - } - - return this; - - } - - validate() { - - let valid = true; - - for ( let i = 0; i < this.tracks.length; i ++ ) { - - valid = valid && this.tracks[ i ].validate(); - - } - - return valid; - - } - - optimize() { - - for ( let i = 0; i < this.tracks.length; i ++ ) { - - this.tracks[ i ].optimize(); - - } - - return this; - - } - - clone() { - - const tracks = []; - - for ( let i = 0; i < this.tracks.length; i ++ ) { - - tracks.push( this.tracks[ i ].clone() ); - - } - - return new this.constructor( this.name, this.duration, tracks, this.blendMode ); - - } - - toJSON() { - - return this.constructor.toJSON( this ); - - } - - } - - function getTrackTypeForValueTypeName( typeName ) { - - switch ( typeName.toLowerCase() ) { - - case 'scalar': - case 'double': - case 'float': - case 'number': - case 'integer': - - return NumberKeyframeTrack; - - case 'vector': - case 'vector2': - case 'vector3': - case 'vector4': - - return VectorKeyframeTrack; - - case 'color': - - return ColorKeyframeTrack; - - case 'quaternion': - - return QuaternionKeyframeTrack; - - case 'bool': - case 'boolean': - - return BooleanKeyframeTrack; - - case 'string': - - return StringKeyframeTrack; - - } - - throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); - - } - - function parseKeyframeTrack( json ) { - - if ( json.type === undefined ) { - - throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); - - } - - const trackType = getTrackTypeForValueTypeName( json.type ); - - if ( json.times === undefined ) { - - const times = [], values = []; - - AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); - - json.times = times; - json.values = values; - - } - - // derived classes can define a static parse method - if ( trackType.parse !== undefined ) { - - return trackType.parse( json ); - - } else { - - // by default, we assume a constructor compatible with the base - return new trackType( json.name, json.times, json.values, json.interpolation ); - - } - - } - - const Cache = { - - enabled: false, - - files: {}, - - add: function ( key, file ) { - - if ( this.enabled === false ) return; - - // console.log( 'THREE.Cache', 'Adding key:', key ); - - this.files[ key ] = file; - - }, - - get: function ( key ) { - - if ( this.enabled === false ) return; - - // console.log( 'THREE.Cache', 'Checking key:', key ); - - return this.files[ key ]; - - }, - - remove: function ( key ) { - - delete this.files[ key ]; - - }, - - clear: function () { - - this.files = {}; - - } - - }; - - class LoadingManager { - - constructor( onLoad, onProgress, onError ) { - - const scope = this; - - let isLoading = false; - let itemsLoaded = 0; - let itemsTotal = 0; - let urlModifier = undefined; - const handlers = []; - - // Refer to #5689 for the reason why we don't set .onStart - // in the constructor - - this.onStart = undefined; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; - - this.itemStart = function ( url ) { - - itemsTotal ++; - - if ( isLoading === false ) { - - if ( scope.onStart !== undefined ) { - - scope.onStart( url, itemsLoaded, itemsTotal ); - - } - - } - - isLoading = true; - - }; - - this.itemEnd = function ( url ) { - - itemsLoaded ++; - - if ( scope.onProgress !== undefined ) { - - scope.onProgress( url, itemsLoaded, itemsTotal ); - - } - - if ( itemsLoaded === itemsTotal ) { - - isLoading = false; - - if ( scope.onLoad !== undefined ) { - - scope.onLoad(); - - } - - } - - }; - - this.itemError = function ( url ) { - - if ( scope.onError !== undefined ) { - - scope.onError( url ); - - } - - }; - - this.resolveURL = function ( url ) { - - if ( urlModifier ) { - - return urlModifier( url ); - - } - - return url; - - }; - - this.setURLModifier = function ( transform ) { - - urlModifier = transform; - - return this; - - }; - - this.addHandler = function ( regex, loader ) { - - handlers.push( regex, loader ); - - return this; - - }; - - this.removeHandler = function ( regex ) { - - const index = handlers.indexOf( regex ); - - if ( index !== - 1 ) { - - handlers.splice( index, 2 ); - - } - - return this; - - }; - - this.getHandler = function ( file ) { - - for ( let i = 0, l = handlers.length; i < l; i += 2 ) { - - const regex = handlers[ i ]; - const loader = handlers[ i + 1 ]; - - if ( regex.global ) regex.lastIndex = 0; // see #17920 - - if ( regex.test( file ) ) { - - return loader; - - } - - } - - return null; - - }; - - } - - } - - const DefaultLoadingManager = new LoadingManager(); - - class Loader { - - constructor( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - this.crossOrigin = 'anonymous'; - this.withCredentials = false; - this.path = ''; - this.resourcePath = ''; - this.requestHeader = {}; - - } - - load( /* url, onLoad, onProgress, onError */ ) {} - - loadAsync( url, onProgress ) { - - const scope = this; - - return new Promise( function ( resolve, reject ) { - - scope.load( url, resolve, onProgress, reject ); - - } ); - - } - - parse( /* data */ ) {} - - setCrossOrigin( crossOrigin ) { - - this.crossOrigin = crossOrigin; - return this; - - } - - setWithCredentials( value ) { - - this.withCredentials = value; - return this; - - } - - setPath( path ) { - - this.path = path; - return this; - - } - - setResourcePath( resourcePath ) { - - this.resourcePath = resourcePath; - return this; - - } - - setRequestHeader( requestHeader ) { - - this.requestHeader = requestHeader; - return this; - - } - - } - - const loading = {}; - - class FileLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - } - - load( url, onLoad, onProgress, onError ) { - - if ( url === undefined ) url = ''; - - if ( this.path !== undefined ) url = this.path + url; - - url = this.manager.resolveURL( url ); - - const scope = this; - - const cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) onLoad( cached ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - // Check if request is duplicate - - if ( loading[ url ] !== undefined ) { - - loading[ url ].push( { - - onLoad: onLoad, - onProgress: onProgress, - onError: onError - - } ); - - return; - - } - - // Check for data: URI - const dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; - const dataUriRegexResult = url.match( dataUriRegex ); - let request; - - // Safari can not handle Data URIs through XMLHttpRequest so process manually - if ( dataUriRegexResult ) { - - const mimeType = dataUriRegexResult[ 1 ]; - const isBase64 = !! dataUriRegexResult[ 2 ]; - - let data = dataUriRegexResult[ 3 ]; - data = decodeURIComponent( data ); - - if ( isBase64 ) data = atob( data ); - - try { - - let response; - const responseType = ( this.responseType || '' ).toLowerCase(); - - switch ( responseType ) { - - case 'arraybuffer': - case 'blob': - - const view = new Uint8Array( data.length ); - - for ( let i = 0; i < data.length; i ++ ) { - - view[ i ] = data.charCodeAt( i ); - - } - - if ( responseType === 'blob' ) { - - response = new Blob( [ view.buffer ], { type: mimeType } ); - - } else { - - response = view.buffer; - - } - - break; - - case 'document': - - const parser = new DOMParser(); - response = parser.parseFromString( data, mimeType ); - - break; - - case 'json': - - response = JSON.parse( data ); - - break; - - default: // 'text' or other - - response = data; - - break; - - } - - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { - - if ( onLoad ) onLoad( response ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - } catch ( error ) { - - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { - - if ( onError ) onError( error ); - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, 0 ); - - } - - } else { - - // Initialise array for duplicate requests - - loading[ url ] = []; - - loading[ url ].push( { - - onLoad: onLoad, - onProgress: onProgress, - onError: onError - - } ); - - request = new XMLHttpRequest(); - - request.open( 'GET', url, true ); - - request.addEventListener( 'load', function ( event ) { - - const response = this.response; - - const callbacks = loading[ url ]; - - delete loading[ url ]; - - if ( this.status === 200 || this.status === 0 ) { - - // Some browsers return HTTP Status 0 when using non-http protocol - // e.g. 'file://' or 'data://'. Handle as success. - - if ( this.status === 0 ) console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); - - // Add to cache only on HTTP success, so that we do not cache - // error response bodies as proper responses to requests. - Cache.add( url, response ); - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onLoad ) callback.onLoad( response ); - - } - - scope.manager.itemEnd( url ); - - } else { - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } - - }, false ); - - request.addEventListener( 'progress', function ( event ) { - - const callbacks = loading[ url ]; - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onProgress ) callback.onProgress( event ); - - } - - }, false ); - - request.addEventListener( 'error', function ( event ) { - - const callbacks = loading[ url ]; - - delete loading[ url ]; - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, false ); - - request.addEventListener( 'abort', function ( event ) { - - const callbacks = loading[ url ]; - - delete loading[ url ]; - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, false ); - - if ( this.responseType !== undefined ) request.responseType = this.responseType; - if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; - - if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); - - for ( const header in this.requestHeader ) { - - request.setRequestHeader( header, this.requestHeader[ header ] ); - - } - - request.send( null ); - - } - - scope.manager.itemStart( url ); - - return request; - - } - - setResponseType( value ) { - - this.responseType = value; - return this; - - } - - setMimeType( value ) { - - this.mimeType = value; - return this; - - } - - } - - class ImageLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - } - - load( url, onLoad, onProgress, onError ) { - - if ( this.path !== undefined ) url = this.path + url; - - url = this.manager.resolveURL( url ); - - const scope = this; - - const cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) onLoad( cached ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - const image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); - - function onImageLoad() { - - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); - - Cache.add( url, this ); - - if ( onLoad ) onLoad( this ); - - scope.manager.itemEnd( url ); - - } - - function onImageError( event ) { - - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); - - if ( onError ) onError( event ); - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } - - image.addEventListener( 'load', onImageLoad, false ); - image.addEventListener( 'error', onImageError, false ); - - if ( url.substr( 0, 5 ) !== 'data:' ) { - - if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; - - } - - scope.manager.itemStart( url ); - - image.src = url; - - return image; - - } - - } - - class CubeTextureLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - } - - load( urls, onLoad, onProgress, onError ) { - - const texture = new CubeTexture(); - - const loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); - - let loaded = 0; - - function loadTexture( i ) { - - loader.load( urls[ i ], function ( image ) { - - texture.images[ i ] = image; - - loaded ++; - - if ( loaded === 6 ) { - - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - } - - }, undefined, onError ); - - } - - for ( let i = 0; i < urls.length; ++ i ) { - - loadTexture( i ); - - } - - return texture; - - } - - } - - class TextureLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - } - - load( url, onLoad, onProgress, onError ) { - - const texture = new Texture(); - - const loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); - - loader.load( url, function ( image ) { - - texture.image = image; - - // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. - const isJPEG = url.search( /\.jpe?g($|\?)/i ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; - - texture.format = isJPEG ? RGBFormat : RGBAFormat; - texture.needsUpdate = true; - - if ( onLoad !== undefined ) { - - onLoad( texture ); - - } - - }, onProgress, onError ); - - return texture; - - } - - } - - /************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ - - class CurvePath extends Curve { - - constructor() { - - super(); - - this.type = 'CurvePath'; - - this.curves = []; - this.autoClose = false; // Automatically closes the path - - } - - add( curve ) { - - this.curves.push( curve ); - - } - - closePath() { - - // Add a line curve if start and end of lines are not connected - const startPoint = this.curves[ 0 ].getPoint( 0 ); - const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); - - if ( ! startPoint.equals( endPoint ) ) { - - this.curves.push( new LineCurve( endPoint, startPoint ) ); - - } - - } - - // To get accurate point with reference to - // entire path distance at time t, - // following has to be done: - - // 1. Length of each sub path have to be known - // 2. Locate and identify type of curve - // 3. Get t for the curve - // 4. Return curve.getPointAt(t') - - getPoint( t ) { - - const d = t * this.getLength(); - const curveLengths = this.getCurveLengths(); - let i = 0; - - // To think about boundaries points. - - while ( i < curveLengths.length ) { - - if ( curveLengths[ i ] >= d ) { - - const diff = curveLengths[ i ] - d; - const curve = this.curves[ i ]; - - const segmentLength = curve.getLength(); - const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; - - return curve.getPointAt( u ); - - } - - i ++; - - } - - return null; - - // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { - - points.push( points[ 0 ] ); - - } - - return points; - - } - - copy( source ) { - - super.copy( source ); - - this.curves = []; - - for ( let i = 0, l = source.curves.length; i < l; i ++ ) { - - const curve = source.curves[ i ]; - - this.curves.push( curve.clone() ); - - } - - this.autoClose = source.autoClose; - - return this; - - } - - toJSON() { - - const data = super.toJSON(); - - data.autoClose = this.autoClose; - data.curves = []; - - for ( let i = 0, l = this.curves.length; i < l; i ++ ) { - - const curve = this.curves[ i ]; - data.curves.push( curve.toJSON() ); - - } - - return data; - - } - - fromJSON( json ) { - - super.fromJSON( json ); - - this.autoClose = json.autoClose; - this.curves = []; - - for ( let i = 0, l = json.curves.length; i < l; i ++ ) { - - const curve = json.curves[ i ]; - this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); - - } - - return this; - - } - - } - - class Path extends CurvePath { - - constructor( points ) { - - super(); - this.type = 'Path'; - - this.currentPoint = new Vector2(); - - if ( points ) { - - this.setFromPoints( points ); - - } - - } - - setFromPoints( points ) { - - this.moveTo( points[ 0 ].x, points[ 0 ].y ); - - for ( let i = 1, l = points.length; i < l; i ++ ) { - - this.lineTo( points[ i ].x, points[ i ].y ); - - } - - return this; - - } - - moveTo( x, y ) { - - this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? - - return this; - - } - - lineTo( x, y ) { - - const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); - this.curves.push( curve ); - - this.currentPoint.set( x, y ); - - return this; - - } - - quadraticCurveTo( aCPx, aCPy, aX, aY ) { - - const curve = new QuadraticBezierCurve( - this.currentPoint.clone(), - new Vector2( aCPx, aCPy ), - new Vector2( aX, aY ) - ); - - this.curves.push( curve ); - - this.currentPoint.set( aX, aY ); - - return this; - - } - - bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - - const curve = new CubicBezierCurve( - this.currentPoint.clone(), - new Vector2( aCP1x, aCP1y ), - new Vector2( aCP2x, aCP2y ), - new Vector2( aX, aY ) - ); - - this.curves.push( curve ); - - this.currentPoint.set( aX, aY ); - - return this; - - } - - splineThru( pts /*Array of Vector*/ ) { - - const npts = [ this.currentPoint.clone() ].concat( pts ); - - const curve = new SplineCurve( npts ); - this.curves.push( curve ); - - this.currentPoint.copy( pts[ pts.length - 1 ] ); - - return this; - - } - - arc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - const x0 = this.currentPoint.x; - const y0 = this.currentPoint.y; - - this.absarc( aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); - - return this; - - } - - absarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - - return this; - - } - - ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - const x0 = this.currentPoint.x; - const y0 = this.currentPoint.y; - - this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - - return this; - - } - - absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - - if ( this.curves.length > 0 ) { - - // if a previous curve is present, attempt to join - const firstPoint = curve.getPoint( 0 ); - - if ( ! firstPoint.equals( this.currentPoint ) ) { - - this.lineTo( firstPoint.x, firstPoint.y ); - - } - - } - - this.curves.push( curve ); - - const lastPoint = curve.getPoint( 1 ); - this.currentPoint.copy( lastPoint ); - - return this; - - } - - copy( source ) { - - super.copy( source ); - - this.currentPoint.copy( source.currentPoint ); - - return this; - - } - - toJSON() { - - const data = super.toJSON(); - - data.currentPoint = this.currentPoint.toArray(); - - return data; - - } - - fromJSON( json ) { - - super.fromJSON( json ); - - this.currentPoint.fromArray( json.currentPoint ); - - return this; - - } - - } - - class Shape extends Path { - - constructor( points ) { - - super( points ); - - this.uuid = generateUUID(); - - this.type = 'Shape'; - - this.holes = []; - - } - - getPointsHoles( divisions ) { - - const holesPts = []; - - for ( let i = 0, l = this.holes.length; i < l; i ++ ) { - - holesPts[ i ] = this.holes[ i ].getPoints( divisions ); - - } - - return holesPts; - - } - - // get points of shape and holes (keypoints based on segments parameter) - - extractPoints( divisions ) { - - return { - - shape: this.getPoints( divisions ), - holes: this.getPointsHoles( divisions ) - - }; - - } - - copy( source ) { - - super.copy( source ); - - this.holes = []; - - for ( let i = 0, l = source.holes.length; i < l; i ++ ) { - - const hole = source.holes[ i ]; - - this.holes.push( hole.clone() ); - - } - - return this; - - } - - toJSON() { - - const data = super.toJSON(); - - data.uuid = this.uuid; - data.holes = []; - - for ( let i = 0, l = this.holes.length; i < l; i ++ ) { - - const hole = this.holes[ i ]; - data.holes.push( hole.toJSON() ); - - } - - return data; - - } - - fromJSON( json ) { - - super.fromJSON( json ); - - this.uuid = json.uuid; - this.holes = []; - - for ( let i = 0, l = json.holes.length; i < l; i ++ ) { - - const hole = json.holes[ i ]; - this.holes.push( new Path().fromJSON( hole ) ); - - } - - return this; - - } - - } - - class Light extends Object3D { - - constructor( color, intensity = 1 ) { - - super(); - - this.type = 'Light'; - - this.color = new Color( color ); - this.intensity = intensity; - - } - - dispose() { - - // Empty here in base class; some subclasses override. - - } - - copy( source ) { - - super.copy( source ); - - this.color.copy( source.color ); - this.intensity = source.intensity; - - return this; - - } - - toJSON( meta ) { - - const data = super.toJSON( meta ); - - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; - - if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); - - if ( this.distance !== undefined ) data.object.distance = this.distance; - if ( this.angle !== undefined ) data.object.angle = this.angle; - if ( this.decay !== undefined ) data.object.decay = this.decay; - if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; - - if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); - - return data; - - } - - } - - Light.prototype.isLight = true; - - class HemisphereLight extends Light { - - constructor( skyColor, groundColor, intensity ) { - - super( skyColor, intensity ); - - this.type = 'HemisphereLight'; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.groundColor = new Color( groundColor ); - - } - - copy( source ) { - - Light.prototype.copy.call( this, source ); - - this.groundColor.copy( source.groundColor ); - - return this; - - } - - } - - HemisphereLight.prototype.isHemisphereLight = true; - - const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); - const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); - const _lookTarget$1 = /*@__PURE__*/ new Vector3(); - - class LightShadow { - - constructor( camera ) { - - this.camera = camera; - - this.bias = 0; - this.normalBias = 0; - this.radius = 1; - this.blurSamples = 8; - - this.mapSize = new Vector2( 512, 512 ); - - this.map = null; - this.mapPass = null; - this.matrix = new Matrix4(); - - this.autoUpdate = true; - this.needsUpdate = false; - - this._frustum = new Frustum(); - this._frameExtents = new Vector2( 1, 1 ); - - this._viewportCount = 1; - - this._viewports = [ - - new Vector4( 0, 0, 1, 1 ) - - ]; - - } - - getViewportCount() { - - return this._viewportCount; - - } - - getFrustum() { - - return this._frustum; - - } - - updateMatrices( light ) { - - const shadowCamera = this.camera; - const shadowMatrix = this.matrix; - - _lightPositionWorld$1.setFromMatrixPosition( light.matrixWorld ); - shadowCamera.position.copy( _lightPositionWorld$1 ); - - _lookTarget$1.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( _lookTarget$1 ); - shadowCamera.updateMatrixWorld(); - - _projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( _projScreenMatrix$1 ); - - shadowMatrix.set( - 0.5, 0.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0 - ); - - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); - - } - - getViewport( viewportIndex ) { - - return this._viewports[ viewportIndex ]; - - } - - getFrameExtents() { - - return this._frameExtents; - - } - - dispose() { - - if ( this.map ) { - - this.map.dispose(); - - } - - if ( this.mapPass ) { - - this.mapPass.dispose(); - - } - - } - - copy( source ) { - - this.camera = source.camera.clone(); - - this.bias = source.bias; - this.radius = source.radius; - - this.mapSize.copy( source.mapSize ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - toJSON() { - - const object = {}; - - if ( this.bias !== 0 ) object.bias = this.bias; - if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; - if ( this.radius !== 1 ) object.radius = this.radius; - if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); - - object.camera = this.camera.toJSON( false ).object; - delete object.camera.matrix; - - return object; - - } - - } - - class SpotLightShadow extends LightShadow { - - constructor() { - - super( new PerspectiveCamera( 50, 1, 0.5, 500 ) ); - - this.focus = 1; - - } - - updateMatrices( light ) { - - const camera = this.camera; - - const fov = RAD2DEG * 2 * light.angle * this.focus; - const aspect = this.mapSize.width / this.mapSize.height; - const far = light.distance || camera.far; - - if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { - - camera.fov = fov; - camera.aspect = aspect; - camera.far = far; - camera.updateProjectionMatrix(); - - } - - super.updateMatrices( light ); - - } - - copy( source ) { - - super.copy( source ); - - this.focus = source.focus; - - return this; - - } - - } - - SpotLightShadow.prototype.isSpotLightShadow = true; - - class SpotLight extends Light { - - constructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 1 ) { - - super( color, intensity ); - - this.type = 'SpotLight'; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.target = new Object3D(); - - this.distance = distance; - this.angle = angle; - this.penumbra = penumbra; - this.decay = decay; // for physically correct lights, should be 2. - - this.shadow = new SpotLightShadow(); - - } - - get power() { - - // compute the light's luminous power (in lumens) from its intensity (in candela) - // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) - return this.intensity * Math.PI; - - } - - set power( power ) { - - // set the light's intensity (in candela) from the desired luminous power (in lumens) - this.intensity = power / Math.PI; - - } - - dispose() { - - this.shadow.dispose(); - - } - - copy( source ) { - - super.copy( source ); - - this.distance = source.distance; - this.angle = source.angle; - this.penumbra = source.penumbra; - this.decay = source.decay; - - this.target = source.target.clone(); - - this.shadow = source.shadow.clone(); - - return this; - - } - - } - - SpotLight.prototype.isSpotLight = true; - - const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); - const _lightPositionWorld = /*@__PURE__*/ new Vector3(); - const _lookTarget = /*@__PURE__*/ new Vector3(); - - class PointLightShadow extends LightShadow { - - constructor() { - - super( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); - - this._frameExtents = new Vector2( 4, 2 ); - - this._viewportCount = 6; - - this._viewports = [ - // These viewports map a cube-map onto a 2D texture with the - // following orientation: - // - // xzXZ - // y Y - // - // X - Positive x direction - // x - Negative x direction - // Y - Positive y direction - // y - Negative y direction - // Z - Positive z direction - // z - Negative z direction - - // positive X - new Vector4( 2, 1, 1, 1 ), - // negative X - new Vector4( 0, 1, 1, 1 ), - // positive Z - new Vector4( 3, 1, 1, 1 ), - // negative Z - new Vector4( 1, 1, 1, 1 ), - // positive Y - new Vector4( 3, 0, 1, 1 ), - // negative Y - new Vector4( 1, 0, 1, 1 ) - ]; - - this._cubeDirections = [ - new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), - new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) - ]; - - this._cubeUps = [ - new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), - new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) - ]; - - } - - updateMatrices( light, viewportIndex = 0 ) { - - const camera = this.camera; - const shadowMatrix = this.matrix; - - const far = light.distance || camera.far; - - if ( far !== camera.far ) { - - camera.far = far; - camera.updateProjectionMatrix(); - - } - - _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - camera.position.copy( _lightPositionWorld ); - - _lookTarget.copy( camera.position ); - _lookTarget.add( this._cubeDirections[ viewportIndex ] ); - camera.up.copy( this._cubeUps[ viewportIndex ] ); - camera.lookAt( _lookTarget ); - camera.updateMatrixWorld(); - - shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); - - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( _projScreenMatrix ); - - } - - } - - PointLightShadow.prototype.isPointLightShadow = true; - - class PointLight extends Light { - - constructor( color, intensity, distance = 0, decay = 1 ) { - - super( color, intensity ); - - this.type = 'PointLight'; - - this.distance = distance; - this.decay = decay; // for physically correct lights, should be 2. - - this.shadow = new PointLightShadow(); - - } - - get power() { - - // compute the light's luminous power (in lumens) from its intensity (in candela) - // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) - return this.intensity * 4 * Math.PI; - - } - - set power( power ) { - - // set the light's intensity (in candela) from the desired luminous power (in lumens) - this.intensity = power / ( 4 * Math.PI ); - - } - - dispose() { - - this.shadow.dispose(); - - } - - copy( source ) { - - super.copy( source ); - - this.distance = source.distance; - this.decay = source.decay; - - this.shadow = source.shadow.clone(); - - return this; - - } - - } - - PointLight.prototype.isPointLight = true; - - class DirectionalLightShadow extends LightShadow { - - constructor() { - - super( new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); - - } - - } - - DirectionalLightShadow.prototype.isDirectionalLightShadow = true; - - class DirectionalLight extends Light { - - constructor( color, intensity ) { - - super( color, intensity ); - - this.type = 'DirectionalLight'; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.target = new Object3D(); - - this.shadow = new DirectionalLightShadow(); - - } - - dispose() { - - this.shadow.dispose(); - - } - - copy( source ) { - - super.copy( source ); - - this.target = source.target.clone(); - this.shadow = source.shadow.clone(); - - return this; - - } - - } - - DirectionalLight.prototype.isDirectionalLight = true; - - class AmbientLight extends Light { - - constructor( color, intensity ) { - - super( color, intensity ); - - this.type = 'AmbientLight'; - - } - - } - - AmbientLight.prototype.isAmbientLight = true; - - class RectAreaLight extends Light { - - constructor( color, intensity, width = 10, height = 10 ) { - - super( color, intensity ); - - this.type = 'RectAreaLight'; - - this.width = width; - this.height = height; - - } - - get power() { - - // compute the light's luminous power (in lumens) from its intensity (in nits) - return this.intensity * this.width * this.height * Math.PI; - - } - - set power( power ) { - - // set the light's intensity (in nits) from the desired luminous power (in lumens) - this.intensity = power / ( this.width * this.height * Math.PI ); - - } - - copy( source ) { - - super.copy( source ); - - this.width = source.width; - this.height = source.height; - - return this; - - } - - toJSON( meta ) { - - const data = super.toJSON( meta ); - - data.object.width = this.width; - data.object.height = this.height; - - return data; - - } - - } - - RectAreaLight.prototype.isRectAreaLight = true; - - /** - * Primary reference: - * https://graphics.stanford.edu/papers/envmap/envmap.pdf - * - * Secondary reference: - * https://www.ppsloan.org/publications/StupidSH36.pdf - */ - - // 3-band SH defined by 9 coefficients - - class SphericalHarmonics3 { - - constructor() { - - this.coefficients = []; - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients.push( new Vector3() ); - - } - - } - - set( coefficients ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].copy( coefficients[ i ] ); - - } - - return this; - - } - - zero() { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].set( 0, 0, 0 ); - - } - - return this; - - } - - // get the radiance in the direction of the normal - // target is a Vector3 - getAt( normal, target ) { - - // normal is assumed to be unit length - - const x = normal.x, y = normal.y, z = normal.z; - - const coeff = this.coefficients; - - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); - - // band 1 - target.addScaledVector( coeff[ 1 ], 0.488603 * y ); - target.addScaledVector( coeff[ 2 ], 0.488603 * z ); - target.addScaledVector( coeff[ 3 ], 0.488603 * x ); - - // band 2 - target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); - target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); - target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); - target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); - target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); - - return target; - - } - - // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal - // target is a Vector3 - // https://graphics.stanford.edu/papers/envmap/envmap.pdf - getIrradianceAt( normal, target ) { - - // normal is assumed to be unit length - - const x = normal.x, y = normal.y, z = normal.z; - - const coeff = this.coefficients; - - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 - - // band 1 - target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 - target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); - target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); - - // band 2 - target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 - target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); - target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 - target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); - target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 - - return target; - - } - - add( sh ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].add( sh.coefficients[ i ] ); - - } - - return this; - - } - - addScaledSH( sh, s ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); - - } - - return this; - - } - - scale( s ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].multiplyScalar( s ); - - } - - return this; - - } - - lerp( sh, alpha ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); - - } - - return this; - - } - - equals( sh ) { - - for ( let i = 0; i < 9; i ++ ) { - - if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { - - return false; - - } - - } - - return true; - - } - - copy( sh ) { - - return this.set( sh.coefficients ); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - fromArray( array, offset = 0 ) { - - const coefficients = this.coefficients; - - for ( let i = 0; i < 9; i ++ ) { - - coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); - - } - - return this; - - } - - toArray( array = [], offset = 0 ) { - - const coefficients = this.coefficients; - - for ( let i = 0; i < 9; i ++ ) { - - coefficients[ i ].toArray( array, offset + ( i * 3 ) ); - - } - - return array; - - } - - // evaluate the basis functions - // shBasis is an Array[ 9 ] - static getBasisAt( normal, shBasis ) { - - // normal is assumed to be unit length - - const x = normal.x, y = normal.y, z = normal.z; - - // band 0 - shBasis[ 0 ] = 0.282095; - - // band 1 - shBasis[ 1 ] = 0.488603 * y; - shBasis[ 2 ] = 0.488603 * z; - shBasis[ 3 ] = 0.488603 * x; - - // band 2 - shBasis[ 4 ] = 1.092548 * x * y; - shBasis[ 5 ] = 1.092548 * y * z; - shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); - shBasis[ 7 ] = 1.092548 * x * z; - shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); - - } - - } - - SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; - - class LightProbe extends Light { - - constructor( sh = new SphericalHarmonics3(), intensity = 1 ) { - - super( undefined, intensity ); - - this.sh = sh; - - } - - copy( source ) { - - super.copy( source ); - - this.sh.copy( source.sh ); - - return this; - - } - - fromJSON( json ) { - - this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); - this.sh.fromArray( json.sh ); - - return this; - - } - - toJSON( meta ) { - - const data = super.toJSON( meta ); - - data.object.sh = this.sh.toArray(); - - return data; - - } - - } - - LightProbe.prototype.isLightProbe = true; - - class LoaderUtils { - - static decodeText( array ) { - - if ( typeof TextDecoder !== 'undefined' ) { - - return new TextDecoder().decode( array ); - - } - - // Avoid the String.fromCharCode.apply(null, array) shortcut, which - // throws a "maximum call stack size exceeded" error for large arrays. - - let s = ''; - - for ( let i = 0, il = array.length; i < il; i ++ ) { - - // Implicitly assumes little-endian. - s += String.fromCharCode( array[ i ] ); - - } - - try { - - // merges multi-byte utf-8 characters. - - return decodeURIComponent( escape( s ) ); - - } catch ( e ) { // see #16358 - - return s; - - } - - } - - static extractUrlBase( url ) { - - const index = url.lastIndexOf( '/' ); - - if ( index === - 1 ) return './'; - - return url.substr( 0, index + 1 ); - - } - - } - - class InstancedBufferGeometry extends BufferGeometry { - - constructor() { - - super(); - - this.type = 'InstancedBufferGeometry'; - this.instanceCount = Infinity; - - } - - copy( source ) { - - super.copy( source ); - - this.instanceCount = source.instanceCount; - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - toJSON() { - - const data = super.toJSON( this ); - - data.instanceCount = this.instanceCount; - - data.isInstancedBufferGeometry = true; - - return data; - - } - - } - - InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; - - class ImageBitmapLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - if ( typeof createImageBitmap === 'undefined' ) { - - console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); - - } - - if ( typeof fetch === 'undefined' ) { - - console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); - - } - - this.options = { premultiplyAlpha: 'none' }; - - } - - setOptions( options ) { - - this.options = options; - - return this; - - } - - load( url, onLoad, onProgress, onError ) { - - if ( url === undefined ) url = ''; - - if ( this.path !== undefined ) url = this.path + url; - - url = this.manager.resolveURL( url ); - - const scope = this; - - const cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) onLoad( cached ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - const fetchOptions = {}; - fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include'; - fetchOptions.headers = this.requestHeader; - - fetch( url, fetchOptions ).then( function ( res ) { - - return res.blob(); - - } ).then( function ( blob ) { - - return createImageBitmap( blob, Object.assign( scope.options, { colorSpaceConversion: 'none' } ) ); - - } ).then( function ( imageBitmap ) { - - Cache.add( url, imageBitmap ); - - if ( onLoad ) onLoad( imageBitmap ); - - scope.manager.itemEnd( url ); - - } ).catch( function ( e ) { - - if ( onError ) onError( e ); - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } ); - - scope.manager.itemStart( url ); - - } - - } - - ImageBitmapLoader.prototype.isImageBitmapLoader = true; - - let _context; - - const AudioContext = { - - getContext: function () { - - if ( _context === undefined ) { - - _context = new ( window.AudioContext || window.webkitAudioContext )(); - - } - - return _context; - - }, - - setContext: function ( value ) { - - _context = value; - - } - - }; - - class AudioLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - } - - load( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.setPath( this.path ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - loader.load( url, function ( buffer ) { - - try { - - // Create a copy of the buffer. The `decodeAudioData` method - // detaches the buffer when complete, preventing reuse. - const bufferCopy = buffer.slice( 0 ); - - const context = AudioContext.getContext(); - context.decodeAudioData( bufferCopy, function ( audioBuffer ) { - - onLoad( audioBuffer ); - - } ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - } - - } - - class HemisphereLightProbe extends LightProbe { - - constructor( skyColor, groundColor, intensity = 1 ) { - - super( undefined, intensity ); - - const color1 = new Color().set( skyColor ); - const color2 = new Color().set( groundColor ); - - const sky = new Vector3( color1.r, color1.g, color1.b ); - const ground = new Vector3( color2.r, color2.g, color2.b ); - - // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); - const c0 = Math.sqrt( Math.PI ); - const c1 = c0 * Math.sqrt( 0.75 ); - - this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); - this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); - - } - - } - - HemisphereLightProbe.prototype.isHemisphereLightProbe = true; - - class AmbientLightProbe extends LightProbe { - - constructor( color, intensity = 1 ) { - - super( undefined, intensity ); - - const color1 = new Color().set( color ); - - // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); - this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); - - } - - } - - AmbientLightProbe.prototype.isAmbientLightProbe = true; - - class Audio extends Object3D { - - constructor( listener ) { - - super(); - - this.type = 'Audio'; - - this.listener = listener; - this.context = listener.context; - - this.gain = this.context.createGain(); - this.gain.connect( listener.getInput() ); - - this.autoplay = false; - - this.buffer = null; - this.detune = 0; - this.loop = false; - this.loopStart = 0; - this.loopEnd = 0; - this.offset = 0; - this.duration = undefined; - this.playbackRate = 1; - this.isPlaying = false; - this.hasPlaybackControl = true; - this.source = null; - this.sourceType = 'empty'; - - this._startedAt = 0; - this._progress = 0; - this._connected = false; - - this.filters = []; - - } - - getOutput() { - - return this.gain; - - } - - setNodeSource( audioNode ) { - - this.hasPlaybackControl = false; - this.sourceType = 'audioNode'; - this.source = audioNode; - this.connect(); - - return this; - - } - - setMediaElementSource( mediaElement ) { - - this.hasPlaybackControl = false; - this.sourceType = 'mediaNode'; - this.source = this.context.createMediaElementSource( mediaElement ); - this.connect(); - - return this; - - } - - setMediaStreamSource( mediaStream ) { - - this.hasPlaybackControl = false; - this.sourceType = 'mediaStreamNode'; - this.source = this.context.createMediaStreamSource( mediaStream ); - this.connect(); - - return this; - - } - - setBuffer( audioBuffer ) { - - this.buffer = audioBuffer; - this.sourceType = 'buffer'; - - if ( this.autoplay ) this.play(); - - return this; - - } - - play( delay = 0 ) { - - if ( this.isPlaying === true ) { - - console.warn( 'THREE.Audio: Audio is already playing.' ); - return; - - } - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this._startedAt = this.context.currentTime + delay; - - const source = this.context.createBufferSource(); - source.buffer = this.buffer; - source.loop = this.loop; - source.loopStart = this.loopStart; - source.loopEnd = this.loopEnd; - source.onended = this.onEnded.bind( this ); - source.start( this._startedAt, this._progress + this.offset, this.duration ); - - this.isPlaying = true; - - this.source = source; - - this.setDetune( this.detune ); - this.setPlaybackRate( this.playbackRate ); - - return this.connect(); - - } - - pause() { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - if ( this.isPlaying === true ) { - - // update current progress - - this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; - - if ( this.loop === true ) { - - // ensure _progress does not exceed duration with looped audios - - this._progress = this._progress % ( this.duration || this.buffer.duration ); - - } - - this.source.stop(); - this.source.onended = null; - - this.isPlaying = false; - - } - - return this; - - } - - stop() { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this._progress = 0; - - this.source.stop(); - this.source.onended = null; - this.isPlaying = false; - - return this; - - } - - connect() { - - if ( this.filters.length > 0 ) { - - this.source.connect( this.filters[ 0 ] ); - - for ( let i = 1, l = this.filters.length; i < l; i ++ ) { - - this.filters[ i - 1 ].connect( this.filters[ i ] ); - - } - - this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); - - } else { - - this.source.connect( this.getOutput() ); - - } - - this._connected = true; - - return this; - - } - - disconnect() { - - if ( this.filters.length > 0 ) { - - this.source.disconnect( this.filters[ 0 ] ); - - for ( let i = 1, l = this.filters.length; i < l; i ++ ) { - - this.filters[ i - 1 ].disconnect( this.filters[ i ] ); - - } - - this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); - - } else { - - this.source.disconnect( this.getOutput() ); - - } - - this._connected = false; - - return this; - - } - - getFilters() { - - return this.filters; - - } - - setFilters( value ) { - - if ( ! value ) value = []; - - if ( this._connected === true ) { - - this.disconnect(); - this.filters = value.slice(); - this.connect(); - - } else { - - this.filters = value.slice(); - - } - - return this; - - } - - setDetune( value ) { - - this.detune = value; - - if ( this.source.detune === undefined ) return; // only set detune when available - - if ( this.isPlaying === true ) { - - this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); - - } - - return this; - - } - - getDetune() { - - return this.detune; - - } - - getFilter() { - - return this.getFilters()[ 0 ]; - - } - - setFilter( filter ) { - - return this.setFilters( filter ? [ filter ] : [] ); - - } - - setPlaybackRate( value ) { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this.playbackRate = value; - - if ( this.isPlaying === true ) { - - this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); - - } - - return this; - - } - - getPlaybackRate() { - - return this.playbackRate; - - } - - onEnded() { - - this.isPlaying = false; - - } - - getLoop() { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return false; - - } - - return this.loop; - - } - - setLoop( value ) { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this.loop = value; - - if ( this.isPlaying === true ) { - - this.source.loop = this.loop; - - } - - return this; - - } - - setLoopStart( value ) { - - this.loopStart = value; - - return this; - - } - - setLoopEnd( value ) { - - this.loopEnd = value; - - return this; - - } - - getVolume() { - - return this.gain.gain.value; - - } - - setVolume( value ) { - - this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); - - return this; - - } - - } - - class PropertyMixer { - - constructor( binding, typeName, valueSize ) { - - this.binding = binding; - this.valueSize = valueSize; - - let mixFunction, - mixFunctionAdditive, - setIdentity; - - // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] - // - // interpolators can use .buffer as their .result - // the data then goes to 'incoming' - // - // 'accu0' and 'accu1' are used frame-interleaved for - // the cumulative result and are compared to detect - // changes - // - // 'orig' stores the original state of the property - // - // 'add' is used for additive cumulative results - // - // 'work' is optional and is only present for quaternion types. It is used - // to store intermediate quaternion multiplication results - - switch ( typeName ) { - - case 'quaternion': - mixFunction = this._slerp; - mixFunctionAdditive = this._slerpAdditive; - setIdentity = this._setAdditiveIdentityQuaternion; - - this.buffer = new Float64Array( valueSize * 6 ); - this._workIndex = 5; - break; - - case 'string': - case 'bool': - mixFunction = this._select; - - // Use the regular mix function and for additive on these types, - // additive is not relevant for non-numeric types - mixFunctionAdditive = this._select; - - setIdentity = this._setAdditiveIdentityOther; - - this.buffer = new Array( valueSize * 5 ); - break; - - default: - mixFunction = this._lerp; - mixFunctionAdditive = this._lerpAdditive; - setIdentity = this._setAdditiveIdentityNumeric; - - this.buffer = new Float64Array( valueSize * 5 ); - - } - - this._mixBufferRegion = mixFunction; - this._mixBufferRegionAdditive = mixFunctionAdditive; - this._setIdentity = setIdentity; - this._origIndex = 3; - this._addIndex = 4; - - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; - - this.useCount = 0; - this.referenceCount = 0; - - } - - // accumulate data in the 'incoming' region into 'accu' - accumulate( accuIndex, weight ) { - - // note: happily accumulating nothing when weight = 0, the caller knows - // the weight and shouldn't have made the call in the first place - - const buffer = this.buffer, - stride = this.valueSize, - offset = accuIndex * stride + stride; - - let currentWeight = this.cumulativeWeight; - - if ( currentWeight === 0 ) { - - // accuN := incoming * weight - - for ( let i = 0; i !== stride; ++ i ) { - - buffer[ offset + i ] = buffer[ i ]; - - } - - currentWeight = weight; - - } else { - - // accuN := accuN + incoming * weight - - currentWeight += weight; - const mix = weight / currentWeight; - this._mixBufferRegion( buffer, offset, 0, mix, stride ); - - } - - this.cumulativeWeight = currentWeight; - - } - - // accumulate data in the 'incoming' region into 'add' - accumulateAdditive( weight ) { - - const buffer = this.buffer, - stride = this.valueSize, - offset = stride * this._addIndex; - - if ( this.cumulativeWeightAdditive === 0 ) { - - // add = identity - - this._setIdentity(); - - } - - // add := add + incoming * weight - - this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); - this.cumulativeWeightAdditive += weight; - - } - - // apply the state of 'accu' to the binding when accus differ - apply( accuIndex ) { - - const stride = this.valueSize, - buffer = this.buffer, - offset = accuIndex * stride + stride, - - weight = this.cumulativeWeight, - weightAdditive = this.cumulativeWeightAdditive, - - binding = this.binding; - - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; - - if ( weight < 1 ) { - - // accuN := accuN + original * ( 1 - cumulativeWeight ) - - const originalValueOffset = stride * this._origIndex; - - this._mixBufferRegion( - buffer, offset, originalValueOffset, 1 - weight, stride ); - - } - - if ( weightAdditive > 0 ) { - - // accuN := accuN + additive accuN - - this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride ); - - } - - for ( let i = stride, e = stride + stride; i !== e; ++ i ) { - - if ( buffer[ i ] !== buffer[ i + stride ] ) { - - // value has changed -> update scene graph - - binding.setValue( buffer, offset ); - break; - - } - - } - - } - - // remember the state of the bound property and copy it to both accus - saveOriginalState() { - - const binding = this.binding; - - const buffer = this.buffer, - stride = this.valueSize, - - originalValueOffset = stride * this._origIndex; - - binding.getValue( buffer, originalValueOffset ); - - // accu[0..1] := orig -- initially detect changes against the original - for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) { - - buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; - - } - - // Add to identity for additive - this._setIdentity(); - - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; - - } - - // apply the state previously taken via 'saveOriginalState' to the binding - restoreOriginalState() { - - const originalValueOffset = this.valueSize * 3; - this.binding.setValue( this.buffer, originalValueOffset ); - - } - - _setAdditiveIdentityNumeric() { - - const startIndex = this._addIndex * this.valueSize; - const endIndex = startIndex + this.valueSize; - - for ( let i = startIndex; i < endIndex; i ++ ) { - - this.buffer[ i ] = 0; - - } - - } - - _setAdditiveIdentityQuaternion() { - - this._setAdditiveIdentityNumeric(); - this.buffer[ this._addIndex * this.valueSize + 3 ] = 1; - - } - - _setAdditiveIdentityOther() { - - const startIndex = this._origIndex * this.valueSize; - const targetIndex = this._addIndex * this.valueSize; - - for ( let i = 0; i < this.valueSize; i ++ ) { - - this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ]; - - } - - } - - - // mix functions - - _select( buffer, dstOffset, srcOffset, t, stride ) { - - if ( t >= 0.5 ) { - - for ( let i = 0; i !== stride; ++ i ) { - - buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; - - } - - } - - } - - _slerp( buffer, dstOffset, srcOffset, t ) { - - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); - - } - - _slerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { - - const workOffset = this._workIndex * stride; - - // Store result in intermediate buffer offset - Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset ); - - // Slerp to the intermediate result - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); - - } - - _lerp( buffer, dstOffset, srcOffset, t, stride ) { - - const s = 1 - t; - - for ( let i = 0; i !== stride; ++ i ) { - - const j = dstOffset + i; - - buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; - - } - - } - - _lerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { - - for ( let i = 0; i !== stride; ++ i ) { - - const j = dstOffset + i; - - buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t; - - } - - } - - } - - // Characters [].:/ are reserved for track binding syntax. - const _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; - const _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' ); - - // Attempts to allow node names from any language. ES5's `\w` regexp matches - // only latin characters, and the unicode \p{L} is not yet supported. So - // instead, we exclude reserved characters and match everything else. - const _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; - const _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\.', '' ) + ']'; - - // Parent directories, delimited by '/' or ':'. Currently unused, but must - // be matched to parse the rest of the track name. - const _directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar ); - - // Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. - const _nodeRe = /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot ); - - // Object on target node, and accessor. May not contain reserved - // characters. Accessor may contain any character except closing bracket. - const _objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar ); - - // Property and accessor. May not contain reserved characters. Accessor may - // contain any non-bracket characters. - const _propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar ); - - const _trackRe = new RegExp( '' - + '^' - + _directoryRe - + _nodeRe - + _objectRe - + _propertyRe - + '$' - ); - - const _supportedObjectNames = [ 'material', 'materials', 'bones' ]; - - class Composite { - - constructor( targetGroup, path, optionalParsedPath ) { - - const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); - - this._targetGroup = targetGroup; - this._bindings = targetGroup.subscribe_( path, parsedPath ); - - } - - getValue( array, offset ) { - - this.bind(); // bind all binding - - const firstValidIndex = this._targetGroup.nCachedObjects_, - binding = this._bindings[ firstValidIndex ]; - - // and only call .getValue on the first - if ( binding !== undefined ) binding.getValue( array, offset ); - - } - - setValue( array, offset ) { - - const bindings = this._bindings; - - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].setValue( array, offset ); - - } - - } - - bind() { - - const bindings = this._bindings; - - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].bind(); - - } - - } - - unbind() { - - const bindings = this._bindings; - - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].unbind(); - - } - - } - - } - - // Note: This class uses a State pattern on a per-method basis: - // 'bind' sets 'this.getValue' / 'setValue' and shadows the - // prototype version of these methods with one that represents - // the bound state. When the property is not found, the methods - // become no-ops. - class PropertyBinding { - - constructor( rootNode, path, parsedPath ) { - - this.path = path; - this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); - - this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; - - this.rootNode = rootNode; - - // initial state of these methods that calls 'bind' - this.getValue = this._getValue_unbound; - this.setValue = this._setValue_unbound; - - } - - - static create( root, path, parsedPath ) { - - if ( ! ( root && root.isAnimationObjectGroup ) ) { - - return new PropertyBinding( root, path, parsedPath ); - - } else { - - return new PropertyBinding.Composite( root, path, parsedPath ); - - } - - } - - /** - * Replaces spaces with underscores and removes unsupported characters from - * node names, to ensure compatibility with parseTrackName(). - * - * @param {string} name Node name to be sanitized. - * @return {string} - */ - static sanitizeNodeName( name ) { - - return name.replace( /\s/g, '_' ).replace( _reservedRe, '' ); - - } - - static parseTrackName( trackName ) { - - const matches = _trackRe.exec( trackName ); - - if ( ! matches ) { - - throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName ); - - } - - const results = { - // directoryName: matches[ 1 ], // (tschw) currently unused - nodeName: matches[ 2 ], - objectName: matches[ 3 ], - objectIndex: matches[ 4 ], - propertyName: matches[ 5 ], // required - propertyIndex: matches[ 6 ] - }; - - const lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' ); - - if ( lastDot !== undefined && lastDot !== - 1 ) { - - const objectName = results.nodeName.substring( lastDot + 1 ); - - // Object names must be checked against an allowlist. Otherwise, there - // is no way to parse 'foo.bar.baz': 'baz' must be a property, but - // 'bar' could be the objectName, or part of a nodeName (which can - // include '.' characters). - if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) { - - results.nodeName = results.nodeName.substring( 0, lastDot ); - results.objectName = objectName; - - } - - } - - if ( results.propertyName === null || results.propertyName.length === 0 ) { - - throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName ); - - } - - return results; - - } - - static findNode( root, nodeName ) { - - if ( ! nodeName || nodeName === '' || nodeName === '.' || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { - - return root; - - } - - // search into skeleton bones. - if ( root.skeleton ) { - - const bone = root.skeleton.getBoneByName( nodeName ); - - if ( bone !== undefined ) { - - return bone; - - } - - } - - // search into node subtree. - if ( root.children ) { - - const searchNodeSubtree = function ( children ) { - - for ( let i = 0; i < children.length; i ++ ) { - - const childNode = children[ i ]; - - if ( childNode.name === nodeName || childNode.uuid === nodeName ) { - - return childNode; - - } - - const result = searchNodeSubtree( childNode.children ); - - if ( result ) return result; - - } - - return null; - - }; - - const subTreeNode = searchNodeSubtree( root.children ); - - if ( subTreeNode ) { - - return subTreeNode; - - } - - } - - return null; - - } - - // these are used to "bind" a nonexistent property - _getValue_unavailable() {} - _setValue_unavailable() {} - - // Getters - - _getValue_direct( buffer, offset ) { - - buffer[ offset ] = this.targetObject[ this.propertyName ]; - - } - - _getValue_array( buffer, offset ) { - - const source = this.resolvedProperty; - - for ( let i = 0, n = source.length; i !== n; ++ i ) { - - buffer[ offset ++ ] = source[ i ]; - - } - - } - - _getValue_arrayElement( buffer, offset ) { - - buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; - - } - - _getValue_toArray( buffer, offset ) { - - this.resolvedProperty.toArray( buffer, offset ); - - } - - // Direct - - _setValue_direct( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - - } - - _setValue_direct_setNeedsUpdate( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; - - } - - _setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - // EntireArray - - _setValue_array( buffer, offset ) { - - const dest = this.resolvedProperty; - - for ( let i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - } - - _setValue_array_setNeedsUpdate( buffer, offset ) { - - const dest = this.resolvedProperty; - - for ( let i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - this.targetObject.needsUpdate = true; - - } - - _setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { - - const dest = this.resolvedProperty; - - for ( let i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - // ArrayElement - - _setValue_arrayElement( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - - } - - _setValue_arrayElement_setNeedsUpdate( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; - - } - - _setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - // HasToFromArray - - _setValue_fromArray( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - - } - - _setValue_fromArray_setNeedsUpdate( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.needsUpdate = true; - - } - - _setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - _getValue_unbound( targetArray, offset ) { - - this.bind(); - this.getValue( targetArray, offset ); - - } - - _setValue_unbound( sourceArray, offset ) { - - this.bind(); - this.setValue( sourceArray, offset ); - - } - - // create getter / setter pair for a property in the scene graph - bind() { - - let targetObject = this.node; - const parsedPath = this.parsedPath; - - const objectName = parsedPath.objectName; - const propertyName = parsedPath.propertyName; - let propertyIndex = parsedPath.propertyIndex; - - if ( ! targetObject ) { - - targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; - - this.node = targetObject; - - } - - // set fail state so we can just 'return' on error - this.getValue = this._getValue_unavailable; - this.setValue = this._setValue_unavailable; - - // ensure there is a value node - if ( ! targetObject ) { - - console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.' ); - return; - - } - - if ( objectName ) { - - let objectIndex = parsedPath.objectIndex; - - // special cases were we need to reach deeper into the hierarchy to get the face materials.... - switch ( objectName ) { - - case 'materials': - - if ( ! targetObject.material ) { - - console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this ); - return; - - } - - if ( ! targetObject.material.materials ) { - - console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this ); - return; - - } - - targetObject = targetObject.material.materials; - - break; - - case 'bones': - - if ( ! targetObject.skeleton ) { - - console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this ); - return; - - } - - // potential future optimization: skip this if propertyIndex is already an integer - // and convert the integer string to a true integer. - - targetObject = targetObject.skeleton.bones; - - // support resolving morphTarget names into indices. - for ( let i = 0; i < targetObject.length; i ++ ) { - - if ( targetObject[ i ].name === objectIndex ) { - - objectIndex = i; - break; - - } - - } - - break; - - default: - - if ( targetObject[ objectName ] === undefined ) { - - console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this ); - return; - - } - - targetObject = targetObject[ objectName ]; - - } - - - if ( objectIndex !== undefined ) { - - if ( targetObject[ objectIndex ] === undefined ) { - - console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject ); - return; - - } - - targetObject = targetObject[ objectIndex ]; - - } - - } - - // resolve property - const nodeProperty = targetObject[ propertyName ]; - - if ( nodeProperty === undefined ) { - - const nodeName = parsedPath.nodeName; - - console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName + - '.' + propertyName + ' but it wasn\'t found.', targetObject ); - return; - - } - - // determine versioning scheme - let versioning = this.Versioning.None; - - this.targetObject = targetObject; - - if ( targetObject.needsUpdate !== undefined ) { // material - - versioning = this.Versioning.NeedsUpdate; - - } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform - - versioning = this.Versioning.MatrixWorldNeedsUpdate; - - } - - // determine how the property gets bound - let bindingType = this.BindingType.Direct; - - if ( propertyIndex !== undefined ) { - - // access a sub element of the property array (only primitives are supported right now) - - if ( propertyName === 'morphTargetInfluences' ) { - - // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. - - // support resolving morphTarget names into indices. - if ( ! targetObject.geometry ) { - - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this ); - return; - - } - - if ( targetObject.geometry.isBufferGeometry ) { - - if ( ! targetObject.geometry.morphAttributes ) { - - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this ); - return; - - } - - if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) { - - propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ]; - - } - - - } else { - - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.', this ); - return; - - } - - } - - bindingType = this.BindingType.ArrayElement; - - this.resolvedProperty = nodeProperty; - this.propertyIndex = propertyIndex; - - } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { - - // must use copy for Object3D.Euler/Quaternion - - bindingType = this.BindingType.HasFromToArray; - - this.resolvedProperty = nodeProperty; - - } else if ( Array.isArray( nodeProperty ) ) { - - bindingType = this.BindingType.EntireArray; - - this.resolvedProperty = nodeProperty; - - } else { - - this.propertyName = propertyName; - - } - - // select getter / setter - this.getValue = this.GetterByBindingType[ bindingType ]; - this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; - - } - - unbind() { - - this.node = null; - - // back to the prototype version of getValue / setValue - // note: avoiding to mutate the shape of 'this' via 'delete' - this.getValue = this._getValue_unbound; - this.setValue = this._setValue_unbound; - - } - - } - - PropertyBinding.Composite = Composite; - - PropertyBinding.prototype.BindingType = { - Direct: 0, - EntireArray: 1, - ArrayElement: 2, - HasFromToArray: 3 - }; - - PropertyBinding.prototype.Versioning = { - None: 0, - NeedsUpdate: 1, - MatrixWorldNeedsUpdate: 2 - }; - - PropertyBinding.prototype.GetterByBindingType = [ - - PropertyBinding.prototype._getValue_direct, - PropertyBinding.prototype._getValue_array, - PropertyBinding.prototype._getValue_arrayElement, - PropertyBinding.prototype._getValue_toArray, - - ]; - - PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [ - - [ - // Direct - PropertyBinding.prototype._setValue_direct, - PropertyBinding.prototype._setValue_direct_setNeedsUpdate, - PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate, - - ], [ - - // EntireArray - - PropertyBinding.prototype._setValue_array, - PropertyBinding.prototype._setValue_array_setNeedsUpdate, - PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate, - - ], [ - - // ArrayElement - PropertyBinding.prototype._setValue_arrayElement, - PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, - PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate, - - ], [ - - // HasToFromArray - PropertyBinding.prototype._setValue_fromArray, - PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, - PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate, - - ] - - ]; - - class AnimationAction { - - constructor( mixer, clip, localRoot = null, blendMode = clip.blendMode ) { - - this._mixer = mixer; - this._clip = clip; - this._localRoot = localRoot; - this.blendMode = blendMode; - - const tracks = clip.tracks, - nTracks = tracks.length, - interpolants = new Array( nTracks ); - - const interpolantSettings = { - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - }; - - for ( let i = 0; i !== nTracks; ++ i ) { - - const interpolant = tracks[ i ].createInterpolant( null ); - interpolants[ i ] = interpolant; - interpolant.settings = interpolantSettings; - - } - - this._interpolantSettings = interpolantSettings; - - this._interpolants = interpolants; // bound by the mixer - - // inside: PropertyMixer (managed by the mixer) - this._propertyBindings = new Array( nTracks ); - - this._cacheIndex = null; // for the memory manager - this._byClipCacheIndex = null; // for the memory manager - - this._timeScaleInterpolant = null; - this._weightInterpolant = null; - - this.loop = LoopRepeat; - this._loopCount = - 1; - - // global mixer time when the action is to be started - // it's set back to 'null' upon start of the action - this._startTime = null; - - // scaled local time of the action - // gets clamped or wrapped to 0..clip.duration according to loop - this.time = 0; - - this.timeScale = 1; - this._effectiveTimeScale = 1; - - this.weight = 1; - this._effectiveWeight = 1; - - this.repetitions = Infinity; // no. of repetitions when looping - - this.paused = false; // true -> zero effective time scale - this.enabled = true; // false -> zero effective weight - - this.clampWhenFinished = false;// keep feeding the last frame? - - this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate - this.zeroSlopeAtEnd = true;// clips for start, loop and end - - } - - // State & Scheduling - - play() { - - this._mixer._activateAction( this ); - - return this; - - } - - stop() { - - this._mixer._deactivateAction( this ); - - return this.reset(); - - } - - reset() { - - this.paused = false; - this.enabled = true; - - this.time = 0; // restart clip - this._loopCount = - 1;// forget previous loops - this._startTime = null;// forget scheduling - - return this.stopFading().stopWarping(); - - } - - isRunning() { - - return this.enabled && ! this.paused && this.timeScale !== 0 && - this._startTime === null && this._mixer._isActiveAction( this ); - - } - - // return true when play has been called - isScheduled() { - - return this._mixer._isActiveAction( this ); - - } - - startAt( time ) { - - this._startTime = time; - - return this; - - } - - setLoop( mode, repetitions ) { - - this.loop = mode; - this.repetitions = repetitions; - - return this; - - } - - // Weight - - // set the weight stopping any scheduled fading - // although .enabled = false yields an effective weight of zero, this - // method does *not* change .enabled, because it would be confusing - setEffectiveWeight( weight ) { - - this.weight = weight; - - // note: same logic as when updated at runtime - this._effectiveWeight = this.enabled ? weight : 0; - - return this.stopFading(); - - } - - // return the weight considering fading and .enabled - getEffectiveWeight() { - - return this._effectiveWeight; - - } - - fadeIn( duration ) { - - return this._scheduleFading( duration, 0, 1 ); - - } - - fadeOut( duration ) { - - return this._scheduleFading( duration, 1, 0 ); - - } - - crossFadeFrom( fadeOutAction, duration, warp ) { - - fadeOutAction.fadeOut( duration ); - this.fadeIn( duration ); - - if ( warp ) { - - const fadeInDuration = this._clip.duration, - fadeOutDuration = fadeOutAction._clip.duration, - - startEndRatio = fadeOutDuration / fadeInDuration, - endStartRatio = fadeInDuration / fadeOutDuration; - - fadeOutAction.warp( 1.0, startEndRatio, duration ); - this.warp( endStartRatio, 1.0, duration ); - - } - - return this; - - } - - crossFadeTo( fadeInAction, duration, warp ) { - - return fadeInAction.crossFadeFrom( this, duration, warp ); - - } - - stopFading() { - - const weightInterpolant = this._weightInterpolant; - - if ( weightInterpolant !== null ) { - - this._weightInterpolant = null; - this._mixer._takeBackControlInterpolant( weightInterpolant ); - - } - - return this; - - } - - // Time Scale Control - - // set the time scale stopping any scheduled warping - // although .paused = true yields an effective time scale of zero, this - // method does *not* change .paused, because it would be confusing - setEffectiveTimeScale( timeScale ) { - - this.timeScale = timeScale; - this._effectiveTimeScale = this.paused ? 0 : timeScale; - - return this.stopWarping(); - - } - - // return the time scale considering warping and .paused - getEffectiveTimeScale() { - - return this._effectiveTimeScale; - - } - - setDuration( duration ) { - - this.timeScale = this._clip.duration / duration; - - return this.stopWarping(); - - } - - syncWith( action ) { - - this.time = action.time; - this.timeScale = action.timeScale; - - return this.stopWarping(); - - } - - halt( duration ) { - - return this.warp( this._effectiveTimeScale, 0, duration ); - - } - - warp( startTimeScale, endTimeScale, duration ) { - - const mixer = this._mixer, - now = mixer.time, - timeScale = this.timeScale; - - let interpolant = this._timeScaleInterpolant; - - if ( interpolant === null ) { - - interpolant = mixer._lendControlInterpolant(); - this._timeScaleInterpolant = interpolant; - - } - - const times = interpolant.parameterPositions, - values = interpolant.sampleValues; - - times[ 0 ] = now; - times[ 1 ] = now + duration; - - values[ 0 ] = startTimeScale / timeScale; - values[ 1 ] = endTimeScale / timeScale; - - return this; - - } - - stopWarping() { - - const timeScaleInterpolant = this._timeScaleInterpolant; - - if ( timeScaleInterpolant !== null ) { - - this._timeScaleInterpolant = null; - this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); - - } - - return this; - - } - - // Object Accessors - - getMixer() { - - return this._mixer; - - } - - getClip() { - - return this._clip; - - } - - getRoot() { - - return this._localRoot || this._mixer._root; - - } - - // Interna - - _update( time, deltaTime, timeDirection, accuIndex ) { - - // called by the mixer - - if ( ! this.enabled ) { - - // call ._updateWeight() to update ._effectiveWeight - - this._updateWeight( time ); - return; - - } - - const startTime = this._startTime; - - if ( startTime !== null ) { - - // check for scheduled start of action - - const timeRunning = ( time - startTime ) * timeDirection; - if ( timeRunning < 0 || timeDirection === 0 ) { - - return; // yet to come / don't decide when delta = 0 - - } - - // start - - this._startTime = null; // unschedule - deltaTime = timeDirection * timeRunning; - - } - - // apply time scale and advance time - - deltaTime *= this._updateTimeScale( time ); - const clipTime = this._updateTime( deltaTime ); - - // note: _updateTime may disable the action resulting in - // an effective weight of 0 - - const weight = this._updateWeight( time ); - - if ( weight > 0 ) { - - const interpolants = this._interpolants; - const propertyMixers = this._propertyBindings; - - switch ( this.blendMode ) { - - case AdditiveAnimationBlendMode: - - for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { - - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulateAdditive( weight ); - - } - - break; - - case NormalAnimationBlendMode: - default: - - for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { - - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulate( accuIndex, weight ); - - } - - } - - } - - } - - _updateWeight( time ) { - - let weight = 0; - - if ( this.enabled ) { - - weight = this.weight; - const interpolant = this._weightInterpolant; - - if ( interpolant !== null ) { - - const interpolantValue = interpolant.evaluate( time )[ 0 ]; - - weight *= interpolantValue; - - if ( time > interpolant.parameterPositions[ 1 ] ) { - - this.stopFading(); - - if ( interpolantValue === 0 ) { - - // faded out, disable - this.enabled = false; - - } - - } - - } - - } - - this._effectiveWeight = weight; - return weight; - - } - - _updateTimeScale( time ) { - - let timeScale = 0; - - if ( ! this.paused ) { - - timeScale = this.timeScale; - - const interpolant = this._timeScaleInterpolant; - - if ( interpolant !== null ) { - - const interpolantValue = interpolant.evaluate( time )[ 0 ]; - - timeScale *= interpolantValue; - - if ( time > interpolant.parameterPositions[ 1 ] ) { - - this.stopWarping(); - - if ( timeScale === 0 ) { - - // motion has halted, pause - this.paused = true; - - } else { - - // warp done - apply final time scale - this.timeScale = timeScale; - - } - - } - - } - - } - - this._effectiveTimeScale = timeScale; - return timeScale; - - } - - _updateTime( deltaTime ) { - - const duration = this._clip.duration; - const loop = this.loop; - - let time = this.time + deltaTime; - let loopCount = this._loopCount; - - const pingPong = ( loop === LoopPingPong ); - - if ( deltaTime === 0 ) { - - if ( loopCount === - 1 ) return time; - - return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; - - } - - if ( loop === LoopOnce ) { - - if ( loopCount === - 1 ) { - - // just started - - this._loopCount = 0; - this._setEndings( true, true, false ); - - } - - handle_stop: { - - if ( time >= duration ) { - - time = duration; - - } else if ( time < 0 ) { - - time = 0; - - } else { - - this.time = time; - - break handle_stop; - - } - - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; - - this.time = time; - - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime < 0 ? - 1 : 1 - } ); - - } - - } else { // repetitive Repeat or PingPong - - if ( loopCount === - 1 ) { - - // just started - - if ( deltaTime >= 0 ) { - - loopCount = 0; - - this._setEndings( true, this.repetitions === 0, pingPong ); - - } else { - - // when looping in reverse direction, the initial - // transition through zero counts as a repetition, - // so leave loopCount at -1 - - this._setEndings( this.repetitions === 0, true, pingPong ); - - } - - } - - if ( time >= duration || time < 0 ) { - - // wrap around - - const loopDelta = Math.floor( time / duration ); // signed - time -= duration * loopDelta; - - loopCount += Math.abs( loopDelta ); - - const pending = this.repetitions - loopCount; - - if ( pending <= 0 ) { - - // have to stop (switch state, clamp time, fire event) - - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; - - time = deltaTime > 0 ? duration : 0; - - this.time = time; - - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime > 0 ? 1 : - 1 - } ); - - } else { - - // keep running - - if ( pending === 1 ) { - - // entering the last round - - const atStart = deltaTime < 0; - this._setEndings( atStart, ! atStart, pingPong ); - - } else { - - this._setEndings( false, false, pingPong ); - - } - - this._loopCount = loopCount; - - this.time = time; - - this._mixer.dispatchEvent( { - type: 'loop', action: this, loopDelta: loopDelta - } ); - - } - - } else { - - this.time = time; - - } - - if ( pingPong && ( loopCount & 1 ) === 1 ) { - - // invert time for the "pong round" - - return duration - time; - - } - - } - - return time; - - } - - _setEndings( atStart, atEnd, pingPong ) { - - const settings = this._interpolantSettings; - - if ( pingPong ) { - - settings.endingStart = ZeroSlopeEnding; - settings.endingEnd = ZeroSlopeEnding; - - } else { - - // assuming for LoopOnce atStart == atEnd == true - - if ( atStart ) { - - settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; - - } else { - - settings.endingStart = WrapAroundEnding; - - } - - if ( atEnd ) { - - settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; - - } else { - - settings.endingEnd = WrapAroundEnding; - - } - - } - - } - - _scheduleFading( duration, weightNow, weightThen ) { - - const mixer = this._mixer, now = mixer.time; - let interpolant = this._weightInterpolant; - - if ( interpolant === null ) { - - interpolant = mixer._lendControlInterpolant(); - this._weightInterpolant = interpolant; - - } - - const times = interpolant.parameterPositions, - values = interpolant.sampleValues; - - times[ 0 ] = now; - values[ 0 ] = weightNow; - times[ 1 ] = now + duration; - values[ 1 ] = weightThen; - - return this; - - } - - } - - class AnimationMixer extends EventDispatcher { - - constructor( root ) { - - super(); - - this._root = root; - this._initMemoryManager(); - this._accuIndex = 0; - this.time = 0; - this.timeScale = 1.0; - - } - - _bindAction( action, prototypeAction ) { - - const root = action._localRoot || this._root, - tracks = action._clip.tracks, - nTracks = tracks.length, - bindings = action._propertyBindings, - interpolants = action._interpolants, - rootUuid = root.uuid, - bindingsByRoot = this._bindingsByRootAndName; - - let bindingsByName = bindingsByRoot[ rootUuid ]; - - if ( bindingsByName === undefined ) { - - bindingsByName = {}; - bindingsByRoot[ rootUuid ] = bindingsByName; - - } - - for ( let i = 0; i !== nTracks; ++ i ) { - - const track = tracks[ i ], - trackName = track.name; - - let binding = bindingsByName[ trackName ]; - - if ( binding !== undefined ) { - - bindings[ i ] = binding; - - } else { - - binding = bindings[ i ]; - - if ( binding !== undefined ) { - - // existing binding, make sure the cache knows - - if ( binding._cacheIndex === null ) { - - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); - - } - - continue; - - } - - const path = prototypeAction && prototypeAction. - _propertyBindings[ i ].binding.parsedPath; - - binding = new PropertyMixer( - PropertyBinding.create( root, trackName, path ), - track.ValueTypeName, track.getValueSize() ); - - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); - - bindings[ i ] = binding; - - } - - interpolants[ i ].resultBuffer = binding.buffer; - - } - - } - - _activateAction( action ) { - - if ( ! this._isActiveAction( action ) ) { - - if ( action._cacheIndex === null ) { - - // this action has been forgotten by the cache, but the user - // appears to be still using it -> rebind - - const rootUuid = ( action._localRoot || this._root ).uuid, - clipUuid = action._clip.uuid, - actionsForClip = this._actionsByClip[ clipUuid ]; - - this._bindAction( action, - actionsForClip && actionsForClip.knownActions[ 0 ] ); - - this._addInactiveAction( action, clipUuid, rootUuid ); - - } - - const bindings = action._propertyBindings; - - // increment reference counts / sort out state - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - - const binding = bindings[ i ]; - - if ( binding.useCount ++ === 0 ) { - - this._lendBinding( binding ); - binding.saveOriginalState(); - - } - - } - - this._lendAction( action ); - - } - - } - - _deactivateAction( action ) { - - if ( this._isActiveAction( action ) ) { - - const bindings = action._propertyBindings; - - // decrement reference counts / sort out state - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - - const binding = bindings[ i ]; - - if ( -- binding.useCount === 0 ) { - - binding.restoreOriginalState(); - this._takeBackBinding( binding ); - - } - - } - - this._takeBackAction( action ); - - } - - } - - // Memory manager - - _initMemoryManager() { - - this._actions = []; // 'nActiveActions' followed by inactive ones - this._nActiveActions = 0; - - this._actionsByClip = {}; - // inside: - // { - // knownActions: Array< AnimationAction > - used as prototypes - // actionByRoot: AnimationAction - lookup - // } - - - this._bindings = []; // 'nActiveBindings' followed by inactive ones - this._nActiveBindings = 0; - - this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > - - - this._controlInterpolants = []; // same game as above - this._nActiveControlInterpolants = 0; - - const scope = this; - - this.stats = { - - actions: { - get total() { - - return scope._actions.length; - - }, - get inUse() { - - return scope._nActiveActions; - - } - }, - bindings: { - get total() { - - return scope._bindings.length; - - }, - get inUse() { - - return scope._nActiveBindings; - - } - }, - controlInterpolants: { - get total() { - - return scope._controlInterpolants.length; - - }, - get inUse() { - - return scope._nActiveControlInterpolants; - - } - } - - }; - - } - - // Memory management for AnimationAction objects - - _isActiveAction( action ) { - - const index = action._cacheIndex; - return index !== null && index < this._nActiveActions; - - } - - _addInactiveAction( action, clipUuid, rootUuid ) { - - const actions = this._actions, - actionsByClip = this._actionsByClip; - - let actionsForClip = actionsByClip[ clipUuid ]; - - if ( actionsForClip === undefined ) { - - actionsForClip = { - - knownActions: [ action ], - actionByRoot: {} - - }; - - action._byClipCacheIndex = 0; - - actionsByClip[ clipUuid ] = actionsForClip; - - } else { - - const knownActions = actionsForClip.knownActions; - - action._byClipCacheIndex = knownActions.length; - knownActions.push( action ); - - } - - action._cacheIndex = actions.length; - actions.push( action ); - - actionsForClip.actionByRoot[ rootUuid ] = action; - - } - - _removeInactiveAction( action ) { - - const actions = this._actions, - lastInactiveAction = actions[ actions.length - 1 ], - cacheIndex = action._cacheIndex; - - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); - - action._cacheIndex = null; - - - const clipUuid = action._clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ], - knownActionsForClip = actionsForClip.knownActions, - - lastKnownAction = - knownActionsForClip[ knownActionsForClip.length - 1 ], - - byClipCacheIndex = action._byClipCacheIndex; - - lastKnownAction._byClipCacheIndex = byClipCacheIndex; - knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; - knownActionsForClip.pop(); - - action._byClipCacheIndex = null; - - - const actionByRoot = actionsForClip.actionByRoot, - rootUuid = ( action._localRoot || this._root ).uuid; - - delete actionByRoot[ rootUuid ]; - - if ( knownActionsForClip.length === 0 ) { - - delete actionsByClip[ clipUuid ]; - - } - - this._removeInactiveBindingsForAction( action ); - - } - - _removeInactiveBindingsForAction( action ) { - - const bindings = action._propertyBindings; - - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - - const binding = bindings[ i ]; - - if ( -- binding.referenceCount === 0 ) { - - this._removeInactiveBinding( binding ); - - } - - } - - } - - _lendAction( action ) { - - // [ active actions | inactive actions ] - // [ active actions >| inactive actions ] - // s a - // <-swap-> - // a s - - const actions = this._actions, - prevIndex = action._cacheIndex, - - lastActiveIndex = this._nActiveActions ++, - - firstInactiveAction = actions[ lastActiveIndex ]; - - action._cacheIndex = lastActiveIndex; - actions[ lastActiveIndex ] = action; - - firstInactiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = firstInactiveAction; - - } - - _takeBackAction( action ) { - - // [ active actions | inactive actions ] - // [ active actions |< inactive actions ] - // a s - // <-swap-> - // s a - - const actions = this._actions, - prevIndex = action._cacheIndex, - - firstInactiveIndex = -- this._nActiveActions, - - lastActiveAction = actions[ firstInactiveIndex ]; - - action._cacheIndex = firstInactiveIndex; - actions[ firstInactiveIndex ] = action; - - lastActiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = lastActiveAction; - - } - - // Memory management for PropertyMixer objects - - _addInactiveBinding( binding, rootUuid, trackName ) { - - const bindingsByRoot = this._bindingsByRootAndName, - bindings = this._bindings; - - let bindingByName = bindingsByRoot[ rootUuid ]; - - if ( bindingByName === undefined ) { - - bindingByName = {}; - bindingsByRoot[ rootUuid ] = bindingByName; - - } - - bindingByName[ trackName ] = binding; - - binding._cacheIndex = bindings.length; - bindings.push( binding ); - - } - - _removeInactiveBinding( binding ) { - - const bindings = this._bindings, - propBinding = binding.binding, - rootUuid = propBinding.rootNode.uuid, - trackName = propBinding.path, - bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ], - - lastInactiveBinding = bindings[ bindings.length - 1 ], - cacheIndex = binding._cacheIndex; - - lastInactiveBinding._cacheIndex = cacheIndex; - bindings[ cacheIndex ] = lastInactiveBinding; - bindings.pop(); - - delete bindingByName[ trackName ]; - - if ( Object.keys( bindingByName ).length === 0 ) { - - delete bindingsByRoot[ rootUuid ]; - - } - - } - - _lendBinding( binding ) { - - const bindings = this._bindings, - prevIndex = binding._cacheIndex, - - lastActiveIndex = this._nActiveBindings ++, - - firstInactiveBinding = bindings[ lastActiveIndex ]; - - binding._cacheIndex = lastActiveIndex; - bindings[ lastActiveIndex ] = binding; - - firstInactiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = firstInactiveBinding; - - } - - _takeBackBinding( binding ) { - - const bindings = this._bindings, - prevIndex = binding._cacheIndex, - - firstInactiveIndex = -- this._nActiveBindings, - - lastActiveBinding = bindings[ firstInactiveIndex ]; - - binding._cacheIndex = firstInactiveIndex; - bindings[ firstInactiveIndex ] = binding; - - lastActiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = lastActiveBinding; - - } - - - // Memory management of Interpolants for weight and time scale - - _lendControlInterpolant() { - - const interpolants = this._controlInterpolants, - lastActiveIndex = this._nActiveControlInterpolants ++; - - let interpolant = interpolants[ lastActiveIndex ]; - - if ( interpolant === undefined ) { - - interpolant = new LinearInterpolant( - new Float32Array( 2 ), new Float32Array( 2 ), - 1, this._controlInterpolantsResultBuffer ); - - interpolant.__cacheIndex = lastActiveIndex; - interpolants[ lastActiveIndex ] = interpolant; - - } - - return interpolant; - - } - - _takeBackControlInterpolant( interpolant ) { - - const interpolants = this._controlInterpolants, - prevIndex = interpolant.__cacheIndex, - - firstInactiveIndex = -- this._nActiveControlInterpolants, - - lastActiveInterpolant = interpolants[ firstInactiveIndex ]; - - interpolant.__cacheIndex = firstInactiveIndex; - interpolants[ firstInactiveIndex ] = interpolant; - - lastActiveInterpolant.__cacheIndex = prevIndex; - interpolants[ prevIndex ] = lastActiveInterpolant; - - } - - // return an action for a clip optionally using a custom root target - // object (this method allocates a lot of dynamic memory in case a - // previously unknown clip/root combination is specified) - clipAction( clip, optionalRoot, blendMode ) { - - const root = optionalRoot || this._root, - rootUuid = root.uuid; - - let clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip; - - const clipUuid = clipObject !== null ? clipObject.uuid : clip; - - const actionsForClip = this._actionsByClip[ clipUuid ]; - let prototypeAction = null; - - if ( blendMode === undefined ) { - - if ( clipObject !== null ) { - - blendMode = clipObject.blendMode; - - } else { - - blendMode = NormalAnimationBlendMode; - - } - - } - - if ( actionsForClip !== undefined ) { - - const existingAction = actionsForClip.actionByRoot[ rootUuid ]; - - if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { - - return existingAction; - - } - - // we know the clip, so we don't have to parse all - // the bindings again but can just copy - prototypeAction = actionsForClip.knownActions[ 0 ]; - - // also, take the clip from the prototype action - if ( clipObject === null ) - clipObject = prototypeAction._clip; - - } - - // clip must be known when specified via string - if ( clipObject === null ) return null; - - // allocate all resources required to run it - const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode ); - - this._bindAction( newAction, prototypeAction ); - - // and make the action known to the memory manager - this._addInactiveAction( newAction, clipUuid, rootUuid ); - - return newAction; - - } - - // get an existing action - existingAction( clip, optionalRoot ) { - - const root = optionalRoot || this._root, - rootUuid = root.uuid, - - clipObject = typeof clip === 'string' ? - AnimationClip.findByName( root, clip ) : clip, - - clipUuid = clipObject ? clipObject.uuid : clip, - - actionsForClip = this._actionsByClip[ clipUuid ]; - - if ( actionsForClip !== undefined ) { - - return actionsForClip.actionByRoot[ rootUuid ] || null; - - } - - return null; - - } - - // deactivates all previously scheduled actions - stopAllAction() { - - const actions = this._actions, - nActions = this._nActiveActions; - - for ( let i = nActions - 1; i >= 0; -- i ) { - - actions[ i ].stop(); - - } - - return this; - - } - - // advance the time and update apply the animation - update( deltaTime ) { - - deltaTime *= this.timeScale; - - const actions = this._actions, - nActions = this._nActiveActions, - - time = this.time += deltaTime, - timeDirection = Math.sign( deltaTime ), - - accuIndex = this._accuIndex ^= 1; - - // run active actions - - for ( let i = 0; i !== nActions; ++ i ) { - - const action = actions[ i ]; - - action._update( time, deltaTime, timeDirection, accuIndex ); - - } - - // update scene graph - - const bindings = this._bindings, - nBindings = this._nActiveBindings; - - for ( let i = 0; i !== nBindings; ++ i ) { - - bindings[ i ].apply( accuIndex ); - - } - - return this; - - } - - // Allows you to seek to a specific time in an animation. - setTime( timeInSeconds ) { - - this.time = 0; // Zero out time attribute for AnimationMixer object; - for ( let i = 0; i < this._actions.length; i ++ ) { - - this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects. - - } - - return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object. - - } - - // return this mixer's root target object - getRoot() { - - return this._root; - - } - - // free all resources specific to a particular clip - uncacheClip( clip ) { - - const actions = this._actions, - clipUuid = clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ]; - - if ( actionsForClip !== undefined ) { - - // note: just calling _removeInactiveAction would mess up the - // iteration state and also require updating the state we can - // just throw away - - const actionsToRemove = actionsForClip.knownActions; - - for ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) { - - const action = actionsToRemove[ i ]; - - this._deactivateAction( action ); - - const cacheIndex = action._cacheIndex, - lastInactiveAction = actions[ actions.length - 1 ]; - - action._cacheIndex = null; - action._byClipCacheIndex = null; - - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); - - this._removeInactiveBindingsForAction( action ); - - } - - delete actionsByClip[ clipUuid ]; - - } - - } - - // free all resources specific to a particular root target object - uncacheRoot( root ) { - - const rootUuid = root.uuid, - actionsByClip = this._actionsByClip; - - for ( const clipUuid in actionsByClip ) { - - const actionByRoot = actionsByClip[ clipUuid ].actionByRoot, - action = actionByRoot[ rootUuid ]; - - if ( action !== undefined ) { - - this._deactivateAction( action ); - this._removeInactiveAction( action ); - - } - - } - - const bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ]; - - if ( bindingByName !== undefined ) { - - for ( const trackName in bindingByName ) { - - const binding = bindingByName[ trackName ]; - binding.restoreOriginalState(); - this._removeInactiveBinding( binding ); - - } - - } - - } - - // remove a targeted clip from the cache - uncacheAction( clip, optionalRoot ) { - - const action = this.existingAction( clip, optionalRoot ); - - if ( action !== null ) { - - this._deactivateAction( action ); - this._removeInactiveAction( action ); - - } - - } - - } - - AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array( 1 ); - - class InstancedInterleavedBuffer extends InterleavedBuffer { - - constructor( array, stride, meshPerAttribute = 1 ) { - - super( array, stride ); - - this.meshPerAttribute = meshPerAttribute; - - } - - copy( source ) { - - super.copy( source ); - - this.meshPerAttribute = source.meshPerAttribute; - - return this; - - } - - clone( data ) { - - const ib = super.clone( data ); - - ib.meshPerAttribute = this.meshPerAttribute; - - return ib; - - } - - toJSON( data ) { - - const json = super.toJSON( data ); - - json.isInstancedInterleavedBuffer = true; - json.meshPerAttribute = this.meshPerAttribute; - - return json; - - } - - } - - InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; - - class Raycaster { - - constructor( origin, direction, near = 0, far = Infinity ) { - - this.ray = new Ray( origin, direction ); - // direction is assumed to be normalized (for accurate distance calculations) - - this.near = near; - this.far = far; - this.camera = null; - this.layers = new Layers(); - - this.params = { - Mesh: {}, - Line: { threshold: 1 }, - LOD: {}, - Points: { threshold: 1 }, - Sprite: {} - }; - - } - - set( origin, direction ) { - - // direction is assumed to be normalized (for accurate distance calculations) - - this.ray.set( origin, direction ); - - } - - setFromCamera( coords, camera ) { - - if ( camera && camera.isPerspectiveCamera ) { - - this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); - this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); - this.camera = camera; - - } else if ( camera && camera.isOrthographicCamera ) { - - this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera - this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); - this.camera = camera; - - } else { - - console.error( 'THREE.Raycaster: Unsupported camera type: ' + camera.type ); - - } - - } - - intersectObject( object, recursive = false, intersects = [] ) { - - intersectObject( object, this, intersects, recursive ); - - intersects.sort( ascSort ); - - return intersects; - - } - - intersectObjects( objects, recursive = false, intersects = [] ) { - - for ( let i = 0, l = objects.length; i < l; i ++ ) { - - intersectObject( objects[ i ], this, intersects, recursive ); - - } - - intersects.sort( ascSort ); - - return intersects; - - } - - } - - function ascSort( a, b ) { - - return a.distance - b.distance; - - } - - function intersectObject( object, raycaster, intersects, recursive ) { - - if ( object.layers.test( raycaster.layers ) ) { - - object.raycast( raycaster, intersects ); - - } - - if ( recursive === true ) { - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - intersectObject( children[ i ], raycaster, intersects, true ); - - } - - } - - } - - const _startP = /*@__PURE__*/ new Vector3(); - const _startEnd = /*@__PURE__*/ new Vector3(); - - class Line3 { - - constructor( start = new Vector3(), end = new Vector3() ) { - - this.start = start; - this.end = end; - - } - - set( start, end ) { - - this.start.copy( start ); - this.end.copy( end ); - - return this; - - } - - copy( line ) { - - this.start.copy( line.start ); - this.end.copy( line.end ); - - return this; - - } - - getCenter( target ) { - - return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); - - } - - delta( target ) { - - return target.subVectors( this.end, this.start ); - - } - - distanceSq() { - - return this.start.distanceToSquared( this.end ); - - } - - distance() { - - return this.start.distanceTo( this.end ); - - } - - at( t, target ) { - - return this.delta( target ).multiplyScalar( t ).add( this.start ); - - } - - closestPointToPointParameter( point, clampToLine ) { - - _startP.subVectors( point, this.start ); - _startEnd.subVectors( this.end, this.start ); - - const startEnd2 = _startEnd.dot( _startEnd ); - const startEnd_startP = _startEnd.dot( _startP ); - - let t = startEnd_startP / startEnd2; - - if ( clampToLine ) { - - t = clamp( t, 0, 1 ); - - } - - return t; - - } - - closestPointToPoint( point, clampToLine, target ) { - - const t = this.closestPointToPointParameter( point, clampToLine ); - - return this.delta( target ).multiplyScalar( t ).add( this.start ); - - } - - applyMatrix4( matrix ) { - - this.start.applyMatrix4( matrix ); - this.end.applyMatrix4( matrix ); - - return this; - - } - - equals( line ) { - - return line.start.equals( this.start ) && line.end.equals( this.end ); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - } - - class ImmediateRenderObject extends Object3D { - - constructor( material ) { - - super(); - - this.material = material; - this.render = function ( /* renderCallback */ ) {}; - - this.hasPositions = false; - this.hasNormals = false; - this.hasColors = false; - this.hasUvs = false; - - this.positionArray = null; - this.normalArray = null; - this.colorArray = null; - this.uvArray = null; - - this.count = 0; - - } - - } - - ImmediateRenderObject.prototype.isImmediateRenderObject = true; - - const _vector$2 = /*@__PURE__*/ new Vector3(); - const _boneMatrix = /*@__PURE__*/ new Matrix4(); - const _matrixWorldInv = /*@__PURE__*/ new Matrix4(); - - - class SkeletonHelper extends LineSegments { - - constructor( object ) { - - const bones = getBoneList( object ); - - const geometry = new BufferGeometry(); - - const vertices = []; - const colors = []; - - const color1 = new Color( 0, 0, 1 ); - const color2 = new Color( 0, 1, 0 ); - - for ( let i = 0; i < bones.length; i ++ ) { - - const bone = bones[ i ]; - - if ( bone.parent && bone.parent.isBone ) { - - vertices.push( 0, 0, 0 ); - vertices.push( 0, 0, 0 ); - colors.push( color1.r, color1.g, color1.b ); - colors.push( color2.r, color2.g, color2.b ); - - } - - } - - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - - const material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } ); - - super( geometry, material ); - - this.type = 'SkeletonHelper'; - this.isSkeletonHelper = true; - - this.root = object; - this.bones = bones; - - this.matrix = object.matrixWorld; - this.matrixAutoUpdate = false; - - } - - updateMatrixWorld( force ) { - - const bones = this.bones; - - const geometry = this.geometry; - const position = geometry.getAttribute( 'position' ); - - _matrixWorldInv.copy( this.root.matrixWorld ).invert(); - - for ( let i = 0, j = 0; i < bones.length; i ++ ) { - - const bone = bones[ i ]; - - if ( bone.parent && bone.parent.isBone ) { - - _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld ); - _vector$2.setFromMatrixPosition( _boneMatrix ); - position.setXYZ( j, _vector$2.x, _vector$2.y, _vector$2.z ); - - _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld ); - _vector$2.setFromMatrixPosition( _boneMatrix ); - position.setXYZ( j + 1, _vector$2.x, _vector$2.y, _vector$2.z ); - - j += 2; - - } - - } - - geometry.getAttribute( 'position' ).needsUpdate = true; - - super.updateMatrixWorld( force ); - - } - - } - - - function getBoneList( object ) { - - const boneList = []; - - if ( object && object.isBone ) { - - boneList.push( object ); - - } - - for ( let i = 0; i < object.children.length; i ++ ) { - - boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); - - } - - return boneList; - - } - - class GridHelper extends LineSegments { - - constructor( size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888 ) { - - color1 = new Color( color1 ); - color2 = new Color( color2 ); - - const center = divisions / 2; - const step = size / divisions; - const halfSize = size / 2; - - const vertices = [], colors = []; - - for ( let i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { - - vertices.push( - halfSize, 0, k, halfSize, 0, k ); - vertices.push( k, 0, - halfSize, k, 0, halfSize ); - - const color = i === center ? color1 : color2; - - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - - } - - const geometry = new BufferGeometry(); - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - - const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); - - super( geometry, material ); - - this.type = 'GridHelper'; - - } - - } - - const _floatView = new Float32Array( 1 ); - new Int32Array( _floatView.buffer ); - const NoColors = 0; - const VertexColors = 2; - - // - - Curve.create = function ( construct, getPoint ) { - - console.log( 'THREE.Curve.create() has been deprecated' ); - - construct.prototype = Object.create( Curve.prototype ); - construct.prototype.constructor = construct; - construct.prototype.getPoint = getPoint; - - return construct; - - }; - - // - - Path.prototype.fromPoints = function ( points ) { - - console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' ); - return this.setFromPoints( points ); - - }; - - GridHelper.prototype.setColors = function () { - - console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' ); - - }; - - SkeletonHelper.prototype.update = function () { - - console.error( 'THREE.SkeletonHelper: update() no longer needs to be called.' ); - - }; - - // - - Loader.prototype.extractUrlBase = function ( url ) { - - console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' ); - return LoaderUtils.extractUrlBase( url ); - - }; - - Loader.Handlers = { - - add: function ( /* regex, loader */ ) { - - console.error( 'THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.' ); - - }, - - get: function ( /* file */ ) { - - console.error( 'THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.' ); - - } - - }; - - // - - Box3.prototype.center = function ( optionalTarget ) { - - console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }; - - Box3.prototype.empty = function () { - - console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }; - - Box3.prototype.isIntersectionBox = function ( box ) { - - console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }; - - Box3.prototype.isIntersectionSphere = function ( sphere ) { - - console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); - - }; - - Box3.prototype.size = function ( optionalTarget ) { - - console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); - - }; - - // - - Sphere.prototype.empty = function () { - - console.warn( 'THREE.Sphere: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }; - - // - - Frustum.prototype.setFromMatrix = function ( m ) { - - console.warn( 'THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().' ); - return this.setFromProjectionMatrix( m ); - - }; - - // - - Line3.prototype.center = function ( optionalTarget ) { - - console.warn( 'THREE.Line3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }; - - // - - Matrix3.prototype.flattenToArrayOffset = function ( array, offset ) { - - console.warn( 'THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); - return this.toArray( array, offset ); - - }; - - Matrix3.prototype.multiplyVector3 = function ( vector ) { - - console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); - return vector.applyMatrix3( this ); - - }; - - Matrix3.prototype.multiplyVector3Array = function ( /* a */ ) { - - console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' ); - - }; - - Matrix3.prototype.applyToBufferAttribute = function ( attribute ) { - - console.warn( 'THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.' ); - return attribute.applyMatrix3( this ); - - }; - - Matrix3.prototype.applyToVector3Array = function ( /* array, offset, length */ ) { - - console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' ); - - }; - - Matrix3.prototype.getInverse = function ( matrix ) { - - console.warn( 'THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); - return this.copy( matrix ).invert(); - - }; - - // - - Matrix4.prototype.extractPosition = function ( m ) { - - console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); - return this.copyPosition( m ); - - }; - - Matrix4.prototype.flattenToArrayOffset = function ( array, offset ) { - - console.warn( 'THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); - return this.toArray( array, offset ); - - }; - - Matrix4.prototype.getPosition = function () { - - console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); - return new Vector3().setFromMatrixColumn( this, 3 ); - - }; - - Matrix4.prototype.setRotationFromQuaternion = function ( q ) { - - console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); - return this.makeRotationFromQuaternion( q ); - - }; - - Matrix4.prototype.multiplyToArray = function () { - - console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' ); - - }; - - Matrix4.prototype.multiplyVector3 = function ( vector ) { - - console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }; - - Matrix4.prototype.multiplyVector4 = function ( vector ) { - - console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }; - - Matrix4.prototype.multiplyVector3Array = function ( /* a */ ) { - - console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' ); - - }; - - Matrix4.prototype.rotateAxis = function ( v ) { - - console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); - v.transformDirection( this ); - - }; - - Matrix4.prototype.crossVector = function ( vector ) { - - console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }; - - Matrix4.prototype.translate = function () { - - console.error( 'THREE.Matrix4: .translate() has been removed.' ); - - }; - - Matrix4.prototype.rotateX = function () { - - console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); - - }; - - Matrix4.prototype.rotateY = function () { - - console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); - - }; - - Matrix4.prototype.rotateZ = function () { - - console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); - - }; - - Matrix4.prototype.rotateByAxis = function () { - - console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); - - }; - - Matrix4.prototype.applyToBufferAttribute = function ( attribute ) { - - console.warn( 'THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.' ); - return attribute.applyMatrix4( this ); - - }; - - Matrix4.prototype.applyToVector3Array = function ( /* array, offset, length */ ) { - - console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' ); - - }; - - Matrix4.prototype.makeFrustum = function ( left, right, bottom, top, near, far ) { - - console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' ); - return this.makePerspective( left, right, top, bottom, near, far ); - - }; - - Matrix4.prototype.getInverse = function ( matrix ) { - - console.warn( 'THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); - return this.copy( matrix ).invert(); - - }; - - // - - Plane.prototype.isIntersectionLine = function ( line ) { - - console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); - return this.intersectsLine( line ); - - }; - - // - - Quaternion.prototype.multiplyVector3 = function ( vector ) { - - console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); - return vector.applyQuaternion( this ); - - }; - - Quaternion.prototype.inverse = function ( ) { - - console.warn( 'THREE.Quaternion: .inverse() has been renamed to invert().' ); - return this.invert(); - - }; - - // - - Ray.prototype.isIntersectionBox = function ( box ) { - - console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }; - - Ray.prototype.isIntersectionPlane = function ( plane ) { - - console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); - return this.intersectsPlane( plane ); - - }; - - Ray.prototype.isIntersectionSphere = function ( sphere ) { - - console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); - - }; - - // - - Triangle.prototype.area = function () { - - console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' ); - return this.getArea(); - - }; - - Triangle.prototype.barycoordFromPoint = function ( point, target ) { - - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return this.getBarycoord( point, target ); - - }; - - Triangle.prototype.midpoint = function ( target ) { - - console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' ); - return this.getMidpoint( target ); - - }; - - Triangle.prototypenormal = function ( target ) { - - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return this.getNormal( target ); - - }; - - Triangle.prototype.plane = function ( target ) { - - console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' ); - return this.getPlane( target ); - - }; - - Triangle.barycoordFromPoint = function ( point, a, b, c, target ) { - - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return Triangle.getBarycoord( point, a, b, c, target ); - - }; - - Triangle.normal = function ( a, b, c, target ) { - - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return Triangle.getNormal( a, b, c, target ); - - }; - - // - - Shape.prototype.extractAllPoints = function ( divisions ) { - - console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' ); - return this.extractPoints( divisions ); - - }; - - Shape.prototype.extrude = function ( options ) { - - console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); - return new ExtrudeGeometry( this, options ); - - }; - - Shape.prototype.makeGeometry = function ( options ) { - - console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); - return new ShapeGeometry( this, options ); - - }; - - // - - Vector2.prototype.fromAttribute = function ( attribute, index, offset ) { - - console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); - - }; - - Vector2.prototype.distanceToManhattan = function ( v ) { - - console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); - - }; - - Vector2.prototype.lengthManhattan = function () { - - console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - - }; - - // - - Vector3.prototype.setEulerFromRotationMatrix = function () { - - console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); - - }; - - Vector3.prototype.setEulerFromQuaternion = function () { - - console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); - - }; - - Vector3.prototype.getPositionFromMatrix = function ( m ) { - - console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); - return this.setFromMatrixPosition( m ); - - }; - - Vector3.prototype.getScaleFromMatrix = function ( m ) { - - console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); - return this.setFromMatrixScale( m ); - - }; - - Vector3.prototype.getColumnFromMatrix = function ( index, matrix ) { - - console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); - return this.setFromMatrixColumn( matrix, index ); - - }; - - Vector3.prototype.applyProjection = function ( m ) { - - console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' ); - return this.applyMatrix4( m ); - - }; - - Vector3.prototype.fromAttribute = function ( attribute, index, offset ) { - - console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); - - }; - - Vector3.prototype.distanceToManhattan = function ( v ) { - - console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); - - }; - - Vector3.prototype.lengthManhattan = function () { - - console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - - }; - - // - - Vector4.prototype.fromAttribute = function ( attribute, index, offset ) { - - console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); - - }; - - Vector4.prototype.lengthManhattan = function () { - - console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - - }; - - // - - Object3D.prototype.getChildByName = function ( name ) { - - console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); - return this.getObjectByName( name ); - - }; - - Object3D.prototype.renderDepth = function () { - - console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); - - }; - - Object3D.prototype.translate = function ( distance, axis ) { - - console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); - return this.translateOnAxis( axis, distance ); - - }; - - Object3D.prototype.getWorldRotation = function () { - - console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' ); - - }; - - Object3D.prototype.applyMatrix = function ( matrix ) { - - console.warn( 'THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); - - }; - - Object.defineProperties( Object3D.prototype, { - - eulerOrder: { - get: function () { - - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - return this.rotation.order; - - }, - set: function ( value ) { - - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - this.rotation.order = value; - - } - }, - useQuaternion: { - get: function () { - - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - - }, - set: function () { - - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - - } - } - - } ); - - Mesh.prototype.setDrawMode = function () { - - console.error( 'THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - - }; - - Object.defineProperties( Mesh.prototype, { - - drawMode: { - get: function () { - - console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode.' ); - return TrianglesDrawMode; - - }, - set: function () { - - console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - - } - } - - } ); - - SkinnedMesh.prototype.initBones = function () { - - console.error( 'THREE.SkinnedMesh: initBones() has been removed.' ); - - }; - - // - - PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { - - console.warn( 'THREE.PerspectiveCamera.setLens is deprecated. ' + - 'Use .setFocalLength and .filmGauge for a photographic setup.' ); - - if ( filmGauge !== undefined ) this.filmGauge = filmGauge; - this.setFocalLength( focalLength ); - - }; - - // - - Object.defineProperties( Light.prototype, { - onlyShadow: { - set: function () { - - console.warn( 'THREE.Light: .onlyShadow has been removed.' ); - - } - }, - shadowCameraFov: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); - this.shadow.camera.fov = value; - - } - }, - shadowCameraLeft: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); - this.shadow.camera.left = value; - - } - }, - shadowCameraRight: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); - this.shadow.camera.right = value; - - } - }, - shadowCameraTop: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); - this.shadow.camera.top = value; - - } - }, - shadowCameraBottom: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); - this.shadow.camera.bottom = value; - - } - }, - shadowCameraNear: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); - this.shadow.camera.near = value; - - } - }, - shadowCameraFar: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); - this.shadow.camera.far = value; - - } - }, - shadowCameraVisible: { - set: function () { - - console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); - - } - }, - shadowBias: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); - this.shadow.bias = value; - - } - }, - shadowDarkness: { - set: function () { - - console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); - - } - }, - shadowMapWidth: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); - this.shadow.mapSize.width = value; - - } - }, - shadowMapHeight: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); - this.shadow.mapSize.height = value; - - } - } - } ); - - // - - Object.defineProperties( BufferAttribute.prototype, { - - length: { - get: function () { - - console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' ); - return this.array.length; - - } - }, - dynamic: { - get: function () { - - console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); - return this.usage === DynamicDrawUsage; - - }, - set: function ( /* value */ ) { - - console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); - this.setUsage( DynamicDrawUsage ); - - } - } - - } ); - - BufferAttribute.prototype.setDynamic = function ( value ) { - - console.warn( 'THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; - - }; - - BufferAttribute.prototype.copyIndicesArray = function ( /* indices */ ) { - - console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' ); - - }, - - BufferAttribute.prototype.setArray = function ( /* array */ ) { - - console.error( 'THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - - }; - - // - - BufferGeometry.prototype.addIndex = function ( index ) { - - console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); - this.setIndex( index ); - - }; - - BufferGeometry.prototype.addAttribute = function ( name, attribute ) { - - console.warn( 'THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().' ); - - if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { - - console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); - - return this.setAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); - - } - - if ( name === 'index' ) { - - console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); - this.setIndex( attribute ); - - return this; - - } - - return this.setAttribute( name, attribute ); - - }; - - BufferGeometry.prototype.addDrawCall = function ( start, count, indexOffset ) { - - if ( indexOffset !== undefined ) { - - console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); - - } - - console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); - this.addGroup( start, count ); - - }; - - BufferGeometry.prototype.clearDrawCalls = function () { - - console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); - this.clearGroups(); - - }; - - BufferGeometry.prototype.computeOffsets = function () { - - console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); - - }; - - BufferGeometry.prototype.removeAttribute = function ( name ) { - - console.warn( 'THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().' ); - - return this.deleteAttribute( name ); - - }; - - BufferGeometry.prototype.applyMatrix = function ( matrix ) { - - console.warn( 'THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); - - }; - - Object.defineProperties( BufferGeometry.prototype, { - - drawcalls: { - get: function () { - - console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); - return this.groups; - - } - }, - offsets: { - get: function () { - - console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); - return this.groups; - - } - } - - } ); - - InterleavedBuffer.prototype.setDynamic = function ( value ) { - - console.warn( 'THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; - - }; - - InterleavedBuffer.prototype.setArray = function ( /* array */ ) { - - console.error( 'THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - - }; - - // - - ExtrudeGeometry.prototype.getArrays = function () { - - console.error( 'THREE.ExtrudeGeometry: .getArrays() has been removed.' ); - - }; - - ExtrudeGeometry.prototype.addShapeList = function () { - - console.error( 'THREE.ExtrudeGeometry: .addShapeList() has been removed.' ); - - }; - - ExtrudeGeometry.prototype.addShape = function () { - - console.error( 'THREE.ExtrudeGeometry: .addShape() has been removed.' ); - - }; - - // - - Scene.prototype.dispose = function () { - - console.error( 'THREE.Scene: .dispose() has been removed.' ); - - }; - - // - - Object.defineProperties( Material.prototype, { - - wrapAround: { - get: function () { - - console.warn( 'THREE.Material: .wrapAround has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.Material: .wrapAround has been removed.' ); - - } - }, - - overdraw: { - get: function () { - - console.warn( 'THREE.Material: .overdraw has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.Material: .overdraw has been removed.' ); - - } - }, - - wrapRGB: { - get: function () { - - console.warn( 'THREE.Material: .wrapRGB has been removed.' ); - return new Color(); - - } - }, - - shading: { - get: function () { - - console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - - }, - set: function ( value ) { - - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( value === FlatShading ); - - } - }, - - stencilMask: { - get: function () { - - console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); - return this.stencilFuncMask; - - }, - set: function ( value ) { - - console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); - this.stencilFuncMask = value; - - } - }, - - vertexTangents: { - get: function () { - - console.warn( 'THREE.' + this.type + ': .vertexTangents has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.' + this.type + ': .vertexTangents has been removed.' ); - - } - }, - - } ); - - Object.defineProperties( ShaderMaterial.prototype, { - - derivatives: { - get: function () { - - console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - return this.extensions.derivatives; - - }, - set: function ( value ) { - - console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - this.extensions.derivatives = value; - - } - } - - } ); - - // - - WebGLRenderer.prototype.clearTarget = function ( renderTarget, color, depth, stencil ) { - - console.warn( 'THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.' ); - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); - - }; - - WebGLRenderer.prototype.animate = function ( callback ) { - - console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' ); - this.setAnimationLoop( callback ); - - }; - - WebGLRenderer.prototype.getCurrentRenderTarget = function () { - - console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' ); - return this.getRenderTarget(); - - }; - - WebGLRenderer.prototype.getMaxAnisotropy = function () { - - console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' ); - return this.capabilities.getMaxAnisotropy(); - - }; - - WebGLRenderer.prototype.getPrecision = function () { - - console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' ); - return this.capabilities.precision; - - }; - - WebGLRenderer.prototype.resetGLState = function () { - - console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' ); - return this.state.reset(); - - }; - - WebGLRenderer.prototype.supportsFloatTextures = function () { - - console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); - return this.extensions.get( 'OES_texture_float' ); - - }; - - WebGLRenderer.prototype.supportsHalfFloatTextures = function () { - - console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); - return this.extensions.get( 'OES_texture_half_float' ); - - }; - - WebGLRenderer.prototype.supportsStandardDerivatives = function () { - - console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); - return this.extensions.get( 'OES_standard_derivatives' ); - - }; - - WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { - - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); - - }; - - WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { - - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - - }; - - WebGLRenderer.prototype.supportsBlendMinMax = function () { - - console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); - return this.extensions.get( 'EXT_blend_minmax' ); - - }; - - WebGLRenderer.prototype.supportsVertexTextures = function () { - - console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); - return this.capabilities.vertexTextures; - - }; - - WebGLRenderer.prototype.supportsInstancedArrays = function () { - - console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); - return this.extensions.get( 'ANGLE_instanced_arrays' ); - - }; - - WebGLRenderer.prototype.enableScissorTest = function ( boolean ) { - - console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); - this.setScissorTest( boolean ); - - }; - - WebGLRenderer.prototype.initMaterial = function () { - - console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); - - }; - - WebGLRenderer.prototype.addPrePlugin = function () { - - console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); - - }; - - WebGLRenderer.prototype.addPostPlugin = function () { - - console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); - - }; - - WebGLRenderer.prototype.updateShadowMap = function () { - - console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); - - }; - - WebGLRenderer.prototype.setFaceCulling = function () { - - console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' ); - - }; - - WebGLRenderer.prototype.allocTextureUnit = function () { - - console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' ); - - }; - - WebGLRenderer.prototype.setTexture = function () { - - console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' ); - - }; - - WebGLRenderer.prototype.setTexture2D = function () { - - console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' ); - - }; - - WebGLRenderer.prototype.setTextureCube = function () { - - console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' ); - - }; - - WebGLRenderer.prototype.getActiveMipMapLevel = function () { - - console.warn( 'THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().' ); - return this.getActiveMipmapLevel(); - - }; - - Object.defineProperties( WebGLRenderer.prototype, { - - shadowMapEnabled: { - get: function () { - - return this.shadowMap.enabled; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); - this.shadowMap.enabled = value; - - } - }, - shadowMapType: { - get: function () { - - return this.shadowMap.type; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); - this.shadowMap.type = value; - - } - }, - shadowMapCullFace: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function ( /* value */ ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - - } - }, - context: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .context has been removed. Use .getContext() instead.' ); - return this.getContext(); - - } - }, - vr: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .vr has been renamed to .xr' ); - return this.xr; - - } - }, - gammaInput: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); - return false; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); - - } - }, - gammaOutput: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); - return false; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); - this.outputEncoding = ( value === true ) ? sRGBEncoding : LinearEncoding; - - } - }, - toneMappingWhitePoint: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); - return 1.0; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); - - } - }, - - } ); - - Object.defineProperties( WebGLShadowMap.prototype, { - - cullFace: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function ( /* cullFace */ ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - - } - }, - renderReverseSided: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); - - } - }, - renderSingleSided: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - - } - } - - } ); - - // - - Object.defineProperties( WebGLRenderTarget.prototype, { - - wrapS: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - return this.texture.wrapS; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - this.texture.wrapS = value; - - } - }, - wrapT: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - return this.texture.wrapT; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - this.texture.wrapT = value; - - } - }, - magFilter: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - return this.texture.magFilter; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - this.texture.magFilter = value; - - } - }, - minFilter: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - return this.texture.minFilter; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - this.texture.minFilter = value; - - } - }, - anisotropy: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - return this.texture.anisotropy; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - this.texture.anisotropy = value; - - } - }, - offset: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - return this.texture.offset; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - this.texture.offset = value; - - } - }, - repeat: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - return this.texture.repeat; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - this.texture.repeat = value; - - } - }, - format: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - return this.texture.format; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - this.texture.format = value; - - } - }, - type: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - return this.texture.type; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - this.texture.type = value; - - } - }, - generateMipmaps: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - return this.texture.generateMipmaps; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - this.texture.generateMipmaps = value; - - } - } - - } ); - - // - - Audio.prototype.load = function ( file ) { - - console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); - const scope = this; - const audioLoader = new AudioLoader(); - audioLoader.load( file, function ( buffer ) { - - scope.setBuffer( buffer ); - - } ); - return this; - - }; - - // - - CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) { - - console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' ); - return this.update( renderer, scene ); - - }; - - CubeCamera.prototype.clear = function ( renderer, color, depth, stencil ) { - - console.warn( 'THREE.CubeCamera: .clear() is now .renderTarget.clear().' ); - return this.renderTarget.clear( renderer, color, depth, stencil ); - - }; - - ImageUtils.crossOrigin = undefined; - - ImageUtils.loadTexture = function ( url, mapping, onLoad, onError ) { - - console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); - - const loader = new TextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); - - const texture = loader.load( url, onLoad, undefined, onError ); - - if ( mapping ) texture.mapping = mapping; - - return texture; - - }; - - ImageUtils.loadTextureCube = function ( urls, mapping, onLoad, onError ) { - - console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); - - const loader = new CubeTextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); - - const texture = loader.load( urls, onLoad, undefined, onError ); - - if ( mapping ) texture.mapping = mapping; - - return texture; - - }; - - ImageUtils.loadCompressedTexture = function () { - - console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); - - }; - - ImageUtils.loadCompressedTextureCube = function () { - - console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); - - }; - - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - - /* eslint-disable no-undef */ - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: { - revision: REVISION, - } } ) ); - /* eslint-enable no-undef */ - - } - - if ( typeof window !== 'undefined' ) { - - if ( window.__THREE__ ) { - - console.warn( 'WARNING: Multiple instances of Three.js being imported.' ); - - } else { - - window.__THREE__ = REVISION; - - } - - } - - const _box$1 = new Box3(); - const _vector = new Vector3(); - - class LineSegmentsGeometry extends InstancedBufferGeometry { - - constructor() { - - super(); - - this.type = 'LineSegmentsGeometry'; - - const positions = [ - 1, 2, 0, 1, 2, 0, - 1, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 0, - 1, - 1, 0, 1, - 1, 0 ]; - const uvs = [ - 1, 2, 1, 2, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 2, 1, - 2 ]; - const index = [ 0, 2, 1, 2, 3, 1, 2, 4, 3, 4, 5, 3, 4, 6, 5, 6, 7, 5 ]; - - this.setIndex( index ); - this.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - applyMatrix4( matrix ) { - - const start = this.attributes.instanceStart; - const end = this.attributes.instanceEnd; - - if ( start !== undefined ) { - - start.applyMatrix4( matrix ); - - end.applyMatrix4( matrix ); - - start.needsUpdate = true; - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - return this; - - } - - setPositions( array ) { - - let lineSegments; - - if ( array instanceof Float32Array ) { - - lineSegments = array; - - } else if ( Array.isArray( array ) ) { - - lineSegments = new Float32Array( array ); - - } - - const instanceBuffer = new InstancedInterleavedBuffer( lineSegments, 6, 1 ); // xyz, xyz - - this.setAttribute( 'instanceStart', new InterleavedBufferAttribute( instanceBuffer, 3, 0 ) ); // xyz - this.setAttribute( 'instanceEnd', new InterleavedBufferAttribute( instanceBuffer, 3, 3 ) ); // xyz - - // - - this.computeBoundingBox(); - this.computeBoundingSphere(); - - return this; - - } - - setColors( array ) { - - let colors; - - if ( array instanceof Float32Array ) { - - colors = array; - - } else if ( Array.isArray( array ) ) { - - colors = new Float32Array( array ); - - } - - const instanceColorBuffer = new InstancedInterleavedBuffer( colors, 6, 1 ); // rgb, rgb - - this.setAttribute( 'instanceColorStart', new InterleavedBufferAttribute( instanceColorBuffer, 3, 0 ) ); // rgb - this.setAttribute( 'instanceColorEnd', new InterleavedBufferAttribute( instanceColorBuffer, 3, 3 ) ); // rgb - - return this; - - } - - fromWireframeGeometry( geometry ) { - - this.setPositions( geometry.attributes.position.array ); - - return this; - - } - - fromEdgesGeometry( geometry ) { - - this.setPositions( geometry.attributes.position.array ); - - return this; - - } - - fromMesh( mesh ) { - - this.fromWireframeGeometry( new WireframeGeometry( mesh.geometry ) ); - - // set colors, maybe - - return this; - - } - - fromLineSegments( lineSegments ) { - - const geometry = lineSegments.geometry; - - if ( geometry.isGeometry ) { - - console.error( 'THREE.LineSegmentsGeometry no longer supports Geometry. Use THREE.BufferGeometry instead.' ); - return; - - } else if ( geometry.isBufferGeometry ) { - - this.setPositions( geometry.attributes.position.array ); // assumes non-indexed - - } - - // set colors, maybe - - return this; - - } - - computeBoundingBox() { - - if ( this.boundingBox === null ) { - - this.boundingBox = new Box3(); - - } - - const start = this.attributes.instanceStart; - const end = this.attributes.instanceEnd; - - if ( start !== undefined && end !== undefined ) { - - this.boundingBox.setFromBufferAttribute( start ); - - _box$1.setFromBufferAttribute( end ); - - this.boundingBox.union( _box$1 ); - - } - - } - - computeBoundingSphere() { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new Sphere(); - - } - - if ( this.boundingBox === null ) { - - this.computeBoundingBox(); - - } - - const start = this.attributes.instanceStart; - const end = this.attributes.instanceEnd; - - if ( start !== undefined && end !== undefined ) { - - const center = this.boundingSphere.center; - - this.boundingBox.getCenter( center ); - - let maxRadiusSq = 0; - - for ( let i = 0, il = start.count; i < il; i ++ ) { - - _vector.fromBufferAttribute( start, i ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) ); - - _vector.fromBufferAttribute( end, i ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) ); - - } - - this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); - - if ( isNaN( this.boundingSphere.radius ) ) { - - console.error( 'THREE.LineSegmentsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.', this ); - - } - - } - - } - - toJSON() { - - // todo - - } - - applyMatrix( matrix ) { - - console.warn( 'THREE.LineSegmentsGeometry: applyMatrix() has been renamed to applyMatrix4().' ); - - return this.applyMatrix4( matrix ); - - } - - } - - LineSegmentsGeometry.prototype.isLineSegmentsGeometry = true; - - /** - * parameters = { - * color: , - * linewidth: , - * dashed: , - * dashScale: , - * dashSize: , - * gapSize: , - * resolution: , // to be set by renderer - * } - */ - - - UniformsLib.line = { - - worldUnits: { value: 1 }, - linewidth: { value: 1 }, - resolution: { value: new Vector2( 1, 1 ) }, - dashScale: { value: 1 }, - dashSize: { value: 1 }, - gapSize: { value: 1 } // todo FIX - maybe change to totalSize - - }; - - ShaderLib[ 'line' ] = { - - uniforms: UniformsUtils.merge( [ - UniformsLib.common, - UniformsLib.fog, - UniformsLib.line - ] ), - - vertexShader: - /* glsl */` - #include - #include - #include - #include - #include - - uniform float linewidth; - uniform vec2 resolution; - - attribute vec3 instanceStart; - attribute vec3 instanceEnd; - - attribute vec3 instanceColorStart; - attribute vec3 instanceColorEnd; - - varying vec2 vUv; - varying vec4 worldPos; - varying vec3 worldStart; - varying vec3 worldEnd; - - #ifdef USE_DASH - - uniform float dashScale; - attribute float instanceDistanceStart; - attribute float instanceDistanceEnd; - varying float vLineDistance; - - #endif - - void trimSegment( const in vec4 start, inout vec4 end ) { - - // trim end segment so it terminates between the camera plane and the near plane - - // conservative estimate of the near plane - float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column - float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column - float nearEstimate = - 0.5 * b / a; - - float alpha = ( nearEstimate - start.z ) / ( end.z - start.z ); - - end.xyz = mix( start.xyz, end.xyz, alpha ); - - } - - void main() { - - #ifdef USE_COLOR - - vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd; - - #endif - - #ifdef USE_DASH - - vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd; - - #endif - - float aspect = resolution.x / resolution.y; - - vUv = uv; - - // camera space - vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 ); - vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 ); - - worldStart = start.xyz; - worldEnd = end.xyz; - - // special case for perspective projection, and segments that terminate either in, or behind, the camera plane - // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space - // but we need to perform ndc-space calculations in the shader, so we must address this issue directly - // perhaps there is a more elegant solution -- WestLangley - - bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column - - if ( perspective ) { - - if ( start.z < 0.0 && end.z >= 0.0 ) { - - trimSegment( start, end ); - - } else if ( end.z < 0.0 && start.z >= 0.0 ) { - - trimSegment( end, start ); - - } - - } - - // clip space - vec4 clipStart = projectionMatrix * start; - vec4 clipEnd = projectionMatrix * end; - - // ndc space - vec3 ndcStart = clipStart.xyz / clipStart.w; - vec3 ndcEnd = clipEnd.xyz / clipEnd.w; - - // direction - vec2 dir = ndcEnd.xy - ndcStart.xy; - - // account for clip-space aspect ratio - dir.x *= aspect; - dir = normalize( dir ); - - #ifdef WORLD_UNITS - - // get the offset direction as perpendicular to the view vector - vec3 worldDir = normalize( end.xyz - start.xyz ); - vec3 offset; - if ( position.y < 0.5 ) { - - offset = normalize( cross( start.xyz, worldDir ) ); - - } else { - - offset = normalize( cross( end.xyz, worldDir ) ); - - } - - // sign flip - if ( position.x < 0.0 ) offset *= - 1.0; - - float forwardOffset = dot( worldDir, vec3( 0.0, 0.0, 1.0 ) ); - - // don't extend the line if we're rendering dashes because we - // won't be rendering the endcaps - #ifndef USE_DASH - - // extend the line bounds to encompass endcaps - start.xyz += - worldDir * linewidth * 0.5; - end.xyz += worldDir * linewidth * 0.5; - - // shift the position of the quad so it hugs the forward edge of the line - offset.xy -= dir * forwardOffset; - offset.z += 0.5; - - #endif - - // endcaps - if ( position.y > 1.0 || position.y < 0.0 ) { - - offset.xy += dir * 2.0 * forwardOffset; - - } - - // adjust for linewidth - offset *= linewidth * 0.5; - - // set the world position - worldPos = ( position.y < 0.5 ) ? start : end; - worldPos.xyz += offset; - - // project the worldpos - vec4 clip = projectionMatrix * worldPos; - - // shift the depth of the projected points so the line - // segements overlap neatly - vec3 clipPose = ( position.y < 0.5 ) ? ndcStart : ndcEnd; - clip.z = clipPose.z * clip.w; - - #else - - vec2 offset = vec2( dir.y, - dir.x ); - // undo aspect ratio adjustment - dir.x /= aspect; - offset.x /= aspect; - - // sign flip - if ( position.x < 0.0 ) offset *= - 1.0; - - // endcaps - if ( position.y < 0.0 ) { - - offset += - dir; - - } else if ( position.y > 1.0 ) { - - offset += dir; - - } - - // adjust for linewidth - offset *= linewidth; - - // adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ... - offset /= resolution.y; - - // select end - vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd; - - // back to clip space - offset *= clip.w; - - clip.xy += offset; - - #endif - - gl_Position = clip; - - vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation - - #include - #include - #include - - } - `, - - fragmentShader: - /* glsl */` - uniform vec3 diffuse; - uniform float opacity; - uniform float linewidth; - - #ifdef USE_DASH - - uniform float dashSize; - uniform float gapSize; - - #endif - - varying float vLineDistance; - varying vec4 worldPos; - varying vec3 worldStart; - varying vec3 worldEnd; - - #include - #include - #include - #include - #include - - varying vec2 vUv; - - vec2 closestLineToLine(vec3 p1, vec3 p2, vec3 p3, vec3 p4) { - - float mua; - float mub; - - vec3 p13 = p1 - p3; - vec3 p43 = p4 - p3; - - vec3 p21 = p2 - p1; - - float d1343 = dot( p13, p43 ); - float d4321 = dot( p43, p21 ); - float d1321 = dot( p13, p21 ); - float d4343 = dot( p43, p43 ); - float d2121 = dot( p21, p21 ); - - float denom = d2121 * d4343 - d4321 * d4321; - - float numer = d1343 * d4321 - d1321 * d4343; - - mua = numer / denom; - mua = clamp( mua, 0.0, 1.0 ); - mub = ( d1343 + d4321 * ( mua ) ) / d4343; - mub = clamp( mub, 0.0, 1.0 ); - - return vec2( mua, mub ); - - } - - void main() { - - #include - - #ifdef USE_DASH - - if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps - - if ( mod( vLineDistance, dashSize + gapSize ) > dashSize ) discard; // todo - FIX - - #endif - - float alpha = opacity; - - #ifdef WORLD_UNITS - - // Find the closest points on the view ray and the line segment - vec3 rayEnd = normalize( worldPos.xyz ) * 1e5; - vec3 lineDir = worldEnd - worldStart; - vec2 params = closestLineToLine( worldStart, worldEnd, vec3( 0.0, 0.0, 0.0 ), rayEnd ); - - vec3 p1 = worldStart + lineDir * params.x; - vec3 p2 = rayEnd * params.y; - vec3 delta = p1 - p2; - float len = length( delta ); - float norm = len / linewidth; - - #ifndef USE_DASH - - #ifdef ALPHA_TO_COVERAGE - - float dnorm = fwidth( norm ); - alpha = 1.0 - smoothstep( 0.5 - dnorm, 0.5 + dnorm, norm ); - - #else - - if ( norm > 0.5 ) { - - discard; - - } - - #endif - - #endif - - #else - - #ifdef ALPHA_TO_COVERAGE - - // artifacts appear on some hardware if a derivative is taken within a conditional - float a = vUv.x; - float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0; - float len2 = a * a + b * b; - float dlen = fwidth( len2 ); - - if ( abs( vUv.y ) > 1.0 ) { - - alpha = 1.0 - smoothstep( 1.0 - dlen, 1.0 + dlen, len2 ); - - } - - #else - - if ( abs( vUv.y ) > 1.0 ) { - - float a = vUv.x; - float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0; - float len2 = a * a + b * b; - - if ( len2 > 1.0 ) discard; - - } - - #endif - - #endif - - vec4 diffuseColor = vec4( diffuse, alpha ); - - #include - #include - - gl_FragColor = vec4( diffuseColor.rgb, alpha ); - - #include - #include - #include - #include - - } - ` - }; - - class LineMaterial extends ShaderMaterial { - - constructor( parameters ) { - - super( { - - type: 'LineMaterial', - - uniforms: UniformsUtils.clone( ShaderLib[ 'line' ].uniforms ), - - vertexShader: ShaderLib[ 'line' ].vertexShader, - fragmentShader: ShaderLib[ 'line' ].fragmentShader, - - clipping: true // required for clipping support - - } ); - - Object.defineProperties( this, { - - color: { - - enumerable: true, - - get: function () { - - return this.uniforms.diffuse.value; - - }, - - set: function ( value ) { - - this.uniforms.diffuse.value = value; - - } - - }, - - worldUnits: { - - enumerable: true, - - get: function () { - - return 'WORLD_UNITS' in this.defines; - - }, - - set: function ( value ) { - - if ( value === true ) { - - this.defines.WORLD_UNITS = ''; - - } else { - - delete this.defines.WORLD_UNITS; - - } - - } - - }, - - linewidth: { - - enumerable: true, - - get: function () { - - return this.uniforms.linewidth.value; - - }, - - set: function ( value ) { - - this.uniforms.linewidth.value = value; - - } - - }, - - dashed: { - - enumerable: true, - - get: function () { - - return Boolean( 'USE_DASH' in this.defines ); - - }, - - set( value ) { - - if ( Boolean( value ) !== Boolean( 'USE_DASH' in this.defines ) ) { - - this.needsUpdate = true; - - } - - if ( value === true ) { - - this.defines.USE_DASH = ''; - - } else { - - delete this.defines.USE_DASH; - - } - - } - - }, - - dashScale: { - - enumerable: true, - - get: function () { - - return this.uniforms.dashScale.value; - - }, - - set: function ( value ) { - - this.uniforms.dashScale.value = value; - - } - - }, - - dashSize: { - - enumerable: true, - - get: function () { - - return this.uniforms.dashSize.value; - - }, - - set: function ( value ) { - - this.uniforms.dashSize.value = value; - - } - - }, - - dashOffset: { - - enumerable: true, - - get: function () { - - return this.uniforms.dashOffset.value; - - }, - - set: function ( value ) { - - this.uniforms.dashOffset.value = value; - - } - - }, - - gapSize: { - - enumerable: true, - - get: function () { - - return this.uniforms.gapSize.value; - - }, - - set: function ( value ) { - - this.uniforms.gapSize.value = value; - - } - - }, - - opacity: { - - enumerable: true, - - get: function () { - - return this.uniforms.opacity.value; - - }, - - set: function ( value ) { - - this.uniforms.opacity.value = value; - - } - - }, - - resolution: { - - enumerable: true, - - get: function () { - - return this.uniforms.resolution.value; - - }, - - set: function ( value ) { - - this.uniforms.resolution.value.copy( value ); - - } - - }, - - alphaToCoverage: { - - enumerable: true, - - get: function () { - - return Boolean( 'ALPHA_TO_COVERAGE' in this.defines ); - - }, - - set: function ( value ) { - - if ( Boolean( value ) !== Boolean( 'ALPHA_TO_COVERAGE' in this.defines ) ) { - - this.needsUpdate = true; - - } - - if ( value === true ) { - - this.defines.ALPHA_TO_COVERAGE = ''; - this.extensions.derivatives = true; - - } else { - - delete this.defines.ALPHA_TO_COVERAGE; - this.extensions.derivatives = false; - - } - - } - - } - - } ); - - this.setValues( parameters ); - - } - - } - - LineMaterial.prototype.isLineMaterial = true; - - const _start = new Vector3(); - const _end = new Vector3(); - - const _start4 = new Vector4(); - const _end4 = new Vector4(); - - const _ssOrigin = new Vector4(); - const _ssOrigin3 = new Vector3(); - const _mvMatrix = new Matrix4(); - const _line = new Line3(); - const _closestPoint = new Vector3(); - - const _box = new Box3(); - const _sphere = new Sphere(); - const _clipToWorldVector = new Vector4(); - - class LineSegments2 extends Mesh { - - constructor( geometry = new LineSegmentsGeometry(), material = new LineMaterial( { color: Math.random() * 0xffffff } ) ) { - - super( geometry, material ); - - this.type = 'LineSegments2'; - - } - - // for backwards-compatability, but could be a method of LineSegmentsGeometry... - - computeLineDistances() { - - const geometry = this.geometry; - - const instanceStart = geometry.attributes.instanceStart; - const instanceEnd = geometry.attributes.instanceEnd; - const lineDistances = new Float32Array( 2 * instanceStart.count ); - - for ( let i = 0, j = 0, l = instanceStart.count; i < l; i ++, j += 2 ) { - - _start.fromBufferAttribute( instanceStart, i ); - _end.fromBufferAttribute( instanceEnd, i ); - - lineDistances[ j ] = ( j === 0 ) ? 0 : lineDistances[ j - 1 ]; - lineDistances[ j + 1 ] = lineDistances[ j ] + _start.distanceTo( _end ); - - } - - const instanceDistanceBuffer = new InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1 - - geometry.setAttribute( 'instanceDistanceStart', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0 - geometry.setAttribute( 'instanceDistanceEnd', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1 - - return this; - - } - - raycast( raycaster, intersects ) { - - if ( raycaster.camera === null ) { - - console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2.' ); - - } - - const threshold = ( raycaster.params.Line2 !== undefined ) ? raycaster.params.Line2.threshold || 0 : 0; - - const ray = raycaster.ray; - const camera = raycaster.camera; - const projectionMatrix = camera.projectionMatrix; - - const matrixWorld = this.matrixWorld; - const geometry = this.geometry; - const material = this.material; - const resolution = material.resolution; - const lineWidth = material.linewidth + threshold; - - const instanceStart = geometry.attributes.instanceStart; - const instanceEnd = geometry.attributes.instanceEnd; - - // camera forward is negative - const near = - camera.near; - - // clip space is [ - 1, 1 ] so multiply by two to get the full - // width in clip space - const ssMaxWidth = 2.0 * Math.max( lineWidth / resolution.width, lineWidth / resolution.height ); - - // - - // check if we intersect the sphere bounds - if ( geometry.boundingSphere === null ) { - - geometry.computeBoundingSphere(); - - } - - _sphere.copy( geometry.boundingSphere ).applyMatrix4( matrixWorld ); - const distanceToSphere = Math.max( camera.near, _sphere.distanceToPoint( ray.origin ) ); - - // get the w component to scale the world space line width - _clipToWorldVector.set( 0, 0, - distanceToSphere, 1.0 ).applyMatrix4( camera.projectionMatrix ); - _clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w ); - _clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse ); - - // increase the sphere bounds by the worst case line screen space width - const sphereMargin = Math.abs( ssMaxWidth / _clipToWorldVector.w ) * 0.5; - _sphere.radius += sphereMargin; - - if ( raycaster.ray.intersectsSphere( _sphere ) === false ) { - - return; - - } - - // - - // check if we intersect the box bounds - if ( geometry.boundingBox === null ) { - - geometry.computeBoundingBox(); - - } - - _box.copy( geometry.boundingBox ).applyMatrix4( matrixWorld ); - const distanceToBox = Math.max( camera.near, _box.distanceToPoint( ray.origin ) ); - - // get the w component to scale the world space line width - _clipToWorldVector.set( 0, 0, - distanceToBox, 1.0 ).applyMatrix4( camera.projectionMatrix ); - _clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w ); - _clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse ); - - // increase the sphere bounds by the worst case line screen space width - const boxMargin = Math.abs( ssMaxWidth / _clipToWorldVector.w ) * 0.5; - _box.max.x += boxMargin; - _box.max.y += boxMargin; - _box.max.z += boxMargin; - _box.min.x -= boxMargin; - _box.min.y -= boxMargin; - _box.min.z -= boxMargin; - - if ( raycaster.ray.intersectsBox( _box ) === false ) { - - return; - - } - - // - - // pick a point 1 unit out along the ray to avoid the ray origin - // sitting at the camera origin which will cause "w" to be 0 when - // applying the projection matrix. - ray.at( 1, _ssOrigin ); - - // ndc space [ - 1.0, 1.0 ] - _ssOrigin.w = 1; - _ssOrigin.applyMatrix4( camera.matrixWorldInverse ); - _ssOrigin.applyMatrix4( projectionMatrix ); - _ssOrigin.multiplyScalar( 1 / _ssOrigin.w ); - - // screen space - _ssOrigin.x *= resolution.x / 2; - _ssOrigin.y *= resolution.y / 2; - _ssOrigin.z = 0; - - _ssOrigin3.copy( _ssOrigin ); - - _mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld ); - - for ( let i = 0, l = instanceStart.count; i < l; i ++ ) { - - _start4.fromBufferAttribute( instanceStart, i ); - _end4.fromBufferAttribute( instanceEnd, i ); - - _start4.w = 1; - _end4.w = 1; - - // camera space - _start4.applyMatrix4( _mvMatrix ); - _end4.applyMatrix4( _mvMatrix ); - - // skip the segment if it's entirely behind the camera - var isBehindCameraNear = _start4.z > near && _end4.z > near; - if ( isBehindCameraNear ) { - - continue; - - } - - // trim the segment if it extends behind camera near - if ( _start4.z > near ) { - - const deltaDist = _start4.z - _end4.z; - const t = ( _start4.z - near ) / deltaDist; - _start4.lerp( _end4, t ); - - } else if ( _end4.z > near ) { - - const deltaDist = _end4.z - _start4.z; - const t = ( _end4.z - near ) / deltaDist; - _end4.lerp( _start4, t ); - - } - - // clip space - _start4.applyMatrix4( projectionMatrix ); - _end4.applyMatrix4( projectionMatrix ); - - // ndc space [ - 1.0, 1.0 ] - _start4.multiplyScalar( 1 / _start4.w ); - _end4.multiplyScalar( 1 / _end4.w ); - - // screen space - _start4.x *= resolution.x / 2; - _start4.y *= resolution.y / 2; - - _end4.x *= resolution.x / 2; - _end4.y *= resolution.y / 2; - - // create 2d segment - _line.start.copy( _start4 ); - _line.start.z = 0; - - _line.end.copy( _end4 ); - _line.end.z = 0; - - // get closest point on ray to segment - const param = _line.closestPointToPointParameter( _ssOrigin3, true ); - _line.at( param, _closestPoint ); - - // check if the intersection point is within clip space - const zPos = MathUtils.lerp( _start4.z, _end4.z, param ); - const isInClipSpace = zPos >= - 1 && zPos <= 1; - - const isInside = _ssOrigin3.distanceTo( _closestPoint ) < lineWidth * 0.5; - - if ( isInClipSpace && isInside ) { - - _line.start.fromBufferAttribute( instanceStart, i ); - _line.end.fromBufferAttribute( instanceEnd, i ); - - _line.start.applyMatrix4( matrixWorld ); - _line.end.applyMatrix4( matrixWorld ); - - const pointOnLine = new Vector3(); - const point = new Vector3(); - - ray.distanceSqToSegment( _line.start, _line.end, point, pointOnLine ); - - intersects.push( { - - point: point, - pointOnLine: pointOnLine, - distance: ray.origin.distanceTo( point ), - - object: this, - face: null, - faceIndex: i, - uv: null, - uv2: null, - - } ); - - } - - } - - } - - } - - LineSegments2.prototype.LineSegments2 = true; - - class LineGeometry extends LineSegmentsGeometry { - - constructor() { - - super(); - this.type = 'LineGeometry'; - - } - - setPositions( array ) { - - // converts [ x1, y1, z1, x2, y2, z2, ... ] to pairs format - - var length = array.length - 3; - var points = new Float32Array( 2 * length ); - - for ( var i = 0; i < length; i += 3 ) { - - points[ 2 * i ] = array[ i ]; - points[ 2 * i + 1 ] = array[ i + 1 ]; - points[ 2 * i + 2 ] = array[ i + 2 ]; - - points[ 2 * i + 3 ] = array[ i + 3 ]; - points[ 2 * i + 4 ] = array[ i + 4 ]; - points[ 2 * i + 5 ] = array[ i + 5 ]; - - } - - super.setPositions( points ); - - return this; - - } - - setColors( array ) { - - // converts [ r1, g1, b1, r2, g2, b2, ... ] to pairs format - - var length = array.length - 3; - var colors = new Float32Array( 2 * length ); - - for ( var i = 0; i < length; i += 3 ) { - - colors[ 2 * i ] = array[ i ]; - colors[ 2 * i + 1 ] = array[ i + 1 ]; - colors[ 2 * i + 2 ] = array[ i + 2 ]; - - colors[ 2 * i + 3 ] = array[ i + 3 ]; - colors[ 2 * i + 4 ] = array[ i + 4 ]; - colors[ 2 * i + 5 ] = array[ i + 5 ]; - - } - - super.setColors( colors ); - - return this; - - } - - fromLine( line ) { - - var geometry = line.geometry; - - if ( geometry.isGeometry ) { - - console.error( 'THREE.LineGeometry no longer supports Geometry. Use THREE.BufferGeometry instead.' ); - return; - - } else if ( geometry.isBufferGeometry ) { - - this.setPositions( geometry.attributes.position.array ); // assumes non-indexed - - } - - // set colors, maybe - - return this; - - } - - } - - LineGeometry.prototype.isLineGeometry = true; - - class Line2 extends LineSegments2 { - - constructor( geometry = new LineGeometry(), material = new LineMaterial( { color: Math.random() * 0xffffff } ) ) { - - super( geometry, material ); - - this.type = 'Line2'; - - } - - } - - Line2.prototype.isLine2 = true; - - const MERCATOR_A = 6378137.0; - const WORLD_SIZE = MERCATOR_A * Math.PI * 2; - - const ThreeboxConstants = { - WORLD_SIZE: WORLD_SIZE, - PROJECTION_WORLD_SIZE: WORLD_SIZE / (MERCATOR_A * Math.PI * 2), - MERCATOR_A: MERCATOR_A, - DEG2RAD: Math.PI / 180, - RAD2DEG: 180 / Math.PI, - EARTH_CIRCUMFERENCE: 40075000 // In meters - }; - - function YToLat(Y) { - return (Math.atan(Math.pow(Math.E, ((Y / 111319.490778) * Math.PI) / 180.0)) * 360.0) / Math.PI - 90.0; - } - - function LatToScale(lat) { - return 1 / Math.cos((lat * Math.PI) / 180); - } - - function GetIntersectingObjects(camera, objects, width, height, x, y) { - // scale position to a percentage of the screen's width and height - const position = new Vector2(); - position.x = (x / width) * 2 - 1; - position.y = 1 - (y / height) * 2; - - const raycaster = new Raycaster(); - raycaster.setFromCamera(position, camera); - let intersects = raycaster.intersectObjects(objects, true); - - return intersects; - } - - function GetModel(modelId, children) { - for (let i = 0; i < children.length; i++) { - const element = children[i]; - if (element.type === 'Group') { - if (element.children) { - const model = GetModel(modelId, element.children); - if (model) { - return model; - } - } - } else if (element.type === 'Mesh') { - if (element.userData.b3dm === modelId) { - return element.parent; - } - } - } - } - - function projectedUnitsPerMeter(latitude) { - let c = ThreeboxConstants; - return Math.abs(c.WORLD_SIZE / Math.cos(c.DEG2RAD * latitude) / c.EARTH_CIRCUMFERENCE); - } - - function projectToWorld(coords) { - // Spherical mercator forward projection, re-scaling to WORLD_SIZE - let c = ThreeboxConstants; - var projected = [ - c.MERCATOR_A * c.DEG2RAD * coords[0] * c.PROJECTION_WORLD_SIZE, - c.MERCATOR_A * Math.log(Math.tan(Math.PI * 0.25 + 0.5 * c.DEG2RAD * coords[1])) * c.PROJECTION_WORLD_SIZE - ]; - - //z dimension, defaulting to 0 if not provided - if (!coords[2]) { - projected.push(0); - } else { - var pixelsPerMeter = projectedUnitsPerMeter(coords[1]); - projected.push(coords[2] * pixelsPerMeter); - } - - var result = new Vector3(projected[0], projected[1], projected[2]); - - return result; - } - - class Highlight { - constructor(scene, map) { - this.scene = scene; - this.map = map; - this.items = []; - } - - add(modelId, color, gradientColor, opacity, boxMargin) { - if (!modelId || this._isHighlighted(modelId)) { - return; - } - - const item = this._createHighlight(modelId, color, gradientColor, opacity, boxMargin); - if (!item) { - return; - } - - this._addToItems(item); - this.map.triggerRepaint(); - } - - remove(modelId) { - const highlighted = this._getHighlighted(modelId); - if (!highlighted) { - return; - } - - highlighted.model.remove(highlighted.highlight); - this._removeFromItems(modelId); - } - - clear() { - this.items.forEach((e) => { - this.remove(e.modelId); - }); - - this.items = []; - } - - _addToItems(item) { - this.items.push(item); - } - - _removeFromItems(modelId) { - this.items = this.items.filter((e) => { - return e.modelId !== modelId; - }); - } - - _isHighlighted(modelId) { - return this._getHighlighted(modelId) !== undefined; - } - - _getHighlighted(modelId) { - for (let i = 0; i < this.items.length; i++) { - if (this.items[i].modelId === modelId) { - return this.items[i]; - } - } - } - - _createHighlight(modelId, color, gradientColor, opacity, boxMargin) { - color = color ? color : '#4C162C'; - gradientColor = gradientColor ? gradientColor : '#ff4c94'; - opacity = opacity ? opacity : 0.5; - boxMargin = boxMargin ? boxMargin : 0.05; - - const model = GetModel(modelId, this.scene.children); - if (!model) { - return; - } - - const box = new Box3().setFromObject(model); - box.min = model.worldToLocal(box.min); - box.max = model.worldToLocal(box.max); - - const xAdd = boxMargin * box.max.x - boxMargin * box.min.x; - const yAdd = boxMargin * box.max.y - boxMargin * box.min.y; - const zAdd = boxMargin * box.max.z - boxMargin * box.min.z; - - const vertices = [ - [box.min.x - xAdd, box.min.y - yAdd, box.min.z - zAdd], - [box.min.x - xAdd, box.max.y + yAdd, box.min.z - zAdd], - [box.max.x + xAdd, box.max.y + yAdd, box.min.z - zAdd], - [box.max.x + xAdd, box.min.y - yAdd, box.min.z - zAdd], - [box.min.x - xAdd, box.min.y - yAdd, box.max.z + zAdd], - [box.min.x - xAdd, box.max.y + yAdd, box.max.z + zAdd], - [box.max.x + xAdd, box.max.y + yAdd, box.max.z + zAdd], - [box.max.x + xAdd, box.min.y - yAdd, box.max.z + zAdd] - ]; - - const planes = [ - this._createPlane([vertices[1], vertices[2], vertices[0], vertices[3]], color, gradientColor, opacity, 'back'), - this._createPlane([vertices[2], vertices[6], vertices[3], vertices[7]], color, gradientColor, opacity, 'back'), - this._createPlane([vertices[1], vertices[5], vertices[0], vertices[4]], color, gradientColor, opacity, 'back'), - this._createPlane([vertices[5], vertices[6], vertices[4], vertices[7]], color, gradientColor, opacity, 'back'), - this._createPlane([vertices[1], vertices[2], vertices[0], vertices[3]], color, gradientColor, opacity, 'front'), - this._createPlane([vertices[2], vertices[6], vertices[3], vertices[7]], color, gradientColor, opacity, 'front'), - this._createPlane([vertices[1], vertices[5], vertices[0], vertices[4]], color, gradientColor, opacity, 'front'), - this._createPlane([vertices[5], vertices[6], vertices[4], vertices[7]], color, gradientColor, opacity, 'front') - ]; - - const line = this._createLine( - [...vertices[1], ...vertices[2], ...vertices[6], ...vertices[5], ...vertices[1]], - gradientColor || color - ); - - const highlight = new Group(); - highlight.add(...planes); - highlight.add(line); - model.add(highlight); - - return { - modelId: modelId, - model: model, - highlight: highlight - }; - } - - _createPlane(vertices, color, gradientColor, opacity, side = 'front') { - const bufferGeom = new BufferGeometry(); - bufferGeom.setAttribute( - 'position', - new BufferAttribute( - new Float32Array([...vertices[0], ...vertices[1], ...vertices[2], ...vertices[3]]), - 3 - ) - ); - - bufferGeom.setIndex([0, 2, 1, 2, 3, 1]); - bufferGeom.computeVertexNormals(); - bufferGeom.computeBoundingBox(); - - const geom = new BufferGeometry().fromBufferGeometry(bufferGeom); - geom.faceVertexUvs = new PlaneGeometry().faceVertexUvs; - geom.uvsNeedUpdate = true; - - const material = new MeshLambertMaterial({ - color: color || '#1C5A6D', - transparent: true, - opacity: opacity, - side: side === 'back' ? BackSide : FrontSide - }); - material.defines = { USE_UV: '' }; - material.onBeforeCompile = (shader) => { - shader.uniforms.gradientColor = { - value: new Color(gradientColor || '#ffff00') - }; - shader.fragmentShader = ` - uniform vec3 gradientColor; - ${shader.fragmentShader.replace( - 'vec4 diffuseColor = vec4( diffuse, opacity );', - 'vec4 diffuseColor = vec4( mix(diffuse, gradientColor, vec3(vUv.y)), opacity);' - )}`; - }; - - const plane = new Mesh(geom, material); - return plane; - } - - _createLine(positions, color) { - const geometry = new LineGeometry(); - geometry.setPositions(positions); - - const matLine = new LineMaterial({ - color, - linewidth: 0.002, - dashed: false - }); - - const line = new Line2(geometry, matLine); - return line; - } - } - - class SVGObject extends Object3D { - - constructor( node ) { - - super(); - - this.node = node; - - } - - } - - SVGObject.prototype.isSVGObject = true; - - /* - mapbox-gl uses a camera fixed at the orgin (the middle of the canvas) The camera is only updated when rotated (bearing angle), - pitched or when the map view is resized. - When panning and zooming the map, the desired part of the world is translated and zoomed in front of the camera. The world is only updated when - the map is panned or zoomed. - - The mapbox-gl internal coordinate system has origin (0,0) located at longitude -180 degrees and latitude 0 degrees. - The scaling is 2^map.getZoom() * 512/EARTH_CIRCUMFERENCE_IN_METERS. At zoom=0 (scale=2^0=1), the whole world fits in 512 units. - */ - class CameraSync { - - constructor(map, camera, world) { - this.map = map; - this.camera = camera; - this.active = true; - this.updateCallback = null; - this.camera.matrixAutoUpdate = false; // We're in charge of the camera now! - - // Postion and configure the world group so we can scale it appropriately when the camera zooms - this.world = world || new Group(); - this.world.position.x = this.world.position.y = ThreeboxConstants.WORLD_SIZE / 2; - this.world.matrixAutoUpdate = false; - - //set up basic camera state - this.state = { - fov: 0.6435011087932844, // Math.atan(0.75); - translateCenter: new Matrix4(), - worldSizeRatio: 512 / ThreeboxConstants.WORLD_SIZE - }; - - this.state.translateCenter.makeTranslation( - ThreeboxConstants.WORLD_SIZE / 2, - -ThreeboxConstants.WORLD_SIZE / 2, - 0 - ); - - this.halfPitch = 1.57079632679; - - // Listen for move events from the map and update the Three.js camera. Some attributes only change when viewport resizes, so update those accordingly - this.updateCameraBound = () => this.updateCamera(); - this.map.on('move', this.updateCameraBound); - this.setupCameraBound = () => this.setupCamera(); - this.map.on('resize', () => this.setupCamera()); - - this.setupCamera(); - } - - detachCamera() { - this.map.off('move', this.updateCameraBound); - this.map.off('resize', this.setupCameraBound); - this.map.off('moveend', this.setupCameraBound); - this.updateCallback = null; - this.map = null; - this.camera = null; - } - - setupCamera() { - var t = this.map.transform; - let fov = t._fov; - const halfFov = fov / 2; - var cameraToCenterDistance = (0.5 / Math.tan(halfFov)) * t.height; - - this.state.cameraToCenterDistance = cameraToCenterDistance; - this.state.cameraTranslateZ = new Matrix4().makeTranslation(0, 0, cameraToCenterDistance); - - this.updateCamera(); - } - - updateCamera(ev) { - if (!this.camera) { - console.log('nocamera'); - return; - } - - var t = this.map.transform; - - // Recalculate pitch when going past 90 degrees to fix groundangle and distance - var pitch = t._pitch > this.halfPitch ? this.halfPitch - (t._pitch - this.halfPitch) : t._pitch; - let fov = t._fov; - var halfFov = fov / 2; - const groundAngle = Math.PI / 2 + pitch; - this.state.topHalfSurfaceDistance = - (Math.sin(halfFov) * this.state.cameraToCenterDistance) / Math.sin(Math.PI - groundAngle - halfFov); - - // Calculate z distance of the farthest fragment that should be rendered. - const furthestDistance = - Math.cos(Math.PI / 2 - pitch) * this.state.topHalfSurfaceDistance + this.state.cameraToCenterDistance; - - // Add a bit extra to avoid precision problems when a fragment's distance is exactly `furthestDistance` - const farZ = furthestDistance * 1.01; - - this.camera.aspect = t.width / t.height; - this.camera.projectionMatrix = this.makePerspectiveMatrix(t._fov, t.width / t.height, 1, farZ); - - var cameraWorldMatrix = new Matrix4(); - var rotatePitch = new Matrix4().makeRotationX(t._pitch); - var rotateBearing = new Matrix4().makeRotationZ(t.angle); - - // Unlike the Mapbox GL JS camera, separate camera translation and rotation out into its world matrix - // If this is applied directly to the projection matrix, it will work OK but break raycasting - cameraWorldMatrix.premultiply(this.state.cameraTranslateZ).premultiply(rotatePitch).premultiply(rotateBearing); - this.camera.matrixWorld.copy(cameraWorldMatrix); - - // Handle scaling and translation of objects in the map in the world's matrix transform, not the camera - let zoomPow = t.scale * this.state.worldSizeRatio; - let scale = new Matrix4(); - scale.makeScale(zoomPow, zoomPow, zoomPow); - - let translateMap = new Matrix4(); - translateMap.makeTranslation(-t.point.x, t.point.y, 0); - - const worldMatrix = new Matrix4(); - worldMatrix - .premultiply(this.state.translateCenter) - .premultiply(scale) - .premultiply(translateMap); - - this.world.matrix = worldMatrix; - - - // Threejs > 119 - let matrixWorldInverse = new Matrix4(); - matrixWorldInverse.copy(this.world.matrix).invert(); - - let projectionMatrixInverse = new Matrix4(); - projectionMatrixInverse.copy(this.camera.projectionMatrix).invert(); - - let cameraMatrixWorldInverse = new Matrix4(); - cameraMatrixWorldInverse.copy(this.camera.matrixWorld).invert(); - - - this.camera.projectionMatrixInverse = projectionMatrixInverse; - this.camera.matrixWorldInverse = cameraMatrixWorldInverse; - this.frustum = new Frustum(); - this.frustum.setFromProjectionMatrix( - new Matrix4().multiplyMatrices(this.camera.projectionMatrix, this.camera.matrixWorldInverse) - ); - - this.cameraPosition = new Vector3(0, 0, 0).unproject(this.camera).applyMatrix4(matrixWorldInverse); - // end - - // Threejs < 120 - /* let matrixWorldInverse = new THREE.Matrix4(); - matrixWorldInverse.getInverse(this.world.matrix); - - this.camera.projectionMatrixInverse.getInverse(this.camera.projectionMatrix); - this.camera.matrixWorldInverse.getInverse(this.camera.matrixWorld); - this.frustum = new THREE.Frustum(); - this.frustum.setFromProjectionMatrix( - new THREE.Matrix4().multiplyMatrices(this.camera.projectionMatrix, this.camera.matrixWorldInverse) - ); - - this.cameraPosition = new THREE.Vector3(0, 0, 0).unproject(this.camera).applyMatrix4(matrixWorldInverse); */ - // end - - if (this.updateCallback) { - // time: experimental - only update after zoom/pan is done. - // this.updateCallback(); - if (this.timeoutHandle) { - window.clearTimeout(this.timeoutHandle); - } - - this.timeoutHandle = window.setTimeout(() => { this.updateCallback(); }, 50); - } - } - - makePerspectiveMatrix(fovy, aspect, near, far) { - let out = new Matrix4(); - let f = 1.0 / Math.tan(fovy / 2), - nf = 1 / (near - far); - - let newMatrix = [f / aspect, 0, 0, 0, - 0, f, 0, 0, - 0, 0, (far + near) * nf, -1, - 0, 0, 2 * far * near * nf, 0]; - - out.elements = newMatrix; - return out; - } - } - - class TileLoader { - // This class contains the common code to load tile content, such as b3dm and pnts files. - // It is not to be used directly. Instead, subclasses are used to implement specific - // content loaders for different tile types. - constructor(url) { - this.url = url; - this.type = url.slice(-4); - this.version = null; - this.byteLength = null; - this.featureTableJSON = null; - this.featureTableBinary = null; - this.batchTableJson = null; - this.batchTableBinary = null; - this.binaryData = null; - } - - // TileLoader.load - async load() { - this.abortController = new AbortController(); - let response = await fetch(this.url, { signal: this.abortController.signal }); - this.abortController = null; - if (!response.ok) { - throw new Error(`HTTP ${response.status} - ${response.statusText}`); - } - let buffer = await response.arrayBuffer(); - let res = await this.parseResponse(buffer); - return res; - } - - abortLoad() { - if (this.abortController) { - this.abortController.abort(); - this.abortController = null; - return true; - } - return false; - } - - async parseResponse(buffer) { - let header = new Uint32Array(buffer.slice(0, 32)); - let decoder = new TextDecoder(); - let magic = decoder.decode(new Uint8Array(buffer.slice(0, 4))); - - if (magic != this.type) { - throw new Error(`Invalid magic string, expected '${this.type}', got '${this.magic}'`); - } - - this.version = header[1]; - this.byteLength = header[2]; - let featureTableJSONByteLength = header[3]; - let featureTableBinaryByteLength = header[4]; - let batchTableJsonByteLength = header[5]; - let batchTableBinaryByteLength = header[6]; - let gltfFormat = magic === 'i3dm' ? header[7] : 1; - - let pos = magic === 'i3dm' ? 32 : 28; // header length - if (featureTableJSONByteLength > 0) { - this.featureTableJSON = JSON.parse( - decoder.decode(new Uint8Array(buffer.slice(pos, pos + featureTableJSONByteLength))) - ); - pos += featureTableJSONByteLength; - } else { - this.featureTableJSON = {}; - } - - this.featureTableBinary = buffer.slice(pos, pos + featureTableBinaryByteLength); - pos += featureTableBinaryByteLength; - if (batchTableJsonByteLength > 0) { - this.batchTableJson = JSON.parse( - decoder.decode(new Uint8Array(buffer.slice(pos, pos + batchTableJsonByteLength))) - ); - pos += batchTableJsonByteLength; - } else { - this.batchTableJson = {}; - } - - this.batchTableBinary = buffer.slice(pos, pos + batchTableBinaryByteLength); - pos += batchTableBinaryByteLength; - - if (gltfFormat === 1) { - this.binaryData = buffer.slice(pos); - } else { - // load binary data from url at pos - this.modelUrl = decoder.decode(new Uint8Array(buffer.slice(pos))); - if (internalGLTFCache.has(this.modelUrl)) { - this.binaryData = internalGLTFCache.get(this.modelUrl); - } else { - let response = await fetch(this.modelUrl); - if (!response.ok) { - throw new Error(`HTTP ${response.status} - ${response.statusText}`); - } - this.binaryData = await response.arrayBuffer(); - internalGLTFCache.set(this.modelUrl, this.binaryData); - } - } - - return this; - } - - copy() { - return { - url: this.url, - type: this.type, - version: this.version, - byteLength: this.byteLength, - featureTableJSON: this.featureTableJSON, - featureTableBinary: this.featureTableBinary.slice(0), - batchTableJson: [...this.batchTableJson.attr], - batchTableBinary: this.batchTableBinary.slice(0), - binaryData: this.binaryData.slice(0), - }; - } - } - - class B3DM extends TileLoader { - constructor(url) { - super(url); - this.glbData = null; - } - async parseResponse(buffer) { - await super.parseResponse(buffer); - this.glbData = this.binaryData; - return this; - } - - copy() { - const copy = super.copy(); - copy.glbData = this.glbData.slice(0); - return copy; - } - } - - class CMPT extends TileLoader { - constructor(url) { - super(url); - } - async parseResponse(buffer) { - let header = new Uint32Array(buffer.slice(0, 4 * 4)); - let decoder = new TextDecoder(); - let magic = decoder.decode(new Uint8Array(buffer.slice(0, 4))); - if (magic != this.type) { - throw new Error(`Invalid magic string, expected '${this.type}', got '${this.magic}'`); - } - this.version = header[1]; - this.byteLength = header[2]; - this.tilesLength = header[3]; - let innerTiles = []; - let tileStart = 16; - for (let i = 0; i < this.tilesLength; i++) { - let tileHeader = new Uint32Array(buffer.slice(tileStart, tileStart + 3 * 4)); - let tileMagic = decoder.decode(new Uint8Array(buffer.slice(tileStart, tileStart + 4))); - //console.log(`innerTile: ${i}, magic: ${tileMagic}`); - let tileByteLength = tileHeader[2]; - let tileData = buffer.slice(tileStart, tileStart + tileByteLength); - innerTiles.push({ type: tileMagic, data: tileData }); - tileStart += tileByteLength; - } - return innerTiles; - } - } - - class PNTS extends TileLoader { - constructor(url) { - super(url); - this.points = new Float32Array(); - this.rgba = null; - this.rgb = null; - } - - parseResponse(buffer) { - super.parseResponse(buffer); - if (this.featureTableJSON.POINTS_LENGTH && this.featureTableJSON.POSITION) { - let len = this.featureTableJSON.POINTS_LENGTH; - let pos = this.featureTableJSON.POSITION.byteOffset; - this.points = new Float32Array( - this.featureTableBinary.slice(pos, pos + len * Float32Array.BYTES_PER_ELEMENT * 3) - ); - this.rtc_center = this.featureTableJSON.RTC_CENTER; - if (this.featureTableJSON.RGBA) { - pos = this.featureTableJSON.RGBA.byteOffset; - let colorInts = new Uint8Array( - this.featureTableBinary.slice(pos, pos + len * Uint8Array.BYTES_PER_ELEMENT * 4) - ); - let rgba = new Float32Array(colorInts.length); - for (let i = 0; i < colorInts.length; i++) { - rgba[i] = colorInts[i] / 255.0; - } - this.rgba = rgba; - } else if (this.featureTableJSON.RGB) { - pos = this.featureTableJSON.RGB.byteOffset; - let colorInts = new Uint8Array( - this.featureTableBinary.slice(pos, pos + len * Uint8Array.BYTES_PER_ELEMENT * 3) - ); - let rgb = new Float32Array(colorInts.length); - for (let i = 0; i < colorInts.length; i++) { - rgb[i] = colorInts[i] / 255.0; - } - this.rgb = rgb; - } else if (this.featureTableJSON.RGB565) { - console.error('RGB565 is currently not supported in pointcloud tiles.'); - } - } - return this; - } - } - - let internalGLTFCache = new Map(); - - const DebugColors = [ - new Color(0x605f8f), - new Color(0xf3aa51), - new Color(0xcee5d5), - new Color(0xff3000), - new Color(0xff8000), - new Color(0x44975c), - new Color(0xb7d3e9), - new Color(0xff50a0), - new Color(0xfc7572), - new Color(0x465b73), - new Color(0x0080ff), - new Color(0xf1c3aa), - new Color(0xffff00), - ]; - - function CreateDebugBox(transformation, box, color) { - let geom = new BoxGeometry(box[3] * 2, box[7] * 2, box[11] * 2); - let edges = new EdgesGeometry(geom); - let line = new LineSegments(edges, new LineBasicMaterial({ color: color })); - line.applyMatrix4(transformation); - - return line; - } - - function CreateDebugLabel(transformation, height, distance, msg, color) { - const spriteMaterial = MakeTextSprite(msg, color); - const sprite = new Sprite(spriteMaterial); - sprite.scale.set(distance, distance, distance); - sprite.applyMatrix4(transformation); - sprite.position.set(sprite.position.x, sprite.position.y, height); - sprite.position.set(sprite.position.x, sprite.position.y, height); - - return sprite; - } - - function CreateDebugLine(transformation, color) { - const material = new LineBasicMaterial({ - color: color - }); - - const points = []; - points.push(new Vector3(0, 0, 0)); - points.push(new Vector3(0, 0, 200)); - const geometry = new BufferGeometry().setFromPoints(points); - const line = new Line(geometry, material); - line.applyMatrix4(transformation); - - return line; - } - - function ThreeColorToByte(color) { - return Math.floor(color >= 1.0 ? 255 : color * 256.0); - } - - function MakeTextSprite(msg, color) { - var fontface = "Arial"; - var fontsize = 22; - var borderThickness = 4; - var borderColor = { r: ThreeColorToByte(color.r), g: ThreeColorToByte(color.g), b: ThreeColorToByte(color.b), a: 1.0 }; - var backgroundColor = { r: 255, g: 255, b: 255, a: 1.0 }; - var canvas = document.createElement('canvas'); - var context = canvas.getContext('2d'); - - context.font = "Bold " + fontsize + "px " + fontface; - - // get size data (height depends only on font size) - var metrics = context.measureText(msg); - var textWidth = metrics.width; - - context.fillStyle = "rgba(" + backgroundColor.r + "," + backgroundColor.g + "," + backgroundColor.b + "," + backgroundColor.a + ")"; - context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + "," + borderColor.b + "," + borderColor.a + ")"; - context.lineWidth = borderThickness; - RoundRect(context, borderThickness / 2, borderThickness / 2, textWidth + borderThickness, fontsize * 1.4 + borderThickness, 2); - - // text color - context.fillStyle = "rgba(0, 0, 0, 1.0)"; - context.fillText(msg, borderThickness, fontsize + borderThickness); - - // canvas contents will be used for a texture - var texture = new Texture(canvas); - texture.minFilter = LinearFilter; - texture.needsUpdate = true; - - var spriteMaterial = new SpriteMaterial({ map: texture, color: 0xffffff, sizeAttenuation: true }); - return spriteMaterial; - } - - function RoundRect(ctx, x, y, w, h, r) { - ctx.beginPath(); - ctx.moveTo(x + r, y); - ctx.lineTo(x + w - r, y); - ctx.quadraticCurveTo(x + w, y, x + w, y + r); - ctx.lineTo(x + w, y + h - r); - ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); - ctx.lineTo(x + r, y + h); - ctx.quadraticCurveTo(x, y + h, x, y + h - r); - ctx.lineTo(x, y + r); - ctx.quadraticCurveTo(x, y, x + r, y); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); - } - - async function IMesh(inmesh, instancesParams, inverseMatrix, modelName, castShadow, receiveShadow) { - /* intancesParams { - positions: float32[] - rtcCenter?: float32[3] - normalsRight?: float32[] - normalsUp?: float32[] - scales?: float32[] - xyzScales?: float32[] - } */ - let matrix = new Matrix4(); - let position = new Vector3(); - let rotation = new Euler(); - let quaternion = new Quaternion(); - let scale = new Vector3(); - let rtcCenter = instancesParams.rtcCenter ? instancesParams.rtcCenter : [0.0, 0.0, 0.0]; - - let geometry = inmesh.geometry; - geometry.applyMatrix4(inmesh.matrixWorld); // apply world modifiers to geometry - - let material = inmesh.material; - let positions = instancesParams.positions; - let instanceCount = positions.length / 3; - let instancedMesh = new InstancedMesh(geometry, material, instanceCount); - instancedMesh.modelType = "i3dm"; - instancedMesh.userData = inmesh.userData; - instancedMesh.model = modelName; - - if (instancesParams.rtcCenter) { - rtcCenter = instancesParams.rtcCenter; - } - - for (let i = 0; i < instanceCount; i++) { - position = { - x: positions[i * 3] + (rtcCenter[0] + inverseMatrix.elements[12]), - y: positions[i * 3 + 1] + (rtcCenter[1] + inverseMatrix.elements[13]), - z: positions[i * 3 + 2] + (rtcCenter[2] + inverseMatrix.elements[14]) - }; - if (instancesParams.normalsRight) { - rotation.set(0, 0, Math.atan2(instancesParams.normalsRight[i * 3 + 1], instancesParams.normalsRight[i * 3])); - quaternion.setFromEuler(rotation); - } - scale.x = scale.y = scale.z = LatToScale(YToLat(positions[i * 3 + 1])); - if (instancesParams.scales) { - scale.x *= instancesParams.scales[i]; - scale.y *= instancesParams.scales[i]; - scale.z *= instancesParams.scales[i]; - } - if (instancesParams.xyzScales) { - scale.x *= instancesParams.xyzScales[i * 3]; - scale.y *= instancesParams.xyzScales[i * 3 + 1]; - scale.z *= instancesParams.xyzScales[i * 3 + 2]; - } - matrix.compose(position, quaternion, scale); - instancedMesh.setMatrixAt(i, matrix); - instancedMesh.castShadow = castShadow; - instancedMesh.receiveShadow = receiveShadow; - } - - return instancedMesh; - } - - function applyStyle(scene, styleParams, renderOptions) { - const type = styleParams.type; - const settings = styleParams.settings; - - if (type) { - switch (type) { - case 'shade': - styleShade(scene, settings, renderOptions); - break; - case 'random': - styleRandom(scene, settings, renderOptions); - break; - case 'property': - styleProperty(scene, settings, renderOptions); - break; - case 'basic': - styleBasic(scene, settings, renderOptions); - break; - case 'shader': - styleShader(scene, settings, renderOptions); - break; - } - } - - return scene; - } - - function applyRenderOptions(scene, renderOptions) { - const stylableMeshes = getStylableMeshes(scene); - - for (let i = 0; i < stylableMeshes.length; i++) { - const child = stylableMeshes[i]; - applyMeshRenderOptions(child, renderOptions); - } - } - - function getStylableMeshes(scene) { - const meshes = []; - scene.traverse(child => { - if (child instanceof Mesh && child.stylable && child.stylable == true && child.geometry.attributes && child.geometry.attributes.position) { - meshes.push(child); - } - }); - - return meshes; - } - - function applyMeshRenderOptions(mesh, renderOptions) { - if (mesh instanceof Mesh) { - mesh.material.castShadow = renderOptions.castShadow; - mesh.material.receiveShadow = renderOptions.receiveShadow; - mesh.material.side = renderOptions.doubleSided ? DoubleSide : FrontSide; - - if (renderOptions.opacity) { - const opacity = renderOptions.opacity >= 1.0 ? 1.0 : renderOptions.opacity <= 0.0 ? 0.0 : renderOptions.opacity; - mesh.material.transparent = opacity === 1.0 ? false : true; - mesh.material.opacity = opacity; - } - } - } - - function styleBasic(scene, styleParams, renderOptions) { - const stylableMeshes = getStylableMeshes(scene); - - for (let i = 0; i < stylableMeshes.length; i++) { - const child = stylableMeshes[i]; - child.material = new MeshStandardMaterial({ - color: styleParams.color ? styleParams.color : "#EFEFEF" - }); - - applyMeshRenderOptions(child, renderOptions); - } - - return scene; - } - - function styleRandom(scene, styleParams, renderOptions) { - const stylableMeshes = getStylableMeshes(scene); - - for (let i = 0; i < stylableMeshes.length; i++) { - const child = stylableMeshes[i]; - const geom = child.geometry; - const count = geom.attributes.position.count; - geom.setAttribute('color', new BufferAttribute(new Float32Array(count * 3), 3)); - const colors = geom.attributes.color; - - let batchColors = {}; - - for (let i = 0; i < count; i++) { - const batchID = geom.attributes._batchid.getX(i); - - if (!batchColors[batchID]) { - batchColors[batchID] = { r: Math.random(), g: Math.random(), b: Math.random() }; - } - - colors.setXYZ(i, batchColors[batchID].r, batchColors[batchID].g, batchColors[batchID].b); - } - - child.material = new MeshStandardMaterial(); - child.material.vertexColors = true; - child.material.depthWrite = true; - applyMeshRenderOptions(child, renderOptions); - } - - return scene; - } - - function styleShader(scene, styleParams, renderOptions) { - const stylableMeshes = getStylableMeshes(scene); - const setAttributes = styleParams.setAttributes ? styleParams.setAttributes : undefined; - - for (let i = 0; i < stylableMeshes.length; i++) { - const child = stylableMeshes[i]; - - if(setAttributes !== undefined) { - setAttributes(child.geometry); - } - - child.material = styleParams.material; - applyMeshRenderOptions(child, renderOptions); - } - - return scene; - } - - function styleProperty(scene, styleParams, renderOptions) { - const stylableMeshes = getStylableMeshes(scene); - const property = styleParams.property; - const colorParams = styleParams.colors; - - for (let i = 0; i < stylableMeshes.length; i++) { - const child = stylableMeshes[i]; - const geom = child.geometry; - const count = geom.attributes.position.count; - geom.setAttribute('color', new BufferAttribute(new Float32Array(count * 3), 3)); - const colors = geom.attributes.color; - - let batchColors = {}; - - for (let i = 0; i < count; i++) { - const batchID = geom.attributes._batchid.getX(i); - - if (!batchColors[batchID]) { - const propertyValue = child.userData[property][batchID]; - - if (propertyValue) { - for (let j = 0; j < colorParams.length; j++) { - const colorParam = colorParams[j]; - const color = byteColorToRGBFloat(colorParam.color); - - switch (colorParam.operator) { - case "smaller-than": - if (propertyValue < colorParam.value) { - batchColors[batchID] = color; - } - break; - case "greater-than": - if (propertyValue > colorParam.value) { - batchColors[batchID] = color; - } - break; - case "range": - if (propertyValue >= colorParam.value[0] && propertyValue <= colorParam.value[1]) { - batchColors[batchID] = color; - } - break; - case "equals": - if (propertyValue.toString().toLowerCase() == colorParam.value.toString().toLowerCase()) { - batchColors[batchID] = color; - } - break; - } - } - } - - if (!batchColors[batchID] && styleParams.fallback) { - batchColors[batchID] = byteColorToRGBFloat(styleParams.fallback); - } - } - - colors.setXYZ(i, batchColors[batchID].r, batchColors[batchID].g, batchColors[batchID].b); - } - - child.material = new MeshStandardMaterial(); - child.material.vertexColors = true; - child.material.depthWrite = true; - applyMeshRenderOptions(child, renderOptions); - } - - return scene; - } - - function styleShade(scene, styleParams, renderOptions) { - let maincolor = null; - if (styleParams.color != null) { - maincolor = createColor(styleParams.color); - } - - const stylableMeshes = getStylableMeshes(scene); - - for (let i = 0; i < stylableMeshes.length; i++) { - const child = stylableMeshes[i]; - - if (styleParams.color != null) { - child.material.color = maincolor; - } - if (styleParams.opacity != null) { - child.material.opacity = styleParams.opacity; - child.material.transparent = styleParams.opacity < 1.0 ? true : false; - } - - // some gltf has wrong bounding data, recompute here - child.geometry.computeBoundingBox(); - child.geometry.computeBoundingSphere(); - - //For changing individual colors later, we have to introduce vertexcolors - //const color = new THREE.Color(); - const positions = child.geometry.attributes.position; - const count = positions.count; - child.geometry.setAttribute('color', new BufferAttribute(new Float32Array(count * 3), 3)); - const colors = child.geometry.attributes.color; - const color = new Color(); - const grey = new Color("rgb(20,20,20)"); - const ymin = child.geometry.boundingBox.min.y; - child.geometry.boundingBox.max.y; - //Currently attributes are kind of hardcoded in the tiles and have to be unpacked - //let magnitude = scaleSequential(interpolateYlGnBu).domain([1600, 2020]) - //const colormap = child.parent.userData.attr.map(d=>magnitude(d[0])); - for (let i = 0; i < count; i++) { - //Assign every vertex it's own color - - //let batchid = child.geometry.attributes._batchid.getX(i); - //let colorval = colormap[batchid]; - let colorval = child.material.color; - color.set(colorval); - //Create a little gradient from black to white - //adding 0.3 not to start at black, dividing by 10 limits effect to bottom - let greyval = Math.min(0.8 + (positions.getY(i) + Math.abs(ymin)) / 1, 1); - color.lerp(grey, 1 - greyval); //lerp to grey - const linear = color.convertSRGBToLinear(); - colors.setXYZ(i, linear.r, linear.g, linear.b); - } - - child.material = new MeshStandardMaterial(); - child.material.vertexColors = true; - child.material.depthWrite = !child.material.transparent; // necessary for Velsen dataset? - applyMeshRenderOptions(child, renderOptions); - } - - if (styleParams.color != null || styleParams.opacity != null) { - let color = createColor(styleParams.color); - for (let i = 0; i < stylableMeshes.length; i++) { - const child = stylableMeshes[i]; - if (styleParams.color != null) - child.material.color = color; - - if (styleParams.opacity != null) { - child.material.opacity = styleParams.opacity; - child.material.transparent = styleParams.opacity < 1.0 ? true : false; - } - } - } - if (styleParams.debugColor) { - for (let i = 0; i < stylableMeshes.length; i++) { - const child = stylableMeshes[i]; - child.material.color = styleParams.debugColor; - } - } - return scene; - } - - function byteColorToRGBFloat(color) { - const threeColor = new Color(`rgb(${color[0]}, ${color[1]}, ${color[2]})`); - return threeColor.convertSRGBToLinear(); - } - - function createColor(color) { - return new Color(color).convertSRGBToLinear(); - } - - class ThreeDeeTile { - constructor(json, resourcePath, styleParams, updateCallback, renderCallback, parentRefine, parentTransform, projectToMercator, loader, renderOptions) { - this.loaded = false; - this.styleParams = styleParams; - this.updateCallback = updateCallback; - this.renderCallback = renderCallback; - this.resourcePath = resourcePath; - this.projectToMercator = projectToMercator; - this.loader = loader; - this.totalContent = new Group(); // Three JS Object3D Group for this tile and all its children - this.tileContent = new Group(); // Three JS Object3D Group for this tile's content - this.childContent = new Group(); // Three JS Object3D Group for this tile's children - this.totalContent.add(this.tileContent); - this.totalContent.add(this.childContent); - this.boundingVolume = json.boundingVolume; - this.tileContentVisible = false; - this.renderOptions = renderOptions; - - if (this.boundingVolume && this.boundingVolume.box) { - let b = this.boundingVolume.box; - let extent = [b[0] - b[3], b[1] - b[7], b[0] + b[3], b[1] + b[7]]; - let sw = new Vector3(extent[0], extent[1], b[2] - b[11]); - let ne = new Vector3(extent[2], extent[3], b[2] + b[11]); - this.box = new Box3(sw, ne); - } else { - this.extent = null; - this.sw = null; - this.ne = null; - this.box = null; - this.center = null; - } - - this.refine = json.refine ? json.refine.toUpperCase() : parentRefine; - this.geometricError = json.geometricError; - this.worldTransform = parentTransform ? parentTransform.clone() : new Matrix4(); - this.transform = json.transform; - - if (this.transform) { - let tileMatrix = new Matrix4().fromArray(this.transform); - this.totalContent.applyMatrix4(tileMatrix); - this.worldTransform.multiply(tileMatrix); - } - - this.content = json.content; - this.children = []; - - if (json.children) { - for (let i = 0; i < json.children.length; i++) { - let child = new ThreeDeeTile( - json.children[i], - resourcePath, - this.styleParams, - updateCallback, - renderCallback, - this.refine, - this.worldTransform, - this.projectToMercator, - this.loader, - this.renderOptions - ); - this.childContent.add(child.totalContent); - this.children.push(child); - } - } - } - - //ThreeDeeTile.load - async load() { - if (this.loaded) { - return this.loaded; - } - - if (this.loading) { - return this.loaded; - } - - this.loading = true; - - if (this.content) { - let url = this.content.uri ? this.content.uri : this.content.url; - - if (!url) { - this.loading = false; - this.loaded = true; - return this.loaded; - } - - if (url.substr(0, 4) != 'http') { - url = this.resourcePath + url; - } - - let type = url.slice(-4); - switch (type) { - case 'json': - // child is a tileset json - this.isParentTileset = true; - this.originalBox = this.box.clone(); - this.originalWorldTransform = this.worldTransform.clone(); - - try { - let subTileset = new Tileset((ts) => this.updateCallback(ts), () => this.renderCallback(), this.loader); - await subTileset.load(url, this.styleParams, this.projectToMercator, this.renderOptions); - //console.log(`loaded json from url ${url}`); - if (subTileset.root) { - this.children.push(subTileset.root); - subTileset.root.box.applyMatrix4(this.worldTransform); - this.childContent.add(subTileset.root.totalContent); - // Threejs > 119 - let inverseMatrix = new Matrix4(); - inverseMatrix.copy(this.worldTransform).invert(); - //end - - // Threejs < 120 - //let inverseMatrix = new THREE.Matrix4().getInverse(this.worldTransform); - //end - - subTileset.root.totalContent.applyMatrix4(inverseMatrix); - subTileset.root.totalContent.updateMatrixWorld(); - await subTileset.root.checkLoad(this.frustum, this.cameraPosition, subTileset.geometricError); - } - } catch (error) { - // load failed (wrong url? connection issues?) - // log error, do not break program flow - console.error(error); - } - break; - case 'b3dm': - try { - this.tileLoader = new B3DM(url); - let b3dmData = await this.tileLoader.load(); - this.tileLoader = null; - this.b3dmAdd(b3dmData, url); - } catch (error) { - if (error.name === "AbortError") { - //console.log(`cancelled ${url}`) - this.loading = false; - this.loaded = false; - return this.loaded; - } - - console.error(error); - } - break; - case 'i3dm': - try { - this.tileLoader = new B3DM(url); - let i3dmData = await this.tileLoader.load(); - this.tileLoader = null; - this.i3dmAdd(i3dmData); - } catch (error) { - if (error.name === "AbortError") { - this.loading = false; - this.loaded = false; - return this.loaded; - } - - console.error(error.message); - } - break; - case 'pnts': - try { - this.tileLoader = new PNTS(url); - let pointData = await this.tileLoader.load(); - this.tileLoader = null; - this.pntsAdd(pointData); - } catch (error) { - if (error.name === "AbortError") { - this.loading = false; - this.loaded = false; - return this.loaded; - } - - console.error(error); - } - break; - case 'cmpt': - try { - this.tileLoader = new CMPT(url); - let compositeTiles = await this.tileLoader.load(); - this.tileLoader = null; - this.cmptAdd(compositeTiles, url); - } catch (error) { - if (error.name === "AbortError") { - this.loading = false; - this.loaded = false; - return this.loaded; - } - - console.error(error); - } - break; - default: - throw new Error('invalid tile type: ' + type); - } - } - - this.loading = false; - this.loaded = true; - this.updateCallback(this); - - return this.loaded; - } - - async cmptAdd(compositeTiles, url) { - if (this.cmptAdded) { - // prevent duplicate adding - return; - } - - this.cmptAdded = true; - for (let innerTile of compositeTiles) { - switch (innerTile.type) { - case 'i3dm': - let i3dm = new B3DM('.i3dm'); - let i3dmData = await i3dm.parseResponse(innerTile.data); - this.i3dmAdd(i3dmData); - break; - case 'b3dm': - let b3dm = new B3DM('.b3dm'); - let b3dmData = await b3dm.parseResponse(innerTile.data); - this.b3dmAdd(b3dmData, url.slice(0, -4) + 'b3dm'); - break; - case 'pnts': - let pnts = new PNTS('.pnts'); - let pointData = pnts.parseResponse(innerTile.data); - this.pntsAdd(pointData); - break; - case 'cmpt': - let cmpt = new CMPT('.cmpt'); - let subCompositeTiles = cmpt.parseResponse(innerTile.data); - this.cmptAdd(subCompositeTiles); - break; - default: - console.error(`Composite type ${innerTile.type} not supported`); - break; - } - //console.log(`type: ${innerTile.type}, size: ${innerTile.data.byteLength}`); - } - } - - pntsAdd(pointData) { - if (this.pntsAdded && !this.cmptAdded) { - // prevent duplicate adding - return; - } - - this.pntsAdded = true; - let geometry = new BufferGeometry(); - geometry.setAttribute('position', new Float32BufferAttribute(pointData.points, 3)); - let material = new PointsMaterial(); - material.size = this.styleParams.pointsize != null ? this.styleParams.pointsize : 1.0; - if (this.styleParams.color) { - material.vertexColors = NoColors; - material.color = new Color(this.styleParams.color); - material.opacity = this.styleParams.opacity != null ? this.styleParams.opacity : 1.0; - } else if (pointData.rgba) { - geometry.setAttribute('color', new Float32BufferAttribute(pointData.rgba, 4)); - material.vertexColors = VertexColors; - } else if (pointData.rgb) { - geometry.setAttribute('color', new Float32BufferAttribute(pointData.rgb, 3)); - material.vertexColors = VertexColors; - } - - this.tileContent.add(new Points(geometry, material)); - - if (pointData.rtc_center) { - let c = pointData.rtc_center; - this.tileContent.applyMatrix4(new Matrix4().makeTranslation(c[0], c[1], c[2])); - } - - this.tileContent.add(new Points(geometry, material)); - this.renderCallback(this); - } - - b3dmAdd(b3dmData, url) { - if (this.b3dmAdded && !this.cmptAdded) { - // prevent duplicate adding - return; - } - - this.b3dmAdded = true; - - this.loader.parse( - b3dmData.glbData, - this.resourcePath, - (gltf) => { - let scene = gltf.scene || gltf.scenes[0]; - let rotateX = new Matrix4().makeRotationAxis(new Vector3(1, 0, 0), Math.PI / 2); - scene.applyMatrix4(rotateX); // convert from GLTF Y-up to Z-up - - //Add the batchtable to the userData since gltfLoader doesn't deal with it - scene.userData = b3dmData.batchTableJson; - scene.userData.b3dm = url.replace(this.resourcePath, '').replace('.b3dm', ''); - - if (scene.userData && Array.isArray(b3dmData.batchTableJson.attr)) { - scene.userData.attr = scene.userData.attr.map((d) => d.split(',')); - delete b3dmData.batchTableJson.attr; - } - - if (scene.userData && Array.isArray(b3dmData.batchTableJson.featureinfo)) { - for (let i = 0; i < scene.userData.featureinfo.length; i++) { - const data = JSON.parse(scene.userData.featureinfo[i]); - let keys = Object.keys(data); - if (keys.length) { - for (let key of keys) { - if (!scene.userData[key]) { - scene.userData[key] = []; - } - - scene.userData[key].push(data[key]); - } - } - } - - delete b3dmData.batchTableJson.featureinfo; - } - - if (this.projectToMercator) { - //TODO: must be a nicer way to get the local Y in webmerc. than worldTransform.elements - scene.scale.setScalar(LatToScale(YToLat(this.worldTransform.elements[13]))); - } - - scene.traverse((child) => { - if (child instanceof Mesh) { - child.stylable = true; - child.castShadow = this.renderOptions.castShadow; - child.receiveShadow = this.renderOptions.receiveShadow; - child.userData = scene.userData; - child.modelType = "b3dm"; - - if (this.styleParams && Object.keys(this.styleParams).length > 0) { - child.material = new MeshStandardMaterial({ - color: '#ffffff' - }); - } - } - }); - - if (this.styleParams && Object.keys(this.styleParams).length > 0) { - this.appliedStyle = this.styleParams.id; - scene = applyStyle(scene, this.styleParams, this.renderOptions); - } - if (this.renderOptions.doubleSided) { - scene.traverse(child=>{ - if (child instanceof Mesh) { - child.material.side = DoubleSide; - } - }); - } - - this.tileContent.add(scene); - this.renderCallback(); - }, - (error) => { - throw new Error('error parsing gltf: ' + error); - } - ); - } - - i3dmAdd(i3dmData) { - if (this.i3dmAdded && !this.cmptAdded) { - // prevent duplicate adding - return; - } - - this.i3dmAdded = true; - - // Check what metadata is present in the featuretable, currently using: https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/Instanced3DModel#instance-orientation. - let metadata = i3dmData.featureTableJSON; - if (!metadata.POSITION) { - console.error(`i3dm missing position metadata`); - return; - } - - let instancesParams = { - positions: new Float32Array(i3dmData.featureTableBinary, metadata.POSITION.byteOffset, metadata.INSTANCES_LENGTH * 3) - }; - - if (metadata.RTC_CENTER) { - if (Array.isArray(metadata.RTC_CENTER) && metadata.RTC_CENTER.length === 3) { - instancesParams.rtcCenter = [metadata.RTC_CENTER[0], metadata.RTC_CENTER[1], metadata.RTC_CENTER[2]]; - } - } - - if (metadata.NORMAL_UP && metadata.NORMAL_RIGHT) { - instancesParams.normalsRight = new Float32Array(i3dmData.featureTableBinary, metadata.NORMAL_RIGHT.byteOffset, metadata.INSTANCES_LENGTH * 3); - instancesParams.normalsUp = new Float32Array(i3dmData.featureTableBinary, metadata.NORMAL_UP.byteOffset, metadata.INSTANCES_LENGTH * 3); - } - - if (metadata.SCALE) { - instancesParams.scales = new Float32Array(i3dmData.featureTableBinary, metadata.SCALE.byteOffset, metadata.INSTANCES_LENGTH); - } - - if (metadata.SCALE_NON_UNIFORM) { - instancesParams.xyzScales = new Float32Array(i3dmData.featureTableBinary, metadata.SCALE_NON_UNIFORM.byteOffset, metadata.INSTANCES_LENGTH); - } - - // Threejs > 119 - let inverseMatrix = new Matrix4(); - inverseMatrix.copy(this.worldTransform).invert(); // in order to offset by the tile - // end - - // Threejs < 120 - //let inverseMatrix = new THREE.Matrix4().getInverse(this.worldTransform); - // end - - let self = this; - - this.loader.parse(i3dmData.glbData, this.resourcePath, (gltf) => { - let scene = gltf.scene || gltf.scenes[0]; - scene.rotateX(Math.PI / 2); // convert from GLTF Y-up to Mapbox Z-up - scene.updateMatrixWorld(true); - - scene.traverse(child => { - if (child instanceof Mesh) { - child.userData = i3dmData.batchTableJson; - IMesh(child, instancesParams, inverseMatrix, i3dmData.modelUrl, self.renderOptions.castShadow, self.renderOptions.receiveShadow) - .then(d => self.tileContent.add(d)); - - //const d = this._createMesh(child, instancesParams, inverseMatrix, i3dmData.modelUrl, self.castShadow, self.receiveShadow); - //self.tileContent.add(d); - } - }); - }); - - this.renderCallback(this); - } - - _createMesh(inmesh, instancesParams, inverseMatrix, modelName, castShadow, receiveShadow) { - const group = new Scene(); - - let matrix = new Matrix4(); - let position = new Vector3(); - let rotation = new Euler(); - let quaternion = new Quaternion(); - let scale = new Vector3(); - let rtcCenter = instancesParams.rtcCenter ? instancesParams.rtcCenter : [0.0, 0.0, 0.0]; - - let geometry = inmesh.geometry; - geometry.applyMatrix4(inmesh.matrixWorld); // apply world modifiers to geometry - - let material = inmesh.material; - let positions = instancesParams.positions; - let instanceCount = positions.length / 3; - //let instancedMesh = new THREE.InstancedMesh(geometry, material, instanceCount); - let instancedMesh = new Mesh(geometry, material); - instancedMesh.modelType = "i3dm"; - //instancedMesh.userData = inmesh.userData; - instancedMesh.model = modelName; - - if (instancesParams.rtcCenter) { - rtcCenter = instancesParams.rtcCenter; - } - - for (let i = 0; i < instanceCount; i++) { - position = { - x: positions[i * 3] + (rtcCenter[0] + inverseMatrix.elements[12]), - y: positions[i * 3 + 1] + (rtcCenter[1] + inverseMatrix.elements[13]), - z: positions[i * 3 + 2] + (rtcCenter[2] + inverseMatrix.elements[14]) - }; - if (instancesParams.normalsRight) { - rotation.set(0, 0, Math.atan2(instancesParams.normalsRight[i * 3 + 1], instancesParams.normalsRight[i * 3])); - quaternion.setFromEuler(rotation); - } - scale.x = scale.y = scale.z = LatToScale(YToLat(positions[i * 3 + 1])); - if (instancesParams.scales) { - scale.x *= instancesParams.scales[i]; - scale.y *= instancesParams.scales[i]; - scale.z *= instancesParams.scales[i]; - } - if (instancesParams.xyzScales) { - scale.x *= instancesParams.xyzScales[i * 3]; - scale.y *= instancesParams.xyzScales[i * 3 + 1]; - scale.z *= instancesParams.xyzScales[i * 3 + 2]; - } - matrix.compose(position, quaternion, scale); - - const clone = instancedMesh.clone(); - clone.applyMatrix4(matrix); - clone.castShadow = castShadow; - clone.receiveShadow = receiveShadow; - - group.add(clone); - } - - return group; - } - - _hide() { - //time: sometimes tiles are not removed with this check - //if (this.tileContentVisible === true) { - this.totalContent.remove(this.tileContent); - this.tileContentVisible = false; - //} - } - - _hideChildren() { - if (this.childContentVisible) { - this.totalContent.remove(this.childContent); - this.childContentVisible = false; - } - } - - _show() { - if (this.tileContentVisible === false) { - this.tileContentVisible = true; - - if (this.appliedStyle != this.styleParams.id) { - applyStyle(this.tileContent, this.styleParams, this.renderOptions); - } - - this.totalContent.add(this.tileContent); - } - } - - _exposeChildren() { - if (!this.childContentVisible) { - this.totalContent.add(this.childContent); - this.childContentVisible = true; - } - } - - _remove(includeChildren) { - if (includeChildren) { - for (const child of this.children) { - child._remove(includeChildren); - } - } - - if (this.loading) { - if (this.tileLoader) { - this.tileLoader.abortLoad(); - } - this.loading = false; - } - - if (!this.loaded) { - return; - } - - this.loaded = false; - this.unloadedTileContent = true; - this.tileContentVisible = false; - this.totalContent.remove(this.tileContent); - this.freeObjectFromMemory(this.tileContent); - this.tileContent = new Group(); - this.totalContent.add(this.tileContent); - this.b3dmAdded = false; - this.i3dmAdded = false; - this.cmptAdded = false; - - if (includeChildren) { - this.unloadedChildContent = true; - this.totalContent.remove(this.childContent); - this.freeObjectFromMemory(this.childContent); - this.totalContent.add(this.childContent); // add empty childContent to totalContent - - if (this.isParentTileset) { - this.children = []; - this.isParentTileset = false; - this.unloadedChildContent = false; - this.unloadedTileContent = false; - } - } - } - - unload(includeChildren) { - if (this.tileLoader) { - this.tileLoader.abortLoad(); - } - - this._remove(includeChildren); - } - - hasLoadingChildren(node) { - if (node.inView && node.children.length) { - for (const child of node.children) { - if (child.loading) { - return true; - } - - if (this.hasLoadingChildren(child)) { - return true; - } - } - } - - return false; - } - - async checkLoad(frustum, cameraPosition, maxGeometricError) { - this.frustum = frustum; - this.cameraPosition = cameraPosition; - let transformedBox = this.box.clone(); - transformedBox.applyMatrix4(this.totalContent.matrixWorld); - - // is this tile inside the view? - if (!frustum.intersectsBox(transformedBox)) { - this.inView = false; - this._hide(); - this._hideChildren(); - this.unload(true); - - return false; - } - - this.inView = true; - //console.log(`checkLoad: ${this.content?this.content.uri:this.children.length?`parent of ${this.children[0].content.uri}`:'empty leaf'}`) - - let worldBox = this.box.clone().applyMatrix4(this.worldTransform); - let dist = worldBox.distanceToPoint(cameraPosition); - - if (this.renderOptions.horizonClip) { - let horizon = Math.round(Math.sqrt(Math.abs(cameraPosition.z)) * this.renderOptions.horizonFactor); - if (dist > horizon) { - this._hide(); - this._hideChildren(); - return false; - } - } - - const error = Math.sqrt(dist) * 10; - //const height = Math.abs(cameraPosition.z); - //let mod = (height >= 1400 ? 1 : (1400 / (height)) * 1.1); - const mod = 1; - - const renderError = error; - const modMax = maxGeometricError / mod; - const modLocal = this.geometricError / mod; - - //console.log(`dist: ${dist}, renderError: ${renderError}`); - if (renderError > modMax) { - // tile too far - this._hide(); - this._hideChildren(); - } else if (renderError > modLocal) { - // tile in range - this.load(); - this._show(); - - if (this.loading) { - return; - } - - // update children for range - for (const child of this.children) { - if (child.geometricError < modLocal) { - child.checkLoad(frustum, cameraPosition, this.geometricError); - } else { - child.checkLoad(frustum, cameraPosition, maxGeometricError); - } - } - - this._exposeChildren(); - } else if (renderError <= modLocal) { - this._exposeChildren(); - - for (let child of this.children) { - if (this.refine === 'REPLACE') { - // show all immediate children, including those that are further away due to oblique viewing - await child.checkLoad(frustum, cameraPosition, maxGeometricError); - } else { - // add children depending on viewing distance - if (child.geometricError < modLocal) { - await child.checkLoad(frustum, cameraPosition, this.geometricError); - } else { - await child.checkLoad(frustum, cameraPosition, maxGeometricError); - } - } - } - if (this.refine === 'REPLACE' && modLocal > 0) { - if (!this.hasLoadingChildren(this)) { - this._hide(); - } - } else { - this.load(); - this._show(); - } - } - - return true; - } - - disposeObject(obj) { - if (obj.material && obj.material.dispose) { - obj.material.dispose(); - - if (obj.material.map) { - obj.material.map.dispose(); - } - } - - if (obj.geometry && obj.geometry.dispose) { - obj.geometry.attributes.color = {}; - obj.geometry.attributes.normal = {}; - obj.geometry.attributes.position = {}; - obj.geometry.attributes.uv = {}; - obj.geometry.attributes._batchid = {}; - obj.geometry.attributes = {}; - obj.geometry.dispose(); - obj.material = {}; - } - } - - freeObjectFromMemory(object) { - object.traverse(obj => { - this.disposeObject(obj); - }); - this.disposeObject(object); - } - - _updateDebugGroup(distance) { - if (this.tileContentVisible === false) { - this._removeDebugGroup(); - return; - } else { - this._addDebugGroup(); - } - - const debugColor = this._getDebugColor(); - const volumeBox = this.boundingVolume.box; - const translation = new Matrix4().makeTranslation(volumeBox[0], volumeBox[1], volumeBox[2]); - - if (!this.debugGroup) { - const box = CreateDebugBox(translation, volumeBox, debugColor); - const line = CreateDebugLine(translation, debugColor); - this.debugGroup = new Scene(); - this.debugGroup.add(box); - this.debugGroup.add(line); - } - - this.debugGroup.remove(this.sprite); - const tileTitle = this._getTileTitle(); - const msg = " " + tileTitle + " - " + distance.toFixed(0) + " "; - this.sprite = CreateDebugLabel(translation, volumeBox[11], distance, msg, debugColor); - this.debugGroup.add(this.sprite); - this.renderCallback(this); - } - - _getTileTitle() { - let title = ""; - if (this.content) { - title = this.content.uri ? this.content.uri : this.content.url; - title = title.split('/')[1]; - } - - return title; - } - - _getDebugColor() { - const parents = this._getParentCount(this.totalContent); - return DebugColors[parents]; - } - - _getParentCount(o, count = 0) { - if (o.parent) { - count++; - return this._getParentCount(o.parent, count); - } - - return count; - } - - _addDebugGroup() { - if (this.debugGroup && !this.debugAdded) { - this.debugAdded = true; - this.totalContent.add(this.debugGroup); - } - } - - _removeDebugGroup() { - if (this.debugGroup && this.debugAdded) { - this.totalContent.remove(this.debugGroup); - this.debugAdded = false; - } - } - } - - class Tileset { - - constructor(updateCallback, renderCallback, loader) { - if (!updateCallback) { updateCallback = () => {}; } - this.updateCallback = updateCallback; - this.renderCallback = renderCallback; - this.url = null; - this.version = null; - this.gltfUpAxis = 'Z'; - this.geometricError = null; - this.root = null; - this.loader = loader; - } - - // TileSet.load - async load(url, styleParams, projectToMercator, renderOptions) { - this.url = url; - let resourcePath = LoaderUtils.extractUrlBase(url); - - let response = await fetch(this.url); - if (!response.ok) { - throw new Error(`HTTP ${response.status} - ${response.statusText}`); - } - let json = await response.json(); - this.version = json.asset.version; - this.geometricError = json.geometricError; - this.refine = json.root.refine ? json.root.refine.toUpperCase() : 'ADD'; - this.root = new ThreeDeeTile( - json.root, - resourcePath, - styleParams, - this.updateCallback, - this.renderCallback, - this.refine, - null, - projectToMercator, - this.loader, - renderOptions - ); - - return; - } - } - - /* - When Subsurface is active in Mapbox3DTiles listen on camera going below or above the surface - Add a plane when going below the surface, show the layer - Remove the surface plane when going above surface, hide the layer - */ - class Subsurface { - constructor(map, world, cameraSync) { - this.map = map; - this.world = world; - this.cameraSync = cameraSync; - this.map.on('move', () => this._cameraUpdated()); - this._setBelowSurfaceState(); - } - - _cameraUpdated() { - this.cameraHeight = this.cameraSync.cameraPosition.z; - - if (!this.belowSurface && this.cameraHeight < 0 || this.belowSurface && this.cameraHeight >= 0) { - this._setBelowSurfaceState(); - } - } - - _setBelowSurfaceState() { - const newState = this.cameraHeight < 0 ? true : false; - if(this.belowSurface && newState == this.belowSurface){ - return; - } - - this.belowSurface = newState; - this._update(this.belowSurface); - } - - _update(belowSurface) { - if(belowSurface){ - this._showLayer(); - }else { - this._hideLayer(); - } - } - - _showLayer() { - this.world.visible = true; - } - - _hideLayer() { - this.world.visible = false; - } - } - - class TilesetRenderOptions { - - constructor(params, changed) { - if(!params) { - params = {}; - } - - this.opacity = params.hasOwnProperty('opacity') ? params.opacity : 1.0; - this.horizonClip = params.hasOwnProperty('horizonClip') ? params.horizonClip : false; - this.horizonFactor = params.hasOwnProperty('horizonFactor') ? params.horizonFactor : 200; - this.castShadow = params.hasOwnProperty('castShadow') ? params.castShadow : true; - this.receiveShadow = params.hasOwnProperty('receiveShadow') ? params.receiveShadow : true; - this.doubleSided = params.hasOwnProperty('doubleSided') ? params.doubleSided : false; - this._changed = changed; - } - - get opacity() { - return this["_opacity"]; - } - - set opacity(value) { - if (!isNaN(value)) { - this.updateValue("_opacity", value); - } - } - - get horizonClip() { - return this["_horizonClip"]; - } - - set horizonClip(value) { - if (typeof value === 'boolean') { - this.updateValue("_horizonClip", value); - } - } - - get horizonFactor() { - return this["_horizonFactor"]; - } - - set horizonFactor(value) { - if (!isNaN(value)) { - this.updateValue("_horizonFactor", value); - } - } - - get castShadow() { - return this["_castShadow"]; - } - - set castShadow(value) { - if (typeof value === 'boolean') { - this.updateValue("_castShadow", value); - } - } - - get receiveShadow() { - return this["_receiveShadow"]; - } - - set receiveShadow(value) { - if (typeof value === 'boolean') { - this.updateValue("_receiveShadow", value); - } - } - - get doubleSided() { - return this["_doubleSided"]; - } - - set doubleSided(value) { - if (typeof value === 'boolean') { - this.updateValue("_doubleSided", value); - } - } - - updateValue(propertyName, newVal) { - if(newVal === undefined) { - return; - } - - if(this[propertyName] != newVal) { - this[propertyName] = newVal; - - if(this._changed !== undefined) { - this._changed(); - } - } - } - } - - class TilesetLayer extends Scene { - - constructor(map, loader, cameraSync, settings, renderCallback) { - super(); - - if (!settings.id) throw new Error('id parameter missing for layer'); - if (!settings.url) throw new Error('url parameter missing for layer'); - - this.map = map; - this.loader = loader; - this.cameraSync = cameraSync; - this.renderCallback = renderCallback; - this.tilesetId = settings.id; - this.url = settings.url; - this.projectToMercator = settings.projectToMercator === undefined ? false : settings.projectToMercator; - this.style = { }; - this.setStyle(settings.style); - this.renderOptions = new TilesetRenderOptions(settings.renderOptions, ()=>this.updateRenderOptions()); - this.subsurface = settings.subsurface ? settings.subsurface : false; - this.tileset = {}; - - if (settings.subsurface && settings.subsurface === true) { - this._addSubsurfaceSupport(); - } - } - - setStyle(style) { - if(!style) { - return; - } - - this.style.id = style.id ? style.id : undefined; - this.style.type = style.type ? style.type : undefined; - this.style.settings = style.settings ? style.settings : undefined; - this.updateStyle(); - } - - updateRenderOptions() { - applyRenderOptions(this, this.renderOptions); - this.renderCallback(); - } - - updateStyle() { - applyStyle(this, this.style, this.renderOptions); - this.renderCallback(); - } - - getStyle() { - return this.style; - } - - _addSubsurfaceSupport() { - this.subsurface = new Subsurface(this.map, this, this.cameraSync); - } - - _updateCallback() { - if (this.tileset.loaded) { - this.tileset.styleParams = this.style; - this.map.triggerRepaint(); - } - } - - _logError(error) { - console.error(`${error} (${this.id}: ${this.url})`); - } - - _addTilesetContent() { - if (this.tileset.root) { - this.add(this.tileset.root.totalContent); - } - } - - _initUpdate() { - this.map.setCenter(this.map.getCenter()); - } - - async load() { - this.tileset = new Tileset(()=> this._updateCallback(), this.renderCallback, this.loader, this.id); - await this.tileset.load(this.url, this.style, this.projectToMercator, this.renderOptions) - .then(async () => { - this._addTilesetContent(); - // delay an map update since we have to wait to download the tilesets - setTimeout(() => { this._initUpdate(); }, 1500); - }) - .catch((error) => this._logError(error)); - this.renderCallback(); - } - - async checkLoad(cameraFrustum, cameraPosition) { - this.cameraFrustum = cameraFrustum; - this.cameraPosition = cameraPosition; - - if (this.tileset && this.tileset.root) { - this.tileset.root.checkLoad(cameraFrustum, cameraPosition, this.tileset.geometricError); - } - } - } - - class FeatureInfo { - constructor(world, map, camera, loader, selectMaterial) { - this.world = world; - this.map = map; - this.camera = camera; - this.loader = loader; - this.selectedObjects = []; - this.selectMaterial = selectMaterial ? selectMaterial : this._createSelectMaterial(); - } - - getAt(result, x, y) { - this.unselect(); - - const intersects = GetIntersectingObjects(this.camera, this.world.children, this.map.transform.width, this.map.transform.height, x, y); - if (!intersects || intersects.length == 0 || !intersects[0].object || !intersects[0].object.modelType) { - return result; - } - - const intersect = intersects[0]; - const type = intersect.object.modelType; - const feature = this._createFeature(type, intersect); - this._selectObject(type, intersect); - result.unshift(feature); - this.map.triggerRepaint(); - - return result; - } - - unselect() { - for (let i = 0; i < this.selectedObjects.length; i++) { - const selected = this.selectedObjects[i]; - selected.parent.remove(selected.object); - } - - this.selectedObjects = []; - this._updateMap(); - } - - _createSelectMaterial() { - const material = new MeshBasicMaterial({ - color: 0xb077f8, - side: FrontSide, - transparent: true, - depthTest: true, - depthWrite: false, - opacity: 0.75 - }); - - material.polygonOffset = true; - material.polygonOffsetUnit = 1; - material.polygonOffsetFactor = -1; - return material; - } - - _updateMap() { - this.map.triggerRepaint(); - } - - _getTilesetID(o) { - if (o instanceof TilesetLayer) { - return o.tilesetId; - } else if (o.parent) { - return this._getTilesetID(o.parent); - } else { - return undefined; - } - } - - async _selectObject(type, intersect) { - let selectedObject = {}; - - switch (type) { - case "i3dm": - selectedObject = await this._createSelectI3dm(intersect); - break; - case "b3dm": - selectedObject = this._createSelectB3dm(intersect); - break; - } - - - intersect.object.parent.add(selectedObject); - - this.selectedObjects.push({ - parent: intersect.object.parent, - object: selectedObject - }); - - this._updateMap(); - } - - _createFeature(type, intersect) { - let feature = { - type: 'Feature', - properties: {}, - geometry: {}, - layer: { id: "", type: 'custom 3d' }, - source: this.url, - 'source-layer': null, - state: {} - }; - - const tilesetId = this._getTilesetID(intersect.object); - feature.layer.id = tilesetId; - - switch (type) { - case "i3dm": - return this._createFeatureI3DM(feature, intersect); - case "b3dm": - return this._createFeatureB3DM(feature, intersect); - } - - return feature; - } - - _createFeatureI3DM(feature, intersect) { - let keys = Object.keys(intersect.object.userData); - if (keys.length) { - for (let propertyName of keys) { - feature.properties[propertyName] = intersect.object.userData[propertyName][intersect.instanceId]; - } - } else { - feature.properties.batchId = intersect.instanceId; - } - - return feature; - } - - _createFeatureB3DM(feature, intersect) { - if (intersect.object.userData.b3dm) { - feature.properties['b3dm'] = intersect.object.userData.b3dm; - } - - let keys = Object.keys(intersect.object.userData); - if (keys.length) { - for (let propertyName of keys) { - if(intersect.object.userData[propertyName]) { - feature.properties[propertyName] = intersect.object.userData[propertyName][intersect.instanceId]; - } - } - } else { - feature.properties.batchId = intersect.instanceId; - } - - const vertexIdx = intersect.face.a; - const propertyIndex = intersect.object.geometry.attributes._batchid.getX(vertexIdx); - feature.properties.batchId = propertyIndex; - //FIXME: userData is not always in the parent, it can also be on the object itself depending on how many separate meshes have been created - let parentKeys = Object.keys(intersect.object.parent.userData); - if (parentKeys.length) { - for (let propertyName of parentKeys) { - feature.properties[propertyName] = - intersect.object.parent.userData[propertyName][propertyIndex]; - } - } - - return feature; - } - - _createSelectB3dm(intersect) { - const vertexIdx = intersect.face.a; - const normals = []; - const positions = []; - const object = intersect.object; - const count = object.geometry.attributes.position.count; - const batchId = object.geometry.attributes._batchid.getX(vertexIdx); - const indexArray = {}; - - for (let i = 0; i < count; i++) { - if (object.geometry.attributes._batchid.getX(i) === batchId) { - indexArray[i] = true; - } - } - - for (let i = 0; i < object.geometry.index.count; i++) { - const val = object.geometry.index.array[i]; - - if (indexArray[val]) { - normals.push(object.geometry.attributes.normal.getX(val)); - normals.push(object.geometry.attributes.normal.getY(val)); - normals.push(object.geometry.attributes.normal.getZ(val)); - - positions.push(object.geometry.attributes.position.getX(val)); - positions.push(object.geometry.attributes.position.getY(val)); - positions.push(object.geometry.attributes.position.getZ(val)); - } - } - - const geometry = new BufferGeometry(); - geometry.setAttribute('position', new BufferAttribute(new Float32Array(positions), 3, false)); - geometry.setAttribute('normal', new BufferAttribute(new Float32Array(normals), 3, false)); - - return new Mesh(geometry, this.selectMaterial); - } - - _createSelectI3dm(intersect) { - return new Promise((resolve, reject) => { - const instanceId = intersect.instanceId; - const objectMatrix = new Matrix4(); - intersect.object.getMatrixAt(instanceId, objectMatrix); - - const cache = internalGLTFCache; - var glbData = cache.get(intersect.object.model); - - let selectScene = undefined; - const resource = intersect.object.model; - - this.loader.parse(glbData, resource, (gltf) => { - selectScene = gltf.scene || gltf.scenes[0]; - selectScene.rotateX(Math.PI / 2); // convert from GLTF Y-up to Mapbox Z-up - selectScene.matrixWorldNeedsUpdate = false; - selectScene.applyMatrix4(objectMatrix); - selectScene.updateMatrixWorld(); - selectScene.traverse(child => { - if (child instanceof Mesh) { - child.material = this.selectMaterial; - } - }); - - selectScene.needsUpdate = true; - resolve(selectScene); - }); - }); - } - } - - const _taskCache$1 = new WeakMap(); - - class DRACOLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - this.decoderPath = ''; - this.decoderConfig = {}; - this.decoderBinary = null; - this.decoderPending = null; - - this.workerLimit = 4; - this.workerPool = []; - this.workerNextTaskID = 1; - this.workerSourceURL = ''; - - this.defaultAttributeIDs = { - position: 'POSITION', - normal: 'NORMAL', - color: 'COLOR', - uv: 'TEX_COORD' - }; - this.defaultAttributeTypes = { - position: 'Float32Array', - normal: 'Float32Array', - color: 'Float32Array', - uv: 'Float32Array' - }; - - } - - setDecoderPath( path ) { - - this.decoderPath = path; - - return this; - - } - - setDecoderConfig( config ) { - - this.decoderConfig = config; - - return this; - - } - - setWorkerLimit( workerLimit ) { - - this.workerLimit = workerLimit; - - return this; - - } - - load( url, onLoad, onProgress, onError ) { - - const loader = new FileLoader( this.manager ); - - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - - loader.load( url, ( buffer ) => { - - const taskConfig = { - attributeIDs: this.defaultAttributeIDs, - attributeTypes: this.defaultAttributeTypes, - useUniqueIDs: false - }; - - this.decodeGeometry( buffer, taskConfig ) - .then( onLoad ) - .catch( onError ); - - }, onProgress, onError ); - - } - - /** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */ - decodeDracoFile( buffer, callback, attributeIDs, attributeTypes ) { - - const taskConfig = { - attributeIDs: attributeIDs || this.defaultAttributeIDs, - attributeTypes: attributeTypes || this.defaultAttributeTypes, - useUniqueIDs: !! attributeIDs - }; - - this.decodeGeometry( buffer, taskConfig ).then( callback ); - - } - - decodeGeometry( buffer, taskConfig ) { - - // TODO: For backward-compatibility, support 'attributeTypes' objects containing - // references (rather than names) to typed array constructors. These must be - // serialized before sending them to the worker. - for ( const attribute in taskConfig.attributeTypes ) { - - const type = taskConfig.attributeTypes[ attribute ]; - - if ( type.BYTES_PER_ELEMENT !== undefined ) { - - taskConfig.attributeTypes[ attribute ] = type.name; - - } - - } - - // - - const taskKey = JSON.stringify( taskConfig ); - - // Check for an existing task using this buffer. A transferred buffer cannot be transferred - // again from this thread. - if ( _taskCache$1.has( buffer ) ) { - - const cachedTask = _taskCache$1.get( buffer ); - - if ( cachedTask.key === taskKey ) { - - return cachedTask.promise; - - } else if ( buffer.byteLength === 0 ) { - - // Technically, it would be possible to wait for the previous task to complete, - // transfer the buffer back, and decode again with the second configuration. That - // is complex, and I don't know of any reason to decode a Draco buffer twice in - // different ways, so this is left unimplemented. - throw new Error( - - 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + - 'settings. Buffer has already been transferred.' - - ); - - } - - } - - // - - let worker; - const taskID = this.workerNextTaskID ++; - const taskCost = buffer.byteLength; - - // Obtain a worker and assign a task, and construct a geometry instance - // when the task completes. - const geometryPending = this._getWorker( taskID, taskCost ) - .then( ( _worker ) => { - - worker = _worker; - - return new Promise( ( resolve, reject ) => { - - worker._callbacks[ taskID ] = { resolve, reject }; - - worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] ); - - // this.debug(); - - } ); - - } ) - .then( ( message ) => this._createGeometry( message.geometry ) ); - - // Remove task from the task list. - // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416) - geometryPending - .catch( () => true ) - .then( () => { - - if ( worker && taskID ) { - - this._releaseTask( worker, taskID ); - - // this.debug(); - - } - - } ); - - // Cache the task result. - _taskCache$1.set( buffer, { - - key: taskKey, - promise: geometryPending - - } ); - - return geometryPending; - - } - - _createGeometry( geometryData ) { - - const geometry = new BufferGeometry(); - - if ( geometryData.index ) { - - geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) ); - - } - - for ( let i = 0; i < geometryData.attributes.length; i ++ ) { - - const attribute = geometryData.attributes[ i ]; - const name = attribute.name; - const array = attribute.array; - const itemSize = attribute.itemSize; - - geometry.setAttribute( name, new BufferAttribute( array, itemSize ) ); - - } - - return geometry; - - } - - _loadLibrary( url, responseType ) { - - const loader = new FileLoader( this.manager ); - loader.setPath( this.decoderPath ); - loader.setResponseType( responseType ); - loader.setWithCredentials( this.withCredentials ); - - return new Promise( ( resolve, reject ) => { - - loader.load( url, resolve, undefined, reject ); - - } ); - - } - - preload() { - - this._initDecoder(); - - return this; - - } - - _initDecoder() { - - if ( this.decoderPending ) return this.decoderPending; - - const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js'; - const librariesPending = []; - - if ( useJS ) { - - librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) ); - - } else { - - librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) ); - librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) ); - - } - - this.decoderPending = Promise.all( librariesPending ) - .then( ( libraries ) => { - - const jsContent = libraries[ 0 ]; - - if ( ! useJS ) { - - this.decoderConfig.wasmBinary = libraries[ 1 ]; - - } - - const fn = DRACOWorker.toString(); - - const body = [ - '/* draco decoder */', - jsContent, - '', - '/* worker */', - fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) - ].join( '\n' ); - - this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); - - } ); - - return this.decoderPending; - - } - - _getWorker( taskID, taskCost ) { - - return this._initDecoder().then( () => { - - if ( this.workerPool.length < this.workerLimit ) { - - const worker = new Worker( this.workerSourceURL ); - - worker._callbacks = {}; - worker._taskCosts = {}; - worker._taskLoad = 0; - - worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } ); - - worker.onmessage = function ( e ) { - - const message = e.data; - - switch ( message.type ) { - - case 'decode': - worker._callbacks[ message.id ].resolve( message ); - break; - - case 'error': - worker._callbacks[ message.id ].reject( message ); - break; - - default: - console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' ); - - } - - }; - - this.workerPool.push( worker ); - - } else { - - this.workerPool.sort( function ( a, b ) { - - return a._taskLoad > b._taskLoad ? - 1 : 1; - - } ); - - } - - const worker = this.workerPool[ this.workerPool.length - 1 ]; - worker._taskCosts[ taskID ] = taskCost; - worker._taskLoad += taskCost; - return worker; - - } ); - - } - - _releaseTask( worker, taskID ) { - - worker._taskLoad -= worker._taskCosts[ taskID ]; - delete worker._callbacks[ taskID ]; - delete worker._taskCosts[ taskID ]; - - } - - debug() { - - console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) ); - - } - - dispose() { - - for ( let i = 0; i < this.workerPool.length; ++ i ) { - - this.workerPool[ i ].terminate(); - - } - - this.workerPool.length = 0; - - return this; - - } - - } - - /* WEB WORKER */ - - function DRACOWorker() { - - let decoderConfig; - let decoderPending; - - onmessage = function ( e ) { - - const message = e.data; - - switch ( message.type ) { - - case 'init': - decoderConfig = message.decoderConfig; - decoderPending = new Promise( function ( resolve/*, reject*/ ) { - - decoderConfig.onModuleLoaded = function ( draco ) { - - // Module is Promise-like. Wrap before resolving to avoid loop. - resolve( { draco: draco } ); - - }; - - DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef - - } ); - break; - - case 'decode': - const buffer = message.buffer; - const taskConfig = message.taskConfig; - decoderPending.then( ( module ) => { - - const draco = module.draco; - const decoder = new draco.Decoder(); - const decoderBuffer = new draco.DecoderBuffer(); - decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength ); - - try { - - const geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig ); - - const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer ); - - if ( geometry.index ) buffers.push( geometry.index.array.buffer ); - - self.postMessage( { type: 'decode', id: message.id, geometry }, buffers ); - - } catch ( error ) { - - console.error( error ); - - self.postMessage( { type: 'error', id: message.id, error: error.message } ); - - } finally { - - draco.destroy( decoderBuffer ); - draco.destroy( decoder ); - - } - - } ); - break; - - } - - }; - - function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) { - - const attributeIDs = taskConfig.attributeIDs; - const attributeTypes = taskConfig.attributeTypes; - - let dracoGeometry; - let decodingStatus; - - const geometryType = decoder.GetEncodedGeometryType( decoderBuffer ); - - if ( geometryType === draco.TRIANGULAR_MESH ) { - - dracoGeometry = new draco.Mesh(); - decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry ); - - } else if ( geometryType === draco.POINT_CLOUD ) { - - dracoGeometry = new draco.PointCloud(); - decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry ); - - } else { - - throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' ); - - } - - if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) { - - throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() ); - - } - - const geometry = { index: null, attributes: [] }; - - // Gather all vertex attributes. - for ( const attributeName in attributeIDs ) { - - const attributeType = self[ attributeTypes[ attributeName ] ]; - - let attribute; - let attributeID; - - // A Draco file may be created with default vertex attributes, whose attribute IDs - // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively, - // a Draco file may contain a custom set of attributes, identified by known unique - // IDs. glTF files always do the latter, and `.drc` files typically do the former. - if ( taskConfig.useUniqueIDs ) { - - attributeID = attributeIDs[ attributeName ]; - attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID ); - - } else { - - attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] ); - - if ( attributeID === - 1 ) continue; - - attribute = decoder.GetAttribute( dracoGeometry, attributeID ); - - } - - geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) ); - - } - - // Add index. - if ( geometryType === draco.TRIANGULAR_MESH ) { - - geometry.index = decodeIndex( draco, decoder, dracoGeometry ); - - } - - draco.destroy( dracoGeometry ); - - return geometry; - - } - - function decodeIndex( draco, decoder, dracoGeometry ) { - - const numFaces = dracoGeometry.num_faces(); - const numIndices = numFaces * 3; - const byteLength = numIndices * 4; - - const ptr = draco._malloc( byteLength ); - decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr ); - const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice(); - draco._free( ptr ); - - return { array: index, itemSize: 1 }; - - } - - function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) { - - const numComponents = attribute.num_components(); - const numPoints = dracoGeometry.num_points(); - const numValues = numPoints * numComponents; - const byteLength = numValues * attributeType.BYTES_PER_ELEMENT; - const dataType = getDracoDataType( draco, attributeType ); - - const ptr = draco._malloc( byteLength ); - decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr ); - const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice(); - draco._free( ptr ); - - return { - name: attributeName, - array: array, - itemSize: numComponents - }; - - } - - function getDracoDataType( draco, attributeType ) { - - switch ( attributeType ) { - - case Float32Array: return draco.DT_FLOAT32; - case Int8Array: return draco.DT_INT8; - case Int16Array: return draco.DT_INT16; - case Int32Array: return draco.DT_INT32; - case Uint8Array: return draco.DT_UINT8; - case Uint16Array: return draco.DT_UINT16; - case Uint32Array: return draco.DT_UINT32; - - } - - } - - } - - class GLTFLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - this.dracoLoader = null; - this.ktx2Loader = null; - this.meshoptDecoder = null; - - this.pluginCallbacks = []; - - this.register( function ( parser ) { - - return new GLTFMaterialsClearcoatExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFTextureBasisUExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFTextureWebPExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsTransmissionExtension$1( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsVolumeExtension$1( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsIorExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsSpecularExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFLightsExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMeshoptCompression( parser ); - - } ); - - } - - load( url, onLoad, onProgress, onError ) { - - const scope = this; - - let resourcePath; - - if ( this.resourcePath !== '' ) { - - resourcePath = this.resourcePath; - - } else if ( this.path !== '' ) { - - resourcePath = this.path; - - } else { - - resourcePath = LoaderUtils.extractUrlBase( url ); - - } - - // Tells the LoadingManager to track an extra item, which resolves after - // the model is fully loaded. This means the count of items loaded will - // be incorrect, but ensures manager.onLoad() does not fire early. - this.manager.itemStart( url ); - - const _onError = function ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }; - - const loader = new FileLoader( this.manager ); - - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - - loader.load( url, function ( data ) { - - try { - - scope.parse( data, resourcePath, function ( gltf ) { - - onLoad( gltf ); - - scope.manager.itemEnd( url ); - - }, _onError ); - - } catch ( e ) { - - _onError( e ); - - } - - }, onProgress, _onError ); - - } - - setDRACOLoader( dracoLoader ) { - - this.dracoLoader = dracoLoader; - return this; - - } - - setDDSLoader() { - - throw new Error( - - 'THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".' - - ); - - } - - setKTX2Loader( ktx2Loader ) { - - this.ktx2Loader = ktx2Loader; - return this; - - } - - setMeshoptDecoder( meshoptDecoder ) { - - this.meshoptDecoder = meshoptDecoder; - return this; - - } - - register( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) { - - this.pluginCallbacks.push( callback ); - - } - - return this; - - } - - unregister( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) { - - this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 ); - - } - - return this; - - } - - parse( data, path, onLoad, onError ) { - - let content; - const extensions = {}; - const plugins = {}; - - if ( typeof data === 'string' ) { - - content = data; - - } else { - - const magic = LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) ); - - if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { - - try { - - extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); - - } catch ( error ) { - - if ( onError ) onError( error ); - return; - - } - - content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; - - } else { - - content = LoaderUtils.decodeText( new Uint8Array( data ) ); - - } - - } - - const json = JSON.parse( content ); - - if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) { - - if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) ); - return; - - } - - const parser = new GLTFParser( json, { - - path: path || this.resourcePath || '', - crossOrigin: this.crossOrigin, - requestHeader: this.requestHeader, - manager: this.manager, - ktx2Loader: this.ktx2Loader, - meshoptDecoder: this.meshoptDecoder - - } ); - - parser.fileLoader.setRequestHeader( this.requestHeader ); - - for ( let i = 0; i < this.pluginCallbacks.length; i ++ ) { - - const plugin = this.pluginCallbacks[ i ]( parser ); - plugins[ plugin.name ] = plugin; - - // Workaround to avoid determining as unknown extension - // in addUnknownExtensionsToUserData(). - // Remove this workaround if we move all the existing - // extension handlers to plugin system - extensions[ plugin.name ] = true; - - } - - if ( json.extensionsUsed ) { - - for ( let i = 0; i < json.extensionsUsed.length; ++ i ) { - - const extensionName = json.extensionsUsed[ i ]; - const extensionsRequired = json.extensionsRequired || []; - - switch ( extensionName ) { - - case EXTENSIONS.KHR_MATERIALS_UNLIT: - extensions[ extensionName ] = new GLTFMaterialsUnlitExtension$1(); - break; - - case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: - extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension(); - break; - - case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION: - extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); - break; - - case EXTENSIONS.KHR_TEXTURE_TRANSFORM: - extensions[ extensionName ] = new GLTFTextureTransformExtension(); - break; - - case EXTENSIONS.KHR_MESH_QUANTIZATION: - extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); - break; - - default: - - if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) { - - console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' ); - - } - - } - - } - - } - - parser.setExtensions( extensions ); - parser.setPlugins( plugins ); - parser.parse( onLoad, onError ); - - } - - } - - /* GLTFREGISTRY */ - - function GLTFRegistry() { - - let objects = {}; - - return { - - get: function ( key ) { - - return objects[ key ]; - - }, - - add: function ( key, object ) { - - objects[ key ] = object; - - }, - - remove: function ( key ) { - - delete objects[ key ]; - - }, - - removeAll: function () { - - objects = {}; - - } - - }; - - } - - /*********************************/ - /********** EXTENSIONS ***********/ - /*********************************/ - - const EXTENSIONS = { - KHR_BINARY_GLTF: 'KHR_binary_glTF', - KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', - KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', - KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', - KHR_MATERIALS_IOR: 'KHR_materials_ior', - KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness', - KHR_MATERIALS_SPECULAR: 'KHR_materials_specular', - KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', - KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', - KHR_MATERIALS_VOLUME: 'KHR_materials_volume', - KHR_TEXTURE_BASISU: 'KHR_texture_basisu', - KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', - KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', - EXT_TEXTURE_WEBP: 'EXT_texture_webp', - EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression' - }; - - /** - * Punctual Lights Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual - */ - class GLTFLightsExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL; - - // Object3D instance caches - this.cache = { refs: {}, uses: {} }; - - } - - _markDefs() { - - const parser = this.parser; - const nodeDefs = this.parser.json.nodes || []; - - for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { - - const nodeDef = nodeDefs[ nodeIndex ]; - - if ( nodeDef.extensions - && nodeDef.extensions[ this.name ] - && nodeDef.extensions[ this.name ].light !== undefined ) { - - parser._addNodeRef( this.cache, nodeDef.extensions[ this.name ].light ); - - } - - } - - } - - _loadLight( lightIndex ) { - - const parser = this.parser; - const cacheKey = 'light:' + lightIndex; - let dependency = parser.cache.get( cacheKey ); - - if ( dependency ) return dependency; - - const json = parser.json; - const extensions = ( json.extensions && json.extensions[ this.name ] ) || {}; - const lightDefs = extensions.lights || []; - const lightDef = lightDefs[ lightIndex ]; - let lightNode; - - const color = new Color( 0xffffff ); - - if ( lightDef.color !== undefined ) color.fromArray( lightDef.color ); - - const range = lightDef.range !== undefined ? lightDef.range : 0; - - switch ( lightDef.type ) { - - case 'directional': - lightNode = new DirectionalLight( color ); - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - case 'point': - lightNode = new PointLight( color ); - lightNode.distance = range; - break; - - case 'spot': - lightNode = new SpotLight( color ); - lightNode.distance = range; - // Handle spotlight properties. - lightDef.spot = lightDef.spot || {}; - lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0; - lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0; - lightNode.angle = lightDef.spot.outerConeAngle; - lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle; - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - default: - throw new Error( 'THREE.GLTFLoader: Unexpected light type: ' + lightDef.type ); - - } - - // Some lights (e.g. spot) default to a position other than the origin. Reset the position - // here, because node-level parsing will only override position if explicitly specified. - lightNode.position.set( 0, 0, 0 ); - - lightNode.decay = 2; - - if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; - - lightNode.name = parser.createUniqueName( lightDef.name || ( 'light_' + lightIndex ) ); - - dependency = Promise.resolve( lightNode ); - - parser.cache.add( cacheKey, dependency ); - - return dependency; - - } - - createNodeAttachment( nodeIndex ) { - - const self = this; - const parser = this.parser; - const json = parser.json; - const nodeDef = json.nodes[ nodeIndex ]; - const lightDef = ( nodeDef.extensions && nodeDef.extensions[ this.name ] ) || {}; - const lightIndex = lightDef.light; - - if ( lightIndex === undefined ) return null; - - return this._loadLight( lightIndex ).then( function ( light ) { - - return parser._getNodeRef( self.cache, lightIndex, light ); - - } ); - - } - - } - - /** - * Unlit Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit - */ - class GLTFMaterialsUnlitExtension$1 { - - constructor() { - - this.name = EXTENSIONS.KHR_MATERIALS_UNLIT; - - } - - getMaterialType() { - - return MeshBasicMaterial; - - } - - extendParams( materialParams, materialDef, parser ) { - - const pending = []; - - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - const metallicRoughness = materialDef.pbrMetallicRoughness; - - if ( metallicRoughness ) { - - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - - const array = metallicRoughness.baseColorFactor; - - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; - - } - - if ( metallicRoughness.baseColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) ); - - } - - } - - return Promise.all( pending ); - - } - - } - - /** - * Clearcoat Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat - */ - class GLTFMaterialsClearcoatExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.clearcoatFactor !== undefined ) { - - materialParams.clearcoat = extension.clearcoatFactor; - - } - - if ( extension.clearcoatTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) ); - - } - - if ( extension.clearcoatRoughnessFactor !== undefined ) { - - materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor; - - } - - if ( extension.clearcoatRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) ); - - } - - if ( extension.clearcoatNormalTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) ); - - if ( extension.clearcoatNormalTexture.scale !== undefined ) { - - const scale = extension.clearcoatNormalTexture.scale; - - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - materialParams.clearcoatNormalScale = new Vector2( scale, - scale ); - - } - - } - - return Promise.all( pending ); - - } - - } - - /** - * Transmission Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission - * Draft: https://github.com/KhronosGroup/glTF/pull/1698 - */ - class GLTFMaterialsTransmissionExtension$1 { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.transmissionFactor !== undefined ) { - - materialParams.transmission = extension.transmissionFactor; - - } - - if ( extension.transmissionTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) ); - - } - - return Promise.all( pending ); - - } - - } - - /** - * Materials Volume Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume - */ - class GLTFMaterialsVolumeExtension$1 { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_VOLUME; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - materialParams.thickness = extension.thicknessFactor !== undefined ? extension.thicknessFactor : 0; - - if ( extension.thicknessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'thicknessMap', extension.thicknessTexture ) ); - - } - - materialParams.attenuationDistance = extension.attenuationDistance || 0; - - const colorArray = extension.attenuationColor || [ 1, 1, 1 ]; - materialParams.attenuationTint = new Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] ); - - return Promise.all( pending ); - - } - - } - - /** - * Materials ior Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior - */ - class GLTFMaterialsIorExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_IOR; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const extension = materialDef.extensions[ this.name ]; - - materialParams.ior = extension.ior !== undefined ? extension.ior : 1.5; - - return Promise.resolve(); - - } - - } - - /** - * Materials specular Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular - */ - class GLTFMaterialsSpecularExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_SPECULAR; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - materialParams.specularIntensity = extension.specularFactor !== undefined ? extension.specularFactor : 1.0; - - if ( extension.specularTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'specularIntensityMap', extension.specularTexture ) ); - - } - - const colorArray = extension.specularColorFactor || [ 1, 1, 1 ]; - materialParams.specularTint = new Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] ); - - if ( extension.specularColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'specularTintMap', extension.specularColorTexture ).then( function ( texture ) { - - texture.encoding = sRGBEncoding; - - } ) ); - - } - - return Promise.all( pending ); - - } - - } - - /** - * BasisU Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu - */ - class GLTFTextureBasisUExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_TEXTURE_BASISU; - - } - - loadTexture( textureIndex ) { - - const parser = this.parser; - const json = parser.json; - - const textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) { - - return null; - - } - - const extension = textureDef.extensions[ this.name ]; - const source = json.images[ extension.source ]; - const loader = parser.options.ktx2Loader; - - if ( ! loader ) { - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' ); - - } else { - - // Assumes that the extension is optional and that a fallback texture is present - return null; - - } - - } - - return parser.loadTextureImage( textureIndex, source, loader ); - - } - - } - - /** - * WebP Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp - */ - class GLTFTextureWebPExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.EXT_TEXTURE_WEBP; - this.isSupported = null; - - } - - loadTexture( textureIndex ) { - - const name = this.name; - const parser = this.parser; - const json = parser.json; - - const textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) { - - return null; - - } - - const extension = textureDef.extensions[ name ]; - const source = json.images[ extension.source ]; - - let loader = parser.textureLoader; - if ( source.uri ) { - - const handler = parser.options.manager.getHandler( source.uri ); - if ( handler !== null ) loader = handler; - - } - - return this.detectSupport().then( function ( isSupported ) { - - if ( isSupported ) return parser.loadTextureImage( textureIndex, source, loader ); - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: WebP required by asset but unsupported.' ); - - } - - // Fall back to PNG or JPEG. - return parser.loadTexture( textureIndex ); - - } ); - - } - - detectSupport() { - - if ( ! this.isSupported ) { - - this.isSupported = new Promise( function ( resolve ) { - - const image = new Image(); - - // Lossy test image. Support for lossy images doesn't guarantee support for all - // WebP images, unfortunately. - image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA'; - - image.onload = image.onerror = function () { - - resolve( image.height === 1 ); - - }; - - } ); - - } - - return this.isSupported; - - } - - } - - /** - * meshopt BufferView Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression - */ - class GLTFMeshoptCompression { - - constructor( parser ) { - - this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION; - this.parser = parser; - - } - - loadBufferView( index ) { - - const json = this.parser.json; - const bufferView = json.bufferViews[ index ]; - - if ( bufferView.extensions && bufferView.extensions[ this.name ] ) { - - const extensionDef = bufferView.extensions[ this.name ]; - - const buffer = this.parser.getDependency( 'buffer', extensionDef.buffer ); - const decoder = this.parser.options.meshoptDecoder; - - if ( ! decoder || ! decoder.supported ) { - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files' ); - - } else { - - // Assumes that the extension is optional and that fallback buffer data is present - return null; - - } - - } - - return Promise.all( [ buffer, decoder.ready ] ).then( function ( res ) { - - const byteOffset = extensionDef.byteOffset || 0; - const byteLength = extensionDef.byteLength || 0; - - const count = extensionDef.count; - const stride = extensionDef.byteStride; - - const result = new ArrayBuffer( count * stride ); - const source = new Uint8Array( res[ 0 ], byteOffset, byteLength ); - - decoder.decodeGltfBuffer( new Uint8Array( result ), count, stride, source, extensionDef.mode, extensionDef.filter ); - return result; - - } ); - - } else { - - return null; - - } - - } - - } - - /* BINARY EXTENSION */ - const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; - const BINARY_EXTENSION_HEADER_LENGTH = 12; - const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; - - class GLTFBinaryExtension { - - constructor( data ) { - - this.name = EXTENSIONS.KHR_BINARY_GLTF; - this.content = null; - this.body = null; - - const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); - - this.header = { - magic: LoaderUtils.decodeText( new Uint8Array( data.slice( 0, 4 ) ) ), - version: headerView.getUint32( 4, true ), - length: headerView.getUint32( 8, true ) - }; - - if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { - - throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); - - } else if ( this.header.version < 2.0 ) { - - throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' ); - - } - - const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH; - const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); - let chunkIndex = 0; - - while ( chunkIndex < chunkContentsLength ) { - - const chunkLength = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; - - const chunkType = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; - - if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { - - const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); - this.content = LoaderUtils.decodeText( contentArray ); - - } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { - - const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; - this.body = data.slice( byteOffset, byteOffset + chunkLength ); - - } - - // Clients must ignore chunks with unknown types. - - chunkIndex += chunkLength; - - } - - if ( this.content === null ) { - - throw new Error( 'THREE.GLTFLoader: JSON content not found.' ); - - } - - } - - } - - /** - * DRACO Mesh Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression - */ - class GLTFDracoMeshCompressionExtension { - - constructor( json, dracoLoader ) { - - if ( ! dracoLoader ) { - - throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' ); - - } - - this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION; - this.json = json; - this.dracoLoader = dracoLoader; - this.dracoLoader.preload(); - - } - - decodePrimitive( primitive, parser ) { - - const json = this.json; - const dracoLoader = this.dracoLoader; - const bufferViewIndex = primitive.extensions[ this.name ].bufferView; - const gltfAttributeMap = primitive.extensions[ this.name ].attributes; - const threeAttributeMap = {}; - const attributeNormalizedMap = {}; - const attributeTypeMap = {}; - - for ( const attributeName in gltfAttributeMap ) { - - const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - - threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; - - } - - for ( const attributeName in primitive.attributes ) { - - const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - - if ( gltfAttributeMap[ attributeName ] !== undefined ) { - - const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; - const componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - - attributeTypeMap[ threeAttributeName ] = componentType; - attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; - - } - - } - - return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) { - - return new Promise( function ( resolve ) { - - dracoLoader.decodeDracoFile( bufferView, function ( geometry ) { - - for ( const attributeName in geometry.attributes ) { - - const attribute = geometry.attributes[ attributeName ]; - const normalized = attributeNormalizedMap[ attributeName ]; - - if ( normalized !== undefined ) attribute.normalized = normalized; - - } - - resolve( geometry ); - - }, threeAttributeMap, attributeTypeMap ); - - } ); - - } ); - - } - - } - - /** - * Texture Transform Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform - */ - class GLTFTextureTransformExtension { - - constructor() { - - this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM; - - } - - extendTexture( texture, transform ) { - - if ( transform.texCoord !== undefined ) { - - console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' ); - - } - - if ( transform.offset === undefined && transform.rotation === undefined && transform.scale === undefined ) { - - // See https://github.com/mrdoob/three.js/issues/21819. - return texture; - - } - - texture = texture.clone(); - - if ( transform.offset !== undefined ) { - - texture.offset.fromArray( transform.offset ); - - } - - if ( transform.rotation !== undefined ) { - - texture.rotation = transform.rotation; - - } - - if ( transform.scale !== undefined ) { - - texture.repeat.fromArray( transform.scale ); - - } - - texture.needsUpdate = true; - - return texture; - - } - - } - - /** - * Specular-Glossiness Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness - */ - - /** - * A sub class of StandardMaterial with some of the functionality - * changed via the `onBeforeCompile` callback - * @pailhead - */ - class GLTFMeshStandardSGMaterial extends MeshStandardMaterial { - - constructor( params ) { - - super(); - - this.isGLTFSpecularGlossinessMaterial = true; - - //various chunks that need replacing - const specularMapParsFragmentChunk = [ - '#ifdef USE_SPECULARMAP', - ' uniform sampler2D specularMap;', - '#endif' - ].join( '\n' ); - - const glossinessMapParsFragmentChunk = [ - '#ifdef USE_GLOSSINESSMAP', - ' uniform sampler2D glossinessMap;', - '#endif' - ].join( '\n' ); - - const specularMapFragmentChunk = [ - 'vec3 specularFactor = specular;', - '#ifdef USE_SPECULARMAP', - ' vec4 texelSpecular = texture2D( specularMap, vUv );', - ' texelSpecular = sRGBToLinear( texelSpecular );', - ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', - ' specularFactor *= texelSpecular.rgb;', - '#endif' - ].join( '\n' ); - - const glossinessMapFragmentChunk = [ - 'float glossinessFactor = glossiness;', - '#ifdef USE_GLOSSINESSMAP', - ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );', - ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture', - ' glossinessFactor *= texelGlossiness.a;', - '#endif' - ].join( '\n' ); - - const lightPhysicalFragmentChunk = [ - 'PhysicalMaterial material;', - 'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );', - 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', - 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );', - 'material.roughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.', - 'material.roughness += geometryRoughness;', - 'material.roughness = min( material.roughness, 1.0 );', - 'material.specularColor = specularFactor;', - ].join( '\n' ); - - const uniforms = { - specular: { value: new Color().setHex( 0xffffff ) }, - glossiness: { value: 1 }, - specularMap: { value: null }, - glossinessMap: { value: null } - }; - - this._extraUniforms = uniforms; - - this.onBeforeCompile = function ( shader ) { - - for ( const uniformName in uniforms ) { - - shader.uniforms[ uniformName ] = uniforms[ uniformName ]; - - } - - shader.fragmentShader = shader.fragmentShader - .replace( 'uniform float roughness;', 'uniform vec3 specular;' ) - .replace( 'uniform float metalness;', 'uniform float glossiness;' ) - .replace( '#include ', specularMapParsFragmentChunk ) - .replace( '#include ', glossinessMapParsFragmentChunk ) - .replace( '#include ', specularMapFragmentChunk ) - .replace( '#include ', glossinessMapFragmentChunk ) - .replace( '#include ', lightPhysicalFragmentChunk ); - - }; - - Object.defineProperties( this, { - - specular: { - get: function () { - - return uniforms.specular.value; - - }, - set: function ( v ) { - - uniforms.specular.value = v; - - } - }, - - specularMap: { - get: function () { - - return uniforms.specularMap.value; - - }, - set: function ( v ) { - - uniforms.specularMap.value = v; - - if ( v ) { - - this.defines.USE_SPECULARMAP = ''; // USE_UV is set by the renderer for specular maps - - } else { - - delete this.defines.USE_SPECULARMAP; - - } - - } - }, - - glossiness: { - get: function () { - - return uniforms.glossiness.value; - - }, - set: function ( v ) { - - uniforms.glossiness.value = v; - - } - }, - - glossinessMap: { - get: function () { - - return uniforms.glossinessMap.value; - - }, - set: function ( v ) { - - uniforms.glossinessMap.value = v; - - if ( v ) { - - this.defines.USE_GLOSSINESSMAP = ''; - this.defines.USE_UV = ''; - - } else { - - delete this.defines.USE_GLOSSINESSMAP; - delete this.defines.USE_UV; - - } - - } - } - - } ); - - delete this.metalness; - delete this.roughness; - delete this.metalnessMap; - delete this.roughnessMap; - - this.setValues( params ); - - } - - copy( source ) { - - super.copy( source ); - - this.specularMap = source.specularMap; - this.specular.copy( source.specular ); - this.glossinessMap = source.glossinessMap; - this.glossiness = source.glossiness; - delete this.metalness; - delete this.roughness; - delete this.metalnessMap; - delete this.roughnessMap; - return this; - - } - - } - - - class GLTFMaterialsPbrSpecularGlossinessExtension { - - constructor() { - - this.name = EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS; - - this.specularGlossinessParams = [ - 'color', - 'map', - 'lightMap', - 'lightMapIntensity', - 'aoMap', - 'aoMapIntensity', - 'emissive', - 'emissiveIntensity', - 'emissiveMap', - 'bumpMap', - 'bumpScale', - 'normalMap', - 'normalMapType', - 'displacementMap', - 'displacementScale', - 'displacementBias', - 'specularMap', - 'specular', - 'glossinessMap', - 'glossiness', - 'alphaMap', - 'envMap', - 'envMapIntensity', - 'refractionRatio', - ]; - - } - - getMaterialType() { - - return GLTFMeshStandardSGMaterial; - - } - - extendParams( materialParams, materialDef, parser ) { - - const pbrSpecularGlossiness = materialDef.extensions[ this.name ]; - - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - const pending = []; - - if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) { - - const array = pbrSpecularGlossiness.diffuseFactor; - - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; - - } - - if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', pbrSpecularGlossiness.diffuseTexture ) ); - - } - - materialParams.emissive = new Color( 0.0, 0.0, 0.0 ); - materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0; - materialParams.specular = new Color( 1.0, 1.0, 1.0 ); - - if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) { - - materialParams.specular.fromArray( pbrSpecularGlossiness.specularFactor ); - - } - - if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) { - - const specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture; - pending.push( parser.assignTexture( materialParams, 'glossinessMap', specGlossMapDef ) ); - pending.push( parser.assignTexture( materialParams, 'specularMap', specGlossMapDef ) ); - - } - - return Promise.all( pending ); - - } - - createMaterial( materialParams ) { - - const material = new GLTFMeshStandardSGMaterial( materialParams ); - material.fog = true; - - material.color = materialParams.color; - - material.map = materialParams.map === undefined ? null : materialParams.map; - - material.lightMap = null; - material.lightMapIntensity = 1.0; - - material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap; - material.aoMapIntensity = 1.0; - - material.emissive = materialParams.emissive; - material.emissiveIntensity = 1.0; - material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap; - - material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap; - material.bumpScale = 1; - - material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap; - material.normalMapType = TangentSpaceNormalMap; - - if ( materialParams.normalScale ) material.normalScale = materialParams.normalScale; - - material.displacementMap = null; - material.displacementScale = 1; - material.displacementBias = 0; - - material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap; - material.specular = materialParams.specular; - - material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap; - material.glossiness = materialParams.glossiness; - - material.alphaMap = null; - - material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap; - material.envMapIntensity = 1.0; - - material.refractionRatio = 0.98; - - return material; - - } - - } - - /** - * Mesh Quantization Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization - */ - class GLTFMeshQuantizationExtension { - - constructor() { - - this.name = EXTENSIONS.KHR_MESH_QUANTIZATION; - - } - - } - - /*********************************/ - /********** INTERPOLATION ********/ - /*********************************/ - - // Spline Interpolation - // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation - class GLTFCubicSplineInterpolant extends Interpolant { - - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - copySampleValue_( index ) { - - // Copies a sample value to the result buffer. See description of glTF - // CUBICSPLINE values layout in interpolate_() function below. - - const result = this.resultBuffer, - values = this.sampleValues, - valueSize = this.valueSize, - offset = index * valueSize * 3 + valueSize; - - for ( let i = 0; i !== valueSize; i ++ ) { - - result[ i ] = values[ offset + i ]; - - } - - return result; - - } - - } - - GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; - - GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; - - GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) { - - const result = this.resultBuffer; - const values = this.sampleValues; - const stride = this.valueSize; - - const stride2 = stride * 2; - const stride3 = stride * 3; - - const td = t1 - t0; - - const p = ( t - t0 ) / td; - const pp = p * p; - const ppp = pp * p; - - const offset1 = i1 * stride3; - const offset0 = offset1 - stride3; - - const s2 = - 2 * ppp + 3 * pp; - const s3 = ppp - pp; - const s0 = 1 - s2; - const s1 = s3 - pp + p; - - // Layout of keyframe output values for CUBICSPLINE animations: - // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] - for ( let i = 0; i !== stride; i ++ ) { - - const p0 = values[ offset0 + i + stride ]; // splineVertex_k - const m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k) - const p1 = values[ offset1 + i + stride ]; // splineVertex_k+1 - const m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k) - - result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; - - } - - return result; - - }; - - const _q = new Quaternion(); - - class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant { - - interpolate_( i1, t0, t, t1 ) { - - const result = super.interpolate_( i1, t0, t, t1 ); - - _q.fromArray( result ).normalize().toArray( result ); - - return result; - - } - - } - - - /*********************************/ - /********** INTERNALS ************/ - /*********************************/ - - /* CONSTANTS */ - - const WEBGL_CONSTANTS$1 = { - FLOAT: 5126, - //FLOAT_MAT2: 35674, - FLOAT_MAT3: 35675, - FLOAT_MAT4: 35676, - FLOAT_VEC2: 35664, - FLOAT_VEC3: 35665, - FLOAT_VEC4: 35666, - LINEAR: 9729, - REPEAT: 10497, - SAMPLER_2D: 35678, - POINTS: 0, - LINES: 1, - LINE_LOOP: 2, - LINE_STRIP: 3, - TRIANGLES: 4, - TRIANGLE_STRIP: 5, - TRIANGLE_FAN: 6, - UNSIGNED_BYTE: 5121, - UNSIGNED_SHORT: 5123 - }; - - const WEBGL_COMPONENT_TYPES = { - 5120: Int8Array, - 5121: Uint8Array, - 5122: Int16Array, - 5123: Uint16Array, - 5125: Uint32Array, - 5126: Float32Array - }; - - const WEBGL_FILTERS = { - 9728: NearestFilter, - 9729: LinearFilter, - 9984: NearestMipmapNearestFilter, - 9985: LinearMipmapNearestFilter, - 9986: NearestMipmapLinearFilter, - 9987: LinearMipmapLinearFilter - }; - - const WEBGL_WRAPPINGS = { - 33071: ClampToEdgeWrapping, - 33648: MirroredRepeatWrapping, - 10497: RepeatWrapping - }; - - const WEBGL_TYPE_SIZES = { - 'SCALAR': 1, - 'VEC2': 2, - 'VEC3': 3, - 'VEC4': 4, - 'MAT2': 4, - 'MAT3': 9, - 'MAT4': 16 - }; - - const ATTRIBUTES = { - POSITION: 'position', - NORMAL: 'normal', - TANGENT: 'tangent', - TEXCOORD_0: 'uv', - TEXCOORD_1: 'uv2', - COLOR_0: 'color', - WEIGHTS_0: 'skinWeight', - JOINTS_0: 'skinIndex', - }; - - const PATH_PROPERTIES$1 = { - scale: 'scale', - translation: 'position', - rotation: 'quaternion', - weights: 'morphTargetInfluences' - }; - - const INTERPOLATION = { - CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each - // keyframe track will be initialized with a default interpolation type, then modified. - LINEAR: InterpolateLinear, - STEP: InterpolateDiscrete - }; - - const ALPHA_MODES = { - OPAQUE: 'OPAQUE', - MASK: 'MASK', - BLEND: 'BLEND' - }; - - /* UTILITY FUNCTIONS */ - - function resolveURL( url, path ) { - - // Invalid URL - if ( typeof url !== 'string' || url === '' ) return ''; - - // Host Relative URL - if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) { - - path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' ); - - } - - // Absolute URL http://,https://,// - if ( /^(https?:)?\/\//i.test( url ) ) return url; - - // Data URI - if ( /^data:.*,.*$/i.test( url ) ) return url; - - // Blob URL - if ( /^blob:.*$/i.test( url ) ) return url; - - // Relative URL - return path + url; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material - */ - function createDefaultMaterial( cache ) { - - if ( cache[ 'DefaultMaterial' ] === undefined ) { - - cache[ 'DefaultMaterial' ] = new MeshStandardMaterial( { - color: 0xFFFFFF, - emissive: 0x000000, - metalness: 1, - roughness: 1, - transparent: false, - depthTest: true, - side: FrontSide - } ); - - } - - return cache[ 'DefaultMaterial' ]; - - } - - function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { - - // Add unknown glTF extensions to an object's userData. - - for ( const name in objectDef.extensions ) { - - if ( knownExtensions[ name ] === undefined ) { - - object.userData.gltfExtensions = object.userData.gltfExtensions || {}; - object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ]; - - } - - } - - } - - /** - * @param {Object3D|Material|BufferGeometry} object - * @param {GLTF.definition} gltfDef - */ - function assignExtrasToUserData( object, gltfDef ) { - - if ( gltfDef.extras !== undefined ) { - - if ( typeof gltfDef.extras === 'object' ) { - - Object.assign( object.userData, gltfDef.extras ); - - } else { - - console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras ); - - } - - } - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets - * - * @param {BufferGeometry} geometry - * @param {Array} targets - * @param {GLTFParser} parser - * @return {Promise} - */ - function addMorphTargets( geometry, targets, parser ) { - - let hasMorphPosition = false; - let hasMorphNormal = false; - - for ( let i = 0, il = targets.length; i < il; i ++ ) { - - const target = targets[ i ]; - - if ( target.POSITION !== undefined ) hasMorphPosition = true; - if ( target.NORMAL !== undefined ) hasMorphNormal = true; - - if ( hasMorphPosition && hasMorphNormal ) break; - - } - - if ( ! hasMorphPosition && ! hasMorphNormal ) return Promise.resolve( geometry ); - - const pendingPositionAccessors = []; - const pendingNormalAccessors = []; - - for ( let i = 0, il = targets.length; i < il; i ++ ) { - - const target = targets[ i ]; - - if ( hasMorphPosition ) { - - const pendingAccessor = target.POSITION !== undefined - ? parser.getDependency( 'accessor', target.POSITION ) - : geometry.attributes.position; - - pendingPositionAccessors.push( pendingAccessor ); - - } - - if ( hasMorphNormal ) { - - const pendingAccessor = target.NORMAL !== undefined - ? parser.getDependency( 'accessor', target.NORMAL ) - : geometry.attributes.normal; - - pendingNormalAccessors.push( pendingAccessor ); - - } - - } - - return Promise.all( [ - Promise.all( pendingPositionAccessors ), - Promise.all( pendingNormalAccessors ) - ] ).then( function ( accessors ) { - - const morphPositions = accessors[ 0 ]; - const morphNormals = accessors[ 1 ]; - - if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions; - if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals; - geometry.morphTargetsRelative = true; - - return geometry; - - } ); - - } - - /** - * @param {Mesh} mesh - * @param {GLTF.Mesh} meshDef - */ - function updateMorphTargets( mesh, meshDef ) { - - mesh.updateMorphTargets(); - - if ( meshDef.weights !== undefined ) { - - for ( let i = 0, il = meshDef.weights.length; i < il; i ++ ) { - - mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; - - } - - } - - // .extras has user-defined data, so check that .extras.targetNames is an array. - if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) { - - const targetNames = meshDef.extras.targetNames; - - if ( mesh.morphTargetInfluences.length === targetNames.length ) { - - mesh.morphTargetDictionary = {}; - - for ( let i = 0, il = targetNames.length; i < il; i ++ ) { - - mesh.morphTargetDictionary[ targetNames[ i ] ] = i; - - } - - } else { - - console.warn( 'THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.' ); - - } - - } - - } - - function createPrimitiveKey( primitiveDef ) { - - const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]; - let geometryKey; - - if ( dracoExtension ) { - - geometryKey = 'draco:' + dracoExtension.bufferView - + ':' + dracoExtension.indices - + ':' + createAttributesKey( dracoExtension.attributes ); - - } else { - - geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; - - } - - return geometryKey; - - } - - function createAttributesKey( attributes ) { - - let attributesKey = ''; - - const keys = Object.keys( attributes ).sort(); - - for ( let i = 0, il = keys.length; i < il; i ++ ) { - - attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';'; - - } - - return attributesKey; - - } - - function getNormalizedComponentScale( constructor ) { - - // Reference: - // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data - - switch ( constructor ) { - - case Int8Array: - return 1 / 127; - - case Uint8Array: - return 1 / 255; - - case Int16Array: - return 1 / 32767; - - case Uint16Array: - return 1 / 65535; - - default: - throw new Error( 'THREE.GLTFLoader: Unsupported normalized accessor component type.' ); - - } - - } - - /* GLTF PARSER */ - - class GLTFParser { - - constructor( json = {}, options = {} ) { - - this.json = json; - this.extensions = {}; - this.plugins = {}; - this.options = options; - - // loader object cache - this.cache = new GLTFRegistry(); - - // associations between Three.js objects and glTF elements - this.associations = new Map(); - - // BufferGeometry caching - this.primitiveCache = {}; - - // Object3D instance caches - this.meshCache = { refs: {}, uses: {} }; - this.cameraCache = { refs: {}, uses: {} }; - this.lightCache = { refs: {}, uses: {} }; - - this.textureCache = {}; - - // Track node names, to ensure no duplicates - this.nodeNamesUsed = {}; - - // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the - // expensive work of uploading a texture to the GPU off the main thread. - if ( typeof createImageBitmap !== 'undefined' && /Firefox/.test( navigator.userAgent ) === false ) { - - this.textureLoader = new ImageBitmapLoader( this.options.manager ); - - } else { - - this.textureLoader = new TextureLoader( this.options.manager ); - - } - - this.textureLoader.setCrossOrigin( this.options.crossOrigin ); - this.textureLoader.setRequestHeader( this.options.requestHeader ); - - this.fileLoader = new FileLoader( this.options.manager ); - this.fileLoader.setResponseType( 'arraybuffer' ); - - if ( this.options.crossOrigin === 'use-credentials' ) { - - this.fileLoader.setWithCredentials( true ); - - } - - } - - setExtensions( extensions ) { - - this.extensions = extensions; - - } - - setPlugins( plugins ) { - - this.plugins = plugins; - - } - - parse( onLoad, onError ) { - - const parser = this; - const json = this.json; - const extensions = this.extensions; - - // Clear the loader cache - this.cache.removeAll(); - - // Mark the special nodes/meshes in json for efficient parse - this._invokeAll( function ( ext ) { - - return ext._markDefs && ext._markDefs(); - - } ); - - Promise.all( this._invokeAll( function ( ext ) { - - return ext.beforeRoot && ext.beforeRoot(); - - } ) ).then( function () { - - return Promise.all( [ - - parser.getDependencies( 'scene' ), - parser.getDependencies( 'animation' ), - parser.getDependencies( 'camera' ), - - ] ); - - } ).then( function ( dependencies ) { - - const result = { - scene: dependencies[ 0 ][ json.scene || 0 ], - scenes: dependencies[ 0 ], - animations: dependencies[ 1 ], - cameras: dependencies[ 2 ], - asset: json.asset, - parser: parser, - userData: {} - }; - - addUnknownExtensionsToUserData( extensions, result, json ); - - assignExtrasToUserData( result, json ); - - Promise.all( parser._invokeAll( function ( ext ) { - - return ext.afterRoot && ext.afterRoot( result ); - - } ) ).then( function () { - - onLoad( result ); - - } ); - - } ).catch( onError ); - - } - - /** - * Marks the special nodes/meshes in json for efficient parse. - */ - _markDefs() { - - const nodeDefs = this.json.nodes || []; - const skinDefs = this.json.skins || []; - const meshDefs = this.json.meshes || []; - - // Nothing in the node definition indicates whether it is a Bone or an - // Object3D. Use the skins' joint references to mark bones. - for ( let skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) { - - const joints = skinDefs[ skinIndex ].joints; - - for ( let i = 0, il = joints.length; i < il; i ++ ) { - - nodeDefs[ joints[ i ] ].isBone = true; - - } - - } - - // Iterate over all nodes, marking references to shared resources, - // as well as skeleton joints. - for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { - - const nodeDef = nodeDefs[ nodeIndex ]; - - if ( nodeDef.mesh !== undefined ) { - - this._addNodeRef( this.meshCache, nodeDef.mesh ); - - // Nothing in the mesh definition indicates whether it is - // a SkinnedMesh or Mesh. Use the node's mesh reference - // to mark SkinnedMesh if node has skin. - if ( nodeDef.skin !== undefined ) { - - meshDefs[ nodeDef.mesh ].isSkinnedMesh = true; - - } - - } - - if ( nodeDef.camera !== undefined ) { - - this._addNodeRef( this.cameraCache, nodeDef.camera ); - - } - - } - - } - - /** - * Counts references to shared node / Object3D resources. These resources - * can be reused, or "instantiated", at multiple nodes in the scene - * hierarchy. Mesh, Camera, and Light instances are instantiated and must - * be marked. Non-scenegraph resources (like Materials, Geometries, and - * Textures) can be reused directly and are not marked here. - * - * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. - */ - _addNodeRef( cache, index ) { - - if ( index === undefined ) return; - - if ( cache.refs[ index ] === undefined ) { - - cache.refs[ index ] = cache.uses[ index ] = 0; - - } - - cache.refs[ index ] ++; - - } - - /** Returns a reference to a shared resource, cloning it if necessary. */ - _getNodeRef( cache, index, object ) { - - if ( cache.refs[ index ] <= 1 ) return object; - - const ref = object.clone(); - - ref.name += '_instance_' + ( cache.uses[ index ] ++ ); - - return ref; - - } - - _invokeOne( func ) { - - const extensions = Object.values( this.plugins ); - extensions.push( this ); - - for ( let i = 0; i < extensions.length; i ++ ) { - - const result = func( extensions[ i ] ); - - if ( result ) return result; - - } - - return null; - - } - - _invokeAll( func ) { - - const extensions = Object.values( this.plugins ); - extensions.unshift( this ); - - const pending = []; - - for ( let i = 0; i < extensions.length; i ++ ) { - - const result = func( extensions[ i ] ); - - if ( result ) pending.push( result ); - - } - - return pending; - - } - - /** - * Requests the specified dependency asynchronously, with caching. - * @param {string} type - * @param {number} index - * @return {Promise} - */ - getDependency( type, index ) { - - const cacheKey = type + ':' + index; - let dependency = this.cache.get( cacheKey ); - - if ( ! dependency ) { - - switch ( type ) { - - case 'scene': - dependency = this.loadScene( index ); - break; - - case 'node': - dependency = this.loadNode( index ); - break; - - case 'mesh': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadMesh && ext.loadMesh( index ); - - } ); - break; - - case 'accessor': - dependency = this.loadAccessor( index ); - break; - - case 'bufferView': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadBufferView && ext.loadBufferView( index ); - - } ); - break; - - case 'buffer': - dependency = this.loadBuffer( index ); - break; - - case 'material': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadMaterial && ext.loadMaterial( index ); - - } ); - break; - - case 'texture': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadTexture && ext.loadTexture( index ); - - } ); - break; - - case 'skin': - dependency = this.loadSkin( index ); - break; - - case 'animation': - dependency = this.loadAnimation( index ); - break; - - case 'camera': - dependency = this.loadCamera( index ); - break; - - default: - throw new Error( 'Unknown type: ' + type ); - - } - - this.cache.add( cacheKey, dependency ); - - } - - return dependency; - - } - - /** - * Requests all dependencies of the specified type asynchronously, with caching. - * @param {string} type - * @return {Promise>} - */ - getDependencies( type ) { - - let dependencies = this.cache.get( type ); - - if ( ! dependencies ) { - - const parser = this; - const defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || []; - - dependencies = Promise.all( defs.map( function ( def, index ) { - - return parser.getDependency( type, index ); - - } ) ); - - this.cache.add( type, dependencies ); - - } - - return dependencies; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferIndex - * @return {Promise} - */ - loadBuffer( bufferIndex ) { - - const bufferDef = this.json.buffers[ bufferIndex ]; - const loader = this.fileLoader; - - if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) { - - throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' ); - - } - - // If present, GLB container is required to be the first buffer. - if ( bufferDef.uri === undefined && bufferIndex === 0 ) { - - return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body ); - - } - - const options = this.options; - - return new Promise( function ( resolve, reject ) { - - loader.load( resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () { - - reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) ); - - } ); - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferViewIndex - * @return {Promise} - */ - loadBufferView( bufferViewIndex ) { - - const bufferViewDef = this.json.bufferViews[ bufferViewIndex ]; - - return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) { - - const byteLength = bufferViewDef.byteLength || 0; - const byteOffset = bufferViewDef.byteOffset || 0; - return buffer.slice( byteOffset, byteOffset + byteLength ); - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors - * @param {number} accessorIndex - * @return {Promise} - */ - loadAccessor( accessorIndex ) { - - const parser = this; - const json = this.json; - - const accessorDef = this.json.accessors[ accessorIndex ]; - - if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { - - // Ignore empty accessors, which may be used to declare runtime - // information about attributes coming from another source (e.g. Draco - // compression extension). - return Promise.resolve( null ); - - } - - const pendingBufferViews = []; - - if ( accessorDef.bufferView !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) ); - - } else { - - pendingBufferViews.push( null ); - - } - - if ( accessorDef.sparse !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) ); - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) ); - - } - - return Promise.all( pendingBufferViews ).then( function ( bufferViews ) { - - const bufferView = bufferViews[ 0 ]; - - const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - - // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. - const elementBytes = TypedArray.BYTES_PER_ELEMENT; - const itemBytes = elementBytes * itemSize; - const byteOffset = accessorDef.byteOffset || 0; - const byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined; - const normalized = accessorDef.normalized === true; - let array, bufferAttribute; - - // The buffer is not interleaved if the stride is the item size in bytes. - if ( byteStride && byteStride !== itemBytes ) { - - // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer - // This makes sure that IBA.count reflects accessor.count properly - const ibSlice = Math.floor( byteOffset / byteStride ); - const ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count; - let ib = parser.cache.get( ibCacheKey ); - - if ( ! ib ) { - - array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes ); - - // Integer parameters to IB/IBA are in array elements, not bytes. - ib = new InterleavedBuffer( array, byteStride / elementBytes ); - - parser.cache.add( ibCacheKey, ib ); - - } - - bufferAttribute = new InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized ); - - } else { - - if ( bufferView === null ) { - - array = new TypedArray( accessorDef.count * itemSize ); - - } else { - - array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize ); - - } - - bufferAttribute = new BufferAttribute( array, itemSize, normalized ); - - } - - // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors - if ( accessorDef.sparse !== undefined ) { - - const itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR; - const TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ]; - - const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; - const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; - - const sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices ); - const sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize ); - - if ( bufferView !== null ) { - - // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. - bufferAttribute = new BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized ); - - } - - for ( let i = 0, il = sparseIndices.length; i < il; i ++ ) { - - const index = sparseIndices[ i ]; - - bufferAttribute.setX( index, sparseValues[ i * itemSize ] ); - if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] ); - if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] ); - if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] ); - if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.' ); - - } - - } - - return bufferAttribute; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures - * @param {number} textureIndex - * @return {Promise} - */ - loadTexture( textureIndex ) { - - const json = this.json; - const options = this.options; - const textureDef = json.textures[ textureIndex ]; - const source = json.images[ textureDef.source ]; - - let loader = this.textureLoader; - - if ( source.uri ) { - - const handler = options.manager.getHandler( source.uri ); - if ( handler !== null ) loader = handler; - - } - - return this.loadTextureImage( textureIndex, source, loader ); - - } - - loadTextureImage( textureIndex, source, loader ) { - - const parser = this; - const json = this.json; - const options = this.options; - - const textureDef = json.textures[ textureIndex ]; - - const cacheKey = ( source.uri || source.bufferView ) + ':' + textureDef.sampler; - - if ( this.textureCache[ cacheKey ] ) { - - // See https://github.com/mrdoob/three.js/issues/21559. - return this.textureCache[ cacheKey ]; - - } - - const URL = self.URL || self.webkitURL; - - let sourceURI = source.uri || ''; - let isObjectURL = false; - let hasAlpha = true; - - const isJPEG = sourceURI.search( /\.jpe?g($|\?)/i ) > 0 || sourceURI.search( /^data\:image\/jpeg/ ) === 0; - - if ( source.mimeType === 'image/jpeg' || isJPEG ) hasAlpha = false; - - if ( source.bufferView !== undefined ) { - - // Load binary image data from bufferView, if provided. - - sourceURI = parser.getDependency( 'bufferView', source.bufferView ).then( function ( bufferView ) { - - if ( source.mimeType === 'image/png' ) { - - // Inspect the PNG 'IHDR' chunk to determine whether the image could have an - // alpha channel. This check is conservative — the image could have an alpha - // channel with all values == 1, and the indexed type (colorType == 3) only - // sometimes contains alpha. - // - // https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header - const colorType = new DataView( bufferView, 25, 1 ).getUint8( 0, false ); - hasAlpha = colorType === 6 || colorType === 4 || colorType === 3; - - } - - isObjectURL = true; - const blob = new Blob( [ bufferView ], { type: source.mimeType } ); - sourceURI = URL.createObjectURL( blob ); - return sourceURI; - - } ); - - } else if ( source.uri === undefined ) { - - throw new Error( 'THREE.GLTFLoader: Image ' + textureIndex + ' is missing URI and bufferView' ); - - } - - const promise = Promise.resolve( sourceURI ).then( function ( sourceURI ) { - - return new Promise( function ( resolve, reject ) { - - let onLoad = resolve; - - if ( loader.isImageBitmapLoader === true ) { - - onLoad = function ( imageBitmap ) { - - const texture = new Texture( imageBitmap ); - texture.needsUpdate = true; - - resolve( texture ); - - }; - - } - - loader.load( resolveURL( sourceURI, options.path ), onLoad, undefined, reject ); - - } ); - - } ).then( function ( texture ) { - - // Clean up resources and configure Texture. - - if ( isObjectURL === true ) { - - URL.revokeObjectURL( sourceURI ); - - } - - texture.flipY = false; - - if ( textureDef.name ) texture.name = textureDef.name; - - // When there is definitely no alpha channel in the texture, set RGBFormat to save space. - if ( ! hasAlpha ) texture.format = RGBFormat; - - const samplers = json.samplers || {}; - const sampler = samplers[ textureDef.sampler ] || {}; - - texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || LinearFilter; - texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || LinearMipmapLinearFilter; - texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || RepeatWrapping; - texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || RepeatWrapping; - - parser.associations.set( texture, { - type: 'textures', - index: textureIndex - } ); - - return texture; - - } ).catch( function () { - - console.error( 'THREE.GLTFLoader: Couldn\'t load texture', sourceURI ); - return null; - - } ); - - this.textureCache[ cacheKey ] = promise; - - return promise; - - } - - /** - * Asynchronously assigns a texture to the given material parameters. - * @param {Object} materialParams - * @param {string} mapName - * @param {Object} mapDef - * @return {Promise} - */ - assignTexture( materialParams, mapName, mapDef ) { - - const parser = this; - - return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) { - - // Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured - // However, we will copy UV set 0 to UV set 1 on demand for aoMap - if ( mapDef.texCoord !== undefined && mapDef.texCoord != 0 && ! ( mapName === 'aoMap' && mapDef.texCoord == 1 ) ) { - - console.warn( 'THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.' ); - - } - - if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) { - - const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined; - - if ( transform ) { - - const gltfReference = parser.associations.get( texture ); - texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); - parser.associations.set( texture, gltfReference ); - - } - - } - - materialParams[ mapName ] = texture; - - return texture; - - } ); - - } - - /** - * Assigns final material to a Mesh, Line, or Points instance. The instance - * already has a material (generated from the glTF material options alone) - * but reuse of the same glTF material may require multiple threejs materials - * to accommodate different primitive types, defines, etc. New materials will - * be created if necessary, and reused from a cache. - * @param {Object3D} mesh Mesh, Line, or Points instance. - */ - assignFinalMaterial( mesh ) { - - const geometry = mesh.geometry; - let material = mesh.material; - - const useVertexTangents = geometry.attributes.tangent !== undefined; - const useVertexColors = geometry.attributes.color !== undefined; - const useFlatShading = geometry.attributes.normal === undefined; - - if ( mesh.isPoints ) { - - const cacheKey = 'PointsMaterial:' + material.uuid; - - let pointsMaterial = this.cache.get( cacheKey ); - - if ( ! pointsMaterial ) { - - pointsMaterial = new PointsMaterial(); - Material.prototype.copy.call( pointsMaterial, material ); - pointsMaterial.color.copy( material.color ); - pointsMaterial.map = material.map; - pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px - - this.cache.add( cacheKey, pointsMaterial ); - - } - - material = pointsMaterial; - - } else if ( mesh.isLine ) { - - const cacheKey = 'LineBasicMaterial:' + material.uuid; - - let lineMaterial = this.cache.get( cacheKey ); - - if ( ! lineMaterial ) { - - lineMaterial = new LineBasicMaterial(); - Material.prototype.copy.call( lineMaterial, material ); - lineMaterial.color.copy( material.color ); - - this.cache.add( cacheKey, lineMaterial ); - - } - - material = lineMaterial; - - } - - // Clone the material if it will be modified - if ( useVertexTangents || useVertexColors || useFlatShading ) { - - let cacheKey = 'ClonedMaterial:' + material.uuid + ':'; - - if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:'; - if ( useVertexTangents ) cacheKey += 'vertex-tangents:'; - if ( useVertexColors ) cacheKey += 'vertex-colors:'; - if ( useFlatShading ) cacheKey += 'flat-shading:'; - - let cachedMaterial = this.cache.get( cacheKey ); - - if ( ! cachedMaterial ) { - - cachedMaterial = material.clone(); - - if ( useVertexColors ) cachedMaterial.vertexColors = true; - if ( useFlatShading ) cachedMaterial.flatShading = true; - - if ( useVertexTangents ) { - - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1; - if ( cachedMaterial.clearcoatNormalScale ) cachedMaterial.clearcoatNormalScale.y *= - 1; - - } - - this.cache.add( cacheKey, cachedMaterial ); - - this.associations.set( cachedMaterial, this.associations.get( material ) ); - - } - - material = cachedMaterial; - - } - - // workarounds for mesh and geometry - - if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) { - - geometry.setAttribute( 'uv2', geometry.attributes.uv ); - - } - - mesh.material = material; - - } - - getMaterialType( /* materialIndex */ ) { - - return MeshStandardMaterial; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials - * @param {number} materialIndex - * @return {Promise} - */ - loadMaterial( materialIndex ) { - - const parser = this; - const json = this.json; - const extensions = this.extensions; - const materialDef = json.materials[ materialIndex ]; - - let materialType; - const materialParams = {}; - const materialExtensions = materialDef.extensions || {}; - - const pending = []; - - if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) { - - const sgExtension = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ]; - materialType = sgExtension.getMaterialType(); - pending.push( sgExtension.extendParams( materialParams, materialDef, parser ) ); - - } else if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) { - - const kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ]; - materialType = kmuExtension.getMaterialType(); - pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); - - } else { - - // Specification: - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material - - const metallicRoughness = materialDef.pbrMetallicRoughness || {}; - - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - - const array = metallicRoughness.baseColorFactor; - - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; - - } - - if ( metallicRoughness.baseColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) ); - - } - - materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; - materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; - - if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) ); - pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) ); - - } - - materialType = this._invokeOne( function ( ext ) { - - return ext.getMaterialType && ext.getMaterialType( materialIndex ); - - } ); - - pending.push( Promise.all( this._invokeAll( function ( ext ) { - - return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams ); - - } ) ) ); - - } - - if ( materialDef.doubleSided === true ) { - - materialParams.side = DoubleSide; - - } - - const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; - - if ( alphaMode === ALPHA_MODES.BLEND ) { - - materialParams.transparent = true; - - // See: https://github.com/mrdoob/three.js/issues/17706 - materialParams.depthWrite = false; - - } else { - - materialParams.format = RGBFormat; - materialParams.transparent = false; - - if ( alphaMode === ALPHA_MODES.MASK ) { - - materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; - - } - - } - - if ( materialDef.normalTexture !== undefined && materialType !== MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); - - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - materialParams.normalScale = new Vector2( 1, - 1 ); - - if ( materialDef.normalTexture.scale !== undefined ) { - - materialParams.normalScale.set( materialDef.normalTexture.scale, - materialDef.normalTexture.scale ); - - } - - } - - if ( materialDef.occlusionTexture !== undefined && materialType !== MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) ); - - if ( materialDef.occlusionTexture.strength !== undefined ) { - - materialParams.aoMapIntensity = materialDef.occlusionTexture.strength; - - } - - } - - if ( materialDef.emissiveFactor !== undefined && materialType !== MeshBasicMaterial ) { - - materialParams.emissive = new Color().fromArray( materialDef.emissiveFactor ); - - } - - if ( materialDef.emissiveTexture !== undefined && materialType !== MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture ) ); - - } - - return Promise.all( pending ).then( function () { - - let material; - - if ( materialType === GLTFMeshStandardSGMaterial ) { - - material = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams ); - - } else { - - material = new materialType( materialParams ); - - } - - if ( materialDef.name ) material.name = materialDef.name; - - // baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding. - if ( material.map ) material.map.encoding = sRGBEncoding; - if ( material.emissiveMap ) material.emissiveMap.encoding = sRGBEncoding; - - assignExtrasToUserData( material, materialDef ); - - parser.associations.set( material, { type: 'materials', index: materialIndex } ); - - if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); - - return material; - - } ); - - } - - /** When Object3D instances are targeted by animation, they need unique names. */ - createUniqueName( originalName ) { - - const sanitizedName = PropertyBinding.sanitizeNodeName( originalName || '' ); - - let name = sanitizedName; - - for ( let i = 1; this.nodeNamesUsed[ name ]; ++ i ) { - - name = sanitizedName + '_' + i; - - } - - this.nodeNamesUsed[ name ] = true; - - return name; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry - * - * Creates BufferGeometries from primitives. - * - * @param {Array} primitives - * @return {Promise>} - */ - loadGeometries( primitives ) { - - const parser = this; - const extensions = this.extensions; - const cache = this.primitiveCache; - - function createDracoPrimitive( primitive ) { - - return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] - .decodePrimitive( primitive, parser ) - .then( function ( geometry ) { - - return addPrimitiveAttributes( geometry, primitive, parser ); - - } ); - - } - - const pending = []; - - for ( let i = 0, il = primitives.length; i < il; i ++ ) { - - const primitive = primitives[ i ]; - const cacheKey = createPrimitiveKey( primitive ); - - // See if we've already created this geometry - const cached = cache[ cacheKey ]; - - if ( cached ) { - - // Use the cached geometry if it exists - pending.push( cached.promise ); - - } else { - - let geometryPromise; - - if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) { - - // Use DRACO geometry if available - geometryPromise = createDracoPrimitive( primitive ); - - } else { - - // Otherwise create a new geometry - geometryPromise = addPrimitiveAttributes( new BufferGeometry(), primitive, parser ); - - } - - // Cache this geometry - cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise }; - - pending.push( geometryPromise ); - - } - - } - - return Promise.all( pending ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes - * @param {number} meshIndex - * @return {Promise} - */ - loadMesh( meshIndex ) { - - const parser = this; - const json = this.json; - const extensions = this.extensions; - - const meshDef = json.meshes[ meshIndex ]; - const primitives = meshDef.primitives; - - const pending = []; - - for ( let i = 0, il = primitives.length; i < il; i ++ ) { - - const material = primitives[ i ].material === undefined - ? createDefaultMaterial( this.cache ) - : this.getDependency( 'material', primitives[ i ].material ); - - pending.push( material ); - - } - - pending.push( parser.loadGeometries( primitives ) ); - - return Promise.all( pending ).then( function ( results ) { - - const materials = results.slice( 0, results.length - 1 ); - const geometries = results[ results.length - 1 ]; - - const meshes = []; - - for ( let i = 0, il = geometries.length; i < il; i ++ ) { - - const geometry = geometries[ i ]; - const primitive = primitives[ i ]; - - // 1. create Mesh - - let mesh; - - const material = materials[ i ]; - - if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLES || - primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP || - primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN || - primitive.mode === undefined ) { - - // .isSkinnedMesh isn't in glTF spec. See ._markDefs() - mesh = meshDef.isSkinnedMesh === true - ? new SkinnedMesh( geometry, material ) - : new Mesh( geometry, material ); - - if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) { - - // we normalize floating point skin weight array to fix malformed assets (see #15319) - // it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs - mesh.normalizeSkinWeights(); - - } - - if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP ) { - - mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleStripDrawMode ); - - } else if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN ) { - - mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleFanDrawMode ); - - } - - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINES ) { - - mesh = new LineSegments( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_STRIP ) { - - mesh = new Line( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_LOOP ) { - - mesh = new LineLoop( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS$1.POINTS ) { - - mesh = new Points( geometry, material ); - - } else { - - throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode ); - - } - - if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { - - updateMorphTargets( mesh, meshDef ); - - } - - mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) ); - - assignExtrasToUserData( mesh, meshDef ); - - if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive ); - - parser.assignFinalMaterial( mesh ); - - meshes.push( mesh ); - - } - - if ( meshes.length === 1 ) { - - return meshes[ 0 ]; - - } - - const group = new Group(); - - for ( let i = 0, il = meshes.length; i < il; i ++ ) { - - group.add( meshes[ i ] ); - - } - - return group; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras - * @param {number} cameraIndex - * @return {Promise} - */ - loadCamera( cameraIndex ) { - - let camera; - const cameraDef = this.json.cameras[ cameraIndex ]; - const params = cameraDef[ cameraDef.type ]; - - if ( ! params ) { - - console.warn( 'THREE.GLTFLoader: Missing camera parameters.' ); - return; - - } - - if ( cameraDef.type === 'perspective' ) { - - camera = new PerspectiveCamera( MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); - - } else if ( cameraDef.type === 'orthographic' ) { - - camera = new OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar ); - - } - - if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name ); - - assignExtrasToUserData( camera, cameraDef ); - - return Promise.resolve( camera ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins - * @param {number} skinIndex - * @return {Promise} - */ - loadSkin( skinIndex ) { - - const skinDef = this.json.skins[ skinIndex ]; - - const skinEntry = { joints: skinDef.joints }; - - if ( skinDef.inverseBindMatrices === undefined ) { - - return Promise.resolve( skinEntry ); - - } - - return this.getDependency( 'accessor', skinDef.inverseBindMatrices ).then( function ( accessor ) { - - skinEntry.inverseBindMatrices = accessor; - - return skinEntry; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations - * @param {number} animationIndex - * @return {Promise} - */ - loadAnimation( animationIndex ) { - - const json = this.json; - - const animationDef = json.animations[ animationIndex ]; - - const pendingNodes = []; - const pendingInputAccessors = []; - const pendingOutputAccessors = []; - const pendingSamplers = []; - const pendingTargets = []; - - for ( let i = 0, il = animationDef.channels.length; i < il; i ++ ) { - - const channel = animationDef.channels[ i ]; - const sampler = animationDef.samplers[ channel.sampler ]; - const target = channel.target; - const name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated. - const input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input; - const output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output; - - pendingNodes.push( this.getDependency( 'node', name ) ); - pendingInputAccessors.push( this.getDependency( 'accessor', input ) ); - pendingOutputAccessors.push( this.getDependency( 'accessor', output ) ); - pendingSamplers.push( sampler ); - pendingTargets.push( target ); - - } - - return Promise.all( [ - - Promise.all( pendingNodes ), - Promise.all( pendingInputAccessors ), - Promise.all( pendingOutputAccessors ), - Promise.all( pendingSamplers ), - Promise.all( pendingTargets ) - - ] ).then( function ( dependencies ) { - - const nodes = dependencies[ 0 ]; - const inputAccessors = dependencies[ 1 ]; - const outputAccessors = dependencies[ 2 ]; - const samplers = dependencies[ 3 ]; - const targets = dependencies[ 4 ]; - - const tracks = []; - - for ( let i = 0, il = nodes.length; i < il; i ++ ) { - - const node = nodes[ i ]; - const inputAccessor = inputAccessors[ i ]; - const outputAccessor = outputAccessors[ i ]; - const sampler = samplers[ i ]; - const target = targets[ i ]; - - if ( node === undefined ) continue; - - node.updateMatrix(); - node.matrixAutoUpdate = true; - - let TypedKeyframeTrack; - - switch ( PATH_PROPERTIES$1[ target.path ] ) { - - case PATH_PROPERTIES$1.weights: - - TypedKeyframeTrack = NumberKeyframeTrack; - break; - - case PATH_PROPERTIES$1.rotation: - - TypedKeyframeTrack = QuaternionKeyframeTrack; - break; - - case PATH_PROPERTIES$1.position: - case PATH_PROPERTIES$1.scale: - default: - - TypedKeyframeTrack = VectorKeyframeTrack; - break; - - } - - const targetName = node.name ? node.name : node.uuid; - - const interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : InterpolateLinear; - - const targetNames = []; - - if ( PATH_PROPERTIES$1[ target.path ] === PATH_PROPERTIES$1.weights ) { - - // Node may be a Group (glTF mesh with several primitives) or a Mesh. - node.traverse( function ( object ) { - - if ( object.isMesh === true && object.morphTargetInfluences ) { - - targetNames.push( object.name ? object.name : object.uuid ); - - } - - } ); - - } else { - - targetNames.push( targetName ); - - } - - let outputArray = outputAccessor.array; - - if ( outputAccessor.normalized ) { - - const scale = getNormalizedComponentScale( outputArray.constructor ); - const scaled = new Float32Array( outputArray.length ); - - for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) { - - scaled[ j ] = outputArray[ j ] * scale; - - } - - outputArray = scaled; - - } - - for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) { - - const track = new TypedKeyframeTrack( - targetNames[ j ] + '.' + PATH_PROPERTIES$1[ target.path ], - inputAccessor.array, - outputArray, - interpolation - ); - - // Override interpolation with custom factory method. - if ( sampler.interpolation === 'CUBICSPLINE' ) { - - track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) { - - // A CUBICSPLINE keyframe in glTF has three output values for each input value, - // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() - // must be divided by three to get the interpolant's sampleSize argument. - - const interpolantType = ( this instanceof QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant; - - return new interpolantType( this.times, this.values, this.getValueSize() / 3, result ); - - }; - - // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants. - track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true; - - } - - tracks.push( track ); - - } - - } - - const name = animationDef.name ? animationDef.name : 'animation_' + animationIndex; - - return new AnimationClip( name, undefined, tracks ); - - } ); - - } - - createNodeMesh( nodeIndex ) { - - const json = this.json; - const parser = this; - const nodeDef = json.nodes[ nodeIndex ]; - - if ( nodeDef.mesh === undefined ) return null; - - return parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) { - - const node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh ); - - // if weights are provided on the node, override weights on the mesh. - if ( nodeDef.weights !== undefined ) { - - node.traverse( function ( o ) { - - if ( ! o.isMesh ) return; - - for ( let i = 0, il = nodeDef.weights.length; i < il; i ++ ) { - - o.morphTargetInfluences[ i ] = nodeDef.weights[ i ]; - - } - - } ); - - } - - return node; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy - * @param {number} nodeIndex - * @return {Promise} - */ - loadNode( nodeIndex ) { - - const json = this.json; - const extensions = this.extensions; - const parser = this; - - const nodeDef = json.nodes[ nodeIndex ]; - - // reserve node's name before its dependencies, so the root has the intended name. - const nodeName = nodeDef.name ? parser.createUniqueName( nodeDef.name ) : ''; - - return ( function () { - - const pending = []; - - const meshPromise = parser._invokeOne( function ( ext ) { - - return ext.createNodeMesh && ext.createNodeMesh( nodeIndex ); - - } ); - - if ( meshPromise ) { - - pending.push( meshPromise ); - - } - - if ( nodeDef.camera !== undefined ) { - - pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) { - - return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera ); - - } ) ); - - } - - parser._invokeAll( function ( ext ) { - - return ext.createNodeAttachment && ext.createNodeAttachment( nodeIndex ); - - } ).forEach( function ( promise ) { - - pending.push( promise ); - - } ); - - return Promise.all( pending ); - - }() ).then( function ( objects ) { - - let node; - - // .isBone isn't in glTF spec. See ._markDefs - if ( nodeDef.isBone === true ) { - - node = new Bone(); - - } else if ( objects.length > 1 ) { - - node = new Group(); - - } else if ( objects.length === 1 ) { - - node = objects[ 0 ]; - - } else { - - node = new Object3D(); - - } - - if ( node !== objects[ 0 ] ) { - - for ( let i = 0, il = objects.length; i < il; i ++ ) { - - node.add( objects[ i ] ); - - } - - } - - if ( nodeDef.name ) { - - node.userData.name = nodeDef.name; - node.name = nodeName; - - } - - assignExtrasToUserData( node, nodeDef ); - - if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); - - if ( nodeDef.matrix !== undefined ) { - - const matrix = new Matrix4(); - matrix.fromArray( nodeDef.matrix ); - node.applyMatrix4( matrix ); - - } else { - - if ( nodeDef.translation !== undefined ) { - - node.position.fromArray( nodeDef.translation ); - - } - - if ( nodeDef.rotation !== undefined ) { - - node.quaternion.fromArray( nodeDef.rotation ); - - } - - if ( nodeDef.scale !== undefined ) { - - node.scale.fromArray( nodeDef.scale ); - - } - - } - - parser.associations.set( node, { type: 'nodes', index: nodeIndex } ); - - return node; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes - * @param {number} sceneIndex - * @return {Promise} - */ - loadScene( sceneIndex ) { - - const json = this.json; - const extensions = this.extensions; - const sceneDef = this.json.scenes[ sceneIndex ]; - const parser = this; - - // Loader returns Group, not Scene. - // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 - const scene = new Group(); - if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name ); - - assignExtrasToUserData( scene, sceneDef ); - - if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); - - const nodeIds = sceneDef.nodes || []; - - const pending = []; - - for ( let i = 0, il = nodeIds.length; i < il; i ++ ) { - - pending.push( buildNodeHierachy( nodeIds[ i ], scene, json, parser ) ); - - } - - return Promise.all( pending ).then( function () { - - return scene; - - } ); - - } - - } - - function buildNodeHierachy( nodeId, parentObject, json, parser ) { - - const nodeDef = json.nodes[ nodeId ]; - - return parser.getDependency( 'node', nodeId ).then( function ( node ) { - - if ( nodeDef.skin === undefined ) return node; - - // build skeleton here as well - - let skinEntry; - - return parser.getDependency( 'skin', nodeDef.skin ).then( function ( skin ) { - - skinEntry = skin; - - const pendingJoints = []; - - for ( let i = 0, il = skinEntry.joints.length; i < il; i ++ ) { - - pendingJoints.push( parser.getDependency( 'node', skinEntry.joints[ i ] ) ); - - } - - return Promise.all( pendingJoints ); - - } ).then( function ( jointNodes ) { - - node.traverse( function ( mesh ) { - - if ( ! mesh.isMesh ) return; - - const bones = []; - const boneInverses = []; - - for ( let j = 0, jl = jointNodes.length; j < jl; j ++ ) { - - const jointNode = jointNodes[ j ]; - - if ( jointNode ) { - - bones.push( jointNode ); - - const mat = new Matrix4(); - - if ( skinEntry.inverseBindMatrices !== undefined ) { - - mat.fromArray( skinEntry.inverseBindMatrices.array, j * 16 ); - - } - - boneInverses.push( mat ); - - } else { - - console.warn( 'THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[ j ] ); - - } - - } - - mesh.bind( new Skeleton( bones, boneInverses ), mesh.matrixWorld ); - - } ); - - return node; - - } ); - - } ).then( function ( node ) { - - // build node hierachy - - parentObject.add( node ); - - const pending = []; - - if ( nodeDef.children ) { - - const children = nodeDef.children; - - for ( let i = 0, il = children.length; i < il; i ++ ) { - - const child = children[ i ]; - pending.push( buildNodeHierachy( child, node, json, parser ) ); - - } - - } - - return Promise.all( pending ); - - } ); - - } - - /** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - */ - function computeBounds( geometry, primitiveDef, parser ) { - - const attributes = primitiveDef.attributes; - - const box = new Box3(); - - if ( attributes.POSITION !== undefined ) { - - const accessor = parser.json.accessors[ attributes.POSITION ]; - - const min = accessor.min; - const max = accessor.max; - - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - - if ( min !== undefined && max !== undefined ) { - - box.set( - new Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ), - new Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) - ); - - if ( accessor.normalized ) { - - const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); - box.min.multiplyScalar( boxScale ); - box.max.multiplyScalar( boxScale ); - - } - - } else { - - console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); - - return; - - } - - } else { - - return; - - } - - const targets = primitiveDef.targets; - - if ( targets !== undefined ) { - - const maxDisplacement = new Vector3(); - const vector = new Vector3(); - - for ( let i = 0, il = targets.length; i < il; i ++ ) { - - const target = targets[ i ]; - - if ( target.POSITION !== undefined ) { - - const accessor = parser.json.accessors[ target.POSITION ]; - const min = accessor.min; - const max = accessor.max; - - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - - if ( min !== undefined && max !== undefined ) { - - // we need to get max of absolute components because target weight is [-1,1] - vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) ); - vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) ); - vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) ); - - - if ( accessor.normalized ) { - - const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); - vector.multiplyScalar( boxScale ); - - } - - // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative - // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets - // are used to implement key-frame animations and as such only two are active at a time - this results in very large - // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. - maxDisplacement.max( vector ); - - } else { - - console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); - - } - - } - - } - - // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. - box.expandByVector( maxDisplacement ); - - } - - geometry.boundingBox = box; - - const sphere = new Sphere(); - - box.getCenter( sphere.center ); - sphere.radius = box.min.distanceTo( box.max ) / 2; - - geometry.boundingSphere = sphere; - - } - - /** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - * @return {Promise} - */ - function addPrimitiveAttributes( geometry, primitiveDef, parser ) { - - const attributes = primitiveDef.attributes; - - const pending = []; - - function assignAttributeAccessor( accessorIndex, attributeName ) { - - return parser.getDependency( 'accessor', accessorIndex ) - .then( function ( accessor ) { - - geometry.setAttribute( attributeName, accessor ); - - } ); - - } - - for ( const gltfAttributeName in attributes ) { - - const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); - - // Skip attributes already provided by e.g. Draco extension. - if ( threeAttributeName in geometry.attributes ) continue; - - pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) ); - - } - - if ( primitiveDef.indices !== undefined && ! geometry.index ) { - - const accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) { - - geometry.setIndex( accessor ); - - } ); - - pending.push( accessor ); - - } - - assignExtrasToUserData( geometry, primitiveDef ); - - computeBounds( geometry, primitiveDef, parser ); - - return Promise.all( pending ).then( function () { - - return primitiveDef.targets !== undefined - ? addMorphTargets( geometry, primitiveDef.targets, parser ) - : geometry; - - } ); - - } - - /** - * @param {BufferGeometry} geometry - * @param {Number} drawMode - * @return {BufferGeometry} - */ - function toTrianglesDrawMode( geometry, drawMode ) { - - let index = geometry.getIndex(); - - // generate index if not present - - if ( index === null ) { - - const indices = []; - - const position = geometry.getAttribute( 'position' ); - - if ( position !== undefined ) { - - for ( let i = 0; i < position.count; i ++ ) { - - indices.push( i ); - - } - - geometry.setIndex( indices ); - index = geometry.getIndex(); - - } else { - - console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' ); - return geometry; - - } - - } - - // - - const numberOfTriangles = index.count - 2; - const newIndices = []; - - if ( drawMode === TriangleFanDrawMode ) { - - // gl.TRIANGLE_FAN - - for ( let i = 1; i <= numberOfTriangles; i ++ ) { - - newIndices.push( index.getX( 0 ) ); - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - - } - - } else { - - // gl.TRIANGLE_STRIP - - for ( let i = 0; i < numberOfTriangles; i ++ ) { - - if ( i % 2 === 0 ) { - - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i + 2 ) ); - - - } else { - - newIndices.push( index.getX( i + 2 ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i ) ); - - } - - } - - } - - if ( ( newIndices.length / 3 ) !== numberOfTriangles ) { - - console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' ); - - } - - // build final geometry - - const newGeometry = geometry.clone(); - newGeometry.setIndex( newIndices ); - - return newGeometry; - - } - - /** - * @author Deepkolos / https://github.com/deepkolos - */ - - class WorkerPool { - - constructor ( pool = 4 ) { - - this.pool = pool; - this.queue = []; - this.workers = []; - this.workersResolve = []; - this.workerStatus = 0; - - } - - _initWorker ( workerId ) { - - if ( !this.workers[ workerId ] ) { - - const worker = this.workerCreator(); - worker.addEventListener( 'message', this._onMessage.bind( this, workerId ) ); - this.workers[ workerId ] = worker; - - } - - } - - _getIdleWorker () { - - for ( let i = 0 ; i < this.pool ; i ++ ) - if ( ! ( this.workerStatus & ( 1 << i ) ) ) return i; - - return -1; - - } - - _onMessage( workerId, msg ) { - - const resolve = this.workersResolve[ workerId ]; - resolve && resolve( msg ); - - if ( this.queue.length ) { - - const { resolve, msg, transfer } = this.queue.shift(); - this.workersResolve[ workerId ] = resolve; - this.workers[ workerId ].postMessage( msg, transfer ); - - } else { - - this.workerStatus ^= 1 << workerId; - - } - - } - - setWorkerCreator ( workerCreator ) { - - this.workerCreator = workerCreator; - - } - - setWorkerLimit ( pool ) { - - this.pool = pool; - - } - - postMessage ( msg, transfer ) { - - return new Promise( ( resolve ) => { - - const workerId = this._getIdleWorker(); - - if ( workerId !== -1 ) { - - this._initWorker( workerId ); - this.workerStatus |= 1 << workerId; - this.workersResolve[ workerId ] = resolve; - this.workers[ workerId ].postMessage( msg, transfer ); - - } else { - - this.queue.push( { resolve, msg, transfer } ); - - } - - } ); - - } - - dispose () { - - this.workers.forEach( ( worker ) => worker.terminate() ); - this.workersResolve.length = 0; - this.workers.length = 0; - this.queue.length = 0; - this.workerStatus = 0; - - } - - } - - /** - * Loader for KTX 2.0 GPU Texture containers. - * - * KTX 2.0 is a container format for various GPU texture formats. The loader - * supports Basis Universal GPU textures, which can be quickly transcoded to - * a wide variety of GPU texture compression formats. While KTX 2.0 also allows - * other hardware-specific formats, this loader does not yet parse them. - * - * References: - * - KTX: http://github.khronos.org/KTX-Specification/ - * - DFD: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#basicdescriptor - */ - - const KTX2TransferSRGB = 2; - const KTX2_ALPHA_PREMULTIPLIED = 1; - const _taskCache = new WeakMap(); - - class KTX2Loader extends Loader { - - constructor( manager ) { - - super( manager ); - - this.transcoderPath = ''; - this.transcoderBinary = null; - this.transcoderPending = null; - - this.workerPool = new WorkerPool(); - this.workerSourceURL = ''; - this.workerConfig = null; - - if ( typeof MSC_TRANSCODER !== 'undefined' ) { - - console.warn( - - 'THREE.KTX2Loader: Please update to latest "basis_transcoder".' - + ' "msc_basis_transcoder" is no longer supported in three.js r125+.' - - ); - - } - - } - - setTranscoderPath( path ) { - - this.transcoderPath = path; - - return this; - - } - - setWorkerLimit( num ) { - - this.workerPool.setWorkerLimit( num ); - - return this; - - } - - detectSupport( renderer ) { - - this.workerConfig = { - astcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_astc' ), - etc1Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc1' ), - etc2Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc' ), - dxtSupported: renderer.extensions.has( 'WEBGL_compressed_texture_s3tc' ), - bptcSupported: renderer.extensions.has( 'EXT_texture_compression_bptc' ), - pvrtcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_pvrtc' ) - || renderer.extensions.has( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ) - }; - - return this; - - } - - dispose() { - - this.workerPool.dispose(); - if ( this.workerSourceURL ) URL.revokeObjectURL( this.workerSourceURL ); - - return this; - - } - - init() { - - if ( ! this.transcoderPending ) { - - // Load transcoder wrapper. - const jsLoader = new FileLoader( this.manager ); - jsLoader.setPath( this.transcoderPath ); - jsLoader.setWithCredentials( this.withCredentials ); - const jsContent = jsLoader.loadAsync( 'basis_transcoder.js' ); - - // Load transcoder WASM binary. - const binaryLoader = new FileLoader( this.manager ); - binaryLoader.setPath( this.transcoderPath ); - binaryLoader.setResponseType( 'arraybuffer' ); - binaryLoader.setWithCredentials( this.withCredentials ); - const binaryContent = binaryLoader.loadAsync( 'basis_transcoder.wasm' ); - - this.transcoderPending = Promise.all( [ jsContent, binaryContent ] ) - .then( ( [ jsContent, binaryContent ] ) => { - - const fn = KTX2Loader.BasisWorker.toString(); - - const body = [ - '/* constants */', - 'let _EngineFormat = ' + JSON.stringify( KTX2Loader.EngineFormat ), - 'let _TranscoderFormat = ' + JSON.stringify( KTX2Loader.TranscoderFormat ), - 'let _BasisFormat = ' + JSON.stringify( KTX2Loader.BasisFormat ), - '/* basis_transcoder.js */', - jsContent, - '/* worker */', - fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) - ].join( '\n' ); - - this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); - this.transcoderBinary = binaryContent; - - this.workerPool.setWorkerCreator( () => { - - const worker = new Worker( this.workerSourceURL ); - const transcoderBinary = this.transcoderBinary.slice( 0 ); - - worker.postMessage( { type: 'init', config: this.workerConfig, transcoderBinary }, [ transcoderBinary ] ); - - return worker; - - } ); - - } ); - - } - - return this.transcoderPending; - - } - - load( url, onLoad, onProgress, onError ) { - - const loader = new FileLoader( this.manager ); - - loader.setResponseType( 'arraybuffer' ); - loader.setWithCredentials( this.withCredentials ); - - const texture = new CompressedTexture(); - - loader.load( url, ( buffer ) => { - - // Check for an existing task using this buffer. A transferred buffer cannot be transferred - // again from this thread. - if ( _taskCache.has( buffer ) ) { - - const cachedTask = _taskCache.get( buffer ); - - return cachedTask.promise.then( onLoad ).catch( onError ); - - } - - this._createTexture( [ buffer ] ) - .then( function ( _texture ) { - - texture.copy( _texture ); - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - } ) - .catch( onError ); - - }, onProgress, onError ); - - return texture; - - } - - createTextureFrom( transcodeResult ) { - const { mipmaps, width, height, format, type, error, dfdTransferFn, dfdFlags } = transcodeResult; - - if ( type === 'error' ) return Promise.reject( error ); - - const texture = new CompressedTexture( mipmaps, width, height, format, UnsignedByteType ); - texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter; - texture.magFilter = LinearFilter; - texture.generateMipmaps = false; - texture.needsUpdate = true; - texture.encoding = dfdTransferFn === KTX2TransferSRGB ? sRGBEncoding: LinearEncoding; - texture.premultiplyAlpha = !! ( dfdFlags & KTX2_ALPHA_PREMULTIPLIED ); - - return texture; - - } - - /** - * @param {ArrayBuffer[]} buffers - * @param {object?} config - * @return {Promise} - */ - _createTexture( buffers, config = {} ) { - - const taskConfig = config; - const texturePending = this.init().then( () => { - - return this.workerPool.postMessage( { type: 'transcode', buffers, taskConfig: taskConfig }, buffers ); - - } ).then( ( e ) => this.createTextureFrom( e.data ) ); - - // Cache the task result. - _taskCache.set( buffers[ 0 ], { promise: texturePending } ); - - return texturePending; - - } - - dispose() { - - URL.revokeObjectURL( this.workerSourceURL ); - this.workerPool.dispose(); - - return this; - - } - - } - - - /* CONSTANTS */ - - KTX2Loader.BasisFormat = { - ETC1S: 0, - UASTC_4x4: 1, - }; - - KTX2Loader.TranscoderFormat = { - ETC1: 0, - ETC2: 1, - BC1: 2, - BC3: 3, - BC4: 4, - BC5: 5, - BC7_M6_OPAQUE_ONLY: 6, - BC7_M5: 7, - PVRTC1_4_RGB: 8, - PVRTC1_4_RGBA: 9, - ASTC_4x4: 10, - ATC_RGB: 11, - ATC_RGBA_INTERPOLATED_ALPHA: 12, - RGBA32: 13, - RGB565: 14, - BGR565: 15, - RGBA4444: 16, - }; - - KTX2Loader.EngineFormat = { - RGBAFormat: RGBAFormat, - RGBA_ASTC_4x4_Format: RGBA_ASTC_4x4_Format, - RGBA_BPTC_Format: RGBA_BPTC_Format, - RGBA_ETC2_EAC_Format: RGBA_ETC2_EAC_Format, - RGBA_PVRTC_4BPPV1_Format: RGBA_PVRTC_4BPPV1_Format, - RGBA_S3TC_DXT5_Format: RGBA_S3TC_DXT5_Format, - RGB_ETC1_Format: RGB_ETC1_Format, - RGB_ETC2_Format: RGB_ETC2_Format, - RGB_PVRTC_4BPPV1_Format: RGB_PVRTC_4BPPV1_Format, - RGB_S3TC_DXT1_Format: RGB_S3TC_DXT1_Format, - }; - - - /* WEB WORKER */ - - KTX2Loader.BasisWorker = function () { - - let config; - let transcoderPending; - let BasisModule; - - const EngineFormat = _EngineFormat; // eslint-disable-line no-undef - const TranscoderFormat = _TranscoderFormat; // eslint-disable-line no-undef - const BasisFormat = _BasisFormat; // eslint-disable-line no-undef - - self.addEventListener( 'message', function ( e ) { - - const message = e.data; - - switch ( message.type ) { - - case 'init': - config = message.config; - init( message.transcoderBinary ); - break; - - case 'transcode': - transcoderPending.then( () => { - - try { - - const { width, height, hasAlpha, mipmaps, format, dfdTransferFn, dfdFlags } = transcode( message.buffers[ 0 ] ); - - const buffers = []; - - for ( let i = 0; i < mipmaps.length; ++ i ) { - - buffers.push( mipmaps[ i ].data.buffer ); - - } - - self.postMessage( { type: 'transcode', id: message.id, width, height, hasAlpha, mipmaps, format, dfdTransferFn, dfdFlags }, buffers ); - - } catch ( error ) { - - console.error( error ); - - self.postMessage( { type: 'error', id: message.id, error: error.message } ); - - } - - } ); - break; - - } - - } ); - - function init( wasmBinary ) { - - transcoderPending = new Promise( ( resolve ) => { - - BasisModule = { wasmBinary, onRuntimeInitialized: resolve }; - BASIS( BasisModule ); // eslint-disable-line no-undef - - } ).then( () => { - - BasisModule.initializeBasis(); - - if ( BasisModule.KTX2File === undefined ) { - - console.warn( 'THREE.KTX2Loader: Please update Basis Universal transcoder.' ); - - } - - } ); - - } - - function transcode( buffer ) { - - const ktx2File = new BasisModule.KTX2File( new Uint8Array( buffer ) ); - - function cleanup() { - - ktx2File.close(); - ktx2File.delete(); - - } - - if ( !ktx2File.isValid() ) { - - cleanup(); - throw new Error( 'THREE.KTX2Loader: Invalid or unsupported .ktx2 file' ); - - } - - const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S; - const width = ktx2File.getWidth(); - const height = ktx2File.getHeight(); - const levels = ktx2File.getLevels(); - const hasAlpha = ktx2File.getHasAlpha(); - const dfdTransferFn = ktx2File.getDFDTransferFunc(); - const dfdFlags = ktx2File.getDFDFlags(); - - const { transcoderFormat, engineFormat } = getTranscoderFormat( basisFormat, width, height, hasAlpha ); - - if ( ! width || ! height || ! levels ) { - - cleanup(); - throw new Error( 'THREE.KTX2Loader: Invalid texture' ); - - } - - if ( ! ktx2File.startTranscoding() ) { - - cleanup(); - throw new Error( 'THREE.KTX2Loader: .startTranscoding failed' ); - - } - - const mipmaps = []; - - for ( let mip = 0; mip < levels; mip ++ ) { - - const levelInfo = ktx2File.getImageLevelInfo( mip, 0, 0 ); - const mipWidth = levelInfo.origWidth; - const mipHeight = levelInfo.origHeight; - const dst = new Uint8Array( ktx2File.getImageTranscodedSizeInBytes( mip, 0, 0, transcoderFormat ) ); - - const status = ktx2File.transcodeImage( - dst, - mip, - 0, - 0, - transcoderFormat, - 0, - -1, - -1, - ); - - if ( ! status ) { - - cleanup(); - throw new Error( 'THREE.KTX2Loader: .transcodeImage failed.' ); - - } - - mipmaps.push( { data: dst, width: mipWidth, height: mipHeight } ); - - } - - cleanup(); - - return { width, height, hasAlpha, mipmaps, format: engineFormat, dfdTransferFn, dfdFlags }; - - } - - // - - // Optimal choice of a transcoder target format depends on the Basis format (ETC1S or UASTC), - // device capabilities, and texture dimensions. The list below ranks the formats separately - // for ETC1S and UASTC. - // - // In some cases, transcoding UASTC to RGBA32 might be preferred for higher quality (at - // significant memory cost) compared to ETC1/2, BC1/3, and PVRTC. The transcoder currently - // chooses RGBA32 only as a last resort and does not expose that option to the caller. - const FORMAT_OPTIONS = [ - { - if: 'astcSupported', - basisFormat: [ BasisFormat.UASTC_4x4 ], - transcoderFormat: [ TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4 ], - engineFormat: [ EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format ], - priorityETC1S: Infinity, - priorityUASTC: 1, - needsPowerOfTwo: false, - }, - { - if: 'bptcSupported', - basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ], - transcoderFormat: [ TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5 ], - engineFormat: [ EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format ], - priorityETC1S: 3, - priorityUASTC: 2, - needsPowerOfTwo: false, - }, - { - if: 'dxtSupported', - basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ], - transcoderFormat: [ TranscoderFormat.BC1, TranscoderFormat.BC3 ], - engineFormat: [ EngineFormat.RGB_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format ], - priorityETC1S: 4, - priorityUASTC: 5, - needsPowerOfTwo: false, - }, - { - if: 'etc2Supported', - basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ], - transcoderFormat: [ TranscoderFormat.ETC1, TranscoderFormat.ETC2 ], - engineFormat: [ EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format ], - priorityETC1S: 1, - priorityUASTC: 3, - needsPowerOfTwo: false, - }, - { - if: 'etc1Supported', - basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ], - transcoderFormat: [ TranscoderFormat.ETC1, TranscoderFormat.ETC1 ], - engineFormat: [ EngineFormat.RGB_ETC1_Format, EngineFormat.RGB_ETC1_Format ], - priorityETC1S: 2, - priorityUASTC: 4, - needsPowerOfTwo: false, - }, - { - if: 'pvrtcSupported', - basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ], - transcoderFormat: [ TranscoderFormat.PVRTC1_4_RGB, TranscoderFormat.PVRTC1_4_RGBA ], - engineFormat: [ EngineFormat.RGB_PVRTC_4BPPV1_Format, EngineFormat.RGBA_PVRTC_4BPPV1_Format ], - priorityETC1S: 5, - priorityUASTC: 6, - needsPowerOfTwo: true, - }, - ]; - - const ETC1S_OPTIONS = FORMAT_OPTIONS.sort( function ( a, b ) { - - return a.priorityETC1S - b.priorityETC1S; - - } ); - const UASTC_OPTIONS = FORMAT_OPTIONS.sort( function ( a, b ) { - - return a.priorityUASTC - b.priorityUASTC; - - } ); - - function getTranscoderFormat( basisFormat, width, height, hasAlpha ) { - - let transcoderFormat; - let engineFormat; - - const options = basisFormat === BasisFormat.ETC1S ? ETC1S_OPTIONS : UASTC_OPTIONS; - - for ( let i = 0; i < options.length; i ++ ) { - - const opt = options[ i ]; - - if ( ! config[ opt.if ] ) continue; - if ( ! opt.basisFormat.includes( basisFormat ) ) continue; - if ( opt.needsPowerOfTwo && ! ( isPowerOfTwo( width ) && isPowerOfTwo( height ) ) ) continue; - - transcoderFormat = opt.transcoderFormat[ hasAlpha ? 1 : 0 ]; - engineFormat = opt.engineFormat[ hasAlpha ? 1 : 0 ]; - - return { transcoderFormat, engineFormat }; - - } - - console.warn( 'THREE.KTX2Loader: No suitable compressed texture format found. Decoding to RGBA32.' ); - - transcoderFormat = TranscoderFormat.RGBA32; - engineFormat = EngineFormat.RGBAFormat; - - return { transcoderFormat, engineFormat }; - - } - - function isPowerOfTwo( value ) { - - if ( value <= 2 ) return true; - - return ( value & ( value - 1 ) ) === 0 && value !== 0; - - } - - }; - - class GLTFExporter { - - constructor() { - - this.pluginCallbacks = []; - - this.register( function ( writer ) { - - return new GLTFLightExtension( writer ); - - } ); - - this.register( function ( writer ) { - - return new GLTFMaterialsUnlitExtension( writer ); - - } ); - - this.register( function ( writer ) { - - return new GLTFMaterialsPBRSpecularGlossiness( writer ); - - } ); - - this.register( function ( writer ) { - - return new GLTFMaterialsTransmissionExtension( writer ); - - } ); - - this.register( function ( writer ) { - - return new GLTFMaterialsVolumeExtension( writer ); - - } ); - - } - - register( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) { - - this.pluginCallbacks.push( callback ); - - } - - return this; - - } - - unregister( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) { - - this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 ); - - } - - return this; - - } - - /** - * Parse scenes and generate GLTF output - * @param {Scene or [THREE.Scenes]} input Scene or Array of THREE.Scenes - * @param {Function} onDone Callback on completed - * @param {Object} options options - */ - parse( input, onDone, options ) { - - const writer = new GLTFWriter(); - const plugins = []; - - for ( let i = 0, il = this.pluginCallbacks.length; i < il; i ++ ) { - - plugins.push( this.pluginCallbacks[ i ]( writer ) ); - - } - - writer.setPlugins( plugins ); - writer.write( input, onDone, options ); - - } - - } - - //------------------------------------------------------------------------------ - // Constants - //------------------------------------------------------------------------------ - - const WEBGL_CONSTANTS = { - POINTS: 0x0000, - LINES: 0x0001, - LINE_LOOP: 0x0002, - LINE_STRIP: 0x0003, - TRIANGLES: 0x0004, - TRIANGLE_STRIP: 0x0005, - TRIANGLE_FAN: 0x0006, - - UNSIGNED_BYTE: 0x1401, - UNSIGNED_SHORT: 0x1403, - FLOAT: 0x1406, - UNSIGNED_INT: 0x1405, - ARRAY_BUFFER: 0x8892, - ELEMENT_ARRAY_BUFFER: 0x8893, - - NEAREST: 0x2600, - LINEAR: 0x2601, - NEAREST_MIPMAP_NEAREST: 0x2700, - LINEAR_MIPMAP_NEAREST: 0x2701, - NEAREST_MIPMAP_LINEAR: 0x2702, - LINEAR_MIPMAP_LINEAR: 0x2703, - - CLAMP_TO_EDGE: 33071, - MIRRORED_REPEAT: 33648, - REPEAT: 10497 - }; - - const THREE_TO_WEBGL = {}; - - THREE_TO_WEBGL[ NearestFilter ] = WEBGL_CONSTANTS.NEAREST; - THREE_TO_WEBGL[ NearestMipmapNearestFilter ] = WEBGL_CONSTANTS.NEAREST_MIPMAP_NEAREST; - THREE_TO_WEBGL[ NearestMipmapLinearFilter ] = WEBGL_CONSTANTS.NEAREST_MIPMAP_LINEAR; - THREE_TO_WEBGL[ LinearFilter ] = WEBGL_CONSTANTS.LINEAR; - THREE_TO_WEBGL[ LinearMipmapNearestFilter ] = WEBGL_CONSTANTS.LINEAR_MIPMAP_NEAREST; - THREE_TO_WEBGL[ LinearMipmapLinearFilter ] = WEBGL_CONSTANTS.LINEAR_MIPMAP_LINEAR; - - THREE_TO_WEBGL[ ClampToEdgeWrapping ] = WEBGL_CONSTANTS.CLAMP_TO_EDGE; - THREE_TO_WEBGL[ RepeatWrapping ] = WEBGL_CONSTANTS.REPEAT; - THREE_TO_WEBGL[ MirroredRepeatWrapping ] = WEBGL_CONSTANTS.MIRRORED_REPEAT; - - const PATH_PROPERTIES = { - scale: 'scale', - position: 'translation', - quaternion: 'rotation', - morphTargetInfluences: 'weights' - }; - - // GLB constants - // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification - - const GLB_HEADER_BYTES = 12; - const GLB_HEADER_MAGIC = 0x46546C67; - const GLB_VERSION = 2; - - const GLB_CHUNK_PREFIX_BYTES = 8; - const GLB_CHUNK_TYPE_JSON = 0x4E4F534A; - const GLB_CHUNK_TYPE_BIN = 0x004E4942; - - //------------------------------------------------------------------------------ - // Utility functions - //------------------------------------------------------------------------------ - - /** - * Compare two arrays - * @param {Array} array1 Array 1 to compare - * @param {Array} array2 Array 2 to compare - * @return {Boolean} Returns true if both arrays are equal - */ - function equalArray( array1, array2 ) { - - return ( array1.length === array2.length ) && array1.every( function ( element, index ) { - - return element === array2[ index ]; - - } ); - - } - - /** - * Converts a string to an ArrayBuffer. - * @param {string} text - * @return {ArrayBuffer} - */ - function stringToArrayBuffer( text ) { - - if ( window.TextEncoder !== undefined ) { - - return new TextEncoder().encode( text ).buffer; - - } - - const array = new Uint8Array( new ArrayBuffer( text.length ) ); - - for ( let i = 0, il = text.length; i < il; i ++ ) { - - const value = text.charCodeAt( i ); - - // Replacing multi-byte character with space(0x20). - array[ i ] = value > 0xFF ? 0x20 : value; - - } - - return array.buffer; - - } - - /** - * Is identity matrix - * - * @param {Matrix4} matrix - * @returns {Boolean} Returns true, if parameter is identity matrix - */ - function isIdentityMatrix( matrix ) { - - return equalArray( matrix.elements, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] ); - - } - - /** - * Get the min and max vectors from the given attribute - * @param {BufferAttribute} attribute Attribute to find the min/max in range from start to start + count - * @param {Integer} start - * @param {Integer} count - * @return {Object} Object containing the `min` and `max` values (As an array of attribute.itemSize components) - */ - function getMinMax( attribute, start, count ) { - - const output = { - - min: new Array( attribute.itemSize ).fill( Number.POSITIVE_INFINITY ), - max: new Array( attribute.itemSize ).fill( Number.NEGATIVE_INFINITY ) - - }; - - for ( let i = start; i < start + count; i ++ ) { - - for ( let a = 0; a < attribute.itemSize; a ++ ) { - - let value; - - if ( attribute.itemSize > 4 ) { - - // no support for interleaved data for itemSize > 4 - - value = attribute.array[ i * attribute.itemSize + a ]; - - } else { - - if ( a === 0 ) value = attribute.getX( i ); - else if ( a === 1 ) value = attribute.getY( i ); - else if ( a === 2 ) value = attribute.getZ( i ); - else if ( a === 3 ) value = attribute.getW( i ); - - } - - output.min[ a ] = Math.min( output.min[ a ], value ); - output.max[ a ] = Math.max( output.max[ a ], value ); - - } - - } - - return output; - - } - - /** - * Get the required size + padding for a buffer, rounded to the next 4-byte boundary. - * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment - * - * @param {Integer} bufferSize The size the original buffer. - * @returns {Integer} new buffer size with required padding. - * - */ - function getPaddedBufferSize( bufferSize ) { - - return Math.ceil( bufferSize / 4 ) * 4; - - } - - /** - * Returns a buffer aligned to 4-byte boundary. - * - * @param {ArrayBuffer} arrayBuffer Buffer to pad - * @param {Integer} paddingByte (Optional) - * @returns {ArrayBuffer} The same buffer if it's already aligned to 4-byte boundary or a new buffer - */ - function getPaddedArrayBuffer( arrayBuffer, paddingByte = 0 ) { - - const paddedLength = getPaddedBufferSize( arrayBuffer.byteLength ); - - if ( paddedLength !== arrayBuffer.byteLength ) { - - const array = new Uint8Array( paddedLength ); - array.set( new Uint8Array( arrayBuffer ) ); - - if ( paddingByte !== 0 ) { - - for ( let i = arrayBuffer.byteLength; i < paddedLength; i ++ ) { - - array[ i ] = paddingByte; - - } - - } - - return array.buffer; - - } - - return arrayBuffer; - - } - - let cachedCanvas = null; - - /** - * Writer - */ - class GLTFWriter { - - constructor() { - - this.plugins = []; - - this.options = {}; - this.pending = []; - this.buffers = []; - - this.byteOffset = 0; - this.buffers = []; - this.nodeMap = new Map(); - this.skins = []; - this.extensionsUsed = {}; - - this.uids = new Map(); - this.uid = 0; - - this.json = { - asset: { - version: '2.0', - generator: 'THREE.GLTFExporter' - } - }; - - this.cache = { - meshes: new Map(), - attributes: new Map(), - attributesNormalized: new Map(), - materials: new Map(), - textures: new Map(), - images: new Map() - }; - - } - - setPlugins( plugins ) { - - this.plugins = plugins; - - } - - /** - * Parse scenes and generate GLTF output - * @param {Scene or [THREE.Scenes]} input Scene or Array of THREE.Scenes - * @param {Function} onDone Callback on completed - * @param {Object} options options - */ - write( input, onDone, options ) { - - this.options = Object.assign( {}, { - // default options - binary: false, - trs: false, - onlyVisible: true, - truncateDrawRange: true, - embedImages: true, - maxTextureSize: Infinity, - animations: [], - includeCustomExtensions: false - }, options ); - - if ( this.options.animations.length > 0 ) { - - // Only TRS properties, and not matrices, may be targeted by animation. - this.options.trs = true; - - } - - this.processInput( input ); - - const writer = this; - - Promise.all( this.pending ).then( function () { - - const buffers = writer.buffers; - const json = writer.json; - const options = writer.options; - const extensionsUsed = writer.extensionsUsed; - - // Merge buffers. - const blob = new Blob( buffers, { type: 'application/octet-stream' } ); - - // Declare extensions. - const extensionsUsedList = Object.keys( extensionsUsed ); - - if ( extensionsUsedList.length > 0 ) json.extensionsUsed = extensionsUsedList; - - // Update bytelength of the single buffer. - if ( json.buffers && json.buffers.length > 0 ) json.buffers[ 0 ].byteLength = blob.size; - - if ( options.binary === true ) { - - // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification - - const reader = new window.FileReader(); - reader.readAsArrayBuffer( blob ); - reader.onloadend = function () { - - // Binary chunk. - const binaryChunk = getPaddedArrayBuffer( reader.result ); - const binaryChunkPrefix = new DataView( new ArrayBuffer( GLB_CHUNK_PREFIX_BYTES ) ); - binaryChunkPrefix.setUint32( 0, binaryChunk.byteLength, true ); - binaryChunkPrefix.setUint32( 4, GLB_CHUNK_TYPE_BIN, true ); - - // JSON chunk. - const jsonChunk = getPaddedArrayBuffer( stringToArrayBuffer( JSON.stringify( json ) ), 0x20 ); - const jsonChunkPrefix = new DataView( new ArrayBuffer( GLB_CHUNK_PREFIX_BYTES ) ); - jsonChunkPrefix.setUint32( 0, jsonChunk.byteLength, true ); - jsonChunkPrefix.setUint32( 4, GLB_CHUNK_TYPE_JSON, true ); - - // GLB header. - const header = new ArrayBuffer( GLB_HEADER_BYTES ); - const headerView = new DataView( header ); - headerView.setUint32( 0, GLB_HEADER_MAGIC, true ); - headerView.setUint32( 4, GLB_VERSION, true ); - const totalByteLength = GLB_HEADER_BYTES - + jsonChunkPrefix.byteLength + jsonChunk.byteLength - + binaryChunkPrefix.byteLength + binaryChunk.byteLength; - headerView.setUint32( 8, totalByteLength, true ); - - const glbBlob = new Blob( [ - header, - jsonChunkPrefix, - jsonChunk, - binaryChunkPrefix, - binaryChunk - ], { type: 'application/octet-stream' } ); - - const glbReader = new window.FileReader(); - glbReader.readAsArrayBuffer( glbBlob ); - glbReader.onloadend = function () { - - onDone( glbReader.result ); - - }; - - }; - - } else { - - if ( json.buffers && json.buffers.length > 0 ) { - - const reader = new window.FileReader(); - reader.readAsDataURL( blob ); - reader.onloadend = function () { - - const base64data = reader.result; - json.buffers[ 0 ].uri = base64data; - onDone( json ); - - }; - - } else { - - onDone( json ); - - } - - } - - } ); - - } - - /** - * Serializes a userData. - * - * @param {THREE.Object3D|THREE.Material} object - * @param {Object} objectDef - */ - serializeUserData( object, objectDef ) { - - if ( Object.keys( object.userData ).length === 0 ) return; - - const options = this.options; - const extensionsUsed = this.extensionsUsed; - - try { - - const json = JSON.parse( JSON.stringify( object.userData ) ); - - if ( options.includeCustomExtensions && json.gltfExtensions ) { - - if ( objectDef.extensions === undefined ) objectDef.extensions = {}; - - for ( const extensionName in json.gltfExtensions ) { - - objectDef.extensions[ extensionName ] = json.gltfExtensions[ extensionName ]; - extensionsUsed[ extensionName ] = true; - - } - - delete json.gltfExtensions; - - } - - if ( Object.keys( json ).length > 0 ) objectDef.extras = json; - - } catch ( error ) { - - console.warn( 'THREE.GLTFExporter: userData of \'' + object.name + '\' ' + - 'won\'t be serialized because of JSON.stringify error - ' + error.message ); - - } - - } - - /** - * Assign and return a temporal unique id for an object - * especially which doesn't have .uuid - * @param {Object} object - * @return {Integer} - */ - getUID( object ) { - - if ( ! this.uids.has( object ) ) this.uids.set( object, this.uid ++ ); - - return this.uids.get( object ); - - } - - /** - * Checks if normal attribute values are normalized. - * - * @param {BufferAttribute} normal - * @returns {Boolean} - */ - isNormalizedNormalAttribute( normal ) { - - const cache = this.cache; - - if ( cache.attributesNormalized.has( normal ) ) return false; - - const v = new Vector3(); - - for ( let i = 0, il = normal.count; i < il; i ++ ) { - - // 0.0005 is from glTF-validator - if ( Math.abs( v.fromBufferAttribute( normal, i ).length() - 1.0 ) > 0.0005 ) return false; - - } - - return true; - - } - - /** - * Creates normalized normal buffer attribute. - * - * @param {BufferAttribute} normal - * @returns {BufferAttribute} - * - */ - createNormalizedNormalAttribute( normal ) { - - const cache = this.cache; - - if ( cache.attributesNormalized.has( normal ) ) return cache.attributesNormalized.get( normal ); - - const attribute = normal.clone(); - const v = new Vector3(); - - for ( let i = 0, il = attribute.count; i < il; i ++ ) { - - v.fromBufferAttribute( attribute, i ); - - if ( v.x === 0 && v.y === 0 && v.z === 0 ) { - - // if values can't be normalized set (1, 0, 0) - v.setX( 1.0 ); - - } else { - - v.normalize(); - - } - - attribute.setXYZ( i, v.x, v.y, v.z ); - - } - - cache.attributesNormalized.set( normal, attribute ); - - return attribute; - - } - - /** - * Applies a texture transform, if present, to the map definition. Requires - * the KHR_texture_transform extension. - * - * @param {Object} mapDef - * @param {THREE.Texture} texture - */ - applyTextureTransform( mapDef, texture ) { - - let didTransform = false; - const transformDef = {}; - - if ( texture.offset.x !== 0 || texture.offset.y !== 0 ) { - - transformDef.offset = texture.offset.toArray(); - didTransform = true; - - } - - if ( texture.rotation !== 0 ) { - - transformDef.rotation = texture.rotation; - didTransform = true; - - } - - if ( texture.repeat.x !== 1 || texture.repeat.y !== 1 ) { - - transformDef.scale = texture.repeat.toArray(); - didTransform = true; - - } - - if ( didTransform ) { - - mapDef.extensions = mapDef.extensions || {}; - mapDef.extensions[ 'KHR_texture_transform' ] = transformDef; - this.extensionsUsed[ 'KHR_texture_transform' ] = true; - - } - - } - - /** - * Process a buffer to append to the default one. - * @param {ArrayBuffer} buffer - * @return {Integer} - */ - processBuffer( buffer ) { - - const json = this.json; - const buffers = this.buffers; - - if ( ! json.buffers ) json.buffers = [ { byteLength: 0 } ]; - - // All buffers are merged before export. - buffers.push( buffer ); - - return 0; - - } - - /** - * Process and generate a BufferView - * @param {BufferAttribute} attribute - * @param {number} componentType - * @param {number} start - * @param {number} count - * @param {number} target (Optional) Target usage of the BufferView - * @return {Object} - */ - processBufferView( attribute, componentType, start, count, target ) { - - const json = this.json; - - if ( ! json.bufferViews ) json.bufferViews = []; - - // Create a new dataview and dump the attribute's array into it - - let componentSize; - - if ( componentType === WEBGL_CONSTANTS.UNSIGNED_BYTE ) { - - componentSize = 1; - - } else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ) { - - componentSize = 2; - - } else { - - componentSize = 4; - - } - - const byteLength = getPaddedBufferSize( count * attribute.itemSize * componentSize ); - const dataView = new DataView( new ArrayBuffer( byteLength ) ); - let offset = 0; - - for ( let i = start; i < start + count; i ++ ) { - - for ( let a = 0; a < attribute.itemSize; a ++ ) { - - let value; - - if ( attribute.itemSize > 4 ) { - - // no support for interleaved data for itemSize > 4 - - value = attribute.array[ i * attribute.itemSize + a ]; - - } else { - - if ( a === 0 ) value = attribute.getX( i ); - else if ( a === 1 ) value = attribute.getY( i ); - else if ( a === 2 ) value = attribute.getZ( i ); - else if ( a === 3 ) value = attribute.getW( i ); - - } - - if ( componentType === WEBGL_CONSTANTS.FLOAT ) { - - dataView.setFloat32( offset, value, true ); - - } else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_INT ) { - - dataView.setUint32( offset, value, true ); - - } else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ) { - - dataView.setUint16( offset, value, true ); - - } else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_BYTE ) { - - dataView.setUint8( offset, value ); - - } - - offset += componentSize; - - } - - } - - const bufferViewDef = { - - buffer: this.processBuffer( dataView.buffer ), - byteOffset: this.byteOffset, - byteLength: byteLength - - }; - - if ( target !== undefined ) bufferViewDef.target = target; - - if ( target === WEBGL_CONSTANTS.ARRAY_BUFFER ) { - - // Only define byteStride for vertex attributes. - bufferViewDef.byteStride = attribute.itemSize * componentSize; - - } - - this.byteOffset += byteLength; - - json.bufferViews.push( bufferViewDef ); - - // @TODO Merge bufferViews where possible. - const output = { - - id: json.bufferViews.length - 1, - byteLength: 0 - - }; - - return output; - - } - - /** - * Process and generate a BufferView from an image Blob. - * @param {Blob} blob - * @return {Promise} - */ - processBufferViewImage( blob ) { - - const writer = this; - const json = writer.json; - - if ( ! json.bufferViews ) json.bufferViews = []; - - return new Promise( function ( resolve ) { - - const reader = new window.FileReader(); - reader.readAsArrayBuffer( blob ); - reader.onloadend = function () { - - const buffer = getPaddedArrayBuffer( reader.result ); - - const bufferViewDef = { - buffer: writer.processBuffer( buffer ), - byteOffset: writer.byteOffset, - byteLength: buffer.byteLength - }; - - writer.byteOffset += buffer.byteLength; - resolve( json.bufferViews.push( bufferViewDef ) - 1 ); - - }; - - } ); - - } - - /** - * Process attribute to generate an accessor - * @param {BufferAttribute} attribute Attribute to process - * @param {THREE.BufferGeometry} geometry (Optional) Geometry used for truncated draw range - * @param {Integer} start (Optional) - * @param {Integer} count (Optional) - * @return {Integer|null} Index of the processed accessor on the "accessors" array - */ - processAccessor( attribute, geometry, start, count ) { - - const options = this.options; - const json = this.json; - - const types = { - - 1: 'SCALAR', - 2: 'VEC2', - 3: 'VEC3', - 4: 'VEC4', - 16: 'MAT4' - - }; - - let componentType; - - // Detect the component type of the attribute array (float, uint or ushort) - if ( attribute.array.constructor === Float32Array ) { - - componentType = WEBGL_CONSTANTS.FLOAT; - - } else if ( attribute.array.constructor === Uint32Array ) { - - componentType = WEBGL_CONSTANTS.UNSIGNED_INT; - - } else if ( attribute.array.constructor === Uint16Array ) { - - componentType = WEBGL_CONSTANTS.UNSIGNED_SHORT; - - } else if ( attribute.array.constructor === Uint8Array ) { - - componentType = WEBGL_CONSTANTS.UNSIGNED_BYTE; - - } else { - - throw new Error( 'THREE.GLTFExporter: Unsupported bufferAttribute component type.' ); - - } - - if ( start === undefined ) start = 0; - if ( count === undefined ) count = attribute.count; - - // @TODO Indexed buffer geometry with drawRange not supported yet - if ( options.truncateDrawRange && geometry !== undefined && geometry.index === null ) { - - const end = start + count; - const end2 = geometry.drawRange.count === Infinity - ? attribute.count - : geometry.drawRange.start + geometry.drawRange.count; - - start = Math.max( start, geometry.drawRange.start ); - count = Math.min( end, end2 ) - start; - - if ( count < 0 ) count = 0; - - } - - // Skip creating an accessor if the attribute doesn't have data to export - if ( count === 0 ) return null; - - const minMax = getMinMax( attribute, start, count ); - let bufferViewTarget; - - // If geometry isn't provided, don't infer the target usage of the bufferView. For - // animation samplers, target must not be set. - if ( geometry !== undefined ) { - - bufferViewTarget = attribute === geometry.index ? WEBGL_CONSTANTS.ELEMENT_ARRAY_BUFFER : WEBGL_CONSTANTS.ARRAY_BUFFER; - - } - - const bufferView = this.processBufferView( attribute, componentType, start, count, bufferViewTarget ); - - const accessorDef = { - - bufferView: bufferView.id, - byteOffset: bufferView.byteOffset, - componentType: componentType, - count: count, - max: minMax.max, - min: minMax.min, - type: types[ attribute.itemSize ] - - }; - - if ( attribute.normalized === true ) accessorDef.normalized = true; - if ( ! json.accessors ) json.accessors = []; - - return json.accessors.push( accessorDef ) - 1; - - } - - /** - * Process image - * @param {Image} image to process - * @param {Integer} format of the image (e.g. RGBFormat, RGBAFormat etc) - * @param {Boolean} flipY before writing out the image - * @return {Integer} Index of the processed texture in the "images" array - */ - processImage( image, format, flipY ) { - - const writer = this; - const cache = writer.cache; - const json = writer.json; - const options = writer.options; - const pending = writer.pending; - - if ( ! cache.images.has( image ) ) cache.images.set( image, {} ); - - const cachedImages = cache.images.get( image ); - const mimeType = format === RGBAFormat ? 'image/png' : 'image/jpeg'; - const key = mimeType + ':flipY/' + flipY.toString(); - - if ( cachedImages[ key ] !== undefined ) return cachedImages[ key ]; - - if ( ! json.images ) json.images = []; - - const imageDef = { mimeType: mimeType }; - - if ( options.embedImages ) { - - const canvas = cachedCanvas = cachedCanvas || document.createElement( 'canvas' ); - - canvas.width = Math.min( image.width, options.maxTextureSize ); - canvas.height = Math.min( image.height, options.maxTextureSize ); - - const ctx = canvas.getContext( '2d' ); - - if ( flipY === true ) { - - ctx.translate( 0, canvas.height ); - ctx.scale( 1, - 1 ); - - } - - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || - ( typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas ) || - ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - - ctx.drawImage( image, 0, 0, canvas.width, canvas.height ); - - } else { - - if ( format !== RGBAFormat && format !== RGBFormat ) { - - console.error( 'GLTFExporter: Only RGB and RGBA formats are supported.' ); - - } - - if ( image.width > options.maxTextureSize || image.height > options.maxTextureSize ) { - - console.warn( 'GLTFExporter: Image size is bigger than maxTextureSize', image ); - - } - - const data = new Uint8ClampedArray( image.height * image.width * 4 ); - - if ( format === RGBAFormat ) { - - for ( let i = 0; i < data.length; i += 4 ) { - - data[ i + 0 ] = image.data[ i + 0 ]; - data[ i + 1 ] = image.data[ i + 1 ]; - data[ i + 2 ] = image.data[ i + 2 ]; - data[ i + 3 ] = image.data[ i + 3 ]; - - } - - } else { - - for ( let i = 0, j = 0; i < data.length; i += 4, j += 3 ) { - - data[ i + 0 ] = image.data[ j + 0 ]; - data[ i + 1 ] = image.data[ j + 1 ]; - data[ i + 2 ] = image.data[ j + 2 ]; - data[ i + 3 ] = 255; - - } - - } - - ctx.putImageData( new ImageData( data, image.width, image.height ), 0, 0 ); - - } - - if ( options.binary === true ) { - - pending.push( new Promise( function ( resolve ) { - - canvas.toBlob( function ( blob ) { - - writer.processBufferViewImage( blob ).then( function ( bufferViewIndex ) { - - imageDef.bufferView = bufferViewIndex; - resolve(); - - } ); - - }, mimeType ); - - } ) ); - - } else { - - imageDef.uri = canvas.toDataURL( mimeType ); - - } - - } else { - - imageDef.uri = image.src; - - } - - const index = json.images.push( imageDef ) - 1; - cachedImages[ key ] = index; - return index; - - } - - /** - * Process sampler - * @param {Texture} map Texture to process - * @return {Integer} Index of the processed texture in the "samplers" array - */ - processSampler( map ) { - - const json = this.json; - - if ( ! json.samplers ) json.samplers = []; - - const samplerDef = { - magFilter: THREE_TO_WEBGL[ map.magFilter ], - minFilter: THREE_TO_WEBGL[ map.minFilter ], - wrapS: THREE_TO_WEBGL[ map.wrapS ], - wrapT: THREE_TO_WEBGL[ map.wrapT ] - }; - - return json.samplers.push( samplerDef ) - 1; - - } - - /** - * Process texture - * @param {Texture} map Map to process - * @return {Integer} Index of the processed texture in the "textures" array - */ - processTexture( map ) { - - const cache = this.cache; - const json = this.json; - - if ( cache.textures.has( map ) ) return cache.textures.get( map ); - - if ( ! json.textures ) json.textures = []; - - const textureDef = { - sampler: this.processSampler( map ), - source: this.processImage( map.image, map.format, map.flipY ) - }; - - if ( map.name ) textureDef.name = map.name; - - this._invokeAll( function ( ext ) { - - ext.writeTexture && ext.writeTexture( map, textureDef ); - - } ); - - const index = json.textures.push( textureDef ) - 1; - cache.textures.set( map, index ); - return index; - - } - - /** - * Process material - * @param {THREE.Material} material Material to process - * @return {Integer|null} Index of the processed material in the "materials" array - */ - processMaterial( material ) { - - const cache = this.cache; - const json = this.json; - - if ( cache.materials.has( material ) ) return cache.materials.get( material ); - - if ( material.isShaderMaterial ) { - - console.warn( 'GLTFExporter: THREE.ShaderMaterial not supported.' ); - return null; - - } - - if ( ! json.materials ) json.materials = []; - - // @QUESTION Should we avoid including any attribute that has the default value? - const materialDef = { pbrMetallicRoughness: {} }; - - if ( material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true ) { - - console.warn( 'GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.' ); - - } - - // pbrMetallicRoughness.baseColorFactor - const color = material.color.toArray().concat( [ material.opacity ] ); - - if ( ! equalArray( color, [ 1, 1, 1, 1 ] ) ) { - - materialDef.pbrMetallicRoughness.baseColorFactor = color; - - } - - if ( material.isMeshStandardMaterial ) { - - materialDef.pbrMetallicRoughness.metallicFactor = material.metalness; - materialDef.pbrMetallicRoughness.roughnessFactor = material.roughness; - - } else { - - materialDef.pbrMetallicRoughness.metallicFactor = 0.5; - materialDef.pbrMetallicRoughness.roughnessFactor = 0.5; - - } - - // pbrMetallicRoughness.metallicRoughnessTexture - if ( material.metalnessMap || material.roughnessMap ) { - - if ( material.metalnessMap === material.roughnessMap ) { - - const metalRoughMapDef = { index: this.processTexture( material.metalnessMap ) }; - this.applyTextureTransform( metalRoughMapDef, material.metalnessMap ); - materialDef.pbrMetallicRoughness.metallicRoughnessTexture = metalRoughMapDef; - - } else { - - console.warn( 'THREE.GLTFExporter: Ignoring metalnessMap and roughnessMap because they are not the same Texture.' ); - - } - - } - - // pbrMetallicRoughness.baseColorTexture or pbrSpecularGlossiness diffuseTexture - if ( material.map ) { - - const baseColorMapDef = { index: this.processTexture( material.map ) }; - this.applyTextureTransform( baseColorMapDef, material.map ); - materialDef.pbrMetallicRoughness.baseColorTexture = baseColorMapDef; - - } - - if ( material.emissive ) { - - // note: emissive components are limited to stay within the 0 - 1 range to accommodate glTF spec. see #21849 and #22000. - const emissive = material.emissive.clone().multiplyScalar( material.emissiveIntensity ); - const maxEmissiveComponent = Math.max( emissive.r, emissive.g, emissive.b ); - - if ( maxEmissiveComponent > 1 ) { - - emissive.multiplyScalar( 1 / maxEmissiveComponent ); - - console.warn( 'THREE.GLTFExporter: Some emissive components exceed 1; emissive has been limited' ); - - } - - if ( maxEmissiveComponent > 0 ) { - - materialDef.emissiveFactor = emissive.toArray(); - - } - - // emissiveTexture - if ( material.emissiveMap ) { - - const emissiveMapDef = { index: this.processTexture( material.emissiveMap ) }; - this.applyTextureTransform( emissiveMapDef, material.emissiveMap ); - materialDef.emissiveTexture = emissiveMapDef; - - } - - } - - // normalTexture - if ( material.normalMap ) { - - const normalMapDef = { index: this.processTexture( material.normalMap ) }; - - if ( material.normalScale && material.normalScale.x !== 1 ) { - - // glTF normal scale is univariate. Ignore `y`, which may be flipped. - // Context: https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - normalMapDef.scale = material.normalScale.x; - - } - - this.applyTextureTransform( normalMapDef, material.normalMap ); - materialDef.normalTexture = normalMapDef; - - } - - // occlusionTexture - if ( material.aoMap ) { - - const occlusionMapDef = { - index: this.processTexture( material.aoMap ), - texCoord: 1 - }; - - if ( material.aoMapIntensity !== 1.0 ) { - - occlusionMapDef.strength = material.aoMapIntensity; - - } - - this.applyTextureTransform( occlusionMapDef, material.aoMap ); - materialDef.occlusionTexture = occlusionMapDef; - - } - - // alphaMode - if ( material.transparent ) { - - materialDef.alphaMode = 'BLEND'; - - } else { - - if ( material.alphaTest > 0.0 ) { - - materialDef.alphaMode = 'MASK'; - materialDef.alphaCutoff = material.alphaTest; - - } - - } - - // doubleSided - if ( material.side === DoubleSide ) materialDef.doubleSided = true; - if ( material.name !== '' ) materialDef.name = material.name; - - this.serializeUserData( material, materialDef ); - - this._invokeAll( function ( ext ) { - - ext.writeMaterial && ext.writeMaterial( material, materialDef ); - - } ); - - const index = json.materials.push( materialDef ) - 1; - cache.materials.set( material, index ); - return index; - - } - - /** - * Process mesh - * @param {THREE.Mesh} mesh Mesh to process - * @return {Integer|null} Index of the processed mesh in the "meshes" array - */ - processMesh( mesh ) { - - const cache = this.cache; - const json = this.json; - - const meshCacheKeyParts = [ mesh.geometry.uuid ]; - - if ( Array.isArray( mesh.material ) ) { - - for ( let i = 0, l = mesh.material.length; i < l; i ++ ) { - - meshCacheKeyParts.push( mesh.material[ i ].uuid ); - - } - - } else { - - meshCacheKeyParts.push( mesh.material.uuid ); - - } - - const meshCacheKey = meshCacheKeyParts.join( ':' ); - - if ( cache.meshes.has( meshCacheKey ) ) return cache.meshes.get( meshCacheKey ); - - const geometry = mesh.geometry; - let mode; - - // Use the correct mode - if ( mesh.isLineSegments ) { - - mode = WEBGL_CONSTANTS.LINES; - - } else if ( mesh.isLineLoop ) { - - mode = WEBGL_CONSTANTS.LINE_LOOP; - - } else if ( mesh.isLine ) { - - mode = WEBGL_CONSTANTS.LINE_STRIP; - - } else if ( mesh.isPoints ) { - - mode = WEBGL_CONSTANTS.POINTS; - - } else { - - mode = mesh.material.wireframe ? WEBGL_CONSTANTS.LINES : WEBGL_CONSTANTS.TRIANGLES; - - } - - if ( geometry.isBufferGeometry !== true ) { - - throw new Error( 'THREE.GLTFExporter: Geometry is not of type THREE.BufferGeometry.' ); - - } - - const meshDef = {}; - const attributes = {}; - const primitives = []; - const targets = []; - - // Conversion between attributes names in threejs and gltf spec - const nameConversion = { - uv: 'TEXCOORD_0', - uv2: 'TEXCOORD_1', - color: 'COLOR_0', - skinWeight: 'WEIGHTS_0', - skinIndex: 'JOINTS_0' - }; - - const originalNormal = geometry.getAttribute( 'normal' ); - - if ( originalNormal !== undefined && ! this.isNormalizedNormalAttribute( originalNormal ) ) { - - console.warn( 'THREE.GLTFExporter: Creating normalized normal attribute from the non-normalized one.' ); - - geometry.setAttribute( 'normal', this.createNormalizedNormalAttribute( originalNormal ) ); - - } - - // @QUESTION Detect if .vertexColors = true? - // For every attribute create an accessor - let modifiedAttribute = null; - - for ( let attributeName in geometry.attributes ) { - - // Ignore morph target attributes, which are exported later. - if ( attributeName.substr( 0, 5 ) === 'morph' ) continue; - - const attribute = geometry.attributes[ attributeName ]; - attributeName = nameConversion[ attributeName ] || attributeName.toUpperCase(); - - // Prefix all geometry attributes except the ones specifically - // listed in the spec; non-spec attributes are considered custom. - const validVertexAttributes = - /^(POSITION|NORMAL|TANGENT|TEXCOORD_\d+|COLOR_\d+|JOINTS_\d+|WEIGHTS_\d+)$/; - - if ( ! validVertexAttributes.test( attributeName ) ) attributeName = '_' + attributeName; - - if ( cache.attributes.has( this.getUID( attribute ) ) ) { - - attributes[ attributeName ] = cache.attributes.get( this.getUID( attribute ) ); - continue; - - } - - // JOINTS_0 must be UNSIGNED_BYTE or UNSIGNED_SHORT. - modifiedAttribute = null; - const array = attribute.array; - - if ( attributeName === 'JOINTS_0' && - ! ( array instanceof Uint16Array ) && - ! ( array instanceof Uint8Array ) ) { - - console.warn( 'GLTFExporter: Attribute "skinIndex" converted to type UNSIGNED_SHORT.' ); - modifiedAttribute = new BufferAttribute( new Uint16Array( array ), attribute.itemSize, attribute.normalized ); - - } - - const accessor = this.processAccessor( modifiedAttribute || attribute, geometry ); - - if ( accessor !== null ) { - - attributes[ attributeName ] = accessor; - cache.attributes.set( this.getUID( attribute ), accessor ); - - } - - } - - if ( originalNormal !== undefined ) geometry.setAttribute( 'normal', originalNormal ); - - // Skip if no exportable attributes found - if ( Object.keys( attributes ).length === 0 ) return null; - - // Morph targets - if ( mesh.morphTargetInfluences !== undefined && mesh.morphTargetInfluences.length > 0 ) { - - const weights = []; - const targetNames = []; - const reverseDictionary = {}; - - if ( mesh.morphTargetDictionary !== undefined ) { - - for ( const key in mesh.morphTargetDictionary ) { - - reverseDictionary[ mesh.morphTargetDictionary[ key ] ] = key; - - } - - } - - for ( let i = 0; i < mesh.morphTargetInfluences.length; ++ i ) { - - const target = {}; - let warned = false; - - for ( const attributeName in geometry.morphAttributes ) { - - // glTF 2.0 morph supports only POSITION/NORMAL/TANGENT. - // Three.js doesn't support TANGENT yet. - - if ( attributeName !== 'position' && attributeName !== 'normal' ) { - - if ( ! warned ) { - - console.warn( 'GLTFExporter: Only POSITION and NORMAL morph are supported.' ); - warned = true; - - } - - continue; - - } - - const attribute = geometry.morphAttributes[ attributeName ][ i ]; - const gltfAttributeName = attributeName.toUpperCase(); - - // Three.js morph attribute has absolute values while the one of glTF has relative values. - // - // glTF 2.0 Specification: - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#morph-targets - - const baseAttribute = geometry.attributes[ attributeName ]; - - if ( cache.attributes.has( this.getUID( attribute ) ) ) { - - target[ gltfAttributeName ] = cache.attributes.get( this.getUID( attribute ) ); - continue; - - } - - // Clones attribute not to override - const relativeAttribute = attribute.clone(); - - if ( ! geometry.morphTargetsRelative ) { - - for ( let j = 0, jl = attribute.count; j < jl; j ++ ) { - - relativeAttribute.setXYZ( - j, - attribute.getX( j ) - baseAttribute.getX( j ), - attribute.getY( j ) - baseAttribute.getY( j ), - attribute.getZ( j ) - baseAttribute.getZ( j ) - ); - - } - - } - - target[ gltfAttributeName ] = this.processAccessor( relativeAttribute, geometry ); - cache.attributes.set( this.getUID( baseAttribute ), target[ gltfAttributeName ] ); - - } - - targets.push( target ); - - weights.push( mesh.morphTargetInfluences[ i ] ); - - if ( mesh.morphTargetDictionary !== undefined ) targetNames.push( reverseDictionary[ i ] ); - - } - - meshDef.weights = weights; - - if ( targetNames.length > 0 ) { - - meshDef.extras = {}; - meshDef.extras.targetNames = targetNames; - - } - - } - - const isMultiMaterial = Array.isArray( mesh.material ); - - if ( isMultiMaterial && geometry.groups.length === 0 ) return null; - - const materials = isMultiMaterial ? mesh.material : [ mesh.material ]; - const groups = isMultiMaterial ? geometry.groups : [ { materialIndex: 0, start: undefined, count: undefined } ]; - - for ( let i = 0, il = groups.length; i < il; i ++ ) { - - const primitive = { - mode: mode, - attributes: attributes, - }; - - this.serializeUserData( geometry, primitive ); - - if ( targets.length > 0 ) primitive.targets = targets; - - if ( geometry.index !== null ) { - - let cacheKey = this.getUID( geometry.index ); - - if ( groups[ i ].start !== undefined || groups[ i ].count !== undefined ) { - - cacheKey += ':' + groups[ i ].start + ':' + groups[ i ].count; - - } - - if ( cache.attributes.has( cacheKey ) ) { - - primitive.indices = cache.attributes.get( cacheKey ); - - } else { - - primitive.indices = this.processAccessor( geometry.index, geometry, groups[ i ].start, groups[ i ].count ); - cache.attributes.set( cacheKey, primitive.indices ); - - } - - if ( primitive.indices === null ) delete primitive.indices; - - } - - const material = this.processMaterial( materials[ groups[ i ].materialIndex ] ); - - if ( material !== null ) primitive.material = material; - - primitives.push( primitive ); - - } - - meshDef.primitives = primitives; - - if ( ! json.meshes ) json.meshes = []; - - this._invokeAll( function ( ext ) { - - ext.writeMesh && ext.writeMesh( mesh, meshDef ); - - } ); - - const index = json.meshes.push( meshDef ) - 1; - cache.meshes.set( meshCacheKey, index ); - return index; - - } - - /** - * Process camera - * @param {THREE.Camera} camera Camera to process - * @return {Integer} Index of the processed mesh in the "camera" array - */ - processCamera( camera ) { - - const json = this.json; - - if ( ! json.cameras ) json.cameras = []; - - const isOrtho = camera.isOrthographicCamera; - - const cameraDef = { - type: isOrtho ? 'orthographic' : 'perspective' - }; - - if ( isOrtho ) { - - cameraDef.orthographic = { - xmag: camera.right * 2, - ymag: camera.top * 2, - zfar: camera.far <= 0 ? 0.001 : camera.far, - znear: camera.near < 0 ? 0 : camera.near - }; - - } else { - - cameraDef.perspective = { - aspectRatio: camera.aspect, - yfov: MathUtils.degToRad( camera.fov ), - zfar: camera.far <= 0 ? 0.001 : camera.far, - znear: camera.near < 0 ? 0 : camera.near - }; - - } - - // Question: Is saving "type" as name intentional? - if ( camera.name !== '' ) cameraDef.name = camera.type; - - return json.cameras.push( cameraDef ) - 1; - - } - - /** - * Creates glTF animation entry from AnimationClip object. - * - * Status: - * - Only properties listed in PATH_PROPERTIES may be animated. - * - * @param {THREE.AnimationClip} clip - * @param {THREE.Object3D} root - * @return {number|null} - */ - processAnimation( clip, root ) { - - const json = this.json; - const nodeMap = this.nodeMap; - - if ( ! json.animations ) json.animations = []; - - clip = GLTFExporter.Utils.mergeMorphTargetTracks( clip.clone(), root ); - - const tracks = clip.tracks; - const channels = []; - const samplers = []; - - for ( let i = 0; i < tracks.length; ++ i ) { - - const track = tracks[ i ]; - const trackBinding = PropertyBinding.parseTrackName( track.name ); - let trackNode = PropertyBinding.findNode( root, trackBinding.nodeName ); - const trackProperty = PATH_PROPERTIES[ trackBinding.propertyName ]; - - if ( trackBinding.objectName === 'bones' ) { - - if ( trackNode.isSkinnedMesh === true ) { - - trackNode = trackNode.skeleton.getBoneByName( trackBinding.objectIndex ); - - } else { - - trackNode = undefined; - - } - - } - - if ( ! trackNode || ! trackProperty ) { - - console.warn( 'THREE.GLTFExporter: Could not export animation track "%s".', track.name ); - return null; - - } - - const inputItemSize = 1; - let outputItemSize = track.values.length / track.times.length; - - if ( trackProperty === PATH_PROPERTIES.morphTargetInfluences ) { - - outputItemSize /= trackNode.morphTargetInfluences.length; - - } - - let interpolation; - - // @TODO export CubicInterpolant(InterpolateSmooth) as CUBICSPLINE - - // Detecting glTF cubic spline interpolant by checking factory method's special property - // GLTFCubicSplineInterpolant is a custom interpolant and track doesn't return - // valid value from .getInterpolation(). - if ( track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline === true ) { - - interpolation = 'CUBICSPLINE'; - - // itemSize of CUBICSPLINE keyframe is 9 - // (VEC3 * 3: inTangent, splineVertex, and outTangent) - // but needs to be stored as VEC3 so dividing by 3 here. - outputItemSize /= 3; - - } else if ( track.getInterpolation() === InterpolateDiscrete ) { - - interpolation = 'STEP'; - - } else { - - interpolation = 'LINEAR'; - - } - - samplers.push( { - input: this.processAccessor( new BufferAttribute( track.times, inputItemSize ) ), - output: this.processAccessor( new BufferAttribute( track.values, outputItemSize ) ), - interpolation: interpolation - } ); - - channels.push( { - sampler: samplers.length - 1, - target: { - node: nodeMap.get( trackNode ), - path: trackProperty - } - } ); - - } - - json.animations.push( { - name: clip.name || 'clip_' + json.animations.length, - samplers: samplers, - channels: channels - } ); - - return json.animations.length - 1; - - } - - /** - * @param {THREE.Object3D} object - * @return {number|null} - */ - processSkin( object ) { - - const json = this.json; - const nodeMap = this.nodeMap; - - const node = json.nodes[ nodeMap.get( object ) ]; - - const skeleton = object.skeleton; - - if ( skeleton === undefined ) return null; - - const rootJoint = object.skeleton.bones[ 0 ]; - - if ( rootJoint === undefined ) return null; - - const joints = []; - const inverseBindMatrices = new Float32Array( skeleton.bones.length * 16 ); - const temporaryBoneInverse = new Matrix4(); - - for ( let i = 0; i < skeleton.bones.length; ++ i ) { - - joints.push( nodeMap.get( skeleton.bones[ i ] ) ); - temporaryBoneInverse.copy( skeleton.boneInverses[ i ] ); - temporaryBoneInverse.multiply( object.bindMatrix ).toArray( inverseBindMatrices, i * 16 ); - - } - - if ( json.skins === undefined ) json.skins = []; - - json.skins.push( { - inverseBindMatrices: this.processAccessor( new BufferAttribute( inverseBindMatrices, 16 ) ), - joints: joints, - skeleton: nodeMap.get( rootJoint ) - } ); - - const skinIndex = node.skin = json.skins.length - 1; - - return skinIndex; - - } - - /** - * Process Object3D node - * @param {THREE.Object3D} node Object3D to processNode - * @return {Integer} Index of the node in the nodes list - */ - processNode( object ) { - - const json = this.json; - const options = this.options; - const nodeMap = this.nodeMap; - - if ( ! json.nodes ) json.nodes = []; - - const nodeDef = {}; - - if ( options.trs ) { - - const rotation = object.quaternion.toArray(); - const position = object.position.toArray(); - const scale = object.scale.toArray(); - - if ( ! equalArray( rotation, [ 0, 0, 0, 1 ] ) ) { - - nodeDef.rotation = rotation; - - } - - if ( ! equalArray( position, [ 0, 0, 0 ] ) ) { - - nodeDef.translation = position; - - } - - if ( ! equalArray( scale, [ 1, 1, 1 ] ) ) { - - nodeDef.scale = scale; - - } - - } else { - - if ( object.matrixAutoUpdate ) { - - object.updateMatrix(); - - } - - if ( isIdentityMatrix( object.matrix ) === false ) { - - nodeDef.matrix = object.matrix.elements; - - } - - } - - // We don't export empty strings name because it represents no-name in Three.js. - if ( object.name !== '' ) nodeDef.name = String( object.name ); - - this.serializeUserData( object, nodeDef ); - - if ( object.isMesh || object.isLine || object.isPoints ) { - - const meshIndex = this.processMesh( object ); - - if ( meshIndex !== null ) nodeDef.mesh = meshIndex; - - } else if ( object.isCamera ) { - - nodeDef.camera = this.processCamera( object ); - - } - - if ( object.isSkinnedMesh ) this.skins.push( object ); - - if ( object.children.length > 0 ) { - - const children = []; - - for ( let i = 0, l = object.children.length; i < l; i ++ ) { - - const child = object.children[ i ]; - - if ( child.visible || options.onlyVisible === false ) { - - const nodeIndex = this.processNode( child ); - - if ( nodeIndex !== null ) children.push( nodeIndex ); - - } - - } - - if ( children.length > 0 ) nodeDef.children = children; - - } - - this._invokeAll( function ( ext ) { - - ext.writeNode && ext.writeNode( object, nodeDef ); - - } ); - - const nodeIndex = json.nodes.push( nodeDef ) - 1; - nodeMap.set( object, nodeIndex ); - return nodeIndex; - - } - - /** - * Process Scene - * @param {Scene} node Scene to process - */ - processScene( scene ) { - - const json = this.json; - const options = this.options; - - if ( ! json.scenes ) { - - json.scenes = []; - json.scene = 0; - - } - - const sceneDef = {}; - - if ( scene.name !== '' ) sceneDef.name = scene.name; - - json.scenes.push( sceneDef ); - - const nodes = []; - - for ( let i = 0, l = scene.children.length; i < l; i ++ ) { - - const child = scene.children[ i ]; - - if ( child.visible || options.onlyVisible === false ) { - - const nodeIndex = this.processNode( child ); - - if ( nodeIndex !== null ) nodes.push( nodeIndex ); - - } - - } - - if ( nodes.length > 0 ) sceneDef.nodes = nodes; - - this.serializeUserData( scene, sceneDef ); - - } - - /** - * Creates a Scene to hold a list of objects and parse it - * @param {Array} objects List of objects to process - */ - processObjects( objects ) { - - const scene = new Scene(); - scene.name = 'AuxScene'; - - for ( let i = 0; i < objects.length; i ++ ) { - - // We push directly to children instead of calling `add` to prevent - // modify the .parent and break its original scene and hierarchy - scene.children.push( objects[ i ] ); - - } - - this.processScene( scene ); - - } - - /** - * @param {THREE.Object3D|Array} input - */ - processInput( input ) { - - const options = this.options; - - input = input instanceof Array ? input : [ input ]; - - this._invokeAll( function ( ext ) { - - ext.beforeParse && ext.beforeParse( input ); - - } ); - - const objectsWithoutScene = []; - - for ( let i = 0; i < input.length; i ++ ) { - - if ( input[ i ] instanceof Scene ) { - - this.processScene( input[ i ] ); - - } else { - - objectsWithoutScene.push( input[ i ] ); - - } - - } - - if ( objectsWithoutScene.length > 0 ) this.processObjects( objectsWithoutScene ); - - for ( let i = 0; i < this.skins.length; ++ i ) { - - this.processSkin( this.skins[ i ] ); - - } - - for ( let i = 0; i < options.animations.length; ++ i ) { - - this.processAnimation( options.animations[ i ], input[ 0 ] ); - - } - - this._invokeAll( function ( ext ) { - - ext.afterParse && ext.afterParse( input ); - - } ); - - } - - _invokeAll( func ) { - - for ( let i = 0, il = this.plugins.length; i < il; i ++ ) { - - func( this.plugins[ i ] ); - - } - - } - - } - - /** - * Punctual Lights Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual - */ - class GLTFLightExtension { - - constructor( writer ) { - - this.writer = writer; - this.name = 'KHR_lights_punctual'; - - } - - writeNode( light, nodeDef ) { - - if ( ! light.isLight ) return; - - if ( ! light.isDirectionalLight && ! light.isPointLight && ! light.isSpotLight ) { - - console.warn( 'THREE.GLTFExporter: Only directional, point, and spot lights are supported.', light ); - return; - - } - - const writer = this.writer; - const json = writer.json; - const extensionsUsed = writer.extensionsUsed; - - const lightDef = {}; - - if ( light.name ) lightDef.name = light.name; - - lightDef.color = light.color.toArray(); - - lightDef.intensity = light.intensity; - - if ( light.isDirectionalLight ) { - - lightDef.type = 'directional'; - - } else if ( light.isPointLight ) { - - lightDef.type = 'point'; - - if ( light.distance > 0 ) lightDef.range = light.distance; - - } else if ( light.isSpotLight ) { - - lightDef.type = 'spot'; - - if ( light.distance > 0 ) lightDef.range = light.distance; - - lightDef.spot = {}; - lightDef.spot.innerConeAngle = ( light.penumbra - 1.0 ) * light.angle * - 1.0; - lightDef.spot.outerConeAngle = light.angle; - - } - - if ( light.decay !== undefined && light.decay !== 2 ) { - - console.warn( 'THREE.GLTFExporter: Light decay may be lost. glTF is physically-based, ' - + 'and expects light.decay=2.' ); - - } - - if ( light.target - && ( light.target.parent !== light - || light.target.position.x !== 0 - || light.target.position.y !== 0 - || light.target.position.z !== - 1 ) ) { - - console.warn( 'THREE.GLTFExporter: Light direction may be lost. For best results, ' - + 'make light.target a child of the light with position 0,0,-1.' ); - - } - - if ( ! extensionsUsed[ this.name ] ) { - - json.extensions = json.extensions || {}; - json.extensions[ this.name ] = { lights: [] }; - extensionsUsed[ this.name ] = true; - - } - - const lights = json.extensions[ this.name ].lights; - lights.push( lightDef ); - - nodeDef.extensions = nodeDef.extensions || {}; - nodeDef.extensions[ this.name ] = { light: lights.length - 1 }; - - } - - } - - /** - * Unlit Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit - */ - class GLTFMaterialsUnlitExtension { - - constructor( writer ) { - - this.writer = writer; - this.name = 'KHR_materials_unlit'; - - } - - writeMaterial( material, materialDef ) { - - if ( ! material.isMeshBasicMaterial ) return; - - const writer = this.writer; - const extensionsUsed = writer.extensionsUsed; - - materialDef.extensions = materialDef.extensions || {}; - materialDef.extensions[ this.name ] = {}; - - extensionsUsed[ this.name ] = true; - - materialDef.pbrMetallicRoughness.metallicFactor = 0.0; - materialDef.pbrMetallicRoughness.roughnessFactor = 0.9; - - } - - } - - /** - * Specular-Glossiness Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness - */ - class GLTFMaterialsPBRSpecularGlossiness { - - constructor( writer ) { - - this.writer = writer; - this.name = 'KHR_materials_pbrSpecularGlossiness'; - - } - - writeMaterial( material, materialDef ) { - - if ( ! material.isGLTFSpecularGlossinessMaterial ) return; - - const writer = this.writer; - const extensionsUsed = writer.extensionsUsed; - - const extensionDef = {}; - - if ( materialDef.pbrMetallicRoughness.baseColorFactor ) { - - extensionDef.diffuseFactor = materialDef.pbrMetallicRoughness.baseColorFactor; - - } - - const specularFactor = [ 1, 1, 1 ]; - material.specular.toArray( specularFactor, 0 ); - extensionDef.specularFactor = specularFactor; - extensionDef.glossinessFactor = material.glossiness; - - if ( materialDef.pbrMetallicRoughness.baseColorTexture ) { - - extensionDef.diffuseTexture = materialDef.pbrMetallicRoughness.baseColorTexture; - - } - - if ( material.specularMap ) { - - const specularMapDef = { index: writer.processTexture( material.specularMap ) }; - writer.applyTextureTransform( specularMapDef, material.specularMap ); - extensionDef.specularGlossinessTexture = specularMapDef; - - } - - materialDef.extensions = materialDef.extensions || {}; - materialDef.extensions[ this.name ] = extensionDef; - extensionsUsed[ this.name ] = true; - - } - - } - - /** - * Transmission Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission - */ - class GLTFMaterialsTransmissionExtension { - - constructor( writer ) { - - this.writer = writer; - this.name = 'KHR_materials_transmission'; - - } - - writeMaterial( material, materialDef ) { - - if ( ! material.isMeshPhysicalMaterial || material.transmission === 0 ) return; - - const writer = this.writer; - const extensionsUsed = writer.extensionsUsed; - - const extensionDef = {}; - - extensionDef.transmissionFactor = material.transmission; - - if ( material.transmissionMap ) { - - const transmissionMapDef = { index: writer.processTexture( material.transmissionMap ) }; - writer.applyTextureTransform( transmissionMapDef, material.transmissionMap ); - extensionDef.transmissionTexture = transmissionMapDef; - - } - - materialDef.extensions = materialDef.extensions || {}; - materialDef.extensions[ this.name ] = extensionDef; - - extensionsUsed[ this.name ] = true; - - } - - } - - /** - * Materials Volume Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume - */ - class GLTFMaterialsVolumeExtension { - - constructor( writer ) { - - this.writer = writer; - this.name = 'KHR_materials_volume'; - - } - - writeMaterial( material, materialDef ) { - - if ( ! material.isMeshPhysicalMaterial || material.thickness === 0 ) return; - - const writer = this.writer; - const extensionsUsed = writer.extensionsUsed; - - const extensionDef = {}; - - extensionDef.thicknessFactor = material.thickness; - - if ( material.thicknessMap ) { - - const thicknessMapDef = { index: writer.processTexture( material.thicknessMap ) }; - writer.applyTextureTransform( thicknessMapDef, material.thicknessMap ); - extensionDef.thicknessTexture = thicknessMapDef; - - } - - extensionDef.attenuationDistance = material.attenuationDistance; - extensionDef.attenuationColor = material.attenuationTint.toArray(); - - materialDef.extensions = materialDef.extensions || {}; - materialDef.extensions[ this.name ] = extensionDef; - - extensionsUsed[ this.name ] = true; - - } - - } - - /** - * Static utility functions - */ - GLTFExporter.Utils = { - - insertKeyframe: function ( track, time ) { - - const tolerance = 0.001; // 1ms - const valueSize = track.getValueSize(); - - const times = new track.TimeBufferType( track.times.length + 1 ); - const values = new track.ValueBufferType( track.values.length + valueSize ); - const interpolant = track.createInterpolant( new track.ValueBufferType( valueSize ) ); - - let index; - - if ( track.times.length === 0 ) { - - times[ 0 ] = time; - - for ( let i = 0; i < valueSize; i ++ ) { - - values[ i ] = 0; - - } - - index = 0; - - } else if ( time < track.times[ 0 ] ) { - - if ( Math.abs( track.times[ 0 ] - time ) < tolerance ) return 0; - - times[ 0 ] = time; - times.set( track.times, 1 ); - - values.set( interpolant.evaluate( time ), 0 ); - values.set( track.values, valueSize ); - - index = 0; - - } else if ( time > track.times[ track.times.length - 1 ] ) { - - if ( Math.abs( track.times[ track.times.length - 1 ] - time ) < tolerance ) { - - return track.times.length - 1; - - } - - times[ times.length - 1 ] = time; - times.set( track.times, 0 ); - - values.set( track.values, 0 ); - values.set( interpolant.evaluate( time ), track.values.length ); - - index = times.length - 1; - - } else { - - for ( let i = 0; i < track.times.length; i ++ ) { - - if ( Math.abs( track.times[ i ] - time ) < tolerance ) return i; - - if ( track.times[ i ] < time && track.times[ i + 1 ] > time ) { - - times.set( track.times.slice( 0, i + 1 ), 0 ); - times[ i + 1 ] = time; - times.set( track.times.slice( i + 1 ), i + 2 ); - - values.set( track.values.slice( 0, ( i + 1 ) * valueSize ), 0 ); - values.set( interpolant.evaluate( time ), ( i + 1 ) * valueSize ); - values.set( track.values.slice( ( i + 1 ) * valueSize ), ( i + 2 ) * valueSize ); - - index = i + 1; - - break; - - } - - } - - } - - track.times = times; - track.values = values; - - return index; - - }, - - mergeMorphTargetTracks: function ( clip, root ) { - - const tracks = []; - const mergedTracks = {}; - const sourceTracks = clip.tracks; - - for ( let i = 0; i < sourceTracks.length; ++ i ) { - - let sourceTrack = sourceTracks[ i ]; - const sourceTrackBinding = PropertyBinding.parseTrackName( sourceTrack.name ); - const sourceTrackNode = PropertyBinding.findNode( root, sourceTrackBinding.nodeName ); - - if ( sourceTrackBinding.propertyName !== 'morphTargetInfluences' || sourceTrackBinding.propertyIndex === undefined ) { - - // Tracks that don't affect morph targets, or that affect all morph targets together, can be left as-is. - tracks.push( sourceTrack ); - continue; - - } - - if ( sourceTrack.createInterpolant !== sourceTrack.InterpolantFactoryMethodDiscrete - && sourceTrack.createInterpolant !== sourceTrack.InterpolantFactoryMethodLinear ) { - - if ( sourceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - - // This should never happen, because glTF morph target animations - // affect all targets already. - throw new Error( 'THREE.GLTFExporter: Cannot merge tracks with glTF CUBICSPLINE interpolation.' ); - - } - - console.warn( 'THREE.GLTFExporter: Morph target interpolation mode not yet supported. Using LINEAR instead.' ); - - sourceTrack = sourceTrack.clone(); - sourceTrack.setInterpolation( InterpolateLinear ); - - } - - const targetCount = sourceTrackNode.morphTargetInfluences.length; - const targetIndex = sourceTrackNode.morphTargetDictionary[ sourceTrackBinding.propertyIndex ]; - - if ( targetIndex === undefined ) { - - throw new Error( 'THREE.GLTFExporter: Morph target name not found: ' + sourceTrackBinding.propertyIndex ); - - } - - let mergedTrack; - - // If this is the first time we've seen this object, create a new - // track to store merged keyframe data for each morph target. - if ( mergedTracks[ sourceTrackNode.uuid ] === undefined ) { - - mergedTrack = sourceTrack.clone(); - - const values = new mergedTrack.ValueBufferType( targetCount * mergedTrack.times.length ); - - for ( let j = 0; j < mergedTrack.times.length; j ++ ) { - - values[ j * targetCount + targetIndex ] = mergedTrack.values[ j ]; - - } - - // We need to take into consideration the intended target node - // of our original un-merged morphTarget animation. - mergedTrack.name = ( sourceTrackBinding.nodeName || '' ) + '.morphTargetInfluences'; - mergedTrack.values = values; - - mergedTracks[ sourceTrackNode.uuid ] = mergedTrack; - tracks.push( mergedTrack ); - - continue; - - } - - const sourceInterpolant = sourceTrack.createInterpolant( new sourceTrack.ValueBufferType( 1 ) ); - - mergedTrack = mergedTracks[ sourceTrackNode.uuid ]; - - // For every existing keyframe of the merged track, write a (possibly - // interpolated) value from the source track. - for ( let j = 0; j < mergedTrack.times.length; j ++ ) { - - mergedTrack.values[ j * targetCount + targetIndex ] = sourceInterpolant.evaluate( mergedTrack.times[ j ] ); - - } - - // For every existing keyframe of the source track, write a (possibly - // new) keyframe to the merged track. Values from the previous loop may - // be written again, but keyframes are de-duplicated. - for ( let j = 0; j < sourceTrack.times.length; j ++ ) { - - const keyframeIndex = this.insertKeyframe( mergedTrack, sourceTrack.times[ j ] ); - mergedTrack.values[ keyframeIndex * targetCount + targetIndex ] = sourceTrack.values[ j ]; - - } - - } - - clip.tracks = tracks; - - return clip; - - } - - }; - - class Exporter { - constructor(map, scene) { - this.map = map; - this.scene = scene; - } - - exportGLTF(filename, options, scale) { - const gltfExporter = new GLTFExporter(); - options = options ? options : { - trs: false, - onlyVisible: false, - //truncateDrawRange: false, - //includeCustomExtensions: true - }; - scale = scale ? scale : 0.05; - - - const group = new Group(); - group.add(this.scene); - group.scale.set(scale, scale, scale); - group.rotation.z = 90 * Math.PI / 180; - group.rotation.x = -90 * Math.PI / 180; - - /* this.scene.traverse((child) => { - if (child instanceof THREE.Group) { - child.position.set(0,0,child.position.z); - } - }); */ - - this.map.triggerRepaint(); - - gltfExporter.parse(group, (result) => { - if (result instanceof ArrayBuffer) { - this._saveArrayBuffer(result, filename); - } else { - const output = JSON.stringify(result, null, 2); - this._saveString(output, filename); - } - - }, options); - } - - _save(blob, filename) { - const link = document.createElement('a'); - link.style.display = 'none'; - document.body.appendChild(link); // Firefox workaround - - link.href = URL.createObjectURL(blob); - link.download = filename; - link.click(); - } - - _saveString(text, filename) { - this._save(new Blob([text], { type: 'text/plain' }), filename); - } - - _saveArrayBuffer(buffer, filename) { - this._save(new Blob([buffer], { type: 'application/octet-stream' }), filename); - } - } - - class TilesetManager { - constructor(world, loader, tilesetConfigs) { - this.loaded = false; - this.world = world; - this.loader = loader; - this.tilesetLayers = []; - this.initTilesetConfigs = tilesetConfigs; - } - - load(map, cameraSync) { - this.map = map; - this.map.on('move', () => this._moving()); - this.cameraSync = cameraSync; - this._createTilesetsFromConfig(this.initTilesetConfigs); - this.initTilesetConfigs = []; - this.loaded = true; - } - - getTilesets() { - return this.tilesetLayers.map((t) => { return { id: t.tilesetId, tileset: t } }); - } - - getTileset(id) { - for(let i = 0; i < this.tilesetLayers.length; i++) { - if(this.tilesetLayers[i].tilesetId == id) { - return this.tilesetLayers[i]; - } - } - - return undefined; - } - - addTileset(tilesetConfig) { - if (this.loaded === false) { - this.initTilesetConfigs.push(tilesetConfig); - } else { - this._createTilesetsFromConfig([tilesetConfig]); - } - } - - removeTileset(id) { - let tilesetLayer = {}; - - for (let i = 0; i < this.tilesetLayers; i++) { - if (this.tilesetLayers[i].id == id) { - tilesetLayer = this.tilesetLayers[i]; - break; - } - } - - this._removeTilesetLayer(tilesetLayer); - } - - _addTilesetLayer(layer) { - this.tilesetLayers.push(layer); - this.world.add(layer); - this.cameraSync.updateCamera(); - } - - _removeTilesetLayer(layer) { - this.world.remove(layer); - let position = this.tilesetLayers.indexOf(layer); - this.tilesetLayers.splice(position, 1); - } - - _createTilesetsFromConfig(tilesets) { - for (let i = 0; i < tilesets.length; i++) { - const tilesetConfig = tilesets[i]; - const tilesetLayer = this._createTilesetLayer(tilesetConfig); - this._addTilesetLayer(tilesetLayer); - } - } - - _createTilesetLayer(tilesetConfig) { - const tilesetLayer = new TilesetLayer(this.map, this.loader, this.cameraSync, tilesetConfig, ()=> this._renderCallback()); - tilesetLayer.load(); - return tilesetLayer; - } - - _moving() { - if (this.timeoutHandle) { - window.clearTimeout(this.timeoutHandle); - } - } - - _renderCallback() { - if (this.timeoutHandle) { - window.clearTimeout(this.timeoutHandle); - } - - this.timeoutHandle = window.setTimeout(() => { this._runRequestRender(); }, 100); - } - - _runRequestRender() { - this.map.triggerRepaint(); - } - } - - var InfiniteGridVert = ` - varying vec3 worldPosition; - varying vec3 camPosition; - - uniform vec3 uMapCenterWorld; - uniform vec3 uMapCameraWorld; - uniform vec3 uOrigin; - uniform float uDistance; - - void main() { - vec3 camDiff = vec3(uOrigin.x - uMapCameraWorld.x, uOrigin.y - uMapCameraWorld.y, uOrigin.z - uMapCameraWorld.z); - camPosition.xy -= camDiff.xy; - - vec3 pos = position.xyz * uDistance; - vec3 diff = vec3(uOrigin.x - uMapCenterWorld.x, uOrigin.y - uMapCenterWorld.y, uOrigin.z - uMapCenterWorld.z); - pos.xy -= diff.xy; - - worldPosition = pos; - gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0); - } -`; - - var InfiniteGridFrag = ` - varying vec3 worldPosition; - varying vec3 camPosition; - - uniform float uSize1; - uniform float uSize2; - uniform vec3 uColor; - uniform float uDistance; - - float getGrid(float size) { - vec2 r = worldPosition.xy / size; - vec2 grid = abs(fract(r - 0.5) - 0.5) / fwidth(r); - float line = min(grid.x, grid.y); - - return 1.0 - min(line, 1.0); - } - - void main() { - float d = 1.0 - min(distance(worldPosition.xyz, camPosition.xyz) / uDistance, 1.0); - float g1 = getGrid(uSize1); - float g2 = getGrid(uSize2); - - gl_FragColor = vec4(uColor.rgb, mix(g2, g1, g1) * pow(d, 3.0)); - gl_FragColor.a = mix(0.5 * gl_FragColor.a, gl_FragColor.a, g2); - - if ( gl_FragColor.a <= 0.0 ) discard; - } -`; - - class InfiniteGridMaterial extends ShaderMaterial { - constructor(origin, size1, size2, color, distance) { - super(); - - this.side = DoubleSide; - this.defines = {}; - this.uniforms = this._getUniforms(origin, size1, size2, color, distance); - this.vertexShader = InfiniteGridVert; - this.fragmentShader = InfiniteGridFrag; - this.transparent = true; - this.extensions = { - derivatives: true - }; - } - - _getUniforms(origin, size1, size2, color, distance) { - color = color ? new Color(color) : new Color('red'); - size1 = size1 || 10; - size2 = size2 || 100; - distance = distance || 2000; - - return { - uSize1: { - value: size1 - }, - uSize2: { - value: size2 - }, - uColor: { - value: color - }, - uDistance: { - value: distance - }, - uMapCenterWorld: { - value: new Vector3(0, 0, 0) - }, - uMapCameraWorld: { - value: new Vector3(0, 0, 0) - }, - uOrigin: { - value: new Vector3(origin.x, origin.y, origin.z) - } - }; - } - } - - class InfiniteGrid extends Mesh { - constructor(map, cameraSync, height, size1, size2, color, distance) { - super(new PlaneGeometry(2, 2, 1, 1)); - - this.map = map; - this.cameraSync = cameraSync; - this.frustumCulled = false; - this.mapCenterWorld = this._getMapCenterWorld(); - this.material = new InfiniteGridMaterial(this.mapCenterWorld, size1, size2, color, distance); - this.position.set(this.mapCenterWorld.x, this.mapCenterWorld.y, height); - - this._updateGrid = () => this._update(); - this.map.on('move', this._updateGrid); - } - - _getMapCenterWorld() { - const center = this.map.getCenter(); - const location = [center.lng, center.lat, 0]; - return projectToWorld(location); - } - - _update() { - if(!this.map) { - return; - } - - const mapCenterWorld = this._getMapCenterWorld(); - - this.material.uniforms.uMapCenterWorld.value = new Vector3( - mapCenterWorld.x, - mapCenterWorld.y, - mapCenterWorld.z - ); - - this.material.uniforms.uMapCameraWorld.value = new Vector3( - this.cameraSync.cameraPosition.x, - this.cameraSync.cameraPosition.y, - this.cameraSync.cameraPosition.z - ); - - this.material.needsUpdate = true; - } - - dispose() { - this.map.off('move', this._updateGrid); - - this.material.dispose(); - this.material = undefined; - this.map = undefined; - this.cameraSync = undefined; - this.mapCenterWorld = undefined; - } - } - - class LayerScene extends Scene { - constructor(map, cameraSync, world) { - super(); - - this.map = map; - this.cameraSync = cameraSync; - this.world = world; - this.lights = this._createLight(); - this.shadowMaterial = this._createShadowMaterial(); - this.shadowPlane = this._createShadowPlane(this.shadowMaterial); - - this.lights.forEach((light) => { - this.add(light); - }); - - this.add(this.world); - this.addShadow(); - - this.map.on('move', () => this._cameraUpdated()); - this._setBelowSurfaceState(); - } - - // Terrain shadow only working when something with transparant material is added to the scene - // ToDo: figure out why it does not work without - addTerrainShadowWorkaround() { - const geometry = new BoxGeometry(1, 1, 1); - const material = new MeshBasicMaterial({ transparent: true }); - const cube = new Mesh(geometry, material); - this.add(cube); - } - - addShadow() { - this.add(this.shadowPlane); - } - - removeShadow() { - this.remove(this.shadowPlane); - } - - setShadowOpacity(opacity) { - const newOpacity = opacity < 0 ? 0.0 : opacity > 1 ? 1.0 : opacity; - this.shadowMaterial.opacity = newOpacity; - } - - setHemisphereIntensity(intensity) { - if (this.lights[0] instanceof HemisphereLight) { - const newIntensity = intensity < 0 ? 0.0 : intensity > 1 ? 1.0 : intensity; - this.lights[0].intensity = newIntensity; - } - } - - _createLight() { - const width = window.innerWidth; - const height = window.innerHeight; - const hemiLight = new HemisphereLight(0xffffff, 0xbebebe, 0.7); - const dirLight = this._getDefaultDirLight(width, height); - - return [hemiLight, dirLight]; - } - - _getDefaultDirLight(width, height) { - const dirLight = new DirectionalLight(0xffffff, 0.5); - dirLight.color.setHSL(0.1, 1, 0.95); - dirLight.position.set(-1, -1.75, 1); - dirLight.position.multiplyScalar(100); - dirLight.castShadow = true; - dirLight.shadow.camera.near = -10000; - dirLight.shadow.camera.far = 30000; - dirLight.shadow.radius = 5; - dirLight.shadow.bias = -0.0002; - dirLight.shadow.mapSize.width = width * 4; - dirLight.shadow.mapSize.height = height * 10; - dirLight.shadow.camera.left = -width; - dirLight.shadow.camera.right = width; - dirLight.shadow.camera.top = -height * 2.5; - dirLight.shadow.camera.bottom = height * 2.5; - dirLight.uuid = 'shadowlight'; - - return dirLight; - } - - _createShadowMaterial() { - const shadowMaterial = new ShadowMaterial(); - shadowMaterial.opacity = 0.09; - - return shadowMaterial; - } - - _createShadowPlane(shadowMaterial) { - var planeGeometry = new PlaneGeometry(10000, 10000, 1, 1); - const shadowPlane = new Mesh(planeGeometry, shadowMaterial); - shadowPlane.receiveShadow = true; - shadowPlane.castShadow = false; - - return shadowPlane; - } - - _cameraUpdated() { - this.cameraHeight = this.cameraSync.cameraPosition.z; - - if ((!this.belowSurface && this.cameraHeight < 0) || (this.belowSurface && this.cameraHeight >= 0)) { - this._setBelowSurfaceState(); - } - } - - _createSurfacePlane() { - var geo = new PlaneGeometry(30000, 30000, 1, 1); - var mat = new MeshBasicMaterial({ - color: 0xc0c0c0, - side: BackSide, - opacity: 0.6, - transparent: true, - depthWrite: true - }); - const plane = new Mesh(geo, mat); - - return plane; - } - - _setBelowSurfaceState() { - const newState = this.cameraHeight < 0 ? true : false; - if (this.belowSurface && newState == this.belowSurface) { - return; - } - - this.belowSurface = newState; - this._update(this.belowSurface); - } - - _update(belowSurface) { - if (belowSurface) { - //this._addSurfacePlane(); - this._addGrid(-10, 10, 100, '#ff0000', 2300); - } else { - //this._removeSurfacePlane(); - this._removeGrid(); - } - } - - _addGrid(height, size1, size2, color, distance) { - this.grid = new InfiniteGrid(this.map, this.cameraSync, height, size1, size2, color, distance); - this.world.add(this.grid); - } - - _removeGrid() { - if (!this.grid) { - return; - } - - this.world.remove(this.grid); - this.grid.dispose(); - delete this.grid; - } - - _addSurfacePlane() { - this.surfacePlane = this._createSurfacePlane(); - this.add(this.surfacePlane); - } - - _removeSurfacePlane() { - if (!this.surfacePlane) { - return; - } - - this.removeGrid(); - this.remove(this.surfacePlane); - this.surfacePlane.geometry.dispose(); - this.surfacePlane.material.dispose(); - delete this.surfacePlane; - } - } - - class Mapbox3DTilesLayer { - constructor(params) { - if (!params) throw new Error('parameters missing for mapbox 3D tiles layer'); - if (!params.id) throw new Error('id parameter missing for mapbox 3D tiles layer'); - - this.params = params; - this._setup(params); - } - - _setup(params) { - this.id = params.id; - this.world = this._createWorld(); - this.loader = this._createLoader(params.dracoDecoderPath); - this.tilesetManager = new TilesetManager(this.world, this.loader, params.tilesets); - this.type = 'custom'; - this.renderingMode = '3d'; - - window.addEventListener('resize', (e) => { - this._resize(e); - }); - } - - _setupOnAdd() { - this.camera = this._createCamera(); - this.mapQueryRenderedFeatures = this.map.queryRenderedFeatures.bind(this.map); - this.map.queryRenderedFeatures = this.queryRenderedFeatures.bind(this); - this.cameraSync = this._createCameraSync(this.map, this.camera, this.world); - this.scene = new LayerScene(this.map, this.cameraSync, this.world); - this.featureInfo = new FeatureInfo(this.scene, this.map, this.camera, this.loader, this.params.selectMaterial); - this.highlight = new Highlight(this.scene, this.map); - this.renderer = this._createRenderer(); - this.exporter = new Exporter(this.map, this.scene); - } - - _createLoader(dracoPath) { - const loader = new GLTFLoader(); - - if (dracoPath) { - let dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath(dracoPath); - - loader.setDRACOLoader(dracoLoader); - } - - var ktx2loader = new KTX2Loader(); - loader.setKTX2Loader(ktx2loader); - - return loader; - } - - onAdd(map) { - this.map = map; - this._setupOnAdd(); - this.tilesetManager.load(this.map, this.cameraSync); - } - - onRemove(map, gl) { - // todo: (much) more cleanup? - this.map.queryRenderedFeatures = this.mapQueryRenderedFeatures; - //this.sceneManager.removeLayer(this); - } - - queryRenderedFeatures(geometry, options) { - let result = this.mapQueryRenderedFeatures(geometry, options); - return this.query(geometry, options, result); - } - - query(geometry, options, result) { - if (!this.map || !this.map.transform) { - return result; - } - - if (geometry && geometry.x && geometry.y) { - result = this.featureInfo.getAt(result, geometry.x, geometry.y); - } - - return result; - } - - _createCamera() { - const camera = new PerspectiveCamera(0, 0, 0, 0); - return camera; - } - - _createCameraSync(map, camera, world) { - const cameraSync = new CameraSync(map, camera, world); - cameraSync.aspect = window.innerWidth / window.innerHeight; - cameraSync.updateCallback = () => this._loadVisibleTiles(); - camera.updateProjectionMatrix(); - - return cameraSync; - } - - _createWorld() { - const world = new Group(); - return world; - } - - _createRenderer(gl) { - const renderer = new WebGLRenderer({ - alpha: true, - antialias: true, - canvas: this.map.getCanvas(), - context: gl - }); - - renderer.outputEncoding = sRGBEncoding; - renderer.autoClear = false; - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = PCFShadowMap; - - return renderer; - } - - async _loadVisibleTiles() { - for (let i = 0; i < this.tilesetManager.tilesetLayers.length; i++) { - const layer = this.tilesetManager.tilesetLayers[i]; - layer.checkLoad(this.cameraSync.frustum, this.cameraSync.cameraPosition); - } - } - - _resize(e) { - if (!this.renderer) { - return; - } - - let width = this.map.transform.size.x; - let height = this.map.transform.size.y; - this.renderer.setSize(width, height); - - for (let i = 0; i < this.scene.children.length; i++) { - let c = this.scene.children[i]; - if (c.uuid === 'shadowlight') { - c = this.scene._getDefaultDirLight(width, height); - } - } - } - - render() { - if (!this.renderer) { - return; - } - - // this._updateMarkers(); - this.renderer.state.reset(); - this.renderer.render(this.scene, this.camera); - } - - /* _updateMarkers() { - const markers = this.marker.getMarkers(); - for (let i = 0; i < markers.length; i++) { - markers[i].renderer.render(markers[i].marker, this.camera); - markers[i].renderer.domElement.style = 'position: absolute; top: 0; pointer-events: none;'; - - for (let j = 0; j < markers[i].renderer.domElement.children.length; j++) { - const child = markers[i].renderer.domElement.children[j]; - child.style = 'pointer-events: auto;'; - child.transform.baseVal[0].matrix.e -= child.firstChild.width.baseVal.value / 2; - child.transform.baseVal[0].matrix.f -= child.firstChild.height.baseVal.value / 2; - } - } - } */ - - logChildNodes(node) { - const children = node.children.filter((child) => child.inView); - if (children.length) { - const result = []; - for (const child of children) { - result.push({ - loaded: child.loaded, - geometricError: child.geometricError, - content: child.content && child.content.uri.split('/').pop(), - children: this.logChildNodes(child) - }); - } - return result; - } - } - - logTileset() { - let result = []; - result.push({ - url: this.tileset.url, - geometricEror: this.tileset.geometricError, - children: this.logChildNodes(this.tileset.root) - }); - console.log(JSON.stringify(result)); - } - } - - var BuildingShadeVert = ` - #define BUILDING - - varying float vHeight; - varying vec3 vViewPosition; - - attribute float height; - - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - void main() { - vHeight = height; - vNormal = normal; - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include - #include - #include - #include - #include - #include - - vViewPosition = - mvPosition.xyz; - - #include - #include - #include - #include - } -`; - - var BuildingShadeFrag = ` - #define BUILDING - - uniform vec3 diffuse; - uniform vec3 emissive; - uniform vec3 specular; - uniform float shininess; - uniform float opacity; - - uniform vec3 colorA; - uniform vec3 colorB; - varying float vHeight; - - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - void main() { - #include - vec3 vColor = vec3(mix(colorA, colorB, vHeight)); - vec4 diffuseColor = vec4(vColor, opacity); - ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - vec3 totalEmissiveRadiance = emissive; - - #include - #include - #include - #include - #include - #include - #include - #include - #include - - // accumulation - #include - #include - #include - #include - - // modulation - #include - vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; - #include - - gl_FragColor = vec4(outgoingLight, diffuseColor.a ); - #include - #include - #include - #include - #include - } -`; - - class BuildingShadeMaterial extends ShaderMaterial { - constructor(colorA = 0x332400, colorB = 0x506a80, emissive = 0x000000, specular = 0x2d2c2c, shininess = 1) { - super(); - this.uniforms = this._getUniforms(colorA, colorB, emissive, specular, shininess); - this.defines = {}; - this.lights = true; - this.fog = true; - this.vertexShader = BuildingShadeVert; - this.fragmentShader = BuildingShadeFrag; - } - - _getUniforms(colorA, colorB, emissive, specular, shininess) { - return { - colorA: { type: 'vec3', value: new Color(colorA) }, - colorB: { type: 'vec3', value: new Color(colorB) }, - diffuse: { value: new Color(0xffffff) }, - opacity: { value: 1.0 }, - emissive: { value: new Color(emissive) }, - specular: { value: new Color(specular) }, - shininess: { value: shininess }, - map: { value: null }, - uvTransform: { value: new Matrix3() }, - uv2Transform: { value: new Matrix3() }, - alphaMap: { value: null }, - specularMap: { value: null }, - envMap: { value: null }, - flipEnvMap: { value: -1 }, - reflectivity: { value: 1.0 }, - refractionRatio: { value: 0.98 }, - maxMipLevel: { value: 0 }, - aoMap: { value: null }, - aoMapIntensity: { value: 1 }, - lightMap: { value: null }, - lightMapIntensity: { value: 1 }, - emissiveMap: { value: null }, - bumpMap: { value: null }, - bumpScale: { value: 1 }, - normalMap: { value: null }, - normalScale: { value: new Vector2(1, 1) }, - displacementMap: { value: null }, - displacementScale: { value: 1 }, - displacementBias: { value: 0 }, - fogDensity: { value: 0.00025 }, - fogNear: { value: 1 }, - fogFar: { value: 2000 }, - fogColor: { value: new Color(0xffffff) }, - ambientLightColor: { value: [] }, - lightProbe: { value: [] }, - directionalLights: { - value: [], - properties: { - direction: {}, - color: {} - } - }, - directionalLightShadows: { - value: [], - properties: { - shadowBias: {}, - shadowNormalBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } - }, - directionalShadowMap: { value: [] }, - directionalShadowMatrix: { value: [] }, - spotLights: { - value: [], - properties: { - color: {}, - position: {}, - direction: {}, - distance: {}, - coneCos: {}, - penumbraCos: {}, - decay: {} - } - }, - spotLightShadows: { - value: [], - properties: { - shadowBias: {}, - shadowNormalBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } - }, - spotShadowMap: { value: [] }, - spotShadowMatrix: { value: [] }, - pointLights: { - value: [], - properties: { - color: {}, - position: {}, - decay: {}, - distance: {} - } - }, - pointLightShadows: { - value: [], - properties: { - shadowBias: {}, - shadowNormalBias: {}, - shadowRadius: {}, - shadowMapSize: {}, - shadowCameraNear: {}, - shadowCameraFar: {} - } - }, - pointShadowMap: { value: [] }, - pointShadowMatrix: { value: [] }, - hemisphereLights: { - value: [], - properties: { - direction: {}, - skyColor: {}, - groundColor: {} - } - }, - rectAreaLights: { - value: [], - properties: { - color: {}, - position: {}, - width: {}, - height: {} - } - }, - ltc_1: { value: null }, - ltc_2: { value: null } - }; - } - } - - class BuildingShadeStyle { - constructor(id, params = {}) { - this.type = 'shader'; - this.id = id === undefined ? Math.random().toString().replace('0.', '') : id; - this.shadeOffset = params.shadeOffset ? params.shadeOffset : 0.0; - - this.settings = { - material: this._createBuildingMaterial(params), - setAttributes: (geom) => this.setAttributes(geom) - }; - } - - _createBuildingMaterial(params) { - const colorA = params.colorA ? params.colorA : 0x807e7e; - const colorB = params.colorB ? params.colorB : 0xbedcda; - const emissive = params.emissive ? params.emissive : 0x000000; - const specular = params.specular ? params.specular : 0x111111; - const shininess = params.shininess ? params.shininess : 30; - - return new BuildingShadeMaterial(colorA, colorB, emissive, specular, shininess); - } - - setAttributes(geom) { - const batchColors = {}; - const count = geom.attributes.position.count; - geom.setAttribute('height', new BufferAttribute(new Float32Array(count), 1)); - const heights = geom.attributes.height; - - // get min and max height per batch - for (let i = 0; i < count; i++) { - const batchID = geom.attributes._batchid.getX(i); - if (batchColors[batchID] === undefined) { - batchColors[batchID] = { - min: undefined, - max: undefined - }; - } - - const vPos = geom.attributes.position.getY(i); - const min = batchColors[batchID].min; - const max = batchColors[batchID].max; - - if (min === undefined || vPos < min) { - batchColors[batchID].min = vPos; - } - - if (max === undefined || vPos > max) { - batchColors[batchID].max = vPos; - } - } - - // calculate height percentage per position and set attribute - for (let i = 0; i < count; i++) { - const batchID = geom.attributes._batchid.getX(i); - const min = batchColors[batchID].min; - const max = batchColors[batchID].max; - const yPos = geom.attributes.position.getY(i); - let height = ((yPos - min) * (1 + this.shadeOffset - 0)) / (max - min) + 0; - height = height > 1.0 ? 1.0 : height; - heights.setX(i, height); - } - } - } - - exports.BuildingShadeMaterial = BuildingShadeMaterial; - exports.BuildingShadeStyle = BuildingShadeStyle; - exports.InfiniteGrid = InfiniteGrid; - exports.Mapbox3DTilesLayer = Mapbox3DTilesLayer; - - Object.defineProperty(exports, '__esModule', { value: true }); - - return exports; - -}({})); -//# sourceMappingURL=Mapbox3DTiles.js.map diff --git a/samples/bordeaux/index.html b/samples/bordeaux/index.html deleted file mode 100644 index 99993d1..0000000 --- a/samples/bordeaux/index.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - -
-
- - - - \ No newline at end of file diff --git a/samples/bordeaux/old/Mapbox3DTiles.js b/samples/bordeaux/old/Mapbox3DTiles.js deleted file mode 100644 index 3322470..0000000 --- a/samples/bordeaux/old/Mapbox3DTiles.js +++ /dev/null @@ -1,60425 +0,0 @@ -var Mapbox3DTiles = (function (exports) { - 'use strict'; - - // Polyfills - - if ( Number.EPSILON === undefined ) { - - Number.EPSILON = Math.pow( 2, - 52 ); - - } - - if ( Number.isInteger === undefined ) { - - // Missing in IE - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger - - Number.isInteger = function ( value ) { - - return typeof value === 'number' && isFinite( value ) && Math.floor( value ) === value; - - }; - - } - - // - - if ( Math.sign === undefined ) { - - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign - - Math.sign = function ( x ) { - - return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; - - }; - - } - - if ( 'name' in Function.prototype === false ) { - - // Missing in IE - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name - - Object.defineProperty( Function.prototype, 'name', { - - get: function () { - - return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[ 1 ]; - - } - - } ); - - } - - if ( Object.assign === undefined ) { - - // Missing in IE - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign - - Object.assign = function ( target ) { - - if ( target === undefined || target === null ) { - - throw new TypeError( 'Cannot convert undefined or null to object' ); - - } - - const output = Object( target ); - - for ( let index = 1; index < arguments.length; index ++ ) { - - const source = arguments[ index ]; - - if ( source !== undefined && source !== null ) { - - for ( const nextKey in source ) { - - if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { - - output[ nextKey ] = source[ nextKey ]; - - } - - } - - } - - } - - return output; - - }; - - } - - const REVISION = '120'; - const CullFaceNone = 0; - const CullFaceBack = 1; - const CullFaceFront = 2; - const PCFShadowMap = 1; - const PCFSoftShadowMap = 2; - const VSMShadowMap = 3; - const FrontSide = 0; - const BackSide = 1; - const DoubleSide = 2; - const FlatShading = 1; - const NoBlending = 0; - const NormalBlending = 1; - const AdditiveBlending = 2; - const SubtractiveBlending = 3; - const MultiplyBlending = 4; - const CustomBlending = 5; - const AddEquation = 100; - const SubtractEquation = 101; - const ReverseSubtractEquation = 102; - const MinEquation = 103; - const MaxEquation = 104; - const ZeroFactor = 200; - const OneFactor = 201; - const SrcColorFactor = 202; - const OneMinusSrcColorFactor = 203; - const SrcAlphaFactor = 204; - const OneMinusSrcAlphaFactor = 205; - const DstAlphaFactor = 206; - const OneMinusDstAlphaFactor = 207; - const DstColorFactor = 208; - const OneMinusDstColorFactor = 209; - const SrcAlphaSaturateFactor = 210; - const NeverDepth = 0; - const AlwaysDepth = 1; - const LessDepth = 2; - const LessEqualDepth = 3; - const EqualDepth = 4; - const GreaterEqualDepth = 5; - const GreaterDepth = 6; - const NotEqualDepth = 7; - const MultiplyOperation = 0; - const MixOperation = 1; - const AddOperation = 2; - const NoToneMapping = 0; - const LinearToneMapping = 1; - const ReinhardToneMapping = 2; - const CineonToneMapping = 3; - const ACESFilmicToneMapping = 4; - const CustomToneMapping = 5; - - const UVMapping = 300; - const CubeReflectionMapping = 301; - const CubeRefractionMapping = 302; - const EquirectangularReflectionMapping = 303; - const EquirectangularRefractionMapping = 304; - const CubeUVReflectionMapping = 306; - const CubeUVRefractionMapping = 307; - const RepeatWrapping = 1000; - const ClampToEdgeWrapping = 1001; - const MirroredRepeatWrapping = 1002; - const NearestFilter = 1003; - const NearestMipmapNearestFilter = 1004; - const NearestMipmapLinearFilter = 1005; - const LinearFilter = 1006; - const LinearMipmapNearestFilter = 1007; - const LinearMipmapLinearFilter = 1008; - const UnsignedByteType = 1009; - const ByteType = 1010; - const ShortType = 1011; - const UnsignedShortType = 1012; - const IntType = 1013; - const UnsignedIntType = 1014; - const FloatType = 1015; - const HalfFloatType = 1016; - const UnsignedShort4444Type = 1017; - const UnsignedShort5551Type = 1018; - const UnsignedShort565Type = 1019; - const UnsignedInt248Type = 1020; - const AlphaFormat = 1021; - const RGBFormat = 1022; - const RGBAFormat = 1023; - const LuminanceFormat = 1024; - const LuminanceAlphaFormat = 1025; - const DepthFormat = 1026; - const DepthStencilFormat = 1027; - const RedFormat = 1028; - const RedIntegerFormat = 1029; - const RGFormat = 1030; - const RGIntegerFormat = 1031; - const RGBIntegerFormat = 1032; - const RGBAIntegerFormat = 1033; - - const RGB_S3TC_DXT1_Format = 33776; - const RGBA_S3TC_DXT1_Format = 33777; - const RGBA_S3TC_DXT3_Format = 33778; - const RGBA_S3TC_DXT5_Format = 33779; - const RGB_PVRTC_4BPPV1_Format = 35840; - const RGB_PVRTC_2BPPV1_Format = 35841; - const RGBA_PVRTC_4BPPV1_Format = 35842; - const RGBA_PVRTC_2BPPV1_Format = 35843; - const RGB_ETC1_Format = 36196; - const RGB_ETC2_Format = 37492; - const RGBA_ETC2_EAC_Format = 37496; - const RGBA_ASTC_4x4_Format = 37808; - const RGBA_ASTC_5x4_Format = 37809; - const RGBA_ASTC_5x5_Format = 37810; - const RGBA_ASTC_6x5_Format = 37811; - const RGBA_ASTC_6x6_Format = 37812; - const RGBA_ASTC_8x5_Format = 37813; - const RGBA_ASTC_8x6_Format = 37814; - const RGBA_ASTC_8x8_Format = 37815; - const RGBA_ASTC_10x5_Format = 37816; - const RGBA_ASTC_10x6_Format = 37817; - const RGBA_ASTC_10x8_Format = 37818; - const RGBA_ASTC_10x10_Format = 37819; - const RGBA_ASTC_12x10_Format = 37820; - const RGBA_ASTC_12x12_Format = 37821; - const RGBA_BPTC_Format = 36492; - const SRGB8_ALPHA8_ASTC_4x4_Format = 37840; - const SRGB8_ALPHA8_ASTC_5x4_Format = 37841; - const SRGB8_ALPHA8_ASTC_5x5_Format = 37842; - const SRGB8_ALPHA8_ASTC_6x5_Format = 37843; - const SRGB8_ALPHA8_ASTC_6x6_Format = 37844; - const SRGB8_ALPHA8_ASTC_8x5_Format = 37845; - const SRGB8_ALPHA8_ASTC_8x6_Format = 37846; - const SRGB8_ALPHA8_ASTC_8x8_Format = 37847; - const SRGB8_ALPHA8_ASTC_10x5_Format = 37848; - const SRGB8_ALPHA8_ASTC_10x6_Format = 37849; - const SRGB8_ALPHA8_ASTC_10x8_Format = 37850; - const SRGB8_ALPHA8_ASTC_10x10_Format = 37851; - const SRGB8_ALPHA8_ASTC_12x10_Format = 37852; - const SRGB8_ALPHA8_ASTC_12x12_Format = 37853; - const LoopOnce = 2200; - const LoopRepeat = 2201; - const LoopPingPong = 2202; - const InterpolateDiscrete = 2300; - const InterpolateLinear = 2301; - const InterpolateSmooth = 2302; - const ZeroCurvatureEnding = 2400; - const ZeroSlopeEnding = 2401; - const WrapAroundEnding = 2402; - const NormalAnimationBlendMode = 2500; - const AdditiveAnimationBlendMode = 2501; - const TrianglesDrawMode = 0; - const TriangleStripDrawMode = 1; - const TriangleFanDrawMode = 2; - const LinearEncoding = 3000; - const sRGBEncoding = 3001; - const GammaEncoding = 3007; - const RGBEEncoding = 3002; - const LogLuvEncoding = 3003; - const RGBM7Encoding = 3004; - const RGBM16Encoding = 3005; - const RGBDEncoding = 3006; - const BasicDepthPacking = 3200; - const RGBADepthPacking = 3201; - const TangentSpaceNormalMap = 0; - const ObjectSpaceNormalMap = 1; - const KeepStencilOp = 7680; - const AlwaysStencilFunc = 519; - - const StaticDrawUsage = 35044; - const DynamicDrawUsage = 35048; - const GLSL3 = "300 es"; - - /** - * https://github.com/mrdoob/eventdispatcher.js/ - */ - - function EventDispatcher() {} - - Object.assign( EventDispatcher.prototype, { - - addEventListener: function ( type, listener ) { - - if ( this._listeners === undefined ) this._listeners = {}; - - const listeners = this._listeners; - - if ( listeners[ type ] === undefined ) { - - listeners[ type ] = []; - - } - - if ( listeners[ type ].indexOf( listener ) === - 1 ) { - - listeners[ type ].push( listener ); - - } - - }, - - hasEventListener: function ( type, listener ) { - - if ( this._listeners === undefined ) return false; - - const listeners = this._listeners; - - return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; - - }, - - removeEventListener: function ( type, listener ) { - - if ( this._listeners === undefined ) return; - - const listeners = this._listeners; - const listenerArray = listeners[ type ]; - - if ( listenerArray !== undefined ) { - - const index = listenerArray.indexOf( listener ); - - if ( index !== - 1 ) { - - listenerArray.splice( index, 1 ); - - } - - } - - }, - - dispatchEvent: function ( event ) { - - if ( this._listeners === undefined ) return; - - const listeners = this._listeners; - const listenerArray = listeners[ event.type ]; - - if ( listenerArray !== undefined ) { - - event.target = this; - - // Make a copy, in case listeners are removed while iterating. - const array = listenerArray.slice( 0 ); - - for ( let i = 0, l = array.length; i < l; i ++ ) { - - array[ i ].call( this, event ); - - } - - } - - } - - } ); - - const _lut = []; - - for ( let i = 0; i < 256; i ++ ) { - - _lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 ); - - } - - let _seed = 1234567; - - const MathUtils = { - - DEG2RAD: Math.PI / 180, - RAD2DEG: 180 / Math.PI, - - generateUUID: function () { - - // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 - - const d0 = Math.random() * 0xffffffff | 0; - const d1 = Math.random() * 0xffffffff | 0; - const d2 = Math.random() * 0xffffffff | 0; - const d3 = Math.random() * 0xffffffff | 0; - const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' + - _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' + - _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] + - _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ]; - - // .toUpperCase() here flattens concatenated strings to save heap memory space. - return uuid.toUpperCase(); - - }, - - clamp: function ( value, min, max ) { - - return Math.max( min, Math.min( max, value ) ); - - }, - - // compute euclidian modulo of m % n - // https://en.wikipedia.org/wiki/Modulo_operation - - euclideanModulo: function ( n, m ) { - - return ( ( n % m ) + m ) % m; - - }, - - // Linear mapping from range to range - - mapLinear: function ( x, a1, a2, b1, b2 ) { - - return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); - - }, - - // https://en.wikipedia.org/wiki/Linear_interpolation - - lerp: function ( x, y, t ) { - - return ( 1 - t ) * x + t * y; - - }, - - // http://en.wikipedia.org/wiki/Smoothstep - - smoothstep: function ( x, min, max ) { - - if ( x <= min ) return 0; - if ( x >= max ) return 1; - - x = ( x - min ) / ( max - min ); - - return x * x * ( 3 - 2 * x ); - - }, - - smootherstep: function ( x, min, max ) { - - if ( x <= min ) return 0; - if ( x >= max ) return 1; - - x = ( x - min ) / ( max - min ); - - return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); - - }, - - // Random integer from interval - - randInt: function ( low, high ) { - - return low + Math.floor( Math.random() * ( high - low + 1 ) ); - - }, - - // Random float from interval - - randFloat: function ( low, high ) { - - return low + Math.random() * ( high - low ); - - }, - - // Random float from <-range/2, range/2> interval - - randFloatSpread: function ( range ) { - - return range * ( 0.5 - Math.random() ); - - }, - - // Deterministic pseudo-random float in the interval [ 0, 1 ] - - seededRandom: function ( s ) { - - if ( s !== undefined ) _seed = s % 2147483647; - - // Park-Miller algorithm - - _seed = _seed * 16807 % 2147483647; - - return ( _seed - 1 ) / 2147483646; - - }, - - degToRad: function ( degrees ) { - - return degrees * MathUtils.DEG2RAD; - - }, - - radToDeg: function ( radians ) { - - return radians * MathUtils.RAD2DEG; - - }, - - isPowerOfTwo: function ( value ) { - - return ( value & ( value - 1 ) ) === 0 && value !== 0; - - }, - - ceilPowerOfTwo: function ( value ) { - - return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); - - }, - - floorPowerOfTwo: function ( value ) { - - return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); - - }, - - setQuaternionFromProperEuler: function ( q, a, b, c, order ) { - - // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles - - // rotations are applied to the axes in the order specified by 'order' - // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c' - // angles are in radians - - const cos = Math.cos; - const sin = Math.sin; - - const c2 = cos( b / 2 ); - const s2 = sin( b / 2 ); - - const c13 = cos( ( a + c ) / 2 ); - const s13 = sin( ( a + c ) / 2 ); - - const c1_3 = cos( ( a - c ) / 2 ); - const s1_3 = sin( ( a - c ) / 2 ); - - const c3_1 = cos( ( c - a ) / 2 ); - const s3_1 = sin( ( c - a ) / 2 ); - - switch ( order ) { - - case 'XYX': - q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 ); - break; - - case 'YZY': - q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 ); - break; - - case 'ZXZ': - q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 ); - break; - - case 'XZX': - q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 ); - break; - - case 'YXY': - q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 ); - break; - - case 'ZYZ': - q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 ); - break; - - default: - console.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order ); - - } - - } - - }; - - class Vector2 { - - constructor( x = 0, y = 0 ) { - - Object.defineProperty( this, 'isVector2', { value: true } ); - - this.x = x; - this.y = y; - - } - - get width() { - - return this.x; - - } - - set width( value ) { - - this.x = value; - - } - - get height() { - - return this.y; - - } - - set height( value ) { - - this.y = value; - - } - - set( x, y ) { - - this.x = x; - this.y = y; - - return this; - - } - - setScalar( scalar ) { - - this.x = scalar; - this.y = scalar; - - return this; - - } - - setX( x ) { - - this.x = x; - - return this; - - } - - setY( y ) { - - this.y = y; - - return this; - - } - - setComponent( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - } - - getComponent( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - default: throw new Error( 'index is out of range: ' + index ); - - } - - } - - clone() { - - return new this.constructor( this.x, this.y ); - - } - - copy( v ) { - - this.x = v.x; - this.y = v.y; - - return this; - - } - - add( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - - return this; - - } - - addScalar( s ) { - - this.x += s; - this.y += s; - - return this; - - } - - addVectors( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - - return this; - - } - - addScaledVector( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - - return this; - - } - - sub( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - - return this; - - } - - subScalar( s ) { - - this.x -= s; - this.y -= s; - - return this; - - } - - subVectors( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - - return this; - - } - - multiply( v ) { - - this.x *= v.x; - this.y *= v.y; - - return this; - - } - - multiplyScalar( scalar ) { - - this.x *= scalar; - this.y *= scalar; - - return this; - - } - - divide( v ) { - - this.x /= v.x; - this.y /= v.y; - - return this; - - } - - divideScalar( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - } - - applyMatrix3( m ) { - - const x = this.x, y = this.y; - const e = m.elements; - - this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; - this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; - - return this; - - } - - min( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - - return this; - - } - - max( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - - return this; - - } - - clamp( min, max ) { - - // assumes min < max, componentwise - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - - return this; - - } - - clampScalar( minVal, maxVal ) { - - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - - return this; - - } - - clampLength( min, max ) { - - const length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - } - - floor() { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - - return this; - - } - - ceil() { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - - return this; - - } - - round() { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - - return this; - - } - - roundToZero() { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - - return this; - - } - - negate() { - - this.x = - this.x; - this.y = - this.y; - - return this; - - } - - dot( v ) { - - return this.x * v.x + this.y * v.y; - - } - - cross( v ) { - - return this.x * v.y - this.y * v.x; - - } - - lengthSq() { - - return this.x * this.x + this.y * this.y; - - } - - length() { - - return Math.sqrt( this.x * this.x + this.y * this.y ); - - } - - manhattanLength() { - - return Math.abs( this.x ) + Math.abs( this.y ); - - } - - normalize() { - - return this.divideScalar( this.length() || 1 ); - - } - - angle() { - - // computes the angle in radians with respect to the positive x-axis - - const angle = Math.atan2( - this.y, - this.x ) + Math.PI; - - return angle; - - } - - distanceTo( v ) { - - return Math.sqrt( this.distanceToSquared( v ) ); - - } - - distanceToSquared( v ) { - - const dx = this.x - v.x, dy = this.y - v.y; - return dx * dx + dy * dy; - - } - - manhattanDistanceTo( v ) { - - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); - - } - - setLength( length ) { - - return this.normalize().multiplyScalar( length ); - - } - - lerp( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - - return this; - - } - - lerpVectors( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - - return this; - - } - - equals( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) ); - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - - return array; - - } - - fromBufferAttribute( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - - return this; - - } - - rotateAround( center, angle ) { - - const c = Math.cos( angle ), s = Math.sin( angle ); - - const x = this.x - center.x; - const y = this.y - center.y; - - this.x = x * c - y * s + center.x; - this.y = x * s + y * c + center.y; - - return this; - - } - - random() { - - this.x = Math.random(); - this.y = Math.random(); - - return this; - - } - - } - - class Matrix3 { - - constructor() { - - Object.defineProperty( this, 'isMatrix3', { value: true } ); - - this.elements = [ - - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 - - ]; - - if ( arguments.length > 0 ) { - - console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); - - } - - } - - set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { - - const te = this.elements; - - te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; - te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; - te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; - - return this; - - } - - identity() { - - this.set( - - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 - - ); - - return this; - - } - - clone() { - - return new this.constructor().fromArray( this.elements ); - - } - - copy( m ) { - - const te = this.elements; - const me = m.elements; - - te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; - te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; - te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; - - return this; - - } - - extractBasis( xAxis, yAxis, zAxis ) { - - xAxis.setFromMatrix3Column( this, 0 ); - yAxis.setFromMatrix3Column( this, 1 ); - zAxis.setFromMatrix3Column( this, 2 ); - - return this; - - } - - setFromMatrix4( m ) { - - const me = m.elements; - - this.set( - - me[ 0 ], me[ 4 ], me[ 8 ], - me[ 1 ], me[ 5 ], me[ 9 ], - me[ 2 ], me[ 6 ], me[ 10 ] - - ); - - return this; - - } - - multiply( m ) { - - return this.multiplyMatrices( this, m ); - - } - - premultiply( m ) { - - return this.multiplyMatrices( m, this ); - - } - - multiplyMatrices( a, b ) { - - const ae = a.elements; - const be = b.elements; - const te = this.elements; - - const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; - const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; - const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; - - const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; - const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; - const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; - - te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; - te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; - te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; - - te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; - te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; - te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; - - te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; - te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; - te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; - - return this; - - } - - multiplyScalar( s ) { - - const te = this.elements; - - te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; - te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; - te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; - - return this; - - } - - determinant() { - - const te = this.elements; - - const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], - d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], - g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; - - return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; - - } - - getInverse( matrix, throwOnDegenerate ) { - - if ( throwOnDegenerate !== undefined ) { - - console.warn( "THREE.Matrix3: .getInverse() can no longer be configured to throw on degenerate." ); - - } - - const me = matrix.elements, - te = this.elements, - - n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], - n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ], - n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ], - - t11 = n33 * n22 - n32 * n23, - t12 = n32 * n13 - n33 * n12, - t13 = n23 * n12 - n22 * n13, - - det = n11 * t11 + n21 * t12 + n31 * t13; - - if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 ); - - const detInv = 1 / det; - - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; - te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; - - te[ 3 ] = t12 * detInv; - te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; - te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; - - te[ 6 ] = t13 * detInv; - te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; - te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; - - return this; - - } - - transpose() { - - let tmp; - const m = this.elements; - - tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; - tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; - tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; - - return this; - - } - - getNormalMatrix( matrix4 ) { - - return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose(); - - } - - transposeIntoArray( r ) { - - const m = this.elements; - - r[ 0 ] = m[ 0 ]; - r[ 1 ] = m[ 3 ]; - r[ 2 ] = m[ 6 ]; - r[ 3 ] = m[ 1 ]; - r[ 4 ] = m[ 4 ]; - r[ 5 ] = m[ 7 ]; - r[ 6 ] = m[ 2 ]; - r[ 7 ] = m[ 5 ]; - r[ 8 ] = m[ 8 ]; - - return this; - - } - - setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) { - - const c = Math.cos( rotation ); - const s = Math.sin( rotation ); - - this.set( - sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, - - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, - 0, 0, 1 - ); - - } - - scale( sx, sy ) { - - const te = this.elements; - - te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx; - te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy; - - return this; - - } - - rotate( theta ) { - - const c = Math.cos( theta ); - const s = Math.sin( theta ); - - const te = this.elements; - - const a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ]; - const a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ]; - - te[ 0 ] = c * a11 + s * a21; - te[ 3 ] = c * a12 + s * a22; - te[ 6 ] = c * a13 + s * a23; - - te[ 1 ] = - s * a11 + c * a21; - te[ 4 ] = - s * a12 + c * a22; - te[ 7 ] = - s * a13 + c * a23; - - return this; - - } - - translate( tx, ty ) { - - const te = this.elements; - - te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ]; - te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ]; - - return this; - - } - - equals( matrix ) { - - const te = this.elements; - const me = matrix.elements; - - for ( let i = 0; i < 9; i ++ ) { - - if ( te[ i ] !== me[ i ] ) return false; - - } - - return true; - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - for ( let i = 0; i < 9; i ++ ) { - - this.elements[ i ] = array[ i + offset ]; - - } - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - const te = this.elements; - - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - - array[ offset + 3 ] = te[ 3 ]; - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; - array[ offset + 8 ] = te[ 8 ]; - - return array; - - } - - } - - let _canvas; - - const ImageUtils = { - - getDataURL: function ( image ) { - - if ( /^data:/i.test( image.src ) ) { - - return image.src; - - } - - if ( typeof HTMLCanvasElement == 'undefined' ) { - - return image.src; - - } - - let canvas; - - if ( image instanceof HTMLCanvasElement ) { - - canvas = image; - - } else { - - if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - - _canvas.width = image.width; - _canvas.height = image.height; - - const context = _canvas.getContext( '2d' ); - - if ( image instanceof ImageData ) { - - context.putImageData( image, 0, 0 ); - - } else { - - context.drawImage( image, 0, 0, image.width, image.height ); - - } - - canvas = _canvas; - - } - - if ( canvas.width > 2048 || canvas.height > 2048 ) { - - return canvas.toDataURL( 'image/jpeg', 0.6 ); - - } else { - - return canvas.toDataURL( 'image/png' ); - - } - - } - - }; - - let textureId = 0; - - function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - - Object.defineProperty( this, 'id', { value: textureId ++ } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - - this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE; - this.mipmaps = []; - - this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING; - - this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping; - this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping; - - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - this.minFilter = minFilter !== undefined ? minFilter : LinearMipmapLinearFilter; - - this.anisotropy = anisotropy !== undefined ? anisotropy : 1; - - this.format = format !== undefined ? format : RGBAFormat; - this.internalFormat = null; - this.type = type !== undefined ? type : UnsignedByteType; - - this.offset = new Vector2( 0, 0 ); - this.repeat = new Vector2( 1, 1 ); - this.center = new Vector2( 0, 0 ); - this.rotation = 0; - - this.matrixAutoUpdate = true; - this.matrix = new Matrix3(); - - this.generateMipmaps = true; - this.premultiplyAlpha = false; - this.flipY = true; - this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - - // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. - // - // Also changing the encoding after already used by a Material will not automatically make the Material - // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. - this.encoding = encoding !== undefined ? encoding : LinearEncoding; - - this.version = 0; - this.onUpdate = null; - - } - - Texture.DEFAULT_IMAGE = undefined; - Texture.DEFAULT_MAPPING = UVMapping; - - Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: Texture, - - isTexture: true, - - updateMatrix: function () { - - this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.name = source.name; - - this.image = source.image; - this.mipmaps = source.mipmaps.slice( 0 ); - - this.mapping = source.mapping; - - this.wrapS = source.wrapS; - this.wrapT = source.wrapT; - - this.magFilter = source.magFilter; - this.minFilter = source.minFilter; - - this.anisotropy = source.anisotropy; - - this.format = source.format; - this.internalFormat = source.internalFormat; - this.type = source.type; - - this.offset.copy( source.offset ); - this.repeat.copy( source.repeat ); - this.center.copy( source.center ); - this.rotation = source.rotation; - - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrix.copy( source.matrix ); - - this.generateMipmaps = source.generateMipmaps; - this.premultiplyAlpha = source.premultiplyAlpha; - this.flipY = source.flipY; - this.unpackAlignment = source.unpackAlignment; - this.encoding = source.encoding; - - return this; - - }, - - toJSON: function ( meta ) { - - const isRootObject = ( meta === undefined || typeof meta === 'string' ); - - if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { - - return meta.textures[ this.uuid ]; - - } - - const output = { - - metadata: { - version: 4.5, - type: 'Texture', - generator: 'Texture.toJSON' - }, - - uuid: this.uuid, - name: this.name, - - mapping: this.mapping, - - repeat: [ this.repeat.x, this.repeat.y ], - offset: [ this.offset.x, this.offset.y ], - center: [ this.center.x, this.center.y ], - rotation: this.rotation, - - wrap: [ this.wrapS, this.wrapT ], - - format: this.format, - type: this.type, - encoding: this.encoding, - - minFilter: this.minFilter, - magFilter: this.magFilter, - anisotropy: this.anisotropy, - - flipY: this.flipY, - - premultiplyAlpha: this.premultiplyAlpha, - unpackAlignment: this.unpackAlignment - - }; - - if ( this.image !== undefined ) { - - // TODO: Move to THREE.Image - - const image = this.image; - - if ( image.uuid === undefined ) { - - image.uuid = MathUtils.generateUUID(); // UGH - - } - - if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { - - let url; - - if ( Array.isArray( image ) ) { - - // process array of images e.g. CubeTexture - - url = []; - - for ( let i = 0, l = image.length; i < l; i ++ ) { - - url.push( ImageUtils.getDataURL( image[ i ] ) ); - - } - - } else { - - // process single image - - url = ImageUtils.getDataURL( image ); - - } - - meta.images[ image.uuid ] = { - uuid: image.uuid, - url: url - }; - - } - - output.image = image.uuid; - - } - - if ( ! isRootObject ) { - - meta.textures[ this.uuid ] = output; - - } - - return output; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - }, - - transformUv: function ( uv ) { - - if ( this.mapping !== UVMapping ) return uv; - - uv.applyMatrix3( this.matrix ); - - if ( uv.x < 0 || uv.x > 1 ) { - - switch ( this.wrapS ) { - - case RepeatWrapping: - - uv.x = uv.x - Math.floor( uv.x ); - break; - - case ClampToEdgeWrapping: - - uv.x = uv.x < 0 ? 0 : 1; - break; - - case MirroredRepeatWrapping: - - if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { - - uv.x = Math.ceil( uv.x ) - uv.x; - - } else { - - uv.x = uv.x - Math.floor( uv.x ); - - } - - break; - - } - - } - - if ( uv.y < 0 || uv.y > 1 ) { - - switch ( this.wrapT ) { - - case RepeatWrapping: - - uv.y = uv.y - Math.floor( uv.y ); - break; - - case ClampToEdgeWrapping: - - uv.y = uv.y < 0 ? 0 : 1; - break; - - case MirroredRepeatWrapping: - - if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { - - uv.y = Math.ceil( uv.y ) - uv.y; - - } else { - - uv.y = uv.y - Math.floor( uv.y ); - - } - - break; - - } - - } - - if ( this.flipY ) { - - uv.y = 1 - uv.y; - - } - - return uv; - - } - - } ); - - Object.defineProperty( Texture.prototype, "needsUpdate", { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - class Vector4 { - - constructor( x = 0, y = 0, z = 0, w = 1 ) { - - Object.defineProperty( this, 'isVector4', { value: true } ); - - this.x = x; - this.y = y; - this.z = z; - this.w = w; - - } - - get width() { - - return this.z; - - } - - set width( value ) { - - this.z = value; - - } - - get height() { - - return this.w; - - } - - set height( value ) { - - this.w = value; - - } - - set( x, y, z, w ) { - - this.x = x; - this.y = y; - this.z = z; - this.w = w; - - return this; - - } - - setScalar( scalar ) { - - this.x = scalar; - this.y = scalar; - this.z = scalar; - this.w = scalar; - - return this; - - } - - setX( x ) { - - this.x = x; - - return this; - - } - - setY( y ) { - - this.y = y; - - return this; - - } - - setZ( z ) { - - this.z = z; - - return this; - - } - - setW( w ) { - - this.w = w; - - return this; - - } - - setComponent( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - case 3: this.w = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - } - - getComponent( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - case 3: return this.w; - default: throw new Error( 'index is out of range: ' + index ); - - } - - } - - clone() { - - return new this.constructor( this.x, this.y, this.z, this.w ); - - } - - copy( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.w = ( v.w !== undefined ) ? v.w : 1; - - return this; - - } - - add( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - this.z += v.z; - this.w += v.w; - - return this; - - } - - addScalar( s ) { - - this.x += s; - this.y += s; - this.z += s; - this.w += s; - - return this; - - } - - addVectors( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - this.w = a.w + b.w; - - return this; - - } - - addScaledVector( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - this.w += v.w * s; - - return this; - - } - - sub( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - this.w -= v.w; - - return this; - - } - - subScalar( s ) { - - this.x -= s; - this.y -= s; - this.z -= s; - this.w -= s; - - return this; - - } - - subVectors( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - this.w = a.w - b.w; - - return this; - - } - - multiplyScalar( scalar ) { - - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - this.w *= scalar; - - return this; - - } - - applyMatrix4( m ) { - - const x = this.x, y = this.y, z = this.z, w = this.w; - const e = m.elements; - - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; - this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; - - return this; - - } - - divideScalar( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - } - - setAxisAngleFromQuaternion( q ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm - - // q is assumed to be normalized - - this.w = 2 * Math.acos( q.w ); - - const s = Math.sqrt( 1 - q.w * q.w ); - - if ( s < 0.0001 ) { - - this.x = 1; - this.y = 0; - this.z = 0; - - } else { - - this.x = q.x / s; - this.y = q.y / s; - this.z = q.z / s; - - } - - return this; - - } - - setAxisAngleFromRotationMatrix( m ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - let angle, x, y, z; // variables for result - const epsilon = 0.01, // margin to allow for rounding errors - epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees - - te = m.elements, - - m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], - m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], - m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - - if ( ( Math.abs( m12 - m21 ) < epsilon ) && - ( Math.abs( m13 - m31 ) < epsilon ) && - ( Math.abs( m23 - m32 ) < epsilon ) ) { - - // singularity found - // first check for identity matrix which must have +1 for all terms - // in leading diagonal and zero in other terms - - if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && - ( Math.abs( m13 + m31 ) < epsilon2 ) && - ( Math.abs( m23 + m32 ) < epsilon2 ) && - ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { - - // this singularity is identity matrix so angle = 0 - - this.set( 1, 0, 0, 0 ); - - return this; // zero angle, arbitrary axis - - } - - // otherwise this singularity is angle = 180 - - angle = Math.PI; - - const xx = ( m11 + 1 ) / 2; - const yy = ( m22 + 1 ) / 2; - const zz = ( m33 + 1 ) / 2; - const xy = ( m12 + m21 ) / 4; - const xz = ( m13 + m31 ) / 4; - const yz = ( m23 + m32 ) / 4; - - if ( ( xx > yy ) && ( xx > zz ) ) { - - // m11 is the largest diagonal term - - if ( xx < epsilon ) { - - x = 0; - y = 0.707106781; - z = 0.707106781; - - } else { - - x = Math.sqrt( xx ); - y = xy / x; - z = xz / x; - - } - - } else if ( yy > zz ) { - - // m22 is the largest diagonal term - - if ( yy < epsilon ) { - - x = 0.707106781; - y = 0; - z = 0.707106781; - - } else { - - y = Math.sqrt( yy ); - x = xy / y; - z = yz / y; - - } - - } else { - - // m33 is the largest diagonal term so base result on this - - if ( zz < epsilon ) { - - x = 0.707106781; - y = 0.707106781; - z = 0; - - } else { - - z = Math.sqrt( zz ); - x = xz / z; - y = yz / z; - - } - - } - - this.set( x, y, z, angle ); - - return this; // return 180 deg rotation - - } - - // as we have reached here there are no singularities so we can handle normally - - let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + - ( m13 - m31 ) * ( m13 - m31 ) + - ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize - - if ( Math.abs( s ) < 0.001 ) s = 1; - - // prevent divide by zero, should not happen if matrix is orthogonal and should be - // caught by singularity test above, but I've left it in just in case - - this.x = ( m32 - m23 ) / s; - this.y = ( m13 - m31 ) / s; - this.z = ( m21 - m12 ) / s; - this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); - - return this; - - } - - min( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - this.w = Math.min( this.w, v.w ); - - return this; - - } - - max( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - this.w = Math.max( this.w, v.w ); - - return this; - - } - - clamp( min, max ) { - - // assumes min < max, componentwise - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - this.z = Math.max( min.z, Math.min( max.z, this.z ) ); - this.w = Math.max( min.w, Math.min( max.w, this.w ) ); - - return this; - - } - - clampScalar( minVal, maxVal ) { - - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); - - return this; - - } - - clampLength( min, max ) { - - const length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - } - - floor() { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - this.w = Math.floor( this.w ); - - return this; - - } - - ceil() { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - this.w = Math.ceil( this.w ); - - return this; - - } - - round() { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - this.w = Math.round( this.w ); - - return this; - - } - - roundToZero() { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); - this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); - - return this; - - } - - negate() { - - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - this.w = - this.w; - - return this; - - } - - dot( v ) { - - return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; - - } - - lengthSq() { - - return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; - - } - - length() { - - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); - - } - - manhattanLength() { - - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); - - } - - normalize() { - - return this.divideScalar( this.length() || 1 ); - - } - - setLength( length ) { - - return this.normalize().multiplyScalar( length ); - - } - - lerp( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - this.w += ( v.w - this.w ) * alpha; - - return this; - - } - - lerpVectors( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - this.z = v1.z + ( v2.z - v1.z ) * alpha; - this.w = v1.w + ( v2.w - v1.w ) * alpha; - - return this; - - } - - equals( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - this.w = array[ offset + 3 ]; - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - array[ offset + 3 ] = this.w; - - return array; - - } - - fromBufferAttribute( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - this.w = attribute.getW( index ); - - return this; - - } - - random() { - - this.x = Math.random(); - this.y = Math.random(); - this.z = Math.random(); - this.w = Math.random(); - - return this; - - } - - } - - /* - In options, we can specify: - * Texture parameters for an auto-generated target texture - * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers - */ - function WebGLRenderTarget( width, height, options ) { - - this.width = width; - this.height = height; - - this.scissor = new Vector4( 0, 0, width, height ); - this.scissorTest = false; - - this.viewport = new Vector4( 0, 0, width, height ); - - options = options || {}; - - this.texture = new Texture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); - - this.texture.image = {}; - this.texture.image.width = width; - this.texture.image.height = height; - - this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; - this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; - - this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; - this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; - this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; - - } - - WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: WebGLRenderTarget, - - isWebGLRenderTarget: true, - - setSize: function ( width, height ) { - - if ( this.width !== width || this.height !== height ) { - - this.width = width; - this.height = height; - - this.texture.image.width = width; - this.texture.image.height = height; - - this.dispose(); - - } - - this.viewport.set( 0, 0, width, height ); - this.scissor.set( 0, 0, width, height ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.width = source.width; - this.height = source.height; - - this.viewport.copy( source.viewport ); - - this.texture = source.texture.clone(); - - this.depthBuffer = source.depthBuffer; - this.stencilBuffer = source.stencilBuffer; - this.depthTexture = source.depthTexture; - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - function WebGLMultisampleRenderTarget( width, height, options ) { - - WebGLRenderTarget.call( this, width, height, options ); - - this.samples = 4; - - } - - WebGLMultisampleRenderTarget.prototype = Object.assign( Object.create( WebGLRenderTarget.prototype ), { - - constructor: WebGLMultisampleRenderTarget, - - isWebGLMultisampleRenderTarget: true, - - copy: function ( source ) { - - WebGLRenderTarget.prototype.copy.call( this, source ); - - this.samples = source.samples; - - return this; - - } - - } ); - - class Quaternion { - - constructor( x = 0, y = 0, z = 0, w = 1 ) { - - Object.defineProperty( this, 'isQuaternion', { value: true } ); - - this._x = x; - this._y = y; - this._z = z; - this._w = w; - - } - - static slerp( qa, qb, qm, t ) { - - return qm.copy( qa ).slerp( qb, t ); - - } - - static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { - - // fuzz-free, array-based Quaternion SLERP operation - - let x0 = src0[ srcOffset0 + 0 ], - y0 = src0[ srcOffset0 + 1 ], - z0 = src0[ srcOffset0 + 2 ], - w0 = src0[ srcOffset0 + 3 ]; - - const x1 = src1[ srcOffset1 + 0 ], - y1 = src1[ srcOffset1 + 1 ], - z1 = src1[ srcOffset1 + 2 ], - w1 = src1[ srcOffset1 + 3 ]; - - if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { - - let s = 1 - t; - const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, - dir = ( cos >= 0 ? 1 : - 1 ), - sqrSin = 1 - cos * cos; - - // Skip the Slerp for tiny steps to avoid numeric problems: - if ( sqrSin > Number.EPSILON ) { - - const sin = Math.sqrt( sqrSin ), - len = Math.atan2( sin, cos * dir ); - - s = Math.sin( s * len ) / sin; - t = Math.sin( t * len ) / sin; - - } - - const tDir = t * dir; - - x0 = x0 * s + x1 * tDir; - y0 = y0 * s + y1 * tDir; - z0 = z0 * s + z1 * tDir; - w0 = w0 * s + w1 * tDir; - - // Normalize in case we just did a lerp: - if ( s === 1 - t ) { - - const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); - - x0 *= f; - y0 *= f; - z0 *= f; - w0 *= f; - - } - - } - - dst[ dstOffset ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; - - } - - static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { - - const x0 = src0[ srcOffset0 ]; - const y0 = src0[ srcOffset0 + 1 ]; - const z0 = src0[ srcOffset0 + 2 ]; - const w0 = src0[ srcOffset0 + 3 ]; - - const x1 = src1[ srcOffset1 ]; - const y1 = src1[ srcOffset1 + 1 ]; - const z1 = src1[ srcOffset1 + 2 ]; - const w1 = src1[ srcOffset1 + 3 ]; - - dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; - dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; - dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; - dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; - - return dst; - - } - - get x() { - - return this._x; - - } - - set x( value ) { - - this._x = value; - this._onChangeCallback(); - - } - - get y() { - - return this._y; - - } - - set y( value ) { - - this._y = value; - this._onChangeCallback(); - - } - - get z() { - - return this._z; - - } - - set z( value ) { - - this._z = value; - this._onChangeCallback(); - - } - - get w() { - - return this._w; - - } - - set w( value ) { - - this._w = value; - this._onChangeCallback(); - - } - - set( x, y, z, w ) { - - this._x = x; - this._y = y; - this._z = z; - this._w = w; - - this._onChangeCallback(); - - return this; - - } - - clone() { - - return new this.constructor( this._x, this._y, this._z, this._w ); - - } - - copy( quaternion ) { - - this._x = quaternion.x; - this._y = quaternion.y; - this._z = quaternion.z; - this._w = quaternion.w; - - this._onChangeCallback(); - - return this; - - } - - setFromEuler( euler, update ) { - - if ( ! ( euler && euler.isEuler ) ) { - - throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - - } - - const x = euler._x, y = euler._y, z = euler._z, order = euler._order; - - // http://www.mathworks.com/matlabcentral/fileexchange/ - // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ - // content/SpinCalc.m - - const cos = Math.cos; - const sin = Math.sin; - - const c1 = cos( x / 2 ); - const c2 = cos( y / 2 ); - const c3 = cos( z / 2 ); - - const s1 = sin( x / 2 ); - const s2 = sin( y / 2 ); - const s3 = sin( z / 2 ); - - switch ( order ) { - - case 'XYZ': - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - break; - - case 'YXZ': - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - break; - - case 'ZXY': - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - break; - - case 'ZYX': - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - break; - - case 'YZX': - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - break; - - case 'XZY': - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - break; - - default: - console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order ); - - } - - if ( update !== false ) this._onChangeCallback(); - - return this; - - } - - setFromAxisAngle( axis, angle ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - - // assumes axis is normalized - - const halfAngle = angle / 2, s = Math.sin( halfAngle ); - - this._x = axis.x * s; - this._y = axis.y * s; - this._z = axis.z * s; - this._w = Math.cos( halfAngle ); - - this._onChangeCallback(); - - return this; - - } - - setFromRotationMatrix( m ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - const te = m.elements, - - m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], - m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], - m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], - - trace = m11 + m22 + m33; - - if ( trace > 0 ) { - - const s = 0.5 / Math.sqrt( trace + 1.0 ); - - this._w = 0.25 / s; - this._x = ( m32 - m23 ) * s; - this._y = ( m13 - m31 ) * s; - this._z = ( m21 - m12 ) * s; - - } else if ( m11 > m22 && m11 > m33 ) { - - const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); - - this._w = ( m32 - m23 ) / s; - this._x = 0.25 * s; - this._y = ( m12 + m21 ) / s; - this._z = ( m13 + m31 ) / s; - - } else if ( m22 > m33 ) { - - const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); - - this._w = ( m13 - m31 ) / s; - this._x = ( m12 + m21 ) / s; - this._y = 0.25 * s; - this._z = ( m23 + m32 ) / s; - - } else { - - const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); - - this._w = ( m21 - m12 ) / s; - this._x = ( m13 + m31 ) / s; - this._y = ( m23 + m32 ) / s; - this._z = 0.25 * s; - - } - - this._onChangeCallback(); - - return this; - - } - - setFromUnitVectors( vFrom, vTo ) { - - // assumes direction vectors vFrom and vTo are normalized - - const EPS = 0.000001; - - let r = vFrom.dot( vTo ) + 1; - - if ( r < EPS ) { - - r = 0; - - if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { - - this._x = - vFrom.y; - this._y = vFrom.x; - this._z = 0; - this._w = r; - - } else { - - this._x = 0; - this._y = - vFrom.z; - this._z = vFrom.y; - this._w = r; - - } - - } else { - - // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 - - this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; - this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; - this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; - this._w = r; - - } - - return this.normalize(); - - } - - angleTo( q ) { - - return 2 * Math.acos( Math.abs( MathUtils.clamp( this.dot( q ), - 1, 1 ) ) ); - - } - - rotateTowards( q, step ) { - - const angle = this.angleTo( q ); - - if ( angle === 0 ) return this; - - const t = Math.min( 1, step / angle ); - - this.slerp( q, t ); - - return this; - - } - - identity() { - - return this.set( 0, 0, 0, 1 ); - - } - - inverse() { - - // quaternion is assumed to have unit length - - return this.conjugate(); - - } - - conjugate() { - - this._x *= - 1; - this._y *= - 1; - this._z *= - 1; - - this._onChangeCallback(); - - return this; - - } - - dot( v ) { - - return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; - - } - - lengthSq() { - - return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; - - } - - length() { - - return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); - - } - - normalize() { - - let l = this.length(); - - if ( l === 0 ) { - - this._x = 0; - this._y = 0; - this._z = 0; - this._w = 1; - - } else { - - l = 1 / l; - - this._x = this._x * l; - this._y = this._y * l; - this._z = this._z * l; - this._w = this._w * l; - - } - - this._onChangeCallback(); - - return this; - - } - - multiply( q, p ) { - - if ( p !== undefined ) { - - console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); - return this.multiplyQuaternions( q, p ); - - } - - return this.multiplyQuaternions( this, q ); - - } - - premultiply( q ) { - - return this.multiplyQuaternions( q, this ); - - } - - multiplyQuaternions( a, b ) { - - // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm - - const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; - const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; - - this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; - this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; - this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; - this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; - - this._onChangeCallback(); - - return this; - - } - - slerp( qb, t ) { - - if ( t === 0 ) return this; - if ( t === 1 ) return this.copy( qb ); - - const x = this._x, y = this._y, z = this._z, w = this._w; - - // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ - - let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; - - if ( cosHalfTheta < 0 ) { - - this._w = - qb._w; - this._x = - qb._x; - this._y = - qb._y; - this._z = - qb._z; - - cosHalfTheta = - cosHalfTheta; - - } else { - - this.copy( qb ); - - } - - if ( cosHalfTheta >= 1.0 ) { - - this._w = w; - this._x = x; - this._y = y; - this._z = z; - - return this; - - } - - const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; - - if ( sqrSinHalfTheta <= Number.EPSILON ) { - - const s = 1 - t; - this._w = s * w + t * this._w; - this._x = s * x + t * this._x; - this._y = s * y + t * this._y; - this._z = s * z + t * this._z; - - this.normalize(); - this._onChangeCallback(); - - return this; - - } - - const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); - const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); - const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, - ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; - - this._w = ( w * ratioA + this._w * ratioB ); - this._x = ( x * ratioA + this._x * ratioB ); - this._y = ( y * ratioA + this._y * ratioB ); - this._z = ( z * ratioA + this._z * ratioB ); - - this._onChangeCallback(); - - return this; - - } - - equals( quaternion ) { - - return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this._x = array[ offset ]; - this._y = array[ offset + 1 ]; - this._z = array[ offset + 2 ]; - this._w = array[ offset + 3 ]; - - this._onChangeCallback(); - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._w; - - return array; - - } - - fromBufferAttribute( attribute, index ) { - - this._x = attribute.getX( index ); - this._y = attribute.getY( index ); - this._z = attribute.getZ( index ); - this._w = attribute.getW( index ); - - return this; - - } - - _onChange( callback ) { - - this._onChangeCallback = callback; - - return this; - - } - - _onChangeCallback() {} - - } - - class Vector3 { - - constructor( x = 0, y = 0, z = 0 ) { - - Object.defineProperty( this, 'isVector3', { value: true } ); - - this.x = x; - this.y = y; - this.z = z; - - } - - set( x, y, z ) { - - if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) - - this.x = x; - this.y = y; - this.z = z; - - return this; - - } - - setScalar( scalar ) { - - this.x = scalar; - this.y = scalar; - this.z = scalar; - - return this; - - } - - setX( x ) { - - this.x = x; - - return this; - - } - - setY( y ) { - - this.y = y; - - return this; - - } - - setZ( z ) { - - this.z = z; - - return this; - - } - - setComponent( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - } - - getComponent( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - default: throw new Error( 'index is out of range: ' + index ); - - } - - } - - clone() { - - return new this.constructor( this.x, this.y, this.z ); - - } - - copy( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; - - return this; - - } - - add( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - this.z += v.z; - - return this; - - } - - addScalar( s ) { - - this.x += s; - this.y += s; - this.z += s; - - return this; - - } - - addVectors( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - - return this; - - } - - addScaledVector( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - - return this; - - } - - sub( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - - return this; - - } - - subScalar( s ) { - - this.x -= s; - this.y -= s; - this.z -= s; - - return this; - - } - - subVectors( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - - return this; - - } - - multiply( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); - return this.multiplyVectors( v, w ); - - } - - this.x *= v.x; - this.y *= v.y; - this.z *= v.z; - - return this; - - } - - multiplyScalar( scalar ) { - - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - - return this; - - } - - multiplyVectors( a, b ) { - - this.x = a.x * b.x; - this.y = a.y * b.y; - this.z = a.z * b.z; - - return this; - - } - - applyEuler( euler ) { - - if ( ! ( euler && euler.isEuler ) ) { - - console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - - } - - return this.applyQuaternion( _quaternion.setFromEuler( euler ) ); - - } - - applyAxisAngle( axis, angle ) { - - return this.applyQuaternion( _quaternion.setFromAxisAngle( axis, angle ) ); - - } - - applyMatrix3( m ) { - - const x = this.x, y = this.y, z = this.z; - const e = m.elements; - - this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; - this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; - this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; - - return this; - - } - - applyNormalMatrix( m ) { - - return this.applyMatrix3( m ).normalize(); - - } - - applyMatrix4( m ) { - - const x = this.x, y = this.y, z = this.z; - const e = m.elements; - - const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); - - this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; - this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; - this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; - - return this; - - } - - applyQuaternion( q ) { - - const x = this.x, y = this.y, z = this.z; - const qx = q.x, qy = q.y, qz = q.z, qw = q.w; - - // calculate quat * vector - - const ix = qw * x + qy * z - qz * y; - const iy = qw * y + qz * x - qx * z; - const iz = qw * z + qx * y - qy * x; - const iw = - qx * x - qy * y - qz * z; - - // calculate result * inverse quat - - this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; - this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; - this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; - - return this; - - } - - project( camera ) { - - return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); - - } - - unproject( camera ) { - - return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); - - } - - transformDirection( m ) { - - // input: THREE.Matrix4 affine matrix - // vector interpreted as a direction - - const x = this.x, y = this.y, z = this.z; - const e = m.elements; - - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; - - return this.normalize(); - - } - - divide( v ) { - - this.x /= v.x; - this.y /= v.y; - this.z /= v.z; - - return this; - - } - - divideScalar( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - } - - min( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - - return this; - - } - - max( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - - return this; - - } - - clamp( min, max ) { - - // assumes min < max, componentwise - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - this.z = Math.max( min.z, Math.min( max.z, this.z ) ); - - return this; - - } - - clampScalar( minVal, maxVal ) { - - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - - return this; - - } - - clampLength( min, max ) { - - const length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - } - - floor() { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - - return this; - - } - - ceil() { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - - return this; - - } - - round() { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - - return this; - - } - - roundToZero() { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); - - return this; - - } - - negate() { - - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - - return this; - - } - - dot( v ) { - - return this.x * v.x + this.y * v.y + this.z * v.z; - - } - - // TODO lengthSquared? - - lengthSq() { - - return this.x * this.x + this.y * this.y + this.z * this.z; - - } - - length() { - - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); - - } - - manhattanLength() { - - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); - - } - - normalize() { - - return this.divideScalar( this.length() || 1 ); - - } - - setLength( length ) { - - return this.normalize().multiplyScalar( length ); - - } - - lerp( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - - return this; - - } - - lerpVectors( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - this.z = v1.z + ( v2.z - v1.z ) * alpha; - - return this; - - } - - cross( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); - return this.crossVectors( v, w ); - - } - - return this.crossVectors( this, v ); - - } - - crossVectors( a, b ) { - - const ax = a.x, ay = a.y, az = a.z; - const bx = b.x, by = b.y, bz = b.z; - - this.x = ay * bz - az * by; - this.y = az * bx - ax * bz; - this.z = ax * by - ay * bx; - - return this; - - } - - projectOnVector( v ) { - - const denominator = v.lengthSq(); - - if ( denominator === 0 ) return this.set( 0, 0, 0 ); - - const scalar = v.dot( this ) / denominator; - - return this.copy( v ).multiplyScalar( scalar ); - - } - - projectOnPlane( planeNormal ) { - - _vector.copy( this ).projectOnVector( planeNormal ); - - return this.sub( _vector ); - - } - - reflect( normal ) { - - // reflect incident vector off plane orthogonal to normal - // normal is assumed to have unit length - - return this.sub( _vector.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); - - } - - angleTo( v ) { - - const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); - - if ( denominator === 0 ) return Math.PI / 2; - - const theta = this.dot( v ) / denominator; - - // clamp, to handle numerical problems - - return Math.acos( MathUtils.clamp( theta, - 1, 1 ) ); - - } - - distanceTo( v ) { - - return Math.sqrt( this.distanceToSquared( v ) ); - - } - - distanceToSquared( v ) { - - const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; - - return dx * dx + dy * dy + dz * dz; - - } - - manhattanDistanceTo( v ) { - - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); - - } - - setFromSpherical( s ) { - - return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); - - } - - setFromSphericalCoords( radius, phi, theta ) { - - const sinPhiRadius = Math.sin( phi ) * radius; - - this.x = sinPhiRadius * Math.sin( theta ); - this.y = Math.cos( phi ) * radius; - this.z = sinPhiRadius * Math.cos( theta ); - - return this; - - } - - setFromCylindrical( c ) { - - return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); - - } - - setFromCylindricalCoords( radius, theta, y ) { - - this.x = radius * Math.sin( theta ); - this.y = y; - this.z = radius * Math.cos( theta ); - - return this; - - } - - setFromMatrixPosition( m ) { - - const e = m.elements; - - this.x = e[ 12 ]; - this.y = e[ 13 ]; - this.z = e[ 14 ]; - - return this; - - } - - setFromMatrixScale( m ) { - - const sx = this.setFromMatrixColumn( m, 0 ).length(); - const sy = this.setFromMatrixColumn( m, 1 ).length(); - const sz = this.setFromMatrixColumn( m, 2 ).length(); - - this.x = sx; - this.y = sy; - this.z = sz; - - return this; - - } - - setFromMatrixColumn( m, index ) { - - return this.fromArray( m.elements, index * 4 ); - - } - - setFromMatrix3Column( m, index ) { - - return this.fromArray( m.elements, index * 3 ); - - } - - equals( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - - return array; - - } - - fromBufferAttribute( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - - return this; - - } - - random() { - - this.x = Math.random(); - this.y = Math.random(); - this.z = Math.random(); - - return this; - - } - - } - - const _vector = new Vector3(); - const _quaternion = new Quaternion(); - - class Box3 { - - constructor( min, max ) { - - Object.defineProperty( this, 'isBox3', { value: true } ); - - this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); - - } - - set( min, max ) { - - this.min.copy( min ); - this.max.copy( max ); - - return this; - - } - - setFromArray( array ) { - - let minX = + Infinity; - let minY = + Infinity; - let minZ = + Infinity; - - let maxX = - Infinity; - let maxY = - Infinity; - let maxZ = - Infinity; - - for ( let i = 0, l = array.length; i < l; i += 3 ) { - - const x = array[ i ]; - const y = array[ i + 1 ]; - const z = array[ i + 2 ]; - - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; - - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; - - } - - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); - - return this; - - } - - setFromBufferAttribute( attribute ) { - - let minX = + Infinity; - let minY = + Infinity; - let minZ = + Infinity; - - let maxX = - Infinity; - let maxY = - Infinity; - let maxZ = - Infinity; - - for ( let i = 0, l = attribute.count; i < l; i ++ ) { - - const x = attribute.getX( i ); - const y = attribute.getY( i ); - const z = attribute.getZ( i ); - - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; - - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; - - } - - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); - - return this; - - } - - setFromPoints( points ) { - - this.makeEmpty(); - - for ( let i = 0, il = points.length; i < il; i ++ ) { - - this.expandByPoint( points[ i ] ); - - } - - return this; - - } - - setFromCenterAndSize( center, size ) { - - const halfSize = _vector$1.copy( size ).multiplyScalar( 0.5 ); - - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); - - return this; - - } - - setFromObject( object ) { - - this.makeEmpty(); - - return this.expandByObject( object ); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( box ) { - - this.min.copy( box.min ); - this.max.copy( box.max ); - - return this; - - } - - makeEmpty() { - - this.min.x = this.min.y = this.min.z = + Infinity; - this.max.x = this.max.y = this.max.z = - Infinity; - - return this; - - } - - isEmpty() { - - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); - - } - - getCenter( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getCenter() target is now required' ); - target = new Vector3(); - - } - - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); - - } - - getSize( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getSize() target is now required' ); - target = new Vector3(); - - } - - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); - - } - - expandByPoint( point ) { - - this.min.min( point ); - this.max.max( point ); - - return this; - - } - - expandByVector( vector ) { - - this.min.sub( vector ); - this.max.add( vector ); - - return this; - - } - - expandByScalar( scalar ) { - - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); - - return this; - - } - - expandByObject( object ) { - - // Computes the world-axis-aligned bounding box of an object (including its children), - // accounting for both the object's, and children's, world transforms - - object.updateWorldMatrix( false, false ); - - const geometry = object.geometry; - - if ( geometry !== undefined ) { - - if ( geometry.boundingBox === null ) { - - geometry.computeBoundingBox(); - - } - - _box.copy( geometry.boundingBox ); - _box.applyMatrix4( object.matrixWorld ); - - this.union( _box ); - - } - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - this.expandByObject( children[ i ] ); - - } - - return this; - - } - - containsPoint( point ) { - - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y || - point.z < this.min.z || point.z > this.max.z ? false : true; - - } - - containsBox( box ) { - - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y && - this.min.z <= box.min.z && box.max.z <= this.max.z; - - } - - getParameter( point, target ) { - - // This can potentially have a divide by zero if the box - // has a size dimension of 0. - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getParameter() target is now required' ); - target = new Vector3(); - - } - - return target.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ), - ( point.z - this.min.z ) / ( this.max.z - this.min.z ) - ); - - } - - intersectsBox( box ) { - - // using 6 splitting planes to rule out intersections. - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y || - box.max.z < this.min.z || box.min.z > this.max.z ? false : true; - - } - - intersectsSphere( sphere ) { - - // Find the point on the AABB closest to the sphere center. - this.clampPoint( sphere.center, _vector$1 ); - - // If that point is inside the sphere, the AABB and sphere intersect. - return _vector$1.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); - - } - - intersectsPlane( plane ) { - - // We compute the minimum and maximum dot product values. If those values - // are on the same side (back or front) of the plane, then there is no intersection. - - let min, max; - - if ( plane.normal.x > 0 ) { - - min = plane.normal.x * this.min.x; - max = plane.normal.x * this.max.x; - - } else { - - min = plane.normal.x * this.max.x; - max = plane.normal.x * this.min.x; - - } - - if ( plane.normal.y > 0 ) { - - min += plane.normal.y * this.min.y; - max += plane.normal.y * this.max.y; - - } else { - - min += plane.normal.y * this.max.y; - max += plane.normal.y * this.min.y; - - } - - if ( plane.normal.z > 0 ) { - - min += plane.normal.z * this.min.z; - max += plane.normal.z * this.max.z; - - } else { - - min += plane.normal.z * this.max.z; - max += plane.normal.z * this.min.z; - - } - - return ( min <= - plane.constant && max >= - plane.constant ); - - } - - intersectsTriangle( triangle ) { - - if ( this.isEmpty() ) { - - return false; - - } - - // compute box center and extents - this.getCenter( _center ); - _extents.subVectors( this.max, _center ); - - // translate triangle to aabb origin - _v0.subVectors( triangle.a, _center ); - _v1.subVectors( triangle.b, _center ); - _v2.subVectors( triangle.c, _center ); - - // compute edge vectors for triangle - _f0.subVectors( _v1, _v0 ); - _f1.subVectors( _v2, _v1 ); - _f2.subVectors( _v0, _v2 ); - - // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb - // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation - // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) - let axes = [ - 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, - _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, - - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 - ]; - if ( ! satForAxes( axes, _v0, _v1, _v2, _extents ) ) { - - return false; - - } - - // test 3 face normals from the aabb - axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; - if ( ! satForAxes( axes, _v0, _v1, _v2, _extents ) ) { - - return false; - - } - - // finally testing the face normal of the triangle - // use already existing triangle edge vectors here - _triangleNormal.crossVectors( _f0, _f1 ); - axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; - - return satForAxes( axes, _v0, _v1, _v2, _extents ); - - } - - clampPoint( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .clampPoint() target is now required' ); - target = new Vector3(); - - } - - return target.copy( point ).clamp( this.min, this.max ); - - } - - distanceToPoint( point ) { - - const clampedPoint = _vector$1.copy( point ).clamp( this.min, this.max ); - - return clampedPoint.sub( point ).length(); - - } - - getBoundingSphere( target ) { - - if ( target === undefined ) { - - console.error( 'THREE.Box3: .getBoundingSphere() target is now required' ); - //target = new Sphere(); // removed to avoid cyclic dependency - - } - - this.getCenter( target.center ); - - target.radius = this.getSize( _vector$1 ).length() * 0.5; - - return target; - - } - - intersect( box ) { - - this.min.max( box.min ); - this.max.min( box.max ); - - // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. - if ( this.isEmpty() ) this.makeEmpty(); - - return this; - - } - - union( box ) { - - this.min.min( box.min ); - this.max.max( box.max ); - - return this; - - } - - applyMatrix4( matrix ) { - - // transform of empty box is an empty box. - if ( this.isEmpty() ) return this; - - // NOTE: I am using a binary pattern to specify all 2^3 combinations below - _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 - _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 - _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 - _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 - _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 - _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 - _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 - _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 - - this.setFromPoints( _points ); - - return this; - - } - - translate( offset ) { - - this.min.add( offset ); - this.max.add( offset ); - - return this; - - } - - equals( box ) { - - return box.min.equals( this.min ) && box.max.equals( this.max ); - - } - - } - - function satForAxes( axes, v0, v1, v2, extents ) { - - for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { - - _testAxis.fromArray( axes, i ); - // project the aabb onto the seperating axis - const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); - // project all 3 vertices of the triangle onto the seperating axis - const p0 = v0.dot( _testAxis ); - const p1 = v1.dot( _testAxis ); - const p2 = v2.dot( _testAxis ); - // actual test, basically see if either of the most extreme of the triangle points intersects r - if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { - - // points of the projected triangle are outside the projected half-length of the aabb - // the axis is seperating and we can exit - return false; - - } - - } - - return true; - - } - - const _points = [ - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3() - ]; - - const _vector$1 = new Vector3(); - - const _box = new Box3(); - - // triangle centered vertices - - const _v0 = new Vector3(); - const _v1 = new Vector3(); - const _v2 = new Vector3(); - - // triangle edge vectors - - const _f0 = new Vector3(); - const _f1 = new Vector3(); - const _f2 = new Vector3(); - - const _center = new Vector3(); - const _extents = new Vector3(); - const _triangleNormal = new Vector3(); - const _testAxis = new Vector3(); - - const _box$1 = new Box3(); - - class Sphere { - - constructor( center, radius ) { - - this.center = ( center !== undefined ) ? center : new Vector3(); - this.radius = ( radius !== undefined ) ? radius : - 1; - - } - - set( center, radius ) { - - this.center.copy( center ); - this.radius = radius; - - return this; - - } - - setFromPoints( points, optionalCenter ) { - - const center = this.center; - - if ( optionalCenter !== undefined ) { - - center.copy( optionalCenter ); - - } else { - - _box$1.setFromPoints( points ).getCenter( center ); - - } - - let maxRadiusSq = 0; - - for ( let i = 0, il = points.length; i < il; i ++ ) { - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); - - } - - this.radius = Math.sqrt( maxRadiusSq ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( sphere ) { - - this.center.copy( sphere.center ); - this.radius = sphere.radius; - - return this; - - } - - isEmpty() { - - return ( this.radius < 0 ); - - } - - makeEmpty() { - - this.center.set( 0, 0, 0 ); - this.radius = - 1; - - return this; - - } - - containsPoint( point ) { - - return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); - - } - - distanceToPoint( point ) { - - return ( point.distanceTo( this.center ) - this.radius ); - - } - - intersectsSphere( sphere ) { - - const radiusSum = this.radius + sphere.radius; - - return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); - - } - - intersectsBox( box ) { - - return box.intersectsSphere( this ); - - } - - intersectsPlane( plane ) { - - return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; - - } - - clampPoint( point, target ) { - - const deltaLengthSq = this.center.distanceToSquared( point ); - - if ( target === undefined ) { - - console.warn( 'THREE.Sphere: .clampPoint() target is now required' ); - target = new Vector3(); - - } - - target.copy( point ); - - if ( deltaLengthSq > ( this.radius * this.radius ) ) { - - target.sub( this.center ).normalize(); - target.multiplyScalar( this.radius ).add( this.center ); - - } - - return target; - - } - - getBoundingBox( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' ); - target = new Box3(); - - } - - if ( this.isEmpty() ) { - - // Empty sphere produces empty bounding box - target.makeEmpty(); - return target; - - } - - target.set( this.center, this.center ); - target.expandByScalar( this.radius ); - - return target; - - } - - applyMatrix4( matrix ) { - - this.center.applyMatrix4( matrix ); - this.radius = this.radius * matrix.getMaxScaleOnAxis(); - - return this; - - } - - translate( offset ) { - - this.center.add( offset ); - - return this; - - } - - equals( sphere ) { - - return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); - - } - - } - - const _vector$2 = new Vector3(); - const _segCenter = new Vector3(); - const _segDir = new Vector3(); - const _diff = new Vector3(); - - const _edge1 = new Vector3(); - const _edge2 = new Vector3(); - const _normal = new Vector3(); - - class Ray { - - constructor( origin, direction ) { - - this.origin = ( origin !== undefined ) ? origin : new Vector3(); - this.direction = ( direction !== undefined ) ? direction : new Vector3( 0, 0, - 1 ); - - } - - set( origin, direction ) { - - this.origin.copy( origin ); - this.direction.copy( direction ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( ray ) { - - this.origin.copy( ray.origin ); - this.direction.copy( ray.direction ); - - return this; - - } - - at( t, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Ray: .at() target is now required' ); - target = new Vector3(); - - } - - return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); - - } - - lookAt( v ) { - - this.direction.copy( v ).sub( this.origin ).normalize(); - - return this; - - } - - recast( t ) { - - this.origin.copy( this.at( t, _vector$2 ) ); - - return this; - - } - - closestPointToPoint( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } - - target.subVectors( point, this.origin ); - - const directionDistance = target.dot( this.direction ); - - if ( directionDistance < 0 ) { - - return target.copy( this.origin ); - - } - - return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - - } - - distanceToPoint( point ) { - - return Math.sqrt( this.distanceSqToPoint( point ) ); - - } - - distanceSqToPoint( point ) { - - const directionDistance = _vector$2.subVectors( point, this.origin ).dot( this.direction ); - - // point behind the ray - - if ( directionDistance < 0 ) { - - return this.origin.distanceToSquared( point ); - - } - - _vector$2.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - - return _vector$2.distanceToSquared( point ); - - } - - distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { - - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h - // It returns the min distance between the ray and the segment - // defined by v0 and v1 - // It can also set two optional targets : - // - The closest point on the ray - // - The closest point on the segment - - _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); - _segDir.copy( v1 ).sub( v0 ).normalize(); - _diff.copy( this.origin ).sub( _segCenter ); - - const segExtent = v0.distanceTo( v1 ) * 0.5; - const a01 = - this.direction.dot( _segDir ); - const b0 = _diff.dot( this.direction ); - const b1 = - _diff.dot( _segDir ); - const c = _diff.lengthSq(); - const det = Math.abs( 1 - a01 * a01 ); - let s0, s1, sqrDist, extDet; - - if ( det > 0 ) { - - // The ray and segment are not parallel. - - s0 = a01 * b1 - b0; - s1 = a01 * b0 - b1; - extDet = segExtent * det; - - if ( s0 >= 0 ) { - - if ( s1 >= - extDet ) { - - if ( s1 <= extDet ) { - - // region 0 - // Minimum at interior points of ray and segment. - - const invDet = 1 / det; - s0 *= invDet; - s1 *= invDet; - sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; - - } else { - - // region 1 - - s1 = segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } else { - - // region 5 - - s1 = - segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } else { - - if ( s1 <= - extDet ) { - - // region 4 - - s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } else if ( s1 <= extDet ) { - - // region 3 - - s0 = 0; - s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = s1 * ( s1 + 2 * b1 ) + c; - - } else { - - // region 2 - - s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } - - } else { - - // Ray and segment are parallel. - - s1 = ( a01 > 0 ) ? - segExtent : segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - if ( optionalPointOnRay ) { - - optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); - - } - - if ( optionalPointOnSegment ) { - - optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter ); - - } - - return sqrDist; - - } - - intersectSphere( sphere, target ) { - - _vector$2.subVectors( sphere.center, this.origin ); - const tca = _vector$2.dot( this.direction ); - const d2 = _vector$2.dot( _vector$2 ) - tca * tca; - const radius2 = sphere.radius * sphere.radius; - - if ( d2 > radius2 ) return null; - - const thc = Math.sqrt( radius2 - d2 ); - - // t0 = first intersect point - entrance on front of sphere - const t0 = tca - thc; - - // t1 = second intersect point - exit point on back of sphere - const t1 = tca + thc; - - // test to see if both t0 and t1 are behind the ray - if so, return null - if ( t0 < 0 && t1 < 0 ) return null; - - // test to see if t0 is behind the ray: - // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, - // in order to always return an intersect point that is in front of the ray. - if ( t0 < 0 ) return this.at( t1, target ); - - // else t0 is in front of the ray, so return the first collision point scaled by t0 - return this.at( t0, target ); - - } - - intersectsSphere( sphere ) { - - return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); - - } - - distanceToPlane( plane ) { - - const denominator = plane.normal.dot( this.direction ); - - if ( denominator === 0 ) { - - // line is coplanar, return origin - if ( plane.distanceToPoint( this.origin ) === 0 ) { - - return 0; - - } - - // Null is preferable to undefined since undefined means.... it is undefined - - return null; - - } - - const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; - - // Return if the ray never intersects the plane - - return t >= 0 ? t : null; - - } - - intersectPlane( plane, target ) { - - const t = this.distanceToPlane( plane ); - - if ( t === null ) { - - return null; - - } - - return this.at( t, target ); - - } - - intersectsPlane( plane ) { - - // check if the ray lies on the plane first - - const distToPoint = plane.distanceToPoint( this.origin ); - - if ( distToPoint === 0 ) { - - return true; - - } - - const denominator = plane.normal.dot( this.direction ); - - if ( denominator * distToPoint < 0 ) { - - return true; - - } - - // ray origin is behind the plane (and is pointing behind it) - - return false; - - } - - intersectBox( box, target ) { - - let tmin, tmax, tymin, tymax, tzmin, tzmax; - - const invdirx = 1 / this.direction.x, - invdiry = 1 / this.direction.y, - invdirz = 1 / this.direction.z; - - const origin = this.origin; - - if ( invdirx >= 0 ) { - - tmin = ( box.min.x - origin.x ) * invdirx; - tmax = ( box.max.x - origin.x ) * invdirx; - - } else { - - tmin = ( box.max.x - origin.x ) * invdirx; - tmax = ( box.min.x - origin.x ) * invdirx; - - } - - if ( invdiry >= 0 ) { - - tymin = ( box.min.y - origin.y ) * invdiry; - tymax = ( box.max.y - origin.y ) * invdiry; - - } else { - - tymin = ( box.max.y - origin.y ) * invdiry; - tymax = ( box.min.y - origin.y ) * invdiry; - - } - - if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; - - // These lines also handle the case where tmin or tmax is NaN - // (result of 0 * Infinity). x !== x returns true if x is NaN - - if ( tymin > tmin || tmin !== tmin ) tmin = tymin; - - if ( tymax < tmax || tmax !== tmax ) tmax = tymax; - - if ( invdirz >= 0 ) { - - tzmin = ( box.min.z - origin.z ) * invdirz; - tzmax = ( box.max.z - origin.z ) * invdirz; - - } else { - - tzmin = ( box.max.z - origin.z ) * invdirz; - tzmax = ( box.min.z - origin.z ) * invdirz; - - } - - if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; - - if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; - - if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; - - //return point closest to the ray (positive side) - - if ( tmax < 0 ) return null; - - return this.at( tmin >= 0 ? tmin : tmax, target ); - - } - - intersectsBox( box ) { - - return this.intersectBox( box, _vector$2 ) !== null; - - } - - intersectTriangle( a, b, c, backfaceCulling, target ) { - - // Compute the offset origin, edges, and normal. - - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h - - _edge1.subVectors( b, a ); - _edge2.subVectors( c, a ); - _normal.crossVectors( _edge1, _edge2 ); - - // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, - // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by - // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) - // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) - // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) - let DdN = this.direction.dot( _normal ); - let sign; - - if ( DdN > 0 ) { - - if ( backfaceCulling ) return null; - sign = 1; - - } else if ( DdN < 0 ) { - - sign = - 1; - DdN = - DdN; - - } else { - - return null; - - } - - _diff.subVectors( this.origin, a ); - const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); - - // b1 < 0, no intersection - if ( DdQxE2 < 0 ) { - - return null; - - } - - const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); - - // b2 < 0, no intersection - if ( DdE1xQ < 0 ) { - - return null; - - } - - // b1+b2 > 1, no intersection - if ( DdQxE2 + DdE1xQ > DdN ) { - - return null; - - } - - // Line intersects triangle, check if ray does. - const QdN = - sign * _diff.dot( _normal ); - - // t < 0, no intersection - if ( QdN < 0 ) { - - return null; - - } - - // Ray intersects triangle. - return this.at( QdN / DdN, target ); - - } - - applyMatrix4( matrix4 ) { - - this.origin.applyMatrix4( matrix4 ); - this.direction.transformDirection( matrix4 ); - - return this; - - } - - equals( ray ) { - - return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); - - } - - } - - class Matrix4 { - - constructor() { - - Object.defineProperty( this, 'isMatrix4', { value: true } ); - - this.elements = [ - - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ]; - - if ( arguments.length > 0 ) { - - console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); - - } - - } - - set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { - - const te = this.elements; - - te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; - te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; - te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; - te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; - - return this; - - } - - identity() { - - this.set( - - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - clone() { - - return new Matrix4().fromArray( this.elements ); - - } - - copy( m ) { - - const te = this.elements; - const me = m.elements; - - te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; - te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; - te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; - te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; - - return this; - - } - - copyPosition( m ) { - - const te = this.elements, me = m.elements; - - te[ 12 ] = me[ 12 ]; - te[ 13 ] = me[ 13 ]; - te[ 14 ] = me[ 14 ]; - - return this; - - } - - extractBasis( xAxis, yAxis, zAxis ) { - - xAxis.setFromMatrixColumn( this, 0 ); - yAxis.setFromMatrixColumn( this, 1 ); - zAxis.setFromMatrixColumn( this, 2 ); - - return this; - - } - - makeBasis( xAxis, yAxis, zAxis ) { - - this.set( - xAxis.x, yAxis.x, zAxis.x, 0, - xAxis.y, yAxis.y, zAxis.y, 0, - xAxis.z, yAxis.z, zAxis.z, 0, - 0, 0, 0, 1 - ); - - return this; - - } - - extractRotation( m ) { - - // this method does not support reflection matrices - - const te = this.elements; - const me = m.elements; - - const scaleX = 1 / _v1$1.setFromMatrixColumn( m, 0 ).length(); - const scaleY = 1 / _v1$1.setFromMatrixColumn( m, 1 ).length(); - const scaleZ = 1 / _v1$1.setFromMatrixColumn( m, 2 ).length(); - - te[ 0 ] = me[ 0 ] * scaleX; - te[ 1 ] = me[ 1 ] * scaleX; - te[ 2 ] = me[ 2 ] * scaleX; - te[ 3 ] = 0; - - te[ 4 ] = me[ 4 ] * scaleY; - te[ 5 ] = me[ 5 ] * scaleY; - te[ 6 ] = me[ 6 ] * scaleY; - te[ 7 ] = 0; - - te[ 8 ] = me[ 8 ] * scaleZ; - te[ 9 ] = me[ 9 ] * scaleZ; - te[ 10 ] = me[ 10 ] * scaleZ; - te[ 11 ] = 0; - - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; - - return this; - - } - - makeRotationFromEuler( euler ) { - - if ( ! ( euler && euler.isEuler ) ) { - - console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); - - } - - const te = this.elements; - - const x = euler.x, y = euler.y, z = euler.z; - const a = Math.cos( x ), b = Math.sin( x ); - const c = Math.cos( y ), d = Math.sin( y ); - const e = Math.cos( z ), f = Math.sin( z ); - - if ( euler.order === 'XYZ' ) { - - const ae = a * e, af = a * f, be = b * e, bf = b * f; - - te[ 0 ] = c * e; - te[ 4 ] = - c * f; - te[ 8 ] = d; - - te[ 1 ] = af + be * d; - te[ 5 ] = ae - bf * d; - te[ 9 ] = - b * c; - - te[ 2 ] = bf - ae * d; - te[ 6 ] = be + af * d; - te[ 10 ] = a * c; - - } else if ( euler.order === 'YXZ' ) { - - const ce = c * e, cf = c * f, de = d * e, df = d * f; - - te[ 0 ] = ce + df * b; - te[ 4 ] = de * b - cf; - te[ 8 ] = a * d; - - te[ 1 ] = a * f; - te[ 5 ] = a * e; - te[ 9 ] = - b; - - te[ 2 ] = cf * b - de; - te[ 6 ] = df + ce * b; - te[ 10 ] = a * c; - - } else if ( euler.order === 'ZXY' ) { - - const ce = c * e, cf = c * f, de = d * e, df = d * f; - - te[ 0 ] = ce - df * b; - te[ 4 ] = - a * f; - te[ 8 ] = de + cf * b; - - te[ 1 ] = cf + de * b; - te[ 5 ] = a * e; - te[ 9 ] = df - ce * b; - - te[ 2 ] = - a * d; - te[ 6 ] = b; - te[ 10 ] = a * c; - - } else if ( euler.order === 'ZYX' ) { - - const ae = a * e, af = a * f, be = b * e, bf = b * f; - - te[ 0 ] = c * e; - te[ 4 ] = be * d - af; - te[ 8 ] = ae * d + bf; - - te[ 1 ] = c * f; - te[ 5 ] = bf * d + ae; - te[ 9 ] = af * d - be; - - te[ 2 ] = - d; - te[ 6 ] = b * c; - te[ 10 ] = a * c; - - } else if ( euler.order === 'YZX' ) { - - const ac = a * c, ad = a * d, bc = b * c, bd = b * d; - - te[ 0 ] = c * e; - te[ 4 ] = bd - ac * f; - te[ 8 ] = bc * f + ad; - - te[ 1 ] = f; - te[ 5 ] = a * e; - te[ 9 ] = - b * e; - - te[ 2 ] = - d * e; - te[ 6 ] = ad * f + bc; - te[ 10 ] = ac - bd * f; - - } else if ( euler.order === 'XZY' ) { - - const ac = a * c, ad = a * d, bc = b * c, bd = b * d; - - te[ 0 ] = c * e; - te[ 4 ] = - f; - te[ 8 ] = d * e; - - te[ 1 ] = ac * f + bd; - te[ 5 ] = a * e; - te[ 9 ] = ad * f - bc; - - te[ 2 ] = bc * f - ad; - te[ 6 ] = b * e; - te[ 10 ] = bd * f + ac; - - } - - // bottom row - te[ 3 ] = 0; - te[ 7 ] = 0; - te[ 11 ] = 0; - - // last column - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; - - return this; - - } - - makeRotationFromQuaternion( q ) { - - return this.compose( _zero, q, _one ); - - } - - lookAt( eye, target, up ) { - - const te = this.elements; - - _z.subVectors( eye, target ); - - if ( _z.lengthSq() === 0 ) { - - // eye and target are in the same position - - _z.z = 1; - - } - - _z.normalize(); - _x.crossVectors( up, _z ); - - if ( _x.lengthSq() === 0 ) { - - // up and z are parallel - - if ( Math.abs( up.z ) === 1 ) { - - _z.x += 0.0001; - - } else { - - _z.z += 0.0001; - - } - - _z.normalize(); - _x.crossVectors( up, _z ); - - } - - _x.normalize(); - _y.crossVectors( _z, _x ); - - te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x; - te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y; - te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z; - - return this; - - } - - multiply( m, n ) { - - if ( n !== undefined ) { - - console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); - return this.multiplyMatrices( m, n ); - - } - - return this.multiplyMatrices( this, m ); - - } - - premultiply( m ) { - - return this.multiplyMatrices( m, this ); - - } - - multiplyMatrices( a, b ) { - - const ae = a.elements; - const be = b.elements; - const te = this.elements; - - const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; - const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; - const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; - const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; - - const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; - const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; - const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; - const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; - - te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; - te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; - te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; - te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; - - te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; - te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; - te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; - te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; - - te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; - te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; - te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; - te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; - - te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; - te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; - te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; - te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; - - return this; - - } - - multiplyScalar( s ) { - - const te = this.elements; - - te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; - te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; - te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; - te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; - - return this; - - } - - determinant() { - - const te = this.elements; - - const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; - const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; - const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; - const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; - - //TODO: make this more efficient - //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) - - return ( - n41 * ( - + n14 * n23 * n32 - - n13 * n24 * n32 - - n14 * n22 * n33 - + n12 * n24 * n33 - + n13 * n22 * n34 - - n12 * n23 * n34 - ) + - n42 * ( - + n11 * n23 * n34 - - n11 * n24 * n33 - + n14 * n21 * n33 - - n13 * n21 * n34 - + n13 * n24 * n31 - - n14 * n23 * n31 - ) + - n43 * ( - + n11 * n24 * n32 - - n11 * n22 * n34 - - n14 * n21 * n32 - + n12 * n21 * n34 - + n14 * n22 * n31 - - n12 * n24 * n31 - ) + - n44 * ( - - n13 * n22 * n31 - - n11 * n23 * n32 - + n11 * n22 * n33 - + n13 * n21 * n32 - - n12 * n21 * n33 - + n12 * n23 * n31 - ) - - ); - - } - - transpose() { - - const te = this.elements; - let tmp; - - tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; - tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; - tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; - - tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; - tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; - tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; - - return this; - - } - - setPosition( x, y, z ) { - - const te = this.elements; - - if ( x.isVector3 ) { - - te[ 12 ] = x.x; - te[ 13 ] = x.y; - te[ 14 ] = x.z; - - } else { - - te[ 12 ] = x; - te[ 13 ] = y; - te[ 14 ] = z; - - } - - return this; - - } - - getInverse( m, throwOnDegenerate ) { - - if ( throwOnDegenerate !== undefined ) { - - console.warn( "THREE.Matrix4: .getInverse() can no longer be configured to throw on degenerate." ); - - } - - // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm - const te = this.elements, - me = m.elements, - - n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ], - n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ], - n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ], - n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ], - - t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, - t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, - t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, - t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; - - const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; - - if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); - - const detInv = 1 / det; - - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; - te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; - te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; - - te[ 4 ] = t12 * detInv; - te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; - te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; - te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; - - te[ 8 ] = t13 * detInv; - te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; - te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; - te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; - - te[ 12 ] = t14 * detInv; - te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; - te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; - te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; - - return this; - - } - - scale( v ) { - - const te = this.elements; - const x = v.x, y = v.y, z = v.z; - - te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; - te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; - te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; - te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; - - return this; - - } - - getMaxScaleOnAxis() { - - const te = this.elements; - - const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; - const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; - const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; - - return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); - - } - - makeTranslation( x, y, z ) { - - this.set( - - 1, 0, 0, x, - 0, 1, 0, y, - 0, 0, 1, z, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeRotationX( theta ) { - - const c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - 1, 0, 0, 0, - 0, c, - s, 0, - 0, s, c, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeRotationY( theta ) { - - const c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - c, 0, s, 0, - 0, 1, 0, 0, - - s, 0, c, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeRotationZ( theta ) { - - const c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - c, - s, 0, 0, - s, c, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeRotationAxis( axis, angle ) { - - // Based on http://www.gamedev.net/reference/articles/article1199.asp - - const c = Math.cos( angle ); - const s = Math.sin( angle ); - const t = 1 - c; - const x = axis.x, y = axis.y, z = axis.z; - const tx = t * x, ty = t * y; - - this.set( - - tx * x + c, tx * y - s * z, tx * z + s * y, 0, - tx * y + s * z, ty * y + c, ty * z - s * x, 0, - tx * z - s * y, ty * z + s * x, t * z * z + c, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeScale( x, y, z ) { - - this.set( - - x, 0, 0, 0, - 0, y, 0, 0, - 0, 0, z, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeShear( x, y, z ) { - - this.set( - - 1, y, z, 0, - x, 1, z, 0, - x, y, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - compose( position, quaternion, scale ) { - - const te = this.elements; - - const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; - const x2 = x + x, y2 = y + y, z2 = z + z; - const xx = x * x2, xy = x * y2, xz = x * z2; - const yy = y * y2, yz = y * z2, zz = z * z2; - const wx = w * x2, wy = w * y2, wz = w * z2; - - const sx = scale.x, sy = scale.y, sz = scale.z; - - te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; - te[ 1 ] = ( xy + wz ) * sx; - te[ 2 ] = ( xz - wy ) * sx; - te[ 3 ] = 0; - - te[ 4 ] = ( xy - wz ) * sy; - te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; - te[ 6 ] = ( yz + wx ) * sy; - te[ 7 ] = 0; - - te[ 8 ] = ( xz + wy ) * sz; - te[ 9 ] = ( yz - wx ) * sz; - te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; - te[ 11 ] = 0; - - te[ 12 ] = position.x; - te[ 13 ] = position.y; - te[ 14 ] = position.z; - te[ 15 ] = 1; - - return this; - - } - - decompose( position, quaternion, scale ) { - - const te = this.elements; - - let sx = _v1$1.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); - const sy = _v1$1.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); - const sz = _v1$1.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); - - // if determine is negative, we need to invert one scale - const det = this.determinant(); - if ( det < 0 ) sx = - sx; - - position.x = te[ 12 ]; - position.y = te[ 13 ]; - position.z = te[ 14 ]; - - // scale the rotation part - _m1.copy( this ); - - const invSX = 1 / sx; - const invSY = 1 / sy; - const invSZ = 1 / sz; - - _m1.elements[ 0 ] *= invSX; - _m1.elements[ 1 ] *= invSX; - _m1.elements[ 2 ] *= invSX; - - _m1.elements[ 4 ] *= invSY; - _m1.elements[ 5 ] *= invSY; - _m1.elements[ 6 ] *= invSY; - - _m1.elements[ 8 ] *= invSZ; - _m1.elements[ 9 ] *= invSZ; - _m1.elements[ 10 ] *= invSZ; - - quaternion.setFromRotationMatrix( _m1 ); - - scale.x = sx; - scale.y = sy; - scale.z = sz; - - return this; - - } - - makePerspective( left, right, top, bottom, near, far ) { - - if ( far === undefined ) { - - console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' ); - - } - - const te = this.elements; - const x = 2 * near / ( right - left ); - const y = 2 * near / ( top - bottom ); - - const a = ( right + left ) / ( right - left ); - const b = ( top + bottom ) / ( top - bottom ); - const c = - ( far + near ) / ( far - near ); - const d = - 2 * far * near / ( far - near ); - - te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; - te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; - - return this; - - } - - makeOrthographic( left, right, top, bottom, near, far ) { - - const te = this.elements; - const w = 1.0 / ( right - left ); - const h = 1.0 / ( top - bottom ); - const p = 1.0 / ( far - near ); - - const x = ( right + left ) * w; - const y = ( top + bottom ) * h; - const z = ( far + near ) * p; - - te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; - te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; - - return this; - - } - - equals( matrix ) { - - const te = this.elements; - const me = matrix.elements; - - for ( let i = 0; i < 16; i ++ ) { - - if ( te[ i ] !== me[ i ] ) return false; - - } - - return true; - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - for ( let i = 0; i < 16; i ++ ) { - - this.elements[ i ] = array[ i + offset ]; - - } - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - const te = this.elements; - - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - array[ offset + 3 ] = te[ 3 ]; - - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; - - array[ offset + 8 ] = te[ 8 ]; - array[ offset + 9 ] = te[ 9 ]; - array[ offset + 10 ] = te[ 10 ]; - array[ offset + 11 ] = te[ 11 ]; - - array[ offset + 12 ] = te[ 12 ]; - array[ offset + 13 ] = te[ 13 ]; - array[ offset + 14 ] = te[ 14 ]; - array[ offset + 15 ] = te[ 15 ]; - - return array; - - } - - } - - const _v1$1 = new Vector3(); - const _m1 = new Matrix4(); - const _zero = new Vector3( 0, 0, 0 ); - const _one = new Vector3( 1, 1, 1 ); - const _x = new Vector3(); - const _y = new Vector3(); - const _z = new Vector3(); - - class Euler { - - constructor( x = 0, y = 0, z = 0, order = Euler.DefaultOrder ) { - - Object.defineProperty( this, 'isEuler', { value: true } ); - - this._x = x; - this._y = y; - this._z = z; - this._order = order; - - } - - get x() { - - return this._x; - - } - - set x( value ) { - - this._x = value; - this._onChangeCallback(); - - } - - get y() { - - return this._y; - - } - - set y( value ) { - - this._y = value; - this._onChangeCallback(); - - } - - get z() { - - return this._z; - - } - - set z( value ) { - - this._z = value; - this._onChangeCallback(); - - } - - get order() { - - return this._order; - - } - - set order( value ) { - - this._order = value; - this._onChangeCallback(); - - } - - set( x, y, z, order ) { - - this._x = x; - this._y = y; - this._z = z; - this._order = order || this._order; - - this._onChangeCallback(); - - return this; - - } - - clone() { - - return new this.constructor( this._x, this._y, this._z, this._order ); - - } - - copy( euler ) { - - this._x = euler._x; - this._y = euler._y; - this._z = euler._z; - this._order = euler._order; - - this._onChangeCallback(); - - return this; - - } - - setFromRotationMatrix( m, order, update ) { - - const clamp = MathUtils.clamp; - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - const te = m.elements; - const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; - const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; - const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - - order = order || this._order; - - switch ( order ) { - - case 'XYZ': - - this._y = Math.asin( clamp( m13, - 1, 1 ) ); - - if ( Math.abs( m13 ) < 0.9999999 ) { - - this._x = Math.atan2( - m23, m33 ); - this._z = Math.atan2( - m12, m11 ); - - } else { - - this._x = Math.atan2( m32, m22 ); - this._z = 0; - - } - - break; - - case 'YXZ': - - this._x = Math.asin( - clamp( m23, - 1, 1 ) ); - - if ( Math.abs( m23 ) < 0.9999999 ) { - - this._y = Math.atan2( m13, m33 ); - this._z = Math.atan2( m21, m22 ); - - } else { - - this._y = Math.atan2( - m31, m11 ); - this._z = 0; - - } - - break; - - case 'ZXY': - - this._x = Math.asin( clamp( m32, - 1, 1 ) ); - - if ( Math.abs( m32 ) < 0.9999999 ) { - - this._y = Math.atan2( - m31, m33 ); - this._z = Math.atan2( - m12, m22 ); - - } else { - - this._y = 0; - this._z = Math.atan2( m21, m11 ); - - } - - break; - - case 'ZYX': - - this._y = Math.asin( - clamp( m31, - 1, 1 ) ); - - if ( Math.abs( m31 ) < 0.9999999 ) { - - this._x = Math.atan2( m32, m33 ); - this._z = Math.atan2( m21, m11 ); - - } else { - - this._x = 0; - this._z = Math.atan2( - m12, m22 ); - - } - - break; - - case 'YZX': - - this._z = Math.asin( clamp( m21, - 1, 1 ) ); - - if ( Math.abs( m21 ) < 0.9999999 ) { - - this._x = Math.atan2( - m23, m22 ); - this._y = Math.atan2( - m31, m11 ); - - } else { - - this._x = 0; - this._y = Math.atan2( m13, m33 ); - - } - - break; - - case 'XZY': - - this._z = Math.asin( - clamp( m12, - 1, 1 ) ); - - if ( Math.abs( m12 ) < 0.9999999 ) { - - this._x = Math.atan2( m32, m22 ); - this._y = Math.atan2( m13, m11 ); - - } else { - - this._x = Math.atan2( - m23, m33 ); - this._y = 0; - - } - - break; - - default: - - console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order ); - - } - - this._order = order; - - if ( update !== false ) this._onChangeCallback(); - - return this; - - } - - setFromQuaternion( q, order, update ) { - - _matrix.makeRotationFromQuaternion( q ); - - return this.setFromRotationMatrix( _matrix, order, update ); - - } - - setFromVector3( v, order ) { - - return this.set( v.x, v.y, v.z, order || this._order ); - - } - - reorder( newOrder ) { - - // WARNING: this discards revolution information -bhouston - - _quaternion$1.setFromEuler( this ); - - return this.setFromQuaternion( _quaternion$1, newOrder ); - - } - - equals( euler ) { - - return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); - - } - - fromArray( array ) { - - this._x = array[ 0 ]; - this._y = array[ 1 ]; - this._z = array[ 2 ]; - if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; - - this._onChangeCallback(); - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._order; - - return array; - - } - - toVector3( optionalResult ) { - - if ( optionalResult ) { - - return optionalResult.set( this._x, this._y, this._z ); - - } else { - - return new Vector3( this._x, this._y, this._z ); - - } - - } - - _onChange( callback ) { - - this._onChangeCallback = callback; - - return this; - - } - - _onChangeCallback() {} - - } - - Euler.DefaultOrder = 'XYZ'; - Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; - - const _matrix = new Matrix4(); - const _quaternion$1 = new Quaternion(); - - class Layers { - - constructor() { - - this.mask = 1 | 0; - - } - - set( channel ) { - - this.mask = 1 << channel | 0; - - } - - enable( channel ) { - - this.mask |= 1 << channel | 0; - - } - - enableAll() { - - this.mask = 0xffffffff | 0; - - } - - toggle( channel ) { - - this.mask ^= 1 << channel | 0; - - } - - disable( channel ) { - - this.mask &= ~ ( 1 << channel | 0 ); - - } - - disableAll() { - - this.mask = 0; - - } - - test( layers ) { - - return ( this.mask & layers.mask ) !== 0; - - } - - } - - let _object3DId = 0; - - const _v1$2 = new Vector3(); - const _q1 = new Quaternion(); - const _m1$1 = new Matrix4(); - const _target = new Vector3(); - - const _position = new Vector3(); - const _scale = new Vector3(); - const _quaternion$2 = new Quaternion(); - - const _xAxis = new Vector3( 1, 0, 0 ); - const _yAxis = new Vector3( 0, 1, 0 ); - const _zAxis = new Vector3( 0, 0, 1 ); - - const _addedEvent = { type: 'added' }; - const _removedEvent = { type: 'removed' }; - - function Object3D() { - - Object.defineProperty( this, 'id', { value: _object3DId ++ } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - this.type = 'Object3D'; - - this.parent = null; - this.children = []; - - this.up = Object3D.DefaultUp.clone(); - - const position = new Vector3(); - const rotation = new Euler(); - const quaternion = new Quaternion(); - const scale = new Vector3( 1, 1, 1 ); - - function onRotationChange() { - - quaternion.setFromEuler( rotation, false ); - - } - - function onQuaternionChange() { - - rotation.setFromQuaternion( quaternion, undefined, false ); - - } - - rotation._onChange( onRotationChange ); - quaternion._onChange( onQuaternionChange ); - - Object.defineProperties( this, { - position: { - configurable: true, - enumerable: true, - value: position - }, - rotation: { - configurable: true, - enumerable: true, - value: rotation - }, - quaternion: { - configurable: true, - enumerable: true, - value: quaternion - }, - scale: { - configurable: true, - enumerable: true, - value: scale - }, - modelViewMatrix: { - value: new Matrix4() - }, - normalMatrix: { - value: new Matrix3() - } - } ); - - this.matrix = new Matrix4(); - this.matrixWorld = new Matrix4(); - - this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; - this.matrixWorldNeedsUpdate = false; - - this.layers = new Layers(); - this.visible = true; - - this.castShadow = false; - this.receiveShadow = false; - - this.frustumCulled = true; - this.renderOrder = 0; - - this.userData = {}; - - } - - Object3D.DefaultUp = new Vector3( 0, 1, 0 ); - Object3D.DefaultMatrixAutoUpdate = true; - - Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: Object3D, - - isObject3D: true, - - onBeforeRender: function () {}, - onAfterRender: function () {}, - - applyMatrix4: function ( matrix ) { - - if ( this.matrixAutoUpdate ) this.updateMatrix(); - - this.matrix.premultiply( matrix ); - - this.matrix.decompose( this.position, this.quaternion, this.scale ); - - }, - - applyQuaternion: function ( q ) { - - this.quaternion.premultiply( q ); - - return this; - - }, - - setRotationFromAxisAngle: function ( axis, angle ) { - - // assumes axis is normalized - - this.quaternion.setFromAxisAngle( axis, angle ); - - }, - - setRotationFromEuler: function ( euler ) { - - this.quaternion.setFromEuler( euler, true ); - - }, - - setRotationFromMatrix: function ( m ) { - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - this.quaternion.setFromRotationMatrix( m ); - - }, - - setRotationFromQuaternion: function ( q ) { - - // assumes q is normalized - - this.quaternion.copy( q ); - - }, - - rotateOnAxis: function ( axis, angle ) { - - // rotate object on axis in object space - // axis is assumed to be normalized - - _q1.setFromAxisAngle( axis, angle ); - - this.quaternion.multiply( _q1 ); - - return this; - - }, - - rotateOnWorldAxis: function ( axis, angle ) { - - // rotate object on axis in world space - // axis is assumed to be normalized - // method assumes no rotated parent - - _q1.setFromAxisAngle( axis, angle ); - - this.quaternion.premultiply( _q1 ); - - return this; - - }, - - rotateX: function ( angle ) { - - return this.rotateOnAxis( _xAxis, angle ); - - }, - - rotateY: function ( angle ) { - - return this.rotateOnAxis( _yAxis, angle ); - - }, - - rotateZ: function ( angle ) { - - return this.rotateOnAxis( _zAxis, angle ); - - }, - - translateOnAxis: function ( axis, distance ) { - - // translate object by distance along axis in object space - // axis is assumed to be normalized - - _v1$2.copy( axis ).applyQuaternion( this.quaternion ); - - this.position.add( _v1$2.multiplyScalar( distance ) ); - - return this; - - }, - - translateX: function ( distance ) { - - return this.translateOnAxis( _xAxis, distance ); - - }, - - translateY: function ( distance ) { - - return this.translateOnAxis( _yAxis, distance ); - - }, - - translateZ: function ( distance ) { - - return this.translateOnAxis( _zAxis, distance ); - - }, - - localToWorld: function ( vector ) { - - return vector.applyMatrix4( this.matrixWorld ); - - }, - - worldToLocal: function ( vector ) { - - return vector.applyMatrix4( _m1$1.getInverse( this.matrixWorld ) ); - - }, - - lookAt: function ( x, y, z ) { - - // This method does not support objects having non-uniformly-scaled parent(s) - - if ( x.isVector3 ) { - - _target.copy( x ); - - } else { - - _target.set( x, y, z ); - - } - - const parent = this.parent; - - this.updateWorldMatrix( true, false ); - - _position.setFromMatrixPosition( this.matrixWorld ); - - if ( this.isCamera || this.isLight ) { - - _m1$1.lookAt( _position, _target, this.up ); - - } else { - - _m1$1.lookAt( _target, _position, this.up ); - - } - - this.quaternion.setFromRotationMatrix( _m1$1 ); - - if ( parent ) { - - _m1$1.extractRotation( parent.matrixWorld ); - _q1.setFromRotationMatrix( _m1$1 ); - this.quaternion.premultiply( _q1.inverse() ); - - } - - }, - - add: function ( object ) { - - if ( arguments.length > 1 ) { - - for ( let i = 0; i < arguments.length; i ++ ) { - - this.add( arguments[ i ] ); - - } - - return this; - - } - - if ( object === this ) { - - console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); - return this; - - } - - if ( ( object && object.isObject3D ) ) { - - if ( object.parent !== null ) { - - object.parent.remove( object ); - - } - - object.parent = this; - this.children.push( object ); - - object.dispatchEvent( _addedEvent ); - - } else { - - console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); - - } - - return this; - - }, - - remove: function ( object ) { - - if ( arguments.length > 1 ) { - - for ( let i = 0; i < arguments.length; i ++ ) { - - this.remove( arguments[ i ] ); - - } - - return this; - - } - - const index = this.children.indexOf( object ); - - if ( index !== - 1 ) { - - object.parent = null; - this.children.splice( index, 1 ); - - object.dispatchEvent( _removedEvent ); - - } - - return this; - - }, - - attach: function ( object ) { - - // adds object as a child of this, while maintaining the object's world transform - - this.updateWorldMatrix( true, false ); - - _m1$1.getInverse( this.matrixWorld ); - - if ( object.parent !== null ) { - - object.parent.updateWorldMatrix( true, false ); - - _m1$1.multiply( object.parent.matrixWorld ); - - } - - object.applyMatrix4( _m1$1 ); - - object.updateWorldMatrix( false, false ); - - this.add( object ); - - return this; - - }, - - getObjectById: function ( id ) { - - return this.getObjectByProperty( 'id', id ); - - }, - - getObjectByName: function ( name ) { - - return this.getObjectByProperty( 'name', name ); - - }, - - getObjectByProperty: function ( name, value ) { - - if ( this[ name ] === value ) return this; - - for ( let i = 0, l = this.children.length; i < l; i ++ ) { - - const child = this.children[ i ]; - const object = child.getObjectByProperty( name, value ); - - if ( object !== undefined ) { - - return object; - - } - - } - - return undefined; - - }, - - getWorldPosition: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' ); - target = new Vector3(); - - } - - this.updateMatrixWorld( true ); - - return target.setFromMatrixPosition( this.matrixWorld ); - - }, - - getWorldQuaternion: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' ); - target = new Quaternion(); - - } - - this.updateMatrixWorld( true ); - - this.matrixWorld.decompose( _position, target, _scale ); - - return target; - - }, - - getWorldScale: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldScale() target is now required' ); - target = new Vector3(); - - } - - this.updateMatrixWorld( true ); - - this.matrixWorld.decompose( _position, _quaternion$2, target ); - - return target; - - }, - - getWorldDirection: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' ); - target = new Vector3(); - - } - - this.updateMatrixWorld( true ); - - const e = this.matrixWorld.elements; - - return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); - - }, - - raycast: function () {}, - - traverse: function ( callback ) { - - callback( this ); - - const children = this.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].traverse( callback ); - - } - - }, - - traverseVisible: function ( callback ) { - - if ( this.visible === false ) return; - - callback( this ); - - const children = this.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].traverseVisible( callback ); - - } - - }, - - traverseAncestors: function ( callback ) { - - const parent = this.parent; - - if ( parent !== null ) { - - callback( parent ); - - parent.traverseAncestors( callback ); - - } - - }, - - updateMatrix: function () { - - this.matrix.compose( this.position, this.quaternion, this.scale ); - - this.matrixWorldNeedsUpdate = true; - - }, - - updateMatrixWorld: function ( force ) { - - if ( this.matrixAutoUpdate ) this.updateMatrix(); - - if ( this.matrixWorldNeedsUpdate || force ) { - - if ( this.parent === null ) { - - this.matrixWorld.copy( this.matrix ); - - } else { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - - } - - this.matrixWorldNeedsUpdate = false; - - force = true; - - } - - // update children - - const children = this.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateMatrixWorld( force ); - - } - - }, - - updateWorldMatrix: function ( updateParents, updateChildren ) { - - const parent = this.parent; - - if ( updateParents === true && parent !== null ) { - - parent.updateWorldMatrix( true, false ); - - } - - if ( this.matrixAutoUpdate ) this.updateMatrix(); - - if ( this.parent === null ) { - - this.matrixWorld.copy( this.matrix ); - - } else { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - - } - - // update children - - if ( updateChildren === true ) { - - const children = this.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateWorldMatrix( false, true ); - - } - - } - - }, - - toJSON: function ( meta ) { - - // meta is a string when called from JSON.stringify - const isRootObject = ( meta === undefined || typeof meta === 'string' ); - - const output = {}; - - // meta is a hash used to collect geometries, materials. - // not providing it implies that this is the root object - // being serialized. - if ( isRootObject ) { - - // initialize meta obj - meta = { - geometries: {}, - materials: {}, - textures: {}, - images: {}, - shapes: {} - }; - - output.metadata = { - version: 4.5, - type: 'Object', - generator: 'Object3D.toJSON' - }; - - } - - // standard Object3D serialization - - const object = {}; - - object.uuid = this.uuid; - object.type = this.type; - - if ( this.name !== '' ) object.name = this.name; - if ( this.castShadow === true ) object.castShadow = true; - if ( this.receiveShadow === true ) object.receiveShadow = true; - if ( this.visible === false ) object.visible = false; - if ( this.frustumCulled === false ) object.frustumCulled = false; - if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; - if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; - - object.layers = this.layers.mask; - object.matrix = this.matrix.toArray(); - - if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; - - // object specific properties - - if ( this.isInstancedMesh ) { - - object.type = 'InstancedMesh'; - object.count = this.count; - object.instanceMatrix = this.instanceMatrix.toJSON(); - - } - - // - - function serialize( library, element ) { - - if ( library[ element.uuid ] === undefined ) { - - library[ element.uuid ] = element.toJSON( meta ); - - } - - return element.uuid; - - } - - if ( this.isMesh || this.isLine || this.isPoints ) { - - object.geometry = serialize( meta.geometries, this.geometry ); - - const parameters = this.geometry.parameters; - - if ( parameters !== undefined && parameters.shapes !== undefined ) { - - const shapes = parameters.shapes; - - if ( Array.isArray( shapes ) ) { - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - - serialize( meta.shapes, shape ); - - } - - } else { - - serialize( meta.shapes, shapes ); - - } - - } - - } - - if ( this.material !== undefined ) { - - if ( Array.isArray( this.material ) ) { - - const uuids = []; - - for ( let i = 0, l = this.material.length; i < l; i ++ ) { - - uuids.push( serialize( meta.materials, this.material[ i ] ) ); - - } - - object.material = uuids; - - } else { - - object.material = serialize( meta.materials, this.material ); - - } - - } - - // - - if ( this.children.length > 0 ) { - - object.children = []; - - for ( let i = 0; i < this.children.length; i ++ ) { - - object.children.push( this.children[ i ].toJSON( meta ).object ); - - } - - } - - if ( isRootObject ) { - - const geometries = extractFromCache( meta.geometries ); - const materials = extractFromCache( meta.materials ); - const textures = extractFromCache( meta.textures ); - const images = extractFromCache( meta.images ); - const shapes = extractFromCache( meta.shapes ); - - if ( geometries.length > 0 ) output.geometries = geometries; - if ( materials.length > 0 ) output.materials = materials; - if ( textures.length > 0 ) output.textures = textures; - if ( images.length > 0 ) output.images = images; - if ( shapes.length > 0 ) output.shapes = shapes; - - } - - output.object = object; - - return output; - - // extract data from the cache hash - // remove metadata on each item - // and return as array - function extractFromCache( cache ) { - - const values = []; - for ( const key in cache ) { - - const data = cache[ key ]; - delete data.metadata; - values.push( data ); - - } - - return values; - - } - - }, - - clone: function ( recursive ) { - - return new this.constructor().copy( this, recursive ); - - }, - - copy: function ( source, recursive ) { - - if ( recursive === undefined ) recursive = true; - - this.name = source.name; - - this.up.copy( source.up ); - - this.position.copy( source.position ); - this.rotation.order = source.rotation.order; - this.quaternion.copy( source.quaternion ); - this.scale.copy( source.scale ); - - this.matrix.copy( source.matrix ); - this.matrixWorld.copy( source.matrixWorld ); - - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; - - this.layers.mask = source.layers.mask; - this.visible = source.visible; - - this.castShadow = source.castShadow; - this.receiveShadow = source.receiveShadow; - - this.frustumCulled = source.frustumCulled; - this.renderOrder = source.renderOrder; - - this.userData = JSON.parse( JSON.stringify( source.userData ) ); - - if ( recursive === true ) { - - for ( let i = 0; i < source.children.length; i ++ ) { - - const child = source.children[ i ]; - this.add( child.clone() ); - - } - - } - - return this; - - } - - } ); - - const _vector1 = new Vector3(); - const _vector2 = new Vector3(); - const _normalMatrix = new Matrix3(); - - class Plane { - - constructor( normal, constant ) { - - Object.defineProperty( this, 'isPlane', { value: true } ); - - // normal is assumed to be normalized - - this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); - this.constant = ( constant !== undefined ) ? constant : 0; - - } - - set( normal, constant ) { - - this.normal.copy( normal ); - this.constant = constant; - - return this; - - } - - setComponents( x, y, z, w ) { - - this.normal.set( x, y, z ); - this.constant = w; - - return this; - - } - - setFromNormalAndCoplanarPoint( normal, point ) { - - this.normal.copy( normal ); - this.constant = - point.dot( this.normal ); - - return this; - - } - - setFromCoplanarPoints( a, b, c ) { - - const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); - - // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? - - this.setFromNormalAndCoplanarPoint( normal, a ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( plane ) { - - this.normal.copy( plane.normal ); - this.constant = plane.constant; - - return this; - - } - - normalize() { - - // Note: will lead to a divide by zero if the plane is invalid. - - const inverseNormalLength = 1.0 / this.normal.length(); - this.normal.multiplyScalar( inverseNormalLength ); - this.constant *= inverseNormalLength; - - return this; - - } - - negate() { - - this.constant *= - 1; - this.normal.negate(); - - return this; - - } - - distanceToPoint( point ) { - - return this.normal.dot( point ) + this.constant; - - } - - distanceToSphere( sphere ) { - - return this.distanceToPoint( sphere.center ) - sphere.radius; - - } - - projectPoint( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .projectPoint() target is now required' ); - target = new Vector3(); - - } - - return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); - - } - - intersectLine( line, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .intersectLine() target is now required' ); - target = new Vector3(); - - } - - const direction = line.delta( _vector1 ); - - const denominator = this.normal.dot( direction ); - - if ( denominator === 0 ) { - - // line is coplanar, return origin - if ( this.distanceToPoint( line.start ) === 0 ) { - - return target.copy( line.start ); - - } - - // Unsure if this is the correct method to handle this case. - return undefined; - - } - - const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; - - if ( t < 0 || t > 1 ) { - - return undefined; - - } - - return target.copy( direction ).multiplyScalar( t ).add( line.start ); - - } - - intersectsLine( line ) { - - // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. - - const startSign = this.distanceToPoint( line.start ); - const endSign = this.distanceToPoint( line.end ); - - return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); - - } - - intersectsBox( box ) { - - return box.intersectsPlane( this ); - - } - - intersectsSphere( sphere ) { - - return sphere.intersectsPlane( this ); - - } - - coplanarPoint( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .coplanarPoint() target is now required' ); - target = new Vector3(); - - } - - return target.copy( this.normal ).multiplyScalar( - this.constant ); - - } - - applyMatrix4( matrix, optionalNormalMatrix ) { - - const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); - - const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); - - const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); - - this.constant = - referencePoint.dot( normal ); - - return this; - - } - - translate( offset ) { - - this.constant -= offset.dot( this.normal ); - - return this; - - } - - equals( plane ) { - - return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); - - } - - } - - const _v0$1 = new Vector3(); - const _v1$3 = new Vector3(); - const _v2$1 = new Vector3(); - const _v3 = new Vector3(); - - const _vab = new Vector3(); - const _vac = new Vector3(); - const _vbc = new Vector3(); - const _vap = new Vector3(); - const _vbp = new Vector3(); - const _vcp = new Vector3(); - - class Triangle { - - constructor( a, b, c ) { - - this.a = ( a !== undefined ) ? a : new Vector3(); - this.b = ( b !== undefined ) ? b : new Vector3(); - this.c = ( c !== undefined ) ? c : new Vector3(); - - } - - static getNormal( a, b, c, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getNormal() target is now required' ); - target = new Vector3(); - - } - - target.subVectors( c, b ); - _v0$1.subVectors( a, b ); - target.cross( _v0$1 ); - - const targetLengthSq = target.lengthSq(); - if ( targetLengthSq > 0 ) { - - return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); - - } - - return target.set( 0, 0, 0 ); - - } - - // static/instance method to calculate barycentric coordinates - // based on: http://www.blackpawn.com/texts/pointinpoly/default.html - static getBarycoord( point, a, b, c, target ) { - - _v0$1.subVectors( c, a ); - _v1$3.subVectors( b, a ); - _v2$1.subVectors( point, a ); - - const dot00 = _v0$1.dot( _v0$1 ); - const dot01 = _v0$1.dot( _v1$3 ); - const dot02 = _v0$1.dot( _v2$1 ); - const dot11 = _v1$3.dot( _v1$3 ); - const dot12 = _v1$3.dot( _v2$1 ); - - const denom = ( dot00 * dot11 - dot01 * dot01 ); - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getBarycoord() target is now required' ); - target = new Vector3(); - - } - - // collinear or singular triangle - if ( denom === 0 ) { - - // arbitrary location outside of triangle? - // not sure if this is the best idea, maybe should be returning undefined - return target.set( - 2, - 1, - 1 ); - - } - - const invDenom = 1 / denom; - const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; - const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; - - // barycentric coordinates must always sum to 1 - return target.set( 1 - u - v, v, u ); - - } - - static containsPoint( point, a, b, c ) { - - this.getBarycoord( point, a, b, c, _v3 ); - - return ( _v3.x >= 0 ) && ( _v3.y >= 0 ) && ( ( _v3.x + _v3.y ) <= 1 ); - - } - - static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { - - this.getBarycoord( point, p1, p2, p3, _v3 ); - - target.set( 0, 0 ); - target.addScaledVector( uv1, _v3.x ); - target.addScaledVector( uv2, _v3.y ); - target.addScaledVector( uv3, _v3.z ); - - return target; - - } - - static isFrontFacing( a, b, c, direction ) { - - _v0$1.subVectors( c, b ); - _v1$3.subVectors( a, b ); - - // strictly front facing - return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; - - } - - set( a, b, c ) { - - this.a.copy( a ); - this.b.copy( b ); - this.c.copy( c ); - - return this; - - } - - setFromPointsAndIndices( points, i0, i1, i2 ) { - - this.a.copy( points[ i0 ] ); - this.b.copy( points[ i1 ] ); - this.c.copy( points[ i2 ] ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( triangle ) { - - this.a.copy( triangle.a ); - this.b.copy( triangle.b ); - this.c.copy( triangle.c ); - - return this; - - } - - getArea() { - - _v0$1.subVectors( this.c, this.b ); - _v1$3.subVectors( this.a, this.b ); - - return _v0$1.cross( _v1$3 ).length() * 0.5; - - } - - getMidpoint( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getMidpoint() target is now required' ); - target = new Vector3(); - - } - - return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); - - } - - getNormal( target ) { - - return Triangle.getNormal( this.a, this.b, this.c, target ); - - } - - getPlane( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getPlane() target is now required' ); - target = new Plane(); - - } - - return target.setFromCoplanarPoints( this.a, this.b, this.c ); - - } - - getBarycoord( point, target ) { - - return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); - - } - - getUV( point, uv1, uv2, uv3, target ) { - - return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); - - } - - containsPoint( point ) { - - return Triangle.containsPoint( point, this.a, this.b, this.c ); - - } - - isFrontFacing( direction ) { - - return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); - - } - - intersectsBox( box ) { - - return box.intersectsTriangle( this ); - - } - - closestPointToPoint( p, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } - - const a = this.a, b = this.b, c = this.c; - let v, w; - - // algorithm thanks to Real-Time Collision Detection by Christer Ericson, - // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., - // under the accompanying license; see chapter 5.1.5 for detailed explanation. - // basically, we're distinguishing which of the voronoi regions of the triangle - // the point lies in with the minimum amount of redundant computation. - - _vab.subVectors( b, a ); - _vac.subVectors( c, a ); - _vap.subVectors( p, a ); - const d1 = _vab.dot( _vap ); - const d2 = _vac.dot( _vap ); - if ( d1 <= 0 && d2 <= 0 ) { - - // vertex region of A; barycentric coords (1, 0, 0) - return target.copy( a ); - - } - - _vbp.subVectors( p, b ); - const d3 = _vab.dot( _vbp ); - const d4 = _vac.dot( _vbp ); - if ( d3 >= 0 && d4 <= d3 ) { - - // vertex region of B; barycentric coords (0, 1, 0) - return target.copy( b ); - - } - - const vc = d1 * d4 - d3 * d2; - if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { - - v = d1 / ( d1 - d3 ); - // edge region of AB; barycentric coords (1-v, v, 0) - return target.copy( a ).addScaledVector( _vab, v ); - - } - - _vcp.subVectors( p, c ); - const d5 = _vab.dot( _vcp ); - const d6 = _vac.dot( _vcp ); - if ( d6 >= 0 && d5 <= d6 ) { - - // vertex region of C; barycentric coords (0, 0, 1) - return target.copy( c ); - - } - - const vb = d5 * d2 - d1 * d6; - if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { - - w = d2 / ( d2 - d6 ); - // edge region of AC; barycentric coords (1-w, 0, w) - return target.copy( a ).addScaledVector( _vac, w ); - - } - - const va = d3 * d6 - d5 * d4; - if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { - - _vbc.subVectors( c, b ); - w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); - // edge region of BC; barycentric coords (0, 1-w, w) - return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC - - } - - // face region - const denom = 1 / ( va + vb + vc ); - // u = va * denom - v = vb * denom; - w = vc * denom; - - return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); - - } - - equals( triangle ) { - - return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); - - } - - } - - const _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, - 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, - 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, - 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, - 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, - 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, - 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, - 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, - 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, - 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, - 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, - 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, - 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, - 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, - 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, - 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, - 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, - 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, - 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, - 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, - 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, - 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, - 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, - 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; - - const _hslA = { h: 0, s: 0, l: 0 }; - const _hslB = { h: 0, s: 0, l: 0 }; - - function hue2rgb( p, q, t ) { - - if ( t < 0 ) t += 1; - if ( t > 1 ) t -= 1; - if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; - if ( t < 1 / 2 ) return q; - if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); - return p; - - } - - function SRGBToLinear( c ) { - - return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); - - } - - function LinearToSRGB( c ) { - - return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; - - } - - class Color { - - constructor( r, g, b ) { - - Object.defineProperty( this, 'isColor', { value: true } ); - - if ( g === undefined && b === undefined ) { - - // r is THREE.Color, hex or string - return this.set( r ); - - } - - return this.setRGB( r, g, b ); - - } - - set( value ) { - - if ( value && value.isColor ) { - - this.copy( value ); - - } else if ( typeof value === 'number' ) { - - this.setHex( value ); - - } else if ( typeof value === 'string' ) { - - this.setStyle( value ); - - } - - return this; - - } - - setScalar( scalar ) { - - this.r = scalar; - this.g = scalar; - this.b = scalar; - - return this; - - } - - setHex( hex ) { - - hex = Math.floor( hex ); - - this.r = ( hex >> 16 & 255 ) / 255; - this.g = ( hex >> 8 & 255 ) / 255; - this.b = ( hex & 255 ) / 255; - - return this; - - } - - setRGB( r, g, b ) { - - this.r = r; - this.g = g; - this.b = b; - - return this; - - } - - setHSL( h, s, l ) { - - // h,s,l ranges are in 0.0 - 1.0 - h = MathUtils.euclideanModulo( h, 1 ); - s = MathUtils.clamp( s, 0, 1 ); - l = MathUtils.clamp( l, 0, 1 ); - - if ( s === 0 ) { - - this.r = this.g = this.b = l; - - } else { - - const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); - const q = ( 2 * l ) - p; - - this.r = hue2rgb( q, p, h + 1 / 3 ); - this.g = hue2rgb( q, p, h ); - this.b = hue2rgb( q, p, h - 1 / 3 ); - - } - - return this; - - } - - setStyle( style ) { - - function handleAlpha( string ) { - - if ( string === undefined ) return; - - if ( parseFloat( string ) < 1 ) { - - console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); - - } - - } - - - let m; - - if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) { - - // rgb / hsl - - let color; - const name = m[ 1 ]; - const components = m[ 2 ]; - - switch ( name ) { - - case 'rgb': - case 'rgba': - - if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { - - // rgb(255,0,0) rgba(255,0,0,0.5) - this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; - this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; - this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; - - handleAlpha( color[ 5 ] ); - - return this; - - } - - if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { - - // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) - this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; - this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; - this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; - - handleAlpha( color[ 5 ] ); - - return this; - - } - - break; - - case 'hsl': - case 'hsla': - - if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { - - // hsl(120,50%,50%) hsla(120,50%,50%,0.5) - const h = parseFloat( color[ 1 ] ) / 360; - const s = parseInt( color[ 2 ], 10 ) / 100; - const l = parseInt( color[ 3 ], 10 ) / 100; - - handleAlpha( color[ 5 ] ); - - return this.setHSL( h, s, l ); - - } - - break; - - } - - } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) { - - // hex color - - const hex = m[ 1 ]; - const size = hex.length; - - if ( size === 3 ) { - - // #ff0 - this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; - this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; - this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; - - return this; - - } else if ( size === 6 ) { - - // #ff0000 - this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; - this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; - this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; - - return this; - - } - - } - - if ( style && style.length > 0 ) { - - return this.setColorName( style ); - - } - - return this; - - } - - setColorName( style ) { - - // color keywords - const hex = _colorKeywords[ style ]; - - if ( hex !== undefined ) { - - // red - this.setHex( hex ); - - } else { - - // unknown color - console.warn( 'THREE.Color: Unknown color ' + style ); - - } - - return this; - - } - - clone() { - - return new this.constructor( this.r, this.g, this.b ); - - } - - copy( color ) { - - this.r = color.r; - this.g = color.g; - this.b = color.b; - - return this; - - } - - copyGammaToLinear( color, gammaFactor ) { - - if ( gammaFactor === undefined ) gammaFactor = 2.0; - - this.r = Math.pow( color.r, gammaFactor ); - this.g = Math.pow( color.g, gammaFactor ); - this.b = Math.pow( color.b, gammaFactor ); - - return this; - - } - - copyLinearToGamma( color, gammaFactor ) { - - if ( gammaFactor === undefined ) gammaFactor = 2.0; - - const safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; - - this.r = Math.pow( color.r, safeInverse ); - this.g = Math.pow( color.g, safeInverse ); - this.b = Math.pow( color.b, safeInverse ); - - return this; - - } - - convertGammaToLinear( gammaFactor ) { - - this.copyGammaToLinear( this, gammaFactor ); - - return this; - - } - - convertLinearToGamma( gammaFactor ) { - - this.copyLinearToGamma( this, gammaFactor ); - - return this; - - } - - copySRGBToLinear( color ) { - - this.r = SRGBToLinear( color.r ); - this.g = SRGBToLinear( color.g ); - this.b = SRGBToLinear( color.b ); - - return this; - - } - - copyLinearToSRGB( color ) { - - this.r = LinearToSRGB( color.r ); - this.g = LinearToSRGB( color.g ); - this.b = LinearToSRGB( color.b ); - - return this; - - } - - convertSRGBToLinear() { - - this.copySRGBToLinear( this ); - - return this; - - } - - convertLinearToSRGB() { - - this.copyLinearToSRGB( this ); - - return this; - - } - - getHex() { - - return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; - - } - - getHexString() { - - return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); - - } - - getHSL( target ) { - - // h,s,l ranges are in 0.0 - 1.0 - - if ( target === undefined ) { - - console.warn( 'THREE.Color: .getHSL() target is now required' ); - target = { h: 0, s: 0, l: 0 }; - - } - - const r = this.r, g = this.g, b = this.b; - - const max = Math.max( r, g, b ); - const min = Math.min( r, g, b ); - - let hue, saturation; - const lightness = ( min + max ) / 2.0; - - if ( min === max ) { - - hue = 0; - saturation = 0; - - } else { - - const delta = max - min; - - saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); - - switch ( max ) { - - case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; - case g: hue = ( b - r ) / delta + 2; break; - case b: hue = ( r - g ) / delta + 4; break; - - } - - hue /= 6; - - } - - target.h = hue; - target.s = saturation; - target.l = lightness; - - return target; - - } - - getStyle() { - - return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; - - } - - offsetHSL( h, s, l ) { - - this.getHSL( _hslA ); - - _hslA.h += h; _hslA.s += s; _hslA.l += l; - - this.setHSL( _hslA.h, _hslA.s, _hslA.l ); - - return this; - - } - - add( color ) { - - this.r += color.r; - this.g += color.g; - this.b += color.b; - - return this; - - } - - addColors( color1, color2 ) { - - this.r = color1.r + color2.r; - this.g = color1.g + color2.g; - this.b = color1.b + color2.b; - - return this; - - } - - addScalar( s ) { - - this.r += s; - this.g += s; - this.b += s; - - return this; - - } - - sub( color ) { - - this.r = Math.max( 0, this.r - color.r ); - this.g = Math.max( 0, this.g - color.g ); - this.b = Math.max( 0, this.b - color.b ); - - return this; - - } - - multiply( color ) { - - this.r *= color.r; - this.g *= color.g; - this.b *= color.b; - - return this; - - } - - multiplyScalar( s ) { - - this.r *= s; - this.g *= s; - this.b *= s; - - return this; - - } - - lerp( color, alpha ) { - - this.r += ( color.r - this.r ) * alpha; - this.g += ( color.g - this.g ) * alpha; - this.b += ( color.b - this.b ) * alpha; - - return this; - - } - - lerpHSL( color, alpha ) { - - this.getHSL( _hslA ); - color.getHSL( _hslB ); - - const h = MathUtils.lerp( _hslA.h, _hslB.h, alpha ); - const s = MathUtils.lerp( _hslA.s, _hslB.s, alpha ); - const l = MathUtils.lerp( _hslA.l, _hslB.l, alpha ); - - this.setHSL( h, s, l ); - - return this; - - } - - equals( c ) { - - return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this.r = array[ offset ]; - this.g = array[ offset + 1 ]; - this.b = array[ offset + 2 ]; - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.r; - array[ offset + 1 ] = this.g; - array[ offset + 2 ] = this.b; - - return array; - - } - - fromBufferAttribute( attribute, index ) { - - this.r = attribute.getX( index ); - this.g = attribute.getY( index ); - this.b = attribute.getZ( index ); - - if ( attribute.normalized === true ) { - - // assuming Uint8Array - - this.r /= 255; - this.g /= 255; - this.b /= 255; - - } - - return this; - - } - - toJSON() { - - return this.getHex(); - - } - - } - - Color.NAMES = _colorKeywords; - Color.prototype.r = 1; - Color.prototype.g = 1; - Color.prototype.b = 1; - - class Face3 { - - constructor( a, b, c, normal, color, materialIndex ) { - - this.a = a; - this.b = b; - this.c = c; - - this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3(); - this.vertexNormals = Array.isArray( normal ) ? normal : []; - - this.color = ( color && color.isColor ) ? color : new Color(); - this.vertexColors = Array.isArray( color ) ? color : []; - - this.materialIndex = materialIndex !== undefined ? materialIndex : 0; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( source ) { - - this.a = source.a; - this.b = source.b; - this.c = source.c; - - this.normal.copy( source.normal ); - this.color.copy( source.color ); - - this.materialIndex = source.materialIndex; - - for ( let i = 0, il = source.vertexNormals.length; i < il; i ++ ) { - - this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); - - } - - for ( let i = 0, il = source.vertexColors.length; i < il; i ++ ) { - - this.vertexColors[ i ] = source.vertexColors[ i ].clone(); - - } - - return this; - - } - - } - - let materialId = 0; - - function Material() { - - Object.defineProperty( this, 'id', { value: materialId ++ } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - this.type = 'Material'; - - this.fog = true; - - this.blending = NormalBlending; - this.side = FrontSide; - this.flatShading = false; - this.vertexColors = false; - - this.opacity = 1; - this.transparent = false; - - this.blendSrc = SrcAlphaFactor; - this.blendDst = OneMinusSrcAlphaFactor; - this.blendEquation = AddEquation; - this.blendSrcAlpha = null; - this.blendDstAlpha = null; - this.blendEquationAlpha = null; - - this.depthFunc = LessEqualDepth; - this.depthTest = true; - this.depthWrite = true; - - this.stencilWriteMask = 0xff; - this.stencilFunc = AlwaysStencilFunc; - this.stencilRef = 0; - this.stencilFuncMask = 0xff; - this.stencilFail = KeepStencilOp; - this.stencilZFail = KeepStencilOp; - this.stencilZPass = KeepStencilOp; - this.stencilWrite = false; - - this.clippingPlanes = null; - this.clipIntersection = false; - this.clipShadows = false; - - this.shadowSide = null; - - this.colorWrite = true; - - this.precision = null; // override the renderer's default precision for this material - - this.polygonOffset = false; - this.polygonOffsetFactor = 0; - this.polygonOffsetUnits = 0; - - this.dithering = false; - - this.alphaTest = 0; - this.premultipliedAlpha = false; - - this.visible = true; - - this.toneMapped = true; - - this.userData = {}; - - this.version = 0; - - } - - Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: Material, - - isMaterial: true, - - onBeforeCompile: function ( /* shaderobject, renderer */ ) {}, - - customProgramCacheKey: function () { - - return this.onBeforeCompile.toString(); - - }, - - setValues: function ( values ) { - - if ( values === undefined ) return; - - for ( const key in values ) { - - const newValue = values[ key ]; - - if ( newValue === undefined ) { - - console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); - continue; - - } - - // for backward compatability if shading is set in the constructor - if ( key === 'shading' ) { - - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( newValue === FlatShading ) ? true : false; - continue; - - } - - const currentValue = this[ key ]; - - if ( currentValue === undefined ) { - - console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); - continue; - - } - - if ( currentValue && currentValue.isColor ) { - - currentValue.set( newValue ); - - } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { - - currentValue.copy( newValue ); - - } else { - - this[ key ] = newValue; - - } - - } - - }, - - toJSON: function ( meta ) { - - const isRoot = ( meta === undefined || typeof meta === 'string' ); - - if ( isRoot ) { - - meta = { - textures: {}, - images: {} - }; - - } - - const data = { - metadata: { - version: 4.5, - type: 'Material', - generator: 'Material.toJSON' - } - }; - - // standard Material serialization - data.uuid = this.uuid; - data.type = this.type; - - if ( this.name !== '' ) data.name = this.name; - - if ( this.color && this.color.isColor ) data.color = this.color.getHex(); - - if ( this.roughness !== undefined ) data.roughness = this.roughness; - if ( this.metalness !== undefined ) data.metalness = this.metalness; - - if ( this.sheen && this.sheen.isColor ) data.sheen = this.sheen.getHex(); - if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); - if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; - - if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); - if ( this.shininess !== undefined ) data.shininess = this.shininess; - if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat; - if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness; - - if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { - - data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; - - } - - if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { - - data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; - - } - - if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { - - data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; - data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); - - } - - if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; - if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; - if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; - if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; - - if ( this.aoMap && this.aoMap.isTexture ) { - - data.aoMap = this.aoMap.toJSON( meta ).uuid; - data.aoMapIntensity = this.aoMapIntensity; - - } - - if ( this.bumpMap && this.bumpMap.isTexture ) { - - data.bumpMap = this.bumpMap.toJSON( meta ).uuid; - data.bumpScale = this.bumpScale; - - } - - if ( this.normalMap && this.normalMap.isTexture ) { - - data.normalMap = this.normalMap.toJSON( meta ).uuid; - data.normalMapType = this.normalMapType; - data.normalScale = this.normalScale.toArray(); - - } - - if ( this.displacementMap && this.displacementMap.isTexture ) { - - data.displacementMap = this.displacementMap.toJSON( meta ).uuid; - data.displacementScale = this.displacementScale; - data.displacementBias = this.displacementBias; - - } - - if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; - if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; - - if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; - if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; - - if ( this.envMap && this.envMap.isTexture ) { - - data.envMap = this.envMap.toJSON( meta ).uuid; - data.reflectivity = this.reflectivity; // Scale behind envMap - data.refractionRatio = this.refractionRatio; - - if ( this.combine !== undefined ) data.combine = this.combine; - if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; - - } - - if ( this.gradientMap && this.gradientMap.isTexture ) { - - data.gradientMap = this.gradientMap.toJSON( meta ).uuid; - - } - - if ( this.size !== undefined ) data.size = this.size; - if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; - - if ( this.blending !== NormalBlending ) data.blending = this.blending; - if ( this.flatShading === true ) data.flatShading = this.flatShading; - if ( this.side !== FrontSide ) data.side = this.side; - if ( this.vertexColors ) data.vertexColors = true; - - if ( this.opacity < 1 ) data.opacity = this.opacity; - if ( this.transparent === true ) data.transparent = this.transparent; - - data.depthFunc = this.depthFunc; - data.depthTest = this.depthTest; - data.depthWrite = this.depthWrite; - - data.stencilWrite = this.stencilWrite; - data.stencilWriteMask = this.stencilWriteMask; - data.stencilFunc = this.stencilFunc; - data.stencilRef = this.stencilRef; - data.stencilFuncMask = this.stencilFuncMask; - data.stencilFail = this.stencilFail; - data.stencilZFail = this.stencilZFail; - data.stencilZPass = this.stencilZPass; - - // rotation (SpriteMaterial) - if ( this.rotation && this.rotation !== 0 ) data.rotation = this.rotation; - - if ( this.polygonOffset === true ) data.polygonOffset = true; - if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; - if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; - - if ( this.linewidth && this.linewidth !== 1 ) data.linewidth = this.linewidth; - if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; - if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; - if ( this.scale !== undefined ) data.scale = this.scale; - - if ( this.dithering === true ) data.dithering = true; - - if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; - if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; - - if ( this.wireframe === true ) data.wireframe = this.wireframe; - if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; - if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap; - if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin; - - if ( this.morphTargets === true ) data.morphTargets = true; - if ( this.morphNormals === true ) data.morphNormals = true; - if ( this.skinning === true ) data.skinning = true; - - if ( this.visible === false ) data.visible = false; - - if ( this.toneMapped === false ) data.toneMapped = false; - - if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData; - - // TODO: Copied from Object3D.toJSON - - function extractFromCache( cache ) { - - const values = []; - - for ( const key in cache ) { - - const data = cache[ key ]; - delete data.metadata; - values.push( data ); - - } - - return values; - - } - - if ( isRoot ) { - - const textures = extractFromCache( meta.textures ); - const images = extractFromCache( meta.images ); - - if ( textures.length > 0 ) data.textures = textures; - if ( images.length > 0 ) data.images = images; - - } - - return data; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.name = source.name; - - this.fog = source.fog; - - this.blending = source.blending; - this.side = source.side; - this.flatShading = source.flatShading; - this.vertexColors = source.vertexColors; - - this.opacity = source.opacity; - this.transparent = source.transparent; - - this.blendSrc = source.blendSrc; - this.blendDst = source.blendDst; - this.blendEquation = source.blendEquation; - this.blendSrcAlpha = source.blendSrcAlpha; - this.blendDstAlpha = source.blendDstAlpha; - this.blendEquationAlpha = source.blendEquationAlpha; - - this.depthFunc = source.depthFunc; - this.depthTest = source.depthTest; - this.depthWrite = source.depthWrite; - - this.stencilWriteMask = source.stencilWriteMask; - this.stencilFunc = source.stencilFunc; - this.stencilRef = source.stencilRef; - this.stencilFuncMask = source.stencilFuncMask; - this.stencilFail = source.stencilFail; - this.stencilZFail = source.stencilZFail; - this.stencilZPass = source.stencilZPass; - this.stencilWrite = source.stencilWrite; - - const srcPlanes = source.clippingPlanes; - let dstPlanes = null; - - if ( srcPlanes !== null ) { - - const n = srcPlanes.length; - dstPlanes = new Array( n ); - - for ( let i = 0; i !== n; ++ i ) { - - dstPlanes[ i ] = srcPlanes[ i ].clone(); - - } - - } - - this.clippingPlanes = dstPlanes; - this.clipIntersection = source.clipIntersection; - this.clipShadows = source.clipShadows; - - this.shadowSide = source.shadowSide; - - this.colorWrite = source.colorWrite; - - this.precision = source.precision; - - this.polygonOffset = source.polygonOffset; - this.polygonOffsetFactor = source.polygonOffsetFactor; - this.polygonOffsetUnits = source.polygonOffsetUnits; - - this.dithering = source.dithering; - - this.alphaTest = source.alphaTest; - this.premultipliedAlpha = source.premultipliedAlpha; - - this.visible = source.visible; - - this.toneMapped = source.toneMapped; - - this.userData = JSON.parse( JSON.stringify( source.userData ) ); - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - Object.defineProperty( Material.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - /** - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: - * } - */ - - function MeshBasicMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshBasicMaterial'; - - this.color = new Color( 0xffffff ); // emissive - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - - this.setValues( parameters ); - - } - - MeshBasicMaterial.prototype = Object.create( Material.prototype ); - MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; - - MeshBasicMaterial.prototype.isMeshBasicMaterial = true; - - MeshBasicMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - return this; - - }; - - const _vector$3 = new Vector3(); - const _vector2$1 = new Vector2(); - - function BufferAttribute( array, itemSize, normalized ) { - - if ( Array.isArray( array ) ) { - - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - - } - - this.name = ''; - - this.array = array; - this.itemSize = itemSize; - this.count = array !== undefined ? array.length / itemSize : 0; - this.normalized = normalized === true; - - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; - - this.version = 0; - - } - - Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - Object.assign( BufferAttribute.prototype, { - - isBufferAttribute: true, - - onUploadCallback: function () {}, - - setUsage: function ( value ) { - - this.usage = value; - - return this; - - }, - - copy: function ( source ) { - - this.name = source.name; - this.array = new source.array.constructor( source.array ); - this.itemSize = source.itemSize; - this.count = source.count; - this.normalized = source.normalized; - - this.usage = source.usage; - - return this; - - }, - - copyAt: function ( index1, attribute, index2 ) { - - index1 *= this.itemSize; - index2 *= attribute.itemSize; - - for ( let i = 0, l = this.itemSize; i < l; i ++ ) { - - this.array[ index1 + i ] = attribute.array[ index2 + i ]; - - } - - return this; - - }, - - copyArray: function ( array ) { - - this.array.set( array ); - - return this; - - }, - - copyColorsArray: function ( colors ) { - - const array = this.array; - let offset = 0; - - for ( let i = 0, l = colors.length; i < l; i ++ ) { - - let color = colors[ i ]; - - if ( color === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); - color = new Color(); - - } - - array[ offset ++ ] = color.r; - array[ offset ++ ] = color.g; - array[ offset ++ ] = color.b; - - } - - return this; - - }, - - copyVector2sArray: function ( vectors ) { - - const array = this.array; - let offset = 0; - - for ( let i = 0, l = vectors.length; i < l; i ++ ) { - - let vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); - vector = new Vector2(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - - } - - return this; - - }, - - copyVector3sArray: function ( vectors ) { - - const array = this.array; - let offset = 0; - - for ( let i = 0, l = vectors.length; i < l; i ++ ) { - - let vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); - vector = new Vector3(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - - } - - return this; - - }, - - copyVector4sArray: function ( vectors ) { - - const array = this.array; - let offset = 0; - - for ( let i = 0, l = vectors.length; i < l; i ++ ) { - - let vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); - vector = new Vector4(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - array[ offset ++ ] = vector.w; - - } - - return this; - - }, - - applyMatrix3: function ( m ) { - - if ( this.itemSize === 2 ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector2$1.fromBufferAttribute( this, i ); - _vector2$1.applyMatrix3( m ); - - this.setXY( i, _vector2$1.x, _vector2$1.y ); - - } - - } else if ( this.itemSize === 3 ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$3.fromBufferAttribute( this, i ); - _vector$3.applyMatrix3( m ); - - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); - - } - - } - - return this; - - }, - - applyMatrix4: function ( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); - - _vector$3.applyMatrix4( m ); - - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); - - } - - return this; - - }, - - applyNormalMatrix: function ( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); - - _vector$3.applyNormalMatrix( m ); - - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); - - } - - return this; - - }, - - transformDirection: function ( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); - - _vector$3.transformDirection( m ); - - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); - - } - - return this; - - }, - - set: function ( value, offset ) { - - if ( offset === undefined ) offset = 0; - - this.array.set( value, offset ); - - return this; - - }, - - getX: function ( index ) { - - return this.array[ index * this.itemSize ]; - - }, - - setX: function ( index, x ) { - - this.array[ index * this.itemSize ] = x; - - return this; - - }, - - getY: function ( index ) { - - return this.array[ index * this.itemSize + 1 ]; - - }, - - setY: function ( index, y ) { - - this.array[ index * this.itemSize + 1 ] = y; - - return this; - - }, - - getZ: function ( index ) { - - return this.array[ index * this.itemSize + 2 ]; - - }, - - setZ: function ( index, z ) { - - this.array[ index * this.itemSize + 2 ] = z; - - return this; - - }, - - getW: function ( index ) { - - return this.array[ index * this.itemSize + 3 ]; - - }, - - setW: function ( index, w ) { - - this.array[ index * this.itemSize + 3 ] = w; - - return this; - - }, - - setXY: function ( index, x, y ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - - return this; - - }, - - setXYZ: function ( index, x, y, z ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - - return this; - - }, - - setXYZW: function ( index, x, y, z, w ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - this.array[ index + 3 ] = w; - - return this; - - }, - - onUpload: function ( callback ) { - - this.onUploadCallback = callback; - - return this; - - }, - - clone: function () { - - return new this.constructor( this.array, this.itemSize ).copy( this ); - - }, - - toJSON: function () { - - return { - itemSize: this.itemSize, - type: this.array.constructor.name, - array: Array.prototype.slice.call( this.array ), - normalized: this.normalized - }; - - } - - } ); - - // - - function Int8BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized ); - - } - - Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int8BufferAttribute.prototype.constructor = Int8BufferAttribute; - - - function Uint8BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized ); - - } - - Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute; - - - function Uint8ClampedBufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized ); - - } - - Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute; - - - function Int16BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized ); - - } - - Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int16BufferAttribute.prototype.constructor = Int16BufferAttribute; - - - function Uint16BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized ); - - } - - Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute; - - - function Int32BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized ); - - } - - Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int32BufferAttribute.prototype.constructor = Int32BufferAttribute; - - - function Uint32BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized ); - - } - - Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute; - - - function Float32BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized ); - - } - - Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; - - - function Float64BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized ); - - } - - Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float64BufferAttribute.prototype.constructor = Float64BufferAttribute; - - class DirectGeometry { - - constructor() { - - this.vertices = []; - this.normals = []; - this.colors = []; - this.uvs = []; - this.uvs2 = []; - - this.groups = []; - - this.morphTargets = {}; - - this.skinWeights = []; - this.skinIndices = []; - - // this.lineDistances = []; - - this.boundingBox = null; - this.boundingSphere = null; - - // update flags - - this.verticesNeedUpdate = false; - this.normalsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.uvsNeedUpdate = false; - this.groupsNeedUpdate = false; - - } - - computeGroups( geometry ) { - - const groups = []; - - let group, i; - let materialIndex = undefined; - - const faces = geometry.faces; - - for ( i = 0; i < faces.length; i ++ ) { - - const face = faces[ i ]; - - // materials - - if ( face.materialIndex !== materialIndex ) { - - materialIndex = face.materialIndex; - - if ( group !== undefined ) { - - group.count = ( i * 3 ) - group.start; - groups.push( group ); - - } - - group = { - start: i * 3, - materialIndex: materialIndex - }; - - } - - } - - if ( group !== undefined ) { - - group.count = ( i * 3 ) - group.start; - groups.push( group ); - - } - - this.groups = groups; - - } - - fromGeometry( geometry ) { - - const faces = geometry.faces; - const vertices = geometry.vertices; - const faceVertexUvs = geometry.faceVertexUvs; - - const hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; - const hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; - - // morphs - - const morphTargets = geometry.morphTargets; - const morphTargetsLength = morphTargets.length; - - let morphTargetsPosition; - - if ( morphTargetsLength > 0 ) { - - morphTargetsPosition = []; - - for ( let i = 0; i < morphTargetsLength; i ++ ) { - - morphTargetsPosition[ i ] = { - name: morphTargets[ i ].name, - data: [] - }; - - } - - this.morphTargets.position = morphTargetsPosition; - - } - - const morphNormals = geometry.morphNormals; - const morphNormalsLength = morphNormals.length; - - let morphTargetsNormal; - - if ( morphNormalsLength > 0 ) { - - morphTargetsNormal = []; - - for ( let i = 0; i < morphNormalsLength; i ++ ) { - - morphTargetsNormal[ i ] = { - name: morphNormals[ i ].name, - data: [] - }; - - } - - this.morphTargets.normal = morphTargetsNormal; - - } - - // skins - - const skinIndices = geometry.skinIndices; - const skinWeights = geometry.skinWeights; - - const hasSkinIndices = skinIndices.length === vertices.length; - const hasSkinWeights = skinWeights.length === vertices.length; - - // - - if ( vertices.length > 0 && faces.length === 0 ) { - - console.error( 'THREE.DirectGeometry: Faceless geometries are not supported.' ); - - } - - for ( let i = 0; i < faces.length; i ++ ) { - - const face = faces[ i ]; - - this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); - - const vertexNormals = face.vertexNormals; - - if ( vertexNormals.length === 3 ) { - - this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); - - } else { - - const normal = face.normal; - - this.normals.push( normal, normal, normal ); - - } - - const vertexColors = face.vertexColors; - - if ( vertexColors.length === 3 ) { - - this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); - - } else { - - const color = face.color; - - this.colors.push( color, color, color ); - - } - - if ( hasFaceVertexUv === true ) { - - const vertexUvs = faceVertexUvs[ 0 ][ i ]; - - if ( vertexUvs !== undefined ) { - - this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); - - } else { - - console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); - - this.uvs.push( new Vector2(), new Vector2(), new Vector2() ); - - } - - } - - if ( hasFaceVertexUv2 === true ) { - - const vertexUvs = faceVertexUvs[ 1 ][ i ]; - - if ( vertexUvs !== undefined ) { - - this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); - - } else { - - console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); - - this.uvs2.push( new Vector2(), new Vector2(), new Vector2() ); - - } - - } - - // morphs - - for ( let j = 0; j < morphTargetsLength; j ++ ) { - - const morphTarget = morphTargets[ j ].vertices; - - morphTargetsPosition[ j ].data.push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); - - } - - for ( let j = 0; j < morphNormalsLength; j ++ ) { - - const morphNormal = morphNormals[ j ].vertexNormals[ i ]; - - morphTargetsNormal[ j ].data.push( morphNormal.a, morphNormal.b, morphNormal.c ); - - } - - // skins - - if ( hasSkinIndices ) { - - this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); - - } - - if ( hasSkinWeights ) { - - this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); - - } - - } - - this.computeGroups( geometry ); - - this.verticesNeedUpdate = geometry.verticesNeedUpdate; - this.normalsNeedUpdate = geometry.normalsNeedUpdate; - this.colorsNeedUpdate = geometry.colorsNeedUpdate; - this.uvsNeedUpdate = geometry.uvsNeedUpdate; - this.groupsNeedUpdate = geometry.groupsNeedUpdate; - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - return this; - - } - - } - - function arrayMax( array ) { - - if ( array.length === 0 ) return - Infinity; - - let max = array[ 0 ]; - - for ( let i = 1, l = array.length; i < l; ++ i ) { - - if ( array[ i ] > max ) max = array[ i ]; - - } - - return max; - - } - - let _bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id - - const _m1$2 = new Matrix4(); - const _obj = new Object3D(); - const _offset = new Vector3(); - const _box$2 = new Box3(); - const _boxMorphTargets = new Box3(); - const _vector$4 = new Vector3(); - - function BufferGeometry() { - - Object.defineProperty( this, 'id', { value: _bufferGeometryId += 2 } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - this.type = 'BufferGeometry'; - - this.index = null; - this.attributes = {}; - - this.morphAttributes = {}; - this.morphTargetsRelative = false; - - this.groups = []; - - this.boundingBox = null; - this.boundingSphere = null; - - this.drawRange = { start: 0, count: Infinity }; - - this.userData = {}; - - } - - BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: BufferGeometry, - - isBufferGeometry: true, - - getIndex: function () { - - return this.index; - - }, - - setIndex: function ( index ) { - - if ( Array.isArray( index ) ) { - - this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); - - } else { - - this.index = index; - - } - - }, - - getAttribute: function ( name ) { - - return this.attributes[ name ]; - - }, - - setAttribute: function ( name, attribute ) { - - this.attributes[ name ] = attribute; - - return this; - - }, - - deleteAttribute: function ( name ) { - - delete this.attributes[ name ]; - - return this; - - }, - - addGroup: function ( start, count, materialIndex ) { - - this.groups.push( { - - start: start, - count: count, - materialIndex: materialIndex !== undefined ? materialIndex : 0 - - } ); - - }, - - clearGroups: function () { - - this.groups = []; - - }, - - setDrawRange: function ( start, count ) { - - this.drawRange.start = start; - this.drawRange.count = count; - - }, - - applyMatrix4: function ( matrix ) { - - const position = this.attributes.position; - - if ( position !== undefined ) { - - position.applyMatrix4( matrix ); - - position.needsUpdate = true; - - } - - const normal = this.attributes.normal; - - if ( normal !== undefined ) { - - const normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - normal.applyNormalMatrix( normalMatrix ); - - normal.needsUpdate = true; - - } - - const tangent = this.attributes.tangent; - - if ( tangent !== undefined ) { - - tangent.transformDirection( matrix ); - - tangent.needsUpdate = true; - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - return this; - - }, - - rotateX: function ( angle ) { - - // rotate geometry around world x-axis - - _m1$2.makeRotationX( angle ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - rotateY: function ( angle ) { - - // rotate geometry around world y-axis - - _m1$2.makeRotationY( angle ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - rotateZ: function ( angle ) { - - // rotate geometry around world z-axis - - _m1$2.makeRotationZ( angle ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - translate: function ( x, y, z ) { - - // translate geometry - - _m1$2.makeTranslation( x, y, z ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - scale: function ( x, y, z ) { - - // scale geometry - - _m1$2.makeScale( x, y, z ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - lookAt: function ( vector ) { - - _obj.lookAt( vector ); - - _obj.updateMatrix(); - - this.applyMatrix4( _obj.matrix ); - - return this; - - }, - - center: function () { - - this.computeBoundingBox(); - - this.boundingBox.getCenter( _offset ).negate(); - - this.translate( _offset.x, _offset.y, _offset.z ); - - return this; - - }, - - setFromObject: function ( object ) { - - // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); - - const geometry = object.geometry; - - if ( object.isPoints || object.isLine ) { - - const positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 ); - const colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 ); - - this.setAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); - this.setAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); - - if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { - - const lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 ); - - this.setAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); - - } - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - } else if ( object.isMesh ) { - - if ( geometry && geometry.isGeometry ) { - - this.fromGeometry( geometry ); - - } - - } - - return this; - - }, - - setFromPoints: function ( points ) { - - const position = []; - - for ( let i = 0, l = points.length; i < l; i ++ ) { - - const point = points[ i ]; - position.push( point.x, point.y, point.z || 0 ); - - } - - this.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) ); - - return this; - - }, - - updateFromObject: function ( object ) { - - let geometry = object.geometry; - - if ( object.isMesh ) { - - let direct = geometry.__directGeometry; - - if ( geometry.elementsNeedUpdate === true ) { - - direct = undefined; - geometry.elementsNeedUpdate = false; - - } - - if ( direct === undefined ) { - - return this.fromGeometry( geometry ); - - } - - direct.verticesNeedUpdate = geometry.verticesNeedUpdate; - direct.normalsNeedUpdate = geometry.normalsNeedUpdate; - direct.colorsNeedUpdate = geometry.colorsNeedUpdate; - direct.uvsNeedUpdate = geometry.uvsNeedUpdate; - direct.groupsNeedUpdate = geometry.groupsNeedUpdate; - - geometry.verticesNeedUpdate = false; - geometry.normalsNeedUpdate = false; - geometry.colorsNeedUpdate = false; - geometry.uvsNeedUpdate = false; - geometry.groupsNeedUpdate = false; - - geometry = direct; - - } - - if ( geometry.verticesNeedUpdate === true ) { - - const attribute = this.attributes.position; - - if ( attribute !== undefined ) { - - attribute.copyVector3sArray( geometry.vertices ); - attribute.needsUpdate = true; - - } - - geometry.verticesNeedUpdate = false; - - } - - if ( geometry.normalsNeedUpdate === true ) { - - const attribute = this.attributes.normal; - - if ( attribute !== undefined ) { - - attribute.copyVector3sArray( geometry.normals ); - attribute.needsUpdate = true; - - } - - geometry.normalsNeedUpdate = false; - - } - - if ( geometry.colorsNeedUpdate === true ) { - - const attribute = this.attributes.color; - - if ( attribute !== undefined ) { - - attribute.copyColorsArray( geometry.colors ); - attribute.needsUpdate = true; - - } - - geometry.colorsNeedUpdate = false; - - } - - if ( geometry.uvsNeedUpdate ) { - - const attribute = this.attributes.uv; - - if ( attribute !== undefined ) { - - attribute.copyVector2sArray( geometry.uvs ); - attribute.needsUpdate = true; - - } - - geometry.uvsNeedUpdate = false; - - } - - if ( geometry.lineDistancesNeedUpdate ) { - - const attribute = this.attributes.lineDistance; - - if ( attribute !== undefined ) { - - attribute.copyArray( geometry.lineDistances ); - attribute.needsUpdate = true; - - } - - geometry.lineDistancesNeedUpdate = false; - - } - - if ( geometry.groupsNeedUpdate ) { - - geometry.computeGroups( object.geometry ); - this.groups = geometry.groups; - - geometry.groupsNeedUpdate = false; - - } - - return this; - - }, - - fromGeometry: function ( geometry ) { - - geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry ); - - return this.fromDirectGeometry( geometry.__directGeometry ); - - }, - - fromDirectGeometry: function ( geometry ) { - - const positions = new Float32Array( geometry.vertices.length * 3 ); - this.setAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); - - if ( geometry.normals.length > 0 ) { - - const normals = new Float32Array( geometry.normals.length * 3 ); - this.setAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); - - } - - if ( geometry.colors.length > 0 ) { - - const colors = new Float32Array( geometry.colors.length * 3 ); - this.setAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); - - } - - if ( geometry.uvs.length > 0 ) { - - const uvs = new Float32Array( geometry.uvs.length * 2 ); - this.setAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); - - } - - if ( geometry.uvs2.length > 0 ) { - - const uvs2 = new Float32Array( geometry.uvs2.length * 2 ); - this.setAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); - - } - - // groups - - this.groups = geometry.groups; - - // morphs - - for ( const name in geometry.morphTargets ) { - - const array = []; - const morphTargets = geometry.morphTargets[ name ]; - - for ( let i = 0, l = morphTargets.length; i < l; i ++ ) { - - const morphTarget = morphTargets[ i ]; - - const attribute = new Float32BufferAttribute( morphTarget.data.length * 3, 3 ); - attribute.name = morphTarget.name; - - array.push( attribute.copyVector3sArray( morphTarget.data ) ); - - } - - this.morphAttributes[ name ] = array; - - } - - // skinning - - if ( geometry.skinIndices.length > 0 ) { - - const skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 ); - this.setAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); - - } - - if ( geometry.skinWeights.length > 0 ) { - - const skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 ); - this.setAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); - - } - - // - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - return this; - - }, - - computeBoundingBox: function () { - - if ( this.boundingBox === null ) { - - this.boundingBox = new Box3(); - - } - - const position = this.attributes.position; - const morphAttributesPosition = this.morphAttributes.position; - - if ( position && position.isGLBufferAttribute ) { - - console.error( 'THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".', this ); - - this.boundingBox.set( - new Vector3( - Infinity, - Infinity, - Infinity ), - new Vector3( + Infinity, + Infinity, + Infinity ) - ); - - return; - - } - - if ( position !== undefined ) { - - this.boundingBox.setFromBufferAttribute( position ); - - // process morph attributes if present - - if ( morphAttributesPosition ) { - - for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - - const morphAttribute = morphAttributesPosition[ i ]; - _box$2.setFromBufferAttribute( morphAttribute ); - - if ( this.morphTargetsRelative ) { - - _vector$4.addVectors( this.boundingBox.min, _box$2.min ); - this.boundingBox.expandByPoint( _vector$4 ); - - _vector$4.addVectors( this.boundingBox.max, _box$2.max ); - this.boundingBox.expandByPoint( _vector$4 ); - - } else { - - this.boundingBox.expandByPoint( _box$2.min ); - this.boundingBox.expandByPoint( _box$2.max ); - - } - - } - - } - - } else { - - this.boundingBox.makeEmpty(); - - } - - if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { - - console.error( 'THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); - - } - - }, - - computeBoundingSphere: function () { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new Sphere(); - - } - - const position = this.attributes.position; - const morphAttributesPosition = this.morphAttributes.position; - - if ( position && position.isGLBufferAttribute ) { - - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".', this ); - - this.boundingSphere.set( new Vector3(), Infinity ); - - return; - - } - - if ( position ) { - - // first, find the center of the bounding sphere - - const center = this.boundingSphere.center; - - _box$2.setFromBufferAttribute( position ); - - // process morph attributes if present - - if ( morphAttributesPosition ) { - - for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - - const morphAttribute = morphAttributesPosition[ i ]; - _boxMorphTargets.setFromBufferAttribute( morphAttribute ); - - if ( this.morphTargetsRelative ) { - - _vector$4.addVectors( _box$2.min, _boxMorphTargets.min ); - _box$2.expandByPoint( _vector$4 ); - - _vector$4.addVectors( _box$2.max, _boxMorphTargets.max ); - _box$2.expandByPoint( _vector$4 ); - - } else { - - _box$2.expandByPoint( _boxMorphTargets.min ); - _box$2.expandByPoint( _boxMorphTargets.max ); - - } - - } - - } - - _box$2.getCenter( center ); - - // second, try to find a boundingSphere with a radius smaller than the - // boundingSphere of the boundingBox: sqrt(3) smaller in the best case - - let maxRadiusSq = 0; - - for ( let i = 0, il = position.count; i < il; i ++ ) { - - _vector$4.fromBufferAttribute( position, i ); - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) ); - - } - - // process morph attributes if present - - if ( morphAttributesPosition ) { - - for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - - const morphAttribute = morphAttributesPosition[ i ]; - const morphTargetsRelative = this.morphTargetsRelative; - - for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) { - - _vector$4.fromBufferAttribute( morphAttribute, j ); - - if ( morphTargetsRelative ) { - - _offset.fromBufferAttribute( position, j ); - _vector$4.add( _offset ); - - } - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) ); - - } - - } - - } - - this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); - - if ( isNaN( this.boundingSphere.radius ) ) { - - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); - - } - - } - - }, - - computeFaceNormals: function () { - - // backwards compatibility - - }, - - computeVertexNormals: function () { - - const index = this.index; - const positionAttribute = this.getAttribute( 'position' ); - - if ( positionAttribute !== undefined ) { - - let normalAttribute = this.getAttribute( 'normal' ); - - if ( normalAttribute === undefined ) { - - normalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 ); - this.setAttribute( 'normal', normalAttribute ); - - } else { - - // reset existing normals to zero - - for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) { - - normalAttribute.setXYZ( i, 0, 0, 0 ); - - } - - } - - const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); - const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); - const cb = new Vector3(), ab = new Vector3(); - - // indexed elements - - if ( index ) { - - for ( let i = 0, il = index.count; i < il; i += 3 ) { - - const vA = index.getX( i + 0 ); - const vB = index.getX( i + 1 ); - const vC = index.getX( i + 2 ); - - pA.fromBufferAttribute( positionAttribute, vA ); - pB.fromBufferAttribute( positionAttribute, vB ); - pC.fromBufferAttribute( positionAttribute, vC ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - nA.fromBufferAttribute( normalAttribute, vA ); - nB.fromBufferAttribute( normalAttribute, vB ); - nC.fromBufferAttribute( normalAttribute, vC ); - - nA.add( cb ); - nB.add( cb ); - nC.add( cb ); - - normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z ); - normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z ); - normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z ); - - } - - } else { - - // non-indexed elements (unconnected triangle soup) - - for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) { - - pA.fromBufferAttribute( positionAttribute, i + 0 ); - pB.fromBufferAttribute( positionAttribute, i + 1 ); - pC.fromBufferAttribute( positionAttribute, i + 2 ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z ); - normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z ); - normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z ); - - } - - } - - this.normalizeNormals(); - - normalAttribute.needsUpdate = true; - - } - - }, - - merge: function ( geometry, offset ) { - - if ( ! ( geometry && geometry.isBufferGeometry ) ) { - - console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); - return; - - } - - if ( offset === undefined ) { - - offset = 0; - - console.warn( - 'THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. ' - + 'Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge.' - ); - - } - - const attributes = this.attributes; - - for ( const key in attributes ) { - - if ( geometry.attributes[ key ] === undefined ) continue; - - const attribute1 = attributes[ key ]; - const attributeArray1 = attribute1.array; - - const attribute2 = geometry.attributes[ key ]; - const attributeArray2 = attribute2.array; - - const attributeOffset = attribute2.itemSize * offset; - const length = Math.min( attributeArray2.length, attributeArray1.length - attributeOffset ); - - for ( let i = 0, j = attributeOffset; i < length; i ++, j ++ ) { - - attributeArray1[ j ] = attributeArray2[ i ]; - - } - - } - - return this; - - }, - - normalizeNormals: function () { - - const normals = this.attributes.normal; - - for ( let i = 0, il = normals.count; i < il; i ++ ) { - - _vector$4.fromBufferAttribute( normals, i ); - - _vector$4.normalize(); - - normals.setXYZ( i, _vector$4.x, _vector$4.y, _vector$4.z ); - - } - - }, - - toNonIndexed: function () { - - function convertBufferAttribute( attribute, indices ) { - - const array = attribute.array; - const itemSize = attribute.itemSize; - const normalized = attribute.normalized; - - const array2 = new array.constructor( indices.length * itemSize ); - - let index = 0, index2 = 0; - - for ( let i = 0, l = indices.length; i < l; i ++ ) { - - index = indices[ i ] * itemSize; - - for ( let j = 0; j < itemSize; j ++ ) { - - array2[ index2 ++ ] = array[ index ++ ]; - - } - - } - - return new BufferAttribute( array2, itemSize, normalized ); - - } - - // - - if ( this.index === null ) { - - console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' ); - return this; - - } - - const geometry2 = new BufferGeometry(); - - const indices = this.index.array; - const attributes = this.attributes; - - // attributes - - for ( const name in attributes ) { - - const attribute = attributes[ name ]; - - const newAttribute = convertBufferAttribute( attribute, indices ); - - geometry2.setAttribute( name, newAttribute ); - - } - - // morph attributes - - const morphAttributes = this.morphAttributes; - - for ( const name in morphAttributes ) { - - const morphArray = []; - const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes - - for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) { - - const attribute = morphAttribute[ i ]; - - const newAttribute = convertBufferAttribute( attribute, indices ); - - morphArray.push( newAttribute ); - - } - - geometry2.morphAttributes[ name ] = morphArray; - - } - - geometry2.morphTargetsRelative = this.morphTargetsRelative; - - // groups - - const groups = this.groups; - - for ( let i = 0, l = groups.length; i < l; i ++ ) { - - const group = groups[ i ]; - geometry2.addGroup( group.start, group.count, group.materialIndex ); - - } - - return geometry2; - - }, - - toJSON: function () { - - const data = { - metadata: { - version: 4.5, - type: 'BufferGeometry', - generator: 'BufferGeometry.toJSON' - } - }; - - // standard BufferGeometry serialization - - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) data.name = this.name; - if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; - - if ( this.parameters !== undefined ) { - - const parameters = this.parameters; - - for ( const key in parameters ) { - - if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; - - } - - return data; - - } - - data.data = { attributes: {} }; - - const index = this.index; - - if ( index !== null ) { - - data.data.index = { - type: index.array.constructor.name, - array: Array.prototype.slice.call( index.array ) - }; - - } - - const attributes = this.attributes; - - for ( const key in attributes ) { - - const attribute = attributes[ key ]; - - const attributeData = attribute.toJSON( data.data ); - - if ( attribute.name !== '' ) attributeData.name = attribute.name; - - data.data.attributes[ key ] = attributeData; - - } - - const morphAttributes = {}; - let hasMorphAttributes = false; - - for ( const key in this.morphAttributes ) { - - const attributeArray = this.morphAttributes[ key ]; - - const array = []; - - for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { - - const attribute = attributeArray[ i ]; - - const attributeData = attribute.toJSON( data.data ); - - if ( attribute.name !== '' ) attributeData.name = attribute.name; - - array.push( attributeData ); - - } - - if ( array.length > 0 ) { - - morphAttributes[ key ] = array; - - hasMorphAttributes = true; - - } - - } - - if ( hasMorphAttributes ) { - - data.data.morphAttributes = morphAttributes; - data.data.morphTargetsRelative = this.morphTargetsRelative; - - } - - const groups = this.groups; - - if ( groups.length > 0 ) { - - data.data.groups = JSON.parse( JSON.stringify( groups ) ); - - } - - const boundingSphere = this.boundingSphere; - - if ( boundingSphere !== null ) { - - data.data.boundingSphere = { - center: boundingSphere.center.toArray(), - radius: boundingSphere.radius - }; - - } - - return data; - - }, - - clone: function () { - - /* - // Handle primitives - - const parameters = this.parameters; - - if ( parameters !== undefined ) { - - const values = []; - - for ( const key in parameters ) { - - values.push( parameters[ key ] ); - - } - - const geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; - - } - - return new this.constructor().copy( this ); - */ - - return new BufferGeometry().copy( this ); - - }, - - copy: function ( source ) { - - // reset - - this.index = null; - this.attributes = {}; - this.morphAttributes = {}; - this.groups = []; - this.boundingBox = null; - this.boundingSphere = null; - - // used for storing cloned, shared data - - const data = {}; - - // name - - this.name = source.name; - - // index - - const index = source.index; - - if ( index !== null ) { - - this.setIndex( index.clone( data ) ); - - } - - // attributes - - const attributes = source.attributes; - - for ( const name in attributes ) { - - const attribute = attributes[ name ]; - this.setAttribute( name, attribute.clone( data ) ); - - } - - // morph attributes - - const morphAttributes = source.morphAttributes; - - for ( const name in morphAttributes ) { - - const array = []; - const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes - - for ( let i = 0, l = morphAttribute.length; i < l; i ++ ) { - - array.push( morphAttribute[ i ].clone( data ) ); - - } - - this.morphAttributes[ name ] = array; - - } - - this.morphTargetsRelative = source.morphTargetsRelative; - - // groups - - const groups = source.groups; - - for ( let i = 0, l = groups.length; i < l; i ++ ) { - - const group = groups[ i ]; - this.addGroup( group.start, group.count, group.materialIndex ); - - } - - // bounding box - - const boundingBox = source.boundingBox; - - if ( boundingBox !== null ) { - - this.boundingBox = boundingBox.clone(); - - } - - // bounding sphere - - const boundingSphere = source.boundingSphere; - - if ( boundingSphere !== null ) { - - this.boundingSphere = boundingSphere.clone(); - - } - - // draw range - - this.drawRange.start = source.drawRange.start; - this.drawRange.count = source.drawRange.count; - - // user data - - this.userData = source.userData; - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - const _inverseMatrix = new Matrix4(); - const _ray = new Ray(); - const _sphere = new Sphere(); - - const _vA = new Vector3(); - const _vB = new Vector3(); - const _vC = new Vector3(); - - const _tempA = new Vector3(); - const _tempB = new Vector3(); - const _tempC = new Vector3(); - - const _morphA = new Vector3(); - const _morphB = new Vector3(); - const _morphC = new Vector3(); - - const _uvA = new Vector2(); - const _uvB = new Vector2(); - const _uvC = new Vector2(); - - const _intersectionPoint = new Vector3(); - const _intersectionPointWorld = new Vector3(); - - function Mesh( geometry, material ) { - - Object3D.call( this ); - - this.type = 'Mesh'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new MeshBasicMaterial(); - - this.updateMorphTargets(); - - } - - Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Mesh, - - isMesh: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - if ( source.morphTargetInfluences !== undefined ) { - - this.morphTargetInfluences = source.morphTargetInfluences.slice(); - - } - - if ( source.morphTargetDictionary !== undefined ) { - - this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); - - } - - this.material = source.material; - this.geometry = source.geometry; - - return this; - - }, - - updateMorphTargets: function () { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); - - if ( keys.length > 0 ) { - - const morphAttribute = morphAttributes[ keys[ 0 ] ]; - - if ( morphAttribute !== undefined ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - - const name = morphAttribute[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } - - } - - } - - } else { - - const morphTargets = geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - console.error( 'THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - }, - - raycast: function ( raycaster, intersects ) { - - const geometry = this.geometry; - const material = this.material; - const matrixWorld = this.matrixWorld; - - if ( material === undefined ) return; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _sphere.copy( geometry.boundingSphere ); - _sphere.applyMatrix4( matrixWorld ); - - if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; - - // - - _inverseMatrix.getInverse( matrixWorld ); - _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); - - // Check boundingBox before continuing - - if ( geometry.boundingBox !== null ) { - - if ( _ray.intersectsBox( geometry.boundingBox ) === false ) return; - - } - - let intersection; - - if ( geometry.isBufferGeometry ) { - - const index = geometry.index; - const position = geometry.attributes.position; - const morphPosition = geometry.morphAttributes.position; - const morphTargetsRelative = geometry.morphTargetsRelative; - const uv = geometry.attributes.uv; - const uv2 = geometry.attributes.uv2; - const groups = geometry.groups; - const drawRange = geometry.drawRange; - - if ( index !== null ) { - - // indexed buffer geometry - - if ( Array.isArray( material ) ) { - - for ( let i = 0, il = groups.length; i < il; i ++ ) { - - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; - - const start = Math.max( group.start, drawRange.start ); - const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); - - for ( let j = start, jl = end; j < jl; j += 3 ) { - - const a = index.getX( j ); - const b = index.getX( j + 1 ); - const c = index.getX( j + 2 ); - - intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics - intersection.face.materialIndex = group.materialIndex; - intersects.push( intersection ); - - } - - } - - } - - } else { - - const start = Math.max( 0, drawRange.start ); - const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - - for ( let i = start, il = end; i < il; i += 3 ) { - - const a = index.getX( i ); - const b = index.getX( i + 1 ); - const c = index.getX( i + 2 ); - - intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics - intersects.push( intersection ); - - } - - } - - } - - } else if ( position !== undefined ) { - - // non-indexed buffer geometry - - if ( Array.isArray( material ) ) { - - for ( let i = 0, il = groups.length; i < il; i ++ ) { - - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; - - const start = Math.max( group.start, drawRange.start ); - const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); - - for ( let j = start, jl = end; j < jl; j += 3 ) { - - const a = j; - const b = j + 1; - const c = j + 2; - - intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics - intersection.face.materialIndex = group.materialIndex; - intersects.push( intersection ); - - } - - } - - } - - } else { - - const start = Math.max( 0, drawRange.start ); - const end = Math.min( position.count, ( drawRange.start + drawRange.count ) ); - - for ( let i = start, il = end; i < il; i += 3 ) { - - const a = i; - const b = i + 1; - const c = i + 2; - - intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics - intersects.push( intersection ); - - } - - } - - } - - } - - } else if ( geometry.isGeometry ) { - - const isMultiMaterial = Array.isArray( material ); - - const vertices = geometry.vertices; - const faces = geometry.faces; - let uvs; - - const faceVertexUvs = geometry.faceVertexUvs[ 0 ]; - if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; - - for ( let f = 0, fl = faces.length; f < fl; f ++ ) { - - const face = faces[ f ]; - const faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material; - - if ( faceMaterial === undefined ) continue; - - const fvA = vertices[ face.a ]; - const fvB = vertices[ face.b ]; - const fvC = vertices[ face.c ]; - - intersection = checkIntersection( this, faceMaterial, raycaster, _ray, fvA, fvB, fvC, _intersectionPoint ); - - if ( intersection ) { - - if ( uvs && uvs[ f ] ) { - - const uvs_f = uvs[ f ]; - _uvA.copy( uvs_f[ 0 ] ); - _uvB.copy( uvs_f[ 1 ] ); - _uvC.copy( uvs_f[ 2 ] ); - - intersection.uv = Triangle.getUV( _intersectionPoint, fvA, fvB, fvC, _uvA, _uvB, _uvC, new Vector2() ); - - } - - intersection.face = face; - intersection.faceIndex = f; - intersects.push( intersection ); - - } - - } - - } - - } - - } ); - - function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { - - let intersect; - - if ( material.side === BackSide ) { - - intersect = ray.intersectTriangle( pC, pB, pA, true, point ); - - } else { - - intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point ); - - } - - if ( intersect === null ) return null; - - _intersectionPointWorld.copy( point ); - _intersectionPointWorld.applyMatrix4( object.matrixWorld ); - - const distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld ); - - if ( distance < raycaster.near || distance > raycaster.far ) return null; - - return { - distance: distance, - point: _intersectionPointWorld.clone(), - object: object - }; - - } - - function checkBufferGeometryIntersection( object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ) { - - _vA.fromBufferAttribute( position, a ); - _vB.fromBufferAttribute( position, b ); - _vC.fromBufferAttribute( position, c ); - - const morphInfluences = object.morphTargetInfluences; - - if ( material.morphTargets && morphPosition && morphInfluences ) { - - _morphA.set( 0, 0, 0 ); - _morphB.set( 0, 0, 0 ); - _morphC.set( 0, 0, 0 ); - - for ( let i = 0, il = morphPosition.length; i < il; i ++ ) { - - const influence = morphInfluences[ i ]; - const morphAttribute = morphPosition[ i ]; - - if ( influence === 0 ) continue; - - _tempA.fromBufferAttribute( morphAttribute, a ); - _tempB.fromBufferAttribute( morphAttribute, b ); - _tempC.fromBufferAttribute( morphAttribute, c ); - - if ( morphTargetsRelative ) { - - _morphA.addScaledVector( _tempA, influence ); - _morphB.addScaledVector( _tempB, influence ); - _morphC.addScaledVector( _tempC, influence ); - - } else { - - _morphA.addScaledVector( _tempA.sub( _vA ), influence ); - _morphB.addScaledVector( _tempB.sub( _vB ), influence ); - _morphC.addScaledVector( _tempC.sub( _vC ), influence ); - - } - - } - - _vA.add( _morphA ); - _vB.add( _morphB ); - _vC.add( _morphC ); - - } - - if ( object.isSkinnedMesh ) { - - object.boneTransform( a, _vA ); - object.boneTransform( b, _vB ); - object.boneTransform( c, _vC ); - - } - - const intersection = checkIntersection( object, material, raycaster, ray, _vA, _vB, _vC, _intersectionPoint ); - - if ( intersection ) { - - if ( uv ) { - - _uvA.fromBufferAttribute( uv, a ); - _uvB.fromBufferAttribute( uv, b ); - _uvC.fromBufferAttribute( uv, c ); - - intersection.uv = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ); - - } - - if ( uv2 ) { - - _uvA.fromBufferAttribute( uv2, a ); - _uvB.fromBufferAttribute( uv2, b ); - _uvC.fromBufferAttribute( uv2, c ); - - intersection.uv2 = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ); - - } - - const face = new Face3( a, b, c ); - Triangle.getNormal( _vA, _vB, _vC, face.normal ); - - intersection.face = face; - - } - - return intersection; - - } - - let _geometryId = 0; // Geometry uses even numbers as Id - const _m1$3 = new Matrix4(); - const _obj$1 = new Object3D(); - const _offset$1 = new Vector3(); - - function Geometry() { - - Object.defineProperty( this, 'id', { value: _geometryId += 2 } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - this.type = 'Geometry'; - - this.vertices = []; - this.colors = []; - this.faces = []; - this.faceVertexUvs = [[]]; - - this.morphTargets = []; - this.morphNormals = []; - - this.skinWeights = []; - this.skinIndices = []; - - this.lineDistances = []; - - this.boundingBox = null; - this.boundingSphere = null; - - // update flags - - this.elementsNeedUpdate = false; - this.verticesNeedUpdate = false; - this.uvsNeedUpdate = false; - this.normalsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.lineDistancesNeedUpdate = false; - this.groupsNeedUpdate = false; - - } - - Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: Geometry, - - isGeometry: true, - - applyMatrix4: function ( matrix ) { - - const normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - for ( let i = 0, il = this.vertices.length; i < il; i ++ ) { - - const vertex = this.vertices[ i ]; - vertex.applyMatrix4( matrix ); - - } - - for ( let i = 0, il = this.faces.length; i < il; i ++ ) { - - const face = this.faces[ i ]; - face.normal.applyMatrix3( normalMatrix ).normalize(); - - for ( let j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { - - face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); - - } - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - this.verticesNeedUpdate = true; - this.normalsNeedUpdate = true; - - return this; - - }, - - rotateX: function ( angle ) { - - // rotate geometry around world x-axis - - _m1$3.makeRotationX( angle ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - rotateY: function ( angle ) { - - // rotate geometry around world y-axis - - _m1$3.makeRotationY( angle ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - rotateZ: function ( angle ) { - - // rotate geometry around world z-axis - - _m1$3.makeRotationZ( angle ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - translate: function ( x, y, z ) { - - // translate geometry - - _m1$3.makeTranslation( x, y, z ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - scale: function ( x, y, z ) { - - // scale geometry - - _m1$3.makeScale( x, y, z ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - lookAt: function ( vector ) { - - _obj$1.lookAt( vector ); - - _obj$1.updateMatrix(); - - this.applyMatrix4( _obj$1.matrix ); - - return this; - - }, - - fromBufferGeometry: function ( geometry ) { - - const scope = this; - - const index = geometry.index !== null ? geometry.index : undefined; - const attributes = geometry.attributes; - - if ( attributes.position === undefined ) { - - console.error( 'THREE.Geometry.fromBufferGeometry(): Position attribute required for conversion.' ); - return this; - - } - - const position = attributes.position; - const normal = attributes.normal; - const color = attributes.color; - const uv = attributes.uv; - const uv2 = attributes.uv2; - - if ( uv2 !== undefined ) this.faceVertexUvs[ 1 ] = []; - - for ( let i = 0; i < position.count; i ++ ) { - - scope.vertices.push( new Vector3().fromBufferAttribute( position, i ) ); - - if ( color !== undefined ) { - - scope.colors.push( new Color().fromBufferAttribute( color, i ) ); - - } - - } - - function addFace( a, b, c, materialIndex ) { - - const vertexColors = ( color === undefined ) ? [] : [ - scope.colors[ a ].clone(), - scope.colors[ b ].clone(), - scope.colors[ c ].clone() - ]; - - const vertexNormals = ( normal === undefined ) ? [] : [ - new Vector3().fromBufferAttribute( normal, a ), - new Vector3().fromBufferAttribute( normal, b ), - new Vector3().fromBufferAttribute( normal, c ) - ]; - - const face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); - - scope.faces.push( face ); - - if ( uv !== undefined ) { - - scope.faceVertexUvs[ 0 ].push( [ - new Vector2().fromBufferAttribute( uv, a ), - new Vector2().fromBufferAttribute( uv, b ), - new Vector2().fromBufferAttribute( uv, c ) - ] ); - - } - - if ( uv2 !== undefined ) { - - scope.faceVertexUvs[ 1 ].push( [ - new Vector2().fromBufferAttribute( uv2, a ), - new Vector2().fromBufferAttribute( uv2, b ), - new Vector2().fromBufferAttribute( uv2, c ) - ] ); - - } - - } - - const groups = geometry.groups; - - if ( groups.length > 0 ) { - - for ( let i = 0; i < groups.length; i ++ ) { - - const group = groups[ i ]; - - const start = group.start; - const count = group.count; - - for ( let j = start, jl = start + count; j < jl; j += 3 ) { - - if ( index !== undefined ) { - - addFace( index.getX( j ), index.getX( j + 1 ), index.getX( j + 2 ), group.materialIndex ); - - } else { - - addFace( j, j + 1, j + 2, group.materialIndex ); - - } - - } - - } - - } else { - - if ( index !== undefined ) { - - for ( let i = 0; i < index.count; i += 3 ) { - - addFace( index.getX( i ), index.getX( i + 1 ), index.getX( i + 2 ) ); - - } - - } else { - - for ( let i = 0; i < position.count; i += 3 ) { - - addFace( i, i + 1, i + 2 ); - - } - - } - - } - - this.computeFaceNormals(); - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - return this; - - }, - - center: function () { - - this.computeBoundingBox(); - - this.boundingBox.getCenter( _offset$1 ).negate(); - - this.translate( _offset$1.x, _offset$1.y, _offset$1.z ); - - return this; - - }, - - normalize: function () { - - this.computeBoundingSphere(); - - const center = this.boundingSphere.center; - const radius = this.boundingSphere.radius; - - const s = radius === 0 ? 1 : 1.0 / radius; - - const matrix = new Matrix4(); - matrix.set( - s, 0, 0, - s * center.x, - 0, s, 0, - s * center.y, - 0, 0, s, - s * center.z, - 0, 0, 0, 1 - ); - - this.applyMatrix4( matrix ); - - return this; - - }, - - computeFaceNormals: function () { - - const cb = new Vector3(), ab = new Vector3(); - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - const vA = this.vertices[ face.a ]; - const vB = this.vertices[ face.b ]; - const vC = this.vertices[ face.c ]; - - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); - - cb.normalize(); - - face.normal.copy( cb ); - - } - - }, - - computeVertexNormals: function ( areaWeighted ) { - - if ( areaWeighted === undefined ) areaWeighted = true; - - const vertices = new Array( this.vertices.length ); - - for ( let v = 0, vl = this.vertices.length; v < vl; v ++ ) { - - vertices[ v ] = new Vector3(); - - } - - if ( areaWeighted ) { - - // vertex normals weighted by triangle areas - // http://www.iquilezles.org/www/articles/normals/normals.htm - - const cb = new Vector3(), ab = new Vector3(); - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - const vA = this.vertices[ face.a ]; - const vB = this.vertices[ face.b ]; - const vC = this.vertices[ face.c ]; - - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); - - vertices[ face.a ].add( cb ); - vertices[ face.b ].add( cb ); - vertices[ face.c ].add( cb ); - - } - - } else { - - this.computeFaceNormals(); - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - vertices[ face.a ].add( face.normal ); - vertices[ face.b ].add( face.normal ); - vertices[ face.c ].add( face.normal ); - - } - - } - - for ( let v = 0, vl = this.vertices.length; v < vl; v ++ ) { - - vertices[ v ].normalize(); - - } - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - const vertexNormals = face.vertexNormals; - - if ( vertexNormals.length === 3 ) { - - vertexNormals[ 0 ].copy( vertices[ face.a ] ); - vertexNormals[ 1 ].copy( vertices[ face.b ] ); - vertexNormals[ 2 ].copy( vertices[ face.c ] ); - - } else { - - vertexNormals[ 0 ] = vertices[ face.a ].clone(); - vertexNormals[ 1 ] = vertices[ face.b ].clone(); - vertexNormals[ 2 ] = vertices[ face.c ].clone(); - - } - - } - - if ( this.faces.length > 0 ) { - - this.normalsNeedUpdate = true; - - } - - }, - - computeFlatVertexNormals: function () { - - this.computeFaceNormals(); - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - const vertexNormals = face.vertexNormals; - - if ( vertexNormals.length === 3 ) { - - vertexNormals[ 0 ].copy( face.normal ); - vertexNormals[ 1 ].copy( face.normal ); - vertexNormals[ 2 ].copy( face.normal ); - - } else { - - vertexNormals[ 0 ] = face.normal.clone(); - vertexNormals[ 1 ] = face.normal.clone(); - vertexNormals[ 2 ] = face.normal.clone(); - - } - - } - - if ( this.faces.length > 0 ) { - - this.normalsNeedUpdate = true; - - } - - }, - - computeMorphNormals: function () { - - // save original normals - // - create temp variables on first access - // otherwise just copy (for faster repeated calls) - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - if ( ! face.__originalFaceNormal ) { - - face.__originalFaceNormal = face.normal.clone(); - - } else { - - face.__originalFaceNormal.copy( face.normal ); - - } - - if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; - - for ( let i = 0, il = face.vertexNormals.length; i < il; i ++ ) { - - if ( ! face.__originalVertexNormals[ i ] ) { - - face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); - - } else { - - face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); - - } - - } - - } - - // use temp geometry to compute face and vertex normals for each morph - - const tmpGeo = new Geometry(); - tmpGeo.faces = this.faces; - - for ( let i = 0, il = this.morphTargets.length; i < il; i ++ ) { - - // create on first access - - if ( ! this.morphNormals[ i ] ) { - - this.morphNormals[ i ] = {}; - this.morphNormals[ i ].faceNormals = []; - this.morphNormals[ i ].vertexNormals = []; - - const dstNormalsFace = this.morphNormals[ i ].faceNormals; - const dstNormalsVertex = this.morphNormals[ i ].vertexNormals; - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const faceNormal = new Vector3(); - const vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() }; - - dstNormalsFace.push( faceNormal ); - dstNormalsVertex.push( vertexNormals ); - - } - - } - - const morphNormals = this.morphNormals[ i ]; - - // set vertices to morph target - - tmpGeo.vertices = this.morphTargets[ i ].vertices; - - // compute morph normals - - tmpGeo.computeFaceNormals(); - tmpGeo.computeVertexNormals(); - - // store morph normals - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - const faceNormal = morphNormals.faceNormals[ f ]; - const vertexNormals = morphNormals.vertexNormals[ f ]; - - faceNormal.copy( face.normal ); - - vertexNormals.a.copy( face.vertexNormals[ 0 ] ); - vertexNormals.b.copy( face.vertexNormals[ 1 ] ); - vertexNormals.c.copy( face.vertexNormals[ 2 ] ); - - } - - } - - // restore original normals - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - face.normal = face.__originalFaceNormal; - face.vertexNormals = face.__originalVertexNormals; - - } - - }, - - computeBoundingBox: function () { - - if ( this.boundingBox === null ) { - - this.boundingBox = new Box3(); - - } - - this.boundingBox.setFromPoints( this.vertices ); - - }, - - computeBoundingSphere: function () { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new Sphere(); - - } - - this.boundingSphere.setFromPoints( this.vertices ); - - }, - - merge: function ( geometry, matrix, materialIndexOffset ) { - - if ( ! ( geometry && geometry.isGeometry ) ) { - - console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); - return; - - } - - let normalMatrix; - const vertexOffset = this.vertices.length, - vertices1 = this.vertices, - vertices2 = geometry.vertices, - faces1 = this.faces, - faces2 = geometry.faces, - colors1 = this.colors, - colors2 = geometry.colors; - - if ( materialIndexOffset === undefined ) materialIndexOffset = 0; - - if ( matrix !== undefined ) { - - normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - } - - // vertices - - for ( let i = 0, il = vertices2.length; i < il; i ++ ) { - - const vertex = vertices2[ i ]; - - const vertexCopy = vertex.clone(); - - if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); - - vertices1.push( vertexCopy ); - - } - - // colors - - for ( let i = 0, il = colors2.length; i < il; i ++ ) { - - colors1.push( colors2[ i ].clone() ); - - } - - // faces - - for ( let i = 0, il = faces2.length; i < il; i ++ ) { - - const face = faces2[ i ]; - let normal, color; - const faceVertexNormals = face.vertexNormals, - faceVertexColors = face.vertexColors; - - const faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); - faceCopy.normal.copy( face.normal ); - - if ( normalMatrix !== undefined ) { - - faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); - - } - - for ( let j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { - - normal = faceVertexNormals[ j ].clone(); - - if ( normalMatrix !== undefined ) { - - normal.applyMatrix3( normalMatrix ).normalize(); - - } - - faceCopy.vertexNormals.push( normal ); - - } - - faceCopy.color.copy( face.color ); - - for ( let j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { - - color = faceVertexColors[ j ]; - faceCopy.vertexColors.push( color.clone() ); - - } - - faceCopy.materialIndex = face.materialIndex + materialIndexOffset; - - faces1.push( faceCopy ); - - } - - // uvs - - for ( let i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { - - const faceVertexUvs2 = geometry.faceVertexUvs[ i ]; - - if ( this.faceVertexUvs[ i ] === undefined ) this.faceVertexUvs[ i ] = []; - - for ( let j = 0, jl = faceVertexUvs2.length; j < jl; j ++ ) { - - const uvs2 = faceVertexUvs2[ j ], uvsCopy = []; - - for ( let k = 0, kl = uvs2.length; k < kl; k ++ ) { - - uvsCopy.push( uvs2[ k ].clone() ); - - } - - this.faceVertexUvs[ i ].push( uvsCopy ); - - } - - } - - }, - - mergeMesh: function ( mesh ) { - - if ( ! ( mesh && mesh.isMesh ) ) { - - console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); - return; - - } - - if ( mesh.matrixAutoUpdate ) mesh.updateMatrix(); - - this.merge( mesh.geometry, mesh.matrix ); - - }, - - /* - * Checks for duplicate vertices with hashmap. - * Duplicated vertices are removed - * and faces' vertices are updated. - */ - - mergeVertices: function () { - - const verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) - const unique = [], changes = []; - - const precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 - const precision = Math.pow( 10, precisionPoints ); - - for ( let i = 0, il = this.vertices.length; i < il; i ++ ) { - - const v = this.vertices[ i ]; - const key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); - - if ( verticesMap[ key ] === undefined ) { - - verticesMap[ key ] = i; - unique.push( this.vertices[ i ] ); - changes[ i ] = unique.length - 1; - - } else { - - //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); - changes[ i ] = changes[ verticesMap[ key ] ]; - - } - - } - - - // if faces are completely degenerate after merging vertices, we - // have to remove them from the geometry. - const faceIndicesToRemove = []; - - for ( let i = 0, il = this.faces.length; i < il; i ++ ) { - - const face = this.faces[ i ]; - - face.a = changes[ face.a ]; - face.b = changes[ face.b ]; - face.c = changes[ face.c ]; - - const indices = [ face.a, face.b, face.c ]; - - // if any duplicate vertices are found in a Face3 - // we have to remove the face as nothing can be saved - for ( let n = 0; n < 3; n ++ ) { - - if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { - - faceIndicesToRemove.push( i ); - break; - - } - - } - - } - - for ( let i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { - - const idx = faceIndicesToRemove[ i ]; - - this.faces.splice( idx, 1 ); - - for ( let j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { - - this.faceVertexUvs[ j ].splice( idx, 1 ); - - } - - } - - // Use unique set of vertices - - const diff = this.vertices.length - unique.length; - this.vertices = unique; - return diff; - - }, - - setFromPoints: function ( points ) { - - this.vertices = []; - - for ( let i = 0, l = points.length; i < l; i ++ ) { - - const point = points[ i ]; - this.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); - - } - - return this; - - }, - - sortFacesByMaterialIndex: function () { - - const faces = this.faces; - const length = faces.length; - - // tag faces - - for ( let i = 0; i < length; i ++ ) { - - faces[ i ]._id = i; - - } - - // sort faces - - function materialIndexSort( a, b ) { - - return a.materialIndex - b.materialIndex; - - } - - faces.sort( materialIndexSort ); - - // sort uvs - - const uvs1 = this.faceVertexUvs[ 0 ]; - const uvs2 = this.faceVertexUvs[ 1 ]; - - let newUvs1, newUvs2; - - if ( uvs1 && uvs1.length === length ) newUvs1 = []; - if ( uvs2 && uvs2.length === length ) newUvs2 = []; - - for ( let i = 0; i < length; i ++ ) { - - const id = faces[ i ]._id; - - if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); - if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); - - } - - if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; - if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; - - }, - - toJSON: function () { - - const data = { - metadata: { - version: 4.5, - type: 'Geometry', - generator: 'Geometry.toJSON' - } - }; - - // standard Geometry serialization - - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) data.name = this.name; - - if ( this.parameters !== undefined ) { - - const parameters = this.parameters; - - for ( const key in parameters ) { - - if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; - - } - - return data; - - } - - const vertices = []; - - for ( let i = 0; i < this.vertices.length; i ++ ) { - - const vertex = this.vertices[ i ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - const faces = []; - const normals = []; - const normalsHash = {}; - const colors = []; - const colorsHash = {}; - const uvs = []; - const uvsHash = {}; - - for ( let i = 0; i < this.faces.length; i ++ ) { - - const face = this.faces[ i ]; - - const hasMaterial = true; - const hasFaceUv = false; // deprecated - const hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; - const hasFaceNormal = face.normal.length() > 0; - const hasFaceVertexNormal = face.vertexNormals.length > 0; - const hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; - const hasFaceVertexColor = face.vertexColors.length > 0; - - let faceType = 0; - - faceType = setBit( faceType, 0, 0 ); // isQuad - faceType = setBit( faceType, 1, hasMaterial ); - faceType = setBit( faceType, 2, hasFaceUv ); - faceType = setBit( faceType, 3, hasFaceVertexUv ); - faceType = setBit( faceType, 4, hasFaceNormal ); - faceType = setBit( faceType, 5, hasFaceVertexNormal ); - faceType = setBit( faceType, 6, hasFaceColor ); - faceType = setBit( faceType, 7, hasFaceVertexColor ); - - faces.push( faceType ); - faces.push( face.a, face.b, face.c ); - faces.push( face.materialIndex ); - - if ( hasFaceVertexUv ) { - - const faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; - - faces.push( - getUvIndex( faceVertexUvs[ 0 ] ), - getUvIndex( faceVertexUvs[ 1 ] ), - getUvIndex( faceVertexUvs[ 2 ] ) - ); - - } - - if ( hasFaceNormal ) { - - faces.push( getNormalIndex( face.normal ) ); - - } - - if ( hasFaceVertexNormal ) { - - const vertexNormals = face.vertexNormals; - - faces.push( - getNormalIndex( vertexNormals[ 0 ] ), - getNormalIndex( vertexNormals[ 1 ] ), - getNormalIndex( vertexNormals[ 2 ] ) - ); - - } - - if ( hasFaceColor ) { - - faces.push( getColorIndex( face.color ) ); - - } - - if ( hasFaceVertexColor ) { - - const vertexColors = face.vertexColors; - - faces.push( - getColorIndex( vertexColors[ 0 ] ), - getColorIndex( vertexColors[ 1 ] ), - getColorIndex( vertexColors[ 2 ] ) - ); - - } - - } - - function setBit( value, position, enabled ) { - - return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); - - } - - function getNormalIndex( normal ) { - - const hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); - - if ( normalsHash[ hash ] !== undefined ) { - - return normalsHash[ hash ]; - - } - - normalsHash[ hash ] = normals.length / 3; - normals.push( normal.x, normal.y, normal.z ); - - return normalsHash[ hash ]; - - } - - function getColorIndex( color ) { - - const hash = color.r.toString() + color.g.toString() + color.b.toString(); - - if ( colorsHash[ hash ] !== undefined ) { - - return colorsHash[ hash ]; - - } - - colorsHash[ hash ] = colors.length; - colors.push( color.getHex() ); - - return colorsHash[ hash ]; - - } - - function getUvIndex( uv ) { - - const hash = uv.x.toString() + uv.y.toString(); - - if ( uvsHash[ hash ] !== undefined ) { - - return uvsHash[ hash ]; - - } - - uvsHash[ hash ] = uvs.length / 2; - uvs.push( uv.x, uv.y ); - - return uvsHash[ hash ]; - - } - - data.data = {}; - - data.data.vertices = vertices; - data.data.normals = normals; - if ( colors.length > 0 ) data.data.colors = colors; - if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility - data.data.faces = faces; - - return data; - - }, - - clone: function () { - - /* - // Handle primitives - - const parameters = this.parameters; - - if ( parameters !== undefined ) { - - const values = []; - - for ( const key in parameters ) { - - values.push( parameters[ key ] ); - - } - - const geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; - - } - - return new this.constructor().copy( this ); - */ - - return new Geometry().copy( this ); - - }, - - copy: function ( source ) { - - // reset - - this.vertices = []; - this.colors = []; - this.faces = []; - this.faceVertexUvs = [[]]; - this.morphTargets = []; - this.morphNormals = []; - this.skinWeights = []; - this.skinIndices = []; - this.lineDistances = []; - this.boundingBox = null; - this.boundingSphere = null; - - // name - - this.name = source.name; - - // vertices - - const vertices = source.vertices; - - for ( let i = 0, il = vertices.length; i < il; i ++ ) { - - this.vertices.push( vertices[ i ].clone() ); - - } - - // colors - - const colors = source.colors; - - for ( let i = 0, il = colors.length; i < il; i ++ ) { - - this.colors.push( colors[ i ].clone() ); - - } - - // faces - - const faces = source.faces; - - for ( let i = 0, il = faces.length; i < il; i ++ ) { - - this.faces.push( faces[ i ].clone() ); - - } - - // face vertex uvs - - for ( let i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { - - const faceVertexUvs = source.faceVertexUvs[ i ]; - - if ( this.faceVertexUvs[ i ] === undefined ) { - - this.faceVertexUvs[ i ] = []; - - } - - for ( let j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { - - const uvs = faceVertexUvs[ j ], uvsCopy = []; - - for ( let k = 0, kl = uvs.length; k < kl; k ++ ) { - - const uv = uvs[ k ]; - - uvsCopy.push( uv.clone() ); - - } - - this.faceVertexUvs[ i ].push( uvsCopy ); - - } - - } - - // morph targets - - const morphTargets = source.morphTargets; - - for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { - - const morphTarget = {}; - morphTarget.name = morphTargets[ i ].name; - - // vertices - - if ( morphTargets[ i ].vertices !== undefined ) { - - morphTarget.vertices = []; - - for ( let j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) { - - morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() ); - - } - - } - - // normals - - if ( morphTargets[ i ].normals !== undefined ) { - - morphTarget.normals = []; - - for ( let j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) { - - morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() ); - - } - - } - - this.morphTargets.push( morphTarget ); - - } - - // morph normals - - const morphNormals = source.morphNormals; - - for ( let i = 0, il = morphNormals.length; i < il; i ++ ) { - - const morphNormal = {}; - - // vertex normals - - if ( morphNormals[ i ].vertexNormals !== undefined ) { - - morphNormal.vertexNormals = []; - - for ( let j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) { - - const srcVertexNormal = morphNormals[ i ].vertexNormals[ j ]; - const destVertexNormal = {}; - - destVertexNormal.a = srcVertexNormal.a.clone(); - destVertexNormal.b = srcVertexNormal.b.clone(); - destVertexNormal.c = srcVertexNormal.c.clone(); - - morphNormal.vertexNormals.push( destVertexNormal ); - - } - - } - - // face normals - - if ( morphNormals[ i ].faceNormals !== undefined ) { - - morphNormal.faceNormals = []; - - for ( let j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) { - - morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() ); - - } - - } - - this.morphNormals.push( morphNormal ); - - } - - // skin weights - - const skinWeights = source.skinWeights; - - for ( let i = 0, il = skinWeights.length; i < il; i ++ ) { - - this.skinWeights.push( skinWeights[ i ].clone() ); - - } - - // skin indices - - const skinIndices = source.skinIndices; - - for ( let i = 0, il = skinIndices.length; i < il; i ++ ) { - - this.skinIndices.push( skinIndices[ i ].clone() ); - - } - - // line distances - - const lineDistances = source.lineDistances; - - for ( let i = 0, il = lineDistances.length; i < il; i ++ ) { - - this.lineDistances.push( lineDistances[ i ] ); - - } - - // bounding box - - const boundingBox = source.boundingBox; - - if ( boundingBox !== null ) { - - this.boundingBox = boundingBox.clone(); - - } - - // bounding sphere - - const boundingSphere = source.boundingSphere; - - if ( boundingSphere !== null ) { - - this.boundingSphere = boundingSphere.clone(); - - } - - // update flags - - this.elementsNeedUpdate = source.elementsNeedUpdate; - this.verticesNeedUpdate = source.verticesNeedUpdate; - this.uvsNeedUpdate = source.uvsNeedUpdate; - this.normalsNeedUpdate = source.normalsNeedUpdate; - this.colorsNeedUpdate = source.colorsNeedUpdate; - this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate; - this.groupsNeedUpdate = source.groupsNeedUpdate; - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - // BoxGeometry - - class BoxGeometry extends Geometry { - - constructor( width, height, depth, widthSegments, heightSegments, depthSegments ) { - - super(); - - this.type = 'BoxGeometry'; - - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; - - this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); - this.mergeVertices(); - - } - - } - - // BoxBufferGeometry - - class BoxBufferGeometry extends BufferGeometry { - - constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) { - - super(); - - this.type = 'BoxBufferGeometry'; - - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; - - const scope = this; - - // segments - - widthSegments = Math.floor( widthSegments ); - heightSegments = Math.floor( heightSegments ); - depthSegments = Math.floor( depthSegments ); - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - let numberOfVertices = 0; - let groupStart = 0; - - // build each side of the box geometry - - buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px - buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx - buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py - buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny - buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz - buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { - - const segmentWidth = width / gridX; - const segmentHeight = height / gridY; - - const widthHalf = width / 2; - const heightHalf = height / 2; - const depthHalf = depth / 2; - - const gridX1 = gridX + 1; - const gridY1 = gridY + 1; - - let vertexCounter = 0; - let groupCount = 0; - - const vector = new Vector3(); - - // generate vertices, normals and uvs - - for ( let iy = 0; iy < gridY1; iy ++ ) { - - const y = iy * segmentHeight - heightHalf; - - for ( let ix = 0; ix < gridX1; ix ++ ) { - - const x = ix * segmentWidth - widthHalf; - - // set values to correct vector component - - vector[ u ] = x * udir; - vector[ v ] = y * vdir; - vector[ w ] = depthHalf; - - // now apply vector to vertex buffer - - vertices.push( vector.x, vector.y, vector.z ); - - // set values to correct vector component - - vector[ u ] = 0; - vector[ v ] = 0; - vector[ w ] = depth > 0 ? 1 : - 1; - - // now apply vector to normal buffer - - normals.push( vector.x, vector.y, vector.z ); - - // uvs - - uvs.push( ix / gridX ); - uvs.push( 1 - ( iy / gridY ) ); - - // counters - - vertexCounter += 1; - - } - - } - - // indices - - // 1. you need three indices to draw a single face - // 2. a single segment consists of two faces - // 3. so we need to generate six (2*3) indices per segment - - for ( let iy = 0; iy < gridY; iy ++ ) { - - for ( let ix = 0; ix < gridX; ix ++ ) { - - const a = numberOfVertices + ix + gridX1 * iy; - const b = numberOfVertices + ix + gridX1 * ( iy + 1 ); - const c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); - const d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - // increase counter - - groupCount += 6; - - } - - } - - // add a group to the geometry. this will ensure multi material support - - scope.addGroup( groupStart, groupCount, materialIndex ); - - // calculate new start value for groups - - groupStart += groupCount; - - // update total number of vertices - - numberOfVertices += vertexCounter; - - } - - } - - } - - /** - * Uniform Utilities - */ - - function cloneUniforms( src ) { - - const dst = {}; - - for ( const u in src ) { - - dst[ u ] = {}; - - for ( const p in src[ u ] ) { - - const property = src[ u ][ p ]; - - if ( property && ( property.isColor || - property.isMatrix3 || property.isMatrix4 || - property.isVector2 || property.isVector3 || property.isVector4 || - property.isTexture ) ) { - - dst[ u ][ p ] = property.clone(); - - } else if ( Array.isArray( property ) ) { - - dst[ u ][ p ] = property.slice(); - - } else { - - dst[ u ][ p ] = property; - - } - - } - - } - - return dst; - - } - - function mergeUniforms( uniforms ) { - - const merged = {}; - - for ( let u = 0; u < uniforms.length; u ++ ) { - - const tmp = cloneUniforms( uniforms[ u ] ); - - for ( const p in tmp ) { - - merged[ p ] = tmp[ p ]; - - } - - } - - return merged; - - } - - // Legacy - - const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; - - var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}"; - - var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}"; - - /** - * parameters = { - * defines: { "label" : "value" }, - * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, - * - * fragmentShader: , - * vertexShader: , - * - * wireframe: , - * wireframeLinewidth: , - * - * lights: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function ShaderMaterial( parameters ) { - - Material.call( this ); - - this.type = 'ShaderMaterial'; - - this.defines = {}; - this.uniforms = {}; - - this.vertexShader = default_vertex; - this.fragmentShader = default_fragment; - - this.linewidth = 1; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; // set to use scene fog - this.lights = false; // set to use scene lights - this.clipping = false; // set to use user-defined clipping planes - - this.skinning = false; // set to use skinning attribute streams - this.morphTargets = false; // set to use morph targets - this.morphNormals = false; // set to use morph normals - - this.extensions = { - derivatives: false, // set to use derivatives - fragDepth: false, // set to use fragment depth values - drawBuffers: false, // set to use draw buffers - shaderTextureLOD: false // set to use shader texture LOD - }; - - // When rendered geometry doesn't include these attributes but the material does, - // use these default values in WebGL. This avoids errors when buffer data is missing. - this.defaultAttributeValues = { - 'color': [ 1, 1, 1 ], - 'uv': [ 0, 0 ], - 'uv2': [ 0, 0 ] - }; - - this.index0AttributeName = undefined; - this.uniformsNeedUpdate = false; - - this.glslVersion = null; - - if ( parameters !== undefined ) { - - if ( parameters.attributes !== undefined ) { - - console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); - - } - - this.setValues( parameters ); - - } - - } - - ShaderMaterial.prototype = Object.create( Material.prototype ); - ShaderMaterial.prototype.constructor = ShaderMaterial; - - ShaderMaterial.prototype.isShaderMaterial = true; - - ShaderMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.fragmentShader = source.fragmentShader; - this.vertexShader = source.vertexShader; - - this.uniforms = cloneUniforms( source.uniforms ); - - this.defines = Object.assign( {}, source.defines ); - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - this.lights = source.lights; - this.clipping = source.clipping; - - this.skinning = source.skinning; - - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - this.extensions = Object.assign( {}, source.extensions ); - - this.glslVersion = source.glslVersion; - - return this; - - }; - - ShaderMaterial.prototype.toJSON = function ( meta ) { - - const data = Material.prototype.toJSON.call( this, meta ); - - data.glslVersion = this.glslVersion; - data.uniforms = {}; - - for ( const name in this.uniforms ) { - - const uniform = this.uniforms[ name ]; - const value = uniform.value; - - if ( value && value.isTexture ) { - - data.uniforms[ name ] = { - type: 't', - value: value.toJSON( meta ).uuid - }; - - } else if ( value && value.isColor ) { - - data.uniforms[ name ] = { - type: 'c', - value: value.getHex() - }; - - } else if ( value && value.isVector2 ) { - - data.uniforms[ name ] = { - type: 'v2', - value: value.toArray() - }; - - } else if ( value && value.isVector3 ) { - - data.uniforms[ name ] = { - type: 'v3', - value: value.toArray() - }; - - } else if ( value && value.isVector4 ) { - - data.uniforms[ name ] = { - type: 'v4', - value: value.toArray() - }; - - } else if ( value && value.isMatrix3 ) { - - data.uniforms[ name ] = { - type: 'm3', - value: value.toArray() - }; - - } else if ( value && value.isMatrix4 ) { - - data.uniforms[ name ] = { - type: 'm4', - value: value.toArray() - }; - - } else { - - data.uniforms[ name ] = { - value: value - }; - - // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far - - } - - } - - if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; - - data.vertexShader = this.vertexShader; - data.fragmentShader = this.fragmentShader; - - const extensions = {}; - - for ( const key in this.extensions ) { - - if ( this.extensions[ key ] === true ) extensions[ key ] = true; - - } - - if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; - - return data; - - }; - - function Camera() { - - Object3D.call( this ); - - this.type = 'Camera'; - - this.matrixWorldInverse = new Matrix4(); - - this.projectionMatrix = new Matrix4(); - this.projectionMatrixInverse = new Matrix4(); - - } - - Camera.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Camera, - - isCamera: true, - - copy: function ( source, recursive ) { - - Object3D.prototype.copy.call( this, source, recursive ); - - this.matrixWorldInverse.copy( source.matrixWorldInverse ); - - this.projectionMatrix.copy( source.projectionMatrix ); - this.projectionMatrixInverse.copy( source.projectionMatrixInverse ); - - return this; - - }, - - getWorldDirection: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Camera: .getWorldDirection() target is now required' ); - target = new Vector3(); - - } - - this.updateMatrixWorld( true ); - - const e = this.matrixWorld.elements; - - return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize(); - - }, - - updateMatrixWorld: function ( force ) { - - Object3D.prototype.updateMatrixWorld.call( this, force ); - - this.matrixWorldInverse.getInverse( this.matrixWorld ); - - }, - - updateWorldMatrix: function ( updateParents, updateChildren ) { - - Object3D.prototype.updateWorldMatrix.call( this, updateParents, updateChildren ); - - this.matrixWorldInverse.getInverse( this.matrixWorld ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - } - - } ); - - function PerspectiveCamera( fov, aspect, near, far ) { - - Camera.call( this ); - - this.type = 'PerspectiveCamera'; - - this.fov = fov !== undefined ? fov : 50; - this.zoom = 1; - - this.near = near !== undefined ? near : 0.1; - this.far = far !== undefined ? far : 2000; - this.focus = 10; - - this.aspect = aspect !== undefined ? aspect : 1; - this.view = null; - - this.filmGauge = 35; // width of the film (default in millimeters) - this.filmOffset = 0; // horizontal film offset (same unit as gauge) - - this.updateProjectionMatrix(); - - } - - PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), { - - constructor: PerspectiveCamera, - - isPerspectiveCamera: true, - - copy: function ( source, recursive ) { - - Camera.prototype.copy.call( this, source, recursive ); - - this.fov = source.fov; - this.zoom = source.zoom; - - this.near = source.near; - this.far = source.far; - this.focus = source.focus; - - this.aspect = source.aspect; - this.view = source.view === null ? null : Object.assign( {}, source.view ); - - this.filmGauge = source.filmGauge; - this.filmOffset = source.filmOffset; - - return this; - - }, - - /** - * Sets the FOV by focal length in respect to the current .filmGauge. - * - * The default film gauge is 35, so that the focal length can be specified for - * a 35mm (full frame) camera. - * - * Values for focal length and film gauge must have the same unit. - */ - setFocalLength: function ( focalLength ) { - - // see http://www.bobatkins.com/photography/technical/field_of_view.html - const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; - - this.fov = MathUtils.RAD2DEG * 2 * Math.atan( vExtentSlope ); - this.updateProjectionMatrix(); - - }, - - /** - * Calculates the focal length from the current .fov and .filmGauge. - */ - getFocalLength: function () { - - const vExtentSlope = Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ); - - return 0.5 * this.getFilmHeight() / vExtentSlope; - - }, - - getEffectiveFOV: function () { - - return MathUtils.RAD2DEG * 2 * Math.atan( - Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom ); - - }, - - getFilmWidth: function () { - - // film not completely covered in portrait format (aspect < 1) - return this.filmGauge * Math.min( this.aspect, 1 ); - - }, - - getFilmHeight: function () { - - // film not completely covered in landscape format (aspect > 1) - return this.filmGauge / Math.max( this.aspect, 1 ); - - }, - - /** - * Sets an offset in a larger frustum. This is useful for multi-window or - * multi-monitor/multi-machine setups. - * - * For example, if you have 3x2 monitors and each monitor is 1920x1080 and - * the monitors are in grid like this - * - * +---+---+---+ - * | A | B | C | - * +---+---+---+ - * | D | E | F | - * +---+---+---+ - * - * then for each monitor you would call it like this - * - * const w = 1920; - * const h = 1080; - * const fullWidth = w * 3; - * const fullHeight = h * 2; - * - * --A-- - * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); - * --B-- - * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); - * --C-- - * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); - * --D-- - * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); - * --E-- - * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); - * --F-- - * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); - * - * Note there is no reason monitors have to be the same size or in a grid. - */ - setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { - - this.aspect = fullWidth / fullHeight; - - if ( this.view === null ) { - - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; - - } - - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; - - this.updateProjectionMatrix(); - - }, - - clearViewOffset: function () { - - if ( this.view !== null ) { - - this.view.enabled = false; - - } - - this.updateProjectionMatrix(); - - }, - - updateProjectionMatrix: function () { - - const near = this.near; - let top = near * Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom; - let height = 2 * top; - let width = this.aspect * height; - let left = - 0.5 * width; - const view = this.view; - - if ( this.view !== null && this.view.enabled ) { - - const fullWidth = view.fullWidth, - fullHeight = view.fullHeight; - - left += view.offsetX * width / fullWidth; - top -= view.offsetY * height / fullHeight; - width *= view.width / fullWidth; - height *= view.height / fullHeight; - - } - - const skew = this.filmOffset; - if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); - - this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); - - this.projectionMatrixInverse.getInverse( this.projectionMatrix ); - - }, - - toJSON: function ( meta ) { - - const data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.fov = this.fov; - data.object.zoom = this.zoom; - - data.object.near = this.near; - data.object.far = this.far; - data.object.focus = this.focus; - - data.object.aspect = this.aspect; - - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - - data.object.filmGauge = this.filmGauge; - data.object.filmOffset = this.filmOffset; - - return data; - - } - - } ); - - const fov = 90, aspect = 1; - - function CubeCamera( near, far, renderTarget ) { - - Object3D.call( this ); - - this.type = 'CubeCamera'; - - if ( renderTarget.isWebGLCubeRenderTarget !== true ) { - - console.error( 'THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter.' ); - return; - - } - - this.renderTarget = renderTarget; - - const cameraPX = new PerspectiveCamera( fov, aspect, near, far ); - cameraPX.layers = this.layers; - cameraPX.up.set( 0, - 1, 0 ); - cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); - this.add( cameraPX ); - - const cameraNX = new PerspectiveCamera( fov, aspect, near, far ); - cameraNX.layers = this.layers; - cameraNX.up.set( 0, - 1, 0 ); - cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); - this.add( cameraNX ); - - const cameraPY = new PerspectiveCamera( fov, aspect, near, far ); - cameraPY.layers = this.layers; - cameraPY.up.set( 0, 0, 1 ); - cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); - this.add( cameraPY ); - - const cameraNY = new PerspectiveCamera( fov, aspect, near, far ); - cameraNY.layers = this.layers; - cameraNY.up.set( 0, 0, - 1 ); - cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); - this.add( cameraNY ); - - const cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraPZ.layers = this.layers; - cameraPZ.up.set( 0, - 1, 0 ); - cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); - this.add( cameraPZ ); - - const cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraNZ.layers = this.layers; - cameraNZ.up.set( 0, - 1, 0 ); - cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); - this.add( cameraNZ ); - - this.update = function ( renderer, scene ) { - - if ( this.parent === null ) this.updateMatrixWorld(); - - const currentXrEnabled = renderer.xr.enabled; - const currentRenderTarget = renderer.getRenderTarget(); - - renderer.xr.enabled = false; - - const generateMipmaps = renderTarget.texture.generateMipmaps; - - renderTarget.texture.generateMipmaps = false; - - renderer.setRenderTarget( renderTarget, 0 ); - renderer.render( scene, cameraPX ); - - renderer.setRenderTarget( renderTarget, 1 ); - renderer.render( scene, cameraNX ); - - renderer.setRenderTarget( renderTarget, 2 ); - renderer.render( scene, cameraPY ); - - renderer.setRenderTarget( renderTarget, 3 ); - renderer.render( scene, cameraNY ); - - renderer.setRenderTarget( renderTarget, 4 ); - renderer.render( scene, cameraPZ ); - - renderTarget.texture.generateMipmaps = generateMipmaps; - - renderer.setRenderTarget( renderTarget, 5 ); - renderer.render( scene, cameraNZ ); - - renderer.setRenderTarget( currentRenderTarget ); - - renderer.xr.enabled = currentXrEnabled; - - }; - - this.clear = function ( renderer, color, depth, stencil ) { - - const currentRenderTarget = renderer.getRenderTarget(); - - for ( let i = 0; i < 6; i ++ ) { - - renderer.setRenderTarget( renderTarget, i ); - - renderer.clear( color, depth, stencil ); - - } - - renderer.setRenderTarget( currentRenderTarget ); - - }; - - } - - CubeCamera.prototype = Object.create( Object3D.prototype ); - CubeCamera.prototype.constructor = CubeCamera; - - function WebGLCubeRenderTarget( size, options, dummy ) { - - if ( Number.isInteger( options ) ) { - - console.warn( 'THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )' ); - - options = dummy; - - } - - WebGLRenderTarget.call( this, size, size, options ); - - this.texture.isWebGLCubeRenderTargetTexture = true; // HACK Why is texture not a CubeTexture? - - } - - WebGLCubeRenderTarget.prototype = Object.create( WebGLRenderTarget.prototype ); - WebGLCubeRenderTarget.prototype.constructor = WebGLCubeRenderTarget; - - WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; - - WebGLCubeRenderTarget.prototype.fromEquirectangularTexture = function ( renderer, texture ) { - - this.texture.type = texture.type; - this.texture.format = RGBAFormat; // see #18859 - this.texture.encoding = texture.encoding; - - this.texture.generateMipmaps = texture.generateMipmaps; - this.texture.minFilter = texture.minFilter; - this.texture.magFilter = texture.magFilter; - - const shader = { - - uniforms: { - tEquirect: { value: null }, - }, - - vertexShader: /* glsl */` - - varying vec3 vWorldDirection; - - vec3 transformDirection( in vec3 dir, in mat4 matrix ) { - - return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); - - } - - void main() { - - vWorldDirection = transformDirection( position, modelMatrix ); - - #include - #include - - } - `, - - fragmentShader: /* glsl */` - - uniform sampler2D tEquirect; - - varying vec3 vWorldDirection; - - #include - - void main() { - - vec3 direction = normalize( vWorldDirection ); - - vec2 sampleUV = equirectUv( direction ); - - gl_FragColor = texture2D( tEquirect, sampleUV ); - - } - ` - }; - - const geometry = new BoxBufferGeometry( 5, 5, 5 ); - - const material = new ShaderMaterial( { - - name: 'CubemapFromEquirect', - - uniforms: cloneUniforms( shader.uniforms ), - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader, - side: BackSide, - blending: NoBlending - - } ); - - material.uniforms.tEquirect.value = texture; - - const mesh = new Mesh( geometry, material ); - - const currentMinFilter = texture.minFilter; - - // Avoid blurred poles - if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter; - - const camera = new CubeCamera( 1, 10, this ); - camera.update( renderer, mesh ); - - texture.minFilter = currentMinFilter; - - mesh.geometry.dispose(); - mesh.material.dispose(); - - return this; - - }; - - function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.image = { data: data || null, width: width || 1, height: height || 1 }; - - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; - - this.needsUpdate = true; - - } - - DataTexture.prototype = Object.create( Texture.prototype ); - DataTexture.prototype.constructor = DataTexture; - - DataTexture.prototype.isDataTexture = true; - - const _sphere$1 = new Sphere(); - const _vector$5 = new Vector3(); - - class Frustum { - - constructor( p0, p1, p2, p3, p4, p5 ) { - - this.planes = [ - - ( p0 !== undefined ) ? p0 : new Plane(), - ( p1 !== undefined ) ? p1 : new Plane(), - ( p2 !== undefined ) ? p2 : new Plane(), - ( p3 !== undefined ) ? p3 : new Plane(), - ( p4 !== undefined ) ? p4 : new Plane(), - ( p5 !== undefined ) ? p5 : new Plane() - - ]; - - } - - set( p0, p1, p2, p3, p4, p5 ) { - - const planes = this.planes; - - planes[ 0 ].copy( p0 ); - planes[ 1 ].copy( p1 ); - planes[ 2 ].copy( p2 ); - planes[ 3 ].copy( p3 ); - planes[ 4 ].copy( p4 ); - planes[ 5 ].copy( p5 ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( frustum ) { - - const planes = this.planes; - - for ( let i = 0; i < 6; i ++ ) { - - planes[ i ].copy( frustum.planes[ i ] ); - - } - - return this; - - } - - setFromProjectionMatrix( m ) { - - const planes = this.planes; - const me = m.elements; - const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; - const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; - const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; - const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; - - planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); - planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); - planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); - planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); - planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); - planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); - - return this; - - } - - intersectsObject( object ) { - - const geometry = object.geometry; - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _sphere$1.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); - - return this.intersectsSphere( _sphere$1 ); - - } - - intersectsSprite( sprite ) { - - _sphere$1.center.set( 0, 0, 0 ); - _sphere$1.radius = 0.7071067811865476; - _sphere$1.applyMatrix4( sprite.matrixWorld ); - - return this.intersectsSphere( _sphere$1 ); - - } - - intersectsSphere( sphere ) { - - const planes = this.planes; - const center = sphere.center; - const negRadius = - sphere.radius; - - for ( let i = 0; i < 6; i ++ ) { - - const distance = planes[ i ].distanceToPoint( center ); - - if ( distance < negRadius ) { - - return false; - - } - - } - - return true; - - } - - intersectsBox( box ) { - - const planes = this.planes; - - for ( let i = 0; i < 6; i ++ ) { - - const plane = planes[ i ]; - - // corner at max distance - - _vector$5.x = plane.normal.x > 0 ? box.max.x : box.min.x; - _vector$5.y = plane.normal.y > 0 ? box.max.y : box.min.y; - _vector$5.z = plane.normal.z > 0 ? box.max.z : box.min.z; - - if ( plane.distanceToPoint( _vector$5 ) < 0 ) { - - return false; - - } - - } - - return true; - - } - - containsPoint( point ) { - - const planes = this.planes; - - for ( let i = 0; i < 6; i ++ ) { - - if ( planes[ i ].distanceToPoint( point ) < 0 ) { - - return false; - - } - - } - - return true; - - } - - } - - function WebGLAnimation() { - - let context = null; - let isAnimating = false; - let animationLoop = null; - let requestId = null; - - function onAnimationFrame( time, frame ) { - - animationLoop( time, frame ); - - requestId = context.requestAnimationFrame( onAnimationFrame ); - - } - - return { - - start: function () { - - if ( isAnimating === true ) return; - if ( animationLoop === null ) return; - - requestId = context.requestAnimationFrame( onAnimationFrame ); - - isAnimating = true; - - }, - - stop: function () { - - context.cancelAnimationFrame( requestId ); - - isAnimating = false; - - }, - - setAnimationLoop: function ( callback ) { - - animationLoop = callback; - - }, - - setContext: function ( value ) { - - context = value; - - } - - }; - - } - - function WebGLAttributes( gl, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - const buffers = new WeakMap(); - - function createBuffer( attribute, bufferType ) { - - const array = attribute.array; - const usage = attribute.usage; - - const buffer = gl.createBuffer(); - - gl.bindBuffer( bufferType, buffer ); - gl.bufferData( bufferType, array, usage ); - - attribute.onUploadCallback(); - - let type = 5126; - - if ( array instanceof Float32Array ) { - - type = 5126; - - } else if ( array instanceof Float64Array ) { - - console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' ); - - } else if ( array instanceof Uint16Array ) { - - type = 5123; - - } else if ( array instanceof Int16Array ) { - - type = 5122; - - } else if ( array instanceof Uint32Array ) { - - type = 5125; - - } else if ( array instanceof Int32Array ) { - - type = 5124; - - } else if ( array instanceof Int8Array ) { - - type = 5120; - - } else if ( array instanceof Uint8Array ) { - - type = 5121; - - } - - return { - buffer: buffer, - type: type, - bytesPerElement: array.BYTES_PER_ELEMENT, - version: attribute.version - }; - - } - - function updateBuffer( buffer, attribute, bufferType ) { - - const array = attribute.array; - const updateRange = attribute.updateRange; - - gl.bindBuffer( bufferType, buffer ); - - if ( updateRange.count === - 1 ) { - - // Not using update ranges - - gl.bufferSubData( bufferType, 0, array ); - - } else { - - if ( isWebGL2 ) { - - gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, - array, updateRange.offset, updateRange.count ); - - } else { - - gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, - array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); - - } - - updateRange.count = - 1; // reset range - - } - - } - - // - - function get( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - - return buffers.get( attribute ); - - } - - function remove( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - - const data = buffers.get( attribute ); - - if ( data ) { - - gl.deleteBuffer( data.buffer ); - - buffers.delete( attribute ); - - } - - } - - function update( attribute, bufferType ) { - - if ( attribute.isGLBufferAttribute ) { - - var cached = buffers.get( attribute ); - - if ( ! cached || cached.version < attribute.version ) { - - buffers.set( attribute, { - buffer: attribute.buffer, - type: attribute.type, - bytesPerElement: attribute.elementSize, - version: attribute.version - } ); - - } - - return; - - } - - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - - const data = buffers.get( attribute ); - - if ( data === undefined ) { - - buffers.set( attribute, createBuffer( attribute, bufferType ) ); - - } else if ( data.version < attribute.version ) { - - updateBuffer( data.buffer, attribute, bufferType ); - - data.version = attribute.version; - - } - - } - - return { - - get: get, - remove: remove, - update: update - - }; - - } - - // PlaneGeometry - - class PlaneGeometry extends Geometry { - - constructor( width, height, widthSegments, heightSegments ) { - - super(); - - this.type = 'PlaneGeometry'; - - this.parameters = { - width: width, - height: height, - widthSegments: widthSegments, - heightSegments: heightSegments - }; - - this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); - this.mergeVertices(); - - } - - } - - // PlaneBufferGeometry - - class PlaneBufferGeometry extends BufferGeometry { - - constructor( width, height, widthSegments, heightSegments ) { - - super(); - this.type = 'PlaneBufferGeometry'; - - this.parameters = { - width: width, - height: height, - widthSegments: widthSegments, - heightSegments: heightSegments - }; - - width = width || 1; - height = height || 1; - - const width_half = width / 2; - const height_half = height / 2; - - const gridX = Math.floor( widthSegments ) || 1; - const gridY = Math.floor( heightSegments ) || 1; - - const gridX1 = gridX + 1; - const gridY1 = gridY + 1; - - const segment_width = width / gridX; - const segment_height = height / gridY; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // generate vertices, normals and uvs - - for ( let iy = 0; iy < gridY1; iy ++ ) { - - const y = iy * segment_height - height_half; - - for ( let ix = 0; ix < gridX1; ix ++ ) { - - const x = ix * segment_width - width_half; - - vertices.push( x, - y, 0 ); - - normals.push( 0, 0, 1 ); - - uvs.push( ix / gridX ); - uvs.push( 1 - ( iy / gridY ) ); - - } - - } - - // indices - - for ( let iy = 0; iy < gridY; iy ++ ) { - - for ( let ix = 0; ix < gridX; ix ++ ) { - - const a = ix + gridX1 * iy; - const b = ix + gridX1 * ( iy + 1 ); - const c = ( ix + 1 ) + gridX1 * ( iy + 1 ); - const d = ( ix + 1 ) + gridX1 * iy; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - } - - var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif"; - - var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; - - var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif"; - - var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif"; - - var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; - - var begin_vertex = "vec3 transformed = vec3( position );"; - - var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif"; - - var bsdfs = "vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\treturn vec2( -1.04, 1.04 ) * a004 + r.zw;\n}\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\tif( cutoffDistance > 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nvec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );\n\tvec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;\n\treturn Fr * fresnel + F0;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie(float roughness, float NoH) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max(1.0 - cos2h, 0.0078125);\treturn (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);\n}\nfloat V_Neubelt(float NoV, float NoL) {\n\treturn saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));\n}\nvec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif"; - - var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tfDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif"; - - var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#pragma unroll_loop_end\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t\tif ( clipped ) discard;\n\t#endif\n#endif"; - - var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif"; - - var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n#endif"; - - var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif"; - - var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif"; - - var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; - - var color_pars_vertex = "#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvarying vec3 vColor;\n#endif"; - - var color_vertex = "#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor.xyz *= color.xyz;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif"; - - var common = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}"; - - var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_maxMipLevel 8.0\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_maxTileSize 256.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tfloat texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 );\n\t\tvec2 f = fract( uv );\n\t\tuv += 0.5 - f;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tif ( mipInt < cubeUV_maxMipLevel ) {\n\t\t\tuv.y += 2.0 * cubeUV_maxTileSize;\n\t\t}\n\t\tuv.y += filterInt * 2.0 * cubeUV_minTileSize;\n\t\tuv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize );\n\t\tuv *= texelSize;\n\t\tvec3 tl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x += texelSize;\n\t\tvec3 tr = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.y += texelSize;\n\t\tvec3 br = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x -= texelSize;\n\t\tvec3 bl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tvec3 tm = mix( tl, tr, f.x );\n\t\tvec3 bm = mix( bl, br, f.x );\n\t\treturn mix( tm, bm, f.y );\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif"; - - var defaultnormal_vertex = "vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif"; - - var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif"; - - var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias );\n#endif"; - - var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; - - var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif"; - - var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; - - var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = clamp( floor( D ) / 255.0, 0.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = cLogLuvM * value.rgb;\n\tXp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract( Le );\n\tvResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}"; - - var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif"; - - var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; - - var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif"; - - var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif"; - - var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif"; - - var fog_vertex = "#ifdef USE_FOG\n\tfogDepth = - mvPosition.z;\n#endif"; - - var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif"; - - var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; - - var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif"; - - var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn texture2D( gradientMap, coord ).rgb;\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}"; - - var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\treflectedLight.indirectDiffuse += PI * lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n#endif"; - - var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif"; - - var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif"; - - var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif"; - - var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float roughness, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat sigma = PI * roughness * roughness / ( 1.0 + roughness );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + log2( sigma );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -viewDir, normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( roughness, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif"; - - var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;"; - - var lights_toon_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)"; - - var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;"; - - var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)"; - - var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.specularRoughness = max( roughnessFactor, 0.0525 );material.specularRoughness += geometryRoughness;\nmaterial.specularRoughness = min( material.specularRoughness, 1.0 );\n#ifdef REFLECTIVITY\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#endif\n#ifdef CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheen;\n#endif"; - - var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat specularRoughness;\n\tvec3 specularColor;\n#ifdef CLEARCOAT\n\tfloat clearcoat;\n\tfloat clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tvec3 sheenColor;\n#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearcoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = ccDotNL * directLight.color;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tccIrradiance *= PI;\n\t\t#endif\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t\treflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(\n\t\t\tmaterial.specularRoughness,\n\t\t\tdirectLight.direction,\n\t\t\tgeometry,\n\t\t\tmaterial.sheenColor\n\t\t);\n\t#else\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);\n\t#endif\n\treflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t\tfloat ccDotNL = ccDotNV;\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\tfloat clearcoatInv = 1.0 - clearcoatDHR;\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; - - var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; - - var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.normal, material.specularRoughness, maxMipLevel );\n\t#ifdef CLEARCOAT\n\t\tclearcoatRadiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, maxMipLevel );\n\t#endif\n#endif"; - - var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif"; - - var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif"; - - var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n\tvarying float vIsPerspective;\n#endif"; - - var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t\tvarying float vIsPerspective;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif"; - - var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif"; - - var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif"; - - var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif"; - - var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif"; - - var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tuniform mat3 uvTransform;\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; - - var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif"; - - var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; - - var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n#endif"; - - var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifndef USE_MORPHNORMALS\n\t\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\t\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; - - var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t#endif\n#endif"; - - var normal_fragment_begin = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t\tbitangent = bitangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;"; - - var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal, mapN );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif"; - - var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\n\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\tvec3 N = normalize( surf_norm );\n\t\tmat3 tsn = mat3( S, T, N );\n\t\tmapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif"; - - var clearcoat_normal_fragment_begin = "#ifdef CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; - - var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN );\n\t#endif\n#endif"; - - var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif"; - - var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ));\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w);\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}"; - - var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; - - var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 );\n#ifdef USE_INSTANCING\n\tmvPosition = instanceMatrix * mvPosition;\n#endif\nmvPosition = modelViewMatrix * mvPosition;\ngl_Position = projectionMatrix * mvPosition;"; - - var dithering_fragment = "#ifdef DITHERING\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif"; - - var dithering_pars_fragment = "#ifdef DITHERING\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif"; - - var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif"; - - var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; - - var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\tfloat hard_shadow = step( compare , distribution.x );\n\t\tif (hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif"; - - var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif"; - - var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0\n\t\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\tvec4 shadowWorldPosition;\n\t#endif\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n#endif"; - - var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}"; - - var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; - - var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform highp sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif"; - - var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif"; - - var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif"; - - var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif"; - - var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif"; - - var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif"; - - var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; - - var transmissionmap_fragment = "#ifdef USE_TRANSMISSIONMAP\n\ttotalTransmission *= texture2D( transmissionMap, vUv ).r;\n#endif"; - - var transmissionmap_pars_fragment = "#ifdef USE_TRANSMISSIONMAP\n\tuniform sampler2D transmissionMap;\n#endif"; - - var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )\n\tvarying vec2 vUv;\n#endif"; - - var uv_pars_vertex = "#ifdef USE_UV\n\t#ifdef UVS_VERTEX_ONLY\n\t\tvec2 vUv;\n\t#else\n\t\tvarying vec2 vUv;\n\t#endif\n\tuniform mat3 uvTransform;\n#endif"; - - var uv_vertex = "#ifdef USE_UV\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif"; - - var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif"; - - var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n\tuniform mat3 uv2Transform;\n#endif"; - - var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n#endif"; - - var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; - - var background_frag = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; - - var background_vert = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; - - var cube_frag = "#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; - - var cube_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; - - var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; - - var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}"; - - var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; - - var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; - - var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; - - var equirect_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; - - var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshmatcap_frag = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshmatcap_vert = "#define MATCAP\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; - - var meshtoon_frag = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshtoon_vert = "#define TOON\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; - - var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshphysical_frag = "#define STANDARD\n#ifdef PHYSICAL\n\t#define REFLECTIVITY\n\t#define CLEARCOAT\n\t#define TRANSMISSION\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef TRANSMISSION\n\tuniform float transmission;\n#endif\n#ifdef REFLECTIVITY\n\tuniform float reflectivity;\n#endif\n#ifdef CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheen;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#ifdef TRANSMISSION\n\t\tfloat totalTransmission = transmission;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#ifdef TRANSMISSION\n\t\tdiffuseColor.a *= saturate( 1. - totalTransmission + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) );\n\t#endif\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshphysical_vert = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; - - var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}"; - - var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; - - var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var points_vert = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}"; - - var shadow_vert = "#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var sprite_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n}"; - - var sprite_vert = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; - - const ShaderChunk = { - alphamap_fragment: alphamap_fragment, - alphamap_pars_fragment: alphamap_pars_fragment, - alphatest_fragment: alphatest_fragment, - aomap_fragment: aomap_fragment, - aomap_pars_fragment: aomap_pars_fragment, - begin_vertex: begin_vertex, - beginnormal_vertex: beginnormal_vertex, - bsdfs: bsdfs, - bumpmap_pars_fragment: bumpmap_pars_fragment, - clipping_planes_fragment: clipping_planes_fragment, - clipping_planes_pars_fragment: clipping_planes_pars_fragment, - clipping_planes_pars_vertex: clipping_planes_pars_vertex, - clipping_planes_vertex: clipping_planes_vertex, - color_fragment: color_fragment, - color_pars_fragment: color_pars_fragment, - color_pars_vertex: color_pars_vertex, - color_vertex: color_vertex, - common: common, - cube_uv_reflection_fragment: cube_uv_reflection_fragment, - defaultnormal_vertex: defaultnormal_vertex, - displacementmap_pars_vertex: displacementmap_pars_vertex, - displacementmap_vertex: displacementmap_vertex, - emissivemap_fragment: emissivemap_fragment, - emissivemap_pars_fragment: emissivemap_pars_fragment, - encodings_fragment: encodings_fragment, - encodings_pars_fragment: encodings_pars_fragment, - envmap_fragment: envmap_fragment, - envmap_common_pars_fragment: envmap_common_pars_fragment, - envmap_pars_fragment: envmap_pars_fragment, - envmap_pars_vertex: envmap_pars_vertex, - envmap_physical_pars_fragment: envmap_physical_pars_fragment, - envmap_vertex: envmap_vertex, - fog_vertex: fog_vertex, - fog_pars_vertex: fog_pars_vertex, - fog_fragment: fog_fragment, - fog_pars_fragment: fog_pars_fragment, - gradientmap_pars_fragment: gradientmap_pars_fragment, - lightmap_fragment: lightmap_fragment, - lightmap_pars_fragment: lightmap_pars_fragment, - lights_lambert_vertex: lights_lambert_vertex, - lights_pars_begin: lights_pars_begin, - lights_toon_fragment: lights_toon_fragment, - lights_toon_pars_fragment: lights_toon_pars_fragment, - lights_phong_fragment: lights_phong_fragment, - lights_phong_pars_fragment: lights_phong_pars_fragment, - lights_physical_fragment: lights_physical_fragment, - lights_physical_pars_fragment: lights_physical_pars_fragment, - lights_fragment_begin: lights_fragment_begin, - lights_fragment_maps: lights_fragment_maps, - lights_fragment_end: lights_fragment_end, - logdepthbuf_fragment: logdepthbuf_fragment, - logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, - logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, - logdepthbuf_vertex: logdepthbuf_vertex, - map_fragment: map_fragment, - map_pars_fragment: map_pars_fragment, - map_particle_fragment: map_particle_fragment, - map_particle_pars_fragment: map_particle_pars_fragment, - metalnessmap_fragment: metalnessmap_fragment, - metalnessmap_pars_fragment: metalnessmap_pars_fragment, - morphnormal_vertex: morphnormal_vertex, - morphtarget_pars_vertex: morphtarget_pars_vertex, - morphtarget_vertex: morphtarget_vertex, - normal_fragment_begin: normal_fragment_begin, - normal_fragment_maps: normal_fragment_maps, - normalmap_pars_fragment: normalmap_pars_fragment, - clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, - clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, - clearcoat_pars_fragment: clearcoat_pars_fragment, - packing: packing, - premultiplied_alpha_fragment: premultiplied_alpha_fragment, - project_vertex: project_vertex, - dithering_fragment: dithering_fragment, - dithering_pars_fragment: dithering_pars_fragment, - roughnessmap_fragment: roughnessmap_fragment, - roughnessmap_pars_fragment: roughnessmap_pars_fragment, - shadowmap_pars_fragment: shadowmap_pars_fragment, - shadowmap_pars_vertex: shadowmap_pars_vertex, - shadowmap_vertex: shadowmap_vertex, - shadowmask_pars_fragment: shadowmask_pars_fragment, - skinbase_vertex: skinbase_vertex, - skinning_pars_vertex: skinning_pars_vertex, - skinning_vertex: skinning_vertex, - skinnormal_vertex: skinnormal_vertex, - specularmap_fragment: specularmap_fragment, - specularmap_pars_fragment: specularmap_pars_fragment, - tonemapping_fragment: tonemapping_fragment, - tonemapping_pars_fragment: tonemapping_pars_fragment, - transmissionmap_fragment: transmissionmap_fragment, - transmissionmap_pars_fragment: transmissionmap_pars_fragment, - uv_pars_fragment: uv_pars_fragment, - uv_pars_vertex: uv_pars_vertex, - uv_vertex: uv_vertex, - uv2_pars_fragment: uv2_pars_fragment, - uv2_pars_vertex: uv2_pars_vertex, - uv2_vertex: uv2_vertex, - worldpos_vertex: worldpos_vertex, - - background_frag: background_frag, - background_vert: background_vert, - cube_frag: cube_frag, - cube_vert: cube_vert, - depth_frag: depth_frag, - depth_vert: depth_vert, - distanceRGBA_frag: distanceRGBA_frag, - distanceRGBA_vert: distanceRGBA_vert, - equirect_frag: equirect_frag, - equirect_vert: equirect_vert, - linedashed_frag: linedashed_frag, - linedashed_vert: linedashed_vert, - meshbasic_frag: meshbasic_frag, - meshbasic_vert: meshbasic_vert, - meshlambert_frag: meshlambert_frag, - meshlambert_vert: meshlambert_vert, - meshmatcap_frag: meshmatcap_frag, - meshmatcap_vert: meshmatcap_vert, - meshtoon_frag: meshtoon_frag, - meshtoon_vert: meshtoon_vert, - meshphong_frag: meshphong_frag, - meshphong_vert: meshphong_vert, - meshphysical_frag: meshphysical_frag, - meshphysical_vert: meshphysical_vert, - normal_frag: normal_frag, - normal_vert: normal_vert, - points_frag: points_frag, - points_vert: points_vert, - shadow_frag: shadow_frag, - shadow_vert: shadow_vert, - sprite_frag: sprite_frag, - sprite_vert: sprite_vert - }; - - /** - * Uniforms library for shared webgl shaders - */ - - const UniformsLib = { - - common: { - - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - - map: { value: null }, - uvTransform: { value: new Matrix3() }, - uv2Transform: { value: new Matrix3() }, - - alphaMap: { value: null }, - - }, - - specularmap: { - - specularMap: { value: null }, - - }, - - envmap: { - - envMap: { value: null }, - flipEnvMap: { value: - 1 }, - reflectivity: { value: 1.0 }, - refractionRatio: { value: 0.98 }, - maxMipLevel: { value: 0 } - - }, - - aomap: { - - aoMap: { value: null }, - aoMapIntensity: { value: 1 } - - }, - - lightmap: { - - lightMap: { value: null }, - lightMapIntensity: { value: 1 } - - }, - - emissivemap: { - - emissiveMap: { value: null } - - }, - - bumpmap: { - - bumpMap: { value: null }, - bumpScale: { value: 1 } - - }, - - normalmap: { - - normalMap: { value: null }, - normalScale: { value: new Vector2( 1, 1 ) } - - }, - - displacementmap: { - - displacementMap: { value: null }, - displacementScale: { value: 1 }, - displacementBias: { value: 0 } - - }, - - roughnessmap: { - - roughnessMap: { value: null } - - }, - - metalnessmap: { - - metalnessMap: { value: null } - - }, - - gradientmap: { - - gradientMap: { value: null } - - }, - - fog: { - - fogDensity: { value: 0.00025 }, - fogNear: { value: 1 }, - fogFar: { value: 2000 }, - fogColor: { value: new Color( 0xffffff ) } - - }, - - lights: { - - ambientLightColor: { value: [] }, - - lightProbe: { value: [] }, - - directionalLights: { value: [], properties: { - direction: {}, - color: {} - } }, - - directionalLightShadows: { value: [], properties: { - shadowBias: {}, - shadowNormalBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - directionalShadowMap: { value: [] }, - directionalShadowMatrix: { value: [] }, - - spotLights: { value: [], properties: { - color: {}, - position: {}, - direction: {}, - distance: {}, - coneCos: {}, - penumbraCos: {}, - decay: {} - } }, - - spotLightShadows: { value: [], properties: { - shadowBias: {}, - shadowNormalBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - spotShadowMap: { value: [] }, - spotShadowMatrix: { value: [] }, - - pointLights: { value: [], properties: { - color: {}, - position: {}, - decay: {}, - distance: {} - } }, - - pointLightShadows: { value: [], properties: { - shadowBias: {}, - shadowNormalBias: {}, - shadowRadius: {}, - shadowMapSize: {}, - shadowCameraNear: {}, - shadowCameraFar: {} - } }, - - pointShadowMap: { value: [] }, - pointShadowMatrix: { value: [] }, - - hemisphereLights: { value: [], properties: { - direction: {}, - skyColor: {}, - groundColor: {} - } }, - - // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src - rectAreaLights: { value: [], properties: { - color: {}, - position: {}, - width: {}, - height: {} - } }, - - ltc_1: { value: null }, - ltc_2: { value: null } - - }, - - points: { - - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - size: { value: 1.0 }, - scale: { value: 1.0 }, - map: { value: null }, - alphaMap: { value: null }, - uvTransform: { value: new Matrix3() } - - }, - - sprite: { - - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - center: { value: new Vector2( 0.5, 0.5 ) }, - rotation: { value: 0.0 }, - map: { value: null }, - alphaMap: { value: null }, - uvTransform: { value: new Matrix3() } - - } - - }; - - const ShaderLib = { - - basic: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.meshbasic_vert, - fragmentShader: ShaderChunk.meshbasic_frag - - }, - - lambert: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) } - } - ] ), - - vertexShader: ShaderChunk.meshlambert_vert, - fragmentShader: ShaderChunk.meshlambert_frag - - }, - - phong: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - specular: { value: new Color( 0x111111 ) }, - shininess: { value: 30 } - } - ] ), - - vertexShader: ShaderChunk.meshphong_vert, - fragmentShader: ShaderChunk.meshphong_frag - - }, - - standard: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.roughnessmap, - UniformsLib.metalnessmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - roughness: { value: 1.0 }, - metalness: { value: 0.0 }, - envMapIntensity: { value: 1 } // temporary - } - ] ), - - vertexShader: ShaderChunk.meshphysical_vert, - fragmentShader: ShaderChunk.meshphysical_frag - - }, - - toon: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.gradientmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) } - } - ] ), - - vertexShader: ShaderChunk.meshtoon_vert, - fragmentShader: ShaderChunk.meshtoon_frag - - }, - - matcap: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.fog, - { - matcap: { value: null } - } - ] ), - - vertexShader: ShaderChunk.meshmatcap_vert, - fragmentShader: ShaderChunk.meshmatcap_frag - - }, - - points: { - - uniforms: mergeUniforms( [ - UniformsLib.points, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.points_vert, - fragmentShader: ShaderChunk.points_frag - - }, - - dashed: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.fog, - { - scale: { value: 1 }, - dashSize: { value: 1 }, - totalSize: { value: 2 } - } - ] ), - - vertexShader: ShaderChunk.linedashed_vert, - fragmentShader: ShaderChunk.linedashed_frag - - }, - - depth: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.displacementmap - ] ), - - vertexShader: ShaderChunk.depth_vert, - fragmentShader: ShaderChunk.depth_frag - - }, - - normal: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - { - opacity: { value: 1.0 } - } - ] ), - - vertexShader: ShaderChunk.normal_vert, - fragmentShader: ShaderChunk.normal_frag - - }, - - sprite: { - - uniforms: mergeUniforms( [ - UniformsLib.sprite, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.sprite_vert, - fragmentShader: ShaderChunk.sprite_frag - - }, - - background: { - - uniforms: { - uvTransform: { value: new Matrix3() }, - t2D: { value: null }, - }, - - vertexShader: ShaderChunk.background_vert, - fragmentShader: ShaderChunk.background_frag - - }, - /* ------------------------------------------------------------------------- - // Cube map shader - ------------------------------------------------------------------------- */ - - cube: { - - uniforms: mergeUniforms( [ - UniformsLib.envmap, - { - opacity: { value: 1.0 } - } - ] ), - - vertexShader: ShaderChunk.cube_vert, - fragmentShader: ShaderChunk.cube_frag - - }, - - equirect: { - - uniforms: { - tEquirect: { value: null }, - }, - - vertexShader: ShaderChunk.equirect_vert, - fragmentShader: ShaderChunk.equirect_frag - - }, - - distanceRGBA: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.displacementmap, - { - referencePosition: { value: new Vector3() }, - nearDistance: { value: 1 }, - farDistance: { value: 1000 } - } - ] ), - - vertexShader: ShaderChunk.distanceRGBA_vert, - fragmentShader: ShaderChunk.distanceRGBA_frag - - }, - - shadow: { - - uniforms: mergeUniforms( [ - UniformsLib.lights, - UniformsLib.fog, - { - color: { value: new Color( 0x00000 ) }, - opacity: { value: 1.0 } - }, - ] ), - - vertexShader: ShaderChunk.shadow_vert, - fragmentShader: ShaderChunk.shadow_frag - - } - - }; - - ShaderLib.physical = { - - uniforms: mergeUniforms( [ - ShaderLib.standard.uniforms, - { - clearcoat: { value: 0 }, - clearcoatMap: { value: null }, - clearcoatRoughness: { value: 0 }, - clearcoatRoughnessMap: { value: null }, - clearcoatNormalScale: { value: new Vector2( 1, 1 ) }, - clearcoatNormalMap: { value: null }, - sheen: { value: new Color( 0x000000 ) }, - transmission: { value: 0 }, - transmissionMap: { value: null }, - } - ] ), - - vertexShader: ShaderChunk.meshphysical_vert, - fragmentShader: ShaderChunk.meshphysical_frag - - }; - - function WebGLBackground( renderer, cubemaps, state, objects, premultipliedAlpha ) { - - const clearColor = new Color( 0x000000 ); - let clearAlpha = 0; - - let planeMesh; - let boxMesh; - - let currentBackground = null; - let currentBackgroundVersion = 0; - let currentTonemapping = null; - - function render( renderList, scene, camera, forceClear ) { - - let background = scene.isScene === true ? scene.background : null; - - if ( background && background.isTexture ) { - - background = cubemaps.get( background ); - - } - - // Ignore background in AR - // TODO: Reconsider this. - - const xr = renderer.xr; - const session = xr.getSession && xr.getSession(); - - if ( session && session.environmentBlendMode === 'additive' ) { - - background = null; - - } - - if ( background === null ) { - - setClear( clearColor, clearAlpha ); - - } else if ( background && background.isColor ) { - - setClear( background, 1 ); - forceClear = true; - - } - - if ( renderer.autoClear || forceClear ) { - - renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); - - } - - if ( background && ( background.isCubeTexture || background.isWebGLCubeRenderTarget || background.isWebGLCubeRenderTargetTexture || background.mapping === CubeUVReflectionMapping ) ) { - - if ( boxMesh === undefined ) { - - boxMesh = new Mesh( - new BoxBufferGeometry( 1, 1, 1 ), - new ShaderMaterial( { - name: 'BackgroundCubeMaterial', - uniforms: cloneUniforms( ShaderLib.cube.uniforms ), - vertexShader: ShaderLib.cube.vertexShader, - fragmentShader: ShaderLib.cube.fragmentShader, - side: BackSide, - depthTest: false, - depthWrite: false, - fog: false - } ) - ); - - boxMesh.geometry.deleteAttribute( 'normal' ); - boxMesh.geometry.deleteAttribute( 'uv' ); - - boxMesh.onBeforeRender = function ( renderer, scene, camera ) { - - this.matrixWorld.copyPosition( camera.matrixWorld ); - - }; - - // enable code injection for non-built-in material - Object.defineProperty( boxMesh.material, 'envMap', { - - get: function () { - - return this.uniforms.envMap.value; - - } - - } ); - - objects.update( boxMesh ); - - } - - if ( background.isWebGLCubeRenderTarget ) { - - // TODO Deprecate - - background = background.texture; - - } - - boxMesh.material.uniforms.envMap.value = background; - boxMesh.material.uniforms.flipEnvMap.value = background.isCubeTexture ? - 1 : 1; - - if ( currentBackground !== background || - currentBackgroundVersion !== background.version || - currentTonemapping !== renderer.toneMapping ) { - - boxMesh.material.needsUpdate = true; - - currentBackground = background; - currentBackgroundVersion = background.version; - currentTonemapping = renderer.toneMapping; - - } - - // push to the pre-sorted opaque render list - renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); - - } else if ( background && background.isTexture ) { - - if ( planeMesh === undefined ) { - - planeMesh = new Mesh( - new PlaneBufferGeometry( 2, 2 ), - new ShaderMaterial( { - name: 'BackgroundMaterial', - uniforms: cloneUniforms( ShaderLib.background.uniforms ), - vertexShader: ShaderLib.background.vertexShader, - fragmentShader: ShaderLib.background.fragmentShader, - side: FrontSide, - depthTest: false, - depthWrite: false, - fog: false - } ) - ); - - planeMesh.geometry.deleteAttribute( 'normal' ); - - // enable code injection for non-built-in material - Object.defineProperty( planeMesh.material, 'map', { - - get: function () { - - return this.uniforms.t2D.value; - - } - - } ); - - objects.update( planeMesh ); - - } - - planeMesh.material.uniforms.t2D.value = background; - - if ( background.matrixAutoUpdate === true ) { - - background.updateMatrix(); - - } - - planeMesh.material.uniforms.uvTransform.value.copy( background.matrix ); - - if ( currentBackground !== background || - currentBackgroundVersion !== background.version || - currentTonemapping !== renderer.toneMapping ) { - - planeMesh.material.needsUpdate = true; - - currentBackground = background; - currentBackgroundVersion = background.version; - currentTonemapping = renderer.toneMapping; - - } - - - // push to the pre-sorted opaque render list - renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null ); - - } - - } - - function setClear( color, alpha ) { - - state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha ); - - } - - return { - - getClearColor: function () { - - return clearColor; - - }, - setClearColor: function ( color, alpha ) { - - clearColor.set( color ); - clearAlpha = alpha !== undefined ? alpha : 1; - setClear( clearColor, clearAlpha ); - - }, - getClearAlpha: function () { - - return clearAlpha; - - }, - setClearAlpha: function ( alpha ) { - - clearAlpha = alpha; - setClear( clearColor, clearAlpha ); - - }, - render: render - - }; - - } - - function WebGLBindingStates( gl, extensions, attributes, capabilities ) { - - const maxVertexAttributes = gl.getParameter( 34921 ); - - const extension = capabilities.isWebGL2 ? null : extensions.get( 'OES_vertex_array_object' ); - const vaoAvailable = capabilities.isWebGL2 || extension !== null; - - const bindingStates = {}; - - const defaultState = createBindingState( null ); - let currentState = defaultState; - - function setup( object, material, program, geometry, index ) { - - let updateBuffers = false; - - if ( vaoAvailable ) { - - const state = getBindingState( geometry, program, material ); - - if ( currentState !== state ) { - - currentState = state; - bindVertexArrayObject( currentState.object ); - - } - - updateBuffers = needsUpdate( geometry, index ); - - if ( updateBuffers ) saveCache( geometry, index ); - - } else { - - const wireframe = ( material.wireframe === true ); - - if ( currentState.geometry !== geometry.id || - currentState.program !== program.id || - currentState.wireframe !== wireframe ) { - - currentState.geometry = geometry.id; - currentState.program = program.id; - currentState.wireframe = wireframe; - - updateBuffers = true; - - } - - } - - if ( object.isInstancedMesh === true ) { - - updateBuffers = true; - - } - - if ( index !== null ) { - - attributes.update( index, 34963 ); - - } - - if ( updateBuffers ) { - - setupVertexAttributes( object, material, program, geometry ); - - if ( index !== null ) { - - gl.bindBuffer( 34963, attributes.get( index ).buffer ); - - } - - } - - } - - function createVertexArrayObject() { - - if ( capabilities.isWebGL2 ) return gl.createVertexArray(); - - return extension.createVertexArrayOES(); - - } - - function bindVertexArrayObject( vao ) { - - if ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao ); - - return extension.bindVertexArrayOES( vao ); - - } - - function deleteVertexArrayObject( vao ) { - - if ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao ); - - return extension.deleteVertexArrayOES( vao ); - - } - - function getBindingState( geometry, program, material ) { - - const wireframe = ( material.wireframe === true ); - - let programMap = bindingStates[ geometry.id ]; - - if ( programMap === undefined ) { - - programMap = {}; - bindingStates[ geometry.id ] = programMap; - - } - - let stateMap = programMap[ program.id ]; - - if ( stateMap === undefined ) { - - stateMap = {}; - programMap[ program.id ] = stateMap; - - } - - let state = stateMap[ wireframe ]; - - if ( state === undefined ) { - - state = createBindingState( createVertexArrayObject() ); - stateMap[ wireframe ] = state; - - } - - return state; - - } - - function createBindingState( vao ) { - - const newAttributes = []; - const enabledAttributes = []; - const attributeDivisors = []; - - for ( let i = 0; i < maxVertexAttributes; i ++ ) { - - newAttributes[ i ] = 0; - enabledAttributes[ i ] = 0; - attributeDivisors[ i ] = 0; - - } - - return { - - // for backward compatibility on non-VAO support browser - geometry: null, - program: null, - wireframe: false, - - newAttributes: newAttributes, - enabledAttributes: enabledAttributes, - attributeDivisors: attributeDivisors, - object: vao, - attributes: {}, - index: null - - }; - - } - - function needsUpdate( geometry, index ) { - - const cachedAttributes = currentState.attributes; - const geometryAttributes = geometry.attributes; - - if ( Object.keys( cachedAttributes ).length !== Object.keys( geometryAttributes ).length ) return true; - - for ( const key in geometryAttributes ) { - - const cachedAttribute = cachedAttributes[ key ]; - const geometryAttribute = geometryAttributes[ key ]; - - if ( cachedAttribute === undefined ) return true; - - if ( cachedAttribute.attribute !== geometryAttribute ) return true; - - if ( cachedAttribute.data !== geometryAttribute.data ) return true; - - } - - if ( currentState.index !== index ) return true; - - return false; - - } - - function saveCache( geometry, index ) { - - const cache = {}; - const attributes = geometry.attributes; - - for ( const key in attributes ) { - - const attribute = attributes[ key ]; - - const data = {}; - data.attribute = attribute; - - if ( attribute.data ) { - - data.data = attribute.data; - - } - - cache[ key ] = data; - - } - - currentState.attributes = cache; - - currentState.index = index; - - } - - function initAttributes() { - - const newAttributes = currentState.newAttributes; - - for ( let i = 0, il = newAttributes.length; i < il; i ++ ) { - - newAttributes[ i ] = 0; - - } - - } - - function enableAttribute( attribute ) { - - enableAttributeAndDivisor( attribute, 0 ); - - } - - function enableAttributeAndDivisor( attribute, meshPerAttribute ) { - - const newAttributes = currentState.newAttributes; - const enabledAttributes = currentState.enabledAttributes; - const attributeDivisors = currentState.attributeDivisors; - - newAttributes[ attribute ] = 1; - - if ( enabledAttributes[ attribute ] === 0 ) { - - gl.enableVertexAttribArray( attribute ); - enabledAttributes[ attribute ] = 1; - - } - - if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { - - const extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' ); - - extension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute ); - attributeDivisors[ attribute ] = meshPerAttribute; - - } - - } - - function disableUnusedAttributes() { - - const newAttributes = currentState.newAttributes; - const enabledAttributes = currentState.enabledAttributes; - - for ( let i = 0, il = enabledAttributes.length; i < il; i ++ ) { - - if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { - - gl.disableVertexAttribArray( i ); - enabledAttributes[ i ] = 0; - - } - - } - - } - - function vertexAttribPointer( index, size, type, normalized, stride, offset ) { - - if ( capabilities.isWebGL2 === true && ( type === 5124 || type === 5125 ) ) { - - gl.vertexAttribIPointer( index, size, type, stride, offset ); - - } else { - - gl.vertexAttribPointer( index, size, type, normalized, stride, offset ); - - } - - } - - function setupVertexAttributes( object, material, program, geometry ) { - - if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) { - - if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) return; - - } - - initAttributes(); - - const geometryAttributes = geometry.attributes; - - const programAttributes = program.getAttributes(); - - const materialDefaultAttributeValues = material.defaultAttributeValues; - - for ( const name in programAttributes ) { - - const programAttribute = programAttributes[ name ]; - - if ( programAttribute >= 0 ) { - - const geometryAttribute = geometryAttributes[ name ]; - - if ( geometryAttribute !== undefined ) { - - const normalized = geometryAttribute.normalized; - const size = geometryAttribute.itemSize; - - const attribute = attributes.get( geometryAttribute ); - - // TODO Attribute may not be available on context restore - - if ( attribute === undefined ) continue; - - const buffer = attribute.buffer; - const type = attribute.type; - const bytesPerElement = attribute.bytesPerElement; - - if ( geometryAttribute.isInterleavedBufferAttribute ) { - - const data = geometryAttribute.data; - const stride = data.stride; - const offset = geometryAttribute.offset; - - if ( data && data.isInstancedInterleavedBuffer ) { - - enableAttributeAndDivisor( programAttribute, data.meshPerAttribute ); - - if ( geometry._maxInstanceCount === undefined ) { - - geometry._maxInstanceCount = data.meshPerAttribute * data.count; - - } - - } else { - - enableAttribute( programAttribute ); - - } - - gl.bindBuffer( 34962, buffer ); - vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement ); - - } else { - - if ( geometryAttribute.isInstancedBufferAttribute ) { - - enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute ); - - if ( geometry._maxInstanceCount === undefined ) { - - geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; - - } - - } else { - - enableAttribute( programAttribute ); - - } - - gl.bindBuffer( 34962, buffer ); - vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 ); - - } - - } else if ( name === 'instanceMatrix' ) { - - const attribute = attributes.get( object.instanceMatrix ); - - // TODO Attribute may not be available on context restore - - if ( attribute === undefined ) continue; - - const buffer = attribute.buffer; - const type = attribute.type; - - enableAttributeAndDivisor( programAttribute + 0, 1 ); - enableAttributeAndDivisor( programAttribute + 1, 1 ); - enableAttributeAndDivisor( programAttribute + 2, 1 ); - enableAttributeAndDivisor( programAttribute + 3, 1 ); - - gl.bindBuffer( 34962, buffer ); - - gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 ); - gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 ); - gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 ); - gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 ); - - } else if ( name === 'instanceColor' ) { - - const attribute = attributes.get( object.instanceColor ); - - // TODO Attribute may not be available on context restore - - if ( attribute === undefined ) continue; - - const buffer = attribute.buffer; - const type = attribute.type; - - enableAttributeAndDivisor( programAttribute, 1 ); - - gl.bindBuffer( 34962, buffer ); - - gl.vertexAttribPointer( programAttribute, 3, type, false, 12, 0 ); - - } else if ( materialDefaultAttributeValues !== undefined ) { - - const value = materialDefaultAttributeValues[ name ]; - - if ( value !== undefined ) { - - switch ( value.length ) { - - case 2: - gl.vertexAttrib2fv( programAttribute, value ); - break; - - case 3: - gl.vertexAttrib3fv( programAttribute, value ); - break; - - case 4: - gl.vertexAttrib4fv( programAttribute, value ); - break; - - default: - gl.vertexAttrib1fv( programAttribute, value ); - - } - - } - - } - - } - - } - - disableUnusedAttributes(); - - } - - function dispose() { - - reset(); - - for ( const geometryId in bindingStates ) { - - const programMap = bindingStates[ geometryId ]; - - for ( const programId in programMap ) { - - const stateMap = programMap[ programId ]; - - for ( const wireframe in stateMap ) { - - deleteVertexArrayObject( stateMap[ wireframe ].object ); - - delete stateMap[ wireframe ]; - - } - - delete programMap[ programId ]; - - } - - delete bindingStates[ geometryId ]; - - } - - } - - function releaseStatesOfGeometry( geometry ) { - - if ( bindingStates[ geometry.id ] === undefined ) return; - - const programMap = bindingStates[ geometry.id ]; - - for ( const programId in programMap ) { - - const stateMap = programMap[ programId ]; - - for ( const wireframe in stateMap ) { - - deleteVertexArrayObject( stateMap[ wireframe ].object ); - - delete stateMap[ wireframe ]; - - } - - delete programMap[ programId ]; - - } - - delete bindingStates[ geometry.id ]; - - } - - function releaseStatesOfProgram( program ) { - - for ( const geometryId in bindingStates ) { - - const programMap = bindingStates[ geometryId ]; - - if ( programMap[ program.id ] === undefined ) continue; - - const stateMap = programMap[ program.id ]; - - for ( const wireframe in stateMap ) { - - deleteVertexArrayObject( stateMap[ wireframe ].object ); - - delete stateMap[ wireframe ]; - - } - - delete programMap[ program.id ]; - - } - - } - - function reset() { - - resetDefaultState(); - - if ( currentState === defaultState ) return; - - currentState = defaultState; - bindVertexArrayObject( currentState.object ); - - } - - // for backward-compatilibity - - function resetDefaultState() { - - defaultState.geometry = null; - defaultState.program = null; - defaultState.wireframe = false; - - } - - return { - - setup: setup, - reset: reset, - resetDefaultState: resetDefaultState, - dispose: dispose, - releaseStatesOfGeometry: releaseStatesOfGeometry, - releaseStatesOfProgram: releaseStatesOfProgram, - - initAttributes: initAttributes, - enableAttribute: enableAttribute, - disableUnusedAttributes: disableUnusedAttributes - - }; - - } - - function WebGLBufferRenderer( gl, extensions, info, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - let mode; - - function setMode( value ) { - - mode = value; - - } - - function render( start, count ) { - - gl.drawArrays( mode, start, count ); - - info.update( count, mode, 1 ); - - } - - function renderInstances( start, count, primcount ) { - - if ( primcount === 0 ) return; - - let extension, methodName; - - if ( isWebGL2 ) { - - extension = gl; - methodName = 'drawArraysInstanced'; - - } else { - - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawArraysInstancedANGLE'; - - if ( extension === null ) { - - console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } - - } - - extension[ methodName ]( mode, start, count, primcount ); - - info.update( count, mode, primcount ); - - } - - // - - this.setMode = setMode; - this.render = render; - this.renderInstances = renderInstances; - - } - - function WebGLCapabilities( gl, extensions, parameters ) { - - let maxAnisotropy; - - function getMaxAnisotropy() { - - if ( maxAnisotropy !== undefined ) return maxAnisotropy; - - const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - - if ( extension !== null ) { - - maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); - - } else { - - maxAnisotropy = 0; - - } - - return maxAnisotropy; - - } - - function getMaxPrecision( precision ) { - - if ( precision === 'highp' ) { - - if ( gl.getShaderPrecisionFormat( 35633, 36338 ).precision > 0 && - gl.getShaderPrecisionFormat( 35632, 36338 ).precision > 0 ) { - - return 'highp'; - - } - - precision = 'mediump'; - - } - - if ( precision === 'mediump' ) { - - if ( gl.getShaderPrecisionFormat( 35633, 36337 ).precision > 0 && - gl.getShaderPrecisionFormat( 35632, 36337 ).precision > 0 ) { - - return 'mediump'; - - } - - } - - return 'lowp'; - - } - - /* eslint-disable no-undef */ - const isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext ) || - ( typeof WebGL2ComputeRenderingContext !== 'undefined' && gl instanceof WebGL2ComputeRenderingContext ); - /* eslint-enable no-undef */ - - let precision = parameters.precision !== undefined ? parameters.precision : 'highp'; - const maxPrecision = getMaxPrecision( precision ); - - if ( maxPrecision !== precision ) { - - console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' ); - precision = maxPrecision; - - } - - const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; - - const maxTextures = gl.getParameter( 34930 ); - const maxVertexTextures = gl.getParameter( 35660 ); - const maxTextureSize = gl.getParameter( 3379 ); - const maxCubemapSize = gl.getParameter( 34076 ); - - const maxAttributes = gl.getParameter( 34921 ); - const maxVertexUniforms = gl.getParameter( 36347 ); - const maxVaryings = gl.getParameter( 36348 ); - const maxFragmentUniforms = gl.getParameter( 36349 ); - - const vertexTextures = maxVertexTextures > 0; - const floatFragmentTextures = isWebGL2 || !! extensions.get( 'OES_texture_float' ); - const floatVertexTextures = vertexTextures && floatFragmentTextures; - - const maxSamples = isWebGL2 ? gl.getParameter( 36183 ) : 0; - - return { - - isWebGL2: isWebGL2, - - getMaxAnisotropy: getMaxAnisotropy, - getMaxPrecision: getMaxPrecision, - - precision: precision, - logarithmicDepthBuffer: logarithmicDepthBuffer, - - maxTextures: maxTextures, - maxVertexTextures: maxVertexTextures, - maxTextureSize: maxTextureSize, - maxCubemapSize: maxCubemapSize, - - maxAttributes: maxAttributes, - maxVertexUniforms: maxVertexUniforms, - maxVaryings: maxVaryings, - maxFragmentUniforms: maxFragmentUniforms, - - vertexTextures: vertexTextures, - floatFragmentTextures: floatFragmentTextures, - floatVertexTextures: floatVertexTextures, - - maxSamples: maxSamples - - }; - - } - - function WebGLClipping( properties ) { - - const scope = this; - - let globalState = null, - numGlobalPlanes = 0, - localClippingEnabled = false, - renderingShadows = false; - - const plane = new Plane(), - viewNormalMatrix = new Matrix3(), - - uniform = { value: null, needsUpdate: false }; - - this.uniform = uniform; - this.numPlanes = 0; - this.numIntersection = 0; - - this.init = function ( planes, enableLocalClipping, camera ) { - - const enabled = - planes.length !== 0 || - enableLocalClipping || - // enable state of previous frame - the clipping code has to - // run another frame in order to reset the state: - numGlobalPlanes !== 0 || - localClippingEnabled; - - localClippingEnabled = enableLocalClipping; - - globalState = projectPlanes( planes, camera, 0 ); - numGlobalPlanes = planes.length; - - return enabled; - - }; - - this.beginShadows = function () { - - renderingShadows = true; - projectPlanes( null ); - - }; - - this.endShadows = function () { - - renderingShadows = false; - resetGlobalState(); - - }; - - this.setState = function ( material, camera, useCache ) { - - const planes = material.clippingPlanes, - clipIntersection = material.clipIntersection, - clipShadows = material.clipShadows; - - const materialProperties = properties.get( material ); - - if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { - - // there's no local clipping - - if ( renderingShadows ) { - - // there's no global clipping - - projectPlanes( null ); - - } else { - - resetGlobalState(); - - } - - } else { - - const nGlobal = renderingShadows ? 0 : numGlobalPlanes, - lGlobal = nGlobal * 4; - - let dstArray = materialProperties.clippingState || null; - - uniform.value = dstArray; // ensure unique state - - dstArray = projectPlanes( planes, camera, lGlobal, useCache ); - - for ( let i = 0; i !== lGlobal; ++ i ) { - - dstArray[ i ] = globalState[ i ]; - - } - - materialProperties.clippingState = dstArray; - this.numIntersection = clipIntersection ? this.numPlanes : 0; - this.numPlanes += nGlobal; - - } - - - }; - - function resetGlobalState() { - - if ( uniform.value !== globalState ) { - - uniform.value = globalState; - uniform.needsUpdate = numGlobalPlanes > 0; - - } - - scope.numPlanes = numGlobalPlanes; - scope.numIntersection = 0; - - } - - function projectPlanes( planes, camera, dstOffset, skipTransform ) { - - const nPlanes = planes !== null ? planes.length : 0; - let dstArray = null; - - if ( nPlanes !== 0 ) { - - dstArray = uniform.value; - - if ( skipTransform !== true || dstArray === null ) { - - const flatSize = dstOffset + nPlanes * 4, - viewMatrix = camera.matrixWorldInverse; - - viewNormalMatrix.getNormalMatrix( viewMatrix ); - - if ( dstArray === null || dstArray.length < flatSize ) { - - dstArray = new Float32Array( flatSize ); - - } - - for ( let i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { - - plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); - - plane.normal.toArray( dstArray, i4 ); - dstArray[ i4 + 3 ] = plane.constant; - - } - - } - - uniform.value = dstArray; - uniform.needsUpdate = true; - - } - - scope.numPlanes = nPlanes; - scope.numIntersection = 0; - - return dstArray; - - } - - } - - function WebGLCubeMaps( renderer ) { - - let cubemaps = new WeakMap(); - - function mapTextureMapping( texture, mapping ) { - - if ( mapping === EquirectangularReflectionMapping ) { - - texture.mapping = CubeReflectionMapping; - - } else if ( mapping === EquirectangularRefractionMapping ) { - - texture.mapping = CubeRefractionMapping; - - } - - return texture; - - } - - function get( texture ) { - - if ( texture && texture.isTexture ) { - - const mapping = texture.mapping; - - if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) { - - if ( cubemaps.has( texture ) ) { - - const cubemap = cubemaps.get( texture ).texture; - return mapTextureMapping( cubemap, texture.mapping ); - - } else { - - const image = texture.image; - - if ( image && image.height > 0 ) { - - const currentRenderList = renderer.getRenderList(); - const currentRenderTarget = renderer.getRenderTarget(); - const currentRenderState = renderer.getRenderState(); - - const renderTarget = new WebGLCubeRenderTarget( image.height / 2 ); - renderTarget.fromEquirectangularTexture( renderer, texture ); - cubemaps.set( texture, renderTarget ); - - renderer.setRenderTarget( currentRenderTarget ); - renderer.setRenderList( currentRenderList ); - renderer.setRenderState( currentRenderState ); - - return mapTextureMapping( renderTarget.texture, texture.mapping ); - - } else { - - // image not yet ready. try the conversion next frame - - return null; - - } - - } - - } - - } - - return texture; - - } - - function dispose() { - - cubemaps = new WeakMap(); - - } - - return { - get: get, - dispose: dispose - }; - - } - - function WebGLExtensions( gl ) { - - const extensions = {}; - - return { - - has: function ( name ) { - - if ( extensions[ name ] !== undefined ) { - - return extensions[ name ] !== null; - - } - - let extension; - - switch ( name ) { - - case 'WEBGL_depth_texture': - extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); - break; - - case 'EXT_texture_filter_anisotropic': - extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); - break; - - case 'WEBGL_compressed_texture_s3tc': - extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); - break; - - case 'WEBGL_compressed_texture_pvrtc': - extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); - break; - - default: - extension = gl.getExtension( name ); - - } - - extensions[ name ] = extension; - - return extension !== null; - - }, - - get: function ( name ) { - - if ( ! this.has( name ) ) { - - console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); - - } - - return extensions[ name ]; - - } - - }; - - } - - function WebGLGeometries( gl, attributes, info, bindingStates ) { - - const geometries = new WeakMap(); - const wireframeAttributes = new WeakMap(); - - function onGeometryDispose( event ) { - - const geometry = event.target; - const buffergeometry = geometries.get( geometry ); - - if ( buffergeometry.index !== null ) { - - attributes.remove( buffergeometry.index ); - - } - - for ( const name in buffergeometry.attributes ) { - - attributes.remove( buffergeometry.attributes[ name ] ); - - } - - geometry.removeEventListener( 'dispose', onGeometryDispose ); - - geometries.delete( geometry ); - - const attribute = wireframeAttributes.get( buffergeometry ); - - if ( attribute ) { - - attributes.remove( attribute ); - wireframeAttributes.delete( buffergeometry ); - - } - - bindingStates.releaseStatesOfGeometry( geometry ); - - if ( geometry.isInstancedBufferGeometry === true ) { - - delete geometry._maxInstanceCount; - - } - - // - - info.memory.geometries --; - - } - - function get( object, geometry ) { - - let buffergeometry = geometries.get( geometry ); - - if ( buffergeometry ) return buffergeometry; - - geometry.addEventListener( 'dispose', onGeometryDispose ); - - if ( geometry.isBufferGeometry ) { - - buffergeometry = geometry; - - } else if ( geometry.isGeometry ) { - - if ( geometry._bufferGeometry === undefined ) { - - geometry._bufferGeometry = new BufferGeometry().setFromObject( object ); - - } - - buffergeometry = geometry._bufferGeometry; - - } - - geometries.set( geometry, buffergeometry ); - - info.memory.geometries ++; - - return buffergeometry; - - } - - function update( geometry ) { - - const geometryAttributes = geometry.attributes; - - // Updating index buffer in VAO now. See WebGLBindingStates. - - for ( const name in geometryAttributes ) { - - attributes.update( geometryAttributes[ name ], 34962 ); - - } - - // morph targets - - const morphAttributes = geometry.morphAttributes; - - for ( const name in morphAttributes ) { - - const array = morphAttributes[ name ]; - - for ( let i = 0, l = array.length; i < l; i ++ ) { - - attributes.update( array[ i ], 34962 ); - - } - - } - - } - - function updateWireframeAttribute( geometry ) { - - const indices = []; - - const geometryIndex = geometry.index; - const geometryPosition = geometry.attributes.position; - let version = 0; - - if ( geometryIndex !== null ) { - - const array = geometryIndex.array; - version = geometryIndex.version; - - for ( let i = 0, l = array.length; i < l; i += 3 ) { - - const a = array[ i + 0 ]; - const b = array[ i + 1 ]; - const c = array[ i + 2 ]; - - indices.push( a, b, b, c, c, a ); - - } - - } else { - - const array = geometryPosition.array; - version = geometryPosition.version; - - for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { - - const a = i + 0; - const b = i + 1; - const c = i + 2; - - indices.push( a, b, b, c, c, a ); - - } - - } - - const attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); - attribute.version = version; - - // Updating index buffer in VAO now. See WebGLBindingStates - - // - - const previousAttribute = wireframeAttributes.get( geometry ); - - if ( previousAttribute ) attributes.remove( previousAttribute ); - - // - - wireframeAttributes.set( geometry, attribute ); - - } - - function getWireframeAttribute( geometry ) { - - const currentAttribute = wireframeAttributes.get( geometry ); - - if ( currentAttribute ) { - - const geometryIndex = geometry.index; - - if ( geometryIndex !== null ) { - - // if the attribute is obsolete, create a new one - - if ( currentAttribute.version < geometryIndex.version ) { - - updateWireframeAttribute( geometry ); - - } - - } - - } else { - - updateWireframeAttribute( geometry ); - - } - - return wireframeAttributes.get( geometry ); - - } - - return { - - get: get, - update: update, - - getWireframeAttribute: getWireframeAttribute - - }; - - } - - function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - let mode; - - function setMode( value ) { - - mode = value; - - } - - let type, bytesPerElement; - - function setIndex( value ) { - - type = value.type; - bytesPerElement = value.bytesPerElement; - - } - - function render( start, count ) { - - gl.drawElements( mode, count, type, start * bytesPerElement ); - - info.update( count, mode, 1 ); - - } - - function renderInstances( start, count, primcount ) { - - if ( primcount === 0 ) return; - - let extension, methodName; - - if ( isWebGL2 ) { - - extension = gl; - methodName = 'drawElementsInstanced'; - - } else { - - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawElementsInstancedANGLE'; - - if ( extension === null ) { - - console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } - - } - - extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); - - info.update( count, mode, primcount ); - - } - - // - - this.setMode = setMode; - this.setIndex = setIndex; - this.render = render; - this.renderInstances = renderInstances; - - } - - function WebGLInfo( gl ) { - - const memory = { - geometries: 0, - textures: 0 - }; - - const render = { - frame: 0, - calls: 0, - triangles: 0, - points: 0, - lines: 0 - }; - - function update( count, mode, instanceCount ) { - - render.calls ++; - - switch ( mode ) { - - case 4: - render.triangles += instanceCount * ( count / 3 ); - break; - - case 1: - render.lines += instanceCount * ( count / 2 ); - break; - - case 3: - render.lines += instanceCount * ( count - 1 ); - break; - - case 2: - render.lines += instanceCount * count; - break; - - case 0: - render.points += instanceCount * count; - break; - - default: - console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); - break; - - } - - } - - function reset() { - - render.frame ++; - render.calls = 0; - render.triangles = 0; - render.points = 0; - render.lines = 0; - - } - - return { - memory: memory, - render: render, - programs: null, - autoReset: true, - reset: reset, - update: update - }; - - } - - function numericalSort( a, b ) { - - return a[ 0 ] - b[ 0 ]; - - } - - function absNumericalSort( a, b ) { - - return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); - - } - - function WebGLMorphtargets( gl ) { - - const influencesList = {}; - const morphInfluences = new Float32Array( 8 ); - - const workInfluences = []; - - for ( let i = 0; i < 8; i ++ ) { - - workInfluences[ i ] = [ i, 0 ]; - - } - - function update( object, geometry, material, program ) { - - const objectInfluences = object.morphTargetInfluences; - - // When object doesn't have morph target influences defined, we treat it as a 0-length array - // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences - - const length = objectInfluences === undefined ? 0 : objectInfluences.length; - - let influences = influencesList[ geometry.id ]; - - if ( influences === undefined ) { - - // initialise list - - influences = []; - - for ( let i = 0; i < length; i ++ ) { - - influences[ i ] = [ i, 0 ]; - - } - - influencesList[ geometry.id ] = influences; - - } - - // Collect influences - - for ( let i = 0; i < length; i ++ ) { - - const influence = influences[ i ]; - - influence[ 0 ] = i; - influence[ 1 ] = objectInfluences[ i ]; - - } - - influences.sort( absNumericalSort ); - - for ( let i = 0; i < 8; i ++ ) { - - if ( i < length && influences[ i ][ 1 ] ) { - - workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; - workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; - - } else { - - workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; - workInfluences[ i ][ 1 ] = 0; - - } - - } - - workInfluences.sort( numericalSort ); - - const morphTargets = material.morphTargets && geometry.morphAttributes.position; - const morphNormals = material.morphNormals && geometry.morphAttributes.normal; - - let morphInfluencesSum = 0; - - for ( let i = 0; i < 8; i ++ ) { - - const influence = workInfluences[ i ]; - const index = influence[ 0 ]; - const value = influence[ 1 ]; - - if ( index !== Number.MAX_SAFE_INTEGER && value ) { - - if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) { - - geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] ); - - } - - if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) { - - geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] ); - - } - - morphInfluences[ i ] = value; - morphInfluencesSum += value; - - } else { - - if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== undefined ) { - - geometry.deleteAttribute( 'morphTarget' + i ); - - } - - if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== undefined ) { - - geometry.deleteAttribute( 'morphNormal' + i ); - - } - - morphInfluences[ i ] = 0; - - } - - } - - // GLSL shader uses formula baseinfluence * base + sum(target * influence) - // This allows us to switch between absolute morphs and relative morphs without changing shader code - // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) - const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; - - program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); - program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); - - } - - return { - - update: update - - }; - - } - - function WebGLObjects( gl, geometries, attributes, info ) { - - let updateMap = new WeakMap(); - - function update( object ) { - - const frame = info.render.frame; - - const geometry = object.geometry; - const buffergeometry = geometries.get( object, geometry ); - - // Update once per frame - - if ( updateMap.get( buffergeometry ) !== frame ) { - - if ( geometry.isGeometry ) { - - buffergeometry.updateFromObject( object ); - - } - - geometries.update( buffergeometry ); - - updateMap.set( buffergeometry, frame ); - - } - - if ( object.isInstancedMesh ) { - - attributes.update( object.instanceMatrix, 34962 ); - - if ( object.instanceColor !== null ) { - - attributes.update( object.instanceColor, 34962 ); - - } - - } - - return buffergeometry; - - } - - function dispose() { - - updateMap = new WeakMap(); - - } - - return { - - update: update, - dispose: dispose - - }; - - } - - function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - - images = images !== undefined ? images : []; - mapping = mapping !== undefined ? mapping : CubeReflectionMapping; - format = format !== undefined ? format : RGBFormat; - - Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.flipY = false; - - } - - CubeTexture.prototype = Object.create( Texture.prototype ); - CubeTexture.prototype.constructor = CubeTexture; - - CubeTexture.prototype.isCubeTexture = true; - - Object.defineProperty( CubeTexture.prototype, 'images', { - - get: function () { - - return this.image; - - }, - - set: function ( value ) { - - this.image = value; - - } - - } ); - - function DataTexture2DArray( data, width, height, depth ) { - - Texture.call( this, null ); - - this.image = { data: data || null, width: width || 1, height: height || 1, depth: depth || 1 }; - - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; - - this.wrapR = ClampToEdgeWrapping; - - this.generateMipmaps = false; - this.flipY = false; - - this.needsUpdate = true; - - } - - DataTexture2DArray.prototype = Object.create( Texture.prototype ); - DataTexture2DArray.prototype.constructor = DataTexture2DArray; - DataTexture2DArray.prototype.isDataTexture2DArray = true; - - function DataTexture3D( data, width, height, depth ) { - - // We're going to add .setXXX() methods for setting properties later. - // Users can still set in DataTexture3D directly. - // - // const texture = new THREE.DataTexture3D( data, width, height, depth ); - // texture.anisotropy = 16; - // - // See #14839 - - Texture.call( this, null ); - - this.image = { data: data || null, width: width || 1, height: height || 1, depth: depth || 1 }; - - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; - - this.wrapR = ClampToEdgeWrapping; - - this.generateMipmaps = false; - this.flipY = false; - - this.needsUpdate = true; - - - } - - DataTexture3D.prototype = Object.create( Texture.prototype ); - DataTexture3D.prototype.constructor = DataTexture3D; - DataTexture3D.prototype.isDataTexture3D = true; - - /** - * Uniforms of a program. - * Those form a tree structure with a special top-level container for the root, - * which you get by calling 'new WebGLUniforms( gl, program )'. - * - * - * Properties of inner nodes including the top-level container: - * - * .seq - array of nested uniforms - * .map - nested uniforms by name - * - * - * Methods of all nodes except the top-level container: - * - * .setValue( gl, value, [textures] ) - * - * uploads a uniform value(s) - * the 'textures' parameter is needed for sampler uniforms - * - * - * Static methods of the top-level container (textures factorizations): - * - * .upload( gl, seq, values, textures ) - * - * sets uniforms in 'seq' to 'values[id].value' - * - * .seqWithValue( seq, values ) : filteredSeq - * - * filters 'seq' entries with corresponding entry in values - * - * - * Methods of the top-level container (textures factorizations): - * - * .setValue( gl, name, value, textures ) - * - * sets uniform with name 'name' to 'value' - * - * .setOptional( gl, obj, prop ) - * - * like .set for an optional property of the object - * - */ - - const emptyTexture = new Texture(); - const emptyTexture2dArray = new DataTexture2DArray(); - const emptyTexture3d = new DataTexture3D(); - const emptyCubeTexture = new CubeTexture(); - - // --- Utilities --- - - // Array Caches (provide typed arrays for temporary by size) - - const arrayCacheF32 = []; - const arrayCacheI32 = []; - - // Float32Array caches used for uploading Matrix uniforms - - const mat4array = new Float32Array( 16 ); - const mat3array = new Float32Array( 9 ); - const mat2array = new Float32Array( 4 ); - - // Flattening for arrays of vectors and matrices - - function flatten( array, nBlocks, blockSize ) { - - const firstElem = array[ 0 ]; - - if ( firstElem <= 0 || firstElem > 0 ) return array; - // unoptimized: ! isNaN( firstElem ) - // see http://jacksondunstan.com/articles/983 - - const n = nBlocks * blockSize; - let r = arrayCacheF32[ n ]; - - if ( r === undefined ) { - - r = new Float32Array( n ); - arrayCacheF32[ n ] = r; - - } - - if ( nBlocks !== 0 ) { - - firstElem.toArray( r, 0 ); - - for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { - - offset += blockSize; - array[ i ].toArray( r, offset ); - - } - - } - - return r; - - } - - function arraysEqual( a, b ) { - - if ( a.length !== b.length ) return false; - - for ( let i = 0, l = a.length; i < l; i ++ ) { - - if ( a[ i ] !== b[ i ] ) return false; - - } - - return true; - - } - - function copyArray( a, b ) { - - for ( let i = 0, l = b.length; i < l; i ++ ) { - - a[ i ] = b[ i ]; - - } - - } - - // Texture unit allocation - - function allocTexUnits( textures, n ) { - - let r = arrayCacheI32[ n ]; - - if ( r === undefined ) { - - r = new Int32Array( n ); - arrayCacheI32[ n ] = r; - - } - - for ( let i = 0; i !== n; ++ i ) { - - r[ i ] = textures.allocateTextureUnit(); - - } - - return r; - - } - - // --- Setters --- - - // Note: Defining these methods externally, because they come in a bunch - // and this way their names minify. - - // Single scalar - - function setValueV1f( gl, v ) { - - const cache = this.cache; - - if ( cache[ 0 ] === v ) return; - - gl.uniform1f( this.addr, v ); - - cache[ 0 ] = v; - - } - - // Single float vector (from flat array or THREE.VectorN) - - function setValueV2f( gl, v ) { - - const cache = this.cache; - - if ( v.x !== undefined ) { - - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { - - gl.uniform2f( this.addr, v.x, v.y ); - - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - - } - - } else { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform2fv( this.addr, v ); - - copyArray( cache, v ); - - } - - } - - function setValueV3f( gl, v ) { - - const cache = this.cache; - - if ( v.x !== undefined ) { - - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { - - gl.uniform3f( this.addr, v.x, v.y, v.z ); - - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; - - } - - } else if ( v.r !== undefined ) { - - if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { - - gl.uniform3f( this.addr, v.r, v.g, v.b ); - - cache[ 0 ] = v.r; - cache[ 1 ] = v.g; - cache[ 2 ] = v.b; - - } - - } else { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform3fv( this.addr, v ); - - copyArray( cache, v ); - - } - - } - - function setValueV4f( gl, v ) { - - const cache = this.cache; - - if ( v.x !== undefined ) { - - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { - - gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); - - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; - cache[ 3 ] = v.w; - - } - - } else { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform4fv( this.addr, v ); - - copyArray( cache, v ); - - } - - } - - // Single matrix (from flat array or MatrixN) - - function setValueM2( gl, v ) { - - const cache = this.cache; - const elements = v.elements; - - if ( elements === undefined ) { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniformMatrix2fv( this.addr, false, v ); - - copyArray( cache, v ); - - } else { - - if ( arraysEqual( cache, elements ) ) return; - - mat2array.set( elements ); - - gl.uniformMatrix2fv( this.addr, false, mat2array ); - - copyArray( cache, elements ); - - } - - } - - function setValueM3( gl, v ) { - - const cache = this.cache; - const elements = v.elements; - - if ( elements === undefined ) { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniformMatrix3fv( this.addr, false, v ); - - copyArray( cache, v ); - - } else { - - if ( arraysEqual( cache, elements ) ) return; - - mat3array.set( elements ); - - gl.uniformMatrix3fv( this.addr, false, mat3array ); - - copyArray( cache, elements ); - - } - - } - - function setValueM4( gl, v ) { - - const cache = this.cache; - const elements = v.elements; - - if ( elements === undefined ) { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniformMatrix4fv( this.addr, false, v ); - - copyArray( cache, v ); - - } else { - - if ( arraysEqual( cache, elements ) ) return; - - mat4array.set( elements ); - - gl.uniformMatrix4fv( this.addr, false, mat4array ); - - copyArray( cache, elements ); - - } - - } - - // Single texture (2D / Cube) - - function setValueT1( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.safeSetTexture2D( v || emptyTexture, unit ); - - } - - function setValueT2DArray1( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.setTexture2DArray( v || emptyTexture2dArray, unit ); - - } - - function setValueT3D1( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.setTexture3D( v || emptyTexture3d, unit ); - - } - - function setValueT6( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.safeSetTextureCube( v || emptyCubeTexture, unit ); - - } - - // Integer / Boolean vectors or arrays thereof (always flat arrays) - - function setValueV1i( gl, v ) { - - const cache = this.cache; - - if ( cache[ 0 ] === v ) return; - - gl.uniform1i( this.addr, v ); - - cache[ 0 ] = v; - - } - - function setValueV2i( gl, v ) { - - const cache = this.cache; - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform2iv( this.addr, v ); - - copyArray( cache, v ); - - } - - function setValueV3i( gl, v ) { - - const cache = this.cache; - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform3iv( this.addr, v ); - - copyArray( cache, v ); - - } - - function setValueV4i( gl, v ) { - - const cache = this.cache; - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform4iv( this.addr, v ); - - copyArray( cache, v ); - - } - - // uint - - function setValueV1ui( gl, v ) { - - const cache = this.cache; - - if ( cache[ 0 ] === v ) return; - - gl.uniform1ui( this.addr, v ); - - cache[ 0 ] = v; - - } - - // Helper to pick the right setter for the singular case - - function getSingularSetter( type ) { - - switch ( type ) { - - case 0x1406: return setValueV1f; // FLOAT - case 0x8b50: return setValueV2f; // _VEC2 - case 0x8b51: return setValueV3f; // _VEC3 - case 0x8b52: return setValueV4f; // _VEC4 - - case 0x8b5a: return setValueM2; // _MAT2 - case 0x8b5b: return setValueM3; // _MAT3 - case 0x8b5c: return setValueM4; // _MAT4 - - case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 - - case 0x1405: return setValueV1ui; // UINT - - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1; - - case 0x8b5f: // SAMPLER_3D - case 0x8dcb: // INT_SAMPLER_3D - case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D - return setValueT3D1; - - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6; - - case 0x8dc1: // SAMPLER_2D_ARRAY - case 0x8dcf: // INT_SAMPLER_2D_ARRAY - case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY - case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW - return setValueT2DArray1; - - } - - } - - // Array of scalars - function setValueV1fArray( gl, v ) { - - gl.uniform1fv( this.addr, v ); - - } - - // Integer / Boolean vectors or arrays thereof (always flat arrays) - function setValueV1iArray( gl, v ) { - - gl.uniform1iv( this.addr, v ); - - } - - function setValueV2iArray( gl, v ) { - - gl.uniform2iv( this.addr, v ); - - } - - function setValueV3iArray( gl, v ) { - - gl.uniform3iv( this.addr, v ); - - } - - function setValueV4iArray( gl, v ) { - - gl.uniform4iv( this.addr, v ); - - } - - - // Array of vectors (flat or from THREE classes) - - function setValueV2fArray( gl, v ) { - - const data = flatten( v, this.size, 2 ); - - gl.uniform2fv( this.addr, data ); - - } - - function setValueV3fArray( gl, v ) { - - const data = flatten( v, this.size, 3 ); - - gl.uniform3fv( this.addr, data ); - - } - - function setValueV4fArray( gl, v ) { - - const data = flatten( v, this.size, 4 ); - - gl.uniform4fv( this.addr, data ); - - } - - // Array of matrices (flat or from THREE clases) - - function setValueM2Array( gl, v ) { - - const data = flatten( v, this.size, 4 ); - - gl.uniformMatrix2fv( this.addr, false, data ); - - } - - function setValueM3Array( gl, v ) { - - const data = flatten( v, this.size, 9 ); - - gl.uniformMatrix3fv( this.addr, false, data ); - - } - - function setValueM4Array( gl, v ) { - - const data = flatten( v, this.size, 16 ); - - gl.uniformMatrix4fv( this.addr, false, data ); - - } - - // Array of textures (2D / Cube) - - function setValueT1Array( gl, v, textures ) { - - const n = v.length; - - const units = allocTexUnits( textures, n ); - - gl.uniform1iv( this.addr, units ); - - for ( let i = 0; i !== n; ++ i ) { - - textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] ); - - } - - } - - function setValueT6Array( gl, v, textures ) { - - const n = v.length; - - const units = allocTexUnits( textures, n ); - - gl.uniform1iv( this.addr, units ); - - for ( let i = 0; i !== n; ++ i ) { - - textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); - - } - - } - - // Helper to pick the right setter for a pure (bottom-level) array - - function getPureArraySetter( type ) { - - switch ( type ) { - - case 0x1406: return setValueV1fArray; // FLOAT - case 0x8b50: return setValueV2fArray; // _VEC2 - case 0x8b51: return setValueV3fArray; // _VEC3 - case 0x8b52: return setValueV4fArray; // _VEC4 - - case 0x8b5a: return setValueM2Array; // _MAT2 - case 0x8b5b: return setValueM3Array; // _MAT3 - case 0x8b5c: return setValueM4Array; // _MAT4 - - case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 - - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1Array; - - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6Array; - - } - - } - - // --- Uniform Classes --- - - function SingleUniform( id, activeInfo, addr ) { - - this.id = id; - this.addr = addr; - this.cache = []; - this.setValue = getSingularSetter( activeInfo.type ); - - // this.path = activeInfo.name; // DEBUG - - } - - function PureArrayUniform( id, activeInfo, addr ) { - - this.id = id; - this.addr = addr; - this.cache = []; - this.size = activeInfo.size; - this.setValue = getPureArraySetter( activeInfo.type ); - - // this.path = activeInfo.name; // DEBUG - - } - - PureArrayUniform.prototype.updateCache = function ( data ) { - - const cache = this.cache; - - if ( data instanceof Float32Array && cache.length !== data.length ) { - - this.cache = new Float32Array( data.length ); - - } - - copyArray( cache, data ); - - }; - - function StructuredUniform( id ) { - - this.id = id; - - this.seq = []; - this.map = {}; - - } - - StructuredUniform.prototype.setValue = function ( gl, value, textures ) { - - const seq = this.seq; - - for ( let i = 0, n = seq.length; i !== n; ++ i ) { - - const u = seq[ i ]; - u.setValue( gl, value[ u.id ], textures ); - - } - - }; - - // --- Top-level --- - - // Parser - builds up the property tree from the path strings - - const RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g; - - // extracts - // - the identifier (member name or array index) - // - followed by an optional right bracket (found when array index) - // - followed by an optional left bracket or dot (type of subscript) - // - // Note: These portions can be read in a non-overlapping fashion and - // allow straightforward parsing of the hierarchy that WebGL encodes - // in the uniform names. - - function addUniform( container, uniformObject ) { - - container.seq.push( uniformObject ); - container.map[ uniformObject.id ] = uniformObject; - - } - - function parseUniform( activeInfo, addr, container ) { - - const path = activeInfo.name, - pathLength = path.length; - - // reset RegExp object, because of the early exit of a previous run - RePathPart.lastIndex = 0; - - while ( true ) { - - const match = RePathPart.exec( path ), - matchEnd = RePathPart.lastIndex; - - let id = match[ 1 ]; - const idIsIndex = match[ 2 ] === ']', - subscript = match[ 3 ]; - - if ( idIsIndex ) id = id | 0; // convert to integer - - if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { - - // bare name or "pure" bottom-level array "[0]" suffix - - addUniform( container, subscript === undefined ? - new SingleUniform( id, activeInfo, addr ) : - new PureArrayUniform( id, activeInfo, addr ) ); - - break; - - } else { - - // step into inner node / create it in case it doesn't exist - - const map = container.map; - let next = map[ id ]; - - if ( next === undefined ) { - - next = new StructuredUniform( id ); - addUniform( container, next ); - - } - - container = next; - - } - - } - - } - - // Root Container - - function WebGLUniforms( gl, program ) { - - this.seq = []; - this.map = {}; - - const n = gl.getProgramParameter( program, 35718 ); - - for ( let i = 0; i < n; ++ i ) { - - const info = gl.getActiveUniform( program, i ), - addr = gl.getUniformLocation( program, info.name ); - - parseUniform( info, addr, this ); - - } - - } - - WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) { - - const u = this.map[ name ]; - - if ( u !== undefined ) u.setValue( gl, value, textures ); - - }; - - WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { - - const v = object[ name ]; - - if ( v !== undefined ) this.setValue( gl, name, v ); - - }; - - - // Static interface - - WebGLUniforms.upload = function ( gl, seq, values, textures ) { - - for ( let i = 0, n = seq.length; i !== n; ++ i ) { - - const u = seq[ i ], - v = values[ u.id ]; - - if ( v.needsUpdate !== false ) { - - // note: always updating when .needsUpdate is undefined - u.setValue( gl, v.value, textures ); - - } - - } - - }; - - WebGLUniforms.seqWithValue = function ( seq, values ) { - - const r = []; - - for ( let i = 0, n = seq.length; i !== n; ++ i ) { - - const u = seq[ i ]; - if ( u.id in values ) r.push( u ); - - } - - return r; - - }; - - function WebGLShader( gl, type, string ) { - - const shader = gl.createShader( type ); - - gl.shaderSource( shader, string ); - gl.compileShader( shader ); - - return shader; - - } - - let programIdCount = 0; - - function addLineNumbers( string ) { - - const lines = string.split( '\n' ); - - for ( let i = 0; i < lines.length; i ++ ) { - - lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; - - } - - return lines.join( '\n' ); - - } - - function getEncodingComponents( encoding ) { - - switch ( encoding ) { - - case LinearEncoding: - return [ 'Linear', '( value )' ]; - case sRGBEncoding: - return [ 'sRGB', '( value )' ]; - case RGBEEncoding: - return [ 'RGBE', '( value )' ]; - case RGBM7Encoding: - return [ 'RGBM', '( value, 7.0 )' ]; - case RGBM16Encoding: - return [ 'RGBM', '( value, 16.0 )' ]; - case RGBDEncoding: - return [ 'RGBD', '( value, 256.0 )' ]; - case GammaEncoding: - return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ]; - case LogLuvEncoding: - return [ 'LogLuv', '( value )' ]; - default: - console.warn( 'THREE.WebGLProgram: Unsupported encoding:', encoding ); - return [ 'Linear', '( value )' ]; - - } - - } - - function getShaderErrors( gl, shader, type ) { - - const status = gl.getShaderParameter( shader, 35713 ); - const log = gl.getShaderInfoLog( shader ).trim(); - - if ( status && log === '' ) return ''; - - // --enable-privileged-webgl-extension - // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); - - const source = gl.getShaderSource( shader ); - - return 'THREE.WebGLShader: gl.getShaderInfoLog() ' + type + '\n' + log + addLineNumbers( source ); - - } - - function getTexelDecodingFunction( functionName, encoding ) { - - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }'; - - } - - function getTexelEncodingFunction( functionName, encoding ) { - - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }'; - - } - - function getToneMappingFunction( functionName, toneMapping ) { - - let toneMappingName; - - switch ( toneMapping ) { - - case LinearToneMapping: - toneMappingName = 'Linear'; - break; - - case ReinhardToneMapping: - toneMappingName = 'Reinhard'; - break; - - case CineonToneMapping: - toneMappingName = 'OptimizedCineon'; - break; - - case ACESFilmicToneMapping: - toneMappingName = 'ACESFilmic'; - break; - - case CustomToneMapping: - toneMappingName = 'Custom'; - break; - - default: - console.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping ); - toneMappingName = 'Linear'; - - } - - return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; - - } - - function generateExtensions( parameters ) { - - const chunks = [ - ( parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '', - ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', - ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '', - ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '' - ]; - - return chunks.filter( filterEmptyLine ).join( '\n' ); - - } - - function generateDefines( defines ) { - - const chunks = []; - - for ( const name in defines ) { - - const value = defines[ name ]; - - if ( value === false ) continue; - - chunks.push( '#define ' + name + ' ' + value ); - - } - - return chunks.join( '\n' ); - - } - - function fetchAttributeLocations( gl, program ) { - - const attributes = {}; - - const n = gl.getProgramParameter( program, 35721 ); - - for ( let i = 0; i < n; i ++ ) { - - const info = gl.getActiveAttrib( program, i ); - const name = info.name; - - // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); - - attributes[ name ] = gl.getAttribLocation( program, name ); - - } - - return attributes; - - } - - function filterEmptyLine( string ) { - - return string !== ''; - - } - - function replaceLightNums( string, parameters ) { - - return string - .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) - .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) - .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) - .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) - .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) - .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) - .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) - .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); - - } - - function replaceClippingPlaneNums( string, parameters ) { - - return string - .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) - .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); - - } - - // Resolve Includes - - const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; - - function resolveIncludes( string ) { - - return string.replace( includePattern, includeReplacer ); - - } - - function includeReplacer( match, include ) { - - const string = ShaderChunk[ include ]; - - if ( string === undefined ) { - - throw new Error( 'Can not resolve #include <' + include + '>' ); - - } - - return resolveIncludes( string ); - - } - - // Unroll Loops - - const deprecatedUnrollLoopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; - const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g; - - function unrollLoops( string ) { - - return string - .replace( unrollLoopPattern, loopReplacer ) - .replace( deprecatedUnrollLoopPattern, deprecatedLoopReplacer ); - - } - - function deprecatedLoopReplacer( match, start, end, snippet ) { - - console.warn( 'WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.' ); - return loopReplacer( match, start, end, snippet ); - - } - - function loopReplacer( match, start, end, snippet ) { - - let string = ''; - - for ( let i = parseInt( start ); i < parseInt( end ); i ++ ) { - - string += snippet - .replace( /\[\s*i\s*\]/g, '[ ' + i + ' ]' ) - .replace( /UNROLLED_LOOP_INDEX/g, i ); - - } - - return string; - - } - - // - - function generatePrecision( parameters ) { - - let precisionstring = "precision " + parameters.precision + " float;\nprecision " + parameters.precision + " int;"; - - if ( parameters.precision === "highp" ) { - - precisionstring += "\n#define HIGH_PRECISION"; - - } else if ( parameters.precision === "mediump" ) { - - precisionstring += "\n#define MEDIUM_PRECISION"; - - } else if ( parameters.precision === "lowp" ) { - - precisionstring += "\n#define LOW_PRECISION"; - - } - - return precisionstring; - - } - - function generateShadowMapTypeDefine( parameters ) { - - let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; - - if ( parameters.shadowMapType === PCFShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; - - } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; - - } else if ( parameters.shadowMapType === VSMShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; - - } - - return shadowMapTypeDefine; - - } - - function generateEnvMapTypeDefine( parameters ) { - - let envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - - if ( parameters.envMap ) { - - switch ( parameters.envMapMode ) { - - case CubeReflectionMapping: - case CubeRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - break; - - case CubeUVReflectionMapping: - case CubeUVRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; - break; - - } - - } - - return envMapTypeDefine; - - } - - function generateEnvMapModeDefine( parameters ) { - - let envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; - - if ( parameters.envMap ) { - - switch ( parameters.envMapMode ) { - - case CubeRefractionMapping: - case CubeUVRefractionMapping: - - envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; - break; - - } - - } - - return envMapModeDefine; - - } - - function generateEnvMapBlendingDefine( parameters ) { - - let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; - - if ( parameters.envMap ) { - - switch ( parameters.combine ) { - - case MultiplyOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - break; - - case MixOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; - break; - - case AddOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; - break; - - } - - } - - return envMapBlendingDefine; - - } - - function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { - - const gl = renderer.getContext(); - - const defines = parameters.defines; - - let vertexShader = parameters.vertexShader; - let fragmentShader = parameters.fragmentShader; - - const shadowMapTypeDefine = generateShadowMapTypeDefine( parameters ); - const envMapTypeDefine = generateEnvMapTypeDefine( parameters ); - const envMapModeDefine = generateEnvMapModeDefine( parameters ); - const envMapBlendingDefine = generateEnvMapBlendingDefine( parameters ); - - - const gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; - - const customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters ); - - const customDefines = generateDefines( defines ); - - const program = gl.createProgram(); - - let prefixVertex, prefixFragment; - let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + "\n" : ''; - - if ( parameters.isRawShaderMaterial ) { - - prefixVertex = [ - - customDefines - - ].filter( filterEmptyLine ).join( '\n' ); - - if ( prefixVertex.length > 0 ) { - - prefixVertex += '\n'; - - } - - prefixFragment = [ - - customExtensions, - customDefines - - ].filter( filterEmptyLine ).join( '\n' ); - - if ( prefixFragment.length > 0 ) { - - prefixFragment += '\n'; - - } - - } else { - - prefixVertex = [ - - generatePrecision( parameters ), - - '#define SHADER_NAME ' + parameters.shaderName, - - customDefines, - - parameters.instancing ? '#define USE_INSTANCING' : '', - parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', - - parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', - - '#define GAMMA_FACTOR ' + gammaFactorDefine, - - '#define MAX_BONES ' + parameters.maxBones, - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - - parameters.map ? '#define USE_MAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', - - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - - parameters.flatShading ? '#define FLAT_SHADED' : '', - - parameters.skinning ? '#define USE_SKINNING' : '', - parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', - - parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', - parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', - - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - - parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', - - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - - 'uniform mat4 modelMatrix;', - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform mat4 viewMatrix;', - 'uniform mat3 normalMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', - - '#ifdef USE_INSTANCING', - - ' attribute mat4 instanceMatrix;', - - '#endif', - - '#ifdef USE_INSTANCING_COLOR', - - ' attribute vec3 instanceColor;', - - '#endif', - - 'attribute vec3 position;', - 'attribute vec3 normal;', - 'attribute vec2 uv;', - - '#ifdef USE_TANGENT', - - ' attribute vec4 tangent;', - - '#endif', - - '#ifdef USE_COLOR', - - ' attribute vec3 color;', - - '#endif', - - '#ifdef USE_MORPHTARGETS', - - ' attribute vec3 morphTarget0;', - ' attribute vec3 morphTarget1;', - ' attribute vec3 morphTarget2;', - ' attribute vec3 morphTarget3;', - - ' #ifdef USE_MORPHNORMALS', - - ' attribute vec3 morphNormal0;', - ' attribute vec3 morphNormal1;', - ' attribute vec3 morphNormal2;', - ' attribute vec3 morphNormal3;', - - ' #else', - - ' attribute vec3 morphTarget4;', - ' attribute vec3 morphTarget5;', - ' attribute vec3 morphTarget6;', - ' attribute vec3 morphTarget7;', - - ' #endif', - - '#endif', - - '#ifdef USE_SKINNING', - - ' attribute vec4 skinIndex;', - ' attribute vec4 skinWeight;', - - '#endif', - - '\n' - - ].filter( filterEmptyLine ).join( '\n' ); - - prefixFragment = [ - - customExtensions, - - generatePrecision( parameters ), - - '#define SHADER_NAME ' + parameters.shaderName, - - customDefines, - - parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest + ( parameters.alphaTest % 1 ? '' : '.0' ) : '', // add '.0' if integer - - '#define GAMMA_FACTOR ' + gammaFactorDefine, - - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - - parameters.map ? '#define USE_MAP' : '', - parameters.matcap ? '#define USE_MATCAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapTypeDefine : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.envMap ? '#define ' + envMapBlendingDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - - parameters.sheen ? '#define USE_SHEEN' : '', - parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', - - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - - parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', - - parameters.flatShading ? '#define FLAT_SHADED' : '', - - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', - - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - - parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', - - parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', - - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - - ( ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ) ? '#define TEXTURE_LOD_EXT' : '', - - 'uniform mat4 viewMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', - - ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', - ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below - ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', - - parameters.dithering ? '#define DITHERING' : '', - - ShaderChunk[ 'encodings_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below - parameters.map ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', - parameters.matcap ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '', - parameters.envMap ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', - parameters.emissiveMap ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', - parameters.lightMap ? getTexelDecodingFunction( 'lightMapTexelToLinear', parameters.lightMapEncoding ) : '', - getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ), - - parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', - - '\n' - - ].filter( filterEmptyLine ).join( '\n' ); - - } - - vertexShader = resolveIncludes( vertexShader ); - vertexShader = replaceLightNums( vertexShader, parameters ); - vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); - - fragmentShader = resolveIncludes( fragmentShader ); - fragmentShader = replaceLightNums( fragmentShader, parameters ); - fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); - - vertexShader = unrollLoops( vertexShader ); - fragmentShader = unrollLoops( fragmentShader ); - - if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { - - // GLSL 3.0 conversion for built-in materials and ShaderMaterial - - versionString = '#version 300 es\n'; - - prefixVertex = [ - '#define attribute in', - '#define varying out', - '#define texture2D texture' - ].join( '\n' ) + '\n' + prefixVertex; - - prefixFragment = [ - '#define varying in', - ( parameters.glslVersion === GLSL3 ) ? '' : 'out highp vec4 pc_fragColor;', - ( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor', - '#define gl_FragDepthEXT gl_FragDepth', - '#define texture2D texture', - '#define textureCube texture', - '#define texture2DProj textureProj', - '#define texture2DLodEXT textureLod', - '#define texture2DProjLodEXT textureProjLod', - '#define textureCubeLodEXT textureLod', - '#define texture2DGradEXT textureGrad', - '#define texture2DProjGradEXT textureProjGrad', - '#define textureCubeGradEXT textureGrad' - ].join( '\n' ) + '\n' + prefixFragment; - - } - - const vertexGlsl = versionString + prefixVertex + vertexShader; - const fragmentGlsl = versionString + prefixFragment + fragmentShader; - - // console.log( '*VERTEX*', vertexGlsl ); - // console.log( '*FRAGMENT*', fragmentGlsl ); - - const glVertexShader = WebGLShader( gl, 35633, vertexGlsl ); - const glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl ); - - gl.attachShader( program, glVertexShader ); - gl.attachShader( program, glFragmentShader ); - - // Force a particular attribute to index 0. - - if ( parameters.index0AttributeName !== undefined ) { - - gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); - - } else if ( parameters.morphTargets === true ) { - - // programs with morphTargets displace position out of attribute 0 - gl.bindAttribLocation( program, 0, 'position' ); - - } - - gl.linkProgram( program ); - - // check for link errors - if ( renderer.debug.checkShaderErrors ) { - - const programLog = gl.getProgramInfoLog( program ).trim(); - const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); - const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); - - let runnable = true; - let haveDiagnostics = true; - - if ( gl.getProgramParameter( program, 35714 ) === false ) { - - runnable = false; - - const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); - const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); - - console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), '35715', gl.getProgramParameter( program, 35715 ), 'gl.getProgramInfoLog', programLog, vertexErrors, fragmentErrors ); - - } else if ( programLog !== '' ) { - - console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); - - } else if ( vertexLog === '' || fragmentLog === '' ) { - - haveDiagnostics = false; - - } - - if ( haveDiagnostics ) { - - this.diagnostics = { - - runnable: runnable, - - programLog: programLog, - - vertexShader: { - - log: vertexLog, - prefix: prefixVertex - - }, - - fragmentShader: { - - log: fragmentLog, - prefix: prefixFragment - - } - - }; - - } - - } - - // Clean up - - // Crashes in iOS9 and iOS10. #18402 - // gl.detachShader( program, glVertexShader ); - // gl.detachShader( program, glFragmentShader ); - - gl.deleteShader( glVertexShader ); - gl.deleteShader( glFragmentShader ); - - // set up caching for uniform locations - - let cachedUniforms; - - this.getUniforms = function () { - - if ( cachedUniforms === undefined ) { - - cachedUniforms = new WebGLUniforms( gl, program ); - - } - - return cachedUniforms; - - }; - - // set up caching for attribute locations - - let cachedAttributes; - - this.getAttributes = function () { - - if ( cachedAttributes === undefined ) { - - cachedAttributes = fetchAttributeLocations( gl, program ); - - } - - return cachedAttributes; - - }; - - // free resource - - this.destroy = function () { - - bindingStates.releaseStatesOfProgram( this ); - - gl.deleteProgram( program ); - this.program = undefined; - - }; - - // - - this.name = parameters.shaderName; - this.id = programIdCount ++; - this.cacheKey = cacheKey; - this.usedTimes = 1; - this.program = program; - this.vertexShader = glVertexShader; - this.fragmentShader = glFragmentShader; - - return this; - - } - - function WebGLPrograms( renderer, cubemaps, extensions, capabilities, bindingStates, clipping ) { - - const programs = []; - - const isWebGL2 = capabilities.isWebGL2; - const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; - const floatVertexTextures = capabilities.floatVertexTextures; - const maxVertexUniforms = capabilities.maxVertexUniforms; - const vertexTextures = capabilities.vertexTextures; - - let precision = capabilities.precision; - - const shaderIDs = { - MeshDepthMaterial: 'depth', - MeshDistanceMaterial: 'distanceRGBA', - MeshNormalMaterial: 'normal', - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - MeshToonMaterial: 'toon', - MeshStandardMaterial: 'physical', - MeshPhysicalMaterial: 'physical', - MeshMatcapMaterial: 'matcap', - LineBasicMaterial: 'basic', - LineDashedMaterial: 'dashed', - PointsMaterial: 'points', - ShadowMaterial: 'shadow', - SpriteMaterial: 'sprite' - }; - - const parameterNames = [ - "precision", "isWebGL2", "supportsVertexTextures", "outputEncoding", "instancing", "instancingColor", - "map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding", "envMapCubeUV", - "lightMap", "lightMapEncoding", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "tangentSpaceNormalMap", "clearcoatMap", "clearcoatRoughnessMap", "clearcoatNormalMap", "displacementMap", "specularMap", - "roughnessMap", "metalnessMap", "gradientMap", - "alphaMap", "combine", "vertexColors", "vertexTangents", "vertexUvs", "uvsVertexOnly", "fog", "useFog", "fogExp2", - "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", - "maxBones", "useVertexTexture", "morphTargets", "morphNormals", - "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", - "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights", - "numDirLightShadows", "numPointLightShadows", "numSpotLightShadows", - "shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights', - "alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering", - "sheen", "transmissionMap" - ]; - - function getMaxBones( object ) { - - const skeleton = object.skeleton; - const bones = skeleton.bones; - - if ( floatVertexTextures ) { - - return 1024; - - } else { - - // default for when object is not specified - // ( for example when prebuilding shader to be used with multiple objects ) - // - // - leave some extra space for other uniforms - // - limit here is ANGLE's 254 max uniform vectors - // (up to 54 should be safe) - - const nVertexUniforms = maxVertexUniforms; - const nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); - - const maxBones = Math.min( nVertexMatrices, bones.length ); - - if ( maxBones < bones.length ) { - - console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); - return 0; - - } - - return maxBones; - - } - - } - - function getTextureEncodingFromMap( map ) { - - let encoding; - - if ( ! map ) { - - encoding = LinearEncoding; - - } else if ( map.isTexture ) { - - encoding = map.encoding; - - } else if ( map.isWebGLRenderTarget ) { - - console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." ); - encoding = map.texture.encoding; - - } - - return encoding; - - } - - function getParameters( material, lights, shadows, scene, object ) { - - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; - - const envMap = cubemaps.get( material.envMap || environment ); - - const shaderID = shaderIDs[ material.type ]; - - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) - - const maxBones = object.isSkinnedMesh ? getMaxBones( object ) : 0; - - if ( material.precision !== null ) { - - precision = capabilities.getMaxPrecision( material.precision ); - - if ( precision !== material.precision ) { - - console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); - - } - - } - - let vertexShader, fragmentShader; - - if ( shaderID ) { - - const shader = ShaderLib[ shaderID ]; - - vertexShader = shader.vertexShader; - fragmentShader = shader.fragmentShader; - - } else { - - vertexShader = material.vertexShader; - fragmentShader = material.fragmentShader; - - } - - const currentRenderTarget = renderer.getRenderTarget(); - - const parameters = { - - isWebGL2: isWebGL2, - - shaderID: shaderID, - shaderName: material.type, - - vertexShader: vertexShader, - fragmentShader: fragmentShader, - defines: material.defines, - - isRawShaderMaterial: material.isRawShaderMaterial === true, - glslVersion: material.glslVersion, - - precision: precision, - - instancing: object.isInstancedMesh === true, - instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, - - supportsVertexTextures: vertexTextures, - outputEncoding: ( currentRenderTarget !== null ) ? getTextureEncodingFromMap( currentRenderTarget.texture ) : renderer.outputEncoding, - map: !! material.map, - mapEncoding: getTextureEncodingFromMap( material.map ), - matcap: !! material.matcap, - matcapEncoding: getTextureEncodingFromMap( material.matcap ), - envMap: !! envMap, - envMapMode: envMap && envMap.mapping, - envMapEncoding: getTextureEncodingFromMap( envMap ), - envMapCubeUV: ( !! envMap ) && ( ( envMap.mapping === CubeUVReflectionMapping ) || ( envMap.mapping === CubeUVRefractionMapping ) ), - lightMap: !! material.lightMap, - lightMapEncoding: getTextureEncodingFromMap( material.lightMap ), - aoMap: !! material.aoMap, - emissiveMap: !! material.emissiveMap, - emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap ), - bumpMap: !! material.bumpMap, - normalMap: !! material.normalMap, - objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, - tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, - clearcoatMap: !! material.clearcoatMap, - clearcoatRoughnessMap: !! material.clearcoatRoughnessMap, - clearcoatNormalMap: !! material.clearcoatNormalMap, - displacementMap: !! material.displacementMap, - roughnessMap: !! material.roughnessMap, - metalnessMap: !! material.metalnessMap, - specularMap: !! material.specularMap, - alphaMap: !! material.alphaMap, - - gradientMap: !! material.gradientMap, - - sheen: !! material.sheen, - - transmissionMap: !! material.transmissionMap, - - combine: material.combine, - - vertexTangents: ( material.normalMap && material.vertexTangents ), - vertexColors: material.vertexColors, - vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap, - uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || !! material.transmissionMap ) && !! material.displacementMap, - - fog: !! fog, - useFog: material.fog, - fogExp2: ( fog && fog.isFogExp2 ), - - flatShading: material.flatShading, - - sizeAttenuation: material.sizeAttenuation, - logarithmicDepthBuffer: logarithmicDepthBuffer, - - skinning: material.skinning && maxBones > 0, - maxBones: maxBones, - useVertexTexture: floatVertexTextures, - - morphTargets: material.morphTargets, - morphNormals: material.morphNormals, - maxMorphTargets: renderer.maxMorphTargets, - maxMorphNormals: renderer.maxMorphNormals, - - numDirLights: lights.directional.length, - numPointLights: lights.point.length, - numSpotLights: lights.spot.length, - numRectAreaLights: lights.rectArea.length, - numHemiLights: lights.hemi.length, - - numDirLightShadows: lights.directionalShadowMap.length, - numPointLightShadows: lights.pointShadowMap.length, - numSpotLightShadows: lights.spotShadowMap.length, - - numClippingPlanes: clipping.numPlanes, - numClipIntersection: clipping.numIntersection, - - dithering: material.dithering, - - shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, - shadowMapType: renderer.shadowMap.type, - - toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, - physicallyCorrectLights: renderer.physicallyCorrectLights, - - premultipliedAlpha: material.premultipliedAlpha, - - alphaTest: material.alphaTest, - doubleSided: material.side === DoubleSide, - flipSided: material.side === BackSide, - - depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false, - - index0AttributeName: material.index0AttributeName, - - extensionDerivatives: material.extensions && material.extensions.derivatives, - extensionFragDepth: material.extensions && material.extensions.fragDepth, - extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, - extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, - - rendererExtensionFragDepth: isWebGL2 || extensions.has( 'EXT_frag_depth' ), - rendererExtensionDrawBuffers: isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ), - rendererExtensionShaderTextureLod: isWebGL2 || extensions.has( 'EXT_shader_texture_lod' ), - - customProgramCacheKey: material.customProgramCacheKey() - - }; - - return parameters; - - } - - function getProgramCacheKey( parameters ) { - - const array = []; - - if ( parameters.shaderID ) { - - array.push( parameters.shaderID ); - - } else { - - array.push( parameters.fragmentShader ); - array.push( parameters.vertexShader ); - - } - - if ( parameters.defines !== undefined ) { - - for ( const name in parameters.defines ) { - - array.push( name ); - array.push( parameters.defines[ name ] ); - - } - - } - - if ( parameters.isRawShaderMaterial === false ) { - - for ( let i = 0; i < parameterNames.length; i ++ ) { - - array.push( parameters[ parameterNames[ i ] ] ); - - } - - array.push( renderer.outputEncoding ); - array.push( renderer.gammaFactor ); - - } - - array.push( parameters.customProgramCacheKey ); - - return array.join(); - - } - - function getUniforms( material ) { - - const shaderID = shaderIDs[ material.type ]; - let uniforms; - - if ( shaderID ) { - - const shader = ShaderLib[ shaderID ]; - uniforms = UniformsUtils.clone( shader.uniforms ); - - } else { - - uniforms = material.uniforms; - - } - - return uniforms; - - } - - function acquireProgram( parameters, cacheKey ) { - - let program; - - // Check if code has been already compiled - for ( let p = 0, pl = programs.length; p < pl; p ++ ) { - - const preexistingProgram = programs[ p ]; - - if ( preexistingProgram.cacheKey === cacheKey ) { - - program = preexistingProgram; - ++ program.usedTimes; - - break; - - } - - } - - if ( program === undefined ) { - - program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); - programs.push( program ); - - } - - return program; - - } - - function releaseProgram( program ) { - - if ( -- program.usedTimes === 0 ) { - - // Remove from unordered set - const i = programs.indexOf( program ); - programs[ i ] = programs[ programs.length - 1 ]; - programs.pop(); - - // Free WebGL resources - program.destroy(); - - } - - } - - return { - getParameters: getParameters, - getProgramCacheKey: getProgramCacheKey, - getUniforms: getUniforms, - acquireProgram: acquireProgram, - releaseProgram: releaseProgram, - // Exposed for resource monitoring & error feedback via renderer.info: - programs: programs - }; - - } - - function WebGLProperties() { - - let properties = new WeakMap(); - - function get( object ) { - - let map = properties.get( object ); - - if ( map === undefined ) { - - map = {}; - properties.set( object, map ); - - } - - return map; - - } - - function remove( object ) { - - properties.delete( object ); - - } - - function update( object, key, value ) { - - properties.get( object )[ key ] = value; - - } - - function dispose() { - - properties = new WeakMap(); - - } - - return { - get: get, - remove: remove, - update: update, - dispose: dispose - }; - - } - - function painterSortStable( a, b ) { - - if ( a.groupOrder !== b.groupOrder ) { - - return a.groupOrder - b.groupOrder; - - } else if ( a.renderOrder !== b.renderOrder ) { - - return a.renderOrder - b.renderOrder; - - } else if ( a.program !== b.program ) { - - return a.program.id - b.program.id; - - } else if ( a.material.id !== b.material.id ) { - - return a.material.id - b.material.id; - - } else if ( a.z !== b.z ) { - - return a.z - b.z; - - } else { - - return a.id - b.id; - - } - - } - - function reversePainterSortStable( a, b ) { - - if ( a.groupOrder !== b.groupOrder ) { - - return a.groupOrder - b.groupOrder; - - } else if ( a.renderOrder !== b.renderOrder ) { - - return a.renderOrder - b.renderOrder; - - } else if ( a.z !== b.z ) { - - return b.z - a.z; - - } else { - - return a.id - b.id; - - } - - } - - - function WebGLRenderList( properties ) { - - const renderItems = []; - let renderItemsIndex = 0; - - const opaque = []; - const transparent = []; - - const defaultProgram = { id: - 1 }; - - function init() { - - renderItemsIndex = 0; - - opaque.length = 0; - transparent.length = 0; - - } - - function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { - - let renderItem = renderItems[ renderItemsIndex ]; - const materialProperties = properties.get( material ); - - if ( renderItem === undefined ) { - - renderItem = { - id: object.id, - object: object, - geometry: geometry, - material: material, - program: materialProperties.program || defaultProgram, - groupOrder: groupOrder, - renderOrder: object.renderOrder, - z: z, - group: group - }; - - renderItems[ renderItemsIndex ] = renderItem; - - } else { - - renderItem.id = object.id; - renderItem.object = object; - renderItem.geometry = geometry; - renderItem.material = material; - renderItem.program = materialProperties.program || defaultProgram; - renderItem.groupOrder = groupOrder; - renderItem.renderOrder = object.renderOrder; - renderItem.z = z; - renderItem.group = group; - - } - - renderItemsIndex ++; - - return renderItem; - - } - - function push( object, geometry, material, groupOrder, z, group ) { - - const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - - ( material.transparent === true ? transparent : opaque ).push( renderItem ); - - } - - function unshift( object, geometry, material, groupOrder, z, group ) { - - const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - - ( material.transparent === true ? transparent : opaque ).unshift( renderItem ); - - } - - function sort( customOpaqueSort, customTransparentSort ) { - - if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); - if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); - - } - - function finish() { - - // Clear references from inactive renderItems in the list - - for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { - - const renderItem = renderItems[ i ]; - - if ( renderItem.id === null ) break; - - renderItem.id = null; - renderItem.object = null; - renderItem.geometry = null; - renderItem.material = null; - renderItem.program = null; - renderItem.group = null; - - } - - } - - return { - - opaque: opaque, - transparent: transparent, - - init: init, - push: push, - unshift: unshift, - finish: finish, - - sort: sort - }; - - } - - function WebGLRenderLists( properties ) { - - let lists = new WeakMap(); - - function get( scene, camera ) { - - const cameras = lists.get( scene ); - let list; - - if ( cameras === undefined ) { - - list = new WebGLRenderList( properties ); - lists.set( scene, new WeakMap() ); - lists.get( scene ).set( camera, list ); - - } else { - - list = cameras.get( camera ); - if ( list === undefined ) { - - list = new WebGLRenderList( properties ); - cameras.set( camera, list ); - - } - - } - - return list; - - } - - function dispose() { - - lists = new WeakMap(); - - } - - return { - get: get, - dispose: dispose - }; - - } - - function UniformsCache() { - - const lights = {}; - - return { - - get: function ( light ) { - - if ( lights[ light.id ] !== undefined ) { - - return lights[ light.id ]; - - } - - let uniforms; - - switch ( light.type ) { - - case 'DirectionalLight': - uniforms = { - direction: new Vector3(), - color: new Color() - }; - break; - - case 'SpotLight': - uniforms = { - position: new Vector3(), - direction: new Vector3(), - color: new Color(), - distance: 0, - coneCos: 0, - penumbraCos: 0, - decay: 0 - }; - break; - - case 'PointLight': - uniforms = { - position: new Vector3(), - color: new Color(), - distance: 0, - decay: 0 - }; - break; - - case 'HemisphereLight': - uniforms = { - direction: new Vector3(), - skyColor: new Color(), - groundColor: new Color() - }; - break; - - case 'RectAreaLight': - uniforms = { - color: new Color(), - position: new Vector3(), - halfWidth: new Vector3(), - halfHeight: new Vector3() - }; - break; - - } - - lights[ light.id ] = uniforms; - - return uniforms; - - } - - }; - - } - - function ShadowUniformsCache() { - - const lights = {}; - - return { - - get: function ( light ) { - - if ( lights[ light.id ] !== undefined ) { - - return lights[ light.id ]; - - } - - let uniforms; - - switch ( light.type ) { - - case 'DirectionalLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; - - case 'SpotLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; - - case 'PointLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2(), - shadowCameraNear: 1, - shadowCameraFar: 1000 - }; - break; - - // TODO (abelnation): set RectAreaLight shadow uniforms - - } - - lights[ light.id ] = uniforms; - - return uniforms; - - } - - }; - - } - - - - let nextVersion = 0; - - function shadowCastingLightsFirst( lightA, lightB ) { - - return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 ); - - } - - function WebGLLights() { - - const cache = new UniformsCache(); - - const shadowCache = ShadowUniformsCache(); - - const state = { - - version: 0, - - hash: { - directionalLength: - 1, - pointLength: - 1, - spotLength: - 1, - rectAreaLength: - 1, - hemiLength: - 1, - - numDirectionalShadows: - 1, - numPointShadows: - 1, - numSpotShadows: - 1 - }, - - ambient: [ 0, 0, 0 ], - probe: [], - directional: [], - directionalShadow: [], - directionalShadowMap: [], - directionalShadowMatrix: [], - spot: [], - spotShadow: [], - spotShadowMap: [], - spotShadowMatrix: [], - rectArea: [], - rectAreaLTC1: null, - rectAreaLTC2: null, - point: [], - pointShadow: [], - pointShadowMap: [], - pointShadowMatrix: [], - hemi: [] - - }; - - for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); - - const vector3 = new Vector3(); - const matrix4 = new Matrix4(); - const matrix42 = new Matrix4(); - - function setup( lights, shadows, camera ) { - - let r = 0, g = 0, b = 0; - - for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); - - let directionalLength = 0; - let pointLength = 0; - let spotLength = 0; - let rectAreaLength = 0; - let hemiLength = 0; - - let numDirectionalShadows = 0; - let numPointShadows = 0; - let numSpotShadows = 0; - - const viewMatrix = camera.matrixWorldInverse; - - lights.sort( shadowCastingLightsFirst ); - - for ( let i = 0, l = lights.length; i < l; i ++ ) { - - const light = lights[ i ]; - - const color = light.color; - const intensity = light.intensity; - const distance = light.distance; - - const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; - - if ( light.isAmbientLight ) { - - r += color.r * intensity; - g += color.g * intensity; - b += color.b * intensity; - - } else if ( light.isLightProbe ) { - - for ( let j = 0; j < 9; j ++ ) { - - state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); - - } - - } else if ( light.isDirectionalLight ) { - - const uniforms = cache.get( light ); - - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); - - if ( light.castShadow ) { - - const shadow = light.shadow; - - const shadowUniforms = shadowCache.get( light ); - - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - - state.directionalShadow[ directionalLength ] = shadowUniforms; - state.directionalShadowMap[ directionalLength ] = shadowMap; - state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; - - numDirectionalShadows ++; - - } - - state.directional[ directionalLength ] = uniforms; - - directionalLength ++; - - } else if ( light.isSpotLight ) { - - const uniforms = cache.get( light ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - uniforms.color.copy( color ).multiplyScalar( intensity ); - uniforms.distance = distance; - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); - - uniforms.coneCos = Math.cos( light.angle ); - uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); - uniforms.decay = light.decay; - - if ( light.castShadow ) { - - const shadow = light.shadow; - - const shadowUniforms = shadowCache.get( light ); - - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - - state.spotShadow[ spotLength ] = shadowUniforms; - state.spotShadowMap[ spotLength ] = shadowMap; - state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; - - numSpotShadows ++; - - } - - state.spot[ spotLength ] = uniforms; - - spotLength ++; - - } else if ( light.isRectAreaLight ) { - - const uniforms = cache.get( light ); - - // (a) intensity is the total visible light emitted - //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); - - // (b) intensity is the brightness of the light - uniforms.color.copy( color ).multiplyScalar( intensity ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - // extract local rotation of light to derive width/height half vectors - matrix42.identity(); - matrix4.copy( light.matrixWorld ); - matrix4.premultiply( viewMatrix ); - matrix42.extractRotation( matrix4 ); - - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - - uniforms.halfWidth.applyMatrix4( matrix42 ); - uniforms.halfHeight.applyMatrix4( matrix42 ); - - // TODO (abelnation): RectAreaLight distance? - // uniforms.distance = distance; - - state.rectArea[ rectAreaLength ] = uniforms; - - rectAreaLength ++; - - } else if ( light.isPointLight ) { - - const uniforms = cache.get( light ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.distance = light.distance; - uniforms.decay = light.decay; - - if ( light.castShadow ) { - - const shadow = light.shadow; - - const shadowUniforms = shadowCache.get( light ); - - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - shadowUniforms.shadowCameraNear = shadow.camera.near; - shadowUniforms.shadowCameraFar = shadow.camera.far; - - state.pointShadow[ pointLength ] = shadowUniforms; - state.pointShadowMap[ pointLength ] = shadowMap; - state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; - - numPointShadows ++; - - } - - state.point[ pointLength ] = uniforms; - - pointLength ++; - - } else if ( light.isHemisphereLight ) { - - const uniforms = cache.get( light ); - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - uniforms.direction.transformDirection( viewMatrix ); - uniforms.direction.normalize(); - - uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); - uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); - - state.hemi[ hemiLength ] = uniforms; - - hemiLength ++; - - } - - } - - if ( rectAreaLength > 0 ) { - - state.rectAreaLTC1 = UniformsLib.LTC_1; - state.rectAreaLTC2 = UniformsLib.LTC_2; - - } - - state.ambient[ 0 ] = r; - state.ambient[ 1 ] = g; - state.ambient[ 2 ] = b; - - const hash = state.hash; - - if ( hash.directionalLength !== directionalLength || - hash.pointLength !== pointLength || - hash.spotLength !== spotLength || - hash.rectAreaLength !== rectAreaLength || - hash.hemiLength !== hemiLength || - hash.numDirectionalShadows !== numDirectionalShadows || - hash.numPointShadows !== numPointShadows || - hash.numSpotShadows !== numSpotShadows ) { - - state.directional.length = directionalLength; - state.spot.length = spotLength; - state.rectArea.length = rectAreaLength; - state.point.length = pointLength; - state.hemi.length = hemiLength; - - state.directionalShadow.length = numDirectionalShadows; - state.directionalShadowMap.length = numDirectionalShadows; - state.pointShadow.length = numPointShadows; - state.pointShadowMap.length = numPointShadows; - state.spotShadow.length = numSpotShadows; - state.spotShadowMap.length = numSpotShadows; - state.directionalShadowMatrix.length = numDirectionalShadows; - state.pointShadowMatrix.length = numPointShadows; - state.spotShadowMatrix.length = numSpotShadows; - - hash.directionalLength = directionalLength; - hash.pointLength = pointLength; - hash.spotLength = spotLength; - hash.rectAreaLength = rectAreaLength; - hash.hemiLength = hemiLength; - - hash.numDirectionalShadows = numDirectionalShadows; - hash.numPointShadows = numPointShadows; - hash.numSpotShadows = numSpotShadows; - - state.version = nextVersion ++; - - } - - } - - return { - setup: setup, - state: state - }; - - } - - function WebGLRenderState() { - - const lights = new WebGLLights(); - - const lightsArray = []; - const shadowsArray = []; - - function init() { - - lightsArray.length = 0; - shadowsArray.length = 0; - - } - - function pushLight( light ) { - - lightsArray.push( light ); - - } - - function pushShadow( shadowLight ) { - - shadowsArray.push( shadowLight ); - - } - - function setupLights( camera ) { - - lights.setup( lightsArray, shadowsArray, camera ); - - } - - const state = { - lightsArray: lightsArray, - shadowsArray: shadowsArray, - - lights: lights - }; - - return { - init: init, - state: state, - setupLights: setupLights, - - pushLight: pushLight, - pushShadow: pushShadow - }; - - } - - function WebGLRenderStates() { - - let renderStates = new WeakMap(); - - function get( scene, camera ) { - - let renderState; - - if ( renderStates.has( scene ) === false ) { - - renderState = new WebGLRenderState(); - renderStates.set( scene, new WeakMap() ); - renderStates.get( scene ).set( camera, renderState ); - - } else { - - if ( renderStates.get( scene ).has( camera ) === false ) { - - renderState = new WebGLRenderState(); - renderStates.get( scene ).set( camera, renderState ); - - } else { - - renderState = renderStates.get( scene ).get( camera ); - - } - - } - - return renderState; - - } - - function dispose() { - - renderStates = new WeakMap(); - - } - - return { - get: get, - dispose: dispose - }; - - } - - /** - * parameters = { - * - * opacity: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ - - function MeshDepthMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshDepthMaterial'; - - this.depthPacking = BasicDepthPacking; - - this.skinning = false; - this.morphTargets = false; - - this.map = null; - - this.alphaMap = null; - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; - - this.setValues( parameters ); - - } - - MeshDepthMaterial.prototype = Object.create( Material.prototype ); - MeshDepthMaterial.prototype.constructor = MeshDepthMaterial; - - MeshDepthMaterial.prototype.isMeshDepthMaterial = true; - - MeshDepthMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.depthPacking = source.depthPacking; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - return this; - - }; - - /** - * parameters = { - * - * referencePosition: , - * nearDistance: , - * farDistance: , - * - * skinning: , - * morphTargets: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: - * - * } - */ - - function MeshDistanceMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshDistanceMaterial'; - - this.referencePosition = new Vector3(); - this.nearDistance = 1; - this.farDistance = 1000; - - this.skinning = false; - this.morphTargets = false; - - this.map = null; - - this.alphaMap = null; - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.fog = false; - - this.setValues( parameters ); - - } - - MeshDistanceMaterial.prototype = Object.create( Material.prototype ); - MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial; - - MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; - - MeshDistanceMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.referencePosition.copy( source.referencePosition ); - this.nearDistance = source.nearDistance; - this.farDistance = source.farDistance; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - return this; - - }; - - var vsm_frag = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n\tfor ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n\t\t#ifdef HORIZONAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean * HALF_SAMPLE_RATE;\n\tsquared_mean = squared_mean * HALF_SAMPLE_RATE;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; - - var vsm_vert = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; - - function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { - - let _frustum = new Frustum(); - - const _shadowMapSize = new Vector2(), - _viewportSize = new Vector2(), - - _viewport = new Vector4(), - - _depthMaterials = [], - _distanceMaterials = [], - - _materialCache = {}; - - const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; - - const shadowMaterialVertical = new ShaderMaterial( { - - defines: { - SAMPLE_RATE: 2.0 / 8.0, - HALF_SAMPLE_RATE: 1.0 / 8.0 - }, - - uniforms: { - shadow_pass: { value: null }, - resolution: { value: new Vector2() }, - radius: { value: 4.0 } - }, - - vertexShader: vsm_vert, - - fragmentShader: vsm_frag - - } ); - - const shadowMaterialHorizonal = shadowMaterialVertical.clone(); - shadowMaterialHorizonal.defines.HORIZONAL_PASS = 1; - - const fullScreenTri = new BufferGeometry(); - fullScreenTri.setAttribute( - "position", - new BufferAttribute( - new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), - 3 - ) - ); - - const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); - - const scope = this; - - this.enabled = false; - - this.autoUpdate = true; - this.needsUpdate = false; - - this.type = PCFShadowMap; - - this.render = function ( lights, scene, camera ) { - - if ( scope.enabled === false ) return; - if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; - - if ( lights.length === 0 ) return; - - const currentRenderTarget = _renderer.getRenderTarget(); - const activeCubeFace = _renderer.getActiveCubeFace(); - const activeMipmapLevel = _renderer.getActiveMipmapLevel(); - - const _state = _renderer.state; - - // Set GL state for depth map. - _state.setBlending( NoBlending ); - _state.buffers.color.setClear( 1, 1, 1, 1 ); - _state.buffers.depth.setTest( true ); - _state.setScissorTest( false ); - - // render depth map - - for ( let i = 0, il = lights.length; i < il; i ++ ) { - - const light = lights[ i ]; - const shadow = light.shadow; - - if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; - - if ( shadow === undefined ) { - - console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); - continue; - - } - - _shadowMapSize.copy( shadow.mapSize ); - - const shadowFrameExtents = shadow.getFrameExtents(); - - _shadowMapSize.multiply( shadowFrameExtents ); - - _viewportSize.copy( shadow.mapSize ); - - if ( _shadowMapSize.x > maxTextureSize || _shadowMapSize.y > maxTextureSize ) { - - if ( _shadowMapSize.x > maxTextureSize ) { - - _viewportSize.x = Math.floor( maxTextureSize / shadowFrameExtents.x ); - _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; - shadow.mapSize.x = _viewportSize.x; - - } - - if ( _shadowMapSize.y > maxTextureSize ) { - - _viewportSize.y = Math.floor( maxTextureSize / shadowFrameExtents.y ); - _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; - shadow.mapSize.y = _viewportSize.y; - - } - - } - - if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - - const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; - - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + ".shadowMap"; - - shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - - shadow.camera.updateProjectionMatrix(); - - } - - if ( shadow.map === null ) { - - const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; - - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + ".shadowMap"; - - shadow.camera.updateProjectionMatrix(); - - } - - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - - const viewportCount = shadow.getViewportCount(); - - for ( let vp = 0; vp < viewportCount; vp ++ ) { - - const viewport = shadow.getViewport( vp ); - - _viewport.set( - _viewportSize.x * viewport.x, - _viewportSize.y * viewport.y, - _viewportSize.x * viewport.z, - _viewportSize.y * viewport.w - ); - - _state.viewport( _viewport ); - - shadow.updateMatrices( light, vp ); - - _frustum = shadow.getFrustum(); - - renderObject( scene, camera, shadow.camera, light, this.type ); - - } - - // do blur pass for VSM - - if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - - VSMPass( shadow, camera ); - - } - - shadow.needsUpdate = false; - - } - - scope.needsUpdate = false; - - _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); - - }; - - function VSMPass( shadow, camera ) { - - const geometry = _objects.update( fullScreenMesh ); - - // vertical pass - - shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; - shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; - shadowMaterialVertical.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.mapPass ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); - - // horizonal pass - - shadowMaterialHorizonal.uniforms.shadow_pass.value = shadow.mapPass.texture; - shadowMaterialHorizonal.uniforms.resolution.value = shadow.mapSize; - shadowMaterialHorizonal.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizonal, fullScreenMesh, null ); - - } - - function getDepthMaterialVariant( useMorphing, useSkinning, useInstancing ) { - - const index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; - - let material = _depthMaterials[ index ]; - - if ( material === undefined ) { - - material = new MeshDepthMaterial( { - - depthPacking: RGBADepthPacking, - - morphTargets: useMorphing, - skinning: useSkinning - - } ); - - _depthMaterials[ index ] = material; - - } - - return material; - - } - - function getDistanceMaterialVariant( useMorphing, useSkinning, useInstancing ) { - - const index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; - - let material = _distanceMaterials[ index ]; - - if ( material === undefined ) { - - material = new MeshDistanceMaterial( { - - morphTargets: useMorphing, - skinning: useSkinning - - } ); - - _distanceMaterials[ index ] = material; - - } - - return material; - - } - - function getDepthMaterial( object, geometry, material, light, shadowCameraNear, shadowCameraFar, type ) { - - let result = null; - - let getMaterialVariant = getDepthMaterialVariant; - let customMaterial = object.customDepthMaterial; - - if ( light.isPointLight === true ) { - - getMaterialVariant = getDistanceMaterialVariant; - customMaterial = object.customDistanceMaterial; - - } - - if ( customMaterial === undefined ) { - - let useMorphing = false; - - if ( material.morphTargets === true ) { - - useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; - - } - - let useSkinning = false; - - if ( object.isSkinnedMesh === true ) { - - if ( material.skinning === true ) { - - useSkinning = true; - - } else { - - console.warn( 'THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object ); - - } - - } - - const useInstancing = object.isInstancedMesh === true; - - result = getMaterialVariant( useMorphing, useSkinning, useInstancing ); - - } else { - - result = customMaterial; - - } - - if ( _renderer.localClippingEnabled && - material.clipShadows === true && - material.clippingPlanes.length !== 0 ) { - - // in this case we need a unique material instance reflecting the - // appropriate state - - const keyA = result.uuid, keyB = material.uuid; - - let materialsForVariant = _materialCache[ keyA ]; - - if ( materialsForVariant === undefined ) { - - materialsForVariant = {}; - _materialCache[ keyA ] = materialsForVariant; - - } - - let cachedMaterial = materialsForVariant[ keyB ]; - - if ( cachedMaterial === undefined ) { - - cachedMaterial = result.clone(); - materialsForVariant[ keyB ] = cachedMaterial; - - } - - result = cachedMaterial; - - } - - result.visible = material.visible; - result.wireframe = material.wireframe; - - if ( type === VSMShadowMap ) { - - result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; - - } else { - - result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; - - } - - result.clipShadows = material.clipShadows; - result.clippingPlanes = material.clippingPlanes; - result.clipIntersection = material.clipIntersection; - - result.wireframeLinewidth = material.wireframeLinewidth; - result.linewidth = material.linewidth; - - if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { - - result.referencePosition.setFromMatrixPosition( light.matrixWorld ); - result.nearDistance = shadowCameraNear; - result.farDistance = shadowCameraFar; - - } - - return result; - - } - - function renderObject( object, camera, shadowCamera, light, type ) { - - if ( object.visible === false ) return; - - const visible = object.layers.test( camera.layers ); - - if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { - - if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { - - object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - - const geometry = _objects.update( object ); - const material = object.material; - - if ( Array.isArray( material ) ) { - - const groups = geometry.groups; - - for ( let k = 0, kl = groups.length; k < kl; k ++ ) { - - const group = groups[ k ]; - const groupMaterial = material[ group.materialIndex ]; - - if ( groupMaterial && groupMaterial.visible ) { - - const depthMaterial = getDepthMaterial( object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); - - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); - - } - - } - - } else if ( material.visible ) { - - const depthMaterial = getDepthMaterial( object, geometry, material, light, shadowCamera.near, shadowCamera.far, type ); - - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); - - } - - } - - } - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - renderObject( children[ i ], camera, shadowCamera, light, type ); - - } - - } - - } - - function WebGLState( gl, extensions, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - function ColorBuffer() { - - let locked = false; - - const color = new Vector4(); - let currentColorMask = null; - const currentColorClear = new Vector4( 0, 0, 0, 0 ); - - return { - - setMask: function ( colorMask ) { - - if ( currentColorMask !== colorMask && ! locked ) { - - gl.colorMask( colorMask, colorMask, colorMask, colorMask ); - currentColorMask = colorMask; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( r, g, b, a, premultipliedAlpha ) { - - if ( premultipliedAlpha === true ) { - - r *= a; g *= a; b *= a; - - } - - color.set( r, g, b, a ); - - if ( currentColorClear.equals( color ) === false ) { - - gl.clearColor( r, g, b, a ); - currentColorClear.copy( color ); - - } - - }, - - reset: function () { - - locked = false; - - currentColorMask = null; - currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state - - } - - }; - - } - - function DepthBuffer() { - - let locked = false; - - let currentDepthMask = null; - let currentDepthFunc = null; - let currentDepthClear = null; - - return { - - setTest: function ( depthTest ) { - - if ( depthTest ) { - - enable( 2929 ); - - } else { - - disable( 2929 ); - - } - - }, - - setMask: function ( depthMask ) { - - if ( currentDepthMask !== depthMask && ! locked ) { - - gl.depthMask( depthMask ); - currentDepthMask = depthMask; - - } - - }, - - setFunc: function ( depthFunc ) { - - if ( currentDepthFunc !== depthFunc ) { - - if ( depthFunc ) { - - switch ( depthFunc ) { - - case NeverDepth: - - gl.depthFunc( 512 ); - break; - - case AlwaysDepth: - - gl.depthFunc( 519 ); - break; - - case LessDepth: - - gl.depthFunc( 513 ); - break; - - case LessEqualDepth: - - gl.depthFunc( 515 ); - break; - - case EqualDepth: - - gl.depthFunc( 514 ); - break; - - case GreaterEqualDepth: - - gl.depthFunc( 518 ); - break; - - case GreaterDepth: - - gl.depthFunc( 516 ); - break; - - case NotEqualDepth: - - gl.depthFunc( 517 ); - break; - - default: - - gl.depthFunc( 515 ); - - } - - } else { - - gl.depthFunc( 515 ); - - } - - currentDepthFunc = depthFunc; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( depth ) { - - if ( currentDepthClear !== depth ) { - - gl.clearDepth( depth ); - currentDepthClear = depth; - - } - - }, - - reset: function () { - - locked = false; - - currentDepthMask = null; - currentDepthFunc = null; - currentDepthClear = null; - - } - - }; - - } - - function StencilBuffer() { - - let locked = false; - - let currentStencilMask = null; - let currentStencilFunc = null; - let currentStencilRef = null; - let currentStencilFuncMask = null; - let currentStencilFail = null; - let currentStencilZFail = null; - let currentStencilZPass = null; - let currentStencilClear = null; - - return { - - setTest: function ( stencilTest ) { - - if ( ! locked ) { - - if ( stencilTest ) { - - enable( 2960 ); - - } else { - - disable( 2960 ); - - } - - } - - }, - - setMask: function ( stencilMask ) { - - if ( currentStencilMask !== stencilMask && ! locked ) { - - gl.stencilMask( stencilMask ); - currentStencilMask = stencilMask; - - } - - }, - - setFunc: function ( stencilFunc, stencilRef, stencilMask ) { - - if ( currentStencilFunc !== stencilFunc || - currentStencilRef !== stencilRef || - currentStencilFuncMask !== stencilMask ) { - - gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); - - currentStencilFunc = stencilFunc; - currentStencilRef = stencilRef; - currentStencilFuncMask = stencilMask; - - } - - }, - - setOp: function ( stencilFail, stencilZFail, stencilZPass ) { - - if ( currentStencilFail !== stencilFail || - currentStencilZFail !== stencilZFail || - currentStencilZPass !== stencilZPass ) { - - gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); - - currentStencilFail = stencilFail; - currentStencilZFail = stencilZFail; - currentStencilZPass = stencilZPass; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( stencil ) { - - if ( currentStencilClear !== stencil ) { - - gl.clearStencil( stencil ); - currentStencilClear = stencil; - - } - - }, - - reset: function () { - - locked = false; - - currentStencilMask = null; - currentStencilFunc = null; - currentStencilRef = null; - currentStencilFuncMask = null; - currentStencilFail = null; - currentStencilZFail = null; - currentStencilZPass = null; - currentStencilClear = null; - - } - - }; - - } - - // - - const colorBuffer = new ColorBuffer(); - const depthBuffer = new DepthBuffer(); - const stencilBuffer = new StencilBuffer(); - - let enabledCapabilities = {}; - - let currentProgram = null; - - let currentBlendingEnabled = null; - let currentBlending = null; - let currentBlendEquation = null; - let currentBlendSrc = null; - let currentBlendDst = null; - let currentBlendEquationAlpha = null; - let currentBlendSrcAlpha = null; - let currentBlendDstAlpha = null; - let currentPremultipledAlpha = false; - - let currentFlipSided = null; - let currentCullFace = null; - - let currentLineWidth = null; - - let currentPolygonOffsetFactor = null; - let currentPolygonOffsetUnits = null; - - const maxTextures = gl.getParameter( 35661 ); - - let lineWidthAvailable = false; - let version = 0; - const glVersion = gl.getParameter( 7938 ); - - if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { - - version = parseFloat( /^WebGL\ ([0-9])/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 1.0 ); - - } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { - - version = parseFloat( /^OpenGL\ ES\ ([0-9])/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 2.0 ); - - } - - let currentTextureSlot = null; - let currentBoundTextures = {}; - - const currentScissor = new Vector4(); - const currentViewport = new Vector4(); - - function createTexture( type, target, count ) { - - const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. - const texture = gl.createTexture(); - - gl.bindTexture( type, texture ); - gl.texParameteri( type, 10241, 9728 ); - gl.texParameteri( type, 10240, 9728 ); - - for ( let i = 0; i < count; i ++ ) { - - gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); - - } - - return texture; - - } - - const emptyTextures = {}; - emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); - emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); - - // init - - colorBuffer.setClear( 0, 0, 0, 1 ); - depthBuffer.setClear( 1 ); - stencilBuffer.setClear( 0 ); - - enable( 2929 ); - depthBuffer.setFunc( LessEqualDepth ); - - setFlipSided( false ); - setCullFace( CullFaceBack ); - enable( 2884 ); - - setBlending( NoBlending ); - - // - - function enable( id ) { - - if ( enabledCapabilities[ id ] !== true ) { - - gl.enable( id ); - enabledCapabilities[ id ] = true; - - } - - } - - function disable( id ) { - - if ( enabledCapabilities[ id ] !== false ) { - - gl.disable( id ); - enabledCapabilities[ id ] = false; - - } - - } - - function useProgram( program ) { - - if ( currentProgram !== program ) { - - gl.useProgram( program ); - - currentProgram = program; - - return true; - - } - - return false; - - } - - const equationToGL = { - [ AddEquation ]: 32774, - [ SubtractEquation ]: 32778, - [ ReverseSubtractEquation ]: 32779 - }; - - if ( isWebGL2 ) { - - equationToGL[ MinEquation ] = 32775; - equationToGL[ MaxEquation ] = 32776; - - } else { - - const extension = extensions.get( 'EXT_blend_minmax' ); - - if ( extension !== null ) { - - equationToGL[ MinEquation ] = extension.MIN_EXT; - equationToGL[ MaxEquation ] = extension.MAX_EXT; - - } - - } - - const factorToGL = { - [ ZeroFactor ]: 0, - [ OneFactor ]: 1, - [ SrcColorFactor ]: 768, - [ SrcAlphaFactor ]: 770, - [ SrcAlphaSaturateFactor ]: 776, - [ DstColorFactor ]: 774, - [ DstAlphaFactor ]: 772, - [ OneMinusSrcColorFactor ]: 769, - [ OneMinusSrcAlphaFactor ]: 771, - [ OneMinusDstColorFactor ]: 775, - [ OneMinusDstAlphaFactor ]: 773 - }; - - function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { - - if ( blending === NoBlending ) { - - if ( currentBlendingEnabled ) { - - disable( 3042 ); - currentBlendingEnabled = false; - - } - - return; - - } - - if ( ! currentBlendingEnabled ) { - - enable( 3042 ); - currentBlendingEnabled = true; - - } - - if ( blending !== CustomBlending ) { - - if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { - - if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { - - gl.blendEquation( 32774 ); - - currentBlendEquation = AddEquation; - currentBlendEquationAlpha = AddEquation; - - } - - if ( premultipliedAlpha ) { - - switch ( blending ) { - - case NormalBlending: - gl.blendFuncSeparate( 1, 771, 1, 771 ); - break; - - case AdditiveBlending: - gl.blendFunc( 1, 1 ); - break; - - case SubtractiveBlending: - gl.blendFuncSeparate( 0, 0, 769, 771 ); - break; - - case MultiplyBlending: - gl.blendFuncSeparate( 0, 768, 0, 770 ); - break; - - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; - - } - - } else { - - switch ( blending ) { - - case NormalBlending: - gl.blendFuncSeparate( 770, 771, 1, 771 ); - break; - - case AdditiveBlending: - gl.blendFunc( 770, 1 ); - break; - - case SubtractiveBlending: - gl.blendFunc( 0, 769 ); - break; - - case MultiplyBlending: - gl.blendFunc( 0, 768 ); - break; - - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; - - } - - } - - currentBlendSrc = null; - currentBlendDst = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; - - currentBlending = blending; - currentPremultipledAlpha = premultipliedAlpha; - - } - - return; - - } - - // custom blending - - blendEquationAlpha = blendEquationAlpha || blendEquation; - blendSrcAlpha = blendSrcAlpha || blendSrc; - blendDstAlpha = blendDstAlpha || blendDst; - - if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { - - gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); - - currentBlendEquation = blendEquation; - currentBlendEquationAlpha = blendEquationAlpha; - - } - - if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { - - gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); - - currentBlendSrc = blendSrc; - currentBlendDst = blendDst; - currentBlendSrcAlpha = blendSrcAlpha; - currentBlendDstAlpha = blendDstAlpha; - - } - - currentBlending = blending; - currentPremultipledAlpha = null; - - } - - function setMaterial( material, frontFaceCW ) { - - material.side === DoubleSide - ? disable( 2884 ) - : enable( 2884 ); - - let flipSided = ( material.side === BackSide ); - if ( frontFaceCW ) flipSided = ! flipSided; - - setFlipSided( flipSided ); - - ( material.blending === NormalBlending && material.transparent === false ) - ? setBlending( NoBlending ) - : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); - - depthBuffer.setFunc( material.depthFunc ); - depthBuffer.setTest( material.depthTest ); - depthBuffer.setMask( material.depthWrite ); - colorBuffer.setMask( material.colorWrite ); - - const stencilWrite = material.stencilWrite; - stencilBuffer.setTest( stencilWrite ); - if ( stencilWrite ) { - - stencilBuffer.setMask( material.stencilWriteMask ); - stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); - stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); - - } - - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - - } - - // - - function setFlipSided( flipSided ) { - - if ( currentFlipSided !== flipSided ) { - - if ( flipSided ) { - - gl.frontFace( 2304 ); - - } else { - - gl.frontFace( 2305 ); - - } - - currentFlipSided = flipSided; - - } - - } - - function setCullFace( cullFace ) { - - if ( cullFace !== CullFaceNone ) { - - enable( 2884 ); - - if ( cullFace !== currentCullFace ) { - - if ( cullFace === CullFaceBack ) { - - gl.cullFace( 1029 ); - - } else if ( cullFace === CullFaceFront ) { - - gl.cullFace( 1028 ); - - } else { - - gl.cullFace( 1032 ); - - } - - } - - } else { - - disable( 2884 ); - - } - - currentCullFace = cullFace; - - } - - function setLineWidth( width ) { - - if ( width !== currentLineWidth ) { - - if ( lineWidthAvailable ) gl.lineWidth( width ); - - currentLineWidth = width; - - } - - } - - function setPolygonOffset( polygonOffset, factor, units ) { - - if ( polygonOffset ) { - - enable( 32823 ); - - if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { - - gl.polygonOffset( factor, units ); - - currentPolygonOffsetFactor = factor; - currentPolygonOffsetUnits = units; - - } - - } else { - - disable( 32823 ); - - } - - } - - function setScissorTest( scissorTest ) { - - if ( scissorTest ) { - - enable( 3089 ); - - } else { - - disable( 3089 ); - - } - - } - - // texture - - function activeTexture( webglSlot ) { - - if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1; - - if ( currentTextureSlot !== webglSlot ) { - - gl.activeTexture( webglSlot ); - currentTextureSlot = webglSlot; - - } - - } - - function bindTexture( webglType, webglTexture ) { - - if ( currentTextureSlot === null ) { - - activeTexture(); - - } - - let boundTexture = currentBoundTextures[ currentTextureSlot ]; - - if ( boundTexture === undefined ) { - - boundTexture = { type: undefined, texture: undefined }; - currentBoundTextures[ currentTextureSlot ] = boundTexture; - - } - - if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { - - gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); - - boundTexture.type = webglType; - boundTexture.texture = webglTexture; - - } - - } - - function unbindTexture() { - - const boundTexture = currentBoundTextures[ currentTextureSlot ]; - - if ( boundTexture !== undefined && boundTexture.type !== undefined ) { - - gl.bindTexture( boundTexture.type, null ); - - boundTexture.type = undefined; - boundTexture.texture = undefined; - - } - - } - - function compressedTexImage2D() { - - try { - - gl.compressedTexImage2D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( 'THREE.WebGLState:', error ); - - } - - } - - function texImage2D() { - - try { - - gl.texImage2D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( 'THREE.WebGLState:', error ); - - } - - } - - function texImage3D() { - - try { - - gl.texImage3D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( 'THREE.WebGLState:', error ); - - } - - } - - // - - function scissor( scissor ) { - - if ( currentScissor.equals( scissor ) === false ) { - - gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); - currentScissor.copy( scissor ); - - } - - } - - function viewport( viewport ) { - - if ( currentViewport.equals( viewport ) === false ) { - - gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); - currentViewport.copy( viewport ); - - } - - } - - // - - function reset() { - - enabledCapabilities = {}; - - currentTextureSlot = null; - currentBoundTextures = {}; - - currentProgram = null; - - currentBlending = null; - - currentFlipSided = null; - currentCullFace = null; - - colorBuffer.reset(); - depthBuffer.reset(); - stencilBuffer.reset(); - - } - - return { - - buffers: { - color: colorBuffer, - depth: depthBuffer, - stencil: stencilBuffer - }, - - enable: enable, - disable: disable, - - useProgram: useProgram, - - setBlending: setBlending, - setMaterial: setMaterial, - - setFlipSided: setFlipSided, - setCullFace: setCullFace, - - setLineWidth: setLineWidth, - setPolygonOffset: setPolygonOffset, - - setScissorTest: setScissorTest, - - activeTexture: activeTexture, - bindTexture: bindTexture, - unbindTexture: unbindTexture, - compressedTexImage2D: compressedTexImage2D, - texImage2D: texImage2D, - texImage3D: texImage3D, - - scissor: scissor, - viewport: viewport, - - reset: reset - - }; - - } - - function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { - - const isWebGL2 = capabilities.isWebGL2; - const maxTextures = capabilities.maxTextures; - const maxCubemapSize = capabilities.maxCubemapSize; - const maxTextureSize = capabilities.maxTextureSize; - const maxSamples = capabilities.maxSamples; - - const _videoTextures = new WeakMap(); - let _canvas; - - // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, - // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! - // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). - - let useOffscreenCanvas = false; - - try { - - useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' - && ( new OffscreenCanvas( 1, 1 ).getContext( "2d" ) ) !== null; - - } catch ( err ) { - - // Ignore any errors - - } - - function createCanvas( width, height ) { - - // Use OffscreenCanvas when available. Specially needed in web workers - - return useOffscreenCanvas ? - new OffscreenCanvas( width, height ) : - document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - - } - - function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { - - let scale = 1; - - // handle case if texture exceeds max size - - if ( image.width > maxSize || image.height > maxSize ) { - - scale = maxSize / Math.max( image.width, image.height ); - - } - - // only perform resize if necessary - - if ( scale < 1 || needsPowerOfTwo === true ) { - - // only perform resize for certain image types - - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || - ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - - const floor = needsPowerOfTwo ? MathUtils.floorPowerOfTwo : Math.floor; - - const width = floor( scale * image.width ); - const height = floor( scale * image.height ); - - if ( _canvas === undefined ) _canvas = createCanvas( width, height ); - - // cube textures can't reuse the same canvas - - const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; - - canvas.width = width; - canvas.height = height; - - const context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, width, height ); - - console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); - - return canvas; - - } else { - - if ( 'data' in image ) { - - console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); - - } - - return image; - - } - - } - - return image; - - } - - function isPowerOfTwo( image ) { - - return MathUtils.isPowerOfTwo( image.width ) && MathUtils.isPowerOfTwo( image.height ); - - } - - function textureNeedsPowerOfTwo( texture ) { - - if ( isWebGL2 ) return false; - - return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || - ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); - - } - - function textureNeedsGenerateMipmaps( texture, supportsMips ) { - - return texture.generateMipmaps && supportsMips && - texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; - - } - - function generateMipmap( target, texture, width, height ) { - - _gl.generateMipmap( target ); - - const textureProperties = properties.get( texture ); - - // Note: Math.log( x ) * Math.LOG2E used instead of Math.log2( x ) which is not supported by IE11 - textureProperties.__maxMipLevel = Math.log( Math.max( width, height ) ) * Math.LOG2E; - - } - - function getInternalFormat( internalFormatName, glFormat, glType ) { - - if ( isWebGL2 === false ) return glFormat; - - if ( internalFormatName !== null ) { - - if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; - - console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); - - } - - let internalFormat = glFormat; - - if ( glFormat === 6403 ) { - - if ( glType === 5126 ) internalFormat = 33326; - if ( glType === 5131 ) internalFormat = 33325; - if ( glType === 5121 ) internalFormat = 33321; - - } - - if ( glFormat === 6407 ) { - - if ( glType === 5126 ) internalFormat = 34837; - if ( glType === 5131 ) internalFormat = 34843; - if ( glType === 5121 ) internalFormat = 32849; - - } - - if ( glFormat === 6408 ) { - - if ( glType === 5126 ) internalFormat = 34836; - if ( glType === 5131 ) internalFormat = 34842; - if ( glType === 5121 ) internalFormat = 32856; - - } - - if ( internalFormat === 33325 || internalFormat === 33326 || - internalFormat === 34842 || internalFormat === 34836 ) { - - extensions.get( 'EXT_color_buffer_float' ); - - } - - return internalFormat; - - } - - // Fallback filters for non-power-of-2 textures - - function filterFallback( f ) { - - if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { - - return 9728; - - } - - return 9729; - - } - - // - - function onTextureDispose( event ) { - - const texture = event.target; - - texture.removeEventListener( 'dispose', onTextureDispose ); - - deallocateTexture( texture ); - - if ( texture.isVideoTexture ) { - - _videoTextures.delete( texture ); - - } - - info.memory.textures --; - - } - - function onRenderTargetDispose( event ) { - - const renderTarget = event.target; - - renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); - - deallocateRenderTarget( renderTarget ); - - info.memory.textures --; - - } - - // - - function deallocateTexture( texture ) { - - const textureProperties = properties.get( texture ); - - if ( textureProperties.__webglInit === undefined ) return; - - _gl.deleteTexture( textureProperties.__webglTexture ); - - properties.remove( texture ); - - } - - function deallocateRenderTarget( renderTarget ) { - - const renderTargetProperties = properties.get( renderTarget ); - const textureProperties = properties.get( renderTarget.texture ); - - if ( ! renderTarget ) return; - - if ( textureProperties.__webglTexture !== undefined ) { - - _gl.deleteTexture( textureProperties.__webglTexture ); - - } - - if ( renderTarget.depthTexture ) { - - renderTarget.depthTexture.dispose(); - - } - - if ( renderTarget.isWebGLCubeRenderTarget ) { - - for ( let i = 0; i < 6; i ++ ) { - - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); - - } - - } else { - - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); - if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); - if ( renderTargetProperties.__webglColorRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer ); - if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); - - } - - properties.remove( renderTarget.texture ); - properties.remove( renderTarget ); - - } - - // - - let textureUnits = 0; - - function resetTextureUnits() { - - textureUnits = 0; - - } - - function allocateTextureUnit() { - - const textureUnit = textureUnits; - - if ( textureUnit >= maxTextures ) { - - console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures ); - - } - - textureUnits += 1; - - return textureUnit; - - } - - // - - function setTexture2D( texture, slot ) { - - const textureProperties = properties.get( texture ); - - if ( texture.isVideoTexture ) updateVideoTexture( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - const image = texture.image; - - if ( image === undefined ) { - - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined' ); - - } else if ( image.complete === false ) { - - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); - - } else { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 3553, textureProperties.__webglTexture ); - - } - - function setTexture2DArray( texture, slot ) { - - const textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 35866, textureProperties.__webglTexture ); - - } - - function setTexture3D( texture, slot ) { - - const textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 32879, textureProperties.__webglTexture ); - - } - - function setTextureCube( texture, slot ) { - - if ( texture.image.length !== 6 ) return; - - const textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - initTexture( textureProperties, texture ); - - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); - - _gl.pixelStorei( 37440, texture.flipY ); - - const isCompressed = ( texture && ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ) ); - const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); - - const cubeImage = []; - - for ( let i = 0; i < 6; i ++ ) { - - if ( ! isCompressed && ! isDataTexture ) { - - cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); - - } else { - - cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; - - } - - } - - const image = cubeImage[ 0 ], - supportsMips = isPowerOfTwo( image ) || isWebGL2, - glFormat = utils.convert( texture.format ), - glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - - setTextureParameters( 34067, texture, supportsMips ); - - let mipmaps; - - if ( isCompressed ) { - - for ( let i = 0; i < 6; i ++ ) { - - mipmaps = cubeImage[ i ].mipmaps; - - for ( let j = 0; j < mipmaps.length; j ++ ) { - - const mipmap = mipmaps[ j ]; - - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { - - if ( glFormat !== null ) { - - state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - - } else { - - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); - - } - - } else { - - state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - } - - } - - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else { - - mipmaps = texture.mipmaps; - - for ( let i = 0; i < 6; i ++ ) { - - if ( isDataTexture ) { - - state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - - for ( let j = 0; j < mipmaps.length; j ++ ) { - - const mipmap = mipmaps[ j ]; - const mipmapImage = mipmap.image[ i ].image; - - state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); - - } - - } else { - - state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); - - for ( let j = 0; j < mipmaps.length; j ++ ) { - - const mipmap = mipmaps[ j ]; - - state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); - - } - - } - - } - - textureProperties.__maxMipLevel = mipmaps.length; - - } - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - // We assume images for cube map have the same size. - generateMipmap( 34067, texture, image.width, image.height ); - - } - - textureProperties.__version = texture.version; - - if ( texture.onUpdate ) texture.onUpdate( texture ); - - } else { - - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); - - } - - } - - function setTextureCubeDynamic( texture, slot ) { - - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, properties.get( texture ).__webglTexture ); - - } - - const wrappingToGL = { - [ RepeatWrapping ]: 10497, - [ ClampToEdgeWrapping ]: 33071, - [ MirroredRepeatWrapping ]: 33648 - }; - - const filterToGL = { - [ NearestFilter ]: 9728, - [ NearestMipmapNearestFilter ]: 9984, - [ NearestMipmapLinearFilter ]: 9986, - - [ LinearFilter ]: 9729, - [ LinearMipmapNearestFilter ]: 9985, - [ LinearMipmapLinearFilter ]: 9987 - }; - - function setTextureParameters( textureType, texture, supportsMips ) { - - if ( supportsMips ) { - - _gl.texParameteri( textureType, 10242, wrappingToGL[ texture.wrapS ] ); - _gl.texParameteri( textureType, 10243, wrappingToGL[ texture.wrapT ] ); - - if ( textureType === 32879 || textureType === 35866 ) { - - _gl.texParameteri( textureType, 32882, wrappingToGL[ texture.wrapR ] ); - - } - - _gl.texParameteri( textureType, 10240, filterToGL[ texture.magFilter ] ); - _gl.texParameteri( textureType, 10241, filterToGL[ texture.minFilter ] ); - - } else { - - _gl.texParameteri( textureType, 10242, 33071 ); - _gl.texParameteri( textureType, 10243, 33071 ); - - if ( textureType === 32879 || textureType === 35866 ) { - - _gl.texParameteri( textureType, 32882, 33071 ); - - } - - if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { - - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); - - } - - _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) ); - - if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { - - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); - - } - - } - - const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - - if ( extension ) { - - if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; - if ( texture.type === HalfFloatType && ( isWebGL2 || extensions.get( 'OES_texture_half_float_linear' ) ) === null ) return; - - if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { - - _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); - properties.get( texture ).__currentAnisotropy = texture.anisotropy; - - } - - } - - } - - function initTexture( textureProperties, texture ) { - - if ( textureProperties.__webglInit === undefined ) { - - textureProperties.__webglInit = true; - - texture.addEventListener( 'dispose', onTextureDispose ); - - textureProperties.__webglTexture = _gl.createTexture(); - - info.memory.textures ++; - - } - - } - - function uploadTexture( textureProperties, texture, slot ) { - - let textureType = 3553; - - if ( texture.isDataTexture2DArray ) textureType = 35866; - if ( texture.isDataTexture3D ) textureType = 32879; - - initTexture( textureProperties, texture ); - - state.activeTexture( 33984 + slot ); - state.bindTexture( textureType, textureProperties.__webglTexture ); - - _gl.pixelStorei( 37440, texture.flipY ); - _gl.pixelStorei( 37441, texture.premultiplyAlpha ); - _gl.pixelStorei( 3317, texture.unpackAlignment ); - - const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false; - const image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); - - const supportsMips = isPowerOfTwo( image ) || isWebGL2, - glFormat = utils.convert( texture.format ); - - let glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - - setTextureParameters( textureType, texture, supportsMips ); - - let mipmap; - const mipmaps = texture.mipmaps; - - if ( texture.isDepthTexture ) { - - // populate depth texture with dummy data - - glInternalFormat = 6402; - - if ( isWebGL2 ) { - - if ( texture.type === FloatType ) { - - glInternalFormat = 36012; - - } else if ( texture.type === UnsignedIntType ) { - - glInternalFormat = 33190; - - } else if ( texture.type === UnsignedInt248Type ) { - - glInternalFormat = 35056; - - } else { - - glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D - - } - - } else { - - if ( texture.type === FloatType ) { - - console.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' ); - - } - - } - - // validation checks for WebGL 1 - - if ( texture.format === DepthFormat && glInternalFormat === 6402 ) { - - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { - - console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); - - texture.type = UnsignedShortType; - glType = utils.convert( texture.type ); - - } - - } - - if ( texture.format === DepthStencilFormat && glInternalFormat === 6402 ) { - - // Depth stencil textures need the DEPTH_STENCIL internal format - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - glInternalFormat = 34041; - - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedInt248Type ) { - - console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); - - texture.type = UnsignedInt248Type; - glType = utils.convert( texture.type ); - - } - - } - - // - - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); - - } else if ( texture.isDataTexture ) { - - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels - - if ( mipmaps.length > 0 && supportsMips ) { - - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else { - - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; - - } - - } else if ( texture.isCompressedTexture ) { - - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { - - if ( glFormat !== null ) { - - state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - - } else { - - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); - - } - - } else { - - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - } - - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else if ( texture.isDataTexture2DArray ) { - - state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; - - } else if ( texture.isDataTexture3D ) { - - state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; - - } else { - - // regular Texture (image, video, canvas) - - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels - - if ( mipmaps.length > 0 && supportsMips ) { - - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); - - } - - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else { - - state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); - textureProperties.__maxMipLevel = 0; - - } - - } - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - generateMipmap( textureType, texture, image.width, image.height ); - - } - - textureProperties.__version = texture.version; - - if ( texture.onUpdate ) texture.onUpdate( texture ); - - } - - // Render targets - - // Setup storage for target texture and bind it to correct framebuffer - function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { - - const glFormat = utils.convert( renderTarget.texture.format ); - const glType = utils.convert( renderTarget.texture.type ); - const glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType ); - state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - _gl.bindFramebuffer( 36160, framebuffer ); - _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); - _gl.bindFramebuffer( 36160, null ); - - } - - // Setup storage for internal depth/stencil buffers and bind to correct framebuffer - function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { - - _gl.bindRenderbuffer( 36161, renderbuffer ); - - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - - let glInternalFormat = 33189; - - if ( isMultisample ) { - - const depthTexture = renderTarget.depthTexture; - - if ( depthTexture && depthTexture.isDepthTexture ) { - - if ( depthTexture.type === FloatType ) { - - glInternalFormat = 36012; - - } else if ( depthTexture.type === UnsignedIntType ) { - - glInternalFormat = 33190; - - } - - } - - const samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - - } else { - - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); - - } - - _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); - - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - - if ( isMultisample ) { - - const samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height ); - - } else { - - _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); - - } - - - _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); - - } else { - - const glFormat = utils.convert( renderTarget.texture.format ); - const glType = utils.convert( renderTarget.texture.type ); - const glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType ); - - if ( isMultisample ) { - - const samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - - } else { - - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); - - } - - } - - _gl.bindRenderbuffer( 36161, null ); - - } - - // Setup resources for a Depth Texture for a FBO (needs an extension) - function setupDepthTexture( framebuffer, renderTarget ) { - - const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); - if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); - - _gl.bindFramebuffer( 36160, framebuffer ); - - if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { - - throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); - - } - - // upload an empty depth texture with framebuffer size - if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || - renderTarget.depthTexture.image.width !== renderTarget.width || - renderTarget.depthTexture.image.height !== renderTarget.height ) { - - renderTarget.depthTexture.image.width = renderTarget.width; - renderTarget.depthTexture.image.height = renderTarget.height; - renderTarget.depthTexture.needsUpdate = true; - - } - - setTexture2D( renderTarget.depthTexture, 0 ); - - const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; - - if ( renderTarget.depthTexture.format === DepthFormat ) { - - _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); - - } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { - - _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); - - } else { - - throw new Error( 'Unknown depthTexture format' ); - - } - - } - - // Setup GL resources for a non-texture depth buffer - function setupDepthRenderbuffer( renderTarget ) { - - const renderTargetProperties = properties.get( renderTarget ); - - const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - - if ( renderTarget.depthTexture ) { - - if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); - - setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); - - } else { - - if ( isCube ) { - - renderTargetProperties.__webglDepthbuffer = []; - - for ( let i = 0; i < 6; i ++ ) { - - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); - renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); - - } - - } else { - - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); - renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); - - } - - } - - _gl.bindFramebuffer( 36160, null ); - - } - - // Set up GL resources for the render target - function setupRenderTarget( renderTarget ) { - - const renderTargetProperties = properties.get( renderTarget ); - const textureProperties = properties.get( renderTarget.texture ); - - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - - textureProperties.__webglTexture = _gl.createTexture(); - - info.memory.textures ++; - - const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - const isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true ); - const supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2; - - // Handles WebGL2 RGBFormat fallback - #18858 - - if ( isWebGL2 && renderTarget.texture.format === RGBFormat && ( renderTarget.texture.type === FloatType || renderTarget.texture.type === HalfFloatType ) ) { - - renderTarget.texture.format = RGBAFormat; - - console.warn( 'THREE.WebGLRenderer: Rendering to textures with RGB format is not supported. Using RGBA format instead.' ); - - } - - // Setup framebuffer - - if ( isCube ) { - - renderTargetProperties.__webglFramebuffer = []; - - for ( let i = 0; i < 6; i ++ ) { - - renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - - } - - } else { - - renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); - - if ( isMultisample ) { - - if ( isWebGL2 ) { - - renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); - renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); - - _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer ); - - const glFormat = utils.convert( renderTarget.texture.format ); - const glType = utils.convert( renderTarget.texture.type ); - const glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType ); - const samples = getRenderTargetSamples( renderTarget ); - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.framebufferRenderbuffer( 36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer ); - _gl.bindRenderbuffer( 36161, null ); - - if ( renderTarget.depthBuffer ) { - - renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); - - } - - _gl.bindFramebuffer( 36160, null ); - - - } else { - - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); - - } - - } - - } - - // Setup color buffer - - if ( isCube ) { - - state.bindTexture( 34067, textureProperties.__webglTexture ); - setTextureParameters( 34067, renderTarget.texture, supportsMips ); - - for ( let i = 0; i < 6; i ++ ) { - - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, 36064, 34069 + i ); - - } - - if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) { - - generateMipmap( 34067, renderTarget.texture, renderTarget.width, renderTarget.height ); - - } - - state.bindTexture( 34067, null ); - - } else { - - state.bindTexture( 3553, textureProperties.__webglTexture ); - setTextureParameters( 3553, renderTarget.texture, supportsMips ); - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, 36064, 3553 ); - - if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) { - - generateMipmap( 3553, renderTarget.texture, renderTarget.width, renderTarget.height ); - - } - - state.bindTexture( 3553, null ); - - } - - // Setup depth and stencil buffers - - if ( renderTarget.depthBuffer ) { - - setupDepthRenderbuffer( renderTarget ); - - } - - } - - function updateRenderTargetMipmap( renderTarget ) { - - const texture = renderTarget.texture; - const supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2; - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - const target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553; - const webglTexture = properties.get( texture ).__webglTexture; - - state.bindTexture( target, webglTexture ); - generateMipmap( target, texture, renderTarget.width, renderTarget.height ); - state.bindTexture( target, null ); - - } - - } - - function updateMultisampleRenderTarget( renderTarget ) { - - if ( renderTarget.isWebGLMultisampleRenderTarget ) { - - if ( isWebGL2 ) { - - const renderTargetProperties = properties.get( renderTarget ); - - _gl.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); - - const width = renderTarget.width; - const height = renderTarget.height; - let mask = 16384; - - if ( renderTarget.depthBuffer ) mask |= 256; - if ( renderTarget.stencilBuffer ) mask |= 1024; - - _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); - - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); // see #18905 - - } else { - - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); - - } - - } - - } - - function getRenderTargetSamples( renderTarget ) { - - return ( isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ? - Math.min( maxSamples, renderTarget.samples ) : 0; - - } - - function updateVideoTexture( texture ) { - - const frame = info.render.frame; - - // Check the last frame we updated the VideoTexture - - if ( _videoTextures.get( texture ) !== frame ) { - - _videoTextures.set( texture, frame ); - texture.update(); - - } - - } - - // backwards compatibility - - let warnedTexture2D = false; - let warnedTextureCube = false; - - function safeSetTexture2D( texture, slot ) { - - if ( texture && texture.isWebGLRenderTarget ) { - - if ( warnedTexture2D === false ) { - - console.warn( "THREE.WebGLTextures.safeSetTexture2D: don't use render targets as textures. Use their .texture property instead." ); - warnedTexture2D = true; - - } - - texture = texture.texture; - - } - - setTexture2D( texture, slot ); - - } - - function safeSetTextureCube( texture, slot ) { - - if ( texture && texture.isWebGLCubeRenderTarget ) { - - if ( warnedTextureCube === false ) { - - console.warn( "THREE.WebGLTextures.safeSetTextureCube: don't use cube render targets as textures. Use their .texture property instead." ); - warnedTextureCube = true; - - } - - texture = texture.texture; - - } - - // currently relying on the fact that WebGLCubeRenderTarget.texture is a Texture and NOT a CubeTexture - // TODO: unify these code paths - if ( ( texture && texture.isCubeTexture ) || - ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { - - // CompressedTexture can have Array in image :/ - - // this function alone should take care of cube textures - setTextureCube( texture, slot ); - - } else { - - // assumed: texture property of THREE.WebGLCubeRenderTarget - setTextureCubeDynamic( texture, slot ); - - } - - } - - // - - this.allocateTextureUnit = allocateTextureUnit; - this.resetTextureUnits = resetTextureUnits; - - this.setTexture2D = setTexture2D; - this.setTexture2DArray = setTexture2DArray; - this.setTexture3D = setTexture3D; - this.setTextureCube = setTextureCube; - this.setTextureCubeDynamic = setTextureCubeDynamic; - this.setupRenderTarget = setupRenderTarget; - this.updateRenderTargetMipmap = updateRenderTargetMipmap; - this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; - - this.safeSetTexture2D = safeSetTexture2D; - this.safeSetTextureCube = safeSetTextureCube; - - } - - function WebGLUtils( gl, extensions, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - function convert( p ) { - - let extension; - - if ( p === UnsignedByteType ) return 5121; - if ( p === UnsignedShort4444Type ) return 32819; - if ( p === UnsignedShort5551Type ) return 32820; - if ( p === UnsignedShort565Type ) return 33635; - - if ( p === ByteType ) return 5120; - if ( p === ShortType ) return 5122; - if ( p === UnsignedShortType ) return 5123; - if ( p === IntType ) return 5124; - if ( p === UnsignedIntType ) return 5125; - if ( p === FloatType ) return 5126; - - if ( p === HalfFloatType ) { - - if ( isWebGL2 ) return 5131; - - extension = extensions.get( 'OES_texture_half_float' ); - - if ( extension !== null ) { - - return extension.HALF_FLOAT_OES; - - } else { - - return null; - - } - - } - - if ( p === AlphaFormat ) return 6406; - if ( p === RGBFormat ) return 6407; - if ( p === RGBAFormat ) return 6408; - if ( p === LuminanceFormat ) return 6409; - if ( p === LuminanceAlphaFormat ) return 6410; - if ( p === DepthFormat ) return 6402; - if ( p === DepthStencilFormat ) return 34041; - if ( p === RedFormat ) return 6403; - - // WebGL2 formats. - - if ( p === RedIntegerFormat ) return 36244; - if ( p === RGFormat ) return 33319; - if ( p === RGIntegerFormat ) return 33320; - if ( p === RGBIntegerFormat ) return 36248; - if ( p === RGBAIntegerFormat ) return 36249; - - if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || - p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); - - if ( extension !== null ) { - - if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; - if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; - - } else { - - return null; - - } - - } - - if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || - p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - - if ( extension !== null ) { - - if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; - if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; - if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; - - } else { - - return null; - - } - - } - - if ( p === RGB_ETC1_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); - - if ( extension !== null ) { - - return extension.COMPRESSED_RGB_ETC1_WEBGL; - - } else { - - return null; - - } - - } - - if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_etc' ); - - if ( extension !== null ) { - - if ( p === RGB_ETC2_Format ) return extension.COMPRESSED_RGB8_ETC2; - if ( p === RGBA_ETC2_EAC_Format ) return extension.COMPRESSED_RGBA8_ETC2_EAC; - - } - - } - - if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || - p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || - p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || - p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || - p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format || - p === SRGB8_ALPHA8_ASTC_4x4_Format || p === SRGB8_ALPHA8_ASTC_5x4_Format || p === SRGB8_ALPHA8_ASTC_5x5_Format || - p === SRGB8_ALPHA8_ASTC_6x5_Format || p === SRGB8_ALPHA8_ASTC_6x6_Format || p === SRGB8_ALPHA8_ASTC_8x5_Format || - p === SRGB8_ALPHA8_ASTC_8x6_Format || p === SRGB8_ALPHA8_ASTC_8x8_Format || p === SRGB8_ALPHA8_ASTC_10x5_Format || - p === SRGB8_ALPHA8_ASTC_10x6_Format || p === SRGB8_ALPHA8_ASTC_10x8_Format || p === SRGB8_ALPHA8_ASTC_10x10_Format || - p === SRGB8_ALPHA8_ASTC_12x10_Format || p === SRGB8_ALPHA8_ASTC_12x12_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_astc' ); - - if ( extension !== null ) { - - // TODO Complete? - - return p; - - } else { - - return null; - - } - - } - - if ( p === RGBA_BPTC_Format ) { - - extension = extensions.get( 'EXT_texture_compression_bptc' ); - - if ( extension !== null ) { - - // TODO Complete? - - return p; - - } else { - - return null; - - } - - } - - if ( p === UnsignedInt248Type ) { - - if ( isWebGL2 ) return 34042; - - extension = extensions.get( 'WEBGL_depth_texture' ); - - if ( extension !== null ) { - - return extension.UNSIGNED_INT_24_8_WEBGL; - - } else { - - return null; - - } - - } - - } - - return { convert: convert }; - - } - - function ArrayCamera( array ) { - - PerspectiveCamera.call( this ); - - this.cameras = array || []; - - } - - ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), { - - constructor: ArrayCamera, - - isArrayCamera: true - - } ); - - function Group() { - - Object3D.call( this ); - - this.type = 'Group'; - - } - - Group.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Group, - - isGroup: true - - } ); - - function WebXRController() { - - this._targetRay = null; - this._grip = null; - this._hand = null; - - } - - Object.assign( WebXRController.prototype, { - - constructor: WebXRController, - - getHandSpace: function () { - - if ( this._hand === null ) { - - this._hand = new Group(); - this._hand.matrixAutoUpdate = false; - this._hand.visible = false; - - this._hand.joints = []; - this._hand.inputState = { pinching: false }; - - if ( window.XRHand ) { - - for ( let i = 0; i <= window.XRHand.LITTLE_PHALANX_TIP; i ++ ) { - - // The transform of this joint will be updated with the joint pose on each frame - const joint = new Group(); - joint.matrixAutoUpdate = false; - joint.visible = false; - this._hand.joints.push( joint ); - // ?? - this._hand.add( joint ); - - } - - } - - } - - return this._hand; - - }, - - getTargetRaySpace: function () { - - if ( this._targetRay === null ) { - - this._targetRay = new Group(); - this._targetRay.matrixAutoUpdate = false; - this._targetRay.visible = false; - - } - - return this._targetRay; - - }, - - getGripSpace: function () { - - if ( this._grip === null ) { - - this._grip = new Group(); - this._grip.matrixAutoUpdate = false; - this._grip.visible = false; - - } - - return this._grip; - - }, - - dispatchEvent: function ( event ) { - - if ( this._targetRay !== null ) { - - this._targetRay.dispatchEvent( event ); - - } - - if ( this._grip !== null ) { - - this._grip.dispatchEvent( event ); - - } - - if ( this._hand !== null ) { - - this._hand.dispatchEvent( event ); - - } - - return this; - - }, - - disconnect: function ( inputSource ) { - - this.dispatchEvent( { type: 'disconnected', data: inputSource } ); - - if ( this._targetRay !== null ) { - - this._targetRay.visible = false; - - } - - if ( this._grip !== null ) { - - this._grip.visible = false; - - } - - if ( this._hand !== null ) { - - this._hand.visible = false; - - } - - return this; - - }, - - update: function ( inputSource, frame, referenceSpace ) { - - let inputPose = null; - let gripPose = null; - let handPose = null; - - const targetRay = this._targetRay; - const grip = this._grip; - const hand = this._hand; - - if ( inputSource ) { - - if ( hand && inputSource.hand ) { - - handPose = true; - - for ( let i = 0; i <= window.XRHand.LITTLE_PHALANX_TIP; i ++ ) { - - if ( inputSource.hand[ i ] ) { - - // Update the joints groups with the XRJoint poses - const jointPose = frame.getJointPose( inputSource.hand[ i ], referenceSpace ); - const joint = hand.joints[ i ]; - - if ( jointPose !== null ) { - - joint.matrix.fromArray( jointPose.transform.matrix ); - joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); - joint.jointRadius = jointPose.radius; - - } - - joint.visible = jointPose !== null; - - // Custom events - - // Check pinch - const indexTip = hand.joints[ window.XRHand.INDEX_PHALANX_TIP ]; - const thumbTip = hand.joints[ window.XRHand.THUMB_PHALANX_TIP ]; - const distance = indexTip.position.distanceTo( thumbTip.position ); - - const distanceToPinch = 0.02; - const threshold = 0.005; - - if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { - - hand.inputState.pinching = false; - this.dispatchEvent( { - type: "pinchend", - handedness: inputSource.handedness, - target: this - } ); - - } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { - - hand.inputState.pinching = true; - this.dispatchEvent( { - type: "pinchstart", - handedness: inputSource.handedness, - target: this - } ); - - } - - } - - } - - } else { - - if ( targetRay !== null ) { - - inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); - - if ( inputPose !== null ) { - - targetRay.matrix.fromArray( inputPose.transform.matrix ); - targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); - - } - - } - - if ( grip !== null && inputSource.gripSpace ) { - - gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); - - if ( gripPose !== null ) { - - grip.matrix.fromArray( gripPose.transform.matrix ); - grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); - - } - - } - - } - - } - - if ( targetRay !== null ) { - - targetRay.visible = ( inputPose !== null ); - - } - - if ( grip !== null ) { - - grip.visible = ( gripPose !== null ); - - } - - if ( hand !== null ) { - - hand.visible = ( handPose !== null ); - - } - - return this; - - } - - } ); - - function WebXRManager( renderer, gl ) { - - const scope = this; - - let session = null; - - let framebufferScaleFactor = 1.0; - - let referenceSpace = null; - let referenceSpaceType = 'local-floor'; - - let pose = null; - - const controllers = []; - const inputSourcesMap = new Map(); - - // - - const cameraL = new PerspectiveCamera(); - cameraL.layers.enable( 1 ); - cameraL.viewport = new Vector4(); - - const cameraR = new PerspectiveCamera(); - cameraR.layers.enable( 2 ); - cameraR.viewport = new Vector4(); - - const cameras = [ cameraL, cameraR ]; - - const cameraVR = new ArrayCamera(); - cameraVR.layers.enable( 1 ); - cameraVR.layers.enable( 2 ); - - let _currentDepthNear = null; - let _currentDepthFar = null; - - // - - this.enabled = false; - - this.isPresenting = false; - - this.getController = function ( index ) { - - let controller = controllers[ index ]; - - if ( controller === undefined ) { - - controller = new WebXRController(); - controllers[ index ] = controller; - - } - - return controller.getTargetRaySpace(); - - }; - - this.getControllerGrip = function ( index ) { - - let controller = controllers[ index ]; - - if ( controller === undefined ) { - - controller = new WebXRController(); - controllers[ index ] = controller; - - } - - return controller.getGripSpace(); - - }; - - this.getHand = function ( index ) { - - let controller = controllers[ index ]; - - if ( controller === undefined ) { - - controller = new WebXRController(); - controllers[ index ] = controller; - - } - - return controller.getHandSpace(); - - }; - - // - - function onSessionEvent( event ) { - - const controller = inputSourcesMap.get( event.inputSource ); - - if ( controller ) { - - controller.dispatchEvent( { type: event.type } ); - - } - - } - - function onSessionEnd() { - - inputSourcesMap.forEach( function ( controller, inputSource ) { - - controller.disconnect( inputSource ); - - } ); - - inputSourcesMap.clear(); - - // - - renderer.setFramebuffer( null ); - renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830 - animation.stop(); - - scope.isPresenting = false; - - scope.dispatchEvent( { type: 'sessionend' } ); - - } - - function onRequestReferenceSpace( value ) { - - referenceSpace = value; - - animation.setContext( session ); - animation.start(); - - scope.isPresenting = true; - - scope.dispatchEvent( { type: 'sessionstart' } ); - - } - - this.setFramebufferScaleFactor = function ( value ) { - - framebufferScaleFactor = value; - - if ( scope.isPresenting === true ) { - - console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' ); - - } - - }; - - this.setReferenceSpaceType = function ( value ) { - - referenceSpaceType = value; - - if ( scope.isPresenting === true ) { - - console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' ); - - } - - }; - - this.getReferenceSpace = function () { - - return referenceSpace; - - }; - - this.getSession = function () { - - return session; - - }; - - this.setSession = function ( value ) { - - session = value; - - if ( session !== null ) { - - session.addEventListener( 'select', onSessionEvent ); - session.addEventListener( 'selectstart', onSessionEvent ); - session.addEventListener( 'selectend', onSessionEvent ); - session.addEventListener( 'squeeze', onSessionEvent ); - session.addEventListener( 'squeezestart', onSessionEvent ); - session.addEventListener( 'squeezeend', onSessionEvent ); - session.addEventListener( 'end', onSessionEnd ); - - const attributes = gl.getContextAttributes(); - - if ( attributes.xrCompatible !== true ) { - - gl.makeXRCompatible(); - - } - - const layerInit = { - antialias: attributes.antialias, - alpha: attributes.alpha, - depth: attributes.depth, - stencil: attributes.stencil, - framebufferScaleFactor: framebufferScaleFactor - }; - - // eslint-disable-next-line no-undef - const baseLayer = new XRWebGLLayer( session, gl, layerInit ); - - session.updateRenderState( { baseLayer: baseLayer } ); - - session.requestReferenceSpace( referenceSpaceType ).then( onRequestReferenceSpace ); - - // - - session.addEventListener( 'inputsourceschange', updateInputSources ); - - } - - }; - - function updateInputSources( event ) { - - const inputSources = session.inputSources; - - // Assign inputSources to available controllers - - for ( let i = 0; i < controllers.length; i ++ ) { - - inputSourcesMap.set( inputSources[ i ], controllers[ i ] ); - - } - - // Notify disconnected - - for ( let i = 0; i < event.removed.length; i ++ ) { - - const inputSource = event.removed[ i ]; - const controller = inputSourcesMap.get( inputSource ); - - if ( controller ) { - - controller.dispatchEvent( { type: 'disconnected', data: inputSource } ); - inputSourcesMap.delete( inputSource ); - - } - - } - - // Notify connected - - for ( let i = 0; i < event.added.length; i ++ ) { - - const inputSource = event.added[ i ]; - const controller = inputSourcesMap.get( inputSource ); - - if ( controller ) { - - controller.dispatchEvent( { type: 'connected', data: inputSource } ); - - } - - } - - } - - // - - const cameraLPos = new Vector3(); - const cameraRPos = new Vector3(); - - /** - * Assumes 2 cameras that are parallel and share an X-axis, and that - * the cameras' projection and world matrices have already been set. - * And that near and far planes are identical for both cameras. - * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 - */ - function setProjectionFromUnion( camera, cameraL, cameraR ) { - - cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); - cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); - - const ipd = cameraLPos.distanceTo( cameraRPos ); - - const projL = cameraL.projectionMatrix.elements; - const projR = cameraR.projectionMatrix.elements; - - // VR systems will have identical far and near planes, and - // most likely identical top and bottom frustum extents. - // Use the left camera for these values. - const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); - const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); - const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; - const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; - - const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; - const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; - const left = near * leftFov; - const right = near * rightFov; - - // Calculate the new camera's position offset from the - // left camera. xOffset should be roughly half `ipd`. - const zOffset = ipd / ( - leftFov + rightFov ); - const xOffset = zOffset * - leftFov; - - // TODO: Better way to apply this offset? - cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); - camera.translateX( xOffset ); - camera.translateZ( zOffset ); - camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); - - // Find the union of the frustum values of the cameras and scale - // the values so that the near plane's position does not change in world space, - // although must now be relative to the new union camera. - const near2 = near + zOffset; - const far2 = far + zOffset; - const left2 = left - xOffset; - const right2 = right + ( ipd - xOffset ); - const top2 = topFov * far / far2 * near2; - const bottom2 = bottomFov * far / far2 * near2; - - camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); - - } - - function updateCamera( camera, parent ) { - - if ( parent === null ) { - - camera.matrixWorld.copy( camera.matrix ); - - } else { - - camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); - - } - - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); - - } - - this.getCamera = function ( camera ) { - - cameraVR.near = cameraR.near = cameraL.near = camera.near; - cameraVR.far = cameraR.far = cameraL.far = camera.far; - - if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { - - // Note that the new renderState won't apply until the next frame. See #18320 - - session.updateRenderState( { - depthNear: cameraVR.near, - depthFar: cameraVR.far - } ); - - _currentDepthNear = cameraVR.near; - _currentDepthFar = cameraVR.far; - - } - - const parent = camera.parent; - const cameras = cameraVR.cameras; - - updateCamera( cameraVR, parent ); - - for ( let i = 0; i < cameras.length; i ++ ) { - - updateCamera( cameras[ i ], parent ); - - } - - // update camera and its children - - camera.matrixWorld.copy( cameraVR.matrixWorld ); - - const children = camera.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateMatrixWorld( true ); - - } - - // update projection matrix for proper view frustum culling - - if ( cameras.length === 2 ) { - - setProjectionFromUnion( cameraVR, cameraL, cameraR ); - - } else { - - // assume single camera setup (AR) - - cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); - - } - - return cameraVR; - - }; - - // Animation Loop - - let onAnimationFrameCallback = null; - - function onAnimationFrame( time, frame ) { - - pose = frame.getViewerPose( referenceSpace ); - - if ( pose !== null ) { - - const views = pose.views; - const baseLayer = session.renderState.baseLayer; - - renderer.setFramebuffer( baseLayer.framebuffer ); - - let cameraVRNeedsUpdate = false; - - // check if it's necessary to rebuild cameraVR's camera list - - if ( views.length !== cameraVR.cameras.length ) { - - cameraVR.cameras.length = 0; - cameraVRNeedsUpdate = true; - - } - - for ( let i = 0; i < views.length; i ++ ) { - - const view = views[ i ]; - const viewport = baseLayer.getViewport( view ); - - const camera = cameras[ i ]; - camera.matrix.fromArray( view.transform.matrix ); - camera.projectionMatrix.fromArray( view.projectionMatrix ); - camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); - - if ( i === 0 ) { - - cameraVR.matrix.copy( camera.matrix ); - - } - - if ( cameraVRNeedsUpdate === true ) { - - cameraVR.cameras.push( camera ); - - } - - } - - } - - // - - const inputSources = session.inputSources; - - for ( let i = 0; i < controllers.length; i ++ ) { - - const controller = controllers[ i ]; - const inputSource = inputSources[ i ]; - - controller.update( inputSource, frame, referenceSpace ); - - } - - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); - - } - - const animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); - - this.setAnimationLoop = function ( callback ) { - - onAnimationFrameCallback = callback; - - }; - - this.dispose = function () {}; - - } - - Object.assign( WebXRManager.prototype, EventDispatcher.prototype ); - - function WebGLMaterials( properties ) { - - function refreshFogUniforms( uniforms, fog ) { - - uniforms.fogColor.value.copy( fog.color ); - - if ( fog.isFog ) { - - uniforms.fogNear.value = fog.near; - uniforms.fogFar.value = fog.far; - - } else if ( fog.isFogExp2 ) { - - uniforms.fogDensity.value = fog.density; - - } - - } - - function refreshMaterialUniforms( uniforms, material, pixelRatio, height ) { - - if ( material.isMeshBasicMaterial ) { - - refreshUniformsCommon( uniforms, material ); - - } else if ( material.isMeshLambertMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsLambert( uniforms, material ); - - } else if ( material.isMeshToonMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsToon( uniforms, material ); - - } else if ( material.isMeshPhongMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsPhong( uniforms, material ); - - } else if ( material.isMeshStandardMaterial ) { - - refreshUniformsCommon( uniforms, material ); - - if ( material.isMeshPhysicalMaterial ) { - - refreshUniformsPhysical( uniforms, material ); - - } else { - - refreshUniformsStandard( uniforms, material ); - - } - - } else if ( material.isMeshMatcapMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsMatcap( uniforms, material ); - - } else if ( material.isMeshDepthMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsDepth( uniforms, material ); - - } else if ( material.isMeshDistanceMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsDistance( uniforms, material ); - - } else if ( material.isMeshNormalMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsNormal( uniforms, material ); - - } else if ( material.isLineBasicMaterial ) { - - refreshUniformsLine( uniforms, material ); - - if ( material.isLineDashedMaterial ) { - - refreshUniformsDash( uniforms, material ); - - } - - } else if ( material.isPointsMaterial ) { - - refreshUniformsPoints( uniforms, material, pixelRatio, height ); - - } else if ( material.isSpriteMaterial ) { - - refreshUniformsSprites( uniforms, material ); - - } else if ( material.isShadowMaterial ) { - - uniforms.color.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - - } else if ( material.isShaderMaterial ) { - - material.uniformsNeedUpdate = false; // #15581 - - } - - } - - function refreshUniformsCommon( uniforms, material ) { - - uniforms.opacity.value = material.opacity; - - if ( material.color ) { - - uniforms.diffuse.value.copy( material.color ); - - } - - if ( material.emissive ) { - - uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); - - } - - if ( material.map ) { - - uniforms.map.value = material.map; - - } - - if ( material.alphaMap ) { - - uniforms.alphaMap.value = material.alphaMap; - - } - - if ( material.specularMap ) { - - uniforms.specularMap.value = material.specularMap; - - } - - const envMap = properties.get( material ).envMap; - - if ( envMap ) { - - uniforms.envMap.value = envMap; - - uniforms.flipEnvMap.value = envMap.isCubeTexture ? - 1 : 1; - - uniforms.reflectivity.value = material.reflectivity; - uniforms.refractionRatio.value = material.refractionRatio; - - const maxMipLevel = properties.get( envMap ).__maxMipLevel; - - if ( maxMipLevel !== undefined ) { - - uniforms.maxMipLevel.value = maxMipLevel; - - } - - } - - if ( material.lightMap ) { - - uniforms.lightMap.value = material.lightMap; - uniforms.lightMapIntensity.value = material.lightMapIntensity; - - } - - if ( material.aoMap ) { - - uniforms.aoMap.value = material.aoMap; - uniforms.aoMapIntensity.value = material.aoMapIntensity; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. displacementMap map - // 4. normal map - // 5. bump map - // 6. roughnessMap map - // 7. metalnessMap map - // 8. alphaMap map - // 9. emissiveMap map - // 10. clearcoat map - // 11. clearcoat normal map - // 12. clearcoat roughnessMap map - - let uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.specularMap ) { - - uvScaleMap = material.specularMap; - - } else if ( material.displacementMap ) { - - uvScaleMap = material.displacementMap; - - } else if ( material.normalMap ) { - - uvScaleMap = material.normalMap; - - } else if ( material.bumpMap ) { - - uvScaleMap = material.bumpMap; - - } else if ( material.roughnessMap ) { - - uvScaleMap = material.roughnessMap; - - } else if ( material.metalnessMap ) { - - uvScaleMap = material.metalnessMap; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } else if ( material.emissiveMap ) { - - uvScaleMap = material.emissiveMap; - - } else if ( material.clearcoatMap ) { - - uvScaleMap = material.clearcoatMap; - - } else if ( material.clearcoatNormalMap ) { - - uvScaleMap = material.clearcoatNormalMap; - - } else if ( material.clearcoatRoughnessMap ) { - - uvScaleMap = material.clearcoatRoughnessMap; - - } - - if ( uvScaleMap !== undefined ) { - - // backwards compatibility - if ( uvScaleMap.isWebGLRenderTarget ) { - - uvScaleMap = uvScaleMap.texture; - - } - - if ( uvScaleMap.matrixAutoUpdate === true ) { - - uvScaleMap.updateMatrix(); - - } - - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - - } - - // uv repeat and offset setting priorities for uv2 - // 1. ao map - // 2. light map - - let uv2ScaleMap; - - if ( material.aoMap ) { - - uv2ScaleMap = material.aoMap; - - } else if ( material.lightMap ) { - - uv2ScaleMap = material.lightMap; - - } - - if ( uv2ScaleMap !== undefined ) { - - // backwards compatibility - if ( uv2ScaleMap.isWebGLRenderTarget ) { - - uv2ScaleMap = uv2ScaleMap.texture; - - } - - if ( uv2ScaleMap.matrixAutoUpdate === true ) { - - uv2ScaleMap.updateMatrix(); - - } - - uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix ); - - } - - } - - function refreshUniformsLine( uniforms, material ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - - } - - function refreshUniformsDash( uniforms, material ) { - - uniforms.dashSize.value = material.dashSize; - uniforms.totalSize.value = material.dashSize + material.gapSize; - uniforms.scale.value = material.scale; - - } - - function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.size.value = material.size * pixelRatio; - uniforms.scale.value = height * 0.5; - - if ( material.map ) { - - uniforms.map.value = material.map; - - } - - if ( material.alphaMap ) { - - uniforms.alphaMap.value = material.alphaMap; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map - - let uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } - - if ( uvScaleMap !== undefined ) { - - if ( uvScaleMap.matrixAutoUpdate === true ) { - - uvScaleMap.updateMatrix(); - - } - - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - - } - - } - - function refreshUniformsSprites( uniforms, material ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.rotation.value = material.rotation; - - if ( material.map ) { - - uniforms.map.value = material.map; - - } - - if ( material.alphaMap ) { - - uniforms.alphaMap.value = material.alphaMap; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map - - let uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } - - if ( uvScaleMap !== undefined ) { - - if ( uvScaleMap.matrixAutoUpdate === true ) { - - uvScaleMap.updateMatrix(); - - } - - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - - } - - } - - function refreshUniformsLambert( uniforms, material ) { - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - } - - function refreshUniformsPhong( uniforms, material ) { - - uniforms.specular.value.copy( material.specular ); - uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsToon( uniforms, material ) { - - if ( material.gradientMap ) { - - uniforms.gradientMap.value = material.gradientMap; - - } - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsStandard( uniforms, material ) { - - uniforms.roughness.value = material.roughness; - uniforms.metalness.value = material.metalness; - - if ( material.roughnessMap ) { - - uniforms.roughnessMap.value = material.roughnessMap; - - } - - if ( material.metalnessMap ) { - - uniforms.metalnessMap.value = material.metalnessMap; - - } - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - const envMap = properties.get( material ).envMap; - - if ( envMap ) { - - //uniforms.envMap.value = material.envMap; // part of uniforms common - uniforms.envMapIntensity.value = material.envMapIntensity; - - } - - } - - function refreshUniformsPhysical( uniforms, material ) { - - refreshUniformsStandard( uniforms, material ); - - uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common - - uniforms.clearcoat.value = material.clearcoat; - uniforms.clearcoatRoughness.value = material.clearcoatRoughness; - if ( material.sheen ) uniforms.sheen.value.copy( material.sheen ); - - if ( material.clearcoatMap ) { - - uniforms.clearcoatMap.value = material.clearcoatMap; - - } - - if ( material.clearcoatRoughnessMap ) { - - uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; - - } - - if ( material.clearcoatNormalMap ) { - - uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); - uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; - - if ( material.side === BackSide ) { - - uniforms.clearcoatNormalScale.value.negate(); - - } - - } - - uniforms.transmission.value = material.transmission; - - if ( material.transmissionMap ) { - - uniforms.transmissionMap.value = material.transmissionMap; - - } - - } - - function refreshUniformsMatcap( uniforms, material ) { - - if ( material.matcap ) { - - uniforms.matcap.value = material.matcap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsDepth( uniforms, material ) { - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsDistance( uniforms, material ) { - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - uniforms.referencePosition.value.copy( material.referencePosition ); - uniforms.nearDistance.value = material.nearDistance; - uniforms.farDistance.value = material.farDistance; - - } - - function refreshUniformsNormal( uniforms, material ) { - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - return { - refreshFogUniforms: refreshFogUniforms, - refreshMaterialUniforms: refreshMaterialUniforms - }; - - } - - function WebGLRenderer( parameters ) { - - parameters = parameters || {}; - - const _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ), - _context = parameters.context !== undefined ? parameters.context : null, - - _alpha = parameters.alpha !== undefined ? parameters.alpha : false, - _depth = parameters.depth !== undefined ? parameters.depth : true, - _stencil = parameters.stencil !== undefined ? parameters.stencil : true, - _antialias = parameters.antialias !== undefined ? parameters.antialias : false, - _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, - _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, - _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default', - _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; - - let currentRenderList = null; - let currentRenderState = null; - - // public properties - - this.domElement = _canvas; - - // Debug configuration container - this.debug = { - - /** - * Enables error checking and reporting when shader programs are being compiled - * @type {boolean} - */ - checkShaderErrors: true - }; - - // clearing - - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; - - // scene graph - - this.sortObjects = true; - - // user-defined clipping - - this.clippingPlanes = []; - this.localClippingEnabled = false; - - // physically based shading - - this.gammaFactor = 2.0; // for backwards compatibility - this.outputEncoding = LinearEncoding; - - // physical lights - - this.physicallyCorrectLights = false; - - // tone mapping - - this.toneMapping = NoToneMapping; - this.toneMappingExposure = 1.0; - - // morphs - - this.maxMorphTargets = 8; - this.maxMorphNormals = 4; - - // internal properties - - const _this = this; - - let _isContextLost = false; - - // internal state cache - - let _framebuffer = null; - - let _currentActiveCubeFace = 0; - let _currentActiveMipmapLevel = 0; - let _currentRenderTarget = null; - let _currentFramebuffer = null; - let _currentMaterialId = - 1; - - let _currentCamera = null; - let _currentArrayCamera = null; - - const _currentViewport = new Vector4(); - const _currentScissor = new Vector4(); - let _currentScissorTest = null; - - // - - let _width = _canvas.width; - let _height = _canvas.height; - - let _pixelRatio = 1; - let _opaqueSort = null; - let _transparentSort = null; - - const _viewport = new Vector4( 0, 0, _width, _height ); - const _scissor = new Vector4( 0, 0, _width, _height ); - let _scissorTest = false; - - // frustum - - const _frustum = new Frustum(); - - // clipping - - let _clippingEnabled = false; - let _localClippingEnabled = false; - - // camera matrices cache - - const _projScreenMatrix = new Matrix4(); - - const _vector3 = new Vector3(); - - const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; - - function getTargetPixelRatio() { - - return _currentRenderTarget === null ? _pixelRatio : 1; - - } - - // initialize - - let _gl = _context; - - function getContext( contextNames, contextAttributes ) { - - for ( let i = 0; i < contextNames.length; i ++ ) { - - const contextName = contextNames[ i ]; - const context = _canvas.getContext( contextName, contextAttributes ); - if ( context !== null ) return context; - - } - - return null; - - } - - try { - - const contextAttributes = { - alpha: _alpha, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer, - powerPreference: _powerPreference, - failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat - }; - - // event listeners must be registered before WebGL context is created, see #12753 - - _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); - - if ( _gl === null ) { - - const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ]; - - if ( _this.isWebGL1Renderer === true ) { - - contextNames.shift(); - - } - - _gl = getContext( contextNames, contextAttributes ); - - if ( _gl === null ) { - - if ( getContext( contextNames ) ) { - - throw new Error( 'Error creating WebGL context with your selected attributes.' ); - - } else { - - throw new Error( 'Error creating WebGL context.' ); - - } - - } - - } - - // Some experimental-webgl implementations do not have getShaderPrecisionFormat - - if ( _gl.getShaderPrecisionFormat === undefined ) { - - _gl.getShaderPrecisionFormat = function () { - - return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; - - }; - - } - - } catch ( error ) { - - console.error( 'THREE.WebGLRenderer: ' + error.message ); - throw error; - - } - - let extensions, capabilities, state, info; - let properties, textures, cubemaps, attributes, geometries, objects; - let programCache, materials, renderLists, renderStates, clipping; - - let background, morphtargets, bufferRenderer, indexedBufferRenderer; - - let utils, bindingStates; - - function initGLContext() { - - extensions = new WebGLExtensions( _gl ); - - capabilities = new WebGLCapabilities( _gl, extensions, parameters ); - - if ( capabilities.isWebGL2 === false ) { - - extensions.get( 'WEBGL_depth_texture' ); - extensions.get( 'OES_texture_float' ); - extensions.get( 'OES_texture_half_float' ); - extensions.get( 'OES_texture_half_float_linear' ); - extensions.get( 'OES_standard_derivatives' ); - extensions.get( 'OES_element_index_uint' ); - extensions.get( 'OES_vertex_array_object' ); - extensions.get( 'ANGLE_instanced_arrays' ); - - } - - extensions.get( 'OES_texture_float_linear' ); - - utils = new WebGLUtils( _gl, extensions, capabilities ); - - state = new WebGLState( _gl, extensions, capabilities ); - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); - - info = new WebGLInfo( _gl ); - properties = new WebGLProperties(); - textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); - cubemaps = new WebGLCubeMaps( _this ); - attributes = new WebGLAttributes( _gl, capabilities ); - bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); - geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); - objects = new WebGLObjects( _gl, geometries, attributes, info ); - morphtargets = new WebGLMorphtargets( _gl ); - clipping = new WebGLClipping( properties ); - programCache = new WebGLPrograms( _this, cubemaps, extensions, capabilities, bindingStates, clipping ); - materials = new WebGLMaterials( properties ); - renderLists = new WebGLRenderLists( properties ); - renderStates = new WebGLRenderStates(); - background = new WebGLBackground( _this, cubemaps, state, objects, _premultipliedAlpha ); - - bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); - indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); - - info.programs = programCache.programs; - - _this.capabilities = capabilities; - _this.extensions = extensions; - _this.properties = properties; - _this.renderLists = renderLists; - _this.state = state; - _this.info = info; - - } - - initGLContext(); - - // xr - - const xr = new WebXRManager( _this, _gl ); - - this.xr = xr; - - // shadow map - - const shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize ); - - this.shadowMap = shadowMap; - - // API - - this.getContext = function () { - - return _gl; - - }; - - this.getContextAttributes = function () { - - return _gl.getContextAttributes(); - - }; - - this.forceContextLoss = function () { - - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.loseContext(); - - }; - - this.forceContextRestore = function () { - - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.restoreContext(); - - }; - - this.getPixelRatio = function () { - - return _pixelRatio; - - }; - - this.setPixelRatio = function ( value ) { - - if ( value === undefined ) return; - - _pixelRatio = value; - - this.setSize( _width, _height, false ); - - }; - - this.getSize = function ( target ) { - - if ( target === undefined ) { - - console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' ); - - target = new Vector2(); - - } - - return target.set( _width, _height ); - - }; - - this.setSize = function ( width, height, updateStyle ) { - - if ( xr.isPresenting ) { - - console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); - return; - - } - - _width = width; - _height = height; - - _canvas.width = Math.floor( width * _pixelRatio ); - _canvas.height = Math.floor( height * _pixelRatio ); - - if ( updateStyle !== false ) { - - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; - - } - - this.setViewport( 0, 0, width, height ); - - }; - - this.getDrawingBufferSize = function ( target ) { - - if ( target === undefined ) { - - console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' ); - - target = new Vector2(); - - } - - return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); - - }; - - this.setDrawingBufferSize = function ( width, height, pixelRatio ) { - - _width = width; - _height = height; - - _pixelRatio = pixelRatio; - - _canvas.width = Math.floor( width * pixelRatio ); - _canvas.height = Math.floor( height * pixelRatio ); - - this.setViewport( 0, 0, width, height ); - - }; - - this.getCurrentViewport = function ( target ) { - - if ( target === undefined ) { - - console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' ); - - target = new Vector4(); - - } - - return target.copy( _currentViewport ); - - }; - - this.getViewport = function ( target ) { - - return target.copy( _viewport ); - - }; - - this.setViewport = function ( x, y, width, height ) { - - if ( x.isVector4 ) { - - _viewport.set( x.x, x.y, x.z, x.w ); - - } else { - - _viewport.set( x, y, width, height ); - - } - - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); - - }; - - this.getScissor = function ( target ) { - - return target.copy( _scissor ); - - }; - - this.setScissor = function ( x, y, width, height ) { - - if ( x.isVector4 ) { - - _scissor.set( x.x, x.y, x.z, x.w ); - - } else { - - _scissor.set( x, y, width, height ); - - } - - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - - }; - - this.getScissorTest = function () { - - return _scissorTest; - - }; - - this.setScissorTest = function ( boolean ) { - - state.setScissorTest( _scissorTest = boolean ); - - }; - - this.setOpaqueSort = function ( method ) { - - _opaqueSort = method; - - }; - - this.setTransparentSort = function ( method ) { - - _transparentSort = method; - - }; - - // Clearing - - this.getClearColor = function () { - - return background.getClearColor(); - - }; - - this.setClearColor = function () { - - background.setClearColor.apply( background, arguments ); - - }; - - this.getClearAlpha = function () { - - return background.getClearAlpha(); - - }; - - this.setClearAlpha = function () { - - background.setClearAlpha.apply( background, arguments ); - - }; - - this.clear = function ( color, depth, stencil ) { - - let bits = 0; - - if ( color === undefined || color ) bits |= 16384; - if ( depth === undefined || depth ) bits |= 256; - if ( stencil === undefined || stencil ) bits |= 1024; - - _gl.clear( bits ); - - }; - - this.clearColor = function () { - - this.clear( true, false, false ); - - }; - - this.clearDepth = function () { - - this.clear( false, true, false ); - - }; - - this.clearStencil = function () { - - this.clear( false, false, true ); - - }; - - // - - this.dispose = function () { - - _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); - - renderLists.dispose(); - renderStates.dispose(); - properties.dispose(); - cubemaps.dispose(); - objects.dispose(); - bindingStates.dispose(); - - xr.dispose(); - - animation.stop(); - - }; - - // Events - - function onContextLost( event ) { - - event.preventDefault(); - - console.log( 'THREE.WebGLRenderer: Context Lost.' ); - - _isContextLost = true; - - } - - function onContextRestore( /* event */ ) { - - console.log( 'THREE.WebGLRenderer: Context Restored.' ); - - _isContextLost = false; - - initGLContext(); - - } - - function onMaterialDispose( event ) { - - const material = event.target; - - material.removeEventListener( 'dispose', onMaterialDispose ); - - deallocateMaterial( material ); - - } - - // Buffer deallocation - - function deallocateMaterial( material ) { - - releaseMaterialProgramReference( material ); - - properties.remove( material ); - - } - - - function releaseMaterialProgramReference( material ) { - - const programInfo = properties.get( material ).program; - - if ( programInfo !== undefined ) { - - programCache.releaseProgram( programInfo ); - - } - - } - - // Buffer rendering - - function renderObjectImmediate( object, program ) { - - object.render( function ( object ) { - - _this.renderBufferImmediate( object, program ); - - } ); - - } - - this.renderBufferImmediate = function ( object, program ) { - - bindingStates.initAttributes(); - - const buffers = properties.get( object ); - - if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); - if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); - if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); - if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); - - const programAttributes = program.getAttributes(); - - if ( object.hasPositions ) { - - _gl.bindBuffer( 34962, buffers.position ); - _gl.bufferData( 34962, object.positionArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.position ); - _gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 ); - - } - - if ( object.hasNormals ) { - - _gl.bindBuffer( 34962, buffers.normal ); - _gl.bufferData( 34962, object.normalArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.normal ); - _gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 ); - - } - - if ( object.hasUvs ) { - - _gl.bindBuffer( 34962, buffers.uv ); - _gl.bufferData( 34962, object.uvArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.uv ); - _gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 ); - - } - - if ( object.hasColors ) { - - _gl.bindBuffer( 34962, buffers.color ); - _gl.bufferData( 34962, object.colorArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.color ); - _gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 ); - - } - - bindingStates.disableUnusedAttributes(); - - _gl.drawArrays( 4, 0, object.count ); - - object.count = 0; - - }; - - this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { - - if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) - - const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); - - const program = setProgram( camera, scene, material, object ); - - state.setMaterial( material, frontFaceCW ); - - // - - let index = geometry.index; - const position = geometry.attributes.position; - - // - - if ( index === null ) { - - if ( position === undefined || position.count === 0 ) return; - - } else if ( index.count === 0 ) { - - return; - - } - - // - - let rangeFactor = 1; - - if ( material.wireframe === true ) { - - index = geometries.getWireframeAttribute( geometry ); - rangeFactor = 2; - - } - - if ( material.morphTargets || material.morphNormals ) { - - morphtargets.update( object, geometry, material, program ); - - } - - bindingStates.setup( object, material, program, geometry, index ); - - let attribute; - let renderer = bufferRenderer; - - if ( index !== null ) { - - attribute = attributes.get( index ); - - renderer = indexedBufferRenderer; - renderer.setIndex( attribute ); - - } - - // - - const dataCount = ( index !== null ) ? index.count : position.count; - - const rangeStart = geometry.drawRange.start * rangeFactor; - const rangeCount = geometry.drawRange.count * rangeFactor; - - const groupStart = group !== null ? group.start * rangeFactor : 0; - const groupCount = group !== null ? group.count * rangeFactor : Infinity; - - const drawStart = Math.max( rangeStart, groupStart ); - const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; - - const drawCount = Math.max( 0, drawEnd - drawStart + 1 ); - - if ( drawCount === 0 ) return; - - // - - if ( object.isMesh ) { - - if ( material.wireframe === true ) { - - state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); - renderer.setMode( 1 ); - - } else { - - renderer.setMode( 4 ); - - } - - } else if ( object.isLine ) { - - let lineWidth = material.linewidth; - - if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material - - state.setLineWidth( lineWidth * getTargetPixelRatio() ); - - if ( object.isLineSegments ) { - - renderer.setMode( 1 ); - - } else if ( object.isLineLoop ) { - - renderer.setMode( 2 ); - - } else { - - renderer.setMode( 3 ); - - } - - } else if ( object.isPoints ) { - - renderer.setMode( 0 ); - - } else if ( object.isSprite ) { - - renderer.setMode( 4 ); - - } - - if ( object.isInstancedMesh ) { - - renderer.renderInstances( drawStart, drawCount, object.count ); - - } else if ( geometry.isInstancedBufferGeometry ) { - - const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount ); - - renderer.renderInstances( drawStart, drawCount, instanceCount ); - - } else { - - renderer.render( drawStart, drawCount ); - - } - - }; - - // Compile - - this.compile = function ( scene, camera ) { - - currentRenderState = renderStates.get( scene, camera ); - currentRenderState.init(); - - scene.traverse( function ( object ) { - - if ( object.isLight ) { - - currentRenderState.pushLight( object ); - - if ( object.castShadow ) { - - currentRenderState.pushShadow( object ); - - } - - } - - } ); - - currentRenderState.setupLights( camera ); - - const compiled = new WeakMap(); - - scene.traverse( function ( object ) { - - const material = object.material; - - if ( material ) { - - if ( Array.isArray( material ) ) { - - for ( let i = 0; i < material.length; i ++ ) { - - const material2 = material[ i ]; - - if ( compiled.has( material2 ) === false ) { - - initMaterial( material2, scene, object ); - compiled.set( material2 ); - - } - - } - - } else if ( compiled.has( material ) === false ) { - - initMaterial( material, scene, object ); - compiled.set( material ); - - } - - } - - } ); - - }; - - // Animation Loop - - let onAnimationFrameCallback = null; - - function onAnimationFrame( time ) { - - if ( xr.isPresenting ) return; - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); - - } - - const animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); - - if ( typeof window !== 'undefined' ) animation.setContext( window ); - - this.setAnimationLoop = function ( callback ) { - - onAnimationFrameCallback = callback; - xr.setAnimationLoop( callback ); - - ( callback === null ) ? animation.stop() : animation.start(); - - }; - - // Rendering - - this.render = function ( scene, camera ) { - - let renderTarget, forceClear; - - if ( arguments[ 2 ] !== undefined ) { - - console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' ); - renderTarget = arguments[ 2 ]; - - } - - if ( arguments[ 3 ] !== undefined ) { - - console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' ); - forceClear = arguments[ 3 ]; - - } - - if ( camera !== undefined && camera.isCamera !== true ) { - - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; - - } - - if ( _isContextLost === true ) return; - - // reset caching for this frame - - bindingStates.resetDefaultState(); - _currentMaterialId = - 1; - _currentCamera = null; - - // update scene graph - - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - - // update camera matrices and frustum - - if ( camera.parent === null ) camera.updateMatrixWorld(); - - if ( xr.enabled === true && xr.isPresenting === true ) { - - camera = xr.getCamera( camera ); - - } - - // - if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget ); - - currentRenderState = renderStates.get( scene, camera ); - currentRenderState.init(); - - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromProjectionMatrix( _projScreenMatrix ); - - _localClippingEnabled = this.localClippingEnabled; - _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); - - currentRenderList = renderLists.get( scene, camera ); - currentRenderList.init(); - - projectObject( scene, camera, 0, _this.sortObjects ); - - currentRenderList.finish(); - - if ( _this.sortObjects === true ) { - - currentRenderList.sort( _opaqueSort, _transparentSort ); - - } - - // - - if ( _clippingEnabled === true ) clipping.beginShadows(); - - const shadowsArray = currentRenderState.state.shadowsArray; - - shadowMap.render( shadowsArray, scene, camera ); - - currentRenderState.setupLights( camera ); - - if ( _clippingEnabled === true ) clipping.endShadows(); - - // - - if ( this.info.autoReset === true ) this.info.reset(); - - if ( renderTarget !== undefined ) { - - this.setRenderTarget( renderTarget ); - - } - - // - - background.render( currentRenderList, scene, camera, forceClear ); - - // render scene - - const opaqueObjects = currentRenderList.opaque; - const transparentObjects = currentRenderList.transparent; - - if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); - if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); - - // - - if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); - - // - - if ( _currentRenderTarget !== null ) { - - // Generate mipmap if we're using any kind of mipmap filtering - - textures.updateRenderTargetMipmap( _currentRenderTarget ); - - // resolve multisample renderbuffers to a single-sample texture if necessary - - textures.updateMultisampleRenderTarget( _currentRenderTarget ); - - } - - // Ensure depth buffer writing is enabled so it can be cleared on next render - - state.buffers.depth.setTest( true ); - state.buffers.depth.setMask( true ); - state.buffers.color.setMask( true ); - - state.setPolygonOffset( false ); - - // _gl.finish(); - - currentRenderList = null; - currentRenderState = null; - - }; - - function projectObject( object, camera, groupOrder, sortObjects ) { - - if ( object.visible === false ) return; - - const visible = object.layers.test( camera.layers ); - - if ( visible ) { - - if ( object.isGroup ) { - - groupOrder = object.renderOrder; - - } else if ( object.isLOD ) { - - if ( object.autoUpdate === true ) object.update( camera ); - - } else if ( object.isLight ) { - - currentRenderState.pushLight( object ); - - if ( object.castShadow ) { - - currentRenderState.pushShadow( object ); - - } - - } else if ( object.isSprite ) { - - if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { - - if ( sortObjects ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - const geometry = objects.update( object ); - const material = object.material; - - if ( material.visible ) { - - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - - } - - } - - } else if ( object.isImmediateRenderObject ) { - - if ( sortObjects ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null ); - - } else if ( object.isMesh || object.isLine || object.isPoints ) { - - if ( object.isSkinnedMesh ) { - - // update skeleton only once in a frame - - if ( object.skeleton.frame !== info.render.frame ) { - - object.skeleton.update(); - object.skeleton.frame = info.render.frame; - - } - - } - - if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { - - if ( sortObjects ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - const geometry = objects.update( object ); - const material = object.material; - - if ( Array.isArray( material ) ) { - - const groups = geometry.groups; - - for ( let i = 0, l = groups.length; i < l; i ++ ) { - - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; - - if ( groupMaterial && groupMaterial.visible ) { - - currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); - - } - - } - - } else if ( material.visible ) { - - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - - } - - } - - } - - } - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - projectObject( children[ i ], camera, groupOrder, sortObjects ); - - } - - } - - function renderObjects( renderList, scene, camera ) { - - const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; - - for ( let i = 0, l = renderList.length; i < l; i ++ ) { - - const renderItem = renderList[ i ]; - - const object = renderItem.object; - const geometry = renderItem.geometry; - const material = overrideMaterial === null ? renderItem.material : overrideMaterial; - const group = renderItem.group; - - if ( camera.isArrayCamera ) { - - _currentArrayCamera = camera; - - const cameras = camera.cameras; - - for ( let j = 0, jl = cameras.length; j < jl; j ++ ) { - - const camera2 = cameras[ j ]; - - if ( object.layers.test( camera2.layers ) ) { - - state.viewport( _currentViewport.copy( camera2.viewport ) ); - - currentRenderState.setupLights( camera2 ); - - renderObject( object, scene, camera2, geometry, material, group ); - - } - - } - - } else { - - _currentArrayCamera = null; - - renderObject( object, scene, camera, geometry, material, group ); - - } - - } - - } - - function renderObject( object, scene, camera, geometry, material, group ) { - - object.onBeforeRender( _this, scene, camera, geometry, material, group ); - currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); - - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - - if ( object.isImmediateRenderObject ) { - - const program = setProgram( camera, scene, material, object ); - - state.setMaterial( material ); - - bindingStates.reset(); - - renderObjectImmediate( object, program ); - - } else { - - _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - - } - - object.onAfterRender( _this, scene, camera, geometry, material, group ); - currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); - - } - - function initMaterial( material, scene, object ) { - - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - - const materialProperties = properties.get( material ); - - const lights = currentRenderState.state.lights; - const shadowsArray = currentRenderState.state.shadowsArray; - - const lightsStateVersion = lights.state.version; - - const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); - const programCacheKey = programCache.getProgramCacheKey( parameters ); - - let program = materialProperties.program; - let programChange = true; - - if ( program === undefined ) { - - // new material - material.addEventListener( 'dispose', onMaterialDispose ); - - } else if ( program.cacheKey !== programCacheKey ) { - - // changed glsl or parameters - releaseMaterialProgramReference( material ); - - } else if ( materialProperties.lightsStateVersion !== lightsStateVersion ) { - - programChange = false; - - } else if ( parameters.shaderID !== undefined ) { - - // same glsl and uniform list, envMap still needs the update here to avoid a frame-late effect - - const environment = material.isMeshStandardMaterial ? scene.environment : null; - materialProperties.envMap = cubemaps.get( material.envMap || environment ); - - return; - - } else { - - // only rebuild uniform list - programChange = false; - - } - - if ( programChange ) { - - parameters.uniforms = programCache.getUniforms( material ); - - material.onBeforeCompile( parameters, _this ); - - program = programCache.acquireProgram( parameters, programCacheKey ); - - materialProperties.program = program; - materialProperties.uniforms = parameters.uniforms; - materialProperties.outputEncoding = parameters.outputEncoding; - - } - - const uniforms = materialProperties.uniforms; - - if ( ! material.isShaderMaterial && - ! material.isRawShaderMaterial || - material.clipping === true ) { - - materialProperties.numClippingPlanes = clipping.numPlanes; - materialProperties.numIntersection = clipping.numIntersection; - uniforms.clippingPlanes = clipping.uniform; - - } - - materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; - materialProperties.fog = scene.fog; - materialProperties.envMap = cubemaps.get( material.envMap || materialProperties.environment ); - - // store the light setup it was created for - - materialProperties.needsLights = materialNeedsLights( material ); - materialProperties.lightsStateVersion = lightsStateVersion; - - if ( materialProperties.needsLights ) { - - // wire up the material to this renderer's lighting state - - uniforms.ambientLightColor.value = lights.state.ambient; - uniforms.lightProbe.value = lights.state.probe; - uniforms.directionalLights.value = lights.state.directional; - uniforms.directionalLightShadows.value = lights.state.directionalShadow; - uniforms.spotLights.value = lights.state.spot; - uniforms.spotLightShadows.value = lights.state.spotShadow; - uniforms.rectAreaLights.value = lights.state.rectArea; - uniforms.ltc_1.value = lights.state.rectAreaLTC1; - uniforms.ltc_2.value = lights.state.rectAreaLTC2; - uniforms.pointLights.value = lights.state.point; - uniforms.pointLightShadows.value = lights.state.pointShadow; - uniforms.hemisphereLights.value = lights.state.hemi; - - uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; - uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; - uniforms.spotShadowMap.value = lights.state.spotShadowMap; - uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; - uniforms.pointShadowMap.value = lights.state.pointShadowMap; - uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; - // TODO (abelnation): add area lights shadow info to uniforms - - } - - const progUniforms = materialProperties.program.getUniforms(); - const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); - - materialProperties.uniformsList = uniformsList; - - } - - function setProgram( camera, scene, material, object ) { - - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - - textures.resetTextureUnits(); - - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; - const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding; - const envMap = cubemaps.get( material.envMap || environment ); - - const materialProperties = properties.get( material ); - const lights = currentRenderState.state.lights; - - if ( _clippingEnabled === true ) { - - if ( _localClippingEnabled === true || camera !== _currentCamera ) { - - const useCache = - camera === _currentCamera && - material.id === _currentMaterialId; - - // we might want to call this function with some ClippingGroup - // object instead of the material, once it becomes feasible - // (#8465, #8379) - clipping.setState( material, camera, useCache ); - - } - - } - - if ( material.version === materialProperties.__version ) { - - if ( material.fog && materialProperties.fog !== fog ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.environment !== environment ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.numClippingPlanes !== undefined && - ( materialProperties.numClippingPlanes !== clipping.numPlanes || - materialProperties.numIntersection !== clipping.numIntersection ) ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.outputEncoding !== encoding ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.envMap !== envMap ) { - - initMaterial( material, scene, object ); - - } - - } else { - - initMaterial( material, scene, object ); - materialProperties.__version = material.version; - - } - - let refreshProgram = false; - let refreshMaterial = false; - let refreshLights = false; - - const program = materialProperties.program, - p_uniforms = program.getUniforms(), - m_uniforms = materialProperties.uniforms; - - if ( state.useProgram( program.program ) ) { - - refreshProgram = true; - refreshMaterial = true; - refreshLights = true; - - } - - if ( material.id !== _currentMaterialId ) { - - _currentMaterialId = material.id; - - refreshMaterial = true; - - } - - if ( refreshProgram || _currentCamera !== camera ) { - - p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); - - if ( capabilities.logarithmicDepthBuffer ) { - - p_uniforms.setValue( _gl, 'logDepthBufFC', - 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - - } - - if ( _currentCamera !== camera ) { - - _currentCamera = camera; - - // lighting uniforms depend on the camera so enforce an update - // now, in case this material supports lights - or later, when - // the next material that does gets activated: - - refreshMaterial = true; // set to true on material change - refreshLights = true; // remains set until update done - - } - - // load material specific uniforms - // (shader material also gets them for the sake of genericity) - - if ( material.isShaderMaterial || - material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshStandardMaterial || - material.envMap ) { - - const uCamPos = p_uniforms.map.cameraPosition; - - if ( uCamPos !== undefined ) { - - uCamPos.setValue( _gl, - _vector3.setFromMatrixPosition( camera.matrixWorld ) ); - - } - - } - - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial ) { - - p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); - - } - - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial || - material.isShadowMaterial || - material.skinning ) { - - p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); - - } - - } - - // skinning uniforms must be set even if material didn't change - // auto-setting of texture unit for bone texture must go before other textures - // otherwise textures used for skinning can take over texture units reserved for other material textures - - if ( material.skinning ) { - - p_uniforms.setOptional( _gl, object, 'bindMatrix' ); - p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); - - const skeleton = object.skeleton; - - if ( skeleton ) { - - const bones = skeleton.bones; - - if ( capabilities.floatVertexTextures ) { - - if ( skeleton.boneTexture === undefined ) { - - // layout (1 matrix = 4 pixels) - // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) - // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) - // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) - // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) - // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) - - - let size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix - size = MathUtils.ceilPowerOfTwo( size ); - size = Math.max( size, 4 ); - - const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel - boneMatrices.set( skeleton.boneMatrices ); // copy current values - - const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); - - skeleton.boneMatrices = boneMatrices; - skeleton.boneTexture = boneTexture; - skeleton.boneTextureSize = size; - - } - - p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); - p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); - - } else { - - p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); - - } - - } - - } - - if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { - - materialProperties.receiveShadow = object.receiveShadow; - p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); - - } - - if ( refreshMaterial ) { - - p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); - - if ( materialProperties.needsLights ) { - - // the current material requires lighting info - - // note: all lighting uniforms are always set correctly - // they simply reference the renderer's state for their - // values - // - // use the current material's .needsUpdate flags to set - // the GL state when required - - markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); - - } - - // refresh uniforms common to several materials - - if ( fog && material.fog ) { - - materials.refreshFogUniforms( m_uniforms, fog ); - - } - - materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height ); - - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - - } - - if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { - - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - material.uniformsNeedUpdate = false; - - } - - if ( material.isSpriteMaterial ) { - - p_uniforms.setValue( _gl, 'center', object.center ); - - } - - // common matrices - - p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); - p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); - p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); - - return program; - - } - - // If uniforms are marked as clean, they don't need to be loaded to the GPU. - - function markUniformsLightsNeedsUpdate( uniforms, value ) { - - uniforms.ambientLightColor.needsUpdate = value; - uniforms.lightProbe.needsUpdate = value; - - uniforms.directionalLights.needsUpdate = value; - uniforms.directionalLightShadows.needsUpdate = value; - uniforms.pointLights.needsUpdate = value; - uniforms.pointLightShadows.needsUpdate = value; - uniforms.spotLights.needsUpdate = value; - uniforms.spotLightShadows.needsUpdate = value; - uniforms.rectAreaLights.needsUpdate = value; - uniforms.hemisphereLights.needsUpdate = value; - - } - - function materialNeedsLights( material ) { - - return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || - material.isMeshStandardMaterial || material.isShadowMaterial || - ( material.isShaderMaterial && material.lights === true ); - - } - - // - this.setFramebuffer = function ( value ) { - - if ( _framebuffer !== value && _currentRenderTarget === null ) _gl.bindFramebuffer( 36160, value ); - - _framebuffer = value; - - }; - - this.getActiveCubeFace = function () { - - return _currentActiveCubeFace; - - }; - - this.getActiveMipmapLevel = function () { - - return _currentActiveMipmapLevel; - - }; - - this.getRenderList = function () { - - return currentRenderList; - - }; - - this.setRenderList = function ( renderList ) { - - currentRenderList = renderList; - - }; - - this.getRenderState = function () { - - return currentRenderState; - - }; - - this.setRenderState = function ( renderState ) { - - currentRenderState = renderState; - - }; - - this.getRenderTarget = function () { - - return _currentRenderTarget; - - }; - - this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { - - _currentRenderTarget = renderTarget; - _currentActiveCubeFace = activeCubeFace; - _currentActiveMipmapLevel = activeMipmapLevel; - - if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { - - textures.setupRenderTarget( renderTarget ); - - } - - let framebuffer = _framebuffer; - let isCube = false; - - if ( renderTarget ) { - - const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; - - if ( renderTarget.isWebGLCubeRenderTarget ) { - - framebuffer = __webglFramebuffer[ activeCubeFace ]; - isCube = true; - - } else if ( renderTarget.isWebGLMultisampleRenderTarget ) { - - framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; - - } else { - - framebuffer = __webglFramebuffer; - - } - - _currentViewport.copy( renderTarget.viewport ); - _currentScissor.copy( renderTarget.scissor ); - _currentScissorTest = renderTarget.scissorTest; - - } else { - - _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); - _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); - _currentScissorTest = _scissorTest; - - } - - if ( _currentFramebuffer !== framebuffer ) { - - _gl.bindFramebuffer( 36160, framebuffer ); - _currentFramebuffer = framebuffer; - - } - - state.viewport( _currentViewport ); - state.scissor( _currentScissor ); - state.setScissorTest( _currentScissorTest ); - - if ( isCube ) { - - const textureProperties = properties.get( renderTarget.texture ); - _gl.framebufferTexture2D( 36160, 36064, 34069 + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); - - } - - }; - - this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { - - if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - return; - - } - - let framebuffer = properties.get( renderTarget ).__webglFramebuffer; - - if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { - - framebuffer = framebuffer[ activeCubeFaceIndex ]; - - } - - if ( framebuffer ) { - - let restore = false; - - if ( framebuffer !== _currentFramebuffer ) { - - _gl.bindFramebuffer( 36160, framebuffer ); - - restore = true; - - } - - try { - - const texture = renderTarget.texture; - const textureFormat = texture.format; - const textureType = texture.type; - - if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); - return; - - } - - if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // IE11, Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! ( textureType === HalfFloatType && ( capabilities.isWebGL2 ? extensions.get( 'EXT_color_buffer_float' ) : extensions.get( 'EXT_color_buffer_half_float' ) ) ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); - return; - - } - - if ( _gl.checkFramebufferStatus( 36160 ) === 36053 ) { - - // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) - - if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); - - } - - } else { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); - - } - - } finally { - - if ( restore ) { - - _gl.bindFramebuffer( 36160, _currentFramebuffer ); - - } - - } - - } - - }; - - this.copyFramebufferToTexture = function ( position, texture, level ) { - - if ( level === undefined ) level = 0; - - const levelScale = Math.pow( 2, - level ); - const width = Math.floor( texture.image.width * levelScale ); - const height = Math.floor( texture.image.height * levelScale ); - const glFormat = utils.convert( texture.format ); - - textures.setTexture2D( texture, 0 ); - - _gl.copyTexImage2D( 3553, level, glFormat, position.x, position.y, width, height, 0 ); - - state.unbindTexture(); - - }; - - this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level ) { - - if ( level === undefined ) level = 0; - - const width = srcTexture.image.width; - const height = srcTexture.image.height; - const glFormat = utils.convert( dstTexture.format ); - const glType = utils.convert( dstTexture.type ); - - textures.setTexture2D( dstTexture, 0 ); - - // As another texture upload may have changed pixelStorei - // parameters, make sure they are correct for the dstTexture - _gl.pixelStorei( 37440, dstTexture.flipY ); - _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); - _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); - - if ( srcTexture.isDataTexture ) { - - _gl.texSubImage2D( 3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); - - } else { - - if ( srcTexture.isCompressedTexture ) { - - _gl.compressedTexSubImage2D( 3553, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); - - } else { - - _gl.texSubImage2D( 3553, level, position.x, position.y, glFormat, glType, srcTexture.image ); - - } - - } - - // Generate mipmaps only when copying level 0 - if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( 3553 ); - - state.unbindTexture(); - - }; - - this.initTexture = function ( texture ) { - - textures.setTexture2D( texture, 0 ); - - state.unbindTexture(); - - }; - - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef - - } - - } - - function WebGL1Renderer( parameters ) { - - WebGLRenderer.call( this, parameters ); - - } - - WebGL1Renderer.prototype = Object.assign( Object.create( WebGLRenderer.prototype ), { - - constructor: WebGL1Renderer, - - isWebGL1Renderer: true - - } ); - - class FogExp2 { - - constructor( color, density ) { - - Object.defineProperty( this, 'isFogExp2', { value: true } ); - - this.name = ''; - - this.color = new Color( color ); - this.density = ( density !== undefined ) ? density : 0.00025; - - } - - clone() { - - return new FogExp2( this.color, this.density ); - - } - - toJSON( /* meta */ ) { - - return { - type: 'FogExp2', - color: this.color.getHex(), - density: this.density - }; - - } - - } - - class Fog { - - constructor( color, near, far ) { - - Object.defineProperty( this, 'isFog', { value: true } ); - - this.name = ''; - - this.color = new Color( color ); - - this.near = ( near !== undefined ) ? near : 1; - this.far = ( far !== undefined ) ? far : 1000; - - } - - clone() { - - return new Fog( this.color, this.near, this.far ); - - } - - toJSON( /* meta */ ) { - - return { - type: 'Fog', - color: this.color.getHex(), - near: this.near, - far: this.far - }; - - } - - } - - class Scene extends Object3D { - - constructor() { - - super(); - - Object.defineProperty( this, 'isScene', { value: true } ); - - this.type = 'Scene'; - - this.background = null; - this.environment = null; - this.fog = null; - - this.overrideMaterial = null; - - this.autoUpdate = true; // checked by the renderer - - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef - - } - - } - - copy( source, recursive ) { - - super.copy( source, recursive ); - - if ( source.background !== null ) this.background = source.background.clone(); - if ( source.environment !== null ) this.environment = source.environment.clone(); - if ( source.fog !== null ) this.fog = source.fog.clone(); - - if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); - - this.autoUpdate = source.autoUpdate; - this.matrixAutoUpdate = source.matrixAutoUpdate; - - return this; - - } - - toJSON( meta ) { - - const data = super.toJSON( meta ); - - if ( this.background !== null ) data.object.background = this.background.toJSON( meta ); - if ( this.environment !== null ) data.object.environment = this.environment.toJSON( meta ); - if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); - - return data; - - } - - } - - function InterleavedBuffer( array, stride ) { - - this.array = array; - this.stride = stride; - this.count = array !== undefined ? array.length / stride : 0; - - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; - - this.version = 0; - - this.uuid = MathUtils.generateUUID(); - - } - - Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - Object.assign( InterleavedBuffer.prototype, { - - isInterleavedBuffer: true, - - onUploadCallback: function () {}, - - setUsage: function ( value ) { - - this.usage = value; - - return this; - - }, - - copy: function ( source ) { - - this.array = new source.array.constructor( source.array ); - this.count = source.count; - this.stride = source.stride; - this.usage = source.usage; - - return this; - - }, - - copyAt: function ( index1, attribute, index2 ) { - - index1 *= this.stride; - index2 *= attribute.stride; - - for ( let i = 0, l = this.stride; i < l; i ++ ) { - - this.array[ index1 + i ] = attribute.array[ index2 + i ]; - - } - - return this; - - }, - - set: function ( value, offset ) { - - if ( offset === undefined ) offset = 0; - - this.array.set( value, offset ); - - return this; - - }, - - clone: function ( data ) { - - if ( data.arrayBuffers === undefined ) { - - data.arrayBuffers = {}; - - } - - if ( this.array.buffer._uuid === undefined ) { - - this.array.buffer._uuid = MathUtils.generateUUID(); - - } - - if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - - data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; - - } - - const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); - - const ib = new InterleavedBuffer( array, this.stride ); - ib.setUsage( this.usage ); - - return ib; - - }, - - onUpload: function ( callback ) { - - this.onUploadCallback = callback; - - return this; - - }, - - toJSON: function ( data ) { - - if ( data.arrayBuffers === undefined ) { - - data.arrayBuffers = {}; - - } - - // generate UUID for array buffer if necessary - - if ( this.array.buffer._uuid === undefined ) { - - this.array.buffer._uuid = MathUtils.generateUUID(); - - } - - if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - - data.arrayBuffers[ this.array.buffer._uuid ] = Array.prototype.slice.call( new Uint32Array( this.array.buffer ) ); - - } - - // - - return { - uuid: this.uuid, - buffer: this.array.buffer._uuid, - type: this.array.constructor.name, - stride: this.stride - }; - - } - - } ); - - const _vector$6 = new Vector3(); - - function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) { - - this.name = ''; - - this.data = interleavedBuffer; - this.itemSize = itemSize; - this.offset = offset; - - this.normalized = normalized === true; - - } - - Object.defineProperties( InterleavedBufferAttribute.prototype, { - - count: { - - get: function () { - - return this.data.count; - - } - - }, - - array: { - - get: function () { - - return this.data.array; - - } - - }, - - needsUpdate: { - - set: function ( value ) { - - this.data.needsUpdate = value; - - } - - } - - } ); - - Object.assign( InterleavedBufferAttribute.prototype, { - - isInterleavedBufferAttribute: true, - - applyMatrix4: function ( m ) { - - for ( let i = 0, l = this.data.count; i < l; i ++ ) { - - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); - - _vector$6.applyMatrix4( m ); - - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - - } - - return this; - - }, - - setX: function ( index, x ) { - - this.data.array[ index * this.data.stride + this.offset ] = x; - - return this; - - }, - - setY: function ( index, y ) { - - this.data.array[ index * this.data.stride + this.offset + 1 ] = y; - - return this; - - }, - - setZ: function ( index, z ) { - - this.data.array[ index * this.data.stride + this.offset + 2 ] = z; - - return this; - - }, - - setW: function ( index, w ) { - - this.data.array[ index * this.data.stride + this.offset + 3 ] = w; - - return this; - - }, - - getX: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset ]; - - }, - - getY: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 1 ]; - - }, - - getZ: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 2 ]; - - }, - - getW: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 3 ]; - - }, - - setXY: function ( index, x, y ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - - return this; - - }, - - setXYZ: function ( index, x, y, z ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - - return this; - - }, - - setXYZW: function ( index, x, y, z, w ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - this.data.array[ index + 3 ] = w; - - return this; - - }, - - clone: function ( data ) { - - if ( data === undefined ) { - - console.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data.' ); - - const array = []; - - for ( let i = 0; i < this.count; i ++ ) { - - const index = i * this.data.stride + this.offset; - - for ( let j = 0; j < this.itemSize; j ++ ) { - - array.push( this.data.array[ index + j ] ); - - } - - } - - return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); - - } else { - - if ( data.interleavedBuffers === undefined ) { - - data.interleavedBuffers = {}; - - } - - if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { - - data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); - - } - - return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); - - } - - }, - - toJSON: function ( data ) { - - if ( data === undefined ) { - - console.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data.' ); - - const array = []; - - for ( let i = 0; i < this.count; i ++ ) { - - const index = i * this.data.stride + this.offset; - - for ( let j = 0; j < this.itemSize; j ++ ) { - - array.push( this.data.array[ index + j ] ); - - } - - } - - // deinterleave data and save it as an ordinary buffer attribute for now - - return { - itemSize: this.itemSize, - type: this.array.constructor.name, - array: array, - normalized: this.normalized - }; - - } else { - - // save as true interlaved attribtue - - if ( data.interleavedBuffers === undefined ) { - - data.interleavedBuffers = {}; - - } - - if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { - - data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); - - } - - return { - isInterleavedBufferAttribute: true, - itemSize: this.itemSize, - data: this.data.uuid, - offset: this.offset, - normalized: this.normalized - }; - - } - - } - - } ); - - /** - * parameters = { - * color: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * rotation: , - * sizeAttenuation: - * } - */ - - function SpriteMaterial( parameters ) { - - Material.call( this ); - - this.type = 'SpriteMaterial'; - - this.color = new Color( 0xffffff ); - - this.map = null; - - this.alphaMap = null; - - this.rotation = 0; - - this.sizeAttenuation = true; - - this.transparent = true; - - this.setValues( parameters ); - - } - - SpriteMaterial.prototype = Object.create( Material.prototype ); - SpriteMaterial.prototype.constructor = SpriteMaterial; - SpriteMaterial.prototype.isSpriteMaterial = true; - - SpriteMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.rotation = source.rotation; - - this.sizeAttenuation = source.sizeAttenuation; - - return this; - - }; - - let _geometry; - - const _intersectPoint = new Vector3(); - const _worldScale = new Vector3(); - const _mvPosition = new Vector3(); - - const _alignedPosition = new Vector2(); - const _rotatedPosition = new Vector2(); - const _viewWorldMatrix = new Matrix4(); - - const _vA$1 = new Vector3(); - const _vB$1 = new Vector3(); - const _vC$1 = new Vector3(); - - const _uvA$1 = new Vector2(); - const _uvB$1 = new Vector2(); - const _uvC$1 = new Vector2(); - - function Sprite( material ) { - - Object3D.call( this ); - - this.type = 'Sprite'; - - if ( _geometry === undefined ) { - - _geometry = new BufferGeometry(); - - const float32Array = new Float32Array( [ - - 0.5, - 0.5, 0, 0, 0, - 0.5, - 0.5, 0, 1, 0, - 0.5, 0.5, 0, 1, 1, - - 0.5, 0.5, 0, 0, 1 - ] ); - - const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); - - _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); - _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); - _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); - - } - - this.geometry = _geometry; - this.material = ( material !== undefined ) ? material : new SpriteMaterial(); - - this.center = new Vector2( 0.5, 0.5 ); - - } - - Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Sprite, - - isSprite: true, - - raycast: function ( raycaster, intersects ) { - - if ( raycaster.camera === null ) { - - console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); - - } - - _worldScale.setFromMatrixScale( this.matrixWorld ); - - _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); - this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); - - _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); - - if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { - - _worldScale.multiplyScalar( - _mvPosition.z ); - - } - - const rotation = this.material.rotation; - let sin, cos; - - if ( rotation !== 0 ) { - - cos = Math.cos( rotation ); - sin = Math.sin( rotation ); - - } - - const center = this.center; - - transformVertex( _vA$1.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vB$1.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vC$1.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - - _uvA$1.set( 0, 0 ); - _uvB$1.set( 1, 0 ); - _uvC$1.set( 1, 1 ); - - // check first triangle - let intersect = raycaster.ray.intersectTriangle( _vA$1, _vB$1, _vC$1, false, _intersectPoint ); - - if ( intersect === null ) { - - // check second triangle - transformVertex( _vB$1.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - _uvB$1.set( 0, 1 ); - - intersect = raycaster.ray.intersectTriangle( _vA$1, _vC$1, _vB$1, false, _intersectPoint ); - if ( intersect === null ) { - - return; - - } - - } - - const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); - - if ( distance < raycaster.near || distance > raycaster.far ) return; - - intersects.push( { - - distance: distance, - point: _intersectPoint.clone(), - uv: Triangle.getUV( _intersectPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ), - face: null, - object: this - - } ); - - }, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - if ( source.center !== undefined ) this.center.copy( source.center ); - - this.material = source.material; - - return this; - - } - - } ); - - function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { - - // compute position in camera space - _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); - - // to check if rotation is not zero - if ( sin !== undefined ) { - - _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); - _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); - - } else { - - _rotatedPosition.copy( _alignedPosition ); - - } - - - vertexPosition.copy( mvPosition ); - vertexPosition.x += _rotatedPosition.x; - vertexPosition.y += _rotatedPosition.y; - - // transform to world space - vertexPosition.applyMatrix4( _viewWorldMatrix ); - - } - - const _v1$4 = new Vector3(); - const _v2$2 = new Vector3(); - - function LOD() { - - Object3D.call( this ); - - this._currentLevel = 0; - - this.type = 'LOD'; - - Object.defineProperties( this, { - levels: { - enumerable: true, - value: [] - } - } ); - - this.autoUpdate = true; - - } - - LOD.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: LOD, - - isLOD: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source, false ); - - const levels = source.levels; - - for ( let i = 0, l = levels.length; i < l; i ++ ) { - - const level = levels[ i ]; - - this.addLevel( level.object.clone(), level.distance ); - - } - - this.autoUpdate = source.autoUpdate; - - return this; - - }, - - addLevel: function ( object, distance ) { - - if ( distance === undefined ) distance = 0; - - distance = Math.abs( distance ); - - const levels = this.levels; - - let l; - - for ( l = 0; l < levels.length; l ++ ) { - - if ( distance < levels[ l ].distance ) { - - break; - - } - - } - - levels.splice( l, 0, { distance: distance, object: object } ); - - this.add( object ); - - return this; - - }, - - getCurrentLevel: function () { - - return this._currentLevel; - - }, - - getObjectForDistance: function ( distance ) { - - const levels = this.levels; - - if ( levels.length > 0 ) { - - let i, l; - - for ( i = 1, l = levels.length; i < l; i ++ ) { - - if ( distance < levels[ i ].distance ) { - - break; - - } - - } - - return levels[ i - 1 ].object; - - } - - return null; - - }, - - raycast: function ( raycaster, intersects ) { - - const levels = this.levels; - - if ( levels.length > 0 ) { - - _v1$4.setFromMatrixPosition( this.matrixWorld ); - - const distance = raycaster.ray.origin.distanceTo( _v1$4 ); - - this.getObjectForDistance( distance ).raycast( raycaster, intersects ); - - } - - }, - - update: function ( camera ) { - - const levels = this.levels; - - if ( levels.length > 1 ) { - - _v1$4.setFromMatrixPosition( camera.matrixWorld ); - _v2$2.setFromMatrixPosition( this.matrixWorld ); - - const distance = _v1$4.distanceTo( _v2$2 ) / camera.zoom; - - levels[ 0 ].object.visible = true; - - let i, l; - - for ( i = 1, l = levels.length; i < l; i ++ ) { - - if ( distance >= levels[ i ].distance ) { - - levels[ i - 1 ].object.visible = false; - levels[ i ].object.visible = true; - - } else { - - break; - - } - - } - - this._currentLevel = i - 1; - - for ( ; i < l; i ++ ) { - - levels[ i ].object.visible = false; - - } - - } - - }, - - toJSON: function ( meta ) { - - const data = Object3D.prototype.toJSON.call( this, meta ); - - if ( this.autoUpdate === false ) data.object.autoUpdate = false; - - data.object.levels = []; - - const levels = this.levels; - - for ( let i = 0, l = levels.length; i < l; i ++ ) { - - const level = levels[ i ]; - - data.object.levels.push( { - object: level.object.uuid, - distance: level.distance - } ); - - } - - return data; - - } - - } ); - - function SkinnedMesh( geometry, material ) { - - if ( geometry && geometry.isGeometry ) { - - console.error( 'THREE.SkinnedMesh no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - Mesh.call( this, geometry, material ); - - this.type = 'SkinnedMesh'; - - this.bindMode = 'attached'; - this.bindMatrix = new Matrix4(); - this.bindMatrixInverse = new Matrix4(); - - } - - SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { - - constructor: SkinnedMesh, - - isSkinnedMesh: true, - - copy: function ( source ) { - - Mesh.prototype.copy.call( this, source ); - - this.bindMode = source.bindMode; - this.bindMatrix.copy( source.bindMatrix ); - this.bindMatrixInverse.copy( source.bindMatrixInverse ); - - this.skeleton = source.skeleton; - - return this; - - }, - - bind: function ( skeleton, bindMatrix ) { - - this.skeleton = skeleton; - - if ( bindMatrix === undefined ) { - - this.updateMatrixWorld( true ); - - this.skeleton.calculateInverses(); - - bindMatrix = this.matrixWorld; - - } - - this.bindMatrix.copy( bindMatrix ); - this.bindMatrixInverse.getInverse( bindMatrix ); - - }, - - pose: function () { - - this.skeleton.pose(); - - }, - - normalizeSkinWeights: function () { - - const vector = new Vector4(); - - const skinWeight = this.geometry.attributes.skinWeight; - - for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { - - vector.x = skinWeight.getX( i ); - vector.y = skinWeight.getY( i ); - vector.z = skinWeight.getZ( i ); - vector.w = skinWeight.getW( i ); - - const scale = 1.0 / vector.manhattanLength(); - - if ( scale !== Infinity ) { - - vector.multiplyScalar( scale ); - - } else { - - vector.set( 1, 0, 0, 0 ); // do something reasonable - - } - - skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); - - } - - }, - - updateMatrixWorld: function ( force ) { - - Mesh.prototype.updateMatrixWorld.call( this, force ); - - if ( this.bindMode === 'attached' ) { - - this.bindMatrixInverse.getInverse( this.matrixWorld ); - - } else if ( this.bindMode === 'detached' ) { - - this.bindMatrixInverse.getInverse( this.bindMatrix ); - - } else { - - console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); - - } - - }, - - boneTransform: ( function () { - - const basePosition = new Vector3(); - - const skinIndex = new Vector4(); - const skinWeight = new Vector4(); - - const vector = new Vector3(); - const matrix = new Matrix4(); - - return function ( index, target ) { - - const skeleton = this.skeleton; - const geometry = this.geometry; - - skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); - skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); - - basePosition.fromBufferAttribute( geometry.attributes.position, index ).applyMatrix4( this.bindMatrix ); - - target.set( 0, 0, 0 ); - - for ( let i = 0; i < 4; i ++ ) { - - const weight = skinWeight.getComponent( i ); - - if ( weight !== 0 ) { - - const boneIndex = skinIndex.getComponent( i ); - - matrix.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); - - target.addScaledVector( vector.copy( basePosition ).applyMatrix4( matrix ), weight ); - - } - - } - - return target.applyMatrix4( this.bindMatrixInverse ); - - }; - - }() ) - - } ); - - const _offsetMatrix = new Matrix4(); - const _identityMatrix = new Matrix4(); - - function Skeleton( bones, boneInverses ) { - - // copy the bone array - - bones = bones || []; - - this.bones = bones.slice( 0 ); - this.boneMatrices = new Float32Array( this.bones.length * 16 ); - - this.frame = - 1; - - // use the supplied bone inverses or calculate the inverses - - if ( boneInverses === undefined ) { - - this.calculateInverses(); - - } else { - - if ( this.bones.length === boneInverses.length ) { - - this.boneInverses = boneInverses.slice( 0 ); - - } else { - - console.warn( 'THREE.Skeleton boneInverses is the wrong length.' ); - - this.boneInverses = []; - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - this.boneInverses.push( new Matrix4() ); - - } - - } - - } - - } - - Object.assign( Skeleton.prototype, { - - calculateInverses: function () { - - this.boneInverses = []; - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - const inverse = new Matrix4(); - - if ( this.bones[ i ] ) { - - inverse.getInverse( this.bones[ i ].matrixWorld ); - - } - - this.boneInverses.push( inverse ); - - } - - }, - - pose: function () { - - // recover the bind-time world matrices - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - const bone = this.bones[ i ]; - - if ( bone ) { - - bone.matrixWorld.getInverse( this.boneInverses[ i ] ); - - } - - } - - // compute the local matrices, positions, rotations and scales - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - const bone = this.bones[ i ]; - - if ( bone ) { - - if ( bone.parent && bone.parent.isBone ) { - - bone.matrix.getInverse( bone.parent.matrixWorld ); - bone.matrix.multiply( bone.matrixWorld ); - - } else { - - bone.matrix.copy( bone.matrixWorld ); - - } - - bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); - - } - - } - - }, - - update: function () { - - const bones = this.bones; - const boneInverses = this.boneInverses; - const boneMatrices = this.boneMatrices; - const boneTexture = this.boneTexture; - - // flatten bone matrices to array - - for ( let i = 0, il = bones.length; i < il; i ++ ) { - - // compute the offset between the current and the original transform - - const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; - - _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); - _offsetMatrix.toArray( boneMatrices, i * 16 ); - - } - - if ( boneTexture !== undefined ) { - - boneTexture.needsUpdate = true; - - } - - }, - - clone: function () { - - return new Skeleton( this.bones, this.boneInverses ); - - }, - - getBoneByName: function ( name ) { - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - const bone = this.bones[ i ]; - - if ( bone.name === name ) { - - return bone; - - } - - } - - return undefined; - - }, - - dispose: function ( ) { - - if ( this.boneTexture ) { - - this.boneTexture.dispose(); - - this.boneTexture = undefined; - - } - - } - - } ); - - function Bone() { - - Object3D.call( this ); - - this.type = 'Bone'; - - } - - Bone.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Bone, - - isBone: true - - } ); - - const _instanceLocalMatrix = new Matrix4(); - const _instanceWorldMatrix = new Matrix4(); - - const _instanceIntersects = []; - - const _mesh = new Mesh(); - - function InstancedMesh( geometry, material, count ) { - - Mesh.call( this, geometry, material ); - - this.instanceMatrix = new BufferAttribute( new Float32Array( count * 16 ), 16 ); - this.instanceColor = null; - - this.count = count; - - this.frustumCulled = false; - - } - - InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { - - constructor: InstancedMesh, - - isInstancedMesh: true, - - copy: function ( source ) { - - Mesh.prototype.copy.call( this, source ); - - this.instanceMatrix.copy( source.instanceMatrix ); - this.count = source.count; - - return this; - - }, - - setColorAt: function ( index, color ) { - - if ( this.instanceColor === null ) { - - this.instanceColor = new BufferAttribute( new Float32Array( this.count * 3 ), 3 ); - - } - - color.toArray( this.instanceColor.array, index * 3 ); - - }, - - getMatrixAt: function ( index, matrix ) { - - matrix.fromArray( this.instanceMatrix.array, index * 16 ); - - }, - - raycast: function ( raycaster, intersects ) { - - const matrixWorld = this.matrixWorld; - const raycastTimes = this.count; - - _mesh.geometry = this.geometry; - _mesh.material = this.material; - - if ( _mesh.material === undefined ) return; - - for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { - - // calculate the world matrix for each instance - - this.getMatrixAt( instanceId, _instanceLocalMatrix ); - - _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); - - // the mesh represents this single instance - - _mesh.matrixWorld = _instanceWorldMatrix; - - _mesh.raycast( raycaster, _instanceIntersects ); - - // process the result of raycast - - for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { - - const intersect = _instanceIntersects[ i ]; - intersect.instanceId = instanceId; - intersect.object = this; - intersects.push( intersect ); - - } - - _instanceIntersects.length = 0; - - } - - }, - - setMatrixAt: function ( index, matrix ) { - - matrix.toArray( this.instanceMatrix.array, index * 16 ); - - }, - - updateMorphTargets: function () { - - } - - } ); - - /** - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * linecap: "round", - * linejoin: "round" - * } - */ - - function LineBasicMaterial( parameters ) { - - Material.call( this ); - - this.type = 'LineBasicMaterial'; - - this.color = new Color( 0xffffff ); - - this.linewidth = 1; - this.linecap = 'round'; - this.linejoin = 'round'; - - this.morphTargets = false; - - this.setValues( parameters ); - - } - - LineBasicMaterial.prototype = Object.create( Material.prototype ); - LineBasicMaterial.prototype.constructor = LineBasicMaterial; - - LineBasicMaterial.prototype.isLineBasicMaterial = true; - - LineBasicMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.linewidth = source.linewidth; - this.linecap = source.linecap; - this.linejoin = source.linejoin; - - this.morphTargets = source.morphTargets; - - return this; - - }; - - const _start = new Vector3(); - const _end = new Vector3(); - const _inverseMatrix$1 = new Matrix4(); - const _ray$1 = new Ray(); - const _sphere$2 = new Sphere(); - - function Line( geometry, material, mode ) { - - if ( mode === 1 ) { - - console.error( 'THREE.Line: parameter THREE.LinePieces no longer supported. Use THREE.LineSegments instead.' ); - - } - - Object3D.call( this ); - - this.type = 'Line'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new LineBasicMaterial(); - - this.updateMorphTargets(); - - } - - Line.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Line, - - isLine: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - this.material = source.material; - this.geometry = source.geometry; - - return this; - - }, - - computeLineDistances: function () { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - // we assume non-indexed geometry - - if ( geometry.index === null ) { - - const positionAttribute = geometry.attributes.position; - const lineDistances = [ 0 ]; - - for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { - - _start.fromBufferAttribute( positionAttribute, i - 1 ); - _end.fromBufferAttribute( positionAttribute, i ); - - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += _start.distanceTo( _end ); - - } - - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - - } else { - - console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - - } - - } else if ( geometry.isGeometry ) { - - const vertices = geometry.vertices; - const lineDistances = geometry.lineDistances; - - lineDistances[ 0 ] = 0; - - for ( let i = 1, l = vertices.length; i < l; i ++ ) { - - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += vertices[ i - 1 ].distanceTo( vertices[ i ] ); - - } - - } - - return this; - - }, - - raycast: function ( raycaster, intersects ) { - - const geometry = this.geometry; - const matrixWorld = this.matrixWorld; - const threshold = raycaster.params.Line.threshold; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _sphere$2.copy( geometry.boundingSphere ); - _sphere$2.applyMatrix4( matrixWorld ); - _sphere$2.radius += threshold; - - if ( raycaster.ray.intersectsSphere( _sphere$2 ) === false ) return; - - // - - _inverseMatrix$1.getInverse( matrixWorld ); - _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); - - const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - const localThresholdSq = localThreshold * localThreshold; - - const vStart = new Vector3(); - const vEnd = new Vector3(); - const interSegment = new Vector3(); - const interRay = new Vector3(); - const step = ( this && this.isLineSegments ) ? 2 : 1; - - if ( geometry.isBufferGeometry ) { - - const index = geometry.index; - const attributes = geometry.attributes; - const positions = attributes.position.array; - - if ( index !== null ) { - - const indices = index.array; - - for ( let i = 0, l = indices.length - 1; i < l; i += step ) { - - const a = indices[ i ]; - const b = indices[ i + 1 ]; - - vStart.fromArray( positions, a * 3 ); - vEnd.fromArray( positions, b * 3 ); - - const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - - if ( distSq > localThresholdSq ) continue; - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - const distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } else { - - for ( let i = 0, l = positions.length / 3 - 1; i < l; i += step ) { - - vStart.fromArray( positions, 3 * i ); - vEnd.fromArray( positions, 3 * i + 3 ); - - const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - - if ( distSq > localThresholdSq ) continue; - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - const distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } - - } else if ( geometry.isGeometry ) { - - const vertices = geometry.vertices; - const nbVertices = vertices.length; - - for ( let i = 0; i < nbVertices - 1; i += step ) { - - const distSq = _ray$1.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); - - if ( distSq > localThresholdSq ) continue; - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - const distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } - - }, - - updateMorphTargets: function () { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); - - if ( keys.length > 0 ) { - - const morphAttribute = morphAttributes[ keys[ 0 ] ]; - - if ( morphAttribute !== undefined ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - - const name = morphAttribute[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } - - } - - } - - } else { - - const morphTargets = geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - console.error( 'THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - } - - } ); - - const _start$1 = new Vector3(); - const _end$1 = new Vector3(); - - function LineSegments( geometry, material ) { - - Line.call( this, geometry, material ); - - this.type = 'LineSegments'; - - } - - LineSegments.prototype = Object.assign( Object.create( Line.prototype ), { - - constructor: LineSegments, - - isLineSegments: true, - - computeLineDistances: function () { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - // we assume non-indexed geometry - - if ( geometry.index === null ) { - - const positionAttribute = geometry.attributes.position; - const lineDistances = []; - - for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { - - _start$1.fromBufferAttribute( positionAttribute, i ); - _end$1.fromBufferAttribute( positionAttribute, i + 1 ); - - lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; - lineDistances[ i + 1 ] = lineDistances[ i ] + _start$1.distanceTo( _end$1 ); - - } - - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - - } else { - - console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - - } - - } else if ( geometry.isGeometry ) { - - const vertices = geometry.vertices; - const lineDistances = geometry.lineDistances; - - for ( let i = 0, l = vertices.length; i < l; i += 2 ) { - - _start$1.copy( vertices[ i ] ); - _end$1.copy( vertices[ i + 1 ] ); - - lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; - lineDistances[ i + 1 ] = lineDistances[ i ] + _start$1.distanceTo( _end$1 ); - - } - - } - - return this; - - } - - } ); - - function LineLoop( geometry, material ) { - - Line.call( this, geometry, material ); - - this.type = 'LineLoop'; - - } - - LineLoop.prototype = Object.assign( Object.create( Line.prototype ), { - - constructor: LineLoop, - - isLineLoop: true, - - } ); - - /** - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * - * size: , - * sizeAttenuation: - * - * morphTargets: - * } - */ - - function PointsMaterial( parameters ) { - - Material.call( this ); - - this.type = 'PointsMaterial'; - - this.color = new Color( 0xffffff ); - - this.map = null; - - this.alphaMap = null; - - this.size = 1; - this.sizeAttenuation = true; - - this.morphTargets = false; - - this.setValues( parameters ); - - } - - PointsMaterial.prototype = Object.create( Material.prototype ); - PointsMaterial.prototype.constructor = PointsMaterial; - - PointsMaterial.prototype.isPointsMaterial = true; - - PointsMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.size = source.size; - this.sizeAttenuation = source.sizeAttenuation; - - this.morphTargets = source.morphTargets; - - return this; - - }; - - const _inverseMatrix$2 = new Matrix4(); - const _ray$2 = new Ray(); - const _sphere$3 = new Sphere(); - const _position$1 = new Vector3(); - - function Points( geometry, material ) { - - Object3D.call( this ); - - this.type = 'Points'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new PointsMaterial(); - - this.updateMorphTargets(); - - } - - Points.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Points, - - isPoints: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - this.material = source.material; - this.geometry = source.geometry; - - return this; - - }, - - raycast: function ( raycaster, intersects ) { - - const geometry = this.geometry; - const matrixWorld = this.matrixWorld; - const threshold = raycaster.params.Points.threshold; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _sphere$3.copy( geometry.boundingSphere ); - _sphere$3.applyMatrix4( matrixWorld ); - _sphere$3.radius += threshold; - - if ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) return; - - // - - _inverseMatrix$2.getInverse( matrixWorld ); - _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); - - const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - const localThresholdSq = localThreshold * localThreshold; - - if ( geometry.isBufferGeometry ) { - - const index = geometry.index; - const attributes = geometry.attributes; - const positions = attributes.position.array; - - if ( index !== null ) { - - const indices = index.array; - - for ( let i = 0, il = indices.length; i < il; i ++ ) { - - const a = indices[ i ]; - - _position$1.fromArray( positions, a * 3 ); - - testPoint( _position$1, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); - - } - - } else { - - for ( let i = 0, l = positions.length / 3; i < l; i ++ ) { - - _position$1.fromArray( positions, i * 3 ); - - testPoint( _position$1, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); - - } - - } - - } else { - - const vertices = geometry.vertices; - - for ( let i = 0, l = vertices.length; i < l; i ++ ) { - - testPoint( vertices[ i ], i, localThresholdSq, matrixWorld, raycaster, intersects, this ); - - } - - } - - }, - - updateMorphTargets: function () { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); - - if ( keys.length > 0 ) { - - const morphAttribute = morphAttributes[ keys[ 0 ] ]; - - if ( morphAttribute !== undefined ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - - const name = morphAttribute[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } - - } - - } - - } else { - - const morphTargets = geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - console.error( 'THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - } - - } ); - - function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { - - const rayPointDistanceSq = _ray$2.distanceSqToPoint( point ); - - if ( rayPointDistanceSq < localThresholdSq ) { - - const intersectPoint = new Vector3(); - - _ray$2.closestPointToPoint( point, intersectPoint ); - intersectPoint.applyMatrix4( matrixWorld ); - - const distance = raycaster.ray.origin.distanceTo( intersectPoint ); - - if ( distance < raycaster.near || distance > raycaster.far ) return; - - intersects.push( { - - distance: distance, - distanceToRay: Math.sqrt( rayPointDistanceSq ), - point: intersectPoint, - index: index, - face: null, - object: object - - } ); - - } - - } - - function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - - Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.format = format !== undefined ? format : RGBFormat; - - this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - - this.generateMipmaps = false; - - const scope = this; - - function updateVideo() { - - scope.needsUpdate = true; - video.requestVideoFrameCallback( updateVideo ); - - } - - if ( 'requestVideoFrameCallback' in video ) { - - video.requestVideoFrameCallback( updateVideo ); - - } - - } - - VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), { - - constructor: VideoTexture, - - isVideoTexture: true, - - update: function () { - - const video = this.image; - const hasVideoFrameCallback = 'requestVideoFrameCallback' in video; - - if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { - - this.needsUpdate = true; - - } - - } - - } ); - - function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.image = { width: width, height: height }; - this.mipmaps = mipmaps; - - // no flipping for cube textures - // (also flipping doesn't work for compressed textures ) - - this.flipY = false; - - // can't generate mipmaps for compressed textures - // mips must be embedded in DDS files - - this.generateMipmaps = false; - - } - - CompressedTexture.prototype = Object.create( Texture.prototype ); - CompressedTexture.prototype.constructor = CompressedTexture; - - CompressedTexture.prototype.isCompressedTexture = true; - - function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - - Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.needsUpdate = true; - - } - - CanvasTexture.prototype = Object.create( Texture.prototype ); - CanvasTexture.prototype.constructor = CanvasTexture; - CanvasTexture.prototype.isCanvasTexture = true; - - function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { - - format = format !== undefined ? format : DepthFormat; - - if ( format !== DepthFormat && format !== DepthStencilFormat ) { - - throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); - - } - - if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; - if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; - - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.image = { width: width, height: height }; - - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - - this.flipY = false; - this.generateMipmaps = false; - - } - - DepthTexture.prototype = Object.create( Texture.prototype ); - DepthTexture.prototype.constructor = DepthTexture; - DepthTexture.prototype.isDepthTexture = true; - - class WireframeGeometry extends BufferGeometry { - - constructor( geometry ) { - - super(); - this.type = 'WireframeGeometry'; - - // buffer - - const vertices = []; - - // helper variables - - const edge = [ 0, 0 ], edges = {}; - const keys = [ 'a', 'b', 'c' ]; - - // different logic for Geometry and BufferGeometry - - if ( geometry && geometry.isGeometry ) { - - // create a data structure that contains all edges without duplicates - - const faces = geometry.faces; - - for ( let i = 0, l = faces.length; i < l; i ++ ) { - - const face = faces[ i ]; - - for ( let j = 0; j < 3; j ++ ) { - - const edge1 = face[ keys[ j ] ]; - const edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; - edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates - edge[ 1 ] = Math.max( edge1, edge2 ); - - const key = edge[ 0 ] + ',' + edge[ 1 ]; - - if ( edges[ key ] === undefined ) { - - edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; - - } - - } - - } - - // generate vertices - - for ( const key in edges ) { - - const e = edges[ key ]; - - let vertex = geometry.vertices[ e.index1 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - vertex = geometry.vertices[ e.index2 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } else if ( geometry && geometry.isBufferGeometry ) { - - const vertex = new Vector3(); - - if ( geometry.index !== null ) { - - // indexed BufferGeometry - - const position = geometry.attributes.position; - const indices = geometry.index; - let groups = geometry.groups; - - if ( groups.length === 0 ) { - - groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; - - } - - // create a data structure that contains all eges without duplicates - - for ( let o = 0, ol = groups.length; o < ol; ++ o ) { - - const group = groups[ o ]; - - const start = group.start; - const count = group.count; - - for ( let i = start, l = ( start + count ); i < l; i += 3 ) { - - for ( let j = 0; j < 3; j ++ ) { - - const edge1 = indices.getX( i + j ); - const edge2 = indices.getX( i + ( j + 1 ) % 3 ); - edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates - edge[ 1 ] = Math.max( edge1, edge2 ); - - const key = edge[ 0 ] + ',' + edge[ 1 ]; - - if ( edges[ key ] === undefined ) { - - edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; - - } - - } - - } - - } - - // generate vertices - - for ( const key in edges ) { - - const e = edges[ key ]; - - vertex.fromBufferAttribute( position, e.index1 ); - vertices.push( vertex.x, vertex.y, vertex.z ); - - vertex.fromBufferAttribute( position, e.index2 ); - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } else { - - // non-indexed BufferGeometry - - const position = geometry.attributes.position; - - for ( let i = 0, l = ( position.count / 3 ); i < l; i ++ ) { - - for ( let j = 0; j < 3; j ++ ) { - - // three edges per triangle, an edge is represented as (index1, index2) - // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) - - const index1 = 3 * i + j; - vertex.fromBufferAttribute( position, index1 ); - vertices.push( vertex.x, vertex.y, vertex.z ); - - const index2 = 3 * i + ( ( j + 1 ) % 3 ); - vertex.fromBufferAttribute( position, index2 ); - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } - - } - - } - - // build geometry - - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - - } - - } - - /** - * Parametric Surfaces Geometry - * based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html - */ - - // ParametricGeometry - - function ParametricGeometry( func, slices, stacks ) { - - Geometry.call( this ); - - this.type = 'ParametricGeometry'; - - this.parameters = { - func: func, - slices: slices, - stacks: stacks - }; - - this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) ); - this.mergeVertices(); - - } - - ParametricGeometry.prototype = Object.create( Geometry.prototype ); - ParametricGeometry.prototype.constructor = ParametricGeometry; - - // ParametricBufferGeometry - - function ParametricBufferGeometry( func, slices, stacks ) { - - BufferGeometry.call( this ); - - this.type = 'ParametricBufferGeometry'; - - this.parameters = { - func: func, - slices: slices, - stacks: stacks - }; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - const EPS = 0.00001; - - const normal = new Vector3(); - - const p0 = new Vector3(), p1 = new Vector3(); - const pu = new Vector3(), pv = new Vector3(); - - if ( func.length < 3 ) { - - console.error( 'THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter.' ); - - } - - // generate vertices, normals and uvs - - const sliceCount = slices + 1; - - for ( let i = 0; i <= stacks; i ++ ) { - - const v = i / stacks; - - for ( let j = 0; j <= slices; j ++ ) { - - const u = j / slices; - - // vertex - - func( u, v, p0 ); - vertices.push( p0.x, p0.y, p0.z ); - - // normal - - // approximate tangent vectors via finite differences - - if ( u - EPS >= 0 ) { - - func( u - EPS, v, p1 ); - pu.subVectors( p0, p1 ); - - } else { - - func( u + EPS, v, p1 ); - pu.subVectors( p1, p0 ); - - } - - if ( v - EPS >= 0 ) { - - func( u, v - EPS, p1 ); - pv.subVectors( p0, p1 ); - - } else { - - func( u, v + EPS, p1 ); - pv.subVectors( p1, p0 ); - - } - - // cross product of tangent vectors returns surface normal - - normal.crossVectors( pu, pv ).normalize(); - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( u, v ); - - } - - } - - // generate indices - - for ( let i = 0; i < stacks; i ++ ) { - - for ( let j = 0; j < slices; j ++ ) { - - const a = i * sliceCount + j; - const b = i * sliceCount + j + 1; - const c = ( i + 1 ) * sliceCount + j + 1; - const d = ( i + 1 ) * sliceCount + j; - - // faces one and two - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry; - - // PolyhedronGeometry - - class PolyhedronGeometry extends Geometry { - - constructor( vertices, indices, radius, detail ) { - - super(); - - this.type = 'PolyhedronGeometry'; - - this.parameters = { - vertices: vertices, - indices: indices, - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) ); - this.mergeVertices(); - - } - - } - - // PolyhedronBufferGeometry - - class PolyhedronBufferGeometry extends BufferGeometry { - - constructor( vertices, indices, radius, detail ) { - - super(); - - this.type = 'PolyhedronBufferGeometry'; - - this.parameters = { - vertices: vertices, - indices: indices, - radius: radius, - detail: detail - }; - - radius = radius || 1; - detail = detail || 0; - - // default buffer data - - const vertexBuffer = []; - const uvBuffer = []; - - // the subdivision creates the vertex buffer data - - subdivide( detail ); - - // all vertices should lie on a conceptual sphere with a given radius - - applyRadius( radius ); - - // finally, create the uv data - - generateUVs(); - - // build non-indexed geometry - - this.setAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) ); - - if ( detail === 0 ) { - - this.computeVertexNormals(); // flat normals - - } else { - - this.normalizeNormals(); // smooth normals - - } - - // helper functions - - function subdivide( detail ) { - - const a = new Vector3(); - const b = new Vector3(); - const c = new Vector3(); - - // iterate over all faces and apply a subdivison with the given detail value - - for ( let i = 0; i < indices.length; i += 3 ) { - - // get the vertices of the face - - getVertexByIndex( indices[ i + 0 ], a ); - getVertexByIndex( indices[ i + 1 ], b ); - getVertexByIndex( indices[ i + 2 ], c ); - - // perform subdivision - - subdivideFace( a, b, c, detail ); - - } - - } - - function subdivideFace( a, b, c, detail ) { - - const cols = Math.pow( 2, detail ); - - // we use this multidimensional array as a data structure for creating the subdivision - - const v = []; - - // construct all of the vertices for this subdivision - - for ( let i = 0; i <= cols; i ++ ) { - - v[ i ] = []; - - const aj = a.clone().lerp( c, i / cols ); - const bj = b.clone().lerp( c, i / cols ); - - const rows = cols - i; - - for ( let j = 0; j <= rows; j ++ ) { - - if ( j === 0 && i === cols ) { - - v[ i ][ j ] = aj; - - } else { - - v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); - - } - - } - - } - - // construct all of the faces - - for ( let i = 0; i < cols; i ++ ) { - - for ( let j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { - - const k = Math.floor( j / 2 ); - - if ( j % 2 === 0 ) { - - pushVertex( v[ i ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k ] ); - pushVertex( v[ i ][ k ] ); - - } else { - - pushVertex( v[ i ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k ] ); - - } - - } - - } - - } - - function applyRadius( radius ) { - - const vertex = new Vector3(); - - // iterate over the entire buffer and apply the radius to each vertex - - for ( let i = 0; i < vertexBuffer.length; i += 3 ) { - - vertex.x = vertexBuffer[ i + 0 ]; - vertex.y = vertexBuffer[ i + 1 ]; - vertex.z = vertexBuffer[ i + 2 ]; - - vertex.normalize().multiplyScalar( radius ); - - vertexBuffer[ i + 0 ] = vertex.x; - vertexBuffer[ i + 1 ] = vertex.y; - vertexBuffer[ i + 2 ] = vertex.z; - - } - - } - - function generateUVs() { - - const vertex = new Vector3(); - - for ( let i = 0; i < vertexBuffer.length; i += 3 ) { - - vertex.x = vertexBuffer[ i + 0 ]; - vertex.y = vertexBuffer[ i + 1 ]; - vertex.z = vertexBuffer[ i + 2 ]; - - const u = azimuth( vertex ) / 2 / Math.PI + 0.5; - const v = inclination( vertex ) / Math.PI + 0.5; - uvBuffer.push( u, 1 - v ); - - } - - correctUVs(); - - correctSeam(); - - } - - function correctSeam() { - - // handle case when face straddles the seam, see #3269 - - for ( let i = 0; i < uvBuffer.length; i += 6 ) { - - // uv data of a single face - - const x0 = uvBuffer[ i + 0 ]; - const x1 = uvBuffer[ i + 2 ]; - const x2 = uvBuffer[ i + 4 ]; - - const max = Math.max( x0, x1, x2 ); - const min = Math.min( x0, x1, x2 ); - - // 0.9 is somewhat arbitrary - - if ( max > 0.9 && min < 0.1 ) { - - if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; - if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; - if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; - - } - - } - - } - - function pushVertex( vertex ) { - - vertexBuffer.push( vertex.x, vertex.y, vertex.z ); - - } - - function getVertexByIndex( index, vertex ) { - - const stride = index * 3; - - vertex.x = vertices[ stride + 0 ]; - vertex.y = vertices[ stride + 1 ]; - vertex.z = vertices[ stride + 2 ]; - - } - - function correctUVs() { - - const a = new Vector3(); - const b = new Vector3(); - const c = new Vector3(); - - const centroid = new Vector3(); - - const uvA = new Vector2(); - const uvB = new Vector2(); - const uvC = new Vector2(); - - for ( let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { - - a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); - b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); - c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); - - uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); - uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); - uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); - - centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); - - const azi = azimuth( centroid ); - - correctUV( uvA, j + 0, a, azi ); - correctUV( uvB, j + 2, b, azi ); - correctUV( uvC, j + 4, c, azi ); - - } - - } - - function correctUV( uv, stride, vector, azimuth ) { - - if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { - - uvBuffer[ stride ] = uv.x - 1; - - } - - if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { - - uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; - - } - - } - - // Angle around the Y axis, counter-clockwise when looking from above. - - function azimuth( vector ) { - - return Math.atan2( vector.z, - vector.x ); - - } - - - // Angle above the XZ plane. - - function inclination( vector ) { - - return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); - - } - - } - - } - - // TetrahedronGeometry - - class TetrahedronGeometry extends Geometry { - - constructor( radius, detail ) { - - super(); - this.type = 'TetrahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - } - - - // TetrahedronBufferGeometry - - class TetrahedronBufferGeometry extends PolyhedronBufferGeometry { - - constructor( radius, detail ) { - - const vertices = [ - 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 - ]; - - const indices = [ - 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 - ]; - - super( vertices, indices, radius, detail ); - - this.type = 'TetrahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - } - - // OctahedronGeometry - - class OctahedronGeometry extends Geometry { - - constructor( radius, detail ) { - - super(); - - this.type = 'OctahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - } - - // OctahedronBufferGeometry - - class OctahedronBufferGeometry extends PolyhedronBufferGeometry { - - constructor( radius, detail ) { - - const vertices = [ - 1, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, - 1, 0, 0, 0, 1, 0, 0, - 1 - ]; - - const indices = [ - 0, 2, 4, 0, 4, 3, 0, 3, 5, - 0, 5, 2, 1, 2, 5, 1, 5, 3, - 1, 3, 4, 1, 4, 2 - ]; - - super( vertices, indices, radius, detail ); - - this.type = 'OctahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - } - - // IcosahedronGeometry - - class IcosahedronGeometry extends Geometry { - - constructor( radius, detail ) { - - super(); - - this.type = 'IcosahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - } - - // IcosahedronBufferGeometry - - class IcosahedronBufferGeometry extends PolyhedronBufferGeometry { - - constructor( radius, detail ) { - - const t = ( 1 + Math.sqrt( 5 ) ) / 2; - - const vertices = [ - - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, - 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, - t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 - ]; - - const indices = [ - 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, - 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, - 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, - 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 - ]; - - super( vertices, indices, radius, detail ); - - this.type = 'IcosahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - } - - // DodecahedronGeometry - - class DodecahedronGeometry extends Geometry { - - constructor( radius, detail ) { - - super(); - this.type = 'DodecahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - } - - // DodecahedronBufferGeometry - - class DodecahedronBufferGeometry extends PolyhedronBufferGeometry { - - constructor( radius, detail ) { - - const t = ( 1 + Math.sqrt( 5 ) ) / 2; - const r = 1 / t; - - const vertices = [ - - // (±1, ±1, ±1) - - 1, - 1, - 1, - 1, - 1, 1, - - 1, 1, - 1, - 1, 1, 1, - 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, 1, 1, 1, - - // (0, ±1/φ, ±φ) - 0, - r, - t, 0, - r, t, - 0, r, - t, 0, r, t, - - // (±1/φ, ±φ, 0) - - r, - t, 0, - r, t, 0, - r, - t, 0, r, t, 0, - - // (±φ, 0, ±1/φ) - - t, 0, - r, t, 0, - r, - - t, 0, r, t, 0, r - ]; - - const indices = [ - 3, 11, 7, 3, 7, 15, 3, 15, 13, - 7, 19, 17, 7, 17, 6, 7, 6, 15, - 17, 4, 8, 17, 8, 10, 17, 10, 6, - 8, 0, 16, 8, 16, 2, 8, 2, 10, - 0, 12, 1, 0, 1, 18, 0, 18, 16, - 6, 10, 2, 6, 2, 13, 6, 13, 15, - 2, 16, 18, 2, 18, 3, 2, 3, 13, - 18, 1, 9, 18, 9, 11, 18, 11, 3, - 4, 14, 12, 4, 12, 0, 4, 0, 8, - 11, 9, 5, 11, 5, 19, 11, 19, 7, - 19, 5, 14, 19, 14, 4, 19, 4, 17, - 1, 12, 14, 1, 14, 5, 1, 5, 9 - ]; - - super( vertices, indices, radius, detail ); - - this.type = 'DodecahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - } - - // TubeGeometry - - class TubeGeometry extends Geometry { - - constructor( path, tubularSegments, radius, radialSegments, closed, taper ) { - - super(); - this.type = 'TubeGeometry'; - - this.parameters = { - path: path, - tubularSegments: tubularSegments, - radius: radius, - radialSegments: radialSegments, - closed: closed - }; - - if ( taper !== undefined ) console.warn( 'THREE.TubeGeometry: taper has been removed.' ); - - const bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ); - - // expose internals - - this.tangents = bufferGeometry.tangents; - this.normals = bufferGeometry.normals; - this.binormals = bufferGeometry.binormals; - - // create geometry - - this.fromBufferGeometry( bufferGeometry ); - this.mergeVertices(); - - } - - } - - - // TubeBufferGeometry - - class TubeBufferGeometry extends BufferGeometry { - - constructor( path, tubularSegments, radius, radialSegments, closed ) { - - super(); - this.type = 'TubeBufferGeometry'; - - this.parameters = { - path: path, - tubularSegments: tubularSegments, - radius: radius, - radialSegments: radialSegments, - closed: closed - }; - - tubularSegments = tubularSegments || 64; - radius = radius || 1; - radialSegments = radialSegments || 8; - closed = closed || false; - - const frames = path.computeFrenetFrames( tubularSegments, closed ); - - // expose internals - - this.tangents = frames.tangents; - this.normals = frames.normals; - this.binormals = frames.binormals; - - // helper variables - - const vertex = new Vector3(); - const normal = new Vector3(); - const uv = new Vector2(); - let P = new Vector3(); - - // buffer - - const vertices = []; - const normals = []; - const uvs = []; - const indices = []; - - // create buffer data - - generateBufferData(); - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - // functions - - function generateBufferData() { - - for ( let i = 0; i < tubularSegments; i ++ ) { - - generateSegment( i ); - - } - - // if the geometry is not closed, generate the last row of vertices and normals - // at the regular position on the given path - // - // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) - - generateSegment( ( closed === false ) ? tubularSegments : 0 ); - - // uvs are generated in a separate function. - // this makes it easy compute correct values for closed geometries - - generateUVs(); - - // finally create faces - - generateIndices(); - - } - - function generateSegment( i ) { - - // we use getPointAt to sample evenly distributed points from the given path - - P = path.getPointAt( i / tubularSegments, P ); - - // retrieve corresponding normal and binormal - - const N = frames.normals[ i ]; - const B = frames.binormals[ i ]; - - // generate normals and vertices for the current segment - - for ( let j = 0; j <= radialSegments; j ++ ) { - - const v = j / radialSegments * Math.PI * 2; - - const sin = Math.sin( v ); - const cos = - Math.cos( v ); - - // normal - - normal.x = ( cos * N.x + sin * B.x ); - normal.y = ( cos * N.y + sin * B.y ); - normal.z = ( cos * N.z + sin * B.z ); - normal.normalize(); - - normals.push( normal.x, normal.y, normal.z ); - - // vertex - - vertex.x = P.x + radius * normal.x; - vertex.y = P.y + radius * normal.y; - vertex.z = P.z + radius * normal.z; - - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } - - function generateIndices() { - - for ( let j = 1; j <= tubularSegments; j ++ ) { - - for ( let i = 1; i <= radialSegments; i ++ ) { - - const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); - const b = ( radialSegments + 1 ) * j + ( i - 1 ); - const c = ( radialSegments + 1 ) * j + i; - const d = ( radialSegments + 1 ) * ( j - 1 ) + i; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - } - - function generateUVs() { - - for ( let i = 0; i <= tubularSegments; i ++ ) { - - for ( let j = 0; j <= radialSegments; j ++ ) { - - uv.x = i / tubularSegments; - uv.y = j / radialSegments; - - uvs.push( uv.x, uv.y ); - - } - - } - - } - - } - toJSON() { - - const data = BufferGeometry.prototype.toJSON.call( this ); - - data.path = this.parameters.path.toJSON(); - - return data; - - } - - } - - // TorusKnotGeometry - - class TorusKnotGeometry extends Geometry { - - constructor( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { - - super(); - this.type = 'TorusKnotGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - tubularSegments: tubularSegments, - radialSegments: radialSegments, - p: p, - q: q - }; - - if ( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' ); - - this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); - this.mergeVertices(); - - } - - } - - - // TorusKnotBufferGeometry - - class TorusKnotBufferGeometry extends BufferGeometry { - - constructor( radius, tube, tubularSegments, radialSegments, p, q ) { - - super(); - this.type = 'TorusKnotBufferGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - tubularSegments: tubularSegments, - radialSegments: radialSegments, - p: p, - q: q - }; - - radius = radius || 1; - tube = tube || 0.4; - tubularSegments = Math.floor( tubularSegments ) || 64; - radialSegments = Math.floor( radialSegments ) || 8; - p = p || 2; - q = q || 3; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - const vertex = new Vector3(); - const normal = new Vector3(); - - const P1 = new Vector3(); - const P2 = new Vector3(); - - const B = new Vector3(); - const T = new Vector3(); - const N = new Vector3(); - - // generate vertices, normals and uvs - - for ( let i = 0; i <= tubularSegments; ++ i ) { - - // the radian "u" is used to calculate the position on the torus curve of the current tubular segement - - const u = i / tubularSegments * p * Math.PI * 2; - - // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. - // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions - - calculatePositionOnCurve( u, p, q, radius, P1 ); - calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); - - // calculate orthonormal basis - - T.subVectors( P2, P1 ); - N.addVectors( P2, P1 ); - B.crossVectors( T, N ); - N.crossVectors( B, T ); - - // normalize B, N. T can be ignored, we don't use it - - B.normalize(); - N.normalize(); - - for ( let j = 0; j <= radialSegments; ++ j ) { - - // now calculate the vertices. they are nothing more than an extrusion of the torus curve. - // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. - - const v = j / radialSegments * Math.PI * 2; - const cx = - tube * Math.cos( v ); - const cy = tube * Math.sin( v ); - - // now calculate the final vertex position. - // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve - - vertex.x = P1.x + ( cx * N.x + cy * B.x ); - vertex.y = P1.y + ( cx * N.y + cy * B.y ); - vertex.z = P1.z + ( cx * N.z + cy * B.z ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) - - normal.subVectors( vertex, P1 ).normalize(); - - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( i / tubularSegments ); - uvs.push( j / radialSegments ); - - } - - } - - // generate indices - - for ( let j = 1; j <= tubularSegments; j ++ ) { - - for ( let i = 1; i <= radialSegments; i ++ ) { - - // indices - - const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); - const b = ( radialSegments + 1 ) * j + ( i - 1 ); - const c = ( radialSegments + 1 ) * j + i; - const d = ( radialSegments + 1 ) * ( j - 1 ) + i; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - // this function calculates the current position on the torus curve - - function calculatePositionOnCurve( u, p, q, radius, position ) { - - const cu = Math.cos( u ); - const su = Math.sin( u ); - const quOverP = q / p * u; - const cs = Math.cos( quOverP ); - - position.x = radius * ( 2 + cs ) * 0.5 * cu; - position.y = radius * ( 2 + cs ) * su * 0.5; - position.z = radius * Math.sin( quOverP ) * 0.5; - - } - - } - - } - - // TorusGeometry - - class TorusGeometry extends Geometry { - - constructor( radius, tube, radialSegments, tubularSegments, arc ) { - - super(); - this.type = 'TorusGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; - - this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); - this.mergeVertices(); - - } - - } - - - // TorusBufferGeometry - - class TorusBufferGeometry extends BufferGeometry { - - constructor( radius, tube, radialSegments, tubularSegments, arc ) { - - super(); - this.type = 'TorusBufferGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; - - radius = radius || 1; - tube = tube || 0.4; - radialSegments = Math.floor( radialSegments ) || 8; - tubularSegments = Math.floor( tubularSegments ) || 6; - arc = arc || Math.PI * 2; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - const center = new Vector3(); - const vertex = new Vector3(); - const normal = new Vector3(); - - // generate vertices, normals and uvs - - for ( let j = 0; j <= radialSegments; j ++ ) { - - for ( let i = 0; i <= tubularSegments; i ++ ) { - - const u = i / tubularSegments * arc; - const v = j / radialSegments * Math.PI * 2; - - // vertex - - vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); - vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); - vertex.z = tube * Math.sin( v ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - center.x = radius * Math.cos( u ); - center.y = radius * Math.sin( u ); - normal.subVectors( vertex, center ).normalize(); - - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( i / tubularSegments ); - uvs.push( j / radialSegments ); - - } - - } - - // generate indices - - for ( let j = 1; j <= radialSegments; j ++ ) { - - for ( let i = 1; i <= tubularSegments; i ++ ) { - - // indices - - const a = ( tubularSegments + 1 ) * j + i - 1; - const b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; - const c = ( tubularSegments + 1 ) * ( j - 1 ) + i; - const d = ( tubularSegments + 1 ) * j + i; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - } - - /** - * Port from https://github.com/mapbox/earcut (v2.2.2) - */ - - const Earcut = { - - triangulate: function ( data, holeIndices, dim ) { - - dim = dim || 2; - - const hasHoles = holeIndices && holeIndices.length; - const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length; - let outerNode = linkedList( data, 0, outerLen, dim, true ); - const triangles = []; - - if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; - - let minX, minY, maxX, maxY, x, y, invSize; - - if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); - - // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox - if ( data.length > 80 * dim ) { - - minX = maxX = data[ 0 ]; - minY = maxY = data[ 1 ]; - - for ( let i = dim; i < outerLen; i += dim ) { - - x = data[ i ]; - y = data[ i + 1 ]; - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - - } - - // minX, minY and invSize are later used to transform coords into integers for z-order calculation - invSize = Math.max( maxX - minX, maxY - minY ); - invSize = invSize !== 0 ? 1 / invSize : 0; - - } - - earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); - - return triangles; - - } - - }; - - // create a circular doubly linked list from polygon points in the specified winding order - function linkedList( data, start, end, dim, clockwise ) { - - let i, last; - - if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { - - for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); - - } else { - - for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); - - } - - if ( last && equals( last, last.next ) ) { - - removeNode( last ); - last = last.next; - - } - - return last; - - } - - // eliminate colinear or duplicate points - function filterPoints( start, end ) { - - if ( ! start ) return start; - if ( ! end ) end = start; - - let p = start, - again; - do { - - again = false; - - if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { - - removeNode( p ); - p = end = p.prev; - if ( p === p.next ) break; - again = true; - - } else { - - p = p.next; - - } - - } while ( again || p !== end ); - - return end; - - } - - // main ear slicing loop which triangulates a polygon (given as a linked list) - function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { - - if ( ! ear ) return; - - // interlink polygon nodes in z-order - if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); - - let stop = ear, - prev, next; - - // iterate through ears, slicing them one by one - while ( ear.prev !== ear.next ) { - - prev = ear.prev; - next = ear.next; - - if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { - - // cut off the triangle - triangles.push( prev.i / dim ); - triangles.push( ear.i / dim ); - triangles.push( next.i / dim ); - - removeNode( ear ); - - // skipping the next vertex leads to less sliver triangles - ear = next.next; - stop = next.next; - - continue; - - } - - ear = next; - - // if we looped through the whole remaining polygon and can't find any more ears - if ( ear === stop ) { - - // try filtering points and slicing again - if ( ! pass ) { - - earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); - - // if this didn't work, try curing all small self-intersections locally - - } else if ( pass === 1 ) { - - ear = cureLocalIntersections( filterPoints( ear ), triangles, dim ); - earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); - - // as a last resort, try splitting the remaining polygon into two - - } else if ( pass === 2 ) { - - splitEarcut( ear, triangles, dim, minX, minY, invSize ); - - } - - break; - - } - - } - - } - - // check whether a polygon node forms a valid ear with adjacent nodes - function isEar( ear ) { - - const a = ear.prev, - b = ear, - c = ear.next; - - if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear - - // now make sure we don't have other points inside the potential ear - let p = ear.next.next; - - while ( p !== ear.prev ) { - - if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.next; - - } - - return true; - - } - - function isEarHashed( ear, minX, minY, invSize ) { - - const a = ear.prev, - b = ear, - c = ear.next; - - if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear - - // triangle bbox; min & max are calculated like this for speed - const minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), - minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), - maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), - maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); - - // z-order range for the current triangle bbox; - const minZ = zOrder( minTX, minTY, minX, minY, invSize ), - maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); - - let p = ear.prevZ, - n = ear.nextZ; - - // look for points inside the triangle in both directions - while ( p && p.z >= minZ && n && n.z <= maxZ ) { - - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; - - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; - - } - - // look for remaining points in decreasing z-order - while ( p && p.z >= minZ ) { - - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; - - } - - // look for remaining points in increasing z-order - while ( n && n.z <= maxZ ) { - - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; - - } - - return true; - - } - - // go through all polygon nodes and cure small local self-intersections - function cureLocalIntersections( start, triangles, dim ) { - - let p = start; - do { - - const a = p.prev, - b = p.next.next; - - if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { - - triangles.push( a.i / dim ); - triangles.push( p.i / dim ); - triangles.push( b.i / dim ); - - // remove two nodes involved - removeNode( p ); - removeNode( p.next ); - - p = start = b; - - } - - p = p.next; - - } while ( p !== start ); - - return filterPoints( p ); - - } - - // try splitting polygon into two and triangulate them independently - function splitEarcut( start, triangles, dim, minX, minY, invSize ) { - - // look for a valid diagonal that divides the polygon into two - let a = start; - do { - - let b = a.next.next; - while ( b !== a.prev ) { - - if ( a.i !== b.i && isValidDiagonal( a, b ) ) { - - // split the polygon in two by the diagonal - let c = splitPolygon( a, b ); - - // filter colinear points around the cuts - a = filterPoints( a, a.next ); - c = filterPoints( c, c.next ); - - // run earcut on each half - earcutLinked( a, triangles, dim, minX, minY, invSize ); - earcutLinked( c, triangles, dim, minX, minY, invSize ); - return; - - } - - b = b.next; - - } - - a = a.next; - - } while ( a !== start ); - - } - - // link every hole into the outer loop, producing a single-ring polygon without holes - function eliminateHoles( data, holeIndices, outerNode, dim ) { - - const queue = []; - let i, len, start, end, list; - - for ( i = 0, len = holeIndices.length; i < len; i ++ ) { - - start = holeIndices[ i ] * dim; - end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; - list = linkedList( data, start, end, dim, false ); - if ( list === list.next ) list.steiner = true; - queue.push( getLeftmost( list ) ); - - } - - queue.sort( compareX ); - - // process holes from left to right - for ( i = 0; i < queue.length; i ++ ) { - - eliminateHole( queue[ i ], outerNode ); - outerNode = filterPoints( outerNode, outerNode.next ); - - } - - return outerNode; - - } - - function compareX( a, b ) { - - return a.x - b.x; - - } - - // find a bridge between vertices that connects hole with an outer ring and and link it - function eliminateHole( hole, outerNode ) { - - outerNode = findHoleBridge( hole, outerNode ); - if ( outerNode ) { - - const b = splitPolygon( outerNode, hole ); - - // filter collinear points around the cuts - filterPoints( outerNode, outerNode.next ); - filterPoints( b, b.next ); - - } - - } - - // David Eberly's algorithm for finding a bridge between hole and outer polygon - function findHoleBridge( hole, outerNode ) { - - let p = outerNode; - const hx = hole.x; - const hy = hole.y; - let qx = - Infinity, m; - - // find a segment intersected by a ray from the hole's leftmost point to the left; - // segment's endpoint with lesser x will be potential connection point - do { - - if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { - - const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); - if ( x <= hx && x > qx ) { - - qx = x; - if ( x === hx ) { - - if ( hy === p.y ) return p; - if ( hy === p.next.y ) return p.next; - - } - - m = p.x < p.next.x ? p : p.next; - - } - - } - - p = p.next; - - } while ( p !== outerNode ); - - if ( ! m ) return null; - - if ( hx === qx ) return m; // hole touches outer segment; pick leftmost endpoint - - // look for points inside the triangle of hole point, segment intersection and endpoint; - // if there are no points found, we have a valid connection; - // otherwise choose the point of the minimum angle with the ray as connection point - - const stop = m, - mx = m.x, - my = m.y; - let tanMin = Infinity, tan; - - p = m; - - do { - - if ( hx >= p.x && p.x >= mx && hx !== p.x && - pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { - - tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential - - if ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) { - - m = p; - tanMin = tan; - - } - - } - - p = p.next; - - } while ( p !== stop ); - - return m; - - } - - // whether sector in vertex m contains sector in vertex p in the same coordinates - function sectorContainsSector( m, p ) { - - return area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0; - - } - - // interlink polygon nodes in z-order - function indexCurve( start, minX, minY, invSize ) { - - let p = start; - do { - - if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); - p.prevZ = p.prev; - p.nextZ = p.next; - p = p.next; - - } while ( p !== start ); - - p.prevZ.nextZ = null; - p.prevZ = null; - - sortLinked( p ); - - } - - // Simon Tatham's linked list merge sort algorithm - // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html - function sortLinked( list ) { - - let i, p, q, e, tail, numMerges, pSize, qSize, - inSize = 1; - - do { - - p = list; - list = null; - tail = null; - numMerges = 0; - - while ( p ) { - - numMerges ++; - q = p; - pSize = 0; - for ( i = 0; i < inSize; i ++ ) { - - pSize ++; - q = q.nextZ; - if ( ! q ) break; - - } - - qSize = inSize; - - while ( pSize > 0 || ( qSize > 0 && q ) ) { - - if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { - - e = p; - p = p.nextZ; - pSize --; - - } else { - - e = q; - q = q.nextZ; - qSize --; - - } - - if ( tail ) tail.nextZ = e; - else list = e; - - e.prevZ = tail; - tail = e; - - } - - p = q; - - } - - tail.nextZ = null; - inSize *= 2; - - } while ( numMerges > 1 ); - - return list; - - } - - // z-order of a point given coords and inverse of the longer side of data bbox - function zOrder( x, y, minX, minY, invSize ) { - - // coords are transformed into non-negative 15-bit integer range - x = 32767 * ( x - minX ) * invSize; - y = 32767 * ( y - minY ) * invSize; - - x = ( x | ( x << 8 ) ) & 0x00FF00FF; - x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; - x = ( x | ( x << 2 ) ) & 0x33333333; - x = ( x | ( x << 1 ) ) & 0x55555555; - - y = ( y | ( y << 8 ) ) & 0x00FF00FF; - y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; - y = ( y | ( y << 2 ) ) & 0x33333333; - y = ( y | ( y << 1 ) ) & 0x55555555; - - return x | ( y << 1 ); - - } - - // find the leftmost node of a polygon ring - function getLeftmost( start ) { - - let p = start, - leftmost = start; - do { - - if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; - p = p.next; - - } while ( p !== start ); - - return leftmost; - - } - - // check if a point lies within a convex triangle - function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { - - return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && - ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && - ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; - - } - - // check if a diagonal between two polygon nodes is valid (lies in polygon interior) - function isValidDiagonal( a, b ) { - - return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // dones't intersect other edges - ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible - ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors - equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case - - } - - // signed area of a triangle - function area( p, q, r ) { - - return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); - - } - - // check if two points are equal - function equals( p1, p2 ) { - - return p1.x === p2.x && p1.y === p2.y; - - } - - // check if two segments intersect - function intersects( p1, q1, p2, q2 ) { - - const o1 = sign( area( p1, q1, p2 ) ); - const o2 = sign( area( p1, q1, q2 ) ); - const o3 = sign( area( p2, q2, p1 ) ); - const o4 = sign( area( p2, q2, q1 ) ); - - if ( o1 !== o2 && o3 !== o4 ) return true; // general case - - if ( o1 === 0 && onSegment( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 - if ( o2 === 0 && onSegment( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 - if ( o3 === 0 && onSegment( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 - if ( o4 === 0 && onSegment( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 - - return false; - - } - - // for collinear points p, q, r, check if point q lies on segment pr - function onSegment( p, q, r ) { - - return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); - - } - - function sign( num ) { - - return num > 0 ? 1 : num < 0 ? - 1 : 0; - - } - - // check if a polygon diagonal intersects any polygon segments - function intersectsPolygon( a, b ) { - - let p = a; - do { - - if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && - intersects( p, p.next, a, b ) ) return true; - p = p.next; - - } while ( p !== a ); - - return false; - - } - - // check if a polygon diagonal is locally inside the polygon - function locallyInside( a, b ) { - - return area( a.prev, a, a.next ) < 0 ? - area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : - area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; - - } - - // check if the middle point of a polygon diagonal is inside the polygon - function middleInside( a, b ) { - - let p = a, - inside = false; - const px = ( a.x + b.x ) / 2, - py = ( a.y + b.y ) / 2; - do { - - if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && - ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) - inside = ! inside; - p = p.next; - - } while ( p !== a ); - - return inside; - - } - - // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; - // if one belongs to the outer ring and another to a hole, it merges it into a single ring - function splitPolygon( a, b ) { - - const a2 = new Node( a.i, a.x, a.y ), - b2 = new Node( b.i, b.x, b.y ), - an = a.next, - bp = b.prev; - - a.next = b; - b.prev = a; - - a2.next = an; - an.prev = a2; - - b2.next = a2; - a2.prev = b2; - - bp.next = b2; - b2.prev = bp; - - return b2; - - } - - // create a node and optionally link it with previous one (in a circular doubly linked list) - function insertNode( i, x, y, last ) { - - const p = new Node( i, x, y ); - - if ( ! last ) { - - p.prev = p; - p.next = p; - - } else { - - p.next = last.next; - p.prev = last; - last.next.prev = p; - last.next = p; - - } - - return p; - - } - - function removeNode( p ) { - - p.next.prev = p.prev; - p.prev.next = p.next; - - if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; - if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; - - } - - function Node( i, x, y ) { - - // vertex index in coordinates array - this.i = i; - - // vertex coordinates - this.x = x; - this.y = y; - - // previous and next vertex nodes in a polygon ring - this.prev = null; - this.next = null; - - // z-order curve value - this.z = null; - - // previous and next nodes in z-order - this.prevZ = null; - this.nextZ = null; - - // indicates whether this is a steiner point - this.steiner = false; - - } - - function signedArea( data, start, end, dim ) { - - let sum = 0; - for ( let i = start, j = end - dim; i < end; i += dim ) { - - sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); - j = i; - - } - - return sum; - - } - - const ShapeUtils = { - - // calculate area of the contour polygon - - area: function ( contour ) { - - const n = contour.length; - let a = 0.0; - - for ( let p = n - 1, q = 0; q < n; p = q ++ ) { - - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; - - } - - return a * 0.5; - - }, - - isClockWise: function ( pts ) { - - return ShapeUtils.area( pts ) < 0; - - }, - - triangulateShape: function ( contour, holes ) { - - const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] - const holeIndices = []; // array of hole indices - const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] - - removeDupEndPts( contour ); - addContour( vertices, contour ); - - // - - let holeIndex = contour.length; - - holes.forEach( removeDupEndPts ); - - for ( let i = 0; i < holes.length; i ++ ) { - - holeIndices.push( holeIndex ); - holeIndex += holes[ i ].length; - addContour( vertices, holes[ i ] ); - - } - - // - - const triangles = Earcut.triangulate( vertices, holeIndices ); - - // - - for ( let i = 0; i < triangles.length; i += 3 ) { - - faces.push( triangles.slice( i, i + 3 ) ); - - } - - return faces; - - } - - }; - - function removeDupEndPts( points ) { - - const l = points.length; - - if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { - - points.pop(); - - } - - } - - function addContour( vertices, contour ) { - - for ( let i = 0; i < contour.length; i ++ ) { - - vertices.push( contour[ i ].x ); - vertices.push( contour[ i ].y ); - - } - - } - - /** - * Creates extruded geometry from a path shape. - * - * parameters = { - * - * curveSegments: , // number of points on the curves - * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too - * depth: , // Depth to extrude the shape - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into the original shape bevel goes - * bevelSize: , // how far from shape outline (including bevelOffset) is bevel - * bevelOffset: , // how far from shape outline does bevel start - * bevelSegments: , // number of bevel layers - * - * extrudePath: // curve to extrude shape along - * - * UVGenerator: // object that provides UV generator functions - * - * } - */ - - // ExtrudeGeometry - - class ExtrudeGeometry extends Geometry { - - constructor( shapes, options ) { - - super(); - - this.type = 'ExtrudeGeometry'; - - this.parameters = { - shapes: shapes, - options: options - }; - - this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) ); - this.mergeVertices(); - - } - - toJSON() { - - const data = super.toJSON(); - - const shapes = this.parameters.shapes; - const options = this.parameters.options; - - return toJSON( shapes, options, data ); - - } - - } - - - // ExtrudeBufferGeometry - - class ExtrudeBufferGeometry extends BufferGeometry { - - constructor( shapes, options ) { - - super(); - - this.type = 'ExtrudeBufferGeometry'; - - this.parameters = { - shapes: shapes, - options: options - }; - - shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; - - const scope = this; - - const verticesArray = []; - const uvArray = []; - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - addShape( shape ); - - } - - // build geometry - - this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); - - this.computeVertexNormals(); - - // functions - - function addShape( shape ) { - - const placeholder = []; - - // options - - const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - const steps = options.steps !== undefined ? options.steps : 1; - let depth = options.depth !== undefined ? options.depth : 100; - - let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; - let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; - let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; - let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; - let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; - - const extrudePath = options.extrudePath; - - const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; - - // deprecated options - - if ( options.amount !== undefined ) { - - console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' ); - depth = options.amount; - - } - - // - - let extrudePts, extrudeByPath = false; - let splineTube, binormal, normal, position2; - - if ( extrudePath ) { - - extrudePts = extrudePath.getSpacedPoints( steps ); - - extrudeByPath = true; - bevelEnabled = false; // bevels not supported for path extrusion - - // SETUP TNB variables - - // TODO1 - have a .isClosed in spline? - - splineTube = extrudePath.computeFrenetFrames( steps, false ); - - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); - - binormal = new Vector3(); - normal = new Vector3(); - position2 = new Vector3(); - - } - - // Safeguards if bevels are not enabled - - if ( ! bevelEnabled ) { - - bevelSegments = 0; - bevelThickness = 0; - bevelSize = 0; - bevelOffset = 0; - - } - - // Variables initialization - - const shapePoints = shape.extractPoints( curveSegments ); - - let vertices = shapePoints.shape; - const holes = shapePoints.holes; - - const reverse = ! ShapeUtils.isClockWise( vertices ); - - if ( reverse ) { - - vertices = vertices.reverse(); - - // Maybe we should also check if holes are in the opposite direction, just to be safe ... - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - - if ( ShapeUtils.isClockWise( ahole ) ) { - - holes[ h ] = ahole.reverse(); - - } - - } - - } - - - const faces = ShapeUtils.triangulateShape( vertices, holes ); - - /* Vertices */ - - const contour = vertices; // vertices has all points but contour has only points of circumference - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - - vertices = vertices.concat( ahole ); - - } - - - function scalePt2( pt, vec, size ) { - - if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); - - return vec.clone().multiplyScalar( size ).add( pt ); - - } - - const vlen = vertices.length, flen = faces.length; - - - // Find directions for point movement - - - function getBevelVec( inPt, inPrev, inNext ) { - - // computes for inPt the corresponding point inPt' on a new contour - // shifted by 1 unit (length of normalized vector) to the left - // if we walk along contour clockwise, this new contour is outside the old one - // - // inPt' is the intersection of the two lines parallel to the two - // adjacent edges of inPt at a distance of 1 unit on the left side. - - let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt - - // good reading for geometry algorithms (here: line-line intersection) - // http://geomalgorithms.com/a05-_intersect-1.html - - const v_prev_x = inPt.x - inPrev.x, - v_prev_y = inPt.y - inPrev.y; - const v_next_x = inNext.x - inPt.x, - v_next_y = inNext.y - inPt.y; - - const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); - - // check for collinear edges - const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - if ( Math.abs( collinear0 ) > Number.EPSILON ) { - - // not collinear - - // length of vectors for normalizing - - const v_prev_len = Math.sqrt( v_prev_lensq ); - const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); - - // shift adjacent points by unit vectors to the left - - const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); - const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); - - const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); - const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); - - // scaling factor for v_prev to intersection point - - const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / - ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - // vector from inPt to intersection point - - v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); - v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); - - // Don't normalize!, otherwise sharp corners become ugly - // but prevent crazy spikes - const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); - if ( v_trans_lensq <= 2 ) { - - return new Vector2( v_trans_x, v_trans_y ); - - } else { - - shrink_by = Math.sqrt( v_trans_lensq / 2 ); - - } - - } else { - - // handle special case of collinear edges - - let direction_eq = false; // assumes: opposite - - if ( v_prev_x > Number.EPSILON ) { - - if ( v_next_x > Number.EPSILON ) { - - direction_eq = true; - - } - - } else { - - if ( v_prev_x < - Number.EPSILON ) { - - if ( v_next_x < - Number.EPSILON ) { - - direction_eq = true; - - } - - } else { - - if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { - - direction_eq = true; - - } - - } - - } - - if ( direction_eq ) { - - // console.log("Warning: lines are a straight sequence"); - v_trans_x = - v_prev_y; - v_trans_y = v_prev_x; - shrink_by = Math.sqrt( v_prev_lensq ); - - } else { - - // console.log("Warning: lines are a straight spike"); - v_trans_x = v_prev_x; - v_trans_y = v_prev_y; - shrink_by = Math.sqrt( v_prev_lensq / 2 ); - - } - - } - - return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); - - } - - - const contourMovements = []; - - for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - - if ( j === il ) j = 0; - if ( k === il ) k = 0; - - // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) - - contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); - - } - - const holesMovements = []; - let oneHoleMovements, verticesMovements = contourMovements.concat(); - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - - oneHoleMovements = []; - - for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - - if ( j === il ) j = 0; - if ( k === il ) k = 0; - - // (j)---(i)---(k) - oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); - - } - - holesMovements.push( oneHoleMovements ); - verticesMovements = verticesMovements.concat( oneHoleMovements ); - - } - - - // Loop bevelSegments, 1 for the front, 1 for the back - - for ( let b = 0; b < bevelSegments; b ++ ) { - - //for ( b = bevelSegments; b > 0; b -- ) { - - const t = b / bevelSegments; - const z = bevelThickness * Math.cos( t * Math.PI / 2 ); - const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - - // contract shape - - for ( let i = 0, il = contour.length; i < il; i ++ ) { - - const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - - v( vert.x, vert.y, - z ); - - } - - // expand holes - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; - - for ( let i = 0, il = ahole.length; i < il; i ++ ) { - - const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - - v( vert.x, vert.y, - z ); - - } - - } - - } - - const bs = bevelSize + bevelOffset; - - // Back facing vertices - - for ( let i = 0; i < vlen; i ++ ) { - - const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, 0 ); - - } else { - - // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); - - normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); - - position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); - - v( position2.x, position2.y, position2.z ); - - } - - } - - // Add stepped vertices... - // Including front facing vertices - - for ( let s = 1; s <= steps; s ++ ) { - - for ( let i = 0; i < vlen; i ++ ) { - - const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, depth / steps * s ); - - } else { - - // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); - - normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); - - position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); - - v( position2.x, position2.y, position2.z ); - - } - - } - - } - - - // Add bevel segments planes - - //for ( b = 1; b <= bevelSegments; b ++ ) { - for ( let b = bevelSegments - 1; b >= 0; b -- ) { - - const t = b / bevelSegments; - const z = bevelThickness * Math.cos( t * Math.PI / 2 ); - const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - - // contract shape - - for ( let i = 0, il = contour.length; i < il; i ++ ) { - - const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, depth + z ); - - } - - // expand holes - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; - - for ( let i = 0, il = ahole.length; i < il; i ++ ) { - - const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, depth + z ); - - } else { - - v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); - - } - - } - - } - - } - - /* Faces */ - - // Top and bottom faces - - buildLidFaces(); - - // Sides faces - - buildSideFaces(); - - - ///// Internal functions - - function buildLidFaces() { - - const start = verticesArray.length / 3; - - if ( bevelEnabled ) { - - let layer = 0; // steps + 1 - let offset = vlen * layer; - - // Bottom faces - - for ( let i = 0; i < flen; i ++ ) { - - const face = faces[ i ]; - f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); - - } - - layer = steps + bevelSegments * 2; - offset = vlen * layer; - - // Top faces - - for ( let i = 0; i < flen; i ++ ) { - - const face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); - - } - - } else { - - // Bottom faces - - for ( let i = 0; i < flen; i ++ ) { - - const face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ] ); - - } - - // Top faces - - for ( let i = 0; i < flen; i ++ ) { - - const face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); - - } - - } - - scope.addGroup( start, verticesArray.length / 3 - start, 0 ); - - } - - // Create faces for the z-sides of the shape - - function buildSideFaces() { - - const start = verticesArray.length / 3; - let layeroffset = 0; - sidewalls( contour, layeroffset ); - layeroffset += contour.length; - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - sidewalls( ahole, layeroffset ); - - //, true - layeroffset += ahole.length; - - } - - - scope.addGroup( start, verticesArray.length / 3 - start, 1 ); - - - } - - function sidewalls( contour, layeroffset ) { - - let i = contour.length; - - while ( -- i >= 0 ) { - - const j = i; - let k = i - 1; - if ( k < 0 ) k = contour.length - 1; - - //console.log('b', i,j, i-1, k,vertices.length); - - for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { - - const slen1 = vlen * s; - const slen2 = vlen * ( s + 1 ); - - const a = layeroffset + j + slen1, - b = layeroffset + k + slen1, - c = layeroffset + k + slen2, - d = layeroffset + j + slen2; - - f4( a, b, c, d ); - - } - - } - - } - - function v( x, y, z ) { - - placeholder.push( x ); - placeholder.push( y ); - placeholder.push( z ); - - } - - - function f3( a, b, c ) { - - addVertex( a ); - addVertex( b ); - addVertex( c ); - - const nextIndex = verticesArray.length / 3; - const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); - - } - - function f4( a, b, c, d ) { - - addVertex( a ); - addVertex( b ); - addVertex( d ); - - addVertex( b ); - addVertex( c ); - addVertex( d ); - - - const nextIndex = verticesArray.length / 3; - const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 3 ] ); - - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); - addUV( uvs[ 3 ] ); - - } - - function addVertex( index ) { - - verticesArray.push( placeholder[ index * 3 + 0 ] ); - verticesArray.push( placeholder[ index * 3 + 1 ] ); - verticesArray.push( placeholder[ index * 3 + 2 ] ); - - } - - - function addUV( vector2 ) { - - uvArray.push( vector2.x ); - uvArray.push( vector2.y ); - - } - - } - - } - - toJSON() { - - const data = BufferGeometry.prototype.toJSON.call( this ); - - const shapes = this.parameters.shapes; - const options = this.parameters.options; - - return toJSON( shapes, options, data ); - - } - - } - - const WorldUVGenerator = { - - generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { - - const a_x = vertices[ indexA * 3 ]; - const a_y = vertices[ indexA * 3 + 1 ]; - const b_x = vertices[ indexB * 3 ]; - const b_y = vertices[ indexB * 3 + 1 ]; - const c_x = vertices[ indexC * 3 ]; - const c_y = vertices[ indexC * 3 + 1 ]; - - return [ - new Vector2( a_x, a_y ), - new Vector2( b_x, b_y ), - new Vector2( c_x, c_y ) - ]; - - }, - - generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { - - const a_x = vertices[ indexA * 3 ]; - const a_y = vertices[ indexA * 3 + 1 ]; - const a_z = vertices[ indexA * 3 + 2 ]; - const b_x = vertices[ indexB * 3 ]; - const b_y = vertices[ indexB * 3 + 1 ]; - const b_z = vertices[ indexB * 3 + 2 ]; - const c_x = vertices[ indexC * 3 ]; - const c_y = vertices[ indexC * 3 + 1 ]; - const c_z = vertices[ indexC * 3 + 2 ]; - const d_x = vertices[ indexD * 3 ]; - const d_y = vertices[ indexD * 3 + 1 ]; - const d_z = vertices[ indexD * 3 + 2 ]; - - if ( Math.abs( a_y - b_y ) < 0.01 ) { - - return [ - new Vector2( a_x, 1 - a_z ), - new Vector2( b_x, 1 - b_z ), - new Vector2( c_x, 1 - c_z ), - new Vector2( d_x, 1 - d_z ) - ]; - - } else { - - return [ - new Vector2( a_y, 1 - a_z ), - new Vector2( b_y, 1 - b_z ), - new Vector2( c_y, 1 - c_z ), - new Vector2( d_y, 1 - d_z ) - ]; - - } - - } - }; - - function toJSON( shapes, options, data ) { - - data.shapes = []; - - if ( Array.isArray( shapes ) ) { - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - - data.shapes.push( shape.uuid ); - - } - - } else { - - data.shapes.push( shapes.uuid ); - - } - - if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); - - return data; - - } - - /** - * Text = 3D Text - * - * parameters = { - * font: , // font - * - * size: , // size of the text - * height: , // thickness to extrude text - * curveSegments: , // number of points on the curves - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into text bevel goes - * bevelSize: , // how far from text outline (including bevelOffset) is bevel - * bevelOffset: // how far from text outline does bevel start - * } - */ - - // TextGeometry - - class TextGeometry extends Geometry { - - constructor( text, parameters ) { - - super(); - this.type = 'TextGeometry'; - - this.parameters = { - text: text, - parameters: parameters - }; - - this.fromBufferGeometry( new TextBufferGeometry( text, parameters ) ); - this.mergeVertices(); - - } - - } - - - // TextBufferGeometry - - class TextBufferGeometry extends ExtrudeBufferGeometry { - - constructor( text, parameters ) { - - parameters = parameters || {}; - - const font = parameters.font; - - if ( ! ( font && font.isFont ) ) { - - console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' ); - return new Geometry(); - - } - - const shapes = font.generateShapes( text, parameters.size ); - - // translate parameters to ExtrudeGeometry API - - parameters.depth = parameters.height !== undefined ? parameters.height : 50; - - // defaults - - if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; - if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; - if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; - - super( shapes, parameters ); - - this.type = 'TextBufferGeometry'; - - } - - } - - // SphereGeometry - - class SphereGeometry extends Geometry { - - constructor( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - - super(); - this.type = 'SphereGeometry'; - - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - } - - // SphereBufferGeometry - - class SphereBufferGeometry extends BufferGeometry { - - constructor( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - - super(); - this.type = 'SphereBufferGeometry'; - - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - radius = radius || 1; - - widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); - heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); - - phiStart = phiStart !== undefined ? phiStart : 0; - phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; - - const thetaEnd = Math.min( thetaStart + thetaLength, Math.PI ); - - let index = 0; - const grid = []; - - const vertex = new Vector3(); - const normal = new Vector3(); - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // generate vertices, normals and uvs - - for ( let iy = 0; iy <= heightSegments; iy ++ ) { - - const verticesRow = []; - - const v = iy / heightSegments; - - // special case for the poles - - let uOffset = 0; - - if ( iy == 0 && thetaStart == 0 ) { - - uOffset = 0.5 / widthSegments; - - } else if ( iy == heightSegments && thetaEnd == Math.PI ) { - - uOffset = - 0.5 / widthSegments; - - } - - for ( let ix = 0; ix <= widthSegments; ix ++ ) { - - const u = ix / widthSegments; - - // vertex - - vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); - vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normal.copy( vertex ).normalize(); - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( u + uOffset, 1 - v ); - - verticesRow.push( index ++ ); - - } - - grid.push( verticesRow ); - - } - - // indices - - for ( let iy = 0; iy < heightSegments; iy ++ ) { - - for ( let ix = 0; ix < widthSegments; ix ++ ) { - - const a = grid[ iy ][ ix + 1 ]; - const b = grid[ iy ][ ix ]; - const c = grid[ iy + 1 ][ ix ]; - const d = grid[ iy + 1 ][ ix + 1 ]; - - if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); - if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - } - - // RingGeometry - - class RingGeometry extends Geometry { - - constructor( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { - - super(); - - this.type = 'RingGeometry'; - - this.parameters = { - innerRadius: innerRadius, - outerRadius: outerRadius, - thetaSegments: thetaSegments, - phiSegments: phiSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - } - - // RingBufferGeometry - - class RingBufferGeometry extends BufferGeometry { - - constructor( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { - - super(); - - this.type = 'RingBufferGeometry'; - - this.parameters = { - innerRadius: innerRadius, - outerRadius: outerRadius, - thetaSegments: thetaSegments, - phiSegments: phiSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - innerRadius = innerRadius || 0.5; - outerRadius = outerRadius || 1; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - - thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; - phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // some helper variables - - let radius = innerRadius; - const radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); - const vertex = new Vector3(); - const uv = new Vector2(); - - // generate vertices, normals and uvs - - for ( let j = 0; j <= phiSegments; j ++ ) { - - for ( let i = 0; i <= thetaSegments; i ++ ) { - - // values are generate from the inside of the ring to the outside - - const segment = thetaStart + i / thetaSegments * thetaLength; - - // vertex - - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normals.push( 0, 0, 1 ); - - // uv - - uv.x = ( vertex.x / outerRadius + 1 ) / 2; - uv.y = ( vertex.y / outerRadius + 1 ) / 2; - - uvs.push( uv.x, uv.y ); - - } - - // increase the radius for next row of vertices - - radius += radiusStep; - - } - - // indices - - for ( let j = 0; j < phiSegments; j ++ ) { - - const thetaSegmentLevel = j * ( thetaSegments + 1 ); - - for ( let i = 0; i < thetaSegments; i ++ ) { - - const segment = i + thetaSegmentLevel; - - const a = segment; - const b = segment + thetaSegments + 1; - const c = segment + thetaSegments + 2; - const d = segment + 1; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - } - - // LatheGeometry - - class LatheGeometry extends Geometry { - - constructor( points, segments, phiStart, phiLength ) { - - super(); - - this.type = 'LatheGeometry'; - - this.parameters = { - points: points, - segments: segments, - phiStart: phiStart, - phiLength: phiLength - }; - - this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) ); - this.mergeVertices(); - - } - - } - - // LatheBufferGeometry - - class LatheBufferGeometry extends BufferGeometry { - - constructor( points, segments, phiStart, phiLength ) { - - super(); - - this.type = 'LatheBufferGeometry'; - - this.parameters = { - points: points, - segments: segments, - phiStart: phiStart, - phiLength: phiLength - }; - - segments = Math.floor( segments ) || 12; - phiStart = phiStart || 0; - phiLength = phiLength || Math.PI * 2; - - // clamp phiLength so it's in range of [ 0, 2PI ] - - phiLength = MathUtils.clamp( phiLength, 0, Math.PI * 2 ); - - - // buffers - - const indices = []; - const vertices = []; - const uvs = []; - - // helper variables - - const inverseSegments = 1.0 / segments; - const vertex = new Vector3(); - const uv = new Vector2(); - - // generate vertices and uvs - - for ( let i = 0; i <= segments; i ++ ) { - - const phi = phiStart + i * inverseSegments * phiLength; - - const sin = Math.sin( phi ); - const cos = Math.cos( phi ); - - for ( let j = 0; j <= ( points.length - 1 ); j ++ ) { - - // vertex - - vertex.x = points[ j ].x * sin; - vertex.y = points[ j ].y; - vertex.z = points[ j ].x * cos; - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // uv - - uv.x = i / segments; - uv.y = j / ( points.length - 1 ); - - uvs.push( uv.x, uv.y ); - - - } - - } - - // indices - - for ( let i = 0; i < segments; i ++ ) { - - for ( let j = 0; j < ( points.length - 1 ); j ++ ) { - - const base = j + i * points.length; - - const a = base; - const b = base + points.length; - const c = base + points.length + 1; - const d = base + 1; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - // generate normals - - this.computeVertexNormals(); - - // if the geometry is closed, we need to average the normals along the seam. - // because the corresponding vertices are identical (but still have different UVs). - - if ( phiLength === Math.PI * 2 ) { - - const normals = this.attributes.normal.array; - const n1 = new Vector3(); - const n2 = new Vector3(); - const n = new Vector3(); - - // this is the buffer offset for the last line of vertices - - const base = segments * points.length * 3; - - for ( let i = 0, j = 0; i < points.length; i ++, j += 3 ) { - - // select the normal of the vertex in the first line - - n1.x = normals[ j + 0 ]; - n1.y = normals[ j + 1 ]; - n1.z = normals[ j + 2 ]; - - // select the normal of the vertex in the last line - - n2.x = normals[ base + j + 0 ]; - n2.y = normals[ base + j + 1 ]; - n2.z = normals[ base + j + 2 ]; - - // average normals - - n.addVectors( n1, n2 ).normalize(); - - // assign the new values to both normals - - normals[ j + 0 ] = normals[ base + j + 0 ] = n.x; - normals[ j + 1 ] = normals[ base + j + 1 ] = n.y; - normals[ j + 2 ] = normals[ base + j + 2 ] = n.z; - - } - - } - - } - - } - - // ShapeGeometry - - class ShapeGeometry extends Geometry { - - constructor( shapes, curveSegments ) { - - super(); - this.type = 'ShapeGeometry'; - - if ( typeof curveSegments === 'object' ) { - - console.warn( 'THREE.ShapeGeometry: Options parameter has been removed.' ); - - curveSegments = curveSegments.curveSegments; - - } - - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; - - this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) ); - this.mergeVertices(); - - } - - toJSON() { - - const data = Geometry.prototype.toJSON.call( this ); - - const shapes = this.parameters.shapes; - - return toJSON$1( shapes, data ); - - } - - } - - // ShapeBufferGeometry - - class ShapeBufferGeometry extends BufferGeometry { - - constructor( shapes, curveSegments ) { - - super(); - this.type = 'ShapeBufferGeometry'; - - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; - - curveSegments = curveSegments || 12; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - let groupStart = 0; - let groupCount = 0; - - // allow single and array values for "shapes" parameter - - if ( Array.isArray( shapes ) === false ) { - - addShape( shapes ); - - } else { - - for ( let i = 0; i < shapes.length; i ++ ) { - - addShape( shapes[ i ] ); - - this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support - - groupStart += groupCount; - groupCount = 0; - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - - // helper functions - - function addShape( shape ) { - - const indexOffset = vertices.length / 3; - const points = shape.extractPoints( curveSegments ); - - let shapeVertices = points.shape; - const shapeHoles = points.holes; - - // check direction of vertices - - if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { - - shapeVertices = shapeVertices.reverse(); - - } - - for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { - - const shapeHole = shapeHoles[ i ]; - - if ( ShapeUtils.isClockWise( shapeHole ) === true ) { - - shapeHoles[ i ] = shapeHole.reverse(); - - } - - } - - const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); - - // join vertices of inner and outer paths to a single array - - for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { - - const shapeHole = shapeHoles[ i ]; - shapeVertices = shapeVertices.concat( shapeHole ); - - } - - // vertices, normals, uvs - - for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { - - const vertex = shapeVertices[ i ]; - - vertices.push( vertex.x, vertex.y, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( vertex.x, vertex.y ); // world uvs - - } - - // incides - - for ( let i = 0, l = faces.length; i < l; i ++ ) { - - const face = faces[ i ]; - - const a = face[ 0 ] + indexOffset; - const b = face[ 1 ] + indexOffset; - const c = face[ 2 ] + indexOffset; - - indices.push( a, b, c ); - groupCount += 3; - - } - - } - - } - - toJSON() { - - const data = BufferGeometry.prototype.toJSON.call( this ); - - const shapes = this.parameters.shapes; - - return toJSON$1( shapes, data ); - - } - - } - - // - - function toJSON$1( shapes, data ) { - - data.shapes = []; - - if ( Array.isArray( shapes ) ) { - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - - data.shapes.push( shape.uuid ); - - } - - } else { - - data.shapes.push( shapes.uuid ); - - } - - return data; - - } - - class EdgesGeometry extends BufferGeometry { - - constructor( geometry, thresholdAngle ) { - - super(); - - this.type = 'EdgesGeometry'; - - this.parameters = { - thresholdAngle: thresholdAngle - }; - - thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; - - // buffer - - const vertices = []; - - // helper variables - - const thresholdDot = Math.cos( MathUtils.DEG2RAD * thresholdAngle ); - const edge = [ 0, 0 ], edges = {}; - let edge1, edge2, key; - const keys = [ 'a', 'b', 'c' ]; - - // prepare source geometry - - let geometry2; - - if ( geometry.isBufferGeometry ) { - - geometry2 = new Geometry(); - geometry2.fromBufferGeometry( geometry ); - - } else { - - geometry2 = geometry.clone(); - - } - - geometry2.mergeVertices(); - geometry2.computeFaceNormals(); - - const sourceVertices = geometry2.vertices; - const faces = geometry2.faces; - - // now create a data structure where each entry represents an edge with its adjoining faces - - for ( let i = 0, l = faces.length; i < l; i ++ ) { - - const face = faces[ i ]; - - for ( let j = 0; j < 3; j ++ ) { - - edge1 = face[ keys[ j ] ]; - edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; - edge[ 0 ] = Math.min( edge1, edge2 ); - edge[ 1 ] = Math.max( edge1, edge2 ); - - key = edge[ 0 ] + ',' + edge[ 1 ]; - - if ( edges[ key ] === undefined ) { - - edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined }; - - } else { - - edges[ key ].face2 = i; - - } - - } - - } - - // generate vertices - - for ( key in edges ) { - - const e = edges[ key ]; - - // an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree. - - if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) { - - let vertex = sourceVertices[ e.index1 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - vertex = sourceVertices[ e.index2 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } - - // build geometry - - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - - } - - } - - // CylinderGeometry - - class CylinderGeometry extends Geometry { - - constructor( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - super(); - this.type = 'CylinderGeometry'; - - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - } - - // CylinderBufferGeometry - - class CylinderBufferGeometry extends BufferGeometry { - - constructor( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - super(); - this.type = 'CylinderBufferGeometry'; - - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - const scope = this; - - radiusTop = radiusTop !== undefined ? radiusTop : 1; - radiusBottom = radiusBottom !== undefined ? radiusBottom : 1; - height = height || 1; - - radialSegments = Math.floor( radialSegments ) || 8; - heightSegments = Math.floor( heightSegments ) || 1; - - openEnded = openEnded !== undefined ? openEnded : false; - thetaStart = thetaStart !== undefined ? thetaStart : 0.0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - let index = 0; - const indexArray = []; - const halfHeight = height / 2; - let groupStart = 0; - - // generate geometry - - generateTorso(); - - if ( openEnded === false ) { - - if ( radiusTop > 0 ) generateCap( true ); - if ( radiusBottom > 0 ) generateCap( false ); - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - function generateTorso() { - - const normal = new Vector3(); - const vertex = new Vector3(); - - let groupCount = 0; - - // this will be used to calculate the normal - const slope = ( radiusBottom - radiusTop ) / height; - - // generate vertices, normals and uvs - - for ( let y = 0; y <= heightSegments; y ++ ) { - - const indexRow = []; - - const v = y / heightSegments; - - // calculate the radius of the current row - - const radius = v * ( radiusBottom - radiusTop ) + radiusTop; - - for ( let x = 0; x <= radialSegments; x ++ ) { - - const u = x / radialSegments; - - const theta = u * thetaLength + thetaStart; - - const sinTheta = Math.sin( theta ); - const cosTheta = Math.cos( theta ); - - // vertex - - vertex.x = radius * sinTheta; - vertex.y = - v * height + halfHeight; - vertex.z = radius * cosTheta; - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normal.set( sinTheta, slope, cosTheta ).normalize(); - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( u, 1 - v ); - - // save index of vertex in respective row - - indexRow.push( index ++ ); - - } - - // now save vertices of the row in our index array - - indexArray.push( indexRow ); - - } - - // generate indices - - for ( let x = 0; x < radialSegments; x ++ ) { - - for ( let y = 0; y < heightSegments; y ++ ) { - - // we use the index array to access the correct indices - - const a = indexArray[ y ][ x ]; - const b = indexArray[ y + 1 ][ x ]; - const c = indexArray[ y + 1 ][ x + 1 ]; - const d = indexArray[ y ][ x + 1 ]; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - // update group counter - - groupCount += 6; - - } - - } - - // add a group to the geometry. this will ensure multi material support - - scope.addGroup( groupStart, groupCount, 0 ); - - // calculate new start value for groups - - groupStart += groupCount; - - } - - function generateCap( top ) { - - // save the index of the first center vertex - const centerIndexStart = index; - - const uv = new Vector2(); - const vertex = new Vector3(); - - let groupCount = 0; - - const radius = ( top === true ) ? radiusTop : radiusBottom; - const sign = ( top === true ) ? 1 : - 1; - - // first we generate the center vertex data of the cap. - // because the geometry needs one set of uvs per face, - // we must generate a center vertex per face/segment - - for ( let x = 1; x <= radialSegments; x ++ ) { - - // vertex - - vertices.push( 0, halfHeight * sign, 0 ); - - // normal - - normals.push( 0, sign, 0 ); - - // uv - - uvs.push( 0.5, 0.5 ); - - // increase index - - index ++; - - } - - // save the index of the last center vertex - const centerIndexEnd = index; - - // now we generate the surrounding vertices, normals and uvs - - for ( let x = 0; x <= radialSegments; x ++ ) { - - const u = x / radialSegments; - const theta = u * thetaLength + thetaStart; - - const cosTheta = Math.cos( theta ); - const sinTheta = Math.sin( theta ); - - // vertex - - vertex.x = radius * sinTheta; - vertex.y = halfHeight * sign; - vertex.z = radius * cosTheta; - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normals.push( 0, sign, 0 ); - - // uv - - uv.x = ( cosTheta * 0.5 ) + 0.5; - uv.y = ( sinTheta * 0.5 * sign ) + 0.5; - uvs.push( uv.x, uv.y ); - - // increase index - - index ++; - - } - - // generate indices - - for ( let x = 0; x < radialSegments; x ++ ) { - - const c = centerIndexStart + x; - const i = centerIndexEnd + x; - - if ( top === true ) { - - // face top - - indices.push( i, i + 1, c ); - - } else { - - // face bottom - - indices.push( i + 1, i, c ); - - } - - groupCount += 3; - - } - - // add a group to the geometry. this will ensure multi material support - - scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); - - // calculate new start value for groups - - groupStart += groupCount; - - } - - } - - } - - // ConeGeometry - - class ConeGeometry extends CylinderGeometry { - - constructor( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - super( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); - this.type = 'ConeGeometry'; - - this.parameters = { - radius: radius, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - } - - } - - // ConeBufferGeometry - - class ConeBufferGeometry extends CylinderBufferGeometry { - - constructor( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - super( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); - this.type = 'ConeBufferGeometry'; - - this.parameters = { - radius: radius, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - } - - } - - // CircleGeometry - - class CircleGeometry extends Geometry { - - constructor( radius, segments, thetaStart, thetaLength ) { - - super(); - this.type = 'CircleGeometry'; - - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - } - - // CircleBufferGeometry - - class CircleBufferGeometry extends BufferGeometry { - - constructor( radius, segments, thetaStart, thetaLength ) { - - super(); - - this.type = 'CircleBufferGeometry'; - - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - radius = radius || 1; - segments = segments !== undefined ? Math.max( 3, segments ) : 8; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - const vertex = new Vector3(); - const uv = new Vector2(); - - // center point - - vertices.push( 0, 0, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( 0.5, 0.5 ); - - for ( let s = 0, i = 3; s <= segments; s ++, i += 3 ) { - - const segment = thetaStart + s / segments * thetaLength; - - // vertex - - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normals.push( 0, 0, 1 ); - - // uvs - - uv.x = ( vertices[ i ] / radius + 1 ) / 2; - uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; - - uvs.push( uv.x, uv.y ); - - } - - // indices - - for ( let i = 1; i <= segments; i ++ ) { - - indices.push( i, i + 1, 0 ); - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - } - - var Geometries = /*#__PURE__*/Object.freeze({ - __proto__: null, - WireframeGeometry: WireframeGeometry, - ParametricGeometry: ParametricGeometry, - ParametricBufferGeometry: ParametricBufferGeometry, - TetrahedronGeometry: TetrahedronGeometry, - TetrahedronBufferGeometry: TetrahedronBufferGeometry, - OctahedronGeometry: OctahedronGeometry, - OctahedronBufferGeometry: OctahedronBufferGeometry, - IcosahedronGeometry: IcosahedronGeometry, - IcosahedronBufferGeometry: IcosahedronBufferGeometry, - DodecahedronGeometry: DodecahedronGeometry, - DodecahedronBufferGeometry: DodecahedronBufferGeometry, - PolyhedronGeometry: PolyhedronGeometry, - PolyhedronBufferGeometry: PolyhedronBufferGeometry, - TubeGeometry: TubeGeometry, - TubeBufferGeometry: TubeBufferGeometry, - TorusKnotGeometry: TorusKnotGeometry, - TorusKnotBufferGeometry: TorusKnotBufferGeometry, - TorusGeometry: TorusGeometry, - TorusBufferGeometry: TorusBufferGeometry, - TextGeometry: TextGeometry, - TextBufferGeometry: TextBufferGeometry, - SphereGeometry: SphereGeometry, - SphereBufferGeometry: SphereBufferGeometry, - RingGeometry: RingGeometry, - RingBufferGeometry: RingBufferGeometry, - PlaneGeometry: PlaneGeometry, - PlaneBufferGeometry: PlaneBufferGeometry, - LatheGeometry: LatheGeometry, - LatheBufferGeometry: LatheBufferGeometry, - ShapeGeometry: ShapeGeometry, - ShapeBufferGeometry: ShapeBufferGeometry, - ExtrudeGeometry: ExtrudeGeometry, - ExtrudeBufferGeometry: ExtrudeBufferGeometry, - EdgesGeometry: EdgesGeometry, - ConeGeometry: ConeGeometry, - ConeBufferGeometry: ConeBufferGeometry, - CylinderGeometry: CylinderGeometry, - CylinderBufferGeometry: CylinderBufferGeometry, - CircleGeometry: CircleGeometry, - CircleBufferGeometry: CircleBufferGeometry, - BoxGeometry: BoxGeometry, - BoxBufferGeometry: BoxBufferGeometry - }); - - /** - * parameters = { - * color: - * } - */ - - function ShadowMaterial( parameters ) { - - Material.call( this ); - - this.type = 'ShadowMaterial'; - - this.color = new Color( 0x000000 ); - this.transparent = true; - - this.setValues( parameters ); - - } - - ShadowMaterial.prototype = Object.create( Material.prototype ); - ShadowMaterial.prototype.constructor = ShadowMaterial; - - ShadowMaterial.prototype.isShadowMaterial = true; - - ShadowMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - return this; - - }; - - function RawShaderMaterial( parameters ) { - - ShaderMaterial.call( this, parameters ); - - this.type = 'RawShaderMaterial'; - - } - - RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); - RawShaderMaterial.prototype.constructor = RawShaderMaterial; - - RawShaderMaterial.prototype.isRawShaderMaterial = true; - - /** - * parameters = { - * color: , - * roughness: , - * metalness: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * roughnessMap: new THREE.Texture( ), - * - * metalnessMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * envMapIntensity: - * - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshStandardMaterial( parameters ) { - - Material.call( this ); - - this.defines = { 'STANDARD': '' }; - - this.type = 'MeshStandardMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - this.roughness = 1.0; - this.metalness = 0.0; - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.roughnessMap = null; - - this.metalnessMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.envMapIntensity = 1.0; - - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.vertexTangents = false; - - this.setValues( parameters ); - - } - - MeshStandardMaterial.prototype = Object.create( Material.prototype ); - MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; - - MeshStandardMaterial.prototype.isMeshStandardMaterial = true; - - MeshStandardMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.defines = { 'STANDARD': '' }; - - this.color.copy( source.color ); - this.roughness = source.roughness; - this.metalness = source.metalness; - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.roughnessMap = source.roughnessMap; - - this.metalnessMap = source.metalnessMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.envMapIntensity = source.envMapIntensity; - - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - this.vertexTangents = source.vertexTangents; - - return this; - - }; - - /** - * parameters = { - * clearcoat: , - * clearcoatMap: new THREE.Texture( ), - * clearcoatRoughness: , - * clearcoatRoughnessMap: new THREE.Texture( ), - * clearcoatNormalScale: , - * clearcoatNormalMap: new THREE.Texture( ), - * - * reflectivity: , - * - * sheen: , - * - * transmission: , - * transmissionMap: new THREE.Texture( ) - * } - */ - - function MeshPhysicalMaterial( parameters ) { - - MeshStandardMaterial.call( this ); - - this.defines = { - - 'STANDARD': '', - 'PHYSICAL': '' - - }; - - this.type = 'MeshPhysicalMaterial'; - - this.clearcoat = 0.0; - this.clearcoatMap = null; - this.clearcoatRoughness = 0.0; - this.clearcoatRoughnessMap = null; - this.clearcoatNormalScale = new Vector2( 1, 1 ); - this.clearcoatNormalMap = null; - - this.reflectivity = 0.5; // maps to F0 = 0.04 - - this.sheen = null; // null will disable sheen bsdf - - this.transmission = 0.0; - this.transmissionMap = null; - - this.setValues( parameters ); - - } - - MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); - MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; - - MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; - - MeshPhysicalMaterial.prototype.copy = function ( source ) { - - MeshStandardMaterial.prototype.copy.call( this, source ); - - this.defines = { - - 'STANDARD': '', - 'PHYSICAL': '' - - }; - - this.clearcoat = source.clearcoat; - this.clearcoatMap = source.clearcoatMap; - this.clearcoatRoughness = source.clearcoatRoughness; - this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; - this.clearcoatNormalMap = source.clearcoatNormalMap; - this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); - - this.reflectivity = source.reflectivity; - - if ( source.sheen ) { - - this.sheen = ( this.sheen || new Color() ).copy( source.sheen ); - - } else { - - this.sheen = null; - - } - - this.transmission = source.transmission; - this.transmissionMap = source.transmissionMap; - - return this; - - }; - - /** - * parameters = { - * color: , - * specular: , - * shininess: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.MultiplyOperation, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshPhongMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshPhongMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - this.specular = new Color( 0x111111 ); - this.shininess = 30; - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshPhongMaterial.prototype = Object.create( Material.prototype ); - MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; - - MeshPhongMaterial.prototype.isMeshPhongMaterial = true; - - MeshPhongMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - this.specular.copy( source.specular ); - this.shininess = source.shininess; - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * parameters = { - * color: , - * - * map: new THREE.Texture( ), - * gradientMap: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshToonMaterial( parameters ) { - - Material.call( this ); - - this.defines = { 'TOON': '' }; - - this.type = 'MeshToonMaterial'; - - this.color = new Color( 0xffffff ); - - this.map = null; - this.gradientMap = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.alphaMap = null; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshToonMaterial.prototype = Object.create( Material.prototype ); - MeshToonMaterial.prototype.constructor = MeshToonMaterial; - - MeshToonMaterial.prototype.isMeshToonMaterial = true; - - MeshToonMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - this.gradientMap = source.gradientMap; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.alphaMap = source.alphaMap; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * parameters = { - * opacity: , - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshNormalMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshNormalMaterial'; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshNormalMaterial.prototype = Object.create( Material.prototype ); - MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; - - MeshNormalMaterial.prototype.isMeshNormalMaterial = true; - - MeshNormalMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * parameters = { - * color: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshLambertMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshLambertMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshLambertMaterial.prototype = Object.create( Material.prototype ); - MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; - - MeshLambertMaterial.prototype.isMeshLambertMaterial = true; - - MeshLambertMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * parameters = { - * color: , - * opacity: , - * - * matcap: new THREE.Texture( ), - * - * map: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshMatcapMaterial( parameters ) { - - Material.call( this ); - - this.defines = { 'MATCAP': '' }; - - this.type = 'MeshMatcapMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - - this.matcap = null; - - this.map = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.alphaMap = null; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshMatcapMaterial.prototype = Object.create( Material.prototype ); - MeshMatcapMaterial.prototype.constructor = MeshMatcapMaterial; - - MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; - - MeshMatcapMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.defines = { 'MATCAP': '' }; - - this.color.copy( source.color ); - - this.matcap = source.matcap; - - this.map = source.map; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.alphaMap = source.alphaMap; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * - * scale: , - * dashSize: , - * gapSize: - * } - */ - - function LineDashedMaterial( parameters ) { - - LineBasicMaterial.call( this ); - - this.type = 'LineDashedMaterial'; - - this.scale = 1; - this.dashSize = 3; - this.gapSize = 1; - - this.setValues( parameters ); - - } - - LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype ); - LineDashedMaterial.prototype.constructor = LineDashedMaterial; - - LineDashedMaterial.prototype.isLineDashedMaterial = true; - - LineDashedMaterial.prototype.copy = function ( source ) { - - LineBasicMaterial.prototype.copy.call( this, source ); - - this.scale = source.scale; - this.dashSize = source.dashSize; - this.gapSize = source.gapSize; - - return this; - - }; - - var Materials = /*#__PURE__*/Object.freeze({ - __proto__: null, - ShadowMaterial: ShadowMaterial, - SpriteMaterial: SpriteMaterial, - RawShaderMaterial: RawShaderMaterial, - ShaderMaterial: ShaderMaterial, - PointsMaterial: PointsMaterial, - MeshPhysicalMaterial: MeshPhysicalMaterial, - MeshStandardMaterial: MeshStandardMaterial, - MeshPhongMaterial: MeshPhongMaterial, - MeshToonMaterial: MeshToonMaterial, - MeshNormalMaterial: MeshNormalMaterial, - MeshLambertMaterial: MeshLambertMaterial, - MeshDepthMaterial: MeshDepthMaterial, - MeshDistanceMaterial: MeshDistanceMaterial, - MeshBasicMaterial: MeshBasicMaterial, - MeshMatcapMaterial: MeshMatcapMaterial, - LineDashedMaterial: LineDashedMaterial, - LineBasicMaterial: LineBasicMaterial, - Material: Material - }); - - const AnimationUtils = { - - // same as Array.prototype.slice, but also works on typed arrays - arraySlice: function ( array, from, to ) { - - if ( AnimationUtils.isTypedArray( array ) ) { - - // in ios9 array.subarray(from, undefined) will return empty array - // but array.subarray(from) or array.subarray(from, len) is correct - return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); - - } - - return array.slice( from, to ); - - }, - - // converts an array to a specific type - convertArray: function ( array, type, forceClone ) { - - if ( ! array || // let 'undefined' and 'null' pass - ! forceClone && array.constructor === type ) return array; - - if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { - - return new type( array ); // create typed array - - } - - return Array.prototype.slice.call( array ); // create Array - - }, - - isTypedArray: function ( object ) { - - return ArrayBuffer.isView( object ) && - ! ( object instanceof DataView ); - - }, - - // returns an array by which times and values can be sorted - getKeyframeOrder: function ( times ) { - - function compareTime( i, j ) { - - return times[ i ] - times[ j ]; - - } - - const n = times.length; - const result = new Array( n ); - for ( let i = 0; i !== n; ++ i ) result[ i ] = i; - - result.sort( compareTime ); - - return result; - - }, - - // uses the array previously returned by 'getKeyframeOrder' to sort data - sortedArray: function ( values, stride, order ) { - - const nValues = values.length; - const result = new values.constructor( nValues ); - - for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { - - const srcOffset = order[ i ] * stride; - - for ( let j = 0; j !== stride; ++ j ) { - - result[ dstOffset ++ ] = values[ srcOffset + j ]; - - } - - } - - return result; - - }, - - // function for parsing AOS keyframe formats - flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { - - let i = 1, key = jsonKeys[ 0 ]; - - while ( key !== undefined && key[ valuePropertyName ] === undefined ) { - - key = jsonKeys[ i ++ ]; - - } - - if ( key === undefined ) return; // no data - - let value = key[ valuePropertyName ]; - if ( value === undefined ) return; // no data - - if ( Array.isArray( value ) ) { - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - values.push.apply( values, value ); // push all elements - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } else if ( value.toArray !== undefined ) { - - // ...assume THREE.Math-ish - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - value.toArray( values, values.length ); - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } else { - - // otherwise push as-is - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - values.push( value ); - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } - - }, - - subclip: function ( sourceClip, name, startFrame, endFrame, fps ) { - - fps = fps || 30; - - const clip = sourceClip.clone(); - - clip.name = name; - - const tracks = []; - - for ( let i = 0; i < clip.tracks.length; ++ i ) { - - const track = clip.tracks[ i ]; - const valueSize = track.getValueSize(); - - const times = []; - const values = []; - - for ( let j = 0; j < track.times.length; ++ j ) { - - const frame = track.times[ j ] * fps; - - if ( frame < startFrame || frame >= endFrame ) continue; - - times.push( track.times[ j ] ); - - for ( let k = 0; k < valueSize; ++ k ) { - - values.push( track.values[ j * valueSize + k ] ); - - } - - } - - if ( times.length === 0 ) continue; - - track.times = AnimationUtils.convertArray( times, track.times.constructor ); - track.values = AnimationUtils.convertArray( values, track.values.constructor ); - - tracks.push( track ); - - } - - clip.tracks = tracks; - - // find minimum .times value across all tracks in the trimmed clip - - let minStartTime = Infinity; - - for ( let i = 0; i < clip.tracks.length; ++ i ) { - - if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { - - minStartTime = clip.tracks[ i ].times[ 0 ]; - - } - - } - - // shift all tracks such that clip begins at t=0 - - for ( let i = 0; i < clip.tracks.length; ++ i ) { - - clip.tracks[ i ].shift( - 1 * minStartTime ); - - } - - clip.resetDuration(); - - return clip; - - }, - - makeClipAdditive: function ( targetClip, referenceFrame, referenceClip, fps ) { - - if ( referenceFrame === undefined ) referenceFrame = 0; - if ( referenceClip === undefined ) referenceClip = targetClip; - if ( fps === undefined || fps <= 0 ) fps = 30; - - const numTracks = targetClip.tracks.length; - const referenceTime = referenceFrame / fps; - - // Make each track's values relative to the values at the reference frame - for ( let i = 0; i < numTracks; ++ i ) { - - const referenceTrack = referenceClip.tracks[ i ]; - const referenceTrackType = referenceTrack.ValueTypeName; - - // Skip this track if it's non-numeric - if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue; - - // Find the track in the target clip whose name and type matches the reference track - const targetTrack = targetClip.tracks.find( function ( track ) { - - return track.name === referenceTrack.name - && track.ValueTypeName === referenceTrackType; - - } ); - - if ( targetTrack === undefined ) continue; - - let referenceOffset = 0; - const referenceValueSize = referenceTrack.getValueSize(); - - if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - - referenceOffset = referenceValueSize / 3; - - } - - let targetOffset = 0; - const targetValueSize = targetTrack.getValueSize(); - - if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - - targetOffset = targetValueSize / 3; - - } - - const lastIndex = referenceTrack.times.length - 1; - let referenceValue; - - // Find the value to subtract out of the track - if ( referenceTime <= referenceTrack.times[ 0 ] ) { - - // Reference frame is earlier than the first keyframe, so just use the first keyframe - const startIndex = referenceOffset; - const endIndex = referenceValueSize - referenceOffset; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); - - } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { - - // Reference frame is after the last keyframe, so just use the last keyframe - const startIndex = lastIndex * referenceValueSize + referenceOffset; - const endIndex = startIndex + referenceValueSize - referenceOffset; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); - - } else { - - // Interpolate to the reference value - const interpolant = referenceTrack.createInterpolant(); - const startIndex = referenceOffset; - const endIndex = referenceValueSize - referenceOffset; - interpolant.evaluate( referenceTime ); - referenceValue = AnimationUtils.arraySlice( interpolant.resultBuffer, startIndex, endIndex ); - - } - - // Conjugate the quaternion - if ( referenceTrackType === 'quaternion' ) { - - const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); - referenceQuat.toArray( referenceValue ); - - } - - // Subtract the reference value from all of the track values - - const numTimes = targetTrack.times.length; - for ( let j = 0; j < numTimes; ++ j ) { - - const valueStart = j * targetValueSize + targetOffset; - - if ( referenceTrackType === 'quaternion' ) { - - // Multiply the conjugate for quaternion track types - Quaternion.multiplyQuaternionsFlat( - targetTrack.values, - valueStart, - referenceValue, - 0, - targetTrack.values, - valueStart - ); - - } else { - - const valueEnd = targetValueSize - targetOffset * 2; - - // Subtract each value for all other numeric track types - for ( let k = 0; k < valueEnd; ++ k ) { - - targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; - - } - - } - - } - - } - - targetClip.blendMode = AdditiveAnimationBlendMode; - - return targetClip; - - } - - }; - - /** - * Abstract base class of interpolants over parametric samples. - * - * The parameter domain is one dimensional, typically the time or a path - * along a curve defined by the data. - * - * The sample values can have any dimensionality and derived classes may - * apply special interpretations to the data. - * - * This class provides the interval seek in a Template Method, deferring - * the actual interpolation to derived classes. - * - * Time complexity is O(1) for linear access crossing at most two points - * and O(log N) for random access, where N is the number of positions. - * - * References: - * - * http://www.oodesign.com/template-method-pattern.html - * - */ - - function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - this.parameterPositions = parameterPositions; - this._cachedIndex = 0; - - this.resultBuffer = resultBuffer !== undefined ? - resultBuffer : new sampleValues.constructor( sampleSize ); - this.sampleValues = sampleValues; - this.valueSize = sampleSize; - - } - - Object.assign( Interpolant.prototype, { - - evaluate: function ( t ) { - - const pp = this.parameterPositions; - let i1 = this._cachedIndex, - t1 = pp[ i1 ], - t0 = pp[ i1 - 1 ]; - - validate_interval: { - - seek: { - - let right; - - linear_scan: { - - //- See http://jsperf.com/comparison-to-undefined/3 - //- slower code: - //- - //- if ( t >= t1 || t1 === undefined ) { - forward_scan: if ( ! ( t < t1 ) ) { - - for ( let giveUpAt = i1 + 2; ; ) { - - if ( t1 === undefined ) { - - if ( t < t0 ) break forward_scan; - - // after end - - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t, t0 ); - - } - - if ( i1 === giveUpAt ) break; // this loop - - t0 = t1; - t1 = pp[ ++ i1 ]; - - if ( t < t1 ) { - - // we have arrived at the sought interval - break seek; - - } - - } - - // prepare binary search on the right side of the index - right = pp.length; - break linear_scan; - - } - - //- slower code: - //- if ( t < t0 || t0 === undefined ) { - if ( ! ( t >= t0 ) ) { - - // looping? - - const t1global = pp[ 1 ]; - - if ( t < t1global ) { - - i1 = 2; // + 1, using the scan for the details - t0 = t1global; - - } - - // linear reverse scan - - for ( let giveUpAt = i1 - 2; ; ) { - - if ( t0 === undefined ) { - - // before start - - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); - - } - - if ( i1 === giveUpAt ) break; // this loop - - t1 = t0; - t0 = pp[ -- i1 - 1 ]; - - if ( t >= t0 ) { - - // we have arrived at the sought interval - break seek; - - } - - } - - // prepare binary search on the left side of the index - right = i1; - i1 = 0; - break linear_scan; - - } - - // the interval is valid - - break validate_interval; - - } // linear scan - - // binary search - - while ( i1 < right ) { - - const mid = ( i1 + right ) >>> 1; - - if ( t < pp[ mid ] ) { - - right = mid; - - } else { - - i1 = mid + 1; - - } - - } - - t1 = pp[ i1 ]; - t0 = pp[ i1 - 1 ]; - - // check boundary cases, again - - if ( t0 === undefined ) { - - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); - - } - - if ( t1 === undefined ) { - - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t0, t ); - - } - - } // seek - - this._cachedIndex = i1; - - this.intervalChanged_( i1, t0, t1 ); - - } // validate_interval - - return this.interpolate_( i1, t0, t, t1 ); - - }, - - settings: null, // optional, subclass-specific settings structure - // Note: The indirection allows central control of many interpolants. - - // --- Protected interface - - DefaultSettings_: {}, - - getSettings_: function () { - - return this.settings || this.DefaultSettings_; - - }, - - copySampleValue_: function ( index ) { - - // copies a sample value to the result buffer - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - offset = index * stride; - - for ( let i = 0; i !== stride; ++ i ) { - - result[ i ] = values[ offset + i ]; - - } - - return result; - - }, - - // Template methods for derived classes: - - interpolate_: function ( /* i1, t0, t, t1 */ ) { - - throw new Error( 'call to abstract method' ); - // implementations shall return this.resultBuffer - - }, - - intervalChanged_: function ( /* i1, t0, t1 */ ) { - - // empty - - } - - } ); - - // DECLARE ALIAS AFTER assign prototype - Object.assign( Interpolant.prototype, { - - //( 0, t, t0 ), returns this.resultBuffer - beforeStart_: Interpolant.prototype.copySampleValue_, - - //( N-1, tN-1, t ), returns this.resultBuffer - afterEnd_: Interpolant.prototype.copySampleValue_, - - } ); - - /** - * Fast and simple cubic spline interpolant. - * - * It was derived from a Hermitian construction setting the first derivative - * at each sample position to the linear slope between neighboring positions - * over their parameter interval. - */ - - function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - this._weightPrev = - 0; - this._offsetPrev = - 0; - this._weightNext = - 0; - this._offsetNext = - 0; - - } - - CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: CubicInterpolant, - - DefaultSettings_: { - - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - - }, - - intervalChanged_: function ( i1, t0, t1 ) { - - const pp = this.parameterPositions; - let iPrev = i1 - 2, - iNext = i1 + 1, - - tPrev = pp[ iPrev ], - tNext = pp[ iNext ]; - - if ( tPrev === undefined ) { - - switch ( this.getSettings_().endingStart ) { - - case ZeroSlopeEnding: - - // f'(t0) = 0 - iPrev = i1; - tPrev = 2 * t0 - t1; - - break; - - case WrapAroundEnding: - - // use the other end of the curve - iPrev = pp.length - 2; - tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; - - break; - - default: // ZeroCurvatureEnding - - // f''(t0) = 0 a.k.a. Natural Spline - iPrev = i1; - tPrev = t1; - - } - - } - - if ( tNext === undefined ) { - - switch ( this.getSettings_().endingEnd ) { - - case ZeroSlopeEnding: - - // f'(tN) = 0 - iNext = i1; - tNext = 2 * t1 - t0; - - break; - - case WrapAroundEnding: - - // use the other end of the curve - iNext = 1; - tNext = t1 + pp[ 1 ] - pp[ 0 ]; - - break; - - default: // ZeroCurvatureEnding - - // f''(tN) = 0, a.k.a. Natural Spline - iNext = i1 - 1; - tNext = t0; - - } - - } - - const halfDt = ( t1 - t0 ) * 0.5, - stride = this.valueSize; - - this._weightPrev = halfDt / ( t0 - tPrev ); - this._weightNext = halfDt / ( tNext - t1 ); - this._offsetPrev = iPrev * stride; - this._offsetNext = iNext * stride; - - }, - - interpolate_: function ( i1, t0, t, t1 ) { - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - o1 = i1 * stride, o0 = o1 - stride, - oP = this._offsetPrev, oN = this._offsetNext, - wP = this._weightPrev, wN = this._weightNext, - - p = ( t - t0 ) / ( t1 - t0 ), - pp = p * p, - ppp = pp * p; - - // evaluate polynomials - - const sP = - wP * ppp + 2 * wP * pp - wP * p; - const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; - const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; - const sN = wN * ppp - wN * pp; - - // combine data linearly - - for ( let i = 0; i !== stride; ++ i ) { - - result[ i ] = - sP * values[ oP + i ] + - s0 * values[ o0 + i ] + - s1 * values[ o1 + i ] + - sN * values[ oN + i ]; - - } - - return result; - - } - - } ); - - function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: LinearInterpolant, - - interpolate_: function ( i1, t0, t, t1 ) { - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - offset1 = i1 * stride, - offset0 = offset1 - stride, - - weight1 = ( t - t0 ) / ( t1 - t0 ), - weight0 = 1 - weight1; - - for ( let i = 0; i !== stride; ++ i ) { - - result[ i ] = - values[ offset0 + i ] * weight0 + - values[ offset1 + i ] * weight1; - - } - - return result; - - } - - } ); - - /** - * - * Interpolant that evaluates to the sample value at the position preceeding - * the parameter. - */ - - function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: DiscreteInterpolant, - - interpolate_: function ( i1 /*, t0, t, t1 */ ) { - - return this.copySampleValue_( i1 - 1 ); - - } - - } ); - - function KeyframeTrack( name, times, values, interpolation ) { - - if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); - if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); - - this.name = name; - - this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); - this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); - - this.setInterpolation( interpolation || this.DefaultInterpolation ); - - } - - // Static methods - - Object.assign( KeyframeTrack, { - - // Serialization (in static context, because of constructor invocation - // and automatic invocation of .toJSON): - - toJSON: function ( track ) { - - const trackType = track.constructor; - - let json; - - // derived classes can define a static toJSON method - if ( trackType.toJSON !== undefined ) { - - json = trackType.toJSON( track ); - - } else { - - // by default, we assume the data can be serialized as-is - json = { - - 'name': track.name, - 'times': AnimationUtils.convertArray( track.times, Array ), - 'values': AnimationUtils.convertArray( track.values, Array ) - - }; - - const interpolation = track.getInterpolation(); - - if ( interpolation !== track.DefaultInterpolation ) { - - json.interpolation = interpolation; - - } - - } - - json.type = track.ValueTypeName; // mandatory - - return json; - - } - - } ); - - Object.assign( KeyframeTrack.prototype, { - - constructor: KeyframeTrack, - - TimeBufferType: Float32Array, - - ValueBufferType: Float32Array, - - DefaultInterpolation: InterpolateLinear, - - InterpolantFactoryMethodDiscrete: function ( result ) { - - return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); - - }, - - InterpolantFactoryMethodLinear: function ( result ) { - - return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); - - }, - - InterpolantFactoryMethodSmooth: function ( result ) { - - return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); - - }, - - setInterpolation: function ( interpolation ) { - - let factoryMethod; - - switch ( interpolation ) { - - case InterpolateDiscrete: - - factoryMethod = this.InterpolantFactoryMethodDiscrete; - - break; - - case InterpolateLinear: - - factoryMethod = this.InterpolantFactoryMethodLinear; - - break; - - case InterpolateSmooth: - - factoryMethod = this.InterpolantFactoryMethodSmooth; - - break; - - } - - if ( factoryMethod === undefined ) { - - const message = "unsupported interpolation for " + - this.ValueTypeName + " keyframe track named " + this.name; - - if ( this.createInterpolant === undefined ) { - - // fall back to default, unless the default itself is messed up - if ( interpolation !== this.DefaultInterpolation ) { - - this.setInterpolation( this.DefaultInterpolation ); - - } else { - - throw new Error( message ); // fatal, in this case - - } - - } - - console.warn( 'THREE.KeyframeTrack:', message ); - return this; - - } - - this.createInterpolant = factoryMethod; - - return this; - - }, - - getInterpolation: function () { - - switch ( this.createInterpolant ) { - - case this.InterpolantFactoryMethodDiscrete: - - return InterpolateDiscrete; - - case this.InterpolantFactoryMethodLinear: - - return InterpolateLinear; - - case this.InterpolantFactoryMethodSmooth: - - return InterpolateSmooth; - - } - - }, - - getValueSize: function () { - - return this.values.length / this.times.length; - - }, - - // move all keyframes either forwards or backwards in time - shift: function ( timeOffset ) { - - if ( timeOffset !== 0.0 ) { - - const times = this.times; - - for ( let i = 0, n = times.length; i !== n; ++ i ) { - - times[ i ] += timeOffset; - - } - - } - - return this; - - }, - - // scale all keyframe times by a factor (useful for frame <-> seconds conversions) - scale: function ( timeScale ) { - - if ( timeScale !== 1.0 ) { - - const times = this.times; - - for ( let i = 0, n = times.length; i !== n; ++ i ) { - - times[ i ] *= timeScale; - - } - - } - - return this; - - }, - - // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. - // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values - trim: function ( startTime, endTime ) { - - const times = this.times, - nKeys = times.length; - - let from = 0, - to = nKeys - 1; - - while ( from !== nKeys && times[ from ] < startTime ) { - - ++ from; - - } - - while ( to !== - 1 && times[ to ] > endTime ) { - - -- to; - - } - - ++ to; // inclusive -> exclusive bound - - if ( from !== 0 || to !== nKeys ) { - - // empty tracks are forbidden, so keep at least one keyframe - if ( from >= to ) { - - to = Math.max( to, 1 ); - from = to - 1; - - } - - const stride = this.getValueSize(); - this.times = AnimationUtils.arraySlice( times, from, to ); - this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); - - } - - return this; - - }, - - // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable - validate: function () { - - let valid = true; - - const valueSize = this.getValueSize(); - if ( valueSize - Math.floor( valueSize ) !== 0 ) { - - console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); - valid = false; - - } - - const times = this.times, - values = this.values, - - nKeys = times.length; - - if ( nKeys === 0 ) { - - console.error( 'THREE.KeyframeTrack: Track is empty.', this ); - valid = false; - - } - - let prevTime = null; - - for ( let i = 0; i !== nKeys; i ++ ) { - - const currTime = times[ i ]; - - if ( typeof currTime === 'number' && isNaN( currTime ) ) { - - console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); - valid = false; - break; - - } - - if ( prevTime !== null && prevTime > currTime ) { - - console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); - valid = false; - break; - - } - - prevTime = currTime; - - } - - if ( values !== undefined ) { - - if ( AnimationUtils.isTypedArray( values ) ) { - - for ( let i = 0, n = values.length; i !== n; ++ i ) { - - const value = values[ i ]; - - if ( isNaN( value ) ) { - - console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); - valid = false; - break; - - } - - } - - } - - } - - return valid; - - }, - - // removes equivalent sequential keys as common in morph target sequences - // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) - optimize: function () { - - // times or values may be shared with other tracks, so overwriting is unsafe - const times = AnimationUtils.arraySlice( this.times ), - values = AnimationUtils.arraySlice( this.values ), - stride = this.getValueSize(), - - smoothInterpolation = this.getInterpolation() === InterpolateSmooth, - - lastIndex = times.length - 1; - - let writeIndex = 1; - - for ( let i = 1; i < lastIndex; ++ i ) { - - let keep = false; - - const time = times[ i ]; - const timeNext = times[ i + 1 ]; - - // remove adjacent keyframes scheduled at the same time - - if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { - - if ( ! smoothInterpolation ) { - - // remove unnecessary keyframes same as their neighbors - - const offset = i * stride, - offsetP = offset - stride, - offsetN = offset + stride; - - for ( let j = 0; j !== stride; ++ j ) { - - const value = values[ offset + j ]; - - if ( value !== values[ offsetP + j ] || - value !== values[ offsetN + j ] ) { - - keep = true; - break; - - } - - } - - } else { - - keep = true; - - } - - } - - // in-place compaction - - if ( keep ) { - - if ( i !== writeIndex ) { - - times[ writeIndex ] = times[ i ]; - - const readOffset = i * stride, - writeOffset = writeIndex * stride; - - for ( let j = 0; j !== stride; ++ j ) { - - values[ writeOffset + j ] = values[ readOffset + j ]; - - } - - } - - ++ writeIndex; - - } - - } - - // flush last keyframe (compaction looks ahead) - - if ( lastIndex > 0 ) { - - times[ writeIndex ] = times[ lastIndex ]; - - for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { - - values[ writeOffset + j ] = values[ readOffset + j ]; - - } - - ++ writeIndex; - - } - - if ( writeIndex !== times.length ) { - - this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); - this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); - - } else { - - this.times = times; - this.values = values; - - } - - return this; - - }, - - clone: function () { - - const times = AnimationUtils.arraySlice( this.times, 0 ); - const values = AnimationUtils.arraySlice( this.values, 0 ); - - const TypedKeyframeTrack = this.constructor; - const track = new TypedKeyframeTrack( this.name, times, values ); - - // Interpolant argument to constructor is not saved, so copy the factory method directly. - track.createInterpolant = this.createInterpolant; - - return track; - - } - - } ); - - /** - * A Track of Boolean keyframe values. - */ - - function BooleanKeyframeTrack( name, times, values ) { - - KeyframeTrack.call( this, name, times, values ); - - } - - BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: BooleanKeyframeTrack, - - ValueTypeName: 'bool', - ValueBufferType: Array, - - DefaultInterpolation: InterpolateDiscrete, - - InterpolantFactoryMethodLinear: undefined, - InterpolantFactoryMethodSmooth: undefined - - // Note: Actually this track could have a optimized / compressed - // representation of a single value and a custom interpolant that - // computes "firstValue ^ isOdd( index )". - - } ); - - /** - * A Track of keyframe values that represent color. - */ - - function ColorKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: ColorKeyframeTrack, - - ValueTypeName: 'color' - - // ValueBufferType is inherited - - // DefaultInterpolation is inherited - - // Note: Very basic implementation and nothing special yet. - // However, this is the place for color space parameterization. - - } ); - - /** - * A Track of numeric keyframe values. - */ - - function NumberKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: NumberKeyframeTrack, - - ValueTypeName: 'number' - - // ValueBufferType is inherited - - // DefaultInterpolation is inherited - - } ); - - /** - * Spherical linear unit quaternion interpolant. - */ - - function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: QuaternionLinearInterpolant, - - interpolate_: function ( i1, t0, t, t1 ) { - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - alpha = ( t - t0 ) / ( t1 - t0 ); - - let offset = i1 * stride; - - for ( let end = offset + stride; offset !== end; offset += 4 ) { - - Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); - - } - - return result; - - } - - } ); - - /** - * A Track of quaternion keyframe values. - */ - - function QuaternionKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: QuaternionKeyframeTrack, - - ValueTypeName: 'quaternion', - - // ValueBufferType is inherited - - DefaultInterpolation: InterpolateLinear, - - InterpolantFactoryMethodLinear: function ( result ) { - - return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); - - }, - - InterpolantFactoryMethodSmooth: undefined // not yet implemented - - } ); - - /** - * A Track that interpolates Strings - */ - - function StringKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: StringKeyframeTrack, - - ValueTypeName: 'string', - ValueBufferType: Array, - - DefaultInterpolation: InterpolateDiscrete, - - InterpolantFactoryMethodLinear: undefined, - - InterpolantFactoryMethodSmooth: undefined - - } ); - - /** - * A Track of vectored keyframe values. - */ - - function VectorKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: VectorKeyframeTrack, - - ValueTypeName: 'vector' - - // ValueBufferType is inherited - - // DefaultInterpolation is inherited - - } ); - - function AnimationClip( name, duration, tracks, blendMode ) { - - this.name = name; - this.tracks = tracks; - this.duration = ( duration !== undefined ) ? duration : - 1; - this.blendMode = ( blendMode !== undefined ) ? blendMode : NormalAnimationBlendMode; - - this.uuid = MathUtils.generateUUID(); - - // this means it should figure out its duration by scanning the tracks - if ( this.duration < 0 ) { - - this.resetDuration(); - - } - - } - - function getTrackTypeForValueTypeName( typeName ) { - - switch ( typeName.toLowerCase() ) { - - case 'scalar': - case 'double': - case 'float': - case 'number': - case 'integer': - - return NumberKeyframeTrack; - - case 'vector': - case 'vector2': - case 'vector3': - case 'vector4': - - return VectorKeyframeTrack; - - case 'color': - - return ColorKeyframeTrack; - - case 'quaternion': - - return QuaternionKeyframeTrack; - - case 'bool': - case 'boolean': - - return BooleanKeyframeTrack; - - case 'string': - - return StringKeyframeTrack; - - } - - throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); - - } - - function parseKeyframeTrack( json ) { - - if ( json.type === undefined ) { - - throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); - - } - - const trackType = getTrackTypeForValueTypeName( json.type ); - - if ( json.times === undefined ) { - - const times = [], values = []; - - AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); - - json.times = times; - json.values = values; - - } - - // derived classes can define a static parse method - if ( trackType.parse !== undefined ) { - - return trackType.parse( json ); - - } else { - - // by default, we assume a constructor compatible with the base - return new trackType( json.name, json.times, json.values, json.interpolation ); - - } - - } - - Object.assign( AnimationClip, { - - parse: function ( json ) { - - const tracks = [], - jsonTracks = json.tracks, - frameTime = 1.0 / ( json.fps || 1.0 ); - - for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { - - tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); - - } - - return new AnimationClip( json.name, json.duration, tracks, json.blendMode ); - - }, - - toJSON: function ( clip ) { - - const tracks = [], - clipTracks = clip.tracks; - - const json = { - - 'name': clip.name, - 'duration': clip.duration, - 'tracks': tracks, - 'uuid': clip.uuid, - 'blendMode': clip.blendMode - - }; - - for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { - - tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); - - } - - return json; - - }, - - CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) { - - const numMorphTargets = morphTargetSequence.length; - const tracks = []; - - for ( let i = 0; i < numMorphTargets; i ++ ) { - - let times = []; - let values = []; - - times.push( - ( i + numMorphTargets - 1 ) % numMorphTargets, - i, - ( i + 1 ) % numMorphTargets ); - - values.push( 0, 1, 0 ); - - const order = AnimationUtils.getKeyframeOrder( times ); - times = AnimationUtils.sortedArray( times, 1, order ); - values = AnimationUtils.sortedArray( values, 1, order ); - - // if there is a key at the first frame, duplicate it as the - // last frame as well for perfect loop. - if ( ! noLoop && times[ 0 ] === 0 ) { - - times.push( numMorphTargets ); - values.push( values[ 0 ] ); - - } - - tracks.push( - new NumberKeyframeTrack( - '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', - times, values - ).scale( 1.0 / fps ) ); - - } - - return new AnimationClip( name, - 1, tracks ); - - }, - - findByName: function ( objectOrClipArray, name ) { - - let clipArray = objectOrClipArray; - - if ( ! Array.isArray( objectOrClipArray ) ) { - - const o = objectOrClipArray; - clipArray = o.geometry && o.geometry.animations || o.animations; - - } - - for ( let i = 0; i < clipArray.length; i ++ ) { - - if ( clipArray[ i ].name === name ) { - - return clipArray[ i ]; - - } - - } - - return null; - - }, - - CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) { - - const animationToMorphTargets = {}; - - // tested with https://regex101.com/ on trick sequences - // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 - const pattern = /^([\w-]*?)([\d]+)$/; - - // sort morph target names into animation groups based - // patterns like Walk_001, Walk_002, Run_001, Run_002 - for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { - - const morphTarget = morphTargets[ i ]; - const parts = morphTarget.name.match( pattern ); - - if ( parts && parts.length > 1 ) { - - const name = parts[ 1 ]; - - let animationMorphTargets = animationToMorphTargets[ name ]; - - if ( ! animationMorphTargets ) { - - animationToMorphTargets[ name ] = animationMorphTargets = []; - - } - - animationMorphTargets.push( morphTarget ); - - } - - } - - const clips = []; - - for ( const name in animationToMorphTargets ) { - - clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); - - } - - return clips; - - }, - - // parse the animation.hierarchy format - parseAnimation: function ( animation, bones ) { - - if ( ! animation ) { - - console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); - return null; - - } - - const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { - - // only return track if there are actually keys. - if ( animationKeys.length !== 0 ) { - - const times = []; - const values = []; - - AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); - - // empty keys are filtered out, so check again - if ( times.length !== 0 ) { - - destTracks.push( new trackType( trackName, times, values ) ); - - } - - } - - }; - - const tracks = []; - - const clipName = animation.name || 'default'; - const fps = animation.fps || 30; - const blendMode = animation.blendMode; - - // automatic length determination in AnimationClip. - let duration = animation.length || - 1; - - const hierarchyTracks = animation.hierarchy || []; - - for ( let h = 0; h < hierarchyTracks.length; h ++ ) { - - const animationKeys = hierarchyTracks[ h ].keys; - - // skip empty tracks - if ( ! animationKeys || animationKeys.length === 0 ) continue; - - // process morph targets - if ( animationKeys[ 0 ].morphTargets ) { - - // figure out all morph targets used in this track - const morphTargetNames = {}; - - let k; - - for ( k = 0; k < animationKeys.length; k ++ ) { - - if ( animationKeys[ k ].morphTargets ) { - - for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { - - morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; - - } - - } - - } - - // create a track for each morph target with all zero - // morphTargetInfluences except for the keys in which - // the morphTarget is named. - for ( const morphTargetName in morphTargetNames ) { - - const times = []; - const values = []; - - for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { - - const animationKey = animationKeys[ k ]; - - times.push( animationKey.time ); - values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); - - } - - tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); - - } - - duration = morphTargetNames.length * ( fps || 1.0 ); - - } else { - - // ...assume skeletal animation - - const boneName = '.bones[' + bones[ h ].name + ']'; - - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.position', - animationKeys, 'pos', tracks ); - - addNonemptyTrack( - QuaternionKeyframeTrack, boneName + '.quaternion', - animationKeys, 'rot', tracks ); - - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.scale', - animationKeys, 'scl', tracks ); - - } - - } - - if ( tracks.length === 0 ) { - - return null; - - } - - const clip = new AnimationClip( clipName, duration, tracks, blendMode ); - - return clip; - - } - - } ); - - Object.assign( AnimationClip.prototype, { - - resetDuration: function () { - - const tracks = this.tracks; - let duration = 0; - - for ( let i = 0, n = tracks.length; i !== n; ++ i ) { - - const track = this.tracks[ i ]; - - duration = Math.max( duration, track.times[ track.times.length - 1 ] ); - - } - - this.duration = duration; - - return this; - - }, - - trim: function () { - - for ( let i = 0; i < this.tracks.length; i ++ ) { - - this.tracks[ i ].trim( 0, this.duration ); - - } - - return this; - - }, - - validate: function () { - - let valid = true; - - for ( let i = 0; i < this.tracks.length; i ++ ) { - - valid = valid && this.tracks[ i ].validate(); - - } - - return valid; - - }, - - optimize: function () { - - for ( let i = 0; i < this.tracks.length; i ++ ) { - - this.tracks[ i ].optimize(); - - } - - return this; - - }, - - clone: function () { - - const tracks = []; - - for ( let i = 0; i < this.tracks.length; i ++ ) { - - tracks.push( this.tracks[ i ].clone() ); - - } - - return new AnimationClip( this.name, this.duration, tracks, this.blendMode ); - - } - - } ); - - const Cache = { - - enabled: false, - - files: {}, - - add: function ( key, file ) { - - if ( this.enabled === false ) return; - - // console.log( 'THREE.Cache', 'Adding key:', key ); - - this.files[ key ] = file; - - }, - - get: function ( key ) { - - if ( this.enabled === false ) return; - - // console.log( 'THREE.Cache', 'Checking key:', key ); - - return this.files[ key ]; - - }, - - remove: function ( key ) { - - delete this.files[ key ]; - - }, - - clear: function () { - - this.files = {}; - - } - - }; - - function LoadingManager( onLoad, onProgress, onError ) { - - const scope = this; - - let isLoading = false; - let itemsLoaded = 0; - let itemsTotal = 0; - let urlModifier = undefined; - const handlers = []; - - // Refer to #5689 for the reason why we don't set .onStart - // in the constructor - - this.onStart = undefined; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; - - this.itemStart = function ( url ) { - - itemsTotal ++; - - if ( isLoading === false ) { - - if ( scope.onStart !== undefined ) { - - scope.onStart( url, itemsLoaded, itemsTotal ); - - } - - } - - isLoading = true; - - }; - - this.itemEnd = function ( url ) { - - itemsLoaded ++; - - if ( scope.onProgress !== undefined ) { - - scope.onProgress( url, itemsLoaded, itemsTotal ); - - } - - if ( itemsLoaded === itemsTotal ) { - - isLoading = false; - - if ( scope.onLoad !== undefined ) { - - scope.onLoad(); - - } - - } - - }; - - this.itemError = function ( url ) { - - if ( scope.onError !== undefined ) { - - scope.onError( url ); - - } - - }; - - this.resolveURL = function ( url ) { - - if ( urlModifier ) { - - return urlModifier( url ); - - } - - return url; - - }; - - this.setURLModifier = function ( transform ) { - - urlModifier = transform; - - return this; - - }; - - this.addHandler = function ( regex, loader ) { - - handlers.push( regex, loader ); - - return this; - - }; - - this.removeHandler = function ( regex ) { - - const index = handlers.indexOf( regex ); - - if ( index !== - 1 ) { - - handlers.splice( index, 2 ); - - } - - return this; - - }; - - this.getHandler = function ( file ) { - - for ( let i = 0, l = handlers.length; i < l; i += 2 ) { - - const regex = handlers[ i ]; - const loader = handlers[ i + 1 ]; - - if ( regex.global ) regex.lastIndex = 0; // see #17920 - - if ( regex.test( file ) ) { - - return loader; - - } - - } - - return null; - - }; - - } - - const DefaultLoadingManager = new LoadingManager(); - - function Loader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - this.crossOrigin = 'anonymous'; - this.path = ''; - this.resourcePath = ''; - this.requestHeader = {}; - - } - - Object.assign( Loader.prototype, { - - load: function ( /* url, onLoad, onProgress, onError */ ) {}, - - loadAsync: function ( url, onProgress ) { - - const scope = this; - - return new Promise( function ( resolve, reject ) { - - scope.load( url, resolve, onProgress, reject ); - - } ); - - }, - - parse: function ( /* data */ ) {}, - - setCrossOrigin: function ( crossOrigin ) { - - this.crossOrigin = crossOrigin; - return this; - - }, - - setPath: function ( path ) { - - this.path = path; - return this; - - }, - - setResourcePath: function ( resourcePath ) { - - this.resourcePath = resourcePath; - return this; - - }, - - setRequestHeader: function ( requestHeader ) { - - this.requestHeader = requestHeader; - return this; - - } - - } ); - - const loading = {}; - - function FileLoader( manager ) { - - Loader.call( this, manager ); - - } - - FileLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: FileLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - if ( url === undefined ) url = ''; - - if ( this.path !== undefined ) url = this.path + url; - - url = this.manager.resolveURL( url ); - - const scope = this; - - const cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) onLoad( cached ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - // Check if request is duplicate - - if ( loading[ url ] !== undefined ) { - - loading[ url ].push( { - - onLoad: onLoad, - onProgress: onProgress, - onError: onError - - } ); - - return; - - } - - // Check for data: URI - const dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; - const dataUriRegexResult = url.match( dataUriRegex ); - let request; - - // Safari can not handle Data URIs through XMLHttpRequest so process manually - if ( dataUriRegexResult ) { - - const mimeType = dataUriRegexResult[ 1 ]; - const isBase64 = !! dataUriRegexResult[ 2 ]; - - let data = dataUriRegexResult[ 3 ]; - data = decodeURIComponent( data ); - - if ( isBase64 ) data = atob( data ); - - try { - - let response; - const responseType = ( this.responseType || '' ).toLowerCase(); - - switch ( responseType ) { - - case 'arraybuffer': - case 'blob': - - const view = new Uint8Array( data.length ); - - for ( let i = 0; i < data.length; i ++ ) { - - view[ i ] = data.charCodeAt( i ); - - } - - if ( responseType === 'blob' ) { - - response = new Blob( [ view.buffer ], { type: mimeType } ); - - } else { - - response = view.buffer; - - } - - break; - - case 'document': - - const parser = new DOMParser(); - response = parser.parseFromString( data, mimeType ); - - break; - - case 'json': - - response = JSON.parse( data ); - - break; - - default: // 'text' or other - - response = data; - - break; - - } - - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { - - if ( onLoad ) onLoad( response ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - } catch ( error ) { - - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { - - if ( onError ) onError( error ); - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, 0 ); - - } - - } else { - - // Initialise array for duplicate requests - - loading[ url ] = []; - - loading[ url ].push( { - - onLoad: onLoad, - onProgress: onProgress, - onError: onError - - } ); - - request = new XMLHttpRequest(); - - request.open( 'GET', url, true ); - - request.addEventListener( 'load', function ( event ) { - - const response = this.response; - - const callbacks = loading[ url ]; - - delete loading[ url ]; - - if ( this.status === 200 || this.status === 0 ) { - - // Some browsers return HTTP Status 0 when using non-http protocol - // e.g. 'file://' or 'data://'. Handle as success. - - if ( this.status === 0 ) console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); - - // Add to cache only on HTTP success, so that we do not cache - // error response bodies as proper responses to requests. - Cache.add( url, response ); - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onLoad ) callback.onLoad( response ); - - } - - scope.manager.itemEnd( url ); - - } else { - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } - - }, false ); - - request.addEventListener( 'progress', function ( event ) { - - const callbacks = loading[ url ]; - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onProgress ) callback.onProgress( event ); - - } - - }, false ); - - request.addEventListener( 'error', function ( event ) { - - const callbacks = loading[ url ]; - - delete loading[ url ]; - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, false ); - - request.addEventListener( 'abort', function ( event ) { - - const callbacks = loading[ url ]; - - delete loading[ url ]; - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, false ); - - if ( this.responseType !== undefined ) request.responseType = this.responseType; - if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; - - if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); - - for ( const header in this.requestHeader ) { - - request.setRequestHeader( header, this.requestHeader[ header ] ); - - } - - request.send( null ); - - } - - scope.manager.itemStart( url ); - - return request; - - }, - - setResponseType: function ( value ) { - - this.responseType = value; - return this; - - }, - - setWithCredentials: function ( value ) { - - this.withCredentials = value; - return this; - - }, - - setMimeType: function ( value ) { - - this.mimeType = value; - return this; - - } - - } ); - - function AnimationLoader( manager ) { - - Loader.call( this, manager ); - - } - - AnimationLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: AnimationLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.load( url, function ( text ) { - - try { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - const animations = []; - - for ( let i = 0; i < json.length; i ++ ) { - - const clip = AnimationClip.parse( json[ i ] ); - - animations.push( clip ); - - } - - return animations; - - } - - } ); - - /** - * Abstract Base class to block based textures loader (dds, pvr, ...) - * - * Sub classes have to implement the parse() method which will be used in load(). - */ - - function CompressedTextureLoader( manager ) { - - Loader.call( this, manager ); - - } - - CompressedTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: CompressedTextureLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const images = []; - - const texture = new CompressedTexture(); - texture.image = images; - - const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - - let loaded = 0; - - function loadTexture( i ) { - - loader.load( url[ i ], function ( buffer ) { - - const texDatas = scope.parse( buffer, true ); - - images[ i ] = { - width: texDatas.width, - height: texDatas.height, - format: texDatas.format, - mipmaps: texDatas.mipmaps - }; - - loaded += 1; - - if ( loaded === 6 ) { - - if ( texDatas.mipmapCount === 1 ) - texture.minFilter = LinearFilter; - - texture.format = texDatas.format; - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - } - - }, onProgress, onError ); - - } - - if ( Array.isArray( url ) ) { - - for ( let i = 0, il = url.length; i < il; ++ i ) { - - loadTexture( i ); - - } - - } else { - - // compressed cubemap texture stored in a single DDS file - - loader.load( url, function ( buffer ) { - - const texDatas = scope.parse( buffer, true ); - - if ( texDatas.isCubemap ) { - - const faces = texDatas.mipmaps.length / texDatas.mipmapCount; - - for ( let f = 0; f < faces; f ++ ) { - - images[ f ] = { mipmaps: [] }; - - for ( let i = 0; i < texDatas.mipmapCount; i ++ ) { - - images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); - images[ f ].format = texDatas.format; - images[ f ].width = texDatas.width; - images[ f ].height = texDatas.height; - - } - - } - - } else { - - texture.image.width = texDatas.width; - texture.image.height = texDatas.height; - texture.mipmaps = texDatas.mipmaps; - - } - - if ( texDatas.mipmapCount === 1 ) { - - texture.minFilter = LinearFilter; - - } - - texture.format = texDatas.format; - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - }, onProgress, onError ); - - } - - return texture; - - } - - } ); - - function ImageLoader( manager ) { - - Loader.call( this, manager ); - - } - - ImageLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: ImageLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - if ( this.path !== undefined ) url = this.path + url; - - url = this.manager.resolveURL( url ); - - const scope = this; - - const cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) onLoad( cached ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - const image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); - - function onImageLoad() { - - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); - - Cache.add( url, this ); - - if ( onLoad ) onLoad( this ); - - scope.manager.itemEnd( url ); - - } - - function onImageError( event ) { - - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); - - if ( onError ) onError( event ); - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } - - image.addEventListener( 'load', onImageLoad, false ); - image.addEventListener( 'error', onImageError, false ); - - if ( url.substr( 0, 5 ) !== 'data:' ) { - - if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; - - } - - scope.manager.itemStart( url ); - - image.src = url; - - return image; - - } - - } ); - - function CubeTextureLoader( manager ) { - - Loader.call( this, manager ); - - } - - CubeTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: CubeTextureLoader, - - load: function ( urls, onLoad, onProgress, onError ) { - - const texture = new CubeTexture(); - - const loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); - - let loaded = 0; - - function loadTexture( i ) { - - loader.load( urls[ i ], function ( image ) { - - texture.images[ i ] = image; - - loaded ++; - - if ( loaded === 6 ) { - - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - } - - }, undefined, onError ); - - } - - for ( let i = 0; i < urls.length; ++ i ) { - - loadTexture( i ); - - } - - return texture; - - } - - } ); - - /** - * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) - * - * Sub classes have to implement the parse() method which will be used in load(). - */ - - function DataTextureLoader( manager ) { - - Loader.call( this, manager ); - - } - - DataTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: DataTextureLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const texture = new DataTexture(); - - const loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setPath( this.path ); - loader.load( url, function ( buffer ) { - - const texData = scope.parse( buffer ); - - if ( ! texData ) return; - - if ( texData.image !== undefined ) { - - texture.image = texData.image; - - } else if ( texData.data !== undefined ) { - - texture.image.width = texData.width; - texture.image.height = texData.height; - texture.image.data = texData.data; - - } - - texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; - texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; - - texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; - texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; - - texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; - - if ( texData.format !== undefined ) { - - texture.format = texData.format; - - } - - if ( texData.type !== undefined ) { - - texture.type = texData.type; - - } - - if ( texData.mipmaps !== undefined ) { - - texture.mipmaps = texData.mipmaps; - texture.minFilter = LinearMipmapLinearFilter; // presumably... - - } - - if ( texData.mipmapCount === 1 ) { - - texture.minFilter = LinearFilter; - - } - - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture, texData ); - - }, onProgress, onError ); - - - return texture; - - } - - } ); - - function TextureLoader( manager ) { - - Loader.call( this, manager ); - - } - - TextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: TextureLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const texture = new Texture(); - - const loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); - - loader.load( url, function ( image ) { - - texture.image = image; - - // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. - const isJPEG = url.search( /\.jpe?g($|\?)/i ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; - - texture.format = isJPEG ? RGBFormat : RGBAFormat; - texture.needsUpdate = true; - - if ( onLoad !== undefined ) { - - onLoad( texture ); - - } - - }, onProgress, onError ); - - return texture; - - } - - } ); - - /** - * Extensible curve object. - * - * Some common of curve methods: - * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) - * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) - * .getPoints(), .getSpacedPoints() - * .getLength() - * .updateArcLengths() - * - * This following curves inherit from THREE.Curve: - * - * -- 2D curves -- - * THREE.ArcCurve - * THREE.CubicBezierCurve - * THREE.EllipseCurve - * THREE.LineCurve - * THREE.QuadraticBezierCurve - * THREE.SplineCurve - * - * -- 3D curves -- - * THREE.CatmullRomCurve3 - * THREE.CubicBezierCurve3 - * THREE.LineCurve3 - * THREE.QuadraticBezierCurve3 - * - * A series of curves can be represented as a THREE.CurvePath. - * - **/ - - function Curve() { - - this.type = 'Curve'; - - this.arcLengthDivisions = 200; - - } - - Object.assign( Curve.prototype, { - - // Virtual base class method to overwrite and implement in subclasses - // - t [0 .. 1] - - getPoint: function ( /* t, optionalTarget */ ) { - - console.warn( 'THREE.Curve: .getPoint() not implemented.' ); - return null; - - }, - - // Get point at relative position in curve according to arc length - // - u [0 .. 1] - - getPointAt: function ( u, optionalTarget ) { - - const t = this.getUtoTmapping( u ); - return this.getPoint( t, optionalTarget ); - - }, - - // Get sequence of points using getPoint( t ) - - getPoints: function ( divisions ) { - - if ( divisions === undefined ) divisions = 5; - - const points = []; - - for ( let d = 0; d <= divisions; d ++ ) { - - points.push( this.getPoint( d / divisions ) ); - - } - - return points; - - }, - - // Get sequence of points using getPointAt( u ) - - getSpacedPoints: function ( divisions ) { - - if ( divisions === undefined ) divisions = 5; - - const points = []; - - for ( let d = 0; d <= divisions; d ++ ) { - - points.push( this.getPointAt( d / divisions ) ); - - } - - return points; - - }, - - // Get total curve arc length - - getLength: function () { - - const lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; - - }, - - // Get list of cumulative segment lengths - - getLengths: function ( divisions ) { - - if ( divisions === undefined ) divisions = this.arcLengthDivisions; - - if ( this.cacheArcLengths && - ( this.cacheArcLengths.length === divisions + 1 ) && - ! this.needsUpdate ) { - - return this.cacheArcLengths; - - } - - this.needsUpdate = false; - - const cache = []; - let current, last = this.getPoint( 0 ); - let sum = 0; - - cache.push( 0 ); - - for ( let p = 1; p <= divisions; p ++ ) { - - current = this.getPoint( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; - - } - - this.cacheArcLengths = cache; - - return cache; // { sums: cache, sum: sum }; Sum is in the last element. - - }, - - updateArcLengths: function () { - - this.needsUpdate = true; - this.getLengths(); - - }, - - // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - - getUtoTmapping: function ( u, distance ) { - - const arcLengths = this.getLengths(); - - let i = 0; - const il = arcLengths.length; - - let targetArcLength; // The targeted u distance value to get - - if ( distance ) { - - targetArcLength = distance; - - } else { - - targetArcLength = u * arcLengths[ il - 1 ]; - - } - - // binary search for the index with largest value smaller than target u distance - - let low = 0, high = il - 1, comparison; - - while ( low <= high ) { - - i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats - - comparison = arcLengths[ i ] - targetArcLength; - - if ( comparison < 0 ) { - - low = i + 1; - - } else if ( comparison > 0 ) { - - high = i - 1; - - } else { - - high = i; - break; - - // DONE - - } - - } - - i = high; - - if ( arcLengths[ i ] === targetArcLength ) { - - return i / ( il - 1 ); - - } - - // we could get finer grain at lengths, or use simple interpolation between two points - - const lengthBefore = arcLengths[ i ]; - const lengthAfter = arcLengths[ i + 1 ]; - - const segmentLength = lengthAfter - lengthBefore; - - // determine where we are between the 'before' and 'after' points - - const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; - - // add that fractional amount to t - - const t = ( i + segmentFraction ) / ( il - 1 ); - - return t; - - }, - - // Returns a unit vector tangent at t - // In case any sub curve does not implement its tangent derivation, - // 2 points a small delta apart will be used to find its gradient - // which seems to give a reasonable approximation - - getTangent: function ( t, optionalTarget ) { - - const delta = 0.0001; - let t1 = t - delta; - let t2 = t + delta; - - // Capping in case of danger - - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; - - const pt1 = this.getPoint( t1 ); - const pt2 = this.getPoint( t2 ); - - const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); - - tangent.copy( pt2 ).sub( pt1 ).normalize(); - - return tangent; - - }, - - getTangentAt: function ( u, optionalTarget ) { - - const t = this.getUtoTmapping( u ); - return this.getTangent( t, optionalTarget ); - - }, - - computeFrenetFrames: function ( segments, closed ) { - - // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf - - const normal = new Vector3(); - - const tangents = []; - const normals = []; - const binormals = []; - - const vec = new Vector3(); - const mat = new Matrix4(); - - // compute the tangent vectors for each segment on the curve - - for ( let i = 0; i <= segments; i ++ ) { - - const u = i / segments; - - tangents[ i ] = this.getTangentAt( u, new Vector3() ); - tangents[ i ].normalize(); - - } - - // select an initial normal vector perpendicular to the first tangent vector, - // and in the direction of the minimum tangent xyz component - - normals[ 0 ] = new Vector3(); - binormals[ 0 ] = new Vector3(); - let min = Number.MAX_VALUE; - const tx = Math.abs( tangents[ 0 ].x ); - const ty = Math.abs( tangents[ 0 ].y ); - const tz = Math.abs( tangents[ 0 ].z ); - - if ( tx <= min ) { - - min = tx; - normal.set( 1, 0, 0 ); - - } - - if ( ty <= min ) { - - min = ty; - normal.set( 0, 1, 0 ); - - } - - if ( tz <= min ) { - - normal.set( 0, 0, 1 ); - - } - - vec.crossVectors( tangents[ 0 ], normal ).normalize(); - - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - - - // compute the slowly-varying normal and binormal vectors for each segment on the curve - - for ( let i = 1; i <= segments; i ++ ) { - - normals[ i ] = normals[ i - 1 ].clone(); - - binormals[ i ] = binormals[ i - 1 ].clone(); - - vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - - if ( vec.length() > Number.EPSILON ) { - - vec.normalize(); - - const theta = Math.acos( MathUtils.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); - - } - - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - - } - - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - - if ( closed === true ) { - - let theta = Math.acos( MathUtils.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); - theta /= segments; - - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { - - theta = - theta; - - } - - for ( let i = 1; i <= segments; i ++ ) { - - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - - } - - } - - return { - tangents: tangents, - normals: normals, - binormals: binormals - }; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.arcLengthDivisions = source.arcLengthDivisions; - - return this; - - }, - - toJSON: function () { - - const data = { - metadata: { - version: 4.5, - type: 'Curve', - generator: 'Curve.toJSON' - } - }; - - data.arcLengthDivisions = this.arcLengthDivisions; - data.type = this.type; - - return data; - - }, - - fromJSON: function ( json ) { - - this.arcLengthDivisions = json.arcLengthDivisions; - - return this; - - } - - } ); - - function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - Curve.call( this ); - - this.type = 'EllipseCurve'; - - this.aX = aX || 0; - this.aY = aY || 0; - - this.xRadius = xRadius || 1; - this.yRadius = yRadius || 1; - - this.aStartAngle = aStartAngle || 0; - this.aEndAngle = aEndAngle || 2 * Math.PI; - - this.aClockwise = aClockwise || false; - - this.aRotation = aRotation || 0; - - } - - EllipseCurve.prototype = Object.create( Curve.prototype ); - EllipseCurve.prototype.constructor = EllipseCurve; - - EllipseCurve.prototype.isEllipseCurve = true; - - EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector2(); - - const twoPi = Math.PI * 2; - let deltaAngle = this.aEndAngle - this.aStartAngle; - const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; - - // ensures that deltaAngle is 0 .. 2 PI - while ( deltaAngle < 0 ) deltaAngle += twoPi; - while ( deltaAngle > twoPi ) deltaAngle -= twoPi; - - if ( deltaAngle < Number.EPSILON ) { - - if ( samePoints ) { - - deltaAngle = 0; - - } else { - - deltaAngle = twoPi; - - } - - } - - if ( this.aClockwise === true && ! samePoints ) { - - if ( deltaAngle === twoPi ) { - - deltaAngle = - twoPi; - - } else { - - deltaAngle = deltaAngle - twoPi; - - } - - } - - const angle = this.aStartAngle + t * deltaAngle; - let x = this.aX + this.xRadius * Math.cos( angle ); - let y = this.aY + this.yRadius * Math.sin( angle ); - - if ( this.aRotation !== 0 ) { - - const cos = Math.cos( this.aRotation ); - const sin = Math.sin( this.aRotation ); - - const tx = x - this.aX; - const ty = y - this.aY; - - // Rotate the point about the center of the ellipse. - x = tx * cos - ty * sin + this.aX; - y = tx * sin + ty * cos + this.aY; - - } - - return point.set( x, y ); - - }; - - EllipseCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.aX = source.aX; - this.aY = source.aY; - - this.xRadius = source.xRadius; - this.yRadius = source.yRadius; - - this.aStartAngle = source.aStartAngle; - this.aEndAngle = source.aEndAngle; - - this.aClockwise = source.aClockwise; - - this.aRotation = source.aRotation; - - return this; - - }; - - - EllipseCurve.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.aX = this.aX; - data.aY = this.aY; - - data.xRadius = this.xRadius; - data.yRadius = this.yRadius; - - data.aStartAngle = this.aStartAngle; - data.aEndAngle = this.aEndAngle; - - data.aClockwise = this.aClockwise; - - data.aRotation = this.aRotation; - - return data; - - }; - - EllipseCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.aX = json.aX; - this.aY = json.aY; - - this.xRadius = json.xRadius; - this.yRadius = json.yRadius; - - this.aStartAngle = json.aStartAngle; - this.aEndAngle = json.aEndAngle; - - this.aClockwise = json.aClockwise; - - this.aRotation = json.aRotation; - - return this; - - }; - - function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - - this.type = 'ArcCurve'; - - } - - ArcCurve.prototype = Object.create( EllipseCurve.prototype ); - ArcCurve.prototype.constructor = ArcCurve; - - ArcCurve.prototype.isArcCurve = true; - - /** - * Centripetal CatmullRom Curve - which is useful for avoiding - * cusps and self-intersections in non-uniform catmull rom curves. - * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf - * - * curve.type accepts centripetal(default), chordal and catmullrom - * curve.tension is used for catmullrom which defaults to 0.5 - */ - - - /* - Based on an optimized c++ solution in - - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - - http://ideone.com/NoEbVM - - This CubicPoly class could be used for reusing some variables and calculations, - but for three.js curve use, it could be possible inlined and flatten into a single function call - which can be placed in CurveUtils. - */ - - function CubicPoly() { - - let c0 = 0, c1 = 0, c2 = 0, c3 = 0; - - /* - * Compute coefficients for a cubic polynomial - * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 - * such that - * p(0) = x0, p(1) = x1 - * and - * p'(0) = t0, p'(1) = t1. - */ - function init( x0, x1, t0, t1 ) { - - c0 = x0; - c1 = t0; - c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; - c3 = 2 * x0 - 2 * x1 + t0 + t1; - - } - - return { - - initCatmullRom: function ( x0, x1, x2, x3, tension ) { - - init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); - - }, - - initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { - - // compute tangents when parameterized in [t1,t2] - let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; - let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; - - // rescale tangents for parametrization in [0,1] - t1 *= dt1; - t2 *= dt1; - - init( x1, x2, t1, t2 ); - - }, - - calc: function ( t ) { - - const t2 = t * t; - const t3 = t2 * t; - return c0 + c1 * t + c2 * t2 + c3 * t3; - - } - - }; - - } - - // - - const tmp = new Vector3(); - const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); - - function CatmullRomCurve3( points, closed, curveType, tension ) { - - Curve.call( this ); - - this.type = 'CatmullRomCurve3'; - - this.points = points || []; - this.closed = closed || false; - this.curveType = curveType || 'centripetal'; - this.tension = ( tension !== undefined ) ? tension : 0.5; - - } - - CatmullRomCurve3.prototype = Object.create( Curve.prototype ); - CatmullRomCurve3.prototype.constructor = CatmullRomCurve3; - - CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; - - CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector3(); - - const points = this.points; - const l = points.length; - - const p = ( l - ( this.closed ? 0 : 1 ) ) * t; - let intPoint = Math.floor( p ); - let weight = p - intPoint; - - if ( this.closed ) { - - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; - - } else if ( weight === 0 && intPoint === l - 1 ) { - - intPoint = l - 2; - weight = 1; - - } - - let p0, p3; // 4 points (p1 & p2 defined below) - - if ( this.closed || intPoint > 0 ) { - - p0 = points[ ( intPoint - 1 ) % l ]; - - } else { - - // extrapolate first point - tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); - p0 = tmp; - - } - - const p1 = points[ intPoint % l ]; - const p2 = points[ ( intPoint + 1 ) % l ]; - - if ( this.closed || intPoint + 2 < l ) { - - p3 = points[ ( intPoint + 2 ) % l ]; - - } else { - - // extrapolate last point - tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); - p3 = tmp; - - } - - if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { - - // init Centripetal / Chordal Catmull-Rom - const pow = this.curveType === 'chordal' ? 0.5 : 0.25; - let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); - let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); - let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); - - // safety check for repeated points - if ( dt1 < 1e-4 ) dt1 = 1.0; - if ( dt0 < 1e-4 ) dt0 = dt1; - if ( dt2 < 1e-4 ) dt2 = dt1; - - px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); - py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); - pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); - - } else if ( this.curveType === 'catmullrom' ) { - - px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); - py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); - pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); - - } - - point.set( - px.calc( weight ), - py.calc( weight ), - pz.calc( weight ) - ); - - return point; - - }; - - CatmullRomCurve3.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.points = []; - - for ( let i = 0, l = source.points.length; i < l; i ++ ) { - - const point = source.points[ i ]; - - this.points.push( point.clone() ); - - } - - this.closed = source.closed; - this.curveType = source.curveType; - this.tension = source.tension; - - return this; - - }; - - CatmullRomCurve3.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.points = []; - - for ( let i = 0, l = this.points.length; i < l; i ++ ) { - - const point = this.points[ i ]; - data.points.push( point.toArray() ); - - } - - data.closed = this.closed; - data.curveType = this.curveType; - data.tension = this.tension; - - return data; - - }; - - CatmullRomCurve3.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.points = []; - - for ( let i = 0, l = json.points.length; i < l; i ++ ) { - - const point = json.points[ i ]; - this.points.push( new Vector3().fromArray( point ) ); - - } - - this.closed = json.closed; - this.curveType = json.curveType; - this.tension = json.tension; - - return this; - - }; - - /** - * Bezier Curves formulas obtained from - * http://en.wikipedia.org/wiki/Bézier_curve - */ - - function CatmullRom( t, p0, p1, p2, p3 ) { - - const v0 = ( p2 - p0 ) * 0.5; - const v1 = ( p3 - p1 ) * 0.5; - const t2 = t * t; - const t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; - - } - - // - - function QuadraticBezierP0( t, p ) { - - const k = 1 - t; - return k * k * p; - - } - - function QuadraticBezierP1( t, p ) { - - return 2 * ( 1 - t ) * t * p; - - } - - function QuadraticBezierP2( t, p ) { - - return t * t * p; - - } - - function QuadraticBezier( t, p0, p1, p2 ) { - - return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + - QuadraticBezierP2( t, p2 ); - - } - - // - - function CubicBezierP0( t, p ) { - - const k = 1 - t; - return k * k * k * p; - - } - - function CubicBezierP1( t, p ) { - - const k = 1 - t; - return 3 * k * k * t * p; - - } - - function CubicBezierP2( t, p ) { - - return 3 * ( 1 - t ) * t * t * p; - - } - - function CubicBezierP3( t, p ) { - - return t * t * t * p; - - } - - function CubicBezier( t, p0, p1, p2, p3 ) { - - return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + - CubicBezierP3( t, p3 ); - - } - - function CubicBezierCurve( v0, v1, v2, v3 ) { - - Curve.call( this ); - - this.type = 'CubicBezierCurve'; - - this.v0 = v0 || new Vector2(); - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); - this.v3 = v3 || new Vector2(); - - } - - CubicBezierCurve.prototype = Object.create( Curve.prototype ); - CubicBezierCurve.prototype.constructor = CubicBezierCurve; - - CubicBezierCurve.prototype.isCubicBezierCurve = true; - - CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector2(); - - const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) - ); - - return point; - - }; - - CubicBezierCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); - - return this; - - }; - - CubicBezierCurve.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); - - return data; - - }; - - CubicBezierCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); - - return this; - - }; - - function CubicBezierCurve3( v0, v1, v2, v3 ) { - - Curve.call( this ); - - this.type = 'CubicBezierCurve3'; - - this.v0 = v0 || new Vector3(); - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); - this.v3 = v3 || new Vector3(); - - } - - CubicBezierCurve3.prototype = Object.create( Curve.prototype ); - CubicBezierCurve3.prototype.constructor = CubicBezierCurve3; - - CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; - - CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector3(); - - const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), - CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) - ); - - return point; - - }; - - CubicBezierCurve3.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); - - return this; - - }; - - CubicBezierCurve3.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); - - return data; - - }; - - CubicBezierCurve3.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); - - return this; - - }; - - function LineCurve( v1, v2 ) { - - Curve.call( this ); - - this.type = 'LineCurve'; - - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); - - } - - LineCurve.prototype = Object.create( Curve.prototype ); - LineCurve.prototype.constructor = LineCurve; - - LineCurve.prototype.isLineCurve = true; - - LineCurve.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector2(); - - if ( t === 1 ) { - - point.copy( this.v2 ); - - } else { - - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); - - } - - return point; - - }; - - // Line curve is linear, so we can overwrite default getPointAt - - LineCurve.prototype.getPointAt = function ( u, optionalTarget ) { - - return this.getPoint( u, optionalTarget ); - - }; - - LineCurve.prototype.getTangent = function ( t, optionalTarget ) { - - const tangent = optionalTarget || new Vector2(); - - tangent.copy( this.v2 ).sub( this.v1 ).normalize(); - - return tangent; - - }; - - LineCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - }; - - LineCurve.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - }; - - LineCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - }; - - function LineCurve3( v1, v2 ) { - - Curve.call( this ); - - this.type = 'LineCurve3'; - - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); - - } - - LineCurve3.prototype = Object.create( Curve.prototype ); - LineCurve3.prototype.constructor = LineCurve3; - - LineCurve3.prototype.isLineCurve3 = true; - - LineCurve3.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector3(); - - if ( t === 1 ) { - - point.copy( this.v2 ); - - } else { - - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); - - } - - return point; - - }; - - // Line curve is linear, so we can overwrite default getPointAt - - LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) { - - return this.getPoint( u, optionalTarget ); - - }; - - LineCurve3.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - }; - - LineCurve3.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - }; - - LineCurve3.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - }; - - function QuadraticBezierCurve( v0, v1, v2 ) { - - Curve.call( this ); - - this.type = 'QuadraticBezierCurve'; - - this.v0 = v0 || new Vector2(); - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); - - } - - QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; - - QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; - - QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector2(); - - const v0 = this.v0, v1 = this.v1, v2 = this.v2; - - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ) - ); - - return point; - - }; - - QuadraticBezierCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - }; - - QuadraticBezierCurve.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - }; - - QuadraticBezierCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - }; - - function QuadraticBezierCurve3( v0, v1, v2 ) { - - Curve.call( this ); - - this.type = 'QuadraticBezierCurve3'; - - this.v0 = v0 || new Vector3(); - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); - - } - - QuadraticBezierCurve3.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3; - - QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; - - QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector3(); - - const v0 = this.v0, v1 = this.v1, v2 = this.v2; - - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ), - QuadraticBezier( t, v0.z, v1.z, v2.z ) - ); - - return point; - - }; - - QuadraticBezierCurve3.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - }; - - QuadraticBezierCurve3.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - }; - - QuadraticBezierCurve3.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - }; - - function SplineCurve( points ) { - - Curve.call( this ); - - this.type = 'SplineCurve'; - - this.points = points || []; - - } - - SplineCurve.prototype = Object.create( Curve.prototype ); - SplineCurve.prototype.constructor = SplineCurve; - - SplineCurve.prototype.isSplineCurve = true; - - SplineCurve.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector2(); - - const points = this.points; - const p = ( points.length - 1 ) * t; - - const intPoint = Math.floor( p ); - const weight = p - intPoint; - - const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; - const p1 = points[ intPoint ]; - const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; - const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - - point.set( - CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), - CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) - ); - - return point; - - }; - - SplineCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.points = []; - - for ( let i = 0, l = source.points.length; i < l; i ++ ) { - - const point = source.points[ i ]; - - this.points.push( point.clone() ); - - } - - return this; - - }; - - SplineCurve.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.points = []; - - for ( let i = 0, l = this.points.length; i < l; i ++ ) { - - const point = this.points[ i ]; - data.points.push( point.toArray() ); - - } - - return data; - - }; - - SplineCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.points = []; - - for ( let i = 0, l = json.points.length; i < l; i ++ ) { - - const point = json.points[ i ]; - this.points.push( new Vector2().fromArray( point ) ); - - } - - return this; - - }; - - var Curves = /*#__PURE__*/Object.freeze({ - __proto__: null, - ArcCurve: ArcCurve, - CatmullRomCurve3: CatmullRomCurve3, - CubicBezierCurve: CubicBezierCurve, - CubicBezierCurve3: CubicBezierCurve3, - EllipseCurve: EllipseCurve, - LineCurve: LineCurve, - LineCurve3: LineCurve3, - QuadraticBezierCurve: QuadraticBezierCurve, - QuadraticBezierCurve3: QuadraticBezierCurve3, - SplineCurve: SplineCurve - }); - - /************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ - - function CurvePath() { - - Curve.call( this ); - - this.type = 'CurvePath'; - - this.curves = []; - this.autoClose = false; // Automatically closes the path - - } - - CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { - - constructor: CurvePath, - - add: function ( curve ) { - - this.curves.push( curve ); - - }, - - closePath: function () { - - // Add a line curve if start and end of lines are not connected - const startPoint = this.curves[ 0 ].getPoint( 0 ); - const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); - - if ( ! startPoint.equals( endPoint ) ) { - - this.curves.push( new LineCurve( endPoint, startPoint ) ); - - } - - }, - - // To get accurate point with reference to - // entire path distance at time t, - // following has to be done: - - // 1. Length of each sub path have to be known - // 2. Locate and identify type of curve - // 3. Get t for the curve - // 4. Return curve.getPointAt(t') - - getPoint: function ( t ) { - - const d = t * this.getLength(); - const curveLengths = this.getCurveLengths(); - let i = 0; - - // To think about boundaries points. - - while ( i < curveLengths.length ) { - - if ( curveLengths[ i ] >= d ) { - - const diff = curveLengths[ i ] - d; - const curve = this.curves[ i ]; - - const segmentLength = curve.getLength(); - const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; - - return curve.getPointAt( u ); - - } - - i ++; - - } - - return null; - - // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { - - points.push( points[ 0 ] ); - - } - - return points; - - }, - - copy: function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.curves = []; - - for ( let i = 0, l = source.curves.length; i < l; i ++ ) { - - const curve = source.curves[ i ]; - - this.curves.push( curve.clone() ); - - } - - this.autoClose = source.autoClose; - - return this; - - }, - - toJSON: function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.autoClose = this.autoClose; - data.curves = []; - - for ( let i = 0, l = this.curves.length; i < l; i ++ ) { - - const curve = this.curves[ i ]; - data.curves.push( curve.toJSON() ); - - } - - return data; - - }, - - fromJSON: function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.autoClose = json.autoClose; - this.curves = []; - - for ( let i = 0, l = json.curves.length; i < l; i ++ ) { - - const curve = json.curves[ i ]; - this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); - - } - - return this; - - } - - } ); - - function Path( points ) { - - CurvePath.call( this ); - - this.type = 'Path'; - - this.currentPoint = new Vector2(); - - if ( points ) { - - this.setFromPoints( points ); - - } - - } - - Path.prototype = Object.assign( Object.create( CurvePath.prototype ), { - - constructor: Path, - - setFromPoints: function ( points ) { - - this.moveTo( points[ 0 ].x, points[ 0 ].y ); - - for ( let i = 1, l = points.length; i < l; i ++ ) { - - this.lineTo( points[ i ].x, points[ i ].y ); - - } - - return this; - - }, - - moveTo: function ( x, y ) { - - this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? - - return this; - - }, - - lineTo: function ( x, y ) { - - const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); - this.curves.push( curve ); - - this.currentPoint.set( x, y ); - - return this; - - }, - - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { - - const curve = new QuadraticBezierCurve( - this.currentPoint.clone(), - new Vector2( aCPx, aCPy ), - new Vector2( aX, aY ) - ); - - this.curves.push( curve ); - - this.currentPoint.set( aX, aY ); - - return this; - - }, - - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - - const curve = new CubicBezierCurve( - this.currentPoint.clone(), - new Vector2( aCP1x, aCP1y ), - new Vector2( aCP2x, aCP2y ), - new Vector2( aX, aY ) - ); - - this.curves.push( curve ); - - this.currentPoint.set( aX, aY ); - - return this; - - }, - - splineThru: function ( pts /*Array of Vector*/ ) { - - const npts = [ this.currentPoint.clone() ].concat( pts ); - - const curve = new SplineCurve( npts ); - this.curves.push( curve ); - - this.currentPoint.copy( pts[ pts.length - 1 ] ); - - return this; - - }, - - arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - const x0 = this.currentPoint.x; - const y0 = this.currentPoint.y; - - this.absarc( aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); - - return this; - - }, - - absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - - return this; - - }, - - ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - const x0 = this.currentPoint.x; - const y0 = this.currentPoint.y; - - this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - - return this; - - }, - - absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - - if ( this.curves.length > 0 ) { - - // if a previous curve is present, attempt to join - const firstPoint = curve.getPoint( 0 ); - - if ( ! firstPoint.equals( this.currentPoint ) ) { - - this.lineTo( firstPoint.x, firstPoint.y ); - - } - - } - - this.curves.push( curve ); - - const lastPoint = curve.getPoint( 1 ); - this.currentPoint.copy( lastPoint ); - - return this; - - }, - - copy: function ( source ) { - - CurvePath.prototype.copy.call( this, source ); - - this.currentPoint.copy( source.currentPoint ); - - return this; - - }, - - toJSON: function () { - - const data = CurvePath.prototype.toJSON.call( this ); - - data.currentPoint = this.currentPoint.toArray(); - - return data; - - }, - - fromJSON: function ( json ) { - - CurvePath.prototype.fromJSON.call( this, json ); - - this.currentPoint.fromArray( json.currentPoint ); - - return this; - - } - - } ); - - function Shape( points ) { - - Path.call( this, points ); - - this.uuid = MathUtils.generateUUID(); - - this.type = 'Shape'; - - this.holes = []; - - } - - Shape.prototype = Object.assign( Object.create( Path.prototype ), { - - constructor: Shape, - - getPointsHoles: function ( divisions ) { - - const holesPts = []; - - for ( let i = 0, l = this.holes.length; i < l; i ++ ) { - - holesPts[ i ] = this.holes[ i ].getPoints( divisions ); - - } - - return holesPts; - - }, - - // get points of shape and holes (keypoints based on segments parameter) - - extractPoints: function ( divisions ) { - - return { - - shape: this.getPoints( divisions ), - holes: this.getPointsHoles( divisions ) - - }; - - }, - - copy: function ( source ) { - - Path.prototype.copy.call( this, source ); - - this.holes = []; - - for ( let i = 0, l = source.holes.length; i < l; i ++ ) { - - const hole = source.holes[ i ]; - - this.holes.push( hole.clone() ); - - } - - return this; - - }, - - toJSON: function () { - - const data = Path.prototype.toJSON.call( this ); - - data.uuid = this.uuid; - data.holes = []; - - for ( let i = 0, l = this.holes.length; i < l; i ++ ) { - - const hole = this.holes[ i ]; - data.holes.push( hole.toJSON() ); - - } - - return data; - - }, - - fromJSON: function ( json ) { - - Path.prototype.fromJSON.call( this, json ); - - this.uuid = json.uuid; - this.holes = []; - - for ( let i = 0, l = json.holes.length; i < l; i ++ ) { - - const hole = json.holes[ i ]; - this.holes.push( new Path().fromJSON( hole ) ); - - } - - return this; - - } - - } ); - - function Light( color, intensity ) { - - Object3D.call( this ); - - this.type = 'Light'; - - this.color = new Color( color ); - this.intensity = intensity !== undefined ? intensity : 1; - - this.receiveShadow = undefined; - - } - - Light.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Light, - - isLight: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - this.intensity = source.intensity; - - return this; - - }, - - toJSON: function ( meta ) { - - const data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; - - if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); - - if ( this.distance !== undefined ) data.object.distance = this.distance; - if ( this.angle !== undefined ) data.object.angle = this.angle; - if ( this.decay !== undefined ) data.object.decay = this.decay; - if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; - - if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); - - return data; - - } - - } ); - - function HemisphereLight( skyColor, groundColor, intensity ) { - - Light.call( this, skyColor, intensity ); - - this.type = 'HemisphereLight'; - - this.castShadow = undefined; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.groundColor = new Color( groundColor ); - - } - - HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: HemisphereLight, - - isHemisphereLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.groundColor.copy( source.groundColor ); - - return this; - - } - - } ); - - function LightShadow( camera ) { - - this.camera = camera; - - this.bias = 0; - this.normalBias = 0; - this.radius = 1; - - this.mapSize = new Vector2( 512, 512 ); - - this.map = null; - this.mapPass = null; - this.matrix = new Matrix4(); - - this.autoUpdate = true; - this.needsUpdate = false; - - this._frustum = new Frustum(); - this._frameExtents = new Vector2( 1, 1 ); - - this._viewportCount = 1; - - this._viewports = [ - - new Vector4( 0, 0, 1, 1 ) - - ]; - - } - - Object.assign( LightShadow.prototype, { - - _projScreenMatrix: new Matrix4(), - - _lightPositionWorld: new Vector3(), - - _lookTarget: new Vector3(), - - getViewportCount: function () { - - return this._viewportCount; - - }, - - getFrustum: function () { - - return this._frustum; - - }, - - updateMatrices: function ( light ) { - - const shadowCamera = this.camera, - shadowMatrix = this.matrix, - projScreenMatrix = this._projScreenMatrix, - lookTarget = this._lookTarget, - lightPositionWorld = this._lightPositionWorld; - - lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - shadowCamera.position.copy( lightPositionWorld ); - - lookTarget.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( lookTarget ); - shadowCamera.updateMatrixWorld(); - - projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( projScreenMatrix ); - - shadowMatrix.set( - 0.5, 0.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0 - ); - - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); - - }, - - getViewport: function ( viewportIndex ) { - - return this._viewports[ viewportIndex ]; - - }, - - getFrameExtents: function () { - - return this._frameExtents; - - }, - - copy: function ( source ) { - - this.camera = source.camera.clone(); - - this.bias = source.bias; - this.radius = source.radius; - - this.mapSize.copy( source.mapSize ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - toJSON: function () { - - const object = {}; - - if ( this.bias !== 0 ) object.bias = this.bias; - if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; - if ( this.radius !== 1 ) object.radius = this.radius; - if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); - - object.camera = this.camera.toJSON( false ).object; - delete object.camera.matrix; - - return object; - - } - - } ); - - function SpotLightShadow() { - - LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); - - } - - SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - - constructor: SpotLightShadow, - - isSpotLightShadow: true, - - updateMatrices: function ( light ) { - - const camera = this.camera; - - const fov = MathUtils.RAD2DEG * 2 * light.angle; - const aspect = this.mapSize.width / this.mapSize.height; - const far = light.distance || camera.far; - - if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { - - camera.fov = fov; - camera.aspect = aspect; - camera.far = far; - camera.updateProjectionMatrix(); - - } - - LightShadow.prototype.updateMatrices.call( this, light ); - - } - - } ); - - function SpotLight( color, intensity, distance, angle, penumbra, decay ) { - - Light.call( this, color, intensity ); - - this.type = 'SpotLight'; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.target = new Object3D(); - - Object.defineProperty( this, 'power', { - get: function () { - - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * Math.PI; - - }, - set: function ( power ) { - - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / Math.PI; - - } - } ); - - this.distance = ( distance !== undefined ) ? distance : 0; - this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; - this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - - this.shadow = new SpotLightShadow(); - - } - - SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: SpotLight, - - isSpotLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.distance = source.distance; - this.angle = source.angle; - this.penumbra = source.penumbra; - this.decay = source.decay; - - this.target = source.target.clone(); - - this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - function PointLightShadow() { - - LightShadow.call( this, new PerspectiveCamera( 90, 1, 0.5, 500 ) ); - - this._frameExtents = new Vector2( 4, 2 ); - - this._viewportCount = 6; - - this._viewports = [ - // These viewports map a cube-map onto a 2D texture with the - // following orientation: - // - // xzXZ - // y Y - // - // X - Positive x direction - // x - Negative x direction - // Y - Positive y direction - // y - Negative y direction - // Z - Positive z direction - // z - Negative z direction - - // positive X - new Vector4( 2, 1, 1, 1 ), - // negative X - new Vector4( 0, 1, 1, 1 ), - // positive Z - new Vector4( 3, 1, 1, 1 ), - // negative Z - new Vector4( 1, 1, 1, 1 ), - // positive Y - new Vector4( 3, 0, 1, 1 ), - // negative Y - new Vector4( 1, 0, 1, 1 ) - ]; - - this._cubeDirections = [ - new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), - new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) - ]; - - this._cubeUps = [ - new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), - new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) - ]; - - } - - PointLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - - constructor: PointLightShadow, - - isPointLightShadow: true, - - updateMatrices: function ( light, viewportIndex ) { - - if ( viewportIndex === undefined ) viewportIndex = 0; - - const camera = this.camera, - shadowMatrix = this.matrix, - lightPositionWorld = this._lightPositionWorld, - lookTarget = this._lookTarget, - projScreenMatrix = this._projScreenMatrix; - - lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - camera.position.copy( lightPositionWorld ); - - lookTarget.copy( camera.position ); - lookTarget.add( this._cubeDirections[ viewportIndex ] ); - camera.up.copy( this._cubeUps[ viewportIndex ] ); - camera.lookAt( lookTarget ); - camera.updateMatrixWorld(); - - shadowMatrix.makeTranslation( - lightPositionWorld.x, - lightPositionWorld.y, - lightPositionWorld.z ); - - projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( projScreenMatrix ); - - } - - } ); - - function PointLight( color, intensity, distance, decay ) { - - Light.call( this, color, intensity ); - - this.type = 'PointLight'; - - Object.defineProperty( this, 'power', { - get: function () { - - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * 4 * Math.PI; - - }, - set: function ( power ) { - - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / ( 4 * Math.PI ); - - } - } ); - - this.distance = ( distance !== undefined ) ? distance : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - - this.shadow = new PointLightShadow(); - - } - - PointLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: PointLight, - - isPointLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.distance = source.distance; - this.decay = source.decay; - - this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - function OrthographicCamera( left, right, top, bottom, near, far ) { - - Camera.call( this ); - - this.type = 'OrthographicCamera'; - - this.zoom = 1; - this.view = null; - - this.left = ( left !== undefined ) ? left : - 1; - this.right = ( right !== undefined ) ? right : 1; - this.top = ( top !== undefined ) ? top : 1; - this.bottom = ( bottom !== undefined ) ? bottom : - 1; - - this.near = ( near !== undefined ) ? near : 0.1; - this.far = ( far !== undefined ) ? far : 2000; - - this.updateProjectionMatrix(); - - } - - OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { - - constructor: OrthographicCamera, - - isOrthographicCamera: true, - - copy: function ( source, recursive ) { - - Camera.prototype.copy.call( this, source, recursive ); - - this.left = source.left; - this.right = source.right; - this.top = source.top; - this.bottom = source.bottom; - this.near = source.near; - this.far = source.far; - - this.zoom = source.zoom; - this.view = source.view === null ? null : Object.assign( {}, source.view ); - - return this; - - }, - - setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { - - if ( this.view === null ) { - - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; - - } - - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; - - this.updateProjectionMatrix(); - - }, - - clearViewOffset: function () { - - if ( this.view !== null ) { - - this.view.enabled = false; - - } - - this.updateProjectionMatrix(); - - }, - - updateProjectionMatrix: function () { - - const dx = ( this.right - this.left ) / ( 2 * this.zoom ); - const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); - const cx = ( this.right + this.left ) / 2; - const cy = ( this.top + this.bottom ) / 2; - - let left = cx - dx; - let right = cx + dx; - let top = cy + dy; - let bottom = cy - dy; - - if ( this.view !== null && this.view.enabled ) { - - const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; - const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; - - left += scaleW * this.view.offsetX; - right = left + scaleW * this.view.width; - top -= scaleH * this.view.offsetY; - bottom = top - scaleH * this.view.height; - - } - - this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); - - this.projectionMatrixInverse.getInverse( this.projectionMatrix ); - - }, - - toJSON: function ( meta ) { - - const data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.zoom = this.zoom; - data.object.left = this.left; - data.object.right = this.right; - data.object.top = this.top; - data.object.bottom = this.bottom; - data.object.near = this.near; - data.object.far = this.far; - - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - - return data; - - } - - } ); - - function DirectionalLightShadow() { - - LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); - - } - - DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - - constructor: DirectionalLightShadow, - - isDirectionalLightShadow: true, - - updateMatrices: function ( light ) { - - LightShadow.prototype.updateMatrices.call( this, light ); - - } - - } ); - - function DirectionalLight( color, intensity ) { - - Light.call( this, color, intensity ); - - this.type = 'DirectionalLight'; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.target = new Object3D(); - - this.shadow = new DirectionalLightShadow(); - - } - - DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: DirectionalLight, - - isDirectionalLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.target = source.target.clone(); - - this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - function AmbientLight( color, intensity ) { - - Light.call( this, color, intensity ); - - this.type = 'AmbientLight'; - - this.castShadow = undefined; - - } - - AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: AmbientLight, - - isAmbientLight: true - - } ); - - function RectAreaLight( color, intensity, width, height ) { - - Light.call( this, color, intensity ); - - this.type = 'RectAreaLight'; - - this.width = ( width !== undefined ) ? width : 10; - this.height = ( height !== undefined ) ? height : 10; - - } - - RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: RectAreaLight, - - isRectAreaLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.width = source.width; - this.height = source.height; - - return this; - - }, - - toJSON: function ( meta ) { - - const data = Light.prototype.toJSON.call( this, meta ); - - data.object.width = this.width; - data.object.height = this.height; - - return data; - - } - - } ); - - /** - * Primary reference: - * https://graphics.stanford.edu/papers/envmap/envmap.pdf - * - * Secondary reference: - * https://www.ppsloan.org/publications/StupidSH36.pdf - */ - - // 3-band SH defined by 9 coefficients - - class SphericalHarmonics3 { - - constructor() { - - Object.defineProperty( this, 'isSphericalHarmonics3', { value: true } ); - - this.coefficients = []; - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients.push( new Vector3() ); - - } - - } - - set( coefficients ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].copy( coefficients[ i ] ); - - } - - return this; - - } - - zero() { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].set( 0, 0, 0 ); - - } - - return this; - - } - - // get the radiance in the direction of the normal - // target is a Vector3 - getAt( normal, target ) { - - // normal is assumed to be unit length - - const x = normal.x, y = normal.y, z = normal.z; - - const coeff = this.coefficients; - - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); - - // band 1 - target.addScaledVector( coeff[ 1 ], 0.488603 * y ); - target.addScaledVector( coeff[ 2 ], 0.488603 * z ); - target.addScaledVector( coeff[ 3 ], 0.488603 * x ); - - // band 2 - target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); - target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); - target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); - target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); - target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); - - return target; - - } - - // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal - // target is a Vector3 - // https://graphics.stanford.edu/papers/envmap/envmap.pdf - getIrradianceAt( normal, target ) { - - // normal is assumed to be unit length - - const x = normal.x, y = normal.y, z = normal.z; - - const coeff = this.coefficients; - - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 - - // band 1 - target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 - target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); - target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); - - // band 2 - target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 - target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); - target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 - target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); - target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 - - return target; - - } - - add( sh ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].add( sh.coefficients[ i ] ); - - } - - return this; - - } - - addScaledSH( sh, s ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); - - } - - return this; - - } - - scale( s ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].multiplyScalar( s ); - - } - - return this; - - } - - lerp( sh, alpha ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); - - } - - return this; - - } - - equals( sh ) { - - for ( let i = 0; i < 9; i ++ ) { - - if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { - - return false; - - } - - } - - return true; - - } - - copy( sh ) { - - return this.set( sh.coefficients ); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - const coefficients = this.coefficients; - - for ( let i = 0; i < 9; i ++ ) { - - coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); - - } - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - const coefficients = this.coefficients; - - for ( let i = 0; i < 9; i ++ ) { - - coefficients[ i ].toArray( array, offset + ( i * 3 ) ); - - } - - return array; - - } - - // evaluate the basis functions - // shBasis is an Array[ 9 ] - static getBasisAt( normal, shBasis ) { - - // normal is assumed to be unit length - - const x = normal.x, y = normal.y, z = normal.z; - - // band 0 - shBasis[ 0 ] = 0.282095; - - // band 1 - shBasis[ 1 ] = 0.488603 * y; - shBasis[ 2 ] = 0.488603 * z; - shBasis[ 3 ] = 0.488603 * x; - - // band 2 - shBasis[ 4 ] = 1.092548 * x * y; - shBasis[ 5 ] = 1.092548 * y * z; - shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); - shBasis[ 7 ] = 1.092548 * x * z; - shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); - - } - - } - - function LightProbe( sh, intensity ) { - - Light.call( this, undefined, intensity ); - - this.type = 'LightProbe'; - - this.sh = ( sh !== undefined ) ? sh : new SphericalHarmonics3(); - - } - - LightProbe.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: LightProbe, - - isLightProbe: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.sh.copy( source.sh ); - - return this; - - }, - - fromJSON: function ( json ) { - - this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); - this.sh.fromArray( json.sh ); - - return this; - - }, - - toJSON: function ( meta ) { - - const data = Light.prototype.toJSON.call( this, meta ); - - data.object.sh = this.sh.toArray(); - - return data; - - } - - } ); - - function MaterialLoader( manager ) { - - Loader.call( this, manager ); - - this.textures = {}; - - } - - MaterialLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: MaterialLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.load( url, function ( text ) { - - try { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - const textures = this.textures; - - function getTexture( name ) { - - if ( textures[ name ] === undefined ) { - - console.warn( 'THREE.MaterialLoader: Undefined texture', name ); - - } - - return textures[ name ]; - - } - - const material = new Materials[ json.type ](); - - if ( json.uuid !== undefined ) material.uuid = json.uuid; - if ( json.name !== undefined ) material.name = json.name; - if ( json.color !== undefined ) material.color.setHex( json.color ); - if ( json.roughness !== undefined ) material.roughness = json.roughness; - if ( json.metalness !== undefined ) material.metalness = json.metalness; - if ( json.sheen !== undefined ) material.sheen = new Color().setHex( json.sheen ); - if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); - if ( json.specular !== undefined ) material.specular.setHex( json.specular ); - if ( json.shininess !== undefined ) material.shininess = json.shininess; - if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat; - if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness; - if ( json.fog !== undefined ) material.fog = json.fog; - if ( json.flatShading !== undefined ) material.flatShading = json.flatShading; - if ( json.blending !== undefined ) material.blending = json.blending; - if ( json.combine !== undefined ) material.combine = json.combine; - if ( json.side !== undefined ) material.side = json.side; - if ( json.opacity !== undefined ) material.opacity = json.opacity; - if ( json.transparent !== undefined ) material.transparent = json.transparent; - if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; - if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; - if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; - if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; - - if ( json.stencilWrite !== undefined ) material.stencilWrite = json.stencilWrite; - if ( json.stencilWriteMask !== undefined ) material.stencilWriteMask = json.stencilWriteMask; - if ( json.stencilFunc !== undefined ) material.stencilFunc = json.stencilFunc; - if ( json.stencilRef !== undefined ) material.stencilRef = json.stencilRef; - if ( json.stencilFuncMask !== undefined ) material.stencilFuncMask = json.stencilFuncMask; - if ( json.stencilFail !== undefined ) material.stencilFail = json.stencilFail; - if ( json.stencilZFail !== undefined ) material.stencilZFail = json.stencilZFail; - if ( json.stencilZPass !== undefined ) material.stencilZPass = json.stencilZPass; - - if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; - if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; - if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; - if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; - - if ( json.rotation !== undefined ) material.rotation = json.rotation; - - if ( json.linewidth !== 1 ) material.linewidth = json.linewidth; - if ( json.dashSize !== undefined ) material.dashSize = json.dashSize; - if ( json.gapSize !== undefined ) material.gapSize = json.gapSize; - if ( json.scale !== undefined ) material.scale = json.scale; - - if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset; - if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor; - if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits; - - if ( json.skinning !== undefined ) material.skinning = json.skinning; - if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets; - if ( json.morphNormals !== undefined ) material.morphNormals = json.morphNormals; - if ( json.dithering !== undefined ) material.dithering = json.dithering; - - if ( json.vertexTangents !== undefined ) material.vertexTangents = json.vertexTangents; - - if ( json.visible !== undefined ) material.visible = json.visible; - - if ( json.toneMapped !== undefined ) material.toneMapped = json.toneMapped; - - if ( json.userData !== undefined ) material.userData = json.userData; - - if ( json.vertexColors !== undefined ) { - - if ( typeof json.vertexColors === 'number' ) { - - material.vertexColors = ( json.vertexColors > 0 ) ? true : false; - - } else { - - material.vertexColors = json.vertexColors; - - } - - } - - // Shader Material - - if ( json.uniforms !== undefined ) { - - for ( const name in json.uniforms ) { - - const uniform = json.uniforms[ name ]; - - material.uniforms[ name ] = {}; - - switch ( uniform.type ) { - - case 't': - material.uniforms[ name ].value = getTexture( uniform.value ); - break; - - case 'c': - material.uniforms[ name ].value = new Color().setHex( uniform.value ); - break; - - case 'v2': - material.uniforms[ name ].value = new Vector2().fromArray( uniform.value ); - break; - - case 'v3': - material.uniforms[ name ].value = new Vector3().fromArray( uniform.value ); - break; - - case 'v4': - material.uniforms[ name ].value = new Vector4().fromArray( uniform.value ); - break; - - case 'm3': - material.uniforms[ name ].value = new Matrix3().fromArray( uniform.value ); - break; - - case 'm4': - material.uniforms[ name ].value = new Matrix4().fromArray( uniform.value ); - break; - - default: - material.uniforms[ name ].value = uniform.value; - - } - - } - - } - - if ( json.defines !== undefined ) material.defines = json.defines; - if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; - if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; - - if ( json.extensions !== undefined ) { - - for ( const key in json.extensions ) { - - material.extensions[ key ] = json.extensions[ key ]; - - } - - } - - // Deprecated - - if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading - - // for PointsMaterial - - if ( json.size !== undefined ) material.size = json.size; - if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; - - // maps - - if ( json.map !== undefined ) material.map = getTexture( json.map ); - if ( json.matcap !== undefined ) material.matcap = getTexture( json.matcap ); - - if ( json.alphaMap !== undefined ) material.alphaMap = getTexture( json.alphaMap ); - - if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); - if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; - - if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); - if ( json.normalMapType !== undefined ) material.normalMapType = json.normalMapType; - if ( json.normalScale !== undefined ) { - - let normalScale = json.normalScale; - - if ( Array.isArray( normalScale ) === false ) { - - // Blender exporter used to export a scalar. See #7459 - - normalScale = [ normalScale, normalScale ]; - - } - - material.normalScale = new Vector2().fromArray( normalScale ); - - } - - if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); - if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; - if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; - - if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); - if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); - - if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); - if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; - - if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); - - if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); - if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity; - - if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; - if ( json.refractionRatio !== undefined ) material.refractionRatio = json.refractionRatio; - - if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); - if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; - - if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); - if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; - - if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); - - if ( json.clearcoatMap !== undefined ) material.clearcoatMap = getTexture( json.clearcoatMap ); - if ( json.clearcoatRoughnessMap !== undefined ) material.clearcoatRoughnessMap = getTexture( json.clearcoatRoughnessMap ); - if ( json.clearcoatNormalMap !== undefined ) material.clearcoatNormalMap = getTexture( json.clearcoatNormalMap ); - if ( json.clearcoatNormalScale !== undefined ) material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale ); - - if ( json.transmission !== undefined ) material.transmission = json.transmission; - if ( json.transmissionMap !== undefined ) material.transmissionMap = getTexture( json.transmissionMap ); - - return material; - - }, - - setTextures: function ( value ) { - - this.textures = value; - return this; - - } - - } ); - - const LoaderUtils = { - - decodeText: function ( array ) { - - if ( typeof TextDecoder !== 'undefined' ) { - - return new TextDecoder().decode( array ); - - } - - // Avoid the String.fromCharCode.apply(null, array) shortcut, which - // throws a "maximum call stack size exceeded" error for large arrays. - - let s = ''; - - for ( let i = 0, il = array.length; i < il; i ++ ) { - - // Implicitly assumes little-endian. - s += String.fromCharCode( array[ i ] ); - - } - - try { - - // merges multi-byte utf-8 characters. - - return decodeURIComponent( escape( s ) ); - - } catch ( e ) { // see #16358 - - return s; - - } - - }, - - extractUrlBase: function ( url ) { - - const index = url.lastIndexOf( '/' ); - - if ( index === - 1 ) return './'; - - return url.substr( 0, index + 1 ); - - } - - }; - - function InstancedBufferGeometry() { - - BufferGeometry.call( this ); - - this.type = 'InstancedBufferGeometry'; - this.instanceCount = Infinity; - - } - - InstancedBufferGeometry.prototype = Object.assign( Object.create( BufferGeometry.prototype ), { - - constructor: InstancedBufferGeometry, - - isInstancedBufferGeometry: true, - - copy: function ( source ) { - - BufferGeometry.prototype.copy.call( this, source ); - - this.instanceCount = source.instanceCount; - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - toJSON: function () { - - const data = BufferGeometry.prototype.toJSON.call( this ); - - data.instanceCount = this.instanceCount; - - data.isInstancedBufferGeometry = true; - - return data; - - } - - } ); - - function InstancedBufferAttribute( array, itemSize, normalized, meshPerAttribute ) { - - if ( typeof ( normalized ) === 'number' ) { - - meshPerAttribute = normalized; - - normalized = false; - - console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' ); - - } - - BufferAttribute.call( this, array, itemSize, normalized ); - - this.meshPerAttribute = meshPerAttribute || 1; - - } - - InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribute.prototype ), { - - constructor: InstancedBufferAttribute, - - isInstancedBufferAttribute: true, - - copy: function ( source ) { - - BufferAttribute.prototype.copy.call( this, source ); - - this.meshPerAttribute = source.meshPerAttribute; - - return this; - - }, - - toJSON: function () { - - const data = BufferAttribute.prototype.toJSON.call( this ); - - data.meshPerAttribute = this.meshPerAttribute; - - data.isInstancedBufferAttribute = true; - - return data; - - } - - } ); - - function BufferGeometryLoader( manager ) { - - Loader.call( this, manager ); - - } - - BufferGeometryLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: BufferGeometryLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.load( url, function ( text ) { - - try { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - const interleavedBufferMap = {}; - const arrayBufferMap = {}; - - function getInterleavedBuffer( json, uuid ) { - - if ( interleavedBufferMap[ uuid ] !== undefined ) return interleavedBufferMap[ uuid ]; - - const interleavedBuffers = json.interleavedBuffers; - const interleavedBuffer = interleavedBuffers[ uuid ]; - - const buffer = getArrayBuffer( json, interleavedBuffer.buffer ); - - const array = new TYPED_ARRAYS[ interleavedBuffer.type ]( buffer ); - const ib = new InterleavedBuffer( array, interleavedBuffer.stride ); - ib.uuid = interleavedBuffer.uuid; - - interleavedBufferMap[ uuid ] = ib; - - return ib; - - } - - function getArrayBuffer( json, uuid ) { - - if ( arrayBufferMap[ uuid ] !== undefined ) return arrayBufferMap[ uuid ]; - - const arrayBuffers = json.arrayBuffers; - const arrayBuffer = arrayBuffers[ uuid ]; - - const ab = new Uint32Array( arrayBuffer ).buffer; - - arrayBufferMap[ uuid ] = ab; - - return ab; - - } - - const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); - - const index = json.data.index; - - if ( index !== undefined ) { - - const typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); - geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); - - } - - const attributes = json.data.attributes; - - for ( const key in attributes ) { - - const attribute = attributes[ key ]; - let bufferAttribute; - - if ( attribute.isInterleavedBufferAttribute ) { - - const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); - bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); - - } else { - - const typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); - const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; - bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized ); - - } - - if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; - geometry.setAttribute( key, bufferAttribute ); - - } - - const morphAttributes = json.data.morphAttributes; - - if ( morphAttributes ) { - - for ( const key in morphAttributes ) { - - const attributeArray = morphAttributes[ key ]; - - const array = []; - - for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { - - const attribute = attributeArray[ i ]; - let bufferAttribute; - - if ( attribute.isInterleavedBufferAttribute ) { - - const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); - bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); - - } else { - - const typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); - bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ); - - } - - if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; - array.push( bufferAttribute ); - - } - - geometry.morphAttributes[ key ] = array; - - } - - } - - const morphTargetsRelative = json.data.morphTargetsRelative; - - if ( morphTargetsRelative ) { - - geometry.morphTargetsRelative = true; - - } - - const groups = json.data.groups || json.data.drawcalls || json.data.offsets; - - if ( groups !== undefined ) { - - for ( let i = 0, n = groups.length; i !== n; ++ i ) { - - const group = groups[ i ]; - - geometry.addGroup( group.start, group.count, group.materialIndex ); - - } - - } - - const boundingSphere = json.data.boundingSphere; - - if ( boundingSphere !== undefined ) { - - const center = new Vector3(); - - if ( boundingSphere.center !== undefined ) { - - center.fromArray( boundingSphere.center ); - - } - - geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); - - } - - if ( json.name ) geometry.name = json.name; - if ( json.userData ) geometry.userData = json.userData; - - return geometry; - - } - - } ); - - const TYPED_ARRAYS = { - Int8Array: Int8Array, - Uint8Array: Uint8Array, - // Workaround for IE11 pre KB2929437. See #11440 - Uint8ClampedArray: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : Uint8Array, - Int16Array: Int16Array, - Uint16Array: Uint16Array, - Int32Array: Int32Array, - Uint32Array: Uint32Array, - Float32Array: Float32Array, - Float64Array: Float64Array - }; - - function ObjectLoader( manager ) { - - Loader.call( this, manager ); - - } - - ObjectLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: ObjectLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path; - this.resourcePath = this.resourcePath || path; - - const loader = new FileLoader( scope.manager ); - loader.setPath( this.path ); - loader.setRequestHeader( this.requestHeader ); - loader.load( url, function ( text ) { - - let json = null; - - try { - - json = JSON.parse( text ); - - } catch ( error ) { - - if ( onError !== undefined ) onError( error ); - - console.error( 'THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message ); - - return; - - } - - const metadata = json.metadata; - - if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { - - console.error( 'THREE.ObjectLoader: Can\'t load ' + url ); - return; - - } - - scope.parse( json, onLoad ); - - }, onProgress, onError ); - - }, - - parse: function ( json, onLoad ) { - - const shapes = this.parseShape( json.shapes ); - const geometries = this.parseGeometries( json.geometries, shapes ); - - const images = this.parseImages( json.images, function () { - - if ( onLoad !== undefined ) onLoad( object ); - - } ); - - const textures = this.parseTextures( json.textures, images ); - const materials = this.parseMaterials( json.materials, textures ); - - const object = this.parseObject( json.object, geometries, materials ); - - if ( json.animations ) { - - object.animations = this.parseAnimations( json.animations ); - - } - - if ( json.images === undefined || json.images.length === 0 ) { - - if ( onLoad !== undefined ) onLoad( object ); - - } - - return object; - - }, - - parseShape: function ( json ) { - - const shapes = {}; - - if ( json !== undefined ) { - - for ( let i = 0, l = json.length; i < l; i ++ ) { - - const shape = new Shape().fromJSON( json[ i ] ); - - shapes[ shape.uuid ] = shape; - - } - - } - - return shapes; - - }, - - parseGeometries: function ( json, shapes ) { - - const geometries = {}; - let geometryShapes; - - if ( json !== undefined ) { - - const bufferGeometryLoader = new BufferGeometryLoader(); - - for ( let i = 0, l = json.length; i < l; i ++ ) { - - let geometry; - const data = json[ i ]; - - switch ( data.type ) { - - case 'PlaneGeometry': - case 'PlaneBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.width, - data.height, - data.widthSegments, - data.heightSegments - ); - - break; - - case 'BoxGeometry': - case 'BoxBufferGeometry': - case 'CubeGeometry': // backwards compatible - - geometry = new Geometries[ data.type ]( - data.width, - data.height, - data.depth, - data.widthSegments, - data.heightSegments, - data.depthSegments - ); - - break; - - case 'CircleGeometry': - case 'CircleBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.segments, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'CylinderGeometry': - case 'CylinderBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radiusTop, - data.radiusBottom, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'ConeGeometry': - case 'ConeBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'SphereGeometry': - case 'SphereBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.widthSegments, - data.heightSegments, - data.phiStart, - data.phiLength, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'DodecahedronGeometry': - case 'DodecahedronBufferGeometry': - case 'IcosahedronGeometry': - case 'IcosahedronBufferGeometry': - case 'OctahedronGeometry': - case 'OctahedronBufferGeometry': - case 'TetrahedronGeometry': - case 'TetrahedronBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.detail - ); - - break; - - case 'RingGeometry': - case 'RingBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.innerRadius, - data.outerRadius, - data.thetaSegments, - data.phiSegments, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'TorusGeometry': - case 'TorusBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.tube, - data.radialSegments, - data.tubularSegments, - data.arc - ); - - break; - - case 'TorusKnotGeometry': - case 'TorusKnotBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.tube, - data.tubularSegments, - data.radialSegments, - data.p, - data.q - ); - - break; - - case 'TubeGeometry': - case 'TubeBufferGeometry': - - // This only works for built-in curves (e.g. CatmullRomCurve3). - // User defined curves or instances of CurvePath will not be deserialized. - geometry = new Geometries[ data.type ]( - new Curves[ data.path.type ]().fromJSON( data.path ), - data.tubularSegments, - data.radius, - data.radialSegments, - data.closed - ); - - break; - - case 'LatheGeometry': - case 'LatheBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.points, - data.segments, - data.phiStart, - data.phiLength - ); - - break; - - case 'PolyhedronGeometry': - case 'PolyhedronBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.vertices, - data.indices, - data.radius, - data.details - ); - - break; - - case 'ShapeGeometry': - case 'ShapeBufferGeometry': - - geometryShapes = []; - - for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { - - const shape = shapes[ data.shapes[ j ] ]; - - geometryShapes.push( shape ); - - } - - geometry = new Geometries[ data.type ]( - geometryShapes, - data.curveSegments - ); - - break; - - - case 'ExtrudeGeometry': - case 'ExtrudeBufferGeometry': - - geometryShapes = []; - - for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { - - const shape = shapes[ data.shapes[ j ] ]; - - geometryShapes.push( shape ); - - } - - const extrudePath = data.options.extrudePath; - - if ( extrudePath !== undefined ) { - - data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); - - } - - geometry = new Geometries[ data.type ]( - geometryShapes, - data.options - ); - - break; - - case 'BufferGeometry': - case 'InstancedBufferGeometry': - - geometry = bufferGeometryLoader.parse( data ); - - break; - - case 'Geometry': - - console.error( 'THREE.ObjectLoader: Loading "Geometry" is not supported anymore.' ); - - break; - - default: - - console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); - - continue; - - } - - geometry.uuid = data.uuid; - - if ( data.name !== undefined ) geometry.name = data.name; - if ( geometry.isBufferGeometry === true && data.userData !== undefined ) geometry.userData = data.userData; - - geometries[ data.uuid ] = geometry; - - } - - } - - return geometries; - - }, - - parseMaterials: function ( json, textures ) { - - const cache = {}; // MultiMaterial - const materials = {}; - - if ( json !== undefined ) { - - const loader = new MaterialLoader(); - loader.setTextures( textures ); - - for ( let i = 0, l = json.length; i < l; i ++ ) { - - const data = json[ i ]; - - if ( data.type === 'MultiMaterial' ) { - - // Deprecated - - const array = []; - - for ( let j = 0; j < data.materials.length; j ++ ) { - - const material = data.materials[ j ]; - - if ( cache[ material.uuid ] === undefined ) { - - cache[ material.uuid ] = loader.parse( material ); - - } - - array.push( cache[ material.uuid ] ); - - } - - materials[ data.uuid ] = array; - - } else { - - if ( cache[ data.uuid ] === undefined ) { - - cache[ data.uuid ] = loader.parse( data ); - - } - - materials[ data.uuid ] = cache[ data.uuid ]; - - } - - } - - } - - return materials; - - }, - - parseAnimations: function ( json ) { - - const animations = []; - - for ( let i = 0; i < json.length; i ++ ) { - - const data = json[ i ]; - - const clip = AnimationClip.parse( data ); - - if ( data.uuid !== undefined ) clip.uuid = data.uuid; - - animations.push( clip ); - - } - - return animations; - - }, - - parseImages: function ( json, onLoad ) { - - const scope = this; - const images = {}; - - let loader; - - function loadImage( url ) { - - scope.manager.itemStart( url ); - - return loader.load( url, function () { - - scope.manager.itemEnd( url ); - - }, undefined, function () { - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } ); - - } - - if ( json !== undefined && json.length > 0 ) { - - const manager = new LoadingManager( onLoad ); - - loader = new ImageLoader( manager ); - loader.setCrossOrigin( this.crossOrigin ); - - for ( let i = 0, il = json.length; i < il; i ++ ) { - - const image = json[ i ]; - const url = image.url; - - if ( Array.isArray( url ) ) { - - // load array of images e.g CubeTexture - - images[ image.uuid ] = []; - - for ( let j = 0, jl = url.length; j < jl; j ++ ) { - - const currentUrl = url[ j ]; - - const path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( currentUrl ) ? currentUrl : scope.resourcePath + currentUrl; - - images[ image.uuid ].push( loadImage( path ) ); - - } - - } else { - - // load single image - - const path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.resourcePath + image.url; - - images[ image.uuid ] = loadImage( path ); - - } - - } - - } - - return images; - - }, - - parseTextures: function ( json, images ) { - - function parseConstant( value, type ) { - - if ( typeof value === 'number' ) return value; - - console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); - - return type[ value ]; - - } - - const textures = {}; - - if ( json !== undefined ) { - - for ( let i = 0, l = json.length; i < l; i ++ ) { - - const data = json[ i ]; - - if ( data.image === undefined ) { - - console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); - - } - - if ( images[ data.image ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); - - } - - let texture; - - if ( Array.isArray( images[ data.image ] ) ) { - - texture = new CubeTexture( images[ data.image ] ); - - } else { - - texture = new Texture( images[ data.image ] ); - - } - - texture.needsUpdate = true; - - texture.uuid = data.uuid; - - if ( data.name !== undefined ) texture.name = data.name; - - if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); - - if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); - if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); - if ( data.center !== undefined ) texture.center.fromArray( data.center ); - if ( data.rotation !== undefined ) texture.rotation = data.rotation; - - if ( data.wrap !== undefined ) { - - texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); - texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); - - } - - if ( data.format !== undefined ) texture.format = data.format; - if ( data.type !== undefined ) texture.type = data.type; - if ( data.encoding !== undefined ) texture.encoding = data.encoding; - - if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); - if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); - if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; - - if ( data.flipY !== undefined ) texture.flipY = data.flipY; - - if ( data.premultiplyAlpha !== undefined ) texture.premultiplyAlpha = data.premultiplyAlpha; - if ( data.unpackAlignment !== undefined ) texture.unpackAlignment = data.unpackAlignment; - - textures[ data.uuid ] = texture; - - } - - } - - return textures; - - }, - - parseObject: function ( data, geometries, materials ) { - - let object; - - function getGeometry( name ) { - - if ( geometries[ name ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); - - } - - return geometries[ name ]; - - } - - function getMaterial( name ) { - - if ( name === undefined ) return undefined; - - if ( Array.isArray( name ) ) { - - const array = []; - - for ( let i = 0, l = name.length; i < l; i ++ ) { - - const uuid = name[ i ]; - - if ( materials[ uuid ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined material', uuid ); - - } - - array.push( materials[ uuid ] ); - - } - - return array; - - } - - if ( materials[ name ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined material', name ); - - } - - return materials[ name ]; - - } - - let geometry, material; - - switch ( data.type ) { - - case 'Scene': - - object = new Scene(); - - if ( data.background !== undefined ) { - - if ( Number.isInteger( data.background ) ) { - - object.background = new Color( data.background ); - - } - - } - - if ( data.fog !== undefined ) { - - if ( data.fog.type === 'Fog' ) { - - object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); - - } else if ( data.fog.type === 'FogExp2' ) { - - object.fog = new FogExp2( data.fog.color, data.fog.density ); - - } - - } - - break; - - case 'PerspectiveCamera': - - object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); - - if ( data.focus !== undefined ) object.focus = data.focus; - if ( data.zoom !== undefined ) object.zoom = data.zoom; - if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; - if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; - if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); - - break; - - case 'OrthographicCamera': - - object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); - - if ( data.zoom !== undefined ) object.zoom = data.zoom; - if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); - - break; - - case 'AmbientLight': - - object = new AmbientLight( data.color, data.intensity ); - - break; - - case 'DirectionalLight': - - object = new DirectionalLight( data.color, data.intensity ); - - break; - - case 'PointLight': - - object = new PointLight( data.color, data.intensity, data.distance, data.decay ); - - break; - - case 'RectAreaLight': - - object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); - - break; - - case 'SpotLight': - - object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); - - break; - - case 'HemisphereLight': - - object = new HemisphereLight( data.color, data.groundColor, data.intensity ); - - break; - - case 'LightProbe': - - object = new LightProbe().fromJSON( data ); - - break; - - case 'SkinnedMesh': - - console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet.' ); - - case 'Mesh': - - geometry = getGeometry( data.geometry ); - material = getMaterial( data.material ); - - object = new Mesh( geometry, material ); - - break; - - case 'InstancedMesh': - - geometry = getGeometry( data.geometry ); - material = getMaterial( data.material ); - const count = data.count; - const instanceMatrix = data.instanceMatrix; - - object = new InstancedMesh( geometry, material, count ); - object.instanceMatrix = new BufferAttribute( new Float32Array( instanceMatrix.array ), 16 ); - - break; - - case 'LOD': - - object = new LOD(); - - break; - - case 'Line': - - object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); - - break; - - case 'LineLoop': - - object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); - - break; - - case 'LineSegments': - - object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); - - break; - - case 'PointCloud': - case 'Points': - - object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); - - break; - - case 'Sprite': - - object = new Sprite( getMaterial( data.material ) ); - - break; - - case 'Group': - - object = new Group(); - - break; - - default: - - object = new Object3D(); - - } - - object.uuid = data.uuid; - - if ( data.name !== undefined ) object.name = data.name; - - if ( data.matrix !== undefined ) { - - object.matrix.fromArray( data.matrix ); - - if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate; - if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale ); - - } else { - - if ( data.position !== undefined ) object.position.fromArray( data.position ); - if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); - if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); - if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); - - } - - if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; - if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; - - if ( data.shadow ) { - - if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; - if ( data.shadow.normalBias !== undefined ) object.shadow.normalBias = data.shadow.normalBias; - if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; - if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); - if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); - - } - - if ( data.visible !== undefined ) object.visible = data.visible; - if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled; - if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder; - if ( data.userData !== undefined ) object.userData = data.userData; - if ( data.layers !== undefined ) object.layers.mask = data.layers; - - if ( data.children !== undefined ) { - - const children = data.children; - - for ( let i = 0; i < children.length; i ++ ) { - - object.add( this.parseObject( children[ i ], geometries, materials ) ); - - } - - } - - if ( data.type === 'LOD' ) { - - if ( data.autoUpdate !== undefined ) object.autoUpdate = data.autoUpdate; - - const levels = data.levels; - - for ( let l = 0; l < levels.length; l ++ ) { - - const level = levels[ l ]; - const child = object.getObjectByProperty( 'uuid', level.object ); - - if ( child !== undefined ) { - - object.addLevel( child, level.distance ); - - } - - } - - } - - return object; - - } - - } ); - - const TEXTURE_MAPPING = { - UVMapping: UVMapping, - CubeReflectionMapping: CubeReflectionMapping, - CubeRefractionMapping: CubeRefractionMapping, - EquirectangularReflectionMapping: EquirectangularReflectionMapping, - EquirectangularRefractionMapping: EquirectangularRefractionMapping, - CubeUVReflectionMapping: CubeUVReflectionMapping, - CubeUVRefractionMapping: CubeUVRefractionMapping - }; - - const TEXTURE_WRAPPING = { - RepeatWrapping: RepeatWrapping, - ClampToEdgeWrapping: ClampToEdgeWrapping, - MirroredRepeatWrapping: MirroredRepeatWrapping - }; - - const TEXTURE_FILTER = { - NearestFilter: NearestFilter, - NearestMipmapNearestFilter: NearestMipmapNearestFilter, - NearestMipmapLinearFilter: NearestMipmapLinearFilter, - LinearFilter: LinearFilter, - LinearMipmapNearestFilter: LinearMipmapNearestFilter, - LinearMipmapLinearFilter: LinearMipmapLinearFilter - }; - - function ImageBitmapLoader( manager ) { - - if ( typeof createImageBitmap === 'undefined' ) { - - console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); - - } - - if ( typeof fetch === 'undefined' ) { - - console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); - - } - - Loader.call( this, manager ); - - this.options = { premultiplyAlpha: 'none' }; - - } - - ImageBitmapLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: ImageBitmapLoader, - - isImageBitmapLoader: true, - - setOptions: function setOptions( options ) { - - this.options = options; - - return this; - - }, - - load: function ( url, onLoad, onProgress, onError ) { - - if ( url === undefined ) url = ''; - - if ( this.path !== undefined ) url = this.path + url; - - url = this.manager.resolveURL( url ); - - const scope = this; - - const cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) onLoad( cached ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - fetch( url ).then( function ( res ) { - - return res.blob(); - - } ).then( function ( blob ) { - - return createImageBitmap( blob, scope.options ); - - } ).then( function ( imageBitmap ) { - - Cache.add( url, imageBitmap ); - - if ( onLoad ) onLoad( imageBitmap ); - - scope.manager.itemEnd( url ); - - } ).catch( function ( e ) { - - if ( onError ) onError( e ); - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } ); - - scope.manager.itemStart( url ); - - } - - } ); - - function ShapePath() { - - this.type = 'ShapePath'; - - this.color = new Color(); - - this.subPaths = []; - this.currentPath = null; - - } - - Object.assign( ShapePath.prototype, { - - moveTo: function ( x, y ) { - - this.currentPath = new Path(); - this.subPaths.push( this.currentPath ); - this.currentPath.moveTo( x, y ); - - return this; - - }, - - lineTo: function ( x, y ) { - - this.currentPath.lineTo( x, y ); - - return this; - - }, - - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { - - this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); - - return this; - - }, - - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - - this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); - - return this; - - }, - - splineThru: function ( pts ) { - - this.currentPath.splineThru( pts ); - - return this; - - }, - - toShapes: function ( isCCW, noHoles ) { - - function toShapesNoHoles( inSubpaths ) { - - const shapes = []; - - for ( let i = 0, l = inSubpaths.length; i < l; i ++ ) { - - const tmpPath = inSubpaths[ i ]; - - const tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; - - shapes.push( tmpShape ); - - } - - return shapes; - - } - - function isPointInsidePolygon( inPt, inPolygon ) { - - const polyLen = inPolygon.length; - - // inPt on polygon contour => immediate success or - // toggling of inside/outside at every single! intersection point of an edge - // with the horizontal line through inPt, left of inPt - // not counting lowerY endpoints of edges and whole edges on that line - let inside = false; - for ( let p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { - - let edgeLowPt = inPolygon[ p ]; - let edgeHighPt = inPolygon[ q ]; - - let edgeDx = edgeHighPt.x - edgeLowPt.x; - let edgeDy = edgeHighPt.y - edgeLowPt.y; - - if ( Math.abs( edgeDy ) > Number.EPSILON ) { - - // not parallel - if ( edgeDy < 0 ) { - - edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; - edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; - - } - - if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; - - if ( inPt.y === edgeLowPt.y ) { - - if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? - // continue; // no intersection or edgeLowPt => doesn't count !!! - - } else { - - const perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); - if ( perpEdge === 0 ) return true; // inPt is on contour ? - if ( perpEdge < 0 ) continue; - inside = ! inside; // true intersection left of inPt - - } - - } else { - - // parallel or collinear - if ( inPt.y !== edgeLowPt.y ) continue; // parallel - // edge lies on the same horizontal line as inPt - if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || - ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! - // continue; - - } - - } - - return inside; - - } - - const isClockWise = ShapeUtils.isClockWise; - - const subPaths = this.subPaths; - if ( subPaths.length === 0 ) return []; - - if ( noHoles === true ) return toShapesNoHoles( subPaths ); - - - let solid, tmpPath, tmpShape; - const shapes = []; - - if ( subPaths.length === 1 ) { - - tmpPath = subPaths[ 0 ]; - tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; - shapes.push( tmpShape ); - return shapes; - - } - - let holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); - holesFirst = isCCW ? ! holesFirst : holesFirst; - - // console.log("Holes first", holesFirst); - - const betterShapeHoles = []; - const newShapes = []; - let newShapeHoles = []; - let mainIdx = 0; - let tmpPoints; - - newShapes[ mainIdx ] = undefined; - newShapeHoles[ mainIdx ] = []; - - for ( let i = 0, l = subPaths.length; i < l; i ++ ) { - - tmpPath = subPaths[ i ]; - tmpPoints = tmpPath.getPoints(); - solid = isClockWise( tmpPoints ); - solid = isCCW ? ! solid : solid; - - if ( solid ) { - - if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; - - newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; - newShapes[ mainIdx ].s.curves = tmpPath.curves; - - if ( holesFirst ) mainIdx ++; - newShapeHoles[ mainIdx ] = []; - - //console.log('cw', i); - - } else { - - newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); - - //console.log('ccw', i); - - } - - } - - // only Holes? -> probably all Shapes with wrong orientation - if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); - - - if ( newShapes.length > 1 ) { - - let ambiguous = false; - const toChange = []; - - for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - - betterShapeHoles[ sIdx ] = []; - - } - - for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - - const sho = newShapeHoles[ sIdx ]; - - for ( let hIdx = 0; hIdx < sho.length; hIdx ++ ) { - - const ho = sho[ hIdx ]; - let hole_unassigned = true; - - for ( let s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { - - if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { - - if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); - if ( hole_unassigned ) { - - hole_unassigned = false; - betterShapeHoles[ s2Idx ].push( ho ); - - } else { - - ambiguous = true; - - } - - } - - } - - if ( hole_unassigned ) { - - betterShapeHoles[ sIdx ].push( ho ); - - } - - } - - } - // console.log("ambiguous: ", ambiguous); - - if ( toChange.length > 0 ) { - - // console.log("to change: ", toChange); - if ( ! ambiguous ) newShapeHoles = betterShapeHoles; - - } - - } - - let tmpHoles; - - for ( let i = 0, il = newShapes.length; i < il; i ++ ) { - - tmpShape = newShapes[ i ].s; - shapes.push( tmpShape ); - tmpHoles = newShapeHoles[ i ]; - - for ( let j = 0, jl = tmpHoles.length; j < jl; j ++ ) { - - tmpShape.holes.push( tmpHoles[ j ].h ); - - } - - } - - //console.log("shape", shapes); - - return shapes; - - } - - } ); - - function Font( data ) { - - this.type = 'Font'; - - this.data = data; - - } - - Object.assign( Font.prototype, { - - isFont: true, - - generateShapes: function ( text, size ) { - - if ( size === undefined ) size = 100; - - const shapes = []; - const paths = createPaths( text, size, this.data ); - - for ( let p = 0, pl = paths.length; p < pl; p ++ ) { - - Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); - - } - - return shapes; - - } - - } ); - - function createPaths( text, size, data ) { - - const chars = Array.from ? Array.from( text ) : String( text ).split( '' ); // workaround for IE11, see #13988 - const scale = size / data.resolution; - const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; - - const paths = []; - - let offsetX = 0, offsetY = 0; - - for ( let i = 0; i < chars.length; i ++ ) { - - const char = chars[ i ]; - - if ( char === '\n' ) { - - offsetX = 0; - offsetY -= line_height; - - } else { - - const ret = createPath( char, scale, offsetX, offsetY, data ); - offsetX += ret.offsetX; - paths.push( ret.path ); - - } - - } - - return paths; - - } - - function createPath( char, scale, offsetX, offsetY, data ) { - - const glyph = data.glyphs[ char ] || data.glyphs[ '?' ]; - - if ( ! glyph ) { - - console.error( 'THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.' ); - - return; - - } - - const path = new ShapePath(); - - let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; - - if ( glyph.o ) { - - const outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); - - for ( let i = 0, l = outline.length; i < l; ) { - - const action = outline[ i ++ ]; - - switch ( action ) { - - case 'm': // moveTo - - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; - - path.moveTo( x, y ); - - break; - - case 'l': // lineTo - - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; - - path.lineTo( x, y ); - - break; - - case 'q': // quadraticCurveTo - - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; - - path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); - - break; - - case 'b': // bezierCurveTo - - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; - cpx2 = outline[ i ++ ] * scale + offsetX; - cpy2 = outline[ i ++ ] * scale + offsetY; - - path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); - - break; - - } - - } - - } - - return { offsetX: glyph.ha * scale, path: path }; - - } - - function FontLoader( manager ) { - - Loader.call( this, manager ); - - } - - FontLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: FontLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setRequestHeader( this.requestHeader ); - loader.load( url, function ( text ) { - - let json; - - try { - - json = JSON.parse( text ); - - } catch ( e ) { - - console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' ); - json = JSON.parse( text.substring( 65, text.length - 2 ) ); - - } - - const font = scope.parse( json ); - - if ( onLoad ) onLoad( font ); - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - return new Font( json ); - - } - - } ); - - let _context; - - const AudioContext = { - - getContext: function () { - - if ( _context === undefined ) { - - _context = new ( window.AudioContext || window.webkitAudioContext )(); - - } - - return _context; - - }, - - setContext: function ( value ) { - - _context = value; - - } - - }; - - function AudioLoader( manager ) { - - Loader.call( this, manager ); - - } - - AudioLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: AudioLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( scope.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.load( url, function ( buffer ) { - - try { - - // Create a copy of the buffer. The `decodeAudioData` method - // detaches the buffer when complete, preventing reuse. - const bufferCopy = buffer.slice( 0 ); - - const context = AudioContext.getContext(); - context.decodeAudioData( bufferCopy, function ( audioBuffer ) { - - onLoad( audioBuffer ); - - } ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - } - - } ); - - function HemisphereLightProbe( skyColor, groundColor, intensity ) { - - LightProbe.call( this, undefined, intensity ); - - const color1 = new Color().set( skyColor ); - const color2 = new Color().set( groundColor ); - - const sky = new Vector3( color1.r, color1.g, color1.b ); - const ground = new Vector3( color2.r, color2.g, color2.b ); - - // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); - const c0 = Math.sqrt( Math.PI ); - const c1 = c0 * Math.sqrt( 0.75 ); - - this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); - this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); - - } - - HemisphereLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), { - - constructor: HemisphereLightProbe, - - isHemisphereLightProbe: true, - - copy: function ( source ) { // modifying colors not currently supported - - LightProbe.prototype.copy.call( this, source ); - - return this; - - }, - - toJSON: function ( meta ) { - - const data = LightProbe.prototype.toJSON.call( this, meta ); - - // data.sh = this.sh.toArray(); // todo - - return data; - - } - - } ); - - function AmbientLightProbe( color, intensity ) { - - LightProbe.call( this, undefined, intensity ); - - const color1 = new Color().set( color ); - - // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); - this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); - - } - - AmbientLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), { - - constructor: AmbientLightProbe, - - isAmbientLightProbe: true, - - copy: function ( source ) { // modifying color not currently supported - - LightProbe.prototype.copy.call( this, source ); - - return this; - - }, - - toJSON: function ( meta ) { - - const data = LightProbe.prototype.toJSON.call( this, meta ); - - // data.sh = this.sh.toArray(); // todo - - return data; - - } - - } ); - - const _eyeRight = new Matrix4(); - const _eyeLeft = new Matrix4(); - - function StereoCamera() { - - this.type = 'StereoCamera'; - - this.aspect = 1; - - this.eyeSep = 0.064; - - this.cameraL = new PerspectiveCamera(); - this.cameraL.layers.enable( 1 ); - this.cameraL.matrixAutoUpdate = false; - - this.cameraR = new PerspectiveCamera(); - this.cameraR.layers.enable( 2 ); - this.cameraR.matrixAutoUpdate = false; - - this._cache = { - focus: null, - fov: null, - aspect: null, - near: null, - far: null, - zoom: null, - eyeSep: null - }; - - } - - Object.assign( StereoCamera.prototype, { - - update: function ( camera ) { - - const cache = this._cache; - - const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || - cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || - cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; - - if ( needsUpdate ) { - - cache.focus = camera.focus; - cache.fov = camera.fov; - cache.aspect = camera.aspect * this.aspect; - cache.near = camera.near; - cache.far = camera.far; - cache.zoom = camera.zoom; - cache.eyeSep = this.eyeSep; - - // Off-axis stereoscopic effect based on - // http://paulbourke.net/stereographics/stereorender/ - - const projectionMatrix = camera.projectionMatrix.clone(); - const eyeSepHalf = cache.eyeSep / 2; - const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; - const ymax = ( cache.near * Math.tan( MathUtils.DEG2RAD * cache.fov * 0.5 ) ) / cache.zoom; - let xmin, xmax; - - // translate xOffset - - _eyeLeft.elements[ 12 ] = - eyeSepHalf; - _eyeRight.elements[ 12 ] = eyeSepHalf; - - // for left eye - - xmin = - ymax * cache.aspect + eyeSepOnProjection; - xmax = ymax * cache.aspect + eyeSepOnProjection; - - projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); - projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); - - this.cameraL.projectionMatrix.copy( projectionMatrix ); - - // for right eye - - xmin = - ymax * cache.aspect - eyeSepOnProjection; - xmax = ymax * cache.aspect - eyeSepOnProjection; - - projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); - projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); - - this.cameraR.projectionMatrix.copy( projectionMatrix ); - - } - - this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeLeft ); - this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeRight ); - - } - - } ); - - class Clock { - - constructor( autoStart ) { - - this.autoStart = ( autoStart !== undefined ) ? autoStart : true; - - this.startTime = 0; - this.oldTime = 0; - this.elapsedTime = 0; - - this.running = false; - - } - - start() { - - this.startTime = ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732 - - this.oldTime = this.startTime; - this.elapsedTime = 0; - this.running = true; - - } - - stop() { - - this.getElapsedTime(); - this.running = false; - this.autoStart = false; - - } - - getElapsedTime() { - - this.getDelta(); - return this.elapsedTime; - - } - - getDelta() { - - let diff = 0; - - if ( this.autoStart && ! this.running ) { - - this.start(); - return 0; - - } - - if ( this.running ) { - - const newTime = ( typeof performance === 'undefined' ? Date : performance ).now(); - - diff = ( newTime - this.oldTime ) / 1000; - this.oldTime = newTime; - - this.elapsedTime += diff; - - } - - return diff; - - } - - } - - const _position$2 = new Vector3(); - const _quaternion$3 = new Quaternion(); - const _scale$1 = new Vector3(); - const _orientation = new Vector3(); - - class Audio extends Object3D { - - constructor( listener ) { - - super(); - - this.type = 'Audio'; - - this.listener = listener; - this.context = listener.context; - - this.gain = this.context.createGain(); - this.gain.connect( listener.getInput() ); - - this.autoplay = false; - - this.buffer = null; - this.detune = 0; - this.loop = false; - this.loopStart = 0; - this.loopEnd = 0; - this.offset = 0; - this.duration = undefined; - this.playbackRate = 1; - this.isPlaying = false; - this.hasPlaybackControl = true; - this.source = null; - this.sourceType = 'empty'; - - this._startedAt = 0; - this._progress = 0; - this._connected = false; - - this.filters = []; - - } - - getOutput() { - - return this.gain; - - } - - setNodeSource( audioNode ) { - - this.hasPlaybackControl = false; - this.sourceType = 'audioNode'; - this.source = audioNode; - this.connect(); - - return this; - - } - - setMediaElementSource( mediaElement ) { - - this.hasPlaybackControl = false; - this.sourceType = 'mediaNode'; - this.source = this.context.createMediaElementSource( mediaElement ); - this.connect(); - - return this; - - } - - setMediaStreamSource( mediaStream ) { - - this.hasPlaybackControl = false; - this.sourceType = 'mediaStreamNode'; - this.source = this.context.createMediaStreamSource( mediaStream ); - this.connect(); - - return this; - - } - - setBuffer( audioBuffer ) { - - this.buffer = audioBuffer; - this.sourceType = 'buffer'; - - if ( this.autoplay ) this.play(); - - return this; - - } - - play( delay ) { - - if ( delay === undefined ) delay = 0; - - if ( this.isPlaying === true ) { - - console.warn( 'THREE.Audio: Audio is already playing.' ); - return; - - } - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this._startedAt = this.context.currentTime + delay; - - const source = this.context.createBufferSource(); - source.buffer = this.buffer; - source.loop = this.loop; - source.loopStart = this.loopStart; - source.loopEnd = this.loopEnd; - source.onended = this.onEnded.bind( this ); - source.start( this._startedAt, this._progress + this.offset, this.duration ); - - this.isPlaying = true; - - this.source = source; - - this.setDetune( this.detune ); - this.setPlaybackRate( this.playbackRate ); - - return this.connect(); - - } - - pause() { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - if ( this.isPlaying === true ) { - - // update current progress - - this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; - - if ( this.loop === true ) { - - // ensure _progress does not exceed duration with looped audios - - this._progress = this._progress % ( this.duration || this.buffer.duration ); - - } - - this.source.stop(); - this.source.onended = null; - - this.isPlaying = false; - - } - - return this; - - } - - stop() { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this._progress = 0; - - this.source.stop(); - this.source.onended = null; - this.isPlaying = false; - - return this; - - } - - connect() { - - if ( this.filters.length > 0 ) { - - this.source.connect( this.filters[ 0 ] ); - - for ( let i = 1, l = this.filters.length; i < l; i ++ ) { - - this.filters[ i - 1 ].connect( this.filters[ i ] ); - - } - - this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); - - } else { - - this.source.connect( this.getOutput() ); - - } - - this._connected = true; - - return this; - - } - - disconnect() { - - if ( this.filters.length > 0 ) { - - this.source.disconnect( this.filters[ 0 ] ); - - for ( let i = 1, l = this.filters.length; i < l; i ++ ) { - - this.filters[ i - 1 ].disconnect( this.filters[ i ] ); - - } - - this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); - - } else { - - this.source.disconnect( this.getOutput() ); - - } - - this._connected = false; - - return this; - - } - - getFilters() { - - return this.filters; - - } - - setFilters( value ) { - - if ( ! value ) value = []; - - if ( this._connected === true ) { - - this.disconnect(); - this.filters = value; - this.connect(); - - } else { - - this.filters = value; - - } - - return this; - - } - - setDetune( value ) { - - this.detune = value; - - if ( this.source.detune === undefined ) return; // only set detune when available - - if ( this.isPlaying === true ) { - - this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); - - } - - return this; - - } - - getDetune() { - - return this.detune; - - } - - getFilter() { - - return this.getFilters()[ 0 ]; - - } - - setFilter( filter ) { - - return this.setFilters( filter ? [ filter ] : [] ); - - } - - setPlaybackRate( value ) { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this.playbackRate = value; - - if ( this.isPlaying === true ) { - - this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); - - } - - return this; - - } - - getPlaybackRate() { - - return this.playbackRate; - - } - - onEnded() { - - this.isPlaying = false; - - } - - getLoop() { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return false; - - } - - return this.loop; - - } - - setLoop( value ) { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this.loop = value; - - if ( this.isPlaying === true ) { - - this.source.loop = this.loop; - - } - - return this; - - } - - setLoopStart( value ) { - - this.loopStart = value; - - return this; - - } - - setLoopEnd( value ) { - - this.loopEnd = value; - - return this; - - } - - getVolume() { - - return this.gain.gain.value; - - } - - setVolume( value ) { - - this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); - - return this; - - } - - } - - const _position$3 = new Vector3(); - const _quaternion$4 = new Quaternion(); - const _scale$2 = new Vector3(); - const _orientation$1 = new Vector3(); - - function PropertyMixer( binding, typeName, valueSize ) { - - this.binding = binding; - this.valueSize = valueSize; - - let mixFunction, - mixFunctionAdditive, - setIdentity; - - // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] - // - // interpolators can use .buffer as their .result - // the data then goes to 'incoming' - // - // 'accu0' and 'accu1' are used frame-interleaved for - // the cumulative result and are compared to detect - // changes - // - // 'orig' stores the original state of the property - // - // 'add' is used for additive cumulative results - // - // 'work' is optional and is only present for quaternion types. It is used - // to store intermediate quaternion multiplication results - - switch ( typeName ) { - - case 'quaternion': - mixFunction = this._slerp; - mixFunctionAdditive = this._slerpAdditive; - setIdentity = this._setAdditiveIdentityQuaternion; - - this.buffer = new Float64Array( valueSize * 6 ); - this._workIndex = 5; - break; - - case 'string': - case 'bool': - mixFunction = this._select; - - // Use the regular mix function and for additive on these types, - // additive is not relevant for non-numeric types - mixFunctionAdditive = this._select; - - setIdentity = this._setAdditiveIdentityOther; - - this.buffer = new Array( valueSize * 5 ); - break; - - default: - mixFunction = this._lerp; - mixFunctionAdditive = this._lerpAdditive; - setIdentity = this._setAdditiveIdentityNumeric; - - this.buffer = new Float64Array( valueSize * 5 ); - - } - - this._mixBufferRegion = mixFunction; - this._mixBufferRegionAdditive = mixFunctionAdditive; - this._setIdentity = setIdentity; - this._origIndex = 3; - this._addIndex = 4; - - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; - - this.useCount = 0; - this.referenceCount = 0; - - } - - Object.assign( PropertyMixer.prototype, { - - // accumulate data in the 'incoming' region into 'accu' - accumulate: function ( accuIndex, weight ) { - - // note: happily accumulating nothing when weight = 0, the caller knows - // the weight and shouldn't have made the call in the first place - - const buffer = this.buffer, - stride = this.valueSize, - offset = accuIndex * stride + stride; - - let currentWeight = this.cumulativeWeight; - - if ( currentWeight === 0 ) { - - // accuN := incoming * weight - - for ( let i = 0; i !== stride; ++ i ) { - - buffer[ offset + i ] = buffer[ i ]; - - } - - currentWeight = weight; - - } else { - - // accuN := accuN + incoming * weight - - currentWeight += weight; - const mix = weight / currentWeight; - this._mixBufferRegion( buffer, offset, 0, mix, stride ); - - } - - this.cumulativeWeight = currentWeight; - - }, - - // accumulate data in the 'incoming' region into 'add' - accumulateAdditive: function ( weight ) { - - const buffer = this.buffer, - stride = this.valueSize, - offset = stride * this._addIndex; - - if ( this.cumulativeWeightAdditive === 0 ) { - - // add = identity - - this._setIdentity(); - - } - - // add := add + incoming * weight - - this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); - this.cumulativeWeightAdditive += weight; - - }, - - // apply the state of 'accu' to the binding when accus differ - apply: function ( accuIndex ) { - - const stride = this.valueSize, - buffer = this.buffer, - offset = accuIndex * stride + stride, - - weight = this.cumulativeWeight, - weightAdditive = this.cumulativeWeightAdditive, - - binding = this.binding; - - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; - - if ( weight < 1 ) { - - // accuN := accuN + original * ( 1 - cumulativeWeight ) - - const originalValueOffset = stride * this._origIndex; - - this._mixBufferRegion( - buffer, offset, originalValueOffset, 1 - weight, stride ); - - } - - if ( weightAdditive > 0 ) { - - // accuN := accuN + additive accuN - - this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride ); - - } - - for ( let i = stride, e = stride + stride; i !== e; ++ i ) { - - if ( buffer[ i ] !== buffer[ i + stride ] ) { - - // value has changed -> update scene graph - - binding.setValue( buffer, offset ); - break; - - } - - } - - }, - - // remember the state of the bound property and copy it to both accus - saveOriginalState: function () { - - const binding = this.binding; - - const buffer = this.buffer, - stride = this.valueSize, - - originalValueOffset = stride * this._origIndex; - - binding.getValue( buffer, originalValueOffset ); - - // accu[0..1] := orig -- initially detect changes against the original - for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) { - - buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; - - } - - // Add to identity for additive - this._setIdentity(); - - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; - - }, - - // apply the state previously taken via 'saveOriginalState' to the binding - restoreOriginalState: function () { - - const originalValueOffset = this.valueSize * 3; - this.binding.setValue( this.buffer, originalValueOffset ); - - }, - - _setAdditiveIdentityNumeric: function () { - - const startIndex = this._addIndex * this.valueSize; - const endIndex = startIndex + this.valueSize; - - for ( let i = startIndex; i < endIndex; i ++ ) { - - this.buffer[ i ] = 0; - - } - - }, - - _setAdditiveIdentityQuaternion: function () { - - this._setAdditiveIdentityNumeric(); - this.buffer[ this._addIndex * this.valueSize + 3 ] = 1; - - }, - - _setAdditiveIdentityOther: function () { - - const startIndex = this._origIndex * this.valueSize; - const targetIndex = this._addIndex * this.valueSize; - - for ( let i = 0; i < this.valueSize; i ++ ) { - - this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ]; - - } - - }, - - - // mix functions - - _select: function ( buffer, dstOffset, srcOffset, t, stride ) { - - if ( t >= 0.5 ) { - - for ( let i = 0; i !== stride; ++ i ) { - - buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; - - } - - } - - }, - - _slerp: function ( buffer, dstOffset, srcOffset, t ) { - - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); - - }, - - _slerpAdditive: function ( buffer, dstOffset, srcOffset, t, stride ) { - - const workOffset = this._workIndex * stride; - - // Store result in intermediate buffer offset - Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset ); - - // Slerp to the intermediate result - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); - - }, - - _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) { - - const s = 1 - t; - - for ( let i = 0; i !== stride; ++ i ) { - - const j = dstOffset + i; - - buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; - - } - - }, - - _lerpAdditive: function ( buffer, dstOffset, srcOffset, t, stride ) { - - for ( let i = 0; i !== stride; ++ i ) { - - const j = dstOffset + i; - - buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t; - - } - - } - - } ); - - // Characters [].:/ are reserved for track binding syntax. - const _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; - const _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' ); - - // Attempts to allow node names from any language. ES5's `\w` regexp matches - // only latin characters, and the unicode \p{L} is not yet supported. So - // instead, we exclude reserved characters and match everything else. - const _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; - const _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\.', '' ) + ']'; - - // Parent directories, delimited by '/' or ':'. Currently unused, but must - // be matched to parse the rest of the track name. - const _directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar ); - - // Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. - const _nodeRe = /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot ); - - // Object on target node, and accessor. May not contain reserved - // characters. Accessor may contain any character except closing bracket. - const _objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar ); - - // Property and accessor. May not contain reserved characters. Accessor may - // contain any non-bracket characters. - const _propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar ); - - const _trackRe = new RegExp( '' - + '^' - + _directoryRe - + _nodeRe - + _objectRe - + _propertyRe - + '$' - ); - - const _supportedObjectNames = [ 'material', 'materials', 'bones' ]; - - function Composite( targetGroup, path, optionalParsedPath ) { - - const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); - - this._targetGroup = targetGroup; - this._bindings = targetGroup.subscribe_( path, parsedPath ); - - } - - Object.assign( Composite.prototype, { - - getValue: function ( array, offset ) { - - this.bind(); // bind all binding - - const firstValidIndex = this._targetGroup.nCachedObjects_, - binding = this._bindings[ firstValidIndex ]; - - // and only call .getValue on the first - if ( binding !== undefined ) binding.getValue( array, offset ); - - }, - - setValue: function ( array, offset ) { - - const bindings = this._bindings; - - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].setValue( array, offset ); - - } - - }, - - bind: function () { - - const bindings = this._bindings; - - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].bind(); - - } - - }, - - unbind: function () { - - const bindings = this._bindings; - - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].unbind(); - - } - - } - - } ); - - - function PropertyBinding( rootNode, path, parsedPath ) { - - this.path = path; - this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); - - this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; - - this.rootNode = rootNode; - - } - - Object.assign( PropertyBinding, { - - Composite: Composite, - - create: function ( root, path, parsedPath ) { - - if ( ! ( root && root.isAnimationObjectGroup ) ) { - - return new PropertyBinding( root, path, parsedPath ); - - } else { - - return new PropertyBinding.Composite( root, path, parsedPath ); - - } - - }, - - /** - * Replaces spaces with underscores and removes unsupported characters from - * node names, to ensure compatibility with parseTrackName(). - * - * @param {string} name Node name to be sanitized. - * @return {string} - */ - sanitizeNodeName: function ( name ) { - - return name.replace( /\s/g, '_' ).replace( _reservedRe, '' ); - - }, - - parseTrackName: function ( trackName ) { - - const matches = _trackRe.exec( trackName ); - - if ( ! matches ) { - - throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName ); - - } - - const results = { - // directoryName: matches[ 1 ], // (tschw) currently unused - nodeName: matches[ 2 ], - objectName: matches[ 3 ], - objectIndex: matches[ 4 ], - propertyName: matches[ 5 ], // required - propertyIndex: matches[ 6 ] - }; - - const lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' ); - - if ( lastDot !== undefined && lastDot !== - 1 ) { - - const objectName = results.nodeName.substring( lastDot + 1 ); - - // Object names must be checked against an allowlist. Otherwise, there - // is no way to parse 'foo.bar.baz': 'baz' must be a property, but - // 'bar' could be the objectName, or part of a nodeName (which can - // include '.' characters). - if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) { - - results.nodeName = results.nodeName.substring( 0, lastDot ); - results.objectName = objectName; - - } - - } - - if ( results.propertyName === null || results.propertyName.length === 0 ) { - - throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName ); - - } - - return results; - - }, - - findNode: function ( root, nodeName ) { - - if ( ! nodeName || nodeName === "" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { - - return root; - - } - - // search into skeleton bones. - if ( root.skeleton ) { - - const bone = root.skeleton.getBoneByName( nodeName ); - - if ( bone !== undefined ) { - - return bone; - - } - - } - - // search into node subtree. - if ( root.children ) { - - const searchNodeSubtree = function ( children ) { - - for ( let i = 0; i < children.length; i ++ ) { - - const childNode = children[ i ]; - - if ( childNode.name === nodeName || childNode.uuid === nodeName ) { - - return childNode; - - } - - const result = searchNodeSubtree( childNode.children ); - - if ( result ) return result; - - } - - return null; - - }; - - const subTreeNode = searchNodeSubtree( root.children ); - - if ( subTreeNode ) { - - return subTreeNode; - - } - - } - - return null; - - } - - } ); - - Object.assign( PropertyBinding.prototype, { // prototype, continued - - // these are used to "bind" a nonexistent property - _getValue_unavailable: function () {}, - _setValue_unavailable: function () {}, - - BindingType: { - Direct: 0, - EntireArray: 1, - ArrayElement: 2, - HasFromToArray: 3 - }, - - Versioning: { - None: 0, - NeedsUpdate: 1, - MatrixWorldNeedsUpdate: 2 - }, - - GetterByBindingType: [ - - function getValue_direct( buffer, offset ) { - - buffer[ offset ] = this.node[ this.propertyName ]; - - }, - - function getValue_array( buffer, offset ) { - - const source = this.resolvedProperty; - - for ( let i = 0, n = source.length; i !== n; ++ i ) { - - buffer[ offset ++ ] = source[ i ]; - - } - - }, - - function getValue_arrayElement( buffer, offset ) { - - buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; - - }, - - function getValue_toArray( buffer, offset ) { - - this.resolvedProperty.toArray( buffer, offset ); - - } - - ], - - SetterByBindingTypeAndVersioning: [ - - [ - // Direct - - function setValue_direct( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - - }, - - function setValue_direct_setNeedsUpdate( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; - - }, - - function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ], [ - - // EntireArray - - function setValue_array( buffer, offset ) { - - const dest = this.resolvedProperty; - - for ( let i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - }, - - function setValue_array_setNeedsUpdate( buffer, offset ) { - - const dest = this.resolvedProperty; - - for ( let i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - this.targetObject.needsUpdate = true; - - }, - - function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { - - const dest = this.resolvedProperty; - - for ( let i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ], [ - - // ArrayElement - - function setValue_arrayElement( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - - }, - - function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; - - }, - - function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ], [ - - // HasToFromArray - - function setValue_fromArray( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - - }, - - function setValue_fromArray_setNeedsUpdate( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.needsUpdate = true; - - }, - - function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ] - - ], - - getValue: function getValue_unbound( targetArray, offset ) { - - this.bind(); - this.getValue( targetArray, offset ); - - // Note: This class uses a State pattern on a per-method basis: - // 'bind' sets 'this.getValue' / 'setValue' and shadows the - // prototype version of these methods with one that represents - // the bound state. When the property is not found, the methods - // become no-ops. - - }, - - setValue: function getValue_unbound( sourceArray, offset ) { - - this.bind(); - this.setValue( sourceArray, offset ); - - }, - - // create getter / setter pair for a property in the scene graph - bind: function () { - - let targetObject = this.node; - const parsedPath = this.parsedPath; - - const objectName = parsedPath.objectName; - const propertyName = parsedPath.propertyName; - let propertyIndex = parsedPath.propertyIndex; - - if ( ! targetObject ) { - - targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; - - this.node = targetObject; - - } - - // set fail state so we can just 'return' on error - this.getValue = this._getValue_unavailable; - this.setValue = this._setValue_unavailable; - - // ensure there is a value node - if ( ! targetObject ) { - - console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.' ); - return; - - } - - if ( objectName ) { - - let objectIndex = parsedPath.objectIndex; - - // special cases were we need to reach deeper into the hierarchy to get the face materials.... - switch ( objectName ) { - - case 'materials': - - if ( ! targetObject.material ) { - - console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this ); - return; - - } - - if ( ! targetObject.material.materials ) { - - console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this ); - return; - - } - - targetObject = targetObject.material.materials; - - break; - - case 'bones': - - if ( ! targetObject.skeleton ) { - - console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this ); - return; - - } - - // potential future optimization: skip this if propertyIndex is already an integer - // and convert the integer string to a true integer. - - targetObject = targetObject.skeleton.bones; - - // support resolving morphTarget names into indices. - for ( let i = 0; i < targetObject.length; i ++ ) { - - if ( targetObject[ i ].name === objectIndex ) { - - objectIndex = i; - break; - - } - - } - - break; - - default: - - if ( targetObject[ objectName ] === undefined ) { - - console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this ); - return; - - } - - targetObject = targetObject[ objectName ]; - - } - - - if ( objectIndex !== undefined ) { - - if ( targetObject[ objectIndex ] === undefined ) { - - console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject ); - return; - - } - - targetObject = targetObject[ objectIndex ]; - - } - - } - - // resolve property - const nodeProperty = targetObject[ propertyName ]; - - if ( nodeProperty === undefined ) { - - const nodeName = parsedPath.nodeName; - - console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName + - '.' + propertyName + ' but it wasn\'t found.', targetObject ); - return; - - } - - // determine versioning scheme - let versioning = this.Versioning.None; - - this.targetObject = targetObject; - - if ( targetObject.needsUpdate !== undefined ) { // material - - versioning = this.Versioning.NeedsUpdate; - - } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform - - versioning = this.Versioning.MatrixWorldNeedsUpdate; - - } - - // determine how the property gets bound - let bindingType = this.BindingType.Direct; - - if ( propertyIndex !== undefined ) { - - // access a sub element of the property array (only primitives are supported right now) - - if ( propertyName === "morphTargetInfluences" ) { - - // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. - - // support resolving morphTarget names into indices. - if ( ! targetObject.geometry ) { - - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this ); - return; - - } - - if ( targetObject.geometry.isBufferGeometry ) { - - if ( ! targetObject.geometry.morphAttributes ) { - - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this ); - return; - - } - - if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) { - - propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ]; - - } - - - } else { - - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.', this ); - return; - - } - - } - - bindingType = this.BindingType.ArrayElement; - - this.resolvedProperty = nodeProperty; - this.propertyIndex = propertyIndex; - - } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { - - // must use copy for Object3D.Euler/Quaternion - - bindingType = this.BindingType.HasFromToArray; - - this.resolvedProperty = nodeProperty; - - } else if ( Array.isArray( nodeProperty ) ) { - - bindingType = this.BindingType.EntireArray; - - this.resolvedProperty = nodeProperty; - - } else { - - this.propertyName = propertyName; - - } - - // select getter / setter - this.getValue = this.GetterByBindingType[ bindingType ]; - this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; - - }, - - unbind: function () { - - this.node = null; - - // back to the prototype version of getValue / setValue - // note: avoiding to mutate the shape of 'this' via 'delete' - this.getValue = this._getValue_unbound; - this.setValue = this._setValue_unbound; - - } - - } ); - - // DECLARE ALIAS AFTER assign prototype - Object.assign( PropertyBinding.prototype, { - - // initial state of these methods that calls 'bind' - _getValue_unbound: PropertyBinding.prototype.getValue, - _setValue_unbound: PropertyBinding.prototype.setValue, - - } ); - - /** - * - * A group of objects that receives a shared animation state. - * - * Usage: - * - * - Add objects you would otherwise pass as 'root' to the - * constructor or the .clipAction method of AnimationMixer. - * - * - Instead pass this object as 'root'. - * - * - You can also add and remove objects later when the mixer - * is running. - * - * Note: - * - * Objects of this class appear as one object to the mixer, - * so cache control of the individual objects must be done - * on the group. - * - * Limitation: - * - * - The animated properties must be compatible among the - * all objects in the group. - * - * - A single property can either be controlled through a - * target group or directly, but not both. - */ - - function AnimationObjectGroup() { - - this.uuid = MathUtils.generateUUID(); - - // cached objects followed by the active ones - this._objects = Array.prototype.slice.call( arguments ); - - this.nCachedObjects_ = 0; // threshold - // note: read by PropertyBinding.Composite - - const indices = {}; - this._indicesByUUID = indices; // for bookkeeping - - for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - - indices[ arguments[ i ].uuid ] = i; - - } - - this._paths = []; // inside: string - this._parsedPaths = []; // inside: { we don't care, here } - this._bindings = []; // inside: Array< PropertyBinding > - this._bindingsIndicesByPath = {}; // inside: indices in these arrays - - const scope = this; - - this.stats = { - - objects: { - get total() { - - return scope._objects.length; - - }, - get inUse() { - - return this.total - scope.nCachedObjects_; - - } - }, - get bindingsPerObject() { - - return scope._bindings.length; - - } - - }; - - } - - Object.assign( AnimationObjectGroup.prototype, { - - isAnimationObjectGroup: true, - - add: function () { - - const objects = this._objects, - indicesByUUID = this._indicesByUUID, - paths = this._paths, - parsedPaths = this._parsedPaths, - bindings = this._bindings, - nBindings = bindings.length; - - let knownObject = undefined, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_; - - for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - - const object = arguments[ i ], - uuid = object.uuid; - let index = indicesByUUID[ uuid ]; - - if ( index === undefined ) { - - // unknown object -> add it to the ACTIVE region - - index = nObjects ++; - indicesByUUID[ uuid ] = index; - objects.push( object ); - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); - - } - - } else if ( index < nCachedObjects ) { - - knownObject = objects[ index ]; - - // move existing object to the ACTIVE region - - const firstActiveIndex = -- nCachedObjects, - lastCachedObject = objects[ firstActiveIndex ]; - - indicesByUUID[ lastCachedObject.uuid ] = index; - objects[ index ] = lastCachedObject; - - indicesByUUID[ uuid ] = firstActiveIndex; - objects[ firstActiveIndex ] = object; - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - const bindingsForPath = bindings[ j ], - lastCached = bindingsForPath[ firstActiveIndex ]; - - let binding = bindingsForPath[ index ]; - - bindingsForPath[ index ] = lastCached; - - if ( binding === undefined ) { - - // since we do not bother to create new bindings - // for objects that are cached, the binding may - // or may not exist - - binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); - - } - - bindingsForPath[ firstActiveIndex ] = binding; - - } - - } else if ( objects[ index ] !== knownObject ) { - - console.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' + - 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' ); - - } // else the object is already where we want it to be - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - remove: function () { - - const objects = this._objects, - indicesByUUID = this._indicesByUUID, - bindings = this._bindings, - nBindings = bindings.length; - - let nCachedObjects = this.nCachedObjects_; - - for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - - const object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; - - if ( index !== undefined && index >= nCachedObjects ) { - - // move existing object into the CACHED region - - const lastCachedIndex = nCachedObjects ++, - firstActiveObject = objects[ lastCachedIndex ]; - - indicesByUUID[ firstActiveObject.uuid ] = index; - objects[ index ] = firstActiveObject; - - indicesByUUID[ uuid ] = lastCachedIndex; - objects[ lastCachedIndex ] = object; - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - const bindingsForPath = bindings[ j ], - firstActive = bindingsForPath[ lastCachedIndex ], - binding = bindingsForPath[ index ]; - - bindingsForPath[ index ] = firstActive; - bindingsForPath[ lastCachedIndex ] = binding; - - } - - } - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - // remove & forget - uncache: function () { - - const objects = this._objects, - indicesByUUID = this._indicesByUUID, - bindings = this._bindings, - nBindings = bindings.length; - - let nCachedObjects = this.nCachedObjects_, - nObjects = objects.length; - - for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - - const object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; - - if ( index !== undefined ) { - - delete indicesByUUID[ uuid ]; - - if ( index < nCachedObjects ) { - - // object is cached, shrink the CACHED region - - const firstActiveIndex = -- nCachedObjects, - lastCachedObject = objects[ firstActiveIndex ], - lastIndex = -- nObjects, - lastObject = objects[ lastIndex ]; - - // last cached object takes this object's place - indicesByUUID[ lastCachedObject.uuid ] = index; - objects[ index ] = lastCachedObject; - - // last object goes to the activated slot and pop - indicesByUUID[ lastObject.uuid ] = firstActiveIndex; - objects[ firstActiveIndex ] = lastObject; - objects.pop(); - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - const bindingsForPath = bindings[ j ], - lastCached = bindingsForPath[ firstActiveIndex ], - last = bindingsForPath[ lastIndex ]; - - bindingsForPath[ index ] = lastCached; - bindingsForPath[ firstActiveIndex ] = last; - bindingsForPath.pop(); - - } - - } else { - - // object is active, just swap with the last and pop - - const lastIndex = -- nObjects, - lastObject = objects[ lastIndex ]; - - indicesByUUID[ lastObject.uuid ] = index; - objects[ index ] = lastObject; - objects.pop(); - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - const bindingsForPath = bindings[ j ]; - - bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; - bindingsForPath.pop(); - - } - - } // cached or active - - } // if object is known - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - // Internal interface used by befriended PropertyBinding.Composite: - - subscribe_: function ( path, parsedPath ) { - - // returns an array of bindings for the given path that is changed - // according to the contained objects in the group - - const indicesByPath = this._bindingsIndicesByPath; - let index = indicesByPath[ path ]; - const bindings = this._bindings; - - if ( index !== undefined ) return bindings[ index ]; - - const paths = this._paths, - parsedPaths = this._parsedPaths, - objects = this._objects, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_, - bindingsForPath = new Array( nObjects ); - - index = bindings.length; - - indicesByPath[ path ] = index; - - paths.push( path ); - parsedPaths.push( parsedPath ); - bindings.push( bindingsForPath ); - - for ( let i = nCachedObjects, n = objects.length; i !== n; ++ i ) { - - const object = objects[ i ]; - bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); - - } - - return bindingsForPath; - - }, - - unsubscribe_: function ( path ) { - - // tells the group to forget about a property path and no longer - // update the array previously obtained with 'subscribe_' - - const indicesByPath = this._bindingsIndicesByPath, - index = indicesByPath[ path ]; - - if ( index !== undefined ) { - - const paths = this._paths, - parsedPaths = this._parsedPaths, - bindings = this._bindings, - lastBindingsIndex = bindings.length - 1, - lastBindings = bindings[ lastBindingsIndex ], - lastBindingsPath = path[ lastBindingsIndex ]; - - indicesByPath[ lastBindingsPath ] = index; - - bindings[ index ] = lastBindings; - bindings.pop(); - - parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; - parsedPaths.pop(); - - paths[ index ] = paths[ lastBindingsIndex ]; - paths.pop(); - - } - - } - - } ); - - class AnimationAction { - - constructor( mixer, clip, localRoot, blendMode ) { - - this._mixer = mixer; - this._clip = clip; - this._localRoot = localRoot || null; - this.blendMode = blendMode || clip.blendMode; - - const tracks = clip.tracks, - nTracks = tracks.length, - interpolants = new Array( nTracks ); - - const interpolantSettings = { - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - }; - - for ( let i = 0; i !== nTracks; ++ i ) { - - const interpolant = tracks[ i ].createInterpolant( null ); - interpolants[ i ] = interpolant; - interpolant.settings = interpolantSettings; - - } - - this._interpolantSettings = interpolantSettings; - - this._interpolants = interpolants; // bound by the mixer - - // inside: PropertyMixer (managed by the mixer) - this._propertyBindings = new Array( nTracks ); - - this._cacheIndex = null; // for the memory manager - this._byClipCacheIndex = null; // for the memory manager - - this._timeScaleInterpolant = null; - this._weightInterpolant = null; - - this.loop = LoopRepeat; - this._loopCount = - 1; - - // global mixer time when the action is to be started - // it's set back to 'null' upon start of the action - this._startTime = null; - - // scaled local time of the action - // gets clamped or wrapped to 0..clip.duration according to loop - this.time = 0; - - this.timeScale = 1; - this._effectiveTimeScale = 1; - - this.weight = 1; - this._effectiveWeight = 1; - - this.repetitions = Infinity; // no. of repetitions when looping - - this.paused = false; // true -> zero effective time scale - this.enabled = true; // false -> zero effective weight - - this.clampWhenFinished = false;// keep feeding the last frame? - - this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate - this.zeroSlopeAtEnd = true;// clips for start, loop and end - - } - - // State & Scheduling - - play() { - - this._mixer._activateAction( this ); - - return this; - - } - - stop() { - - this._mixer._deactivateAction( this ); - - return this.reset(); - - } - - reset() { - - this.paused = false; - this.enabled = true; - - this.time = 0; // restart clip - this._loopCount = - 1;// forget previous loops - this._startTime = null;// forget scheduling - - return this.stopFading().stopWarping(); - - } - - isRunning() { - - return this.enabled && ! this.paused && this.timeScale !== 0 && - this._startTime === null && this._mixer._isActiveAction( this ); - - } - - // return true when play has been called - isScheduled() { - - return this._mixer._isActiveAction( this ); - - } - - startAt( time ) { - - this._startTime = time; - - return this; - - } - - setLoop( mode, repetitions ) { - - this.loop = mode; - this.repetitions = repetitions; - - return this; - - } - - // Weight - - // set the weight stopping any scheduled fading - // although .enabled = false yields an effective weight of zero, this - // method does *not* change .enabled, because it would be confusing - setEffectiveWeight( weight ) { - - this.weight = weight; - - // note: same logic as when updated at runtime - this._effectiveWeight = this.enabled ? weight : 0; - - return this.stopFading(); - - } - - // return the weight considering fading and .enabled - getEffectiveWeight() { - - return this._effectiveWeight; - - } - - fadeIn( duration ) { - - return this._scheduleFading( duration, 0, 1 ); - - } - - fadeOut( duration ) { - - return this._scheduleFading( duration, 1, 0 ); - - } - - crossFadeFrom( fadeOutAction, duration, warp ) { - - fadeOutAction.fadeOut( duration ); - this.fadeIn( duration ); - - if ( warp ) { - - const fadeInDuration = this._clip.duration, - fadeOutDuration = fadeOutAction._clip.duration, - - startEndRatio = fadeOutDuration / fadeInDuration, - endStartRatio = fadeInDuration / fadeOutDuration; - - fadeOutAction.warp( 1.0, startEndRatio, duration ); - this.warp( endStartRatio, 1.0, duration ); - - } - - return this; - - } - - crossFadeTo( fadeInAction, duration, warp ) { - - return fadeInAction.crossFadeFrom( this, duration, warp ); - - } - - stopFading() { - - const weightInterpolant = this._weightInterpolant; - - if ( weightInterpolant !== null ) { - - this._weightInterpolant = null; - this._mixer._takeBackControlInterpolant( weightInterpolant ); - - } - - return this; - - } - - // Time Scale Control - - // set the time scale stopping any scheduled warping - // although .paused = true yields an effective time scale of zero, this - // method does *not* change .paused, because it would be confusing - setEffectiveTimeScale( timeScale ) { - - this.timeScale = timeScale; - this._effectiveTimeScale = this.paused ? 0 : timeScale; - - return this.stopWarping(); - - } - - // return the time scale considering warping and .paused - getEffectiveTimeScale() { - - return this._effectiveTimeScale; - - } - - setDuration( duration ) { - - this.timeScale = this._clip.duration / duration; - - return this.stopWarping(); - - } - - syncWith( action ) { - - this.time = action.time; - this.timeScale = action.timeScale; - - return this.stopWarping(); - - } - - halt( duration ) { - - return this.warp( this._effectiveTimeScale, 0, duration ); - - } - - warp( startTimeScale, endTimeScale, duration ) { - - const mixer = this._mixer, - now = mixer.time, - timeScale = this.timeScale; - - let interpolant = this._timeScaleInterpolant; - - if ( interpolant === null ) { - - interpolant = mixer._lendControlInterpolant(); - this._timeScaleInterpolant = interpolant; - - } - - const times = interpolant.parameterPositions, - values = interpolant.sampleValues; - - times[ 0 ] = now; - times[ 1 ] = now + duration; - - values[ 0 ] = startTimeScale / timeScale; - values[ 1 ] = endTimeScale / timeScale; - - return this; - - } - - stopWarping() { - - const timeScaleInterpolant = this._timeScaleInterpolant; - - if ( timeScaleInterpolant !== null ) { - - this._timeScaleInterpolant = null; - this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); - - } - - return this; - - } - - // Object Accessors - - getMixer() { - - return this._mixer; - - } - - getClip() { - - return this._clip; - - } - - getRoot() { - - return this._localRoot || this._mixer._root; - - } - - // Interna - - _update( time, deltaTime, timeDirection, accuIndex ) { - - // called by the mixer - - if ( ! this.enabled ) { - - // call ._updateWeight() to update ._effectiveWeight - - this._updateWeight( time ); - return; - - } - - const startTime = this._startTime; - - if ( startTime !== null ) { - - // check for scheduled start of action - - const timeRunning = ( time - startTime ) * timeDirection; - if ( timeRunning < 0 || timeDirection === 0 ) { - - return; // yet to come / don't decide when delta = 0 - - } - - // start - - this._startTime = null; // unschedule - deltaTime = timeDirection * timeRunning; - - } - - // apply time scale and advance time - - deltaTime *= this._updateTimeScale( time ); - const clipTime = this._updateTime( deltaTime ); - - // note: _updateTime may disable the action resulting in - // an effective weight of 0 - - const weight = this._updateWeight( time ); - - if ( weight > 0 ) { - - const interpolants = this._interpolants; - const propertyMixers = this._propertyBindings; - - switch ( this.blendMode ) { - - case AdditiveAnimationBlendMode: - - for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { - - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulateAdditive( weight ); - - } - - break; - - case NormalAnimationBlendMode: - default: - - for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { - - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulate( accuIndex, weight ); - - } - - } - - } - - } - - _updateWeight( time ) { - - let weight = 0; - - if ( this.enabled ) { - - weight = this.weight; - const interpolant = this._weightInterpolant; - - if ( interpolant !== null ) { - - const interpolantValue = interpolant.evaluate( time )[ 0 ]; - - weight *= interpolantValue; - - if ( time > interpolant.parameterPositions[ 1 ] ) { - - this.stopFading(); - - if ( interpolantValue === 0 ) { - - // faded out, disable - this.enabled = false; - - } - - } - - } - - } - - this._effectiveWeight = weight; - return weight; - - } - - _updateTimeScale( time ) { - - let timeScale = 0; - - if ( ! this.paused ) { - - timeScale = this.timeScale; - - const interpolant = this._timeScaleInterpolant; - - if ( interpolant !== null ) { - - const interpolantValue = interpolant.evaluate( time )[ 0 ]; - - timeScale *= interpolantValue; - - if ( time > interpolant.parameterPositions[ 1 ] ) { - - this.stopWarping(); - - if ( timeScale === 0 ) { - - // motion has halted, pause - this.paused = true; - - } else { - - // warp done - apply final time scale - this.timeScale = timeScale; - - } - - } - - } - - } - - this._effectiveTimeScale = timeScale; - return timeScale; - - } - - _updateTime( deltaTime ) { - - const duration = this._clip.duration; - const loop = this.loop; - - let time = this.time + deltaTime; - let loopCount = this._loopCount; - - const pingPong = ( loop === LoopPingPong ); - - if ( deltaTime === 0 ) { - - if ( loopCount === - 1 ) return time; - - return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; - - } - - if ( loop === LoopOnce ) { - - if ( loopCount === - 1 ) { - - // just started - - this._loopCount = 0; - this._setEndings( true, true, false ); - - } - - handle_stop: { - - if ( time >= duration ) { - - time = duration; - - } else if ( time < 0 ) { - - time = 0; - - } else { - - this.time = time; - - break handle_stop; - - } - - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; - - this.time = time; - - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime < 0 ? - 1 : 1 - } ); - - } - - } else { // repetitive Repeat or PingPong - - if ( loopCount === - 1 ) { - - // just started - - if ( deltaTime >= 0 ) { - - loopCount = 0; - - this._setEndings( true, this.repetitions === 0, pingPong ); - - } else { - - // when looping in reverse direction, the initial - // transition through zero counts as a repetition, - // so leave loopCount at -1 - - this._setEndings( this.repetitions === 0, true, pingPong ); - - } - - } - - if ( time >= duration || time < 0 ) { - - // wrap around - - const loopDelta = Math.floor( time / duration ); // signed - time -= duration * loopDelta; - - loopCount += Math.abs( loopDelta ); - - const pending = this.repetitions - loopCount; - - if ( pending <= 0 ) { - - // have to stop (switch state, clamp time, fire event) - - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; - - time = deltaTime > 0 ? duration : 0; - - this.time = time; - - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime > 0 ? 1 : - 1 - } ); - - } else { - - // keep running - - if ( pending === 1 ) { - - // entering the last round - - const atStart = deltaTime < 0; - this._setEndings( atStart, ! atStart, pingPong ); - - } else { - - this._setEndings( false, false, pingPong ); - - } - - this._loopCount = loopCount; - - this.time = time; - - this._mixer.dispatchEvent( { - type: 'loop', action: this, loopDelta: loopDelta - } ); - - } - - } else { - - this.time = time; - - } - - if ( pingPong && ( loopCount & 1 ) === 1 ) { - - // invert time for the "pong round" - - return duration - time; - - } - - } - - return time; - - } - - _setEndings( atStart, atEnd, pingPong ) { - - const settings = this._interpolantSettings; - - if ( pingPong ) { - - settings.endingStart = ZeroSlopeEnding; - settings.endingEnd = ZeroSlopeEnding; - - } else { - - // assuming for LoopOnce atStart == atEnd == true - - if ( atStart ) { - - settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; - - } else { - - settings.endingStart = WrapAroundEnding; - - } - - if ( atEnd ) { - - settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; - - } else { - - settings.endingEnd = WrapAroundEnding; - - } - - } - - } - - _scheduleFading( duration, weightNow, weightThen ) { - - const mixer = this._mixer, now = mixer.time; - let interpolant = this._weightInterpolant; - - if ( interpolant === null ) { - - interpolant = mixer._lendControlInterpolant(); - this._weightInterpolant = interpolant; - - } - - const times = interpolant.parameterPositions, - values = interpolant.sampleValues; - - times[ 0 ] = now; - values[ 0 ] = weightNow; - times[ 1 ] = now + duration; - values[ 1 ] = weightThen; - - return this; - - } - - } - - function AnimationMixer( root ) { - - this._root = root; - this._initMemoryManager(); - this._accuIndex = 0; - - this.time = 0; - - this.timeScale = 1.0; - - } - - AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: AnimationMixer, - - _bindAction: function ( action, prototypeAction ) { - - const root = action._localRoot || this._root, - tracks = action._clip.tracks, - nTracks = tracks.length, - bindings = action._propertyBindings, - interpolants = action._interpolants, - rootUuid = root.uuid, - bindingsByRoot = this._bindingsByRootAndName; - - let bindingsByName = bindingsByRoot[ rootUuid ]; - - if ( bindingsByName === undefined ) { - - bindingsByName = {}; - bindingsByRoot[ rootUuid ] = bindingsByName; - - } - - for ( let i = 0; i !== nTracks; ++ i ) { - - const track = tracks[ i ], - trackName = track.name; - - let binding = bindingsByName[ trackName ]; - - if ( binding !== undefined ) { - - bindings[ i ] = binding; - - } else { - - binding = bindings[ i ]; - - if ( binding !== undefined ) { - - // existing binding, make sure the cache knows - - if ( binding._cacheIndex === null ) { - - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); - - } - - continue; - - } - - const path = prototypeAction && prototypeAction. - _propertyBindings[ i ].binding.parsedPath; - - binding = new PropertyMixer( - PropertyBinding.create( root, trackName, path ), - track.ValueTypeName, track.getValueSize() ); - - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); - - bindings[ i ] = binding; - - } - - interpolants[ i ].resultBuffer = binding.buffer; - - } - - }, - - _activateAction: function ( action ) { - - if ( ! this._isActiveAction( action ) ) { - - if ( action._cacheIndex === null ) { - - // this action has been forgotten by the cache, but the user - // appears to be still using it -> rebind - - const rootUuid = ( action._localRoot || this._root ).uuid, - clipUuid = action._clip.uuid, - actionsForClip = this._actionsByClip[ clipUuid ]; - - this._bindAction( action, - actionsForClip && actionsForClip.knownActions[ 0 ] ); - - this._addInactiveAction( action, clipUuid, rootUuid ); - - } - - const bindings = action._propertyBindings; - - // increment reference counts / sort out state - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - - const binding = bindings[ i ]; - - if ( binding.useCount ++ === 0 ) { - - this._lendBinding( binding ); - binding.saveOriginalState(); - - } - - } - - this._lendAction( action ); - - } - - }, - - _deactivateAction: function ( action ) { - - if ( this._isActiveAction( action ) ) { - - const bindings = action._propertyBindings; - - // decrement reference counts / sort out state - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - - const binding = bindings[ i ]; - - if ( -- binding.useCount === 0 ) { - - binding.restoreOriginalState(); - this._takeBackBinding( binding ); - - } - - } - - this._takeBackAction( action ); - - } - - }, - - // Memory manager - - _initMemoryManager: function () { - - this._actions = []; // 'nActiveActions' followed by inactive ones - this._nActiveActions = 0; - - this._actionsByClip = {}; - // inside: - // { - // knownActions: Array< AnimationAction > - used as prototypes - // actionByRoot: AnimationAction - lookup - // } - - - this._bindings = []; // 'nActiveBindings' followed by inactive ones - this._nActiveBindings = 0; - - this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > - - - this._controlInterpolants = []; // same game as above - this._nActiveControlInterpolants = 0; - - const scope = this; - - this.stats = { - - actions: { - get total() { - - return scope._actions.length; - - }, - get inUse() { - - return scope._nActiveActions; - - } - }, - bindings: { - get total() { - - return scope._bindings.length; - - }, - get inUse() { - - return scope._nActiveBindings; - - } - }, - controlInterpolants: { - get total() { - - return scope._controlInterpolants.length; - - }, - get inUse() { - - return scope._nActiveControlInterpolants; - - } - } - - }; - - }, - - // Memory management for AnimationAction objects - - _isActiveAction: function ( action ) { - - const index = action._cacheIndex; - return index !== null && index < this._nActiveActions; - - }, - - _addInactiveAction: function ( action, clipUuid, rootUuid ) { - - const actions = this._actions, - actionsByClip = this._actionsByClip; - - let actionsForClip = actionsByClip[ clipUuid ]; - - if ( actionsForClip === undefined ) { - - actionsForClip = { - - knownActions: [ action ], - actionByRoot: {} - - }; - - action._byClipCacheIndex = 0; - - actionsByClip[ clipUuid ] = actionsForClip; - - } else { - - const knownActions = actionsForClip.knownActions; - - action._byClipCacheIndex = knownActions.length; - knownActions.push( action ); - - } - - action._cacheIndex = actions.length; - actions.push( action ); - - actionsForClip.actionByRoot[ rootUuid ] = action; - - }, - - _removeInactiveAction: function ( action ) { - - const actions = this._actions, - lastInactiveAction = actions[ actions.length - 1 ], - cacheIndex = action._cacheIndex; - - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); - - action._cacheIndex = null; - - - const clipUuid = action._clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ], - knownActionsForClip = actionsForClip.knownActions, - - lastKnownAction = - knownActionsForClip[ knownActionsForClip.length - 1 ], - - byClipCacheIndex = action._byClipCacheIndex; - - lastKnownAction._byClipCacheIndex = byClipCacheIndex; - knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; - knownActionsForClip.pop(); - - action._byClipCacheIndex = null; - - - const actionByRoot = actionsForClip.actionByRoot, - rootUuid = ( action._localRoot || this._root ).uuid; - - delete actionByRoot[ rootUuid ]; - - if ( knownActionsForClip.length === 0 ) { - - delete actionsByClip[ clipUuid ]; - - } - - this._removeInactiveBindingsForAction( action ); - - }, - - _removeInactiveBindingsForAction: function ( action ) { - - const bindings = action._propertyBindings; - - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - - const binding = bindings[ i ]; - - if ( -- binding.referenceCount === 0 ) { - - this._removeInactiveBinding( binding ); - - } - - } - - }, - - _lendAction: function ( action ) { - - // [ active actions | inactive actions ] - // [ active actions >| inactive actions ] - // s a - // <-swap-> - // a s - - const actions = this._actions, - prevIndex = action._cacheIndex, - - lastActiveIndex = this._nActiveActions ++, - - firstInactiveAction = actions[ lastActiveIndex ]; - - action._cacheIndex = lastActiveIndex; - actions[ lastActiveIndex ] = action; - - firstInactiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = firstInactiveAction; - - }, - - _takeBackAction: function ( action ) { - - // [ active actions | inactive actions ] - // [ active actions |< inactive actions ] - // a s - // <-swap-> - // s a - - const actions = this._actions, - prevIndex = action._cacheIndex, - - firstInactiveIndex = -- this._nActiveActions, - - lastActiveAction = actions[ firstInactiveIndex ]; - - action._cacheIndex = firstInactiveIndex; - actions[ firstInactiveIndex ] = action; - - lastActiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = lastActiveAction; - - }, - - // Memory management for PropertyMixer objects - - _addInactiveBinding: function ( binding, rootUuid, trackName ) { - - const bindingsByRoot = this._bindingsByRootAndName, - bindings = this._bindings; - - let bindingByName = bindingsByRoot[ rootUuid ]; - - if ( bindingByName === undefined ) { - - bindingByName = {}; - bindingsByRoot[ rootUuid ] = bindingByName; - - } - - bindingByName[ trackName ] = binding; - - binding._cacheIndex = bindings.length; - bindings.push( binding ); - - }, - - _removeInactiveBinding: function ( binding ) { - - const bindings = this._bindings, - propBinding = binding.binding, - rootUuid = propBinding.rootNode.uuid, - trackName = propBinding.path, - bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ], - - lastInactiveBinding = bindings[ bindings.length - 1 ], - cacheIndex = binding._cacheIndex; - - lastInactiveBinding._cacheIndex = cacheIndex; - bindings[ cacheIndex ] = lastInactiveBinding; - bindings.pop(); - - delete bindingByName[ trackName ]; - - if ( Object.keys( bindingByName ).length === 0 ) { - - delete bindingsByRoot[ rootUuid ]; - - } - - }, - - _lendBinding: function ( binding ) { - - const bindings = this._bindings, - prevIndex = binding._cacheIndex, - - lastActiveIndex = this._nActiveBindings ++, - - firstInactiveBinding = bindings[ lastActiveIndex ]; - - binding._cacheIndex = lastActiveIndex; - bindings[ lastActiveIndex ] = binding; - - firstInactiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = firstInactiveBinding; - - }, - - _takeBackBinding: function ( binding ) { - - const bindings = this._bindings, - prevIndex = binding._cacheIndex, - - firstInactiveIndex = -- this._nActiveBindings, - - lastActiveBinding = bindings[ firstInactiveIndex ]; - - binding._cacheIndex = firstInactiveIndex; - bindings[ firstInactiveIndex ] = binding; - - lastActiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = lastActiveBinding; - - }, - - - // Memory management of Interpolants for weight and time scale - - _lendControlInterpolant: function () { - - const interpolants = this._controlInterpolants, - lastActiveIndex = this._nActiveControlInterpolants ++; - - let interpolant = interpolants[ lastActiveIndex ]; - - if ( interpolant === undefined ) { - - interpolant = new LinearInterpolant( - new Float32Array( 2 ), new Float32Array( 2 ), - 1, this._controlInterpolantsResultBuffer ); - - interpolant.__cacheIndex = lastActiveIndex; - interpolants[ lastActiveIndex ] = interpolant; - - } - - return interpolant; - - }, - - _takeBackControlInterpolant: function ( interpolant ) { - - const interpolants = this._controlInterpolants, - prevIndex = interpolant.__cacheIndex, - - firstInactiveIndex = -- this._nActiveControlInterpolants, - - lastActiveInterpolant = interpolants[ firstInactiveIndex ]; - - interpolant.__cacheIndex = firstInactiveIndex; - interpolants[ firstInactiveIndex ] = interpolant; - - lastActiveInterpolant.__cacheIndex = prevIndex; - interpolants[ prevIndex ] = lastActiveInterpolant; - - }, - - _controlInterpolantsResultBuffer: new Float32Array( 1 ), - - // return an action for a clip optionally using a custom root target - // object (this method allocates a lot of dynamic memory in case a - // previously unknown clip/root combination is specified) - clipAction: function ( clip, optionalRoot, blendMode ) { - - const root = optionalRoot || this._root, - rootUuid = root.uuid; - - let clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip; - - const clipUuid = clipObject !== null ? clipObject.uuid : clip; - - const actionsForClip = this._actionsByClip[ clipUuid ]; - let prototypeAction = null; - - if ( blendMode === undefined ) { - - if ( clipObject !== null ) { - - blendMode = clipObject.blendMode; - - } else { - - blendMode = NormalAnimationBlendMode; - - } - - } - - if ( actionsForClip !== undefined ) { - - const existingAction = actionsForClip.actionByRoot[ rootUuid ]; - - if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { - - return existingAction; - - } - - // we know the clip, so we don't have to parse all - // the bindings again but can just copy - prototypeAction = actionsForClip.knownActions[ 0 ]; - - // also, take the clip from the prototype action - if ( clipObject === null ) - clipObject = prototypeAction._clip; - - } - - // clip must be known when specified via string - if ( clipObject === null ) return null; - - // allocate all resources required to run it - const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode ); - - this._bindAction( newAction, prototypeAction ); - - // and make the action known to the memory manager - this._addInactiveAction( newAction, clipUuid, rootUuid ); - - return newAction; - - }, - - // get an existing action - existingAction: function ( clip, optionalRoot ) { - - const root = optionalRoot || this._root, - rootUuid = root.uuid, - - clipObject = typeof clip === 'string' ? - AnimationClip.findByName( root, clip ) : clip, - - clipUuid = clipObject ? clipObject.uuid : clip, - - actionsForClip = this._actionsByClip[ clipUuid ]; - - if ( actionsForClip !== undefined ) { - - return actionsForClip.actionByRoot[ rootUuid ] || null; - - } - - return null; - - }, - - // deactivates all previously scheduled actions - stopAllAction: function () { - - const actions = this._actions, - nActions = this._nActiveActions; - - for ( let i = nActions - 1; i >= 0; -- i ) { - - actions[ i ].stop(); - - } - - return this; - - }, - - // advance the time and update apply the animation - update: function ( deltaTime ) { - - deltaTime *= this.timeScale; - - const actions = this._actions, - nActions = this._nActiveActions, - - time = this.time += deltaTime, - timeDirection = Math.sign( deltaTime ), - - accuIndex = this._accuIndex ^= 1; - - // run active actions - - for ( let i = 0; i !== nActions; ++ i ) { - - const action = actions[ i ]; - - action._update( time, deltaTime, timeDirection, accuIndex ); - - } - - // update scene graph - - const bindings = this._bindings, - nBindings = this._nActiveBindings; - - for ( let i = 0; i !== nBindings; ++ i ) { - - bindings[ i ].apply( accuIndex ); - - } - - return this; - - }, - - // Allows you to seek to a specific time in an animation. - setTime: function ( timeInSeconds ) { - - this.time = 0; // Zero out time attribute for AnimationMixer object; - for ( let i = 0; i < this._actions.length; i ++ ) { - - this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects. - - } - - return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object. - - }, - - // return this mixer's root target object - getRoot: function () { - - return this._root; - - }, - - // free all resources specific to a particular clip - uncacheClip: function ( clip ) { - - const actions = this._actions, - clipUuid = clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ]; - - if ( actionsForClip !== undefined ) { - - // note: just calling _removeInactiveAction would mess up the - // iteration state and also require updating the state we can - // just throw away - - const actionsToRemove = actionsForClip.knownActions; - - for ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) { - - const action = actionsToRemove[ i ]; - - this._deactivateAction( action ); - - const cacheIndex = action._cacheIndex, - lastInactiveAction = actions[ actions.length - 1 ]; - - action._cacheIndex = null; - action._byClipCacheIndex = null; - - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); - - this._removeInactiveBindingsForAction( action ); - - } - - delete actionsByClip[ clipUuid ]; - - } - - }, - - // free all resources specific to a particular root target object - uncacheRoot: function ( root ) { - - const rootUuid = root.uuid, - actionsByClip = this._actionsByClip; - - for ( const clipUuid in actionsByClip ) { - - const actionByRoot = actionsByClip[ clipUuid ].actionByRoot, - action = actionByRoot[ rootUuid ]; - - if ( action !== undefined ) { - - this._deactivateAction( action ); - this._removeInactiveAction( action ); - - } - - } - - const bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ]; - - if ( bindingByName !== undefined ) { - - for ( const trackName in bindingByName ) { - - const binding = bindingByName[ trackName ]; - binding.restoreOriginalState(); - this._removeInactiveBinding( binding ); - - } - - } - - }, - - // remove a targeted clip from the cache - uncacheAction: function ( clip, optionalRoot ) { - - const action = this.existingAction( clip, optionalRoot ); - - if ( action !== null ) { - - this._deactivateAction( action ); - this._removeInactiveAction( action ); - - } - - } - - } ); - - class Uniform { - - constructor( value ) { - - if ( typeof value === 'string' ) { - - console.warn( 'THREE.Uniform: Type parameter is no longer needed.' ); - value = arguments[ 1 ]; - - } - - this.value = value; - - } - - clone() { - - return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); - - } - - } - - function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) { - - InterleavedBuffer.call( this, array, stride ); - - this.meshPerAttribute = meshPerAttribute || 1; - - } - - InstancedInterleavedBuffer.prototype = Object.assign( Object.create( InterleavedBuffer.prototype ), { - - constructor: InstancedInterleavedBuffer, - - isInstancedInterleavedBuffer: true, - - copy: function ( source ) { - - InterleavedBuffer.prototype.copy.call( this, source ); - - this.meshPerAttribute = source.meshPerAttribute; - - return this; - - }, - - clone: function ( data ) { - - const ib = InterleavedBuffer.prototype.clone.call( this, data ); - - ib.meshPerAttribute = this.meshPerAttribute; - - return ib; - - }, - - toJSON: function ( data ) { - - const json = InterleavedBuffer.prototype.toJSON.call( this, data ); - - json.isInstancedInterleavedBuffer = true; - json.meshPerAttribute = this.meshPerAttribute; - - return json; - - } - - } ); - - /** - * @author raub / https://github.com/raub - */ - - /** - * Element size is one of: - * 5126: 4 - * 5123: 2 - * 5122: 2 - * 5125: 4 - * 5124: 4 - * 5120: 1 - * 5121: 1 - */ - function GLBufferAttribute( buffer, type, itemSize, elementSize, count ) { - - this.buffer = buffer; - this.type = type; - this.itemSize = itemSize; - this.elementSize = elementSize; - this.count = count; - - this.version = 0; - - } - - Object.defineProperty( GLBufferAttribute.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - Object.assign( GLBufferAttribute.prototype, { - - isGLBufferAttribute: true, - - setBuffer: function ( buffer ) { - - this.buffer = buffer; - - return this; - - }, - - setType: function ( type, elementSize ) { - - this.type = type; - this.elementSize = elementSize; - - return this; - - }, - - setItemSize: function ( itemSize ) { - - this.itemSize = itemSize; - - return this; - - }, - - setCount: function ( count ) { - - this.count = count; - - return this; - - }, - - } ); - - function Raycaster( origin, direction, near, far ) { - - this.ray = new Ray( origin, direction ); - // direction is assumed to be normalized (for accurate distance calculations) - - this.near = near || 0; - this.far = far || Infinity; - this.camera = null; - this.layers = new Layers(); - - this.params = { - Mesh: {}, - Line: { threshold: 1 }, - LOD: {}, - Points: { threshold: 1 }, - Sprite: {} - }; - - Object.defineProperties( this.params, { - PointCloud: { - get: function () { - - console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' ); - return this.Points; - - } - } - } ); - - } - - function ascSort( a, b ) { - - return a.distance - b.distance; - - } - - function intersectObject( object, raycaster, intersects, recursive ) { - - if ( object.layers.test( raycaster.layers ) ) { - - object.raycast( raycaster, intersects ); - - } - - if ( recursive === true ) { - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - intersectObject( children[ i ], raycaster, intersects, true ); - - } - - } - - } - - Object.assign( Raycaster.prototype, { - - set: function ( origin, direction ) { - - // direction is assumed to be normalized (for accurate distance calculations) - - this.ray.set( origin, direction ); - - }, - - setFromCamera: function ( coords, camera ) { - - if ( ( camera && camera.isPerspectiveCamera ) ) { - - this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); - this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); - this.camera = camera; - - } else if ( ( camera && camera.isOrthographicCamera ) ) { - - this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera - this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); - this.camera = camera; - - } else { - - console.error( 'THREE.Raycaster: Unsupported camera type.' ); - - } - - }, - - intersectObject: function ( object, recursive, optionalTarget ) { - - const intersects = optionalTarget || []; - - intersectObject( object, this, intersects, recursive ); - - intersects.sort( ascSort ); - - return intersects; - - }, - - intersectObjects: function ( objects, recursive, optionalTarget ) { - - const intersects = optionalTarget || []; - - if ( Array.isArray( objects ) === false ) { - - console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); - return intersects; - - } - - for ( let i = 0, l = objects.length; i < l; i ++ ) { - - intersectObject( objects[ i ], this, intersects, recursive ); - - } - - intersects.sort( ascSort ); - - return intersects; - - } - - } ); - - const _vector$7 = new Vector2(); - - class Box2 { - - constructor( min, max ) { - - Object.defineProperty( this, 'isBox2', { value: true } ); - - this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity ); - - } - - set( min, max ) { - - this.min.copy( min ); - this.max.copy( max ); - - return this; - - } - - setFromPoints( points ) { - - this.makeEmpty(); - - for ( let i = 0, il = points.length; i < il; i ++ ) { - - this.expandByPoint( points[ i ] ); - - } - - return this; - - } - - setFromCenterAndSize( center, size ) { - - const halfSize = _vector$7.copy( size ).multiplyScalar( 0.5 ); - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( box ) { - - this.min.copy( box.min ); - this.max.copy( box.max ); - - return this; - - } - - makeEmpty() { - - this.min.x = this.min.y = + Infinity; - this.max.x = this.max.y = - Infinity; - - return this; - - } - - isEmpty() { - - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); - - } - - getCenter( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box2: .getCenter() target is now required' ); - target = new Vector2(); - - } - - return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); - - } - - getSize( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box2: .getSize() target is now required' ); - target = new Vector2(); - - } - - return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min ); - - } - - expandByPoint( point ) { - - this.min.min( point ); - this.max.max( point ); - - return this; - - } - - expandByVector( vector ) { - - this.min.sub( vector ); - this.max.add( vector ); - - return this; - - } - - expandByScalar( scalar ) { - - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); - - return this; - - } - - containsPoint( point ) { - - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y ? false : true; - - } - - containsBox( box ) { - - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y; - - } - - getParameter( point, target ) { - - // This can potentially have a divide by zero if the box - // has a size dimension of 0. - - if ( target === undefined ) { - - console.warn( 'THREE.Box2: .getParameter() target is now required' ); - target = new Vector2(); - - } - - return target.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ) - ); - - } - - intersectsBox( box ) { - - // using 4 splitting planes to rule out intersections - - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y ? false : true; - - } - - clampPoint( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box2: .clampPoint() target is now required' ); - target = new Vector2(); - - } - - return target.copy( point ).clamp( this.min, this.max ); - - } - - distanceToPoint( point ) { - - const clampedPoint = _vector$7.copy( point ).clamp( this.min, this.max ); - return clampedPoint.sub( point ).length(); - - } - - intersect( box ) { - - this.min.max( box.min ); - this.max.min( box.max ); - - return this; - - } - - union( box ) { - - this.min.min( box.min ); - this.max.max( box.max ); - - return this; - - } - - translate( offset ) { - - this.min.add( offset ); - this.max.add( offset ); - - return this; - - } - - equals( box ) { - - return box.min.equals( this.min ) && box.max.equals( this.max ); - - } - - } - - const _startP = new Vector3(); - const _startEnd = new Vector3(); - - class Line3 { - - constructor( start, end ) { - - this.start = ( start !== undefined ) ? start : new Vector3(); - this.end = ( end !== undefined ) ? end : new Vector3(); - - } - - set( start, end ) { - - this.start.copy( start ); - this.end.copy( end ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( line ) { - - this.start.copy( line.start ); - this.end.copy( line.end ); - - return this; - - } - - getCenter( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .getCenter() target is now required' ); - target = new Vector3(); - - } - - return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); - - } - - delta( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .delta() target is now required' ); - target = new Vector3(); - - } - - return target.subVectors( this.end, this.start ); - - } - - distanceSq() { - - return this.start.distanceToSquared( this.end ); - - } - - distance() { - - return this.start.distanceTo( this.end ); - - } - - at( t, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .at() target is now required' ); - target = new Vector3(); - - } - - return this.delta( target ).multiplyScalar( t ).add( this.start ); - - } - - closestPointToPointParameter( point, clampToLine ) { - - _startP.subVectors( point, this.start ); - _startEnd.subVectors( this.end, this.start ); - - const startEnd2 = _startEnd.dot( _startEnd ); - const startEnd_startP = _startEnd.dot( _startP ); - - let t = startEnd_startP / startEnd2; - - if ( clampToLine ) { - - t = MathUtils.clamp( t, 0, 1 ); - - } - - return t; - - } - - closestPointToPoint( point, clampToLine, target ) { - - const t = this.closestPointToPointParameter( point, clampToLine ); - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } - - return this.delta( target ).multiplyScalar( t ).add( this.start ); - - } - - applyMatrix4( matrix ) { - - this.start.applyMatrix4( matrix ); - this.end.applyMatrix4( matrix ); - - return this; - - } - - equals( line ) { - - return line.start.equals( this.start ) && line.end.equals( this.end ); - - } - - } - - function ImmediateRenderObject( material ) { - - Object3D.call( this ); - - this.material = material; - this.render = function ( /* renderCallback */ ) {}; - - this.hasPositions = false; - this.hasNormals = false; - this.hasColors = false; - this.hasUvs = false; - - this.positionArray = null; - this.normalArray = null; - this.colorArray = null; - this.uvArray = null; - - this.count = 0; - - } - - ImmediateRenderObject.prototype = Object.create( Object3D.prototype ); - ImmediateRenderObject.prototype.constructor = ImmediateRenderObject; - - ImmediateRenderObject.prototype.isImmediateRenderObject = true; - - const _vector$8 = new Vector3(); - - const _vector$9 = new Vector3(); - const _boneMatrix = new Matrix4(); - const _matrixWorldInv = new Matrix4(); - - const _vector$a = new Vector3(); - const _color1 = new Color(); - const _color2 = new Color(); - - const _v1$5 = new Vector3(); - const _v2$3 = new Vector3(); - const _v3$1 = new Vector3(); - - const _vector$b = new Vector3(); - const _camera = new Camera(); - - const _box$3 = new Box3(); - - class Box3Helper extends LineSegments { - - constructor( box, color = 0xffff00 ) { - - const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); - - const positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ]; - - const geometry = new BufferGeometry(); - - geometry.setIndex( new BufferAttribute( indices, 1 ) ); - - geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - - super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); - - this.box = box; - - this.type = 'Box3Helper'; - - this.geometry.computeBoundingSphere(); - - } - - updateMatrixWorld( force ) { - - const box = this.box; - - if ( box.isEmpty() ) return; - - box.getCenter( this.position ); - - box.getSize( this.scale ); - - this.scale.multiplyScalar( 0.5 ); - - super.updateMatrixWorld( force ); - - } - - } - - const _axis = new Vector3(); - - const LOD_MIN = 4; - const LOD_MAX = 8; - - // The standard deviations (radians) associated with the extra mips. These are - // chosen to approximate a Trowbridge-Reitz distribution function times the - // geometric shadowing function. These sigma values squared must match the - // variance #defines in cube_uv_reflection_fragment.glsl.js. - const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; - - const TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; - - const _flatCamera = new OrthographicCamera(); - const { _lodPlanes, _sizeLods, _sigmas } = _createPlanes(); - - // Golden Ratio - const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; - const INV_PHI = 1 / PHI; - - // Vertices of a dodecahedron (except the opposites, which represent the - // same axis), used as axis directions evenly spread on a sphere. - const _axisDirections = [ - new Vector3( 1, 1, 1 ), - new Vector3( - 1, 1, 1 ), - new Vector3( 1, 1, - 1 ), - new Vector3( - 1, 1, - 1 ), - new Vector3( 0, PHI, INV_PHI ), - new Vector3( 0, PHI, - INV_PHI ), - new Vector3( INV_PHI, 0, PHI ), - new Vector3( - INV_PHI, 0, PHI ), - new Vector3( PHI, INV_PHI, 0 ), - new Vector3( - PHI, INV_PHI, 0 ) ]; - - function _createPlanes() { - - const _lodPlanes = []; - const _sizeLods = []; - const _sigmas = []; - - let lod = LOD_MAX; - - for ( let i = 0; i < TOTAL_LODS; i ++ ) { - - const sizeLod = Math.pow( 2, lod ); - _sizeLods.push( sizeLod ); - let sigma = 1.0 / sizeLod; - - if ( i > LOD_MAX - LOD_MIN ) { - - sigma = EXTRA_LOD_SIGMA[ i - LOD_MAX + LOD_MIN - 1 ]; - - } else if ( i == 0 ) { - - sigma = 0; - - } - - _sigmas.push( sigma ); - - const texelSize = 1.0 / ( sizeLod - 1 ); - const min = - texelSize / 2; - const max = 1 + texelSize / 2; - const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ]; - - const cubeFaces = 6; - const vertices = 6; - const positionSize = 3; - const uvSize = 2; - const faceIndexSize = 1; - - const position = new Float32Array( positionSize * vertices * cubeFaces ); - const uv = new Float32Array( uvSize * vertices * cubeFaces ); - const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); - - for ( let face = 0; face < cubeFaces; face ++ ) { - - const x = ( face % 3 ) * 2 / 3 - 1; - const y = face > 2 ? 0 : - 1; - const coordinates = [ - x, y, 0, - x + 2 / 3, y, 0, - x + 2 / 3, y + 1, 0, - x, y, 0, - x + 2 / 3, y + 1, 0, - x, y + 1, 0 - ]; - position.set( coordinates, positionSize * vertices * face ); - uv.set( uv1, uvSize * vertices * face ); - const fill = [ face, face, face, face, face, face ]; - faceIndex.set( fill, faceIndexSize * vertices * face ); - - } - - const planes = new BufferGeometry(); - planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) ); - planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) ); - planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) ); - _lodPlanes.push( planes ); - - if ( lod > LOD_MIN ) { - - lod --; - - } - - } - - return { _lodPlanes, _sizeLods, _sigmas }; - - } - const NoColors = 0; - const VertexColors = 2; - - // - - Curve.create = function ( construct, getPoint ) { - - console.log( 'THREE.Curve.create() has been deprecated' ); - - construct.prototype = Object.create( Curve.prototype ); - construct.prototype.constructor = construct; - construct.prototype.getPoint = getPoint; - - return construct; - - }; - - // - - Object.assign( CurvePath.prototype, { - - createPointsGeometry: function ( divisions ) { - - console.warn( 'THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' ); - - // generate geometry from path points (for Line or Points objects) - - const pts = this.getPoints( divisions ); - return this.createGeometry( pts ); - - }, - - createSpacedPointsGeometry: function ( divisions ) { - - console.warn( 'THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' ); - - // generate geometry from equidistant sampling along the path - - const pts = this.getSpacedPoints( divisions ); - return this.createGeometry( pts ); - - }, - - createGeometry: function ( points ) { - - console.warn( 'THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' ); - - const geometry = new Geometry(); - - for ( let i = 0, l = points.length; i < l; i ++ ) { - - const point = points[ i ]; - geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); - - } - - return geometry; - - } - - } ); - - // - - Object.assign( Path.prototype, { - - fromPoints: function ( points ) { - - console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' ); - return this.setFromPoints( points ); - - } - - } ); - - // - - function Spline( points ) { - - console.warn( 'THREE.Spline has been removed. Use THREE.CatmullRomCurve3 instead.' ); - - CatmullRomCurve3.call( this, points ); - this.type = 'catmullrom'; - - } - - Spline.prototype = Object.create( CatmullRomCurve3.prototype ); - - Object.assign( Spline.prototype, { - - initFromArray: function ( /* a */ ) { - - console.error( 'THREE.Spline: .initFromArray() has been removed.' ); - - }, - getControlPointsArray: function ( /* optionalTarget */ ) { - - console.error( 'THREE.Spline: .getControlPointsArray() has been removed.' ); - - }, - reparametrizeByArcLength: function ( /* samplingCoef */ ) { - - console.error( 'THREE.Spline: .reparametrizeByArcLength() has been removed.' ); - - } - - } ); - - // - - Object.assign( Loader.prototype, { - - extractUrlBase: function ( url ) { - - console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' ); - return LoaderUtils.extractUrlBase( url ); - - } - - } ); - - Loader.Handlers = { - - add: function ( /* regex, loader */ ) { - - console.error( 'THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.' ); - - }, - - get: function ( /* file */ ) { - - console.error( 'THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.' ); - - } - - }; - - Object.assign( ObjectLoader.prototype, { - - setTexturePath: function ( value ) { - - console.warn( 'THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath().' ); - return this.setResourcePath( value ); - - } - - } ); - - // - - Object.assign( Box2.prototype, { - - center: function ( optionalTarget ) { - - console.warn( 'THREE.Box2: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }, - empty: function () { - - console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }, - isIntersectionBox: function ( box ) { - - console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }, - size: function ( optionalTarget ) { - - console.warn( 'THREE.Box2: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); - - } - } ); - - Object.assign( Box3.prototype, { - - center: function ( optionalTarget ) { - - console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }, - empty: function () { - - console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }, - isIntersectionBox: function ( box ) { - - console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }, - isIntersectionSphere: function ( sphere ) { - - console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); - - }, - size: function ( optionalTarget ) { - - console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); - - } - } ); - - Object.assign( Sphere.prototype, { - - empty: function () { - - console.warn( 'THREE.Sphere: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }, - - } ); - - Frustum.prototype.setFromMatrix = function ( m ) { - - console.warn( 'THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().' ); - return this.setFromProjectionMatrix( m ); - - }; - - Line3.prototype.center = function ( optionalTarget ) { - - console.warn( 'THREE.Line3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }; - - Object.assign( MathUtils, { - - random16: function () { - - console.warn( 'THREE.Math: .random16() has been deprecated. Use Math.random() instead.' ); - return Math.random(); - - }, - - nearestPowerOfTwo: function ( value ) { - - console.warn( 'THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo().' ); - return MathUtils.floorPowerOfTwo( value ); - - }, - - nextPowerOfTwo: function ( value ) { - - console.warn( 'THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo().' ); - return MathUtils.ceilPowerOfTwo( value ); - - } - - } ); - - Object.assign( Matrix3.prototype, { - - flattenToArrayOffset: function ( array, offset ) { - - console.warn( "THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); - return this.toArray( array, offset ); - - }, - multiplyVector3: function ( vector ) { - - console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); - return vector.applyMatrix3( this ); - - }, - multiplyVector3Array: function ( /* a */ ) { - - console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' ); - - }, - applyToBufferAttribute: function ( attribute ) { - - console.warn( 'THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.' ); - return attribute.applyMatrix3( this ); - - }, - applyToVector3Array: function ( /* array, offset, length */ ) { - - console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' ); - - } - - } ); - - Object.assign( Matrix4.prototype, { - - extractPosition: function ( m ) { - - console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); - return this.copyPosition( m ); - - }, - flattenToArrayOffset: function ( array, offset ) { - - console.warn( "THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); - return this.toArray( array, offset ); - - }, - getPosition: function () { - - console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); - return new Vector3().setFromMatrixColumn( this, 3 ); - - }, - setRotationFromQuaternion: function ( q ) { - - console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); - return this.makeRotationFromQuaternion( q ); - - }, - multiplyToArray: function () { - - console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' ); - - }, - multiplyVector3: function ( vector ) { - - console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }, - multiplyVector4: function ( vector ) { - - console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }, - multiplyVector3Array: function ( /* a */ ) { - - console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' ); - - }, - rotateAxis: function ( v ) { - - console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); - v.transformDirection( this ); - - }, - crossVector: function ( vector ) { - - console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }, - translate: function () { - - console.error( 'THREE.Matrix4: .translate() has been removed.' ); - - }, - rotateX: function () { - - console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); - - }, - rotateY: function () { - - console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); - - }, - rotateZ: function () { - - console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); - - }, - rotateByAxis: function () { - - console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); - - }, - applyToBufferAttribute: function ( attribute ) { - - console.warn( 'THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.' ); - return attribute.applyMatrix4( this ); - - }, - applyToVector3Array: function ( /* array, offset, length */ ) { - - console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' ); - - }, - makeFrustum: function ( left, right, bottom, top, near, far ) { - - console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' ); - return this.makePerspective( left, right, top, bottom, near, far ); - - } - - } ); - - Plane.prototype.isIntersectionLine = function ( line ) { - - console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); - return this.intersectsLine( line ); - - }; - - Quaternion.prototype.multiplyVector3 = function ( vector ) { - - console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); - return vector.applyQuaternion( this ); - - }; - - Object.assign( Ray.prototype, { - - isIntersectionBox: function ( box ) { - - console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }, - isIntersectionPlane: function ( plane ) { - - console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); - return this.intersectsPlane( plane ); - - }, - isIntersectionSphere: function ( sphere ) { - - console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); - - } - - } ); - - Object.assign( Triangle.prototype, { - - area: function () { - - console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' ); - return this.getArea(); - - }, - barycoordFromPoint: function ( point, target ) { - - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return this.getBarycoord( point, target ); - - }, - midpoint: function ( target ) { - - console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' ); - return this.getMidpoint( target ); - - }, - normal: function ( target ) { - - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return this.getNormal( target ); - - }, - plane: function ( target ) { - - console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' ); - return this.getPlane( target ); - - } - - } ); - - Object.assign( Triangle, { - - barycoordFromPoint: function ( point, a, b, c, target ) { - - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return Triangle.getBarycoord( point, a, b, c, target ); - - }, - normal: function ( a, b, c, target ) { - - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return Triangle.getNormal( a, b, c, target ); - - } - - } ); - - Object.assign( Shape.prototype, { - - extractAllPoints: function ( divisions ) { - - console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' ); - return this.extractPoints( divisions ); - - }, - extrude: function ( options ) { - - console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); - return new ExtrudeGeometry( this, options ); - - }, - makeGeometry: function ( options ) { - - console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); - return new ShapeGeometry( this, options ); - - } - - } ); - - Object.assign( Vector2.prototype, { - - fromAttribute: function ( attribute, index, offset ) { - - console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); - - }, - distanceToManhattan: function ( v ) { - - console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); - - }, - lengthManhattan: function () { - - console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - - } - - } ); - - Object.assign( Vector3.prototype, { - - setEulerFromRotationMatrix: function () { - - console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); - - }, - setEulerFromQuaternion: function () { - - console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); - - }, - getPositionFromMatrix: function ( m ) { - - console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); - return this.setFromMatrixPosition( m ); - - }, - getScaleFromMatrix: function ( m ) { - - console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); - return this.setFromMatrixScale( m ); - - }, - getColumnFromMatrix: function ( index, matrix ) { - - console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); - return this.setFromMatrixColumn( matrix, index ); - - }, - applyProjection: function ( m ) { - - console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' ); - return this.applyMatrix4( m ); - - }, - fromAttribute: function ( attribute, index, offset ) { - - console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); - - }, - distanceToManhattan: function ( v ) { - - console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); - - }, - lengthManhattan: function () { - - console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - - } - - } ); - - Object.assign( Vector4.prototype, { - - fromAttribute: function ( attribute, index, offset ) { - - console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); - - }, - lengthManhattan: function () { - - console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - - } - - } ); - - // - - Object.assign( Geometry.prototype, { - - computeTangents: function () { - - console.error( 'THREE.Geometry: .computeTangents() has been removed.' ); - - }, - computeLineDistances: function () { - - console.error( 'THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead.' ); - - }, - applyMatrix: function ( matrix ) { - - console.warn( 'THREE.Geometry: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); - - } - - } ); - - Object.assign( Object3D.prototype, { - - getChildByName: function ( name ) { - - console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); - return this.getObjectByName( name ); - - }, - renderDepth: function () { - - console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); - - }, - translate: function ( distance, axis ) { - - console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); - return this.translateOnAxis( axis, distance ); - - }, - getWorldRotation: function () { - - console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' ); - - }, - applyMatrix: function ( matrix ) { - - console.warn( 'THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); - - } - - } ); - - Object.defineProperties( Object3D.prototype, { - - eulerOrder: { - get: function () { - - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - return this.rotation.order; - - }, - set: function ( value ) { - - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - this.rotation.order = value; - - } - }, - useQuaternion: { - get: function () { - - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - - }, - set: function () { - - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - - } - } - - } ); - - Object.assign( Mesh.prototype, { - - setDrawMode: function () { - - console.error( 'THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - - }, - - } ); - - Object.defineProperties( Mesh.prototype, { - - drawMode: { - get: function () { - - console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode.' ); - return TrianglesDrawMode; - - }, - set: function () { - - console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - - } - } - - } ); - - Object.defineProperties( LOD.prototype, { - - objects: { - get: function () { - - console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); - return this.levels; - - } - } - - } ); - - Object.defineProperty( Skeleton.prototype, 'useVertexTexture', { - - get: function () { - - console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' ); - - } - - } ); - - SkinnedMesh.prototype.initBones = function () { - - console.error( 'THREE.SkinnedMesh: initBones() has been removed.' ); - - }; - - Object.defineProperty( Curve.prototype, '__arcLengthDivisions', { - - get: function () { - - console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' ); - return this.arcLengthDivisions; - - }, - set: function ( value ) { - - console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' ); - this.arcLengthDivisions = value; - - } - - } ); - - // - - PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { - - console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " + - "Use .setFocalLength and .filmGauge for a photographic setup." ); - - if ( filmGauge !== undefined ) this.filmGauge = filmGauge; - this.setFocalLength( focalLength ); - - }; - - // - - Object.defineProperties( Light.prototype, { - onlyShadow: { - set: function () { - - console.warn( 'THREE.Light: .onlyShadow has been removed.' ); - - } - }, - shadowCameraFov: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); - this.shadow.camera.fov = value; - - } - }, - shadowCameraLeft: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); - this.shadow.camera.left = value; - - } - }, - shadowCameraRight: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); - this.shadow.camera.right = value; - - } - }, - shadowCameraTop: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); - this.shadow.camera.top = value; - - } - }, - shadowCameraBottom: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); - this.shadow.camera.bottom = value; - - } - }, - shadowCameraNear: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); - this.shadow.camera.near = value; - - } - }, - shadowCameraFar: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); - this.shadow.camera.far = value; - - } - }, - shadowCameraVisible: { - set: function () { - - console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); - - } - }, - shadowBias: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); - this.shadow.bias = value; - - } - }, - shadowDarkness: { - set: function () { - - console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); - - } - }, - shadowMapWidth: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); - this.shadow.mapSize.width = value; - - } - }, - shadowMapHeight: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); - this.shadow.mapSize.height = value; - - } - } - } ); - - // - - Object.defineProperties( BufferAttribute.prototype, { - - length: { - get: function () { - - console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' ); - return this.array.length; - - } - }, - dynamic: { - get: function () { - - console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); - return this.usage === DynamicDrawUsage; - - }, - set: function ( /* value */ ) { - - console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); - this.setUsage( DynamicDrawUsage ); - - } - } - - } ); - - Object.assign( BufferAttribute.prototype, { - setDynamic: function ( value ) { - - console.warn( 'THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; - - }, - copyIndicesArray: function ( /* indices */ ) { - - console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' ); - - }, - setArray: function ( /* array */ ) { - - console.error( 'THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - - } - } ); - - Object.assign( BufferGeometry.prototype, { - - addIndex: function ( index ) { - - console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); - this.setIndex( index ); - - }, - addAttribute: function ( name, attribute ) { - - console.warn( 'THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().' ); - - if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { - - console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); - - return this.setAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); - - } - - if ( name === 'index' ) { - - console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); - this.setIndex( attribute ); - - return this; - - } - - return this.setAttribute( name, attribute ); - - }, - addDrawCall: function ( start, count, indexOffset ) { - - if ( indexOffset !== undefined ) { - - console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); - - } - - console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); - this.addGroup( start, count ); - - }, - clearDrawCalls: function () { - - console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); - this.clearGroups(); - - }, - computeTangents: function () { - - console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' ); - - }, - computeOffsets: function () { - - console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); - - }, - removeAttribute: function ( name ) { - - console.warn( 'THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().' ); - - return this.deleteAttribute( name ); - - }, - applyMatrix: function ( matrix ) { - - console.warn( 'THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); - - } - - } ); - - Object.defineProperties( BufferGeometry.prototype, { - - drawcalls: { - get: function () { - - console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); - return this.groups; - - } - }, - offsets: { - get: function () { - - console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); - return this.groups; - - } - } - - } ); - - Object.defineProperties( InstancedBufferGeometry.prototype, { - - maxInstancedCount: { - get: function () { - - console.warn( 'THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount.' ); - return this.instanceCount; - - }, - set: function ( value ) { - - console.warn( 'THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount.' ); - this.instanceCount = value; - - } - } - - } ); - - Object.defineProperties( Raycaster.prototype, { - - linePrecision: { - get: function () { - - console.warn( 'THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead.' ); - return this.params.Line.threshold; - - }, - set: function ( value ) { - - console.warn( 'THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead.' ); - this.params.Line.threshold = value; - - } - } - - } ); - - Object.defineProperties( InterleavedBuffer.prototype, { - - dynamic: { - get: function () { - - console.warn( 'THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead.' ); - return this.usage === DynamicDrawUsage; - - }, - set: function ( value ) { - - console.warn( 'THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead.' ); - this.setUsage( value ); - - } - } - - } ); - - Object.assign( InterleavedBuffer.prototype, { - setDynamic: function ( value ) { - - console.warn( 'THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; - - }, - setArray: function ( /* array */ ) { - - console.error( 'THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - - } - } ); - - // - - Object.assign( ExtrudeBufferGeometry.prototype, { - - getArrays: function () { - - console.error( 'THREE.ExtrudeBufferGeometry: .getArrays() has been removed.' ); - - }, - - addShapeList: function () { - - console.error( 'THREE.ExtrudeBufferGeometry: .addShapeList() has been removed.' ); - - }, - - addShape: function () { - - console.error( 'THREE.ExtrudeBufferGeometry: .addShape() has been removed.' ); - - } - - } ); - - // - - Object.assign( Scene.prototype, { - - dispose: function () { - - console.error( 'THREE.Scene: .dispose() has been removed.' ); - - } - - } ); - - // - - Object.defineProperties( Uniform.prototype, { - - dynamic: { - set: function () { - - console.warn( 'THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.' ); - - } - }, - onUpdate: { - value: function () { - - console.warn( 'THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead.' ); - return this; - - } - } - - } ); - - // - - Object.defineProperties( Material.prototype, { - - wrapAround: { - get: function () { - - console.warn( 'THREE.Material: .wrapAround has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.Material: .wrapAround has been removed.' ); - - } - }, - - overdraw: { - get: function () { - - console.warn( 'THREE.Material: .overdraw has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.Material: .overdraw has been removed.' ); - - } - }, - - wrapRGB: { - get: function () { - - console.warn( 'THREE.Material: .wrapRGB has been removed.' ); - return new Color(); - - } - }, - - shading: { - get: function () { - - console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - - }, - set: function ( value ) { - - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( value === FlatShading ); - - } - }, - - stencilMask: { - get: function () { - - console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); - return this.stencilFuncMask; - - }, - set: function ( value ) { - - console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); - this.stencilFuncMask = value; - - } - } - - } ); - - Object.defineProperties( MeshPhongMaterial.prototype, { - - metal: { - get: function () { - - console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' ); - return false; - - }, - set: function () { - - console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' ); - - } - } - - } ); - - Object.defineProperties( MeshPhysicalMaterial.prototype, { - - transparency: { - get: function () { - - console.warn( 'THREE.MeshPhysicalMaterial: .transparency has been renamed to .transmission.' ); - return this.transmission; - - }, - set: function ( value ) { - - console.warn( 'THREE.MeshPhysicalMaterial: .transparency has been renamed to .transmission.' ); - this.transmission = value; - - } - } - - } ); - - Object.defineProperties( ShaderMaterial.prototype, { - - derivatives: { - get: function () { - - console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - return this.extensions.derivatives; - - }, - set: function ( value ) { - - console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - this.extensions.derivatives = value; - - } - } - - } ); - - // - - Object.assign( WebGLRenderer.prototype, { - - clearTarget: function ( renderTarget, color, depth, stencil ) { - - console.warn( 'THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.' ); - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); - - }, - animate: function ( callback ) { - - console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' ); - this.setAnimationLoop( callback ); - - }, - getCurrentRenderTarget: function () { - - console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' ); - return this.getRenderTarget(); - - }, - getMaxAnisotropy: function () { - - console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' ); - return this.capabilities.getMaxAnisotropy(); - - }, - getPrecision: function () { - - console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' ); - return this.capabilities.precision; - - }, - resetGLState: function () { - - console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' ); - return this.state.reset(); - - }, - supportsFloatTextures: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); - return this.extensions.get( 'OES_texture_float' ); - - }, - supportsHalfFloatTextures: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); - return this.extensions.get( 'OES_texture_half_float' ); - - }, - supportsStandardDerivatives: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); - return this.extensions.get( 'OES_standard_derivatives' ); - - }, - supportsCompressedTextureS3TC: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); - - }, - supportsCompressedTexturePVRTC: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - - }, - supportsBlendMinMax: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); - return this.extensions.get( 'EXT_blend_minmax' ); - - }, - supportsVertexTextures: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); - return this.capabilities.vertexTextures; - - }, - supportsInstancedArrays: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); - return this.extensions.get( 'ANGLE_instanced_arrays' ); - - }, - enableScissorTest: function ( boolean ) { - - console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); - this.setScissorTest( boolean ); - - }, - initMaterial: function () { - - console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); - - }, - addPrePlugin: function () { - - console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); - - }, - addPostPlugin: function () { - - console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); - - }, - updateShadowMap: function () { - - console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); - - }, - setFaceCulling: function () { - - console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' ); - - }, - allocTextureUnit: function () { - - console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' ); - - }, - setTexture: function () { - - console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' ); - - }, - setTexture2D: function () { - - console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' ); - - }, - setTextureCube: function () { - - console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' ); - - }, - getActiveMipMapLevel: function () { - - console.warn( 'THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().' ); - return this.getActiveMipmapLevel(); - - } - - } ); - - Object.defineProperties( WebGLRenderer.prototype, { - - shadowMapEnabled: { - get: function () { - - return this.shadowMap.enabled; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); - this.shadowMap.enabled = value; - - } - }, - shadowMapType: { - get: function () { - - return this.shadowMap.type; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); - this.shadowMap.type = value; - - } - }, - shadowMapCullFace: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function ( /* value */ ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - - } - }, - context: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .context has been removed. Use .getContext() instead.' ); - return this.getContext(); - - } - }, - vr: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .vr has been renamed to .xr' ); - return this.xr; - - } - }, - gammaInput: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); - return false; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); - - } - }, - gammaOutput: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); - return false; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); - this.outputEncoding = ( value === true ) ? sRGBEncoding : LinearEncoding; - - } - }, - toneMappingWhitePoint: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); - return 1.0; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); - - } - }, - - } ); - - Object.defineProperties( WebGLShadowMap.prototype, { - - cullFace: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function ( /* cullFace */ ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - - } - }, - renderReverseSided: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); - - } - }, - renderSingleSided: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - - } - } - - } ); - - // - - Object.defineProperties( WebGLRenderTarget.prototype, { - - wrapS: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - return this.texture.wrapS; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - this.texture.wrapS = value; - - } - }, - wrapT: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - return this.texture.wrapT; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - this.texture.wrapT = value; - - } - }, - magFilter: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - return this.texture.magFilter; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - this.texture.magFilter = value; - - } - }, - minFilter: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - return this.texture.minFilter; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - this.texture.minFilter = value; - - } - }, - anisotropy: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - return this.texture.anisotropy; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - this.texture.anisotropy = value; - - } - }, - offset: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - return this.texture.offset; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - this.texture.offset = value; - - } - }, - repeat: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - return this.texture.repeat; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - this.texture.repeat = value; - - } - }, - format: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - return this.texture.format; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - this.texture.format = value; - - } - }, - type: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - return this.texture.type; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - this.texture.type = value; - - } - }, - generateMipmaps: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - return this.texture.generateMipmaps; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - this.texture.generateMipmaps = value; - - } - } - - } ); - - // - - Object.defineProperties( Audio.prototype, { - - load: { - value: function ( file ) { - - console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); - const scope = this; - const audioLoader = new AudioLoader(); - audioLoader.load( file, function ( buffer ) { - - scope.setBuffer( buffer ); - - } ); - return this; - - } - }, - startTime: { - set: function () { - - console.warn( 'THREE.Audio: .startTime is now .play( delay ).' ); - - } - } - - } ); - - // - - CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) { - - console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' ); - return this.update( renderer, scene ); - - }; - - ImageUtils.crossOrigin = undefined; - - ImageUtils.loadTexture = function ( url, mapping, onLoad, onError ) { - - console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); - - const loader = new TextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); - - const texture = loader.load( url, onLoad, undefined, onError ); - - if ( mapping ) texture.mapping = mapping; - - return texture; - - }; - - ImageUtils.loadTextureCube = function ( urls, mapping, onLoad, onError ) { - - console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); - - const loader = new CubeTextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); - - const texture = loader.load( urls, onLoad, undefined, onError ); - - if ( mapping ) texture.mapping = mapping; - - return texture; - - }; - - ImageUtils.loadCompressedTexture = function () { - - console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); - - }; - - ImageUtils.loadCompressedTextureCube = function () { - - console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); - - }; - - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - - /* eslint-disable no-undef */ - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: { - revision: REVISION, - } } ) ); - /* eslint-enable no-undef */ - - } - - const MERCATOR_A = 6378137.0; - const WORLD_SIZE = MERCATOR_A * Math.PI * 2; - - const ThreeboxConstants = { - WORLD_SIZE: WORLD_SIZE, - PROJECTION_WORLD_SIZE: WORLD_SIZE / (MERCATOR_A * Math.PI * 2), - MERCATOR_A: MERCATOR_A, - DEG2RAD: Math.PI / 180, - RAD2DEG: 180 / Math.PI, - EARTH_CIRCUMFERENCE: 40075000 // In meters - }; - - /* - mapbox-gl uses a camera fixed at the orgin (the middle of the canvas) The camera is only updated when rotated (bearing angle), - pitched or when the map view is resized. - When panning and zooming the map, the desired part of the world is translated and zoomed in front of the camera. The world is only updated when - the map is panned or zoomed. - - The mapbox-gl internal coordinate system has origin (0,0) located at longitude -180 degrees and latitude 0 degrees. - The scaling is 2^map.getZoom() * 512/EARTH_CIRCUMFERENCE_IN_METERS. At zoom=0 (scale=2^0=1), the whole world fits in 512 units. - */ - class CameraSync { - constructor(map, camera, world) { - this.map = map; - this.camera = camera; - this.active = true; - this.updateCallback = null; - this.camera.matrixAutoUpdate = false; // We're in charge of the camera now! - - // Postion and configure the world group so we can scale it appropriately when the camera zooms - this.world = world || new Group(); - this.world.position.x = this.world.position.y = ThreeboxConstants.WORLD_SIZE / 2; - this.world.matrixAutoUpdate = false; - - //set up basic camera state - this.state = { - fov: 0.6435011087932844, // Math.atan(0.75); - translateCenter: new Matrix4(), - worldSizeRatio: 512 / ThreeboxConstants.WORLD_SIZE - }; - - this.state.translateCenter.makeTranslation( - ThreeboxConstants.WORLD_SIZE / 2, - -ThreeboxConstants.WORLD_SIZE / 2, - 0 - ); - - // Listen for move events from the map and update the Three.js camera. Some attributes only change when viewport resizes, so update those accordingly - this.updateCameraBound = () => this.updateCamera(); - this.map.on('move', this.updateCameraBound); - this.setupCameraBound = () => this.setupCamera(); - this.map.on('resize', this.setupCameraBound); - //this.map.on('moveend', ()=>this.updateCallback()) - - this.setupCamera(); - } - detachCamera() { - this.map.off('move', this.updateCameraBound); - this.map.off('resize', this.setupCameraBound); - this.updateCallback = null; - this.map = null; - this.camera = null; - } - setupCamera() { - var t = this.map.transform; - const halfFov = this.state.fov / 2; - var cameraToCenterDistance = (0.5 / Math.tan(halfFov)) * t.height; - - this.state.cameraToCenterDistance = cameraToCenterDistance; - this.state.cameraTranslateZ = new Matrix4().makeTranslation(0, 0, cameraToCenterDistance); - - this.updateCamera(); - } - updateCamera(ev) { - if (!this.camera) { - console.log('nocamera'); - return; - } - - var t = this.map.transform; - - var halfFov = this.state.fov / 2; - const groundAngle = Math.PI / 2 + t._pitch; - this.state.topHalfSurfaceDistance = - (Math.sin(halfFov) * this.state.cameraToCenterDistance) / Math.sin(Math.PI - groundAngle - halfFov); - - // Calculate z distance of the farthest fragment that should be rendered. - const furthestDistance = - Math.cos(Math.PI / 2 - t._pitch) * this.state.topHalfSurfaceDistance + this.state.cameraToCenterDistance; - - // Add a bit extra to avoid precision problems when a fragment's distance is exactly `furthestDistance` - const farZ = furthestDistance * 1.01; - - this.camera.aspect = t.width / t.height; - this.camera.projectionMatrix = this.makePerspectiveMatrix(this.state.fov, t.width / t.height, 1, farZ); - - var cameraWorldMatrix = new Matrix4(); - var rotatePitch = new Matrix4().makeRotationX(t._pitch); - var rotateBearing = new Matrix4().makeRotationZ(t.angle); - - // Unlike the Mapbox GL JS camera, separate camera translation and rotation out into its world matrix - // If this is applied directly to the projection matrix, it will work OK but break raycasting - - cameraWorldMatrix.premultiply(this.state.cameraTranslateZ).premultiply(rotatePitch).premultiply(rotateBearing); - - this.camera.matrixWorld.copy(cameraWorldMatrix); - - // Handle scaling and translation of objects in the map in the world's matrix transform, not the camera - let zoomPow = t.scale * this.state.worldSizeRatio; - let scale = new Matrix4(); - scale.makeScale(zoomPow, zoomPow, zoomPow); - //console.log(`zoomPow: ${zoomPow}`); - - let translateMap = new Matrix4(); - translateMap.makeTranslation(-t.point.x, t.point.y, 0); - - this.world.matrix = new Matrix4(); - this.world.matrix - //.premultiply(rotateMap) - .premultiply(this.state.translateCenter) - .premultiply(scale) - .premultiply(translateMap); - let matrixWorldInverse = new Matrix4(); - matrixWorldInverse.getInverse(this.world.matrix); - - this.camera.projectionMatrixInverse.getInverse(this.camera.projectionMatrix); - this.camera.matrixWorldInverse.getInverse(this.camera.matrixWorld); - this.frustum = new Frustum(); - this.frustum.setFromProjectionMatrix( - new Matrix4().multiplyMatrices(this.camera.projectionMatrix, this.camera.matrixWorldInverse) - ); - - this.cameraPosition = new Vector3(0, 0, 0).unproject(this.camera).applyMatrix4(matrixWorldInverse); - - if (this.updateCallback) { - this.updateCallback(); - } - } - makePerspectiveMatrix(fovy, aspect, near, far) { - let out = new Matrix4(); - let f = 1.0 / Math.tan(fovy / 2), - nf = 1 / (near - far); - - let newMatrix = [f / aspect, 0, 0, 0, - 0, f, 0, 0, - 0, 0, (far + near) * nf, -1, - 0, 0, 2 * far * near * nf, 0]; - - out.elements = newMatrix; - return out; - } - } - - var GLTFLoader = ( function () { - - function GLTFLoader( manager ) { - - Loader.call( this, manager ); - - this.dracoLoader = null; - this.ddsLoader = null; - this.ktx2Loader = null; - - this.pluginCallbacks = []; - - this.register( function ( parser ) { - - return new GLTFMaterialsClearcoatExtension( parser ); - - } ); - this.register( function ( parser ) { - - return new GLTFTextureBasisUExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsTransmissionExtension( parser ); - - } ); - - } - - GLTFLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: GLTFLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var resourcePath; - - if ( this.resourcePath !== '' ) { - - resourcePath = this.resourcePath; - - } else if ( this.path !== '' ) { - - resourcePath = this.path; - - } else { - - resourcePath = LoaderUtils.extractUrlBase( url ); - - } - - // Tells the LoadingManager to track an extra item, which resolves after - // the model is fully loaded. This means the count of items loaded will - // be incorrect, but ensures manager.onLoad() does not fire early. - scope.manager.itemStart( url ); - - var _onError = function ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }; - - var loader = new FileLoader( scope.manager ); - - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - - if ( scope.crossOrigin === 'use-credentials' ) { - - loader.setWithCredentials( true ); - - } - - loader.load( url, function ( data ) { - - try { - - scope.parse( data, resourcePath, function ( gltf ) { - - onLoad( gltf ); - - scope.manager.itemEnd( url ); - - }, _onError ); - - } catch ( e ) { - - _onError( e ); - - } - - }, onProgress, _onError ); - - }, - - setDRACOLoader: function ( dracoLoader ) { - - this.dracoLoader = dracoLoader; - return this; - - }, - - setDDSLoader: function ( ddsLoader ) { - - this.ddsLoader = ddsLoader; - return this; - - }, - - setKTX2Loader: function ( ktx2Loader ) { - - this.ktx2Loader = ktx2Loader; - return this; - - }, - - register: function ( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) { - - this.pluginCallbacks.push( callback ); - - } - - return this; - - }, - - unregister: function ( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) { - - this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 ); - - } - - return this; - - }, - - parse: function ( data, path, onLoad, onError ) { - - var content; - var extensions = {}; - var plugins = {}; - - if ( typeof data === 'string' ) { - - content = data; - - } else { - - var magic = LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) ); - - if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { - - try { - - extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); - - } catch ( error ) { - - if ( onError ) onError( error ); - return; - - } - - content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; - - } else { - - content = LoaderUtils.decodeText( new Uint8Array( data ) ); - - } - - } - - var json = JSON.parse( content ); - - if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) { - - if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) ); - return; - - } - - var parser = new GLTFParser( json, { - - path: path || this.resourcePath || '', - crossOrigin: this.crossOrigin, - manager: this.manager, - ktx2Loader: this.ktx2Loader - - } ); - - parser.fileLoader.setRequestHeader( this.requestHeader ); - - for ( var i = 0; i < this.pluginCallbacks.length; i ++ ) { - - var plugin = this.pluginCallbacks[ i ]( parser ); - plugins[ plugin.name ] = plugin; - - // Workaround to avoid determining as unknown extension - // in addUnknownExtensionsToUserData(). - // Remove this workaround if we move all the existing - // extension handlers to plugin system - extensions[ plugin.name ] = true; - - } - - if ( json.extensionsUsed ) { - - for ( var i = 0; i < json.extensionsUsed.length; ++ i ) { - - var extensionName = json.extensionsUsed[ i ]; - var extensionsRequired = json.extensionsRequired || []; - - switch ( extensionName ) { - - case EXTENSIONS.KHR_LIGHTS_PUNCTUAL: - extensions[ extensionName ] = new GLTFLightsExtension( json ); - break; - - case EXTENSIONS.KHR_MATERIALS_UNLIT: - extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); - break; - - case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: - extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension(); - break; - - case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION: - extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); - break; - - case EXTENSIONS.MSFT_TEXTURE_DDS: - extensions[ extensionName ] = new GLTFTextureDDSExtension( this.ddsLoader ); - break; - - case EXTENSIONS.KHR_TEXTURE_TRANSFORM: - extensions[ extensionName ] = new GLTFTextureTransformExtension(); - break; - - case EXTENSIONS.KHR_MESH_QUANTIZATION: - extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); - break; - - default: - - if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) { - - console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' ); - - } - - } - - } - - } - - parser.setExtensions( extensions ); - parser.setPlugins( plugins ); - parser.parse( onLoad, onError ); - - } - - } ); - - /* GLTFREGISTRY */ - - function GLTFRegistry() { - - var objects = {}; - - return { - - get: function ( key ) { - - return objects[ key ]; - - }, - - add: function ( key, object ) { - - objects[ key ] = object; - - }, - - remove: function ( key ) { - - delete objects[ key ]; - - }, - - removeAll: function () { - - objects = {}; - - } - - }; - - } - - /*********************************/ - /********** EXTENSIONS ***********/ - /*********************************/ - - var EXTENSIONS = { - KHR_BINARY_GLTF: 'KHR_binary_glTF', - KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', - KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', - KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', - KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness', - KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', - KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', - KHR_TEXTURE_BASISU: 'KHR_texture_basisu', - KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', - KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', - MSFT_TEXTURE_DDS: 'MSFT_texture_dds' - }; - - /** - * DDS Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds - * - */ - function GLTFTextureDDSExtension( ddsLoader ) { - - if ( ! ddsLoader ) { - - throw new Error( 'THREE.GLTFLoader: Attempting to load .dds texture without importing DDSLoader' ); - - } - - this.name = EXTENSIONS.MSFT_TEXTURE_DDS; - this.ddsLoader = ddsLoader; - - } - - /** - * Punctual Lights Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual - */ - function GLTFLightsExtension( json ) { - - this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL; - - var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ] ) || {}; - this.lightDefs = extension.lights || []; - - } - - GLTFLightsExtension.prototype.loadLight = function ( lightIndex ) { - - var lightDef = this.lightDefs[ lightIndex ]; - var lightNode; - - var color = new Color( 0xffffff ); - if ( lightDef.color !== undefined ) color.fromArray( lightDef.color ); - - var range = lightDef.range !== undefined ? lightDef.range : 0; - - switch ( lightDef.type ) { - - case 'directional': - lightNode = new DirectionalLight( color ); - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - case 'point': - lightNode = new PointLight( color ); - lightNode.distance = range; - break; - - case 'spot': - lightNode = new SpotLight( color ); - lightNode.distance = range; - // Handle spotlight properties. - lightDef.spot = lightDef.spot || {}; - lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0; - lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0; - lightNode.angle = lightDef.spot.outerConeAngle; - lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle; - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - default: - throw new Error( 'THREE.GLTFLoader: Unexpected light type, "' + lightDef.type + '".' ); - - } - - // Some lights (e.g. spot) default to a position other than the origin. Reset the position - // here, because node-level parsing will only override position if explicitly specified. - lightNode.position.set( 0, 0, 0 ); - - lightNode.decay = 2; - - if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; - - lightNode.name = lightDef.name || ( 'light_' + lightIndex ); - - return Promise.resolve( lightNode ); - - }; - - /** - * Unlit Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit - */ - function GLTFMaterialsUnlitExtension() { - - this.name = EXTENSIONS.KHR_MATERIALS_UNLIT; - - } - - GLTFMaterialsUnlitExtension.prototype.getMaterialType = function () { - - return MeshBasicMaterial; - - }; - - GLTFMaterialsUnlitExtension.prototype.extendParams = function ( materialParams, materialDef, parser ) { - - var pending = []; - - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - var metallicRoughness = materialDef.pbrMetallicRoughness; - - if ( metallicRoughness ) { - - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - - var array = metallicRoughness.baseColorFactor; - - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; - - } - - if ( metallicRoughness.baseColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) ); - - } - - } - - return Promise.all( pending ); - - }; - - /** - * Clearcoat Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat - */ - function GLTFMaterialsClearcoatExtension( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT; - - } - - GLTFMaterialsClearcoatExtension.prototype.getMaterialType = function ( materialIndex ) { - - var parser = this.parser; - var materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - }; - - GLTFMaterialsClearcoatExtension.prototype.extendMaterialParams = function ( materialIndex, materialParams ) { - - var parser = this.parser; - var materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - var pending = []; - - var extension = materialDef.extensions[ this.name ]; - - if ( extension.clearcoatFactor !== undefined ) { - - materialParams.clearcoat = extension.clearcoatFactor; - - } - - if ( extension.clearcoatTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) ); - - } - - if ( extension.clearcoatRoughnessFactor !== undefined ) { - - materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor; - - } - - if ( extension.clearcoatRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) ); - - } - - if ( extension.clearcoatNormalTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) ); - - if ( extension.clearcoatNormalTexture.scale !== undefined ) { - - var scale = extension.clearcoatNormalTexture.scale; - - materialParams.clearcoatNormalScale = new Vector2( scale, scale ); - - } - - } - - return Promise.all( pending ); - - }; - - /** - * Transmission Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission - * Draft: https://github.com/KhronosGroup/glTF/pull/1698 - */ - function GLTFMaterialsTransmissionExtension( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION; - - } - - GLTFMaterialsTransmissionExtension.prototype.getMaterialType = function ( materialIndex ) { - - var parser = this.parser; - var materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - }; - - GLTFMaterialsTransmissionExtension.prototype.extendMaterialParams = function ( materialIndex, materialParams ) { - - var parser = this.parser; - var materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - var pending = []; - - var extension = materialDef.extensions[ this.name ]; - - if ( extension.transmissionFactor !== undefined ) { - - materialParams.transmission = extension.transmissionFactor; - - } - - if ( extension.transmissionTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) ); - - } - - return Promise.all( pending ); - - }; - - /** - * BasisU Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu - * (draft PR https://github.com/KhronosGroup/glTF/pull/1751) - */ - function GLTFTextureBasisUExtension( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_TEXTURE_BASISU; - - } - - GLTFTextureBasisUExtension.prototype.loadTexture = function ( textureIndex ) { - - var parser = this.parser; - var json = parser.json; - - var textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) { - - return null; - - } - - var extension = textureDef.extensions[ this.name ]; - var source = json.images[ extension.source ]; - var loader = parser.options.ktx2Loader; - - if ( ! loader ) { - - throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' ); - - } - - return parser.loadTextureImage( textureIndex, source, loader ); - - }; - - /* BINARY EXTENSION */ - var BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; - var BINARY_EXTENSION_HEADER_LENGTH = 12; - var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; - - function GLTFBinaryExtension( data ) { - - this.name = EXTENSIONS.KHR_BINARY_GLTF; - this.content = null; - this.body = null; - - var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); - - this.header = { - magic: LoaderUtils.decodeText( new Uint8Array( data.slice( 0, 4 ) ) ), - version: headerView.getUint32( 4, true ), - length: headerView.getUint32( 8, true ) - }; - - if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { - - throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); - - } else if ( this.header.version < 2.0 ) { - - throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' ); - - } - - var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); - var chunkIndex = 0; - - while ( chunkIndex < chunkView.byteLength ) { - - var chunkLength = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; - - var chunkType = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; - - if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { - - var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); - this.content = LoaderUtils.decodeText( contentArray ); - - } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { - - var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; - this.body = data.slice( byteOffset, byteOffset + chunkLength ); - - } - - // Clients must ignore chunks with unknown types. - - chunkIndex += chunkLength; - - } - - if ( this.content === null ) { - - throw new Error( 'THREE.GLTFLoader: JSON content not found.' ); - - } - - } - - /** - * DRACO Mesh Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression - */ - function GLTFDracoMeshCompressionExtension( json, dracoLoader ) { - - if ( ! dracoLoader ) { - - throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' ); - - } - - this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION; - this.json = json; - this.dracoLoader = dracoLoader; - this.dracoLoader.preload(); - - } - - GLTFDracoMeshCompressionExtension.prototype.decodePrimitive = function ( primitive, parser ) { - - var json = this.json; - var dracoLoader = this.dracoLoader; - var bufferViewIndex = primitive.extensions[ this.name ].bufferView; - var gltfAttributeMap = primitive.extensions[ this.name ].attributes; - var threeAttributeMap = {}; - var attributeNormalizedMap = {}; - var attributeTypeMap = {}; - - for ( var attributeName in gltfAttributeMap ) { - - var threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - - threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; - - } - - for ( attributeName in primitive.attributes ) { - - var threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - - if ( gltfAttributeMap[ attributeName ] !== undefined ) { - - var accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; - var componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - - attributeTypeMap[ threeAttributeName ] = componentType; - attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; - - } - - } - - return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) { - - return new Promise( function ( resolve ) { - - dracoLoader.decodeDracoFile( bufferView, function ( geometry ) { - - for ( var attributeName in geometry.attributes ) { - - var attribute = geometry.attributes[ attributeName ]; - var normalized = attributeNormalizedMap[ attributeName ]; - - if ( normalized !== undefined ) attribute.normalized = normalized; - - } - - resolve( geometry ); - - }, threeAttributeMap, attributeTypeMap ); - - } ); - - } ); - - }; - - /** - * Texture Transform Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform - */ - function GLTFTextureTransformExtension() { - - this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM; - - } - - GLTFTextureTransformExtension.prototype.extendTexture = function ( texture, transform ) { - - texture = texture.clone(); - - if ( transform.offset !== undefined ) { - - texture.offset.fromArray( transform.offset ); - - } - - if ( transform.rotation !== undefined ) { - - texture.rotation = transform.rotation; - - } - - if ( transform.scale !== undefined ) { - - texture.repeat.fromArray( transform.scale ); - - } - - if ( transform.texCoord !== undefined ) { - - console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' ); - - } - - texture.needsUpdate = true; - - return texture; - - }; - - /** - * Specular-Glossiness Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness - */ - - /** - * A sub class of StandardMaterial with some of the functionality - * changed via the `onBeforeCompile` callback - * @pailhead - */ - - function GLTFMeshStandardSGMaterial( params ) { - - MeshStandardMaterial.call( this ); - - this.isGLTFSpecularGlossinessMaterial = true; - - //various chunks that need replacing - var specularMapParsFragmentChunk = [ - '#ifdef USE_SPECULARMAP', - ' uniform sampler2D specularMap;', - '#endif' - ].join( '\n' ); - - var glossinessMapParsFragmentChunk = [ - '#ifdef USE_GLOSSINESSMAP', - ' uniform sampler2D glossinessMap;', - '#endif' - ].join( '\n' ); - - var specularMapFragmentChunk = [ - 'vec3 specularFactor = specular;', - '#ifdef USE_SPECULARMAP', - ' vec4 texelSpecular = texture2D( specularMap, vUv );', - ' texelSpecular = sRGBToLinear( texelSpecular );', - ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', - ' specularFactor *= texelSpecular.rgb;', - '#endif' - ].join( '\n' ); - - var glossinessMapFragmentChunk = [ - 'float glossinessFactor = glossiness;', - '#ifdef USE_GLOSSINESSMAP', - ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );', - ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture', - ' glossinessFactor *= texelGlossiness.a;', - '#endif' - ].join( '\n' ); - - var lightPhysicalFragmentChunk = [ - 'PhysicalMaterial material;', - 'material.diffuseColor = diffuseColor.rgb;', - 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', - 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );', - 'material.specularRoughness = max( 1.0 - glossinessFactor, 0.0525 );// 0.0525 corresponds to the base mip of a 256 cubemap.', - 'material.specularRoughness += geometryRoughness;', - 'material.specularRoughness = min( material.specularRoughness, 1.0 );', - 'material.specularColor = specularFactor.rgb;', - ].join( '\n' ); - - var uniforms = { - specular: { value: new Color().setHex( 0xffffff ) }, - glossiness: { value: 1 }, - specularMap: { value: null }, - glossinessMap: { value: null } - }; - - this._extraUniforms = uniforms; - - // please see #14031 or #13198 for an alternate approach - this.onBeforeCompile = function ( shader ) { - - for ( var uniformName in uniforms ) { - - shader.uniforms[ uniformName ] = uniforms[ uniformName ]; - - } - - shader.fragmentShader = shader.fragmentShader.replace( 'uniform float roughness;', 'uniform vec3 specular;' ); - shader.fragmentShader = shader.fragmentShader.replace( 'uniform float metalness;', 'uniform float glossiness;' ); - shader.fragmentShader = shader.fragmentShader.replace( '#include ', specularMapParsFragmentChunk ); - shader.fragmentShader = shader.fragmentShader.replace( '#include ', glossinessMapParsFragmentChunk ); - shader.fragmentShader = shader.fragmentShader.replace( '#include ', specularMapFragmentChunk ); - shader.fragmentShader = shader.fragmentShader.replace( '#include ', glossinessMapFragmentChunk ); - shader.fragmentShader = shader.fragmentShader.replace( '#include ', lightPhysicalFragmentChunk ); - - }; - - /*eslint-disable*/ - Object.defineProperties( - this, - { - specular: { - get: function () { return uniforms.specular.value; }, - set: function ( v ) { uniforms.specular.value = v; } - }, - specularMap: { - get: function () { return uniforms.specularMap.value; }, - set: function ( v ) { uniforms.specularMap.value = v; } - }, - glossiness: { - get: function () { return uniforms.glossiness.value; }, - set: function ( v ) { uniforms.glossiness.value = v; } - }, - glossinessMap: { - get: function () { return uniforms.glossinessMap.value; }, - set: function ( v ) { - - uniforms.glossinessMap.value = v; - //how about something like this - @pailhead - if ( v ) { - - this.defines.USE_GLOSSINESSMAP = ''; - // set USE_ROUGHNESSMAP to enable vUv - this.defines.USE_ROUGHNESSMAP = ''; - - } else { - - delete this.defines.USE_ROUGHNESSMAP; - delete this.defines.USE_GLOSSINESSMAP; - - } - - } - } - } - ); - - /*eslint-enable*/ - delete this.metalness; - delete this.roughness; - delete this.metalnessMap; - delete this.roughnessMap; - - this.setValues( params ); - - } - - GLTFMeshStandardSGMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); - GLTFMeshStandardSGMaterial.prototype.constructor = GLTFMeshStandardSGMaterial; - - GLTFMeshStandardSGMaterial.prototype.copy = function ( source ) { - - MeshStandardMaterial.prototype.copy.call( this, source ); - this.specularMap = source.specularMap; - this.specular.copy( source.specular ); - this.glossinessMap = source.glossinessMap; - this.glossiness = source.glossiness; - delete this.metalness; - delete this.roughness; - delete this.metalnessMap; - delete this.roughnessMap; - return this; - - }; - - function GLTFMaterialsPbrSpecularGlossinessExtension() { - - return { - - name: EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS, - - specularGlossinessParams: [ - 'color', - 'map', - 'lightMap', - 'lightMapIntensity', - 'aoMap', - 'aoMapIntensity', - 'emissive', - 'emissiveIntensity', - 'emissiveMap', - 'bumpMap', - 'bumpScale', - 'normalMap', - 'normalMapType', - 'displacementMap', - 'displacementScale', - 'displacementBias', - 'specularMap', - 'specular', - 'glossinessMap', - 'glossiness', - 'alphaMap', - 'envMap', - 'envMapIntensity', - 'refractionRatio', - ], - - getMaterialType: function () { - - return GLTFMeshStandardSGMaterial; - - }, - - extendParams: function ( materialParams, materialDef, parser ) { - - var pbrSpecularGlossiness = materialDef.extensions[ this.name ]; - - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - var pending = []; - - if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) { - - var array = pbrSpecularGlossiness.diffuseFactor; - - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; - - } - - if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', pbrSpecularGlossiness.diffuseTexture ) ); - - } - - materialParams.emissive = new Color( 0.0, 0.0, 0.0 ); - materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0; - materialParams.specular = new Color( 1.0, 1.0, 1.0 ); - - if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) { - - materialParams.specular.fromArray( pbrSpecularGlossiness.specularFactor ); - - } - - if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) { - - var specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture; - pending.push( parser.assignTexture( materialParams, 'glossinessMap', specGlossMapDef ) ); - pending.push( parser.assignTexture( materialParams, 'specularMap', specGlossMapDef ) ); - - } - - return Promise.all( pending ); - - }, - - createMaterial: function ( materialParams ) { - - var material = new GLTFMeshStandardSGMaterial( materialParams ); - material.fog = true; - - material.color = materialParams.color; - - material.map = materialParams.map === undefined ? null : materialParams.map; - - material.lightMap = null; - material.lightMapIntensity = 1.0; - - material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap; - material.aoMapIntensity = 1.0; - - material.emissive = materialParams.emissive; - material.emissiveIntensity = 1.0; - material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap; - - material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap; - material.bumpScale = 1; - - material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap; - material.normalMapType = TangentSpaceNormalMap; - - if ( materialParams.normalScale ) material.normalScale = materialParams.normalScale; - - material.displacementMap = null; - material.displacementScale = 1; - material.displacementBias = 0; - - material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap; - material.specular = materialParams.specular; - - material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap; - material.glossiness = materialParams.glossiness; - - material.alphaMap = null; - - material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap; - material.envMapIntensity = 1.0; - - material.refractionRatio = 0.98; - - return material; - - }, - - }; - - } - - /** - * Mesh Quantization Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization - */ - function GLTFMeshQuantizationExtension() { - - this.name = EXTENSIONS.KHR_MESH_QUANTIZATION; - - } - - /*********************************/ - /********** INTERPOLATION ********/ - /*********************************/ - - // Spline Interpolation - // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation - function GLTFCubicSplineInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - GLTFCubicSplineInterpolant.prototype = Object.create( Interpolant.prototype ); - GLTFCubicSplineInterpolant.prototype.constructor = GLTFCubicSplineInterpolant; - - GLTFCubicSplineInterpolant.prototype.copySampleValue_ = function ( index ) { - - // Copies a sample value to the result buffer. See description of glTF - // CUBICSPLINE values layout in interpolate_() function below. - - var result = this.resultBuffer, - values = this.sampleValues, - valueSize = this.valueSize, - offset = index * valueSize * 3 + valueSize; - - for ( var i = 0; i !== valueSize; i ++ ) { - - result[ i ] = values[ offset + i ]; - - } - - return result; - - }; - - GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; - - GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; - - GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) { - - var result = this.resultBuffer; - var values = this.sampleValues; - var stride = this.valueSize; - - var stride2 = stride * 2; - var stride3 = stride * 3; - - var td = t1 - t0; - - var p = ( t - t0 ) / td; - var pp = p * p; - var ppp = pp * p; - - var offset1 = i1 * stride3; - var offset0 = offset1 - stride3; - - var s2 = - 2 * ppp + 3 * pp; - var s3 = ppp - pp; - var s0 = 1 - s2; - var s1 = s3 - pp + p; - - // Layout of keyframe output values for CUBICSPLINE animations: - // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] - for ( var i = 0; i !== stride; i ++ ) { - - var p0 = values[ offset0 + i + stride ]; // splineVertex_k - var m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k) - var p1 = values[ offset1 + i + stride ]; // splineVertex_k+1 - var m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k) - - result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; - - } - - return result; - - }; - - /*********************************/ - /********** INTERNALS ************/ - /*********************************/ - - /* CONSTANTS */ - - var WEBGL_CONSTANTS = { - FLOAT: 5126, - //FLOAT_MAT2: 35674, - FLOAT_MAT3: 35675, - FLOAT_MAT4: 35676, - FLOAT_VEC2: 35664, - FLOAT_VEC3: 35665, - FLOAT_VEC4: 35666, - LINEAR: 9729, - REPEAT: 10497, - SAMPLER_2D: 35678, - POINTS: 0, - LINES: 1, - LINE_LOOP: 2, - LINE_STRIP: 3, - TRIANGLES: 4, - TRIANGLE_STRIP: 5, - TRIANGLE_FAN: 6, - UNSIGNED_BYTE: 5121, - UNSIGNED_SHORT: 5123 - }; - - var WEBGL_COMPONENT_TYPES = { - 5120: Int8Array, - 5121: Uint8Array, - 5122: Int16Array, - 5123: Uint16Array, - 5125: Uint32Array, - 5126: Float32Array - }; - - var WEBGL_FILTERS = { - 9728: NearestFilter, - 9729: LinearFilter, - 9984: NearestMipmapNearestFilter, - 9985: LinearMipmapNearestFilter, - 9986: NearestMipmapLinearFilter, - 9987: LinearMipmapLinearFilter - }; - - var WEBGL_WRAPPINGS = { - 33071: ClampToEdgeWrapping, - 33648: MirroredRepeatWrapping, - 10497: RepeatWrapping - }; - - var WEBGL_TYPE_SIZES = { - 'SCALAR': 1, - 'VEC2': 2, - 'VEC3': 3, - 'VEC4': 4, - 'MAT2': 4, - 'MAT3': 9, - 'MAT4': 16 - }; - - var ATTRIBUTES = { - POSITION: 'position', - NORMAL: 'normal', - TANGENT: 'tangent', - TEXCOORD_0: 'uv', - TEXCOORD_1: 'uv2', - COLOR_0: 'color', - WEIGHTS_0: 'skinWeight', - JOINTS_0: 'skinIndex', - }; - - var PATH_PROPERTIES = { - scale: 'scale', - translation: 'position', - rotation: 'quaternion', - weights: 'morphTargetInfluences' - }; - - var INTERPOLATION = { - CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each - // keyframe track will be initialized with a default interpolation type, then modified. - LINEAR: InterpolateLinear, - STEP: InterpolateDiscrete - }; - - var ALPHA_MODES = { - OPAQUE: 'OPAQUE', - MASK: 'MASK', - BLEND: 'BLEND' - }; - - /* UTILITY FUNCTIONS */ - - function resolveURL( url, path ) { - - // Invalid URL - if ( typeof url !== 'string' || url === '' ) return ''; - - // Host Relative URL - if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) { - - path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' ); - - } - - // Absolute URL http://,https://,// - if ( /^(https?:)?\/\//i.test( url ) ) return url; - - // Data URI - if ( /^data:.*,.*$/i.test( url ) ) return url; - - // Blob URL - if ( /^blob:.*$/i.test( url ) ) return url; - - // Relative URL - return path + url; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material - */ - function createDefaultMaterial( cache ) { - - if ( cache[ 'DefaultMaterial' ] === undefined ) { - - cache[ 'DefaultMaterial' ] = new MeshStandardMaterial( { - color: 0xFFFFFF, - emissive: 0x000000, - metalness: 1, - roughness: 1, - transparent: false, - depthTest: true, - side: FrontSide - } ); - - } - - return cache[ 'DefaultMaterial' ]; - - } - - function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { - - // Add unknown glTF extensions to an object's userData. - - for ( var name in objectDef.extensions ) { - - if ( knownExtensions[ name ] === undefined ) { - - object.userData.gltfExtensions = object.userData.gltfExtensions || {}; - object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ]; - - } - - } - - } - - /** - * @param {Object3D|Material|BufferGeometry} object - * @param {GLTF.definition} gltfDef - */ - function assignExtrasToUserData( object, gltfDef ) { - - if ( gltfDef.extras !== undefined ) { - - if ( typeof gltfDef.extras === 'object' ) { - - Object.assign( object.userData, gltfDef.extras ); - - } else { - - console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras ); - - } - - } - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets - * - * @param {BufferGeometry} geometry - * @param {Array} targets - * @param {GLTFParser} parser - * @return {Promise} - */ - function addMorphTargets( geometry, targets, parser ) { - - var hasMorphPosition = false; - var hasMorphNormal = false; - - for ( var i = 0, il = targets.length; i < il; i ++ ) { - - var target = targets[ i ]; - - if ( target.POSITION !== undefined ) hasMorphPosition = true; - if ( target.NORMAL !== undefined ) hasMorphNormal = true; - - if ( hasMorphPosition && hasMorphNormal ) break; - - } - - if ( ! hasMorphPosition && ! hasMorphNormal ) return Promise.resolve( geometry ); - - var pendingPositionAccessors = []; - var pendingNormalAccessors = []; - - for ( var i = 0, il = targets.length; i < il; i ++ ) { - - var target = targets[ i ]; - - if ( hasMorphPosition ) { - - var pendingAccessor = target.POSITION !== undefined - ? parser.getDependency( 'accessor', target.POSITION ) - : geometry.attributes.position; - - pendingPositionAccessors.push( pendingAccessor ); - - } - - if ( hasMorphNormal ) { - - var pendingAccessor = target.NORMAL !== undefined - ? parser.getDependency( 'accessor', target.NORMAL ) - : geometry.attributes.normal; - - pendingNormalAccessors.push( pendingAccessor ); - - } - - } - - return Promise.all( [ - Promise.all( pendingPositionAccessors ), - Promise.all( pendingNormalAccessors ) - ] ).then( function ( accessors ) { - - var morphPositions = accessors[ 0 ]; - var morphNormals = accessors[ 1 ]; - - if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions; - if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals; - geometry.morphTargetsRelative = true; - - return geometry; - - } ); - - } - - /** - * @param {Mesh} mesh - * @param {GLTF.Mesh} meshDef - */ - function updateMorphTargets( mesh, meshDef ) { - - mesh.updateMorphTargets(); - - if ( meshDef.weights !== undefined ) { - - for ( var i = 0, il = meshDef.weights.length; i < il; i ++ ) { - - mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; - - } - - } - - // .extras has user-defined data, so check that .extras.targetNames is an array. - if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) { - - var targetNames = meshDef.extras.targetNames; - - if ( mesh.morphTargetInfluences.length === targetNames.length ) { - - mesh.morphTargetDictionary = {}; - - for ( var i = 0, il = targetNames.length; i < il; i ++ ) { - - mesh.morphTargetDictionary[ targetNames[ i ] ] = i; - - } - - } else { - - console.warn( 'THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.' ); - - } - - } - - } - - function createPrimitiveKey( primitiveDef ) { - - var dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]; - var geometryKey; - - if ( dracoExtension ) { - - geometryKey = 'draco:' + dracoExtension.bufferView - + ':' + dracoExtension.indices - + ':' + createAttributesKey( dracoExtension.attributes ); - - } else { - - geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; - - } - - return geometryKey; - - } - - function createAttributesKey( attributes ) { - - var attributesKey = ''; - - var keys = Object.keys( attributes ).sort(); - - for ( var i = 0, il = keys.length; i < il; i ++ ) { - - attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';'; - - } - - return attributesKey; - - } - - /* GLTF PARSER */ - - function GLTFParser( json, options ) { - - this.json = json || {}; - this.extensions = {}; - this.plugins = {}; - this.options = options || {}; - - // loader object cache - this.cache = new GLTFRegistry(); - - // associations between Three.js objects and glTF elements - this.associations = new Map(); - - // BufferGeometry caching - this.primitiveCache = {}; - - // Object3D instance caches - this.meshCache = { refs: {}, uses: {} }; - this.cameraCache = { refs: {}, uses: {} }; - this.lightCache = { refs: {}, uses: {} }; - - // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the - // expensive work of uploading a texture to the GPU off the main thread. - if ( typeof createImageBitmap !== 'undefined' && /Firefox/.test( navigator.userAgent ) === false ) { - - this.textureLoader = new ImageBitmapLoader( this.options.manager ); - - } else { - - this.textureLoader = new TextureLoader( this.options.manager ); - - } - - this.textureLoader.setCrossOrigin( this.options.crossOrigin ); - - this.fileLoader = new FileLoader( this.options.manager ); - this.fileLoader.setResponseType( 'arraybuffer' ); - - if ( this.options.crossOrigin === 'use-credentials' ) { - - this.fileLoader.setWithCredentials( true ); - - } - - } - - GLTFParser.prototype.setExtensions = function ( extensions ) { - - this.extensions = extensions; - - }; - - GLTFParser.prototype.setPlugins = function ( plugins ) { - - this.plugins = plugins; - - }; - - GLTFParser.prototype.parse = function ( onLoad, onError ) { - - var parser = this; - var json = this.json; - var extensions = this.extensions; - - // Clear the loader cache - this.cache.removeAll(); - - // Mark the special nodes/meshes in json for efficient parse - this._markDefs(); - - Promise.all( [ - - this.getDependencies( 'scene' ), - this.getDependencies( 'animation' ), - this.getDependencies( 'camera' ), - - ] ).then( function ( dependencies ) { - - var result = { - scene: dependencies[ 0 ][ json.scene || 0 ], - scenes: dependencies[ 0 ], - animations: dependencies[ 1 ], - cameras: dependencies[ 2 ], - asset: json.asset, - parser: parser, - userData: {} - }; - - addUnknownExtensionsToUserData( extensions, result, json ); - - assignExtrasToUserData( result, json ); - - onLoad( result ); - - } ).catch( onError ); - - }; - - /** - * Marks the special nodes/meshes in json for efficient parse. - */ - GLTFParser.prototype._markDefs = function () { - - var nodeDefs = this.json.nodes || []; - var skinDefs = this.json.skins || []; - var meshDefs = this.json.meshes || []; - - // Nothing in the node definition indicates whether it is a Bone or an - // Object3D. Use the skins' joint references to mark bones. - for ( var skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) { - - var joints = skinDefs[ skinIndex ].joints; - - for ( var i = 0, il = joints.length; i < il; i ++ ) { - - nodeDefs[ joints[ i ] ].isBone = true; - - } - - } - - // Iterate over all nodes, marking references to shared resources, - // as well as skeleton joints. - for ( var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { - - var nodeDef = nodeDefs[ nodeIndex ]; - - if ( nodeDef.mesh !== undefined ) { - - this._addNodeRef( this.meshCache, nodeDef.mesh ); - - // Nothing in the mesh definition indicates whether it is - // a SkinnedMesh or Mesh. Use the node's mesh reference - // to mark SkinnedMesh if node has skin. - if ( nodeDef.skin !== undefined ) { - - meshDefs[ nodeDef.mesh ].isSkinnedMesh = true; - - } - - } - - if ( nodeDef.camera !== undefined ) { - - this._addNodeRef( this.cameraCache, nodeDef.camera ); - - } - - if ( nodeDef.extensions - && nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ] - && nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light !== undefined ) { - - this._addNodeRef( this.lightCache, nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light ); - - } - - } - - }; - - /** - * Counts references to shared node / Object3D resources. These resources - * can be reused, or "instantiated", at multiple nodes in the scene - * hierarchy. Mesh, Camera, and Light instances are instantiated and must - * be marked. Non-scenegraph resources (like Materials, Geometries, and - * Textures) can be reused directly and are not marked here. - * - * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. - */ - GLTFParser.prototype._addNodeRef = function ( cache, index ) { - - if ( index === undefined ) return; - - if ( cache.refs[ index ] === undefined ) { - - cache.refs[ index ] = cache.uses[ index ] = 0; - - } - - cache.refs[ index ] ++; - - }; - - /** Returns a reference to a shared resource, cloning it if necessary. */ - GLTFParser.prototype._getNodeRef = function ( cache, index, object ) { - - if ( cache.refs[ index ] <= 1 ) return object; - - var ref = object.clone(); - - ref.name += '_instance_' + ( cache.uses[ index ] ++ ); - - return ref; - - }; - - GLTFParser.prototype._invokeOne = function ( func ) { - - var extensions = Object.values( this.plugins ); - extensions.push( this ); - - for ( var i = 0; i < extensions.length; i ++ ) { - - var result = func( extensions[ i ] ); - - if ( result ) return result; - - } - - }; - - GLTFParser.prototype._invokeAll = function ( func ) { - - var extensions = Object.values( this.plugins ); - extensions.unshift( this ); - - var pending = []; - - for ( var i = 0; i < extensions.length; i ++ ) { - - pending.push( func( extensions[ i ] ) ); - - } - - return Promise.all( pending ); - - }; - - /** - * Requests the specified dependency asynchronously, with caching. - * @param {string} type - * @param {number} index - * @return {Promise} - */ - GLTFParser.prototype.getDependency = function ( type, index ) { - - var cacheKey = type + ':' + index; - var dependency = this.cache.get( cacheKey ); - - if ( ! dependency ) { - - switch ( type ) { - - case 'scene': - dependency = this.loadScene( index ); - break; - - case 'node': - dependency = this.loadNode( index ); - break; - - case 'mesh': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadMesh && ext.loadMesh( index ); - - } ); - break; - - case 'accessor': - dependency = this.loadAccessor( index ); - break; - - case 'bufferView': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadBufferView && ext.loadBufferView( index ); - - } ); - break; - - case 'buffer': - dependency = this.loadBuffer( index ); - break; - - case 'material': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadMaterial && ext.loadMaterial( index ); - - } ); - break; - - case 'texture': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadTexture && ext.loadTexture( index ); - - } ); - break; - - case 'skin': - dependency = this.loadSkin( index ); - break; - - case 'animation': - dependency = this.loadAnimation( index ); - break; - - case 'camera': - dependency = this.loadCamera( index ); - break; - - case 'light': - dependency = this.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].loadLight( index ); - break; - - default: - throw new Error( 'Unknown type: ' + type ); - - } - - this.cache.add( cacheKey, dependency ); - - } - - return dependency; - - }; - - /** - * Requests all dependencies of the specified type asynchronously, with caching. - * @param {string} type - * @return {Promise>} - */ - GLTFParser.prototype.getDependencies = function ( type ) { - - var dependencies = this.cache.get( type ); - - if ( ! dependencies ) { - - var parser = this; - var defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || []; - - dependencies = Promise.all( defs.map( function ( def, index ) { - - return parser.getDependency( type, index ); - - } ) ); - - this.cache.add( type, dependencies ); - - } - - return dependencies; - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferIndex - * @return {Promise} - */ - GLTFParser.prototype.loadBuffer = function ( bufferIndex ) { - - var bufferDef = this.json.buffers[ bufferIndex ]; - var loader = this.fileLoader; - - if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) { - - throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' ); - - } - - // If present, GLB container is required to be the first buffer. - if ( bufferDef.uri === undefined && bufferIndex === 0 ) { - - return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body ); - - } - - var options = this.options; - - return new Promise( function ( resolve, reject ) { - - loader.load( resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () { - - reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) ); - - } ); - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferViewIndex - * @return {Promise} - */ - GLTFParser.prototype.loadBufferView = function ( bufferViewIndex ) { - - var bufferViewDef = this.json.bufferViews[ bufferViewIndex ]; - - return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) { - - var byteLength = bufferViewDef.byteLength || 0; - var byteOffset = bufferViewDef.byteOffset || 0; - return buffer.slice( byteOffset, byteOffset + byteLength ); - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors - * @param {number} accessorIndex - * @return {Promise} - */ - GLTFParser.prototype.loadAccessor = function ( accessorIndex ) { - - var parser = this; - var json = this.json; - - var accessorDef = this.json.accessors[ accessorIndex ]; - - if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { - - // Ignore empty accessors, which may be used to declare runtime - // information about attributes coming from another source (e.g. Draco - // compression extension). - return Promise.resolve( null ); - - } - - var pendingBufferViews = []; - - if ( accessorDef.bufferView !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) ); - - } else { - - pendingBufferViews.push( null ); - - } - - if ( accessorDef.sparse !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) ); - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) ); - - } - - return Promise.all( pendingBufferViews ).then( function ( bufferViews ) { - - var bufferView = bufferViews[ 0 ]; - - var itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; - var TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - - // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. - var elementBytes = TypedArray.BYTES_PER_ELEMENT; - var itemBytes = elementBytes * itemSize; - var byteOffset = accessorDef.byteOffset || 0; - var byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined; - var normalized = accessorDef.normalized === true; - var array, bufferAttribute; - - // The buffer is not interleaved if the stride is the item size in bytes. - if ( byteStride && byteStride !== itemBytes ) { - - // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer - // This makes sure that IBA.count reflects accessor.count properly - var ibSlice = Math.floor( byteOffset / byteStride ); - var ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count; - var ib = parser.cache.get( ibCacheKey ); - - if ( ! ib ) { - - array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes ); - - // Integer parameters to IB/IBA are in array elements, not bytes. - ib = new InterleavedBuffer( array, byteStride / elementBytes ); - - parser.cache.add( ibCacheKey, ib ); - - } - - bufferAttribute = new InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized ); - - } else { - - if ( bufferView === null ) { - - array = new TypedArray( accessorDef.count * itemSize ); - - } else { - - array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize ); - - } - - bufferAttribute = new BufferAttribute( array, itemSize, normalized ); - - } - - // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors - if ( accessorDef.sparse !== undefined ) { - - var itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR; - var TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ]; - - var byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; - var byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; - - var sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices ); - var sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize ); - - if ( bufferView !== null ) { - - // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. - bufferAttribute = new BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized ); - - } - - for ( var i = 0, il = sparseIndices.length; i < il; i ++ ) { - - var index = sparseIndices[ i ]; - - bufferAttribute.setX( index, sparseValues[ i * itemSize ] ); - if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] ); - if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] ); - if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] ); - if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.' ); - - } - - } - - return bufferAttribute; - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures - * @param {number} textureIndex - * @return {Promise} - */ - GLTFParser.prototype.loadTexture = function ( textureIndex ) { - - var parser = this; - var json = this.json; - var options = this.options; - - var textureDef = json.textures[ textureIndex ]; - - var textureExtensions = textureDef.extensions || {}; - - var source; - - if ( textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] ) { - - source = json.images[ textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ].source ]; - - } else { - - source = json.images[ textureDef.source ]; - - } - - var loader; - - if ( source.uri ) { - - loader = options.manager.getHandler( source.uri ); - - } - - if ( ! loader ) { - - loader = textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] - ? parser.extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ].ddsLoader - : this.textureLoader; - - } - - return this.loadTextureImage( textureIndex, source, loader ); - - }; - - GLTFParser.prototype.loadTextureImage = function ( textureIndex, source, loader ) { - - var parser = this; - var json = this.json; - var options = this.options; - - var textureDef = json.textures[ textureIndex ]; - - var URL = self.URL || self.webkitURL; - - var sourceURI = source.uri; - var isObjectURL = false; - var hasAlpha = true; - - if ( source.mimeType === 'image/jpeg' ) hasAlpha = false; - - if ( source.bufferView !== undefined ) { - - // Load binary image data from bufferView, if provided. - - sourceURI = parser.getDependency( 'bufferView', source.bufferView ).then( function ( bufferView ) { - - if ( source.mimeType === 'image/png' ) { - - // https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header - hasAlpha = new DataView( bufferView, 25, 1 ).getUint8( 0, false ) === 6; - - } - - isObjectURL = true; - var blob = new Blob( [ bufferView ], { type: source.mimeType } ); - sourceURI = URL.createObjectURL( blob ); - return sourceURI; - - } ); - - } - - return Promise.resolve( sourceURI ).then( function ( sourceURI ) { - - return new Promise( function ( resolve, reject ) { - - var onLoad = resolve; - - if ( loader.isImageBitmapLoader === true ) { - - onLoad = function ( imageBitmap ) { - - resolve( new CanvasTexture( imageBitmap ) ); - - }; - - } - - loader.load( resolveURL( sourceURI, options.path ), onLoad, undefined, reject ); - - } ); - - } ).then( function ( texture ) { - - // Clean up resources and configure Texture. - - if ( isObjectURL === true ) { - - URL.revokeObjectURL( sourceURI ); - - } - - texture.flipY = false; - - if ( textureDef.name ) texture.name = textureDef.name; - - // When there is definitely no alpha channel in the texture, set RGBFormat to save space. - if ( ! hasAlpha ) texture.format = RGBFormat; - - var samplers = json.samplers || {}; - var sampler = samplers[ textureDef.sampler ] || {}; - - texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || LinearFilter; - texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || LinearMipmapLinearFilter; - texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || RepeatWrapping; - texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || RepeatWrapping; - - parser.associations.set( texture, { - type: 'textures', - index: textureIndex - } ); - - return texture; - - } ); - - }; - - /** - * Asynchronously assigns a texture to the given material parameters. - * @param {Object} materialParams - * @param {string} mapName - * @param {Object} mapDef - * @return {Promise} - */ - GLTFParser.prototype.assignTexture = function ( materialParams, mapName, mapDef ) { - - var parser = this; - - return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) { - - // Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured - // However, we will copy UV set 0 to UV set 1 on demand for aoMap - if ( mapDef.texCoord !== undefined && mapDef.texCoord != 0 && ! ( mapName === 'aoMap' && mapDef.texCoord == 1 ) ) { - - console.warn( 'THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.' ); - - } - - if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) { - - var transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined; - - if ( transform ) { - - var gltfReference = parser.associations.get( texture ); - texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); - parser.associations.set( texture, gltfReference ); - - } - - } - - materialParams[ mapName ] = texture; - - } ); - - }; - - /** - * Assigns final material to a Mesh, Line, or Points instance. The instance - * already has a material (generated from the glTF material options alone) - * but reuse of the same glTF material may require multiple threejs materials - * to accomodate different primitive types, defines, etc. New materials will - * be created if necessary, and reused from a cache. - * @param {Object3D} mesh Mesh, Line, or Points instance. - */ - GLTFParser.prototype.assignFinalMaterial = function ( mesh ) { - - var geometry = mesh.geometry; - var material = mesh.material; - - var useVertexTangents = geometry.attributes.tangent !== undefined; - var useVertexColors = geometry.attributes.color !== undefined; - var useFlatShading = geometry.attributes.normal === undefined; - var useSkinning = mesh.isSkinnedMesh === true; - var useMorphTargets = Object.keys( geometry.morphAttributes ).length > 0; - var useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined; - - if ( mesh.isPoints ) { - - var cacheKey = 'PointsMaterial:' + material.uuid; - - var pointsMaterial = this.cache.get( cacheKey ); - - if ( ! pointsMaterial ) { - - pointsMaterial = new PointsMaterial(); - Material.prototype.copy.call( pointsMaterial, material ); - pointsMaterial.color.copy( material.color ); - pointsMaterial.map = material.map; - pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px - - this.cache.add( cacheKey, pointsMaterial ); - - } - - material = pointsMaterial; - - } else if ( mesh.isLine ) { - - var cacheKey = 'LineBasicMaterial:' + material.uuid; - - var lineMaterial = this.cache.get( cacheKey ); - - if ( ! lineMaterial ) { - - lineMaterial = new LineBasicMaterial(); - Material.prototype.copy.call( lineMaterial, material ); - lineMaterial.color.copy( material.color ); - - this.cache.add( cacheKey, lineMaterial ); - - } - - material = lineMaterial; - - } - - // Clone the material if it will be modified - if ( useVertexTangents || useVertexColors || useFlatShading || useSkinning || useMorphTargets ) { - - var cacheKey = 'ClonedMaterial:' + material.uuid + ':'; - - if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:'; - if ( useSkinning ) cacheKey += 'skinning:'; - if ( useVertexTangents ) cacheKey += 'vertex-tangents:'; - if ( useVertexColors ) cacheKey += 'vertex-colors:'; - if ( useFlatShading ) cacheKey += 'flat-shading:'; - if ( useMorphTargets ) cacheKey += 'morph-targets:'; - if ( useMorphNormals ) cacheKey += 'morph-normals:'; - - var cachedMaterial = this.cache.get( cacheKey ); - - if ( ! cachedMaterial ) { - - cachedMaterial = material.clone(); - - if ( useSkinning ) cachedMaterial.skinning = true; - if ( useVertexTangents ) cachedMaterial.vertexTangents = true; - if ( useVertexColors ) cachedMaterial.vertexColors = true; - if ( useFlatShading ) cachedMaterial.flatShading = true; - if ( useMorphTargets ) cachedMaterial.morphTargets = true; - if ( useMorphNormals ) cachedMaterial.morphNormals = true; - - this.cache.add( cacheKey, cachedMaterial ); - - this.associations.set( cachedMaterial, this.associations.get( material ) ); - - } - - material = cachedMaterial; - - } - - // workarounds for mesh and geometry - - if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) { - - geometry.setAttribute( 'uv2', geometry.attributes.uv ); - - } - - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - if ( material.normalScale && ! useVertexTangents ) { - - material.normalScale.y = - material.normalScale.y; - - } - - if ( material.clearcoatNormalScale && ! useVertexTangents ) { - - material.clearcoatNormalScale.y = - material.clearcoatNormalScale.y; - - } - - mesh.material = material; - - }; - - GLTFParser.prototype.getMaterialType = function ( /* materialIndex */ ) { - - return MeshStandardMaterial; - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials - * @param {number} materialIndex - * @return {Promise} - */ - GLTFParser.prototype.loadMaterial = function ( materialIndex ) { - - var parser = this; - var json = this.json; - var extensions = this.extensions; - var materialDef = json.materials[ materialIndex ]; - - var materialType; - var materialParams = {}; - var materialExtensions = materialDef.extensions || {}; - - var pending = []; - - if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) { - - var sgExtension = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ]; - materialType = sgExtension.getMaterialType(); - pending.push( sgExtension.extendParams( materialParams, materialDef, parser ) ); - - } else if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) { - - var kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ]; - materialType = kmuExtension.getMaterialType(); - pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); - - } else { - - // Specification: - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material - - var metallicRoughness = materialDef.pbrMetallicRoughness || {}; - - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - - var array = metallicRoughness.baseColorFactor; - - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; - - } - - if ( metallicRoughness.baseColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) ); - - } - - materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; - materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; - - if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) ); - pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) ); - - } - - materialType = this._invokeOne( function ( ext ) { - - return ext.getMaterialType && ext.getMaterialType( materialIndex ); - - } ); - - pending.push( this._invokeAll( function ( ext ) { - - return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams ); - - } ) ); - - } - - if ( materialDef.doubleSided === true ) { - - materialParams.side = DoubleSide; - - } - - var alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; - - if ( alphaMode === ALPHA_MODES.BLEND ) { - - materialParams.transparent = true; - - // See: https://github.com/mrdoob/three.js/issues/17706 - materialParams.depthWrite = false; - - } else { - - materialParams.transparent = false; - - if ( alphaMode === ALPHA_MODES.MASK ) { - - materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; - - } - - } - - if ( materialDef.normalTexture !== undefined && materialType !== MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); - - materialParams.normalScale = new Vector2( 1, 1 ); - - if ( materialDef.normalTexture.scale !== undefined ) { - - materialParams.normalScale.set( materialDef.normalTexture.scale, materialDef.normalTexture.scale ); - - } - - } - - if ( materialDef.occlusionTexture !== undefined && materialType !== MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) ); - - if ( materialDef.occlusionTexture.strength !== undefined ) { - - materialParams.aoMapIntensity = materialDef.occlusionTexture.strength; - - } - - } - - if ( materialDef.emissiveFactor !== undefined && materialType !== MeshBasicMaterial ) { - - materialParams.emissive = new Color().fromArray( materialDef.emissiveFactor ); - - } - - if ( materialDef.emissiveTexture !== undefined && materialType !== MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture ) ); - - } - - return Promise.all( pending ).then( function () { - - var material; - - if ( materialType === GLTFMeshStandardSGMaterial ) { - - material = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams ); - - } else { - - material = new materialType( materialParams ); - - } - - if ( materialDef.name ) material.name = materialDef.name; - - // baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding. - if ( material.map ) material.map.encoding = sRGBEncoding; - if ( material.emissiveMap ) material.emissiveMap.encoding = sRGBEncoding; - - assignExtrasToUserData( material, materialDef ); - - parser.associations.set( material, { type: 'materials', index: materialIndex } ); - - if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); - - return material; - - } ); - - }; - - /** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - */ - function computeBounds( geometry, primitiveDef, parser ) { - - var attributes = primitiveDef.attributes; - - var box = new Box3(); - - if ( attributes.POSITION !== undefined ) { - - var accessor = parser.json.accessors[ attributes.POSITION ]; - - var min = accessor.min; - var max = accessor.max; - - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - - if ( min !== undefined && max !== undefined ) { - - box.set( - new Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ), - new Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) ); - - } else { - - console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); - - return; - - } - - } else { - - return; - - } - - var targets = primitiveDef.targets; - - if ( targets !== undefined ) { - - var maxDisplacement = new Vector3(); - var vector = new Vector3(); - - for ( var i = 0, il = targets.length; i < il; i ++ ) { - - var target = targets[ i ]; - - if ( target.POSITION !== undefined ) { - - var accessor = parser.json.accessors[ target.POSITION ]; - var min = accessor.min; - var max = accessor.max; - - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - - if ( min !== undefined && max !== undefined ) { - - // we need to get max of absolute components because target weight is [-1,1] - vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) ); - vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) ); - vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) ); - - // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative - // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets - // are used to implement key-frame animations and as such only two are active at a time - this results in very large - // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. - maxDisplacement.max( vector ); - - } else { - - console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); - - } - - } - - } - - // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. - box.expandByVector( maxDisplacement ); - - } - - geometry.boundingBox = box; - - var sphere = new Sphere(); - - box.getCenter( sphere.center ); - sphere.radius = box.min.distanceTo( box.max ) / 2; - - geometry.boundingSphere = sphere; - - } - - /** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - * @return {Promise} - */ - function addPrimitiveAttributes( geometry, primitiveDef, parser ) { - - var attributes = primitiveDef.attributes; - - var pending = []; - - function assignAttributeAccessor( accessorIndex, attributeName ) { - - return parser.getDependency( 'accessor', accessorIndex ) - .then( function ( accessor ) { - - geometry.setAttribute( attributeName, accessor ); - - } ); - - } - - for ( var gltfAttributeName in attributes ) { - - var threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); - - // Skip attributes already provided by e.g. Draco extension. - if ( threeAttributeName in geometry.attributes ) continue; - - pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) ); - - } - - if ( primitiveDef.indices !== undefined && ! geometry.index ) { - - var accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) { - - geometry.setIndex( accessor ); - - } ); - - pending.push( accessor ); - - } - - assignExtrasToUserData( geometry, primitiveDef ); - - computeBounds( geometry, primitiveDef, parser ); - - return Promise.all( pending ).then( function () { - - return primitiveDef.targets !== undefined - ? addMorphTargets( geometry, primitiveDef.targets, parser ) - : geometry; - - } ); - - } - - /** - * @param {BufferGeometry} geometry - * @param {Number} drawMode - * @return {BufferGeometry} - */ - function toTrianglesDrawMode( geometry, drawMode ) { - - var index = geometry.getIndex(); - - // generate index if not present - - if ( index === null ) { - - var indices = []; - - var position = geometry.getAttribute( 'position' ); - - if ( position !== undefined ) { - - for ( var i = 0; i < position.count; i ++ ) { - - indices.push( i ); - - } - - geometry.setIndex( indices ); - index = geometry.getIndex(); - - } else { - - console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' ); - return geometry; - - } - - } - - // - - var numberOfTriangles = index.count - 2; - var newIndices = []; - - if ( drawMode === TriangleFanDrawMode ) { - - // gl.TRIANGLE_FAN - - for ( var i = 1; i <= numberOfTriangles; i ++ ) { - - newIndices.push( index.getX( 0 ) ); - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - - } - - } else { - - // gl.TRIANGLE_STRIP - - for ( var i = 0; i < numberOfTriangles; i ++ ) { - - if ( i % 2 === 0 ) { - - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i + 2 ) ); - - - } else { - - newIndices.push( index.getX( i + 2 ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i ) ); - - } - - } - - } - - if ( ( newIndices.length / 3 ) !== numberOfTriangles ) { - - console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' ); - - } - - // build final geometry - - var newGeometry = geometry.clone(); - newGeometry.setIndex( newIndices ); - - return newGeometry; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry - * - * Creates BufferGeometries from primitives. - * - * @param {Array} primitives - * @return {Promise>} - */ - GLTFParser.prototype.loadGeometries = function ( primitives ) { - - var parser = this; - var extensions = this.extensions; - var cache = this.primitiveCache; - - function createDracoPrimitive( primitive ) { - - return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] - .decodePrimitive( primitive, parser ) - .then( function ( geometry ) { - - return addPrimitiveAttributes( geometry, primitive, parser ); - - } ); - - } - - var pending = []; - - for ( var i = 0, il = primitives.length; i < il; i ++ ) { - - var primitive = primitives[ i ]; - var cacheKey = createPrimitiveKey( primitive ); - - // See if we've already created this geometry - var cached = cache[ cacheKey ]; - - if ( cached ) { - - // Use the cached geometry if it exists - pending.push( cached.promise ); - - } else { - - var geometryPromise; - - if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) { - - // Use DRACO geometry if available - geometryPromise = createDracoPrimitive( primitive ); - - } else { - - // Otherwise create a new geometry - geometryPromise = addPrimitiveAttributes( new BufferGeometry(), primitive, parser ); - - } - - // Cache this geometry - cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise }; - - pending.push( geometryPromise ); - - } - - } - - return Promise.all( pending ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes - * @param {number} meshIndex - * @return {Promise} - */ - GLTFParser.prototype.loadMesh = function ( meshIndex ) { - - var parser = this; - var json = this.json; - - var meshDef = json.meshes[ meshIndex ]; - var primitives = meshDef.primitives; - - var pending = []; - - for ( var i = 0, il = primitives.length; i < il; i ++ ) { - - var material = primitives[ i ].material === undefined - ? createDefaultMaterial( this.cache ) - : this.getDependency( 'material', primitives[ i ].material ); - - pending.push( material ); - - } - - pending.push( parser.loadGeometries( primitives ) ); - - return Promise.all( pending ).then( function ( results ) { - - var materials = results.slice( 0, results.length - 1 ); - var geometries = results[ results.length - 1 ]; - - var meshes = []; - - for ( var i = 0, il = geometries.length; i < il; i ++ ) { - - var geometry = geometries[ i ]; - var primitive = primitives[ i ]; - - // 1. create Mesh - - var mesh; - - var material = materials[ i ]; - - if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || - primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP || - primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN || - primitive.mode === undefined ) { - - // .isSkinnedMesh isn't in glTF spec. See ._markDefs() - mesh = meshDef.isSkinnedMesh === true - ? new SkinnedMesh( geometry, material ) - : new Mesh( geometry, material ); - - if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) { - - // we normalize floating point skin weight array to fix malformed assets (see #15319) - // it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs - mesh.normalizeSkinWeights(); - - } - - if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) { - - mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleStripDrawMode ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) { - - mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleFanDrawMode ); - - } - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { - - mesh = new LineSegments( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) { - - mesh = new Line( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) { - - mesh = new LineLoop( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) { - - mesh = new Points( geometry, material ); - - } else { - - throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode ); - - } - - if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { - - updateMorphTargets( mesh, meshDef ); - - } - - mesh.name = meshDef.name || ( 'mesh_' + meshIndex ); - - if ( geometries.length > 1 ) mesh.name += '_' + i; - - assignExtrasToUserData( mesh, meshDef ); - - parser.assignFinalMaterial( mesh ); - - meshes.push( mesh ); - - } - - if ( meshes.length === 1 ) { - - return meshes[ 0 ]; - - } - - var group = new Group(); - - for ( var i = 0, il = meshes.length; i < il; i ++ ) { - - group.add( meshes[ i ] ); - - } - - return group; - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras - * @param {number} cameraIndex - * @return {Promise} - */ - GLTFParser.prototype.loadCamera = function ( cameraIndex ) { - - var camera; - var cameraDef = this.json.cameras[ cameraIndex ]; - var params = cameraDef[ cameraDef.type ]; - - if ( ! params ) { - - console.warn( 'THREE.GLTFLoader: Missing camera parameters.' ); - return; - - } - - if ( cameraDef.type === 'perspective' ) { - - camera = new PerspectiveCamera( MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); - - } else if ( cameraDef.type === 'orthographic' ) { - - camera = new OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar ); - - } - - if ( cameraDef.name ) camera.name = cameraDef.name; - - assignExtrasToUserData( camera, cameraDef ); - - return Promise.resolve( camera ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins - * @param {number} skinIndex - * @return {Promise} - */ - GLTFParser.prototype.loadSkin = function ( skinIndex ) { - - var skinDef = this.json.skins[ skinIndex ]; - - var skinEntry = { joints: skinDef.joints }; - - if ( skinDef.inverseBindMatrices === undefined ) { - - return Promise.resolve( skinEntry ); - - } - - return this.getDependency( 'accessor', skinDef.inverseBindMatrices ).then( function ( accessor ) { - - skinEntry.inverseBindMatrices = accessor; - - return skinEntry; - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations - * @param {number} animationIndex - * @return {Promise} - */ - GLTFParser.prototype.loadAnimation = function ( animationIndex ) { - - var json = this.json; - - var animationDef = json.animations[ animationIndex ]; - - var pendingNodes = []; - var pendingInputAccessors = []; - var pendingOutputAccessors = []; - var pendingSamplers = []; - var pendingTargets = []; - - for ( var i = 0, il = animationDef.channels.length; i < il; i ++ ) { - - var channel = animationDef.channels[ i ]; - var sampler = animationDef.samplers[ channel.sampler ]; - var target = channel.target; - var name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated. - var input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input; - var output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output; - - pendingNodes.push( this.getDependency( 'node', name ) ); - pendingInputAccessors.push( this.getDependency( 'accessor', input ) ); - pendingOutputAccessors.push( this.getDependency( 'accessor', output ) ); - pendingSamplers.push( sampler ); - pendingTargets.push( target ); - - } - - return Promise.all( [ - - Promise.all( pendingNodes ), - Promise.all( pendingInputAccessors ), - Promise.all( pendingOutputAccessors ), - Promise.all( pendingSamplers ), - Promise.all( pendingTargets ) - - ] ).then( function ( dependencies ) { - - var nodes = dependencies[ 0 ]; - var inputAccessors = dependencies[ 1 ]; - var outputAccessors = dependencies[ 2 ]; - var samplers = dependencies[ 3 ]; - var targets = dependencies[ 4 ]; - - var tracks = []; - - for ( var i = 0, il = nodes.length; i < il; i ++ ) { - - var node = nodes[ i ]; - var inputAccessor = inputAccessors[ i ]; - var outputAccessor = outputAccessors[ i ]; - var sampler = samplers[ i ]; - var target = targets[ i ]; - - if ( node === undefined ) continue; - - node.updateMatrix(); - node.matrixAutoUpdate = true; - - var TypedKeyframeTrack; - - switch ( PATH_PROPERTIES[ target.path ] ) { - - case PATH_PROPERTIES.weights: - - TypedKeyframeTrack = NumberKeyframeTrack; - break; - - case PATH_PROPERTIES.rotation: - - TypedKeyframeTrack = QuaternionKeyframeTrack; - break; - - case PATH_PROPERTIES.position: - case PATH_PROPERTIES.scale: - default: - - TypedKeyframeTrack = VectorKeyframeTrack; - break; - - } - - var targetName = node.name ? node.name : node.uuid; - - var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : InterpolateLinear; - - var targetNames = []; - - if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) { - - // Node may be a Group (glTF mesh with several primitives) or a Mesh. - node.traverse( function ( object ) { - - if ( object.isMesh === true && object.morphTargetInfluences ) { - - targetNames.push( object.name ? object.name : object.uuid ); - - } - - } ); - - } else { - - targetNames.push( targetName ); - - } - - var outputArray = outputAccessor.array; - - if ( outputAccessor.normalized ) { - - var scale; - - if ( outputArray.constructor === Int8Array ) { - - scale = 1 / 127; - - } else if ( outputArray.constructor === Uint8Array ) { - - scale = 1 / 255; - - } else if ( outputArray.constructor == Int16Array ) { - - scale = 1 / 32767; - - } else if ( outputArray.constructor === Uint16Array ) { - - scale = 1 / 65535; - - } else { - - throw new Error( 'THREE.GLTFLoader: Unsupported output accessor component type.' ); - - } - - var scaled = new Float32Array( outputArray.length ); - - for ( var j = 0, jl = outputArray.length; j < jl; j ++ ) { - - scaled[ j ] = outputArray[ j ] * scale; - - } - - outputArray = scaled; - - } - - for ( var j = 0, jl = targetNames.length; j < jl; j ++ ) { - - var track = new TypedKeyframeTrack( - targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ], - inputAccessor.array, - outputArray, - interpolation - ); - - // Override interpolation with custom factory method. - if ( sampler.interpolation === 'CUBICSPLINE' ) { - - track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) { - - // A CUBICSPLINE keyframe in glTF has three output values for each input value, - // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() - // must be divided by three to get the interpolant's sampleSize argument. - - return new GLTFCubicSplineInterpolant( this.times, this.values, this.getValueSize() / 3, result ); - - }; - - // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants. - track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true; - - } - - tracks.push( track ); - - } - - } - - var name = animationDef.name ? animationDef.name : 'animation_' + animationIndex; - - return new AnimationClip( name, undefined, tracks ); - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy - * @param {number} nodeIndex - * @return {Promise} - */ - GLTFParser.prototype.loadNode = function ( nodeIndex ) { - - var json = this.json; - var extensions = this.extensions; - var parser = this; - - var nodeDef = json.nodes[ nodeIndex ]; - - return ( function () { - - var pending = []; - - if ( nodeDef.mesh !== undefined ) { - - pending.push( parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) { - - var node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh ); - - // if weights are provided on the node, override weights on the mesh. - if ( nodeDef.weights !== undefined ) { - - node.traverse( function ( o ) { - - if ( ! o.isMesh ) return; - - for ( var i = 0, il = nodeDef.weights.length; i < il; i ++ ) { - - o.morphTargetInfluences[ i ] = nodeDef.weights[ i ]; - - } - - } ); - - } - - return node; - - } ) ); - - } - - if ( nodeDef.camera !== undefined ) { - - pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) { - - return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera ); - - } ) ); - - } - - if ( nodeDef.extensions - && nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ] - && nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light !== undefined ) { - - var lightIndex = nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light; - - pending.push( parser.getDependency( 'light', lightIndex ).then( function ( light ) { - - return parser._getNodeRef( parser.lightCache, lightIndex, light ); - - } ) ); - - } - - return Promise.all( pending ); - - }() ).then( function ( objects ) { - - var node; - - // .isBone isn't in glTF spec. See ._markDefs - if ( nodeDef.isBone === true ) { - - node = new Bone(); - - } else if ( objects.length > 1 ) { - - node = new Group(); - - } else if ( objects.length === 1 ) { - - node = objects[ 0 ]; - - } else { - - node = new Object3D(); - - } - - if ( node !== objects[ 0 ] ) { - - for ( var i = 0, il = objects.length; i < il; i ++ ) { - - node.add( objects[ i ] ); - - } - - } - - if ( nodeDef.name ) { - - node.userData.name = nodeDef.name; - node.name = PropertyBinding.sanitizeNodeName( nodeDef.name ); - - } - - assignExtrasToUserData( node, nodeDef ); - - if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); - - if ( nodeDef.matrix !== undefined ) { - - var matrix = new Matrix4(); - matrix.fromArray( nodeDef.matrix ); - node.applyMatrix4( matrix ); - - } else { - - if ( nodeDef.translation !== undefined ) { - - node.position.fromArray( nodeDef.translation ); - - } - - if ( nodeDef.rotation !== undefined ) { - - node.quaternion.fromArray( nodeDef.rotation ); - - } - - if ( nodeDef.scale !== undefined ) { - - node.scale.fromArray( nodeDef.scale ); - - } - - } - - parser.associations.set( node, { type: 'nodes', index: nodeIndex } ); - - return node; - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes - * @param {number} sceneIndex - * @return {Promise} - */ - GLTFParser.prototype.loadScene = function () { - - // scene node hierachy builder - - function buildNodeHierachy( nodeId, parentObject, json, parser ) { - - var nodeDef = json.nodes[ nodeId ]; - - return parser.getDependency( 'node', nodeId ).then( function ( node ) { - - if ( nodeDef.skin === undefined ) return node; - - // build skeleton here as well - - var skinEntry; - - return parser.getDependency( 'skin', nodeDef.skin ).then( function ( skin ) { - - skinEntry = skin; - - var pendingJoints = []; - - for ( var i = 0, il = skinEntry.joints.length; i < il; i ++ ) { - - pendingJoints.push( parser.getDependency( 'node', skinEntry.joints[ i ] ) ); - - } - - return Promise.all( pendingJoints ); - - } ).then( function ( jointNodes ) { - - node.traverse( function ( mesh ) { - - if ( ! mesh.isMesh ) return; - - var bones = []; - var boneInverses = []; - - for ( var j = 0, jl = jointNodes.length; j < jl; j ++ ) { - - var jointNode = jointNodes[ j ]; - - if ( jointNode ) { - - bones.push( jointNode ); - - var mat = new Matrix4(); - - if ( skinEntry.inverseBindMatrices !== undefined ) { - - mat.fromArray( skinEntry.inverseBindMatrices.array, j * 16 ); - - } - - boneInverses.push( mat ); - - } else { - - console.warn( 'THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[ j ] ); - - } - - } - - mesh.bind( new Skeleton( bones, boneInverses ), mesh.matrixWorld ); - - } ); - - return node; - - } ); - - } ).then( function ( node ) { - - // build node hierachy - - parentObject.add( node ); - - var pending = []; - - if ( nodeDef.children ) { - - var children = nodeDef.children; - - for ( var i = 0, il = children.length; i < il; i ++ ) { - - var child = children[ i ]; - pending.push( buildNodeHierachy( child, node, json, parser ) ); - - } - - } - - return Promise.all( pending ); - - } ); - - } - - return function loadScene( sceneIndex ) { - - var json = this.json; - var extensions = this.extensions; - var sceneDef = this.json.scenes[ sceneIndex ]; - var parser = this; - - // Loader returns Group, not Scene. - // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 - var scene = new Group(); - if ( sceneDef.name ) scene.name = sceneDef.name; - - assignExtrasToUserData( scene, sceneDef ); - - if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); - - var nodeIds = sceneDef.nodes || []; - - var pending = []; - - for ( var i = 0, il = nodeIds.length; i < il; i ++ ) { - - pending.push( buildNodeHierachy( nodeIds[ i ], scene, json, parser ) ); - - } - - return Promise.all( pending ).then( function () { - - return scene; - - } ); - - }; - - }(); - - return GLTFLoader; - - } )(); - - var DRACOLoader = function ( manager ) { - - Loader.call( this, manager ); - - this.decoderPath = ''; - this.decoderConfig = {}; - this.decoderBinary = null; - this.decoderPending = null; - - this.workerLimit = 4; - this.workerPool = []; - this.workerNextTaskID = 1; - this.workerSourceURL = ''; - - this.defaultAttributeIDs = { - position: 'POSITION', - normal: 'NORMAL', - color: 'COLOR', - uv: 'TEX_COORD' - }; - this.defaultAttributeTypes = { - position: 'Float32Array', - normal: 'Float32Array', - color: 'Float32Array', - uv: 'Float32Array' - }; - - }; - - DRACOLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: DRACOLoader, - - setDecoderPath: function ( path ) { - - this.decoderPath = path; - - return this; - - }, - - setDecoderConfig: function ( config ) { - - this.decoderConfig = config; - - return this; - - }, - - setWorkerLimit: function ( workerLimit ) { - - this.workerLimit = workerLimit; - - return this; - - }, - - /** @deprecated */ - setVerbosity: function () { - - console.warn( 'THREE.DRACOLoader: The .setVerbosity() method has been removed.' ); - - }, - - /** @deprecated */ - setDrawMode: function () { - - console.warn( 'THREE.DRACOLoader: The .setDrawMode() method has been removed.' ); - - }, - - /** @deprecated */ - setSkipDequantization: function () { - - console.warn( 'THREE.DRACOLoader: The .setSkipDequantization() method has been removed.' ); - - }, - - load: function ( url, onLoad, onProgress, onError ) { - - var loader = new FileLoader( this.manager ); - - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - - if ( this.crossOrigin === 'use-credentials' ) { - - loader.setWithCredentials( true ); - - } - - loader.load( url, ( buffer ) => { - - var taskConfig = { - attributeIDs: this.defaultAttributeIDs, - attributeTypes: this.defaultAttributeTypes, - useUniqueIDs: false - }; - - this.decodeGeometry( buffer, taskConfig ) - .then( onLoad ) - .catch( onError ); - - }, onProgress, onError ); - - }, - - /** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */ - decodeDracoFile: function ( buffer, callback, attributeIDs, attributeTypes ) { - - var taskConfig = { - attributeIDs: attributeIDs || this.defaultAttributeIDs, - attributeTypes: attributeTypes || this.defaultAttributeTypes, - useUniqueIDs: !! attributeIDs - }; - - this.decodeGeometry( buffer, taskConfig ).then( callback ); - - }, - - decodeGeometry: function ( buffer, taskConfig ) { - - // TODO: For backward-compatibility, support 'attributeTypes' objects containing - // references (rather than names) to typed array constructors. These must be - // serialized before sending them to the worker. - for ( var attribute in taskConfig.attributeTypes ) { - - var type = taskConfig.attributeTypes[ attribute ]; - - if ( type.BYTES_PER_ELEMENT !== undefined ) { - - taskConfig.attributeTypes[ attribute ] = type.name; - - } - - } - - // - - var taskKey = JSON.stringify( taskConfig ); - - // Check for an existing task using this buffer. A transferred buffer cannot be transferred - // again from this thread. - if ( DRACOLoader.taskCache.has( buffer ) ) { - - var cachedTask = DRACOLoader.taskCache.get( buffer ); - - if ( cachedTask.key === taskKey ) { - - return cachedTask.promise; - - } else if ( buffer.byteLength === 0 ) { - - // Technically, it would be possible to wait for the previous task to complete, - // transfer the buffer back, and decode again with the second configuration. That - // is complex, and I don't know of any reason to decode a Draco buffer twice in - // different ways, so this is left unimplemented. - throw new Error( - - 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + - 'settings. Buffer has already been transferred.' - - ); - - } - - } - - // - - var worker; - var taskID = this.workerNextTaskID ++; - var taskCost = buffer.byteLength; - - // Obtain a worker and assign a task, and construct a geometry instance - // when the task completes. - var geometryPending = this._getWorker( taskID, taskCost ) - .then( ( _worker ) => { - - worker = _worker; - - return new Promise( ( resolve, reject ) => { - - worker._callbacks[ taskID ] = { resolve, reject }; - - worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] ); - - // this.debug(); - - } ); - - } ) - .then( ( message ) => this._createGeometry( message.geometry ) ); - - // Remove task from the task list. - // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416) - geometryPending - .catch( () => true ) - .then( () => { - - if ( worker && taskID ) { - - this._releaseTask( worker, taskID ); - - // this.debug(); - - } - - } ); - - // Cache the task result. - DRACOLoader.taskCache.set( buffer, { - - key: taskKey, - promise: geometryPending - - } ); - - return geometryPending; - - }, - - _createGeometry: function ( geometryData ) { - - var geometry = new BufferGeometry(); - - if ( geometryData.index ) { - - geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) ); - - } - - for ( var i = 0; i < geometryData.attributes.length; i ++ ) { - - var attribute = geometryData.attributes[ i ]; - var name = attribute.name; - var array = attribute.array; - var itemSize = attribute.itemSize; - - geometry.setAttribute( name, new BufferAttribute( array, itemSize ) ); - - } - - return geometry; - - }, - - _loadLibrary: function ( url, responseType ) { - - var loader = new FileLoader( this.manager ); - loader.setPath( this.decoderPath ); - loader.setResponseType( responseType ); - - return new Promise( ( resolve, reject ) => { - - loader.load( url, resolve, undefined, reject ); - - } ); - - }, - - preload: function () { - - this._initDecoder(); - - return this; - - }, - - _initDecoder: function () { - - if ( this.decoderPending ) return this.decoderPending; - - var useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js'; - var librariesPending = []; - - if ( useJS ) { - - librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) ); - - } else { - - librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) ); - librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) ); - - } - - this.decoderPending = Promise.all( librariesPending ) - .then( ( libraries ) => { - - var jsContent = libraries[ 0 ]; - - if ( ! useJS ) { - - this.decoderConfig.wasmBinary = libraries[ 1 ]; - - } - - var fn = DRACOLoader.DRACOWorker.toString(); - - var body = [ - '/* draco decoder */', - jsContent, - '', - '/* worker */', - fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) - ].join( '\n' ); - - this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); - - } ); - - return this.decoderPending; - - }, - - _getWorker: function ( taskID, taskCost ) { - - return this._initDecoder().then( () => { - - if ( this.workerPool.length < this.workerLimit ) { - - var worker = new Worker( this.workerSourceURL ); - - worker._callbacks = {}; - worker._taskCosts = {}; - worker._taskLoad = 0; - - worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } ); - - worker.onmessage = function ( e ) { - - var message = e.data; - - switch ( message.type ) { - - case 'decode': - worker._callbacks[ message.id ].resolve( message ); - break; - - case 'error': - worker._callbacks[ message.id ].reject( message ); - break; - - default: - console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' ); - - } - - }; - - this.workerPool.push( worker ); - - } else { - - this.workerPool.sort( function ( a, b ) { - - return a._taskLoad > b._taskLoad ? - 1 : 1; - - } ); - - } - - var worker = this.workerPool[ this.workerPool.length - 1 ]; - worker._taskCosts[ taskID ] = taskCost; - worker._taskLoad += taskCost; - return worker; - - } ); - - }, - - _releaseTask: function ( worker, taskID ) { - - worker._taskLoad -= worker._taskCosts[ taskID ]; - delete worker._callbacks[ taskID ]; - delete worker._taskCosts[ taskID ]; - - }, - - debug: function () { - - console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) ); - - }, - - dispose: function () { - - for ( var i = 0; i < this.workerPool.length; ++ i ) { - - this.workerPool[ i ].terminate(); - - } - - this.workerPool.length = 0; - - return this; - - } - - } ); - - /* WEB WORKER */ - - DRACOLoader.DRACOWorker = function () { - - var decoderConfig; - var decoderPending; - - onmessage = function ( e ) { - - var message = e.data; - - switch ( message.type ) { - - case 'init': - decoderConfig = message.decoderConfig; - decoderPending = new Promise( function ( resolve/*, reject*/ ) { - - decoderConfig.onModuleLoaded = function ( draco ) { - - // Module is Promise-like. Wrap before resolving to avoid loop. - resolve( { draco: draco } ); - - }; - - DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef - - } ); - break; - - case 'decode': - var buffer = message.buffer; - var taskConfig = message.taskConfig; - decoderPending.then( ( module ) => { - - var draco = module.draco; - var decoder = new draco.Decoder(); - var decoderBuffer = new draco.DecoderBuffer(); - decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength ); - - try { - - var geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig ); - - var buffers = geometry.attributes.map( ( attr ) => attr.array.buffer ); - - if ( geometry.index ) buffers.push( geometry.index.array.buffer ); - - self.postMessage( { type: 'decode', id: message.id, geometry }, buffers ); - - } catch ( error ) { - - console.error( error ); - - self.postMessage( { type: 'error', id: message.id, error: error.message } ); - - } finally { - - draco.destroy( decoderBuffer ); - draco.destroy( decoder ); - - } - - } ); - break; - - } - - }; - - function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) { - - var attributeIDs = taskConfig.attributeIDs; - var attributeTypes = taskConfig.attributeTypes; - - var dracoGeometry; - var decodingStatus; - - var geometryType = decoder.GetEncodedGeometryType( decoderBuffer ); - - if ( geometryType === draco.TRIANGULAR_MESH ) { - - dracoGeometry = new draco.Mesh(); - decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry ); - - } else if ( geometryType === draco.POINT_CLOUD ) { - - dracoGeometry = new draco.PointCloud(); - decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry ); - - } else { - - throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' ); - - } - - if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) { - - throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() ); - - } - - var geometry = { index: null, attributes: [] }; - - // Gather all vertex attributes. - for ( var attributeName in attributeIDs ) { - - var attributeType = self[ attributeTypes[ attributeName ] ]; - - var attribute; - var attributeID; - - // A Draco file may be created with default vertex attributes, whose attribute IDs - // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively, - // a Draco file may contain a custom set of attributes, identified by known unique - // IDs. glTF files always do the latter, and `.drc` files typically do the former. - if ( taskConfig.useUniqueIDs ) { - - attributeID = attributeIDs[ attributeName ]; - attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID ); - - } else { - - attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] ); - - if ( attributeID === - 1 ) continue; - - attribute = decoder.GetAttribute( dracoGeometry, attributeID ); - - } - - geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) ); - - } - - // Add index. - if ( geometryType === draco.TRIANGULAR_MESH ) { - - // Generate mesh faces. - var numFaces = dracoGeometry.num_faces(); - var numIndices = numFaces * 3; - var index = new Uint32Array( numIndices ); - var indexArray = new draco.DracoInt32Array(); - - for ( var i = 0; i < numFaces; ++ i ) { - - decoder.GetFaceFromMesh( dracoGeometry, i, indexArray ); - - for ( var j = 0; j < 3; ++ j ) { - - index[ i * 3 + j ] = indexArray.GetValue( j ); - - } - - } - - geometry.index = { array: index, itemSize: 1 }; - - draco.destroy( indexArray ); - - } - - draco.destroy( dracoGeometry ); - - return geometry; - - } - - function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) { - - var numComponents = attribute.num_components(); - var numPoints = dracoGeometry.num_points(); - var numValues = numPoints * numComponents; - var dracoArray; - - var array; - - switch ( attributeType ) { - - case Float32Array: - dracoArray = new draco.DracoFloat32Array(); - decoder.GetAttributeFloatForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Float32Array( numValues ); - break; - - case Int8Array: - dracoArray = new draco.DracoInt8Array(); - decoder.GetAttributeInt8ForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Int8Array( numValues ); - break; - - case Int16Array: - dracoArray = new draco.DracoInt16Array(); - decoder.GetAttributeInt16ForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Int16Array( numValues ); - break; - - case Int32Array: - dracoArray = new draco.DracoInt32Array(); - decoder.GetAttributeInt32ForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Int32Array( numValues ); - break; - - case Uint8Array: - dracoArray = new draco.DracoUInt8Array(); - decoder.GetAttributeUInt8ForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Uint8Array( numValues ); - break; - - case Uint16Array: - dracoArray = new draco.DracoUInt16Array(); - decoder.GetAttributeUInt16ForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Uint16Array( numValues ); - break; - - case Uint32Array: - dracoArray = new draco.DracoUInt32Array(); - decoder.GetAttributeUInt32ForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Uint32Array( numValues ); - break; - - default: - throw new Error( 'THREE.DRACOLoader: Unexpected attribute type.' ); - - } - - for ( var i = 0; i < numValues; i ++ ) { - - array[ i ] = dracoArray.GetValue( i ); - - } - - draco.destroy( dracoArray ); - - return { - name: attributeName, - array: array, - itemSize: numComponents - }; - - } - - }; - - DRACOLoader.taskCache = new WeakMap(); - - /** Deprecated static methods */ - - /** @deprecated */ - DRACOLoader.setDecoderPath = function () { - - console.warn( 'THREE.DRACOLoader: The .setDecoderPath() method has been removed. Use instance methods.' ); - - }; - - /** @deprecated */ - DRACOLoader.setDecoderConfig = function () { - - console.warn( 'THREE.DRACOLoader: The .setDecoderConfig() method has been removed. Use instance methods.' ); - - }; - - /** @deprecated */ - DRACOLoader.releaseDecoderModule = function () { - - console.warn( 'THREE.DRACOLoader: The .releaseDecoderModule() method has been removed. Use instance methods.' ); - - }; - - /** @deprecated */ - DRACOLoader.getDecoderModule = function () { - - console.warn( 'THREE.DRACOLoader: The .getDecoderModule() method has been removed. Use instance methods.' ); - - }; - - /** - * @author Don McCurdy / https://www.donmccurdy.com - */ - - let init, instance, heap; - - const importObject = { - - env: { - - emscripten_notify_memory_growth: function ( index ) { - - heap = new Uint8Array( instance.exports.memory.buffer ); - - } - - } - - }; - - /** - * ZSTD (Zstandard) decoder. - * - * Compiled from https://github.com/facebook/zstd/tree/dev/contrib/single_file_libs, with the - * following steps: - * - * ``` - * ./combine.sh -r ../../lib -o zstddeclib.c zstddeclib-in.c - * emcc zstddeclib.c -Oz -s EXPORTED_FUNCTIONS="['_ZSTD_decompress', '_ZSTD_findDecompressedSize', '_ZSTD_isError', '_malloc', '_free']" -s ALLOW_MEMORY_GROWTH=1 -s MALLOC=emmalloc -o zstddec.wasm - * base64 zstddec.wasm > zstddec.txt - * ``` - * - * The base64 string written to `zstddec.txt` is embedded as the `wasm` variable at the bottom - * of this file. The rest of this file is written by hand, in order to avoid an additional JS - * wrapper generated by Emscripten. - */ - class ZSTDDecoder { - - init () { - - if ( ! init ) { - - init = fetch( 'data:application/wasm;base64,' + wasm ) - .then( ( response ) => response.arrayBuffer() ) - .then( ( arrayBuffer ) => WebAssembly.instantiate( arrayBuffer, importObject ) ) - .then( ( result ) => { - - instance = result.instance; - - importObject.env.emscripten_notify_memory_growth( 0 ); // initialize heap. - - }); - - } - - return init; - - } - - decode ( array, uncompressedSize = 0 ) { - - // Write compressed data into WASM memory. - const compressedSize = array.byteLength; - const compressedPtr = instance.exports.malloc( compressedSize ); - heap.set( array, compressedPtr ); - - // Decompress into WASM memory. - uncompressedSize = uncompressedSize || Number( instance.exports.ZSTD_findDecompressedSize( compressedPtr, compressedSize ) ); - const uncompressedPtr = instance.exports.malloc( uncompressedSize ); - const actualSize = instance.exports.ZSTD_decompress( uncompressedPtr, uncompressedSize, compressedPtr, compressedSize ); - - // Read decompressed data and free WASM memory. - const dec = heap.slice( uncompressedPtr, uncompressedPtr + actualSize ); - instance.exports.free( compressedPtr ); - instance.exports.free( uncompressedPtr ); - - return dec; - - } - - } - - /** - * BSD License - * - * For Zstandard software - * - * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name Facebook nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - const wasm = '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IAMNACAEQQNxBEADQCACRQ0FIAAgAkF/aiICaiIDIAEgAmotAAA6AAAgA0EDcQ0ACwsgAkEDTQ0AA0AgACACQXxqIgJqIAEgAmooAgA2AgAgAkEDSw0ACwsgAkUNAgNAIAAgAkF/aiICaiABIAJqLQAAOgAAIAINAAsMAgsgAkEDTQ0AIAIhBANAIAMgASgCADYCACABQQRqIQEgA0EEaiEDIARBfGoiBEEDSw0ACyACQQNxIQILIAJFDQADQCADIAEtAAA6AAAgA0EBaiEDIAFBAWohASACQX9qIgINAAsLIAAL8wICAn8BfgJAIAJFDQAgACACaiIDQX9qIAE6AAAgACABOgAAIAJBA0kNACADQX5qIAE6AAAgACABOgABIANBfWogAToAACAAIAE6AAIgAkEHSQ0AIANBfGogAToAACAAIAE6AAMgAkEJSQ0AIABBACAAa0EDcSIEaiIDIAFB/wFxQYGChAhsIgE2AgAgAyACIARrQXxxIgRqIgJBfGogATYCACAEQQlJDQAgAyABNgIIIAMgATYCBCACQXhqIAE2AgAgAkF0aiABNgIAIARBGUkNACADIAE2AhggAyABNgIUIAMgATYCECADIAE2AgwgAkFwaiABNgIAIAJBbGogATYCACACQWhqIAE2AgAgAkFkaiABNgIAIAQgA0EEcUEYciIEayICQSBJDQAgAa0iBUIghiAFhCEFIAMgBGohAQNAIAEgBTcDGCABIAU3AxAgASAFNwMIIAEgBTcDACABQSBqIQEgAkFgaiICQR9LDQALCyAACy8BAn8gACgCBCAAKAIAQQJ0aiICLQACIQMgACACLwEAIAEgAi0AAxAIajYCACADCy8BAn8gACgCBCAAKAIAQQJ0aiICLQACIQMgACACLwEAIAEgAi0AAxAFajYCACADCx8AIAAgASACKAIEEAg2AgAgARAEGiAAIAJBCGo2AgQLCAAgAGdBH3MLugUBDX8jAEEQayIKJAACfyAEQQNNBEAgCkEANgIMIApBDGogAyAEEAsaIAAgASACIApBDGpBBBAVIgBBbCAAEAMbIAAgACAESxsMAQsgAEEAIAEoAgBBAXRBAmoQECENQVQgAygAACIGQQ9xIgBBCksNABogAiAAQQVqNgIAIAMgBGoiAkF8aiEMIAJBeWohDiACQXtqIRAgAEEGaiELQQQh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ASIDNgLE4AEgACgCvOABIQQgACABNgK84AEgACABIAJqNgK44AEgACABIAQgA2tqNgLA4AELpgEBAX8gACAAKALs4QEQFjYCyOABIABCADcD+OABIABCADcDuOABIABBwOABakIANwMAIABBqNAAaiIBQYyAgOAANgIAIABBADYCmOIBIABCADcDiOEBIABCAzcDgOEBIABBrNABakHgEikCADcCACAAQbTQAWpB6BIoAgA2AgAgACABNgIMIAAgAEGYIGo2AgggACAAQaAwajYCBCAAIABBEGo2AgALYQEBf0G4fyEDAkAgAUEDSQ0AIAIgABAhIgFBA3YiADYCCCACIAFBAXE2AgQgAiABQQF2QQNxIgM2AgACQCADQX9qIgFBAksNAAJAIAFBAWsOAgEAAgtBbA8LIAAhAwsgAwsMACAAIAEgAkEAEC4LiAQCA38CfiADEBYhBCAAQQBBKBAQIQAgBCACSwRAIAQPCyABRQRAQX8PCwJAAkAgA0EBRg0AIAEoAAAiBkGo6r5pRg0AQXYhAyAGQXBxQdDUtMIBRw0BQQghAyACQQhJDQEgAEEAQSgQECEAIAEoAAQhASAAQQE2AhQgACABrTcDAEEADwsgASACIAMQLyIDIAJLDQAgACADNgIYQXIhAyABIARqIgVBf2otAAAiAkEIcQ0AIAJBIHEiBkUEQEFwIQMgBS0AACIFQacBSw0BIAVBB3GtQgEgBUEDdkEKaq2GIgdCA4h+IAd8IQggBEEBaiEECyACQQZ2IQMgAkECdiEFAkAgAkEDcUF/aiICQQJLBEBBACECDAELAkACQAJAIAJBAWsOAgECAAsgASAEai0AACECIARBAWohBAwCCyABIARqLwAAIQIgBEECaiEEDAELIAEgBGooAAAhAiAEQQRqIQQLIAVBAXEhBQJ+AkACQAJAIANBf2oiA0ECTQRAIANBAWsOAgIDAQtCfyAGRQ0DGiABIARqMQAADAMLIAEgBGovAACtQoACfAwCCyABIARqKAAArQwBCyABIARqKQAACyEHIAAgBTYCICAAIAI2AhwgACAHNwMAQQAhAyAAQQA2AhQgACAHIAggBhsiBzcDCCAAIAdCgIAIIAdCgIAIVBs+AhALIAMLWwEBf0G4fyEDIAIQFiICIAFNBH8gACACakF/ai0AACIAQQNxQQJ0QaAeaigC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C0Egak8NAQsgByAHKQNoNwMQIAcgBykDYDcDCCAEIA0gB0EIaiAHQdwAaiAPIA4gESAQEB4hCwwBCyACIARqIQggBCAKEAcgAkERTwRAIARBEGohAgNAIAIgCkEQaiIKEAcgAkEQaiICIAhJDQALCyAIIAlrIQIgByATNgJcIAkgCCAOa0sEQCAJIAggEWtLBEBBbCELDAILIBAgAiAOayICaiIKIANqIBBNBEAgCCAKIAMQDxoMAgsgCCAKQQAgAmsQDyEIIAcgAiADaiIDNgJkIAggAmshCCAOIQILIAlBEE8EQCADIAhqIQMDQCAIIAIQByACQRBqIQIgCEEQaiIIIANJDQALDAELAkAgCUEHTQRAIAggAi0AADoAACAIIAItAAE6AAEgCCACLQACOgACIAggAi0AAzoAAyAIQQRqIAIgCUECdCIDQcAeaigCAGoiAhAXIAIgA0HgHmooAgBrIQIgBygCZCEDDAELIAggAhAMCyADQQlJDQAgAyAIaiEDIAhBCGoiCCACQQhqIgJrQQ9MBEADQCAIIAIQDCACQQhqIQIgCEEIaiIIIANJDQAMAgALAAsDQCAIIAIQByACQRBqIQIgCEEQaiIIIANJDQALCyAHQRhqEAQaIAsgDCALEAMiAhshDCAEIAQgC2ogAhshBCAFQX9qIgUNAAsgDBADDQFBbCEMIAdBGGoQBEECSQ0BQQAhCANAIAhBA0cEQCAAIAhBAnQiAmpBrNABaiACIAdqKAJENgIAIAhBAWohCAwBCwsgBygCXCEIC0G6fyEMIA8gCGsiACANIARrSw0AIAQEfyAEIAggABALIABqBUEACyABayEMCyAHQfAAaiQAIAwLkRcCFn8FfiMAQdABayIHJAAgByAAKALw4QEiCDYCvAEgASACaiESIAggACgCgOIBaiETAkACQCAFRQRAIAEhAwwBCyAAKALE4AEhESAAKALA4AEhFSAAKAK84AEhDyAAQQE2AozhAUEAIQgDQCAIQQNHBEAgByAIQQJ0IgJqIAAgAmpBrNABaigCADYCVCAIQQFqIQgMAQsLIAcgETYCZCAHIA82AmAgByABIA9rNgJoQWwhECAHQShqIAMgBBAGEAMNASAFQQQgBUEESBshFyAHQTxqIAdBKGogACgCABATIAdBxABqIAdBKGogACgCCBATIAdBzABqIAdBKGogACgCBBATQQAhBCAHQeAAaiEMIAdB5ABqIQoDQCAHQShqEARBAksgBCAXTnJFBEAgBygCQCAHKAI8QQN0aikCACIdQhCIp0H/AXEhCyAHKAJQIAcoAkxBA3RqKQIAIh5CEIinQf8BcSEJIAcoAkggBygCREEDdGopAgAiH0IgiKchCCAeQiCIISAgHUIgiKchAgJAIB9CEIinQf8BcSIDQQJPBEACQCAGRSADQRlJckUEQCAIIAdBKGogA0EgIAcoAixrIg0gDSADSxsiDRAFIAMgDWsiA3RqIQggB0EoahAEGiADRQ0BIAdBKGogAxAFIAhqIQgMAQsgB0EoaiADEAUgCGohCCAHQShqEAQaCyAHKQJUISEgByAINgJUIAcgITcDWAwBCwJAIANFBEAgAgRAIAcoAlQhCAwDCyAHKAJYIQgMAQsCQAJAIAdBKGpBARAFIAggAkVqaiIDQQNGBEAgBygCVEF/aiIDIANFaiEIDAELIANBAnQgB2ooAlQiCCAIRWohCCADQQFGDQELIAcgBygCWDYCXAsLIAcgBygCVDYCWCAHIAg2AlQLICCnIQMgCQRAIAdBKGogCRAFIANqIQMLIAkgC2pBFE8EQCAHQShqEAQaCyALBEAgB0EoaiALEAUgAmohAgsgB0EoahAEGiAHIAcoAmggAmoiCSADajYCaCAKIAwgCCAJSxsoAgAhDSAHIAdBKGogHUIYiKdB/wFxEAggHadB//8DcWo2AjwgByAHQShqIB5CGIinQf8BcRAIIB6nQf//A3FqNgJMIAdBKGoQBBogB0EoaiAfQhiIp0H/AXEQCCEOIAdB8ABqIARBBHRqIgsgCSANaiAIazYCDCALIAg2AgggCyADNgIEIAsgAjYCACAHIA4gH6dB//8DcWo2AkQgBEEBaiEEDAELCyAEIBdIDQEgEkFgaiEYIAdB4ABqIRogB0HkAGohGyABIQMDQCAHQShqEARBAksgBCAFTnJFBEAgBygCQCAHKAI8QQN0aikCACIdQhCIp0H/AXEhCyAHKAJQIAcoAkxBA3RqKQIAIh5CEIinQf8BcSEIIAcoAkggBygCREEDdGopAgAiH0IgiKchCSAeQiCIISAgHUIgiKchDAJAIB9CEIinQf8BcSICQQJPBEACQCAGRSACQRlJckUEQCAJIAdBKGogAkEgIAcoAixrIgogCiACSxsiChAFIAIgCmsiAnRqIQkgB0EoahAEGiACRQ0BIAdBKGogAhAFIAlqIQkMAQsgB0EoaiACEAUgCWohCSAHQShqEAQaCyAHKQJUISEgByAJNgJUIAcgITcDWAwBCwJAIAJFBEAgDARAIAcoAlQhCQwDCyAHKAJYIQkMAQsCQAJAIAdBKGpBARAFIAkgDEVqaiICQQNGBEAgBygCVEF/aiICIAJFaiEJDAELIAJBAnQgB2ooAlQiCSAJRWohCSACQQFGDQELIAcgBygCWDYCXAsLIAcgBygCVDYCWCAHIAk2AlQLICCnIRQgCARAIAdBKGogCBAFIBRqIRQLIAggC2pBFE8EQCAHQShqEAQaCyALBEAgB0EoaiALEAUgDGohDAsgB0EoahAEGiAHIAcoAmggDGoiGSAUajYCaCAbIBogCSAZSxsoAgAhHCAHIAdBKGogHUIYiKdB/wFxEAggHadB//8DcWo2AjwgByAHQShqIB5CGIinQf8BcRAIIB6nQf//A3FqNgJMIAdBKGoQBBogByAHQShqIB9CGIinQf8BcRAIIB+nQf//A3FqNgJEIAcgB0HwAGogBEEDcUEEdGoiDSkDCCIdNwPIASAHIA0pAwAiHjcDwAECQAJAAkAgBygCvAEiDiAepyICaiIWIBNLDQAgAyAHKALEASIKIAJqIgtqIBhLDQAgEiADayALQSBqTw0BCyAHIAcpA8gBNwMQIAcgBykDwAE3AwggAyASIAdBCGogB0G8AWogEyAPIBUgERAeIQsMAQsgAiADaiEIIAMgDhAHIAJBEU8EQCADQRBqIQIDQCACIA5BEGoiDhAHIAJBEGoiAiAISQ0ACwsgCCAdpyIOayECIAcgFjYCvAEgDiAIIA9rSwRAIA4gCCAVa0sEQEFsIQsMAgsgESACIA9rIgJqIhYgCmogEU0EQCAIIBYgChAPGgwCCyAIIBZBACACaxAPIQggByACIApqIgo2AsQBIAggAmshCCAPIQILIA5BEE8EQCAIIApqIQoDQCAIIAIQByACQRBqIQIgCEEQaiIIIApJDQALDAELAkAgDkEHTQRAIAggAi0AADoAACAIIAItAAE6AAEgCCACLQACOgACIAggAi0AAzoAAyAIQQRqIAIgDkECdCIKQcAeaigCAGoiAhAXIAIgCkHgHmooAgBrIQIgBygCxAEhCgwBCyAIIAIQDAsgCkEJSQ0AIAggCmohCiAIQQhqIgggAkEIaiICa0EPTARAA0AgCCACEAwgAkEIaiECIAhBCGoiCCAKSQ0ADAIACwALA0AgCCACEAcgAkEQaiECIAhBEGoiCCAKSQ0ACwsgCxADBEAgCyEQDAQFIA0gDDYCACANIBkgHGogCWs2AgwgDSAJNgIIIA0gFDYCBCAEQQFqIQQgAyALaiEDDAILAAsLIAQgBUgNASAEIBdrIQtBACEEA0AgCyAFSARAIAcgB0HwAGogC0EDcUEEdGoiAikDCCIdNwPIASAHIAIpAwAiHjcDwAECQAJAAkAgBygCvAEiDCAepyICaiIKIBNLDQAgAyAHKALEASIJIAJqIhBqIBhLDQAgEiADayAQQSBqTw0BCyAHIAcpA8gBNwMgIAcgBykDwAE3AxggAyASIAdBGGogB0G8AWogEyAPIBUgERAeIRAMAQsgAiADaiEIIAMgDBAHIAJBEU8EQCADQRBqIQIDQCACIAxBEGoiDBAHIAJBEGoiAiAISQ0ACwsgCCAdpyIGayECIAcgCjYCvAEgBiAIIA9rSwRAIAYgCCAVa0sEQEFsIRAMAgsgESACIA9rIgJqIgwgCWogEU0EQCAIIAwgCRAPGgwCCyAIIAxBACACaxAPIQggByACIAlqIgk2AsQBIAggAmshCCAPIQILIAZBEE8EQCAIIAlqIQYDQCAIIAIQByACQRBqIQIgCEEQaiIIIAZJDQALDAELAkAgBkEHTQRAIAggAi0AADoAACAIIAItAAE6AAEgCCACLQACOgACIAggAi0AAzoAAyAIQQRqIAIgBkECdCIGQcAeaigCAGoiAhAXIAIgBkHgHmooAgBrIQIgBygCxAEhCQwBCyAIIAIQDAsgCUEJSQ0AIAggCWohBiAIQQhqIgggAkEIaiICa0EPTARAA0AgCCACEAwgAkEIaiECIAhBCGoiCCAGSQ0ADAIACwALA0AgCCACEAcgAkEQaiECIAhBEGoiCCAGSQ0ACwsgEBADDQMgC0EBaiELIAMgEGohAwwBCwsDQCAEQQNHBEAgACAEQQJ0IgJqQazQAWogAiAHaigCVDYCACAEQQFqIQQMAQsLIAcoArwBIQgLQbp/IRAgEyAIayIAIBIgA2tLDQAgAwR/IAMgCCAAEAsgAGoFQQALIAFrIRALIAdB0AFqJAAgEAslACAAQgA3AgAgAEEAOwEIIABBADoACyAAIAE2AgwgACACOgAKC7QFAQN/IwBBMGsiBCQAIABB/wFqIgVBfWohBgJAIAMvAQIEQCAEQRhqIAEgAhAGIgIQAw0BIARBEGogBEEYaiADEBwgBEEIaiAEQRhqIAMQHCAAIQMDQAJAIARBGGoQBCADIAZPckUEQCADIARBEGogBEEYahASOgAAIAMgBEEIaiAEQRhqEBI6AAEgBEEYahAERQ0BIANBAmohAwsgBUF+aiEFAn8DQEG6fyECIAMiASAFSw0FIAEgBEEQaiAEQRhqEBI6AAAgAUEBaiEDIARBGGoQBEEDRgRAQQIhAiAEQQhqDAILIAMgBUsNBSABIARBCGogBEEYahASOgABIAFBAmohA0EDIQIgBEEYahAEQQNHDQALIARBEGoLIQUgAyAFIARBGGoQEjoAACABIAJqIABrIQIMAwsgAyAEQRBqIARBGGoQEjoAAiADIARBCGogBEEYahASOgADIANBBGohAwwAAAsACyAEQRhqIAEgAhAGIgIQAw0AIARBEGogBEEYaiADEBwgBEEIaiAEQRhqIAMQHCAAIQMDQAJAIARBGGoQBCADIAZPckUEQCADIARBEGogBEEYahAROgAAIAMgBEEIaiAEQRhqEBE6AAEgBEEYahAERQ0BIANBAmohAwsgBUF+aiEFAn8DQEG6fyECIAMiASAFSw0EIAEgBEEQaiAEQRhqEBE6AAAgAUEBaiEDIARBGGoQBEEDRgRAQQIhAiAEQQhqDAILIAMgBUsNBCABIARBCGogBEEYahAROgABIAFBAmohA0EDIQIgBEEYahAEQQNHDQALIARBEGoLIQUgAyAFIARBGGoQEToAACABIAJqIABrIQIMAgsgAyAEQRBqIARBGGoQEToAAiADIARBCGogBEEYahAROgADIANBBGohAwwAAAsACyAEQTBqJAAgAgtpAQF/An8CQAJAIAJBB00NACABKAAAQbfIwuF+Rw0AIAAgASgABDYCmOIBQWIgAEEQaiABIAIQPiIDEAMNAhogAEKBgICAEDcDiOEBIAAgASADaiACIANrECoMAQsgACABIAIQKgtBAAsLrQMBBn8jAEGAAWsiAyQAQWIhCAJAIAJBCUkNACAAQZjQAGogAUEIaiIEIAJBeGogAEGY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wOABCwutAgIEfwF+IwBBQGoiBCQAAkACQCACQQhJDQAgASgAAEFwcUHQ1LTCAUcNACABIAIQIiEBIABCADcDCCAAQQA2AgQgACABNgIADAELIARBGGogASACEC0iAxADBEAgACADEBoMAQsgAwRAIABBuH8QGgwBCyACIAQoAjAiA2shAiABIANqIQMDQAJAIAAgAyACIARBCGoQLCIFEAMEfyAFBSACIAVBA2oiBU8NAUG4fwsQGgwCCyAGQQFqIQYgAiAFayECIAMgBWohAyAEKAIMRQ0ACyAEKAI4BEAgAkEDTQRAIABBuH8QGgwCCyADQQRqIQMLIAQoAighAiAEKQMYIQcgAEEANgIEIAAgAyABazYCACAAIAIgBmytIAcgB0J/URs3AwgLIARBQGskAAslAQF/IwBBEGsiAiQAIAIgACABEFEgAigCACEAIAJBEGokACAAC30BBH8jAEGQBGsiBCQAIARB/wE2AggCQCAEQRBqIARBCGogBEEMaiABIAIQFSIGEAMEQCAGIQUMAQtBVCEFIAQoAgwiB0EGSw0AIAMgBEEQaiAEKAIIIAcQQSIFEAMNACAAIAEgBmogAiAGayADEDwhBQsgBEGQBGokACAFC4cBAgJ/An5BABAWIQMCQANAIAEgA08EQAJAIAAoAABBcHFB0NS0wgFGBEAgACABECIiAhADRQ0BQn4PCyAAIAEQVSIEQn1WDQMgBCAFfCIFIARUIQJCfiEEIAINAyAAIAEQUiICEAMNAwsgASACayEBIAAgAmohAAwBCwtCfiAFIAEbIQQLIAQLPwIBfwF+IwBBMGsiAiQAAn5CfiACQQhqIAAgARAtDQAaQgAgAigCHEEBRg0AGiACKQMICyEDIAJBMGokACADC40BAQJ/IwBBMGsiASQAAkAgAEUNACAAKAKI4gENACABIABB/OEBaigCADYCKCABIAApAvThATcDICAAEDAgACgCqOIBIQIgASABKAIoNgIYIAEgASkDIDcDECACIAFBEGoQGyAAQQA2AqjiASABIAEoAig2AgggASABKQMgNwMAIAAgARAbCyABQTBqJAALKgECfyMAQRBrIgAkACAAQQA2AgggAEIANwMAIAAQWCEBIABBEGokACABC4cBAQN/IwBBEGsiAiQAAkAgACgCAEUgACgCBEVzDQAgAiAAKAIINgIIIAIgACkCADcDAAJ/IAIoAgAiAQRAIAIoAghBqOMJIAERBQAMAQtBqOMJECgLIgFFDQAgASAAKQIANwL04QEgAUH84QFqIAAoAgg2AgAgARBZIAEhAwsgAkEQaiQAIAMLywEBAn8jAEEgayIBJAAgAEGBgIDAADYCtOIBIABBADYCiOIBIABBADYC7OEBIABCADcDkOIBIABBADYCpOMJIABBADYC3OIBIABCADcCzOIBIABBADYCvOIBIABBADYCxOABIABCADcCnOIBIABBpOIBakIANwIAIABBrOIBakEANgIAIAFCADcCECABQgA3AhggASABKQMYNwMIIAEgASkDEDcDACABKAIIQQh2QQFxIQIgAEEANgLg4gEgACACNgKM4gEgAUEgaiQAC3YBA38jAEEwayIBJAAgAARAIAEgAEHE0AFqIgIoAgA2AiggASAAKQK80AE3AyAgACgCACEDIAEgAigCADYCGCABIAApArzQATcDECADIAFBEGoQGyABIAEoAig2AgggASABKQMgNwMAIAAgARAbCyABQTBqJAALzAEBAX8gACABKAK00AE2ApjiASAAIAEoAgQiAjYCwOABIAAgAjYCvOABIAAgAiABKAIIaiICNgK44AEgACACNgLE4AEgASgCuNABBEAgAEKBgICAEDcDiOEBIAAgAUGk0ABqNgIMIAAgAUGUIGo2AgggACABQZwwajYCBCAAIAFBDGo2AgAgAEGs0AFqIAFBqNABaigCADYCACAAQbDQAWogAUGs0AFqKAIANgIAIABBtNABaiABQbDQAWooAgA2AgAPCyAAQgA3A4jhAQs7ACACRQRAQbp/DwsgBEUEQEFsDwsgAiAEEGAEQCAAIAEgAiADIAQgBRBhDwsgACABIAIgAyAEIAUQZQtGAQF/IwBBEGsiBSQAIAVBCGogBBAOAn8gBS0ACQRAIAAgASACIAMgBBAyDAELIAAgASACIAMgBBA0CyEAIAVBEGokACAACzQAIAAgAyAEIAUQNiIFEAMEQCAFDwsgBSAESQR/IAEgAiADIAVqIAQgBWsgABA1BUG4fwsLRgEBfyMAQRBrIgUkACAFQQhqIAQQDgJ/IAUtAAkEQCAAIAEgAiADIAQQYgwBCyAAIAEgAiADIAQQNQshACAFQRBqJAAgAAtZAQF/QQ8hAiABIABJBEAgAUEE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'; - - /** - * References: - * - KTX: http://github.khronos.org/KTX-Specification/ - * - DFD: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#basicdescriptor - * - * To do: - * - [ ] Cross-platform testing - * - [ ] Specify JS/WASM transcoder path - * - [ ] High-quality demo - * - [ ] Documentation - * - [ ] TypeScript definitions - * - [ ] (Optional) Include BC5 - * - [ ] (Optional) Include EAC RG on mobile (WEBGL_compressed_texture_etc) - * - [ ] (Optional) Include two-texture output mode (see: clearcoat + clearcoatRoughness) - * - [ ] (Optional) Support Web Workers, after #18234 - */ - - // Data Format Descriptor (DFD) constants. - - const DFDModel = { - ETC1S: 163, - UASTC: 166, - }; - - const DFDChannel = { - ETC1S: { - RGB: 0, - RRR: 3, - GGG: 4, - AAA: 15, - }, - UASTC: { - RGB: 0, - RGBA: 3, - RRR: 4, - RRRG: 5 - }, - }; - - // - - class KTX2Loader extends CompressedTextureLoader { - - constructor( manager ) { - - super( manager ); - - this.basisModule = null; - this.basisModulePending = null; - - this.transcoderConfig = {}; - - } - - detectSupport( renderer ) { - - this.transcoderConfig = { - astcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_astc' ), - etc1Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc1' ), - etc2Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc' ), - dxtSupported: renderer.extensions.has( 'WEBGL_compressed_texture_s3tc' ), - bptcSupported: renderer.extensions.has( 'EXT_texture_compression_bptc' ), - pvrtcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_pvrtc' ) - || renderer.extensions.has( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ) - }; - - return this; - - } - - initModule() { - - if ( this.basisModulePending ) { - - return; - - } - - var scope = this; - - // The Emscripten wrapper returns a fake Promise, which can cause - // infinite recursion when mixed with native Promises. Wrap the module - // initialization to return a native Promise. - scope.basisModulePending = new Promise( function ( resolve ) { - - MSC_TRANSCODER().then( function ( basisModule ) { - - scope.basisModule = basisModule; - - basisModule.initTranscoders(); - - resolve(); - - } ); - - } ); - - } - - load( url, onLoad, onProgress, onError ) { - - var scope = this; - - var texture = new CompressedTexture(); - - var bufferPending = new Promise( function ( resolve, reject ) { - - new FileLoader( scope.manager ) - .setPath( scope.path ) - .setResponseType( 'arraybuffer' ) - .load( url, resolve, onProgress, reject ); - - } ); - - this.initModule(); - - Promise.all( [ bufferPending, this.basisModulePending ] ).then( function ( [ buffer ] ) { - - scope.parse( buffer, function ( _texture ) { - - texture.copy( _texture ); - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - }, onError ); - - } ); - - return texture; - - } - - parse( buffer, onLoad, onError ) { - - var BasisLzEtc1sImageTranscoder = this.basisModule.BasisLzEtc1sImageTranscoder; - var UastcImageTranscoder = this.basisModule.UastcImageTranscoder; - var TextureFormat = this.basisModule.TextureFormat; - - var ktx = new KTX2Container( this.basisModule, buffer ); - - // TODO(donmccurdy): Should test if texture is transcodable before attempting - // any transcoding. If supercompressionScheme is KTX_SS_BASIS_LZ and dfd - // colorModel is ETC1S (163) or if dfd colorModel is UASTCF (166) - // then texture must be transcoded. - var transcoder = ktx.getTexFormat() === TextureFormat.UASTC4x4 - ? new UastcImageTranscoder() - : new BasisLzEtc1sImageTranscoder(); - - ktx.initMipmaps( transcoder, this.transcoderConfig ) - .then( function () { - - var texture = new CompressedTexture( - ktx.mipmaps, - ktx.getWidth(), - ktx.getHeight(), - ktx.transcodedFormat, - UnsignedByteType - ); - - texture.encoding = ktx.getEncoding(); - texture.premultiplyAlpha = ktx.getPremultiplyAlpha(); - texture.minFilter = ktx.mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter; - texture.magFilter = LinearFilter; - - onLoad( texture ); - - } ) - .catch( onError ); - - return this; - - } - - } - - class KTX2Container { - - constructor( basisModule, arrayBuffer ) { - - this.basisModule = basisModule; - this.arrayBuffer = arrayBuffer; - - this.zstd = new ZSTDDecoder(); - this.zstd.init(); - - this.mipmaps = null; - this.transcodedFormat = null; - - // Confirm this is a KTX 2.0 file, based on the identifier in the first 12 bytes. - var idByteLength = 12; - var id = new Uint8Array( this.arrayBuffer, 0, idByteLength ); - if ( id[ 0 ] !== 0xAB || // '´' - id[ 1 ] !== 0x4B || // 'K' - id[ 2 ] !== 0x54 || // 'T' - id[ 3 ] !== 0x58 || // 'X' - id[ 4 ] !== 0x20 || // ' ' - id[ 5 ] !== 0x32 || // '2' - id[ 6 ] !== 0x30 || // '0' - id[ 7 ] !== 0xBB || // 'ª' - id[ 8 ] !== 0x0D || // '\r' - id[ 9 ] !== 0x0A || // '\n' - id[ 10 ] !== 0x1A || // '\x1A' - id[ 11 ] !== 0x0A // '\n' - ) { - - throw new Error( 'THREE.KTX2Loader: Missing KTX 2.0 identifier.' ); - - } - - // TODO(donmccurdy): If we need to support BE, derive this from typeSize. - var littleEndian = true; - - - /////////////////////////////////////////////////// - // Header. - /////////////////////////////////////////////////// - - var headerByteLength = 17 * Uint32Array.BYTES_PER_ELEMENT; - var headerReader = new KTX2BufferReader( this.arrayBuffer, idByteLength, headerByteLength, littleEndian ); - - this.header = { - - vkFormat: headerReader.nextUint32(), - typeSize: headerReader.nextUint32(), - pixelWidth: headerReader.nextUint32(), - pixelHeight: headerReader.nextUint32(), - pixelDepth: headerReader.nextUint32(), - arrayElementCount: headerReader.nextUint32(), - faceCount: headerReader.nextUint32(), - levelCount: headerReader.nextUint32(), - - supercompressionScheme: headerReader.nextUint32(), - - dfdByteOffset: headerReader.nextUint32(), - dfdByteLength: headerReader.nextUint32(), - kvdByteOffset: headerReader.nextUint32(), - kvdByteLength: headerReader.nextUint32(), - sgdByteOffset: headerReader.nextUint64(), - sgdByteLength: headerReader.nextUint64(), - - }; - - if ( this.header.pixelDepth > 0 ) { - - throw new Error( 'THREE.KTX2Loader: Only 2D textures are currently supported.' ); - - } - - if ( this.header.arrayElementCount > 1 ) { - - throw new Error( 'THREE.KTX2Loader: Array textures are not currently supported.' ); - - } - - if ( this.header.faceCount > 1 ) { - - throw new Error( 'THREE.KTX2Loader: Cube textures are not currently supported.' ); - - } - - - /////////////////////////////////////////////////// - // Level index - /////////////////////////////////////////////////// - - var levelByteLength = this.header.levelCount * 3 * 8; - var levelReader = new KTX2BufferReader( this.arrayBuffer, idByteLength + headerByteLength, levelByteLength, littleEndian ); - - this.levels = []; - - for ( var i = 0; i < this.header.levelCount; i ++ ) { - - this.levels.push( { - - byteOffset: levelReader.nextUint64(), - byteLength: levelReader.nextUint64(), - uncompressedByteLength: levelReader.nextUint64(), - - } ); - - } - - - /////////////////////////////////////////////////// - // Data Format Descriptor (DFD) - /////////////////////////////////////////////////// - - var dfdReader = new KTX2BufferReader( - this.arrayBuffer, - this.header.dfdByteOffset, - this.header.dfdByteLength, - littleEndian - ); - - const sampleStart = 6; - const sampleWords = 4; - - this.dfd = { - - vendorId: dfdReader.skip( 4 /* totalSize */ ).nextUint16(), - versionNumber: dfdReader.skip( 2 /* descriptorType */ ).nextUint16(), - descriptorBlockSize: dfdReader.nextUint16(), - colorModel: dfdReader.nextUint8(), - colorPrimaries: dfdReader.nextUint8(), - transferFunction: dfdReader.nextUint8(), - flags: dfdReader.nextUint8(), - texelBlockDimension: { - x: dfdReader.nextUint8() + 1, - y: dfdReader.nextUint8() + 1, - z: dfdReader.nextUint8() + 1, - w: dfdReader.nextUint8() + 1, - }, - bytesPlane0: dfdReader.nextUint8(), - numSamples: 0, - samples: [], - - }; - - this.dfd.numSamples = ( this.dfd.descriptorBlockSize / 4 - sampleStart ) / sampleWords; - - dfdReader.skip( 7 /* bytesPlane[1-7] */ ); - - for ( var i = 0; i < this.dfd.numSamples; i ++ ) { - - this.dfd.samples[ i ] = { - - channelID: dfdReader.skip( 3 /* bitOffset + bitLength */ ).nextUint8(), - // ... remainder not implemented. - - }; - - dfdReader.skip( 12 /* samplePosition[0-3], lower, upper */ ); - - } - - if ( this.header.vkFormat !== 0x00 /* VK_FORMAT_UNDEFINED */ && - ! ( this.header.supercompressionScheme === 1 /* BasisLZ */ || - this.dfd.colorModel === DFDModel.UASTC ) ) { - - throw new Error( 'THREE.KTX2Loader: Only Basis Universal supercompression is currently supported.' ); - - } - - - /////////////////////////////////////////////////// - // Key/Value Data (KVD) - /////////////////////////////////////////////////// - - // Not implemented. - this.kvd = {}; - - - /////////////////////////////////////////////////// - // Supercompression Global Data (SGD) - /////////////////////////////////////////////////// - - this.sgd = {}; - - if ( this.header.sgdByteLength <= 0 ) return; - - var sgdReader = new KTX2BufferReader( - this.arrayBuffer, - this.header.sgdByteOffset, - this.header.sgdByteLength, - littleEndian - ); - - this.sgd.endpointCount = sgdReader.nextUint16(); - this.sgd.selectorCount = sgdReader.nextUint16(); - this.sgd.endpointsByteLength = sgdReader.nextUint32(); - this.sgd.selectorsByteLength = sgdReader.nextUint32(); - this.sgd.tablesByteLength = sgdReader.nextUint32(); - this.sgd.extendedByteLength = sgdReader.nextUint32(); - this.sgd.imageDescs = []; - this.sgd.endpointsData = null; - this.sgd.selectorsData = null; - this.sgd.tablesData = null; - this.sgd.extendedData = null; - - for ( var i = 0; i < this.header.levelCount; i ++ ) { - - this.sgd.imageDescs.push( { - - imageFlags: sgdReader.nextUint32(), - rgbSliceByteOffset: sgdReader.nextUint32(), - rgbSliceByteLength: sgdReader.nextUint32(), - alphaSliceByteOffset: sgdReader.nextUint32(), - alphaSliceByteLength: sgdReader.nextUint32(), - - } ); - - } - - var endpointsByteOffset = this.header.sgdByteOffset + sgdReader.offset; - var selectorsByteOffset = endpointsByteOffset + this.sgd.endpointsByteLength; - var tablesByteOffset = selectorsByteOffset + this.sgd.selectorsByteLength; - var extendedByteOffset = tablesByteOffset + this.sgd.tablesByteLength; - - this.sgd.endpointsData = new Uint8Array( this.arrayBuffer, endpointsByteOffset, this.sgd.endpointsByteLength ); - this.sgd.selectorsData = new Uint8Array( this.arrayBuffer, selectorsByteOffset, this.sgd.selectorsByteLength ); - this.sgd.tablesData = new Uint8Array( this.arrayBuffer, tablesByteOffset, this.sgd.tablesByteLength ); - this.sgd.extendedData = new Uint8Array( this.arrayBuffer, extendedByteOffset, this.sgd.extendedByteLength ); - - } - - async initMipmaps( transcoder, config ) { - - await this.zstd.init(); - - var TranscodeTarget = this.basisModule.TranscodeTarget; - var TextureFormat = this.basisModule.TextureFormat; - var ImageInfo = this.basisModule.ImageInfo; - - var scope = this; - - var mipmaps = []; - var width = this.getWidth(); - var height = this.getHeight(); - var texFormat = this.getTexFormat(); - var hasAlpha = this.getAlpha(); - var isVideo = false; - - // PVRTC1 transcoders (from both ETC1S and UASTC) only support power of 2 dimensions. - var pvrtcTranscodable = MathUtils.isPowerOfTwo( width ) && MathUtils.isPowerOfTwo( height ); - - if ( texFormat === TextureFormat.ETC1S ) { - - var numEndpoints = this.sgd.endpointCount; - var numSelectors = this.sgd.selectorCount; - var endpoints = this.sgd.endpointsData; - var selectors = this.sgd.selectorsData; - var tables = this.sgd.tablesData; - - transcoder.decodePalettes( numEndpoints, endpoints, numSelectors, selectors ); - transcoder.decodeTables( tables ); - - } - - - var targetFormat; - - if ( config.astcSupported ) { - - targetFormat = TranscodeTarget.ASTC_4x4_RGBA; - this.transcodedFormat = RGBA_ASTC_4x4_Format; - - } else if ( config.bptcSupported && texFormat === TextureFormat.UASTC4x4 ) { - - targetFormat = TranscodeTarget.BC7_M5_RGBA; - this.transcodedFormat = RGBA_BPTC_Format; - - } else if ( config.dxtSupported ) { - - targetFormat = hasAlpha ? TranscodeTarget.BC3_RGBA : TranscodeTarget.BC1_RGB; - this.transcodedFormat = hasAlpha ? RGBA_S3TC_DXT5_Format : RGB_S3TC_DXT1_Format; - - } else if ( config.pvrtcSupported && pvrtcTranscodable ) { - - targetFormat = hasAlpha ? TranscodeTarget.PVRTC1_4_RGBA : TranscodeTarget.PVRTC1_4_RGB; - this.transcodedFormat = hasAlpha ? RGBA_PVRTC_4BPPV1_Format : RGB_PVRTC_4BPPV1_Format; - - } else if ( config.etc2Supported ) { - - targetFormat = hasAlpha ? TranscodeTarget.ETC2_RGBA : TranscodeTarget.ETC1_RGB/* subset of ETC2 */; - this.transcodedFormat = hasAlpha ? RGBA_ETC2_EAC_Format : RGB_ETC2_Format; - - } else if ( config.etc1Supported ) { - - targetFormat = TranscodeTarget.ETC1_RGB; - this.transcodedFormat = RGB_ETC1_Format; - - } else { - - console.warn( 'THREE.KTX2Loader: No suitable compressed texture format found. Decoding to RGBA32.' ); - - targetFormat = TranscodeTarget.RGBA32; - this.transcodedFormat = RGBAFormat; - - } - - if ( ! this.basisModule.isFormatSupported( targetFormat, texFormat ) ) { - - throw new Error( 'THREE.KTX2Loader: Selected texture format not supported by current transcoder build.' ); - - } - - var imageDescIndex = 0; - - for ( var level = 0; level < this.header.levelCount; level ++ ) { - - var levelWidth = Math.ceil( width / Math.pow( 2, level ) ); - var levelHeight = Math.ceil( height / Math.pow( 2, level ) ); - - var numImagesInLevel = 1; // TODO(donmccurdy): Support cubemaps, arrays and 3D. - var imageOffsetInLevel = 0; - var imageInfo = new ImageInfo( texFormat, levelWidth, levelHeight, level ); - var levelByteLength = this.levels[ level ].byteLength; - var levelUncompressedByteLength = this.levels[ level ].uncompressedByteLength; - - for ( var imageIndex = 0; imageIndex < numImagesInLevel; imageIndex ++ ) { - - var result; - var encodedData; - - if ( texFormat === TextureFormat.UASTC4x4 ) { - - // UASTC - - imageInfo.flags = 0; - imageInfo.rgbByteOffset = 0; - imageInfo.rgbByteLength = levelUncompressedByteLength; - imageInfo.alphaByteOffset = 0; - imageInfo.alphaByteLength = 0; - - encodedData = new Uint8Array( this.arrayBuffer, this.levels[ level ].byteOffset + imageOffsetInLevel, levelByteLength ); - - if ( this.header.supercompressionScheme === 2 /* ZSTD */ ) { - - encodedData = this.zstd.decode( encodedData, levelUncompressedByteLength ); - - } - - result = transcoder.transcodeImage( targetFormat, encodedData, imageInfo, 0, hasAlpha, isVideo ); - - } else { - - // ETC1S - - var imageDesc = this.sgd.imageDescs[ imageDescIndex ++ ]; - - imageInfo.flags = imageDesc.imageFlags; - imageInfo.rgbByteOffset = 0; - imageInfo.rgbByteLength = imageDesc.rgbSliceByteLength; - imageInfo.alphaByteOffset = imageDesc.alphaSliceByteOffset > 0 ? imageDesc.rgbSliceByteLength : 0; - imageInfo.alphaByteLength = imageDesc.alphaSliceByteLength; - - encodedData = new Uint8Array( this.arrayBuffer, this.levels[ level ].byteOffset + imageDesc.rgbSliceByteOffset, imageDesc.rgbSliceByteLength + imageDesc.alphaSliceByteLength ); - - result = transcoder.transcodeImage( targetFormat, encodedData, imageInfo, 0, isVideo ); - - } - - if ( result.transcodedImage === undefined ) { - - throw new Error( 'THREE.KTX2Loader: Unable to transcode image.' ); - - } - - // Transcoded image is written in memory allocated by WASM. We could avoid copying - // the image by waiting until the image is uploaded to the GPU, then calling - // delete(). However, (1) we don't know if the user will later need to re-upload it - // e.g. after calling texture.clone(), and (2) this code will eventually be in a - // Web Worker, and transferring WASM's memory seems like a very bad idea. - var levelData = result.transcodedImage.get_typed_memory_view().slice(); - result.transcodedImage.delete(); - - mipmaps.push( { data: levelData, width: levelWidth, height: levelHeight } ); - imageOffsetInLevel += levelByteLength; - - } - - } - - scope.mipmaps = mipmaps; - - } - - getWidth() { - - return this.header.pixelWidth; - - } - - getHeight() { - - return this.header.pixelHeight; - - } - - getEncoding() { - - return this.dfd.transferFunction === 2 /* KHR_DF_TRANSFER_SRGB */ - ? sRGBEncoding - : LinearEncoding; - - } - - getTexFormat() { - - var TextureFormat = this.basisModule.TextureFormat; - - return this.dfd.colorModel === DFDModel.UASTC ? TextureFormat.UASTC4x4 : TextureFormat.ETC1S; - - } - - getAlpha() { - - var TextureFormat = this.basisModule.TextureFormat; - - // TODO(donmccurdy): Handle all channelIDs (i.e. the R & R+G cases), - // choosing appropriate transcode target formats or providing queries - // for applications so they know what to do with the content. - - if ( this.getTexFormat() === TextureFormat.UASTC4x4 ) { - - // UASTC - - if ( ( this.dfd.samples[ 0 ].channelID & 0xF ) === DFDChannel.UASTC.RGBA ) { - - return true; - - } - - return false; - - } - - // ETC1S - - if ( this.dfd.numSamples === 2 && ( this.dfd.samples[ 1 ].channelID & 0xF ) === DFDChannel.ETC1S.AAA ) { - - return true; - - } - - return false; - - } - - getPremultiplyAlpha() { - - return !! ( this.dfd.flags & 1 /* KHR_DF_FLAG_ALPHA_PREMULTIPLIED */ ); - - } - - } - - class KTX2BufferReader { - - constructor( arrayBuffer, byteOffset, byteLength, littleEndian ) { - - this.dataView = new DataView( arrayBuffer, byteOffset, byteLength ); - this.littleEndian = littleEndian; - this.offset = 0; - - } - - nextUint8() { - - var value = this.dataView.getUint8( this.offset, this.littleEndian ); - - this.offset += 1; - - return value; - - } - - nextUint16() { - - var value = this.dataView.getUint16( this.offset, this.littleEndian ); - - this.offset += 2; - - return value; - - } - - nextUint32() { - - var value = this.dataView.getUint32( this.offset, this.littleEndian ); - - this.offset += 4; - - return value; - - } - - nextUint64() { - - // https://stackoverflow.com/questions/53103695/ - var left = this.dataView.getUint32( this.offset, this.littleEndian ); - var right = this.dataView.getUint32( this.offset + 4, this.littleEndian ); - var value = this.littleEndian ? left + ( 2 ** 32 * right ) : ( 2 ** 32 * left ) + right; - - if ( ! Number.isSafeInteger( value ) ) { - - console.warn( 'THREE.KTX2Loader: ' + value + ' exceeds MAX_SAFE_INTEGER. Precision may be lost.' ); - - } - - this.offset += 8; - - return value; - - } - - skip( bytes ) { - - this.offset += bytes; - - return this; - - } - - } - - class TileLoader { - // This class contains the common code to load tile content, such as b3dm and pnts files. - // It is not to be used directly. Instead, subclasses are used to implement specific - // content loaders for different tile types. - constructor(url) { - this.url = url; - this.type = url.slice(-4); - this.version = null; - this.byteLength = null; - this.featureTableJSON = null; - this.featureTableBinary = null; - this.batchTableJson = null; - this.batchTableBinary = null; - this.binaryData = null; - } - // TileLoader.load - async load() { - this.abortController = new AbortController(); - let response = await fetch(this.url, {signal: this.abortController.signal}); - this.abortController = null; - if (!response.ok) { - throw new Error(`HTTP ${response.status} - ${response.statusText}`); - } - let buffer = await response.arrayBuffer(); - let res = await this.parseResponse(buffer); - return res; - } - abortLoad() { - if (this.abortController) { - this.abortController.abort(); - this.abortController = null; - return true; - } - return false; - } - async parseResponse(buffer) { - let header = new Uint32Array(buffer.slice(0, 32)); - let decoder = new TextDecoder(); - let magic = decoder.decode(new Uint8Array(buffer.slice(0, 4))); - if (magic != this.type) { - throw new Error(`Invalid magic string, expected '${this.type}', got '${this.magic}'`); - } - this.version = header[1]; - this.byteLength = header[2]; - let featureTableJSONByteLength = header[3]; - let featureTableBinaryByteLength = header[4]; - let batchTableJsonByteLength = header[5]; - let batchTableBinaryByteLength = header[6]; - let gltfFormat = magic === 'i3dm' ? header[7] : 1; - - /* - console.log('magic: ' + magic); - console.log('version: ' + this.version); - console.log('featureTableJSONByteLength: ' + featureTableJSONByteLength); - console.log('featureTableBinaryByteLength: ' + featureTableBinaryByteLength); - console.log('batchTableJsonByteLength: ' + batchTableJsonByteLength); - console.log('batchTableBinaryByteLength: ' + batchTableBinaryByteLength); - */ - - let pos = magic === 'i3dm' ? 32 : 28; // header length - if (featureTableJSONByteLength > 0) { - this.featureTableJSON = JSON.parse( - decoder.decode(new Uint8Array(buffer.slice(pos, pos + featureTableJSONByteLength))) - ); - pos += featureTableJSONByteLength; - } else { - this.featureTableJSON = {}; - } - this.featureTableBinary = buffer.slice(pos, pos + featureTableBinaryByteLength); - pos += featureTableBinaryByteLength; - if (batchTableJsonByteLength > 0) { - this.batchTableJson = JSON.parse( - decoder.decode(new Uint8Array(buffer.slice(pos, pos + batchTableJsonByteLength))) - ); - pos += batchTableJsonByteLength; - } else { - this.batchTableJson = {}; - } - this.batchTableBinary = buffer.slice(pos, pos + batchTableBinaryByteLength); - pos += batchTableBinaryByteLength; - if (gltfFormat === 1) { - this.binaryData = buffer.slice(pos); - } else { - // load binary data from url at pos - let modelUrl = decoder.decode(new Uint8Array(buffer.slice(pos))); - if (internalGLTFCache.has(modelUrl)) { - this.binaryData = internalGLTFCache.get(modelUrl); - } else { - let response = await fetch(modelUrl); - if (!response.ok) { - throw new Error(`HTTP ${response.status} - ${response.statusText}`); - } - this.binaryData = await response.arrayBuffer(); - internalGLTFCache.set(modelUrl, this.binaryData); - } - } - return this; - } - } - - class B3DM extends TileLoader { - constructor(url) { - super(url); - this.glbData = null; - } - async parseResponse(buffer) { - await super.parseResponse(buffer); - this.glbData = this.binaryData; - return this; - } - } - - class CMPT extends TileLoader { - constructor(url) { - super(url); - } - async parseResponse(buffer) { - let header = new Uint32Array(buffer.slice(0, 4*4)); - let decoder = new TextDecoder(); - let magic = decoder.decode(new Uint8Array(buffer.slice(0, 4))); - if (magic != this.type) { - throw new Error(`Invalid magic string, expected '${this.type}', got '${this.magic}'`); - } - this.version = header[1]; - this.byteLength = header[2]; - this.tilesLength = header[3]; - let innerTiles = []; - let tileStart = 16; - for (let i = 0; i < this.tilesLength; i++) { - let tileHeader = new Uint32Array(buffer.slice(tileStart, tileStart + 3 * 4)); - let tileMagic = decoder.decode(new Uint8Array(buffer.slice(tileStart, tileStart + 4))); - //console.log(`innerTile: ${i}, magic: ${tileMagic}`); - let tileByteLength = tileHeader[2]; - let tileData = buffer.slice(tileStart, tileStart + tileByteLength); - innerTiles.push({type: tileMagic, data: tileData}); - tileStart += tileByteLength; - } - return innerTiles; - } - } - - class PNTS extends TileLoader { - constructor(url) { - super(url); - this.points = new Float32Array(); - this.rgba = null; - this.rgb = null; - } - parseResponse(buffer) { - super.parseResponse(buffer); - if (this.featureTableJSON.POINTS_LENGTH && this.featureTableJSON.POSITION) { - let len = this.featureTableJSON.POINTS_LENGTH; - let pos = this.featureTableJSON.POSITION.byteOffset; - this.points = new Float32Array( - this.featureTableBinary.slice(pos, pos + len * Float32Array.BYTES_PER_ELEMENT * 3) - ); - this.rtc_center = this.featureTableJSON.RTC_CENTER; - if (this.featureTableJSON.RGBA) { - pos = this.featureTableJSON.RGBA.byteOffset; - let colorInts = new Uint8Array( - this.featureTableBinary.slice(pos, pos + len * Uint8Array.BYTES_PER_ELEMENT * 4) - ); - let rgba = new Float32Array(colorInts.length); - for (let i = 0; i < colorInts.length; i++) { - rgba[i] = colorInts[i] / 255.0; - } - this.rgba = rgba; - } else if (this.featureTableJSON.RGB) { - pos = this.featureTableJSON.RGB.byteOffset; - let colorInts = new Uint8Array( - this.featureTableBinary.slice(pos, pos + len * Uint8Array.BYTES_PER_ELEMENT * 3) - ); - let rgb = new Float32Array(colorInts.length); - for (let i = 0; i < colorInts.length; i++) { - rgb[i] = colorInts[i] / 255.0; - } - this.rgb = rgb; - } else if (this.featureTableJSON.RGB565) { - console.error('RGB565 is currently not supported in pointcloud tiles.'); - } - } - return this; - } - } - - let internalGLTFCache = new Map(); - - function YToLat(Y) { - return (Math.atan(Math.pow(Math.E, ((Y / 111319.490778) * Math.PI) / 180.0)) * 360.0) / Math.PI - 90.0; - } - - function LatToScale(lat) { - return 1 / Math.cos((lat * Math.PI) / 180); - } - - function GetModel(modelId, children) { - for (let i = 0; i < children.length; i++) { - const element = children[i]; - if (element.type === 'Group') { - if (element.children) { - const model = GetModel(modelId, element.children); - if (model) { - return model; - } - } - } else if (element.type === 'Mesh') { - if (element.userData.b3dm === modelId) { - return element.parent; - } - } - } - } - - async function IMesh(inmesh, instancesParams, inverseMatrix) { - /* intancesParams { - positions: float32[] - rtcCenter?: float32[3] - normalsRight?: float32[] - normalsUp?: float32[] - scales?: float32[] - xyzScales?: float32[] - } */ - let matrix = new Matrix4(); - let position = new Vector3(); - let rotation = new Euler(); - let quaternion = new Quaternion(); - let scale = new Vector3(); - let rtcCenter = instancesParams.rtcCenter ? instancesParams.rtcCenter : [0.0, 0.0, 0.0]; - - let geometry = inmesh.geometry; - geometry.applyMatrix4(inmesh.matrixWorld); // apply world modifiers to geometry - - let material = inmesh.material; - let positions = instancesParams.positions; - let instanceCount = positions.length / 3; - let instancedMesh = new InstancedMesh(geometry, material, instanceCount); - instancedMesh.userData = inmesh.userData; - - if (instancesParams.rtcCenter) { - rtcCenter = instancesParams.rtcCenter; - } - - for (let i = 0; i < instanceCount; i++) { - position = { - x: positions[i * 3] + (rtcCenter[0] + inverseMatrix.elements[12]), - y: positions[i * 3 + 1] + (rtcCenter[1] + inverseMatrix.elements[13]), - z: positions[i * 3 + 2] + (rtcCenter[2] + inverseMatrix.elements[14]) - }; - if (instancesParams.normalsRight) { - rotation.set(0, 0, Math.atan2(instancesParams.normalsRight[i * 3 + 1], instancesParams.normalsRight[i * 3])); - quaternion.setFromEuler(rotation); - } - scale.x = scale.y = scale.z = LatToScale(YToLat(positions[i * 3 + 1])); - if (instancesParams.scales) { - scale.x *= instancesParams.scales[i]; - scale.y *= instancesParams.scales[i]; - scale.z *= instancesParams.scales[i]; - } - if (instancesParams.xyzScales) { - scale.x *= instancesParams.xyzScales[i * 3]; - scale.y *= instancesParams.xyzScales[i * 3 + 1]; - scale.z *= instancesParams.xyzScales[i * 3 + 2]; - } - matrix.compose(position, quaternion, scale); - instancedMesh.setMatrixAt(i, matrix); - instancedMesh.castShadow = true; - } - - return instancedMesh; - } - - function applyStyle(scene,styleParams){ - let maincolor = null; - if (styleParams.color != null) { - maincolor = new Color(styleParams.color); - } - scene.traverse(child => { - if (child instanceof Mesh) { - - if (styleParams.color != null) { - child.material.color = maincolor; - } - if (styleParams.opacity != null) { - child.material.opacity = styleParams.opacity; - child.material.transparent = styleParams.opacity < 1.0 ? true : false; - } - - // some gltf has wrong bounding data, recompute here - child.geometry.computeBoundingBox(); - child.geometry.computeBoundingSphere(); - child.castShadow = true; - - //For changing individual colors later, we have to introduce vertexcolors - //const color = new THREE.Color(); - const positions = child.geometry.attributes.position; - const count = positions.count; - child.geometry.setAttribute( 'color', new BufferAttribute( new Float32Array( count * 3 ), 3 ) ); - const colors = child.geometry.attributes.color; - const color = new Color(); - const grey = new Color("rgb(20,20,20)"); - const ymin = child.geometry.boundingBox.min.y; - const ymax = child.geometry.boundingBox.max.y; - //Currently attributes are kind of hardcoded in the tiles and have to be unpacked - //let magnitude = scaleSequential(interpolateYlGnBu).domain([1600, 2020]) - //const colormap = child.parent.userData.attr.map(d=>magnitude(d[0])); - for ( let i = 0; i < count; i ++ ) { - //Assign every vertex it's own color - - //let batchid = child.geometry.attributes._batchid.getX(i); - //let colorval = colormap[batchid]; - let colorval = child.material.color; - color.set(colorval); - //Create a little gradient from black to white - //adding 0.3 not to start at black, dividing by 10 limits effect to bottom - let greyval = Math.min( 0.8 + ( positions.getY( i ) + Math.abs( ymin )) / 1, 1 ); - color.lerp ( grey, 1-greyval ); //lerp to grey - colors.setXYZ( i, color.r, color.g, color.b ); - } - child.material.vertexColors = true; - child.material.depthWrite = !child.material.transparent; // necessary for Velsen dataset? - - } - }); - /* - if (styleParams.color != null || styleParams.opacity != null) { - let color = new THREE.Color(styleParams.color); - scene.traverse(child => { - if (child instanceof THREE.Mesh) { - if (styleParams.color != null) - child.material.color = color; - - if (styleParams.opacity != null) { - child.material.opacity = styleParams.opacity; - child.material.transparent = styleParams.opacity < 1.0 ? true : false; - } - } - }); - }*/ - if (styleParams.debugColor) { - scene.traverse(child => { - if (child instanceof Mesh) { - child.material.color = styleParams.debugColor; - } - }); - } - return scene; - } - - class ThreeDeeTile { - constructor(json, resourcePath, styleParams, updateCallback, parentRefine, parentTransform,projectToMercator) { - this.loaded = false; - this.styleParams = styleParams; - this.updateCallback = updateCallback; - this.resourcePath = resourcePath; - this.projectToMercator = projectToMercator; - this.totalContent = new Group(); // Three JS Object3D Group for this tile and all its children - this.tileContent = new Group(); // Three JS Object3D Group for this tile's content - this.childContent = new Group(); // Three JS Object3D Group for this tile's children - this.totalContent.add(this.tileContent); - this.totalContent.add(this.childContent); - this.boundingVolume = json.boundingVolume; - if (this.boundingVolume && this.boundingVolume.box) { - let b = this.boundingVolume.box; - let extent = [b[0] - b[3], b[1] - b[7], b[0] + b[3], b[1] + b[7]]; - let sw = new Vector3(extent[0], extent[1], b[2] - b[11]); - let ne = new Vector3(extent[2], extent[3], b[2] + b[11]); - this.box = new Box3(sw, ne); - { - //ToDo: I3BM doesn't seem to work without the debugLine, add a transparant one for now - let line = new LineSegments( new EdgesGeometry(new BoxGeometry(b[3] * 2, b[7] * 2, b[11] * 2)), new LineBasicMaterial( {color: new Color(0xff0000), transparent: true, linewidth: 0, depthWrite: false, visible: true, opacity: 0.0}) ); - this.debugLine = line; - } - } else { - this.extent = null; - this.sw = null; - this.ne = null; - this.box = null; - this.center = null; - } - this.refine = json.refine ? json.refine.toUpperCase() : parentRefine; - this.geometricError = json.geometricError; - this.worldTransform = parentTransform ? parentTransform.clone() : new Matrix4(); - this.transform = json.transform; - if (this.transform) - { - let tileMatrix = new Matrix4().fromArray(this.transform); - this.totalContent.applyMatrix4(tileMatrix); - this.worldTransform.multiply(tileMatrix); - } - this.content = json.content; - this.children = []; - if (json.children) { - for (let i=0; ithis.updateCallback(ts)); - await subTileset.load(url, this.styleParams); - if (subTileset.root) { - this.box.applyMatrix4(this.worldTransform); - let inverseMatrix = new Matrix4().getInverse(this.worldTransform); - this.totalContent.applyMatrix4(inverseMatrix); - this.totalContent.updateMatrixWorld(); - this.worldTransform = new Matrix4(); - - this.children.push(subTileset.root); - this.childContent.add(subTileset.root.totalContent); - subTileset.root.totalContent.updateMatrixWorld(); - subTileset.root.checkLoad(this.frustum, this.cameraPosition); - } - } catch (error) { - // load failed (wrong url? connection issues?) - // log error, do not break program flow - console.error(error); - } - break; - case 'b3dm': - try { - this.tileLoader = new B3DM(url); - let b3dmData = await this.tileLoader.load(); - this.tileLoader = null; - this.b3dmAdd(b3dmData, url); - } catch (error) { - if (error.name === "AbortError") { - this.loaded = false; - return; - } - console.error(error); - } - break; - case 'i3dm': - try { - this.tileLoader = new B3DM(url); - let i3dmData = await this.tileLoader.load(); - this.tileLoader = null; - this.i3dmAdd(i3dmData); - } catch (error) { - if (error.name === "AbortError") { - this.loaded = false; - return; - } - console.error(error.message); - } - break; - case 'pnts': - try { - this.tileLoader = new PNTS(url); - let pointData = await this.tileLoader.load(); - this.tileLoader = null; - this.pntsAdd(pointData); - } catch (error) { - if (error.name === "AbortError") { - this.loaded = false; - return; - } - console.error(error); - } - break; - case 'cmpt': - try { - this.tileLoader = new CMPT(url); - let compositeTiles = await this.tileLoader.load(); - this.tileLoader = null; - this.cmptAdd(compositeTiles, url); - } catch (error) { - if (error.name === "AbortError") { - this.loaded = false; - return; - } - console.error(error); - } - break; - default: - throw new Error('invalid tile type: ' + type); - } - } - this.updateCallback(this); - } - async cmptAdd(compositeTiles, url) { - if (this.cmptAdded) { - // prevent duplicate adding - return; - } - this.cmptAdded = true; - for (let innerTile of compositeTiles) { - switch(innerTile.type) { - case 'i3dm': - let i3dm = new B3DM('.i3dm'); - let i3dmData = await i3dm.parseResponse(innerTile.data); - this.i3dmAdd(i3dmData); - break; - case 'b3dm': - let b3dm = new B3DM('.b3dm'); - let b3dmData = await b3dm.parseResponse(innerTile.data); - this.b3dmAdd(b3dmData, url.slice(0,-4) + 'b3dm'); - break; - case 'pnts': - let pnts = new PNTS('.pnts'); - let pointData = pnts.parseResponse(innerTile.data); - this.pntsAdd(pointData); - break; - case 'cmpt': - let cmpt = new CMPT('.cmpt'); - let subCompositeTiles = cmpt.parseResponse(innerTile.data); - this.cmptAdd(subCompositeTiles); - break; - default: - console.error(`Composite type ${innerTile.type} not supported`); - break; - } - //console.log(`type: ${innerTile.type}, size: ${innerTile.data.byteLength}`); - } - } - pntsAdd(pointData) { - if (this.pntsAdded && !this.cmptAdded) { - // prevent duplicate adding - return; - } - this.pntsAdded = true; - let geometry = new BufferGeometry(); - geometry.setAttribute('position', new Float32BufferAttribute(pointData.points, 3)); - let material = new PointsMaterial(); - material.size = this.styleParams.pointsize != null ? this.styleParams.pointsize : 1.0; - if (this.styleParams.color) { - material.vertexColors = NoColors; - material.color = new Color(this.styleParams.color); - material.opacity = this.styleParams.opacity != null ? this.styleParams.opacity : 1.0; - } else if (pointData.rgba) { - geometry.setAttribute('color', new Float32BufferAttribute(pointData.rgba, 4)); - material.vertexColors = VertexColors; - } else if (pointData.rgb) { - geometry.setAttribute('color', new Float32BufferAttribute(pointData.rgb, 3)); - material.vertexColors = VertexColors; - } - this.tileContent.add(new Points( geometry, material )); - if (pointData.rtc_center) { - let c = pointData.rtc_center; - this.tileContent.applyMatrix4(new Matrix4().makeTranslation(c[0], c[1], c[2])); - } - this.tileContent.add(new Points( geometry, material )); - } - b3dmAdd(b3dmData, url) { - if (this.b3dmAdded && !this.cmptAdded) { - // prevent duplicate adding - return; - } - this.b3dmAdded = true; - let dracoloader = new DRACOLoader().setDecoderPath('assets/wasm/'); - let loader = new GLTFLoader().setDRACOLoader(dracoloader).setKTX2Loader(new KTX2Loader()); - let rotateX = new Matrix4().makeRotationAxis(new Vector3(1, 0, 0), Math.PI / 2); - this.tileContent.applyMatrix4(rotateX); // convert from GLTF Y-up to Z-up - loader.parse(b3dmData.glbData, this.resourcePath, (gltf) => { - let scene = gltf.scene || gltf.scenes[0]; - //Add the batchtable to the userData since gltfLoader doesn't deal with it - scene.userData = b3dmData.batchTableJson; - if (scene.userData && Array.isArray(b3dmData.batchTableJson.attr)) { - scene.userData.attr = scene.userData.attr.map(d=>d.split(",")); - scene.userData.b3dm= url.replace(this.resourcePath, '').replace('.b3dm', ''); - } - scene = applyStyle(scene,this.styleParams); - - if (this.projectToMercator) { - //TODO: must be a nicer way to get the local Y in webmerc. than worldTransform.elements - scene.scale.setScalar(LatToScale(YToLat(this.worldTransform.elements[13]))); - } - this.tileContent.add(scene); - dracoloader.dispose(); - }, (error) => { - throw new Error('error parsing gltf: ' + error); - }); - } - i3dmAdd(i3dmData) { - if (this.i3dmAdded && !this.cmptAdded) { - // prevent duplicate adding - return; - } - this.i3dmAdded = true; - let loader = new GLTFLoader().setDRACOLoader(new DRACOLoader().setDecoderPath('assets/wasm/')).setKTX2Loader(new KTX2Loader()); - // Check what metadata is present in the featuretable, currently using: https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/Instanced3DModel#instance-orientation. - let metadata = i3dmData.featureTableJSON; - if (!metadata.POSITION) { - console.error(`i3dm missing position metadata`); - return; - } - let instancesParams = { - positions : new Float32Array(i3dmData.featureTableBinary, metadata.POSITION.byteOffset, metadata.INSTANCES_LENGTH * 3) - }; - if (metadata.RTC_CENTER) { - if (Array.isArray(metadata.RTC_CENTER) && metadata.RTC_CENTER.length === 3) { - instancesParams.rtcCenter = [metadata.RTC_CENTER[0], metadata.RTC_CENTER[1],metadata.RTC_CENTER[2]]; - } - } - if (metadata.NORMAL_UP && metadata.NORMAL_RIGHT) { - instancesParams.normalsRight = new Float32Array(i3dmData.featureTableBinary, metadata.NORMAL_RIGHT.byteOffset, metadata.INSTANCES_LENGTH * 3); - instancesParams.normalsUp = new Float32Array(i3dmData.featureTableBinary, metadata.NORMAL_UP.byteOffset, metadata.INSTANCES_LENGTH * 3); - } - if (metadata.SCALE) { - instancesParams.scales = new Float32Array(i3dmData.featureTableBinary, metadata.SCALE.byteOffset, metadata.INSTANCES_LENGTH); - } - if (metadata.SCALE_NON_UNIFORM) { - instancesParams.xyzScales = new Float32Array(i3dmData.featureTableBinary, metadata.SCALE_NON_UNIFORM.byteOffset, metadata.INSTANCES_LENGTH); - } - let inverseMatrix = new Matrix4().getInverse(this.worldTransform); // in order to offset by the tile - let self = this; - loader.parse(i3dmData.glbData, this.resourcePath, (gltf) => { - let scene = gltf.scene || gltf.scenes[0]; - scene.rotateX(Math.PI / 2); // convert from GLTF Y-up to Mapbox Z-up - scene.updateMatrixWorld(true); - - scene.traverse(child => { - if (child instanceof Mesh) { - child.userData = i3dmData.batchTableJson; - IMesh(child, instancesParams, inverseMatrix) - .then(d=>self.tileContent.add(d)); - } - }); - }); - } - - unload(includeChildren) { - if (this.tileLoader) { - this.tileLoader.abortLoad(); - } - - this.unloadedTileContent = true; - - //Clean up (TODO: make a grace period in which object can stay in cache) - this.freeObjectFromMemory(this.tileContent); - this.totalContent.remove(this.tileContent); - this.tileContent = new Group(); - this.loaded = false; - this.b3dmAdded = false; - - //this.tileContent.visible = false; - if (includeChildren) { - this.unloadedChildContent = true; - this.totalContent.remove(this.childContent); - //this.childContent.visible = false; - } else { - if (this.unloadedChildContent) { - this.unloadedChildContent = false; - this.totalContent.add(this.childContent); - } - } - if (this.debugLine) { - this.totalContent.remove(this.debugLine); - this.unloadedDebugContent = true; - } - this.updateCallback(this); - - - } - checkLoad(frustum, cameraPosition) { - - this.frustum = frustum; - this.cameraPosition = cameraPosition; - /*this.load(); - for (let i=0; i 0.0 && dist > this.geometricError * 50.0) { - // remove from memory - this.unload(true); - return; - } - - //console.log(`camPos: ${cameraPosition.z}, dist: ${dist}, geometricError: ${this.geometricError}`); - // should we load this tile? - if ((this.refine == 'REPLACE' && dist < this.geometricError * 20.0 && this.children.length > 0)) { - this.unload(false); - } else { - this.load(); - } - - // should we load its children? - for (let i=0; i 0.25) { - this.load(); - this.children.forEach(child => { - child.checkLoad(camera); - }); - }*/ - - } - - freeObjectFromMemory(object) { - object.traverse(function(obj){ - if (obj.material && obj.material.dispose) { - obj.material.dispose(); - if (obj.material.map) { - obj.material.map.dispose(); - } - } - if (obj.geometry && obj.geometry.dispose) { - obj.geometry.dispose(); - obj.geometry.attributes.color = {}; - obj.geometry.attributes.normal = {}; - obj.geometry.attributes.position = {}; - obj.geometry.attributes.uv = {}; - obj.geometry.attributes = {}; - obj.material = {}; - } - }); - } - } - - class TileSet { - constructor(updateCallback) { - if (!updateCallback) { - updateCallback = () => {}; - } - this.updateCallback = updateCallback; - this.url = null; - this.version = null; - this.gltfUpAxis = 'Z'; - this.geometricError = null; - this.root = null; - } - // TileSet.load - async load(url, styleParams, projectToMercator) { - this.url = url; - let resourcePath = LoaderUtils.extractUrlBase(url); - - let response = await fetch(this.url); - if (!response.ok) { - throw new Error(`HTTP ${response.status} - ${response.statusText}`); - } - let json = await response.json(); - this.version = json.asset.version; - this.geometricError = json.geometricError; - this.refine = json.root.refine ? json.root.refine.toUpperCase() : 'ADD'; - this.root = new ThreeDeeTile( - json.root, - resourcePath, - styleParams, - this.updateCallback, - this.refine, - null, - projectToMercator - ); - return; - } - } - - var LineSegmentsGeometry = function () { - - InstancedBufferGeometry.call( this ); - - this.type = 'LineSegmentsGeometry'; - - var positions = [ - 1, 2, 0, 1, 2, 0, - 1, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 0, - 1, - 1, 0, 1, - 1, 0 ]; - var uvs = [ - 1, 2, 1, 2, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 2, 1, - 2 ]; - var index = [ 0, 2, 1, 2, 3, 1, 2, 4, 3, 4, 5, 3, 4, 6, 5, 6, 7, 5 ]; - - this.setIndex( index ); - this.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - }; - - LineSegmentsGeometry.prototype = Object.assign( Object.create( InstancedBufferGeometry.prototype ), { - - constructor: LineSegmentsGeometry, - - isLineSegmentsGeometry: true, - - applyMatrix4: function ( matrix ) { - - var start = this.attributes.instanceStart; - var end = this.attributes.instanceEnd; - - if ( start !== undefined ) { - - start.applyMatrix4( matrix ); - - end.applyMatrix4( matrix ); - - start.needsUpdate = true; - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - return this; - - }, - - setPositions: function ( array ) { - - var lineSegments; - - if ( array instanceof Float32Array ) { - - lineSegments = array; - - } else if ( Array.isArray( array ) ) { - - lineSegments = new Float32Array( array ); - - } - - var instanceBuffer = new InstancedInterleavedBuffer( lineSegments, 6, 1 ); // xyz, xyz - - this.setAttribute( 'instanceStart', new InterleavedBufferAttribute( instanceBuffer, 3, 0 ) ); // xyz - this.setAttribute( 'instanceEnd', new InterleavedBufferAttribute( instanceBuffer, 3, 3 ) ); // xyz - - // - - this.computeBoundingBox(); - this.computeBoundingSphere(); - - return this; - - }, - - setColors: function ( array ) { - - var colors; - - if ( array instanceof Float32Array ) { - - colors = array; - - } else if ( Array.isArray( array ) ) { - - colors = new Float32Array( array ); - - } - - var instanceColorBuffer = new InstancedInterleavedBuffer( colors, 6, 1 ); // rgb, rgb - - this.setAttribute( 'instanceColorStart', new InterleavedBufferAttribute( instanceColorBuffer, 3, 0 ) ); // rgb - this.setAttribute( 'instanceColorEnd', new InterleavedBufferAttribute( instanceColorBuffer, 3, 3 ) ); // rgb - - return this; - - }, - - fromWireframeGeometry: function ( geometry ) { - - this.setPositions( geometry.attributes.position.array ); - - return this; - - }, - - fromEdgesGeometry: function ( geometry ) { - - this.setPositions( geometry.attributes.position.array ); - - return this; - - }, - - fromMesh: function ( mesh ) { - - this.fromWireframeGeometry( new WireframeGeometry( mesh.geometry ) ); - - // set colors, maybe - - return this; - - }, - - fromLineSegments: function ( lineSegments ) { - - var geometry = lineSegments.geometry; - - if ( geometry.isGeometry ) { - - this.setPositions( geometry.vertices ); - - } else if ( geometry.isBufferGeometry ) { - - this.setPositions( geometry.attributes.position.array ); // assumes non-indexed - - } - - // set colors, maybe - - return this; - - }, - - computeBoundingBox: function () { - - var box = new Box3(); - - return function computeBoundingBox() { - - if ( this.boundingBox === null ) { - - this.boundingBox = new Box3(); - - } - - var start = this.attributes.instanceStart; - var end = this.attributes.instanceEnd; - - if ( start !== undefined && end !== undefined ) { - - this.boundingBox.setFromBufferAttribute( start ); - - box.setFromBufferAttribute( end ); - - this.boundingBox.union( box ); - - } - - }; - - }(), - - computeBoundingSphere: function () { - - var vector = new Vector3(); - - return function computeBoundingSphere() { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new Sphere(); - - } - - if ( this.boundingBox === null ) { - - this.computeBoundingBox(); - - } - - var start = this.attributes.instanceStart; - var end = this.attributes.instanceEnd; - - if ( start !== undefined && end !== undefined ) { - - var center = this.boundingSphere.center; - - this.boundingBox.getCenter( center ); - - var maxRadiusSq = 0; - - for ( var i = 0, il = start.count; i < il; i ++ ) { - - vector.fromBufferAttribute( start, i ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); - - vector.fromBufferAttribute( end, i ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); - - } - - this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); - - if ( isNaN( this.boundingSphere.radius ) ) { - - console.error( 'THREE.LineSegmentsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.', this ); - - } - - } - - }; - - }(), - - toJSON: function () { - - // todo - - }, - - applyMatrix: function ( matrix ) { - - console.warn( 'THREE.LineSegmentsGeometry: applyMatrix() has been renamed to applyMatrix4().' ); - - return this.applyMatrix4( matrix ); - - } - - } ); - - /** - * parameters = { - * color: , - * linewidth: , - * dashed: , - * dashScale: , - * dashSize: , - * gapSize: , - * resolution: , // to be set by renderer - * } - */ - - UniformsLib.line = { - - linewidth: { value: 1 }, - resolution: { value: new Vector2( 1, 1 ) }, - dashScale: { value: 1 }, - dashSize: { value: 1 }, - gapSize: { value: 1 }, // todo FIX - maybe change to totalSize - opacity: { value: 1 } - - }; - - ShaderLib[ 'line' ] = { - - uniforms: UniformsUtils.merge( [ - UniformsLib.common, - UniformsLib.fog, - UniformsLib.line - ] ), - - vertexShader: - ` - #include - #include - #include - #include - #include - - uniform float linewidth; - uniform vec2 resolution; - - attribute vec3 instanceStart; - attribute vec3 instanceEnd; - - attribute vec3 instanceColorStart; - attribute vec3 instanceColorEnd; - - varying vec2 vUv; - - #ifdef USE_DASH - - uniform float dashScale; - attribute float instanceDistanceStart; - attribute float instanceDistanceEnd; - varying float vLineDistance; - - #endif - - void trimSegment( const in vec4 start, inout vec4 end ) { - - // trim end segment so it terminates between the camera plane and the near plane - - // conservative estimate of the near plane - float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column - float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column - float nearEstimate = - 0.5 * b / a; - - float alpha = ( nearEstimate - start.z ) / ( end.z - start.z ); - - end.xyz = mix( start.xyz, end.xyz, alpha ); - - } - - void main() { - - #ifdef USE_COLOR - - vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd; - - #endif - - #ifdef USE_DASH - - vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd; - - #endif - - float aspect = resolution.x / resolution.y; - - vUv = uv; - - // camera space - vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 ); - vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 ); - - // special case for perspective projection, and segments that terminate either in, or behind, the camera plane - // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space - // but we need to perform ndc-space calculations in the shader, so we must address this issue directly - // perhaps there is a more elegant solution -- WestLangley - - bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column - - if ( perspective ) { - - if ( start.z < 0.0 && end.z >= 0.0 ) { - - trimSegment( start, end ); - - } else if ( end.z < 0.0 && start.z >= 0.0 ) { - - trimSegment( end, start ); - - } - - } - - // clip space - vec4 clipStart = projectionMatrix * start; - vec4 clipEnd = projectionMatrix * end; - - // ndc space - vec2 ndcStart = clipStart.xy / clipStart.w; - vec2 ndcEnd = clipEnd.xy / clipEnd.w; - - // direction - vec2 dir = ndcEnd - ndcStart; - - // account for clip-space aspect ratio - dir.x *= aspect; - dir = normalize( dir ); - - // perpendicular to dir - vec2 offset = vec2( dir.y, - dir.x ); - - // undo aspect ratio adjustment - dir.x /= aspect; - offset.x /= aspect; - - // sign flip - if ( position.x < 0.0 ) offset *= - 1.0; - - // endcaps - if ( position.y < 0.0 ) { - - offset += - dir; - - } else if ( position.y > 1.0 ) { - - offset += dir; - - } - - // adjust for linewidth - offset *= linewidth; - - // adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ... - offset /= resolution.y; - - // select end - vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd; - - // back to clip space - offset *= clip.w; - - clip.xy += offset; - - gl_Position = clip; - - vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation - - #include - #include - #include - - } - `, - - fragmentShader: - ` - uniform vec3 diffuse; - uniform float opacity; - - #ifdef USE_DASH - - uniform float dashSize; - uniform float gapSize; - - #endif - - varying float vLineDistance; - - #include - #include - #include - #include - #include - - varying vec2 vUv; - - void main() { - - #include - - #ifdef USE_DASH - - if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps - - if ( mod( vLineDistance, dashSize + gapSize ) > dashSize ) discard; // todo - FIX - - #endif - - if ( abs( vUv.y ) > 1.0 ) { - - float a = vUv.x; - float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0; - float len2 = a * a + b * b; - - if ( len2 > 1.0 ) discard; - - } - - vec4 diffuseColor = vec4( diffuse, opacity ); - - #include - #include - - gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a ); - - #include - #include - #include - #include - - } - ` - }; - - var LineMaterial = function ( parameters ) { - - ShaderMaterial.call( this, { - - type: 'LineMaterial', - - uniforms: UniformsUtils.clone( ShaderLib[ 'line' ].uniforms ), - - vertexShader: ShaderLib[ 'line' ].vertexShader, - fragmentShader: ShaderLib[ 'line' ].fragmentShader, - - clipping: true // required for clipping support - - } ); - - this.dashed = false; - - Object.defineProperties( this, { - - color: { - - enumerable: true, - - get: function () { - - return this.uniforms.diffuse.value; - - }, - - set: function ( value ) { - - this.uniforms.diffuse.value = value; - - } - - }, - - linewidth: { - - enumerable: true, - - get: function () { - - return this.uniforms.linewidth.value; - - }, - - set: function ( value ) { - - this.uniforms.linewidth.value = value; - - } - - }, - - dashScale: { - - enumerable: true, - - get: function () { - - return this.uniforms.dashScale.value; - - }, - - set: function ( value ) { - - this.uniforms.dashScale.value = value; - - } - - }, - - dashSize: { - - enumerable: true, - - get: function () { - - return this.uniforms.dashSize.value; - - }, - - set: function ( value ) { - - this.uniforms.dashSize.value = value; - - } - - }, - - gapSize: { - - enumerable: true, - - get: function () { - - return this.uniforms.gapSize.value; - - }, - - set: function ( value ) { - - this.uniforms.gapSize.value = value; - - } - - }, - - opacity: { - - enumerable: true, - - get: function () { - - return this.uniforms.opacity.value; - - }, - - set: function ( value ) { - - this.uniforms.opacity.value = value; - - } - - }, - - resolution: { - - enumerable: true, - - get: function () { - - return this.uniforms.resolution.value; - - }, - - set: function ( value ) { - - this.uniforms.resolution.value.copy( value ); - - } - - } - - } ); - - this.setValues( parameters ); - - }; - - LineMaterial.prototype = Object.create( ShaderMaterial.prototype ); - LineMaterial.prototype.constructor = LineMaterial; - - LineMaterial.prototype.isLineMaterial = true; - - var LineSegments2 = function ( geometry, material ) { - - Mesh.call( this ); - - this.type = 'LineSegments2'; - - this.geometry = geometry !== undefined ? geometry : new LineSegmentsGeometry(); - this.material = material !== undefined ? material : new LineMaterial( { color: Math.random() * 0xffffff } ); - - }; - - LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), { - - constructor: LineSegments2, - - isLineSegments2: true, - - computeLineDistances: ( function () { // for backwards-compatability, but could be a method of LineSegmentsGeometry... - - var start = new Vector3(); - var end = new Vector3(); - - return function computeLineDistances() { - - var geometry = this.geometry; - - var instanceStart = geometry.attributes.instanceStart; - var instanceEnd = geometry.attributes.instanceEnd; - var lineDistances = new Float32Array( 2 * instanceStart.data.count ); - - for ( var i = 0, j = 0, l = instanceStart.data.count; i < l; i ++, j += 2 ) { - - start.fromBufferAttribute( instanceStart, i ); - end.fromBufferAttribute( instanceEnd, i ); - - lineDistances[ j ] = ( j === 0 ) ? 0 : lineDistances[ j - 1 ]; - lineDistances[ j + 1 ] = lineDistances[ j ] + start.distanceTo( end ); - - } - - var instanceDistanceBuffer = new InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1 - - geometry.setAttribute( 'instanceDistanceStart', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0 - geometry.setAttribute( 'instanceDistanceEnd', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1 - - return this; - - }; - - }() ), - - raycast: ( function () { - - var start = new Vector4(); - var end = new Vector4(); - - var ssOrigin = new Vector4(); - var ssOrigin3 = new Vector3(); - var mvMatrix = new Matrix4(); - var line = new Line3(); - var closestPoint = new Vector3(); - - return function raycast( raycaster, intersects ) { - - if ( raycaster.camera === null ) { - - console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2.' ); - - } - - var ray = raycaster.ray; - var camera = raycaster.camera; - var projectionMatrix = camera.projectionMatrix; - - var geometry = this.geometry; - var material = this.material; - var resolution = material.resolution; - var lineWidth = material.linewidth; - - var instanceStart = geometry.attributes.instanceStart; - var instanceEnd = geometry.attributes.instanceEnd; - - // pick a point 1 unit out along the ray to avoid the ray origin - // sitting at the camera origin which will cause "w" to be 0 when - // applying the projection matrix. - ray.at( 1, ssOrigin ); - - // ndc space [ - 1.0, 1.0 ] - ssOrigin.w = 1; - ssOrigin.applyMatrix4( camera.matrixWorldInverse ); - ssOrigin.applyMatrix4( projectionMatrix ); - ssOrigin.multiplyScalar( 1 / ssOrigin.w ); - - // screen space - ssOrigin.x *= resolution.x / 2; - ssOrigin.y *= resolution.y / 2; - ssOrigin.z = 0; - - ssOrigin3.copy( ssOrigin ); - - var matrixWorld = this.matrixWorld; - mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld ); - - for ( var i = 0, l = instanceStart.count; i < l; i ++ ) { - - start.fromBufferAttribute( instanceStart, i ); - end.fromBufferAttribute( instanceEnd, i ); - - start.w = 1; - end.w = 1; - - // camera space - start.applyMatrix4( mvMatrix ); - end.applyMatrix4( mvMatrix ); - - // clip space - start.applyMatrix4( projectionMatrix ); - end.applyMatrix4( projectionMatrix ); - - // ndc space [ - 1.0, 1.0 ] - start.multiplyScalar( 1 / start.w ); - end.multiplyScalar( 1 / end.w ); - - // skip the segment if it's outside the camera near and far planes - var isBehindCameraNear = start.z < - 1 && end.z < - 1; - var isPastCameraFar = start.z > 1 && end.z > 1; - if ( isBehindCameraNear || isPastCameraFar ) { - - continue; - - } - - // screen space - start.x *= resolution.x / 2; - start.y *= resolution.y / 2; - - end.x *= resolution.x / 2; - end.y *= resolution.y / 2; - - // create 2d segment - line.start.copy( start ); - line.start.z = 0; - - line.end.copy( end ); - line.end.z = 0; - - // get closest point on ray to segment - var param = line.closestPointToPointParameter( ssOrigin3, true ); - line.at( param, closestPoint ); - - // check if the intersection point is within clip space - var zPos = MathUtils.lerp( start.z, end.z, param ); - var isInClipSpace = zPos >= - 1 && zPos <= 1; - - var isInside = ssOrigin3.distanceTo( closestPoint ) < lineWidth * 0.5; - - if ( isInClipSpace && isInside ) { - - line.start.fromBufferAttribute( instanceStart, i ); - line.end.fromBufferAttribute( instanceEnd, i ); - - line.start.applyMatrix4( matrixWorld ); - line.end.applyMatrix4( matrixWorld ); - - var pointOnLine = new Vector3(); - var point = new Vector3(); - - ray.distanceSqToSegment( line.start, line.end, point, pointOnLine ); - - intersects.push( { - - point: point, - pointOnLine: pointOnLine, - distance: ray.origin.distanceTo( point ), - - object: this, - face: null, - faceIndex: i, - uv: null, - uv2: null, - - } ); - - } - - } - - }; - - }() ) - - } ); - - var LineGeometry = function () { - - LineSegmentsGeometry.call( this ); - - this.type = 'LineGeometry'; - - }; - - LineGeometry.prototype = Object.assign( Object.create( LineSegmentsGeometry.prototype ), { - - constructor: LineGeometry, - - isLineGeometry: true, - - setPositions: function ( array ) { - - // converts [ x1, y1, z1, x2, y2, z2, ... ] to pairs format - - var length = array.length - 3; - var points = new Float32Array( 2 * length ); - - for ( var i = 0; i < length; i += 3 ) { - - points[ 2 * i ] = array[ i ]; - points[ 2 * i + 1 ] = array[ i + 1 ]; - points[ 2 * i + 2 ] = array[ i + 2 ]; - - points[ 2 * i + 3 ] = array[ i + 3 ]; - points[ 2 * i + 4 ] = array[ i + 4 ]; - points[ 2 * i + 5 ] = array[ i + 5 ]; - - } - - LineSegmentsGeometry.prototype.setPositions.call( this, points ); - - return this; - - }, - - setColors: function ( array ) { - - // converts [ r1, g1, b1, r2, g2, b2, ... ] to pairs format - - var length = array.length - 3; - var colors = new Float32Array( 2 * length ); - - for ( var i = 0; i < length; i += 3 ) { - - colors[ 2 * i ] = array[ i ]; - colors[ 2 * i + 1 ] = array[ i + 1 ]; - colors[ 2 * i + 2 ] = array[ i + 2 ]; - - colors[ 2 * i + 3 ] = array[ i + 3 ]; - colors[ 2 * i + 4 ] = array[ i + 4 ]; - colors[ 2 * i + 5 ] = array[ i + 5 ]; - - } - - LineSegmentsGeometry.prototype.setColors.call( this, colors ); - - return this; - - }, - - fromLine: function ( line ) { - - var geometry = line.geometry; - - if ( geometry.isGeometry ) { - - this.setPositions( geometry.vertices ); - - } else if ( geometry.isBufferGeometry ) { - - this.setPositions( geometry.attributes.position.array ); // assumes non-indexed - - } - - // set colors, maybe - - return this; - - }, - - copy: function ( /* source */ ) { - - // todo - - return this; - - } - - } ); - - var Line2 = function ( geometry, material ) { - - LineSegments2.call( this ); - - this.type = 'Line2'; - - this.geometry = geometry !== undefined ? geometry : new LineGeometry(); - this.material = material !== undefined ? material : new LineMaterial( { color: Math.random() * 0xffffff } ); - - }; - - Line2.prototype = Object.assign( Object.create( LineSegments2.prototype ), { - - constructor: Line2, - - isLine2: true - - } ); - - class Highlight { - constructor(scene, map) { - this.scene = scene; - this.map = map; - this.items = []; - } - - add(modelId, color, gradientColor, opacity, boxMargin) { - if (!modelId || this._isHighlighted(modelId)) { - return; - } - - const item = this._createHighlight(modelId, color, gradientColor, opacity, boxMargin); - if (!item) { - return; - } - - this._addToItems(item); - this.map.triggerRepaint(); - } - - remove(modelId) { - const highlighted = this._getHighlighted(modelId); - if (!highlighted) { - return; - } - - highlighted.model.remove(highlighted.highlight); - this._removeFromItems(modelId); - } - - clear() { - this.items.forEach((e) => { - this.remove(e.modelId); - }); - - this.items = []; - } - - _addToItems(item) { - this.items.push(item); - } - - _removeFromItems(modelId) { - this.items = this.items.filter((e) => { - return e.modelId !== modelId; - }); - } - - _isHighlighted(modelId) { - return this._getHighlighted(modelId) !== undefined; - } - - _getHighlighted(modelId) { - for (let i = 0; i < this.items.length; i++) { - if (this.items[i].modelId === modelId) { - return this.items[i]; - } - } - } - - _createHighlight(modelId, color, gradientColor, opacity, boxMargin) { - color = color ? color : '#4C162C'; - gradientColor = gradientColor ? gradientColor : '#ff4c94'; - opacity = opacity ? opacity : 0.5; - boxMargin = boxMargin ? boxMargin : 0.05; - - const model = GetModel(modelId, this.scene.children); - if (!model) { - return; - } - - const box = new Box3().setFromObject(model); - var helper = new Box3Helper( box, 0xffff00 ); - - box.min = model.worldToLocal(box.min); - box.max = model.worldToLocal(box.max); - - const xAdd = boxMargin * box.max.x - boxMargin * box.min.x; - const yAdd = boxMargin * box.max.y - boxMargin * box.min.y; - const zAdd = boxMargin * box.max.z - boxMargin * box.min.z; - - const vertices = [ - [box.min.x - xAdd, box.min.y - yAdd, box.min.z - zAdd], - [box.min.x - xAdd, box.max.y + yAdd, box.min.z - zAdd], - [box.max.x + xAdd, box.max.y + yAdd, box.min.z - zAdd], - [box.max.x + xAdd, box.min.y - yAdd, box.min.z - zAdd], - [box.min.x - xAdd, box.min.y - yAdd, box.max.z + zAdd], - [box.min.x - xAdd, box.max.y + yAdd, box.max.z + zAdd], - [box.max.x + xAdd, box.max.y + yAdd, box.max.z + zAdd], - [box.max.x + xAdd, box.min.y - yAdd, box.max.z + zAdd] - ]; - - const planes = [ - this._createPlane([vertices[1], vertices[2], vertices[0], vertices[3]], color, gradientColor, opacity, 'back'), - this._createPlane([vertices[2], vertices[6], vertices[3], vertices[7]], color, gradientColor, opacity, 'back'), - this._createPlane([vertices[1], vertices[5], vertices[0], vertices[4]], color, gradientColor, opacity, 'back'), - this._createPlane([vertices[5], vertices[6], vertices[4], vertices[7]], color, gradientColor, opacity, 'back'), - this._createPlane([vertices[1], vertices[2], vertices[0], vertices[3]], color, gradientColor, opacity, 'front'), - this._createPlane([vertices[2], vertices[6], vertices[3], vertices[7]], color, gradientColor, opacity, 'front'), - this._createPlane([vertices[1], vertices[5], vertices[0], vertices[4]], color, gradientColor, opacity, 'front'), - this._createPlane([vertices[5], vertices[6], vertices[4], vertices[7]], color, gradientColor, opacity, 'front') - ]; - - const line = this._createLine( - [...vertices[1], ...vertices[2], ...vertices[6], ...vertices[5], ...vertices[1]], - gradientColor || color - ); - - const highlight = new Group(); - highlight.add(...planes); - highlight.add(line); - model.add(helper); - - return { - modelId: modelId, - model: model, - highlight: highlight - }; - } - - _createPlane(vertices, color, gradientColor, opacity, side = 'front') { - const bufferGeom = new BufferGeometry(); - bufferGeom.setAttribute( - 'position', - new BufferAttribute( - new Float32Array([...vertices[0], ...vertices[1], ...vertices[2], ...vertices[3]]), - 3 - ) - ); - - //bufferGeom.setIndex([0, 2, 1, 2, 3, 1]); - bufferGeom.setIndex([0, 2, 1, 2, 3, 1]); - bufferGeom.computeVertexNormals(); - bufferGeom.computeBoundingBox(); - - const geom = new Geometry().fromBufferGeometry(bufferGeom); - geom.faceVertexUvs = new PlaneGeometry().faceVertexUvs; - geom.uvsNeedUpdate = true; - - const material = new MeshLambertMaterial({ - color: color || '#1C5A6D', - transparent: true, - opacity: opacity, - side: side === 'back' ? BackSide : FrontSide - }); - material.defines = { USE_UV: '' }; - material.onBeforeCompile = (shader) => { - shader.uniforms.gradientColor = { - value: new Color(gradientColor || '#ffff00') - }; - shader.fragmentShader = ` - uniform vec3 gradientColor; - ${shader.fragmentShader.replace( - 'vec4 diffuseColor = vec4( diffuse, opacity );', - 'vec4 diffuseColor = vec4( mix(diffuse, gradientColor, vec3(vUv.y)), opacity);' - )}`; - }; - - const plane = new Mesh(geom, material); - return plane; - } - - _createLine(positions, color) { - const geometry = new LineGeometry(); - geometry.setPositions(positions); - - const matLine = new LineMaterial({ - color, - linewidth: 0.002, - dashed: false - }); - - const line = new Line2(geometry, matLine); - return line; - } - } - - var RenderableObject = function () { - - this.id = 0; - - this.object = null; - this.z = 0; - this.renderOrder = 0; - - }; - - // - - var RenderableFace = function () { - - this.id = 0; - - this.v1 = new RenderableVertex(); - this.v2 = new RenderableVertex(); - this.v3 = new RenderableVertex(); - - this.normalModel = new Vector3(); - - this.vertexNormalsModel = [ new Vector3(), new Vector3(), new Vector3() ]; - this.vertexNormalsLength = 0; - - this.color = new Color(); - this.material = null; - this.uvs = [ new Vector2(), new Vector2(), new Vector2() ]; - - this.z = 0; - this.renderOrder = 0; - - }; - - // - - var RenderableVertex = function () { - - this.position = new Vector3(); - this.positionWorld = new Vector3(); - this.positionScreen = new Vector4(); - - this.visible = true; - - }; - - RenderableVertex.prototype.copy = function ( vertex ) { - - this.positionWorld.copy( vertex.positionWorld ); - this.positionScreen.copy( vertex.positionScreen ); - - }; - - // - - var RenderableLine = function () { - - this.id = 0; - - this.v1 = new RenderableVertex(); - this.v2 = new RenderableVertex(); - - this.vertexColors = [ new Color(), new Color() ]; - this.material = null; - - this.z = 0; - this.renderOrder = 0; - - }; - - // - - var RenderableSprite = function () { - - this.id = 0; - - this.object = null; - - this.x = 0; - this.y = 0; - this.z = 0; - - this.rotation = 0; - this.scale = new Vector2(); - - this.material = null; - this.renderOrder = 0; - - }; - - // - - var Projector = function () { - - var _object, _objectCount, _objectPool = [], _objectPoolLength = 0, - _vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0, - _face, _faceCount, _facePool = [], _facePoolLength = 0, - _line, _lineCount, _linePool = [], _linePoolLength = 0, - _sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0, - - _renderData = { objects: [], lights: [], elements: [] }, - - _vector3 = new Vector3(), - _vector4 = new Vector4(), - - _clipBox = new Box3( new Vector3( - 1, - 1, - 1 ), new Vector3( 1, 1, 1 ) ), - _boundingBox = new Box3(), - _points3 = new Array( 3 ), - - _viewMatrix = new Matrix4(), - _viewProjectionMatrix = new Matrix4(), - - _modelMatrix, - _modelViewProjectionMatrix = new Matrix4(), - - _normalMatrix = new Matrix3(), - - _frustum = new Frustum(), - - _clippedVertex1PositionScreen = new Vector4(), - _clippedVertex2PositionScreen = new Vector4(); - - // - - this.projectVector = function ( vector, camera ) { - - console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); - vector.project( camera ); - - }; - - this.unprojectVector = function ( vector, camera ) { - - console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); - vector.unproject( camera ); - - }; - - this.pickingRay = function () { - - console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); - - }; - - // - - var RenderList = function () { - - var normals = []; - var colors = []; - var uvs = []; - - var object = null; - - var normalMatrix = new Matrix3(); - - function setObject( value ) { - - object = value; - - normalMatrix.getNormalMatrix( object.matrixWorld ); - - normals.length = 0; - colors.length = 0; - uvs.length = 0; - - } - - function projectVertex( vertex ) { - - var position = vertex.position; - var positionWorld = vertex.positionWorld; - var positionScreen = vertex.positionScreen; - - positionWorld.copy( position ).applyMatrix4( _modelMatrix ); - positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix ); - - var invW = 1 / positionScreen.w; - - positionScreen.x *= invW; - positionScreen.y *= invW; - positionScreen.z *= invW; - - vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 && - positionScreen.y >= - 1 && positionScreen.y <= 1 && - positionScreen.z >= - 1 && positionScreen.z <= 1; - - } - - function pushVertex( x, y, z ) { - - _vertex = getNextVertexInPool(); - _vertex.position.set( x, y, z ); - - projectVertex( _vertex ); - - } - - function pushNormal( x, y, z ) { - - normals.push( x, y, z ); - - } - - function pushColor( r, g, b ) { - - colors.push( r, g, b ); - - } - - function pushUv( x, y ) { - - uvs.push( x, y ); - - } - - function checkTriangleVisibility( v1, v2, v3 ) { - - if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true; - - _points3[ 0 ] = v1.positionScreen; - _points3[ 1 ] = v2.positionScreen; - _points3[ 2 ] = v3.positionScreen; - - return _clipBox.intersectsBox( _boundingBox.setFromPoints( _points3 ) ); - - } - - function checkBackfaceCulling( v1, v2, v3 ) { - - return ( ( v3.positionScreen.x - v1.positionScreen.x ) * - ( v2.positionScreen.y - v1.positionScreen.y ) - - ( v3.positionScreen.y - v1.positionScreen.y ) * - ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0; - - } - - function pushLine( a, b ) { - - var v1 = _vertexPool[ a ]; - var v2 = _vertexPool[ b ]; - - // Clip - - v1.positionScreen.copy( v1.position ).applyMatrix4( _modelViewProjectionMatrix ); - v2.positionScreen.copy( v2.position ).applyMatrix4( _modelViewProjectionMatrix ); - - if ( clipLine( v1.positionScreen, v2.positionScreen ) === true ) { - - // Perform the perspective divide - v1.positionScreen.multiplyScalar( 1 / v1.positionScreen.w ); - v2.positionScreen.multiplyScalar( 1 / v2.positionScreen.w ); - - _line = getNextLineInPool(); - _line.id = object.id; - _line.v1.copy( v1 ); - _line.v2.copy( v2 ); - _line.z = Math.max( v1.positionScreen.z, v2.positionScreen.z ); - _line.renderOrder = object.renderOrder; - - _line.material = object.material; - - if ( object.material.vertexColors ) { - - _line.vertexColors[ 0 ].fromArray( colors, a * 3 ); - _line.vertexColors[ 1 ].fromArray( colors, b * 3 ); - - } - - _renderData.elements.push( _line ); - - } - - } - - function pushTriangle( a, b, c, material ) { - - var v1 = _vertexPool[ a ]; - var v2 = _vertexPool[ b ]; - var v3 = _vertexPool[ c ]; - - if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; - - if ( material.side === DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) { - - _face = getNextFaceInPool(); - - _face.id = object.id; - _face.v1.copy( v1 ); - _face.v2.copy( v2 ); - _face.v3.copy( v3 ); - _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; - _face.renderOrder = object.renderOrder; - - // face normal - _vector3.subVectors( v3.position, v2.position ); - _vector4.subVectors( v1.position, v2.position ); - _vector3.cross( _vector4 ); - _face.normalModel.copy( _vector3 ); - _face.normalModel.applyMatrix3( normalMatrix ).normalize(); - - for ( var i = 0; i < 3; i ++ ) { - - var normal = _face.vertexNormalsModel[ i ]; - normal.fromArray( normals, arguments[ i ] * 3 ); - normal.applyMatrix3( normalMatrix ).normalize(); - - var uv = _face.uvs[ i ]; - uv.fromArray( uvs, arguments[ i ] * 2 ); - - } - - _face.vertexNormalsLength = 3; - - _face.material = material; - - if ( material.vertexColors ) { - - _face.color.fromArray( colors, a * 3 ); - - } - - _renderData.elements.push( _face ); - - } - - } - - return { - setObject: setObject, - projectVertex: projectVertex, - checkTriangleVisibility: checkTriangleVisibility, - checkBackfaceCulling: checkBackfaceCulling, - pushVertex: pushVertex, - pushNormal: pushNormal, - pushColor: pushColor, - pushUv: pushUv, - pushLine: pushLine, - pushTriangle: pushTriangle - }; - - }; - - var renderList = new RenderList(); - - function projectObject( object ) { - - if ( object.visible === false ) return; - - if ( object instanceof Light ) { - - _renderData.lights.push( object ); - - } else if ( object instanceof Mesh || object instanceof Line || object instanceof Points ) { - - if ( object.material.visible === false ) return; - if ( object.frustumCulled === true && _frustum.intersectsObject( object ) === false ) return; - - addObject( object ); - - } else if ( object instanceof Sprite ) { - - if ( object.material.visible === false ) return; - if ( object.frustumCulled === true && _frustum.intersectsSprite( object ) === false ) return; - - addObject( object ); - - } - - var children = object.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - projectObject( children[ i ] ); - - } - - } - - function addObject( object ) { - - _object = getNextObjectInPool(); - _object.id = object.id; - _object.object = object; - - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyMatrix4( _viewProjectionMatrix ); - _object.z = _vector3.z; - _object.renderOrder = object.renderOrder; - - _renderData.objects.push( _object ); - - } - - this.projectScene = function ( scene, camera, sortObjects, sortElements ) { - - _faceCount = 0; - _lineCount = 0; - _spriteCount = 0; - - _renderData.elements.length = 0; - - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - if ( camera.parent === null ) camera.updateMatrixWorld(); - - _viewMatrix.copy( camera.matrixWorldInverse ); - _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); - - _frustum.setFromProjectionMatrix( _viewProjectionMatrix ); - - // - - _objectCount = 0; - - _renderData.objects.length = 0; - _renderData.lights.length = 0; - - projectObject( scene ); - - if ( sortObjects === true ) { - - _renderData.objects.sort( painterSort ); - - } - - // - - var objects = _renderData.objects; - - for ( var o = 0, ol = objects.length; o < ol; o ++ ) { - - var object = objects[ o ].object; - var geometry = object.geometry; - - renderList.setObject( object ); - - _modelMatrix = object.matrixWorld; - - _vertexCount = 0; - - if ( object instanceof Mesh ) { - - if ( geometry instanceof BufferGeometry ) { - - var material = object.material; - - var isMultiMaterial = Array.isArray( material ); - - var attributes = geometry.attributes; - var groups = geometry.groups; - - if ( attributes.position === undefined ) continue; - - var positions = attributes.position.array; - - for ( var i = 0, l = positions.length; i < l; i += 3 ) { - - var x = positions[ i ]; - var y = positions[ i + 1 ]; - var z = positions[ i + 2 ]; - - if ( material.morphTargets === true ) { - - var morphTargets = geometry.morphAttributes.position; - var morphTargetsRelative = geometry.morphTargetsRelative; - var morphInfluences = object.morphTargetInfluences; - - for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { - - var influence = morphInfluences[ t ]; - - if ( influence === 0 ) continue; - - var target = morphTargets[ t ]; - - if ( morphTargetsRelative ) { - - x += target.getX( i / 3 ) * influence; - y += target.getY( i / 3 ) * influence; - z += target.getZ( i / 3 ) * influence; - - } else { - - x += ( target.getX( i / 3 ) - positions[ i ] ) * influence; - y += ( target.getY( i / 3 ) - positions[ i + 1 ] ) * influence; - z += ( target.getZ( i / 3 ) - positions[ i + 2 ] ) * influence; - - } - - } - - } - - renderList.pushVertex( x, y, z ); - - } - - if ( attributes.normal !== undefined ) { - - var normals = attributes.normal.array; - - for ( var i = 0, l = normals.length; i < l; i += 3 ) { - - renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ); - - } - - } - - if ( attributes.color !== undefined ) { - - var colors = attributes.color.array; - - for ( var i = 0, l = colors.length; i < l; i += 3 ) { - - renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ); - - } - - } - - if ( attributes.uv !== undefined ) { - - var uvs = attributes.uv.array; - - for ( var i = 0, l = uvs.length; i < l; i += 2 ) { - - renderList.pushUv( uvs[ i ], uvs[ i + 1 ] ); - - } - - } - - if ( geometry.index !== null ) { - - var indices = geometry.index.array; - - if ( groups.length > 0 ) { - - for ( var g = 0; g < groups.length; g ++ ) { - - var group = groups[ g ]; - - material = isMultiMaterial === true - ? object.material[ group.materialIndex ] - : object.material; - - if ( material === undefined ) continue; - - for ( var i = group.start, l = group.start + group.count; i < l; i += 3 ) { - - renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material ); - - } - - } - - } else { - - for ( var i = 0, l = indices.length; i < l; i += 3 ) { - - renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material ); - - } - - } - - } else { - - if ( groups.length > 0 ) { - - for ( var g = 0; g < groups.length; g ++ ) { - - var group = groups[ g ]; - - material = isMultiMaterial === true - ? object.material[ group.materialIndex ] - : object.material; - - if ( material === undefined ) continue; - - for ( var i = group.start, l = group.start + group.count; i < l; i += 3 ) { - - renderList.pushTriangle( i, i + 1, i + 2, material ); - - } - - } - - } else { - - for ( var i = 0, l = positions.length / 3; i < l; i += 3 ) { - - renderList.pushTriangle( i, i + 1, i + 2, material ); - - } - - } - - } - - } else if ( geometry instanceof Geometry ) { - - var vertices = geometry.vertices; - var faces = geometry.faces; - var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; - - _normalMatrix.getNormalMatrix( _modelMatrix ); - - var material = object.material; - - var isMultiMaterial = Array.isArray( material ); - - for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { - - var vertex = vertices[ v ]; - - _vector3.copy( vertex ); - - if ( material.morphTargets === true ) { - - var morphTargets = geometry.morphTargets; - var morphInfluences = object.morphTargetInfluences; - - for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { - - var influence = morphInfluences[ t ]; - - if ( influence === 0 ) continue; - - var target = morphTargets[ t ]; - var targetVertex = target.vertices[ v ]; - - _vector3.x += ( targetVertex.x - vertex.x ) * influence; - _vector3.y += ( targetVertex.y - vertex.y ) * influence; - _vector3.z += ( targetVertex.z - vertex.z ) * influence; - - } - - } - - renderList.pushVertex( _vector3.x, _vector3.y, _vector3.z ); - - } - - for ( var f = 0, fl = faces.length; f < fl; f ++ ) { - - var face = faces[ f ]; - - material = isMultiMaterial === true - ? object.material[ face.materialIndex ] - : object.material; - - if ( material === undefined ) continue; - - var side = material.side; - - var v1 = _vertexPool[ face.a ]; - var v2 = _vertexPool[ face.b ]; - var v3 = _vertexPool[ face.c ]; - - if ( renderList.checkTriangleVisibility( v1, v2, v3 ) === false ) continue; - - var visible = renderList.checkBackfaceCulling( v1, v2, v3 ); - - if ( side !== DoubleSide ) { - - if ( side === FrontSide && visible === false ) continue; - if ( side === BackSide && visible === true ) continue; - - } - - _face = getNextFaceInPool(); - - _face.id = object.id; - _face.v1.copy( v1 ); - _face.v2.copy( v2 ); - _face.v3.copy( v3 ); - - _face.normalModel.copy( face.normal ); - - if ( visible === false && ( side === BackSide || side === DoubleSide ) ) { - - _face.normalModel.negate(); - - } - - _face.normalModel.applyMatrix3( _normalMatrix ).normalize(); - - var faceVertexNormals = face.vertexNormals; - - for ( var n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) { - - var normalModel = _face.vertexNormalsModel[ n ]; - normalModel.copy( faceVertexNormals[ n ] ); - - if ( visible === false && ( side === BackSide || side === DoubleSide ) ) { - - normalModel.negate(); - - } - - normalModel.applyMatrix3( _normalMatrix ).normalize(); - - } - - _face.vertexNormalsLength = faceVertexNormals.length; - - var vertexUvs = faceVertexUvs[ f ]; - - if ( vertexUvs !== undefined ) { - - for ( var u = 0; u < 3; u ++ ) { - - _face.uvs[ u ].copy( vertexUvs[ u ] ); - - } - - } - - _face.color = face.color; - _face.material = material; - - _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; - _face.renderOrder = object.renderOrder; - - _renderData.elements.push( _face ); - - } - - } - - } else if ( object instanceof Line ) { - - _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); - - if ( geometry instanceof BufferGeometry ) { - - var attributes = geometry.attributes; - - if ( attributes.position !== undefined ) { - - var positions = attributes.position.array; - - for ( var i = 0, l = positions.length; i < l; i += 3 ) { - - renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); - - } - - if ( attributes.color !== undefined ) { - - var colors = attributes.color.array; - - for ( var i = 0, l = colors.length; i < l; i += 3 ) { - - renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ); - - } - - } - - if ( geometry.index !== null ) { - - var indices = geometry.index.array; - - for ( var i = 0, l = indices.length; i < l; i += 2 ) { - - renderList.pushLine( indices[ i ], indices[ i + 1 ] ); - - } - - } else { - - var step = object instanceof LineSegments ? 2 : 1; - - for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) { - - renderList.pushLine( i, i + 1 ); - - } - - } - - } - - } else if ( geometry instanceof Geometry ) { - - var vertices = object.geometry.vertices; - - if ( vertices.length === 0 ) continue; - - v1 = getNextVertexInPool(); - v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix ); - - var step = object instanceof LineSegments ? 2 : 1; - - for ( var v = 1, vl = vertices.length; v < vl; v ++ ) { - - v1 = getNextVertexInPool(); - v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix ); - - if ( ( v + 1 ) % step > 0 ) continue; - - v2 = _vertexPool[ _vertexCount - 2 ]; - - _clippedVertex1PositionScreen.copy( v1.positionScreen ); - _clippedVertex2PositionScreen.copy( v2.positionScreen ); - - if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) { - - // Perform the perspective divide - _clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w ); - _clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w ); - - _line = getNextLineInPool(); - - _line.id = object.id; - _line.v1.positionScreen.copy( _clippedVertex1PositionScreen ); - _line.v2.positionScreen.copy( _clippedVertex2PositionScreen ); - - _line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z ); - _line.renderOrder = object.renderOrder; - - _line.material = object.material; - - if ( object.material.vertexColors ) { - - _line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] ); - _line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] ); - - } - - _renderData.elements.push( _line ); - - } - - } - - } - - } else if ( object instanceof Points ) { - - _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); - - if ( geometry instanceof Geometry ) { - - var vertices = object.geometry.vertices; - - for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { - - var vertex = vertices[ v ]; - - _vector4.set( vertex.x, vertex.y, vertex.z, 1 ); - _vector4.applyMatrix4( _modelViewProjectionMatrix ); - - pushPoint( _vector4, object, camera ); - - } - - } else if ( geometry instanceof BufferGeometry ) { - - var attributes = geometry.attributes; - - if ( attributes.position !== undefined ) { - - var positions = attributes.position.array; - - for ( var i = 0, l = positions.length; i < l; i += 3 ) { - - _vector4.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ], 1 ); - _vector4.applyMatrix4( _modelViewProjectionMatrix ); - - pushPoint( _vector4, object, camera ); - - } - - } - - } - - } else if ( object instanceof Sprite ) { - - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - _vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 ); - _vector4.applyMatrix4( _viewProjectionMatrix ); - - pushPoint( _vector4, object, camera ); - - } - - } - - if ( sortElements === true ) { - - _renderData.elements.sort( painterSort ); - - } - - return _renderData; - - }; - - function pushPoint( _vector4, object, camera ) { - - var invW = 1 / _vector4.w; - - _vector4.z *= invW; - - if ( _vector4.z >= - 1 && _vector4.z <= 1 ) { - - _sprite = getNextSpriteInPool(); - _sprite.id = object.id; - _sprite.x = _vector4.x * invW; - _sprite.y = _vector4.y * invW; - _sprite.z = _vector4.z; - _sprite.renderOrder = object.renderOrder; - _sprite.object = object; - - _sprite.rotation = object.rotation; - - _sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) ); - _sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) ); - - _sprite.material = object.material; - - _renderData.elements.push( _sprite ); - - } - - } - - // Pools - - function getNextObjectInPool() { - - if ( _objectCount === _objectPoolLength ) { - - var object = new RenderableObject(); - _objectPool.push( object ); - _objectPoolLength ++; - _objectCount ++; - return object; - - } - - return _objectPool[ _objectCount ++ ]; - - } - - function getNextVertexInPool() { - - if ( _vertexCount === _vertexPoolLength ) { - - var vertex = new RenderableVertex(); - _vertexPool.push( vertex ); - _vertexPoolLength ++; - _vertexCount ++; - return vertex; - - } - - return _vertexPool[ _vertexCount ++ ]; - - } - - function getNextFaceInPool() { - - if ( _faceCount === _facePoolLength ) { - - var face = new RenderableFace(); - _facePool.push( face ); - _facePoolLength ++; - _faceCount ++; - return face; - - } - - return _facePool[ _faceCount ++ ]; - - - } - - function getNextLineInPool() { - - if ( _lineCount === _linePoolLength ) { - - var line = new RenderableLine(); - _linePool.push( line ); - _linePoolLength ++; - _lineCount ++; - return line; - - } - - return _linePool[ _lineCount ++ ]; - - } - - function getNextSpriteInPool() { - - if ( _spriteCount === _spritePoolLength ) { - - var sprite = new RenderableSprite(); - _spritePool.push( sprite ); - _spritePoolLength ++; - _spriteCount ++; - return sprite; - - } - - return _spritePool[ _spriteCount ++ ]; - - } - - // - - function painterSort( a, b ) { - - if ( a.renderOrder !== b.renderOrder ) { - - return a.renderOrder - b.renderOrder; - - } else if ( a.z !== b.z ) { - - return b.z - a.z; - - } else if ( a.id !== b.id ) { - - return a.id - b.id; - - } else { - - return 0; - - } - - } - - function clipLine( s1, s2 ) { - - var alpha1 = 0, alpha2 = 1, - - // Calculate the boundary coordinate of each vertex for the near and far clip planes, - // Z = -1 and Z = +1, respectively. - - bc1near = s1.z + s1.w, - bc2near = s2.z + s2.w, - bc1far = - s1.z + s1.w, - bc2far = - s2.z + s2.w; - - if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { - - // Both vertices lie entirely within all clip planes. - return true; - - } else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) { - - // Both vertices lie entirely outside one of the clip planes. - return false; - - } else { - - // The line segment spans at least one clip plane. - - if ( bc1near < 0 ) { - - // v1 lies outside the near plane, v2 inside - alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) ); - - } else if ( bc2near < 0 ) { - - // v2 lies outside the near plane, v1 inside - alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) ); - - } - - if ( bc1far < 0 ) { - - // v1 lies outside the far plane, v2 inside - alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) ); - - } else if ( bc2far < 0 ) { - - // v2 lies outside the far plane, v2 inside - alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) ); - - } - - if ( alpha2 < alpha1 ) { - - // The line segment spans two boundaries, but is outside both of them. - // (This can't happen when we're only clipping against just near/far but good - // to leave the check here for future usage if other clip planes are added.) - return false; - - } else { - - // Update the s1 and s2 vertices to match the clipped line segment. - s1.lerp( s2, alpha1 ); - s2.lerp( s1, 1 - alpha2 ); - - return true; - - } - - } - - } - - }; - - var SVGObject = function ( node ) { - - Object3D.call( this ); - - this.node = node; - - }; - - SVGObject.prototype = Object.create( Object3D.prototype ); - SVGObject.prototype.constructor = SVGObject; - - var SVGRenderer = function () { - - var _this = this, - _renderData, _elements, _lights, - _projector = new Projector(), - _svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ), - _svgWidth, _svgHeight, _svgWidthHalf, _svgHeightHalf, - - _v1, _v2, _v3, - - _clipBox = new Box2(), - _elemBox = new Box2(), - - _color = new Color(), - _diffuseColor = new Color(), - _ambientLight = new Color(), - _directionalLights = new Color(), - _pointLights = new Color(), - _clearColor = new Color(), - - _vector3 = new Vector3(), // Needed for PointLight - _centroid = new Vector3(), - _normal = new Vector3(), - _normalViewMatrix = new Matrix3(), - - _viewMatrix = new Matrix4(), - _viewProjectionMatrix = new Matrix4(), - - _svgPathPool = [], - _svgNode, _pathCount = 0, - - _currentPath, _currentStyle, - - _quality = 1, _precision = null; - - this.domElement = _svg; - - this.autoClear = true; - this.sortObjects = true; - this.sortElements = true; - - this.overdraw = 0.5; - - this.info = { - - render: { - - vertices: 0, - faces: 0 - - } - - }; - - this.setQuality = function ( quality ) { - - switch ( quality ) { - - case "high": _quality = 1; break; - case "low": _quality = 0; break; - - } - - }; - - this.setClearColor = function ( color ) { - - _clearColor.set( color ); - - }; - - this.setPixelRatio = function () {}; - - this.setSize = function ( width, height ) { - - _svgWidth = width; _svgHeight = height; - _svgWidthHalf = _svgWidth / 2; _svgHeightHalf = _svgHeight / 2; - - _svg.setAttribute( 'viewBox', ( - _svgWidthHalf ) + ' ' + ( - _svgHeightHalf ) + ' ' + _svgWidth + ' ' + _svgHeight ); - _svg.setAttribute( 'width', _svgWidth ); - _svg.setAttribute( 'height', _svgHeight ); - - _clipBox.min.set( - _svgWidthHalf, - _svgHeightHalf ); - _clipBox.max.set( _svgWidthHalf, _svgHeightHalf ); - - }; - - this.getSize = function () { - - return { - width: _svgWidth, - height: _svgHeight - }; - - }; - - this.setPrecision = function ( precision ) { - - _precision = precision; - - }; - - function removeChildNodes() { - - _pathCount = 0; - - while ( _svg.childNodes.length > 0 ) { - - _svg.removeChild( _svg.childNodes[ 0 ] ); - - } - - } - - function convert( c ) { - - return _precision !== null ? c.toFixed( _precision ) : c; - - } - - this.clear = function () { - - removeChildNodes(); - _svg.style.backgroundColor = _clearColor.getStyle(); - - }; - - this.render = function ( scene, camera ) { - - if ( camera instanceof Camera === false ) { - - console.error( 'THREE.SVGRenderer.render: camera is not an instance of Camera.' ); - return; - - } - - var background = scene.background; - - if ( background && background.isColor ) { - - removeChildNodes(); - _svg.style.backgroundColor = background.getStyle(); - - } else if ( this.autoClear === true ) { - - this.clear(); - - } - - _this.info.render.vertices = 0; - _this.info.render.faces = 0; - - _viewMatrix.copy( camera.matrixWorldInverse ); - _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); - - _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); - _elements = _renderData.elements; - _lights = _renderData.lights; - - _normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse ); - - calculateLights( _lights ); - - // reset accumulated path - - _currentPath = ''; - _currentStyle = ''; - - for ( var e = 0, el = _elements.length; e < el; e ++ ) { - - var element = _elements[ e ]; - var material = element.material; - - if ( material === undefined || material.opacity === 0 ) continue; - - _elemBox.makeEmpty(); - - if ( element instanceof RenderableSprite ) { - - _v1 = element; - _v1.x *= _svgWidthHalf; _v1.y *= - _svgHeightHalf; - - renderSprite( _v1, element, material ); - - } else if ( element instanceof RenderableLine ) { - - _v1 = element.v1; _v2 = element.v2; - - _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; - _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; - - _elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] ); - - if ( _clipBox.intersectsBox( _elemBox ) === true ) { - - renderLine( _v1, _v2, element, material ); - - } - - } else if ( element instanceof RenderableFace ) { - - _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; - - if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue; - if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue; - if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue; - - _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; - _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; - _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf; - - if ( this.overdraw > 0 ) { - - expand( _v1.positionScreen, _v2.positionScreen, this.overdraw ); - expand( _v2.positionScreen, _v3.positionScreen, this.overdraw ); - expand( _v3.positionScreen, _v1.positionScreen, this.overdraw ); - - } - - _elemBox.setFromPoints( [ - _v1.positionScreen, - _v2.positionScreen, - _v3.positionScreen - ] ); - - if ( _clipBox.intersectsBox( _elemBox ) === true ) { - - renderFace3( _v1, _v2, _v3, element, material ); - - } - - } - - } - - flushPath(); // just to flush last svg:path - - scene.traverseVisible( function ( object ) { - - if ( object instanceof SVGObject ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyMatrix4( _viewProjectionMatrix ); - - if ( _vector3.z < - 1 || _vector3.z > 1 ) return; - - var x = _vector3.x * _svgWidthHalf; - var y = - _vector3.y * _svgHeightHalf; - - var node = object.node; - node.setAttribute( 'transform', 'translate(' + x + ',' + y + ')' ); - - _svg.appendChild( node ); - - } - - } ); - - }; - - function calculateLights( lights ) { - - _ambientLight.setRGB( 0, 0, 0 ); - _directionalLights.setRGB( 0, 0, 0 ); - _pointLights.setRGB( 0, 0, 0 ); - - for ( var l = 0, ll = lights.length; l < ll; l ++ ) { - - var light = lights[ l ]; - var lightColor = light.color; - - if ( light.isAmbientLight ) { - - _ambientLight.r += lightColor.r; - _ambientLight.g += lightColor.g; - _ambientLight.b += lightColor.b; - - } else if ( light.isDirectionalLight ) { - - _directionalLights.r += lightColor.r; - _directionalLights.g += lightColor.g; - _directionalLights.b += lightColor.b; - - } else if ( light.isPointLight ) { - - _pointLights.r += lightColor.r; - _pointLights.g += lightColor.g; - _pointLights.b += lightColor.b; - - } - - } - - } - - function calculateLight( lights, position, normal, color ) { - - for ( var l = 0, ll = lights.length; l < ll; l ++ ) { - - var light = lights[ l ]; - var lightColor = light.color; - - if ( light.isDirectionalLight ) { - - var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); - - var amount = normal.dot( lightPosition ); - - if ( amount <= 0 ) continue; - - amount *= light.intensity; - - color.r += lightColor.r * amount; - color.g += lightColor.g * amount; - color.b += lightColor.b * amount; - - } else if ( light.isPointLight ) { - - var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); - - var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); - - if ( amount <= 0 ) continue; - - amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); - - if ( amount == 0 ) continue; - - amount *= light.intensity; - - color.r += lightColor.r * amount; - color.g += lightColor.g * amount; - color.b += lightColor.b * amount; - - } - - } - - } - - function renderSprite( v1, element, material ) { - - var scaleX = element.scale.x * _svgWidthHalf; - var scaleY = element.scale.y * _svgHeightHalf; - - if ( material.isPointsMaterial ) { - - scaleX *= material.size; - scaleY *= material.size; - - } - - var path = 'M' + convert( v1.x - scaleX * 0.5 ) + ',' + convert( v1.y - scaleY * 0.5 ) + 'h' + convert( scaleX ) + 'v' + convert( scaleY ) + 'h' + convert( - scaleX ) + 'z'; - var style = ""; - - if ( material.isSpriteMaterial || material.isPointsMaterial ) { - - style = 'fill:' + material.color.getStyle() + ';fill-opacity:' + material.opacity; - - } - - addPath( style, path ); - - } - - function renderLine( v1, v2, element, material ) { - - var path = 'M' + convert( v1.positionScreen.x ) + ',' + convert( v1.positionScreen.y ) + 'L' + convert( v2.positionScreen.x ) + ',' + convert( v2.positionScreen.y ); - - if ( material.isLineBasicMaterial ) { - - var style = 'fill:none;stroke:' + material.color.getStyle() + ';stroke-opacity:' + material.opacity + ';stroke-width:' + material.linewidth + ';stroke-linecap:' + material.linecap; - - if ( material.isLineDashedMaterial ) { - - style = style + ';stroke-dasharray:' + material.dashSize + "," + material.gapSize; - - } - - addPath( style, path ); - - } - - } - - function renderFace3( v1, v2, v3, element, material ) { - - _this.info.render.vertices += 3; - _this.info.render.faces ++; - - var path = 'M' + convert( v1.positionScreen.x ) + ',' + convert( v1.positionScreen.y ) + 'L' + convert( v2.positionScreen.x ) + ',' + convert( v2.positionScreen.y ) + 'L' + convert( v3.positionScreen.x ) + ',' + convert( v3.positionScreen.y ) + 'z'; - var style = ''; - - if ( material.isMeshBasicMaterial ) { - - _color.copy( material.color ); - - if ( material.vertexColors ) { - - _color.multiply( element.color ); - - } - - } else if ( material.isMeshLambertMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial ) { - - _diffuseColor.copy( material.color ); - - if ( material.vertexColors ) { - - _diffuseColor.multiply( element.color ); - - } - - _color.copy( _ambientLight ); - - _centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 ); - - calculateLight( _lights, _centroid, element.normalModel, _color ); - - _color.multiply( _diffuseColor ).add( material.emissive ); - - } else if ( material.isMeshNormalMaterial ) { - - _normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ).normalize(); - - _color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); - - } - - if ( material.wireframe ) { - - style = 'fill:none;stroke:' + _color.getStyle() + ';stroke-opacity:' + material.opacity + ';stroke-width:' + material.wireframeLinewidth + ';stroke-linecap:' + material.wireframeLinecap + ';stroke-linejoin:' + material.wireframeLinejoin; - - } else { - - style = 'fill:' + _color.getStyle() + ';fill-opacity:' + material.opacity; - - } - - addPath( style, path ); - - } - - // Hide anti-alias gaps - - function expand( v1, v2, pixels ) { - - var x = v2.x - v1.x, y = v2.y - v1.y, - det = x * x + y * y, idet; - - if ( det === 0 ) return; - - idet = pixels / Math.sqrt( det ); - - x *= idet; y *= idet; - - v2.x += x; v2.y += y; - v1.x -= x; v1.y -= y; - - } - - function addPath( style, path ) { - - if ( _currentStyle === style ) { - - _currentPath += path; - - } else { - - flushPath(); - - _currentStyle = style; - _currentPath = path; - - } - - } - - function flushPath() { - - if ( _currentPath ) { - - _svgNode = getPathNode( _pathCount ++ ); - _svgNode.setAttribute( 'd', _currentPath ); - _svgNode.setAttribute( 'style', _currentStyle ); - _svg.appendChild( _svgNode ); - - } - - _currentPath = ''; - _currentStyle = ''; - - } - - function getPathNode( id ) { - - if ( _svgPathPool[ id ] == null ) { - - _svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); - - if ( _quality == 0 ) { - - _svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed - - } - - return _svgPathPool[ id ]; - - } - - return _svgPathPool[ id ]; - - } - - }; - - class Marker { - constructor(scene, map) { - this.scene = scene; - this.map = map; - this.items = []; - } - - add(modelId, svg, scale = 1.0, offset = { x: 0, y: 0, z: 0 }, onclickListener) { - if (!modelId || this._hasMarker(modelId)) { - return; - } - - const item = this._addMarkerAboveModel(modelId, svg, (scale = 1.0), (offset = { x: 0, y: 0, z: 0 }), onclickListener); - if (!item) { - return; - } - - this._addToItems(item); - this.map.triggerRepaint(); - } - - remove(modelId) { - const item = this._getItem(modelId); - if (!item) { - return; - } - - document.body.removeChild(item.renderer.domElement); - item.model.remove(item.marker); - this._removeFromItems(modelId); - } - - clear() { - this.items.forEach((e) => { - this.remove(e.modelId); - }); - - this.items = []; - } - - getMarkers() { - return this.items; - } - - _createRenderer() { - const svgRenderer = new SVGRenderer(); - svgRenderer.setSize(window.innerWidth, window.innerHeight); - svgRenderer.setQuality('low'); - document.body.appendChild(svgRenderer.domElement); - window.addEventListener('resize', () => { - svgRenderer.setSize(window.innerWidth, window.innerHeight); - }); - - return svgRenderer; - } - - _addMarkerAboveModel(modelId, svg, scale = 1.0, offset = { x: 0, y: 0, z: 0 }, onclickListener) { - const model = GetModel(modelId, this.scene.children); - if (!model) { - return; - } - - let marker = {}; - const renderer = this._createRenderer(); - const svgScene = new Scene(); - const loader = new FileLoader(); - const box = new Box3().setFromObject(model); - const boxCenter = box.getCenter(); - - // weird stuff, apparantly setting box min and max from world to local fixes postition issues but looks like it should do nothing - box.min = model.worldToLocal(box.min); - box.max = model.worldToLocal(box.max); - - const center = model.worldToLocal(boxCenter); - - loader.load(svg, (data) => { - const node = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - const parser = new DOMParser(); - const doc = parser.parseFromString(data, 'image/svg+xml'); - - node.appendChild(doc.documentElement); - if (onclickListener) { - node.firstChild.addEventListener('mousedown', onclickListener.bind(this)); - node.firstChild.onmouseover = () => { - node.firstChild.style = 'cursor: pointer;'; - }; - node.firstChild.onmouseout = () => { - node.firstChild.style = 'cursor: unset;'; - }; - } - - marker = new SVGObject(node); - //marker.applyMatrix4(new THREE.Matrix4().makeScale(scale, scale, scale)); - marker.position.x = center.x + offset.x; - marker.position.y = box.max.y + offset.y; - marker.position.z = center.z + offset.z; - svgScene.add(marker); - //svgScene.applyMatrix4( model.matrixWorld ); - model.add(svgScene); - }); - - return { - modelId: modelId, - model: model, - marker: svgScene, - renderer: renderer - }; - } - - _addToItems(item) { - this.items.push(item); - } - - _removeFromItems(modelId) { - this.items = this.items.filter((e) => { - return e.modelId !== modelId; - }); - } - - _hasMarker(modelId) { - return this._getItem(modelId) !== undefined; - } - - _getItem(modelId) { - for (let i = 0; i < this.items.length; i++) { - if (this.items[i].modelId === modelId) { - return this.items[i]; - } - } - } - } - - /** - * Full-screen textured quad shader - */ - - var CopyShader = { - - uniforms: { - - "tDiffuse": { value: null }, - "opacity": { value: 1.0 } - - }, - - vertexShader: [ - - "varying vec2 vUv;", - - "void main() {", - - " vUv = uv;", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - - "}" - - ].join( "\n" ), - - fragmentShader: [ - - "uniform float opacity;", - - "uniform sampler2D tDiffuse;", - - "varying vec2 vUv;", - - "void main() {", - - " vec4 texel = texture2D( tDiffuse, vUv );", - " gl_FragColor = opacity * texel;", - - "}" - - ].join( "\n" ) - - }; - - function Pass() { - - // if set to true, the pass is processed by the composer - this.enabled = true; - - // if set to true, the pass indicates to swap read and write buffer after rendering - this.needsSwap = true; - - // if set to true, the pass clears its buffer before rendering - this.clear = false; - - // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. - this.renderToScreen = false; - - } - - Object.assign( Pass.prototype, { - - setSize: function ( /* width, height */ ) {}, - - render: function ( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) { - - console.error( 'THREE.Pass: .render() must be implemented in derived pass.' ); - - } - - } ); - - // Helper for passes that need to fill the viewport with a single quad. - - Pass.FullScreenQuad = ( function () { - - var camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); - var geometry = new PlaneBufferGeometry( 2, 2 ); - - var FullScreenQuad = function ( material ) { - - this._mesh = new Mesh( geometry, material ); - - }; - - Object.defineProperty( FullScreenQuad.prototype, 'material', { - - get: function () { - - return this._mesh.material; - - }, - - set: function ( value ) { - - this._mesh.material = value; - - } - - } ); - - Object.assign( FullScreenQuad.prototype, { - - dispose: function () { - - this._mesh.geometry.dispose(); - - }, - - render: function ( renderer ) { - - renderer.render( this._mesh, camera ); - - } - - } ); - - return FullScreenQuad; - - } )(); - - var ShaderPass = function ( shader, textureID ) { - - Pass.call( this ); - - this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse"; - - if ( shader instanceof ShaderMaterial ) { - - this.uniforms = shader.uniforms; - - this.material = shader; - - } else if ( shader ) { - - this.uniforms = UniformsUtils.clone( shader.uniforms ); - - this.material = new ShaderMaterial( { - - defines: Object.assign( {}, shader.defines ), - uniforms: this.uniforms, - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader - - } ); - - } - - this.fsQuad = new Pass.FullScreenQuad( this.material ); - - }; - - ShaderPass.prototype = Object.assign( Object.create( Pass.prototype ), { - - constructor: ShaderPass, - - render: function ( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { - - if ( this.uniforms[ this.textureID ] ) { - - this.uniforms[ this.textureID ].value = readBuffer.texture; - - } - - this.fsQuad.material = this.material; - - if ( this.renderToScreen ) { - - renderer.setRenderTarget( null ); - this.fsQuad.render( renderer ); - - } else { - - renderer.setRenderTarget( writeBuffer ); - // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 - if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); - this.fsQuad.render( renderer ); - - } - - } - - } ); - - var MaskPass = function ( scene, camera ) { - - Pass.call( this ); - - this.scene = scene; - this.camera = camera; - - this.clear = true; - this.needsSwap = false; - - this.inverse = false; - - }; - - MaskPass.prototype = Object.assign( Object.create( Pass.prototype ), { - - constructor: MaskPass, - - render: function ( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { - - var context = renderer.getContext(); - var state = renderer.state; - - // don't update color or depth - - state.buffers.color.setMask( false ); - state.buffers.depth.setMask( false ); - - // lock buffers - - state.buffers.color.setLocked( true ); - state.buffers.depth.setLocked( true ); - - // set up stencil - - var writeValue, clearValue; - - if ( this.inverse ) { - - writeValue = 0; - clearValue = 1; - - } else { - - writeValue = 1; - clearValue = 0; - - } - - state.buffers.stencil.setTest( true ); - state.buffers.stencil.setOp( context.REPLACE, context.REPLACE, context.REPLACE ); - state.buffers.stencil.setFunc( context.ALWAYS, writeValue, 0xffffffff ); - state.buffers.stencil.setClear( clearValue ); - state.buffers.stencil.setLocked( true ); - - // draw into the stencil buffer - - renderer.setRenderTarget( readBuffer ); - if ( this.clear ) renderer.clear(); - renderer.render( this.scene, this.camera ); - - renderer.setRenderTarget( writeBuffer ); - if ( this.clear ) renderer.clear(); - renderer.render( this.scene, this.camera ); - - // unlock color and depth buffer for subsequent rendering - - state.buffers.color.setLocked( false ); - state.buffers.depth.setLocked( false ); - - // only render where stencil is set to 1 - - state.buffers.stencil.setLocked( false ); - state.buffers.stencil.setFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1 - state.buffers.stencil.setOp( context.KEEP, context.KEEP, context.KEEP ); - state.buffers.stencil.setLocked( true ); - - } - - } ); - - - var ClearMaskPass = function () { - - Pass.call( this ); - - this.needsSwap = false; - - }; - - ClearMaskPass.prototype = Object.create( Pass.prototype ); - - Object.assign( ClearMaskPass.prototype, { - - render: function ( renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */ ) { - - renderer.state.buffers.stencil.setLocked( false ); - renderer.state.buffers.stencil.setTest( false ); - - } - - } ); - - var EffectComposer = function ( renderer, renderTarget ) { - - this.renderer = renderer; - - if ( renderTarget === undefined ) { - - var parameters = { - minFilter: LinearFilter, - magFilter: LinearFilter, - format: RGBAFormat - }; - - var size = renderer.getSize( new Vector2() ); - this._pixelRatio = renderer.getPixelRatio(); - this._width = size.width; - this._height = size.height; - - renderTarget = new WebGLRenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, parameters ); - renderTarget.texture.name = 'EffectComposer.rt1'; - - } else { - - this._pixelRatio = 1; - this._width = renderTarget.width; - this._height = renderTarget.height; - - } - - this.renderTarget1 = renderTarget; - this.renderTarget2 = renderTarget.clone(); - this.renderTarget2.texture.name = 'EffectComposer.rt2'; - - this.writeBuffer = this.renderTarget1; - this.readBuffer = this.renderTarget2; - - this.renderToScreen = true; - - this.passes = []; - - // dependencies - - if ( CopyShader === undefined ) { - - console.error( 'THREE.EffectComposer relies on CopyShader' ); - - } - - if ( ShaderPass === undefined ) { - - console.error( 'THREE.EffectComposer relies on ShaderPass' ); - - } - - this.copyPass = new ShaderPass( CopyShader ); - - this.clock = new Clock(); - - }; - - Object.assign( EffectComposer.prototype, { - - swapBuffers: function () { - - var tmp = this.readBuffer; - this.readBuffer = this.writeBuffer; - this.writeBuffer = tmp; - - }, - - addPass: function ( pass ) { - - this.passes.push( pass ); - pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); - - }, - - insertPass: function ( pass, index ) { - - this.passes.splice( index, 0, pass ); - pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); - - }, - - isLastEnabledPass: function ( passIndex ) { - - for ( var i = passIndex + 1; i < this.passes.length; i ++ ) { - - if ( this.passes[ i ].enabled ) { - - return false; - - } - - } - - return true; - - }, - - render: function ( deltaTime ) { - - // deltaTime value is in seconds - - if ( deltaTime === undefined ) { - - deltaTime = this.clock.getDelta(); - - } - - var currentRenderTarget = this.renderer.getRenderTarget(); - - var maskActive = false; - - var pass, i, il = this.passes.length; - - for ( i = 0; i < il; i ++ ) { - - pass = this.passes[ i ]; - - if ( pass.enabled === false ) continue; - - pass.renderToScreen = ( this.renderToScreen && this.isLastEnabledPass( i ) ); - pass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive ); - - if ( pass.needsSwap ) { - - if ( maskActive ) { - - var context = this.renderer.getContext(); - var stencil = this.renderer.state.buffers.stencil; - - //context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff ); - stencil.setFunc( context.NOTEQUAL, 1, 0xffffffff ); - - this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime ); - - //context.stencilFunc( context.EQUAL, 1, 0xffffffff ); - stencil.setFunc( context.EQUAL, 1, 0xffffffff ); - - } - - this.swapBuffers(); - - } - - if ( MaskPass !== undefined ) { - - if ( pass instanceof MaskPass ) { - - maskActive = true; - - } else if ( pass instanceof ClearMaskPass ) { - - maskActive = false; - - } - - } - - } - - this.renderer.setRenderTarget( currentRenderTarget ); - - }, - - reset: function ( renderTarget ) { - - if ( renderTarget === undefined ) { - - var size = this.renderer.getSize( new Vector2() ); - this._pixelRatio = this.renderer.getPixelRatio(); - this._width = size.width; - this._height = size.height; - - renderTarget = this.renderTarget1.clone(); - renderTarget.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); - - } - - this.renderTarget1.dispose(); - this.renderTarget2.dispose(); - this.renderTarget1 = renderTarget; - this.renderTarget2 = renderTarget.clone(); - - this.writeBuffer = this.renderTarget1; - this.readBuffer = this.renderTarget2; - - }, - - setSize: function ( width, height ) { - - this._width = width; - this._height = height; - - var effectiveWidth = this._width * this._pixelRatio; - var effectiveHeight = this._height * this._pixelRatio; - - this.renderTarget1.setSize( effectiveWidth, effectiveHeight ); - this.renderTarget2.setSize( effectiveWidth, effectiveHeight ); - - for ( var i = 0; i < this.passes.length; i ++ ) { - - this.passes[ i ].setSize( effectiveWidth, effectiveHeight ); - - } - - }, - - setPixelRatio: function ( pixelRatio ) { - - this._pixelRatio = pixelRatio; - - this.setSize( this._width, this._height ); - - } - - } ); - - - var Pass$1 = function () { - - // if set to true, the pass is processed by the composer - this.enabled = true; - - // if set to true, the pass indicates to swap read and write buffer after rendering - this.needsSwap = true; - - // if set to true, the pass clears its buffer before rendering - this.clear = false; - - // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. - this.renderToScreen = false; - - }; - - Object.assign( Pass$1.prototype, { - - setSize: function ( /* width, height */ ) {}, - - render: function ( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) { - - console.error( 'THREE.Pass: .render() must be implemented in derived pass.' ); - - } - - } ); - - // Helper for passes that need to fill the viewport with a single quad. - Pass$1.FullScreenQuad = ( function () { - - var camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); - var geometry = new PlaneBufferGeometry( 2, 2 ); - - var FullScreenQuad = function ( material ) { - - this._mesh = new Mesh( geometry, material ); - - }; - - Object.defineProperty( FullScreenQuad.prototype, 'material', { - - get: function () { - - return this._mesh.material; - - }, - - set: function ( value ) { - - this._mesh.material = value; - - } - - } ); - - Object.assign( FullScreenQuad.prototype, { - - dispose: function () { - - this._mesh.geometry.dispose(); - - }, - - render: function ( renderer ) { - - renderer.render( this._mesh, camera ); - - } - - } ); - - return FullScreenQuad; - - } )(); - - // Ported from Stefan Gustavson's java implementation - // http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf - // Read Stefan's excellent paper for details on how this code works. - // - // Sean McCullough banksean@gmail.com - // - // Added 4D noise - - /** - * You can pass in a random number generator object if you like. - * It is assumed to have a random() method. - */ - var SimplexNoise = function ( r ) { - - if ( r == undefined ) r = Math; - this.grad3 = [[ 1, 1, 0 ], [ - 1, 1, 0 ], [ 1, - 1, 0 ], [ - 1, - 1, 0 ], - [ 1, 0, 1 ], [ - 1, 0, 1 ], [ 1, 0, - 1 ], [ - 1, 0, - 1 ], - [ 0, 1, 1 ], [ 0, - 1, 1 ], [ 0, 1, - 1 ], [ 0, - 1, - 1 ]]; - - this.grad4 = [[ 0, 1, 1, 1 ], [ 0, 1, 1, - 1 ], [ 0, 1, - 1, 1 ], [ 0, 1, - 1, - 1 ], - [ 0, - 1, 1, 1 ], [ 0, - 1, 1, - 1 ], [ 0, - 1, - 1, 1 ], [ 0, - 1, - 1, - 1 ], - [ 1, 0, 1, 1 ], [ 1, 0, 1, - 1 ], [ 1, 0, - 1, 1 ], [ 1, 0, - 1, - 1 ], - [ - 1, 0, 1, 1 ], [ - 1, 0, 1, - 1 ], [ - 1, 0, - 1, 1 ], [ - 1, 0, - 1, - 1 ], - [ 1, 1, 0, 1 ], [ 1, 1, 0, - 1 ], [ 1, - 1, 0, 1 ], [ 1, - 1, 0, - 1 ], - [ - 1, 1, 0, 1 ], [ - 1, 1, 0, - 1 ], [ - 1, - 1, 0, 1 ], [ - 1, - 1, 0, - 1 ], - [ 1, 1, 1, 0 ], [ 1, 1, - 1, 0 ], [ 1, - 1, 1, 0 ], [ 1, - 1, - 1, 0 ], - [ - 1, 1, 1, 0 ], [ - 1, 1, - 1, 0 ], [ - 1, - 1, 1, 0 ], [ - 1, - 1, - 1, 0 ]]; - - this.p = []; - - for ( var i = 0; i < 256; i ++ ) { - - this.p[ i ] = Math.floor( r.random() * 256 ); - - } - - // To remove the need for index wrapping, double the permutation table length - this.perm = []; - - for ( var i = 0; i < 512; i ++ ) { - - this.perm[ i ] = this.p[ i & 255 ]; - - } - - // A lookup table to traverse the simplex around a given point in 4D. - // Details can be found where this table is used, in the 4D noise method. - this.simplex = [ - [ 0, 1, 2, 3 ], [ 0, 1, 3, 2 ], [ 0, 0, 0, 0 ], [ 0, 2, 3, 1 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 1, 2, 3, 0 ], - [ 0, 2, 1, 3 ], [ 0, 0, 0, 0 ], [ 0, 3, 1, 2 ], [ 0, 3, 2, 1 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 1, 3, 2, 0 ], - [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], - [ 1, 2, 0, 3 ], [ 0, 0, 0, 0 ], [ 1, 3, 0, 2 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 2, 3, 0, 1 ], [ 2, 3, 1, 0 ], - [ 1, 0, 2, 3 ], [ 1, 0, 3, 2 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 2, 0, 3, 1 ], [ 0, 0, 0, 0 ], [ 2, 1, 3, 0 ], - [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], - [ 2, 0, 1, 3 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 3, 0, 1, 2 ], [ 3, 0, 2, 1 ], [ 0, 0, 0, 0 ], [ 3, 1, 2, 0 ], - [ 2, 1, 0, 3 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 3, 1, 0, 2 ], [ 0, 0, 0, 0 ], [ 3, 2, 0, 1 ], [ 3, 2, 1, 0 ]]; - - }; - - SimplexNoise.prototype.dot = function ( g, x, y ) { - - return g[ 0 ] * x + g[ 1 ] * y; - - }; - - SimplexNoise.prototype.dot3 = function ( g, x, y, z ) { - - return g[ 0 ] * x + g[ 1 ] * y + g[ 2 ] * z; - - }; - - SimplexNoise.prototype.dot4 = function ( g, x, y, z, w ) { - - return g[ 0 ] * x + g[ 1 ] * y + g[ 2 ] * z + g[ 3 ] * w; - - }; - - SimplexNoise.prototype.noise = function ( xin, yin ) { - - var n0, n1, n2; // Noise contributions from the three corners - // Skew the input space to determine which simplex cell we're in - var F2 = 0.5 * ( Math.sqrt( 3.0 ) - 1.0 ); - var s = ( xin + yin ) * F2; // Hairy factor for 2D - var i = Math.floor( xin + s ); - var j = Math.floor( yin + s ); - var G2 = ( 3.0 - Math.sqrt( 3.0 ) ) / 6.0; - var t = ( i + j ) * G2; - var X0 = i - t; // Unskew the cell origin back to (x,y) space - var Y0 = j - t; - var x0 = xin - X0; // The x,y distances from the cell origin - var y0 = yin - Y0; - // For the 2D case, the simplex shape is an equilateral triangle. - // Determine which simplex we are in. - var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords - if ( x0 > y0 ) { - - i1 = 1; j1 = 0; - - // lower triangle, XY order: (0,0)->(1,0)->(1,1) - - } else { - - i1 = 0; j1 = 1; - - } // upper triangle, YX order: (0,0)->(0,1)->(1,1) - - // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and - // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where - // c = (3-sqrt(3))/6 - var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords - var y1 = y0 - j1 + G2; - var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords - var y2 = y0 - 1.0 + 2.0 * G2; - // Work out the hashed gradient indices of the three simplex corners - var ii = i & 255; - var jj = j & 255; - var gi0 = this.perm[ ii + this.perm[ jj ] ] % 12; - var gi1 = this.perm[ ii + i1 + this.perm[ jj + j1 ] ] % 12; - var gi2 = this.perm[ ii + 1 + this.perm[ jj + 1 ] ] % 12; - // Calculate the contribution from the three corners - var t0 = 0.5 - x0 * x0 - y0 * y0; - if ( t0 < 0 ) n0 = 0.0; - else { - - t0 *= t0; - n0 = t0 * t0 * this.dot( this.grad3[ gi0 ], x0, y0 ); // (x,y) of grad3 used for 2D gradient - - } - - var t1 = 0.5 - x1 * x1 - y1 * y1; - if ( t1 < 0 ) n1 = 0.0; - else { - - t1 *= t1; - n1 = t1 * t1 * this.dot( this.grad3[ gi1 ], x1, y1 ); - - } - - var t2 = 0.5 - x2 * x2 - y2 * y2; - if ( t2 < 0 ) n2 = 0.0; - else { - - t2 *= t2; - n2 = t2 * t2 * this.dot( this.grad3[ gi2 ], x2, y2 ); - - } - - // Add contributions from each corner to get the final noise value. - // The result is scaled to return values in the interval [-1,1]. - return 70.0 * ( n0 + n1 + n2 ); - - }; - - // 3D simplex noise - SimplexNoise.prototype.noise3d = function ( xin, yin, zin ) { - - var n0, n1, n2, n3; // Noise contributions from the four corners - // Skew the input space to determine which simplex cell we're in - var F3 = 1.0 / 3.0; - var s = ( xin + yin + zin ) * F3; // Very nice and simple skew factor for 3D - var i = Math.floor( xin + s ); - var j = Math.floor( yin + s ); - var k = Math.floor( zin + s ); - var G3 = 1.0 / 6.0; // Very nice and simple unskew factor, too - var t = ( i + j + k ) * G3; - var X0 = i - t; // Unskew the cell origin back to (x,y,z) space - var Y0 = j - t; - var Z0 = k - t; - var x0 = xin - X0; // The x,y,z distances from the cell origin - var y0 = yin - Y0; - var z0 = zin - Z0; - // For the 3D case, the simplex shape is a slightly irregular tetrahedron. - // Determine which simplex we are in. - var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords - var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords - if ( x0 >= y0 ) { - - if ( y0 >= z0 ) { - - i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; - - // X Y Z order - - } else if ( x0 >= z0 ) { - - i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; - - // X Z Y order - - } else { - - i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; - - } // Z X Y order - - } else { // x0 y0 ) ? 32 : 0; - var c2 = ( x0 > z0 ) ? 16 : 0; - var c3 = ( y0 > z0 ) ? 8 : 0; - var c4 = ( x0 > w0 ) ? 4 : 0; - var c5 = ( y0 > w0 ) ? 2 : 0; - var c6 = ( z0 > w0 ) ? 1 : 0; - var c = c1 + c2 + c3 + c4 + c5 + c6; - var i1, j1, k1, l1; // The integer offsets for the second simplex corner - var i2, j2, k2, l2; // The integer offsets for the third simplex corner - var i3, j3, k3, l3; // The integer offsets for the fourth simplex corner - // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. - // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0; - j1 = simplex[ c ][ 1 ] >= 3 ? 1 : 0; - k1 = simplex[ c ][ 2 ] >= 3 ? 1 : 0; - l1 = simplex[ c ][ 3 ] >= 3 ? 1 : 0; - // The number 2 in the "simplex" array is at the second largest coordinate. - i2 = simplex[ c ][ 0 ] >= 2 ? 1 : 0; - j2 = simplex[ c ][ 1 ] >= 2 ? 1 : 0; k2 = simplex[ c ][ 2 ] >= 2 ? 1 : 0; - l2 = simplex[ c ][ 3 ] >= 2 ? 1 : 0; - // The number 1 in the "simplex" array is at the second smallest coordinate. - i3 = simplex[ c ][ 0 ] >= 1 ? 1 : 0; - j3 = simplex[ c ][ 1 ] >= 1 ? 1 : 0; - k3 = simplex[ c ][ 2 ] >= 1 ? 1 : 0; - l3 = simplex[ c ][ 3 ] >= 1 ? 1 : 0; - // The fifth corner has all coordinate offsets = 1, so no need to look that up. - var x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords - var y1 = y0 - j1 + G4; - var z1 = z0 - k1 + G4; - var w1 = w0 - l1 + G4; - var x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords - var y2 = y0 - j2 + 2.0 * G4; - var z2 = z0 - k2 + 2.0 * G4; - var w2 = w0 - l2 + 2.0 * G4; - var x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords - var y3 = y0 - j3 + 3.0 * G4; - var z3 = z0 - k3 + 3.0 * G4; - var w3 = w0 - l3 + 3.0 * G4; - var x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords - var y4 = y0 - 1.0 + 4.0 * G4; - var z4 = z0 - 1.0 + 4.0 * G4; - var w4 = w0 - 1.0 + 4.0 * G4; - // Work out the hashed gradient indices of the five simplex corners - var ii = i & 255; - var jj = j & 255; - var kk = k & 255; - var ll = l & 255; - var gi0 = perm[ ii + perm[ jj + perm[ kk + perm[ ll ] ] ] ] % 32; - var gi1 = perm[ ii + i1 + perm[ jj + j1 + perm[ kk + k1 + perm[ ll + l1 ] ] ] ] % 32; - var gi2 = perm[ ii + i2 + perm[ jj + j2 + perm[ kk + k2 + perm[ ll + l2 ] ] ] ] % 32; - var gi3 = perm[ ii + i3 + perm[ jj + j3 + perm[ kk + k3 + perm[ ll + l3 ] ] ] ] % 32; - var gi4 = perm[ ii + 1 + perm[ jj + 1 + perm[ kk + 1 + perm[ ll + 1 ] ] ] ] % 32; - // Calculate the contribution from the five corners - var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0; - if ( t0 < 0 ) n0 = 0.0; - else { - - t0 *= t0; - n0 = t0 * t0 * this.dot4( grad4[ gi0 ], x0, y0, z0, w0 ); - - } - - var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1; - if ( t1 < 0 ) n1 = 0.0; - else { - - t1 *= t1; - n1 = t1 * t1 * this.dot4( grad4[ gi1 ], x1, y1, z1, w1 ); - - } - - var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2; - if ( t2 < 0 ) n2 = 0.0; - else { - - t2 *= t2; - n2 = t2 * t2 * this.dot4( grad4[ gi2 ], x2, y2, z2, w2 ); - - } - - var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3; - if ( t3 < 0 ) n3 = 0.0; - else { - - t3 *= t3; - n3 = t3 * t3 * this.dot4( grad4[ gi3 ], x3, y3, z3, w3 ); - - } - - var t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4; - if ( t4 < 0 ) n4 = 0.0; - else { - - t4 *= t4; - n4 = t4 * t4 * this.dot4( grad4[ gi4 ], x4, y4, z4, w4 ); - - } - - // Sum up and scale the result to cover the range [-1,1] - return 27.0 * ( n0 + n1 + n2 + n3 + n4 ); - - }; - - /** - * References: - * http://john-chapman-graphics.blogspot.com/2013/01/ssao-tutorial.html - * https://learnopengl.com/Advanced-Lighting/SSAO - * https://github.com/McNopper/OpenGL/blob/master/Example28/shader/ssao.frag.glsl - */ - - var SSAOShader = { - - defines: { - "PERSPECTIVE_CAMERA": 1, - "KERNEL_SIZE": 32 - }, - - uniforms: { - - "tDiffuse": { value: null }, - "tNormal": { value: null }, - "tDepth": { value: null }, - "tNoise": { value: null }, - "kernel": { value: null }, - "cameraNear": { value: null }, - "cameraFar": { value: null }, - "resolution": { value: new Vector2() }, - "cameraProjectionMatrix": { value: new Matrix4() }, - "cameraInverseProjectionMatrix": { value: new Matrix4() }, - "kernelRadius": { value: 8 }, - "minDistance": { value: 0.005 }, - "maxDistance": { value: 0.05 }, - - }, - - vertexShader: [ - - "varying vec2 vUv;", - - "void main() {", - - " vUv = uv;", - - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - - "}" - - ].join( "\n" ), - - fragmentShader: [ - - "uniform sampler2D tDiffuse;", - "uniform sampler2D tNormal;", - "uniform sampler2D tDepth;", - "uniform sampler2D tNoise;", - - "uniform vec3 kernel[ KERNEL_SIZE ];", - - "uniform vec2 resolution;", - - "uniform float cameraNear;", - "uniform float cameraFar;", - "uniform mat4 cameraProjectionMatrix;", - "uniform mat4 cameraInverseProjectionMatrix;", - - "uniform float kernelRadius;", - "uniform float minDistance;", // avoid artifacts caused by neighbour fragments with minimal depth difference - "uniform float maxDistance;", // avoid the influence of fragments which are too far away - - "varying vec2 vUv;", - - "#include ", - - "float getDepth( const in vec2 screenPosition ) {", - - " return texture2D( tDepth, screenPosition ).x;", - - "}", - - "float getLinearDepth( const in vec2 screenPosition ) {", - - " #if PERSPECTIVE_CAMERA == 1", - - " float fragCoordZ = texture2D( tDepth, screenPosition ).x;", - " float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );", - " return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );", - - " #else", - - " return texture2D( depthSampler, coord ).x;", - - " #endif", - - "}", - - "float getViewZ( const in float depth ) {", - - " #if PERSPECTIVE_CAMERA == 1", - - " return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );", - - " #else", - - " return orthographicDepthToViewZ( depth, cameraNear, cameraFar );", - - " #endif", - - "}", - - "vec3 getViewPosition( const in vec2 screenPosition, const in float depth, const in float viewZ ) {", - - " float clipW = cameraProjectionMatrix[2][3] * viewZ + cameraProjectionMatrix[3][3];", - - " vec4 clipPosition = vec4( ( vec3( screenPosition, depth ) - 0.5 ) * 2.0, 1.0 );", - - " clipPosition *= clipW; // unprojection.", - - " return ( cameraInverseProjectionMatrix * clipPosition ).xyz;", - - "}", - - "vec3 getViewNormal( const in vec2 screenPosition ) {", - - " return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz );", - - "}", - - "void main() {", - - " float depth = getDepth( vUv );", - " float viewZ = getViewZ( depth );", - - " vec3 viewPosition = getViewPosition( vUv, depth, viewZ );", - " vec3 viewNormal = getViewNormal( vUv );", - - " vec2 noiseScale = vec2( resolution.x / 4.0, resolution.y / 4.0 );", - " vec3 random = texture2D( tNoise, vUv * noiseScale ).xyz;", - - // compute matrix used to reorient a kernel vector - - " vec3 tangent = normalize( random - viewNormal * dot( random, viewNormal ) );", - " vec3 bitangent = cross( viewNormal, tangent );", - " mat3 kernelMatrix = mat3( tangent, bitangent, viewNormal );", - - " float occlusion = 0.0;", - - " for ( int i = 0; i < KERNEL_SIZE; i ++ ) {", - - " vec3 sampleVector = kernelMatrix * kernel[ i ];", // reorient sample vector in view space - " vec3 samplePoint = viewPosition + ( sampleVector * kernelRadius );", // calculate sample point - - " vec4 samplePointNDC = cameraProjectionMatrix * vec4( samplePoint, 1.0 );", // project point and calculate NDC - " samplePointNDC /= samplePointNDC.w;", - - " vec2 samplePointUv = samplePointNDC.xy * 0.5 + 0.5;", // compute uv coordinates - - " float realDepth = getLinearDepth( samplePointUv );", // get linear depth from depth texture - " float sampleDepth = viewZToOrthographicDepth( samplePoint.z, cameraNear, cameraFar );", // compute linear depth of the sample view Z value - " float delta = sampleDepth - realDepth;", - - " if ( delta > minDistance && delta < maxDistance ) {", // if fragment is before sample point, increase occlusion - - " occlusion += 1.0;", - - " }", - - " }", - - " occlusion = clamp( occlusion / float( KERNEL_SIZE ), 0.0, 1.0 );", - - " gl_FragColor = vec4( vec3( 1.0 - occlusion ), 1.0 );", - - "}" - - ].join( "\n" ) - - }; - - var SSAODepthShader = { - - defines: { - "PERSPECTIVE_CAMERA": 1 - }, - - uniforms: { - - "tDepth": { value: null }, - "cameraNear": { value: null }, - "cameraFar": { value: null }, - - }, - - vertexShader: [ - - "varying vec2 vUv;", - - "void main() {", - - " vUv = uv;", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - - "}" - - ].join( "\n" ), - - fragmentShader: [ - - "uniform sampler2D tDepth;", - - "uniform float cameraNear;", - "uniform float cameraFar;", - - "varying vec2 vUv;", - - "#include ", - - "float getLinearDepth( const in vec2 screenPosition ) {", - - " #if PERSPECTIVE_CAMERA == 1", - - " float fragCoordZ = texture2D( tDepth, screenPosition ).x;", - " float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );", - " return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );", - - " #else", - - " return texture2D( depthSampler, coord ).x;", - - " #endif", - - "}", - - "void main() {", - - " float depth = getLinearDepth( vUv );", - " gl_FragColor = vec4( vec3( 1.0 - depth ), 1.0 );", - - "}" - - ].join( "\n" ) - - }; - - var SSAOBlurShader = { - - uniforms: { - - "tDiffuse": { value: null }, - "resolution": { value: new Vector2() } - - }, - - vertexShader: [ - - "varying vec2 vUv;", - - "void main() {", - - " vUv = uv;", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - - "}" - - ].join( "\n" ), - - fragmentShader: [ - - "uniform sampler2D tDiffuse;", - - "uniform vec2 resolution;", - - "varying vec2 vUv;", - - "void main() {", - - " vec2 texelSize = ( 1.0 / resolution );", - " float result = 0.0;", - - " for ( int i = - 2; i <= 2; i ++ ) {", - - " for ( int j = - 2; j <= 2; j ++ ) {", - - " vec2 offset = ( vec2( float( i ), float( j ) ) ) * texelSize;", - " result += texture2D( tDiffuse, vUv + offset ).r;", - - " }", - - " }", - - " gl_FragColor = vec4( vec3( result / ( 5.0 * 5.0 ) ), 1.0 );", - - "}" - - ].join( "\n" ) - - }; - - var SSAOPass = function ( scene, camera, width, height ) { - - Pass.call( this ); - - this.width = ( width !== undefined ) ? width : 512; - this.height = ( height !== undefined ) ? height : 512; - - this.clear = true; - - this.camera = camera; - this.scene = scene; - - this.kernelRadius = 8; - this.kernelSize = 32; - this.kernel = []; - this.noiseTexture = null; - this.output = 0; - - this.minDistance = 0.005; - this.maxDistance = 0.1; - - // - - this.generateSampleKernel(); - this.generateRandomKernelRotations(); - - // beauty render target with depth buffer - - var depthTexture = new DepthTexture(); - depthTexture.type = UnsignedShortType; - depthTexture.minFilter = NearestFilter; - depthTexture.maxFilter = NearestFilter; - - this.beautyRenderTarget = new WebGLRenderTarget( this.width, this.height, { - minFilter: LinearFilter, - magFilter: LinearFilter, - format: RGBAFormat, - depthTexture: depthTexture, - depthBuffer: true - } ); - - // normal render target - - this.normalRenderTarget = new WebGLRenderTarget( this.width, this.height, { - minFilter: NearestFilter, - magFilter: NearestFilter, - format: RGBAFormat - } ); - - // ssao render target - - this.ssaoRenderTarget = new WebGLRenderTarget( this.width, this.height, { - minFilter: LinearFilter, - magFilter: LinearFilter, - format: RGBAFormat - } ); - - this.blurRenderTarget = this.ssaoRenderTarget.clone(); - - // ssao material - - if ( SSAOShader === undefined ) { - - console.error( 'THREE.SSAOPass: The pass relies on SSAOShader.' ); - - } - - this.ssaoMaterial = new ShaderMaterial( { - defines: Object.assign( {}, SSAOShader.defines ), - uniforms: UniformsUtils.clone( SSAOShader.uniforms ), - vertexShader: SSAOShader.vertexShader, - fragmentShader: SSAOShader.fragmentShader, - blending: NoBlending - } ); - - this.ssaoMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture; - this.ssaoMaterial.uniforms[ 'tNormal' ].value = this.normalRenderTarget.texture; - this.ssaoMaterial.uniforms[ 'tDepth' ].value = this.beautyRenderTarget.depthTexture; - this.ssaoMaterial.uniforms[ 'tNoise' ].value = this.noiseTexture; - this.ssaoMaterial.uniforms[ 'kernel' ].value = this.kernel; - this.ssaoMaterial.uniforms[ 'cameraNear' ].value = this.camera.near; - this.ssaoMaterial.uniforms[ 'cameraFar' ].value = this.camera.far; - this.ssaoMaterial.uniforms[ 'resolution' ].value.set( this.width, this.height ); - this.ssaoMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix ); - this.ssaoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.getInverse( this.camera.projectionMatrix ); - - // normal material - - this.normalMaterial = new MeshNormalMaterial(); - this.normalMaterial.blending = NoBlending; - - // blur material - - this.blurMaterial = new ShaderMaterial( { - defines: Object.assign( {}, SSAOBlurShader.defines ), - uniforms: UniformsUtils.clone( SSAOBlurShader.uniforms ), - vertexShader: SSAOBlurShader.vertexShader, - fragmentShader: SSAOBlurShader.fragmentShader - } ); - this.blurMaterial.uniforms[ 'tDiffuse' ].value = this.ssaoRenderTarget.texture; - this.blurMaterial.uniforms[ 'resolution' ].value.set( this.width, this.height ); - - // material for rendering the depth - - this.depthRenderMaterial = new ShaderMaterial( { - defines: Object.assign( {}, SSAODepthShader.defines ), - uniforms: UniformsUtils.clone( SSAODepthShader.uniforms ), - vertexShader: SSAODepthShader.vertexShader, - fragmentShader: SSAODepthShader.fragmentShader, - blending: NoBlending - } ); - this.depthRenderMaterial.uniforms[ 'tDepth' ].value = this.beautyRenderTarget.depthTexture; - this.depthRenderMaterial.uniforms[ 'cameraNear' ].value = this.camera.near; - this.depthRenderMaterial.uniforms[ 'cameraFar' ].value = this.camera.far; - - // material for rendering the content of a render target - - this.copyMaterial = new ShaderMaterial( { - uniforms: UniformsUtils.clone( CopyShader.uniforms ), - vertexShader: CopyShader.vertexShader, - fragmentShader: CopyShader.fragmentShader, - transparent: true, - depthTest: false, - depthWrite: false, - blendSrc: DstColorFactor, - blendDst: ZeroFactor, - blendEquation: AddEquation, - blendSrcAlpha: DstAlphaFactor, - blendDstAlpha: ZeroFactor, - blendEquationAlpha: AddEquation - } ); - - this.fsQuad = new Pass.FullScreenQuad( null ); - - this.originalClearColor = new Color(); - - }; - - SSAOPass.prototype = Object.assign( Object.create( Pass.prototype ), { - - constructor: SSAOPass, - - dispose: function () { - - // dispose render targets - - this.beautyRenderTarget.dispose(); - this.normalRenderTarget.dispose(); - this.ssaoRenderTarget.dispose(); - this.blurRenderTarget.dispose(); - - // dispose materials - - this.normalMaterial.dispose(); - this.blurMaterial.dispose(); - this.copyMaterial.dispose(); - this.depthRenderMaterial.dispose(); - - // dipsose full screen quad - - this.fsQuad.dispose(); - - }, - - render: function ( renderer, writeBuffer /*, readBuffer, deltaTime, maskActive */ ) { - - // render beauty and depth - - renderer.setRenderTarget( this.beautyRenderTarget ); - renderer.clear(); - renderer.render( this.scene, this.camera ); - - // render normals - - this.renderOverride( renderer, this.normalMaterial, this.normalRenderTarget, 0x7777ff, 1.0 ); - - // render SSAO - - this.ssaoMaterial.uniforms[ 'kernelRadius' ].value = this.kernelRadius; - this.ssaoMaterial.uniforms[ 'minDistance' ].value = this.minDistance; - this.ssaoMaterial.uniforms[ 'maxDistance' ].value = this.maxDistance; - this.renderPass( renderer, this.ssaoMaterial, this.ssaoRenderTarget ); - - // render blur - - this.renderPass( renderer, this.blurMaterial, this.blurRenderTarget ); - - // output result to screen - - switch ( this.output ) { - - case SSAOPass.OUTPUT.SSAO: - - this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssaoRenderTarget.texture; - this.copyMaterial.blending = NoBlending; - this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); - - break; - - case SSAOPass.OUTPUT.Blur: - - this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget.texture; - this.copyMaterial.blending = NoBlending; - this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); - - break; - - case SSAOPass.OUTPUT.Beauty: - - this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture; - this.copyMaterial.blending = NoBlending; - this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); - - break; - - case SSAOPass.OUTPUT.Depth: - - this.renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : writeBuffer ); - - break; - - case SSAOPass.OUTPUT.Normal: - - this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.normalRenderTarget.texture; - this.copyMaterial.blending = NoBlending; - this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); - - break; - - case SSAOPass.OUTPUT.Default: - - this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture; - this.copyMaterial.blending = NoBlending; - this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); - - this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget.texture; - this.copyMaterial.blending = CustomBlending; - this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); - - break; - - default: - console.warn( 'THREE.SSAOPass: Unknown output type.' ); - - } - - }, - - renderPass: function ( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) { - - // save original state - this.originalClearColor.copy( renderer.getClearColor() ); - var originalClearAlpha = renderer.getClearAlpha(); - var originalAutoClear = renderer.autoClear; - - renderer.setRenderTarget( renderTarget ); - - // setup pass state - renderer.autoClear = false; - if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) { - - renderer.setClearColor( clearColor ); - renderer.setClearAlpha( clearAlpha || 0.0 ); - renderer.clear(); - - } - - this.fsQuad.material = passMaterial; - this.fsQuad.render( renderer ); - - // restore original state - renderer.autoClear = originalAutoClear; - renderer.setClearColor( this.originalClearColor ); - renderer.setClearAlpha( originalClearAlpha ); - - }, - - renderOverride: function ( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) { - - this.originalClearColor.copy( renderer.getClearColor() ); - var originalClearAlpha = renderer.getClearAlpha(); - var originalAutoClear = renderer.autoClear; - - renderer.setRenderTarget( renderTarget ); - renderer.autoClear = false; - - clearColor = overrideMaterial.clearColor || clearColor; - clearAlpha = overrideMaterial.clearAlpha || clearAlpha; - - if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) { - - renderer.setClearColor( clearColor ); - renderer.setClearAlpha( clearAlpha || 0.0 ); - renderer.clear(); - - } - - this.scene.overrideMaterial = overrideMaterial; - renderer.render( this.scene, this.camera ); - this.scene.overrideMaterial = null; - - // restore original state - - renderer.autoClear = originalAutoClear; - renderer.setClearColor( this.originalClearColor ); - renderer.setClearAlpha( originalClearAlpha ); - - }, - - setSize: function ( width, height ) { - - this.width = width; - this.height = height; - - this.beautyRenderTarget.setSize( width, height ); - this.ssaoRenderTarget.setSize( width, height ); - this.normalRenderTarget.setSize( width, height ); - this.blurRenderTarget.setSize( width, height ); - - this.ssaoMaterial.uniforms[ 'resolution' ].value.set( width, height ); - this.ssaoMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix ); - this.ssaoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.getInverse( this.camera.projectionMatrix ); - - this.blurMaterial.uniforms[ 'resolution' ].value.set( width, height ); - - }, - - generateSampleKernel: function () { - - var kernelSize = this.kernelSize; - var kernel = this.kernel; - - for ( var i = 0; i < kernelSize; i ++ ) { - - var sample = new Vector3(); - sample.x = ( Math.random() * 2 ) - 1; - sample.y = ( Math.random() * 2 ) - 1; - sample.z = Math.random(); - - sample.normalize(); - - var scale = i / kernelSize; - scale = MathUtils.lerp( 0.1, 1, scale * scale ); - sample.multiplyScalar( scale ); - - kernel.push( sample ); - - } - - }, - - generateRandomKernelRotations: function () { - - var width = 4, height = 4; - - if ( SimplexNoise === undefined ) { - - console.error( 'THREE.SSAOPass: The pass relies on SimplexNoise.' ); - - } - - var simplex = new SimplexNoise(); - - var size = width * height; - var data = new Float32Array( size * 4 ); - - for ( var i = 0; i < size; i ++ ) { - - var stride = i * 4; - - var x = ( Math.random() * 2 ) - 1; - var y = ( Math.random() * 2 ) - 1; - var z = 0; - - var noise = simplex.noise3d( x, y, z ); - - data[ stride ] = noise; - data[ stride + 1 ] = noise; - data[ stride + 2 ] = noise; - data[ stride + 3 ] = 1; - - } - - this.noiseTexture = new DataTexture( data, width, height, RGBAFormat, FloatType ); - this.noiseTexture.wrapS = RepeatWrapping; - this.noiseTexture.wrapT = RepeatWrapping; - - } - - } ); - - SSAOPass.OUTPUT = { - 'Default': 0, - 'SSAO': 1, - 'Blur': 2, - 'Beauty': 3, - 'Depth': 4, - 'Normal': 5 - }; - - /** - * TODO - */ - - var SAOShader = { - defines: { - "NUM_SAMPLES": 7, - "NUM_RINGS": 4, - "NORMAL_TEXTURE": 0, - "DIFFUSE_TEXTURE": 0, - "DEPTH_PACKING": 1, - "PERSPECTIVE_CAMERA": 1 - }, - uniforms: { - - "tDepth": { value: null }, - "tDiffuse": { value: null }, - "tNormal": { value: null }, - "size": { value: new Vector2( 512, 512 ) }, - - "cameraNear": { value: 1 }, - "cameraFar": { value: 100 }, - "cameraProjectionMatrix": { value: new Matrix4() }, - "cameraInverseProjectionMatrix": { value: new Matrix4() }, - - "scale": { value: 1.0 }, - "intensity": { value: 0.1 }, - "bias": { value: 0.5 }, - - "minResolution": { value: 0.0 }, - "kernelRadius": { value: 100.0 }, - "randomSeed": { value: 0.0 } - }, - vertexShader: [ - "varying vec2 vUv;", - - "void main() {", - " vUv = uv;", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - "}" - - ].join( "\n" ), - fragmentShader: [ - "#include ", - - "varying vec2 vUv;", - - "#if DIFFUSE_TEXTURE == 1", - "uniform sampler2D tDiffuse;", - "#endif", - - "uniform sampler2D tDepth;", - - "#if NORMAL_TEXTURE == 1", - "uniform sampler2D tNormal;", - "#endif", - - "uniform float cameraNear;", - "uniform float cameraFar;", - "uniform mat4 cameraProjectionMatrix;", - "uniform mat4 cameraInverseProjectionMatrix;", - - "uniform float scale;", - "uniform float intensity;", - "uniform float bias;", - "uniform float kernelRadius;", - "uniform float minResolution;", - "uniform vec2 size;", - "uniform float randomSeed;", - - "// RGBA depth", - - "#include ", - - "vec4 getDefaultColor( const in vec2 screenPosition ) {", - " #if DIFFUSE_TEXTURE == 1", - " return texture2D( tDiffuse, vUv );", - " #else", - " return vec4( 1.0 );", - " #endif", - "}", - - "float getDepth( const in vec2 screenPosition ) {", - " #if DEPTH_PACKING == 1", - " return unpackRGBAToDepth( texture2D( tDepth, screenPosition ) );", - " #else", - " return texture2D( tDepth, screenPosition ).x;", - " #endif", - "}", - - "float getViewZ( const in float depth ) {", - " #if PERSPECTIVE_CAMERA == 1", - " return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );", - " #else", - " return orthographicDepthToViewZ( depth, cameraNear, cameraFar );", - " #endif", - "}", - - "vec3 getViewPosition( const in vec2 screenPosition, const in float depth, const in float viewZ ) {", - " float clipW = cameraProjectionMatrix[2][3] * viewZ + cameraProjectionMatrix[3][3];", - " vec4 clipPosition = vec4( ( vec3( screenPosition, depth ) - 0.5 ) * 2.0, 1.0 );", - " clipPosition *= clipW; // unprojection.", - - " return ( cameraInverseProjectionMatrix * clipPosition ).xyz;", - "}", - - "vec3 getViewNormal( const in vec3 viewPosition, const in vec2 screenPosition ) {", - " #if NORMAL_TEXTURE == 1", - " return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz );", - " #else", - " return normalize( cross( dFdx( viewPosition ), dFdy( viewPosition ) ) );", - " #endif", - "}", - - "float scaleDividedByCameraFar;", - "float minResolutionMultipliedByCameraFar;", - - "float getOcclusion( const in vec3 centerViewPosition, const in vec3 centerViewNormal, const in vec3 sampleViewPosition ) {", - " vec3 viewDelta = sampleViewPosition - centerViewPosition;", - " float viewDistance = length( viewDelta );", - " float scaledScreenDistance = scaleDividedByCameraFar * viewDistance;", - - " return max(0.0, (dot(centerViewNormal, viewDelta) - minResolutionMultipliedByCameraFar) / scaledScreenDistance - bias) / (1.0 + pow2( scaledScreenDistance ) );", - "}", - - "// moving costly divides into consts", - "const float ANGLE_STEP = PI2 * float( NUM_RINGS ) / float( NUM_SAMPLES );", - "const float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES );", - - "float getAmbientOcclusion( const in vec3 centerViewPosition ) {", - " // precompute some variables require in getOcclusion.", - " scaleDividedByCameraFar = scale / cameraFar;", - " minResolutionMultipliedByCameraFar = minResolution * cameraFar;", - " vec3 centerViewNormal = getViewNormal( centerViewPosition, vUv );", - - " // jsfiddle that shows sample pattern: https://jsfiddle.net/a16ff1p7/", - " float angle = rand( vUv + randomSeed ) * PI2;", - " vec2 radius = vec2( kernelRadius * INV_NUM_SAMPLES ) / size;", - " vec2 radiusStep = radius;", - - " float occlusionSum = 0.0;", - " float weightSum = 0.0;", - - " for( int i = 0; i < NUM_SAMPLES; i ++ ) {", - " vec2 sampleUv = vUv + vec2( cos( angle ), sin( angle ) ) * radius;", - " radius += radiusStep;", - " angle += ANGLE_STEP;", - - " float sampleDepth = getDepth( sampleUv );", - " if( sampleDepth >= ( 1.0 - EPSILON ) ) {", - " continue;", - " }", - - " float sampleViewZ = getViewZ( sampleDepth );", - " vec3 sampleViewPosition = getViewPosition( sampleUv, sampleDepth, sampleViewZ );", - " occlusionSum += getOcclusion( centerViewPosition, centerViewNormal, sampleViewPosition );", - " weightSum += 1.0;", - " }", - - " if( weightSum == 0.0 ) discard;", - - " return occlusionSum * ( intensity / weightSum );", - "}", - - - "void main() {", - " float centerDepth = getDepth( vUv );", - " if( centerDepth >= ( 1.0 - EPSILON ) ) {", - " discard;", - " }", - - " float centerViewZ = getViewZ( centerDepth );", - " vec3 viewPosition = getViewPosition( vUv, centerDepth, centerViewZ );", - - " float ambientOcclusion = getAmbientOcclusion( viewPosition );", - - " gl_FragColor = getDefaultColor( vUv );", - " gl_FragColor.xyz *= 1.0 - ambientOcclusion;", - "}" - ].join( "\n" ) - }; - - /** - * TODO - */ - - var DepthLimitedBlurShader = { - defines: { - "KERNEL_RADIUS": 4, - "DEPTH_PACKING": 1, - "PERSPECTIVE_CAMERA": 1 - }, - uniforms: { - "tDiffuse": { value: null }, - "size": { value: new Vector2( 512, 512 ) }, - "sampleUvOffsets": { value: [ new Vector2( 0, 0 ) ] }, - "sampleWeights": { value: [ 1.0 ] }, - "tDepth": { value: null }, - "cameraNear": { value: 10 }, - "cameraFar": { value: 1000 }, - "depthCutoff": { value: 10 }, - }, - vertexShader: [ - "#include ", - - "uniform vec2 size;", - - "varying vec2 vUv;", - "varying vec2 vInvSize;", - - "void main() {", - " vUv = uv;", - " vInvSize = 1.0 / size;", - - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - "}" - - ].join( "\n" ), - fragmentShader: [ - "#include ", - "#include ", - - "uniform sampler2D tDiffuse;", - "uniform sampler2D tDepth;", - - "uniform float cameraNear;", - "uniform float cameraFar;", - "uniform float depthCutoff;", - - "uniform vec2 sampleUvOffsets[ KERNEL_RADIUS + 1 ];", - "uniform float sampleWeights[ KERNEL_RADIUS + 1 ];", - - "varying vec2 vUv;", - "varying vec2 vInvSize;", - - "float getDepth( const in vec2 screenPosition ) {", - " #if DEPTH_PACKING == 1", - " return unpackRGBAToDepth( texture2D( tDepth, screenPosition ) );", - " #else", - " return texture2D( tDepth, screenPosition ).x;", - " #endif", - "}", - - "float getViewZ( const in float depth ) {", - " #if PERSPECTIVE_CAMERA == 1", - " return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );", - " #else", - " return orthographicDepthToViewZ( depth, cameraNear, cameraFar );", - " #endif", - "}", - - "void main() {", - " float depth = getDepth( vUv );", - " if( depth >= ( 1.0 - EPSILON ) ) {", - " discard;", - " }", - - " float centerViewZ = -getViewZ( depth );", - " bool rBreak = false, lBreak = false;", - - " float weightSum = sampleWeights[0];", - " vec4 diffuseSum = texture2D( tDiffuse, vUv ) * weightSum;", - - " for( int i = 1; i <= KERNEL_RADIUS; i ++ ) {", - - " float sampleWeight = sampleWeights[i];", - " vec2 sampleUvOffset = sampleUvOffsets[i] * vInvSize;", - - " vec2 sampleUv = vUv + sampleUvOffset;", - " float viewZ = -getViewZ( getDepth( sampleUv ) );", - - " if( abs( viewZ - centerViewZ ) > depthCutoff ) rBreak = true;", - - " if( ! rBreak ) {", - " diffuseSum += texture2D( tDiffuse, sampleUv ) * sampleWeight;", - " weightSum += sampleWeight;", - " }", - - " sampleUv = vUv - sampleUvOffset;", - " viewZ = -getViewZ( getDepth( sampleUv ) );", - - " if( abs( viewZ - centerViewZ ) > depthCutoff ) lBreak = true;", - - " if( ! lBreak ) {", - " diffuseSum += texture2D( tDiffuse, sampleUv ) * sampleWeight;", - " weightSum += sampleWeight;", - " }", - - " }", - - " gl_FragColor = diffuseSum / weightSum;", - "}" - ].join( "\n" ) - }; - - var BlurShaderUtils = { - - createSampleWeights: function ( kernelRadius, stdDev ) { - - var gaussian = function ( x, stdDev ) { - - return Math.exp( - ( x * x ) / ( 2.0 * ( stdDev * stdDev ) ) ) / ( Math.sqrt( 2.0 * Math.PI ) * stdDev ); - - }; - - var weights = []; - - for ( var i = 0; i <= kernelRadius; i ++ ) { - - weights.push( gaussian( i, stdDev ) ); - - } - - return weights; - - }, - - createSampleOffsets: function ( kernelRadius, uvIncrement ) { - - var offsets = []; - - for ( var i = 0; i <= kernelRadius; i ++ ) { - - offsets.push( uvIncrement.clone().multiplyScalar( i ) ); - - } - - return offsets; - - }, - - configure: function ( material, kernelRadius, stdDev, uvIncrement ) { - - material.defines[ "KERNEL_RADIUS" ] = kernelRadius; - material.uniforms[ "sampleUvOffsets" ].value = BlurShaderUtils.createSampleOffsets( kernelRadius, uvIncrement ); - material.uniforms[ "sampleWeights" ].value = BlurShaderUtils.createSampleWeights( kernelRadius, stdDev ); - material.needsUpdate = true; - - } - - }; - - /** - * Unpack RGBA depth shader - * - show RGBA encoded depth as monochrome color - */ - - var UnpackDepthRGBAShader = { - - uniforms: { - - "tDiffuse": { value: null }, - "opacity": { value: 1.0 } - - }, - - vertexShader: [ - - "varying vec2 vUv;", - - "void main() {", - - " vUv = uv;", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - - "}" - - ].join( "\n" ), - - fragmentShader: [ - - "uniform float opacity;", - - "uniform sampler2D tDiffuse;", - - "varying vec2 vUv;", - - "#include ", - - "void main() {", - - " float depth = 1.0 - unpackRGBAToDepth( texture2D( tDiffuse, vUv ) );", - " gl_FragColor = vec4( vec3( depth ), opacity );", - - "}" - - ].join( "\n" ) - - }; - - /** - * SAO implementation inspired from bhouston previous SAO work - */ - - var SAOPass = function ( scene, camera, depthTexture, useNormals, resolution ) { - - Pass.call( this ); - - this.scene = scene; - this.camera = camera; - - this.clear = true; - this.needsSwap = false; - - this.supportsDepthTextureExtension = ( depthTexture !== undefined ) ? depthTexture : false; - this.supportsNormalTexture = ( useNormals !== undefined ) ? useNormals : false; - - this.originalClearColor = new Color(); - this.oldClearColor = new Color(); - this.oldClearAlpha = 1; - - this.params = { - output: 0, - saoBias: 0.5, - saoIntensity: 0.18, - saoScale: 1, - saoKernelRadius: 100, - saoMinResolution: 0, - saoBlur: true, - saoBlurRadius: 8, - saoBlurStdDev: 4, - saoBlurDepthCutoff: 0.01 - }; - - this.resolution = ( resolution !== undefined ) ? new Vector2( resolution.x, resolution.y ) : new Vector2( 256, 256 ); - - this.saoRenderTarget = new WebGLRenderTarget( this.resolution.x, this.resolution.y, { - minFilter: LinearFilter, - magFilter: LinearFilter, - format: RGBAFormat - } ); - this.blurIntermediateRenderTarget = this.saoRenderTarget.clone(); - this.beautyRenderTarget = this.saoRenderTarget.clone(); - - this.normalRenderTarget = new WebGLRenderTarget( this.resolution.x, this.resolution.y, { - minFilter: NearestFilter, - magFilter: NearestFilter, - format: RGBAFormat - } ); - this.depthRenderTarget = this.normalRenderTarget.clone(); - - if ( this.supportsDepthTextureExtension ) { - - var depthTexture = new DepthTexture(); - depthTexture.type = UnsignedShortType; - depthTexture.minFilter = NearestFilter; - depthTexture.maxFilter = NearestFilter; - - this.beautyRenderTarget.depthTexture = depthTexture; - this.beautyRenderTarget.depthBuffer = true; - - } - - this.depthMaterial = new MeshDepthMaterial(); - this.depthMaterial.depthPacking = RGBADepthPacking; - this.depthMaterial.blending = NoBlending; - - this.normalMaterial = new MeshNormalMaterial(); - this.normalMaterial.blending = NoBlending; - - if ( SAOShader === undefined ) { - - console.error( 'THREE.SAOPass relies on SAOShader' ); - - } - - this.saoMaterial = new ShaderMaterial( { - defines: Object.assign( {}, SAOShader.defines ), - fragmentShader: SAOShader.fragmentShader, - vertexShader: SAOShader.vertexShader, - uniforms: UniformsUtils.clone( SAOShader.uniforms ) - } ); - this.saoMaterial.extensions.derivatives = true; - this.saoMaterial.defines[ 'DEPTH_PACKING' ] = this.supportsDepthTextureExtension ? 0 : 1; - this.saoMaterial.defines[ 'NORMAL_TEXTURE' ] = this.supportsNormalTexture ? 1 : 0; - this.saoMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0; - this.saoMaterial.uniforms[ 'tDepth' ].value = ( this.supportsDepthTextureExtension ) ? depthTexture : this.depthRenderTarget.texture; - this.saoMaterial.uniforms[ 'tNormal' ].value = this.normalRenderTarget.texture; - this.saoMaterial.uniforms[ 'size' ].value.set( this.resolution.x, this.resolution.y ); - this.saoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.getInverse( this.camera.projectionMatrix ); - this.saoMaterial.uniforms[ 'cameraProjectionMatrix' ].value = this.camera.projectionMatrix; - this.saoMaterial.blending = NoBlending; - - if ( DepthLimitedBlurShader === undefined ) { - - console.error( 'THREE.SAOPass relies on DepthLimitedBlurShader' ); - - } - - this.vBlurMaterial = new ShaderMaterial( { - uniforms: UniformsUtils.clone( DepthLimitedBlurShader.uniforms ), - defines: Object.assign( {}, DepthLimitedBlurShader.defines ), - vertexShader: DepthLimitedBlurShader.vertexShader, - fragmentShader: DepthLimitedBlurShader.fragmentShader - } ); - this.vBlurMaterial.defines[ 'DEPTH_PACKING' ] = this.supportsDepthTextureExtension ? 0 : 1; - this.vBlurMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0; - this.vBlurMaterial.uniforms[ 'tDiffuse' ].value = this.saoRenderTarget.texture; - this.vBlurMaterial.uniforms[ 'tDepth' ].value = ( this.supportsDepthTextureExtension ) ? depthTexture : this.depthRenderTarget.texture; - this.vBlurMaterial.uniforms[ 'size' ].value.set( this.resolution.x, this.resolution.y ); - this.vBlurMaterial.blending = NoBlending; - - this.hBlurMaterial = new ShaderMaterial( { - uniforms: UniformsUtils.clone( DepthLimitedBlurShader.uniforms ), - defines: Object.assign( {}, DepthLimitedBlurShader.defines ), - vertexShader: DepthLimitedBlurShader.vertexShader, - fragmentShader: DepthLimitedBlurShader.fragmentShader - } ); - this.hBlurMaterial.defines[ 'DEPTH_PACKING' ] = this.supportsDepthTextureExtension ? 0 : 1; - this.hBlurMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0; - this.hBlurMaterial.uniforms[ 'tDiffuse' ].value = this.blurIntermediateRenderTarget.texture; - this.hBlurMaterial.uniforms[ 'tDepth' ].value = ( this.supportsDepthTextureExtension ) ? depthTexture : this.depthRenderTarget.texture; - this.hBlurMaterial.uniforms[ 'size' ].value.set( this.resolution.x, this.resolution.y ); - this.hBlurMaterial.blending = NoBlending; - - if ( CopyShader === undefined ) { - - console.error( 'THREE.SAOPass relies on CopyShader' ); - - } - - this.materialCopy = new ShaderMaterial( { - uniforms: UniformsUtils.clone( CopyShader.uniforms ), - vertexShader: CopyShader.vertexShader, - fragmentShader: CopyShader.fragmentShader, - blending: NoBlending - } ); - this.materialCopy.transparent = true; - this.materialCopy.depthTest = false; - this.materialCopy.depthWrite = false; - this.materialCopy.blending = CustomBlending; - this.materialCopy.blendSrc = DstColorFactor; - this.materialCopy.blendDst = ZeroFactor; - this.materialCopy.blendEquation = AddEquation; - this.materialCopy.blendSrcAlpha = DstAlphaFactor; - this.materialCopy.blendDstAlpha = ZeroFactor; - this.materialCopy.blendEquationAlpha = AddEquation; - - if ( UnpackDepthRGBAShader === undefined ) { - - console.error( 'THREE.SAOPass relies on UnpackDepthRGBAShader' ); - - } - - this.depthCopy = new ShaderMaterial( { - uniforms: UniformsUtils.clone( UnpackDepthRGBAShader.uniforms ), - vertexShader: UnpackDepthRGBAShader.vertexShader, - fragmentShader: UnpackDepthRGBAShader.fragmentShader, - blending: NoBlending - } ); - - this.fsQuad = new Pass.FullScreenQuad( null ); - - }; - - SAOPass.OUTPUT = { - 'Beauty': 1, - 'Default': 0, - 'SAO': 2, - 'Depth': 3, - 'Normal': 4 - }; - - SAOPass.prototype = Object.assign( Object.create( Pass.prototype ), { - constructor: SAOPass, - - render: function ( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive*/ ) { - - // Rendering readBuffer first when rendering to screen - if ( this.renderToScreen ) { - - this.materialCopy.blending = NoBlending; - this.materialCopy.uniforms[ 'tDiffuse' ].value = readBuffer.texture; - this.materialCopy.needsUpdate = true; - this.renderPass( renderer, this.materialCopy, null ); - - } - - if ( this.params.output === 1 ) { - - return; - - } - - this.oldClearColor.copy( renderer.getClearColor() ); - this.oldClearAlpha = renderer.getClearAlpha(); - var oldAutoClear = renderer.autoClear; - renderer.autoClear = false; - - renderer.setRenderTarget( this.depthRenderTarget ); - renderer.clear(); - - this.saoMaterial.uniforms[ 'bias' ].value = this.params.saoBias; - this.saoMaterial.uniforms[ 'intensity' ].value = this.params.saoIntensity; - this.saoMaterial.uniforms[ 'scale' ].value = this.params.saoScale; - this.saoMaterial.uniforms[ 'kernelRadius' ].value = this.params.saoKernelRadius; - this.saoMaterial.uniforms[ 'minResolution' ].value = this.params.saoMinResolution; - this.saoMaterial.uniforms[ 'cameraNear' ].value = this.camera.near; - this.saoMaterial.uniforms[ 'cameraFar' ].value = this.camera.far; - // this.saoMaterial.uniforms['randomSeed'].value = Math.random(); - - var depthCutoff = this.params.saoBlurDepthCutoff * ( this.camera.far - this.camera.near ); - this.vBlurMaterial.uniforms[ 'depthCutoff' ].value = depthCutoff; - this.hBlurMaterial.uniforms[ 'depthCutoff' ].value = depthCutoff; - - this.vBlurMaterial.uniforms[ 'cameraNear' ].value = this.camera.near; - this.vBlurMaterial.uniforms[ 'cameraFar' ].value = this.camera.far; - this.hBlurMaterial.uniforms[ 'cameraNear' ].value = this.camera.near; - this.hBlurMaterial.uniforms[ 'cameraFar' ].value = this.camera.far; - - this.params.saoBlurRadius = Math.floor( this.params.saoBlurRadius ); - if ( ( this.prevStdDev !== this.params.saoBlurStdDev ) || ( this.prevNumSamples !== this.params.saoBlurRadius ) ) { - - BlurShaderUtils.configure( this.vBlurMaterial, this.params.saoBlurRadius, this.params.saoBlurStdDev, new Vector2( 0, 1 ) ); - BlurShaderUtils.configure( this.hBlurMaterial, this.params.saoBlurRadius, this.params.saoBlurStdDev, new Vector2( 1, 0 ) ); - this.prevStdDev = this.params.saoBlurStdDev; - this.prevNumSamples = this.params.saoBlurRadius; - - } - - // Rendering scene to depth texture - renderer.setClearColor( 0x000000 ); - renderer.setRenderTarget( this.beautyRenderTarget ); - renderer.clear(); - renderer.render( this.scene, this.camera ); - - // Re-render scene if depth texture extension is not supported - if ( ! this.supportsDepthTextureExtension ) { - - // Clear rule : far clipping plane in both RGBA and Basic encoding - this.renderOverride( renderer, this.depthMaterial, this.depthRenderTarget, 0x000000, 1.0 ); - - } - - if ( this.supportsNormalTexture ) { - - // Clear rule : default normal is facing the camera - this.renderOverride( renderer, this.normalMaterial, this.normalRenderTarget, 0x7777ff, 1.0 ); - - } - - // Rendering SAO texture - this.renderPass( renderer, this.saoMaterial, this.saoRenderTarget, 0xffffff, 1.0 ); - - // Blurring SAO texture - if ( this.params.saoBlur ) { - - this.renderPass( renderer, this.vBlurMaterial, this.blurIntermediateRenderTarget, 0xffffff, 1.0 ); - this.renderPass( renderer, this.hBlurMaterial, this.saoRenderTarget, 0xffffff, 1.0 ); - - } - - var outputMaterial = this.materialCopy; - // Setting up SAO rendering - if ( this.params.output === 3 ) { - - if ( this.supportsDepthTextureExtension ) { - - this.materialCopy.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.depthTexture; - this.materialCopy.needsUpdate = true; - - } else { - - this.depthCopy.uniforms[ 'tDiffuse' ].value = this.depthRenderTarget.texture; - this.depthCopy.needsUpdate = true; - outputMaterial = this.depthCopy; - - } - - } else if ( this.params.output === 4 ) { - - this.materialCopy.uniforms[ 'tDiffuse' ].value = this.normalRenderTarget.texture; - this.materialCopy.needsUpdate = true; - - } else { - - this.materialCopy.uniforms[ 'tDiffuse' ].value = this.saoRenderTarget.texture; - this.materialCopy.needsUpdate = true; - - } - - // Blending depends on output, only want a CustomBlending when showing SAO - if ( this.params.output === 0 ) { - - outputMaterial.blending = CustomBlending; - - } else { - - outputMaterial.blending = NoBlending; - - } - - // Rendering SAOPass result on top of previous pass - this.renderPass( renderer, outputMaterial, this.renderToScreen ? null : readBuffer ); - - renderer.setClearColor( this.oldClearColor, this.oldClearAlpha ); - renderer.autoClear = oldAutoClear; - - }, - - renderPass: function ( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) { - - // save original state - this.originalClearColor.copy( renderer.getClearColor() ); - var originalClearAlpha = renderer.getClearAlpha(); - var originalAutoClear = renderer.autoClear; - - renderer.setRenderTarget( renderTarget ); - - // setup pass state - renderer.autoClear = false; - if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) { - - renderer.setClearColor( clearColor ); - renderer.setClearAlpha( clearAlpha || 0.0 ); - renderer.clear(); - - } - - this.fsQuad.material = passMaterial; - this.fsQuad.render( renderer ); - - // restore original state - renderer.autoClear = originalAutoClear; - renderer.setClearColor( this.originalClearColor ); - renderer.setClearAlpha( originalClearAlpha ); - - }, - - renderOverride: function ( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) { - - this.originalClearColor.copy( renderer.getClearColor() ); - var originalClearAlpha = renderer.getClearAlpha(); - var originalAutoClear = renderer.autoClear; - - renderer.setRenderTarget( renderTarget ); - renderer.autoClear = false; - - clearColor = overrideMaterial.clearColor || clearColor; - clearAlpha = overrideMaterial.clearAlpha || clearAlpha; - if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) { - - renderer.setClearColor( clearColor ); - renderer.setClearAlpha( clearAlpha || 0.0 ); - renderer.clear(); - - } - - this.scene.overrideMaterial = overrideMaterial; - renderer.render( this.scene, this.camera ); - this.scene.overrideMaterial = null; - - // restore original state - renderer.autoClear = originalAutoClear; - renderer.setClearColor( this.originalClearColor ); - renderer.setClearAlpha( originalClearAlpha ); - - }, - - setSize: function ( width, height ) { - - this.beautyRenderTarget.setSize( width, height ); - this.saoRenderTarget.setSize( width, height ); - this.blurIntermediateRenderTarget.setSize( width, height ); - this.normalRenderTarget.setSize( width, height ); - this.depthRenderTarget.setSize( width, height ); - - this.saoMaterial.uniforms[ 'size' ].value.set( width, height ); - this.saoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.getInverse( this.camera.projectionMatrix ); - this.saoMaterial.uniforms[ 'cameraProjectionMatrix' ].value = this.camera.projectionMatrix; - this.saoMaterial.needsUpdate = true; - - this.vBlurMaterial.uniforms[ 'size' ].value.set( width, height ); - this.vBlurMaterial.needsUpdate = true; - - this.hBlurMaterial.uniforms[ 'size' ].value.set( width, height ); - this.hBlurMaterial.needsUpdate = true; - - } - - } ); - - function projectedUnitsPerMeter(latitude) { - let c = ThreeboxConstants; - return Math.abs(c.WORLD_SIZE / Math.cos(c.DEG2RAD * latitude) / c.EARTH_CIRCUMFERENCE); - } - - function projectToWorld(coords) { - // Spherical mercator forward projection, re-scaling to WORLD_SIZE - let c = ThreeboxConstants; - var projected = [ - c.MERCATOR_A * c.DEG2RAD * coords[0] * c.PROJECTION_WORLD_SIZE, - c.MERCATOR_A * Math.log(Math.tan(Math.PI * 0.25 + 0.5 * c.DEG2RAD * coords[1])) * c.PROJECTION_WORLD_SIZE - ]; - - //z dimension, defaulting to 0 if not provided - if (!coords[2]) { - projected.push(0); - } else { - var pixelsPerMeter = projectedUnitsPerMeter(coords[1]); - projected.push(coords[2] * pixelsPerMeter); - } - - var result = new Vector3(projected[0], projected[1], projected[2]); - - return result; - } - - class Mapbox3DTilesLayer { - constructor(params) { - if (!params) throw new Error('parameters missing for mapbox 3D tiles layer'); - if (!params.id) throw new Error('id parameter missing for mapbox 3D tiles layer'); - //if (!params.url) throw new Error('url parameter missing for mapbox 3D tiles layer'); - - (this.id = params.id), (this.url = params.url); - this.styleParams = {}; - this.projectToMercator = params.projectToMercator ? params.projectToMercator : false; - this.lights = params.lights ? params.lights : this.getDefaultLights(); - if ('color' in params) this.styleParams.color = params.color; - if ('opacity' in params) this.styleParams.opacity = params.opacity; - if ('pointsize' in params) this.styleParams.pointsize = params.pointsize; - - this.style = params.style || this.styleParams; //styleparams to be replaced by style config - this.loadStatus = 0; - this.viewProjectionMatrix = null; - this.type = 'custom'; - this.renderingMode = '3d'; - - window.addEventListener('resize', (e) => { - this._resize(e); - }); - } - - getDefaultLights() { - const width = window.innerWidth; - const height = window.innerHeight; - const hemiLight = new HemisphereLight(0xffffff, 0xbebebe, 0.7); - const dirLight = this._getDefaultDirLight(width, height); - - return [hemiLight, dirLight]; - } - - _getDefaultDirLight(width, height) { - const dirLight = new DirectionalLight(0xffffff, 0.5); - dirLight.color.setHSL(0.1, 1, 0.95); - dirLight.position.set(-1, -1.75, 1); - dirLight.position.multiplyScalar(100); - dirLight.castShadow = true; - dirLight.shadow.camera.near = -10000; - dirLight.shadow.camera.far = 2000000; - dirLight.shadow.bias = 0.0038; - dirLight.shadow.mapSize.width = width; - dirLight.shadow.mapSize.height = height * 2.5; - dirLight.shadow.camera.left = -width; - dirLight.shadow.camera.right = width; - dirLight.shadow.camera.top = -height * 2.5; - dirLight.shadow.camera.bottom = height * 2.5; - dirLight.uuid = 'shadowlight'; - - return dirLight; - } - - loadVisibleTiles() { - if (this.tileset && this.tileset.root) { - this.tileset.root.checkLoad(this.cameraSync.frustum, this.cameraSync.cameraPosition); - } - } - - onAdd(map, gl) { - this.map = map; - const fov = 36.8; - const aspect = map.getCanvas().width / map.getCanvas().height; - const near = 0.000000000001; - const far = Infinity; - // create perspective camera, parameters reinitialized by CameraSync - this.camera = new PerspectiveCamera(fov, aspect, near, far); - this.mapQueryRenderedFeatures = map.queryRenderedFeatures.bind(this.map); - this.map.queryRenderedFeatures = this.queryRenderedFeatures.bind(this); - this.scene = new Scene(); - this.rootTransform = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; - - this.lights.forEach((light) => { - this.scene.add(light); - if (light.shadow && light.shadow.camera) ; - }); - - this.world = new Group(); - this.world.name = 'flatMercatorWorld'; - this.scene.add(this.world); - - this.renderer = new WebGLRenderer({ - alpha: true, - antialias: true, - canvas: map.getCanvas(), - context: gl - }); - - this.renderer.shadowMap.enabled = true; - this.renderer.shadowMap.type = PCFShadowMap; - - this.highlight = new Highlight(this.scene, this.map); - this.marker = new Marker(this.scene, this.map); - - /* WIP on composer */ - let width = window.innerWidth; - let height = window.innerHeight; - this.composer = new EffectComposer(this.renderer); - - let ssaoPass = new SSAOPass(this.scene, this.camera, width, height); - ssaoPass.kernelRadius = 0.1; - //this.composer.addPass( ssaoPass ); //Renders white screen - - let saoPass = new SAOPass(this.scene, this.camera, false, true); - saoPass._render = saoPass.render; - saoPass.render = function (renderer) { - //renderer.setRenderTarget( _____ ) - renderer.clear(); - this._render.apply(this, arguments); - }; - //this.composer.addPass( saoPass ); //Renders black screen - - //let renderScene = new RenderPass(this.scene, this.camera); - //let bloomPass = new UnrealBloomPass( - // new THREE.Vector2(window.innerWidth, window.innerHeight), - // 1.5, - // 0.4, - // 0.85 - //); - //bloomPass.threshold = 0; - //bloomPass.strength = 1.5; - //bloomPass.radius = 0; - //this.composer.addPass( renderScene ); - //this.composer.addPass( bloomPass ); - - /* END OF WIP */ - - this.renderer.shadowMap.enabled = true; - this.renderer.autoClear = false; - - this.cameraSync = new CameraSync(this.map, this.camera, this.world); - this.cameraSync.aspect = width / height; - this.cameraSync.updateCallback = () => this.loadVisibleTiles(); - - //raycaster for mouse events - this.raycaster = new Raycaster(); - if (this.url) { - this.tileset = new TileSet((ts) => { - if (ts.loaded) { - //WIP, poor performance - ts.styleParams = this.style; - this.map.triggerRepaint(); - } - }); - this.tileset - .load(this.url, this.style, this.projectToMercator) - .then(() => { - if (this.tileset.root) { - this.world.add(this.tileset.root.totalContent); - this.world.updateMatrixWorld(); - this.loadStatus = 1; - this.loadVisibleTiles(); - } - }) - .catch((error) => { - console.error(`${error} (${this.url})`); - }); - } - - this.addShadow(); - } - - onRemove(map, gl) { - // todo: (much) more cleanup? - this.map.queryRenderedFeatures = this.mapQueryRenderedFeatures; - this.cameraSync.detachCamera(); - this.cameraSync = null; - } - - _resize(e) { - let width = window.innerWidth; - let height = window.innerHeight; - this.renderer.setSize(width, height); - this.cameraSync.aspect = width / height; - this.camera.aspect = width / height; - this.composer.setSize(width, height); - - for (let i = 0; i < this.scene.children.length; i++) { - let c = this.scene.children[i]; - if (c.uuid === 'shadowlight') { - c = this._getDefaultDirLight(width, height); - } - } - } - - addShadow() { - //debug plane - //var geo1 = new THREE.PlaneBufferGeometry(10000, 10000, 1, 1); - //var mat1 = new THREE.MeshBasicMaterial({ color: 0xFFFFFF, side: THREE.DoubleSide }); - //var plane1 = new THREE.Mesh(geo1, mat1); - //plane1.receiveShadow = true; - //this.scene.add(plane1); - - if (!this.shadowPlane) { - var planeGeometry = new PlaneBufferGeometry(10000, 10000, 1, 1); - this.shadowMaterial = new ShadowMaterial(); - this.shadowMaterial.opacity = 0.3; - this.shadowPlane = new Mesh(planeGeometry, this.shadowMaterial); - this.shadowPlane.receiveShadow = true; - } - - this.scene.add(this.shadowPlane); - } - - removeShadow() { - this.scene.remove(this.shadowPlane); - } - - setShadowOpacity(opacity) { - const newOpacity = opacity < 0 ? 0.0 : opacity > 1 ? 1.0 : opacity; - this.shadowMaterial.opacity = newOpacity; - } - - setStyle(style) { - //WIP - this.style = style - ? style - : { - color: 0xff00ff - }; - applyStyle(this.world, this.style); - } - - //ToDo: currently based on default lights, can be overriden by user, handle differently - setHismphereIntensity(intensity) { - if (this.lights[0] instanceof HemisphereLight) { - const newIntensity = intensity < 0 ? 0.0 : intensity > 1 ? 1.0 : intensity; - this.lights[0].intensity = newIntensity; - } - } - - queryRenderedFeatures(geometry, options) { - let result = this.mapQueryRenderedFeatures(geometry, options); - if (!this.map || !this.map.transform) { - return result; - } - if (!(options && options.layers && !options.layers.includes(this.id))) { - if (geometry && geometry.x && geometry.y) { - var mouse = new Vector2(); - - // scale mouse pixel position to a percentage of the screen's width and height - mouse.x = (geometry.x / this.map.transform.width) * 2 - 1; - mouse.y = 1 - (geometry.y / this.map.transform.height) * 2; - - this.raycaster.setFromCamera(mouse, this.camera); - - // calculate objects intersecting the picking ray - let intersects = this.raycaster.intersectObjects(this.world.children, true); - - //TODO: make this code nicer and more efficient - /* temp disabled coloring - if ((intersects.length === 0 && this.previntersect) || (intersects.length && this.previntersect && intersects[0].object.uuid != this.previntersect.object.uuid)) { - const object = this.previntersect.object; - if (object.geometry.attributes.color) { - const count = object.geometry.attributes.position.count; - for (let i = 0;ii % stride >= offset-1 && i % stride <= itemSize-1); - let positions = new THREE.BufferAttribute( new Float32Array( count * 3 ),3); - for (let i =0;i<=count;i++){ - mypositions.setXYZ(i,attribute.getX(i),attribute.getY(i),attribute.getZ(i)); - } - - //const normals = attributes.normal.data.array.filter((d,i)=>i % 7 >= 0 && i % 7 <= 2); - - - object.geometry.setAttribute( 'color', new THREE.BufferAttribute( new Float32Array( count * 3 ), 3 ) ); - for ( let i = 0; i < count; i ++ ) { - const color = new THREE.Color(); - const positions = object.geometry.attributes.position; - const colors = object.geometry.attributes.color; - if (geometry.attributes._batchid.data.array[i * 7 + 6] == propertyIndex){ - color.setRGB( ( positions.getY( i ) / radius + 1 ) / 2, 1.0, 0.5 ); - } - else { - color.setRGB( 0.9, 0.9, 0.9 ); - } - colors.setXYZ( i, color.r, color.g, color.b ); - } - - const material = new THREE.MeshPhongMaterial( { - color: 'white', - flatShading: true, - vertexColors: true, - shininess: 0 - } ); - - object.material = material; - /* End of WIP on coloring */ - } else { - if (intersect.index != null) { - feature.properties.index = intersect.index; - } else { - feature.properties.name = this.id; - } - } - /* WORK in progress - if (options.outline != false && (intersect.object !== this.outlinedObject || - (propertyIndex != null && propertyIndex !== this.outlinePropertyIndex) - || (propertyIndex == null && intersect.index !== this.outlineIndex))) { - - //WIP - - //this.outlinePass.selectedObjects = [intersect.object]; - - // update outline - if (this.outlineMesh) { - let parent = this.outlineMesh.parent; - parent.remove(this.outlineMesh); - this.outlineMesh = null; - } - this.outlinePropertyIndex = propertyIndex; - this.outlineIndex = intersect.index; - if (intersect.object instanceof THREE.Mesh) { - this.outlinedObject = intersect.object; - let outlineMaterial = new THREE.MeshBasicMaterial({color: options.outlineColor? options.outlineColor : 0xff0000, wireframe: true}); - let outlineMesh; - if (intersect.object && - intersect.object.geometry && - intersect.object.geometry.attributes && - intersect.object.geometry.attributes._batchid) { - // create new geometry from faces that have same _batchid - let geometry = intersect.object.geometry; - if (geometry.index) { - let ip1 = geometry.index.array[intersect.faceIndex*3]; - let idx = geometry.attributes._batchid.data.array[ip1*7+6]; - let blockFaces = []; - for (let faceIndex = 0; faceIndex < geometry.index.array.length; faceIndex += 3) { - let p1 = geometry.index.array[faceIndex]; - if (geometry.attributes._batchid.data.array[p1*7+6] === idx) { - let p2 = geometry.index.array[faceIndex+1]; - if (geometry.attributes._batchid.data.array[p2*7+6] === idx) { - let p3 = geometry.index.array[faceIndex+2]; - if (geometry.attributes._batchid.data.array[p3*7+6] === idx) { - blockFaces.push(faceIndex); - } - } - } - } - let highLightGeometry = new THREE.Geometry(); - for (let vertexCount = 0, face = 0; face < blockFaces.length; face++) { - let faceIndex = blockFaces[face]; - let p1 = geometry.index.array[faceIndex]; - let p2 = geometry.index.array[faceIndex+1]; - let p3 = geometry.index.array[faceIndex+2]; - let positions = geometry.attributes.position.data.array; - highLightGeometry.vertices.push( - new THREE.Vector3(positions[p1*7], positions[p1*7+1], positions[p1*7+2]), - new THREE.Vector3(positions[p2*7], positions[p2*7+1], positions[p2*7+2]), - new THREE.Vector3(positions[p3*7], positions[p3*7+1], positions[p3*7+2]), - ) - highLightGeometry.faces.push(new THREE.Face3(vertexCount, vertexCount+1, vertexCount+2)); - vertexCount += 3; - } - highLightGeometry.computeBoundingSphere(); - outlineMesh = new THREE.Mesh(highLightGeometry, outlineMaterial); - } else { - let ip1 = intersect.faceIndex*3; - let idx = geometry.attributes._batchid.array[ip1]; - let blockFaces = []; - for (let faceIndex = 0; faceIndex < geometry.attributes._batchid.array.length; faceIndex += 3) { - let p1 = faceIndex; - if (geometry.attributes._batchid.array[p1] === idx) { - let p2 = faceIndex + 1; - if (geometry.attributes._batchid.array[p2] === idx) { - let p3 = faceIndex + 2; - if (geometry.attributes._batchid.array[p3] === idx) { - blockFaces.push(faceIndex); - } - } - } - } - let highLightGeometry = new THREE.Geometry(); - for (let vertexCount = 0, face = 0; face < blockFaces.length; face++) { - let faceIndex = blockFaces[face] * 3; - let positions = geometry.attributes.position.array; - highLightGeometry.vertices.push( - new THREE.Vector3(positions[faceIndex], positions[faceIndex+1], positions[faceIndex+2]), - new THREE.Vector3(positions[faceIndex+3], positions[faceIndex+4], positions[faceIndex+5]), - new THREE.Vector3(positions[faceIndex+6], positions[faceIndex+7], positions[faceIndex+8]), - ) - highLightGeometry.faces.push(new THREE.Face3(vertexCount, vertexCount+1, vertexCount+2)); - vertexCount += 3; - } - highLightGeometry.computeBoundingSphere(); - outlineMesh = new THREE.Mesh(highLightGeometry, outlineMaterial); - } - } else { - outlineMesh = new THREE.Mesh(this.outlinedObject.geometry, outlineMaterial); - } - outlineMesh.position.x = this.outlinedObject.position.x+0.1; - outlineMesh.position.y = this.outlinedObject.position.y+0.1; - outlineMesh.position.z = this.outlinedObject.position.z+0.1; - outlineMesh.quaternion.copy(this.outlinedObject.quaternion); - outlineMesh.scale.copy(this.outlinedObject.scale); - outlineMesh.matrix.copy(this.outlinedObject.matrix); - outlineMesh.raycast = () =>{}; - outlineMesh.name = "outline"; - outlineMesh.wireframe = true; - this.outlinedObject.parent.add(outlineMesh); - this.outlineMesh = outlineMesh; - - } - } - /* END OF work in progress */ - result.unshift(feature); - this.map.triggerRepaint(); - } else { - this.outlinedObject = null; - if (this.outlineMesh) { - let parent = this.outlineMesh.parent; - parent.remove(this.outlineMesh); - this.outlineMesh = null; - this.map.triggerRepaint(); - } - } - } - } - - return result; - } - - _update() { - this.renderer.state.reset(); - //WIP on composer - //this.composer.render (); - this.renderer.render(this.scene, this.camera); - - /*if (this.loadStatus == 1) { // first render after root tile is loaded - this.loadStatus = 2; - let frustum = new THREE.Frustum(); - frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(this.camera.projectionMatrix, this.camera.matrixWorldInverse)); - if (this.tileset.root) { - this.tileset.root.checkLoad(frustum, this.getCameraPosition()); - } - }*/ - } - - update() { - requestAnimationFrame(() => this._update()); - } - - render() { - const markers = this.marker.getMarkers(); - for (let i = 0; i < markers.length; i++) { - markers[i].renderer.render(markers[i].marker, this.camera); - markers[i].renderer.domElement.style = 'position: absolute; top: 0; pointer-events: none;'; - - for (let j = 0; j < markers[i].renderer.domElement.children.length; j++) { - const child = markers[i].renderer.domElement.children[j]; - child.style = 'pointer-events: auto;'; - child.transform.baseVal[0].matrix.e -= child.firstChild.width.baseVal.value / 2; - child.transform.baseVal[0].matrix.f -= child.firstChild.height.baseVal.value / 2; - } - } - - this._update(); - } - } - - exports.Mapbox3DTilesLayer = Mapbox3DTilesLayer; - exports.projectToWorld = projectToWorld; - exports.projectedUnitsPerMeter = projectedUnitsPerMeter; - - return exports; - -}({})); -//# sourceMappingURL=Mapbox3DTiles.js.map diff --git a/samples/bordeaux/old/index.html b/samples/bordeaux/old/index.html deleted file mode 100644 index 2710113..0000000 --- a/samples/bordeaux/old/index.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - -
-
- - - - \ No newline at end of file diff --git a/samples/traffic_lights/mapbox/Box.glb b/samples/traffic_lights/mapbox/Box.glb deleted file mode 100644 index 95ec886b6b92b134291fd41d34ac9d5349306e0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1664 zcmb7EOK;jh5S}DW+O$p6v`u^8GeNd_1bm4oEftl43Q#VHgE0$OGB&bJ+Q_oRvHz-n zq(7!Jix*Y|3Dweg=e6_A%bt4u#xVe_&H(fXY|f(@CPIkBiUbn22;I3GyAPRY#|SxE#v~@J z-RZV!7Blr6`_bt&`^`?9nFdzn`eWB2AFOMR#W1rd(&eFRdl`st&r#1>1WTZ{gEyie zT(@AfoJ@Fl@A97_$mlWVoykNr7-KrYd=dEEkNb}c3{ujK0x6e1_PSp7z%Cj)?0>TUa1Jlw h71BAph6{KDmq-`z7OvnOyhpl%4{!}1;S 0 ) ? 1 : + x; - - }; - - } - - if ( 'name' in Function.prototype === false ) { - - // Missing in IE - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name - - Object.defineProperty( Function.prototype, 'name', { - - get: function () { - - return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[ 1 ]; - - } - - } ); - - } - - if ( Object.assign === undefined ) { - - // Missing in IE - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign - - Object.assign = function ( target ) { - - if ( target === undefined || target === null ) { - - throw new TypeError( 'Cannot convert undefined or null to object' ); - - } - - const output = Object( target ); - - for ( let index = 1; index < arguments.length; index ++ ) { - - const source = arguments[ index ]; - - if ( source !== undefined && source !== null ) { - - for ( const nextKey in source ) { - - if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { - - output[ nextKey ] = source[ nextKey ]; - - } - - } - - } - - } - - return output; - - }; - - } - - const REVISION = '120'; - const CullFaceNone = 0; - const CullFaceBack = 1; - const CullFaceFront = 2; - const PCFShadowMap = 1; - const PCFSoftShadowMap = 2; - const VSMShadowMap = 3; - const FrontSide = 0; - const BackSide = 1; - const DoubleSide = 2; - const FlatShading = 1; - const NoBlending = 0; - const NormalBlending = 1; - const AdditiveBlending = 2; - const SubtractiveBlending = 3; - const MultiplyBlending = 4; - const CustomBlending = 5; - const AddEquation = 100; - const SubtractEquation = 101; - const ReverseSubtractEquation = 102; - const MinEquation = 103; - const MaxEquation = 104; - const ZeroFactor = 200; - const OneFactor = 201; - const SrcColorFactor = 202; - const OneMinusSrcColorFactor = 203; - const SrcAlphaFactor = 204; - const OneMinusSrcAlphaFactor = 205; - const DstAlphaFactor = 206; - const OneMinusDstAlphaFactor = 207; - const DstColorFactor = 208; - const OneMinusDstColorFactor = 209; - const SrcAlphaSaturateFactor = 210; - const NeverDepth = 0; - const AlwaysDepth = 1; - const LessDepth = 2; - const LessEqualDepth = 3; - const EqualDepth = 4; - const GreaterEqualDepth = 5; - const GreaterDepth = 6; - const NotEqualDepth = 7; - const MultiplyOperation = 0; - const MixOperation = 1; - const AddOperation = 2; - const NoToneMapping = 0; - const LinearToneMapping = 1; - const ReinhardToneMapping = 2; - const CineonToneMapping = 3; - const ACESFilmicToneMapping = 4; - const CustomToneMapping = 5; - - const UVMapping = 300; - const CubeReflectionMapping = 301; - const CubeRefractionMapping = 302; - const EquirectangularReflectionMapping = 303; - const EquirectangularRefractionMapping = 304; - const CubeUVReflectionMapping = 306; - const CubeUVRefractionMapping = 307; - const RepeatWrapping = 1000; - const ClampToEdgeWrapping = 1001; - const MirroredRepeatWrapping = 1002; - const NearestFilter = 1003; - const NearestMipmapNearestFilter = 1004; - const NearestMipmapLinearFilter = 1005; - const LinearFilter = 1006; - const LinearMipmapNearestFilter = 1007; - const LinearMipmapLinearFilter = 1008; - const UnsignedByteType = 1009; - const ByteType = 1010; - const ShortType = 1011; - const UnsignedShortType = 1012; - const IntType = 1013; - const UnsignedIntType = 1014; - const FloatType = 1015; - const HalfFloatType = 1016; - const UnsignedShort4444Type = 1017; - const UnsignedShort5551Type = 1018; - const UnsignedShort565Type = 1019; - const UnsignedInt248Type = 1020; - const AlphaFormat = 1021; - const RGBFormat = 1022; - const RGBAFormat = 1023; - const LuminanceFormat = 1024; - const LuminanceAlphaFormat = 1025; - const DepthFormat = 1026; - const DepthStencilFormat = 1027; - const RedFormat = 1028; - const RedIntegerFormat = 1029; - const RGFormat = 1030; - const RGIntegerFormat = 1031; - const RGBIntegerFormat = 1032; - const RGBAIntegerFormat = 1033; - - const RGB_S3TC_DXT1_Format = 33776; - const RGBA_S3TC_DXT1_Format = 33777; - const RGBA_S3TC_DXT3_Format = 33778; - const RGBA_S3TC_DXT5_Format = 33779; - const RGB_PVRTC_4BPPV1_Format = 35840; - const RGB_PVRTC_2BPPV1_Format = 35841; - const RGBA_PVRTC_4BPPV1_Format = 35842; - const RGBA_PVRTC_2BPPV1_Format = 35843; - const RGB_ETC1_Format = 36196; - const RGB_ETC2_Format = 37492; - const RGBA_ETC2_EAC_Format = 37496; - const RGBA_ASTC_4x4_Format = 37808; - const RGBA_ASTC_5x4_Format = 37809; - const RGBA_ASTC_5x5_Format = 37810; - const RGBA_ASTC_6x5_Format = 37811; - const RGBA_ASTC_6x6_Format = 37812; - const RGBA_ASTC_8x5_Format = 37813; - const RGBA_ASTC_8x6_Format = 37814; - const RGBA_ASTC_8x8_Format = 37815; - const RGBA_ASTC_10x5_Format = 37816; - const RGBA_ASTC_10x6_Format = 37817; - const RGBA_ASTC_10x8_Format = 37818; - const RGBA_ASTC_10x10_Format = 37819; - const RGBA_ASTC_12x10_Format = 37820; - const RGBA_ASTC_12x12_Format = 37821; - const RGBA_BPTC_Format = 36492; - const SRGB8_ALPHA8_ASTC_4x4_Format = 37840; - const SRGB8_ALPHA8_ASTC_5x4_Format = 37841; - const SRGB8_ALPHA8_ASTC_5x5_Format = 37842; - const SRGB8_ALPHA8_ASTC_6x5_Format = 37843; - const SRGB8_ALPHA8_ASTC_6x6_Format = 37844; - const SRGB8_ALPHA8_ASTC_8x5_Format = 37845; - const SRGB8_ALPHA8_ASTC_8x6_Format = 37846; - const SRGB8_ALPHA8_ASTC_8x8_Format = 37847; - const SRGB8_ALPHA8_ASTC_10x5_Format = 37848; - const SRGB8_ALPHA8_ASTC_10x6_Format = 37849; - const SRGB8_ALPHA8_ASTC_10x8_Format = 37850; - const SRGB8_ALPHA8_ASTC_10x10_Format = 37851; - const SRGB8_ALPHA8_ASTC_12x10_Format = 37852; - const SRGB8_ALPHA8_ASTC_12x12_Format = 37853; - const LoopOnce = 2200; - const LoopRepeat = 2201; - const LoopPingPong = 2202; - const InterpolateDiscrete = 2300; - const InterpolateLinear = 2301; - const InterpolateSmooth = 2302; - const ZeroCurvatureEnding = 2400; - const ZeroSlopeEnding = 2401; - const WrapAroundEnding = 2402; - const NormalAnimationBlendMode = 2500; - const AdditiveAnimationBlendMode = 2501; - const TrianglesDrawMode = 0; - const TriangleStripDrawMode = 1; - const TriangleFanDrawMode = 2; - const LinearEncoding = 3000; - const sRGBEncoding = 3001; - const GammaEncoding = 3007; - const RGBEEncoding = 3002; - const LogLuvEncoding = 3003; - const RGBM7Encoding = 3004; - const RGBM16Encoding = 3005; - const RGBDEncoding = 3006; - const BasicDepthPacking = 3200; - const RGBADepthPacking = 3201; - const TangentSpaceNormalMap = 0; - const ObjectSpaceNormalMap = 1; - const KeepStencilOp = 7680; - const AlwaysStencilFunc = 519; - - const StaticDrawUsage = 35044; - const DynamicDrawUsage = 35048; - const GLSL3 = "300 es"; - - /** - * https://github.com/mrdoob/eventdispatcher.js/ - */ - - function EventDispatcher() {} - - Object.assign( EventDispatcher.prototype, { - - addEventListener: function ( type, listener ) { - - if ( this._listeners === undefined ) this._listeners = {}; - - const listeners = this._listeners; - - if ( listeners[ type ] === undefined ) { - - listeners[ type ] = []; - - } - - if ( listeners[ type ].indexOf( listener ) === - 1 ) { - - listeners[ type ].push( listener ); - - } - - }, - - hasEventListener: function ( type, listener ) { - - if ( this._listeners === undefined ) return false; - - const listeners = this._listeners; - - return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; - - }, - - removeEventListener: function ( type, listener ) { - - if ( this._listeners === undefined ) return; - - const listeners = this._listeners; - const listenerArray = listeners[ type ]; - - if ( listenerArray !== undefined ) { - - const index = listenerArray.indexOf( listener ); - - if ( index !== - 1 ) { - - listenerArray.splice( index, 1 ); - - } - - } - - }, - - dispatchEvent: function ( event ) { - - if ( this._listeners === undefined ) return; - - const listeners = this._listeners; - const listenerArray = listeners[ event.type ]; - - if ( listenerArray !== undefined ) { - - event.target = this; - - // Make a copy, in case listeners are removed while iterating. - const array = listenerArray.slice( 0 ); - - for ( let i = 0, l = array.length; i < l; i ++ ) { - - array[ i ].call( this, event ); - - } - - } - - } - - } ); - - const _lut = []; - - for ( let i = 0; i < 256; i ++ ) { - - _lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 ); - - } - - let _seed = 1234567; - - const MathUtils = { - - DEG2RAD: Math.PI / 180, - RAD2DEG: 180 / Math.PI, - - generateUUID: function () { - - // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 - - const d0 = Math.random() * 0xffffffff | 0; - const d1 = Math.random() * 0xffffffff | 0; - const d2 = Math.random() * 0xffffffff | 0; - const d3 = Math.random() * 0xffffffff | 0; - const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' + - _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' + - _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] + - _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ]; - - // .toUpperCase() here flattens concatenated strings to save heap memory space. - return uuid.toUpperCase(); - - }, - - clamp: function ( value, min, max ) { - - return Math.max( min, Math.min( max, value ) ); - - }, - - // compute euclidian modulo of m % n - // https://en.wikipedia.org/wiki/Modulo_operation - - euclideanModulo: function ( n, m ) { - - return ( ( n % m ) + m ) % m; - - }, - - // Linear mapping from range to range - - mapLinear: function ( x, a1, a2, b1, b2 ) { - - return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); - - }, - - // https://en.wikipedia.org/wiki/Linear_interpolation - - lerp: function ( x, y, t ) { - - return ( 1 - t ) * x + t * y; - - }, - - // http://en.wikipedia.org/wiki/Smoothstep - - smoothstep: function ( x, min, max ) { - - if ( x <= min ) return 0; - if ( x >= max ) return 1; - - x = ( x - min ) / ( max - min ); - - return x * x * ( 3 - 2 * x ); - - }, - - smootherstep: function ( x, min, max ) { - - if ( x <= min ) return 0; - if ( x >= max ) return 1; - - x = ( x - min ) / ( max - min ); - - return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); - - }, - - // Random integer from interval - - randInt: function ( low, high ) { - - return low + Math.floor( Math.random() * ( high - low + 1 ) ); - - }, - - // Random float from interval - - randFloat: function ( low, high ) { - - return low + Math.random() * ( high - low ); - - }, - - // Random float from <-range/2, range/2> interval - - randFloatSpread: function ( range ) { - - return range * ( 0.5 - Math.random() ); - - }, - - // Deterministic pseudo-random float in the interval [ 0, 1 ] - - seededRandom: function ( s ) { - - if ( s !== undefined ) _seed = s % 2147483647; - - // Park-Miller algorithm - - _seed = _seed * 16807 % 2147483647; - - return ( _seed - 1 ) / 2147483646; - - }, - - degToRad: function ( degrees ) { - - return degrees * MathUtils.DEG2RAD; - - }, - - radToDeg: function ( radians ) { - - return radians * MathUtils.RAD2DEG; - - }, - - isPowerOfTwo: function ( value ) { - - return ( value & ( value - 1 ) ) === 0 && value !== 0; - - }, - - ceilPowerOfTwo: function ( value ) { - - return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); - - }, - - floorPowerOfTwo: function ( value ) { - - return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); - - }, - - setQuaternionFromProperEuler: function ( q, a, b, c, order ) { - - // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles - - // rotations are applied to the axes in the order specified by 'order' - // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c' - // angles are in radians - - const cos = Math.cos; - const sin = Math.sin; - - const c2 = cos( b / 2 ); - const s2 = sin( b / 2 ); - - const c13 = cos( ( a + c ) / 2 ); - const s13 = sin( ( a + c ) / 2 ); - - const c1_3 = cos( ( a - c ) / 2 ); - const s1_3 = sin( ( a - c ) / 2 ); - - const c3_1 = cos( ( c - a ) / 2 ); - const s3_1 = sin( ( c - a ) / 2 ); - - switch ( order ) { - - case 'XYX': - q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 ); - break; - - case 'YZY': - q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 ); - break; - - case 'ZXZ': - q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 ); - break; - - case 'XZX': - q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 ); - break; - - case 'YXY': - q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 ); - break; - - case 'ZYZ': - q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 ); - break; - - default: - console.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order ); - - } - - } - - }; - - class Vector2 { - - constructor( x = 0, y = 0 ) { - - Object.defineProperty( this, 'isVector2', { value: true } ); - - this.x = x; - this.y = y; - - } - - get width() { - - return this.x; - - } - - set width( value ) { - - this.x = value; - - } - - get height() { - - return this.y; - - } - - set height( value ) { - - this.y = value; - - } - - set( x, y ) { - - this.x = x; - this.y = y; - - return this; - - } - - setScalar( scalar ) { - - this.x = scalar; - this.y = scalar; - - return this; - - } - - setX( x ) { - - this.x = x; - - return this; - - } - - setY( y ) { - - this.y = y; - - return this; - - } - - setComponent( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - } - - getComponent( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - default: throw new Error( 'index is out of range: ' + index ); - - } - - } - - clone() { - - return new this.constructor( this.x, this.y ); - - } - - copy( v ) { - - this.x = v.x; - this.y = v.y; - - return this; - - } - - add( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - - return this; - - } - - addScalar( s ) { - - this.x += s; - this.y += s; - - return this; - - } - - addVectors( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - - return this; - - } - - addScaledVector( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - - return this; - - } - - sub( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - - return this; - - } - - subScalar( s ) { - - this.x -= s; - this.y -= s; - - return this; - - } - - subVectors( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - - return this; - - } - - multiply( v ) { - - this.x *= v.x; - this.y *= v.y; - - return this; - - } - - multiplyScalar( scalar ) { - - this.x *= scalar; - this.y *= scalar; - - return this; - - } - - divide( v ) { - - this.x /= v.x; - this.y /= v.y; - - return this; - - } - - divideScalar( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - } - - applyMatrix3( m ) { - - const x = this.x, y = this.y; - const e = m.elements; - - this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; - this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; - - return this; - - } - - min( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - - return this; - - } - - max( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - - return this; - - } - - clamp( min, max ) { - - // assumes min < max, componentwise - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - - return this; - - } - - clampScalar( minVal, maxVal ) { - - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - - return this; - - } - - clampLength( min, max ) { - - const length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - } - - floor() { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - - return this; - - } - - ceil() { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - - return this; - - } - - round() { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - - return this; - - } - - roundToZero() { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - - return this; - - } - - negate() { - - this.x = - this.x; - this.y = - this.y; - - return this; - - } - - dot( v ) { - - return this.x * v.x + this.y * v.y; - - } - - cross( v ) { - - return this.x * v.y - this.y * v.x; - - } - - lengthSq() { - - return this.x * this.x + this.y * this.y; - - } - - length() { - - return Math.sqrt( this.x * this.x + this.y * this.y ); - - } - - manhattanLength() { - - return Math.abs( this.x ) + Math.abs( this.y ); - - } - - normalize() { - - return this.divideScalar( this.length() || 1 ); - - } - - angle() { - - // computes the angle in radians with respect to the positive x-axis - - const angle = Math.atan2( - this.y, - this.x ) + Math.PI; - - return angle; - - } - - distanceTo( v ) { - - return Math.sqrt( this.distanceToSquared( v ) ); - - } - - distanceToSquared( v ) { - - const dx = this.x - v.x, dy = this.y - v.y; - return dx * dx + dy * dy; - - } - - manhattanDistanceTo( v ) { - - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); - - } - - setLength( length ) { - - return this.normalize().multiplyScalar( length ); - - } - - lerp( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - - return this; - - } - - lerpVectors( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - - return this; - - } - - equals( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) ); - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - - return array; - - } - - fromBufferAttribute( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - - return this; - - } - - rotateAround( center, angle ) { - - const c = Math.cos( angle ), s = Math.sin( angle ); - - const x = this.x - center.x; - const y = this.y - center.y; - - this.x = x * c - y * s + center.x; - this.y = x * s + y * c + center.y; - - return this; - - } - - random() { - - this.x = Math.random(); - this.y = Math.random(); - - return this; - - } - - } - - class Matrix3 { - - constructor() { - - Object.defineProperty( this, 'isMatrix3', { value: true } ); - - this.elements = [ - - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 - - ]; - - if ( arguments.length > 0 ) { - - console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); - - } - - } - - set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { - - const te = this.elements; - - te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; - te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; - te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; - - return this; - - } - - identity() { - - this.set( - - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 - - ); - - return this; - - } - - clone() { - - return new this.constructor().fromArray( this.elements ); - - } - - copy( m ) { - - const te = this.elements; - const me = m.elements; - - te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; - te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; - te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; - - return this; - - } - - extractBasis( xAxis, yAxis, zAxis ) { - - xAxis.setFromMatrix3Column( this, 0 ); - yAxis.setFromMatrix3Column( this, 1 ); - zAxis.setFromMatrix3Column( this, 2 ); - - return this; - - } - - setFromMatrix4( m ) { - - const me = m.elements; - - this.set( - - me[ 0 ], me[ 4 ], me[ 8 ], - me[ 1 ], me[ 5 ], me[ 9 ], - me[ 2 ], me[ 6 ], me[ 10 ] - - ); - - return this; - - } - - multiply( m ) { - - return this.multiplyMatrices( this, m ); - - } - - premultiply( m ) { - - return this.multiplyMatrices( m, this ); - - } - - multiplyMatrices( a, b ) { - - const ae = a.elements; - const be = b.elements; - const te = this.elements; - - const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; - const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; - const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; - - const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; - const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; - const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; - - te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; - te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; - te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; - - te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; - te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; - te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; - - te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; - te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; - te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; - - return this; - - } - - multiplyScalar( s ) { - - const te = this.elements; - - te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; - te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; - te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; - - return this; - - } - - determinant() { - - const te = this.elements; - - const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], - d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], - g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; - - return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; - - } - - getInverse( matrix, throwOnDegenerate ) { - - if ( throwOnDegenerate !== undefined ) { - - console.warn( "THREE.Matrix3: .getInverse() can no longer be configured to throw on degenerate." ); - - } - - const me = matrix.elements, - te = this.elements, - - n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], - n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ], - n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ], - - t11 = n33 * n22 - n32 * n23, - t12 = n32 * n13 - n33 * n12, - t13 = n23 * n12 - n22 * n13, - - det = n11 * t11 + n21 * t12 + n31 * t13; - - if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 ); - - const detInv = 1 / det; - - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; - te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; - - te[ 3 ] = t12 * detInv; - te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; - te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; - - te[ 6 ] = t13 * detInv; - te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; - te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; - - return this; - - } - - transpose() { - - let tmp; - const m = this.elements; - - tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; - tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; - tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; - - return this; - - } - - getNormalMatrix( matrix4 ) { - - return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose(); - - } - - transposeIntoArray( r ) { - - const m = this.elements; - - r[ 0 ] = m[ 0 ]; - r[ 1 ] = m[ 3 ]; - r[ 2 ] = m[ 6 ]; - r[ 3 ] = m[ 1 ]; - r[ 4 ] = m[ 4 ]; - r[ 5 ] = m[ 7 ]; - r[ 6 ] = m[ 2 ]; - r[ 7 ] = m[ 5 ]; - r[ 8 ] = m[ 8 ]; - - return this; - - } - - setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) { - - const c = Math.cos( rotation ); - const s = Math.sin( rotation ); - - this.set( - sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, - - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, - 0, 0, 1 - ); - - } - - scale( sx, sy ) { - - const te = this.elements; - - te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx; - te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy; - - return this; - - } - - rotate( theta ) { - - const c = Math.cos( theta ); - const s = Math.sin( theta ); - - const te = this.elements; - - const a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ]; - const a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ]; - - te[ 0 ] = c * a11 + s * a21; - te[ 3 ] = c * a12 + s * a22; - te[ 6 ] = c * a13 + s * a23; - - te[ 1 ] = - s * a11 + c * a21; - te[ 4 ] = - s * a12 + c * a22; - te[ 7 ] = - s * a13 + c * a23; - - return this; - - } - - translate( tx, ty ) { - - const te = this.elements; - - te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ]; - te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ]; - - return this; - - } - - equals( matrix ) { - - const te = this.elements; - const me = matrix.elements; - - for ( let i = 0; i < 9; i ++ ) { - - if ( te[ i ] !== me[ i ] ) return false; - - } - - return true; - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - for ( let i = 0; i < 9; i ++ ) { - - this.elements[ i ] = array[ i + offset ]; - - } - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - const te = this.elements; - - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - - array[ offset + 3 ] = te[ 3 ]; - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; - array[ offset + 8 ] = te[ 8 ]; - - return array; - - } - - } - - let _canvas; - - const ImageUtils = { - - getDataURL: function ( image ) { - - if ( /^data:/i.test( image.src ) ) { - - return image.src; - - } - - if ( typeof HTMLCanvasElement == 'undefined' ) { - - return image.src; - - } - - let canvas; - - if ( image instanceof HTMLCanvasElement ) { - - canvas = image; - - } else { - - if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - - _canvas.width = image.width; - _canvas.height = image.height; - - const context = _canvas.getContext( '2d' ); - - if ( image instanceof ImageData ) { - - context.putImageData( image, 0, 0 ); - - } else { - - context.drawImage( image, 0, 0, image.width, image.height ); - - } - - canvas = _canvas; - - } - - if ( canvas.width > 2048 || canvas.height > 2048 ) { - - return canvas.toDataURL( 'image/jpeg', 0.6 ); - - } else { - - return canvas.toDataURL( 'image/png' ); - - } - - } - - }; - - let textureId = 0; - - function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - - Object.defineProperty( this, 'id', { value: textureId ++ } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - - this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE; - this.mipmaps = []; - - this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING; - - this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping; - this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping; - - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - this.minFilter = minFilter !== undefined ? minFilter : LinearMipmapLinearFilter; - - this.anisotropy = anisotropy !== undefined ? anisotropy : 1; - - this.format = format !== undefined ? format : RGBAFormat; - this.internalFormat = null; - this.type = type !== undefined ? type : UnsignedByteType; - - this.offset = new Vector2( 0, 0 ); - this.repeat = new Vector2( 1, 1 ); - this.center = new Vector2( 0, 0 ); - this.rotation = 0; - - this.matrixAutoUpdate = true; - this.matrix = new Matrix3(); - - this.generateMipmaps = true; - this.premultiplyAlpha = false; - this.flipY = true; - this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - - // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. - // - // Also changing the encoding after already used by a Material will not automatically make the Material - // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. - this.encoding = encoding !== undefined ? encoding : LinearEncoding; - - this.version = 0; - this.onUpdate = null; - - } - - Texture.DEFAULT_IMAGE = undefined; - Texture.DEFAULT_MAPPING = UVMapping; - - Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: Texture, - - isTexture: true, - - updateMatrix: function () { - - this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.name = source.name; - - this.image = source.image; - this.mipmaps = source.mipmaps.slice( 0 ); - - this.mapping = source.mapping; - - this.wrapS = source.wrapS; - this.wrapT = source.wrapT; - - this.magFilter = source.magFilter; - this.minFilter = source.minFilter; - - this.anisotropy = source.anisotropy; - - this.format = source.format; - this.internalFormat = source.internalFormat; - this.type = source.type; - - this.offset.copy( source.offset ); - this.repeat.copy( source.repeat ); - this.center.copy( source.center ); - this.rotation = source.rotation; - - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrix.copy( source.matrix ); - - this.generateMipmaps = source.generateMipmaps; - this.premultiplyAlpha = source.premultiplyAlpha; - this.flipY = source.flipY; - this.unpackAlignment = source.unpackAlignment; - this.encoding = source.encoding; - - return this; - - }, - - toJSON: function ( meta ) { - - const isRootObject = ( meta === undefined || typeof meta === 'string' ); - - if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { - - return meta.textures[ this.uuid ]; - - } - - const output = { - - metadata: { - version: 4.5, - type: 'Texture', - generator: 'Texture.toJSON' - }, - - uuid: this.uuid, - name: this.name, - - mapping: this.mapping, - - repeat: [ this.repeat.x, this.repeat.y ], - offset: [ this.offset.x, this.offset.y ], - center: [ this.center.x, this.center.y ], - rotation: this.rotation, - - wrap: [ this.wrapS, this.wrapT ], - - format: this.format, - type: this.type, - encoding: this.encoding, - - minFilter: this.minFilter, - magFilter: this.magFilter, - anisotropy: this.anisotropy, - - flipY: this.flipY, - - premultiplyAlpha: this.premultiplyAlpha, - unpackAlignment: this.unpackAlignment - - }; - - if ( this.image !== undefined ) { - - // TODO: Move to THREE.Image - - const image = this.image; - - if ( image.uuid === undefined ) { - - image.uuid = MathUtils.generateUUID(); // UGH - - } - - if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { - - let url; - - if ( Array.isArray( image ) ) { - - // process array of images e.g. CubeTexture - - url = []; - - for ( let i = 0, l = image.length; i < l; i ++ ) { - - url.push( ImageUtils.getDataURL( image[ i ] ) ); - - } - - } else { - - // process single image - - url = ImageUtils.getDataURL( image ); - - } - - meta.images[ image.uuid ] = { - uuid: image.uuid, - url: url - }; - - } - - output.image = image.uuid; - - } - - if ( ! isRootObject ) { - - meta.textures[ this.uuid ] = output; - - } - - return output; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - }, - - transformUv: function ( uv ) { - - if ( this.mapping !== UVMapping ) return uv; - - uv.applyMatrix3( this.matrix ); - - if ( uv.x < 0 || uv.x > 1 ) { - - switch ( this.wrapS ) { - - case RepeatWrapping: - - uv.x = uv.x - Math.floor( uv.x ); - break; - - case ClampToEdgeWrapping: - - uv.x = uv.x < 0 ? 0 : 1; - break; - - case MirroredRepeatWrapping: - - if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { - - uv.x = Math.ceil( uv.x ) - uv.x; - - } else { - - uv.x = uv.x - Math.floor( uv.x ); - - } - - break; - - } - - } - - if ( uv.y < 0 || uv.y > 1 ) { - - switch ( this.wrapT ) { - - case RepeatWrapping: - - uv.y = uv.y - Math.floor( uv.y ); - break; - - case ClampToEdgeWrapping: - - uv.y = uv.y < 0 ? 0 : 1; - break; - - case MirroredRepeatWrapping: - - if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { - - uv.y = Math.ceil( uv.y ) - uv.y; - - } else { - - uv.y = uv.y - Math.floor( uv.y ); - - } - - break; - - } - - } - - if ( this.flipY ) { - - uv.y = 1 - uv.y; - - } - - return uv; - - } - - } ); - - Object.defineProperty( Texture.prototype, "needsUpdate", { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - class Vector4 { - - constructor( x = 0, y = 0, z = 0, w = 1 ) { - - Object.defineProperty( this, 'isVector4', { value: true } ); - - this.x = x; - this.y = y; - this.z = z; - this.w = w; - - } - - get width() { - - return this.z; - - } - - set width( value ) { - - this.z = value; - - } - - get height() { - - return this.w; - - } - - set height( value ) { - - this.w = value; - - } - - set( x, y, z, w ) { - - this.x = x; - this.y = y; - this.z = z; - this.w = w; - - return this; - - } - - setScalar( scalar ) { - - this.x = scalar; - this.y = scalar; - this.z = scalar; - this.w = scalar; - - return this; - - } - - setX( x ) { - - this.x = x; - - return this; - - } - - setY( y ) { - - this.y = y; - - return this; - - } - - setZ( z ) { - - this.z = z; - - return this; - - } - - setW( w ) { - - this.w = w; - - return this; - - } - - setComponent( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - case 3: this.w = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - } - - getComponent( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - case 3: return this.w; - default: throw new Error( 'index is out of range: ' + index ); - - } - - } - - clone() { - - return new this.constructor( this.x, this.y, this.z, this.w ); - - } - - copy( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.w = ( v.w !== undefined ) ? v.w : 1; - - return this; - - } - - add( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - this.z += v.z; - this.w += v.w; - - return this; - - } - - addScalar( s ) { - - this.x += s; - this.y += s; - this.z += s; - this.w += s; - - return this; - - } - - addVectors( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - this.w = a.w + b.w; - - return this; - - } - - addScaledVector( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - this.w += v.w * s; - - return this; - - } - - sub( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - this.w -= v.w; - - return this; - - } - - subScalar( s ) { - - this.x -= s; - this.y -= s; - this.z -= s; - this.w -= s; - - return this; - - } - - subVectors( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - this.w = a.w - b.w; - - return this; - - } - - multiplyScalar( scalar ) { - - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - this.w *= scalar; - - return this; - - } - - applyMatrix4( m ) { - - const x = this.x, y = this.y, z = this.z, w = this.w; - const e = m.elements; - - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; - this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; - - return this; - - } - - divideScalar( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - } - - setAxisAngleFromQuaternion( q ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm - - // q is assumed to be normalized - - this.w = 2 * Math.acos( q.w ); - - const s = Math.sqrt( 1 - q.w * q.w ); - - if ( s < 0.0001 ) { - - this.x = 1; - this.y = 0; - this.z = 0; - - } else { - - this.x = q.x / s; - this.y = q.y / s; - this.z = q.z / s; - - } - - return this; - - } - - setAxisAngleFromRotationMatrix( m ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - let angle, x, y, z; // variables for result - const epsilon = 0.01, // margin to allow for rounding errors - epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees - - te = m.elements, - - m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], - m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], - m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - - if ( ( Math.abs( m12 - m21 ) < epsilon ) && - ( Math.abs( m13 - m31 ) < epsilon ) && - ( Math.abs( m23 - m32 ) < epsilon ) ) { - - // singularity found - // first check for identity matrix which must have +1 for all terms - // in leading diagonal and zero in other terms - - if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && - ( Math.abs( m13 + m31 ) < epsilon2 ) && - ( Math.abs( m23 + m32 ) < epsilon2 ) && - ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { - - // this singularity is identity matrix so angle = 0 - - this.set( 1, 0, 0, 0 ); - - return this; // zero angle, arbitrary axis - - } - - // otherwise this singularity is angle = 180 - - angle = Math.PI; - - const xx = ( m11 + 1 ) / 2; - const yy = ( m22 + 1 ) / 2; - const zz = ( m33 + 1 ) / 2; - const xy = ( m12 + m21 ) / 4; - const xz = ( m13 + m31 ) / 4; - const yz = ( m23 + m32 ) / 4; - - if ( ( xx > yy ) && ( xx > zz ) ) { - - // m11 is the largest diagonal term - - if ( xx < epsilon ) { - - x = 0; - y = 0.707106781; - z = 0.707106781; - - } else { - - x = Math.sqrt( xx ); - y = xy / x; - z = xz / x; - - } - - } else if ( yy > zz ) { - - // m22 is the largest diagonal term - - if ( yy < epsilon ) { - - x = 0.707106781; - y = 0; - z = 0.707106781; - - } else { - - y = Math.sqrt( yy ); - x = xy / y; - z = yz / y; - - } - - } else { - - // m33 is the largest diagonal term so base result on this - - if ( zz < epsilon ) { - - x = 0.707106781; - y = 0.707106781; - z = 0; - - } else { - - z = Math.sqrt( zz ); - x = xz / z; - y = yz / z; - - } - - } - - this.set( x, y, z, angle ); - - return this; // return 180 deg rotation - - } - - // as we have reached here there are no singularities so we can handle normally - - let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + - ( m13 - m31 ) * ( m13 - m31 ) + - ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize - - if ( Math.abs( s ) < 0.001 ) s = 1; - - // prevent divide by zero, should not happen if matrix is orthogonal and should be - // caught by singularity test above, but I've left it in just in case - - this.x = ( m32 - m23 ) / s; - this.y = ( m13 - m31 ) / s; - this.z = ( m21 - m12 ) / s; - this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); - - return this; - - } - - min( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - this.w = Math.min( this.w, v.w ); - - return this; - - } - - max( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - this.w = Math.max( this.w, v.w ); - - return this; - - } - - clamp( min, max ) { - - // assumes min < max, componentwise - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - this.z = Math.max( min.z, Math.min( max.z, this.z ) ); - this.w = Math.max( min.w, Math.min( max.w, this.w ) ); - - return this; - - } - - clampScalar( minVal, maxVal ) { - - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); - - return this; - - } - - clampLength( min, max ) { - - const length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - } - - floor() { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - this.w = Math.floor( this.w ); - - return this; - - } - - ceil() { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - this.w = Math.ceil( this.w ); - - return this; - - } - - round() { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - this.w = Math.round( this.w ); - - return this; - - } - - roundToZero() { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); - this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); - - return this; - - } - - negate() { - - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - this.w = - this.w; - - return this; - - } - - dot( v ) { - - return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; - - } - - lengthSq() { - - return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; - - } - - length() { - - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); - - } - - manhattanLength() { - - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); - - } - - normalize() { - - return this.divideScalar( this.length() || 1 ); - - } - - setLength( length ) { - - return this.normalize().multiplyScalar( length ); - - } - - lerp( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - this.w += ( v.w - this.w ) * alpha; - - return this; - - } - - lerpVectors( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - this.z = v1.z + ( v2.z - v1.z ) * alpha; - this.w = v1.w + ( v2.w - v1.w ) * alpha; - - return this; - - } - - equals( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - this.w = array[ offset + 3 ]; - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - array[ offset + 3 ] = this.w; - - return array; - - } - - fromBufferAttribute( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - this.w = attribute.getW( index ); - - return this; - - } - - random() { - - this.x = Math.random(); - this.y = Math.random(); - this.z = Math.random(); - this.w = Math.random(); - - return this; - - } - - } - - /* - In options, we can specify: - * Texture parameters for an auto-generated target texture - * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers - */ - function WebGLRenderTarget( width, height, options ) { - - this.width = width; - this.height = height; - - this.scissor = new Vector4( 0, 0, width, height ); - this.scissorTest = false; - - this.viewport = new Vector4( 0, 0, width, height ); - - options = options || {}; - - this.texture = new Texture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); - - this.texture.image = {}; - this.texture.image.width = width; - this.texture.image.height = height; - - this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; - this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; - - this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; - this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; - this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; - - } - - WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: WebGLRenderTarget, - - isWebGLRenderTarget: true, - - setSize: function ( width, height ) { - - if ( this.width !== width || this.height !== height ) { - - this.width = width; - this.height = height; - - this.texture.image.width = width; - this.texture.image.height = height; - - this.dispose(); - - } - - this.viewport.set( 0, 0, width, height ); - this.scissor.set( 0, 0, width, height ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.width = source.width; - this.height = source.height; - - this.viewport.copy( source.viewport ); - - this.texture = source.texture.clone(); - - this.depthBuffer = source.depthBuffer; - this.stencilBuffer = source.stencilBuffer; - this.depthTexture = source.depthTexture; - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - function WebGLMultisampleRenderTarget( width, height, options ) { - - WebGLRenderTarget.call( this, width, height, options ); - - this.samples = 4; - - } - - WebGLMultisampleRenderTarget.prototype = Object.assign( Object.create( WebGLRenderTarget.prototype ), { - - constructor: WebGLMultisampleRenderTarget, - - isWebGLMultisampleRenderTarget: true, - - copy: function ( source ) { - - WebGLRenderTarget.prototype.copy.call( this, source ); - - this.samples = source.samples; - - return this; - - } - - } ); - - class Quaternion { - - constructor( x = 0, y = 0, z = 0, w = 1 ) { - - Object.defineProperty( this, 'isQuaternion', { value: true } ); - - this._x = x; - this._y = y; - this._z = z; - this._w = w; - - } - - static slerp( qa, qb, qm, t ) { - - return qm.copy( qa ).slerp( qb, t ); - - } - - static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { - - // fuzz-free, array-based Quaternion SLERP operation - - let x0 = src0[ srcOffset0 + 0 ], - y0 = src0[ srcOffset0 + 1 ], - z0 = src0[ srcOffset0 + 2 ], - w0 = src0[ srcOffset0 + 3 ]; - - const x1 = src1[ srcOffset1 + 0 ], - y1 = src1[ srcOffset1 + 1 ], - z1 = src1[ srcOffset1 + 2 ], - w1 = src1[ srcOffset1 + 3 ]; - - if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { - - let s = 1 - t; - const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, - dir = ( cos >= 0 ? 1 : - 1 ), - sqrSin = 1 - cos * cos; - - // Skip the Slerp for tiny steps to avoid numeric problems: - if ( sqrSin > Number.EPSILON ) { - - const sin = Math.sqrt( sqrSin ), - len = Math.atan2( sin, cos * dir ); - - s = Math.sin( s * len ) / sin; - t = Math.sin( t * len ) / sin; - - } - - const tDir = t * dir; - - x0 = x0 * s + x1 * tDir; - y0 = y0 * s + y1 * tDir; - z0 = z0 * s + z1 * tDir; - w0 = w0 * s + w1 * tDir; - - // Normalize in case we just did a lerp: - if ( s === 1 - t ) { - - const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); - - x0 *= f; - y0 *= f; - z0 *= f; - w0 *= f; - - } - - } - - dst[ dstOffset ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; - - } - - static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { - - const x0 = src0[ srcOffset0 ]; - const y0 = src0[ srcOffset0 + 1 ]; - const z0 = src0[ srcOffset0 + 2 ]; - const w0 = src0[ srcOffset0 + 3 ]; - - const x1 = src1[ srcOffset1 ]; - const y1 = src1[ srcOffset1 + 1 ]; - const z1 = src1[ srcOffset1 + 2 ]; - const w1 = src1[ srcOffset1 + 3 ]; - - dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; - dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; - dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; - dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; - - return dst; - - } - - get x() { - - return this._x; - - } - - set x( value ) { - - this._x = value; - this._onChangeCallback(); - - } - - get y() { - - return this._y; - - } - - set y( value ) { - - this._y = value; - this._onChangeCallback(); - - } - - get z() { - - return this._z; - - } - - set z( value ) { - - this._z = value; - this._onChangeCallback(); - - } - - get w() { - - return this._w; - - } - - set w( value ) { - - this._w = value; - this._onChangeCallback(); - - } - - set( x, y, z, w ) { - - this._x = x; - this._y = y; - this._z = z; - this._w = w; - - this._onChangeCallback(); - - return this; - - } - - clone() { - - return new this.constructor( this._x, this._y, this._z, this._w ); - - } - - copy( quaternion ) { - - this._x = quaternion.x; - this._y = quaternion.y; - this._z = quaternion.z; - this._w = quaternion.w; - - this._onChangeCallback(); - - return this; - - } - - setFromEuler( euler, update ) { - - if ( ! ( euler && euler.isEuler ) ) { - - throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - - } - - const x = euler._x, y = euler._y, z = euler._z, order = euler._order; - - // http://www.mathworks.com/matlabcentral/fileexchange/ - // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ - // content/SpinCalc.m - - const cos = Math.cos; - const sin = Math.sin; - - const c1 = cos( x / 2 ); - const c2 = cos( y / 2 ); - const c3 = cos( z / 2 ); - - const s1 = sin( x / 2 ); - const s2 = sin( y / 2 ); - const s3 = sin( z / 2 ); - - switch ( order ) { - - case 'XYZ': - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - break; - - case 'YXZ': - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - break; - - case 'ZXY': - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - break; - - case 'ZYX': - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - break; - - case 'YZX': - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - break; - - case 'XZY': - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - break; - - default: - console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order ); - - } - - if ( update !== false ) this._onChangeCallback(); - - return this; - - } - - setFromAxisAngle( axis, angle ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - - // assumes axis is normalized - - const halfAngle = angle / 2, s = Math.sin( halfAngle ); - - this._x = axis.x * s; - this._y = axis.y * s; - this._z = axis.z * s; - this._w = Math.cos( halfAngle ); - - this._onChangeCallback(); - - return this; - - } - - setFromRotationMatrix( m ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - const te = m.elements, - - m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], - m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], - m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], - - trace = m11 + m22 + m33; - - if ( trace > 0 ) { - - const s = 0.5 / Math.sqrt( trace + 1.0 ); - - this._w = 0.25 / s; - this._x = ( m32 - m23 ) * s; - this._y = ( m13 - m31 ) * s; - this._z = ( m21 - m12 ) * s; - - } else if ( m11 > m22 && m11 > m33 ) { - - const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); - - this._w = ( m32 - m23 ) / s; - this._x = 0.25 * s; - this._y = ( m12 + m21 ) / s; - this._z = ( m13 + m31 ) / s; - - } else if ( m22 > m33 ) { - - const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); - - this._w = ( m13 - m31 ) / s; - this._x = ( m12 + m21 ) / s; - this._y = 0.25 * s; - this._z = ( m23 + m32 ) / s; - - } else { - - const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); - - this._w = ( m21 - m12 ) / s; - this._x = ( m13 + m31 ) / s; - this._y = ( m23 + m32 ) / s; - this._z = 0.25 * s; - - } - - this._onChangeCallback(); - - return this; - - } - - setFromUnitVectors( vFrom, vTo ) { - - // assumes direction vectors vFrom and vTo are normalized - - const EPS = 0.000001; - - let r = vFrom.dot( vTo ) + 1; - - if ( r < EPS ) { - - r = 0; - - if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { - - this._x = - vFrom.y; - this._y = vFrom.x; - this._z = 0; - this._w = r; - - } else { - - this._x = 0; - this._y = - vFrom.z; - this._z = vFrom.y; - this._w = r; - - } - - } else { - - // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 - - this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; - this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; - this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; - this._w = r; - - } - - return this.normalize(); - - } - - angleTo( q ) { - - return 2 * Math.acos( Math.abs( MathUtils.clamp( this.dot( q ), - 1, 1 ) ) ); - - } - - rotateTowards( q, step ) { - - const angle = this.angleTo( q ); - - if ( angle === 0 ) return this; - - const t = Math.min( 1, step / angle ); - - this.slerp( q, t ); - - return this; - - } - - identity() { - - return this.set( 0, 0, 0, 1 ); - - } - - inverse() { - - // quaternion is assumed to have unit length - - return this.conjugate(); - - } - - conjugate() { - - this._x *= - 1; - this._y *= - 1; - this._z *= - 1; - - this._onChangeCallback(); - - return this; - - } - - dot( v ) { - - return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; - - } - - lengthSq() { - - return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; - - } - - length() { - - return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); - - } - - normalize() { - - let l = this.length(); - - if ( l === 0 ) { - - this._x = 0; - this._y = 0; - this._z = 0; - this._w = 1; - - } else { - - l = 1 / l; - - this._x = this._x * l; - this._y = this._y * l; - this._z = this._z * l; - this._w = this._w * l; - - } - - this._onChangeCallback(); - - return this; - - } - - multiply( q, p ) { - - if ( p !== undefined ) { - - console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); - return this.multiplyQuaternions( q, p ); - - } - - return this.multiplyQuaternions( this, q ); - - } - - premultiply( q ) { - - return this.multiplyQuaternions( q, this ); - - } - - multiplyQuaternions( a, b ) { - - // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm - - const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; - const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; - - this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; - this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; - this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; - this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; - - this._onChangeCallback(); - - return this; - - } - - slerp( qb, t ) { - - if ( t === 0 ) return this; - if ( t === 1 ) return this.copy( qb ); - - const x = this._x, y = this._y, z = this._z, w = this._w; - - // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ - - let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; - - if ( cosHalfTheta < 0 ) { - - this._w = - qb._w; - this._x = - qb._x; - this._y = - qb._y; - this._z = - qb._z; - - cosHalfTheta = - cosHalfTheta; - - } else { - - this.copy( qb ); - - } - - if ( cosHalfTheta >= 1.0 ) { - - this._w = w; - this._x = x; - this._y = y; - this._z = z; - - return this; - - } - - const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; - - if ( sqrSinHalfTheta <= Number.EPSILON ) { - - const s = 1 - t; - this._w = s * w + t * this._w; - this._x = s * x + t * this._x; - this._y = s * y + t * this._y; - this._z = s * z + t * this._z; - - this.normalize(); - this._onChangeCallback(); - - return this; - - } - - const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); - const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); - const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, - ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; - - this._w = ( w * ratioA + this._w * ratioB ); - this._x = ( x * ratioA + this._x * ratioB ); - this._y = ( y * ratioA + this._y * ratioB ); - this._z = ( z * ratioA + this._z * ratioB ); - - this._onChangeCallback(); - - return this; - - } - - equals( quaternion ) { - - return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this._x = array[ offset ]; - this._y = array[ offset + 1 ]; - this._z = array[ offset + 2 ]; - this._w = array[ offset + 3 ]; - - this._onChangeCallback(); - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._w; - - return array; - - } - - fromBufferAttribute( attribute, index ) { - - this._x = attribute.getX( index ); - this._y = attribute.getY( index ); - this._z = attribute.getZ( index ); - this._w = attribute.getW( index ); - - return this; - - } - - _onChange( callback ) { - - this._onChangeCallback = callback; - - return this; - - } - - _onChangeCallback() {} - - } - - class Vector3 { - - constructor( x = 0, y = 0, z = 0 ) { - - Object.defineProperty( this, 'isVector3', { value: true } ); - - this.x = x; - this.y = y; - this.z = z; - - } - - set( x, y, z ) { - - if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) - - this.x = x; - this.y = y; - this.z = z; - - return this; - - } - - setScalar( scalar ) { - - this.x = scalar; - this.y = scalar; - this.z = scalar; - - return this; - - } - - setX( x ) { - - this.x = x; - - return this; - - } - - setY( y ) { - - this.y = y; - - return this; - - } - - setZ( z ) { - - this.z = z; - - return this; - - } - - setComponent( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - } - - getComponent( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - default: throw new Error( 'index is out of range: ' + index ); - - } - - } - - clone() { - - return new this.constructor( this.x, this.y, this.z ); - - } - - copy( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; - - return this; - - } - - add( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - this.z += v.z; - - return this; - - } - - addScalar( s ) { - - this.x += s; - this.y += s; - this.z += s; - - return this; - - } - - addVectors( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - - return this; - - } - - addScaledVector( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - - return this; - - } - - sub( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - - return this; - - } - - subScalar( s ) { - - this.x -= s; - this.y -= s; - this.z -= s; - - return this; - - } - - subVectors( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - - return this; - - } - - multiply( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); - return this.multiplyVectors( v, w ); - - } - - this.x *= v.x; - this.y *= v.y; - this.z *= v.z; - - return this; - - } - - multiplyScalar( scalar ) { - - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - - return this; - - } - - multiplyVectors( a, b ) { - - this.x = a.x * b.x; - this.y = a.y * b.y; - this.z = a.z * b.z; - - return this; - - } - - applyEuler( euler ) { - - if ( ! ( euler && euler.isEuler ) ) { - - console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - - } - - return this.applyQuaternion( _quaternion.setFromEuler( euler ) ); - - } - - applyAxisAngle( axis, angle ) { - - return this.applyQuaternion( _quaternion.setFromAxisAngle( axis, angle ) ); - - } - - applyMatrix3( m ) { - - const x = this.x, y = this.y, z = this.z; - const e = m.elements; - - this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; - this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; - this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; - - return this; - - } - - applyNormalMatrix( m ) { - - return this.applyMatrix3( m ).normalize(); - - } - - applyMatrix4( m ) { - - const x = this.x, y = this.y, z = this.z; - const e = m.elements; - - const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); - - this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; - this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; - this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; - - return this; - - } - - applyQuaternion( q ) { - - const x = this.x, y = this.y, z = this.z; - const qx = q.x, qy = q.y, qz = q.z, qw = q.w; - - // calculate quat * vector - - const ix = qw * x + qy * z - qz * y; - const iy = qw * y + qz * x - qx * z; - const iz = qw * z + qx * y - qy * x; - const iw = - qx * x - qy * y - qz * z; - - // calculate result * inverse quat - - this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; - this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; - this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; - - return this; - - } - - project( camera ) { - - return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); - - } - - unproject( camera ) { - - return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); - - } - - transformDirection( m ) { - - // input: THREE.Matrix4 affine matrix - // vector interpreted as a direction - - const x = this.x, y = this.y, z = this.z; - const e = m.elements; - - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; - - return this.normalize(); - - } - - divide( v ) { - - this.x /= v.x; - this.y /= v.y; - this.z /= v.z; - - return this; - - } - - divideScalar( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - } - - min( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - - return this; - - } - - max( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - - return this; - - } - - clamp( min, max ) { - - // assumes min < max, componentwise - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - this.z = Math.max( min.z, Math.min( max.z, this.z ) ); - - return this; - - } - - clampScalar( minVal, maxVal ) { - - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - - return this; - - } - - clampLength( min, max ) { - - const length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - } - - floor() { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - - return this; - - } - - ceil() { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - - return this; - - } - - round() { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - - return this; - - } - - roundToZero() { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); - - return this; - - } - - negate() { - - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - - return this; - - } - - dot( v ) { - - return this.x * v.x + this.y * v.y + this.z * v.z; - - } - - // TODO lengthSquared? - - lengthSq() { - - return this.x * this.x + this.y * this.y + this.z * this.z; - - } - - length() { - - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); - - } - - manhattanLength() { - - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); - - } - - normalize() { - - return this.divideScalar( this.length() || 1 ); - - } - - setLength( length ) { - - return this.normalize().multiplyScalar( length ); - - } - - lerp( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - - return this; - - } - - lerpVectors( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - this.z = v1.z + ( v2.z - v1.z ) * alpha; - - return this; - - } - - cross( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); - return this.crossVectors( v, w ); - - } - - return this.crossVectors( this, v ); - - } - - crossVectors( a, b ) { - - const ax = a.x, ay = a.y, az = a.z; - const bx = b.x, by = b.y, bz = b.z; - - this.x = ay * bz - az * by; - this.y = az * bx - ax * bz; - this.z = ax * by - ay * bx; - - return this; - - } - - projectOnVector( v ) { - - const denominator = v.lengthSq(); - - if ( denominator === 0 ) return this.set( 0, 0, 0 ); - - const scalar = v.dot( this ) / denominator; - - return this.copy( v ).multiplyScalar( scalar ); - - } - - projectOnPlane( planeNormal ) { - - _vector.copy( this ).projectOnVector( planeNormal ); - - return this.sub( _vector ); - - } - - reflect( normal ) { - - // reflect incident vector off plane orthogonal to normal - // normal is assumed to have unit length - - return this.sub( _vector.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); - - } - - angleTo( v ) { - - const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); - - if ( denominator === 0 ) return Math.PI / 2; - - const theta = this.dot( v ) / denominator; - - // clamp, to handle numerical problems - - return Math.acos( MathUtils.clamp( theta, - 1, 1 ) ); - - } - - distanceTo( v ) { - - return Math.sqrt( this.distanceToSquared( v ) ); - - } - - distanceToSquared( v ) { - - const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; - - return dx * dx + dy * dy + dz * dz; - - } - - manhattanDistanceTo( v ) { - - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); - - } - - setFromSpherical( s ) { - - return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); - - } - - setFromSphericalCoords( radius, phi, theta ) { - - const sinPhiRadius = Math.sin( phi ) * radius; - - this.x = sinPhiRadius * Math.sin( theta ); - this.y = Math.cos( phi ) * radius; - this.z = sinPhiRadius * Math.cos( theta ); - - return this; - - } - - setFromCylindrical( c ) { - - return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); - - } - - setFromCylindricalCoords( radius, theta, y ) { - - this.x = radius * Math.sin( theta ); - this.y = y; - this.z = radius * Math.cos( theta ); - - return this; - - } - - setFromMatrixPosition( m ) { - - const e = m.elements; - - this.x = e[ 12 ]; - this.y = e[ 13 ]; - this.z = e[ 14 ]; - - return this; - - } - - setFromMatrixScale( m ) { - - const sx = this.setFromMatrixColumn( m, 0 ).length(); - const sy = this.setFromMatrixColumn( m, 1 ).length(); - const sz = this.setFromMatrixColumn( m, 2 ).length(); - - this.x = sx; - this.y = sy; - this.z = sz; - - return this; - - } - - setFromMatrixColumn( m, index ) { - - return this.fromArray( m.elements, index * 4 ); - - } - - setFromMatrix3Column( m, index ) { - - return this.fromArray( m.elements, index * 3 ); - - } - - equals( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - - return array; - - } - - fromBufferAttribute( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - - return this; - - } - - random() { - - this.x = Math.random(); - this.y = Math.random(); - this.z = Math.random(); - - return this; - - } - - } - - const _vector = new Vector3(); - const _quaternion = new Quaternion(); - - class Box3 { - - constructor( min, max ) { - - Object.defineProperty( this, 'isBox3', { value: true } ); - - this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); - - } - - set( min, max ) { - - this.min.copy( min ); - this.max.copy( max ); - - return this; - - } - - setFromArray( array ) { - - let minX = + Infinity; - let minY = + Infinity; - let minZ = + Infinity; - - let maxX = - Infinity; - let maxY = - Infinity; - let maxZ = - Infinity; - - for ( let i = 0, l = array.length; i < l; i += 3 ) { - - const x = array[ i ]; - const y = array[ i + 1 ]; - const z = array[ i + 2 ]; - - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; - - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; - - } - - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); - - return this; - - } - - setFromBufferAttribute( attribute ) { - - let minX = + Infinity; - let minY = + Infinity; - let minZ = + Infinity; - - let maxX = - Infinity; - let maxY = - Infinity; - let maxZ = - Infinity; - - for ( let i = 0, l = attribute.count; i < l; i ++ ) { - - const x = attribute.getX( i ); - const y = attribute.getY( i ); - const z = attribute.getZ( i ); - - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; - - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; - - } - - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); - - return this; - - } - - setFromPoints( points ) { - - this.makeEmpty(); - - for ( let i = 0, il = points.length; i < il; i ++ ) { - - this.expandByPoint( points[ i ] ); - - } - - return this; - - } - - setFromCenterAndSize( center, size ) { - - const halfSize = _vector$1.copy( size ).multiplyScalar( 0.5 ); - - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); - - return this; - - } - - setFromObject( object ) { - - this.makeEmpty(); - - return this.expandByObject( object ); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( box ) { - - this.min.copy( box.min ); - this.max.copy( box.max ); - - return this; - - } - - makeEmpty() { - - this.min.x = this.min.y = this.min.z = + Infinity; - this.max.x = this.max.y = this.max.z = - Infinity; - - return this; - - } - - isEmpty() { - - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); - - } - - getCenter( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getCenter() target is now required' ); - target = new Vector3(); - - } - - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); - - } - - getSize( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getSize() target is now required' ); - target = new Vector3(); - - } - - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); - - } - - expandByPoint( point ) { - - this.min.min( point ); - this.max.max( point ); - - return this; - - } - - expandByVector( vector ) { - - this.min.sub( vector ); - this.max.add( vector ); - - return this; - - } - - expandByScalar( scalar ) { - - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); - - return this; - - } - - expandByObject( object ) { - - // Computes the world-axis-aligned bounding box of an object (including its children), - // accounting for both the object's, and children's, world transforms - - object.updateWorldMatrix( false, false ); - - const geometry = object.geometry; - - if ( geometry !== undefined ) { - - if ( geometry.boundingBox === null ) { - - geometry.computeBoundingBox(); - - } - - _box.copy( geometry.boundingBox ); - _box.applyMatrix4( object.matrixWorld ); - - this.union( _box ); - - } - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - this.expandByObject( children[ i ] ); - - } - - return this; - - } - - containsPoint( point ) { - - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y || - point.z < this.min.z || point.z > this.max.z ? false : true; - - } - - containsBox( box ) { - - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y && - this.min.z <= box.min.z && box.max.z <= this.max.z; - - } - - getParameter( point, target ) { - - // This can potentially have a divide by zero if the box - // has a size dimension of 0. - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getParameter() target is now required' ); - target = new Vector3(); - - } - - return target.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ), - ( point.z - this.min.z ) / ( this.max.z - this.min.z ) - ); - - } - - intersectsBox( box ) { - - // using 6 splitting planes to rule out intersections. - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y || - box.max.z < this.min.z || box.min.z > this.max.z ? false : true; - - } - - intersectsSphere( sphere ) { - - // Find the point on the AABB closest to the sphere center. - this.clampPoint( sphere.center, _vector$1 ); - - // If that point is inside the sphere, the AABB and sphere intersect. - return _vector$1.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); - - } - - intersectsPlane( plane ) { - - // We compute the minimum and maximum dot product values. If those values - // are on the same side (back or front) of the plane, then there is no intersection. - - let min, max; - - if ( plane.normal.x > 0 ) { - - min = plane.normal.x * this.min.x; - max = plane.normal.x * this.max.x; - - } else { - - min = plane.normal.x * this.max.x; - max = plane.normal.x * this.min.x; - - } - - if ( plane.normal.y > 0 ) { - - min += plane.normal.y * this.min.y; - max += plane.normal.y * this.max.y; - - } else { - - min += plane.normal.y * this.max.y; - max += plane.normal.y * this.min.y; - - } - - if ( plane.normal.z > 0 ) { - - min += plane.normal.z * this.min.z; - max += plane.normal.z * this.max.z; - - } else { - - min += plane.normal.z * this.max.z; - max += plane.normal.z * this.min.z; - - } - - return ( min <= - plane.constant && max >= - plane.constant ); - - } - - intersectsTriangle( triangle ) { - - if ( this.isEmpty() ) { - - return false; - - } - - // compute box center and extents - this.getCenter( _center ); - _extents.subVectors( this.max, _center ); - - // translate triangle to aabb origin - _v0.subVectors( triangle.a, _center ); - _v1.subVectors( triangle.b, _center ); - _v2.subVectors( triangle.c, _center ); - - // compute edge vectors for triangle - _f0.subVectors( _v1, _v0 ); - _f1.subVectors( _v2, _v1 ); - _f2.subVectors( _v0, _v2 ); - - // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb - // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation - // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) - let axes = [ - 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, - _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, - - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 - ]; - if ( ! satForAxes( axes, _v0, _v1, _v2, _extents ) ) { - - return false; - - } - - // test 3 face normals from the aabb - axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; - if ( ! satForAxes( axes, _v0, _v1, _v2, _extents ) ) { - - return false; - - } - - // finally testing the face normal of the triangle - // use already existing triangle edge vectors here - _triangleNormal.crossVectors( _f0, _f1 ); - axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; - - return satForAxes( axes, _v0, _v1, _v2, _extents ); - - } - - clampPoint( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .clampPoint() target is now required' ); - target = new Vector3(); - - } - - return target.copy( point ).clamp( this.min, this.max ); - - } - - distanceToPoint( point ) { - - const clampedPoint = _vector$1.copy( point ).clamp( this.min, this.max ); - - return clampedPoint.sub( point ).length(); - - } - - getBoundingSphere( target ) { - - if ( target === undefined ) { - - console.error( 'THREE.Box3: .getBoundingSphere() target is now required' ); - //target = new Sphere(); // removed to avoid cyclic dependency - - } - - this.getCenter( target.center ); - - target.radius = this.getSize( _vector$1 ).length() * 0.5; - - return target; - - } - - intersect( box ) { - - this.min.max( box.min ); - this.max.min( box.max ); - - // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. - if ( this.isEmpty() ) this.makeEmpty(); - - return this; - - } - - union( box ) { - - this.min.min( box.min ); - this.max.max( box.max ); - - return this; - - } - - applyMatrix4( matrix ) { - - // transform of empty box is an empty box. - if ( this.isEmpty() ) return this; - - // NOTE: I am using a binary pattern to specify all 2^3 combinations below - _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 - _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 - _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 - _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 - _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 - _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 - _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 - _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 - - this.setFromPoints( _points ); - - return this; - - } - - translate( offset ) { - - this.min.add( offset ); - this.max.add( offset ); - - return this; - - } - - equals( box ) { - - return box.min.equals( this.min ) && box.max.equals( this.max ); - - } - - } - - function satForAxes( axes, v0, v1, v2, extents ) { - - for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { - - _testAxis.fromArray( axes, i ); - // project the aabb onto the seperating axis - const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); - // project all 3 vertices of the triangle onto the seperating axis - const p0 = v0.dot( _testAxis ); - const p1 = v1.dot( _testAxis ); - const p2 = v2.dot( _testAxis ); - // actual test, basically see if either of the most extreme of the triangle points intersects r - if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { - - // points of the projected triangle are outside the projected half-length of the aabb - // the axis is seperating and we can exit - return false; - - } - - } - - return true; - - } - - const _points = [ - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3() - ]; - - const _vector$1 = new Vector3(); - - const _box = new Box3(); - - // triangle centered vertices - - const _v0 = new Vector3(); - const _v1 = new Vector3(); - const _v2 = new Vector3(); - - // triangle edge vectors - - const _f0 = new Vector3(); - const _f1 = new Vector3(); - const _f2 = new Vector3(); - - const _center = new Vector3(); - const _extents = new Vector3(); - const _triangleNormal = new Vector3(); - const _testAxis = new Vector3(); - - const _box$1 = new Box3(); - - class Sphere { - - constructor( center, radius ) { - - this.center = ( center !== undefined ) ? center : new Vector3(); - this.radius = ( radius !== undefined ) ? radius : - 1; - - } - - set( center, radius ) { - - this.center.copy( center ); - this.radius = radius; - - return this; - - } - - setFromPoints( points, optionalCenter ) { - - const center = this.center; - - if ( optionalCenter !== undefined ) { - - center.copy( optionalCenter ); - - } else { - - _box$1.setFromPoints( points ).getCenter( center ); - - } - - let maxRadiusSq = 0; - - for ( let i = 0, il = points.length; i < il; i ++ ) { - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); - - } - - this.radius = Math.sqrt( maxRadiusSq ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( sphere ) { - - this.center.copy( sphere.center ); - this.radius = sphere.radius; - - return this; - - } - - isEmpty() { - - return ( this.radius < 0 ); - - } - - makeEmpty() { - - this.center.set( 0, 0, 0 ); - this.radius = - 1; - - return this; - - } - - containsPoint( point ) { - - return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); - - } - - distanceToPoint( point ) { - - return ( point.distanceTo( this.center ) - this.radius ); - - } - - intersectsSphere( sphere ) { - - const radiusSum = this.radius + sphere.radius; - - return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); - - } - - intersectsBox( box ) { - - return box.intersectsSphere( this ); - - } - - intersectsPlane( plane ) { - - return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; - - } - - clampPoint( point, target ) { - - const deltaLengthSq = this.center.distanceToSquared( point ); - - if ( target === undefined ) { - - console.warn( 'THREE.Sphere: .clampPoint() target is now required' ); - target = new Vector3(); - - } - - target.copy( point ); - - if ( deltaLengthSq > ( this.radius * this.radius ) ) { - - target.sub( this.center ).normalize(); - target.multiplyScalar( this.radius ).add( this.center ); - - } - - return target; - - } - - getBoundingBox( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' ); - target = new Box3(); - - } - - if ( this.isEmpty() ) { - - // Empty sphere produces empty bounding box - target.makeEmpty(); - return target; - - } - - target.set( this.center, this.center ); - target.expandByScalar( this.radius ); - - return target; - - } - - applyMatrix4( matrix ) { - - this.center.applyMatrix4( matrix ); - this.radius = this.radius * matrix.getMaxScaleOnAxis(); - - return this; - - } - - translate( offset ) { - - this.center.add( offset ); - - return this; - - } - - equals( sphere ) { - - return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); - - } - - } - - const _vector$2 = new Vector3(); - const _segCenter = new Vector3(); - const _segDir = new Vector3(); - const _diff = new Vector3(); - - const _edge1 = new Vector3(); - const _edge2 = new Vector3(); - const _normal = new Vector3(); - - class Ray { - - constructor( origin, direction ) { - - this.origin = ( origin !== undefined ) ? origin : new Vector3(); - this.direction = ( direction !== undefined ) ? direction : new Vector3( 0, 0, - 1 ); - - } - - set( origin, direction ) { - - this.origin.copy( origin ); - this.direction.copy( direction ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( ray ) { - - this.origin.copy( ray.origin ); - this.direction.copy( ray.direction ); - - return this; - - } - - at( t, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Ray: .at() target is now required' ); - target = new Vector3(); - - } - - return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); - - } - - lookAt( v ) { - - this.direction.copy( v ).sub( this.origin ).normalize(); - - return this; - - } - - recast( t ) { - - this.origin.copy( this.at( t, _vector$2 ) ); - - return this; - - } - - closestPointToPoint( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } - - target.subVectors( point, this.origin ); - - const directionDistance = target.dot( this.direction ); - - if ( directionDistance < 0 ) { - - return target.copy( this.origin ); - - } - - return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - - } - - distanceToPoint( point ) { - - return Math.sqrt( this.distanceSqToPoint( point ) ); - - } - - distanceSqToPoint( point ) { - - const directionDistance = _vector$2.subVectors( point, this.origin ).dot( this.direction ); - - // point behind the ray - - if ( directionDistance < 0 ) { - - return this.origin.distanceToSquared( point ); - - } - - _vector$2.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - - return _vector$2.distanceToSquared( point ); - - } - - distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { - - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h - // It returns the min distance between the ray and the segment - // defined by v0 and v1 - // It can also set two optional targets : - // - The closest point on the ray - // - The closest point on the segment - - _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); - _segDir.copy( v1 ).sub( v0 ).normalize(); - _diff.copy( this.origin ).sub( _segCenter ); - - const segExtent = v0.distanceTo( v1 ) * 0.5; - const a01 = - this.direction.dot( _segDir ); - const b0 = _diff.dot( this.direction ); - const b1 = - _diff.dot( _segDir ); - const c = _diff.lengthSq(); - const det = Math.abs( 1 - a01 * a01 ); - let s0, s1, sqrDist, extDet; - - if ( det > 0 ) { - - // The ray and segment are not parallel. - - s0 = a01 * b1 - b0; - s1 = a01 * b0 - b1; - extDet = segExtent * det; - - if ( s0 >= 0 ) { - - if ( s1 >= - extDet ) { - - if ( s1 <= extDet ) { - - // region 0 - // Minimum at interior points of ray and segment. - - const invDet = 1 / det; - s0 *= invDet; - s1 *= invDet; - sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; - - } else { - - // region 1 - - s1 = segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } else { - - // region 5 - - s1 = - segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } else { - - if ( s1 <= - extDet ) { - - // region 4 - - s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } else if ( s1 <= extDet ) { - - // region 3 - - s0 = 0; - s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = s1 * ( s1 + 2 * b1 ) + c; - - } else { - - // region 2 - - s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } - - } else { - - // Ray and segment are parallel. - - s1 = ( a01 > 0 ) ? - segExtent : segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - if ( optionalPointOnRay ) { - - optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); - - } - - if ( optionalPointOnSegment ) { - - optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter ); - - } - - return sqrDist; - - } - - intersectSphere( sphere, target ) { - - _vector$2.subVectors( sphere.center, this.origin ); - const tca = _vector$2.dot( this.direction ); - const d2 = _vector$2.dot( _vector$2 ) - tca * tca; - const radius2 = sphere.radius * sphere.radius; - - if ( d2 > radius2 ) return null; - - const thc = Math.sqrt( radius2 - d2 ); - - // t0 = first intersect point - entrance on front of sphere - const t0 = tca - thc; - - // t1 = second intersect point - exit point on back of sphere - const t1 = tca + thc; - - // test to see if both t0 and t1 are behind the ray - if so, return null - if ( t0 < 0 && t1 < 0 ) return null; - - // test to see if t0 is behind the ray: - // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, - // in order to always return an intersect point that is in front of the ray. - if ( t0 < 0 ) return this.at( t1, target ); - - // else t0 is in front of the ray, so return the first collision point scaled by t0 - return this.at( t0, target ); - - } - - intersectsSphere( sphere ) { - - return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); - - } - - distanceToPlane( plane ) { - - const denominator = plane.normal.dot( this.direction ); - - if ( denominator === 0 ) { - - // line is coplanar, return origin - if ( plane.distanceToPoint( this.origin ) === 0 ) { - - return 0; - - } - - // Null is preferable to undefined since undefined means.... it is undefined - - return null; - - } - - const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; - - // Return if the ray never intersects the plane - - return t >= 0 ? t : null; - - } - - intersectPlane( plane, target ) { - - const t = this.distanceToPlane( plane ); - - if ( t === null ) { - - return null; - - } - - return this.at( t, target ); - - } - - intersectsPlane( plane ) { - - // check if the ray lies on the plane first - - const distToPoint = plane.distanceToPoint( this.origin ); - - if ( distToPoint === 0 ) { - - return true; - - } - - const denominator = plane.normal.dot( this.direction ); - - if ( denominator * distToPoint < 0 ) { - - return true; - - } - - // ray origin is behind the plane (and is pointing behind it) - - return false; - - } - - intersectBox( box, target ) { - - let tmin, tmax, tymin, tymax, tzmin, tzmax; - - const invdirx = 1 / this.direction.x, - invdiry = 1 / this.direction.y, - invdirz = 1 / this.direction.z; - - const origin = this.origin; - - if ( invdirx >= 0 ) { - - tmin = ( box.min.x - origin.x ) * invdirx; - tmax = ( box.max.x - origin.x ) * invdirx; - - } else { - - tmin = ( box.max.x - origin.x ) * invdirx; - tmax = ( box.min.x - origin.x ) * invdirx; - - } - - if ( invdiry >= 0 ) { - - tymin = ( box.min.y - origin.y ) * invdiry; - tymax = ( box.max.y - origin.y ) * invdiry; - - } else { - - tymin = ( box.max.y - origin.y ) * invdiry; - tymax = ( box.min.y - origin.y ) * invdiry; - - } - - if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; - - // These lines also handle the case where tmin or tmax is NaN - // (result of 0 * Infinity). x !== x returns true if x is NaN - - if ( tymin > tmin || tmin !== tmin ) tmin = tymin; - - if ( tymax < tmax || tmax !== tmax ) tmax = tymax; - - if ( invdirz >= 0 ) { - - tzmin = ( box.min.z - origin.z ) * invdirz; - tzmax = ( box.max.z - origin.z ) * invdirz; - - } else { - - tzmin = ( box.max.z - origin.z ) * invdirz; - tzmax = ( box.min.z - origin.z ) * invdirz; - - } - - if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; - - if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; - - if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; - - //return point closest to the ray (positive side) - - if ( tmax < 0 ) return null; - - return this.at( tmin >= 0 ? tmin : tmax, target ); - - } - - intersectsBox( box ) { - - return this.intersectBox( box, _vector$2 ) !== null; - - } - - intersectTriangle( a, b, c, backfaceCulling, target ) { - - // Compute the offset origin, edges, and normal. - - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h - - _edge1.subVectors( b, a ); - _edge2.subVectors( c, a ); - _normal.crossVectors( _edge1, _edge2 ); - - // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, - // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by - // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) - // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) - // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) - let DdN = this.direction.dot( _normal ); - let sign; - - if ( DdN > 0 ) { - - if ( backfaceCulling ) return null; - sign = 1; - - } else if ( DdN < 0 ) { - - sign = - 1; - DdN = - DdN; - - } else { - - return null; - - } - - _diff.subVectors( this.origin, a ); - const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); - - // b1 < 0, no intersection - if ( DdQxE2 < 0 ) { - - return null; - - } - - const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); - - // b2 < 0, no intersection - if ( DdE1xQ < 0 ) { - - return null; - - } - - // b1+b2 > 1, no intersection - if ( DdQxE2 + DdE1xQ > DdN ) { - - return null; - - } - - // Line intersects triangle, check if ray does. - const QdN = - sign * _diff.dot( _normal ); - - // t < 0, no intersection - if ( QdN < 0 ) { - - return null; - - } - - // Ray intersects triangle. - return this.at( QdN / DdN, target ); - - } - - applyMatrix4( matrix4 ) { - - this.origin.applyMatrix4( matrix4 ); - this.direction.transformDirection( matrix4 ); - - return this; - - } - - equals( ray ) { - - return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); - - } - - } - - class Matrix4 { - - constructor() { - - Object.defineProperty( this, 'isMatrix4', { value: true } ); - - this.elements = [ - - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ]; - - if ( arguments.length > 0 ) { - - console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); - - } - - } - - set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { - - const te = this.elements; - - te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; - te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; - te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; - te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; - - return this; - - } - - identity() { - - this.set( - - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - clone() { - - return new Matrix4().fromArray( this.elements ); - - } - - copy( m ) { - - const te = this.elements; - const me = m.elements; - - te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; - te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; - te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; - te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; - - return this; - - } - - copyPosition( m ) { - - const te = this.elements, me = m.elements; - - te[ 12 ] = me[ 12 ]; - te[ 13 ] = me[ 13 ]; - te[ 14 ] = me[ 14 ]; - - return this; - - } - - extractBasis( xAxis, yAxis, zAxis ) { - - xAxis.setFromMatrixColumn( this, 0 ); - yAxis.setFromMatrixColumn( this, 1 ); - zAxis.setFromMatrixColumn( this, 2 ); - - return this; - - } - - makeBasis( xAxis, yAxis, zAxis ) { - - this.set( - xAxis.x, yAxis.x, zAxis.x, 0, - xAxis.y, yAxis.y, zAxis.y, 0, - xAxis.z, yAxis.z, zAxis.z, 0, - 0, 0, 0, 1 - ); - - return this; - - } - - extractRotation( m ) { - - // this method does not support reflection matrices - - const te = this.elements; - const me = m.elements; - - const scaleX = 1 / _v1$1.setFromMatrixColumn( m, 0 ).length(); - const scaleY = 1 / _v1$1.setFromMatrixColumn( m, 1 ).length(); - const scaleZ = 1 / _v1$1.setFromMatrixColumn( m, 2 ).length(); - - te[ 0 ] = me[ 0 ] * scaleX; - te[ 1 ] = me[ 1 ] * scaleX; - te[ 2 ] = me[ 2 ] * scaleX; - te[ 3 ] = 0; - - te[ 4 ] = me[ 4 ] * scaleY; - te[ 5 ] = me[ 5 ] * scaleY; - te[ 6 ] = me[ 6 ] * scaleY; - te[ 7 ] = 0; - - te[ 8 ] = me[ 8 ] * scaleZ; - te[ 9 ] = me[ 9 ] * scaleZ; - te[ 10 ] = me[ 10 ] * scaleZ; - te[ 11 ] = 0; - - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; - - return this; - - } - - makeRotationFromEuler( euler ) { - - if ( ! ( euler && euler.isEuler ) ) { - - console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); - - } - - const te = this.elements; - - const x = euler.x, y = euler.y, z = euler.z; - const a = Math.cos( x ), b = Math.sin( x ); - const c = Math.cos( y ), d = Math.sin( y ); - const e = Math.cos( z ), f = Math.sin( z ); - - if ( euler.order === 'XYZ' ) { - - const ae = a * e, af = a * f, be = b * e, bf = b * f; - - te[ 0 ] = c * e; - te[ 4 ] = - c * f; - te[ 8 ] = d; - - te[ 1 ] = af + be * d; - te[ 5 ] = ae - bf * d; - te[ 9 ] = - b * c; - - te[ 2 ] = bf - ae * d; - te[ 6 ] = be + af * d; - te[ 10 ] = a * c; - - } else if ( euler.order === 'YXZ' ) { - - const ce = c * e, cf = c * f, de = d * e, df = d * f; - - te[ 0 ] = ce + df * b; - te[ 4 ] = de * b - cf; - te[ 8 ] = a * d; - - te[ 1 ] = a * f; - te[ 5 ] = a * e; - te[ 9 ] = - b; - - te[ 2 ] = cf * b - de; - te[ 6 ] = df + ce * b; - te[ 10 ] = a * c; - - } else if ( euler.order === 'ZXY' ) { - - const ce = c * e, cf = c * f, de = d * e, df = d * f; - - te[ 0 ] = ce - df * b; - te[ 4 ] = - a * f; - te[ 8 ] = de + cf * b; - - te[ 1 ] = cf + de * b; - te[ 5 ] = a * e; - te[ 9 ] = df - ce * b; - - te[ 2 ] = - a * d; - te[ 6 ] = b; - te[ 10 ] = a * c; - - } else if ( euler.order === 'ZYX' ) { - - const ae = a * e, af = a * f, be = b * e, bf = b * f; - - te[ 0 ] = c * e; - te[ 4 ] = be * d - af; - te[ 8 ] = ae * d + bf; - - te[ 1 ] = c * f; - te[ 5 ] = bf * d + ae; - te[ 9 ] = af * d - be; - - te[ 2 ] = - d; - te[ 6 ] = b * c; - te[ 10 ] = a * c; - - } else if ( euler.order === 'YZX' ) { - - const ac = a * c, ad = a * d, bc = b * c, bd = b * d; - - te[ 0 ] = c * e; - te[ 4 ] = bd - ac * f; - te[ 8 ] = bc * f + ad; - - te[ 1 ] = f; - te[ 5 ] = a * e; - te[ 9 ] = - b * e; - - te[ 2 ] = - d * e; - te[ 6 ] = ad * f + bc; - te[ 10 ] = ac - bd * f; - - } else if ( euler.order === 'XZY' ) { - - const ac = a * c, ad = a * d, bc = b * c, bd = b * d; - - te[ 0 ] = c * e; - te[ 4 ] = - f; - te[ 8 ] = d * e; - - te[ 1 ] = ac * f + bd; - te[ 5 ] = a * e; - te[ 9 ] = ad * f - bc; - - te[ 2 ] = bc * f - ad; - te[ 6 ] = b * e; - te[ 10 ] = bd * f + ac; - - } - - // bottom row - te[ 3 ] = 0; - te[ 7 ] = 0; - te[ 11 ] = 0; - - // last column - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; - - return this; - - } - - makeRotationFromQuaternion( q ) { - - return this.compose( _zero, q, _one ); - - } - - lookAt( eye, target, up ) { - - const te = this.elements; - - _z.subVectors( eye, target ); - - if ( _z.lengthSq() === 0 ) { - - // eye and target are in the same position - - _z.z = 1; - - } - - _z.normalize(); - _x.crossVectors( up, _z ); - - if ( _x.lengthSq() === 0 ) { - - // up and z are parallel - - if ( Math.abs( up.z ) === 1 ) { - - _z.x += 0.0001; - - } else { - - _z.z += 0.0001; - - } - - _z.normalize(); - _x.crossVectors( up, _z ); - - } - - _x.normalize(); - _y.crossVectors( _z, _x ); - - te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x; - te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y; - te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z; - - return this; - - } - - multiply( m, n ) { - - if ( n !== undefined ) { - - console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); - return this.multiplyMatrices( m, n ); - - } - - return this.multiplyMatrices( this, m ); - - } - - premultiply( m ) { - - return this.multiplyMatrices( m, this ); - - } - - multiplyMatrices( a, b ) { - - const ae = a.elements; - const be = b.elements; - const te = this.elements; - - const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; - const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; - const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; - const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; - - const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; - const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; - const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; - const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; - - te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; - te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; - te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; - te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; - - te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; - te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; - te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; - te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; - - te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; - te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; - te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; - te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; - - te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; - te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; - te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; - te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; - - return this; - - } - - multiplyScalar( s ) { - - const te = this.elements; - - te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; - te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; - te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; - te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; - - return this; - - } - - determinant() { - - const te = this.elements; - - const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; - const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; - const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; - const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; - - //TODO: make this more efficient - //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) - - return ( - n41 * ( - + n14 * n23 * n32 - - n13 * n24 * n32 - - n14 * n22 * n33 - + n12 * n24 * n33 - + n13 * n22 * n34 - - n12 * n23 * n34 - ) + - n42 * ( - + n11 * n23 * n34 - - n11 * n24 * n33 - + n14 * n21 * n33 - - n13 * n21 * n34 - + n13 * n24 * n31 - - n14 * n23 * n31 - ) + - n43 * ( - + n11 * n24 * n32 - - n11 * n22 * n34 - - n14 * n21 * n32 - + n12 * n21 * n34 - + n14 * n22 * n31 - - n12 * n24 * n31 - ) + - n44 * ( - - n13 * n22 * n31 - - n11 * n23 * n32 - + n11 * n22 * n33 - + n13 * n21 * n32 - - n12 * n21 * n33 - + n12 * n23 * n31 - ) - - ); - - } - - transpose() { - - const te = this.elements; - let tmp; - - tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; - tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; - tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; - - tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; - tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; - tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; - - return this; - - } - - setPosition( x, y, z ) { - - const te = this.elements; - - if ( x.isVector3 ) { - - te[ 12 ] = x.x; - te[ 13 ] = x.y; - te[ 14 ] = x.z; - - } else { - - te[ 12 ] = x; - te[ 13 ] = y; - te[ 14 ] = z; - - } - - return this; - - } - - getInverse( m, throwOnDegenerate ) { - - if ( throwOnDegenerate !== undefined ) { - - console.warn( "THREE.Matrix4: .getInverse() can no longer be configured to throw on degenerate." ); - - } - - // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm - const te = this.elements, - me = m.elements, - - n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ], - n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ], - n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ], - n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ], - - t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, - t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, - t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, - t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; - - const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; - - if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); - - const detInv = 1 / det; - - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; - te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; - te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; - - te[ 4 ] = t12 * detInv; - te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; - te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; - te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; - - te[ 8 ] = t13 * detInv; - te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; - te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; - te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; - - te[ 12 ] = t14 * detInv; - te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; - te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; - te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; - - return this; - - } - - scale( v ) { - - const te = this.elements; - const x = v.x, y = v.y, z = v.z; - - te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; - te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; - te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; - te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; - - return this; - - } - - getMaxScaleOnAxis() { - - const te = this.elements; - - const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; - const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; - const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; - - return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); - - } - - makeTranslation( x, y, z ) { - - this.set( - - 1, 0, 0, x, - 0, 1, 0, y, - 0, 0, 1, z, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeRotationX( theta ) { - - const c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - 1, 0, 0, 0, - 0, c, - s, 0, - 0, s, c, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeRotationY( theta ) { - - const c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - c, 0, s, 0, - 0, 1, 0, 0, - - s, 0, c, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeRotationZ( theta ) { - - const c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - c, - s, 0, 0, - s, c, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeRotationAxis( axis, angle ) { - - // Based on http://www.gamedev.net/reference/articles/article1199.asp - - const c = Math.cos( angle ); - const s = Math.sin( angle ); - const t = 1 - c; - const x = axis.x, y = axis.y, z = axis.z; - const tx = t * x, ty = t * y; - - this.set( - - tx * x + c, tx * y - s * z, tx * z + s * y, 0, - tx * y + s * z, ty * y + c, ty * z - s * x, 0, - tx * z - s * y, ty * z + s * x, t * z * z + c, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeScale( x, y, z ) { - - this.set( - - x, 0, 0, 0, - 0, y, 0, 0, - 0, 0, z, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - makeShear( x, y, z ) { - - this.set( - - 1, y, z, 0, - x, 1, z, 0, - x, y, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - } - - compose( position, quaternion, scale ) { - - const te = this.elements; - - const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; - const x2 = x + x, y2 = y + y, z2 = z + z; - const xx = x * x2, xy = x * y2, xz = x * z2; - const yy = y * y2, yz = y * z2, zz = z * z2; - const wx = w * x2, wy = w * y2, wz = w * z2; - - const sx = scale.x, sy = scale.y, sz = scale.z; - - te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; - te[ 1 ] = ( xy + wz ) * sx; - te[ 2 ] = ( xz - wy ) * sx; - te[ 3 ] = 0; - - te[ 4 ] = ( xy - wz ) * sy; - te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; - te[ 6 ] = ( yz + wx ) * sy; - te[ 7 ] = 0; - - te[ 8 ] = ( xz + wy ) * sz; - te[ 9 ] = ( yz - wx ) * sz; - te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; - te[ 11 ] = 0; - - te[ 12 ] = position.x; - te[ 13 ] = position.y; - te[ 14 ] = position.z; - te[ 15 ] = 1; - - return this; - - } - - decompose( position, quaternion, scale ) { - - const te = this.elements; - - let sx = _v1$1.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); - const sy = _v1$1.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); - const sz = _v1$1.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); - - // if determine is negative, we need to invert one scale - const det = this.determinant(); - if ( det < 0 ) sx = - sx; - - position.x = te[ 12 ]; - position.y = te[ 13 ]; - position.z = te[ 14 ]; - - // scale the rotation part - _m1.copy( this ); - - const invSX = 1 / sx; - const invSY = 1 / sy; - const invSZ = 1 / sz; - - _m1.elements[ 0 ] *= invSX; - _m1.elements[ 1 ] *= invSX; - _m1.elements[ 2 ] *= invSX; - - _m1.elements[ 4 ] *= invSY; - _m1.elements[ 5 ] *= invSY; - _m1.elements[ 6 ] *= invSY; - - _m1.elements[ 8 ] *= invSZ; - _m1.elements[ 9 ] *= invSZ; - _m1.elements[ 10 ] *= invSZ; - - quaternion.setFromRotationMatrix( _m1 ); - - scale.x = sx; - scale.y = sy; - scale.z = sz; - - return this; - - } - - makePerspective( left, right, top, bottom, near, far ) { - - if ( far === undefined ) { - - console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' ); - - } - - const te = this.elements; - const x = 2 * near / ( right - left ); - const y = 2 * near / ( top - bottom ); - - const a = ( right + left ) / ( right - left ); - const b = ( top + bottom ) / ( top - bottom ); - const c = - ( far + near ) / ( far - near ); - const d = - 2 * far * near / ( far - near ); - - te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; - te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; - - return this; - - } - - makeOrthographic( left, right, top, bottom, near, far ) { - - const te = this.elements; - const w = 1.0 / ( right - left ); - const h = 1.0 / ( top - bottom ); - const p = 1.0 / ( far - near ); - - const x = ( right + left ) * w; - const y = ( top + bottom ) * h; - const z = ( far + near ) * p; - - te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; - te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; - - return this; - - } - - equals( matrix ) { - - const te = this.elements; - const me = matrix.elements; - - for ( let i = 0; i < 16; i ++ ) { - - if ( te[ i ] !== me[ i ] ) return false; - - } - - return true; - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - for ( let i = 0; i < 16; i ++ ) { - - this.elements[ i ] = array[ i + offset ]; - - } - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - const te = this.elements; - - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - array[ offset + 3 ] = te[ 3 ]; - - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; - - array[ offset + 8 ] = te[ 8 ]; - array[ offset + 9 ] = te[ 9 ]; - array[ offset + 10 ] = te[ 10 ]; - array[ offset + 11 ] = te[ 11 ]; - - array[ offset + 12 ] = te[ 12 ]; - array[ offset + 13 ] = te[ 13 ]; - array[ offset + 14 ] = te[ 14 ]; - array[ offset + 15 ] = te[ 15 ]; - - return array; - - } - - } - - const _v1$1 = new Vector3(); - const _m1 = new Matrix4(); - const _zero = new Vector3( 0, 0, 0 ); - const _one = new Vector3( 1, 1, 1 ); - const _x = new Vector3(); - const _y = new Vector3(); - const _z = new Vector3(); - - class Euler { - - constructor( x = 0, y = 0, z = 0, order = Euler.DefaultOrder ) { - - Object.defineProperty( this, 'isEuler', { value: true } ); - - this._x = x; - this._y = y; - this._z = z; - this._order = order; - - } - - get x() { - - return this._x; - - } - - set x( value ) { - - this._x = value; - this._onChangeCallback(); - - } - - get y() { - - return this._y; - - } - - set y( value ) { - - this._y = value; - this._onChangeCallback(); - - } - - get z() { - - return this._z; - - } - - set z( value ) { - - this._z = value; - this._onChangeCallback(); - - } - - get order() { - - return this._order; - - } - - set order( value ) { - - this._order = value; - this._onChangeCallback(); - - } - - set( x, y, z, order ) { - - this._x = x; - this._y = y; - this._z = z; - this._order = order || this._order; - - this._onChangeCallback(); - - return this; - - } - - clone() { - - return new this.constructor( this._x, this._y, this._z, this._order ); - - } - - copy( euler ) { - - this._x = euler._x; - this._y = euler._y; - this._z = euler._z; - this._order = euler._order; - - this._onChangeCallback(); - - return this; - - } - - setFromRotationMatrix( m, order, update ) { - - const clamp = MathUtils.clamp; - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - const te = m.elements; - const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; - const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; - const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - - order = order || this._order; - - switch ( order ) { - - case 'XYZ': - - this._y = Math.asin( clamp( m13, - 1, 1 ) ); - - if ( Math.abs( m13 ) < 0.9999999 ) { - - this._x = Math.atan2( - m23, m33 ); - this._z = Math.atan2( - m12, m11 ); - - } else { - - this._x = Math.atan2( m32, m22 ); - this._z = 0; - - } - - break; - - case 'YXZ': - - this._x = Math.asin( - clamp( m23, - 1, 1 ) ); - - if ( Math.abs( m23 ) < 0.9999999 ) { - - this._y = Math.atan2( m13, m33 ); - this._z = Math.atan2( m21, m22 ); - - } else { - - this._y = Math.atan2( - m31, m11 ); - this._z = 0; - - } - - break; - - case 'ZXY': - - this._x = Math.asin( clamp( m32, - 1, 1 ) ); - - if ( Math.abs( m32 ) < 0.9999999 ) { - - this._y = Math.atan2( - m31, m33 ); - this._z = Math.atan2( - m12, m22 ); - - } else { - - this._y = 0; - this._z = Math.atan2( m21, m11 ); - - } - - break; - - case 'ZYX': - - this._y = Math.asin( - clamp( m31, - 1, 1 ) ); - - if ( Math.abs( m31 ) < 0.9999999 ) { - - this._x = Math.atan2( m32, m33 ); - this._z = Math.atan2( m21, m11 ); - - } else { - - this._x = 0; - this._z = Math.atan2( - m12, m22 ); - - } - - break; - - case 'YZX': - - this._z = Math.asin( clamp( m21, - 1, 1 ) ); - - if ( Math.abs( m21 ) < 0.9999999 ) { - - this._x = Math.atan2( - m23, m22 ); - this._y = Math.atan2( - m31, m11 ); - - } else { - - this._x = 0; - this._y = Math.atan2( m13, m33 ); - - } - - break; - - case 'XZY': - - this._z = Math.asin( - clamp( m12, - 1, 1 ) ); - - if ( Math.abs( m12 ) < 0.9999999 ) { - - this._x = Math.atan2( m32, m22 ); - this._y = Math.atan2( m13, m11 ); - - } else { - - this._x = Math.atan2( - m23, m33 ); - this._y = 0; - - } - - break; - - default: - - console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order ); - - } - - this._order = order; - - if ( update !== false ) this._onChangeCallback(); - - return this; - - } - - setFromQuaternion( q, order, update ) { - - _matrix.makeRotationFromQuaternion( q ); - - return this.setFromRotationMatrix( _matrix, order, update ); - - } - - setFromVector3( v, order ) { - - return this.set( v.x, v.y, v.z, order || this._order ); - - } - - reorder( newOrder ) { - - // WARNING: this discards revolution information -bhouston - - _quaternion$1.setFromEuler( this ); - - return this.setFromQuaternion( _quaternion$1, newOrder ); - - } - - equals( euler ) { - - return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); - - } - - fromArray( array ) { - - this._x = array[ 0 ]; - this._y = array[ 1 ]; - this._z = array[ 2 ]; - if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; - - this._onChangeCallback(); - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._order; - - return array; - - } - - toVector3( optionalResult ) { - - if ( optionalResult ) { - - return optionalResult.set( this._x, this._y, this._z ); - - } else { - - return new Vector3( this._x, this._y, this._z ); - - } - - } - - _onChange( callback ) { - - this._onChangeCallback = callback; - - return this; - - } - - _onChangeCallback() {} - - } - - Euler.DefaultOrder = 'XYZ'; - Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; - - const _matrix = new Matrix4(); - const _quaternion$1 = new Quaternion(); - - class Layers { - - constructor() { - - this.mask = 1 | 0; - - } - - set( channel ) { - - this.mask = 1 << channel | 0; - - } - - enable( channel ) { - - this.mask |= 1 << channel | 0; - - } - - enableAll() { - - this.mask = 0xffffffff | 0; - - } - - toggle( channel ) { - - this.mask ^= 1 << channel | 0; - - } - - disable( channel ) { - - this.mask &= ~ ( 1 << channel | 0 ); - - } - - disableAll() { - - this.mask = 0; - - } - - test( layers ) { - - return ( this.mask & layers.mask ) !== 0; - - } - - } - - let _object3DId = 0; - - const _v1$2 = new Vector3(); - const _q1 = new Quaternion(); - const _m1$1 = new Matrix4(); - const _target = new Vector3(); - - const _position = new Vector3(); - const _scale = new Vector3(); - const _quaternion$2 = new Quaternion(); - - const _xAxis = new Vector3( 1, 0, 0 ); - const _yAxis = new Vector3( 0, 1, 0 ); - const _zAxis = new Vector3( 0, 0, 1 ); - - const _addedEvent = { type: 'added' }; - const _removedEvent = { type: 'removed' }; - - function Object3D() { - - Object.defineProperty( this, 'id', { value: _object3DId ++ } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - this.type = 'Object3D'; - - this.parent = null; - this.children = []; - - this.up = Object3D.DefaultUp.clone(); - - const position = new Vector3(); - const rotation = new Euler(); - const quaternion = new Quaternion(); - const scale = new Vector3( 1, 1, 1 ); - - function onRotationChange() { - - quaternion.setFromEuler( rotation, false ); - - } - - function onQuaternionChange() { - - rotation.setFromQuaternion( quaternion, undefined, false ); - - } - - rotation._onChange( onRotationChange ); - quaternion._onChange( onQuaternionChange ); - - Object.defineProperties( this, { - position: { - configurable: true, - enumerable: true, - value: position - }, - rotation: { - configurable: true, - enumerable: true, - value: rotation - }, - quaternion: { - configurable: true, - enumerable: true, - value: quaternion - }, - scale: { - configurable: true, - enumerable: true, - value: scale - }, - modelViewMatrix: { - value: new Matrix4() - }, - normalMatrix: { - value: new Matrix3() - } - } ); - - this.matrix = new Matrix4(); - this.matrixWorld = new Matrix4(); - - this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; - this.matrixWorldNeedsUpdate = false; - - this.layers = new Layers(); - this.visible = true; - - this.castShadow = false; - this.receiveShadow = false; - - this.frustumCulled = true; - this.renderOrder = 0; - - this.userData = {}; - - } - - Object3D.DefaultUp = new Vector3( 0, 1, 0 ); - Object3D.DefaultMatrixAutoUpdate = true; - - Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: Object3D, - - isObject3D: true, - - onBeforeRender: function () {}, - onAfterRender: function () {}, - - applyMatrix4: function ( matrix ) { - - if ( this.matrixAutoUpdate ) this.updateMatrix(); - - this.matrix.premultiply( matrix ); - - this.matrix.decompose( this.position, this.quaternion, this.scale ); - - }, - - applyQuaternion: function ( q ) { - - this.quaternion.premultiply( q ); - - return this; - - }, - - setRotationFromAxisAngle: function ( axis, angle ) { - - // assumes axis is normalized - - this.quaternion.setFromAxisAngle( axis, angle ); - - }, - - setRotationFromEuler: function ( euler ) { - - this.quaternion.setFromEuler( euler, true ); - - }, - - setRotationFromMatrix: function ( m ) { - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - this.quaternion.setFromRotationMatrix( m ); - - }, - - setRotationFromQuaternion: function ( q ) { - - // assumes q is normalized - - this.quaternion.copy( q ); - - }, - - rotateOnAxis: function ( axis, angle ) { - - // rotate object on axis in object space - // axis is assumed to be normalized - - _q1.setFromAxisAngle( axis, angle ); - - this.quaternion.multiply( _q1 ); - - return this; - - }, - - rotateOnWorldAxis: function ( axis, angle ) { - - // rotate object on axis in world space - // axis is assumed to be normalized - // method assumes no rotated parent - - _q1.setFromAxisAngle( axis, angle ); - - this.quaternion.premultiply( _q1 ); - - return this; - - }, - - rotateX: function ( angle ) { - - return this.rotateOnAxis( _xAxis, angle ); - - }, - - rotateY: function ( angle ) { - - return this.rotateOnAxis( _yAxis, angle ); - - }, - - rotateZ: function ( angle ) { - - return this.rotateOnAxis( _zAxis, angle ); - - }, - - translateOnAxis: function ( axis, distance ) { - - // translate object by distance along axis in object space - // axis is assumed to be normalized - - _v1$2.copy( axis ).applyQuaternion( this.quaternion ); - - this.position.add( _v1$2.multiplyScalar( distance ) ); - - return this; - - }, - - translateX: function ( distance ) { - - return this.translateOnAxis( _xAxis, distance ); - - }, - - translateY: function ( distance ) { - - return this.translateOnAxis( _yAxis, distance ); - - }, - - translateZ: function ( distance ) { - - return this.translateOnAxis( _zAxis, distance ); - - }, - - localToWorld: function ( vector ) { - - return vector.applyMatrix4( this.matrixWorld ); - - }, - - worldToLocal: function ( vector ) { - - return vector.applyMatrix4( _m1$1.getInverse( this.matrixWorld ) ); - - }, - - lookAt: function ( x, y, z ) { - - // This method does not support objects having non-uniformly-scaled parent(s) - - if ( x.isVector3 ) { - - _target.copy( x ); - - } else { - - _target.set( x, y, z ); - - } - - const parent = this.parent; - - this.updateWorldMatrix( true, false ); - - _position.setFromMatrixPosition( this.matrixWorld ); - - if ( this.isCamera || this.isLight ) { - - _m1$1.lookAt( _position, _target, this.up ); - - } else { - - _m1$1.lookAt( _target, _position, this.up ); - - } - - this.quaternion.setFromRotationMatrix( _m1$1 ); - - if ( parent ) { - - _m1$1.extractRotation( parent.matrixWorld ); - _q1.setFromRotationMatrix( _m1$1 ); - this.quaternion.premultiply( _q1.inverse() ); - - } - - }, - - add: function ( object ) { - - if ( arguments.length > 1 ) { - - for ( let i = 0; i < arguments.length; i ++ ) { - - this.add( arguments[ i ] ); - - } - - return this; - - } - - if ( object === this ) { - - console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); - return this; - - } - - if ( ( object && object.isObject3D ) ) { - - if ( object.parent !== null ) { - - object.parent.remove( object ); - - } - - object.parent = this; - this.children.push( object ); - - object.dispatchEvent( _addedEvent ); - - } else { - - console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); - - } - - return this; - - }, - - remove: function ( object ) { - - if ( arguments.length > 1 ) { - - for ( let i = 0; i < arguments.length; i ++ ) { - - this.remove( arguments[ i ] ); - - } - - return this; - - } - - const index = this.children.indexOf( object ); - - if ( index !== - 1 ) { - - object.parent = null; - this.children.splice( index, 1 ); - - object.dispatchEvent( _removedEvent ); - - } - - return this; - - }, - - attach: function ( object ) { - - // adds object as a child of this, while maintaining the object's world transform - - this.updateWorldMatrix( true, false ); - - _m1$1.getInverse( this.matrixWorld ); - - if ( object.parent !== null ) { - - object.parent.updateWorldMatrix( true, false ); - - _m1$1.multiply( object.parent.matrixWorld ); - - } - - object.applyMatrix4( _m1$1 ); - - object.updateWorldMatrix( false, false ); - - this.add( object ); - - return this; - - }, - - getObjectById: function ( id ) { - - return this.getObjectByProperty( 'id', id ); - - }, - - getObjectByName: function ( name ) { - - return this.getObjectByProperty( 'name', name ); - - }, - - getObjectByProperty: function ( name, value ) { - - if ( this[ name ] === value ) return this; - - for ( let i = 0, l = this.children.length; i < l; i ++ ) { - - const child = this.children[ i ]; - const object = child.getObjectByProperty( name, value ); - - if ( object !== undefined ) { - - return object; - - } - - } - - return undefined; - - }, - - getWorldPosition: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' ); - target = new Vector3(); - - } - - this.updateMatrixWorld( true ); - - return target.setFromMatrixPosition( this.matrixWorld ); - - }, - - getWorldQuaternion: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' ); - target = new Quaternion(); - - } - - this.updateMatrixWorld( true ); - - this.matrixWorld.decompose( _position, target, _scale ); - - return target; - - }, - - getWorldScale: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldScale() target is now required' ); - target = new Vector3(); - - } - - this.updateMatrixWorld( true ); - - this.matrixWorld.decompose( _position, _quaternion$2, target ); - - return target; - - }, - - getWorldDirection: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' ); - target = new Vector3(); - - } - - this.updateMatrixWorld( true ); - - const e = this.matrixWorld.elements; - - return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); - - }, - - raycast: function () {}, - - traverse: function ( callback ) { - - callback( this ); - - const children = this.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].traverse( callback ); - - } - - }, - - traverseVisible: function ( callback ) { - - if ( this.visible === false ) return; - - callback( this ); - - const children = this.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].traverseVisible( callback ); - - } - - }, - - traverseAncestors: function ( callback ) { - - const parent = this.parent; - - if ( parent !== null ) { - - callback( parent ); - - parent.traverseAncestors( callback ); - - } - - }, - - updateMatrix: function () { - - this.matrix.compose( this.position, this.quaternion, this.scale ); - - this.matrixWorldNeedsUpdate = true; - - }, - - updateMatrixWorld: function ( force ) { - - if ( this.matrixAutoUpdate ) this.updateMatrix(); - - if ( this.matrixWorldNeedsUpdate || force ) { - - if ( this.parent === null ) { - - this.matrixWorld.copy( this.matrix ); - - } else { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - - } - - this.matrixWorldNeedsUpdate = false; - - force = true; - - } - - // update children - - const children = this.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateMatrixWorld( force ); - - } - - }, - - updateWorldMatrix: function ( updateParents, updateChildren ) { - - const parent = this.parent; - - if ( updateParents === true && parent !== null ) { - - parent.updateWorldMatrix( true, false ); - - } - - if ( this.matrixAutoUpdate ) this.updateMatrix(); - - if ( this.parent === null ) { - - this.matrixWorld.copy( this.matrix ); - - } else { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - - } - - // update children - - if ( updateChildren === true ) { - - const children = this.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateWorldMatrix( false, true ); - - } - - } - - }, - - toJSON: function ( meta ) { - - // meta is a string when called from JSON.stringify - const isRootObject = ( meta === undefined || typeof meta === 'string' ); - - const output = {}; - - // meta is a hash used to collect geometries, materials. - // not providing it implies that this is the root object - // being serialized. - if ( isRootObject ) { - - // initialize meta obj - meta = { - geometries: {}, - materials: {}, - textures: {}, - images: {}, - shapes: {} - }; - - output.metadata = { - version: 4.5, - type: 'Object', - generator: 'Object3D.toJSON' - }; - - } - - // standard Object3D serialization - - const object = {}; - - object.uuid = this.uuid; - object.type = this.type; - - if ( this.name !== '' ) object.name = this.name; - if ( this.castShadow === true ) object.castShadow = true; - if ( this.receiveShadow === true ) object.receiveShadow = true; - if ( this.visible === false ) object.visible = false; - if ( this.frustumCulled === false ) object.frustumCulled = false; - if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; - if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; - - object.layers = this.layers.mask; - object.matrix = this.matrix.toArray(); - - if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; - - // object specific properties - - if ( this.isInstancedMesh ) { - - object.type = 'InstancedMesh'; - object.count = this.count; - object.instanceMatrix = this.instanceMatrix.toJSON(); - - } - - // - - function serialize( library, element ) { - - if ( library[ element.uuid ] === undefined ) { - - library[ element.uuid ] = element.toJSON( meta ); - - } - - return element.uuid; - - } - - if ( this.isMesh || this.isLine || this.isPoints ) { - - object.geometry = serialize( meta.geometries, this.geometry ); - - const parameters = this.geometry.parameters; - - if ( parameters !== undefined && parameters.shapes !== undefined ) { - - const shapes = parameters.shapes; - - if ( Array.isArray( shapes ) ) { - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - - serialize( meta.shapes, shape ); - - } - - } else { - - serialize( meta.shapes, shapes ); - - } - - } - - } - - if ( this.material !== undefined ) { - - if ( Array.isArray( this.material ) ) { - - const uuids = []; - - for ( let i = 0, l = this.material.length; i < l; i ++ ) { - - uuids.push( serialize( meta.materials, this.material[ i ] ) ); - - } - - object.material = uuids; - - } else { - - object.material = serialize( meta.materials, this.material ); - - } - - } - - // - - if ( this.children.length > 0 ) { - - object.children = []; - - for ( let i = 0; i < this.children.length; i ++ ) { - - object.children.push( this.children[ i ].toJSON( meta ).object ); - - } - - } - - if ( isRootObject ) { - - const geometries = extractFromCache( meta.geometries ); - const materials = extractFromCache( meta.materials ); - const textures = extractFromCache( meta.textures ); - const images = extractFromCache( meta.images ); - const shapes = extractFromCache( meta.shapes ); - - if ( geometries.length > 0 ) output.geometries = geometries; - if ( materials.length > 0 ) output.materials = materials; - if ( textures.length > 0 ) output.textures = textures; - if ( images.length > 0 ) output.images = images; - if ( shapes.length > 0 ) output.shapes = shapes; - - } - - output.object = object; - - return output; - - // extract data from the cache hash - // remove metadata on each item - // and return as array - function extractFromCache( cache ) { - - const values = []; - for ( const key in cache ) { - - const data = cache[ key ]; - delete data.metadata; - values.push( data ); - - } - - return values; - - } - - }, - - clone: function ( recursive ) { - - return new this.constructor().copy( this, recursive ); - - }, - - copy: function ( source, recursive ) { - - if ( recursive === undefined ) recursive = true; - - this.name = source.name; - - this.up.copy( source.up ); - - this.position.copy( source.position ); - this.rotation.order = source.rotation.order; - this.quaternion.copy( source.quaternion ); - this.scale.copy( source.scale ); - - this.matrix.copy( source.matrix ); - this.matrixWorld.copy( source.matrixWorld ); - - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; - - this.layers.mask = source.layers.mask; - this.visible = source.visible; - - this.castShadow = source.castShadow; - this.receiveShadow = source.receiveShadow; - - this.frustumCulled = source.frustumCulled; - this.renderOrder = source.renderOrder; - - this.userData = JSON.parse( JSON.stringify( source.userData ) ); - - if ( recursive === true ) { - - for ( let i = 0; i < source.children.length; i ++ ) { - - const child = source.children[ i ]; - this.add( child.clone() ); - - } - - } - - return this; - - } - - } ); - - const _vector1 = new Vector3(); - const _vector2 = new Vector3(); - const _normalMatrix = new Matrix3(); - - class Plane { - - constructor( normal, constant ) { - - Object.defineProperty( this, 'isPlane', { value: true } ); - - // normal is assumed to be normalized - - this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); - this.constant = ( constant !== undefined ) ? constant : 0; - - } - - set( normal, constant ) { - - this.normal.copy( normal ); - this.constant = constant; - - return this; - - } - - setComponents( x, y, z, w ) { - - this.normal.set( x, y, z ); - this.constant = w; - - return this; - - } - - setFromNormalAndCoplanarPoint( normal, point ) { - - this.normal.copy( normal ); - this.constant = - point.dot( this.normal ); - - return this; - - } - - setFromCoplanarPoints( a, b, c ) { - - const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); - - // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? - - this.setFromNormalAndCoplanarPoint( normal, a ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( plane ) { - - this.normal.copy( plane.normal ); - this.constant = plane.constant; - - return this; - - } - - normalize() { - - // Note: will lead to a divide by zero if the plane is invalid. - - const inverseNormalLength = 1.0 / this.normal.length(); - this.normal.multiplyScalar( inverseNormalLength ); - this.constant *= inverseNormalLength; - - return this; - - } - - negate() { - - this.constant *= - 1; - this.normal.negate(); - - return this; - - } - - distanceToPoint( point ) { - - return this.normal.dot( point ) + this.constant; - - } - - distanceToSphere( sphere ) { - - return this.distanceToPoint( sphere.center ) - sphere.radius; - - } - - projectPoint( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .projectPoint() target is now required' ); - target = new Vector3(); - - } - - return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); - - } - - intersectLine( line, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .intersectLine() target is now required' ); - target = new Vector3(); - - } - - const direction = line.delta( _vector1 ); - - const denominator = this.normal.dot( direction ); - - if ( denominator === 0 ) { - - // line is coplanar, return origin - if ( this.distanceToPoint( line.start ) === 0 ) { - - return target.copy( line.start ); - - } - - // Unsure if this is the correct method to handle this case. - return undefined; - - } - - const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; - - if ( t < 0 || t > 1 ) { - - return undefined; - - } - - return target.copy( direction ).multiplyScalar( t ).add( line.start ); - - } - - intersectsLine( line ) { - - // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. - - const startSign = this.distanceToPoint( line.start ); - const endSign = this.distanceToPoint( line.end ); - - return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); - - } - - intersectsBox( box ) { - - return box.intersectsPlane( this ); - - } - - intersectsSphere( sphere ) { - - return sphere.intersectsPlane( this ); - - } - - coplanarPoint( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .coplanarPoint() target is now required' ); - target = new Vector3(); - - } - - return target.copy( this.normal ).multiplyScalar( - this.constant ); - - } - - applyMatrix4( matrix, optionalNormalMatrix ) { - - const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); - - const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); - - const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); - - this.constant = - referencePoint.dot( normal ); - - return this; - - } - - translate( offset ) { - - this.constant -= offset.dot( this.normal ); - - return this; - - } - - equals( plane ) { - - return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); - - } - - } - - const _v0$1 = new Vector3(); - const _v1$3 = new Vector3(); - const _v2$1 = new Vector3(); - const _v3 = new Vector3(); - - const _vab = new Vector3(); - const _vac = new Vector3(); - const _vbc = new Vector3(); - const _vap = new Vector3(); - const _vbp = new Vector3(); - const _vcp = new Vector3(); - - class Triangle { - - constructor( a, b, c ) { - - this.a = ( a !== undefined ) ? a : new Vector3(); - this.b = ( b !== undefined ) ? b : new Vector3(); - this.c = ( c !== undefined ) ? c : new Vector3(); - - } - - static getNormal( a, b, c, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getNormal() target is now required' ); - target = new Vector3(); - - } - - target.subVectors( c, b ); - _v0$1.subVectors( a, b ); - target.cross( _v0$1 ); - - const targetLengthSq = target.lengthSq(); - if ( targetLengthSq > 0 ) { - - return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); - - } - - return target.set( 0, 0, 0 ); - - } - - // static/instance method to calculate barycentric coordinates - // based on: http://www.blackpawn.com/texts/pointinpoly/default.html - static getBarycoord( point, a, b, c, target ) { - - _v0$1.subVectors( c, a ); - _v1$3.subVectors( b, a ); - _v2$1.subVectors( point, a ); - - const dot00 = _v0$1.dot( _v0$1 ); - const dot01 = _v0$1.dot( _v1$3 ); - const dot02 = _v0$1.dot( _v2$1 ); - const dot11 = _v1$3.dot( _v1$3 ); - const dot12 = _v1$3.dot( _v2$1 ); - - const denom = ( dot00 * dot11 - dot01 * dot01 ); - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getBarycoord() target is now required' ); - target = new Vector3(); - - } - - // collinear or singular triangle - if ( denom === 0 ) { - - // arbitrary location outside of triangle? - // not sure if this is the best idea, maybe should be returning undefined - return target.set( - 2, - 1, - 1 ); - - } - - const invDenom = 1 / denom; - const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; - const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; - - // barycentric coordinates must always sum to 1 - return target.set( 1 - u - v, v, u ); - - } - - static containsPoint( point, a, b, c ) { - - this.getBarycoord( point, a, b, c, _v3 ); - - return ( _v3.x >= 0 ) && ( _v3.y >= 0 ) && ( ( _v3.x + _v3.y ) <= 1 ); - - } - - static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { - - this.getBarycoord( point, p1, p2, p3, _v3 ); - - target.set( 0, 0 ); - target.addScaledVector( uv1, _v3.x ); - target.addScaledVector( uv2, _v3.y ); - target.addScaledVector( uv3, _v3.z ); - - return target; - - } - - static isFrontFacing( a, b, c, direction ) { - - _v0$1.subVectors( c, b ); - _v1$3.subVectors( a, b ); - - // strictly front facing - return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; - - } - - set( a, b, c ) { - - this.a.copy( a ); - this.b.copy( b ); - this.c.copy( c ); - - return this; - - } - - setFromPointsAndIndices( points, i0, i1, i2 ) { - - this.a.copy( points[ i0 ] ); - this.b.copy( points[ i1 ] ); - this.c.copy( points[ i2 ] ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( triangle ) { - - this.a.copy( triangle.a ); - this.b.copy( triangle.b ); - this.c.copy( triangle.c ); - - return this; - - } - - getArea() { - - _v0$1.subVectors( this.c, this.b ); - _v1$3.subVectors( this.a, this.b ); - - return _v0$1.cross( _v1$3 ).length() * 0.5; - - } - - getMidpoint( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getMidpoint() target is now required' ); - target = new Vector3(); - - } - - return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); - - } - - getNormal( target ) { - - return Triangle.getNormal( this.a, this.b, this.c, target ); - - } - - getPlane( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getPlane() target is now required' ); - target = new Plane(); - - } - - return target.setFromCoplanarPoints( this.a, this.b, this.c ); - - } - - getBarycoord( point, target ) { - - return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); - - } - - getUV( point, uv1, uv2, uv3, target ) { - - return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); - - } - - containsPoint( point ) { - - return Triangle.containsPoint( point, this.a, this.b, this.c ); - - } - - isFrontFacing( direction ) { - - return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); - - } - - intersectsBox( box ) { - - return box.intersectsTriangle( this ); - - } - - closestPointToPoint( p, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } - - const a = this.a, b = this.b, c = this.c; - let v, w; - - // algorithm thanks to Real-Time Collision Detection by Christer Ericson, - // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., - // under the accompanying license; see chapter 5.1.5 for detailed explanation. - // basically, we're distinguishing which of the voronoi regions of the triangle - // the point lies in with the minimum amount of redundant computation. - - _vab.subVectors( b, a ); - _vac.subVectors( c, a ); - _vap.subVectors( p, a ); - const d1 = _vab.dot( _vap ); - const d2 = _vac.dot( _vap ); - if ( d1 <= 0 && d2 <= 0 ) { - - // vertex region of A; barycentric coords (1, 0, 0) - return target.copy( a ); - - } - - _vbp.subVectors( p, b ); - const d3 = _vab.dot( _vbp ); - const d4 = _vac.dot( _vbp ); - if ( d3 >= 0 && d4 <= d3 ) { - - // vertex region of B; barycentric coords (0, 1, 0) - return target.copy( b ); - - } - - const vc = d1 * d4 - d3 * d2; - if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { - - v = d1 / ( d1 - d3 ); - // edge region of AB; barycentric coords (1-v, v, 0) - return target.copy( a ).addScaledVector( _vab, v ); - - } - - _vcp.subVectors( p, c ); - const d5 = _vab.dot( _vcp ); - const d6 = _vac.dot( _vcp ); - if ( d6 >= 0 && d5 <= d6 ) { - - // vertex region of C; barycentric coords (0, 0, 1) - return target.copy( c ); - - } - - const vb = d5 * d2 - d1 * d6; - if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { - - w = d2 / ( d2 - d6 ); - // edge region of AC; barycentric coords (1-w, 0, w) - return target.copy( a ).addScaledVector( _vac, w ); - - } - - const va = d3 * d6 - d5 * d4; - if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { - - _vbc.subVectors( c, b ); - w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); - // edge region of BC; barycentric coords (0, 1-w, w) - return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC - - } - - // face region - const denom = 1 / ( va + vb + vc ); - // u = va * denom - v = vb * denom; - w = vc * denom; - - return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); - - } - - equals( triangle ) { - - return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); - - } - - } - - const _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, - 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, - 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, - 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, - 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, - 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, - 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, - 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, - 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, - 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, - 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, - 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, - 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, - 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, - 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, - 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, - 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, - 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, - 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, - 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, - 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, - 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, - 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, - 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; - - const _hslA = { h: 0, s: 0, l: 0 }; - const _hslB = { h: 0, s: 0, l: 0 }; - - function hue2rgb( p, q, t ) { - - if ( t < 0 ) t += 1; - if ( t > 1 ) t -= 1; - if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; - if ( t < 1 / 2 ) return q; - if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); - return p; - - } - - function SRGBToLinear( c ) { - - return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); - - } - - function LinearToSRGB( c ) { - - return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; - - } - - class Color { - - constructor( r, g, b ) { - - Object.defineProperty( this, 'isColor', { value: true } ); - - if ( g === undefined && b === undefined ) { - - // r is THREE.Color, hex or string - return this.set( r ); - - } - - return this.setRGB( r, g, b ); - - } - - set( value ) { - - if ( value && value.isColor ) { - - this.copy( value ); - - } else if ( typeof value === 'number' ) { - - this.setHex( value ); - - } else if ( typeof value === 'string' ) { - - this.setStyle( value ); - - } - - return this; - - } - - setScalar( scalar ) { - - this.r = scalar; - this.g = scalar; - this.b = scalar; - - return this; - - } - - setHex( hex ) { - - hex = Math.floor( hex ); - - this.r = ( hex >> 16 & 255 ) / 255; - this.g = ( hex >> 8 & 255 ) / 255; - this.b = ( hex & 255 ) / 255; - - return this; - - } - - setRGB( r, g, b ) { - - this.r = r; - this.g = g; - this.b = b; - - return this; - - } - - setHSL( h, s, l ) { - - // h,s,l ranges are in 0.0 - 1.0 - h = MathUtils.euclideanModulo( h, 1 ); - s = MathUtils.clamp( s, 0, 1 ); - l = MathUtils.clamp( l, 0, 1 ); - - if ( s === 0 ) { - - this.r = this.g = this.b = l; - - } else { - - const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); - const q = ( 2 * l ) - p; - - this.r = hue2rgb( q, p, h + 1 / 3 ); - this.g = hue2rgb( q, p, h ); - this.b = hue2rgb( q, p, h - 1 / 3 ); - - } - - return this; - - } - - setStyle( style ) { - - function handleAlpha( string ) { - - if ( string === undefined ) return; - - if ( parseFloat( string ) < 1 ) { - - console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); - - } - - } - - - let m; - - if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) { - - // rgb / hsl - - let color; - const name = m[ 1 ]; - const components = m[ 2 ]; - - switch ( name ) { - - case 'rgb': - case 'rgba': - - if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { - - // rgb(255,0,0) rgba(255,0,0,0.5) - this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; - this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; - this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; - - handleAlpha( color[ 5 ] ); - - return this; - - } - - if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { - - // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) - this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; - this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; - this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; - - handleAlpha( color[ 5 ] ); - - return this; - - } - - break; - - case 'hsl': - case 'hsla': - - if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { - - // hsl(120,50%,50%) hsla(120,50%,50%,0.5) - const h = parseFloat( color[ 1 ] ) / 360; - const s = parseInt( color[ 2 ], 10 ) / 100; - const l = parseInt( color[ 3 ], 10 ) / 100; - - handleAlpha( color[ 5 ] ); - - return this.setHSL( h, s, l ); - - } - - break; - - } - - } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) { - - // hex color - - const hex = m[ 1 ]; - const size = hex.length; - - if ( size === 3 ) { - - // #ff0 - this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; - this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; - this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; - - return this; - - } else if ( size === 6 ) { - - // #ff0000 - this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; - this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; - this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; - - return this; - - } - - } - - if ( style && style.length > 0 ) { - - return this.setColorName( style ); - - } - - return this; - - } - - setColorName( style ) { - - // color keywords - const hex = _colorKeywords[ style ]; - - if ( hex !== undefined ) { - - // red - this.setHex( hex ); - - } else { - - // unknown color - console.warn( 'THREE.Color: Unknown color ' + style ); - - } - - return this; - - } - - clone() { - - return new this.constructor( this.r, this.g, this.b ); - - } - - copy( color ) { - - this.r = color.r; - this.g = color.g; - this.b = color.b; - - return this; - - } - - copyGammaToLinear( color, gammaFactor ) { - - if ( gammaFactor === undefined ) gammaFactor = 2.0; - - this.r = Math.pow( color.r, gammaFactor ); - this.g = Math.pow( color.g, gammaFactor ); - this.b = Math.pow( color.b, gammaFactor ); - - return this; - - } - - copyLinearToGamma( color, gammaFactor ) { - - if ( gammaFactor === undefined ) gammaFactor = 2.0; - - const safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; - - this.r = Math.pow( color.r, safeInverse ); - this.g = Math.pow( color.g, safeInverse ); - this.b = Math.pow( color.b, safeInverse ); - - return this; - - } - - convertGammaToLinear( gammaFactor ) { - - this.copyGammaToLinear( this, gammaFactor ); - - return this; - - } - - convertLinearToGamma( gammaFactor ) { - - this.copyLinearToGamma( this, gammaFactor ); - - return this; - - } - - copySRGBToLinear( color ) { - - this.r = SRGBToLinear( color.r ); - this.g = SRGBToLinear( color.g ); - this.b = SRGBToLinear( color.b ); - - return this; - - } - - copyLinearToSRGB( color ) { - - this.r = LinearToSRGB( color.r ); - this.g = LinearToSRGB( color.g ); - this.b = LinearToSRGB( color.b ); - - return this; - - } - - convertSRGBToLinear() { - - this.copySRGBToLinear( this ); - - return this; - - } - - convertLinearToSRGB() { - - this.copyLinearToSRGB( this ); - - return this; - - } - - getHex() { - - return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; - - } - - getHexString() { - - return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); - - } - - getHSL( target ) { - - // h,s,l ranges are in 0.0 - 1.0 - - if ( target === undefined ) { - - console.warn( 'THREE.Color: .getHSL() target is now required' ); - target = { h: 0, s: 0, l: 0 }; - - } - - const r = this.r, g = this.g, b = this.b; - - const max = Math.max( r, g, b ); - const min = Math.min( r, g, b ); - - let hue, saturation; - const lightness = ( min + max ) / 2.0; - - if ( min === max ) { - - hue = 0; - saturation = 0; - - } else { - - const delta = max - min; - - saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); - - switch ( max ) { - - case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; - case g: hue = ( b - r ) / delta + 2; break; - case b: hue = ( r - g ) / delta + 4; break; - - } - - hue /= 6; - - } - - target.h = hue; - target.s = saturation; - target.l = lightness; - - return target; - - } - - getStyle() { - - return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; - - } - - offsetHSL( h, s, l ) { - - this.getHSL( _hslA ); - - _hslA.h += h; _hslA.s += s; _hslA.l += l; - - this.setHSL( _hslA.h, _hslA.s, _hslA.l ); - - return this; - - } - - add( color ) { - - this.r += color.r; - this.g += color.g; - this.b += color.b; - - return this; - - } - - addColors( color1, color2 ) { - - this.r = color1.r + color2.r; - this.g = color1.g + color2.g; - this.b = color1.b + color2.b; - - return this; - - } - - addScalar( s ) { - - this.r += s; - this.g += s; - this.b += s; - - return this; - - } - - sub( color ) { - - this.r = Math.max( 0, this.r - color.r ); - this.g = Math.max( 0, this.g - color.g ); - this.b = Math.max( 0, this.b - color.b ); - - return this; - - } - - multiply( color ) { - - this.r *= color.r; - this.g *= color.g; - this.b *= color.b; - - return this; - - } - - multiplyScalar( s ) { - - this.r *= s; - this.g *= s; - this.b *= s; - - return this; - - } - - lerp( color, alpha ) { - - this.r += ( color.r - this.r ) * alpha; - this.g += ( color.g - this.g ) * alpha; - this.b += ( color.b - this.b ) * alpha; - - return this; - - } - - lerpHSL( color, alpha ) { - - this.getHSL( _hslA ); - color.getHSL( _hslB ); - - const h = MathUtils.lerp( _hslA.h, _hslB.h, alpha ); - const s = MathUtils.lerp( _hslA.s, _hslB.s, alpha ); - const l = MathUtils.lerp( _hslA.l, _hslB.l, alpha ); - - this.setHSL( h, s, l ); - - return this; - - } - - equals( c ) { - - return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this.r = array[ offset ]; - this.g = array[ offset + 1 ]; - this.b = array[ offset + 2 ]; - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.r; - array[ offset + 1 ] = this.g; - array[ offset + 2 ] = this.b; - - return array; - - } - - fromBufferAttribute( attribute, index ) { - - this.r = attribute.getX( index ); - this.g = attribute.getY( index ); - this.b = attribute.getZ( index ); - - if ( attribute.normalized === true ) { - - // assuming Uint8Array - - this.r /= 255; - this.g /= 255; - this.b /= 255; - - } - - return this; - - } - - toJSON() { - - return this.getHex(); - - } - - } - - Color.NAMES = _colorKeywords; - Color.prototype.r = 1; - Color.prototype.g = 1; - Color.prototype.b = 1; - - class Face3 { - - constructor( a, b, c, normal, color, materialIndex ) { - - this.a = a; - this.b = b; - this.c = c; - - this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3(); - this.vertexNormals = Array.isArray( normal ) ? normal : []; - - this.color = ( color && color.isColor ) ? color : new Color(); - this.vertexColors = Array.isArray( color ) ? color : []; - - this.materialIndex = materialIndex !== undefined ? materialIndex : 0; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( source ) { - - this.a = source.a; - this.b = source.b; - this.c = source.c; - - this.normal.copy( source.normal ); - this.color.copy( source.color ); - - this.materialIndex = source.materialIndex; - - for ( let i = 0, il = source.vertexNormals.length; i < il; i ++ ) { - - this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); - - } - - for ( let i = 0, il = source.vertexColors.length; i < il; i ++ ) { - - this.vertexColors[ i ] = source.vertexColors[ i ].clone(); - - } - - return this; - - } - - } - - let materialId = 0; - - function Material() { - - Object.defineProperty( this, 'id', { value: materialId ++ } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - this.type = 'Material'; - - this.fog = true; - - this.blending = NormalBlending; - this.side = FrontSide; - this.flatShading = false; - this.vertexColors = false; - - this.opacity = 1; - this.transparent = false; - - this.blendSrc = SrcAlphaFactor; - this.blendDst = OneMinusSrcAlphaFactor; - this.blendEquation = AddEquation; - this.blendSrcAlpha = null; - this.blendDstAlpha = null; - this.blendEquationAlpha = null; - - this.depthFunc = LessEqualDepth; - this.depthTest = true; - this.depthWrite = true; - - this.stencilWriteMask = 0xff; - this.stencilFunc = AlwaysStencilFunc; - this.stencilRef = 0; - this.stencilFuncMask = 0xff; - this.stencilFail = KeepStencilOp; - this.stencilZFail = KeepStencilOp; - this.stencilZPass = KeepStencilOp; - this.stencilWrite = false; - - this.clippingPlanes = null; - this.clipIntersection = false; - this.clipShadows = false; - - this.shadowSide = null; - - this.colorWrite = true; - - this.precision = null; // override the renderer's default precision for this material - - this.polygonOffset = false; - this.polygonOffsetFactor = 0; - this.polygonOffsetUnits = 0; - - this.dithering = false; - - this.alphaTest = 0; - this.premultipliedAlpha = false; - - this.visible = true; - - this.toneMapped = true; - - this.userData = {}; - - this.version = 0; - - } - - Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: Material, - - isMaterial: true, - - onBeforeCompile: function ( /* shaderobject, renderer */ ) {}, - - customProgramCacheKey: function () { - - return this.onBeforeCompile.toString(); - - }, - - setValues: function ( values ) { - - if ( values === undefined ) return; - - for ( const key in values ) { - - const newValue = values[ key ]; - - if ( newValue === undefined ) { - - console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); - continue; - - } - - // for backward compatability if shading is set in the constructor - if ( key === 'shading' ) { - - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( newValue === FlatShading ) ? true : false; - continue; - - } - - const currentValue = this[ key ]; - - if ( currentValue === undefined ) { - - console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); - continue; - - } - - if ( currentValue && currentValue.isColor ) { - - currentValue.set( newValue ); - - } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { - - currentValue.copy( newValue ); - - } else { - - this[ key ] = newValue; - - } - - } - - }, - - toJSON: function ( meta ) { - - const isRoot = ( meta === undefined || typeof meta === 'string' ); - - if ( isRoot ) { - - meta = { - textures: {}, - images: {} - }; - - } - - const data = { - metadata: { - version: 4.5, - type: 'Material', - generator: 'Material.toJSON' - } - }; - - // standard Material serialization - data.uuid = this.uuid; - data.type = this.type; - - if ( this.name !== '' ) data.name = this.name; - - if ( this.color && this.color.isColor ) data.color = this.color.getHex(); - - if ( this.roughness !== undefined ) data.roughness = this.roughness; - if ( this.metalness !== undefined ) data.metalness = this.metalness; - - if ( this.sheen && this.sheen.isColor ) data.sheen = this.sheen.getHex(); - if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); - if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; - - if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); - if ( this.shininess !== undefined ) data.shininess = this.shininess; - if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat; - if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness; - - if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { - - data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; - - } - - if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { - - data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; - - } - - if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { - - data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; - data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); - - } - - if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; - if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; - if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; - if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; - - if ( this.aoMap && this.aoMap.isTexture ) { - - data.aoMap = this.aoMap.toJSON( meta ).uuid; - data.aoMapIntensity = this.aoMapIntensity; - - } - - if ( this.bumpMap && this.bumpMap.isTexture ) { - - data.bumpMap = this.bumpMap.toJSON( meta ).uuid; - data.bumpScale = this.bumpScale; - - } - - if ( this.normalMap && this.normalMap.isTexture ) { - - data.normalMap = this.normalMap.toJSON( meta ).uuid; - data.normalMapType = this.normalMapType; - data.normalScale = this.normalScale.toArray(); - - } - - if ( this.displacementMap && this.displacementMap.isTexture ) { - - data.displacementMap = this.displacementMap.toJSON( meta ).uuid; - data.displacementScale = this.displacementScale; - data.displacementBias = this.displacementBias; - - } - - if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; - if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; - - if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; - if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; - - if ( this.envMap && this.envMap.isTexture ) { - - data.envMap = this.envMap.toJSON( meta ).uuid; - data.reflectivity = this.reflectivity; // Scale behind envMap - data.refractionRatio = this.refractionRatio; - - if ( this.combine !== undefined ) data.combine = this.combine; - if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; - - } - - if ( this.gradientMap && this.gradientMap.isTexture ) { - - data.gradientMap = this.gradientMap.toJSON( meta ).uuid; - - } - - if ( this.size !== undefined ) data.size = this.size; - if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; - - if ( this.blending !== NormalBlending ) data.blending = this.blending; - if ( this.flatShading === true ) data.flatShading = this.flatShading; - if ( this.side !== FrontSide ) data.side = this.side; - if ( this.vertexColors ) data.vertexColors = true; - - if ( this.opacity < 1 ) data.opacity = this.opacity; - if ( this.transparent === true ) data.transparent = this.transparent; - - data.depthFunc = this.depthFunc; - data.depthTest = this.depthTest; - data.depthWrite = this.depthWrite; - - data.stencilWrite = this.stencilWrite; - data.stencilWriteMask = this.stencilWriteMask; - data.stencilFunc = this.stencilFunc; - data.stencilRef = this.stencilRef; - data.stencilFuncMask = this.stencilFuncMask; - data.stencilFail = this.stencilFail; - data.stencilZFail = this.stencilZFail; - data.stencilZPass = this.stencilZPass; - - // rotation (SpriteMaterial) - if ( this.rotation && this.rotation !== 0 ) data.rotation = this.rotation; - - if ( this.polygonOffset === true ) data.polygonOffset = true; - if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; - if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; - - if ( this.linewidth && this.linewidth !== 1 ) data.linewidth = this.linewidth; - if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; - if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; - if ( this.scale !== undefined ) data.scale = this.scale; - - if ( this.dithering === true ) data.dithering = true; - - if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; - if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; - - if ( this.wireframe === true ) data.wireframe = this.wireframe; - if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; - if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap; - if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin; - - if ( this.morphTargets === true ) data.morphTargets = true; - if ( this.morphNormals === true ) data.morphNormals = true; - if ( this.skinning === true ) data.skinning = true; - - if ( this.visible === false ) data.visible = false; - - if ( this.toneMapped === false ) data.toneMapped = false; - - if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData; - - // TODO: Copied from Object3D.toJSON - - function extractFromCache( cache ) { - - const values = []; - - for ( const key in cache ) { - - const data = cache[ key ]; - delete data.metadata; - values.push( data ); - - } - - return values; - - } - - if ( isRoot ) { - - const textures = extractFromCache( meta.textures ); - const images = extractFromCache( meta.images ); - - if ( textures.length > 0 ) data.textures = textures; - if ( images.length > 0 ) data.images = images; - - } - - return data; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.name = source.name; - - this.fog = source.fog; - - this.blending = source.blending; - this.side = source.side; - this.flatShading = source.flatShading; - this.vertexColors = source.vertexColors; - - this.opacity = source.opacity; - this.transparent = source.transparent; - - this.blendSrc = source.blendSrc; - this.blendDst = source.blendDst; - this.blendEquation = source.blendEquation; - this.blendSrcAlpha = source.blendSrcAlpha; - this.blendDstAlpha = source.blendDstAlpha; - this.blendEquationAlpha = source.blendEquationAlpha; - - this.depthFunc = source.depthFunc; - this.depthTest = source.depthTest; - this.depthWrite = source.depthWrite; - - this.stencilWriteMask = source.stencilWriteMask; - this.stencilFunc = source.stencilFunc; - this.stencilRef = source.stencilRef; - this.stencilFuncMask = source.stencilFuncMask; - this.stencilFail = source.stencilFail; - this.stencilZFail = source.stencilZFail; - this.stencilZPass = source.stencilZPass; - this.stencilWrite = source.stencilWrite; - - const srcPlanes = source.clippingPlanes; - let dstPlanes = null; - - if ( srcPlanes !== null ) { - - const n = srcPlanes.length; - dstPlanes = new Array( n ); - - for ( let i = 0; i !== n; ++ i ) { - - dstPlanes[ i ] = srcPlanes[ i ].clone(); - - } - - } - - this.clippingPlanes = dstPlanes; - this.clipIntersection = source.clipIntersection; - this.clipShadows = source.clipShadows; - - this.shadowSide = source.shadowSide; - - this.colorWrite = source.colorWrite; - - this.precision = source.precision; - - this.polygonOffset = source.polygonOffset; - this.polygonOffsetFactor = source.polygonOffsetFactor; - this.polygonOffsetUnits = source.polygonOffsetUnits; - - this.dithering = source.dithering; - - this.alphaTest = source.alphaTest; - this.premultipliedAlpha = source.premultipliedAlpha; - - this.visible = source.visible; - - this.toneMapped = source.toneMapped; - - this.userData = JSON.parse( JSON.stringify( source.userData ) ); - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - Object.defineProperty( Material.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - /** - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: - * } - */ - - function MeshBasicMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshBasicMaterial'; - - this.color = new Color( 0xffffff ); // emissive - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - - this.setValues( parameters ); - - } - - MeshBasicMaterial.prototype = Object.create( Material.prototype ); - MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; - - MeshBasicMaterial.prototype.isMeshBasicMaterial = true; - - MeshBasicMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - return this; - - }; - - const _vector$3 = new Vector3(); - const _vector2$1 = new Vector2(); - - function BufferAttribute( array, itemSize, normalized ) { - - if ( Array.isArray( array ) ) { - - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - - } - - this.name = ''; - - this.array = array; - this.itemSize = itemSize; - this.count = array !== undefined ? array.length / itemSize : 0; - this.normalized = normalized === true; - - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; - - this.version = 0; - - } - - Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - Object.assign( BufferAttribute.prototype, { - - isBufferAttribute: true, - - onUploadCallback: function () {}, - - setUsage: function ( value ) { - - this.usage = value; - - return this; - - }, - - copy: function ( source ) { - - this.name = source.name; - this.array = new source.array.constructor( source.array ); - this.itemSize = source.itemSize; - this.count = source.count; - this.normalized = source.normalized; - - this.usage = source.usage; - - return this; - - }, - - copyAt: function ( index1, attribute, index2 ) { - - index1 *= this.itemSize; - index2 *= attribute.itemSize; - - for ( let i = 0, l = this.itemSize; i < l; i ++ ) { - - this.array[ index1 + i ] = attribute.array[ index2 + i ]; - - } - - return this; - - }, - - copyArray: function ( array ) { - - this.array.set( array ); - - return this; - - }, - - copyColorsArray: function ( colors ) { - - const array = this.array; - let offset = 0; - - for ( let i = 0, l = colors.length; i < l; i ++ ) { - - let color = colors[ i ]; - - if ( color === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); - color = new Color(); - - } - - array[ offset ++ ] = color.r; - array[ offset ++ ] = color.g; - array[ offset ++ ] = color.b; - - } - - return this; - - }, - - copyVector2sArray: function ( vectors ) { - - const array = this.array; - let offset = 0; - - for ( let i = 0, l = vectors.length; i < l; i ++ ) { - - let vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); - vector = new Vector2(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - - } - - return this; - - }, - - copyVector3sArray: function ( vectors ) { - - const array = this.array; - let offset = 0; - - for ( let i = 0, l = vectors.length; i < l; i ++ ) { - - let vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); - vector = new Vector3(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - - } - - return this; - - }, - - copyVector4sArray: function ( vectors ) { - - const array = this.array; - let offset = 0; - - for ( let i = 0, l = vectors.length; i < l; i ++ ) { - - let vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); - vector = new Vector4(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - array[ offset ++ ] = vector.w; - - } - - return this; - - }, - - applyMatrix3: function ( m ) { - - if ( this.itemSize === 2 ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector2$1.fromBufferAttribute( this, i ); - _vector2$1.applyMatrix3( m ); - - this.setXY( i, _vector2$1.x, _vector2$1.y ); - - } - - } else if ( this.itemSize === 3 ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$3.fromBufferAttribute( this, i ); - _vector$3.applyMatrix3( m ); - - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); - - } - - } - - return this; - - }, - - applyMatrix4: function ( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); - - _vector$3.applyMatrix4( m ); - - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); - - } - - return this; - - }, - - applyNormalMatrix: function ( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); - - _vector$3.applyNormalMatrix( m ); - - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); - - } - - return this; - - }, - - transformDirection: function ( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); - - _vector$3.transformDirection( m ); - - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); - - } - - return this; - - }, - - set: function ( value, offset ) { - - if ( offset === undefined ) offset = 0; - - this.array.set( value, offset ); - - return this; - - }, - - getX: function ( index ) { - - return this.array[ index * this.itemSize ]; - - }, - - setX: function ( index, x ) { - - this.array[ index * this.itemSize ] = x; - - return this; - - }, - - getY: function ( index ) { - - return this.array[ index * this.itemSize + 1 ]; - - }, - - setY: function ( index, y ) { - - this.array[ index * this.itemSize + 1 ] = y; - - return this; - - }, - - getZ: function ( index ) { - - return this.array[ index * this.itemSize + 2 ]; - - }, - - setZ: function ( index, z ) { - - this.array[ index * this.itemSize + 2 ] = z; - - return this; - - }, - - getW: function ( index ) { - - return this.array[ index * this.itemSize + 3 ]; - - }, - - setW: function ( index, w ) { - - this.array[ index * this.itemSize + 3 ] = w; - - return this; - - }, - - setXY: function ( index, x, y ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - - return this; - - }, - - setXYZ: function ( index, x, y, z ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - - return this; - - }, - - setXYZW: function ( index, x, y, z, w ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - this.array[ index + 3 ] = w; - - return this; - - }, - - onUpload: function ( callback ) { - - this.onUploadCallback = callback; - - return this; - - }, - - clone: function () { - - return new this.constructor( this.array, this.itemSize ).copy( this ); - - }, - - toJSON: function () { - - return { - itemSize: this.itemSize, - type: this.array.constructor.name, - array: Array.prototype.slice.call( this.array ), - normalized: this.normalized - }; - - } - - } ); - - // - - function Int8BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized ); - - } - - Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int8BufferAttribute.prototype.constructor = Int8BufferAttribute; - - - function Uint8BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized ); - - } - - Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute; - - - function Uint8ClampedBufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized ); - - } - - Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute; - - - function Int16BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized ); - - } - - Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int16BufferAttribute.prototype.constructor = Int16BufferAttribute; - - - function Uint16BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized ); - - } - - Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute; - - - function Int32BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized ); - - } - - Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int32BufferAttribute.prototype.constructor = Int32BufferAttribute; - - - function Uint32BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized ); - - } - - Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute; - - - function Float32BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized ); - - } - - Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; - - - function Float64BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized ); - - } - - Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float64BufferAttribute.prototype.constructor = Float64BufferAttribute; - - class DirectGeometry { - - constructor() { - - this.vertices = []; - this.normals = []; - this.colors = []; - this.uvs = []; - this.uvs2 = []; - - this.groups = []; - - this.morphTargets = {}; - - this.skinWeights = []; - this.skinIndices = []; - - // this.lineDistances = []; - - this.boundingBox = null; - this.boundingSphere = null; - - // update flags - - this.verticesNeedUpdate = false; - this.normalsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.uvsNeedUpdate = false; - this.groupsNeedUpdate = false; - - } - - computeGroups( geometry ) { - - const groups = []; - - let group, i; - let materialIndex = undefined; - - const faces = geometry.faces; - - for ( i = 0; i < faces.length; i ++ ) { - - const face = faces[ i ]; - - // materials - - if ( face.materialIndex !== materialIndex ) { - - materialIndex = face.materialIndex; - - if ( group !== undefined ) { - - group.count = ( i * 3 ) - group.start; - groups.push( group ); - - } - - group = { - start: i * 3, - materialIndex: materialIndex - }; - - } - - } - - if ( group !== undefined ) { - - group.count = ( i * 3 ) - group.start; - groups.push( group ); - - } - - this.groups = groups; - - } - - fromGeometry( geometry ) { - - const faces = geometry.faces; - const vertices = geometry.vertices; - const faceVertexUvs = geometry.faceVertexUvs; - - const hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; - const hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; - - // morphs - - const morphTargets = geometry.morphTargets; - const morphTargetsLength = morphTargets.length; - - let morphTargetsPosition; - - if ( morphTargetsLength > 0 ) { - - morphTargetsPosition = []; - - for ( let i = 0; i < morphTargetsLength; i ++ ) { - - morphTargetsPosition[ i ] = { - name: morphTargets[ i ].name, - data: [] - }; - - } - - this.morphTargets.position = morphTargetsPosition; - - } - - const morphNormals = geometry.morphNormals; - const morphNormalsLength = morphNormals.length; - - let morphTargetsNormal; - - if ( morphNormalsLength > 0 ) { - - morphTargetsNormal = []; - - for ( let i = 0; i < morphNormalsLength; i ++ ) { - - morphTargetsNormal[ i ] = { - name: morphNormals[ i ].name, - data: [] - }; - - } - - this.morphTargets.normal = morphTargetsNormal; - - } - - // skins - - const skinIndices = geometry.skinIndices; - const skinWeights = geometry.skinWeights; - - const hasSkinIndices = skinIndices.length === vertices.length; - const hasSkinWeights = skinWeights.length === vertices.length; - - // - - if ( vertices.length > 0 && faces.length === 0 ) { - - console.error( 'THREE.DirectGeometry: Faceless geometries are not supported.' ); - - } - - for ( let i = 0; i < faces.length; i ++ ) { - - const face = faces[ i ]; - - this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); - - const vertexNormals = face.vertexNormals; - - if ( vertexNormals.length === 3 ) { - - this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); - - } else { - - const normal = face.normal; - - this.normals.push( normal, normal, normal ); - - } - - const vertexColors = face.vertexColors; - - if ( vertexColors.length === 3 ) { - - this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); - - } else { - - const color = face.color; - - this.colors.push( color, color, color ); - - } - - if ( hasFaceVertexUv === true ) { - - const vertexUvs = faceVertexUvs[ 0 ][ i ]; - - if ( vertexUvs !== undefined ) { - - this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); - - } else { - - console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); - - this.uvs.push( new Vector2(), new Vector2(), new Vector2() ); - - } - - } - - if ( hasFaceVertexUv2 === true ) { - - const vertexUvs = faceVertexUvs[ 1 ][ i ]; - - if ( vertexUvs !== undefined ) { - - this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); - - } else { - - console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); - - this.uvs2.push( new Vector2(), new Vector2(), new Vector2() ); - - } - - } - - // morphs - - for ( let j = 0; j < morphTargetsLength; j ++ ) { - - const morphTarget = morphTargets[ j ].vertices; - - morphTargetsPosition[ j ].data.push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); - - } - - for ( let j = 0; j < morphNormalsLength; j ++ ) { - - const morphNormal = morphNormals[ j ].vertexNormals[ i ]; - - morphTargetsNormal[ j ].data.push( morphNormal.a, morphNormal.b, morphNormal.c ); - - } - - // skins - - if ( hasSkinIndices ) { - - this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); - - } - - if ( hasSkinWeights ) { - - this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); - - } - - } - - this.computeGroups( geometry ); - - this.verticesNeedUpdate = geometry.verticesNeedUpdate; - this.normalsNeedUpdate = geometry.normalsNeedUpdate; - this.colorsNeedUpdate = geometry.colorsNeedUpdate; - this.uvsNeedUpdate = geometry.uvsNeedUpdate; - this.groupsNeedUpdate = geometry.groupsNeedUpdate; - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - return this; - - } - - } - - function arrayMax( array ) { - - if ( array.length === 0 ) return - Infinity; - - let max = array[ 0 ]; - - for ( let i = 1, l = array.length; i < l; ++ i ) { - - if ( array[ i ] > max ) max = array[ i ]; - - } - - return max; - - } - - let _bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id - - const _m1$2 = new Matrix4(); - const _obj = new Object3D(); - const _offset = new Vector3(); - const _box$2 = new Box3(); - const _boxMorphTargets = new Box3(); - const _vector$4 = new Vector3(); - - function BufferGeometry() { - - Object.defineProperty( this, 'id', { value: _bufferGeometryId += 2 } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - this.type = 'BufferGeometry'; - - this.index = null; - this.attributes = {}; - - this.morphAttributes = {}; - this.morphTargetsRelative = false; - - this.groups = []; - - this.boundingBox = null; - this.boundingSphere = null; - - this.drawRange = { start: 0, count: Infinity }; - - this.userData = {}; - - } - - BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: BufferGeometry, - - isBufferGeometry: true, - - getIndex: function () { - - return this.index; - - }, - - setIndex: function ( index ) { - - if ( Array.isArray( index ) ) { - - this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); - - } else { - - this.index = index; - - } - - }, - - getAttribute: function ( name ) { - - return this.attributes[ name ]; - - }, - - setAttribute: function ( name, attribute ) { - - this.attributes[ name ] = attribute; - - return this; - - }, - - deleteAttribute: function ( name ) { - - delete this.attributes[ name ]; - - return this; - - }, - - addGroup: function ( start, count, materialIndex ) { - - this.groups.push( { - - start: start, - count: count, - materialIndex: materialIndex !== undefined ? materialIndex : 0 - - } ); - - }, - - clearGroups: function () { - - this.groups = []; - - }, - - setDrawRange: function ( start, count ) { - - this.drawRange.start = start; - this.drawRange.count = count; - - }, - - applyMatrix4: function ( matrix ) { - - const position = this.attributes.position; - - if ( position !== undefined ) { - - position.applyMatrix4( matrix ); - - position.needsUpdate = true; - - } - - const normal = this.attributes.normal; - - if ( normal !== undefined ) { - - const normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - normal.applyNormalMatrix( normalMatrix ); - - normal.needsUpdate = true; - - } - - const tangent = this.attributes.tangent; - - if ( tangent !== undefined ) { - - tangent.transformDirection( matrix ); - - tangent.needsUpdate = true; - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - return this; - - }, - - rotateX: function ( angle ) { - - // rotate geometry around world x-axis - - _m1$2.makeRotationX( angle ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - rotateY: function ( angle ) { - - // rotate geometry around world y-axis - - _m1$2.makeRotationY( angle ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - rotateZ: function ( angle ) { - - // rotate geometry around world z-axis - - _m1$2.makeRotationZ( angle ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - translate: function ( x, y, z ) { - - // translate geometry - - _m1$2.makeTranslation( x, y, z ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - scale: function ( x, y, z ) { - - // scale geometry - - _m1$2.makeScale( x, y, z ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - lookAt: function ( vector ) { - - _obj.lookAt( vector ); - - _obj.updateMatrix(); - - this.applyMatrix4( _obj.matrix ); - - return this; - - }, - - center: function () { - - this.computeBoundingBox(); - - this.boundingBox.getCenter( _offset ).negate(); - - this.translate( _offset.x, _offset.y, _offset.z ); - - return this; - - }, - - setFromObject: function ( object ) { - - // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); - - const geometry = object.geometry; - - if ( object.isPoints || object.isLine ) { - - const positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 ); - const colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 ); - - this.setAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); - this.setAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); - - if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { - - const lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 ); - - this.setAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); - - } - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - } else if ( object.isMesh ) { - - if ( geometry && geometry.isGeometry ) { - - this.fromGeometry( geometry ); - - } - - } - - return this; - - }, - - setFromPoints: function ( points ) { - - const position = []; - - for ( let i = 0, l = points.length; i < l; i ++ ) { - - const point = points[ i ]; - position.push( point.x, point.y, point.z || 0 ); - - } - - this.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) ); - - return this; - - }, - - updateFromObject: function ( object ) { - - let geometry = object.geometry; - - if ( object.isMesh ) { - - let direct = geometry.__directGeometry; - - if ( geometry.elementsNeedUpdate === true ) { - - direct = undefined; - geometry.elementsNeedUpdate = false; - - } - - if ( direct === undefined ) { - - return this.fromGeometry( geometry ); - - } - - direct.verticesNeedUpdate = geometry.verticesNeedUpdate; - direct.normalsNeedUpdate = geometry.normalsNeedUpdate; - direct.colorsNeedUpdate = geometry.colorsNeedUpdate; - direct.uvsNeedUpdate = geometry.uvsNeedUpdate; - direct.groupsNeedUpdate = geometry.groupsNeedUpdate; - - geometry.verticesNeedUpdate = false; - geometry.normalsNeedUpdate = false; - geometry.colorsNeedUpdate = false; - geometry.uvsNeedUpdate = false; - geometry.groupsNeedUpdate = false; - - geometry = direct; - - } - - if ( geometry.verticesNeedUpdate === true ) { - - const attribute = this.attributes.position; - - if ( attribute !== undefined ) { - - attribute.copyVector3sArray( geometry.vertices ); - attribute.needsUpdate = true; - - } - - geometry.verticesNeedUpdate = false; - - } - - if ( geometry.normalsNeedUpdate === true ) { - - const attribute = this.attributes.normal; - - if ( attribute !== undefined ) { - - attribute.copyVector3sArray( geometry.normals ); - attribute.needsUpdate = true; - - } - - geometry.normalsNeedUpdate = false; - - } - - if ( geometry.colorsNeedUpdate === true ) { - - const attribute = this.attributes.color; - - if ( attribute !== undefined ) { - - attribute.copyColorsArray( geometry.colors ); - attribute.needsUpdate = true; - - } - - geometry.colorsNeedUpdate = false; - - } - - if ( geometry.uvsNeedUpdate ) { - - const attribute = this.attributes.uv; - - if ( attribute !== undefined ) { - - attribute.copyVector2sArray( geometry.uvs ); - attribute.needsUpdate = true; - - } - - geometry.uvsNeedUpdate = false; - - } - - if ( geometry.lineDistancesNeedUpdate ) { - - const attribute = this.attributes.lineDistance; - - if ( attribute !== undefined ) { - - attribute.copyArray( geometry.lineDistances ); - attribute.needsUpdate = true; - - } - - geometry.lineDistancesNeedUpdate = false; - - } - - if ( geometry.groupsNeedUpdate ) { - - geometry.computeGroups( object.geometry ); - this.groups = geometry.groups; - - geometry.groupsNeedUpdate = false; - - } - - return this; - - }, - - fromGeometry: function ( geometry ) { - - geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry ); - - return this.fromDirectGeometry( geometry.__directGeometry ); - - }, - - fromDirectGeometry: function ( geometry ) { - - const positions = new Float32Array( geometry.vertices.length * 3 ); - this.setAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); - - if ( geometry.normals.length > 0 ) { - - const normals = new Float32Array( geometry.normals.length * 3 ); - this.setAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); - - } - - if ( geometry.colors.length > 0 ) { - - const colors = new Float32Array( geometry.colors.length * 3 ); - this.setAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); - - } - - if ( geometry.uvs.length > 0 ) { - - const uvs = new Float32Array( geometry.uvs.length * 2 ); - this.setAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); - - } - - if ( geometry.uvs2.length > 0 ) { - - const uvs2 = new Float32Array( geometry.uvs2.length * 2 ); - this.setAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); - - } - - // groups - - this.groups = geometry.groups; - - // morphs - - for ( const name in geometry.morphTargets ) { - - const array = []; - const morphTargets = geometry.morphTargets[ name ]; - - for ( let i = 0, l = morphTargets.length; i < l; i ++ ) { - - const morphTarget = morphTargets[ i ]; - - const attribute = new Float32BufferAttribute( morphTarget.data.length * 3, 3 ); - attribute.name = morphTarget.name; - - array.push( attribute.copyVector3sArray( morphTarget.data ) ); - - } - - this.morphAttributes[ name ] = array; - - } - - // skinning - - if ( geometry.skinIndices.length > 0 ) { - - const skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 ); - this.setAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); - - } - - if ( geometry.skinWeights.length > 0 ) { - - const skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 ); - this.setAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); - - } - - // - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - return this; - - }, - - computeBoundingBox: function () { - - if ( this.boundingBox === null ) { - - this.boundingBox = new Box3(); - - } - - const position = this.attributes.position; - const morphAttributesPosition = this.morphAttributes.position; - - if ( position && position.isGLBufferAttribute ) { - - console.error( 'THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".', this ); - - this.boundingBox.set( - new Vector3( - Infinity, - Infinity, - Infinity ), - new Vector3( + Infinity, + Infinity, + Infinity ) - ); - - return; - - } - - if ( position !== undefined ) { - - this.boundingBox.setFromBufferAttribute( position ); - - // process morph attributes if present - - if ( morphAttributesPosition ) { - - for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - - const morphAttribute = morphAttributesPosition[ i ]; - _box$2.setFromBufferAttribute( morphAttribute ); - - if ( this.morphTargetsRelative ) { - - _vector$4.addVectors( this.boundingBox.min, _box$2.min ); - this.boundingBox.expandByPoint( _vector$4 ); - - _vector$4.addVectors( this.boundingBox.max, _box$2.max ); - this.boundingBox.expandByPoint( _vector$4 ); - - } else { - - this.boundingBox.expandByPoint( _box$2.min ); - this.boundingBox.expandByPoint( _box$2.max ); - - } - - } - - } - - } else { - - this.boundingBox.makeEmpty(); - - } - - if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { - - console.error( 'THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); - - } - - }, - - computeBoundingSphere: function () { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new Sphere(); - - } - - const position = this.attributes.position; - const morphAttributesPosition = this.morphAttributes.position; - - if ( position && position.isGLBufferAttribute ) { - - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".', this ); - - this.boundingSphere.set( new Vector3(), Infinity ); - - return; - - } - - if ( position ) { - - // first, find the center of the bounding sphere - - const center = this.boundingSphere.center; - - _box$2.setFromBufferAttribute( position ); - - // process morph attributes if present - - if ( morphAttributesPosition ) { - - for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - - const morphAttribute = morphAttributesPosition[ i ]; - _boxMorphTargets.setFromBufferAttribute( morphAttribute ); - - if ( this.morphTargetsRelative ) { - - _vector$4.addVectors( _box$2.min, _boxMorphTargets.min ); - _box$2.expandByPoint( _vector$4 ); - - _vector$4.addVectors( _box$2.max, _boxMorphTargets.max ); - _box$2.expandByPoint( _vector$4 ); - - } else { - - _box$2.expandByPoint( _boxMorphTargets.min ); - _box$2.expandByPoint( _boxMorphTargets.max ); - - } - - } - - } - - _box$2.getCenter( center ); - - // second, try to find a boundingSphere with a radius smaller than the - // boundingSphere of the boundingBox: sqrt(3) smaller in the best case - - let maxRadiusSq = 0; - - for ( let i = 0, il = position.count; i < il; i ++ ) { - - _vector$4.fromBufferAttribute( position, i ); - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) ); - - } - - // process morph attributes if present - - if ( morphAttributesPosition ) { - - for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - - const morphAttribute = morphAttributesPosition[ i ]; - const morphTargetsRelative = this.morphTargetsRelative; - - for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) { - - _vector$4.fromBufferAttribute( morphAttribute, j ); - - if ( morphTargetsRelative ) { - - _offset.fromBufferAttribute( position, j ); - _vector$4.add( _offset ); - - } - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) ); - - } - - } - - } - - this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); - - if ( isNaN( this.boundingSphere.radius ) ) { - - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); - - } - - } - - }, - - computeFaceNormals: function () { - - // backwards compatibility - - }, - - computeVertexNormals: function () { - - const index = this.index; - const positionAttribute = this.getAttribute( 'position' ); - - if ( positionAttribute !== undefined ) { - - let normalAttribute = this.getAttribute( 'normal' ); - - if ( normalAttribute === undefined ) { - - normalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 ); - this.setAttribute( 'normal', normalAttribute ); - - } else { - - // reset existing normals to zero - - for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) { - - normalAttribute.setXYZ( i, 0, 0, 0 ); - - } - - } - - const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); - const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); - const cb = new Vector3(), ab = new Vector3(); - - // indexed elements - - if ( index ) { - - for ( let i = 0, il = index.count; i < il; i += 3 ) { - - const vA = index.getX( i + 0 ); - const vB = index.getX( i + 1 ); - const vC = index.getX( i + 2 ); - - pA.fromBufferAttribute( positionAttribute, vA ); - pB.fromBufferAttribute( positionAttribute, vB ); - pC.fromBufferAttribute( positionAttribute, vC ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - nA.fromBufferAttribute( normalAttribute, vA ); - nB.fromBufferAttribute( normalAttribute, vB ); - nC.fromBufferAttribute( normalAttribute, vC ); - - nA.add( cb ); - nB.add( cb ); - nC.add( cb ); - - normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z ); - normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z ); - normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z ); - - } - - } else { - - // non-indexed elements (unconnected triangle soup) - - for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) { - - pA.fromBufferAttribute( positionAttribute, i + 0 ); - pB.fromBufferAttribute( positionAttribute, i + 1 ); - pC.fromBufferAttribute( positionAttribute, i + 2 ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z ); - normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z ); - normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z ); - - } - - } - - this.normalizeNormals(); - - normalAttribute.needsUpdate = true; - - } - - }, - - merge: function ( geometry, offset ) { - - if ( ! ( geometry && geometry.isBufferGeometry ) ) { - - console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); - return; - - } - - if ( offset === undefined ) { - - offset = 0; - - console.warn( - 'THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. ' - + 'Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge.' - ); - - } - - const attributes = this.attributes; - - for ( const key in attributes ) { - - if ( geometry.attributes[ key ] === undefined ) continue; - - const attribute1 = attributes[ key ]; - const attributeArray1 = attribute1.array; - - const attribute2 = geometry.attributes[ key ]; - const attributeArray2 = attribute2.array; - - const attributeOffset = attribute2.itemSize * offset; - const length = Math.min( attributeArray2.length, attributeArray1.length - attributeOffset ); - - for ( let i = 0, j = attributeOffset; i < length; i ++, j ++ ) { - - attributeArray1[ j ] = attributeArray2[ i ]; - - } - - } - - return this; - - }, - - normalizeNormals: function () { - - const normals = this.attributes.normal; - - for ( let i = 0, il = normals.count; i < il; i ++ ) { - - _vector$4.fromBufferAttribute( normals, i ); - - _vector$4.normalize(); - - normals.setXYZ( i, _vector$4.x, _vector$4.y, _vector$4.z ); - - } - - }, - - toNonIndexed: function () { - - function convertBufferAttribute( attribute, indices ) { - - const array = attribute.array; - const itemSize = attribute.itemSize; - const normalized = attribute.normalized; - - const array2 = new array.constructor( indices.length * itemSize ); - - let index = 0, index2 = 0; - - for ( let i = 0, l = indices.length; i < l; i ++ ) { - - index = indices[ i ] * itemSize; - - for ( let j = 0; j < itemSize; j ++ ) { - - array2[ index2 ++ ] = array[ index ++ ]; - - } - - } - - return new BufferAttribute( array2, itemSize, normalized ); - - } - - // - - if ( this.index === null ) { - - console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' ); - return this; - - } - - const geometry2 = new BufferGeometry(); - - const indices = this.index.array; - const attributes = this.attributes; - - // attributes - - for ( const name in attributes ) { - - const attribute = attributes[ name ]; - - const newAttribute = convertBufferAttribute( attribute, indices ); - - geometry2.setAttribute( name, newAttribute ); - - } - - // morph attributes - - const morphAttributes = this.morphAttributes; - - for ( const name in morphAttributes ) { - - const morphArray = []; - const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes - - for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) { - - const attribute = morphAttribute[ i ]; - - const newAttribute = convertBufferAttribute( attribute, indices ); - - morphArray.push( newAttribute ); - - } - - geometry2.morphAttributes[ name ] = morphArray; - - } - - geometry2.morphTargetsRelative = this.morphTargetsRelative; - - // groups - - const groups = this.groups; - - for ( let i = 0, l = groups.length; i < l; i ++ ) { - - const group = groups[ i ]; - geometry2.addGroup( group.start, group.count, group.materialIndex ); - - } - - return geometry2; - - }, - - toJSON: function () { - - const data = { - metadata: { - version: 4.5, - type: 'BufferGeometry', - generator: 'BufferGeometry.toJSON' - } - }; - - // standard BufferGeometry serialization - - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) data.name = this.name; - if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; - - if ( this.parameters !== undefined ) { - - const parameters = this.parameters; - - for ( const key in parameters ) { - - if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; - - } - - return data; - - } - - data.data = { attributes: {} }; - - const index = this.index; - - if ( index !== null ) { - - data.data.index = { - type: index.array.constructor.name, - array: Array.prototype.slice.call( index.array ) - }; - - } - - const attributes = this.attributes; - - for ( const key in attributes ) { - - const attribute = attributes[ key ]; - - const attributeData = attribute.toJSON( data.data ); - - if ( attribute.name !== '' ) attributeData.name = attribute.name; - - data.data.attributes[ key ] = attributeData; - - } - - const morphAttributes = {}; - let hasMorphAttributes = false; - - for ( const key in this.morphAttributes ) { - - const attributeArray = this.morphAttributes[ key ]; - - const array = []; - - for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { - - const attribute = attributeArray[ i ]; - - const attributeData = attribute.toJSON( data.data ); - - if ( attribute.name !== '' ) attributeData.name = attribute.name; - - array.push( attributeData ); - - } - - if ( array.length > 0 ) { - - morphAttributes[ key ] = array; - - hasMorphAttributes = true; - - } - - } - - if ( hasMorphAttributes ) { - - data.data.morphAttributes = morphAttributes; - data.data.morphTargetsRelative = this.morphTargetsRelative; - - } - - const groups = this.groups; - - if ( groups.length > 0 ) { - - data.data.groups = JSON.parse( JSON.stringify( groups ) ); - - } - - const boundingSphere = this.boundingSphere; - - if ( boundingSphere !== null ) { - - data.data.boundingSphere = { - center: boundingSphere.center.toArray(), - radius: boundingSphere.radius - }; - - } - - return data; - - }, - - clone: function () { - - /* - // Handle primitives - - const parameters = this.parameters; - - if ( parameters !== undefined ) { - - const values = []; - - for ( const key in parameters ) { - - values.push( parameters[ key ] ); - - } - - const geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; - - } - - return new this.constructor().copy( this ); - */ - - return new BufferGeometry().copy( this ); - - }, - - copy: function ( source ) { - - // reset - - this.index = null; - this.attributes = {}; - this.morphAttributes = {}; - this.groups = []; - this.boundingBox = null; - this.boundingSphere = null; - - // used for storing cloned, shared data - - const data = {}; - - // name - - this.name = source.name; - - // index - - const index = source.index; - - if ( index !== null ) { - - this.setIndex( index.clone( data ) ); - - } - - // attributes - - const attributes = source.attributes; - - for ( const name in attributes ) { - - const attribute = attributes[ name ]; - this.setAttribute( name, attribute.clone( data ) ); - - } - - // morph attributes - - const morphAttributes = source.morphAttributes; - - for ( const name in morphAttributes ) { - - const array = []; - const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes - - for ( let i = 0, l = morphAttribute.length; i < l; i ++ ) { - - array.push( morphAttribute[ i ].clone( data ) ); - - } - - this.morphAttributes[ name ] = array; - - } - - this.morphTargetsRelative = source.morphTargetsRelative; - - // groups - - const groups = source.groups; - - for ( let i = 0, l = groups.length; i < l; i ++ ) { - - const group = groups[ i ]; - this.addGroup( group.start, group.count, group.materialIndex ); - - } - - // bounding box - - const boundingBox = source.boundingBox; - - if ( boundingBox !== null ) { - - this.boundingBox = boundingBox.clone(); - - } - - // bounding sphere - - const boundingSphere = source.boundingSphere; - - if ( boundingSphere !== null ) { - - this.boundingSphere = boundingSphere.clone(); - - } - - // draw range - - this.drawRange.start = source.drawRange.start; - this.drawRange.count = source.drawRange.count; - - // user data - - this.userData = source.userData; - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - const _inverseMatrix = new Matrix4(); - const _ray = new Ray(); - const _sphere = new Sphere(); - - const _vA = new Vector3(); - const _vB = new Vector3(); - const _vC = new Vector3(); - - const _tempA = new Vector3(); - const _tempB = new Vector3(); - const _tempC = new Vector3(); - - const _morphA = new Vector3(); - const _morphB = new Vector3(); - const _morphC = new Vector3(); - - const _uvA = new Vector2(); - const _uvB = new Vector2(); - const _uvC = new Vector2(); - - const _intersectionPoint = new Vector3(); - const _intersectionPointWorld = new Vector3(); - - function Mesh( geometry, material ) { - - Object3D.call( this ); - - this.type = 'Mesh'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new MeshBasicMaterial(); - - this.updateMorphTargets(); - - } - - Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Mesh, - - isMesh: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - if ( source.morphTargetInfluences !== undefined ) { - - this.morphTargetInfluences = source.morphTargetInfluences.slice(); - - } - - if ( source.morphTargetDictionary !== undefined ) { - - this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); - - } - - this.material = source.material; - this.geometry = source.geometry; - - return this; - - }, - - updateMorphTargets: function () { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); - - if ( keys.length > 0 ) { - - const morphAttribute = morphAttributes[ keys[ 0 ] ]; - - if ( morphAttribute !== undefined ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - - const name = morphAttribute[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } - - } - - } - - } else { - - const morphTargets = geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - console.error( 'THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - }, - - raycast: function ( raycaster, intersects ) { - - const geometry = this.geometry; - const material = this.material; - const matrixWorld = this.matrixWorld; - - if ( material === undefined ) return; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _sphere.copy( geometry.boundingSphere ); - _sphere.applyMatrix4( matrixWorld ); - - if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; - - // - - _inverseMatrix.getInverse( matrixWorld ); - _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); - - // Check boundingBox before continuing - - if ( geometry.boundingBox !== null ) { - - if ( _ray.intersectsBox( geometry.boundingBox ) === false ) return; - - } - - let intersection; - - if ( geometry.isBufferGeometry ) { - - const index = geometry.index; - const position = geometry.attributes.position; - const morphPosition = geometry.morphAttributes.position; - const morphTargetsRelative = geometry.morphTargetsRelative; - const uv = geometry.attributes.uv; - const uv2 = geometry.attributes.uv2; - const groups = geometry.groups; - const drawRange = geometry.drawRange; - - if ( index !== null ) { - - // indexed buffer geometry - - if ( Array.isArray( material ) ) { - - for ( let i = 0, il = groups.length; i < il; i ++ ) { - - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; - - const start = Math.max( group.start, drawRange.start ); - const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); - - for ( let j = start, jl = end; j < jl; j += 3 ) { - - const a = index.getX( j ); - const b = index.getX( j + 1 ); - const c = index.getX( j + 2 ); - - intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics - intersection.face.materialIndex = group.materialIndex; - intersects.push( intersection ); - - } - - } - - } - - } else { - - const start = Math.max( 0, drawRange.start ); - const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - - for ( let i = start, il = end; i < il; i += 3 ) { - - const a = index.getX( i ); - const b = index.getX( i + 1 ); - const c = index.getX( i + 2 ); - - intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics - intersects.push( intersection ); - - } - - } - - } - - } else if ( position !== undefined ) { - - // non-indexed buffer geometry - - if ( Array.isArray( material ) ) { - - for ( let i = 0, il = groups.length; i < il; i ++ ) { - - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; - - const start = Math.max( group.start, drawRange.start ); - const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); - - for ( let j = start, jl = end; j < jl; j += 3 ) { - - const a = j; - const b = j + 1; - const c = j + 2; - - intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics - intersection.face.materialIndex = group.materialIndex; - intersects.push( intersection ); - - } - - } - - } - - } else { - - const start = Math.max( 0, drawRange.start ); - const end = Math.min( position.count, ( drawRange.start + drawRange.count ) ); - - for ( let i = start, il = end; i < il; i += 3 ) { - - const a = i; - const b = i + 1; - const c = i + 2; - - intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics - intersects.push( intersection ); - - } - - } - - } - - } - - } else if ( geometry.isGeometry ) { - - const isMultiMaterial = Array.isArray( material ); - - const vertices = geometry.vertices; - const faces = geometry.faces; - let uvs; - - const faceVertexUvs = geometry.faceVertexUvs[ 0 ]; - if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; - - for ( let f = 0, fl = faces.length; f < fl; f ++ ) { - - const face = faces[ f ]; - const faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material; - - if ( faceMaterial === undefined ) continue; - - const fvA = vertices[ face.a ]; - const fvB = vertices[ face.b ]; - const fvC = vertices[ face.c ]; - - intersection = checkIntersection( this, faceMaterial, raycaster, _ray, fvA, fvB, fvC, _intersectionPoint ); - - if ( intersection ) { - - if ( uvs && uvs[ f ] ) { - - const uvs_f = uvs[ f ]; - _uvA.copy( uvs_f[ 0 ] ); - _uvB.copy( uvs_f[ 1 ] ); - _uvC.copy( uvs_f[ 2 ] ); - - intersection.uv = Triangle.getUV( _intersectionPoint, fvA, fvB, fvC, _uvA, _uvB, _uvC, new Vector2() ); - - } - - intersection.face = face; - intersection.faceIndex = f; - intersects.push( intersection ); - - } - - } - - } - - } - - } ); - - function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { - - let intersect; - - if ( material.side === BackSide ) { - - intersect = ray.intersectTriangle( pC, pB, pA, true, point ); - - } else { - - intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point ); - - } - - if ( intersect === null ) return null; - - _intersectionPointWorld.copy( point ); - _intersectionPointWorld.applyMatrix4( object.matrixWorld ); - - const distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld ); - - if ( distance < raycaster.near || distance > raycaster.far ) return null; - - return { - distance: distance, - point: _intersectionPointWorld.clone(), - object: object - }; - - } - - function checkBufferGeometryIntersection( object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ) { - - _vA.fromBufferAttribute( position, a ); - _vB.fromBufferAttribute( position, b ); - _vC.fromBufferAttribute( position, c ); - - const morphInfluences = object.morphTargetInfluences; - - if ( material.morphTargets && morphPosition && morphInfluences ) { - - _morphA.set( 0, 0, 0 ); - _morphB.set( 0, 0, 0 ); - _morphC.set( 0, 0, 0 ); - - for ( let i = 0, il = morphPosition.length; i < il; i ++ ) { - - const influence = morphInfluences[ i ]; - const morphAttribute = morphPosition[ i ]; - - if ( influence === 0 ) continue; - - _tempA.fromBufferAttribute( morphAttribute, a ); - _tempB.fromBufferAttribute( morphAttribute, b ); - _tempC.fromBufferAttribute( morphAttribute, c ); - - if ( morphTargetsRelative ) { - - _morphA.addScaledVector( _tempA, influence ); - _morphB.addScaledVector( _tempB, influence ); - _morphC.addScaledVector( _tempC, influence ); - - } else { - - _morphA.addScaledVector( _tempA.sub( _vA ), influence ); - _morphB.addScaledVector( _tempB.sub( _vB ), influence ); - _morphC.addScaledVector( _tempC.sub( _vC ), influence ); - - } - - } - - _vA.add( _morphA ); - _vB.add( _morphB ); - _vC.add( _morphC ); - - } - - if ( object.isSkinnedMesh ) { - - object.boneTransform( a, _vA ); - object.boneTransform( b, _vB ); - object.boneTransform( c, _vC ); - - } - - const intersection = checkIntersection( object, material, raycaster, ray, _vA, _vB, _vC, _intersectionPoint ); - - if ( intersection ) { - - if ( uv ) { - - _uvA.fromBufferAttribute( uv, a ); - _uvB.fromBufferAttribute( uv, b ); - _uvC.fromBufferAttribute( uv, c ); - - intersection.uv = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ); - - } - - if ( uv2 ) { - - _uvA.fromBufferAttribute( uv2, a ); - _uvB.fromBufferAttribute( uv2, b ); - _uvC.fromBufferAttribute( uv2, c ); - - intersection.uv2 = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ); - - } - - const face = new Face3( a, b, c ); - Triangle.getNormal( _vA, _vB, _vC, face.normal ); - - intersection.face = face; - - } - - return intersection; - - } - - let _geometryId = 0; // Geometry uses even numbers as Id - const _m1$3 = new Matrix4(); - const _obj$1 = new Object3D(); - const _offset$1 = new Vector3(); - - function Geometry() { - - Object.defineProperty( this, 'id', { value: _geometryId += 2 } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - this.type = 'Geometry'; - - this.vertices = []; - this.colors = []; - this.faces = []; - this.faceVertexUvs = [[]]; - - this.morphTargets = []; - this.morphNormals = []; - - this.skinWeights = []; - this.skinIndices = []; - - this.lineDistances = []; - - this.boundingBox = null; - this.boundingSphere = null; - - // update flags - - this.elementsNeedUpdate = false; - this.verticesNeedUpdate = false; - this.uvsNeedUpdate = false; - this.normalsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.lineDistancesNeedUpdate = false; - this.groupsNeedUpdate = false; - - } - - Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: Geometry, - - isGeometry: true, - - applyMatrix4: function ( matrix ) { - - const normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - for ( let i = 0, il = this.vertices.length; i < il; i ++ ) { - - const vertex = this.vertices[ i ]; - vertex.applyMatrix4( matrix ); - - } - - for ( let i = 0, il = this.faces.length; i < il; i ++ ) { - - const face = this.faces[ i ]; - face.normal.applyMatrix3( normalMatrix ).normalize(); - - for ( let j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { - - face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); - - } - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - this.verticesNeedUpdate = true; - this.normalsNeedUpdate = true; - - return this; - - }, - - rotateX: function ( angle ) { - - // rotate geometry around world x-axis - - _m1$3.makeRotationX( angle ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - rotateY: function ( angle ) { - - // rotate geometry around world y-axis - - _m1$3.makeRotationY( angle ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - rotateZ: function ( angle ) { - - // rotate geometry around world z-axis - - _m1$3.makeRotationZ( angle ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - translate: function ( x, y, z ) { - - // translate geometry - - _m1$3.makeTranslation( x, y, z ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - scale: function ( x, y, z ) { - - // scale geometry - - _m1$3.makeScale( x, y, z ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - lookAt: function ( vector ) { - - _obj$1.lookAt( vector ); - - _obj$1.updateMatrix(); - - this.applyMatrix4( _obj$1.matrix ); - - return this; - - }, - - fromBufferGeometry: function ( geometry ) { - - const scope = this; - - const index = geometry.index !== null ? geometry.index : undefined; - const attributes = geometry.attributes; - - if ( attributes.position === undefined ) { - - console.error( 'THREE.Geometry.fromBufferGeometry(): Position attribute required for conversion.' ); - return this; - - } - - const position = attributes.position; - const normal = attributes.normal; - const color = attributes.color; - const uv = attributes.uv; - const uv2 = attributes.uv2; - - if ( uv2 !== undefined ) this.faceVertexUvs[ 1 ] = []; - - for ( let i = 0; i < position.count; i ++ ) { - - scope.vertices.push( new Vector3().fromBufferAttribute( position, i ) ); - - if ( color !== undefined ) { - - scope.colors.push( new Color().fromBufferAttribute( color, i ) ); - - } - - } - - function addFace( a, b, c, materialIndex ) { - - const vertexColors = ( color === undefined ) ? [] : [ - scope.colors[ a ].clone(), - scope.colors[ b ].clone(), - scope.colors[ c ].clone() - ]; - - const vertexNormals = ( normal === undefined ) ? [] : [ - new Vector3().fromBufferAttribute( normal, a ), - new Vector3().fromBufferAttribute( normal, b ), - new Vector3().fromBufferAttribute( normal, c ) - ]; - - const face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); - - scope.faces.push( face ); - - if ( uv !== undefined ) { - - scope.faceVertexUvs[ 0 ].push( [ - new Vector2().fromBufferAttribute( uv, a ), - new Vector2().fromBufferAttribute( uv, b ), - new Vector2().fromBufferAttribute( uv, c ) - ] ); - - } - - if ( uv2 !== undefined ) { - - scope.faceVertexUvs[ 1 ].push( [ - new Vector2().fromBufferAttribute( uv2, a ), - new Vector2().fromBufferAttribute( uv2, b ), - new Vector2().fromBufferAttribute( uv2, c ) - ] ); - - } - - } - - const groups = geometry.groups; - - if ( groups.length > 0 ) { - - for ( let i = 0; i < groups.length; i ++ ) { - - const group = groups[ i ]; - - const start = group.start; - const count = group.count; - - for ( let j = start, jl = start + count; j < jl; j += 3 ) { - - if ( index !== undefined ) { - - addFace( index.getX( j ), index.getX( j + 1 ), index.getX( j + 2 ), group.materialIndex ); - - } else { - - addFace( j, j + 1, j + 2, group.materialIndex ); - - } - - } - - } - - } else { - - if ( index !== undefined ) { - - for ( let i = 0; i < index.count; i += 3 ) { - - addFace( index.getX( i ), index.getX( i + 1 ), index.getX( i + 2 ) ); - - } - - } else { - - for ( let i = 0; i < position.count; i += 3 ) { - - addFace( i, i + 1, i + 2 ); - - } - - } - - } - - this.computeFaceNormals(); - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - return this; - - }, - - center: function () { - - this.computeBoundingBox(); - - this.boundingBox.getCenter( _offset$1 ).negate(); - - this.translate( _offset$1.x, _offset$1.y, _offset$1.z ); - - return this; - - }, - - normalize: function () { - - this.computeBoundingSphere(); - - const center = this.boundingSphere.center; - const radius = this.boundingSphere.radius; - - const s = radius === 0 ? 1 : 1.0 / radius; - - const matrix = new Matrix4(); - matrix.set( - s, 0, 0, - s * center.x, - 0, s, 0, - s * center.y, - 0, 0, s, - s * center.z, - 0, 0, 0, 1 - ); - - this.applyMatrix4( matrix ); - - return this; - - }, - - computeFaceNormals: function () { - - const cb = new Vector3(), ab = new Vector3(); - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - const vA = this.vertices[ face.a ]; - const vB = this.vertices[ face.b ]; - const vC = this.vertices[ face.c ]; - - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); - - cb.normalize(); - - face.normal.copy( cb ); - - } - - }, - - computeVertexNormals: function ( areaWeighted ) { - - if ( areaWeighted === undefined ) areaWeighted = true; - - const vertices = new Array( this.vertices.length ); - - for ( let v = 0, vl = this.vertices.length; v < vl; v ++ ) { - - vertices[ v ] = new Vector3(); - - } - - if ( areaWeighted ) { - - // vertex normals weighted by triangle areas - // http://www.iquilezles.org/www/articles/normals/normals.htm - - const cb = new Vector3(), ab = new Vector3(); - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - const vA = this.vertices[ face.a ]; - const vB = this.vertices[ face.b ]; - const vC = this.vertices[ face.c ]; - - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); - - vertices[ face.a ].add( cb ); - vertices[ face.b ].add( cb ); - vertices[ face.c ].add( cb ); - - } - - } else { - - this.computeFaceNormals(); - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - vertices[ face.a ].add( face.normal ); - vertices[ face.b ].add( face.normal ); - vertices[ face.c ].add( face.normal ); - - } - - } - - for ( let v = 0, vl = this.vertices.length; v < vl; v ++ ) { - - vertices[ v ].normalize(); - - } - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - const vertexNormals = face.vertexNormals; - - if ( vertexNormals.length === 3 ) { - - vertexNormals[ 0 ].copy( vertices[ face.a ] ); - vertexNormals[ 1 ].copy( vertices[ face.b ] ); - vertexNormals[ 2 ].copy( vertices[ face.c ] ); - - } else { - - vertexNormals[ 0 ] = vertices[ face.a ].clone(); - vertexNormals[ 1 ] = vertices[ face.b ].clone(); - vertexNormals[ 2 ] = vertices[ face.c ].clone(); - - } - - } - - if ( this.faces.length > 0 ) { - - this.normalsNeedUpdate = true; - - } - - }, - - computeFlatVertexNormals: function () { - - this.computeFaceNormals(); - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - const vertexNormals = face.vertexNormals; - - if ( vertexNormals.length === 3 ) { - - vertexNormals[ 0 ].copy( face.normal ); - vertexNormals[ 1 ].copy( face.normal ); - vertexNormals[ 2 ].copy( face.normal ); - - } else { - - vertexNormals[ 0 ] = face.normal.clone(); - vertexNormals[ 1 ] = face.normal.clone(); - vertexNormals[ 2 ] = face.normal.clone(); - - } - - } - - if ( this.faces.length > 0 ) { - - this.normalsNeedUpdate = true; - - } - - }, - - computeMorphNormals: function () { - - // save original normals - // - create temp variables on first access - // otherwise just copy (for faster repeated calls) - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - if ( ! face.__originalFaceNormal ) { - - face.__originalFaceNormal = face.normal.clone(); - - } else { - - face.__originalFaceNormal.copy( face.normal ); - - } - - if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; - - for ( let i = 0, il = face.vertexNormals.length; i < il; i ++ ) { - - if ( ! face.__originalVertexNormals[ i ] ) { - - face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); - - } else { - - face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); - - } - - } - - } - - // use temp geometry to compute face and vertex normals for each morph - - const tmpGeo = new Geometry(); - tmpGeo.faces = this.faces; - - for ( let i = 0, il = this.morphTargets.length; i < il; i ++ ) { - - // create on first access - - if ( ! this.morphNormals[ i ] ) { - - this.morphNormals[ i ] = {}; - this.morphNormals[ i ].faceNormals = []; - this.morphNormals[ i ].vertexNormals = []; - - const dstNormalsFace = this.morphNormals[ i ].faceNormals; - const dstNormalsVertex = this.morphNormals[ i ].vertexNormals; - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const faceNormal = new Vector3(); - const vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() }; - - dstNormalsFace.push( faceNormal ); - dstNormalsVertex.push( vertexNormals ); - - } - - } - - const morphNormals = this.morphNormals[ i ]; - - // set vertices to morph target - - tmpGeo.vertices = this.morphTargets[ i ].vertices; - - // compute morph normals - - tmpGeo.computeFaceNormals(); - tmpGeo.computeVertexNormals(); - - // store morph normals - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - const faceNormal = morphNormals.faceNormals[ f ]; - const vertexNormals = morphNormals.vertexNormals[ f ]; - - faceNormal.copy( face.normal ); - - vertexNormals.a.copy( face.vertexNormals[ 0 ] ); - vertexNormals.b.copy( face.vertexNormals[ 1 ] ); - vertexNormals.c.copy( face.vertexNormals[ 2 ] ); - - } - - } - - // restore original normals - - for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) { - - const face = this.faces[ f ]; - - face.normal = face.__originalFaceNormal; - face.vertexNormals = face.__originalVertexNormals; - - } - - }, - - computeBoundingBox: function () { - - if ( this.boundingBox === null ) { - - this.boundingBox = new Box3(); - - } - - this.boundingBox.setFromPoints( this.vertices ); - - }, - - computeBoundingSphere: function () { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new Sphere(); - - } - - this.boundingSphere.setFromPoints( this.vertices ); - - }, - - merge: function ( geometry, matrix, materialIndexOffset ) { - - if ( ! ( geometry && geometry.isGeometry ) ) { - - console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); - return; - - } - - let normalMatrix; - const vertexOffset = this.vertices.length, - vertices1 = this.vertices, - vertices2 = geometry.vertices, - faces1 = this.faces, - faces2 = geometry.faces, - colors1 = this.colors, - colors2 = geometry.colors; - - if ( materialIndexOffset === undefined ) materialIndexOffset = 0; - - if ( matrix !== undefined ) { - - normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - } - - // vertices - - for ( let i = 0, il = vertices2.length; i < il; i ++ ) { - - const vertex = vertices2[ i ]; - - const vertexCopy = vertex.clone(); - - if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); - - vertices1.push( vertexCopy ); - - } - - // colors - - for ( let i = 0, il = colors2.length; i < il; i ++ ) { - - colors1.push( colors2[ i ].clone() ); - - } - - // faces - - for ( let i = 0, il = faces2.length; i < il; i ++ ) { - - const face = faces2[ i ]; - let normal, color; - const faceVertexNormals = face.vertexNormals, - faceVertexColors = face.vertexColors; - - const faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); - faceCopy.normal.copy( face.normal ); - - if ( normalMatrix !== undefined ) { - - faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); - - } - - for ( let j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { - - normal = faceVertexNormals[ j ].clone(); - - if ( normalMatrix !== undefined ) { - - normal.applyMatrix3( normalMatrix ).normalize(); - - } - - faceCopy.vertexNormals.push( normal ); - - } - - faceCopy.color.copy( face.color ); - - for ( let j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { - - color = faceVertexColors[ j ]; - faceCopy.vertexColors.push( color.clone() ); - - } - - faceCopy.materialIndex = face.materialIndex + materialIndexOffset; - - faces1.push( faceCopy ); - - } - - // uvs - - for ( let i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { - - const faceVertexUvs2 = geometry.faceVertexUvs[ i ]; - - if ( this.faceVertexUvs[ i ] === undefined ) this.faceVertexUvs[ i ] = []; - - for ( let j = 0, jl = faceVertexUvs2.length; j < jl; j ++ ) { - - const uvs2 = faceVertexUvs2[ j ], uvsCopy = []; - - for ( let k = 0, kl = uvs2.length; k < kl; k ++ ) { - - uvsCopy.push( uvs2[ k ].clone() ); - - } - - this.faceVertexUvs[ i ].push( uvsCopy ); - - } - - } - - }, - - mergeMesh: function ( mesh ) { - - if ( ! ( mesh && mesh.isMesh ) ) { - - console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); - return; - - } - - if ( mesh.matrixAutoUpdate ) mesh.updateMatrix(); - - this.merge( mesh.geometry, mesh.matrix ); - - }, - - /* - * Checks for duplicate vertices with hashmap. - * Duplicated vertices are removed - * and faces' vertices are updated. - */ - - mergeVertices: function () { - - const verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) - const unique = [], changes = []; - - const precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 - const precision = Math.pow( 10, precisionPoints ); - - for ( let i = 0, il = this.vertices.length; i < il; i ++ ) { - - const v = this.vertices[ i ]; - const key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); - - if ( verticesMap[ key ] === undefined ) { - - verticesMap[ key ] = i; - unique.push( this.vertices[ i ] ); - changes[ i ] = unique.length - 1; - - } else { - - //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); - changes[ i ] = changes[ verticesMap[ key ] ]; - - } - - } - - - // if faces are completely degenerate after merging vertices, we - // have to remove them from the geometry. - const faceIndicesToRemove = []; - - for ( let i = 0, il = this.faces.length; i < il; i ++ ) { - - const face = this.faces[ i ]; - - face.a = changes[ face.a ]; - face.b = changes[ face.b ]; - face.c = changes[ face.c ]; - - const indices = [ face.a, face.b, face.c ]; - - // if any duplicate vertices are found in a Face3 - // we have to remove the face as nothing can be saved - for ( let n = 0; n < 3; n ++ ) { - - if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { - - faceIndicesToRemove.push( i ); - break; - - } - - } - - } - - for ( let i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { - - const idx = faceIndicesToRemove[ i ]; - - this.faces.splice( idx, 1 ); - - for ( let j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { - - this.faceVertexUvs[ j ].splice( idx, 1 ); - - } - - } - - // Use unique set of vertices - - const diff = this.vertices.length - unique.length; - this.vertices = unique; - return diff; - - }, - - setFromPoints: function ( points ) { - - this.vertices = []; - - for ( let i = 0, l = points.length; i < l; i ++ ) { - - const point = points[ i ]; - this.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); - - } - - return this; - - }, - - sortFacesByMaterialIndex: function () { - - const faces = this.faces; - const length = faces.length; - - // tag faces - - for ( let i = 0; i < length; i ++ ) { - - faces[ i ]._id = i; - - } - - // sort faces - - function materialIndexSort( a, b ) { - - return a.materialIndex - b.materialIndex; - - } - - faces.sort( materialIndexSort ); - - // sort uvs - - const uvs1 = this.faceVertexUvs[ 0 ]; - const uvs2 = this.faceVertexUvs[ 1 ]; - - let newUvs1, newUvs2; - - if ( uvs1 && uvs1.length === length ) newUvs1 = []; - if ( uvs2 && uvs2.length === length ) newUvs2 = []; - - for ( let i = 0; i < length; i ++ ) { - - const id = faces[ i ]._id; - - if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); - if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); - - } - - if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; - if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; - - }, - - toJSON: function () { - - const data = { - metadata: { - version: 4.5, - type: 'Geometry', - generator: 'Geometry.toJSON' - } - }; - - // standard Geometry serialization - - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) data.name = this.name; - - if ( this.parameters !== undefined ) { - - const parameters = this.parameters; - - for ( const key in parameters ) { - - if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; - - } - - return data; - - } - - const vertices = []; - - for ( let i = 0; i < this.vertices.length; i ++ ) { - - const vertex = this.vertices[ i ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - const faces = []; - const normals = []; - const normalsHash = {}; - const colors = []; - const colorsHash = {}; - const uvs = []; - const uvsHash = {}; - - for ( let i = 0; i < this.faces.length; i ++ ) { - - const face = this.faces[ i ]; - - const hasMaterial = true; - const hasFaceUv = false; // deprecated - const hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; - const hasFaceNormal = face.normal.length() > 0; - const hasFaceVertexNormal = face.vertexNormals.length > 0; - const hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; - const hasFaceVertexColor = face.vertexColors.length > 0; - - let faceType = 0; - - faceType = setBit( faceType, 0, 0 ); // isQuad - faceType = setBit( faceType, 1, hasMaterial ); - faceType = setBit( faceType, 2, hasFaceUv ); - faceType = setBit( faceType, 3, hasFaceVertexUv ); - faceType = setBit( faceType, 4, hasFaceNormal ); - faceType = setBit( faceType, 5, hasFaceVertexNormal ); - faceType = setBit( faceType, 6, hasFaceColor ); - faceType = setBit( faceType, 7, hasFaceVertexColor ); - - faces.push( faceType ); - faces.push( face.a, face.b, face.c ); - faces.push( face.materialIndex ); - - if ( hasFaceVertexUv ) { - - const faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; - - faces.push( - getUvIndex( faceVertexUvs[ 0 ] ), - getUvIndex( faceVertexUvs[ 1 ] ), - getUvIndex( faceVertexUvs[ 2 ] ) - ); - - } - - if ( hasFaceNormal ) { - - faces.push( getNormalIndex( face.normal ) ); - - } - - if ( hasFaceVertexNormal ) { - - const vertexNormals = face.vertexNormals; - - faces.push( - getNormalIndex( vertexNormals[ 0 ] ), - getNormalIndex( vertexNormals[ 1 ] ), - getNormalIndex( vertexNormals[ 2 ] ) - ); - - } - - if ( hasFaceColor ) { - - faces.push( getColorIndex( face.color ) ); - - } - - if ( hasFaceVertexColor ) { - - const vertexColors = face.vertexColors; - - faces.push( - getColorIndex( vertexColors[ 0 ] ), - getColorIndex( vertexColors[ 1 ] ), - getColorIndex( vertexColors[ 2 ] ) - ); - - } - - } - - function setBit( value, position, enabled ) { - - return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); - - } - - function getNormalIndex( normal ) { - - const hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); - - if ( normalsHash[ hash ] !== undefined ) { - - return normalsHash[ hash ]; - - } - - normalsHash[ hash ] = normals.length / 3; - normals.push( normal.x, normal.y, normal.z ); - - return normalsHash[ hash ]; - - } - - function getColorIndex( color ) { - - const hash = color.r.toString() + color.g.toString() + color.b.toString(); - - if ( colorsHash[ hash ] !== undefined ) { - - return colorsHash[ hash ]; - - } - - colorsHash[ hash ] = colors.length; - colors.push( color.getHex() ); - - return colorsHash[ hash ]; - - } - - function getUvIndex( uv ) { - - const hash = uv.x.toString() + uv.y.toString(); - - if ( uvsHash[ hash ] !== undefined ) { - - return uvsHash[ hash ]; - - } - - uvsHash[ hash ] = uvs.length / 2; - uvs.push( uv.x, uv.y ); - - return uvsHash[ hash ]; - - } - - data.data = {}; - - data.data.vertices = vertices; - data.data.normals = normals; - if ( colors.length > 0 ) data.data.colors = colors; - if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility - data.data.faces = faces; - - return data; - - }, - - clone: function () { - - /* - // Handle primitives - - const parameters = this.parameters; - - if ( parameters !== undefined ) { - - const values = []; - - for ( const key in parameters ) { - - values.push( parameters[ key ] ); - - } - - const geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; - - } - - return new this.constructor().copy( this ); - */ - - return new Geometry().copy( this ); - - }, - - copy: function ( source ) { - - // reset - - this.vertices = []; - this.colors = []; - this.faces = []; - this.faceVertexUvs = [[]]; - this.morphTargets = []; - this.morphNormals = []; - this.skinWeights = []; - this.skinIndices = []; - this.lineDistances = []; - this.boundingBox = null; - this.boundingSphere = null; - - // name - - this.name = source.name; - - // vertices - - const vertices = source.vertices; - - for ( let i = 0, il = vertices.length; i < il; i ++ ) { - - this.vertices.push( vertices[ i ].clone() ); - - } - - // colors - - const colors = source.colors; - - for ( let i = 0, il = colors.length; i < il; i ++ ) { - - this.colors.push( colors[ i ].clone() ); - - } - - // faces - - const faces = source.faces; - - for ( let i = 0, il = faces.length; i < il; i ++ ) { - - this.faces.push( faces[ i ].clone() ); - - } - - // face vertex uvs - - for ( let i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { - - const faceVertexUvs = source.faceVertexUvs[ i ]; - - if ( this.faceVertexUvs[ i ] === undefined ) { - - this.faceVertexUvs[ i ] = []; - - } - - for ( let j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { - - const uvs = faceVertexUvs[ j ], uvsCopy = []; - - for ( let k = 0, kl = uvs.length; k < kl; k ++ ) { - - const uv = uvs[ k ]; - - uvsCopy.push( uv.clone() ); - - } - - this.faceVertexUvs[ i ].push( uvsCopy ); - - } - - } - - // morph targets - - const morphTargets = source.morphTargets; - - for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { - - const morphTarget = {}; - morphTarget.name = morphTargets[ i ].name; - - // vertices - - if ( morphTargets[ i ].vertices !== undefined ) { - - morphTarget.vertices = []; - - for ( let j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) { - - morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() ); - - } - - } - - // normals - - if ( morphTargets[ i ].normals !== undefined ) { - - morphTarget.normals = []; - - for ( let j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) { - - morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() ); - - } - - } - - this.morphTargets.push( morphTarget ); - - } - - // morph normals - - const morphNormals = source.morphNormals; - - for ( let i = 0, il = morphNormals.length; i < il; i ++ ) { - - const morphNormal = {}; - - // vertex normals - - if ( morphNormals[ i ].vertexNormals !== undefined ) { - - morphNormal.vertexNormals = []; - - for ( let j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) { - - const srcVertexNormal = morphNormals[ i ].vertexNormals[ j ]; - const destVertexNormal = {}; - - destVertexNormal.a = srcVertexNormal.a.clone(); - destVertexNormal.b = srcVertexNormal.b.clone(); - destVertexNormal.c = srcVertexNormal.c.clone(); - - morphNormal.vertexNormals.push( destVertexNormal ); - - } - - } - - // face normals - - if ( morphNormals[ i ].faceNormals !== undefined ) { - - morphNormal.faceNormals = []; - - for ( let j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) { - - morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() ); - - } - - } - - this.morphNormals.push( morphNormal ); - - } - - // skin weights - - const skinWeights = source.skinWeights; - - for ( let i = 0, il = skinWeights.length; i < il; i ++ ) { - - this.skinWeights.push( skinWeights[ i ].clone() ); - - } - - // skin indices - - const skinIndices = source.skinIndices; - - for ( let i = 0, il = skinIndices.length; i < il; i ++ ) { - - this.skinIndices.push( skinIndices[ i ].clone() ); - - } - - // line distances - - const lineDistances = source.lineDistances; - - for ( let i = 0, il = lineDistances.length; i < il; i ++ ) { - - this.lineDistances.push( lineDistances[ i ] ); - - } - - // bounding box - - const boundingBox = source.boundingBox; - - if ( boundingBox !== null ) { - - this.boundingBox = boundingBox.clone(); - - } - - // bounding sphere - - const boundingSphere = source.boundingSphere; - - if ( boundingSphere !== null ) { - - this.boundingSphere = boundingSphere.clone(); - - } - - // update flags - - this.elementsNeedUpdate = source.elementsNeedUpdate; - this.verticesNeedUpdate = source.verticesNeedUpdate; - this.uvsNeedUpdate = source.uvsNeedUpdate; - this.normalsNeedUpdate = source.normalsNeedUpdate; - this.colorsNeedUpdate = source.colorsNeedUpdate; - this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate; - this.groupsNeedUpdate = source.groupsNeedUpdate; - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - // BoxGeometry - - class BoxGeometry extends Geometry { - - constructor( width, height, depth, widthSegments, heightSegments, depthSegments ) { - - super(); - - this.type = 'BoxGeometry'; - - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; - - this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); - this.mergeVertices(); - - } - - } - - // BoxBufferGeometry - - class BoxBufferGeometry extends BufferGeometry { - - constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) { - - super(); - - this.type = 'BoxBufferGeometry'; - - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; - - const scope = this; - - // segments - - widthSegments = Math.floor( widthSegments ); - heightSegments = Math.floor( heightSegments ); - depthSegments = Math.floor( depthSegments ); - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - let numberOfVertices = 0; - let groupStart = 0; - - // build each side of the box geometry - - buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px - buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx - buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py - buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny - buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz - buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { - - const segmentWidth = width / gridX; - const segmentHeight = height / gridY; - - const widthHalf = width / 2; - const heightHalf = height / 2; - const depthHalf = depth / 2; - - const gridX1 = gridX + 1; - const gridY1 = gridY + 1; - - let vertexCounter = 0; - let groupCount = 0; - - const vector = new Vector3(); - - // generate vertices, normals and uvs - - for ( let iy = 0; iy < gridY1; iy ++ ) { - - const y = iy * segmentHeight - heightHalf; - - for ( let ix = 0; ix < gridX1; ix ++ ) { - - const x = ix * segmentWidth - widthHalf; - - // set values to correct vector component - - vector[ u ] = x * udir; - vector[ v ] = y * vdir; - vector[ w ] = depthHalf; - - // now apply vector to vertex buffer - - vertices.push( vector.x, vector.y, vector.z ); - - // set values to correct vector component - - vector[ u ] = 0; - vector[ v ] = 0; - vector[ w ] = depth > 0 ? 1 : - 1; - - // now apply vector to normal buffer - - normals.push( vector.x, vector.y, vector.z ); - - // uvs - - uvs.push( ix / gridX ); - uvs.push( 1 - ( iy / gridY ) ); - - // counters - - vertexCounter += 1; - - } - - } - - // indices - - // 1. you need three indices to draw a single face - // 2. a single segment consists of two faces - // 3. so we need to generate six (2*3) indices per segment - - for ( let iy = 0; iy < gridY; iy ++ ) { - - for ( let ix = 0; ix < gridX; ix ++ ) { - - const a = numberOfVertices + ix + gridX1 * iy; - const b = numberOfVertices + ix + gridX1 * ( iy + 1 ); - const c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); - const d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - // increase counter - - groupCount += 6; - - } - - } - - // add a group to the geometry. this will ensure multi material support - - scope.addGroup( groupStart, groupCount, materialIndex ); - - // calculate new start value for groups - - groupStart += groupCount; - - // update total number of vertices - - numberOfVertices += vertexCounter; - - } - - } - - } - - /** - * Uniform Utilities - */ - - function cloneUniforms( src ) { - - const dst = {}; - - for ( const u in src ) { - - dst[ u ] = {}; - - for ( const p in src[ u ] ) { - - const property = src[ u ][ p ]; - - if ( property && ( property.isColor || - property.isMatrix3 || property.isMatrix4 || - property.isVector2 || property.isVector3 || property.isVector4 || - property.isTexture ) ) { - - dst[ u ][ p ] = property.clone(); - - } else if ( Array.isArray( property ) ) { - - dst[ u ][ p ] = property.slice(); - - } else { - - dst[ u ][ p ] = property; - - } - - } - - } - - return dst; - - } - - function mergeUniforms( uniforms ) { - - const merged = {}; - - for ( let u = 0; u < uniforms.length; u ++ ) { - - const tmp = cloneUniforms( uniforms[ u ] ); - - for ( const p in tmp ) { - - merged[ p ] = tmp[ p ]; - - } - - } - - return merged; - - } - - // Legacy - - const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; - - var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}"; - - var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}"; - - /** - * parameters = { - * defines: { "label" : "value" }, - * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, - * - * fragmentShader: , - * vertexShader: , - * - * wireframe: , - * wireframeLinewidth: , - * - * lights: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function ShaderMaterial( parameters ) { - - Material.call( this ); - - this.type = 'ShaderMaterial'; - - this.defines = {}; - this.uniforms = {}; - - this.vertexShader = default_vertex; - this.fragmentShader = default_fragment; - - this.linewidth = 1; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; // set to use scene fog - this.lights = false; // set to use scene lights - this.clipping = false; // set to use user-defined clipping planes - - this.skinning = false; // set to use skinning attribute streams - this.morphTargets = false; // set to use morph targets - this.morphNormals = false; // set to use morph normals - - this.extensions = { - derivatives: false, // set to use derivatives - fragDepth: false, // set to use fragment depth values - drawBuffers: false, // set to use draw buffers - shaderTextureLOD: false // set to use shader texture LOD - }; - - // When rendered geometry doesn't include these attributes but the material does, - // use these default values in WebGL. This avoids errors when buffer data is missing. - this.defaultAttributeValues = { - 'color': [ 1, 1, 1 ], - 'uv': [ 0, 0 ], - 'uv2': [ 0, 0 ] - }; - - this.index0AttributeName = undefined; - this.uniformsNeedUpdate = false; - - this.glslVersion = null; - - if ( parameters !== undefined ) { - - if ( parameters.attributes !== undefined ) { - - console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); - - } - - this.setValues( parameters ); - - } - - } - - ShaderMaterial.prototype = Object.create( Material.prototype ); - ShaderMaterial.prototype.constructor = ShaderMaterial; - - ShaderMaterial.prototype.isShaderMaterial = true; - - ShaderMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.fragmentShader = source.fragmentShader; - this.vertexShader = source.vertexShader; - - this.uniforms = cloneUniforms( source.uniforms ); - - this.defines = Object.assign( {}, source.defines ); - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - this.lights = source.lights; - this.clipping = source.clipping; - - this.skinning = source.skinning; - - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - this.extensions = Object.assign( {}, source.extensions ); - - this.glslVersion = source.glslVersion; - - return this; - - }; - - ShaderMaterial.prototype.toJSON = function ( meta ) { - - const data = Material.prototype.toJSON.call( this, meta ); - - data.glslVersion = this.glslVersion; - data.uniforms = {}; - - for ( const name in this.uniforms ) { - - const uniform = this.uniforms[ name ]; - const value = uniform.value; - - if ( value && value.isTexture ) { - - data.uniforms[ name ] = { - type: 't', - value: value.toJSON( meta ).uuid - }; - - } else if ( value && value.isColor ) { - - data.uniforms[ name ] = { - type: 'c', - value: value.getHex() - }; - - } else if ( value && value.isVector2 ) { - - data.uniforms[ name ] = { - type: 'v2', - value: value.toArray() - }; - - } else if ( value && value.isVector3 ) { - - data.uniforms[ name ] = { - type: 'v3', - value: value.toArray() - }; - - } else if ( value && value.isVector4 ) { - - data.uniforms[ name ] = { - type: 'v4', - value: value.toArray() - }; - - } else if ( value && value.isMatrix3 ) { - - data.uniforms[ name ] = { - type: 'm3', - value: value.toArray() - }; - - } else if ( value && value.isMatrix4 ) { - - data.uniforms[ name ] = { - type: 'm4', - value: value.toArray() - }; - - } else { - - data.uniforms[ name ] = { - value: value - }; - - // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far - - } - - } - - if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; - - data.vertexShader = this.vertexShader; - data.fragmentShader = this.fragmentShader; - - const extensions = {}; - - for ( const key in this.extensions ) { - - if ( this.extensions[ key ] === true ) extensions[ key ] = true; - - } - - if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; - - return data; - - }; - - function Camera() { - - Object3D.call( this ); - - this.type = 'Camera'; - - this.matrixWorldInverse = new Matrix4(); - - this.projectionMatrix = new Matrix4(); - this.projectionMatrixInverse = new Matrix4(); - - } - - Camera.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Camera, - - isCamera: true, - - copy: function ( source, recursive ) { - - Object3D.prototype.copy.call( this, source, recursive ); - - this.matrixWorldInverse.copy( source.matrixWorldInverse ); - - this.projectionMatrix.copy( source.projectionMatrix ); - this.projectionMatrixInverse.copy( source.projectionMatrixInverse ); - - return this; - - }, - - getWorldDirection: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Camera: .getWorldDirection() target is now required' ); - target = new Vector3(); - - } - - this.updateMatrixWorld( true ); - - const e = this.matrixWorld.elements; - - return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize(); - - }, - - updateMatrixWorld: function ( force ) { - - Object3D.prototype.updateMatrixWorld.call( this, force ); - - this.matrixWorldInverse.getInverse( this.matrixWorld ); - - }, - - updateWorldMatrix: function ( updateParents, updateChildren ) { - - Object3D.prototype.updateWorldMatrix.call( this, updateParents, updateChildren ); - - this.matrixWorldInverse.getInverse( this.matrixWorld ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - } - - } ); - - function PerspectiveCamera( fov, aspect, near, far ) { - - Camera.call( this ); - - this.type = 'PerspectiveCamera'; - - this.fov = fov !== undefined ? fov : 50; - this.zoom = 1; - - this.near = near !== undefined ? near : 0.1; - this.far = far !== undefined ? far : 2000; - this.focus = 10; - - this.aspect = aspect !== undefined ? aspect : 1; - this.view = null; - - this.filmGauge = 35; // width of the film (default in millimeters) - this.filmOffset = 0; // horizontal film offset (same unit as gauge) - - this.updateProjectionMatrix(); - - } - - PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), { - - constructor: PerspectiveCamera, - - isPerspectiveCamera: true, - - copy: function ( source, recursive ) { - - Camera.prototype.copy.call( this, source, recursive ); - - this.fov = source.fov; - this.zoom = source.zoom; - - this.near = source.near; - this.far = source.far; - this.focus = source.focus; - - this.aspect = source.aspect; - this.view = source.view === null ? null : Object.assign( {}, source.view ); - - this.filmGauge = source.filmGauge; - this.filmOffset = source.filmOffset; - - return this; - - }, - - /** - * Sets the FOV by focal length in respect to the current .filmGauge. - * - * The default film gauge is 35, so that the focal length can be specified for - * a 35mm (full frame) camera. - * - * Values for focal length and film gauge must have the same unit. - */ - setFocalLength: function ( focalLength ) { - - // see http://www.bobatkins.com/photography/technical/field_of_view.html - const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; - - this.fov = MathUtils.RAD2DEG * 2 * Math.atan( vExtentSlope ); - this.updateProjectionMatrix(); - - }, - - /** - * Calculates the focal length from the current .fov and .filmGauge. - */ - getFocalLength: function () { - - const vExtentSlope = Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ); - - return 0.5 * this.getFilmHeight() / vExtentSlope; - - }, - - getEffectiveFOV: function () { - - return MathUtils.RAD2DEG * 2 * Math.atan( - Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom ); - - }, - - getFilmWidth: function () { - - // film not completely covered in portrait format (aspect < 1) - return this.filmGauge * Math.min( this.aspect, 1 ); - - }, - - getFilmHeight: function () { - - // film not completely covered in landscape format (aspect > 1) - return this.filmGauge / Math.max( this.aspect, 1 ); - - }, - - /** - * Sets an offset in a larger frustum. This is useful for multi-window or - * multi-monitor/multi-machine setups. - * - * For example, if you have 3x2 monitors and each monitor is 1920x1080 and - * the monitors are in grid like this - * - * +---+---+---+ - * | A | B | C | - * +---+---+---+ - * | D | E | F | - * +---+---+---+ - * - * then for each monitor you would call it like this - * - * const w = 1920; - * const h = 1080; - * const fullWidth = w * 3; - * const fullHeight = h * 2; - * - * --A-- - * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); - * --B-- - * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); - * --C-- - * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); - * --D-- - * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); - * --E-- - * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); - * --F-- - * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); - * - * Note there is no reason monitors have to be the same size or in a grid. - */ - setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { - - this.aspect = fullWidth / fullHeight; - - if ( this.view === null ) { - - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; - - } - - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; - - this.updateProjectionMatrix(); - - }, - - clearViewOffset: function () { - - if ( this.view !== null ) { - - this.view.enabled = false; - - } - - this.updateProjectionMatrix(); - - }, - - updateProjectionMatrix: function () { - - const near = this.near; - let top = near * Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom; - let height = 2 * top; - let width = this.aspect * height; - let left = - 0.5 * width; - const view = this.view; - - if ( this.view !== null && this.view.enabled ) { - - const fullWidth = view.fullWidth, - fullHeight = view.fullHeight; - - left += view.offsetX * width / fullWidth; - top -= view.offsetY * height / fullHeight; - width *= view.width / fullWidth; - height *= view.height / fullHeight; - - } - - const skew = this.filmOffset; - if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); - - this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); - - this.projectionMatrixInverse.getInverse( this.projectionMatrix ); - - }, - - toJSON: function ( meta ) { - - const data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.fov = this.fov; - data.object.zoom = this.zoom; - - data.object.near = this.near; - data.object.far = this.far; - data.object.focus = this.focus; - - data.object.aspect = this.aspect; - - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - - data.object.filmGauge = this.filmGauge; - data.object.filmOffset = this.filmOffset; - - return data; - - } - - } ); - - const fov = 90, aspect = 1; - - function CubeCamera( near, far, renderTarget ) { - - Object3D.call( this ); - - this.type = 'CubeCamera'; - - if ( renderTarget.isWebGLCubeRenderTarget !== true ) { - - console.error( 'THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter.' ); - return; - - } - - this.renderTarget = renderTarget; - - const cameraPX = new PerspectiveCamera( fov, aspect, near, far ); - cameraPX.layers = this.layers; - cameraPX.up.set( 0, - 1, 0 ); - cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); - this.add( cameraPX ); - - const cameraNX = new PerspectiveCamera( fov, aspect, near, far ); - cameraNX.layers = this.layers; - cameraNX.up.set( 0, - 1, 0 ); - cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); - this.add( cameraNX ); - - const cameraPY = new PerspectiveCamera( fov, aspect, near, far ); - cameraPY.layers = this.layers; - cameraPY.up.set( 0, 0, 1 ); - cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); - this.add( cameraPY ); - - const cameraNY = new PerspectiveCamera( fov, aspect, near, far ); - cameraNY.layers = this.layers; - cameraNY.up.set( 0, 0, - 1 ); - cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); - this.add( cameraNY ); - - const cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraPZ.layers = this.layers; - cameraPZ.up.set( 0, - 1, 0 ); - cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); - this.add( cameraPZ ); - - const cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraNZ.layers = this.layers; - cameraNZ.up.set( 0, - 1, 0 ); - cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); - this.add( cameraNZ ); - - this.update = function ( renderer, scene ) { - - if ( this.parent === null ) this.updateMatrixWorld(); - - const currentXrEnabled = renderer.xr.enabled; - const currentRenderTarget = renderer.getRenderTarget(); - - renderer.xr.enabled = false; - - const generateMipmaps = renderTarget.texture.generateMipmaps; - - renderTarget.texture.generateMipmaps = false; - - renderer.setRenderTarget( renderTarget, 0 ); - renderer.render( scene, cameraPX ); - - renderer.setRenderTarget( renderTarget, 1 ); - renderer.render( scene, cameraNX ); - - renderer.setRenderTarget( renderTarget, 2 ); - renderer.render( scene, cameraPY ); - - renderer.setRenderTarget( renderTarget, 3 ); - renderer.render( scene, cameraNY ); - - renderer.setRenderTarget( renderTarget, 4 ); - renderer.render( scene, cameraPZ ); - - renderTarget.texture.generateMipmaps = generateMipmaps; - - renderer.setRenderTarget( renderTarget, 5 ); - renderer.render( scene, cameraNZ ); - - renderer.setRenderTarget( currentRenderTarget ); - - renderer.xr.enabled = currentXrEnabled; - - }; - - this.clear = function ( renderer, color, depth, stencil ) { - - const currentRenderTarget = renderer.getRenderTarget(); - - for ( let i = 0; i < 6; i ++ ) { - - renderer.setRenderTarget( renderTarget, i ); - - renderer.clear( color, depth, stencil ); - - } - - renderer.setRenderTarget( currentRenderTarget ); - - }; - - } - - CubeCamera.prototype = Object.create( Object3D.prototype ); - CubeCamera.prototype.constructor = CubeCamera; - - function WebGLCubeRenderTarget( size, options, dummy ) { - - if ( Number.isInteger( options ) ) { - - console.warn( 'THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )' ); - - options = dummy; - - } - - WebGLRenderTarget.call( this, size, size, options ); - - this.texture.isWebGLCubeRenderTargetTexture = true; // HACK Why is texture not a CubeTexture? - - } - - WebGLCubeRenderTarget.prototype = Object.create( WebGLRenderTarget.prototype ); - WebGLCubeRenderTarget.prototype.constructor = WebGLCubeRenderTarget; - - WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; - - WebGLCubeRenderTarget.prototype.fromEquirectangularTexture = function ( renderer, texture ) { - - this.texture.type = texture.type; - this.texture.format = RGBAFormat; // see #18859 - this.texture.encoding = texture.encoding; - - this.texture.generateMipmaps = texture.generateMipmaps; - this.texture.minFilter = texture.minFilter; - this.texture.magFilter = texture.magFilter; - - const shader = { - - uniforms: { - tEquirect: { value: null }, - }, - - vertexShader: /* glsl */` - - varying vec3 vWorldDirection; - - vec3 transformDirection( in vec3 dir, in mat4 matrix ) { - - return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); - - } - - void main() { - - vWorldDirection = transformDirection( position, modelMatrix ); - - #include - #include - - } - `, - - fragmentShader: /* glsl */` - - uniform sampler2D tEquirect; - - varying vec3 vWorldDirection; - - #include - - void main() { - - vec3 direction = normalize( vWorldDirection ); - - vec2 sampleUV = equirectUv( direction ); - - gl_FragColor = texture2D( tEquirect, sampleUV ); - - } - ` - }; - - const geometry = new BoxBufferGeometry( 5, 5, 5 ); - - const material = new ShaderMaterial( { - - name: 'CubemapFromEquirect', - - uniforms: cloneUniforms( shader.uniforms ), - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader, - side: BackSide, - blending: NoBlending - - } ); - - material.uniforms.tEquirect.value = texture; - - const mesh = new Mesh( geometry, material ); - - const currentMinFilter = texture.minFilter; - - // Avoid blurred poles - if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter; - - const camera = new CubeCamera( 1, 10, this ); - camera.update( renderer, mesh ); - - texture.minFilter = currentMinFilter; - - mesh.geometry.dispose(); - mesh.material.dispose(); - - return this; - - }; - - function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.image = { data: data || null, width: width || 1, height: height || 1 }; - - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; - - this.needsUpdate = true; - - } - - DataTexture.prototype = Object.create( Texture.prototype ); - DataTexture.prototype.constructor = DataTexture; - - DataTexture.prototype.isDataTexture = true; - - const _sphere$1 = new Sphere(); - const _vector$5 = new Vector3(); - - class Frustum { - - constructor( p0, p1, p2, p3, p4, p5 ) { - - this.planes = [ - - ( p0 !== undefined ) ? p0 : new Plane(), - ( p1 !== undefined ) ? p1 : new Plane(), - ( p2 !== undefined ) ? p2 : new Plane(), - ( p3 !== undefined ) ? p3 : new Plane(), - ( p4 !== undefined ) ? p4 : new Plane(), - ( p5 !== undefined ) ? p5 : new Plane() - - ]; - - } - - set( p0, p1, p2, p3, p4, p5 ) { - - const planes = this.planes; - - planes[ 0 ].copy( p0 ); - planes[ 1 ].copy( p1 ); - planes[ 2 ].copy( p2 ); - planes[ 3 ].copy( p3 ); - planes[ 4 ].copy( p4 ); - planes[ 5 ].copy( p5 ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( frustum ) { - - const planes = this.planes; - - for ( let i = 0; i < 6; i ++ ) { - - planes[ i ].copy( frustum.planes[ i ] ); - - } - - return this; - - } - - setFromProjectionMatrix( m ) { - - const planes = this.planes; - const me = m.elements; - const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; - const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; - const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; - const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; - - planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); - planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); - planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); - planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); - planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); - planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); - - return this; - - } - - intersectsObject( object ) { - - const geometry = object.geometry; - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _sphere$1.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); - - return this.intersectsSphere( _sphere$1 ); - - } - - intersectsSprite( sprite ) { - - _sphere$1.center.set( 0, 0, 0 ); - _sphere$1.radius = 0.7071067811865476; - _sphere$1.applyMatrix4( sprite.matrixWorld ); - - return this.intersectsSphere( _sphere$1 ); - - } - - intersectsSphere( sphere ) { - - const planes = this.planes; - const center = sphere.center; - const negRadius = - sphere.radius; - - for ( let i = 0; i < 6; i ++ ) { - - const distance = planes[ i ].distanceToPoint( center ); - - if ( distance < negRadius ) { - - return false; - - } - - } - - return true; - - } - - intersectsBox( box ) { - - const planes = this.planes; - - for ( let i = 0; i < 6; i ++ ) { - - const plane = planes[ i ]; - - // corner at max distance - - _vector$5.x = plane.normal.x > 0 ? box.max.x : box.min.x; - _vector$5.y = plane.normal.y > 0 ? box.max.y : box.min.y; - _vector$5.z = plane.normal.z > 0 ? box.max.z : box.min.z; - - if ( plane.distanceToPoint( _vector$5 ) < 0 ) { - - return false; - - } - - } - - return true; - - } - - containsPoint( point ) { - - const planes = this.planes; - - for ( let i = 0; i < 6; i ++ ) { - - if ( planes[ i ].distanceToPoint( point ) < 0 ) { - - return false; - - } - - } - - return true; - - } - - } - - function WebGLAnimation() { - - let context = null; - let isAnimating = false; - let animationLoop = null; - let requestId = null; - - function onAnimationFrame( time, frame ) { - - animationLoop( time, frame ); - - requestId = context.requestAnimationFrame( onAnimationFrame ); - - } - - return { - - start: function () { - - if ( isAnimating === true ) return; - if ( animationLoop === null ) return; - - requestId = context.requestAnimationFrame( onAnimationFrame ); - - isAnimating = true; - - }, - - stop: function () { - - context.cancelAnimationFrame( requestId ); - - isAnimating = false; - - }, - - setAnimationLoop: function ( callback ) { - - animationLoop = callback; - - }, - - setContext: function ( value ) { - - context = value; - - } - - }; - - } - - function WebGLAttributes( gl, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - const buffers = new WeakMap(); - - function createBuffer( attribute, bufferType ) { - - const array = attribute.array; - const usage = attribute.usage; - - const buffer = gl.createBuffer(); - - gl.bindBuffer( bufferType, buffer ); - gl.bufferData( bufferType, array, usage ); - - attribute.onUploadCallback(); - - let type = 5126; - - if ( array instanceof Float32Array ) { - - type = 5126; - - } else if ( array instanceof Float64Array ) { - - console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' ); - - } else if ( array instanceof Uint16Array ) { - - type = 5123; - - } else if ( array instanceof Int16Array ) { - - type = 5122; - - } else if ( array instanceof Uint32Array ) { - - type = 5125; - - } else if ( array instanceof Int32Array ) { - - type = 5124; - - } else if ( array instanceof Int8Array ) { - - type = 5120; - - } else if ( array instanceof Uint8Array ) { - - type = 5121; - - } - - return { - buffer: buffer, - type: type, - bytesPerElement: array.BYTES_PER_ELEMENT, - version: attribute.version - }; - - } - - function updateBuffer( buffer, attribute, bufferType ) { - - const array = attribute.array; - const updateRange = attribute.updateRange; - - gl.bindBuffer( bufferType, buffer ); - - if ( updateRange.count === - 1 ) { - - // Not using update ranges - - gl.bufferSubData( bufferType, 0, array ); - - } else { - - if ( isWebGL2 ) { - - gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, - array, updateRange.offset, updateRange.count ); - - } else { - - gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, - array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); - - } - - updateRange.count = - 1; // reset range - - } - - } - - // - - function get( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - - return buffers.get( attribute ); - - } - - function remove( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - - const data = buffers.get( attribute ); - - if ( data ) { - - gl.deleteBuffer( data.buffer ); - - buffers.delete( attribute ); - - } - - } - - function update( attribute, bufferType ) { - - if ( attribute.isGLBufferAttribute ) { - - var cached = buffers.get( attribute ); - - if ( ! cached || cached.version < attribute.version ) { - - buffers.set( attribute, { - buffer: attribute.buffer, - type: attribute.type, - bytesPerElement: attribute.elementSize, - version: attribute.version - } ); - - } - - return; - - } - - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - - const data = buffers.get( attribute ); - - if ( data === undefined ) { - - buffers.set( attribute, createBuffer( attribute, bufferType ) ); - - } else if ( data.version < attribute.version ) { - - updateBuffer( data.buffer, attribute, bufferType ); - - data.version = attribute.version; - - } - - } - - return { - - get: get, - remove: remove, - update: update - - }; - - } - - // PlaneGeometry - - class PlaneGeometry extends Geometry { - - constructor( width, height, widthSegments, heightSegments ) { - - super(); - - this.type = 'PlaneGeometry'; - - this.parameters = { - width: width, - height: height, - widthSegments: widthSegments, - heightSegments: heightSegments - }; - - this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); - this.mergeVertices(); - - } - - } - - // PlaneBufferGeometry - - class PlaneBufferGeometry extends BufferGeometry { - - constructor( width, height, widthSegments, heightSegments ) { - - super(); - this.type = 'PlaneBufferGeometry'; - - this.parameters = { - width: width, - height: height, - widthSegments: widthSegments, - heightSegments: heightSegments - }; - - width = width || 1; - height = height || 1; - - const width_half = width / 2; - const height_half = height / 2; - - const gridX = Math.floor( widthSegments ) || 1; - const gridY = Math.floor( heightSegments ) || 1; - - const gridX1 = gridX + 1; - const gridY1 = gridY + 1; - - const segment_width = width / gridX; - const segment_height = height / gridY; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // generate vertices, normals and uvs - - for ( let iy = 0; iy < gridY1; iy ++ ) { - - const y = iy * segment_height - height_half; - - for ( let ix = 0; ix < gridX1; ix ++ ) { - - const x = ix * segment_width - width_half; - - vertices.push( x, - y, 0 ); - - normals.push( 0, 0, 1 ); - - uvs.push( ix / gridX ); - uvs.push( 1 - ( iy / gridY ) ); - - } - - } - - // indices - - for ( let iy = 0; iy < gridY; iy ++ ) { - - for ( let ix = 0; ix < gridX; ix ++ ) { - - const a = ix + gridX1 * iy; - const b = ix + gridX1 * ( iy + 1 ); - const c = ( ix + 1 ) + gridX1 * ( iy + 1 ); - const d = ( ix + 1 ) + gridX1 * iy; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - } - - var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif"; - - var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; - - var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif"; - - var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif"; - - var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; - - var begin_vertex = "vec3 transformed = vec3( position );"; - - var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif"; - - var bsdfs = "vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\treturn vec2( -1.04, 1.04 ) * a004 + r.zw;\n}\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\tif( cutoffDistance > 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nvec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );\n\tvec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;\n\treturn Fr * fresnel + F0;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie(float roughness, float NoH) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max(1.0 - cos2h, 0.0078125);\treturn (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);\n}\nfloat V_Neubelt(float NoV, float NoL) {\n\treturn saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));\n}\nvec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif"; - - var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tfDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif"; - - var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#pragma unroll_loop_end\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t\tif ( clipped ) discard;\n\t#endif\n#endif"; - - var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif"; - - var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n#endif"; - - var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif"; - - var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif"; - - var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; - - var color_pars_vertex = "#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvarying vec3 vColor;\n#endif"; - - var color_vertex = "#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor.xyz *= color.xyz;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif"; - - var common = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}"; - - var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_maxMipLevel 8.0\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_maxTileSize 256.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tfloat texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 );\n\t\tvec2 f = fract( uv );\n\t\tuv += 0.5 - f;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tif ( mipInt < cubeUV_maxMipLevel ) {\n\t\t\tuv.y += 2.0 * cubeUV_maxTileSize;\n\t\t}\n\t\tuv.y += filterInt * 2.0 * cubeUV_minTileSize;\n\t\tuv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize );\n\t\tuv *= texelSize;\n\t\tvec3 tl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x += texelSize;\n\t\tvec3 tr = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.y += texelSize;\n\t\tvec3 br = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x -= texelSize;\n\t\tvec3 bl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tvec3 tm = mix( tl, tr, f.x );\n\t\tvec3 bm = mix( bl, br, f.x );\n\t\treturn mix( tm, bm, f.y );\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif"; - - var defaultnormal_vertex = "vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif"; - - var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif"; - - var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias );\n#endif"; - - var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; - - var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif"; - - var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; - - var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = clamp( floor( D ) / 255.0, 0.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = cLogLuvM * value.rgb;\n\tXp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract( Le );\n\tvResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}"; - - var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif"; - - var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; - - var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif"; - - var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif"; - - var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif"; - - var fog_vertex = "#ifdef USE_FOG\n\tfogDepth = - mvPosition.z;\n#endif"; - - var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif"; - - var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; - - var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif"; - - var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn texture2D( gradientMap, coord ).rgb;\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}"; - - var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\treflectedLight.indirectDiffuse += PI * lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n#endif"; - - var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif"; - - var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif"; - - var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif"; - - var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float roughness, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat sigma = PI * roughness * roughness / ( 1.0 + roughness );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + log2( sigma );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -viewDir, normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( roughness, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif"; - - var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;"; - - var lights_toon_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)"; - - var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;"; - - var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)"; - - var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.specularRoughness = max( roughnessFactor, 0.0525 );material.specularRoughness += geometryRoughness;\nmaterial.specularRoughness = min( material.specularRoughness, 1.0 );\n#ifdef REFLECTIVITY\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#endif\n#ifdef CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheen;\n#endif"; - - var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat specularRoughness;\n\tvec3 specularColor;\n#ifdef CLEARCOAT\n\tfloat clearcoat;\n\tfloat clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tvec3 sheenColor;\n#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearcoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = ccDotNL * directLight.color;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tccIrradiance *= PI;\n\t\t#endif\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t\treflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(\n\t\t\tmaterial.specularRoughness,\n\t\t\tdirectLight.direction,\n\t\t\tgeometry,\n\t\t\tmaterial.sheenColor\n\t\t);\n\t#else\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);\n\t#endif\n\treflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t\tfloat ccDotNL = ccDotNV;\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\tfloat clearcoatInv = 1.0 - clearcoatDHR;\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; - - var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; - - var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.normal, material.specularRoughness, maxMipLevel );\n\t#ifdef CLEARCOAT\n\t\tclearcoatRadiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, maxMipLevel );\n\t#endif\n#endif"; - - var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif"; - - var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif"; - - var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n\tvarying float vIsPerspective;\n#endif"; - - var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t\tvarying float vIsPerspective;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif"; - - var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif"; - - var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif"; - - var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif"; - - var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif"; - - var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tuniform mat3 uvTransform;\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; - - var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif"; - - var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; - - var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n#endif"; - - var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifndef USE_MORPHNORMALS\n\t\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\t\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; - - var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t#endif\n#endif"; - - var normal_fragment_begin = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t\tbitangent = bitangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;"; - - var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal, mapN );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif"; - - var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\n\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\tvec3 N = normalize( surf_norm );\n\t\tmat3 tsn = mat3( S, T, N );\n\t\tmapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif"; - - var clearcoat_normal_fragment_begin = "#ifdef CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; - - var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN );\n\t#endif\n#endif"; - - var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif"; - - var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ));\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w);\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}"; - - var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; - - var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 );\n#ifdef USE_INSTANCING\n\tmvPosition = instanceMatrix * mvPosition;\n#endif\nmvPosition = modelViewMatrix * mvPosition;\ngl_Position = projectionMatrix * mvPosition;"; - - var dithering_fragment = "#ifdef DITHERING\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif"; - - var dithering_pars_fragment = "#ifdef DITHERING\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif"; - - var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif"; - - var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; - - var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\tfloat hard_shadow = step( compare , distribution.x );\n\t\tif (hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif"; - - var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif"; - - var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0\n\t\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\tvec4 shadowWorldPosition;\n\t#endif\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n#endif"; - - var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}"; - - var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; - - var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform highp sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif"; - - var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif"; - - var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif"; - - var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif"; - - var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif"; - - var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif"; - - var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; - - var transmissionmap_fragment = "#ifdef USE_TRANSMISSIONMAP\n\ttotalTransmission *= texture2D( transmissionMap, vUv ).r;\n#endif"; - - var transmissionmap_pars_fragment = "#ifdef USE_TRANSMISSIONMAP\n\tuniform sampler2D transmissionMap;\n#endif"; - - var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )\n\tvarying vec2 vUv;\n#endif"; - - var uv_pars_vertex = "#ifdef USE_UV\n\t#ifdef UVS_VERTEX_ONLY\n\t\tvec2 vUv;\n\t#else\n\t\tvarying vec2 vUv;\n\t#endif\n\tuniform mat3 uvTransform;\n#endif"; - - var uv_vertex = "#ifdef USE_UV\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif"; - - var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif"; - - var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n\tuniform mat3 uv2Transform;\n#endif"; - - var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n#endif"; - - var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; - - var background_frag = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; - - var background_vert = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; - - var cube_frag = "#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; - - var cube_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; - - var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; - - var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}"; - - var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; - - var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; - - var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; - - var equirect_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; - - var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshmatcap_frag = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshmatcap_vert = "#define MATCAP\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; - - var meshtoon_frag = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshtoon_vert = "#define TOON\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; - - var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshphysical_frag = "#define STANDARD\n#ifdef PHYSICAL\n\t#define REFLECTIVITY\n\t#define CLEARCOAT\n\t#define TRANSMISSION\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef TRANSMISSION\n\tuniform float transmission;\n#endif\n#ifdef REFLECTIVITY\n\tuniform float reflectivity;\n#endif\n#ifdef CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheen;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#ifdef TRANSMISSION\n\t\tfloat totalTransmission = transmission;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#ifdef TRANSMISSION\n\t\tdiffuseColor.a *= saturate( 1. - totalTransmission + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) );\n\t#endif\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshphysical_vert = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; - - var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}"; - - var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; - - var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var points_vert = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}"; - - var shadow_vert = "#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var sprite_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n}"; - - var sprite_vert = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; - - const ShaderChunk = { - alphamap_fragment: alphamap_fragment, - alphamap_pars_fragment: alphamap_pars_fragment, - alphatest_fragment: alphatest_fragment, - aomap_fragment: aomap_fragment, - aomap_pars_fragment: aomap_pars_fragment, - begin_vertex: begin_vertex, - beginnormal_vertex: beginnormal_vertex, - bsdfs: bsdfs, - bumpmap_pars_fragment: bumpmap_pars_fragment, - clipping_planes_fragment: clipping_planes_fragment, - clipping_planes_pars_fragment: clipping_planes_pars_fragment, - clipping_planes_pars_vertex: clipping_planes_pars_vertex, - clipping_planes_vertex: clipping_planes_vertex, - color_fragment: color_fragment, - color_pars_fragment: color_pars_fragment, - color_pars_vertex: color_pars_vertex, - color_vertex: color_vertex, - common: common, - cube_uv_reflection_fragment: cube_uv_reflection_fragment, - defaultnormal_vertex: defaultnormal_vertex, - displacementmap_pars_vertex: displacementmap_pars_vertex, - displacementmap_vertex: displacementmap_vertex, - emissivemap_fragment: emissivemap_fragment, - emissivemap_pars_fragment: emissivemap_pars_fragment, - encodings_fragment: encodings_fragment, - encodings_pars_fragment: encodings_pars_fragment, - envmap_fragment: envmap_fragment, - envmap_common_pars_fragment: envmap_common_pars_fragment, - envmap_pars_fragment: envmap_pars_fragment, - envmap_pars_vertex: envmap_pars_vertex, - envmap_physical_pars_fragment: envmap_physical_pars_fragment, - envmap_vertex: envmap_vertex, - fog_vertex: fog_vertex, - fog_pars_vertex: fog_pars_vertex, - fog_fragment: fog_fragment, - fog_pars_fragment: fog_pars_fragment, - gradientmap_pars_fragment: gradientmap_pars_fragment, - lightmap_fragment: lightmap_fragment, - lightmap_pars_fragment: lightmap_pars_fragment, - lights_lambert_vertex: lights_lambert_vertex, - lights_pars_begin: lights_pars_begin, - lights_toon_fragment: lights_toon_fragment, - lights_toon_pars_fragment: lights_toon_pars_fragment, - lights_phong_fragment: lights_phong_fragment, - lights_phong_pars_fragment: lights_phong_pars_fragment, - lights_physical_fragment: lights_physical_fragment, - lights_physical_pars_fragment: lights_physical_pars_fragment, - lights_fragment_begin: lights_fragment_begin, - lights_fragment_maps: lights_fragment_maps, - lights_fragment_end: lights_fragment_end, - logdepthbuf_fragment: logdepthbuf_fragment, - logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, - logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, - logdepthbuf_vertex: logdepthbuf_vertex, - map_fragment: map_fragment, - map_pars_fragment: map_pars_fragment, - map_particle_fragment: map_particle_fragment, - map_particle_pars_fragment: map_particle_pars_fragment, - metalnessmap_fragment: metalnessmap_fragment, - metalnessmap_pars_fragment: metalnessmap_pars_fragment, - morphnormal_vertex: morphnormal_vertex, - morphtarget_pars_vertex: morphtarget_pars_vertex, - morphtarget_vertex: morphtarget_vertex, - normal_fragment_begin: normal_fragment_begin, - normal_fragment_maps: normal_fragment_maps, - normalmap_pars_fragment: normalmap_pars_fragment, - clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, - clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, - clearcoat_pars_fragment: clearcoat_pars_fragment, - packing: packing, - premultiplied_alpha_fragment: premultiplied_alpha_fragment, - project_vertex: project_vertex, - dithering_fragment: dithering_fragment, - dithering_pars_fragment: dithering_pars_fragment, - roughnessmap_fragment: roughnessmap_fragment, - roughnessmap_pars_fragment: roughnessmap_pars_fragment, - shadowmap_pars_fragment: shadowmap_pars_fragment, - shadowmap_pars_vertex: shadowmap_pars_vertex, - shadowmap_vertex: shadowmap_vertex, - shadowmask_pars_fragment: shadowmask_pars_fragment, - skinbase_vertex: skinbase_vertex, - skinning_pars_vertex: skinning_pars_vertex, - skinning_vertex: skinning_vertex, - skinnormal_vertex: skinnormal_vertex, - specularmap_fragment: specularmap_fragment, - specularmap_pars_fragment: specularmap_pars_fragment, - tonemapping_fragment: tonemapping_fragment, - tonemapping_pars_fragment: tonemapping_pars_fragment, - transmissionmap_fragment: transmissionmap_fragment, - transmissionmap_pars_fragment: transmissionmap_pars_fragment, - uv_pars_fragment: uv_pars_fragment, - uv_pars_vertex: uv_pars_vertex, - uv_vertex: uv_vertex, - uv2_pars_fragment: uv2_pars_fragment, - uv2_pars_vertex: uv2_pars_vertex, - uv2_vertex: uv2_vertex, - worldpos_vertex: worldpos_vertex, - - background_frag: background_frag, - background_vert: background_vert, - cube_frag: cube_frag, - cube_vert: cube_vert, - depth_frag: depth_frag, - depth_vert: depth_vert, - distanceRGBA_frag: distanceRGBA_frag, - distanceRGBA_vert: distanceRGBA_vert, - equirect_frag: equirect_frag, - equirect_vert: equirect_vert, - linedashed_frag: linedashed_frag, - linedashed_vert: linedashed_vert, - meshbasic_frag: meshbasic_frag, - meshbasic_vert: meshbasic_vert, - meshlambert_frag: meshlambert_frag, - meshlambert_vert: meshlambert_vert, - meshmatcap_frag: meshmatcap_frag, - meshmatcap_vert: meshmatcap_vert, - meshtoon_frag: meshtoon_frag, - meshtoon_vert: meshtoon_vert, - meshphong_frag: meshphong_frag, - meshphong_vert: meshphong_vert, - meshphysical_frag: meshphysical_frag, - meshphysical_vert: meshphysical_vert, - normal_frag: normal_frag, - normal_vert: normal_vert, - points_frag: points_frag, - points_vert: points_vert, - shadow_frag: shadow_frag, - shadow_vert: shadow_vert, - sprite_frag: sprite_frag, - sprite_vert: sprite_vert - }; - - /** - * Uniforms library for shared webgl shaders - */ - - const UniformsLib = { - - common: { - - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - - map: { value: null }, - uvTransform: { value: new Matrix3() }, - uv2Transform: { value: new Matrix3() }, - - alphaMap: { value: null }, - - }, - - specularmap: { - - specularMap: { value: null }, - - }, - - envmap: { - - envMap: { value: null }, - flipEnvMap: { value: - 1 }, - reflectivity: { value: 1.0 }, - refractionRatio: { value: 0.98 }, - maxMipLevel: { value: 0 } - - }, - - aomap: { - - aoMap: { value: null }, - aoMapIntensity: { value: 1 } - - }, - - lightmap: { - - lightMap: { value: null }, - lightMapIntensity: { value: 1 } - - }, - - emissivemap: { - - emissiveMap: { value: null } - - }, - - bumpmap: { - - bumpMap: { value: null }, - bumpScale: { value: 1 } - - }, - - normalmap: { - - normalMap: { value: null }, - normalScale: { value: new Vector2( 1, 1 ) } - - }, - - displacementmap: { - - displacementMap: { value: null }, - displacementScale: { value: 1 }, - displacementBias: { value: 0 } - - }, - - roughnessmap: { - - roughnessMap: { value: null } - - }, - - metalnessmap: { - - metalnessMap: { value: null } - - }, - - gradientmap: { - - gradientMap: { value: null } - - }, - - fog: { - - fogDensity: { value: 0.00025 }, - fogNear: { value: 1 }, - fogFar: { value: 2000 }, - fogColor: { value: new Color( 0xffffff ) } - - }, - - lights: { - - ambientLightColor: { value: [] }, - - lightProbe: { value: [] }, - - directionalLights: { value: [], properties: { - direction: {}, - color: {} - } }, - - directionalLightShadows: { value: [], properties: { - shadowBias: {}, - shadowNormalBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - directionalShadowMap: { value: [] }, - directionalShadowMatrix: { value: [] }, - - spotLights: { value: [], properties: { - color: {}, - position: {}, - direction: {}, - distance: {}, - coneCos: {}, - penumbraCos: {}, - decay: {} - } }, - - spotLightShadows: { value: [], properties: { - shadowBias: {}, - shadowNormalBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - spotShadowMap: { value: [] }, - spotShadowMatrix: { value: [] }, - - pointLights: { value: [], properties: { - color: {}, - position: {}, - decay: {}, - distance: {} - } }, - - pointLightShadows: { value: [], properties: { - shadowBias: {}, - shadowNormalBias: {}, - shadowRadius: {}, - shadowMapSize: {}, - shadowCameraNear: {}, - shadowCameraFar: {} - } }, - - pointShadowMap: { value: [] }, - pointShadowMatrix: { value: [] }, - - hemisphereLights: { value: [], properties: { - direction: {}, - skyColor: {}, - groundColor: {} - } }, - - // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src - rectAreaLights: { value: [], properties: { - color: {}, - position: {}, - width: {}, - height: {} - } }, - - ltc_1: { value: null }, - ltc_2: { value: null } - - }, - - points: { - - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - size: { value: 1.0 }, - scale: { value: 1.0 }, - map: { value: null }, - alphaMap: { value: null }, - uvTransform: { value: new Matrix3() } - - }, - - sprite: { - - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - center: { value: new Vector2( 0.5, 0.5 ) }, - rotation: { value: 0.0 }, - map: { value: null }, - alphaMap: { value: null }, - uvTransform: { value: new Matrix3() } - - } - - }; - - const ShaderLib = { - - basic: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.meshbasic_vert, - fragmentShader: ShaderChunk.meshbasic_frag - - }, - - lambert: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) } - } - ] ), - - vertexShader: ShaderChunk.meshlambert_vert, - fragmentShader: ShaderChunk.meshlambert_frag - - }, - - phong: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - specular: { value: new Color( 0x111111 ) }, - shininess: { value: 30 } - } - ] ), - - vertexShader: ShaderChunk.meshphong_vert, - fragmentShader: ShaderChunk.meshphong_frag - - }, - - standard: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.roughnessmap, - UniformsLib.metalnessmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - roughness: { value: 1.0 }, - metalness: { value: 0.0 }, - envMapIntensity: { value: 1 } // temporary - } - ] ), - - vertexShader: ShaderChunk.meshphysical_vert, - fragmentShader: ShaderChunk.meshphysical_frag - - }, - - toon: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.gradientmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) } - } - ] ), - - vertexShader: ShaderChunk.meshtoon_vert, - fragmentShader: ShaderChunk.meshtoon_frag - - }, - - matcap: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.fog, - { - matcap: { value: null } - } - ] ), - - vertexShader: ShaderChunk.meshmatcap_vert, - fragmentShader: ShaderChunk.meshmatcap_frag - - }, - - points: { - - uniforms: mergeUniforms( [ - UniformsLib.points, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.points_vert, - fragmentShader: ShaderChunk.points_frag - - }, - - dashed: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.fog, - { - scale: { value: 1 }, - dashSize: { value: 1 }, - totalSize: { value: 2 } - } - ] ), - - vertexShader: ShaderChunk.linedashed_vert, - fragmentShader: ShaderChunk.linedashed_frag - - }, - - depth: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.displacementmap - ] ), - - vertexShader: ShaderChunk.depth_vert, - fragmentShader: ShaderChunk.depth_frag - - }, - - normal: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - { - opacity: { value: 1.0 } - } - ] ), - - vertexShader: ShaderChunk.normal_vert, - fragmentShader: ShaderChunk.normal_frag - - }, - - sprite: { - - uniforms: mergeUniforms( [ - UniformsLib.sprite, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.sprite_vert, - fragmentShader: ShaderChunk.sprite_frag - - }, - - background: { - - uniforms: { - uvTransform: { value: new Matrix3() }, - t2D: { value: null }, - }, - - vertexShader: ShaderChunk.background_vert, - fragmentShader: ShaderChunk.background_frag - - }, - /* ------------------------------------------------------------------------- - // Cube map shader - ------------------------------------------------------------------------- */ - - cube: { - - uniforms: mergeUniforms( [ - UniformsLib.envmap, - { - opacity: { value: 1.0 } - } - ] ), - - vertexShader: ShaderChunk.cube_vert, - fragmentShader: ShaderChunk.cube_frag - - }, - - equirect: { - - uniforms: { - tEquirect: { value: null }, - }, - - vertexShader: ShaderChunk.equirect_vert, - fragmentShader: ShaderChunk.equirect_frag - - }, - - distanceRGBA: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.displacementmap, - { - referencePosition: { value: new Vector3() }, - nearDistance: { value: 1 }, - farDistance: { value: 1000 } - } - ] ), - - vertexShader: ShaderChunk.distanceRGBA_vert, - fragmentShader: ShaderChunk.distanceRGBA_frag - - }, - - shadow: { - - uniforms: mergeUniforms( [ - UniformsLib.lights, - UniformsLib.fog, - { - color: { value: new Color( 0x00000 ) }, - opacity: { value: 1.0 } - }, - ] ), - - vertexShader: ShaderChunk.shadow_vert, - fragmentShader: ShaderChunk.shadow_frag - - } - - }; - - ShaderLib.physical = { - - uniforms: mergeUniforms( [ - ShaderLib.standard.uniforms, - { - clearcoat: { value: 0 }, - clearcoatMap: { value: null }, - clearcoatRoughness: { value: 0 }, - clearcoatRoughnessMap: { value: null }, - clearcoatNormalScale: { value: new Vector2( 1, 1 ) }, - clearcoatNormalMap: { value: null }, - sheen: { value: new Color( 0x000000 ) }, - transmission: { value: 0 }, - transmissionMap: { value: null }, - } - ] ), - - vertexShader: ShaderChunk.meshphysical_vert, - fragmentShader: ShaderChunk.meshphysical_frag - - }; - - function WebGLBackground( renderer, cubemaps, state, objects, premultipliedAlpha ) { - - const clearColor = new Color( 0x000000 ); - let clearAlpha = 0; - - let planeMesh; - let boxMesh; - - let currentBackground = null; - let currentBackgroundVersion = 0; - let currentTonemapping = null; - - function render( renderList, scene, camera, forceClear ) { - - let background = scene.isScene === true ? scene.background : null; - - if ( background && background.isTexture ) { - - background = cubemaps.get( background ); - - } - - // Ignore background in AR - // TODO: Reconsider this. - - const xr = renderer.xr; - const session = xr.getSession && xr.getSession(); - - if ( session && session.environmentBlendMode === 'additive' ) { - - background = null; - - } - - if ( background === null ) { - - setClear( clearColor, clearAlpha ); - - } else if ( background && background.isColor ) { - - setClear( background, 1 ); - forceClear = true; - - } - - if ( renderer.autoClear || forceClear ) { - - renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); - - } - - if ( background && ( background.isCubeTexture || background.isWebGLCubeRenderTarget || background.isWebGLCubeRenderTargetTexture || background.mapping === CubeUVReflectionMapping ) ) { - - if ( boxMesh === undefined ) { - - boxMesh = new Mesh( - new BoxBufferGeometry( 1, 1, 1 ), - new ShaderMaterial( { - name: 'BackgroundCubeMaterial', - uniforms: cloneUniforms( ShaderLib.cube.uniforms ), - vertexShader: ShaderLib.cube.vertexShader, - fragmentShader: ShaderLib.cube.fragmentShader, - side: BackSide, - depthTest: false, - depthWrite: false, - fog: false - } ) - ); - - boxMesh.geometry.deleteAttribute( 'normal' ); - boxMesh.geometry.deleteAttribute( 'uv' ); - - boxMesh.onBeforeRender = function ( renderer, scene, camera ) { - - this.matrixWorld.copyPosition( camera.matrixWorld ); - - }; - - // enable code injection for non-built-in material - Object.defineProperty( boxMesh.material, 'envMap', { - - get: function () { - - return this.uniforms.envMap.value; - - } - - } ); - - objects.update( boxMesh ); - - } - - if ( background.isWebGLCubeRenderTarget ) { - - // TODO Deprecate - - background = background.texture; - - } - - boxMesh.material.uniforms.envMap.value = background; - boxMesh.material.uniforms.flipEnvMap.value = background.isCubeTexture ? - 1 : 1; - - if ( currentBackground !== background || - currentBackgroundVersion !== background.version || - currentTonemapping !== renderer.toneMapping ) { - - boxMesh.material.needsUpdate = true; - - currentBackground = background; - currentBackgroundVersion = background.version; - currentTonemapping = renderer.toneMapping; - - } - - // push to the pre-sorted opaque render list - renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); - - } else if ( background && background.isTexture ) { - - if ( planeMesh === undefined ) { - - planeMesh = new Mesh( - new PlaneBufferGeometry( 2, 2 ), - new ShaderMaterial( { - name: 'BackgroundMaterial', - uniforms: cloneUniforms( ShaderLib.background.uniforms ), - vertexShader: ShaderLib.background.vertexShader, - fragmentShader: ShaderLib.background.fragmentShader, - side: FrontSide, - depthTest: false, - depthWrite: false, - fog: false - } ) - ); - - planeMesh.geometry.deleteAttribute( 'normal' ); - - // enable code injection for non-built-in material - Object.defineProperty( planeMesh.material, 'map', { - - get: function () { - - return this.uniforms.t2D.value; - - } - - } ); - - objects.update( planeMesh ); - - } - - planeMesh.material.uniforms.t2D.value = background; - - if ( background.matrixAutoUpdate === true ) { - - background.updateMatrix(); - - } - - planeMesh.material.uniforms.uvTransform.value.copy( background.matrix ); - - if ( currentBackground !== background || - currentBackgroundVersion !== background.version || - currentTonemapping !== renderer.toneMapping ) { - - planeMesh.material.needsUpdate = true; - - currentBackground = background; - currentBackgroundVersion = background.version; - currentTonemapping = renderer.toneMapping; - - } - - - // push to the pre-sorted opaque render list - renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null ); - - } - - } - - function setClear( color, alpha ) { - - state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha ); - - } - - return { - - getClearColor: function () { - - return clearColor; - - }, - setClearColor: function ( color, alpha ) { - - clearColor.set( color ); - clearAlpha = alpha !== undefined ? alpha : 1; - setClear( clearColor, clearAlpha ); - - }, - getClearAlpha: function () { - - return clearAlpha; - - }, - setClearAlpha: function ( alpha ) { - - clearAlpha = alpha; - setClear( clearColor, clearAlpha ); - - }, - render: render - - }; - - } - - function WebGLBindingStates( gl, extensions, attributes, capabilities ) { - - const maxVertexAttributes = gl.getParameter( 34921 ); - - const extension = capabilities.isWebGL2 ? null : extensions.get( 'OES_vertex_array_object' ); - const vaoAvailable = capabilities.isWebGL2 || extension !== null; - - const bindingStates = {}; - - const defaultState = createBindingState( null ); - let currentState = defaultState; - - function setup( object, material, program, geometry, index ) { - - let updateBuffers = false; - - if ( vaoAvailable ) { - - const state = getBindingState( geometry, program, material ); - - if ( currentState !== state ) { - - currentState = state; - bindVertexArrayObject( currentState.object ); - - } - - updateBuffers = needsUpdate( geometry, index ); - - if ( updateBuffers ) saveCache( geometry, index ); - - } else { - - const wireframe = ( material.wireframe === true ); - - if ( currentState.geometry !== geometry.id || - currentState.program !== program.id || - currentState.wireframe !== wireframe ) { - - currentState.geometry = geometry.id; - currentState.program = program.id; - currentState.wireframe = wireframe; - - updateBuffers = true; - - } - - } - - if ( object.isInstancedMesh === true ) { - - updateBuffers = true; - - } - - if ( index !== null ) { - - attributes.update( index, 34963 ); - - } - - if ( updateBuffers ) { - - setupVertexAttributes( object, material, program, geometry ); - - if ( index !== null ) { - - gl.bindBuffer( 34963, attributes.get( index ).buffer ); - - } - - } - - } - - function createVertexArrayObject() { - - if ( capabilities.isWebGL2 ) return gl.createVertexArray(); - - return extension.createVertexArrayOES(); - - } - - function bindVertexArrayObject( vao ) { - - if ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao ); - - return extension.bindVertexArrayOES( vao ); - - } - - function deleteVertexArrayObject( vao ) { - - if ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao ); - - return extension.deleteVertexArrayOES( vao ); - - } - - function getBindingState( geometry, program, material ) { - - const wireframe = ( material.wireframe === true ); - - let programMap = bindingStates[ geometry.id ]; - - if ( programMap === undefined ) { - - programMap = {}; - bindingStates[ geometry.id ] = programMap; - - } - - let stateMap = programMap[ program.id ]; - - if ( stateMap === undefined ) { - - stateMap = {}; - programMap[ program.id ] = stateMap; - - } - - let state = stateMap[ wireframe ]; - - if ( state === undefined ) { - - state = createBindingState( createVertexArrayObject() ); - stateMap[ wireframe ] = state; - - } - - return state; - - } - - function createBindingState( vao ) { - - const newAttributes = []; - const enabledAttributes = []; - const attributeDivisors = []; - - for ( let i = 0; i < maxVertexAttributes; i ++ ) { - - newAttributes[ i ] = 0; - enabledAttributes[ i ] = 0; - attributeDivisors[ i ] = 0; - - } - - return { - - // for backward compatibility on non-VAO support browser - geometry: null, - program: null, - wireframe: false, - - newAttributes: newAttributes, - enabledAttributes: enabledAttributes, - attributeDivisors: attributeDivisors, - object: vao, - attributes: {}, - index: null - - }; - - } - - function needsUpdate( geometry, index ) { - - const cachedAttributes = currentState.attributes; - const geometryAttributes = geometry.attributes; - - if ( Object.keys( cachedAttributes ).length !== Object.keys( geometryAttributes ).length ) return true; - - for ( const key in geometryAttributes ) { - - const cachedAttribute = cachedAttributes[ key ]; - const geometryAttribute = geometryAttributes[ key ]; - - if ( cachedAttribute === undefined ) return true; - - if ( cachedAttribute.attribute !== geometryAttribute ) return true; - - if ( cachedAttribute.data !== geometryAttribute.data ) return true; - - } - - if ( currentState.index !== index ) return true; - - return false; - - } - - function saveCache( geometry, index ) { - - const cache = {}; - const attributes = geometry.attributes; - - for ( const key in attributes ) { - - const attribute = attributes[ key ]; - - const data = {}; - data.attribute = attribute; - - if ( attribute.data ) { - - data.data = attribute.data; - - } - - cache[ key ] = data; - - } - - currentState.attributes = cache; - - currentState.index = index; - - } - - function initAttributes() { - - const newAttributes = currentState.newAttributes; - - for ( let i = 0, il = newAttributes.length; i < il; i ++ ) { - - newAttributes[ i ] = 0; - - } - - } - - function enableAttribute( attribute ) { - - enableAttributeAndDivisor( attribute, 0 ); - - } - - function enableAttributeAndDivisor( attribute, meshPerAttribute ) { - - const newAttributes = currentState.newAttributes; - const enabledAttributes = currentState.enabledAttributes; - const attributeDivisors = currentState.attributeDivisors; - - newAttributes[ attribute ] = 1; - - if ( enabledAttributes[ attribute ] === 0 ) { - - gl.enableVertexAttribArray( attribute ); - enabledAttributes[ attribute ] = 1; - - } - - if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { - - const extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' ); - - extension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute ); - attributeDivisors[ attribute ] = meshPerAttribute; - - } - - } - - function disableUnusedAttributes() { - - const newAttributes = currentState.newAttributes; - const enabledAttributes = currentState.enabledAttributes; - - for ( let i = 0, il = enabledAttributes.length; i < il; i ++ ) { - - if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { - - gl.disableVertexAttribArray( i ); - enabledAttributes[ i ] = 0; - - } - - } - - } - - function vertexAttribPointer( index, size, type, normalized, stride, offset ) { - - if ( capabilities.isWebGL2 === true && ( type === 5124 || type === 5125 ) ) { - - gl.vertexAttribIPointer( index, size, type, stride, offset ); - - } else { - - gl.vertexAttribPointer( index, size, type, normalized, stride, offset ); - - } - - } - - function setupVertexAttributes( object, material, program, geometry ) { - - if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) { - - if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) return; - - } - - initAttributes(); - - const geometryAttributes = geometry.attributes; - - const programAttributes = program.getAttributes(); - - const materialDefaultAttributeValues = material.defaultAttributeValues; - - for ( const name in programAttributes ) { - - const programAttribute = programAttributes[ name ]; - - if ( programAttribute >= 0 ) { - - const geometryAttribute = geometryAttributes[ name ]; - - if ( geometryAttribute !== undefined ) { - - const normalized = geometryAttribute.normalized; - const size = geometryAttribute.itemSize; - - const attribute = attributes.get( geometryAttribute ); - - // TODO Attribute may not be available on context restore - - if ( attribute === undefined ) continue; - - const buffer = attribute.buffer; - const type = attribute.type; - const bytesPerElement = attribute.bytesPerElement; - - if ( geometryAttribute.isInterleavedBufferAttribute ) { - - const data = geometryAttribute.data; - const stride = data.stride; - const offset = geometryAttribute.offset; - - if ( data && data.isInstancedInterleavedBuffer ) { - - enableAttributeAndDivisor( programAttribute, data.meshPerAttribute ); - - if ( geometry._maxInstanceCount === undefined ) { - - geometry._maxInstanceCount = data.meshPerAttribute * data.count; - - } - - } else { - - enableAttribute( programAttribute ); - - } - - gl.bindBuffer( 34962, buffer ); - vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement ); - - } else { - - if ( geometryAttribute.isInstancedBufferAttribute ) { - - enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute ); - - if ( geometry._maxInstanceCount === undefined ) { - - geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; - - } - - } else { - - enableAttribute( programAttribute ); - - } - - gl.bindBuffer( 34962, buffer ); - vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 ); - - } - - } else if ( name === 'instanceMatrix' ) { - - const attribute = attributes.get( object.instanceMatrix ); - - // TODO Attribute may not be available on context restore - - if ( attribute === undefined ) continue; - - const buffer = attribute.buffer; - const type = attribute.type; - - enableAttributeAndDivisor( programAttribute + 0, 1 ); - enableAttributeAndDivisor( programAttribute + 1, 1 ); - enableAttributeAndDivisor( programAttribute + 2, 1 ); - enableAttributeAndDivisor( programAttribute + 3, 1 ); - - gl.bindBuffer( 34962, buffer ); - - gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 ); - gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 ); - gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 ); - gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 ); - - } else if ( name === 'instanceColor' ) { - - const attribute = attributes.get( object.instanceColor ); - - // TODO Attribute may not be available on context restore - - if ( attribute === undefined ) continue; - - const buffer = attribute.buffer; - const type = attribute.type; - - enableAttributeAndDivisor( programAttribute, 1 ); - - gl.bindBuffer( 34962, buffer ); - - gl.vertexAttribPointer( programAttribute, 3, type, false, 12, 0 ); - - } else if ( materialDefaultAttributeValues !== undefined ) { - - const value = materialDefaultAttributeValues[ name ]; - - if ( value !== undefined ) { - - switch ( value.length ) { - - case 2: - gl.vertexAttrib2fv( programAttribute, value ); - break; - - case 3: - gl.vertexAttrib3fv( programAttribute, value ); - break; - - case 4: - gl.vertexAttrib4fv( programAttribute, value ); - break; - - default: - gl.vertexAttrib1fv( programAttribute, value ); - - } - - } - - } - - } - - } - - disableUnusedAttributes(); - - } - - function dispose() { - - reset(); - - for ( const geometryId in bindingStates ) { - - const programMap = bindingStates[ geometryId ]; - - for ( const programId in programMap ) { - - const stateMap = programMap[ programId ]; - - for ( const wireframe in stateMap ) { - - deleteVertexArrayObject( stateMap[ wireframe ].object ); - - delete stateMap[ wireframe ]; - - } - - delete programMap[ programId ]; - - } - - delete bindingStates[ geometryId ]; - - } - - } - - function releaseStatesOfGeometry( geometry ) { - - if ( bindingStates[ geometry.id ] === undefined ) return; - - const programMap = bindingStates[ geometry.id ]; - - for ( const programId in programMap ) { - - const stateMap = programMap[ programId ]; - - for ( const wireframe in stateMap ) { - - deleteVertexArrayObject( stateMap[ wireframe ].object ); - - delete stateMap[ wireframe ]; - - } - - delete programMap[ programId ]; - - } - - delete bindingStates[ geometry.id ]; - - } - - function releaseStatesOfProgram( program ) { - - for ( const geometryId in bindingStates ) { - - const programMap = bindingStates[ geometryId ]; - - if ( programMap[ program.id ] === undefined ) continue; - - const stateMap = programMap[ program.id ]; - - for ( const wireframe in stateMap ) { - - deleteVertexArrayObject( stateMap[ wireframe ].object ); - - delete stateMap[ wireframe ]; - - } - - delete programMap[ program.id ]; - - } - - } - - function reset() { - - resetDefaultState(); - - if ( currentState === defaultState ) return; - - currentState = defaultState; - bindVertexArrayObject( currentState.object ); - - } - - // for backward-compatilibity - - function resetDefaultState() { - - defaultState.geometry = null; - defaultState.program = null; - defaultState.wireframe = false; - - } - - return { - - setup: setup, - reset: reset, - resetDefaultState: resetDefaultState, - dispose: dispose, - releaseStatesOfGeometry: releaseStatesOfGeometry, - releaseStatesOfProgram: releaseStatesOfProgram, - - initAttributes: initAttributes, - enableAttribute: enableAttribute, - disableUnusedAttributes: disableUnusedAttributes - - }; - - } - - function WebGLBufferRenderer( gl, extensions, info, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - let mode; - - function setMode( value ) { - - mode = value; - - } - - function render( start, count ) { - - gl.drawArrays( mode, start, count ); - - info.update( count, mode, 1 ); - - } - - function renderInstances( start, count, primcount ) { - - if ( primcount === 0 ) return; - - let extension, methodName; - - if ( isWebGL2 ) { - - extension = gl; - methodName = 'drawArraysInstanced'; - - } else { - - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawArraysInstancedANGLE'; - - if ( extension === null ) { - - console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } - - } - - extension[ methodName ]( mode, start, count, primcount ); - - info.update( count, mode, primcount ); - - } - - // - - this.setMode = setMode; - this.render = render; - this.renderInstances = renderInstances; - - } - - function WebGLCapabilities( gl, extensions, parameters ) { - - let maxAnisotropy; - - function getMaxAnisotropy() { - - if ( maxAnisotropy !== undefined ) return maxAnisotropy; - - const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - - if ( extension !== null ) { - - maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); - - } else { - - maxAnisotropy = 0; - - } - - return maxAnisotropy; - - } - - function getMaxPrecision( precision ) { - - if ( precision === 'highp' ) { - - if ( gl.getShaderPrecisionFormat( 35633, 36338 ).precision > 0 && - gl.getShaderPrecisionFormat( 35632, 36338 ).precision > 0 ) { - - return 'highp'; - - } - - precision = 'mediump'; - - } - - if ( precision === 'mediump' ) { - - if ( gl.getShaderPrecisionFormat( 35633, 36337 ).precision > 0 && - gl.getShaderPrecisionFormat( 35632, 36337 ).precision > 0 ) { - - return 'mediump'; - - } - - } - - return 'lowp'; - - } - - /* eslint-disable no-undef */ - const isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext ) || - ( typeof WebGL2ComputeRenderingContext !== 'undefined' && gl instanceof WebGL2ComputeRenderingContext ); - /* eslint-enable no-undef */ - - let precision = parameters.precision !== undefined ? parameters.precision : 'highp'; - const maxPrecision = getMaxPrecision( precision ); - - if ( maxPrecision !== precision ) { - - console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' ); - precision = maxPrecision; - - } - - const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; - - const maxTextures = gl.getParameter( 34930 ); - const maxVertexTextures = gl.getParameter( 35660 ); - const maxTextureSize = gl.getParameter( 3379 ); - const maxCubemapSize = gl.getParameter( 34076 ); - - const maxAttributes = gl.getParameter( 34921 ); - const maxVertexUniforms = gl.getParameter( 36347 ); - const maxVaryings = gl.getParameter( 36348 ); - const maxFragmentUniforms = gl.getParameter( 36349 ); - - const vertexTextures = maxVertexTextures > 0; - const floatFragmentTextures = isWebGL2 || !! extensions.get( 'OES_texture_float' ); - const floatVertexTextures = vertexTextures && floatFragmentTextures; - - const maxSamples = isWebGL2 ? gl.getParameter( 36183 ) : 0; - - return { - - isWebGL2: isWebGL2, - - getMaxAnisotropy: getMaxAnisotropy, - getMaxPrecision: getMaxPrecision, - - precision: precision, - logarithmicDepthBuffer: logarithmicDepthBuffer, - - maxTextures: maxTextures, - maxVertexTextures: maxVertexTextures, - maxTextureSize: maxTextureSize, - maxCubemapSize: maxCubemapSize, - - maxAttributes: maxAttributes, - maxVertexUniforms: maxVertexUniforms, - maxVaryings: maxVaryings, - maxFragmentUniforms: maxFragmentUniforms, - - vertexTextures: vertexTextures, - floatFragmentTextures: floatFragmentTextures, - floatVertexTextures: floatVertexTextures, - - maxSamples: maxSamples - - }; - - } - - function WebGLClipping( properties ) { - - const scope = this; - - let globalState = null, - numGlobalPlanes = 0, - localClippingEnabled = false, - renderingShadows = false; - - const plane = new Plane(), - viewNormalMatrix = new Matrix3(), - - uniform = { value: null, needsUpdate: false }; - - this.uniform = uniform; - this.numPlanes = 0; - this.numIntersection = 0; - - this.init = function ( planes, enableLocalClipping, camera ) { - - const enabled = - planes.length !== 0 || - enableLocalClipping || - // enable state of previous frame - the clipping code has to - // run another frame in order to reset the state: - numGlobalPlanes !== 0 || - localClippingEnabled; - - localClippingEnabled = enableLocalClipping; - - globalState = projectPlanes( planes, camera, 0 ); - numGlobalPlanes = planes.length; - - return enabled; - - }; - - this.beginShadows = function () { - - renderingShadows = true; - projectPlanes( null ); - - }; - - this.endShadows = function () { - - renderingShadows = false; - resetGlobalState(); - - }; - - this.setState = function ( material, camera, useCache ) { - - const planes = material.clippingPlanes, - clipIntersection = material.clipIntersection, - clipShadows = material.clipShadows; - - const materialProperties = properties.get( material ); - - if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { - - // there's no local clipping - - if ( renderingShadows ) { - - // there's no global clipping - - projectPlanes( null ); - - } else { - - resetGlobalState(); - - } - - } else { - - const nGlobal = renderingShadows ? 0 : numGlobalPlanes, - lGlobal = nGlobal * 4; - - let dstArray = materialProperties.clippingState || null; - - uniform.value = dstArray; // ensure unique state - - dstArray = projectPlanes( planes, camera, lGlobal, useCache ); - - for ( let i = 0; i !== lGlobal; ++ i ) { - - dstArray[ i ] = globalState[ i ]; - - } - - materialProperties.clippingState = dstArray; - this.numIntersection = clipIntersection ? this.numPlanes : 0; - this.numPlanes += nGlobal; - - } - - - }; - - function resetGlobalState() { - - if ( uniform.value !== globalState ) { - - uniform.value = globalState; - uniform.needsUpdate = numGlobalPlanes > 0; - - } - - scope.numPlanes = numGlobalPlanes; - scope.numIntersection = 0; - - } - - function projectPlanes( planes, camera, dstOffset, skipTransform ) { - - const nPlanes = planes !== null ? planes.length : 0; - let dstArray = null; - - if ( nPlanes !== 0 ) { - - dstArray = uniform.value; - - if ( skipTransform !== true || dstArray === null ) { - - const flatSize = dstOffset + nPlanes * 4, - viewMatrix = camera.matrixWorldInverse; - - viewNormalMatrix.getNormalMatrix( viewMatrix ); - - if ( dstArray === null || dstArray.length < flatSize ) { - - dstArray = new Float32Array( flatSize ); - - } - - for ( let i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { - - plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); - - plane.normal.toArray( dstArray, i4 ); - dstArray[ i4 + 3 ] = plane.constant; - - } - - } - - uniform.value = dstArray; - uniform.needsUpdate = true; - - } - - scope.numPlanes = nPlanes; - scope.numIntersection = 0; - - return dstArray; - - } - - } - - function WebGLCubeMaps( renderer ) { - - let cubemaps = new WeakMap(); - - function mapTextureMapping( texture, mapping ) { - - if ( mapping === EquirectangularReflectionMapping ) { - - texture.mapping = CubeReflectionMapping; - - } else if ( mapping === EquirectangularRefractionMapping ) { - - texture.mapping = CubeRefractionMapping; - - } - - return texture; - - } - - function get( texture ) { - - if ( texture && texture.isTexture ) { - - const mapping = texture.mapping; - - if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) { - - if ( cubemaps.has( texture ) ) { - - const cubemap = cubemaps.get( texture ).texture; - return mapTextureMapping( cubemap, texture.mapping ); - - } else { - - const image = texture.image; - - if ( image && image.height > 0 ) { - - const currentRenderList = renderer.getRenderList(); - const currentRenderTarget = renderer.getRenderTarget(); - const currentRenderState = renderer.getRenderState(); - - const renderTarget = new WebGLCubeRenderTarget( image.height / 2 ); - renderTarget.fromEquirectangularTexture( renderer, texture ); - cubemaps.set( texture, renderTarget ); - - renderer.setRenderTarget( currentRenderTarget ); - renderer.setRenderList( currentRenderList ); - renderer.setRenderState( currentRenderState ); - - return mapTextureMapping( renderTarget.texture, texture.mapping ); - - } else { - - // image not yet ready. try the conversion next frame - - return null; - - } - - } - - } - - } - - return texture; - - } - - function dispose() { - - cubemaps = new WeakMap(); - - } - - return { - get: get, - dispose: dispose - }; - - } - - function WebGLExtensions( gl ) { - - const extensions = {}; - - return { - - has: function ( name ) { - - if ( extensions[ name ] !== undefined ) { - - return extensions[ name ] !== null; - - } - - let extension; - - switch ( name ) { - - case 'WEBGL_depth_texture': - extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); - break; - - case 'EXT_texture_filter_anisotropic': - extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); - break; - - case 'WEBGL_compressed_texture_s3tc': - extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); - break; - - case 'WEBGL_compressed_texture_pvrtc': - extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); - break; - - default: - extension = gl.getExtension( name ); - - } - - extensions[ name ] = extension; - - return extension !== null; - - }, - - get: function ( name ) { - - if ( ! this.has( name ) ) { - - console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); - - } - - return extensions[ name ]; - - } - - }; - - } - - function WebGLGeometries( gl, attributes, info, bindingStates ) { - - const geometries = new WeakMap(); - const wireframeAttributes = new WeakMap(); - - function onGeometryDispose( event ) { - - const geometry = event.target; - const buffergeometry = geometries.get( geometry ); - - if ( buffergeometry.index !== null ) { - - attributes.remove( buffergeometry.index ); - - } - - for ( const name in buffergeometry.attributes ) { - - attributes.remove( buffergeometry.attributes[ name ] ); - - } - - geometry.removeEventListener( 'dispose', onGeometryDispose ); - - geometries.delete( geometry ); - - const attribute = wireframeAttributes.get( buffergeometry ); - - if ( attribute ) { - - attributes.remove( attribute ); - wireframeAttributes.delete( buffergeometry ); - - } - - bindingStates.releaseStatesOfGeometry( geometry ); - - if ( geometry.isInstancedBufferGeometry === true ) { - - delete geometry._maxInstanceCount; - - } - - // - - info.memory.geometries --; - - } - - function get( object, geometry ) { - - let buffergeometry = geometries.get( geometry ); - - if ( buffergeometry ) return buffergeometry; - - geometry.addEventListener( 'dispose', onGeometryDispose ); - - if ( geometry.isBufferGeometry ) { - - buffergeometry = geometry; - - } else if ( geometry.isGeometry ) { - - if ( geometry._bufferGeometry === undefined ) { - - geometry._bufferGeometry = new BufferGeometry().setFromObject( object ); - - } - - buffergeometry = geometry._bufferGeometry; - - } - - geometries.set( geometry, buffergeometry ); - - info.memory.geometries ++; - - return buffergeometry; - - } - - function update( geometry ) { - - const geometryAttributes = geometry.attributes; - - // Updating index buffer in VAO now. See WebGLBindingStates. - - for ( const name in geometryAttributes ) { - - attributes.update( geometryAttributes[ name ], 34962 ); - - } - - // morph targets - - const morphAttributes = geometry.morphAttributes; - - for ( const name in morphAttributes ) { - - const array = morphAttributes[ name ]; - - for ( let i = 0, l = array.length; i < l; i ++ ) { - - attributes.update( array[ i ], 34962 ); - - } - - } - - } - - function updateWireframeAttribute( geometry ) { - - const indices = []; - - const geometryIndex = geometry.index; - const geometryPosition = geometry.attributes.position; - let version = 0; - - if ( geometryIndex !== null ) { - - const array = geometryIndex.array; - version = geometryIndex.version; - - for ( let i = 0, l = array.length; i < l; i += 3 ) { - - const a = array[ i + 0 ]; - const b = array[ i + 1 ]; - const c = array[ i + 2 ]; - - indices.push( a, b, b, c, c, a ); - - } - - } else { - - const array = geometryPosition.array; - version = geometryPosition.version; - - for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { - - const a = i + 0; - const b = i + 1; - const c = i + 2; - - indices.push( a, b, b, c, c, a ); - - } - - } - - const attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); - attribute.version = version; - - // Updating index buffer in VAO now. See WebGLBindingStates - - // - - const previousAttribute = wireframeAttributes.get( geometry ); - - if ( previousAttribute ) attributes.remove( previousAttribute ); - - // - - wireframeAttributes.set( geometry, attribute ); - - } - - function getWireframeAttribute( geometry ) { - - const currentAttribute = wireframeAttributes.get( geometry ); - - if ( currentAttribute ) { - - const geometryIndex = geometry.index; - - if ( geometryIndex !== null ) { - - // if the attribute is obsolete, create a new one - - if ( currentAttribute.version < geometryIndex.version ) { - - updateWireframeAttribute( geometry ); - - } - - } - - } else { - - updateWireframeAttribute( geometry ); - - } - - return wireframeAttributes.get( geometry ); - - } - - return { - - get: get, - update: update, - - getWireframeAttribute: getWireframeAttribute - - }; - - } - - function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - let mode; - - function setMode( value ) { - - mode = value; - - } - - let type, bytesPerElement; - - function setIndex( value ) { - - type = value.type; - bytesPerElement = value.bytesPerElement; - - } - - function render( start, count ) { - - gl.drawElements( mode, count, type, start * bytesPerElement ); - - info.update( count, mode, 1 ); - - } - - function renderInstances( start, count, primcount ) { - - if ( primcount === 0 ) return; - - let extension, methodName; - - if ( isWebGL2 ) { - - extension = gl; - methodName = 'drawElementsInstanced'; - - } else { - - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawElementsInstancedANGLE'; - - if ( extension === null ) { - - console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } - - } - - extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); - - info.update( count, mode, primcount ); - - } - - // - - this.setMode = setMode; - this.setIndex = setIndex; - this.render = render; - this.renderInstances = renderInstances; - - } - - function WebGLInfo( gl ) { - - const memory = { - geometries: 0, - textures: 0 - }; - - const render = { - frame: 0, - calls: 0, - triangles: 0, - points: 0, - lines: 0 - }; - - function update( count, mode, instanceCount ) { - - render.calls ++; - - switch ( mode ) { - - case 4: - render.triangles += instanceCount * ( count / 3 ); - break; - - case 1: - render.lines += instanceCount * ( count / 2 ); - break; - - case 3: - render.lines += instanceCount * ( count - 1 ); - break; - - case 2: - render.lines += instanceCount * count; - break; - - case 0: - render.points += instanceCount * count; - break; - - default: - console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); - break; - - } - - } - - function reset() { - - render.frame ++; - render.calls = 0; - render.triangles = 0; - render.points = 0; - render.lines = 0; - - } - - return { - memory: memory, - render: render, - programs: null, - autoReset: true, - reset: reset, - update: update - }; - - } - - function numericalSort( a, b ) { - - return a[ 0 ] - b[ 0 ]; - - } - - function absNumericalSort( a, b ) { - - return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); - - } - - function WebGLMorphtargets( gl ) { - - const influencesList = {}; - const morphInfluences = new Float32Array( 8 ); - - const workInfluences = []; - - for ( let i = 0; i < 8; i ++ ) { - - workInfluences[ i ] = [ i, 0 ]; - - } - - function update( object, geometry, material, program ) { - - const objectInfluences = object.morphTargetInfluences; - - // When object doesn't have morph target influences defined, we treat it as a 0-length array - // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences - - const length = objectInfluences === undefined ? 0 : objectInfluences.length; - - let influences = influencesList[ geometry.id ]; - - if ( influences === undefined ) { - - // initialise list - - influences = []; - - for ( let i = 0; i < length; i ++ ) { - - influences[ i ] = [ i, 0 ]; - - } - - influencesList[ geometry.id ] = influences; - - } - - // Collect influences - - for ( let i = 0; i < length; i ++ ) { - - const influence = influences[ i ]; - - influence[ 0 ] = i; - influence[ 1 ] = objectInfluences[ i ]; - - } - - influences.sort( absNumericalSort ); - - for ( let i = 0; i < 8; i ++ ) { - - if ( i < length && influences[ i ][ 1 ] ) { - - workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; - workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; - - } else { - - workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; - workInfluences[ i ][ 1 ] = 0; - - } - - } - - workInfluences.sort( numericalSort ); - - const morphTargets = material.morphTargets && geometry.morphAttributes.position; - const morphNormals = material.morphNormals && geometry.morphAttributes.normal; - - let morphInfluencesSum = 0; - - for ( let i = 0; i < 8; i ++ ) { - - const influence = workInfluences[ i ]; - const index = influence[ 0 ]; - const value = influence[ 1 ]; - - if ( index !== Number.MAX_SAFE_INTEGER && value ) { - - if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) { - - geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] ); - - } - - if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) { - - geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] ); - - } - - morphInfluences[ i ] = value; - morphInfluencesSum += value; - - } else { - - if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== undefined ) { - - geometry.deleteAttribute( 'morphTarget' + i ); - - } - - if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== undefined ) { - - geometry.deleteAttribute( 'morphNormal' + i ); - - } - - morphInfluences[ i ] = 0; - - } - - } - - // GLSL shader uses formula baseinfluence * base + sum(target * influence) - // This allows us to switch between absolute morphs and relative morphs without changing shader code - // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) - const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; - - program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); - program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); - - } - - return { - - update: update - - }; - - } - - function WebGLObjects( gl, geometries, attributes, info ) { - - let updateMap = new WeakMap(); - - function update( object ) { - - const frame = info.render.frame; - - const geometry = object.geometry; - const buffergeometry = geometries.get( object, geometry ); - - // Update once per frame - - if ( updateMap.get( buffergeometry ) !== frame ) { - - if ( geometry.isGeometry ) { - - buffergeometry.updateFromObject( object ); - - } - - geometries.update( buffergeometry ); - - updateMap.set( buffergeometry, frame ); - - } - - if ( object.isInstancedMesh ) { - - attributes.update( object.instanceMatrix, 34962 ); - - if ( object.instanceColor !== null ) { - - attributes.update( object.instanceColor, 34962 ); - - } - - } - - return buffergeometry; - - } - - function dispose() { - - updateMap = new WeakMap(); - - } - - return { - - update: update, - dispose: dispose - - }; - - } - - function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - - images = images !== undefined ? images : []; - mapping = mapping !== undefined ? mapping : CubeReflectionMapping; - format = format !== undefined ? format : RGBFormat; - - Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.flipY = false; - - } - - CubeTexture.prototype = Object.create( Texture.prototype ); - CubeTexture.prototype.constructor = CubeTexture; - - CubeTexture.prototype.isCubeTexture = true; - - Object.defineProperty( CubeTexture.prototype, 'images', { - - get: function () { - - return this.image; - - }, - - set: function ( value ) { - - this.image = value; - - } - - } ); - - function DataTexture2DArray( data, width, height, depth ) { - - Texture.call( this, null ); - - this.image = { data: data || null, width: width || 1, height: height || 1, depth: depth || 1 }; - - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; - - this.wrapR = ClampToEdgeWrapping; - - this.generateMipmaps = false; - this.flipY = false; - - this.needsUpdate = true; - - } - - DataTexture2DArray.prototype = Object.create( Texture.prototype ); - DataTexture2DArray.prototype.constructor = DataTexture2DArray; - DataTexture2DArray.prototype.isDataTexture2DArray = true; - - function DataTexture3D( data, width, height, depth ) { - - // We're going to add .setXXX() methods for setting properties later. - // Users can still set in DataTexture3D directly. - // - // const texture = new THREE.DataTexture3D( data, width, height, depth ); - // texture.anisotropy = 16; - // - // See #14839 - - Texture.call( this, null ); - - this.image = { data: data || null, width: width || 1, height: height || 1, depth: depth || 1 }; - - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; - - this.wrapR = ClampToEdgeWrapping; - - this.generateMipmaps = false; - this.flipY = false; - - this.needsUpdate = true; - - - } - - DataTexture3D.prototype = Object.create( Texture.prototype ); - DataTexture3D.prototype.constructor = DataTexture3D; - DataTexture3D.prototype.isDataTexture3D = true; - - /** - * Uniforms of a program. - * Those form a tree structure with a special top-level container for the root, - * which you get by calling 'new WebGLUniforms( gl, program )'. - * - * - * Properties of inner nodes including the top-level container: - * - * .seq - array of nested uniforms - * .map - nested uniforms by name - * - * - * Methods of all nodes except the top-level container: - * - * .setValue( gl, value, [textures] ) - * - * uploads a uniform value(s) - * the 'textures' parameter is needed for sampler uniforms - * - * - * Static methods of the top-level container (textures factorizations): - * - * .upload( gl, seq, values, textures ) - * - * sets uniforms in 'seq' to 'values[id].value' - * - * .seqWithValue( seq, values ) : filteredSeq - * - * filters 'seq' entries with corresponding entry in values - * - * - * Methods of the top-level container (textures factorizations): - * - * .setValue( gl, name, value, textures ) - * - * sets uniform with name 'name' to 'value' - * - * .setOptional( gl, obj, prop ) - * - * like .set for an optional property of the object - * - */ - - const emptyTexture = new Texture(); - const emptyTexture2dArray = new DataTexture2DArray(); - const emptyTexture3d = new DataTexture3D(); - const emptyCubeTexture = new CubeTexture(); - - // --- Utilities --- - - // Array Caches (provide typed arrays for temporary by size) - - const arrayCacheF32 = []; - const arrayCacheI32 = []; - - // Float32Array caches used for uploading Matrix uniforms - - const mat4array = new Float32Array( 16 ); - const mat3array = new Float32Array( 9 ); - const mat2array = new Float32Array( 4 ); - - // Flattening for arrays of vectors and matrices - - function flatten( array, nBlocks, blockSize ) { - - const firstElem = array[ 0 ]; - - if ( firstElem <= 0 || firstElem > 0 ) return array; - // unoptimized: ! isNaN( firstElem ) - // see http://jacksondunstan.com/articles/983 - - const n = nBlocks * blockSize; - let r = arrayCacheF32[ n ]; - - if ( r === undefined ) { - - r = new Float32Array( n ); - arrayCacheF32[ n ] = r; - - } - - if ( nBlocks !== 0 ) { - - firstElem.toArray( r, 0 ); - - for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { - - offset += blockSize; - array[ i ].toArray( r, offset ); - - } - - } - - return r; - - } - - function arraysEqual( a, b ) { - - if ( a.length !== b.length ) return false; - - for ( let i = 0, l = a.length; i < l; i ++ ) { - - if ( a[ i ] !== b[ i ] ) return false; - - } - - return true; - - } - - function copyArray( a, b ) { - - for ( let i = 0, l = b.length; i < l; i ++ ) { - - a[ i ] = b[ i ]; - - } - - } - - // Texture unit allocation - - function allocTexUnits( textures, n ) { - - let r = arrayCacheI32[ n ]; - - if ( r === undefined ) { - - r = new Int32Array( n ); - arrayCacheI32[ n ] = r; - - } - - for ( let i = 0; i !== n; ++ i ) { - - r[ i ] = textures.allocateTextureUnit(); - - } - - return r; - - } - - // --- Setters --- - - // Note: Defining these methods externally, because they come in a bunch - // and this way their names minify. - - // Single scalar - - function setValueV1f( gl, v ) { - - const cache = this.cache; - - if ( cache[ 0 ] === v ) return; - - gl.uniform1f( this.addr, v ); - - cache[ 0 ] = v; - - } - - // Single float vector (from flat array or THREE.VectorN) - - function setValueV2f( gl, v ) { - - const cache = this.cache; - - if ( v.x !== undefined ) { - - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { - - gl.uniform2f( this.addr, v.x, v.y ); - - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - - } - - } else { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform2fv( this.addr, v ); - - copyArray( cache, v ); - - } - - } - - function setValueV3f( gl, v ) { - - const cache = this.cache; - - if ( v.x !== undefined ) { - - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { - - gl.uniform3f( this.addr, v.x, v.y, v.z ); - - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; - - } - - } else if ( v.r !== undefined ) { - - if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { - - gl.uniform3f( this.addr, v.r, v.g, v.b ); - - cache[ 0 ] = v.r; - cache[ 1 ] = v.g; - cache[ 2 ] = v.b; - - } - - } else { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform3fv( this.addr, v ); - - copyArray( cache, v ); - - } - - } - - function setValueV4f( gl, v ) { - - const cache = this.cache; - - if ( v.x !== undefined ) { - - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { - - gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); - - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; - cache[ 3 ] = v.w; - - } - - } else { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform4fv( this.addr, v ); - - copyArray( cache, v ); - - } - - } - - // Single matrix (from flat array or MatrixN) - - function setValueM2( gl, v ) { - - const cache = this.cache; - const elements = v.elements; - - if ( elements === undefined ) { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniformMatrix2fv( this.addr, false, v ); - - copyArray( cache, v ); - - } else { - - if ( arraysEqual( cache, elements ) ) return; - - mat2array.set( elements ); - - gl.uniformMatrix2fv( this.addr, false, mat2array ); - - copyArray( cache, elements ); - - } - - } - - function setValueM3( gl, v ) { - - const cache = this.cache; - const elements = v.elements; - - if ( elements === undefined ) { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniformMatrix3fv( this.addr, false, v ); - - copyArray( cache, v ); - - } else { - - if ( arraysEqual( cache, elements ) ) return; - - mat3array.set( elements ); - - gl.uniformMatrix3fv( this.addr, false, mat3array ); - - copyArray( cache, elements ); - - } - - } - - function setValueM4( gl, v ) { - - const cache = this.cache; - const elements = v.elements; - - if ( elements === undefined ) { - - if ( arraysEqual( cache, v ) ) return; - - gl.uniformMatrix4fv( this.addr, false, v ); - - copyArray( cache, v ); - - } else { - - if ( arraysEqual( cache, elements ) ) return; - - mat4array.set( elements ); - - gl.uniformMatrix4fv( this.addr, false, mat4array ); - - copyArray( cache, elements ); - - } - - } - - // Single texture (2D / Cube) - - function setValueT1( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.safeSetTexture2D( v || emptyTexture, unit ); - - } - - function setValueT2DArray1( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.setTexture2DArray( v || emptyTexture2dArray, unit ); - - } - - function setValueT3D1( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.setTexture3D( v || emptyTexture3d, unit ); - - } - - function setValueT6( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.safeSetTextureCube( v || emptyCubeTexture, unit ); - - } - - // Integer / Boolean vectors or arrays thereof (always flat arrays) - - function setValueV1i( gl, v ) { - - const cache = this.cache; - - if ( cache[ 0 ] === v ) return; - - gl.uniform1i( this.addr, v ); - - cache[ 0 ] = v; - - } - - function setValueV2i( gl, v ) { - - const cache = this.cache; - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform2iv( this.addr, v ); - - copyArray( cache, v ); - - } - - function setValueV3i( gl, v ) { - - const cache = this.cache; - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform3iv( this.addr, v ); - - copyArray( cache, v ); - - } - - function setValueV4i( gl, v ) { - - const cache = this.cache; - - if ( arraysEqual( cache, v ) ) return; - - gl.uniform4iv( this.addr, v ); - - copyArray( cache, v ); - - } - - // uint - - function setValueV1ui( gl, v ) { - - const cache = this.cache; - - if ( cache[ 0 ] === v ) return; - - gl.uniform1ui( this.addr, v ); - - cache[ 0 ] = v; - - } - - // Helper to pick the right setter for the singular case - - function getSingularSetter( type ) { - - switch ( type ) { - - case 0x1406: return setValueV1f; // FLOAT - case 0x8b50: return setValueV2f; // _VEC2 - case 0x8b51: return setValueV3f; // _VEC3 - case 0x8b52: return setValueV4f; // _VEC4 - - case 0x8b5a: return setValueM2; // _MAT2 - case 0x8b5b: return setValueM3; // _MAT3 - case 0x8b5c: return setValueM4; // _MAT4 - - case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 - - case 0x1405: return setValueV1ui; // UINT - - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1; - - case 0x8b5f: // SAMPLER_3D - case 0x8dcb: // INT_SAMPLER_3D - case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D - return setValueT3D1; - - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6; - - case 0x8dc1: // SAMPLER_2D_ARRAY - case 0x8dcf: // INT_SAMPLER_2D_ARRAY - case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY - case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW - return setValueT2DArray1; - - } - - } - - // Array of scalars - function setValueV1fArray( gl, v ) { - - gl.uniform1fv( this.addr, v ); - - } - - // Integer / Boolean vectors or arrays thereof (always flat arrays) - function setValueV1iArray( gl, v ) { - - gl.uniform1iv( this.addr, v ); - - } - - function setValueV2iArray( gl, v ) { - - gl.uniform2iv( this.addr, v ); - - } - - function setValueV3iArray( gl, v ) { - - gl.uniform3iv( this.addr, v ); - - } - - function setValueV4iArray( gl, v ) { - - gl.uniform4iv( this.addr, v ); - - } - - - // Array of vectors (flat or from THREE classes) - - function setValueV2fArray( gl, v ) { - - const data = flatten( v, this.size, 2 ); - - gl.uniform2fv( this.addr, data ); - - } - - function setValueV3fArray( gl, v ) { - - const data = flatten( v, this.size, 3 ); - - gl.uniform3fv( this.addr, data ); - - } - - function setValueV4fArray( gl, v ) { - - const data = flatten( v, this.size, 4 ); - - gl.uniform4fv( this.addr, data ); - - } - - // Array of matrices (flat or from THREE clases) - - function setValueM2Array( gl, v ) { - - const data = flatten( v, this.size, 4 ); - - gl.uniformMatrix2fv( this.addr, false, data ); - - } - - function setValueM3Array( gl, v ) { - - const data = flatten( v, this.size, 9 ); - - gl.uniformMatrix3fv( this.addr, false, data ); - - } - - function setValueM4Array( gl, v ) { - - const data = flatten( v, this.size, 16 ); - - gl.uniformMatrix4fv( this.addr, false, data ); - - } - - // Array of textures (2D / Cube) - - function setValueT1Array( gl, v, textures ) { - - const n = v.length; - - const units = allocTexUnits( textures, n ); - - gl.uniform1iv( this.addr, units ); - - for ( let i = 0; i !== n; ++ i ) { - - textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] ); - - } - - } - - function setValueT6Array( gl, v, textures ) { - - const n = v.length; - - const units = allocTexUnits( textures, n ); - - gl.uniform1iv( this.addr, units ); - - for ( let i = 0; i !== n; ++ i ) { - - textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); - - } - - } - - // Helper to pick the right setter for a pure (bottom-level) array - - function getPureArraySetter( type ) { - - switch ( type ) { - - case 0x1406: return setValueV1fArray; // FLOAT - case 0x8b50: return setValueV2fArray; // _VEC2 - case 0x8b51: return setValueV3fArray; // _VEC3 - case 0x8b52: return setValueV4fArray; // _VEC4 - - case 0x8b5a: return setValueM2Array; // _MAT2 - case 0x8b5b: return setValueM3Array; // _MAT3 - case 0x8b5c: return setValueM4Array; // _MAT4 - - case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 - - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1Array; - - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6Array; - - } - - } - - // --- Uniform Classes --- - - function SingleUniform( id, activeInfo, addr ) { - - this.id = id; - this.addr = addr; - this.cache = []; - this.setValue = getSingularSetter( activeInfo.type ); - - // this.path = activeInfo.name; // DEBUG - - } - - function PureArrayUniform( id, activeInfo, addr ) { - - this.id = id; - this.addr = addr; - this.cache = []; - this.size = activeInfo.size; - this.setValue = getPureArraySetter( activeInfo.type ); - - // this.path = activeInfo.name; // DEBUG - - } - - PureArrayUniform.prototype.updateCache = function ( data ) { - - const cache = this.cache; - - if ( data instanceof Float32Array && cache.length !== data.length ) { - - this.cache = new Float32Array( data.length ); - - } - - copyArray( cache, data ); - - }; - - function StructuredUniform( id ) { - - this.id = id; - - this.seq = []; - this.map = {}; - - } - - StructuredUniform.prototype.setValue = function ( gl, value, textures ) { - - const seq = this.seq; - - for ( let i = 0, n = seq.length; i !== n; ++ i ) { - - const u = seq[ i ]; - u.setValue( gl, value[ u.id ], textures ); - - } - - }; - - // --- Top-level --- - - // Parser - builds up the property tree from the path strings - - const RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g; - - // extracts - // - the identifier (member name or array index) - // - followed by an optional right bracket (found when array index) - // - followed by an optional left bracket or dot (type of subscript) - // - // Note: These portions can be read in a non-overlapping fashion and - // allow straightforward parsing of the hierarchy that WebGL encodes - // in the uniform names. - - function addUniform( container, uniformObject ) { - - container.seq.push( uniformObject ); - container.map[ uniformObject.id ] = uniformObject; - - } - - function parseUniform( activeInfo, addr, container ) { - - const path = activeInfo.name, - pathLength = path.length; - - // reset RegExp object, because of the early exit of a previous run - RePathPart.lastIndex = 0; - - while ( true ) { - - const match = RePathPart.exec( path ), - matchEnd = RePathPart.lastIndex; - - let id = match[ 1 ]; - const idIsIndex = match[ 2 ] === ']', - subscript = match[ 3 ]; - - if ( idIsIndex ) id = id | 0; // convert to integer - - if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { - - // bare name or "pure" bottom-level array "[0]" suffix - - addUniform( container, subscript === undefined ? - new SingleUniform( id, activeInfo, addr ) : - new PureArrayUniform( id, activeInfo, addr ) ); - - break; - - } else { - - // step into inner node / create it in case it doesn't exist - - const map = container.map; - let next = map[ id ]; - - if ( next === undefined ) { - - next = new StructuredUniform( id ); - addUniform( container, next ); - - } - - container = next; - - } - - } - - } - - // Root Container - - function WebGLUniforms( gl, program ) { - - this.seq = []; - this.map = {}; - - const n = gl.getProgramParameter( program, 35718 ); - - for ( let i = 0; i < n; ++ i ) { - - const info = gl.getActiveUniform( program, i ), - addr = gl.getUniformLocation( program, info.name ); - - parseUniform( info, addr, this ); - - } - - } - - WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) { - - const u = this.map[ name ]; - - if ( u !== undefined ) u.setValue( gl, value, textures ); - - }; - - WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { - - const v = object[ name ]; - - if ( v !== undefined ) this.setValue( gl, name, v ); - - }; - - - // Static interface - - WebGLUniforms.upload = function ( gl, seq, values, textures ) { - - for ( let i = 0, n = seq.length; i !== n; ++ i ) { - - const u = seq[ i ], - v = values[ u.id ]; - - if ( v.needsUpdate !== false ) { - - // note: always updating when .needsUpdate is undefined - u.setValue( gl, v.value, textures ); - - } - - } - - }; - - WebGLUniforms.seqWithValue = function ( seq, values ) { - - const r = []; - - for ( let i = 0, n = seq.length; i !== n; ++ i ) { - - const u = seq[ i ]; - if ( u.id in values ) r.push( u ); - - } - - return r; - - }; - - function WebGLShader( gl, type, string ) { - - const shader = gl.createShader( type ); - - gl.shaderSource( shader, string ); - gl.compileShader( shader ); - - return shader; - - } - - let programIdCount = 0; - - function addLineNumbers( string ) { - - const lines = string.split( '\n' ); - - for ( let i = 0; i < lines.length; i ++ ) { - - lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; - - } - - return lines.join( '\n' ); - - } - - function getEncodingComponents( encoding ) { - - switch ( encoding ) { - - case LinearEncoding: - return [ 'Linear', '( value )' ]; - case sRGBEncoding: - return [ 'sRGB', '( value )' ]; - case RGBEEncoding: - return [ 'RGBE', '( value )' ]; - case RGBM7Encoding: - return [ 'RGBM', '( value, 7.0 )' ]; - case RGBM16Encoding: - return [ 'RGBM', '( value, 16.0 )' ]; - case RGBDEncoding: - return [ 'RGBD', '( value, 256.0 )' ]; - case GammaEncoding: - return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ]; - case LogLuvEncoding: - return [ 'LogLuv', '( value )' ]; - default: - console.warn( 'THREE.WebGLProgram: Unsupported encoding:', encoding ); - return [ 'Linear', '( value )' ]; - - } - - } - - function getShaderErrors( gl, shader, type ) { - - const status = gl.getShaderParameter( shader, 35713 ); - const log = gl.getShaderInfoLog( shader ).trim(); - - if ( status && log === '' ) return ''; - - // --enable-privileged-webgl-extension - // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); - - const source = gl.getShaderSource( shader ); - - return 'THREE.WebGLShader: gl.getShaderInfoLog() ' + type + '\n' + log + addLineNumbers( source ); - - } - - function getTexelDecodingFunction( functionName, encoding ) { - - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }'; - - } - - function getTexelEncodingFunction( functionName, encoding ) { - - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }'; - - } - - function getToneMappingFunction( functionName, toneMapping ) { - - let toneMappingName; - - switch ( toneMapping ) { - - case LinearToneMapping: - toneMappingName = 'Linear'; - break; - - case ReinhardToneMapping: - toneMappingName = 'Reinhard'; - break; - - case CineonToneMapping: - toneMappingName = 'OptimizedCineon'; - break; - - case ACESFilmicToneMapping: - toneMappingName = 'ACESFilmic'; - break; - - case CustomToneMapping: - toneMappingName = 'Custom'; - break; - - default: - console.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping ); - toneMappingName = 'Linear'; - - } - - return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; - - } - - function generateExtensions( parameters ) { - - const chunks = [ - ( parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '', - ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', - ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '', - ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '' - ]; - - return chunks.filter( filterEmptyLine ).join( '\n' ); - - } - - function generateDefines( defines ) { - - const chunks = []; - - for ( const name in defines ) { - - const value = defines[ name ]; - - if ( value === false ) continue; - - chunks.push( '#define ' + name + ' ' + value ); - - } - - return chunks.join( '\n' ); - - } - - function fetchAttributeLocations( gl, program ) { - - const attributes = {}; - - const n = gl.getProgramParameter( program, 35721 ); - - for ( let i = 0; i < n; i ++ ) { - - const info = gl.getActiveAttrib( program, i ); - const name = info.name; - - // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); - - attributes[ name ] = gl.getAttribLocation( program, name ); - - } - - return attributes; - - } - - function filterEmptyLine( string ) { - - return string !== ''; - - } - - function replaceLightNums( string, parameters ) { - - return string - .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) - .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) - .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) - .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) - .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) - .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) - .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) - .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); - - } - - function replaceClippingPlaneNums( string, parameters ) { - - return string - .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) - .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); - - } - - // Resolve Includes - - const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; - - function resolveIncludes( string ) { - - return string.replace( includePattern, includeReplacer ); - - } - - function includeReplacer( match, include ) { - - const string = ShaderChunk[ include ]; - - if ( string === undefined ) { - - throw new Error( 'Can not resolve #include <' + include + '>' ); - - } - - return resolveIncludes( string ); - - } - - // Unroll Loops - - const deprecatedUnrollLoopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; - const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g; - - function unrollLoops( string ) { - - return string - .replace( unrollLoopPattern, loopReplacer ) - .replace( deprecatedUnrollLoopPattern, deprecatedLoopReplacer ); - - } - - function deprecatedLoopReplacer( match, start, end, snippet ) { - - console.warn( 'WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.' ); - return loopReplacer( match, start, end, snippet ); - - } - - function loopReplacer( match, start, end, snippet ) { - - let string = ''; - - for ( let i = parseInt( start ); i < parseInt( end ); i ++ ) { - - string += snippet - .replace( /\[\s*i\s*\]/g, '[ ' + i + ' ]' ) - .replace( /UNROLLED_LOOP_INDEX/g, i ); - - } - - return string; - - } - - // - - function generatePrecision( parameters ) { - - let precisionstring = "precision " + parameters.precision + " float;\nprecision " + parameters.precision + " int;"; - - if ( parameters.precision === "highp" ) { - - precisionstring += "\n#define HIGH_PRECISION"; - - } else if ( parameters.precision === "mediump" ) { - - precisionstring += "\n#define MEDIUM_PRECISION"; - - } else if ( parameters.precision === "lowp" ) { - - precisionstring += "\n#define LOW_PRECISION"; - - } - - return precisionstring; - - } - - function generateShadowMapTypeDefine( parameters ) { - - let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; - - if ( parameters.shadowMapType === PCFShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; - - } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; - - } else if ( parameters.shadowMapType === VSMShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; - - } - - return shadowMapTypeDefine; - - } - - function generateEnvMapTypeDefine( parameters ) { - - let envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - - if ( parameters.envMap ) { - - switch ( parameters.envMapMode ) { - - case CubeReflectionMapping: - case CubeRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - break; - - case CubeUVReflectionMapping: - case CubeUVRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; - break; - - } - - } - - return envMapTypeDefine; - - } - - function generateEnvMapModeDefine( parameters ) { - - let envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; - - if ( parameters.envMap ) { - - switch ( parameters.envMapMode ) { - - case CubeRefractionMapping: - case CubeUVRefractionMapping: - - envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; - break; - - } - - } - - return envMapModeDefine; - - } - - function generateEnvMapBlendingDefine( parameters ) { - - let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; - - if ( parameters.envMap ) { - - switch ( parameters.combine ) { - - case MultiplyOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - break; - - case MixOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; - break; - - case AddOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; - break; - - } - - } - - return envMapBlendingDefine; - - } - - function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { - - const gl = renderer.getContext(); - - const defines = parameters.defines; - - let vertexShader = parameters.vertexShader; - let fragmentShader = parameters.fragmentShader; - - const shadowMapTypeDefine = generateShadowMapTypeDefine( parameters ); - const envMapTypeDefine = generateEnvMapTypeDefine( parameters ); - const envMapModeDefine = generateEnvMapModeDefine( parameters ); - const envMapBlendingDefine = generateEnvMapBlendingDefine( parameters ); - - - const gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; - - const customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters ); - - const customDefines = generateDefines( defines ); - - const program = gl.createProgram(); - - let prefixVertex, prefixFragment; - let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + "\n" : ''; - - if ( parameters.isRawShaderMaterial ) { - - prefixVertex = [ - - customDefines - - ].filter( filterEmptyLine ).join( '\n' ); - - if ( prefixVertex.length > 0 ) { - - prefixVertex += '\n'; - - } - - prefixFragment = [ - - customExtensions, - customDefines - - ].filter( filterEmptyLine ).join( '\n' ); - - if ( prefixFragment.length > 0 ) { - - prefixFragment += '\n'; - - } - - } else { - - prefixVertex = [ - - generatePrecision( parameters ), - - '#define SHADER_NAME ' + parameters.shaderName, - - customDefines, - - parameters.instancing ? '#define USE_INSTANCING' : '', - parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', - - parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', - - '#define GAMMA_FACTOR ' + gammaFactorDefine, - - '#define MAX_BONES ' + parameters.maxBones, - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - - parameters.map ? '#define USE_MAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', - - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - - parameters.flatShading ? '#define FLAT_SHADED' : '', - - parameters.skinning ? '#define USE_SKINNING' : '', - parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', - - parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', - parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', - - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - - parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', - - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - - 'uniform mat4 modelMatrix;', - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform mat4 viewMatrix;', - 'uniform mat3 normalMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', - - '#ifdef USE_INSTANCING', - - ' attribute mat4 instanceMatrix;', - - '#endif', - - '#ifdef USE_INSTANCING_COLOR', - - ' attribute vec3 instanceColor;', - - '#endif', - - 'attribute vec3 position;', - 'attribute vec3 normal;', - 'attribute vec2 uv;', - - '#ifdef USE_TANGENT', - - ' attribute vec4 tangent;', - - '#endif', - - '#ifdef USE_COLOR', - - ' attribute vec3 color;', - - '#endif', - - '#ifdef USE_MORPHTARGETS', - - ' attribute vec3 morphTarget0;', - ' attribute vec3 morphTarget1;', - ' attribute vec3 morphTarget2;', - ' attribute vec3 morphTarget3;', - - ' #ifdef USE_MORPHNORMALS', - - ' attribute vec3 morphNormal0;', - ' attribute vec3 morphNormal1;', - ' attribute vec3 morphNormal2;', - ' attribute vec3 morphNormal3;', - - ' #else', - - ' attribute vec3 morphTarget4;', - ' attribute vec3 morphTarget5;', - ' attribute vec3 morphTarget6;', - ' attribute vec3 morphTarget7;', - - ' #endif', - - '#endif', - - '#ifdef USE_SKINNING', - - ' attribute vec4 skinIndex;', - ' attribute vec4 skinWeight;', - - '#endif', - - '\n' - - ].filter( filterEmptyLine ).join( '\n' ); - - prefixFragment = [ - - customExtensions, - - generatePrecision( parameters ), - - '#define SHADER_NAME ' + parameters.shaderName, - - customDefines, - - parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest + ( parameters.alphaTest % 1 ? '' : '.0' ) : '', // add '.0' if integer - - '#define GAMMA_FACTOR ' + gammaFactorDefine, - - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - - parameters.map ? '#define USE_MAP' : '', - parameters.matcap ? '#define USE_MATCAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapTypeDefine : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.envMap ? '#define ' + envMapBlendingDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - - parameters.sheen ? '#define USE_SHEEN' : '', - parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', - - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - - parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', - - parameters.flatShading ? '#define FLAT_SHADED' : '', - - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', - - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - - parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', - - parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', - - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - - ( ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ) ? '#define TEXTURE_LOD_EXT' : '', - - 'uniform mat4 viewMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', - - ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', - ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below - ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', - - parameters.dithering ? '#define DITHERING' : '', - - ShaderChunk[ 'encodings_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below - parameters.map ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', - parameters.matcap ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '', - parameters.envMap ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', - parameters.emissiveMap ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', - parameters.lightMap ? getTexelDecodingFunction( 'lightMapTexelToLinear', parameters.lightMapEncoding ) : '', - getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ), - - parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', - - '\n' - - ].filter( filterEmptyLine ).join( '\n' ); - - } - - vertexShader = resolveIncludes( vertexShader ); - vertexShader = replaceLightNums( vertexShader, parameters ); - vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); - - fragmentShader = resolveIncludes( fragmentShader ); - fragmentShader = replaceLightNums( fragmentShader, parameters ); - fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); - - vertexShader = unrollLoops( vertexShader ); - fragmentShader = unrollLoops( fragmentShader ); - - if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { - - // GLSL 3.0 conversion for built-in materials and ShaderMaterial - - versionString = '#version 300 es\n'; - - prefixVertex = [ - '#define attribute in', - '#define varying out', - '#define texture2D texture' - ].join( '\n' ) + '\n' + prefixVertex; - - prefixFragment = [ - '#define varying in', - ( parameters.glslVersion === GLSL3 ) ? '' : 'out highp vec4 pc_fragColor;', - ( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor', - '#define gl_FragDepthEXT gl_FragDepth', - '#define texture2D texture', - '#define textureCube texture', - '#define texture2DProj textureProj', - '#define texture2DLodEXT textureLod', - '#define texture2DProjLodEXT textureProjLod', - '#define textureCubeLodEXT textureLod', - '#define texture2DGradEXT textureGrad', - '#define texture2DProjGradEXT textureProjGrad', - '#define textureCubeGradEXT textureGrad' - ].join( '\n' ) + '\n' + prefixFragment; - - } - - const vertexGlsl = versionString + prefixVertex + vertexShader; - const fragmentGlsl = versionString + prefixFragment + fragmentShader; - - // console.log( '*VERTEX*', vertexGlsl ); - // console.log( '*FRAGMENT*', fragmentGlsl ); - - const glVertexShader = WebGLShader( gl, 35633, vertexGlsl ); - const glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl ); - - gl.attachShader( program, glVertexShader ); - gl.attachShader( program, glFragmentShader ); - - // Force a particular attribute to index 0. - - if ( parameters.index0AttributeName !== undefined ) { - - gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); - - } else if ( parameters.morphTargets === true ) { - - // programs with morphTargets displace position out of attribute 0 - gl.bindAttribLocation( program, 0, 'position' ); - - } - - gl.linkProgram( program ); - - // check for link errors - if ( renderer.debug.checkShaderErrors ) { - - const programLog = gl.getProgramInfoLog( program ).trim(); - const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); - const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); - - let runnable = true; - let haveDiagnostics = true; - - if ( gl.getProgramParameter( program, 35714 ) === false ) { - - runnable = false; - - const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); - const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); - - console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), '35715', gl.getProgramParameter( program, 35715 ), 'gl.getProgramInfoLog', programLog, vertexErrors, fragmentErrors ); - - } else if ( programLog !== '' ) { - - console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); - - } else if ( vertexLog === '' || fragmentLog === '' ) { - - haveDiagnostics = false; - - } - - if ( haveDiagnostics ) { - - this.diagnostics = { - - runnable: runnable, - - programLog: programLog, - - vertexShader: { - - log: vertexLog, - prefix: prefixVertex - - }, - - fragmentShader: { - - log: fragmentLog, - prefix: prefixFragment - - } - - }; - - } - - } - - // Clean up - - // Crashes in iOS9 and iOS10. #18402 - // gl.detachShader( program, glVertexShader ); - // gl.detachShader( program, glFragmentShader ); - - gl.deleteShader( glVertexShader ); - gl.deleteShader( glFragmentShader ); - - // set up caching for uniform locations - - let cachedUniforms; - - this.getUniforms = function () { - - if ( cachedUniforms === undefined ) { - - cachedUniforms = new WebGLUniforms( gl, program ); - - } - - return cachedUniforms; - - }; - - // set up caching for attribute locations - - let cachedAttributes; - - this.getAttributes = function () { - - if ( cachedAttributes === undefined ) { - - cachedAttributes = fetchAttributeLocations( gl, program ); - - } - - return cachedAttributes; - - }; - - // free resource - - this.destroy = function () { - - bindingStates.releaseStatesOfProgram( this ); - - gl.deleteProgram( program ); - this.program = undefined; - - }; - - // - - this.name = parameters.shaderName; - this.id = programIdCount ++; - this.cacheKey = cacheKey; - this.usedTimes = 1; - this.program = program; - this.vertexShader = glVertexShader; - this.fragmentShader = glFragmentShader; - - return this; - - } - - function WebGLPrograms( renderer, cubemaps, extensions, capabilities, bindingStates, clipping ) { - - const programs = []; - - const isWebGL2 = capabilities.isWebGL2; - const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; - const floatVertexTextures = capabilities.floatVertexTextures; - const maxVertexUniforms = capabilities.maxVertexUniforms; - const vertexTextures = capabilities.vertexTextures; - - let precision = capabilities.precision; - - const shaderIDs = { - MeshDepthMaterial: 'depth', - MeshDistanceMaterial: 'distanceRGBA', - MeshNormalMaterial: 'normal', - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - MeshToonMaterial: 'toon', - MeshStandardMaterial: 'physical', - MeshPhysicalMaterial: 'physical', - MeshMatcapMaterial: 'matcap', - LineBasicMaterial: 'basic', - LineDashedMaterial: 'dashed', - PointsMaterial: 'points', - ShadowMaterial: 'shadow', - SpriteMaterial: 'sprite' - }; - - const parameterNames = [ - "precision", "isWebGL2", "supportsVertexTextures", "outputEncoding", "instancing", "instancingColor", - "map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding", "envMapCubeUV", - "lightMap", "lightMapEncoding", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "tangentSpaceNormalMap", "clearcoatMap", "clearcoatRoughnessMap", "clearcoatNormalMap", "displacementMap", "specularMap", - "roughnessMap", "metalnessMap", "gradientMap", - "alphaMap", "combine", "vertexColors", "vertexTangents", "vertexUvs", "uvsVertexOnly", "fog", "useFog", "fogExp2", - "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", - "maxBones", "useVertexTexture", "morphTargets", "morphNormals", - "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", - "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights", - "numDirLightShadows", "numPointLightShadows", "numSpotLightShadows", - "shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights', - "alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering", - "sheen", "transmissionMap" - ]; - - function getMaxBones( object ) { - - const skeleton = object.skeleton; - const bones = skeleton.bones; - - if ( floatVertexTextures ) { - - return 1024; - - } else { - - // default for when object is not specified - // ( for example when prebuilding shader to be used with multiple objects ) - // - // - leave some extra space for other uniforms - // - limit here is ANGLE's 254 max uniform vectors - // (up to 54 should be safe) - - const nVertexUniforms = maxVertexUniforms; - const nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); - - const maxBones = Math.min( nVertexMatrices, bones.length ); - - if ( maxBones < bones.length ) { - - console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); - return 0; - - } - - return maxBones; - - } - - } - - function getTextureEncodingFromMap( map ) { - - let encoding; - - if ( ! map ) { - - encoding = LinearEncoding; - - } else if ( map.isTexture ) { - - encoding = map.encoding; - - } else if ( map.isWebGLRenderTarget ) { - - console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." ); - encoding = map.texture.encoding; - - } - - return encoding; - - } - - function getParameters( material, lights, shadows, scene, object ) { - - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; - - const envMap = cubemaps.get( material.envMap || environment ); - - const shaderID = shaderIDs[ material.type ]; - - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) - - const maxBones = object.isSkinnedMesh ? getMaxBones( object ) : 0; - - if ( material.precision !== null ) { - - precision = capabilities.getMaxPrecision( material.precision ); - - if ( precision !== material.precision ) { - - console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); - - } - - } - - let vertexShader, fragmentShader; - - if ( shaderID ) { - - const shader = ShaderLib[ shaderID ]; - - vertexShader = shader.vertexShader; - fragmentShader = shader.fragmentShader; - - } else { - - vertexShader = material.vertexShader; - fragmentShader = material.fragmentShader; - - } - - const currentRenderTarget = renderer.getRenderTarget(); - - const parameters = { - - isWebGL2: isWebGL2, - - shaderID: shaderID, - shaderName: material.type, - - vertexShader: vertexShader, - fragmentShader: fragmentShader, - defines: material.defines, - - isRawShaderMaterial: material.isRawShaderMaterial === true, - glslVersion: material.glslVersion, - - precision: precision, - - instancing: object.isInstancedMesh === true, - instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, - - supportsVertexTextures: vertexTextures, - outputEncoding: ( currentRenderTarget !== null ) ? getTextureEncodingFromMap( currentRenderTarget.texture ) : renderer.outputEncoding, - map: !! material.map, - mapEncoding: getTextureEncodingFromMap( material.map ), - matcap: !! material.matcap, - matcapEncoding: getTextureEncodingFromMap( material.matcap ), - envMap: !! envMap, - envMapMode: envMap && envMap.mapping, - envMapEncoding: getTextureEncodingFromMap( envMap ), - envMapCubeUV: ( !! envMap ) && ( ( envMap.mapping === CubeUVReflectionMapping ) || ( envMap.mapping === CubeUVRefractionMapping ) ), - lightMap: !! material.lightMap, - lightMapEncoding: getTextureEncodingFromMap( material.lightMap ), - aoMap: !! material.aoMap, - emissiveMap: !! material.emissiveMap, - emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap ), - bumpMap: !! material.bumpMap, - normalMap: !! material.normalMap, - objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, - tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, - clearcoatMap: !! material.clearcoatMap, - clearcoatRoughnessMap: !! material.clearcoatRoughnessMap, - clearcoatNormalMap: !! material.clearcoatNormalMap, - displacementMap: !! material.displacementMap, - roughnessMap: !! material.roughnessMap, - metalnessMap: !! material.metalnessMap, - specularMap: !! material.specularMap, - alphaMap: !! material.alphaMap, - - gradientMap: !! material.gradientMap, - - sheen: !! material.sheen, - - transmissionMap: !! material.transmissionMap, - - combine: material.combine, - - vertexTangents: ( material.normalMap && material.vertexTangents ), - vertexColors: material.vertexColors, - vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap, - uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || !! material.transmissionMap ) && !! material.displacementMap, - - fog: !! fog, - useFog: material.fog, - fogExp2: ( fog && fog.isFogExp2 ), - - flatShading: material.flatShading, - - sizeAttenuation: material.sizeAttenuation, - logarithmicDepthBuffer: logarithmicDepthBuffer, - - skinning: material.skinning && maxBones > 0, - maxBones: maxBones, - useVertexTexture: floatVertexTextures, - - morphTargets: material.morphTargets, - morphNormals: material.morphNormals, - maxMorphTargets: renderer.maxMorphTargets, - maxMorphNormals: renderer.maxMorphNormals, - - numDirLights: lights.directional.length, - numPointLights: lights.point.length, - numSpotLights: lights.spot.length, - numRectAreaLights: lights.rectArea.length, - numHemiLights: lights.hemi.length, - - numDirLightShadows: lights.directionalShadowMap.length, - numPointLightShadows: lights.pointShadowMap.length, - numSpotLightShadows: lights.spotShadowMap.length, - - numClippingPlanes: clipping.numPlanes, - numClipIntersection: clipping.numIntersection, - - dithering: material.dithering, - - shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, - shadowMapType: renderer.shadowMap.type, - - toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, - physicallyCorrectLights: renderer.physicallyCorrectLights, - - premultipliedAlpha: material.premultipliedAlpha, - - alphaTest: material.alphaTest, - doubleSided: material.side === DoubleSide, - flipSided: material.side === BackSide, - - depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false, - - index0AttributeName: material.index0AttributeName, - - extensionDerivatives: material.extensions && material.extensions.derivatives, - extensionFragDepth: material.extensions && material.extensions.fragDepth, - extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, - extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, - - rendererExtensionFragDepth: isWebGL2 || extensions.has( 'EXT_frag_depth' ), - rendererExtensionDrawBuffers: isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ), - rendererExtensionShaderTextureLod: isWebGL2 || extensions.has( 'EXT_shader_texture_lod' ), - - customProgramCacheKey: material.customProgramCacheKey() - - }; - - return parameters; - - } - - function getProgramCacheKey( parameters ) { - - const array = []; - - if ( parameters.shaderID ) { - - array.push( parameters.shaderID ); - - } else { - - array.push( parameters.fragmentShader ); - array.push( parameters.vertexShader ); - - } - - if ( parameters.defines !== undefined ) { - - for ( const name in parameters.defines ) { - - array.push( name ); - array.push( parameters.defines[ name ] ); - - } - - } - - if ( parameters.isRawShaderMaterial === false ) { - - for ( let i = 0; i < parameterNames.length; i ++ ) { - - array.push( parameters[ parameterNames[ i ] ] ); - - } - - array.push( renderer.outputEncoding ); - array.push( renderer.gammaFactor ); - - } - - array.push( parameters.customProgramCacheKey ); - - return array.join(); - - } - - function getUniforms( material ) { - - const shaderID = shaderIDs[ material.type ]; - let uniforms; - - if ( shaderID ) { - - const shader = ShaderLib[ shaderID ]; - uniforms = UniformsUtils.clone( shader.uniforms ); - - } else { - - uniforms = material.uniforms; - - } - - return uniforms; - - } - - function acquireProgram( parameters, cacheKey ) { - - let program; - - // Check if code has been already compiled - for ( let p = 0, pl = programs.length; p < pl; p ++ ) { - - const preexistingProgram = programs[ p ]; - - if ( preexistingProgram.cacheKey === cacheKey ) { - - program = preexistingProgram; - ++ program.usedTimes; - - break; - - } - - } - - if ( program === undefined ) { - - program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); - programs.push( program ); - - } - - return program; - - } - - function releaseProgram( program ) { - - if ( -- program.usedTimes === 0 ) { - - // Remove from unordered set - const i = programs.indexOf( program ); - programs[ i ] = programs[ programs.length - 1 ]; - programs.pop(); - - // Free WebGL resources - program.destroy(); - - } - - } - - return { - getParameters: getParameters, - getProgramCacheKey: getProgramCacheKey, - getUniforms: getUniforms, - acquireProgram: acquireProgram, - releaseProgram: releaseProgram, - // Exposed for resource monitoring & error feedback via renderer.info: - programs: programs - }; - - } - - function WebGLProperties() { - - let properties = new WeakMap(); - - function get( object ) { - - let map = properties.get( object ); - - if ( map === undefined ) { - - map = {}; - properties.set( object, map ); - - } - - return map; - - } - - function remove( object ) { - - properties.delete( object ); - - } - - function update( object, key, value ) { - - properties.get( object )[ key ] = value; - - } - - function dispose() { - - properties = new WeakMap(); - - } - - return { - get: get, - remove: remove, - update: update, - dispose: dispose - }; - - } - - function painterSortStable( a, b ) { - - if ( a.groupOrder !== b.groupOrder ) { - - return a.groupOrder - b.groupOrder; - - } else if ( a.renderOrder !== b.renderOrder ) { - - return a.renderOrder - b.renderOrder; - - } else if ( a.program !== b.program ) { - - return a.program.id - b.program.id; - - } else if ( a.material.id !== b.material.id ) { - - return a.material.id - b.material.id; - - } else if ( a.z !== b.z ) { - - return a.z - b.z; - - } else { - - return a.id - b.id; - - } - - } - - function reversePainterSortStable( a, b ) { - - if ( a.groupOrder !== b.groupOrder ) { - - return a.groupOrder - b.groupOrder; - - } else if ( a.renderOrder !== b.renderOrder ) { - - return a.renderOrder - b.renderOrder; - - } else if ( a.z !== b.z ) { - - return b.z - a.z; - - } else { - - return a.id - b.id; - - } - - } - - - function WebGLRenderList( properties ) { - - const renderItems = []; - let renderItemsIndex = 0; - - const opaque = []; - const transparent = []; - - const defaultProgram = { id: - 1 }; - - function init() { - - renderItemsIndex = 0; - - opaque.length = 0; - transparent.length = 0; - - } - - function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { - - let renderItem = renderItems[ renderItemsIndex ]; - const materialProperties = properties.get( material ); - - if ( renderItem === undefined ) { - - renderItem = { - id: object.id, - object: object, - geometry: geometry, - material: material, - program: materialProperties.program || defaultProgram, - groupOrder: groupOrder, - renderOrder: object.renderOrder, - z: z, - group: group - }; - - renderItems[ renderItemsIndex ] = renderItem; - - } else { - - renderItem.id = object.id; - renderItem.object = object; - renderItem.geometry = geometry; - renderItem.material = material; - renderItem.program = materialProperties.program || defaultProgram; - renderItem.groupOrder = groupOrder; - renderItem.renderOrder = object.renderOrder; - renderItem.z = z; - renderItem.group = group; - - } - - renderItemsIndex ++; - - return renderItem; - - } - - function push( object, geometry, material, groupOrder, z, group ) { - - const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - - ( material.transparent === true ? transparent : opaque ).push( renderItem ); - - } - - function unshift( object, geometry, material, groupOrder, z, group ) { - - const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - - ( material.transparent === true ? transparent : opaque ).unshift( renderItem ); - - } - - function sort( customOpaqueSort, customTransparentSort ) { - - if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); - if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); - - } - - function finish() { - - // Clear references from inactive renderItems in the list - - for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { - - const renderItem = renderItems[ i ]; - - if ( renderItem.id === null ) break; - - renderItem.id = null; - renderItem.object = null; - renderItem.geometry = null; - renderItem.material = null; - renderItem.program = null; - renderItem.group = null; - - } - - } - - return { - - opaque: opaque, - transparent: transparent, - - init: init, - push: push, - unshift: unshift, - finish: finish, - - sort: sort - }; - - } - - function WebGLRenderLists( properties ) { - - let lists = new WeakMap(); - - function get( scene, camera ) { - - const cameras = lists.get( scene ); - let list; - - if ( cameras === undefined ) { - - list = new WebGLRenderList( properties ); - lists.set( scene, new WeakMap() ); - lists.get( scene ).set( camera, list ); - - } else { - - list = cameras.get( camera ); - if ( list === undefined ) { - - list = new WebGLRenderList( properties ); - cameras.set( camera, list ); - - } - - } - - return list; - - } - - function dispose() { - - lists = new WeakMap(); - - } - - return { - get: get, - dispose: dispose - }; - - } - - function UniformsCache() { - - const lights = {}; - - return { - - get: function ( light ) { - - if ( lights[ light.id ] !== undefined ) { - - return lights[ light.id ]; - - } - - let uniforms; - - switch ( light.type ) { - - case 'DirectionalLight': - uniforms = { - direction: new Vector3(), - color: new Color() - }; - break; - - case 'SpotLight': - uniforms = { - position: new Vector3(), - direction: new Vector3(), - color: new Color(), - distance: 0, - coneCos: 0, - penumbraCos: 0, - decay: 0 - }; - break; - - case 'PointLight': - uniforms = { - position: new Vector3(), - color: new Color(), - distance: 0, - decay: 0 - }; - break; - - case 'HemisphereLight': - uniforms = { - direction: new Vector3(), - skyColor: new Color(), - groundColor: new Color() - }; - break; - - case 'RectAreaLight': - uniforms = { - color: new Color(), - position: new Vector3(), - halfWidth: new Vector3(), - halfHeight: new Vector3() - }; - break; - - } - - lights[ light.id ] = uniforms; - - return uniforms; - - } - - }; - - } - - function ShadowUniformsCache() { - - const lights = {}; - - return { - - get: function ( light ) { - - if ( lights[ light.id ] !== undefined ) { - - return lights[ light.id ]; - - } - - let uniforms; - - switch ( light.type ) { - - case 'DirectionalLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; - - case 'SpotLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; - - case 'PointLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2(), - shadowCameraNear: 1, - shadowCameraFar: 1000 - }; - break; - - // TODO (abelnation): set RectAreaLight shadow uniforms - - } - - lights[ light.id ] = uniforms; - - return uniforms; - - } - - }; - - } - - - - let nextVersion = 0; - - function shadowCastingLightsFirst( lightA, lightB ) { - - return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 ); - - } - - function WebGLLights() { - - const cache = new UniformsCache(); - - const shadowCache = ShadowUniformsCache(); - - const state = { - - version: 0, - - hash: { - directionalLength: - 1, - pointLength: - 1, - spotLength: - 1, - rectAreaLength: - 1, - hemiLength: - 1, - - numDirectionalShadows: - 1, - numPointShadows: - 1, - numSpotShadows: - 1 - }, - - ambient: [ 0, 0, 0 ], - probe: [], - directional: [], - directionalShadow: [], - directionalShadowMap: [], - directionalShadowMatrix: [], - spot: [], - spotShadow: [], - spotShadowMap: [], - spotShadowMatrix: [], - rectArea: [], - rectAreaLTC1: null, - rectAreaLTC2: null, - point: [], - pointShadow: [], - pointShadowMap: [], - pointShadowMatrix: [], - hemi: [] - - }; - - for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); - - const vector3 = new Vector3(); - const matrix4 = new Matrix4(); - const matrix42 = new Matrix4(); - - function setup( lights, shadows, camera ) { - - let r = 0, g = 0, b = 0; - - for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); - - let directionalLength = 0; - let pointLength = 0; - let spotLength = 0; - let rectAreaLength = 0; - let hemiLength = 0; - - let numDirectionalShadows = 0; - let numPointShadows = 0; - let numSpotShadows = 0; - - const viewMatrix = camera.matrixWorldInverse; - - lights.sort( shadowCastingLightsFirst ); - - for ( let i = 0, l = lights.length; i < l; i ++ ) { - - const light = lights[ i ]; - - const color = light.color; - const intensity = light.intensity; - const distance = light.distance; - - const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; - - if ( light.isAmbientLight ) { - - r += color.r * intensity; - g += color.g * intensity; - b += color.b * intensity; - - } else if ( light.isLightProbe ) { - - for ( let j = 0; j < 9; j ++ ) { - - state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); - - } - - } else if ( light.isDirectionalLight ) { - - const uniforms = cache.get( light ); - - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); - - if ( light.castShadow ) { - - const shadow = light.shadow; - - const shadowUniforms = shadowCache.get( light ); - - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - - state.directionalShadow[ directionalLength ] = shadowUniforms; - state.directionalShadowMap[ directionalLength ] = shadowMap; - state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; - - numDirectionalShadows ++; - - } - - state.directional[ directionalLength ] = uniforms; - - directionalLength ++; - - } else if ( light.isSpotLight ) { - - const uniforms = cache.get( light ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - uniforms.color.copy( color ).multiplyScalar( intensity ); - uniforms.distance = distance; - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); - - uniforms.coneCos = Math.cos( light.angle ); - uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); - uniforms.decay = light.decay; - - if ( light.castShadow ) { - - const shadow = light.shadow; - - const shadowUniforms = shadowCache.get( light ); - - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - - state.spotShadow[ spotLength ] = shadowUniforms; - state.spotShadowMap[ spotLength ] = shadowMap; - state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; - - numSpotShadows ++; - - } - - state.spot[ spotLength ] = uniforms; - - spotLength ++; - - } else if ( light.isRectAreaLight ) { - - const uniforms = cache.get( light ); - - // (a) intensity is the total visible light emitted - //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); - - // (b) intensity is the brightness of the light - uniforms.color.copy( color ).multiplyScalar( intensity ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - // extract local rotation of light to derive width/height half vectors - matrix42.identity(); - matrix4.copy( light.matrixWorld ); - matrix4.premultiply( viewMatrix ); - matrix42.extractRotation( matrix4 ); - - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - - uniforms.halfWidth.applyMatrix4( matrix42 ); - uniforms.halfHeight.applyMatrix4( matrix42 ); - - // TODO (abelnation): RectAreaLight distance? - // uniforms.distance = distance; - - state.rectArea[ rectAreaLength ] = uniforms; - - rectAreaLength ++; - - } else if ( light.isPointLight ) { - - const uniforms = cache.get( light ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.distance = light.distance; - uniforms.decay = light.decay; - - if ( light.castShadow ) { - - const shadow = light.shadow; - - const shadowUniforms = shadowCache.get( light ); - - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - shadowUniforms.shadowCameraNear = shadow.camera.near; - shadowUniforms.shadowCameraFar = shadow.camera.far; - - state.pointShadow[ pointLength ] = shadowUniforms; - state.pointShadowMap[ pointLength ] = shadowMap; - state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; - - numPointShadows ++; - - } - - state.point[ pointLength ] = uniforms; - - pointLength ++; - - } else if ( light.isHemisphereLight ) { - - const uniforms = cache.get( light ); - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - uniforms.direction.transformDirection( viewMatrix ); - uniforms.direction.normalize(); - - uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); - uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); - - state.hemi[ hemiLength ] = uniforms; - - hemiLength ++; - - } - - } - - if ( rectAreaLength > 0 ) { - - state.rectAreaLTC1 = UniformsLib.LTC_1; - state.rectAreaLTC2 = UniformsLib.LTC_2; - - } - - state.ambient[ 0 ] = r; - state.ambient[ 1 ] = g; - state.ambient[ 2 ] = b; - - const hash = state.hash; - - if ( hash.directionalLength !== directionalLength || - hash.pointLength !== pointLength || - hash.spotLength !== spotLength || - hash.rectAreaLength !== rectAreaLength || - hash.hemiLength !== hemiLength || - hash.numDirectionalShadows !== numDirectionalShadows || - hash.numPointShadows !== numPointShadows || - hash.numSpotShadows !== numSpotShadows ) { - - state.directional.length = directionalLength; - state.spot.length = spotLength; - state.rectArea.length = rectAreaLength; - state.point.length = pointLength; - state.hemi.length = hemiLength; - - state.directionalShadow.length = numDirectionalShadows; - state.directionalShadowMap.length = numDirectionalShadows; - state.pointShadow.length = numPointShadows; - state.pointShadowMap.length = numPointShadows; - state.spotShadow.length = numSpotShadows; - state.spotShadowMap.length = numSpotShadows; - state.directionalShadowMatrix.length = numDirectionalShadows; - state.pointShadowMatrix.length = numPointShadows; - state.spotShadowMatrix.length = numSpotShadows; - - hash.directionalLength = directionalLength; - hash.pointLength = pointLength; - hash.spotLength = spotLength; - hash.rectAreaLength = rectAreaLength; - hash.hemiLength = hemiLength; - - hash.numDirectionalShadows = numDirectionalShadows; - hash.numPointShadows = numPointShadows; - hash.numSpotShadows = numSpotShadows; - - state.version = nextVersion ++; - - } - - } - - return { - setup: setup, - state: state - }; - - } - - function WebGLRenderState() { - - const lights = new WebGLLights(); - - const lightsArray = []; - const shadowsArray = []; - - function init() { - - lightsArray.length = 0; - shadowsArray.length = 0; - - } - - function pushLight( light ) { - - lightsArray.push( light ); - - } - - function pushShadow( shadowLight ) { - - shadowsArray.push( shadowLight ); - - } - - function setupLights( camera ) { - - lights.setup( lightsArray, shadowsArray, camera ); - - } - - const state = { - lightsArray: lightsArray, - shadowsArray: shadowsArray, - - lights: lights - }; - - return { - init: init, - state: state, - setupLights: setupLights, - - pushLight: pushLight, - pushShadow: pushShadow - }; - - } - - function WebGLRenderStates() { - - let renderStates = new WeakMap(); - - function get( scene, camera ) { - - let renderState; - - if ( renderStates.has( scene ) === false ) { - - renderState = new WebGLRenderState(); - renderStates.set( scene, new WeakMap() ); - renderStates.get( scene ).set( camera, renderState ); - - } else { - - if ( renderStates.get( scene ).has( camera ) === false ) { - - renderState = new WebGLRenderState(); - renderStates.get( scene ).set( camera, renderState ); - - } else { - - renderState = renderStates.get( scene ).get( camera ); - - } - - } - - return renderState; - - } - - function dispose() { - - renderStates = new WeakMap(); - - } - - return { - get: get, - dispose: dispose - }; - - } - - /** - * parameters = { - * - * opacity: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ - - function MeshDepthMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshDepthMaterial'; - - this.depthPacking = BasicDepthPacking; - - this.skinning = false; - this.morphTargets = false; - - this.map = null; - - this.alphaMap = null; - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; - - this.setValues( parameters ); - - } - - MeshDepthMaterial.prototype = Object.create( Material.prototype ); - MeshDepthMaterial.prototype.constructor = MeshDepthMaterial; - - MeshDepthMaterial.prototype.isMeshDepthMaterial = true; - - MeshDepthMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.depthPacking = source.depthPacking; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - return this; - - }; - - /** - * parameters = { - * - * referencePosition: , - * nearDistance: , - * farDistance: , - * - * skinning: , - * morphTargets: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: - * - * } - */ - - function MeshDistanceMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshDistanceMaterial'; - - this.referencePosition = new Vector3(); - this.nearDistance = 1; - this.farDistance = 1000; - - this.skinning = false; - this.morphTargets = false; - - this.map = null; - - this.alphaMap = null; - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.fog = false; - - this.setValues( parameters ); - - } - - MeshDistanceMaterial.prototype = Object.create( Material.prototype ); - MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial; - - MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; - - MeshDistanceMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.referencePosition.copy( source.referencePosition ); - this.nearDistance = source.nearDistance; - this.farDistance = source.farDistance; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - return this; - - }; - - var vsm_frag = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n\tfor ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n\t\t#ifdef HORIZONAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean * HALF_SAMPLE_RATE;\n\tsquared_mean = squared_mean * HALF_SAMPLE_RATE;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; - - var vsm_vert = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; - - function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { - - let _frustum = new Frustum(); - - const _shadowMapSize = new Vector2(), - _viewportSize = new Vector2(), - - _viewport = new Vector4(), - - _depthMaterials = [], - _distanceMaterials = [], - - _materialCache = {}; - - const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; - - const shadowMaterialVertical = new ShaderMaterial( { - - defines: { - SAMPLE_RATE: 2.0 / 8.0, - HALF_SAMPLE_RATE: 1.0 / 8.0 - }, - - uniforms: { - shadow_pass: { value: null }, - resolution: { value: new Vector2() }, - radius: { value: 4.0 } - }, - - vertexShader: vsm_vert, - - fragmentShader: vsm_frag - - } ); - - const shadowMaterialHorizonal = shadowMaterialVertical.clone(); - shadowMaterialHorizonal.defines.HORIZONAL_PASS = 1; - - const fullScreenTri = new BufferGeometry(); - fullScreenTri.setAttribute( - "position", - new BufferAttribute( - new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), - 3 - ) - ); - - const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); - - const scope = this; - - this.enabled = false; - - this.autoUpdate = true; - this.needsUpdate = false; - - this.type = PCFShadowMap; - - this.render = function ( lights, scene, camera ) { - - if ( scope.enabled === false ) return; - if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; - - if ( lights.length === 0 ) return; - - const currentRenderTarget = _renderer.getRenderTarget(); - const activeCubeFace = _renderer.getActiveCubeFace(); - const activeMipmapLevel = _renderer.getActiveMipmapLevel(); - - const _state = _renderer.state; - - // Set GL state for depth map. - _state.setBlending( NoBlending ); - _state.buffers.color.setClear( 1, 1, 1, 1 ); - _state.buffers.depth.setTest( true ); - _state.setScissorTest( false ); - - // render depth map - - for ( let i = 0, il = lights.length; i < il; i ++ ) { - - const light = lights[ i ]; - const shadow = light.shadow; - - if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; - - if ( shadow === undefined ) { - - console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); - continue; - - } - - _shadowMapSize.copy( shadow.mapSize ); - - const shadowFrameExtents = shadow.getFrameExtents(); - - _shadowMapSize.multiply( shadowFrameExtents ); - - _viewportSize.copy( shadow.mapSize ); - - if ( _shadowMapSize.x > maxTextureSize || _shadowMapSize.y > maxTextureSize ) { - - if ( _shadowMapSize.x > maxTextureSize ) { - - _viewportSize.x = Math.floor( maxTextureSize / shadowFrameExtents.x ); - _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; - shadow.mapSize.x = _viewportSize.x; - - } - - if ( _shadowMapSize.y > maxTextureSize ) { - - _viewportSize.y = Math.floor( maxTextureSize / shadowFrameExtents.y ); - _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; - shadow.mapSize.y = _viewportSize.y; - - } - - } - - if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - - const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; - - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + ".shadowMap"; - - shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - - shadow.camera.updateProjectionMatrix(); - - } - - if ( shadow.map === null ) { - - const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; - - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + ".shadowMap"; - - shadow.camera.updateProjectionMatrix(); - - } - - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - - const viewportCount = shadow.getViewportCount(); - - for ( let vp = 0; vp < viewportCount; vp ++ ) { - - const viewport = shadow.getViewport( vp ); - - _viewport.set( - _viewportSize.x * viewport.x, - _viewportSize.y * viewport.y, - _viewportSize.x * viewport.z, - _viewportSize.y * viewport.w - ); - - _state.viewport( _viewport ); - - shadow.updateMatrices( light, vp ); - - _frustum = shadow.getFrustum(); - - renderObject( scene, camera, shadow.camera, light, this.type ); - - } - - // do blur pass for VSM - - if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - - VSMPass( shadow, camera ); - - } - - shadow.needsUpdate = false; - - } - - scope.needsUpdate = false; - - _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); - - }; - - function VSMPass( shadow, camera ) { - - const geometry = _objects.update( fullScreenMesh ); - - // vertical pass - - shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; - shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; - shadowMaterialVertical.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.mapPass ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); - - // horizonal pass - - shadowMaterialHorizonal.uniforms.shadow_pass.value = shadow.mapPass.texture; - shadowMaterialHorizonal.uniforms.resolution.value = shadow.mapSize; - shadowMaterialHorizonal.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizonal, fullScreenMesh, null ); - - } - - function getDepthMaterialVariant( useMorphing, useSkinning, useInstancing ) { - - const index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; - - let material = _depthMaterials[ index ]; - - if ( material === undefined ) { - - material = new MeshDepthMaterial( { - - depthPacking: RGBADepthPacking, - - morphTargets: useMorphing, - skinning: useSkinning - - } ); - - _depthMaterials[ index ] = material; - - } - - return material; - - } - - function getDistanceMaterialVariant( useMorphing, useSkinning, useInstancing ) { - - const index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; - - let material = _distanceMaterials[ index ]; - - if ( material === undefined ) { - - material = new MeshDistanceMaterial( { - - morphTargets: useMorphing, - skinning: useSkinning - - } ); - - _distanceMaterials[ index ] = material; - - } - - return material; - - } - - function getDepthMaterial( object, geometry, material, light, shadowCameraNear, shadowCameraFar, type ) { - - let result = null; - - let getMaterialVariant = getDepthMaterialVariant; - let customMaterial = object.customDepthMaterial; - - if ( light.isPointLight === true ) { - - getMaterialVariant = getDistanceMaterialVariant; - customMaterial = object.customDistanceMaterial; - - } - - if ( customMaterial === undefined ) { - - let useMorphing = false; - - if ( material.morphTargets === true ) { - - useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; - - } - - let useSkinning = false; - - if ( object.isSkinnedMesh === true ) { - - if ( material.skinning === true ) { - - useSkinning = true; - - } else { - - console.warn( 'THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object ); - - } - - } - - const useInstancing = object.isInstancedMesh === true; - - result = getMaterialVariant( useMorphing, useSkinning, useInstancing ); - - } else { - - result = customMaterial; - - } - - if ( _renderer.localClippingEnabled && - material.clipShadows === true && - material.clippingPlanes.length !== 0 ) { - - // in this case we need a unique material instance reflecting the - // appropriate state - - const keyA = result.uuid, keyB = material.uuid; - - let materialsForVariant = _materialCache[ keyA ]; - - if ( materialsForVariant === undefined ) { - - materialsForVariant = {}; - _materialCache[ keyA ] = materialsForVariant; - - } - - let cachedMaterial = materialsForVariant[ keyB ]; - - if ( cachedMaterial === undefined ) { - - cachedMaterial = result.clone(); - materialsForVariant[ keyB ] = cachedMaterial; - - } - - result = cachedMaterial; - - } - - result.visible = material.visible; - result.wireframe = material.wireframe; - - if ( type === VSMShadowMap ) { - - result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; - - } else { - - result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; - - } - - result.clipShadows = material.clipShadows; - result.clippingPlanes = material.clippingPlanes; - result.clipIntersection = material.clipIntersection; - - result.wireframeLinewidth = material.wireframeLinewidth; - result.linewidth = material.linewidth; - - if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { - - result.referencePosition.setFromMatrixPosition( light.matrixWorld ); - result.nearDistance = shadowCameraNear; - result.farDistance = shadowCameraFar; - - } - - return result; - - } - - function renderObject( object, camera, shadowCamera, light, type ) { - - if ( object.visible === false ) return; - - const visible = object.layers.test( camera.layers ); - - if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { - - if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { - - object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - - const geometry = _objects.update( object ); - const material = object.material; - - if ( Array.isArray( material ) ) { - - const groups = geometry.groups; - - for ( let k = 0, kl = groups.length; k < kl; k ++ ) { - - const group = groups[ k ]; - const groupMaterial = material[ group.materialIndex ]; - - if ( groupMaterial && groupMaterial.visible ) { - - const depthMaterial = getDepthMaterial( object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); - - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); - - } - - } - - } else if ( material.visible ) { - - const depthMaterial = getDepthMaterial( object, geometry, material, light, shadowCamera.near, shadowCamera.far, type ); - - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); - - } - - } - - } - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - renderObject( children[ i ], camera, shadowCamera, light, type ); - - } - - } - - } - - function WebGLState( gl, extensions, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - function ColorBuffer() { - - let locked = false; - - const color = new Vector4(); - let currentColorMask = null; - const currentColorClear = new Vector4( 0, 0, 0, 0 ); - - return { - - setMask: function ( colorMask ) { - - if ( currentColorMask !== colorMask && ! locked ) { - - gl.colorMask( colorMask, colorMask, colorMask, colorMask ); - currentColorMask = colorMask; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( r, g, b, a, premultipliedAlpha ) { - - if ( premultipliedAlpha === true ) { - - r *= a; g *= a; b *= a; - - } - - color.set( r, g, b, a ); - - if ( currentColorClear.equals( color ) === false ) { - - gl.clearColor( r, g, b, a ); - currentColorClear.copy( color ); - - } - - }, - - reset: function () { - - locked = false; - - currentColorMask = null; - currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state - - } - - }; - - } - - function DepthBuffer() { - - let locked = false; - - let currentDepthMask = null; - let currentDepthFunc = null; - let currentDepthClear = null; - - return { - - setTest: function ( depthTest ) { - - if ( depthTest ) { - - enable( 2929 ); - - } else { - - disable( 2929 ); - - } - - }, - - setMask: function ( depthMask ) { - - if ( currentDepthMask !== depthMask && ! locked ) { - - gl.depthMask( depthMask ); - currentDepthMask = depthMask; - - } - - }, - - setFunc: function ( depthFunc ) { - - if ( currentDepthFunc !== depthFunc ) { - - if ( depthFunc ) { - - switch ( depthFunc ) { - - case NeverDepth: - - gl.depthFunc( 512 ); - break; - - case AlwaysDepth: - - gl.depthFunc( 519 ); - break; - - case LessDepth: - - gl.depthFunc( 513 ); - break; - - case LessEqualDepth: - - gl.depthFunc( 515 ); - break; - - case EqualDepth: - - gl.depthFunc( 514 ); - break; - - case GreaterEqualDepth: - - gl.depthFunc( 518 ); - break; - - case GreaterDepth: - - gl.depthFunc( 516 ); - break; - - case NotEqualDepth: - - gl.depthFunc( 517 ); - break; - - default: - - gl.depthFunc( 515 ); - - } - - } else { - - gl.depthFunc( 515 ); - - } - - currentDepthFunc = depthFunc; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( depth ) { - - if ( currentDepthClear !== depth ) { - - gl.clearDepth( depth ); - currentDepthClear = depth; - - } - - }, - - reset: function () { - - locked = false; - - currentDepthMask = null; - currentDepthFunc = null; - currentDepthClear = null; - - } - - }; - - } - - function StencilBuffer() { - - let locked = false; - - let currentStencilMask = null; - let currentStencilFunc = null; - let currentStencilRef = null; - let currentStencilFuncMask = null; - let currentStencilFail = null; - let currentStencilZFail = null; - let currentStencilZPass = null; - let currentStencilClear = null; - - return { - - setTest: function ( stencilTest ) { - - if ( ! locked ) { - - if ( stencilTest ) { - - enable( 2960 ); - - } else { - - disable( 2960 ); - - } - - } - - }, - - setMask: function ( stencilMask ) { - - if ( currentStencilMask !== stencilMask && ! locked ) { - - gl.stencilMask( stencilMask ); - currentStencilMask = stencilMask; - - } - - }, - - setFunc: function ( stencilFunc, stencilRef, stencilMask ) { - - if ( currentStencilFunc !== stencilFunc || - currentStencilRef !== stencilRef || - currentStencilFuncMask !== stencilMask ) { - - gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); - - currentStencilFunc = stencilFunc; - currentStencilRef = stencilRef; - currentStencilFuncMask = stencilMask; - - } - - }, - - setOp: function ( stencilFail, stencilZFail, stencilZPass ) { - - if ( currentStencilFail !== stencilFail || - currentStencilZFail !== stencilZFail || - currentStencilZPass !== stencilZPass ) { - - gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); - - currentStencilFail = stencilFail; - currentStencilZFail = stencilZFail; - currentStencilZPass = stencilZPass; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( stencil ) { - - if ( currentStencilClear !== stencil ) { - - gl.clearStencil( stencil ); - currentStencilClear = stencil; - - } - - }, - - reset: function () { - - locked = false; - - currentStencilMask = null; - currentStencilFunc = null; - currentStencilRef = null; - currentStencilFuncMask = null; - currentStencilFail = null; - currentStencilZFail = null; - currentStencilZPass = null; - currentStencilClear = null; - - } - - }; - - } - - // - - const colorBuffer = new ColorBuffer(); - const depthBuffer = new DepthBuffer(); - const stencilBuffer = new StencilBuffer(); - - let enabledCapabilities = {}; - - let currentProgram = null; - - let currentBlendingEnabled = null; - let currentBlending = null; - let currentBlendEquation = null; - let currentBlendSrc = null; - let currentBlendDst = null; - let currentBlendEquationAlpha = null; - let currentBlendSrcAlpha = null; - let currentBlendDstAlpha = null; - let currentPremultipledAlpha = false; - - let currentFlipSided = null; - let currentCullFace = null; - - let currentLineWidth = null; - - let currentPolygonOffsetFactor = null; - let currentPolygonOffsetUnits = null; - - const maxTextures = gl.getParameter( 35661 ); - - let lineWidthAvailable = false; - let version = 0; - const glVersion = gl.getParameter( 7938 ); - - if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { - - version = parseFloat( /^WebGL\ ([0-9])/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 1.0 ); - - } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { - - version = parseFloat( /^OpenGL\ ES\ ([0-9])/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 2.0 ); - - } - - let currentTextureSlot = null; - let currentBoundTextures = {}; - - const currentScissor = new Vector4(); - const currentViewport = new Vector4(); - - function createTexture( type, target, count ) { - - const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. - const texture = gl.createTexture(); - - gl.bindTexture( type, texture ); - gl.texParameteri( type, 10241, 9728 ); - gl.texParameteri( type, 10240, 9728 ); - - for ( let i = 0; i < count; i ++ ) { - - gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); - - } - - return texture; - - } - - const emptyTextures = {}; - emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); - emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); - - // init - - colorBuffer.setClear( 0, 0, 0, 1 ); - depthBuffer.setClear( 1 ); - stencilBuffer.setClear( 0 ); - - enable( 2929 ); - depthBuffer.setFunc( LessEqualDepth ); - - setFlipSided( false ); - setCullFace( CullFaceBack ); - enable( 2884 ); - - setBlending( NoBlending ); - - // - - function enable( id ) { - - if ( enabledCapabilities[ id ] !== true ) { - - gl.enable( id ); - enabledCapabilities[ id ] = true; - - } - - } - - function disable( id ) { - - if ( enabledCapabilities[ id ] !== false ) { - - gl.disable( id ); - enabledCapabilities[ id ] = false; - - } - - } - - function useProgram( program ) { - - if ( currentProgram !== program ) { - - gl.useProgram( program ); - - currentProgram = program; - - return true; - - } - - return false; - - } - - const equationToGL = { - [ AddEquation ]: 32774, - [ SubtractEquation ]: 32778, - [ ReverseSubtractEquation ]: 32779 - }; - - if ( isWebGL2 ) { - - equationToGL[ MinEquation ] = 32775; - equationToGL[ MaxEquation ] = 32776; - - } else { - - const extension = extensions.get( 'EXT_blend_minmax' ); - - if ( extension !== null ) { - - equationToGL[ MinEquation ] = extension.MIN_EXT; - equationToGL[ MaxEquation ] = extension.MAX_EXT; - - } - - } - - const factorToGL = { - [ ZeroFactor ]: 0, - [ OneFactor ]: 1, - [ SrcColorFactor ]: 768, - [ SrcAlphaFactor ]: 770, - [ SrcAlphaSaturateFactor ]: 776, - [ DstColorFactor ]: 774, - [ DstAlphaFactor ]: 772, - [ OneMinusSrcColorFactor ]: 769, - [ OneMinusSrcAlphaFactor ]: 771, - [ OneMinusDstColorFactor ]: 775, - [ OneMinusDstAlphaFactor ]: 773 - }; - - function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { - - if ( blending === NoBlending ) { - - if ( currentBlendingEnabled ) { - - disable( 3042 ); - currentBlendingEnabled = false; - - } - - return; - - } - - if ( ! currentBlendingEnabled ) { - - enable( 3042 ); - currentBlendingEnabled = true; - - } - - if ( blending !== CustomBlending ) { - - if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { - - if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { - - gl.blendEquation( 32774 ); - - currentBlendEquation = AddEquation; - currentBlendEquationAlpha = AddEquation; - - } - - if ( premultipliedAlpha ) { - - switch ( blending ) { - - case NormalBlending: - gl.blendFuncSeparate( 1, 771, 1, 771 ); - break; - - case AdditiveBlending: - gl.blendFunc( 1, 1 ); - break; - - case SubtractiveBlending: - gl.blendFuncSeparate( 0, 0, 769, 771 ); - break; - - case MultiplyBlending: - gl.blendFuncSeparate( 0, 768, 0, 770 ); - break; - - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; - - } - - } else { - - switch ( blending ) { - - case NormalBlending: - gl.blendFuncSeparate( 770, 771, 1, 771 ); - break; - - case AdditiveBlending: - gl.blendFunc( 770, 1 ); - break; - - case SubtractiveBlending: - gl.blendFunc( 0, 769 ); - break; - - case MultiplyBlending: - gl.blendFunc( 0, 768 ); - break; - - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; - - } - - } - - currentBlendSrc = null; - currentBlendDst = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; - - currentBlending = blending; - currentPremultipledAlpha = premultipliedAlpha; - - } - - return; - - } - - // custom blending - - blendEquationAlpha = blendEquationAlpha || blendEquation; - blendSrcAlpha = blendSrcAlpha || blendSrc; - blendDstAlpha = blendDstAlpha || blendDst; - - if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { - - gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); - - currentBlendEquation = blendEquation; - currentBlendEquationAlpha = blendEquationAlpha; - - } - - if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { - - gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); - - currentBlendSrc = blendSrc; - currentBlendDst = blendDst; - currentBlendSrcAlpha = blendSrcAlpha; - currentBlendDstAlpha = blendDstAlpha; - - } - - currentBlending = blending; - currentPremultipledAlpha = null; - - } - - function setMaterial( material, frontFaceCW ) { - - material.side === DoubleSide - ? disable( 2884 ) - : enable( 2884 ); - - let flipSided = ( material.side === BackSide ); - if ( frontFaceCW ) flipSided = ! flipSided; - - setFlipSided( flipSided ); - - ( material.blending === NormalBlending && material.transparent === false ) - ? setBlending( NoBlending ) - : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); - - depthBuffer.setFunc( material.depthFunc ); - depthBuffer.setTest( material.depthTest ); - depthBuffer.setMask( material.depthWrite ); - colorBuffer.setMask( material.colorWrite ); - - const stencilWrite = material.stencilWrite; - stencilBuffer.setTest( stencilWrite ); - if ( stencilWrite ) { - - stencilBuffer.setMask( material.stencilWriteMask ); - stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); - stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); - - } - - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - - } - - // - - function setFlipSided( flipSided ) { - - if ( currentFlipSided !== flipSided ) { - - if ( flipSided ) { - - gl.frontFace( 2304 ); - - } else { - - gl.frontFace( 2305 ); - - } - - currentFlipSided = flipSided; - - } - - } - - function setCullFace( cullFace ) { - - if ( cullFace !== CullFaceNone ) { - - enable( 2884 ); - - if ( cullFace !== currentCullFace ) { - - if ( cullFace === CullFaceBack ) { - - gl.cullFace( 1029 ); - - } else if ( cullFace === CullFaceFront ) { - - gl.cullFace( 1028 ); - - } else { - - gl.cullFace( 1032 ); - - } - - } - - } else { - - disable( 2884 ); - - } - - currentCullFace = cullFace; - - } - - function setLineWidth( width ) { - - if ( width !== currentLineWidth ) { - - if ( lineWidthAvailable ) gl.lineWidth( width ); - - currentLineWidth = width; - - } - - } - - function setPolygonOffset( polygonOffset, factor, units ) { - - if ( polygonOffset ) { - - enable( 32823 ); - - if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { - - gl.polygonOffset( factor, units ); - - currentPolygonOffsetFactor = factor; - currentPolygonOffsetUnits = units; - - } - - } else { - - disable( 32823 ); - - } - - } - - function setScissorTest( scissorTest ) { - - if ( scissorTest ) { - - enable( 3089 ); - - } else { - - disable( 3089 ); - - } - - } - - // texture - - function activeTexture( webglSlot ) { - - if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1; - - if ( currentTextureSlot !== webglSlot ) { - - gl.activeTexture( webglSlot ); - currentTextureSlot = webglSlot; - - } - - } - - function bindTexture( webglType, webglTexture ) { - - if ( currentTextureSlot === null ) { - - activeTexture(); - - } - - let boundTexture = currentBoundTextures[ currentTextureSlot ]; - - if ( boundTexture === undefined ) { - - boundTexture = { type: undefined, texture: undefined }; - currentBoundTextures[ currentTextureSlot ] = boundTexture; - - } - - if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { - - gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); - - boundTexture.type = webglType; - boundTexture.texture = webglTexture; - - } - - } - - function unbindTexture() { - - const boundTexture = currentBoundTextures[ currentTextureSlot ]; - - if ( boundTexture !== undefined && boundTexture.type !== undefined ) { - - gl.bindTexture( boundTexture.type, null ); - - boundTexture.type = undefined; - boundTexture.texture = undefined; - - } - - } - - function compressedTexImage2D() { - - try { - - gl.compressedTexImage2D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( 'THREE.WebGLState:', error ); - - } - - } - - function texImage2D() { - - try { - - gl.texImage2D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( 'THREE.WebGLState:', error ); - - } - - } - - function texImage3D() { - - try { - - gl.texImage3D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( 'THREE.WebGLState:', error ); - - } - - } - - // - - function scissor( scissor ) { - - if ( currentScissor.equals( scissor ) === false ) { - - gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); - currentScissor.copy( scissor ); - - } - - } - - function viewport( viewport ) { - - if ( currentViewport.equals( viewport ) === false ) { - - gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); - currentViewport.copy( viewport ); - - } - - } - - // - - function reset() { - - enabledCapabilities = {}; - - currentTextureSlot = null; - currentBoundTextures = {}; - - currentProgram = null; - - currentBlending = null; - - currentFlipSided = null; - currentCullFace = null; - - colorBuffer.reset(); - depthBuffer.reset(); - stencilBuffer.reset(); - - } - - return { - - buffers: { - color: colorBuffer, - depth: depthBuffer, - stencil: stencilBuffer - }, - - enable: enable, - disable: disable, - - useProgram: useProgram, - - setBlending: setBlending, - setMaterial: setMaterial, - - setFlipSided: setFlipSided, - setCullFace: setCullFace, - - setLineWidth: setLineWidth, - setPolygonOffset: setPolygonOffset, - - setScissorTest: setScissorTest, - - activeTexture: activeTexture, - bindTexture: bindTexture, - unbindTexture: unbindTexture, - compressedTexImage2D: compressedTexImage2D, - texImage2D: texImage2D, - texImage3D: texImage3D, - - scissor: scissor, - viewport: viewport, - - reset: reset - - }; - - } - - function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { - - const isWebGL2 = capabilities.isWebGL2; - const maxTextures = capabilities.maxTextures; - const maxCubemapSize = capabilities.maxCubemapSize; - const maxTextureSize = capabilities.maxTextureSize; - const maxSamples = capabilities.maxSamples; - - const _videoTextures = new WeakMap(); - let _canvas; - - // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, - // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! - // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). - - let useOffscreenCanvas = false; - - try { - - useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' - && ( new OffscreenCanvas( 1, 1 ).getContext( "2d" ) ) !== null; - - } catch ( err ) { - - // Ignore any errors - - } - - function createCanvas( width, height ) { - - // Use OffscreenCanvas when available. Specially needed in web workers - - return useOffscreenCanvas ? - new OffscreenCanvas( width, height ) : - document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - - } - - function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { - - let scale = 1; - - // handle case if texture exceeds max size - - if ( image.width > maxSize || image.height > maxSize ) { - - scale = maxSize / Math.max( image.width, image.height ); - - } - - // only perform resize if necessary - - if ( scale < 1 || needsPowerOfTwo === true ) { - - // only perform resize for certain image types - - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || - ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - - const floor = needsPowerOfTwo ? MathUtils.floorPowerOfTwo : Math.floor; - - const width = floor( scale * image.width ); - const height = floor( scale * image.height ); - - if ( _canvas === undefined ) _canvas = createCanvas( width, height ); - - // cube textures can't reuse the same canvas - - const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; - - canvas.width = width; - canvas.height = height; - - const context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, width, height ); - - console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); - - return canvas; - - } else { - - if ( 'data' in image ) { - - console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); - - } - - return image; - - } - - } - - return image; - - } - - function isPowerOfTwo( image ) { - - return MathUtils.isPowerOfTwo( image.width ) && MathUtils.isPowerOfTwo( image.height ); - - } - - function textureNeedsPowerOfTwo( texture ) { - - if ( isWebGL2 ) return false; - - return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || - ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); - - } - - function textureNeedsGenerateMipmaps( texture, supportsMips ) { - - return texture.generateMipmaps && supportsMips && - texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; - - } - - function generateMipmap( target, texture, width, height ) { - - _gl.generateMipmap( target ); - - const textureProperties = properties.get( texture ); - - // Note: Math.log( x ) * Math.LOG2E used instead of Math.log2( x ) which is not supported by IE11 - textureProperties.__maxMipLevel = Math.log( Math.max( width, height ) ) * Math.LOG2E; - - } - - function getInternalFormat( internalFormatName, glFormat, glType ) { - - if ( isWebGL2 === false ) return glFormat; - - if ( internalFormatName !== null ) { - - if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; - - console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); - - } - - let internalFormat = glFormat; - - if ( glFormat === 6403 ) { - - if ( glType === 5126 ) internalFormat = 33326; - if ( glType === 5131 ) internalFormat = 33325; - if ( glType === 5121 ) internalFormat = 33321; - - } - - if ( glFormat === 6407 ) { - - if ( glType === 5126 ) internalFormat = 34837; - if ( glType === 5131 ) internalFormat = 34843; - if ( glType === 5121 ) internalFormat = 32849; - - } - - if ( glFormat === 6408 ) { - - if ( glType === 5126 ) internalFormat = 34836; - if ( glType === 5131 ) internalFormat = 34842; - if ( glType === 5121 ) internalFormat = 32856; - - } - - if ( internalFormat === 33325 || internalFormat === 33326 || - internalFormat === 34842 || internalFormat === 34836 ) { - - extensions.get( 'EXT_color_buffer_float' ); - - } - - return internalFormat; - - } - - // Fallback filters for non-power-of-2 textures - - function filterFallback( f ) { - - if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { - - return 9728; - - } - - return 9729; - - } - - // - - function onTextureDispose( event ) { - - const texture = event.target; - - texture.removeEventListener( 'dispose', onTextureDispose ); - - deallocateTexture( texture ); - - if ( texture.isVideoTexture ) { - - _videoTextures.delete( texture ); - - } - - info.memory.textures --; - - } - - function onRenderTargetDispose( event ) { - - const renderTarget = event.target; - - renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); - - deallocateRenderTarget( renderTarget ); - - info.memory.textures --; - - } - - // - - function deallocateTexture( texture ) { - - const textureProperties = properties.get( texture ); - - if ( textureProperties.__webglInit === undefined ) return; - - _gl.deleteTexture( textureProperties.__webglTexture ); - - properties.remove( texture ); - - } - - function deallocateRenderTarget( renderTarget ) { - - const renderTargetProperties = properties.get( renderTarget ); - const textureProperties = properties.get( renderTarget.texture ); - - if ( ! renderTarget ) return; - - if ( textureProperties.__webglTexture !== undefined ) { - - _gl.deleteTexture( textureProperties.__webglTexture ); - - } - - if ( renderTarget.depthTexture ) { - - renderTarget.depthTexture.dispose(); - - } - - if ( renderTarget.isWebGLCubeRenderTarget ) { - - for ( let i = 0; i < 6; i ++ ) { - - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); - - } - - } else { - - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); - if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); - if ( renderTargetProperties.__webglColorRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer ); - if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); - - } - - properties.remove( renderTarget.texture ); - properties.remove( renderTarget ); - - } - - // - - let textureUnits = 0; - - function resetTextureUnits() { - - textureUnits = 0; - - } - - function allocateTextureUnit() { - - const textureUnit = textureUnits; - - if ( textureUnit >= maxTextures ) { - - console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures ); - - } - - textureUnits += 1; - - return textureUnit; - - } - - // - - function setTexture2D( texture, slot ) { - - const textureProperties = properties.get( texture ); - - if ( texture.isVideoTexture ) updateVideoTexture( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - const image = texture.image; - - if ( image === undefined ) { - - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined' ); - - } else if ( image.complete === false ) { - - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); - - } else { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 3553, textureProperties.__webglTexture ); - - } - - function setTexture2DArray( texture, slot ) { - - const textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 35866, textureProperties.__webglTexture ); - - } - - function setTexture3D( texture, slot ) { - - const textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 32879, textureProperties.__webglTexture ); - - } - - function setTextureCube( texture, slot ) { - - if ( texture.image.length !== 6 ) return; - - const textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - initTexture( textureProperties, texture ); - - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); - - _gl.pixelStorei( 37440, texture.flipY ); - - const isCompressed = ( texture && ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ) ); - const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); - - const cubeImage = []; - - for ( let i = 0; i < 6; i ++ ) { - - if ( ! isCompressed && ! isDataTexture ) { - - cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); - - } else { - - cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; - - } - - } - - const image = cubeImage[ 0 ], - supportsMips = isPowerOfTwo( image ) || isWebGL2, - glFormat = utils.convert( texture.format ), - glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - - setTextureParameters( 34067, texture, supportsMips ); - - let mipmaps; - - if ( isCompressed ) { - - for ( let i = 0; i < 6; i ++ ) { - - mipmaps = cubeImage[ i ].mipmaps; - - for ( let j = 0; j < mipmaps.length; j ++ ) { - - const mipmap = mipmaps[ j ]; - - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { - - if ( glFormat !== null ) { - - state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - - } else { - - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); - - } - - } else { - - state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - } - - } - - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else { - - mipmaps = texture.mipmaps; - - for ( let i = 0; i < 6; i ++ ) { - - if ( isDataTexture ) { - - state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - - for ( let j = 0; j < mipmaps.length; j ++ ) { - - const mipmap = mipmaps[ j ]; - const mipmapImage = mipmap.image[ i ].image; - - state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); - - } - - } else { - - state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); - - for ( let j = 0; j < mipmaps.length; j ++ ) { - - const mipmap = mipmaps[ j ]; - - state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); - - } - - } - - } - - textureProperties.__maxMipLevel = mipmaps.length; - - } - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - // We assume images for cube map have the same size. - generateMipmap( 34067, texture, image.width, image.height ); - - } - - textureProperties.__version = texture.version; - - if ( texture.onUpdate ) texture.onUpdate( texture ); - - } else { - - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); - - } - - } - - function setTextureCubeDynamic( texture, slot ) { - - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, properties.get( texture ).__webglTexture ); - - } - - const wrappingToGL = { - [ RepeatWrapping ]: 10497, - [ ClampToEdgeWrapping ]: 33071, - [ MirroredRepeatWrapping ]: 33648 - }; - - const filterToGL = { - [ NearestFilter ]: 9728, - [ NearestMipmapNearestFilter ]: 9984, - [ NearestMipmapLinearFilter ]: 9986, - - [ LinearFilter ]: 9729, - [ LinearMipmapNearestFilter ]: 9985, - [ LinearMipmapLinearFilter ]: 9987 - }; - - function setTextureParameters( textureType, texture, supportsMips ) { - - if ( supportsMips ) { - - _gl.texParameteri( textureType, 10242, wrappingToGL[ texture.wrapS ] ); - _gl.texParameteri( textureType, 10243, wrappingToGL[ texture.wrapT ] ); - - if ( textureType === 32879 || textureType === 35866 ) { - - _gl.texParameteri( textureType, 32882, wrappingToGL[ texture.wrapR ] ); - - } - - _gl.texParameteri( textureType, 10240, filterToGL[ texture.magFilter ] ); - _gl.texParameteri( textureType, 10241, filterToGL[ texture.minFilter ] ); - - } else { - - _gl.texParameteri( textureType, 10242, 33071 ); - _gl.texParameteri( textureType, 10243, 33071 ); - - if ( textureType === 32879 || textureType === 35866 ) { - - _gl.texParameteri( textureType, 32882, 33071 ); - - } - - if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { - - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); - - } - - _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) ); - - if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { - - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); - - } - - } - - const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - - if ( extension ) { - - if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; - if ( texture.type === HalfFloatType && ( isWebGL2 || extensions.get( 'OES_texture_half_float_linear' ) ) === null ) return; - - if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { - - _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); - properties.get( texture ).__currentAnisotropy = texture.anisotropy; - - } - - } - - } - - function initTexture( textureProperties, texture ) { - - if ( textureProperties.__webglInit === undefined ) { - - textureProperties.__webglInit = true; - - texture.addEventListener( 'dispose', onTextureDispose ); - - textureProperties.__webglTexture = _gl.createTexture(); - - info.memory.textures ++; - - } - - } - - function uploadTexture( textureProperties, texture, slot ) { - - let textureType = 3553; - - if ( texture.isDataTexture2DArray ) textureType = 35866; - if ( texture.isDataTexture3D ) textureType = 32879; - - initTexture( textureProperties, texture ); - - state.activeTexture( 33984 + slot ); - state.bindTexture( textureType, textureProperties.__webglTexture ); - - _gl.pixelStorei( 37440, texture.flipY ); - _gl.pixelStorei( 37441, texture.premultiplyAlpha ); - _gl.pixelStorei( 3317, texture.unpackAlignment ); - - const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false; - const image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); - - const supportsMips = isPowerOfTwo( image ) || isWebGL2, - glFormat = utils.convert( texture.format ); - - let glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - - setTextureParameters( textureType, texture, supportsMips ); - - let mipmap; - const mipmaps = texture.mipmaps; - - if ( texture.isDepthTexture ) { - - // populate depth texture with dummy data - - glInternalFormat = 6402; - - if ( isWebGL2 ) { - - if ( texture.type === FloatType ) { - - glInternalFormat = 36012; - - } else if ( texture.type === UnsignedIntType ) { - - glInternalFormat = 33190; - - } else if ( texture.type === UnsignedInt248Type ) { - - glInternalFormat = 35056; - - } else { - - glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D - - } - - } else { - - if ( texture.type === FloatType ) { - - console.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' ); - - } - - } - - // validation checks for WebGL 1 - - if ( texture.format === DepthFormat && glInternalFormat === 6402 ) { - - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { - - console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); - - texture.type = UnsignedShortType; - glType = utils.convert( texture.type ); - - } - - } - - if ( texture.format === DepthStencilFormat && glInternalFormat === 6402 ) { - - // Depth stencil textures need the DEPTH_STENCIL internal format - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - glInternalFormat = 34041; - - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedInt248Type ) { - - console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); - - texture.type = UnsignedInt248Type; - glType = utils.convert( texture.type ); - - } - - } - - // - - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); - - } else if ( texture.isDataTexture ) { - - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels - - if ( mipmaps.length > 0 && supportsMips ) { - - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else { - - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; - - } - - } else if ( texture.isCompressedTexture ) { - - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { - - if ( glFormat !== null ) { - - state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - - } else { - - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); - - } - - } else { - - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - } - - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else if ( texture.isDataTexture2DArray ) { - - state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; - - } else if ( texture.isDataTexture3D ) { - - state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; - - } else { - - // regular Texture (image, video, canvas) - - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels - - if ( mipmaps.length > 0 && supportsMips ) { - - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); - - } - - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else { - - state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); - textureProperties.__maxMipLevel = 0; - - } - - } - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - generateMipmap( textureType, texture, image.width, image.height ); - - } - - textureProperties.__version = texture.version; - - if ( texture.onUpdate ) texture.onUpdate( texture ); - - } - - // Render targets - - // Setup storage for target texture and bind it to correct framebuffer - function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { - - const glFormat = utils.convert( renderTarget.texture.format ); - const glType = utils.convert( renderTarget.texture.type ); - const glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType ); - state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - _gl.bindFramebuffer( 36160, framebuffer ); - _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); - _gl.bindFramebuffer( 36160, null ); - - } - - // Setup storage for internal depth/stencil buffers and bind to correct framebuffer - function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { - - _gl.bindRenderbuffer( 36161, renderbuffer ); - - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - - let glInternalFormat = 33189; - - if ( isMultisample ) { - - const depthTexture = renderTarget.depthTexture; - - if ( depthTexture && depthTexture.isDepthTexture ) { - - if ( depthTexture.type === FloatType ) { - - glInternalFormat = 36012; - - } else if ( depthTexture.type === UnsignedIntType ) { - - glInternalFormat = 33190; - - } - - } - - const samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - - } else { - - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); - - } - - _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); - - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - - if ( isMultisample ) { - - const samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height ); - - } else { - - _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); - - } - - - _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); - - } else { - - const glFormat = utils.convert( renderTarget.texture.format ); - const glType = utils.convert( renderTarget.texture.type ); - const glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType ); - - if ( isMultisample ) { - - const samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - - } else { - - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); - - } - - } - - _gl.bindRenderbuffer( 36161, null ); - - } - - // Setup resources for a Depth Texture for a FBO (needs an extension) - function setupDepthTexture( framebuffer, renderTarget ) { - - const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); - if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); - - _gl.bindFramebuffer( 36160, framebuffer ); - - if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { - - throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); - - } - - // upload an empty depth texture with framebuffer size - if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || - renderTarget.depthTexture.image.width !== renderTarget.width || - renderTarget.depthTexture.image.height !== renderTarget.height ) { - - renderTarget.depthTexture.image.width = renderTarget.width; - renderTarget.depthTexture.image.height = renderTarget.height; - renderTarget.depthTexture.needsUpdate = true; - - } - - setTexture2D( renderTarget.depthTexture, 0 ); - - const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; - - if ( renderTarget.depthTexture.format === DepthFormat ) { - - _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); - - } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { - - _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); - - } else { - - throw new Error( 'Unknown depthTexture format' ); - - } - - } - - // Setup GL resources for a non-texture depth buffer - function setupDepthRenderbuffer( renderTarget ) { - - const renderTargetProperties = properties.get( renderTarget ); - - const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - - if ( renderTarget.depthTexture ) { - - if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); - - setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); - - } else { - - if ( isCube ) { - - renderTargetProperties.__webglDepthbuffer = []; - - for ( let i = 0; i < 6; i ++ ) { - - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); - renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); - - } - - } else { - - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); - renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); - - } - - } - - _gl.bindFramebuffer( 36160, null ); - - } - - // Set up GL resources for the render target - function setupRenderTarget( renderTarget ) { - - const renderTargetProperties = properties.get( renderTarget ); - const textureProperties = properties.get( renderTarget.texture ); - - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - - textureProperties.__webglTexture = _gl.createTexture(); - - info.memory.textures ++; - - const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - const isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true ); - const supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2; - - // Handles WebGL2 RGBFormat fallback - #18858 - - if ( isWebGL2 && renderTarget.texture.format === RGBFormat && ( renderTarget.texture.type === FloatType || renderTarget.texture.type === HalfFloatType ) ) { - - renderTarget.texture.format = RGBAFormat; - - console.warn( 'THREE.WebGLRenderer: Rendering to textures with RGB format is not supported. Using RGBA format instead.' ); - - } - - // Setup framebuffer - - if ( isCube ) { - - renderTargetProperties.__webglFramebuffer = []; - - for ( let i = 0; i < 6; i ++ ) { - - renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - - } - - } else { - - renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); - - if ( isMultisample ) { - - if ( isWebGL2 ) { - - renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); - renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); - - _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer ); - - const glFormat = utils.convert( renderTarget.texture.format ); - const glType = utils.convert( renderTarget.texture.type ); - const glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType ); - const samples = getRenderTargetSamples( renderTarget ); - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.framebufferRenderbuffer( 36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer ); - _gl.bindRenderbuffer( 36161, null ); - - if ( renderTarget.depthBuffer ) { - - renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); - - } - - _gl.bindFramebuffer( 36160, null ); - - - } else { - - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); - - } - - } - - } - - // Setup color buffer - - if ( isCube ) { - - state.bindTexture( 34067, textureProperties.__webglTexture ); - setTextureParameters( 34067, renderTarget.texture, supportsMips ); - - for ( let i = 0; i < 6; i ++ ) { - - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, 36064, 34069 + i ); - - } - - if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) { - - generateMipmap( 34067, renderTarget.texture, renderTarget.width, renderTarget.height ); - - } - - state.bindTexture( 34067, null ); - - } else { - - state.bindTexture( 3553, textureProperties.__webglTexture ); - setTextureParameters( 3553, renderTarget.texture, supportsMips ); - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, 36064, 3553 ); - - if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) { - - generateMipmap( 3553, renderTarget.texture, renderTarget.width, renderTarget.height ); - - } - - state.bindTexture( 3553, null ); - - } - - // Setup depth and stencil buffers - - if ( renderTarget.depthBuffer ) { - - setupDepthRenderbuffer( renderTarget ); - - } - - } - - function updateRenderTargetMipmap( renderTarget ) { - - const texture = renderTarget.texture; - const supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2; - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - const target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553; - const webglTexture = properties.get( texture ).__webglTexture; - - state.bindTexture( target, webglTexture ); - generateMipmap( target, texture, renderTarget.width, renderTarget.height ); - state.bindTexture( target, null ); - - } - - } - - function updateMultisampleRenderTarget( renderTarget ) { - - if ( renderTarget.isWebGLMultisampleRenderTarget ) { - - if ( isWebGL2 ) { - - const renderTargetProperties = properties.get( renderTarget ); - - _gl.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); - - const width = renderTarget.width; - const height = renderTarget.height; - let mask = 16384; - - if ( renderTarget.depthBuffer ) mask |= 256; - if ( renderTarget.stencilBuffer ) mask |= 1024; - - _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); - - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); // see #18905 - - } else { - - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); - - } - - } - - } - - function getRenderTargetSamples( renderTarget ) { - - return ( isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ? - Math.min( maxSamples, renderTarget.samples ) : 0; - - } - - function updateVideoTexture( texture ) { - - const frame = info.render.frame; - - // Check the last frame we updated the VideoTexture - - if ( _videoTextures.get( texture ) !== frame ) { - - _videoTextures.set( texture, frame ); - texture.update(); - - } - - } - - // backwards compatibility - - let warnedTexture2D = false; - let warnedTextureCube = false; - - function safeSetTexture2D( texture, slot ) { - - if ( texture && texture.isWebGLRenderTarget ) { - - if ( warnedTexture2D === false ) { - - console.warn( "THREE.WebGLTextures.safeSetTexture2D: don't use render targets as textures. Use their .texture property instead." ); - warnedTexture2D = true; - - } - - texture = texture.texture; - - } - - setTexture2D( texture, slot ); - - } - - function safeSetTextureCube( texture, slot ) { - - if ( texture && texture.isWebGLCubeRenderTarget ) { - - if ( warnedTextureCube === false ) { - - console.warn( "THREE.WebGLTextures.safeSetTextureCube: don't use cube render targets as textures. Use their .texture property instead." ); - warnedTextureCube = true; - - } - - texture = texture.texture; - - } - - // currently relying on the fact that WebGLCubeRenderTarget.texture is a Texture and NOT a CubeTexture - // TODO: unify these code paths - if ( ( texture && texture.isCubeTexture ) || - ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { - - // CompressedTexture can have Array in image :/ - - // this function alone should take care of cube textures - setTextureCube( texture, slot ); - - } else { - - // assumed: texture property of THREE.WebGLCubeRenderTarget - setTextureCubeDynamic( texture, slot ); - - } - - } - - // - - this.allocateTextureUnit = allocateTextureUnit; - this.resetTextureUnits = resetTextureUnits; - - this.setTexture2D = setTexture2D; - this.setTexture2DArray = setTexture2DArray; - this.setTexture3D = setTexture3D; - this.setTextureCube = setTextureCube; - this.setTextureCubeDynamic = setTextureCubeDynamic; - this.setupRenderTarget = setupRenderTarget; - this.updateRenderTargetMipmap = updateRenderTargetMipmap; - this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; - - this.safeSetTexture2D = safeSetTexture2D; - this.safeSetTextureCube = safeSetTextureCube; - - } - - function WebGLUtils( gl, extensions, capabilities ) { - - const isWebGL2 = capabilities.isWebGL2; - - function convert( p ) { - - let extension; - - if ( p === UnsignedByteType ) return 5121; - if ( p === UnsignedShort4444Type ) return 32819; - if ( p === UnsignedShort5551Type ) return 32820; - if ( p === UnsignedShort565Type ) return 33635; - - if ( p === ByteType ) return 5120; - if ( p === ShortType ) return 5122; - if ( p === UnsignedShortType ) return 5123; - if ( p === IntType ) return 5124; - if ( p === UnsignedIntType ) return 5125; - if ( p === FloatType ) return 5126; - - if ( p === HalfFloatType ) { - - if ( isWebGL2 ) return 5131; - - extension = extensions.get( 'OES_texture_half_float' ); - - if ( extension !== null ) { - - return extension.HALF_FLOAT_OES; - - } else { - - return null; - - } - - } - - if ( p === AlphaFormat ) return 6406; - if ( p === RGBFormat ) return 6407; - if ( p === RGBAFormat ) return 6408; - if ( p === LuminanceFormat ) return 6409; - if ( p === LuminanceAlphaFormat ) return 6410; - if ( p === DepthFormat ) return 6402; - if ( p === DepthStencilFormat ) return 34041; - if ( p === RedFormat ) return 6403; - - // WebGL2 formats. - - if ( p === RedIntegerFormat ) return 36244; - if ( p === RGFormat ) return 33319; - if ( p === RGIntegerFormat ) return 33320; - if ( p === RGBIntegerFormat ) return 36248; - if ( p === RGBAIntegerFormat ) return 36249; - - if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || - p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); - - if ( extension !== null ) { - - if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; - if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; - - } else { - - return null; - - } - - } - - if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || - p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - - if ( extension !== null ) { - - if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; - if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; - if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; - - } else { - - return null; - - } - - } - - if ( p === RGB_ETC1_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); - - if ( extension !== null ) { - - return extension.COMPRESSED_RGB_ETC1_WEBGL; - - } else { - - return null; - - } - - } - - if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_etc' ); - - if ( extension !== null ) { - - if ( p === RGB_ETC2_Format ) return extension.COMPRESSED_RGB8_ETC2; - if ( p === RGBA_ETC2_EAC_Format ) return extension.COMPRESSED_RGBA8_ETC2_EAC; - - } - - } - - if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || - p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || - p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || - p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || - p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format || - p === SRGB8_ALPHA8_ASTC_4x4_Format || p === SRGB8_ALPHA8_ASTC_5x4_Format || p === SRGB8_ALPHA8_ASTC_5x5_Format || - p === SRGB8_ALPHA8_ASTC_6x5_Format || p === SRGB8_ALPHA8_ASTC_6x6_Format || p === SRGB8_ALPHA8_ASTC_8x5_Format || - p === SRGB8_ALPHA8_ASTC_8x6_Format || p === SRGB8_ALPHA8_ASTC_8x8_Format || p === SRGB8_ALPHA8_ASTC_10x5_Format || - p === SRGB8_ALPHA8_ASTC_10x6_Format || p === SRGB8_ALPHA8_ASTC_10x8_Format || p === SRGB8_ALPHA8_ASTC_10x10_Format || - p === SRGB8_ALPHA8_ASTC_12x10_Format || p === SRGB8_ALPHA8_ASTC_12x12_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_astc' ); - - if ( extension !== null ) { - - // TODO Complete? - - return p; - - } else { - - return null; - - } - - } - - if ( p === RGBA_BPTC_Format ) { - - extension = extensions.get( 'EXT_texture_compression_bptc' ); - - if ( extension !== null ) { - - // TODO Complete? - - return p; - - } else { - - return null; - - } - - } - - if ( p === UnsignedInt248Type ) { - - if ( isWebGL2 ) return 34042; - - extension = extensions.get( 'WEBGL_depth_texture' ); - - if ( extension !== null ) { - - return extension.UNSIGNED_INT_24_8_WEBGL; - - } else { - - return null; - - } - - } - - } - - return { convert: convert }; - - } - - function ArrayCamera( array ) { - - PerspectiveCamera.call( this ); - - this.cameras = array || []; - - } - - ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), { - - constructor: ArrayCamera, - - isArrayCamera: true - - } ); - - function Group() { - - Object3D.call( this ); - - this.type = 'Group'; - - } - - Group.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Group, - - isGroup: true - - } ); - - function WebXRController() { - - this._targetRay = null; - this._grip = null; - this._hand = null; - - } - - Object.assign( WebXRController.prototype, { - - constructor: WebXRController, - - getHandSpace: function () { - - if ( this._hand === null ) { - - this._hand = new Group(); - this._hand.matrixAutoUpdate = false; - this._hand.visible = false; - - this._hand.joints = []; - this._hand.inputState = { pinching: false }; - - if ( window.XRHand ) { - - for ( let i = 0; i <= window.XRHand.LITTLE_PHALANX_TIP; i ++ ) { - - // The transform of this joint will be updated with the joint pose on each frame - const joint = new Group(); - joint.matrixAutoUpdate = false; - joint.visible = false; - this._hand.joints.push( joint ); - // ?? - this._hand.add( joint ); - - } - - } - - } - - return this._hand; - - }, - - getTargetRaySpace: function () { - - if ( this._targetRay === null ) { - - this._targetRay = new Group(); - this._targetRay.matrixAutoUpdate = false; - this._targetRay.visible = false; - - } - - return this._targetRay; - - }, - - getGripSpace: function () { - - if ( this._grip === null ) { - - this._grip = new Group(); - this._grip.matrixAutoUpdate = false; - this._grip.visible = false; - - } - - return this._grip; - - }, - - dispatchEvent: function ( event ) { - - if ( this._targetRay !== null ) { - - this._targetRay.dispatchEvent( event ); - - } - - if ( this._grip !== null ) { - - this._grip.dispatchEvent( event ); - - } - - if ( this._hand !== null ) { - - this._hand.dispatchEvent( event ); - - } - - return this; - - }, - - disconnect: function ( inputSource ) { - - this.dispatchEvent( { type: 'disconnected', data: inputSource } ); - - if ( this._targetRay !== null ) { - - this._targetRay.visible = false; - - } - - if ( this._grip !== null ) { - - this._grip.visible = false; - - } - - if ( this._hand !== null ) { - - this._hand.visible = false; - - } - - return this; - - }, - - update: function ( inputSource, frame, referenceSpace ) { - - let inputPose = null; - let gripPose = null; - let handPose = null; - - const targetRay = this._targetRay; - const grip = this._grip; - const hand = this._hand; - - if ( inputSource ) { - - if ( hand && inputSource.hand ) { - - handPose = true; - - for ( let i = 0; i <= window.XRHand.LITTLE_PHALANX_TIP; i ++ ) { - - if ( inputSource.hand[ i ] ) { - - // Update the joints groups with the XRJoint poses - const jointPose = frame.getJointPose( inputSource.hand[ i ], referenceSpace ); - const joint = hand.joints[ i ]; - - if ( jointPose !== null ) { - - joint.matrix.fromArray( jointPose.transform.matrix ); - joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); - joint.jointRadius = jointPose.radius; - - } - - joint.visible = jointPose !== null; - - // Custom events - - // Check pinch - const indexTip = hand.joints[ window.XRHand.INDEX_PHALANX_TIP ]; - const thumbTip = hand.joints[ window.XRHand.THUMB_PHALANX_TIP ]; - const distance = indexTip.position.distanceTo( thumbTip.position ); - - const distanceToPinch = 0.02; - const threshold = 0.005; - - if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { - - hand.inputState.pinching = false; - this.dispatchEvent( { - type: "pinchend", - handedness: inputSource.handedness, - target: this - } ); - - } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { - - hand.inputState.pinching = true; - this.dispatchEvent( { - type: "pinchstart", - handedness: inputSource.handedness, - target: this - } ); - - } - - } - - } - - } else { - - if ( targetRay !== null ) { - - inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); - - if ( inputPose !== null ) { - - targetRay.matrix.fromArray( inputPose.transform.matrix ); - targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); - - } - - } - - if ( grip !== null && inputSource.gripSpace ) { - - gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); - - if ( gripPose !== null ) { - - grip.matrix.fromArray( gripPose.transform.matrix ); - grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); - - } - - } - - } - - } - - if ( targetRay !== null ) { - - targetRay.visible = ( inputPose !== null ); - - } - - if ( grip !== null ) { - - grip.visible = ( gripPose !== null ); - - } - - if ( hand !== null ) { - - hand.visible = ( handPose !== null ); - - } - - return this; - - } - - } ); - - function WebXRManager( renderer, gl ) { - - const scope = this; - - let session = null; - - let framebufferScaleFactor = 1.0; - - let referenceSpace = null; - let referenceSpaceType = 'local-floor'; - - let pose = null; - - const controllers = []; - const inputSourcesMap = new Map(); - - // - - const cameraL = new PerspectiveCamera(); - cameraL.layers.enable( 1 ); - cameraL.viewport = new Vector4(); - - const cameraR = new PerspectiveCamera(); - cameraR.layers.enable( 2 ); - cameraR.viewport = new Vector4(); - - const cameras = [ cameraL, cameraR ]; - - const cameraVR = new ArrayCamera(); - cameraVR.layers.enable( 1 ); - cameraVR.layers.enable( 2 ); - - let _currentDepthNear = null; - let _currentDepthFar = null; - - // - - this.enabled = false; - - this.isPresenting = false; - - this.getController = function ( index ) { - - let controller = controllers[ index ]; - - if ( controller === undefined ) { - - controller = new WebXRController(); - controllers[ index ] = controller; - - } - - return controller.getTargetRaySpace(); - - }; - - this.getControllerGrip = function ( index ) { - - let controller = controllers[ index ]; - - if ( controller === undefined ) { - - controller = new WebXRController(); - controllers[ index ] = controller; - - } - - return controller.getGripSpace(); - - }; - - this.getHand = function ( index ) { - - let controller = controllers[ index ]; - - if ( controller === undefined ) { - - controller = new WebXRController(); - controllers[ index ] = controller; - - } - - return controller.getHandSpace(); - - }; - - // - - function onSessionEvent( event ) { - - const controller = inputSourcesMap.get( event.inputSource ); - - if ( controller ) { - - controller.dispatchEvent( { type: event.type } ); - - } - - } - - function onSessionEnd() { - - inputSourcesMap.forEach( function ( controller, inputSource ) { - - controller.disconnect( inputSource ); - - } ); - - inputSourcesMap.clear(); - - // - - renderer.setFramebuffer( null ); - renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830 - animation.stop(); - - scope.isPresenting = false; - - scope.dispatchEvent( { type: 'sessionend' } ); - - } - - function onRequestReferenceSpace( value ) { - - referenceSpace = value; - - animation.setContext( session ); - animation.start(); - - scope.isPresenting = true; - - scope.dispatchEvent( { type: 'sessionstart' } ); - - } - - this.setFramebufferScaleFactor = function ( value ) { - - framebufferScaleFactor = value; - - if ( scope.isPresenting === true ) { - - console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' ); - - } - - }; - - this.setReferenceSpaceType = function ( value ) { - - referenceSpaceType = value; - - if ( scope.isPresenting === true ) { - - console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' ); - - } - - }; - - this.getReferenceSpace = function () { - - return referenceSpace; - - }; - - this.getSession = function () { - - return session; - - }; - - this.setSession = function ( value ) { - - session = value; - - if ( session !== null ) { - - session.addEventListener( 'select', onSessionEvent ); - session.addEventListener( 'selectstart', onSessionEvent ); - session.addEventListener( 'selectend', onSessionEvent ); - session.addEventListener( 'squeeze', onSessionEvent ); - session.addEventListener( 'squeezestart', onSessionEvent ); - session.addEventListener( 'squeezeend', onSessionEvent ); - session.addEventListener( 'end', onSessionEnd ); - - const attributes = gl.getContextAttributes(); - - if ( attributes.xrCompatible !== true ) { - - gl.makeXRCompatible(); - - } - - const layerInit = { - antialias: attributes.antialias, - alpha: attributes.alpha, - depth: attributes.depth, - stencil: attributes.stencil, - framebufferScaleFactor: framebufferScaleFactor - }; - - // eslint-disable-next-line no-undef - const baseLayer = new XRWebGLLayer( session, gl, layerInit ); - - session.updateRenderState( { baseLayer: baseLayer } ); - - session.requestReferenceSpace( referenceSpaceType ).then( onRequestReferenceSpace ); - - // - - session.addEventListener( 'inputsourceschange', updateInputSources ); - - } - - }; - - function updateInputSources( event ) { - - const inputSources = session.inputSources; - - // Assign inputSources to available controllers - - for ( let i = 0; i < controllers.length; i ++ ) { - - inputSourcesMap.set( inputSources[ i ], controllers[ i ] ); - - } - - // Notify disconnected - - for ( let i = 0; i < event.removed.length; i ++ ) { - - const inputSource = event.removed[ i ]; - const controller = inputSourcesMap.get( inputSource ); - - if ( controller ) { - - controller.dispatchEvent( { type: 'disconnected', data: inputSource } ); - inputSourcesMap.delete( inputSource ); - - } - - } - - // Notify connected - - for ( let i = 0; i < event.added.length; i ++ ) { - - const inputSource = event.added[ i ]; - const controller = inputSourcesMap.get( inputSource ); - - if ( controller ) { - - controller.dispatchEvent( { type: 'connected', data: inputSource } ); - - } - - } - - } - - // - - const cameraLPos = new Vector3(); - const cameraRPos = new Vector3(); - - /** - * Assumes 2 cameras that are parallel and share an X-axis, and that - * the cameras' projection and world matrices have already been set. - * And that near and far planes are identical for both cameras. - * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 - */ - function setProjectionFromUnion( camera, cameraL, cameraR ) { - - cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); - cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); - - const ipd = cameraLPos.distanceTo( cameraRPos ); - - const projL = cameraL.projectionMatrix.elements; - const projR = cameraR.projectionMatrix.elements; - - // VR systems will have identical far and near planes, and - // most likely identical top and bottom frustum extents. - // Use the left camera for these values. - const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); - const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); - const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; - const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; - - const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; - const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; - const left = near * leftFov; - const right = near * rightFov; - - // Calculate the new camera's position offset from the - // left camera. xOffset should be roughly half `ipd`. - const zOffset = ipd / ( - leftFov + rightFov ); - const xOffset = zOffset * - leftFov; - - // TODO: Better way to apply this offset? - cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); - camera.translateX( xOffset ); - camera.translateZ( zOffset ); - camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); - - // Find the union of the frustum values of the cameras and scale - // the values so that the near plane's position does not change in world space, - // although must now be relative to the new union camera. - const near2 = near + zOffset; - const far2 = far + zOffset; - const left2 = left - xOffset; - const right2 = right + ( ipd - xOffset ); - const top2 = topFov * far / far2 * near2; - const bottom2 = bottomFov * far / far2 * near2; - - camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); - - } - - function updateCamera( camera, parent ) { - - if ( parent === null ) { - - camera.matrixWorld.copy( camera.matrix ); - - } else { - - camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); - - } - - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); - - } - - this.getCamera = function ( camera ) { - - cameraVR.near = cameraR.near = cameraL.near = camera.near; - cameraVR.far = cameraR.far = cameraL.far = camera.far; - - if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { - - // Note that the new renderState won't apply until the next frame. See #18320 - - session.updateRenderState( { - depthNear: cameraVR.near, - depthFar: cameraVR.far - } ); - - _currentDepthNear = cameraVR.near; - _currentDepthFar = cameraVR.far; - - } - - const parent = camera.parent; - const cameras = cameraVR.cameras; - - updateCamera( cameraVR, parent ); - - for ( let i = 0; i < cameras.length; i ++ ) { - - updateCamera( cameras[ i ], parent ); - - } - - // update camera and its children - - camera.matrixWorld.copy( cameraVR.matrixWorld ); - - const children = camera.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateMatrixWorld( true ); - - } - - // update projection matrix for proper view frustum culling - - if ( cameras.length === 2 ) { - - setProjectionFromUnion( cameraVR, cameraL, cameraR ); - - } else { - - // assume single camera setup (AR) - - cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); - - } - - return cameraVR; - - }; - - // Animation Loop - - let onAnimationFrameCallback = null; - - function onAnimationFrame( time, frame ) { - - pose = frame.getViewerPose( referenceSpace ); - - if ( pose !== null ) { - - const views = pose.views; - const baseLayer = session.renderState.baseLayer; - - renderer.setFramebuffer( baseLayer.framebuffer ); - - let cameraVRNeedsUpdate = false; - - // check if it's necessary to rebuild cameraVR's camera list - - if ( views.length !== cameraVR.cameras.length ) { - - cameraVR.cameras.length = 0; - cameraVRNeedsUpdate = true; - - } - - for ( let i = 0; i < views.length; i ++ ) { - - const view = views[ i ]; - const viewport = baseLayer.getViewport( view ); - - const camera = cameras[ i ]; - camera.matrix.fromArray( view.transform.matrix ); - camera.projectionMatrix.fromArray( view.projectionMatrix ); - camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); - - if ( i === 0 ) { - - cameraVR.matrix.copy( camera.matrix ); - - } - - if ( cameraVRNeedsUpdate === true ) { - - cameraVR.cameras.push( camera ); - - } - - } - - } - - // - - const inputSources = session.inputSources; - - for ( let i = 0; i < controllers.length; i ++ ) { - - const controller = controllers[ i ]; - const inputSource = inputSources[ i ]; - - controller.update( inputSource, frame, referenceSpace ); - - } - - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); - - } - - const animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); - - this.setAnimationLoop = function ( callback ) { - - onAnimationFrameCallback = callback; - - }; - - this.dispose = function () {}; - - } - - Object.assign( WebXRManager.prototype, EventDispatcher.prototype ); - - function WebGLMaterials( properties ) { - - function refreshFogUniforms( uniforms, fog ) { - - uniforms.fogColor.value.copy( fog.color ); - - if ( fog.isFog ) { - - uniforms.fogNear.value = fog.near; - uniforms.fogFar.value = fog.far; - - } else if ( fog.isFogExp2 ) { - - uniforms.fogDensity.value = fog.density; - - } - - } - - function refreshMaterialUniforms( uniforms, material, pixelRatio, height ) { - - if ( material.isMeshBasicMaterial ) { - - refreshUniformsCommon( uniforms, material ); - - } else if ( material.isMeshLambertMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsLambert( uniforms, material ); - - } else if ( material.isMeshToonMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsToon( uniforms, material ); - - } else if ( material.isMeshPhongMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsPhong( uniforms, material ); - - } else if ( material.isMeshStandardMaterial ) { - - refreshUniformsCommon( uniforms, material ); - - if ( material.isMeshPhysicalMaterial ) { - - refreshUniformsPhysical( uniforms, material ); - - } else { - - refreshUniformsStandard( uniforms, material ); - - } - - } else if ( material.isMeshMatcapMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsMatcap( uniforms, material ); - - } else if ( material.isMeshDepthMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsDepth( uniforms, material ); - - } else if ( material.isMeshDistanceMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsDistance( uniforms, material ); - - } else if ( material.isMeshNormalMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsNormal( uniforms, material ); - - } else if ( material.isLineBasicMaterial ) { - - refreshUniformsLine( uniforms, material ); - - if ( material.isLineDashedMaterial ) { - - refreshUniformsDash( uniforms, material ); - - } - - } else if ( material.isPointsMaterial ) { - - refreshUniformsPoints( uniforms, material, pixelRatio, height ); - - } else if ( material.isSpriteMaterial ) { - - refreshUniformsSprites( uniforms, material ); - - } else if ( material.isShadowMaterial ) { - - uniforms.color.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - - } else if ( material.isShaderMaterial ) { - - material.uniformsNeedUpdate = false; // #15581 - - } - - } - - function refreshUniformsCommon( uniforms, material ) { - - uniforms.opacity.value = material.opacity; - - if ( material.color ) { - - uniforms.diffuse.value.copy( material.color ); - - } - - if ( material.emissive ) { - - uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); - - } - - if ( material.map ) { - - uniforms.map.value = material.map; - - } - - if ( material.alphaMap ) { - - uniforms.alphaMap.value = material.alphaMap; - - } - - if ( material.specularMap ) { - - uniforms.specularMap.value = material.specularMap; - - } - - const envMap = properties.get( material ).envMap; - - if ( envMap ) { - - uniforms.envMap.value = envMap; - - uniforms.flipEnvMap.value = envMap.isCubeTexture ? - 1 : 1; - - uniforms.reflectivity.value = material.reflectivity; - uniforms.refractionRatio.value = material.refractionRatio; - - const maxMipLevel = properties.get( envMap ).__maxMipLevel; - - if ( maxMipLevel !== undefined ) { - - uniforms.maxMipLevel.value = maxMipLevel; - - } - - } - - if ( material.lightMap ) { - - uniforms.lightMap.value = material.lightMap; - uniforms.lightMapIntensity.value = material.lightMapIntensity; - - } - - if ( material.aoMap ) { - - uniforms.aoMap.value = material.aoMap; - uniforms.aoMapIntensity.value = material.aoMapIntensity; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. displacementMap map - // 4. normal map - // 5. bump map - // 6. roughnessMap map - // 7. metalnessMap map - // 8. alphaMap map - // 9. emissiveMap map - // 10. clearcoat map - // 11. clearcoat normal map - // 12. clearcoat roughnessMap map - - let uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.specularMap ) { - - uvScaleMap = material.specularMap; - - } else if ( material.displacementMap ) { - - uvScaleMap = material.displacementMap; - - } else if ( material.normalMap ) { - - uvScaleMap = material.normalMap; - - } else if ( material.bumpMap ) { - - uvScaleMap = material.bumpMap; - - } else if ( material.roughnessMap ) { - - uvScaleMap = material.roughnessMap; - - } else if ( material.metalnessMap ) { - - uvScaleMap = material.metalnessMap; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } else if ( material.emissiveMap ) { - - uvScaleMap = material.emissiveMap; - - } else if ( material.clearcoatMap ) { - - uvScaleMap = material.clearcoatMap; - - } else if ( material.clearcoatNormalMap ) { - - uvScaleMap = material.clearcoatNormalMap; - - } else if ( material.clearcoatRoughnessMap ) { - - uvScaleMap = material.clearcoatRoughnessMap; - - } - - if ( uvScaleMap !== undefined ) { - - // backwards compatibility - if ( uvScaleMap.isWebGLRenderTarget ) { - - uvScaleMap = uvScaleMap.texture; - - } - - if ( uvScaleMap.matrixAutoUpdate === true ) { - - uvScaleMap.updateMatrix(); - - } - - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - - } - - // uv repeat and offset setting priorities for uv2 - // 1. ao map - // 2. light map - - let uv2ScaleMap; - - if ( material.aoMap ) { - - uv2ScaleMap = material.aoMap; - - } else if ( material.lightMap ) { - - uv2ScaleMap = material.lightMap; - - } - - if ( uv2ScaleMap !== undefined ) { - - // backwards compatibility - if ( uv2ScaleMap.isWebGLRenderTarget ) { - - uv2ScaleMap = uv2ScaleMap.texture; - - } - - if ( uv2ScaleMap.matrixAutoUpdate === true ) { - - uv2ScaleMap.updateMatrix(); - - } - - uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix ); - - } - - } - - function refreshUniformsLine( uniforms, material ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - - } - - function refreshUniformsDash( uniforms, material ) { - - uniforms.dashSize.value = material.dashSize; - uniforms.totalSize.value = material.dashSize + material.gapSize; - uniforms.scale.value = material.scale; - - } - - function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.size.value = material.size * pixelRatio; - uniforms.scale.value = height * 0.5; - - if ( material.map ) { - - uniforms.map.value = material.map; - - } - - if ( material.alphaMap ) { - - uniforms.alphaMap.value = material.alphaMap; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map - - let uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } - - if ( uvScaleMap !== undefined ) { - - if ( uvScaleMap.matrixAutoUpdate === true ) { - - uvScaleMap.updateMatrix(); - - } - - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - - } - - } - - function refreshUniformsSprites( uniforms, material ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.rotation.value = material.rotation; - - if ( material.map ) { - - uniforms.map.value = material.map; - - } - - if ( material.alphaMap ) { - - uniforms.alphaMap.value = material.alphaMap; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map - - let uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } - - if ( uvScaleMap !== undefined ) { - - if ( uvScaleMap.matrixAutoUpdate === true ) { - - uvScaleMap.updateMatrix(); - - } - - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - - } - - } - - function refreshUniformsLambert( uniforms, material ) { - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - } - - function refreshUniformsPhong( uniforms, material ) { - - uniforms.specular.value.copy( material.specular ); - uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsToon( uniforms, material ) { - - if ( material.gradientMap ) { - - uniforms.gradientMap.value = material.gradientMap; - - } - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsStandard( uniforms, material ) { - - uniforms.roughness.value = material.roughness; - uniforms.metalness.value = material.metalness; - - if ( material.roughnessMap ) { - - uniforms.roughnessMap.value = material.roughnessMap; - - } - - if ( material.metalnessMap ) { - - uniforms.metalnessMap.value = material.metalnessMap; - - } - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - const envMap = properties.get( material ).envMap; - - if ( envMap ) { - - //uniforms.envMap.value = material.envMap; // part of uniforms common - uniforms.envMapIntensity.value = material.envMapIntensity; - - } - - } - - function refreshUniformsPhysical( uniforms, material ) { - - refreshUniformsStandard( uniforms, material ); - - uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common - - uniforms.clearcoat.value = material.clearcoat; - uniforms.clearcoatRoughness.value = material.clearcoatRoughness; - if ( material.sheen ) uniforms.sheen.value.copy( material.sheen ); - - if ( material.clearcoatMap ) { - - uniforms.clearcoatMap.value = material.clearcoatMap; - - } - - if ( material.clearcoatRoughnessMap ) { - - uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; - - } - - if ( material.clearcoatNormalMap ) { - - uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); - uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; - - if ( material.side === BackSide ) { - - uniforms.clearcoatNormalScale.value.negate(); - - } - - } - - uniforms.transmission.value = material.transmission; - - if ( material.transmissionMap ) { - - uniforms.transmissionMap.value = material.transmissionMap; - - } - - } - - function refreshUniformsMatcap( uniforms, material ) { - - if ( material.matcap ) { - - uniforms.matcap.value = material.matcap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsDepth( uniforms, material ) { - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsDistance( uniforms, material ) { - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - uniforms.referencePosition.value.copy( material.referencePosition ); - uniforms.nearDistance.value = material.nearDistance; - uniforms.farDistance.value = material.farDistance; - - } - - function refreshUniformsNormal( uniforms, material ) { - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - return { - refreshFogUniforms: refreshFogUniforms, - refreshMaterialUniforms: refreshMaterialUniforms - }; - - } - - function WebGLRenderer( parameters ) { - - parameters = parameters || {}; - - const _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ), - _context = parameters.context !== undefined ? parameters.context : null, - - _alpha = parameters.alpha !== undefined ? parameters.alpha : false, - _depth = parameters.depth !== undefined ? parameters.depth : true, - _stencil = parameters.stencil !== undefined ? parameters.stencil : true, - _antialias = parameters.antialias !== undefined ? parameters.antialias : false, - _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, - _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, - _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default', - _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; - - let currentRenderList = null; - let currentRenderState = null; - - // public properties - - this.domElement = _canvas; - - // Debug configuration container - this.debug = { - - /** - * Enables error checking and reporting when shader programs are being compiled - * @type {boolean} - */ - checkShaderErrors: true - }; - - // clearing - - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; - - // scene graph - - this.sortObjects = true; - - // user-defined clipping - - this.clippingPlanes = []; - this.localClippingEnabled = false; - - // physically based shading - - this.gammaFactor = 2.0; // for backwards compatibility - this.outputEncoding = LinearEncoding; - - // physical lights - - this.physicallyCorrectLights = false; - - // tone mapping - - this.toneMapping = NoToneMapping; - this.toneMappingExposure = 1.0; - - // morphs - - this.maxMorphTargets = 8; - this.maxMorphNormals = 4; - - // internal properties - - const _this = this; - - let _isContextLost = false; - - // internal state cache - - let _framebuffer = null; - - let _currentActiveCubeFace = 0; - let _currentActiveMipmapLevel = 0; - let _currentRenderTarget = null; - let _currentFramebuffer = null; - let _currentMaterialId = - 1; - - let _currentCamera = null; - let _currentArrayCamera = null; - - const _currentViewport = new Vector4(); - const _currentScissor = new Vector4(); - let _currentScissorTest = null; - - // - - let _width = _canvas.width; - let _height = _canvas.height; - - let _pixelRatio = 1; - let _opaqueSort = null; - let _transparentSort = null; - - const _viewport = new Vector4( 0, 0, _width, _height ); - const _scissor = new Vector4( 0, 0, _width, _height ); - let _scissorTest = false; - - // frustum - - const _frustum = new Frustum(); - - // clipping - - let _clippingEnabled = false; - let _localClippingEnabled = false; - - // camera matrices cache - - const _projScreenMatrix = new Matrix4(); - - const _vector3 = new Vector3(); - - const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; - - function getTargetPixelRatio() { - - return _currentRenderTarget === null ? _pixelRatio : 1; - - } - - // initialize - - let _gl = _context; - - function getContext( contextNames, contextAttributes ) { - - for ( let i = 0; i < contextNames.length; i ++ ) { - - const contextName = contextNames[ i ]; - const context = _canvas.getContext( contextName, contextAttributes ); - if ( context !== null ) return context; - - } - - return null; - - } - - try { - - const contextAttributes = { - alpha: _alpha, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer, - powerPreference: _powerPreference, - failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat - }; - - // event listeners must be registered before WebGL context is created, see #12753 - - _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); - - if ( _gl === null ) { - - const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ]; - - if ( _this.isWebGL1Renderer === true ) { - - contextNames.shift(); - - } - - _gl = getContext( contextNames, contextAttributes ); - - if ( _gl === null ) { - - if ( getContext( contextNames ) ) { - - throw new Error( 'Error creating WebGL context with your selected attributes.' ); - - } else { - - throw new Error( 'Error creating WebGL context.' ); - - } - - } - - } - - // Some experimental-webgl implementations do not have getShaderPrecisionFormat - - if ( _gl.getShaderPrecisionFormat === undefined ) { - - _gl.getShaderPrecisionFormat = function () { - - return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; - - }; - - } - - } catch ( error ) { - - console.error( 'THREE.WebGLRenderer: ' + error.message ); - throw error; - - } - - let extensions, capabilities, state, info; - let properties, textures, cubemaps, attributes, geometries, objects; - let programCache, materials, renderLists, renderStates, clipping; - - let background, morphtargets, bufferRenderer, indexedBufferRenderer; - - let utils, bindingStates; - - function initGLContext() { - - extensions = new WebGLExtensions( _gl ); - - capabilities = new WebGLCapabilities( _gl, extensions, parameters ); - - if ( capabilities.isWebGL2 === false ) { - - extensions.get( 'WEBGL_depth_texture' ); - extensions.get( 'OES_texture_float' ); - extensions.get( 'OES_texture_half_float' ); - extensions.get( 'OES_texture_half_float_linear' ); - extensions.get( 'OES_standard_derivatives' ); - extensions.get( 'OES_element_index_uint' ); - extensions.get( 'OES_vertex_array_object' ); - extensions.get( 'ANGLE_instanced_arrays' ); - - } - - extensions.get( 'OES_texture_float_linear' ); - - utils = new WebGLUtils( _gl, extensions, capabilities ); - - state = new WebGLState( _gl, extensions, capabilities ); - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); - - info = new WebGLInfo( _gl ); - properties = new WebGLProperties(); - textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); - cubemaps = new WebGLCubeMaps( _this ); - attributes = new WebGLAttributes( _gl, capabilities ); - bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); - geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); - objects = new WebGLObjects( _gl, geometries, attributes, info ); - morphtargets = new WebGLMorphtargets( _gl ); - clipping = new WebGLClipping( properties ); - programCache = new WebGLPrograms( _this, cubemaps, extensions, capabilities, bindingStates, clipping ); - materials = new WebGLMaterials( properties ); - renderLists = new WebGLRenderLists( properties ); - renderStates = new WebGLRenderStates(); - background = new WebGLBackground( _this, cubemaps, state, objects, _premultipliedAlpha ); - - bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); - indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); - - info.programs = programCache.programs; - - _this.capabilities = capabilities; - _this.extensions = extensions; - _this.properties = properties; - _this.renderLists = renderLists; - _this.state = state; - _this.info = info; - - } - - initGLContext(); - - // xr - - const xr = new WebXRManager( _this, _gl ); - - this.xr = xr; - - // shadow map - - const shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize ); - - this.shadowMap = shadowMap; - - // API - - this.getContext = function () { - - return _gl; - - }; - - this.getContextAttributes = function () { - - return _gl.getContextAttributes(); - - }; - - this.forceContextLoss = function () { - - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.loseContext(); - - }; - - this.forceContextRestore = function () { - - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.restoreContext(); - - }; - - this.getPixelRatio = function () { - - return _pixelRatio; - - }; - - this.setPixelRatio = function ( value ) { - - if ( value === undefined ) return; - - _pixelRatio = value; - - this.setSize( _width, _height, false ); - - }; - - this.getSize = function ( target ) { - - if ( target === undefined ) { - - console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' ); - - target = new Vector2(); - - } - - return target.set( _width, _height ); - - }; - - this.setSize = function ( width, height, updateStyle ) { - - if ( xr.isPresenting ) { - - console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); - return; - - } - - _width = width; - _height = height; - - _canvas.width = Math.floor( width * _pixelRatio ); - _canvas.height = Math.floor( height * _pixelRatio ); - - if ( updateStyle !== false ) { - - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; - - } - - this.setViewport( 0, 0, width, height ); - - }; - - this.getDrawingBufferSize = function ( target ) { - - if ( target === undefined ) { - - console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' ); - - target = new Vector2(); - - } - - return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); - - }; - - this.setDrawingBufferSize = function ( width, height, pixelRatio ) { - - _width = width; - _height = height; - - _pixelRatio = pixelRatio; - - _canvas.width = Math.floor( width * pixelRatio ); - _canvas.height = Math.floor( height * pixelRatio ); - - this.setViewport( 0, 0, width, height ); - - }; - - this.getCurrentViewport = function ( target ) { - - if ( target === undefined ) { - - console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' ); - - target = new Vector4(); - - } - - return target.copy( _currentViewport ); - - }; - - this.getViewport = function ( target ) { - - return target.copy( _viewport ); - - }; - - this.setViewport = function ( x, y, width, height ) { - - if ( x.isVector4 ) { - - _viewport.set( x.x, x.y, x.z, x.w ); - - } else { - - _viewport.set( x, y, width, height ); - - } - - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); - - }; - - this.getScissor = function ( target ) { - - return target.copy( _scissor ); - - }; - - this.setScissor = function ( x, y, width, height ) { - - if ( x.isVector4 ) { - - _scissor.set( x.x, x.y, x.z, x.w ); - - } else { - - _scissor.set( x, y, width, height ); - - } - - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - - }; - - this.getScissorTest = function () { - - return _scissorTest; - - }; - - this.setScissorTest = function ( boolean ) { - - state.setScissorTest( _scissorTest = boolean ); - - }; - - this.setOpaqueSort = function ( method ) { - - _opaqueSort = method; - - }; - - this.setTransparentSort = function ( method ) { - - _transparentSort = method; - - }; - - // Clearing - - this.getClearColor = function () { - - return background.getClearColor(); - - }; - - this.setClearColor = function () { - - background.setClearColor.apply( background, arguments ); - - }; - - this.getClearAlpha = function () { - - return background.getClearAlpha(); - - }; - - this.setClearAlpha = function () { - - background.setClearAlpha.apply( background, arguments ); - - }; - - this.clear = function ( color, depth, stencil ) { - - let bits = 0; - - if ( color === undefined || color ) bits |= 16384; - if ( depth === undefined || depth ) bits |= 256; - if ( stencil === undefined || stencil ) bits |= 1024; - - _gl.clear( bits ); - - }; - - this.clearColor = function () { - - this.clear( true, false, false ); - - }; - - this.clearDepth = function () { - - this.clear( false, true, false ); - - }; - - this.clearStencil = function () { - - this.clear( false, false, true ); - - }; - - // - - this.dispose = function () { - - _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); - - renderLists.dispose(); - renderStates.dispose(); - properties.dispose(); - cubemaps.dispose(); - objects.dispose(); - bindingStates.dispose(); - - xr.dispose(); - - animation.stop(); - - }; - - // Events - - function onContextLost( event ) { - - event.preventDefault(); - - console.log( 'THREE.WebGLRenderer: Context Lost.' ); - - _isContextLost = true; - - } - - function onContextRestore( /* event */ ) { - - console.log( 'THREE.WebGLRenderer: Context Restored.' ); - - _isContextLost = false; - - initGLContext(); - - } - - function onMaterialDispose( event ) { - - const material = event.target; - - material.removeEventListener( 'dispose', onMaterialDispose ); - - deallocateMaterial( material ); - - } - - // Buffer deallocation - - function deallocateMaterial( material ) { - - releaseMaterialProgramReference( material ); - - properties.remove( material ); - - } - - - function releaseMaterialProgramReference( material ) { - - const programInfo = properties.get( material ).program; - - if ( programInfo !== undefined ) { - - programCache.releaseProgram( programInfo ); - - } - - } - - // Buffer rendering - - function renderObjectImmediate( object, program ) { - - object.render( function ( object ) { - - _this.renderBufferImmediate( object, program ); - - } ); - - } - - this.renderBufferImmediate = function ( object, program ) { - - bindingStates.initAttributes(); - - const buffers = properties.get( object ); - - if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); - if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); - if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); - if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); - - const programAttributes = program.getAttributes(); - - if ( object.hasPositions ) { - - _gl.bindBuffer( 34962, buffers.position ); - _gl.bufferData( 34962, object.positionArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.position ); - _gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 ); - - } - - if ( object.hasNormals ) { - - _gl.bindBuffer( 34962, buffers.normal ); - _gl.bufferData( 34962, object.normalArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.normal ); - _gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 ); - - } - - if ( object.hasUvs ) { - - _gl.bindBuffer( 34962, buffers.uv ); - _gl.bufferData( 34962, object.uvArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.uv ); - _gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 ); - - } - - if ( object.hasColors ) { - - _gl.bindBuffer( 34962, buffers.color ); - _gl.bufferData( 34962, object.colorArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.color ); - _gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 ); - - } - - bindingStates.disableUnusedAttributes(); - - _gl.drawArrays( 4, 0, object.count ); - - object.count = 0; - - }; - - this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { - - if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) - - const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); - - const program = setProgram( camera, scene, material, object ); - - state.setMaterial( material, frontFaceCW ); - - // - - let index = geometry.index; - const position = geometry.attributes.position; - - // - - if ( index === null ) { - - if ( position === undefined || position.count === 0 ) return; - - } else if ( index.count === 0 ) { - - return; - - } - - // - - let rangeFactor = 1; - - if ( material.wireframe === true ) { - - index = geometries.getWireframeAttribute( geometry ); - rangeFactor = 2; - - } - - if ( material.morphTargets || material.morphNormals ) { - - morphtargets.update( object, geometry, material, program ); - - } - - bindingStates.setup( object, material, program, geometry, index ); - - let attribute; - let renderer = bufferRenderer; - - if ( index !== null ) { - - attribute = attributes.get( index ); - - renderer = indexedBufferRenderer; - renderer.setIndex( attribute ); - - } - - // - - const dataCount = ( index !== null ) ? index.count : position.count; - - const rangeStart = geometry.drawRange.start * rangeFactor; - const rangeCount = geometry.drawRange.count * rangeFactor; - - const groupStart = group !== null ? group.start * rangeFactor : 0; - const groupCount = group !== null ? group.count * rangeFactor : Infinity; - - const drawStart = Math.max( rangeStart, groupStart ); - const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; - - const drawCount = Math.max( 0, drawEnd - drawStart + 1 ); - - if ( drawCount === 0 ) return; - - // - - if ( object.isMesh ) { - - if ( material.wireframe === true ) { - - state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); - renderer.setMode( 1 ); - - } else { - - renderer.setMode( 4 ); - - } - - } else if ( object.isLine ) { - - let lineWidth = material.linewidth; - - if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material - - state.setLineWidth( lineWidth * getTargetPixelRatio() ); - - if ( object.isLineSegments ) { - - renderer.setMode( 1 ); - - } else if ( object.isLineLoop ) { - - renderer.setMode( 2 ); - - } else { - - renderer.setMode( 3 ); - - } - - } else if ( object.isPoints ) { - - renderer.setMode( 0 ); - - } else if ( object.isSprite ) { - - renderer.setMode( 4 ); - - } - - if ( object.isInstancedMesh ) { - - renderer.renderInstances( drawStart, drawCount, object.count ); - - } else if ( geometry.isInstancedBufferGeometry ) { - - const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount ); - - renderer.renderInstances( drawStart, drawCount, instanceCount ); - - } else { - - renderer.render( drawStart, drawCount ); - - } - - }; - - // Compile - - this.compile = function ( scene, camera ) { - - currentRenderState = renderStates.get( scene, camera ); - currentRenderState.init(); - - scene.traverse( function ( object ) { - - if ( object.isLight ) { - - currentRenderState.pushLight( object ); - - if ( object.castShadow ) { - - currentRenderState.pushShadow( object ); - - } - - } - - } ); - - currentRenderState.setupLights( camera ); - - const compiled = new WeakMap(); - - scene.traverse( function ( object ) { - - const material = object.material; - - if ( material ) { - - if ( Array.isArray( material ) ) { - - for ( let i = 0; i < material.length; i ++ ) { - - const material2 = material[ i ]; - - if ( compiled.has( material2 ) === false ) { - - initMaterial( material2, scene, object ); - compiled.set( material2 ); - - } - - } - - } else if ( compiled.has( material ) === false ) { - - initMaterial( material, scene, object ); - compiled.set( material ); - - } - - } - - } ); - - }; - - // Animation Loop - - let onAnimationFrameCallback = null; - - function onAnimationFrame( time ) { - - if ( xr.isPresenting ) return; - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); - - } - - const animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); - - if ( typeof window !== 'undefined' ) animation.setContext( window ); - - this.setAnimationLoop = function ( callback ) { - - onAnimationFrameCallback = callback; - xr.setAnimationLoop( callback ); - - ( callback === null ) ? animation.stop() : animation.start(); - - }; - - // Rendering - - this.render = function ( scene, camera ) { - - let renderTarget, forceClear; - - if ( arguments[ 2 ] !== undefined ) { - - console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' ); - renderTarget = arguments[ 2 ]; - - } - - if ( arguments[ 3 ] !== undefined ) { - - console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' ); - forceClear = arguments[ 3 ]; - - } - - if ( camera !== undefined && camera.isCamera !== true ) { - - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; - - } - - if ( _isContextLost === true ) return; - - // reset caching for this frame - - bindingStates.resetDefaultState(); - _currentMaterialId = - 1; - _currentCamera = null; - - // update scene graph - - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - - // update camera matrices and frustum - - if ( camera.parent === null ) camera.updateMatrixWorld(); - - if ( xr.enabled === true && xr.isPresenting === true ) { - - camera = xr.getCamera( camera ); - - } - - // - if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget ); - - currentRenderState = renderStates.get( scene, camera ); - currentRenderState.init(); - - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromProjectionMatrix( _projScreenMatrix ); - - _localClippingEnabled = this.localClippingEnabled; - _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); - - currentRenderList = renderLists.get( scene, camera ); - currentRenderList.init(); - - projectObject( scene, camera, 0, _this.sortObjects ); - - currentRenderList.finish(); - - if ( _this.sortObjects === true ) { - - currentRenderList.sort( _opaqueSort, _transparentSort ); - - } - - // - - if ( _clippingEnabled === true ) clipping.beginShadows(); - - const shadowsArray = currentRenderState.state.shadowsArray; - - shadowMap.render( shadowsArray, scene, camera ); - - currentRenderState.setupLights( camera ); - - if ( _clippingEnabled === true ) clipping.endShadows(); - - // - - if ( this.info.autoReset === true ) this.info.reset(); - - if ( renderTarget !== undefined ) { - - this.setRenderTarget( renderTarget ); - - } - - // - - background.render( currentRenderList, scene, camera, forceClear ); - - // render scene - - const opaqueObjects = currentRenderList.opaque; - const transparentObjects = currentRenderList.transparent; - - if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); - if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); - - // - - if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); - - // - - if ( _currentRenderTarget !== null ) { - - // Generate mipmap if we're using any kind of mipmap filtering - - textures.updateRenderTargetMipmap( _currentRenderTarget ); - - // resolve multisample renderbuffers to a single-sample texture if necessary - - textures.updateMultisampleRenderTarget( _currentRenderTarget ); - - } - - // Ensure depth buffer writing is enabled so it can be cleared on next render - - state.buffers.depth.setTest( true ); - state.buffers.depth.setMask( true ); - state.buffers.color.setMask( true ); - - state.setPolygonOffset( false ); - - // _gl.finish(); - - currentRenderList = null; - currentRenderState = null; - - }; - - function projectObject( object, camera, groupOrder, sortObjects ) { - - if ( object.visible === false ) return; - - const visible = object.layers.test( camera.layers ); - - if ( visible ) { - - if ( object.isGroup ) { - - groupOrder = object.renderOrder; - - } else if ( object.isLOD ) { - - if ( object.autoUpdate === true ) object.update( camera ); - - } else if ( object.isLight ) { - - currentRenderState.pushLight( object ); - - if ( object.castShadow ) { - - currentRenderState.pushShadow( object ); - - } - - } else if ( object.isSprite ) { - - if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { - - if ( sortObjects ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - const geometry = objects.update( object ); - const material = object.material; - - if ( material.visible ) { - - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - - } - - } - - } else if ( object.isImmediateRenderObject ) { - - if ( sortObjects ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null ); - - } else if ( object.isMesh || object.isLine || object.isPoints ) { - - if ( object.isSkinnedMesh ) { - - // update skeleton only once in a frame - - if ( object.skeleton.frame !== info.render.frame ) { - - object.skeleton.update(); - object.skeleton.frame = info.render.frame; - - } - - } - - if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { - - if ( sortObjects ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - const geometry = objects.update( object ); - const material = object.material; - - if ( Array.isArray( material ) ) { - - const groups = geometry.groups; - - for ( let i = 0, l = groups.length; i < l; i ++ ) { - - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; - - if ( groupMaterial && groupMaterial.visible ) { - - currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); - - } - - } - - } else if ( material.visible ) { - - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - - } - - } - - } - - } - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - projectObject( children[ i ], camera, groupOrder, sortObjects ); - - } - - } - - function renderObjects( renderList, scene, camera ) { - - const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; - - for ( let i = 0, l = renderList.length; i < l; i ++ ) { - - const renderItem = renderList[ i ]; - - const object = renderItem.object; - const geometry = renderItem.geometry; - const material = overrideMaterial === null ? renderItem.material : overrideMaterial; - const group = renderItem.group; - - if ( camera.isArrayCamera ) { - - _currentArrayCamera = camera; - - const cameras = camera.cameras; - - for ( let j = 0, jl = cameras.length; j < jl; j ++ ) { - - const camera2 = cameras[ j ]; - - if ( object.layers.test( camera2.layers ) ) { - - state.viewport( _currentViewport.copy( camera2.viewport ) ); - - currentRenderState.setupLights( camera2 ); - - renderObject( object, scene, camera2, geometry, material, group ); - - } - - } - - } else { - - _currentArrayCamera = null; - - renderObject( object, scene, camera, geometry, material, group ); - - } - - } - - } - - function renderObject( object, scene, camera, geometry, material, group ) { - - object.onBeforeRender( _this, scene, camera, geometry, material, group ); - currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); - - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - - if ( object.isImmediateRenderObject ) { - - const program = setProgram( camera, scene, material, object ); - - state.setMaterial( material ); - - bindingStates.reset(); - - renderObjectImmediate( object, program ); - - } else { - - _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - - } - - object.onAfterRender( _this, scene, camera, geometry, material, group ); - currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); - - } - - function initMaterial( material, scene, object ) { - - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - - const materialProperties = properties.get( material ); - - const lights = currentRenderState.state.lights; - const shadowsArray = currentRenderState.state.shadowsArray; - - const lightsStateVersion = lights.state.version; - - const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); - const programCacheKey = programCache.getProgramCacheKey( parameters ); - - let program = materialProperties.program; - let programChange = true; - - if ( program === undefined ) { - - // new material - material.addEventListener( 'dispose', onMaterialDispose ); - - } else if ( program.cacheKey !== programCacheKey ) { - - // changed glsl or parameters - releaseMaterialProgramReference( material ); - - } else if ( materialProperties.lightsStateVersion !== lightsStateVersion ) { - - programChange = false; - - } else if ( parameters.shaderID !== undefined ) { - - // same glsl and uniform list, envMap still needs the update here to avoid a frame-late effect - - const environment = material.isMeshStandardMaterial ? scene.environment : null; - materialProperties.envMap = cubemaps.get( material.envMap || environment ); - - return; - - } else { - - // only rebuild uniform list - programChange = false; - - } - - if ( programChange ) { - - parameters.uniforms = programCache.getUniforms( material ); - - material.onBeforeCompile( parameters, _this ); - - program = programCache.acquireProgram( parameters, programCacheKey ); - - materialProperties.program = program; - materialProperties.uniforms = parameters.uniforms; - materialProperties.outputEncoding = parameters.outputEncoding; - - } - - const uniforms = materialProperties.uniforms; - - if ( ! material.isShaderMaterial && - ! material.isRawShaderMaterial || - material.clipping === true ) { - - materialProperties.numClippingPlanes = clipping.numPlanes; - materialProperties.numIntersection = clipping.numIntersection; - uniforms.clippingPlanes = clipping.uniform; - - } - - materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; - materialProperties.fog = scene.fog; - materialProperties.envMap = cubemaps.get( material.envMap || materialProperties.environment ); - - // store the light setup it was created for - - materialProperties.needsLights = materialNeedsLights( material ); - materialProperties.lightsStateVersion = lightsStateVersion; - - if ( materialProperties.needsLights ) { - - // wire up the material to this renderer's lighting state - - uniforms.ambientLightColor.value = lights.state.ambient; - uniforms.lightProbe.value = lights.state.probe; - uniforms.directionalLights.value = lights.state.directional; - uniforms.directionalLightShadows.value = lights.state.directionalShadow; - uniforms.spotLights.value = lights.state.spot; - uniforms.spotLightShadows.value = lights.state.spotShadow; - uniforms.rectAreaLights.value = lights.state.rectArea; - uniforms.ltc_1.value = lights.state.rectAreaLTC1; - uniforms.ltc_2.value = lights.state.rectAreaLTC2; - uniforms.pointLights.value = lights.state.point; - uniforms.pointLightShadows.value = lights.state.pointShadow; - uniforms.hemisphereLights.value = lights.state.hemi; - - uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; - uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; - uniforms.spotShadowMap.value = lights.state.spotShadowMap; - uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; - uniforms.pointShadowMap.value = lights.state.pointShadowMap; - uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; - // TODO (abelnation): add area lights shadow info to uniforms - - } - - const progUniforms = materialProperties.program.getUniforms(); - const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); - - materialProperties.uniformsList = uniformsList; - - } - - function setProgram( camera, scene, material, object ) { - - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - - textures.resetTextureUnits(); - - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; - const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding; - const envMap = cubemaps.get( material.envMap || environment ); - - const materialProperties = properties.get( material ); - const lights = currentRenderState.state.lights; - - if ( _clippingEnabled === true ) { - - if ( _localClippingEnabled === true || camera !== _currentCamera ) { - - const useCache = - camera === _currentCamera && - material.id === _currentMaterialId; - - // we might want to call this function with some ClippingGroup - // object instead of the material, once it becomes feasible - // (#8465, #8379) - clipping.setState( material, camera, useCache ); - - } - - } - - if ( material.version === materialProperties.__version ) { - - if ( material.fog && materialProperties.fog !== fog ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.environment !== environment ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.numClippingPlanes !== undefined && - ( materialProperties.numClippingPlanes !== clipping.numPlanes || - materialProperties.numIntersection !== clipping.numIntersection ) ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.outputEncoding !== encoding ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.envMap !== envMap ) { - - initMaterial( material, scene, object ); - - } - - } else { - - initMaterial( material, scene, object ); - materialProperties.__version = material.version; - - } - - let refreshProgram = false; - let refreshMaterial = false; - let refreshLights = false; - - const program = materialProperties.program, - p_uniforms = program.getUniforms(), - m_uniforms = materialProperties.uniforms; - - if ( state.useProgram( program.program ) ) { - - refreshProgram = true; - refreshMaterial = true; - refreshLights = true; - - } - - if ( material.id !== _currentMaterialId ) { - - _currentMaterialId = material.id; - - refreshMaterial = true; - - } - - if ( refreshProgram || _currentCamera !== camera ) { - - p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); - - if ( capabilities.logarithmicDepthBuffer ) { - - p_uniforms.setValue( _gl, 'logDepthBufFC', - 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - - } - - if ( _currentCamera !== camera ) { - - _currentCamera = camera; - - // lighting uniforms depend on the camera so enforce an update - // now, in case this material supports lights - or later, when - // the next material that does gets activated: - - refreshMaterial = true; // set to true on material change - refreshLights = true; // remains set until update done - - } - - // load material specific uniforms - // (shader material also gets them for the sake of genericity) - - if ( material.isShaderMaterial || - material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshStandardMaterial || - material.envMap ) { - - const uCamPos = p_uniforms.map.cameraPosition; - - if ( uCamPos !== undefined ) { - - uCamPos.setValue( _gl, - _vector3.setFromMatrixPosition( camera.matrixWorld ) ); - - } - - } - - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial ) { - - p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); - - } - - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial || - material.isShadowMaterial || - material.skinning ) { - - p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); - - } - - } - - // skinning uniforms must be set even if material didn't change - // auto-setting of texture unit for bone texture must go before other textures - // otherwise textures used for skinning can take over texture units reserved for other material textures - - if ( material.skinning ) { - - p_uniforms.setOptional( _gl, object, 'bindMatrix' ); - p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); - - const skeleton = object.skeleton; - - if ( skeleton ) { - - const bones = skeleton.bones; - - if ( capabilities.floatVertexTextures ) { - - if ( skeleton.boneTexture === undefined ) { - - // layout (1 matrix = 4 pixels) - // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) - // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) - // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) - // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) - // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) - - - let size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix - size = MathUtils.ceilPowerOfTwo( size ); - size = Math.max( size, 4 ); - - const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel - boneMatrices.set( skeleton.boneMatrices ); // copy current values - - const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); - - skeleton.boneMatrices = boneMatrices; - skeleton.boneTexture = boneTexture; - skeleton.boneTextureSize = size; - - } - - p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); - p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); - - } else { - - p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); - - } - - } - - } - - if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { - - materialProperties.receiveShadow = object.receiveShadow; - p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); - - } - - if ( refreshMaterial ) { - - p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); - - if ( materialProperties.needsLights ) { - - // the current material requires lighting info - - // note: all lighting uniforms are always set correctly - // they simply reference the renderer's state for their - // values - // - // use the current material's .needsUpdate flags to set - // the GL state when required - - markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); - - } - - // refresh uniforms common to several materials - - if ( fog && material.fog ) { - - materials.refreshFogUniforms( m_uniforms, fog ); - - } - - materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height ); - - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - - } - - if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { - - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - material.uniformsNeedUpdate = false; - - } - - if ( material.isSpriteMaterial ) { - - p_uniforms.setValue( _gl, 'center', object.center ); - - } - - // common matrices - - p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); - p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); - p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); - - return program; - - } - - // If uniforms are marked as clean, they don't need to be loaded to the GPU. - - function markUniformsLightsNeedsUpdate( uniforms, value ) { - - uniforms.ambientLightColor.needsUpdate = value; - uniforms.lightProbe.needsUpdate = value; - - uniforms.directionalLights.needsUpdate = value; - uniforms.directionalLightShadows.needsUpdate = value; - uniforms.pointLights.needsUpdate = value; - uniforms.pointLightShadows.needsUpdate = value; - uniforms.spotLights.needsUpdate = value; - uniforms.spotLightShadows.needsUpdate = value; - uniforms.rectAreaLights.needsUpdate = value; - uniforms.hemisphereLights.needsUpdate = value; - - } - - function materialNeedsLights( material ) { - - return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || - material.isMeshStandardMaterial || material.isShadowMaterial || - ( material.isShaderMaterial && material.lights === true ); - - } - - // - this.setFramebuffer = function ( value ) { - - if ( _framebuffer !== value && _currentRenderTarget === null ) _gl.bindFramebuffer( 36160, value ); - - _framebuffer = value; - - }; - - this.getActiveCubeFace = function () { - - return _currentActiveCubeFace; - - }; - - this.getActiveMipmapLevel = function () { - - return _currentActiveMipmapLevel; - - }; - - this.getRenderList = function () { - - return currentRenderList; - - }; - - this.setRenderList = function ( renderList ) { - - currentRenderList = renderList; - - }; - - this.getRenderState = function () { - - return currentRenderState; - - }; - - this.setRenderState = function ( renderState ) { - - currentRenderState = renderState; - - }; - - this.getRenderTarget = function () { - - return _currentRenderTarget; - - }; - - this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { - - _currentRenderTarget = renderTarget; - _currentActiveCubeFace = activeCubeFace; - _currentActiveMipmapLevel = activeMipmapLevel; - - if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { - - textures.setupRenderTarget( renderTarget ); - - } - - let framebuffer = _framebuffer; - let isCube = false; - - if ( renderTarget ) { - - const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; - - if ( renderTarget.isWebGLCubeRenderTarget ) { - - framebuffer = __webglFramebuffer[ activeCubeFace ]; - isCube = true; - - } else if ( renderTarget.isWebGLMultisampleRenderTarget ) { - - framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; - - } else { - - framebuffer = __webglFramebuffer; - - } - - _currentViewport.copy( renderTarget.viewport ); - _currentScissor.copy( renderTarget.scissor ); - _currentScissorTest = renderTarget.scissorTest; - - } else { - - _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); - _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); - _currentScissorTest = _scissorTest; - - } - - if ( _currentFramebuffer !== framebuffer ) { - - _gl.bindFramebuffer( 36160, framebuffer ); - _currentFramebuffer = framebuffer; - - } - - state.viewport( _currentViewport ); - state.scissor( _currentScissor ); - state.setScissorTest( _currentScissorTest ); - - if ( isCube ) { - - const textureProperties = properties.get( renderTarget.texture ); - _gl.framebufferTexture2D( 36160, 36064, 34069 + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); - - } - - }; - - this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { - - if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - return; - - } - - let framebuffer = properties.get( renderTarget ).__webglFramebuffer; - - if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { - - framebuffer = framebuffer[ activeCubeFaceIndex ]; - - } - - if ( framebuffer ) { - - let restore = false; - - if ( framebuffer !== _currentFramebuffer ) { - - _gl.bindFramebuffer( 36160, framebuffer ); - - restore = true; - - } - - try { - - const texture = renderTarget.texture; - const textureFormat = texture.format; - const textureType = texture.type; - - if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); - return; - - } - - if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // IE11, Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! ( textureType === HalfFloatType && ( capabilities.isWebGL2 ? extensions.get( 'EXT_color_buffer_float' ) : extensions.get( 'EXT_color_buffer_half_float' ) ) ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); - return; - - } - - if ( _gl.checkFramebufferStatus( 36160 ) === 36053 ) { - - // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) - - if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); - - } - - } else { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); - - } - - } finally { - - if ( restore ) { - - _gl.bindFramebuffer( 36160, _currentFramebuffer ); - - } - - } - - } - - }; - - this.copyFramebufferToTexture = function ( position, texture, level ) { - - if ( level === undefined ) level = 0; - - const levelScale = Math.pow( 2, - level ); - const width = Math.floor( texture.image.width * levelScale ); - const height = Math.floor( texture.image.height * levelScale ); - const glFormat = utils.convert( texture.format ); - - textures.setTexture2D( texture, 0 ); - - _gl.copyTexImage2D( 3553, level, glFormat, position.x, position.y, width, height, 0 ); - - state.unbindTexture(); - - }; - - this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level ) { - - if ( level === undefined ) level = 0; - - const width = srcTexture.image.width; - const height = srcTexture.image.height; - const glFormat = utils.convert( dstTexture.format ); - const glType = utils.convert( dstTexture.type ); - - textures.setTexture2D( dstTexture, 0 ); - - // As another texture upload may have changed pixelStorei - // parameters, make sure they are correct for the dstTexture - _gl.pixelStorei( 37440, dstTexture.flipY ); - _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); - _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); - - if ( srcTexture.isDataTexture ) { - - _gl.texSubImage2D( 3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); - - } else { - - if ( srcTexture.isCompressedTexture ) { - - _gl.compressedTexSubImage2D( 3553, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); - - } else { - - _gl.texSubImage2D( 3553, level, position.x, position.y, glFormat, glType, srcTexture.image ); - - } - - } - - // Generate mipmaps only when copying level 0 - if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( 3553 ); - - state.unbindTexture(); - - }; - - this.initTexture = function ( texture ) { - - textures.setTexture2D( texture, 0 ); - - state.unbindTexture(); - - }; - - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef - - } - - } - - function WebGL1Renderer( parameters ) { - - WebGLRenderer.call( this, parameters ); - - } - - WebGL1Renderer.prototype = Object.assign( Object.create( WebGLRenderer.prototype ), { - - constructor: WebGL1Renderer, - - isWebGL1Renderer: true - - } ); - - class FogExp2 { - - constructor( color, density ) { - - Object.defineProperty( this, 'isFogExp2', { value: true } ); - - this.name = ''; - - this.color = new Color( color ); - this.density = ( density !== undefined ) ? density : 0.00025; - - } - - clone() { - - return new FogExp2( this.color, this.density ); - - } - - toJSON( /* meta */ ) { - - return { - type: 'FogExp2', - color: this.color.getHex(), - density: this.density - }; - - } - - } - - class Fog { - - constructor( color, near, far ) { - - Object.defineProperty( this, 'isFog', { value: true } ); - - this.name = ''; - - this.color = new Color( color ); - - this.near = ( near !== undefined ) ? near : 1; - this.far = ( far !== undefined ) ? far : 1000; - - } - - clone() { - - return new Fog( this.color, this.near, this.far ); - - } - - toJSON( /* meta */ ) { - - return { - type: 'Fog', - color: this.color.getHex(), - near: this.near, - far: this.far - }; - - } - - } - - class Scene extends Object3D { - - constructor() { - - super(); - - Object.defineProperty( this, 'isScene', { value: true } ); - - this.type = 'Scene'; - - this.background = null; - this.environment = null; - this.fog = null; - - this.overrideMaterial = null; - - this.autoUpdate = true; // checked by the renderer - - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef - - } - - } - - copy( source, recursive ) { - - super.copy( source, recursive ); - - if ( source.background !== null ) this.background = source.background.clone(); - if ( source.environment !== null ) this.environment = source.environment.clone(); - if ( source.fog !== null ) this.fog = source.fog.clone(); - - if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); - - this.autoUpdate = source.autoUpdate; - this.matrixAutoUpdate = source.matrixAutoUpdate; - - return this; - - } - - toJSON( meta ) { - - const data = super.toJSON( meta ); - - if ( this.background !== null ) data.object.background = this.background.toJSON( meta ); - if ( this.environment !== null ) data.object.environment = this.environment.toJSON( meta ); - if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); - - return data; - - } - - } - - function InterleavedBuffer( array, stride ) { - - this.array = array; - this.stride = stride; - this.count = array !== undefined ? array.length / stride : 0; - - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; - - this.version = 0; - - this.uuid = MathUtils.generateUUID(); - - } - - Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - Object.assign( InterleavedBuffer.prototype, { - - isInterleavedBuffer: true, - - onUploadCallback: function () {}, - - setUsage: function ( value ) { - - this.usage = value; - - return this; - - }, - - copy: function ( source ) { - - this.array = new source.array.constructor( source.array ); - this.count = source.count; - this.stride = source.stride; - this.usage = source.usage; - - return this; - - }, - - copyAt: function ( index1, attribute, index2 ) { - - index1 *= this.stride; - index2 *= attribute.stride; - - for ( let i = 0, l = this.stride; i < l; i ++ ) { - - this.array[ index1 + i ] = attribute.array[ index2 + i ]; - - } - - return this; - - }, - - set: function ( value, offset ) { - - if ( offset === undefined ) offset = 0; - - this.array.set( value, offset ); - - return this; - - }, - - clone: function ( data ) { - - if ( data.arrayBuffers === undefined ) { - - data.arrayBuffers = {}; - - } - - if ( this.array.buffer._uuid === undefined ) { - - this.array.buffer._uuid = MathUtils.generateUUID(); - - } - - if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - - data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; - - } - - const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); - - const ib = new InterleavedBuffer( array, this.stride ); - ib.setUsage( this.usage ); - - return ib; - - }, - - onUpload: function ( callback ) { - - this.onUploadCallback = callback; - - return this; - - }, - - toJSON: function ( data ) { - - if ( data.arrayBuffers === undefined ) { - - data.arrayBuffers = {}; - - } - - // generate UUID for array buffer if necessary - - if ( this.array.buffer._uuid === undefined ) { - - this.array.buffer._uuid = MathUtils.generateUUID(); - - } - - if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - - data.arrayBuffers[ this.array.buffer._uuid ] = Array.prototype.slice.call( new Uint32Array( this.array.buffer ) ); - - } - - // - - return { - uuid: this.uuid, - buffer: this.array.buffer._uuid, - type: this.array.constructor.name, - stride: this.stride - }; - - } - - } ); - - const _vector$6 = new Vector3(); - - function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) { - - this.name = ''; - - this.data = interleavedBuffer; - this.itemSize = itemSize; - this.offset = offset; - - this.normalized = normalized === true; - - } - - Object.defineProperties( InterleavedBufferAttribute.prototype, { - - count: { - - get: function () { - - return this.data.count; - - } - - }, - - array: { - - get: function () { - - return this.data.array; - - } - - }, - - needsUpdate: { - - set: function ( value ) { - - this.data.needsUpdate = value; - - } - - } - - } ); - - Object.assign( InterleavedBufferAttribute.prototype, { - - isInterleavedBufferAttribute: true, - - applyMatrix4: function ( m ) { - - for ( let i = 0, l = this.data.count; i < l; i ++ ) { - - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); - - _vector$6.applyMatrix4( m ); - - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - - } - - return this; - - }, - - setX: function ( index, x ) { - - this.data.array[ index * this.data.stride + this.offset ] = x; - - return this; - - }, - - setY: function ( index, y ) { - - this.data.array[ index * this.data.stride + this.offset + 1 ] = y; - - return this; - - }, - - setZ: function ( index, z ) { - - this.data.array[ index * this.data.stride + this.offset + 2 ] = z; - - return this; - - }, - - setW: function ( index, w ) { - - this.data.array[ index * this.data.stride + this.offset + 3 ] = w; - - return this; - - }, - - getX: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset ]; - - }, - - getY: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 1 ]; - - }, - - getZ: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 2 ]; - - }, - - getW: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 3 ]; - - }, - - setXY: function ( index, x, y ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - - return this; - - }, - - setXYZ: function ( index, x, y, z ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - - return this; - - }, - - setXYZW: function ( index, x, y, z, w ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - this.data.array[ index + 3 ] = w; - - return this; - - }, - - clone: function ( data ) { - - if ( data === undefined ) { - - console.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data.' ); - - const array = []; - - for ( let i = 0; i < this.count; i ++ ) { - - const index = i * this.data.stride + this.offset; - - for ( let j = 0; j < this.itemSize; j ++ ) { - - array.push( this.data.array[ index + j ] ); - - } - - } - - return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); - - } else { - - if ( data.interleavedBuffers === undefined ) { - - data.interleavedBuffers = {}; - - } - - if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { - - data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); - - } - - return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); - - } - - }, - - toJSON: function ( data ) { - - if ( data === undefined ) { - - console.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data.' ); - - const array = []; - - for ( let i = 0; i < this.count; i ++ ) { - - const index = i * this.data.stride + this.offset; - - for ( let j = 0; j < this.itemSize; j ++ ) { - - array.push( this.data.array[ index + j ] ); - - } - - } - - // deinterleave data and save it as an ordinary buffer attribute for now - - return { - itemSize: this.itemSize, - type: this.array.constructor.name, - array: array, - normalized: this.normalized - }; - - } else { - - // save as true interlaved attribtue - - if ( data.interleavedBuffers === undefined ) { - - data.interleavedBuffers = {}; - - } - - if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { - - data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); - - } - - return { - isInterleavedBufferAttribute: true, - itemSize: this.itemSize, - data: this.data.uuid, - offset: this.offset, - normalized: this.normalized - }; - - } - - } - - } ); - - /** - * parameters = { - * color: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * rotation: , - * sizeAttenuation: - * } - */ - - function SpriteMaterial( parameters ) { - - Material.call( this ); - - this.type = 'SpriteMaterial'; - - this.color = new Color( 0xffffff ); - - this.map = null; - - this.alphaMap = null; - - this.rotation = 0; - - this.sizeAttenuation = true; - - this.transparent = true; - - this.setValues( parameters ); - - } - - SpriteMaterial.prototype = Object.create( Material.prototype ); - SpriteMaterial.prototype.constructor = SpriteMaterial; - SpriteMaterial.prototype.isSpriteMaterial = true; - - SpriteMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.rotation = source.rotation; - - this.sizeAttenuation = source.sizeAttenuation; - - return this; - - }; - - let _geometry; - - const _intersectPoint = new Vector3(); - const _worldScale = new Vector3(); - const _mvPosition = new Vector3(); - - const _alignedPosition = new Vector2(); - const _rotatedPosition = new Vector2(); - const _viewWorldMatrix = new Matrix4(); - - const _vA$1 = new Vector3(); - const _vB$1 = new Vector3(); - const _vC$1 = new Vector3(); - - const _uvA$1 = new Vector2(); - const _uvB$1 = new Vector2(); - const _uvC$1 = new Vector2(); - - function Sprite( material ) { - - Object3D.call( this ); - - this.type = 'Sprite'; - - if ( _geometry === undefined ) { - - _geometry = new BufferGeometry(); - - const float32Array = new Float32Array( [ - - 0.5, - 0.5, 0, 0, 0, - 0.5, - 0.5, 0, 1, 0, - 0.5, 0.5, 0, 1, 1, - - 0.5, 0.5, 0, 0, 1 - ] ); - - const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); - - _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); - _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); - _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); - - } - - this.geometry = _geometry; - this.material = ( material !== undefined ) ? material : new SpriteMaterial(); - - this.center = new Vector2( 0.5, 0.5 ); - - } - - Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Sprite, - - isSprite: true, - - raycast: function ( raycaster, intersects ) { - - if ( raycaster.camera === null ) { - - console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); - - } - - _worldScale.setFromMatrixScale( this.matrixWorld ); - - _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); - this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); - - _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); - - if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { - - _worldScale.multiplyScalar( - _mvPosition.z ); - - } - - const rotation = this.material.rotation; - let sin, cos; - - if ( rotation !== 0 ) { - - cos = Math.cos( rotation ); - sin = Math.sin( rotation ); - - } - - const center = this.center; - - transformVertex( _vA$1.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vB$1.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vC$1.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - - _uvA$1.set( 0, 0 ); - _uvB$1.set( 1, 0 ); - _uvC$1.set( 1, 1 ); - - // check first triangle - let intersect = raycaster.ray.intersectTriangle( _vA$1, _vB$1, _vC$1, false, _intersectPoint ); - - if ( intersect === null ) { - - // check second triangle - transformVertex( _vB$1.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - _uvB$1.set( 0, 1 ); - - intersect = raycaster.ray.intersectTriangle( _vA$1, _vC$1, _vB$1, false, _intersectPoint ); - if ( intersect === null ) { - - return; - - } - - } - - const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); - - if ( distance < raycaster.near || distance > raycaster.far ) return; - - intersects.push( { - - distance: distance, - point: _intersectPoint.clone(), - uv: Triangle.getUV( _intersectPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ), - face: null, - object: this - - } ); - - }, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - if ( source.center !== undefined ) this.center.copy( source.center ); - - this.material = source.material; - - return this; - - } - - } ); - - function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { - - // compute position in camera space - _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); - - // to check if rotation is not zero - if ( sin !== undefined ) { - - _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); - _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); - - } else { - - _rotatedPosition.copy( _alignedPosition ); - - } - - - vertexPosition.copy( mvPosition ); - vertexPosition.x += _rotatedPosition.x; - vertexPosition.y += _rotatedPosition.y; - - // transform to world space - vertexPosition.applyMatrix4( _viewWorldMatrix ); - - } - - const _v1$4 = new Vector3(); - const _v2$2 = new Vector3(); - - function LOD() { - - Object3D.call( this ); - - this._currentLevel = 0; - - this.type = 'LOD'; - - Object.defineProperties( this, { - levels: { - enumerable: true, - value: [] - } - } ); - - this.autoUpdate = true; - - } - - LOD.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: LOD, - - isLOD: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source, false ); - - const levels = source.levels; - - for ( let i = 0, l = levels.length; i < l; i ++ ) { - - const level = levels[ i ]; - - this.addLevel( level.object.clone(), level.distance ); - - } - - this.autoUpdate = source.autoUpdate; - - return this; - - }, - - addLevel: function ( object, distance ) { - - if ( distance === undefined ) distance = 0; - - distance = Math.abs( distance ); - - const levels = this.levels; - - let l; - - for ( l = 0; l < levels.length; l ++ ) { - - if ( distance < levels[ l ].distance ) { - - break; - - } - - } - - levels.splice( l, 0, { distance: distance, object: object } ); - - this.add( object ); - - return this; - - }, - - getCurrentLevel: function () { - - return this._currentLevel; - - }, - - getObjectForDistance: function ( distance ) { - - const levels = this.levels; - - if ( levels.length > 0 ) { - - let i, l; - - for ( i = 1, l = levels.length; i < l; i ++ ) { - - if ( distance < levels[ i ].distance ) { - - break; - - } - - } - - return levels[ i - 1 ].object; - - } - - return null; - - }, - - raycast: function ( raycaster, intersects ) { - - const levels = this.levels; - - if ( levels.length > 0 ) { - - _v1$4.setFromMatrixPosition( this.matrixWorld ); - - const distance = raycaster.ray.origin.distanceTo( _v1$4 ); - - this.getObjectForDistance( distance ).raycast( raycaster, intersects ); - - } - - }, - - update: function ( camera ) { - - const levels = this.levels; - - if ( levels.length > 1 ) { - - _v1$4.setFromMatrixPosition( camera.matrixWorld ); - _v2$2.setFromMatrixPosition( this.matrixWorld ); - - const distance = _v1$4.distanceTo( _v2$2 ) / camera.zoom; - - levels[ 0 ].object.visible = true; - - let i, l; - - for ( i = 1, l = levels.length; i < l; i ++ ) { - - if ( distance >= levels[ i ].distance ) { - - levels[ i - 1 ].object.visible = false; - levels[ i ].object.visible = true; - - } else { - - break; - - } - - } - - this._currentLevel = i - 1; - - for ( ; i < l; i ++ ) { - - levels[ i ].object.visible = false; - - } - - } - - }, - - toJSON: function ( meta ) { - - const data = Object3D.prototype.toJSON.call( this, meta ); - - if ( this.autoUpdate === false ) data.object.autoUpdate = false; - - data.object.levels = []; - - const levels = this.levels; - - for ( let i = 0, l = levels.length; i < l; i ++ ) { - - const level = levels[ i ]; - - data.object.levels.push( { - object: level.object.uuid, - distance: level.distance - } ); - - } - - return data; - - } - - } ); - - function SkinnedMesh( geometry, material ) { - - if ( geometry && geometry.isGeometry ) { - - console.error( 'THREE.SkinnedMesh no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - Mesh.call( this, geometry, material ); - - this.type = 'SkinnedMesh'; - - this.bindMode = 'attached'; - this.bindMatrix = new Matrix4(); - this.bindMatrixInverse = new Matrix4(); - - } - - SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { - - constructor: SkinnedMesh, - - isSkinnedMesh: true, - - copy: function ( source ) { - - Mesh.prototype.copy.call( this, source ); - - this.bindMode = source.bindMode; - this.bindMatrix.copy( source.bindMatrix ); - this.bindMatrixInverse.copy( source.bindMatrixInverse ); - - this.skeleton = source.skeleton; - - return this; - - }, - - bind: function ( skeleton, bindMatrix ) { - - this.skeleton = skeleton; - - if ( bindMatrix === undefined ) { - - this.updateMatrixWorld( true ); - - this.skeleton.calculateInverses(); - - bindMatrix = this.matrixWorld; - - } - - this.bindMatrix.copy( bindMatrix ); - this.bindMatrixInverse.getInverse( bindMatrix ); - - }, - - pose: function () { - - this.skeleton.pose(); - - }, - - normalizeSkinWeights: function () { - - const vector = new Vector4(); - - const skinWeight = this.geometry.attributes.skinWeight; - - for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { - - vector.x = skinWeight.getX( i ); - vector.y = skinWeight.getY( i ); - vector.z = skinWeight.getZ( i ); - vector.w = skinWeight.getW( i ); - - const scale = 1.0 / vector.manhattanLength(); - - if ( scale !== Infinity ) { - - vector.multiplyScalar( scale ); - - } else { - - vector.set( 1, 0, 0, 0 ); // do something reasonable - - } - - skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); - - } - - }, - - updateMatrixWorld: function ( force ) { - - Mesh.prototype.updateMatrixWorld.call( this, force ); - - if ( this.bindMode === 'attached' ) { - - this.bindMatrixInverse.getInverse( this.matrixWorld ); - - } else if ( this.bindMode === 'detached' ) { - - this.bindMatrixInverse.getInverse( this.bindMatrix ); - - } else { - - console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); - - } - - }, - - boneTransform: ( function () { - - const basePosition = new Vector3(); - - const skinIndex = new Vector4(); - const skinWeight = new Vector4(); - - const vector = new Vector3(); - const matrix = new Matrix4(); - - return function ( index, target ) { - - const skeleton = this.skeleton; - const geometry = this.geometry; - - skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); - skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); - - basePosition.fromBufferAttribute( geometry.attributes.position, index ).applyMatrix4( this.bindMatrix ); - - target.set( 0, 0, 0 ); - - for ( let i = 0; i < 4; i ++ ) { - - const weight = skinWeight.getComponent( i ); - - if ( weight !== 0 ) { - - const boneIndex = skinIndex.getComponent( i ); - - matrix.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); - - target.addScaledVector( vector.copy( basePosition ).applyMatrix4( matrix ), weight ); - - } - - } - - return target.applyMatrix4( this.bindMatrixInverse ); - - }; - - }() ) - - } ); - - const _offsetMatrix = new Matrix4(); - const _identityMatrix = new Matrix4(); - - function Skeleton( bones, boneInverses ) { - - // copy the bone array - - bones = bones || []; - - this.bones = bones.slice( 0 ); - this.boneMatrices = new Float32Array( this.bones.length * 16 ); - - this.frame = - 1; - - // use the supplied bone inverses or calculate the inverses - - if ( boneInverses === undefined ) { - - this.calculateInverses(); - - } else { - - if ( this.bones.length === boneInverses.length ) { - - this.boneInverses = boneInverses.slice( 0 ); - - } else { - - console.warn( 'THREE.Skeleton boneInverses is the wrong length.' ); - - this.boneInverses = []; - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - this.boneInverses.push( new Matrix4() ); - - } - - } - - } - - } - - Object.assign( Skeleton.prototype, { - - calculateInverses: function () { - - this.boneInverses = []; - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - const inverse = new Matrix4(); - - if ( this.bones[ i ] ) { - - inverse.getInverse( this.bones[ i ].matrixWorld ); - - } - - this.boneInverses.push( inverse ); - - } - - }, - - pose: function () { - - // recover the bind-time world matrices - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - const bone = this.bones[ i ]; - - if ( bone ) { - - bone.matrixWorld.getInverse( this.boneInverses[ i ] ); - - } - - } - - // compute the local matrices, positions, rotations and scales - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - const bone = this.bones[ i ]; - - if ( bone ) { - - if ( bone.parent && bone.parent.isBone ) { - - bone.matrix.getInverse( bone.parent.matrixWorld ); - bone.matrix.multiply( bone.matrixWorld ); - - } else { - - bone.matrix.copy( bone.matrixWorld ); - - } - - bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); - - } - - } - - }, - - update: function () { - - const bones = this.bones; - const boneInverses = this.boneInverses; - const boneMatrices = this.boneMatrices; - const boneTexture = this.boneTexture; - - // flatten bone matrices to array - - for ( let i = 0, il = bones.length; i < il; i ++ ) { - - // compute the offset between the current and the original transform - - const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; - - _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); - _offsetMatrix.toArray( boneMatrices, i * 16 ); - - } - - if ( boneTexture !== undefined ) { - - boneTexture.needsUpdate = true; - - } - - }, - - clone: function () { - - return new Skeleton( this.bones, this.boneInverses ); - - }, - - getBoneByName: function ( name ) { - - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - - const bone = this.bones[ i ]; - - if ( bone.name === name ) { - - return bone; - - } - - } - - return undefined; - - }, - - dispose: function ( ) { - - if ( this.boneTexture ) { - - this.boneTexture.dispose(); - - this.boneTexture = undefined; - - } - - } - - } ); - - function Bone() { - - Object3D.call( this ); - - this.type = 'Bone'; - - } - - Bone.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Bone, - - isBone: true - - } ); - - const _instanceLocalMatrix = new Matrix4(); - const _instanceWorldMatrix = new Matrix4(); - - const _instanceIntersects = []; - - const _mesh = new Mesh(); - - function InstancedMesh( geometry, material, count ) { - - Mesh.call( this, geometry, material ); - - this.instanceMatrix = new BufferAttribute( new Float32Array( count * 16 ), 16 ); - this.instanceColor = null; - - this.count = count; - - this.frustumCulled = false; - - } - - InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { - - constructor: InstancedMesh, - - isInstancedMesh: true, - - copy: function ( source ) { - - Mesh.prototype.copy.call( this, source ); - - this.instanceMatrix.copy( source.instanceMatrix ); - this.count = source.count; - - return this; - - }, - - setColorAt: function ( index, color ) { - - if ( this.instanceColor === null ) { - - this.instanceColor = new BufferAttribute( new Float32Array( this.count * 3 ), 3 ); - - } - - color.toArray( this.instanceColor.array, index * 3 ); - - }, - - getMatrixAt: function ( index, matrix ) { - - matrix.fromArray( this.instanceMatrix.array, index * 16 ); - - }, - - raycast: function ( raycaster, intersects ) { - - const matrixWorld = this.matrixWorld; - const raycastTimes = this.count; - - _mesh.geometry = this.geometry; - _mesh.material = this.material; - - if ( _mesh.material === undefined ) return; - - for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { - - // calculate the world matrix for each instance - - this.getMatrixAt( instanceId, _instanceLocalMatrix ); - - _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); - - // the mesh represents this single instance - - _mesh.matrixWorld = _instanceWorldMatrix; - - _mesh.raycast( raycaster, _instanceIntersects ); - - // process the result of raycast - - for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { - - const intersect = _instanceIntersects[ i ]; - intersect.instanceId = instanceId; - intersect.object = this; - intersects.push( intersect ); - - } - - _instanceIntersects.length = 0; - - } - - }, - - setMatrixAt: function ( index, matrix ) { - - matrix.toArray( this.instanceMatrix.array, index * 16 ); - - }, - - updateMorphTargets: function () { - - } - - } ); - - /** - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * linecap: "round", - * linejoin: "round" - * } - */ - - function LineBasicMaterial( parameters ) { - - Material.call( this ); - - this.type = 'LineBasicMaterial'; - - this.color = new Color( 0xffffff ); - - this.linewidth = 1; - this.linecap = 'round'; - this.linejoin = 'round'; - - this.morphTargets = false; - - this.setValues( parameters ); - - } - - LineBasicMaterial.prototype = Object.create( Material.prototype ); - LineBasicMaterial.prototype.constructor = LineBasicMaterial; - - LineBasicMaterial.prototype.isLineBasicMaterial = true; - - LineBasicMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.linewidth = source.linewidth; - this.linecap = source.linecap; - this.linejoin = source.linejoin; - - this.morphTargets = source.morphTargets; - - return this; - - }; - - const _start = new Vector3(); - const _end = new Vector3(); - const _inverseMatrix$1 = new Matrix4(); - const _ray$1 = new Ray(); - const _sphere$2 = new Sphere(); - - function Line( geometry, material, mode ) { - - if ( mode === 1 ) { - - console.error( 'THREE.Line: parameter THREE.LinePieces no longer supported. Use THREE.LineSegments instead.' ); - - } - - Object3D.call( this ); - - this.type = 'Line'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new LineBasicMaterial(); - - this.updateMorphTargets(); - - } - - Line.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Line, - - isLine: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - this.material = source.material; - this.geometry = source.geometry; - - return this; - - }, - - computeLineDistances: function () { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - // we assume non-indexed geometry - - if ( geometry.index === null ) { - - const positionAttribute = geometry.attributes.position; - const lineDistances = [ 0 ]; - - for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { - - _start.fromBufferAttribute( positionAttribute, i - 1 ); - _end.fromBufferAttribute( positionAttribute, i ); - - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += _start.distanceTo( _end ); - - } - - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - - } else { - - console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - - } - - } else if ( geometry.isGeometry ) { - - const vertices = geometry.vertices; - const lineDistances = geometry.lineDistances; - - lineDistances[ 0 ] = 0; - - for ( let i = 1, l = vertices.length; i < l; i ++ ) { - - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += vertices[ i - 1 ].distanceTo( vertices[ i ] ); - - } - - } - - return this; - - }, - - raycast: function ( raycaster, intersects ) { - - const geometry = this.geometry; - const matrixWorld = this.matrixWorld; - const threshold = raycaster.params.Line.threshold; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _sphere$2.copy( geometry.boundingSphere ); - _sphere$2.applyMatrix4( matrixWorld ); - _sphere$2.radius += threshold; - - if ( raycaster.ray.intersectsSphere( _sphere$2 ) === false ) return; - - // - - _inverseMatrix$1.getInverse( matrixWorld ); - _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); - - const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - const localThresholdSq = localThreshold * localThreshold; - - const vStart = new Vector3(); - const vEnd = new Vector3(); - const interSegment = new Vector3(); - const interRay = new Vector3(); - const step = ( this && this.isLineSegments ) ? 2 : 1; - - if ( geometry.isBufferGeometry ) { - - const index = geometry.index; - const attributes = geometry.attributes; - const positions = attributes.position.array; - - if ( index !== null ) { - - const indices = index.array; - - for ( let i = 0, l = indices.length - 1; i < l; i += step ) { - - const a = indices[ i ]; - const b = indices[ i + 1 ]; - - vStart.fromArray( positions, a * 3 ); - vEnd.fromArray( positions, b * 3 ); - - const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - - if ( distSq > localThresholdSq ) continue; - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - const distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } else { - - for ( let i = 0, l = positions.length / 3 - 1; i < l; i += step ) { - - vStart.fromArray( positions, 3 * i ); - vEnd.fromArray( positions, 3 * i + 3 ); - - const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - - if ( distSq > localThresholdSq ) continue; - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - const distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } - - } else if ( geometry.isGeometry ) { - - const vertices = geometry.vertices; - const nbVertices = vertices.length; - - for ( let i = 0; i < nbVertices - 1; i += step ) { - - const distSq = _ray$1.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); - - if ( distSq > localThresholdSq ) continue; - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - const distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } - - }, - - updateMorphTargets: function () { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); - - if ( keys.length > 0 ) { - - const morphAttribute = morphAttributes[ keys[ 0 ] ]; - - if ( morphAttribute !== undefined ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - - const name = morphAttribute[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } - - } - - } - - } else { - - const morphTargets = geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - console.error( 'THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - } - - } ); - - const _start$1 = new Vector3(); - const _end$1 = new Vector3(); - - function LineSegments( geometry, material ) { - - Line.call( this, geometry, material ); - - this.type = 'LineSegments'; - - } - - LineSegments.prototype = Object.assign( Object.create( Line.prototype ), { - - constructor: LineSegments, - - isLineSegments: true, - - computeLineDistances: function () { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - // we assume non-indexed geometry - - if ( geometry.index === null ) { - - const positionAttribute = geometry.attributes.position; - const lineDistances = []; - - for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { - - _start$1.fromBufferAttribute( positionAttribute, i ); - _end$1.fromBufferAttribute( positionAttribute, i + 1 ); - - lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; - lineDistances[ i + 1 ] = lineDistances[ i ] + _start$1.distanceTo( _end$1 ); - - } - - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - - } else { - - console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - - } - - } else if ( geometry.isGeometry ) { - - const vertices = geometry.vertices; - const lineDistances = geometry.lineDistances; - - for ( let i = 0, l = vertices.length; i < l; i += 2 ) { - - _start$1.copy( vertices[ i ] ); - _end$1.copy( vertices[ i + 1 ] ); - - lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; - lineDistances[ i + 1 ] = lineDistances[ i ] + _start$1.distanceTo( _end$1 ); - - } - - } - - return this; - - } - - } ); - - function LineLoop( geometry, material ) { - - Line.call( this, geometry, material ); - - this.type = 'LineLoop'; - - } - - LineLoop.prototype = Object.assign( Object.create( Line.prototype ), { - - constructor: LineLoop, - - isLineLoop: true, - - } ); - - /** - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * - * size: , - * sizeAttenuation: - * - * morphTargets: - * } - */ - - function PointsMaterial( parameters ) { - - Material.call( this ); - - this.type = 'PointsMaterial'; - - this.color = new Color( 0xffffff ); - - this.map = null; - - this.alphaMap = null; - - this.size = 1; - this.sizeAttenuation = true; - - this.morphTargets = false; - - this.setValues( parameters ); - - } - - PointsMaterial.prototype = Object.create( Material.prototype ); - PointsMaterial.prototype.constructor = PointsMaterial; - - PointsMaterial.prototype.isPointsMaterial = true; - - PointsMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.size = source.size; - this.sizeAttenuation = source.sizeAttenuation; - - this.morphTargets = source.morphTargets; - - return this; - - }; - - const _inverseMatrix$2 = new Matrix4(); - const _ray$2 = new Ray(); - const _sphere$3 = new Sphere(); - const _position$1 = new Vector3(); - - function Points( geometry, material ) { - - Object3D.call( this ); - - this.type = 'Points'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new PointsMaterial(); - - this.updateMorphTargets(); - - } - - Points.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Points, - - isPoints: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - this.material = source.material; - this.geometry = source.geometry; - - return this; - - }, - - raycast: function ( raycaster, intersects ) { - - const geometry = this.geometry; - const matrixWorld = this.matrixWorld; - const threshold = raycaster.params.Points.threshold; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - _sphere$3.copy( geometry.boundingSphere ); - _sphere$3.applyMatrix4( matrixWorld ); - _sphere$3.radius += threshold; - - if ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) return; - - // - - _inverseMatrix$2.getInverse( matrixWorld ); - _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); - - const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - const localThresholdSq = localThreshold * localThreshold; - - if ( geometry.isBufferGeometry ) { - - const index = geometry.index; - const attributes = geometry.attributes; - const positions = attributes.position.array; - - if ( index !== null ) { - - const indices = index.array; - - for ( let i = 0, il = indices.length; i < il; i ++ ) { - - const a = indices[ i ]; - - _position$1.fromArray( positions, a * 3 ); - - testPoint( _position$1, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); - - } - - } else { - - for ( let i = 0, l = positions.length / 3; i < l; i ++ ) { - - _position$1.fromArray( positions, i * 3 ); - - testPoint( _position$1, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); - - } - - } - - } else { - - const vertices = geometry.vertices; - - for ( let i = 0, l = vertices.length; i < l; i ++ ) { - - testPoint( vertices[ i ], i, localThresholdSq, matrixWorld, raycaster, intersects, this ); - - } - - } - - }, - - updateMorphTargets: function () { - - const geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); - - if ( keys.length > 0 ) { - - const morphAttribute = morphAttributes[ keys[ 0 ] ]; - - if ( morphAttribute !== undefined ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - - const name = morphAttribute[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } - - } - - } - - } else { - - const morphTargets = geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - console.error( 'THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - } - - } ); - - function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { - - const rayPointDistanceSq = _ray$2.distanceSqToPoint( point ); - - if ( rayPointDistanceSq < localThresholdSq ) { - - const intersectPoint = new Vector3(); - - _ray$2.closestPointToPoint( point, intersectPoint ); - intersectPoint.applyMatrix4( matrixWorld ); - - const distance = raycaster.ray.origin.distanceTo( intersectPoint ); - - if ( distance < raycaster.near || distance > raycaster.far ) return; - - intersects.push( { - - distance: distance, - distanceToRay: Math.sqrt( rayPointDistanceSq ), - point: intersectPoint, - index: index, - face: null, - object: object - - } ); - - } - - } - - function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - - Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.format = format !== undefined ? format : RGBFormat; - - this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - - this.generateMipmaps = false; - - const scope = this; - - function updateVideo() { - - scope.needsUpdate = true; - video.requestVideoFrameCallback( updateVideo ); - - } - - if ( 'requestVideoFrameCallback' in video ) { - - video.requestVideoFrameCallback( updateVideo ); - - } - - } - - VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), { - - constructor: VideoTexture, - - isVideoTexture: true, - - update: function () { - - const video = this.image; - const hasVideoFrameCallback = 'requestVideoFrameCallback' in video; - - if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { - - this.needsUpdate = true; - - } - - } - - } ); - - function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.image = { width: width, height: height }; - this.mipmaps = mipmaps; - - // no flipping for cube textures - // (also flipping doesn't work for compressed textures ) - - this.flipY = false; - - // can't generate mipmaps for compressed textures - // mips must be embedded in DDS files - - this.generateMipmaps = false; - - } - - CompressedTexture.prototype = Object.create( Texture.prototype ); - CompressedTexture.prototype.constructor = CompressedTexture; - - CompressedTexture.prototype.isCompressedTexture = true; - - function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - - Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.needsUpdate = true; - - } - - CanvasTexture.prototype = Object.create( Texture.prototype ); - CanvasTexture.prototype.constructor = CanvasTexture; - CanvasTexture.prototype.isCanvasTexture = true; - - function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { - - format = format !== undefined ? format : DepthFormat; - - if ( format !== DepthFormat && format !== DepthStencilFormat ) { - - throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); - - } - - if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; - if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; - - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.image = { width: width, height: height }; - - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - - this.flipY = false; - this.generateMipmaps = false; - - } - - DepthTexture.prototype = Object.create( Texture.prototype ); - DepthTexture.prototype.constructor = DepthTexture; - DepthTexture.prototype.isDepthTexture = true; - - class WireframeGeometry extends BufferGeometry { - - constructor( geometry ) { - - super(); - this.type = 'WireframeGeometry'; - - // buffer - - const vertices = []; - - // helper variables - - const edge = [ 0, 0 ], edges = {}; - const keys = [ 'a', 'b', 'c' ]; - - // different logic for Geometry and BufferGeometry - - if ( geometry && geometry.isGeometry ) { - - // create a data structure that contains all edges without duplicates - - const faces = geometry.faces; - - for ( let i = 0, l = faces.length; i < l; i ++ ) { - - const face = faces[ i ]; - - for ( let j = 0; j < 3; j ++ ) { - - const edge1 = face[ keys[ j ] ]; - const edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; - edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates - edge[ 1 ] = Math.max( edge1, edge2 ); - - const key = edge[ 0 ] + ',' + edge[ 1 ]; - - if ( edges[ key ] === undefined ) { - - edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; - - } - - } - - } - - // generate vertices - - for ( const key in edges ) { - - const e = edges[ key ]; - - let vertex = geometry.vertices[ e.index1 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - vertex = geometry.vertices[ e.index2 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } else if ( geometry && geometry.isBufferGeometry ) { - - const vertex = new Vector3(); - - if ( geometry.index !== null ) { - - // indexed BufferGeometry - - const position = geometry.attributes.position; - const indices = geometry.index; - let groups = geometry.groups; - - if ( groups.length === 0 ) { - - groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; - - } - - // create a data structure that contains all eges without duplicates - - for ( let o = 0, ol = groups.length; o < ol; ++ o ) { - - const group = groups[ o ]; - - const start = group.start; - const count = group.count; - - for ( let i = start, l = ( start + count ); i < l; i += 3 ) { - - for ( let j = 0; j < 3; j ++ ) { - - const edge1 = indices.getX( i + j ); - const edge2 = indices.getX( i + ( j + 1 ) % 3 ); - edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates - edge[ 1 ] = Math.max( edge1, edge2 ); - - const key = edge[ 0 ] + ',' + edge[ 1 ]; - - if ( edges[ key ] === undefined ) { - - edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; - - } - - } - - } - - } - - // generate vertices - - for ( const key in edges ) { - - const e = edges[ key ]; - - vertex.fromBufferAttribute( position, e.index1 ); - vertices.push( vertex.x, vertex.y, vertex.z ); - - vertex.fromBufferAttribute( position, e.index2 ); - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } else { - - // non-indexed BufferGeometry - - const position = geometry.attributes.position; - - for ( let i = 0, l = ( position.count / 3 ); i < l; i ++ ) { - - for ( let j = 0; j < 3; j ++ ) { - - // three edges per triangle, an edge is represented as (index1, index2) - // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) - - const index1 = 3 * i + j; - vertex.fromBufferAttribute( position, index1 ); - vertices.push( vertex.x, vertex.y, vertex.z ); - - const index2 = 3 * i + ( ( j + 1 ) % 3 ); - vertex.fromBufferAttribute( position, index2 ); - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } - - } - - } - - // build geometry - - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - - } - - } - - /** - * Parametric Surfaces Geometry - * based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html - */ - - // ParametricGeometry - - function ParametricGeometry( func, slices, stacks ) { - - Geometry.call( this ); - - this.type = 'ParametricGeometry'; - - this.parameters = { - func: func, - slices: slices, - stacks: stacks - }; - - this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) ); - this.mergeVertices(); - - } - - ParametricGeometry.prototype = Object.create( Geometry.prototype ); - ParametricGeometry.prototype.constructor = ParametricGeometry; - - // ParametricBufferGeometry - - function ParametricBufferGeometry( func, slices, stacks ) { - - BufferGeometry.call( this ); - - this.type = 'ParametricBufferGeometry'; - - this.parameters = { - func: func, - slices: slices, - stacks: stacks - }; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - const EPS = 0.00001; - - const normal = new Vector3(); - - const p0 = new Vector3(), p1 = new Vector3(); - const pu = new Vector3(), pv = new Vector3(); - - if ( func.length < 3 ) { - - console.error( 'THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter.' ); - - } - - // generate vertices, normals and uvs - - const sliceCount = slices + 1; - - for ( let i = 0; i <= stacks; i ++ ) { - - const v = i / stacks; - - for ( let j = 0; j <= slices; j ++ ) { - - const u = j / slices; - - // vertex - - func( u, v, p0 ); - vertices.push( p0.x, p0.y, p0.z ); - - // normal - - // approximate tangent vectors via finite differences - - if ( u - EPS >= 0 ) { - - func( u - EPS, v, p1 ); - pu.subVectors( p0, p1 ); - - } else { - - func( u + EPS, v, p1 ); - pu.subVectors( p1, p0 ); - - } - - if ( v - EPS >= 0 ) { - - func( u, v - EPS, p1 ); - pv.subVectors( p0, p1 ); - - } else { - - func( u, v + EPS, p1 ); - pv.subVectors( p1, p0 ); - - } - - // cross product of tangent vectors returns surface normal - - normal.crossVectors( pu, pv ).normalize(); - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( u, v ); - - } - - } - - // generate indices - - for ( let i = 0; i < stacks; i ++ ) { - - for ( let j = 0; j < slices; j ++ ) { - - const a = i * sliceCount + j; - const b = i * sliceCount + j + 1; - const c = ( i + 1 ) * sliceCount + j + 1; - const d = ( i + 1 ) * sliceCount + j; - - // faces one and two - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry; - - // PolyhedronGeometry - - class PolyhedronGeometry extends Geometry { - - constructor( vertices, indices, radius, detail ) { - - super(); - - this.type = 'PolyhedronGeometry'; - - this.parameters = { - vertices: vertices, - indices: indices, - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) ); - this.mergeVertices(); - - } - - } - - // PolyhedronBufferGeometry - - class PolyhedronBufferGeometry extends BufferGeometry { - - constructor( vertices, indices, radius, detail ) { - - super(); - - this.type = 'PolyhedronBufferGeometry'; - - this.parameters = { - vertices: vertices, - indices: indices, - radius: radius, - detail: detail - }; - - radius = radius || 1; - detail = detail || 0; - - // default buffer data - - const vertexBuffer = []; - const uvBuffer = []; - - // the subdivision creates the vertex buffer data - - subdivide( detail ); - - // all vertices should lie on a conceptual sphere with a given radius - - applyRadius( radius ); - - // finally, create the uv data - - generateUVs(); - - // build non-indexed geometry - - this.setAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) ); - - if ( detail === 0 ) { - - this.computeVertexNormals(); // flat normals - - } else { - - this.normalizeNormals(); // smooth normals - - } - - // helper functions - - function subdivide( detail ) { - - const a = new Vector3(); - const b = new Vector3(); - const c = new Vector3(); - - // iterate over all faces and apply a subdivison with the given detail value - - for ( let i = 0; i < indices.length; i += 3 ) { - - // get the vertices of the face - - getVertexByIndex( indices[ i + 0 ], a ); - getVertexByIndex( indices[ i + 1 ], b ); - getVertexByIndex( indices[ i + 2 ], c ); - - // perform subdivision - - subdivideFace( a, b, c, detail ); - - } - - } - - function subdivideFace( a, b, c, detail ) { - - const cols = Math.pow( 2, detail ); - - // we use this multidimensional array as a data structure for creating the subdivision - - const v = []; - - // construct all of the vertices for this subdivision - - for ( let i = 0; i <= cols; i ++ ) { - - v[ i ] = []; - - const aj = a.clone().lerp( c, i / cols ); - const bj = b.clone().lerp( c, i / cols ); - - const rows = cols - i; - - for ( let j = 0; j <= rows; j ++ ) { - - if ( j === 0 && i === cols ) { - - v[ i ][ j ] = aj; - - } else { - - v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); - - } - - } - - } - - // construct all of the faces - - for ( let i = 0; i < cols; i ++ ) { - - for ( let j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { - - const k = Math.floor( j / 2 ); - - if ( j % 2 === 0 ) { - - pushVertex( v[ i ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k ] ); - pushVertex( v[ i ][ k ] ); - - } else { - - pushVertex( v[ i ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k ] ); - - } - - } - - } - - } - - function applyRadius( radius ) { - - const vertex = new Vector3(); - - // iterate over the entire buffer and apply the radius to each vertex - - for ( let i = 0; i < vertexBuffer.length; i += 3 ) { - - vertex.x = vertexBuffer[ i + 0 ]; - vertex.y = vertexBuffer[ i + 1 ]; - vertex.z = vertexBuffer[ i + 2 ]; - - vertex.normalize().multiplyScalar( radius ); - - vertexBuffer[ i + 0 ] = vertex.x; - vertexBuffer[ i + 1 ] = vertex.y; - vertexBuffer[ i + 2 ] = vertex.z; - - } - - } - - function generateUVs() { - - const vertex = new Vector3(); - - for ( let i = 0; i < vertexBuffer.length; i += 3 ) { - - vertex.x = vertexBuffer[ i + 0 ]; - vertex.y = vertexBuffer[ i + 1 ]; - vertex.z = vertexBuffer[ i + 2 ]; - - const u = azimuth( vertex ) / 2 / Math.PI + 0.5; - const v = inclination( vertex ) / Math.PI + 0.5; - uvBuffer.push( u, 1 - v ); - - } - - correctUVs(); - - correctSeam(); - - } - - function correctSeam() { - - // handle case when face straddles the seam, see #3269 - - for ( let i = 0; i < uvBuffer.length; i += 6 ) { - - // uv data of a single face - - const x0 = uvBuffer[ i + 0 ]; - const x1 = uvBuffer[ i + 2 ]; - const x2 = uvBuffer[ i + 4 ]; - - const max = Math.max( x0, x1, x2 ); - const min = Math.min( x0, x1, x2 ); - - // 0.9 is somewhat arbitrary - - if ( max > 0.9 && min < 0.1 ) { - - if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; - if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; - if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; - - } - - } - - } - - function pushVertex( vertex ) { - - vertexBuffer.push( vertex.x, vertex.y, vertex.z ); - - } - - function getVertexByIndex( index, vertex ) { - - const stride = index * 3; - - vertex.x = vertices[ stride + 0 ]; - vertex.y = vertices[ stride + 1 ]; - vertex.z = vertices[ stride + 2 ]; - - } - - function correctUVs() { - - const a = new Vector3(); - const b = new Vector3(); - const c = new Vector3(); - - const centroid = new Vector3(); - - const uvA = new Vector2(); - const uvB = new Vector2(); - const uvC = new Vector2(); - - for ( let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { - - a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); - b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); - c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); - - uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); - uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); - uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); - - centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); - - const azi = azimuth( centroid ); - - correctUV( uvA, j + 0, a, azi ); - correctUV( uvB, j + 2, b, azi ); - correctUV( uvC, j + 4, c, azi ); - - } - - } - - function correctUV( uv, stride, vector, azimuth ) { - - if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { - - uvBuffer[ stride ] = uv.x - 1; - - } - - if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { - - uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; - - } - - } - - // Angle around the Y axis, counter-clockwise when looking from above. - - function azimuth( vector ) { - - return Math.atan2( vector.z, - vector.x ); - - } - - - // Angle above the XZ plane. - - function inclination( vector ) { - - return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); - - } - - } - - } - - // TetrahedronGeometry - - class TetrahedronGeometry extends Geometry { - - constructor( radius, detail ) { - - super(); - this.type = 'TetrahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - } - - - // TetrahedronBufferGeometry - - class TetrahedronBufferGeometry extends PolyhedronBufferGeometry { - - constructor( radius, detail ) { - - const vertices = [ - 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 - ]; - - const indices = [ - 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 - ]; - - super( vertices, indices, radius, detail ); - - this.type = 'TetrahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - } - - // OctahedronGeometry - - class OctahedronGeometry extends Geometry { - - constructor( radius, detail ) { - - super(); - - this.type = 'OctahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - } - - // OctahedronBufferGeometry - - class OctahedronBufferGeometry extends PolyhedronBufferGeometry { - - constructor( radius, detail ) { - - const vertices = [ - 1, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, - 1, 0, 0, 0, 1, 0, 0, - 1 - ]; - - const indices = [ - 0, 2, 4, 0, 4, 3, 0, 3, 5, - 0, 5, 2, 1, 2, 5, 1, 5, 3, - 1, 3, 4, 1, 4, 2 - ]; - - super( vertices, indices, radius, detail ); - - this.type = 'OctahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - } - - // IcosahedronGeometry - - class IcosahedronGeometry extends Geometry { - - constructor( radius, detail ) { - - super(); - - this.type = 'IcosahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - } - - // IcosahedronBufferGeometry - - class IcosahedronBufferGeometry extends PolyhedronBufferGeometry { - - constructor( radius, detail ) { - - const t = ( 1 + Math.sqrt( 5 ) ) / 2; - - const vertices = [ - - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, - 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, - t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 - ]; - - const indices = [ - 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, - 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, - 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, - 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 - ]; - - super( vertices, indices, radius, detail ); - - this.type = 'IcosahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - } - - // DodecahedronGeometry - - class DodecahedronGeometry extends Geometry { - - constructor( radius, detail ) { - - super(); - this.type = 'DodecahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - } - - // DodecahedronBufferGeometry - - class DodecahedronBufferGeometry extends PolyhedronBufferGeometry { - - constructor( radius, detail ) { - - const t = ( 1 + Math.sqrt( 5 ) ) / 2; - const r = 1 / t; - - const vertices = [ - - // (±1, ±1, ±1) - - 1, - 1, - 1, - 1, - 1, 1, - - 1, 1, - 1, - 1, 1, 1, - 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, 1, 1, 1, - - // (0, ±1/φ, ±φ) - 0, - r, - t, 0, - r, t, - 0, r, - t, 0, r, t, - - // (±1/φ, ±φ, 0) - - r, - t, 0, - r, t, 0, - r, - t, 0, r, t, 0, - - // (±φ, 0, ±1/φ) - - t, 0, - r, t, 0, - r, - - t, 0, r, t, 0, r - ]; - - const indices = [ - 3, 11, 7, 3, 7, 15, 3, 15, 13, - 7, 19, 17, 7, 17, 6, 7, 6, 15, - 17, 4, 8, 17, 8, 10, 17, 10, 6, - 8, 0, 16, 8, 16, 2, 8, 2, 10, - 0, 12, 1, 0, 1, 18, 0, 18, 16, - 6, 10, 2, 6, 2, 13, 6, 13, 15, - 2, 16, 18, 2, 18, 3, 2, 3, 13, - 18, 1, 9, 18, 9, 11, 18, 11, 3, - 4, 14, 12, 4, 12, 0, 4, 0, 8, - 11, 9, 5, 11, 5, 19, 11, 19, 7, - 19, 5, 14, 19, 14, 4, 19, 4, 17, - 1, 12, 14, 1, 14, 5, 1, 5, 9 - ]; - - super( vertices, indices, radius, detail ); - - this.type = 'DodecahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - } - - // TubeGeometry - - class TubeGeometry extends Geometry { - - constructor( path, tubularSegments, radius, radialSegments, closed, taper ) { - - super(); - this.type = 'TubeGeometry'; - - this.parameters = { - path: path, - tubularSegments: tubularSegments, - radius: radius, - radialSegments: radialSegments, - closed: closed - }; - - if ( taper !== undefined ) console.warn( 'THREE.TubeGeometry: taper has been removed.' ); - - const bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ); - - // expose internals - - this.tangents = bufferGeometry.tangents; - this.normals = bufferGeometry.normals; - this.binormals = bufferGeometry.binormals; - - // create geometry - - this.fromBufferGeometry( bufferGeometry ); - this.mergeVertices(); - - } - - } - - - // TubeBufferGeometry - - class TubeBufferGeometry extends BufferGeometry { - - constructor( path, tubularSegments, radius, radialSegments, closed ) { - - super(); - this.type = 'TubeBufferGeometry'; - - this.parameters = { - path: path, - tubularSegments: tubularSegments, - radius: radius, - radialSegments: radialSegments, - closed: closed - }; - - tubularSegments = tubularSegments || 64; - radius = radius || 1; - radialSegments = radialSegments || 8; - closed = closed || false; - - const frames = path.computeFrenetFrames( tubularSegments, closed ); - - // expose internals - - this.tangents = frames.tangents; - this.normals = frames.normals; - this.binormals = frames.binormals; - - // helper variables - - const vertex = new Vector3(); - const normal = new Vector3(); - const uv = new Vector2(); - let P = new Vector3(); - - // buffer - - const vertices = []; - const normals = []; - const uvs = []; - const indices = []; - - // create buffer data - - generateBufferData(); - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - // functions - - function generateBufferData() { - - for ( let i = 0; i < tubularSegments; i ++ ) { - - generateSegment( i ); - - } - - // if the geometry is not closed, generate the last row of vertices and normals - // at the regular position on the given path - // - // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) - - generateSegment( ( closed === false ) ? tubularSegments : 0 ); - - // uvs are generated in a separate function. - // this makes it easy compute correct values for closed geometries - - generateUVs(); - - // finally create faces - - generateIndices(); - - } - - function generateSegment( i ) { - - // we use getPointAt to sample evenly distributed points from the given path - - P = path.getPointAt( i / tubularSegments, P ); - - // retrieve corresponding normal and binormal - - const N = frames.normals[ i ]; - const B = frames.binormals[ i ]; - - // generate normals and vertices for the current segment - - for ( let j = 0; j <= radialSegments; j ++ ) { - - const v = j / radialSegments * Math.PI * 2; - - const sin = Math.sin( v ); - const cos = - Math.cos( v ); - - // normal - - normal.x = ( cos * N.x + sin * B.x ); - normal.y = ( cos * N.y + sin * B.y ); - normal.z = ( cos * N.z + sin * B.z ); - normal.normalize(); - - normals.push( normal.x, normal.y, normal.z ); - - // vertex - - vertex.x = P.x + radius * normal.x; - vertex.y = P.y + radius * normal.y; - vertex.z = P.z + radius * normal.z; - - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } - - function generateIndices() { - - for ( let j = 1; j <= tubularSegments; j ++ ) { - - for ( let i = 1; i <= radialSegments; i ++ ) { - - const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); - const b = ( radialSegments + 1 ) * j + ( i - 1 ); - const c = ( radialSegments + 1 ) * j + i; - const d = ( radialSegments + 1 ) * ( j - 1 ) + i; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - } - - function generateUVs() { - - for ( let i = 0; i <= tubularSegments; i ++ ) { - - for ( let j = 0; j <= radialSegments; j ++ ) { - - uv.x = i / tubularSegments; - uv.y = j / radialSegments; - - uvs.push( uv.x, uv.y ); - - } - - } - - } - - } - toJSON() { - - const data = BufferGeometry.prototype.toJSON.call( this ); - - data.path = this.parameters.path.toJSON(); - - return data; - - } - - } - - // TorusKnotGeometry - - class TorusKnotGeometry extends Geometry { - - constructor( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { - - super(); - this.type = 'TorusKnotGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - tubularSegments: tubularSegments, - radialSegments: radialSegments, - p: p, - q: q - }; - - if ( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' ); - - this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); - this.mergeVertices(); - - } - - } - - - // TorusKnotBufferGeometry - - class TorusKnotBufferGeometry extends BufferGeometry { - - constructor( radius, tube, tubularSegments, radialSegments, p, q ) { - - super(); - this.type = 'TorusKnotBufferGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - tubularSegments: tubularSegments, - radialSegments: radialSegments, - p: p, - q: q - }; - - radius = radius || 1; - tube = tube || 0.4; - tubularSegments = Math.floor( tubularSegments ) || 64; - radialSegments = Math.floor( radialSegments ) || 8; - p = p || 2; - q = q || 3; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - const vertex = new Vector3(); - const normal = new Vector3(); - - const P1 = new Vector3(); - const P2 = new Vector3(); - - const B = new Vector3(); - const T = new Vector3(); - const N = new Vector3(); - - // generate vertices, normals and uvs - - for ( let i = 0; i <= tubularSegments; ++ i ) { - - // the radian "u" is used to calculate the position on the torus curve of the current tubular segement - - const u = i / tubularSegments * p * Math.PI * 2; - - // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. - // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions - - calculatePositionOnCurve( u, p, q, radius, P1 ); - calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); - - // calculate orthonormal basis - - T.subVectors( P2, P1 ); - N.addVectors( P2, P1 ); - B.crossVectors( T, N ); - N.crossVectors( B, T ); - - // normalize B, N. T can be ignored, we don't use it - - B.normalize(); - N.normalize(); - - for ( let j = 0; j <= radialSegments; ++ j ) { - - // now calculate the vertices. they are nothing more than an extrusion of the torus curve. - // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. - - const v = j / radialSegments * Math.PI * 2; - const cx = - tube * Math.cos( v ); - const cy = tube * Math.sin( v ); - - // now calculate the final vertex position. - // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve - - vertex.x = P1.x + ( cx * N.x + cy * B.x ); - vertex.y = P1.y + ( cx * N.y + cy * B.y ); - vertex.z = P1.z + ( cx * N.z + cy * B.z ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) - - normal.subVectors( vertex, P1 ).normalize(); - - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( i / tubularSegments ); - uvs.push( j / radialSegments ); - - } - - } - - // generate indices - - for ( let j = 1; j <= tubularSegments; j ++ ) { - - for ( let i = 1; i <= radialSegments; i ++ ) { - - // indices - - const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); - const b = ( radialSegments + 1 ) * j + ( i - 1 ); - const c = ( radialSegments + 1 ) * j + i; - const d = ( radialSegments + 1 ) * ( j - 1 ) + i; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - // this function calculates the current position on the torus curve - - function calculatePositionOnCurve( u, p, q, radius, position ) { - - const cu = Math.cos( u ); - const su = Math.sin( u ); - const quOverP = q / p * u; - const cs = Math.cos( quOverP ); - - position.x = radius * ( 2 + cs ) * 0.5 * cu; - position.y = radius * ( 2 + cs ) * su * 0.5; - position.z = radius * Math.sin( quOverP ) * 0.5; - - } - - } - - } - - // TorusGeometry - - class TorusGeometry extends Geometry { - - constructor( radius, tube, radialSegments, tubularSegments, arc ) { - - super(); - this.type = 'TorusGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; - - this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); - this.mergeVertices(); - - } - - } - - - // TorusBufferGeometry - - class TorusBufferGeometry extends BufferGeometry { - - constructor( radius, tube, radialSegments, tubularSegments, arc ) { - - super(); - this.type = 'TorusBufferGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; - - radius = radius || 1; - tube = tube || 0.4; - radialSegments = Math.floor( radialSegments ) || 8; - tubularSegments = Math.floor( tubularSegments ) || 6; - arc = arc || Math.PI * 2; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - const center = new Vector3(); - const vertex = new Vector3(); - const normal = new Vector3(); - - // generate vertices, normals and uvs - - for ( let j = 0; j <= radialSegments; j ++ ) { - - for ( let i = 0; i <= tubularSegments; i ++ ) { - - const u = i / tubularSegments * arc; - const v = j / radialSegments * Math.PI * 2; - - // vertex - - vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); - vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); - vertex.z = tube * Math.sin( v ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - center.x = radius * Math.cos( u ); - center.y = radius * Math.sin( u ); - normal.subVectors( vertex, center ).normalize(); - - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( i / tubularSegments ); - uvs.push( j / radialSegments ); - - } - - } - - // generate indices - - for ( let j = 1; j <= radialSegments; j ++ ) { - - for ( let i = 1; i <= tubularSegments; i ++ ) { - - // indices - - const a = ( tubularSegments + 1 ) * j + i - 1; - const b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; - const c = ( tubularSegments + 1 ) * ( j - 1 ) + i; - const d = ( tubularSegments + 1 ) * j + i; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - } - - /** - * Port from https://github.com/mapbox/earcut (v2.2.2) - */ - - const Earcut = { - - triangulate: function ( data, holeIndices, dim ) { - - dim = dim || 2; - - const hasHoles = holeIndices && holeIndices.length; - const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length; - let outerNode = linkedList( data, 0, outerLen, dim, true ); - const triangles = []; - - if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; - - let minX, minY, maxX, maxY, x, y, invSize; - - if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); - - // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox - if ( data.length > 80 * dim ) { - - minX = maxX = data[ 0 ]; - minY = maxY = data[ 1 ]; - - for ( let i = dim; i < outerLen; i += dim ) { - - x = data[ i ]; - y = data[ i + 1 ]; - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - - } - - // minX, minY and invSize are later used to transform coords into integers for z-order calculation - invSize = Math.max( maxX - minX, maxY - minY ); - invSize = invSize !== 0 ? 1 / invSize : 0; - - } - - earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); - - return triangles; - - } - - }; - - // create a circular doubly linked list from polygon points in the specified winding order - function linkedList( data, start, end, dim, clockwise ) { - - let i, last; - - if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { - - for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); - - } else { - - for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); - - } - - if ( last && equals( last, last.next ) ) { - - removeNode( last ); - last = last.next; - - } - - return last; - - } - - // eliminate colinear or duplicate points - function filterPoints( start, end ) { - - if ( ! start ) return start; - if ( ! end ) end = start; - - let p = start, - again; - do { - - again = false; - - if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { - - removeNode( p ); - p = end = p.prev; - if ( p === p.next ) break; - again = true; - - } else { - - p = p.next; - - } - - } while ( again || p !== end ); - - return end; - - } - - // main ear slicing loop which triangulates a polygon (given as a linked list) - function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { - - if ( ! ear ) return; - - // interlink polygon nodes in z-order - if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); - - let stop = ear, - prev, next; - - // iterate through ears, slicing them one by one - while ( ear.prev !== ear.next ) { - - prev = ear.prev; - next = ear.next; - - if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { - - // cut off the triangle - triangles.push( prev.i / dim ); - triangles.push( ear.i / dim ); - triangles.push( next.i / dim ); - - removeNode( ear ); - - // skipping the next vertex leads to less sliver triangles - ear = next.next; - stop = next.next; - - continue; - - } - - ear = next; - - // if we looped through the whole remaining polygon and can't find any more ears - if ( ear === stop ) { - - // try filtering points and slicing again - if ( ! pass ) { - - earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); - - // if this didn't work, try curing all small self-intersections locally - - } else if ( pass === 1 ) { - - ear = cureLocalIntersections( filterPoints( ear ), triangles, dim ); - earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); - - // as a last resort, try splitting the remaining polygon into two - - } else if ( pass === 2 ) { - - splitEarcut( ear, triangles, dim, minX, minY, invSize ); - - } - - break; - - } - - } - - } - - // check whether a polygon node forms a valid ear with adjacent nodes - function isEar( ear ) { - - const a = ear.prev, - b = ear, - c = ear.next; - - if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear - - // now make sure we don't have other points inside the potential ear - let p = ear.next.next; - - while ( p !== ear.prev ) { - - if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.next; - - } - - return true; - - } - - function isEarHashed( ear, minX, minY, invSize ) { - - const a = ear.prev, - b = ear, - c = ear.next; - - if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear - - // triangle bbox; min & max are calculated like this for speed - const minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), - minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), - maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), - maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); - - // z-order range for the current triangle bbox; - const minZ = zOrder( minTX, minTY, minX, minY, invSize ), - maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); - - let p = ear.prevZ, - n = ear.nextZ; - - // look for points inside the triangle in both directions - while ( p && p.z >= minZ && n && n.z <= maxZ ) { - - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; - - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; - - } - - // look for remaining points in decreasing z-order - while ( p && p.z >= minZ ) { - - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; - - } - - // look for remaining points in increasing z-order - while ( n && n.z <= maxZ ) { - - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; - - } - - return true; - - } - - // go through all polygon nodes and cure small local self-intersections - function cureLocalIntersections( start, triangles, dim ) { - - let p = start; - do { - - const a = p.prev, - b = p.next.next; - - if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { - - triangles.push( a.i / dim ); - triangles.push( p.i / dim ); - triangles.push( b.i / dim ); - - // remove two nodes involved - removeNode( p ); - removeNode( p.next ); - - p = start = b; - - } - - p = p.next; - - } while ( p !== start ); - - return filterPoints( p ); - - } - - // try splitting polygon into two and triangulate them independently - function splitEarcut( start, triangles, dim, minX, minY, invSize ) { - - // look for a valid diagonal that divides the polygon into two - let a = start; - do { - - let b = a.next.next; - while ( b !== a.prev ) { - - if ( a.i !== b.i && isValidDiagonal( a, b ) ) { - - // split the polygon in two by the diagonal - let c = splitPolygon( a, b ); - - // filter colinear points around the cuts - a = filterPoints( a, a.next ); - c = filterPoints( c, c.next ); - - // run earcut on each half - earcutLinked( a, triangles, dim, minX, minY, invSize ); - earcutLinked( c, triangles, dim, minX, minY, invSize ); - return; - - } - - b = b.next; - - } - - a = a.next; - - } while ( a !== start ); - - } - - // link every hole into the outer loop, producing a single-ring polygon without holes - function eliminateHoles( data, holeIndices, outerNode, dim ) { - - const queue = []; - let i, len, start, end, list; - - for ( i = 0, len = holeIndices.length; i < len; i ++ ) { - - start = holeIndices[ i ] * dim; - end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; - list = linkedList( data, start, end, dim, false ); - if ( list === list.next ) list.steiner = true; - queue.push( getLeftmost( list ) ); - - } - - queue.sort( compareX ); - - // process holes from left to right - for ( i = 0; i < queue.length; i ++ ) { - - eliminateHole( queue[ i ], outerNode ); - outerNode = filterPoints( outerNode, outerNode.next ); - - } - - return outerNode; - - } - - function compareX( a, b ) { - - return a.x - b.x; - - } - - // find a bridge between vertices that connects hole with an outer ring and and link it - function eliminateHole( hole, outerNode ) { - - outerNode = findHoleBridge( hole, outerNode ); - if ( outerNode ) { - - const b = splitPolygon( outerNode, hole ); - - // filter collinear points around the cuts - filterPoints( outerNode, outerNode.next ); - filterPoints( b, b.next ); - - } - - } - - // David Eberly's algorithm for finding a bridge between hole and outer polygon - function findHoleBridge( hole, outerNode ) { - - let p = outerNode; - const hx = hole.x; - const hy = hole.y; - let qx = - Infinity, m; - - // find a segment intersected by a ray from the hole's leftmost point to the left; - // segment's endpoint with lesser x will be potential connection point - do { - - if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { - - const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); - if ( x <= hx && x > qx ) { - - qx = x; - if ( x === hx ) { - - if ( hy === p.y ) return p; - if ( hy === p.next.y ) return p.next; - - } - - m = p.x < p.next.x ? p : p.next; - - } - - } - - p = p.next; - - } while ( p !== outerNode ); - - if ( ! m ) return null; - - if ( hx === qx ) return m; // hole touches outer segment; pick leftmost endpoint - - // look for points inside the triangle of hole point, segment intersection and endpoint; - // if there are no points found, we have a valid connection; - // otherwise choose the point of the minimum angle with the ray as connection point - - const stop = m, - mx = m.x, - my = m.y; - let tanMin = Infinity, tan; - - p = m; - - do { - - if ( hx >= p.x && p.x >= mx && hx !== p.x && - pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { - - tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential - - if ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) { - - m = p; - tanMin = tan; - - } - - } - - p = p.next; - - } while ( p !== stop ); - - return m; - - } - - // whether sector in vertex m contains sector in vertex p in the same coordinates - function sectorContainsSector( m, p ) { - - return area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0; - - } - - // interlink polygon nodes in z-order - function indexCurve( start, minX, minY, invSize ) { - - let p = start; - do { - - if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); - p.prevZ = p.prev; - p.nextZ = p.next; - p = p.next; - - } while ( p !== start ); - - p.prevZ.nextZ = null; - p.prevZ = null; - - sortLinked( p ); - - } - - // Simon Tatham's linked list merge sort algorithm - // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html - function sortLinked( list ) { - - let i, p, q, e, tail, numMerges, pSize, qSize, - inSize = 1; - - do { - - p = list; - list = null; - tail = null; - numMerges = 0; - - while ( p ) { - - numMerges ++; - q = p; - pSize = 0; - for ( i = 0; i < inSize; i ++ ) { - - pSize ++; - q = q.nextZ; - if ( ! q ) break; - - } - - qSize = inSize; - - while ( pSize > 0 || ( qSize > 0 && q ) ) { - - if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { - - e = p; - p = p.nextZ; - pSize --; - - } else { - - e = q; - q = q.nextZ; - qSize --; - - } - - if ( tail ) tail.nextZ = e; - else list = e; - - e.prevZ = tail; - tail = e; - - } - - p = q; - - } - - tail.nextZ = null; - inSize *= 2; - - } while ( numMerges > 1 ); - - return list; - - } - - // z-order of a point given coords and inverse of the longer side of data bbox - function zOrder( x, y, minX, minY, invSize ) { - - // coords are transformed into non-negative 15-bit integer range - x = 32767 * ( x - minX ) * invSize; - y = 32767 * ( y - minY ) * invSize; - - x = ( x | ( x << 8 ) ) & 0x00FF00FF; - x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; - x = ( x | ( x << 2 ) ) & 0x33333333; - x = ( x | ( x << 1 ) ) & 0x55555555; - - y = ( y | ( y << 8 ) ) & 0x00FF00FF; - y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; - y = ( y | ( y << 2 ) ) & 0x33333333; - y = ( y | ( y << 1 ) ) & 0x55555555; - - return x | ( y << 1 ); - - } - - // find the leftmost node of a polygon ring - function getLeftmost( start ) { - - let p = start, - leftmost = start; - do { - - if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; - p = p.next; - - } while ( p !== start ); - - return leftmost; - - } - - // check if a point lies within a convex triangle - function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { - - return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && - ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && - ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; - - } - - // check if a diagonal between two polygon nodes is valid (lies in polygon interior) - function isValidDiagonal( a, b ) { - - return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // dones't intersect other edges - ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible - ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors - equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case - - } - - // signed area of a triangle - function area( p, q, r ) { - - return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); - - } - - // check if two points are equal - function equals( p1, p2 ) { - - return p1.x === p2.x && p1.y === p2.y; - - } - - // check if two segments intersect - function intersects( p1, q1, p2, q2 ) { - - const o1 = sign( area( p1, q1, p2 ) ); - const o2 = sign( area( p1, q1, q2 ) ); - const o3 = sign( area( p2, q2, p1 ) ); - const o4 = sign( area( p2, q2, q1 ) ); - - if ( o1 !== o2 && o3 !== o4 ) return true; // general case - - if ( o1 === 0 && onSegment( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 - if ( o2 === 0 && onSegment( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 - if ( o3 === 0 && onSegment( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 - if ( o4 === 0 && onSegment( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 - - return false; - - } - - // for collinear points p, q, r, check if point q lies on segment pr - function onSegment( p, q, r ) { - - return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); - - } - - function sign( num ) { - - return num > 0 ? 1 : num < 0 ? - 1 : 0; - - } - - // check if a polygon diagonal intersects any polygon segments - function intersectsPolygon( a, b ) { - - let p = a; - do { - - if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && - intersects( p, p.next, a, b ) ) return true; - p = p.next; - - } while ( p !== a ); - - return false; - - } - - // check if a polygon diagonal is locally inside the polygon - function locallyInside( a, b ) { - - return area( a.prev, a, a.next ) < 0 ? - area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : - area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; - - } - - // check if the middle point of a polygon diagonal is inside the polygon - function middleInside( a, b ) { - - let p = a, - inside = false; - const px = ( a.x + b.x ) / 2, - py = ( a.y + b.y ) / 2; - do { - - if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && - ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) - inside = ! inside; - p = p.next; - - } while ( p !== a ); - - return inside; - - } - - // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; - // if one belongs to the outer ring and another to a hole, it merges it into a single ring - function splitPolygon( a, b ) { - - const a2 = new Node( a.i, a.x, a.y ), - b2 = new Node( b.i, b.x, b.y ), - an = a.next, - bp = b.prev; - - a.next = b; - b.prev = a; - - a2.next = an; - an.prev = a2; - - b2.next = a2; - a2.prev = b2; - - bp.next = b2; - b2.prev = bp; - - return b2; - - } - - // create a node and optionally link it with previous one (in a circular doubly linked list) - function insertNode( i, x, y, last ) { - - const p = new Node( i, x, y ); - - if ( ! last ) { - - p.prev = p; - p.next = p; - - } else { - - p.next = last.next; - p.prev = last; - last.next.prev = p; - last.next = p; - - } - - return p; - - } - - function removeNode( p ) { - - p.next.prev = p.prev; - p.prev.next = p.next; - - if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; - if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; - - } - - function Node( i, x, y ) { - - // vertex index in coordinates array - this.i = i; - - // vertex coordinates - this.x = x; - this.y = y; - - // previous and next vertex nodes in a polygon ring - this.prev = null; - this.next = null; - - // z-order curve value - this.z = null; - - // previous and next nodes in z-order - this.prevZ = null; - this.nextZ = null; - - // indicates whether this is a steiner point - this.steiner = false; - - } - - function signedArea( data, start, end, dim ) { - - let sum = 0; - for ( let i = start, j = end - dim; i < end; i += dim ) { - - sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); - j = i; - - } - - return sum; - - } - - const ShapeUtils = { - - // calculate area of the contour polygon - - area: function ( contour ) { - - const n = contour.length; - let a = 0.0; - - for ( let p = n - 1, q = 0; q < n; p = q ++ ) { - - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; - - } - - return a * 0.5; - - }, - - isClockWise: function ( pts ) { - - return ShapeUtils.area( pts ) < 0; - - }, - - triangulateShape: function ( contour, holes ) { - - const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] - const holeIndices = []; // array of hole indices - const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] - - removeDupEndPts( contour ); - addContour( vertices, contour ); - - // - - let holeIndex = contour.length; - - holes.forEach( removeDupEndPts ); - - for ( let i = 0; i < holes.length; i ++ ) { - - holeIndices.push( holeIndex ); - holeIndex += holes[ i ].length; - addContour( vertices, holes[ i ] ); - - } - - // - - const triangles = Earcut.triangulate( vertices, holeIndices ); - - // - - for ( let i = 0; i < triangles.length; i += 3 ) { - - faces.push( triangles.slice( i, i + 3 ) ); - - } - - return faces; - - } - - }; - - function removeDupEndPts( points ) { - - const l = points.length; - - if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { - - points.pop(); - - } - - } - - function addContour( vertices, contour ) { - - for ( let i = 0; i < contour.length; i ++ ) { - - vertices.push( contour[ i ].x ); - vertices.push( contour[ i ].y ); - - } - - } - - /** - * Creates extruded geometry from a path shape. - * - * parameters = { - * - * curveSegments: , // number of points on the curves - * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too - * depth: , // Depth to extrude the shape - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into the original shape bevel goes - * bevelSize: , // how far from shape outline (including bevelOffset) is bevel - * bevelOffset: , // how far from shape outline does bevel start - * bevelSegments: , // number of bevel layers - * - * extrudePath: // curve to extrude shape along - * - * UVGenerator: // object that provides UV generator functions - * - * } - */ - - // ExtrudeGeometry - - class ExtrudeGeometry extends Geometry { - - constructor( shapes, options ) { - - super(); - - this.type = 'ExtrudeGeometry'; - - this.parameters = { - shapes: shapes, - options: options - }; - - this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) ); - this.mergeVertices(); - - } - - toJSON() { - - const data = super.toJSON(); - - const shapes = this.parameters.shapes; - const options = this.parameters.options; - - return toJSON( shapes, options, data ); - - } - - } - - - // ExtrudeBufferGeometry - - class ExtrudeBufferGeometry extends BufferGeometry { - - constructor( shapes, options ) { - - super(); - - this.type = 'ExtrudeBufferGeometry'; - - this.parameters = { - shapes: shapes, - options: options - }; - - shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; - - const scope = this; - - const verticesArray = []; - const uvArray = []; - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - addShape( shape ); - - } - - // build geometry - - this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); - - this.computeVertexNormals(); - - // functions - - function addShape( shape ) { - - const placeholder = []; - - // options - - const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - const steps = options.steps !== undefined ? options.steps : 1; - let depth = options.depth !== undefined ? options.depth : 100; - - let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; - let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; - let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; - let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; - let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; - - const extrudePath = options.extrudePath; - - const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; - - // deprecated options - - if ( options.amount !== undefined ) { - - console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' ); - depth = options.amount; - - } - - // - - let extrudePts, extrudeByPath = false; - let splineTube, binormal, normal, position2; - - if ( extrudePath ) { - - extrudePts = extrudePath.getSpacedPoints( steps ); - - extrudeByPath = true; - bevelEnabled = false; // bevels not supported for path extrusion - - // SETUP TNB variables - - // TODO1 - have a .isClosed in spline? - - splineTube = extrudePath.computeFrenetFrames( steps, false ); - - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); - - binormal = new Vector3(); - normal = new Vector3(); - position2 = new Vector3(); - - } - - // Safeguards if bevels are not enabled - - if ( ! bevelEnabled ) { - - bevelSegments = 0; - bevelThickness = 0; - bevelSize = 0; - bevelOffset = 0; - - } - - // Variables initialization - - const shapePoints = shape.extractPoints( curveSegments ); - - let vertices = shapePoints.shape; - const holes = shapePoints.holes; - - const reverse = ! ShapeUtils.isClockWise( vertices ); - - if ( reverse ) { - - vertices = vertices.reverse(); - - // Maybe we should also check if holes are in the opposite direction, just to be safe ... - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - - if ( ShapeUtils.isClockWise( ahole ) ) { - - holes[ h ] = ahole.reverse(); - - } - - } - - } - - - const faces = ShapeUtils.triangulateShape( vertices, holes ); - - /* Vertices */ - - const contour = vertices; // vertices has all points but contour has only points of circumference - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - - vertices = vertices.concat( ahole ); - - } - - - function scalePt2( pt, vec, size ) { - - if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); - - return vec.clone().multiplyScalar( size ).add( pt ); - - } - - const vlen = vertices.length, flen = faces.length; - - - // Find directions for point movement - - - function getBevelVec( inPt, inPrev, inNext ) { - - // computes for inPt the corresponding point inPt' on a new contour - // shifted by 1 unit (length of normalized vector) to the left - // if we walk along contour clockwise, this new contour is outside the old one - // - // inPt' is the intersection of the two lines parallel to the two - // adjacent edges of inPt at a distance of 1 unit on the left side. - - let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt - - // good reading for geometry algorithms (here: line-line intersection) - // http://geomalgorithms.com/a05-_intersect-1.html - - const v_prev_x = inPt.x - inPrev.x, - v_prev_y = inPt.y - inPrev.y; - const v_next_x = inNext.x - inPt.x, - v_next_y = inNext.y - inPt.y; - - const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); - - // check for collinear edges - const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - if ( Math.abs( collinear0 ) > Number.EPSILON ) { - - // not collinear - - // length of vectors for normalizing - - const v_prev_len = Math.sqrt( v_prev_lensq ); - const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); - - // shift adjacent points by unit vectors to the left - - const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); - const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); - - const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); - const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); - - // scaling factor for v_prev to intersection point - - const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / - ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - // vector from inPt to intersection point - - v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); - v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); - - // Don't normalize!, otherwise sharp corners become ugly - // but prevent crazy spikes - const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); - if ( v_trans_lensq <= 2 ) { - - return new Vector2( v_trans_x, v_trans_y ); - - } else { - - shrink_by = Math.sqrt( v_trans_lensq / 2 ); - - } - - } else { - - // handle special case of collinear edges - - let direction_eq = false; // assumes: opposite - - if ( v_prev_x > Number.EPSILON ) { - - if ( v_next_x > Number.EPSILON ) { - - direction_eq = true; - - } - - } else { - - if ( v_prev_x < - Number.EPSILON ) { - - if ( v_next_x < - Number.EPSILON ) { - - direction_eq = true; - - } - - } else { - - if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { - - direction_eq = true; - - } - - } - - } - - if ( direction_eq ) { - - // console.log("Warning: lines are a straight sequence"); - v_trans_x = - v_prev_y; - v_trans_y = v_prev_x; - shrink_by = Math.sqrt( v_prev_lensq ); - - } else { - - // console.log("Warning: lines are a straight spike"); - v_trans_x = v_prev_x; - v_trans_y = v_prev_y; - shrink_by = Math.sqrt( v_prev_lensq / 2 ); - - } - - } - - return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); - - } - - - const contourMovements = []; - - for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - - if ( j === il ) j = 0; - if ( k === il ) k = 0; - - // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) - - contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); - - } - - const holesMovements = []; - let oneHoleMovements, verticesMovements = contourMovements.concat(); - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - - oneHoleMovements = []; - - for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - - if ( j === il ) j = 0; - if ( k === il ) k = 0; - - // (j)---(i)---(k) - oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); - - } - - holesMovements.push( oneHoleMovements ); - verticesMovements = verticesMovements.concat( oneHoleMovements ); - - } - - - // Loop bevelSegments, 1 for the front, 1 for the back - - for ( let b = 0; b < bevelSegments; b ++ ) { - - //for ( b = bevelSegments; b > 0; b -- ) { - - const t = b / bevelSegments; - const z = bevelThickness * Math.cos( t * Math.PI / 2 ); - const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - - // contract shape - - for ( let i = 0, il = contour.length; i < il; i ++ ) { - - const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - - v( vert.x, vert.y, - z ); - - } - - // expand holes - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; - - for ( let i = 0, il = ahole.length; i < il; i ++ ) { - - const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - - v( vert.x, vert.y, - z ); - - } - - } - - } - - const bs = bevelSize + bevelOffset; - - // Back facing vertices - - for ( let i = 0; i < vlen; i ++ ) { - - const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, 0 ); - - } else { - - // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); - - normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); - - position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); - - v( position2.x, position2.y, position2.z ); - - } - - } - - // Add stepped vertices... - // Including front facing vertices - - for ( let s = 1; s <= steps; s ++ ) { - - for ( let i = 0; i < vlen; i ++ ) { - - const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, depth / steps * s ); - - } else { - - // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); - - normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); - - position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); - - v( position2.x, position2.y, position2.z ); - - } - - } - - } - - - // Add bevel segments planes - - //for ( b = 1; b <= bevelSegments; b ++ ) { - for ( let b = bevelSegments - 1; b >= 0; b -- ) { - - const t = b / bevelSegments; - const z = bevelThickness * Math.cos( t * Math.PI / 2 ); - const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - - // contract shape - - for ( let i = 0, il = contour.length; i < il; i ++ ) { - - const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, depth + z ); - - } - - // expand holes - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; - - for ( let i = 0, il = ahole.length; i < il; i ++ ) { - - const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, depth + z ); - - } else { - - v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); - - } - - } - - } - - } - - /* Faces */ - - // Top and bottom faces - - buildLidFaces(); - - // Sides faces - - buildSideFaces(); - - - ///// Internal functions - - function buildLidFaces() { - - const start = verticesArray.length / 3; - - if ( bevelEnabled ) { - - let layer = 0; // steps + 1 - let offset = vlen * layer; - - // Bottom faces - - for ( let i = 0; i < flen; i ++ ) { - - const face = faces[ i ]; - f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); - - } - - layer = steps + bevelSegments * 2; - offset = vlen * layer; - - // Top faces - - for ( let i = 0; i < flen; i ++ ) { - - const face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); - - } - - } else { - - // Bottom faces - - for ( let i = 0; i < flen; i ++ ) { - - const face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ] ); - - } - - // Top faces - - for ( let i = 0; i < flen; i ++ ) { - - const face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); - - } - - } - - scope.addGroup( start, verticesArray.length / 3 - start, 0 ); - - } - - // Create faces for the z-sides of the shape - - function buildSideFaces() { - - const start = verticesArray.length / 3; - let layeroffset = 0; - sidewalls( contour, layeroffset ); - layeroffset += contour.length; - - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - - const ahole = holes[ h ]; - sidewalls( ahole, layeroffset ); - - //, true - layeroffset += ahole.length; - - } - - - scope.addGroup( start, verticesArray.length / 3 - start, 1 ); - - - } - - function sidewalls( contour, layeroffset ) { - - let i = contour.length; - - while ( -- i >= 0 ) { - - const j = i; - let k = i - 1; - if ( k < 0 ) k = contour.length - 1; - - //console.log('b', i,j, i-1, k,vertices.length); - - for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { - - const slen1 = vlen * s; - const slen2 = vlen * ( s + 1 ); - - const a = layeroffset + j + slen1, - b = layeroffset + k + slen1, - c = layeroffset + k + slen2, - d = layeroffset + j + slen2; - - f4( a, b, c, d ); - - } - - } - - } - - function v( x, y, z ) { - - placeholder.push( x ); - placeholder.push( y ); - placeholder.push( z ); - - } - - - function f3( a, b, c ) { - - addVertex( a ); - addVertex( b ); - addVertex( c ); - - const nextIndex = verticesArray.length / 3; - const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); - - } - - function f4( a, b, c, d ) { - - addVertex( a ); - addVertex( b ); - addVertex( d ); - - addVertex( b ); - addVertex( c ); - addVertex( d ); - - - const nextIndex = verticesArray.length / 3; - const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 3 ] ); - - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); - addUV( uvs[ 3 ] ); - - } - - function addVertex( index ) { - - verticesArray.push( placeholder[ index * 3 + 0 ] ); - verticesArray.push( placeholder[ index * 3 + 1 ] ); - verticesArray.push( placeholder[ index * 3 + 2 ] ); - - } - - - function addUV( vector2 ) { - - uvArray.push( vector2.x ); - uvArray.push( vector2.y ); - - } - - } - - } - - toJSON() { - - const data = BufferGeometry.prototype.toJSON.call( this ); - - const shapes = this.parameters.shapes; - const options = this.parameters.options; - - return toJSON( shapes, options, data ); - - } - - } - - const WorldUVGenerator = { - - generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { - - const a_x = vertices[ indexA * 3 ]; - const a_y = vertices[ indexA * 3 + 1 ]; - const b_x = vertices[ indexB * 3 ]; - const b_y = vertices[ indexB * 3 + 1 ]; - const c_x = vertices[ indexC * 3 ]; - const c_y = vertices[ indexC * 3 + 1 ]; - - return [ - new Vector2( a_x, a_y ), - new Vector2( b_x, b_y ), - new Vector2( c_x, c_y ) - ]; - - }, - - generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { - - const a_x = vertices[ indexA * 3 ]; - const a_y = vertices[ indexA * 3 + 1 ]; - const a_z = vertices[ indexA * 3 + 2 ]; - const b_x = vertices[ indexB * 3 ]; - const b_y = vertices[ indexB * 3 + 1 ]; - const b_z = vertices[ indexB * 3 + 2 ]; - const c_x = vertices[ indexC * 3 ]; - const c_y = vertices[ indexC * 3 + 1 ]; - const c_z = vertices[ indexC * 3 + 2 ]; - const d_x = vertices[ indexD * 3 ]; - const d_y = vertices[ indexD * 3 + 1 ]; - const d_z = vertices[ indexD * 3 + 2 ]; - - if ( Math.abs( a_y - b_y ) < 0.01 ) { - - return [ - new Vector2( a_x, 1 - a_z ), - new Vector2( b_x, 1 - b_z ), - new Vector2( c_x, 1 - c_z ), - new Vector2( d_x, 1 - d_z ) - ]; - - } else { - - return [ - new Vector2( a_y, 1 - a_z ), - new Vector2( b_y, 1 - b_z ), - new Vector2( c_y, 1 - c_z ), - new Vector2( d_y, 1 - d_z ) - ]; - - } - - } - }; - - function toJSON( shapes, options, data ) { - - data.shapes = []; - - if ( Array.isArray( shapes ) ) { - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - - data.shapes.push( shape.uuid ); - - } - - } else { - - data.shapes.push( shapes.uuid ); - - } - - if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); - - return data; - - } - - /** - * Text = 3D Text - * - * parameters = { - * font: , // font - * - * size: , // size of the text - * height: , // thickness to extrude text - * curveSegments: , // number of points on the curves - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into text bevel goes - * bevelSize: , // how far from text outline (including bevelOffset) is bevel - * bevelOffset: // how far from text outline does bevel start - * } - */ - - // TextGeometry - - class TextGeometry extends Geometry { - - constructor( text, parameters ) { - - super(); - this.type = 'TextGeometry'; - - this.parameters = { - text: text, - parameters: parameters - }; - - this.fromBufferGeometry( new TextBufferGeometry( text, parameters ) ); - this.mergeVertices(); - - } - - } - - - // TextBufferGeometry - - class TextBufferGeometry extends ExtrudeBufferGeometry { - - constructor( text, parameters ) { - - parameters = parameters || {}; - - const font = parameters.font; - - if ( ! ( font && font.isFont ) ) { - - console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' ); - return new Geometry(); - - } - - const shapes = font.generateShapes( text, parameters.size ); - - // translate parameters to ExtrudeGeometry API - - parameters.depth = parameters.height !== undefined ? parameters.height : 50; - - // defaults - - if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; - if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; - if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; - - super( shapes, parameters ); - - this.type = 'TextBufferGeometry'; - - } - - } - - // SphereGeometry - - class SphereGeometry extends Geometry { - - constructor( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - - super(); - this.type = 'SphereGeometry'; - - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - } - - // SphereBufferGeometry - - class SphereBufferGeometry extends BufferGeometry { - - constructor( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - - super(); - this.type = 'SphereBufferGeometry'; - - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - radius = radius || 1; - - widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); - heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); - - phiStart = phiStart !== undefined ? phiStart : 0; - phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; - - const thetaEnd = Math.min( thetaStart + thetaLength, Math.PI ); - - let index = 0; - const grid = []; - - const vertex = new Vector3(); - const normal = new Vector3(); - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // generate vertices, normals and uvs - - for ( let iy = 0; iy <= heightSegments; iy ++ ) { - - const verticesRow = []; - - const v = iy / heightSegments; - - // special case for the poles - - let uOffset = 0; - - if ( iy == 0 && thetaStart == 0 ) { - - uOffset = 0.5 / widthSegments; - - } else if ( iy == heightSegments && thetaEnd == Math.PI ) { - - uOffset = - 0.5 / widthSegments; - - } - - for ( let ix = 0; ix <= widthSegments; ix ++ ) { - - const u = ix / widthSegments; - - // vertex - - vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); - vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normal.copy( vertex ).normalize(); - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( u + uOffset, 1 - v ); - - verticesRow.push( index ++ ); - - } - - grid.push( verticesRow ); - - } - - // indices - - for ( let iy = 0; iy < heightSegments; iy ++ ) { - - for ( let ix = 0; ix < widthSegments; ix ++ ) { - - const a = grid[ iy ][ ix + 1 ]; - const b = grid[ iy ][ ix ]; - const c = grid[ iy + 1 ][ ix ]; - const d = grid[ iy + 1 ][ ix + 1 ]; - - if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); - if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - } - - // RingGeometry - - class RingGeometry extends Geometry { - - constructor( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { - - super(); - - this.type = 'RingGeometry'; - - this.parameters = { - innerRadius: innerRadius, - outerRadius: outerRadius, - thetaSegments: thetaSegments, - phiSegments: phiSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - } - - // RingBufferGeometry - - class RingBufferGeometry extends BufferGeometry { - - constructor( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { - - super(); - - this.type = 'RingBufferGeometry'; - - this.parameters = { - innerRadius: innerRadius, - outerRadius: outerRadius, - thetaSegments: thetaSegments, - phiSegments: phiSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - innerRadius = innerRadius || 0.5; - outerRadius = outerRadius || 1; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - - thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; - phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // some helper variables - - let radius = innerRadius; - const radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); - const vertex = new Vector3(); - const uv = new Vector2(); - - // generate vertices, normals and uvs - - for ( let j = 0; j <= phiSegments; j ++ ) { - - for ( let i = 0; i <= thetaSegments; i ++ ) { - - // values are generate from the inside of the ring to the outside - - const segment = thetaStart + i / thetaSegments * thetaLength; - - // vertex - - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normals.push( 0, 0, 1 ); - - // uv - - uv.x = ( vertex.x / outerRadius + 1 ) / 2; - uv.y = ( vertex.y / outerRadius + 1 ) / 2; - - uvs.push( uv.x, uv.y ); - - } - - // increase the radius for next row of vertices - - radius += radiusStep; - - } - - // indices - - for ( let j = 0; j < phiSegments; j ++ ) { - - const thetaSegmentLevel = j * ( thetaSegments + 1 ); - - for ( let i = 0; i < thetaSegments; i ++ ) { - - const segment = i + thetaSegmentLevel; - - const a = segment; - const b = segment + thetaSegments + 1; - const c = segment + thetaSegments + 2; - const d = segment + 1; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - } - - // LatheGeometry - - class LatheGeometry extends Geometry { - - constructor( points, segments, phiStart, phiLength ) { - - super(); - - this.type = 'LatheGeometry'; - - this.parameters = { - points: points, - segments: segments, - phiStart: phiStart, - phiLength: phiLength - }; - - this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) ); - this.mergeVertices(); - - } - - } - - // LatheBufferGeometry - - class LatheBufferGeometry extends BufferGeometry { - - constructor( points, segments, phiStart, phiLength ) { - - super(); - - this.type = 'LatheBufferGeometry'; - - this.parameters = { - points: points, - segments: segments, - phiStart: phiStart, - phiLength: phiLength - }; - - segments = Math.floor( segments ) || 12; - phiStart = phiStart || 0; - phiLength = phiLength || Math.PI * 2; - - // clamp phiLength so it's in range of [ 0, 2PI ] - - phiLength = MathUtils.clamp( phiLength, 0, Math.PI * 2 ); - - - // buffers - - const indices = []; - const vertices = []; - const uvs = []; - - // helper variables - - const inverseSegments = 1.0 / segments; - const vertex = new Vector3(); - const uv = new Vector2(); - - // generate vertices and uvs - - for ( let i = 0; i <= segments; i ++ ) { - - const phi = phiStart + i * inverseSegments * phiLength; - - const sin = Math.sin( phi ); - const cos = Math.cos( phi ); - - for ( let j = 0; j <= ( points.length - 1 ); j ++ ) { - - // vertex - - vertex.x = points[ j ].x * sin; - vertex.y = points[ j ].y; - vertex.z = points[ j ].x * cos; - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // uv - - uv.x = i / segments; - uv.y = j / ( points.length - 1 ); - - uvs.push( uv.x, uv.y ); - - - } - - } - - // indices - - for ( let i = 0; i < segments; i ++ ) { - - for ( let j = 0; j < ( points.length - 1 ); j ++ ) { - - const base = j + i * points.length; - - const a = base; - const b = base + points.length; - const c = base + points.length + 1; - const d = base + 1; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - // generate normals - - this.computeVertexNormals(); - - // if the geometry is closed, we need to average the normals along the seam. - // because the corresponding vertices are identical (but still have different UVs). - - if ( phiLength === Math.PI * 2 ) { - - const normals = this.attributes.normal.array; - const n1 = new Vector3(); - const n2 = new Vector3(); - const n = new Vector3(); - - // this is the buffer offset for the last line of vertices - - const base = segments * points.length * 3; - - for ( let i = 0, j = 0; i < points.length; i ++, j += 3 ) { - - // select the normal of the vertex in the first line - - n1.x = normals[ j + 0 ]; - n1.y = normals[ j + 1 ]; - n1.z = normals[ j + 2 ]; - - // select the normal of the vertex in the last line - - n2.x = normals[ base + j + 0 ]; - n2.y = normals[ base + j + 1 ]; - n2.z = normals[ base + j + 2 ]; - - // average normals - - n.addVectors( n1, n2 ).normalize(); - - // assign the new values to both normals - - normals[ j + 0 ] = normals[ base + j + 0 ] = n.x; - normals[ j + 1 ] = normals[ base + j + 1 ] = n.y; - normals[ j + 2 ] = normals[ base + j + 2 ] = n.z; - - } - - } - - } - - } - - // ShapeGeometry - - class ShapeGeometry extends Geometry { - - constructor( shapes, curveSegments ) { - - super(); - this.type = 'ShapeGeometry'; - - if ( typeof curveSegments === 'object' ) { - - console.warn( 'THREE.ShapeGeometry: Options parameter has been removed.' ); - - curveSegments = curveSegments.curveSegments; - - } - - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; - - this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) ); - this.mergeVertices(); - - } - - toJSON() { - - const data = Geometry.prototype.toJSON.call( this ); - - const shapes = this.parameters.shapes; - - return toJSON$1( shapes, data ); - - } - - } - - // ShapeBufferGeometry - - class ShapeBufferGeometry extends BufferGeometry { - - constructor( shapes, curveSegments ) { - - super(); - this.type = 'ShapeBufferGeometry'; - - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; - - curveSegments = curveSegments || 12; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - let groupStart = 0; - let groupCount = 0; - - // allow single and array values for "shapes" parameter - - if ( Array.isArray( shapes ) === false ) { - - addShape( shapes ); - - } else { - - for ( let i = 0; i < shapes.length; i ++ ) { - - addShape( shapes[ i ] ); - - this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support - - groupStart += groupCount; - groupCount = 0; - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - - // helper functions - - function addShape( shape ) { - - const indexOffset = vertices.length / 3; - const points = shape.extractPoints( curveSegments ); - - let shapeVertices = points.shape; - const shapeHoles = points.holes; - - // check direction of vertices - - if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { - - shapeVertices = shapeVertices.reverse(); - - } - - for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { - - const shapeHole = shapeHoles[ i ]; - - if ( ShapeUtils.isClockWise( shapeHole ) === true ) { - - shapeHoles[ i ] = shapeHole.reverse(); - - } - - } - - const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); - - // join vertices of inner and outer paths to a single array - - for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { - - const shapeHole = shapeHoles[ i ]; - shapeVertices = shapeVertices.concat( shapeHole ); - - } - - // vertices, normals, uvs - - for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { - - const vertex = shapeVertices[ i ]; - - vertices.push( vertex.x, vertex.y, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( vertex.x, vertex.y ); // world uvs - - } - - // incides - - for ( let i = 0, l = faces.length; i < l; i ++ ) { - - const face = faces[ i ]; - - const a = face[ 0 ] + indexOffset; - const b = face[ 1 ] + indexOffset; - const c = face[ 2 ] + indexOffset; - - indices.push( a, b, c ); - groupCount += 3; - - } - - } - - } - - toJSON() { - - const data = BufferGeometry.prototype.toJSON.call( this ); - - const shapes = this.parameters.shapes; - - return toJSON$1( shapes, data ); - - } - - } - - // - - function toJSON$1( shapes, data ) { - - data.shapes = []; - - if ( Array.isArray( shapes ) ) { - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - - data.shapes.push( shape.uuid ); - - } - - } else { - - data.shapes.push( shapes.uuid ); - - } - - return data; - - } - - class EdgesGeometry extends BufferGeometry { - - constructor( geometry, thresholdAngle ) { - - super(); - - this.type = 'EdgesGeometry'; - - this.parameters = { - thresholdAngle: thresholdAngle - }; - - thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; - - // buffer - - const vertices = []; - - // helper variables - - const thresholdDot = Math.cos( MathUtils.DEG2RAD * thresholdAngle ); - const edge = [ 0, 0 ], edges = {}; - let edge1, edge2, key; - const keys = [ 'a', 'b', 'c' ]; - - // prepare source geometry - - let geometry2; - - if ( geometry.isBufferGeometry ) { - - geometry2 = new Geometry(); - geometry2.fromBufferGeometry( geometry ); - - } else { - - geometry2 = geometry.clone(); - - } - - geometry2.mergeVertices(); - geometry2.computeFaceNormals(); - - const sourceVertices = geometry2.vertices; - const faces = geometry2.faces; - - // now create a data structure where each entry represents an edge with its adjoining faces - - for ( let i = 0, l = faces.length; i < l; i ++ ) { - - const face = faces[ i ]; - - for ( let j = 0; j < 3; j ++ ) { - - edge1 = face[ keys[ j ] ]; - edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; - edge[ 0 ] = Math.min( edge1, edge2 ); - edge[ 1 ] = Math.max( edge1, edge2 ); - - key = edge[ 0 ] + ',' + edge[ 1 ]; - - if ( edges[ key ] === undefined ) { - - edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined }; - - } else { - - edges[ key ].face2 = i; - - } - - } - - } - - // generate vertices - - for ( key in edges ) { - - const e = edges[ key ]; - - // an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree. - - if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) { - - let vertex = sourceVertices[ e.index1 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - vertex = sourceVertices[ e.index2 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } - - // build geometry - - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - - } - - } - - // CylinderGeometry - - class CylinderGeometry extends Geometry { - - constructor( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - super(); - this.type = 'CylinderGeometry'; - - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - } - - // CylinderBufferGeometry - - class CylinderBufferGeometry extends BufferGeometry { - - constructor( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - super(); - this.type = 'CylinderBufferGeometry'; - - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - const scope = this; - - radiusTop = radiusTop !== undefined ? radiusTop : 1; - radiusBottom = radiusBottom !== undefined ? radiusBottom : 1; - height = height || 1; - - radialSegments = Math.floor( radialSegments ) || 8; - heightSegments = Math.floor( heightSegments ) || 1; - - openEnded = openEnded !== undefined ? openEnded : false; - thetaStart = thetaStart !== undefined ? thetaStart : 0.0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - let index = 0; - const indexArray = []; - const halfHeight = height / 2; - let groupStart = 0; - - // generate geometry - - generateTorso(); - - if ( openEnded === false ) { - - if ( radiusTop > 0 ) generateCap( true ); - if ( radiusBottom > 0 ) generateCap( false ); - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - function generateTorso() { - - const normal = new Vector3(); - const vertex = new Vector3(); - - let groupCount = 0; - - // this will be used to calculate the normal - const slope = ( radiusBottom - radiusTop ) / height; - - // generate vertices, normals and uvs - - for ( let y = 0; y <= heightSegments; y ++ ) { - - const indexRow = []; - - const v = y / heightSegments; - - // calculate the radius of the current row - - const radius = v * ( radiusBottom - radiusTop ) + radiusTop; - - for ( let x = 0; x <= radialSegments; x ++ ) { - - const u = x / radialSegments; - - const theta = u * thetaLength + thetaStart; - - const sinTheta = Math.sin( theta ); - const cosTheta = Math.cos( theta ); - - // vertex - - vertex.x = radius * sinTheta; - vertex.y = - v * height + halfHeight; - vertex.z = radius * cosTheta; - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normal.set( sinTheta, slope, cosTheta ).normalize(); - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( u, 1 - v ); - - // save index of vertex in respective row - - indexRow.push( index ++ ); - - } - - // now save vertices of the row in our index array - - indexArray.push( indexRow ); - - } - - // generate indices - - for ( let x = 0; x < radialSegments; x ++ ) { - - for ( let y = 0; y < heightSegments; y ++ ) { - - // we use the index array to access the correct indices - - const a = indexArray[ y ][ x ]; - const b = indexArray[ y + 1 ][ x ]; - const c = indexArray[ y + 1 ][ x + 1 ]; - const d = indexArray[ y ][ x + 1 ]; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - // update group counter - - groupCount += 6; - - } - - } - - // add a group to the geometry. this will ensure multi material support - - scope.addGroup( groupStart, groupCount, 0 ); - - // calculate new start value for groups - - groupStart += groupCount; - - } - - function generateCap( top ) { - - // save the index of the first center vertex - const centerIndexStart = index; - - const uv = new Vector2(); - const vertex = new Vector3(); - - let groupCount = 0; - - const radius = ( top === true ) ? radiusTop : radiusBottom; - const sign = ( top === true ) ? 1 : - 1; - - // first we generate the center vertex data of the cap. - // because the geometry needs one set of uvs per face, - // we must generate a center vertex per face/segment - - for ( let x = 1; x <= radialSegments; x ++ ) { - - // vertex - - vertices.push( 0, halfHeight * sign, 0 ); - - // normal - - normals.push( 0, sign, 0 ); - - // uv - - uvs.push( 0.5, 0.5 ); - - // increase index - - index ++; - - } - - // save the index of the last center vertex - const centerIndexEnd = index; - - // now we generate the surrounding vertices, normals and uvs - - for ( let x = 0; x <= radialSegments; x ++ ) { - - const u = x / radialSegments; - const theta = u * thetaLength + thetaStart; - - const cosTheta = Math.cos( theta ); - const sinTheta = Math.sin( theta ); - - // vertex - - vertex.x = radius * sinTheta; - vertex.y = halfHeight * sign; - vertex.z = radius * cosTheta; - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normals.push( 0, sign, 0 ); - - // uv - - uv.x = ( cosTheta * 0.5 ) + 0.5; - uv.y = ( sinTheta * 0.5 * sign ) + 0.5; - uvs.push( uv.x, uv.y ); - - // increase index - - index ++; - - } - - // generate indices - - for ( let x = 0; x < radialSegments; x ++ ) { - - const c = centerIndexStart + x; - const i = centerIndexEnd + x; - - if ( top === true ) { - - // face top - - indices.push( i, i + 1, c ); - - } else { - - // face bottom - - indices.push( i + 1, i, c ); - - } - - groupCount += 3; - - } - - // add a group to the geometry. this will ensure multi material support - - scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); - - // calculate new start value for groups - - groupStart += groupCount; - - } - - } - - } - - // ConeGeometry - - class ConeGeometry extends CylinderGeometry { - - constructor( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - super( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); - this.type = 'ConeGeometry'; - - this.parameters = { - radius: radius, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - } - - } - - // ConeBufferGeometry - - class ConeBufferGeometry extends CylinderBufferGeometry { - - constructor( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - super( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); - this.type = 'ConeBufferGeometry'; - - this.parameters = { - radius: radius, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - } - - } - - // CircleGeometry - - class CircleGeometry extends Geometry { - - constructor( radius, segments, thetaStart, thetaLength ) { - - super(); - this.type = 'CircleGeometry'; - - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - } - - // CircleBufferGeometry - - class CircleBufferGeometry extends BufferGeometry { - - constructor( radius, segments, thetaStart, thetaLength ) { - - super(); - - this.type = 'CircleBufferGeometry'; - - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - radius = radius || 1; - segments = segments !== undefined ? Math.max( 3, segments ) : 8; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - - // buffers - - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; - - // helper variables - - const vertex = new Vector3(); - const uv = new Vector2(); - - // center point - - vertices.push( 0, 0, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( 0.5, 0.5 ); - - for ( let s = 0, i = 3; s <= segments; s ++, i += 3 ) { - - const segment = thetaStart + s / segments * thetaLength; - - // vertex - - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normals.push( 0, 0, 1 ); - - // uvs - - uv.x = ( vertices[ i ] / radius + 1 ) / 2; - uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; - - uvs.push( uv.x, uv.y ); - - } - - // indices - - for ( let i = 1; i <= segments; i ++ ) { - - indices.push( i, i + 1, 0 ); - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - } - - var Geometries = /*#__PURE__*/Object.freeze({ - __proto__: null, - WireframeGeometry: WireframeGeometry, - ParametricGeometry: ParametricGeometry, - ParametricBufferGeometry: ParametricBufferGeometry, - TetrahedronGeometry: TetrahedronGeometry, - TetrahedronBufferGeometry: TetrahedronBufferGeometry, - OctahedronGeometry: OctahedronGeometry, - OctahedronBufferGeometry: OctahedronBufferGeometry, - IcosahedronGeometry: IcosahedronGeometry, - IcosahedronBufferGeometry: IcosahedronBufferGeometry, - DodecahedronGeometry: DodecahedronGeometry, - DodecahedronBufferGeometry: DodecahedronBufferGeometry, - PolyhedronGeometry: PolyhedronGeometry, - PolyhedronBufferGeometry: PolyhedronBufferGeometry, - TubeGeometry: TubeGeometry, - TubeBufferGeometry: TubeBufferGeometry, - TorusKnotGeometry: TorusKnotGeometry, - TorusKnotBufferGeometry: TorusKnotBufferGeometry, - TorusGeometry: TorusGeometry, - TorusBufferGeometry: TorusBufferGeometry, - TextGeometry: TextGeometry, - TextBufferGeometry: TextBufferGeometry, - SphereGeometry: SphereGeometry, - SphereBufferGeometry: SphereBufferGeometry, - RingGeometry: RingGeometry, - RingBufferGeometry: RingBufferGeometry, - PlaneGeometry: PlaneGeometry, - PlaneBufferGeometry: PlaneBufferGeometry, - LatheGeometry: LatheGeometry, - LatheBufferGeometry: LatheBufferGeometry, - ShapeGeometry: ShapeGeometry, - ShapeBufferGeometry: ShapeBufferGeometry, - ExtrudeGeometry: ExtrudeGeometry, - ExtrudeBufferGeometry: ExtrudeBufferGeometry, - EdgesGeometry: EdgesGeometry, - ConeGeometry: ConeGeometry, - ConeBufferGeometry: ConeBufferGeometry, - CylinderGeometry: CylinderGeometry, - CylinderBufferGeometry: CylinderBufferGeometry, - CircleGeometry: CircleGeometry, - CircleBufferGeometry: CircleBufferGeometry, - BoxGeometry: BoxGeometry, - BoxBufferGeometry: BoxBufferGeometry - }); - - /** - * parameters = { - * color: - * } - */ - - function ShadowMaterial( parameters ) { - - Material.call( this ); - - this.type = 'ShadowMaterial'; - - this.color = new Color( 0x000000 ); - this.transparent = true; - - this.setValues( parameters ); - - } - - ShadowMaterial.prototype = Object.create( Material.prototype ); - ShadowMaterial.prototype.constructor = ShadowMaterial; - - ShadowMaterial.prototype.isShadowMaterial = true; - - ShadowMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - return this; - - }; - - function RawShaderMaterial( parameters ) { - - ShaderMaterial.call( this, parameters ); - - this.type = 'RawShaderMaterial'; - - } - - RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); - RawShaderMaterial.prototype.constructor = RawShaderMaterial; - - RawShaderMaterial.prototype.isRawShaderMaterial = true; - - /** - * parameters = { - * color: , - * roughness: , - * metalness: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * roughnessMap: new THREE.Texture( ), - * - * metalnessMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * envMapIntensity: - * - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshStandardMaterial( parameters ) { - - Material.call( this ); - - this.defines = { 'STANDARD': '' }; - - this.type = 'MeshStandardMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - this.roughness = 1.0; - this.metalness = 0.0; - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.roughnessMap = null; - - this.metalnessMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.envMapIntensity = 1.0; - - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.vertexTangents = false; - - this.setValues( parameters ); - - } - - MeshStandardMaterial.prototype = Object.create( Material.prototype ); - MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; - - MeshStandardMaterial.prototype.isMeshStandardMaterial = true; - - MeshStandardMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.defines = { 'STANDARD': '' }; - - this.color.copy( source.color ); - this.roughness = source.roughness; - this.metalness = source.metalness; - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.roughnessMap = source.roughnessMap; - - this.metalnessMap = source.metalnessMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.envMapIntensity = source.envMapIntensity; - - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - this.vertexTangents = source.vertexTangents; - - return this; - - }; - - /** - * parameters = { - * clearcoat: , - * clearcoatMap: new THREE.Texture( ), - * clearcoatRoughness: , - * clearcoatRoughnessMap: new THREE.Texture( ), - * clearcoatNormalScale: , - * clearcoatNormalMap: new THREE.Texture( ), - * - * reflectivity: , - * - * sheen: , - * - * transmission: , - * transmissionMap: new THREE.Texture( ) - * } - */ - - function MeshPhysicalMaterial( parameters ) { - - MeshStandardMaterial.call( this ); - - this.defines = { - - 'STANDARD': '', - 'PHYSICAL': '' - - }; - - this.type = 'MeshPhysicalMaterial'; - - this.clearcoat = 0.0; - this.clearcoatMap = null; - this.clearcoatRoughness = 0.0; - this.clearcoatRoughnessMap = null; - this.clearcoatNormalScale = new Vector2( 1, 1 ); - this.clearcoatNormalMap = null; - - this.reflectivity = 0.5; // maps to F0 = 0.04 - - this.sheen = null; // null will disable sheen bsdf - - this.transmission = 0.0; - this.transmissionMap = null; - - this.setValues( parameters ); - - } - - MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); - MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; - - MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; - - MeshPhysicalMaterial.prototype.copy = function ( source ) { - - MeshStandardMaterial.prototype.copy.call( this, source ); - - this.defines = { - - 'STANDARD': '', - 'PHYSICAL': '' - - }; - - this.clearcoat = source.clearcoat; - this.clearcoatMap = source.clearcoatMap; - this.clearcoatRoughness = source.clearcoatRoughness; - this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; - this.clearcoatNormalMap = source.clearcoatNormalMap; - this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); - - this.reflectivity = source.reflectivity; - - if ( source.sheen ) { - - this.sheen = ( this.sheen || new Color() ).copy( source.sheen ); - - } else { - - this.sheen = null; - - } - - this.transmission = source.transmission; - this.transmissionMap = source.transmissionMap; - - return this; - - }; - - /** - * parameters = { - * color: , - * specular: , - * shininess: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.MultiplyOperation, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshPhongMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshPhongMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - this.specular = new Color( 0x111111 ); - this.shininess = 30; - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshPhongMaterial.prototype = Object.create( Material.prototype ); - MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; - - MeshPhongMaterial.prototype.isMeshPhongMaterial = true; - - MeshPhongMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - this.specular.copy( source.specular ); - this.shininess = source.shininess; - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * parameters = { - * color: , - * - * map: new THREE.Texture( ), - * gradientMap: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshToonMaterial( parameters ) { - - Material.call( this ); - - this.defines = { 'TOON': '' }; - - this.type = 'MeshToonMaterial'; - - this.color = new Color( 0xffffff ); - - this.map = null; - this.gradientMap = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.alphaMap = null; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshToonMaterial.prototype = Object.create( Material.prototype ); - MeshToonMaterial.prototype.constructor = MeshToonMaterial; - - MeshToonMaterial.prototype.isMeshToonMaterial = true; - - MeshToonMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - this.gradientMap = source.gradientMap; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.alphaMap = source.alphaMap; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * parameters = { - * opacity: , - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshNormalMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshNormalMaterial'; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshNormalMaterial.prototype = Object.create( Material.prototype ); - MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; - - MeshNormalMaterial.prototype.isMeshNormalMaterial = true; - - MeshNormalMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * parameters = { - * color: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshLambertMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshLambertMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshLambertMaterial.prototype = Object.create( Material.prototype ); - MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; - - MeshLambertMaterial.prototype.isMeshLambertMaterial = true; - - MeshLambertMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * parameters = { - * color: , - * opacity: , - * - * matcap: new THREE.Texture( ), - * - * map: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshMatcapMaterial( parameters ) { - - Material.call( this ); - - this.defines = { 'MATCAP': '' }; - - this.type = 'MeshMatcapMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - - this.matcap = null; - - this.map = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.alphaMap = null; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshMatcapMaterial.prototype = Object.create( Material.prototype ); - MeshMatcapMaterial.prototype.constructor = MeshMatcapMaterial; - - MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; - - MeshMatcapMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.defines = { 'MATCAP': '' }; - - this.color.copy( source.color ); - - this.matcap = source.matcap; - - this.map = source.map; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.alphaMap = source.alphaMap; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * - * scale: , - * dashSize: , - * gapSize: - * } - */ - - function LineDashedMaterial( parameters ) { - - LineBasicMaterial.call( this ); - - this.type = 'LineDashedMaterial'; - - this.scale = 1; - this.dashSize = 3; - this.gapSize = 1; - - this.setValues( parameters ); - - } - - LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype ); - LineDashedMaterial.prototype.constructor = LineDashedMaterial; - - LineDashedMaterial.prototype.isLineDashedMaterial = true; - - LineDashedMaterial.prototype.copy = function ( source ) { - - LineBasicMaterial.prototype.copy.call( this, source ); - - this.scale = source.scale; - this.dashSize = source.dashSize; - this.gapSize = source.gapSize; - - return this; - - }; - - var Materials = /*#__PURE__*/Object.freeze({ - __proto__: null, - ShadowMaterial: ShadowMaterial, - SpriteMaterial: SpriteMaterial, - RawShaderMaterial: RawShaderMaterial, - ShaderMaterial: ShaderMaterial, - PointsMaterial: PointsMaterial, - MeshPhysicalMaterial: MeshPhysicalMaterial, - MeshStandardMaterial: MeshStandardMaterial, - MeshPhongMaterial: MeshPhongMaterial, - MeshToonMaterial: MeshToonMaterial, - MeshNormalMaterial: MeshNormalMaterial, - MeshLambertMaterial: MeshLambertMaterial, - MeshDepthMaterial: MeshDepthMaterial, - MeshDistanceMaterial: MeshDistanceMaterial, - MeshBasicMaterial: MeshBasicMaterial, - MeshMatcapMaterial: MeshMatcapMaterial, - LineDashedMaterial: LineDashedMaterial, - LineBasicMaterial: LineBasicMaterial, - Material: Material - }); - - const AnimationUtils = { - - // same as Array.prototype.slice, but also works on typed arrays - arraySlice: function ( array, from, to ) { - - if ( AnimationUtils.isTypedArray( array ) ) { - - // in ios9 array.subarray(from, undefined) will return empty array - // but array.subarray(from) or array.subarray(from, len) is correct - return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); - - } - - return array.slice( from, to ); - - }, - - // converts an array to a specific type - convertArray: function ( array, type, forceClone ) { - - if ( ! array || // let 'undefined' and 'null' pass - ! forceClone && array.constructor === type ) return array; - - if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { - - return new type( array ); // create typed array - - } - - return Array.prototype.slice.call( array ); // create Array - - }, - - isTypedArray: function ( object ) { - - return ArrayBuffer.isView( object ) && - ! ( object instanceof DataView ); - - }, - - // returns an array by which times and values can be sorted - getKeyframeOrder: function ( times ) { - - function compareTime( i, j ) { - - return times[ i ] - times[ j ]; - - } - - const n = times.length; - const result = new Array( n ); - for ( let i = 0; i !== n; ++ i ) result[ i ] = i; - - result.sort( compareTime ); - - return result; - - }, - - // uses the array previously returned by 'getKeyframeOrder' to sort data - sortedArray: function ( values, stride, order ) { - - const nValues = values.length; - const result = new values.constructor( nValues ); - - for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { - - const srcOffset = order[ i ] * stride; - - for ( let j = 0; j !== stride; ++ j ) { - - result[ dstOffset ++ ] = values[ srcOffset + j ]; - - } - - } - - return result; - - }, - - // function for parsing AOS keyframe formats - flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { - - let i = 1, key = jsonKeys[ 0 ]; - - while ( key !== undefined && key[ valuePropertyName ] === undefined ) { - - key = jsonKeys[ i ++ ]; - - } - - if ( key === undefined ) return; // no data - - let value = key[ valuePropertyName ]; - if ( value === undefined ) return; // no data - - if ( Array.isArray( value ) ) { - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - values.push.apply( values, value ); // push all elements - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } else if ( value.toArray !== undefined ) { - - // ...assume THREE.Math-ish - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - value.toArray( values, values.length ); - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } else { - - // otherwise push as-is - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - values.push( value ); - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } - - }, - - subclip: function ( sourceClip, name, startFrame, endFrame, fps ) { - - fps = fps || 30; - - const clip = sourceClip.clone(); - - clip.name = name; - - const tracks = []; - - for ( let i = 0; i < clip.tracks.length; ++ i ) { - - const track = clip.tracks[ i ]; - const valueSize = track.getValueSize(); - - const times = []; - const values = []; - - for ( let j = 0; j < track.times.length; ++ j ) { - - const frame = track.times[ j ] * fps; - - if ( frame < startFrame || frame >= endFrame ) continue; - - times.push( track.times[ j ] ); - - for ( let k = 0; k < valueSize; ++ k ) { - - values.push( track.values[ j * valueSize + k ] ); - - } - - } - - if ( times.length === 0 ) continue; - - track.times = AnimationUtils.convertArray( times, track.times.constructor ); - track.values = AnimationUtils.convertArray( values, track.values.constructor ); - - tracks.push( track ); - - } - - clip.tracks = tracks; - - // find minimum .times value across all tracks in the trimmed clip - - let minStartTime = Infinity; - - for ( let i = 0; i < clip.tracks.length; ++ i ) { - - if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { - - minStartTime = clip.tracks[ i ].times[ 0 ]; - - } - - } - - // shift all tracks such that clip begins at t=0 - - for ( let i = 0; i < clip.tracks.length; ++ i ) { - - clip.tracks[ i ].shift( - 1 * minStartTime ); - - } - - clip.resetDuration(); - - return clip; - - }, - - makeClipAdditive: function ( targetClip, referenceFrame, referenceClip, fps ) { - - if ( referenceFrame === undefined ) referenceFrame = 0; - if ( referenceClip === undefined ) referenceClip = targetClip; - if ( fps === undefined || fps <= 0 ) fps = 30; - - const numTracks = targetClip.tracks.length; - const referenceTime = referenceFrame / fps; - - // Make each track's values relative to the values at the reference frame - for ( let i = 0; i < numTracks; ++ i ) { - - const referenceTrack = referenceClip.tracks[ i ]; - const referenceTrackType = referenceTrack.ValueTypeName; - - // Skip this track if it's non-numeric - if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue; - - // Find the track in the target clip whose name and type matches the reference track - const targetTrack = targetClip.tracks.find( function ( track ) { - - return track.name === referenceTrack.name - && track.ValueTypeName === referenceTrackType; - - } ); - - if ( targetTrack === undefined ) continue; - - let referenceOffset = 0; - const referenceValueSize = referenceTrack.getValueSize(); - - if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - - referenceOffset = referenceValueSize / 3; - - } - - let targetOffset = 0; - const targetValueSize = targetTrack.getValueSize(); - - if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - - targetOffset = targetValueSize / 3; - - } - - const lastIndex = referenceTrack.times.length - 1; - let referenceValue; - - // Find the value to subtract out of the track - if ( referenceTime <= referenceTrack.times[ 0 ] ) { - - // Reference frame is earlier than the first keyframe, so just use the first keyframe - const startIndex = referenceOffset; - const endIndex = referenceValueSize - referenceOffset; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); - - } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { - - // Reference frame is after the last keyframe, so just use the last keyframe - const startIndex = lastIndex * referenceValueSize + referenceOffset; - const endIndex = startIndex + referenceValueSize - referenceOffset; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); - - } else { - - // Interpolate to the reference value - const interpolant = referenceTrack.createInterpolant(); - const startIndex = referenceOffset; - const endIndex = referenceValueSize - referenceOffset; - interpolant.evaluate( referenceTime ); - referenceValue = AnimationUtils.arraySlice( interpolant.resultBuffer, startIndex, endIndex ); - - } - - // Conjugate the quaternion - if ( referenceTrackType === 'quaternion' ) { - - const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); - referenceQuat.toArray( referenceValue ); - - } - - // Subtract the reference value from all of the track values - - const numTimes = targetTrack.times.length; - for ( let j = 0; j < numTimes; ++ j ) { - - const valueStart = j * targetValueSize + targetOffset; - - if ( referenceTrackType === 'quaternion' ) { - - // Multiply the conjugate for quaternion track types - Quaternion.multiplyQuaternionsFlat( - targetTrack.values, - valueStart, - referenceValue, - 0, - targetTrack.values, - valueStart - ); - - } else { - - const valueEnd = targetValueSize - targetOffset * 2; - - // Subtract each value for all other numeric track types - for ( let k = 0; k < valueEnd; ++ k ) { - - targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; - - } - - } - - } - - } - - targetClip.blendMode = AdditiveAnimationBlendMode; - - return targetClip; - - } - - }; - - /** - * Abstract base class of interpolants over parametric samples. - * - * The parameter domain is one dimensional, typically the time or a path - * along a curve defined by the data. - * - * The sample values can have any dimensionality and derived classes may - * apply special interpretations to the data. - * - * This class provides the interval seek in a Template Method, deferring - * the actual interpolation to derived classes. - * - * Time complexity is O(1) for linear access crossing at most two points - * and O(log N) for random access, where N is the number of positions. - * - * References: - * - * http://www.oodesign.com/template-method-pattern.html - * - */ - - function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - this.parameterPositions = parameterPositions; - this._cachedIndex = 0; - - this.resultBuffer = resultBuffer !== undefined ? - resultBuffer : new sampleValues.constructor( sampleSize ); - this.sampleValues = sampleValues; - this.valueSize = sampleSize; - - } - - Object.assign( Interpolant.prototype, { - - evaluate: function ( t ) { - - const pp = this.parameterPositions; - let i1 = this._cachedIndex, - t1 = pp[ i1 ], - t0 = pp[ i1 - 1 ]; - - validate_interval: { - - seek: { - - let right; - - linear_scan: { - - //- See http://jsperf.com/comparison-to-undefined/3 - //- slower code: - //- - //- if ( t >= t1 || t1 === undefined ) { - forward_scan: if ( ! ( t < t1 ) ) { - - for ( let giveUpAt = i1 + 2; ; ) { - - if ( t1 === undefined ) { - - if ( t < t0 ) break forward_scan; - - // after end - - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t, t0 ); - - } - - if ( i1 === giveUpAt ) break; // this loop - - t0 = t1; - t1 = pp[ ++ i1 ]; - - if ( t < t1 ) { - - // we have arrived at the sought interval - break seek; - - } - - } - - // prepare binary search on the right side of the index - right = pp.length; - break linear_scan; - - } - - //- slower code: - //- if ( t < t0 || t0 === undefined ) { - if ( ! ( t >= t0 ) ) { - - // looping? - - const t1global = pp[ 1 ]; - - if ( t < t1global ) { - - i1 = 2; // + 1, using the scan for the details - t0 = t1global; - - } - - // linear reverse scan - - for ( let giveUpAt = i1 - 2; ; ) { - - if ( t0 === undefined ) { - - // before start - - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); - - } - - if ( i1 === giveUpAt ) break; // this loop - - t1 = t0; - t0 = pp[ -- i1 - 1 ]; - - if ( t >= t0 ) { - - // we have arrived at the sought interval - break seek; - - } - - } - - // prepare binary search on the left side of the index - right = i1; - i1 = 0; - break linear_scan; - - } - - // the interval is valid - - break validate_interval; - - } // linear scan - - // binary search - - while ( i1 < right ) { - - const mid = ( i1 + right ) >>> 1; - - if ( t < pp[ mid ] ) { - - right = mid; - - } else { - - i1 = mid + 1; - - } - - } - - t1 = pp[ i1 ]; - t0 = pp[ i1 - 1 ]; - - // check boundary cases, again - - if ( t0 === undefined ) { - - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); - - } - - if ( t1 === undefined ) { - - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t0, t ); - - } - - } // seek - - this._cachedIndex = i1; - - this.intervalChanged_( i1, t0, t1 ); - - } // validate_interval - - return this.interpolate_( i1, t0, t, t1 ); - - }, - - settings: null, // optional, subclass-specific settings structure - // Note: The indirection allows central control of many interpolants. - - // --- Protected interface - - DefaultSettings_: {}, - - getSettings_: function () { - - return this.settings || this.DefaultSettings_; - - }, - - copySampleValue_: function ( index ) { - - // copies a sample value to the result buffer - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - offset = index * stride; - - for ( let i = 0; i !== stride; ++ i ) { - - result[ i ] = values[ offset + i ]; - - } - - return result; - - }, - - // Template methods for derived classes: - - interpolate_: function ( /* i1, t0, t, t1 */ ) { - - throw new Error( 'call to abstract method' ); - // implementations shall return this.resultBuffer - - }, - - intervalChanged_: function ( /* i1, t0, t1 */ ) { - - // empty - - } - - } ); - - // DECLARE ALIAS AFTER assign prototype - Object.assign( Interpolant.prototype, { - - //( 0, t, t0 ), returns this.resultBuffer - beforeStart_: Interpolant.prototype.copySampleValue_, - - //( N-1, tN-1, t ), returns this.resultBuffer - afterEnd_: Interpolant.prototype.copySampleValue_, - - } ); - - /** - * Fast and simple cubic spline interpolant. - * - * It was derived from a Hermitian construction setting the first derivative - * at each sample position to the linear slope between neighboring positions - * over their parameter interval. - */ - - function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - this._weightPrev = - 0; - this._offsetPrev = - 0; - this._weightNext = - 0; - this._offsetNext = - 0; - - } - - CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: CubicInterpolant, - - DefaultSettings_: { - - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - - }, - - intervalChanged_: function ( i1, t0, t1 ) { - - const pp = this.parameterPositions; - let iPrev = i1 - 2, - iNext = i1 + 1, - - tPrev = pp[ iPrev ], - tNext = pp[ iNext ]; - - if ( tPrev === undefined ) { - - switch ( this.getSettings_().endingStart ) { - - case ZeroSlopeEnding: - - // f'(t0) = 0 - iPrev = i1; - tPrev = 2 * t0 - t1; - - break; - - case WrapAroundEnding: - - // use the other end of the curve - iPrev = pp.length - 2; - tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; - - break; - - default: // ZeroCurvatureEnding - - // f''(t0) = 0 a.k.a. Natural Spline - iPrev = i1; - tPrev = t1; - - } - - } - - if ( tNext === undefined ) { - - switch ( this.getSettings_().endingEnd ) { - - case ZeroSlopeEnding: - - // f'(tN) = 0 - iNext = i1; - tNext = 2 * t1 - t0; - - break; - - case WrapAroundEnding: - - // use the other end of the curve - iNext = 1; - tNext = t1 + pp[ 1 ] - pp[ 0 ]; - - break; - - default: // ZeroCurvatureEnding - - // f''(tN) = 0, a.k.a. Natural Spline - iNext = i1 - 1; - tNext = t0; - - } - - } - - const halfDt = ( t1 - t0 ) * 0.5, - stride = this.valueSize; - - this._weightPrev = halfDt / ( t0 - tPrev ); - this._weightNext = halfDt / ( tNext - t1 ); - this._offsetPrev = iPrev * stride; - this._offsetNext = iNext * stride; - - }, - - interpolate_: function ( i1, t0, t, t1 ) { - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - o1 = i1 * stride, o0 = o1 - stride, - oP = this._offsetPrev, oN = this._offsetNext, - wP = this._weightPrev, wN = this._weightNext, - - p = ( t - t0 ) / ( t1 - t0 ), - pp = p * p, - ppp = pp * p; - - // evaluate polynomials - - const sP = - wP * ppp + 2 * wP * pp - wP * p; - const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; - const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; - const sN = wN * ppp - wN * pp; - - // combine data linearly - - for ( let i = 0; i !== stride; ++ i ) { - - result[ i ] = - sP * values[ oP + i ] + - s0 * values[ o0 + i ] + - s1 * values[ o1 + i ] + - sN * values[ oN + i ]; - - } - - return result; - - } - - } ); - - function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: LinearInterpolant, - - interpolate_: function ( i1, t0, t, t1 ) { - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - offset1 = i1 * stride, - offset0 = offset1 - stride, - - weight1 = ( t - t0 ) / ( t1 - t0 ), - weight0 = 1 - weight1; - - for ( let i = 0; i !== stride; ++ i ) { - - result[ i ] = - values[ offset0 + i ] * weight0 + - values[ offset1 + i ] * weight1; - - } - - return result; - - } - - } ); - - /** - * - * Interpolant that evaluates to the sample value at the position preceeding - * the parameter. - */ - - function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: DiscreteInterpolant, - - interpolate_: function ( i1 /*, t0, t, t1 */ ) { - - return this.copySampleValue_( i1 - 1 ); - - } - - } ); - - function KeyframeTrack( name, times, values, interpolation ) { - - if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); - if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); - - this.name = name; - - this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); - this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); - - this.setInterpolation( interpolation || this.DefaultInterpolation ); - - } - - // Static methods - - Object.assign( KeyframeTrack, { - - // Serialization (in static context, because of constructor invocation - // and automatic invocation of .toJSON): - - toJSON: function ( track ) { - - const trackType = track.constructor; - - let json; - - // derived classes can define a static toJSON method - if ( trackType.toJSON !== undefined ) { - - json = trackType.toJSON( track ); - - } else { - - // by default, we assume the data can be serialized as-is - json = { - - 'name': track.name, - 'times': AnimationUtils.convertArray( track.times, Array ), - 'values': AnimationUtils.convertArray( track.values, Array ) - - }; - - const interpolation = track.getInterpolation(); - - if ( interpolation !== track.DefaultInterpolation ) { - - json.interpolation = interpolation; - - } - - } - - json.type = track.ValueTypeName; // mandatory - - return json; - - } - - } ); - - Object.assign( KeyframeTrack.prototype, { - - constructor: KeyframeTrack, - - TimeBufferType: Float32Array, - - ValueBufferType: Float32Array, - - DefaultInterpolation: InterpolateLinear, - - InterpolantFactoryMethodDiscrete: function ( result ) { - - return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); - - }, - - InterpolantFactoryMethodLinear: function ( result ) { - - return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); - - }, - - InterpolantFactoryMethodSmooth: function ( result ) { - - return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); - - }, - - setInterpolation: function ( interpolation ) { - - let factoryMethod; - - switch ( interpolation ) { - - case InterpolateDiscrete: - - factoryMethod = this.InterpolantFactoryMethodDiscrete; - - break; - - case InterpolateLinear: - - factoryMethod = this.InterpolantFactoryMethodLinear; - - break; - - case InterpolateSmooth: - - factoryMethod = this.InterpolantFactoryMethodSmooth; - - break; - - } - - if ( factoryMethod === undefined ) { - - const message = "unsupported interpolation for " + - this.ValueTypeName + " keyframe track named " + this.name; - - if ( this.createInterpolant === undefined ) { - - // fall back to default, unless the default itself is messed up - if ( interpolation !== this.DefaultInterpolation ) { - - this.setInterpolation( this.DefaultInterpolation ); - - } else { - - throw new Error( message ); // fatal, in this case - - } - - } - - console.warn( 'THREE.KeyframeTrack:', message ); - return this; - - } - - this.createInterpolant = factoryMethod; - - return this; - - }, - - getInterpolation: function () { - - switch ( this.createInterpolant ) { - - case this.InterpolantFactoryMethodDiscrete: - - return InterpolateDiscrete; - - case this.InterpolantFactoryMethodLinear: - - return InterpolateLinear; - - case this.InterpolantFactoryMethodSmooth: - - return InterpolateSmooth; - - } - - }, - - getValueSize: function () { - - return this.values.length / this.times.length; - - }, - - // move all keyframes either forwards or backwards in time - shift: function ( timeOffset ) { - - if ( timeOffset !== 0.0 ) { - - const times = this.times; - - for ( let i = 0, n = times.length; i !== n; ++ i ) { - - times[ i ] += timeOffset; - - } - - } - - return this; - - }, - - // scale all keyframe times by a factor (useful for frame <-> seconds conversions) - scale: function ( timeScale ) { - - if ( timeScale !== 1.0 ) { - - const times = this.times; - - for ( let i = 0, n = times.length; i !== n; ++ i ) { - - times[ i ] *= timeScale; - - } - - } - - return this; - - }, - - // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. - // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values - trim: function ( startTime, endTime ) { - - const times = this.times, - nKeys = times.length; - - let from = 0, - to = nKeys - 1; - - while ( from !== nKeys && times[ from ] < startTime ) { - - ++ from; - - } - - while ( to !== - 1 && times[ to ] > endTime ) { - - -- to; - - } - - ++ to; // inclusive -> exclusive bound - - if ( from !== 0 || to !== nKeys ) { - - // empty tracks are forbidden, so keep at least one keyframe - if ( from >= to ) { - - to = Math.max( to, 1 ); - from = to - 1; - - } - - const stride = this.getValueSize(); - this.times = AnimationUtils.arraySlice( times, from, to ); - this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); - - } - - return this; - - }, - - // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable - validate: function () { - - let valid = true; - - const valueSize = this.getValueSize(); - if ( valueSize - Math.floor( valueSize ) !== 0 ) { - - console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); - valid = false; - - } - - const times = this.times, - values = this.values, - - nKeys = times.length; - - if ( nKeys === 0 ) { - - console.error( 'THREE.KeyframeTrack: Track is empty.', this ); - valid = false; - - } - - let prevTime = null; - - for ( let i = 0; i !== nKeys; i ++ ) { - - const currTime = times[ i ]; - - if ( typeof currTime === 'number' && isNaN( currTime ) ) { - - console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); - valid = false; - break; - - } - - if ( prevTime !== null && prevTime > currTime ) { - - console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); - valid = false; - break; - - } - - prevTime = currTime; - - } - - if ( values !== undefined ) { - - if ( AnimationUtils.isTypedArray( values ) ) { - - for ( let i = 0, n = values.length; i !== n; ++ i ) { - - const value = values[ i ]; - - if ( isNaN( value ) ) { - - console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); - valid = false; - break; - - } - - } - - } - - } - - return valid; - - }, - - // removes equivalent sequential keys as common in morph target sequences - // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) - optimize: function () { - - // times or values may be shared with other tracks, so overwriting is unsafe - const times = AnimationUtils.arraySlice( this.times ), - values = AnimationUtils.arraySlice( this.values ), - stride = this.getValueSize(), - - smoothInterpolation = this.getInterpolation() === InterpolateSmooth, - - lastIndex = times.length - 1; - - let writeIndex = 1; - - for ( let i = 1; i < lastIndex; ++ i ) { - - let keep = false; - - const time = times[ i ]; - const timeNext = times[ i + 1 ]; - - // remove adjacent keyframes scheduled at the same time - - if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { - - if ( ! smoothInterpolation ) { - - // remove unnecessary keyframes same as their neighbors - - const offset = i * stride, - offsetP = offset - stride, - offsetN = offset + stride; - - for ( let j = 0; j !== stride; ++ j ) { - - const value = values[ offset + j ]; - - if ( value !== values[ offsetP + j ] || - value !== values[ offsetN + j ] ) { - - keep = true; - break; - - } - - } - - } else { - - keep = true; - - } - - } - - // in-place compaction - - if ( keep ) { - - if ( i !== writeIndex ) { - - times[ writeIndex ] = times[ i ]; - - const readOffset = i * stride, - writeOffset = writeIndex * stride; - - for ( let j = 0; j !== stride; ++ j ) { - - values[ writeOffset + j ] = values[ readOffset + j ]; - - } - - } - - ++ writeIndex; - - } - - } - - // flush last keyframe (compaction looks ahead) - - if ( lastIndex > 0 ) { - - times[ writeIndex ] = times[ lastIndex ]; - - for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { - - values[ writeOffset + j ] = values[ readOffset + j ]; - - } - - ++ writeIndex; - - } - - if ( writeIndex !== times.length ) { - - this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); - this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); - - } else { - - this.times = times; - this.values = values; - - } - - return this; - - }, - - clone: function () { - - const times = AnimationUtils.arraySlice( this.times, 0 ); - const values = AnimationUtils.arraySlice( this.values, 0 ); - - const TypedKeyframeTrack = this.constructor; - const track = new TypedKeyframeTrack( this.name, times, values ); - - // Interpolant argument to constructor is not saved, so copy the factory method directly. - track.createInterpolant = this.createInterpolant; - - return track; - - } - - } ); - - /** - * A Track of Boolean keyframe values. - */ - - function BooleanKeyframeTrack( name, times, values ) { - - KeyframeTrack.call( this, name, times, values ); - - } - - BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: BooleanKeyframeTrack, - - ValueTypeName: 'bool', - ValueBufferType: Array, - - DefaultInterpolation: InterpolateDiscrete, - - InterpolantFactoryMethodLinear: undefined, - InterpolantFactoryMethodSmooth: undefined - - // Note: Actually this track could have a optimized / compressed - // representation of a single value and a custom interpolant that - // computes "firstValue ^ isOdd( index )". - - } ); - - /** - * A Track of keyframe values that represent color. - */ - - function ColorKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: ColorKeyframeTrack, - - ValueTypeName: 'color' - - // ValueBufferType is inherited - - // DefaultInterpolation is inherited - - // Note: Very basic implementation and nothing special yet. - // However, this is the place for color space parameterization. - - } ); - - /** - * A Track of numeric keyframe values. - */ - - function NumberKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: NumberKeyframeTrack, - - ValueTypeName: 'number' - - // ValueBufferType is inherited - - // DefaultInterpolation is inherited - - } ); - - /** - * Spherical linear unit quaternion interpolant. - */ - - function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: QuaternionLinearInterpolant, - - interpolate_: function ( i1, t0, t, t1 ) { - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - alpha = ( t - t0 ) / ( t1 - t0 ); - - let offset = i1 * stride; - - for ( let end = offset + stride; offset !== end; offset += 4 ) { - - Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); - - } - - return result; - - } - - } ); - - /** - * A Track of quaternion keyframe values. - */ - - function QuaternionKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: QuaternionKeyframeTrack, - - ValueTypeName: 'quaternion', - - // ValueBufferType is inherited - - DefaultInterpolation: InterpolateLinear, - - InterpolantFactoryMethodLinear: function ( result ) { - - return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); - - }, - - InterpolantFactoryMethodSmooth: undefined // not yet implemented - - } ); - - /** - * A Track that interpolates Strings - */ - - function StringKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: StringKeyframeTrack, - - ValueTypeName: 'string', - ValueBufferType: Array, - - DefaultInterpolation: InterpolateDiscrete, - - InterpolantFactoryMethodLinear: undefined, - - InterpolantFactoryMethodSmooth: undefined - - } ); - - /** - * A Track of vectored keyframe values. - */ - - function VectorKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: VectorKeyframeTrack, - - ValueTypeName: 'vector' - - // ValueBufferType is inherited - - // DefaultInterpolation is inherited - - } ); - - function AnimationClip( name, duration, tracks, blendMode ) { - - this.name = name; - this.tracks = tracks; - this.duration = ( duration !== undefined ) ? duration : - 1; - this.blendMode = ( blendMode !== undefined ) ? blendMode : NormalAnimationBlendMode; - - this.uuid = MathUtils.generateUUID(); - - // this means it should figure out its duration by scanning the tracks - if ( this.duration < 0 ) { - - this.resetDuration(); - - } - - } - - function getTrackTypeForValueTypeName( typeName ) { - - switch ( typeName.toLowerCase() ) { - - case 'scalar': - case 'double': - case 'float': - case 'number': - case 'integer': - - return NumberKeyframeTrack; - - case 'vector': - case 'vector2': - case 'vector3': - case 'vector4': - - return VectorKeyframeTrack; - - case 'color': - - return ColorKeyframeTrack; - - case 'quaternion': - - return QuaternionKeyframeTrack; - - case 'bool': - case 'boolean': - - return BooleanKeyframeTrack; - - case 'string': - - return StringKeyframeTrack; - - } - - throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); - - } - - function parseKeyframeTrack( json ) { - - if ( json.type === undefined ) { - - throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); - - } - - const trackType = getTrackTypeForValueTypeName( json.type ); - - if ( json.times === undefined ) { - - const times = [], values = []; - - AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); - - json.times = times; - json.values = values; - - } - - // derived classes can define a static parse method - if ( trackType.parse !== undefined ) { - - return trackType.parse( json ); - - } else { - - // by default, we assume a constructor compatible with the base - return new trackType( json.name, json.times, json.values, json.interpolation ); - - } - - } - - Object.assign( AnimationClip, { - - parse: function ( json ) { - - const tracks = [], - jsonTracks = json.tracks, - frameTime = 1.0 / ( json.fps || 1.0 ); - - for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { - - tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); - - } - - return new AnimationClip( json.name, json.duration, tracks, json.blendMode ); - - }, - - toJSON: function ( clip ) { - - const tracks = [], - clipTracks = clip.tracks; - - const json = { - - 'name': clip.name, - 'duration': clip.duration, - 'tracks': tracks, - 'uuid': clip.uuid, - 'blendMode': clip.blendMode - - }; - - for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { - - tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); - - } - - return json; - - }, - - CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) { - - const numMorphTargets = morphTargetSequence.length; - const tracks = []; - - for ( let i = 0; i < numMorphTargets; i ++ ) { - - let times = []; - let values = []; - - times.push( - ( i + numMorphTargets - 1 ) % numMorphTargets, - i, - ( i + 1 ) % numMorphTargets ); - - values.push( 0, 1, 0 ); - - const order = AnimationUtils.getKeyframeOrder( times ); - times = AnimationUtils.sortedArray( times, 1, order ); - values = AnimationUtils.sortedArray( values, 1, order ); - - // if there is a key at the first frame, duplicate it as the - // last frame as well for perfect loop. - if ( ! noLoop && times[ 0 ] === 0 ) { - - times.push( numMorphTargets ); - values.push( values[ 0 ] ); - - } - - tracks.push( - new NumberKeyframeTrack( - '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', - times, values - ).scale( 1.0 / fps ) ); - - } - - return new AnimationClip( name, - 1, tracks ); - - }, - - findByName: function ( objectOrClipArray, name ) { - - let clipArray = objectOrClipArray; - - if ( ! Array.isArray( objectOrClipArray ) ) { - - const o = objectOrClipArray; - clipArray = o.geometry && o.geometry.animations || o.animations; - - } - - for ( let i = 0; i < clipArray.length; i ++ ) { - - if ( clipArray[ i ].name === name ) { - - return clipArray[ i ]; - - } - - } - - return null; - - }, - - CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) { - - const animationToMorphTargets = {}; - - // tested with https://regex101.com/ on trick sequences - // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 - const pattern = /^([\w-]*?)([\d]+)$/; - - // sort morph target names into animation groups based - // patterns like Walk_001, Walk_002, Run_001, Run_002 - for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { - - const morphTarget = morphTargets[ i ]; - const parts = morphTarget.name.match( pattern ); - - if ( parts && parts.length > 1 ) { - - const name = parts[ 1 ]; - - let animationMorphTargets = animationToMorphTargets[ name ]; - - if ( ! animationMorphTargets ) { - - animationToMorphTargets[ name ] = animationMorphTargets = []; - - } - - animationMorphTargets.push( morphTarget ); - - } - - } - - const clips = []; - - for ( const name in animationToMorphTargets ) { - - clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); - - } - - return clips; - - }, - - // parse the animation.hierarchy format - parseAnimation: function ( animation, bones ) { - - if ( ! animation ) { - - console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); - return null; - - } - - const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { - - // only return track if there are actually keys. - if ( animationKeys.length !== 0 ) { - - const times = []; - const values = []; - - AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); - - // empty keys are filtered out, so check again - if ( times.length !== 0 ) { - - destTracks.push( new trackType( trackName, times, values ) ); - - } - - } - - }; - - const tracks = []; - - const clipName = animation.name || 'default'; - const fps = animation.fps || 30; - const blendMode = animation.blendMode; - - // automatic length determination in AnimationClip. - let duration = animation.length || - 1; - - const hierarchyTracks = animation.hierarchy || []; - - for ( let h = 0; h < hierarchyTracks.length; h ++ ) { - - const animationKeys = hierarchyTracks[ h ].keys; - - // skip empty tracks - if ( ! animationKeys || animationKeys.length === 0 ) continue; - - // process morph targets - if ( animationKeys[ 0 ].morphTargets ) { - - // figure out all morph targets used in this track - const morphTargetNames = {}; - - let k; - - for ( k = 0; k < animationKeys.length; k ++ ) { - - if ( animationKeys[ k ].morphTargets ) { - - for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { - - morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; - - } - - } - - } - - // create a track for each morph target with all zero - // morphTargetInfluences except for the keys in which - // the morphTarget is named. - for ( const morphTargetName in morphTargetNames ) { - - const times = []; - const values = []; - - for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { - - const animationKey = animationKeys[ k ]; - - times.push( animationKey.time ); - values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); - - } - - tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); - - } - - duration = morphTargetNames.length * ( fps || 1.0 ); - - } else { - - // ...assume skeletal animation - - const boneName = '.bones[' + bones[ h ].name + ']'; - - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.position', - animationKeys, 'pos', tracks ); - - addNonemptyTrack( - QuaternionKeyframeTrack, boneName + '.quaternion', - animationKeys, 'rot', tracks ); - - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.scale', - animationKeys, 'scl', tracks ); - - } - - } - - if ( tracks.length === 0 ) { - - return null; - - } - - const clip = new AnimationClip( clipName, duration, tracks, blendMode ); - - return clip; - - } - - } ); - - Object.assign( AnimationClip.prototype, { - - resetDuration: function () { - - const tracks = this.tracks; - let duration = 0; - - for ( let i = 0, n = tracks.length; i !== n; ++ i ) { - - const track = this.tracks[ i ]; - - duration = Math.max( duration, track.times[ track.times.length - 1 ] ); - - } - - this.duration = duration; - - return this; - - }, - - trim: function () { - - for ( let i = 0; i < this.tracks.length; i ++ ) { - - this.tracks[ i ].trim( 0, this.duration ); - - } - - return this; - - }, - - validate: function () { - - let valid = true; - - for ( let i = 0; i < this.tracks.length; i ++ ) { - - valid = valid && this.tracks[ i ].validate(); - - } - - return valid; - - }, - - optimize: function () { - - for ( let i = 0; i < this.tracks.length; i ++ ) { - - this.tracks[ i ].optimize(); - - } - - return this; - - }, - - clone: function () { - - const tracks = []; - - for ( let i = 0; i < this.tracks.length; i ++ ) { - - tracks.push( this.tracks[ i ].clone() ); - - } - - return new AnimationClip( this.name, this.duration, tracks, this.blendMode ); - - } - - } ); - - const Cache = { - - enabled: false, - - files: {}, - - add: function ( key, file ) { - - if ( this.enabled === false ) return; - - // console.log( 'THREE.Cache', 'Adding key:', key ); - - this.files[ key ] = file; - - }, - - get: function ( key ) { - - if ( this.enabled === false ) return; - - // console.log( 'THREE.Cache', 'Checking key:', key ); - - return this.files[ key ]; - - }, - - remove: function ( key ) { - - delete this.files[ key ]; - - }, - - clear: function () { - - this.files = {}; - - } - - }; - - function LoadingManager( onLoad, onProgress, onError ) { - - const scope = this; - - let isLoading = false; - let itemsLoaded = 0; - let itemsTotal = 0; - let urlModifier = undefined; - const handlers = []; - - // Refer to #5689 for the reason why we don't set .onStart - // in the constructor - - this.onStart = undefined; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; - - this.itemStart = function ( url ) { - - itemsTotal ++; - - if ( isLoading === false ) { - - if ( scope.onStart !== undefined ) { - - scope.onStart( url, itemsLoaded, itemsTotal ); - - } - - } - - isLoading = true; - - }; - - this.itemEnd = function ( url ) { - - itemsLoaded ++; - - if ( scope.onProgress !== undefined ) { - - scope.onProgress( url, itemsLoaded, itemsTotal ); - - } - - if ( itemsLoaded === itemsTotal ) { - - isLoading = false; - - if ( scope.onLoad !== undefined ) { - - scope.onLoad(); - - } - - } - - }; - - this.itemError = function ( url ) { - - if ( scope.onError !== undefined ) { - - scope.onError( url ); - - } - - }; - - this.resolveURL = function ( url ) { - - if ( urlModifier ) { - - return urlModifier( url ); - - } - - return url; - - }; - - this.setURLModifier = function ( transform ) { - - urlModifier = transform; - - return this; - - }; - - this.addHandler = function ( regex, loader ) { - - handlers.push( regex, loader ); - - return this; - - }; - - this.removeHandler = function ( regex ) { - - const index = handlers.indexOf( regex ); - - if ( index !== - 1 ) { - - handlers.splice( index, 2 ); - - } - - return this; - - }; - - this.getHandler = function ( file ) { - - for ( let i = 0, l = handlers.length; i < l; i += 2 ) { - - const regex = handlers[ i ]; - const loader = handlers[ i + 1 ]; - - if ( regex.global ) regex.lastIndex = 0; // see #17920 - - if ( regex.test( file ) ) { - - return loader; - - } - - } - - return null; - - }; - - } - - const DefaultLoadingManager = new LoadingManager(); - - function Loader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - this.crossOrigin = 'anonymous'; - this.path = ''; - this.resourcePath = ''; - this.requestHeader = {}; - - } - - Object.assign( Loader.prototype, { - - load: function ( /* url, onLoad, onProgress, onError */ ) {}, - - loadAsync: function ( url, onProgress ) { - - const scope = this; - - return new Promise( function ( resolve, reject ) { - - scope.load( url, resolve, onProgress, reject ); - - } ); - - }, - - parse: function ( /* data */ ) {}, - - setCrossOrigin: function ( crossOrigin ) { - - this.crossOrigin = crossOrigin; - return this; - - }, - - setPath: function ( path ) { - - this.path = path; - return this; - - }, - - setResourcePath: function ( resourcePath ) { - - this.resourcePath = resourcePath; - return this; - - }, - - setRequestHeader: function ( requestHeader ) { - - this.requestHeader = requestHeader; - return this; - - } - - } ); - - const loading = {}; - - function FileLoader( manager ) { - - Loader.call( this, manager ); - - } - - FileLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: FileLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - if ( url === undefined ) url = ''; - - if ( this.path !== undefined ) url = this.path + url; - - url = this.manager.resolveURL( url ); - - const scope = this; - - const cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) onLoad( cached ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - // Check if request is duplicate - - if ( loading[ url ] !== undefined ) { - - loading[ url ].push( { - - onLoad: onLoad, - onProgress: onProgress, - onError: onError - - } ); - - return; - - } - - // Check for data: URI - const dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; - const dataUriRegexResult = url.match( dataUriRegex ); - let request; - - // Safari can not handle Data URIs through XMLHttpRequest so process manually - if ( dataUriRegexResult ) { - - const mimeType = dataUriRegexResult[ 1 ]; - const isBase64 = !! dataUriRegexResult[ 2 ]; - - let data = dataUriRegexResult[ 3 ]; - data = decodeURIComponent( data ); - - if ( isBase64 ) data = atob( data ); - - try { - - let response; - const responseType = ( this.responseType || '' ).toLowerCase(); - - switch ( responseType ) { - - case 'arraybuffer': - case 'blob': - - const view = new Uint8Array( data.length ); - - for ( let i = 0; i < data.length; i ++ ) { - - view[ i ] = data.charCodeAt( i ); - - } - - if ( responseType === 'blob' ) { - - response = new Blob( [ view.buffer ], { type: mimeType } ); - - } else { - - response = view.buffer; - - } - - break; - - case 'document': - - const parser = new DOMParser(); - response = parser.parseFromString( data, mimeType ); - - break; - - case 'json': - - response = JSON.parse( data ); - - break; - - default: // 'text' or other - - response = data; - - break; - - } - - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { - - if ( onLoad ) onLoad( response ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - } catch ( error ) { - - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { - - if ( onError ) onError( error ); - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, 0 ); - - } - - } else { - - // Initialise array for duplicate requests - - loading[ url ] = []; - - loading[ url ].push( { - - onLoad: onLoad, - onProgress: onProgress, - onError: onError - - } ); - - request = new XMLHttpRequest(); - - request.open( 'GET', url, true ); - - request.addEventListener( 'load', function ( event ) { - - const response = this.response; - - const callbacks = loading[ url ]; - - delete loading[ url ]; - - if ( this.status === 200 || this.status === 0 ) { - - // Some browsers return HTTP Status 0 when using non-http protocol - // e.g. 'file://' or 'data://'. Handle as success. - - if ( this.status === 0 ) console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); - - // Add to cache only on HTTP success, so that we do not cache - // error response bodies as proper responses to requests. - Cache.add( url, response ); - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onLoad ) callback.onLoad( response ); - - } - - scope.manager.itemEnd( url ); - - } else { - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } - - }, false ); - - request.addEventListener( 'progress', function ( event ) { - - const callbacks = loading[ url ]; - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onProgress ) callback.onProgress( event ); - - } - - }, false ); - - request.addEventListener( 'error', function ( event ) { - - const callbacks = loading[ url ]; - - delete loading[ url ]; - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, false ); - - request.addEventListener( 'abort', function ( event ) { - - const callbacks = loading[ url ]; - - delete loading[ url ]; - - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, false ); - - if ( this.responseType !== undefined ) request.responseType = this.responseType; - if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; - - if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); - - for ( const header in this.requestHeader ) { - - request.setRequestHeader( header, this.requestHeader[ header ] ); - - } - - request.send( null ); - - } - - scope.manager.itemStart( url ); - - return request; - - }, - - setResponseType: function ( value ) { - - this.responseType = value; - return this; - - }, - - setWithCredentials: function ( value ) { - - this.withCredentials = value; - return this; - - }, - - setMimeType: function ( value ) { - - this.mimeType = value; - return this; - - } - - } ); - - function AnimationLoader( manager ) { - - Loader.call( this, manager ); - - } - - AnimationLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: AnimationLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.load( url, function ( text ) { - - try { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - const animations = []; - - for ( let i = 0; i < json.length; i ++ ) { - - const clip = AnimationClip.parse( json[ i ] ); - - animations.push( clip ); - - } - - return animations; - - } - - } ); - - /** - * Abstract Base class to block based textures loader (dds, pvr, ...) - * - * Sub classes have to implement the parse() method which will be used in load(). - */ - - function CompressedTextureLoader( manager ) { - - Loader.call( this, manager ); - - } - - CompressedTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: CompressedTextureLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const images = []; - - const texture = new CompressedTexture(); - texture.image = images; - - const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - - let loaded = 0; - - function loadTexture( i ) { - - loader.load( url[ i ], function ( buffer ) { - - const texDatas = scope.parse( buffer, true ); - - images[ i ] = { - width: texDatas.width, - height: texDatas.height, - format: texDatas.format, - mipmaps: texDatas.mipmaps - }; - - loaded += 1; - - if ( loaded === 6 ) { - - if ( texDatas.mipmapCount === 1 ) - texture.minFilter = LinearFilter; - - texture.format = texDatas.format; - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - } - - }, onProgress, onError ); - - } - - if ( Array.isArray( url ) ) { - - for ( let i = 0, il = url.length; i < il; ++ i ) { - - loadTexture( i ); - - } - - } else { - - // compressed cubemap texture stored in a single DDS file - - loader.load( url, function ( buffer ) { - - const texDatas = scope.parse( buffer, true ); - - if ( texDatas.isCubemap ) { - - const faces = texDatas.mipmaps.length / texDatas.mipmapCount; - - for ( let f = 0; f < faces; f ++ ) { - - images[ f ] = { mipmaps: [] }; - - for ( let i = 0; i < texDatas.mipmapCount; i ++ ) { - - images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); - images[ f ].format = texDatas.format; - images[ f ].width = texDatas.width; - images[ f ].height = texDatas.height; - - } - - } - - } else { - - texture.image.width = texDatas.width; - texture.image.height = texDatas.height; - texture.mipmaps = texDatas.mipmaps; - - } - - if ( texDatas.mipmapCount === 1 ) { - - texture.minFilter = LinearFilter; - - } - - texture.format = texDatas.format; - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - }, onProgress, onError ); - - } - - return texture; - - } - - } ); - - function ImageLoader( manager ) { - - Loader.call( this, manager ); - - } - - ImageLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: ImageLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - if ( this.path !== undefined ) url = this.path + url; - - url = this.manager.resolveURL( url ); - - const scope = this; - - const cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) onLoad( cached ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - const image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); - - function onImageLoad() { - - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); - - Cache.add( url, this ); - - if ( onLoad ) onLoad( this ); - - scope.manager.itemEnd( url ); - - } - - function onImageError( event ) { - - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); - - if ( onError ) onError( event ); - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } - - image.addEventListener( 'load', onImageLoad, false ); - image.addEventListener( 'error', onImageError, false ); - - if ( url.substr( 0, 5 ) !== 'data:' ) { - - if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; - - } - - scope.manager.itemStart( url ); - - image.src = url; - - return image; - - } - - } ); - - function CubeTextureLoader( manager ) { - - Loader.call( this, manager ); - - } - - CubeTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: CubeTextureLoader, - - load: function ( urls, onLoad, onProgress, onError ) { - - const texture = new CubeTexture(); - - const loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); - - let loaded = 0; - - function loadTexture( i ) { - - loader.load( urls[ i ], function ( image ) { - - texture.images[ i ] = image; - - loaded ++; - - if ( loaded === 6 ) { - - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - } - - }, undefined, onError ); - - } - - for ( let i = 0; i < urls.length; ++ i ) { - - loadTexture( i ); - - } - - return texture; - - } - - } ); - - /** - * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) - * - * Sub classes have to implement the parse() method which will be used in load(). - */ - - function DataTextureLoader( manager ) { - - Loader.call( this, manager ); - - } - - DataTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: DataTextureLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const texture = new DataTexture(); - - const loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setPath( this.path ); - loader.load( url, function ( buffer ) { - - const texData = scope.parse( buffer ); - - if ( ! texData ) return; - - if ( texData.image !== undefined ) { - - texture.image = texData.image; - - } else if ( texData.data !== undefined ) { - - texture.image.width = texData.width; - texture.image.height = texData.height; - texture.image.data = texData.data; - - } - - texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; - texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; - - texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; - texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; - - texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; - - if ( texData.format !== undefined ) { - - texture.format = texData.format; - - } - - if ( texData.type !== undefined ) { - - texture.type = texData.type; - - } - - if ( texData.mipmaps !== undefined ) { - - texture.mipmaps = texData.mipmaps; - texture.minFilter = LinearMipmapLinearFilter; // presumably... - - } - - if ( texData.mipmapCount === 1 ) { - - texture.minFilter = LinearFilter; - - } - - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture, texData ); - - }, onProgress, onError ); - - - return texture; - - } - - } ); - - function TextureLoader( manager ) { - - Loader.call( this, manager ); - - } - - TextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: TextureLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const texture = new Texture(); - - const loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); - - loader.load( url, function ( image ) { - - texture.image = image; - - // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. - const isJPEG = url.search( /\.jpe?g($|\?)/i ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; - - texture.format = isJPEG ? RGBFormat : RGBAFormat; - texture.needsUpdate = true; - - if ( onLoad !== undefined ) { - - onLoad( texture ); - - } - - }, onProgress, onError ); - - return texture; - - } - - } ); - - /** - * Extensible curve object. - * - * Some common of curve methods: - * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) - * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) - * .getPoints(), .getSpacedPoints() - * .getLength() - * .updateArcLengths() - * - * This following curves inherit from THREE.Curve: - * - * -- 2D curves -- - * THREE.ArcCurve - * THREE.CubicBezierCurve - * THREE.EllipseCurve - * THREE.LineCurve - * THREE.QuadraticBezierCurve - * THREE.SplineCurve - * - * -- 3D curves -- - * THREE.CatmullRomCurve3 - * THREE.CubicBezierCurve3 - * THREE.LineCurve3 - * THREE.QuadraticBezierCurve3 - * - * A series of curves can be represented as a THREE.CurvePath. - * - **/ - - function Curve() { - - this.type = 'Curve'; - - this.arcLengthDivisions = 200; - - } - - Object.assign( Curve.prototype, { - - // Virtual base class method to overwrite and implement in subclasses - // - t [0 .. 1] - - getPoint: function ( /* t, optionalTarget */ ) { - - console.warn( 'THREE.Curve: .getPoint() not implemented.' ); - return null; - - }, - - // Get point at relative position in curve according to arc length - // - u [0 .. 1] - - getPointAt: function ( u, optionalTarget ) { - - const t = this.getUtoTmapping( u ); - return this.getPoint( t, optionalTarget ); - - }, - - // Get sequence of points using getPoint( t ) - - getPoints: function ( divisions ) { - - if ( divisions === undefined ) divisions = 5; - - const points = []; - - for ( let d = 0; d <= divisions; d ++ ) { - - points.push( this.getPoint( d / divisions ) ); - - } - - return points; - - }, - - // Get sequence of points using getPointAt( u ) - - getSpacedPoints: function ( divisions ) { - - if ( divisions === undefined ) divisions = 5; - - const points = []; - - for ( let d = 0; d <= divisions; d ++ ) { - - points.push( this.getPointAt( d / divisions ) ); - - } - - return points; - - }, - - // Get total curve arc length - - getLength: function () { - - const lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; - - }, - - // Get list of cumulative segment lengths - - getLengths: function ( divisions ) { - - if ( divisions === undefined ) divisions = this.arcLengthDivisions; - - if ( this.cacheArcLengths && - ( this.cacheArcLengths.length === divisions + 1 ) && - ! this.needsUpdate ) { - - return this.cacheArcLengths; - - } - - this.needsUpdate = false; - - const cache = []; - let current, last = this.getPoint( 0 ); - let sum = 0; - - cache.push( 0 ); - - for ( let p = 1; p <= divisions; p ++ ) { - - current = this.getPoint( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; - - } - - this.cacheArcLengths = cache; - - return cache; // { sums: cache, sum: sum }; Sum is in the last element. - - }, - - updateArcLengths: function () { - - this.needsUpdate = true; - this.getLengths(); - - }, - - // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - - getUtoTmapping: function ( u, distance ) { - - const arcLengths = this.getLengths(); - - let i = 0; - const il = arcLengths.length; - - let targetArcLength; // The targeted u distance value to get - - if ( distance ) { - - targetArcLength = distance; - - } else { - - targetArcLength = u * arcLengths[ il - 1 ]; - - } - - // binary search for the index with largest value smaller than target u distance - - let low = 0, high = il - 1, comparison; - - while ( low <= high ) { - - i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats - - comparison = arcLengths[ i ] - targetArcLength; - - if ( comparison < 0 ) { - - low = i + 1; - - } else if ( comparison > 0 ) { - - high = i - 1; - - } else { - - high = i; - break; - - // DONE - - } - - } - - i = high; - - if ( arcLengths[ i ] === targetArcLength ) { - - return i / ( il - 1 ); - - } - - // we could get finer grain at lengths, or use simple interpolation between two points - - const lengthBefore = arcLengths[ i ]; - const lengthAfter = arcLengths[ i + 1 ]; - - const segmentLength = lengthAfter - lengthBefore; - - // determine where we are between the 'before' and 'after' points - - const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; - - // add that fractional amount to t - - const t = ( i + segmentFraction ) / ( il - 1 ); - - return t; - - }, - - // Returns a unit vector tangent at t - // In case any sub curve does not implement its tangent derivation, - // 2 points a small delta apart will be used to find its gradient - // which seems to give a reasonable approximation - - getTangent: function ( t, optionalTarget ) { - - const delta = 0.0001; - let t1 = t - delta; - let t2 = t + delta; - - // Capping in case of danger - - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; - - const pt1 = this.getPoint( t1 ); - const pt2 = this.getPoint( t2 ); - - const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); - - tangent.copy( pt2 ).sub( pt1 ).normalize(); - - return tangent; - - }, - - getTangentAt: function ( u, optionalTarget ) { - - const t = this.getUtoTmapping( u ); - return this.getTangent( t, optionalTarget ); - - }, - - computeFrenetFrames: function ( segments, closed ) { - - // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf - - const normal = new Vector3(); - - const tangents = []; - const normals = []; - const binormals = []; - - const vec = new Vector3(); - const mat = new Matrix4(); - - // compute the tangent vectors for each segment on the curve - - for ( let i = 0; i <= segments; i ++ ) { - - const u = i / segments; - - tangents[ i ] = this.getTangentAt( u, new Vector3() ); - tangents[ i ].normalize(); - - } - - // select an initial normal vector perpendicular to the first tangent vector, - // and in the direction of the minimum tangent xyz component - - normals[ 0 ] = new Vector3(); - binormals[ 0 ] = new Vector3(); - let min = Number.MAX_VALUE; - const tx = Math.abs( tangents[ 0 ].x ); - const ty = Math.abs( tangents[ 0 ].y ); - const tz = Math.abs( tangents[ 0 ].z ); - - if ( tx <= min ) { - - min = tx; - normal.set( 1, 0, 0 ); - - } - - if ( ty <= min ) { - - min = ty; - normal.set( 0, 1, 0 ); - - } - - if ( tz <= min ) { - - normal.set( 0, 0, 1 ); - - } - - vec.crossVectors( tangents[ 0 ], normal ).normalize(); - - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - - - // compute the slowly-varying normal and binormal vectors for each segment on the curve - - for ( let i = 1; i <= segments; i ++ ) { - - normals[ i ] = normals[ i - 1 ].clone(); - - binormals[ i ] = binormals[ i - 1 ].clone(); - - vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - - if ( vec.length() > Number.EPSILON ) { - - vec.normalize(); - - const theta = Math.acos( MathUtils.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); - - } - - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - - } - - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - - if ( closed === true ) { - - let theta = Math.acos( MathUtils.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); - theta /= segments; - - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { - - theta = - theta; - - } - - for ( let i = 1; i <= segments; i ++ ) { - - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - - } - - } - - return { - tangents: tangents, - normals: normals, - binormals: binormals - }; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.arcLengthDivisions = source.arcLengthDivisions; - - return this; - - }, - - toJSON: function () { - - const data = { - metadata: { - version: 4.5, - type: 'Curve', - generator: 'Curve.toJSON' - } - }; - - data.arcLengthDivisions = this.arcLengthDivisions; - data.type = this.type; - - return data; - - }, - - fromJSON: function ( json ) { - - this.arcLengthDivisions = json.arcLengthDivisions; - - return this; - - } - - } ); - - function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - Curve.call( this ); - - this.type = 'EllipseCurve'; - - this.aX = aX || 0; - this.aY = aY || 0; - - this.xRadius = xRadius || 1; - this.yRadius = yRadius || 1; - - this.aStartAngle = aStartAngle || 0; - this.aEndAngle = aEndAngle || 2 * Math.PI; - - this.aClockwise = aClockwise || false; - - this.aRotation = aRotation || 0; - - } - - EllipseCurve.prototype = Object.create( Curve.prototype ); - EllipseCurve.prototype.constructor = EllipseCurve; - - EllipseCurve.prototype.isEllipseCurve = true; - - EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector2(); - - const twoPi = Math.PI * 2; - let deltaAngle = this.aEndAngle - this.aStartAngle; - const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; - - // ensures that deltaAngle is 0 .. 2 PI - while ( deltaAngle < 0 ) deltaAngle += twoPi; - while ( deltaAngle > twoPi ) deltaAngle -= twoPi; - - if ( deltaAngle < Number.EPSILON ) { - - if ( samePoints ) { - - deltaAngle = 0; - - } else { - - deltaAngle = twoPi; - - } - - } - - if ( this.aClockwise === true && ! samePoints ) { - - if ( deltaAngle === twoPi ) { - - deltaAngle = - twoPi; - - } else { - - deltaAngle = deltaAngle - twoPi; - - } - - } - - const angle = this.aStartAngle + t * deltaAngle; - let x = this.aX + this.xRadius * Math.cos( angle ); - let y = this.aY + this.yRadius * Math.sin( angle ); - - if ( this.aRotation !== 0 ) { - - const cos = Math.cos( this.aRotation ); - const sin = Math.sin( this.aRotation ); - - const tx = x - this.aX; - const ty = y - this.aY; - - // Rotate the point about the center of the ellipse. - x = tx * cos - ty * sin + this.aX; - y = tx * sin + ty * cos + this.aY; - - } - - return point.set( x, y ); - - }; - - EllipseCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.aX = source.aX; - this.aY = source.aY; - - this.xRadius = source.xRadius; - this.yRadius = source.yRadius; - - this.aStartAngle = source.aStartAngle; - this.aEndAngle = source.aEndAngle; - - this.aClockwise = source.aClockwise; - - this.aRotation = source.aRotation; - - return this; - - }; - - - EllipseCurve.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.aX = this.aX; - data.aY = this.aY; - - data.xRadius = this.xRadius; - data.yRadius = this.yRadius; - - data.aStartAngle = this.aStartAngle; - data.aEndAngle = this.aEndAngle; - - data.aClockwise = this.aClockwise; - - data.aRotation = this.aRotation; - - return data; - - }; - - EllipseCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.aX = json.aX; - this.aY = json.aY; - - this.xRadius = json.xRadius; - this.yRadius = json.yRadius; - - this.aStartAngle = json.aStartAngle; - this.aEndAngle = json.aEndAngle; - - this.aClockwise = json.aClockwise; - - this.aRotation = json.aRotation; - - return this; - - }; - - function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - - this.type = 'ArcCurve'; - - } - - ArcCurve.prototype = Object.create( EllipseCurve.prototype ); - ArcCurve.prototype.constructor = ArcCurve; - - ArcCurve.prototype.isArcCurve = true; - - /** - * Centripetal CatmullRom Curve - which is useful for avoiding - * cusps and self-intersections in non-uniform catmull rom curves. - * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf - * - * curve.type accepts centripetal(default), chordal and catmullrom - * curve.tension is used for catmullrom which defaults to 0.5 - */ - - - /* - Based on an optimized c++ solution in - - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - - http://ideone.com/NoEbVM - - This CubicPoly class could be used for reusing some variables and calculations, - but for three.js curve use, it could be possible inlined and flatten into a single function call - which can be placed in CurveUtils. - */ - - function CubicPoly() { - - let c0 = 0, c1 = 0, c2 = 0, c3 = 0; - - /* - * Compute coefficients for a cubic polynomial - * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 - * such that - * p(0) = x0, p(1) = x1 - * and - * p'(0) = t0, p'(1) = t1. - */ - function init( x0, x1, t0, t1 ) { - - c0 = x0; - c1 = t0; - c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; - c3 = 2 * x0 - 2 * x1 + t0 + t1; - - } - - return { - - initCatmullRom: function ( x0, x1, x2, x3, tension ) { - - init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); - - }, - - initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { - - // compute tangents when parameterized in [t1,t2] - let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; - let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; - - // rescale tangents for parametrization in [0,1] - t1 *= dt1; - t2 *= dt1; - - init( x1, x2, t1, t2 ); - - }, - - calc: function ( t ) { - - const t2 = t * t; - const t3 = t2 * t; - return c0 + c1 * t + c2 * t2 + c3 * t3; - - } - - }; - - } - - // - - const tmp = new Vector3(); - const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); - - function CatmullRomCurve3( points, closed, curveType, tension ) { - - Curve.call( this ); - - this.type = 'CatmullRomCurve3'; - - this.points = points || []; - this.closed = closed || false; - this.curveType = curveType || 'centripetal'; - this.tension = ( tension !== undefined ) ? tension : 0.5; - - } - - CatmullRomCurve3.prototype = Object.create( Curve.prototype ); - CatmullRomCurve3.prototype.constructor = CatmullRomCurve3; - - CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; - - CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector3(); - - const points = this.points; - const l = points.length; - - const p = ( l - ( this.closed ? 0 : 1 ) ) * t; - let intPoint = Math.floor( p ); - let weight = p - intPoint; - - if ( this.closed ) { - - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; - - } else if ( weight === 0 && intPoint === l - 1 ) { - - intPoint = l - 2; - weight = 1; - - } - - let p0, p3; // 4 points (p1 & p2 defined below) - - if ( this.closed || intPoint > 0 ) { - - p0 = points[ ( intPoint - 1 ) % l ]; - - } else { - - // extrapolate first point - tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); - p0 = tmp; - - } - - const p1 = points[ intPoint % l ]; - const p2 = points[ ( intPoint + 1 ) % l ]; - - if ( this.closed || intPoint + 2 < l ) { - - p3 = points[ ( intPoint + 2 ) % l ]; - - } else { - - // extrapolate last point - tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); - p3 = tmp; - - } - - if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { - - // init Centripetal / Chordal Catmull-Rom - const pow = this.curveType === 'chordal' ? 0.5 : 0.25; - let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); - let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); - let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); - - // safety check for repeated points - if ( dt1 < 1e-4 ) dt1 = 1.0; - if ( dt0 < 1e-4 ) dt0 = dt1; - if ( dt2 < 1e-4 ) dt2 = dt1; - - px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); - py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); - pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); - - } else if ( this.curveType === 'catmullrom' ) { - - px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); - py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); - pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); - - } - - point.set( - px.calc( weight ), - py.calc( weight ), - pz.calc( weight ) - ); - - return point; - - }; - - CatmullRomCurve3.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.points = []; - - for ( let i = 0, l = source.points.length; i < l; i ++ ) { - - const point = source.points[ i ]; - - this.points.push( point.clone() ); - - } - - this.closed = source.closed; - this.curveType = source.curveType; - this.tension = source.tension; - - return this; - - }; - - CatmullRomCurve3.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.points = []; - - for ( let i = 0, l = this.points.length; i < l; i ++ ) { - - const point = this.points[ i ]; - data.points.push( point.toArray() ); - - } - - data.closed = this.closed; - data.curveType = this.curveType; - data.tension = this.tension; - - return data; - - }; - - CatmullRomCurve3.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.points = []; - - for ( let i = 0, l = json.points.length; i < l; i ++ ) { - - const point = json.points[ i ]; - this.points.push( new Vector3().fromArray( point ) ); - - } - - this.closed = json.closed; - this.curveType = json.curveType; - this.tension = json.tension; - - return this; - - }; - - /** - * Bezier Curves formulas obtained from - * http://en.wikipedia.org/wiki/Bézier_curve - */ - - function CatmullRom( t, p0, p1, p2, p3 ) { - - const v0 = ( p2 - p0 ) * 0.5; - const v1 = ( p3 - p1 ) * 0.5; - const t2 = t * t; - const t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; - - } - - // - - function QuadraticBezierP0( t, p ) { - - const k = 1 - t; - return k * k * p; - - } - - function QuadraticBezierP1( t, p ) { - - return 2 * ( 1 - t ) * t * p; - - } - - function QuadraticBezierP2( t, p ) { - - return t * t * p; - - } - - function QuadraticBezier( t, p0, p1, p2 ) { - - return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + - QuadraticBezierP2( t, p2 ); - - } - - // - - function CubicBezierP0( t, p ) { - - const k = 1 - t; - return k * k * k * p; - - } - - function CubicBezierP1( t, p ) { - - const k = 1 - t; - return 3 * k * k * t * p; - - } - - function CubicBezierP2( t, p ) { - - return 3 * ( 1 - t ) * t * t * p; - - } - - function CubicBezierP3( t, p ) { - - return t * t * t * p; - - } - - function CubicBezier( t, p0, p1, p2, p3 ) { - - return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + - CubicBezierP3( t, p3 ); - - } - - function CubicBezierCurve( v0, v1, v2, v3 ) { - - Curve.call( this ); - - this.type = 'CubicBezierCurve'; - - this.v0 = v0 || new Vector2(); - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); - this.v3 = v3 || new Vector2(); - - } - - CubicBezierCurve.prototype = Object.create( Curve.prototype ); - CubicBezierCurve.prototype.constructor = CubicBezierCurve; - - CubicBezierCurve.prototype.isCubicBezierCurve = true; - - CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector2(); - - const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) - ); - - return point; - - }; - - CubicBezierCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); - - return this; - - }; - - CubicBezierCurve.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); - - return data; - - }; - - CubicBezierCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); - - return this; - - }; - - function CubicBezierCurve3( v0, v1, v2, v3 ) { - - Curve.call( this ); - - this.type = 'CubicBezierCurve3'; - - this.v0 = v0 || new Vector3(); - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); - this.v3 = v3 || new Vector3(); - - } - - CubicBezierCurve3.prototype = Object.create( Curve.prototype ); - CubicBezierCurve3.prototype.constructor = CubicBezierCurve3; - - CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; - - CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector3(); - - const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), - CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) - ); - - return point; - - }; - - CubicBezierCurve3.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); - - return this; - - }; - - CubicBezierCurve3.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); - - return data; - - }; - - CubicBezierCurve3.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); - - return this; - - }; - - function LineCurve( v1, v2 ) { - - Curve.call( this ); - - this.type = 'LineCurve'; - - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); - - } - - LineCurve.prototype = Object.create( Curve.prototype ); - LineCurve.prototype.constructor = LineCurve; - - LineCurve.prototype.isLineCurve = true; - - LineCurve.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector2(); - - if ( t === 1 ) { - - point.copy( this.v2 ); - - } else { - - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); - - } - - return point; - - }; - - // Line curve is linear, so we can overwrite default getPointAt - - LineCurve.prototype.getPointAt = function ( u, optionalTarget ) { - - return this.getPoint( u, optionalTarget ); - - }; - - LineCurve.prototype.getTangent = function ( t, optionalTarget ) { - - const tangent = optionalTarget || new Vector2(); - - tangent.copy( this.v2 ).sub( this.v1 ).normalize(); - - return tangent; - - }; - - LineCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - }; - - LineCurve.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - }; - - LineCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - }; - - function LineCurve3( v1, v2 ) { - - Curve.call( this ); - - this.type = 'LineCurve3'; - - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); - - } - - LineCurve3.prototype = Object.create( Curve.prototype ); - LineCurve3.prototype.constructor = LineCurve3; - - LineCurve3.prototype.isLineCurve3 = true; - - LineCurve3.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector3(); - - if ( t === 1 ) { - - point.copy( this.v2 ); - - } else { - - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); - - } - - return point; - - }; - - // Line curve is linear, so we can overwrite default getPointAt - - LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) { - - return this.getPoint( u, optionalTarget ); - - }; - - LineCurve3.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - }; - - LineCurve3.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - }; - - LineCurve3.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - }; - - function QuadraticBezierCurve( v0, v1, v2 ) { - - Curve.call( this ); - - this.type = 'QuadraticBezierCurve'; - - this.v0 = v0 || new Vector2(); - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); - - } - - QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; - - QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; - - QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector2(); - - const v0 = this.v0, v1 = this.v1, v2 = this.v2; - - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ) - ); - - return point; - - }; - - QuadraticBezierCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - }; - - QuadraticBezierCurve.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - }; - - QuadraticBezierCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - }; - - function QuadraticBezierCurve3( v0, v1, v2 ) { - - Curve.call( this ); - - this.type = 'QuadraticBezierCurve3'; - - this.v0 = v0 || new Vector3(); - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); - - } - - QuadraticBezierCurve3.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3; - - QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; - - QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector3(); - - const v0 = this.v0, v1 = this.v1, v2 = this.v2; - - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ), - QuadraticBezier( t, v0.z, v1.z, v2.z ) - ); - - return point; - - }; - - QuadraticBezierCurve3.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - }; - - QuadraticBezierCurve3.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - }; - - QuadraticBezierCurve3.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - }; - - function SplineCurve( points ) { - - Curve.call( this ); - - this.type = 'SplineCurve'; - - this.points = points || []; - - } - - SplineCurve.prototype = Object.create( Curve.prototype ); - SplineCurve.prototype.constructor = SplineCurve; - - SplineCurve.prototype.isSplineCurve = true; - - SplineCurve.prototype.getPoint = function ( t, optionalTarget ) { - - const point = optionalTarget || new Vector2(); - - const points = this.points; - const p = ( points.length - 1 ) * t; - - const intPoint = Math.floor( p ); - const weight = p - intPoint; - - const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; - const p1 = points[ intPoint ]; - const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; - const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - - point.set( - CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), - CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) - ); - - return point; - - }; - - SplineCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.points = []; - - for ( let i = 0, l = source.points.length; i < l; i ++ ) { - - const point = source.points[ i ]; - - this.points.push( point.clone() ); - - } - - return this; - - }; - - SplineCurve.prototype.toJSON = function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.points = []; - - for ( let i = 0, l = this.points.length; i < l; i ++ ) { - - const point = this.points[ i ]; - data.points.push( point.toArray() ); - - } - - return data; - - }; - - SplineCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.points = []; - - for ( let i = 0, l = json.points.length; i < l; i ++ ) { - - const point = json.points[ i ]; - this.points.push( new Vector2().fromArray( point ) ); - - } - - return this; - - }; - - var Curves = /*#__PURE__*/Object.freeze({ - __proto__: null, - ArcCurve: ArcCurve, - CatmullRomCurve3: CatmullRomCurve3, - CubicBezierCurve: CubicBezierCurve, - CubicBezierCurve3: CubicBezierCurve3, - EllipseCurve: EllipseCurve, - LineCurve: LineCurve, - LineCurve3: LineCurve3, - QuadraticBezierCurve: QuadraticBezierCurve, - QuadraticBezierCurve3: QuadraticBezierCurve3, - SplineCurve: SplineCurve - }); - - /************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ - - function CurvePath() { - - Curve.call( this ); - - this.type = 'CurvePath'; - - this.curves = []; - this.autoClose = false; // Automatically closes the path - - } - - CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { - - constructor: CurvePath, - - add: function ( curve ) { - - this.curves.push( curve ); - - }, - - closePath: function () { - - // Add a line curve if start and end of lines are not connected - const startPoint = this.curves[ 0 ].getPoint( 0 ); - const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); - - if ( ! startPoint.equals( endPoint ) ) { - - this.curves.push( new LineCurve( endPoint, startPoint ) ); - - } - - }, - - // To get accurate point with reference to - // entire path distance at time t, - // following has to be done: - - // 1. Length of each sub path have to be known - // 2. Locate and identify type of curve - // 3. Get t for the curve - // 4. Return curve.getPointAt(t') - - getPoint: function ( t ) { - - const d = t * this.getLength(); - const curveLengths = this.getCurveLengths(); - let i = 0; - - // To think about boundaries points. - - while ( i < curveLengths.length ) { - - if ( curveLengths[ i ] >= d ) { - - const diff = curveLengths[ i ] - d; - const curve = this.curves[ i ]; - - const segmentLength = curve.getLength(); - const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; - - return curve.getPointAt( u ); - - } - - i ++; - - } - - return null; - - // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { - - points.push( points[ 0 ] ); - - } - - return points; - - }, - - copy: function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.curves = []; - - for ( let i = 0, l = source.curves.length; i < l; i ++ ) { - - const curve = source.curves[ i ]; - - this.curves.push( curve.clone() ); - - } - - this.autoClose = source.autoClose; - - return this; - - }, - - toJSON: function () { - - const data = Curve.prototype.toJSON.call( this ); - - data.autoClose = this.autoClose; - data.curves = []; - - for ( let i = 0, l = this.curves.length; i < l; i ++ ) { - - const curve = this.curves[ i ]; - data.curves.push( curve.toJSON() ); - - } - - return data; - - }, - - fromJSON: function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.autoClose = json.autoClose; - this.curves = []; - - for ( let i = 0, l = json.curves.length; i < l; i ++ ) { - - const curve = json.curves[ i ]; - this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); - - } - - return this; - - } - - } ); - - function Path( points ) { - - CurvePath.call( this ); - - this.type = 'Path'; - - this.currentPoint = new Vector2(); - - if ( points ) { - - this.setFromPoints( points ); - - } - - } - - Path.prototype = Object.assign( Object.create( CurvePath.prototype ), { - - constructor: Path, - - setFromPoints: function ( points ) { - - this.moveTo( points[ 0 ].x, points[ 0 ].y ); - - for ( let i = 1, l = points.length; i < l; i ++ ) { - - this.lineTo( points[ i ].x, points[ i ].y ); - - } - - return this; - - }, - - moveTo: function ( x, y ) { - - this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? - - return this; - - }, - - lineTo: function ( x, y ) { - - const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); - this.curves.push( curve ); - - this.currentPoint.set( x, y ); - - return this; - - }, - - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { - - const curve = new QuadraticBezierCurve( - this.currentPoint.clone(), - new Vector2( aCPx, aCPy ), - new Vector2( aX, aY ) - ); - - this.curves.push( curve ); - - this.currentPoint.set( aX, aY ); - - return this; - - }, - - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - - const curve = new CubicBezierCurve( - this.currentPoint.clone(), - new Vector2( aCP1x, aCP1y ), - new Vector2( aCP2x, aCP2y ), - new Vector2( aX, aY ) - ); - - this.curves.push( curve ); - - this.currentPoint.set( aX, aY ); - - return this; - - }, - - splineThru: function ( pts /*Array of Vector*/ ) { - - const npts = [ this.currentPoint.clone() ].concat( pts ); - - const curve = new SplineCurve( npts ); - this.curves.push( curve ); - - this.currentPoint.copy( pts[ pts.length - 1 ] ); - - return this; - - }, - - arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - const x0 = this.currentPoint.x; - const y0 = this.currentPoint.y; - - this.absarc( aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); - - return this; - - }, - - absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - - return this; - - }, - - ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - const x0 = this.currentPoint.x; - const y0 = this.currentPoint.y; - - this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - - return this; - - }, - - absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - - if ( this.curves.length > 0 ) { - - // if a previous curve is present, attempt to join - const firstPoint = curve.getPoint( 0 ); - - if ( ! firstPoint.equals( this.currentPoint ) ) { - - this.lineTo( firstPoint.x, firstPoint.y ); - - } - - } - - this.curves.push( curve ); - - const lastPoint = curve.getPoint( 1 ); - this.currentPoint.copy( lastPoint ); - - return this; - - }, - - copy: function ( source ) { - - CurvePath.prototype.copy.call( this, source ); - - this.currentPoint.copy( source.currentPoint ); - - return this; - - }, - - toJSON: function () { - - const data = CurvePath.prototype.toJSON.call( this ); - - data.currentPoint = this.currentPoint.toArray(); - - return data; - - }, - - fromJSON: function ( json ) { - - CurvePath.prototype.fromJSON.call( this, json ); - - this.currentPoint.fromArray( json.currentPoint ); - - return this; - - } - - } ); - - function Shape( points ) { - - Path.call( this, points ); - - this.uuid = MathUtils.generateUUID(); - - this.type = 'Shape'; - - this.holes = []; - - } - - Shape.prototype = Object.assign( Object.create( Path.prototype ), { - - constructor: Shape, - - getPointsHoles: function ( divisions ) { - - const holesPts = []; - - for ( let i = 0, l = this.holes.length; i < l; i ++ ) { - - holesPts[ i ] = this.holes[ i ].getPoints( divisions ); - - } - - return holesPts; - - }, - - // get points of shape and holes (keypoints based on segments parameter) - - extractPoints: function ( divisions ) { - - return { - - shape: this.getPoints( divisions ), - holes: this.getPointsHoles( divisions ) - - }; - - }, - - copy: function ( source ) { - - Path.prototype.copy.call( this, source ); - - this.holes = []; - - for ( let i = 0, l = source.holes.length; i < l; i ++ ) { - - const hole = source.holes[ i ]; - - this.holes.push( hole.clone() ); - - } - - return this; - - }, - - toJSON: function () { - - const data = Path.prototype.toJSON.call( this ); - - data.uuid = this.uuid; - data.holes = []; - - for ( let i = 0, l = this.holes.length; i < l; i ++ ) { - - const hole = this.holes[ i ]; - data.holes.push( hole.toJSON() ); - - } - - return data; - - }, - - fromJSON: function ( json ) { - - Path.prototype.fromJSON.call( this, json ); - - this.uuid = json.uuid; - this.holes = []; - - for ( let i = 0, l = json.holes.length; i < l; i ++ ) { - - const hole = json.holes[ i ]; - this.holes.push( new Path().fromJSON( hole ) ); - - } - - return this; - - } - - } ); - - function Light( color, intensity ) { - - Object3D.call( this ); - - this.type = 'Light'; - - this.color = new Color( color ); - this.intensity = intensity !== undefined ? intensity : 1; - - this.receiveShadow = undefined; - - } - - Light.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Light, - - isLight: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - this.intensity = source.intensity; - - return this; - - }, - - toJSON: function ( meta ) { - - const data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; - - if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); - - if ( this.distance !== undefined ) data.object.distance = this.distance; - if ( this.angle !== undefined ) data.object.angle = this.angle; - if ( this.decay !== undefined ) data.object.decay = this.decay; - if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; - - if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); - - return data; - - } - - } ); - - function HemisphereLight( skyColor, groundColor, intensity ) { - - Light.call( this, skyColor, intensity ); - - this.type = 'HemisphereLight'; - - this.castShadow = undefined; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.groundColor = new Color( groundColor ); - - } - - HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: HemisphereLight, - - isHemisphereLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.groundColor.copy( source.groundColor ); - - return this; - - } - - } ); - - function LightShadow( camera ) { - - this.camera = camera; - - this.bias = 0; - this.normalBias = 0; - this.radius = 1; - - this.mapSize = new Vector2( 512, 512 ); - - this.map = null; - this.mapPass = null; - this.matrix = new Matrix4(); - - this.autoUpdate = true; - this.needsUpdate = false; - - this._frustum = new Frustum(); - this._frameExtents = new Vector2( 1, 1 ); - - this._viewportCount = 1; - - this._viewports = [ - - new Vector4( 0, 0, 1, 1 ) - - ]; - - } - - Object.assign( LightShadow.prototype, { - - _projScreenMatrix: new Matrix4(), - - _lightPositionWorld: new Vector3(), - - _lookTarget: new Vector3(), - - getViewportCount: function () { - - return this._viewportCount; - - }, - - getFrustum: function () { - - return this._frustum; - - }, - - updateMatrices: function ( light ) { - - const shadowCamera = this.camera, - shadowMatrix = this.matrix, - projScreenMatrix = this._projScreenMatrix, - lookTarget = this._lookTarget, - lightPositionWorld = this._lightPositionWorld; - - lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - shadowCamera.position.copy( lightPositionWorld ); - - lookTarget.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( lookTarget ); - shadowCamera.updateMatrixWorld(); - - projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( projScreenMatrix ); - - shadowMatrix.set( - 0.5, 0.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0 - ); - - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); - - }, - - getViewport: function ( viewportIndex ) { - - return this._viewports[ viewportIndex ]; - - }, - - getFrameExtents: function () { - - return this._frameExtents; - - }, - - copy: function ( source ) { - - this.camera = source.camera.clone(); - - this.bias = source.bias; - this.radius = source.radius; - - this.mapSize.copy( source.mapSize ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - toJSON: function () { - - const object = {}; - - if ( this.bias !== 0 ) object.bias = this.bias; - if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; - if ( this.radius !== 1 ) object.radius = this.radius; - if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); - - object.camera = this.camera.toJSON( false ).object; - delete object.camera.matrix; - - return object; - - } - - } ); - - function SpotLightShadow() { - - LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); - - } - - SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - - constructor: SpotLightShadow, - - isSpotLightShadow: true, - - updateMatrices: function ( light ) { - - const camera = this.camera; - - const fov = MathUtils.RAD2DEG * 2 * light.angle; - const aspect = this.mapSize.width / this.mapSize.height; - const far = light.distance || camera.far; - - if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { - - camera.fov = fov; - camera.aspect = aspect; - camera.far = far; - camera.updateProjectionMatrix(); - - } - - LightShadow.prototype.updateMatrices.call( this, light ); - - } - - } ); - - function SpotLight( color, intensity, distance, angle, penumbra, decay ) { - - Light.call( this, color, intensity ); - - this.type = 'SpotLight'; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.target = new Object3D(); - - Object.defineProperty( this, 'power', { - get: function () { - - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * Math.PI; - - }, - set: function ( power ) { - - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / Math.PI; - - } - } ); - - this.distance = ( distance !== undefined ) ? distance : 0; - this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; - this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - - this.shadow = new SpotLightShadow(); - - } - - SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: SpotLight, - - isSpotLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.distance = source.distance; - this.angle = source.angle; - this.penumbra = source.penumbra; - this.decay = source.decay; - - this.target = source.target.clone(); - - this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - function PointLightShadow() { - - LightShadow.call( this, new PerspectiveCamera( 90, 1, 0.5, 500 ) ); - - this._frameExtents = new Vector2( 4, 2 ); - - this._viewportCount = 6; - - this._viewports = [ - // These viewports map a cube-map onto a 2D texture with the - // following orientation: - // - // xzXZ - // y Y - // - // X - Positive x direction - // x - Negative x direction - // Y - Positive y direction - // y - Negative y direction - // Z - Positive z direction - // z - Negative z direction - - // positive X - new Vector4( 2, 1, 1, 1 ), - // negative X - new Vector4( 0, 1, 1, 1 ), - // positive Z - new Vector4( 3, 1, 1, 1 ), - // negative Z - new Vector4( 1, 1, 1, 1 ), - // positive Y - new Vector4( 3, 0, 1, 1 ), - // negative Y - new Vector4( 1, 0, 1, 1 ) - ]; - - this._cubeDirections = [ - new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), - new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) - ]; - - this._cubeUps = [ - new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), - new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) - ]; - - } - - PointLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - - constructor: PointLightShadow, - - isPointLightShadow: true, - - updateMatrices: function ( light, viewportIndex ) { - - if ( viewportIndex === undefined ) viewportIndex = 0; - - const camera = this.camera, - shadowMatrix = this.matrix, - lightPositionWorld = this._lightPositionWorld, - lookTarget = this._lookTarget, - projScreenMatrix = this._projScreenMatrix; - - lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - camera.position.copy( lightPositionWorld ); - - lookTarget.copy( camera.position ); - lookTarget.add( this._cubeDirections[ viewportIndex ] ); - camera.up.copy( this._cubeUps[ viewportIndex ] ); - camera.lookAt( lookTarget ); - camera.updateMatrixWorld(); - - shadowMatrix.makeTranslation( - lightPositionWorld.x, - lightPositionWorld.y, - lightPositionWorld.z ); - - projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( projScreenMatrix ); - - } - - } ); - - function PointLight( color, intensity, distance, decay ) { - - Light.call( this, color, intensity ); - - this.type = 'PointLight'; - - Object.defineProperty( this, 'power', { - get: function () { - - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * 4 * Math.PI; - - }, - set: function ( power ) { - - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / ( 4 * Math.PI ); - - } - } ); - - this.distance = ( distance !== undefined ) ? distance : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - - this.shadow = new PointLightShadow(); - - } - - PointLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: PointLight, - - isPointLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.distance = source.distance; - this.decay = source.decay; - - this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - function OrthographicCamera( left, right, top, bottom, near, far ) { - - Camera.call( this ); - - this.type = 'OrthographicCamera'; - - this.zoom = 1; - this.view = null; - - this.left = ( left !== undefined ) ? left : - 1; - this.right = ( right !== undefined ) ? right : 1; - this.top = ( top !== undefined ) ? top : 1; - this.bottom = ( bottom !== undefined ) ? bottom : - 1; - - this.near = ( near !== undefined ) ? near : 0.1; - this.far = ( far !== undefined ) ? far : 2000; - - this.updateProjectionMatrix(); - - } - - OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { - - constructor: OrthographicCamera, - - isOrthographicCamera: true, - - copy: function ( source, recursive ) { - - Camera.prototype.copy.call( this, source, recursive ); - - this.left = source.left; - this.right = source.right; - this.top = source.top; - this.bottom = source.bottom; - this.near = source.near; - this.far = source.far; - - this.zoom = source.zoom; - this.view = source.view === null ? null : Object.assign( {}, source.view ); - - return this; - - }, - - setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { - - if ( this.view === null ) { - - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; - - } - - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; - - this.updateProjectionMatrix(); - - }, - - clearViewOffset: function () { - - if ( this.view !== null ) { - - this.view.enabled = false; - - } - - this.updateProjectionMatrix(); - - }, - - updateProjectionMatrix: function () { - - const dx = ( this.right - this.left ) / ( 2 * this.zoom ); - const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); - const cx = ( this.right + this.left ) / 2; - const cy = ( this.top + this.bottom ) / 2; - - let left = cx - dx; - let right = cx + dx; - let top = cy + dy; - let bottom = cy - dy; - - if ( this.view !== null && this.view.enabled ) { - - const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; - const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; - - left += scaleW * this.view.offsetX; - right = left + scaleW * this.view.width; - top -= scaleH * this.view.offsetY; - bottom = top - scaleH * this.view.height; - - } - - this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); - - this.projectionMatrixInverse.getInverse( this.projectionMatrix ); - - }, - - toJSON: function ( meta ) { - - const data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.zoom = this.zoom; - data.object.left = this.left; - data.object.right = this.right; - data.object.top = this.top; - data.object.bottom = this.bottom; - data.object.near = this.near; - data.object.far = this.far; - - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - - return data; - - } - - } ); - - function DirectionalLightShadow() { - - LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); - - } - - DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - - constructor: DirectionalLightShadow, - - isDirectionalLightShadow: true, - - updateMatrices: function ( light ) { - - LightShadow.prototype.updateMatrices.call( this, light ); - - } - - } ); - - function DirectionalLight( color, intensity ) { - - Light.call( this, color, intensity ); - - this.type = 'DirectionalLight'; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.target = new Object3D(); - - this.shadow = new DirectionalLightShadow(); - - } - - DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: DirectionalLight, - - isDirectionalLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.target = source.target.clone(); - - this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - function AmbientLight( color, intensity ) { - - Light.call( this, color, intensity ); - - this.type = 'AmbientLight'; - - this.castShadow = undefined; - - } - - AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: AmbientLight, - - isAmbientLight: true - - } ); - - function RectAreaLight( color, intensity, width, height ) { - - Light.call( this, color, intensity ); - - this.type = 'RectAreaLight'; - - this.width = ( width !== undefined ) ? width : 10; - this.height = ( height !== undefined ) ? height : 10; - - } - - RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: RectAreaLight, - - isRectAreaLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.width = source.width; - this.height = source.height; - - return this; - - }, - - toJSON: function ( meta ) { - - const data = Light.prototype.toJSON.call( this, meta ); - - data.object.width = this.width; - data.object.height = this.height; - - return data; - - } - - } ); - - /** - * Primary reference: - * https://graphics.stanford.edu/papers/envmap/envmap.pdf - * - * Secondary reference: - * https://www.ppsloan.org/publications/StupidSH36.pdf - */ - - // 3-band SH defined by 9 coefficients - - class SphericalHarmonics3 { - - constructor() { - - Object.defineProperty( this, 'isSphericalHarmonics3', { value: true } ); - - this.coefficients = []; - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients.push( new Vector3() ); - - } - - } - - set( coefficients ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].copy( coefficients[ i ] ); - - } - - return this; - - } - - zero() { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].set( 0, 0, 0 ); - - } - - return this; - - } - - // get the radiance in the direction of the normal - // target is a Vector3 - getAt( normal, target ) { - - // normal is assumed to be unit length - - const x = normal.x, y = normal.y, z = normal.z; - - const coeff = this.coefficients; - - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); - - // band 1 - target.addScaledVector( coeff[ 1 ], 0.488603 * y ); - target.addScaledVector( coeff[ 2 ], 0.488603 * z ); - target.addScaledVector( coeff[ 3 ], 0.488603 * x ); - - // band 2 - target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); - target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); - target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); - target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); - target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); - - return target; - - } - - // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal - // target is a Vector3 - // https://graphics.stanford.edu/papers/envmap/envmap.pdf - getIrradianceAt( normal, target ) { - - // normal is assumed to be unit length - - const x = normal.x, y = normal.y, z = normal.z; - - const coeff = this.coefficients; - - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 - - // band 1 - target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 - target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); - target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); - - // band 2 - target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 - target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); - target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 - target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); - target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 - - return target; - - } - - add( sh ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].add( sh.coefficients[ i ] ); - - } - - return this; - - } - - addScaledSH( sh, s ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); - - } - - return this; - - } - - scale( s ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].multiplyScalar( s ); - - } - - return this; - - } - - lerp( sh, alpha ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); - - } - - return this; - - } - - equals( sh ) { - - for ( let i = 0; i < 9; i ++ ) { - - if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { - - return false; - - } - - } - - return true; - - } - - copy( sh ) { - - return this.set( sh.coefficients ); - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - fromArray( array, offset ) { - - if ( offset === undefined ) offset = 0; - - const coefficients = this.coefficients; - - for ( let i = 0; i < 9; i ++ ) { - - coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); - - } - - return this; - - } - - toArray( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - const coefficients = this.coefficients; - - for ( let i = 0; i < 9; i ++ ) { - - coefficients[ i ].toArray( array, offset + ( i * 3 ) ); - - } - - return array; - - } - - // evaluate the basis functions - // shBasis is an Array[ 9 ] - static getBasisAt( normal, shBasis ) { - - // normal is assumed to be unit length - - const x = normal.x, y = normal.y, z = normal.z; - - // band 0 - shBasis[ 0 ] = 0.282095; - - // band 1 - shBasis[ 1 ] = 0.488603 * y; - shBasis[ 2 ] = 0.488603 * z; - shBasis[ 3 ] = 0.488603 * x; - - // band 2 - shBasis[ 4 ] = 1.092548 * x * y; - shBasis[ 5 ] = 1.092548 * y * z; - shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); - shBasis[ 7 ] = 1.092548 * x * z; - shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); - - } - - } - - function LightProbe( sh, intensity ) { - - Light.call( this, undefined, intensity ); - - this.type = 'LightProbe'; - - this.sh = ( sh !== undefined ) ? sh : new SphericalHarmonics3(); - - } - - LightProbe.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: LightProbe, - - isLightProbe: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.sh.copy( source.sh ); - - return this; - - }, - - fromJSON: function ( json ) { - - this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); - this.sh.fromArray( json.sh ); - - return this; - - }, - - toJSON: function ( meta ) { - - const data = Light.prototype.toJSON.call( this, meta ); - - data.object.sh = this.sh.toArray(); - - return data; - - } - - } ); - - function MaterialLoader( manager ) { - - Loader.call( this, manager ); - - this.textures = {}; - - } - - MaterialLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: MaterialLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.load( url, function ( text ) { - - try { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - const textures = this.textures; - - function getTexture( name ) { - - if ( textures[ name ] === undefined ) { - - console.warn( 'THREE.MaterialLoader: Undefined texture', name ); - - } - - return textures[ name ]; - - } - - const material = new Materials[ json.type ](); - - if ( json.uuid !== undefined ) material.uuid = json.uuid; - if ( json.name !== undefined ) material.name = json.name; - if ( json.color !== undefined ) material.color.setHex( json.color ); - if ( json.roughness !== undefined ) material.roughness = json.roughness; - if ( json.metalness !== undefined ) material.metalness = json.metalness; - if ( json.sheen !== undefined ) material.sheen = new Color().setHex( json.sheen ); - if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); - if ( json.specular !== undefined ) material.specular.setHex( json.specular ); - if ( json.shininess !== undefined ) material.shininess = json.shininess; - if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat; - if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness; - if ( json.fog !== undefined ) material.fog = json.fog; - if ( json.flatShading !== undefined ) material.flatShading = json.flatShading; - if ( json.blending !== undefined ) material.blending = json.blending; - if ( json.combine !== undefined ) material.combine = json.combine; - if ( json.side !== undefined ) material.side = json.side; - if ( json.opacity !== undefined ) material.opacity = json.opacity; - if ( json.transparent !== undefined ) material.transparent = json.transparent; - if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; - if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; - if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; - if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; - - if ( json.stencilWrite !== undefined ) material.stencilWrite = json.stencilWrite; - if ( json.stencilWriteMask !== undefined ) material.stencilWriteMask = json.stencilWriteMask; - if ( json.stencilFunc !== undefined ) material.stencilFunc = json.stencilFunc; - if ( json.stencilRef !== undefined ) material.stencilRef = json.stencilRef; - if ( json.stencilFuncMask !== undefined ) material.stencilFuncMask = json.stencilFuncMask; - if ( json.stencilFail !== undefined ) material.stencilFail = json.stencilFail; - if ( json.stencilZFail !== undefined ) material.stencilZFail = json.stencilZFail; - if ( json.stencilZPass !== undefined ) material.stencilZPass = json.stencilZPass; - - if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; - if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; - if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; - if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; - - if ( json.rotation !== undefined ) material.rotation = json.rotation; - - if ( json.linewidth !== 1 ) material.linewidth = json.linewidth; - if ( json.dashSize !== undefined ) material.dashSize = json.dashSize; - if ( json.gapSize !== undefined ) material.gapSize = json.gapSize; - if ( json.scale !== undefined ) material.scale = json.scale; - - if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset; - if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor; - if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits; - - if ( json.skinning !== undefined ) material.skinning = json.skinning; - if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets; - if ( json.morphNormals !== undefined ) material.morphNormals = json.morphNormals; - if ( json.dithering !== undefined ) material.dithering = json.dithering; - - if ( json.vertexTangents !== undefined ) material.vertexTangents = json.vertexTangents; - - if ( json.visible !== undefined ) material.visible = json.visible; - - if ( json.toneMapped !== undefined ) material.toneMapped = json.toneMapped; - - if ( json.userData !== undefined ) material.userData = json.userData; - - if ( json.vertexColors !== undefined ) { - - if ( typeof json.vertexColors === 'number' ) { - - material.vertexColors = ( json.vertexColors > 0 ) ? true : false; - - } else { - - material.vertexColors = json.vertexColors; - - } - - } - - // Shader Material - - if ( json.uniforms !== undefined ) { - - for ( const name in json.uniforms ) { - - const uniform = json.uniforms[ name ]; - - material.uniforms[ name ] = {}; - - switch ( uniform.type ) { - - case 't': - material.uniforms[ name ].value = getTexture( uniform.value ); - break; - - case 'c': - material.uniforms[ name ].value = new Color().setHex( uniform.value ); - break; - - case 'v2': - material.uniforms[ name ].value = new Vector2().fromArray( uniform.value ); - break; - - case 'v3': - material.uniforms[ name ].value = new Vector3().fromArray( uniform.value ); - break; - - case 'v4': - material.uniforms[ name ].value = new Vector4().fromArray( uniform.value ); - break; - - case 'm3': - material.uniforms[ name ].value = new Matrix3().fromArray( uniform.value ); - break; - - case 'm4': - material.uniforms[ name ].value = new Matrix4().fromArray( uniform.value ); - break; - - default: - material.uniforms[ name ].value = uniform.value; - - } - - } - - } - - if ( json.defines !== undefined ) material.defines = json.defines; - if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; - if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; - - if ( json.extensions !== undefined ) { - - for ( const key in json.extensions ) { - - material.extensions[ key ] = json.extensions[ key ]; - - } - - } - - // Deprecated - - if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading - - // for PointsMaterial - - if ( json.size !== undefined ) material.size = json.size; - if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; - - // maps - - if ( json.map !== undefined ) material.map = getTexture( json.map ); - if ( json.matcap !== undefined ) material.matcap = getTexture( json.matcap ); - - if ( json.alphaMap !== undefined ) material.alphaMap = getTexture( json.alphaMap ); - - if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); - if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; - - if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); - if ( json.normalMapType !== undefined ) material.normalMapType = json.normalMapType; - if ( json.normalScale !== undefined ) { - - let normalScale = json.normalScale; - - if ( Array.isArray( normalScale ) === false ) { - - // Blender exporter used to export a scalar. See #7459 - - normalScale = [ normalScale, normalScale ]; - - } - - material.normalScale = new Vector2().fromArray( normalScale ); - - } - - if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); - if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; - if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; - - if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); - if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); - - if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); - if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; - - if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); - - if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); - if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity; - - if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; - if ( json.refractionRatio !== undefined ) material.refractionRatio = json.refractionRatio; - - if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); - if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; - - if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); - if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; - - if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); - - if ( json.clearcoatMap !== undefined ) material.clearcoatMap = getTexture( json.clearcoatMap ); - if ( json.clearcoatRoughnessMap !== undefined ) material.clearcoatRoughnessMap = getTexture( json.clearcoatRoughnessMap ); - if ( json.clearcoatNormalMap !== undefined ) material.clearcoatNormalMap = getTexture( json.clearcoatNormalMap ); - if ( json.clearcoatNormalScale !== undefined ) material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale ); - - if ( json.transmission !== undefined ) material.transmission = json.transmission; - if ( json.transmissionMap !== undefined ) material.transmissionMap = getTexture( json.transmissionMap ); - - return material; - - }, - - setTextures: function ( value ) { - - this.textures = value; - return this; - - } - - } ); - - const LoaderUtils = { - - decodeText: function ( array ) { - - if ( typeof TextDecoder !== 'undefined' ) { - - return new TextDecoder().decode( array ); - - } - - // Avoid the String.fromCharCode.apply(null, array) shortcut, which - // throws a "maximum call stack size exceeded" error for large arrays. - - let s = ''; - - for ( let i = 0, il = array.length; i < il; i ++ ) { - - // Implicitly assumes little-endian. - s += String.fromCharCode( array[ i ] ); - - } - - try { - - // merges multi-byte utf-8 characters. - - return decodeURIComponent( escape( s ) ); - - } catch ( e ) { // see #16358 - - return s; - - } - - }, - - extractUrlBase: function ( url ) { - - const index = url.lastIndexOf( '/' ); - - if ( index === - 1 ) return './'; - - return url.substr( 0, index + 1 ); - - } - - }; - - function InstancedBufferGeometry() { - - BufferGeometry.call( this ); - - this.type = 'InstancedBufferGeometry'; - this.instanceCount = Infinity; - - } - - InstancedBufferGeometry.prototype = Object.assign( Object.create( BufferGeometry.prototype ), { - - constructor: InstancedBufferGeometry, - - isInstancedBufferGeometry: true, - - copy: function ( source ) { - - BufferGeometry.prototype.copy.call( this, source ); - - this.instanceCount = source.instanceCount; - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - toJSON: function () { - - const data = BufferGeometry.prototype.toJSON.call( this ); - - data.instanceCount = this.instanceCount; - - data.isInstancedBufferGeometry = true; - - return data; - - } - - } ); - - function InstancedBufferAttribute( array, itemSize, normalized, meshPerAttribute ) { - - if ( typeof ( normalized ) === 'number' ) { - - meshPerAttribute = normalized; - - normalized = false; - - console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' ); - - } - - BufferAttribute.call( this, array, itemSize, normalized ); - - this.meshPerAttribute = meshPerAttribute || 1; - - } - - InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribute.prototype ), { - - constructor: InstancedBufferAttribute, - - isInstancedBufferAttribute: true, - - copy: function ( source ) { - - BufferAttribute.prototype.copy.call( this, source ); - - this.meshPerAttribute = source.meshPerAttribute; - - return this; - - }, - - toJSON: function () { - - const data = BufferAttribute.prototype.toJSON.call( this ); - - data.meshPerAttribute = this.meshPerAttribute; - - data.isInstancedBufferAttribute = true; - - return data; - - } - - } ); - - function BufferGeometryLoader( manager ) { - - Loader.call( this, manager ); - - } - - BufferGeometryLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: BufferGeometryLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.load( url, function ( text ) { - - try { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - const interleavedBufferMap = {}; - const arrayBufferMap = {}; - - function getInterleavedBuffer( json, uuid ) { - - if ( interleavedBufferMap[ uuid ] !== undefined ) return interleavedBufferMap[ uuid ]; - - const interleavedBuffers = json.interleavedBuffers; - const interleavedBuffer = interleavedBuffers[ uuid ]; - - const buffer = getArrayBuffer( json, interleavedBuffer.buffer ); - - const array = new TYPED_ARRAYS[ interleavedBuffer.type ]( buffer ); - const ib = new InterleavedBuffer( array, interleavedBuffer.stride ); - ib.uuid = interleavedBuffer.uuid; - - interleavedBufferMap[ uuid ] = ib; - - return ib; - - } - - function getArrayBuffer( json, uuid ) { - - if ( arrayBufferMap[ uuid ] !== undefined ) return arrayBufferMap[ uuid ]; - - const arrayBuffers = json.arrayBuffers; - const arrayBuffer = arrayBuffers[ uuid ]; - - const ab = new Uint32Array( arrayBuffer ).buffer; - - arrayBufferMap[ uuid ] = ab; - - return ab; - - } - - const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); - - const index = json.data.index; - - if ( index !== undefined ) { - - const typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); - geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); - - } - - const attributes = json.data.attributes; - - for ( const key in attributes ) { - - const attribute = attributes[ key ]; - let bufferAttribute; - - if ( attribute.isInterleavedBufferAttribute ) { - - const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); - bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); - - } else { - - const typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); - const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; - bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized ); - - } - - if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; - geometry.setAttribute( key, bufferAttribute ); - - } - - const morphAttributes = json.data.morphAttributes; - - if ( morphAttributes ) { - - for ( const key in morphAttributes ) { - - const attributeArray = morphAttributes[ key ]; - - const array = []; - - for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { - - const attribute = attributeArray[ i ]; - let bufferAttribute; - - if ( attribute.isInterleavedBufferAttribute ) { - - const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); - bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); - - } else { - - const typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); - bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ); - - } - - if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; - array.push( bufferAttribute ); - - } - - geometry.morphAttributes[ key ] = array; - - } - - } - - const morphTargetsRelative = json.data.morphTargetsRelative; - - if ( morphTargetsRelative ) { - - geometry.morphTargetsRelative = true; - - } - - const groups = json.data.groups || json.data.drawcalls || json.data.offsets; - - if ( groups !== undefined ) { - - for ( let i = 0, n = groups.length; i !== n; ++ i ) { - - const group = groups[ i ]; - - geometry.addGroup( group.start, group.count, group.materialIndex ); - - } - - } - - const boundingSphere = json.data.boundingSphere; - - if ( boundingSphere !== undefined ) { - - const center = new Vector3(); - - if ( boundingSphere.center !== undefined ) { - - center.fromArray( boundingSphere.center ); - - } - - geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); - - } - - if ( json.name ) geometry.name = json.name; - if ( json.userData ) geometry.userData = json.userData; - - return geometry; - - } - - } ); - - const TYPED_ARRAYS = { - Int8Array: Int8Array, - Uint8Array: Uint8Array, - // Workaround for IE11 pre KB2929437. See #11440 - Uint8ClampedArray: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : Uint8Array, - Int16Array: Int16Array, - Uint16Array: Uint16Array, - Int32Array: Int32Array, - Uint32Array: Uint32Array, - Float32Array: Float32Array, - Float64Array: Float64Array - }; - - function ObjectLoader( manager ) { - - Loader.call( this, manager ); - - } - - ObjectLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: ObjectLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path; - this.resourcePath = this.resourcePath || path; - - const loader = new FileLoader( scope.manager ); - loader.setPath( this.path ); - loader.setRequestHeader( this.requestHeader ); - loader.load( url, function ( text ) { - - let json = null; - - try { - - json = JSON.parse( text ); - - } catch ( error ) { - - if ( onError !== undefined ) onError( error ); - - console.error( 'THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message ); - - return; - - } - - const metadata = json.metadata; - - if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { - - console.error( 'THREE.ObjectLoader: Can\'t load ' + url ); - return; - - } - - scope.parse( json, onLoad ); - - }, onProgress, onError ); - - }, - - parse: function ( json, onLoad ) { - - const shapes = this.parseShape( json.shapes ); - const geometries = this.parseGeometries( json.geometries, shapes ); - - const images = this.parseImages( json.images, function () { - - if ( onLoad !== undefined ) onLoad( object ); - - } ); - - const textures = this.parseTextures( json.textures, images ); - const materials = this.parseMaterials( json.materials, textures ); - - const object = this.parseObject( json.object, geometries, materials ); - - if ( json.animations ) { - - object.animations = this.parseAnimations( json.animations ); - - } - - if ( json.images === undefined || json.images.length === 0 ) { - - if ( onLoad !== undefined ) onLoad( object ); - - } - - return object; - - }, - - parseShape: function ( json ) { - - const shapes = {}; - - if ( json !== undefined ) { - - for ( let i = 0, l = json.length; i < l; i ++ ) { - - const shape = new Shape().fromJSON( json[ i ] ); - - shapes[ shape.uuid ] = shape; - - } - - } - - return shapes; - - }, - - parseGeometries: function ( json, shapes ) { - - const geometries = {}; - let geometryShapes; - - if ( json !== undefined ) { - - const bufferGeometryLoader = new BufferGeometryLoader(); - - for ( let i = 0, l = json.length; i < l; i ++ ) { - - let geometry; - const data = json[ i ]; - - switch ( data.type ) { - - case 'PlaneGeometry': - case 'PlaneBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.width, - data.height, - data.widthSegments, - data.heightSegments - ); - - break; - - case 'BoxGeometry': - case 'BoxBufferGeometry': - case 'CubeGeometry': // backwards compatible - - geometry = new Geometries[ data.type ]( - data.width, - data.height, - data.depth, - data.widthSegments, - data.heightSegments, - data.depthSegments - ); - - break; - - case 'CircleGeometry': - case 'CircleBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.segments, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'CylinderGeometry': - case 'CylinderBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radiusTop, - data.radiusBottom, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'ConeGeometry': - case 'ConeBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'SphereGeometry': - case 'SphereBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.widthSegments, - data.heightSegments, - data.phiStart, - data.phiLength, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'DodecahedronGeometry': - case 'DodecahedronBufferGeometry': - case 'IcosahedronGeometry': - case 'IcosahedronBufferGeometry': - case 'OctahedronGeometry': - case 'OctahedronBufferGeometry': - case 'TetrahedronGeometry': - case 'TetrahedronBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.detail - ); - - break; - - case 'RingGeometry': - case 'RingBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.innerRadius, - data.outerRadius, - data.thetaSegments, - data.phiSegments, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'TorusGeometry': - case 'TorusBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.tube, - data.radialSegments, - data.tubularSegments, - data.arc - ); - - break; - - case 'TorusKnotGeometry': - case 'TorusKnotBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.tube, - data.tubularSegments, - data.radialSegments, - data.p, - data.q - ); - - break; - - case 'TubeGeometry': - case 'TubeBufferGeometry': - - // This only works for built-in curves (e.g. CatmullRomCurve3). - // User defined curves or instances of CurvePath will not be deserialized. - geometry = new Geometries[ data.type ]( - new Curves[ data.path.type ]().fromJSON( data.path ), - data.tubularSegments, - data.radius, - data.radialSegments, - data.closed - ); - - break; - - case 'LatheGeometry': - case 'LatheBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.points, - data.segments, - data.phiStart, - data.phiLength - ); - - break; - - case 'PolyhedronGeometry': - case 'PolyhedronBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.vertices, - data.indices, - data.radius, - data.details - ); - - break; - - case 'ShapeGeometry': - case 'ShapeBufferGeometry': - - geometryShapes = []; - - for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { - - const shape = shapes[ data.shapes[ j ] ]; - - geometryShapes.push( shape ); - - } - - geometry = new Geometries[ data.type ]( - geometryShapes, - data.curveSegments - ); - - break; - - - case 'ExtrudeGeometry': - case 'ExtrudeBufferGeometry': - - geometryShapes = []; - - for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { - - const shape = shapes[ data.shapes[ j ] ]; - - geometryShapes.push( shape ); - - } - - const extrudePath = data.options.extrudePath; - - if ( extrudePath !== undefined ) { - - data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); - - } - - geometry = new Geometries[ data.type ]( - geometryShapes, - data.options - ); - - break; - - case 'BufferGeometry': - case 'InstancedBufferGeometry': - - geometry = bufferGeometryLoader.parse( data ); - - break; - - case 'Geometry': - - console.error( 'THREE.ObjectLoader: Loading "Geometry" is not supported anymore.' ); - - break; - - default: - - console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); - - continue; - - } - - geometry.uuid = data.uuid; - - if ( data.name !== undefined ) geometry.name = data.name; - if ( geometry.isBufferGeometry === true && data.userData !== undefined ) geometry.userData = data.userData; - - geometries[ data.uuid ] = geometry; - - } - - } - - return geometries; - - }, - - parseMaterials: function ( json, textures ) { - - const cache = {}; // MultiMaterial - const materials = {}; - - if ( json !== undefined ) { - - const loader = new MaterialLoader(); - loader.setTextures( textures ); - - for ( let i = 0, l = json.length; i < l; i ++ ) { - - const data = json[ i ]; - - if ( data.type === 'MultiMaterial' ) { - - // Deprecated - - const array = []; - - for ( let j = 0; j < data.materials.length; j ++ ) { - - const material = data.materials[ j ]; - - if ( cache[ material.uuid ] === undefined ) { - - cache[ material.uuid ] = loader.parse( material ); - - } - - array.push( cache[ material.uuid ] ); - - } - - materials[ data.uuid ] = array; - - } else { - - if ( cache[ data.uuid ] === undefined ) { - - cache[ data.uuid ] = loader.parse( data ); - - } - - materials[ data.uuid ] = cache[ data.uuid ]; - - } - - } - - } - - return materials; - - }, - - parseAnimations: function ( json ) { - - const animations = []; - - for ( let i = 0; i < json.length; i ++ ) { - - const data = json[ i ]; - - const clip = AnimationClip.parse( data ); - - if ( data.uuid !== undefined ) clip.uuid = data.uuid; - - animations.push( clip ); - - } - - return animations; - - }, - - parseImages: function ( json, onLoad ) { - - const scope = this; - const images = {}; - - let loader; - - function loadImage( url ) { - - scope.manager.itemStart( url ); - - return loader.load( url, function () { - - scope.manager.itemEnd( url ); - - }, undefined, function () { - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } ); - - } - - if ( json !== undefined && json.length > 0 ) { - - const manager = new LoadingManager( onLoad ); - - loader = new ImageLoader( manager ); - loader.setCrossOrigin( this.crossOrigin ); - - for ( let i = 0, il = json.length; i < il; i ++ ) { - - const image = json[ i ]; - const url = image.url; - - if ( Array.isArray( url ) ) { - - // load array of images e.g CubeTexture - - images[ image.uuid ] = []; - - for ( let j = 0, jl = url.length; j < jl; j ++ ) { - - const currentUrl = url[ j ]; - - const path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( currentUrl ) ? currentUrl : scope.resourcePath + currentUrl; - - images[ image.uuid ].push( loadImage( path ) ); - - } - - } else { - - // load single image - - const path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.resourcePath + image.url; - - images[ image.uuid ] = loadImage( path ); - - } - - } - - } - - return images; - - }, - - parseTextures: function ( json, images ) { - - function parseConstant( value, type ) { - - if ( typeof value === 'number' ) return value; - - console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); - - return type[ value ]; - - } - - const textures = {}; - - if ( json !== undefined ) { - - for ( let i = 0, l = json.length; i < l; i ++ ) { - - const data = json[ i ]; - - if ( data.image === undefined ) { - - console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); - - } - - if ( images[ data.image ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); - - } - - let texture; - - if ( Array.isArray( images[ data.image ] ) ) { - - texture = new CubeTexture( images[ data.image ] ); - - } else { - - texture = new Texture( images[ data.image ] ); - - } - - texture.needsUpdate = true; - - texture.uuid = data.uuid; - - if ( data.name !== undefined ) texture.name = data.name; - - if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); - - if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); - if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); - if ( data.center !== undefined ) texture.center.fromArray( data.center ); - if ( data.rotation !== undefined ) texture.rotation = data.rotation; - - if ( data.wrap !== undefined ) { - - texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); - texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); - - } - - if ( data.format !== undefined ) texture.format = data.format; - if ( data.type !== undefined ) texture.type = data.type; - if ( data.encoding !== undefined ) texture.encoding = data.encoding; - - if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); - if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); - if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; - - if ( data.flipY !== undefined ) texture.flipY = data.flipY; - - if ( data.premultiplyAlpha !== undefined ) texture.premultiplyAlpha = data.premultiplyAlpha; - if ( data.unpackAlignment !== undefined ) texture.unpackAlignment = data.unpackAlignment; - - textures[ data.uuid ] = texture; - - } - - } - - return textures; - - }, - - parseObject: function ( data, geometries, materials ) { - - let object; - - function getGeometry( name ) { - - if ( geometries[ name ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); - - } - - return geometries[ name ]; - - } - - function getMaterial( name ) { - - if ( name === undefined ) return undefined; - - if ( Array.isArray( name ) ) { - - const array = []; - - for ( let i = 0, l = name.length; i < l; i ++ ) { - - const uuid = name[ i ]; - - if ( materials[ uuid ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined material', uuid ); - - } - - array.push( materials[ uuid ] ); - - } - - return array; - - } - - if ( materials[ name ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined material', name ); - - } - - return materials[ name ]; - - } - - let geometry, material; - - switch ( data.type ) { - - case 'Scene': - - object = new Scene(); - - if ( data.background !== undefined ) { - - if ( Number.isInteger( data.background ) ) { - - object.background = new Color( data.background ); - - } - - } - - if ( data.fog !== undefined ) { - - if ( data.fog.type === 'Fog' ) { - - object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); - - } else if ( data.fog.type === 'FogExp2' ) { - - object.fog = new FogExp2( data.fog.color, data.fog.density ); - - } - - } - - break; - - case 'PerspectiveCamera': - - object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); - - if ( data.focus !== undefined ) object.focus = data.focus; - if ( data.zoom !== undefined ) object.zoom = data.zoom; - if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; - if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; - if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); - - break; - - case 'OrthographicCamera': - - object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); - - if ( data.zoom !== undefined ) object.zoom = data.zoom; - if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); - - break; - - case 'AmbientLight': - - object = new AmbientLight( data.color, data.intensity ); - - break; - - case 'DirectionalLight': - - object = new DirectionalLight( data.color, data.intensity ); - - break; - - case 'PointLight': - - object = new PointLight( data.color, data.intensity, data.distance, data.decay ); - - break; - - case 'RectAreaLight': - - object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); - - break; - - case 'SpotLight': - - object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); - - break; - - case 'HemisphereLight': - - object = new HemisphereLight( data.color, data.groundColor, data.intensity ); - - break; - - case 'LightProbe': - - object = new LightProbe().fromJSON( data ); - - break; - - case 'SkinnedMesh': - - console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet.' ); - - case 'Mesh': - - geometry = getGeometry( data.geometry ); - material = getMaterial( data.material ); - - object = new Mesh( geometry, material ); - - break; - - case 'InstancedMesh': - - geometry = getGeometry( data.geometry ); - material = getMaterial( data.material ); - const count = data.count; - const instanceMatrix = data.instanceMatrix; - - object = new InstancedMesh( geometry, material, count ); - object.instanceMatrix = new BufferAttribute( new Float32Array( instanceMatrix.array ), 16 ); - - break; - - case 'LOD': - - object = new LOD(); - - break; - - case 'Line': - - object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); - - break; - - case 'LineLoop': - - object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); - - break; - - case 'LineSegments': - - object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); - - break; - - case 'PointCloud': - case 'Points': - - object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); - - break; - - case 'Sprite': - - object = new Sprite( getMaterial( data.material ) ); - - break; - - case 'Group': - - object = new Group(); - - break; - - default: - - object = new Object3D(); - - } - - object.uuid = data.uuid; - - if ( data.name !== undefined ) object.name = data.name; - - if ( data.matrix !== undefined ) { - - object.matrix.fromArray( data.matrix ); - - if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate; - if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale ); - - } else { - - if ( data.position !== undefined ) object.position.fromArray( data.position ); - if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); - if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); - if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); - - } - - if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; - if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; - - if ( data.shadow ) { - - if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; - if ( data.shadow.normalBias !== undefined ) object.shadow.normalBias = data.shadow.normalBias; - if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; - if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); - if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); - - } - - if ( data.visible !== undefined ) object.visible = data.visible; - if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled; - if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder; - if ( data.userData !== undefined ) object.userData = data.userData; - if ( data.layers !== undefined ) object.layers.mask = data.layers; - - if ( data.children !== undefined ) { - - const children = data.children; - - for ( let i = 0; i < children.length; i ++ ) { - - object.add( this.parseObject( children[ i ], geometries, materials ) ); - - } - - } - - if ( data.type === 'LOD' ) { - - if ( data.autoUpdate !== undefined ) object.autoUpdate = data.autoUpdate; - - const levels = data.levels; - - for ( let l = 0; l < levels.length; l ++ ) { - - const level = levels[ l ]; - const child = object.getObjectByProperty( 'uuid', level.object ); - - if ( child !== undefined ) { - - object.addLevel( child, level.distance ); - - } - - } - - } - - return object; - - } - - } ); - - const TEXTURE_MAPPING = { - UVMapping: UVMapping, - CubeReflectionMapping: CubeReflectionMapping, - CubeRefractionMapping: CubeRefractionMapping, - EquirectangularReflectionMapping: EquirectangularReflectionMapping, - EquirectangularRefractionMapping: EquirectangularRefractionMapping, - CubeUVReflectionMapping: CubeUVReflectionMapping, - CubeUVRefractionMapping: CubeUVRefractionMapping - }; - - const TEXTURE_WRAPPING = { - RepeatWrapping: RepeatWrapping, - ClampToEdgeWrapping: ClampToEdgeWrapping, - MirroredRepeatWrapping: MirroredRepeatWrapping - }; - - const TEXTURE_FILTER = { - NearestFilter: NearestFilter, - NearestMipmapNearestFilter: NearestMipmapNearestFilter, - NearestMipmapLinearFilter: NearestMipmapLinearFilter, - LinearFilter: LinearFilter, - LinearMipmapNearestFilter: LinearMipmapNearestFilter, - LinearMipmapLinearFilter: LinearMipmapLinearFilter - }; - - function ImageBitmapLoader( manager ) { - - if ( typeof createImageBitmap === 'undefined' ) { - - console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); - - } - - if ( typeof fetch === 'undefined' ) { - - console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); - - } - - Loader.call( this, manager ); - - this.options = { premultiplyAlpha: 'none' }; - - } - - ImageBitmapLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: ImageBitmapLoader, - - isImageBitmapLoader: true, - - setOptions: function setOptions( options ) { - - this.options = options; - - return this; - - }, - - load: function ( url, onLoad, onProgress, onError ) { - - if ( url === undefined ) url = ''; - - if ( this.path !== undefined ) url = this.path + url; - - url = this.manager.resolveURL( url ); - - const scope = this; - - const cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) onLoad( cached ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - fetch( url ).then( function ( res ) { - - return res.blob(); - - } ).then( function ( blob ) { - - return createImageBitmap( blob, scope.options ); - - } ).then( function ( imageBitmap ) { - - Cache.add( url, imageBitmap ); - - if ( onLoad ) onLoad( imageBitmap ); - - scope.manager.itemEnd( url ); - - } ).catch( function ( e ) { - - if ( onError ) onError( e ); - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } ); - - scope.manager.itemStart( url ); - - } - - } ); - - function ShapePath() { - - this.type = 'ShapePath'; - - this.color = new Color(); - - this.subPaths = []; - this.currentPath = null; - - } - - Object.assign( ShapePath.prototype, { - - moveTo: function ( x, y ) { - - this.currentPath = new Path(); - this.subPaths.push( this.currentPath ); - this.currentPath.moveTo( x, y ); - - return this; - - }, - - lineTo: function ( x, y ) { - - this.currentPath.lineTo( x, y ); - - return this; - - }, - - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { - - this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); - - return this; - - }, - - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - - this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); - - return this; - - }, - - splineThru: function ( pts ) { - - this.currentPath.splineThru( pts ); - - return this; - - }, - - toShapes: function ( isCCW, noHoles ) { - - function toShapesNoHoles( inSubpaths ) { - - const shapes = []; - - for ( let i = 0, l = inSubpaths.length; i < l; i ++ ) { - - const tmpPath = inSubpaths[ i ]; - - const tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; - - shapes.push( tmpShape ); - - } - - return shapes; - - } - - function isPointInsidePolygon( inPt, inPolygon ) { - - const polyLen = inPolygon.length; - - // inPt on polygon contour => immediate success or - // toggling of inside/outside at every single! intersection point of an edge - // with the horizontal line through inPt, left of inPt - // not counting lowerY endpoints of edges and whole edges on that line - let inside = false; - for ( let p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { - - let edgeLowPt = inPolygon[ p ]; - let edgeHighPt = inPolygon[ q ]; - - let edgeDx = edgeHighPt.x - edgeLowPt.x; - let edgeDy = edgeHighPt.y - edgeLowPt.y; - - if ( Math.abs( edgeDy ) > Number.EPSILON ) { - - // not parallel - if ( edgeDy < 0 ) { - - edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; - edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; - - } - - if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; - - if ( inPt.y === edgeLowPt.y ) { - - if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? - // continue; // no intersection or edgeLowPt => doesn't count !!! - - } else { - - const perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); - if ( perpEdge === 0 ) return true; // inPt is on contour ? - if ( perpEdge < 0 ) continue; - inside = ! inside; // true intersection left of inPt - - } - - } else { - - // parallel or collinear - if ( inPt.y !== edgeLowPt.y ) continue; // parallel - // edge lies on the same horizontal line as inPt - if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || - ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! - // continue; - - } - - } - - return inside; - - } - - const isClockWise = ShapeUtils.isClockWise; - - const subPaths = this.subPaths; - if ( subPaths.length === 0 ) return []; - - if ( noHoles === true ) return toShapesNoHoles( subPaths ); - - - let solid, tmpPath, tmpShape; - const shapes = []; - - if ( subPaths.length === 1 ) { - - tmpPath = subPaths[ 0 ]; - tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; - shapes.push( tmpShape ); - return shapes; - - } - - let holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); - holesFirst = isCCW ? ! holesFirst : holesFirst; - - // console.log("Holes first", holesFirst); - - const betterShapeHoles = []; - const newShapes = []; - let newShapeHoles = []; - let mainIdx = 0; - let tmpPoints; - - newShapes[ mainIdx ] = undefined; - newShapeHoles[ mainIdx ] = []; - - for ( let i = 0, l = subPaths.length; i < l; i ++ ) { - - tmpPath = subPaths[ i ]; - tmpPoints = tmpPath.getPoints(); - solid = isClockWise( tmpPoints ); - solid = isCCW ? ! solid : solid; - - if ( solid ) { - - if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; - - newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; - newShapes[ mainIdx ].s.curves = tmpPath.curves; - - if ( holesFirst ) mainIdx ++; - newShapeHoles[ mainIdx ] = []; - - //console.log('cw', i); - - } else { - - newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); - - //console.log('ccw', i); - - } - - } - - // only Holes? -> probably all Shapes with wrong orientation - if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); - - - if ( newShapes.length > 1 ) { - - let ambiguous = false; - const toChange = []; - - for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - - betterShapeHoles[ sIdx ] = []; - - } - - for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - - const sho = newShapeHoles[ sIdx ]; - - for ( let hIdx = 0; hIdx < sho.length; hIdx ++ ) { - - const ho = sho[ hIdx ]; - let hole_unassigned = true; - - for ( let s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { - - if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { - - if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); - if ( hole_unassigned ) { - - hole_unassigned = false; - betterShapeHoles[ s2Idx ].push( ho ); - - } else { - - ambiguous = true; - - } - - } - - } - - if ( hole_unassigned ) { - - betterShapeHoles[ sIdx ].push( ho ); - - } - - } - - } - // console.log("ambiguous: ", ambiguous); - - if ( toChange.length > 0 ) { - - // console.log("to change: ", toChange); - if ( ! ambiguous ) newShapeHoles = betterShapeHoles; - - } - - } - - let tmpHoles; - - for ( let i = 0, il = newShapes.length; i < il; i ++ ) { - - tmpShape = newShapes[ i ].s; - shapes.push( tmpShape ); - tmpHoles = newShapeHoles[ i ]; - - for ( let j = 0, jl = tmpHoles.length; j < jl; j ++ ) { - - tmpShape.holes.push( tmpHoles[ j ].h ); - - } - - } - - //console.log("shape", shapes); - - return shapes; - - } - - } ); - - function Font( data ) { - - this.type = 'Font'; - - this.data = data; - - } - - Object.assign( Font.prototype, { - - isFont: true, - - generateShapes: function ( text, size ) { - - if ( size === undefined ) size = 100; - - const shapes = []; - const paths = createPaths( text, size, this.data ); - - for ( let p = 0, pl = paths.length; p < pl; p ++ ) { - - Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); - - } - - return shapes; - - } - - } ); - - function createPaths( text, size, data ) { - - const chars = Array.from ? Array.from( text ) : String( text ).split( '' ); // workaround for IE11, see #13988 - const scale = size / data.resolution; - const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; - - const paths = []; - - let offsetX = 0, offsetY = 0; - - for ( let i = 0; i < chars.length; i ++ ) { - - const char = chars[ i ]; - - if ( char === '\n' ) { - - offsetX = 0; - offsetY -= line_height; - - } else { - - const ret = createPath( char, scale, offsetX, offsetY, data ); - offsetX += ret.offsetX; - paths.push( ret.path ); - - } - - } - - return paths; - - } - - function createPath( char, scale, offsetX, offsetY, data ) { - - const glyph = data.glyphs[ char ] || data.glyphs[ '?' ]; - - if ( ! glyph ) { - - console.error( 'THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.' ); - - return; - - } - - const path = new ShapePath(); - - let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; - - if ( glyph.o ) { - - const outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); - - for ( let i = 0, l = outline.length; i < l; ) { - - const action = outline[ i ++ ]; - - switch ( action ) { - - case 'm': // moveTo - - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; - - path.moveTo( x, y ); - - break; - - case 'l': // lineTo - - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; - - path.lineTo( x, y ); - - break; - - case 'q': // quadraticCurveTo - - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; - - path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); - - break; - - case 'b': // bezierCurveTo - - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; - cpx2 = outline[ i ++ ] * scale + offsetX; - cpy2 = outline[ i ++ ] * scale + offsetY; - - path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); - - break; - - } - - } - - } - - return { offsetX: glyph.ha * scale, path: path }; - - } - - function FontLoader( manager ) { - - Loader.call( this, manager ); - - } - - FontLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: FontLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setRequestHeader( this.requestHeader ); - loader.load( url, function ( text ) { - - let json; - - try { - - json = JSON.parse( text ); - - } catch ( e ) { - - console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' ); - json = JSON.parse( text.substring( 65, text.length - 2 ) ); - - } - - const font = scope.parse( json ); - - if ( onLoad ) onLoad( font ); - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - return new Font( json ); - - } - - } ); - - let _context; - - const AudioContext = { - - getContext: function () { - - if ( _context === undefined ) { - - _context = new ( window.AudioContext || window.webkitAudioContext )(); - - } - - return _context; - - }, - - setContext: function ( value ) { - - _context = value; - - } - - }; - - function AudioLoader( manager ) { - - Loader.call( this, manager ); - - } - - AudioLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: AudioLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - const scope = this; - - const loader = new FileLoader( scope.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.load( url, function ( buffer ) { - - try { - - // Create a copy of the buffer. The `decodeAudioData` method - // detaches the buffer when complete, preventing reuse. - const bufferCopy = buffer.slice( 0 ); - - const context = AudioContext.getContext(); - context.decodeAudioData( bufferCopy, function ( audioBuffer ) { - - onLoad( audioBuffer ); - - } ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - } - - } ); - - function HemisphereLightProbe( skyColor, groundColor, intensity ) { - - LightProbe.call( this, undefined, intensity ); - - const color1 = new Color().set( skyColor ); - const color2 = new Color().set( groundColor ); - - const sky = new Vector3( color1.r, color1.g, color1.b ); - const ground = new Vector3( color2.r, color2.g, color2.b ); - - // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); - const c0 = Math.sqrt( Math.PI ); - const c1 = c0 * Math.sqrt( 0.75 ); - - this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); - this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); - - } - - HemisphereLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), { - - constructor: HemisphereLightProbe, - - isHemisphereLightProbe: true, - - copy: function ( source ) { // modifying colors not currently supported - - LightProbe.prototype.copy.call( this, source ); - - return this; - - }, - - toJSON: function ( meta ) { - - const data = LightProbe.prototype.toJSON.call( this, meta ); - - // data.sh = this.sh.toArray(); // todo - - return data; - - } - - } ); - - function AmbientLightProbe( color, intensity ) { - - LightProbe.call( this, undefined, intensity ); - - const color1 = new Color().set( color ); - - // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); - this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); - - } - - AmbientLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), { - - constructor: AmbientLightProbe, - - isAmbientLightProbe: true, - - copy: function ( source ) { // modifying color not currently supported - - LightProbe.prototype.copy.call( this, source ); - - return this; - - }, - - toJSON: function ( meta ) { - - const data = LightProbe.prototype.toJSON.call( this, meta ); - - // data.sh = this.sh.toArray(); // todo - - return data; - - } - - } ); - - const _eyeRight = new Matrix4(); - const _eyeLeft = new Matrix4(); - - function StereoCamera() { - - this.type = 'StereoCamera'; - - this.aspect = 1; - - this.eyeSep = 0.064; - - this.cameraL = new PerspectiveCamera(); - this.cameraL.layers.enable( 1 ); - this.cameraL.matrixAutoUpdate = false; - - this.cameraR = new PerspectiveCamera(); - this.cameraR.layers.enable( 2 ); - this.cameraR.matrixAutoUpdate = false; - - this._cache = { - focus: null, - fov: null, - aspect: null, - near: null, - far: null, - zoom: null, - eyeSep: null - }; - - } - - Object.assign( StereoCamera.prototype, { - - update: function ( camera ) { - - const cache = this._cache; - - const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || - cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || - cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; - - if ( needsUpdate ) { - - cache.focus = camera.focus; - cache.fov = camera.fov; - cache.aspect = camera.aspect * this.aspect; - cache.near = camera.near; - cache.far = camera.far; - cache.zoom = camera.zoom; - cache.eyeSep = this.eyeSep; - - // Off-axis stereoscopic effect based on - // http://paulbourke.net/stereographics/stereorender/ - - const projectionMatrix = camera.projectionMatrix.clone(); - const eyeSepHalf = cache.eyeSep / 2; - const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; - const ymax = ( cache.near * Math.tan( MathUtils.DEG2RAD * cache.fov * 0.5 ) ) / cache.zoom; - let xmin, xmax; - - // translate xOffset - - _eyeLeft.elements[ 12 ] = - eyeSepHalf; - _eyeRight.elements[ 12 ] = eyeSepHalf; - - // for left eye - - xmin = - ymax * cache.aspect + eyeSepOnProjection; - xmax = ymax * cache.aspect + eyeSepOnProjection; - - projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); - projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); - - this.cameraL.projectionMatrix.copy( projectionMatrix ); - - // for right eye - - xmin = - ymax * cache.aspect - eyeSepOnProjection; - xmax = ymax * cache.aspect - eyeSepOnProjection; - - projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); - projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); - - this.cameraR.projectionMatrix.copy( projectionMatrix ); - - } - - this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeLeft ); - this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeRight ); - - } - - } ); - - class Clock { - - constructor( autoStart ) { - - this.autoStart = ( autoStart !== undefined ) ? autoStart : true; - - this.startTime = 0; - this.oldTime = 0; - this.elapsedTime = 0; - - this.running = false; - - } - - start() { - - this.startTime = ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732 - - this.oldTime = this.startTime; - this.elapsedTime = 0; - this.running = true; - - } - - stop() { - - this.getElapsedTime(); - this.running = false; - this.autoStart = false; - - } - - getElapsedTime() { - - this.getDelta(); - return this.elapsedTime; - - } - - getDelta() { - - let diff = 0; - - if ( this.autoStart && ! this.running ) { - - this.start(); - return 0; - - } - - if ( this.running ) { - - const newTime = ( typeof performance === 'undefined' ? Date : performance ).now(); - - diff = ( newTime - this.oldTime ) / 1000; - this.oldTime = newTime; - - this.elapsedTime += diff; - - } - - return diff; - - } - - } - - const _position$2 = new Vector3(); - const _quaternion$3 = new Quaternion(); - const _scale$1 = new Vector3(); - const _orientation = new Vector3(); - - class Audio extends Object3D { - - constructor( listener ) { - - super(); - - this.type = 'Audio'; - - this.listener = listener; - this.context = listener.context; - - this.gain = this.context.createGain(); - this.gain.connect( listener.getInput() ); - - this.autoplay = false; - - this.buffer = null; - this.detune = 0; - this.loop = false; - this.loopStart = 0; - this.loopEnd = 0; - this.offset = 0; - this.duration = undefined; - this.playbackRate = 1; - this.isPlaying = false; - this.hasPlaybackControl = true; - this.source = null; - this.sourceType = 'empty'; - - this._startedAt = 0; - this._progress = 0; - this._connected = false; - - this.filters = []; - - } - - getOutput() { - - return this.gain; - - } - - setNodeSource( audioNode ) { - - this.hasPlaybackControl = false; - this.sourceType = 'audioNode'; - this.source = audioNode; - this.connect(); - - return this; - - } - - setMediaElementSource( mediaElement ) { - - this.hasPlaybackControl = false; - this.sourceType = 'mediaNode'; - this.source = this.context.createMediaElementSource( mediaElement ); - this.connect(); - - return this; - - } - - setMediaStreamSource( mediaStream ) { - - this.hasPlaybackControl = false; - this.sourceType = 'mediaStreamNode'; - this.source = this.context.createMediaStreamSource( mediaStream ); - this.connect(); - - return this; - - } - - setBuffer( audioBuffer ) { - - this.buffer = audioBuffer; - this.sourceType = 'buffer'; - - if ( this.autoplay ) this.play(); - - return this; - - } - - play( delay ) { - - if ( delay === undefined ) delay = 0; - - if ( this.isPlaying === true ) { - - console.warn( 'THREE.Audio: Audio is already playing.' ); - return; - - } - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this._startedAt = this.context.currentTime + delay; - - const source = this.context.createBufferSource(); - source.buffer = this.buffer; - source.loop = this.loop; - source.loopStart = this.loopStart; - source.loopEnd = this.loopEnd; - source.onended = this.onEnded.bind( this ); - source.start( this._startedAt, this._progress + this.offset, this.duration ); - - this.isPlaying = true; - - this.source = source; - - this.setDetune( this.detune ); - this.setPlaybackRate( this.playbackRate ); - - return this.connect(); - - } - - pause() { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - if ( this.isPlaying === true ) { - - // update current progress - - this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; - - if ( this.loop === true ) { - - // ensure _progress does not exceed duration with looped audios - - this._progress = this._progress % ( this.duration || this.buffer.duration ); - - } - - this.source.stop(); - this.source.onended = null; - - this.isPlaying = false; - - } - - return this; - - } - - stop() { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this._progress = 0; - - this.source.stop(); - this.source.onended = null; - this.isPlaying = false; - - return this; - - } - - connect() { - - if ( this.filters.length > 0 ) { - - this.source.connect( this.filters[ 0 ] ); - - for ( let i = 1, l = this.filters.length; i < l; i ++ ) { - - this.filters[ i - 1 ].connect( this.filters[ i ] ); - - } - - this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); - - } else { - - this.source.connect( this.getOutput() ); - - } - - this._connected = true; - - return this; - - } - - disconnect() { - - if ( this.filters.length > 0 ) { - - this.source.disconnect( this.filters[ 0 ] ); - - for ( let i = 1, l = this.filters.length; i < l; i ++ ) { - - this.filters[ i - 1 ].disconnect( this.filters[ i ] ); - - } - - this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); - - } else { - - this.source.disconnect( this.getOutput() ); - - } - - this._connected = false; - - return this; - - } - - getFilters() { - - return this.filters; - - } - - setFilters( value ) { - - if ( ! value ) value = []; - - if ( this._connected === true ) { - - this.disconnect(); - this.filters = value; - this.connect(); - - } else { - - this.filters = value; - - } - - return this; - - } - - setDetune( value ) { - - this.detune = value; - - if ( this.source.detune === undefined ) return; // only set detune when available - - if ( this.isPlaying === true ) { - - this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); - - } - - return this; - - } - - getDetune() { - - return this.detune; - - } - - getFilter() { - - return this.getFilters()[ 0 ]; - - } - - setFilter( filter ) { - - return this.setFilters( filter ? [ filter ] : [] ); - - } - - setPlaybackRate( value ) { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this.playbackRate = value; - - if ( this.isPlaying === true ) { - - this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); - - } - - return this; - - } - - getPlaybackRate() { - - return this.playbackRate; - - } - - onEnded() { - - this.isPlaying = false; - - } - - getLoop() { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return false; - - } - - return this.loop; - - } - - setLoop( value ) { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this.loop = value; - - if ( this.isPlaying === true ) { - - this.source.loop = this.loop; - - } - - return this; - - } - - setLoopStart( value ) { - - this.loopStart = value; - - return this; - - } - - setLoopEnd( value ) { - - this.loopEnd = value; - - return this; - - } - - getVolume() { - - return this.gain.gain.value; - - } - - setVolume( value ) { - - this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); - - return this; - - } - - } - - const _position$3 = new Vector3(); - const _quaternion$4 = new Quaternion(); - const _scale$2 = new Vector3(); - const _orientation$1 = new Vector3(); - - function PropertyMixer( binding, typeName, valueSize ) { - - this.binding = binding; - this.valueSize = valueSize; - - let mixFunction, - mixFunctionAdditive, - setIdentity; - - // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] - // - // interpolators can use .buffer as their .result - // the data then goes to 'incoming' - // - // 'accu0' and 'accu1' are used frame-interleaved for - // the cumulative result and are compared to detect - // changes - // - // 'orig' stores the original state of the property - // - // 'add' is used for additive cumulative results - // - // 'work' is optional and is only present for quaternion types. It is used - // to store intermediate quaternion multiplication results - - switch ( typeName ) { - - case 'quaternion': - mixFunction = this._slerp; - mixFunctionAdditive = this._slerpAdditive; - setIdentity = this._setAdditiveIdentityQuaternion; - - this.buffer = new Float64Array( valueSize * 6 ); - this._workIndex = 5; - break; - - case 'string': - case 'bool': - mixFunction = this._select; - - // Use the regular mix function and for additive on these types, - // additive is not relevant for non-numeric types - mixFunctionAdditive = this._select; - - setIdentity = this._setAdditiveIdentityOther; - - this.buffer = new Array( valueSize * 5 ); - break; - - default: - mixFunction = this._lerp; - mixFunctionAdditive = this._lerpAdditive; - setIdentity = this._setAdditiveIdentityNumeric; - - this.buffer = new Float64Array( valueSize * 5 ); - - } - - this._mixBufferRegion = mixFunction; - this._mixBufferRegionAdditive = mixFunctionAdditive; - this._setIdentity = setIdentity; - this._origIndex = 3; - this._addIndex = 4; - - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; - - this.useCount = 0; - this.referenceCount = 0; - - } - - Object.assign( PropertyMixer.prototype, { - - // accumulate data in the 'incoming' region into 'accu' - accumulate: function ( accuIndex, weight ) { - - // note: happily accumulating nothing when weight = 0, the caller knows - // the weight and shouldn't have made the call in the first place - - const buffer = this.buffer, - stride = this.valueSize, - offset = accuIndex * stride + stride; - - let currentWeight = this.cumulativeWeight; - - if ( currentWeight === 0 ) { - - // accuN := incoming * weight - - for ( let i = 0; i !== stride; ++ i ) { - - buffer[ offset + i ] = buffer[ i ]; - - } - - currentWeight = weight; - - } else { - - // accuN := accuN + incoming * weight - - currentWeight += weight; - const mix = weight / currentWeight; - this._mixBufferRegion( buffer, offset, 0, mix, stride ); - - } - - this.cumulativeWeight = currentWeight; - - }, - - // accumulate data in the 'incoming' region into 'add' - accumulateAdditive: function ( weight ) { - - const buffer = this.buffer, - stride = this.valueSize, - offset = stride * this._addIndex; - - if ( this.cumulativeWeightAdditive === 0 ) { - - // add = identity - - this._setIdentity(); - - } - - // add := add + incoming * weight - - this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); - this.cumulativeWeightAdditive += weight; - - }, - - // apply the state of 'accu' to the binding when accus differ - apply: function ( accuIndex ) { - - const stride = this.valueSize, - buffer = this.buffer, - offset = accuIndex * stride + stride, - - weight = this.cumulativeWeight, - weightAdditive = this.cumulativeWeightAdditive, - - binding = this.binding; - - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; - - if ( weight < 1 ) { - - // accuN := accuN + original * ( 1 - cumulativeWeight ) - - const originalValueOffset = stride * this._origIndex; - - this._mixBufferRegion( - buffer, offset, originalValueOffset, 1 - weight, stride ); - - } - - if ( weightAdditive > 0 ) { - - // accuN := accuN + additive accuN - - this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride ); - - } - - for ( let i = stride, e = stride + stride; i !== e; ++ i ) { - - if ( buffer[ i ] !== buffer[ i + stride ] ) { - - // value has changed -> update scene graph - - binding.setValue( buffer, offset ); - break; - - } - - } - - }, - - // remember the state of the bound property and copy it to both accus - saveOriginalState: function () { - - const binding = this.binding; - - const buffer = this.buffer, - stride = this.valueSize, - - originalValueOffset = stride * this._origIndex; - - binding.getValue( buffer, originalValueOffset ); - - // accu[0..1] := orig -- initially detect changes against the original - for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) { - - buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; - - } - - // Add to identity for additive - this._setIdentity(); - - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; - - }, - - // apply the state previously taken via 'saveOriginalState' to the binding - restoreOriginalState: function () { - - const originalValueOffset = this.valueSize * 3; - this.binding.setValue( this.buffer, originalValueOffset ); - - }, - - _setAdditiveIdentityNumeric: function () { - - const startIndex = this._addIndex * this.valueSize; - const endIndex = startIndex + this.valueSize; - - for ( let i = startIndex; i < endIndex; i ++ ) { - - this.buffer[ i ] = 0; - - } - - }, - - _setAdditiveIdentityQuaternion: function () { - - this._setAdditiveIdentityNumeric(); - this.buffer[ this._addIndex * this.valueSize + 3 ] = 1; - - }, - - _setAdditiveIdentityOther: function () { - - const startIndex = this._origIndex * this.valueSize; - const targetIndex = this._addIndex * this.valueSize; - - for ( let i = 0; i < this.valueSize; i ++ ) { - - this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ]; - - } - - }, - - - // mix functions - - _select: function ( buffer, dstOffset, srcOffset, t, stride ) { - - if ( t >= 0.5 ) { - - for ( let i = 0; i !== stride; ++ i ) { - - buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; - - } - - } - - }, - - _slerp: function ( buffer, dstOffset, srcOffset, t ) { - - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); - - }, - - _slerpAdditive: function ( buffer, dstOffset, srcOffset, t, stride ) { - - const workOffset = this._workIndex * stride; - - // Store result in intermediate buffer offset - Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset ); - - // Slerp to the intermediate result - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); - - }, - - _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) { - - const s = 1 - t; - - for ( let i = 0; i !== stride; ++ i ) { - - const j = dstOffset + i; - - buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; - - } - - }, - - _lerpAdditive: function ( buffer, dstOffset, srcOffset, t, stride ) { - - for ( let i = 0; i !== stride; ++ i ) { - - const j = dstOffset + i; - - buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t; - - } - - } - - } ); - - // Characters [].:/ are reserved for track binding syntax. - const _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; - const _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' ); - - // Attempts to allow node names from any language. ES5's `\w` regexp matches - // only latin characters, and the unicode \p{L} is not yet supported. So - // instead, we exclude reserved characters and match everything else. - const _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; - const _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\.', '' ) + ']'; - - // Parent directories, delimited by '/' or ':'. Currently unused, but must - // be matched to parse the rest of the track name. - const _directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar ); - - // Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. - const _nodeRe = /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot ); - - // Object on target node, and accessor. May not contain reserved - // characters. Accessor may contain any character except closing bracket. - const _objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar ); - - // Property and accessor. May not contain reserved characters. Accessor may - // contain any non-bracket characters. - const _propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar ); - - const _trackRe = new RegExp( '' - + '^' - + _directoryRe - + _nodeRe - + _objectRe - + _propertyRe - + '$' - ); - - const _supportedObjectNames = [ 'material', 'materials', 'bones' ]; - - function Composite( targetGroup, path, optionalParsedPath ) { - - const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); - - this._targetGroup = targetGroup; - this._bindings = targetGroup.subscribe_( path, parsedPath ); - - } - - Object.assign( Composite.prototype, { - - getValue: function ( array, offset ) { - - this.bind(); // bind all binding - - const firstValidIndex = this._targetGroup.nCachedObjects_, - binding = this._bindings[ firstValidIndex ]; - - // and only call .getValue on the first - if ( binding !== undefined ) binding.getValue( array, offset ); - - }, - - setValue: function ( array, offset ) { - - const bindings = this._bindings; - - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].setValue( array, offset ); - - } - - }, - - bind: function () { - - const bindings = this._bindings; - - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].bind(); - - } - - }, - - unbind: function () { - - const bindings = this._bindings; - - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].unbind(); - - } - - } - - } ); - - - function PropertyBinding( rootNode, path, parsedPath ) { - - this.path = path; - this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); - - this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; - - this.rootNode = rootNode; - - } - - Object.assign( PropertyBinding, { - - Composite: Composite, - - create: function ( root, path, parsedPath ) { - - if ( ! ( root && root.isAnimationObjectGroup ) ) { - - return new PropertyBinding( root, path, parsedPath ); - - } else { - - return new PropertyBinding.Composite( root, path, parsedPath ); - - } - - }, - - /** - * Replaces spaces with underscores and removes unsupported characters from - * node names, to ensure compatibility with parseTrackName(). - * - * @param {string} name Node name to be sanitized. - * @return {string} - */ - sanitizeNodeName: function ( name ) { - - return name.replace( /\s/g, '_' ).replace( _reservedRe, '' ); - - }, - - parseTrackName: function ( trackName ) { - - const matches = _trackRe.exec( trackName ); - - if ( ! matches ) { - - throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName ); - - } - - const results = { - // directoryName: matches[ 1 ], // (tschw) currently unused - nodeName: matches[ 2 ], - objectName: matches[ 3 ], - objectIndex: matches[ 4 ], - propertyName: matches[ 5 ], // required - propertyIndex: matches[ 6 ] - }; - - const lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' ); - - if ( lastDot !== undefined && lastDot !== - 1 ) { - - const objectName = results.nodeName.substring( lastDot + 1 ); - - // Object names must be checked against an allowlist. Otherwise, there - // is no way to parse 'foo.bar.baz': 'baz' must be a property, but - // 'bar' could be the objectName, or part of a nodeName (which can - // include '.' characters). - if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) { - - results.nodeName = results.nodeName.substring( 0, lastDot ); - results.objectName = objectName; - - } - - } - - if ( results.propertyName === null || results.propertyName.length === 0 ) { - - throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName ); - - } - - return results; - - }, - - findNode: function ( root, nodeName ) { - - if ( ! nodeName || nodeName === "" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { - - return root; - - } - - // search into skeleton bones. - if ( root.skeleton ) { - - const bone = root.skeleton.getBoneByName( nodeName ); - - if ( bone !== undefined ) { - - return bone; - - } - - } - - // search into node subtree. - if ( root.children ) { - - const searchNodeSubtree = function ( children ) { - - for ( let i = 0; i < children.length; i ++ ) { - - const childNode = children[ i ]; - - if ( childNode.name === nodeName || childNode.uuid === nodeName ) { - - return childNode; - - } - - const result = searchNodeSubtree( childNode.children ); - - if ( result ) return result; - - } - - return null; - - }; - - const subTreeNode = searchNodeSubtree( root.children ); - - if ( subTreeNode ) { - - return subTreeNode; - - } - - } - - return null; - - } - - } ); - - Object.assign( PropertyBinding.prototype, { // prototype, continued - - // these are used to "bind" a nonexistent property - _getValue_unavailable: function () {}, - _setValue_unavailable: function () {}, - - BindingType: { - Direct: 0, - EntireArray: 1, - ArrayElement: 2, - HasFromToArray: 3 - }, - - Versioning: { - None: 0, - NeedsUpdate: 1, - MatrixWorldNeedsUpdate: 2 - }, - - GetterByBindingType: [ - - function getValue_direct( buffer, offset ) { - - buffer[ offset ] = this.node[ this.propertyName ]; - - }, - - function getValue_array( buffer, offset ) { - - const source = this.resolvedProperty; - - for ( let i = 0, n = source.length; i !== n; ++ i ) { - - buffer[ offset ++ ] = source[ i ]; - - } - - }, - - function getValue_arrayElement( buffer, offset ) { - - buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; - - }, - - function getValue_toArray( buffer, offset ) { - - this.resolvedProperty.toArray( buffer, offset ); - - } - - ], - - SetterByBindingTypeAndVersioning: [ - - [ - // Direct - - function setValue_direct( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - - }, - - function setValue_direct_setNeedsUpdate( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; - - }, - - function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ], [ - - // EntireArray - - function setValue_array( buffer, offset ) { - - const dest = this.resolvedProperty; - - for ( let i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - }, - - function setValue_array_setNeedsUpdate( buffer, offset ) { - - const dest = this.resolvedProperty; - - for ( let i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - this.targetObject.needsUpdate = true; - - }, - - function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { - - const dest = this.resolvedProperty; - - for ( let i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ], [ - - // ArrayElement - - function setValue_arrayElement( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - - }, - - function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; - - }, - - function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ], [ - - // HasToFromArray - - function setValue_fromArray( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - - }, - - function setValue_fromArray_setNeedsUpdate( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.needsUpdate = true; - - }, - - function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ] - - ], - - getValue: function getValue_unbound( targetArray, offset ) { - - this.bind(); - this.getValue( targetArray, offset ); - - // Note: This class uses a State pattern on a per-method basis: - // 'bind' sets 'this.getValue' / 'setValue' and shadows the - // prototype version of these methods with one that represents - // the bound state. When the property is not found, the methods - // become no-ops. - - }, - - setValue: function getValue_unbound( sourceArray, offset ) { - - this.bind(); - this.setValue( sourceArray, offset ); - - }, - - // create getter / setter pair for a property in the scene graph - bind: function () { - - let targetObject = this.node; - const parsedPath = this.parsedPath; - - const objectName = parsedPath.objectName; - const propertyName = parsedPath.propertyName; - let propertyIndex = parsedPath.propertyIndex; - - if ( ! targetObject ) { - - targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; - - this.node = targetObject; - - } - - // set fail state so we can just 'return' on error - this.getValue = this._getValue_unavailable; - this.setValue = this._setValue_unavailable; - - // ensure there is a value node - if ( ! targetObject ) { - - console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.' ); - return; - - } - - if ( objectName ) { - - let objectIndex = parsedPath.objectIndex; - - // special cases were we need to reach deeper into the hierarchy to get the face materials.... - switch ( objectName ) { - - case 'materials': - - if ( ! targetObject.material ) { - - console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this ); - return; - - } - - if ( ! targetObject.material.materials ) { - - console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this ); - return; - - } - - targetObject = targetObject.material.materials; - - break; - - case 'bones': - - if ( ! targetObject.skeleton ) { - - console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this ); - return; - - } - - // potential future optimization: skip this if propertyIndex is already an integer - // and convert the integer string to a true integer. - - targetObject = targetObject.skeleton.bones; - - // support resolving morphTarget names into indices. - for ( let i = 0; i < targetObject.length; i ++ ) { - - if ( targetObject[ i ].name === objectIndex ) { - - objectIndex = i; - break; - - } - - } - - break; - - default: - - if ( targetObject[ objectName ] === undefined ) { - - console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this ); - return; - - } - - targetObject = targetObject[ objectName ]; - - } - - - if ( objectIndex !== undefined ) { - - if ( targetObject[ objectIndex ] === undefined ) { - - console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject ); - return; - - } - - targetObject = targetObject[ objectIndex ]; - - } - - } - - // resolve property - const nodeProperty = targetObject[ propertyName ]; - - if ( nodeProperty === undefined ) { - - const nodeName = parsedPath.nodeName; - - console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName + - '.' + propertyName + ' but it wasn\'t found.', targetObject ); - return; - - } - - // determine versioning scheme - let versioning = this.Versioning.None; - - this.targetObject = targetObject; - - if ( targetObject.needsUpdate !== undefined ) { // material - - versioning = this.Versioning.NeedsUpdate; - - } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform - - versioning = this.Versioning.MatrixWorldNeedsUpdate; - - } - - // determine how the property gets bound - let bindingType = this.BindingType.Direct; - - if ( propertyIndex !== undefined ) { - - // access a sub element of the property array (only primitives are supported right now) - - if ( propertyName === "morphTargetInfluences" ) { - - // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. - - // support resolving morphTarget names into indices. - if ( ! targetObject.geometry ) { - - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this ); - return; - - } - - if ( targetObject.geometry.isBufferGeometry ) { - - if ( ! targetObject.geometry.morphAttributes ) { - - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this ); - return; - - } - - if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) { - - propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ]; - - } - - - } else { - - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.', this ); - return; - - } - - } - - bindingType = this.BindingType.ArrayElement; - - this.resolvedProperty = nodeProperty; - this.propertyIndex = propertyIndex; - - } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { - - // must use copy for Object3D.Euler/Quaternion - - bindingType = this.BindingType.HasFromToArray; - - this.resolvedProperty = nodeProperty; - - } else if ( Array.isArray( nodeProperty ) ) { - - bindingType = this.BindingType.EntireArray; - - this.resolvedProperty = nodeProperty; - - } else { - - this.propertyName = propertyName; - - } - - // select getter / setter - this.getValue = this.GetterByBindingType[ bindingType ]; - this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; - - }, - - unbind: function () { - - this.node = null; - - // back to the prototype version of getValue / setValue - // note: avoiding to mutate the shape of 'this' via 'delete' - this.getValue = this._getValue_unbound; - this.setValue = this._setValue_unbound; - - } - - } ); - - // DECLARE ALIAS AFTER assign prototype - Object.assign( PropertyBinding.prototype, { - - // initial state of these methods that calls 'bind' - _getValue_unbound: PropertyBinding.prototype.getValue, - _setValue_unbound: PropertyBinding.prototype.setValue, - - } ); - - /** - * - * A group of objects that receives a shared animation state. - * - * Usage: - * - * - Add objects you would otherwise pass as 'root' to the - * constructor or the .clipAction method of AnimationMixer. - * - * - Instead pass this object as 'root'. - * - * - You can also add and remove objects later when the mixer - * is running. - * - * Note: - * - * Objects of this class appear as one object to the mixer, - * so cache control of the individual objects must be done - * on the group. - * - * Limitation: - * - * - The animated properties must be compatible among the - * all objects in the group. - * - * - A single property can either be controlled through a - * target group or directly, but not both. - */ - - function AnimationObjectGroup() { - - this.uuid = MathUtils.generateUUID(); - - // cached objects followed by the active ones - this._objects = Array.prototype.slice.call( arguments ); - - this.nCachedObjects_ = 0; // threshold - // note: read by PropertyBinding.Composite - - const indices = {}; - this._indicesByUUID = indices; // for bookkeeping - - for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - - indices[ arguments[ i ].uuid ] = i; - - } - - this._paths = []; // inside: string - this._parsedPaths = []; // inside: { we don't care, here } - this._bindings = []; // inside: Array< PropertyBinding > - this._bindingsIndicesByPath = {}; // inside: indices in these arrays - - const scope = this; - - this.stats = { - - objects: { - get total() { - - return scope._objects.length; - - }, - get inUse() { - - return this.total - scope.nCachedObjects_; - - } - }, - get bindingsPerObject() { - - return scope._bindings.length; - - } - - }; - - } - - Object.assign( AnimationObjectGroup.prototype, { - - isAnimationObjectGroup: true, - - add: function () { - - const objects = this._objects, - indicesByUUID = this._indicesByUUID, - paths = this._paths, - parsedPaths = this._parsedPaths, - bindings = this._bindings, - nBindings = bindings.length; - - let knownObject = undefined, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_; - - for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - - const object = arguments[ i ], - uuid = object.uuid; - let index = indicesByUUID[ uuid ]; - - if ( index === undefined ) { - - // unknown object -> add it to the ACTIVE region - - index = nObjects ++; - indicesByUUID[ uuid ] = index; - objects.push( object ); - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); - - } - - } else if ( index < nCachedObjects ) { - - knownObject = objects[ index ]; - - // move existing object to the ACTIVE region - - const firstActiveIndex = -- nCachedObjects, - lastCachedObject = objects[ firstActiveIndex ]; - - indicesByUUID[ lastCachedObject.uuid ] = index; - objects[ index ] = lastCachedObject; - - indicesByUUID[ uuid ] = firstActiveIndex; - objects[ firstActiveIndex ] = object; - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - const bindingsForPath = bindings[ j ], - lastCached = bindingsForPath[ firstActiveIndex ]; - - let binding = bindingsForPath[ index ]; - - bindingsForPath[ index ] = lastCached; - - if ( binding === undefined ) { - - // since we do not bother to create new bindings - // for objects that are cached, the binding may - // or may not exist - - binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); - - } - - bindingsForPath[ firstActiveIndex ] = binding; - - } - - } else if ( objects[ index ] !== knownObject ) { - - console.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' + - 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' ); - - } // else the object is already where we want it to be - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - remove: function () { - - const objects = this._objects, - indicesByUUID = this._indicesByUUID, - bindings = this._bindings, - nBindings = bindings.length; - - let nCachedObjects = this.nCachedObjects_; - - for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - - const object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; - - if ( index !== undefined && index >= nCachedObjects ) { - - // move existing object into the CACHED region - - const lastCachedIndex = nCachedObjects ++, - firstActiveObject = objects[ lastCachedIndex ]; - - indicesByUUID[ firstActiveObject.uuid ] = index; - objects[ index ] = firstActiveObject; - - indicesByUUID[ uuid ] = lastCachedIndex; - objects[ lastCachedIndex ] = object; - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - const bindingsForPath = bindings[ j ], - firstActive = bindingsForPath[ lastCachedIndex ], - binding = bindingsForPath[ index ]; - - bindingsForPath[ index ] = firstActive; - bindingsForPath[ lastCachedIndex ] = binding; - - } - - } - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - // remove & forget - uncache: function () { - - const objects = this._objects, - indicesByUUID = this._indicesByUUID, - bindings = this._bindings, - nBindings = bindings.length; - - let nCachedObjects = this.nCachedObjects_, - nObjects = objects.length; - - for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - - const object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; - - if ( index !== undefined ) { - - delete indicesByUUID[ uuid ]; - - if ( index < nCachedObjects ) { - - // object is cached, shrink the CACHED region - - const firstActiveIndex = -- nCachedObjects, - lastCachedObject = objects[ firstActiveIndex ], - lastIndex = -- nObjects, - lastObject = objects[ lastIndex ]; - - // last cached object takes this object's place - indicesByUUID[ lastCachedObject.uuid ] = index; - objects[ index ] = lastCachedObject; - - // last object goes to the activated slot and pop - indicesByUUID[ lastObject.uuid ] = firstActiveIndex; - objects[ firstActiveIndex ] = lastObject; - objects.pop(); - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - const bindingsForPath = bindings[ j ], - lastCached = bindingsForPath[ firstActiveIndex ], - last = bindingsForPath[ lastIndex ]; - - bindingsForPath[ index ] = lastCached; - bindingsForPath[ firstActiveIndex ] = last; - bindingsForPath.pop(); - - } - - } else { - - // object is active, just swap with the last and pop - - const lastIndex = -- nObjects, - lastObject = objects[ lastIndex ]; - - indicesByUUID[ lastObject.uuid ] = index; - objects[ index ] = lastObject; - objects.pop(); - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - const bindingsForPath = bindings[ j ]; - - bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; - bindingsForPath.pop(); - - } - - } // cached or active - - } // if object is known - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - // Internal interface used by befriended PropertyBinding.Composite: - - subscribe_: function ( path, parsedPath ) { - - // returns an array of bindings for the given path that is changed - // according to the contained objects in the group - - const indicesByPath = this._bindingsIndicesByPath; - let index = indicesByPath[ path ]; - const bindings = this._bindings; - - if ( index !== undefined ) return bindings[ index ]; - - const paths = this._paths, - parsedPaths = this._parsedPaths, - objects = this._objects, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_, - bindingsForPath = new Array( nObjects ); - - index = bindings.length; - - indicesByPath[ path ] = index; - - paths.push( path ); - parsedPaths.push( parsedPath ); - bindings.push( bindingsForPath ); - - for ( let i = nCachedObjects, n = objects.length; i !== n; ++ i ) { - - const object = objects[ i ]; - bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); - - } - - return bindingsForPath; - - }, - - unsubscribe_: function ( path ) { - - // tells the group to forget about a property path and no longer - // update the array previously obtained with 'subscribe_' - - const indicesByPath = this._bindingsIndicesByPath, - index = indicesByPath[ path ]; - - if ( index !== undefined ) { - - const paths = this._paths, - parsedPaths = this._parsedPaths, - bindings = this._bindings, - lastBindingsIndex = bindings.length - 1, - lastBindings = bindings[ lastBindingsIndex ], - lastBindingsPath = path[ lastBindingsIndex ]; - - indicesByPath[ lastBindingsPath ] = index; - - bindings[ index ] = lastBindings; - bindings.pop(); - - parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; - parsedPaths.pop(); - - paths[ index ] = paths[ lastBindingsIndex ]; - paths.pop(); - - } - - } - - } ); - - class AnimationAction { - - constructor( mixer, clip, localRoot, blendMode ) { - - this._mixer = mixer; - this._clip = clip; - this._localRoot = localRoot || null; - this.blendMode = blendMode || clip.blendMode; - - const tracks = clip.tracks, - nTracks = tracks.length, - interpolants = new Array( nTracks ); - - const interpolantSettings = { - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - }; - - for ( let i = 0; i !== nTracks; ++ i ) { - - const interpolant = tracks[ i ].createInterpolant( null ); - interpolants[ i ] = interpolant; - interpolant.settings = interpolantSettings; - - } - - this._interpolantSettings = interpolantSettings; - - this._interpolants = interpolants; // bound by the mixer - - // inside: PropertyMixer (managed by the mixer) - this._propertyBindings = new Array( nTracks ); - - this._cacheIndex = null; // for the memory manager - this._byClipCacheIndex = null; // for the memory manager - - this._timeScaleInterpolant = null; - this._weightInterpolant = null; - - this.loop = LoopRepeat; - this._loopCount = - 1; - - // global mixer time when the action is to be started - // it's set back to 'null' upon start of the action - this._startTime = null; - - // scaled local time of the action - // gets clamped or wrapped to 0..clip.duration according to loop - this.time = 0; - - this.timeScale = 1; - this._effectiveTimeScale = 1; - - this.weight = 1; - this._effectiveWeight = 1; - - this.repetitions = Infinity; // no. of repetitions when looping - - this.paused = false; // true -> zero effective time scale - this.enabled = true; // false -> zero effective weight - - this.clampWhenFinished = false;// keep feeding the last frame? - - this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate - this.zeroSlopeAtEnd = true;// clips for start, loop and end - - } - - // State & Scheduling - - play() { - - this._mixer._activateAction( this ); - - return this; - - } - - stop() { - - this._mixer._deactivateAction( this ); - - return this.reset(); - - } - - reset() { - - this.paused = false; - this.enabled = true; - - this.time = 0; // restart clip - this._loopCount = - 1;// forget previous loops - this._startTime = null;// forget scheduling - - return this.stopFading().stopWarping(); - - } - - isRunning() { - - return this.enabled && ! this.paused && this.timeScale !== 0 && - this._startTime === null && this._mixer._isActiveAction( this ); - - } - - // return true when play has been called - isScheduled() { - - return this._mixer._isActiveAction( this ); - - } - - startAt( time ) { - - this._startTime = time; - - return this; - - } - - setLoop( mode, repetitions ) { - - this.loop = mode; - this.repetitions = repetitions; - - return this; - - } - - // Weight - - // set the weight stopping any scheduled fading - // although .enabled = false yields an effective weight of zero, this - // method does *not* change .enabled, because it would be confusing - setEffectiveWeight( weight ) { - - this.weight = weight; - - // note: same logic as when updated at runtime - this._effectiveWeight = this.enabled ? weight : 0; - - return this.stopFading(); - - } - - // return the weight considering fading and .enabled - getEffectiveWeight() { - - return this._effectiveWeight; - - } - - fadeIn( duration ) { - - return this._scheduleFading( duration, 0, 1 ); - - } - - fadeOut( duration ) { - - return this._scheduleFading( duration, 1, 0 ); - - } - - crossFadeFrom( fadeOutAction, duration, warp ) { - - fadeOutAction.fadeOut( duration ); - this.fadeIn( duration ); - - if ( warp ) { - - const fadeInDuration = this._clip.duration, - fadeOutDuration = fadeOutAction._clip.duration, - - startEndRatio = fadeOutDuration / fadeInDuration, - endStartRatio = fadeInDuration / fadeOutDuration; - - fadeOutAction.warp( 1.0, startEndRatio, duration ); - this.warp( endStartRatio, 1.0, duration ); - - } - - return this; - - } - - crossFadeTo( fadeInAction, duration, warp ) { - - return fadeInAction.crossFadeFrom( this, duration, warp ); - - } - - stopFading() { - - const weightInterpolant = this._weightInterpolant; - - if ( weightInterpolant !== null ) { - - this._weightInterpolant = null; - this._mixer._takeBackControlInterpolant( weightInterpolant ); - - } - - return this; - - } - - // Time Scale Control - - // set the time scale stopping any scheduled warping - // although .paused = true yields an effective time scale of zero, this - // method does *not* change .paused, because it would be confusing - setEffectiveTimeScale( timeScale ) { - - this.timeScale = timeScale; - this._effectiveTimeScale = this.paused ? 0 : timeScale; - - return this.stopWarping(); - - } - - // return the time scale considering warping and .paused - getEffectiveTimeScale() { - - return this._effectiveTimeScale; - - } - - setDuration( duration ) { - - this.timeScale = this._clip.duration / duration; - - return this.stopWarping(); - - } - - syncWith( action ) { - - this.time = action.time; - this.timeScale = action.timeScale; - - return this.stopWarping(); - - } - - halt( duration ) { - - return this.warp( this._effectiveTimeScale, 0, duration ); - - } - - warp( startTimeScale, endTimeScale, duration ) { - - const mixer = this._mixer, - now = mixer.time, - timeScale = this.timeScale; - - let interpolant = this._timeScaleInterpolant; - - if ( interpolant === null ) { - - interpolant = mixer._lendControlInterpolant(); - this._timeScaleInterpolant = interpolant; - - } - - const times = interpolant.parameterPositions, - values = interpolant.sampleValues; - - times[ 0 ] = now; - times[ 1 ] = now + duration; - - values[ 0 ] = startTimeScale / timeScale; - values[ 1 ] = endTimeScale / timeScale; - - return this; - - } - - stopWarping() { - - const timeScaleInterpolant = this._timeScaleInterpolant; - - if ( timeScaleInterpolant !== null ) { - - this._timeScaleInterpolant = null; - this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); - - } - - return this; - - } - - // Object Accessors - - getMixer() { - - return this._mixer; - - } - - getClip() { - - return this._clip; - - } - - getRoot() { - - return this._localRoot || this._mixer._root; - - } - - // Interna - - _update( time, deltaTime, timeDirection, accuIndex ) { - - // called by the mixer - - if ( ! this.enabled ) { - - // call ._updateWeight() to update ._effectiveWeight - - this._updateWeight( time ); - return; - - } - - const startTime = this._startTime; - - if ( startTime !== null ) { - - // check for scheduled start of action - - const timeRunning = ( time - startTime ) * timeDirection; - if ( timeRunning < 0 || timeDirection === 0 ) { - - return; // yet to come / don't decide when delta = 0 - - } - - // start - - this._startTime = null; // unschedule - deltaTime = timeDirection * timeRunning; - - } - - // apply time scale and advance time - - deltaTime *= this._updateTimeScale( time ); - const clipTime = this._updateTime( deltaTime ); - - // note: _updateTime may disable the action resulting in - // an effective weight of 0 - - const weight = this._updateWeight( time ); - - if ( weight > 0 ) { - - const interpolants = this._interpolants; - const propertyMixers = this._propertyBindings; - - switch ( this.blendMode ) { - - case AdditiveAnimationBlendMode: - - for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { - - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulateAdditive( weight ); - - } - - break; - - case NormalAnimationBlendMode: - default: - - for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { - - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulate( accuIndex, weight ); - - } - - } - - } - - } - - _updateWeight( time ) { - - let weight = 0; - - if ( this.enabled ) { - - weight = this.weight; - const interpolant = this._weightInterpolant; - - if ( interpolant !== null ) { - - const interpolantValue = interpolant.evaluate( time )[ 0 ]; - - weight *= interpolantValue; - - if ( time > interpolant.parameterPositions[ 1 ] ) { - - this.stopFading(); - - if ( interpolantValue === 0 ) { - - // faded out, disable - this.enabled = false; - - } - - } - - } - - } - - this._effectiveWeight = weight; - return weight; - - } - - _updateTimeScale( time ) { - - let timeScale = 0; - - if ( ! this.paused ) { - - timeScale = this.timeScale; - - const interpolant = this._timeScaleInterpolant; - - if ( interpolant !== null ) { - - const interpolantValue = interpolant.evaluate( time )[ 0 ]; - - timeScale *= interpolantValue; - - if ( time > interpolant.parameterPositions[ 1 ] ) { - - this.stopWarping(); - - if ( timeScale === 0 ) { - - // motion has halted, pause - this.paused = true; - - } else { - - // warp done - apply final time scale - this.timeScale = timeScale; - - } - - } - - } - - } - - this._effectiveTimeScale = timeScale; - return timeScale; - - } - - _updateTime( deltaTime ) { - - const duration = this._clip.duration; - const loop = this.loop; - - let time = this.time + deltaTime; - let loopCount = this._loopCount; - - const pingPong = ( loop === LoopPingPong ); - - if ( deltaTime === 0 ) { - - if ( loopCount === - 1 ) return time; - - return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; - - } - - if ( loop === LoopOnce ) { - - if ( loopCount === - 1 ) { - - // just started - - this._loopCount = 0; - this._setEndings( true, true, false ); - - } - - handle_stop: { - - if ( time >= duration ) { - - time = duration; - - } else if ( time < 0 ) { - - time = 0; - - } else { - - this.time = time; - - break handle_stop; - - } - - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; - - this.time = time; - - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime < 0 ? - 1 : 1 - } ); - - } - - } else { // repetitive Repeat or PingPong - - if ( loopCount === - 1 ) { - - // just started - - if ( deltaTime >= 0 ) { - - loopCount = 0; - - this._setEndings( true, this.repetitions === 0, pingPong ); - - } else { - - // when looping in reverse direction, the initial - // transition through zero counts as a repetition, - // so leave loopCount at -1 - - this._setEndings( this.repetitions === 0, true, pingPong ); - - } - - } - - if ( time >= duration || time < 0 ) { - - // wrap around - - const loopDelta = Math.floor( time / duration ); // signed - time -= duration * loopDelta; - - loopCount += Math.abs( loopDelta ); - - const pending = this.repetitions - loopCount; - - if ( pending <= 0 ) { - - // have to stop (switch state, clamp time, fire event) - - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; - - time = deltaTime > 0 ? duration : 0; - - this.time = time; - - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime > 0 ? 1 : - 1 - } ); - - } else { - - // keep running - - if ( pending === 1 ) { - - // entering the last round - - const atStart = deltaTime < 0; - this._setEndings( atStart, ! atStart, pingPong ); - - } else { - - this._setEndings( false, false, pingPong ); - - } - - this._loopCount = loopCount; - - this.time = time; - - this._mixer.dispatchEvent( { - type: 'loop', action: this, loopDelta: loopDelta - } ); - - } - - } else { - - this.time = time; - - } - - if ( pingPong && ( loopCount & 1 ) === 1 ) { - - // invert time for the "pong round" - - return duration - time; - - } - - } - - return time; - - } - - _setEndings( atStart, atEnd, pingPong ) { - - const settings = this._interpolantSettings; - - if ( pingPong ) { - - settings.endingStart = ZeroSlopeEnding; - settings.endingEnd = ZeroSlopeEnding; - - } else { - - // assuming for LoopOnce atStart == atEnd == true - - if ( atStart ) { - - settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; - - } else { - - settings.endingStart = WrapAroundEnding; - - } - - if ( atEnd ) { - - settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; - - } else { - - settings.endingEnd = WrapAroundEnding; - - } - - } - - } - - _scheduleFading( duration, weightNow, weightThen ) { - - const mixer = this._mixer, now = mixer.time; - let interpolant = this._weightInterpolant; - - if ( interpolant === null ) { - - interpolant = mixer._lendControlInterpolant(); - this._weightInterpolant = interpolant; - - } - - const times = interpolant.parameterPositions, - values = interpolant.sampleValues; - - times[ 0 ] = now; - values[ 0 ] = weightNow; - times[ 1 ] = now + duration; - values[ 1 ] = weightThen; - - return this; - - } - - } - - function AnimationMixer( root ) { - - this._root = root; - this._initMemoryManager(); - this._accuIndex = 0; - - this.time = 0; - - this.timeScale = 1.0; - - } - - AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: AnimationMixer, - - _bindAction: function ( action, prototypeAction ) { - - const root = action._localRoot || this._root, - tracks = action._clip.tracks, - nTracks = tracks.length, - bindings = action._propertyBindings, - interpolants = action._interpolants, - rootUuid = root.uuid, - bindingsByRoot = this._bindingsByRootAndName; - - let bindingsByName = bindingsByRoot[ rootUuid ]; - - if ( bindingsByName === undefined ) { - - bindingsByName = {}; - bindingsByRoot[ rootUuid ] = bindingsByName; - - } - - for ( let i = 0; i !== nTracks; ++ i ) { - - const track = tracks[ i ], - trackName = track.name; - - let binding = bindingsByName[ trackName ]; - - if ( binding !== undefined ) { - - bindings[ i ] = binding; - - } else { - - binding = bindings[ i ]; - - if ( binding !== undefined ) { - - // existing binding, make sure the cache knows - - if ( binding._cacheIndex === null ) { - - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); - - } - - continue; - - } - - const path = prototypeAction && prototypeAction. - _propertyBindings[ i ].binding.parsedPath; - - binding = new PropertyMixer( - PropertyBinding.create( root, trackName, path ), - track.ValueTypeName, track.getValueSize() ); - - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); - - bindings[ i ] = binding; - - } - - interpolants[ i ].resultBuffer = binding.buffer; - - } - - }, - - _activateAction: function ( action ) { - - if ( ! this._isActiveAction( action ) ) { - - if ( action._cacheIndex === null ) { - - // this action has been forgotten by the cache, but the user - // appears to be still using it -> rebind - - const rootUuid = ( action._localRoot || this._root ).uuid, - clipUuid = action._clip.uuid, - actionsForClip = this._actionsByClip[ clipUuid ]; - - this._bindAction( action, - actionsForClip && actionsForClip.knownActions[ 0 ] ); - - this._addInactiveAction( action, clipUuid, rootUuid ); - - } - - const bindings = action._propertyBindings; - - // increment reference counts / sort out state - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - - const binding = bindings[ i ]; - - if ( binding.useCount ++ === 0 ) { - - this._lendBinding( binding ); - binding.saveOriginalState(); - - } - - } - - this._lendAction( action ); - - } - - }, - - _deactivateAction: function ( action ) { - - if ( this._isActiveAction( action ) ) { - - const bindings = action._propertyBindings; - - // decrement reference counts / sort out state - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - - const binding = bindings[ i ]; - - if ( -- binding.useCount === 0 ) { - - binding.restoreOriginalState(); - this._takeBackBinding( binding ); - - } - - } - - this._takeBackAction( action ); - - } - - }, - - // Memory manager - - _initMemoryManager: function () { - - this._actions = []; // 'nActiveActions' followed by inactive ones - this._nActiveActions = 0; - - this._actionsByClip = {}; - // inside: - // { - // knownActions: Array< AnimationAction > - used as prototypes - // actionByRoot: AnimationAction - lookup - // } - - - this._bindings = []; // 'nActiveBindings' followed by inactive ones - this._nActiveBindings = 0; - - this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > - - - this._controlInterpolants = []; // same game as above - this._nActiveControlInterpolants = 0; - - const scope = this; - - this.stats = { - - actions: { - get total() { - - return scope._actions.length; - - }, - get inUse() { - - return scope._nActiveActions; - - } - }, - bindings: { - get total() { - - return scope._bindings.length; - - }, - get inUse() { - - return scope._nActiveBindings; - - } - }, - controlInterpolants: { - get total() { - - return scope._controlInterpolants.length; - - }, - get inUse() { - - return scope._nActiveControlInterpolants; - - } - } - - }; - - }, - - // Memory management for AnimationAction objects - - _isActiveAction: function ( action ) { - - const index = action._cacheIndex; - return index !== null && index < this._nActiveActions; - - }, - - _addInactiveAction: function ( action, clipUuid, rootUuid ) { - - const actions = this._actions, - actionsByClip = this._actionsByClip; - - let actionsForClip = actionsByClip[ clipUuid ]; - - if ( actionsForClip === undefined ) { - - actionsForClip = { - - knownActions: [ action ], - actionByRoot: {} - - }; - - action._byClipCacheIndex = 0; - - actionsByClip[ clipUuid ] = actionsForClip; - - } else { - - const knownActions = actionsForClip.knownActions; - - action._byClipCacheIndex = knownActions.length; - knownActions.push( action ); - - } - - action._cacheIndex = actions.length; - actions.push( action ); - - actionsForClip.actionByRoot[ rootUuid ] = action; - - }, - - _removeInactiveAction: function ( action ) { - - const actions = this._actions, - lastInactiveAction = actions[ actions.length - 1 ], - cacheIndex = action._cacheIndex; - - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); - - action._cacheIndex = null; - - - const clipUuid = action._clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ], - knownActionsForClip = actionsForClip.knownActions, - - lastKnownAction = - knownActionsForClip[ knownActionsForClip.length - 1 ], - - byClipCacheIndex = action._byClipCacheIndex; - - lastKnownAction._byClipCacheIndex = byClipCacheIndex; - knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; - knownActionsForClip.pop(); - - action._byClipCacheIndex = null; - - - const actionByRoot = actionsForClip.actionByRoot, - rootUuid = ( action._localRoot || this._root ).uuid; - - delete actionByRoot[ rootUuid ]; - - if ( knownActionsForClip.length === 0 ) { - - delete actionsByClip[ clipUuid ]; - - } - - this._removeInactiveBindingsForAction( action ); - - }, - - _removeInactiveBindingsForAction: function ( action ) { - - const bindings = action._propertyBindings; - - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - - const binding = bindings[ i ]; - - if ( -- binding.referenceCount === 0 ) { - - this._removeInactiveBinding( binding ); - - } - - } - - }, - - _lendAction: function ( action ) { - - // [ active actions | inactive actions ] - // [ active actions >| inactive actions ] - // s a - // <-swap-> - // a s - - const actions = this._actions, - prevIndex = action._cacheIndex, - - lastActiveIndex = this._nActiveActions ++, - - firstInactiveAction = actions[ lastActiveIndex ]; - - action._cacheIndex = lastActiveIndex; - actions[ lastActiveIndex ] = action; - - firstInactiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = firstInactiveAction; - - }, - - _takeBackAction: function ( action ) { - - // [ active actions | inactive actions ] - // [ active actions |< inactive actions ] - // a s - // <-swap-> - // s a - - const actions = this._actions, - prevIndex = action._cacheIndex, - - firstInactiveIndex = -- this._nActiveActions, - - lastActiveAction = actions[ firstInactiveIndex ]; - - action._cacheIndex = firstInactiveIndex; - actions[ firstInactiveIndex ] = action; - - lastActiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = lastActiveAction; - - }, - - // Memory management for PropertyMixer objects - - _addInactiveBinding: function ( binding, rootUuid, trackName ) { - - const bindingsByRoot = this._bindingsByRootAndName, - bindings = this._bindings; - - let bindingByName = bindingsByRoot[ rootUuid ]; - - if ( bindingByName === undefined ) { - - bindingByName = {}; - bindingsByRoot[ rootUuid ] = bindingByName; - - } - - bindingByName[ trackName ] = binding; - - binding._cacheIndex = bindings.length; - bindings.push( binding ); - - }, - - _removeInactiveBinding: function ( binding ) { - - const bindings = this._bindings, - propBinding = binding.binding, - rootUuid = propBinding.rootNode.uuid, - trackName = propBinding.path, - bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ], - - lastInactiveBinding = bindings[ bindings.length - 1 ], - cacheIndex = binding._cacheIndex; - - lastInactiveBinding._cacheIndex = cacheIndex; - bindings[ cacheIndex ] = lastInactiveBinding; - bindings.pop(); - - delete bindingByName[ trackName ]; - - if ( Object.keys( bindingByName ).length === 0 ) { - - delete bindingsByRoot[ rootUuid ]; - - } - - }, - - _lendBinding: function ( binding ) { - - const bindings = this._bindings, - prevIndex = binding._cacheIndex, - - lastActiveIndex = this._nActiveBindings ++, - - firstInactiveBinding = bindings[ lastActiveIndex ]; - - binding._cacheIndex = lastActiveIndex; - bindings[ lastActiveIndex ] = binding; - - firstInactiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = firstInactiveBinding; - - }, - - _takeBackBinding: function ( binding ) { - - const bindings = this._bindings, - prevIndex = binding._cacheIndex, - - firstInactiveIndex = -- this._nActiveBindings, - - lastActiveBinding = bindings[ firstInactiveIndex ]; - - binding._cacheIndex = firstInactiveIndex; - bindings[ firstInactiveIndex ] = binding; - - lastActiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = lastActiveBinding; - - }, - - - // Memory management of Interpolants for weight and time scale - - _lendControlInterpolant: function () { - - const interpolants = this._controlInterpolants, - lastActiveIndex = this._nActiveControlInterpolants ++; - - let interpolant = interpolants[ lastActiveIndex ]; - - if ( interpolant === undefined ) { - - interpolant = new LinearInterpolant( - new Float32Array( 2 ), new Float32Array( 2 ), - 1, this._controlInterpolantsResultBuffer ); - - interpolant.__cacheIndex = lastActiveIndex; - interpolants[ lastActiveIndex ] = interpolant; - - } - - return interpolant; - - }, - - _takeBackControlInterpolant: function ( interpolant ) { - - const interpolants = this._controlInterpolants, - prevIndex = interpolant.__cacheIndex, - - firstInactiveIndex = -- this._nActiveControlInterpolants, - - lastActiveInterpolant = interpolants[ firstInactiveIndex ]; - - interpolant.__cacheIndex = firstInactiveIndex; - interpolants[ firstInactiveIndex ] = interpolant; - - lastActiveInterpolant.__cacheIndex = prevIndex; - interpolants[ prevIndex ] = lastActiveInterpolant; - - }, - - _controlInterpolantsResultBuffer: new Float32Array( 1 ), - - // return an action for a clip optionally using a custom root target - // object (this method allocates a lot of dynamic memory in case a - // previously unknown clip/root combination is specified) - clipAction: function ( clip, optionalRoot, blendMode ) { - - const root = optionalRoot || this._root, - rootUuid = root.uuid; - - let clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip; - - const clipUuid = clipObject !== null ? clipObject.uuid : clip; - - const actionsForClip = this._actionsByClip[ clipUuid ]; - let prototypeAction = null; - - if ( blendMode === undefined ) { - - if ( clipObject !== null ) { - - blendMode = clipObject.blendMode; - - } else { - - blendMode = NormalAnimationBlendMode; - - } - - } - - if ( actionsForClip !== undefined ) { - - const existingAction = actionsForClip.actionByRoot[ rootUuid ]; - - if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { - - return existingAction; - - } - - // we know the clip, so we don't have to parse all - // the bindings again but can just copy - prototypeAction = actionsForClip.knownActions[ 0 ]; - - // also, take the clip from the prototype action - if ( clipObject === null ) - clipObject = prototypeAction._clip; - - } - - // clip must be known when specified via string - if ( clipObject === null ) return null; - - // allocate all resources required to run it - const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode ); - - this._bindAction( newAction, prototypeAction ); - - // and make the action known to the memory manager - this._addInactiveAction( newAction, clipUuid, rootUuid ); - - return newAction; - - }, - - // get an existing action - existingAction: function ( clip, optionalRoot ) { - - const root = optionalRoot || this._root, - rootUuid = root.uuid, - - clipObject = typeof clip === 'string' ? - AnimationClip.findByName( root, clip ) : clip, - - clipUuid = clipObject ? clipObject.uuid : clip, - - actionsForClip = this._actionsByClip[ clipUuid ]; - - if ( actionsForClip !== undefined ) { - - return actionsForClip.actionByRoot[ rootUuid ] || null; - - } - - return null; - - }, - - // deactivates all previously scheduled actions - stopAllAction: function () { - - const actions = this._actions, - nActions = this._nActiveActions; - - for ( let i = nActions - 1; i >= 0; -- i ) { - - actions[ i ].stop(); - - } - - return this; - - }, - - // advance the time and update apply the animation - update: function ( deltaTime ) { - - deltaTime *= this.timeScale; - - const actions = this._actions, - nActions = this._nActiveActions, - - time = this.time += deltaTime, - timeDirection = Math.sign( deltaTime ), - - accuIndex = this._accuIndex ^= 1; - - // run active actions - - for ( let i = 0; i !== nActions; ++ i ) { - - const action = actions[ i ]; - - action._update( time, deltaTime, timeDirection, accuIndex ); - - } - - // update scene graph - - const bindings = this._bindings, - nBindings = this._nActiveBindings; - - for ( let i = 0; i !== nBindings; ++ i ) { - - bindings[ i ].apply( accuIndex ); - - } - - return this; - - }, - - // Allows you to seek to a specific time in an animation. - setTime: function ( timeInSeconds ) { - - this.time = 0; // Zero out time attribute for AnimationMixer object; - for ( let i = 0; i < this._actions.length; i ++ ) { - - this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects. - - } - - return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object. - - }, - - // return this mixer's root target object - getRoot: function () { - - return this._root; - - }, - - // free all resources specific to a particular clip - uncacheClip: function ( clip ) { - - const actions = this._actions, - clipUuid = clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ]; - - if ( actionsForClip !== undefined ) { - - // note: just calling _removeInactiveAction would mess up the - // iteration state and also require updating the state we can - // just throw away - - const actionsToRemove = actionsForClip.knownActions; - - for ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) { - - const action = actionsToRemove[ i ]; - - this._deactivateAction( action ); - - const cacheIndex = action._cacheIndex, - lastInactiveAction = actions[ actions.length - 1 ]; - - action._cacheIndex = null; - action._byClipCacheIndex = null; - - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); - - this._removeInactiveBindingsForAction( action ); - - } - - delete actionsByClip[ clipUuid ]; - - } - - }, - - // free all resources specific to a particular root target object - uncacheRoot: function ( root ) { - - const rootUuid = root.uuid, - actionsByClip = this._actionsByClip; - - for ( const clipUuid in actionsByClip ) { - - const actionByRoot = actionsByClip[ clipUuid ].actionByRoot, - action = actionByRoot[ rootUuid ]; - - if ( action !== undefined ) { - - this._deactivateAction( action ); - this._removeInactiveAction( action ); - - } - - } - - const bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ]; - - if ( bindingByName !== undefined ) { - - for ( const trackName in bindingByName ) { - - const binding = bindingByName[ trackName ]; - binding.restoreOriginalState(); - this._removeInactiveBinding( binding ); - - } - - } - - }, - - // remove a targeted clip from the cache - uncacheAction: function ( clip, optionalRoot ) { - - const action = this.existingAction( clip, optionalRoot ); - - if ( action !== null ) { - - this._deactivateAction( action ); - this._removeInactiveAction( action ); - - } - - } - - } ); - - class Uniform { - - constructor( value ) { - - if ( typeof value === 'string' ) { - - console.warn( 'THREE.Uniform: Type parameter is no longer needed.' ); - value = arguments[ 1 ]; - - } - - this.value = value; - - } - - clone() { - - return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); - - } - - } - - function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) { - - InterleavedBuffer.call( this, array, stride ); - - this.meshPerAttribute = meshPerAttribute || 1; - - } - - InstancedInterleavedBuffer.prototype = Object.assign( Object.create( InterleavedBuffer.prototype ), { - - constructor: InstancedInterleavedBuffer, - - isInstancedInterleavedBuffer: true, - - copy: function ( source ) { - - InterleavedBuffer.prototype.copy.call( this, source ); - - this.meshPerAttribute = source.meshPerAttribute; - - return this; - - }, - - clone: function ( data ) { - - const ib = InterleavedBuffer.prototype.clone.call( this, data ); - - ib.meshPerAttribute = this.meshPerAttribute; - - return ib; - - }, - - toJSON: function ( data ) { - - const json = InterleavedBuffer.prototype.toJSON.call( this, data ); - - json.isInstancedInterleavedBuffer = true; - json.meshPerAttribute = this.meshPerAttribute; - - return json; - - } - - } ); - - /** - * @author raub / https://github.com/raub - */ - - /** - * Element size is one of: - * 5126: 4 - * 5123: 2 - * 5122: 2 - * 5125: 4 - * 5124: 4 - * 5120: 1 - * 5121: 1 - */ - function GLBufferAttribute( buffer, type, itemSize, elementSize, count ) { - - this.buffer = buffer; - this.type = type; - this.itemSize = itemSize; - this.elementSize = elementSize; - this.count = count; - - this.version = 0; - - } - - Object.defineProperty( GLBufferAttribute.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - Object.assign( GLBufferAttribute.prototype, { - - isGLBufferAttribute: true, - - setBuffer: function ( buffer ) { - - this.buffer = buffer; - - return this; - - }, - - setType: function ( type, elementSize ) { - - this.type = type; - this.elementSize = elementSize; - - return this; - - }, - - setItemSize: function ( itemSize ) { - - this.itemSize = itemSize; - - return this; - - }, - - setCount: function ( count ) { - - this.count = count; - - return this; - - }, - - } ); - - function Raycaster( origin, direction, near, far ) { - - this.ray = new Ray( origin, direction ); - // direction is assumed to be normalized (for accurate distance calculations) - - this.near = near || 0; - this.far = far || Infinity; - this.camera = null; - this.layers = new Layers(); - - this.params = { - Mesh: {}, - Line: { threshold: 1 }, - LOD: {}, - Points: { threshold: 1 }, - Sprite: {} - }; - - Object.defineProperties( this.params, { - PointCloud: { - get: function () { - - console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' ); - return this.Points; - - } - } - } ); - - } - - function ascSort( a, b ) { - - return a.distance - b.distance; - - } - - function intersectObject( object, raycaster, intersects, recursive ) { - - if ( object.layers.test( raycaster.layers ) ) { - - object.raycast( raycaster, intersects ); - - } - - if ( recursive === true ) { - - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - intersectObject( children[ i ], raycaster, intersects, true ); - - } - - } - - } - - Object.assign( Raycaster.prototype, { - - set: function ( origin, direction ) { - - // direction is assumed to be normalized (for accurate distance calculations) - - this.ray.set( origin, direction ); - - }, - - setFromCamera: function ( coords, camera ) { - - if ( ( camera && camera.isPerspectiveCamera ) ) { - - this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); - this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); - this.camera = camera; - - } else if ( ( camera && camera.isOrthographicCamera ) ) { - - this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera - this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); - this.camera = camera; - - } else { - - console.error( 'THREE.Raycaster: Unsupported camera type.' ); - - } - - }, - - intersectObject: function ( object, recursive, optionalTarget ) { - - const intersects = optionalTarget || []; - - intersectObject( object, this, intersects, recursive ); - - intersects.sort( ascSort ); - - return intersects; - - }, - - intersectObjects: function ( objects, recursive, optionalTarget ) { - - const intersects = optionalTarget || []; - - if ( Array.isArray( objects ) === false ) { - - console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); - return intersects; - - } - - for ( let i = 0, l = objects.length; i < l; i ++ ) { - - intersectObject( objects[ i ], this, intersects, recursive ); - - } - - intersects.sort( ascSort ); - - return intersects; - - } - - } ); - - const _vector$7 = new Vector2(); - - class Box2 { - - constructor( min, max ) { - - Object.defineProperty( this, 'isBox2', { value: true } ); - - this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity ); - - } - - set( min, max ) { - - this.min.copy( min ); - this.max.copy( max ); - - return this; - - } - - setFromPoints( points ) { - - this.makeEmpty(); - - for ( let i = 0, il = points.length; i < il; i ++ ) { - - this.expandByPoint( points[ i ] ); - - } - - return this; - - } - - setFromCenterAndSize( center, size ) { - - const halfSize = _vector$7.copy( size ).multiplyScalar( 0.5 ); - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( box ) { - - this.min.copy( box.min ); - this.max.copy( box.max ); - - return this; - - } - - makeEmpty() { - - this.min.x = this.min.y = + Infinity; - this.max.x = this.max.y = - Infinity; - - return this; - - } - - isEmpty() { - - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); - - } - - getCenter( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box2: .getCenter() target is now required' ); - target = new Vector2(); - - } - - return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); - - } - - getSize( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box2: .getSize() target is now required' ); - target = new Vector2(); - - } - - return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min ); - - } - - expandByPoint( point ) { - - this.min.min( point ); - this.max.max( point ); - - return this; - - } - - expandByVector( vector ) { - - this.min.sub( vector ); - this.max.add( vector ); - - return this; - - } - - expandByScalar( scalar ) { - - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); - - return this; - - } - - containsPoint( point ) { - - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y ? false : true; - - } - - containsBox( box ) { - - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y; - - } - - getParameter( point, target ) { - - // This can potentially have a divide by zero if the box - // has a size dimension of 0. - - if ( target === undefined ) { - - console.warn( 'THREE.Box2: .getParameter() target is now required' ); - target = new Vector2(); - - } - - return target.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ) - ); - - } - - intersectsBox( box ) { - - // using 4 splitting planes to rule out intersections - - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y ? false : true; - - } - - clampPoint( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box2: .clampPoint() target is now required' ); - target = new Vector2(); - - } - - return target.copy( point ).clamp( this.min, this.max ); - - } - - distanceToPoint( point ) { - - const clampedPoint = _vector$7.copy( point ).clamp( this.min, this.max ); - return clampedPoint.sub( point ).length(); - - } - - intersect( box ) { - - this.min.max( box.min ); - this.max.min( box.max ); - - return this; - - } - - union( box ) { - - this.min.min( box.min ); - this.max.max( box.max ); - - return this; - - } - - translate( offset ) { - - this.min.add( offset ); - this.max.add( offset ); - - return this; - - } - - equals( box ) { - - return box.min.equals( this.min ) && box.max.equals( this.max ); - - } - - } - - const _startP = new Vector3(); - const _startEnd = new Vector3(); - - class Line3 { - - constructor( start, end ) { - - this.start = ( start !== undefined ) ? start : new Vector3(); - this.end = ( end !== undefined ) ? end : new Vector3(); - - } - - set( start, end ) { - - this.start.copy( start ); - this.end.copy( end ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( line ) { - - this.start.copy( line.start ); - this.end.copy( line.end ); - - return this; - - } - - getCenter( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .getCenter() target is now required' ); - target = new Vector3(); - - } - - return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); - - } - - delta( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .delta() target is now required' ); - target = new Vector3(); - - } - - return target.subVectors( this.end, this.start ); - - } - - distanceSq() { - - return this.start.distanceToSquared( this.end ); - - } - - distance() { - - return this.start.distanceTo( this.end ); - - } - - at( t, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .at() target is now required' ); - target = new Vector3(); - - } - - return this.delta( target ).multiplyScalar( t ).add( this.start ); - - } - - closestPointToPointParameter( point, clampToLine ) { - - _startP.subVectors( point, this.start ); - _startEnd.subVectors( this.end, this.start ); - - const startEnd2 = _startEnd.dot( _startEnd ); - const startEnd_startP = _startEnd.dot( _startP ); - - let t = startEnd_startP / startEnd2; - - if ( clampToLine ) { - - t = MathUtils.clamp( t, 0, 1 ); - - } - - return t; - - } - - closestPointToPoint( point, clampToLine, target ) { - - const t = this.closestPointToPointParameter( point, clampToLine ); - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } - - return this.delta( target ).multiplyScalar( t ).add( this.start ); - - } - - applyMatrix4( matrix ) { - - this.start.applyMatrix4( matrix ); - this.end.applyMatrix4( matrix ); - - return this; - - } - - equals( line ) { - - return line.start.equals( this.start ) && line.end.equals( this.end ); - - } - - } - - function ImmediateRenderObject( material ) { - - Object3D.call( this ); - - this.material = material; - this.render = function ( /* renderCallback */ ) {}; - - this.hasPositions = false; - this.hasNormals = false; - this.hasColors = false; - this.hasUvs = false; - - this.positionArray = null; - this.normalArray = null; - this.colorArray = null; - this.uvArray = null; - - this.count = 0; - - } - - ImmediateRenderObject.prototype = Object.create( Object3D.prototype ); - ImmediateRenderObject.prototype.constructor = ImmediateRenderObject; - - ImmediateRenderObject.prototype.isImmediateRenderObject = true; - - const _vector$8 = new Vector3(); - - const _vector$9 = new Vector3(); - const _boneMatrix = new Matrix4(); - const _matrixWorldInv = new Matrix4(); - - const _vector$a = new Vector3(); - const _color1 = new Color(); - const _color2 = new Color(); - - const _v1$5 = new Vector3(); - const _v2$3 = new Vector3(); - const _v3$1 = new Vector3(); - - const _vector$b = new Vector3(); - const _camera = new Camera(); - - const _box$3 = new Box3(); - - class Box3Helper extends LineSegments { - - constructor( box, color = 0xffff00 ) { - - const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); - - const positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ]; - - const geometry = new BufferGeometry(); - - geometry.setIndex( new BufferAttribute( indices, 1 ) ); - - geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - - super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); - - this.box = box; - - this.type = 'Box3Helper'; - - this.geometry.computeBoundingSphere(); - - } - - updateMatrixWorld( force ) { - - const box = this.box; - - if ( box.isEmpty() ) return; - - box.getCenter( this.position ); - - box.getSize( this.scale ); - - this.scale.multiplyScalar( 0.5 ); - - super.updateMatrixWorld( force ); - - } - - } - - const _axis = new Vector3(); - - const LOD_MIN = 4; - const LOD_MAX = 8; - - // The standard deviations (radians) associated with the extra mips. These are - // chosen to approximate a Trowbridge-Reitz distribution function times the - // geometric shadowing function. These sigma values squared must match the - // variance #defines in cube_uv_reflection_fragment.glsl.js. - const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; - - const TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; - - const _flatCamera = new OrthographicCamera(); - const { _lodPlanes, _sizeLods, _sigmas } = _createPlanes(); - - // Golden Ratio - const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; - const INV_PHI = 1 / PHI; - - // Vertices of a dodecahedron (except the opposites, which represent the - // same axis), used as axis directions evenly spread on a sphere. - const _axisDirections = [ - new Vector3( 1, 1, 1 ), - new Vector3( - 1, 1, 1 ), - new Vector3( 1, 1, - 1 ), - new Vector3( - 1, 1, - 1 ), - new Vector3( 0, PHI, INV_PHI ), - new Vector3( 0, PHI, - INV_PHI ), - new Vector3( INV_PHI, 0, PHI ), - new Vector3( - INV_PHI, 0, PHI ), - new Vector3( PHI, INV_PHI, 0 ), - new Vector3( - PHI, INV_PHI, 0 ) ]; - - function _createPlanes() { - - const _lodPlanes = []; - const _sizeLods = []; - const _sigmas = []; - - let lod = LOD_MAX; - - for ( let i = 0; i < TOTAL_LODS; i ++ ) { - - const sizeLod = Math.pow( 2, lod ); - _sizeLods.push( sizeLod ); - let sigma = 1.0 / sizeLod; - - if ( i > LOD_MAX - LOD_MIN ) { - - sigma = EXTRA_LOD_SIGMA[ i - LOD_MAX + LOD_MIN - 1 ]; - - } else if ( i == 0 ) { - - sigma = 0; - - } - - _sigmas.push( sigma ); - - const texelSize = 1.0 / ( sizeLod - 1 ); - const min = - texelSize / 2; - const max = 1 + texelSize / 2; - const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ]; - - const cubeFaces = 6; - const vertices = 6; - const positionSize = 3; - const uvSize = 2; - const faceIndexSize = 1; - - const position = new Float32Array( positionSize * vertices * cubeFaces ); - const uv = new Float32Array( uvSize * vertices * cubeFaces ); - const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); - - for ( let face = 0; face < cubeFaces; face ++ ) { - - const x = ( face % 3 ) * 2 / 3 - 1; - const y = face > 2 ? 0 : - 1; - const coordinates = [ - x, y, 0, - x + 2 / 3, y, 0, - x + 2 / 3, y + 1, 0, - x, y, 0, - x + 2 / 3, y + 1, 0, - x, y + 1, 0 - ]; - position.set( coordinates, positionSize * vertices * face ); - uv.set( uv1, uvSize * vertices * face ); - const fill = [ face, face, face, face, face, face ]; - faceIndex.set( fill, faceIndexSize * vertices * face ); - - } - - const planes = new BufferGeometry(); - planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) ); - planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) ); - planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) ); - _lodPlanes.push( planes ); - - if ( lod > LOD_MIN ) { - - lod --; - - } - - } - - return { _lodPlanes, _sizeLods, _sigmas }; - - } - const NoColors = 0; - const VertexColors = 2; - - // - - Curve.create = function ( construct, getPoint ) { - - console.log( 'THREE.Curve.create() has been deprecated' ); - - construct.prototype = Object.create( Curve.prototype ); - construct.prototype.constructor = construct; - construct.prototype.getPoint = getPoint; - - return construct; - - }; - - // - - Object.assign( CurvePath.prototype, { - - createPointsGeometry: function ( divisions ) { - - console.warn( 'THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' ); - - // generate geometry from path points (for Line or Points objects) - - const pts = this.getPoints( divisions ); - return this.createGeometry( pts ); - - }, - - createSpacedPointsGeometry: function ( divisions ) { - - console.warn( 'THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' ); - - // generate geometry from equidistant sampling along the path - - const pts = this.getSpacedPoints( divisions ); - return this.createGeometry( pts ); - - }, - - createGeometry: function ( points ) { - - console.warn( 'THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' ); - - const geometry = new Geometry(); - - for ( let i = 0, l = points.length; i < l; i ++ ) { - - const point = points[ i ]; - geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); - - } - - return geometry; - - } - - } ); - - // - - Object.assign( Path.prototype, { - - fromPoints: function ( points ) { - - console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' ); - return this.setFromPoints( points ); - - } - - } ); - - // - - function Spline( points ) { - - console.warn( 'THREE.Spline has been removed. Use THREE.CatmullRomCurve3 instead.' ); - - CatmullRomCurve3.call( this, points ); - this.type = 'catmullrom'; - - } - - Spline.prototype = Object.create( CatmullRomCurve3.prototype ); - - Object.assign( Spline.prototype, { - - initFromArray: function ( /* a */ ) { - - console.error( 'THREE.Spline: .initFromArray() has been removed.' ); - - }, - getControlPointsArray: function ( /* optionalTarget */ ) { - - console.error( 'THREE.Spline: .getControlPointsArray() has been removed.' ); - - }, - reparametrizeByArcLength: function ( /* samplingCoef */ ) { - - console.error( 'THREE.Spline: .reparametrizeByArcLength() has been removed.' ); - - } - - } ); - - // - - Object.assign( Loader.prototype, { - - extractUrlBase: function ( url ) { - - console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' ); - return LoaderUtils.extractUrlBase( url ); - - } - - } ); - - Loader.Handlers = { - - add: function ( /* regex, loader */ ) { - - console.error( 'THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.' ); - - }, - - get: function ( /* file */ ) { - - console.error( 'THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.' ); - - } - - }; - - Object.assign( ObjectLoader.prototype, { - - setTexturePath: function ( value ) { - - console.warn( 'THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath().' ); - return this.setResourcePath( value ); - - } - - } ); - - // - - Object.assign( Box2.prototype, { - - center: function ( optionalTarget ) { - - console.warn( 'THREE.Box2: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }, - empty: function () { - - console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }, - isIntersectionBox: function ( box ) { - - console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }, - size: function ( optionalTarget ) { - - console.warn( 'THREE.Box2: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); - - } - } ); - - Object.assign( Box3.prototype, { - - center: function ( optionalTarget ) { - - console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }, - empty: function () { - - console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }, - isIntersectionBox: function ( box ) { - - console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }, - isIntersectionSphere: function ( sphere ) { - - console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); - - }, - size: function ( optionalTarget ) { - - console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); - - } - } ); - - Object.assign( Sphere.prototype, { - - empty: function () { - - console.warn( 'THREE.Sphere: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }, - - } ); - - Frustum.prototype.setFromMatrix = function ( m ) { - - console.warn( 'THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().' ); - return this.setFromProjectionMatrix( m ); - - }; - - Line3.prototype.center = function ( optionalTarget ) { - - console.warn( 'THREE.Line3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }; - - Object.assign( MathUtils, { - - random16: function () { - - console.warn( 'THREE.Math: .random16() has been deprecated. Use Math.random() instead.' ); - return Math.random(); - - }, - - nearestPowerOfTwo: function ( value ) { - - console.warn( 'THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo().' ); - return MathUtils.floorPowerOfTwo( value ); - - }, - - nextPowerOfTwo: function ( value ) { - - console.warn( 'THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo().' ); - return MathUtils.ceilPowerOfTwo( value ); - - } - - } ); - - Object.assign( Matrix3.prototype, { - - flattenToArrayOffset: function ( array, offset ) { - - console.warn( "THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); - return this.toArray( array, offset ); - - }, - multiplyVector3: function ( vector ) { - - console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); - return vector.applyMatrix3( this ); - - }, - multiplyVector3Array: function ( /* a */ ) { - - console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' ); - - }, - applyToBufferAttribute: function ( attribute ) { - - console.warn( 'THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.' ); - return attribute.applyMatrix3( this ); - - }, - applyToVector3Array: function ( /* array, offset, length */ ) { - - console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' ); - - } - - } ); - - Object.assign( Matrix4.prototype, { - - extractPosition: function ( m ) { - - console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); - return this.copyPosition( m ); - - }, - flattenToArrayOffset: function ( array, offset ) { - - console.warn( "THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); - return this.toArray( array, offset ); - - }, - getPosition: function () { - - console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); - return new Vector3().setFromMatrixColumn( this, 3 ); - - }, - setRotationFromQuaternion: function ( q ) { - - console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); - return this.makeRotationFromQuaternion( q ); - - }, - multiplyToArray: function () { - - console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' ); - - }, - multiplyVector3: function ( vector ) { - - console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }, - multiplyVector4: function ( vector ) { - - console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }, - multiplyVector3Array: function ( /* a */ ) { - - console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' ); - - }, - rotateAxis: function ( v ) { - - console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); - v.transformDirection( this ); - - }, - crossVector: function ( vector ) { - - console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }, - translate: function () { - - console.error( 'THREE.Matrix4: .translate() has been removed.' ); - - }, - rotateX: function () { - - console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); - - }, - rotateY: function () { - - console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); - - }, - rotateZ: function () { - - console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); - - }, - rotateByAxis: function () { - - console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); - - }, - applyToBufferAttribute: function ( attribute ) { - - console.warn( 'THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.' ); - return attribute.applyMatrix4( this ); - - }, - applyToVector3Array: function ( /* array, offset, length */ ) { - - console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' ); - - }, - makeFrustum: function ( left, right, bottom, top, near, far ) { - - console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' ); - return this.makePerspective( left, right, top, bottom, near, far ); - - } - - } ); - - Plane.prototype.isIntersectionLine = function ( line ) { - - console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); - return this.intersectsLine( line ); - - }; - - Quaternion.prototype.multiplyVector3 = function ( vector ) { - - console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); - return vector.applyQuaternion( this ); - - }; - - Object.assign( Ray.prototype, { - - isIntersectionBox: function ( box ) { - - console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }, - isIntersectionPlane: function ( plane ) { - - console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); - return this.intersectsPlane( plane ); - - }, - isIntersectionSphere: function ( sphere ) { - - console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); - - } - - } ); - - Object.assign( Triangle.prototype, { - - area: function () { - - console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' ); - return this.getArea(); - - }, - barycoordFromPoint: function ( point, target ) { - - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return this.getBarycoord( point, target ); - - }, - midpoint: function ( target ) { - - console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' ); - return this.getMidpoint( target ); - - }, - normal: function ( target ) { - - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return this.getNormal( target ); - - }, - plane: function ( target ) { - - console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' ); - return this.getPlane( target ); - - } - - } ); - - Object.assign( Triangle, { - - barycoordFromPoint: function ( point, a, b, c, target ) { - - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return Triangle.getBarycoord( point, a, b, c, target ); - - }, - normal: function ( a, b, c, target ) { - - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return Triangle.getNormal( a, b, c, target ); - - } - - } ); - - Object.assign( Shape.prototype, { - - extractAllPoints: function ( divisions ) { - - console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' ); - return this.extractPoints( divisions ); - - }, - extrude: function ( options ) { - - console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); - return new ExtrudeGeometry( this, options ); - - }, - makeGeometry: function ( options ) { - - console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); - return new ShapeGeometry( this, options ); - - } - - } ); - - Object.assign( Vector2.prototype, { - - fromAttribute: function ( attribute, index, offset ) { - - console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); - - }, - distanceToManhattan: function ( v ) { - - console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); - - }, - lengthManhattan: function () { - - console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - - } - - } ); - - Object.assign( Vector3.prototype, { - - setEulerFromRotationMatrix: function () { - - console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); - - }, - setEulerFromQuaternion: function () { - - console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); - - }, - getPositionFromMatrix: function ( m ) { - - console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); - return this.setFromMatrixPosition( m ); - - }, - getScaleFromMatrix: function ( m ) { - - console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); - return this.setFromMatrixScale( m ); - - }, - getColumnFromMatrix: function ( index, matrix ) { - - console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); - return this.setFromMatrixColumn( matrix, index ); - - }, - applyProjection: function ( m ) { - - console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' ); - return this.applyMatrix4( m ); - - }, - fromAttribute: function ( attribute, index, offset ) { - - console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); - - }, - distanceToManhattan: function ( v ) { - - console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); - - }, - lengthManhattan: function () { - - console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - - } - - } ); - - Object.assign( Vector4.prototype, { - - fromAttribute: function ( attribute, index, offset ) { - - console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); - - }, - lengthManhattan: function () { - - console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - - } - - } ); - - // - - Object.assign( Geometry.prototype, { - - computeTangents: function () { - - console.error( 'THREE.Geometry: .computeTangents() has been removed.' ); - - }, - computeLineDistances: function () { - - console.error( 'THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead.' ); - - }, - applyMatrix: function ( matrix ) { - - console.warn( 'THREE.Geometry: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); - - } - - } ); - - Object.assign( Object3D.prototype, { - - getChildByName: function ( name ) { - - console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); - return this.getObjectByName( name ); - - }, - renderDepth: function () { - - console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); - - }, - translate: function ( distance, axis ) { - - console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); - return this.translateOnAxis( axis, distance ); - - }, - getWorldRotation: function () { - - console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' ); - - }, - applyMatrix: function ( matrix ) { - - console.warn( 'THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); - - } - - } ); - - Object.defineProperties( Object3D.prototype, { - - eulerOrder: { - get: function () { - - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - return this.rotation.order; - - }, - set: function ( value ) { - - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - this.rotation.order = value; - - } - }, - useQuaternion: { - get: function () { - - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - - }, - set: function () { - - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - - } - } - - } ); - - Object.assign( Mesh.prototype, { - - setDrawMode: function () { - - console.error( 'THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - - }, - - } ); - - Object.defineProperties( Mesh.prototype, { - - drawMode: { - get: function () { - - console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode.' ); - return TrianglesDrawMode; - - }, - set: function () { - - console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - - } - } - - } ); - - Object.defineProperties( LOD.prototype, { - - objects: { - get: function () { - - console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); - return this.levels; - - } - } - - } ); - - Object.defineProperty( Skeleton.prototype, 'useVertexTexture', { - - get: function () { - - console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' ); - - } - - } ); - - SkinnedMesh.prototype.initBones = function () { - - console.error( 'THREE.SkinnedMesh: initBones() has been removed.' ); - - }; - - Object.defineProperty( Curve.prototype, '__arcLengthDivisions', { - - get: function () { - - console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' ); - return this.arcLengthDivisions; - - }, - set: function ( value ) { - - console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' ); - this.arcLengthDivisions = value; - - } - - } ); - - // - - PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { - - console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " + - "Use .setFocalLength and .filmGauge for a photographic setup." ); - - if ( filmGauge !== undefined ) this.filmGauge = filmGauge; - this.setFocalLength( focalLength ); - - }; - - // - - Object.defineProperties( Light.prototype, { - onlyShadow: { - set: function () { - - console.warn( 'THREE.Light: .onlyShadow has been removed.' ); - - } - }, - shadowCameraFov: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); - this.shadow.camera.fov = value; - - } - }, - shadowCameraLeft: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); - this.shadow.camera.left = value; - - } - }, - shadowCameraRight: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); - this.shadow.camera.right = value; - - } - }, - shadowCameraTop: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); - this.shadow.camera.top = value; - - } - }, - shadowCameraBottom: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); - this.shadow.camera.bottom = value; - - } - }, - shadowCameraNear: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); - this.shadow.camera.near = value; - - } - }, - shadowCameraFar: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); - this.shadow.camera.far = value; - - } - }, - shadowCameraVisible: { - set: function () { - - console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); - - } - }, - shadowBias: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); - this.shadow.bias = value; - - } - }, - shadowDarkness: { - set: function () { - - console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); - - } - }, - shadowMapWidth: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); - this.shadow.mapSize.width = value; - - } - }, - shadowMapHeight: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); - this.shadow.mapSize.height = value; - - } - } - } ); - - // - - Object.defineProperties( BufferAttribute.prototype, { - - length: { - get: function () { - - console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' ); - return this.array.length; - - } - }, - dynamic: { - get: function () { - - console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); - return this.usage === DynamicDrawUsage; - - }, - set: function ( /* value */ ) { - - console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); - this.setUsage( DynamicDrawUsage ); - - } - } - - } ); - - Object.assign( BufferAttribute.prototype, { - setDynamic: function ( value ) { - - console.warn( 'THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; - - }, - copyIndicesArray: function ( /* indices */ ) { - - console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' ); - - }, - setArray: function ( /* array */ ) { - - console.error( 'THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - - } - } ); - - Object.assign( BufferGeometry.prototype, { - - addIndex: function ( index ) { - - console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); - this.setIndex( index ); - - }, - addAttribute: function ( name, attribute ) { - - console.warn( 'THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().' ); - - if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { - - console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); - - return this.setAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); - - } - - if ( name === 'index' ) { - - console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); - this.setIndex( attribute ); - - return this; - - } - - return this.setAttribute( name, attribute ); - - }, - addDrawCall: function ( start, count, indexOffset ) { - - if ( indexOffset !== undefined ) { - - console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); - - } - - console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); - this.addGroup( start, count ); - - }, - clearDrawCalls: function () { - - console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); - this.clearGroups(); - - }, - computeTangents: function () { - - console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' ); - - }, - computeOffsets: function () { - - console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); - - }, - removeAttribute: function ( name ) { - - console.warn( 'THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().' ); - - return this.deleteAttribute( name ); - - }, - applyMatrix: function ( matrix ) { - - console.warn( 'THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); - - } - - } ); - - Object.defineProperties( BufferGeometry.prototype, { - - drawcalls: { - get: function () { - - console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); - return this.groups; - - } - }, - offsets: { - get: function () { - - console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); - return this.groups; - - } - } - - } ); - - Object.defineProperties( InstancedBufferGeometry.prototype, { - - maxInstancedCount: { - get: function () { - - console.warn( 'THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount.' ); - return this.instanceCount; - - }, - set: function ( value ) { - - console.warn( 'THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount.' ); - this.instanceCount = value; - - } - } - - } ); - - Object.defineProperties( Raycaster.prototype, { - - linePrecision: { - get: function () { - - console.warn( 'THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead.' ); - return this.params.Line.threshold; - - }, - set: function ( value ) { - - console.warn( 'THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead.' ); - this.params.Line.threshold = value; - - } - } - - } ); - - Object.defineProperties( InterleavedBuffer.prototype, { - - dynamic: { - get: function () { - - console.warn( 'THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead.' ); - return this.usage === DynamicDrawUsage; - - }, - set: function ( value ) { - - console.warn( 'THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead.' ); - this.setUsage( value ); - - } - } - - } ); - - Object.assign( InterleavedBuffer.prototype, { - setDynamic: function ( value ) { - - console.warn( 'THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; - - }, - setArray: function ( /* array */ ) { - - console.error( 'THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - - } - } ); - - // - - Object.assign( ExtrudeBufferGeometry.prototype, { - - getArrays: function () { - - console.error( 'THREE.ExtrudeBufferGeometry: .getArrays() has been removed.' ); - - }, - - addShapeList: function () { - - console.error( 'THREE.ExtrudeBufferGeometry: .addShapeList() has been removed.' ); - - }, - - addShape: function () { - - console.error( 'THREE.ExtrudeBufferGeometry: .addShape() has been removed.' ); - - } - - } ); - - // - - Object.assign( Scene.prototype, { - - dispose: function () { - - console.error( 'THREE.Scene: .dispose() has been removed.' ); - - } - - } ); - - // - - Object.defineProperties( Uniform.prototype, { - - dynamic: { - set: function () { - - console.warn( 'THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.' ); - - } - }, - onUpdate: { - value: function () { - - console.warn( 'THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead.' ); - return this; - - } - } - - } ); - - // - - Object.defineProperties( Material.prototype, { - - wrapAround: { - get: function () { - - console.warn( 'THREE.Material: .wrapAround has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.Material: .wrapAround has been removed.' ); - - } - }, - - overdraw: { - get: function () { - - console.warn( 'THREE.Material: .overdraw has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.Material: .overdraw has been removed.' ); - - } - }, - - wrapRGB: { - get: function () { - - console.warn( 'THREE.Material: .wrapRGB has been removed.' ); - return new Color(); - - } - }, - - shading: { - get: function () { - - console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - - }, - set: function ( value ) { - - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( value === FlatShading ); - - } - }, - - stencilMask: { - get: function () { - - console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); - return this.stencilFuncMask; - - }, - set: function ( value ) { - - console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); - this.stencilFuncMask = value; - - } - } - - } ); - - Object.defineProperties( MeshPhongMaterial.prototype, { - - metal: { - get: function () { - - console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' ); - return false; - - }, - set: function () { - - console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' ); - - } - } - - } ); - - Object.defineProperties( MeshPhysicalMaterial.prototype, { - - transparency: { - get: function () { - - console.warn( 'THREE.MeshPhysicalMaterial: .transparency has been renamed to .transmission.' ); - return this.transmission; - - }, - set: function ( value ) { - - console.warn( 'THREE.MeshPhysicalMaterial: .transparency has been renamed to .transmission.' ); - this.transmission = value; - - } - } - - } ); - - Object.defineProperties( ShaderMaterial.prototype, { - - derivatives: { - get: function () { - - console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - return this.extensions.derivatives; - - }, - set: function ( value ) { - - console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - this.extensions.derivatives = value; - - } - } - - } ); - - // - - Object.assign( WebGLRenderer.prototype, { - - clearTarget: function ( renderTarget, color, depth, stencil ) { - - console.warn( 'THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.' ); - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); - - }, - animate: function ( callback ) { - - console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' ); - this.setAnimationLoop( callback ); - - }, - getCurrentRenderTarget: function () { - - console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' ); - return this.getRenderTarget(); - - }, - getMaxAnisotropy: function () { - - console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' ); - return this.capabilities.getMaxAnisotropy(); - - }, - getPrecision: function () { - - console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' ); - return this.capabilities.precision; - - }, - resetGLState: function () { - - console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' ); - return this.state.reset(); - - }, - supportsFloatTextures: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); - return this.extensions.get( 'OES_texture_float' ); - - }, - supportsHalfFloatTextures: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); - return this.extensions.get( 'OES_texture_half_float' ); - - }, - supportsStandardDerivatives: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); - return this.extensions.get( 'OES_standard_derivatives' ); - - }, - supportsCompressedTextureS3TC: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); - - }, - supportsCompressedTexturePVRTC: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - - }, - supportsBlendMinMax: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); - return this.extensions.get( 'EXT_blend_minmax' ); - - }, - supportsVertexTextures: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); - return this.capabilities.vertexTextures; - - }, - supportsInstancedArrays: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); - return this.extensions.get( 'ANGLE_instanced_arrays' ); - - }, - enableScissorTest: function ( boolean ) { - - console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); - this.setScissorTest( boolean ); - - }, - initMaterial: function () { - - console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); - - }, - addPrePlugin: function () { - - console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); - - }, - addPostPlugin: function () { - - console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); - - }, - updateShadowMap: function () { - - console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); - - }, - setFaceCulling: function () { - - console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' ); - - }, - allocTextureUnit: function () { - - console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' ); - - }, - setTexture: function () { - - console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' ); - - }, - setTexture2D: function () { - - console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' ); - - }, - setTextureCube: function () { - - console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' ); - - }, - getActiveMipMapLevel: function () { - - console.warn( 'THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().' ); - return this.getActiveMipmapLevel(); - - } - - } ); - - Object.defineProperties( WebGLRenderer.prototype, { - - shadowMapEnabled: { - get: function () { - - return this.shadowMap.enabled; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); - this.shadowMap.enabled = value; - - } - }, - shadowMapType: { - get: function () { - - return this.shadowMap.type; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); - this.shadowMap.type = value; - - } - }, - shadowMapCullFace: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function ( /* value */ ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - - } - }, - context: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .context has been removed. Use .getContext() instead.' ); - return this.getContext(); - - } - }, - vr: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .vr has been renamed to .xr' ); - return this.xr; - - } - }, - gammaInput: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); - return false; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); - - } - }, - gammaOutput: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); - return false; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); - this.outputEncoding = ( value === true ) ? sRGBEncoding : LinearEncoding; - - } - }, - toneMappingWhitePoint: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); - return 1.0; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); - - } - }, - - } ); - - Object.defineProperties( WebGLShadowMap.prototype, { - - cullFace: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function ( /* cullFace */ ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - - } - }, - renderReverseSided: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); - - } - }, - renderSingleSided: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - - } - } - - } ); - - // - - Object.defineProperties( WebGLRenderTarget.prototype, { - - wrapS: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - return this.texture.wrapS; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - this.texture.wrapS = value; - - } - }, - wrapT: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - return this.texture.wrapT; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - this.texture.wrapT = value; - - } - }, - magFilter: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - return this.texture.magFilter; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - this.texture.magFilter = value; - - } - }, - minFilter: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - return this.texture.minFilter; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - this.texture.minFilter = value; - - } - }, - anisotropy: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - return this.texture.anisotropy; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - this.texture.anisotropy = value; - - } - }, - offset: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - return this.texture.offset; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - this.texture.offset = value; - - } - }, - repeat: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - return this.texture.repeat; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - this.texture.repeat = value; - - } - }, - format: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - return this.texture.format; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - this.texture.format = value; - - } - }, - type: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - return this.texture.type; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - this.texture.type = value; - - } - }, - generateMipmaps: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - return this.texture.generateMipmaps; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - this.texture.generateMipmaps = value; - - } - } - - } ); - - // - - Object.defineProperties( Audio.prototype, { - - load: { - value: function ( file ) { - - console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); - const scope = this; - const audioLoader = new AudioLoader(); - audioLoader.load( file, function ( buffer ) { - - scope.setBuffer( buffer ); - - } ); - return this; - - } - }, - startTime: { - set: function () { - - console.warn( 'THREE.Audio: .startTime is now .play( delay ).' ); - - } - } - - } ); - - // - - CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) { - - console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' ); - return this.update( renderer, scene ); - - }; - - ImageUtils.crossOrigin = undefined; - - ImageUtils.loadTexture = function ( url, mapping, onLoad, onError ) { - - console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); - - const loader = new TextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); - - const texture = loader.load( url, onLoad, undefined, onError ); - - if ( mapping ) texture.mapping = mapping; - - return texture; - - }; - - ImageUtils.loadTextureCube = function ( urls, mapping, onLoad, onError ) { - - console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); - - const loader = new CubeTextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); - - const texture = loader.load( urls, onLoad, undefined, onError ); - - if ( mapping ) texture.mapping = mapping; - - return texture; - - }; - - ImageUtils.loadCompressedTexture = function () { - - console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); - - }; - - ImageUtils.loadCompressedTextureCube = function () { - - console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); - - }; - - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - - /* eslint-disable no-undef */ - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: { - revision: REVISION, - } } ) ); - /* eslint-enable no-undef */ - - } - - const MERCATOR_A = 6378137.0; - const WORLD_SIZE = MERCATOR_A * Math.PI * 2; - - const ThreeboxConstants = { - WORLD_SIZE: WORLD_SIZE, - PROJECTION_WORLD_SIZE: WORLD_SIZE / (MERCATOR_A * Math.PI * 2), - MERCATOR_A: MERCATOR_A, - DEG2RAD: Math.PI / 180, - RAD2DEG: 180 / Math.PI, - EARTH_CIRCUMFERENCE: 40075000 // In meters - }; - - /* - mapbox-gl uses a camera fixed at the orgin (the middle of the canvas) The camera is only updated when rotated (bearing angle), - pitched or when the map view is resized. - When panning and zooming the map, the desired part of the world is translated and zoomed in front of the camera. The world is only updated when - the map is panned or zoomed. - - The mapbox-gl internal coordinate system has origin (0,0) located at longitude -180 degrees and latitude 0 degrees. - The scaling is 2^map.getZoom() * 512/EARTH_CIRCUMFERENCE_IN_METERS. At zoom=0 (scale=2^0=1), the whole world fits in 512 units. - */ - class CameraSync { - constructor(map, camera, world) { - this.map = map; - this.camera = camera; - this.active = true; - this.updateCallback = null; - this.camera.matrixAutoUpdate = false; // We're in charge of the camera now! - - // Postion and configure the world group so we can scale it appropriately when the camera zooms - this.world = world || new Group(); - this.world.position.x = this.world.position.y = ThreeboxConstants.WORLD_SIZE / 2; - this.world.matrixAutoUpdate = false; - - //set up basic camera state - this.state = { - fov: 0.6435011087932844, // Math.atan(0.75); - translateCenter: new Matrix4(), - worldSizeRatio: 512 / ThreeboxConstants.WORLD_SIZE - }; - - this.state.translateCenter.makeTranslation( - ThreeboxConstants.WORLD_SIZE / 2, - -ThreeboxConstants.WORLD_SIZE / 2, - 0 - ); - - // Listen for move events from the map and update the Three.js camera. Some attributes only change when viewport resizes, so update those accordingly - this.updateCameraBound = () => this.updateCamera(); - this.map.on('move', this.updateCameraBound); - this.setupCameraBound = () => this.setupCamera(); - this.map.on('resize', this.setupCameraBound); - //this.map.on('moveend', ()=>this.updateCallback()) - - this.setupCamera(); - } - detachCamera() { - this.map.off('move', this.updateCameraBound); - this.map.off('resize', this.setupCameraBound); - this.updateCallback = null; - this.map = null; - this.camera = null; - } - setupCamera() { - var t = this.map.transform; - const halfFov = this.state.fov / 2; - var cameraToCenterDistance = (0.5 / Math.tan(halfFov)) * t.height; - - this.state.cameraToCenterDistance = cameraToCenterDistance; - this.state.cameraTranslateZ = new Matrix4().makeTranslation(0, 0, cameraToCenterDistance); - - this.updateCamera(); - } - updateCamera(ev) { - if (!this.camera) { - console.log('nocamera'); - return; - } - - var t = this.map.transform; - - var halfFov = this.state.fov / 2; - const groundAngle = Math.PI / 2 + t._pitch; - this.state.topHalfSurfaceDistance = - (Math.sin(halfFov) * this.state.cameraToCenterDistance) / Math.sin(Math.PI - groundAngle - halfFov); - - // Calculate z distance of the farthest fragment that should be rendered. - const furthestDistance = - Math.cos(Math.PI / 2 - t._pitch) * this.state.topHalfSurfaceDistance + this.state.cameraToCenterDistance; - - // Add a bit extra to avoid precision problems when a fragment's distance is exactly `furthestDistance` - const farZ = furthestDistance * 1.01; - - this.camera.aspect = t.width / t.height; - this.camera.projectionMatrix = this.makePerspectiveMatrix(this.state.fov, t.width / t.height, 1, farZ); - - var cameraWorldMatrix = new Matrix4(); - var rotatePitch = new Matrix4().makeRotationX(t._pitch); - var rotateBearing = new Matrix4().makeRotationZ(t.angle); - - // Unlike the Mapbox GL JS camera, separate camera translation and rotation out into its world matrix - // If this is applied directly to the projection matrix, it will work OK but break raycasting - - cameraWorldMatrix.premultiply(this.state.cameraTranslateZ).premultiply(rotatePitch).premultiply(rotateBearing); - - this.camera.matrixWorld.copy(cameraWorldMatrix); - - // Handle scaling and translation of objects in the map in the world's matrix transform, not the camera - let zoomPow = t.scale * this.state.worldSizeRatio; - let scale = new Matrix4(); - scale.makeScale(zoomPow, zoomPow, zoomPow); - //console.log(`zoomPow: ${zoomPow}`); - - let translateMap = new Matrix4(); - translateMap.makeTranslation(-t.point.x, t.point.y, 0); - - this.world.matrix = new Matrix4(); - this.world.matrix - //.premultiply(rotateMap) - .premultiply(this.state.translateCenter) - .premultiply(scale) - .premultiply(translateMap); - let matrixWorldInverse = new Matrix4(); - matrixWorldInverse.getInverse(this.world.matrix); - - this.camera.projectionMatrixInverse.getInverse(this.camera.projectionMatrix); - this.camera.matrixWorldInverse.getInverse(this.camera.matrixWorld); - this.frustum = new Frustum(); - this.frustum.setFromProjectionMatrix( - new Matrix4().multiplyMatrices(this.camera.projectionMatrix, this.camera.matrixWorldInverse) - ); - - this.cameraPosition = new Vector3(0, 0, 0).unproject(this.camera).applyMatrix4(matrixWorldInverse); - - if (this.updateCallback) { - this.updateCallback(); - } - } - makePerspectiveMatrix(fovy, aspect, near, far) { - let out = new Matrix4(); - let f = 1.0 / Math.tan(fovy / 2), - nf = 1 / (near - far); - - let newMatrix = [f / aspect, 0, 0, 0, - 0, f, 0, 0, - 0, 0, (far + near) * nf, -1, - 0, 0, 2 * far * near * nf, 0]; - - out.elements = newMatrix; - return out; - } - } - - var GLTFLoader = ( function () { - - function GLTFLoader( manager ) { - - Loader.call( this, manager ); - - this.dracoLoader = null; - this.ddsLoader = null; - this.ktx2Loader = null; - - this.pluginCallbacks = []; - - this.register( function ( parser ) { - - return new GLTFMaterialsClearcoatExtension( parser ); - - } ); - this.register( function ( parser ) { - - return new GLTFTextureBasisUExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsTransmissionExtension( parser ); - - } ); - - } - - GLTFLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: GLTFLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var resourcePath; - - if ( this.resourcePath !== '' ) { - - resourcePath = this.resourcePath; - - } else if ( this.path !== '' ) { - - resourcePath = this.path; - - } else { - - resourcePath = LoaderUtils.extractUrlBase( url ); - - } - - // Tells the LoadingManager to track an extra item, which resolves after - // the model is fully loaded. This means the count of items loaded will - // be incorrect, but ensures manager.onLoad() does not fire early. - scope.manager.itemStart( url ); - - var _onError = function ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }; - - var loader = new FileLoader( scope.manager ); - - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - - if ( scope.crossOrigin === 'use-credentials' ) { - - loader.setWithCredentials( true ); - - } - - loader.load( url, function ( data ) { - - try { - - scope.parse( data, resourcePath, function ( gltf ) { - - onLoad( gltf ); - - scope.manager.itemEnd( url ); - - }, _onError ); - - } catch ( e ) { - - _onError( e ); - - } - - }, onProgress, _onError ); - - }, - - setDRACOLoader: function ( dracoLoader ) { - - this.dracoLoader = dracoLoader; - return this; - - }, - - setDDSLoader: function ( ddsLoader ) { - - this.ddsLoader = ddsLoader; - return this; - - }, - - setKTX2Loader: function ( ktx2Loader ) { - - this.ktx2Loader = ktx2Loader; - return this; - - }, - - register: function ( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) { - - this.pluginCallbacks.push( callback ); - - } - - return this; - - }, - - unregister: function ( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) { - - this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 ); - - } - - return this; - - }, - - parse: function ( data, path, onLoad, onError ) { - - var content; - var extensions = {}; - var plugins = {}; - - if ( typeof data === 'string' ) { - - content = data; - - } else { - - var magic = LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) ); - - if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { - - try { - - extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); - - } catch ( error ) { - - if ( onError ) onError( error ); - return; - - } - - content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; - - } else { - - content = LoaderUtils.decodeText( new Uint8Array( data ) ); - - } - - } - - var json = JSON.parse( content ); - - if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) { - - if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) ); - return; - - } - - var parser = new GLTFParser( json, { - - path: path || this.resourcePath || '', - crossOrigin: this.crossOrigin, - manager: this.manager, - ktx2Loader: this.ktx2Loader - - } ); - - parser.fileLoader.setRequestHeader( this.requestHeader ); - - for ( var i = 0; i < this.pluginCallbacks.length; i ++ ) { - - var plugin = this.pluginCallbacks[ i ]( parser ); - plugins[ plugin.name ] = plugin; - - // Workaround to avoid determining as unknown extension - // in addUnknownExtensionsToUserData(). - // Remove this workaround if we move all the existing - // extension handlers to plugin system - extensions[ plugin.name ] = true; - - } - - if ( json.extensionsUsed ) { - - for ( var i = 0; i < json.extensionsUsed.length; ++ i ) { - - var extensionName = json.extensionsUsed[ i ]; - var extensionsRequired = json.extensionsRequired || []; - - switch ( extensionName ) { - - case EXTENSIONS.KHR_LIGHTS_PUNCTUAL: - extensions[ extensionName ] = new GLTFLightsExtension( json ); - break; - - case EXTENSIONS.KHR_MATERIALS_UNLIT: - extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); - break; - - case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: - extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension(); - break; - - case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION: - extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); - break; - - case EXTENSIONS.MSFT_TEXTURE_DDS: - extensions[ extensionName ] = new GLTFTextureDDSExtension( this.ddsLoader ); - break; - - case EXTENSIONS.KHR_TEXTURE_TRANSFORM: - extensions[ extensionName ] = new GLTFTextureTransformExtension(); - break; - - case EXTENSIONS.KHR_MESH_QUANTIZATION: - extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); - break; - - default: - - if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) { - - console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' ); - - } - - } - - } - - } - - parser.setExtensions( extensions ); - parser.setPlugins( plugins ); - parser.parse( onLoad, onError ); - - } - - } ); - - /* GLTFREGISTRY */ - - function GLTFRegistry() { - - var objects = {}; - - return { - - get: function ( key ) { - - return objects[ key ]; - - }, - - add: function ( key, object ) { - - objects[ key ] = object; - - }, - - remove: function ( key ) { - - delete objects[ key ]; - - }, - - removeAll: function () { - - objects = {}; - - } - - }; - - } - - /*********************************/ - /********** EXTENSIONS ***********/ - /*********************************/ - - var EXTENSIONS = { - KHR_BINARY_GLTF: 'KHR_binary_glTF', - KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', - KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', - KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', - KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness', - KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', - KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', - KHR_TEXTURE_BASISU: 'KHR_texture_basisu', - KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', - KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', - MSFT_TEXTURE_DDS: 'MSFT_texture_dds' - }; - - /** - * DDS Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds - * - */ - function GLTFTextureDDSExtension( ddsLoader ) { - - if ( ! ddsLoader ) { - - throw new Error( 'THREE.GLTFLoader: Attempting to load .dds texture without importing DDSLoader' ); - - } - - this.name = EXTENSIONS.MSFT_TEXTURE_DDS; - this.ddsLoader = ddsLoader; - - } - - /** - * Punctual Lights Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual - */ - function GLTFLightsExtension( json ) { - - this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL; - - var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ] ) || {}; - this.lightDefs = extension.lights || []; - - } - - GLTFLightsExtension.prototype.loadLight = function ( lightIndex ) { - - var lightDef = this.lightDefs[ lightIndex ]; - var lightNode; - - var color = new Color( 0xffffff ); - if ( lightDef.color !== undefined ) color.fromArray( lightDef.color ); - - var range = lightDef.range !== undefined ? lightDef.range : 0; - - switch ( lightDef.type ) { - - case 'directional': - lightNode = new DirectionalLight( color ); - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - case 'point': - lightNode = new PointLight( color ); - lightNode.distance = range; - break; - - case 'spot': - lightNode = new SpotLight( color ); - lightNode.distance = range; - // Handle spotlight properties. - lightDef.spot = lightDef.spot || {}; - lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0; - lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0; - lightNode.angle = lightDef.spot.outerConeAngle; - lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle; - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - default: - throw new Error( 'THREE.GLTFLoader: Unexpected light type, "' + lightDef.type + '".' ); - - } - - // Some lights (e.g. spot) default to a position other than the origin. Reset the position - // here, because node-level parsing will only override position if explicitly specified. - lightNode.position.set( 0, 0, 0 ); - - lightNode.decay = 2; - - if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; - - lightNode.name = lightDef.name || ( 'light_' + lightIndex ); - - return Promise.resolve( lightNode ); - - }; - - /** - * Unlit Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit - */ - function GLTFMaterialsUnlitExtension() { - - this.name = EXTENSIONS.KHR_MATERIALS_UNLIT; - - } - - GLTFMaterialsUnlitExtension.prototype.getMaterialType = function () { - - return MeshBasicMaterial; - - }; - - GLTFMaterialsUnlitExtension.prototype.extendParams = function ( materialParams, materialDef, parser ) { - - var pending = []; - - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - var metallicRoughness = materialDef.pbrMetallicRoughness; - - if ( metallicRoughness ) { - - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - - var array = metallicRoughness.baseColorFactor; - - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; - - } - - if ( metallicRoughness.baseColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) ); - - } - - } - - return Promise.all( pending ); - - }; - - /** - * Clearcoat Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat - */ - function GLTFMaterialsClearcoatExtension( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT; - - } - - GLTFMaterialsClearcoatExtension.prototype.getMaterialType = function ( materialIndex ) { - - var parser = this.parser; - var materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - }; - - GLTFMaterialsClearcoatExtension.prototype.extendMaterialParams = function ( materialIndex, materialParams ) { - - var parser = this.parser; - var materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - var pending = []; - - var extension = materialDef.extensions[ this.name ]; - - if ( extension.clearcoatFactor !== undefined ) { - - materialParams.clearcoat = extension.clearcoatFactor; - - } - - if ( extension.clearcoatTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) ); - - } - - if ( extension.clearcoatRoughnessFactor !== undefined ) { - - materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor; - - } - - if ( extension.clearcoatRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) ); - - } - - if ( extension.clearcoatNormalTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) ); - - if ( extension.clearcoatNormalTexture.scale !== undefined ) { - - var scale = extension.clearcoatNormalTexture.scale; - - materialParams.clearcoatNormalScale = new Vector2( scale, scale ); - - } - - } - - return Promise.all( pending ); - - }; - - /** - * Transmission Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission - * Draft: https://github.com/KhronosGroup/glTF/pull/1698 - */ - function GLTFMaterialsTransmissionExtension( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION; - - } - - GLTFMaterialsTransmissionExtension.prototype.getMaterialType = function ( materialIndex ) { - - var parser = this.parser; - var materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - }; - - GLTFMaterialsTransmissionExtension.prototype.extendMaterialParams = function ( materialIndex, materialParams ) { - - var parser = this.parser; - var materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - var pending = []; - - var extension = materialDef.extensions[ this.name ]; - - if ( extension.transmissionFactor !== undefined ) { - - materialParams.transmission = extension.transmissionFactor; - - } - - if ( extension.transmissionTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) ); - - } - - return Promise.all( pending ); - - }; - - /** - * BasisU Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu - * (draft PR https://github.com/KhronosGroup/glTF/pull/1751) - */ - function GLTFTextureBasisUExtension( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_TEXTURE_BASISU; - - } - - GLTFTextureBasisUExtension.prototype.loadTexture = function ( textureIndex ) { - - var parser = this.parser; - var json = parser.json; - - var textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) { - - return null; - - } - - var extension = textureDef.extensions[ this.name ]; - var source = json.images[ extension.source ]; - var loader = parser.options.ktx2Loader; - - if ( ! loader ) { - - throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' ); - - } - - return parser.loadTextureImage( textureIndex, source, loader ); - - }; - - /* BINARY EXTENSION */ - var BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; - var BINARY_EXTENSION_HEADER_LENGTH = 12; - var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; - - function GLTFBinaryExtension( data ) { - - this.name = EXTENSIONS.KHR_BINARY_GLTF; - this.content = null; - this.body = null; - - var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); - - this.header = { - magic: LoaderUtils.decodeText( new Uint8Array( data.slice( 0, 4 ) ) ), - version: headerView.getUint32( 4, true ), - length: headerView.getUint32( 8, true ) - }; - - if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { - - throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); - - } else if ( this.header.version < 2.0 ) { - - throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' ); - - } - - var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); - var chunkIndex = 0; - - while ( chunkIndex < chunkView.byteLength ) { - - var chunkLength = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; - - var chunkType = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; - - if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { - - var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); - this.content = LoaderUtils.decodeText( contentArray ); - - } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { - - var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; - this.body = data.slice( byteOffset, byteOffset + chunkLength ); - - } - - // Clients must ignore chunks with unknown types. - - chunkIndex += chunkLength; - - } - - if ( this.content === null ) { - - throw new Error( 'THREE.GLTFLoader: JSON content not found.' ); - - } - - } - - /** - * DRACO Mesh Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression - */ - function GLTFDracoMeshCompressionExtension( json, dracoLoader ) { - - if ( ! dracoLoader ) { - - throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' ); - - } - - this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION; - this.json = json; - this.dracoLoader = dracoLoader; - this.dracoLoader.preload(); - - } - - GLTFDracoMeshCompressionExtension.prototype.decodePrimitive = function ( primitive, parser ) { - - var json = this.json; - var dracoLoader = this.dracoLoader; - var bufferViewIndex = primitive.extensions[ this.name ].bufferView; - var gltfAttributeMap = primitive.extensions[ this.name ].attributes; - var threeAttributeMap = {}; - var attributeNormalizedMap = {}; - var attributeTypeMap = {}; - - for ( var attributeName in gltfAttributeMap ) { - - var threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - - threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; - - } - - for ( attributeName in primitive.attributes ) { - - var threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - - if ( gltfAttributeMap[ attributeName ] !== undefined ) { - - var accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; - var componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - - attributeTypeMap[ threeAttributeName ] = componentType; - attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; - - } - - } - - return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) { - - return new Promise( function ( resolve ) { - - dracoLoader.decodeDracoFile( bufferView, function ( geometry ) { - - for ( var attributeName in geometry.attributes ) { - - var attribute = geometry.attributes[ attributeName ]; - var normalized = attributeNormalizedMap[ attributeName ]; - - if ( normalized !== undefined ) attribute.normalized = normalized; - - } - - resolve( geometry ); - - }, threeAttributeMap, attributeTypeMap ); - - } ); - - } ); - - }; - - /** - * Texture Transform Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform - */ - function GLTFTextureTransformExtension() { - - this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM; - - } - - GLTFTextureTransformExtension.prototype.extendTexture = function ( texture, transform ) { - - texture = texture.clone(); - - if ( transform.offset !== undefined ) { - - texture.offset.fromArray( transform.offset ); - - } - - if ( transform.rotation !== undefined ) { - - texture.rotation = transform.rotation; - - } - - if ( transform.scale !== undefined ) { - - texture.repeat.fromArray( transform.scale ); - - } - - if ( transform.texCoord !== undefined ) { - - console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' ); - - } - - texture.needsUpdate = true; - - return texture; - - }; - - /** - * Specular-Glossiness Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness - */ - - /** - * A sub class of StandardMaterial with some of the functionality - * changed via the `onBeforeCompile` callback - * @pailhead - */ - - function GLTFMeshStandardSGMaterial( params ) { - - MeshStandardMaterial.call( this ); - - this.isGLTFSpecularGlossinessMaterial = true; - - //various chunks that need replacing - var specularMapParsFragmentChunk = [ - '#ifdef USE_SPECULARMAP', - ' uniform sampler2D specularMap;', - '#endif' - ].join( '\n' ); - - var glossinessMapParsFragmentChunk = [ - '#ifdef USE_GLOSSINESSMAP', - ' uniform sampler2D glossinessMap;', - '#endif' - ].join( '\n' ); - - var specularMapFragmentChunk = [ - 'vec3 specularFactor = specular;', - '#ifdef USE_SPECULARMAP', - ' vec4 texelSpecular = texture2D( specularMap, vUv );', - ' texelSpecular = sRGBToLinear( texelSpecular );', - ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', - ' specularFactor *= texelSpecular.rgb;', - '#endif' - ].join( '\n' ); - - var glossinessMapFragmentChunk = [ - 'float glossinessFactor = glossiness;', - '#ifdef USE_GLOSSINESSMAP', - ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );', - ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture', - ' glossinessFactor *= texelGlossiness.a;', - '#endif' - ].join( '\n' ); - - var lightPhysicalFragmentChunk = [ - 'PhysicalMaterial material;', - 'material.diffuseColor = diffuseColor.rgb;', - 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', - 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );', - 'material.specularRoughness = max( 1.0 - glossinessFactor, 0.0525 );// 0.0525 corresponds to the base mip of a 256 cubemap.', - 'material.specularRoughness += geometryRoughness;', - 'material.specularRoughness = min( material.specularRoughness, 1.0 );', - 'material.specularColor = specularFactor.rgb;', - ].join( '\n' ); - - var uniforms = { - specular: { value: new Color().setHex( 0xffffff ) }, - glossiness: { value: 1 }, - specularMap: { value: null }, - glossinessMap: { value: null } - }; - - this._extraUniforms = uniforms; - - // please see #14031 or #13198 for an alternate approach - this.onBeforeCompile = function ( shader ) { - - for ( var uniformName in uniforms ) { - - shader.uniforms[ uniformName ] = uniforms[ uniformName ]; - - } - - shader.fragmentShader = shader.fragmentShader.replace( 'uniform float roughness;', 'uniform vec3 specular;' ); - shader.fragmentShader = shader.fragmentShader.replace( 'uniform float metalness;', 'uniform float glossiness;' ); - shader.fragmentShader = shader.fragmentShader.replace( '#include ', specularMapParsFragmentChunk ); - shader.fragmentShader = shader.fragmentShader.replace( '#include ', glossinessMapParsFragmentChunk ); - shader.fragmentShader = shader.fragmentShader.replace( '#include ', specularMapFragmentChunk ); - shader.fragmentShader = shader.fragmentShader.replace( '#include ', glossinessMapFragmentChunk ); - shader.fragmentShader = shader.fragmentShader.replace( '#include ', lightPhysicalFragmentChunk ); - - }; - - /*eslint-disable*/ - Object.defineProperties( - this, - { - specular: { - get: function () { return uniforms.specular.value; }, - set: function ( v ) { uniforms.specular.value = v; } - }, - specularMap: { - get: function () { return uniforms.specularMap.value; }, - set: function ( v ) { uniforms.specularMap.value = v; } - }, - glossiness: { - get: function () { return uniforms.glossiness.value; }, - set: function ( v ) { uniforms.glossiness.value = v; } - }, - glossinessMap: { - get: function () { return uniforms.glossinessMap.value; }, - set: function ( v ) { - - uniforms.glossinessMap.value = v; - //how about something like this - @pailhead - if ( v ) { - - this.defines.USE_GLOSSINESSMAP = ''; - // set USE_ROUGHNESSMAP to enable vUv - this.defines.USE_ROUGHNESSMAP = ''; - - } else { - - delete this.defines.USE_ROUGHNESSMAP; - delete this.defines.USE_GLOSSINESSMAP; - - } - - } - } - } - ); - - /*eslint-enable*/ - delete this.metalness; - delete this.roughness; - delete this.metalnessMap; - delete this.roughnessMap; - - this.setValues( params ); - - } - - GLTFMeshStandardSGMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); - GLTFMeshStandardSGMaterial.prototype.constructor = GLTFMeshStandardSGMaterial; - - GLTFMeshStandardSGMaterial.prototype.copy = function ( source ) { - - MeshStandardMaterial.prototype.copy.call( this, source ); - this.specularMap = source.specularMap; - this.specular.copy( source.specular ); - this.glossinessMap = source.glossinessMap; - this.glossiness = source.glossiness; - delete this.metalness; - delete this.roughness; - delete this.metalnessMap; - delete this.roughnessMap; - return this; - - }; - - function GLTFMaterialsPbrSpecularGlossinessExtension() { - - return { - - name: EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS, - - specularGlossinessParams: [ - 'color', - 'map', - 'lightMap', - 'lightMapIntensity', - 'aoMap', - 'aoMapIntensity', - 'emissive', - 'emissiveIntensity', - 'emissiveMap', - 'bumpMap', - 'bumpScale', - 'normalMap', - 'normalMapType', - 'displacementMap', - 'displacementScale', - 'displacementBias', - 'specularMap', - 'specular', - 'glossinessMap', - 'glossiness', - 'alphaMap', - 'envMap', - 'envMapIntensity', - 'refractionRatio', - ], - - getMaterialType: function () { - - return GLTFMeshStandardSGMaterial; - - }, - - extendParams: function ( materialParams, materialDef, parser ) { - - var pbrSpecularGlossiness = materialDef.extensions[ this.name ]; - - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - var pending = []; - - if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) { - - var array = pbrSpecularGlossiness.diffuseFactor; - - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; - - } - - if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', pbrSpecularGlossiness.diffuseTexture ) ); - - } - - materialParams.emissive = new Color( 0.0, 0.0, 0.0 ); - materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0; - materialParams.specular = new Color( 1.0, 1.0, 1.0 ); - - if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) { - - materialParams.specular.fromArray( pbrSpecularGlossiness.specularFactor ); - - } - - if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) { - - var specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture; - pending.push( parser.assignTexture( materialParams, 'glossinessMap', specGlossMapDef ) ); - pending.push( parser.assignTexture( materialParams, 'specularMap', specGlossMapDef ) ); - - } - - return Promise.all( pending ); - - }, - - createMaterial: function ( materialParams ) { - - var material = new GLTFMeshStandardSGMaterial( materialParams ); - material.fog = true; - - material.color = materialParams.color; - - material.map = materialParams.map === undefined ? null : materialParams.map; - - material.lightMap = null; - material.lightMapIntensity = 1.0; - - material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap; - material.aoMapIntensity = 1.0; - - material.emissive = materialParams.emissive; - material.emissiveIntensity = 1.0; - material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap; - - material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap; - material.bumpScale = 1; - - material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap; - material.normalMapType = TangentSpaceNormalMap; - - if ( materialParams.normalScale ) material.normalScale = materialParams.normalScale; - - material.displacementMap = null; - material.displacementScale = 1; - material.displacementBias = 0; - - material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap; - material.specular = materialParams.specular; - - material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap; - material.glossiness = materialParams.glossiness; - - material.alphaMap = null; - - material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap; - material.envMapIntensity = 1.0; - - material.refractionRatio = 0.98; - - return material; - - }, - - }; - - } - - /** - * Mesh Quantization Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization - */ - function GLTFMeshQuantizationExtension() { - - this.name = EXTENSIONS.KHR_MESH_QUANTIZATION; - - } - - /*********************************/ - /********** INTERPOLATION ********/ - /*********************************/ - - // Spline Interpolation - // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation - function GLTFCubicSplineInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - GLTFCubicSplineInterpolant.prototype = Object.create( Interpolant.prototype ); - GLTFCubicSplineInterpolant.prototype.constructor = GLTFCubicSplineInterpolant; - - GLTFCubicSplineInterpolant.prototype.copySampleValue_ = function ( index ) { - - // Copies a sample value to the result buffer. See description of glTF - // CUBICSPLINE values layout in interpolate_() function below. - - var result = this.resultBuffer, - values = this.sampleValues, - valueSize = this.valueSize, - offset = index * valueSize * 3 + valueSize; - - for ( var i = 0; i !== valueSize; i ++ ) { - - result[ i ] = values[ offset + i ]; - - } - - return result; - - }; - - GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; - - GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; - - GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) { - - var result = this.resultBuffer; - var values = this.sampleValues; - var stride = this.valueSize; - - var stride2 = stride * 2; - var stride3 = stride * 3; - - var td = t1 - t0; - - var p = ( t - t0 ) / td; - var pp = p * p; - var ppp = pp * p; - - var offset1 = i1 * stride3; - var offset0 = offset1 - stride3; - - var s2 = - 2 * ppp + 3 * pp; - var s3 = ppp - pp; - var s0 = 1 - s2; - var s1 = s3 - pp + p; - - // Layout of keyframe output values for CUBICSPLINE animations: - // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] - for ( var i = 0; i !== stride; i ++ ) { - - var p0 = values[ offset0 + i + stride ]; // splineVertex_k - var m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k) - var p1 = values[ offset1 + i + stride ]; // splineVertex_k+1 - var m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k) - - result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; - - } - - return result; - - }; - - /*********************************/ - /********** INTERNALS ************/ - /*********************************/ - - /* CONSTANTS */ - - var WEBGL_CONSTANTS = { - FLOAT: 5126, - //FLOAT_MAT2: 35674, - FLOAT_MAT3: 35675, - FLOAT_MAT4: 35676, - FLOAT_VEC2: 35664, - FLOAT_VEC3: 35665, - FLOAT_VEC4: 35666, - LINEAR: 9729, - REPEAT: 10497, - SAMPLER_2D: 35678, - POINTS: 0, - LINES: 1, - LINE_LOOP: 2, - LINE_STRIP: 3, - TRIANGLES: 4, - TRIANGLE_STRIP: 5, - TRIANGLE_FAN: 6, - UNSIGNED_BYTE: 5121, - UNSIGNED_SHORT: 5123 - }; - - var WEBGL_COMPONENT_TYPES = { - 5120: Int8Array, - 5121: Uint8Array, - 5122: Int16Array, - 5123: Uint16Array, - 5125: Uint32Array, - 5126: Float32Array - }; - - var WEBGL_FILTERS = { - 9728: NearestFilter, - 9729: LinearFilter, - 9984: NearestMipmapNearestFilter, - 9985: LinearMipmapNearestFilter, - 9986: NearestMipmapLinearFilter, - 9987: LinearMipmapLinearFilter - }; - - var WEBGL_WRAPPINGS = { - 33071: ClampToEdgeWrapping, - 33648: MirroredRepeatWrapping, - 10497: RepeatWrapping - }; - - var WEBGL_TYPE_SIZES = { - 'SCALAR': 1, - 'VEC2': 2, - 'VEC3': 3, - 'VEC4': 4, - 'MAT2': 4, - 'MAT3': 9, - 'MAT4': 16 - }; - - var ATTRIBUTES = { - POSITION: 'position', - NORMAL: 'normal', - TANGENT: 'tangent', - TEXCOORD_0: 'uv', - TEXCOORD_1: 'uv2', - COLOR_0: 'color', - WEIGHTS_0: 'skinWeight', - JOINTS_0: 'skinIndex', - }; - - var PATH_PROPERTIES = { - scale: 'scale', - translation: 'position', - rotation: 'quaternion', - weights: 'morphTargetInfluences' - }; - - var INTERPOLATION = { - CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each - // keyframe track will be initialized with a default interpolation type, then modified. - LINEAR: InterpolateLinear, - STEP: InterpolateDiscrete - }; - - var ALPHA_MODES = { - OPAQUE: 'OPAQUE', - MASK: 'MASK', - BLEND: 'BLEND' - }; - - /* UTILITY FUNCTIONS */ - - function resolveURL( url, path ) { - - // Invalid URL - if ( typeof url !== 'string' || url === '' ) return ''; - - // Host Relative URL - if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) { - - path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' ); - - } - - // Absolute URL http://,https://,// - if ( /^(https?:)?\/\//i.test( url ) ) return url; - - // Data URI - if ( /^data:.*,.*$/i.test( url ) ) return url; - - // Blob URL - if ( /^blob:.*$/i.test( url ) ) return url; - - // Relative URL - return path + url; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material - */ - function createDefaultMaterial( cache ) { - - if ( cache[ 'DefaultMaterial' ] === undefined ) { - - cache[ 'DefaultMaterial' ] = new MeshStandardMaterial( { - color: 0xFFFFFF, - emissive: 0x000000, - metalness: 1, - roughness: 1, - transparent: false, - depthTest: true, - side: FrontSide - } ); - - } - - return cache[ 'DefaultMaterial' ]; - - } - - function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { - - // Add unknown glTF extensions to an object's userData. - - for ( var name in objectDef.extensions ) { - - if ( knownExtensions[ name ] === undefined ) { - - object.userData.gltfExtensions = object.userData.gltfExtensions || {}; - object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ]; - - } - - } - - } - - /** - * @param {Object3D|Material|BufferGeometry} object - * @param {GLTF.definition} gltfDef - */ - function assignExtrasToUserData( object, gltfDef ) { - - if ( gltfDef.extras !== undefined ) { - - if ( typeof gltfDef.extras === 'object' ) { - - Object.assign( object.userData, gltfDef.extras ); - - } else { - - console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras ); - - } - - } - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets - * - * @param {BufferGeometry} geometry - * @param {Array} targets - * @param {GLTFParser} parser - * @return {Promise} - */ - function addMorphTargets( geometry, targets, parser ) { - - var hasMorphPosition = false; - var hasMorphNormal = false; - - for ( var i = 0, il = targets.length; i < il; i ++ ) { - - var target = targets[ i ]; - - if ( target.POSITION !== undefined ) hasMorphPosition = true; - if ( target.NORMAL !== undefined ) hasMorphNormal = true; - - if ( hasMorphPosition && hasMorphNormal ) break; - - } - - if ( ! hasMorphPosition && ! hasMorphNormal ) return Promise.resolve( geometry ); - - var pendingPositionAccessors = []; - var pendingNormalAccessors = []; - - for ( var i = 0, il = targets.length; i < il; i ++ ) { - - var target = targets[ i ]; - - if ( hasMorphPosition ) { - - var pendingAccessor = target.POSITION !== undefined - ? parser.getDependency( 'accessor', target.POSITION ) - : geometry.attributes.position; - - pendingPositionAccessors.push( pendingAccessor ); - - } - - if ( hasMorphNormal ) { - - var pendingAccessor = target.NORMAL !== undefined - ? parser.getDependency( 'accessor', target.NORMAL ) - : geometry.attributes.normal; - - pendingNormalAccessors.push( pendingAccessor ); - - } - - } - - return Promise.all( [ - Promise.all( pendingPositionAccessors ), - Promise.all( pendingNormalAccessors ) - ] ).then( function ( accessors ) { - - var morphPositions = accessors[ 0 ]; - var morphNormals = accessors[ 1 ]; - - if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions; - if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals; - geometry.morphTargetsRelative = true; - - return geometry; - - } ); - - } - - /** - * @param {Mesh} mesh - * @param {GLTF.Mesh} meshDef - */ - function updateMorphTargets( mesh, meshDef ) { - - mesh.updateMorphTargets(); - - if ( meshDef.weights !== undefined ) { - - for ( var i = 0, il = meshDef.weights.length; i < il; i ++ ) { - - mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; - - } - - } - - // .extras has user-defined data, so check that .extras.targetNames is an array. - if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) { - - var targetNames = meshDef.extras.targetNames; - - if ( mesh.morphTargetInfluences.length === targetNames.length ) { - - mesh.morphTargetDictionary = {}; - - for ( var i = 0, il = targetNames.length; i < il; i ++ ) { - - mesh.morphTargetDictionary[ targetNames[ i ] ] = i; - - } - - } else { - - console.warn( 'THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.' ); - - } - - } - - } - - function createPrimitiveKey( primitiveDef ) { - - var dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]; - var geometryKey; - - if ( dracoExtension ) { - - geometryKey = 'draco:' + dracoExtension.bufferView - + ':' + dracoExtension.indices - + ':' + createAttributesKey( dracoExtension.attributes ); - - } else { - - geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; - - } - - return geometryKey; - - } - - function createAttributesKey( attributes ) { - - var attributesKey = ''; - - var keys = Object.keys( attributes ).sort(); - - for ( var i = 0, il = keys.length; i < il; i ++ ) { - - attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';'; - - } - - return attributesKey; - - } - - /* GLTF PARSER */ - - function GLTFParser( json, options ) { - - this.json = json || {}; - this.extensions = {}; - this.plugins = {}; - this.options = options || {}; - - // loader object cache - this.cache = new GLTFRegistry(); - - // associations between Three.js objects and glTF elements - this.associations = new Map(); - - // BufferGeometry caching - this.primitiveCache = {}; - - // Object3D instance caches - this.meshCache = { refs: {}, uses: {} }; - this.cameraCache = { refs: {}, uses: {} }; - this.lightCache = { refs: {}, uses: {} }; - - // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the - // expensive work of uploading a texture to the GPU off the main thread. - if ( typeof createImageBitmap !== 'undefined' && /Firefox/.test( navigator.userAgent ) === false ) { - - this.textureLoader = new ImageBitmapLoader( this.options.manager ); - - } else { - - this.textureLoader = new TextureLoader( this.options.manager ); - - } - - this.textureLoader.setCrossOrigin( this.options.crossOrigin ); - - this.fileLoader = new FileLoader( this.options.manager ); - this.fileLoader.setResponseType( 'arraybuffer' ); - - if ( this.options.crossOrigin === 'use-credentials' ) { - - this.fileLoader.setWithCredentials( true ); - - } - - } - - GLTFParser.prototype.setExtensions = function ( extensions ) { - - this.extensions = extensions; - - }; - - GLTFParser.prototype.setPlugins = function ( plugins ) { - - this.plugins = plugins; - - }; - - GLTFParser.prototype.parse = function ( onLoad, onError ) { - - var parser = this; - var json = this.json; - var extensions = this.extensions; - - // Clear the loader cache - this.cache.removeAll(); - - // Mark the special nodes/meshes in json for efficient parse - this._markDefs(); - - Promise.all( [ - - this.getDependencies( 'scene' ), - this.getDependencies( 'animation' ), - this.getDependencies( 'camera' ), - - ] ).then( function ( dependencies ) { - - var result = { - scene: dependencies[ 0 ][ json.scene || 0 ], - scenes: dependencies[ 0 ], - animations: dependencies[ 1 ], - cameras: dependencies[ 2 ], - asset: json.asset, - parser: parser, - userData: {} - }; - - addUnknownExtensionsToUserData( extensions, result, json ); - - assignExtrasToUserData( result, json ); - - onLoad( result ); - - } ).catch( onError ); - - }; - - /** - * Marks the special nodes/meshes in json for efficient parse. - */ - GLTFParser.prototype._markDefs = function () { - - var nodeDefs = this.json.nodes || []; - var skinDefs = this.json.skins || []; - var meshDefs = this.json.meshes || []; - - // Nothing in the node definition indicates whether it is a Bone or an - // Object3D. Use the skins' joint references to mark bones. - for ( var skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) { - - var joints = skinDefs[ skinIndex ].joints; - - for ( var i = 0, il = joints.length; i < il; i ++ ) { - - nodeDefs[ joints[ i ] ].isBone = true; - - } - - } - - // Iterate over all nodes, marking references to shared resources, - // as well as skeleton joints. - for ( var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { - - var nodeDef = nodeDefs[ nodeIndex ]; - - if ( nodeDef.mesh !== undefined ) { - - this._addNodeRef( this.meshCache, nodeDef.mesh ); - - // Nothing in the mesh definition indicates whether it is - // a SkinnedMesh or Mesh. Use the node's mesh reference - // to mark SkinnedMesh if node has skin. - if ( nodeDef.skin !== undefined ) { - - meshDefs[ nodeDef.mesh ].isSkinnedMesh = true; - - } - - } - - if ( nodeDef.camera !== undefined ) { - - this._addNodeRef( this.cameraCache, nodeDef.camera ); - - } - - if ( nodeDef.extensions - && nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ] - && nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light !== undefined ) { - - this._addNodeRef( this.lightCache, nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light ); - - } - - } - - }; - - /** - * Counts references to shared node / Object3D resources. These resources - * can be reused, or "instantiated", at multiple nodes in the scene - * hierarchy. Mesh, Camera, and Light instances are instantiated and must - * be marked. Non-scenegraph resources (like Materials, Geometries, and - * Textures) can be reused directly and are not marked here. - * - * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. - */ - GLTFParser.prototype._addNodeRef = function ( cache, index ) { - - if ( index === undefined ) return; - - if ( cache.refs[ index ] === undefined ) { - - cache.refs[ index ] = cache.uses[ index ] = 0; - - } - - cache.refs[ index ] ++; - - }; - - /** Returns a reference to a shared resource, cloning it if necessary. */ - GLTFParser.prototype._getNodeRef = function ( cache, index, object ) { - - if ( cache.refs[ index ] <= 1 ) return object; - - var ref = object.clone(); - - ref.name += '_instance_' + ( cache.uses[ index ] ++ ); - - return ref; - - }; - - GLTFParser.prototype._invokeOne = function ( func ) { - - var extensions = Object.values( this.plugins ); - extensions.push( this ); - - for ( var i = 0; i < extensions.length; i ++ ) { - - var result = func( extensions[ i ] ); - - if ( result ) return result; - - } - - }; - - GLTFParser.prototype._invokeAll = function ( func ) { - - var extensions = Object.values( this.plugins ); - extensions.unshift( this ); - - var pending = []; - - for ( var i = 0; i < extensions.length; i ++ ) { - - pending.push( func( extensions[ i ] ) ); - - } - - return Promise.all( pending ); - - }; - - /** - * Requests the specified dependency asynchronously, with caching. - * @param {string} type - * @param {number} index - * @return {Promise} - */ - GLTFParser.prototype.getDependency = function ( type, index ) { - - var cacheKey = type + ':' + index; - var dependency = this.cache.get( cacheKey ); - - if ( ! dependency ) { - - switch ( type ) { - - case 'scene': - dependency = this.loadScene( index ); - break; - - case 'node': - dependency = this.loadNode( index ); - break; - - case 'mesh': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadMesh && ext.loadMesh( index ); - - } ); - break; - - case 'accessor': - dependency = this.loadAccessor( index ); - break; - - case 'bufferView': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadBufferView && ext.loadBufferView( index ); - - } ); - break; - - case 'buffer': - dependency = this.loadBuffer( index ); - break; - - case 'material': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadMaterial && ext.loadMaterial( index ); - - } ); - break; - - case 'texture': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadTexture && ext.loadTexture( index ); - - } ); - break; - - case 'skin': - dependency = this.loadSkin( index ); - break; - - case 'animation': - dependency = this.loadAnimation( index ); - break; - - case 'camera': - dependency = this.loadCamera( index ); - break; - - case 'light': - dependency = this.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].loadLight( index ); - break; - - default: - throw new Error( 'Unknown type: ' + type ); - - } - - this.cache.add( cacheKey, dependency ); - - } - - return dependency; - - }; - - /** - * Requests all dependencies of the specified type asynchronously, with caching. - * @param {string} type - * @return {Promise>} - */ - GLTFParser.prototype.getDependencies = function ( type ) { - - var dependencies = this.cache.get( type ); - - if ( ! dependencies ) { - - var parser = this; - var defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || []; - - dependencies = Promise.all( defs.map( function ( def, index ) { - - return parser.getDependency( type, index ); - - } ) ); - - this.cache.add( type, dependencies ); - - } - - return dependencies; - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferIndex - * @return {Promise} - */ - GLTFParser.prototype.loadBuffer = function ( bufferIndex ) { - - var bufferDef = this.json.buffers[ bufferIndex ]; - var loader = this.fileLoader; - - if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) { - - throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' ); - - } - - // If present, GLB container is required to be the first buffer. - if ( bufferDef.uri === undefined && bufferIndex === 0 ) { - - return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body ); - - } - - var options = this.options; - - return new Promise( function ( resolve, reject ) { - - loader.load( resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () { - - reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) ); - - } ); - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferViewIndex - * @return {Promise} - */ - GLTFParser.prototype.loadBufferView = function ( bufferViewIndex ) { - - var bufferViewDef = this.json.bufferViews[ bufferViewIndex ]; - - return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) { - - var byteLength = bufferViewDef.byteLength || 0; - var byteOffset = bufferViewDef.byteOffset || 0; - return buffer.slice( byteOffset, byteOffset + byteLength ); - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors - * @param {number} accessorIndex - * @return {Promise} - */ - GLTFParser.prototype.loadAccessor = function ( accessorIndex ) { - - var parser = this; - var json = this.json; - - var accessorDef = this.json.accessors[ accessorIndex ]; - - if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { - - // Ignore empty accessors, which may be used to declare runtime - // information about attributes coming from another source (e.g. Draco - // compression extension). - return Promise.resolve( null ); - - } - - var pendingBufferViews = []; - - if ( accessorDef.bufferView !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) ); - - } else { - - pendingBufferViews.push( null ); - - } - - if ( accessorDef.sparse !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) ); - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) ); - - } - - return Promise.all( pendingBufferViews ).then( function ( bufferViews ) { - - var bufferView = bufferViews[ 0 ]; - - var itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; - var TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - - // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. - var elementBytes = TypedArray.BYTES_PER_ELEMENT; - var itemBytes = elementBytes * itemSize; - var byteOffset = accessorDef.byteOffset || 0; - var byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined; - var normalized = accessorDef.normalized === true; - var array, bufferAttribute; - - // The buffer is not interleaved if the stride is the item size in bytes. - if ( byteStride && byteStride !== itemBytes ) { - - // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer - // This makes sure that IBA.count reflects accessor.count properly - var ibSlice = Math.floor( byteOffset / byteStride ); - var ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count; - var ib = parser.cache.get( ibCacheKey ); - - if ( ! ib ) { - - array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes ); - - // Integer parameters to IB/IBA are in array elements, not bytes. - ib = new InterleavedBuffer( array, byteStride / elementBytes ); - - parser.cache.add( ibCacheKey, ib ); - - } - - bufferAttribute = new InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized ); - - } else { - - if ( bufferView === null ) { - - array = new TypedArray( accessorDef.count * itemSize ); - - } else { - - array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize ); - - } - - bufferAttribute = new BufferAttribute( array, itemSize, normalized ); - - } - - // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors - if ( accessorDef.sparse !== undefined ) { - - var itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR; - var TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ]; - - var byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; - var byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; - - var sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices ); - var sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize ); - - if ( bufferView !== null ) { - - // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. - bufferAttribute = new BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized ); - - } - - for ( var i = 0, il = sparseIndices.length; i < il; i ++ ) { - - var index = sparseIndices[ i ]; - - bufferAttribute.setX( index, sparseValues[ i * itemSize ] ); - if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] ); - if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] ); - if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] ); - if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.' ); - - } - - } - - return bufferAttribute; - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures - * @param {number} textureIndex - * @return {Promise} - */ - GLTFParser.prototype.loadTexture = function ( textureIndex ) { - - var parser = this; - var json = this.json; - var options = this.options; - - var textureDef = json.textures[ textureIndex ]; - - var textureExtensions = textureDef.extensions || {}; - - var source; - - if ( textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] ) { - - source = json.images[ textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ].source ]; - - } else { - - source = json.images[ textureDef.source ]; - - } - - var loader; - - if ( source.uri ) { - - loader = options.manager.getHandler( source.uri ); - - } - - if ( ! loader ) { - - loader = textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] - ? parser.extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ].ddsLoader - : this.textureLoader; - - } - - return this.loadTextureImage( textureIndex, source, loader ); - - }; - - GLTFParser.prototype.loadTextureImage = function ( textureIndex, source, loader ) { - - var parser = this; - var json = this.json; - var options = this.options; - - var textureDef = json.textures[ textureIndex ]; - - var URL = self.URL || self.webkitURL; - - var sourceURI = source.uri; - var isObjectURL = false; - var hasAlpha = true; - - if ( source.mimeType === 'image/jpeg' ) hasAlpha = false; - - if ( source.bufferView !== undefined ) { - - // Load binary image data from bufferView, if provided. - - sourceURI = parser.getDependency( 'bufferView', source.bufferView ).then( function ( bufferView ) { - - if ( source.mimeType === 'image/png' ) { - - // https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header - hasAlpha = new DataView( bufferView, 25, 1 ).getUint8( 0, false ) === 6; - - } - - isObjectURL = true; - var blob = new Blob( [ bufferView ], { type: source.mimeType } ); - sourceURI = URL.createObjectURL( blob ); - return sourceURI; - - } ); - - } - - return Promise.resolve( sourceURI ).then( function ( sourceURI ) { - - return new Promise( function ( resolve, reject ) { - - var onLoad = resolve; - - if ( loader.isImageBitmapLoader === true ) { - - onLoad = function ( imageBitmap ) { - - resolve( new CanvasTexture( imageBitmap ) ); - - }; - - } - - loader.load( resolveURL( sourceURI, options.path ), onLoad, undefined, reject ); - - } ); - - } ).then( function ( texture ) { - - // Clean up resources and configure Texture. - - if ( isObjectURL === true ) { - - URL.revokeObjectURL( sourceURI ); - - } - - texture.flipY = false; - - if ( textureDef.name ) texture.name = textureDef.name; - - // When there is definitely no alpha channel in the texture, set RGBFormat to save space. - if ( ! hasAlpha ) texture.format = RGBFormat; - - var samplers = json.samplers || {}; - var sampler = samplers[ textureDef.sampler ] || {}; - - texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || LinearFilter; - texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || LinearMipmapLinearFilter; - texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || RepeatWrapping; - texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || RepeatWrapping; - - parser.associations.set( texture, { - type: 'textures', - index: textureIndex - } ); - - return texture; - - } ); - - }; - - /** - * Asynchronously assigns a texture to the given material parameters. - * @param {Object} materialParams - * @param {string} mapName - * @param {Object} mapDef - * @return {Promise} - */ - GLTFParser.prototype.assignTexture = function ( materialParams, mapName, mapDef ) { - - var parser = this; - - return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) { - - // Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured - // However, we will copy UV set 0 to UV set 1 on demand for aoMap - if ( mapDef.texCoord !== undefined && mapDef.texCoord != 0 && ! ( mapName === 'aoMap' && mapDef.texCoord == 1 ) ) { - - console.warn( 'THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.' ); - - } - - if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) { - - var transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined; - - if ( transform ) { - - var gltfReference = parser.associations.get( texture ); - texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); - parser.associations.set( texture, gltfReference ); - - } - - } - - materialParams[ mapName ] = texture; - - } ); - - }; - - /** - * Assigns final material to a Mesh, Line, or Points instance. The instance - * already has a material (generated from the glTF material options alone) - * but reuse of the same glTF material may require multiple threejs materials - * to accomodate different primitive types, defines, etc. New materials will - * be created if necessary, and reused from a cache. - * @param {Object3D} mesh Mesh, Line, or Points instance. - */ - GLTFParser.prototype.assignFinalMaterial = function ( mesh ) { - - var geometry = mesh.geometry; - var material = mesh.material; - - var useVertexTangents = geometry.attributes.tangent !== undefined; - var useVertexColors = geometry.attributes.color !== undefined; - var useFlatShading = geometry.attributes.normal === undefined; - var useSkinning = mesh.isSkinnedMesh === true; - var useMorphTargets = Object.keys( geometry.morphAttributes ).length > 0; - var useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined; - - if ( mesh.isPoints ) { - - var cacheKey = 'PointsMaterial:' + material.uuid; - - var pointsMaterial = this.cache.get( cacheKey ); - - if ( ! pointsMaterial ) { - - pointsMaterial = new PointsMaterial(); - Material.prototype.copy.call( pointsMaterial, material ); - pointsMaterial.color.copy( material.color ); - pointsMaterial.map = material.map; - pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px - - this.cache.add( cacheKey, pointsMaterial ); - - } - - material = pointsMaterial; - - } else if ( mesh.isLine ) { - - var cacheKey = 'LineBasicMaterial:' + material.uuid; - - var lineMaterial = this.cache.get( cacheKey ); - - if ( ! lineMaterial ) { - - lineMaterial = new LineBasicMaterial(); - Material.prototype.copy.call( lineMaterial, material ); - lineMaterial.color.copy( material.color ); - - this.cache.add( cacheKey, lineMaterial ); - - } - - material = lineMaterial; - - } - - // Clone the material if it will be modified - if ( useVertexTangents || useVertexColors || useFlatShading || useSkinning || useMorphTargets ) { - - var cacheKey = 'ClonedMaterial:' + material.uuid + ':'; - - if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:'; - if ( useSkinning ) cacheKey += 'skinning:'; - if ( useVertexTangents ) cacheKey += 'vertex-tangents:'; - if ( useVertexColors ) cacheKey += 'vertex-colors:'; - if ( useFlatShading ) cacheKey += 'flat-shading:'; - if ( useMorphTargets ) cacheKey += 'morph-targets:'; - if ( useMorphNormals ) cacheKey += 'morph-normals:'; - - var cachedMaterial = this.cache.get( cacheKey ); - - if ( ! cachedMaterial ) { - - cachedMaterial = material.clone(); - - if ( useSkinning ) cachedMaterial.skinning = true; - if ( useVertexTangents ) cachedMaterial.vertexTangents = true; - if ( useVertexColors ) cachedMaterial.vertexColors = true; - if ( useFlatShading ) cachedMaterial.flatShading = true; - if ( useMorphTargets ) cachedMaterial.morphTargets = true; - if ( useMorphNormals ) cachedMaterial.morphNormals = true; - - this.cache.add( cacheKey, cachedMaterial ); - - this.associations.set( cachedMaterial, this.associations.get( material ) ); - - } - - material = cachedMaterial; - - } - - // workarounds for mesh and geometry - - if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) { - - geometry.setAttribute( 'uv2', geometry.attributes.uv ); - - } - - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - if ( material.normalScale && ! useVertexTangents ) { - - material.normalScale.y = - material.normalScale.y; - - } - - if ( material.clearcoatNormalScale && ! useVertexTangents ) { - - material.clearcoatNormalScale.y = - material.clearcoatNormalScale.y; - - } - - mesh.material = material; - - }; - - GLTFParser.prototype.getMaterialType = function ( /* materialIndex */ ) { - - return MeshStandardMaterial; - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials - * @param {number} materialIndex - * @return {Promise} - */ - GLTFParser.prototype.loadMaterial = function ( materialIndex ) { - - var parser = this; - var json = this.json; - var extensions = this.extensions; - var materialDef = json.materials[ materialIndex ]; - - var materialType; - var materialParams = {}; - var materialExtensions = materialDef.extensions || {}; - - var pending = []; - - if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) { - - var sgExtension = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ]; - materialType = sgExtension.getMaterialType(); - pending.push( sgExtension.extendParams( materialParams, materialDef, parser ) ); - - } else if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) { - - var kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ]; - materialType = kmuExtension.getMaterialType(); - pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); - - } else { - - // Specification: - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material - - var metallicRoughness = materialDef.pbrMetallicRoughness || {}; - - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - - var array = metallicRoughness.baseColorFactor; - - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; - - } - - if ( metallicRoughness.baseColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) ); - - } - - materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; - materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; - - if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) ); - pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) ); - - } - - materialType = this._invokeOne( function ( ext ) { - - return ext.getMaterialType && ext.getMaterialType( materialIndex ); - - } ); - - pending.push( this._invokeAll( function ( ext ) { - - return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams ); - - } ) ); - - } - - if ( materialDef.doubleSided === true ) { - - materialParams.side = DoubleSide; - - } - - var alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; - - if ( alphaMode === ALPHA_MODES.BLEND ) { - - materialParams.transparent = true; - - // See: https://github.com/mrdoob/three.js/issues/17706 - materialParams.depthWrite = false; - - } else { - - materialParams.transparent = false; - - if ( alphaMode === ALPHA_MODES.MASK ) { - - materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; - - } - - } - - if ( materialDef.normalTexture !== undefined && materialType !== MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); - - materialParams.normalScale = new Vector2( 1, 1 ); - - if ( materialDef.normalTexture.scale !== undefined ) { - - materialParams.normalScale.set( materialDef.normalTexture.scale, materialDef.normalTexture.scale ); - - } - - } - - if ( materialDef.occlusionTexture !== undefined && materialType !== MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) ); - - if ( materialDef.occlusionTexture.strength !== undefined ) { - - materialParams.aoMapIntensity = materialDef.occlusionTexture.strength; - - } - - } - - if ( materialDef.emissiveFactor !== undefined && materialType !== MeshBasicMaterial ) { - - materialParams.emissive = new Color().fromArray( materialDef.emissiveFactor ); - - } - - if ( materialDef.emissiveTexture !== undefined && materialType !== MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture ) ); - - } - - return Promise.all( pending ).then( function () { - - var material; - - if ( materialType === GLTFMeshStandardSGMaterial ) { - - material = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams ); - - } else { - - material = new materialType( materialParams ); - - } - - if ( materialDef.name ) material.name = materialDef.name; - - // baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding. - if ( material.map ) material.map.encoding = sRGBEncoding; - if ( material.emissiveMap ) material.emissiveMap.encoding = sRGBEncoding; - - assignExtrasToUserData( material, materialDef ); - - parser.associations.set( material, { type: 'materials', index: materialIndex } ); - - if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); - - return material; - - } ); - - }; - - /** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - */ - function computeBounds( geometry, primitiveDef, parser ) { - - var attributes = primitiveDef.attributes; - - var box = new Box3(); - - if ( attributes.POSITION !== undefined ) { - - var accessor = parser.json.accessors[ attributes.POSITION ]; - - var min = accessor.min; - var max = accessor.max; - - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - - if ( min !== undefined && max !== undefined ) { - - box.set( - new Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ), - new Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) ); - - } else { - - console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); - - return; - - } - - } else { - - return; - - } - - var targets = primitiveDef.targets; - - if ( targets !== undefined ) { - - var maxDisplacement = new Vector3(); - var vector = new Vector3(); - - for ( var i = 0, il = targets.length; i < il; i ++ ) { - - var target = targets[ i ]; - - if ( target.POSITION !== undefined ) { - - var accessor = parser.json.accessors[ target.POSITION ]; - var min = accessor.min; - var max = accessor.max; - - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - - if ( min !== undefined && max !== undefined ) { - - // we need to get max of absolute components because target weight is [-1,1] - vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) ); - vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) ); - vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) ); - - // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative - // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets - // are used to implement key-frame animations and as such only two are active at a time - this results in very large - // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. - maxDisplacement.max( vector ); - - } else { - - console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); - - } - - } - - } - - // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. - box.expandByVector( maxDisplacement ); - - } - - geometry.boundingBox = box; - - var sphere = new Sphere(); - - box.getCenter( sphere.center ); - sphere.radius = box.min.distanceTo( box.max ) / 2; - - geometry.boundingSphere = sphere; - - } - - /** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - * @return {Promise} - */ - function addPrimitiveAttributes( geometry, primitiveDef, parser ) { - - var attributes = primitiveDef.attributes; - - var pending = []; - - function assignAttributeAccessor( accessorIndex, attributeName ) { - - return parser.getDependency( 'accessor', accessorIndex ) - .then( function ( accessor ) { - - geometry.setAttribute( attributeName, accessor ); - - } ); - - } - - for ( var gltfAttributeName in attributes ) { - - var threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); - - // Skip attributes already provided by e.g. Draco extension. - if ( threeAttributeName in geometry.attributes ) continue; - - pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) ); - - } - - if ( primitiveDef.indices !== undefined && ! geometry.index ) { - - var accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) { - - geometry.setIndex( accessor ); - - } ); - - pending.push( accessor ); - - } - - assignExtrasToUserData( geometry, primitiveDef ); - - computeBounds( geometry, primitiveDef, parser ); - - return Promise.all( pending ).then( function () { - - return primitiveDef.targets !== undefined - ? addMorphTargets( geometry, primitiveDef.targets, parser ) - : geometry; - - } ); - - } - - /** - * @param {BufferGeometry} geometry - * @param {Number} drawMode - * @return {BufferGeometry} - */ - function toTrianglesDrawMode( geometry, drawMode ) { - - var index = geometry.getIndex(); - - // generate index if not present - - if ( index === null ) { - - var indices = []; - - var position = geometry.getAttribute( 'position' ); - - if ( position !== undefined ) { - - for ( var i = 0; i < position.count; i ++ ) { - - indices.push( i ); - - } - - geometry.setIndex( indices ); - index = geometry.getIndex(); - - } else { - - console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' ); - return geometry; - - } - - } - - // - - var numberOfTriangles = index.count - 2; - var newIndices = []; - - if ( drawMode === TriangleFanDrawMode ) { - - // gl.TRIANGLE_FAN - - for ( var i = 1; i <= numberOfTriangles; i ++ ) { - - newIndices.push( index.getX( 0 ) ); - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - - } - - } else { - - // gl.TRIANGLE_STRIP - - for ( var i = 0; i < numberOfTriangles; i ++ ) { - - if ( i % 2 === 0 ) { - - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i + 2 ) ); - - - } else { - - newIndices.push( index.getX( i + 2 ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i ) ); - - } - - } - - } - - if ( ( newIndices.length / 3 ) !== numberOfTriangles ) { - - console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' ); - - } - - // build final geometry - - var newGeometry = geometry.clone(); - newGeometry.setIndex( newIndices ); - - return newGeometry; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry - * - * Creates BufferGeometries from primitives. - * - * @param {Array} primitives - * @return {Promise>} - */ - GLTFParser.prototype.loadGeometries = function ( primitives ) { - - var parser = this; - var extensions = this.extensions; - var cache = this.primitiveCache; - - function createDracoPrimitive( primitive ) { - - return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] - .decodePrimitive( primitive, parser ) - .then( function ( geometry ) { - - return addPrimitiveAttributes( geometry, primitive, parser ); - - } ); - - } - - var pending = []; - - for ( var i = 0, il = primitives.length; i < il; i ++ ) { - - var primitive = primitives[ i ]; - var cacheKey = createPrimitiveKey( primitive ); - - // See if we've already created this geometry - var cached = cache[ cacheKey ]; - - if ( cached ) { - - // Use the cached geometry if it exists - pending.push( cached.promise ); - - } else { - - var geometryPromise; - - if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) { - - // Use DRACO geometry if available - geometryPromise = createDracoPrimitive( primitive ); - - } else { - - // Otherwise create a new geometry - geometryPromise = addPrimitiveAttributes( new BufferGeometry(), primitive, parser ); - - } - - // Cache this geometry - cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise }; - - pending.push( geometryPromise ); - - } - - } - - return Promise.all( pending ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes - * @param {number} meshIndex - * @return {Promise} - */ - GLTFParser.prototype.loadMesh = function ( meshIndex ) { - - var parser = this; - var json = this.json; - - var meshDef = json.meshes[ meshIndex ]; - var primitives = meshDef.primitives; - - var pending = []; - - for ( var i = 0, il = primitives.length; i < il; i ++ ) { - - var material = primitives[ i ].material === undefined - ? createDefaultMaterial( this.cache ) - : this.getDependency( 'material', primitives[ i ].material ); - - pending.push( material ); - - } - - pending.push( parser.loadGeometries( primitives ) ); - - return Promise.all( pending ).then( function ( results ) { - - var materials = results.slice( 0, results.length - 1 ); - var geometries = results[ results.length - 1 ]; - - var meshes = []; - - for ( var i = 0, il = geometries.length; i < il; i ++ ) { - - var geometry = geometries[ i ]; - var primitive = primitives[ i ]; - - // 1. create Mesh - - var mesh; - - var material = materials[ i ]; - - if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || - primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP || - primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN || - primitive.mode === undefined ) { - - // .isSkinnedMesh isn't in glTF spec. See ._markDefs() - mesh = meshDef.isSkinnedMesh === true - ? new SkinnedMesh( geometry, material ) - : new Mesh( geometry, material ); - - if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) { - - // we normalize floating point skin weight array to fix malformed assets (see #15319) - // it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs - mesh.normalizeSkinWeights(); - - } - - if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) { - - mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleStripDrawMode ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) { - - mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleFanDrawMode ); - - } - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { - - mesh = new LineSegments( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) { - - mesh = new Line( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) { - - mesh = new LineLoop( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) { - - mesh = new Points( geometry, material ); - - } else { - - throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode ); - - } - - if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { - - updateMorphTargets( mesh, meshDef ); - - } - - mesh.name = meshDef.name || ( 'mesh_' + meshIndex ); - - if ( geometries.length > 1 ) mesh.name += '_' + i; - - assignExtrasToUserData( mesh, meshDef ); - - parser.assignFinalMaterial( mesh ); - - meshes.push( mesh ); - - } - - if ( meshes.length === 1 ) { - - return meshes[ 0 ]; - - } - - var group = new Group(); - - for ( var i = 0, il = meshes.length; i < il; i ++ ) { - - group.add( meshes[ i ] ); - - } - - return group; - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras - * @param {number} cameraIndex - * @return {Promise} - */ - GLTFParser.prototype.loadCamera = function ( cameraIndex ) { - - var camera; - var cameraDef = this.json.cameras[ cameraIndex ]; - var params = cameraDef[ cameraDef.type ]; - - if ( ! params ) { - - console.warn( 'THREE.GLTFLoader: Missing camera parameters.' ); - return; - - } - - if ( cameraDef.type === 'perspective' ) { - - camera = new PerspectiveCamera( MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); - - } else if ( cameraDef.type === 'orthographic' ) { - - camera = new OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar ); - - } - - if ( cameraDef.name ) camera.name = cameraDef.name; - - assignExtrasToUserData( camera, cameraDef ); - - return Promise.resolve( camera ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins - * @param {number} skinIndex - * @return {Promise} - */ - GLTFParser.prototype.loadSkin = function ( skinIndex ) { - - var skinDef = this.json.skins[ skinIndex ]; - - var skinEntry = { joints: skinDef.joints }; - - if ( skinDef.inverseBindMatrices === undefined ) { - - return Promise.resolve( skinEntry ); - - } - - return this.getDependency( 'accessor', skinDef.inverseBindMatrices ).then( function ( accessor ) { - - skinEntry.inverseBindMatrices = accessor; - - return skinEntry; - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations - * @param {number} animationIndex - * @return {Promise} - */ - GLTFParser.prototype.loadAnimation = function ( animationIndex ) { - - var json = this.json; - - var animationDef = json.animations[ animationIndex ]; - - var pendingNodes = []; - var pendingInputAccessors = []; - var pendingOutputAccessors = []; - var pendingSamplers = []; - var pendingTargets = []; - - for ( var i = 0, il = animationDef.channels.length; i < il; i ++ ) { - - var channel = animationDef.channels[ i ]; - var sampler = animationDef.samplers[ channel.sampler ]; - var target = channel.target; - var name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated. - var input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input; - var output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output; - - pendingNodes.push( this.getDependency( 'node', name ) ); - pendingInputAccessors.push( this.getDependency( 'accessor', input ) ); - pendingOutputAccessors.push( this.getDependency( 'accessor', output ) ); - pendingSamplers.push( sampler ); - pendingTargets.push( target ); - - } - - return Promise.all( [ - - Promise.all( pendingNodes ), - Promise.all( pendingInputAccessors ), - Promise.all( pendingOutputAccessors ), - Promise.all( pendingSamplers ), - Promise.all( pendingTargets ) - - ] ).then( function ( dependencies ) { - - var nodes = dependencies[ 0 ]; - var inputAccessors = dependencies[ 1 ]; - var outputAccessors = dependencies[ 2 ]; - var samplers = dependencies[ 3 ]; - var targets = dependencies[ 4 ]; - - var tracks = []; - - for ( var i = 0, il = nodes.length; i < il; i ++ ) { - - var node = nodes[ i ]; - var inputAccessor = inputAccessors[ i ]; - var outputAccessor = outputAccessors[ i ]; - var sampler = samplers[ i ]; - var target = targets[ i ]; - - if ( node === undefined ) continue; - - node.updateMatrix(); - node.matrixAutoUpdate = true; - - var TypedKeyframeTrack; - - switch ( PATH_PROPERTIES[ target.path ] ) { - - case PATH_PROPERTIES.weights: - - TypedKeyframeTrack = NumberKeyframeTrack; - break; - - case PATH_PROPERTIES.rotation: - - TypedKeyframeTrack = QuaternionKeyframeTrack; - break; - - case PATH_PROPERTIES.position: - case PATH_PROPERTIES.scale: - default: - - TypedKeyframeTrack = VectorKeyframeTrack; - break; - - } - - var targetName = node.name ? node.name : node.uuid; - - var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : InterpolateLinear; - - var targetNames = []; - - if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) { - - // Node may be a Group (glTF mesh with several primitives) or a Mesh. - node.traverse( function ( object ) { - - if ( object.isMesh === true && object.morphTargetInfluences ) { - - targetNames.push( object.name ? object.name : object.uuid ); - - } - - } ); - - } else { - - targetNames.push( targetName ); - - } - - var outputArray = outputAccessor.array; - - if ( outputAccessor.normalized ) { - - var scale; - - if ( outputArray.constructor === Int8Array ) { - - scale = 1 / 127; - - } else if ( outputArray.constructor === Uint8Array ) { - - scale = 1 / 255; - - } else if ( outputArray.constructor == Int16Array ) { - - scale = 1 / 32767; - - } else if ( outputArray.constructor === Uint16Array ) { - - scale = 1 / 65535; - - } else { - - throw new Error( 'THREE.GLTFLoader: Unsupported output accessor component type.' ); - - } - - var scaled = new Float32Array( outputArray.length ); - - for ( var j = 0, jl = outputArray.length; j < jl; j ++ ) { - - scaled[ j ] = outputArray[ j ] * scale; - - } - - outputArray = scaled; - - } - - for ( var j = 0, jl = targetNames.length; j < jl; j ++ ) { - - var track = new TypedKeyframeTrack( - targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ], - inputAccessor.array, - outputArray, - interpolation - ); - - // Override interpolation with custom factory method. - if ( sampler.interpolation === 'CUBICSPLINE' ) { - - track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) { - - // A CUBICSPLINE keyframe in glTF has three output values for each input value, - // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() - // must be divided by three to get the interpolant's sampleSize argument. - - return new GLTFCubicSplineInterpolant( this.times, this.values, this.getValueSize() / 3, result ); - - }; - - // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants. - track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true; - - } - - tracks.push( track ); - - } - - } - - var name = animationDef.name ? animationDef.name : 'animation_' + animationIndex; - - return new AnimationClip( name, undefined, tracks ); - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy - * @param {number} nodeIndex - * @return {Promise} - */ - GLTFParser.prototype.loadNode = function ( nodeIndex ) { - - var json = this.json; - var extensions = this.extensions; - var parser = this; - - var nodeDef = json.nodes[ nodeIndex ]; - - return ( function () { - - var pending = []; - - if ( nodeDef.mesh !== undefined ) { - - pending.push( parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) { - - var node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh ); - - // if weights are provided on the node, override weights on the mesh. - if ( nodeDef.weights !== undefined ) { - - node.traverse( function ( o ) { - - if ( ! o.isMesh ) return; - - for ( var i = 0, il = nodeDef.weights.length; i < il; i ++ ) { - - o.morphTargetInfluences[ i ] = nodeDef.weights[ i ]; - - } - - } ); - - } - - return node; - - } ) ); - - } - - if ( nodeDef.camera !== undefined ) { - - pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) { - - return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera ); - - } ) ); - - } - - if ( nodeDef.extensions - && nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ] - && nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light !== undefined ) { - - var lightIndex = nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light; - - pending.push( parser.getDependency( 'light', lightIndex ).then( function ( light ) { - - return parser._getNodeRef( parser.lightCache, lightIndex, light ); - - } ) ); - - } - - return Promise.all( pending ); - - }() ).then( function ( objects ) { - - var node; - - // .isBone isn't in glTF spec. See ._markDefs - if ( nodeDef.isBone === true ) { - - node = new Bone(); - - } else if ( objects.length > 1 ) { - - node = new Group(); - - } else if ( objects.length === 1 ) { - - node = objects[ 0 ]; - - } else { - - node = new Object3D(); - - } - - if ( node !== objects[ 0 ] ) { - - for ( var i = 0, il = objects.length; i < il; i ++ ) { - - node.add( objects[ i ] ); - - } - - } - - if ( nodeDef.name ) { - - node.userData.name = nodeDef.name; - node.name = PropertyBinding.sanitizeNodeName( nodeDef.name ); - - } - - assignExtrasToUserData( node, nodeDef ); - - if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); - - if ( nodeDef.matrix !== undefined ) { - - var matrix = new Matrix4(); - matrix.fromArray( nodeDef.matrix ); - node.applyMatrix4( matrix ); - - } else { - - if ( nodeDef.translation !== undefined ) { - - node.position.fromArray( nodeDef.translation ); - - } - - if ( nodeDef.rotation !== undefined ) { - - node.quaternion.fromArray( nodeDef.rotation ); - - } - - if ( nodeDef.scale !== undefined ) { - - node.scale.fromArray( nodeDef.scale ); - - } - - } - - parser.associations.set( node, { type: 'nodes', index: nodeIndex } ); - - return node; - - } ); - - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes - * @param {number} sceneIndex - * @return {Promise} - */ - GLTFParser.prototype.loadScene = function () { - - // scene node hierachy builder - - function buildNodeHierachy( nodeId, parentObject, json, parser ) { - - var nodeDef = json.nodes[ nodeId ]; - - return parser.getDependency( 'node', nodeId ).then( function ( node ) { - - if ( nodeDef.skin === undefined ) return node; - - // build skeleton here as well - - var skinEntry; - - return parser.getDependency( 'skin', nodeDef.skin ).then( function ( skin ) { - - skinEntry = skin; - - var pendingJoints = []; - - for ( var i = 0, il = skinEntry.joints.length; i < il; i ++ ) { - - pendingJoints.push( parser.getDependency( 'node', skinEntry.joints[ i ] ) ); - - } - - return Promise.all( pendingJoints ); - - } ).then( function ( jointNodes ) { - - node.traverse( function ( mesh ) { - - if ( ! mesh.isMesh ) return; - - var bones = []; - var boneInverses = []; - - for ( var j = 0, jl = jointNodes.length; j < jl; j ++ ) { - - var jointNode = jointNodes[ j ]; - - if ( jointNode ) { - - bones.push( jointNode ); - - var mat = new Matrix4(); - - if ( skinEntry.inverseBindMatrices !== undefined ) { - - mat.fromArray( skinEntry.inverseBindMatrices.array, j * 16 ); - - } - - boneInverses.push( mat ); - - } else { - - console.warn( 'THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[ j ] ); - - } - - } - - mesh.bind( new Skeleton( bones, boneInverses ), mesh.matrixWorld ); - - } ); - - return node; - - } ); - - } ).then( function ( node ) { - - // build node hierachy - - parentObject.add( node ); - - var pending = []; - - if ( nodeDef.children ) { - - var children = nodeDef.children; - - for ( var i = 0, il = children.length; i < il; i ++ ) { - - var child = children[ i ]; - pending.push( buildNodeHierachy( child, node, json, parser ) ); - - } - - } - - return Promise.all( pending ); - - } ); - - } - - return function loadScene( sceneIndex ) { - - var json = this.json; - var extensions = this.extensions; - var sceneDef = this.json.scenes[ sceneIndex ]; - var parser = this; - - // Loader returns Group, not Scene. - // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 - var scene = new Group(); - if ( sceneDef.name ) scene.name = sceneDef.name; - - assignExtrasToUserData( scene, sceneDef ); - - if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); - - var nodeIds = sceneDef.nodes || []; - - var pending = []; - - for ( var i = 0, il = nodeIds.length; i < il; i ++ ) { - - pending.push( buildNodeHierachy( nodeIds[ i ], scene, json, parser ) ); - - } - - return Promise.all( pending ).then( function () { - - return scene; - - } ); - - }; - - }(); - - return GLTFLoader; - - } )(); - - var DRACOLoader = function ( manager ) { - - Loader.call( this, manager ); - - this.decoderPath = ''; - this.decoderConfig = {}; - this.decoderBinary = null; - this.decoderPending = null; - - this.workerLimit = 4; - this.workerPool = []; - this.workerNextTaskID = 1; - this.workerSourceURL = ''; - - this.defaultAttributeIDs = { - position: 'POSITION', - normal: 'NORMAL', - color: 'COLOR', - uv: 'TEX_COORD' - }; - this.defaultAttributeTypes = { - position: 'Float32Array', - normal: 'Float32Array', - color: 'Float32Array', - uv: 'Float32Array' - }; - - }; - - DRACOLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: DRACOLoader, - - setDecoderPath: function ( path ) { - - this.decoderPath = path; - - return this; - - }, - - setDecoderConfig: function ( config ) { - - this.decoderConfig = config; - - return this; - - }, - - setWorkerLimit: function ( workerLimit ) { - - this.workerLimit = workerLimit; - - return this; - - }, - - /** @deprecated */ - setVerbosity: function () { - - console.warn( 'THREE.DRACOLoader: The .setVerbosity() method has been removed.' ); - - }, - - /** @deprecated */ - setDrawMode: function () { - - console.warn( 'THREE.DRACOLoader: The .setDrawMode() method has been removed.' ); - - }, - - /** @deprecated */ - setSkipDequantization: function () { - - console.warn( 'THREE.DRACOLoader: The .setSkipDequantization() method has been removed.' ); - - }, - - load: function ( url, onLoad, onProgress, onError ) { - - var loader = new FileLoader( this.manager ); - - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - - if ( this.crossOrigin === 'use-credentials' ) { - - loader.setWithCredentials( true ); - - } - - loader.load( url, ( buffer ) => { - - var taskConfig = { - attributeIDs: this.defaultAttributeIDs, - attributeTypes: this.defaultAttributeTypes, - useUniqueIDs: false - }; - - this.decodeGeometry( buffer, taskConfig ) - .then( onLoad ) - .catch( onError ); - - }, onProgress, onError ); - - }, - - /** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */ - decodeDracoFile: function ( buffer, callback, attributeIDs, attributeTypes ) { - - var taskConfig = { - attributeIDs: attributeIDs || this.defaultAttributeIDs, - attributeTypes: attributeTypes || this.defaultAttributeTypes, - useUniqueIDs: !! attributeIDs - }; - - this.decodeGeometry( buffer, taskConfig ).then( callback ); - - }, - - decodeGeometry: function ( buffer, taskConfig ) { - - // TODO: For backward-compatibility, support 'attributeTypes' objects containing - // references (rather than names) to typed array constructors. These must be - // serialized before sending them to the worker. - for ( var attribute in taskConfig.attributeTypes ) { - - var type = taskConfig.attributeTypes[ attribute ]; - - if ( type.BYTES_PER_ELEMENT !== undefined ) { - - taskConfig.attributeTypes[ attribute ] = type.name; - - } - - } - - // - - var taskKey = JSON.stringify( taskConfig ); - - // Check for an existing task using this buffer. A transferred buffer cannot be transferred - // again from this thread. - if ( DRACOLoader.taskCache.has( buffer ) ) { - - var cachedTask = DRACOLoader.taskCache.get( buffer ); - - if ( cachedTask.key === taskKey ) { - - return cachedTask.promise; - - } else if ( buffer.byteLength === 0 ) { - - // Technically, it would be possible to wait for the previous task to complete, - // transfer the buffer back, and decode again with the second configuration. That - // is complex, and I don't know of any reason to decode a Draco buffer twice in - // different ways, so this is left unimplemented. - throw new Error( - - 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + - 'settings. Buffer has already been transferred.' - - ); - - } - - } - - // - - var worker; - var taskID = this.workerNextTaskID ++; - var taskCost = buffer.byteLength; - - // Obtain a worker and assign a task, and construct a geometry instance - // when the task completes. - var geometryPending = this._getWorker( taskID, taskCost ) - .then( ( _worker ) => { - - worker = _worker; - - return new Promise( ( resolve, reject ) => { - - worker._callbacks[ taskID ] = { resolve, reject }; - - worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] ); - - // this.debug(); - - } ); - - } ) - .then( ( message ) => this._createGeometry( message.geometry ) ); - - // Remove task from the task list. - // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416) - geometryPending - .catch( () => true ) - .then( () => { - - if ( worker && taskID ) { - - this._releaseTask( worker, taskID ); - - // this.debug(); - - } - - } ); - - // Cache the task result. - DRACOLoader.taskCache.set( buffer, { - - key: taskKey, - promise: geometryPending - - } ); - - return geometryPending; - - }, - - _createGeometry: function ( geometryData ) { - - var geometry = new BufferGeometry(); - - if ( geometryData.index ) { - - geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) ); - - } - - for ( var i = 0; i < geometryData.attributes.length; i ++ ) { - - var attribute = geometryData.attributes[ i ]; - var name = attribute.name; - var array = attribute.array; - var itemSize = attribute.itemSize; - - geometry.setAttribute( name, new BufferAttribute( array, itemSize ) ); - - } - - return geometry; - - }, - - _loadLibrary: function ( url, responseType ) { - - var loader = new FileLoader( this.manager ); - loader.setPath( this.decoderPath ); - loader.setResponseType( responseType ); - - return new Promise( ( resolve, reject ) => { - - loader.load( url, resolve, undefined, reject ); - - } ); - - }, - - preload: function () { - - this._initDecoder(); - - return this; - - }, - - _initDecoder: function () { - - if ( this.decoderPending ) return this.decoderPending; - - var useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js'; - var librariesPending = []; - - if ( useJS ) { - - librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) ); - - } else { - - librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) ); - librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) ); - - } - - this.decoderPending = Promise.all( librariesPending ) - .then( ( libraries ) => { - - var jsContent = libraries[ 0 ]; - - if ( ! useJS ) { - - this.decoderConfig.wasmBinary = libraries[ 1 ]; - - } - - var fn = DRACOLoader.DRACOWorker.toString(); - - var body = [ - '/* draco decoder */', - jsContent, - '', - '/* worker */', - fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) - ].join( '\n' ); - - this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); - - } ); - - return this.decoderPending; - - }, - - _getWorker: function ( taskID, taskCost ) { - - return this._initDecoder().then( () => { - - if ( this.workerPool.length < this.workerLimit ) { - - var worker = new Worker( this.workerSourceURL ); - - worker._callbacks = {}; - worker._taskCosts = {}; - worker._taskLoad = 0; - - worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } ); - - worker.onmessage = function ( e ) { - - var message = e.data; - - switch ( message.type ) { - - case 'decode': - worker._callbacks[ message.id ].resolve( message ); - break; - - case 'error': - worker._callbacks[ message.id ].reject( message ); - break; - - default: - console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' ); - - } - - }; - - this.workerPool.push( worker ); - - } else { - - this.workerPool.sort( function ( a, b ) { - - return a._taskLoad > b._taskLoad ? - 1 : 1; - - } ); - - } - - var worker = this.workerPool[ this.workerPool.length - 1 ]; - worker._taskCosts[ taskID ] = taskCost; - worker._taskLoad += taskCost; - return worker; - - } ); - - }, - - _releaseTask: function ( worker, taskID ) { - - worker._taskLoad -= worker._taskCosts[ taskID ]; - delete worker._callbacks[ taskID ]; - delete worker._taskCosts[ taskID ]; - - }, - - debug: function () { - - console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) ); - - }, - - dispose: function () { - - for ( var i = 0; i < this.workerPool.length; ++ i ) { - - this.workerPool[ i ].terminate(); - - } - - this.workerPool.length = 0; - - return this; - - } - - } ); - - /* WEB WORKER */ - - DRACOLoader.DRACOWorker = function () { - - var decoderConfig; - var decoderPending; - - onmessage = function ( e ) { - - var message = e.data; - - switch ( message.type ) { - - case 'init': - decoderConfig = message.decoderConfig; - decoderPending = new Promise( function ( resolve/*, reject*/ ) { - - decoderConfig.onModuleLoaded = function ( draco ) { - - // Module is Promise-like. Wrap before resolving to avoid loop. - resolve( { draco: draco } ); - - }; - - DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef - - } ); - break; - - case 'decode': - var buffer = message.buffer; - var taskConfig = message.taskConfig; - decoderPending.then( ( module ) => { - - var draco = module.draco; - var decoder = new draco.Decoder(); - var decoderBuffer = new draco.DecoderBuffer(); - decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength ); - - try { - - var geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig ); - - var buffers = geometry.attributes.map( ( attr ) => attr.array.buffer ); - - if ( geometry.index ) buffers.push( geometry.index.array.buffer ); - - self.postMessage( { type: 'decode', id: message.id, geometry }, buffers ); - - } catch ( error ) { - - console.error( error ); - - self.postMessage( { type: 'error', id: message.id, error: error.message } ); - - } finally { - - draco.destroy( decoderBuffer ); - draco.destroy( decoder ); - - } - - } ); - break; - - } - - }; - - function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) { - - var attributeIDs = taskConfig.attributeIDs; - var attributeTypes = taskConfig.attributeTypes; - - var dracoGeometry; - var decodingStatus; - - var geometryType = decoder.GetEncodedGeometryType( decoderBuffer ); - - if ( geometryType === draco.TRIANGULAR_MESH ) { - - dracoGeometry = new draco.Mesh(); - decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry ); - - } else if ( geometryType === draco.POINT_CLOUD ) { - - dracoGeometry = new draco.PointCloud(); - decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry ); - - } else { - - throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' ); - - } - - if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) { - - throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() ); - - } - - var geometry = { index: null, attributes: [] }; - - // Gather all vertex attributes. - for ( var attributeName in attributeIDs ) { - - var attributeType = self[ attributeTypes[ attributeName ] ]; - - var attribute; - var attributeID; - - // A Draco file may be created with default vertex attributes, whose attribute IDs - // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively, - // a Draco file may contain a custom set of attributes, identified by known unique - // IDs. glTF files always do the latter, and `.drc` files typically do the former. - if ( taskConfig.useUniqueIDs ) { - - attributeID = attributeIDs[ attributeName ]; - attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID ); - - } else { - - attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] ); - - if ( attributeID === - 1 ) continue; - - attribute = decoder.GetAttribute( dracoGeometry, attributeID ); - - } - - geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) ); - - } - - // Add index. - if ( geometryType === draco.TRIANGULAR_MESH ) { - - // Generate mesh faces. - var numFaces = dracoGeometry.num_faces(); - var numIndices = numFaces * 3; - var index = new Uint32Array( numIndices ); - var indexArray = new draco.DracoInt32Array(); - - for ( var i = 0; i < numFaces; ++ i ) { - - decoder.GetFaceFromMesh( dracoGeometry, i, indexArray ); - - for ( var j = 0; j < 3; ++ j ) { - - index[ i * 3 + j ] = indexArray.GetValue( j ); - - } - - } - - geometry.index = { array: index, itemSize: 1 }; - - draco.destroy( indexArray ); - - } - - draco.destroy( dracoGeometry ); - - return geometry; - - } - - function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) { - - var numComponents = attribute.num_components(); - var numPoints = dracoGeometry.num_points(); - var numValues = numPoints * numComponents; - var dracoArray; - - var array; - - switch ( attributeType ) { - - case Float32Array: - dracoArray = new draco.DracoFloat32Array(); - decoder.GetAttributeFloatForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Float32Array( numValues ); - break; - - case Int8Array: - dracoArray = new draco.DracoInt8Array(); - decoder.GetAttributeInt8ForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Int8Array( numValues ); - break; - - case Int16Array: - dracoArray = new draco.DracoInt16Array(); - decoder.GetAttributeInt16ForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Int16Array( numValues ); - break; - - case Int32Array: - dracoArray = new draco.DracoInt32Array(); - decoder.GetAttributeInt32ForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Int32Array( numValues ); - break; - - case Uint8Array: - dracoArray = new draco.DracoUInt8Array(); - decoder.GetAttributeUInt8ForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Uint8Array( numValues ); - break; - - case Uint16Array: - dracoArray = new draco.DracoUInt16Array(); - decoder.GetAttributeUInt16ForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Uint16Array( numValues ); - break; - - case Uint32Array: - dracoArray = new draco.DracoUInt32Array(); - decoder.GetAttributeUInt32ForAllPoints( dracoGeometry, attribute, dracoArray ); - array = new Uint32Array( numValues ); - break; - - default: - throw new Error( 'THREE.DRACOLoader: Unexpected attribute type.' ); - - } - - for ( var i = 0; i < numValues; i ++ ) { - - array[ i ] = dracoArray.GetValue( i ); - - } - - draco.destroy( dracoArray ); - - return { - name: attributeName, - array: array, - itemSize: numComponents - }; - - } - - }; - - DRACOLoader.taskCache = new WeakMap(); - - /** Deprecated static methods */ - - /** @deprecated */ - DRACOLoader.setDecoderPath = function () { - - console.warn( 'THREE.DRACOLoader: The .setDecoderPath() method has been removed. Use instance methods.' ); - - }; - - /** @deprecated */ - DRACOLoader.setDecoderConfig = function () { - - console.warn( 'THREE.DRACOLoader: The .setDecoderConfig() method has been removed. Use instance methods.' ); - - }; - - /** @deprecated */ - DRACOLoader.releaseDecoderModule = function () { - - console.warn( 'THREE.DRACOLoader: The .releaseDecoderModule() method has been removed. Use instance methods.' ); - - }; - - /** @deprecated */ - DRACOLoader.getDecoderModule = function () { - - console.warn( 'THREE.DRACOLoader: The .getDecoderModule() method has been removed. Use instance methods.' ); - - }; - - /** - * @author Don McCurdy / https://www.donmccurdy.com - */ - - let init, instance, heap; - - const importObject = { - - env: { - - emscripten_notify_memory_growth: function ( index ) { - - heap = new Uint8Array( instance.exports.memory.buffer ); - - } - - } - - }; - - /** - * ZSTD (Zstandard) decoder. - * - * Compiled from https://github.com/facebook/zstd/tree/dev/contrib/single_file_libs, with the - * following steps: - * - * ``` - * ./combine.sh -r ../../lib -o zstddeclib.c zstddeclib-in.c - * emcc zstddeclib.c -Oz -s EXPORTED_FUNCTIONS="['_ZSTD_decompress', '_ZSTD_findDecompressedSize', '_ZSTD_isError', '_malloc', '_free']" -s ALLOW_MEMORY_GROWTH=1 -s MALLOC=emmalloc -o zstddec.wasm - * base64 zstddec.wasm > zstddec.txt - * ``` - * - * The base64 string written to `zstddec.txt` is embedded as the `wasm` variable at the bottom - * of this file. The rest of this file is written by hand, in order to avoid an additional JS - * wrapper generated by Emscripten. - */ - class ZSTDDecoder { - - init () { - - if ( ! init ) { - - init = fetch( 'data:application/wasm;base64,' + wasm ) - .then( ( response ) => response.arrayBuffer() ) - .then( ( arrayBuffer ) => WebAssembly.instantiate( arrayBuffer, importObject ) ) - .then( ( result ) => { - - instance = result.instance; - - importObject.env.emscripten_notify_memory_growth( 0 ); // initialize heap. - - }); - - } - - return init; - - } - - decode ( array, uncompressedSize = 0 ) { - - // Write compressed data into WASM memory. - const compressedSize = array.byteLength; - const compressedPtr = instance.exports.malloc( compressedSize ); - heap.set( array, compressedPtr ); - - // Decompress into WASM memory. - uncompressedSize = uncompressedSize || Number( instance.exports.ZSTD_findDecompressedSize( compressedPtr, compressedSize ) ); - const uncompressedPtr = instance.exports.malloc( uncompressedSize ); - const actualSize = instance.exports.ZSTD_decompress( uncompressedPtr, uncompressedSize, compressedPtr, compressedSize ); - - // Read decompressed data and free WASM memory. - const dec = heap.slice( uncompressedPtr, uncompressedPtr + actualSize ); - instance.exports.free( compressedPtr ); - instance.exports.free( uncompressedPtr ); - - return dec; - - } - - } - - /** - * BSD License - * - * For Zstandard software - * - * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name Facebook nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - const wasm = '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IAMNACAEQQNxBEADQCACRQ0FIAAgAkF/aiICaiIDIAEgAmotAAA6AAAgA0EDcQ0ACwsgAkEDTQ0AA0AgACACQXxqIgJqIAEgAmooAgA2AgAgAkEDSw0ACwsgAkUNAgNAIAAgAkF/aiICaiABIAJqLQAAOgAAIAINAAsMAgsgAkEDTQ0AIAIhBANAIAMgASgCADYCACABQQRqIQEgA0EEaiEDIARBfGoiBEEDSw0ACyACQQNxIQILIAJFDQADQCADIAEtAAA6AAAgA0EBaiEDIAFBAWohASACQX9qIgINAAsLIAAL8wICAn8BfgJAIAJFDQAgACACaiIDQX9qIAE6AAAgACABOgAAIAJBA0kNACADQX5qIAE6AAAgACABOgABIANBfWogAToAACAAIAE6AAIgAkEHSQ0AIANBfGogAToAACAAIAE6AAMgAkEJSQ0AIABBACAAa0EDcSIEaiIDIAFB/wFxQYGChAhsIgE2AgAgAyACIARrQXxxIgRqIgJBfGogATYCACAEQQlJDQAgAyABNgIIIAMgATYCBCACQXhqIAE2AgAgAkF0aiABNgIAIARBGUkNACADIAE2AhggAyABNgIUIAMgATYCECADIAE2AgwgAkFwaiABNgIAIAJBbGogATYCACACQWhqIAE2AgAgAkFkaiABNgIAIAQgA0EEcUEYciIEayICQSBJDQAgAa0iBUIghiAFhCEFIAMgBGohAQNAIAEgBTcDGCABIAU3AxAgASAFNwMIIAEgBTcDACABQSBqIQEgAkFgaiICQR9LDQALCyAACy8BAn8gACgCBCAAKAIAQQJ0aiICLQACIQMgACACLwEAIAEgAi0AAxAIajYCACADCy8BAn8gACgCBCAAKAIAQQJ0aiICLQACIQMgACACLwEAIAEgAi0AAxAFajYCACADCx8AIAAgASACKAIEEAg2AgAgARAEGiAAIAJBCGo2AgQLCAAgAGdBH3MLugUBDX8jAEEQayIKJAACfyAEQQNNBEAgCkEANgIMIApBDGogAyAEEAsaIAAgASACIApBDGpBBBAVIgBBbCAAEAMbIAAgACAESxsMAQsgAEEAIAEoAgBBAXRBAmoQECENQVQgAygAACIGQQ9xIgBBCksNABogAiAAQQVqNgIAIAMgBGoiAkF8aiEMIAJBeWohDiACQXtqIRAgAEEGaiELQQQh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ASIDNgLE4AEgACgCvOABIQQgACABNgK84AEgACABIAJqNgK44AEgACABIAQgA2tqNgLA4AELpgEBAX8gACAAKALs4QEQFjYCyOABIABCADcD+OABIABCADcDuOABIABBwOABakIANwMAIABBqNAAaiIBQYyAgOAANgIAIABBADYCmOIBIABCADcDiOEBIABCAzcDgOEBIABBrNABakHgEikCADcCACAAQbTQAWpB6BIoAgA2AgAgACABNgIMIAAgAEGYIGo2AgggACAAQaAwajYCBCAAIABBEGo2AgALYQEBf0G4fyEDAkAgAUEDSQ0AIAIgABAhIgFBA3YiADYCCCACIAFBAXE2AgQgAiABQQF2QQNxIgM2AgACQCADQX9qIgFBAksNAAJAIAFBAWsOAgEAAgtBbA8LIAAhAwsgAwsMACAAIAEgAkEAEC4LiAQCA38CfiADEBYhBCAAQQBBKBAQIQAgBCACSwRAIAQPCyABRQRAQX8PCwJAAkAgA0EBRg0AIAEoAAAiBkGo6r5pRg0AQXYhAyAGQXBxQdDUtMIBRw0BQQghAyACQQhJDQEgAEEAQSgQECEAIAEoAAQhASAAQQE2AhQgACABrTcDAEEADwsgASACIAMQLyIDIAJLDQAgACADNgIYQXIhAyABIARqIgVBf2otAAAiAkEIcQ0AIAJBIHEiBkUEQEFwIQMgBS0AACIFQacBSw0BIAVBB3GtQgEgBUEDdkEKaq2GIgdCA4h+IAd8IQggBEEBaiEECyACQQZ2IQMgAkECdiEFAkAgAkEDcUF/aiICQQJLBEBBACECDAELAkACQAJAIAJBAWsOAgECAAsgASAEai0AACECIARBAWohBAwCCyABIARqLwAAIQIgBEECaiEEDAELIAEgBGooAAAhAiAEQQRqIQQLIAVBAXEhBQJ+AkACQAJAIANBf2oiA0ECTQRAIANBAWsOAgIDAQtCfyAGRQ0DGiABIARqMQAADAMLIAEgBGovAACtQoACfAwCCyABIARqKAAArQwBCyABIARqKQAACyEHIAAgBTYCICAAIAI2AhwgACAHNwMAQQAhAyAAQQA2AhQgACAHIAggBhsiBzcDCCAAIAdCgIAIIAdCgIAIVBs+AhALIAMLWwEBf0G4fyEDIAIQFiICIAFNBH8gACACakF/ai0AACIAQQNxQQJ0QaAeaigC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C0Egak8NAQsgByAHKQNoNwMQIAcgBykDYDcDCCAEIA0gB0EIaiAHQdwAaiAPIA4gESAQEB4hCwwBCyACIARqIQggBCAKEAcgAkERTwRAIARBEGohAgNAIAIgCkEQaiIKEAcgAkEQaiICIAhJDQALCyAIIAlrIQIgByATNgJcIAkgCCAOa0sEQCAJIAggEWtLBEBBbCELDAILIBAgAiAOayICaiIKIANqIBBNBEAgCCAKIAMQDxoMAgsgCCAKQQAgAmsQDyEIIAcgAiADaiIDNgJkIAggAmshCCAOIQILIAlBEE8EQCADIAhqIQMDQCAIIAIQByACQRBqIQIgCEEQaiIIIANJDQALDAELAkAgCUEHTQRAIAggAi0AADoAACAIIAItAAE6AAEgCCACLQACOgACIAggAi0AAzoAAyAIQQRqIAIgCUECdCIDQcAeaigCAGoiAhAXIAIgA0HgHmooAgBrIQIgBygCZCEDDAELIAggAhAMCyADQQlJDQAgAyAIaiEDIAhBCGoiCCACQQhqIgJrQQ9MBEADQCAIIAIQDCACQQhqIQIgCEEIaiIIIANJDQAMAgALAAsDQCAIIAIQByACQRBqIQIgCEEQaiIIIANJDQALCyAHQRhqEAQaIAsgDCALEAMiAhshDCAEIAQgC2ogAhshBCAFQX9qIgUNAAsgDBADDQFBbCEMIAdBGGoQBEECSQ0BQQAhCANAIAhBA0cEQCAAIAhBAnQiAmpBrNABaiACIAdqKAJENgIAIAhBAWohCAwBCwsgBygCXCEIC0G6fyEMIA8gCGsiACANIARrSw0AIAQEfyAEIAggABALIABqBUEACyABayEMCyAHQfAAaiQAIAwLkRcCFn8FfiMAQdABayIHJAAgByAAKALw4QEiCDYCvAEgASACaiESIAggACgCgOIBaiETAkACQCAFRQRAIAEhAwwBCyAAKALE4AEhESAAKALA4AEhFSAAKAK84AEhDyAAQQE2AozhAUEAIQgDQCAIQQNHBEAgByAIQQJ0IgJqIAAgAmpBrNABaigCADYCVCAIQQFqIQgMAQsLIAcgETYCZCAHIA82AmAgByABIA9rNgJoQWwhECAHQShqIAMgBBAGEAMNASAFQQQgBUEESBshFyAHQTxqIAdBKGogACgCABATIAdBxABqIAdBKGogACgCCBATIAdBzABqIAdB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IAdB0AFqJAAgEAslACAAQgA3AgAgAEEAOwEIIABBADoACyAAIAE2AgwgACACOgAKC7QFAQN/IwBBMGsiBCQAIABB/wFqIgVBfWohBgJAIAMvAQIEQCAEQRhqIAEgAhAGIgIQAw0BIARBEGogBEEYaiADEBwgBEEIaiAEQRhqIAMQHCAAIQMDQAJAIARBGGoQBCADIAZPckUEQCADIARBEGogBEEYahASOgAAIAMgBEEIaiAEQRhqEBI6AAEgBEEYahAERQ0BIANBAmohAwsgBUF+aiEFAn8DQEG6fyECIAMiASAFSw0FIAEgBEEQaiAEQRhqEBI6AAAgAUEBaiEDIARBGGoQBEEDRgRAQQIhAiAEQQhqDAILIAMgBUsNBSABIARBCGogBEEYahASOgABIAFBAmohA0EDIQIgBEEYahAEQQNHDQALIARBEGoLIQUgAyAFIARBGGoQEjoAACABIAJqIABrIQIMAwsgAyAEQRBqIARBGGoQEjoAAiADIARBCGogBEEYahASOgADIANBBGohAwwAAAsACyAEQRhqIAEgAhAGIgIQAw0AIARBEGogBEEYaiADEBwgBEEIaiAEQRhqIAMQHCAAIQMDQAJAIARBGGoQBCADIAZPckUEQCADIARBEGogBEEYahAROgAAIAMgBEEIaiAEQRhqEBE6AAEgBEEYahAERQ0BIANBAmohAwsgBUF+aiEFAn8DQEG6fyECIAMiASAFSw0EIAEgBEEQaiAEQRhqEBE6AAAgAUEBaiEDIARBGGoQBEEDRgRAQQIhAiAEQQhqDAILIAMgBUsNBCABIARBCGogBEEYahAROgABIAFBAmohA0EDIQIgBEEYahAEQQNHDQALIARBEGoLIQUgAyAFIARBGGoQEToAACABIAJqIABrIQIMAgsgAyAEQRBqIARBGGoQEToAAiADIARBCGogBEEYahAROgADIANBBGohAwwAAAsACyAEQTBqJAAgAgtpAQF/An8CQAJAIAJBB00NACABKAAAQbfIwuF+Rw0AIAAgASgABDYCmOIBQWIgAEEQaiABIAIQPiIDEAMNAhogAEKBgICAEDcDiOEBIAAgASADaiACIANrECoMAQsgACABIAIQKgtBAAsLrQMBBn8jAEGAAWsiAyQAQWIhCAJAIAJBCUkNACAAQZjQAGogAUEIaiIEIAJBeGogAEGY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wOABCwutAgIEfwF+IwBBQGoiBCQAAkACQCACQQhJDQAgASgAAEFwcUHQ1LTCAUcNACABIAIQIiEBIABCADcDCCAAQQA2AgQgACABNgIADAELIARBGGogASACEC0iAxADBEAgACADEBoMAQsgAwRAIABBuH8QGgwBCyACIAQoAjAiA2shAiABIANqIQMDQAJAIAAgAyACIARBCGoQLCIFEAMEfyAFBSACIAVBA2oiBU8NAUG4fwsQGgwCCyAGQQFqIQYgAiAFayECIAMgBWohAyAEKAIMRQ0ACyAEKAI4BEAgAkEDTQRAIABBuH8QGgwCCyADQQRqIQMLIAQoAighAiAEKQMYIQcgAEEANgIEIAAgAyABazYCACAAIAIgBmytIAcgB0J/URs3AwgLIARBQGskAAslAQF/IwBBEGsiAiQAIAIgACABEFEgAigCACEAIAJBEGokACAAC30BBH8jAEGQBGsiBCQAIARB/wE2AggCQCAEQRBqIARBCGogBEEMaiABIAIQFSIGEAMEQCAGIQUMAQtBVCEFIAQoAgwiB0EGSw0AIAMgBEEQaiAEKAIIIAcQQSIFEAMNACAAIAEgBmogAiAGayADEDwhBQsgBEGQBGokACAFC4cBAgJ/An5BABAWIQMCQANAIAEgA08EQAJAIAAoAABBcHFB0NS0wgFGBEAgACABECIiAhADRQ0BQn4PCyAAIAEQVSIEQn1WDQMgBCAFfCIFIARUIQJCfiEEIAINAyAAIAEQUiICEAMNAwsgASACayEBIAAgAmohAAwBCwtCfiAFIAEbIQQLIAQLPwIBfwF+IwBBMGsiAiQAAn5CfiACQQhqIAAgARAtDQAaQgAgAigCHEEBRg0AGiACKQMICyEDIAJBMGokACADC40BAQJ/IwBBMGsiASQAAkAgAEUNACAAKAKI4gENACABIABB/OEBaigCADYCKCABIAApAvThATcDICAAEDAgACgCqOIBIQIgASABKAIoNgIYIAEgASkDIDcDECACIAFBEGoQGyAAQQA2AqjiASABIAEoAig2AgggASABKQMgNwMAIAAgARAbCyABQTBqJAALKgECfyMAQRBrIgAkACAAQQA2AgggAEIANwMAIAAQWCEBIABBEGokACABC4cBAQN/IwBBEGsiAiQAAkAgACgCAEUgACgCBEVzDQAgAiAAKAIINgIIIAIgACkCADcDAAJ/IAIoAgAiAQRAIAIoAghBqOMJIAERBQAMAQtBqOMJECgLIgFFDQAgASAAKQIANwL04QEgAUH84QFqIAAoAgg2AgAgARBZIAEhAwsgAkEQaiQAIAMLywEBAn8jAEEgayIBJAAgAEGBgIDAADYCtOIBIABBADYCiOIBIABBADYC7OEBIABCADcDkOIBIABBADYCpOMJIABBADYC3OIBIABCADcCzOIBIABBADYCvOIBIABBADYCxOABIABCADcCnOIBIABBpOIBakIANwIAIABBrOIBakEANgIAIAFCADcCECABQgA3AhggASABKQMYNwMIIAEgASkDEDcDACABKAIIQQh2QQFxIQIgAEEANgLg4gEgACACNgKM4gEgAUEgaiQAC3YBA38jAEEwayIBJAAgAARAIAEgAEHE0AFqIgIoAgA2AiggASAAKQK80AE3AyAgACgCACEDIAEgAigCADYCGCABIAApArzQATcDECADIAFBEGoQGyABIAEoAig2AgggASABKQMgNwMAIAAgARAbCyABQTBqJAALzAEBAX8gACABKAK00AE2ApjiASAAIAEoAgQiAjYCwOABIAAgAjYCvOABIAAgAiABKAIIaiICNgK44AEgACACNgLE4AEgASgCuNABBEAgAEKBgICAEDcDiOEBIAAgAUGk0ABqNgIMIAAgAUGUIGo2AgggACABQZwwajYCBCAAIAFBDGo2AgAgAEGs0AFqIAFBqNABaigCADYCACAAQbDQAWogAUGs0AFqKAIANgIAIABBtNABaiABQbDQAWooAgA2AgAPCyAAQgA3A4jhAQs7ACACRQRAQbp/DwsgBEUEQEFsDwsgAiAEEGAEQCAAIAEgAiADIAQgBRBhDwsgACABIAIgAyAEIAUQZQtGAQF/IwBBEGsiBSQAIAVBCGogBBAOAn8gBS0ACQRAIAAgASACIAMgBBAyDAELIAAgASACIAMgBBA0CyEAIAVBEGokACAACzQAIAAgAyAEIAUQNiIFEAMEQCAFDwsgBSAESQR/IAEgAiADIAVqIAQgBWsgABA1BUG4fwsLRgEBfyMAQRBrIgUkACAFQQhqIAQQDgJ/IAUtAAkEQCAAIAEgAiADIAQQYgwBCyAAIAEgAiADIAQQNQshACAFQRBqJAAgAAtZAQF/QQ8hAiABIABJBEAgAUEEdCAAbiECCyAAQQh2IgEgAkEYbCIAQYwIaigCAGwgAEGICGooAgBqIgJBA3YgAmogAEGACGooAgAgAEGECGooAgAgAWxqSQs3ACAAIAMgBCAFQYAQEDMiBRADBEAgBQ8LIAUgBEkEfyABIAIgAyAFaiAEIAVrIAAQMgVBuH8LC78DAQN/IwBBIGsiBSQAIAVBCGogAiADEAYiAhADRQRAIAAgAWoiB0F9aiEGIAUgBBAOIARBBGohAiAFLQACIQMDQEEAIAAgBkkgBUEIahAEGwRAIAAgAiAFQQhqIAMQAkECdGoiBC8BADsAACAFQQhqIAQtAAIQASAAIAQtAANqIgQgAiAFQQhqIAMQAkECdGoiAC8BADsAACAFQQhqIAAtAAIQASAEIAAtAANqIQAMAQUgB0F+aiEEA0AgBUEIahAEIAAgBEtyRQRAIAAgAiAFQQhqIAMQAkECdGoiBi8BADsAACAFQQhqIAYtAAIQASAAIAYtAANqIQAMAQsLA0AgACAES0UEQCAAIAIgBUEIaiADEAJBAnRqIgYvAQA7AAAgBUEIaiAGLQACEAEgACAGLQADaiEADAELCwJAIAAgB08NACAAIAIgBUEIaiADEAIiA0ECdGoiAC0AADoAACAALQADQQFGBEAgBUEIaiAALQACEAEMAQsgBSgCDEEfSw0AIAVBCGogAiADQQJ0ai0AAhABIAUoAgxBIUkNACAFQSA2AgwLIAFBbCAFQQhqEAobIQILCwsgBUEgaiQAIAILkgIBBH8jAEFAaiIJJAAgCSADQTQQCyEDAkAgBEECSA0AIAMgBEECdGooAgAhCSADQTxqIAgQIyADQQE6AD8gAyACOgA+QQAhBCADKAI8IQoDQCAEIAlGDQEgACAEQQJ0aiAKNgEAIARBAWohBAwAAAsAC0EAIQkDQCAGIAlGRQRAIAMgBSAJQQF0aiIKLQABIgtBAnRqIgwoAgAhBCADQTxqIAotAABBCHQgCGpB//8DcRAjIANBAjoAPyADIAcgC2siCiACajoAPiAEQQEgASAKa3RqIQogAygCPCELA0AgACAEQQJ0aiALNgEAIARBAWoiBCAKSQ0ACyAMIAo2AgAgCUEBaiEJDAELCyADQUBrJAALowIBCX8jAEHQAGsiCSQAIAlBEGogBUE0EAsaIAcgBmshDyAHIAFrIRADQAJAIAMgCkcEQEEBIAEgByACIApBAXRqIgYtAAEiDGsiCGsiC3QhDSAGLQAAIQ4gCUEQaiAMQQJ0aiIMKAIAIQYgCyAPTwRAIAAgBkECdGogCyAIIAUgCEE0bGogCCAQaiIIQQEgCEEBShsiCCACIAQgCEECdGooAgAiCEEBdGogAyAIayAHIA4QYyAGIA1qIQgMAgsgCUEMaiAOECMgCUEBOgAPIAkgCDoADiAGIA1qIQggCSgCDCELA0AgBiAITw0CIAAgBkECdGogCzYBACAGQQFqIQYMAAALAAsgCUHQAGokAA8LIAwgCDYCACAKQQFqIQoMAAALAAs0ACAAIAMgBCAFEDYiBRADBEAgBQ8LIAUgBEkEfyABIAIgAyAFaiAEIAVrIAAQNAVBuH8LCyMAIAA/AEEQdGtB//8DakEQdkAAQX9GBEBBAA8LQQAQAEEBCzsBAX8gAgRAA0AgACABIAJBgCAgAkGAIEkbIgMQCyEAIAFBgCBqIQEgAEGAIGohACACIANrIgINAAsLCwYAIAAQAwsLqBUJAEGICAsNAQAAAAEAAAACAAAAAgBBoAgLswYBAAAAAQAAAAIAAAACAAAAJgAAAIIAAAAhBQAASgAAAGcIAAAmAAAAwAEAAIAAAABJBQAASgAAAL4IAAApAAAALAIAAIAAAABJBQAASgAAAL4IAAAvAAAAygIAAIAAAACKBQAASgAAAIQJAAA1AAAAcwMAAIAAAACdBQAASgAAAKAJAAA9AAAAgQMAAIAAAADrBQAASwAAAD4KAABEAAAAngMAAIAAAABNBgAASwAAAKoKAABLAAAAswMAAIAAAADBBgAATQAAAB8NAABNAAAAUwQAAIAAAAAjCAAAUQAAAKYPAABUAAAAmQQAAIAAAABLCQAAVwAAALESAABYAAAA2gQAAIAAAABvCQAAXQAAACMUAABUAAAARQUAAIAAAABUCgAAagAAAIwUAABqAAAArwUAAIAAAAB2CQAAfAAAAE4QAAB8AAAA0gIAAIAAAABjBwAAkQAAAJAHAACSAAAAAAAAAAEAAAABAAAABQAAAA0AAAAdAAAAPQAAAH0AAAD9AAAA/QEAAP0DAAD9BwAA/Q8AAP0fAAD9PwAA/X8AAP3/AAD9/wEA/f8DAP3/BwD9/w8A/f8fAP3/PwD9/38A/f//AP3//wH9//8D/f//B/3//w/9//8f/f//P/3//38AAAAAAQAAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAFgAAABcAAAAYAAAAGQAAABoAAAAbAAAAHAAAAB0AAAAeAAAAHwAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAAAB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACUAAAAnAAAAKQAAACsAAAAvAAAAMwAAADsAAABDAAAAUwAAAGMAAACDAAAAAwEAAAMCAAADBAAAAwgAAAMQAAADIAAAA0AAAAOAAAADAAEAQeAPC1EBAAAAAQAAAAEAAAABAAAAAgAAAAIAAAADAAAAAwAAAAQAAAAEAAAABQAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAOAAAADwAAABAAQcQQC4sBAQAAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAAEAAAABIAAAAUAAAAFgAAABgAAAAcAAAAIAAAACgAAAAwAAAAQAAAAIAAAAAAAQAAAAIAAAAEAAAACAAAABAAAAAgAAAAQAAAAIAAAAAAAQBBkBIL5gQBAAAAAQAAAAEAAAABAAAAAgAAAAIAAAADAAAAAwAAAAQAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAAEAAAAAEAAAAEAAAACAAAAAAAAAABAAEBBgAAAAAAAAQAAAAAEAAABAAAAAAgAAAFAQAAAAAAAAUDAAAAAAAABQQAAAAAAAAFBgAAAAAAAAUHAAAAAAAABQkAAAAAAAAFCgAAAAAAAAUMAAAAAAAABg4AAAAAAAEFEAAAAAAAAQUUAAAAAAABBRYAAAAAAAIFHAAAAAAAAwUgAAAAAAAEBTAAAAAgAAYFQAAAAAAABwWAAAAAAAAIBgABAAAAAAoGAAQAAAAADAYAEAAAIAAABAAAAAAAAAAEAQAAAAAAAAUCAAAAIAAABQQAAAAAAAAFBQAAACAAAAUHAAAAAAAABQgAAAAgAAAFCgAAAAAAAAULAAAAAAAABg0AAAAgAAEFEAAAAAAAAQUSAAAAIAABBRYAAAAAAAIFGAAAACAAAwUgAAAAAAADBSgAAAAAAAYEQAAAABAABgRAAAAAIAAHBYAAAAAAAAkGAAIAAAAACwYACAAAMAAABAAAAAAQAAAEAQAAACAAAAUCAAAAIAAABQMAAAAgAAAFBQAAACAAAAUGAAAAIAAABQgAAAAgAAAFCQAAACAAAAULAAAAIAAABQwAAAAAAAAGDwAAACAAAQUSAAAAIAABBRQAAAAgAAIFGAAAACAAAgUcAAAAIAADBSgAAAAgAAQFMAAAAAAAEAYAAAEAAAAPBgCAAAAAAA4GAEAAAAAADQYAIABBgBcLhwIBAAEBBQAAAAAAAAUAAAAAAAAGBD0AAAAAAAkF/QEAAAAADwX9fwAAAAAVBf3/HwAAAAMFBQAAAAAABwR9AAAAAAAMBf0PAAAAABIF/f8DAAAAFwX9/38AAAAFBR0AAAAAAAgE/QAAAAAADgX9PwAAAAAUBf3/DwAAAAIFAQAAABAABwR9AAAAAAALBf0HAAAAABEF/f8BAAAAFgX9/z8AAAAEBQ0AAAAQAAgE/QAAAAAADQX9HwAAAAATBf3/BwAAAAEFAQAAABAABgQ9AAAAAAAKBf0DAAAAABAF/f8AAAAAHAX9//8PAAAbBf3//wcAABoF/f//AwAAGQX9//8BAAAYBf3//wBBkBkLhgQBAAEBBgAAAAAAAAYDAAAAAAAABAQAAAAgAAAFBQAAAAAAAAUGAAAAAAAABQgAAAAAAAAFCQAAAAAAAAULAAAAAAAABg0AAAAAAAAGEAAAAAAAAAYTAAAAAAAABhYAAAAAAAAGGQAAAAAAAAYcAAAAAAAABh8AAAAAAAAGIgAAAAAAAQYlAAAAAAABBikAAAAAAAIGLwAAAAAAAwY7AAAAAAAEBlMAAAAAAAcGgwAAAAAACQYDAgAAEAAABAQAAAAAAAAEBQAAACAAAAUGAAAAAAAABQcAAAAgAAAFCQAAAAAAAAUKAAAAAAAABgwAAAAAAAAGDwAAAAAAAAYSAAAAAAAABhUAAAAAAAAGGAAAAAAAAAYbAAAAAAAABh4AAAAAAAAGIQAAAAAAAQYjAAAAAAABBicAAAAAAAIGKwAAAAAAAwYzAAAAAAAEBkMAAAAAAAUGYwAAAAAACAYDAQAAIAAABAQAAAAwAAAEBAAAABAAAAQFAAAAIAAABQcAAAAgAAAFCAAAACAAAAUKAAAAIAAABQsAAAAAAAAGDgAAAAAAAAYRAAAAAAAABhQAAAAAAAAGFwAAAAAAAAYaAAAAAAAABh0AAAAAAAAGIAAAAAAAEAYDAAEAAAAPBgOAAAAAAA4GA0AAAAAADQYDIAAAAAAMBgMQAAAAAAsGAwgAAAAACgYDBABBpB0L2QEBAAAAAwAAAAcAAAAPAAAAHwAAAD8AAAB/AAAA/wAAAP8BAAD/AwAA/wcAAP8PAAD/HwAA/z8AAP9/AAD//wAA//8BAP//AwD//wcA//8PAP//HwD//z8A//9/AP///wD///8B////A////wf///8P////H////z////9/AAAAAAEAAAACAAAABAAAAAAAAAACAAAABAAAAAgAAAAAAAAAAQAAAAIAAAABAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAcAAAAIAAAACQAAAAoAAAALAEGgIAsDwBBQ'; - - /** - * References: - * - KTX: http://github.khronos.org/KTX-Specification/ - * - DFD: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#basicdescriptor - * - * To do: - * - [ ] Cross-platform testing - * - [ ] Specify JS/WASM transcoder path - * - [ ] High-quality demo - * - [ ] Documentation - * - [ ] TypeScript definitions - * - [ ] (Optional) Include BC5 - * - [ ] (Optional) Include EAC RG on mobile (WEBGL_compressed_texture_etc) - * - [ ] (Optional) Include two-texture output mode (see: clearcoat + clearcoatRoughness) - * - [ ] (Optional) Support Web Workers, after #18234 - */ - - // Data Format Descriptor (DFD) constants. - - const DFDModel = { - ETC1S: 163, - UASTC: 166, - }; - - const DFDChannel = { - ETC1S: { - RGB: 0, - RRR: 3, - GGG: 4, - AAA: 15, - }, - UASTC: { - RGB: 0, - RGBA: 3, - RRR: 4, - RRRG: 5 - }, - }; - - // - - class KTX2Loader extends CompressedTextureLoader { - - constructor( manager ) { - - super( manager ); - - this.basisModule = null; - this.basisModulePending = null; - - this.transcoderConfig = {}; - - } - - detectSupport( renderer ) { - - this.transcoderConfig = { - astcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_astc' ), - etc1Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc1' ), - etc2Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc' ), - dxtSupported: renderer.extensions.has( 'WEBGL_compressed_texture_s3tc' ), - bptcSupported: renderer.extensions.has( 'EXT_texture_compression_bptc' ), - pvrtcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_pvrtc' ) - || renderer.extensions.has( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ) - }; - - return this; - - } - - initModule() { - - if ( this.basisModulePending ) { - - return; - - } - - var scope = this; - - // The Emscripten wrapper returns a fake Promise, which can cause - // infinite recursion when mixed with native Promises. Wrap the module - // initialization to return a native Promise. - scope.basisModulePending = new Promise( function ( resolve ) { - - MSC_TRANSCODER().then( function ( basisModule ) { - - scope.basisModule = basisModule; - - basisModule.initTranscoders(); - - resolve(); - - } ); - - } ); - - } - - load( url, onLoad, onProgress, onError ) { - - var scope = this; - - var texture = new CompressedTexture(); - - var bufferPending = new Promise( function ( resolve, reject ) { - - new FileLoader( scope.manager ) - .setPath( scope.path ) - .setResponseType( 'arraybuffer' ) - .load( url, resolve, onProgress, reject ); - - } ); - - this.initModule(); - - Promise.all( [ bufferPending, this.basisModulePending ] ).then( function ( [ buffer ] ) { - - scope.parse( buffer, function ( _texture ) { - - texture.copy( _texture ); - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - }, onError ); - - } ); - - return texture; - - } - - parse( buffer, onLoad, onError ) { - - var BasisLzEtc1sImageTranscoder = this.basisModule.BasisLzEtc1sImageTranscoder; - var UastcImageTranscoder = this.basisModule.UastcImageTranscoder; - var TextureFormat = this.basisModule.TextureFormat; - - var ktx = new KTX2Container( this.basisModule, buffer ); - - // TODO(donmccurdy): Should test if texture is transcodable before attempting - // any transcoding. If supercompressionScheme is KTX_SS_BASIS_LZ and dfd - // colorModel is ETC1S (163) or if dfd colorModel is UASTCF (166) - // then texture must be transcoded. - var transcoder = ktx.getTexFormat() === TextureFormat.UASTC4x4 - ? new UastcImageTranscoder() - : new BasisLzEtc1sImageTranscoder(); - - ktx.initMipmaps( transcoder, this.transcoderConfig ) - .then( function () { - - var texture = new CompressedTexture( - ktx.mipmaps, - ktx.getWidth(), - ktx.getHeight(), - ktx.transcodedFormat, - UnsignedByteType - ); - - texture.encoding = ktx.getEncoding(); - texture.premultiplyAlpha = ktx.getPremultiplyAlpha(); - texture.minFilter = ktx.mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter; - texture.magFilter = LinearFilter; - - onLoad( texture ); - - } ) - .catch( onError ); - - return this; - - } - - } - - class KTX2Container { - - constructor( basisModule, arrayBuffer ) { - - this.basisModule = basisModule; - this.arrayBuffer = arrayBuffer; - - this.zstd = new ZSTDDecoder(); - this.zstd.init(); - - this.mipmaps = null; - this.transcodedFormat = null; - - // Confirm this is a KTX 2.0 file, based on the identifier in the first 12 bytes. - var idByteLength = 12; - var id = new Uint8Array( this.arrayBuffer, 0, idByteLength ); - if ( id[ 0 ] !== 0xAB || // '´' - id[ 1 ] !== 0x4B || // 'K' - id[ 2 ] !== 0x54 || // 'T' - id[ 3 ] !== 0x58 || // 'X' - id[ 4 ] !== 0x20 || // ' ' - id[ 5 ] !== 0x32 || // '2' - id[ 6 ] !== 0x30 || // '0' - id[ 7 ] !== 0xBB || // 'ª' - id[ 8 ] !== 0x0D || // '\r' - id[ 9 ] !== 0x0A || // '\n' - id[ 10 ] !== 0x1A || // '\x1A' - id[ 11 ] !== 0x0A // '\n' - ) { - - throw new Error( 'THREE.KTX2Loader: Missing KTX 2.0 identifier.' ); - - } - - // TODO(donmccurdy): If we need to support BE, derive this from typeSize. - var littleEndian = true; - - - /////////////////////////////////////////////////// - // Header. - /////////////////////////////////////////////////// - - var headerByteLength = 17 * Uint32Array.BYTES_PER_ELEMENT; - var headerReader = new KTX2BufferReader( this.arrayBuffer, idByteLength, headerByteLength, littleEndian ); - - this.header = { - - vkFormat: headerReader.nextUint32(), - typeSize: headerReader.nextUint32(), - pixelWidth: headerReader.nextUint32(), - pixelHeight: headerReader.nextUint32(), - pixelDepth: headerReader.nextUint32(), - arrayElementCount: headerReader.nextUint32(), - faceCount: headerReader.nextUint32(), - levelCount: headerReader.nextUint32(), - - supercompressionScheme: headerReader.nextUint32(), - - dfdByteOffset: headerReader.nextUint32(), - dfdByteLength: headerReader.nextUint32(), - kvdByteOffset: headerReader.nextUint32(), - kvdByteLength: headerReader.nextUint32(), - sgdByteOffset: headerReader.nextUint64(), - sgdByteLength: headerReader.nextUint64(), - - }; - - if ( this.header.pixelDepth > 0 ) { - - throw new Error( 'THREE.KTX2Loader: Only 2D textures are currently supported.' ); - - } - - if ( this.header.arrayElementCount > 1 ) { - - throw new Error( 'THREE.KTX2Loader: Array textures are not currently supported.' ); - - } - - if ( this.header.faceCount > 1 ) { - - throw new Error( 'THREE.KTX2Loader: Cube textures are not currently supported.' ); - - } - - - /////////////////////////////////////////////////// - // Level index - /////////////////////////////////////////////////// - - var levelByteLength = this.header.levelCount * 3 * 8; - var levelReader = new KTX2BufferReader( this.arrayBuffer, idByteLength + headerByteLength, levelByteLength, littleEndian ); - - this.levels = []; - - for ( var i = 0; i < this.header.levelCount; i ++ ) { - - this.levels.push( { - - byteOffset: levelReader.nextUint64(), - byteLength: levelReader.nextUint64(), - uncompressedByteLength: levelReader.nextUint64(), - - } ); - - } - - - /////////////////////////////////////////////////// - // Data Format Descriptor (DFD) - /////////////////////////////////////////////////// - - var dfdReader = new KTX2BufferReader( - this.arrayBuffer, - this.header.dfdByteOffset, - this.header.dfdByteLength, - littleEndian - ); - - const sampleStart = 6; - const sampleWords = 4; - - this.dfd = { - - vendorId: dfdReader.skip( 4 /* totalSize */ ).nextUint16(), - versionNumber: dfdReader.skip( 2 /* descriptorType */ ).nextUint16(), - descriptorBlockSize: dfdReader.nextUint16(), - colorModel: dfdReader.nextUint8(), - colorPrimaries: dfdReader.nextUint8(), - transferFunction: dfdReader.nextUint8(), - flags: dfdReader.nextUint8(), - texelBlockDimension: { - x: dfdReader.nextUint8() + 1, - y: dfdReader.nextUint8() + 1, - z: dfdReader.nextUint8() + 1, - w: dfdReader.nextUint8() + 1, - }, - bytesPlane0: dfdReader.nextUint8(), - numSamples: 0, - samples: [], - - }; - - this.dfd.numSamples = ( this.dfd.descriptorBlockSize / 4 - sampleStart ) / sampleWords; - - dfdReader.skip( 7 /* bytesPlane[1-7] */ ); - - for ( var i = 0; i < this.dfd.numSamples; i ++ ) { - - this.dfd.samples[ i ] = { - - channelID: dfdReader.skip( 3 /* bitOffset + bitLength */ ).nextUint8(), - // ... remainder not implemented. - - }; - - dfdReader.skip( 12 /* samplePosition[0-3], lower, upper */ ); - - } - - if ( this.header.vkFormat !== 0x00 /* VK_FORMAT_UNDEFINED */ && - ! ( this.header.supercompressionScheme === 1 /* BasisLZ */ || - this.dfd.colorModel === DFDModel.UASTC ) ) { - - throw new Error( 'THREE.KTX2Loader: Only Basis Universal supercompression is currently supported.' ); - - } - - - /////////////////////////////////////////////////// - // Key/Value Data (KVD) - /////////////////////////////////////////////////// - - // Not implemented. - this.kvd = {}; - - - /////////////////////////////////////////////////// - // Supercompression Global Data (SGD) - /////////////////////////////////////////////////// - - this.sgd = {}; - - if ( this.header.sgdByteLength <= 0 ) return; - - var sgdReader = new KTX2BufferReader( - this.arrayBuffer, - this.header.sgdByteOffset, - this.header.sgdByteLength, - littleEndian - ); - - this.sgd.endpointCount = sgdReader.nextUint16(); - this.sgd.selectorCount = sgdReader.nextUint16(); - this.sgd.endpointsByteLength = sgdReader.nextUint32(); - this.sgd.selectorsByteLength = sgdReader.nextUint32(); - this.sgd.tablesByteLength = sgdReader.nextUint32(); - this.sgd.extendedByteLength = sgdReader.nextUint32(); - this.sgd.imageDescs = []; - this.sgd.endpointsData = null; - this.sgd.selectorsData = null; - this.sgd.tablesData = null; - this.sgd.extendedData = null; - - for ( var i = 0; i < this.header.levelCount; i ++ ) { - - this.sgd.imageDescs.push( { - - imageFlags: sgdReader.nextUint32(), - rgbSliceByteOffset: sgdReader.nextUint32(), - rgbSliceByteLength: sgdReader.nextUint32(), - alphaSliceByteOffset: sgdReader.nextUint32(), - alphaSliceByteLength: sgdReader.nextUint32(), - - } ); - - } - - var endpointsByteOffset = this.header.sgdByteOffset + sgdReader.offset; - var selectorsByteOffset = endpointsByteOffset + this.sgd.endpointsByteLength; - var tablesByteOffset = selectorsByteOffset + this.sgd.selectorsByteLength; - var extendedByteOffset = tablesByteOffset + this.sgd.tablesByteLength; - - this.sgd.endpointsData = new Uint8Array( this.arrayBuffer, endpointsByteOffset, this.sgd.endpointsByteLength ); - this.sgd.selectorsData = new Uint8Array( this.arrayBuffer, selectorsByteOffset, this.sgd.selectorsByteLength ); - this.sgd.tablesData = new Uint8Array( this.arrayBuffer, tablesByteOffset, this.sgd.tablesByteLength ); - this.sgd.extendedData = new Uint8Array( this.arrayBuffer, extendedByteOffset, this.sgd.extendedByteLength ); - - } - - async initMipmaps( transcoder, config ) { - - await this.zstd.init(); - - var TranscodeTarget = this.basisModule.TranscodeTarget; - var TextureFormat = this.basisModule.TextureFormat; - var ImageInfo = this.basisModule.ImageInfo; - - var scope = this; - - var mipmaps = []; - var width = this.getWidth(); - var height = this.getHeight(); - var texFormat = this.getTexFormat(); - var hasAlpha = this.getAlpha(); - var isVideo = false; - - // PVRTC1 transcoders (from both ETC1S and UASTC) only support power of 2 dimensions. - var pvrtcTranscodable = MathUtils.isPowerOfTwo( width ) && MathUtils.isPowerOfTwo( height ); - - if ( texFormat === TextureFormat.ETC1S ) { - - var numEndpoints = this.sgd.endpointCount; - var numSelectors = this.sgd.selectorCount; - var endpoints = this.sgd.endpointsData; - var selectors = this.sgd.selectorsData; - var tables = this.sgd.tablesData; - - transcoder.decodePalettes( numEndpoints, endpoints, numSelectors, selectors ); - transcoder.decodeTables( tables ); - - } - - - var targetFormat; - - if ( config.astcSupported ) { - - targetFormat = TranscodeTarget.ASTC_4x4_RGBA; - this.transcodedFormat = RGBA_ASTC_4x4_Format; - - } else if ( config.bptcSupported && texFormat === TextureFormat.UASTC4x4 ) { - - targetFormat = TranscodeTarget.BC7_M5_RGBA; - this.transcodedFormat = RGBA_BPTC_Format; - - } else if ( config.dxtSupported ) { - - targetFormat = hasAlpha ? TranscodeTarget.BC3_RGBA : TranscodeTarget.BC1_RGB; - this.transcodedFormat = hasAlpha ? RGBA_S3TC_DXT5_Format : RGB_S3TC_DXT1_Format; - - } else if ( config.pvrtcSupported && pvrtcTranscodable ) { - - targetFormat = hasAlpha ? TranscodeTarget.PVRTC1_4_RGBA : TranscodeTarget.PVRTC1_4_RGB; - this.transcodedFormat = hasAlpha ? RGBA_PVRTC_4BPPV1_Format : RGB_PVRTC_4BPPV1_Format; - - } else if ( config.etc2Supported ) { - - targetFormat = hasAlpha ? TranscodeTarget.ETC2_RGBA : TranscodeTarget.ETC1_RGB/* subset of ETC2 */; - this.transcodedFormat = hasAlpha ? RGBA_ETC2_EAC_Format : RGB_ETC2_Format; - - } else if ( config.etc1Supported ) { - - targetFormat = TranscodeTarget.ETC1_RGB; - this.transcodedFormat = RGB_ETC1_Format; - - } else { - - console.warn( 'THREE.KTX2Loader: No suitable compressed texture format found. Decoding to RGBA32.' ); - - targetFormat = TranscodeTarget.RGBA32; - this.transcodedFormat = RGBAFormat; - - } - - if ( ! this.basisModule.isFormatSupported( targetFormat, texFormat ) ) { - - throw new Error( 'THREE.KTX2Loader: Selected texture format not supported by current transcoder build.' ); - - } - - var imageDescIndex = 0; - - for ( var level = 0; level < this.header.levelCount; level ++ ) { - - var levelWidth = Math.ceil( width / Math.pow( 2, level ) ); - var levelHeight = Math.ceil( height / Math.pow( 2, level ) ); - - var numImagesInLevel = 1; // TODO(donmccurdy): Support cubemaps, arrays and 3D. - var imageOffsetInLevel = 0; - var imageInfo = new ImageInfo( texFormat, levelWidth, levelHeight, level ); - var levelByteLength = this.levels[ level ].byteLength; - var levelUncompressedByteLength = this.levels[ level ].uncompressedByteLength; - - for ( var imageIndex = 0; imageIndex < numImagesInLevel; imageIndex ++ ) { - - var result; - var encodedData; - - if ( texFormat === TextureFormat.UASTC4x4 ) { - - // UASTC - - imageInfo.flags = 0; - imageInfo.rgbByteOffset = 0; - imageInfo.rgbByteLength = levelUncompressedByteLength; - imageInfo.alphaByteOffset = 0; - imageInfo.alphaByteLength = 0; - - encodedData = new Uint8Array( this.arrayBuffer, this.levels[ level ].byteOffset + imageOffsetInLevel, levelByteLength ); - - if ( this.header.supercompressionScheme === 2 /* ZSTD */ ) { - - encodedData = this.zstd.decode( encodedData, levelUncompressedByteLength ); - - } - - result = transcoder.transcodeImage( targetFormat, encodedData, imageInfo, 0, hasAlpha, isVideo ); - - } else { - - // ETC1S - - var imageDesc = this.sgd.imageDescs[ imageDescIndex ++ ]; - - imageInfo.flags = imageDesc.imageFlags; - imageInfo.rgbByteOffset = 0; - imageInfo.rgbByteLength = imageDesc.rgbSliceByteLength; - imageInfo.alphaByteOffset = imageDesc.alphaSliceByteOffset > 0 ? imageDesc.rgbSliceByteLength : 0; - imageInfo.alphaByteLength = imageDesc.alphaSliceByteLength; - - encodedData = new Uint8Array( this.arrayBuffer, this.levels[ level ].byteOffset + imageDesc.rgbSliceByteOffset, imageDesc.rgbSliceByteLength + imageDesc.alphaSliceByteLength ); - - result = transcoder.transcodeImage( targetFormat, encodedData, imageInfo, 0, isVideo ); - - } - - if ( result.transcodedImage === undefined ) { - - throw new Error( 'THREE.KTX2Loader: Unable to transcode image.' ); - - } - - // Transcoded image is written in memory allocated by WASM. We could avoid copying - // the image by waiting until the image is uploaded to the GPU, then calling - // delete(). However, (1) we don't know if the user will later need to re-upload it - // e.g. after calling texture.clone(), and (2) this code will eventually be in a - // Web Worker, and transferring WASM's memory seems like a very bad idea. - var levelData = result.transcodedImage.get_typed_memory_view().slice(); - result.transcodedImage.delete(); - - mipmaps.push( { data: levelData, width: levelWidth, height: levelHeight } ); - imageOffsetInLevel += levelByteLength; - - } - - } - - scope.mipmaps = mipmaps; - - } - - getWidth() { - - return this.header.pixelWidth; - - } - - getHeight() { - - return this.header.pixelHeight; - - } - - getEncoding() { - - return this.dfd.transferFunction === 2 /* KHR_DF_TRANSFER_SRGB */ - ? sRGBEncoding - : LinearEncoding; - - } - - getTexFormat() { - - var TextureFormat = this.basisModule.TextureFormat; - - return this.dfd.colorModel === DFDModel.UASTC ? TextureFormat.UASTC4x4 : TextureFormat.ETC1S; - - } - - getAlpha() { - - var TextureFormat = this.basisModule.TextureFormat; - - // TODO(donmccurdy): Handle all channelIDs (i.e. the R & R+G cases), - // choosing appropriate transcode target formats or providing queries - // for applications so they know what to do with the content. - - if ( this.getTexFormat() === TextureFormat.UASTC4x4 ) { - - // UASTC - - if ( ( this.dfd.samples[ 0 ].channelID & 0xF ) === DFDChannel.UASTC.RGBA ) { - - return true; - - } - - return false; - - } - - // ETC1S - - if ( this.dfd.numSamples === 2 && ( this.dfd.samples[ 1 ].channelID & 0xF ) === DFDChannel.ETC1S.AAA ) { - - return true; - - } - - return false; - - } - - getPremultiplyAlpha() { - - return !! ( this.dfd.flags & 1 /* KHR_DF_FLAG_ALPHA_PREMULTIPLIED */ ); - - } - - } - - class KTX2BufferReader { - - constructor( arrayBuffer, byteOffset, byteLength, littleEndian ) { - - this.dataView = new DataView( arrayBuffer, byteOffset, byteLength ); - this.littleEndian = littleEndian; - this.offset = 0; - - } - - nextUint8() { - - var value = this.dataView.getUint8( this.offset, this.littleEndian ); - - this.offset += 1; - - return value; - - } - - nextUint16() { - - var value = this.dataView.getUint16( this.offset, this.littleEndian ); - - this.offset += 2; - - return value; - - } - - nextUint32() { - - var value = this.dataView.getUint32( this.offset, this.littleEndian ); - - this.offset += 4; - - return value; - - } - - nextUint64() { - - // https://stackoverflow.com/questions/53103695/ - var left = this.dataView.getUint32( this.offset, this.littleEndian ); - var right = this.dataView.getUint32( this.offset + 4, this.littleEndian ); - var value = this.littleEndian ? left + ( 2 ** 32 * right ) : ( 2 ** 32 * left ) + right; - - if ( ! Number.isSafeInteger( value ) ) { - - console.warn( 'THREE.KTX2Loader: ' + value + ' exceeds MAX_SAFE_INTEGER. Precision may be lost.' ); - - } - - this.offset += 8; - - return value; - - } - - skip( bytes ) { - - this.offset += bytes; - - return this; - - } - - } - - class TileLoader { - // This class contains the common code to load tile content, such as b3dm and pnts files. - // It is not to be used directly. Instead, subclasses are used to implement specific - // content loaders for different tile types. - constructor(url) { - this.url = url; - this.type = url.slice(-4); - this.version = null; - this.byteLength = null; - this.featureTableJSON = null; - this.featureTableBinary = null; - this.batchTableJson = null; - this.batchTableBinary = null; - this.binaryData = null; - } - // TileLoader.load - async load() { - this.abortController = new AbortController(); - let response = await fetch(this.url, {signal: this.abortController.signal}); - this.abortController = null; - if (!response.ok) { - throw new Error(`HTTP ${response.status} - ${response.statusText}`); - } - let buffer = await response.arrayBuffer(); - let res = await this.parseResponse(buffer); - return res; - } - abortLoad() { - if (this.abortController) { - this.abortController.abort(); - this.abortController = null; - return true; - } - return false; - } - async parseResponse(buffer) { - let header = new Uint32Array(buffer.slice(0, 32)); - let decoder = new TextDecoder(); - let magic = decoder.decode(new Uint8Array(buffer.slice(0, 4))); - if (magic != this.type) { - throw new Error(`Invalid magic string, expected '${this.type}', got '${this.magic}'`); - } - this.version = header[1]; - this.byteLength = header[2]; - let featureTableJSONByteLength = header[3]; - let featureTableBinaryByteLength = header[4]; - let batchTableJsonByteLength = header[5]; - let batchTableBinaryByteLength = header[6]; - let gltfFormat = magic === 'i3dm' ? header[7] : 1; - - /* - console.log('magic: ' + magic); - console.log('version: ' + this.version); - console.log('featureTableJSONByteLength: ' + featureTableJSONByteLength); - console.log('featureTableBinaryByteLength: ' + featureTableBinaryByteLength); - console.log('batchTableJsonByteLength: ' + batchTableJsonByteLength); - console.log('batchTableBinaryByteLength: ' + batchTableBinaryByteLength); - */ - - let pos = magic === 'i3dm' ? 32 : 28; // header length - if (featureTableJSONByteLength > 0) { - this.featureTableJSON = JSON.parse( - decoder.decode(new Uint8Array(buffer.slice(pos, pos + featureTableJSONByteLength))) - ); - pos += featureTableJSONByteLength; - } else { - this.featureTableJSON = {}; - } - this.featureTableBinary = buffer.slice(pos, pos + featureTableBinaryByteLength); - pos += featureTableBinaryByteLength; - if (batchTableJsonByteLength > 0) { - this.batchTableJson = JSON.parse( - decoder.decode(new Uint8Array(buffer.slice(pos, pos + batchTableJsonByteLength))) - ); - pos += batchTableJsonByteLength; - } else { - this.batchTableJson = {}; - } - this.batchTableBinary = buffer.slice(pos, pos + batchTableBinaryByteLength); - pos += batchTableBinaryByteLength; - if (gltfFormat === 1) { - this.binaryData = buffer.slice(pos); - } else { - // load binary data from url at pos - let modelUrl = decoder.decode(new Uint8Array(buffer.slice(pos))); - if (internalGLTFCache.has(modelUrl)) { - this.binaryData = internalGLTFCache.get(modelUrl); - } else { - let response = await fetch(modelUrl); - if (!response.ok) { - throw new Error(`HTTP ${response.status} - ${response.statusText}`); - } - this.binaryData = await response.arrayBuffer(); - internalGLTFCache.set(modelUrl, this.binaryData); - } - } - return this; - } - } - - class B3DM extends TileLoader { - constructor(url) { - super(url); - this.glbData = null; - } - async parseResponse(buffer) { - await super.parseResponse(buffer); - this.glbData = this.binaryData; - return this; - } - } - - class CMPT extends TileLoader { - constructor(url) { - super(url); - } - async parseResponse(buffer) { - let header = new Uint32Array(buffer.slice(0, 4*4)); - let decoder = new TextDecoder(); - let magic = decoder.decode(new Uint8Array(buffer.slice(0, 4))); - if (magic != this.type) { - throw new Error(`Invalid magic string, expected '${this.type}', got '${this.magic}'`); - } - this.version = header[1]; - this.byteLength = header[2]; - this.tilesLength = header[3]; - let innerTiles = []; - let tileStart = 16; - for (let i = 0; i < this.tilesLength; i++) { - let tileHeader = new Uint32Array(buffer.slice(tileStart, tileStart + 3 * 4)); - let tileMagic = decoder.decode(new Uint8Array(buffer.slice(tileStart, tileStart + 4))); - //console.log(`innerTile: ${i}, magic: ${tileMagic}`); - let tileByteLength = tileHeader[2]; - let tileData = buffer.slice(tileStart, tileStart + tileByteLength); - innerTiles.push({type: tileMagic, data: tileData}); - tileStart += tileByteLength; - } - return innerTiles; - } - } - - class PNTS extends TileLoader { - constructor(url) { - super(url); - this.points = new Float32Array(); - this.rgba = null; - this.rgb = null; - } - parseResponse(buffer) { - super.parseResponse(buffer); - if (this.featureTableJSON.POINTS_LENGTH && this.featureTableJSON.POSITION) { - let len = this.featureTableJSON.POINTS_LENGTH; - let pos = this.featureTableJSON.POSITION.byteOffset; - this.points = new Float32Array( - this.featureTableBinary.slice(pos, pos + len * Float32Array.BYTES_PER_ELEMENT * 3) - ); - this.rtc_center = this.featureTableJSON.RTC_CENTER; - if (this.featureTableJSON.RGBA) { - pos = this.featureTableJSON.RGBA.byteOffset; - let colorInts = new Uint8Array( - this.featureTableBinary.slice(pos, pos + len * Uint8Array.BYTES_PER_ELEMENT * 4) - ); - let rgba = new Float32Array(colorInts.length); - for (let i = 0; i < colorInts.length; i++) { - rgba[i] = colorInts[i] / 255.0; - } - this.rgba = rgba; - } else if (this.featureTableJSON.RGB) { - pos = this.featureTableJSON.RGB.byteOffset; - let colorInts = new Uint8Array( - this.featureTableBinary.slice(pos, pos + len * Uint8Array.BYTES_PER_ELEMENT * 3) - ); - let rgb = new Float32Array(colorInts.length); - for (let i = 0; i < colorInts.length; i++) { - rgb[i] = colorInts[i] / 255.0; - } - this.rgb = rgb; - } else if (this.featureTableJSON.RGB565) { - console.error('RGB565 is currently not supported in pointcloud tiles.'); - } - } - return this; - } - } - - let internalGLTFCache = new Map(); - - function YToLat(Y) { - return (Math.atan(Math.pow(Math.E, ((Y / 111319.490778) * Math.PI) / 180.0)) * 360.0) / Math.PI - 90.0; - } - - function LatToScale(lat) { - return 1 / Math.cos((lat * Math.PI) / 180); - } - - function GetModel(modelId, children) { - for (let i = 0; i < children.length; i++) { - const element = children[i]; - if (element.type === 'Group') { - if (element.children) { - const model = GetModel(modelId, element.children); - if (model) { - return model; - } - } - } else if (element.type === 'Mesh') { - if (element.userData.b3dm === modelId) { - return element.parent; - } - } - } - } - - async function IMesh(inmesh, instancesParams, inverseMatrix) { - /* intancesParams { - positions: float32[] - rtcCenter?: float32[3] - normalsRight?: float32[] - normalsUp?: float32[] - scales?: float32[] - xyzScales?: float32[] - } */ - let matrix = new Matrix4(); - let position = new Vector3(); - let rotation = new Euler(); - let quaternion = new Quaternion(); - let scale = new Vector3(); - let rtcCenter = instancesParams.rtcCenter ? instancesParams.rtcCenter : [0.0, 0.0, 0.0]; - - let geometry = inmesh.geometry; - geometry.applyMatrix4(inmesh.matrixWorld); // apply world modifiers to geometry - - let material = inmesh.material; - let positions = instancesParams.positions; - let instanceCount = positions.length / 3; - let instancedMesh = new InstancedMesh(geometry, material, instanceCount); - instancedMesh.userData = inmesh.userData; - - if (instancesParams.rtcCenter) { - rtcCenter = instancesParams.rtcCenter; - } - - for (let i = 0; i < instanceCount; i++) { - position = { - x: positions[i * 3] + (rtcCenter[0] + inverseMatrix.elements[12]), - y: positions[i * 3 + 1] + (rtcCenter[1] + inverseMatrix.elements[13]), - z: positions[i * 3 + 2] + (rtcCenter[2] + inverseMatrix.elements[14]) - }; - if (instancesParams.normalsRight) { - rotation.set(0, 0, Math.atan2(instancesParams.normalsRight[i * 3 + 1], instancesParams.normalsRight[i * 3])); - quaternion.setFromEuler(rotation); - } - scale.x = scale.y = scale.z = LatToScale(YToLat(positions[i * 3 + 1])); - if (instancesParams.scales) { - scale.x *= instancesParams.scales[i]; - scale.y *= instancesParams.scales[i]; - scale.z *= instancesParams.scales[i]; - } - if (instancesParams.xyzScales) { - scale.x *= instancesParams.xyzScales[i * 3]; - scale.y *= instancesParams.xyzScales[i * 3 + 1]; - scale.z *= instancesParams.xyzScales[i * 3 + 2]; - } - matrix.compose(position, quaternion, scale); - instancedMesh.setMatrixAt(i, matrix); - instancedMesh.castShadow = true; - } - - return instancedMesh; - } - - function applyStyle(scene,styleParams){ - let maincolor = null; - if (styleParams.color != null) { - maincolor = new Color(styleParams.color); - } - scene.traverse(child => { - if (child instanceof Mesh) { - - if (styleParams.color != null) { - child.material.color = maincolor; - } - if (styleParams.opacity != null) { - child.material.opacity = styleParams.opacity; - child.material.transparent = styleParams.opacity < 1.0 ? true : false; - } - - // some gltf has wrong bounding data, recompute here - child.geometry.computeBoundingBox(); - child.geometry.computeBoundingSphere(); - child.castShadow = true; - - //For changing individual colors later, we have to introduce vertexcolors - //const color = new THREE.Color(); - const positions = child.geometry.attributes.position; - const count = positions.count; - child.geometry.setAttribute( 'color', new BufferAttribute( new Float32Array( count * 3 ), 3 ) ); - const colors = child.geometry.attributes.color; - const color = new Color(); - const grey = new Color("rgb(20,20,20)"); - const ymin = child.geometry.boundingBox.min.y; - const ymax = child.geometry.boundingBox.max.y; - //Currently attributes are kind of hardcoded in the tiles and have to be unpacked - //let magnitude = scaleSequential(interpolateYlGnBu).domain([1600, 2020]) - //const colormap = child.parent.userData.attr.map(d=>magnitude(d[0])); - for ( let i = 0; i < count; i ++ ) { - //Assign every vertex it's own color - - //let batchid = child.geometry.attributes._batchid.getX(i); - //let colorval = colormap[batchid]; - let colorval = child.material.color; - color.set(colorval); - //Create a little gradient from black to white - //adding 0.3 not to start at black, dividing by 10 limits effect to bottom - let greyval = Math.min( 0.8 + ( positions.getY( i ) + Math.abs( ymin )) / 1, 1 ); - color.lerp ( grey, 1-greyval ); //lerp to grey - colors.setXYZ( i, color.r, color.g, color.b ); - } - child.material.vertexColors = true; - child.material.depthWrite = !child.material.transparent; // necessary for Velsen dataset? - - } - }); - /* - if (styleParams.color != null || styleParams.opacity != null) { - let color = new THREE.Color(styleParams.color); - scene.traverse(child => { - if (child instanceof THREE.Mesh) { - if (styleParams.color != null) - child.material.color = color; - - if (styleParams.opacity != null) { - child.material.opacity = styleParams.opacity; - child.material.transparent = styleParams.opacity < 1.0 ? true : false; - } - } - }); - }*/ - if (styleParams.debugColor) { - scene.traverse(child => { - if (child instanceof Mesh) { - child.material.color = styleParams.debugColor; - } - }); - } - return scene; - } - - class ThreeDeeTile { - constructor(json, resourcePath, styleParams, updateCallback, parentRefine, parentTransform,projectToMercator) { - this.loaded = false; - this.styleParams = styleParams; - this.updateCallback = updateCallback; - this.resourcePath = resourcePath; - this.projectToMercator = projectToMercator; - this.totalContent = new Group(); // Three JS Object3D Group for this tile and all its children - this.tileContent = new Group(); // Three JS Object3D Group for this tile's content - this.childContent = new Group(); // Three JS Object3D Group for this tile's children - this.totalContent.add(this.tileContent); - this.totalContent.add(this.childContent); - this.boundingVolume = json.boundingVolume; - if (this.boundingVolume && this.boundingVolume.box) { - let b = this.boundingVolume.box; - let extent = [b[0] - b[3], b[1] - b[7], b[0] + b[3], b[1] + b[7]]; - let sw = new Vector3(extent[0], extent[1], b[2] - b[11]); - let ne = new Vector3(extent[2], extent[3], b[2] + b[11]); - this.box = new Box3(sw, ne); - { - //ToDo: I3BM doesn't seem to work without the debugLine, add a transparant one for now - let line = new LineSegments( new EdgesGeometry(new BoxGeometry(b[3] * 2, b[7] * 2, b[11] * 2)), new LineBasicMaterial( {color: new Color(0xff0000), transparent: true, linewidth: 0, depthWrite: false, visible: true, opacity: 0.0}) ); - this.debugLine = line; - } - } else { - this.extent = null; - this.sw = null; - this.ne = null; - this.box = null; - this.center = null; - } - this.refine = json.refine ? json.refine.toUpperCase() : parentRefine; - this.geometricError = json.geometricError; - this.worldTransform = parentTransform ? parentTransform.clone() : new Matrix4(); - this.transform = json.transform; - if (this.transform) - { - let tileMatrix = new Matrix4().fromArray(this.transform); - this.totalContent.applyMatrix4(tileMatrix); - this.worldTransform.multiply(tileMatrix); - } - this.content = json.content; - this.children = []; - if (json.children) { - for (let i=0; ithis.updateCallback(ts)); - await subTileset.load(url, this.styleParams); - if (subTileset.root) { - this.box.applyMatrix4(this.worldTransform); - let inverseMatrix = new Matrix4().getInverse(this.worldTransform); - this.totalContent.applyMatrix4(inverseMatrix); - this.totalContent.updateMatrixWorld(); - this.worldTransform = new Matrix4(); - - this.children.push(subTileset.root); - this.childContent.add(subTileset.root.totalContent); - subTileset.root.totalContent.updateMatrixWorld(); - subTileset.root.checkLoad(this.frustum, this.cameraPosition); - } - } catch (error) { - // load failed (wrong url? connection issues?) - // log error, do not break program flow - console.error(error); - } - break; - case 'b3dm': - try { - this.tileLoader = new B3DM(url); - let b3dmData = await this.tileLoader.load(); - this.tileLoader = null; - this.b3dmAdd(b3dmData, url); - } catch (error) { - if (error.name === "AbortError") { - this.loaded = false; - return; - } - console.error(error); - } - break; - case 'i3dm': - try { - this.tileLoader = new B3DM(url); - let i3dmData = await this.tileLoader.load(); - this.tileLoader = null; - this.i3dmAdd(i3dmData); - } catch (error) { - if (error.name === "AbortError") { - this.loaded = false; - return; - } - console.error(error.message); - } - break; - case 'pnts': - try { - this.tileLoader = new PNTS(url); - let pointData = await this.tileLoader.load(); - this.tileLoader = null; - this.pntsAdd(pointData); - } catch (error) { - if (error.name === "AbortError") { - this.loaded = false; - return; - } - console.error(error); - } - break; - case 'cmpt': - try { - this.tileLoader = new CMPT(url); - let compositeTiles = await this.tileLoader.load(); - this.tileLoader = null; - this.cmptAdd(compositeTiles, url); - } catch (error) { - if (error.name === "AbortError") { - this.loaded = false; - return; - } - console.error(error); - } - break; - default: - throw new Error('invalid tile type: ' + type); - } - } - this.updateCallback(this); - } - async cmptAdd(compositeTiles, url) { - if (this.cmptAdded) { - // prevent duplicate adding - return; - } - this.cmptAdded = true; - for (let innerTile of compositeTiles) { - switch(innerTile.type) { - case 'i3dm': - let i3dm = new B3DM('.i3dm'); - let i3dmData = await i3dm.parseResponse(innerTile.data); - this.i3dmAdd(i3dmData); - break; - case 'b3dm': - let b3dm = new B3DM('.b3dm'); - let b3dmData = await b3dm.parseResponse(innerTile.data); - this.b3dmAdd(b3dmData, url.slice(0,-4) + 'b3dm'); - break; - case 'pnts': - let pnts = new PNTS('.pnts'); - let pointData = pnts.parseResponse(innerTile.data); - this.pntsAdd(pointData); - break; - case 'cmpt': - let cmpt = new CMPT('.cmpt'); - let subCompositeTiles = cmpt.parseResponse(innerTile.data); - this.cmptAdd(subCompositeTiles); - break; - default: - console.error(`Composite type ${innerTile.type} not supported`); - break; - } - //console.log(`type: ${innerTile.type}, size: ${innerTile.data.byteLength}`); - } - } - pntsAdd(pointData) { - if (this.pntsAdded && !this.cmptAdded) { - // prevent duplicate adding - return; - } - this.pntsAdded = true; - let geometry = new BufferGeometry(); - geometry.setAttribute('position', new Float32BufferAttribute(pointData.points, 3)); - let material = new PointsMaterial(); - material.size = this.styleParams.pointsize != null ? this.styleParams.pointsize : 1.0; - if (this.styleParams.color) { - material.vertexColors = NoColors; - material.color = new Color(this.styleParams.color); - material.opacity = this.styleParams.opacity != null ? this.styleParams.opacity : 1.0; - } else if (pointData.rgba) { - geometry.setAttribute('color', new Float32BufferAttribute(pointData.rgba, 4)); - material.vertexColors = VertexColors; - } else if (pointData.rgb) { - geometry.setAttribute('color', new Float32BufferAttribute(pointData.rgb, 3)); - material.vertexColors = VertexColors; - } - this.tileContent.add(new Points( geometry, material )); - if (pointData.rtc_center) { - let c = pointData.rtc_center; - this.tileContent.applyMatrix4(new Matrix4().makeTranslation(c[0], c[1], c[2])); - } - this.tileContent.add(new Points( geometry, material )); - } - b3dmAdd(b3dmData, url) { - if (this.b3dmAdded && !this.cmptAdded) { - // prevent duplicate adding - return; - } - this.b3dmAdded = true; - let dracoloader = new DRACOLoader().setDecoderPath('assets/wasm/'); - let loader = new GLTFLoader().setDRACOLoader(dracoloader).setKTX2Loader(new KTX2Loader()); - let rotateX = new Matrix4().makeRotationAxis(new Vector3(1, 0, 0), Math.PI / 2); - this.tileContent.applyMatrix4(rotateX); // convert from GLTF Y-up to Z-up - loader.parse(b3dmData.glbData, this.resourcePath, (gltf) => { - let scene = gltf.scene || gltf.scenes[0]; - //Add the batchtable to the userData since gltfLoader doesn't deal with it - scene.userData = b3dmData.batchTableJson; - if (scene.userData && Array.isArray(b3dmData.batchTableJson.attr)) { - scene.userData.attr = scene.userData.attr.map(d=>d.split(",")); - scene.userData.b3dm= url.replace(this.resourcePath, '').replace('.b3dm', ''); - } - scene = applyStyle(scene,this.styleParams); - - if (this.projectToMercator) { - //TODO: must be a nicer way to get the local Y in webmerc. than worldTransform.elements - scene.scale.setScalar(LatToScale(YToLat(this.worldTransform.elements[13]))); - } - this.tileContent.add(scene); - dracoloader.dispose(); - }, (error) => { - throw new Error('error parsing gltf: ' + error); - }); - } - i3dmAdd(i3dmData) { - if (this.i3dmAdded && !this.cmptAdded) { - // prevent duplicate adding - return; - } - this.i3dmAdded = true; - let loader = new GLTFLoader().setDRACOLoader(new DRACOLoader().setDecoderPath('assets/wasm/')).setKTX2Loader(new KTX2Loader()); - // Check what metadata is present in the featuretable, currently using: https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/Instanced3DModel#instance-orientation. - let metadata = i3dmData.featureTableJSON; - if (!metadata.POSITION) { - console.error(`i3dm missing position metadata`); - return; - } - let instancesParams = { - positions : new Float32Array(i3dmData.featureTableBinary, metadata.POSITION.byteOffset, metadata.INSTANCES_LENGTH * 3) - }; - if (metadata.RTC_CENTER) { - if (Array.isArray(metadata.RTC_CENTER) && metadata.RTC_CENTER.length === 3) { - instancesParams.rtcCenter = [metadata.RTC_CENTER[0], metadata.RTC_CENTER[1],metadata.RTC_CENTER[2]]; - } - } - if (metadata.NORMAL_UP && metadata.NORMAL_RIGHT) { - instancesParams.normalsRight = new Float32Array(i3dmData.featureTableBinary, metadata.NORMAL_RIGHT.byteOffset, metadata.INSTANCES_LENGTH * 3); - instancesParams.normalsUp = new Float32Array(i3dmData.featureTableBinary, metadata.NORMAL_UP.byteOffset, metadata.INSTANCES_LENGTH * 3); - } - if (metadata.SCALE) { - instancesParams.scales = new Float32Array(i3dmData.featureTableBinary, metadata.SCALE.byteOffset, metadata.INSTANCES_LENGTH); - } - if (metadata.SCALE_NON_UNIFORM) { - instancesParams.xyzScales = new Float32Array(i3dmData.featureTableBinary, metadata.SCALE_NON_UNIFORM.byteOffset, metadata.INSTANCES_LENGTH); - } - let inverseMatrix = new Matrix4().getInverse(this.worldTransform); // in order to offset by the tile - let self = this; - loader.parse(i3dmData.glbData, this.resourcePath, (gltf) => { - let scene = gltf.scene || gltf.scenes[0]; - scene.rotateX(Math.PI / 2); // convert from GLTF Y-up to Mapbox Z-up - scene.updateMatrixWorld(true); - - scene.traverse(child => { - if (child instanceof Mesh) { - child.userData = i3dmData.batchTableJson; - IMesh(child, instancesParams, inverseMatrix) - .then(d=>self.tileContent.add(d)); - } - }); - }); - } - - unload(includeChildren) { - if (this.tileLoader) { - this.tileLoader.abortLoad(); - } - - this.unloadedTileContent = true; - - //Clean up (TODO: make a grace period in which object can stay in cache) - this.freeObjectFromMemory(this.tileContent); - this.totalContent.remove(this.tileContent); - this.tileContent = new Group(); - this.loaded = false; - this.b3dmAdded = false; - - //this.tileContent.visible = false; - if (includeChildren) { - this.unloadedChildContent = true; - this.totalContent.remove(this.childContent); - //this.childContent.visible = false; - } else { - if (this.unloadedChildContent) { - this.unloadedChildContent = false; - this.totalContent.add(this.childContent); - } - } - if (this.debugLine) { - this.totalContent.remove(this.debugLine); - this.unloadedDebugContent = true; - } - this.updateCallback(this); - - - } - checkLoad(frustum, cameraPosition) { - - this.frustum = frustum; - this.cameraPosition = cameraPosition; - /*this.load(); - for (let i=0; i 0.0 && dist > this.geometricError * 50.0) { - // remove from memory - this.unload(true); - return; - } - - //console.log(`camPos: ${cameraPosition.z}, dist: ${dist}, geometricError: ${this.geometricError}`); - // should we load this tile? - if ((this.refine == 'REPLACE' && dist < this.geometricError * 20.0 && this.children.length > 0)) { - this.unload(false); - } else { - this.load(); - } - - // should we load its children? - for (let i=0; i 0.25) { - this.load(); - this.children.forEach(child => { - child.checkLoad(camera); - }); - }*/ - - } - - freeObjectFromMemory(object) { - object.traverse(function(obj){ - if (obj.material && obj.material.dispose) { - obj.material.dispose(); - if (obj.material.map) { - obj.material.map.dispose(); - } - } - if (obj.geometry && obj.geometry.dispose) { - obj.geometry.dispose(); - obj.geometry.attributes.color = {}; - obj.geometry.attributes.normal = {}; - obj.geometry.attributes.position = {}; - obj.geometry.attributes.uv = {}; - obj.geometry.attributes = {}; - obj.material = {}; - } - }); - } - } - - class TileSet { - constructor(updateCallback) { - if (!updateCallback) { - updateCallback = () => {}; - } - this.updateCallback = updateCallback; - this.url = null; - this.version = null; - this.gltfUpAxis = 'Z'; - this.geometricError = null; - this.root = null; - } - // TileSet.load - async load(url, styleParams, projectToMercator) { - this.url = url; - let resourcePath = LoaderUtils.extractUrlBase(url); - - let response = await fetch(this.url); - if (!response.ok) { - throw new Error(`HTTP ${response.status} - ${response.statusText}`); - } - let json = await response.json(); - this.version = json.asset.version; - this.geometricError = json.geometricError; - this.refine = json.root.refine ? json.root.refine.toUpperCase() : 'ADD'; - this.root = new ThreeDeeTile( - json.root, - resourcePath, - styleParams, - this.updateCallback, - this.refine, - null, - projectToMercator - ); - return; - } - } - - var LineSegmentsGeometry = function () { - - InstancedBufferGeometry.call( this ); - - this.type = 'LineSegmentsGeometry'; - - var positions = [ - 1, 2, 0, 1, 2, 0, - 1, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 0, - 1, - 1, 0, 1, - 1, 0 ]; - var uvs = [ - 1, 2, 1, 2, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 2, 1, - 2 ]; - var index = [ 0, 2, 1, 2, 3, 1, 2, 4, 3, 4, 5, 3, 4, 6, 5, 6, 7, 5 ]; - - this.setIndex( index ); - this.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - }; - - LineSegmentsGeometry.prototype = Object.assign( Object.create( InstancedBufferGeometry.prototype ), { - - constructor: LineSegmentsGeometry, - - isLineSegmentsGeometry: true, - - applyMatrix4: function ( matrix ) { - - var start = this.attributes.instanceStart; - var end = this.attributes.instanceEnd; - - if ( start !== undefined ) { - - start.applyMatrix4( matrix ); - - end.applyMatrix4( matrix ); - - start.needsUpdate = true; - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - return this; - - }, - - setPositions: function ( array ) { - - var lineSegments; - - if ( array instanceof Float32Array ) { - - lineSegments = array; - - } else if ( Array.isArray( array ) ) { - - lineSegments = new Float32Array( array ); - - } - - var instanceBuffer = new InstancedInterleavedBuffer( lineSegments, 6, 1 ); // xyz, xyz - - this.setAttribute( 'instanceStart', new InterleavedBufferAttribute( instanceBuffer, 3, 0 ) ); // xyz - this.setAttribute( 'instanceEnd', new InterleavedBufferAttribute( instanceBuffer, 3, 3 ) ); // xyz - - // - - this.computeBoundingBox(); - this.computeBoundingSphere(); - - return this; - - }, - - setColors: function ( array ) { - - var colors; - - if ( array instanceof Float32Array ) { - - colors = array; - - } else if ( Array.isArray( array ) ) { - - colors = new Float32Array( array ); - - } - - var instanceColorBuffer = new InstancedInterleavedBuffer( colors, 6, 1 ); // rgb, rgb - - this.setAttribute( 'instanceColorStart', new InterleavedBufferAttribute( instanceColorBuffer, 3, 0 ) ); // rgb - this.setAttribute( 'instanceColorEnd', new InterleavedBufferAttribute( instanceColorBuffer, 3, 3 ) ); // rgb - - return this; - - }, - - fromWireframeGeometry: function ( geometry ) { - - this.setPositions( geometry.attributes.position.array ); - - return this; - - }, - - fromEdgesGeometry: function ( geometry ) { - - this.setPositions( geometry.attributes.position.array ); - - return this; - - }, - - fromMesh: function ( mesh ) { - - this.fromWireframeGeometry( new WireframeGeometry( mesh.geometry ) ); - - // set colors, maybe - - return this; - - }, - - fromLineSegments: function ( lineSegments ) { - - var geometry = lineSegments.geometry; - - if ( geometry.isGeometry ) { - - this.setPositions( geometry.vertices ); - - } else if ( geometry.isBufferGeometry ) { - - this.setPositions( geometry.attributes.position.array ); // assumes non-indexed - - } - - // set colors, maybe - - return this; - - }, - - computeBoundingBox: function () { - - var box = new Box3(); - - return function computeBoundingBox() { - - if ( this.boundingBox === null ) { - - this.boundingBox = new Box3(); - - } - - var start = this.attributes.instanceStart; - var end = this.attributes.instanceEnd; - - if ( start !== undefined && end !== undefined ) { - - this.boundingBox.setFromBufferAttribute( start ); - - box.setFromBufferAttribute( end ); - - this.boundingBox.union( box ); - - } - - }; - - }(), - - computeBoundingSphere: function () { - - var vector = new Vector3(); - - return function computeBoundingSphere() { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new Sphere(); - - } - - if ( this.boundingBox === null ) { - - this.computeBoundingBox(); - - } - - var start = this.attributes.instanceStart; - var end = this.attributes.instanceEnd; - - if ( start !== undefined && end !== undefined ) { - - var center = this.boundingSphere.center; - - this.boundingBox.getCenter( center ); - - var maxRadiusSq = 0; - - for ( var i = 0, il = start.count; i < il; i ++ ) { - - vector.fromBufferAttribute( start, i ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); - - vector.fromBufferAttribute( end, i ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); - - } - - this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); - - if ( isNaN( this.boundingSphere.radius ) ) { - - console.error( 'THREE.LineSegmentsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.', this ); - - } - - } - - }; - - }(), - - toJSON: function () { - - // todo - - }, - - applyMatrix: function ( matrix ) { - - console.warn( 'THREE.LineSegmentsGeometry: applyMatrix() has been renamed to applyMatrix4().' ); - - return this.applyMatrix4( matrix ); - - } - - } ); - - /** - * parameters = { - * color: , - * linewidth: , - * dashed: , - * dashScale: , - * dashSize: , - * gapSize: , - * resolution: , // to be set by renderer - * } - */ - - UniformsLib.line = { - - linewidth: { value: 1 }, - resolution: { value: new Vector2( 1, 1 ) }, - dashScale: { value: 1 }, - dashSize: { value: 1 }, - gapSize: { value: 1 }, // todo FIX - maybe change to totalSize - opacity: { value: 1 } - - }; - - ShaderLib[ 'line' ] = { - - uniforms: UniformsUtils.merge( [ - UniformsLib.common, - UniformsLib.fog, - UniformsLib.line - ] ), - - vertexShader: - ` - #include - #include - #include - #include - #include - - uniform float linewidth; - uniform vec2 resolution; - - attribute vec3 instanceStart; - attribute vec3 instanceEnd; - - attribute vec3 instanceColorStart; - attribute vec3 instanceColorEnd; - - varying vec2 vUv; - - #ifdef USE_DASH - - uniform float dashScale; - attribute float instanceDistanceStart; - attribute float instanceDistanceEnd; - varying float vLineDistance; - - #endif - - void trimSegment( const in vec4 start, inout vec4 end ) { - - // trim end segment so it terminates between the camera plane and the near plane - - // conservative estimate of the near plane - float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column - float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column - float nearEstimate = - 0.5 * b / a; - - float alpha = ( nearEstimate - start.z ) / ( end.z - start.z ); - - end.xyz = mix( start.xyz, end.xyz, alpha ); - - } - - void main() { - - #ifdef USE_COLOR - - vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd; - - #endif - - #ifdef USE_DASH - - vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd; - - #endif - - float aspect = resolution.x / resolution.y; - - vUv = uv; - - // camera space - vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 ); - vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 ); - - // special case for perspective projection, and segments that terminate either in, or behind, the camera plane - // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space - // but we need to perform ndc-space calculations in the shader, so we must address this issue directly - // perhaps there is a more elegant solution -- WestLangley - - bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column - - if ( perspective ) { - - if ( start.z < 0.0 && end.z >= 0.0 ) { - - trimSegment( start, end ); - - } else if ( end.z < 0.0 && start.z >= 0.0 ) { - - trimSegment( end, start ); - - } - - } - - // clip space - vec4 clipStart = projectionMatrix * start; - vec4 clipEnd = projectionMatrix * end; - - // ndc space - vec2 ndcStart = clipStart.xy / clipStart.w; - vec2 ndcEnd = clipEnd.xy / clipEnd.w; - - // direction - vec2 dir = ndcEnd - ndcStart; - - // account for clip-space aspect ratio - dir.x *= aspect; - dir = normalize( dir ); - - // perpendicular to dir - vec2 offset = vec2( dir.y, - dir.x ); - - // undo aspect ratio adjustment - dir.x /= aspect; - offset.x /= aspect; - - // sign flip - if ( position.x < 0.0 ) offset *= - 1.0; - - // endcaps - if ( position.y < 0.0 ) { - - offset += - dir; - - } else if ( position.y > 1.0 ) { - - offset += dir; - - } - - // adjust for linewidth - offset *= linewidth; - - // adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ... - offset /= resolution.y; - - // select end - vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd; - - // back to clip space - offset *= clip.w; - - clip.xy += offset; - - gl_Position = clip; - - vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation - - #include - #include - #include - - } - `, - - fragmentShader: - ` - uniform vec3 diffuse; - uniform float opacity; - - #ifdef USE_DASH - - uniform float dashSize; - uniform float gapSize; - - #endif - - varying float vLineDistance; - - #include - #include - #include - #include - #include - - varying vec2 vUv; - - void main() { - - #include - - #ifdef USE_DASH - - if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps - - if ( mod( vLineDistance, dashSize + gapSize ) > dashSize ) discard; // todo - FIX - - #endif - - if ( abs( vUv.y ) > 1.0 ) { - - float a = vUv.x; - float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0; - float len2 = a * a + b * b; - - if ( len2 > 1.0 ) discard; - - } - - vec4 diffuseColor = vec4( diffuse, opacity ); - - #include - #include - - gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a ); - - #include - #include - #include - #include - - } - ` - }; - - var LineMaterial = function ( parameters ) { - - ShaderMaterial.call( this, { - - type: 'LineMaterial', - - uniforms: UniformsUtils.clone( ShaderLib[ 'line' ].uniforms ), - - vertexShader: ShaderLib[ 'line' ].vertexShader, - fragmentShader: ShaderLib[ 'line' ].fragmentShader, - - clipping: true // required for clipping support - - } ); - - this.dashed = false; - - Object.defineProperties( this, { - - color: { - - enumerable: true, - - get: function () { - - return this.uniforms.diffuse.value; - - }, - - set: function ( value ) { - - this.uniforms.diffuse.value = value; - - } - - }, - - linewidth: { - - enumerable: true, - - get: function () { - - return this.uniforms.linewidth.value; - - }, - - set: function ( value ) { - - this.uniforms.linewidth.value = value; - - } - - }, - - dashScale: { - - enumerable: true, - - get: function () { - - return this.uniforms.dashScale.value; - - }, - - set: function ( value ) { - - this.uniforms.dashScale.value = value; - - } - - }, - - dashSize: { - - enumerable: true, - - get: function () { - - return this.uniforms.dashSize.value; - - }, - - set: function ( value ) { - - this.uniforms.dashSize.value = value; - - } - - }, - - gapSize: { - - enumerable: true, - - get: function () { - - return this.uniforms.gapSize.value; - - }, - - set: function ( value ) { - - this.uniforms.gapSize.value = value; - - } - - }, - - opacity: { - - enumerable: true, - - get: function () { - - return this.uniforms.opacity.value; - - }, - - set: function ( value ) { - - this.uniforms.opacity.value = value; - - } - - }, - - resolution: { - - enumerable: true, - - get: function () { - - return this.uniforms.resolution.value; - - }, - - set: function ( value ) { - - this.uniforms.resolution.value.copy( value ); - - } - - } - - } ); - - this.setValues( parameters ); - - }; - - LineMaterial.prototype = Object.create( ShaderMaterial.prototype ); - LineMaterial.prototype.constructor = LineMaterial; - - LineMaterial.prototype.isLineMaterial = true; - - var LineSegments2 = function ( geometry, material ) { - - Mesh.call( this ); - - this.type = 'LineSegments2'; - - this.geometry = geometry !== undefined ? geometry : new LineSegmentsGeometry(); - this.material = material !== undefined ? material : new LineMaterial( { color: Math.random() * 0xffffff } ); - - }; - - LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), { - - constructor: LineSegments2, - - isLineSegments2: true, - - computeLineDistances: ( function () { // for backwards-compatability, but could be a method of LineSegmentsGeometry... - - var start = new Vector3(); - var end = new Vector3(); - - return function computeLineDistances() { - - var geometry = this.geometry; - - var instanceStart = geometry.attributes.instanceStart; - var instanceEnd = geometry.attributes.instanceEnd; - var lineDistances = new Float32Array( 2 * instanceStart.data.count ); - - for ( var i = 0, j = 0, l = instanceStart.data.count; i < l; i ++, j += 2 ) { - - start.fromBufferAttribute( instanceStart, i ); - end.fromBufferAttribute( instanceEnd, i ); - - lineDistances[ j ] = ( j === 0 ) ? 0 : lineDistances[ j - 1 ]; - lineDistances[ j + 1 ] = lineDistances[ j ] + start.distanceTo( end ); - - } - - var instanceDistanceBuffer = new InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1 - - geometry.setAttribute( 'instanceDistanceStart', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0 - geometry.setAttribute( 'instanceDistanceEnd', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1 - - return this; - - }; - - }() ), - - raycast: ( function () { - - var start = new Vector4(); - var end = new Vector4(); - - var ssOrigin = new Vector4(); - var ssOrigin3 = new Vector3(); - var mvMatrix = new Matrix4(); - var line = new Line3(); - var closestPoint = new Vector3(); - - return function raycast( raycaster, intersects ) { - - if ( raycaster.camera === null ) { - - console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2.' ); - - } - - var ray = raycaster.ray; - var camera = raycaster.camera; - var projectionMatrix = camera.projectionMatrix; - - var geometry = this.geometry; - var material = this.material; - var resolution = material.resolution; - var lineWidth = material.linewidth; - - var instanceStart = geometry.attributes.instanceStart; - var instanceEnd = geometry.attributes.instanceEnd; - - // pick a point 1 unit out along the ray to avoid the ray origin - // sitting at the camera origin which will cause "w" to be 0 when - // applying the projection matrix. - ray.at( 1, ssOrigin ); - - // ndc space [ - 1.0, 1.0 ] - ssOrigin.w = 1; - ssOrigin.applyMatrix4( camera.matrixWorldInverse ); - ssOrigin.applyMatrix4( projectionMatrix ); - ssOrigin.multiplyScalar( 1 / ssOrigin.w ); - - // screen space - ssOrigin.x *= resolution.x / 2; - ssOrigin.y *= resolution.y / 2; - ssOrigin.z = 0; - - ssOrigin3.copy( ssOrigin ); - - var matrixWorld = this.matrixWorld; - mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld ); - - for ( var i = 0, l = instanceStart.count; i < l; i ++ ) { - - start.fromBufferAttribute( instanceStart, i ); - end.fromBufferAttribute( instanceEnd, i ); - - start.w = 1; - end.w = 1; - - // camera space - start.applyMatrix4( mvMatrix ); - end.applyMatrix4( mvMatrix ); - - // clip space - start.applyMatrix4( projectionMatrix ); - end.applyMatrix4( projectionMatrix ); - - // ndc space [ - 1.0, 1.0 ] - start.multiplyScalar( 1 / start.w ); - end.multiplyScalar( 1 / end.w ); - - // skip the segment if it's outside the camera near and far planes - var isBehindCameraNear = start.z < - 1 && end.z < - 1; - var isPastCameraFar = start.z > 1 && end.z > 1; - if ( isBehindCameraNear || isPastCameraFar ) { - - continue; - - } - - // screen space - start.x *= resolution.x / 2; - start.y *= resolution.y / 2; - - end.x *= resolution.x / 2; - end.y *= resolution.y / 2; - - // create 2d segment - line.start.copy( start ); - line.start.z = 0; - - line.end.copy( end ); - line.end.z = 0; - - // get closest point on ray to segment - var param = line.closestPointToPointParameter( ssOrigin3, true ); - line.at( param, closestPoint ); - - // check if the intersection point is within clip space - var zPos = MathUtils.lerp( start.z, end.z, param ); - var isInClipSpace = zPos >= - 1 && zPos <= 1; - - var isInside = ssOrigin3.distanceTo( closestPoint ) < lineWidth * 0.5; - - if ( isInClipSpace && isInside ) { - - line.start.fromBufferAttribute( instanceStart, i ); - line.end.fromBufferAttribute( instanceEnd, i ); - - line.start.applyMatrix4( matrixWorld ); - line.end.applyMatrix4( matrixWorld ); - - var pointOnLine = new Vector3(); - var point = new Vector3(); - - ray.distanceSqToSegment( line.start, line.end, point, pointOnLine ); - - intersects.push( { - - point: point, - pointOnLine: pointOnLine, - distance: ray.origin.distanceTo( point ), - - object: this, - face: null, - faceIndex: i, - uv: null, - uv2: null, - - } ); - - } - - } - - }; - - }() ) - - } ); - - var LineGeometry = function () { - - LineSegmentsGeometry.call( this ); - - this.type = 'LineGeometry'; - - }; - - LineGeometry.prototype = Object.assign( Object.create( LineSegmentsGeometry.prototype ), { - - constructor: LineGeometry, - - isLineGeometry: true, - - setPositions: function ( array ) { - - // converts [ x1, y1, z1, x2, y2, z2, ... ] to pairs format - - var length = array.length - 3; - var points = new Float32Array( 2 * length ); - - for ( var i = 0; i < length; i += 3 ) { - - points[ 2 * i ] = array[ i ]; - points[ 2 * i + 1 ] = array[ i + 1 ]; - points[ 2 * i + 2 ] = array[ i + 2 ]; - - points[ 2 * i + 3 ] = array[ i + 3 ]; - points[ 2 * i + 4 ] = array[ i + 4 ]; - points[ 2 * i + 5 ] = array[ i + 5 ]; - - } - - LineSegmentsGeometry.prototype.setPositions.call( this, points ); - - return this; - - }, - - setColors: function ( array ) { - - // converts [ r1, g1, b1, r2, g2, b2, ... ] to pairs format - - var length = array.length - 3; - var colors = new Float32Array( 2 * length ); - - for ( var i = 0; i < length; i += 3 ) { - - colors[ 2 * i ] = array[ i ]; - colors[ 2 * i + 1 ] = array[ i + 1 ]; - colors[ 2 * i + 2 ] = array[ i + 2 ]; - - colors[ 2 * i + 3 ] = array[ i + 3 ]; - colors[ 2 * i + 4 ] = array[ i + 4 ]; - colors[ 2 * i + 5 ] = array[ i + 5 ]; - - } - - LineSegmentsGeometry.prototype.setColors.call( this, colors ); - - return this; - - }, - - fromLine: function ( line ) { - - var geometry = line.geometry; - - if ( geometry.isGeometry ) { - - this.setPositions( geometry.vertices ); - - } else if ( geometry.isBufferGeometry ) { - - this.setPositions( geometry.attributes.position.array ); // assumes non-indexed - - } - - // set colors, maybe - - return this; - - }, - - copy: function ( /* source */ ) { - - // todo - - return this; - - } - - } ); - - var Line2 = function ( geometry, material ) { - - LineSegments2.call( this ); - - this.type = 'Line2'; - - this.geometry = geometry !== undefined ? geometry : new LineGeometry(); - this.material = material !== undefined ? material : new LineMaterial( { color: Math.random() * 0xffffff } ); - - }; - - Line2.prototype = Object.assign( Object.create( LineSegments2.prototype ), { - - constructor: Line2, - - isLine2: true - - } ); - - class Highlight { - constructor(scene, map) { - this.scene = scene; - this.map = map; - this.items = []; - } - - add(modelId, color, gradientColor, opacity, boxMargin) { - if (!modelId || this._isHighlighted(modelId)) { - return; - } - - const item = this._createHighlight(modelId, color, gradientColor, opacity, boxMargin); - if (!item) { - return; - } - - this._addToItems(item); - this.map.triggerRepaint(); - } - - remove(modelId) { - const highlighted = this._getHighlighted(modelId); - if (!highlighted) { - return; - } - - highlighted.model.remove(highlighted.highlight); - this._removeFromItems(modelId); - } - - clear() { - this.items.forEach((e) => { - this.remove(e.modelId); - }); - - this.items = []; - } - - _addToItems(item) { - this.items.push(item); - } - - _removeFromItems(modelId) { - this.items = this.items.filter((e) => { - return e.modelId !== modelId; - }); - } - - _isHighlighted(modelId) { - return this._getHighlighted(modelId) !== undefined; - } - - _getHighlighted(modelId) { - for (let i = 0; i < this.items.length; i++) { - if (this.items[i].modelId === modelId) { - return this.items[i]; - } - } - } - - _createHighlight(modelId, color, gradientColor, opacity, boxMargin) { - color = color ? color : '#4C162C'; - gradientColor = gradientColor ? gradientColor : '#ff4c94'; - opacity = opacity ? opacity : 0.5; - boxMargin = boxMargin ? boxMargin : 0.05; - - const model = GetModel(modelId, this.scene.children); - if (!model) { - return; - } - - const box = new Box3().setFromObject(model); - var helper = new Box3Helper( box, 0xffff00 ); - - box.min = model.worldToLocal(box.min); - box.max = model.worldToLocal(box.max); - - const xAdd = boxMargin * box.max.x - boxMargin * box.min.x; - const yAdd = boxMargin * box.max.y - boxMargin * box.min.y; - const zAdd = boxMargin * box.max.z - boxMargin * box.min.z; - - const vertices = [ - [box.min.x - xAdd, box.min.y - yAdd, box.min.z - zAdd], - [box.min.x - xAdd, box.max.y + yAdd, box.min.z - zAdd], - [box.max.x + xAdd, box.max.y + yAdd, box.min.z - zAdd], - [box.max.x + xAdd, box.min.y - yAdd, box.min.z - zAdd], - [box.min.x - xAdd, box.min.y - yAdd, box.max.z + zAdd], - [box.min.x - xAdd, box.max.y + yAdd, box.max.z + zAdd], - [box.max.x + xAdd, box.max.y + yAdd, box.max.z + zAdd], - [box.max.x + xAdd, box.min.y - yAdd, box.max.z + zAdd] - ]; - - const planes = [ - this._createPlane([vertices[1], vertices[2], vertices[0], vertices[3]], color, gradientColor, opacity, 'back'), - this._createPlane([vertices[2], vertices[6], vertices[3], vertices[7]], color, gradientColor, opacity, 'back'), - this._createPlane([vertices[1], vertices[5], vertices[0], vertices[4]], color, gradientColor, opacity, 'back'), - this._createPlane([vertices[5], vertices[6], vertices[4], vertices[7]], color, gradientColor, opacity, 'back'), - this._createPlane([vertices[1], vertices[2], vertices[0], vertices[3]], color, gradientColor, opacity, 'front'), - this._createPlane([vertices[2], vertices[6], vertices[3], vertices[7]], color, gradientColor, opacity, 'front'), - this._createPlane([vertices[1], vertices[5], vertices[0], vertices[4]], color, gradientColor, opacity, 'front'), - this._createPlane([vertices[5], vertices[6], vertices[4], vertices[7]], color, gradientColor, opacity, 'front') - ]; - - const line = this._createLine( - [...vertices[1], ...vertices[2], ...vertices[6], ...vertices[5], ...vertices[1]], - gradientColor || color - ); - - const highlight = new Group(); - highlight.add(...planes); - highlight.add(line); - model.add(helper); - - return { - modelId: modelId, - model: model, - highlight: highlight - }; - } - - _createPlane(vertices, color, gradientColor, opacity, side = 'front') { - const bufferGeom = new BufferGeometry(); - bufferGeom.setAttribute( - 'position', - new BufferAttribute( - new Float32Array([...vertices[0], ...vertices[1], ...vertices[2], ...vertices[3]]), - 3 - ) - ); - - //bufferGeom.setIndex([0, 2, 1, 2, 3, 1]); - bufferGeom.setIndex([0, 2, 1, 2, 3, 1]); - bufferGeom.computeVertexNormals(); - bufferGeom.computeBoundingBox(); - - const geom = new Geometry().fromBufferGeometry(bufferGeom); - geom.faceVertexUvs = new PlaneGeometry().faceVertexUvs; - geom.uvsNeedUpdate = true; - - const material = new MeshLambertMaterial({ - color: color || '#1C5A6D', - transparent: true, - opacity: opacity, - side: side === 'back' ? BackSide : FrontSide - }); - material.defines = { USE_UV: '' }; - material.onBeforeCompile = (shader) => { - shader.uniforms.gradientColor = { - value: new Color(gradientColor || '#ffff00') - }; - shader.fragmentShader = ` - uniform vec3 gradientColor; - ${shader.fragmentShader.replace( - 'vec4 diffuseColor = vec4( diffuse, opacity );', - 'vec4 diffuseColor = vec4( mix(diffuse, gradientColor, vec3(vUv.y)), opacity);' - )}`; - }; - - const plane = new Mesh(geom, material); - return plane; - } - - _createLine(positions, color) { - const geometry = new LineGeometry(); - geometry.setPositions(positions); - - const matLine = new LineMaterial({ - color, - linewidth: 0.002, - dashed: false - }); - - const line = new Line2(geometry, matLine); - return line; - } - } - - var RenderableObject = function () { - - this.id = 0; - - this.object = null; - this.z = 0; - this.renderOrder = 0; - - }; - - // - - var RenderableFace = function () { - - this.id = 0; - - this.v1 = new RenderableVertex(); - this.v2 = new RenderableVertex(); - this.v3 = new RenderableVertex(); - - this.normalModel = new Vector3(); - - this.vertexNormalsModel = [ new Vector3(), new Vector3(), new Vector3() ]; - this.vertexNormalsLength = 0; - - this.color = new Color(); - this.material = null; - this.uvs = [ new Vector2(), new Vector2(), new Vector2() ]; - - this.z = 0; - this.renderOrder = 0; - - }; - - // - - var RenderableVertex = function () { - - this.position = new Vector3(); - this.positionWorld = new Vector3(); - this.positionScreen = new Vector4(); - - this.visible = true; - - }; - - RenderableVertex.prototype.copy = function ( vertex ) { - - this.positionWorld.copy( vertex.positionWorld ); - this.positionScreen.copy( vertex.positionScreen ); - - }; - - // - - var RenderableLine = function () { - - this.id = 0; - - this.v1 = new RenderableVertex(); - this.v2 = new RenderableVertex(); - - this.vertexColors = [ new Color(), new Color() ]; - this.material = null; - - this.z = 0; - this.renderOrder = 0; - - }; - - // - - var RenderableSprite = function () { - - this.id = 0; - - this.object = null; - - this.x = 0; - this.y = 0; - this.z = 0; - - this.rotation = 0; - this.scale = new Vector2(); - - this.material = null; - this.renderOrder = 0; - - }; - - // - - var Projector = function () { - - var _object, _objectCount, _objectPool = [], _objectPoolLength = 0, - _vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0, - _face, _faceCount, _facePool = [], _facePoolLength = 0, - _line, _lineCount, _linePool = [], _linePoolLength = 0, - _sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0, - - _renderData = { objects: [], lights: [], elements: [] }, - - _vector3 = new Vector3(), - _vector4 = new Vector4(), - - _clipBox = new Box3( new Vector3( - 1, - 1, - 1 ), new Vector3( 1, 1, 1 ) ), - _boundingBox = new Box3(), - _points3 = new Array( 3 ), - - _viewMatrix = new Matrix4(), - _viewProjectionMatrix = new Matrix4(), - - _modelMatrix, - _modelViewProjectionMatrix = new Matrix4(), - - _normalMatrix = new Matrix3(), - - _frustum = new Frustum(), - - _clippedVertex1PositionScreen = new Vector4(), - _clippedVertex2PositionScreen = new Vector4(); - - // - - this.projectVector = function ( vector, camera ) { - - console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); - vector.project( camera ); - - }; - - this.unprojectVector = function ( vector, camera ) { - - console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); - vector.unproject( camera ); - - }; - - this.pickingRay = function () { - - console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); - - }; - - // - - var RenderList = function () { - - var normals = []; - var colors = []; - var uvs = []; - - var object = null; - - var normalMatrix = new Matrix3(); - - function setObject( value ) { - - object = value; - - normalMatrix.getNormalMatrix( object.matrixWorld ); - - normals.length = 0; - colors.length = 0; - uvs.length = 0; - - } - - function projectVertex( vertex ) { - - var position = vertex.position; - var positionWorld = vertex.positionWorld; - var positionScreen = vertex.positionScreen; - - positionWorld.copy( position ).applyMatrix4( _modelMatrix ); - positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix ); - - var invW = 1 / positionScreen.w; - - positionScreen.x *= invW; - positionScreen.y *= invW; - positionScreen.z *= invW; - - vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 && - positionScreen.y >= - 1 && positionScreen.y <= 1 && - positionScreen.z >= - 1 && positionScreen.z <= 1; - - } - - function pushVertex( x, y, z ) { - - _vertex = getNextVertexInPool(); - _vertex.position.set( x, y, z ); - - projectVertex( _vertex ); - - } - - function pushNormal( x, y, z ) { - - normals.push( x, y, z ); - - } - - function pushColor( r, g, b ) { - - colors.push( r, g, b ); - - } - - function pushUv( x, y ) { - - uvs.push( x, y ); - - } - - function checkTriangleVisibility( v1, v2, v3 ) { - - if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true; - - _points3[ 0 ] = v1.positionScreen; - _points3[ 1 ] = v2.positionScreen; - _points3[ 2 ] = v3.positionScreen; - - return _clipBox.intersectsBox( _boundingBox.setFromPoints( _points3 ) ); - - } - - function checkBackfaceCulling( v1, v2, v3 ) { - - return ( ( v3.positionScreen.x - v1.positionScreen.x ) * - ( v2.positionScreen.y - v1.positionScreen.y ) - - ( v3.positionScreen.y - v1.positionScreen.y ) * - ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0; - - } - - function pushLine( a, b ) { - - var v1 = _vertexPool[ a ]; - var v2 = _vertexPool[ b ]; - - // Clip - - v1.positionScreen.copy( v1.position ).applyMatrix4( _modelViewProjectionMatrix ); - v2.positionScreen.copy( v2.position ).applyMatrix4( _modelViewProjectionMatrix ); - - if ( clipLine( v1.positionScreen, v2.positionScreen ) === true ) { - - // Perform the perspective divide - v1.positionScreen.multiplyScalar( 1 / v1.positionScreen.w ); - v2.positionScreen.multiplyScalar( 1 / v2.positionScreen.w ); - - _line = getNextLineInPool(); - _line.id = object.id; - _line.v1.copy( v1 ); - _line.v2.copy( v2 ); - _line.z = Math.max( v1.positionScreen.z, v2.positionScreen.z ); - _line.renderOrder = object.renderOrder; - - _line.material = object.material; - - if ( object.material.vertexColors ) { - - _line.vertexColors[ 0 ].fromArray( colors, a * 3 ); - _line.vertexColors[ 1 ].fromArray( colors, b * 3 ); - - } - - _renderData.elements.push( _line ); - - } - - } - - function pushTriangle( a, b, c, material ) { - - var v1 = _vertexPool[ a ]; - var v2 = _vertexPool[ b ]; - var v3 = _vertexPool[ c ]; - - if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; - - if ( material.side === DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) { - - _face = getNextFaceInPool(); - - _face.id = object.id; - _face.v1.copy( v1 ); - _face.v2.copy( v2 ); - _face.v3.copy( v3 ); - _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; - _face.renderOrder = object.renderOrder; - - // face normal - _vector3.subVectors( v3.position, v2.position ); - _vector4.subVectors( v1.position, v2.position ); - _vector3.cross( _vector4 ); - _face.normalModel.copy( _vector3 ); - _face.normalModel.applyMatrix3( normalMatrix ).normalize(); - - for ( var i = 0; i < 3; i ++ ) { - - var normal = _face.vertexNormalsModel[ i ]; - normal.fromArray( normals, arguments[ i ] * 3 ); - normal.applyMatrix3( normalMatrix ).normalize(); - - var uv = _face.uvs[ i ]; - uv.fromArray( uvs, arguments[ i ] * 2 ); - - } - - _face.vertexNormalsLength = 3; - - _face.material = material; - - if ( material.vertexColors ) { - - _face.color.fromArray( colors, a * 3 ); - - } - - _renderData.elements.push( _face ); - - } - - } - - return { - setObject: setObject, - projectVertex: projectVertex, - checkTriangleVisibility: checkTriangleVisibility, - checkBackfaceCulling: checkBackfaceCulling, - pushVertex: pushVertex, - pushNormal: pushNormal, - pushColor: pushColor, - pushUv: pushUv, - pushLine: pushLine, - pushTriangle: pushTriangle - }; - - }; - - var renderList = new RenderList(); - - function projectObject( object ) { - - if ( object.visible === false ) return; - - if ( object instanceof Light ) { - - _renderData.lights.push( object ); - - } else if ( object instanceof Mesh || object instanceof Line || object instanceof Points ) { - - if ( object.material.visible === false ) return; - if ( object.frustumCulled === true && _frustum.intersectsObject( object ) === false ) return; - - addObject( object ); - - } else if ( object instanceof Sprite ) { - - if ( object.material.visible === false ) return; - if ( object.frustumCulled === true && _frustum.intersectsSprite( object ) === false ) return; - - addObject( object ); - - } - - var children = object.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - projectObject( children[ i ] ); - - } - - } - - function addObject( object ) { - - _object = getNextObjectInPool(); - _object.id = object.id; - _object.object = object; - - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyMatrix4( _viewProjectionMatrix ); - _object.z = _vector3.z; - _object.renderOrder = object.renderOrder; - - _renderData.objects.push( _object ); - - } - - this.projectScene = function ( scene, camera, sortObjects, sortElements ) { - - _faceCount = 0; - _lineCount = 0; - _spriteCount = 0; - - _renderData.elements.length = 0; - - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - if ( camera.parent === null ) camera.updateMatrixWorld(); - - _viewMatrix.copy( camera.matrixWorldInverse ); - _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); - - _frustum.setFromProjectionMatrix( _viewProjectionMatrix ); - - // - - _objectCount = 0; - - _renderData.objects.length = 0; - _renderData.lights.length = 0; - - projectObject( scene ); - - if ( sortObjects === true ) { - - _renderData.objects.sort( painterSort ); - - } - - // - - var objects = _renderData.objects; - - for ( var o = 0, ol = objects.length; o < ol; o ++ ) { - - var object = objects[ o ].object; - var geometry = object.geometry; - - renderList.setObject( object ); - - _modelMatrix = object.matrixWorld; - - _vertexCount = 0; - - if ( object instanceof Mesh ) { - - if ( geometry instanceof BufferGeometry ) { - - var material = object.material; - - var isMultiMaterial = Array.isArray( material ); - - var attributes = geometry.attributes; - var groups = geometry.groups; - - if ( attributes.position === undefined ) continue; - - var positions = attributes.position.array; - - for ( var i = 0, l = positions.length; i < l; i += 3 ) { - - var x = positions[ i ]; - var y = positions[ i + 1 ]; - var z = positions[ i + 2 ]; - - if ( material.morphTargets === true ) { - - var morphTargets = geometry.morphAttributes.position; - var morphTargetsRelative = geometry.morphTargetsRelative; - var morphInfluences = object.morphTargetInfluences; - - for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { - - var influence = morphInfluences[ t ]; - - if ( influence === 0 ) continue; - - var target = morphTargets[ t ]; - - if ( morphTargetsRelative ) { - - x += target.getX( i / 3 ) * influence; - y += target.getY( i / 3 ) * influence; - z += target.getZ( i / 3 ) * influence; - - } else { - - x += ( target.getX( i / 3 ) - positions[ i ] ) * influence; - y += ( target.getY( i / 3 ) - positions[ i + 1 ] ) * influence; - z += ( target.getZ( i / 3 ) - positions[ i + 2 ] ) * influence; - - } - - } - - } - - renderList.pushVertex( x, y, z ); - - } - - if ( attributes.normal !== undefined ) { - - var normals = attributes.normal.array; - - for ( var i = 0, l = normals.length; i < l; i += 3 ) { - - renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ); - - } - - } - - if ( attributes.color !== undefined ) { - - var colors = attributes.color.array; - - for ( var i = 0, l = colors.length; i < l; i += 3 ) { - - renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ); - - } - - } - - if ( attributes.uv !== undefined ) { - - var uvs = attributes.uv.array; - - for ( var i = 0, l = uvs.length; i < l; i += 2 ) { - - renderList.pushUv( uvs[ i ], uvs[ i + 1 ] ); - - } - - } - - if ( geometry.index !== null ) { - - var indices = geometry.index.array; - - if ( groups.length > 0 ) { - - for ( var g = 0; g < groups.length; g ++ ) { - - var group = groups[ g ]; - - material = isMultiMaterial === true - ? object.material[ group.materialIndex ] - : object.material; - - if ( material === undefined ) continue; - - for ( var i = group.start, l = group.start + group.count; i < l; i += 3 ) { - - renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material ); - - } - - } - - } else { - - for ( var i = 0, l = indices.length; i < l; i += 3 ) { - - renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material ); - - } - - } - - } else { - - if ( groups.length > 0 ) { - - for ( var g = 0; g < groups.length; g ++ ) { - - var group = groups[ g ]; - - material = isMultiMaterial === true - ? object.material[ group.materialIndex ] - : object.material; - - if ( material === undefined ) continue; - - for ( var i = group.start, l = group.start + group.count; i < l; i += 3 ) { - - renderList.pushTriangle( i, i + 1, i + 2, material ); - - } - - } - - } else { - - for ( var i = 0, l = positions.length / 3; i < l; i += 3 ) { - - renderList.pushTriangle( i, i + 1, i + 2, material ); - - } - - } - - } - - } else if ( geometry instanceof Geometry ) { - - var vertices = geometry.vertices; - var faces = geometry.faces; - var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; - - _normalMatrix.getNormalMatrix( _modelMatrix ); - - var material = object.material; - - var isMultiMaterial = Array.isArray( material ); - - for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { - - var vertex = vertices[ v ]; - - _vector3.copy( vertex ); - - if ( material.morphTargets === true ) { - - var morphTargets = geometry.morphTargets; - var morphInfluences = object.morphTargetInfluences; - - for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { - - var influence = morphInfluences[ t ]; - - if ( influence === 0 ) continue; - - var target = morphTargets[ t ]; - var targetVertex = target.vertices[ v ]; - - _vector3.x += ( targetVertex.x - vertex.x ) * influence; - _vector3.y += ( targetVertex.y - vertex.y ) * influence; - _vector3.z += ( targetVertex.z - vertex.z ) * influence; - - } - - } - - renderList.pushVertex( _vector3.x, _vector3.y, _vector3.z ); - - } - - for ( var f = 0, fl = faces.length; f < fl; f ++ ) { - - var face = faces[ f ]; - - material = isMultiMaterial === true - ? object.material[ face.materialIndex ] - : object.material; - - if ( material === undefined ) continue; - - var side = material.side; - - var v1 = _vertexPool[ face.a ]; - var v2 = _vertexPool[ face.b ]; - var v3 = _vertexPool[ face.c ]; - - if ( renderList.checkTriangleVisibility( v1, v2, v3 ) === false ) continue; - - var visible = renderList.checkBackfaceCulling( v1, v2, v3 ); - - if ( side !== DoubleSide ) { - - if ( side === FrontSide && visible === false ) continue; - if ( side === BackSide && visible === true ) continue; - - } - - _face = getNextFaceInPool(); - - _face.id = object.id; - _face.v1.copy( v1 ); - _face.v2.copy( v2 ); - _face.v3.copy( v3 ); - - _face.normalModel.copy( face.normal ); - - if ( visible === false && ( side === BackSide || side === DoubleSide ) ) { - - _face.normalModel.negate(); - - } - - _face.normalModel.applyMatrix3( _normalMatrix ).normalize(); - - var faceVertexNormals = face.vertexNormals; - - for ( var n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) { - - var normalModel = _face.vertexNormalsModel[ n ]; - normalModel.copy( faceVertexNormals[ n ] ); - - if ( visible === false && ( side === BackSide || side === DoubleSide ) ) { - - normalModel.negate(); - - } - - normalModel.applyMatrix3( _normalMatrix ).normalize(); - - } - - _face.vertexNormalsLength = faceVertexNormals.length; - - var vertexUvs = faceVertexUvs[ f ]; - - if ( vertexUvs !== undefined ) { - - for ( var u = 0; u < 3; u ++ ) { - - _face.uvs[ u ].copy( vertexUvs[ u ] ); - - } - - } - - _face.color = face.color; - _face.material = material; - - _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; - _face.renderOrder = object.renderOrder; - - _renderData.elements.push( _face ); - - } - - } - - } else if ( object instanceof Line ) { - - _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); - - if ( geometry instanceof BufferGeometry ) { - - var attributes = geometry.attributes; - - if ( attributes.position !== undefined ) { - - var positions = attributes.position.array; - - for ( var i = 0, l = positions.length; i < l; i += 3 ) { - - renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); - - } - - if ( attributes.color !== undefined ) { - - var colors = attributes.color.array; - - for ( var i = 0, l = colors.length; i < l; i += 3 ) { - - renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ); - - } - - } - - if ( geometry.index !== null ) { - - var indices = geometry.index.array; - - for ( var i = 0, l = indices.length; i < l; i += 2 ) { - - renderList.pushLine( indices[ i ], indices[ i + 1 ] ); - - } - - } else { - - var step = object instanceof LineSegments ? 2 : 1; - - for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) { - - renderList.pushLine( i, i + 1 ); - - } - - } - - } - - } else if ( geometry instanceof Geometry ) { - - var vertices = object.geometry.vertices; - - if ( vertices.length === 0 ) continue; - - v1 = getNextVertexInPool(); - v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix ); - - var step = object instanceof LineSegments ? 2 : 1; - - for ( var v = 1, vl = vertices.length; v < vl; v ++ ) { - - v1 = getNextVertexInPool(); - v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix ); - - if ( ( v + 1 ) % step > 0 ) continue; - - v2 = _vertexPool[ _vertexCount - 2 ]; - - _clippedVertex1PositionScreen.copy( v1.positionScreen ); - _clippedVertex2PositionScreen.copy( v2.positionScreen ); - - if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) { - - // Perform the perspective divide - _clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w ); - _clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w ); - - _line = getNextLineInPool(); - - _line.id = object.id; - _line.v1.positionScreen.copy( _clippedVertex1PositionScreen ); - _line.v2.positionScreen.copy( _clippedVertex2PositionScreen ); - - _line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z ); - _line.renderOrder = object.renderOrder; - - _line.material = object.material; - - if ( object.material.vertexColors ) { - - _line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] ); - _line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] ); - - } - - _renderData.elements.push( _line ); - - } - - } - - } - - } else if ( object instanceof Points ) { - - _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); - - if ( geometry instanceof Geometry ) { - - var vertices = object.geometry.vertices; - - for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { - - var vertex = vertices[ v ]; - - _vector4.set( vertex.x, vertex.y, vertex.z, 1 ); - _vector4.applyMatrix4( _modelViewProjectionMatrix ); - - pushPoint( _vector4, object, camera ); - - } - - } else if ( geometry instanceof BufferGeometry ) { - - var attributes = geometry.attributes; - - if ( attributes.position !== undefined ) { - - var positions = attributes.position.array; - - for ( var i = 0, l = positions.length; i < l; i += 3 ) { - - _vector4.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ], 1 ); - _vector4.applyMatrix4( _modelViewProjectionMatrix ); - - pushPoint( _vector4, object, camera ); - - } - - } - - } - - } else if ( object instanceof Sprite ) { - - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - _vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 ); - _vector4.applyMatrix4( _viewProjectionMatrix ); - - pushPoint( _vector4, object, camera ); - - } - - } - - if ( sortElements === true ) { - - _renderData.elements.sort( painterSort ); - - } - - return _renderData; - - }; - - function pushPoint( _vector4, object, camera ) { - - var invW = 1 / _vector4.w; - - _vector4.z *= invW; - - if ( _vector4.z >= - 1 && _vector4.z <= 1 ) { - - _sprite = getNextSpriteInPool(); - _sprite.id = object.id; - _sprite.x = _vector4.x * invW; - _sprite.y = _vector4.y * invW; - _sprite.z = _vector4.z; - _sprite.renderOrder = object.renderOrder; - _sprite.object = object; - - _sprite.rotation = object.rotation; - - _sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) ); - _sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) ); - - _sprite.material = object.material; - - _renderData.elements.push( _sprite ); - - } - - } - - // Pools - - function getNextObjectInPool() { - - if ( _objectCount === _objectPoolLength ) { - - var object = new RenderableObject(); - _objectPool.push( object ); - _objectPoolLength ++; - _objectCount ++; - return object; - - } - - return _objectPool[ _objectCount ++ ]; - - } - - function getNextVertexInPool() { - - if ( _vertexCount === _vertexPoolLength ) { - - var vertex = new RenderableVertex(); - _vertexPool.push( vertex ); - _vertexPoolLength ++; - _vertexCount ++; - return vertex; - - } - - return _vertexPool[ _vertexCount ++ ]; - - } - - function getNextFaceInPool() { - - if ( _faceCount === _facePoolLength ) { - - var face = new RenderableFace(); - _facePool.push( face ); - _facePoolLength ++; - _faceCount ++; - return face; - - } - - return _facePool[ _faceCount ++ ]; - - - } - - function getNextLineInPool() { - - if ( _lineCount === _linePoolLength ) { - - var line = new RenderableLine(); - _linePool.push( line ); - _linePoolLength ++; - _lineCount ++; - return line; - - } - - return _linePool[ _lineCount ++ ]; - - } - - function getNextSpriteInPool() { - - if ( _spriteCount === _spritePoolLength ) { - - var sprite = new RenderableSprite(); - _spritePool.push( sprite ); - _spritePoolLength ++; - _spriteCount ++; - return sprite; - - } - - return _spritePool[ _spriteCount ++ ]; - - } - - // - - function painterSort( a, b ) { - - if ( a.renderOrder !== b.renderOrder ) { - - return a.renderOrder - b.renderOrder; - - } else if ( a.z !== b.z ) { - - return b.z - a.z; - - } else if ( a.id !== b.id ) { - - return a.id - b.id; - - } else { - - return 0; - - } - - } - - function clipLine( s1, s2 ) { - - var alpha1 = 0, alpha2 = 1, - - // Calculate the boundary coordinate of each vertex for the near and far clip planes, - // Z = -1 and Z = +1, respectively. - - bc1near = s1.z + s1.w, - bc2near = s2.z + s2.w, - bc1far = - s1.z + s1.w, - bc2far = - s2.z + s2.w; - - if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { - - // Both vertices lie entirely within all clip planes. - return true; - - } else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) { - - // Both vertices lie entirely outside one of the clip planes. - return false; - - } else { - - // The line segment spans at least one clip plane. - - if ( bc1near < 0 ) { - - // v1 lies outside the near plane, v2 inside - alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) ); - - } else if ( bc2near < 0 ) { - - // v2 lies outside the near plane, v1 inside - alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) ); - - } - - if ( bc1far < 0 ) { - - // v1 lies outside the far plane, v2 inside - alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) ); - - } else if ( bc2far < 0 ) { - - // v2 lies outside the far plane, v2 inside - alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) ); - - } - - if ( alpha2 < alpha1 ) { - - // The line segment spans two boundaries, but is outside both of them. - // (This can't happen when we're only clipping against just near/far but good - // to leave the check here for future usage if other clip planes are added.) - return false; - - } else { - - // Update the s1 and s2 vertices to match the clipped line segment. - s1.lerp( s2, alpha1 ); - s2.lerp( s1, 1 - alpha2 ); - - return true; - - } - - } - - } - - }; - - var SVGObject = function ( node ) { - - Object3D.call( this ); - - this.node = node; - - }; - - SVGObject.prototype = Object.create( Object3D.prototype ); - SVGObject.prototype.constructor = SVGObject; - - var SVGRenderer = function () { - - var _this = this, - _renderData, _elements, _lights, - _projector = new Projector(), - _svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ), - _svgWidth, _svgHeight, _svgWidthHalf, _svgHeightHalf, - - _v1, _v2, _v3, - - _clipBox = new Box2(), - _elemBox = new Box2(), - - _color = new Color(), - _diffuseColor = new Color(), - _ambientLight = new Color(), - _directionalLights = new Color(), - _pointLights = new Color(), - _clearColor = new Color(), - - _vector3 = new Vector3(), // Needed for PointLight - _centroid = new Vector3(), - _normal = new Vector3(), - _normalViewMatrix = new Matrix3(), - - _viewMatrix = new Matrix4(), - _viewProjectionMatrix = new Matrix4(), - - _svgPathPool = [], - _svgNode, _pathCount = 0, - - _currentPath, _currentStyle, - - _quality = 1, _precision = null; - - this.domElement = _svg; - - this.autoClear = true; - this.sortObjects = true; - this.sortElements = true; - - this.overdraw = 0.5; - - this.info = { - - render: { - - vertices: 0, - faces: 0 - - } - - }; - - this.setQuality = function ( quality ) { - - switch ( quality ) { - - case "high": _quality = 1; break; - case "low": _quality = 0; break; - - } - - }; - - this.setClearColor = function ( color ) { - - _clearColor.set( color ); - - }; - - this.setPixelRatio = function () {}; - - this.setSize = function ( width, height ) { - - _svgWidth = width; _svgHeight = height; - _svgWidthHalf = _svgWidth / 2; _svgHeightHalf = _svgHeight / 2; - - _svg.setAttribute( 'viewBox', ( - _svgWidthHalf ) + ' ' + ( - _svgHeightHalf ) + ' ' + _svgWidth + ' ' + _svgHeight ); - _svg.setAttribute( 'width', _svgWidth ); - _svg.setAttribute( 'height', _svgHeight ); - - _clipBox.min.set( - _svgWidthHalf, - _svgHeightHalf ); - _clipBox.max.set( _svgWidthHalf, _svgHeightHalf ); - - }; - - this.getSize = function () { - - return { - width: _svgWidth, - height: _svgHeight - }; - - }; - - this.setPrecision = function ( precision ) { - - _precision = precision; - - }; - - function removeChildNodes() { - - _pathCount = 0; - - while ( _svg.childNodes.length > 0 ) { - - _svg.removeChild( _svg.childNodes[ 0 ] ); - - } - - } - - function convert( c ) { - - return _precision !== null ? c.toFixed( _precision ) : c; - - } - - this.clear = function () { - - removeChildNodes(); - _svg.style.backgroundColor = _clearColor.getStyle(); - - }; - - this.render = function ( scene, camera ) { - - if ( camera instanceof Camera === false ) { - - console.error( 'THREE.SVGRenderer.render: camera is not an instance of Camera.' ); - return; - - } - - var background = scene.background; - - if ( background && background.isColor ) { - - removeChildNodes(); - _svg.style.backgroundColor = background.getStyle(); - - } else if ( this.autoClear === true ) { - - this.clear(); - - } - - _this.info.render.vertices = 0; - _this.info.render.faces = 0; - - _viewMatrix.copy( camera.matrixWorldInverse ); - _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); - - _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); - _elements = _renderData.elements; - _lights = _renderData.lights; - - _normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse ); - - calculateLights( _lights ); - - // reset accumulated path - - _currentPath = ''; - _currentStyle = ''; - - for ( var e = 0, el = _elements.length; e < el; e ++ ) { - - var element = _elements[ e ]; - var material = element.material; - - if ( material === undefined || material.opacity === 0 ) continue; - - _elemBox.makeEmpty(); - - if ( element instanceof RenderableSprite ) { - - _v1 = element; - _v1.x *= _svgWidthHalf; _v1.y *= - _svgHeightHalf; - - renderSprite( _v1, element, material ); - - } else if ( element instanceof RenderableLine ) { - - _v1 = element.v1; _v2 = element.v2; - - _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; - _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; - - _elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] ); - - if ( _clipBox.intersectsBox( _elemBox ) === true ) { - - renderLine( _v1, _v2, element, material ); - - } - - } else if ( element instanceof RenderableFace ) { - - _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; - - if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue; - if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue; - if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue; - - _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; - _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; - _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf; - - if ( this.overdraw > 0 ) { - - expand( _v1.positionScreen, _v2.positionScreen, this.overdraw ); - expand( _v2.positionScreen, _v3.positionScreen, this.overdraw ); - expand( _v3.positionScreen, _v1.positionScreen, this.overdraw ); - - } - - _elemBox.setFromPoints( [ - _v1.positionScreen, - _v2.positionScreen, - _v3.positionScreen - ] ); - - if ( _clipBox.intersectsBox( _elemBox ) === true ) { - - renderFace3( _v1, _v2, _v3, element, material ); - - } - - } - - } - - flushPath(); // just to flush last svg:path - - scene.traverseVisible( function ( object ) { - - if ( object instanceof SVGObject ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyMatrix4( _viewProjectionMatrix ); - - if ( _vector3.z < - 1 || _vector3.z > 1 ) return; - - var x = _vector3.x * _svgWidthHalf; - var y = - _vector3.y * _svgHeightHalf; - - var node = object.node; - node.setAttribute( 'transform', 'translate(' + x + ',' + y + ')' ); - - _svg.appendChild( node ); - - } - - } ); - - }; - - function calculateLights( lights ) { - - _ambientLight.setRGB( 0, 0, 0 ); - _directionalLights.setRGB( 0, 0, 0 ); - _pointLights.setRGB( 0, 0, 0 ); - - for ( var l = 0, ll = lights.length; l < ll; l ++ ) { - - var light = lights[ l ]; - var lightColor = light.color; - - if ( light.isAmbientLight ) { - - _ambientLight.r += lightColor.r; - _ambientLight.g += lightColor.g; - _ambientLight.b += lightColor.b; - - } else if ( light.isDirectionalLight ) { - - _directionalLights.r += lightColor.r; - _directionalLights.g += lightColor.g; - _directionalLights.b += lightColor.b; - - } else if ( light.isPointLight ) { - - _pointLights.r += lightColor.r; - _pointLights.g += lightColor.g; - _pointLights.b += lightColor.b; - - } - - } - - } - - function calculateLight( lights, position, normal, color ) { - - for ( var l = 0, ll = lights.length; l < ll; l ++ ) { - - var light = lights[ l ]; - var lightColor = light.color; - - if ( light.isDirectionalLight ) { - - var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); - - var amount = normal.dot( lightPosition ); - - if ( amount <= 0 ) continue; - - amount *= light.intensity; - - color.r += lightColor.r * amount; - color.g += lightColor.g * amount; - color.b += lightColor.b * amount; - - } else if ( light.isPointLight ) { - - var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); - - var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); - - if ( amount <= 0 ) continue; - - amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); - - if ( amount == 0 ) continue; - - amount *= light.intensity; - - color.r += lightColor.r * amount; - color.g += lightColor.g * amount; - color.b += lightColor.b * amount; - - } - - } - - } - - function renderSprite( v1, element, material ) { - - var scaleX = element.scale.x * _svgWidthHalf; - var scaleY = element.scale.y * _svgHeightHalf; - - if ( material.isPointsMaterial ) { - - scaleX *= material.size; - scaleY *= material.size; - - } - - var path = 'M' + convert( v1.x - scaleX * 0.5 ) + ',' + convert( v1.y - scaleY * 0.5 ) + 'h' + convert( scaleX ) + 'v' + convert( scaleY ) + 'h' + convert( - scaleX ) + 'z'; - var style = ""; - - if ( material.isSpriteMaterial || material.isPointsMaterial ) { - - style = 'fill:' + material.color.getStyle() + ';fill-opacity:' + material.opacity; - - } - - addPath( style, path ); - - } - - function renderLine( v1, v2, element, material ) { - - var path = 'M' + convert( v1.positionScreen.x ) + ',' + convert( v1.positionScreen.y ) + 'L' + convert( v2.positionScreen.x ) + ',' + convert( v2.positionScreen.y ); - - if ( material.isLineBasicMaterial ) { - - var style = 'fill:none;stroke:' + material.color.getStyle() + ';stroke-opacity:' + material.opacity + ';stroke-width:' + material.linewidth + ';stroke-linecap:' + material.linecap; - - if ( material.isLineDashedMaterial ) { - - style = style + ';stroke-dasharray:' + material.dashSize + "," + material.gapSize; - - } - - addPath( style, path ); - - } - - } - - function renderFace3( v1, v2, v3, element, material ) { - - _this.info.render.vertices += 3; - _this.info.render.faces ++; - - var path = 'M' + convert( v1.positionScreen.x ) + ',' + convert( v1.positionScreen.y ) + 'L' + convert( v2.positionScreen.x ) + ',' + convert( v2.positionScreen.y ) + 'L' + convert( v3.positionScreen.x ) + ',' + convert( v3.positionScreen.y ) + 'z'; - var style = ''; - - if ( material.isMeshBasicMaterial ) { - - _color.copy( material.color ); - - if ( material.vertexColors ) { - - _color.multiply( element.color ); - - } - - } else if ( material.isMeshLambertMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial ) { - - _diffuseColor.copy( material.color ); - - if ( material.vertexColors ) { - - _diffuseColor.multiply( element.color ); - - } - - _color.copy( _ambientLight ); - - _centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 ); - - calculateLight( _lights, _centroid, element.normalModel, _color ); - - _color.multiply( _diffuseColor ).add( material.emissive ); - - } else if ( material.isMeshNormalMaterial ) { - - _normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ).normalize(); - - _color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); - - } - - if ( material.wireframe ) { - - style = 'fill:none;stroke:' + _color.getStyle() + ';stroke-opacity:' + material.opacity + ';stroke-width:' + material.wireframeLinewidth + ';stroke-linecap:' + material.wireframeLinecap + ';stroke-linejoin:' + material.wireframeLinejoin; - - } else { - - style = 'fill:' + _color.getStyle() + ';fill-opacity:' + material.opacity; - - } - - addPath( style, path ); - - } - - // Hide anti-alias gaps - - function expand( v1, v2, pixels ) { - - var x = v2.x - v1.x, y = v2.y - v1.y, - det = x * x + y * y, idet; - - if ( det === 0 ) return; - - idet = pixels / Math.sqrt( det ); - - x *= idet; y *= idet; - - v2.x += x; v2.y += y; - v1.x -= x; v1.y -= y; - - } - - function addPath( style, path ) { - - if ( _currentStyle === style ) { - - _currentPath += path; - - } else { - - flushPath(); - - _currentStyle = style; - _currentPath = path; - - } - - } - - function flushPath() { - - if ( _currentPath ) { - - _svgNode = getPathNode( _pathCount ++ ); - _svgNode.setAttribute( 'd', _currentPath ); - _svgNode.setAttribute( 'style', _currentStyle ); - _svg.appendChild( _svgNode ); - - } - - _currentPath = ''; - _currentStyle = ''; - - } - - function getPathNode( id ) { - - if ( _svgPathPool[ id ] == null ) { - - _svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); - - if ( _quality == 0 ) { - - _svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed - - } - - return _svgPathPool[ id ]; - - } - - return _svgPathPool[ id ]; - - } - - }; - - class Marker { - constructor(scene, map) { - this.scene = scene; - this.map = map; - this.items = []; - } - - add(modelId, svg, scale = 1.0, offset = { x: 0, y: 0, z: 0 }, onclickListener) { - if (!modelId || this._hasMarker(modelId)) { - return; - } - - const item = this._addMarkerAboveModel(modelId, svg, (scale = 1.0), (offset = { x: 0, y: 0, z: 0 }), onclickListener); - if (!item) { - return; - } - - this._addToItems(item); - this.map.triggerRepaint(); - } - - remove(modelId) { - const item = this._getItem(modelId); - if (!item) { - return; - } - - document.body.removeChild(item.renderer.domElement); - item.model.remove(item.marker); - this._removeFromItems(modelId); - } - - clear() { - this.items.forEach((e) => { - this.remove(e.modelId); - }); - - this.items = []; - } - - getMarkers() { - return this.items; - } - - _createRenderer() { - const svgRenderer = new SVGRenderer(); - svgRenderer.setSize(window.innerWidth, window.innerHeight); - svgRenderer.setQuality('low'); - document.body.appendChild(svgRenderer.domElement); - window.addEventListener('resize', () => { - svgRenderer.setSize(window.innerWidth, window.innerHeight); - }); - - return svgRenderer; - } - - _addMarkerAboveModel(modelId, svg, scale = 1.0, offset = { x: 0, y: 0, z: 0 }, onclickListener) { - const model = GetModel(modelId, this.scene.children); - if (!model) { - return; - } - - let marker = {}; - const renderer = this._createRenderer(); - const svgScene = new Scene(); - const loader = new FileLoader(); - const box = new Box3().setFromObject(model); - const boxCenter = box.getCenter(); - - // weird stuff, apparantly setting box min and max from world to local fixes postition issues but looks like it should do nothing - box.min = model.worldToLocal(box.min); - box.max = model.worldToLocal(box.max); - - const center = model.worldToLocal(boxCenter); - - loader.load(svg, (data) => { - const node = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - const parser = new DOMParser(); - const doc = parser.parseFromString(data, 'image/svg+xml'); - - node.appendChild(doc.documentElement); - if (onclickListener) { - node.firstChild.addEventListener('mousedown', onclickListener.bind(this)); - node.firstChild.onmouseover = () => { - node.firstChild.style = 'cursor: pointer;'; - }; - node.firstChild.onmouseout = () => { - node.firstChild.style = 'cursor: unset;'; - }; - } - - marker = new SVGObject(node); - //marker.applyMatrix4(new THREE.Matrix4().makeScale(scale, scale, scale)); - marker.position.x = center.x + offset.x; - marker.position.y = box.max.y + offset.y; - marker.position.z = center.z + offset.z; - svgScene.add(marker); - //svgScene.applyMatrix4( model.matrixWorld ); - model.add(svgScene); - }); - - return { - modelId: modelId, - model: model, - marker: svgScene, - renderer: renderer - }; - } - - _addToItems(item) { - this.items.push(item); - } - - _removeFromItems(modelId) { - this.items = this.items.filter((e) => { - return e.modelId !== modelId; - }); - } - - _hasMarker(modelId) { - return this._getItem(modelId) !== undefined; - } - - _getItem(modelId) { - for (let i = 0; i < this.items.length; i++) { - if (this.items[i].modelId === modelId) { - return this.items[i]; - } - } - } - } - - /** - * Full-screen textured quad shader - */ - - var CopyShader = { - - uniforms: { - - "tDiffuse": { value: null }, - "opacity": { value: 1.0 } - - }, - - vertexShader: [ - - "varying vec2 vUv;", - - "void main() {", - - " vUv = uv;", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - - "}" - - ].join( "\n" ), - - fragmentShader: [ - - "uniform float opacity;", - - "uniform sampler2D tDiffuse;", - - "varying vec2 vUv;", - - "void main() {", - - " vec4 texel = texture2D( tDiffuse, vUv );", - " gl_FragColor = opacity * texel;", - - "}" - - ].join( "\n" ) - - }; - - function Pass() { - - // if set to true, the pass is processed by the composer - this.enabled = true; - - // if set to true, the pass indicates to swap read and write buffer after rendering - this.needsSwap = true; - - // if set to true, the pass clears its buffer before rendering - this.clear = false; - - // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. - this.renderToScreen = false; - - } - - Object.assign( Pass.prototype, { - - setSize: function ( /* width, height */ ) {}, - - render: function ( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) { - - console.error( 'THREE.Pass: .render() must be implemented in derived pass.' ); - - } - - } ); - - // Helper for passes that need to fill the viewport with a single quad. - - Pass.FullScreenQuad = ( function () { - - var camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); - var geometry = new PlaneBufferGeometry( 2, 2 ); - - var FullScreenQuad = function ( material ) { - - this._mesh = new Mesh( geometry, material ); - - }; - - Object.defineProperty( FullScreenQuad.prototype, 'material', { - - get: function () { - - return this._mesh.material; - - }, - - set: function ( value ) { - - this._mesh.material = value; - - } - - } ); - - Object.assign( FullScreenQuad.prototype, { - - dispose: function () { - - this._mesh.geometry.dispose(); - - }, - - render: function ( renderer ) { - - renderer.render( this._mesh, camera ); - - } - - } ); - - return FullScreenQuad; - - } )(); - - var ShaderPass = function ( shader, textureID ) { - - Pass.call( this ); - - this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse"; - - if ( shader instanceof ShaderMaterial ) { - - this.uniforms = shader.uniforms; - - this.material = shader; - - } else if ( shader ) { - - this.uniforms = UniformsUtils.clone( shader.uniforms ); - - this.material = new ShaderMaterial( { - - defines: Object.assign( {}, shader.defines ), - uniforms: this.uniforms, - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader - - } ); - - } - - this.fsQuad = new Pass.FullScreenQuad( this.material ); - - }; - - ShaderPass.prototype = Object.assign( Object.create( Pass.prototype ), { - - constructor: ShaderPass, - - render: function ( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { - - if ( this.uniforms[ this.textureID ] ) { - - this.uniforms[ this.textureID ].value = readBuffer.texture; - - } - - this.fsQuad.material = this.material; - - if ( this.renderToScreen ) { - - renderer.setRenderTarget( null ); - this.fsQuad.render( renderer ); - - } else { - - renderer.setRenderTarget( writeBuffer ); - // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 - if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); - this.fsQuad.render( renderer ); - - } - - } - - } ); - - var MaskPass = function ( scene, camera ) { - - Pass.call( this ); - - this.scene = scene; - this.camera = camera; - - this.clear = true; - this.needsSwap = false; - - this.inverse = false; - - }; - - MaskPass.prototype = Object.assign( Object.create( Pass.prototype ), { - - constructor: MaskPass, - - render: function ( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { - - var context = renderer.getContext(); - var state = renderer.state; - - // don't update color or depth - - state.buffers.color.setMask( false ); - state.buffers.depth.setMask( false ); - - // lock buffers - - state.buffers.color.setLocked( true ); - state.buffers.depth.setLocked( true ); - - // set up stencil - - var writeValue, clearValue; - - if ( this.inverse ) { - - writeValue = 0; - clearValue = 1; - - } else { - - writeValue = 1; - clearValue = 0; - - } - - state.buffers.stencil.setTest( true ); - state.buffers.stencil.setOp( context.REPLACE, context.REPLACE, context.REPLACE ); - state.buffers.stencil.setFunc( context.ALWAYS, writeValue, 0xffffffff ); - state.buffers.stencil.setClear( clearValue ); - state.buffers.stencil.setLocked( true ); - - // draw into the stencil buffer - - renderer.setRenderTarget( readBuffer ); - if ( this.clear ) renderer.clear(); - renderer.render( this.scene, this.camera ); - - renderer.setRenderTarget( writeBuffer ); - if ( this.clear ) renderer.clear(); - renderer.render( this.scene, this.camera ); - - // unlock color and depth buffer for subsequent rendering - - state.buffers.color.setLocked( false ); - state.buffers.depth.setLocked( false ); - - // only render where stencil is set to 1 - - state.buffers.stencil.setLocked( false ); - state.buffers.stencil.setFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1 - state.buffers.stencil.setOp( context.KEEP, context.KEEP, context.KEEP ); - state.buffers.stencil.setLocked( true ); - - } - - } ); - - - var ClearMaskPass = function () { - - Pass.call( this ); - - this.needsSwap = false; - - }; - - ClearMaskPass.prototype = Object.create( Pass.prototype ); - - Object.assign( ClearMaskPass.prototype, { - - render: function ( renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */ ) { - - renderer.state.buffers.stencil.setLocked( false ); - renderer.state.buffers.stencil.setTest( false ); - - } - - } ); - - var EffectComposer = function ( renderer, renderTarget ) { - - this.renderer = renderer; - - if ( renderTarget === undefined ) { - - var parameters = { - minFilter: LinearFilter, - magFilter: LinearFilter, - format: RGBAFormat - }; - - var size = renderer.getSize( new Vector2() ); - this._pixelRatio = renderer.getPixelRatio(); - this._width = size.width; - this._height = size.height; - - renderTarget = new WebGLRenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, parameters ); - renderTarget.texture.name = 'EffectComposer.rt1'; - - } else { - - this._pixelRatio = 1; - this._width = renderTarget.width; - this._height = renderTarget.height; - - } - - this.renderTarget1 = renderTarget; - this.renderTarget2 = renderTarget.clone(); - this.renderTarget2.texture.name = 'EffectComposer.rt2'; - - this.writeBuffer = this.renderTarget1; - this.readBuffer = this.renderTarget2; - - this.renderToScreen = true; - - this.passes = []; - - // dependencies - - if ( CopyShader === undefined ) { - - console.error( 'THREE.EffectComposer relies on CopyShader' ); - - } - - if ( ShaderPass === undefined ) { - - console.error( 'THREE.EffectComposer relies on ShaderPass' ); - - } - - this.copyPass = new ShaderPass( CopyShader ); - - this.clock = new Clock(); - - }; - - Object.assign( EffectComposer.prototype, { - - swapBuffers: function () { - - var tmp = this.readBuffer; - this.readBuffer = this.writeBuffer; - this.writeBuffer = tmp; - - }, - - addPass: function ( pass ) { - - this.passes.push( pass ); - pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); - - }, - - insertPass: function ( pass, index ) { - - this.passes.splice( index, 0, pass ); - pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); - - }, - - isLastEnabledPass: function ( passIndex ) { - - for ( var i = passIndex + 1; i < this.passes.length; i ++ ) { - - if ( this.passes[ i ].enabled ) { - - return false; - - } - - } - - return true; - - }, - - render: function ( deltaTime ) { - - // deltaTime value is in seconds - - if ( deltaTime === undefined ) { - - deltaTime = this.clock.getDelta(); - - } - - var currentRenderTarget = this.renderer.getRenderTarget(); - - var maskActive = false; - - var pass, i, il = this.passes.length; - - for ( i = 0; i < il; i ++ ) { - - pass = this.passes[ i ]; - - if ( pass.enabled === false ) continue; - - pass.renderToScreen = ( this.renderToScreen && this.isLastEnabledPass( i ) ); - pass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive ); - - if ( pass.needsSwap ) { - - if ( maskActive ) { - - var context = this.renderer.getContext(); - var stencil = this.renderer.state.buffers.stencil; - - //context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff ); - stencil.setFunc( context.NOTEQUAL, 1, 0xffffffff ); - - this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime ); - - //context.stencilFunc( context.EQUAL, 1, 0xffffffff ); - stencil.setFunc( context.EQUAL, 1, 0xffffffff ); - - } - - this.swapBuffers(); - - } - - if ( MaskPass !== undefined ) { - - if ( pass instanceof MaskPass ) { - - maskActive = true; - - } else if ( pass instanceof ClearMaskPass ) { - - maskActive = false; - - } - - } - - } - - this.renderer.setRenderTarget( currentRenderTarget ); - - }, - - reset: function ( renderTarget ) { - - if ( renderTarget === undefined ) { - - var size = this.renderer.getSize( new Vector2() ); - this._pixelRatio = this.renderer.getPixelRatio(); - this._width = size.width; - this._height = size.height; - - renderTarget = this.renderTarget1.clone(); - renderTarget.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); - - } - - this.renderTarget1.dispose(); - this.renderTarget2.dispose(); - this.renderTarget1 = renderTarget; - this.renderTarget2 = renderTarget.clone(); - - this.writeBuffer = this.renderTarget1; - this.readBuffer = this.renderTarget2; - - }, - - setSize: function ( width, height ) { - - this._width = width; - this._height = height; - - var effectiveWidth = this._width * this._pixelRatio; - var effectiveHeight = this._height * this._pixelRatio; - - this.renderTarget1.setSize( effectiveWidth, effectiveHeight ); - this.renderTarget2.setSize( effectiveWidth, effectiveHeight ); - - for ( var i = 0; i < this.passes.length; i ++ ) { - - this.passes[ i ].setSize( effectiveWidth, effectiveHeight ); - - } - - }, - - setPixelRatio: function ( pixelRatio ) { - - this._pixelRatio = pixelRatio; - - this.setSize( this._width, this._height ); - - } - - } ); - - - var Pass$1 = function () { - - // if set to true, the pass is processed by the composer - this.enabled = true; - - // if set to true, the pass indicates to swap read and write buffer after rendering - this.needsSwap = true; - - // if set to true, the pass clears its buffer before rendering - this.clear = false; - - // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. - this.renderToScreen = false; - - }; - - Object.assign( Pass$1.prototype, { - - setSize: function ( /* width, height */ ) {}, - - render: function ( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) { - - console.error( 'THREE.Pass: .render() must be implemented in derived pass.' ); - - } - - } ); - - // Helper for passes that need to fill the viewport with a single quad. - Pass$1.FullScreenQuad = ( function () { - - var camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); - var geometry = new PlaneBufferGeometry( 2, 2 ); - - var FullScreenQuad = function ( material ) { - - this._mesh = new Mesh( geometry, material ); - - }; - - Object.defineProperty( FullScreenQuad.prototype, 'material', { - - get: function () { - - return this._mesh.material; - - }, - - set: function ( value ) { - - this._mesh.material = value; - - } - - } ); - - Object.assign( FullScreenQuad.prototype, { - - dispose: function () { - - this._mesh.geometry.dispose(); - - }, - - render: function ( renderer ) { - - renderer.render( this._mesh, camera ); - - } - - } ); - - return FullScreenQuad; - - } )(); - - // Ported from Stefan Gustavson's java implementation - // http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf - // Read Stefan's excellent paper for details on how this code works. - // - // Sean McCullough banksean@gmail.com - // - // Added 4D noise - - /** - * You can pass in a random number generator object if you like. - * It is assumed to have a random() method. - */ - var SimplexNoise = function ( r ) { - - if ( r == undefined ) r = Math; - this.grad3 = [[ 1, 1, 0 ], [ - 1, 1, 0 ], [ 1, - 1, 0 ], [ - 1, - 1, 0 ], - [ 1, 0, 1 ], [ - 1, 0, 1 ], [ 1, 0, - 1 ], [ - 1, 0, - 1 ], - [ 0, 1, 1 ], [ 0, - 1, 1 ], [ 0, 1, - 1 ], [ 0, - 1, - 1 ]]; - - this.grad4 = [[ 0, 1, 1, 1 ], [ 0, 1, 1, - 1 ], [ 0, 1, - 1, 1 ], [ 0, 1, - 1, - 1 ], - [ 0, - 1, 1, 1 ], [ 0, - 1, 1, - 1 ], [ 0, - 1, - 1, 1 ], [ 0, - 1, - 1, - 1 ], - [ 1, 0, 1, 1 ], [ 1, 0, 1, - 1 ], [ 1, 0, - 1, 1 ], [ 1, 0, - 1, - 1 ], - [ - 1, 0, 1, 1 ], [ - 1, 0, 1, - 1 ], [ - 1, 0, - 1, 1 ], [ - 1, 0, - 1, - 1 ], - [ 1, 1, 0, 1 ], [ 1, 1, 0, - 1 ], [ 1, - 1, 0, 1 ], [ 1, - 1, 0, - 1 ], - [ - 1, 1, 0, 1 ], [ - 1, 1, 0, - 1 ], [ - 1, - 1, 0, 1 ], [ - 1, - 1, 0, - 1 ], - [ 1, 1, 1, 0 ], [ 1, 1, - 1, 0 ], [ 1, - 1, 1, 0 ], [ 1, - 1, - 1, 0 ], - [ - 1, 1, 1, 0 ], [ - 1, 1, - 1, 0 ], [ - 1, - 1, 1, 0 ], [ - 1, - 1, - 1, 0 ]]; - - this.p = []; - - for ( var i = 0; i < 256; i ++ ) { - - this.p[ i ] = Math.floor( r.random() * 256 ); - - } - - // To remove the need for index wrapping, double the permutation table length - this.perm = []; - - for ( var i = 0; i < 512; i ++ ) { - - this.perm[ i ] = this.p[ i & 255 ]; - - } - - // A lookup table to traverse the simplex around a given point in 4D. - // Details can be found where this table is used, in the 4D noise method. - this.simplex = [ - [ 0, 1, 2, 3 ], [ 0, 1, 3, 2 ], [ 0, 0, 0, 0 ], [ 0, 2, 3, 1 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 1, 2, 3, 0 ], - [ 0, 2, 1, 3 ], [ 0, 0, 0, 0 ], [ 0, 3, 1, 2 ], [ 0, 3, 2, 1 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 1, 3, 2, 0 ], - [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], - [ 1, 2, 0, 3 ], [ 0, 0, 0, 0 ], [ 1, 3, 0, 2 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 2, 3, 0, 1 ], [ 2, 3, 1, 0 ], - [ 1, 0, 2, 3 ], [ 1, 0, 3, 2 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 2, 0, 3, 1 ], [ 0, 0, 0, 0 ], [ 2, 1, 3, 0 ], - [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], - [ 2, 0, 1, 3 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 3, 0, 1, 2 ], [ 3, 0, 2, 1 ], [ 0, 0, 0, 0 ], [ 3, 1, 2, 0 ], - [ 2, 1, 0, 3 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 3, 1, 0, 2 ], [ 0, 0, 0, 0 ], [ 3, 2, 0, 1 ], [ 3, 2, 1, 0 ]]; - - }; - - SimplexNoise.prototype.dot = function ( g, x, y ) { - - return g[ 0 ] * x + g[ 1 ] * y; - - }; - - SimplexNoise.prototype.dot3 = function ( g, x, y, z ) { - - return g[ 0 ] * x + g[ 1 ] * y + g[ 2 ] * z; - - }; - - SimplexNoise.prototype.dot4 = function ( g, x, y, z, w ) { - - return g[ 0 ] * x + g[ 1 ] * y + g[ 2 ] * z + g[ 3 ] * w; - - }; - - SimplexNoise.prototype.noise = function ( xin, yin ) { - - var n0, n1, n2; // Noise contributions from the three corners - // Skew the input space to determine which simplex cell we're in - var F2 = 0.5 * ( Math.sqrt( 3.0 ) - 1.0 ); - var s = ( xin + yin ) * F2; // Hairy factor for 2D - var i = Math.floor( xin + s ); - var j = Math.floor( yin + s ); - var G2 = ( 3.0 - Math.sqrt( 3.0 ) ) / 6.0; - var t = ( i + j ) * G2; - var X0 = i - t; // Unskew the cell origin back to (x,y) space - var Y0 = j - t; - var x0 = xin - X0; // The x,y distances from the cell origin - var y0 = yin - Y0; - // For the 2D case, the simplex shape is an equilateral triangle. - // Determine which simplex we are in. - var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords - if ( x0 > y0 ) { - - i1 = 1; j1 = 0; - - // lower triangle, XY order: (0,0)->(1,0)->(1,1) - - } else { - - i1 = 0; j1 = 1; - - } // upper triangle, YX order: (0,0)->(0,1)->(1,1) - - // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and - // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where - // c = (3-sqrt(3))/6 - var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords - var y1 = y0 - j1 + G2; - var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords - var y2 = y0 - 1.0 + 2.0 * G2; - // Work out the hashed gradient indices of the three simplex corners - var ii = i & 255; - var jj = j & 255; - var gi0 = this.perm[ ii + this.perm[ jj ] ] % 12; - var gi1 = this.perm[ ii + i1 + this.perm[ jj + j1 ] ] % 12; - var gi2 = this.perm[ ii + 1 + this.perm[ jj + 1 ] ] % 12; - // Calculate the contribution from the three corners - var t0 = 0.5 - x0 * x0 - y0 * y0; - if ( t0 < 0 ) n0 = 0.0; - else { - - t0 *= t0; - n0 = t0 * t0 * this.dot( this.grad3[ gi0 ], x0, y0 ); // (x,y) of grad3 used for 2D gradient - - } - - var t1 = 0.5 - x1 * x1 - y1 * y1; - if ( t1 < 0 ) n1 = 0.0; - else { - - t1 *= t1; - n1 = t1 * t1 * this.dot( this.grad3[ gi1 ], x1, y1 ); - - } - - var t2 = 0.5 - x2 * x2 - y2 * y2; - if ( t2 < 0 ) n2 = 0.0; - else { - - t2 *= t2; - n2 = t2 * t2 * this.dot( this.grad3[ gi2 ], x2, y2 ); - - } - - // Add contributions from each corner to get the final noise value. - // The result is scaled to return values in the interval [-1,1]. - return 70.0 * ( n0 + n1 + n2 ); - - }; - - // 3D simplex noise - SimplexNoise.prototype.noise3d = function ( xin, yin, zin ) { - - var n0, n1, n2, n3; // Noise contributions from the four corners - // Skew the input space to determine which simplex cell we're in - var F3 = 1.0 / 3.0; - var s = ( xin + yin + zin ) * F3; // Very nice and simple skew factor for 3D - var i = Math.floor( xin + s ); - var j = Math.floor( yin + s ); - var k = Math.floor( zin + s ); - var G3 = 1.0 / 6.0; // Very nice and simple unskew factor, too - var t = ( i + j + k ) * G3; - var X0 = i - t; // Unskew the cell origin back to (x,y,z) space - var Y0 = j - t; - var Z0 = k - t; - var x0 = xin - X0; // The x,y,z distances from the cell origin - var y0 = yin - Y0; - var z0 = zin - Z0; - // For the 3D case, the simplex shape is a slightly irregular tetrahedron. - // Determine which simplex we are in. - var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords - var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords - if ( x0 >= y0 ) { - - if ( y0 >= z0 ) { - - i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; - - // X Y Z order - - } else if ( x0 >= z0 ) { - - i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; - - // X Z Y order - - } else { - - i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; - - } // Z X Y order - - } else { // x0 y0 ) ? 32 : 0; - var c2 = ( x0 > z0 ) ? 16 : 0; - var c3 = ( y0 > z0 ) ? 8 : 0; - var c4 = ( x0 > w0 ) ? 4 : 0; - var c5 = ( y0 > w0 ) ? 2 : 0; - var c6 = ( z0 > w0 ) ? 1 : 0; - var c = c1 + c2 + c3 + c4 + c5 + c6; - var i1, j1, k1, l1; // The integer offsets for the second simplex corner - var i2, j2, k2, l2; // The integer offsets for the third simplex corner - var i3, j3, k3, l3; // The integer offsets for the fourth simplex corner - // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. - // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0; - j1 = simplex[ c ][ 1 ] >= 3 ? 1 : 0; - k1 = simplex[ c ][ 2 ] >= 3 ? 1 : 0; - l1 = simplex[ c ][ 3 ] >= 3 ? 1 : 0; - // The number 2 in the "simplex" array is at the second largest coordinate. - i2 = simplex[ c ][ 0 ] >= 2 ? 1 : 0; - j2 = simplex[ c ][ 1 ] >= 2 ? 1 : 0; k2 = simplex[ c ][ 2 ] >= 2 ? 1 : 0; - l2 = simplex[ c ][ 3 ] >= 2 ? 1 : 0; - // The number 1 in the "simplex" array is at the second smallest coordinate. - i3 = simplex[ c ][ 0 ] >= 1 ? 1 : 0; - j3 = simplex[ c ][ 1 ] >= 1 ? 1 : 0; - k3 = simplex[ c ][ 2 ] >= 1 ? 1 : 0; - l3 = simplex[ c ][ 3 ] >= 1 ? 1 : 0; - // The fifth corner has all coordinate offsets = 1, so no need to look that up. - var x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords - var y1 = y0 - j1 + G4; - var z1 = z0 - k1 + G4; - var w1 = w0 - l1 + G4; - var x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords - var y2 = y0 - j2 + 2.0 * G4; - var z2 = z0 - k2 + 2.0 * G4; - var w2 = w0 - l2 + 2.0 * G4; - var x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords - var y3 = y0 - j3 + 3.0 * G4; - var z3 = z0 - k3 + 3.0 * G4; - var w3 = w0 - l3 + 3.0 * G4; - var x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords - var y4 = y0 - 1.0 + 4.0 * G4; - var z4 = z0 - 1.0 + 4.0 * G4; - var w4 = w0 - 1.0 + 4.0 * G4; - // Work out the hashed gradient indices of the five simplex corners - var ii = i & 255; - var jj = j & 255; - var kk = k & 255; - var ll = l & 255; - var gi0 = perm[ ii + perm[ jj + perm[ kk + perm[ ll ] ] ] ] % 32; - var gi1 = perm[ ii + i1 + perm[ jj + j1 + perm[ kk + k1 + perm[ ll + l1 ] ] ] ] % 32; - var gi2 = perm[ ii + i2 + perm[ jj + j2 + perm[ kk + k2 + perm[ ll + l2 ] ] ] ] % 32; - var gi3 = perm[ ii + i3 + perm[ jj + j3 + perm[ kk + k3 + perm[ ll + l3 ] ] ] ] % 32; - var gi4 = perm[ ii + 1 + perm[ jj + 1 + perm[ kk + 1 + perm[ ll + 1 ] ] ] ] % 32; - // Calculate the contribution from the five corners - var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0; - if ( t0 < 0 ) n0 = 0.0; - else { - - t0 *= t0; - n0 = t0 * t0 * this.dot4( grad4[ gi0 ], x0, y0, z0, w0 ); - - } - - var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1; - if ( t1 < 0 ) n1 = 0.0; - else { - - t1 *= t1; - n1 = t1 * t1 * this.dot4( grad4[ gi1 ], x1, y1, z1, w1 ); - - } - - var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2; - if ( t2 < 0 ) n2 = 0.0; - else { - - t2 *= t2; - n2 = t2 * t2 * this.dot4( grad4[ gi2 ], x2, y2, z2, w2 ); - - } - - var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3; - if ( t3 < 0 ) n3 = 0.0; - else { - - t3 *= t3; - n3 = t3 * t3 * this.dot4( grad4[ gi3 ], x3, y3, z3, w3 ); - - } - - var t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4; - if ( t4 < 0 ) n4 = 0.0; - else { - - t4 *= t4; - n4 = t4 * t4 * this.dot4( grad4[ gi4 ], x4, y4, z4, w4 ); - - } - - // Sum up and scale the result to cover the range [-1,1] - return 27.0 * ( n0 + n1 + n2 + n3 + n4 ); - - }; - - /** - * References: - * http://john-chapman-graphics.blogspot.com/2013/01/ssao-tutorial.html - * https://learnopengl.com/Advanced-Lighting/SSAO - * https://github.com/McNopper/OpenGL/blob/master/Example28/shader/ssao.frag.glsl - */ - - var SSAOShader = { - - defines: { - "PERSPECTIVE_CAMERA": 1, - "KERNEL_SIZE": 32 - }, - - uniforms: { - - "tDiffuse": { value: null }, - "tNormal": { value: null }, - "tDepth": { value: null }, - "tNoise": { value: null }, - "kernel": { value: null }, - "cameraNear": { value: null }, - "cameraFar": { value: null }, - "resolution": { value: new Vector2() }, - "cameraProjectionMatrix": { value: new Matrix4() }, - "cameraInverseProjectionMatrix": { value: new Matrix4() }, - "kernelRadius": { value: 8 }, - "minDistance": { value: 0.005 }, - "maxDistance": { value: 0.05 }, - - }, - - vertexShader: [ - - "varying vec2 vUv;", - - "void main() {", - - " vUv = uv;", - - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - - "}" - - ].join( "\n" ), - - fragmentShader: [ - - "uniform sampler2D tDiffuse;", - "uniform sampler2D tNormal;", - "uniform sampler2D tDepth;", - "uniform sampler2D tNoise;", - - "uniform vec3 kernel[ KERNEL_SIZE ];", - - "uniform vec2 resolution;", - - "uniform float cameraNear;", - "uniform float cameraFar;", - "uniform mat4 cameraProjectionMatrix;", - "uniform mat4 cameraInverseProjectionMatrix;", - - "uniform float kernelRadius;", - "uniform float minDistance;", // avoid artifacts caused by neighbour fragments with minimal depth difference - "uniform float maxDistance;", // avoid the influence of fragments which are too far away - - "varying vec2 vUv;", - - "#include ", - - "float getDepth( const in vec2 screenPosition ) {", - - " return texture2D( tDepth, screenPosition ).x;", - - "}", - - "float getLinearDepth( const in vec2 screenPosition ) {", - - " #if PERSPECTIVE_CAMERA == 1", - - " float fragCoordZ = texture2D( tDepth, screenPosition ).x;", - " float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );", - " return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );", - - " #else", - - " return texture2D( depthSampler, coord ).x;", - - " #endif", - - "}", - - "float getViewZ( const in float depth ) {", - - " #if PERSPECTIVE_CAMERA == 1", - - " return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );", - - " #else", - - " return orthographicDepthToViewZ( depth, cameraNear, cameraFar );", - - " #endif", - - "}", - - "vec3 getViewPosition( const in vec2 screenPosition, const in float depth, const in float viewZ ) {", - - " float clipW = cameraProjectionMatrix[2][3] * viewZ + cameraProjectionMatrix[3][3];", - - " vec4 clipPosition = vec4( ( vec3( screenPosition, depth ) - 0.5 ) * 2.0, 1.0 );", - - " clipPosition *= clipW; // unprojection.", - - " return ( cameraInverseProjectionMatrix * clipPosition ).xyz;", - - "}", - - "vec3 getViewNormal( const in vec2 screenPosition ) {", - - " return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz );", - - "}", - - "void main() {", - - " float depth = getDepth( vUv );", - " float viewZ = getViewZ( depth );", - - " vec3 viewPosition = getViewPosition( vUv, depth, viewZ );", - " vec3 viewNormal = getViewNormal( vUv );", - - " vec2 noiseScale = vec2( resolution.x / 4.0, resolution.y / 4.0 );", - " vec3 random = texture2D( tNoise, vUv * noiseScale ).xyz;", - - // compute matrix used to reorient a kernel vector - - " vec3 tangent = normalize( random - viewNormal * dot( random, viewNormal ) );", - " vec3 bitangent = cross( viewNormal, tangent );", - " mat3 kernelMatrix = mat3( tangent, bitangent, viewNormal );", - - " float occlusion = 0.0;", - - " for ( int i = 0; i < KERNEL_SIZE; i ++ ) {", - - " vec3 sampleVector = kernelMatrix * kernel[ i ];", // reorient sample vector in view space - " vec3 samplePoint = viewPosition + ( sampleVector * kernelRadius );", // calculate sample point - - " vec4 samplePointNDC = cameraProjectionMatrix * vec4( samplePoint, 1.0 );", // project point and calculate NDC - " samplePointNDC /= samplePointNDC.w;", - - " vec2 samplePointUv = samplePointNDC.xy * 0.5 + 0.5;", // compute uv coordinates - - " float realDepth = getLinearDepth( samplePointUv );", // get linear depth from depth texture - " float sampleDepth = viewZToOrthographicDepth( samplePoint.z, cameraNear, cameraFar );", // compute linear depth of the sample view Z value - " float delta = sampleDepth - realDepth;", - - " if ( delta > minDistance && delta < maxDistance ) {", // if fragment is before sample point, increase occlusion - - " occlusion += 1.0;", - - " }", - - " }", - - " occlusion = clamp( occlusion / float( KERNEL_SIZE ), 0.0, 1.0 );", - - " gl_FragColor = vec4( vec3( 1.0 - occlusion ), 1.0 );", - - "}" - - ].join( "\n" ) - - }; - - var SSAODepthShader = { - - defines: { - "PERSPECTIVE_CAMERA": 1 - }, - - uniforms: { - - "tDepth": { value: null }, - "cameraNear": { value: null }, - "cameraFar": { value: null }, - - }, - - vertexShader: [ - - "varying vec2 vUv;", - - "void main() {", - - " vUv = uv;", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - - "}" - - ].join( "\n" ), - - fragmentShader: [ - - "uniform sampler2D tDepth;", - - "uniform float cameraNear;", - "uniform float cameraFar;", - - "varying vec2 vUv;", - - "#include ", - - "float getLinearDepth( const in vec2 screenPosition ) {", - - " #if PERSPECTIVE_CAMERA == 1", - - " float fragCoordZ = texture2D( tDepth, screenPosition ).x;", - " float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );", - " return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );", - - " #else", - - " return texture2D( depthSampler, coord ).x;", - - " #endif", - - "}", - - "void main() {", - - " float depth = getLinearDepth( vUv );", - " gl_FragColor = vec4( vec3( 1.0 - depth ), 1.0 );", - - "}" - - ].join( "\n" ) - - }; - - var SSAOBlurShader = { - - uniforms: { - - "tDiffuse": { value: null }, - "resolution": { value: new Vector2() } - - }, - - vertexShader: [ - - "varying vec2 vUv;", - - "void main() {", - - " vUv = uv;", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - - "}" - - ].join( "\n" ), - - fragmentShader: [ - - "uniform sampler2D tDiffuse;", - - "uniform vec2 resolution;", - - "varying vec2 vUv;", - - "void main() {", - - " vec2 texelSize = ( 1.0 / resolution );", - " float result = 0.0;", - - " for ( int i = - 2; i <= 2; i ++ ) {", - - " for ( int j = - 2; j <= 2; j ++ ) {", - - " vec2 offset = ( vec2( float( i ), float( j ) ) ) * texelSize;", - " result += texture2D( tDiffuse, vUv + offset ).r;", - - " }", - - " }", - - " gl_FragColor = vec4( vec3( result / ( 5.0 * 5.0 ) ), 1.0 );", - - "}" - - ].join( "\n" ) - - }; - - var SSAOPass = function ( scene, camera, width, height ) { - - Pass.call( this ); - - this.width = ( width !== undefined ) ? width : 512; - this.height = ( height !== undefined ) ? height : 512; - - this.clear = true; - - this.camera = camera; - this.scene = scene; - - this.kernelRadius = 8; - this.kernelSize = 32; - this.kernel = []; - this.noiseTexture = null; - this.output = 0; - - this.minDistance = 0.005; - this.maxDistance = 0.1; - - // - - this.generateSampleKernel(); - this.generateRandomKernelRotations(); - - // beauty render target with depth buffer - - var depthTexture = new DepthTexture(); - depthTexture.type = UnsignedShortType; - depthTexture.minFilter = NearestFilter; - depthTexture.maxFilter = NearestFilter; - - this.beautyRenderTarget = new WebGLRenderTarget( this.width, this.height, { - minFilter: LinearFilter, - magFilter: LinearFilter, - format: RGBAFormat, - depthTexture: depthTexture, - depthBuffer: true - } ); - - // normal render target - - this.normalRenderTarget = new WebGLRenderTarget( this.width, this.height, { - minFilter: NearestFilter, - magFilter: NearestFilter, - format: RGBAFormat - } ); - - // ssao render target - - this.ssaoRenderTarget = new WebGLRenderTarget( this.width, this.height, { - minFilter: LinearFilter, - magFilter: LinearFilter, - format: RGBAFormat - } ); - - this.blurRenderTarget = this.ssaoRenderTarget.clone(); - - // ssao material - - if ( SSAOShader === undefined ) { - - console.error( 'THREE.SSAOPass: The pass relies on SSAOShader.' ); - - } - - this.ssaoMaterial = new ShaderMaterial( { - defines: Object.assign( {}, SSAOShader.defines ), - uniforms: UniformsUtils.clone( SSAOShader.uniforms ), - vertexShader: SSAOShader.vertexShader, - fragmentShader: SSAOShader.fragmentShader, - blending: NoBlending - } ); - - this.ssaoMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture; - this.ssaoMaterial.uniforms[ 'tNormal' ].value = this.normalRenderTarget.texture; - this.ssaoMaterial.uniforms[ 'tDepth' ].value = this.beautyRenderTarget.depthTexture; - this.ssaoMaterial.uniforms[ 'tNoise' ].value = this.noiseTexture; - this.ssaoMaterial.uniforms[ 'kernel' ].value = this.kernel; - this.ssaoMaterial.uniforms[ 'cameraNear' ].value = this.camera.near; - this.ssaoMaterial.uniforms[ 'cameraFar' ].value = this.camera.far; - this.ssaoMaterial.uniforms[ 'resolution' ].value.set( this.width, this.height ); - this.ssaoMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix ); - this.ssaoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.getInverse( this.camera.projectionMatrix ); - - // normal material - - this.normalMaterial = new MeshNormalMaterial(); - this.normalMaterial.blending = NoBlending; - - // blur material - - this.blurMaterial = new ShaderMaterial( { - defines: Object.assign( {}, SSAOBlurShader.defines ), - uniforms: UniformsUtils.clone( SSAOBlurShader.uniforms ), - vertexShader: SSAOBlurShader.vertexShader, - fragmentShader: SSAOBlurShader.fragmentShader - } ); - this.blurMaterial.uniforms[ 'tDiffuse' ].value = this.ssaoRenderTarget.texture; - this.blurMaterial.uniforms[ 'resolution' ].value.set( this.width, this.height ); - - // material for rendering the depth - - this.depthRenderMaterial = new ShaderMaterial( { - defines: Object.assign( {}, SSAODepthShader.defines ), - uniforms: UniformsUtils.clone( SSAODepthShader.uniforms ), - vertexShader: SSAODepthShader.vertexShader, - fragmentShader: SSAODepthShader.fragmentShader, - blending: NoBlending - } ); - this.depthRenderMaterial.uniforms[ 'tDepth' ].value = this.beautyRenderTarget.depthTexture; - this.depthRenderMaterial.uniforms[ 'cameraNear' ].value = this.camera.near; - this.depthRenderMaterial.uniforms[ 'cameraFar' ].value = this.camera.far; - - // material for rendering the content of a render target - - this.copyMaterial = new ShaderMaterial( { - uniforms: UniformsUtils.clone( CopyShader.uniforms ), - vertexShader: CopyShader.vertexShader, - fragmentShader: CopyShader.fragmentShader, - transparent: true, - depthTest: false, - depthWrite: false, - blendSrc: DstColorFactor, - blendDst: ZeroFactor, - blendEquation: AddEquation, - blendSrcAlpha: DstAlphaFactor, - blendDstAlpha: ZeroFactor, - blendEquationAlpha: AddEquation - } ); - - this.fsQuad = new Pass.FullScreenQuad( null ); - - this.originalClearColor = new Color(); - - }; - - SSAOPass.prototype = Object.assign( Object.create( Pass.prototype ), { - - constructor: SSAOPass, - - dispose: function () { - - // dispose render targets - - this.beautyRenderTarget.dispose(); - this.normalRenderTarget.dispose(); - this.ssaoRenderTarget.dispose(); - this.blurRenderTarget.dispose(); - - // dispose materials - - this.normalMaterial.dispose(); - this.blurMaterial.dispose(); - this.copyMaterial.dispose(); - this.depthRenderMaterial.dispose(); - - // dipsose full screen quad - - this.fsQuad.dispose(); - - }, - - render: function ( renderer, writeBuffer /*, readBuffer, deltaTime, maskActive */ ) { - - // render beauty and depth - - renderer.setRenderTarget( this.beautyRenderTarget ); - renderer.clear(); - renderer.render( this.scene, this.camera ); - - // render normals - - this.renderOverride( renderer, this.normalMaterial, this.normalRenderTarget, 0x7777ff, 1.0 ); - - // render SSAO - - this.ssaoMaterial.uniforms[ 'kernelRadius' ].value = this.kernelRadius; - this.ssaoMaterial.uniforms[ 'minDistance' ].value = this.minDistance; - this.ssaoMaterial.uniforms[ 'maxDistance' ].value = this.maxDistance; - this.renderPass( renderer, this.ssaoMaterial, this.ssaoRenderTarget ); - - // render blur - - this.renderPass( renderer, this.blurMaterial, this.blurRenderTarget ); - - // output result to screen - - switch ( this.output ) { - - case SSAOPass.OUTPUT.SSAO: - - this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssaoRenderTarget.texture; - this.copyMaterial.blending = NoBlending; - this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); - - break; - - case SSAOPass.OUTPUT.Blur: - - this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget.texture; - this.copyMaterial.blending = NoBlending; - this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); - - break; - - case SSAOPass.OUTPUT.Beauty: - - this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture; - this.copyMaterial.blending = NoBlending; - this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); - - break; - - case SSAOPass.OUTPUT.Depth: - - this.renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : writeBuffer ); - - break; - - case SSAOPass.OUTPUT.Normal: - - this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.normalRenderTarget.texture; - this.copyMaterial.blending = NoBlending; - this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); - - break; - - case SSAOPass.OUTPUT.Default: - - this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture; - this.copyMaterial.blending = NoBlending; - this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); - - this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget.texture; - this.copyMaterial.blending = CustomBlending; - this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); - - break; - - default: - console.warn( 'THREE.SSAOPass: Unknown output type.' ); - - } - - }, - - renderPass: function ( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) { - - // save original state - this.originalClearColor.copy( renderer.getClearColor() ); - var originalClearAlpha = renderer.getClearAlpha(); - var originalAutoClear = renderer.autoClear; - - renderer.setRenderTarget( renderTarget ); - - // setup pass state - renderer.autoClear = false; - if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) { - - renderer.setClearColor( clearColor ); - renderer.setClearAlpha( clearAlpha || 0.0 ); - renderer.clear(); - - } - - this.fsQuad.material = passMaterial; - this.fsQuad.render( renderer ); - - // restore original state - renderer.autoClear = originalAutoClear; - renderer.setClearColor( this.originalClearColor ); - renderer.setClearAlpha( originalClearAlpha ); - - }, - - renderOverride: function ( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) { - - this.originalClearColor.copy( renderer.getClearColor() ); - var originalClearAlpha = renderer.getClearAlpha(); - var originalAutoClear = renderer.autoClear; - - renderer.setRenderTarget( renderTarget ); - renderer.autoClear = false; - - clearColor = overrideMaterial.clearColor || clearColor; - clearAlpha = overrideMaterial.clearAlpha || clearAlpha; - - if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) { - - renderer.setClearColor( clearColor ); - renderer.setClearAlpha( clearAlpha || 0.0 ); - renderer.clear(); - - } - - this.scene.overrideMaterial = overrideMaterial; - renderer.render( this.scene, this.camera ); - this.scene.overrideMaterial = null; - - // restore original state - - renderer.autoClear = originalAutoClear; - renderer.setClearColor( this.originalClearColor ); - renderer.setClearAlpha( originalClearAlpha ); - - }, - - setSize: function ( width, height ) { - - this.width = width; - this.height = height; - - this.beautyRenderTarget.setSize( width, height ); - this.ssaoRenderTarget.setSize( width, height ); - this.normalRenderTarget.setSize( width, height ); - this.blurRenderTarget.setSize( width, height ); - - this.ssaoMaterial.uniforms[ 'resolution' ].value.set( width, height ); - this.ssaoMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix ); - this.ssaoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.getInverse( this.camera.projectionMatrix ); - - this.blurMaterial.uniforms[ 'resolution' ].value.set( width, height ); - - }, - - generateSampleKernel: function () { - - var kernelSize = this.kernelSize; - var kernel = this.kernel; - - for ( var i = 0; i < kernelSize; i ++ ) { - - var sample = new Vector3(); - sample.x = ( Math.random() * 2 ) - 1; - sample.y = ( Math.random() * 2 ) - 1; - sample.z = Math.random(); - - sample.normalize(); - - var scale = i / kernelSize; - scale = MathUtils.lerp( 0.1, 1, scale * scale ); - sample.multiplyScalar( scale ); - - kernel.push( sample ); - - } - - }, - - generateRandomKernelRotations: function () { - - var width = 4, height = 4; - - if ( SimplexNoise === undefined ) { - - console.error( 'THREE.SSAOPass: The pass relies on SimplexNoise.' ); - - } - - var simplex = new SimplexNoise(); - - var size = width * height; - var data = new Float32Array( size * 4 ); - - for ( var i = 0; i < size; i ++ ) { - - var stride = i * 4; - - var x = ( Math.random() * 2 ) - 1; - var y = ( Math.random() * 2 ) - 1; - var z = 0; - - var noise = simplex.noise3d( x, y, z ); - - data[ stride ] = noise; - data[ stride + 1 ] = noise; - data[ stride + 2 ] = noise; - data[ stride + 3 ] = 1; - - } - - this.noiseTexture = new DataTexture( data, width, height, RGBAFormat, FloatType ); - this.noiseTexture.wrapS = RepeatWrapping; - this.noiseTexture.wrapT = RepeatWrapping; - - } - - } ); - - SSAOPass.OUTPUT = { - 'Default': 0, - 'SSAO': 1, - 'Blur': 2, - 'Beauty': 3, - 'Depth': 4, - 'Normal': 5 - }; - - /** - * TODO - */ - - var SAOShader = { - defines: { - "NUM_SAMPLES": 7, - "NUM_RINGS": 4, - "NORMAL_TEXTURE": 0, - "DIFFUSE_TEXTURE": 0, - "DEPTH_PACKING": 1, - "PERSPECTIVE_CAMERA": 1 - }, - uniforms: { - - "tDepth": { value: null }, - "tDiffuse": { value: null }, - "tNormal": { value: null }, - "size": { value: new Vector2( 512, 512 ) }, - - "cameraNear": { value: 1 }, - "cameraFar": { value: 100 }, - "cameraProjectionMatrix": { value: new Matrix4() }, - "cameraInverseProjectionMatrix": { value: new Matrix4() }, - - "scale": { value: 1.0 }, - "intensity": { value: 0.1 }, - "bias": { value: 0.5 }, - - "minResolution": { value: 0.0 }, - "kernelRadius": { value: 100.0 }, - "randomSeed": { value: 0.0 } - }, - vertexShader: [ - "varying vec2 vUv;", - - "void main() {", - " vUv = uv;", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - "}" - - ].join( "\n" ), - fragmentShader: [ - "#include ", - - "varying vec2 vUv;", - - "#if DIFFUSE_TEXTURE == 1", - "uniform sampler2D tDiffuse;", - "#endif", - - "uniform sampler2D tDepth;", - - "#if NORMAL_TEXTURE == 1", - "uniform sampler2D tNormal;", - "#endif", - - "uniform float cameraNear;", - "uniform float cameraFar;", - "uniform mat4 cameraProjectionMatrix;", - "uniform mat4 cameraInverseProjectionMatrix;", - - "uniform float scale;", - "uniform float intensity;", - "uniform float bias;", - "uniform float kernelRadius;", - "uniform float minResolution;", - "uniform vec2 size;", - "uniform float randomSeed;", - - "// RGBA depth", - - "#include ", - - "vec4 getDefaultColor( const in vec2 screenPosition ) {", - " #if DIFFUSE_TEXTURE == 1", - " return texture2D( tDiffuse, vUv );", - " #else", - " return vec4( 1.0 );", - " #endif", - "}", - - "float getDepth( const in vec2 screenPosition ) {", - " #if DEPTH_PACKING == 1", - " return unpackRGBAToDepth( texture2D( tDepth, screenPosition ) );", - " #else", - " return texture2D( tDepth, screenPosition ).x;", - " #endif", - "}", - - "float getViewZ( const in float depth ) {", - " #if PERSPECTIVE_CAMERA == 1", - " return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );", - " #else", - " return orthographicDepthToViewZ( depth, cameraNear, cameraFar );", - " #endif", - "}", - - "vec3 getViewPosition( const in vec2 screenPosition, const in float depth, const in float viewZ ) {", - " float clipW = cameraProjectionMatrix[2][3] * viewZ + cameraProjectionMatrix[3][3];", - " vec4 clipPosition = vec4( ( vec3( screenPosition, depth ) - 0.5 ) * 2.0, 1.0 );", - " clipPosition *= clipW; // unprojection.", - - " return ( cameraInverseProjectionMatrix * clipPosition ).xyz;", - "}", - - "vec3 getViewNormal( const in vec3 viewPosition, const in vec2 screenPosition ) {", - " #if NORMAL_TEXTURE == 1", - " return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz );", - " #else", - " return normalize( cross( dFdx( viewPosition ), dFdy( viewPosition ) ) );", - " #endif", - "}", - - "float scaleDividedByCameraFar;", - "float minResolutionMultipliedByCameraFar;", - - "float getOcclusion( const in vec3 centerViewPosition, const in vec3 centerViewNormal, const in vec3 sampleViewPosition ) {", - " vec3 viewDelta = sampleViewPosition - centerViewPosition;", - " float viewDistance = length( viewDelta );", - " float scaledScreenDistance = scaleDividedByCameraFar * viewDistance;", - - " return max(0.0, (dot(centerViewNormal, viewDelta) - minResolutionMultipliedByCameraFar) / scaledScreenDistance - bias) / (1.0 + pow2( scaledScreenDistance ) );", - "}", - - "// moving costly divides into consts", - "const float ANGLE_STEP = PI2 * float( NUM_RINGS ) / float( NUM_SAMPLES );", - "const float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES );", - - "float getAmbientOcclusion( const in vec3 centerViewPosition ) {", - " // precompute some variables require in getOcclusion.", - " scaleDividedByCameraFar = scale / cameraFar;", - " minResolutionMultipliedByCameraFar = minResolution * cameraFar;", - " vec3 centerViewNormal = getViewNormal( centerViewPosition, vUv );", - - " // jsfiddle that shows sample pattern: https://jsfiddle.net/a16ff1p7/", - " float angle = rand( vUv + randomSeed ) * PI2;", - " vec2 radius = vec2( kernelRadius * INV_NUM_SAMPLES ) / size;", - " vec2 radiusStep = radius;", - - " float occlusionSum = 0.0;", - " float weightSum = 0.0;", - - " for( int i = 0; i < NUM_SAMPLES; i ++ ) {", - " vec2 sampleUv = vUv + vec2( cos( angle ), sin( angle ) ) * radius;", - " radius += radiusStep;", - " angle += ANGLE_STEP;", - - " float sampleDepth = getDepth( sampleUv );", - " if( sampleDepth >= ( 1.0 - EPSILON ) ) {", - " continue;", - " }", - - " float sampleViewZ = getViewZ( sampleDepth );", - " vec3 sampleViewPosition = getViewPosition( sampleUv, sampleDepth, sampleViewZ );", - " occlusionSum += getOcclusion( centerViewPosition, centerViewNormal, sampleViewPosition );", - " weightSum += 1.0;", - " }", - - " if( weightSum == 0.0 ) discard;", - - " return occlusionSum * ( intensity / weightSum );", - "}", - - - "void main() {", - " float centerDepth = getDepth( vUv );", - " if( centerDepth >= ( 1.0 - EPSILON ) ) {", - " discard;", - " }", - - " float centerViewZ = getViewZ( centerDepth );", - " vec3 viewPosition = getViewPosition( vUv, centerDepth, centerViewZ );", - - " float ambientOcclusion = getAmbientOcclusion( viewPosition );", - - " gl_FragColor = getDefaultColor( vUv );", - " gl_FragColor.xyz *= 1.0 - ambientOcclusion;", - "}" - ].join( "\n" ) - }; - - /** - * TODO - */ - - var DepthLimitedBlurShader = { - defines: { - "KERNEL_RADIUS": 4, - "DEPTH_PACKING": 1, - "PERSPECTIVE_CAMERA": 1 - }, - uniforms: { - "tDiffuse": { value: null }, - "size": { value: new Vector2( 512, 512 ) }, - "sampleUvOffsets": { value: [ new Vector2( 0, 0 ) ] }, - "sampleWeights": { value: [ 1.0 ] }, - "tDepth": { value: null }, - "cameraNear": { value: 10 }, - "cameraFar": { value: 1000 }, - "depthCutoff": { value: 10 }, - }, - vertexShader: [ - "#include ", - - "uniform vec2 size;", - - "varying vec2 vUv;", - "varying vec2 vInvSize;", - - "void main() {", - " vUv = uv;", - " vInvSize = 1.0 / size;", - - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - "}" - - ].join( "\n" ), - fragmentShader: [ - "#include ", - "#include ", - - "uniform sampler2D tDiffuse;", - "uniform sampler2D tDepth;", - - "uniform float cameraNear;", - "uniform float cameraFar;", - "uniform float depthCutoff;", - - "uniform vec2 sampleUvOffsets[ KERNEL_RADIUS + 1 ];", - "uniform float sampleWeights[ KERNEL_RADIUS + 1 ];", - - "varying vec2 vUv;", - "varying vec2 vInvSize;", - - "float getDepth( const in vec2 screenPosition ) {", - " #if DEPTH_PACKING == 1", - " return unpackRGBAToDepth( texture2D( tDepth, screenPosition ) );", - " #else", - " return texture2D( tDepth, screenPosition ).x;", - " #endif", - "}", - - "float getViewZ( const in float depth ) {", - " #if PERSPECTIVE_CAMERA == 1", - " return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );", - " #else", - " return orthographicDepthToViewZ( depth, cameraNear, cameraFar );", - " #endif", - "}", - - "void main() {", - " float depth = getDepth( vUv );", - " if( depth >= ( 1.0 - EPSILON ) ) {", - " discard;", - " }", - - " float centerViewZ = -getViewZ( depth );", - " bool rBreak = false, lBreak = false;", - - " float weightSum = sampleWeights[0];", - " vec4 diffuseSum = texture2D( tDiffuse, vUv ) * weightSum;", - - " for( int i = 1; i <= KERNEL_RADIUS; i ++ ) {", - - " float sampleWeight = sampleWeights[i];", - " vec2 sampleUvOffset = sampleUvOffsets[i] * vInvSize;", - - " vec2 sampleUv = vUv + sampleUvOffset;", - " float viewZ = -getViewZ( getDepth( sampleUv ) );", - - " if( abs( viewZ - centerViewZ ) > depthCutoff ) rBreak = true;", - - " if( ! rBreak ) {", - " diffuseSum += texture2D( tDiffuse, sampleUv ) * sampleWeight;", - " weightSum += sampleWeight;", - " }", - - " sampleUv = vUv - sampleUvOffset;", - " viewZ = -getViewZ( getDepth( sampleUv ) );", - - " if( abs( viewZ - centerViewZ ) > depthCutoff ) lBreak = true;", - - " if( ! lBreak ) {", - " diffuseSum += texture2D( tDiffuse, sampleUv ) * sampleWeight;", - " weightSum += sampleWeight;", - " }", - - " }", - - " gl_FragColor = diffuseSum / weightSum;", - "}" - ].join( "\n" ) - }; - - var BlurShaderUtils = { - - createSampleWeights: function ( kernelRadius, stdDev ) { - - var gaussian = function ( x, stdDev ) { - - return Math.exp( - ( x * x ) / ( 2.0 * ( stdDev * stdDev ) ) ) / ( Math.sqrt( 2.0 * Math.PI ) * stdDev ); - - }; - - var weights = []; - - for ( var i = 0; i <= kernelRadius; i ++ ) { - - weights.push( gaussian( i, stdDev ) ); - - } - - return weights; - - }, - - createSampleOffsets: function ( kernelRadius, uvIncrement ) { - - var offsets = []; - - for ( var i = 0; i <= kernelRadius; i ++ ) { - - offsets.push( uvIncrement.clone().multiplyScalar( i ) ); - - } - - return offsets; - - }, - - configure: function ( material, kernelRadius, stdDev, uvIncrement ) { - - material.defines[ "KERNEL_RADIUS" ] = kernelRadius; - material.uniforms[ "sampleUvOffsets" ].value = BlurShaderUtils.createSampleOffsets( kernelRadius, uvIncrement ); - material.uniforms[ "sampleWeights" ].value = BlurShaderUtils.createSampleWeights( kernelRadius, stdDev ); - material.needsUpdate = true; - - } - - }; - - /** - * Unpack RGBA depth shader - * - show RGBA encoded depth as monochrome color - */ - - var UnpackDepthRGBAShader = { - - uniforms: { - - "tDiffuse": { value: null }, - "opacity": { value: 1.0 } - - }, - - vertexShader: [ - - "varying vec2 vUv;", - - "void main() {", - - " vUv = uv;", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - - "}" - - ].join( "\n" ), - - fragmentShader: [ - - "uniform float opacity;", - - "uniform sampler2D tDiffuse;", - - "varying vec2 vUv;", - - "#include ", - - "void main() {", - - " float depth = 1.0 - unpackRGBAToDepth( texture2D( tDiffuse, vUv ) );", - " gl_FragColor = vec4( vec3( depth ), opacity );", - - "}" - - ].join( "\n" ) - - }; - - /** - * SAO implementation inspired from bhouston previous SAO work - */ - - var SAOPass = function ( scene, camera, depthTexture, useNormals, resolution ) { - - Pass.call( this ); - - this.scene = scene; - this.camera = camera; - - this.clear = true; - this.needsSwap = false; - - this.supportsDepthTextureExtension = ( depthTexture !== undefined ) ? depthTexture : false; - this.supportsNormalTexture = ( useNormals !== undefined ) ? useNormals : false; - - this.originalClearColor = new Color(); - this.oldClearColor = new Color(); - this.oldClearAlpha = 1; - - this.params = { - output: 0, - saoBias: 0.5, - saoIntensity: 0.18, - saoScale: 1, - saoKernelRadius: 100, - saoMinResolution: 0, - saoBlur: true, - saoBlurRadius: 8, - saoBlurStdDev: 4, - saoBlurDepthCutoff: 0.01 - }; - - this.resolution = ( resolution !== undefined ) ? new Vector2( resolution.x, resolution.y ) : new Vector2( 256, 256 ); - - this.saoRenderTarget = new WebGLRenderTarget( this.resolution.x, this.resolution.y, { - minFilter: LinearFilter, - magFilter: LinearFilter, - format: RGBAFormat - } ); - this.blurIntermediateRenderTarget = this.saoRenderTarget.clone(); - this.beautyRenderTarget = this.saoRenderTarget.clone(); - - this.normalRenderTarget = new WebGLRenderTarget( this.resolution.x, this.resolution.y, { - minFilter: NearestFilter, - magFilter: NearestFilter, - format: RGBAFormat - } ); - this.depthRenderTarget = this.normalRenderTarget.clone(); - - if ( this.supportsDepthTextureExtension ) { - - var depthTexture = new DepthTexture(); - depthTexture.type = UnsignedShortType; - depthTexture.minFilter = NearestFilter; - depthTexture.maxFilter = NearestFilter; - - this.beautyRenderTarget.depthTexture = depthTexture; - this.beautyRenderTarget.depthBuffer = true; - - } - - this.depthMaterial = new MeshDepthMaterial(); - this.depthMaterial.depthPacking = RGBADepthPacking; - this.depthMaterial.blending = NoBlending; - - this.normalMaterial = new MeshNormalMaterial(); - this.normalMaterial.blending = NoBlending; - - if ( SAOShader === undefined ) { - - console.error( 'THREE.SAOPass relies on SAOShader' ); - - } - - this.saoMaterial = new ShaderMaterial( { - defines: Object.assign( {}, SAOShader.defines ), - fragmentShader: SAOShader.fragmentShader, - vertexShader: SAOShader.vertexShader, - uniforms: UniformsUtils.clone( SAOShader.uniforms ) - } ); - this.saoMaterial.extensions.derivatives = true; - this.saoMaterial.defines[ 'DEPTH_PACKING' ] = this.supportsDepthTextureExtension ? 0 : 1; - this.saoMaterial.defines[ 'NORMAL_TEXTURE' ] = this.supportsNormalTexture ? 1 : 0; - this.saoMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0; - this.saoMaterial.uniforms[ 'tDepth' ].value = ( this.supportsDepthTextureExtension ) ? depthTexture : this.depthRenderTarget.texture; - this.saoMaterial.uniforms[ 'tNormal' ].value = this.normalRenderTarget.texture; - this.saoMaterial.uniforms[ 'size' ].value.set( this.resolution.x, this.resolution.y ); - this.saoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.getInverse( this.camera.projectionMatrix ); - this.saoMaterial.uniforms[ 'cameraProjectionMatrix' ].value = this.camera.projectionMatrix; - this.saoMaterial.blending = NoBlending; - - if ( DepthLimitedBlurShader === undefined ) { - - console.error( 'THREE.SAOPass relies on DepthLimitedBlurShader' ); - - } - - this.vBlurMaterial = new ShaderMaterial( { - uniforms: UniformsUtils.clone( DepthLimitedBlurShader.uniforms ), - defines: Object.assign( {}, DepthLimitedBlurShader.defines ), - vertexShader: DepthLimitedBlurShader.vertexShader, - fragmentShader: DepthLimitedBlurShader.fragmentShader - } ); - this.vBlurMaterial.defines[ 'DEPTH_PACKING' ] = this.supportsDepthTextureExtension ? 0 : 1; - this.vBlurMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0; - this.vBlurMaterial.uniforms[ 'tDiffuse' ].value = this.saoRenderTarget.texture; - this.vBlurMaterial.uniforms[ 'tDepth' ].value = ( this.supportsDepthTextureExtension ) ? depthTexture : this.depthRenderTarget.texture; - this.vBlurMaterial.uniforms[ 'size' ].value.set( this.resolution.x, this.resolution.y ); - this.vBlurMaterial.blending = NoBlending; - - this.hBlurMaterial = new ShaderMaterial( { - uniforms: UniformsUtils.clone( DepthLimitedBlurShader.uniforms ), - defines: Object.assign( {}, DepthLimitedBlurShader.defines ), - vertexShader: DepthLimitedBlurShader.vertexShader, - fragmentShader: DepthLimitedBlurShader.fragmentShader - } ); - this.hBlurMaterial.defines[ 'DEPTH_PACKING' ] = this.supportsDepthTextureExtension ? 0 : 1; - this.hBlurMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0; - this.hBlurMaterial.uniforms[ 'tDiffuse' ].value = this.blurIntermediateRenderTarget.texture; - this.hBlurMaterial.uniforms[ 'tDepth' ].value = ( this.supportsDepthTextureExtension ) ? depthTexture : this.depthRenderTarget.texture; - this.hBlurMaterial.uniforms[ 'size' ].value.set( this.resolution.x, this.resolution.y ); - this.hBlurMaterial.blending = NoBlending; - - if ( CopyShader === undefined ) { - - console.error( 'THREE.SAOPass relies on CopyShader' ); - - } - - this.materialCopy = new ShaderMaterial( { - uniforms: UniformsUtils.clone( CopyShader.uniforms ), - vertexShader: CopyShader.vertexShader, - fragmentShader: CopyShader.fragmentShader, - blending: NoBlending - } ); - this.materialCopy.transparent = true; - this.materialCopy.depthTest = false; - this.materialCopy.depthWrite = false; - this.materialCopy.blending = CustomBlending; - this.materialCopy.blendSrc = DstColorFactor; - this.materialCopy.blendDst = ZeroFactor; - this.materialCopy.blendEquation = AddEquation; - this.materialCopy.blendSrcAlpha = DstAlphaFactor; - this.materialCopy.blendDstAlpha = ZeroFactor; - this.materialCopy.blendEquationAlpha = AddEquation; - - if ( UnpackDepthRGBAShader === undefined ) { - - console.error( 'THREE.SAOPass relies on UnpackDepthRGBAShader' ); - - } - - this.depthCopy = new ShaderMaterial( { - uniforms: UniformsUtils.clone( UnpackDepthRGBAShader.uniforms ), - vertexShader: UnpackDepthRGBAShader.vertexShader, - fragmentShader: UnpackDepthRGBAShader.fragmentShader, - blending: NoBlending - } ); - - this.fsQuad = new Pass.FullScreenQuad( null ); - - }; - - SAOPass.OUTPUT = { - 'Beauty': 1, - 'Default': 0, - 'SAO': 2, - 'Depth': 3, - 'Normal': 4 - }; - - SAOPass.prototype = Object.assign( Object.create( Pass.prototype ), { - constructor: SAOPass, - - render: function ( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive*/ ) { - - // Rendering readBuffer first when rendering to screen - if ( this.renderToScreen ) { - - this.materialCopy.blending = NoBlending; - this.materialCopy.uniforms[ 'tDiffuse' ].value = readBuffer.texture; - this.materialCopy.needsUpdate = true; - this.renderPass( renderer, this.materialCopy, null ); - - } - - if ( this.params.output === 1 ) { - - return; - - } - - this.oldClearColor.copy( renderer.getClearColor() ); - this.oldClearAlpha = renderer.getClearAlpha(); - var oldAutoClear = renderer.autoClear; - renderer.autoClear = false; - - renderer.setRenderTarget( this.depthRenderTarget ); - renderer.clear(); - - this.saoMaterial.uniforms[ 'bias' ].value = this.params.saoBias; - this.saoMaterial.uniforms[ 'intensity' ].value = this.params.saoIntensity; - this.saoMaterial.uniforms[ 'scale' ].value = this.params.saoScale; - this.saoMaterial.uniforms[ 'kernelRadius' ].value = this.params.saoKernelRadius; - this.saoMaterial.uniforms[ 'minResolution' ].value = this.params.saoMinResolution; - this.saoMaterial.uniforms[ 'cameraNear' ].value = this.camera.near; - this.saoMaterial.uniforms[ 'cameraFar' ].value = this.camera.far; - // this.saoMaterial.uniforms['randomSeed'].value = Math.random(); - - var depthCutoff = this.params.saoBlurDepthCutoff * ( this.camera.far - this.camera.near ); - this.vBlurMaterial.uniforms[ 'depthCutoff' ].value = depthCutoff; - this.hBlurMaterial.uniforms[ 'depthCutoff' ].value = depthCutoff; - - this.vBlurMaterial.uniforms[ 'cameraNear' ].value = this.camera.near; - this.vBlurMaterial.uniforms[ 'cameraFar' ].value = this.camera.far; - this.hBlurMaterial.uniforms[ 'cameraNear' ].value = this.camera.near; - this.hBlurMaterial.uniforms[ 'cameraFar' ].value = this.camera.far; - - this.params.saoBlurRadius = Math.floor( this.params.saoBlurRadius ); - if ( ( this.prevStdDev !== this.params.saoBlurStdDev ) || ( this.prevNumSamples !== this.params.saoBlurRadius ) ) { - - BlurShaderUtils.configure( this.vBlurMaterial, this.params.saoBlurRadius, this.params.saoBlurStdDev, new Vector2( 0, 1 ) ); - BlurShaderUtils.configure( this.hBlurMaterial, this.params.saoBlurRadius, this.params.saoBlurStdDev, new Vector2( 1, 0 ) ); - this.prevStdDev = this.params.saoBlurStdDev; - this.prevNumSamples = this.params.saoBlurRadius; - - } - - // Rendering scene to depth texture - renderer.setClearColor( 0x000000 ); - renderer.setRenderTarget( this.beautyRenderTarget ); - renderer.clear(); - renderer.render( this.scene, this.camera ); - - // Re-render scene if depth texture extension is not supported - if ( ! this.supportsDepthTextureExtension ) { - - // Clear rule : far clipping plane in both RGBA and Basic encoding - this.renderOverride( renderer, this.depthMaterial, this.depthRenderTarget, 0x000000, 1.0 ); - - } - - if ( this.supportsNormalTexture ) { - - // Clear rule : default normal is facing the camera - this.renderOverride( renderer, this.normalMaterial, this.normalRenderTarget, 0x7777ff, 1.0 ); - - } - - // Rendering SAO texture - this.renderPass( renderer, this.saoMaterial, this.saoRenderTarget, 0xffffff, 1.0 ); - - // Blurring SAO texture - if ( this.params.saoBlur ) { - - this.renderPass( renderer, this.vBlurMaterial, this.blurIntermediateRenderTarget, 0xffffff, 1.0 ); - this.renderPass( renderer, this.hBlurMaterial, this.saoRenderTarget, 0xffffff, 1.0 ); - - } - - var outputMaterial = this.materialCopy; - // Setting up SAO rendering - if ( this.params.output === 3 ) { - - if ( this.supportsDepthTextureExtension ) { - - this.materialCopy.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.depthTexture; - this.materialCopy.needsUpdate = true; - - } else { - - this.depthCopy.uniforms[ 'tDiffuse' ].value = this.depthRenderTarget.texture; - this.depthCopy.needsUpdate = true; - outputMaterial = this.depthCopy; - - } - - } else if ( this.params.output === 4 ) { - - this.materialCopy.uniforms[ 'tDiffuse' ].value = this.normalRenderTarget.texture; - this.materialCopy.needsUpdate = true; - - } else { - - this.materialCopy.uniforms[ 'tDiffuse' ].value = this.saoRenderTarget.texture; - this.materialCopy.needsUpdate = true; - - } - - // Blending depends on output, only want a CustomBlending when showing SAO - if ( this.params.output === 0 ) { - - outputMaterial.blending = CustomBlending; - - } else { - - outputMaterial.blending = NoBlending; - - } - - // Rendering SAOPass result on top of previous pass - this.renderPass( renderer, outputMaterial, this.renderToScreen ? null : readBuffer ); - - renderer.setClearColor( this.oldClearColor, this.oldClearAlpha ); - renderer.autoClear = oldAutoClear; - - }, - - renderPass: function ( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) { - - // save original state - this.originalClearColor.copy( renderer.getClearColor() ); - var originalClearAlpha = renderer.getClearAlpha(); - var originalAutoClear = renderer.autoClear; - - renderer.setRenderTarget( renderTarget ); - - // setup pass state - renderer.autoClear = false; - if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) { - - renderer.setClearColor( clearColor ); - renderer.setClearAlpha( clearAlpha || 0.0 ); - renderer.clear(); - - } - - this.fsQuad.material = passMaterial; - this.fsQuad.render( renderer ); - - // restore original state - renderer.autoClear = originalAutoClear; - renderer.setClearColor( this.originalClearColor ); - renderer.setClearAlpha( originalClearAlpha ); - - }, - - renderOverride: function ( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) { - - this.originalClearColor.copy( renderer.getClearColor() ); - var originalClearAlpha = renderer.getClearAlpha(); - var originalAutoClear = renderer.autoClear; - - renderer.setRenderTarget( renderTarget ); - renderer.autoClear = false; - - clearColor = overrideMaterial.clearColor || clearColor; - clearAlpha = overrideMaterial.clearAlpha || clearAlpha; - if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) { - - renderer.setClearColor( clearColor ); - renderer.setClearAlpha( clearAlpha || 0.0 ); - renderer.clear(); - - } - - this.scene.overrideMaterial = overrideMaterial; - renderer.render( this.scene, this.camera ); - this.scene.overrideMaterial = null; - - // restore original state - renderer.autoClear = originalAutoClear; - renderer.setClearColor( this.originalClearColor ); - renderer.setClearAlpha( originalClearAlpha ); - - }, - - setSize: function ( width, height ) { - - this.beautyRenderTarget.setSize( width, height ); - this.saoRenderTarget.setSize( width, height ); - this.blurIntermediateRenderTarget.setSize( width, height ); - this.normalRenderTarget.setSize( width, height ); - this.depthRenderTarget.setSize( width, height ); - - this.saoMaterial.uniforms[ 'size' ].value.set( width, height ); - this.saoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.getInverse( this.camera.projectionMatrix ); - this.saoMaterial.uniforms[ 'cameraProjectionMatrix' ].value = this.camera.projectionMatrix; - this.saoMaterial.needsUpdate = true; - - this.vBlurMaterial.uniforms[ 'size' ].value.set( width, height ); - this.vBlurMaterial.needsUpdate = true; - - this.hBlurMaterial.uniforms[ 'size' ].value.set( width, height ); - this.hBlurMaterial.needsUpdate = true; - - } - - } ); - - function projectedUnitsPerMeter(latitude) { - let c = ThreeboxConstants; - return Math.abs(c.WORLD_SIZE / Math.cos(c.DEG2RAD * latitude) / c.EARTH_CIRCUMFERENCE); - } - - function projectToWorld(coords) { - // Spherical mercator forward projection, re-scaling to WORLD_SIZE - let c = ThreeboxConstants; - var projected = [ - c.MERCATOR_A * c.DEG2RAD * coords[0] * c.PROJECTION_WORLD_SIZE, - c.MERCATOR_A * Math.log(Math.tan(Math.PI * 0.25 + 0.5 * c.DEG2RAD * coords[1])) * c.PROJECTION_WORLD_SIZE - ]; - - //z dimension, defaulting to 0 if not provided - if (!coords[2]) { - projected.push(0); - } else { - var pixelsPerMeter = projectedUnitsPerMeter(coords[1]); - projected.push(coords[2] * pixelsPerMeter); - } - - var result = new Vector3(projected[0], projected[1], projected[2]); - - return result; - } - - class Mapbox3DTilesLayer { - constructor(params) { - if (!params) throw new Error('parameters missing for mapbox 3D tiles layer'); - if (!params.id) throw new Error('id parameter missing for mapbox 3D tiles layer'); - //if (!params.url) throw new Error('url parameter missing for mapbox 3D tiles layer'); - - (this.id = params.id), (this.url = params.url); - this.styleParams = {}; - this.projectToMercator = params.projectToMercator ? params.projectToMercator : false; - this.lights = params.lights ? params.lights : this.getDefaultLights(); - if ('color' in params) this.styleParams.color = params.color; - if ('opacity' in params) this.styleParams.opacity = params.opacity; - if ('pointsize' in params) this.styleParams.pointsize = params.pointsize; - - this.style = params.style || this.styleParams; //styleparams to be replaced by style config - this.loadStatus = 0; - this.viewProjectionMatrix = null; - this.type = 'custom'; - this.renderingMode = '3d'; - - window.addEventListener('resize', (e) => { - this._resize(e); - }); - } - - getDefaultLights() { - const width = window.innerWidth; - const height = window.innerHeight; - const hemiLight = new HemisphereLight(0xffffff, 0xbebebe, 0.7); - const dirLight = this._getDefaultDirLight(width, height); - - return [hemiLight, dirLight]; - } - - _getDefaultDirLight(width, height) { - const dirLight = new DirectionalLight(0xffffff, 0.5); - dirLight.color.setHSL(0.1, 1, 0.95); - dirLight.position.set(-1, -1.75, 1); - dirLight.position.multiplyScalar(100); - dirLight.castShadow = true; - dirLight.shadow.camera.near = -10000; - dirLight.shadow.camera.far = 2000000; - dirLight.shadow.bias = 0.0038; - dirLight.shadow.mapSize.width = width; - dirLight.shadow.mapSize.height = height * 2.5; - dirLight.shadow.camera.left = -width; - dirLight.shadow.camera.right = width; - dirLight.shadow.camera.top = -height * 2.5; - dirLight.shadow.camera.bottom = height * 2.5; - dirLight.uuid = 'shadowlight'; - - return dirLight; - } - - loadVisibleTiles() { - if (this.tileset && this.tileset.root) { - this.tileset.root.checkLoad(this.cameraSync.frustum, this.cameraSync.cameraPosition); - } - } - - onAdd(map, gl) { - this.map = map; - const fov = 36.8; - const aspect = map.getCanvas().width / map.getCanvas().height; - const near = 0.000000000001; - const far = Infinity; - // create perspective camera, parameters reinitialized by CameraSync - this.camera = new PerspectiveCamera(fov, aspect, near, far); - this.mapQueryRenderedFeatures = map.queryRenderedFeatures.bind(this.map); - this.map.queryRenderedFeatures = this.queryRenderedFeatures.bind(this); - this.scene = new Scene(); - this.rootTransform = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; - - this.lights.forEach((light) => { - this.scene.add(light); - if (light.shadow && light.shadow.camera) ; - }); - - this.world = new Group(); - this.world.name = 'flatMercatorWorld'; - this.scene.add(this.world); - - this.renderer = new WebGLRenderer({ - alpha: true, - antialias: true, - canvas: map.getCanvas(), - context: gl - }); - - this.renderer.shadowMap.enabled = true; - this.renderer.shadowMap.type = PCFShadowMap; - - this.highlight = new Highlight(this.scene, this.map); - this.marker = new Marker(this.scene, this.map); - - /* WIP on composer */ - let width = window.innerWidth; - let height = window.innerHeight; - this.composer = new EffectComposer(this.renderer); - - let ssaoPass = new SSAOPass(this.scene, this.camera, width, height); - ssaoPass.kernelRadius = 0.1; - //this.composer.addPass( ssaoPass ); //Renders white screen - - let saoPass = new SAOPass(this.scene, this.camera, false, true); - saoPass._render = saoPass.render; - saoPass.render = function (renderer) { - //renderer.setRenderTarget( _____ ) - renderer.clear(); - this._render.apply(this, arguments); - }; - //this.composer.addPass( saoPass ); //Renders black screen - - //let renderScene = new RenderPass(this.scene, this.camera); - //let bloomPass = new UnrealBloomPass( - // new THREE.Vector2(window.innerWidth, window.innerHeight), - // 1.5, - // 0.4, - // 0.85 - //); - //bloomPass.threshold = 0; - //bloomPass.strength = 1.5; - //bloomPass.radius = 0; - //this.composer.addPass( renderScene ); - //this.composer.addPass( bloomPass ); - - /* END OF WIP */ - - this.renderer.shadowMap.enabled = true; - this.renderer.autoClear = false; - - this.cameraSync = new CameraSync(this.map, this.camera, this.world); - this.cameraSync.aspect = width / height; - this.cameraSync.updateCallback = () => this.loadVisibleTiles(); - - //raycaster for mouse events - this.raycaster = new Raycaster(); - if (this.url) { - this.tileset = new TileSet((ts) => { - if (ts.loaded) { - //WIP, poor performance - ts.styleParams = this.style; - this.map.triggerRepaint(); - } - }); - this.tileset - .load(this.url, this.style, this.projectToMercator) - .then(() => { - if (this.tileset.root) { - this.world.add(this.tileset.root.totalContent); - this.world.updateMatrixWorld(); - this.loadStatus = 1; - this.loadVisibleTiles(); - } - }) - .catch((error) => { - console.error(`${error} (${this.url})`); - }); - } - - this.addShadow(); - } - - onRemove(map, gl) { - // todo: (much) more cleanup? - this.map.queryRenderedFeatures = this.mapQueryRenderedFeatures; - this.cameraSync.detachCamera(); - this.cameraSync = null; - } - - _resize(e) { - let width = window.innerWidth; - let height = window.innerHeight; - this.renderer.setSize(width, height); - this.cameraSync.aspect = width / height; - this.camera.aspect = width / height; - this.composer.setSize(width, height); - - for (let i = 0; i < this.scene.children.length; i++) { - let c = this.scene.children[i]; - if (c.uuid === 'shadowlight') { - c = this._getDefaultDirLight(width, height); - } - } - } - - addShadow() { - //debug plane - //var geo1 = new THREE.PlaneBufferGeometry(10000, 10000, 1, 1); - //var mat1 = new THREE.MeshBasicMaterial({ color: 0xFFFFFF, side: THREE.DoubleSide }); - //var plane1 = new THREE.Mesh(geo1, mat1); - //plane1.receiveShadow = true; - //this.scene.add(plane1); - - if (!this.shadowPlane) { - var planeGeometry = new PlaneBufferGeometry(10000, 10000, 1, 1); - this.shadowMaterial = new ShadowMaterial(); - this.shadowMaterial.opacity = 0.3; - this.shadowPlane = new Mesh(planeGeometry, this.shadowMaterial); - this.shadowPlane.receiveShadow = true; - } - - this.scene.add(this.shadowPlane); - } - - removeShadow() { - this.scene.remove(this.shadowPlane); - } - - setShadowOpacity(opacity) { - const newOpacity = opacity < 0 ? 0.0 : opacity > 1 ? 1.0 : opacity; - this.shadowMaterial.opacity = newOpacity; - } - - setStyle(style) { - //WIP - this.style = style - ? style - : { - color: 0xff00ff - }; - applyStyle(this.world, this.style); - } - - //ToDo: currently based on default lights, can be overriden by user, handle differently - setHismphereIntensity(intensity) { - if (this.lights[0] instanceof HemisphereLight) { - const newIntensity = intensity < 0 ? 0.0 : intensity > 1 ? 1.0 : intensity; - this.lights[0].intensity = newIntensity; - } - } - - queryRenderedFeatures(geometry, options) { - let result = this.mapQueryRenderedFeatures(geometry, options); - if (!this.map || !this.map.transform) { - return result; - } - if (!(options && options.layers && !options.layers.includes(this.id))) { - if (geometry && geometry.x && geometry.y) { - var mouse = new Vector2(); - - // scale mouse pixel position to a percentage of the screen's width and height - mouse.x = (geometry.x / this.map.transform.width) * 2 - 1; - mouse.y = 1 - (geometry.y / this.map.transform.height) * 2; - - this.raycaster.setFromCamera(mouse, this.camera); - - // calculate objects intersecting the picking ray - let intersects = this.raycaster.intersectObjects(this.world.children, true); - - //TODO: make this code nicer and more efficient - /* temp disabled coloring - if ((intersects.length === 0 && this.previntersect) || (intersects.length && this.previntersect && intersects[0].object.uuid != this.previntersect.object.uuid)) { - const object = this.previntersect.object; - if (object.geometry.attributes.color) { - const count = object.geometry.attributes.position.count; - for (let i = 0;ii % stride >= offset-1 && i % stride <= itemSize-1); - let positions = new THREE.BufferAttribute( new Float32Array( count * 3 ),3); - for (let i =0;i<=count;i++){ - mypositions.setXYZ(i,attribute.getX(i),attribute.getY(i),attribute.getZ(i)); - } - - //const normals = attributes.normal.data.array.filter((d,i)=>i % 7 >= 0 && i % 7 <= 2); - - - object.geometry.setAttribute( 'color', new THREE.BufferAttribute( new Float32Array( count * 3 ), 3 ) ); - for ( let i = 0; i < count; i ++ ) { - const color = new THREE.Color(); - const positions = object.geometry.attributes.position; - const colors = object.geometry.attributes.color; - if (geometry.attributes._batchid.data.array[i * 7 + 6] == propertyIndex){ - color.setRGB( ( positions.getY( i ) / radius + 1 ) / 2, 1.0, 0.5 ); - } - else { - color.setRGB( 0.9, 0.9, 0.9 ); - } - colors.setXYZ( i, color.r, color.g, color.b ); - } - - const material = new THREE.MeshPhongMaterial( { - color: 'white', - flatShading: true, - vertexColors: true, - shininess: 0 - } ); - - object.material = material; - /* End of WIP on coloring */ - } else { - if (intersect.index != null) { - feature.properties.index = intersect.index; - } else { - feature.properties.name = this.id; - } - } - /* WORK in progress - if (options.outline != false && (intersect.object !== this.outlinedObject || - (propertyIndex != null && propertyIndex !== this.outlinePropertyIndex) - || (propertyIndex == null && intersect.index !== this.outlineIndex))) { - - //WIP - - //this.outlinePass.selectedObjects = [intersect.object]; - - // update outline - if (this.outlineMesh) { - let parent = this.outlineMesh.parent; - parent.remove(this.outlineMesh); - this.outlineMesh = null; - } - this.outlinePropertyIndex = propertyIndex; - this.outlineIndex = intersect.index; - if (intersect.object instanceof THREE.Mesh) { - this.outlinedObject = intersect.object; - let outlineMaterial = new THREE.MeshBasicMaterial({color: options.outlineColor? options.outlineColor : 0xff0000, wireframe: true}); - let outlineMesh; - if (intersect.object && - intersect.object.geometry && - intersect.object.geometry.attributes && - intersect.object.geometry.attributes._batchid) { - // create new geometry from faces that have same _batchid - let geometry = intersect.object.geometry; - if (geometry.index) { - let ip1 = geometry.index.array[intersect.faceIndex*3]; - let idx = geometry.attributes._batchid.data.array[ip1*7+6]; - let blockFaces = []; - for (let faceIndex = 0; faceIndex < geometry.index.array.length; faceIndex += 3) { - let p1 = geometry.index.array[faceIndex]; - if (geometry.attributes._batchid.data.array[p1*7+6] === idx) { - let p2 = geometry.index.array[faceIndex+1]; - if (geometry.attributes._batchid.data.array[p2*7+6] === idx) { - let p3 = geometry.index.array[faceIndex+2]; - if (geometry.attributes._batchid.data.array[p3*7+6] === idx) { - blockFaces.push(faceIndex); - } - } - } - } - let highLightGeometry = new THREE.Geometry(); - for (let vertexCount = 0, face = 0; face < blockFaces.length; face++) { - let faceIndex = blockFaces[face]; - let p1 = geometry.index.array[faceIndex]; - let p2 = geometry.index.array[faceIndex+1]; - let p3 = geometry.index.array[faceIndex+2]; - let positions = geometry.attributes.position.data.array; - highLightGeometry.vertices.push( - new THREE.Vector3(positions[p1*7], positions[p1*7+1], positions[p1*7+2]), - new THREE.Vector3(positions[p2*7], positions[p2*7+1], positions[p2*7+2]), - new THREE.Vector3(positions[p3*7], positions[p3*7+1], positions[p3*7+2]), - ) - highLightGeometry.faces.push(new THREE.Face3(vertexCount, vertexCount+1, vertexCount+2)); - vertexCount += 3; - } - highLightGeometry.computeBoundingSphere(); - outlineMesh = new THREE.Mesh(highLightGeometry, outlineMaterial); - } else { - let ip1 = intersect.faceIndex*3; - let idx = geometry.attributes._batchid.array[ip1]; - let blockFaces = []; - for (let faceIndex = 0; faceIndex < geometry.attributes._batchid.array.length; faceIndex += 3) { - let p1 = faceIndex; - if (geometry.attributes._batchid.array[p1] === idx) { - let p2 = faceIndex + 1; - if (geometry.attributes._batchid.array[p2] === idx) { - let p3 = faceIndex + 2; - if (geometry.attributes._batchid.array[p3] === idx) { - blockFaces.push(faceIndex); - } - } - } - } - let highLightGeometry = new THREE.Geometry(); - for (let vertexCount = 0, face = 0; face < blockFaces.length; face++) { - let faceIndex = blockFaces[face] * 3; - let positions = geometry.attributes.position.array; - highLightGeometry.vertices.push( - new THREE.Vector3(positions[faceIndex], positions[faceIndex+1], positions[faceIndex+2]), - new THREE.Vector3(positions[faceIndex+3], positions[faceIndex+4], positions[faceIndex+5]), - new THREE.Vector3(positions[faceIndex+6], positions[faceIndex+7], positions[faceIndex+8]), - ) - highLightGeometry.faces.push(new THREE.Face3(vertexCount, vertexCount+1, vertexCount+2)); - vertexCount += 3; - } - highLightGeometry.computeBoundingSphere(); - outlineMesh = new THREE.Mesh(highLightGeometry, outlineMaterial); - } - } else { - outlineMesh = new THREE.Mesh(this.outlinedObject.geometry, outlineMaterial); - } - outlineMesh.position.x = this.outlinedObject.position.x+0.1; - outlineMesh.position.y = this.outlinedObject.position.y+0.1; - outlineMesh.position.z = this.outlinedObject.position.z+0.1; - outlineMesh.quaternion.copy(this.outlinedObject.quaternion); - outlineMesh.scale.copy(this.outlinedObject.scale); - outlineMesh.matrix.copy(this.outlinedObject.matrix); - outlineMesh.raycast = () =>{}; - outlineMesh.name = "outline"; - outlineMesh.wireframe = true; - this.outlinedObject.parent.add(outlineMesh); - this.outlineMesh = outlineMesh; - - } - } - /* END OF work in progress */ - result.unshift(feature); - this.map.triggerRepaint(); - } else { - this.outlinedObject = null; - if (this.outlineMesh) { - let parent = this.outlineMesh.parent; - parent.remove(this.outlineMesh); - this.outlineMesh = null; - this.map.triggerRepaint(); - } - } - } - } - - return result; - } - - _update() { - this.renderer.state.reset(); - //WIP on composer - //this.composer.render (); - this.renderer.render(this.scene, this.camera); - - /*if (this.loadStatus == 1) { // first render after root tile is loaded - this.loadStatus = 2; - let frustum = new THREE.Frustum(); - frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(this.camera.projectionMatrix, this.camera.matrixWorldInverse)); - if (this.tileset.root) { - this.tileset.root.checkLoad(frustum, this.getCameraPosition()); - } - }*/ - } - - update() { - requestAnimationFrame(() => this._update()); - } - - render() { - const markers = this.marker.getMarkers(); - for (let i = 0; i < markers.length; i++) { - markers[i].renderer.render(markers[i].marker, this.camera); - markers[i].renderer.domElement.style = 'position: absolute; top: 0; pointer-events: none;'; - - for (let j = 0; j < markers[i].renderer.domElement.children.length; j++) { - const child = markers[i].renderer.domElement.children[j]; - child.style = 'pointer-events: auto;'; - child.transform.baseVal[0].matrix.e -= child.firstChild.width.baseVal.value / 2; - child.transform.baseVal[0].matrix.f -= child.firstChild.height.baseVal.value / 2; - } - } - - this._update(); - } - } - - exports.Mapbox3DTilesLayer = Mapbox3DTilesLayer; - exports.projectToWorld = projectToWorld; - exports.projectedUnitsPerMeter = projectedUnitsPerMeter; - - return exports; - -}({})); -//# sourceMappingURL=Mapbox3DTiles.js.map diff --git a/samples/traffic_lights/mapbox/index.html b/samples/traffic_lights/mapbox/index.html deleted file mode 100644 index 0c0d059..0000000 --- a/samples/traffic_lights/mapbox/index.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - -
-
- - - - \ No newline at end of file diff --git a/samples/traffic_lights/mapbox/output/tiles/0_0_5_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_0_5_9.i3dm deleted file mode 100644 index 494205e22a9745bedef540b0ea7ba9890c50be1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2728 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_0_9_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_0_9_9.i3dm deleted file mode 100644 index c59b8907519f29271f0ee413a5353bc75f0800dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1952 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 KdbSYSb?jet6>Eb4 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_0_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_0_6.i3dm deleted file mode 100644 index ebb64c63c567e8343176816ba2ab08eaf96ef15b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1952 zcma)6&2G~`5Z+MQQlLPA!i7V%Ck|=lpEOOvAqpXBQ>96jxaCkqm6L4Jl^i>=okC?r z>IK9D@BlmoM|g(bc@9n-nAvy}<3G@O1pGBn1_&Rt2eeXa z8iiW1)I6`2YWv0k&8bOxTyIv4O1(yN0c~ITtlsN+%%?eJoTRmSN zRQ3;ycz~9IfM&5!EyX>08a$;!(>RCh#(_xgyCXJ!^*&Mg{q^Qh6g+)h=ii)s|M7Xx z`H7}KpKkI2i&qrGe~&tvw`G_5T?4H4rl@$@aDd?N^O5P0~x|dfc(Wq3tLX%J(`D z3@r*J4`2goJ6(p7(i%fiQVVG3f?Hje!SOQ{90SvL`A1ZCl%z6;wxf=y91RA0Yn%+| zfO!`tb&Xe0+-oY5S*8nay)G&XE1bZX8Xp~5R&YwnvTYi4gJ^C z2!(CYbi514b}1nFB37(Yp#i;zC5?c<{w@T8%pr&-?jw7Tt;IGef+-kR5q>8rg!TwtX%ZyG*=Tg7dKIj<1dY}^-CFjC`&G7)TK@GxKG-V$4SOYj((IwJwfh}C7=YkOP0wB sSp`}q_sAN#PaXiRk%wfRJR*;Q*2xpHL7tLlKpW&a*(5K>OQ22i7YGBqpa1{> diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_0_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_0_7.i3dm deleted file mode 100644 index 68b2afeac9433024cb5ed0635a1c32defc8e2544..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2288 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 YMH59+#VdF!}mJR!ZyGy{X z2rX(t;~2+|F-96qO4M-@G!`=nntg3ri9a1%#z`6zW3+0mN~aA@XOh&RJ@>x*WQ$bI zbTZSKW@j$vd*^=VJLleWAMC|rs`Bw1$Nh35$L&I!pTKcqJOca>e?(9UOC9#Y?3~hN z1v!Ozj(j1*Y)TOp7L_UvrKnKIhzOM{!fH`dU;6$Z!qfe=ttRwn6<&sg z?;)p;an7QDGSmEW>!5zuOT4m#adMI4V%UK=m*I(9c;y(wml40g@C?jb%CHG@t!H>N z>T3+wqRt&GFTutd=VI(k#%Xva46l8GSANRy`WJa6o8jG? zc*Vl76ZsOuS2y!Y8^dKOgHAMq9DpN$-&cTb`IMdsgt_yofh*o#LPei_%5 z&G4Ie9#%6<&Yb>f0RwT|xA@yV`R30+VB4W+*(@(TcXu6YhFv?R&n5eQ=fPUwcfKDb z_{ryO@KU)*IgwW*S&a`iYQ$fE*r)aGd6#m&UproVW}ujId>5Owt&7iO5>EB_V%T(G z0`)f+IiLC5pCo+7zmjIlma5@=l=sjZfiWqIb^oR(b$E_>)_PwPLo@sgl(sF<(6bI^=5B>iMeCs0-T|LZIRq5$AI91#QFrotj$M-|ts#f@W_J`mC|*a6dzbi@9de%RCfXDFN54X2f( zu(|647}4GVGuCf_hjaR1Zfgr%Y*_<)&wmB)NB6=%&F{k1-cESELWAYqr=e{7Y1njb z4U9W~3Qo75goxm&5;B$wQkpqk5>lwOGMi4V#b#nQjaf6bR#9YDkF$z8&noIXt4Mif zOS*;d%oZEvqosVbGV?Q@jpju^&5PDX=UE=JRn+6Gq8`Wcp`YeMtLs=rJ)c$7^{k?< zXBC;Aq}O4Un4ZM+BwD}ODlt8Y=}AmaVtNwOlbD{&^kk+dGd-E<$xKgXIx>rwS-gqy zO^k0Md|5J?h%T{2mspEXks?&8PphG@$L;aEN#6=Qu9~pV844pO7U!v6HK{gGs|Io6 zKR@Jp)lf+F*EyZuB$I_Cyd6($qTAu$oRda3@W0p0!=3To<_d0J(e3ZV|Buei`}4oL z_pw#S;@H0F=P%YXeE%!(+Lh$?I&$OiZ)M9!jypAizyaGoq9?PfIV8|2jjd`uEt!!47bGD{-U^%#3kww7$8k&W*A z^&f+&7v*G|Zk1S0Pakf5!=)Be&DeH222&Hq9OK8()P&{?(=O=STvr)fpoX1Zug6sq zsCU=+u?6&B5YCXA9q?TJv6V9!BLI(B6P{hY$t7wP z?eXA-$9DOK4x6pGT+)pK)$b1DskF-2K4;KP-Y-*{&0-qt-VMQcyc}gMqddxmw=Z@1tzfbCcx58gX_nWT8&?oj*bzkR@ zwUR-;E0bYh!y5irYs?XAU@~t!$6Df#9b|5}XW}M9lek3gUL2FSe*qP;Ajbdz diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_1_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_1_5.i3dm deleted file mode 100644 index 6cc39d4cf9ed94d1ef28407e17fc7139d7537062..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1952 zcmb7EOK;Oa5Z<(uQlLPA!nN8HLTTkknkKnKAtY_8JgUU>Qbm=MY*I^(9obHyvMhRp zKfoDr=fnZ^fchJ_bK_TVV`k$`j2kJ!(s<{!^UcdP)ZQ>b2>HAU1m_E&3=lr3_i3fx z(hBursdZW{)%Udnnv;|CsL`rul}4TBeA>D2Sfk%}nMZR{kfilSvsS2{zB!5;Wo2e< zR`w6HxPy`chgPvrEyXQr8Z4zkOFM<=+5r#lIb#-FeM?lnfBbpK6P~^<^Dn=?zrOli z`+=%IUq0o1YV>ICger1cO3`+bcGx*{J!4>41JsvI=DN%p>AJb4rX|`Aw)itJwek|g zxV%lsuVq3GTaCI;b?}91eL7$kb9B#kXpR;e)oS5&LD{crWtybt%yA9d0)w(6QApqI zf-|%zQc*b}@n=yRxF9a%@5W z)igq3TND**F|b_{a31&Nt5j%0?_o(JAh5p+K_GGnqVRe|@3FPmMuEorK7Gxgk4r^$ z5wMcU%&Tu6w6wDeW9Kx3M#Z*x#i7wn_xKbhZ60-;8uN71G`dZDJUFvp1pG78T~@SB z+bQc^zQ`xiPDbL7EN2xdt7NAe7gipI5f@;^0@&TjkejT>XFO|objmj-yt@bG;xwwv z8hEgkdkTzCcLw-RC>)NGf(^MjckZ|=!4n$-pst2H$QJ3 zy*J1Jz&G;%-y{DO=>ZZglE=)fx2#gVTxo6AD)kj>l@)SXcDvCsEwfQ)g_w2jN4&AM z74nD`lyR2T8_o4nZS&*X)5?5G+cZ~Jt!V>QBa2qKRI40Q8KkO|TGl4|w^oJwC>Zha zf>=L1$(U^X(0?|7`cq;=jh1#bN2Y z;$OEO75{klMKNYhj}{QWdqdlH z7xcV>CoCQe{Gn@y(X5*`*0k?h)l+!Ky)yvMP6Mp98uggj=o`Bnvp)BDU`Kwy3as3y z)k+_gw3V7w#Sl9@2p!);g|?_LY+BewV;qeHg?P=Fd47)*QrcroLhfSL-FDnw!14Ho zii&|91*K@jLgLw@w8CF;l*X4m@yGanw9-y49Q&8xr`z;L{cR6hApS0P$jiR#2UWW(26;nSG!(H^by-uEwdKQ! zOEOQ~$OAZHW1QVdlbgIJdVEyybeE5HICmY>rEb)?*N<>2uWQ&oJLuE<(wCO=`ce0e z9mbJMA3gNd!pWscRHuAtG<=hbC6`JC&Mz6`_V@ul_7BZE{g#q7+y8H3yeIx`v*G{Z z_y4|&^iO=|94J?!VnPy2F6C)1b;Knmz1*ic$M4mOnWJld|D^v z#HUHxN=|yh$vy+8;51SO-heqc180%uU>?rFd3Y1)9J~b=;B9yZ=>oh97vVj)gme)u M!xgv+?;~A-mwU?qzW@LL diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_1_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_1_7.i3dm deleted file mode 100644 index 78feb2a5b3521bcc369d6c62339303276eed4bd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4168 zcmeHKYitx%6uv`k6_tljBpTE*F@mO)+1c%Ob`yK;yHonWcEJdgvfZ7wL-*zGY@w{x znrL{LAdLliSTqIegBn3pD9H{Ws6`Yd27{J}CK>@X8l#vPiRa$AL#Ho@Km1|hCb#Fi z-}&x2_ndpTy;bf0Acqhtos7`SU_VGjNJ;?!AHpYixw6KSS6NV0Q&(P8S>h?>-S(VJ zetuPr?2)S~d3S=ZZ`G8lhK87;@ou3lldr6*uE;B|TQGmP)8TLox~t`qQqOROBuSvC zDab1?8t$^$?IQS!@@hPFkik<*6KK)6(w32t@qIrh@9g}vjO;rJ_%Or0ot(UBo54T7 zjgv2JHE`#4PX3nR3g9`WIS>37jDN3(lUueJ`j2`!`RsEB-nyHU`xzDvadOutgBK2S z@>+&3=$zcca0~EHFr0FflV>n|*ZZ7&fbH`G*t5=X?s3S=@JsJ;@_B}6gXWGGZk!@*{4l zpV}AG(_Y+8`4=ufu3x|M)Ik!nZ}m!?wINRVaV5q2t3S=8`cG5BxZU>-~!G@g!>_t0?b`FHL^@O)Fj7+Ob5>{F$S8Y4TzH+>SJ=*>Fc8E?j>% z$`u7L$7w*}V&%fB8TlYXS z#d5L%KX>SClGL)|bS_TqzK`;4$M4mPH)D#ser&~&y|q;HgKYwSdEEla3+)_-QEcwf?D=1!eqFyg+{gGuPv490-*p6+^*wcNt{qRlCgIK+~D5lj5UntqZ`w7Fa)j%c^j zJ`n5RYcu(JrA3KpYNHxzBz-GaeN9@>8`D5L7A{c&N=9?IS&4#Z$Rt0X7Qu{leQ`jE z#gtIQ>kVYsouoq}nIWNuOva9D&0)nOqmN`fN;DXb1dL+Go?Jcb)x9H=UR^(?*EOPL zl(Fiqcx2wO>Z>KMpNx#gfTwsI{61(;LudfrZ)G)Al?mPpX9kb51mCEHl&Dt=M|n42 zP*q-@SC}W3lzWQd99xuVObv&?A!Z3Yys%w^`saMVatA8%+-q6<}J1r0Lf)tkxt!of&5q=Yn2YXpL1+eAAd<00t3-APiBwup98 zi%Qxh0Me+IzIXGgq4)5Xwj_ZXu9*ZPvm_#|$Lu|6Eoq~fjrRS@kHORniwf-5N-Rp! zX!VVjTFA0u5rlKil3hGV8gAaJmQ42bD&&u z8s$o;QG;8VBf{}{qmAVIWp}uo_QBH|u^6tLb?KE@HE?suO|%V*C6dc$Y>aKnlGy2x z+G^X@z?VH=t|U(Y_W398<#zh_-&lAp{7>Tk+j}wN6ML^M%@WsZd_E)>y`jvyDovXca4EmI+wnndFt79iK~<4SX3 zq^%ZL*Q{}ag%ldp^0`uBT(fMVrjV;y+Zf$iQ^BRz=Y#uiW5w_LpC8bDf9}VMA48h> z;n~Y-3Sck$^4-IOA5(V^NcLghKKT9Z*L$L0yn3D8|N1E#FwtU}EtW_xCsHh~aGKTQ ztid01Uy8PH+N3Xu=C178zC@dq4)=ZT^lZB`n_MzjeK0%Q?pQ0QFz((oz_-%?4{DWi zz-+X`E(NU39q!rE^;m}GE2UEIe$HGiSt}UrG537oI;b$`4Tfd=O*F>VhLn%#1Ln9b zPLxp};N>(Iu;#Alv^*``LEVH1l%ROOt!X%E}MLAoi zw3O6tqKEH#!$gh#&JOoB1%IM~99hFSYr5T@>u^U}&w3bSF<~Z2*mto1$t6lfZ84Kn zi=cKHNJ(X@Rxnq^-s5sdl3)!dCc(&I5>wTqdrz&UHtKAu?}wjJ>ditvc~W9Cm6}xF zq}0azB92|bC^ZS@^k+OZnb}fJVc6zg!&~Rl?sP=6>h{~a4vs+mp0>~PZpZaj?50}e zEn_}qsFO&fO(Sil4>vBXJPe~9aK#3=yTc(@c}vZB)bQw(k4<=S8Ox<vyjD;IS=j8((UrA7%Q6dt!tLx4~ME#v@F7!Ks%n)UkMQo1t6PFy*5u$>BsOtiKq_Dc5J?5+dV z)->Y80aXZ29N>UZRiL6b4j|d8gwzW{suU?8^@PNMTTeye0K7MzNftL!)I%j!dVb%$ z_vXEMKQlH$jgF5Ha(9N1YrsE(#XJh&L*~+4pOW3qAHUr zS2iI)Wrh29tUenoz?Z)5%LTr=eICyp1)RM;#u31;2^Hg#`1tSl{unOq)_KgOOE-t-PINflR@X-E zz0(|D|L3yy>x-2i@LWrm=Ct)MOC!YJ?OCIH(_ah`SAV;E?&j}0x1ag(@@Oyo@)FAD zr)Ed7YwKM8;ok1>&TfnEoqPKe?c2^(E&kH`+M@NT_WkQ0Yx7tC)UNz;LmR$zOG`~( z(LPK4q`A~+(BuYHgNaCt`Z*5@t_mI!JS=!b@TlPNF^>s(EJ~|>T4USHc8sQBHqn2! z&1{>QJzZ}vgj5A~{NE&bquz2ly6w;^Olq@T$sU9H?wlm#&T&G{mx~3L>W~9m9+x(m z$t>OJS~N-1#e6>XYD!(sSF(`THnVJ_Yl1=zC={-OT?b>haRS*;4wstU21BG&2T&}z za%sI~v>O(K?HfT*baclu-o-^g@hgYWS+By2y~ZG~4*V|dFuR3U9~}ea2XJZ6GCGE1 zK)d+KbO%D#`VKB#@kYQ~;(ba~$jUGqMjdS+uMQ+blVN^bW*K^01Tpmv$muGE#h`i} zcGz8O9H`dc+G5rPgT2F@{O5J@(|Whl>zd4TDi?d;Bob6ZDD0b1|4I`_T}=8B{Vl2~#n_j0L2E-~um+RIVyCDa|ZM!6)f zUSiZCmRw?7OPS=8Iw?m!Nzztw)FT#aA2~*j1Nq1aGDS|3X`m_c2$>;|lE;8%$m3*| dJV8zY&5|d{9C?a74KznilQZNQ@+{C9@-Ml=RR#b6 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_2_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_2_2.i3dm deleted file mode 100644 index f7c496f16d08451cc0654d1341d4639132aa71f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6856 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_2_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_2_6.i3dm deleted file mode 100644 index 887ece386bf26af8f04966840b670c4c5b8c7374..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3928 zcmeHJYitx%6u#3!p&&v9YEp?dlL!*Fb>F+)U~g(|%fQlNcY$JAVY?lcX}Y_0cS?a} zX;IYrgEv2TNPq-QgAx$r5lOpBAOh(R3E@>B6-*Ej5Tl}{Af9{YPIv5sipKcINp8<~ z&-LgD zgc}Sy?j`uU&vQcCIz?v;;DZd?0Kdy{V;d*5GJNX-Cp0s>^%f^gX0a}w`g3=^HgUo|h2QR~#XnA7OZ7jw*V*#*h1Y4UXa|Q=SInl^v1mNbXvY+P z@gTG1;a5v&tUK0{c#|ug@;{%ov&CNZ2IW6{X;^MXV~p|-CB2z@YjYjNN7wDooiaN_ z@vQqx@zv)J(3+Gz6O9kbE26n=+FKVtJAE0&Gkz||6FQTq&xWCeShDfdw%oh8<&a|` z<&W`fX=&Z8)Hy!9J}y~zQ=g^>DR}krSyaEYc{kR**NV%=9l|qr-Njd%KE|(ZIge*- z`VQy+z7dbT--t(yY{K>DKEsPEnsC~KAMlPBaxAEmAhj8-QEBHmUAEzGK zj@NHqgU8Q2hR2>chG&0y5;x7+i|=1QjaPau;P*xjby znVy~T?F!#+QTTS7;?K_Hb~}k@G+6aSk5D6{BtN6cWFUSBrE;KD4wU3e_|zV#jrZvI z3UPrLktM$r^pm<2R*R8{7_9Mnt20ej;_0t&YC>TdEI|9wsHZQdMhFq=1bL;VG+#B4Il`3<*>AnT(b2#eJ>|VE6`%CGVnbyU6c=q zk@;lBB8~yl1fqORSPDq81Y=Ay^U4siqE;r?DBHz&he5ltksV74R!Tmi1MdpJV|a&| zrYnXe7-S_#&>H|gKb3?fLA^e(h=jsQpo-dg^ThBBNvxwz{W6$q9a^^-gfDwzq z>?%zz6)S0v69w0HxkraBHh3=ShEojsWtd8v5!&Ys`^oRiWX`dg65Z=z3?rvC?egV< z$t9C$=;q5vhEGY1C8Y^$HQQsJSR?${UlxkVO2WR!**6>L|7XRpKm0g;|Lt5<|Ah9J zcb5)%D>;}Q6{}R`YpE(DRK-ND-nEqQwTH-hyA!^8v~m>_8Nn)5F}bTem3Eh@d_ub) zRad1%PB6Q3C0GsL*b;NC(tnTBpME97#e}n&{Jq6oHR5F MjYjF{X*i?NA4nzBL;wH) diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_2_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_2_7.i3dm deleted file mode 100644 index d672da7ef36fb617a593737c6fc95bd554ac8048..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2672 zcmc&#U1%It6uz69rm3c}Y7`YA-WU6@39~bspIy{(O*hGu&2HIUizO^^vU{@`J3G6~ z%+`isO%O%Vg5ZPr_o5aQq)+uhN}F1wiYP@9iw~{%P!&OZ@LxpmoV)iX(@h{$6fc}P z-#zEtbIv{I-aD37sYM7Ojh%$N3H&RVOrrn>vVa=dLNS${NEhZZ>FiW-nkHjvlJ8iU{C}80iC+@9Csn&R z*#-St*-psCZG`MAEkpIZxp;`;in8A7|W?xWwe2P z)nGF;8RzLT*D`Gp#4&3ir!yE9gPLVn;nm%Cp!w#)0&@>o>=Ewdiq|np%k^5L?l8wM zE;Yc(NKDmG*mR)&+9-y?vZxyF#lmtafP8Mt-^ElOY7cL2Cn_Z?n+K@CdY>(|TwW49QPtXf!i@-i3CV8}sfA^G(~f z%K3V;y68X)_&>w+*hJl~yOU;_ALLnOcwFHl7E7o~LQSllII(l#MlOIO)`GL!ZgQSg zc#qc$9`5pn4v&n%cCi~7=2U$+m1FR;^i8*l_e+Z>Mz!_sZCDIPF1&QjR{>5go<#id zhJC4c_}Ynyq(Xs5nXVG_ZwM!qQ$tHf6xkltJ@2=!dK(>zs^PaN4{0wYc{x7 zBwdiil1q7*OC55FQ7`9Vj`A>}ZnHVcC5818qYkm;660LTB=6KoIr2`DwvwYBv6x3l z7ug0BA=i*@vYqq*b(0;Wm+U0h0`-#XNFUin`hoh$^<+1>f!qkRo7_YO$j#&ypaJqX DMf diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_2_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_2_8.i3dm deleted file mode 100644 index d0c0c5de1e4c59377d31db30c2bdbbad22bcf541..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2712 zcmd5-U2Gdg5Z-I(FSJmgARY=~DM$#{I-mb7HHUE%=YnI0*aSg{qKmz8j@ozW-MO?f zCV>Y8LLiDjDgs`hpa9|pDFUI02*e8lgg`v>p)@LpA0Y&S(t_Zj%&hm4s~aimLnT%k z&$lzPGqW>$VD>6;L!HV{G`6B1*0TMjPK$6z0gGZLo$KvX zMPdn1^`?uZaR^ZA=l)&0!CKw09y=pxnODCYz`TQiZx;CV(~>s-N?ZQfpOW^7kdOQ& zX?F^I_76!rC2Zubb=qA*?w*x2Md0_qPnW;1e}H-I{Peq?gL@gzG5E>ho|~$TMU+1pOYS+nFvD@zJ2Q*YN7+3n zr%&%+eDbL`dJrG`eQBZfW83E0AF}Ebzudt6YzxdRo_zi&x4-e;hZgU@C&2BG4n3^y zI`T7@&wd^2`TN@@_rLSa{p!YF=XgFJfB3z+wCfA?nG>(6FD)EauYce<_0-h7`tR*8 zs%I*5>bv)SrM`9Iqu zxIP(|xgI#zC*vW09(V-pfJX(72_C0o0Xo4BFvm5jhFQh9Iioz~)^*1P8$a%=F~?!% zw652>!!e9_@g)AZSyp4+`>cJ;hLGoM{8pX0jJ2w@QXh;0GP{A0<6VRd6btz#)!`2C zkvD0TnatK*%cd!sDdckLy=f(zEA>H;1I%^|%LIiIlqr0&P8p10G6>{AnVQtJDh!c4 z)`B8>yCyA98MTVd;P_|=6m{LTjf1!e$^m&5+U-~PvELZvW351w)|oSfBMKb@I-T`=GEqJ@_CJ(a;@9|2({arqXR5a;EmG`m@-W5p|+?+oRkgzuv>BL+o)6v7a07JG@TBV9lexrD4I8^}hW^<)#- oOtz3qfi{!N$X2qATn@CATtT*zE6G(r+sV~r2f2n^3$%m$2l{1*y8r+H diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_2_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_2_9.i3dm deleted file mode 100644 index 25caaa13d490422335b9db7075b6850c065db7a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2144 zcma)6Piq@T6n~2A)NYeDZky&B210UB)ZLXVOS%YIk!=N83eqNr8sV&@u{^Na6}u}p zW)U?MO20!7E-isx@&y9LJ?9YUy@x;`B)R0$V-J0Av}0{$C-%bI_nY^3=Djx~(!D_f z03I#?{Eqw=q(?~fkvwG8ddn=;%azu4tx{h%H&`K)WcM1as#$H+Ss`Sd{eU-ic6=VN zf;LXFdZW2ns%_uDH>=F#rrKt8eZ!nJ$Y;@@RW8*kvl=~wmb3Yd=w_X&VfAg{!GSOp&EvDyJv^+}^*R_c2 z5jR*n$vS+G`+?{Sr%(24Hurt*3@yvPp3aiTNl8s~cY}fD2dJImwk%EGHrFoTo)0bp z{5=P7x7DbJ%)*%1t&sJ(!#yi-Jyu}lMy*!5Q!>_T<{C!YH0ZkU4IT6Vlpa+`L+ath+1hp2yLBOGm}P3OwVD1kw$g*yvNjP$_ z{^=}LhRR~3=`4cEr6CHWt-K1QCe|J=Su6>j;mjl$IZ9&Sb)6)kvPN{AP1gPRH%`zCKo~a)qa1L=#yGo^CO3Ic_IO(Gc$ZIfczFen zOWmk(ryt-{t{B)p%j?tqN@w!f^t5{?oN?shuE()jIJq>5`b;c?hHtX5Xj8GoS$tmG z)W5h<*= zlE=By5m%V>Y8~e!j}z%mTa#Q-T(2jYeYIiv)9 v1oLnaK1P~{1z3bn;8UbUxCEEsGx!|oGF*Wr_yWE}T7s|ODtryskgmdiRa5X{ diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_3_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_3_1.i3dm deleted file mode 100644 index a6819f6551f41b8ac321a3ec92e1fa5c7e63d8b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6368 zcmeHLX>=4-7A^{igiR3HBpgGrV+h^VOVWY#H3?ZN6B0;TL6D`B6rGk%ckJ%S5}SZg zVOR%7W=2O490WuMkWq*Xq|`a02SHFl0YN1?jO>dGdT^QhUfqHw0g-ct`DLH;`hM@; z?=J7YSC!85T1v~C2!ilg7eQEqvP&yLm=S{jKTKaGDfvaNwET?BqVai|`Pr@SsjI_MWhBnz|v!g9Dt;jVVQ@C#?IJ1({`YcO-Sruw(t~h%x~PnQ zK-c#kE-K4?IzET^&xF4jCMw4XPey%gxz1UHxEbMt`J%FzIQb^sOcwtB8tu zlI}Y=S5&^FygPpewXkW^gT~F3uC@axHd;rb`rKCrzypm zbGNAUB>e%zr@VR%dk`NXyeUmox)Wz6_NN!&<=CHogb$;B1mOv&|Ac&}A?`~09}yoV zeQT$voFIHV`c@O?dDK5adPl0L>?QrDAS#cL{%OP>!ZWca`#pNiA?$f8;ztlmgxd}k zl{DJZV{xMLBkjxTJF(}a*Bqj9J7KHkGRM zQ%m|K=(~mRGUPO+I@rh1H!o=tc&CNqcI(v7e_@nKHE`+zK?&w zeLoppsFfXU&-v+3-095u^kL2!G3gHNP+}g(+x+L8Ltk@q+Y6W zINqPwQR~ujILDj!-LLJ5G~u|q!(wNT`ztyAPkpu4ME-gR>+__u&LMLz>V5muvGq>> z0~MUJ>_kmn!5Dpw-`RiM)+<(D`&$VCaLwQ3WWJrxj)T@OUgY?e-7c+dURz*#Tb4(= zZ`Q|LKS-UPC#*>G6p6VMyeC*m$5j?3kl)&dG}p z!;#uJu3uU=9UfbI(QM`#ik8?C)@D_ZBD^co?RS{1z4+ z`v%Niz5sLO35Y$m1qvs83GH6I0Qb*)368op!N+gTgAsA_Abs9WnA~~}^jz>QOpiMQ zQqf_k46cEZj~xa3y0_uvgqe`O{VSMNd>RhkS_9*9cY^1Y8W_C&FYw5&w_t6nKR`_S zeApcKI?P$|6s-OIPf)*e2OQqD0UQrL1=5m@(4yoty!Q6z5VLnS1i#(|@_^M)G-Ef! zOgI2llDAY!86%l3cG)f^Fln`MnM`ws$-?IjlWupI9QxdBB5Bs8ER!U@Y^6E*$w@R% zCP{HD6vskwEELB=aV!+aqSxiH63;1G@cs$u`;q@@d@-fNs zG0EaG$>K4|;-R$D-0TMv^IKZ)We>GAD)9NA>%b}WyrPe#e( zq2%#ov&kflO^}M!htyEm>+$+M>>TBJOUlCK?ob$gqOnU2mIo?)x*^M_hC-^p!tM6O zTX>tJsnIxF^{Mfb0+ZAr#`~3%#?h}x9vwa!H+H*rzF+oh9I3G;I>NQ{Tw#fJiN=kC z|F1MRn&sX?C4OK$?SA7TqQhMa-)yhO@qdjby4uG4XlpcX%)H_tUNLFR{eNnHx$2u$ z$ymIX@%UU>BK`(ocS?tv3PNsCL4K9w#@7R07F9_e)vpHK;XqJIkunPM^3sN;$=P|X zEPNY0qy|IYfFBKVqDjJwpHK;s@rH>|2wz84l0Q(YGGrPX!IzaWtCC8}yuQ+)itb~~ zXef7wgWf4@l4wdWU1bc6HbmoSGU~@h5~`$fHB`o~x>#U@ynrgHBIqsmhQ0W@~lHeMyn*<|M5}B{Z=sjyKYon2k_x;k><<$3PW>~J5SWZs9 z+WM}R+LUO+ZRg{1YNi-yd^JqXXznoI1-;D`#lc}}*zNOqO9}&(o-#jf0l%Vjht!OK zFA&Ufm+*r;#*~%*Ka3UCC!Z`{{Ed|0B=fzwLF$>){)Td$VgX;xl=@x-W6qTG;>^1~#OjkCujw zNdq&xF^`r^A3d1uYICMHMzr0)%*L=G4b0|-&X5glhMviW!{8c{*%_vJ6G0T3;%Fi? z6PgP#!Yw$O3oV3Lp{39YN376VXd|>0ZpG0?XeYE6ZWB7-XfNC@bQC%XopE#&{tLf~ B5Lf^J diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_3_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_3_2.i3dm deleted file mode 100644 index 8f2a32aaf4946ed920144bf26619a4ed84d7a747..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9504 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_3_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_3_4.i3dm deleted file mode 100644 index 0fc8ae123fcde8db78ce3b4aef14a04e656d1f36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17336 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 ya)cZu$AFHIN60LBlspDBOCBf3$rEG_=s0V diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_3_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_3_7.i3dm deleted file mode 100644 index 972e90e43f78fb295cc85c897ce0d7577cd86bef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3448 zcmeHJeP~-%6u*62>$(s3-SrP~^$w(ztV{CJBrjm^vMlXhmnNf$#jTd{n!cnD^0nl3 z-FVBa=-m9?#>P;qZo*)KABc`3N;3ZthtrKYM9?Wt8G;H5f(m|~d*6NSGp&~W;a~dT zh_WgOQzgZmU(g{r+inpWMAAQESoR^MXY@ z6pVyxRerArRN-Jeu@eF$wlM#Caa1k0z_g30`Rr3>KCw-LeC-JnPlN>dUXH^X1zF_zU*0Up>$vX|&g;M>mAjwHiv zkG57%ytI+wt#_=eUJ=7pn$M4|FH{eK4 zlPj6~$XlUF(sOl!TzuvY^7S{LkyF9X$?<(Z5VhwznYa8sa_Zw_#C7Q*vi6m0WbNUP zNSpCCng8YmVqk3;2L`aqBl+Ff!MI4dPjY!U_fjtTJTA_e9juuhaApU*gX=AOA7A@9 zmrUN_mez7);vcz7cnRWPQ?M(Yx$soHeT3XVXL>Im0#v2g2 z3U^*lJQg*u0)2o>%)n_irxq1GU&H|%ibW#9%|Ul}B+&)E*{v2!T0RE~ce@kAl~hWC zG2DC{m7whg&gF+yikyRG7z%4;;N*yw87``@eZU2ZtfCjSJ#^9TbU0_B^>&3F+l@g! zSaujVtCmLS6-*riWC<9!P}H)TuEAKc%oH6$4vp$`Y4E!m2VC|m-3_PGa>H7Z+Q7N8 za2OnbTOZO@i<*+*L2^nK>?q}nW}uM4{r}!5IDn}LBKN)LSHdv z5L#AFQ_)!6r+%hG_lHB`T!G=4Gu!!Q3+-(0fVMN54owB?t)JS^)Qoq*EOTL~*sJPF zCZi?$@}udI9JGMFos^Oq%4hP$E+xqZdBEB3ce2mrlH5+oElo`v9U$<_-B=r7#L6(c zW|RBWVbo!GrJK{b7>u>(&vWuDaADXe~BK+D^7QB#%35f+DL2bl&?5U zE}cYIjW0JHJ~J_%lx?@y<1Y`E55b>(lN_a2BL8mW?K95qKy%|<_(uHx+qqc&DW5Cv zDI0oLN`#YzEpEx}+|p5QVXC**c24DXqPp4ERBn-7Z(*vV*y0wZYfEPFTAd}Qyp}9m zi&H(td|ihGG!Li_%}4d90o?>tj~1Xtv=A)-VpkN1hEtw72E=78fc)A&F)6Q7BT1qM~Nny zC>q6mRP>*bL>-f87fFnx{zhZO7){)lF~UsTHG(>~Ufl|Y+}Why zEriFSeG%b5qrD5^i(M7`Qo{WZ%bZT#6nqD=$0N>IA?c4mPCDUnhzkghM9y@=ClSve z?2DYj<NU+zCDTdSya2{ymE=c7}k+aIQs2GI34jt!am4ZK$;O~zef0N zw7*H3tI+-e@%LcvjR<$aGuc4+BA%68>z!EZqhyc3`c@Kt4)HNM&)1N1m9P`>--zQz z&Jx0_utsLWm%G&BZJldt-4N~FiPNYD_BP@5*kjeCpWFxQLO8TWIu!UY{K&qU#A}U0`c2~PauvbycqZ1r+zqv_Gihy9qn>Yo<;jH`5x3O z_~nE<;5m;Vek`7|+;6FP&PuYM!n#Z+yaIbqL3oK5E)A{S3xgDVOXBRt=Y}ug+lbrC zcD%>C6TXD^%VmmZFvjo^**hZ6r*kl4oO20hge&;o#JPyiF8Mh&3hNb3_7R9v2si1f z;6n%-5f39Ai<}08Er_pE42!VVZK=L}(JntXHny+rftOm<{$AjoSMX~}r2FpMjtah< z@Eycugs*x%PhQ2`<=+u!FrE_fCH{_)KGQ%(9QPDIJ(H(B@U#b>_Q2B~_~m-QXYF8N zaOaNf_e|j@SIu7~S_ScZJ813ZZ^LpK{>PF2Fr{e^#_99QRnOb`R)$|bJW-glyU-)@ zKU{doyrYwy**jF1dbSRk!1nHGkSugx@D1bmSWcQ##wIa)N?Bte*EyQu{-MjvA4fPD zu0Lv#`7pRZ)ZF=bs=2gJF2e;;F~asn^%-YM&tWipNh^lG+p8CH1GJ2DI&=ce)@(3~ zd!4W53$~1@K_V`xnhNh1eaP^sE{-kJNdYn&Ic`~3j3N??+`gtD%{|1X=I%Ko35H0 zq)K&BRpbdDw{F1fy>I5js7t8%; z_8D(uoUX6**&bb!&+wvcR&dO|!eUN&SMc~*)0usENw)A&)Emr~q9Rxr7vGcVjQo44 zdGi>lzUrKH<`o_ZM>Xm$n6`v6PMJ0vE_9pEG;@_%(0)Q0)5(p?hk8BUV72)4+!!da zHe|k>SK0{yjV)|%=UIIrXFxxO#d8}zZUH;D0|lo%Lt07wQ>;^ivE0b^=1ndNDz)V^ zKHQBI_64_L_TuL%JzG-~n7w{h6UZtX$?TRU8RpeBIn3V1&=%GVj%4`kgb5z~ZpnUa z!_lDqMN=97^{GRJ%Gs?M{^Hz3Vg4wo=Qr&d6Bu|$@*6wG0>7^h>xcjlRc;~CyH z-U3}xCA;!ux^O9I2Rny570(F%AB+4Lym&po4pt<6o$o*NS+1cm#tz28qCBJ^JU9K+@FHuP(Q=O}90 zpN|)gw7$&t-i)(>De)}B+g>O#4>e6>d~V}d^WKxveP>){f%MMpn0-U>Sor!>4U6a4 zn--ymUdk(WUOn@!Q+Z7PLT zOUg$>rM@xSTd}B#r{|J9hFi8A0c-!+jNy=`fpC0t9}v%T;~5`FUL4EpUvg`LuJn@f z`ud~Y<}3N*nSD*43+693Br<+=zv>-|7E)iOHJ&IO%zd53aIfbvPm@K-jPLt$j8Iuq zkKtLn>qE_nml+Nn*-vPl+n3=s|4>73es5scu8%(i6V4(uU9JlDxA$UeP zJGYP3;jrMduFQVeJsDoih+_7}4dR3=em-ok=TnaL%R8b~|JXH?%_RGNSEQbmC1z`KW z46>TvhkdS#2FXDx9hh-($XdH-uv&utfOzhAKqF9lg?d&l9|iE+G94H zm~;?|PyH3F=L=xsX9ch|_$)jdcmu|iO@|w^4uj*pVklHyhPAnWhXdyqf^*aRP>?tu zMi`<94{dwG@UMD`9rdCD6xB2a|R-RCT!y#qF*@v1Jzg zvD~$Xd$HPD&S21ei)tpAxz>nLCu|QaIWwg%o%tO zF86y40%J>Hqi+?2Ydx?&cQ*JMXTh4yOCYS-0*D-61)1S%VBx6KV9PFr^QCWsBYG!n zn*9m1{rv&B7x+GW(Cr+|xOEUbz21dJUvGrsp`{=wmchc2p6jR$VDz5u=lu7l>gi?AxT2Gm2312^V>!L76>Dg$DaYJ)*-Q2Mda zB#jysjcOV-G-_$|r%^|vo<`Eq7->#=TGG>!o|g2qq^Bi4E$L}VPfL1Q($kWjKk50C zo}P5{q@yPtJ?ZFazme9Bv~HwzBl$7PJn~~AKPK{HqJ1XXXQF*3+GmpXnKU#~-XOdm(4Z`7z+JUB}GG-`H^IA{5rv^q97`5UFV zP9@ECDtXRyO*$3J14otzj?52^%#TT@rgb%~OZ|a%d7b59(n)#X$l}3K@~8Kg_UZj4 zUA@1wPp^~W(Cef)^g79pUPpW#@pUp^@}t+YyiIz&cK(x*4c$CUy?keXL(JY7?GgoS!n@HqqvE*^})V$>MWmK$^|zv}I&jt!Z7gtb)I# zWt1)1meF;h&5?wUqTkZ-ujebmo{-{7w>n)?WvRRP3s~60rmoJ9hRJT_a9f(K>-fy^Hpde-V_gUT_boqt z1ixPVzlPrj3iiO{zsuv-i1ELRr0y>7*q#K}Rd^DMxAK$lfBevYp0m=9x4k0Igt#T_f zG81hgQpM%rmo9ncRwks_(-Ivv+#aLGg>#wk{8_j*haJCsNKP`W>1bCD5#5STtqHipndy)m#b+la*&HM7wnP1)Lv1qhnE_U&_RES)R%U)Ir8IEUc6Qae77cym9}l}0 zX;v4jf^^JT@s1%jmo+WTo-jN!J2@o-OTb>_vN~;{nQ57h0oDZ8$T6zEMiu+upST(o z{{8zy$5G}aBX7VK%fsH4iagww$ZA}B;NDfPQ(>I}(-o@`VarH%VOJV7SU#&GS-iir z{wBS)wt96oW6ODO{Xr`VJ6G&P^+Q@3vH7IHC@3$-_;+nyT;5E)+57Vr@!xm!na2KS zCruuQr?LOz5%)3paay8^H|OEG_u8ZI&xu8fSo-N@+hv0nC3p5f~$v@4_BXS zz%}F=;njd^%ss>Xj%$L~Gh9=y8TTyL9Is}aFV})=$+g0(1=pHu!?oqw;njxw57iiY AJpcdz diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_3_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_3_9.i3dm deleted file mode 100644 index 6c91f77ba0a89dadb2bc257f90ea9619858ec66a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2608 zcmcgtO>7%g5PsP#q);JPX*}PX znK$!h=Do3{)mmeO5cwn_9{|4v3mFCQA?r~iUn-~bnQUo2m(4Gf7imgS!gRS%GRj6F zPg6Zw+4NYUUU!*CQ*u8{^M&G4I=8;EJgQVQbx&P178c8+7HTX47Ntx&mmO7U$rz}z z=~8(e0+biIf6wW%{uF%4rzB(Uy)Q4}xihk4JS*_cf@Itfm}!!c7dY~mWb6q1)s$p> zFYxiKWGL?s{rs7ejNb&_nU{=T1U?J+CxN%~lJS<u!B8{`I_;f`+M*P$CNQq!!gU zk40s{hw@ldg|#ZjBN4%)g2x1pBUcj97+nj~3cJQ!&uUn91LMp$nd>sUW17t=Ery=U z%xSedP1E&Yv0wd9Bsr_P;kEvahr^?+@oPTSXqM+C=*iYGLcTdd$fZ&t-=ikv1dp#r z8_Z^o>9rl2qM1T2m!3&0(;8RRKlI^c?FYT+GN}`2d;@DuOs@4Dz*pxJO&e-N4a-jsfxm^k~Pi zT9#))`}oOB4?B^Pqz4M9&S7g-;dw_`YgVGp`<0W?mIlN015IR$ir gJU~v92gw{f)wg|K-q`TC&(cai#!BVNO18NE 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 OzFa@9KQ{ndKkk3l%j@$1 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_4_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_4_2.i3dm deleted file mode 100644 index c27bb315f679a0214d556e2b3aec4ca805c73a75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10104 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_4_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_4_3.i3dm deleted file mode 100644 index 32c2b3d93da5c1accb268d0e84dd9caac2eb166c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18080 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 VS#5m1C#xf?E2}4~kFUD2e*j%r3ON7( diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_4_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_4_4.i3dm deleted file mode 100644 index 3554be39360263e40ba1dd0aed5e87f2b60f80d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26432 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_4_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_4_5.i3dm deleted file mode 100644 index 57105b80d702e6ab04ea86cda63c2da7f501de9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14440 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 BJ8S>| diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_4_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_4_6.i3dm deleted file mode 100644 index f4847b93bfb9ed8649fe120eab64a9068f56a343..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5856 zcmeI03s6+o8GtX>g7J;SN3>e?IvS^<+4nBA?g>G}Yk0}xqXB{oTwu#SaQ6b@x&<|k z7_F9C(M)Vwoy13c5l5}hy-rh``XE&!HYQ3<)apc|X=pr<0vI z{NMf0|2qFUyX+p9vDg#Eaop)%9JdB!QwNT_5Dfq?st*cch9f5_BRR!Ukd~5>nln~N zv>4)rtW1ZPBW7j@i9w;TMwT*5N&=EBBQf29-}IM6(e7q|W5$T(W}h8kYXM?Mq5 z>%qo>`dih!xE1*j@Hyl>u-}5b73_j~dk4 z^l(nBShECu+lxFJ_HG^ewg7AW|0(bRjH3hJH<8PM-$Q;H&X~3r*Wrw9#+dYctFJnu z5yySU-}U7G>kQnwRI3adwr@Ax*H5q3DkqjSGS0XZBFb>yPWAIWLzTb3^qfLDmY+yg z&FIYD6M^9~lzufjW>cDSEG&Lm#%%U1$PT|XcRI82yMG?e?pVe2X-_r(sOe@c)AwDO ztpsYjGv0n6sI;E9u{e#(YL%9DC3|SDP<6SzWpEAi{UV%1jys-a_J7TaRX&*NVBDNo zt<<)@#C)&ZswMyWYFi@p-O^N`Z2jl;Fy-ElcXrd^H&X4mYp2U;_QE6url@gDU6E; zOi`*ls{O@^GUfWq$C>@MGLa04{sD`5b$*Gmx}lZnr!O#syY`sD^qVd}p=?N-%;GFw z-=1Xk{t4ri+ZTs>_OE0%aVt)QV|J7>{TC}b*^j*%V)`lTYsoVoX0ZNW{n~EtI4j6( zx>%NsdUw3KcXh8#C-v`VvAhGWg-F*E9OGW5?#h$Lvzh&%hK=E&YbzMbm-~{b2cKv0 ztERqff8p0|#xt9D>>hTdmht5CvC6rQ4#q!j?V%h?&SE@!&e!(qH(q31ExO6V=zU?@ z=UvOkDKl&OG5yKiN5WU@U5pPG&b0rnshV+Y!-4RQe&vjZzwsWK6!!|5oqCzf8s9=r zty@pd#GWP-nm#3=+@oY|-*v=v`fZ}`e4HFHUnI+ovq+(NHt8R{NsJqok(TqnA^oqt zLnfWBC(mqLLp;6)GT}-cvHW%g>9FoB>ExJ0^5%U;hP5mtj_4bt&ynAg@((YOkIub9 z*8296#%qVkg}g?xZRBcF@$nk+ho)Ji+_IUxI^xe{%U^B~+n}YSM~747W$6R5b6PX0 zt86Aq(|3}x%SXwP7PGp*P5-MfGI&p_OFrm$S%~efx>rZ7oTU zTScT5pO8(uw@6;^EhJ^Z5)u?##X@4PV9+HPbwWIodXxr~MwBL$W|S6`R+KiB392+< zJQMnv(9eW^CiF9*p9%fU=x4?{%vgsR>o8+HGsZDv91Hqc(9eQ?7WA{Ap9Sl%pua`) z$2u%nhZW;lF`gCUSuvg!<5@AD72{bko)zO+F`f)vft^Ht0jLqEb*Hk z9wvCI^Gt<(NMkg6k({jeZDXo(H0hiAU6NZsG0e<)gioh6d7?1++@evffzG8`zE`JTBP@ZgRF% z%=Y+B!y|Y3o*gz@VYzfS(j;%G45!ixmvgeyUrOI!MpJ^tcxU(SafTxo>3TaB{F8Fg zlc;YK%RmpGS{N1;k=SCgh4Mr7@MeEb%%ER|@%1RbOY7KoV%70M{3vn1?OL?>R6eNg z+ctEqbPyaGYf{rkN^Ooxjp@Ah94V8Wfx zG+Ry5dCK@4#qnG_7*X8!xM(hhYY!uudxGo0b>uq1=)iU6x^P{&ZZNuVv0Qg9j(ZYD QcdiH5lk3IxhS8JzH|6fM;{X5v diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_4_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_4_7.i3dm deleted file mode 100644 index 8ff492799bbbaf5a2413928aa770c88d56dde922..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11328 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 YRc%!rRb9MltLmxhs~V^p;#FVuAB+u4-v9sr diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_4_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_4_8.i3dm deleted file mode 100644 index ef4ec9560101e7817ea9b6c77776f79b50dc5261..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15856 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 ByVC#w diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_5_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_5_0.i3dm deleted file mode 100644 index 7a6f22d4e76fe3d5a730726cfea8388bb58f0379..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3256 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 S@oB7Trt(oWSGB;$NA)kSPeDEa diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_5_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_5_2.i3dm deleted file mode 100644 index bceee6488e2b642a80c45c1684b383f9dd6c7a3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20936 zcmeHvcUV-{^Y^;8ps~fMiN^9>yQ0}$*o8&i5sm2D1w;)tghi32uz*UlYhsP4#MltA zH$?2RSH-SqY{cHX#uyv1zjNozezQu5=FRW-{_#HYJeTu1b7wwhX3m_s7sL&63J5Kv zP$>3wQ7F=Jywq5s_^}EC{F3FdDpN-PG4?=~toC$-v9)wz?G$=lSa zqslE-)o-Z9+$k_H(ri(=spB109Xok;aBttIYv*@wI_cGR_dQLm+jzgL;-V!Lua@rZ zTfKWvqjsg6t=zr5``{wpZDjB8I z;sT{<9%F}vO4C!u8Hm#vkC?4A1u>TWcV*lVaSz5F(9bfAe?=eaGp>R1QjB+^|2-KW zMtNbzwd{0mO;MVzbFNllj4^{nPU@ph9hPsHhxr*K4+CFUXA)k z7zd&LF2+%IxI&83w3+3@=Kskbjrax2?RDlLe>}^7!2OM6dC{n~&I7?~T%5~Cehtjl~u{-kI4i`Fg7AZ}4nDg*5 zrOCi~3q#zQ@t##mQ(MNp5D#U%A9bP`-&vtFO=i3%LupE2yd_;}TF1CF%9k@9wNYtW z%h*3hX;Sl^dahTRuCY96gVJ=CamB4zw~SkDQJTKwc&jf~nl3S3yhLeg%&~umaaCsA zeyP%w$2wJTU+!@1mvHY+GX4zrdJE&ZsY=rf#^n)@W2{B}HH-rg&t!Z7&q4{t5m^6y z8K)uV_cn|=rx{g#hsk@b?4vg#URGM^*olwV#arR!N zNzeGs0i~%4oQ(PAq?jXT-KvidAX)l;sO?-O7wh zV~i6SPeR{poF$RxIm>ep7h->`lG5bBwd#Y|g>eq$p*F#aC* zVi>RGi1-WkS%tU?I`os}W$=vGVeF3m!GZA&jC~d34tS>aF+PFyypnN! zl&@#p6Y+A!U*j1+$#}^ctP{?O7JG;f%T?R49vJ&z?PV}NwM}Un$#@giyiH$+wq02s zgf&x@`%f#3@j3U62AKanj2&@*f8%wPn5!txl?n6Tig8Pn-(sG2h}~Ge67%fHxOAG* znaDNUIyzmD=rTyO1Bu3}sb z^Zc6q-;a9~$nuAnhYaRPL%fjjAnd&o;}pa;c~Q(^Hp`nK&jiN(u>XWIu7%HtVjOSX z)k;%&#(5~O&$tP4Zeaa!$P>%h19^rs9)|KkjD1i(nQi?})C7Auvefs8j`J)h?NjYIq!9drkmh5L)e0KXWE`;(p#y|XsYcbA1`54AK5l1ubgE}#cuOur?gBce^ zxj$n-{XvW`;IqbqeN!Nwz_=XEo-*f%owX%+(#nYiD9F<@_u_9ot#!hPKWupN09E&H7t$-!+`K zmxwDeet^At6X!vR_!q{hSR40P#~ZP&XJ%kudciu0$bXXke~kTS3*)1Rvly2_e2Q@& zJV)&~t|f?djNjn<VL&L zYw%e!jMo}@NNGyreX-;yP3L*ty(r(tI0NM!*jD~IP<#ys8&W7f;*UA`SOXtx;A0JZ ztbva;@UaFy*1*Ra_*erUYv5xI{Qq79U*sf67oY8JCdZjwuZ^^BR(1Nh@Z}pv*q6~% zlI1swjmp|MG?8#r(X&}~$0rcpdVDA-NB1$xI?3Hbr9$T=!V~AJA*qXjc$Oy&hAY>X zkv!#cs8#i52-(iqSkw40Gmh|mV@av)Q6Iu59wkVnDs3iz#zsd=cO$+h+aJEE1Rch8 zB+iq)i!?iMUidTS$A;*CGqMeTmv~)Nx%sl6TmWW$n2qlx)qN7g?{F zB1oRQ#L>99YaGdcJrxJz^fj!qt!w*Q;OF@b$sOx&Y!>(|fw0rLHq!Eq)yTH~y3@v8 znf*y#DR+kT3+(z z4x128^1*}4N(+6x2uEf`No^CS5YLHE2TQU2SCVhdQwke9Uy3JADIi8#pEingMkGf` z^+rx59fcuY8WviSopr>)aFQ#OB_MomH^Q5D4}sA?4JMuFmx<8h zO#P;EjO8jHwFayTBL4Aba+~#;De?&`+Hbr1JdtdN7WbC6S;`RZrOUIPYb<;|T)8_0 zG;<(ZH%mupaqmimgEyL`&z@hVwSKA_E=4chV3gN7a6VR&TDcHjG^U}kNhOO z6CY8BEzx$}uBLY`Ayp3UM)KRozqMAY zIE=8XwAtEVN*Litbxg6m-`-(`-FMdmSNWT)&4}HXbl~1S&!N*J_*WUixYSoCd z@LjB{R6Jfo@{oHGkThWdA(Em=4C!3U3WH9EHxWMG zxT3LVlX&7e_UfAPRbBwdv8*iA0!9?8OL9wJ4U|cCCHae+e$unk4=DCUfr-+_PbShC zIoDum)*6*qD`?j_Pt2IUg_8d|a6P z2`^U!R(0?t{pF=5XQ}%M&haO^0W5bQ&g=coS)Y~bPdbVf{!+W@cgWW5W}=i^{W8__ zs#Jei0e6Vs;Ywer)RM=f)8*+{tNLLy*?RgnhUo*F5YDa_Cs{U#vo=VTAi3>-N^$M% zptqL29ZTz0J-E&|AVc&4MR+;mvi{S8y*w2BCif=1R+A_#?vqOECRDBhSC_RTo#mGr^;fHAwi=v^)MH<#9iiC)^PQ+x1gUYY#q(lIk@Y$4HyUDJ9&xhe{hKRBNt zEgZU?{PFc20xf$CA76F!xveLkBPm=i^*-aCRv<|5yKuC z6P^4(u9-p?d%&xK#Rxy{SO}{8F3#Y&)2*d(FRK$z(#nR?rkmnfpmZ!Imuo9N$- zRjhs_*IpeAYZooC%D$yfYbm*^Ym>bBvqb5oPcYRO^@u&Ss=RjC*_|)=>_&+Js73vf!P4Xur6CvbI z9If@AByWgVRGMsWJ-2RabYIllA4~d5CF7ou?fjb;ja3bzC(NHd!rFa#4C&9Pn_~2+ zEB1OoOdIK zA=}zbHe?ljDe803^vA}}Z}z3N2CCzwn_iWOGknxA;}loyiTGtk?hxNhiX|h^Tbg!L z#JIh7N|v{ah{dsVBK(oPhICrg9i0^tBjin=JvPoiD4v69(*BlHuk+HdhhPRU%}ecwxsjj@!QrItH}Rsx5Cnx!@eZ{ zZOUHbwe7)Vo960b^}7&D@*Q5ENKvc1liV}FPfFPRkaE(j-mR?3Jw(5axKIZ6@c9b= z{R?S$+YY3^tdj~B<+UQ7Nqt+wxZv7^(<6&Zxo)E7Z`?d=?cHD?=?Ax(V9f0yL}4~^23t=S_ir1Qs@ z$yozCh+MsLijkg_9Ygv<;u4|z$`O?F#dBg{&Z`l`zr*4K9yuk+&x4O+VS`CW^56zr zt?D-7+4}udA8A#p&}r8#PMSN?kvJ;{12lPApKN>j{BAt9NAwC)il5bg{ZNvxO1o|~ z9u6Si()NBWegBmk$p`MNCapf+hU6!6W*9#|B>W$e5G1v@dzAQ_R;mV*zZK8lgs|?$ zAeUjJ|A}Rw2)_IJve?xKB$~M1a zorupxS^x0!$5uD4TaR9e5M-E2d3G)PlW~PgyeBu!Yaz|NDSGD6sR^(+`4s7NDpuT> zl^st!mLm~TRYNNIeBzhLW?2)&J_Z~b3X_ZVCHeSY%1QOio}_>1p1(CON#xmYyjsdI z8zkAE>YLYFUAl%7KE5azwwBpPF(xk?ES3CyDdEZ&CR#JAMH5f&)CkGPV=QB z-)%>>s}?qBitn|ApFK&0ylzvd&u@Nt(fGhg^q&Y%i}cNtkWsS~tQop-;29 z^F*H+G^8u^>rje#PN!Rqg=d5k&fYe~I3zO)WS<8diI+AnDoQ@MA8%p2>nYCTf}m#7 zdKX91&$$&W{d#N<@i(p>1;dsnk-lbKqLildpnR^Lea8AcUi_S*YuQhlI`TKtu|7|? zws#fJAQzWtDNs9+1z-oDrz3(!dQ9Ohyu?L{+@+olq>p5cfrh+dC;KRLMZ%r4jkT_10&Zgg1zm3gEpR-@S^rE7(OQzv`fyz3ZJbIeLWqX z7L_3Pz)mpxyoM#8ro&6a9O!u9HB|WiA#|K`4!%je4Zn^#4pHuDu)Xsw@C{0befKAT z;pJT@@#q-z@y`W^-M_>BbbJ*Ro(_(Q|OM+!k0Ibqb{6 z$H4ORBn*klfa20oxc}${^bbsddgpWC#)+|T+`|gfO=F;P#Y~7=aSys0a^Z~cUhulS z4^9?MhF<%o!{uM^!g|KndR+!V_KK#;lEELb#1bUX}aKHCHlJ)gl;+`D5_4#L&ncfr6RIZ!j#3UNN`;PcSwkfM79o-qp` z9`YwbAo_E20dj>ok{{nhG7zx`Kjt1rJbpWS+fr_~^!1CjC*j;2lEFH8Jx)jNT zFHGwp?eHFO*m@1zUfsu^^BzH5i`6j6`XlU*-3;CduOUw>!7vyFCAuU*RmB4+^XW{m z1TF=ILk={A7`4BV83bn?hgKk+06wkZ@5zF_$Q_rhVZsl3%osb7N z-H*W3*5l#8gKQXga3757wjRDMKO0)!`vHpd&W7TT7eMyO%`oY~257N-B7Df|N0mlr(7TG!Ma`p@M<*VgdDQX9b}lYFx7i!S+?9D;H9TiNSLSnN zK3C>*WjIVLlD>X_!yTc3QU6vYnRgv}~tkJFU%*{m`-> zPV9#h^Eok}6Z1JSpA+*rF`qNrIkTNJ+c~qHGut_{ov0JN+L`@uW3nOD>q&N&WIXE^6LM4jQBdAPnbTwfaAcMa!5!}-u~J~W&U4d(+- zqI_QT8okITBA$!*GG0@ z6t6+c_F8c+a4vYX%%f!B=*zbEY7Jx z$8qRf#JW0BPbe4qIv2_t&c(Vq7qPBR)SH3xha=?$N3pI>^gW!5eCT+6o!AGIGau(i z$NABTdNk<7{@}=bq8@S1e4-w4&i;wMhjaE%)TIF%m|Rad3jg$K!KYV?c=3SqeGxAn zIGzjt@WAj~p;(SpyxW!a~Wf;n zp7u{3WqqyMh3Z5e9~Vm&8{6X*0?kVoRa;2e01KiyHFj=qmVmO9c!J`BEL8iuaj2nFOEE?^N8uD z^C*wx4~{xjFGp2BbF?|q64XB^tiSxeKAFjX#W1L zy=H53h`B~^c(6GF&FpXFr})2->-=vs z*za@?)BW#Ng?;Dwr}#Zy(XUeq;(U)S{V4iRbqabbFUW^~ z*Q)Y`_wU^;==DB7^INWe;KZIqq{==q_IKY?fcbtLa{3SnZRy{y_}0_e0FQ!nOE38no_3G3yR^^AkGs4fridEt3w>iSk5+0#)Q?=~WzP5t0zL0XI=@z+nWs<7|?vy9Ze;_;VLwppyo ze?U-3K!h31eKmL^)Xx$TG(?^>Ry(Ty;%a1n!yel!qr6wVW2`FF963P#L@${bAvqAM z8Wa%}8e|E=UyqSzeimG^UzA0j#_~shsoXU7pAe?SsDi=*g8XG2j4Kp_QMoyjyXJ@> zzYty|%r6w>st)q9@}hqJXb~A6Ar|Tv6&Pra@Ch=<5XoB~I_9YI4-Xv_9%c@+cn=+f zBs%m@ z-s#WX%V&%wCvT+9^uJtQSh;c~YTn_}%Ec!fW2fw__0F#Gz2e8?VQ*pTDF5jae+{R< zV-l11;o`md diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_5_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_5_3.i3dm deleted file mode 100644 index bcea6809cf5bbea2149c30ddedb4b050c04305be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18176 zcmeHP2~?D2_n*dHbNMu*ToOm4GPPNlO=WJVK%LMqGGw>mgR1)|NGwOPR&G=-_rSi=X`q3bbrr%@BQ87xzFTS31!F=#ag zGzskB?eFvZXJ(z&6QB8b2L|=W7(s7Qh=jP|w$w@6l-3VN9qJ_KU2Utp!+p>H7eFV=O)6!-!vEyb|rB7@tM_ z7UKl;9n3gIQd*-Kw?doVjH_=^T3a%9L)?P#%FRk^d&V<1Dy=tCM7>+l<`Ux%Q18em z2KDvWegx{Tv(0|Q-!Xm`ZAuttZ&F%6VcZ&Ro@L)UpDC>mSzi%z*ui+-2Bozk+YCYc z0^@4gO6$cWk=y6UQ^feuKBcums?ZmHp|t+M_8kzHF#dU$(t3w+!#zstZN{Tf@32?x zR9X{Q|4gycx|H#B)DL4EUG&>Lv`0LX^}5wc>(88jFyhmUHy~cmxWXExbspn-tCZIB zj9WP2m1w_^_3nr#GhTxDBgSsX^CRQLwMy#)#c#=bI>M$@wceAFwQ_ch;bXV31=KKS82UAQuO}>;$p_HAg;hRP39@BYgoSn z^>-PEB3{Qhe7e%wpRodGeiY-3Jf(FdVUcHEa5jv2syK4g953OoypvsWsuXV_*l*0L{I^kK~c z>@&}2L5|W|iE;LPrL{KOsBz{`vc46b5yxIMLj7RwTNgY>GZ_bCjj4=#Yw&E&V%!yN>M?#3aU;f6 z5vv&=Kwm$`d$B)?G_e<@FrNyX&vEQeY@(>;wv$aP+KgiR?WiBgxHHzfp7C7l{|3f|Xn&4z2h7KzZ{lRr z3-L0x8G(2j8Gng6A7h+~_$$Uow`1QJ_ds0CxCHy0#<+sqXU4vWgBg!O+>UW& z>}LYkyAk82vi=<6v5f0t{x35wM&D}=U&N8z^G1ln8T%u4ydTeDJ{hb}$N5iVJR9$s z8H^WVtT4tKaj)Aju7|#Pyf5|ft~T>*k3ycNd`|a%sv$I}WBYT`H#+3T3az5hx7j| z-P>le&2{=9Ef{;na_Sl#P=9`VgIKy2D~3WVZ0l0 zA>*1j!&jN-4%+|vodkRC_zw0N#;bi|N;-%~xi+D2Q zc8GHr562mJmHR(;h0?l>_1=gb@9u{K6;+%ZOoF%xY-fScP zY#{z*fCDKMPw~^9Je`53Gw^f};BI|n$yFf7uk^QY!@lx>P zg%s=1$RX0b{>KQbR(MOQs8@(*eT(+(hTcyn`{AWlsri7$gh%)%Lv+y)vM+d?4nsZ7 zZRJ?CzRj>7-!X!0mQ24_kW(*>aR1~+us6GtB-=C|_Otzbg)q_|x-<&f#=bjfMR|3sR~~F#D{p+X$QfbXTB%<+;i!W(&|Dx*?!vX zD$=tTgUMz>MqlY{eg(qed+u9~mcB#y>a1kYeldW27l+h@bx}P)_H9{9Cw0GVAY6K; z7Q7K2MD`zi=__sVX-c@>&2-7vCp%xZuij;(boN9~;>qj2v|w|~bh3FX+9q{YouQl$ z9yh_^7c``Q^M!ZemGO7TK4SEC%ZtV+(jSZ7VA)g@12U)2qPkMdx7`UZ_V9r8!uDh{ zbIH5*?4o4CRU0QsL55Mplly}aMmExte)-H@mJ3n@;d#}Qr5o;1WaGA_snjpQm-N>b zUM*PcE%qh;Xujp*jVO@gUaS>oUuI7tn^za?w%Er1bloEixcc}3*A zsz_tG+h`Q|-fS=cdi!4?n=R|R!R+?WQ4Sx6HIvRh=R-F0E+@ftj~McuTYG|6+ZeHT zJ7+~mFZA6_zV02`Lhh8-WPfGiN0w?4@r1SRG0^_tX7U}C(yO4|GEr|z;VbRtJPEbPlW^kuufh3)KsL?B^aJKcLHKiglb-Tp4sRqGocscE8$cvdV8uy5Tql6az* zd|)Y#98P%Bj@9;-i((0Xp3v0dw@>u{(c#ktUqy<$>xO$WjLwZ9&N(?67!qtE`(1+q zCHK%egl$veV9t&uRJ$SThW$jVP)V-Wzk!?7mV4E0w^CYAw;$Peuh$r6zubxN@sc#@ z+^gHESCJn53s!$5YH3%mlk`oC#>At}9v~eHzC`?r6%Q?2`ipyD(c_`8%55*@_I=Lo zg5sUxTpV7x*{k@Tz!fU5v+E48#Ito_I#_Ro&`hSB=nB1~>r)O>JRaMVu7{A#ySq=@ zr;W7{ZZN6D^4^p|gjKOY(&TSz5w5;)zt^`L1^l_{ z<}Fdn*JFbuX{)%mi)yEV$E5pI?}W@~saK_~l>b$aAW83An|ufM0jXi_7KBg7r%TgL zjw5@ou;y?cI)dEW1dmE^VObx-BOknE?|M$`-Sv*SmiKoJBVYerJ1wJoMv_hHhACbb z!^9aWY@mb37Y$@Hq|ZqB@JtWNKW^w7&@Z74+57bllrATT{mqMM3Q?=Y{j+;+Cn+Gc z5&3SYTFE}^h?wVFtu|PW9u#-rklt1754Vf^B(b=^G<>y~$rt*BLgwY`RPWF(&FnsT zBIlyb8o1rTM4Zo*EVi875Jx$z+FlVpKhuZwbx*u2UCX~ozMIAmgZ}GR5Y7&af@QZP z!i~I#7JN2S^n8EtPD@;rsJ);~3ZxZvB%6?vIrc9~;)t_t(MF4XjL7-TCC%;celm*m z$Mk7%BL5oAi8L=oD!SIrBKQ2`>wTmaxfLn5iWP6TqWUAZ#~zI&-L*-VN_f-&_+ z5PrFx27b&Fb)8(-4UTjW_t^LLsg{^;#Tjgtf7Cv0(GcR?(Je*Fdfc9TPu1^k=`?>N z=^NasEcs99O}QmLxZ|~_v6#IF#i#5wya$m@?A&@@Nk>IK)$`NggOoNjGlpKnC7b7T zyPW^gQah~Zg$UldAwY^_D1SU-*{NnjhFuGKHH;CUR*?aXD@2Nw|QJ}8l zN%?e(PKTDM6Ue^R?4eNY;XcyW2r0DBt1j$^4I68Tu#TV{rd-XiSKBUdOjrU`899e? zsC9B6Obb6pzJ|6QaCMD%=Ps_*P`bLlGx@#-HfiMdXUHZo_n4)@Q1M;MGe5`D`K*|S z+^Bj|n6*3Eo8G!$AJ=Rk;hWu~rR=*~DOT>I?$X1dbxGe*eYL>1gV>8sHIrfPjY#58 z+Wa~kDDfhjeaUI?=#^7c;~Ot7_sV=(>`}9YBkYw2CxN^#r)LfWpTT0Dw_F)0U2Gw0 zITM>GotyVQ#jV#O2vWUklYNcNdT0?S-W^5X#MuX~Poh|T`?rHbeZ>AMrd+ZVP8~q{ z{Jv(XWV)L0y6-zl{hEsNyZG=B`{d5a#MyIeXX$FihGdiRT09sVETUR;0i9vt^oC@! zJaHs!I2cI!nf;v>a`-|14z(r~Oe@9(Z{CJjE)k$Bo>1xSTvH}d`DN;B|WEbgua znaMDym5u6pHZet7aTf@CWjwTWk9ddd?<7vLAMTz=_`P0rx0UdBk}gByEIxUn-m`a} zx17<5xb6qjEFWAKv+?4JbSdD-+thP?jYu%PA@UsFv!P{5HDTZK*w2=#7ek5v;Miy> z=4Kw{;NJYQy=buLo8OvZuaYms{yrMmPWmvYl_c+%`KO=ku^wTB@AnyDF-3@d+@)O% z+QbF0{>HbU?mq)IMQjAa7704f{}Q@P{sJ1V%zCIA@I&LqFT(BD&?O6aGj|;()HVNux`~Z^f2k6>u2lTeDg(2%kgCedNcAlOD zjpB-5&B%Z4y8eB!OQJ)sE~UChPD0$x@2#I;J~lo%P||^$gC__TX+J3ugrj& zop!;G!Lz{gwT)13_y$n*$pg1~Qz5DG4hY$M1cnVg0B&{0!AoP0z?*qf;p>~T;eqdG zaLT+8JjZN-(TWq$ZTeogv|%ia$vO{1XYPXMx~zix4R680Er;Q1%strr%?`*rIR(@o z--3Q6mtkV-OQ3%JJ23o+&!(3^=E_xYcIF+Zk(32v!monQ@(D1h#crtg!A)4GFNNdZ zZ-Et)O2H$0Em(q&!|M9Kz}>GWLyJpO;g^Q2asvZEJHfzA^{hy&u!Xn5!Fb5t+WkSx*LMYPA0p$`qOny)T*Nz>60ima% z zw!`*gx1i;|ufq5gfBm0L`>dq3*^^NMF4cDu!gjv|)2#>)>24f4&wvpPC30 zm#l;qr88mE#OrYU^_eiL*B8)f#$niU;vpP)egf1f$%He{&4fv>PKWOEAHZ4vX)w3T zN3g`>B3vDM7q%8ZgqWJwU}Lw%5Zh!DtQnXIhf+&nTlK9FGj#zh?41pDYfgZ((;vX) zb6>;7Rax*{lZ7zr%3YAWmw~;?Uifa^V)*Xh9(cLJVMsVR9RfWbK#D4SpsGz@l}2YU zsZ}jWYFKJn>R5WRG_W+XG_f=bX=WZX^O%{(%sgi1G3(i$`OM5`W!?1xLC1OMm`}%iI_48|g?7xR zV?G`8iGJw~qF*SP&y(};WIs>#^JG6g=b`63^z5f+d+x81c}-lWiSsjYekRV(WEB1; zqo~8o`(oyOF!Mf`c^}N&N3%iXVK#`m%?44o*&y;a8z>)xTJ+zj7v}>d#X(7aD5)-^ zUfd%nsVKjVIZxH)uG>H8}NqlCVIG1L< zf$UL|y;*OR>(gp9dYM;?2SSc3OF3^@%5i1scrM44*D??Ot8kioSqggt?HjIX-()Gr z*J_OHFZK)9GzYSzcv>~x16q_6N2{ZK(rWcohb)N)rLfnF{BTYAp%i|4nnT&1@D6bLDq{nM(RJVssFMRag3a|k@Gfk z-bT*b$axw$Pb24PqFJ zyypCAK5xlL*9tYXtqGDnrh4;U*aN#e1 zQ8qC@TcpiBA|}EXhk1Jk^lI-eXN}n9fq(dbAqiYu`RwICDI2$}#wFl?kg9B?Cv32D z(Pc@>YFr9%S^PPZ^HXQ+5`l)%MMHDt62@imghjfTmF0gDO}RN!hEKv!*5=PSou57l zF3a!IDwl;zL!NL^mcOjVIZu~>E(@1{&MwZlEV+xOoKg7;o3dVipXP5H43`@JjVON~ zkIM$TES@B$%d9-%!iATyBh{Dq58`HS)i z%70Qe_5YOSpSS|b+0;L0`17abSN#9~q-=*>&PLhCf3ESQQ&N6enA z`SZlYcger7`InBkDA}30tfsu^<<3nxnUrf^&eS?*RbCE%i%mKCm%o+&qfg2<-+7B& zjzRg0vLsKU`BysboSZW*8?Su!a{Xv#%J=&(d*ZSk&Ncm;d4{A8jtufu;@`DpR8}ZX zR8S~71$GTcQHA1<#rV4fQdIcLw#9`e#KfuEs5*4@_xJATt@ZN{^2Jv;zRBamW1{ha zwzXP?zZM{V02<>jAYc@aKW?X}qGJZyWTXyB#UI8Us}$9MA>ok&<80{OSA!3tLKEV` zljKEfbxZXhd|Ej_aK_HYs1HePnWBoa#Sf8x6M{^PkP=8y#m0q4g(rmL{|=zcLK84# z*zg2-nZmy^LDfd%{F@cDSXFrR!0-XG4b~Nf#i-hNQo6RdaQyjO1c?rfLcOYs999k* zIsiT5W8y@hu;GIS+2VSJ+eQ$H%P(tasTvRy6&n+6i%tkijzto^Myr#R!=thPI)fZa zZi`kY*AgzbON}UjZ0VOYc)yLk$6o>A6a;Jd%OJ?iL9|qlqxW)apn`_QskXaenmXme~>To+qHXk=vgfVX3Y z4;~VYTR^|SA~fFCAto{=&Np-b4RT*~Ym=J(Xf*ggf6ZF+uM@|WGmXOmM=TX*S8Vdz zwt=+Azct)>m&@$1-iYPOyWww(9-M$vY1HEOg~kn*&zH{AY|#C-du5z)m48G=MF4Cld(fOu)KEY z9P;V<^TWy diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_5_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_5_4.i3dm deleted file mode 100644 index b162135a39a69242290469a0ef4f2e3612f049b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17920 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 T0zbu7B~_(VrB!9{Q%dze$?^Th diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_5_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_5_5.i3dm deleted file mode 100644 index 77bf0fcc5e27daf36b0e5ceb39e2ea6d32a7817b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10704 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 H##HnVyPnCb diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_5_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_5_7.i3dm deleted file mode 100644 index c60b27a38b139d91f37014de2e96d5631400d4c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15880 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 I@u{i$Z<+@Y82|tP diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_5_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_5_8.i3dm deleted file mode 100644 index c6b9e0936177f2144123343d503503809bac4910..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9016 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_5_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_5_9.i3dm deleted file mode 100644 index e29d03725b5f7e1b47bc510da8e1c5ff39face85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3792 zcmeHJeQZ-z6u&$e43JNmprS6%Aexl%`r7pi*maC;yxDcIR#28Py7p~*p&!fZ8!~IR zVN?(dgby(h7>EjzIN5?A3amtt$b^WA82lhXNpO?M5~E>}MLqZSzOAl&nY_S2*)sg`S1H!z38^ zMI~;@BbB&#N0hIu*W{9_s)(%d4k2dXT_vT(xz36w7Yz)WEJ@)~sc@lZV1`vB8ScDX zXa2yDQLvF%ey-b70ScalR6tYf-Ouo2TREw2 zUm|DhHconv`KN(@IrER+$w@N9mjSo3oDa`%QWwL!0AFRe2Jj_@7kti19SooC;G{DQ z_kPStCm6nRij%r^|0kT3!?eyF=A=@Foo$@-0K=P(aMA{r-vc>o8CJnBG5j@Pf#I$X zIH`u=HH4Gl48SWG?gsoU!#59cQZ~bW&|SvxT&RhI;k^erDQkbCZlu16PZQP=LjU7` zyCzTl)fjKsxM>dIwXU0t3w~Z4Cs=slQLG(#oZ@eOX~b*yo;*(c=gyeoH|ANW|B-i# zaZhj>#j0sUe2%>glN>2451-yQgW@&U9Juyc7R9659?mH^{2a|+cfl8LuJ6hr`E943 zh%di!AI;e@y&?X4Vb2NTZ{6&VyI;N9PVn(V*K^L+SJJg_y>hgDM@NX}ADF#0=YX+} z;wfo+a|{<%ip{$lG5_*%%F}yuMZBr-H>$Pgazng%dwd?@e6z)c%@e26e1GTp_SHv~ zIPw3qz9T37!IczW>5bvnc|_N0UoD=y=Z{9}ZxpY$pWjhQ*S<8|9$(TeP`tFW5nt>5 zZZ6@eNo$C|IO9*MHEGvPJpb5roKf@x-nMxs9y7ij7aiY>m$v_kJB=UVr-c?=x_v7? z+u4NoExU@R&+Nj}Kl>41m~aYDIM#_TP5l&qi(4>%?-u;V*pqm+rww6_<4ZLRL@em zS`KBdlGUm~!%oFlqXoSYZO~{c-JRyAG8D=IIdf%rrL2PFttZJs$vBlT)pPs&+edG` zqdV0}%}@0tb*DU*Ssm~ckmp0wNQAy0j!==i#1-Ye&|`QUM0seWta`Pu$~*YH5~nkF zL9STn^b|lHp}!F&9D;zDCGhY|AMrslyeJroz;%i8p|D>jNGOlNWzuI+-dCdp{HhGg z%Zv~RdNoy9O(t1_K^SDrN)9CBg zD;wHQN53?2%+mi1lqP9jjdmeX=GscNSk}COfZ{6+*Hzbqpat|z>W#>G;Xqg|@cQUQ zE)%kB0{s}_zp7vt?R__not)9=3*d^y;O-`xTq^r%kNXRr+~t%GXItU8q#I5-RIS0S zw2II^uUbvMUnaBNV(RZ+%3`>3$xHjV+;DTrO*9VR63OLDY>aIxN!Vhx#mZyr;LAQ= za*>}^_PLX{U=#h#PAnV>-wE%3y%(LI*rE2_mqX4<26>N=p<~_Z{$#7ih}AKP>+@tw z{KV2nc((Ihk(-3?e-v7^`k diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_6_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_6_0.i3dm deleted file mode 100644 index 126f3bd1ab580df6576ff9fdbe0887ac33392e99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8048 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 H?YaK~Z6DRF diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_6_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_6_1.i3dm deleted file mode 100644 index 228d80d76e0d640e5ef3f05aeec24a58aaf4e8b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5336 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 NQB_k_3tum){sp-`9ispM diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_6_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_6_3.i3dm deleted file mode 100644 index e5f7d21b9cdb6285a1ff8e84e3f8973da8652285..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10880 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_6_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_6_6.i3dm deleted file mode 100644 index 9bde7a09fd385feec65760bfd133ad271730d398..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2336 zcmcIl-HRJl6u+Cb)m3Y4wbqxya$iKqF7uI(Nt6yTW_PD#lah@4vV`pM}<6K~O}b)PisNph$i2Q9*FoLZJ_R^TiiMP^-SQ|3E?ToSV6uX}Yi$ap2_q?m6e4 zbI-@UvDI#Wh7dA8N60nc&%@$6aQsjnQKQy0OSN*PxlygumdzDf%%|!3debnCdW{w% z+TIRXeRDHlAuURyG_BPetEKA3Yv-qxT4thc7|Sc>w1J${z@k|$RV&jfC98m{QfitT z;NM)~?n8gbM)Ul8>#vk?_v0VV;kkeNGi7{ub&Svco-%I6^0hxw#`Nd%GmRhrZ9e{Eq&?OP}r{e(!Z(|NQb7 zXA$4{?&j|Ix8COVR_3_=w_O(TLnBm7|zHK>i5ZCI1 zoUUS63~F^?N8tJ6K<(k?CiB;9_6B!y-*ryYj@KV}E^|Y3djL+dvZA7J=tBL~9EQTO zC@SV+W4RxzvE#!y}Oh0y!KS1U$z<6rPW$J(d>BD5CMY@Bd6fuT{$GVSyD* zJ1D<{LQ9KTXgjA#XcR1npXtzOW`(>9<1!E0{wfPC$FVyNZ`j*%p#}VvvjSH39M4~} zI((4VrA1BRpDY&?sh|}0CoaxBb|W^xh>c)&$4zdqF7NTA;K?pO(BW(z(#396ncE9t zD)S1o&+>bCztl`2r%t-}z-Ab^Y2>-|rJNLc~=8A;>0Z0N1F62orbjSrpy;vtX%9Dh; zgVrb)l++81I>dqtjB6nid|D^u$frrz3XXcj@p^`&$Slwdd4wDxN6Di=N62Gjjyz7D k0GcCDl4Im4avbOwd77LcC&@EFC&;tp6nT!E20BIl0lps~+yDRo diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_6_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_6_7.i3dm deleted file mode 100644 index 1ad3f9cc87bc616050d5e07908d15c82d4ecdac9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14032 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_6_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_6_8.i3dm deleted file mode 100644 index be2ff3209b6c14b9906e9a989757d755b7a1e58e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17608 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_6_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_6_9.i3dm deleted file mode 100644 index 5e519b97d4bbd6c6f330fedc036ca3d775749a07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11128 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_7_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_7_0.i3dm deleted file mode 100644 index c9790072f41ddbe6e93c12e5e896f6ab06d605e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3000 zcmdT_Z)jUp6u;f;ru)A+XGN!si=s#t-h27q(O%OoSzk?)(Zq>lS$R!f)5nsR^72}( zY1(}m_zx66h#-F1L^?&7B088`go&sVb@PK-h9IcO(0-|(M8$J%-fNy|vFw8bADo=u zJ%8^x=jLABS15N7Le6d`WFGkZIzm210er}6R7s{Y(PV!tJ(7qe4`v2vw-oZwL#ecq zQBq0TU8AGZmX;bDt7;bQ7V93GObri36C)2FYFEmlzoi~l4i046E&O5_EYkhaM66vE zlw?rFqUp>CB*+Z#_?9`T)%U{p>s~?WDO?`JeIM%;lyf%zoC(TV8($j~lw7`P^P?mv zS8Vz7ilC%yJh@X)dTsm#*nDl{QvpFaZ{ybn1m$iUd$$S7f*rGWyPyo(a_Yjdt%CB9 zEnnEu!k_GE?fGrMdu$u5q4^zW4kG0Li61Sc&%Zi9$LpA$iZwp?g*LFJle1my!y`#9 zKk#&<@w+N=c{=-PZ}k3S98bS6!^*;a7tsF5&)0gUQnOtC-qAUheet;-lt1&-H2d>p zf!oAiTDWj=w#aRM>XaJaFNPV~bbV869J(0dHov95yzuOT&TaZ0e4_ErrAKsXW5mzI=QcY=YAGF>*e_B%o002GtX|n_kFhi>MLwl@nvQ$o?xD%XP9xv zN9>PHC)k0~61(Szuh^T5=h$0)@3PBpoMt=Dyw0{i@EuF_{l#ijFVOBRm3+aVpL(bi zl!HN=hnhSjA`b;*ne(8?c{t+d@^B;|fm{|tA(YEf(_fY)F@kGZk}wYPu+43`jQ${( zxj%53<~(#%o6@S5Uet{u?kl0^$E~tjwZJxB(yCR>m{8TyUSANs|6RM%{su{SWj81n z^dSc~PO+4U!&OO+Zy@BVK*(S^m8?+}`URI@jTSXSGgYf%(r((HN+hBOqVmB+CJsHE z(#)z}F+d@EMG9A1H4nycbK0m1W2jN1QqT~IxjGEVS=DHMTrU+&4cxO5D9Wm3>W|~X zD|*CLXrEi*#%^Pf=jxssEo;?ryuvXsK)!(*oiO#XZs~AL_|8-dQjShqxUAWCJ-i9- zCjl>N-6-gJw1K+HPz>$%^X+P;u9oa1hFS(W9m2GjRLz4&wPH3CjZTh@Y35;FdyEJ9 z$1CHZ`AT`BVrYhynVx_k0ZI0uaMFPO`+}GX+amk079HCq0RT4a4JJGj88R-`2X_xBSBwldMlja z$hlpYW2NEb;z^X+V##>;nj5n>Jx!Z#Di!47xiZtm!iI~euO+Rah!Ce*FAM!7?7y@OGQ*x?SwwIg$Q zyUvj#Zzspr;iyM!uRF*(A^>%ePO_eCAU6T6CpVLgWE0s8w2^EfTgf)E9cU}rL3WZ` T$S$Cr9c9XvWOaa5I diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_7_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_7_1.i3dm deleted file mode 100644 index 65fdc0f5f6168f519a7af6627dac717b9cf4b6e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8664 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_7_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_7_2.i3dm deleted file mode 100644 index 068a9abcdea1587e2c36d80f3dbfb23ef34587dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12168 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 Nt9njV50AR4{{yYi&&dD) diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_7_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_7_3.i3dm deleted file mode 100644 index 56a93178988c1970cf48787673e788a4692cb3e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12552 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_7_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_7_4.i3dm deleted file mode 100644 index 4d3fd7098b9d679e2316c2485bac8765807cb942..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18800 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 Ay#N3J diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_7_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_7_5.i3dm deleted file mode 100644 index e0417afdaf99590bba90bc59988c6ed251e07594..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12496 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_7_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_7_7.i3dm deleted file mode 100644 index 75e4ef0aca51427026253dee44285be44e9339f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15288 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 SZXJ~M6!jGjiUxSqSNs=OCH%4g diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_7_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_7_8.i3dm deleted file mode 100644 index d68698669398bb94df954b25d268b3506fdc3b54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17600 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_7_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_7_9.i3dm deleted file mode 100644 index 0371ba215bf8591f14561d93fbff28135d837cb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 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 Vv=Cb2YA&=AS_^H2wzygg{{%3x?W6zz diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_8_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_8_0.i3dm deleted file mode 100644 index cf73a67cfeeeadf63cecb89ee534ce99f34f8720..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3464 zcmeHJeQZ-z6u)b%aR2@{xQDf z^!)DcoO{l__nhWl%^FS2B7_VrBIGdW69OTFvjM<|d`^^ofuP6N&=~0UHu{=_Euvep znZ)+CfE<+De4;xihPUZzTTf3$)kU{CZxVfN{#K8-yQ96_C^?-|X20Cr5-j(qb=bip z(BSblmYbY*8<-kBfnYZz2)3~JdOEA-tKqBJA;`~0&aKCFTXqU^faB(NL4Gk>uUI(Gp-%=yE>5AnHQ0d{a)*(%6u zI6mkTpssPzAs9mXI{)OiXcS5_8q`0>?ch4Js&+1V`I_j>*KoQE0z z_PV8o;2nO(uY7M)Vb!S$#-_J?2$b%q`qI(#iPWCOYe#Rof}@-|jNg1)7WD%JB8E(B%-r zE0;Y~xbOI>G4vUztt&JhtYQ2;YmST!pGh$+R~;MMKaeb-&&2Ne^vfqVGk!>Ws{X)< zEaTt&{ZYDj_nA@j>FA9XR=K`m>#06pPv1YZiuu%C*g<=ajM7)Cex`dD?5Csej?x3? zU!d22{FUBCU#0`(02`bPXi`q}tVdbjr~wSK>o4sV#C zUtBy*`#0~SXFs?^3wtN%FOFk0Cu&jA-6dM=lEWgJn3hmGtX4-++ss@uyTe**XJebC zXt!CMFqSORq;?g@63zo&!g;7ME@~^+Hm+;A#<*a&V_eicuARqo@OW&UV0Z9%4yzb4 ziD7k%n$fkGmW<(kn&N6EqbB)rWHMv7Tsb)o7d}E=aHJd!6w-K7B!vGQb{mK zHD(d6s7wT$;U?rL18tBKlc}hR$Q;T;lNh6%80ppG(Xd| z$*2}VAJ|s{HY2*>6#&as)0z_JNs>wec(E1JVp1gnGcu`kF;O_%)1#(0YU)-Nj-VKwX z<*<;()X7)@=v?eXOIa)lo3FSqzNnJfcAGOF$`8SxeXZ=nE02FS^A;LpcVcnkO!#Kv z{@ZIY;-j8v-IG4JR}{*{z=k&XQf=5!8yM}zxKtxwDrlQ-jJzSGb_1ggv7rr&V}mnv zxy|5Fmy6+RXtX2d<5^@j5kO{=yA~G3vesOLNQ7Wh(bd4X}2a#5W!e9*X4+`5Cw|!Dj<~cX_wlOVq`yl9p zlk>ahoO{l>=iHoY_;Y3pA*9q!$a&y%t-w(LAF>KndQ2S}PBmNjLjkZz_C;d@ z&8qI6AgBhS$y5dcq=uP)*DmYT4){9L9<^#-K8$(4_L4{aiQ`KzdeknfF24=(QI0W%3KJzO8(_|BBLf;1}iVKYmc!%V(93F5XfuPMlU=zkNf=Tv$+)v&+iW z6JIIsEUYMdv!5y}$(u^_<2hyb@J*#cjU0`PQ(w?8`KT9p*w1-@^KQ_mq{tUigD~RATpJNKCkw_f@i=mz+!bmSb2=g^U4>?d zRfT4!jY7`WVf(lb6sG3d#xY!UNnU9K>TFar;znbTr>ouyHFakiM=v@C$P%c~l5LoV zYru72nQ1PBoGiPztne8Pv%GPX<8(HxoRLKvIF|{Bp^*SfSGNtVz=K$t3357uVKJzd zg&j`Ot_PYdPfh9eQA2;0Ir+!y;HBB3St?q(<))68z)81H_M@e(9y1Jq5jMhSF6IX`Vd3)q{fIeK5QP_zfN zEW60#QddY~pAQ~jDJ+LqZyY~4cOy2y6|2JCtv7j0&#@k_6}+*_>pBdtbiErf-O9Ug zD|=*UpJwOr|K$&agZ{Pdt=kM&4jU=9uKG&C&BdGOYw{)I<*O&glWN!<41}ub>OB0} z52Z|5TXBi}6VRU14jlEO(~!G*k$3mtNSQ7_hw9OaFK zx{cN-7u2X17$dNabuoWEji21sOv=R?c3%QSMA#LP-pe^J9vXyKj k?Lb?}gJe6|L3RRdCl8TbWH;FZw2SN|`^bLsFwj2oHy9+m1ONa4 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_8_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_8_2.i3dm deleted file mode 100644 index 079d6fe8ab63f5cdefb838ea40072585597d3b1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12832 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_8_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_8_5.i3dm deleted file mode 100644 index 30d2d3f0b8c85b86c5d2cf58866881df4ab7d27b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12176 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 QxJz-j;vPj1UUw`04Nye!s{jB1 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_8_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_8_6.i3dm deleted file mode 100644 index a0fca0d474bbf8ad8fbf7f9549dd0c359f9d4fa6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14464 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 Qbeud$#>qqEVW4sHAIMTr-T(jq diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_9_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_9_1.i3dm deleted file mode 100644 index 1a3e62dfb1f946883147e15fdb8055b0f16d3620..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4816 zcmeHKdvH|M89(7BfLN5E&;q5zYt>ek^gi~Pz#i9-$AwJ-$%3_MNH@E8H&^x%cJIn# z*^*IOJBn>ZQ*E(!g0@YeV@9x0$4PEsXecnXpI4LcJg-PC%%u7yvwk&+9cG-BgPhXK@U|VCvYz1|zsM=h`%IhVzvh%pjK2x& zf5`Cu{habwhSU2vWgEj!3~)+-<@)w7IHh)_8Rz+3oZ?}4V;(;ba3jmr26kL1BrM6sMeL{CMCmGXA+9PT9_C+YU8b>z3ZhDN)Vz9sew+ zoMiPh0sl6`)h}>LkkvK^;=jiFO2C?^jGvL$*N>o1>kN=HVLn~hLU79ekLhCkZ?8lB zi48`3!<2rq6dgM&jC=0*l0oo4FZgirtUD+^_p7zWz9V1r68_|a3yfE0+9-cp_e%pU zPbMh7xFBLYcJ#FYVsr7{8Aj?uKjpWtTZU&XnoRNDy+89F-METkA+&iwUXsDY{*>>G zxA?6v#kV^)de<#oP4R)R%rahGvzXdHwQ`U5rxW57Z``u!W$nraYBM+^YkbgFOY@%S zFY!L}N{;d?`nP&lyp*E$>e9~|+g@Ho`AOvwW9GEiX)o`;f6lx6d^_bozdniY`TL7B z&e8{-SrD$=Kz%Qj-D)U{tEo*-#BXG7nMwJwV>x5l^b#89w+FJ`OSi6}{CuI;d)uK^ z)IO7u4fL3k;x9wJ{wtd+Nx#os$qel8GVN^}E-#o^{B`Qv&=$q3W}l`ts6Q$89y*bu z{FK#`jZ1<-)FkImgX)D|z;Tfj7Uon%eI_8OLW^ z`)N!(dyaANkvhs>I(xZ4JkHGf-oa_!3E0fFCFQ|~*9p{S>(tYji$0B=Z#<0;)||nK zqTTq>x6a`oT-b&yZ#ariEdDN@(RT&+&FjOztA7=r9UR1aE_CBb7oNbvoqO?pf7yk# z-}KBBAI=W+7gUo>FXBpZ2lkadjxQfMhOggz9Jfz5@Xzmh7;Ae@+M7HJ(W(Mk0#$wTcylDRC1Zc?8t+G+hn5ESyroy+s5?JgBF%HDppQ*o0BWynIDI&Nl|WHD}B6aW&mRt~MkvKw3bS z?@VcNP1oS{rXdNfN<0 z@O(WC0qZY2 zNh-1|l1zHh$Z`pQbZSelUA`t*d$`l(C4nBsOahTv5{dR>tvy*)Nj7&|;f;!y=bD2BaQ}wDIjK#E2Q!*3nNWd1* zD`_yTRwZM}R825M4|0o84*$c*BZ?kL@JODa6URDdVJrtYVmUaw<|a3(VYM#huzkT)l)PWE-Q$#pcdx)1j$FR$P^@M+x#T2@BVtM9@R^0N zqVf_u?e1J_?pyG(->o!|&pP&eoclx<{mwES$HE^c?ti-$D?X89)jecGdL;w1VPQ*J ze7>}FL|T~Wt$DsAeEuN1(dLA=M!w#{L`SeCEllPXXUUN|izjmAuxu?!^aQhc5gLa$ z7)7WUm7wux0*n$g5nYESq3dB>hdzZSqZ`nTFealZs1!{_H^C@HH=}82I{Gw>Y3N^9 Cqowlz diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_9_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_9_2.i3dm deleted file mode 100644 index 6837502ab56a616db0ef4f2887cdef318ea1a3e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12576 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 Hrslr@|00Gh diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_9_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_9_3.i3dm deleted file mode 100644 index ddca9328e341101cd462378512ab4d0b69b8c1e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12680 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 I_^Gb=Um#l}wEzGB diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_9_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_9_5.i3dm deleted file mode 100644 index dfe8d1c76c403190dbf90e491316a0d9340a314d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13440 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_9_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_9_6.i3dm deleted file mode 100644 index 6532af498d769fb419f90866ba8aec311ef763a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8560 zcmeHMdw5LO7C)RwB}LJC^j0;-z1pi0b7qp6j51529*2lx_=+!LlAI*dWF|40AY#Ph zB2sT!T&+^H`ke1ArMN{uS(6ma`v$$)ovxgyk9$)=)Kl6Rt`t7xU>$le0 zd!LNtIE*RIN`fF{Hxh(}XeZSagc%_S@WcH1Iyt_(EhfHA+wO^RZR6Y9I_O$RdQ-UW zl`h?7o7^Q{*CJn+l;=^pq^9O59$gE)H(VFrrCaBixWpc>+`1?k%!V85-Q@NiY`0Pj zQIU~I>E0$LuI;UB#%RfeYi(n?+Y&K{tpla-xN{Y6z4f9zX3EZvtnPz|r*V92gDB7A z_$cC89B)KCljER`q8!QbMfCge)0;%OBlmxceiz5vkiTcLmh1iZMLFdS4gd2KQT}n7 zhEFUK<*FR-Lws(k=GQM4<&j*cVE`VE96RSUK)=W_sG@w3^DD0r<&E6`9pWV%mtsAs zJl9FoX~OwCJ{INYIZi{JQqEbjR+P(mtb#ozaL#)edxqnTk3_jA?<*Vehdg#O*0z!3 z!N?al-h#L5+X2ZQ5 zI7!1_V?Ag2{;$R!Yjb`n*7iB~hfWja5gfY^J2_s5xn$1wAs)-|hp&tB<6Qr#@uEC{ z=iP<=CLF(o{y2_zpnvapt)6v=H*s8df+%n2IPX(Y{+{Q366?Ik@x0l%w-dD3hN!un z``zP2xgW2g81ZP1hagVkczKa1cjb6C`oHG&EJp0(c#GrK99pW?)iZz7jkUC zye_U&^9@lB=JkXl?#S_n(?z)^$0Obp<<&g42=PZ8zm4@oa{Sc98#OFLyn)v?dy*(0 z;kX>@+{f|r$O+=uh3B=1-;;gF>A*E>U_DRs8rovsCpcb;9D|>OckBe$VL$J*&oJ;K zL3o58?a8A%@aPWw|K5R^J-O<_qZ6#`jHBB0g(KCj`WT)Qzr?!dg>3p+7(TLTNyO#? z!Xs=0)Osne6Q^0@JP00{L3mq@{w2-{8fVDA=UX2e;wFCRpHHbHjjs^??NbQ~=CcZ>N!?8%D1EOK;myY?^WO0AW+`) zDHlqHJ~Nd3NA~2alS85@c5aGWwSG3AIMx3eqDC!mK)7A7Lk+sNo3OF&DBsIQO>^$N z65r9SPSQ#3WKvre8wsBnHotiNnF5v7kg+Ej&UjByY^PqqFCDz5^*gIom~ZC3A;hWH z&5mL^iJ$)|cl`$a*77hSIB3v22nd^9H?#3rpNSL`R?8l^cc=9Yot&wq*^p_N#@O`vXtF7RCJN2zPjfj(QWsNYoNZZ3- zR}WQtIJQwQvp?&so=&b$xn{iI#y7WvcBZid9oCaE8b3SM3&yNEq;oZSm9Kn`i};aG z3Q)2zfpR_D@^Lk6cTe&Utz2K7f4MXHrX8VNX!NMpfaVBh^R}vhd`I~my z01G~u0xuQJfJ^Vpgjz?pLiM6j==Ae9U|Y2sGR;52*sJHE?1k;HdSDq8E305!>ori2 zG74<9{sYsdj)qEgc0+0UdWh+89FB)ygT?hvLhiR;z?7d(!7DvRz`p)tq3f0dud3S-f`H{@;JO(J`Z$F4}oLga#$0)0M`FJ8-D1v z5<=l3#Ke_B*Woi@eYy|&6cs~(Z6=gWD}+HAr@*jt55sLCp)_L*@N*Abj{T*x6BfAM|D*Ev#jE&Ax9Bcsf^aAu;( zm?AY3r5TfEG>VB+EaWunH6x+1NP4zrktCyLmw9tQ4AWz zKopaa)=WlPGeuD%QkCyVI)pRXf*TQuz zT-T!Mnk7Bym?epH%tjNd%b<@W9@;48XO{bEf7qPmVV31#mep_2o9LjJWx8ldmsu^| zB(XZ#T#GkJTD*zQlKENx%(DIsdb3f}F-K~3m?JeEGxdw>)UN@r0_mY8J;|W0OJ+Us zC2WxC86-^yt$$AS;GE(OxT6}6t{?KvoM+}dGv}Ep53W<+XsK?rR5x07PAs15(LFMt zL*wBBts5k5-9YDv>jp`e5Uxv71}HfmN1DTx#-5_sTt`l}-JXddMvFcyHB-sSQCtt- zW4bk$O>sN3vNP>Do`+gWK)(Sn&XJt%aca2(-1h^G6*{WuxxL;Uk_Wo)7vDY6y(qT3 zaT&s}89bMYo;&GOWZsJ>P^}_AaP>~OV$_3qZn};)O&(0|!J^tLnMznjR)*rn&A-RV zO~pRQ@1h!^4RjSQ6x$-7@0HIh5K*B z_lizC+ZKb7_2OXLrAOo;F1-|t>4tlcuN(uCU=Mdqg33IJf%@_9J=fX8Qdr?KbD7-=yXZ=7C*V7zjHq1Uy#-;eIYuEnp!TuJ(h9CQ_-ydlGYs~sFTldccE%OHswsyNY^ZQ5Ox*xMOhW*x$+1&5*+gsQC zer9hSey-oLb%y!8l2BO?aa0n5gepR?@EDFNLRBF|2o)a35h7F*stYxQCva33Y6`W4 V+QO4KY6(vXb%eS?Jsfp}{{cx?jRODx diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_9_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_9_7.i3dm deleted file mode 100644 index 8519ca3992c2edd6e6c67be174f33f2765180dda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16960 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 m%Bsk!%Bsn#dNZLKEqdC+5Z4w(g?Ev diff --git a/samples/traffic_lights/mapbox/output/tiles/0_1_9_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_1_9_8.i3dm deleted file mode 100644 index 70d4ca1603a012d122cdbd9efe93dce73663e18d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22952 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/0_2_9_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/0_2_9_0.i3dm deleted file mode 100644 index 26bd2aa5e55c46111310c40a0e67dafd1a306aba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4040 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_0_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_0_9.i3dm deleted file mode 100644 index 800cd4d43912c15454dda491353ccb233b59fb3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6272 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_1_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_1_7.i3dm deleted file mode 100644 index cfc740ec8c2b4c1fbc9c266f755692bf88d75e6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2600 zcmcgtO>7%g5PoT$G)Wt1XbVC@5FVF8ooIg?$F?9VC*)^?V^{Gu96|_d`^8zwUPrs@ zLbbINRYH&ph!%vz0XQKJaHxc!LLwGIoRE;{1&Q|90}|rMffLL;zZWlVq$min(s;f% zGjC?z%zI-;uhjbpA?Jq)`4spqSe!=ze8@s-=8IM~KRZ|4$j#*!tVNnp)i}LaD4Ld8 z$kR+nOIHF`*xdA4Kr>1!PVw(79*>!;5|6~pd4aEje@kGD-T8J5i3s_B@z42pr(YaswlV(8 zcP<#EZ=U11d~h{1-fkFsC_hV2x4(a<$mP~|OUBN^V;o<9qSgNO%%jt2^Vy{v(|=rO za=H8M?Y(F74#)A*;`HyQLXO}1{Qc?FwSeOfe{31|_WJqW-D^J>{nx)V2EY2+xcSCy zGvGeF4QEkXD(?JUeK3 zG(%?#xm@=7thSJ|<{^*E%=4Xw3kq#Qq40$KG8n^4708FOgw$L-3~nGHC#+6apeFSk1C=#Y7FxARy?G2=5OK1K*s=i0wLY@oVpV@&^Df# z9YDxZGr(miW(&-q=m^58;(5{agMA5$$(%Z>VW!)0z@UD*aFld7hpu<1hm^(2PEvS>P<#ldnZfC6sI z=UH|IY7etA5(Lh0Xb|u$f@u6avi4Y7ETfFZ>%RZf4ZS)ys~;9vOQjAv-$9|3iAiWX zr*3EzjLVL-I_hzygHKttpd f86gjl2Z2V&X);P4B4>a`$ysuaJWL(|I!FEn>|9l! diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_1_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_1_8.i3dm deleted file mode 100644 index 058e413d1d26f07ff30c0593e75d8e8dc3d7959a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11056 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 A8UO$Q diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_1_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_1_9.i3dm deleted file mode 100644 index 5b3b7845f3be8b263208e0f1b0526feed4a1d42e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5112 zcmeHKdr(x@8Na|4u|5e>4HMJNZLFgMWcTjtUaA}rdE8{#HLzkTQe2h`yUDVPy9+)x z1gjGgZ5=1kP8%OFonl~WO(y9}BkZ+GQKofF&|)TWMiY}J(r7?OYvKg^opaBX4Uf_M zk;y;VnZx%xzwddz@9eP0Ypkw|V;JUU3d6L6EJ|V+4+{W3n2&Ihz0{d&&&w~ZwB_3i zokd)h*_h5Pb(Bg@$zkWRB3#wFuOGhd#M$53eL@F$D zjKAIBXAbY<|E(-C_e>wj>dTl z=BxD+_wY;F+qef<=?z*#-~FugCyKk1SgDWps(Aq`l~6uDos}#UmqE{u()gf2 zGEKpF`!827y1BKP+{a5#`;a)TD;x7$=Pp%VOiCqu{QSY_)m|5|f9yb^l4jBoKC(AW z@l3Q4+@EtJ`~Ki6fD>5Jl+*DglT^-c@n|A&tJ^x}}3cju{x zl*&~F#COLlx3ia@svx+pKDi_KRkMP9tzwQcEoDC8+uO>~%aH_vzv^gE0vorHnD_0i zRf?y-NASUwd$P~%3K5%Q*BX_!{uWZh^-W)Po=$m&@E^Oa(I?ME2yU!@H=4(I2<~4| zsBGS;Q*hqxmbuX<_dQMUwKxf#e(phHllaOP+4DA4671-&L@P3GlDzN!`&{;>8#Tm! z{n2NX+vPo-I8Nnn79q!kbi(I&;*jmbl>{5s&dy#tw~64B!F=W6K@MU2H#7aHXa8YR z^W~0;=)}wGh|T7Ka?}!+K=9SY3!=OnfI#9*Q2$`#HhOd`{(&^XjY< z3#ycN)_hK4-kSJ$G=BGbV)OU*)`iEes(s$wW=HbuStNeqp+}H?K^C!@@t@bC%1iZx zKQVckk~lb(@WS3kG~n1w&i>qy7drM`SL1Y@YDQi2=8*f=o70LO8#sh^T=)d-zwi;- zYTAyH+Xs+sWG>K*|o#U+* zgNaIQZ=riDl>(KbDtSGX1}b?fjZ~`ftfD5>cvex3C+Jl_L9hA=JlP9oJ;^Uv41^af zJl%^VpI{MHJBvv4qQH}SM8Qb(qF|!DiPQmml3#?txGs=XFOWE*pq?KjsShNn4c^7!84Lo>X9S7!7!77phmkiM zh@KaXR0?LUBAu&}8|6^gTkG}L;<+tu@P_JLE+4plXVB(#d%|_DP#B6B5071((W2MY z_~cMX_TTFv#!IkVu9f||MmbnBZY#!f9bHAC?2~m*1)h?FBaRevjIY_;<7RZG(M(K= zv0>~nVl2j?M<*DA5l4=}IGPzXs(VaW0k0LEuGZ%)h=>0N8xk4jTmr*9TI#SzI2TMF z9#IhvW+w+-;Xshf;_@6eTkeuvzR>0@fcC>kL*9TN6nv(hg9lN_4aV^Dq9_Dkg9zsj zRLdCYE1KX-qU|D_+vD|B2W4Vw`oZ`cc8Q&O2L3@NJ`!gw2@Z$&Q45c{OWPe8A~+Uv$PaIQKy43}jh z<;p=Xe7n^oepemvTrp0Ile*mC5efv=L{$wnHFB`bE3Y9zzV=E@=iGt1`hZ{dhn?%{ zA&A+)!>iUEXz)Y-jTW2=w}m(2vv_g4^nk*|mOM{$OQ84g_KisbXSiz;h)k34B1f${DU)tm~av zrEqicO*D*%#pBDT7DkJTNo+9*O%+Yg!F1lg4K|S9XR71f@HdJ3ZP%j3 z$MSA<-?71G#S8xq#Y@AQ)c9Db>9Eu=)@%D%iTT)tbz|)@udP_UhOrJ~O==kLHBOTw zbsCT5$fen866-Oh`#2_^Nq`l{u*?J|k(mf<0+Yl{Vt&9(hBb+q!c1kRG1Fm9Wo9tR V%zaD>tYl^;lgj*%nFTAA`9F$?(}(~7 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_2_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_2_7.i3dm deleted file mode 100644 index b313c3cd7e625055e98946de00be2f5ac635e3dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2264 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_2_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_2_9.i3dm deleted file mode 100644 index 9e339adc4f4b33523b6977d9c4c2ad94ed783e6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14264 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_3_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_3_7.i3dm deleted file mode 100644 index 83205e9bb21051977066b3739d92de6755c16688..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1944 zcma)6Pj3=Y5TDwAR;^mK8gJ&EJkVxYC@L-2L(-G+ z?!}{LzeK-)7w_KuBF;Sa!MY{EO=f4_{QJ$DnO%|S43+@k+d9B^9rPqRGz$4jF=|Pq&{E7dj0?up>1)?Gp>i2WG}8KNI9}_%MvMqqPFM> zx{IK8X()NpSFTXLj=jg_41wU@E(F2M0f;W|qk2!Rr8X)w+4t#d27Oj6Bo+bd>GZt& z&4bo<_i*fzX3(VAQLiX8xtX4v!lcc^mUF^Av)>o(x;^S$S~vpvd6_OR*nQhMG~05K z&$ZpOCZBjbqiY#GGu^o0=0O@2fGak}-JJ}%&O36(vxbMKd}qRY`*>ZNMul5F54Un( z$MKm?kNzTwWG0oEP47-Hu3R{DnyZ1EOPd&vg@9AZ^td_xf*<=qxjOxW z;FmA|9TUYO`Q~;OxJX`K1^hrcP71}uwBVyBUy1J(^=^hkJ^-;c$Ac{;$$Z|I4{9HSVmfc6+8I;33imJc3Pl3{Q|Y;VEpvGkA`)1utM5UcxJ+ZTJhf>9|Dz diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_3_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_3_8.i3dm deleted file mode 100644 index 023afcf7c0c233713e5e4c85f01ad7e5fead4cec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17488 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 RNmUtDSyeezd3=>s{SQlHOr8J$ diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_3_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_3_9.i3dm deleted file mode 100644 index 4d55c04dfe2c59bb4d2a4431231598d2d8ddb761..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18136 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�M44#M9*xP+6 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

jF$LGfz}%{aPo#Dz$O28CP}KU0WSXi#WPp+WJq zLNouL2X6XT4{_10eG`pCpS&IxD#Gh2Z?>O~tm3e8OdR|aM|IRY(Ekw@%I6ISZ&JuN z;W)dzPYGsGh?3h=f0u_I$I4*JX zc=dr-XkF2q3&l}r@FsaVL>QPD7Vhu;4*uP5WGR*EoRdn`u2To!1WgcrhQ#j(NYLOX z1#3)DTvUw4Q`548k5BVf&2?>j{Jrs$1Ac&rwM9kZgwDlPga5mKY%r4Xiv<|P;>Xqm zO=MJvRYtCXiTI&b86{|f``N-nVyw74K#LO*L2)s*!ScYxwXy4KUQHY)9I+!A^?`|v z6EqRl*naXi9LR=onjsEzX7?t0}UeICR-JK29Dc@K+&dJhhJBm>_?Nfh{t` z7A$ixuLw*=T4QYZ5nDKk42nR#roHS|b{Z6nD`KN!grmOkp`q58E;j2RGE(SE z7#eGWqavcCBCU~e{zIbCh&%qCLsrH|V*T|7*_B)tonFqxCYQ?iGhg| z@UXYA_{x9k=bw@EcW^QICp?n=?#-`&PODVHKonw{^>%$s@h^VZgT zqa^_Neg)uLDO^%^ULtaCr$^?u*y z0V^oeB&*dMo26>|`qsQM8`Cz7^$m00K-I{iSuRy83#uHcDy62`M*rr9a36RRKD{8; zpT8vxsQ+<|?tSZF!gyMn;mnhSu^h=ijNz~UOBiP&Ih~oU-J(PQN5k#*UqAm)AMDY& z)vaHyTrG_R{$_V&FZ=mTf#3h}@}Bj@t+5eieEhY zu6Xm-m&K6TJyzIeT0XC)SyJ#ka$QjsHR2j^Rn6(Fm1G@$hx>s&u$=*&)3eHj{*e1V zcgB`AB+CU!TA#OBl<4{AZCX*C2Samh8SlP(0^sjs0N0xJTF5Mnhusg^fIHl?0@q^& zR<2j8rH@M5devORd)(okZ@UgEw3NcInSK|IabzOoorERE(^^v+vO+r6iR@=we(E$8lqnx2DOEjkv!$b9 zJClI6VKn5dpvK>VhFRH8&+d{9<~71(SRpO0%RL+0F$>~YBb2jE3QIw)E}rmRZx*OC z>G!#J!{&E|ljojSl6BqD*mbxQnD@u%B%^9N2`3KLU(Zq~DvPF5E;f}*K@r&}3!>q6@P!k2~Hb53J$P?l#=XV8_80h`)~I^Rhd1y)~;V26qUjinBXwa)b9oj}HnS@A5*2Gpm>`b)(9iL4Z@as$u&qZ$R%$Pv^7x zLH8C8)3;SHpd@Ftvsx8QB0Q}7O)hBI&$ Q=`@^!^YAXbhjbqP2l@j7!2kdN diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_4_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_4_8.i3dm deleted file mode 100644 index 165c8d1ab7b009da9b53a3e5a26e72abbd72a513..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12144 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 Dap=lB diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_5_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_5_7.i3dm deleted file mode 100644 index 3b7cf4097e8083529a0c23c74e36fa44dd507029..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2000 zcmb7E%}yFo6h3OLwQ5_dw%g{exKpW&3sIcTa;=~j$|aWeS@X){<+C%Fdn~O4F;*&9kF&+o_m!wpO^&tI z!oi^)HAtq=pq9%P^HI%?ikf`3rk|pB{ZKgfoB&PW7s zomoEVW^&)Zy&>7x`rgf#+t)&`1wS&Ee{M5AGutd(XPS~wk}M|9E!K#!CcogWXLd}h zL;8yCc!tqM3yB)REBrcLeSa2xUq1x+JqK`9E0=s`xS~5g>u`%ZhG#o0&2r^pG5ap7 z9u)O`^mD--*R(BEsBwj1xo!)Mv85yAVpyM9cAFDY8UZFHIiI!8&2HP_c)gxLMbGdY z^8+z)C8kWE&9EYj!^S9Y1TmlWxO+~sCC3Ow0zT_IX3z9YY>UXu@GxX^;1TPOmVl)b z;a#C2npWFvkqy?>!(v!EDbnSRiR~N)v5X$d*)fHsphgRCxVAG4)Eu0hap%P3m%_lb4!yVtiZZh87uADwI^RE>lK3%66-rcl%tRij!=YL|k@6SiXYvQ_LoE_Emf)-W{) zdL9K)MLn|j)LLqzj3)Yi_cIQClFw<=0;{Rir1~a>R^nSYc0uFNB-oTc(a>aOcw!2} zHusy(G53sa*KAeoLFe4U5s1H~;qsi_wVi#VB^J4^#8Zm+5{X?^*;RM%Hf~h;$c?;! zD;D7H4u@RjZ876yh>3^~pHdz^!!{C=o^X^%9wyRH;z=$kte2Q{gyktQ@lqzw zs7}i1jFPmKC+P`~_!*dmIiwkQ0Q2w=7LexQ5iG)ESVCHaWmthH@DynUp1~?Shc%>C NSceUG0WXm@;4j#G#ytQ4 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_5_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_5_8.i3dm deleted file mode 100644 index d0a2cb3b63f5556d44295753b795189864a621f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3856 zcmeHJeQZ-z6u)!p#y~y>45MJIk6_SjqwVXrZZ~DL+ap~&y0VOoX7=N)dr3c*_7$0> zjzmC|sEKIe2gXE$pi2Oipn|k!hD0OI05K2)8l6T1Bk~6db0&K3?R{J6W(I!{|FOy4 z`Q6_+=XdTs_jbJ!ABvVBgci(4Xg}Cs$UOfaBv;v^ z4*2zq-vGRYVQoSD)1Y%7tF;~0>1O)rB`V=wZ@!kdN+mqO__2j5;d7=#dY%7l#R@{` ze|$T8lK3HnQOD0&Qsb!|QGDfkmf|yiT+0T=y6HL0eybm!7zs3xnBMw6yyW{sH0Ie4 zL_ApeBjxumOXG?MnketPZqI$~;%M&9`OfUF3y)KLaWs?_w)azyVLkB*xsFLQoef`IF{XIbWSN2?N7dja2!;hTA z$I_?q)&4@as2MS zH}J^yxACjXv-sSF4{>~WA9jq5$QXEKO_ zoAuNWmioa`zeT4v6F*o>-WoK_QeBJA$mYiUTyM(H^(JLbbuD@m)dic^)thKtm>WTl z*YbLjM=Z%BmgEsj@`xpQypGpW9+pMpjV3M42WujJux8Fz!v(}mVp5jEQY=jRusS6r z`~3b0gxm^rO2J+^>QBo5n#UV8>Yj+0Oo}n4Uzju1JC%Wk6)D^dGgarmWxQe{8t;qb zTXi!_DL96y;f~3Us=*80pUO=3*_Tm=BVGr2erzpA=zJ+cYunteH0Ot7fyYIf3yU!^ z;g{nH&dN2qole^to36>}b%5|DF_DzwF>vVWG#tDXl0gWD7YRd2xISqv77vL8X?z*D zT*@rX1$(7PC?P_6mlhmRznqXZlS!SXMl;Q*Ep!y(!eHQinVK{g6_dT>X+(qp(gmcs zzJwH&WC^Y}U725ol7W;=rfK$u;;c73waDv9iiM;giGh7ZVKbanPnRnuB=}b3i^Tj< z;JFr3mK5~|AtM=2B-Jw0NgT@oLoA~(F=YPeuL+82+BG1=SS2O`#$k!vUIz> zk)NqB{BN9Yf76xL)EVH|>6k1{h+5@OacL6fm+2|w+uRpOw1~1l5|M(Qcq-f*gCn4C zT7Ob(j7Q=Lhd)R!a+jvgtf3#R)}qr`be4%5$9B$Slmxh98MwRoL-vRvdd8CtFFfTb zCu}gmcFAct#aLK|TWQk4@%a;B^8MoV79&4-dQ+0&$`z(gXtlx3B{xx9q@^R5FTXIh zsDi>qy*cB{?1V4-YQaT*X4&Uf-j7@9Z*@L#CVVTpw|gy$KCv^cdm@JHl??JGAw$86 zRro@y_=r_7@hkH}OZdV;eACSduZ%*!f{Bk{#VVN06;83mK7}W?cqp-oC4Pe0yae5W zN@0|sGNeM~r~-xx%|exEHkt#Y65WaBqIqaOjJaq5szM9VA{bR@F{(yO&|NU9(LdK= BlyLw6 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_5_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_5_9.i3dm deleted file mode 100644 index 076df9339166d94c4ff24508ef8dd18c3d785876..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2936 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_7_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_7_8.i3dm deleted file mode 100644 index 9bff8d905d72d3d5a505390d761f9ac29fae546b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4024 zcmeHJYfKbZ6uzM2gQC>>s;N;ru?cBbI=ee8yCiap$YbaN0t;F#(P0^u(PbBR2Q02z zHhn}}HL0mhRhnA0O)#b*K3Xv36)Cw|%Lnwa~0QiVL#7jkGuB@W$oU&!koT5BeKA&N= zn)rpqWs*xOF5)vnd{vXC6xY-Q6^+jj!Y00`xU?|KxvYHQV58Mx8!(qjdHJrv9_bDX zc$8&lIdcY^%x1eCY<Fu4NFxl}`g`fd(anyy^QBJgZ?pbv`IM7f4By|)Nt+nv0AFQz?q{6zA>*$H+|2MSz^fU~ z0=$c1atHbs!3IL;fBawcWREi(9ldx;2Z??2HjjIPIrL5{B1yQVuZ-gP-E!ux{Y@0# z`*{u?cRD*t{EtNZ*zwIldiFsd!gzKi^=Y{^HhQvmF~vJq)JOAfwPh0jgxA+)PPp1g z&*shgIP+3UkouJGyqtNr-9z=AO*3%o4JXwvdOr<6tBF+q{KK`;U0Y|;SQVGNxU2L8 z#baKmh>o`>Qvdmxt@FZsGsRWE9GJH%zL}nNJ!r#FMWLnR;T=8CE4h0+a$j6U&n6}(N0%JSqqsYzBzmP~ zGWCD@f*egT_0oFV64v5P%dHf@(X$#a;v+QYEk_GF4*j%-`Ul$^qtR{asn3#kcjM<8 zPU4pr9Knejzr|hN8`zd`4NuGI#W$ySpyzhtWc;%e+_{5G*oVRcz{@}p|d?I=a zpPqORzp||dC++zPFZj9zN5oV3``$LZY4bXK8@-36?z{N4u8(kV&X4%G*oh0{I`Ek9 zw&Tmo&*8>r&fy!aJ8GDI$v79q`c`CJM{x;;VVsimC~pLHPxf~JfttVUL_b*eD$*IO}5$y^WUX$s_t5? zP7Z2+n)9zS46P-WGZsJE^Kq?1{r`yO(fRy441=Rol3@crkJptO5C3l>V-UIm^HM=s zaZ!ku;qAZ>6yiOKPYKAHKfq`3*~Lz0)`Bccp3{{Jci*T4f~wyK21}~I!$cEwgELG{ z3qZnhFBcy4EoBj zDa6+)!CErH5n_Nefe>FGQ0r7pg`Ww{Ox7S|Rf9&hAvWvr8RpoLkj!wZuUd5zAE>Ji zis3UvnywO1WiJcjlj}gw7m~0fsO*LlL4QCGRMk*ZqXa5cWi=)F-IQtK-Tu0IzfbXL zuBLh*Ni$nuDs}rCe9(WZjf5g?u~9fzTB>+18N)TkQ7X>KiV!keUY9PS-$aqL^#^8629p$r`-{z0LJi zfkH)-yTmGW`rr!a>@5eCY`@nZ$d%pnAuknD?E?MGW`{*^SRDN?j#bWRj05n* z!ti$WODRW4Jh|AT z{aj`6a>+|H58|?r$EPR8l8Omz6Yb&3@Ot>M&zFkGh{3+cnP($(j?fQ|gdd0ZpU%bL zCvv2``+Z2QWRdxYECU-Pp%UqK{p~Hr$@*jTLJ*FtHJANCT6-p)=%Q zo1rIi@G^W2N$do(eH@BMqhQ6M(TGE1&{$X;8i&TC1oQ-~@hA~ZKoe0CtO@8zGzmS0 SCc~P9rl6^48k!DkD*6qspVwLd diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_7_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_7_9.i3dm deleted file mode 100644 index 761c428f00969eaaecb795d0e03a5423284ad452..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5232 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 x>q!RB-NNa!gp5JlKLBJ*cvS!Z diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_8_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_8_9.i3dm deleted file mode 100644 index d2aef4dbb3ba74c52a2936521cdb79917d0a6c69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7528 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 F@DKfws8#>~ diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_9_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_9_5.i3dm deleted file mode 100644 index ebcaf1d6af5dd5885916fd4b998d09386a15c59c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14592 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_9_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_9_6.i3dm deleted file mode 100644 index 7551815156aa39eb7eae5821df16bffca28f8946..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12024 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 NXecxi-0^BC{4eZZv77(^ diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_9_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_9_8.i3dm deleted file mode 100644 index 49b9b61f81e621f2cfb4aa85ce78ad059a6dd0cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2976 zcmdT_U2Gdg5Z<&Tkp7Vt0ToKx&`xVtn| zj`JhbHsYnU0)$YJ_<0ErsDOkjkXn~i2qI9WzJP!tqKcow6A$H~f+A+td&$)aL`C9( zlg8h-GqW>0v+G^MKb)^2guJ$ukOjbnO@zFI3iuF;G#XE)!tqE~awygn?@9I24mIeb zhZ4zXDw>GXjv`GTb$DWA#O4m|P)a@;PxSYNV?$3Isy1p$>#Dgw+S8k=_Gnc?;E{}k zV_nszfU1G1E1XOXL4s7Ti0@bvywn8Wmfvfmdy`iV;<^`sPfOl>t2TOA@<;xxjT(}V zdH6K=9GChJ{-}+flKef;&q@9>@RO214ReE%KLoiNQkA?J@Oe?{7l7}Re4D>6`rM(4 z&kXPrlD`tFi|XretfBJFGM5qZ|Kv}7Ia9v)Si^a&yV^Y0iT<;D2pc;yDD=A)BNzI6ih{Q+b(MSPsovf;YU>$ZnPsV(z79 zk9RKBOqS8*M>fUIyxAh=+LxEvs~_E9OE=2wE8lhY*^VDraNlM2&0DwFsn(s9ek8;$|TL99%7AZ2>O z!C_I}>2UXZuYSCc4RhGYpbylQhhk_)t5`0#3_T~4n0g-cv=7r_QauAR?1EKEl%5zF z;nrb;Pl_P7y(T`IDdfisCO4hb(QydUrfPmvPMFYte*jZqTQon`Vqm)zV23#W%2*z zZ*33wS9fpCWH@r(sVlLPaB}e^s#UQxJbaaf$whe*2Ut6A}T2Yu3m%Hfdz3AEyU5<8l>_yaj1#Rn%QSXY^?sBvt zchTiIc6BaR+gv?jwYa`6q8+&$*N{zQGe`}&i`0@jayLjVxrfw~Eo3W5J=sRKlO5z< aknN;_>?FI$eIPr@{iKmRKpq5XB!2-$&B+r0 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_0_9_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_0_9_9.i3dm deleted file mode 100644 index 2eef8987e0067925aaca12698cc41c504bdc614b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2328 zcmcIl&2Jk;6rVKMO@IIen)blK8VL@_iS{G@l&IwxC)wiImA#}$scY8u#97tewRYEq zvKVsUz>On+0fGxE7o;Ge`VT-z<ZvxJbx1$YF5^96yx%RI4`hVzpFmv?}H5roKhTx{qq@UL%i z_ntFi;{^ZK7vkEzYrkB^bGOdMwI4(LZ9K019^z_5{?B||vzvka5C6urh4%uy@F=Do zUJWq%3ciDaK!p4^yz}S$WTmys{k`#9@=~+$@dSOB{`hEeq5IAy#CMmzzw`CT<~Eb^ zUw3xie4pDa_e+y|&DXj7@9UpVF8W`*h4%OFJ}i9p$#;bZuN@ZXm$wVP_Elj!cDwM? z^3MgInq8XTrD|HvMf!q$_g#0>B z$mK?@>Qe(^!<6~7$1LU;p6$>)E!8TO;@d@Kv!ZW+(tbE$pH~U?O!S-Dl6a&L^%nxy~DkY^8XeFwM;;1pmo8zQU2h82a zJA#e@@&ovE=$Hf3Goj45yb7k(H9Kenc?}>LnqT9`WsV80 z2!dF~0OWKV!(vdQ13O&X2?Di8dwa~;G1&*)$rIN&NjvsnXj{zk^jkx4l9Ck_g(C~< zuck2+mPJu97Zb}R0rI#lzrn>i)E*v(ND#>3)F9wF45ILSMD4M(SVj?z*Zt^c8hWQ( zQcnx4WV0vbcT#9+H3eNR^p#5=M*WYHo%CDVRnNi*IAeMcv|pimydNgm4S4z z8x>~tJebOi0_`)L9^Nl?EtgiO-8;4!MlQPb$X5d<7bj7k@ulGK1qTZc6$zYP%Z{7l z&*8GKYgK&C!tZI`*#rJQ1{+VqPviIhK8x^={Iqi)*KoF~iV0?>K#7#ijmivR!s diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_0_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_0_0.i3dm deleted file mode 100644 index 0fbd1c809448b64d936dc3915ab4fde06d4ec10e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8952 zcmeHMdstM}7C$ne`7TLKvl~s?{WPR`a%Mv-8^tf^b$X2sJpC-6RN_P6+Ua*^AXuVsc7!Vr*P;MnYWTu$1BID6Li( zu6`gXSxS+T64g<~>da|wThgRSE}L5&rSXKT6O+b_icZKF`@p4(TD`$=aea(5Y7Tk*|B>?T}=oP5HI&|W}zBIY^NrtlA9tQ*O`8gs59{3~)kC;d#s2MOPU z_#48vA@=3q#l6&$ea?B6bd>PVi0cWj!&v21tDlj-hxQ(gd7dTw8s^zVIQwgrw1@T{ zf_2zJxZ1A{BM+;j4P;OLOeMWcc;Nw+w2JV_29@+Y;ep7nBbeUXCc>4d(@0qViAp+{snoL!aVgn{;C^?eEB2*dsU$tswlm^5!s8Je36Db@ zOZYU_wuS0;2JxS%ZgUaeLi!63R}mHwZzlc`#51XXZoyhDAba}9D(M%(LHkwG%@oUy zar+QHjCeTV_nTEx2Ju&-&Le~$KB1D963#`tgYYu6FC%;&&(7E9*{4-fSIU3cw^&0u z-xRcWBKvf-A0hiAr&LmBve%!v*c;ztZ}@a-v1b~IKNa_TfUpj09!kDZXrD)QD8u*b zEamng+E>sXpU0jbNVozyKM==+_8P*i7|Zuw=OU+t&N>A-A;ejX_T_|^U~dDRODNMP zJQ1;*aCk_tG?MTs(Pzk_b1EsDINyD#l6H{J zV_1_Cs((l9letu%FY$c;MEdM=Q~4zEhgdL6F!#`PY!-UC@ zK3yj>{?U_@p~b$>!tm=MD=g>sxVZg^%?0r3kvi@hy0H|VEsYtzZ$c8J$OR{JLd><>5RB$2BjE^?K5#bNu$N zaq?SnYGC|rO;hD=TOB-yKQ)2eR5Xy=k58Ni57gxF9F8|9dxsTG=k^61AF<5*U@F(? zQDKL_@A`=QMkFU&;ynsy$?hI2_j$PIH z@cv9M*Li+=FId?(30Q6oEhX}X){VT*VHugwwU5#prr`!?c+<#z@7kFU{WjL~JU9H} zlD}?Q#__7CG`agn0@qnSAs$}de+S2}?CA+HRf_)E));x-X9KuSm3)sppnnv{p&K)x zu=y<4>Am-e<&7X4_njLuUB0c|L~eib%PE!;=TvTQoPL`(^ZOYbCqHC>?_N=IGf4d) zWcvuN+4qnI;-4DCapMj1hVGuM^lHz3PI>UeHC(f=F$?~B?1Y!?{eFiTa!WLvpS8*1 z{jN!gRWomecYeLn!>M0)f*tM0^SGyfh=X;b)ttX6YmhgsK*{H!x#gD7_(wTs$jY_e zwLy0 zn`0SOJ%#fv(G%oh?}u+64NujKjK*8`yWObq9Y|1eK(oVK0o zZy=RM~P|#W^o*8zR@93g@__wG=AtpKoP31TF6_FFBda^}F1g05Pe3 zImgvdJ#=oA^6o?&n*z1*vw7UudrM{Q;%Z*Er6ohbo*Uk zn|~0-RxN<9`fr0#)fI48_N|fW7ru*n9E?INVYWV#GFxIavw2cCUdU-KyZ9>e=8ry9v77Qv;iK zFMz?LUxNW5zrb^zpCHcp9t@i>3rc#Q0o}SJd0JdIz+4%HgLeWw1A_4vJpc0y}>^3RAkg1;uK6wmK?R ztrLxUvpQU@Gev52H0o&-X*AJDJgrfgYn63Tqs&E3B(s}!My_Kvn7JR0+|O*(X!*KP ziGy*8GTH)_aVt5Nt`($Ok$j9Q+r*{CHwB|n^#o|30oG$=gLpzuV4 z5?3@RJkg-U6%9&U(V)Z?jY?e6sKga%e^@x4k7y)4Bk37Q&q#VkMaN`j`RX-Vk=fbE z;;@me<7i@W*vR6rk@zNBH`BVA)=5vNWAXJGosPw4Ba6>QC5}jbBKe8rCz@IQdW~pS z{6(|kFPas9(d_eA@)Y?w>NO^fl81?(Cz~sInly@@iT4AuD>^2Ao_ei;*I%y{BRL;O z&c~6*#gX%I(&dJY2ekSrW zk)Mh3Fi{@LIpA?o9!foRSV2}toq_iyj*1?RTu-O;v0i6X&Q}+y)Cos}IxSqCX`5nm zx$Qajf*kffCfKtkyYsCsH)@U>BZUnPqscH^o-Hibk!y3}ia*OA#}wIJg;r}GODIwk zHYv~Ma@h*V;!nI1ZWATN=FE2#<|)Yp>gmmwMz{Le} z33u0K5~yO(Uz%E=rA?$ZlR$2uB|33@OxR@#8<@+LO8rOdfl&f2e!buyDv%hsxi-}Z zbohOi|DMF})BOK>wQ0|5ocNu}HF~t6U9s%~-^D=7A9@!9OWmeR{7<*dLST0VDqV}E zjp#K_u0{8M#c7k$?-TXw4L=yD1f1@c$2G?FHeW zN)SdQCnXlEt@!nVcZkJm{Ajj0t!{@?9i@&=7LxJek&p|&_KMX7j%*u4nlul7Df(u`>a5B3ylkfp-BY!=kZ*N6?Niw#LKCjJ z%rn@3!5{mR(VpfBFIMN}I_>#(w;jKhd1h8OhRiH-vuQD1mExVM z|BWiU)7in`z8?MfBj_=1;dsbRjMqe~u_Y1!+h8`CetG`-coyl~${ar4!Cc=ov z&Yz2+nPIT+*QKEu&Fbc-pp>~V(>coKw&vy8v&J}zawZqx5%7y^tIHPa$a6U3ty#R0 zQ#BFzZ(a7I)tYr0v(9{><7nq3<8#0k^I-2PhdjoX&CmE^!TnD;;Dn7LZkL@#f~_FO zja@0?oxR)Y%wg}B-e8W@Up&14XKXot*9%(7*tx6|+5Nx2mX0-_k{BiB7dX;j@}zkx z@nw&Z64{>&=;z9ROZ71>-anP`zbPp8tKr|G>x=YTe6ALsY5H?6#JJWyFdln=9v}9N zKD+vO znp$Sw6Z2MPFvq;0c}?@0Selv_?B+G^|2k{0$wBO~zW@8b|Noxfd7i_1_d4%>*IIk8 zeHigL4gJ$AsZ^?GTB}qG(5|hgQgwJ30e%#Ju14w>7a!3rvU6M?d*^Ol;-Ark=}h_% zO>9h@6febe(}d+}`VI3qVp3AF93D-WHZMfeEv9>PguT!6v3K6oS@g!+_q$77o{7Jc zVhA-_kP;UeVefqBJ(JO_$9tV4;^O;YjQD3LgvXuj$SbQHAZ>K+>Z;^@0P#x3wa=-g z`l-VI+iA6Qe1O28o>EIQn4?GkB*wNgYH0-HE@f)zOXjac{0-xvvubG(CD7Yij8d$I@O>OMwGLZf!8<6YTGLQ7x(2|32pV1N)C) z{+k)kNBCH{S9xarSH>)zb3Ev;sq4Ty)ZKO5`K;d<*}U3S(C!*x5z{-!(CQgxnd z{{h#Q^;K9`3hOMz{Oyd_Tvtoe7}vO=mi93J7UC$zg{lDQI%B|o=*D;(*3y;nYUFfg zd=l&Gz}WRbfK-$wYJ3fAoW%G;#FH7XRR>7R8J|Erk8yj%>lkmqxH99-7;x`y?MeOU17Pvm2v;Psq`xsY4yq~cZbGXHLBI0d~b1<&2PtGCU z&HJvx9Ck52jCn3%JO=mu7~>K=o285!VUMk6`~h-)VLTkqh3_0$k>k_M{9Y}sV2*Mw zMl;@ue&4fV51s`x`$yp!8O;6p;sLdk$+*fvwX~g|CsUB~E8}+%f6MsHKDBg{`7-)d zta<5cwX}{oi97K5!}tNjOBmO}8s{+o3+(^b8BahwoAD!v3t9h(^J-~4&&|hrk1(h8 zWwq3tpBqDd!Sl|1JMM*-Ii0HnNc9+hj(cHa&ROJq;>!m)-=v7MumO8uG2>Inu``HpKmJE zEMWhOxDG41$13^tl5*C3y)<^OT6&(JKY@6T+VOc0$2@&!BmnuoGrJDg)0ck;_P`dd z_jk-~gtSb+KDFfK&@M8?fesHKaXTMK-?**WgF_#7)^&eyo_Lz!d7=j2qz zWAOQJ;Ilc_4Bpe{V%GP6-YUNr#b*TgkV@$+MM+S=6_C^+i3ZRgH-$S?QZ-+uZOkwfyYL*-5TT~zPMaryG9 zjoFlQqw0C`!nzHqmg?%^a$xbz_DU@yhUdyZMFIJHr@R8IgIf`fy^#;&s@5%1I9)q- zg@m6QQLMRk8_a3figGyob2e0I^*Y7+EH4Xwe{>e%n`;u}m;^O(YE^xxIQmdNVfXc+ z@KX&J>5sjhFE?x6qMf2YD=9^u)8TuXyL_}=Zuh%b{~;gs@-C?_*09^zAo^-_Tq$3#Tpj<7z8DwMXbjr43p<%_ov(*{OVgu zk+FfqSzY9AHrU&|H` zKR2yOoDbh|!;bNDXzrq?Vqp1zM~VM(!)vxf^@YCX>UP_<-UG=Wzvy_eF=aT-ZB=Ku z?Sq>l=VjU#psc||C z)4??Nh@*}C)PQip`)>AvYm)&gMgl78~j8TyUj%p9&gAoR8!CdR3bRUR3iXXt}*I`RhI0S8n|A zWy<;T6EX7eOtB`1=H!6Aq<}ON9_%Og?E5R>;{_I5pPjih7f-Mpe>7%|68BK-V-Tl~ z1_dv#GaLfmxJqX{e4`7NEm%!+KkT%%Xjhroqu}XzFmX>citAZ546e*dBE0ot3X~ow zC4Rl9^JRE`F7?uw6VJ-kw>6-+U8`%nZAxCDSgv}Hz*m;;J=g!kDB zC6Nmtb@~YCT)Z2OW_=31b}WT=19n1M^>I+E*#XG)&VYw6uYs*^d=IlKUxF>B(eUHC zQ7~rZUf8dIOZP$Y^ljj7cOK@S-UMAQo`bz9H(>gT zQpjz)2_m0)6-L$>0mtVqhDpV*f>AvRJO{r7&)ks^yJ9nx78XJ8bE6<`!Cbh$XdEPt zy#mj^bqU&keI6#xeG_`l`5YX-e+@NyoPt$fT!g-#&x1`L6+y)5GDvG!3NL$#k>d%Hv#WUg4wx2+)-~|v^X#xa&a}lO2I|}VBBVgQ1?}7IG zO=who5iC9Ig^N?ag2~tCLrCE3@L-?aaG~*T7;QfdF|*!-QS*<${M30+!~Pj`Z}K*@ zeEbT8C0>HJ>XpD3H@$HFqjfN7b}_6yS^z6{{sw*9$xx~849Hz`4jQ#N21A?Ag!utw zaB%Ksn9z7Ew5_`af{$H?=Emkq#Viof(R^NP) zk5w!3v1)~$RZDtCgWhLJ7ssS)(g{7ZG~cARkiN;FC0!HGLre3}ig^YjWg|1cPZL*5Ep<0nQTAGiR=A)&!XelmQii?(X(ULA&(nU*k zglct^4_emKv7V0gbgZXiJss=mSWol;&SyP6>*-lftY@fJ)Q^_+^sJ|6Jw5B`IX}J7 z(~12IHM5SHbNFcfoo0i`%WM#NnGGTzvq9uz zHi&%8Mp2*HDC#pCS;xpaM%EGg9%?p<`pibwGqRqM^-Qd1Vm%YnV#QvVsW*wEw;r9LufL#dAp zIwRGqSS3G$4jY zRHrLdd7i~P-02yaX-Qcg6fZY0gldCR(i~Y?4p(MUQkoL`KhR@$CJ*qWi}L;x^Z$3| zTyFcz4gQ^3RE*`nhyNX1uB5*>Fj((A1%H-7yd%|-mW9V8&f`u>^5i(&gYG<1e=%k` zTEzr!7PpRcX+(YCpWmO3D ze~Xit7c4%hY4K5&@$VEPs;E?@YW$mXTuirIO%i^)!gtbgHK`7l!=2>GaBIRekui3A zM5hRS7khjZK5U0L+*!^H7v9jf(Q5GZvaDnzs8fUyQUbY} zOt&-L>2c!MOv)_DgCYB6dz4`=zj}x7==r~)r@S=hboF;8D;%sV9gER~87W1QaVpEDMdTqaz3dm!9oHUTCG-n|HQX}@DsvD$)#F=xWoeaV z^hKj}|KrE)&^~GADzys*IM<~KRS*pXBpoF zY_UA-U9rjC9sOyKZ!ft2E?3xLlNrlZcEj#)rFyU{&G-(eC&`_voG*jX8fv(`dle>Q z%lS|JLkqvjIh9V--JzvdnolIgN%;j<-VaU8dle6RN2#0gr%C?&o&UcaL4VT}6Yqup zj_&_-E-rJp4np1yE39LaJ<`Q_h zs)95rUEnRqS7Drocrp8Yg?KyTpU^*)eZD~cBj#HougMTPv&&VGQkb{=qLvymKK7GZ zYRWkrM!o^_KcfGUR5AA);=_!;L41wzw6D}sFvnf?gIe0ic*nPDsRrX-h?_9}=9*gC z#dzb_YRQlB-lJ-%nB$HpR7?GtZ|YOwH+=5CTDs2s(p_q4F5`oUmot9u-)iY~#$6Ck zV64HMYjYjGS67gpXZ|wkzcfOuF%oed&LN>f1!)uGMD*W~B76p6Z9R3qa8)gxN*4V3 z^J=Mx@tm`2X(!{2KdPnqjF%yPlkvIhYDvqQ+M8;rCF8B=vx$9PIH8u-FusO0d4YXw zHy+L%hk1JH@CN4Jl-HP4q?Xq58nA5Ec&N0Zi6+9WxNV;I@fkS&K<{i9r7Oh0rH8=&&0hO z&Uh>0@r)Z_ti6n&(r5fU&po7;bj(L$ zKX2vr*2J}JDL*ORU*I^Qdh?L2i4MP&bcw}bvpA; zW3N`_xfSs_S)VlxxEEirPX_kY5azdH4i?5)$j30==EeVtxR_(zx%05c8em^FVE@7w$zI*MA}Qz#87)R(J+?b8VyX+43*Wb2>iL)-Zn%`)42XoiR_( z>)n0y8O!{7d`4B|xQW;sD#r1+7oPLg3;Vx4^SOB5w=lkfIsB9BmW(y`ymt4-wP&#Y zSnSD{87n`#h_A8WK`PY~{G=vNdf-V9Jn4ZaJ@BLlp7g-qy9bORCV6JQQC9Y;m$k^f zsYVq2EYq>wNZ498%dYSpot5pUHe?g8u973Wf{ODM-fv%&d~EVv^4XY@B)3bD-3ov8 zz)HJq-f+_FIiae2G5BS|6B6p%XVnrq#kG9o?W+e6|KY73^1k);30H07E1&s35)_^H zJ07$D8W~6Yr$6|}y-dRYhZWztP3z;xXXmMd_Gt&=$*1|QcsZ#0A<7}YemA+(CymJ` z=Oan3{&_?4S*{-~uPtatIe$B&4ph$QMSS0dv)rwXF2eV-GvsxFlgOv)m*H^p$Lge$ zq_(*CoXjHJWY)^SAvMR4|I^xV`*UM52`_3D3@eW`A)mOYYj*=`pY^XBE|R_TUTd9t~q@ZsvOi`}E4A$*13^A@{`{@j44z~U$B+ zgW-DO9m3iho#eWkpC_I14YFWq%V3(@;Btn%w$%i}M|$3~x11eCK5~~}*tf0;;rlUt zp<`k-!c|W7mR-5E2)FAQ#K%Kz)?A@<3~#a^%LS7?9db&-F;+dX0BJU^OS_w5|H zP1R$R!<&xL5N&Ktb1xTXK;H%933pyH-@Sfl8u`~4Fa}yLo<{za8;*wFTXf`epifUZ z;G7>QbvQbA82n`0MSNbhWT+DUrCs5(efqnlcA~ef3H@PWi%R6*b72(t81IpP^1Tr7 zd-Yl3KW>!`4=VW)f3o&-?gmeZ^S(8yq5HzmqMv7CKl*;&{7Xe&w|{f~neTHb&x1c* zvUf_4A)O8(W8^luGbrbab1KNyei=Y~!*gGESan(Ss(N3%?0fYf#fs0=$@Oxqq%)v; zjC^V272>Bvjg(u|9cfo$UH@*keV!wMe4?ss53F90Lz=hu_JG6-^@+c`!$)>?7O@UL z+gQ$gw;S>4R-7j`56b(&1LeWgEX?tAI9d%}m}to2j9Ecd=$m3%^Pw3N?BcBHv` z-{>b-npK(jfM#}ieL`#EV`esk*bAahQkRU86XN>Odi@SK;ZXWM(jQqhNG@vDlKfvw zNQTg?O~kJm8x2JR3kiSK-_Je0x#-UkpJoJBn<)CK(v=*!#g;tMbT-HY-@yaiN*y+= zje=V@?h>B(c>}p~vUr9_aZ}wXbJ8i#tQu|M%?<4dU&wbr#)(rD>v+(Lz&-Uu{Tmgh zK}7Lv;)DAo${)J7lcwbFlo!8TKzLH59zvE|NN390Bssy)P5$c-kC9g=)y!A&d1i`D zzWu6}d}db~45{11^Q~r5C3oM=VqNbIACT`}B-RxglnFr_UM8Jc-JI@^JCCBdjwuQ5 znL2?hObwOSS?dws`Yn}v?>*6nSEYVX`=&VKO=f)2Vc<_fGq3su_X(Xi%bF9Tf@1MK@7i~fJ3QG&g>Rp?(FTN&!S-%nQZZ`o=v+>pd`&yc*y?UR&DaTn%k;e^pf zK0aILyN^eU^Z3>vf4OCFAo-jv90N1&F7BY{bna8+j;Rtw{o$hH}M-ks3tC9SRuSEFS9|wZQPffVow!*-BI&r4v{u=2X+9MMb{pOL~<-CIp z2%n!kFK~aL*vCm{vf$imGwJVrCK}8aZqnM@MkK(iFYX~+cqP^T!s?MU7k|egA3j@W zqoVmvjUV!7XNz;x(l<+WT~|iS z=K>p%f6Th2?o&^tlF!gvIr81Nzo6d8tv*WL{Y@h2G%3iC_Z}6m9Z4&O%l>0l65oE| zI_Oks5p-=lAAWpmD}3AH7BrbK4?gH{6hd3?fh{d^p_<=HFy*`tg96@#9kafGpowom z?+#m_{&RESTHUQsKJzm4bWMlK*~j6yVH+$nEroISF2jz@YjAVKPB?#iH-!BB4eY-5 z0jzGg3>pP|2s=8Sg_Ic|K&|1$@Rjd}kpKF7@N?~%@M`V?$l0<4J|8+47Nq9EnQb4# zgbvFgH+Ujk)gOjkCniJUr94<+I|7BPA3$R8$1r2|F~|)lhQLqGz~$yeP&9rMES$U; z+T=|J$ABN8{rs(PXXG5noN)9^sd9*bal;sl6& zum-~Gu7x{0_rjppPD7ES5DH(K1Q!RNfsq#vKtYQ&kQjRaUfs6 zpsVp~@L8}F-W$09-hAg>XdV9}oM^urCLenf=3V;)D&F1*{+i|Riq9_SI_hV*;rAAN z_WT5xRPGQ2i(v9CfWwKm zp~mZzVbZuO(6{gVU>~*$?o|8-%=n=Q`o-LbUGv7mi@V0b#>*eTl%YojW`r;ax zxattx+;JW@&0h>h=PUq6K*v0`zpwJY7Z1wSOr-cXRIb*phj;n7_1t9 z#TvD2b!_!)4Q!2UO>E6mNTiD+!;+w4^zS%0`o2??g*(&0jMO{p0E9+TVPwW@+tS9!%Waj#q zxjtsDkD2RX=6aZItY;H?7GAf7^R$WmHQDqcZ<}7ku^B{uHiO8|W)SPO8HA2a^rgvW zV1Ln{CY#uAv|^n$(Kk3|9V6=)MP4=|>lryd*V!ie$z(IJj*0Uzv5tx7n|QwHOZ4Y_ z%%bi#v#7hx%sOV)F|&@D^D(oY=xdWr^tH)mVI2$WSXjrxIu_R9ez5Vr+j!q?ye~Gf zAJ|4TAKQi2iFEr#Uw9g6f6&r7!P7|V#nY%&>ZDkTt25Ji#xb2|#WGKQiDT+Z z#WGKQiDT+Z#VYm3)6IE^zBlM>w7&+uR^*9RsiQ%!6S`PyM7=S`S*P zgI+w340<#5J6aJBx5KO%v2xwB&!IAf==H-I!oUqNB;Mlwpq4M^4NCy&T>O-uSO7L5bAM zBI=!lH!jQE62boH;+oR9YKbPU3Ih-Ztl&E}(K?%1q4!oCJdO_ZI z%P3e{B(jgxq)iik(J3(~k-c}U5;sZ=yuC_?`g2T)m6ewGo!j0Sl}NS3piJ#ky2bwf zOsO9t%CgcDB`xFN4{uEA31wltx6>OxW^U=tDP4@x%wIICL~f-u%fOUSD4pluj49D2 zkLj^e3od#5%CMRe5&nYWl*;JQ78;b@M0~|1MszKYztbFBQKkA`tx|Ol4-3uGMB~rx z_&@epnm9+YBQ@HUlBx;NbP5Xz3F;iA4-Sdwia()bI8xJ`Dam+4-&U)^f0s{-L1+BO zd`4;bHXuuroD%C$kakcuzDe+mvNSRA&cxVM2TmWT!y8G_u2knpWzbgZuYJtb#`}gh z_I5^oP_}=TCdrW&uY5v45k^P}WNC({I+L6(C%#Rg%%WWwa!9&M8D{ZE3Gg!q-k&&7 zJ_O)Qj&;T;KDe$VT#P2bNa;FKo%ps!1WArgLSEBT39AH+j=>peDXAjRko2KL9jTE{ z$7>`~;zJDnnwXTN;VH?EWLLzf;V5F#=?w~*o{aT3n3YgUS@Z^FEl#CeT0}1Lr4KIz z^}*WXM_&(q zInIS$Y0=~MMW@Co=gVNUnGFwjuhe90Iq#{zY2o`hr_zbKN3`@x^NGYbDX+k0qcwX_ z_S<;aUy?$VKV$IMSo+(A82la{OMm}h-`~N%r=_TP`#d`KzV|r%M{*Gvi|=Kgr>sx8 zfG-dBSP$=QJ@*vr!ODHl*xM?+_odu>+*si~m-l@SR_-a-V?9_IdpM7M^qz-T?4y^* z*JG9Y3g&S+Re6;fuX3siDj!uvRVBQ9RFzd#R8>{JcvVq7rK+ZST2&pdYN{HlnyOl= U+IZDe)log8s;jDp*E6dB0F3d#u>b%7 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_0_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_0_3.i3dm deleted file mode 100644 index ff7cd56f82fb1c50d9559975d0225a5dfbeb4e83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24480 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_0_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_0_4.i3dm deleted file mode 100644 index e278c72400ab90864edce10949cc12867a9a1d8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21752 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 JD&tjA^*{9HSi%4R diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_0_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_0_6.i3dm deleted file mode 100644 index b1d203e9d02e0c5a4aea118ae30e1f455052d092..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25120 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_0_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_0_7.i3dm deleted file mode 100644 index b89dde129007c59364abf83acb78fe29e4ce7d84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13504 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_0_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_0_8.i3dm deleted file mode 100644 index bbd4e36b1d9ab7302035153dda89d184a25629f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26104 zcmeI5cU)B0^1#Q6-5!T7eqjmq7)GkA=bpMfLNljYsB88 zvez0F3t~sHD+(%hMbY@1J7@M~QF$?W@BQ)neDw3d^F4Ft%*>fH=bXFf2mEaY1r}1N zRJlGX)kdY&VOtXc|0wb(bA6|-9xXdLx9Qs7wN0mX9xmpNwpKP4=AGTT>OJ&soy;Ah z%m+q>`MCM|hWdn=J6c6sn0In>@7U6{f49ybKD5=^*#G(5UEj{d<3kn6-a&&ZU7cIH zw)yaxwXL0%79X~0+0~;zI`MEJA7LTkKGC(rpF^+J`qzun+AHzry;bXze-Rw`POV?g zyi#SRKh69d@_ptLh0OFfm#s9cLB7gd{h3oFz6>eCv%?+wZ0MSbVT`3 zj`Jb<>diVc(AQa(H@T(OZ(yE(RIT@6op00B`q|92PpS25nWgh;eKvEs3u=7|^SH}u zy&Ky(c3Z6*(_Js=rEBTJm7-jC8Z)0po#IQyxv>e)pNh<*k?SyDd92nu zFgqZ3U|x(mHJKZuT+iGFV=(G$!*k4$f$b1GlnRNiqzWg^d^$WhD#kcTnHBZn|gM*Bv&6WSTh z@)Cp<}l<5CLGJ`i}Hocw@vi>A_uU1GM*>ZCI-MVkkh9Z<)(7AYp|i2M`lWSzoy6|U8=G|UNe zCA9fH^XU}4@0gXC9hh5U%*K6m1@({f8TB37-^Lt|XXZlYG}Q0MTn5ii<2#Zb?H6Am z>fs*RiQ!z;#dBN3T>UJ*k8#WjXEQfM&SWlz{E+z)j?HBbMZUtk4Ru~J*E@&rHf(>w zd9}U}b5mraFF?M^`sb04F)u-W!5o5i%z128l$T^ST)?}7`PoH$=P}wxp3iK3O|8Gp z`HaN7>>6_!WaHU;3V9LB^Y9M6!dw;a!AR!4*D(*A+md+yUgtZg1&%eoV_(8^z7@w@ z=$=|%mw6+~OEK@qd#V=mD!j{#wd9EUZ}`lyKwpnor`ZjBZ()}3{UMa+wG8KYin$QV zgPBX?oj#U%Eaqn$a~jTN6ti;g8+EFq{1(fbU~X?PUqsGe_C`C;n75+*9CISdvzb?; z{3`Q86ZtPD^8R@DC9uwSc%K^g@*tGYWVs9SHlA+{ye}3q7s0!GIqU4k&o2h%QpkIm zo8V`ebms2(IpR3;kNEjzC37O`4`yEQMy;RAybg6HF*iUv#`C8(+8NFAB`6uJ^Q9hg_#&Q%mpT5K zcy63!j>XzeVg317OZS=^JYiSAlO2<1TjQ3F(?u&UGb4{%I(adM@ zU7{)LtU-O_d18)n4q^MJa4s&)GS1PG*#$X=<4nhQm|}dcDtG!wmYco$^FF$1~v%jx!DGA(H1i59{g(ujypmBQtpHQj^-a zi)&q<$Bw{yFrH&gP3oj9?gc*{+YW223CA-I>&li{#{Ia5eVKlKJc{#5V|h

xayt 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 NB~@ir75u8C`agQ5-Wvb_ diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_0_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_0_9.i3dm deleted file mode 100644 index 9ed5858070797121e262fb162796cc0965c46e14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11984 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 HFKYe=5STHY diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_1_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_1_0.i3dm deleted file mode 100644 index 445db55aa13e0c6264576a702be8315047c34427..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8432 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 QbDg-(+@o9v~3CIA2c diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_1_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_1_1.i3dm deleted file mode 100644 index 4a5c554793993f15087e8af855cba2caf6e6c920..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6072 zcmeHLeRLGn6(3{_22A)+K3gCRMWG~dXJ$VWvd<(07z_zb!j}Q*l1w%WyPLSXA%-P@ zd`W={3VK9oF$fe0^e6?vLU#BN3Y>#fsV!=nV~jv0&|)Jf9E9F?=PfJ==OozjSDkZS z?(g0Ad-r$WeQ$>4x#U7mJC5V_^yIkBUm7OytHFN5?vCRi9)`;O8W#p*5 z<_ytfrj*?D)Xa?LW0Gv7V;QNrc~e0mZxkv7d}V57TwJfdyprwta5T-kz7ww;bL)6* zPhK%GT-u#iE-=pfJ$PlJSLft(dz$oww?4l*366$81QW_#+5<&_T@rnxO+{3fXRakkf6 z#oIL>3w0jDINbmbW_UZCu~Q6x_5`mSU~^3n)5^}tJUC0qOzUm%pJO@GcR+vUkuc)8 zAMuYh`Tsiu4Zl;g*LU{S=-$5-)f76A<3L=oGL*8jVGiQ@v{~BV*C*I1XaDcB$d1^a z_})x>akBQrx9t!wT-VEfWnd-Zf{R*+jPu~!K`E>49iqz+TlWpuicbtgPVzFJR(kR+ zjCr+aXlO>kZ1k@>^RO0MGYzkJ4iy`cj6=9dO(FF%=%xarx8 z_6Z;8Ydo@HAc?LUu2H_H+GgK=Ac&mRFLWi(t<6DyLTXga$=bPypXpYv?TucHT9uRR z+VsSs=r3}<7s`0ki*wJd=pAb8Q;B|0L$-Zn^(^$Se>a7k`$FgcI(R6Q`i8z2`Hgdl z?X?s8Xb$z!721*n55_NT^pS}#RUtn3-5|2=A88m<8C9&kk@PoQ_TX*_eC99pu5_@d^M^w}Sgn142sipDpI{SV8DByS)?o34?^X4jDI z@tcV6wasK*(*`mz^fAfnbAcRu=OU5L){qlv8_BNv<;3%Nh#al|oNSx)N3y_rg&Y{Z zfQ&I7CEpazC$+ABk$PtpSsk;0oH1-7Y5U(Hb7Iz#kCV5Oo2F}IdenF1k(e7K-h6?W zn&uPJ%MIja)jraAY8iRu*lJ=AT_%oQRiygRE>cuud0=rKSeeGPn)Up^vT+&5ZALu~Sj@|2wjv)a#s?er zqb~F@Pn+3>dNzxU`e1dQMaFeOU*}t7oezoAJZu&t>Vu{FvOz>WYH1wVAmKWwr95hN zo-FF)vPgAggV{jiQLFRJSSRRwia4# z@wCV%(^XI$@HqT|HfGxjzYRHzRNZRajFK6u4}?` zy=9_p(7L0n&G=K`vH>2aJ8#4T@bBw9xGSk`&vB!3v$KMN1HKz@2@({Xs#o+pbI7(->LsG|U8_)C1c(6q9mBGotErIurmJ8qfcg@O`KX^B_$2J$LOA&5zoWa=#Q zLj7ekm7-;lWV#j?EtdgM0J(U*l$ryzhr6o?5m-a3A`qF0NVuMG?P+Og8N+H=_gjB% zOOMY;ms=B-tk%1&?{3nDL=$W~w%gLwkr4jVT$<7x0o(<>%%#(OW7L4d?RFL9l$1G( zy|4v%_2%%a=_T$G-v~zm9^`yOqSb(pDB2{0O|soOact!*On3quu}V0*`X=Y7g}BGJ z3m&=4Eq2&sf#uTO$W*<~0GvvT1l#BEIqCZ)!*3k<_U^Tq3`Z_<>aAG0aB}HM6r078 z=;6~7V@X8_n~m1WDV0m$WlvMG=uc|)+{^!^=i%>fePV0)UgF;GT*C3GZ7uIx9J*H8 z2y0;+wqbvy4UbV9#&kT~kF?Yu+348a`qUq8k>g=Z$0!clFsA*mFKnBSh5ghvZ((lO z(s7Deza94g*B)9sj^`faqPPyw9^@Y4qPdP-Cuq^!!(0p(%XNkp!*$`ha^1K`pmpUQ O<+^h{xSr6ubN>MkqSfL6 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_1_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_1_2.i3dm deleted file mode 100644 index 6429c14a1d0917c19a8e15af0cf825384b651192..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25288 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 CL*fts diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_1_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_1_3.i3dm deleted file mode 100644 index c5c75ac7c5236db3f89719876e043fdf292b7c93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18872 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 Zm!g`Yx}pYN)f6=qwG_1#b?~aC_%C7N`@;YL diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_1_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_1_5.i3dm deleted file mode 100644 index 9aec62ee0183fda0cc7d4ec8d303b53b671dedb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21512 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_1_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_1_7.i3dm deleted file mode 100644 index 82a65507653f79a986dcb4da7503e7320a8f125d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20344 zcmeHv2Ut{BxAv%`F*ZbviYt%791}P*4N}W7pVw?c zc)Gc|cX<1qTIH;lUUqQx^csYNcy(rDL`M!Y#npW_nJScKDSDUJI`=SWSZ@h0J5#FqHsFO|l-6yI21X>3CJ zp0-NkIg0PaxTT&m8z_w@D85q`f80cAw4%Ot_J8V}Xs9$+rMMC~I>M`v6F~S*W2G^c za2|Th5_d+JN8FF_8r1Y6d>eI25}uFoN`$L5!#zTHHs)+gI34-Z2xl}> z8oLob^p(#%QU!r{nqAuNs6pRhgF&4KV9 zE&7&lgsakckIuU90qaKiD&n6A*LG1FH&XtCo$>4S18bjC( zj-Mz9bz7#McSGuA($5s8&H3PbNGH`%WNy z3*$c!E`{;6gwrveK{y5REW%R}uO=Lh{LO^NSSyXQ3ELnigz$XKA&Bq`otz>PF%0mgll0umHIwMJd5xIT%)nXu||BA;^D~ojBp=ZU&~!~7x&Rc zinqXgjuWngcoOBj1o_RVot1-Td za4FPGCVUP#mhWtaws@CO&U+DCG`k`vjrchjFK4MO#&1yE3h{4*U6At~%_|G&7uzAS#Hr8>l+ zf3_26J+ALq!i&-8mxz;wUTQ((2IF_vGlVm7?v}mZ81E^|*(KdIwJ3)Y*teK8-KG2~ z-U{#f>V)Ux{b>2_R|Dr2Oyf$p5sh^S&j-u6@feSwzH_mjmh=4|;HBpLT$UGjD} zoV(>`1TFgCvKJ)J7m&Ug_vR`ZHxzL=;kvSYdI9Tfscj_AYX)&9OG(mSu~iK5%)-_s@R7A=6&97pyu zWyxmC%$Yr+L@B;)W+Ft42x0uRO_{Av7bmiKo9Zvk?zILn?D8i`>b=060?{mf$fc6FAkT~8$E%XeKI>x`HZ-(>-i%v@#EQ*VQ~#lhNDLg1pBT< zEQjF>SVybyYLOA2JX*oG-6zi>iNj(*sX;h4YyP&7utaPgqchW3{u z7(Ss_L&FTcDAgz1zBJTZ(~re3Kg`Q$JuQL7du65=%BS)?cLl}5m8cjtR_?2sQ0U6* z?{zp)eDZ5Ew$^vEkDH$?9l~^G*bftvBl*0_&mSdj9c9Pnm32SQrR%f=#__e<>@xcc zUc+-Q8;kQj+Ol}X+CK0g`w`ZerG$MhwRdc7w*g9H(wh5FO5(WOZBJE5}?WjeByx-b*~zjOVuVkPW2%;LiG9 z7!?Qs%kxk6Yq)w0=)KlN^?=-hcGhDQ~KZS98yQ$#vVrP(aU}`h4Q5u+j_RevJ=Y_*r+%2lEcsbw5 z;>Y}-n{Vaw`F2*0cor`*z%k~|~ zaKqJ+Vt(sYY%X#8gQ3UNEH1$9%@I9EM{2H2{C+@l2hH;+7^b`Zqt-1bJ@#36CHCa5{ zubFwOAK#B#PgaI6YI`!yhH8tRTTh7~<)Kx}+B#&PI~ zcT_R=QnQyO!TT}4PmdOQz@}1s@7#$RA#ST3!1^}Txrz(!^D}wjiMi(OtD{B9tN*&$ z1C~6i$~5zyB!cG4>1;39SB(*$l^SP|IQ8fE7Tc98$M~J1{4<^|NMwA?H7^KssKjvG z{%|pAWD4VNNsAVv+~+df=0#iBscX#oem&j>67%^vd{N!E1dkn`6i^cL-l(!{VE+ zC5l<23)oziZ>S0tzWkc$?6Ptc?F$8l^Ewt7Om+Mib}Ybi)&B@P+Z&a26Zf6r_lsld zZyBc}_&wWdM;YkH=A;P`n9*7wwAPx0JyD>l~p znpWb*4*a{^;_*KiGK8Uw^HolwIIS+<<7GN|i1trwGXA)m@u15r$;Q1kI9?2Jw`R5S zsj1qRL3{93Q351S-pS5~!e5pcDo%`IeMj%92RrgRGu+cINX#F4hV}iWab5F-XWYZ> zZVwSxj^y{ddAt=YFXVoXIoBK(-QsghIpraCZ_PbfW^6oM(^O*lEa+4ct`F?b^bMsQ zAYz^iNHr|p-A+8Lwqx<~zN0g0B=Y)bg#dB$%S){9lV#T8_Q^dNzw^sPvHdGIwszAl z#)6w7f^o*$O*9v+=4YT?i3D+z(`$BCIP7i=S0}b*`a4(0flr+_j6d=2T62X`e7!pF z?<$^tS&MQ0H8oybEL1d0xqY4A8ZO-C-b;3}F}T%>XE~U)3GkrHLsr9Y%LIz+W}RpJ zi1yvZ@~+hx=lq^gFcuoJv3{I0N{mghW4M1GPqEeT%8aws5CB_$xy1UmDB%w|CvGv0 zOH!WM^orkKwcQW6^vmIQadGxXuRUmPHz0)JPJQ&^-WB|O)9>vD&x?8fKW?3oz6Q?-sSceQ z7+s!in090A3DtEa_}}l#gHZ}zGs6Gn(wH~aN^1?FUen z_ACteDjibdE<$VDAK-W2JQ%U!5NsWs1pS?l!u-mUVA9V^!7z6r*gGwPMlJ8cl=G`0 zzs?*e>plglIxK*tH7>#F{33YW;5+Dhb06%Tat(TI9}j`nH=xsnLim2gYN&hVB`iF- z7$&BsLPD?UaBss5xYcDM6j)z}toW5MaPdQkZMp|s{O*JK%pEYsABWMo6QRbzTqw3G zgwoG*VAG4$(51l?Sh{d53`r2-E6-xcOuY{tHjCi5oB2>$oe5h~w?OgCDFDNcf#<59 zp>fD1=;=HIs!ZJj-7ftOKcAfp8M_}tm}w?_lb!}!i?70gHN`N-=R8!b{TC;%U?Ch3&cKypw_(0v5~Nn%2D`kDLQKd}Xn*MiY+ZIADvB?m z2rhuP&3VY`zYaFF%7GS-3{I5J%)N=GvM?Wuc7|<4KVd|8jRRI0qz`r z3Kx`VP^ZscI2x4%X)P{5i;~x1Wbs6pq?-WyT=HQ5smri#=Oq|!H4mz-%ZCi(I+z}{ z3~rAc59K|cL09+laIMw}D4KK-yw>f9dR9NeI=dtYsB#lZ%{~cf!ciEI`U@mAUkN8C z{0f;T$HKZHE1|`l8SqQ;G)V532WP5W22-glyk};?vt}9apky+X`1Tx}D}4jvdQ5D|H?9m-ti1P*WQNA zuz3J&=R-NqmvAlSF7!#?04ocUVdbi+aMJ%AtZX<20yP53*(dvB=SLFuO+cp+j zciaeZ2MS^K+MD3CVHddCJ%U+&lVDr>9LQX^2yR{c0qk~Uf%o<2uzpf9R8t>^Uf)cE zZ>H^rrWKAr_0HE|>zbp`W%yF4;&LBGCG3H-elOvE@c~%<=o&N+z6}9$(;=hMJ*Yk5 zBuw|51G`JFg_?68L9){hC|z?V=(WXA+qe`KF1rZ&m{Z`9vK-7SM?=Gz=U`*KLg-pK z2c}Pa1p&&7FwK|>L!WGdINKn9+g9J$>QpMVtpherQsb=AP@~~bTHXk((Ng2cpPYE3 zSJ()j;OU`lG0Rwc1jw$LwXw0(^7w}G%xI@rT$v#&x$U^slS%`Yc2gLA1&p>N{@Vj^jPtwXVPPp zmY%7fK>Zvk54HsOo~?n@Qa?wUha=_TNO?F?9*(5vNP14B$9#o4PBbqk(sLp`C(?5w zJtxw0B0aYH*q<%G)Y80|0i|!>~gtV`h?` zxt^fndV-4U2`a89sJWh?=6Zsf>j~s5fqW&9uLL#ek*@^ul|a4{$X5dSN+4eeWyfl;-`AZ;Q3FIe%d?b*M1TFO=9|`0mfqW#8j|B3Oz_zhkC9r)gwNw}K zl|a4{$X5dSN+4eed1oD$WeiFz}0{KZG zKMCX~f&3(pp9J!gKz#U9r;>EzSdEGI?7K+`ROP>9p$H^{B)F`j`Gt{emcreXUR{`{iAc{ zzScQ&U+bK?uXWCR-|3wBKG8YzeW!EgzScQ&Kj@rkzRp}vPv@_mT{~)(Ud8qFDz2xe zb6HR4vR=jY^t8Y9w7>MUzx1@f^n72bHT>MfmgR*l%S-LV_k-Gr?+0wze6VHXW6Q?J zmiKeA^yB*k<7__IvU#YT`2N6_bolA& zVaw`*E#u>L&BoQLct31eKWte)t%{$M*s^i4Wjfe09c;M{7D&>^>zer)uW3um`)R13 zhWcr!pN7{J`Fvarzqj#vr)Ta9d}ix`EzeiO>!j7ra)6j)O#1=Kb4|;kl)~FDvun?#(SflZ#>95eDu20G(VdE&%O0Vj&D4C z)U4j;68UUpLNfTio-)dR3jTM>`A>3rUm5?2#$R*)F7rRULg0-o-guBNjvQ;9RrLcx zOi@v$Fgob|_ebRUzYoi5`R;_?TNzoe|HDvTlmEZd_&cw@@0^ll_{O@t|3T~Ywr6D; z|2~wLPmce6<$qt{qYE7P&Sfc|%KPwtqq1_Tce{T~L{^oLq00yS+pe;gGWeeEGRC_} z$X0=^q%AI|K5NP66O<% zaa#{*SZPonKkN||9?1vt9TpH^iu4XL#W0aKeo2&rtzUR(M0l7fEZS>C1d2G~-;_zg zVPRN*t&=pAR2Gd^noE#WE)}9^#$~@8%C#rf9)IUZ|XN4VgI9+Kq+)ZJD(dbH@2A9t#GEmxITEW>#`{(Mt z(-|!%@A`+9CpuShqWUc@jbuKa7$qee*h$dG4~U)CWfIb#%X)SSiYk4QN7A(EDJj+`tF8@gH{_0tZTR!sl z7A(D!u%)$N>Ddyqv~Rz&#HIG_kA-V#rS}r1=MsvNicB|jUk@TXpBdU#v>}8(Rgh2*EKSb_>6hk{3Y+T_3Qe6^{e{o zt8SIHYBO`aIF9>S#c@l~<~QKDF1`rxNA?$~#Q5ZtsQBoZP$tis?M@lS9;j|Z6hBxxz#TtwDb(ibDUW*qWeyrf5=x;{; za>Sv8*Vg96kBQUKmlruV2V*7jCm??%;dI2e3D2yH`U@5PVTkV%)*!x2__cbx*p&De z>Qr*tq2HVQe?z_x;a!MtQfv?8gpohCIWI;pP;!MMjv`#E1uv!%{s#Ts2+wQAizdRO z(QhQ|g;*r)kFhy~_o2TA;ry1Bxvn55iTo031`_Uq{0H-udVE#9xPkDY=Xr5G;qHh( zC43V#zbCvA{o4rpVeBV_ry*WTIKe~b9LBnJu3+L!W$7^nXBjwMb0k5R}jBIoFL=`5>7|F zk2ri=UK~MqKjH+!n_uL`?+6zozD0N$YNiqP$9&MELo@%D%0`85m4^{H7r3j5}Mcje*U-E%SpXC{eiDD;@6J9u6VsaFE#>OuGv z_9~WeUBB;C38|AmdmUgSo+<}k&L6tzh_#!=^uAbgpmTBNMC-W~`SP!lnlUZ!d9V20PSw~je;TC4- zvi&;S*Qu~Xsn9!^@uNzN($zRM^#$**BlJTCUun#pL|}DdswUrkODgQVE)?8Z<}KE{TMD9V3k_Fx}WWR^cSJf z*4$m;ubyD)xPB1hK$BIj4X%9VzoR~8+SEIn`J04va{Uye)cFheK#kO1%x~%OCVZan z$M^xwSDEgYDzQ>mZz~7LJq#X~%Sy`*YzdhZNmn4)fpgdkJcM70F@)S{1<3=`&ev4Lcuk zMFsR{IzyNAkOq$QXPi~(`&=)~Ri1^kT&oltq4cxyp8jC0+3$_z^%g-Knu1IxQ)&j=|o z?+Z4Qv&JkiefE=``TH0{V8k?Kmd2&LC9OHQoM~D^kGdYr&So6VP+RHI>B{pmeNnzN zZTTw3IeXp!L4F#>xe+`>@>)EM`D?5nCVjb~l}YZ^__8Uc7557m$M|(OX>9G5Ap3Vj z>Ls6Y9rIW850U2Q#4~?(9SgKybcV&+C&f#qE+NdnJiQ56^~!7;N5#9QmnwTq9&C~B z-9OFvx%eIkj5x{O1Mg&=bM5V;yo<(h6I}E54q~|;_8cZnP3^$=d)g0_%DeA&$@Pp_ z-UG^pDrYp{R;<+bgXWB1|6Vps9({yy%4&Oos!1BF=Y@8OQaC-!Az?kG^R0cE|KeG1 zQ_r)?-1;s~hxhz1vt0S-d8yu>-b^z(^flA*^Gf}TrtC32e|7-#^V~Vpwn0kGb=29? zq%+4@-qDl$Nr@+wcS3vZ`>xXOm3L0ow)aeq{sk=W#;`A383D@7kC?RpdY#!0JB5Ek z$fS+XX4e6H-WJ1{W9#9^%4f?|_o;-iH}nF?8>C92PIz3CDtNz%TF3 zg~))J5Pkjx9A0q@TAaNPu5Zu6@i`ws)`NGzY~KpI>wX9q+iry8Umk=br4wPS?`p`p zy9Ex_tbm@EmP6>;B`|GQISf2C1%}SP2{Elo;dtY<5HjmD96C4wej2iyeL zGWBQJ`SEsm`PgSr5%4bf-(CxaN#DTO6;oiqk!7$UVgVd|V?DS!eFmwsTu|UR4YOx_ z1O*S4fV$I6*l~0L)H{U!A$K70bP4!O8V}zDUV!h8>;-Au`|!z+{{?5jHP{yT4t#xX z2V}Nc1XDxr!j>~PAY{;fX#MkMP_4QO(+XC?!KDXaUUV7E_niP?6J~<#+7#$ku@n~M zm&1vme}Vs*IgrX%!1u1LaBSyQDB~``f#&<*9KRN7?^y@4U%UvX_l$z^SAT#H4@`qT z3tbT8brtU1+6<$MzlWP+@502enNV@@Ftpuy4gPuW4tO7#41fQ84zx?S4Qq-ILwU(V zkjiI3&qWn*{k6%^C1E2h?!6d_RMt#YWU5M|j!>&r9hlXS)soeb)sqb;YanY>tcKz> z6t5A;PdXaX(U6XYbTkpfBRvi238W{Go_7fyQN6c<7M2=YfLc?@br-=J1_2DPGZpmQ+LIT$pwPUm2t{u=}(uR&1s4Rjs` z>aT(NYoPubsJ{m4r-Axupne*3q@!bXYK+SM1+`L_pjPHdP%HgG%jy@@5oC=lPEZ@! z{se!2?7v^XN*Nv* z;;b3{ow-V>RdlP|cmz}tdi;$?ga@o5TII%r^atVNYpJqQCF|pFo~DR5E!h@Z%Rv@< z7M{oBZ>p5>J8zz*jK7xaIXHi6W^K5-Wmb;G;jq|#?*s9t=C3jnRgS63N|kf=DB#I` zc{1av~BMC!;l1_jr`>G|;oeKNgoZyk&MyN>^|ExBjSF9QSPvj_aPB z5MQJ+<0RoX#3B_wq!zo`nP*o;s-hF(;-X%S61v5ubj61jpIwJF&xQ*^C$$Pc2Rbs4 zj2|8eIq&eMf4g$D?1Bp*ne%foJwwspq2MxmD{C8qf4`Ml-ARwf z&aTSTvZJH>kLsyqn%T)tL1}Y-y8U&F)0~rI%}B~C$nI~$BVZq{%??X+UQV99t2u)W za;mzMLCt?whhCb3t#>tf@QBzGzkcUqxjEeF|9FE`Y(}qvL zhy4{XUjFYr{XEU@f=99clPiHw!cU|7|E|TYFWV>8{fHy)RlelkCCHZ>yRF;rY29nG zbz^zmeeY>yzvm^d{nowgcVC|CZY;0K*lpcdzIXfFw)&deFWc&uo9njnx{T@Gi}U7c z;N`{fTurVP=Yv;Gt~Te(`EmYu`EmhVAXkU0i&r35kE_o$;DYd~&o$(Nxkg+FUcub| E0HybyrtxWZmF4eD<7q%T}^vf7w``tgB@7t{3{_VAX zYwf-E8I-In9ZgAm|H;=LLnIn5TE92cK9HYq-NkZrIg#u62- z8JdzN*o2g1O^jEQF~#FZ$<8izcr-EE(r`_3O6ri+{nT_@9sluSaw#F`c zgFYHv(&A#1;v26Sj21nv#mA=E#$pcJV3xw;E^(BGZSKztOV-pTN_Dq z6vCS|yfB?`2Ktl|Ufr7)W)q%_H7j^qPhMC~d@aT*ds%XSJq|{H2l+Hd{1o9nh?fxF zjx~%R+!=YL&J9?D5*rZC3+d$Z%Y*fMKZ{u5xAx(MMDpp2{7}MkkWVH2JmR|vXX$xi z1>x_|U-4OqXBbU<65^hOA8x@5Cc@Vcx1&BbMW340avw({e}eEbhFfwmKSDH4O%nidQHx|XCN<3BmN2Cg)M|HSnF{e z-o2&7Uw?=f784G!@WKMZ4%A#kxDtJK6CPyZg^7eu$M8Zm;kAgBJ9!0hHu1^Pyx<}H zHR>om4e7@VlZhXRdBX_jA>Kx3vIYGY5MG9RSx9&_>W?JcvQyueZYhdS~?OTv5zFAOAHgZ|GE{u=Q-!mnfAe-ZA4d6j2*J>pihueb0S|Bif& zMqVhRyhrh#D0@7G&uJFL}5W9LTv? zznN_%55i^PMzjFrDz3Q^b!G9Ly)UF2vWZe<{oL(4K5~ z@6{8GpD_9->%J3Xng5x$n~8mgj%GgVg4;mA4{~h-Q-+JR0j(InYUpBXz&jr1-)rqC zv2u3+!}qQ3UnN$~U_MjJx{21s2@LP;cFlV3aalj%;@N(2r)8g~{~YUkKf}vn4?L3r z+ooP*^*gJ_iAOqIWjLbzr+)jsoWXo9$CX)ox0}c`U#&Us8`m$B@h^1Bh6yEKF#m&3 zHx-9fq%;28Rc(9$Pn0se>Qql_{L9l=Y?)mHKYkO>@QVKTTU*Va#(WO1X$MYo8pEBx zA0d9~ZO-uGxMFddcLh7EJm2e8|L7{$f6TJJ>b?*e=S5EPHFr&BnpY1Ggh$qQXSnX7 z1$;mDWjY5}&VZyxNB5WdwNE!n44)Oqd=7a*Oh5Yo^LeqZ9ei^wmGK9Yrimq!BG|rG zj_m-0(}ppALes}#;rTko>(W!j?@tFaep=;^zVsI37%uxZKs+{Y6vLy8cZ<(=83Iz? zdvHvBX+CRqpARW8T^kc1hk>v*QL|L^Jt(ykhP4jde0# zlUv>Ya_w}c`RZLOeB(wGGhBXcnD|jqdxnqa*dg8nk2Vn zxTv-iLUWrjo$@s^plpkcozKS50oH}-UglGoHUOHf>BaE7D-2MaV`jMdJ%6`G#yA;n zpE$#}`P?LimwlLPz4XWwhUfOl6X$$&fYp;#Qez!^K9BKhheeAot<{OretUYNe49S= zGMt^X)H*rV!|>noyyAhA53|@G9y{P`A>=WB(y?>C>Z$UxV5=S?W)JBClFo!T$3n|i zKQsSOU%0QzCg1;l7cRk-zb=H{e_0HRwl9K&<#S=e=QZ$b?{er-QwG&N&cF+Y=D>3o zR)f#67a|YU!r&QSLa%vCq1)67*yi~Z_UB)RfW6Ov`@Rb38T%wmT~-bs>bFAIDLY{Y ze+sxzT;DkpLV8s|(4psG_tRUT@a@^KaKLg{8ucvnu+;0|y{{Q@J#ErH1k_CR3paoF|BELh>*1+i(LK;ox!p+)d4aBg@H z-n7n#Yws=uP4recwR0BqPkRauPQMIuuN;Mi%eTU72QI>e3CAII%zSwN`_Ewey;acW zu??`M^+xa}R=|F~7FOv$1N~do0QXcwP^W5e7oCII^U7eer4m8|&%*3qpM=9Lufy^; zD&W@%M__1=58!o69W)vF16u8+MEf(5o{98Kq-Q2QGsT-J-c0dkiZ@fdnc~e9A4Ty|a(p!H!(wLj>5LZUr!z$} z9i7P{^JXp6*O|34Z`R6wW*yVhnRVo+WBbE7(?`qdLrd{Gir2CFkZ1LwB|Saq=}Avd zdh&hHnf0WnCp|ss$@c-*Wjzb|Tjaj#EOKAuk7E94nLqw{Nwx-@j|tagI3_xZJxq9te@2~kK#8-MDpSE*>)(1st*XZU`E8CIM}F~dorS6twUmnXSG{#*{=N;G&|soU zq0T%!RS1g+qfYtj`7`QZm8J@+J^tf(e@B&~3aZ7`*gxK%`ECDOAM>qO{BLuE9DaBG zHm9u)R^#6h=D%xyrop}NUsh*s;M!o);84^ds%H5g&mUvd@_$U>|1C;&Nb1e0apRK5 zl!i&4oP1ltUHEUunE@PkyeY>erlll%HFo?W#+M3SO^(CmaN9kFZcU6PE+r``c3`Z2 zP?9YHzmz6A+{Mm97cS@{wHkasP@IX*_$Go-F@CRmHLk)ehlI3aO7UA?nRzvtxz7A7 zw*$k|b+}Mq_qd&trAeeVTziudp0;k7` zzjs(=b`Pe^DDg;BFTFd_#OTzoSfsZePFI#QQ}V%m72swxF-BId!|lZ1Q*siQy#RU5 z5Gk#c)SihE#f5G;QASC2w!=N#>6pYs8oad$*JKtJ6cxH0E{|SfW67G7dzq#^9$Vx_Dpt>)3uS&TK3WDEcovvz2(M@qn(qC5`b5%6z{Iwv8>otGn#iVN(=tSL#NR!;qEUhbZwUv1Fkk)QCmw08U*A*-jpYxpkoadPgUb`V7 zrJPEo>Sk7{79bt?vPw0z4g&m<`MDZvXhdW{Xkbu8Oh`~@*T`-fKfPAxrRfnCVU4tg zg=+kAHE}r(TUcUZrp=-8)8=_;Lc_wl2ZY4*>hbud-dm?H-4C~R?H2jCh_^}ajUo|& z0U<$;@A>KtKDZYY5D^)JJ|er33r9wlEpN_HwKXPwf3O_)l1Q~RWq`o*5HE=n*wIUE z)x-+yhqx-^YdzG~5XM6z)Yk5dzY9}aYq?-WJ_TjAvi-NvP9>I+aSg^_#;C0qS*AE% zZLPxmC|fBGMtq57Y78vJ?GP*SSJO-Rjfa%-8>f}VuNqt`(<4`H^* z+abQpe2b&hPA$YrJmvFB`PC3Bc6z>Dip_|Xnp}TVZ9U622Vm|$FjkLJTaPn7i1IIS zY@-X*)}NR^7JIat@n`+i)^m*gu`fFqH%2>+S@)n_ZEax_ecXaI*E0WItj{Q(fxKX~ zbsy`V#Xc&rdG%CVw=-XbxH{{04=;_g9`XmU%+DCZodn_Q9mFLp-yGv~zHWSrwDe59gEP~a`So+u z)?XCANu|BGi}S4X+9OkKb#k1I5FcTj_LkZj#JcAa)z;ozhgQjIYio{S+;FvZe~hTr zD8xyO<8TfS^cVcvct#F0za(01jb;2k)@MJ<1f%^~?CV#oL%I?_VkhI8h$k`bk9`@+ z_*JaoV8&Mw|A%o1VjJV_So6M&&miBQ@lU)gz`fecR?J<*wkNbEnvJ6 z@h6PyBObu@Da5=6Gfo|#wq`S~h`IM=+z4&fV>}G~HfMYf`|>g46E4^vdpC>udc?CC zm&blZa4%k$`5ez1gVa_7V+Yxu~JeFsCExmMR9;B(Q0nD$7G529S6Z2B)_B!_H zZRWQbqPF&9Tnn+%uMostng1fj=3rbM`BuhRh?P2TN9@J?e)zf2jqy~(itdK2(jN6e zn@ZnXWtZkV7U%rmtXmWDGRCsZ62>KXj+8s(CSv98U6!G?`mu~2?}~Li|BG-=au^@O zv(|%U?5S#Nd#?FE5O-rd8ST{JbNwI8s~KYz@|8RG0`is5DI4-nv;7muS8x#GJIt5Q z=3T}cut&M%;?|At=+3 zaW=+aVw{5Zm3u~mwYBo>sgXa3?VQGbbz=D)_*tghKe<@XAeNbiHi7Yc%w5Oz>5RHb ztQ&whf^l8+WoP+&n2Tc52mLBI8-1&tPS?{}Rr>@)`XC_PrhRTVZUc zxK=aqbKy1S??IVh#;>8x+8jgQaJ4mxeI=lL3F}sfz%#=97(C<0nBN%hL*-t53GE!> zyd;e6Q^u9Czmcr_8J@L#=EvjAD`#Og&Rzh=GY;!^hVA!4oWilahS-z&AL49`WIOWj zSmLJu6r@r;$Iol>db8=M zKe`Ykjfv3^o}X1qI#nr>aKrRHmTjE}5mq%B3Y9NRaLV!{BL69+zSjt39NwzEl()i< z@E0euq@!crCz*_hT&ZYlC&I0+=SamF(H2?$>%zv8XJIhO&kT;1K3c6JTq8VNx^X3& zWV*gQ4B9OkL%O}M<%6Tf$_}#b!5`N;rFE$!zpGPSX3*Og@A-fr*z z-8cD!U%G#+_*mWul3DaxFG;hh24TPRW;pzNdy=WtZkhkSTKU9}*ky(xjkSa~O}px> z?jI-k|7Z#K&IORX%{NR^Mbsx8o0=~bXO1hDW>fJ(zSO=U1>a zJ&{N9r_RrHE@_`Z{4bAmmeh-0A-rb)a?2q|C%ma;6U)?Jhm(Byb@@;j=uh^y3?3wH zbnYhmadnG|zr88)s)sc{wOG`=f9)@dXL*QR()UI|6>|l$b9&)c|Lt2x5WZTjKkU2q zfNGu?HdNA#8&7zDKoq>zLQOIq?r5cocTJL+?Hwm?yz^Vjp?(_a?S zoP;%RFL@OCkxa{^bgA3DWn^dZ(u>aXf$_wTind6tTDJi?wy!E*^>5x$#m}+H%)PHeVTOh0SN05Wh$@90q_l$)|q3(X#OC zWU|xS`hs+INe_~_y`qVvS!gAG|HCgh%Xbs`-r6(_KHpYAHusLMDXAAml6?OgVNk9| zJ(BM{E+3|64x>K);@J$QZxc1?z5anS$g@AmADTP}y4~GHF?eq62M?}_USwB^koI4w zO?DQfd%+H)Kj9OJ*DcW-V@Y?1uAzUk17haiZZ=%X%G6Oja~5}YUVJ^5#wdQ zU5|BbotLe{NoGyVAXxg#ZnD4B8YR_uQBC~N*55nVOid!)kz4bnhN{+dA2!{Xy(l_RcA!9KUz0a_0RzO_Jr0W&6UQaeBi4>N-IB z@aS#wTj7;_IO(x~^1Y%S@4OK^l>Abt;?bgan4BT zr-Yg$?}2w)ix0Pu%^TUTN^NsHk?xrOX>jMA)fDsHP7di%r)ea!^plG4Rdhd+(O%D& zJ}$SE&ik;LK`?2phHO@gQ&}$E%_Et_^&6e%HYO8)R?J{%(sw)A{4D#n^P9K<#2;n! zkg|urL3nGI8U89?zQdOmSl-+=jO=U~8!lCy?@2iMa-MXuN_n!=)Q|v@$Ipb@H*6qP zoF7UuHx6b?XP2cDzeP?e^wO>)yx;!~>Gos~k~#3+V5#{1ZN$IQu{(5o@FMa19X{qC zKSG=@&j}wphn&qMzAEwy=aH7;ZZ9{_0n0Z}Cfy}-f?(b%k^At~E1lV2rhz;I1ES)k z-y$wrWL#-x2g~r!#CcrwX$z?56G%22wO{RwP7^b;;JtS(m8Xb#XyP9VGZr==8GTj- zY3$-C!pRrBEQiB~ll{!K9#TLnQMZ0Giv6?Bjv#*L1v@ND8Vn}9Wlm*i-B_HX?nk;q zMcs?Uziu4{qf$mwY=d+!z|YPek{stJoAae1KY7t?YvxY#5AP~^(Wd)d=Z^3gk{MZ{ zJk)tdoE>ZJL})(o1lgHAv6mFzx(3OmTQ~+o)jOhJHI9#vieLJQ;;i5i zD^0C?i)_wY^}xSf1yP5hTi-)k;C!gp^aF@|PyqFN{0mkEjfCytS0M2CDrmlYC0xw? z9XftE2UZxC!}7^{;K7TBKw7#3x_mwd)>b|YC3}is$gL?b{N8d{Q?v%IUpWf;%44AW z$qi7_?cb2tWH(H{{|U4{F&FAjx&uYYMR1|kVL%z14yB-~pGeo-S~-{z@s;J`%)u6qo=A6E=n7w^D(-z|WWMN1)X=xpd>J_Cbp z9fWiF2jTSo?Qra^(=hJy18`?TAylk&0bbrU3+hBog^yxZ!Id=!!N2__xSBl?Mi-BP z4XaMVBzqxDuzv>+A|Arw181PH%~43%y$lN8oDSKW_kp?HLMV2A4@I?aK}gOA5Or!3 z)b+dqrr@zKZSY~}wQ2**{cH^wR_p|ihb7Q($PCB{-UeSacnDM1UxSPGEwJmxdN^|8 zJj73*2y$^DJ~aIUiDU7C}^-ccJYEb6|J#-=JW6 z5%`_n2sIY{26OgYg7}LY!PaOBR6Mp3dVg>nF5cP$eKHQixJ%1m!QuH}t8){=<3EQX zTP8xzg9A|i%RBJtY$r@AnGJ0+SHP|r(_wt#Oemam7V0MKgP^Zx!ucAz!1MiU;2(Mv z`n*^Sh2MPyi=w`S-pQ*Vu<`@2HGU7WP9296dr!lW-*3W?0ec~0!hIOb$eyHE}K6 zR^5cIr>?@VJ@+9r`BxaZ3-w;V2P0C7p?Sn+7&!O}T$s2T3hK{?xueHG&uSk)_k(+3 ze#k&#c!AzFsf*dQl&4dyVC*)0firZ{vyJn8Z%<-6gg}&KW=$n0IeS=o3ljAjLbv`m5 zsacLsrgGde<$be|*K38GUdwBpTyKL`PkocA&@<@8zJcbzpf%7O$W+)fP@M8w*fUW6 z@>bgtwz z$4BQ%UUR;5uH-e_qjM#%xqdufCLb{uW?!nGPHUpIPRzMM=j%)II=n#S{vjoQNU0u3 z$sbbcH&XJ4RM;_TSzq*D$Nfjj_C)`2&GzVAp&!wAok{c!DcckM(s93#QazBeJ<%^* zvprEyok`SF$Mw^h=w3lTKCJJA~lg8y+PC+Daj)xdA&FDy+yro zP5JA+O;jJfQRJmJig@)#(RZYjpWZ0qK`Q+Ci2CY%M17G`eUS=zA5mYukEpNSNAyqc zV;1`2Uc^(6aYuV;;%q~0nGSoBJvB-GT8udBcuRwHR&1l6uZNYUz;ehSO>a{%W}xeTz>UN<5%Vl z9r>LbduoC`UY5bUQZN~fpEreT%dq3`^TJ7LYzp!<-DS73)7W^_$V|%+j^eTs6Kxs2 z?Y3cLq|8e{UYhu{l=QSzTdE^6CmoF#b$Wx$%u2=j8+>F}a#{2SITyQJE-fMl$3Fh!rCuo_S=qVqi_Ht?EGAiDv?vMT`b=^BC(C{?JdXwo%`8~FvH>TzkoUAyeoVWTgmD|aKrmYU?i zsr1!j`(iVa96$W{bSt~`ml$8724;S+&zP%e&ryiIx0dE@Y~ zcd~}c{{+Q9f9C%i6-bH}-WX58f5xt2_0Q-$&A)8ZRpyb8r^Nk8PnMG(;77p9m0DF%RY_GDpNgs~s;VjvRW*F7s;aAMsA{Te;Zs9ZTji;$qpFLKr|Jb&Jym^G J1AOYK{tL-0YMcN7 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_2_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_2_1.i3dm deleted file mode 100644 index 965a945536816693389ccdea614d329ecedd0548..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12032 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 ND{3fe;;Xvi{{TC53OfJ* diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_2_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_2_4.i3dm deleted file mode 100644 index edc522ef79d524167f222fe77d6b8a2de570003d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9536 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 LHC46nQ$zKC1^(tN diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_2_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_2_9.i3dm deleted file mode 100644 index e27de5c74bd92e53dca621fe6551e05e1a0aebe4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13376 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 Lku}AuvFyJ9@iAL^ diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_3_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_3_0.i3dm deleted file mode 100644 index a34e983675878864c015f6e8b00d574fe2540c63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10552 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

>(# zus3}Z_}sn@O9w85ON(!UX7L&D3LOa%^=HB4W(iQ!eLT!dz7J1K>F}WT2^crvEJPpL z21(&Fp|ox@JeYMCUUq&CnyX8oS8F$V;eK?hPDRlMK64CqV87r@>ZZ zH0-O935VjRz?Pg_pkc-mn7u3sUYvdeNzX39Zls!KMmhB!5H$(0~wP(lS$kF9+E8qkSF0lpz z?(KzEu@|9e-z%_b{CA)+uY^i9QlQJd1Q^hKIXp_+2iHa&g34$1f_w9A@MX)Tu)FGd zsN(+{4{v9{63-uD=EncP&xuRm@zk$xB zdMu=`O@mo;XTsiI>!EMURWNYWIau-h2v`j%8=_SHeN^t5OTMn->pGsN=j(dD?#g2~o^Nua`3Aj#q?^%5 z(x?{pMz!EGvY$q^@YARkej3$co>47$Om5=3$xU20xrys0H~V$LZ{qb$M&XaiNb6xb zXpEBVQIfr0Z6JU3YP+40*3qkl|0so>MlI%Rg#UVt@K3MN2tJL5?KFZ)~%;h#?QyFsTB{^>NrKb=PSr_(T> zs3Y1jpQt1L_bKKR^~AA=SEpq@E%RxaPs@B-kw2Z5`NY05=tLi)WFE1faLhb9=Fu^a z=vRZ$DC~_!v95{ZHL+hN_RGY6nb91 zBhG2u{dAtllHx^4{-C7%p%nAQIWF7T=aWA;rn+i)KcHkioeO9$_616|6Me4XK1a#x zivGkguPgcz$Go2COB^$g=u7;+nG`=t<`I1<|DR2{o+#OW(U&-8KGByrWRuEiK+iny53-bT4hS{Wc z_WQl@W|Q&XHTx$Scl6vju7ASxPiFrM@%9skkvEd+1#?cKbIP1;vY~l@$j*KC z4o!Bv$c9ap4$d-gtgIt;_9e@-EQ9}@?K|q@l-DfFWf}aV{AMFl&aq@!!TE9)Z{870 zcGh&Z$&SV;OU}+^@%``1>pVG2WZ4|N{}BDBt2_JStof&$?+?Q6ca}%?EauLuf278_ zw9bWevdL1&e{11?n!a~*x3lZn(KvwQANI`$znre$ zAn&F*@efIc=TRum=29qHc(rLArSiqkvhX|eqEz^ag*C)CEGR_fu4>$-Wy^+58fu%h z^lpl8v+;wBQ2(Gnyr8Y4R^hkbh5Df}e$gJIQ2h8TN);H?$0{SWPYiyzW*Jwtc>D@GVA;32*#D9o9sH3i}e#gtj@q#0EG)BEoOzkLDfHkzg{EdGyF+y@6N);U9 zAK)M6k00-mXTD*$Wbg1Wc^JiC4XAS0IDUhm{Dpu1fqne_WE+et0E1Dv>&abfh(CVR zCl(3x4M4rBmAtIHsIMQ+2n`Am3-u1~+t(V>(cc9%#Jv10e zT=CaJvNAjn^RF|=OUY@`>f~7b<#eeLg^?|N8DYcrn0x%HLx)8$h7T@+%)E$};<4vm zPOY3q`)ZW;KmYk_>5iU_bst(7f8+VC_};a&x{fQBonC(}Eh}8?|Gd4lY~~wARS;=mZ2JNHAugH4(dO;3~t C+6)~4 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_3_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_3_3.i3dm deleted file mode 100644 index c445ef38eb4d80a6b312a74842221a30213ff8a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21176 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_3_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_3_4.i3dm deleted file mode 100644 index d373f8df1cc9a8f207b3b11b7981ac1a0e6a1df0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21544 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_3_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_3_7.i3dm deleted file mode 100644 index e07be86608f487c89c8e5696c68a4f0c176414ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41936 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_3_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_3_8.i3dm deleted file mode 100644 index acabc35178f7da47255f78bfb88db314dbd66d86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38160 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 ZB6daXis7e-U2(e-b|vjf;irV%{{fBabPoUk diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_3_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_3_9.i3dm deleted file mode 100644 index bab12378165c5eb313bca496cef9e1ae3c2d728f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32048 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 MlyoSCUnLy=2fObExBvhE diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_4_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_4_0.i3dm deleted file mode 100644 index 0200027e50428dcde31eb74a555113baabf077d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11256 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_4_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_4_1.i3dm deleted file mode 100644 index 0ff470e4ef80c94ec055d11decb2116c47f0c6eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12696 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 vs*0+rs+y`gUR6~!R5evzs#Z|?(cp}j4 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_4_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_4_2.i3dm deleted file mode 100644 index 82c24de4eefad4e77d99f609284b47005374bc0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24520 zcmeHP2Ut|swjOoV*n8}4G>Uy1%na%*0ToBFV@a%tG^tWVMFbmaj3u!fd#n+SmBhjv zdvAzcG%EIrCHC^xS$oZO$e0^$ZtlD9d!K&aWdD1ef3Lpx*`S#qUH{Nr3WZ{SABAEq zj^&IBMc--&@Q*ByQ(4+|_I7LM(V}x7uNLh*y<4eVbxst)ZtTf8mp+o@dRRQ-lT z`?dG?kMfIFxvJyqtJ<~i)Yi?bPnQnAf2h}~jUS$OvUs-g{yoL$qH#e=XAd{87QcU{ z(VDb+eAvRRvv(gf;@ygDL`TN>#a9!5&K*@+Dhx_%E&Jd2u+nlTMqsb~N{fo+ngdG9 zW0s#kq_o^%+;^waqGnDs;;Ss*wMS{uvHZt z^-yU!#qvfEl$P0yyWUq?ibo4Szek*x@dU)R8JE1Mv~**f@3PXOVO-=A#>INZBBwXY zeSTG1R)e6Z7Go*&&DK}f zFjux(H@@isdL<+aVvqe{zL#$O`mJYyfk#o3={ zs3(=>a-ZC1EceMK#v{?Lp7|BgHx1)4m}gt;JrPf5ehG{#j`3N{xfkQ=xR-4?1eAAW zc>wZl=RiT!Q-JGrv!SZ9* zF+a@LpHo`;+16gbvyM4?ajlYZ@oU(h%(-+&X*tRG>Rrq~y zNu^~J<0#De_mM(A5%s(oDDVuFw`KXZG^M2_<3gvE7FWgtu@+Ssr(`NE6&WYrP+E#H zHljay81Kh?zGnUQc+L~02q17L$)CEsGh?II6Vtxw>peZAyzQ{0ev$u-i3A>GM<3F*PeAYMI6g`{!M&0vE3Kg zlTj>>LOh4D{4SWlIPkX8lFa@uLOr&)e6fyBtYVqolo_1(_c9c?+Wt<$(R zn&nfFGl_93<|l&jW6Vhd#(gml)fm4(c@f56+2yJ<#(0tAT88pF?AuDz`I_wp$#TXs zP+ovJKO**FoQWJ;4;;CL=LyGN8T0SMcCTXpn;9oz|JP(3iF(Gdo|33%IQw%A&&+Gg zsg3(Oo^_5u`3&y!nt0xh=CyL{e$2mf1n)?UZy|oh_!qP_o!73x9tdPS+YbBU-ao+n zKDZZjtlxlf&1WAH@eXj4{rnv74;MH;bFfCXUX4Lpo0&fl-xnJhe}kM9#$^y+XPkxl zBN%VPwP)CF0gS5-`{RqTd$O$&n5!L(*JCe5^4ey2C%4@pmtt?<<@cHs#`1viZjA9B z>kNLbv?MZKgPhi!lYn<C%AV)IfsW(e}Be35Zm(84e>0Fs|&7GaZYMu-pVuo0q%wFEboz_w74^t{n^T# zZFuHiU<_EVWajL^JD%;`3G=g)d#oPzq3!;h8_!0Y&$;oAVe8M@xbKd!&M)!)W;-{WFy|8MS7MCU z_?~tc_rg)u-yZwF7|Y*aer!L#_# zKN~UjnvCu5;s5H-YxX}c+y5NxhOygzzP69?J;r7GIcO`sg9dOculHjPxfTsD#vrW!H%-Gy{`6%v>2?uMl4q5VhY~Bx zlb&X;T1qWSRwvFZT_ea^?n)R|50H|k9wyG%*9*;_$|#asPWemw`(8B5G2Uw1BK6LO z1kzdkdaQYIKM|v9QWuz4zX-{L6N-U{!kchdr;b~b-zSisI=#!7^LxaTe5xWI%utDR z=JTy-R;(FDoCD@^*2T32N1MB@b$FzZ|72E4bMm$%&dNrfQqAMlNKgKh^XBcb0VH?7 z8w16fPa>Uj`V@e|dAk$m>a=gISz$4Rr!9?yEbR}(DZ4oVri?YG%5i0!9uA$4?Ib+< zQGyiPKY5!h_pLR;y6cx%T8qC6lRPpDY?0-Q9yc<7sToFcj1P3)EgH-6>jgf83mL+0 z_v1N*-q zdHxx((y>M($>(Lm+ey7TlqLDW&I6!S{iDR6+$9XE-`GO9$d$ox_H;Pm`lEidKI%76 zl6AiRs)sb-bsoZQTLR%&n^VM@=oSnMt{*VV99@M2<`SX7#OX1!9n5Q3mT;ka1t4Zs zH;_5Ct_Df-8XTv!d4~0Y>6>5D+BM&+B)82ih%?i*ue7`1bK>|Z3WHzguB0d8esS24 zuQPFG9CwyNje3&L80s&@ox4Q*v)gJ(uba7(y!NGdSdf{A^mi#2Y#m%j?B6Aq^26Kb zBG2n~wX&A}EROhR7RJKDizA3X|89cx$oDYSu|=Cskol$raaxrd1jPzXBu=lDRVAp@ z3go!foH=REI2lNsYYRKTPd!SLT&i6cBANr?-lMBa%jStb{3;<93TBKXpLcHzkm_tW zM>@6Di^8hA-lS*yv>j%T_aU^l^5J$;{~MnZK2R+IPG4EkRJQxN$583To-VX@$j$EX z_C^q%`XD#pLrs?|SB%{Y4KSsTu_jy%&6bpjMwxtR8pXC-%Od znSSC)(_0OO6P{C}Ueg16#d%WZy+JDRPD4HqDEq|Rq@54pr_S}PQGVhaICawu3ntek z`N&HBLGrytJ~U4Xf))GI2-j=Z!g^09`r*W?m*y)8eaMz!@0!&6#f7Z{U&Vsc!%@WV zb*Z`JvqbF8)K0l2&+a`*f8ck+V2H9daf%k-W`0p2jO3dxF0qF6iX@$`c<#=|^H+|& zV!se;R+%A$Uw7{%U7xEUev1;{rI!9uoPmuicbAe(1&9;*>Y({`Ua{At7WH6Eb2G`m z-P~Kc5F*ZeeXAkT{z5_I!?c})CFf%Ut+FlOjSbBWK-8{M^El|QY)+h^OCzMnrE3U} zJNt|E{#Nlm>bS$r?9^A}4Agss9un(j;zzO5Scr4(06^j<;BAj!080@)fq z@QU@;SwD)!Tq_K2pWaIPO@-@9k9z{?Sz7@0FZ7v}^+5M_EIqucx#o{^W}Nq$=gI2>&veX(F|}Tc7kq zx145PoWlM9Z!Lb^IKU-VQ+P8*y*Pj{hDLMD8Mttj%FQlSs;pZ5)B2v4=F2wih zkN{p6r&A6WwkrS`#kvzeu5_4`uHHueYf>Vhpk)o=BEJ@eK_TKCRw-7P$ES<_S$ojg z{Omy->74gC(41lsd-jLP&Twjop5&R8=2>ek6ZfB^E8^jLt%9U;>4lb{G*&0OnukGB zz9+{>erTaedbhC!$zR6~k#3*#Gs}4k_tjY^78GY!V%88Sme7mj2Uex07F{Ir-#_`C zd2E^9w6?*J?$B$+XN0rfZn9Qv6;60$=5WY8*GfH>Hgtu#o>jy$^=$%_OfB3*wzae5 zPpPGfi{2==e#^FJ4@3`p{h9#5{c=&>6y1kNZ{7ru4;`Gp0_&x`Ag^6pB@PUte`bdG(#hv8t%JR~L?Ow!}?{8Bp zoc~4Exu$4gDBD`xN!*7_wzjp#kPk;QL!{Fcc2XYtS$A8zcNg#O^frT~y&Z>;{H-+9 zyi-4zbb3uHWS%%z?2F24gPR6U75N`9Hb6QZb)NXUkF4BUi|@cImi2@iM{?2Hi%$7q zcmA))ZkKWalBHD!@$ar~Aq`xqBF@QzQBdlncrVZF5-4eNow3UPU+LEXu6AoeYp;1V zhWeu#5+zTJG&HI#7q?gsemM=jyKda3bn;+RgglpcfFo82p&HV1tjNb8lYtQYoKtY6yxnk)D)MZq|yv;>;QIU;w=I zKSX-o1~!qJcW6KyuV7zj^7&noXBuLpb9twlWj)_lt`4~$iSzT@Itx-ym_(1Q4jL|9 zT%SpH8zwiFk}EX?ng7jzUef&G3c}%aA2-$MMSgBAiA`0F6KgY`8q9}I34bmf(7}zS zF2uhXF+`fx-kq6mpVVtTiWkYjN~UKZ19Jpcbu)II%Y(1E-Dhj^D7yTIUOKZus^~H)qrdBK<#oRtt(= za3?*L()+-x#21veae)!A=E+Zl^EdN`!qtlrr#qe*t#(h_D(7eM+z_}Fy^A=D5+^|S zFVdid*E?7~?iAErE5S0=QAjB{7dEN)L)G$=;8@abcvpNic(l0!?oSrOytjuTcKtX| zbvcYb^PYjyJvKvMua!_cWfin{Pk~WY#)9RyS5T_NUPx=W79vL-1otktA?VrzSik={ z%-X#iR?eITFWlEbpF4A*?fS#8L3aX%E_?#9BQ`=gT?))Du?(gR7zwi#2jJer9Wb!^ zD42KU8bm}`A?8#Dymi_QHI~kRlR>{hsZl#1{>oN(?S2NfZ$1Ol7W@p2;uk_{uX*tB z?T^r@)h+l<@eI75-2X|rzFD3@Xc^7|2jBZYBc2fYCd>WKMAp(Q=#aFt8k_E5_tIAI`DY=7_94` zK}L;55Zm|{C|2MmoSOC!T-Cc^_pTK%ZFB}?rEP~5eV#$tolD_D;e9Z*+#1MeehxY| z+W}Fr=}_V8@t{8;!7;-_s5tj9gkQ^qf|ZZM@Y8o7C2j%er~Cjfo+m<`i;v+}=vqkn z>N2D(O@@`5_d@i$z0lzP9jN%^2ABrThcl%{f}4IL)}G(O_rsH5Z}9i9IAI@*j!S~pk^A7>)0vRxmsFU% z_z`?=J_tp&T!GQb_ps#c7I>|k0(mE&gByF3pzOk@FeGg=JRScXgp4}`Q76X3dF2Ke z;PeQZUR@5&TR(wwnd$Ir+$`w!!wuMD9Rri2$H0&8u0l%iMlg+A2c5RA1a1Bupm_Bw zoStl7bAH?kXWP#Qmbtb{GHpwvK=q+&0 z^AkMi_!QobISWo-JO}gmEV%dGE9jE~|c zjSsDys)uZ<8q@@tqec{HelPQZ>r@1g7c z*-)dxDcJ0E3BG+i9kl&!!h~7p;74s5gc@eR!trlm(4JK=vhOc&+#?5CJ!=03Lm>GW@8mwFfoolZK+MS7n&P21|G$8 zqsAoWTA{~i)Og$m^U*AI>QcIIVV>1QZ%~Wp2DQ*< zP>bgVwe7j^&mijLVi0x0QTS&Nb;7yu&miiAbJi#7gmcy>>g2-x=wjr281=%gk@I2_ z`V4B(KWL8pKsC0J@@3Epy#~F=6OQBujuaP;< zN858+k8@tHB|mUZei$`6svC}!7aXY$II=z+>(Q|u(SP`y^@#q%v@l=P4d=X0)D7os zU+goS3;SA;C!<#EGaQ9|t*9@~g%w{rA`Y!V#Gw`aY}AT=#!=)& zYY={E4XoE7{L+g0AdmGKS)Y;h8QCwRh*N81eMX^A$9dO@dKq=j!cV<8FN}JQM=$yn z=fb|8l(n#m;2NgQSUIy_cny*N@_I8vYM)EcTYj&!c$NavG0%68;Y_FEq1{L7;p zH;!~}%cIb%A-`}=amk~wE8Yt_wb&mzH9n#BD5257bAyY}Z=n3>)CSgLU_A!**Fbra zpUd@?@ z{W7v&M)phGAMgto=p2(rq1Q;~m`*M32l6QLAnph9T=-++dYNdy%W|>aMEhNy3%^XX z-*Ha+T^?B<^|d@_ebm?TT+~(EBjh>dT_er|+y(SLl}E}CmYMRaab~%*h)3f>`vG?c zo#Q$@ZplBbn)Zb}Cp$QjpLh(*`$VVJ(0QWMYU#a#qmKO3YUy0pX~q7LNAe3tv0m&K zl#6<6b)ud)3OhPcH?26w@KmL9Ngl;>@qWZP?MEC%d|GF{u+}Y)4GU$ z!qqIJfac9Nb5^4MZM{bUx^`MzKF}6neNHv?j6twDLah zRbSQ5Z;)S9bWlK0Sb%(w_6qV1j1KjQipHGg1mh`H%|FC1D#|Y`!pA2>u7rcc+b=RS zJR(FW&EdpR%ikt3{kg|~n=OZ2Jo7j=M;b|OJZIEB2Kc2{cJaH_GosN$~$0hdp{v&_v zbU7-pdzf9xma=d9;6KkL{L!9NLrZu^l^GiX1)sYm#gg%$6C)KC=axZA}hO zmYjHJA9qgpAK^H>`TnkVo1>m=S+Fb9zaK@mlscBdF>E{Qjt@Vg;6IinJ4dqhlA~9S z`TPhcCs%jJSaP!Jzsqs(`QPuE9A(eJZcbMHtG;C`{J*t0o2Hz&>d3L(obCO{33k-< z*Ch6~{+forZKLF%<+B|;*(QI{_Z-;Fk>(sU?w`18Uxs+RQ=hD z+#%X*3+y9tG;MoFJD&R4cawt++fH#*>6oRQaI!_5!%0r`I(m~WJB}*;dA=Og{a!<_ z_-X+m-Ys+EzXwaqhyTw~9)+TH=l1R5R6h9oPW(>fI2B%F{UUv$!y{F$Dv$PFUT)3Z zw4PqxE%82xx7(tOQCCmK!mi;0$}as1WKDp!sDH$-bOs-Up`LB28v;|j%KRIYk**Do>% ze;q80g!zP`T-8=KD;xFk#T8NEk-|{F7=M4i$gV+tv7{v1mrmDL`G$u^gopWsMSBm4 zKqb!j*UGXmCJghhbCFHSY0>KBSc2qqsS!mJm%eP;trO-RzslOq2*&WIMo=>w(Na9N z+{>wz(`d6sdH=_MKA7&>!bA6GhVkzxKNjD|Osg9@W7+BR!L%%BVEfPSP0KW&XsUup zb40($wtmq*At6D&ox)=R0>iKb^hMl0QGOobA>olNeSE2rd#D>4@o)9;5B?P%{;jsw z^pB3?$XShT0k&8?_O2*$C%^tw;|~e9ud+jhoedbSTn#V3uz+anN`n^5=Mxzq?=PL+ z5-|5oI%_kgVr|b;lzaNS36+aF)ySt^G{7*pnPa^%> zP%-&4oJjxfl(~O`|4f&x#h&x~x&5<`;eW*!;j#E{j@QZY$sgd44ckVW+y%|^9noeu5Wzr`~JVbUf1mT-RC*?={p|89bZj%zYGe6Vo4GFe*^ym>} z33jUK5?;;8-J?~Dy3IScZu$F%TD3;~^?56E<0f9e7g4#m>ReIAvwq#?4ecIkbw(XN zY*^RRt20{hYC=YW1N&RT%g~>&14?tNL8qF^{ueu@G;a+NI4k1i;Q~K8t~7gw3Y;fV zX?6?~I0^Z=SZ2*prFkdwiy?l?xF_l<&N4p8KhFF)$d6+jaad`t&pxzAyn2ApAC7nh z<2Q$t=HD3CK)yHQ?njj7HjLd-rY++yDASbjtCvc%^FX2VE#j(-yC8OC{Op6$oSAXt z3#GX*<73a2<|2%BX>fVOg_!>dj3++D zxs1m=R+`&0PC%?;ob#sAT$XY36BrlQ=riIt#>E~g%{@5Jc@g_D&Vx9NvE`o99KhJ| zfzsT7ahCh&8{?@c(}b}V`KgS(Pb$rqIG-7BDa|>!UPll+FdlVHX*O~m#$8vMOEV5b zotYSqM|_a=G{&5md9FL+CXDxBZv7bhA-@yjS4m3q6UGye-+^NfL)?+^Li96?Wm=t5 znxC=%`;dQwldy&RcYSFv8=s<*bUc|%^yADddv?(nMyo23~^D$%Md%*{71VrnZF8c-DB*C{1nFT(#UMY`r7u! z0crG~#JSg5J{W!QVEwTeV*$q5aJ?*L-2R5re4g{5JE=7La6daHD$NTSFFK<%k7Qi^ zH13Ctm*L!0#$|Cm-emj|eJIX4*J5wfVVv?>X`aTo>?_<$8E5&VG#_L98{%b*>*8KL zly$ztI!3*cI~!Tx{7JUBA{UK*LKsMBU^E$+9qS!OBXfs7}j%zVbD5ie#u z8uvQextV%NX@1AH4&u5y#JC;i@GRrZnA^*Y+u|BL$M`z-$tA|lsNdEDrIFu>^IRTr zN5);T-)#GpJN98K<{!ekt69%d#2svN5$9$8dzA0P{5qJQ_RMdN_*ced((D_r5VvLi z4D=y}d*;+ptR3S5sIxBP`k3d(jD0XaS-EFgWB4&VLoRuK8$_amV0bH_HzL1JcMg}IQR2H?A3XUgHV1H<8g?4GTw^Vb`Hki zT5HJs2dHxd`;!528OAjcD;RIYK55E$J@&Beo=^+(V>|c99afq*vu`E$D$OStZ{COd z3FF_8Z^J!Mem3)SA)d)N{1EQdjI$k3nul>+_M)x#j8`Mx%=j^4+geVGKjL1|n{z1R zW{jf|7h~*!I3we6xMs7n&RWRN&A2w=S1dCdWem($pwA1~Pe;V-8E;4bEsPf<4&>Uc zLEMk=u_I`U^B<4+72`O>`B`QW;yH}FA@*jSPKe7hzJqp`@Ev9g;`)qZ(AIdy1<`*6 z`=1YU70q?rh5S3rKZtlK$F&jbZsEBhPrjb@1+j-Ku%EYZKODmH6A@2m+ywR8a_EVD zp1`&`A>PV((FgEy^=4Alqf!0A*$~u7LTk%)RP_dG=!eS0N7JxGEz~V3{(Hm1bAQ z3m+)W$=sjb7-MCYzj8up-p4X$F+bT^XEyYI3FA%Z^8)sH#VMt^I^&6$|6+_C@eIAf z@_rY;zV}tWfbXhow>Rd&wpVXNoq5^rRXo2|asDSEUdFgKo)=9yZ%uIxYMAeYI2Yrr zcktfG_z3o@f_-?3wXp4V2QhZrJ9VkEO7jKQnH_yQ!#D)-MUG_*=A;$p{2}VU!ThDz zlkXVUlkIX2KcPR9*{%lrurX{0WSIK887A zyafBxrauMs9AUeOh-Wjdf^*{-=RvzK7@t9GJL^kgT(el`XRNg!<0@$D6yvwZKgxI< z>fg$GW+F~vycuy>mI*`swmu2QoY&;M#iBnASSIJmujgqCl;6($kLb^2*4Yc~e&$+8 z*u%DWu2soO^8wCheeAcxEOX}J*Zco;tnW(ZmqH(Gd#n~^Y(F!R--{b_?Utaek1XE@ z*R|~qRtoPtIoY>EXm>crQV)HOXZ|AGe>(Bp%^0KYXCr4(eiQe(8uhH@xL)8H(U|!w zuxDzp54j(G?Y%eH=TXeRg0@DmEd}DXjMMJV`7mBH`*t7Kk!`Qbg#2dAe}?gPX50|5 zC*u#e&$MJ)Iq}|N`&mjQT<^C0KYfPxbGDm;*!J#_4P*4@xLokw)sST>Aio~-pCfL- z*a5MLadVU}!MHc_Gcg{I{A8|gb*y_d<5ZLhW|{ifZ(3gCz4zdqoMVxHCYFV3krU-@ zIWK`axA5F@$S=r#) z{bsv=9>6m+Q-t_=RCnCNV>#yusd#r{ybtdfws+X2c-GtABX**^?cHi5%Ez#L=(VqV z$SvH5d$SMmxTjee%Rgrne};e!DHK2OkDUDIfge5aqX&NUz>gmI(E~qv;71Sq=z$+S z@S_KQ^uUiE_|XGDdf-P7{OExnJ@EJUK=nIr($IB3(a#!RmK$X~n`akvQRhFsSE zQ^E;P_I_h>j_X2r!w8FMa##q-mu)i;Dis)ElIMQN9SNT<#U;r2fy;eU$6nn@{?#Ol z!qGZBHH_vS`QR(TrKl+<31=LhYHhT!6UjWcZm>q(@h4n*WN(Oj*5l}Ub@PgALe)se8VQ%R}bV>97Wx4KB`m9NQ`#b|_wTU@QvT^@)5na$AH&e%Qjr_YNyfn%4$I<1u2$yhDET>NAb$Dh-K6OJ z56Pd|Rey0?vRL#<=Z$AgHLmp}TPvEifx5YJk^Gyty(D$5qvX%cVFA*jZgDhsmft|> zn)h(xH+)eM-W04y@(Fh%B>nXS>XSyNJ4urYr;_}kT))6z&+H`gsGXNI?L!{2b@62* z$=gBn#?eCEpkVhWq;u!22+6tPdFqEbUSTkLcQxYwI;pkPuu5*y^K*W^H2tNT^dx$O zOQ$jxBAMJP`$6Sr%gJum&R+20Wge0*7h6I~$|Ca9aF|(Ie5WMwkIlYodUjIu^Wi(I zt-VqLNavlN5!M>-2NHh$o67o2sW8IX_pM4ORXmb#*z@+%{#8oC>qkXOni3)8GaPm{ zwJ{6%j?tN=gFfv^W>@Aarkvq|e|6_zsl_69@-ye)w$Rd;gJhzw9I^%s?hSJPH1Dl3 zRnEb7JsVg}bwY{n+H~g5#$SXF+cNf*+C{~ao(N+zY26wJl5x(S4Sv4%3+c=}`X^~s zb`#;9p$6$}n2PY=Plpo%cSVqVg%f^Ie?kJ~uwtn)aK+k~_=P`vNl}?a4x84kB)v?m zOLH$DO-TrFiXgnQQKYo`-fXJ-?AR3RskUN2d9rYxY5Ale(z8FHwP|vf!6bj6aFDcM zaxB@JKl`Mq_z_>?J9fzeyYIFm`J3~4Le#-?6xXV{VW1jZo$!QB8NBMgiDJR zV!f-|M8f4ZQ>l(ATL(#*!UIXZ^{7jxI+73RZ!vtBHS;*JCp)|emO^(;BbiY5z7UXO zCt>f_Ell^0iFkKSX)A3hkb~wni)$z)E^{J0ZLA(l14R#8{KIjt$Y+)7o+Bj^TD+PN zFXM!X-4ag52tNyk1wnF|`J_Mj-cZx*1;U4&GaaB!(`FQJJ6&VAeXtDa&rp1aX=Qj{ z!fziqLy@bsNT$KNNbs?QlK$9YA<#tIhjg}oIm9$g5;}*58LjvChLU{qrGu>&-5|o+ z4Uy7|$i`Ib8oLHdC0d9c9yZBK@?IhOWNOmwgv`F8ujUmDwFcD~L_Qpz&>wmvOd?w+ zH&+5fue!wVu`>{ItzSg^{KZx!+%6$%dahG|w72p)tDFaQ84pMeEJ8kvm{$reK5s&E zuTQ9k>&}gE)x$j`)6{FEr_)d2(oa{4(%gETLZy2T>JooMY!D>DJmSxK*BgqAIY79y zOC>mSP4sry=Ev6Z^}3PH+GAQvqqSnsS-3DrQXA)yp3^tRnKIQEeWH&`p!soJW3qV+|R+1b`eAYrR`f5wUiV{_%Qkpt6HzKJH9Qj$C5p%|u zlzu&6CYg61JHp7!U&wADXapVKl_mMp6C)(wLl>zQh3+?pr~$=Een64Crn4`)6HZ$7 zDZwMFSf>qhw~~}?3eeo~*?px!&(Dy~3GM5e3R*<1kDY8O)hSkpWWHpMG94W$VsuK% z0j+YkC7p-LxkxSdi*tEjSO}PhS}5Kr(}Sc3!$p2x%ngxtxOXGIXUImAb+dXw9QP~wl8+!wNL+)ln_9J0ze#EuAXqs+jC4-bIZI22)Fz#mHf4m0rG5purrV9# zp=wF7-&T5m*z~JgZ<2Xby9eC4aD(RNE}@g0pJ_<`K(S(Ohfa%iy}ykY)cPXM#0ncc zK(nGS&5arK)&!2?KC`yUASu>Al=Percxc*tLG)*Lw{X~zQkeKL8)ukKFApaDm)b{4 zIhszUc*D+AhTQ$?kj_k9a>30)Z3q{Q>;QG7Oe7z>zmrt2o;a_Sing#NMVv=HdPTtW zp|{=S7YtuN>39iOBq`6fF6#2XM@4|R8xmt;Mk7P&}0nj4AVu5XA` zIjjfiH?{8$a{}&D?3KHQKsk3G!LM+CXUESXxABwBaOtEt>#sI8nPQ_vpU1oPfn28j zH1}bpFsbAb7n-}YOKWJ}FE`n}bviSQs@sZpIJH1}=WNU6f4 zDU|1Vm#dh{28WT%mL~0`%u;5;F5UY{#llvS{sASbzzVfk@9h^fk{S*ZYuV+Zvo!y4 zElG}d(i>N)MIkNWPRHi>|cTEb-ZkLXi-h<3Hu$2TRlpLlMih}yGX@Lr;wfz)r(87 z99t0o>hqkY^0S3Zma`6S<4+3xs{?kLeyu6a@mS4dQ`2y|K73cQlV_M2oH>Pw?5rD zm~c|2GuE0Q-o<96PBax?)nAfx_0YWDw6;nB;a_ewm4=paBz)EqE}w6(~b>!c+9_S8t}f^T{1foyAwK*Klgq(AyfH7I{p?DGR$w^(<%`4j(C;tK1^ zVf_dz52)c>Z9VB(?&xVMI%F{6ZEN$vdX*>PUP}f^=T`@jZ@y7utn+pZApV@&0kEXS z8uDkCTU#hLQ|z}T8(y9$_N$1H>AGosk zGU>_RuNmYPSr(RaT^8$czO{3bSl1&S5OcR8Q8!SViNmm1*bx=?Qlzw z^E^va%Q`Af)a!XvPe?6(p6qssyJ_l^O|0=&PeY}p$y(x{K2#1e?`l9k7lK|i*Nz_<46Pztklo1@CzwVii=J#U#=i|WUNmzA7;lSWNj#VO!GcM^3rdm z9*zyTXL=eV-Wh)z&>Mz6759XwiIGynbN(b#AVmeIvKUCu%`3sRSKysn&PmkTNGYLI zbCM}F{*^VTx;M$V_KdYIcMKxz-ffOsnfgLL^z~Y6nVA7J7jNv+qDCD_=JcURDI#NK z^8fm(2#D)^Qs6*bPr7`RPsa-W(&WusC|7G1mA3A=F3zTBGXh}Do3+HRa?D3+;&F-O zyKRk>oc7ltJ*(G*!J6LXiC=qUd5Ccmbs0Z*iSzn!=uYd<`iM;v_qoR zt8FieeY-<>)5~?@3|euinY86f3Ccr)!B?75M%-f~3=N=C@AAaAEPZXQnk4q^;oAAu z_(5Vn&$#_gLdG6q9Uac#52=1zDaI3TGDu}7{z`NAD?;G>en0ZFfaWJ?-r7X`C+=@e zhbnaexsDI6Yb2K{I^y5Wxx-`}(wFeTm#rk99Qg^Cz3&Ing~dB{@zOI)3nkIRb@PTx z?e>%)yU(xnFfHjR)_8*FWYd&EVsGER{GN4xwCJ(ay+z{Tljy^lC0a;3RK-bu!+C{a z#|{sW{YfgB50*_6d(K*)UDlA=e#FmOp}bTvr~%<2U7AY29&{vJZP*RB34=r)u0DGO zW$)~UcRP|{WRsyV=9hP{Y1Lb(P#^}TS3d(SI{e(pXsC zW;iTA^%=6dO@;eXDin+T3^}TYQj2;8;29Jcp@kuc4&_P&scN$E~^$J|aM#H5(3*dUs<)AdJhR6Gc!i}qE zpz7PTkmLOY@ZNn0c9&WJSL?(;aPzTn&OIIy%O1mb(33EI(tfyCZ7pc3jDgIicqpWr z0^tRR!Jv{uU{2H2z=E+c@a6IwxRVF{$ng*s6iI|E_lLr$`Lki!r#moC{T^}+ zTn`;@MZ==9&*4qVNeEKDg#)*eAo1-UxcBo3D7EJeT=U)yx$2C8Jko3knsF8`lz$BC zp6`GU`R2iy-gDsg&|46ka}9V^9SV<^d;oA>2T>2UL(vN_;cC)OXrGz{S=#S`W$os{ z5aSKlH+>I;Pxu1OYVLrCqxQk_o6Df+rV&t1wHsYS z2I{skaH?=T1gwjK;~hRgp0Zov(BlU%W&AiOyfOu5c|C>gE%!s015@CW26g+tgsmek zfO6Lun0DtJT)(vdHWi-+y6s26I6DT0*VqiBw@ifUD|W!jC@ajt=RWn9!|R~c@UqS+ z=({Q&f-CHVb3SoUZRm4Yv*aUmyATTv95=vrX(!|kTLYV&mVkA_MR>gaGi20`g01KF zL4jq-(C|nioL-g$8IvZ1rtW;WKVmoJ`8*0d{YHWL$QvkjU_BHF9S0+p#y~{zBd~CD z9Q0{=7|u<(36nFPgv^m|p>W7DIE}anKT_*xBCLm-VXuK_8Vc%qaENBe+ueNxB%zI%!K+? zw?V#N=fbgv&mc$YN4&R2LrCNO@Y~pBkQ8zb9<z} zjJyHca!-Zs>RWJn_a5l3*$gXIFM+LdkHggxb6|JIiO`|rILJG58q{sD0d{4$4!6s1 zf=Q)sz`Bx8VN|wzaBRaI*i_~L6uN!}_EkCu1L_?G-<~^R5JKH!!8=tMYehGQ1 zO@_EfV_{D4RyaK61L%`?z_u4RA#%-Th!|&ua+;TrZ3XTfIqyS1>nn%~I05;G%!Oig zQlQqy4e&J29XOgd8LHl12T3iLL*|#U(5>h_*wktl7=0c?qdrrh{Msjws2T$s9iriS z|5-3E!zg(5>OL3}x4`P>agdO_7!;F+L59|;(0}$^NM5lHE?2k=d8VS?SwleiTNLb! zS_;>??T6A`Kf#r_Wl$|_0URAY5;A={2>0hCz{>~6q3e_sct3w9c;y%dAH1WXdel*f z9J?MyG?)X5q+QUj)C2gC;{lYEcEiOl&!9=+*^u(^7_8d76dGXd{qmfHm(J6{C&yIi z^85v44~m8n<=?`3w;`}>op!a^q3Fxw;qRR$7^7odka!ZoPxWH zKS7?Tr;w@9I&j{55qwwu20zEXftj`5K&X>%cc+>ioYWevtJbL+Q5{pgpjtIk4O3U5 zdRHN*cO^MjwSo9ZNe@zb?y7Ndk>!!ne5687<09lVDt@kFd6iMfs|DYnBKxiem6&f( ziTMVVm~T+4NZz1kIW^0vSx(Jz8iSD22>k|E9=q~b%l5RaPsHtN(6T-)>(dH94O$Vm zLCboytVhRsbgW0me(6|`Ud%Ug9*mp^ql=9%^cr1+UL(h0N+5qa0TxRQOOtlyRV#4+1(<$0nXkQHa zihALg=Zku2Rkk=py|gZ(UP##=9qZGvKRVW@V}Eq4PtWn`IX*ql*YkW)f31t2=j(aC zp684DXkBde5bFrXEN@_W1IruPo{{Yt*`86@Q*k|2Tu+sY$hS)LmsTa#6;d%zCDxx- zCDxx-CDsE{p2vAsah_EiuZrVUal9%KuMQWkyzaDWqwrs&u~Ff_hWkO!_0V%YcwK1? zVjUqB@fbM&2F|~M^Kan%8`vKM`(w}xzYTiP9|rc%!1XY2Jq%nA1J}X8buh611_SFc zupY5)v<9w|f$PNUOs5g`LQ3^QO7ZG6qFzWTFGxu*Qqqf*>VTBuMN0jKl=6m@^%;de zS5aS`tEewhq0d#UTO130u43KdSm<*V{f=Xy&(($XiTzmTsuKRWiv1VI!arBhCpZ@V zxr%)o$HG5X73&ju)wzniB4zy|uhfj zv}{+)cC})C>9m|L9p_8O`O^c zxQ~onZ$4LaMvlwKdiZ?N@%e&O)YGUIaTvupqvLZ1|Hos(o>AuD7A)blx_cMho6rWx#@~6lDC7WC)y+)ijNQJyc+z<2`u^%E8^ED!Vy+*{3)W)Ye z;#k<%i2X>f5q*!8@`aS*MJndGigQozD*6Da@XwX~aAiMS*$-E7p6Ol1{)yKz>NBKr zeKam=+P{#}{vlIYAKu1j9mrJnL#DDHGL`+1sq8%p>so{7U!=_ zDSowD#DP@EsfB;o)DVtl zKR2*F1KTsOJ#lZ>VEc)<4YUv9nD!;4bT30n>s_Y8A8{VbW6C>HkuPyBBcJmm&SiNl z@?_vV88}bkT-K-MI7QjErX zwx3q&UuF4+p0w-vzbzYoUhzNfls|s*k45$>dHV11 zIjx6(gulm=bm{*8bdWZG-;=Zdu#C~}l(y&m>6Pj`%WNO!KcA>{rtJ;j50rj)14%dG zpSS{^NoD#D6CzonPN$INgLl9@wi& z+aBp&8`2H_d|ourU%Dr0z5nAF(>+N$C7rJC9N7E%FXMbidwrusKW3%* zfyDm<`LSQ;|7#Vr|2aXrCjYNX*1s_|--zAbx;@9fRQ59eDvrG^d(QXRvY%zgNxLb2 zBQtjL|DcS$w|1J-HRvA?{xJvs?lA2g{y)k|mudS0E#2dE1AE#3EGM0F-#JLz7U`zg zhw>MX)7?J*4z>1%>^Xmjy1(%9&vmD}E!m5v3(@}ZKejNxG0SeDr3)(EzSDH^BuSOa1-v$iHqEKATq);^V z^l%S#^2X~deurDAlaHmZCD1$AKhUYBQ+uX zc~IS@n#(sn&S@W{#c73+-!Z&esFR;1sHgl5bh2WE^sG8mU127_OFNA6kzeetg!g^|AAe#m!fA)A$r zdUwMaLH>clP}lxFdRPM6_*w>%l5}5^SIw!Lzh8iVUrXO$uaE##g1>&Ik(vGbV*cgt z*Yoq1)1ubMvG~gAazPYKvh>|}bz5QX@vHaJ7{M6+(gTkvOEz>U5wODrgd^IgIoNfR4y=hs@JD92<(j3q=u!SYqyH6kA zZmsIVE}PyB)k<2Z6wW1E02 z7LL6uirmW5oof6w;c2UES7H3EEK!Z-mcBm0*p+%Umd`uTM_ym}D>gdK*Xr3##+H){ zX`A{tUHBKzzH%q3ey2+`iQ|_;USW3 z7Va2-f&WUEtSPO`@5gDMeGC7UebSf4M|mv&`R;l0T=@h1v0)pv@zYYF+-_SsRs~%b3R*6d4tn@RLEI zRAg3UQDntWW<@qdc0~?FPW)t7u^0U2&7CD{))>vn|10N{bMXG&yy-J{?p+%}+JKM@3WcI#ghH_r*Qq}#6tyZM zz%Q8|?P6`z+}op3ox082dDd-I-@Ac}n@($Xb!pPLxz*d+xRHxnv`dGmh``33I)w*D zxVWieTwNM9Zsz6T*{(&CzrNIK45mNdH?!7n;Qdz?wMwrwAW!o;9-eibUg}LM9losV z(cHToTJdf`Mk2x@17ph2*M zF2<>^|I9Q)&h(lBfTKYUP4@i@iF8n#rTK= zE`&O(G5tRJnVD>G`?7{S35D#U11ATbFcFUj-xtYEh=^L3o0C5+_mCmhV|907uNWabeZIB+~fI}JIN8bvwpEn)w{*y}U5_^9lp3Jx<>a54IB{6S5vE5yW z^Ke{iF>iBOXUY3YYZB9+Jyu%ZF~14v$xL5@d!Z@oT)GEs@t#V=*mv;W^w_7gmSFm9 z1v|6bHTm359m+1HHY5i2=|R-7TvxNm<)`Z&h-wqwsTzK_^G zL&jiSoj5-U4!GZ5>>sAzMm=p9XGHyF7|(ozdysL1d$?ygmcuv`7BhVa^8CTrg!DdK zyUK_=Gk%71V?N`D@;qTY6!AvJTFl#e#=g&#*5iyL5npFK2YJS_58GcTtrMB9LY`@i z4`zXdrF$9gK>b%3N8QIg!q|*>HRA}x3a(=&l(qBpxump?VER^^ z8#5V?LA#TD3u$IP>Q|J$Cv}-1wBzdW?JT z3Gyspy#2V+8o>G&pe;Yf%@Mz6p8D8_H#sL=_uvfS7@K1smSVgI`_0VQQ|>*+n{Z!f z8Sh5^LX4YWuNGh|_i8@Ip$&ZsX?R{Uo{haUlKVCSaZ|=ccPOoatkV~B70dJo z+mzPIj1v%-W<5`E7U`MZ5$ou}_$Ka!8_ZJ?@qNa5uy2zXEAbqR<-QHSiFXyoFVWB4 z>_b^aW@~rG8}BKt7UnUc&P2u&vFF#=eZZbnF`k3HWItcdp>O?|KO^?)SjJVbKMyef zRIIOEPdw)B0nuiTJ`2fc<0Bf3^dEVmQ_htRr zF(;LnzZ&+>D(0_<`kS-#CSH&dFdbUe%wb#+1720H#766J;2&A&W1X3v8_Ka-W}}2cAPCQ zST+p(Jk9hmxW9%lzhk^jP`{4pj`1G8g8TgwaUH&c=M?LA#6HOH#r!wWhiZ%~p$}VF z|KItbMxIv8Gr~dEu@<{A&-U6C!9BQ=_4oYnXIzf?4?;ap*v}&9TR-NngL;a7BL7`6 zE)S-M;b)&%_UAa}R$^I49Dw=X#QZ(qmWcp*w;RnWHI8UzG^X7mZWv^i^ zvNL}%}J@z|BRXktnf5M+< zI2-LgSH;{`VxHINTV>Yo7~?(MFN0YBnWy-9it%OSf5UyX1#@+cab27h_POC`YYFDf z-g}OA9c?|uST?dh<8eMzX50dOw%^ZlBF{_qvnbN@GEW7Jr7Yu14)k*9=M3hTt}3nF znWsPMnZ~g=;%yj-;PWpU=rW&MNE2OrjX9c!_Z>3cDsF^r#}KlU@OC+_zk<_|{Mp`XB> zDC^I%Bhd#h_F)h5mtY)@{ZNW=HH>8~^Ot{*XAtXj^s@`jSo@ty{yegic^v)x3H`U9 zH9z5=_2F8~#(3?YXC3`dz__|Hj{^6#8~fvE*D*h%(dQOycOCY?9roD+NeaXzzK){5(fJXP9S`-2W^)2XA(Z}?kIzV*Pj9{AP+ z-+JI%4}9x^Z$0p>2fp>dw;uS`1K)b!TMvBefp0zVtp~pKz_%XwpXq@HJ9IE9&^7v zw;$o0<6A@F`k4uL%3o9ZVOdqeziC=nPIv4>_j!(5 zrY6fg{hH;Is%&XS^lmTeNar_}BkWqY5CnTP2AL<-91A}cnd2_wzEdAqV0#Cmr>q=h zQ~%hLaMvNdrLGGiEi#YMP#PZgYC!aD^|vSc92LI3zfj!PUMcdnD5N=L{UtB)v~fRV zi7nh!l4aXmUuV;l=tlJC4ZWdto;-vH{$5lnw#}1xnpI4)OllBH*!9#JTgjDzzxVMj zQin6gDJQEpE=j&rJeFiXtQnGAGE86>Z9D0D@*CpGc{3Ky4Zc}ZwtG6JhBUHKHR7+m zIsyWEPNrC5?#06FYzH>WJW2H?SX9*_iN_GxM|yd;Iq69#{>HsmK@p>3mzxy(y~w$) zOHL^+j}P(pon8ZM)rJ0+#a?Z`cSZQ)GqW;;p0^Os05=aQ>3#*m$8(yc2{%-dY!n^rfrXNEMSZ5P#;v zGiY-&3MGcqO|Ly<2~sRBV`@@XbTTrTV3XPS<1Yq|Rt%ms_awU~ zi}jQ4C%>ZF`C*K;+uxuVRrycYJa=^=*~;c_Qd7@$r1RqLHkP4pqDVH*KSVmQQX-x6 z!wu3e6SPG4+|tLkO4plYFK_Gx7fbdh*~{miZT{n`n0s$3pRp`I-kEr&RSAcAxe|zH zZ%8yO`>hsX!`OHmT#h83Wx7?CSyj7}{&9yhB7YmAR~cLc-c0u*x~V`R>2iSB3;SLa z1=|o2yVvySV3ZL`GmVnMvemYh|UdiZ$IBu*M0IDQTISoZp*PjF+?B!*cYZ`$V7Vj!&=Lr znPOHKuQ%K>EJ2(dtE+}fdlU&4Sg*@}2JRexz|t7=d56XjZ3f@<_3oG-DHVTT0q|0 zIf-8SRZYpf)|F%@o+&47JtuNlC$a*Rp65aIBGHd5u64y7%+SN^>ISQO;AnOG~5MiTh8)`sT3aZXU8Vs`?V!${syP_GX(fcz1aL`MECPJIh>~ z*e@A=yJBg+Kalt@TYJIIkU^BU=s8_s&C&gY@8h1`HY*49Pm{1-(sau}qE9dxq2*q2 zww@hbPuf1^2htPW=&7ykATiT&CufG|*49KXb;)M?V`K>7y;}-MKeiO}%A<2__;GA` zqSsq9&z3PM4CESBPE^6{I%c9*UgQm>hvX(aX8NX_n&=DIj%7uL)ubqEyaNRtpHcoy;{ z{#KWoNSiYiApHf$bcCZL?~zZpUIt&&yR|(uh0rNRQX)LeT4EBl2@&a2F}h ztdnH-Ot}x1w5NW=e`t0cxcI}5gnQSyYH9JC=qs1d+_nb;Vn{Y|Y%^Qz&_2X-{A5-* z;@gt&dXFDq_py4!Q)g^A-tA|Re!mMDEEgUM*-SZ7l51TOv1A|I8={lM{lD?J!jPeH zBhpi0@ayEQg#}Mtt2eaDpNHt9RBBM{FcBVJ=Vw@dzXai8+q%QW49m&ZxY|8ni+L%< zn{ue7bf8pL;_0A@mEhVu%3IiwesE~uIqJ7PnfBPu_V|@}23&cU9Fbk*zrvPSX^Xd( zczRV?YAaK>ha~rNrAd9IVq40S{-frhmekwgd|bJsAAD%JoAm$jJ{EHLWutu^8X62n z@9pHq;bCS`qqbF}q2uwoL_htkjGO)M zwrri(2TVhnkj}@AV;~_dKk;O}77N8^rP5w-jf;XD^~4!be@_nEpy^^(Y*?OCDt4s> z`EdJ9PpR*^Atbx4YOEA+XFmDUvqVRTsCS?A$4u~%3eV0#u?Olj5V_Y#xWe$vU<+zZ zdfHEnOJ37Y^xF>^GfL-%ioFwGCIq@YNg@3|PV|vnX9+$1md1eBnQTNKQKAp5@M}%B z3NG`ME^RAHI%B20lDT|yvK8LBwRCh;W};7T8wOcUi5Xaaw7c|MURS}B+yd@h&q+K# z&G(hA56DFLx@SWOm{^>6yrk{PHx7%w+WFpBvfL9hdG+8D&~U=f#M1`v3+5>G$<@l~h)Difci9AE`)I;akrOYiy5}cO!bUbtbq9V%Ak{W3#;b zP2?vbWU!@o1#!-rbEerk^Jkg5c@J44f9OiG8G<^)@@Hpl@;+MMue?;dr?`8sjWkK6 z2jbqwuaYkPpyibp)Q9a$yGu2O3qQ?m7uzBxi#^z`QXU9!^CteN#G>vOj)=HwUM!b< zcdx*{t2L0ViRe}12pu$w)l0JeHoqiV*5&Czvct38ty$-Wn3-{@#iR+l#7utZR|jT> z{z&}NbnJ)e9jPDQCO)-2?&?pnWd}8em$wTN&*bqj&~jN$imPnbjJ99y#t?n^qgZ%) zU>(hY?zR0Tb)8h=8J4HFq|OmTILs@bo85S+YHWZ(Ud?#9Mi{AGko=$@^g11{6!VC9j0RM$zKaT5gIXNm) z%r~DCzM!iv?%4Cz2gCj+Tgh&(XWgViL8}S3QoGu=w-%of3g_u&%kiK$=?v^P&GNE_ z=zsH%ZQ$J^@fmzZCU2>GpFEPB=dnYQEZGd=EY7BBEA48KMmiI_m4S{i^+`|a?GE6c z^MOU?U%sgy6gj+)@_f|IMM}w9k9eXBMOYrp7Ixd_%`A0UBkm#B(!!-?Pp6ZtZ}wmq zsohEX_di%}X*9Jv`EWI}pA=U`?3c0gEEcaJ;w)ZsKO5c^Ip=q}N;&%NBiU?=kJ%0u z4I;bUV}fCN4l%cP+J3SrY6#hlA1YcJ#q=fpM>1ZwEieQM99dLaddrh=!o_E{8%Nud zp4ol-NgrBYq_~#!&6B+Cl<2oEewid`qc8EFdW~mOjrKIRGc@&vBj1S}ChsUKE!g@K z@sydl!Zx|Lh-=-+($e>@L=MkwD=($GiG5^f=`Fdu&rNo_qmO>?D^gsS8ZL(6TkpgB z`bVL*WfyE}I0*F8N|?226Koln2$!qOg~i|Phir>}ho5UE!Mo}=;Are#m@;$=_{={B z7dqX6OO@>cYrr6g`e1_! zd6&TII~(Egl^f81@;i9kX#o`9c?VwXJ`9y|y@pK_#=xMTKET=w4`JKl@ldSP6)4o> z7F1V1fFjm$@ObEEP(Ho}kd^?&qY}Xtp2N16W8u-+jnHK03V3Bsg^PDzKw^fw;E{G1 zsux%blfGXG>rTvr9ELckb|@Y;{&5?&z8VH;^R7Um+hI5ry$ycdv>c*6&w};WTTt=D zc&L`=DZFYr2!I!76=3--5q!qnf@6zQU|-sM0L3Vn{q`_~uGtGK zir>W>;t@#ex&h9>EhxYB5L9wI3peAJ!OK71K zdrgOf-czB}snbw*%1ZEj_yE>qSP!+be}JajjzVC~G-&ncH#qvsX=pL>80_^~0g*#) z!Q@<5p=!l=*x>yHR&<^Q2@98iDeM&dx^Ni0^nVWnuAPHd@2A;zR;JyB>%-Q;)VSl2&tntJYK^_vVK{WF{RSdDS3r%*n1hp>;B4YJ zxNvkgJWYKLaa%S*Q27OLGIj*Kj#>)uTg-q{^ACb?-Ev4Qa2R$k*$2i!Zy@876zCrE z5{3*J3Fd%nIJ?Hd@N83{i)k--W!nWlSx!TT%}3zs;RINb;}#?;PQWikPr#NVA@rIO4-NXi zh0~?iL9sb=;ZVRznDqEEn5L(KHs33_mU#xW>bw+|7TN*sSEfKj)HE<@=fa3J{UKn( z2du>i@SeF7QjhG0vZW?MgVKw@)$I}N-+dicjlT;I*1v@<$HqdbE2H66tDzA8a4_bZ)$WfWOP>8O=Grw_Y`6pmwa*~S>&Z~=b}G#KJqgb3 zSpsqIu%G8Ih47z~;g$LrY-v0UTGks2Et;f2l`*(yIz5L+V^4y|=+Q8>^aQA~F9|Ak zmT%X6m z#wr(JaOzVqtr-IoyBvVN@6Le7;-io=d@6J~eHiu}N`f`1Z(wfMIk+b#L8dBqVf*tV zaOK1X@E=XSxavAaM$F%jAw z*$k@+ErAXB$H4XbBVhZo{t%&F0h9h%4H*xlKqk5*yl17uIE1lJByxx4>d2s zarIe%&A)?b%Q!fY@&U$|OobrjT*$I?Cme0C6Eu&XLZKtW;AqbWP`>^Lh<$MuTxRTq zz|5QBm{$s9dUqSzT;2)Wj`fEc$u}VHyTkC|<_73w*$ThSI|aLYuY#0Cqd+-z4m@l6 z5RC6HL(%Aq;4|SB%#5*t*Xq;I3+Hk}%Nkgmcn}We-U2fd?n6NQ1-P(n4#bX3fI<1T zLa5gg*nK?}^yL%a;w+q5-gn^D$WyTOObLU<`se3634;=Nwa@AY~?H|Tg3&qkAYHVe8=W29$X={>FnnIBi9SWTo)r`L)1dYyQ$*NOM+ z2fimibb8?jt}Lh5kRFpk$eBbOI+IbnHyQc8Q7ib^9~0X*87WTW7y3;mic@D|zf2~U zH?h2k58n%aO(tR2%>J0!AG2QYoAn}ov&fgutQT>a^}?=MFYKBP!mimM{5BheU9*At z4b0DRm^ltJ=he)4HFI2Mj?2t(nK=$K$6>bnZDRc<)^8SZnav!R*-j@v^(r;J$CcjW zO7gf8pI$BEz?I^_mEypa@_?(5*J#CijaIzZXoWtFsFz+N;>1cq1#eHC=0PRJQWAL@;w|8W&@8%5vgjiS!DvK~=qd}eu3 zXM7g%7)8DDS>(qg>a90%o=lt{lR@}lGK;uOW})9?w$p`vlc_( zW)=05-mDV(%$x@^=fSKJ`pha(53@@6YgP$;W|i>QtYUp?)~9BDYVyyZ74^iGGDeZ5)O*Nb`~o#jNm z@L9xT5P3G3bVAOo7IJ2d@XM?f`7`VIS@fAvCHf0j@&i}WXH({Dvw2$RgJnOY`UbGm7 zoIkA^8r?~T+yS{!@Au8HQY{4DI6 zs9*4z^_ets9kg1G$E>4$sl{W7^qckkj2lblGaH3pW~0cf*+_kdbP=x^|A#Z;6X&Yd zEY4MV75YrHf3;?D?&wtFT)>s|<4W=A&>cA*d6nyeE9Dzknxi_EhW3rTiuYQ|n@**p z{innB6?6m56?~@sr^9l{bCG?F(`zI1An@E2FP zA9dV^@+#!a)UWtV^GK&r(Rn4WavgLUHT8wO@-y|nPNSjz!AZhgQsaomMaMsKtYp-i!H%bWumG*l#+m zf#w&ktXK4tPHUq5jjPBrrkCog73Y;srxO0k1(x;8fzgUPR;)B%ao3AF=yXC}r>Aoc z$-+OKk@>{F(dkTdzeF|>x1ReC*(naahW4!v!=ilYwKPxgndP)JH}G0Y`$E3Hl3#dT z74oP+_D^pR@#zh;pYU1qCEf=p?*_H#YXcTq<~L|)USYN99KcodEuLSb-=GuqGU!FV za20X}(O0-o9S!0hgS(#Qx6UB$v$)dvqcfPrzQmQ}aodyMxKdvjIS+XKA$_QZ`V6l% z$7KEiEIewa8vczg;zo!GZnUh$s&#_N`_Bjzez_nD9T0k2JD53fJG(tSW@ z=Dsj%M7_;gF(1rYu|LiDzfQ_=;xR(@aNCj}c+IDL;`N^LU^cKmlb9c7agW0(A?5?7 zMb?AuNaw43t(NPe$MneZxRO3xNe^~6o*#a$E*%1U28Krjbq)&cEPsysC9rc~XqldY zVVzLuqX(@)Ri;yLV0d_7Xm@}AV0`UN@o8o)Q?*Qpe|Uua+WgM~|D@t;ZGPl{cVJjZ zx9-8hn~&anbzrr<%7|m}YaRN?(nk-@q5oGD&!CQ7BJ4%@0*C)AyZ@D*|JVKhukzrm z=l_SyIL7FRowMOg`Dn*~;e)f5|DBZjfx&@ge(m;aU>Hu!fAjG7QQ~jN{bgV~Q~q73 zfA8mipzq(Q_`hGuv6uhr1pa5y|EnYWUt0f!a5nJ^cMP z{5Nl$E!!_39~pIK`x1&{!oHUOqhvWMa>SpDxN~4|I8Himyk-*k5(k`lK4-z<&iD6h zj_x|E|HRGJxmmwa96t(tNj99XqF*BQf0XB=K5_QMDI%TruV?N@8l3q52RqH#*8dpg zA7XT}`_bp1uN8LQt{-jlPh`}|{#Ostk1Txj;OxzpIPeK6Cnx_iJPsm9!s3^x{ft-XuRd-1OSC&k zehK@(<4N~39emboI+wFG2RR4$83AV%UqW%v?EurwIMBXCTe_Sx^H*weR`4a1uQcMo z?f{()eI_ht=KqTFS&#lJBhGr#V|50fVRnA?B@|~9>69-KcjieKcLtwfc7Ekdai~VR z?oZL2C7mhh3Y=g4SCmh=_Ely-!|b5@OWaM@bCh)J4soQ@zQpWT zF*_Lf65U_P{v|4%c^r(S$C3_e+b<~2a_Kyu0S>wy;AeC>zjCHHXi7KzS*}kBq)R%y zbXM|}T+RwUtNpX=>872z(ut)1$e-M{Np*WvOq4>FZ<3`ag{`k=n zf96`W3;tnLV3>bIw=fqsmpY9-Jw1N%(A4+zu7@{Byh(-!bqmE8nrbQ+{NZWg9g!J- zP#dFg{Nt=>m(XqjfihD0#o!-y*`K0aI(7*P4hRcGabGpQ2=R{y3+gRDR8zUCKJi`E z@r5IHWJbDQjBB(@NMLvu`6tH7iV>0n(JtM?fl7 zr%r)kK0$%KNXbV()XvqVW4Dm*-9iIHBfO)!qY}MZqm_w~p_qTIK{h3)MWdBt36j&L zLKH!~^t0?dnqltoN98#f!5IG02x?{{8j8oBdpWgo8tv97@9Dq%Y1*f59qm6E)|gD6 zitkgVRn_!ZcKZI)v`naK|K+cyWj6l^s)9&!_YPrRff4?}!9g9Hb&KrWB@|0QKj+Us zJg`o;;BH~{{5w)3`>Lv$RP=>^bp-!bl*XLiaU40Tu?t{}#bED>A~y>Rpc?;~aK|b; zRakGtaOG-v28MQyz^*iEuzdbuo#p+d)tL?2Kda{?j4kIVny#xkcCOrs>c8mH$jv7l zW2YPp<6k_8@rxOTo4vNRk^E1N_$!Y7y^MJHCmcur7KeHN0soUOS&JjjU!NV{eF}e# zFT!K->le$(@yXx7-*#+Y?R3Z0{!U))SblGRc3fq;mutjMDH4!<)ivMRDEvMX}ncQ!>% kMJ`2dMIQXlrO2zur}$oxAHVY{3MdLH3MmTXcR|Jf0(yhK^8f$< diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_4_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_4_5.i3dm deleted file mode 100644 index a54f6e0af3651fa05b5aeb3527b9f4877df96247..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38016 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 F{uia6?uY;Y diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_4_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_4_6.i3dm deleted file mode 100644 index 064320ae5801f95eb454cd37ad56cc652f5f0b45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39752 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

    d>W~V8lb$_A{XxIX> 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 aPI;a3IpxQnyiNt23OW^XDvUn`o&Fy}Pg9Zr diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_4_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_4_7.i3dm deleted file mode 100644 index e1a640827477a6ed86ce8088b8f2c139f4879514..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36960 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_4_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_4_8.i3dm deleted file mode 100644 index a76ad4aeb8bd56cd5f7a78bb58f42649ff560491..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37552 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 YC%PSVq2=ffpf0omtwgKPYM_^Z)<= diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_5_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_5_1.i3dm deleted file mode 100644 index 9258405c78ae069f658cab990bce2a8d523c2c65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20272 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 gswjr9B8uXQ5{i>i_@% diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_5_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_5_4.i3dm deleted file mode 100644 index c2d69608377e544528af4c66745b2945c0d227bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26560 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_5_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_5_5.i3dm deleted file mode 100644 index 31a0b91e0f085fd3b7635db1c3b53e66ec855a6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30896 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 Xh1?3`r=VLAx1w&v+=}C;sN4SmpySq} diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_5_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_5_6.i3dm deleted file mode 100644 index 5cc6072b19d2691e5806e911f9a4e98de9b513fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43640 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_5_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_5_9.i3dm deleted file mode 100644 index a2ab66ce51f0dad41405168f43ae8517f8b761a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4680 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_6_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_6_1.i3dm deleted file mode 100644 index 52471d8cb296cb6cb7fd6838e651beda5e197f21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13584 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_6_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_6_2.i3dm deleted file mode 100644 index a1408b6a3d4e734e5556369452851c2860cbee3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17944 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_6_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_6_4.i3dm deleted file mode 100644 index 9ea0be4e0bad64e4a5e551ee7944a9043454d7da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24200 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 H$|?R2Wy}7h diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_6_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_6_5.i3dm deleted file mode 100644 index 3bc261dbe160d3bb0b0b1a91f191582ed3fc714e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35704 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 z 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 TmEWb1OJSEHE=BRHu*?4g$_^sa diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_6_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_6_6.i3dm deleted file mode 100644 index e8713de3e8ff7be41d7f15ef788a0dafc22d31bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37296 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 XsGd>P#jB3$S(U%4p6WTg{8j$}BxrTo diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_7_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_7_0.i3dm deleted file mode 100644 index e4882b71339fb18c9faa729ac7317e8373bb663d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5280 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 R+LF4GdXoB*2KcHc`7g*|;XwcZ diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_7_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_7_3.i3dm deleted file mode 100644 index a6bff0f8b953decfef81792d50c2df9924f5335e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18112 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 SS9MuUSuI&@Ssi@UlKmHtwJ9qA diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_7_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_7_4.i3dm deleted file mode 100644 index 2c627d94a696251854b4076d98f49250bee18f3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17616 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 Ab^rhX diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_7_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_7_5.i3dm deleted file mode 100644 index e6ef25756468438399e26bcc3d569697095545a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20696 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 XDxxZiUxihlsfwvSR~5&vVygcE3SwF> diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_7_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_7_7.i3dm deleted file mode 100644 index fe89e1a5c6a57d7a6b6a379c511bb522dd690965..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10136 zcmeHNdstNE*57DTX?edLEsr>Q)J|wKd-mK=W`!4U3_*dT$14sn;vft%3`inEUe2kL z`k4vdQScI)Cg!z-*&fr>$4twEnPsNg=lycLgk>ks+UtD>w}PfUecxaDJlpl#>;0|Y zTJL(-o&nu1Lt<)xAPDn13qmQ{rOgGQ>=^|3NAc%q?U6CDp^?4%#KcAPi42eJs}0s0 z#31c}s2F>!Jt|TgoTH6@)$5EJJvzhb)duTwgS3%R(fvas;{Gz=-bKC9BHdY!wukqP zy_aIpn=D9)=@lB$=iW8RY!q>=PiRbR9Oj7aODVjbOlNMuT8;gkMW6Lk>K^y5#$Lwp z=rtOR33AK^)9+%Viq-ey($|L}OpWaohJb_IQrJTd%SA(s~oWN`nFh^KP=JaP&+eg*xjIgVA~W4NyudChTHzrb

    WeSVkvmnvb{kQfZ|! zdcD!Q-}`glb2=?=Bnb2VdC;{)Gr4FNl708I? zklB@+9B)phJ-%OX-!4DYVQn}`SL#Nz*_z@aQyH!%?K3%2r29+5>3Pll?meVTM$T9D zo~;BjxzZ%69W_e@&7SUUIaeOF2Ddr4=H+Dmfp<$LbM(wT(4 zs~1aqugtqXc;=N$>w}k<>s_UD3FE#JQ<fC(U1eI}4;$TVUaldB=~A94KH(f|Me diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_8_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_8_3.i3dm deleted file mode 100644 index 009b896f49e858ed2b6382df276e61ff5b302285..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16152 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 H)s+1UGuF6x diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_8_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_8_5.i3dm deleted file mode 100644 index f04b03cc7be96e6544642a94d101f309766ea05a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20192 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 O6=f7<73J_%M)5z?{Gg@) diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_8_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_8_6.i3dm deleted file mode 100644 index 08b2573f74f5f85cf635421bc596bcc9ce0f2f0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24072 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 Hs^-4{jMYE* diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_8_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_8_8.i3dm deleted file mode 100644 index 81c910b2b8c7ed1fcf6ac15803d5be4936da3712..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7376 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_9_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_9_0.i3dm deleted file mode 100644 index dad13e504f8218bf266a85345185fd881d390ad4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7376 zcmeI1dt6mj7RQed4vFFed}pP{Oj5M_eqgX91U$-97|TJj3moApT=3qDuPY*1N!n*d ze5a$1IW{_Fn)>M_a6Ve(Yn(At(@ZV(lVzFjnVeE*?S0nO3;1fPzoz^7Y}R+Lz4mYI z*Ew)EPGeqC6G0I6bQ6TtXcvVF!nt4s{4l>yl2bA=V^iYdGe#xFrzB<$mZEIdNGUBf zL(Y^_Q=}-Llrzbzq>di#QM^)=t}IeYNgb9Pn>6ZoX$>3oI+N*U`!G3iaAw00gU)8d zkc_z4r1*wyM!nI1ZSk=gnWHdA=3t(}>n>Hwy3aI;a^~odhOlwZLmo#w2)UHFRZmeK zlcU9y{SYMg$<_EmQ&HYa{(+(>zeYUzK2aV_JiLV{FCflrh5E$JTZ?jU;;6Qw+>Cfa zm?#_l94^Y299pg}9YnbY`5$O6%BRU+jQ)>^7396dXOXWF5AH0=dx(!BA10pM<);4| z^w*KU8~WcN&g>-0FB7-!D9Uq)m!N(naX;j7#IeXF#4C`;6Cb)?l#dfH>>f%BS9F?}&6f&5(`7G)ptbmZrV&-W7LCBz^07UkE8S3M%iYlvTn5ar#(`Hzb7 zQDU*LD1SlxT%;(6(Vm9)7v%?uXCPaN`|9xw5RcP|vP?Y4Aj*@8yP8FL9q~@1C@&&* zTSWO3aWe9LVy2Ujr|s_%9K&$p(>TXX#Mf|r{QM@Kx2uX4|NZqKIqVsYa|KbpI7;KD z$bNrsNe&gTP` zCRNlP+Bku~4}v4g)Pcs%F>EcSMNEOqn{}Kov@*cvb5qdQ_N%Udw5KSR0hxP>|D!Yf=9j`{m<{Ga{r1k&EZ(5EY6`<%hmR=-Ktqk$O|pi z-YbW5e{A)5m@>7J=bfL_|8D?-*e%|8hwbiw#R08)cFUOa{tJx5(sO*j&r}Bm!pqFXfYu} zACG?Ty>gx_@nqkamQPRNy!?SQm>1rG^Rpk0R5vbc!a45vwV1Zz(<;lmbh;1TIvm3_ z4`okO&*>*}-nMyy>S8O zAd8yVOxvR|ha=Bp9BZ%Fdun66#4n6b}Bu=V8r*LU<) zOLj+dJ{kFXZCa;sTxb3Ea>yDO!F6<@-pJx=Yk zpDsHN`-h$aU*GHS#g4VGwC54nH1#7`6gd}a`YizE#6Gy%aXP#$RYAr3o54MK7Jz9M z6j%%bYY5{9K+fbY_4L3jN)T&kJ}O5xvN&ZskR8 z9jf8A)AJ$x+5vF(s)NOAcS82O7okV`X4o?6OX#kD6?$Gj04JZk2+vQu1c%b+!5^-z zfn9UIfeCT@;Qmf4Kw0-8hy|+vHY|l1BNxN@KQ4jNFIPixbroz)-wz90yaFfNyb8;O zHSpp;u7daDnGh2GF6{dJG+f>L0c@&V0=wU>gU)~d4@7J~2~9Vg1!w09$V{n$t-X$d zPjco-QCX7FY|!haNNx>ejojJ|7R?&{)=G67S-WOUI6u8+ zM0zIDGn1Z~^vtAZCOtFhnMuz~dS=oylb)IMETm^4JqzhsNY6rg7Sgkjo`v)*q-P;L zE9qHD&q{h$(zBADmGrEnXC*x==~+q7MtU~Vvyq;S^lYSOBRw1G*+|budN$Itla8Hq z?4)BS9XsjRNykn)cG9tP9g9xK$Ay;bq2+pLxgJ`shnDN1<$7qj9$K!4mh|+br|pwP zr|pkLr|l0~%A@TM)+vv+KUmlD=y9Ccky4H_Uh#OH`A%0pJI4uyipQh4N*s>D?j{4v z^iw@XK8_@3Zh^PR;qjtEAP1^xk@w;WRQNf3FS&lHm_(&e={~l2tm4Kcs8_k&4C)2d ztK6v-lO`?G2v|Blp}kznv@hfDAtgel$n69fbojk<1BVzgQ34p!h2p% zE(YU`GLZ*gzdp%ToTo6-WtZX0*d@k(l4+f zkOPC!pIsK|lZq5i0lV5}!ianTKB>g*EOL6C`0Ig>%;Ck9Ii+4!_0b);6r~TmDrdLX zPFJ2Ym&M?`if}Sgl!*^laXTG_l*HvILcf&E(z2wETKaD zB*8h{H3=$H5(A%)f9=`QvSsvV6E3F3E8xD_%!op)+?_acO>m3%7v(N5bJz;))B4-3g9devq?t{cZS1 z41V-_yFq6+*l(OTnmNh%JKzy3!?UYx@-QWj@A1tA58UNOJ8ZV%blGksDXx4ko=U3$ zx6k3uXU~_>WVaY^?p~wLc;o`R-q6axlgmz`zJZp39X@SfG^l{Y7L%6omF>J1^u$j|(G1Z$0O@(Gynh4E>AVCy@ uu>=YC2`z*Wp(U0ULMtIuXf3qC5-PM6+6iGoIF@!od!d8SQRsxFgYaJ%uLRlv diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_9_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_9_1.i3dm deleted file mode 100644 index afd07ff10d5aa74d687e1428dc03e3b579fc2b0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3888 zcmeHJeQXp(6dwwl0!1iTP(&;XL8V7?cYE90p1`D(ek{4R*q+tU)Uv&9uWRq4xx0SA zd0dkcp(RiwA5Aa@A`wc2nEHp2fOje+VgRFtAOXc77$pd5NC+UPZ)RuP`zUEJ;XgN- zzTf=jz2AHDX4;O*H+sh)giKQq8UTBC970pa0)UV3VNR;3b{19?6<60giYrQ;Wt^Qi z8@ZL0)sj=Htl;cnu3=3`scdQrDk08piWs?y%Bu20NBv7HQx|!gWq7$tDlK!Sdf2R1 z@Te{-bQGs9na$>WSSv29cGg1%XBkZp3bZMaoSdBBXBnhw-#05r-Cv3Zsn?y3D}dj| zcp30~hWFYG(iDdG1D?$A$KW%G;ZuMYGyE&y+fADOc))Kke(eH-l<}IzTjm?2T!tO< z3{n-tv!)uPPd%E?4!{=~woWrhUongUf6MTRaR#ZpMe~=QFi59aUL)Xh43qj_Wi?!Y zKIF1KYydom;ge4pBs0UuvkcM>ra$=PFn=Z6Af57Rxy(?*VdnET=+v{=KcJo>RuAd5 z_H|($A@o0fm_51s_w(_C&6xJPTR0Qjw!WF-tStg|hplmv+rMafytn;D%JVZ<;$5?| zDSv0;jQE=^m6YGpcYAUF#VF+u9c{;}$~MMG?5&_;qaUPb!V=fBCVcA9Y74{$bg( zv2%T|)7YKM+i>RKTeSWgbACB;_2($%xAoNG&kl~I{(HNu_|is!@~>nl@t)I{ssF0= zCGqHTivJWc%zm(RfqZ4$-rI{Wd`Yj^O$4aafRF@X1NI*d0B9K+}Pda*os z3t!uQ6Tj!(j{9nM;EoRl@!3zd;^?v6_=s-+mu|j|&je25+3R-VAD8`wkIub>zkB~4 z&U|h^-g@{hzI5$#eC6V9ym0U`Ua(~+?zNuBxtmX5fA0<4)_MkqIkl0q*K)ko##=Ze z#|svdnOS|#Yjd-SSu3*wv-!;0m=!f^)op<`7g)T&^aZ9bFnxjP3QSjEx&qS`n4YNd zqNwSKqNXQ`nw}_XdZMW5h(rEVCtoO_@deg=o~tu*4NAKb45@C_=O%q9RXj>gi@!w) zK+z>0B^XqEt+MRN;qytlbmqPRR!eHYe`D^KIK7l4rhsHmDlTb~v;1 ztO#>5TmyJ~ggLk3Qv!0xAK>gTs4o&)SthQ1$y@!IEd<;3W}sfit{V z7z)Dm3UfYxqe76WE&`WNpM^PBv+8LKC=gz2h6S%23aFiAl4mlSMi`3{7ZP!zGw^j0 zW0>|sfKvKM%+oTMd5Wfw#Q{Q)geLt9gm5~xv?4l0uLx-)Vvzqi%zQ+y%k znpO}I%od)IZ9eEfpHEVewpe(w7nQWj1Smv(>ASA53VIK3#)KrWhf$M&GD~8i`_X$( zT1(ofXQO=|`Z1ikrnrb7EwM!uN7~;=sZDtT96KGusR^-2|B;%SxXB@U3R;_68v^A@ zNcMPCSCzlb-Ry%Spl@C|s1*4<{y>TBq8GW=lqZ_#XEqmDOa+#Lp&Q3`&SdlexMC5w zyV@aFDUI}uhZ~-F%4sJo*kHTlG#rZ09fDhFv%vAm0XO;m;;jYw{P5|e1;dp~3?0&{ zhMP-nqB%v&LN1?H7%M6valTcI)I~PJFZ(j7f;=hM_aN`fF8cpl3mgqUi0;F#Mb{^G zw7Q3U$X?07Z0K0GI-h9uHDYy4*7bR!C4Ax_Ya`7GuaCra9g{VJb*p1C*E!v$uIW6n zsYCbGEmXO@T2PO+{H~8k!Cx P3q6jq(F`;bMmG8v51gZY diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_9_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_9_2.i3dm deleted file mode 100644 index a4c7481c36c4125d0526ad7704c814c831d4f71c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11232 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 I@OX;*AM#4;xc~qF diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_9_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_9_4.i3dm deleted file mode 100644 index 12d10e796a8af9745e729223e718bab3d194d6df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10816 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_9_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_9_5.i3dm deleted file mode 100644 index 6f9e2ebab90e8ed9477294faa14c11da626d3a1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21888 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 aDx)f^DyJ%sud=ENs*0*gs>=APsQMpr&qQhf diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_9_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_9_7.i3dm deleted file mode 100644 index 7a65c398d118e398db1494ca7a3ac64d9a5fbe17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15440 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 J##c?%{{rogFm?a{ diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_9_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_9_8.i3dm deleted file mode 100644 index ff1ccd51ee732fd641531d795e1347df37da4f81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6064 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_1_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_1_0.i3dm deleted file mode 100644 index 549a1b201b88f3291bdd6ed2b83f09c8dd4fcca8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2368 zcmcIkPly{;7=PPMS68jI*4m4Q@@_)9PG*wb%p5vRV|Fu3HYrKm3fr(wlDFBR$xJ#k zS*=6r9z2MkCt(Hg)^j$FUngXAq~0xij}z!1orfT^(Ki{sy<%y+cOVpKtKJ&7c0zf4Kga{@lqg z^`n2ksej&kPk(*+uKx1sSGsfY7d@a>n`&z`tEgF;M5Y2u$x=4rsuZr(RK(?oE6A1f zLWVYyw8^%Z=UW}i?x43N)@7$IIhU9VM)xhw4J@y3nq83pE7*wo-s#pC4?zvKX9)Rq znvmsMr5sSx4zrnS`i@IAny-{fxfgTtQmMWOVYis;S&j_~c}}7*TwV)|VT2>( zK>`7_oi;-xHHMI$SOv7TX?5E!gY9c6Q1ndSwO+%;oRpOIq0@0i9LJ48-WVnW+GE}( z-X(MlkRKqReb?$)z6Ay5Co_Er*&O(|45CX%wN(6W;%&0*w$(x#$g2m*P%X`m%Ulbp z9|p0_9?0nmhQ**}3wC&p8wP3)Ha3{MYOz)-?5qP*RS-!NhT#LC>+@E z8YuG^3d1NUNGu5Ca9|Md90ieiKBD$mS}dc8#_PWK8;4#k z2nsDD%jT*Z?CogxL+7TxD(E<8i^`U4Ed$nFUA} zyHR3x$A_t0kfD91+rigfNvre9xO)$5hLMYJ-Sbt0$;C-bP56>=_`-ulhl&NBPiKdX z;ahOo&lzQW_M-1$-r#Ni7KR%S!Vly3|2~WGkNlu>@7Z9kNP0v93ohhwE_BERM!i_a zIm+XNy8YHD7ZleEj5@@E3yfBzTiE=XuY2u1IwTO91foJit4|hY0%!bVweuaM>{q*AKxS^F$!CfH%EQMSspD$9ke^-A(uw;OQDa>h8psp!tUn#%;_W23rn?>4*XyPa5G_NUu z)BLB`Umia7dml*l``7*akG%s$|8(BwKYAbYArl>zJ7#HfJIxY=GOU?kE&iMbQaHkO zNLvx@o*dYLM3dB(@u1u1LBQRiZTDGo`~Z*Bw|18?#_1Zs_j3SG8ntT3Y_!5Ih0NhD z_igFy`+@LWRG6Cv!?J@m8e?lCD!>Fn=6W4Yl+hexdYTJa zyC?b`pX2r8Bq|2B^uQY8?wp)UqP3T%+d z%$69kHIl@J(K4`HGQKl3OyPD!n{2SI0T#n@X_YSb1vYUK#I*+~X9pCPg4%7o5qSP2 zP;1ofa{oy17s|*`5S?U_W{QL(7yF;uqEOTpGexxsYL|hORJLjb z3w7*0E_W;l*03-LdX9pasvg~YYAv-x=J*Z%?2pRT=~IJm&6HV0pQ<-# zV!w!9b7;h$eF!5%LG4bNb58=#%SJst;xXJAM&7U_jZ)V;TMzcEu0ADWyyvFqqX%C4$ zk_W6%s+!qSE?+$^=1aTg9!u*nc37?!%tE=u(gCZV`Mlg}dE94dZ4hIna^)afJbrQ* zRqBbcwo=&LGouEHEi|a+vc-H&sNQ2^lt7+=f2zLgXHpj;q2#^eaiRY%6wt< z#}E@g{LV890i0((et7x*_u7MZB#VDu$$a}%mHK;c-(;S#Uzvc3CQBc&gq|{3jBTcp zaaN15IzQ!}FWSOxllD8X{lX$`Q;~u=Y6Ip&)pMsX@^5D2Oi4qiRp3r825$vhEkZapS zkdL&DElqy$__nTX>)RI-H#+&qjoN@CHo(~(Ho3x^vd80shr9ewhw)+#yHVtJ+sCO) z;j83ZZkz6xk=RZe>l8Apy9skUDDs^a9*B*r7YbQ->4Vxy!&yOa28)dsKdvOg-6 z#!n5tHDhKPK7Svwor0(NB2QtPC?D2M!vB-YIBSeKC2yGr*(k~&CL%t(DtUMf+bB$W z!cii5m`Fc~C%K}qUSZM^R;$9qE16oOIwhwyO3GHPq$fP$XJ8iQkY->W7T^+GMp}R? lun1S-8qy+Mhb35s6{ID&0jqEmZXvD08r+6Ea2M$|`~iXj$?gCE diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_1_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_1_7.i3dm deleted file mode 100644 index eebf93ddb178440b4915984da314fad87422fcdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2464 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 BNEiSB diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_2_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_2_6.i3dm deleted file mode 100644 index 252fbd362a7e73b4442795651e81fe3a6c31be76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2064 zcmb7EOK;Oa5S~JT@_xe!3GF#Vj-9k=4n!fOO;DOti9twJ6gkN@T`94H?eLHl)FXd@ zBjUh?zfdH^4bI$w|9}t@M`qUE#to55R~pZ^GqW>053ewK!vz4iv;^=T`E#UABw8d7 zSf$o93$t>ndj0D@PH!EhPUSqj{bzb%5U*LHd%oxkyH7^<#=;v})&MQ$!4@XdADC;$Fvjjl zfN#eE9yIHpCT3nb2u~zdX9qVG9OiYDlL^!MU!>k|4c(a zDiw{x0_)lAy!_^c){+@)yQFDo65LUr+0bNW`LYXPnMWOWgZtKCAi51_+~2XW1@gDH zJYIALj=N@ctNQBx#6_7$ZqxxBu?fy@*yIN9$sSJ&9`Eu$9nP#^ zy3~y-xBEU$<%*8&v)n$tUPgL#*_d|kpTjtE@uT~`nmD;MiK!W1It^cVvFK8Zg9ej_JboQMJ<-4C^`a|$h{3@?UIdhb|DF4L&`yAy*l#fVZ#g#nH zm5#W=q*wbmCwZJmH*ZgJMRC2tq$8}j!n9X1#bt>UC79PJn27#v4hfJHa~ sC*c&*30Q*Da0bpIorZI89xlK|r1NkIF2fbLigX#S!F9L+H<7NxUl3B*NB{r; diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_3_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_3_0.i3dm deleted file mode 100644 index 2ac9468a45592cf4ad833acd2ebcea65a36acd79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16304 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�NpFtP?CA>9e3+ONJ zg)w2}aP`PdxSPKis(rS@&ST#~_3gXR>wFHp>GmBQ%FhSioQ?3(*K6SLk}~M%G7q+v ztburAHEgXdf|Px=Q1j^~=;^i!a?X{&dplNxVfQVl>`@DAd)|P!f{9Q&s1#CO-vI@I zd%(~8T}aed!NFtOA*1WpaQbc;gpXeZwF8Uc`kWKcY5H)*Ww(=64n z&t3%ss%Js)$0hJ$@5#_7ZVR+C?*!j1tKfsDzJ!rUiy-$@HQc(mA0j;Oz}2qnAwBUt zoOl}j+?WqNep~`=D;7df)45=oErEUBCvf?#MQ}0o5`55O64+JN7?oFqN^kHGRPMNV zYWYG}I)0_+i-9jjw&>M-(eQ=a>A9Vr+vz>{Jx{)HFM94p&%GG97X$ZV;9d;e+`!EZ z+}yy;4cy$o&5hjL$jy!1+{lwKaxX^i#mK!FnHQr6vom@yJEI4)GkWqXJ-^~xG4ib# z`Bsd2=EbPzUiemwd@Dx26(iq@k#EJw_h96EF!DVZ`5ugX4@SNRBj1CO@4?9TVB~u+ z@;w;&9*ndHBddhbC|AOuR?9oWMcxB0@*Z%JD}jq#30&k#;3Dq<7kLl3$X*O;4L8tm z0}VIOa04wDYPnELLheP&y=b`?Ezd>Zxd=QLfx8yC7lC^bxEFzY5x5tDdl9%79rvQ+ zUOf0c4}Q;+-(wv!sM*ls!XtRH2wD#oLF>UHXgyd2E%&13UbNhcmV421FIw(JOI|#= z7f-=?WT_uAt#wwA_o9d(pC71m0qS zw^-mU7PQ<8Z?V8zEbtZ!yu|`Za#R6}!z?&-YrV6~N0&l9I#n)=tlTU-?VEVYqy_T<8 zd|W+Lk?yJ}OPVFcW{tHb#>#I;f~VSbRJr2Z086~ZH7+U6l8hH&mgIz_ zvGL{<8(!4c4IeDJ_Hs>l(Cr7iy2o#rCDsz3!lDt>kJJ#!5L>d@Y&1G|T>Vbn{<^H{dv1uN-d-BiwSSjY{S5z{=Rf7AZrAR4n|biX zJ)c|dJ8^Z6Q(ez>`Onq%!Ra*c&OIsAE7kvgw;?0^;6nUAxYLlH>4f_~cQvGJ4bl1a zy{93{_l!e*gSw6W&2Rk!S8qa`pa1Uh|KQfvjZ;61f0v|gl)C)jy#8zM)E~LJ8}v=J zrqHikzhY#jYixYjfX4Xm5fhs!6sMZt{~Z_-8e&(O@fS4w?xS55Ye}>un{7$SDle5^ zXmGGke;;i?aM%F+jVH~LoMKH%#2eb)Y88HsF(n$6@e7kgDfs)IU6q&=W06T6nTfv* zI}Ubyfk&c2`Ya%)0!A#jh1zAUkSJwm6wjC zYe}}^@2@;aqB#NOszGvCIjA`rEmD$_d7!A&QKKx$;a1C=%*j1pE_GK$CnbzcO0*=} z!ZOC96Az76kcFvic*_bZ-QmM~itUZ2ZwN4P+!yg7g&lE(< z_T#L*Tw1w|&Sn-A8r#gtvGVy6 zbVg6%x9-(5#*wRQ`l~PemE0;%qUJtdT6y?*Vw6;!z@9ooW@P3xJnX(=i2P5G^z*O$ z|2qEvxVYgT@xS8tug=BkUta$x?_YJ~e_xef^1qwPFDE-!r@Zd!yeF?tmhU^S>#nlA z?n}P+d)Knu`Ko*0$?`p!ovV}OYp2Y)-ha<2m)HAWPF?3J-F~#GGC-C){;z>no#Z!tl_-d_at7xZaujqiUc8dQ2 DeTI*Z diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_3_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_3_1.i3dm deleted file mode 100644 index e4accf882a5f7d2d0959205b1ce574bf21919311..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2192 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_3_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_3_5.i3dm deleted file mode 100644 index 395d88fbaa611d21374c763d7a697947bd9f0628..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9432 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_4_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_4_0.i3dm deleted file mode 100644 index 2eff02295cccaba267b632d2e8ccfe3f327202aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10288 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_4_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_4_3.i3dm deleted file mode 100644 index 59c6c5ff762f8e591da46bb83a30ed02d0f8a746..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10056 zcmeHN3wVs@w*JXa(PFDx-C9OB-2`*bOv)?~A-^3W7fS8WN%AMAl1Va|5RHgla#X4n zm!p@Qy0&iZZq=o+OXhFWDB==PRFt{|6}zcRMcGvM`q%dj6XR^0=j`V>=XuQY{MP%e zZ>@K&Z>{xBg5P0COl=?tLU>O>ScNjXjUc@KC<6Sb`W%fkA|^IuL}*ydgov;aLu11= z!8(I3P%|NJQA3d&VH08TZ0MVqzy?jM#7r;dW)ox#d?y>Dz6U!_>UD>=dP} zoeamE7o`nb8Gh?$QA*^v^950QoZ}OhMJb&779j4I{x1IzrEfTnM7)mM#G}nNjzfPHrR5xVM$Q0^uOhzu4dWSgLzF5xkBW;qu11~} z9G^n|EbjZobx|5$&U}v`{*L2~=(~gCv51Q}eiLIY~a88tJ zb};=rXg`j}>hqf@y~25xV69%`JTp&;(rm6T$NH3W`$EL|9Pha(N=D8ZfVExB&wCne z_Hz6l*5_{=hwl=lM>+qWc8gLq=O2vvwH%+^DoTeqr)!5OWpKT@T$Bnp4oBZsoF@(K zL%98=uW=qY?u)pBV=dZvd+OXKN;i1T7h>G^IevGKD2?E_7viTlzJk8TIgf<+BFAI* zic%@BgY#Qay2|l0h`n=ls8W>5xc-TQqBNi5+5@6Af#VxTMCnIfpPS!fe>vV$B}y^e zrYq{(a2$6WdAQBlA8y-kIw4BWa((P+QEJWcJ2j$ojNgk+l{m<~Ff-mM1xW-UmnFy|8k< z6>HLh=PY4O3>-JSi8I6TLFDYr@e7EXb37DhNy}$G24|@WKT8kn^;E9^uir&!CD-@D zJNQ1=cfgt7&3S%4iSx$u55_ul=k>`z`%C7nnH4uKG@Cjw*yy1lRJ~ImJ zTbnCty(;v7_tYgM5MKG$rZC~#=g4ONz9U=AXY)wE&=#S*G`b_%5wvwNFSCrO{uDU#-{RjEX`8VC(I!%&yF0hbR z(q%^o;U^lqm4jvPc~sv=Hs&c6M?y~59b!2YwL_XpqylQI`Sei73e4k(P zrtNUU$)ta}XAWGB8bUmy3an6L*OE-s>--e(D$`)5Hw z%{1tcEe;Vlv<)xm_FS!UG9aP~N*zQHbEvk%Ap*0b9Ms~Gb{BD|}#?3!BUin

    4z)|5E!LrTVR*6nCU`y0YTHg#jv0(y!^t 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_4_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_4_6.i3dm deleted file mode 100644 index aeaab662f75e61938f307158aed7751024ac1771..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6376 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_5_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_5_2.i3dm deleted file mode 100644 index d5eb711ee157cff6bded669eec209d6957e7b1cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8120 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 M3QdJzydD+)4Izf+JOBUy diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_5_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_5_3.i3dm deleted file mode 100644 index b002ecfc3fb47b7bbef7d2c9addfd4eed3beed78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9712 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_5_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_5_4.i3dm deleted file mode 100644 index b2e87601096acde17432f9c465cc82167b660fd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8832 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 JoABx){Rc*gt``6R diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_5_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_5_5.i3dm deleted file mode 100644 index ac741c6acca14639b8c52d9586a031e5a0f1b951..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13224 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_5_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_5_6.i3dm deleted file mode 100644 index 0112e3f81aebe0ada4c3bcbf1bbdd25c0d96eef5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5984 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_6_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_6_0.i3dm deleted file mode 100644 index 2ba891a5a524da31e8029f9da826bd3258efc8af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16392 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 Pr>L)Jpm+^m^%egIXOoH0 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_6_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_6_1.i3dm deleted file mode 100644 index ea38f968e1f1a440c165a6bd900464bed121e4e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9640 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_6_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_6_2.i3dm deleted file mode 100644 index dd67135b59e8828f06388b1ffab52f459e7dc36c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9664 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_6_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_6_3.i3dm deleted file mode 100644 index fe4f6c14726883676aad8c6d63dc62856d5f010c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5528 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_6_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_6_6.i3dm deleted file mode 100644 index 28201122dcc72e455bb3cf0abdea3a852ebb0469..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2072 zcmb7Ey>HuA5I-lCo3zb`o4P5`48iLVAW#f`*s`QWP!w5KB1?h91PNThrKBhMArdK& zRO)~b>aknLj$PaTp@7${oifB*3uMdGu|w}3`7Bydz6c!e-u>>~Hz~~CXbAwmIS24N z@&lwtNc50CWYv1hD%MNo)?Te#uUOkGZ>HJ(MyqO78+DcsS?5{68~wh|1D4mOX;yDE zcZ#*W2lwYeGc!Y*)ylRtZ?I{gL90}(mFE#%%OX@RwyZsLZ*5EOfj8mP>(TRTB~|_D z_^-Q^_b=P2YC4uz?xd>6FC#r+(Q{Z(62NKs#cJW`+V?k)2><8b$AyE+vb6o}!vHUL6b9#=Ykr^7Yjci6|?KJD~r`!+3KsW<3x;+v8 z*tUmgVP>@J{UP^#?!=lCuuU}$hSt^@%;9hu;P@=S-BzO>G8^r&%OM+ZhkJJ5dMwXM zjasdEr)X4a))snx$~|AW4g$uS#;|LC7maZ=vE*YWA#>awCrN8hF;8_0S$AIydmhK@ zyE+0RJMhE-9oDq8wt#LWfux)?Mtysl4%vwN`?MqE7)6;t$i|)+i9ledWM+1NAv=?R zj$yn^EUzbbj@CptJ<%l_tZRhDu>87AmwN&m90hUg5$f3vg{7c&7jO8k7X|7}`hD&_ z6#Pf& z9El)U!+V3E=QxNV>ruU@)>0c)G}-r)*DUnIa>;yOU?ZDdRNtb|+S&$=UD7NxDQ>CP zd}uPW1387L&Et-@!vlLb6y2sf8SFba0{MB{J}5U>s8*svOiET<>62Vx#Fb3AIwv{llSJ5}GwBs2aU~N* zvf@gnv(hO(k10Lzc~Z8D6Han`UV<}l7HJ7SffOvmr${L{2j}54_#Ejxd;u5WB3wec X0GD9}zJx1CEASPp!q;#WX%+qls3Y1B diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_7_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_7_0.i3dm deleted file mode 100644 index 682e8f491e8f544b7d88971288f94611982b7afa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 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 HU#k8G*q=j3 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_7_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_7_1.i3dm deleted file mode 100644 index 3fd04971e1961042b386c530d60c74d800c58f17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19376 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_7_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_7_4.i3dm deleted file mode 100644 index b685557f1bd6739b54c39f8742a17fa221844397..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9392 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 YdH@YZL(otdgV8WF9F0IDVGKuq1M;owXaE2J diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_8_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_8_0.i3dm deleted file mode 100644 index cad7d6b7282ea53e1859c2ad16f58a39d030894a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11760 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_8_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_8_3.i3dm deleted file mode 100644 index 1dde2415486cc151e7a304dbd63db6c2c44fe51a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9144 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 ZLN}qi&_n2nS9hV8&|Byu^u?>U@E>t0J$?WH diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_9_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_9_0.i3dm deleted file mode 100644 index fe8f8f4fd5fe70f1d4f0ebd9b1b34a34a63430c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6000 zcmeHLdr(tX8o#KyRnYoG)UJwm9Um*wo807vM9tYK2-o4E5UmxF2Dm^ua^*cDioYYSGo&`lyd~tCd{G?aofMtzBtHY4NeHy3SJ9+O4gwsQaCh6G;JwtuyXl zX6D@QcYo*iJZQFFFl$>H| zuTYD80!Xr^nNyS6&uO%34a_B(vn;ug!jeKVc-`f4Rc!3Br&wv+JAa*l>n^>mlJ@Nj z;@fvs(y0AG+`k(uMN!-s!AiHOW=?ljxxQtBo$@E*SSgG0d;%-|hVu77(@b&So~*Q$#y9I&Nuhk2mX)qk{GA4BqqvcW zyfo(QF|3qLu|?gM_wBK)w1M(VRjgD&<4@_wN*gI|=*3FsY0SzeSZNT&9N_U3Ck|jG z*WTc|(g4q*_}D;JnoIi}`3qK>zbB~4fb~wG_(RaIq58$pt7?j?TUF8pTEjOiD=ntE zw1ZgbJ&MQ1u+kBVD@9hiLGi1@S!p4SlQ^E0E>r%W@vKxvaR7Ryrnmxd48?kgvy0}v z1?S;1#q|O!y-hXS!tnFZ|4o#i1HJv0;-?|baf*Awex0Fs9@Hbz+7#Gp59Jp@e-=>Q z3pkhJ!;q^7#oGa^DAo^TrPnETK%K3$&LMEdY!nZL9!{mW7<{v7uB(v1oEcdkFrRXh_f}(xUpcpRMFZCN{i7d6K5d?Yao!C- zN}j!bGREhdjA-WFc%t8V!G_EeJ_}&}(+o*DGkOH!C#}&dn$#hm z9Ik5d6MlZg8PoW$3khDPI~$l#Ur03TSM4>e9quChsD)<(b#+00#?>-y3q z=)=l!CLHtd`fF2qntny}dCdk?6vdM`Y3Z}j9|{K&er(YYQ|y>3!nb%o3(PoaBmABP zeUzK_*#sAAT*|>=dr8c?E0+S=h62Ky-(P^%m$edp*vds{*~+B^UxS`UUsz|vHAJen z2l9R>Bl;OF_X2-8n4^%|UMWK*c{_;yy*NJ_ma&!e_H_I!Ci$Y5=vNJ%sjM6xP4s8r z>vTiS2!eB?OlV~71fp4dHpirHsU-Xhjf+vy=cy!Sf)&!BzPy@;maEm+IxnPe?%*b21&)hd#AXt(nXA1w@?jV+pLwB_y_Xn)fi z=!3kE(fk!_(UH14Xx^`PqTbo7QOuue(3r!YqGvC@g@ zfJQ`YN3H|4XprFt6llJMj_g*@j$QAf{yk2jrZ>-_Q{6A3b1&{jjU#JNV)eJ^lJ6gA z?aErzwC@8nS9b$Ve*QWddhHgHx1L4~O?y$zfbUVA>qV3h`w8N(qi82T%(d z903eb3Yy@Y20Lg#icLZSmIyqLV+yUq*CjT)0WjT)0+g8d_% z(22o0F*ui?#r+g{9iPe{a->nK%6Yw<=7HuiR^|T zKe~B_Msg~*Ru3IR(JvOHUCN%Lp2M;1V`5=ZT9u#@t)56C&oCPvQ zd`=a7mW1wnTz;|5Uf`C&JzEVErB<)oR)KHg`8d9lF)@519EXd6*@aiy}S7(aT! zfdP^Ne4NW|E46uT@M%UWvw9(AUbz?F`sgzn&ZrK5l!G6y*c=76e5`?WmBL~;qn4B_ zyKV3x7);`@mIBYE;j}oZH6J`YPIoX-UU^}m?4Dzj7m*-8dO8%x9aWPmN_(FWH4)n_YSCG}&vl z+im%o&hnyS2W$a(WM=iq)0}pvd%88B9OP_1J^{Wj;iXm^G`vA$xPRj4%4wL8101m` zIJ?13&Xfzt9=A0-e3v`yuuc!l#k-LzJBqw;D)k!JKC8P3zrO^nK@{3{uY)rjxp3F} zv9jRg;*+Ru7fXW=U$8J*R9Is85mJ>?wH)5;Q>AqL6Pdn#%75Kz^7}aG*cJXMaUXRp zq4?N#Rrh@jUMn8>e*hjK9I_!k+=k|`4PiVVx(~OQ4Vf1GPG11ImCI&_{^Iv?a%hLb= diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_9_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_9_1.i3dm deleted file mode 100644 index d23a351af2fa56162a67a4a09a957952fafc6fab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15344 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_9_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_9_2.i3dm deleted file mode 100644 index a6d9c5736e3c1e303a0bda4aee069c8ca3f0983d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11920 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/1_2_9_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_2_9_4.i3dm deleted file mode 100644 index b3d241c82d411222b4e89589a610c8cbcf6e23ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2008 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_0_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_0_3.i3dm deleted file mode 100644 index fe81a230a3799d1c4f55383c55fcdb4db708ee86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5960 zcmeHLdvp}l8J`3gKoC(0P%V#)MS_HNX7-V6kiCRJlEEcOvkMdw9=qAeW?>&;cf+I0 zc0mr0`T#WuDwK1yB|xKqD2?T@gGC{tSYs<3KrBSS%0nz3ER{p)ckkQ@(~usBr}dw@ z=iGe1``zF7ykB0|XY>S87=|hA!!R2`GIzjYF<=<1k8x6w!5tUUNZ>F-RNI{aR=P;_dYoFp zO7{;{Icq1fQZwbhKbe(`52^Z%Wvt|(_+mLLU8Nj-Zcl@sdg|v2;Abg52snf0*4@KO zhBorl*E_3&DE0#0M}697vQmGlUkP|7#ZLilrkvA$R&rB)7RT zvAyrGNGtx(3+xjw>vgY(9;^&_W_U|rf`~~sjeDYrO z2jfo(zY%$r*vrQV=h7QdblJ0tCL=!4iEzwu1DahroZ#PP)Fr?CNtob%wYli&C4&jB&a>OjAN{$4{rq`N zI;wpG*_FHMf@38$CZ- znOFM&vCY_?P@Eg`h`zjj7TS7dBdKxS^aMJQT)rE}?KUW;Y?+@=^uJnErZhHmApW1P zo3CX3Xb{m4Y)GIT_v8}3YuPKd>+CGT@yCXu!M$t*PvX;*Ze2@A+`6@M(4=b%i7m>x zmK>3oN;rEBW6|Qy{RrniQ&idU(hD}6L*V*k)U&oD(Qo_P3ELxAWP%HRwsOQFhuZVx zjDT|aF@^B^t~MwIIl~F3IVD|*cP>EK&;5@+zkAu(1kopUZLw_)R}+2rFUpn1@)V*! zw|9rlRvsky_3akrz7iwBmml+@!%L2moLALWC+j|*OKdj=yy&eHs=n~aRmr29qlEv# zfS{=mMlX}>#w4#Z*4+7-fKYpH++f~%&14F&z?db zKGcj}^c_Zx=mq2~d}}M8yNZfB??sW7yU_ACkD>I<>(I#EOVOeF3ux0f-=LJ`ThVh9euQZJNTrENGnFdeWYnZ;XEJ7!bCc1kN)u0{RiM(O+FQ+ns<)_iRx61k@FJB) z!WSSh%mYdFX5v@ivxpyo&!)U=k{|3fJ;@99#J|8>sUIuJ5B91bfmiJXUOg9h)ea0v zTp+4aQ2h!5-K+f(1hrlP{sdQfW;GANOzkaHPxBJ8sehV}V5NMksu$Hh38JXli$*nn z(L~SHz6nOPZy;5kMNs2d1l6B~=4%n@p8B)UzFKHsEk==>lF7N{*>WW6^ZJ5bJP(eF zDpxq@lf!Pg#w+`wvj3K5)PHu^a`dQQ4!B)G&uxZnC;jG1+UcO3^p>)24tzTrT{IoM z9m|%$H-pv!g-!tdM#@OJS@2ZkBz zC@zX|t_XRQh;d#yD2H9qP?*c%@`~;D+|jvWf!&!8-{H-ep11X0g?n_Ty@wN@I`&_V~%9zib6~r~Pkf197dTtyJi-WoANW-+@>bE>GI6wW z>P9nw5sSm@s+U|Ud&nK%Xt@5CZ@OV~7LdbJ6@`d8@ozIJj245M3J9q^8$P?F>r|<8y7Vmsqb~_^hox)@w_Du3>zJu_iT) z_nJLn0sN}&Gcf@nBGhuSZU0^0Ag{{g#Z8m diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_0_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_0_4.i3dm deleted file mode 100644 index 6654e560bd3d85c95fbb2b38b0399cc06e0b6975..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23888 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_0_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_0_6.i3dm deleted file mode 100644 index 0c6fb886886c54953c6ae0f0ab2bd1aca22f97ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20144 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_0_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_0_8.i3dm deleted file mode 100644 index 063546a7911f38dabafd53f99f5cbebde4ce97b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11512 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_1_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_1_0.i3dm deleted file mode 100644 index 9c31fcbe1338edb22d916396812169903ac05f71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2488 zcmd5--HRJl6u-M`V^^)V+tz{)QZ5CBW|zt2W0SDmA;#=(YBoueu`g>}Cdu7oAemWa zCcBm)xKN5d>q8Ogg9X8sY9ESFp=?3ui*KU%QU%2a{{tVzbMD-`8PmYhhk^qq=XcLJ z_ndn^?u{95_ooOU_h$+D3V0V5M^OMDGM^U9bv;+k7wVg(LU~nRquET9Rx9iJx$Fs_QL2~ZVs zb$t^8=xf}+=M32JEPm&1MT$Sn{df`29mdnQBgF^D1Nn8pUkdya;BO}23wI*LKZJZ2 z@NWWN`#MtmSzwG2e0!QigghI3sXM#>&fOu8)6JdIKDk$Yh%s+`{?C5(wf8xu)oaVU z=XbfyZT9E#%?9K6+=pFlY5kXdw7>t?1?|&2=ehmO>+@RejU|pRJ>Gd3z46g9+I;`T zJ?;0o&$O$RyV^-_PixOS(w44%r@j8@Z|&B5-)d*xe60D@Y}4!pO(rvo2^!@*DR@fo zMZwd8X9Av91dj=>(ngdv*$#6(vtwEv^jmLr4aYK>(`17V>w)F}OVS9&?QEx~uS`SD z_9BG*Iz!0CdZp}B!{xWYryXW7$M9^2W@)}sD&^kJsjDS@1@hTpj%(T$DAWao!c4m@ zFovrmkPBt-sb#krBBe2eBFU9cTV1o)b{K5mh=HPSc#io2E*6xiGJ)p9iZBivgS;_} z`n1p7F5Y2u43Hndr`wL%H$4;D#7|~;5VAS&aOsQ7PP4J_o1j8grqwoEXajlmAsL!Y z@Z&PaG6k1tGLfbiwLZe__{)~r4GsELu2+F+Obk>0x8|F$8}PUe4M1MCAf%m7W1=g1Kf vAd%6`*6}I5|P)$Vs3R%@zFIZM6;di{G|MHUazvCzQKi&0x4^zx73O_+$VQjNmwAxT zUtD@~4dcH3Frh#CF2v6tCv-i+=bk3?`3S#wme4mMjAz1cPm>6$a4ftL7~1>kZ-wrY ze>b$MJtOdf@ymYo{^Iv&@ArkvFW-ucyogLoqx(tqQra&;T ze3$=>izOwgOrXWMAdch8u-_Ub13F;d4$dYT1}Fju=&s8L+~-g}5t-$K%l6R6We^Q9 zEvDkLu0mGa?(z=mKwbk#h88m-T;_7CA33qD0qoN?bc;@{4xI2DH+0ktpFJMN0r5re#`EaesPrBVe|DX4{miHkCi z%*X>UVk4N{u*nV96+J#Ic)ZI;I-FgGbg>%-vwJ>F<+2Lxv)mrOp6N^>mp<&?k;5=@ z@uLT}nlQOIiK#JLDh^*5SQJ#uaW0b|wMM_fW&c61;nNg-$9wlelDRPQTlLYw!XqucRGvo~U5@?2;C9~ulIS({Tz9Mtv0{I$fj(kJr L$pX0uG*3PP+|=|u diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_1_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_1_2.i3dm deleted file mode 100644 index e5e07e4b882f0688bed0d1b391f788ddeff43616..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8464 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 D1mSDN diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_1_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_1_3.i3dm deleted file mode 100644 index 7891f3d1cbc11f18dd7f6e403b0c3eb6007b1c52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15040 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 FzX5%T7gPWM diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_1_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_1_4.i3dm deleted file mode 100644 index 3cdb490b8ae5423117a213cac6d2e6273a966733..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9040 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 SBg3C*&a_}!GOfsH!TcND>KP>f diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_1_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_1_5.i3dm deleted file mode 100644 index 0e54a7e1c19add25b7534702ec01033eba6f5981..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17832 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%0AY8)_ExM$_p)5yFHf|t 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_1_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_1_8.i3dm deleted file mode 100644 index bbedb300b047a64948eef7b2370c880d7be6296a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11608 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 JBUcmd{{S`=lyCq5 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_1_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_1_9.i3dm deleted file mode 100644 index d2511667c2125c743c9fcb6e7df9c2245e7f4fb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2920 zcmdT`U5pb|6uvAnh`{nEVq#42f-x~=na=cY8{C_+ba!Vh?b? z5-BCEB;q*Sz}ZntOB4z<&B9@^>A~?ta%UuVVAoK)QWAZ$>ZH;?kZ!l|i6O8^^+saR zc2&SDfhrnFr4K-W^Z@g3nRTtX5x&D?g7U=vn}am(r!EM}**!LX^rE2L;5czkP=4HN z%TJvZl>X=E;(KQVWs=LkeML~-c)_;0`m&&G&)E3naY5NO55D!1pseCKyaj%SxF0u8 zy(uV|%iZ`>$Y+}8Mr*LYW5huS{Xg;ehC#CD$;Vr?j!f{wp7VFBtftoQn@Ie^4uRICVBL^)*l?HviBw`BQ=I{rdQk*7TRj z9=i9&`_*S|ojS^FUQb@zcJ5~zuQ+|ZXZ++rW?woRZM}FMGnQWN(XS>+^Y#=mCw?Wje>p+=KL3E6p1wo$r#~jg zJFk+7&R5B2N3W2vkIs{+Bje=A+ux8Y=U*dx$8M6bYu}Lu*7G>L5Bud{D1bfK9}4(o z&V8KwId}9yz7BETZS#Q0cu@3mF4)MZZAtXLQ7eI?e*n*xv~pH8@?fKf* z(tU8Xp<@C<-!DdJFqMcmuv%l6Rs$C`Lo-#YV&X9FO~hi69TBNNmhOXGAg7vMF+d@8 zi5RY+S`LiirsJpvZEIkolGi8_Gfil!vufbnuwKfW8f@R^1w~o4OnrndxBs^(xvtzz1Nvh_kiGk5FSVdms-uYw2XD&=a$ z& z(|g)l+D0cD>-)^lZ0Oz5UU{LwQYbWEee;DDyZkVAMzf)*V2kt99-5k|7Mp_I=4#g5 zsaa~Nr00^AdU4o*5wN#Xt!cfLQpN03bL=4R6T3np`@G(6N$i%oXHFcioV#&0z!7W0 z*|mq9)beb`vkiAodCr9WK`56_Bc>Te3r=NFg7K+lk^WO4`?>@2?DXbrh9l?RI^!z^ zCzqZ?Z<{ZP9zHuUo|G$az!z#}nkV3oeY+B;&jSDM=lwj!-T`~#Lim3C{@1xU{wZH5 z?-?6fD<#6o!47xiZtmzPcQDmEYd5EIH&NYuYbtlht#>fhQS5LB)3qaWc)QM#Q{GOF zt;4CFV!mF4I?!UEMd$&v1PN#<&=T|@T817%%Yl}m6=)^uM5};SqSa^(T8q{Jtw9f? M_2?0_0cbt?3#*p4b^rhX diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_2_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_2_0.i3dm deleted file mode 100644 index 319f33ad5b2a510443fee99c8b59580c7c3893e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3640 zcmeHJdu&rx7(cuUOc+dg8KankqLMP&+upu(J9eXE7rJh)bqHjR+xD)zv5)2UmPcw~ zA>bpT{xyxnm|zqQs5nV*#$+{OR1zcsHT)q_Fc>34LIg}qB>J6m&)u$N%Yq4se|VFx z-|u|SbH4L!=TlwD%p`=6uNtAZDW8ha;A8;sQF#$dk!UO!X$(cX!lB5jSTha?UOR4U zjY=`8HG%_09N(%dt=-*uMaKb0$&Mqf?JdD@*Q0HfN|)dsRkus4nq!p~9=8uHqK(0D zs8Z$k3ZM!Fqp>bX5Nl@fb*-S3?uM`WyiFntPOPT;{s!>l954FSChcEn$e*}qllF1> zcsL0*v$?$$9|!ps?rX)DE%JLV*`$r!rUvjQ97h3njDhD|ut^avzi-$k?HI%NEwFin z@5hQa9YHB{D(`Jx z!fb@c)bg(WlS5R#&gmwvoD!M+@As}PU;DUg^=A;~?Afl=J;VC%@1Mjd^5GygcB!$o4$>>f!o77v-2d`$vpC zKdYMI@PZ{}91F2HgAXq)dk!=(e8>8q$%-FeB)50HM)s^ZLk|6Lgv>bcG>I(xlFYoY zmmF_9N2)q@lD6GD$rbxyQm6Hkfmg4P*tD0(g~6-j1bLTazTHROMDLL%=UH-p{nsQv z@EbW)HB5Ft`wn^GuIET!{~KgW$2C&KY7z%FV9_bMJlIaT+rhbub2sNA=cb*PuYCp= zxu59dYk_kfN91ut9!F$xM6byFf%~bw=yNiE;LIO5^B4Ud!_Mz9?EGGqPxN~Yd-(sz z{QX{T?{nc!JB}-xl)SE{)NG2LGn(j;wXCXWaix$_(%^CPBx}-2CN5`_ki-ISUcP@9 zxncZ5{AYLFSxTj2P4t=WvmtbP3PP)+t&t*@^Xw5W;*^qAG+EDSIDi{l!{Ok{ps*?& zYl5rTq-c3Hmj#7T>%j1&<`ZBHFE>YdXpzcZSE@~Zi#~8HSs<2|KF~~bh_9D(G`5rn#(7*uM28y^>Q!}crLL0N4$vUKr7j(KT z@>v51oYs+q4hm{EsV1lmoGSx|!2vhhuA-@OnkUK18Ia=^nwBP&6X220X-1-Wp}Skr zI#gvd3-XUC$Bq-ZOm8l$WcAqAUI-#O1s4?-vM>a>JTw(8i{PSXQE9mxfOKZd=9^$U z)E;IhOANlvgv-r%V9IoRiP z`UQty@Q-vHKRFL$IzWq+pm&W+Zda1*jz1~9SY)Xf&^a^l&vX-Jl zuFLKBxJGa9I%jA(tLsRtD0D9EL}x`Tfi|D9F}^8FVmcCcmY#t>_68|JpH}|e$XwRP z-c`eKB77rpZ}wiy_>@ny?-3h%R!W4EiA`?Gt=!a6Zepr8*H%vDR-(GG)>LkiRc~Ud zquAsorfXAX@=Bd4r@WF(Ta!~g#e6*pO-56ICZVZl8nU73K-16+G!s>!TYzSwS!gz@ cMst8>qq(RC-HL7lszLM6e6#>91e%Zj2DZ+9rvLx| diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_2_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_2_1.i3dm deleted file mode 100644 index a92f7802714dc6dc252d22a62c3fee2c5d9d9cbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6592 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 D-nWIe diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_2_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_2_2.i3dm deleted file mode 100644 index 617b60301fa540013ef20563050811ed3f9cab56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4120 zcmeHKdu&rx7(e4t9B!DLlIgs7yx_-KgNm0ZckpZ)8(!xbQRC{6mZ#sC4*a7 z;ubw(Nimll<0_jJsidYRA}L(9F`mH{my{Oe6;>=-*jH%gb>dQSeu1a2hRsYg+|Im0 zS6`8dw-T8v&+Vyz2%Z8OKnX{scxq~D=#oKvd-{O|B=3#C8pLG`uRCQBKS)*i`vJew z4<2&HAa*l;1z?`pHUPfB_;J8f8Gk+C2N}Ks_z=@c`Pm>k7(W|u7vr}APGkI|fTuJ3 z?OB6(mFesUJW|sEe1!2I|HUBw#<0FFeZBvM+n^^dPOZJ=vO!E{Sl@@Mkbfuhug^yx zE9aa+EMU1co;QdaXQ)2)H~~H1%J_O*2mIkPReka{>Zb*32%-P+oz#;$yf7i1nvftl zmCYTE*K~U5TOF95hC8GAl)t5QlOz8Rh2qn$=ER`p^EpKS&CQP_)*Uxc{?UE)ILEoe zLH#5*CC*s_6u)H$G*h4HNe3P4n`DY-S+=&hE=MTd&~;%>`l(eEpMNnovGcW= z6i3nIMEdoj1o64FYYhJLQ#Zv2pStE){@yaGvu&~$|8V3IjTQWRRl;Ogyqo9;?t7wb z{H_M7f2?hpqwQ!D#gn$DC5{YJ>oTnFatPOa)X#|r7U0;#sT8N|9+{}@T14@W2@7_K zW@`I>YdlduB)N_FfAm!)@!ZKRH13IxzxM2$)J%0&{@#S!1Lag_#`auCM<7Q1eA$qn zh)zqRI!;^q9@Mdl;`>ibbL1ahN%41I)H`;zH>%jN9M9^$O8p#{5950`t--CYe~-(3 z&)~NnI)e*JHe-MA1+0AYIsUq{1)oh@kDbbO+@A6Vo+4btd!PLZUs!Sx=i0a8)t-0p zQqMJF_{IPQVTCIYGzcGTX-wGXEADH z)UHy&$f$`?=1*Y$1m;g*{siVnV15MV$IR@_%-+KEEKJYB^sFqNRn@mLeXCiGXEm$w ztY$TyRkgF(v#7nzZlm@FtiesENLYJ%<=aYg`*sFxXTsG${DJ;y(&*SG8dgj3pt&qYIITQo|pK0V^ zV~SLPGHhlHMPLkLTrgBE5o9cn!;osX7*|y*`>Vqen3tJA5b!EtxshBljTy#%&TPFv zkM+vHFOO%$xPTO?B`YFv43H)eQK0pN2*BrFN)tpbZkD69smjMmgh;l;AlK%M++i_PGw zLV>zaPzoxZraEw9G4TRnMuV{A3RV(|w1pQ)Ei!4B5s*T4>HeBm3cZKjSQi9p7%&KU zW8tr?}S8wRWE~hY1VBTiyufG058#68N?DX`8CXCtISKrV?&8yH? zP}^Kr87`6(uir0Mm4>3e+8{gu-R->*$rm;rYB_ANeOnFx#y{@9W)Q z3?rvE?eXP?$t9C$>f?(i!>1<3lF|jXnr-p&_*(d}&lQWwD#1RtvKP0|9YHl52;Yj| z+ntN%pU{Ew?$IH&l7rdNutqh$o@z2eHB98%T~7&Le~7HVJK<|bFV`@U5v)-Sle@;# zXkVGeC$#U;bTvxk1hab*8iWSJNkT)=P-H;E;0#4~p=2~1jewJkMxs$@G#UeE6dH@h Vp}Wy|IOEU+G!acgDR3sDe*n~I1>67t diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_2_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_2_3.i3dm deleted file mode 100644 index f3e78f1446062f7eb300c84bf4269fbe9a2795e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15240 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 Gn*Rl>=9PN@ diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_2_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_2_4.i3dm deleted file mode 100644 index 6d08f264dd64ce13f9c0c690fa8ea88b11fc856d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12328 zcmeHN3shCr*51Chr73D=sKtCqQ-h$p(945xIDVf`pgcxkkGKcfx*N6-tYcNgWYVtHy%0beB-fG>23y4US#+{1$YhfFD3sMUQw-w`KcuPEQG0GI6(<-t^2 zVu2`M>csONXZThH*zk=gKTi7BS#N8ZIN!;7Tc2FJ}Gp^zTit)~G8JmEyXTjI=)_wWPaa%RR(`}MU;P{b25$LS=2Y}UE9X)-Fo6^@pb0Uq#Cq+ z(_(E8&$IsgMD+)rV?C!_T0HefQT~;3Y46}0>^X3q&W~=7D7Pn0&H+&#NHu8ja^?>p ze~W{n+^qt9h}{Jv`L+0G=5Iv)>g-;epqjO~-62tqB)=9v$8yymzZSRt?%wl6i_fs< zl5bsFyp-L+rL=TQGA6)`{IwUzzpF$8=4U6$^i!v|{HyGGIYImk#yL!QC%Z4rsaIO8 zXZJma{Ms|A#aeysGiNf(n?kWC*t0s1zH7pEh;lLE2@ID$06)uO3yAY3`|fB$-&5u6 zyW_D6aBKG6(U$zZ){C;2V%6_s{*w(pBnS`j!=5}m0}s!@!!z*k3_Lso|BW-CyfaR* z*Ll%_xw}Ykeuq&}agU&}%CEKD=pUZ*mG`|HBhml8FkE@2;H+2W)QGz`^O-E(E2&vK{q_ec{es3l7`kp{Qj)E z%8uL!#Pe2P_KsTRL|nB&rjj?N3Dz0+$arY#`el=Kz8ACmEBmeli zc;&;YUaa3-zqPXSn{F7p=XzC`c5^Utx}SVjIeWVs;$!=U!seEDk$>&UIHk$mFA>-C zm|?(N3&tu}vy}PIFUnWf-sVylCHhcDjD4o|GVhWDqmZ*dHce?ic@owZ@RM5!xws#3 z(<@oZzBiU(?AxupK2MXl$(USpKpIb$)`hLUTJkb^Iy|I}*Z1gwzXSO4MR~qu`Hy@|m z4IhM>zdD;b8a}@TwTuZOQSh<$Q_^u zwGVxCO&xP!5x*F!=6Vxt~?7?{ycf3%2~fU67s7F$lonxLBpH5cwdejdDVNj zQ5y2+G)#ux8A~y5M$k;h_1skSXB|7|IJ+?#amf5sSTJcOa+XGQhg$AuQS;k#n>Q^k z;^#apdc0DYb{h3>To|Y^E+>|GTYo$XIj83@a4fWVFm~;pEal?tR+y_!JiCJv+hJ`3XZ#c} zq%)7*y7fi)Y;yyQ6|?djr!)DTy(q?mE^!Oy+OWMP$nKt~X*!;z7@xj_=eEV2OjtWe zK#u?DJr2kDSCBI=@(E>bZa>rt+^3@zRcF>p2W&5Bhxme@N4YimZRA5jmNN6zDaem4%Tflsyc6^0TxbK$ z2l0D!Vc~b)4RNuUt43H8r93bgb++&A4bY_-;?{4BfufjH)T~nEQL7IgCi$l17VVZL7?gWfYTl|%GINF=EmoZ_Bg1 zhDlq;DBTK1dDV3-Dr4uRv^VlMEZv-6c!A&FvlGX`%`;)>Uomg3LpLoEYdAFQHOJbM zyeGRq?4qQ$^hbZ2^^KIqseQ4A79T9}CT`--oEBT^E1!76(7$p^P34(dFCqV}4;m{6 zxA8tW9y#aDxx&x?(3mW+d^#2TDfjNL466GW#(wC~D+gED5jQHxf}3-iA*Xqs4F>fx zpyr#SEZ*x^_;+0Bkk*hpn?Hxc-s%ONLY_v>)`l_4;gBL+SJx%+%CZD6_F;c)mXcAM z=Y9W)d`LP`2J>3oggw7elR>mGJ1bY^dIR6ZkcH9qdPzz_|UI^xo z&O@Ig3t?_iDcrhs7B*cihC%v#SY7%(YKh6gKqK}}o*#yWA`y6UED22Xzx5JMG<=}d|7$%E{!EBiXHM`t_+g%nyTjONt z(clU^dUP`k+x0q}XL=idD2CV;ry*(fFVJb(r||xi39vYLBba{p0`@s}!Im}C;Lw-9 zz=Y|`AS7lRw2RvW9bdlz9~kz-&MU7$$G5^4G4_jD(zB7C zjr44!XCplu>DjSvtBLo8S*nls#cDC|dCACi=$u(46VD@=eU|5=^Jb;<#w^b#@$+Vt z_dm-t0eaxZ`yFRGTEY{1c z&yVY3W7KCB>tz=8B|YztWZ?VAEY{5|_K8`pYvB9H#$4CH_mPdcu7U3(8*^O)+sDWb zx+vFZSBl3S<4%ZCpO4|uvCiZKw<|fyl^WxUV^5;Lnx{{kD?ZAZFr1a9ft9IJLw_Kw zjNYHgskq=jvx5KST$=9x@3cX)=!)Tgsuddb|CHokU*}I*^q-QXGQAER?MimXut)Z9 zpfXW^HIAm8|0b-=iYk__OypmUQ}IeF_Wp%yuS{tbFG}P6gCf7D|3R(a3slDQCnSl? ze472l#6|S1%Km4TEeb+WH9_bd9vYIabEe=sm+86~SAr|q=}Ao1b=Gwc4Gs=`A<)<> zIHD(eg0Lq^iaRlZO&I<4I`;ZxN;D(07c~i`us>tdbqR^XT`JN?X0ktWeWP?;bgVmW zc(RLy4>qugc&8`XJw_e)>pSQx_;l1xXt9>e{E?X*(sl8!lvwqRQB{~hEFfK%lUM*$splaVhRbL8qCk%H-s~onjc(xc_XET<|o}|2t~3<+KQFa*&_yT4CnYAh5k{s%`XT z!@mFa>t5>qJ-VA7l-OvqRkXf}QtSOKY}@g3FSY9E==*hlYL({n;4bhsCq*Uqb$Og| zaqj4_#MGGB1hxhEqOUW>)jcsTF}bHR8V~Ydy}wP5KL&%{sJ9#KznwT*Im!3}*b&QQ zXP0ksm}@xh@x6v?clmcaY?0V<)!hhoCB%5xsg&3|&K_rSjQUQq$!xcp?(N?1g4vPN zhW@4%&Q7j+5)Joh8P&tb3!|bmiLGW^W@P3RcG5`+Poo|D4aS+WS8~uFtCTDyDH2p{h`geX0o6g&Kk=)MTF;!Xv_? rLM@>-`#dTF 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_2_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_2_6.i3dm deleted file mode 100644 index 0788aacaa60e1e6d3219e4e316abdbb63b824428..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11896 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_2_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_2_8.i3dm deleted file mode 100644 index 2b4374f92fef6f3e52055a285853b3b35b2ec8b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14640 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_3_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_3_2.i3dm deleted file mode 100644 index d443cd59e72f6f1e43452a6caf2f8b5c65626ff5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15584 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 PdWu&R9*X+-yrTGD>Yk6) diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_3_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_3_3.i3dm deleted file mode 100644 index db8e5f1bc7890df639718affc9fd6b89939a0555..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22344 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 RqNt*%s;H*;6hBoJ{{>aFN^}4K diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_3_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_3_4.i3dm deleted file mode 100644 index aca0d8f6274cf28973648e8545de72fbb586b404..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8368 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_3_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_3_5.i3dm deleted file mode 100644 index 80c237669d2b84a906f30df9d0cb09d0cc5ff90d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10688 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 H9k_o2WJOd> diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_3_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_3_6.i3dm deleted file mode 100644 index f18deb7b6f5388b3b8b975ceaac89400edc5fcb6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13816 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_3_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_3_7.i3dm deleted file mode 100644 index bcfec51c5d90862cb2a00af9bd1b6f743a22dc61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21256 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_4_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_4_3.i3dm deleted file mode 100644 index 6a2c0667aa6382efc769cf9378946406596a3540..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16968 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_4_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_4_6.i3dm deleted file mode 100644 index c2c530fe9f145ecc25a3857ccdc732ee439fcfa4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17528 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 eG%l0*$j@1ci={QMC=byB6 zyriY$B`x!-V{wyv=2yotzdDZj)v>zL>Dl~~mf7hYSUlv9h{bhGOrSxc_1x| zL(l4g+_O0JtRBcc`eSuL?$MtP{jvHW=NqkW(o%nV4O{1=rS_zy@sO6S8%bB_n$?{=JS~_o)%l+a_I*rWl&P0c$L11@2H1I4-XF(b;!7)$Y2zX8X7M7cqA|qJBbdAB63Avv0Jgzz+hsL5EIWj3Q7tI zF~$3bn?_M5|MVq!J4J9zRBTMNDLTQH7;c1EvO(a#RQKEplmVc_y|N7N1-0rL8EJUgEPi-HUR|C4FvT@tMe$*23bw zMP_MVeP)r1?W>nX*V2m5MaKI|GAr3X$W=*JSyn|>RVF7_71;-}YO?CG8sw@bt0}7` ht1YWTu39o{SzTE@S$%TVl{Jtxlr@qyCRan*{{g1P$Xx&c diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_4_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_4_7.i3dm deleted file mode 100644 index 1802362071ef9472fa914c6d2ba325b48d1a2bd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11808 zcmeI230M?YwtyQ^D`qsffXg#P(=O47+H^PFT_}Bm8o{s$-UySd~`0~Dy@2i}@PMz(Z zd+Lg&th$7BHE&R=BGDm@mcr4h{zbs5gE)<*s^jgd4CNSq;A1`dMb6_lmy8}Z0RXT zwTO#)2~tDi(Y*z!ZxFY4OBSSN66cjkf;6)?=i8`nLi%j0Ae|23dhZlLvJ!VhHjw=? zak^U_5v0l8+{TYHZpLkQIAdMoMBIFFxS05}$^GJUg z^|MHyi~9D&Q;{Qxe?V?c_M4Goi5DR^C!2%F38a5#h#WPnGuKL73;d#=A zI27v-Bwmka&M@MMI2)D3kC4w0Z^Hf@Ag+hJgShBDeD7$6uOP<}*NhURvBY^etEb66 zSQMlaw02j%fooY#`XR^@h`;YFNDYVs&4Toh_F@#WC+Q~x#w2|Jp2_V=KPO0#esVJE zcMzXIUQ7Hn=GsEOm00IqshCB1nCvxd_24SoiSa`OxJVO_0NdrxTkd~ zmn*xzce8K?s! zfq(BA*yY_CUfI<}R_5SV>>0<6qY3QK$#I!89k)B#7&j_tB*#RBGMhG|T7fRKE7RX> zp9$^8eeFEUH!1efjatj& zck_K2`Y;_j&)LXocE9Kbp3|e4eVJviqcYCQxZRd&=CSAUSj<%ibLA*gA+s<3lTBVW zbvomA?{<<`6?9YweM21^faXZGEy@?lxoTDGq9oAx-q z`pC*+-t_@!+~sZN+hFfS$K{dnjE9DGkROih#BAQpPJus^{J>(0m38EWzw68N#qKt9 z(>cQ!-z+~KP@bL7_{asVT;9aUIA~HXXySj*e6LN-lWU~bW}LA($Wi5K=l0WPZCV+? z*Zbg{g}aQ4qTcWP^RTJb<(VISioMkuD2&7%0Em!%D7pBXnCKf z8}q&EG1GjvCx3TVe(46O>-m1IQ4IlDww%>a%PY-1?mvbz`=~z!$#-^SHuU$Ly%{8y*vY3sd-*Akt;PIQ7(%@qM?JQ2MkL7?Z$N8Bs z#5Qe1|3`d{svYk^!PZ)==GOO{!TG~MjN3ZL@2-<4=y>1%6&T-N`lj-;A zZkp$A7zE0`%dsi)yi0uEKHShi&aK~v*=%TM4_NXcpMhx`3Se-nPV7wE?p*-4gr#gP z?|WI{la2dWoijE|aB`|Q^BwM16RtPodwOu8ufy;LUsv&pMV$k#@tDnHddhF^dY#qq z`YJDZ*Rn9iD-yjOJ~v0QICJm#$`RE~Oz-jSD{v>hKjZRK17O1D>dab2W_XQojDle-{)y;((A_=D`dKu!i{naPJM$gYVh9XBzl_byWEG3>gw4_Z`h1&jM^_-@t-2p&`d z7Y}WR>JJuxeE%9G%-jOG_r8W>Y4>1C%wi~*Tm{=UR6$nR0l4$GPhjf(RdBIlHjG|2 z3nJ&thEt>0fZOg$=$G>uEPZ=3%+s8JZ@tQ4Kz1>V3;z)^7Jm*GTde@^@HvpOeAO|1HsN!){MLBzw9kRGYNH?}W(=$d zIRgoo=EK>og)n3P8E99!5B5J=4YB2m!L!9qXrI0oc6@pW=9m5vj^tj2onBYrs8<;z z2dst7trkG7LwjJf+sClFK!*91Cn55OgD^B{C)_VS20h=p4W2v8V0zg@cy(O~^j~xg zKAt!U&NUnhLie+9XX!*}x#j}==b%ccYKt+qd!Hj0OW@CN z7$&Y93u?dd@Ok-Ni0QZpihjHY2c$~qcKa5rt0;%On@1p8w-BzKKM%WaEQ9rj4nWkB zQLwk50>+&D5bAgO3OYUd7+&3e9qejrg4%ze+E1r77}P#Ynn;RV8huG}z0Sb(I@0Tm zTJ~IL)RXih$>ZsaM!F||Bl(*&TyG+OlaB5wj)~%!D2|EZm?(~k;)oPSZIIPo#Jv#S$m8iXJf2>|q}TE|dM(A#kzPmkI-Xar z<9YQuve%Klp4Y9{Q$9WS7yY=ONc$rixW8!RaYZAKBT}D4>WfJI5UC#`^+Tk7hM5Sl zppN!Y$61D2vTfERYetfCo<+nb$7W?%Em?7voFq#co-8j?(<{xA9v7RD@PgC6$R)na z^~=NcoFnj@=D)S>yOi&OFS5%2qqHw_i7#{g@+d*i4QTh#+?^2oz{+fOJMf3mp z!~b>aUlZWp*~VpST=2Kvk?6dnw8$RS@ZaMKYpPT~xvNw?Bf>)M>ey`dt({$+WXZ5( z#o98n)c)$OVId)b-2$~iA(1`s7rvpEtZZv$20qYs(5UgVx$Jl}#t-y}vhmkbyE-E? z!J?2RIuCz&b>7+4@yXV-ge(iX5A?+c>9Mvf>oDb}gT_bml&ih#16Ou6MtyXik6oQ^ z$xc?j6jTBuvI6Yt!CBUHtIdkPKC{YVZJ07H$EMuc>61eI_R#gqL*-*YYes@KUa`S- zrQ>4M{(4rfCCeI{MoBVa(^0SPt)x|w#>S&Zc4ih&6ql2jXvyklwdArO|K(#wA9Z|Y z`rynAONK3S_+Sj;=d0Bz%A5@R)=_6rQYmfG>Xfxum3C>6Y|NH@#26Tky~j@^U6SA$ zo|yzAQxYv(kF)nmYn3)Sv$4Ma{GZ3E`*rWCdsbqt$@H}KJuS7SgCA}?yB?=j6z!e= zc`~(P7Hea>z}q}HE~~f27Mqr4jStVvNlMPZEnpvb#%5c(W~OCk^@xpUgFH~v!K7h7 zzP_SXgMUB$dE#j06vpX*BbJA=%QrdPlEC)(al>7A`5$)J&xp%ab|b`+kz~WEG~&0d zw%DvB|LZ# z<)7f`=Xw6WY$5x1TkiNQexA7h>RO!f75S{Xf3{K9s$4*qlbzD3ca_d(igdE_+hG2v B{rms` diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_4_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_4_8.i3dm deleted file mode 100644 index 96c02de2049ec2f81352a3b210306d01dca5ef04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12616 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 UXac$mWIURPCZW4g0mvluH@h&brvLx| diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_5_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_5_2.i3dm deleted file mode 100644 index ac6927cd4a0f95a69f4342870d63d89dcab7d8b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10560 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 SjHkJ_Ts!U=?pZS0asLBsXoBGY diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_5_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_5_3.i3dm deleted file mode 100644 index 5f63e2dfd1c959e4c67ba4698aa890c79574de57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15288 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_5_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_5_4.i3dm deleted file mode 100644 index 402cd0b59c7c88447d6dece92a92e68f30143091..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5432 zcmeHLdvH|M89yO`D`-R-(3a_gxn-ITgn0Lv-H3Y(F(fx6yO7;vWYQ4!akE*w*$w+3 zrptziIuPY$m>@nPI!=ocbPB~%jpj-UX<7#%ksuazptQA81jPqJEA%_(o}1hzu>l#U z|9EH4e!ug5zwdR*-qEGOm-Ra7+=NCAuOA73T zt|C6yWXj?fI-H_QblCaa1n+5#NsgMDs1)OKg=7|Qca)Xpl~k869Bwq5HRdw0u*fx> zp)=?S#hIU1QZT}1wGdlDp3_wgDO^P~Lo5=Pk~3y}v78enU0*S&JH43`4+sjL4)|S$ zk3YhRm5j4w3n$*h@E*Vw4EF-w!}yaP9pVf;#)&@0sehakYZy*E%ZXN?cl_t3=aTS<9`e|mBrNJz8#!+p7DF{uS zSB76{;>1#hO-W8%%&=t>CoW|;y@eAuj)Jv)Y4KuMhjg|M+BMhW(kD6bE>^Pv_D-$; zAm9Yk`(UpT!{j{tl;O*O)%`sSy{l_gALGO%<8(xzFNU+@oLI!_Prx~x$1pjI%F~Qh zgwVJ6+co+Boq>SaAiw*DStfh1c8X{-rzPmwyij_=_S6Z9>hG<*#g-`~ss0ah&e$fm zd#K)@Ia}_nwoz=%xKrNz$V`gUUU>cXjLs&?zi{`@<>Hxt>>}~Eytm$V^l+SVD*Bsb zv1s4zM1S()N_l5X$y|cpFFW0Zzvrc#_53m0j3s`G`wqr%+ly^9PW>Hz`JJuDDDJk$ z@yTGTjrfZ9|Gn#6n~(CBo?3}tFYKo=|B{xC4c)g<{nn1px;}htDa8c~a`AUJ--?Ov z4XHKq>ix%Q%*C&*zRhV-N%>0ryFYDumDIei>cg(KH@crl8bJD;$TII}M6<;!U~R9_)g+rk5l6lb+wz;6v~!}BsvV{36cPWfmH z{4*YV!i1QKGd=YcW>Q`XCFO?8;-2OyViE&sUL5{$I9=;!av@}F8&n$ zzU~#gZuf&YfBk+u@ZdxE<@lqxGxH3tyk|3RK7Jl&Y(0#huW7}1wCuzWuUmzW_Uy!t zy%+KNijDZsv)AHPFCD%;%78j6usG?NDG@=7|+V$SsCA|S9n&&x3aia=4Vy> zOoHNP5)?m^p!k`Mirs8d>}C_wvpUQcrCzf|sXs?h_&GWyPL58QTMcGfht)vyn+2s$ zv!JK`AgRAuFshRBVNUrLLGc4g>jp{VS_C7l3na}4lGX>3_6w562T9|Cq;WyAxJDLF z>C+-8eOj!_I)K#kRav}8S}sLn0e>LmC;RC1*1991fE4jaalaIV&aOn`ST02Z{!z(; zrUgN%&f^aGMrB<|l4}ldB}u+H&NY`m@2hv@nqz-+ZGXMUnuav+nqz&v03*Ey5A{gX zm1zD)OI2{!Bac7mnm-QyrZi7L=$%xAik%L7f_F#hB{aeNrH~YH$HEalm(O>Ul;qu+ zr!OpV&4-r@Ud3o290G$rTj1eB8TA4gZmtYP;Wbb2p|DRPNT^D}mw-A;@ZQ=$&=--w zy;28;I(IA*xQ9%#g)Cu|F-L3AVl5f;RmrRbUnfOt$rYLe21pA?@b!^ET_6^KuOV8Q zI|eB|@fevV*j<~?)oHKfdgv+;@&&wv1AWy&GkmU*mJ44*?jTDNa@T>LFC}S7Qnwd8 zqTz^=$P=%rks=iVseuOhuUmc=?+w@0heJ{*=4z~mASRvOK$P(i{0Lw$lT>6`^aj#P zfGn2)C`P&TdY@MYYY)F2Xp%q=Vqpm$!TC$94HoESEA491t3i1tOCDvOk zqxCmhY9ZSM+fK(&YND8>+$d+9;06tXP>eRMjjUa;z|gC~x4&cdh;aKw^uc9l&olYDfKhZbDB%OiH!lmqRO z-6)Yl{urFf96fBGJK`rl@)(R(vtelWMmWQf)4C4Ea>B_aCs8*nmYy6wr7%{MCb8LQ zNmeD-z{@^Qw3B}$*>g2_sAgI9f^Up5@YTdsqh5{ASoujzEoU&t7nF$Fw)UYXbSoc%78HiO-0kt&FB^w)6jna D*cx7L diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_5_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_5_5.i3dm deleted file mode 100644 index d522c6c929066e41a4a2fe37eb5ed64e22f72c2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4784 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_5_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_5_8.i3dm deleted file mode 100644 index 2c53ec68ed8e90d31689783c38d0f01d718040b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4608 zcmeHLYitx%6dphs6e<)fh%p66Q6Q!6&UUxcLic8^1uiVRu)DS>R^0ARw-fhKW~UWc zmR1x+MIKRkiD`vkL@8GA0UEl|*cdIM_=t!KzCc81LVV#XdhVS&?N|s%BL2}y?w;?S zbME=hIrmO8w~8$s>&3w{nB$%Vv7;~iTtE~YNAxKHd;9^Hr?kvJ+g;|F6qqa&7ufQI z>0Un$V6R6gP6<`>G}&8Sos>19*pki@JYL^4mwWb&(|a22R!6tlhbK)A^z^V=Y$CY$ zOI_}=Q*8DE32bF9e_%FH1SV4kO|6sDBS-Fdh{tov_f8>syC3H99Sk1>T+MLBMjo&6 z={|LU=Q8{Z;26V?J;CGUUfpNKQ#@WUUB~TD^Y~?^AGU?Z+Zk?Y;qerPMag### z|HPODNPE=MM(WpAUxC&vo{}MW)vR@GdxmNh_gyqIQ@*Yr_3yLCS2A{U1JzH6Y;<<4 zSE>G<`rOPT12NV6>+3VOX!kowoJEN@ot;{x#q=d$A~OKNV?x$N7=P^=ms&(=EqPGG{L;U)+o?*!m^%E03en*P76gfp4R=sbwht z=a10ZosDSa$))J0QJWKv?XNSC68Nh^~g zlQt%4Jcp%_+Cfq~NNNXZ*eM^(DIZA62a@uEql$3na}8q)n*I6RPBT zIjJcTB_1Jt_SVa)5`hN!Lp4D)uE=VYTo;j}|Da}SRE|{zir?!=3Be@&m`({1 zIWDU~EujjX7_*cRs!^h0RfgrWtYC-*HC35QCi#{;%W1|qv%!qb&Y-VM=cR<0oU9>N0TLJ> zO&}%Is!B}J6nGtJWxMM*kceW#FA~=igCtIPio>8)aWkgZLY0Sr^#9{8dXBRL|vpN z4qHI4aKWTpnusRUiNO#($g?c@l7&82YmsOv5{tS{9IKp#F&4lPOT*dKH`ynL=^l4C z+`P-D?6AEM$|bwumg5l(PGzA8+ZR+L#>J?60zr8?kA1(8%SSxHheB|&v!0Hd?L@5cb5;Tl?=p& zfemTs&C;+DX<%YE=4MIs=0R+yn-je;%yt748^MM&Fqs=VL-w>8dLnxc!`G0+PB5GI z;(Bv^VDy52*IdZ;<#-qua{aiAxQn^|FfQT-a9P|X+&~yv+#oI+{>3u{MmBdTm%|O^ JhQY|;{sj1mbcX-{ diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_5_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_5_9.i3dm deleted file mode 100644 index c2bbdb6cf3fcbd6606437dffe25ce3ab3593dc7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2088 zcmb7E&2Q665TDWqg~!L^^T7$B6$cKXa_lBea*6Wt(l#hfP-5VTDpZcMO%^3~WjhpP zMXK~TaN@v;6KBL90Z3fAz?lO#gy4UGnYFiZL#5J{#xpxJJHMR|uQ0m9834F;6yPQD zhe&-STBHwIwcavI^>U@PUaQoX%@tNKQtW1%^C;krjSZg%te}lktlns@ zmTK#_ZcZDG?8MrvF0Yu=4vRWEw92JgW!jR~a%ifQTIM>2H&x>VU1;sOxj>6mdR?_e8d+u)=sewf581f^n}wRzn0y% zJV$V^!$&B}{93FANDo-bSn4f?#su!_Eo&e*D0^6^?BbKEW`No$Ys(rOnnyDtV^kK^&RG#Z9h z;E9K{nb%U<9&|lvNXkiP)VIf}kPW%trr6HjIah z71GHWq_GiBSJ>o(c?~fcR#=eN<({wxF^FRgQO{NhmOw2VPx!7E0d+I0+pl?^o#(uzhF1J^HdqHdS8Em_x2{b9LtIsqvxmkhi zLR99>j@v2+@~=wk7u62RGo+_*h{f#_cL$^W|3y#4>$}*U=Hap9ED@>CmcsQ1}ESooPxiQPQqz8183nJ Q(iu1p7vOKWh;#vd0*;j7^Z)<= diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_6_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_6_3.i3dm deleted file mode 100644 index 0a16eb6a530af6d9646eaaad62c50451dc70dcc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3488 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_0_6_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_0_6_6.i3dm deleted file mode 100644 index 266499f99e5edfedf7e27523a2c71a736565c086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5848 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_0_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_0_3.i3dm deleted file mode 100644 index 80c9c0d811f762b456c691694fc91de214a5bfc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10528 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 BKT7}r diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_0_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_0_4.i3dm deleted file mode 100644 index bad28f3abd6ed830381ab89376f9bca8b8f1d033..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12168 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_0_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_0_5.i3dm deleted file mode 100644 index 48fc4936aec853f03b9ae98370142a8c7858c94e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20000 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 RrKqE*tEi`_kDt1V{{cSIzhVFY diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_0_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_0_6.i3dm deleted file mode 100644 index 06632f48a8d0d9baef52198f3af166a60bb8b34d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10288 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 RS98HzXeG23+Thhn_zxh*1GoSH diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_0_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_0_8.i3dm deleted file mode 100644 index c7a4eb661b0280ec71c55b48e62df0ad03b49b97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2528 zcmcIl&2Jk;6rVKh(m+ZHX(-2PE*$Dc>)pg(5?LA7Nw#wA$aX&@6QZ>}aTe^gwYv_D ztW|OV1Og-^Bsd^IAV8I%NWck+l1mYALhYfaBFd!#A>}{dzWgWwVf{ zsUEGa`>e3C;xV75)F49hh2laww|staP#M)i>Y_O}Ummp3rqH64N$0YIDm{j(Y`Rol zh5+Sx?%#L2EI0*U(p1dg{Tt`-+}(M_{8ZqXImP@~;9oO}*%tWovx?~n{07)u7Wm|% zV!kQx_9eyqK;XNUVqO*a^b3mlqre!i|7{u)5pwYO`SFddKW<-r8e?uWo-#(iO!N33 zo`2=qEq8-s`==8|tF*}Rck9)yf4|wDLHkPZ_xGop&ut-o^5#b~U)^yz{_BgoGvB^m z@9+JgV*FwbbNR)!u`PC`$mQ3@KQZ3^`8DIci?@tRe_l5ZH(xS7d-)y1`0Z=s#kN^)6hLw0 zsz+<9cC+p>*j|Z(qGkE6eHj;%YDC?G&V&_V95x2|QV{9U7V}o|s-t6o`~W@LaqX7v z+t40hU?hQhLFI_6?y zxl}+tx8?6px(Kz0H!c(ea@aQrcoso4o{y|OmKMt>qw%`${OpFlkj?1(1=bRYz4F^D zv^qHrZRfNb8U<(M&tPaYvwYr#ewjN}cY*mVP~g~9B3KGuh0Feona8J9 zd=K(pu*u)s{>J_AgZMq@vq=BQ_dEBF4d#l3|0_rmOD^SME_KKyM!j5zIm*L?y1mvY zmlW1Zj5@@UON?tNlYCGo<;Vv~+DeXk#9}=}4v}G?A##`;Aqsg2=m7%g5PoT?A(YS*THwH;dS^FU@A@y6=*l%tvc*5aHsugmW^F$^E9_lscbyU~ zra2&}P%0rV93q4e7k(uEN)e)hNYgr@)q!yVKD@bAId$d70Q)#A(JiFa@oRiWre09Qkbq5%UVS% z7HG<&^-Y%*TP=sVG$jqfv`{SN)4AFctCPxz98j0E<(0~$g&akTaweV2PO6kRs

    F zr3L{iE8M?p_u1eSd{_G+?WcFXJ%{I>-w0{H`M6?-H1*w){K-~GyY%)5-*iLT(3fw6 z{p&vV0DtZ8UA_>~zVvY#{51W&FFGM@)yEib^lhmE5%T}zD>tiW9=^TFV`}+ppZ)sx zMUHoVsj0d$`~uIiJ(SgFk0!W$_3>MeT;6$(B^@^oFw(&8?&(HVN zhraue<2Q`#^_4#RASS&8#3RnVIJR0#? zl2*gC&Ni6i8g0XDqt~3#Sa-X+M+M)rY{L718}e1f00yq z6G9)hJC&tFkk_pcAwL`@V64!pr^ z$FNLL$O{sMR(Bd;3@^$@4qV)$rqyJKr0M|DB32%4tQ(!C&0zal1QcD}wT)+Ru^@$| zedu&h5yU}bkXHv`k9L`}j!zpM1LOzrXwNpfhHF4!_{nq^Le~2(Eh>&?nQmpX2To!USwUgng#0UU425Zt6}*dq>5>4s+?LOsbO~|~Gc6DV?l3h7c=m(H z{5~T0m|9Guh{p3i{_Tc7pUo)K0?Wzde)roiw6qX|vUA!Eje^tSHyIkubeC6Ql;&RD z&NElbKWTC;^|zy2CP7$Fi6725;mwX(1``FA`alrA2vh+;RTReK%qQwAcW8 zcU0sOYw{ZJCOlZ>Jr#~6;JR3i95dT4bY((@^67RP-=nhsa@|8FB|XLPF$Dpd;iiGE43zM}cO^J!FpDOO64}k$G~Q+(+&QI!;cI MljH&NAkazj4+eivg8%>k diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_1_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_1_0.i3dm deleted file mode 100644 index 5e242e2e03d66fb5fac147da2d134c9749efefb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1944 zcma)6&2G~`5Z<=5r9goKg(GNB9Ma0M^M8rbkhZDPq)JRLRj8a~o@RnTkXF(As^NW`2_qWkOG7c+I?EB zxAaoITxp%xD)j^XkQR~&deUfB^=hL|3qI{!d92azyUe2nB}mYEqj_AaoxeVbJCo|n z+pHcO>hS|GfEiBnqCsu8UvPk3YVC zc7LMj+w<>5pYk3poYAzF&1GpjK|Ab{xgIyUWukqLjZHS7?cl}aF$enoGMv3$BjooA zAxEu7-KT~tZs}8#Sxz*!cjDdR%;WD(466G?78-ozUGKOHM z#|Vp{Mi)-Fwle|hjQf4&oO1S7B>88J60~a%N4CW*Prn*Ll5|ql&^WeW%d`waVOvxU z@4~TN3P_%a6)RL~LhoTYBOtK13qc@r2%?Jn$lhaXv5gW<^nLo8L7!I2+9F^zmz#IL zdCjOxir^ILFL01_R!0+GF#=f)R+%%WzrQ9@x&l(G`n) zrtIbv@g$RZRmrRQ>BfaO57Wp4uwnu1?qtYK))OCMILKR9wDEVPR1b;7lqPw1fBXvF}yu(?1A& z`Nf&CNWK>TnMwR2c~LIR5{-%LQQtgy5x&S5sTXy8{`(xBMJk3QWyz&I%B2svl+iEs zC`Wsg&^J#WSOi0Es+$TF3<+KM>ffQ@&IU)JS1D>5qS)>MV^pt@{~LS+9rPiM~_PF+oHoIXtLrzY~ zYR?cYR!YfCj89E!zb2WZ=~_~JX4X^)k(I(C_`T&yFe2hwEiZpO?b9(d@6TuPa&@VO zx6b6{c*Nh$=H)3Rn!mA%m&>PXct$lZk3>9ZE-z0)yaPB-AYQ(Jm-nN-6JiAs9|61@ z@t1}iR@U+IR`ma}o|k#V3jp^-occRn{?4W4&;|Uf5xW4NL>#r0mm3kMy~xW45hpF= z<)N6{`=Ik4*46M5FBf3Fs{t1wJ_4ML$oc3sUT#6ZjbZfco z1$+zrlOUfa|Jn}kf#fAk43y}LVN@KX2b=6ry$-6=kLHg;{ZR7 z_-q9){{lHj0ry6n40sxzLwCSF#P0-oISui7sH+NVv;$7ZGdvD;y@5FLd0uWU*6Lj~ zpO-5ThXUS>xON^duR%N!deIm6hS%_N9qPA1jgKR40i1+6ybgRT;(tMI$%v1@{hWsU z%ite^{u_Yx{{Fj?m;0grZ;*d3o=FR6>b1KU^KvF~=zBnWRTCX?+)wzYGx`6!19in^ z>Y?lJHq+kUIP4|8OluilbS8r&4e!a`A0_;?=H!Sxm2y_DlbT-LRmJev;jNCWPSY7@ zL$^rv*@ifVE!(O{VR}LwR0s#5F8_$C@R z=g2b0DpJn;+4H8W$#3stI;Xx0knPV{9h6hKWWM85VJ73OC?`!*w`(~#pPZ=vGd6_z zPwslt@sD}WF?=fIkfTkQ#_*ocXQ&4%YMEx-@Jtf=;iD?mY>L?#x8YJH!^axic80ZS znxQ*7lZ{VIVg8!j+3MicMJ$Kj!@TOq$KGIg`&Z$P`tl&-WYipuYj|DLnNvSbT~^+Q zQ2kk3BOO9^1>=-#e!p4#qle*^zr?Cru17K4I47<-p+P&_?pG_w9?L+c-|v-PYX39i zna*!_Cacdr6~XvZ4pgZlUKZH7J#R`T!w+eG=lTps-hw%de`S4;qy<7*y~VT4>VSGH z^UrkUljQ?0GyQK%Q`7?qgP7)tiEEoa61Cd*pARKh!zM8PyFt+r7E!_Qfy4z(-FzBn z%qJnwZ2extiCt=%CpBq(Sv@e|XpNf1_}Sy))wM^TX8POK%v4j(*06j&e63W?p5M%H z!@7RzsZnW+vt{I|rswJ_+1|_9qsXMc4`cq{52#YF_T0qom`6F+eEPJ)IA7PSB4gGr zC;a$%8Wb6Kk6oVdG=gVHFq<4wem~Sb>m4={oY*CzwiRt zzi2KgTlWF^X8SRctDGcHwp}DShZm5t{%c6{goEU-#TUq;Z?}=nVaLgs#S6*B9Xm+q zBdbWxo1c*_Pc0&O>t7)k*X$!#YFf!}Vos2AD>sr2nJr{a-9hqFSR-lZ{3o*b#Ab3` ztRo>gOG$XdEi(VYEz8vh;8(2?(wNA$GE0wwP?u zLZo0eTPzZ_7L%3D!CG)`MQcNAM;oJAn+dIm7Ikc9oTH8nb!@0(LmeCH*ipxhI(F2t zqmCW(vZJ0I_3Wr;M?E|0#h^|M>cnU|qNrO+9Pr|w-tXsmmC9GS*x+Sbz!n!4_Thi*XMr-{7%i@D&@vYHX zJg_W3u(+=E73M5IYqZu6ur?t#Qpi(gC_cZd$mK4g_afDmU+gb+`uxzt(Itw{r?|_U z&XNc-tE!{N$WK;El!)n`>53ON-!ts2TO zVx!JoAZ}+V_=T}FEDh?+haEnTR|}L^URbDjb6m@pCs zz`q6REO*2Co2@hyJr>DKYjM%zG6C{4E_=)qp8;nN&yWm3poRwq0c8v#v3m5gr$vWF%?}DJ+3okL?_QxzQ5I-B8+Sre#|ZsT`_Po;^s_E#$6S`@9jo}A zB_*!>3{QDcu^U>z9)UT1N}{L4;~nkHXBT<0DavkQFZdTNnPQ}v+cyp?M;UzsT(KbB zU9HI(N&)NfodY*^xkHC7HYk^NBUN!1`QcXDBxs-0TSVVq=I9u!`A+vbY=$dm+j$GR_aEF(zt{i( diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_1_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_1_2.i3dm deleted file mode 100644 index a663ad6ffde6ca4dccc400dc32fa4416603ba69f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3712 zcmeHJZEO@p7@k7A&;mt3G%<>GF)=1b-Q8=iy+b;dvn@-GE8d-;)~4)Tx9!S(vA3m# zqaT=(vV6YWShV1kpwkHNg-32pCLkAQq$Xow?cexVAQp{&bVM z_nGJYoOfn#N3+(4vJgU@`3M~X*`1Bhu}J{nA$**d-Cke0+gahQb5*#jd`ozTWVi6k ztG%*Mu6FZ|IPYI)sMQS(G1cH5LYswmS9_L~yXu}^o~g7Jl@6;ta@7)FriIOJ0}HRS z+*OgO5{057&{dRseRYt*w}d7z^k%iKprGLD%baY9URp}(zO|i`_b~j%CQiN{PVw6S zUt{>w9!}mHN$~>U1jCoRIr#*``W8;kV{z)Xa`H-sKZjho3||ME-c5yPtaQt;nzQF3g`5GsG!SG`z zfoC>*PH=K3!)Jm2nc*#fe_(hX*c@T_!f{T1iQ&z_zt8aQK2E;KFzIRP>BJ_26Z1c& z&G>hHXqwVrwklgol2DfEedqS&bx@2w2tVFcOYthlkwo@SQQGgR_dlD+D_l?U)vmVW zm6H4f@jaHkHhI2xYZ<|p_dS*L+?Y*$xxH)f_D2p=n@i1hyskx{yl2BR*uQ@&#qyc= z5{n;>P<-HQ2mT{x;W-lL!#VBvbkFTY1i$27QFhX|p4uGT*@3+^FHoEKzM8U+@>2B- ze$}45@z4j9pB>zp7|3d-_P-WXCY=xPn8f+|)NhGD8x@Mx@7i(iUkPg8{xP52*jqvQ zGXt%-Wp9|S#lE{W@%+kG%G>x2$@wmk^2h6sCTxAFo_y)c!H0XEqc+E~vXahuwG{XE zy@e+&+<|j(7uKKX!V8`K_>-#%tgY$AWnHIn_uRMfsg8cU$^RMtWz{Y08W_MY-u@PM z&Ho-}Cwg$G|1|cWdlNr+auCO+?ZOB4Z^SLv&fxRg2XW}iMf~WG2XV{NS8)EpJ=mz+ zj2FFo6UTY2o_EynR%>x_5pSWgn8;$mPUj%axsCB9OqMcfr&1IICPgMCCap}S;)y~k zo+zZ^i9#x#D5T=2!rX;Re7^430yb4@c@% zf`r;OI4tHY&Ig*bV7;z_dyNQ+kYeasE147u7GadJAYGA;(~W_zZL`GrkQ!?uR}m5z zAT1!yM|CZv85$f{TA5-%N`JFKrg3&>;T>Z7)kcCYz@N=rvu-)O0Y!eZEVI)+mdVuAUSnVOg>2Hk~Jo1=bxnQAD(pce2%nj4$K zum$uEsl-%gBpA^vl>j}+H9}#jKp#=GOM)G)v*saUD`#O$2hg^`*-dS-N3ExOJlyc~ zT^_N+#U-#@vKuZn+-Sh5ERkUQ6uptWU)CbK%{sh$Bb?#LrMnKr^1{g_CsE9ZC6U9I zDvTACme>ZD@7lI*c-a@rZt^K+&qVGook`OR{xQbE6Nzg^oruqP`AN)lo1q-z)IH=! z?8v}unAnsipDs-uktQa3bDk~1YPZM>Ao}KzE^8Xf~PyV-~s_%|-X1 JdtuB)e*+kcf_wk~ diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_1_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_1_3.i3dm deleted file mode 100644 index 783d1f0184667cfe68b09191aea8a233942dc742..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5944 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 bMqlm`E{uDW8vrAW8_0!ogSf|FgmeD}4W`J> diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_1_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_1_4.i3dm deleted file mode 100644 index bb061baab45d6c92ec8e529896ebec2be22ebca2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5872 zcmeI03vg8B6~`|@Zln;N;bjMbD+LEb+*kHh$mu3PazogJWP@S|ut{!`9g^L!y9tkF zNrJqoRK&?lrGO&xke5P*0n1)2Rb=XzLJ<&yV^eXoRD@EBtp$3%@16^5z@bjH)6TFn zU(WBI?|lDrzQ=BMKcBh0rh^~|7yAmr7PLz{3&LO#0bk4?kz{wF$K}o(TR1IutUJq- zEjg{m6e&NiQ1-}qZpj&uO6P=?yo!pD5|*5XXo}>{E12NQomQ0JzSCwl-Rdrov$8$y zBa9ZS1w#rmUAbf1_nC|iEB1|b6?&#&3QsoA5DwNU(ZPfBBcgn2{;?cZ_rTerY*?Ui z4)PzhS6fw&p@dXpB@x=EB3@;|mnlot}u#IaSxE0BG}QOvc6czqmyHcyl{kpFxf z*P`YE@_+QKD2Isugd8Tmi1BL|Y4ub_MR^CY4f&HNHGfeYUuqEL#pEB_D9X!;PhhUI zr1>G%HmpG#>qPyxX??D36y+A;X*hS?zY_Tk@;|y+lv{~&wu*8g>1=#il&2H-LQW_C z5@WK6#T}xYNW2rd4{^q;qU>qZ>KVUVl%F8>qCY^K_X_G0doU(MJa>;M&m_KvtjDzc zQj|-{zZ&C@6YoRbKwN|IG2(-}MA=E~c}=`4I*+I@%_m9TCp>& zy&6F$LAb~F*5v>H4E$)-Xw}#?g`exEhE7n6hYnELzCN(o2VgxGW8A@%9P8|ka=-V@ z32(Gxc{>?+tceh&g6V)|DJ;*IyP|L zHf#zsY)SwYzcXvH+GScI_fK8h05i8uAI<#Zev%e5T#0bMV#!fK7|6LOxwkrM=|ny@ zbg2Pcd$<3J$3I&?`oU$I{;+fPU@Ka}>zQ=&tyuD>GkE-{kym4Vjnlc#69?Sto7ekt ze@kj_SXrlOb|2$Z53hZc`+aZChpY$R=JizcFsl#$)yn-B2SmZMbpT(VjN?mUT~5?< ze}4aLczov|mDN*lp%>M$ik3F@?r{>zU zHUFUh>*+rq@8|KOuGWL&*kV2xf3g>hpI_!&wzoAs>3Ri^X`Ve>J^W-n_m|Y18yzas z_61f%;f2N|o@>ye)v;qk>v;V9?fEcud@^UXd|T|QQ`@6DQH*8069ra(cI}I?oHaon zlO6D=t!KJ%{`=Jd2e%z-;4vQ@s)60_9pw6*{F7nkkVNi3(bPlr=NEE5aJd`?bw9^r zx}A9>)--h<_dhe`!SrS4=cz2$`lA`@kb%QEFKl{9E&6H<=i#k`RP}NekIy_(4_i~0 z@;N4qEK-N7Nj#>xWfH7&b>T4?dtQv)_jHi+O2-!JjjWy&r{{P+eu<#`91hkXdEs?NZXmp4ID@DMm} zv_g{iFHrQi7MOB$2fTawE9iIVdAR)04OqQ-9c<|S0t8RB!q&W>LhHW6@X_km;ggqE zK+A$J;N+2ifa7!{yn6OISas+s{IORX$iLeJ&s_K%mVLYk66Q6+Z`{v8r^W;D#@RM- zm7Is1ci)G{6I-Eq%Q^^_FNWfa+hNK6KS0~QlhEzPHTd$%0?0`JElm99DOi-W8&*Bg z2ocFwE;)-Ov&Ck%N-2`rYB!k4TF9o6)%$JqY}c&OK-NgsM3!`nq+=uCfMP?92*D)mYz?5p(oE7`t}t5!lG#b4|7 zRu9%z?z{5j`pT-pHQrDdSMj^jyr=V@@6`YDzGNxY%HSD+8A=cj&A<5&e17m@pzQAR^TUeKo$Y4?iAxW!^BREUgQ(#|Yh#NY1ZarLfx(pTFE!#$s?@ zH8>f`Y2oE6L7%splK8zf=$9t2v@EH&3`c|lK`l{fT}6cwoa9qxagndx&ZJ0XftuQY zU-5@Mb81l}&1f<+XPqC{-)v>6*s_?+Y%V^wTn3~tkLA}gt^!%!n zCO#j1?b*_@Wz@6rb-(%NR_aM(GtGBPY_i+$G`~BgHl(KEw)1u?HFJ#A|Flod!n|R= z3)(W*mIfy%VQ+P{udE}Y?-Riy>!{*8w`|ZG*&brn8@mB9+R%h0)KgV0dc;XpKjat>3(M0H$7Ijte-JGcMuYUj@UW~_X(W@QAotrNw{C=Ec`&|f~~XA hRY(%L3EiAZbFU#;N%}!s^YqvcmA8%%uFVguh5y(>ngGmz`WEdVe1~cr;04g9T z=0|=+87SpyQej!1!p%N$UT%KnnrUkrdrU;ztj%L7o0^BR{oniUlLy+Gd)z&H_B7{Q z{=fUb|F8SM_YLy&n(~TzaU55C3&*{HGBSeW`t?J=57CzkQd*`nAuTZ}b82c*+8Aew z5O0f%5+eL4&^jtKEqOEH^LmHFf z>}g^Y4Hh)XOiV~k>bYhTjd8e^l#uD1iaDGqG({*-B3DMHy~ayVEZsMj)V+2VFD+&q zTg^)qjAu0SQa)qTN?!V6v1;GCf|qVvqVjXd7RJr>ycEaSi+q;Z?_1AH?TimS&r31P z=FH=~^a0b0$Zs&VAivJIY5^}LKdR=c*752^y!0uv=|KH9#_wbOn{^y!ydU-NGR|4Z zOIsP&R`Jp<#`QWboXtx+nSLhn4#thhM;UvNn;G{(-$urDIzERsYni^Fl9xVb>_`1M z#tX~4a(#j}&oaFk&jl{R=`(rh1nZ#z`76fv&Eus>?7Zea$x9;{FJHn-4=~<_=XEb* zYZEWUGj^}zrJESPw~?2&Yv=e1FMY=N81g#Cv+8)s%NX#imoPqA%S+je*Dd3v$&6dD z1}Ebrv?*n5#=WO7UWxl1%s378R>qzRUa~PBj(wZLxEOmSGyW0zKE`{|CWEmHS-Y!V zjMS7Hsf zv%DO}FV~nn==4(!8gbk&{7X-MxdZ=?cfdcV#ZlSEr;xo(Z8#Jjk(y7R`=q-o;IGR? z2hqEJXbrm#6;S=wvQj1NeU$1~-k+oNe&aOd*c%HS&YNaZn-d53gd3u!Q?3;&97VUz zP)MA`on=Zv;_NWteWOjv3p*^-X5EZprQzvUXdFx5WEctp)sNaz3QuG$r258Rr9k`Y z5mc|drQGw`xhl$M-zig+h&j||V%{`xZTSn0FWYhz=S$yEeXsT#M`wJb+VA;sWccfc zDyXmY`zVEv7)|-@`La@`9HG8#yB~zm9sLwiXZFcHigkMywb?Q$v^iyQ71ig|z80Ra z)KB%lNp0O+^7w4Z?PscB(zSC&5#R9G0m|VIomAi3Jt=%TX%^*cmUIo5tVlXCd^3RoZg zYa0J_|2>Yjtm#x=Gwd5j|D0UP`Huu3s^eK&&&?fGO5*w#T1eg|PrPETh^023y~E&W zT{;lG`RPoE9Xg2W#}6EV_Fci*)C(t;n`&nSmk0#1<>TgrBPNz`&>-WdOF0+~HuX2?s+uLgB z-gnQM`%@>44zI2+r}j1TXNRRT zWt89Ozrj)PO)+J2c4K(l`vD-n!RR3{@2(8WALY!2+D$v*^JC4>Hm?Q_ui6MjXAeU0 z&Q93aunG<}cR=p+>AHX>5TUc{^Eo^nJfCKk0g1OEbaO~R%pLDzn>-&VE{@^OG zk6Qz=$G(K`+xEa03)6;_4+0Xs^nK|WOv{v*4g?(o|XG4M$^^7e9gYxg2pF{Ke6Jo+6R zpKuZ)nw!9P>RE8@pAXiX&Oz0HX7C?>9^U-=V@PP-3O3^oa4dTShPEGo6U)AYtFg|- z!XB6vYJljBIxy{TgPhWrA^7b!$P%_eWAbCr`CSu?d#D<2Gkpj-d0W9d?nStBdn+_4 zA4BqEtDybwufx%6o`Ewk7JV7XR+B? zJUfeLXYuT6JiDmIvx{mxyQs#qi)uW(sK&F4YCOBB#hmyuaN$W#N}3dns; z^?Ig-irm2v-i@E)80_!tgZnz6?g4&I43G_iPboei7x*y!Xnz1YXQ?2>= zHD7Loi5XI4bmT>z#VggsZYR@iqAyj?7iR9}reD7BQBXUj52t6n@6Ye)|Bjcs->x0G zDkI6KpwOAz8~?3U&2!wRS8?3f%=EN!!HwUa`0}<~D3E<}z#Z}jgm@t_JvBAqz69f# zRA(|?$Wl2F^!k0cV2lw3d=DG+pfSGjWfa7(>vF;8&yxv>u1fqe*JkB{XPUP#FCe4) z6ay|4xkCZ(3^Iukqr{7hX#Iko^~R`oRYsKyMRIT&dG$;JBhmuOh2ns>$Q$zFuM=9C zJA^57OG0E?&fZZA@do{?Y$NW<>&x?chz;(m2sb0dNNV#ZEEZF6yMV7wf17Z!Rw8UB)jX+Asx`acHl zpq%J0^aqmN9(s|dh%q*iehdb?QM4QF=WiU_ISZpX;1#RHyQ>~@hMY&wxU1p%Q@-GY z%~sqlIgM1=R}jKmX*J^UxdR2{`7*`XEvBy1yTBQ*oZj_(tW3PQOOBn_DTl%`;!cf zHL2&;z-qZikQ>&x-n)j0Zc{kUtmYq|b7uHmlZBDewEKpYX=AZ{=>gu5QcU~VWkjJtsw Ij$;`2Pp%y$C;$Ke diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_1_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_1_6.i3dm deleted file mode 100644 index d0de197de64317b994cfa8f0a82f73add3d73a2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2392 zcmcIkO>7%Q6rQwXOQ0#V&{W*2;fNcpcVovkDvN8JWGlz6Y!e|OD_YwVdy&1icGoG< z;>dvtdH{(^6&z7dREQhmfDp-%9ypXsPm#EAfl3vs=&6VD-gqWn+@PokR(kusdGF18 z^L}P*z22T6guFFH$W7p{!{Rn@{7@cHGhZsF^OZZ>M>+2r#X-e%!XueQfPUqI%yfm&%#z)$sxwupwH;5)Ruqb8Hx$L+~OC&&*O_$1R z;J>`Y-TQ8j_0RI}`)$R%{prsa@!Y?>ub6iQzA{kE`vONlQq1cDr>-jIErHWtC}v6E zV>cA@J%Kl`DCTe1hd$Bo@EaHs5%S;Ry~x&X<)uH*qwmiy40eilF7F~fHS@{Nw=)|& z=JA!i^EW=&;`o-;+}-}^&JNnBC$@GU+<*83;>iy`e(mj7hW69(tnqc~W$x$ZtJjRl zGY^d3AHN#($KQ>6k3KiPtXwm`Q@=8{-}%M}s9mS2RjSX&k{XS0t_waVcuerP;PZkf zIFHApv=X6Jw!u8#ZrDx({pIXh({Edz4>tdV7h25onA5ea)>%ESg5|%HjQm&njaGT# z2-IVs5VCiakc*{4KA;xFfhi4WgE`E#{EkagG*igs(r=`-#awv-Vs9|lvpWtbv{{wH z(0Mg5hS3(tgR}zbbm|O|TIoa5auv{8({9yW2HRJoplDmZYrl(&Sv8^_K z(9|41E^}?GC4x9s8{~8u!(vdY20OftI}B9qt*^7@Ym7uSfT|5Av!yn^5@|g@;s4YRUbH6PXJ)as!N5A7*#h8a_q+eJ&Z-N8NjDGmKn#>%OlNOfF7hbj+8A!#6yb zI8-Qbd@j+i^smBYzhLI^2^8Oxyv+mtMh-U~hM&am|9uweANgVD-nYSAkqki+OD^SM zE_KKyM!j5zIm*L?x`WmzmlW1Zj5@@UON?tNlYCq!<;cfL+DeXk#9}=`j*z236XYo} wNfdGnXp%flrpPnoS)eI$oJ^Awe@;o_7PLUUYPLdbNX);630G%d(0ZTeB%m4rY diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_1_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_1_7.i3dm deleted file mode 100644 index af5cbc9be50b3c33024c9022cc3d831a430027b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2936 zcmdT_U2Gdg5Z)xjZ75A!N~wSc(tQHSwa))PkerOk&sC0HY}0_!D7x4e=OX(YozMNr z$*oC|K$R#JQcGx~ydla<_<5)bl(uBPJf%Zz##p(XwLTh){LnL^9PMjSoM&v(e}ddlt1l{=G;Ek`z_1%x$>>{biJzFXfHPH#%LN2PpsK zch&l}zgp%XUb z6hAi8BwjneoBB*X^i2K8dz&eL?vo>8?`LO3*SQ0_`of(`MFD?iPapA3}SCE;0s|V)nQBfJxnv+ z&-j2q`Cyo9Hyd02!LXN&eN=~p0MkLHt#~0fgkPVdZ6k(ie6Sr8W=n-0mGG4&Vn=C zd<>Og+fA&MatcAhP!*cOM<&jWsfC=b!1O^k7>cr?tNX~nB{+p;WV_v9$98AnhpJ8! z7nSlDxq?YxfV2P;Pv~kkjv@+R%lp_^`3{7@d!<}xw(#b8YYB@Dad|+KgSPYK% zXt|26$_180lZ(LPev+0Xm9sFTT+*#XBbCunMc<_=dufm*uY?n4OT~$jrf5cH{{#f_ zyFFe)Ry5duFFY_snQV*4OV*;2?GgYP)R(>w(G=`Gygjxgu!a?rKxCH0L)XLio@_1I zMxKrC`@+v+>RsJ2?@Eb1q0n;cTQ0TW@+5;Laa2LTyhfK z4Y54r@L7eiqHMF_KCTW`Uxq*XQ&NIF8SGol+ghVMI`*Ka}1ha7y zYDO&}P3RufiX3PSNGn>4+R(jd9Y`Cx53NV-XamT4v=MDW_oK}qo6rMj3wjW31=)iB E26axsz5oCK diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_1_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_1_9.i3dm deleted file mode 100644 index 0b0ec321fe450feacb62ee50a971fe020dea411a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5528 zcmeHLdvH|M8NUH?F$QQ80WE?9S3vC~xO?}N-I{!p#5^v91hWxGQXZS+X0vUwo9u2R zaY>W32C-Bv3K$?#i;sl&8=k#96#GED#@HPu3l6! zmPh&LHj2tiT)*N~QJKT}F^CH|UjLe?EaQ0DVo_=4_+|;bPDu=nBF!s>SaT)sD!Ts!dR#fie`ZZ{KH^-TEqSDIo>gl2~pW`Im*{DIE zlepfyR8&6axEb>);rZNwd-?*$XWkT*UYt{jd+{XKkHlJB9LFKQnPVTG$uXQCi~HV} z<6897hvP)#4CeSV>{}woY0rtuFL)o=nT~#%KtqD?E&jGA|Nm#;{DlJe;ITmwwwH-B zx268(_)iEYA8JdptP2p1Y-xlmOHxxAXT;i?NXMT$(imQRegt&oWYatG&gMdBn36!a zebnN#;!uF_kKRxrE9VsP7qm%Gw>gXSUD@Lxz3pDYPoEqY`R$=05#}@hVk3-xvHcN- z5ACRqw12QN%y9W9jo?mNxRv2iAC3y&wRi^c`@(|ooXjBk+4=Osw8IHeoO;n6*|y>x zvb|jQy$HOYN1U4A%5ZB}HQ~=^3*p&U8px0Ir^nzxIFa~?TXAe^T zhsQf2PrUpv>31X#j;MtNgvIR9@Q1fD2+vtm3r*#}q`03=tA{D2Zn7)%H~c>G<4fuY@7q%oVf7a88w<6Lp`?HJ%rIzbQb_++ znGwEvDTQ#$Mmsc`4aB*#>!)dx8mAL}`@!iDC~Kh{-ab$W&eR0rdyNH%;wNtiLGwWeVI|?A@Vpr_h-3PFY--|(MzUSib~L=by@K@4`^zFH zbI*oZ&chzRls13eG~$GoG)4w&okZtngTEM#9PUm0_32aKi-a$UKk)20*sya5=?7e~ zg_j)-k?oN8E{Fg5)l|YKG8!Y!`bFet==}|md7JK`+%jVhr*(pdI2o(sBTvqnNPZqF z{3DEx-wlEK7vb;^+F`)7R=6-{HW$w%gW;#w!oAP@3m*7*7Yti+7V736feDt6 z;ESc5kkM}*ENHm|kF@+1PQ`764S_H`9J>t8UVI*YRK|NA&>sXh-CGuOiC zwf}*W3+6)WuGs+Qeem4K<8WW313vumOemDMLj3csFmHV)Jg{&tJnGs8oh3T~PJ9Y~ zS+fWZjywf1_jJLYDJNmm58s6I7w16NqGs5?|0CE?vmM;RIhfIQ3`*NSg@NC{47soh zgpLk)V*61zGvOHYKhOe~&b6f6l#ikXE)!NQ^-W{R1ZDQ04(P`VbofyG0?;#urQ zYP7*by*E%;3=mb4Gn_GJSgc)|oFtW~PpN^R$L;aD*`{UGdIHrhS0yrZFwaw35v+0r zg8z&92LANE^;k#0O{n*Ft2hVS!kTa+yPi|htaPh-2Re1nP*2ul9s34q9iwMxvKoe$ zSE_-4>gD@;dzzvO)nDbSu8bar+nrq>TVL0W@by;Tlx8yCC*74r*}d@pNRucCUA+Zi ztg|3LB)PDIc*zY(Zq=*$T|u8;a!8p4d3ouNrpq~bMcH_0o>Kh*kI#z>@)(1J7uG;2 zlJREDQ2^hxkmU81sSFt=H{g4y%|cRXg{QL2ucCRe5f`dlLBFStO~x3K3_Xlw{em9r z$*7;)kQ9=t)IbHhqBF+`DS?nw?e|o9f*$-!K$*FM7_y``$fhBFmzNx0yYjPJyvJMS zDP;p;|_Je68X7UA}IIEjBEd?M9yJbqDcO+GN~5m*36aFO%7B zHC@}i>x}Wp=}oWta^lHlC(+o=m&^`dG%=o(E-?NtY?$0I3t#pOC7*qDK1R+tl3rB)5P#7c(7VgF|Ncb;o++^AS diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_2_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_2_2.i3dm deleted file mode 100644 index 061b539f01e32aab5dba1629f4346d2166d0c056..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2152 zcmb7E&2JM&6rVsb&=4SPNu{2mIUrS9qW!R)*lMwi>m-XD8`)bpL{_x6C(bDAU3Yg) zh*nl|C=&bwy|%|*`VX{49O2qSZ``@`T=i7-z1bam>lg)Adi&nI_vZcPeavv9H&`Hq z99$vfXW%~podV&9`iPqKmQ}7-s;#|RwZ3I-(_&Vk4;n4gG8=VTjA-XLWR3m(fQ7WE zjul#OGB1P8nYWyYt!7?wq_j)d2nb|%C+jOMayQvR4uoxJqT}Yi|C<0V&m2L zV5Xj+N5QvbfO}i@wc~@QAP{pTd1zj!Zg~`O>072X&jNAYtHip@ao7`kQ(c@{s(_KE( z;oLf;i`}R(w;#e(uItb~+wbH3GO~rdG40-&%`kH5t&>7TV#cN4s?tB2MqM< AUH||9 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_2_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_2_3.i3dm deleted file mode 100644 index 8b74bc992f645723f085bae7e9eeb41bc90add18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3200 zcmds3U2GIp6u$foAVoxq5bZ;~4@S*WXJ)tC?M8Z8YJUc{yQI5mr`s|6m)U8d zS++DLCO&A4{Ah>~HPPUMVoXG0pk2_1QUWF<`lKjAff%YW@j;`+c+TBB?YK0leei`z z&Ytg{bME=QGj~mIZ+ZbCWTc9a4}jM$B4qtS0Pvu^K($CL9*Q)##Ja;Rk+yg{4XUz- zZi~jWxE77jV1f1w*+#UlFK^g1C>K3667B2=g}a~LR;g5dE_J8Y)*i34@CDE!)*K4A zRH_DRv<35iVqDU87t1(uQqrC;l=1i}Nt+V5 z`n06|A@JWI|6SlsB}v;V{QL+uet{2-O4<&ATLJGB_nuMb%t0{4yB4o6)|Xy**~jZU zb9f)y{OKcOSoeE1zEaIpeF^dTvmcI~xSiv8{1hv-y!#lp|Lwx9rm80s97``g%vQbC z%5AG<|+RyG_Hzt;I`>hX6u#Srd*xsY>u#IC^+0AD@XWiF6V(~TKu;8(;*|lT8unU)7 zW4WWRvyr8Lu@yTHv&)CBu#jdBWu$@z{vH~@GX>b?y`Wn3T z)B`*qHy{uAWWV6fIw01KWv4$t^F}78>#15Vx4c^=VKdQhr}exI-TW^#&ey)uXBYo& zCR6cN{M?U7gnS3z{w=X+q(F7(Ieg3uG-+fEOSiKY4btXlI2?K^q_&0Qt&roOVdc$i z1{7+YOySdplKuDk5>^o2FMdA(41waP1}Uy#WT}w2-!1WeP|rYLpJ8n`jXm7Szivwp zO=Q!#Y{tmg@u3_zsaI4l3I{UKf3F`yVOvx$)?#A2WI#5z<@aQ$6M7GKuqz1EFlP|( zEP|-K9;f%%T5O{ejrV=}XD0OamS*or!q8}={BE2 zxy`vAtHZGMRLV?rW(Sh}85jZo525Fc=4>i!wdx6ekax*-0hvFF(x}RfYUA{Y6O{`$ z&IUMQML4_VA$J7QyRAJoEX_Fv9Pq^2WLFeEk03 zxj6oj&z1MI4c3ap1UcB@j@->19dZYw-dVdj%H4#z+14m`$gOuU>JU5J!MJv04zJWX za^#id*g72bh{bvVSx6QEEg*}@5+aeMKugFnvYgyQRsbz0Rb(Z(m#hL>N$w-7$^B#v Q&}vdm){+OvI-s@WA8!=}k^lez diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_2_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_2_4.i3dm deleted file mode 100644 index 75fe913b8987f447769575c3cca5d19c6a014384..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3552 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_2_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_2_7.i3dm deleted file mode 100644 index 172b9dbd7a3d8fd35ecfd42a60dc7983df881170..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5848 zcmeHLYj6|S6<)x|F%a`Ari4sumJ$dUkF;yOoLCpzV6#Ek#uAX$HAcv5TVP9$B;y!K zJep7-1p-U~hZ34V8VD(*WzsYOtZgWh2~z^*)$lMB2#+b45?|u@)`>?!R z=WssVB&P5M`3}h`<>&F~A$~$#P|hza3&=q}T^~u|^YRNvX4*?e7qoPmji$TqLMeB+ zvn9h|GzdU(WM|rkwe$&s$pXG%nGR z;`pOF$&C0c=yXN=^%0%qLwo`FqY#HcGlEzE&QQcxz-$wp&z`YU29n?urVyzk>=ZlNgxJ`gBBkl;C-pJtqUqZVP z@DpecL!SK+KLK^n?7xRtCy~Dx_SgeC3!t_Mh?^nLbI4f_I(rZ=0sbEl&jHS>h~t5i zjcYFh?t*wW#2StG1y~zKel6hnh+hSrg@{)IzZP-tQ_xF@`#^5bqK+T%O0;kKr%sxJ zy>|leaKvHYbjLc=URR&#L_-|+J^sEX|Gz!3=y{WJWWC7xeW@dn+}!3==y{);HY8*5 zpQkZy11FJbn^Y^!qhT|L9MQk^6>y)(n{jl_1bHpK83)rI7Z zPr52}?PlAaXtTk??90X-xBYH~%=bVi1Ib~_sVYib$eytE}bTC^w3>@(v>+Dk>xhC z=yt>WxT%bDmCGQ%T${qy{(W_|@?!mZ#(#R}P_p)ezKpYAc9nAAUr|<*ck;7rxjzjt z{-A;}4oOP$@ZYWA;tEH*7Bda+=DS-AnoWfSu{|dhhAz)tWHF@0}S!!ovqJ z&fdLGDPvNy7@k-WQj&UPGR>NIPj4!|t@fUAXa?CnUd@xN?MKQ7Wiy?>=$dUE2D=&F z+Bk-MNIEi|Zi#;){d3Qg3bB!Jm9LP#DZ9yUK3Ye9n013J8h4b;EBln};6Eo*4&Ef8 zsrBT-h7IJ(Ta6^=eugIF`_mI85dNSf?QIa$BD*5u8 zZDd8?Eu?q;TV%z!#pLBVJBYG>Bk5Rlk?h#InxxHHOsQ4^7FENxXBMHS<@H zNTEUoncpOlr0XQ^o3o_Ec8Y|%H3}q{4i^NGxG~3l_!`~o?udWf=T5GCXHu7zEz#gdYlb7 z3pk^m8TBx(fN=#g>X}i`jCvN-v!I>@^(?4oK|KrRXTkg|sAoYv3+h=>&x(3h)U%?V z74@vBXGJ|L>RD0Gs_Kb)RZrBbdZJ#{6ZNW|s8{twy{aebRXtI!>WO;PG2l7_t}`It zfPMkv2x^=(QRStHs(zYi>hVT zu*ww(LQlkG(&{a%#uK1PgChp!%9V2RB;O?25B6B8SR5mpEShN1zB|i<C z{CD!M_W6Tqr5_#Lli`18uh=TI=8q|qk_RVaM|{h=+(TxsDbg25;7dY# z3-Rs>Pi3iJhUG;Da8$X1e$R{aB~_oIzt0#P>xjj%WUv=UQbK%{9H^jI98?$}OCZEo z`#n{jpa;IDSZ1yugq%26ee%MZo7*15KzYE0g_WJaR1)x#t!pg?Ia^s(oJB8+6uHg9zNKz^#qj zSL22HizXV1mPHWhUOcp1dO$(OW!Ep6g;0CA+ldJRdw6INP{tqv+mBXzT3T90EgGx) zoj-R&j~SLNJ}j_cvD|Ne_Y190HNvs8aW^zI4A%a%3{7dSAUg%M%+(Y8Bjup0veM%& z^wpGCc;N`x)s!nBXZtFB{v4N^HFA+Y)uLyQ!C)2iR>69w<8bFFqb-0Ii$L$Hhg>L^ zvNOJ0@Yqvsb;3q7Y?q#fUG|m-p)1V-9G}ZyPCs8_n$;xUJ-t?oq2*$i-qCVE=h99z zw9pc0^QnnpQZa!|X_iQFWHx-*v!p!wvjCq*`3*Xq{hzBYd>DQd-S0XVO`pyW%li(8 z?v)OLL&Msv*<)wTM`sOFzxEzGQ+w>7zWcpXyEbC|8m2yqwOPaTU9)L(OP^+^bIYM| zwVC=U#`iW{Tdp09He4Lnp3`yhFxqpEaS2=pt|N>Dt`qk-m&kR7@i^Co>&kWGy2I$o P{ebJiC2>7r^x*yt;aR?v diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_2_8.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_2_8.i3dm deleted file mode 100644 index af9be18f5a3d9c34372bf98d5e535b769d01e3b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2008 zcma(R%We`;^wcV~K3ks)A2D}Ln_-|7NDQG!TN(-tj9u7}W`GO53Cxh0L2V{Pmi~l^ z3;hcp-{4LY6FxQLL0%#%GOFk5}4lmwPOu4Wq1FsqW`W$4?KYm3m^LtrmB7 zjcJ2K3Jq%cT&Xav+0;=}$kmKv^lt1*=bkg*!}s4J#ozC~>`}fCe@2QM0Zsh)jIxRX z7-b*teE1Yuv83!(<^Jb;ANM|!{@I(i*&h#nWql@EEOW$?dXhz1QcI**J<1yVId?tL z5mtwkTLas-Q}*!Eh(uFAIa0{8r$d4?7`obHqzx*0rcM1J=TdFda5Vd3_l5S&zFXG-Gm%q)fnPeMj_!C$K{@ zGtR~Y~laT3hM_^;eK`gU}a<)%lDX7`R8?Nn) z12qQiHg^sMe<7XxwT4mFw0nKq;+AKe_R+~kOplXrU}68`NeV@6(c@H$pmu3UdD2#{ zV6KY2$K?(M!5ZcULC--DUDl&|Ppzdks%WzBXP-&v!$LkjFR-3U&8lx!Xl;E1$1Y(K zngna=GaZ`DOixZ>+~$76+2@|w?TTjA9&}DD9D)2jO_%5GuI+4_O}WTN+ImWpCl*WV zT3Sz^ZCp@!;6`1*6&vF2j)z?3Eji;!!^2blXTlqsST0SY#I25pTe*p^l4m*{`oH3d zbTU4f-anUd<-%9beARGsX%k~pzI0l?abiJIp}n%af-D-Z4 zXly@^p683agl(qzux=JTFTV5_%9nUL|9y|{5@Z8Vw8Bar#!5$6(WF;=7?V5EWmlV01;S1xBwSn87{$Pgk`t_ cSK%65N4N?runITeCc-M*g4=Kh?jqcVKgX`h$p8QV diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_2_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_2_9.i3dm deleted file mode 100644 index cabce51a93cd76edb63fa53bfc4730cab7634beb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5128 zcmeHKdr(x@8NU#`A}z5h#v(PPw_4f>(%rjnDxBFMk4tz3R!B;y*Lo%)6qOkG)k_s|6FMX1teeEwe3#5VkxLA?Oc@P07iwAy#19yYq40Lm#lh3p-^zaU$53s`Y?K0=FPgx;};@=Fi!p@gv{ZG!Zf|KIapRhuZ`ajvh3Nh4A%sy7Q z`ijiYI>idTRDa+-R`@=(O*zC0HB{dYcn-zgV7rOh79U`R45}aOWQA)~Kl3mv-1c{0vh+ac{Ch55)`iLR^X~p>OxozFh{KPw_nH)k*5}B;@=i#nYib z6%_jqvO+P%Goa^-C^nyGg>5vqe2CjiW0mb^g*z$dY4DRn@kQv5QsY#pa)$c+G2qh_4?wId6z}O{g%GXfvA7!v#p8fXKb1y-P8dkh&~A(dgs22Z+AsL>ihG`wN8gX*4~A>Jp9-Tl1A>^p{I}Gq66cjn8aL zSWfsy2Qtuxop%xbP*RIDyn6@X{Cmx@1((mu^>d6&6O#t+@~D`|M~jR^L0BPUnN zd_9@iZZ0uLUg-`KpY<2Ix}NKACi=3uEs^94ONf5(`d69n?WrQz9e68JJlRWd>#y~w z|7iok*2`BT71jq4_8*lPjVhPPb-fs|OR=F#B<{+VL}dG@fbc7xvPqpU+)i)`ACRj1 ze@i$g&gY>Ix86zg@Ha?$>a}}1vCq0Mr)BPPG!g%ovwAXTy;V!_RxV9?wO#Jp>kl?d z>vW%z-X5G)htd|jLwY#(&p(j?@Gb%jXisCy@p)GwKNSfM<9FY^~#`902VC^B)mi1Q@GPa^L=U%jI_6M&OYvEEkBd;N|mdrevb!66)*+6C!nawbp z%<_zFwY83{W4%t3TvIOw0-~2*mRmJyzRozB zKe46?zhCNYInnUNa=Wb(|{IcSrFlUFj@TdxNP_O8>2Yr4noy#gND6lTH^0@`J z9H<}K5ODjvu)(KkICvlhoIr+`7)1fN_+ifLs}?cRR5ih8p{&B3v({Z-?H9qkQVSa% zd(iKG7%$Q^shTmyeD#JJtI43RYDx`r9x+ghpM=;kK$1Y1Yw){0?w}h!Op=*B2q7Jz zAYO*)+mTDxs-KuVROR+oyPcQ=b$Or|E?rO375#4bhUFk$y9e}KAr6a!+MQq#@cHFH zj!;dF=r4DRONf*IdbOrZ)%<=I)fi30Ox^H}wb6rl}l~Xs00bH>rxV!Qpmx|Toj7J)-KIL0Z*kFQk@o5x@URMxq zrHO~*v-@56`_kzxM%~Ei-C_(^PHlSAR~g(~d=s^!eDV15$%)aVRDq3pb5m8*YWUdi z7K-q{VD$HO-UKVi|4p*tMEL9Y{ibtK{9`#$-Zwe8Ry+_L3Ra||S4(9NO9kV7Wv!N2 zuO4`BtTomvL%pwHyoa$O6^z%4PLZSc6g`%shr(4P-p81(qnK#sHW*P%3^R#gnaMCF tF|o`PCXV?Ij48}_nRw=QW-5$$=6lREW;!zi#xy2@naSM2%z`nK`44D|4x9i0 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_3_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_3_4.i3dm deleted file mode 100644 index f9c294fbcca8330d2a4dea947254630daecca2fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3424 zcmeHJYiv|S6rO_Z6>Fi$D<*2{p!UyFcJDsAyRl5x(!wR%ZS5|N0Sn&Uz1_XZKAO9C z5nPs5tnrbki7^@!e_)8gAU+dKAnhU;{Q=4=J}3lQM+YHkUy54MC>L{?(I z=(6FC_OKL@+C$i1z|l>*(%#dPQ*`XN7i~Dy-q{uiu3z0z?i9V=ad)S*Vr8T}!rkbB zh;VZt*i!EDdR*XY34|l-Awgs%05bZ*C$6U%m2sSJOUjeJSw zB?rheuY6DJ7l+BaPwXLI9lA`;ANY(EtB;Tda*6o=y+C{eSIORYPLMy7r^tcDACiHO z_K~69Z%FCw;{^Y_gN*!jloYTU$Nsh0>1=d4v5jiC-Oye>_PUI*&quY(<#KQ>Qtk0J zvN+Hz&S&?S+D-jF(dFT~k!vs4jL#<;`9L#X(2N%};{|Q-iEhs4=6r6>=l0@m8;&X) zl$@?6)J%fzr!}SIa!RIGmQ!_3k?v@M#dE!upc+f+X*s6@!;Rtz87Hsdx-Hj}cS0_< zJL{~;-kW9VW?iAKE|H3~(stHgh0tl}ZL7lVp#qknCeW-3IH6<|P1ds-_T%REU@)*e zAg%~TTH%y8C|XXCEtX;oL@`mvSCI;4!| zbvi8Yo{aqtYwH$aS867%#%K)eD-D~$e(3J7Tt!pm6ir$1+8Oem#fDOm8>E3Kc$>@tkx>W*hOAzZ}8gL z=WzH$yHE6u-8g!IfS1#l6X1#!;qDqm?o{He#^VQWt@5M_!-LJJMo`Hlbhwp`B9u?o z67>J&boo5a@#;;Q3|Ed8(wsWR6^5HjZ=$1&OQe_2SQuZFWwyuVEp`{5g+Kd4Qi!%b z{@utMr=RseW8!r9MtE=bT1|*wm)os!bo&CZ>LKY}M3m73!O6 zOzoyv{U)Y9icM`|IyP;lF87&ss>{WUH8u59%*PdI2AT;{fo?&Ss0s-nmFQM93*CmQ jL1v-bQ4N}n=77|oS~M5kf#!kCMf1@Dv=A)1( zZJ5PssoXd>%GCq&kml7CJ*hP+W~El8`H;3Q16J#HeHPHXGEUKIt$tiI&R?G-ovJqT z)++~xW->raLqMZcG|EYjo&is}*f7r_yLl**2i}N{|EOz~cOSkViGruEtHQV3j~71& z{41Ki{Q6u7Deuty8P#)YR;A4pZLtgH2fW9f9@-7w?gzHz2edhU22XEb?k~gHtDA)U zULoYDQLBd3^2IGf+G7s$tibhXo|bBcQG8X@4h(ajrsxIpeC|5n&~_9GW&3RihSo%q z4{#xM+zvxYX^x>7sfD!N=Yx*N;P{ye4%-Sm{uY%TC8f-v?YJW@$AiJ%9H&BRGrx~B z!o(no0zx|UxXl9&?GTk&0YJ7!0jgm%%*4!bj@;?+HpalcY`6@~r$xEUC-}NRytx>njyi?BJh$R24QHr)*d+0jM3CznOB-vFp9gQOgwoK0;6t+dv z@h%+OrGONOSg}III`kfvGX?^CyAT92M!@r20&Smu3^sXI-m5U#p=4!y^;wGwzTpBLlq_C){7;q+?9XH4C;bVVUsZReO z@Z}d~$|Ctn{AVWdi{wSQFiSipsmFcu_AADbN=A3)nBaYybcN diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_3_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_3_6.i3dm deleted file mode 100644 index 70fc1e6eeb22f76c3861d42f3337413be664ef46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5832 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 E0jiD3CIA2c diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_4_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_4_4.i3dm deleted file mode 100644 index 8e4a46e72a9d2d967084aff791ac10eca4c0cbc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3656 zcmeHJdu&rx7(ZjcmB-k4IZRB%8ze}&dV6oXwv%zJv)eA|R#>|Mj%D0-cioNc?P%|q zqn0h0HAgT00$8FOa$Q61O(8BczEHRoB?#A&_(rNr&CW;P!Vo9twtT(}*Uek-iWnysGr1Lm$2r-(nc8=+ z;^b2d-?N&Nzhw9b!e=*8n|GTzImB?`T25ZeY_2Mte45E;cW`o;$$!^4ImqyZBq#4- zHCXZGI48fuSCLpOYWlNacbBzZ~J@ zi>x+v4JWT*_)nB8#%lX`B`1H&FrFR#`k;vb_#cmFCIcro*#R?VFDfn{B#1KX2{B zBu?O3FMLm5wFKK-JNg_f@$Z8*{d?f9GoQkrrtgH;U)v5>z4Q&dC~SrI7CaAKw$rd~ z#rtsAz;0M79)e#_+zt!3{028Xv>C$Uzu=PdAHf}$o`EIZm*H33C0Kgn3@i{|gqt#N z!oOeN4YN0Yg}&q0VTRY*_=;v;@_1cN-o{H_!Q-Ob>tWnWxI+*acQ7t8E-_C19Rl@t z2<1%fW}L=z2rQn1#dFYjl0@T45{)NGG@c~Uc#=fpNlqqrQn}Mb{hcoA?{rarC+erg z#z)lkYTD3ZnjXV_R>#$JTGcxhB|cvgaRp;Yyd!KzN^U34ZQBI2Xorzd(#EK@{kQg( zUPjh=JCgDd{oY~8I64}2hArg&i^ap$_}cE948V7V04xgy>odH9_8MKv89t`!YDzJZ zDZYZQ3I+nc$9!T2! z%J9(+E#8(=k$bZPDH4j2(w@W%yI>PWp)#w&imk>--jcOt_=KA7z)uPs7(r4%hVM*i z3C+;Zu_l!%21*&}GVn6P-X45~!}>(Q4+l+e)1uf0^_4))@D)x{uA0)6I7^}{2_)z1 za9W&Hi6W15GDQC6oc#n9dll3lS59c=-53o2=~ zDj83vs+A}?$jyS?BaqMG@QQ+0^bVdl);SAfI-nz#MQ4|8a)a7N_IPOE)?FU4!!9>! z7w<+u)nf)am2MGjpOT8<|Ci+SmPGwedBqj^^tp-I*x_M6ZcNnV#ddOthxtnaIcu~I|Gx6O>WAq+|*%i zVyrjUR*vOX!n)DcSZ#vvmQO016QmfQeud vm<%{XlfYeI3YZF}A({fFgCZ~k6eB7EC156)1!g0f3Fd&g;BHWgXfF5{6Hjd} diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_4_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_4_5.i3dm deleted file mode 100644 index 3536cf45980c1d43860bfe5361a288008ed5ad3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7528 zcmeHMX;4&G7JhELn1sZL%VaUZH>QY+*!zOY38LVWwvkOmL2cjxtxY%f0%D606HsxY zLE|#nG#XP9H4~$S8}@Taxl~NlF(frEBoRYJlQ^Tq=%^XzzWYvVLrZC@W-{}`uDaas zefOO2oO{mg*Ys->{7Rol$Ylc++4JxERr)PrHqL+O-Ucyc2TL&>h7$k@Cl>R z+M1}8N)<6ljf*vnY`dmb>I`IUWNd2M6cQtC6b<2adW5p@pN%Ykw(|IBDepgIvi!*^ z8NV}yMSFCZ{(K}d<^-E zlUSZb{$*m{9r1_6<`&uCb7J^TCPJq!8*i)$FTg%h?R*fe;)DILsBi}&ylgOVZXEG0Un#Qqw z6WSMb2Aa#M=UJje+IA{*&DIvt%zT zQKuKlvjTB8=~p`9tpvY;xRl@!)NCRBy@t8{MEdAL{$?u6Cn8@%YN$utjnupo>gdQ> z^4CYYFUYS7@FRwKfFIQ4!5MgP1}b3I2WsDVb~e2?oR44c(;u86N_Dw4(+l%=f=|Ld zw#LHrsS(ts!8>4B)WLF!Pn=rpJNp+8wg2|SQ1R%gNXma&l?w>s&{@EBQ&!r2MzTD^Jy=3g0BVG{>50m(&S7(c%&IoE#eXc~@TU$lpm`<6ytas z>!XMO)0tRmljv^rJ+w5BV(Zhr!MV;%?Kd{p#2m}^P#hAL4_hXmrm<|Bcg3t5WT*B~ z&8%-^eHq1jekh0j-cReLxR-}ViM8{GP~UmgwZ8R>d|!Xz zb*oIUrr}A7^Do(9>Hcjr*2U*%h)zh}@!uKzM z`S~B=qw9;Har{T%XlZ~A8EatGQx(wn%oZ5Xa1j>8{uxYfu6);r05hnZU zU_o^i>^{B+A`jF-^YYhWaR2p?(ozYIH!i^2+D{;iuY%5z&tT@J zmC*6+YM5931-zSB3;GMo;I*EsVV!aZtWk;J{%H$5Ub_jF?>Y+Cyp_N{y$ZCE@4<+L z??BO-J@D;><io(lEUXs1RyHQK4sPK|aNjH|)8a-K$o2IFcl zt_JlqsHZ`HE&6NGUyFHb(O--H{(WoFUyJ!@F&{1J=}=FHdOFn8p`H%?b?C1{e;xYk z(O-}Ldi2+$zaI0^<9_t0r$;?K>giF>fO-bhGoYRU^$e(IKpg|>7*NN6I!4qnqK*-D zjHqKo9V6ytL_MRdM;;*1J}Z^7y;3RLE0wan67x`F9W~fb4fa!m{nTJTHP}xL_EV#g z<7hN;-Wu$$hV(ZplFJr~1((~JZ?)%3=Q-XcxLkt0$YQaDtLe#Y#n2wMVyKPDnp5B| zw7A@4C&8-;B!rEuv^NKP+z%5Nvpw8OCphd?_u+QkaN37Ex5w>%>-)R4{lYZ;3)S~ zrC=}0v=_bAAr3V952SKG=?2>bAD3W;dfmx3t1LV`-7qOTuy=2mg^LV`y?ecY@Ul&20p@ zxz-%XhU}}5Y=(>0&~yc-)ndaSc1t1QxiL~$DX1lfc(@!+IZ(DIFHdlevkE0tq}8ti zkz9_Wu*hK-?C!MEA|j$ys?-wcv6K3%by6s)EGo6M7pqh*1wn3VOMeZBO(C@>zcmB| zA$z!I5TcAhRCGW7+DoOC%IJ?q>wf#sozUY(#;NZWSS9@+61=~=g;qpqN!#hT6Ivn$ z`Tw*HEty%|vUzv*x5YJoyE7(gONRiNz(vIcyGRyd{SoabQ%wkvhRB-r!a$g1TtDxTktb}jM#5xDNh(wc<*vmZ;>e$GF)UGwwO+;;eF{j;<#VZ83ZbYwb_(ShmAbYVgm xmW(dULrhoZVdfDsx-yS4q0D2<<79*~-Iyns?o1dNPcS{0o=h*MHyJ&de*>_&RMP+e diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_4_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_4_9.i3dm deleted file mode 100644 index a3e52834c887a70dfc539986666b447a8b676a59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2144 zcmb7EJ8v6D5FSafgJgx#suOIoQ~VOftgAGo~Q={VeF zS!EPqm1=D*U*5cXXIdFgPqekt%BnVPkV&FJy^t>#r!~=d996}9UE4(W+N$vG+Cx5C z^uLGS%$M@d_HNU;e?hDS_yyvX0Dp)0>j0CT|Lx=?0yrA~-fr&h&Ha6oY+r6a`s24( z+hX6ZPnLK8{P`<^SHHiWyOzDVOE&+#{*e3U?VH@*zwdHCMc(H;X0%y$gC$al6pIi` z3zkg!EX5iT*5upVagDBFcFFc0?{af-o7)|<*|&&gl*LYuI}SGoy52+ew4@OfrrXn& zXYo8crvd(&1GrtURy?NTPVAP)FaWo8*Rolb6{_WO{#IUHDQnB<5#u_BWuih|QW#d> zX`wN;I6w{_(PO67=7f~S2osPikF~apUfbrleItsBzV6z_eOfFj5#itp30k}5-GQDc;gLFG~qxx!Xlg?tTbkC!YI1amkx2zm~JsCZrdAXL_X#?eIG z$G=JF^VtLQhce9sQbUP106!(i0BWGcXHt zNHg#WoPyIZk8}#oz*#s4=aJ6Br?3E@!RJT|Z~-pD7jOybB3y7%g5PnH%-KG=>v;}dB9zyisM(bV2f2dg**GaZ=?8tU{v7BgQZ=6l-b+o%q z8y2gP5EU0xLc$wopuFQ|ZEFCY>EFj?hF%4AQY& zK`AP^EKN9cYQffW)6%8cDmEgO6c)iuk~j5$@U_el|q=GXoc-Z<-DB@f2g#;DBB zL9JdK>V|$S?jq#d9fTY!s3>O>7@`T8mevTr*P0O1cgoL zuwUWFeq)fA+d+pmG;0R07IX}dCE(CGQ*Y?D4((!@sWyb1YS}n+ygLZ)D*si57l>|D z^$Oa+x*D(;nh3LWHB(pXUJyfVfSitESPZIGV20H+-9S^V>1oY8u4^ZmlLv0UAgweT zb4^1t?Bc>4I2j0u5(--e)L)8VC@hO8;aYSomjK9Sw(QkCJ+4?pME%XmS+ZnBgM!{kJ>kN%%s?ECKmU(W< z9Mx>KUe_!6W~(-1KnvKLpjuk0S#O#{YK3j&lF%O$*cS@LMIkQ6*LIw@a?cH)09&jL zd)IApUaPVmuNU0kvS;O-sh2s%vy?Yxb!O_m6z5 zyw_}Stw{L(AaTq&m-{)_A?FzNeC+2a_Y>+i8>5_)U(Ye>5OdBkj=7BUP92vc?<8)^ zIqDI6<1W%ob^vvehsaK{iv)mnlHFtv=^=Z8_K=6kKJo~86lfoLjP#Pn$rC`mWIs7T L4w5H<4v@bAv59oz diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_5_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_5_4.i3dm deleted file mode 100644 index 58dbbc1a0369fb208c42ce3646ab3a841314b70d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8752 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_5_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_5_5.i3dm deleted file mode 100644 index 7d9449f136d0b1cbb32b5c35882c58ffbc6a1c80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3848 zcmeHJeQXp(6rYxsrAqk{kZ7XKB1*zh_HM83?Fk)gZOfAD2fecan_92e?RDWkn!PQw z9tSldVk<@yiLnra#DGX57BFf;dJ1X`5I&^;_z_4%hyeuC5J-U#-^|Xo+q;@VqW`(c z-22UYzxUp}H?y~+ip}8+gwUj^2z?Clvn+(BO#lE7(Z_kY%IhnsDlPLiddjLQd`o$^ zDA@R#YOm~*tE+f-oNwyXmFkw3n4=Sn5k<6gddP zTUz2NOEuXA7qOLFEZQ;ajF?U0PB$$zTUyfXIU=NU*qY*CPL_c{BQTcR^dAtC;N$hW1zf$*^_a~!CS=S-p)eGIk!842k}>w^M08V_Z%sp`qSKm z|2jlZazWr_BVU`GpFj5fAR8+WY0&-lZ5lz+}XH$Wi`c-UHQqo3!bJN zX~B>EkF}{(UpJ);zq@KK)gN4Za>=%R>nZMDEa9C81j_mPR2;7hxM*FyowJiG9yMax z0HE8I<3;{d=6Z;&Z(7 zn=AO{&_&$))*#;d>0x|sU?Wz-hw<$#m+F22FWHz{kB zn63uZNRZTD9#Ud4CDP{ihw?;P!6;23iJh+TsDV~J?2qXwR;MGCpRR$AP>NT&=3czg zIrpMT&t+6?t*Al?DS4}-s}(JEq3I2}7Y!}gnqX8z(^~dVOC05u-o*5kq;t|WkiCI? zzk?xPc_#cBK|Pw`AU+OB=rZtBNo++L{052Es9pB zDr;$wQMWc5ABcwAq7fyc`#ReoNP*oU5@mY?`Y#reRHQ8qk<_A+b_sxV%BASJ80cY3PEkZG#fn(V;1@we-x+! diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_5_7.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_5_7.i3dm deleted file mode 100644 index 622c0a56262fa7fff94f8ee8eb72cacada26b1ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2832 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 UumbnsKEet-fQRr19wR)2zb8%4p#T5? diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_6_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_6_2.i3dm deleted file mode 100644 index 9a4553ab17b1ae135eb80b8ad08523c2bc7bcac8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2408 zcmcIk&2Jk;6rZ%TCP0A#;p2j$IdDL3wBEHH+f}<5<0M;c?8-L1L{_x6C-zA8uGw7| zs>LD_mx8zf0uImv7gP`k1S%ZToTv~;ATFGOREqj1AWjJ0o85^wPNYx?R(kt>?_=JZ zdGF0|tuvS*g!EE(fM^vEEpR z_>C12-gifAJTJblZzhcQ-Jj3lzOVe6FkX%D4Zy<)=YC5VJi@D63FD_x=>Pdn!g$x& zk3R(c3y~hKlwYvJ6pr;U!MH* zEb3o-r>Z~x`%M4isW0?$ z;kJJ5^&j-xe}1oj^VMJam%&?lKzWB2)~J?C=QA`ZcsAmT5zisdYw1+TGm@*3tkGtY zw%A4H`Mk^RF2-BxGtXo8(6ajTT3!Lqf1@#ZyXOxq&)@66SDM$d`{GS&Xfv9RZojc~ z1ZuK*oRFQPgq*8aD*?4&Z!on1?J}FWmhZTI1w%U?oaK}jkH(5bi~j^oat zZ;q1z9WbwlcMbys6dMTW(B%W}b7+IundL*u*2qUOh%P5Bq~doNZymQgyp29^t^pi| z7Sdw7%;i=;N@80B(9=~+i%G3EtneH+Ow<}}Y%uo%XD^B%d!B_PZ99XZV>8=tTpB`< zOiI;IIkKVtS{74bSyTc0D%q`putX@@0-x9Ons9h6#G$Uxf#O;V%cl>AMnMmNhBT?orOY`Lq$>}@plGPT2yrwMV6!E3tVO8>Kes|)clSg6X0vNF|%x>7^8taH2PYNFI z^1cpd7U8(q4U^ejAEt6qh4xu)7w?yr&S$kr_x3G@k&7?gja7%q#Ys#}#Zqzj!i`0n ziY3mbbK~atGF|;!-a?8JBj;$??uK(e%QWueekSEdPEXSF7ISmK{Dzk#qAQK4YA}BqbzljPurv(`7}vi$s- diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_6_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_6_3.i3dm deleted file mode 100644 index e431510aafebe88f5e510035835a0e15ab7f3ba3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6192 zcmeHLX;c(f7B1IPqnMaD>gaJPmlGn8>b;w>c_M-qYXU7gD#4~HpmUmrbT@8I17aMB zGjY_Mc#K9|q8>HJ2pUCYDYp!9iMS*hm$*A8F`7h8bP_V-y!YysHcLmHocU*;^SEE# z_ucQlci-&-o?Vlf+lFBnzBj|H$98%bhRN@U0saubmlG0GERl&((JATi(TQU$W4Um> zA%uG+DMhddNr_yzm&=&p5tFjA+@gmI=Y1huVp4KKWPJL#SDH^6dF{jLWMRx$OLK-w z&l@o%B`PvLy7`=1t5V~+=*SdHI!<93OEY*}1)^`@K+kSgDEA$UCF|~fffdX%Wvsf) z3Y!4m#<(2tT|XR$aV7Yd;MhTciB1II(o3wc1906%Ru}*{_bXPI5BQtYtWfNg^L}`e z6`}x7InD~N70Uk2Cs<(&;0@Ss1009_UciRW@j3z5A7h1MpmPCZ75L9!j#G*Kf)$DY zzj=feY5+$cV1;VH)_<_VVyI8U9#)tMc*Z_fr~-Ts*YFhJp_nrt@D0ql4fsclZvkF~ z`9*+p{OaGgjuq~LUw?=d`T!2b>+cThvEVh|oG#a73a-^hfcs;AJz!fED|80_M2x!u zF5S)w34rsru|h21gP5ZQd=g_F;NyP&O3eQw_}|;b3d;dE?!-ON3cl~>pNjd{fU|f9 zD~tv_?-N!ifHTr#{yOkasAPq+fJ-r619h0S1@{u*&l)ginp_7X#{GfMpJj!Kz|r{O z$!Az01pM3ldg)!Zi;8=gWVn?Gs)^{hiBhMZCGwO0mk<7cG7*j^ePt7oc^!7DN!8 z39bU^sBd8%!R}o-QpdP2sQ>Mf)e&>|xg_Eb9y(SsmJgt@%fDo$gN+lYe}B3rVsn>5 z>K}PwCi>-O9F67bi_p|d^=1-;gHuSWk6sS|_1P-?yW=5@L@JB!_d8dU2qdS8}QqGbO>PNhF zL#{*B`!?x(>UFxWy}hz(=PZ%^wZ9&z>HV#YZv~G=a|(x0%?=k&MO+?~Lvg1CW|ZD7 zgyP-9dPxJ8CsG_#I=|X}UykkJcoEG%G?Mxo#}BJ}`@KSm?B%a>ZBn-l*XjC&IX)x| z529-*n{AQS)&x<`8bjB*s>MDU%YIxag*FVPoDp`NRJ>A4Ih|ikMbEbHO7Vw7Ls8g* z5fuNtvKU#*CfAZ&H;3n;u8X$Q*ug?4X~;JDT(FGWQ7*GC3P zB_}NuA0DMawR`oH^Md(eUCfXy>JRGqvh>dWaO!U~->R)O$h}n4VT<`TEAO3O#-MwD zPNSUT>q}}E7RhI4yi<(y*H_h$TrYkoTz0oqq1_n4)=r-oDi4zo@g zdRVU8zrL$LZ@qjS&13E%&4Hg#^0M=&tZ+G6ef1(bJ!L7na&;LxJm@fTb=-|i?{7yD zH@BjzpVgsRy*8qZou8v?;}4>paiyp}Wdj;Ccq59}96{AT??%0YKS4p`Dp6$YU9{%) z1L&XIE}@~zub^M&l%w*B@6d_TT9k3+82WnMCUh?NJUX$l3=JD!g;tNdkIrtHjlNy6 zAKhuJL@_r$K<0Ui(SfRk=+&wPsQr!&X!A=K(5!XUsASklH0q@Ww8F6hwQXC0vaX#* zp-WGresNdOX7My_pSY_U)fguXk%ue&5+B=hF(5cK95s4EYN6xxF8;&Ku~{ zH6Nj_jpvY;vuASQlQ@mWs4{RN)EbF3>WwDZns~4(uxhXxuv)M>uzIkdW0G|YysTs3 zWgP=A>lk=h$H2=v242=N@Uo79r|U8rcv;WDgPscXRG_Byc*)w z5T}MXHLODo@mk<%A&(aLTHtGeuT}VvPYdhTLS8N8)q$Q4^mL%713ew+=|E2hdOFb4 zfu0WZ^q^;ec>~NFLDwkjnjoJE@|hr?3G__{IiJZO=Q9~hTv`a1Ax;(D9(%UknN9j6 z#v!`hqBGxWbqv(d&S>dLO%?`*4$QT>J-88?Ow*D-jwjx3%ki|P&@v_6eiKdV38)Z& z|N9mEGy3Bu%8y-vCQ$*_9571&eoM{3N7b^>)Dtk}Z_l<|(EnBl8WnI20W<^h1MIO$ znSOg;kFD?onk{Sf*q(qpZK=?*R+Qtf@OyCc{ifkN4)V!%SYq1Z-~A={j^a*t zGQP0^6`shodR+GD<-6>;c8?vuKIzJ=9-K0xz(XFraL2@jtNgE`$n}uj znQ6BX4&GNT-V7J6rOOpvcB=!DIIX$Z&n1wwB&pSgBiwl|IZ;MIR+i{WwTsiJNR!*G z5YCpDo1f0tz zJv=fAR)!>Mx*w(Xq_m`rN;X>e2R|OBPK}PzJSwr;Xl!kNt)=F}bokimc$k_vLX{uQ zQxlriLr+02bAE;^LG)N14!bQmuOK_eiI0F@`B~j!RGuTx6=Sv0MxMln8F~6qsZ46# zq&7Y1IM_MJC;_;|e7JYzLrxYm=@~yPxc@1)IANUuZ$OH-ny(Ch*+&V9B1Iq*^VNp%0;YfB2mN mM`xxh(~SvYy5rG}d6s#O>A^gY$8*dJOi$(yOfNioGXDjvlo{>- diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_6_4.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_6_4.i3dm deleted file mode 100644 index ff738b0234f70d3528257327aef3a8a9477649f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7904 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 mWVB`4GaZnR}ToWOQbNnXXJXraKv3ng0O~6VGh` diff --git a/samples/traffic_lights/mapbox/output/tiles/2_1_6_5.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_1_6_5.i3dm deleted file mode 100644 index c1a05b9384a48116786f54dadded38a85316d27d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1952 zcma)6+iuf95ZzMR(n5g(h1Y7IKuRlL($vXI6hhLb%B4z7d8newNj9k^Un1KnRFn+FCn{r!jlKyghl(mHfr+M->1Cb+3U7Q?N4`3W2=a!cR#)r zLuw9a;ex7~rfRgCp*?oZ{Jwa@?*H@@JKh z(@v`yQUhY)wjmuchj~WedbB{xt$Mxmp`;ww^(xKKYv%c;>wrT!kSLVz_aPWs6iYt9 zhSYHf3?-=>K~X{rY5&T!1|Eas7cw|(Bk;^Gs2oTcX#wpg9Z5ME4EAo638~HeE1VT3 z29XyK(y?dSW?({Fcx6TakiAKOY8VeSHG7<==?u(1#=yO7xC||1dAZCp4J!t53>)mU zfv^Z_^x=f>dQ+g@WH@Brxye5BB>${ahW1^1>^jT|^qVmxIg}L@jS~m@ujUX6+oGs= z7ZckhffVpqzDlJw^d6Qp0RsEG6a+HIAPT>a=smU;+bGa@-)FBm^m(PME(2CHZPEP} zK}!dRFm_IJXf*7LR~j0_jDSyJ+U9Z3Yp}quEVJKsC!;F|M!-Ka!)ImJa=oh2=ZkzH z9cU7NWI3-$c_lyFxcKIA8gT$tEP~yg4!O++e8%&JC#QUG!iPt2U7SXpIimo!@<@U4 z8QuuLFEyLbsq^XGI}9tAJUYwOfz8EDlvBABT)t^xaZw52Tvm&^(RcXR-`AS6KM8#K z{2wwkER%2V<_DL_%W`3+WK3F5`WDH{@Oi#Oy{O~!-{%kiG@*RTnecx>g-%?)Iv(2MKzx*7DiQa5>d?%>;0(|`gLA@FH{OYQpz7qKC4F&aFfH9uG))kHj*?p`ntSOaeUK(P| zH-G(Sc)9fCHN<9wD(|Ic*j=t4y*a#fqQT^+Kl^<6;>k9XFT8Mcc;Q`-;m4a-k3GNN zV)%!#>0{${Uw&!vl=8)w?=b(uhfiPo{p&{=o{qh$JoeKkigEFZa{l9=730P^<*npz z%D>OuQqE7DRrb8Gp(H-Kpm@}5(d-h9Cq$7(k;Q!``Ah~Ti(<-WX`f|)O{G$32@EZh z&~;jm(uTgKJFaP%mVsWU`=-;=v^H4mI;^w#GTOD7F{tnQ1R+0;6LP#-E_qaQ*wA~_ z&@J88+^$WtG+!Q39fpw$AKmm-g~`Q9jF0$|aQOU-1(ym1PA1ZW`rs^l?9*xq zpVVO8$(z2;-dz9SPWVpz?)F`{f8;yed(#GUMZ*6OB#t@f@-XK*p4apV$M0nJ(qDls^fCxqr`1FM?KT> i$=&1txrZDCIza9vhsb^8exO6-0dkl;NFD+@O#TDm0Y-=b diff --git a/samples/traffic_lights/mapbox/output/tiles/2_2_0_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_2_0_0.i3dm deleted file mode 100644 index 60b9ef4ea77aeb0d8613ae22a6abb43fc91a5711..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17000 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_2_0_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_2_0_1.i3dm deleted file mode 100644 index 07cc45bdad2d173c65af3cded45c8dedd9a739bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13128 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_2_0_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_2_0_3.i3dm deleted file mode 100644 index 7e437a23c9056cd7896e84fa0b6a8813fa4bf332..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2504 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_2_1_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_2_1_2.i3dm deleted file mode 100644 index b6960d72220e1ed7c34ae7c5cf62c91b16b9bac0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3000 zcmds2U2GIp6uzZ)5fE(AsxKyRBZ-=Aot@d8{fYLvlu`z_+orpsrZ#1|o$e0VpE5Ho z(Cu0TA21jb!vh2wP)i8Wpb7e*2D`+V7)ihsQ86(-_yayPNc;}Mq)I%IjK*8r5`D3@czbdKjrhV| z+SQp*l1gWsMvAn5hoyB64w{-pBSOhb% zG}czFl0$w_wM7%jJ_wN9!2MgsuvS_HV|3i394b$5#Jo@J^C;^+tl+l&9wjnW!CPMU zD3@%xe$1nE*>V-|2R6=)d6bhjUJ8DuPgT|)0DQ#8lM{1T1fQ#I`v&l%+PDV%Y_ajv zklPVEx6ErErEKTGz~>p;C+7UNjsJSnqZqb*YPU!E+P25qDq|;e5F!6B{Nv`sE$?qN z%UIuODlz(<%xiu1KAp9{_4yRa2Y1!5Kh8hPZ6>abvhiz53(CKGqP6_f?ls)L>Ezb( z59Qkp`)u?HlG>eHiy@YvAx#?EZ6#$;${K@-iz@eSk)Ks4V-amj=Z!ekFl_xkL^k zmP7FMNj?dfC`)K1%6>Exg8@DZ%6>kR1IR)?)cM5_XMPc#1cD*-0t^?(em;}^;Q;OR z(td43Gc7%>=hK)@OiyL3oN8KNxpr7Lhg3CtQ|LB!<6BzjGQNoqNfuTOW;N5)@^(7O zjArBtL)m}Jv#VfORmu0ljg!tM*TYpwc6tc;rk0S6iOzVDs^A)js%7ZgandY!g?XsSt#D(vG01yM-XhIuW(KctbPSLuP^3eKp3^NI_Lpa- zS`e~-*urVizU$#la6btmRHf$!^c31aT{$R*M*KWo&CuZ}RD$Hy9LQ-0hQ*+23Rai} zqY|ipcyLfNHtE_n?&Ke@jF+YgxuHT{%Uj7EL*OLf6D1T5=b`_SjG?eCqJ*{R*e(H( z#clcf677cG!`tNw0yQic1U%b8L|%{6du%PX(TT?UKKq#qy{WBLS}3p>3e8vFe4&Nr z0Bk#_xzH$Raz53e(M+}YE>zk))NgcXmYU7#sqVsXI+KSj;BT*LYORHA!C0@R_(ARw znnMB~K3`ZA!eV&##MzbGZkz>h#7b~>E1TS{4e&jlYq)!tZ`k2L5X!~fh-vw>1*bA7 z!uF{~8vifJAC{%L-Me8i969&WSzie_xp)$NRlY<#e3iuPq+EgFqAT^5_Q21+R*B

    s%cF$QR0c)&^@u!Z#4f!47xiZtm!iI~euO+|5z$Ce+P0 zN4Z08y@OGQ*x?Swxg&FUwa$?vuO`RV;iyM!&uhpcQVUc=ZX&jpT1>SkqMi diff --git a/samples/traffic_lights/mapbox/output/tiles/2_2_1_6.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_2_1_6.i3dm deleted file mode 100644 index a43d339ece757991610e3d073ff347d2d7f755be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1944 zcmb7EUvJV-6hGVC=B87pPG8OS#Ro=8%NX!N7_yCp!2)$J3t0+WSQBVTTc%5GvIigh z2tFAf z>EKY0I;cr>s2B3(V$`Cg&{E9T^>d7_ABy0fGvvXauPde3x4(}>!jso^?$iFqo4xh; zcT)ZO`6K5uv(2(+ETyCr)`+ntzvQlGc1^2G>J!`X45P;y!K=~J?dkiAc=vh*;C2b% zs9vr3%y31OKI?LeJBDXFEXxYjaykDtuO5{3eHLSv+;L6YLW3Gt7^dyE&>34Z=3K<{ znPs;*aY`e=Y^3C~)`i(?I~=c{CD70}JjeV%LR^U{GiW<(2=ifQ)Hi~d&-&cGpczqM zWJLl#8#rd)^i1rB$jtB%vN`lf^vA=D%$(+ET5YpMK3G>Di(%QMNS8Y%_HhJa8GY2V zV}d16qlGtI+Zh2hhn)_0PEGz^1o>x;VytEN2e!p6Prn*qke!68k#cC^mT4)1qPD0S z)nZb+6y!YND^@69!`|a^hCpy{=YnA5F^DSak-ewZQX3_j==+2IdOfpMvxnUa3r8S6FT>>pyJtK5MoTR6 znG#Pc;z=Yjs*+JNlZ_i!J`N)<;EDyfyQ3l3cw5YP+VJp{?@V}S7t5t-l)2UQa4UCJ z9G~HI=`W%sGbwF4y*rn2<-%7dvFf=(n8#n=gnZ}l^I}n?uuoJD+h+0qlgl(~#GI42$b)<&<&Y;LIebce_ze3^>ju_`pT2+#E<0#ScD~{1-J*xumY<{%Wxmo jU>zPHt-(XsfJg8cX#<|XCOm~_NSp8+w%`T4MB0MC;KjNS diff --git a/samples/traffic_lights/mapbox/output/tiles/2_2_2_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_2_2_0.i3dm deleted file mode 100644 index 24b048d72f31c67ae7f5c9b556bbe2619674b57e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9192 zcmeHMdw5Le*56uoU5e@nIjtgw(}N~4vu84y31$gLViR#ms)&e5GKp!DnV6ZVW)jiQ zrEbANQR?)#^*eQ^ahKT~CA8%zpG!5-w56gP)a6rJ@~wC6cWM%RdV2o(zUMK|v-kUZ z*ZW()wchovJtMYVpPo}k5QM6Bg77ySO}`U_Q4J8_huOUvIX)pVI=*MGgp{~m@v(`$ zHBmZAAF3J9KS55E`^RgdyqeTEJ+}TC8E%_L6QwN-)x`H7)GsKkvixZo5a6 zyKww+g(&wZQSCY9qTG*T)2E`mn&YPri}Iz9RL-m~MLC??w;vJZKXd!VlcF5CLFJs> zFUk(Czv5$2p1|>Zv^VAW_&!nAbG&7*D9`42INIOgcmn3IX|1Z+ZMP^_aeJ9hl+SZK zr&N@Kc-(6{MY+R9m0!MHlw&#Wh&X}c9a}|t7{@oa;J!ExMoza)s_*G-qP&FT1jNfY zKDtYkw{iSmXg|d9Gdo0i9>*Uew)pv&PeYFDqW*A>m!bVlj^9F^9vpW;JecF1m}dsZ zKD2k@xIXUJ&T$ClvyS6gsPE%=KH7^o?u&Rf$89j~iyZGm+>Ybj7;6Z}5vUW+aa+V| zI39!e0LKdY`Za?vpRwHTMmum^j$eZU^8(_YMSBqEKU*%! zR^HnSun$M_9&3wxtl+o>*2;gTFQVOl?pyKPZ}DEze<{lIxu)hTQNF=*h&zorbNljZ zqP%&Xy7!;1BcJ2hH$?dhjtkJfkK>c5|195oSA349^L&osxs2rYEyx+laTQ`0$E^?- zaGZ?K?5q5lZH;wsaC`-OERJ(hQ1h=GJMbLe=A3z`^C`DqJ}S!1{5h0i%{lIa-!m`r zbNS?|C?Dtf+Yh29-}@cZU&VDa=R|oaubZw?l+SScw9}$|oyQ%4^{mg&MZi3Jb51qp z=HYQ?Bd+E;703zU_#LzhoS%nhm&|j$tK^VKP>dU=k$4*1{?|@cCibGme$}~^y%2&?Mj3;~bp)BRy^6|lkY|n{ zPUQZXzSL8Bgo}GLfwbd^ga_WqSF%pXK9R!2EuNiE9Lum=<+JDw zq<`X@BBieTMpwo^Dl}9~e|ZfU_H1pb+|czUoN}xP?sg6)oudb`!1cOXpMsJC#k#j0 z>5MP>r0cbd>Y47_U*Z!dILY^eCixIGJc&5f;!Vr9Wh2S{*^!r&sG)rbFS^)XdH=JX zguk2PP>gG~6aUMgHZn4gx*Xi zoj>ia<2!JrknoPTB+G;#^<0wU3ze@s1`+?jrmk?YI+S$gXkLUeTO8r8`i+*#uw24h zJ9LKMZ;B%9oLCQLWe+2Kq*0PmJgzR`e)iE&Jz^DUesf`<;+yh3usqM3he5BC^$4Hq zw9c|=r>dFsZnm;x;$Gta^jeH^_KUW}S@zK`%fdT3gcCH;%KZ8r6sA+r=&WU^!AAD< z_RD?E{^lY)t)9iVx!6k_{5O#D%AV9H#(!^lYvs91y@}JHqEPw1Ze5Bs@4sV8S8pmJ z`_#*3-$I}2yTMh}?LUT$Bm0Er!OFf5;|VWrYlqpBkNKG9vQ0l&TCKB^U5s67`TH%G z!tAqOFHp{wb|Ox4R*|yq#MExgKB>XemM-TCiE}5_sX(sU=esYuOV>9VF; z?U{n(`7mT{9O>*cUGbF;R-c6;)Hf)dDE|(Hjp5*sS1E@j6V6-mUQH+ccdq0q-~VAI z`L0|#4n8P6M!nkoZM%~A@#o}wd}tmV*UTZ#is~1j&GQp!oic(r2lKx4W%S7;{lhC?QYPiA&&AgtMfje& z>Lq)dzOm4DT?pZWpNjCx@}cBAKHUX-45WsuJoQ=;}y8*Q1d{qZc~ABU>asG;h2 zY|5opmVaJRpM{p;<6!ds>$I1&*u}oIAKb)$R{?C84lPsW!qV%du(UB{h}zws6fIP)`wde%9(F?T;~ zdTu7HJbe(B`sP6Lj>T}OatpNReF&~?-wl7ZE`)CF{sFg&KL^Ei8!kmwK+&Pa&?Rpo zq=wywyPbTn=%;ef=l=-9`hN_01?OQ)+D-W5g1OLO?oqhyn*px$GjL$o8W{0mB^>sw zg7nH=aHZ`uukkwpGW=()3%jCi^P+li(d%c<@4~S*9XBti(%@-V#pX* z0u5W1!c&cRKvhZ!Ts-v=3|%k@#+JPe_wH?mBikoJ(-w!JaL*n%*I_-xo%;@wyL<)p z8*hWMofqKZ?)M?;#x~f!b}KYqw;n1d?SLh77QtJ~Dxl4_EnruEgpCRNA*fji9NM@X zyc&DDCMro|&}%|9#z+I+BlQy9BiP-brMr&ZBN-=B(IAR%Mka>?%K`^-(5o|j1e4e4spdGSYmwZ@ zg6i~CQ=LJLXHerA3`8-gDh4A9BkOFdA8LBTN(s2Nog1Oy^}jJsIaaslk!rEF zY+Fc{Gt1^e%A+R%sRU})a0uZ1Mi?ml8zpLF^lwiRN7-CC&fILZg8!Fhzp*?IcdNOv z0L~*0x;$x4ha2B6rFrZ&eCU3&DKNi>?;ybXE7A~5Ar{aH`{Tj6gIRVA2DgGd}wk5eRMEzuD zC&oO5e@l!P1)=nhYLAY zkIO!mO~SRI+J{_WfeV2+kc{@^!cea!$L1cz-uNIHnkOSW!vlqU0N4hYBK`nD`s;i&PW6jRCrwwxEWsY*-5zvcztJ~Jo zneBANSktJHleFO`Eq!!4v!pdk=3hFF@0`o{9k9g;v3J!&9%M_WGrnK&z*BzUgd-wx zyX-XLY>rG1c4ef5$7gkAvgb>0FdOyvPwxR|Y`H+!U$hdib6F?qYH3NV`P9UCQUQUD z22){j;S_w>d&u$ZzYq9lJpIkBP9BHH)8FBo`xyK##a#1r@@4woQ z?T=l+FF*E=etY2PUt^;mvvvPGaAfwti>*Cu&g}jxaNUpD8pHn4kJ;RB^N+RH{B}0h zzWiMO$krL=^E$#)!qa%w5$X!{1W|YfuX;j#p@Gm)XoOb-;aQ=v&_rm8S7YHhp_%Z! U@H@Pk2|+@0p@k5PS99Ti0BSB$Gynhq diff --git a/samples/traffic_lights/mapbox/output/tiles/2_2_2_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_2_2_1.i3dm deleted file mode 100644 index 20618ab7eefb85fdb2fb04c6b7389a0b47879313..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4720 zcmeHKdvH|M8NVUs@)AiR6lsScUNR_SB)fO-Zgx|$$0aXrOg4}#2&RGAY;KZk_BFek z09iLsWgu!NTE-e{8LCbT{^28|X$T|95?UTjeI&yuaS((Kr~_D}kvg;v_B-F6OKu(x z(CMG<%sJ{+k*La1|C|W^MneQkqoG@mwi7-~^sB~?B5Uv#%Arfj*Vi_49U*zSaKMj@8c`v-o z%Q1$Xf8*uvyAr%-fR_!7-wk*U!;3-lQ-<4t*ZkxHZ(;n0M|t@bhJOqA48v*1c=>aN z*8%??!^eUDhT$#1Ut_o!_zxK#1YYV+%jbQq@sEQh%y0(q zn*IsM)iBcwU-7b&X?}eH;xgO`{1S#=_?nlWVpzJu%PPZHkMlBN`P>b;I>FXL&r0IA zm1v0Le#AfKBvuA?<1e}$NQ*l@}`ILQs3Hwd|p8y{!n^1@=1F;`lKf#$X~k} zCCjqS$cxEk| zGV=G8=(FhU9rnO*GvY6M9c1q6`%piprMLIu=ZQHle4rpcCz^?x+6(RW%;Sl59WA*h z{_`VcsDHE0N0K|@cDj}UV~j}I^Dvfw)%L!Zrfo#qXHH$W2dAz_{Q8;t_{ndFQ8V20 zLEnJ%81n7QTx7}O+32%v2zFX^6veB_1AB&FYjaX-1V z)xPZDM&!E&f~4)l8_0ikC1fA!+!UvCyD<2Pz5k7R)I9U&x$#B6FGt?CyfwbL=^uGi zb7kZU`-rs``Mu4HNoH(0>Q7HuK&r(O7A|KpCrnGDZ`ciIyDNgN6#Aq25IsPSa$-8Jf%@l|Vx!lF>$;OQMNJl`J?Ni@`)C z77@oRCX53Ok|qPSvq%=|+G4gCgz7AzMrl^U5w%|R*VEiBZ&JfSx7!N}9V}M7N=9R# zQ3(M*rsHZ*LcT!I>kda?XlzgCKnF`zPea5vnf_L+#{?hK(fhp>JjQrT$7C^N)OnR~ zSn;z|{J$6}=YR36U(J|_j_K%CdOUINy3NqRiMol5POcAeyLh7G@k}*blQZhQuA(XM zcWx`sai79HzofF<85P{HFK~H}3iXO#3ArPIkdQ0nmzS103LIi_sjCRiVY3nns{ua@ zh)WFuT*Sj35W|h1p)efvsNfIODij&2WAGtpy{O=6P`$Mw1b6Y1E=&-fvW+q1P1Vn{F0Zv|1-y-(;Z;OUKR8PMz zlVr1)Mt5(5F&sI)=?!0%aB}HMG>-En(!-aS7@L$Xutl=Qs$*^Nu`idM^lHO?KYX{< z6r*pTt$2S)@HfM^<5!DxJ2p3qPkrh&H)7oC8mNvI!it8qR^#=pHbz?w({Zh@Zz-=Y zbZoLd<+Y+8*DxKUSZg&*`x>XUur rxar&sZYGrJ+@0Jk?k;XNlv!LVm&T=YcSA|z=5TYlpK$Y_%;o+Id-k8v diff --git a/samples/traffic_lights/mapbox/output/tiles/2_2_2_2.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_2_2_2.i3dm deleted file mode 100644 index 0a31dab7bc0f18041a397dbb0e32a8b61ee6a152..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2264 zcmcIk&1)M+6dxzG>NHMTw@Gse#7uh$DC&O5Ye|PJiXz(zvSg%f0x`m@q>;Q-+7-L2 zq+t>G5}H3CJv4z*dMfnR9&&0#PyL{moDxh9p@m)wCI3L7@6GO5TghO2=)l|eoA=(l zH}gK$QrrDGLdc5+LcRh18t5w^TqqA{sZuxcl|r$;Q!ZB4jSZU9l60$DFBzq3h2{d< zyyvrOr{gi7=A==QR;snle0k^Nt!d?|GSSvb>l?peRs%4%X~fiIZ;af`0NJW`_eCo(xnJ50{%L}n@{5b$RaPQlq%kqw>? z*PbpAA;-r*eR==t{_sgKs z3MC~`D7x1IV`!}id2soF+D@Axk{Tm;n%D)jwQKd-E`#ISGAR0{?^>VYCM6}ML+DCe z5yx?3kT*ujfcBZUi#?8x0rCq3bl_Tj%eNr8{AQ*PA)7-Vw?Q-vG$+Sr2Is)C+g1y0 z;9Y%q49%tab(w2HF~T6W*#|k@#IP9DY{3c7al=5(VW-2~n-;svo&0AEleFdZ2ae5b z-?%pbCo8g|qHt(K{#BUZzKLm3RD6qt>5>5X+?G#Zz6QC6;f@7?HyjxRJV!wk{vMHg zOf9BSMB{ls_)J3IEEd$G0xOx!VedOEw3J$bvU8e*M!^;FnGTI+rq8Porg_kGH<@qt zdRD9E47X;v96j(=ht9=Ll&5?t z*nHu|qD#e2Yw66WF?s;Qeyvo&-&eGb^UvJl|GMztQTRB1|L?m9|HzNJ_kj(*6-kds zV8Mkv&V>%Sz^E7dI7fM$PVS#%##Fp6KI|+kW=I>avJCqStMu3S@Jf}8S)NUBJYy-fR@NPa-O_TE&!b;{{rML B5as{? diff --git a/samples/traffic_lights/mapbox/output/tiles/2_2_2_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_2_2_3.i3dm deleted file mode 100644 index f55e78047ad9a4e479ed27448da8b8c6b7fb0906..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2952 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 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_2_3_3.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_2_3_3.i3dm deleted file mode 100644 index d8707a75a739a88abde70c1059f5ab4e04b45041..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2368 zcmcIkO>7%Q6rME1CX`YNEj=M%t~k+p?Krm8cIz4^*~+nly;Q0q%UatLXNA3M?XCmS z%4);`fsg?8&Iu0O%B6@4SVH_98mZy{C!|UU4ycDBkkTG1%6qdr_TpNKD#1!`-#72Q zd2im&jFD({X9ywZ=Lz`~IV`>ajvvZBs+Ou+u9PoS>%~H8Ra>Lkbc}A4tE#4!OEl}z z#;(iCTU!ouX;vDaNsj;@AuC8g52Jwsx7S(*NSeR7F=`^ScxvEwN z|JoXN@7jGfc#(fsNmO;d{ox{>`z64i1o+E?h#LLQm!G>EQC|q~<$DqJL16RQTvYw@ zhHtY2_~^cmZ$-x#{rcaYA`l_}9sYIU!B_P^YTWm=+sB9dpI3(%=iOa8Ed5yE_~yE& ze0(N<4&~2(P#a!V2$!G!YgRd^Z*qL{=Goy--@nZ9_50-Pm0MRhKJ(r^<-?DDQ(k@h z7iI4C_m%TsA1HU;+E@O*c3t`A^6!dAjTX&r(s(kSqA_I4z!Fk?$!92p6$(>QD&eyv zXK9(%Vzj|_nBy94!)&9U3w^`s>3Rn&ro*0P*>2M^9cW_HH5eF95p95x?`4ce{%X+s zc1K$|0`=I75c2aZAs4IVl1FuiPoqcM%w)FiS~ktne7RW6y^@nxi`oiA+hMk2SSBds zMTx?2IZZHz5e|?8X?WDMS`3j?8$gm`<eV zhmApA8^k=?WzIIwVAgAjX7K7?d*x^{VAE?pa+G6%4gT29>{Oh{MXw&NUER&h8 zw%Y?IOK~}Y!oCTwK_Z2ruq<){b1|@75+Ik`@|&KkK<(iX2?c>14h;gHgCH``N7Nom zi)9qic-=?8ap+5heB!Xc@a9a*Z(3++aS7VaX&f2_3*t8!8qIW+%MW#UX&KVRZWNi>c3~=)WoVynxAC=4Br~bRxO)$6hLH!lj(n1Yt>CCf9IR)^5i$!j zLmne@BtoJm{R9t~pb$IGj`&d{0(=K`Kj zExXN-QyKweBPE}Jb}54Vvqmx6vik$uVwPuI4YE3x z#FH>~K{IGn?8sLX8r@7!Okv#SLDM;Ap4sd1R?QxEFDw{=_`FP)73`kv?3*pI$Y)AC zt%yfeGn$goGSiKlR6YqKFTjcgu)E_S*H~N3c-HXnlNLq=QEA1wQt@Qf2xFfiFKd z6Bh9|cOl;e{GwPGDeM!K!?t<+|Ku{u8ZZ~+E%HDgNjcz=P!69`A3noAl1Dr8Q9^x~ z&^}K_y`->R@@PX|o{~pd>f{-DN}d62lILWLydW=uw#X~8O7%Q6rMC~O@RIcC|siv7bGXzUH>GuN>^^k&qj_N?4?qaTFJ(q*eiK=-Q9H| zGDeZgp>RNO;J^VP^~41cD2NJ5BH9awB7yjkkWhrwBSKIRrwYM)vpezPMvZb{rRVp} zdvD&G_cLQNQL_dJA=icoc^CNWu=p7`ekkuytx(o;g~_S%a(=2XqtDW;EXCNk2J$(;S3wAae)W;{cPmh+l&G~j^ zd!R`;e)**z&V1!HxQ+3}Ut6&^SGfG!Yrm`Sw0=>4Tlh|0S=>}DeMf!rpEuM?e|(_6 zcI&o!DgL#3`_EU@4mE2uyF}%rtk5`3O43BYGMvd$z+_-)X*?OQRKU{6loZz~DTP+z zbd_CXo^RGoyN+JZwoR{P7)>zDo7IMI8J>S195G*TTtC>@LlHF$*~nUxc^!=kQbTj znT`z#WlW+l(q0ve;pT+Mg9JO&c4`cfROv$cV%4G5hS{vS47M-Hps)O9JF`TmHV}N>F=vyCOj#hkb*9=P-!E z^AWYj(qb7!G+y`JpI+#5QPf;owzXb(2dvtBi4o4 z4VqkHHQwW1!J}Qir^BgnNEf@2XLj9(sT^0JeTG}d`;|y$(urR8?%50@7u~w+s|=Hi zlPLH3QgHZ!gN28R1WqR>x|Qx_xa?=OLhp$`&>O$O-_c<6e)ypO9q9ESd-jXZ{YB0j zoEl<8WcS5rhopuiu;4--;a&lSjy-K*QuQ Na*8}oo&Y*U{slR!Ol1H7 diff --git a/samples/traffic_lights/mapbox/output/tiles/2_2_6_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/2_2_6_1.i3dm deleted file mode 100644 index a696e5d78895ecf4340da6016f1f26d56ce18970..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2584 zcmc&#O>7%g5PnH%O#-2mkQQ#`apB-b>$T%JmdMr(`Ps;^6MHEtksGb;jkChuHM{FT zS*%9%!VxJF;)2u*2&odK2TCDotO}tbkbnva1PUVjsuBl^#FfI#+kN)pMv8JktTa2{ z{JfcYZ{E)8(Q>Va5b{tzA#VZS2H_TPd{FOFIg?jXnTg5#QhG8ot<2D5L=4mUY+hF6 zY=$OXT0G-u+2v(hb7)d%hG{07n@gpao}BMCMq;s!IVVrgDBT`nA^{%xiBx*B+Z2n6 zV46(jl_f}^%&_>5)zF$p;o1H;B!BYWjajVwn;Rkdg)1J;T@T4W`TChlA$k3>r@!=W zNdDZ%$1jKE_kDaZpdSbQ6<@#bMJHwupMsdG@AKC?A^8~}V;$bJCUHc_|A$WFQo zHCD@P-<-5H^Dx7|-fXt&ul9~(jn{I&Y+n1Z+(P`+>X3B(t1QFH4+pQl^7UEfe_I`F ztuHJv{o}>sE#>FOnEu>$YW&U9F4M1VyeJh8Ke~x=21ajfo*pbRAN$+0tqZU9Uq$`R z_3x$Pcdtw1U;ZKWKJuyb(VrXAXTRN+&cFSk^v2c&>F*C-lAg6LN*hm~lYTePOD@&R zG`UD4u}GALk;Q;T1ttMQDK5n09vk&o!ee8MjS3!%FqZJ7M1&T?w5Y9Wwxd^cqk=h3 zG<3VJs#S1I>!lT^rrHkRfI0x@P0R6o_l^Q#+OWW`^U+FGnd*geT020<);>aJ^Vy6` zRhxZeF0E*WW~q*8(IlP7rqih>QsQ)4nSwG`HOtmb0}SG@K;e_IOW+J&rjKmcqDu|4 ztRWH#O*jB9xwN#RSId?L%NHYHsHu*nKaaw&5EgczV?jd@2c1D*Xog){)9e)-5f~UC zE5N07ORwpU4lQ7nsSczpHXIaPf0n?^362~b1iDeyOXvgps=;PxGRDf)EFC)MB{9?* z=;<7$#iVKpR@kQHB`P+Smo@98u06+s{A-GZY00eBO+zyr4IKB>-}mFPmSf9GpGO#y}F-!>&mnvY$j``{8GgM~lbEv$1n;zdET;PEJI3 zOAJGHr~U1eS{NRIwlnIaM#VAy>Q0Srs>8bA9do^C&1sHWt?H$m*{G}-&;mBwRa=`d ztEM%jme@sJ6owN5dm@oBQ5X})+BeSMxgUlvfGgI7yX!SMr8HMd) zH`1C>ao|>t!cWantqT6cqOq}fw9~yi7Q>YbE^Wuk!_CE;h(E|+EDVt%~on+K=3fehF8)D8mMw#n4@3wJ0@^0e3oTDAFFZYmMvJa?-+(q`210)2r qpWIFQNI$s;sE^!B2FQKnAkYB0pBy3&kU^kB GenerateTiles(Options o, NpgsqlConnection conn, Boundin private static byte[] CreateTile(Options o, NpgsqlConnection conn, BoundingBox tileBounds, int epsg, string where, bool useGpuInstancing = false) { var instances = InstancesRepository.GetInstances(conn, o.Table, o.GeometryColumn, tileBounds, epsg, where, (bool)o.UseScaleNonUniform, useGpuInstancing); - var tile = TileHandler.GetTile(instances, o.Format, (bool)o.UseExternalModel, (bool)o.UseScaleNonUniform, useGpuInstancing); + var tile = TileHandler.GetTile(instances, (bool)o.UseExternalModel, (bool)o.UseScaleNonUniform, useGpuInstancing); return tile; } } diff --git a/src/Options.cs b/src/Options.cs index e4caa9e..a787614 100644 --- a/src/Options.cs +++ b/src/Options.cs @@ -2,11 +2,6 @@ namespace i3dm.export; -public enum Format { - Mapbox, - Cesium -} - public class Options { [Option('c', "connection", Required = true, HelpText = "database connection string")] @@ -29,9 +24,6 @@ public class Options [Option("use_scale_non_uniform", Required = false, Default = false, HelpText = "Use scale_non_uniform")] public bool? UseScaleNonUniform { get; set; } - - [Option('f', "format", Required = false, Default = Format.Cesium, HelpText = "Output format mapbox/cesium")] - public Format Format { get; set; } [Option('q', "query", Required = false, Default = "", HelpText = "Query parameter")] public string Query { get; set; } diff --git a/src/TileHandler.cs b/src/TileHandler.cs index e55657f..dfeaacb 100644 --- a/src/TileHandler.cs +++ b/src/TileHandler.cs @@ -16,7 +16,7 @@ namespace i3dm.export; public static class TileHandler { - public static byte[] GetTile(List instances, Format format, bool UseExternalModel = false, bool UseScaleNonUniform = false, bool useGpuInstancing = false) + public static byte[] GetTile(List instances, bool UseExternalModel = false, bool UseScaleNonUniform = false, bool useGpuInstancing = false) { if (useGpuInstancing && instances.Select(s => s.Model).Distinct().Count() > 1) { @@ -54,7 +54,7 @@ public static byte[] GetTile(List instances, Format format, bool UseEx } else { - CalculateArrays(modelInstances, format, UseScaleNonUniform, positions, scales, scalesNonUniform, normalUps, normalRights, tags); + CalculateArrays(modelInstances, UseScaleNonUniform, positions, scales, scalesNonUniform, normalUps, normalRights, tags); var i3dm = GetI3dm(model, positions, scales, scalesNonUniform, normalUps, normalRights, tags, UseExternalModel, UseScaleNonUniform); var bytesI3dm = I3dmWriter.Write(i3dm); tiles.Add(bytesI3dm); @@ -66,7 +66,7 @@ public static byte[] GetTile(List instances, Format format, bool UseEx return bytes; } - private static void CalculateArrays(List instances, Format format, bool UseScaleNonUniform, List positions, List scales, List scalesNonUniform, List normalUps, List normalRights, List tags) + private static void CalculateArrays(List instances, bool UseScaleNonUniform, List positions, List scales, List scalesNonUniform, List normalUps, List normalRights, List tags) { foreach (var instance in instances) { @@ -84,7 +84,7 @@ private static void CalculateArrays(List instances, Format format, boo { scalesNonUniform.Add(new Vector3((float)instance.ScaleNonUniform[0], (float)instance.ScaleNonUniform[1], (float)instance.ScaleNonUniform[2])); } - var (East, North, Up) = EnuCalculator.GetLocalEnu(format, instance.Rotation, positionVector3); + var (East, North, Up) = EnuCalculator.GetLocalEnu(instance.Rotation, positionVector3); normalUps.Add(Up); normalRights.Add(East); tags.Add(instance.Tags); @@ -116,7 +116,7 @@ private static byte[] GetGpuGlb(object model, List positions, bool Use new Vector3((float)p.ScaleNonUniform[0], (float)p.ScaleNonUniform[1], (float)p.ScaleNonUniform[2]) : new Vector3((float)p.Scale, (float)p.Scale, (float)p.Scale); - var enu = EnuCalculator.GetLocalEnu(Format.Cesium, 0, new Vector3((float)point.X, (float)point.Y, (float)point.Z)); + var enu = EnuCalculator.GetLocalEnu(0, new Vector3((float)point.X, (float)point.Y, (float)point.Z)); var forward = Vector3.Cross(enu.East, enu.Up); forward = Vector3.Normalize(forward); diff --git a/src/i3dm.export.csproj b/src/i3dm.export.csproj index 5a9bb78..df98713 100644 --- a/src/i3dm.export.csproj +++ b/src/i3dm.export.csproj @@ -11,7 +11,7 @@ https://github.com/geodan/i3dm.export https://github.com/geodan/i3dm.export git - i3dm 3dtiles mapbox + i3dm 3dtiles cesium add cesium support Console tool for exporting instanced 3D Tiles (glb or i3dm's) and tileset.json from PostGIS table. Geodan diff --git a/tests/RotationTests.cs b/tests/RotationTests.cs deleted file mode 100644 index 8ebfaf5..0000000 --- a/tests/RotationTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using NUnit.Framework; -using System.Numerics; - -namespace i3dm.export.tests; - -public class RotationTests -{ - [Test] - public void FirstRotationTests() - { - // arrange - var angle = 63.0; - var expectedEast = new Vector3(0.45399f, -0.891007f, 0); - var expectedNorth = new Vector3(0.891007f, 0.45399f, 0); - var expectedUp = new Vector3(0, 0, 1f); - - // act - var localEnu = EnuCalculator.GetLocalEnuMapbox(angle); - - // assert - Assert.That(localEnu.East.Equals(expectedEast)); - Assert.That(localEnu.North.Equals(expectedNorth)); - Assert.That(localEnu.Up.Equals(expectedUp)); - } -} \ No newline at end of file diff --git a/tests/TileHandlerTests.cs b/tests/TileHandlerTests.cs index a93ad83..2cf5b70 100644 --- a/tests/TileHandlerTests.cs +++ b/tests/TileHandlerTests.cs @@ -28,7 +28,7 @@ public void GetGpuTileTest() instances.Add(instance); // act - var tile = TileHandler.GetTile(instances, Format.Cesium, useGpuInstancing: true); + var tile = TileHandler.GetTile(instances, useGpuInstancing: true); var fileName = Path.Combine(TestContext.CurrentContext.WorkDirectory, "ams_building_multiple_colors.glb"); File.WriteAllBytes(fileName, tile); @@ -60,7 +60,7 @@ public void GetTileTest() instances.Add(instance); // act - var tile = TileHandler.GetTile(instances, Format.Mapbox); + var tile = TileHandler.GetTile(instances); var cmpt = CmptReader.Read(new MemoryStream(tile)); var i3dmBytes = cmpt.Tiles.First(); var i3dm = I3dmReader.Read(new MemoryStream(i3dmBytes)); @@ -96,7 +96,7 @@ public void GetCompositeTileTest() instances.Add(instance3); // act - var tile = TileHandler.GetTile(instances, Format.Mapbox, UseExternalModel: true); + var tile = TileHandler.GetTile(instances, UseExternalModel: true); var cmpt = CmptReader.Read(new MemoryStream(tile)); // assert @@ -126,7 +126,7 @@ public void GetTileWithScaleNonUniformTest() instances.Add(instance); // act - var tile = TileHandler.GetTile(instances, Format.Mapbox, UseScaleNonUniform: true); + var tile = TileHandler.GetTile(instances, UseScaleNonUniform: true); var cmpt = CmptReader.Read(new MemoryStream(tile)); var i3dm = I3dmReader.Read(new MemoryStream(cmpt.Tiles.First())); @@ -148,7 +148,7 @@ public void GetTileWithExternalModelTest() instances.Add(instance); // act - var tile = TileHandler.GetTile(instances, Format.Mapbox, UseExternalModel: true); + var tile = TileHandler.GetTile(instances, UseExternalModel: true); var cmpt = CmptReader.Read(new MemoryStream(tile)); var i3dm = I3dmReader.Read(new MemoryStream(cmpt.Tiles.First())); @@ -175,7 +175,7 @@ public void GetTileWithRtcCenterTest() instances.Add(instance1); // act - var tile = TileHandler.GetTile(instances, Format.Mapbox); + var tile = TileHandler.GetTile(instances); var cmpt = CmptReader.Read(new MemoryStream(tile)); var i3dm = I3dmReader.Read(new MemoryStream(cmpt.Tiles.First())); @@ -199,7 +199,7 @@ public void GetTileWithTagsTest() instances.Add(instance); // act - var tile = TileHandler.GetTile(instances, Format.Mapbox); + var tile = TileHandler.GetTile(instances); var cmpt = CmptReader.Read(new MemoryStream(tile)); var i3dm = I3dmReader.Read(new MemoryStream(cmpt.Tiles.First()));

    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 bS5v9E)Iw@0wZf}~^t9AkdPZu4S8M6-iR(A! diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_7_9.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_7_9.i3dm deleted file mode 100644 index b1624e0ca5fcff2d9a96ad003d9345dea605b83f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25856 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 H4J7{sGr_B| diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_8_0.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_8_0.i3dm deleted file mode 100644 index 29bf588bac6b20c8f782c58cfb6bc0ddfb8a4abe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9600 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 Z@QBbs@Dn=X)j{wVItiVHE_ihk{sF07iYfpA diff --git a/samples/traffic_lights/mapbox/output/tiles/1_1_8_1.i3dm b/samples/traffic_lights/mapbox/output/tiles/1_1_8_1.i3dm deleted file mode 100644 index 70f8725eb30a7cbcdd0a721a3c520df51ffe0142..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6400 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