diff --git a/.gitignore b/.gitignore
index 8e077ab5a..fc874a75d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,7 +20,7 @@ bower_components
build/Release
# Dependency directories
-node_modules/three/src
+node_modules
# Optional npm cache directory
.npm
diff --git a/examples/10-geometry/avatar/index.html b/examples/10-geometry/avatar/index.html
index 580f42517..cb570944b 100644
--- a/examples/10-geometry/avatar/index.html
+++ b/examples/10-geometry/avatar/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/10-geometry/boxes-and-barrels/index.html b/examples/10-geometry/boxes-and-barrels/index.html
index 30a923d29..aac9deecf 100644
--- a/examples/10-geometry/boxes-and-barrels/index.html
+++ b/examples/10-geometry/boxes-and-barrels/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/10-geometry/buildings-civil/index.html b/examples/10-geometry/buildings-civil/index.html
index f6e835114..644d13ebf 100644
--- a/examples/10-geometry/buildings-civil/index.html
+++ b/examples/10-geometry/buildings-civil/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/10-geometry/buildings-urban/index.html b/examples/10-geometry/buildings-urban/index.html
index b76ba20fe..5ca5e8059 100644
--- a/examples/10-geometry/buildings-urban/index.html
+++ b/examples/10-geometry/buildings-urban/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/10-geometry/buildings-war/index.html b/examples/10-geometry/buildings-war/index.html
index f12197acd..f5d5ac768 100644
--- a/examples/10-geometry/buildings-war/index.html
+++ b/examples/10-geometry/buildings-war/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/10-geometry/locomotive/index.html b/examples/10-geometry/locomotive/index.html
index b1c34119e..18d4441ce 100644
--- a/examples/10-geometry/locomotive/index.html
+++ b/examples/10-geometry/locomotive/index.html
@@ -14,8 +14,8 @@
diff --git a/examples/10-geometry/trees-cartoon/index.html b/examples/10-geometry/trees-cartoon/index.html
index cc08a96dd..3cb97c171 100644
--- a/examples/10-geometry/trees-cartoon/index.html
+++ b/examples/10-geometry/trees-cartoon/index.html
@@ -14,8 +14,8 @@
diff --git a/examples/10-geometry/trees/index.html b/examples/10-geometry/trees/index.html
index b251b2b4b..420dd3c7b 100644
--- a/examples/10-geometry/trees/index.html
+++ b/examples/10-geometry/trees/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/15-animations/dark-city/index.html b/examples/15-animations/dark-city/index.html
index 7d71c1fb3..8c0a6224c 100644
--- a/examples/15-animations/dark-city/index.html
+++ b/examples/15-animations/dark-city/index.html
@@ -13,8 +13,8 @@
diff --git a/examples/15-animations/parting-the-sea/index.html b/examples/15-animations/parting-the-sea/index.html
index 6f3591b57..5696ef023 100644
--- a/examples/15-animations/parting-the-sea/index.html
+++ b/examples/15-animations/parting-the-sea/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/15-animations/planets/index.html b/examples/15-animations/planets/index.html
index 6762a1d8f..e61ae2819 100644
--- a/examples/15-animations/planets/index.html
+++ b/examples/15-animations/planets/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/15-animations/tank/index.html b/examples/15-animations/tank/index.html
index 074402d98..2061e30bf 100644
--- a/examples/15-animations/tank/index.html
+++ b/examples/15-animations/tank/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/15-animations/train/index.html b/examples/15-animations/train/index.html
index 4a6cf5052..fa8a1c027 100644
--- a/examples/15-animations/train/index.html
+++ b/examples/15-animations/train/index.html
@@ -10,8 +10,8 @@
diff --git a/examples/15-animations/waves/index.html b/examples/15-animations/waves/index.html
index 6f3591b57..5696ef023 100644
--- a/examples/15-animations/waves/index.html
+++ b/examples/15-animations/waves/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/20-particles/explosion/index.html b/examples/20-particles/explosion/index.html
index f17ab1834..dff56a983 100644
--- a/examples/20-particles/explosion/index.html
+++ b/examples/20-particles/explosion/index.html
@@ -10,8 +10,8 @@
Press anywhere
diff --git a/examples/20-particles/fire/index.html b/examples/20-particles/fire/index.html
index 903a9ec25..580063ec7 100644
--- a/examples/20-particles/fire/index.html
+++ b/examples/20-particles/fire/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/20-particles/flame/index.html b/examples/20-particles/flame/index.html
index 0b8c07fb8..deabe74d9 100644
--- a/examples/20-particles/flame/index.html
+++ b/examples/20-particles/flame/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/20-particles/flying-through-space/index.html b/examples/20-particles/flying-through-space/index.html
index 7471fb20a..dc51c6e4e 100644
--- a/examples/20-particles/flying-through-space/index.html
+++ b/examples/20-particles/flying-through-space/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/20-particles/rain/index.html b/examples/20-particles/rain/index.html
index 790845de3..7ece39345 100644
--- a/examples/20-particles/rain/index.html
+++ b/examples/20-particles/rain/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/20-particles/smoke/index.html b/examples/20-particles/smoke/index.html
index 903a9ec25..580063ec7 100644
--- a/examples/20-particles/smoke/index.html
+++ b/examples/20-particles/smoke/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/20-particles/snow/index.html b/examples/20-particles/snow/index.html
index a9d20fc17..1f541008e 100644
--- a/examples/20-particles/snow/index.html
+++ b/examples/20-particles/snow/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/20-particles/stars/index.html b/examples/20-particles/stars/index.html
index c3b7706ad..b70cb92cb 100644
--- a/examples/20-particles/stars/index.html
+++ b/examples/20-particles/stars/index.html
@@ -8,8 +8,8 @@
diff --git a/examples/25-shaders/fireball/index.html b/examples/25-shaders/fireball/index.html
index c0a847b5c..323276e56 100644
--- a/examples/25-shaders/fireball/index.html
+++ b/examples/25-shaders/fireball/index.html
@@ -15,8 +15,8 @@
diff --git a/examples/25-shaders/golden-flow/index.html b/examples/25-shaders/golden-flow/index.html
index c0a847b5c..323276e56 100644
--- a/examples/25-shaders/golden-flow/index.html
+++ b/examples/25-shaders/golden-flow/index.html
@@ -15,8 +15,8 @@
diff --git a/examples/25-shaders/gradient/index.html b/examples/25-shaders/gradient/index.html
index 064c04b55..fac4d0cba 100644
--- a/examples/25-shaders/gradient/index.html
+++ b/examples/25-shaders/gradient/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/25-shaders/lava/index.html b/examples/25-shaders/lava/index.html
index 097926438..8a7a6e657 100644
--- a/examples/25-shaders/lava/index.html
+++ b/examples/25-shaders/lava/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/25-shaders/led/index.html b/examples/25-shaders/led/index.html
index 472e0cb70..f7987122a 100644
--- a/examples/25-shaders/led/index.html
+++ b/examples/25-shaders/led/index.html
@@ -10,8 +10,8 @@
diff --git a/examples/25-shaders/lightning-led/index.html b/examples/25-shaders/lightning-led/index.html
index c0a847b5c..323276e56 100644
--- a/examples/25-shaders/lightning-led/index.html
+++ b/examples/25-shaders/lightning-led/index.html
@@ -15,8 +15,8 @@
diff --git a/examples/25-shaders/marble/index.html b/examples/25-shaders/marble/index.html
index 9d349d786..348d510ad 100644
--- a/examples/25-shaders/marble/index.html
+++ b/examples/25-shaders/marble/index.html
@@ -20,8 +20,8 @@
diff --git a/examples/25-shaders/raging-sea/index.html b/examples/25-shaders/raging-sea/index.html
index 975907081..42aaba053 100644
--- a/examples/25-shaders/raging-sea/index.html
+++ b/examples/25-shaders/raging-sea/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/25-shaders/ripple/index.html b/examples/25-shaders/ripple/index.html
index 097926438..8a7a6e657 100644
--- a/examples/25-shaders/ripple/index.html
+++ b/examples/25-shaders/ripple/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/25-shaders/toon-shader-ghibli/index.html b/examples/25-shaders/toon-shader-ghibli/index.html
index c60a2a7b4..ba076102f 100644
--- a/examples/25-shaders/toon-shader-ghibli/index.html
+++ b/examples/25-shaders/toon-shader-ghibli/index.html
@@ -10,8 +10,8 @@
diff --git a/examples/25-shaders/toon-shader/index.html b/examples/25-shaders/toon-shader/index.html
index c60a2a7b4..ba076102f 100644
--- a/examples/25-shaders/toon-shader/index.html
+++ b/examples/25-shaders/toon-shader/index.html
@@ -10,8 +10,8 @@
diff --git a/examples/25-shaders/voronoi/index.html b/examples/25-shaders/voronoi/index.html
index b62fb1964..5b7445358 100644
--- a/examples/25-shaders/voronoi/index.html
+++ b/examples/25-shaders/voronoi/index.html
@@ -16,8 +16,8 @@
diff --git a/examples/25-shaders/wood/index.html b/examples/25-shaders/wood/index.html
index c70c10e70..4e76d9a19 100644
--- a/examples/25-shaders/wood/index.html
+++ b/examples/25-shaders/wood/index.html
@@ -17,8 +17,8 @@
diff --git a/examples/30-player/aim-at-target/index.html b/examples/30-player/aim-at-target/index.html
index 1674058ab..385f21ebd 100644
--- a/examples/30-player/aim-at-target/index.html
+++ b/examples/30-player/aim-at-target/index.html
@@ -10,8 +10,8 @@
diff --git a/examples/30-player/change-camera/index.html b/examples/30-player/change-camera/index.html
index ec5949f25..d970e6616 100644
--- a/examples/30-player/change-camera/index.html
+++ b/examples/30-player/change-camera/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/30-player/drag-controls/index.html b/examples/30-player/drag-controls/index.html
index 48a681555..0233b51dd 100644
--- a/examples/30-player/drag-controls/index.html
+++ b/examples/30-player/drag-controls/index.html
@@ -22,8 +22,8 @@
diff --git a/examples/30-player/player-collision/index.html b/examples/30-player/player-collision/index.html
index 152aa1568..096d3af63 100644
--- a/examples/30-player/player-collision/index.html
+++ b/examples/30-player/player-collision/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/30-player/third-person-camera-simple/index.html b/examples/30-player/third-person-camera-simple/index.html
index 8801df9f2..09214c48e 100644
--- a/examples/30-player/third-person-camera-simple/index.html
+++ b/examples/30-player/third-person-camera-simple/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/30-player/third-person-camera/index.html b/examples/30-player/third-person-camera/index.html
index c44975c77..ec05211a4 100644
--- a/examples/30-player/third-person-camera/index.html
+++ b/examples/30-player/third-person-camera/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/35-mazes/city-led/index.html b/examples/35-mazes/city-led/index.html
index 327e832eb..58a4221e3 100644
--- a/examples/35-mazes/city-led/index.html
+++ b/examples/35-mazes/city-led/index.html
@@ -13,8 +13,8 @@
diff --git a/examples/35-mazes/city/index.html b/examples/35-mazes/city/index.html
index bb8b33d44..496f935b6 100644
--- a/examples/35-mazes/city/index.html
+++ b/examples/35-mazes/city/index.html
@@ -8,8 +8,8 @@
diff --git a/examples/35-mazes/minas-tirith/index.html b/examples/35-mazes/minas-tirith/index.html
index 3096eacd5..cee019f1b 100644
--- a/examples/35-mazes/minas-tirith/index.html
+++ b/examples/35-mazes/minas-tirith/index.html
@@ -8,8 +8,8 @@
diff --git a/examples/35-mazes/pathfind/index.html b/examples/35-mazes/pathfind/index.html
index c6f31527e..88b2914b6 100644
--- a/examples/35-mazes/pathfind/index.html
+++ b/examples/35-mazes/pathfind/index.html
@@ -8,8 +8,8 @@
diff --git a/examples/35-mazes/pipes/index.html b/examples/35-mazes/pipes/index.html
index 3096eacd5..cee019f1b 100644
--- a/examples/35-mazes/pipes/index.html
+++ b/examples/35-mazes/pipes/index.html
@@ -8,8 +8,8 @@
diff --git a/examples/35-mazes/pyramid/index.html b/examples/35-mazes/pyramid/index.html
index c225acec2..eb2256114 100644
--- a/examples/35-mazes/pyramid/index.html
+++ b/examples/35-mazes/pyramid/index.html
@@ -8,8 +8,8 @@
diff --git a/examples/35-mazes/ruins-polar/index.html b/examples/35-mazes/ruins-polar/index.html
index 3096eacd5..cee019f1b 100644
--- a/examples/35-mazes/ruins-polar/index.html
+++ b/examples/35-mazes/ruins-polar/index.html
@@ -8,8 +8,8 @@
diff --git a/examples/35-mazes/ruins/index.html b/examples/35-mazes/ruins/index.html
index 074adf368..f304710c4 100644
--- a/examples/35-mazes/ruins/index.html
+++ b/examples/35-mazes/ruins/index.html
@@ -13,8 +13,8 @@
diff --git a/examples/35-mazes/tilemap/index.html b/examples/35-mazes/tilemap/index.html
index 3eba7f294..1a6452674 100644
--- a/examples/35-mazes/tilemap/index.html
+++ b/examples/35-mazes/tilemap/index.html
@@ -10,8 +10,8 @@
diff --git a/examples/35-mazes/walls/index.html b/examples/35-mazes/walls/index.html
index c6b01f7bf..f22aa0681 100644
--- a/examples/35-mazes/walls/index.html
+++ b/examples/35-mazes/walls/index.html
@@ -8,8 +8,8 @@
diff --git a/examples/40-procedural/building/index.html b/examples/40-procedural/building/index.html
index b76ba20fe..5ca5e8059 100644
--- a/examples/40-procedural/building/index.html
+++ b/examples/40-procedural/building/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/40-procedural/castle/index.html b/examples/40-procedural/castle/index.html
index e3b4f03d7..af5db5a24 100644
--- a/examples/40-procedural/castle/index.html
+++ b/examples/40-procedural/castle/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/40-procedural/city-colorful/index.html b/examples/40-procedural/city-colorful/index.html
index b53fd59a8..0f325ab70 100644
--- a/examples/40-procedural/city-colorful/index.html
+++ b/examples/40-procedural/city-colorful/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/40-procedural/city-empty-center/index.html b/examples/40-procedural/city-empty-center/index.html
index b53fd59a8..0f325ab70 100644
--- a/examples/40-procedural/city-empty-center/index.html
+++ b/examples/40-procedural/city-empty-center/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/40-procedural/city-graffiti/index.html b/examples/40-procedural/city-graffiti/index.html
index 67f74794d..c1831190e 100644
--- a/examples/40-procedural/city-graffiti/index.html
+++ b/examples/40-procedural/city-graffiti/index.html
@@ -13,8 +13,8 @@
diff --git a/examples/40-procedural/city-night/index.html b/examples/40-procedural/city-night/index.html
index b53fd59a8..0f325ab70 100644
--- a/examples/40-procedural/city-night/index.html
+++ b/examples/40-procedural/city-night/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/40-procedural/city/index.html b/examples/40-procedural/city/index.html
index 46c8d71b3..54fe9298c 100644
--- a/examples/40-procedural/city/index.html
+++ b/examples/40-procedural/city/index.html
@@ -16,8 +16,8 @@
diff --git a/examples/40-procedural/towers/index.html b/examples/40-procedural/towers/index.html
index f0820304c..ea1ca5463 100644
--- a/examples/40-procedural/towers/index.html
+++ b/examples/40-procedural/towers/index.html
@@ -13,8 +13,8 @@
diff --git a/examples/45-terrain/craters/index.html b/examples/45-terrain/craters/index.html
index de1c2f25b..01d5410be 100644
--- a/examples/45-terrain/craters/index.html
+++ b/examples/45-terrain/craters/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/45-terrain/dunes/index.html b/examples/45-terrain/dunes/index.html
index 13e268342..158623a5b 100644
--- a/examples/45-terrain/dunes/index.html
+++ b/examples/45-terrain/dunes/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/45-terrain/heightmap-europe/index.html b/examples/45-terrain/heightmap-europe/index.html
index 1df32cd7b..21adfa2c9 100644
--- a/examples/45-terrain/heightmap-europe/index.html
+++ b/examples/45-terrain/heightmap-europe/index.html
@@ -14,8 +14,8 @@
diff --git a/examples/45-terrain/heightmap-yu/index.html b/examples/45-terrain/heightmap-yu/index.html
index 9a87da729..333d233fd 100644
--- a/examples/45-terrain/heightmap-yu/index.html
+++ b/examples/45-terrain/heightmap-yu/index.html
@@ -16,8 +16,8 @@
diff --git a/examples/45-terrain/heightmap/index.html b/examples/45-terrain/heightmap/index.html
index 1df32cd7b..21adfa2c9 100644
--- a/examples/45-terrain/heightmap/index.html
+++ b/examples/45-terrain/heightmap/index.html
@@ -14,8 +14,8 @@
diff --git a/examples/45-terrain/hilly-terrain/index.html b/examples/45-terrain/hilly-terrain/index.html
index d1e1e2323..c90730c30 100644
--- a/examples/45-terrain/hilly-terrain/index.html
+++ b/examples/45-terrain/hilly-terrain/index.html
@@ -14,8 +14,8 @@
diff --git a/examples/45-terrain/random-terrain/index.html b/examples/45-terrain/random-terrain/index.html
index 737ccea50..bb925bffb 100644
--- a/examples/45-terrain/random-terrain/index.html
+++ b/examples/45-terrain/random-terrain/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/50-physics/ammo-basic/index.html b/examples/50-physics/ammo-basic/index.html
index e4e35538c..bdd3fa70f 100644
--- a/examples/50-physics/ammo-basic/index.html
+++ b/examples/50-physics/ammo-basic/index.html
@@ -10,8 +10,8 @@
diff --git a/examples/50-physics/ball-shoot-wall/index.html b/examples/50-physics/ball-shoot-wall/index.html
index f19c0a482..e1a632f2a 100644
--- a/examples/50-physics/ball-shoot-wall/index.html
+++ b/examples/50-physics/ball-shoot-wall/index.html
@@ -18,8 +18,8 @@
diff --git a/examples/50-physics/physics-vehicle/index.html b/examples/50-physics/physics-vehicle/index.html
index 2f4b35a8d..8b96ee95a 100644
--- a/examples/50-physics/physics-vehicle/index.html
+++ b/examples/50-physics/physics-vehicle/index.html
@@ -7,8 +7,8 @@
diff --git a/examples/60-models/barrel/index.html b/examples/60-models/barrel/index.html
index ef5fbcd74..a964bef58 100644
--- a/examples/60-models/barrel/index.html
+++ b/examples/60-models/barrel/index.html
@@ -10,8 +10,8 @@
diff --git a/examples/60-models/birds/index.html b/examples/60-models/birds/index.html
index 61082d9b9..24f9a62dc 100644
--- a/examples/60-models/birds/index.html
+++ b/examples/60-models/birds/index.html
@@ -13,8 +13,8 @@
diff --git a/examples/60-models/castle-texture/index.html b/examples/60-models/castle-texture/index.html
index c011b045b..e5f98e582 100644
--- a/examples/60-models/castle-texture/index.html
+++ b/examples/60-models/castle-texture/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/60-models/castles/index.html b/examples/60-models/castles/index.html
index 7fb48032a..0db6acc1b 100644
--- a/examples/60-models/castles/index.html
+++ b/examples/60-models/castles/index.html
@@ -17,8 +17,8 @@
diff --git a/examples/60-models/horse/index.html b/examples/60-models/horse/index.html
index 8377e95d5..1b803e028 100644
--- a/examples/60-models/horse/index.html
+++ b/examples/60-models/horse/index.html
@@ -17,8 +17,8 @@
diff --git a/examples/60-models/house-inside/index.html b/examples/60-models/house-inside/index.html
index 9167a1202..fa84a27c0 100644
--- a/examples/60-models/house-inside/index.html
+++ b/examples/60-models/house-inside/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/60-models/model-lookat-cursor/index.html b/examples/60-models/model-lookat-cursor/index.html
index 3694ee037..bdd2ae73c 100644
--- a/examples/60-models/model-lookat-cursor/index.html
+++ b/examples/60-models/model-lookat-cursor/index.html
@@ -13,8 +13,8 @@
diff --git a/examples/60-models/rifle/index.html b/examples/60-models/rifle/index.html
index e1883fba0..d5d9d167e 100644
--- a/examples/60-models/rifle/index.html
+++ b/examples/60-models/rifle/index.html
@@ -10,8 +10,8 @@
diff --git a/examples/65-characters/fantasy/barbarian/index.html b/examples/65-characters/fantasy/barbarian/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/fantasy/barbarian/index.html
+++ b/examples/65-characters/fantasy/barbarian/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/fantasy/demon/index.html b/examples/65-characters/fantasy/demon/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/fantasy/demon/index.html
+++ b/examples/65-characters/fantasy/demon/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/fantasy/goblin/index.html b/examples/65-characters/fantasy/goblin/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/fantasy/goblin/index.html
+++ b/examples/65-characters/fantasy/goblin/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/fantasy/golem/index.html b/examples/65-characters/fantasy/golem/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/fantasy/golem/index.html
+++ b/examples/65-characters/fantasy/golem/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/fantasy/orc-ogre/index.html b/examples/65-characters/fantasy/orc-ogre/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/fantasy/orc-ogre/index.html
+++ b/examples/65-characters/fantasy/orc-ogre/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/fantasy/orc/index.html b/examples/65-characters/fantasy/orc/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/fantasy/orc/index.html
+++ b/examples/65-characters/fantasy/orc/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/fantasy/sorceress/index.html b/examples/65-characters/fantasy/sorceress/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/fantasy/sorceress/index.html
+++ b/examples/65-characters/fantasy/sorceress/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/fantasy/troll/index.html b/examples/65-characters/fantasy/troll/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/fantasy/troll/index.html
+++ b/examples/65-characters/fantasy/troll/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/fantasy/witch/index.html b/examples/65-characters/fantasy/witch/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/fantasy/witch/index.html
+++ b/examples/65-characters/fantasy/witch/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/horror/goth-girl/index.html b/examples/65-characters/horror/goth-girl/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/horror/goth-girl/index.html
+++ b/examples/65-characters/horror/goth-girl/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/horror/nude-victim/index.html b/examples/65-characters/horror/nude-victim/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/horror/nude-victim/index.html
+++ b/examples/65-characters/horror/nude-victim/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/horror/skeleton/index.html b/examples/65-characters/horror/skeleton/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/horror/skeleton/index.html
+++ b/examples/65-characters/horror/skeleton/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/horror/zombie-barefoot/index.html b/examples/65-characters/horror/zombie-barefoot/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/horror/zombie-barefoot/index.html
+++ b/examples/65-characters/horror/zombie-barefoot/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/horror/zombie-cop/index.html b/examples/65-characters/horror/zombie-cop/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/horror/zombie-cop/index.html
+++ b/examples/65-characters/horror/zombie-cop/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/horror/zombie-doctor-crawl/index.html b/examples/65-characters/horror/zombie-doctor-crawl/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/horror/zombie-doctor-crawl/index.html
+++ b/examples/65-characters/horror/zombie-doctor-crawl/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/horror/zombie-doctor/index.html b/examples/65-characters/horror/zombie-doctor/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/horror/zombie-doctor/index.html
+++ b/examples/65-characters/horror/zombie-doctor/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/horror/zombie-guard/index.html b/examples/65-characters/horror/zombie-guard/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/horror/zombie-guard/index.html
+++ b/examples/65-characters/horror/zombie-guard/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/ww2/german-flame-thrower/index.html b/examples/65-characters/ww2/german-flame-thrower/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/ww2/german-flame-thrower/index.html
+++ b/examples/65-characters/ww2/german-flame-thrower/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/ww2/german-machine-gunner/index.html b/examples/65-characters/ww2/german-machine-gunner/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/ww2/german-machine-gunner/index.html
+++ b/examples/65-characters/ww2/german-machine-gunner/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/ww2/german-soldier/index.html b/examples/65-characters/ww2/german-soldier/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/ww2/german-soldier/index.html
+++ b/examples/65-characters/ww2/german-soldier/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/ww2/nazi-agent/index.html b/examples/65-characters/ww2/nazi-agent/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/ww2/nazi-agent/index.html
+++ b/examples/65-characters/ww2/nazi-agent/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/ww2/nazi-officer/index.html b/examples/65-characters/ww2/nazi-officer/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/ww2/nazi-officer/index.html
+++ b/examples/65-characters/ww2/nazi-officer/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/ww2/nazi/index.html b/examples/65-characters/ww2/nazi/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/ww2/nazi/index.html
+++ b/examples/65-characters/ww2/nazi/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/ww2/partisan-lowpoly/index.html b/examples/65-characters/ww2/partisan-lowpoly/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/ww2/partisan-lowpoly/index.html
+++ b/examples/65-characters/ww2/partisan-lowpoly/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/ww2/partisan/index.html b/examples/65-characters/ww2/partisan/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/ww2/partisan/index.html
+++ b/examples/65-characters/ww2/partisan/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/ww2/resistance-fighter/index.html b/examples/65-characters/ww2/resistance-fighter/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/ww2/resistance-fighter/index.html
+++ b/examples/65-characters/ww2/resistance-fighter/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/ww2/soldier/index.html b/examples/65-characters/ww2/soldier/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/ww2/soldier/index.html
+++ b/examples/65-characters/ww2/soldier/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/65-characters/ww2/soviet-partisan/index.html b/examples/65-characters/ww2/soviet-partisan/index.html
index c44975c77..ec05211a4 100644
--- a/examples/65-characters/ww2/soviet-partisan/index.html
+++ b/examples/65-characters/ww2/soviet-partisan/index.html
@@ -9,8 +9,8 @@
diff --git a/examples/70-ai/flee/index.html b/examples/70-ai/flee/index.html
index a41cd79f9..a005fd9fe 100644
--- a/examples/70-ai/flee/index.html
+++ b/examples/70-ai/flee/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/70-ai/follow/index.html b/examples/70-ai/follow/index.html
index a41cd79f9..a005fd9fe 100644
--- a/examples/70-ai/follow/index.html
+++ b/examples/70-ai/follow/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/70-ai/idle/index.html b/examples/70-ai/idle/index.html
index a41cd79f9..a005fd9fe 100644
--- a/examples/70-ai/idle/index.html
+++ b/examples/70-ai/idle/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/70-ai/patrol/index.html b/examples/70-ai/patrol/index.html
index a41cd79f9..a005fd9fe 100644
--- a/examples/70-ai/patrol/index.html
+++ b/examples/70-ai/patrol/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/70-ai/pursue/index.html b/examples/70-ai/pursue/index.html
index a41cd79f9..a005fd9fe 100644
--- a/examples/70-ai/pursue/index.html
+++ b/examples/70-ai/pursue/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/70-ai/wander/index.html b/examples/70-ai/wander/index.html
index a41cd79f9..a005fd9fe 100644
--- a/examples/70-ai/wander/index.html
+++ b/examples/70-ai/wander/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/80-scenes/avatar-planets/index.html b/examples/80-scenes/avatar-planets/index.html
index adb7a27ee..56b171dbd 100644
--- a/examples/80-scenes/avatar-planets/index.html
+++ b/examples/80-scenes/avatar-planets/index.html
@@ -13,8 +13,8 @@
diff --git a/examples/80-scenes/fps-airport/index.html b/examples/80-scenes/fps-airport/index.html
index a5f39fb96..bdcd8ec83 100644
--- a/examples/80-scenes/fps-airport/index.html
+++ b/examples/80-scenes/fps-airport/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/80-scenes/fps-maze/index.html b/examples/80-scenes/fps-maze/index.html
index a5f39fb96..bdcd8ec83 100644
--- a/examples/80-scenes/fps-maze/index.html
+++ b/examples/80-scenes/fps-maze/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/80-scenes/graveyard-survival/index.html b/examples/80-scenes/graveyard-survival/index.html
index c59a1b236..1b255ff0c 100644
--- a/examples/80-scenes/graveyard-survival/index.html
+++ b/examples/80-scenes/graveyard-survival/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/80-scenes/moon-landing/index.html b/examples/80-scenes/moon-landing/index.html
index 7a44b4f37..c72751e18 100644
--- a/examples/80-scenes/moon-landing/index.html
+++ b/examples/80-scenes/moon-landing/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/80-scenes/physics-cannon/index.html b/examples/80-scenes/physics-cannon/index.html
index b1a39967d..ce1f830ad 100644
--- a/examples/80-scenes/physics-cannon/index.html
+++ b/examples/80-scenes/physics-cannon/index.html
@@ -10,8 +10,8 @@
diff --git a/examples/80-scenes/physics-tank/index.html b/examples/80-scenes/physics-tank/index.html
index 904eb4803..ee182ca34 100644
--- a/examples/80-scenes/physics-tank/index.html
+++ b/examples/80-scenes/physics-tank/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/80-scenes/random-boxes/index.html b/examples/80-scenes/random-boxes/index.html
index 6eb77f814..cbb81cb72 100644
--- a/examples/80-scenes/random-boxes/index.html
+++ b/examples/80-scenes/random-boxes/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/80-scenes/rpg-fantasy/index.html b/examples/80-scenes/rpg-fantasy/index.html
index 8dca6d268..0ad01772b 100644
--- a/examples/80-scenes/rpg-fantasy/index.html
+++ b/examples/80-scenes/rpg-fantasy/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/80-scenes/spomeniks/index.html b/examples/80-scenes/spomeniks/index.html
index 48d97c087..fcf2293ef 100644
--- a/examples/80-scenes/spomeniks/index.html
+++ b/examples/80-scenes/spomeniks/index.html
@@ -11,8 +11,8 @@
diff --git a/examples/80-scenes/urban-guerrilla/index.html b/examples/80-scenes/urban-guerrilla/index.html
index 67f74794d..c1831190e 100644
--- a/examples/80-scenes/urban-guerrilla/index.html
+++ b/examples/80-scenes/urban-guerrilla/index.html
@@ -13,8 +13,8 @@
diff --git a/examples/80-scenes/warplane/index.html b/examples/80-scenes/warplane/index.html
index 061d3c7f4..bf05b43ee 100644
--- a/examples/80-scenes/warplane/index.html
+++ b/examples/80-scenes/warplane/index.html
@@ -12,8 +12,8 @@
diff --git a/examples/80-scenes/zeppelin/index.html b/examples/80-scenes/zeppelin/index.html
index 3d752f27b..640e1965a 100644
--- a/examples/80-scenes/zeppelin/index.html
+++ b/examples/80-scenes/zeppelin/index.html
@@ -14,8 +14,8 @@
diff --git a/node_modules/three/build/three.cjs b/libs/three/build/three.cjs
similarity index 100%
rename from node_modules/three/build/three.cjs
rename to libs/three/build/three.cjs
diff --git a/node_modules/three/build/three.js b/libs/three/build/three.js
similarity index 100%
rename from node_modules/three/build/three.js
rename to libs/three/build/three.js
diff --git a/node_modules/three/build/three.min.js b/libs/three/build/three.min.js
similarity index 100%
rename from node_modules/three/build/three.min.js
rename to libs/three/build/three.min.js
diff --git a/node_modules/three/build/three.module.js b/libs/three/build/three.module.js
similarity index 100%
rename from node_modules/three/build/three.module.js
rename to libs/three/build/three.module.js
diff --git a/node_modules/three/build/three.module.min.js b/libs/three/build/three.module.min.js
similarity index 100%
rename from node_modules/three/build/three.module.min.js
rename to libs/three/build/three.module.min.js
diff --git a/node_modules/three/examples/fonts/LICENSE b/libs/three/examples/fonts/LICENSE
similarity index 100%
rename from node_modules/three/examples/fonts/LICENSE
rename to libs/three/examples/fonts/LICENSE
diff --git a/node_modules/three/examples/fonts/README.md b/libs/three/examples/fonts/README.md
similarity index 100%
rename from node_modules/three/examples/fonts/README.md
rename to libs/three/examples/fonts/README.md
diff --git a/node_modules/three/examples/fonts/droid/NOTICE b/libs/three/examples/fonts/droid/NOTICE
similarity index 100%
rename from node_modules/three/examples/fonts/droid/NOTICE
rename to libs/three/examples/fonts/droid/NOTICE
diff --git a/node_modules/three/examples/fonts/droid/README.txt b/libs/three/examples/fonts/droid/README.txt
similarity index 100%
rename from node_modules/three/examples/fonts/droid/README.txt
rename to libs/three/examples/fonts/droid/README.txt
diff --git a/node_modules/three/examples/fonts/droid/droid_sans_bold.typeface.json b/libs/three/examples/fonts/droid/droid_sans_bold.typeface.json
similarity index 100%
rename from node_modules/three/examples/fonts/droid/droid_sans_bold.typeface.json
rename to libs/three/examples/fonts/droid/droid_sans_bold.typeface.json
diff --git a/node_modules/three/examples/fonts/droid/droid_sans_mono_regular.typeface.json b/libs/three/examples/fonts/droid/droid_sans_mono_regular.typeface.json
similarity index 100%
rename from node_modules/three/examples/fonts/droid/droid_sans_mono_regular.typeface.json
rename to libs/three/examples/fonts/droid/droid_sans_mono_regular.typeface.json
diff --git a/node_modules/three/examples/fonts/droid/droid_sans_regular.typeface.json b/libs/three/examples/fonts/droid/droid_sans_regular.typeface.json
similarity index 100%
rename from node_modules/three/examples/fonts/droid/droid_sans_regular.typeface.json
rename to libs/three/examples/fonts/droid/droid_sans_regular.typeface.json
diff --git a/node_modules/three/examples/fonts/droid/droid_serif_bold.typeface.json b/libs/three/examples/fonts/droid/droid_serif_bold.typeface.json
similarity index 100%
rename from node_modules/three/examples/fonts/droid/droid_serif_bold.typeface.json
rename to libs/three/examples/fonts/droid/droid_serif_bold.typeface.json
diff --git a/node_modules/three/examples/fonts/droid/droid_serif_regular.typeface.json b/libs/three/examples/fonts/droid/droid_serif_regular.typeface.json
similarity index 100%
rename from node_modules/three/examples/fonts/droid/droid_serif_regular.typeface.json
rename to libs/three/examples/fonts/droid/droid_serif_regular.typeface.json
diff --git a/node_modules/three/examples/fonts/gentilis_bold.typeface.json b/libs/three/examples/fonts/gentilis_bold.typeface.json
similarity index 100%
rename from node_modules/three/examples/fonts/gentilis_bold.typeface.json
rename to libs/three/examples/fonts/gentilis_bold.typeface.json
diff --git a/node_modules/three/examples/fonts/gentilis_regular.typeface.json b/libs/three/examples/fonts/gentilis_regular.typeface.json
similarity index 100%
rename from node_modules/three/examples/fonts/gentilis_regular.typeface.json
rename to libs/three/examples/fonts/gentilis_regular.typeface.json
diff --git a/node_modules/three/examples/fonts/helvetiker_bold.typeface.json b/libs/three/examples/fonts/helvetiker_bold.typeface.json
similarity index 100%
rename from node_modules/three/examples/fonts/helvetiker_bold.typeface.json
rename to libs/three/examples/fonts/helvetiker_bold.typeface.json
diff --git a/node_modules/three/examples/fonts/helvetiker_regular.typeface.json b/libs/three/examples/fonts/helvetiker_regular.typeface.json
similarity index 100%
rename from node_modules/three/examples/fonts/helvetiker_regular.typeface.json
rename to libs/three/examples/fonts/helvetiker_regular.typeface.json
diff --git a/node_modules/three/examples/fonts/optimer_bold.typeface.json b/libs/three/examples/fonts/optimer_bold.typeface.json
similarity index 100%
rename from node_modules/three/examples/fonts/optimer_bold.typeface.json
rename to libs/three/examples/fonts/optimer_bold.typeface.json
diff --git a/node_modules/three/examples/fonts/optimer_regular.typeface.json b/libs/three/examples/fonts/optimer_regular.typeface.json
similarity index 100%
rename from node_modules/three/examples/fonts/optimer_regular.typeface.json
rename to libs/three/examples/fonts/optimer_regular.typeface.json
diff --git a/node_modules/three/examples/fonts/ttf/README.md b/libs/three/examples/fonts/ttf/README.md
similarity index 100%
rename from node_modules/three/examples/fonts/ttf/README.md
rename to libs/three/examples/fonts/ttf/README.md
diff --git a/node_modules/three/examples/fonts/ttf/kenpixel.ttf b/libs/three/examples/fonts/ttf/kenpixel.ttf
similarity index 100%
rename from node_modules/three/examples/fonts/ttf/kenpixel.ttf
rename to libs/three/examples/fonts/ttf/kenpixel.ttf
diff --git a/node_modules/three/examples/jsm/Addons.js b/libs/three/examples/jsm/Addons.js
similarity index 100%
rename from node_modules/three/examples/jsm/Addons.js
rename to libs/three/examples/jsm/Addons.js
diff --git a/node_modules/three/examples/jsm/animation/AnimationClipCreator.js b/libs/three/examples/jsm/animation/AnimationClipCreator.js
similarity index 100%
rename from node_modules/three/examples/jsm/animation/AnimationClipCreator.js
rename to libs/three/examples/jsm/animation/AnimationClipCreator.js
diff --git a/node_modules/three/examples/jsm/animation/CCDIKSolver.js b/libs/three/examples/jsm/animation/CCDIKSolver.js
similarity index 100%
rename from node_modules/three/examples/jsm/animation/CCDIKSolver.js
rename to libs/three/examples/jsm/animation/CCDIKSolver.js
diff --git a/node_modules/three/examples/jsm/animation/MMDAnimationHelper.js b/libs/three/examples/jsm/animation/MMDAnimationHelper.js
similarity index 100%
rename from node_modules/three/examples/jsm/animation/MMDAnimationHelper.js
rename to libs/three/examples/jsm/animation/MMDAnimationHelper.js
diff --git a/node_modules/three/examples/jsm/animation/MMDPhysics.js b/libs/three/examples/jsm/animation/MMDPhysics.js
similarity index 100%
rename from node_modules/three/examples/jsm/animation/MMDPhysics.js
rename to libs/three/examples/jsm/animation/MMDPhysics.js
diff --git a/node_modules/three/examples/jsm/cameras/CinematicCamera.js b/libs/three/examples/jsm/cameras/CinematicCamera.js
similarity index 100%
rename from node_modules/three/examples/jsm/cameras/CinematicCamera.js
rename to libs/three/examples/jsm/cameras/CinematicCamera.js
diff --git a/node_modules/three/examples/jsm/capabilities/WebGL.js b/libs/three/examples/jsm/capabilities/WebGL.js
similarity index 100%
rename from node_modules/three/examples/jsm/capabilities/WebGL.js
rename to libs/three/examples/jsm/capabilities/WebGL.js
diff --git a/node_modules/three/examples/jsm/capabilities/WebGPU.js b/libs/three/examples/jsm/capabilities/WebGPU.js
similarity index 100%
rename from node_modules/three/examples/jsm/capabilities/WebGPU.js
rename to libs/three/examples/jsm/capabilities/WebGPU.js
diff --git a/node_modules/three/examples/jsm/controls/ArcballControls.js b/libs/three/examples/jsm/controls/ArcballControls.js
similarity index 100%
rename from node_modules/three/examples/jsm/controls/ArcballControls.js
rename to libs/three/examples/jsm/controls/ArcballControls.js
diff --git a/node_modules/three/examples/jsm/controls/DragControls.js b/libs/three/examples/jsm/controls/DragControls.js
similarity index 100%
rename from node_modules/three/examples/jsm/controls/DragControls.js
rename to libs/three/examples/jsm/controls/DragControls.js
diff --git a/node_modules/three/examples/jsm/controls/FirstPersonControls.js b/libs/three/examples/jsm/controls/FirstPersonControls.js
similarity index 100%
rename from node_modules/three/examples/jsm/controls/FirstPersonControls.js
rename to libs/three/examples/jsm/controls/FirstPersonControls.js
diff --git a/node_modules/three/examples/jsm/controls/FlyControls.js b/libs/three/examples/jsm/controls/FlyControls.js
similarity index 100%
rename from node_modules/three/examples/jsm/controls/FlyControls.js
rename to libs/three/examples/jsm/controls/FlyControls.js
diff --git a/node_modules/three/examples/jsm/controls/MapControls.js b/libs/three/examples/jsm/controls/MapControls.js
similarity index 100%
rename from node_modules/three/examples/jsm/controls/MapControls.js
rename to libs/three/examples/jsm/controls/MapControls.js
diff --git a/node_modules/three/examples/jsm/controls/OrbitControls.js b/libs/three/examples/jsm/controls/OrbitControls.js
similarity index 100%
rename from node_modules/three/examples/jsm/controls/OrbitControls.js
rename to libs/three/examples/jsm/controls/OrbitControls.js
diff --git a/node_modules/three/examples/jsm/controls/PointerLockControls.js b/libs/three/examples/jsm/controls/PointerLockControls.js
similarity index 100%
rename from node_modules/three/examples/jsm/controls/PointerLockControls.js
rename to libs/three/examples/jsm/controls/PointerLockControls.js
diff --git a/node_modules/three/examples/jsm/controls/TrackballControls.js b/libs/three/examples/jsm/controls/TrackballControls.js
similarity index 100%
rename from node_modules/three/examples/jsm/controls/TrackballControls.js
rename to libs/three/examples/jsm/controls/TrackballControls.js
diff --git a/node_modules/three/examples/jsm/controls/TransformControls.js b/libs/three/examples/jsm/controls/TransformControls.js
similarity index 100%
rename from node_modules/three/examples/jsm/controls/TransformControls.js
rename to libs/three/examples/jsm/controls/TransformControls.js
diff --git a/node_modules/three/examples/jsm/csm/CSM.js b/libs/three/examples/jsm/csm/CSM.js
similarity index 100%
rename from node_modules/three/examples/jsm/csm/CSM.js
rename to libs/three/examples/jsm/csm/CSM.js
diff --git a/node_modules/three/examples/jsm/csm/CSMFrustum.js b/libs/three/examples/jsm/csm/CSMFrustum.js
similarity index 100%
rename from node_modules/three/examples/jsm/csm/CSMFrustum.js
rename to libs/three/examples/jsm/csm/CSMFrustum.js
diff --git a/node_modules/three/examples/jsm/csm/CSMHelper.js b/libs/three/examples/jsm/csm/CSMHelper.js
similarity index 100%
rename from node_modules/three/examples/jsm/csm/CSMHelper.js
rename to libs/three/examples/jsm/csm/CSMHelper.js
diff --git a/node_modules/three/examples/jsm/csm/CSMShader.js b/libs/three/examples/jsm/csm/CSMShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/csm/CSMShader.js
rename to libs/three/examples/jsm/csm/CSMShader.js
diff --git a/node_modules/three/examples/jsm/curves/CurveExtras.js b/libs/three/examples/jsm/curves/CurveExtras.js
similarity index 100%
rename from node_modules/three/examples/jsm/curves/CurveExtras.js
rename to libs/three/examples/jsm/curves/CurveExtras.js
diff --git a/node_modules/three/examples/jsm/curves/NURBSCurve.js b/libs/three/examples/jsm/curves/NURBSCurve.js
similarity index 100%
rename from node_modules/three/examples/jsm/curves/NURBSCurve.js
rename to libs/three/examples/jsm/curves/NURBSCurve.js
diff --git a/node_modules/three/examples/jsm/curves/NURBSSurface.js b/libs/three/examples/jsm/curves/NURBSSurface.js
similarity index 100%
rename from node_modules/three/examples/jsm/curves/NURBSSurface.js
rename to libs/three/examples/jsm/curves/NURBSSurface.js
diff --git a/node_modules/three/examples/jsm/curves/NURBSUtils.js b/libs/three/examples/jsm/curves/NURBSUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/curves/NURBSUtils.js
rename to libs/three/examples/jsm/curves/NURBSUtils.js
diff --git a/node_modules/three/examples/jsm/effects/AnaglyphEffect.js b/libs/three/examples/jsm/effects/AnaglyphEffect.js
similarity index 100%
rename from node_modules/three/examples/jsm/effects/AnaglyphEffect.js
rename to libs/three/examples/jsm/effects/AnaglyphEffect.js
diff --git a/node_modules/three/examples/jsm/effects/AsciiEffect.js b/libs/three/examples/jsm/effects/AsciiEffect.js
similarity index 100%
rename from node_modules/three/examples/jsm/effects/AsciiEffect.js
rename to libs/three/examples/jsm/effects/AsciiEffect.js
diff --git a/node_modules/three/examples/jsm/effects/OutlineEffect.js b/libs/three/examples/jsm/effects/OutlineEffect.js
similarity index 100%
rename from node_modules/three/examples/jsm/effects/OutlineEffect.js
rename to libs/three/examples/jsm/effects/OutlineEffect.js
diff --git a/node_modules/three/examples/jsm/effects/ParallaxBarrierEffect.js b/libs/three/examples/jsm/effects/ParallaxBarrierEffect.js
similarity index 100%
rename from node_modules/three/examples/jsm/effects/ParallaxBarrierEffect.js
rename to libs/three/examples/jsm/effects/ParallaxBarrierEffect.js
diff --git a/node_modules/three/examples/jsm/effects/PeppersGhostEffect.js b/libs/three/examples/jsm/effects/PeppersGhostEffect.js
similarity index 100%
rename from node_modules/three/examples/jsm/effects/PeppersGhostEffect.js
rename to libs/three/examples/jsm/effects/PeppersGhostEffect.js
diff --git a/node_modules/three/examples/jsm/effects/StereoEffect.js b/libs/three/examples/jsm/effects/StereoEffect.js
similarity index 100%
rename from node_modules/three/examples/jsm/effects/StereoEffect.js
rename to libs/three/examples/jsm/effects/StereoEffect.js
diff --git a/node_modules/three/examples/jsm/environments/DebugEnvironment.js b/libs/three/examples/jsm/environments/DebugEnvironment.js
similarity index 100%
rename from node_modules/three/examples/jsm/environments/DebugEnvironment.js
rename to libs/three/examples/jsm/environments/DebugEnvironment.js
diff --git a/node_modules/three/examples/jsm/environments/RoomEnvironment.js b/libs/three/examples/jsm/environments/RoomEnvironment.js
similarity index 100%
rename from node_modules/three/examples/jsm/environments/RoomEnvironment.js
rename to libs/three/examples/jsm/environments/RoomEnvironment.js
diff --git a/node_modules/three/examples/jsm/exporters/DRACOExporter.js b/libs/three/examples/jsm/exporters/DRACOExporter.js
similarity index 100%
rename from node_modules/three/examples/jsm/exporters/DRACOExporter.js
rename to libs/three/examples/jsm/exporters/DRACOExporter.js
diff --git a/node_modules/three/examples/jsm/exporters/EXRExporter.js b/libs/three/examples/jsm/exporters/EXRExporter.js
similarity index 100%
rename from node_modules/three/examples/jsm/exporters/EXRExporter.js
rename to libs/three/examples/jsm/exporters/EXRExporter.js
diff --git a/node_modules/three/examples/jsm/exporters/GLTFExporter.js b/libs/three/examples/jsm/exporters/GLTFExporter.js
similarity index 100%
rename from node_modules/three/examples/jsm/exporters/GLTFExporter.js
rename to libs/three/examples/jsm/exporters/GLTFExporter.js
diff --git a/node_modules/three/examples/jsm/exporters/KTX2Exporter.js b/libs/three/examples/jsm/exporters/KTX2Exporter.js
similarity index 100%
rename from node_modules/three/examples/jsm/exporters/KTX2Exporter.js
rename to libs/three/examples/jsm/exporters/KTX2Exporter.js
diff --git a/node_modules/three/examples/jsm/exporters/MMDExporter.js b/libs/three/examples/jsm/exporters/MMDExporter.js
similarity index 100%
rename from node_modules/three/examples/jsm/exporters/MMDExporter.js
rename to libs/three/examples/jsm/exporters/MMDExporter.js
diff --git a/node_modules/three/examples/jsm/exporters/OBJExporter.js b/libs/three/examples/jsm/exporters/OBJExporter.js
similarity index 100%
rename from node_modules/three/examples/jsm/exporters/OBJExporter.js
rename to libs/three/examples/jsm/exporters/OBJExporter.js
diff --git a/node_modules/three/examples/jsm/exporters/PLYExporter.js b/libs/three/examples/jsm/exporters/PLYExporter.js
similarity index 100%
rename from node_modules/three/examples/jsm/exporters/PLYExporter.js
rename to libs/three/examples/jsm/exporters/PLYExporter.js
diff --git a/node_modules/three/examples/jsm/exporters/STLExporter.js b/libs/three/examples/jsm/exporters/STLExporter.js
similarity index 100%
rename from node_modules/three/examples/jsm/exporters/STLExporter.js
rename to libs/three/examples/jsm/exporters/STLExporter.js
diff --git a/node_modules/three/examples/jsm/exporters/USDZExporter.js b/libs/three/examples/jsm/exporters/USDZExporter.js
similarity index 100%
rename from node_modules/three/examples/jsm/exporters/USDZExporter.js
rename to libs/three/examples/jsm/exporters/USDZExporter.js
diff --git a/node_modules/three/examples/jsm/geometries/BoxLineGeometry.js b/libs/three/examples/jsm/geometries/BoxLineGeometry.js
similarity index 100%
rename from node_modules/three/examples/jsm/geometries/BoxLineGeometry.js
rename to libs/three/examples/jsm/geometries/BoxLineGeometry.js
diff --git a/node_modules/three/examples/jsm/geometries/ConvexGeometry.js b/libs/three/examples/jsm/geometries/ConvexGeometry.js
similarity index 100%
rename from node_modules/three/examples/jsm/geometries/ConvexGeometry.js
rename to libs/three/examples/jsm/geometries/ConvexGeometry.js
diff --git a/node_modules/three/examples/jsm/geometries/DecalGeometry.js b/libs/three/examples/jsm/geometries/DecalGeometry.js
similarity index 100%
rename from node_modules/three/examples/jsm/geometries/DecalGeometry.js
rename to libs/three/examples/jsm/geometries/DecalGeometry.js
diff --git a/node_modules/three/examples/jsm/geometries/InstancedPointsGeometry.js b/libs/three/examples/jsm/geometries/InstancedPointsGeometry.js
similarity index 100%
rename from node_modules/three/examples/jsm/geometries/InstancedPointsGeometry.js
rename to libs/three/examples/jsm/geometries/InstancedPointsGeometry.js
diff --git a/node_modules/three/examples/jsm/geometries/ParametricGeometries.js b/libs/three/examples/jsm/geometries/ParametricGeometries.js
similarity index 100%
rename from node_modules/three/examples/jsm/geometries/ParametricGeometries.js
rename to libs/three/examples/jsm/geometries/ParametricGeometries.js
diff --git a/node_modules/three/examples/jsm/geometries/ParametricGeometry.js b/libs/three/examples/jsm/geometries/ParametricGeometry.js
similarity index 100%
rename from node_modules/three/examples/jsm/geometries/ParametricGeometry.js
rename to libs/three/examples/jsm/geometries/ParametricGeometry.js
diff --git a/node_modules/three/examples/jsm/geometries/RoundedBoxGeometry.js b/libs/three/examples/jsm/geometries/RoundedBoxGeometry.js
similarity index 100%
rename from node_modules/three/examples/jsm/geometries/RoundedBoxGeometry.js
rename to libs/three/examples/jsm/geometries/RoundedBoxGeometry.js
diff --git a/node_modules/three/examples/jsm/geometries/SDFGeometryGenerator.js b/libs/three/examples/jsm/geometries/SDFGeometryGenerator.js
similarity index 100%
rename from node_modules/three/examples/jsm/geometries/SDFGeometryGenerator.js
rename to libs/three/examples/jsm/geometries/SDFGeometryGenerator.js
diff --git a/node_modules/three/examples/jsm/geometries/TeapotGeometry.js b/libs/three/examples/jsm/geometries/TeapotGeometry.js
similarity index 100%
rename from node_modules/three/examples/jsm/geometries/TeapotGeometry.js
rename to libs/three/examples/jsm/geometries/TeapotGeometry.js
diff --git a/node_modules/three/examples/jsm/geometries/TextGeometry.js b/libs/three/examples/jsm/geometries/TextGeometry.js
similarity index 100%
rename from node_modules/three/examples/jsm/geometries/TextGeometry.js
rename to libs/three/examples/jsm/geometries/TextGeometry.js
diff --git a/node_modules/three/examples/jsm/helpers/LightProbeHelper.js b/libs/three/examples/jsm/helpers/LightProbeHelper.js
similarity index 100%
rename from node_modules/three/examples/jsm/helpers/LightProbeHelper.js
rename to libs/three/examples/jsm/helpers/LightProbeHelper.js
diff --git a/node_modules/three/examples/jsm/helpers/OctreeHelper.js b/libs/three/examples/jsm/helpers/OctreeHelper.js
similarity index 100%
rename from node_modules/three/examples/jsm/helpers/OctreeHelper.js
rename to libs/three/examples/jsm/helpers/OctreeHelper.js
diff --git a/node_modules/three/examples/jsm/helpers/PositionalAudioHelper.js b/libs/three/examples/jsm/helpers/PositionalAudioHelper.js
similarity index 100%
rename from node_modules/three/examples/jsm/helpers/PositionalAudioHelper.js
rename to libs/three/examples/jsm/helpers/PositionalAudioHelper.js
diff --git a/node_modules/three/examples/jsm/helpers/RectAreaLightHelper.js b/libs/three/examples/jsm/helpers/RectAreaLightHelper.js
similarity index 100%
rename from node_modules/three/examples/jsm/helpers/RectAreaLightHelper.js
rename to libs/three/examples/jsm/helpers/RectAreaLightHelper.js
diff --git a/node_modules/three/examples/jsm/helpers/TextureHelper.js b/libs/three/examples/jsm/helpers/TextureHelper.js
similarity index 100%
rename from node_modules/three/examples/jsm/helpers/TextureHelper.js
rename to libs/three/examples/jsm/helpers/TextureHelper.js
diff --git a/node_modules/three/examples/jsm/helpers/VertexNormalsHelper.js b/libs/three/examples/jsm/helpers/VertexNormalsHelper.js
similarity index 100%
rename from node_modules/three/examples/jsm/helpers/VertexNormalsHelper.js
rename to libs/three/examples/jsm/helpers/VertexNormalsHelper.js
diff --git a/node_modules/three/examples/jsm/helpers/VertexTangentsHelper.js b/libs/three/examples/jsm/helpers/VertexTangentsHelper.js
similarity index 100%
rename from node_modules/three/examples/jsm/helpers/VertexTangentsHelper.js
rename to libs/three/examples/jsm/helpers/VertexTangentsHelper.js
diff --git a/node_modules/three/examples/jsm/helpers/ViewHelper.js b/libs/three/examples/jsm/helpers/ViewHelper.js
similarity index 100%
rename from node_modules/three/examples/jsm/helpers/ViewHelper.js
rename to libs/three/examples/jsm/helpers/ViewHelper.js
diff --git a/node_modules/three/examples/jsm/interactive/HTMLMesh.js b/libs/three/examples/jsm/interactive/HTMLMesh.js
similarity index 100%
rename from node_modules/three/examples/jsm/interactive/HTMLMesh.js
rename to libs/three/examples/jsm/interactive/HTMLMesh.js
diff --git a/node_modules/three/examples/jsm/interactive/InteractiveGroup.js b/libs/three/examples/jsm/interactive/InteractiveGroup.js
similarity index 100%
rename from node_modules/three/examples/jsm/interactive/InteractiveGroup.js
rename to libs/three/examples/jsm/interactive/InteractiveGroup.js
diff --git a/node_modules/three/examples/jsm/interactive/SelectionBox.js b/libs/three/examples/jsm/interactive/SelectionBox.js
similarity index 100%
rename from node_modules/three/examples/jsm/interactive/SelectionBox.js
rename to libs/three/examples/jsm/interactive/SelectionBox.js
diff --git a/node_modules/three/examples/jsm/interactive/SelectionHelper.js b/libs/three/examples/jsm/interactive/SelectionHelper.js
similarity index 100%
rename from node_modules/three/examples/jsm/interactive/SelectionHelper.js
rename to libs/three/examples/jsm/interactive/SelectionHelper.js
diff --git a/node_modules/three/examples/jsm/libs/ammo.wasm.js b/libs/three/examples/jsm/libs/ammo.wasm.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/ammo.wasm.js
rename to libs/three/examples/jsm/libs/ammo.wasm.js
diff --git a/node_modules/three/examples/jsm/libs/ammo.wasm.wasm b/libs/three/examples/jsm/libs/ammo.wasm.wasm
similarity index 100%
rename from node_modules/three/examples/jsm/libs/ammo.wasm.wasm
rename to libs/three/examples/jsm/libs/ammo.wasm.wasm
diff --git a/node_modules/three/examples/jsm/libs/basis/README.md b/libs/three/examples/jsm/libs/basis/README.md
similarity index 100%
rename from node_modules/three/examples/jsm/libs/basis/README.md
rename to libs/three/examples/jsm/libs/basis/README.md
diff --git a/node_modules/three/examples/jsm/libs/basis/basis_transcoder.js b/libs/three/examples/jsm/libs/basis/basis_transcoder.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/basis/basis_transcoder.js
rename to libs/three/examples/jsm/libs/basis/basis_transcoder.js
diff --git a/node_modules/three/examples/jsm/libs/basis/basis_transcoder.wasm b/libs/three/examples/jsm/libs/basis/basis_transcoder.wasm
similarity index 100%
rename from node_modules/three/examples/jsm/libs/basis/basis_transcoder.wasm
rename to libs/three/examples/jsm/libs/basis/basis_transcoder.wasm
diff --git a/node_modules/three/examples/jsm/libs/chevrotain.module.min.js b/libs/three/examples/jsm/libs/chevrotain.module.min.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/chevrotain.module.min.js
rename to libs/three/examples/jsm/libs/chevrotain.module.min.js
diff --git a/node_modules/three/examples/jsm/libs/draco/README.md b/libs/three/examples/jsm/libs/draco/README.md
similarity index 100%
rename from node_modules/three/examples/jsm/libs/draco/README.md
rename to libs/three/examples/jsm/libs/draco/README.md
diff --git a/node_modules/three/examples/jsm/libs/draco/draco_decoder.js b/libs/three/examples/jsm/libs/draco/draco_decoder.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/draco/draco_decoder.js
rename to libs/three/examples/jsm/libs/draco/draco_decoder.js
diff --git a/node_modules/three/examples/jsm/libs/draco/draco_decoder.wasm b/libs/three/examples/jsm/libs/draco/draco_decoder.wasm
similarity index 100%
rename from node_modules/three/examples/jsm/libs/draco/draco_decoder.wasm
rename to libs/three/examples/jsm/libs/draco/draco_decoder.wasm
diff --git a/node_modules/three/examples/jsm/libs/draco/draco_encoder.js b/libs/three/examples/jsm/libs/draco/draco_encoder.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/draco/draco_encoder.js
rename to libs/three/examples/jsm/libs/draco/draco_encoder.js
diff --git a/node_modules/three/examples/jsm/libs/draco/draco_wasm_wrapper.js b/libs/three/examples/jsm/libs/draco/draco_wasm_wrapper.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/draco/draco_wasm_wrapper.js
rename to libs/three/examples/jsm/libs/draco/draco_wasm_wrapper.js
diff --git a/node_modules/three/examples/jsm/libs/draco/gltf/draco_decoder.js b/libs/three/examples/jsm/libs/draco/gltf/draco_decoder.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/draco/gltf/draco_decoder.js
rename to libs/three/examples/jsm/libs/draco/gltf/draco_decoder.js
diff --git a/node_modules/three/examples/jsm/libs/draco/gltf/draco_decoder.wasm b/libs/three/examples/jsm/libs/draco/gltf/draco_decoder.wasm
similarity index 100%
rename from node_modules/three/examples/jsm/libs/draco/gltf/draco_decoder.wasm
rename to libs/three/examples/jsm/libs/draco/gltf/draco_decoder.wasm
diff --git a/node_modules/three/examples/jsm/libs/draco/gltf/draco_encoder.js b/libs/three/examples/jsm/libs/draco/gltf/draco_encoder.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/draco/gltf/draco_encoder.js
rename to libs/three/examples/jsm/libs/draco/gltf/draco_encoder.js
diff --git a/node_modules/three/examples/jsm/libs/draco/gltf/draco_wasm_wrapper.js b/libs/three/examples/jsm/libs/draco/gltf/draco_wasm_wrapper.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/draco/gltf/draco_wasm_wrapper.js
rename to libs/three/examples/jsm/libs/draco/gltf/draco_wasm_wrapper.js
diff --git a/node_modules/three/examples/jsm/libs/ecsy.module.js b/libs/three/examples/jsm/libs/ecsy.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/ecsy.module.js
rename to libs/three/examples/jsm/libs/ecsy.module.js
diff --git a/node_modules/three/examples/jsm/libs/fflate.module.js b/libs/three/examples/jsm/libs/fflate.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/fflate.module.js
rename to libs/three/examples/jsm/libs/fflate.module.js
diff --git a/node_modules/three/examples/jsm/libs/ktx-parse.module.js b/libs/three/examples/jsm/libs/ktx-parse.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/ktx-parse.module.js
rename to libs/three/examples/jsm/libs/ktx-parse.module.js
diff --git a/node_modules/three/examples/jsm/libs/lil-gui.module.min.js b/libs/three/examples/jsm/libs/lil-gui.module.min.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/lil-gui.module.min.js
rename to libs/three/examples/jsm/libs/lil-gui.module.min.js
diff --git a/node_modules/three/examples/jsm/libs/lottie_canvas.module.js b/libs/three/examples/jsm/libs/lottie_canvas.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/lottie_canvas.module.js
rename to libs/three/examples/jsm/libs/lottie_canvas.module.js
diff --git a/node_modules/three/examples/jsm/libs/meshopt_decoder.module.js b/libs/three/examples/jsm/libs/meshopt_decoder.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/meshopt_decoder.module.js
rename to libs/three/examples/jsm/libs/meshopt_decoder.module.js
diff --git a/node_modules/three/examples/jsm/libs/mikktspace.module.js b/libs/three/examples/jsm/libs/mikktspace.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/mikktspace.module.js
rename to libs/three/examples/jsm/libs/mikktspace.module.js
diff --git a/node_modules/three/examples/jsm/libs/mmdparser.module.js b/libs/three/examples/jsm/libs/mmdparser.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/mmdparser.module.js
rename to libs/three/examples/jsm/libs/mmdparser.module.js
diff --git a/node_modules/three/examples/jsm/libs/motion-controllers.module.js b/libs/three/examples/jsm/libs/motion-controllers.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/motion-controllers.module.js
rename to libs/three/examples/jsm/libs/motion-controllers.module.js
diff --git a/node_modules/three/examples/jsm/libs/opentype.module.js b/libs/three/examples/jsm/libs/opentype.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/opentype.module.js
rename to libs/three/examples/jsm/libs/opentype.module.js
diff --git a/node_modules/three/examples/jsm/libs/potpack.module.js b/libs/three/examples/jsm/libs/potpack.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/potpack.module.js
rename to libs/three/examples/jsm/libs/potpack.module.js
diff --git a/node_modules/three/examples/jsm/libs/rhino3dm/rhino3dm.js b/libs/three/examples/jsm/libs/rhino3dm/rhino3dm.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/rhino3dm/rhino3dm.js
rename to libs/three/examples/jsm/libs/rhino3dm/rhino3dm.js
diff --git a/node_modules/three/examples/jsm/libs/rhino3dm/rhino3dm.module.js b/libs/three/examples/jsm/libs/rhino3dm/rhino3dm.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/rhino3dm/rhino3dm.module.js
rename to libs/three/examples/jsm/libs/rhino3dm/rhino3dm.module.js
diff --git a/node_modules/three/examples/jsm/libs/rhino3dm/rhino3dm.wasm b/libs/three/examples/jsm/libs/rhino3dm/rhino3dm.wasm
similarity index 100%
rename from node_modules/three/examples/jsm/libs/rhino3dm/rhino3dm.wasm
rename to libs/three/examples/jsm/libs/rhino3dm/rhino3dm.wasm
diff --git a/node_modules/three/examples/jsm/libs/stats.module.js b/libs/three/examples/jsm/libs/stats.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/stats.module.js
rename to libs/three/examples/jsm/libs/stats.module.js
diff --git a/node_modules/three/examples/jsm/libs/surfaceNet.js b/libs/three/examples/jsm/libs/surfaceNet.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/surfaceNet.js
rename to libs/three/examples/jsm/libs/surfaceNet.js
diff --git a/node_modules/three/examples/jsm/libs/tween.module.js b/libs/three/examples/jsm/libs/tween.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/tween.module.js
rename to libs/three/examples/jsm/libs/tween.module.js
diff --git a/node_modules/three/examples/jsm/libs/utif.module.js b/libs/three/examples/jsm/libs/utif.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/utif.module.js
rename to libs/three/examples/jsm/libs/utif.module.js
diff --git a/node_modules/three/examples/jsm/libs/zstddec.module.js b/libs/three/examples/jsm/libs/zstddec.module.js
similarity index 100%
rename from node_modules/three/examples/jsm/libs/zstddec.module.js
rename to libs/three/examples/jsm/libs/zstddec.module.js
diff --git a/node_modules/three/examples/jsm/lights/IESSpotLight.js b/libs/three/examples/jsm/lights/IESSpotLight.js
similarity index 100%
rename from node_modules/three/examples/jsm/lights/IESSpotLight.js
rename to libs/three/examples/jsm/lights/IESSpotLight.js
diff --git a/node_modules/three/examples/jsm/lights/LightProbeGenerator.js b/libs/three/examples/jsm/lights/LightProbeGenerator.js
similarity index 100%
rename from node_modules/three/examples/jsm/lights/LightProbeGenerator.js
rename to libs/three/examples/jsm/lights/LightProbeGenerator.js
diff --git a/node_modules/three/examples/jsm/lights/RectAreaLightUniformsLib.js b/libs/three/examples/jsm/lights/RectAreaLightUniformsLib.js
similarity index 100%
rename from node_modules/three/examples/jsm/lights/RectAreaLightUniformsLib.js
rename to libs/three/examples/jsm/lights/RectAreaLightUniformsLib.js
diff --git a/node_modules/three/examples/jsm/lines/Line2.js b/libs/three/examples/jsm/lines/Line2.js
similarity index 100%
rename from node_modules/three/examples/jsm/lines/Line2.js
rename to libs/three/examples/jsm/lines/Line2.js
diff --git a/node_modules/three/examples/jsm/lines/LineGeometry.js b/libs/three/examples/jsm/lines/LineGeometry.js
similarity index 100%
rename from node_modules/three/examples/jsm/lines/LineGeometry.js
rename to libs/three/examples/jsm/lines/LineGeometry.js
diff --git a/node_modules/three/examples/jsm/lines/LineMaterial.js b/libs/three/examples/jsm/lines/LineMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/lines/LineMaterial.js
rename to libs/three/examples/jsm/lines/LineMaterial.js
diff --git a/node_modules/three/examples/jsm/lines/LineSegments2.js b/libs/three/examples/jsm/lines/LineSegments2.js
similarity index 100%
rename from node_modules/three/examples/jsm/lines/LineSegments2.js
rename to libs/three/examples/jsm/lines/LineSegments2.js
diff --git a/node_modules/three/examples/jsm/lines/LineSegmentsGeometry.js b/libs/three/examples/jsm/lines/LineSegmentsGeometry.js
similarity index 100%
rename from node_modules/three/examples/jsm/lines/LineSegmentsGeometry.js
rename to libs/three/examples/jsm/lines/LineSegmentsGeometry.js
diff --git a/node_modules/three/examples/jsm/lines/Wireframe.js b/libs/three/examples/jsm/lines/Wireframe.js
similarity index 100%
rename from node_modules/three/examples/jsm/lines/Wireframe.js
rename to libs/three/examples/jsm/lines/Wireframe.js
diff --git a/node_modules/three/examples/jsm/lines/WireframeGeometry2.js b/libs/three/examples/jsm/lines/WireframeGeometry2.js
similarity index 100%
rename from node_modules/three/examples/jsm/lines/WireframeGeometry2.js
rename to libs/three/examples/jsm/lines/WireframeGeometry2.js
diff --git a/node_modules/three/examples/jsm/loaders/3DMLoader.js b/libs/three/examples/jsm/loaders/3DMLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/3DMLoader.js
rename to libs/three/examples/jsm/loaders/3DMLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/3MFLoader.js b/libs/three/examples/jsm/loaders/3MFLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/3MFLoader.js
rename to libs/three/examples/jsm/loaders/3MFLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/AMFLoader.js b/libs/three/examples/jsm/loaders/AMFLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/AMFLoader.js
rename to libs/three/examples/jsm/loaders/AMFLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/BVHLoader.js b/libs/three/examples/jsm/loaders/BVHLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/BVHLoader.js
rename to libs/three/examples/jsm/loaders/BVHLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/ColladaLoader.js b/libs/three/examples/jsm/loaders/ColladaLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/ColladaLoader.js
rename to libs/three/examples/jsm/loaders/ColladaLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/DDSLoader.js b/libs/three/examples/jsm/loaders/DDSLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/DDSLoader.js
rename to libs/three/examples/jsm/loaders/DDSLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/DRACOLoader.js b/libs/three/examples/jsm/loaders/DRACOLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/DRACOLoader.js
rename to libs/three/examples/jsm/loaders/DRACOLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/EXRLoader.js b/libs/three/examples/jsm/loaders/EXRLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/EXRLoader.js
rename to libs/three/examples/jsm/loaders/EXRLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/FBXLoader.js b/libs/three/examples/jsm/loaders/FBXLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/FBXLoader.js
rename to libs/three/examples/jsm/loaders/FBXLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/FontLoader.js b/libs/three/examples/jsm/loaders/FontLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/FontLoader.js
rename to libs/three/examples/jsm/loaders/FontLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/GCodeLoader.js b/libs/three/examples/jsm/loaders/GCodeLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/GCodeLoader.js
rename to libs/three/examples/jsm/loaders/GCodeLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/GLTFLoader.js b/libs/three/examples/jsm/loaders/GLTFLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/GLTFLoader.js
rename to libs/three/examples/jsm/loaders/GLTFLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/HDRCubeTextureLoader.js b/libs/three/examples/jsm/loaders/HDRCubeTextureLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/HDRCubeTextureLoader.js
rename to libs/three/examples/jsm/loaders/HDRCubeTextureLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/IESLoader.js b/libs/three/examples/jsm/loaders/IESLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/IESLoader.js
rename to libs/three/examples/jsm/loaders/IESLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/KMZLoader.js b/libs/three/examples/jsm/loaders/KMZLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/KMZLoader.js
rename to libs/three/examples/jsm/loaders/KMZLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/KTX2Loader.js b/libs/three/examples/jsm/loaders/KTX2Loader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/KTX2Loader.js
rename to libs/three/examples/jsm/loaders/KTX2Loader.js
diff --git a/node_modules/three/examples/jsm/loaders/KTXLoader.js b/libs/three/examples/jsm/loaders/KTXLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/KTXLoader.js
rename to libs/three/examples/jsm/loaders/KTXLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/LDrawLoader.js b/libs/three/examples/jsm/loaders/LDrawLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/LDrawLoader.js
rename to libs/three/examples/jsm/loaders/LDrawLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/LUT3dlLoader.js b/libs/three/examples/jsm/loaders/LUT3dlLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/LUT3dlLoader.js
rename to libs/three/examples/jsm/loaders/LUT3dlLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/LUTCubeLoader.js b/libs/three/examples/jsm/loaders/LUTCubeLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/LUTCubeLoader.js
rename to libs/three/examples/jsm/loaders/LUTCubeLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/LUTImageLoader.js b/libs/three/examples/jsm/loaders/LUTImageLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/LUTImageLoader.js
rename to libs/three/examples/jsm/loaders/LUTImageLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/LWOLoader.js b/libs/three/examples/jsm/loaders/LWOLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/LWOLoader.js
rename to libs/three/examples/jsm/loaders/LWOLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/LogLuvLoader.js b/libs/three/examples/jsm/loaders/LogLuvLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/LogLuvLoader.js
rename to libs/three/examples/jsm/loaders/LogLuvLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/LottieLoader.js b/libs/three/examples/jsm/loaders/LottieLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/LottieLoader.js
rename to libs/three/examples/jsm/loaders/LottieLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/MD2Loader.js b/libs/three/examples/jsm/loaders/MD2Loader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/MD2Loader.js
rename to libs/three/examples/jsm/loaders/MD2Loader.js
diff --git a/node_modules/three/examples/jsm/loaders/MDDLoader.js b/libs/three/examples/jsm/loaders/MDDLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/MDDLoader.js
rename to libs/three/examples/jsm/loaders/MDDLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/MMDLoader.js b/libs/three/examples/jsm/loaders/MMDLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/MMDLoader.js
rename to libs/three/examples/jsm/loaders/MMDLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/MTLLoader.js b/libs/three/examples/jsm/loaders/MTLLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/MTLLoader.js
rename to libs/three/examples/jsm/loaders/MTLLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/MaterialXLoader.js b/libs/three/examples/jsm/loaders/MaterialXLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/MaterialXLoader.js
rename to libs/three/examples/jsm/loaders/MaterialXLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/NRRDLoader.js b/libs/three/examples/jsm/loaders/NRRDLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/NRRDLoader.js
rename to libs/three/examples/jsm/loaders/NRRDLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/OBJLoader.js b/libs/three/examples/jsm/loaders/OBJLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/OBJLoader.js
rename to libs/three/examples/jsm/loaders/OBJLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/PCDLoader.js b/libs/three/examples/jsm/loaders/PCDLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/PCDLoader.js
rename to libs/three/examples/jsm/loaders/PCDLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/PDBLoader.js b/libs/three/examples/jsm/loaders/PDBLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/PDBLoader.js
rename to libs/three/examples/jsm/loaders/PDBLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/PLYLoader.js b/libs/three/examples/jsm/loaders/PLYLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/PLYLoader.js
rename to libs/three/examples/jsm/loaders/PLYLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/PVRLoader.js b/libs/three/examples/jsm/loaders/PVRLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/PVRLoader.js
rename to libs/three/examples/jsm/loaders/PVRLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/RGBELoader.js b/libs/three/examples/jsm/loaders/RGBELoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/RGBELoader.js
rename to libs/three/examples/jsm/loaders/RGBELoader.js
diff --git a/node_modules/three/examples/jsm/loaders/RGBMLoader.js b/libs/three/examples/jsm/loaders/RGBMLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/RGBMLoader.js
rename to libs/three/examples/jsm/loaders/RGBMLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/STLLoader.js b/libs/three/examples/jsm/loaders/STLLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/STLLoader.js
rename to libs/three/examples/jsm/loaders/STLLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/SVGLoader.js b/libs/three/examples/jsm/loaders/SVGLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/SVGLoader.js
rename to libs/three/examples/jsm/loaders/SVGLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/TDSLoader.js b/libs/three/examples/jsm/loaders/TDSLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/TDSLoader.js
rename to libs/three/examples/jsm/loaders/TDSLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/TGALoader.js b/libs/three/examples/jsm/loaders/TGALoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/TGALoader.js
rename to libs/three/examples/jsm/loaders/TGALoader.js
diff --git a/node_modules/three/examples/jsm/loaders/TIFFLoader.js b/libs/three/examples/jsm/loaders/TIFFLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/TIFFLoader.js
rename to libs/three/examples/jsm/loaders/TIFFLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/TTFLoader.js b/libs/three/examples/jsm/loaders/TTFLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/TTFLoader.js
rename to libs/three/examples/jsm/loaders/TTFLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/TiltLoader.js b/libs/three/examples/jsm/loaders/TiltLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/TiltLoader.js
rename to libs/three/examples/jsm/loaders/TiltLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/USDZLoader.js b/libs/three/examples/jsm/loaders/USDZLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/USDZLoader.js
rename to libs/three/examples/jsm/loaders/USDZLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/VOXLoader.js b/libs/three/examples/jsm/loaders/VOXLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/VOXLoader.js
rename to libs/three/examples/jsm/loaders/VOXLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/VRMLLoader.js b/libs/three/examples/jsm/loaders/VRMLLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/VRMLLoader.js
rename to libs/three/examples/jsm/loaders/VRMLLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/VTKLoader.js b/libs/three/examples/jsm/loaders/VTKLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/VTKLoader.js
rename to libs/three/examples/jsm/loaders/VTKLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/XYZLoader.js b/libs/three/examples/jsm/loaders/XYZLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/XYZLoader.js
rename to libs/three/examples/jsm/loaders/XYZLoader.js
diff --git a/node_modules/three/examples/jsm/loaders/lwo/IFFParser.js b/libs/three/examples/jsm/loaders/lwo/IFFParser.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/lwo/IFFParser.js
rename to libs/three/examples/jsm/loaders/lwo/IFFParser.js
diff --git a/node_modules/three/examples/jsm/loaders/lwo/LWO2Parser.js b/libs/three/examples/jsm/loaders/lwo/LWO2Parser.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/lwo/LWO2Parser.js
rename to libs/three/examples/jsm/loaders/lwo/LWO2Parser.js
diff --git a/node_modules/three/examples/jsm/loaders/lwo/LWO3Parser.js b/libs/three/examples/jsm/loaders/lwo/LWO3Parser.js
similarity index 100%
rename from node_modules/three/examples/jsm/loaders/lwo/LWO3Parser.js
rename to libs/three/examples/jsm/loaders/lwo/LWO3Parser.js
diff --git a/node_modules/three/examples/jsm/materials/MeshGouraudMaterial.js b/libs/three/examples/jsm/materials/MeshGouraudMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/materials/MeshGouraudMaterial.js
rename to libs/three/examples/jsm/materials/MeshGouraudMaterial.js
diff --git a/node_modules/three/examples/jsm/math/Capsule.js b/libs/three/examples/jsm/math/Capsule.js
similarity index 100%
rename from node_modules/three/examples/jsm/math/Capsule.js
rename to libs/three/examples/jsm/math/Capsule.js
diff --git a/node_modules/three/examples/jsm/math/ColorConverter.js b/libs/three/examples/jsm/math/ColorConverter.js
similarity index 100%
rename from node_modules/three/examples/jsm/math/ColorConverter.js
rename to libs/three/examples/jsm/math/ColorConverter.js
diff --git a/node_modules/three/examples/jsm/math/ConvexHull.js b/libs/three/examples/jsm/math/ConvexHull.js
similarity index 100%
rename from node_modules/three/examples/jsm/math/ConvexHull.js
rename to libs/three/examples/jsm/math/ConvexHull.js
diff --git a/node_modules/three/examples/jsm/math/ImprovedNoise.js b/libs/three/examples/jsm/math/ImprovedNoise.js
similarity index 100%
rename from node_modules/three/examples/jsm/math/ImprovedNoise.js
rename to libs/three/examples/jsm/math/ImprovedNoise.js
diff --git a/node_modules/three/examples/jsm/math/Lut.js b/libs/three/examples/jsm/math/Lut.js
similarity index 100%
rename from node_modules/three/examples/jsm/math/Lut.js
rename to libs/three/examples/jsm/math/Lut.js
diff --git a/node_modules/three/examples/jsm/math/MeshSurfaceSampler.js b/libs/three/examples/jsm/math/MeshSurfaceSampler.js
similarity index 100%
rename from node_modules/three/examples/jsm/math/MeshSurfaceSampler.js
rename to libs/three/examples/jsm/math/MeshSurfaceSampler.js
diff --git a/node_modules/three/examples/jsm/math/OBB.js b/libs/three/examples/jsm/math/OBB.js
similarity index 100%
rename from node_modules/three/examples/jsm/math/OBB.js
rename to libs/three/examples/jsm/math/OBB.js
diff --git a/node_modules/three/examples/jsm/math/Octree.js b/libs/three/examples/jsm/math/Octree.js
similarity index 100%
rename from node_modules/three/examples/jsm/math/Octree.js
rename to libs/three/examples/jsm/math/Octree.js
diff --git a/node_modules/three/examples/jsm/math/SimplexNoise.js b/libs/three/examples/jsm/math/SimplexNoise.js
similarity index 100%
rename from node_modules/three/examples/jsm/math/SimplexNoise.js
rename to libs/three/examples/jsm/math/SimplexNoise.js
diff --git a/node_modules/three/examples/jsm/misc/ConvexObjectBreaker.js b/libs/three/examples/jsm/misc/ConvexObjectBreaker.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/ConvexObjectBreaker.js
rename to libs/three/examples/jsm/misc/ConvexObjectBreaker.js
diff --git a/node_modules/three/examples/jsm/misc/GPUComputationRenderer.js b/libs/three/examples/jsm/misc/GPUComputationRenderer.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/GPUComputationRenderer.js
rename to libs/three/examples/jsm/misc/GPUComputationRenderer.js
diff --git a/node_modules/three/examples/jsm/misc/Gyroscope.js b/libs/three/examples/jsm/misc/Gyroscope.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/Gyroscope.js
rename to libs/three/examples/jsm/misc/Gyroscope.js
diff --git a/node_modules/three/examples/jsm/misc/MD2Character.js b/libs/three/examples/jsm/misc/MD2Character.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/MD2Character.js
rename to libs/three/examples/jsm/misc/MD2Character.js
diff --git a/node_modules/three/examples/jsm/misc/MD2CharacterComplex.js b/libs/three/examples/jsm/misc/MD2CharacterComplex.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/MD2CharacterComplex.js
rename to libs/three/examples/jsm/misc/MD2CharacterComplex.js
diff --git a/node_modules/three/examples/jsm/misc/MorphAnimMesh.js b/libs/three/examples/jsm/misc/MorphAnimMesh.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/MorphAnimMesh.js
rename to libs/three/examples/jsm/misc/MorphAnimMesh.js
diff --git a/node_modules/three/examples/jsm/misc/MorphBlendMesh.js b/libs/three/examples/jsm/misc/MorphBlendMesh.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/MorphBlendMesh.js
rename to libs/three/examples/jsm/misc/MorphBlendMesh.js
diff --git a/node_modules/three/examples/jsm/misc/ProgressiveLightMap.js b/libs/three/examples/jsm/misc/ProgressiveLightMap.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/ProgressiveLightMap.js
rename to libs/three/examples/jsm/misc/ProgressiveLightMap.js
diff --git a/node_modules/three/examples/jsm/misc/RollerCoaster.js b/libs/three/examples/jsm/misc/RollerCoaster.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/RollerCoaster.js
rename to libs/three/examples/jsm/misc/RollerCoaster.js
diff --git a/node_modules/three/examples/jsm/misc/Timer.js b/libs/three/examples/jsm/misc/Timer.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/Timer.js
rename to libs/three/examples/jsm/misc/Timer.js
diff --git a/node_modules/three/examples/jsm/misc/TubePainter.js b/libs/three/examples/jsm/misc/TubePainter.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/TubePainter.js
rename to libs/three/examples/jsm/misc/TubePainter.js
diff --git a/node_modules/three/examples/jsm/misc/Volume.js b/libs/three/examples/jsm/misc/Volume.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/Volume.js
rename to libs/three/examples/jsm/misc/Volume.js
diff --git a/node_modules/three/examples/jsm/misc/VolumeSlice.js b/libs/three/examples/jsm/misc/VolumeSlice.js
similarity index 100%
rename from node_modules/three/examples/jsm/misc/VolumeSlice.js
rename to libs/three/examples/jsm/misc/VolumeSlice.js
diff --git a/node_modules/three/examples/jsm/modifiers/CurveModifier.js b/libs/three/examples/jsm/modifiers/CurveModifier.js
similarity index 100%
rename from node_modules/three/examples/jsm/modifiers/CurveModifier.js
rename to libs/three/examples/jsm/modifiers/CurveModifier.js
diff --git a/node_modules/three/examples/jsm/modifiers/EdgeSplitModifier.js b/libs/three/examples/jsm/modifiers/EdgeSplitModifier.js
similarity index 100%
rename from node_modules/three/examples/jsm/modifiers/EdgeSplitModifier.js
rename to libs/three/examples/jsm/modifiers/EdgeSplitModifier.js
diff --git a/node_modules/three/examples/jsm/modifiers/SimplifyModifier.js b/libs/three/examples/jsm/modifiers/SimplifyModifier.js
similarity index 100%
rename from node_modules/three/examples/jsm/modifiers/SimplifyModifier.js
rename to libs/three/examples/jsm/modifiers/SimplifyModifier.js
diff --git a/node_modules/three/examples/jsm/modifiers/TessellateModifier.js b/libs/three/examples/jsm/modifiers/TessellateModifier.js
similarity index 100%
rename from node_modules/three/examples/jsm/modifiers/TessellateModifier.js
rename to libs/three/examples/jsm/modifiers/TessellateModifier.js
diff --git a/node_modules/three/examples/jsm/nodes/Nodes.js b/libs/three/examples/jsm/nodes/Nodes.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/Nodes.js
rename to libs/three/examples/jsm/nodes/Nodes.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/BitangentNode.js b/libs/three/examples/jsm/nodes/accessors/BitangentNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/BitangentNode.js
rename to libs/three/examples/jsm/nodes/accessors/BitangentNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/BufferAttributeNode.js b/libs/three/examples/jsm/nodes/accessors/BufferAttributeNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/BufferAttributeNode.js
rename to libs/three/examples/jsm/nodes/accessors/BufferAttributeNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/BufferNode.js b/libs/three/examples/jsm/nodes/accessors/BufferNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/BufferNode.js
rename to libs/three/examples/jsm/nodes/accessors/BufferNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/CameraNode.js b/libs/three/examples/jsm/nodes/accessors/CameraNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/CameraNode.js
rename to libs/three/examples/jsm/nodes/accessors/CameraNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/CubeTextureNode.js b/libs/three/examples/jsm/nodes/accessors/CubeTextureNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/CubeTextureNode.js
rename to libs/three/examples/jsm/nodes/accessors/CubeTextureNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/InstanceNode.js b/libs/three/examples/jsm/nodes/accessors/InstanceNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/InstanceNode.js
rename to libs/three/examples/jsm/nodes/accessors/InstanceNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/InstancedPointsMaterialNode.js b/libs/three/examples/jsm/nodes/accessors/InstancedPointsMaterialNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/InstancedPointsMaterialNode.js
rename to libs/three/examples/jsm/nodes/accessors/InstancedPointsMaterialNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/MaterialNode.js b/libs/three/examples/jsm/nodes/accessors/MaterialNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/MaterialNode.js
rename to libs/three/examples/jsm/nodes/accessors/MaterialNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/MaterialReferenceNode.js b/libs/three/examples/jsm/nodes/accessors/MaterialReferenceNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/MaterialReferenceNode.js
rename to libs/three/examples/jsm/nodes/accessors/MaterialReferenceNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/ModelNode.js b/libs/three/examples/jsm/nodes/accessors/ModelNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/ModelNode.js
rename to libs/three/examples/jsm/nodes/accessors/ModelNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/ModelViewProjectionNode.js b/libs/three/examples/jsm/nodes/accessors/ModelViewProjectionNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/ModelViewProjectionNode.js
rename to libs/three/examples/jsm/nodes/accessors/ModelViewProjectionNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/MorphNode.js b/libs/three/examples/jsm/nodes/accessors/MorphNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/MorphNode.js
rename to libs/three/examples/jsm/nodes/accessors/MorphNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/NormalNode.js b/libs/three/examples/jsm/nodes/accessors/NormalNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/NormalNode.js
rename to libs/three/examples/jsm/nodes/accessors/NormalNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/Object3DNode.js b/libs/three/examples/jsm/nodes/accessors/Object3DNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/Object3DNode.js
rename to libs/three/examples/jsm/nodes/accessors/Object3DNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/PointUVNode.js b/libs/three/examples/jsm/nodes/accessors/PointUVNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/PointUVNode.js
rename to libs/three/examples/jsm/nodes/accessors/PointUVNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/PositionNode.js b/libs/three/examples/jsm/nodes/accessors/PositionNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/PositionNode.js
rename to libs/three/examples/jsm/nodes/accessors/PositionNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/ReferenceNode.js b/libs/three/examples/jsm/nodes/accessors/ReferenceNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/ReferenceNode.js
rename to libs/three/examples/jsm/nodes/accessors/ReferenceNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/ReflectVectorNode.js b/libs/three/examples/jsm/nodes/accessors/ReflectVectorNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/ReflectVectorNode.js
rename to libs/three/examples/jsm/nodes/accessors/ReflectVectorNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/SceneNode.js b/libs/three/examples/jsm/nodes/accessors/SceneNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/SceneNode.js
rename to libs/three/examples/jsm/nodes/accessors/SceneNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/SkinningNode.js b/libs/three/examples/jsm/nodes/accessors/SkinningNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/SkinningNode.js
rename to libs/three/examples/jsm/nodes/accessors/SkinningNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/StorageBufferNode.js b/libs/three/examples/jsm/nodes/accessors/StorageBufferNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/StorageBufferNode.js
rename to libs/three/examples/jsm/nodes/accessors/StorageBufferNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/TangentNode.js b/libs/three/examples/jsm/nodes/accessors/TangentNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/TangentNode.js
rename to libs/three/examples/jsm/nodes/accessors/TangentNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/TextureBicubicNode.js b/libs/three/examples/jsm/nodes/accessors/TextureBicubicNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/TextureBicubicNode.js
rename to libs/three/examples/jsm/nodes/accessors/TextureBicubicNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/TextureNode.js b/libs/three/examples/jsm/nodes/accessors/TextureNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/TextureNode.js
rename to libs/three/examples/jsm/nodes/accessors/TextureNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/TextureSizeNode.js b/libs/three/examples/jsm/nodes/accessors/TextureSizeNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/TextureSizeNode.js
rename to libs/three/examples/jsm/nodes/accessors/TextureSizeNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/TextureStoreNode.js b/libs/three/examples/jsm/nodes/accessors/TextureStoreNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/TextureStoreNode.js
rename to libs/three/examples/jsm/nodes/accessors/TextureStoreNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/UVNode.js b/libs/three/examples/jsm/nodes/accessors/UVNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/UVNode.js
rename to libs/three/examples/jsm/nodes/accessors/UVNode.js
diff --git a/node_modules/three/examples/jsm/nodes/accessors/UserDataNode.js b/libs/three/examples/jsm/nodes/accessors/UserDataNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/accessors/UserDataNode.js
rename to libs/three/examples/jsm/nodes/accessors/UserDataNode.js
diff --git a/node_modules/three/examples/jsm/nodes/code/CodeNode.js b/libs/three/examples/jsm/nodes/code/CodeNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/code/CodeNode.js
rename to libs/three/examples/jsm/nodes/code/CodeNode.js
diff --git a/node_modules/three/examples/jsm/nodes/code/ExpressionNode.js b/libs/three/examples/jsm/nodes/code/ExpressionNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/code/ExpressionNode.js
rename to libs/three/examples/jsm/nodes/code/ExpressionNode.js
diff --git a/node_modules/three/examples/jsm/nodes/code/FunctionCallNode.js b/libs/three/examples/jsm/nodes/code/FunctionCallNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/code/FunctionCallNode.js
rename to libs/three/examples/jsm/nodes/code/FunctionCallNode.js
diff --git a/node_modules/three/examples/jsm/nodes/code/FunctionNode.js b/libs/three/examples/jsm/nodes/code/FunctionNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/code/FunctionNode.js
rename to libs/three/examples/jsm/nodes/code/FunctionNode.js
diff --git a/node_modules/three/examples/jsm/nodes/code/ScriptableNode.js b/libs/three/examples/jsm/nodes/code/ScriptableNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/code/ScriptableNode.js
rename to libs/three/examples/jsm/nodes/code/ScriptableNode.js
diff --git a/node_modules/three/examples/jsm/nodes/code/ScriptableValueNode.js b/libs/three/examples/jsm/nodes/code/ScriptableValueNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/code/ScriptableValueNode.js
rename to libs/three/examples/jsm/nodes/code/ScriptableValueNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/ArrayUniformNode.js b/libs/three/examples/jsm/nodes/core/ArrayUniformNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/ArrayUniformNode.js
rename to libs/three/examples/jsm/nodes/core/ArrayUniformNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/AssignNode.js b/libs/three/examples/jsm/nodes/core/AssignNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/AssignNode.js
rename to libs/three/examples/jsm/nodes/core/AssignNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/AttributeNode.js b/libs/three/examples/jsm/nodes/core/AttributeNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/AttributeNode.js
rename to libs/three/examples/jsm/nodes/core/AttributeNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/BypassNode.js b/libs/three/examples/jsm/nodes/core/BypassNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/BypassNode.js
rename to libs/three/examples/jsm/nodes/core/BypassNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/CacheNode.js b/libs/three/examples/jsm/nodes/core/CacheNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/CacheNode.js
rename to libs/three/examples/jsm/nodes/core/CacheNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/ConstNode.js b/libs/three/examples/jsm/nodes/core/ConstNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/ConstNode.js
rename to libs/three/examples/jsm/nodes/core/ConstNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/ContextNode.js b/libs/three/examples/jsm/nodes/core/ContextNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/ContextNode.js
rename to libs/three/examples/jsm/nodes/core/ContextNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/IndexNode.js b/libs/three/examples/jsm/nodes/core/IndexNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/IndexNode.js
rename to libs/three/examples/jsm/nodes/core/IndexNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/InputNode.js b/libs/three/examples/jsm/nodes/core/InputNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/InputNode.js
rename to libs/three/examples/jsm/nodes/core/InputNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/LightingModel.js b/libs/three/examples/jsm/nodes/core/LightingModel.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/LightingModel.js
rename to libs/three/examples/jsm/nodes/core/LightingModel.js
diff --git a/node_modules/three/examples/jsm/nodes/core/Node.js b/libs/three/examples/jsm/nodes/core/Node.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/Node.js
rename to libs/three/examples/jsm/nodes/core/Node.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeAttribute.js b/libs/three/examples/jsm/nodes/core/NodeAttribute.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeAttribute.js
rename to libs/three/examples/jsm/nodes/core/NodeAttribute.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeBuilder.js b/libs/three/examples/jsm/nodes/core/NodeBuilder.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeBuilder.js
rename to libs/three/examples/jsm/nodes/core/NodeBuilder.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeCache.js b/libs/three/examples/jsm/nodes/core/NodeCache.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeCache.js
rename to libs/three/examples/jsm/nodes/core/NodeCache.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeCode.js b/libs/three/examples/jsm/nodes/core/NodeCode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeCode.js
rename to libs/three/examples/jsm/nodes/core/NodeCode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeFrame.js b/libs/three/examples/jsm/nodes/core/NodeFrame.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeFrame.js
rename to libs/three/examples/jsm/nodes/core/NodeFrame.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeFunction.js b/libs/three/examples/jsm/nodes/core/NodeFunction.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeFunction.js
rename to libs/three/examples/jsm/nodes/core/NodeFunction.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeFunctionInput.js b/libs/three/examples/jsm/nodes/core/NodeFunctionInput.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeFunctionInput.js
rename to libs/three/examples/jsm/nodes/core/NodeFunctionInput.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeKeywords.js b/libs/three/examples/jsm/nodes/core/NodeKeywords.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeKeywords.js
rename to libs/three/examples/jsm/nodes/core/NodeKeywords.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeParser.js b/libs/three/examples/jsm/nodes/core/NodeParser.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeParser.js
rename to libs/three/examples/jsm/nodes/core/NodeParser.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeUniform.js b/libs/three/examples/jsm/nodes/core/NodeUniform.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeUniform.js
rename to libs/three/examples/jsm/nodes/core/NodeUniform.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeUtils.js b/libs/three/examples/jsm/nodes/core/NodeUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeUtils.js
rename to libs/three/examples/jsm/nodes/core/NodeUtils.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeVar.js b/libs/three/examples/jsm/nodes/core/NodeVar.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeVar.js
rename to libs/three/examples/jsm/nodes/core/NodeVar.js
diff --git a/node_modules/three/examples/jsm/nodes/core/NodeVarying.js b/libs/three/examples/jsm/nodes/core/NodeVarying.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/NodeVarying.js
rename to libs/three/examples/jsm/nodes/core/NodeVarying.js
diff --git a/node_modules/three/examples/jsm/nodes/core/OutputStructNode.js b/libs/three/examples/jsm/nodes/core/OutputStructNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/OutputStructNode.js
rename to libs/three/examples/jsm/nodes/core/OutputStructNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/ParameterNode.js b/libs/three/examples/jsm/nodes/core/ParameterNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/ParameterNode.js
rename to libs/three/examples/jsm/nodes/core/ParameterNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/PropertyNode.js b/libs/three/examples/jsm/nodes/core/PropertyNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/PropertyNode.js
rename to libs/three/examples/jsm/nodes/core/PropertyNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/StackNode.js b/libs/three/examples/jsm/nodes/core/StackNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/StackNode.js
rename to libs/three/examples/jsm/nodes/core/StackNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/StructTypeNode.js b/libs/three/examples/jsm/nodes/core/StructTypeNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/StructTypeNode.js
rename to libs/three/examples/jsm/nodes/core/StructTypeNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/TempNode.js b/libs/three/examples/jsm/nodes/core/TempNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/TempNode.js
rename to libs/three/examples/jsm/nodes/core/TempNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/UniformGroup.js b/libs/three/examples/jsm/nodes/core/UniformGroup.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/UniformGroup.js
rename to libs/three/examples/jsm/nodes/core/UniformGroup.js
diff --git a/node_modules/three/examples/jsm/nodes/core/UniformGroupNode.js b/libs/three/examples/jsm/nodes/core/UniformGroupNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/UniformGroupNode.js
rename to libs/three/examples/jsm/nodes/core/UniformGroupNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/UniformNode.js b/libs/three/examples/jsm/nodes/core/UniformNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/UniformNode.js
rename to libs/three/examples/jsm/nodes/core/UniformNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/VarNode.js b/libs/three/examples/jsm/nodes/core/VarNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/VarNode.js
rename to libs/three/examples/jsm/nodes/core/VarNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/VaryingNode.js b/libs/three/examples/jsm/nodes/core/VaryingNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/VaryingNode.js
rename to libs/three/examples/jsm/nodes/core/VaryingNode.js
diff --git a/node_modules/three/examples/jsm/nodes/core/constants.js b/libs/three/examples/jsm/nodes/core/constants.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/core/constants.js
rename to libs/three/examples/jsm/nodes/core/constants.js
diff --git a/node_modules/three/examples/jsm/nodes/display/BlendModeNode.js b/libs/three/examples/jsm/nodes/display/BlendModeNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/BlendModeNode.js
rename to libs/three/examples/jsm/nodes/display/BlendModeNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/BumpMapNode.js b/libs/three/examples/jsm/nodes/display/BumpMapNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/BumpMapNode.js
rename to libs/three/examples/jsm/nodes/display/BumpMapNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/ColorAdjustmentNode.js b/libs/three/examples/jsm/nodes/display/ColorAdjustmentNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/ColorAdjustmentNode.js
rename to libs/three/examples/jsm/nodes/display/ColorAdjustmentNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/ColorSpaceNode.js b/libs/three/examples/jsm/nodes/display/ColorSpaceNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/ColorSpaceNode.js
rename to libs/three/examples/jsm/nodes/display/ColorSpaceNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/FrontFacingNode.js b/libs/three/examples/jsm/nodes/display/FrontFacingNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/FrontFacingNode.js
rename to libs/three/examples/jsm/nodes/display/FrontFacingNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/GaussianBlurNode.js b/libs/three/examples/jsm/nodes/display/GaussianBlurNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/GaussianBlurNode.js
rename to libs/three/examples/jsm/nodes/display/GaussianBlurNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/NormalMapNode.js b/libs/three/examples/jsm/nodes/display/NormalMapNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/NormalMapNode.js
rename to libs/three/examples/jsm/nodes/display/NormalMapNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/PassNode.js b/libs/three/examples/jsm/nodes/display/PassNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/PassNode.js
rename to libs/three/examples/jsm/nodes/display/PassNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/PosterizeNode.js b/libs/three/examples/jsm/nodes/display/PosterizeNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/PosterizeNode.js
rename to libs/three/examples/jsm/nodes/display/PosterizeNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/ToneMappingNode.js b/libs/three/examples/jsm/nodes/display/ToneMappingNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/ToneMappingNode.js
rename to libs/three/examples/jsm/nodes/display/ToneMappingNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/ViewportDepthNode.js b/libs/three/examples/jsm/nodes/display/ViewportDepthNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/ViewportDepthNode.js
rename to libs/three/examples/jsm/nodes/display/ViewportDepthNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/ViewportDepthTextureNode.js b/libs/three/examples/jsm/nodes/display/ViewportDepthTextureNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/ViewportDepthTextureNode.js
rename to libs/three/examples/jsm/nodes/display/ViewportDepthTextureNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/ViewportNode.js b/libs/three/examples/jsm/nodes/display/ViewportNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/ViewportNode.js
rename to libs/three/examples/jsm/nodes/display/ViewportNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/ViewportSharedTextureNode.js b/libs/three/examples/jsm/nodes/display/ViewportSharedTextureNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/ViewportSharedTextureNode.js
rename to libs/three/examples/jsm/nodes/display/ViewportSharedTextureNode.js
diff --git a/node_modules/three/examples/jsm/nodes/display/ViewportTextureNode.js b/libs/three/examples/jsm/nodes/display/ViewportTextureNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/display/ViewportTextureNode.js
rename to libs/three/examples/jsm/nodes/display/ViewportTextureNode.js
diff --git a/node_modules/three/examples/jsm/nodes/fog/FogExp2Node.js b/libs/three/examples/jsm/nodes/fog/FogExp2Node.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/fog/FogExp2Node.js
rename to libs/three/examples/jsm/nodes/fog/FogExp2Node.js
diff --git a/node_modules/three/examples/jsm/nodes/fog/FogNode.js b/libs/three/examples/jsm/nodes/fog/FogNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/fog/FogNode.js
rename to libs/three/examples/jsm/nodes/fog/FogNode.js
diff --git a/node_modules/three/examples/jsm/nodes/fog/FogRangeNode.js b/libs/three/examples/jsm/nodes/fog/FogRangeNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/fog/FogRangeNode.js
rename to libs/three/examples/jsm/nodes/fog/FogRangeNode.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/BSDF/BRDF_GGX.js b/libs/three/examples/jsm/nodes/functions/BSDF/BRDF_GGX.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/BSDF/BRDF_GGX.js
rename to libs/three/examples/jsm/nodes/functions/BSDF/BRDF_GGX.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/BSDF/BRDF_Lambert.js b/libs/three/examples/jsm/nodes/functions/BSDF/BRDF_Lambert.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/BSDF/BRDF_Lambert.js
rename to libs/three/examples/jsm/nodes/functions/BSDF/BRDF_Lambert.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/BSDF/BRDF_Sheen.js b/libs/three/examples/jsm/nodes/functions/BSDF/BRDF_Sheen.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/BSDF/BRDF_Sheen.js
rename to libs/three/examples/jsm/nodes/functions/BSDF/BRDF_Sheen.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/BSDF/DFGApprox.js b/libs/three/examples/jsm/nodes/functions/BSDF/DFGApprox.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/BSDF/DFGApprox.js
rename to libs/three/examples/jsm/nodes/functions/BSDF/DFGApprox.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/BSDF/D_GGX.js b/libs/three/examples/jsm/nodes/functions/BSDF/D_GGX.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/BSDF/D_GGX.js
rename to libs/three/examples/jsm/nodes/functions/BSDF/D_GGX.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/BSDF/EnvironmentBRDF.js b/libs/three/examples/jsm/nodes/functions/BSDF/EnvironmentBRDF.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/BSDF/EnvironmentBRDF.js
rename to libs/three/examples/jsm/nodes/functions/BSDF/EnvironmentBRDF.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/BSDF/F_Schlick.js b/libs/three/examples/jsm/nodes/functions/BSDF/F_Schlick.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/BSDF/F_Schlick.js
rename to libs/three/examples/jsm/nodes/functions/BSDF/F_Schlick.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/BSDF/Schlick_to_F0.js b/libs/three/examples/jsm/nodes/functions/BSDF/Schlick_to_F0.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/BSDF/Schlick_to_F0.js
rename to libs/three/examples/jsm/nodes/functions/BSDF/Schlick_to_F0.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/BSDF/V_GGX_SmithCorrelated.js b/libs/three/examples/jsm/nodes/functions/BSDF/V_GGX_SmithCorrelated.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/BSDF/V_GGX_SmithCorrelated.js
rename to libs/three/examples/jsm/nodes/functions/BSDF/V_GGX_SmithCorrelated.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/PhongLightingModel.js b/libs/three/examples/jsm/nodes/functions/PhongLightingModel.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/PhongLightingModel.js
rename to libs/three/examples/jsm/nodes/functions/PhongLightingModel.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/PhysicalLightingModel.js b/libs/three/examples/jsm/nodes/functions/PhysicalLightingModel.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/PhysicalLightingModel.js
rename to libs/three/examples/jsm/nodes/functions/PhysicalLightingModel.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/material/getGeometryRoughness.js b/libs/three/examples/jsm/nodes/functions/material/getGeometryRoughness.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/material/getGeometryRoughness.js
rename to libs/three/examples/jsm/nodes/functions/material/getGeometryRoughness.js
diff --git a/node_modules/three/examples/jsm/nodes/functions/material/getRoughness.js b/libs/three/examples/jsm/nodes/functions/material/getRoughness.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/functions/material/getRoughness.js
rename to libs/three/examples/jsm/nodes/functions/material/getRoughness.js
diff --git a/node_modules/three/examples/jsm/nodes/geometry/RangeNode.js b/libs/three/examples/jsm/nodes/geometry/RangeNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/geometry/RangeNode.js
rename to libs/three/examples/jsm/nodes/geometry/RangeNode.js
diff --git a/node_modules/three/examples/jsm/nodes/gpgpu/ComputeNode.js b/libs/three/examples/jsm/nodes/gpgpu/ComputeNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/gpgpu/ComputeNode.js
rename to libs/three/examples/jsm/nodes/gpgpu/ComputeNode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/AONode.js b/libs/three/examples/jsm/nodes/lighting/AONode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/AONode.js
rename to libs/three/examples/jsm/nodes/lighting/AONode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/AmbientLightNode.js b/libs/three/examples/jsm/nodes/lighting/AmbientLightNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/AmbientLightNode.js
rename to libs/three/examples/jsm/nodes/lighting/AmbientLightNode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/AnalyticLightNode.js b/libs/three/examples/jsm/nodes/lighting/AnalyticLightNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/AnalyticLightNode.js
rename to libs/three/examples/jsm/nodes/lighting/AnalyticLightNode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/DirectionalLightNode.js b/libs/three/examples/jsm/nodes/lighting/DirectionalLightNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/DirectionalLightNode.js
rename to libs/three/examples/jsm/nodes/lighting/DirectionalLightNode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/EnvironmentNode.js b/libs/three/examples/jsm/nodes/lighting/EnvironmentNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/EnvironmentNode.js
rename to libs/three/examples/jsm/nodes/lighting/EnvironmentNode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/HemisphereLightNode.js b/libs/three/examples/jsm/nodes/lighting/HemisphereLightNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/HemisphereLightNode.js
rename to libs/three/examples/jsm/nodes/lighting/HemisphereLightNode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/IESSpotLightNode.js b/libs/three/examples/jsm/nodes/lighting/IESSpotLightNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/IESSpotLightNode.js
rename to libs/three/examples/jsm/nodes/lighting/IESSpotLightNode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/LightNode.js b/libs/three/examples/jsm/nodes/lighting/LightNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/LightNode.js
rename to libs/three/examples/jsm/nodes/lighting/LightNode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/LightUtils.js b/libs/three/examples/jsm/nodes/lighting/LightUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/LightUtils.js
rename to libs/three/examples/jsm/nodes/lighting/LightUtils.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/LightingContextNode.js b/libs/three/examples/jsm/nodes/lighting/LightingContextNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/LightingContextNode.js
rename to libs/three/examples/jsm/nodes/lighting/LightingContextNode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/LightingNode.js b/libs/three/examples/jsm/nodes/lighting/LightingNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/LightingNode.js
rename to libs/three/examples/jsm/nodes/lighting/LightingNode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/LightsNode.js b/libs/three/examples/jsm/nodes/lighting/LightsNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/LightsNode.js
rename to libs/three/examples/jsm/nodes/lighting/LightsNode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/PointLightNode.js b/libs/three/examples/jsm/nodes/lighting/PointLightNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/PointLightNode.js
rename to libs/three/examples/jsm/nodes/lighting/PointLightNode.js
diff --git a/node_modules/three/examples/jsm/nodes/lighting/SpotLightNode.js b/libs/three/examples/jsm/nodes/lighting/SpotLightNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/lighting/SpotLightNode.js
rename to libs/three/examples/jsm/nodes/lighting/SpotLightNode.js
diff --git a/node_modules/three/examples/jsm/nodes/loaders/NodeLoader.js b/libs/three/examples/jsm/nodes/loaders/NodeLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/loaders/NodeLoader.js
rename to libs/three/examples/jsm/nodes/loaders/NodeLoader.js
diff --git a/node_modules/three/examples/jsm/nodes/loaders/NodeMaterialLoader.js b/libs/three/examples/jsm/nodes/loaders/NodeMaterialLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/loaders/NodeMaterialLoader.js
rename to libs/three/examples/jsm/nodes/loaders/NodeMaterialLoader.js
diff --git a/node_modules/three/examples/jsm/nodes/loaders/NodeObjectLoader.js b/libs/three/examples/jsm/nodes/loaders/NodeObjectLoader.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/loaders/NodeObjectLoader.js
rename to libs/three/examples/jsm/nodes/loaders/NodeObjectLoader.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/InstancedPointsNodeMaterial.js b/libs/three/examples/jsm/nodes/materials/InstancedPointsNodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/InstancedPointsNodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/InstancedPointsNodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/Line2NodeMaterial.js b/libs/three/examples/jsm/nodes/materials/Line2NodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/Line2NodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/Line2NodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/LineBasicNodeMaterial.js b/libs/three/examples/jsm/nodes/materials/LineBasicNodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/LineBasicNodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/LineBasicNodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/LineDashedNodeMaterial.js b/libs/three/examples/jsm/nodes/materials/LineDashedNodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/LineDashedNodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/LineDashedNodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/Materials.js b/libs/three/examples/jsm/nodes/materials/Materials.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/Materials.js
rename to libs/three/examples/jsm/nodes/materials/Materials.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/MeshBasicNodeMaterial.js b/libs/three/examples/jsm/nodes/materials/MeshBasicNodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/MeshBasicNodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/MeshBasicNodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/MeshLambertNodeMaterial.js b/libs/three/examples/jsm/nodes/materials/MeshLambertNodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/MeshLambertNodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/MeshLambertNodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js b/libs/three/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js b/libs/three/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/MeshPhysicalNodeMaterial.js b/libs/three/examples/jsm/nodes/materials/MeshPhysicalNodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/MeshPhysicalNodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/MeshPhysicalNodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js b/libs/three/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/NodeMaterial.js b/libs/three/examples/jsm/nodes/materials/NodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/NodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/NodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/PointsNodeMaterial.js b/libs/three/examples/jsm/nodes/materials/PointsNodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/PointsNodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/PointsNodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materials/SpriteNodeMaterial.js b/libs/three/examples/jsm/nodes/materials/SpriteNodeMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materials/SpriteNodeMaterial.js
rename to libs/three/examples/jsm/nodes/materials/SpriteNodeMaterial.js
diff --git a/node_modules/three/examples/jsm/nodes/materialx/DISCLAIMER.md b/libs/three/examples/jsm/nodes/materialx/DISCLAIMER.md
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materialx/DISCLAIMER.md
rename to libs/three/examples/jsm/nodes/materialx/DISCLAIMER.md
diff --git a/node_modules/three/examples/jsm/nodes/materialx/MaterialXNodes.js b/libs/three/examples/jsm/nodes/materialx/MaterialXNodes.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materialx/MaterialXNodes.js
rename to libs/three/examples/jsm/nodes/materialx/MaterialXNodes.js
diff --git a/node_modules/three/examples/jsm/nodes/materialx/lib/mx_hsv.js b/libs/three/examples/jsm/nodes/materialx/lib/mx_hsv.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materialx/lib/mx_hsv.js
rename to libs/three/examples/jsm/nodes/materialx/lib/mx_hsv.js
diff --git a/node_modules/three/examples/jsm/nodes/materialx/lib/mx_noise.js b/libs/three/examples/jsm/nodes/materialx/lib/mx_noise.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materialx/lib/mx_noise.js
rename to libs/three/examples/jsm/nodes/materialx/lib/mx_noise.js
diff --git a/node_modules/three/examples/jsm/nodes/materialx/lib/mx_transform_color.js b/libs/three/examples/jsm/nodes/materialx/lib/mx_transform_color.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/materialx/lib/mx_transform_color.js
rename to libs/three/examples/jsm/nodes/materialx/lib/mx_transform_color.js
diff --git a/node_modules/three/examples/jsm/nodes/math/CondNode.js b/libs/three/examples/jsm/nodes/math/CondNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/math/CondNode.js
rename to libs/three/examples/jsm/nodes/math/CondNode.js
diff --git a/node_modules/three/examples/jsm/nodes/math/HashNode.js b/libs/three/examples/jsm/nodes/math/HashNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/math/HashNode.js
rename to libs/three/examples/jsm/nodes/math/HashNode.js
diff --git a/node_modules/three/examples/jsm/nodes/math/MathNode.js b/libs/three/examples/jsm/nodes/math/MathNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/math/MathNode.js
rename to libs/three/examples/jsm/nodes/math/MathNode.js
diff --git a/node_modules/three/examples/jsm/nodes/math/OperatorNode.js b/libs/three/examples/jsm/nodes/math/OperatorNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/math/OperatorNode.js
rename to libs/three/examples/jsm/nodes/math/OperatorNode.js
diff --git a/node_modules/three/examples/jsm/nodes/parsers/GLSLNodeFunction.js b/libs/three/examples/jsm/nodes/parsers/GLSLNodeFunction.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/parsers/GLSLNodeFunction.js
rename to libs/three/examples/jsm/nodes/parsers/GLSLNodeFunction.js
diff --git a/node_modules/three/examples/jsm/nodes/parsers/GLSLNodeParser.js b/libs/three/examples/jsm/nodes/parsers/GLSLNodeParser.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/parsers/GLSLNodeParser.js
rename to libs/three/examples/jsm/nodes/parsers/GLSLNodeParser.js
diff --git a/node_modules/three/examples/jsm/nodes/procedural/CheckerNode.js b/libs/three/examples/jsm/nodes/procedural/CheckerNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/procedural/CheckerNode.js
rename to libs/three/examples/jsm/nodes/procedural/CheckerNode.js
diff --git a/node_modules/three/examples/jsm/nodes/shadernode/ShaderNode.js b/libs/three/examples/jsm/nodes/shadernode/ShaderNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/shadernode/ShaderNode.js
rename to libs/three/examples/jsm/nodes/shadernode/ShaderNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/ArrayElementNode.js b/libs/three/examples/jsm/nodes/utils/ArrayElementNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/ArrayElementNode.js
rename to libs/three/examples/jsm/nodes/utils/ArrayElementNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/ConvertNode.js b/libs/three/examples/jsm/nodes/utils/ConvertNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/ConvertNode.js
rename to libs/three/examples/jsm/nodes/utils/ConvertNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/DiscardNode.js b/libs/three/examples/jsm/nodes/utils/DiscardNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/DiscardNode.js
rename to libs/three/examples/jsm/nodes/utils/DiscardNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/EquirectUVNode.js b/libs/three/examples/jsm/nodes/utils/EquirectUVNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/EquirectUVNode.js
rename to libs/three/examples/jsm/nodes/utils/EquirectUVNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/FunctionOverloadingNode.js b/libs/three/examples/jsm/nodes/utils/FunctionOverloadingNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/FunctionOverloadingNode.js
rename to libs/three/examples/jsm/nodes/utils/FunctionOverloadingNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/JoinNode.js b/libs/three/examples/jsm/nodes/utils/JoinNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/JoinNode.js
rename to libs/three/examples/jsm/nodes/utils/JoinNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/LoopNode.js b/libs/three/examples/jsm/nodes/utils/LoopNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/LoopNode.js
rename to libs/three/examples/jsm/nodes/utils/LoopNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/MatcapUVNode.js b/libs/three/examples/jsm/nodes/utils/MatcapUVNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/MatcapUVNode.js
rename to libs/three/examples/jsm/nodes/utils/MatcapUVNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/MaxMipLevelNode.js b/libs/three/examples/jsm/nodes/utils/MaxMipLevelNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/MaxMipLevelNode.js
rename to libs/three/examples/jsm/nodes/utils/MaxMipLevelNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/OscNode.js b/libs/three/examples/jsm/nodes/utils/OscNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/OscNode.js
rename to libs/three/examples/jsm/nodes/utils/OscNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/PackingNode.js b/libs/three/examples/jsm/nodes/utils/PackingNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/PackingNode.js
rename to libs/three/examples/jsm/nodes/utils/PackingNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/RemapNode.js b/libs/three/examples/jsm/nodes/utils/RemapNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/RemapNode.js
rename to libs/three/examples/jsm/nodes/utils/RemapNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/RotateUVNode.js b/libs/three/examples/jsm/nodes/utils/RotateUVNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/RotateUVNode.js
rename to libs/three/examples/jsm/nodes/utils/RotateUVNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/SetNode.js b/libs/three/examples/jsm/nodes/utils/SetNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/SetNode.js
rename to libs/three/examples/jsm/nodes/utils/SetNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/SpecularMIPLevelNode.js b/libs/three/examples/jsm/nodes/utils/SpecularMIPLevelNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/SpecularMIPLevelNode.js
rename to libs/three/examples/jsm/nodes/utils/SpecularMIPLevelNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/SplitNode.js b/libs/three/examples/jsm/nodes/utils/SplitNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/SplitNode.js
rename to libs/three/examples/jsm/nodes/utils/SplitNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/SpriteSheetUVNode.js b/libs/three/examples/jsm/nodes/utils/SpriteSheetUVNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/SpriteSheetUVNode.js
rename to libs/three/examples/jsm/nodes/utils/SpriteSheetUVNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/TimerNode.js b/libs/three/examples/jsm/nodes/utils/TimerNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/TimerNode.js
rename to libs/three/examples/jsm/nodes/utils/TimerNode.js
diff --git a/node_modules/three/examples/jsm/nodes/utils/TriplanarTexturesNode.js b/libs/three/examples/jsm/nodes/utils/TriplanarTexturesNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/nodes/utils/TriplanarTexturesNode.js
rename to libs/three/examples/jsm/nodes/utils/TriplanarTexturesNode.js
diff --git a/node_modules/three/examples/jsm/objects/GroundProjectedSkybox.js b/libs/three/examples/jsm/objects/GroundProjectedSkybox.js
similarity index 100%
rename from node_modules/three/examples/jsm/objects/GroundProjectedSkybox.js
rename to libs/three/examples/jsm/objects/GroundProjectedSkybox.js
diff --git a/node_modules/three/examples/jsm/objects/InstancedPoints.js b/libs/three/examples/jsm/objects/InstancedPoints.js
similarity index 100%
rename from node_modules/three/examples/jsm/objects/InstancedPoints.js
rename to libs/three/examples/jsm/objects/InstancedPoints.js
diff --git a/node_modules/three/examples/jsm/objects/Lensflare.js b/libs/three/examples/jsm/objects/Lensflare.js
similarity index 100%
rename from node_modules/three/examples/jsm/objects/Lensflare.js
rename to libs/three/examples/jsm/objects/Lensflare.js
diff --git a/node_modules/three/examples/jsm/objects/MarchingCubes.js b/libs/three/examples/jsm/objects/MarchingCubes.js
similarity index 100%
rename from node_modules/three/examples/jsm/objects/MarchingCubes.js
rename to libs/three/examples/jsm/objects/MarchingCubes.js
diff --git a/node_modules/three/examples/jsm/objects/QuadMesh.js b/libs/three/examples/jsm/objects/QuadMesh.js
similarity index 100%
rename from node_modules/three/examples/jsm/objects/QuadMesh.js
rename to libs/three/examples/jsm/objects/QuadMesh.js
diff --git a/node_modules/three/examples/jsm/objects/Reflector.js b/libs/three/examples/jsm/objects/Reflector.js
similarity index 100%
rename from node_modules/three/examples/jsm/objects/Reflector.js
rename to libs/three/examples/jsm/objects/Reflector.js
diff --git a/node_modules/three/examples/jsm/objects/ReflectorForSSRPass.js b/libs/three/examples/jsm/objects/ReflectorForSSRPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/objects/ReflectorForSSRPass.js
rename to libs/three/examples/jsm/objects/ReflectorForSSRPass.js
diff --git a/node_modules/three/examples/jsm/objects/Refractor.js b/libs/three/examples/jsm/objects/Refractor.js
similarity index 100%
rename from node_modules/three/examples/jsm/objects/Refractor.js
rename to libs/three/examples/jsm/objects/Refractor.js
diff --git a/node_modules/three/examples/jsm/objects/ShadowMesh.js b/libs/three/examples/jsm/objects/ShadowMesh.js
similarity index 100%
rename from node_modules/three/examples/jsm/objects/ShadowMesh.js
rename to libs/three/examples/jsm/objects/ShadowMesh.js
diff --git a/node_modules/three/examples/jsm/objects/Sky.js b/libs/three/examples/jsm/objects/Sky.js
similarity index 100%
rename from node_modules/three/examples/jsm/objects/Sky.js
rename to libs/three/examples/jsm/objects/Sky.js
diff --git a/node_modules/three/examples/jsm/objects/Water.js b/libs/three/examples/jsm/objects/Water.js
similarity index 100%
rename from node_modules/three/examples/jsm/objects/Water.js
rename to libs/three/examples/jsm/objects/Water.js
diff --git a/node_modules/three/examples/jsm/objects/Water2.js b/libs/three/examples/jsm/objects/Water2.js
similarity index 100%
rename from node_modules/three/examples/jsm/objects/Water2.js
rename to libs/three/examples/jsm/objects/Water2.js
diff --git a/node_modules/three/examples/jsm/offscreen/jank.js b/libs/three/examples/jsm/offscreen/jank.js
similarity index 100%
rename from node_modules/three/examples/jsm/offscreen/jank.js
rename to libs/three/examples/jsm/offscreen/jank.js
diff --git a/node_modules/three/examples/jsm/offscreen/offscreen.js b/libs/three/examples/jsm/offscreen/offscreen.js
similarity index 100%
rename from node_modules/three/examples/jsm/offscreen/offscreen.js
rename to libs/three/examples/jsm/offscreen/offscreen.js
diff --git a/node_modules/three/examples/jsm/offscreen/scene.js b/libs/three/examples/jsm/offscreen/scene.js
similarity index 100%
rename from node_modules/three/examples/jsm/offscreen/scene.js
rename to libs/three/examples/jsm/offscreen/scene.js
diff --git a/node_modules/three/examples/jsm/physics/AmmoPhysics.js b/libs/three/examples/jsm/physics/AmmoPhysics.js
similarity index 100%
rename from node_modules/three/examples/jsm/physics/AmmoPhysics.js
rename to libs/three/examples/jsm/physics/AmmoPhysics.js
diff --git a/node_modules/three/examples/jsm/physics/RapierPhysics.js b/libs/three/examples/jsm/physics/RapierPhysics.js
similarity index 100%
rename from node_modules/three/examples/jsm/physics/RapierPhysics.js
rename to libs/three/examples/jsm/physics/RapierPhysics.js
diff --git a/node_modules/three/examples/jsm/postprocessing/AfterimagePass.js b/libs/three/examples/jsm/postprocessing/AfterimagePass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/AfterimagePass.js
rename to libs/three/examples/jsm/postprocessing/AfterimagePass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/BloomPass.js b/libs/three/examples/jsm/postprocessing/BloomPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/BloomPass.js
rename to libs/three/examples/jsm/postprocessing/BloomPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/BokehPass.js b/libs/three/examples/jsm/postprocessing/BokehPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/BokehPass.js
rename to libs/three/examples/jsm/postprocessing/BokehPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/ClearPass.js b/libs/three/examples/jsm/postprocessing/ClearPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/ClearPass.js
rename to libs/three/examples/jsm/postprocessing/ClearPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/CubeTexturePass.js b/libs/three/examples/jsm/postprocessing/CubeTexturePass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/CubeTexturePass.js
rename to libs/three/examples/jsm/postprocessing/CubeTexturePass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/DotScreenPass.js b/libs/three/examples/jsm/postprocessing/DotScreenPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/DotScreenPass.js
rename to libs/three/examples/jsm/postprocessing/DotScreenPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/EffectComposer.js b/libs/three/examples/jsm/postprocessing/EffectComposer.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/EffectComposer.js
rename to libs/three/examples/jsm/postprocessing/EffectComposer.js
diff --git a/node_modules/three/examples/jsm/postprocessing/FilmPass.js b/libs/three/examples/jsm/postprocessing/FilmPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/FilmPass.js
rename to libs/three/examples/jsm/postprocessing/FilmPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/GTAOPass.js b/libs/three/examples/jsm/postprocessing/GTAOPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/GTAOPass.js
rename to libs/three/examples/jsm/postprocessing/GTAOPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/GlitchPass.js b/libs/three/examples/jsm/postprocessing/GlitchPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/GlitchPass.js
rename to libs/three/examples/jsm/postprocessing/GlitchPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/HalftonePass.js b/libs/three/examples/jsm/postprocessing/HalftonePass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/HalftonePass.js
rename to libs/three/examples/jsm/postprocessing/HalftonePass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/LUTPass.js b/libs/three/examples/jsm/postprocessing/LUTPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/LUTPass.js
rename to libs/three/examples/jsm/postprocessing/LUTPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/MaskPass.js b/libs/three/examples/jsm/postprocessing/MaskPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/MaskPass.js
rename to libs/three/examples/jsm/postprocessing/MaskPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/OutlinePass.js b/libs/three/examples/jsm/postprocessing/OutlinePass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/OutlinePass.js
rename to libs/three/examples/jsm/postprocessing/OutlinePass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/OutputPass.js b/libs/three/examples/jsm/postprocessing/OutputPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/OutputPass.js
rename to libs/three/examples/jsm/postprocessing/OutputPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/Pass.js b/libs/three/examples/jsm/postprocessing/Pass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/Pass.js
rename to libs/three/examples/jsm/postprocessing/Pass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/RenderPass.js b/libs/three/examples/jsm/postprocessing/RenderPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/RenderPass.js
rename to libs/three/examples/jsm/postprocessing/RenderPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/RenderPixelatedPass.js b/libs/three/examples/jsm/postprocessing/RenderPixelatedPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/RenderPixelatedPass.js
rename to libs/three/examples/jsm/postprocessing/RenderPixelatedPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/SAOPass.js b/libs/three/examples/jsm/postprocessing/SAOPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/SAOPass.js
rename to libs/three/examples/jsm/postprocessing/SAOPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/SMAAPass.js b/libs/three/examples/jsm/postprocessing/SMAAPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/SMAAPass.js
rename to libs/three/examples/jsm/postprocessing/SMAAPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/SSAARenderPass.js b/libs/three/examples/jsm/postprocessing/SSAARenderPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/SSAARenderPass.js
rename to libs/three/examples/jsm/postprocessing/SSAARenderPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/SSAOPass.js b/libs/three/examples/jsm/postprocessing/SSAOPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/SSAOPass.js
rename to libs/three/examples/jsm/postprocessing/SSAOPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/SSRPass.js b/libs/three/examples/jsm/postprocessing/SSRPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/SSRPass.js
rename to libs/three/examples/jsm/postprocessing/SSRPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/SavePass.js b/libs/three/examples/jsm/postprocessing/SavePass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/SavePass.js
rename to libs/three/examples/jsm/postprocessing/SavePass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/ShaderPass.js b/libs/three/examples/jsm/postprocessing/ShaderPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/ShaderPass.js
rename to libs/three/examples/jsm/postprocessing/ShaderPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/TAARenderPass.js b/libs/three/examples/jsm/postprocessing/TAARenderPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/TAARenderPass.js
rename to libs/three/examples/jsm/postprocessing/TAARenderPass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/TexturePass.js b/libs/three/examples/jsm/postprocessing/TexturePass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/TexturePass.js
rename to libs/three/examples/jsm/postprocessing/TexturePass.js
diff --git a/node_modules/three/examples/jsm/postprocessing/UnrealBloomPass.js b/libs/three/examples/jsm/postprocessing/UnrealBloomPass.js
similarity index 100%
rename from node_modules/three/examples/jsm/postprocessing/UnrealBloomPass.js
rename to libs/three/examples/jsm/postprocessing/UnrealBloomPass.js
diff --git a/node_modules/three/examples/jsm/renderers/CSS2DRenderer.js b/libs/three/examples/jsm/renderers/CSS2DRenderer.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/CSS2DRenderer.js
rename to libs/three/examples/jsm/renderers/CSS2DRenderer.js
diff --git a/node_modules/three/examples/jsm/renderers/CSS3DRenderer.js b/libs/three/examples/jsm/renderers/CSS3DRenderer.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/CSS3DRenderer.js
rename to libs/three/examples/jsm/renderers/CSS3DRenderer.js
diff --git a/node_modules/three/examples/jsm/renderers/Projector.js b/libs/three/examples/jsm/renderers/Projector.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/Projector.js
rename to libs/three/examples/jsm/renderers/Projector.js
diff --git a/node_modules/three/examples/jsm/renderers/SVGRenderer.js b/libs/three/examples/jsm/renderers/SVGRenderer.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/SVGRenderer.js
rename to libs/three/examples/jsm/renderers/SVGRenderer.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Animation.js b/libs/three/examples/jsm/renderers/common/Animation.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Animation.js
rename to libs/three/examples/jsm/renderers/common/Animation.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Attributes.js b/libs/three/examples/jsm/renderers/common/Attributes.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Attributes.js
rename to libs/three/examples/jsm/renderers/common/Attributes.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Backend.js b/libs/three/examples/jsm/renderers/common/Backend.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Backend.js
rename to libs/three/examples/jsm/renderers/common/Backend.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Background.js b/libs/three/examples/jsm/renderers/common/Background.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Background.js
rename to libs/three/examples/jsm/renderers/common/Background.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Binding.js b/libs/three/examples/jsm/renderers/common/Binding.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Binding.js
rename to libs/three/examples/jsm/renderers/common/Binding.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Bindings.js b/libs/three/examples/jsm/renderers/common/Bindings.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Bindings.js
rename to libs/three/examples/jsm/renderers/common/Bindings.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Buffer.js b/libs/three/examples/jsm/renderers/common/Buffer.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Buffer.js
rename to libs/three/examples/jsm/renderers/common/Buffer.js
diff --git a/node_modules/three/examples/jsm/renderers/common/BufferUtils.js b/libs/three/examples/jsm/renderers/common/BufferUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/BufferUtils.js
rename to libs/three/examples/jsm/renderers/common/BufferUtils.js
diff --git a/node_modules/three/examples/jsm/renderers/common/ChainMap.js b/libs/three/examples/jsm/renderers/common/ChainMap.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/ChainMap.js
rename to libs/three/examples/jsm/renderers/common/ChainMap.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Color4.js b/libs/three/examples/jsm/renderers/common/Color4.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Color4.js
rename to libs/three/examples/jsm/renderers/common/Color4.js
diff --git a/node_modules/three/examples/jsm/renderers/common/ComputePipeline.js b/libs/three/examples/jsm/renderers/common/ComputePipeline.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/ComputePipeline.js
rename to libs/three/examples/jsm/renderers/common/ComputePipeline.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Constants.js b/libs/three/examples/jsm/renderers/common/Constants.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Constants.js
rename to libs/three/examples/jsm/renderers/common/Constants.js
diff --git a/node_modules/three/examples/jsm/renderers/common/CubeRenderTarget.js b/libs/three/examples/jsm/renderers/common/CubeRenderTarget.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/CubeRenderTarget.js
rename to libs/three/examples/jsm/renderers/common/CubeRenderTarget.js
diff --git a/node_modules/three/examples/jsm/renderers/common/DataMap.js b/libs/three/examples/jsm/renderers/common/DataMap.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/DataMap.js
rename to libs/three/examples/jsm/renderers/common/DataMap.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Geometries.js b/libs/three/examples/jsm/renderers/common/Geometries.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Geometries.js
rename to libs/three/examples/jsm/renderers/common/Geometries.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Info.js b/libs/three/examples/jsm/renderers/common/Info.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Info.js
rename to libs/three/examples/jsm/renderers/common/Info.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Pipeline.js b/libs/three/examples/jsm/renderers/common/Pipeline.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Pipeline.js
rename to libs/three/examples/jsm/renderers/common/Pipeline.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Pipelines.js b/libs/three/examples/jsm/renderers/common/Pipelines.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Pipelines.js
rename to libs/three/examples/jsm/renderers/common/Pipelines.js
diff --git a/node_modules/three/examples/jsm/renderers/common/PostProcessing.js b/libs/three/examples/jsm/renderers/common/PostProcessing.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/PostProcessing.js
rename to libs/three/examples/jsm/renderers/common/PostProcessing.js
diff --git a/node_modules/three/examples/jsm/renderers/common/ProgrammableStage.js b/libs/three/examples/jsm/renderers/common/ProgrammableStage.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/ProgrammableStage.js
rename to libs/three/examples/jsm/renderers/common/ProgrammableStage.js
diff --git a/node_modules/three/examples/jsm/renderers/common/RenderContext.js b/libs/three/examples/jsm/renderers/common/RenderContext.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/RenderContext.js
rename to libs/three/examples/jsm/renderers/common/RenderContext.js
diff --git a/node_modules/three/examples/jsm/renderers/common/RenderContexts.js b/libs/three/examples/jsm/renderers/common/RenderContexts.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/RenderContexts.js
rename to libs/three/examples/jsm/renderers/common/RenderContexts.js
diff --git a/node_modules/three/examples/jsm/renderers/common/RenderList.js b/libs/three/examples/jsm/renderers/common/RenderList.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/RenderList.js
rename to libs/three/examples/jsm/renderers/common/RenderList.js
diff --git a/node_modules/three/examples/jsm/renderers/common/RenderLists.js b/libs/three/examples/jsm/renderers/common/RenderLists.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/RenderLists.js
rename to libs/three/examples/jsm/renderers/common/RenderLists.js
diff --git a/node_modules/three/examples/jsm/renderers/common/RenderObject.js b/libs/three/examples/jsm/renderers/common/RenderObject.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/RenderObject.js
rename to libs/three/examples/jsm/renderers/common/RenderObject.js
diff --git a/node_modules/three/examples/jsm/renderers/common/RenderObjects.js b/libs/three/examples/jsm/renderers/common/RenderObjects.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/RenderObjects.js
rename to libs/three/examples/jsm/renderers/common/RenderObjects.js
diff --git a/node_modules/three/examples/jsm/renderers/common/RenderPipeline.js b/libs/three/examples/jsm/renderers/common/RenderPipeline.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/RenderPipeline.js
rename to libs/three/examples/jsm/renderers/common/RenderPipeline.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Renderer.js b/libs/three/examples/jsm/renderers/common/Renderer.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Renderer.js
rename to libs/three/examples/jsm/renderers/common/Renderer.js
diff --git a/node_modules/three/examples/jsm/renderers/common/SampledTexture.js b/libs/three/examples/jsm/renderers/common/SampledTexture.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/SampledTexture.js
rename to libs/three/examples/jsm/renderers/common/SampledTexture.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Sampler.js b/libs/three/examples/jsm/renderers/common/Sampler.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Sampler.js
rename to libs/three/examples/jsm/renderers/common/Sampler.js
diff --git a/node_modules/three/examples/jsm/renderers/common/StorageBuffer.js b/libs/three/examples/jsm/renderers/common/StorageBuffer.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/StorageBuffer.js
rename to libs/three/examples/jsm/renderers/common/StorageBuffer.js
diff --git a/node_modules/three/examples/jsm/renderers/common/StorageTexture.js b/libs/three/examples/jsm/renderers/common/StorageTexture.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/StorageTexture.js
rename to libs/three/examples/jsm/renderers/common/StorageTexture.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Textures.js b/libs/three/examples/jsm/renderers/common/Textures.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Textures.js
rename to libs/three/examples/jsm/renderers/common/Textures.js
diff --git a/node_modules/three/examples/jsm/renderers/common/Uniform.js b/libs/three/examples/jsm/renderers/common/Uniform.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/Uniform.js
rename to libs/three/examples/jsm/renderers/common/Uniform.js
diff --git a/node_modules/three/examples/jsm/renderers/common/UniformBuffer.js b/libs/three/examples/jsm/renderers/common/UniformBuffer.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/UniformBuffer.js
rename to libs/three/examples/jsm/renderers/common/UniformBuffer.js
diff --git a/node_modules/three/examples/jsm/renderers/common/UniformsGroup.js b/libs/three/examples/jsm/renderers/common/UniformsGroup.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/UniformsGroup.js
rename to libs/three/examples/jsm/renderers/common/UniformsGroup.js
diff --git a/node_modules/three/examples/jsm/renderers/common/nodes/NodeBuilderState.js b/libs/three/examples/jsm/renderers/common/nodes/NodeBuilderState.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/nodes/NodeBuilderState.js
rename to libs/three/examples/jsm/renderers/common/nodes/NodeBuilderState.js
diff --git a/node_modules/three/examples/jsm/renderers/common/nodes/NodeSampledTexture.js b/libs/three/examples/jsm/renderers/common/nodes/NodeSampledTexture.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/nodes/NodeSampledTexture.js
rename to libs/three/examples/jsm/renderers/common/nodes/NodeSampledTexture.js
diff --git a/node_modules/three/examples/jsm/renderers/common/nodes/NodeSampler.js b/libs/three/examples/jsm/renderers/common/nodes/NodeSampler.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/nodes/NodeSampler.js
rename to libs/three/examples/jsm/renderers/common/nodes/NodeSampler.js
diff --git a/node_modules/three/examples/jsm/renderers/common/nodes/NodeUniform.js b/libs/three/examples/jsm/renderers/common/nodes/NodeUniform.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/nodes/NodeUniform.js
rename to libs/three/examples/jsm/renderers/common/nodes/NodeUniform.js
diff --git a/node_modules/three/examples/jsm/renderers/common/nodes/NodeUniformsGroup.js b/libs/three/examples/jsm/renderers/common/nodes/NodeUniformsGroup.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/nodes/NodeUniformsGroup.js
rename to libs/three/examples/jsm/renderers/common/nodes/NodeUniformsGroup.js
diff --git a/node_modules/three/examples/jsm/renderers/common/nodes/Nodes.js b/libs/three/examples/jsm/renderers/common/nodes/Nodes.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/common/nodes/Nodes.js
rename to libs/three/examples/jsm/renderers/common/nodes/Nodes.js
diff --git a/node_modules/three/examples/jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js b/libs/three/examples/jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js
rename to libs/three/examples/jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js
diff --git a/node_modules/three/examples/jsm/renderers/webgl-legacy/nodes/SlotNode.js b/libs/three/examples/jsm/renderers/webgl-legacy/nodes/SlotNode.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgl-legacy/nodes/SlotNode.js
rename to libs/three/examples/jsm/renderers/webgl-legacy/nodes/SlotNode.js
diff --git a/node_modules/three/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js b/libs/three/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js
rename to libs/three/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js
diff --git a/node_modules/three/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodes.js b/libs/three/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodes.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodes.js
rename to libs/three/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodes.js
diff --git a/node_modules/three/examples/jsm/renderers/webgl/WebGLBackend.js b/libs/three/examples/jsm/renderers/webgl/WebGLBackend.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgl/WebGLBackend.js
rename to libs/three/examples/jsm/renderers/webgl/WebGLBackend.js
diff --git a/node_modules/three/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js b/libs/three/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js
rename to libs/three/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js
diff --git a/node_modules/three/examples/jsm/renderers/webgl/utils/WebGLAttributeUtils.js b/libs/three/examples/jsm/renderers/webgl/utils/WebGLAttributeUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgl/utils/WebGLAttributeUtils.js
rename to libs/three/examples/jsm/renderers/webgl/utils/WebGLAttributeUtils.js
diff --git a/node_modules/three/examples/jsm/renderers/webgl/utils/WebGLCapabilities.js b/libs/three/examples/jsm/renderers/webgl/utils/WebGLCapabilities.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgl/utils/WebGLCapabilities.js
rename to libs/three/examples/jsm/renderers/webgl/utils/WebGLCapabilities.js
diff --git a/node_modules/three/examples/jsm/renderers/webgl/utils/WebGLExtensions.js b/libs/three/examples/jsm/renderers/webgl/utils/WebGLExtensions.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgl/utils/WebGLExtensions.js
rename to libs/three/examples/jsm/renderers/webgl/utils/WebGLExtensions.js
diff --git a/node_modules/three/examples/jsm/renderers/webgl/utils/WebGLState.js b/libs/three/examples/jsm/renderers/webgl/utils/WebGLState.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgl/utils/WebGLState.js
rename to libs/three/examples/jsm/renderers/webgl/utils/WebGLState.js
diff --git a/node_modules/three/examples/jsm/renderers/webgl/utils/WebGLTextureUtils.js b/libs/three/examples/jsm/renderers/webgl/utils/WebGLTextureUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgl/utils/WebGLTextureUtils.js
rename to libs/three/examples/jsm/renderers/webgl/utils/WebGLTextureUtils.js
diff --git a/node_modules/three/examples/jsm/renderers/webgl/utils/WebGLUtils.js b/libs/three/examples/jsm/renderers/webgl/utils/WebGLUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgl/utils/WebGLUtils.js
rename to libs/three/examples/jsm/renderers/webgl/utils/WebGLUtils.js
diff --git a/node_modules/three/examples/jsm/renderers/webgpu/WebGPUBackend.js b/libs/three/examples/jsm/renderers/webgpu/WebGPUBackend.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgpu/WebGPUBackend.js
rename to libs/three/examples/jsm/renderers/webgpu/WebGPUBackend.js
diff --git a/node_modules/three/examples/jsm/renderers/webgpu/WebGPURenderer.js b/libs/three/examples/jsm/renderers/webgpu/WebGPURenderer.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgpu/WebGPURenderer.js
rename to libs/three/examples/jsm/renderers/webgpu/WebGPURenderer.js
diff --git a/node_modules/three/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js b/libs/three/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
rename to libs/three/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
diff --git a/node_modules/three/examples/jsm/renderers/webgpu/nodes/WGSLNodeFunction.js b/libs/three/examples/jsm/renderers/webgpu/nodes/WGSLNodeFunction.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgpu/nodes/WGSLNodeFunction.js
rename to libs/three/examples/jsm/renderers/webgpu/nodes/WGSLNodeFunction.js
diff --git a/node_modules/three/examples/jsm/renderers/webgpu/nodes/WGSLNodeParser.js b/libs/three/examples/jsm/renderers/webgpu/nodes/WGSLNodeParser.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgpu/nodes/WGSLNodeParser.js
rename to libs/three/examples/jsm/renderers/webgpu/nodes/WGSLNodeParser.js
diff --git a/node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUAttributeUtils.js b/libs/three/examples/jsm/renderers/webgpu/utils/WebGPUAttributeUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUAttributeUtils.js
rename to libs/three/examples/jsm/renderers/webgpu/utils/WebGPUAttributeUtils.js
diff --git a/node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js b/libs/three/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js
rename to libs/three/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js
diff --git a/node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUConstants.js b/libs/three/examples/jsm/renderers/webgpu/utils/WebGPUConstants.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUConstants.js
rename to libs/three/examples/jsm/renderers/webgpu/utils/WebGPUConstants.js
diff --git a/node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js b/libs/three/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js
rename to libs/three/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js
diff --git a/node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUTexturePassUtils.js b/libs/three/examples/jsm/renderers/webgpu/utils/WebGPUTexturePassUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUTexturePassUtils.js
rename to libs/three/examples/jsm/renderers/webgpu/utils/WebGPUTexturePassUtils.js
diff --git a/node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js b/libs/three/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js
rename to libs/three/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js
diff --git a/node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUUtils.js b/libs/three/examples/jsm/renderers/webgpu/utils/WebGPUUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/renderers/webgpu/utils/WebGPUUtils.js
rename to libs/three/examples/jsm/renderers/webgpu/utils/WebGPUUtils.js
diff --git a/node_modules/three/examples/jsm/shaders/ACESFilmicToneMappingShader.js b/libs/three/examples/jsm/shaders/ACESFilmicToneMappingShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/ACESFilmicToneMappingShader.js
rename to libs/three/examples/jsm/shaders/ACESFilmicToneMappingShader.js
diff --git a/node_modules/three/examples/jsm/shaders/AfterimageShader.js b/libs/three/examples/jsm/shaders/AfterimageShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/AfterimageShader.js
rename to libs/three/examples/jsm/shaders/AfterimageShader.js
diff --git a/node_modules/three/examples/jsm/shaders/BasicShader.js b/libs/three/examples/jsm/shaders/BasicShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/BasicShader.js
rename to libs/three/examples/jsm/shaders/BasicShader.js
diff --git a/node_modules/three/examples/jsm/shaders/BleachBypassShader.js b/libs/three/examples/jsm/shaders/BleachBypassShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/BleachBypassShader.js
rename to libs/three/examples/jsm/shaders/BleachBypassShader.js
diff --git a/node_modules/three/examples/jsm/shaders/BlendShader.js b/libs/three/examples/jsm/shaders/BlendShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/BlendShader.js
rename to libs/three/examples/jsm/shaders/BlendShader.js
diff --git a/node_modules/three/examples/jsm/shaders/BokehShader.js b/libs/three/examples/jsm/shaders/BokehShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/BokehShader.js
rename to libs/three/examples/jsm/shaders/BokehShader.js
diff --git a/node_modules/three/examples/jsm/shaders/BokehShader2.js b/libs/three/examples/jsm/shaders/BokehShader2.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/BokehShader2.js
rename to libs/three/examples/jsm/shaders/BokehShader2.js
diff --git a/node_modules/three/examples/jsm/shaders/BrightnessContrastShader.js b/libs/three/examples/jsm/shaders/BrightnessContrastShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/BrightnessContrastShader.js
rename to libs/three/examples/jsm/shaders/BrightnessContrastShader.js
diff --git a/node_modules/three/examples/jsm/shaders/ColorCorrectionShader.js b/libs/three/examples/jsm/shaders/ColorCorrectionShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/ColorCorrectionShader.js
rename to libs/three/examples/jsm/shaders/ColorCorrectionShader.js
diff --git a/node_modules/three/examples/jsm/shaders/ColorifyShader.js b/libs/three/examples/jsm/shaders/ColorifyShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/ColorifyShader.js
rename to libs/three/examples/jsm/shaders/ColorifyShader.js
diff --git a/node_modules/three/examples/jsm/shaders/ConvolutionShader.js b/libs/three/examples/jsm/shaders/ConvolutionShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/ConvolutionShader.js
rename to libs/three/examples/jsm/shaders/ConvolutionShader.js
diff --git a/node_modules/three/examples/jsm/shaders/CopyShader.js b/libs/three/examples/jsm/shaders/CopyShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/CopyShader.js
rename to libs/three/examples/jsm/shaders/CopyShader.js
diff --git a/node_modules/three/examples/jsm/shaders/DOFMipMapShader.js b/libs/three/examples/jsm/shaders/DOFMipMapShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/DOFMipMapShader.js
rename to libs/three/examples/jsm/shaders/DOFMipMapShader.js
diff --git a/node_modules/three/examples/jsm/shaders/DepthLimitedBlurShader.js b/libs/three/examples/jsm/shaders/DepthLimitedBlurShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/DepthLimitedBlurShader.js
rename to libs/three/examples/jsm/shaders/DepthLimitedBlurShader.js
diff --git a/node_modules/three/examples/jsm/shaders/DigitalGlitch.js b/libs/three/examples/jsm/shaders/DigitalGlitch.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/DigitalGlitch.js
rename to libs/three/examples/jsm/shaders/DigitalGlitch.js
diff --git a/node_modules/three/examples/jsm/shaders/DotScreenShader.js b/libs/three/examples/jsm/shaders/DotScreenShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/DotScreenShader.js
rename to libs/three/examples/jsm/shaders/DotScreenShader.js
diff --git a/node_modules/three/examples/jsm/shaders/ExposureShader.js b/libs/three/examples/jsm/shaders/ExposureShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/ExposureShader.js
rename to libs/three/examples/jsm/shaders/ExposureShader.js
diff --git a/node_modules/three/examples/jsm/shaders/FXAAShader.js b/libs/three/examples/jsm/shaders/FXAAShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/FXAAShader.js
rename to libs/three/examples/jsm/shaders/FXAAShader.js
diff --git a/node_modules/three/examples/jsm/shaders/FilmShader.js b/libs/three/examples/jsm/shaders/FilmShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/FilmShader.js
rename to libs/three/examples/jsm/shaders/FilmShader.js
diff --git a/node_modules/three/examples/jsm/shaders/FocusShader.js b/libs/three/examples/jsm/shaders/FocusShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/FocusShader.js
rename to libs/three/examples/jsm/shaders/FocusShader.js
diff --git a/node_modules/three/examples/jsm/shaders/FreiChenShader.js b/libs/three/examples/jsm/shaders/FreiChenShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/FreiChenShader.js
rename to libs/three/examples/jsm/shaders/FreiChenShader.js
diff --git a/node_modules/three/examples/jsm/shaders/GTAOShader.js b/libs/three/examples/jsm/shaders/GTAOShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/GTAOShader.js
rename to libs/three/examples/jsm/shaders/GTAOShader.js
diff --git a/node_modules/three/examples/jsm/shaders/GammaCorrectionShader.js b/libs/three/examples/jsm/shaders/GammaCorrectionShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/GammaCorrectionShader.js
rename to libs/three/examples/jsm/shaders/GammaCorrectionShader.js
diff --git a/node_modules/three/examples/jsm/shaders/GodRaysShader.js b/libs/three/examples/jsm/shaders/GodRaysShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/GodRaysShader.js
rename to libs/three/examples/jsm/shaders/GodRaysShader.js
diff --git a/node_modules/three/examples/jsm/shaders/HalftoneShader.js b/libs/three/examples/jsm/shaders/HalftoneShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/HalftoneShader.js
rename to libs/three/examples/jsm/shaders/HalftoneShader.js
diff --git a/node_modules/three/examples/jsm/shaders/HorizontalBlurShader.js b/libs/three/examples/jsm/shaders/HorizontalBlurShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/HorizontalBlurShader.js
rename to libs/three/examples/jsm/shaders/HorizontalBlurShader.js
diff --git a/node_modules/three/examples/jsm/shaders/HorizontalTiltShiftShader.js b/libs/three/examples/jsm/shaders/HorizontalTiltShiftShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/HorizontalTiltShiftShader.js
rename to libs/three/examples/jsm/shaders/HorizontalTiltShiftShader.js
diff --git a/node_modules/three/examples/jsm/shaders/HueSaturationShader.js b/libs/three/examples/jsm/shaders/HueSaturationShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/HueSaturationShader.js
rename to libs/three/examples/jsm/shaders/HueSaturationShader.js
diff --git a/node_modules/three/examples/jsm/shaders/KaleidoShader.js b/libs/three/examples/jsm/shaders/KaleidoShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/KaleidoShader.js
rename to libs/three/examples/jsm/shaders/KaleidoShader.js
diff --git a/node_modules/three/examples/jsm/shaders/LuminosityHighPassShader.js b/libs/three/examples/jsm/shaders/LuminosityHighPassShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/LuminosityHighPassShader.js
rename to libs/three/examples/jsm/shaders/LuminosityHighPassShader.js
diff --git a/node_modules/three/examples/jsm/shaders/LuminosityShader.js b/libs/three/examples/jsm/shaders/LuminosityShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/LuminosityShader.js
rename to libs/three/examples/jsm/shaders/LuminosityShader.js
diff --git a/node_modules/three/examples/jsm/shaders/MMDToonShader.js b/libs/three/examples/jsm/shaders/MMDToonShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/MMDToonShader.js
rename to libs/three/examples/jsm/shaders/MMDToonShader.js
diff --git a/node_modules/three/examples/jsm/shaders/MirrorShader.js b/libs/three/examples/jsm/shaders/MirrorShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/MirrorShader.js
rename to libs/three/examples/jsm/shaders/MirrorShader.js
diff --git a/node_modules/three/examples/jsm/shaders/NormalMapShader.js b/libs/three/examples/jsm/shaders/NormalMapShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/NormalMapShader.js
rename to libs/three/examples/jsm/shaders/NormalMapShader.js
diff --git a/node_modules/three/examples/jsm/shaders/OutputShader.js b/libs/three/examples/jsm/shaders/OutputShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/OutputShader.js
rename to libs/three/examples/jsm/shaders/OutputShader.js
diff --git a/node_modules/three/examples/jsm/shaders/PoissonDenoiseShader.js b/libs/three/examples/jsm/shaders/PoissonDenoiseShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/PoissonDenoiseShader.js
rename to libs/three/examples/jsm/shaders/PoissonDenoiseShader.js
diff --git a/node_modules/three/examples/jsm/shaders/RGBShiftShader.js b/libs/three/examples/jsm/shaders/RGBShiftShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/RGBShiftShader.js
rename to libs/three/examples/jsm/shaders/RGBShiftShader.js
diff --git a/node_modules/three/examples/jsm/shaders/SAOShader.js b/libs/three/examples/jsm/shaders/SAOShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/SAOShader.js
rename to libs/three/examples/jsm/shaders/SAOShader.js
diff --git a/node_modules/three/examples/jsm/shaders/SMAAShader.js b/libs/three/examples/jsm/shaders/SMAAShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/SMAAShader.js
rename to libs/three/examples/jsm/shaders/SMAAShader.js
diff --git a/node_modules/three/examples/jsm/shaders/SSAOShader.js b/libs/three/examples/jsm/shaders/SSAOShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/SSAOShader.js
rename to libs/three/examples/jsm/shaders/SSAOShader.js
diff --git a/node_modules/three/examples/jsm/shaders/SSRShader.js b/libs/three/examples/jsm/shaders/SSRShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/SSRShader.js
rename to libs/three/examples/jsm/shaders/SSRShader.js
diff --git a/node_modules/three/examples/jsm/shaders/SepiaShader.js b/libs/three/examples/jsm/shaders/SepiaShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/SepiaShader.js
rename to libs/three/examples/jsm/shaders/SepiaShader.js
diff --git a/node_modules/three/examples/jsm/shaders/SobelOperatorShader.js b/libs/three/examples/jsm/shaders/SobelOperatorShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/SobelOperatorShader.js
rename to libs/three/examples/jsm/shaders/SobelOperatorShader.js
diff --git a/node_modules/three/examples/jsm/shaders/SubsurfaceScatteringShader.js b/libs/three/examples/jsm/shaders/SubsurfaceScatteringShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/SubsurfaceScatteringShader.js
rename to libs/three/examples/jsm/shaders/SubsurfaceScatteringShader.js
diff --git a/node_modules/three/examples/jsm/shaders/TechnicolorShader.js b/libs/three/examples/jsm/shaders/TechnicolorShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/TechnicolorShader.js
rename to libs/three/examples/jsm/shaders/TechnicolorShader.js
diff --git a/node_modules/three/examples/jsm/shaders/ToonShader.js b/libs/three/examples/jsm/shaders/ToonShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/ToonShader.js
rename to libs/three/examples/jsm/shaders/ToonShader.js
diff --git a/node_modules/three/examples/jsm/shaders/TriangleBlurShader.js b/libs/three/examples/jsm/shaders/TriangleBlurShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/TriangleBlurShader.js
rename to libs/three/examples/jsm/shaders/TriangleBlurShader.js
diff --git a/node_modules/three/examples/jsm/shaders/UnpackDepthRGBAShader.js b/libs/three/examples/jsm/shaders/UnpackDepthRGBAShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/UnpackDepthRGBAShader.js
rename to libs/three/examples/jsm/shaders/UnpackDepthRGBAShader.js
diff --git a/node_modules/three/examples/jsm/shaders/VelocityShader.js b/libs/three/examples/jsm/shaders/VelocityShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/VelocityShader.js
rename to libs/three/examples/jsm/shaders/VelocityShader.js
diff --git a/node_modules/three/examples/jsm/shaders/VerticalBlurShader.js b/libs/three/examples/jsm/shaders/VerticalBlurShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/VerticalBlurShader.js
rename to libs/three/examples/jsm/shaders/VerticalBlurShader.js
diff --git a/node_modules/three/examples/jsm/shaders/VerticalTiltShiftShader.js b/libs/three/examples/jsm/shaders/VerticalTiltShiftShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/VerticalTiltShiftShader.js
rename to libs/three/examples/jsm/shaders/VerticalTiltShiftShader.js
diff --git a/node_modules/three/examples/jsm/shaders/VignetteShader.js b/libs/three/examples/jsm/shaders/VignetteShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/VignetteShader.js
rename to libs/three/examples/jsm/shaders/VignetteShader.js
diff --git a/node_modules/three/examples/jsm/shaders/VolumeShader.js b/libs/three/examples/jsm/shaders/VolumeShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/VolumeShader.js
rename to libs/three/examples/jsm/shaders/VolumeShader.js
diff --git a/node_modules/three/examples/jsm/shaders/WaterRefractionShader.js b/libs/three/examples/jsm/shaders/WaterRefractionShader.js
similarity index 100%
rename from node_modules/three/examples/jsm/shaders/WaterRefractionShader.js
rename to libs/three/examples/jsm/shaders/WaterRefractionShader.js
diff --git a/node_modules/three/examples/jsm/textures/FlakesTexture.js b/libs/three/examples/jsm/textures/FlakesTexture.js
similarity index 100%
rename from node_modules/three/examples/jsm/textures/FlakesTexture.js
rename to libs/three/examples/jsm/textures/FlakesTexture.js
diff --git a/node_modules/three/examples/jsm/transpiler/AST.js b/libs/three/examples/jsm/transpiler/AST.js
similarity index 100%
rename from node_modules/three/examples/jsm/transpiler/AST.js
rename to libs/three/examples/jsm/transpiler/AST.js
diff --git a/node_modules/three/examples/jsm/transpiler/GLSLDecoder.js b/libs/three/examples/jsm/transpiler/GLSLDecoder.js
similarity index 100%
rename from node_modules/three/examples/jsm/transpiler/GLSLDecoder.js
rename to libs/three/examples/jsm/transpiler/GLSLDecoder.js
diff --git a/node_modules/three/examples/jsm/transpiler/ShaderToyDecoder.js b/libs/three/examples/jsm/transpiler/ShaderToyDecoder.js
similarity index 100%
rename from node_modules/three/examples/jsm/transpiler/ShaderToyDecoder.js
rename to libs/three/examples/jsm/transpiler/ShaderToyDecoder.js
diff --git a/node_modules/three/examples/jsm/transpiler/TSLEncoder.js b/libs/three/examples/jsm/transpiler/TSLEncoder.js
similarity index 100%
rename from node_modules/three/examples/jsm/transpiler/TSLEncoder.js
rename to libs/three/examples/jsm/transpiler/TSLEncoder.js
diff --git a/node_modules/three/examples/jsm/transpiler/Transpiler.js b/libs/three/examples/jsm/transpiler/Transpiler.js
similarity index 100%
rename from node_modules/three/examples/jsm/transpiler/Transpiler.js
rename to libs/three/examples/jsm/transpiler/Transpiler.js
diff --git a/node_modules/three/examples/jsm/utils/BufferGeometryUtils.js b/libs/three/examples/jsm/utils/BufferGeometryUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/BufferGeometryUtils.js
rename to libs/three/examples/jsm/utils/BufferGeometryUtils.js
diff --git a/node_modules/three/examples/jsm/utils/CameraUtils.js b/libs/three/examples/jsm/utils/CameraUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/CameraUtils.js
rename to libs/three/examples/jsm/utils/CameraUtils.js
diff --git a/node_modules/three/examples/jsm/utils/GPUStatsPanel.js b/libs/three/examples/jsm/utils/GPUStatsPanel.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/GPUStatsPanel.js
rename to libs/three/examples/jsm/utils/GPUStatsPanel.js
diff --git a/node_modules/three/examples/jsm/utils/GeometryCompressionUtils.js b/libs/three/examples/jsm/utils/GeometryCompressionUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/GeometryCompressionUtils.js
rename to libs/three/examples/jsm/utils/GeometryCompressionUtils.js
diff --git a/node_modules/three/examples/jsm/utils/GeometryUtils.js b/libs/three/examples/jsm/utils/GeometryUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/GeometryUtils.js
rename to libs/three/examples/jsm/utils/GeometryUtils.js
diff --git a/node_modules/three/examples/jsm/utils/LDrawUtils.js b/libs/three/examples/jsm/utils/LDrawUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/LDrawUtils.js
rename to libs/three/examples/jsm/utils/LDrawUtils.js
diff --git a/node_modules/three/examples/jsm/utils/PackedPhongMaterial.js b/libs/three/examples/jsm/utils/PackedPhongMaterial.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/PackedPhongMaterial.js
rename to libs/three/examples/jsm/utils/PackedPhongMaterial.js
diff --git a/node_modules/three/examples/jsm/utils/SceneUtils.js b/libs/three/examples/jsm/utils/SceneUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/SceneUtils.js
rename to libs/three/examples/jsm/utils/SceneUtils.js
diff --git a/node_modules/three/examples/jsm/utils/ShadowMapViewer.js b/libs/three/examples/jsm/utils/ShadowMapViewer.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/ShadowMapViewer.js
rename to libs/three/examples/jsm/utils/ShadowMapViewer.js
diff --git a/node_modules/three/examples/jsm/utils/SkeletonUtils.js b/libs/three/examples/jsm/utils/SkeletonUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/SkeletonUtils.js
rename to libs/three/examples/jsm/utils/SkeletonUtils.js
diff --git a/node_modules/three/examples/jsm/utils/SortUtils.js b/libs/three/examples/jsm/utils/SortUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/SortUtils.js
rename to libs/three/examples/jsm/utils/SortUtils.js
diff --git a/node_modules/three/examples/jsm/utils/TextureUtils.js b/libs/three/examples/jsm/utils/TextureUtils.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/TextureUtils.js
rename to libs/three/examples/jsm/utils/TextureUtils.js
diff --git a/node_modules/three/examples/jsm/utils/UVsDebug.js b/libs/three/examples/jsm/utils/UVsDebug.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/UVsDebug.js
rename to libs/three/examples/jsm/utils/UVsDebug.js
diff --git a/node_modules/three/examples/jsm/utils/WorkerPool.js b/libs/three/examples/jsm/utils/WorkerPool.js
similarity index 100%
rename from node_modules/three/examples/jsm/utils/WorkerPool.js
rename to libs/three/examples/jsm/utils/WorkerPool.js
diff --git a/node_modules/three/examples/jsm/webxr/ARButton.js b/libs/three/examples/jsm/webxr/ARButton.js
similarity index 100%
rename from node_modules/three/examples/jsm/webxr/ARButton.js
rename to libs/three/examples/jsm/webxr/ARButton.js
diff --git a/node_modules/three/examples/jsm/webxr/OculusHandModel.js b/libs/three/examples/jsm/webxr/OculusHandModel.js
similarity index 100%
rename from node_modules/three/examples/jsm/webxr/OculusHandModel.js
rename to libs/three/examples/jsm/webxr/OculusHandModel.js
diff --git a/node_modules/three/examples/jsm/webxr/OculusHandPointerModel.js b/libs/three/examples/jsm/webxr/OculusHandPointerModel.js
similarity index 100%
rename from node_modules/three/examples/jsm/webxr/OculusHandPointerModel.js
rename to libs/three/examples/jsm/webxr/OculusHandPointerModel.js
diff --git a/node_modules/three/examples/jsm/webxr/Text2D.js b/libs/three/examples/jsm/webxr/Text2D.js
similarity index 100%
rename from node_modules/three/examples/jsm/webxr/Text2D.js
rename to libs/three/examples/jsm/webxr/Text2D.js
diff --git a/node_modules/three/examples/jsm/webxr/VRButton.js b/libs/three/examples/jsm/webxr/VRButton.js
similarity index 100%
rename from node_modules/three/examples/jsm/webxr/VRButton.js
rename to libs/three/examples/jsm/webxr/VRButton.js
diff --git a/node_modules/three/examples/jsm/webxr/XRButton.js b/libs/three/examples/jsm/webxr/XRButton.js
similarity index 100%
rename from node_modules/three/examples/jsm/webxr/XRButton.js
rename to libs/three/examples/jsm/webxr/XRButton.js
diff --git a/node_modules/three/examples/jsm/webxr/XRControllerModelFactory.js b/libs/three/examples/jsm/webxr/XRControllerModelFactory.js
similarity index 100%
rename from node_modules/three/examples/jsm/webxr/XRControllerModelFactory.js
rename to libs/three/examples/jsm/webxr/XRControllerModelFactory.js
diff --git a/node_modules/three/examples/jsm/webxr/XREstimatedLight.js b/libs/three/examples/jsm/webxr/XREstimatedLight.js
similarity index 100%
rename from node_modules/three/examples/jsm/webxr/XREstimatedLight.js
rename to libs/three/examples/jsm/webxr/XREstimatedLight.js
diff --git a/node_modules/three/examples/jsm/webxr/XRHandMeshModel.js b/libs/three/examples/jsm/webxr/XRHandMeshModel.js
similarity index 100%
rename from node_modules/three/examples/jsm/webxr/XRHandMeshModel.js
rename to libs/three/examples/jsm/webxr/XRHandMeshModel.js
diff --git a/node_modules/three/examples/jsm/webxr/XRHandModelFactory.js b/libs/three/examples/jsm/webxr/XRHandModelFactory.js
similarity index 100%
rename from node_modules/three/examples/jsm/webxr/XRHandModelFactory.js
rename to libs/three/examples/jsm/webxr/XRHandModelFactory.js
diff --git a/node_modules/three/examples/jsm/webxr/XRHandPrimitiveModel.js b/libs/three/examples/jsm/webxr/XRHandPrimitiveModel.js
similarity index 100%
rename from node_modules/three/examples/jsm/webxr/XRHandPrimitiveModel.js
rename to libs/three/examples/jsm/webxr/XRHandPrimitiveModel.js
diff --git a/node_modules/three/examples/jsm/webxr/XRPlanes.js b/libs/three/examples/jsm/webxr/XRPlanes.js
similarity index 100%
rename from node_modules/three/examples/jsm/webxr/XRPlanes.js
rename to libs/three/examples/jsm/webxr/XRPlanes.js
diff --git a/node_modules/three/package.json b/libs/three/package.json
similarity index 100%
rename from node_modules/three/package.json
rename to libs/three/package.json
diff --git a/libs/three/src/Three.Legacy.js b/libs/three/src/Three.Legacy.js
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/libs/three/src/Three.Legacy.js
@@ -0,0 +1 @@
+
diff --git a/libs/three/src/Three.js b/libs/three/src/Three.js
new file mode 100644
index 000000000..aa29f27e2
--- /dev/null
+++ b/libs/three/src/Three.js
@@ -0,0 +1,184 @@
+import { REVISION } from './constants.js';
+
+export { WebGLArrayRenderTarget } from './renderers/WebGLArrayRenderTarget.js';
+export { WebGL3DRenderTarget } from './renderers/WebGL3DRenderTarget.js';
+export { WebGLMultipleRenderTargets } from './renderers/WebGLMultipleRenderTargets.js';
+export { WebGLCubeRenderTarget } from './renderers/WebGLCubeRenderTarget.js';
+export { WebGLRenderTarget } from './renderers/WebGLRenderTarget.js';
+export { WebGLRenderer } from './renderers/WebGLRenderer.js';
+export { WebGL1Renderer } from './renderers/WebGL1Renderer.js';
+export { ShaderLib } from './renderers/shaders/ShaderLib.js';
+export { UniformsLib } from './renderers/shaders/UniformsLib.js';
+export { UniformsUtils } from './renderers/shaders/UniformsUtils.js';
+export { ShaderChunk } from './renderers/shaders/ShaderChunk.js';
+export { FogExp2 } from './scenes/FogExp2.js';
+export { Fog } from './scenes/Fog.js';
+export { Scene } from './scenes/Scene.js';
+export { Sprite } from './objects/Sprite.js';
+export { LOD } from './objects/LOD.js';
+export { SkinnedMesh } from './objects/SkinnedMesh.js';
+export { Skeleton } from './objects/Skeleton.js';
+export { Bone } from './objects/Bone.js';
+export { Mesh } from './objects/Mesh.js';
+export { InstancedMesh } from './objects/InstancedMesh.js';
+export { BatchedMesh } from './objects/BatchedMesh.js';
+export { LineSegments } from './objects/LineSegments.js';
+export { LineLoop } from './objects/LineLoop.js';
+export { Line } from './objects/Line.js';
+export { Points } from './objects/Points.js';
+export { Group } from './objects/Group.js';
+export { VideoTexture } from './textures/VideoTexture.js';
+export { FramebufferTexture } from './textures/FramebufferTexture.js';
+export { Source } from './textures/Source.js';
+export { DataTexture } from './textures/DataTexture.js';
+export { DataArrayTexture } from './textures/DataArrayTexture.js';
+export { Data3DTexture } from './textures/Data3DTexture.js';
+export { CompressedTexture } from './textures/CompressedTexture.js';
+export { CompressedArrayTexture } from './textures/CompressedArrayTexture.js';
+export { CompressedCubeTexture } from './textures/CompressedCubeTexture.js';
+export { CubeTexture } from './textures/CubeTexture.js';
+export { CanvasTexture } from './textures/CanvasTexture.js';
+export { DepthTexture } from './textures/DepthTexture.js';
+export { Texture } from './textures/Texture.js';
+export * from './geometries/Geometries.js';
+export * from './materials/Materials.js';
+export { AnimationLoader } from './loaders/AnimationLoader.js';
+export { CompressedTextureLoader } from './loaders/CompressedTextureLoader.js';
+export { CubeTextureLoader } from './loaders/CubeTextureLoader.js';
+export { DataTextureLoader } from './loaders/DataTextureLoader.js';
+export { TextureLoader } from './loaders/TextureLoader.js';
+export { ObjectLoader } from './loaders/ObjectLoader.js';
+export { MaterialLoader } from './loaders/MaterialLoader.js';
+export { BufferGeometryLoader } from './loaders/BufferGeometryLoader.js';
+export { DefaultLoadingManager, LoadingManager } from './loaders/LoadingManager.js';
+export { ImageLoader } from './loaders/ImageLoader.js';
+export { ImageBitmapLoader } from './loaders/ImageBitmapLoader.js';
+export { FileLoader } from './loaders/FileLoader.js';
+export { Loader } from './loaders/Loader.js';
+export { LoaderUtils } from './loaders/LoaderUtils.js';
+export { Cache } from './loaders/Cache.js';
+export { AudioLoader } from './loaders/AudioLoader.js';
+export { SpotLight } from './lights/SpotLight.js';
+export { PointLight } from './lights/PointLight.js';
+export { RectAreaLight } from './lights/RectAreaLight.js';
+export { HemisphereLight } from './lights/HemisphereLight.js';
+export { DirectionalLight } from './lights/DirectionalLight.js';
+export { AmbientLight } from './lights/AmbientLight.js';
+export { Light } from './lights/Light.js';
+export { LightProbe } from './lights/LightProbe.js';
+export { StereoCamera } from './cameras/StereoCamera.js';
+export { PerspectiveCamera } from './cameras/PerspectiveCamera.js';
+export { OrthographicCamera } from './cameras/OrthographicCamera.js';
+export { CubeCamera } from './cameras/CubeCamera.js';
+export { ArrayCamera } from './cameras/ArrayCamera.js';
+export { Camera } from './cameras/Camera.js';
+export { AudioListener } from './audio/AudioListener.js';
+export { PositionalAudio } from './audio/PositionalAudio.js';
+export { AudioContext } from './audio/AudioContext.js';
+export { AudioAnalyser } from './audio/AudioAnalyser.js';
+export { Audio } from './audio/Audio.js';
+export { VectorKeyframeTrack } from './animation/tracks/VectorKeyframeTrack.js';
+export { StringKeyframeTrack } from './animation/tracks/StringKeyframeTrack.js';
+export { QuaternionKeyframeTrack } from './animation/tracks/QuaternionKeyframeTrack.js';
+export { NumberKeyframeTrack } from './animation/tracks/NumberKeyframeTrack.js';
+export { ColorKeyframeTrack } from './animation/tracks/ColorKeyframeTrack.js';
+export { BooleanKeyframeTrack } from './animation/tracks/BooleanKeyframeTrack.js';
+export { PropertyMixer } from './animation/PropertyMixer.js';
+export { PropertyBinding } from './animation/PropertyBinding.js';
+export { KeyframeTrack } from './animation/KeyframeTrack.js';
+export { AnimationUtils } from './animation/AnimationUtils.js';
+export { AnimationObjectGroup } from './animation/AnimationObjectGroup.js';
+export { AnimationMixer } from './animation/AnimationMixer.js';
+export { AnimationClip } from './animation/AnimationClip.js';
+export { AnimationAction } from './animation/AnimationAction.js';
+export { RenderTarget } from './core/RenderTarget.js';
+export { Uniform } from './core/Uniform.js';
+export { UniformsGroup } from './core/UniformsGroup.js';
+export { InstancedBufferGeometry } from './core/InstancedBufferGeometry.js';
+export { BufferGeometry } from './core/BufferGeometry.js';
+export { InterleavedBufferAttribute } from './core/InterleavedBufferAttribute.js';
+export { InstancedInterleavedBuffer } from './core/InstancedInterleavedBuffer.js';
+export { InterleavedBuffer } from './core/InterleavedBuffer.js';
+export { InstancedBufferAttribute } from './core/InstancedBufferAttribute.js';
+export { GLBufferAttribute } from './core/GLBufferAttribute.js';
+export * from './core/BufferAttribute.js';
+export { Object3D } from './core/Object3D.js';
+export { Raycaster } from './core/Raycaster.js';
+export { Layers } from './core/Layers.js';
+export { EventDispatcher } from './core/EventDispatcher.js';
+export { Clock } from './core/Clock.js';
+export { QuaternionLinearInterpolant } from './math/interpolants/QuaternionLinearInterpolant.js';
+export { LinearInterpolant } from './math/interpolants/LinearInterpolant.js';
+export { DiscreteInterpolant } from './math/interpolants/DiscreteInterpolant.js';
+export { CubicInterpolant } from './math/interpolants/CubicInterpolant.js';
+export { Interpolant } from './math/Interpolant.js';
+export { Triangle } from './math/Triangle.js';
+export { MathUtils } from './math/MathUtils.js';
+export { Spherical } from './math/Spherical.js';
+export { Cylindrical } from './math/Cylindrical.js';
+export { Plane } from './math/Plane.js';
+export { Frustum } from './math/Frustum.js';
+export { Sphere } from './math/Sphere.js';
+export { Ray } from './math/Ray.js';
+export { Matrix4 } from './math/Matrix4.js';
+export { Matrix3 } from './math/Matrix3.js';
+export { Box3 } from './math/Box3.js';
+export { Box2 } from './math/Box2.js';
+export { Line3 } from './math/Line3.js';
+export { Euler } from './math/Euler.js';
+export { Vector4 } from './math/Vector4.js';
+export { Vector3 } from './math/Vector3.js';
+export { Vector2 } from './math/Vector2.js';
+export { Quaternion } from './math/Quaternion.js';
+export { Color } from './math/Color.js';
+export { ColorManagement } from './math/ColorManagement.js';
+export { SphericalHarmonics3 } from './math/SphericalHarmonics3.js';
+export { SpotLightHelper } from './helpers/SpotLightHelper.js';
+export { SkeletonHelper } from './helpers/SkeletonHelper.js';
+export { PointLightHelper } from './helpers/PointLightHelper.js';
+export { HemisphereLightHelper } from './helpers/HemisphereLightHelper.js';
+export { GridHelper } from './helpers/GridHelper.js';
+export { PolarGridHelper } from './helpers/PolarGridHelper.js';
+export { DirectionalLightHelper } from './helpers/DirectionalLightHelper.js';
+export { CameraHelper } from './helpers/CameraHelper.js';
+export { BoxHelper } from './helpers/BoxHelper.js';
+export { Box3Helper } from './helpers/Box3Helper.js';
+export { PlaneHelper } from './helpers/PlaneHelper.js';
+export { ArrowHelper } from './helpers/ArrowHelper.js';
+export { AxesHelper } from './helpers/AxesHelper.js';
+export * from './extras/curves/Curves.js';
+export { Shape } from './extras/core/Shape.js';
+export { Path } from './extras/core/Path.js';
+export { ShapePath } from './extras/core/ShapePath.js';
+export { CurvePath } from './extras/core/CurvePath.js';
+export { Curve } from './extras/core/Curve.js';
+export { DataUtils } from './extras/DataUtils.js';
+export { ImageUtils } from './extras/ImageUtils.js';
+export { ShapeUtils } from './extras/ShapeUtils.js';
+export { PMREMGenerator } from './extras/PMREMGenerator.js';
+export { WebGLUtils } from './renderers/webgl/WebGLUtils.js';
+export { createCanvasElement } from './utils.js';
+export * from './constants.js';
+export * from './Three.Legacy.js';
+
+if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) {
+
+ __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: {
+ revision: REVISION,
+ } } ) );
+
+}
+
+if ( typeof window !== 'undefined' ) {
+
+ if ( window.__THREE__ ) {
+
+ console.warn( 'WARNING: Multiple instances of Three.js being imported.' );
+
+ } else {
+
+ window.__THREE__ = REVISION;
+
+ }
+
+}
diff --git a/libs/three/src/animation/AnimationAction.js b/libs/three/src/animation/AnimationAction.js
new file mode 100644
index 000000000..056297b26
--- /dev/null
+++ b/libs/three/src/animation/AnimationAction.js
@@ -0,0 +1,700 @@
+import { WrapAroundEnding, ZeroCurvatureEnding, ZeroSlopeEnding, LoopPingPong, LoopOnce, LoopRepeat, NormalAnimationBlendMode, AdditiveAnimationBlendMode } from '../constants.js';
+
+
+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 ) {
+
+ deltaTime = 0;
+
+ } else {
+
+
+ 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;
+
+ }
+
+}
+
+
+export { AnimationAction };
diff --git a/libs/three/src/animation/AnimationClip.js b/libs/three/src/animation/AnimationClip.js
new file mode 100644
index 000000000..20d1da29c
--- /dev/null
+++ b/libs/three/src/animation/AnimationClip.js
@@ -0,0 +1,473 @@
+import * as AnimationUtils from './AnimationUtils.js';
+import { KeyframeTrack } from './KeyframeTrack.js';
+import { BooleanKeyframeTrack } from './tracks/BooleanKeyframeTrack.js';
+import { ColorKeyframeTrack } from './tracks/ColorKeyframeTrack.js';
+import { NumberKeyframeTrack } from './tracks/NumberKeyframeTrack.js';
+import { QuaternionKeyframeTrack } from './tracks/QuaternionKeyframeTrack.js';
+import { StringKeyframeTrack } from './tracks/StringKeyframeTrack.js';
+import { VectorKeyframeTrack } from './tracks/VectorKeyframeTrack.js';
+import * as MathUtils from '../math/MathUtils.js';
+import { NormalAnimationBlendMode } from '../constants.js';
+
+class AnimationClip {
+
+ constructor( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) {
+
+ this.name = name;
+ this.tracks = tracks;
+ this.duration = duration;
+ this.blendMode = blendMode;
+
+ this.uuid = MathUtils.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;
+
+ } 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 );
+
+ }
+
+}
+
+export { AnimationClip };
diff --git a/libs/three/src/animation/AnimationMixer.js b/libs/three/src/animation/AnimationMixer.js
new file mode 100644
index 000000000..e7e0f3ea5
--- /dev/null
+++ b/libs/three/src/animation/AnimationMixer.js
@@ -0,0 +1,770 @@
+import { AnimationAction } from './AnimationAction.js';
+import { EventDispatcher } from '../core/EventDispatcher.js';
+import { LinearInterpolant } from '../math/interpolants/LinearInterpolant.js';
+import { PropertyBinding } from './PropertyBinding.js';
+import { PropertyMixer } from './PropertyMixer.js';
+import { AnimationClip } from './AnimationClip.js';
+import { NormalAnimationBlendMode } from '../constants.js';
+
+
+const _controlInterpolantsResultBuffer = new Float32Array( 1 );
+
+
+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 ) {
+
+ ++ binding.referenceCount;
+ 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, _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 );
+
+ }
+
+ }
+
+}
+
+export { AnimationMixer };
diff --git a/libs/three/src/animation/AnimationObjectGroup.js b/libs/three/src/animation/AnimationObjectGroup.js
new file mode 100644
index 000000000..d43a114f0
--- /dev/null
+++ b/libs/three/src/animation/AnimationObjectGroup.js
@@ -0,0 +1,387 @@
+import { PropertyBinding } from './PropertyBinding.js';
+import * as MathUtils from '../math/MathUtils.js';
+
+/**
+ *
+ * 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.
+ */
+
+class AnimationObjectGroup {
+
+ constructor() {
+
+ this.isAnimationObjectGroup = true;
+
+ 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;
+
+ }
+
+ };
+
+ }
+
+ add() {
+
+ 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() {
+
+ 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() {
+
+ 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 ];
+
+ if ( lastIndex > 0 ) {
+
+ 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_( 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_( 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();
+
+ }
+
+ }
+
+}
+
+export { AnimationObjectGroup };
diff --git a/libs/three/src/animation/AnimationUtils.js b/libs/three/src/animation/AnimationUtils.js
new file mode 100644
index 000000000..4b144c537
--- /dev/null
+++ b/libs/three/src/animation/AnimationUtils.js
@@ -0,0 +1,356 @@
+import { Quaternion } from '../math/Quaternion.js';
+import { AdditiveAnimationBlendMode } from '../constants.js';
+
+// converts an array to a specific type
+function convertArray( 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
+
+}
+
+function isTypedArray( object ) {
+
+ return ArrayBuffer.isView( object ) &&
+ ! ( object instanceof DataView );
+
+}
+
+// returns an array by which times and values can be sorted
+function getKeyframeOrder( 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
+function sortedArray( 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
+function flattenJSON( 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 );
+
+ }
+
+}
+
+function subclip( 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 = convertArray( times, track.times.constructor );
+ track.values = 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;
+
+}
+
+function makeClipAdditive( 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 = referenceTrack.values.slice( 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 = referenceTrack.values.slice( startIndex, endIndex );
+
+ } else {
+
+ // Interpolate to the reference value
+ const interpolant = referenceTrack.createInterpolant();
+ const startIndex = referenceOffset;
+ const endIndex = referenceValueSize - referenceOffset;
+ interpolant.evaluate( referenceTime );
+ referenceValue = interpolant.resultBuffer.slice( 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;
+
+}
+
+const AnimationUtils = {
+ convertArray: convertArray,
+ isTypedArray: isTypedArray,
+ getKeyframeOrder: getKeyframeOrder,
+ sortedArray: sortedArray,
+ flattenJSON: flattenJSON,
+ subclip: subclip,
+ makeClipAdditive: makeClipAdditive
+};
+
+export {
+ convertArray,
+ isTypedArray,
+ getKeyframeOrder,
+ sortedArray,
+ flattenJSON,
+ subclip,
+ makeClipAdditive,
+ AnimationUtils
+};
diff --git a/libs/three/src/animation/KeyframeTrack.js b/libs/three/src/animation/KeyframeTrack.js
new file mode 100644
index 000000000..9da91c67f
--- /dev/null
+++ b/libs/three/src/animation/KeyframeTrack.js
@@ -0,0 +1,462 @@
+import {
+ InterpolateLinear,
+ InterpolateSmooth,
+ InterpolateDiscrete
+} from '../constants.js';
+import { CubicInterpolant } from '../math/interpolants/CubicInterpolant.js';
+import { LinearInterpolant } from '../math/interpolants/LinearInterpolant.js';
+import { DiscreteInterpolant } from '../math/interpolants/DiscreteInterpolant.js';
+import * as AnimationUtils from './AnimationUtils.js';
+
+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 = times.slice( from, to );
+ this.values = this.values.slice( 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 = this.times.slice(),
+ values = this.values.slice(),
+ 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 = times.slice( 0, writeIndex );
+ this.values = values.slice( 0, writeIndex * stride );
+
+ } else {
+
+ this.times = times;
+ this.values = values;
+
+ }
+
+ return this;
+
+ }
+
+ clone() {
+
+ const times = this.times.slice();
+ const values = this.values.slice();
+
+ 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;
+
+export { KeyframeTrack };
diff --git a/libs/three/src/animation/PropertyBinding.js b/libs/three/src/animation/PropertyBinding.js
new file mode 100644
index 000000000..eb3afe519
--- /dev/null
+++ b/libs/three/src/animation/PropertyBinding.js
@@ -0,0 +1,719 @@
+// 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 = /*@__PURE__*/ /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar );
+
+// Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'.
+const _nodeRe = /*@__PURE__*/ /(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 = /*@__PURE__*/ /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar );
+
+// Property and accessor. May not contain reserved characters. Accessor may
+// contain any non-bracket characters.
+const _propertyRe = /*@__PURE__*/ /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar );
+
+const _trackRe = new RegExp( ''
+ + '^'
+ + _directoryRe
+ + _nodeRe
+ + _objectRe
+ + _propertyRe
+ + '$'
+);
+
+const _supportedObjectNames = [ 'material', 'materials', 'bones', 'map' ];
+
+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 );
+
+ 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 === null ) {
+
+ 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 === undefined || 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.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.warn( 'THREE.PropertyBinding: No target node found for track: ' + this.path + '.' );
+ 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;
+
+ case 'map':
+
+ if ( 'map' in targetObject ) {
+
+ targetObject = targetObject.map;
+ break;
+
+ }
+
+ if ( ! targetObject.material ) {
+
+ console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this );
+ return;
+
+ }
+
+ if ( ! targetObject.material.map ) {
+
+ console.error( 'THREE.PropertyBinding: Can not bind to material.map as node.material does not have a map.', this );
+ return;
+
+ }
+
+ targetObject = targetObject.material.map;
+ 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.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 ];
+
+ }
+
+ }
+
+ 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,
+
+ ]
+
+];
+
+
+export { PropertyBinding };
diff --git a/libs/three/src/animation/PropertyMixer.js b/libs/three/src/animation/PropertyMixer.js
new file mode 100644
index 000000000..751c90d7e
--- /dev/null
+++ b/libs/three/src/animation/PropertyMixer.js
@@ -0,0 +1,318 @@
+import { Quaternion } from '../math/Quaternion.js';
+
+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;
+
+ }
+
+ }
+
+}
+
+
+export { PropertyMixer };
diff --git a/libs/three/src/animation/tracks/BooleanKeyframeTrack.js b/libs/three/src/animation/tracks/BooleanKeyframeTrack.js
new file mode 100644
index 000000000..277b36963
--- /dev/null
+++ b/libs/three/src/animation/tracks/BooleanKeyframeTrack.js
@@ -0,0 +1,19 @@
+import { InterpolateDiscrete } from '../../constants.js';
+import { KeyframeTrack } from '../KeyframeTrack.js';
+
+/**
+ * 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;
+
+// Note: Actually this track could have a optimized / compressed
+// representation of a single value and a custom interpolant that
+// computes "firstValue ^ isOdd( index )".
+
+export { BooleanKeyframeTrack };
diff --git a/libs/three/src/animation/tracks/ColorKeyframeTrack.js b/libs/three/src/animation/tracks/ColorKeyframeTrack.js
new file mode 100644
index 000000000..ee789efac
--- /dev/null
+++ b/libs/three/src/animation/tracks/ColorKeyframeTrack.js
@@ -0,0 +1,15 @@
+import { KeyframeTrack } from '../KeyframeTrack.js';
+
+/**
+ * A Track of keyframe values that represent color.
+ */
+class ColorKeyframeTrack extends KeyframeTrack {}
+
+ColorKeyframeTrack.prototype.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.
+
+export { ColorKeyframeTrack };
diff --git a/libs/three/src/animation/tracks/NumberKeyframeTrack.js b/libs/three/src/animation/tracks/NumberKeyframeTrack.js
new file mode 100644
index 000000000..58b74ca54
--- /dev/null
+++ b/libs/three/src/animation/tracks/NumberKeyframeTrack.js
@@ -0,0 +1,12 @@
+import { KeyframeTrack } from '../KeyframeTrack.js';
+
+/**
+ * A Track of numeric keyframe values.
+ */
+class NumberKeyframeTrack extends KeyframeTrack {}
+
+NumberKeyframeTrack.prototype.ValueTypeName = 'number';
+// ValueBufferType is inherited
+// DefaultInterpolation is inherited
+
+export { NumberKeyframeTrack };
diff --git a/libs/three/src/animation/tracks/QuaternionKeyframeTrack.js b/libs/three/src/animation/tracks/QuaternionKeyframeTrack.js
new file mode 100644
index 000000000..d070e4e0f
--- /dev/null
+++ b/libs/three/src/animation/tracks/QuaternionKeyframeTrack.js
@@ -0,0 +1,23 @@
+import { InterpolateLinear } from '../../constants.js';
+import { KeyframeTrack } from '../KeyframeTrack.js';
+import { QuaternionLinearInterpolant } from '../../math/interpolants/QuaternionLinearInterpolant.js';
+
+/**
+ * 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;
+
+export { QuaternionKeyframeTrack };
diff --git a/libs/three/src/animation/tracks/StringKeyframeTrack.js b/libs/three/src/animation/tracks/StringKeyframeTrack.js
new file mode 100644
index 000000000..e7e615703
--- /dev/null
+++ b/libs/three/src/animation/tracks/StringKeyframeTrack.js
@@ -0,0 +1,15 @@
+import { InterpolateDiscrete } from '../../constants.js';
+import { KeyframeTrack } from '../KeyframeTrack.js';
+
+/**
+ * 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;
+
+export { StringKeyframeTrack };
diff --git a/libs/three/src/animation/tracks/VectorKeyframeTrack.js b/libs/three/src/animation/tracks/VectorKeyframeTrack.js
new file mode 100644
index 000000000..bdccf8b22
--- /dev/null
+++ b/libs/three/src/animation/tracks/VectorKeyframeTrack.js
@@ -0,0 +1,12 @@
+import { KeyframeTrack } from '../KeyframeTrack.js';
+
+/**
+ * A Track of vectored keyframe values.
+ */
+class VectorKeyframeTrack extends KeyframeTrack {}
+
+VectorKeyframeTrack.prototype.ValueTypeName = 'vector';
+// ValueBufferType is inherited
+// DefaultInterpolation is inherited
+
+export { VectorKeyframeTrack };
diff --git a/libs/three/src/audio/Audio.js b/libs/three/src/audio/Audio.js
new file mode 100644
index 000000000..9609d21ea
--- /dev/null
+++ b/libs/three/src/audio/Audio.js
@@ -0,0 +1,402 @@
+import { Object3D } from '../core/Object3D.js';
+
+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;
+
+ if ( this.source !== null ) {
+
+ 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._connected === false ) {
+
+ return;
+
+ }
+
+ 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;
+
+ }
+
+}
+
+export { Audio };
diff --git a/libs/three/src/audio/AudioAnalyser.js b/libs/three/src/audio/AudioAnalyser.js
new file mode 100644
index 000000000..bb34c791f
--- /dev/null
+++ b/libs/three/src/audio/AudioAnalyser.js
@@ -0,0 +1,40 @@
+class AudioAnalyser {
+
+ constructor( audio, fftSize = 2048 ) {
+
+ this.analyser = audio.context.createAnalyser();
+ this.analyser.fftSize = fftSize;
+
+ this.data = new Uint8Array( this.analyser.frequencyBinCount );
+
+ audio.getOutput().connect( this.analyser );
+
+ }
+
+
+ getFrequencyData() {
+
+ this.analyser.getByteFrequencyData( this.data );
+
+ return this.data;
+
+ }
+
+ getAverageFrequency() {
+
+ let value = 0;
+ const data = this.getFrequencyData();
+
+ for ( let i = 0; i < data.length; i ++ ) {
+
+ value += data[ i ];
+
+ }
+
+ return value / data.length;
+
+ }
+
+}
+
+export { AudioAnalyser };
diff --git a/libs/three/src/audio/AudioContext.js b/libs/three/src/audio/AudioContext.js
new file mode 100644
index 000000000..a7cd91132
--- /dev/null
+++ b/libs/three/src/audio/AudioContext.js
@@ -0,0 +1,25 @@
+let _context;
+
+class AudioContext {
+
+ static getContext() {
+
+ if ( _context === undefined ) {
+
+ _context = new ( window.AudioContext || window.webkitAudioContext )();
+
+ }
+
+ return _context;
+
+ }
+
+ static setContext( value ) {
+
+ _context = value;
+
+ }
+
+}
+
+export { AudioContext };
diff --git a/libs/three/src/audio/AudioListener.js b/libs/three/src/audio/AudioListener.js
new file mode 100644
index 000000000..330b6b2de
--- /dev/null
+++ b/libs/three/src/audio/AudioListener.js
@@ -0,0 +1,137 @@
+import { Vector3 } from '../math/Vector3.js';
+import { Quaternion } from '../math/Quaternion.js';
+import { Clock } from '../core/Clock.js';
+import { Object3D } from '../core/Object3D.js';
+import { AudioContext } from './AudioContext.js';
+
+const _position = /*@__PURE__*/ new Vector3();
+const _quaternion = /*@__PURE__*/ new Quaternion();
+const _scale = /*@__PURE__*/ new Vector3();
+const _orientation = /*@__PURE__*/ new Vector3();
+
+class AudioListener extends Object3D {
+
+ constructor() {
+
+ super();
+
+ this.type = 'AudioListener';
+
+ this.context = AudioContext.getContext();
+
+ this.gain = this.context.createGain();
+ this.gain.connect( this.context.destination );
+
+ this.filter = null;
+
+ this.timeDelta = 0;
+
+ // private
+
+ this._clock = new Clock();
+
+ }
+
+ getInput() {
+
+ return this.gain;
+
+ }
+
+ removeFilter() {
+
+ if ( this.filter !== null ) {
+
+ this.gain.disconnect( this.filter );
+ this.filter.disconnect( this.context.destination );
+ this.gain.connect( this.context.destination );
+ this.filter = null;
+
+ }
+
+ return this;
+
+ }
+
+ getFilter() {
+
+ return this.filter;
+
+ }
+
+ setFilter( value ) {
+
+ if ( this.filter !== null ) {
+
+ this.gain.disconnect( this.filter );
+ this.filter.disconnect( this.context.destination );
+
+ } else {
+
+ this.gain.disconnect( this.context.destination );
+
+ }
+
+ this.filter = value;
+ this.gain.connect( this.filter );
+ this.filter.connect( this.context.destination );
+
+ return this;
+
+ }
+
+ getMasterVolume() {
+
+ return this.gain.gain.value;
+
+ }
+
+ setMasterVolume( value ) {
+
+ this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 );
+
+ return this;
+
+ }
+
+ updateMatrixWorld( force ) {
+
+ super.updateMatrixWorld( force );
+
+ const listener = this.context.listener;
+ const up = this.up;
+
+ this.timeDelta = this._clock.getDelta();
+
+ this.matrixWorld.decompose( _position, _quaternion, _scale );
+
+ _orientation.set( 0, 0, - 1 ).applyQuaternion( _quaternion );
+
+ if ( listener.positionX ) {
+
+ // code path for Chrome (see #14393)
+
+ const endTime = this.context.currentTime + this.timeDelta;
+
+ listener.positionX.linearRampToValueAtTime( _position.x, endTime );
+ listener.positionY.linearRampToValueAtTime( _position.y, endTime );
+ listener.positionZ.linearRampToValueAtTime( _position.z, endTime );
+ listener.forwardX.linearRampToValueAtTime( _orientation.x, endTime );
+ listener.forwardY.linearRampToValueAtTime( _orientation.y, endTime );
+ listener.forwardZ.linearRampToValueAtTime( _orientation.z, endTime );
+ listener.upX.linearRampToValueAtTime( up.x, endTime );
+ listener.upY.linearRampToValueAtTime( up.y, endTime );
+ listener.upZ.linearRampToValueAtTime( up.z, endTime );
+
+ } else {
+
+ listener.setPosition( _position.x, _position.y, _position.z );
+ listener.setOrientation( _orientation.x, _orientation.y, _orientation.z, up.x, up.y, up.z );
+
+ }
+
+ }
+
+}
+
+export { AudioListener };
diff --git a/libs/three/src/audio/PositionalAudio.js b/libs/three/src/audio/PositionalAudio.js
new file mode 100644
index 000000000..f60bcb383
--- /dev/null
+++ b/libs/three/src/audio/PositionalAudio.js
@@ -0,0 +1,146 @@
+import { Vector3 } from '../math/Vector3.js';
+import { Quaternion } from '../math/Quaternion.js';
+import { Audio } from './Audio.js';
+
+const _position = /*@__PURE__*/ new Vector3();
+const _quaternion = /*@__PURE__*/ new Quaternion();
+const _scale = /*@__PURE__*/ new Vector3();
+const _orientation = /*@__PURE__*/ new Vector3();
+
+class PositionalAudio extends Audio {
+
+ constructor( listener ) {
+
+ super( listener );
+
+ this.panner = this.context.createPanner();
+ this.panner.panningModel = 'HRTF';
+ this.panner.connect( this.gain );
+
+ }
+
+ connect() {
+
+ super.connect();
+
+ this.panner.connect( this.gain );
+
+ }
+
+ disconnect() {
+
+ super.disconnect();
+
+ this.panner.disconnect( this.gain );
+
+ }
+
+ getOutput() {
+
+ return this.panner;
+
+ }
+
+ getRefDistance() {
+
+ return this.panner.refDistance;
+
+ }
+
+ setRefDistance( value ) {
+
+ this.panner.refDistance = value;
+
+ return this;
+
+ }
+
+ getRolloffFactor() {
+
+ return this.panner.rolloffFactor;
+
+ }
+
+ setRolloffFactor( value ) {
+
+ this.panner.rolloffFactor = value;
+
+ return this;
+
+ }
+
+ getDistanceModel() {
+
+ return this.panner.distanceModel;
+
+ }
+
+ setDistanceModel( value ) {
+
+ this.panner.distanceModel = value;
+
+ return this;
+
+ }
+
+ getMaxDistance() {
+
+ return this.panner.maxDistance;
+
+ }
+
+ setMaxDistance( value ) {
+
+ this.panner.maxDistance = value;
+
+ return this;
+
+ }
+
+ setDirectionalCone( coneInnerAngle, coneOuterAngle, coneOuterGain ) {
+
+ this.panner.coneInnerAngle = coneInnerAngle;
+ this.panner.coneOuterAngle = coneOuterAngle;
+ this.panner.coneOuterGain = coneOuterGain;
+
+ return this;
+
+ }
+
+ updateMatrixWorld( force ) {
+
+ super.updateMatrixWorld( force );
+
+ if ( this.hasPlaybackControl === true && this.isPlaying === false ) return;
+
+ this.matrixWorld.decompose( _position, _quaternion, _scale );
+
+ _orientation.set( 0, 0, 1 ).applyQuaternion( _quaternion );
+
+ const panner = this.panner;
+
+ if ( panner.positionX ) {
+
+ // code path for Chrome and Firefox (see #14393)
+
+ const endTime = this.context.currentTime + this.listener.timeDelta;
+
+ panner.positionX.linearRampToValueAtTime( _position.x, endTime );
+ panner.positionY.linearRampToValueAtTime( _position.y, endTime );
+ panner.positionZ.linearRampToValueAtTime( _position.z, endTime );
+ panner.orientationX.linearRampToValueAtTime( _orientation.x, endTime );
+ panner.orientationY.linearRampToValueAtTime( _orientation.y, endTime );
+ panner.orientationZ.linearRampToValueAtTime( _orientation.z, endTime );
+
+ } else {
+
+ panner.setPosition( _position.x, _position.y, _position.z );
+ panner.setOrientation( _orientation.x, _orientation.y, _orientation.z );
+
+ }
+
+ }
+
+}
+
+export { PositionalAudio };
diff --git a/libs/three/src/cameras/ArrayCamera.js b/libs/three/src/cameras/ArrayCamera.js
new file mode 100644
index 000000000..4da2d1d4a
--- /dev/null
+++ b/libs/three/src/cameras/ArrayCamera.js
@@ -0,0 +1,17 @@
+import { PerspectiveCamera } from './PerspectiveCamera.js';
+
+class ArrayCamera extends PerspectiveCamera {
+
+ constructor( array = [] ) {
+
+ super();
+
+ this.isArrayCamera = true;
+
+ this.cameras = array;
+
+ }
+
+}
+
+export { ArrayCamera };
diff --git a/libs/three/src/cameras/Camera.js b/libs/three/src/cameras/Camera.js
new file mode 100644
index 000000000..3f68b3131
--- /dev/null
+++ b/libs/three/src/cameras/Camera.js
@@ -0,0 +1,69 @@
+import { WebGLCoordinateSystem } from '../constants.js';
+import { Matrix4 } from '../math/Matrix4.js';
+import { Object3D } from '../core/Object3D.js';
+
+class Camera extends Object3D {
+
+ constructor() {
+
+ super();
+
+ this.isCamera = true;
+
+ this.type = 'Camera';
+
+ this.matrixWorldInverse = new Matrix4();
+
+ this.projectionMatrix = new Matrix4();
+ this.projectionMatrixInverse = new Matrix4();
+
+ this.coordinateSystem = WebGLCoordinateSystem;
+
+ }
+
+ copy( source, recursive ) {
+
+ super.copy( source, recursive );
+
+ this.matrixWorldInverse.copy( source.matrixWorldInverse );
+
+ this.projectionMatrix.copy( source.projectionMatrix );
+ this.projectionMatrixInverse.copy( source.projectionMatrixInverse );
+
+ this.coordinateSystem = source.coordinateSystem;
+
+ return this;
+
+ }
+
+ getWorldDirection( target ) {
+
+ return super.getWorldDirection( target ).negate();
+
+ }
+
+ 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 );
+
+ }
+
+}
+
+export { Camera };
diff --git a/libs/three/src/cameras/CubeCamera.js b/libs/three/src/cameras/CubeCamera.js
new file mode 100644
index 000000000..66ba89760
--- /dev/null
+++ b/libs/three/src/cameras/CubeCamera.js
@@ -0,0 +1,173 @@
+import { WebGLCoordinateSystem, WebGPUCoordinateSystem } from '../constants.js';
+import { Object3D } from '../core/Object3D.js';
+import { PerspectiveCamera } from './PerspectiveCamera.js';
+
+const fov = - 90; // negative fov is not an error
+const aspect = 1;
+
+class CubeCamera extends Object3D {
+
+ constructor( near, far, renderTarget ) {
+
+ super();
+
+ this.type = 'CubeCamera';
+
+ this.renderTarget = renderTarget;
+ this.coordinateSystem = null;
+ this.activeMipmapLevel = 0;
+
+ const cameraPX = new PerspectiveCamera( fov, aspect, near, far );
+ cameraPX.layers = this.layers;
+ this.add( cameraPX );
+
+ const cameraNX = new PerspectiveCamera( fov, aspect, near, far );
+ cameraNX.layers = this.layers;
+ this.add( cameraNX );
+
+ const cameraPY = new PerspectiveCamera( fov, aspect, near, far );
+ cameraPY.layers = this.layers;
+ this.add( cameraPY );
+
+ const cameraNY = new PerspectiveCamera( fov, aspect, near, far );
+ cameraNY.layers = this.layers;
+ this.add( cameraNY );
+
+ const cameraPZ = new PerspectiveCamera( fov, aspect, near, far );
+ cameraPZ.layers = this.layers;
+ this.add( cameraPZ );
+
+ const cameraNZ = new PerspectiveCamera( fov, aspect, near, far );
+ cameraNZ.layers = this.layers;
+ this.add( cameraNZ );
+
+ }
+
+ updateCoordinateSystem() {
+
+ const coordinateSystem = this.coordinateSystem;
+
+ const cameras = this.children.concat();
+
+ const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = cameras;
+
+ for ( const camera of cameras ) this.remove( camera );
+
+ if ( coordinateSystem === WebGLCoordinateSystem ) {
+
+ cameraPX.up.set( 0, 1, 0 );
+ cameraPX.lookAt( 1, 0, 0 );
+
+ cameraNX.up.set( 0, 1, 0 );
+ cameraNX.lookAt( - 1, 0, 0 );
+
+ cameraPY.up.set( 0, 0, - 1 );
+ cameraPY.lookAt( 0, 1, 0 );
+
+ cameraNY.up.set( 0, 0, 1 );
+ cameraNY.lookAt( 0, - 1, 0 );
+
+ cameraPZ.up.set( 0, 1, 0 );
+ cameraPZ.lookAt( 0, 0, 1 );
+
+ cameraNZ.up.set( 0, 1, 0 );
+ cameraNZ.lookAt( 0, 0, - 1 );
+
+ } else if ( coordinateSystem === WebGPUCoordinateSystem ) {
+
+ cameraPX.up.set( 0, - 1, 0 );
+ cameraPX.lookAt( - 1, 0, 0 );
+
+ cameraNX.up.set( 0, - 1, 0 );
+ cameraNX.lookAt( 1, 0, 0 );
+
+ cameraPY.up.set( 0, 0, 1 );
+ cameraPY.lookAt( 0, 1, 0 );
+
+ cameraNY.up.set( 0, 0, - 1 );
+ cameraNY.lookAt( 0, - 1, 0 );
+
+ cameraPZ.up.set( 0, - 1, 0 );
+ cameraPZ.lookAt( 0, 0, 1 );
+
+ cameraNZ.up.set( 0, - 1, 0 );
+ cameraNZ.lookAt( 0, 0, - 1 );
+
+ } else {
+
+ throw new Error( 'THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: ' + coordinateSystem );
+
+ }
+
+ for ( const camera of cameras ) {
+
+ this.add( camera );
+
+ camera.updateMatrixWorld();
+
+ }
+
+ }
+
+ update( renderer, scene ) {
+
+ if ( this.parent === null ) this.updateMatrixWorld();
+
+ const { renderTarget, activeMipmapLevel } = this;
+
+ if ( this.coordinateSystem !== renderer.coordinateSystem ) {
+
+ this.coordinateSystem = renderer.coordinateSystem;
+
+ this.updateCoordinateSystem();
+
+ }
+
+ const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children;
+
+ const currentRenderTarget = renderer.getRenderTarget();
+ const currentActiveCubeFace = renderer.getActiveCubeFace();
+ const currentActiveMipmapLevel = renderer.getActiveMipmapLevel();
+
+ const currentXrEnabled = renderer.xr.enabled;
+
+ renderer.xr.enabled = false;
+
+ const generateMipmaps = renderTarget.texture.generateMipmaps;
+
+ renderTarget.texture.generateMipmaps = false;
+
+ renderer.setRenderTarget( renderTarget, 0, activeMipmapLevel );
+ renderer.render( scene, cameraPX );
+
+ renderer.setRenderTarget( renderTarget, 1, activeMipmapLevel );
+ renderer.render( scene, cameraNX );
+
+ renderer.setRenderTarget( renderTarget, 2, activeMipmapLevel );
+ renderer.render( scene, cameraPY );
+
+ renderer.setRenderTarget( renderTarget, 3, activeMipmapLevel );
+ renderer.render( scene, cameraNY );
+
+ renderer.setRenderTarget( renderTarget, 4, activeMipmapLevel );
+ renderer.render( scene, cameraPZ );
+
+ // mipmaps are generated during the last call of render()
+ // at this point, all sides of the cube render target are defined
+
+ renderTarget.texture.generateMipmaps = generateMipmaps;
+
+ renderer.setRenderTarget( renderTarget, 5, activeMipmapLevel );
+ renderer.render( scene, cameraNZ );
+
+ renderer.setRenderTarget( currentRenderTarget, currentActiveCubeFace, currentActiveMipmapLevel );
+
+ renderer.xr.enabled = currentXrEnabled;
+
+ renderTarget.texture.needsPMREMUpdate = true;
+
+ }
+
+}
+
+export { CubeCamera };
diff --git a/libs/three/src/cameras/OrthographicCamera.js b/libs/three/src/cameras/OrthographicCamera.js
new file mode 100755
index 000000000..87f9691e4
--- /dev/null
+++ b/libs/three/src/cameras/OrthographicCamera.js
@@ -0,0 +1,136 @@
+import { Camera } from './Camera.js';
+
+class OrthographicCamera extends Camera {
+
+ constructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) {
+
+ super();
+
+ this.isOrthographicCamera = true;
+
+ 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.coordinateSystem );
+
+ 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;
+
+ }
+
+}
+
+export { OrthographicCamera };
diff --git a/libs/three/src/cameras/PerspectiveCamera.js b/libs/three/src/cameras/PerspectiveCamera.js
new file mode 100644
index 000000000..e6450d2c3
--- /dev/null
+++ b/libs/three/src/cameras/PerspectiveCamera.js
@@ -0,0 +1,233 @@
+import { Camera } from './Camera.js';
+import * as MathUtils from '../math/MathUtils.js';
+
+class PerspectiveCamera extends Camera {
+
+ constructor( fov = 50, aspect = 1, near = 0.1, far = 2000 ) {
+
+ super();
+
+ this.isPerspectiveCamera = true;
+
+ 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 = MathUtils.RAD2DEG * 2 * Math.atan( vExtentSlope );
+ this.updateProjectionMatrix();
+
+ }
+
+ /**
+ * Calculates the focal length from the current .fov and .filmGauge.
+ */
+ getFocalLength() {
+
+ const vExtentSlope = Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov );
+
+ return 0.5 * this.getFilmHeight() / vExtentSlope;
+
+ }
+
+ getEffectiveFOV() {
+
+ return MathUtils.RAD2DEG * 2 * Math.atan(
+ Math.tan( MathUtils.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( 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.coordinateSystem );
+
+ 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;
+
+ }
+
+}
+
+export { PerspectiveCamera };
diff --git a/libs/three/src/cameras/StereoCamera.js b/libs/three/src/cameras/StereoCamera.js
new file mode 100644
index 000000000..ae8f8485e
--- /dev/null
+++ b/libs/three/src/cameras/StereoCamera.js
@@ -0,0 +1,100 @@
+import { Matrix4 } from '../math/Matrix4.js';
+import * as MathUtils from '../math/MathUtils.js';
+import { PerspectiveCamera } from './PerspectiveCamera.js';
+
+const _eyeRight = /*@__PURE__*/ new Matrix4();
+const _eyeLeft = /*@__PURE__*/ new Matrix4();
+const _projectionMatrix = /*@__PURE__*/ new Matrix4();
+
+class StereoCamera {
+
+ constructor() {
+
+ 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
+ };
+
+ }
+
+ update( 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/
+
+ _projectionMatrix.copy( camera.projectionMatrix );
+ 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 );
+
+ }
+
+}
+
+export { StereoCamera };
diff --git a/libs/three/src/constants.js b/libs/three/src/constants.js
new file mode 100644
index 000000000..9a9f7a6f9
--- /dev/null
+++ b/libs/three/src/constants.js
@@ -0,0 +1,217 @@
+export const REVISION = '160';
+
+export const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 };
+export const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 };
+export const CullFaceNone = 0;
+export const CullFaceBack = 1;
+export const CullFaceFront = 2;
+export const CullFaceFrontBack = 3;
+export const BasicShadowMap = 0;
+export const PCFShadowMap = 1;
+export const PCFSoftShadowMap = 2;
+export const VSMShadowMap = 3;
+export const FrontSide = 0;
+export const BackSide = 1;
+export const DoubleSide = 2;
+export const TwoPassDoubleSide = 2; // r149
+export const NoBlending = 0;
+export const NormalBlending = 1;
+export const AdditiveBlending = 2;
+export const SubtractiveBlending = 3;
+export const MultiplyBlending = 4;
+export const CustomBlending = 5;
+export const AddEquation = 100;
+export const SubtractEquation = 101;
+export const ReverseSubtractEquation = 102;
+export const MinEquation = 103;
+export const MaxEquation = 104;
+export const ZeroFactor = 200;
+export const OneFactor = 201;
+export const SrcColorFactor = 202;
+export const OneMinusSrcColorFactor = 203;
+export const SrcAlphaFactor = 204;
+export const OneMinusSrcAlphaFactor = 205;
+export const DstAlphaFactor = 206;
+export const OneMinusDstAlphaFactor = 207;
+export const DstColorFactor = 208;
+export const OneMinusDstColorFactor = 209;
+export const SrcAlphaSaturateFactor = 210;
+export const ConstantColorFactor = 211;
+export const OneMinusConstantColorFactor = 212;
+export const ConstantAlphaFactor = 213;
+export const OneMinusConstantAlphaFactor = 214;
+export const NeverDepth = 0;
+export const AlwaysDepth = 1;
+export const LessDepth = 2;
+export const LessEqualDepth = 3;
+export const EqualDepth = 4;
+export const GreaterEqualDepth = 5;
+export const GreaterDepth = 6;
+export const NotEqualDepth = 7;
+export const MultiplyOperation = 0;
+export const MixOperation = 1;
+export const AddOperation = 2;
+export const NoToneMapping = 0;
+export const LinearToneMapping = 1;
+export const ReinhardToneMapping = 2;
+export const CineonToneMapping = 3;
+export const ACESFilmicToneMapping = 4;
+export const CustomToneMapping = 5;
+export const AgXToneMapping = 6;
+export const AttachedBindMode = 'attached';
+export const DetachedBindMode = 'detached';
+
+export const UVMapping = 300;
+export const CubeReflectionMapping = 301;
+export const CubeRefractionMapping = 302;
+export const EquirectangularReflectionMapping = 303;
+export const EquirectangularRefractionMapping = 304;
+export const CubeUVReflectionMapping = 306;
+export const RepeatWrapping = 1000;
+export const ClampToEdgeWrapping = 1001;
+export const MirroredRepeatWrapping = 1002;
+export const NearestFilter = 1003;
+export const NearestMipmapNearestFilter = 1004;
+export const NearestMipMapNearestFilter = 1004;
+export const NearestMipmapLinearFilter = 1005;
+export const NearestMipMapLinearFilter = 1005;
+export const LinearFilter = 1006;
+export const LinearMipmapNearestFilter = 1007;
+export const LinearMipMapNearestFilter = 1007;
+export const LinearMipmapLinearFilter = 1008;
+export const LinearMipMapLinearFilter = 1008;
+export const UnsignedByteType = 1009;
+export const ByteType = 1010;
+export const ShortType = 1011;
+export const UnsignedShortType = 1012;
+export const IntType = 1013;
+export const UnsignedIntType = 1014;
+export const FloatType = 1015;
+export const HalfFloatType = 1016;
+export const UnsignedShort4444Type = 1017;
+export const UnsignedShort5551Type = 1018;
+export const UnsignedInt248Type = 1020;
+export const AlphaFormat = 1021;
+export const RGBAFormat = 1023;
+export const LuminanceFormat = 1024;
+export const LuminanceAlphaFormat = 1025;
+export const DepthFormat = 1026;
+export const DepthStencilFormat = 1027;
+export const RedFormat = 1028;
+export const RedIntegerFormat = 1029;
+export const RGFormat = 1030;
+export const RGIntegerFormat = 1031;
+export const RGBAIntegerFormat = 1033;
+
+export const RGB_S3TC_DXT1_Format = 33776;
+export const RGBA_S3TC_DXT1_Format = 33777;
+export const RGBA_S3TC_DXT3_Format = 33778;
+export const RGBA_S3TC_DXT5_Format = 33779;
+export const RGB_PVRTC_4BPPV1_Format = 35840;
+export const RGB_PVRTC_2BPPV1_Format = 35841;
+export const RGBA_PVRTC_4BPPV1_Format = 35842;
+export const RGBA_PVRTC_2BPPV1_Format = 35843;
+export const RGB_ETC1_Format = 36196;
+export const RGB_ETC2_Format = 37492;
+export const RGBA_ETC2_EAC_Format = 37496;
+export const RGBA_ASTC_4x4_Format = 37808;
+export const RGBA_ASTC_5x4_Format = 37809;
+export const RGBA_ASTC_5x5_Format = 37810;
+export const RGBA_ASTC_6x5_Format = 37811;
+export const RGBA_ASTC_6x6_Format = 37812;
+export const RGBA_ASTC_8x5_Format = 37813;
+export const RGBA_ASTC_8x6_Format = 37814;
+export const RGBA_ASTC_8x8_Format = 37815;
+export const RGBA_ASTC_10x5_Format = 37816;
+export const RGBA_ASTC_10x6_Format = 37817;
+export const RGBA_ASTC_10x8_Format = 37818;
+export const RGBA_ASTC_10x10_Format = 37819;
+export const RGBA_ASTC_12x10_Format = 37820;
+export const RGBA_ASTC_12x12_Format = 37821;
+export const RGBA_BPTC_Format = 36492;
+export const RGB_BPTC_SIGNED_Format = 36494;
+export const RGB_BPTC_UNSIGNED_Format = 36495;
+export const RED_RGTC1_Format = 36283;
+export const SIGNED_RED_RGTC1_Format = 36284;
+export const RED_GREEN_RGTC2_Format = 36285;
+export const SIGNED_RED_GREEN_RGTC2_Format = 36286;
+export const LoopOnce = 2200;
+export const LoopRepeat = 2201;
+export const LoopPingPong = 2202;
+export const InterpolateDiscrete = 2300;
+export const InterpolateLinear = 2301;
+export const InterpolateSmooth = 2302;
+export const ZeroCurvatureEnding = 2400;
+export const ZeroSlopeEnding = 2401;
+export const WrapAroundEnding = 2402;
+export const NormalAnimationBlendMode = 2500;
+export const AdditiveAnimationBlendMode = 2501;
+export const TrianglesDrawMode = 0;
+export const TriangleStripDrawMode = 1;
+export const TriangleFanDrawMode = 2;
+/** @deprecated Use LinearSRGBColorSpace or NoColorSpace in three.js r152+. */
+export const LinearEncoding = 3000;
+/** @deprecated Use SRGBColorSpace in three.js r152+. */
+export const sRGBEncoding = 3001;
+export const BasicDepthPacking = 3200;
+export const RGBADepthPacking = 3201;
+export const TangentSpaceNormalMap = 0;
+export const ObjectSpaceNormalMap = 1;
+
+// Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available.
+export const NoColorSpace = '';
+export const SRGBColorSpace = 'srgb';
+export const LinearSRGBColorSpace = 'srgb-linear';
+export const DisplayP3ColorSpace = 'display-p3';
+export const LinearDisplayP3ColorSpace = 'display-p3-linear';
+
+export const LinearTransfer = 'linear';
+export const SRGBTransfer = 'srgb';
+
+export const Rec709Primaries = 'rec709';
+export const P3Primaries = 'p3';
+
+export const ZeroStencilOp = 0;
+export const KeepStencilOp = 7680;
+export const ReplaceStencilOp = 7681;
+export const IncrementStencilOp = 7682;
+export const DecrementStencilOp = 7683;
+export const IncrementWrapStencilOp = 34055;
+export const DecrementWrapStencilOp = 34056;
+export const InvertStencilOp = 5386;
+
+export const NeverStencilFunc = 512;
+export const LessStencilFunc = 513;
+export const EqualStencilFunc = 514;
+export const LessEqualStencilFunc = 515;
+export const GreaterStencilFunc = 516;
+export const NotEqualStencilFunc = 517;
+export const GreaterEqualStencilFunc = 518;
+export const AlwaysStencilFunc = 519;
+
+export const NeverCompare = 512;
+export const LessCompare = 513;
+export const EqualCompare = 514;
+export const LessEqualCompare = 515;
+export const GreaterCompare = 516;
+export const NotEqualCompare = 517;
+export const GreaterEqualCompare = 518;
+export const AlwaysCompare = 519;
+
+export const StaticDrawUsage = 35044;
+export const DynamicDrawUsage = 35048;
+export const StreamDrawUsage = 35040;
+export const StaticReadUsage = 35045;
+export const DynamicReadUsage = 35049;
+export const StreamReadUsage = 35041;
+export const StaticCopyUsage = 35046;
+export const DynamicCopyUsage = 35050;
+export const StreamCopyUsage = 35042;
+
+export const GLSL1 = '100';
+export const GLSL3 = '300 es';
+
+export const _SRGBAFormat = 1035; // fallback for WebGL 1
+
+export const WebGLCoordinateSystem = 2000;
+export const WebGPUCoordinateSystem = 2001;
diff --git a/libs/three/src/core/BufferAttribute.js b/libs/three/src/core/BufferAttribute.js
new file mode 100644
index 000000000..a6b91bda7
--- /dev/null
+++ b/libs/three/src/core/BufferAttribute.js
@@ -0,0 +1,649 @@
+import { Vector3 } from '../math/Vector3.js';
+import { Vector2 } from '../math/Vector2.js';
+import { denormalize, normalize } from '../math/MathUtils.js';
+import { StaticDrawUsage, FloatType } from '../constants.js';
+import { fromHalfFloat, toHalfFloat } from '../extras/DataUtils.js';
+
+const _vector = /*@__PURE__*/ new Vector3();
+const _vector2 = /*@__PURE__*/ new Vector2();
+
+class BufferAttribute {
+
+ constructor( array, itemSize, normalized = false ) {
+
+ if ( Array.isArray( array ) ) {
+
+ throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );
+
+ }
+
+ this.isBufferAttribute = true;
+
+ this.name = '';
+
+ this.array = array;
+ this.itemSize = itemSize;
+ this.count = array !== undefined ? array.length / itemSize : 0;
+ this.normalized = normalized;
+
+ this.usage = StaticDrawUsage;
+ this._updateRange = { offset: 0, count: - 1 };
+ this.updateRanges = [];
+ this.gpuType = FloatType;
+
+ this.version = 0;
+
+ }
+
+ onUploadCallback() {}
+
+ set needsUpdate( value ) {
+
+ if ( value === true ) this.version ++;
+
+ }
+
+ get updateRange() {
+
+ console.warn( 'THREE.BufferAttribute: updateRange() is deprecated and will be removed in r169. Use addUpdateRange() instead.' ); // @deprecated, r159
+ return this._updateRange;
+
+ }
+
+ setUsage( value ) {
+
+ this.usage = value;
+
+ return this;
+
+ }
+
+ addUpdateRange( start, count ) {
+
+ this.updateRanges.push( { start, count } );
+
+ }
+
+ clearUpdateRanges() {
+
+ this.updateRanges.length = 0;
+
+ }
+
+ 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;
+ this.gpuType = source.gpuType;
+
+ 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;
+
+ }
+
+ applyMatrix3( m ) {
+
+ if ( this.itemSize === 2 ) {
+
+ for ( let i = 0, l = this.count; i < l; i ++ ) {
+
+ _vector2.fromBufferAttribute( this, i );
+ _vector2.applyMatrix3( m );
+
+ this.setXY( i, _vector2.x, _vector2.y );
+
+ }
+
+ } else if ( this.itemSize === 3 ) {
+
+ for ( let i = 0, l = this.count; i < l; i ++ ) {
+
+ _vector.fromBufferAttribute( this, i );
+ _vector.applyMatrix3( m );
+
+ this.setXYZ( i, _vector.x, _vector.y, _vector.z );
+
+ }
+
+ }
+
+ return this;
+
+ }
+
+ applyMatrix4( m ) {
+
+ for ( let i = 0, l = this.count; i < l; i ++ ) {
+
+ _vector.fromBufferAttribute( this, i );
+
+ _vector.applyMatrix4( m );
+
+ this.setXYZ( i, _vector.x, _vector.y, _vector.z );
+
+ }
+
+ return this;
+
+ }
+
+ applyNormalMatrix( m ) {
+
+ for ( let i = 0, l = this.count; i < l; i ++ ) {
+
+ _vector.fromBufferAttribute( this, i );
+
+ _vector.applyNormalMatrix( m );
+
+ this.setXYZ( i, _vector.x, _vector.y, _vector.z );
+
+ }
+
+ return this;
+
+ }
+
+ transformDirection( m ) {
+
+ for ( let i = 0, l = this.count; i < l; i ++ ) {
+
+ _vector.fromBufferAttribute( this, i );
+
+ _vector.transformDirection( m );
+
+ this.setXYZ( i, _vector.x, _vector.y, _vector.z );
+
+ }
+
+ return this;
+
+ }
+
+ set( value, offset = 0 ) {
+
+ // Matching BufferAttribute constructor, do not normalize the array.
+ this.array.set( value, offset );
+
+ return this;
+
+ }
+
+ getComponent( index, component ) {
+
+ let value = this.array[ index * this.itemSize + component ];
+
+ if ( this.normalized ) value = denormalize( value, this.array );
+
+ return value;
+
+ }
+
+ setComponent( index, component, value ) {
+
+ if ( this.normalized ) value = normalize( value, this.array );
+
+ this.array[ index * this.itemSize + component ] = value;
+
+ return this;
+
+ }
+
+ getX( index ) {
+
+ let x = this.array[ index * this.itemSize ];
+
+ if ( this.normalized ) x = denormalize( x, this.array );
+
+ return x;
+
+ }
+
+ setX( index, x ) {
+
+ if ( this.normalized ) x = normalize( x, this.array );
+
+ this.array[ index * this.itemSize ] = x;
+
+ return this;
+
+ }
+
+ getY( index ) {
+
+ let y = this.array[ index * this.itemSize + 1 ];
+
+ if ( this.normalized ) y = denormalize( y, this.array );
+
+ return y;
+
+ }
+
+ setY( index, y ) {
+
+ if ( this.normalized ) y = normalize( y, this.array );
+
+ this.array[ index * this.itemSize + 1 ] = y;
+
+ return this;
+
+ }
+
+ getZ( index ) {
+
+ let z = this.array[ index * this.itemSize + 2 ];
+
+ if ( this.normalized ) z = denormalize( z, this.array );
+
+ return z;
+
+ }
+
+ setZ( index, z ) {
+
+ if ( this.normalized ) z = normalize( z, this.array );
+
+ this.array[ index * this.itemSize + 2 ] = z;
+
+ return this;
+
+ }
+
+ getW( index ) {
+
+ let w = this.array[ index * this.itemSize + 3 ];
+
+ if ( this.normalized ) w = denormalize( w, this.array );
+
+ return w;
+
+ }
+
+ setW( index, w ) {
+
+ if ( this.normalized ) w = normalize( w, this.array );
+
+ this.array[ index * this.itemSize + 3 ] = w;
+
+ return this;
+
+ }
+
+ setXY( index, x, y ) {
+
+ index *= this.itemSize;
+
+ if ( this.normalized ) {
+
+ x = normalize( x, this.array );
+ y = normalize( y, this.array );
+
+ }
+
+ this.array[ index + 0 ] = x;
+ this.array[ index + 1 ] = y;
+
+ return this;
+
+ }
+
+ setXYZ( index, x, y, z ) {
+
+ index *= this.itemSize;
+
+ if ( this.normalized ) {
+
+ x = normalize( x, this.array );
+ y = normalize( y, this.array );
+ z = normalize( z, this.array );
+
+ }
+
+ 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;
+
+ if ( this.normalized ) {
+
+ x = normalize( x, this.array );
+ y = normalize( y, this.array );
+ z = normalize( z, this.array );
+ w = normalize( w, this.array );
+
+ }
+
+ 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.from( this.array ),
+ normalized: this.normalized
+ };
+
+ if ( this.name !== '' ) data.name = this.name;
+ if ( this.usage !== StaticDrawUsage ) data.usage = this.usage;
+
+ return data;
+
+ }
+
+}
+
+//
+
+class Int8BufferAttribute extends BufferAttribute {
+
+ constructor( array, itemSize, normalized ) {
+
+ super( new Int8Array( array ), itemSize, normalized );
+
+ }
+
+}
+
+class Uint8BufferAttribute extends BufferAttribute {
+
+ constructor( array, itemSize, normalized ) {
+
+ super( new Uint8Array( array ), itemSize, normalized );
+
+ }
+
+}
+
+class Uint8ClampedBufferAttribute extends BufferAttribute {
+
+ constructor( array, itemSize, normalized ) {
+
+ super( new Uint8ClampedArray( array ), itemSize, normalized );
+
+ }
+
+}
+
+class Int16BufferAttribute extends BufferAttribute {
+
+ constructor( array, itemSize, normalized ) {
+
+ super( new Int16Array( array ), itemSize, normalized );
+
+ }
+
+}
+
+class Uint16BufferAttribute extends BufferAttribute {
+
+ constructor( array, itemSize, normalized ) {
+
+ super( new Uint16Array( array ), itemSize, normalized );
+
+ }
+
+}
+
+class Int32BufferAttribute extends BufferAttribute {
+
+ constructor( array, itemSize, normalized ) {
+
+ super( new Int32Array( 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 );
+
+ this.isFloat16BufferAttribute = true;
+
+ }
+
+ getX( index ) {
+
+ let x = fromHalfFloat( this.array[ index * this.itemSize ] );
+
+ if ( this.normalized ) x = denormalize( x, this.array );
+
+ return x;
+
+ }
+
+ setX( index, x ) {
+
+ if ( this.normalized ) x = normalize( x, this.array );
+
+ this.array[ index * this.itemSize ] = toHalfFloat( x );
+
+ return this;
+
+ }
+
+ getY( index ) {
+
+ let y = fromHalfFloat( this.array[ index * this.itemSize + 1 ] );
+
+ if ( this.normalized ) y = denormalize( y, this.array );
+
+ return y;
+
+ }
+
+ setY( index, y ) {
+
+ if ( this.normalized ) y = normalize( y, this.array );
+
+ this.array[ index * this.itemSize + 1 ] = toHalfFloat( y );
+
+ return this;
+
+ }
+
+ getZ( index ) {
+
+ let z = fromHalfFloat( this.array[ index * this.itemSize + 2 ] );
+
+ if ( this.normalized ) z = denormalize( z, this.array );
+
+ return z;
+
+ }
+
+ setZ( index, z ) {
+
+ if ( this.normalized ) z = normalize( z, this.array );
+
+ this.array[ index * this.itemSize + 2 ] = toHalfFloat( z );
+
+ return this;
+
+ }
+
+ getW( index ) {
+
+ let w = fromHalfFloat( this.array[ index * this.itemSize + 3 ] );
+
+ if ( this.normalized ) w = denormalize( w, this.array );
+
+ return w;
+
+ }
+
+ setW( index, w ) {
+
+ if ( this.normalized ) w = normalize( w, this.array );
+
+ this.array[ index * this.itemSize + 3 ] = toHalfFloat( w );
+
+ return this;
+
+ }
+
+ setXY( index, x, y ) {
+
+ index *= this.itemSize;
+
+ if ( this.normalized ) {
+
+ x = normalize( x, this.array );
+ y = normalize( y, this.array );
+
+ }
+
+ this.array[ index + 0 ] = toHalfFloat( x );
+ this.array[ index + 1 ] = toHalfFloat( y );
+
+ return this;
+
+ }
+
+ setXYZ( index, x, y, z ) {
+
+ index *= this.itemSize;
+
+ if ( this.normalized ) {
+
+ x = normalize( x, this.array );
+ y = normalize( y, this.array );
+ z = normalize( z, this.array );
+
+ }
+
+ this.array[ index + 0 ] = toHalfFloat( x );
+ this.array[ index + 1 ] = toHalfFloat( y );
+ this.array[ index + 2 ] = toHalfFloat( z );
+
+ return this;
+
+ }
+
+ setXYZW( index, x, y, z, w ) {
+
+ index *= this.itemSize;
+
+ if ( this.normalized ) {
+
+ x = normalize( x, this.array );
+ y = normalize( y, this.array );
+ z = normalize( z, this.array );
+ w = normalize( w, this.array );
+
+ }
+
+ this.array[ index + 0 ] = toHalfFloat( x );
+ this.array[ index + 1 ] = toHalfFloat( y );
+ this.array[ index + 2 ] = toHalfFloat( z );
+ this.array[ index + 3 ] = toHalfFloat( w );
+
+ return this;
+
+ }
+
+}
+
+
+class Float32BufferAttribute extends BufferAttribute {
+
+ constructor( array, itemSize, normalized ) {
+
+ super( new Float32Array( array ), itemSize, normalized );
+
+ }
+
+}
+
+class Float64BufferAttribute extends BufferAttribute {
+
+ constructor( array, itemSize, normalized ) {
+
+ super( new Float64Array( array ), itemSize, normalized );
+
+ }
+
+}
+
+//
+
+export {
+ Float64BufferAttribute,
+ Float32BufferAttribute,
+ Float16BufferAttribute,
+ Uint32BufferAttribute,
+ Int32BufferAttribute,
+ Uint16BufferAttribute,
+ Int16BufferAttribute,
+ Uint8ClampedBufferAttribute,
+ Uint8BufferAttribute,
+ Int8BufferAttribute,
+ BufferAttribute
+};
diff --git a/libs/three/src/core/BufferGeometry.js b/libs/three/src/core/BufferGeometry.js
new file mode 100644
index 000000000..93ad6fcc2
--- /dev/null
+++ b/libs/three/src/core/BufferGeometry.js
@@ -0,0 +1,1079 @@
+import { Vector3 } from '../math/Vector3.js';
+import { Vector2 } from '../math/Vector2.js';
+import { Box3 } from '../math/Box3.js';
+import { EventDispatcher } from './EventDispatcher.js';
+import { BufferAttribute, Float32BufferAttribute, Uint16BufferAttribute, Uint32BufferAttribute } from './BufferAttribute.js';
+import { Sphere } from '../math/Sphere.js';
+import { Object3D } from './Object3D.js';
+import { Matrix4 } from '../math/Matrix4.js';
+import { Matrix3 } from '../math/Matrix3.js';
+import * as MathUtils from '../math/MathUtils.js';
+import { arrayNeedsUint32 } from '../utils.js';
+
+let _id = 0;
+
+const _m1 = /*@__PURE__*/ new Matrix4();
+const _obj = /*@__PURE__*/ new Object3D();
+const _offset = /*@__PURE__*/ new Vector3();
+const _box = /*@__PURE__*/ new Box3();
+const _boxMorphTargets = /*@__PURE__*/ new Box3();
+const _vector = /*@__PURE__*/ new Vector3();
+
+class BufferGeometry extends EventDispatcher {
+
+ constructor() {
+
+ super();
+
+ this.isBufferGeometry = true;
+
+ Object.defineProperty( this, 'id', { value: _id ++ } );
+
+ 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 = {};
+
+ }
+
+ getIndex() {
+
+ return this.index;
+
+ }
+
+ setIndex( index ) {
+
+ if ( Array.isArray( index ) ) {
+
+ this.index = new ( arrayNeedsUint32( index ) ? 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.setFromBufferAttribute( morphAttribute );
+
+ if ( this.morphTargetsRelative ) {
+
+ _vector.addVectors( this.boundingBox.min, _box.min );
+ this.boundingBox.expandByPoint( _vector );
+
+ _vector.addVectors( this.boundingBox.max, _box.max );
+ this.boundingBox.expandByPoint( _vector );
+
+ } else {
+
+ this.boundingBox.expandByPoint( _box.min );
+ this.boundingBox.expandByPoint( _box.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.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.addVectors( _box.min, _boxMorphTargets.min );
+ _box.expandByPoint( _vector );
+
+ _vector.addVectors( _box.max, _boxMorphTargets.max );
+ _box.expandByPoint( _vector );
+
+ } else {
+
+ _box.expandByPoint( _boxMorphTargets.min );
+ _box.expandByPoint( _boxMorphTargets.max );
+
+ }
+
+ }
+
+ }
+
+ _box.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.fromBufferAttribute( position, i );
+
+ maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) );
+
+ }
+
+ // 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.fromBufferAttribute( morphAttribute, j );
+
+ if ( morphTargetsRelative ) {
+
+ _offset.fromBufferAttribute( position, j );
+ _vector.add( _offset );
+
+ }
+
+ maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) );
+
+ }
+
+ }
+
+ }
+
+ 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 ( this.hasAttribute( 'tangent' ) === false ) {
+
+ this.setAttribute( 'tangent', new BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) );
+
+ }
+
+ const tangents = this.getAttribute( '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;
+
+ }
+
+ }
+
+ normalizeNormals() {
+
+ const normals = this.attributes.normal;
+
+ for ( let i = 0, il = normals.count; i < il; i ++ ) {
+
+ _vector.fromBufferAttribute( normals, i );
+
+ _vector.normalize();
+
+ normals.setXYZ( i, _vector.x, _vector.y, _vector.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.6,
+ 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() {
+
+ return new this.constructor().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' } );
+
+ }
+
+}
+
+export { BufferGeometry };
diff --git a/libs/three/src/core/Clock.js b/libs/three/src/core/Clock.js
new file mode 100644
index 000000000..0bba330d0
--- /dev/null
+++ b/libs/three/src/core/Clock.js
@@ -0,0 +1,74 @@
+class Clock {
+
+ constructor( autoStart = true ) {
+
+ this.autoStart = autoStart;
+
+ this.startTime = 0;
+ this.oldTime = 0;
+ this.elapsedTime = 0;
+
+ this.running = false;
+
+ }
+
+ start() {
+
+ this.startTime = now();
+
+ 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 = now();
+
+ diff = ( newTime - this.oldTime ) / 1000;
+ this.oldTime = newTime;
+
+ this.elapsedTime += diff;
+
+ }
+
+ return diff;
+
+ }
+
+}
+
+function now() {
+
+ return ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732
+
+}
+
+export { Clock };
diff --git a/libs/three/src/core/EventDispatcher.js b/libs/three/src/core/EventDispatcher.js
new file mode 100644
index 000000000..e75d1e71d
--- /dev/null
+++ b/libs/three/src/core/EventDispatcher.js
@@ -0,0 +1,87 @@
+/**
+ * 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;
+
+ }
+
+ }
+
+}
+
+
+export { EventDispatcher };
diff --git a/libs/three/src/core/GLBufferAttribute.js b/libs/three/src/core/GLBufferAttribute.js
new file mode 100644
index 000000000..41f690894
--- /dev/null
+++ b/libs/three/src/core/GLBufferAttribute.js
@@ -0,0 +1,60 @@
+class GLBufferAttribute {
+
+ constructor( buffer, type, itemSize, elementSize, count ) {
+
+ this.isGLBufferAttribute = true;
+
+ this.name = '';
+
+ this.buffer = buffer;
+ this.type = type;
+ this.itemSize = itemSize;
+ this.elementSize = elementSize;
+ this.count = count;
+
+ this.version = 0;
+
+ }
+
+ set needsUpdate( value ) {
+
+ if ( value === true ) this.version ++;
+
+ }
+
+ setBuffer( buffer ) {
+
+ this.buffer = buffer;
+
+ return this;
+
+ }
+
+ setType( type, elementSize ) {
+
+ this.type = type;
+ this.elementSize = elementSize;
+
+ return this;
+
+ }
+
+ setItemSize( itemSize ) {
+
+ this.itemSize = itemSize;
+
+ return this;
+
+ }
+
+ setCount( count ) {
+
+ this.count = count;
+
+ return this;
+
+ }
+
+}
+
+export { GLBufferAttribute };
diff --git a/libs/three/src/core/InstancedBufferAttribute.js b/libs/three/src/core/InstancedBufferAttribute.js
new file mode 100644
index 000000000..ed317ee32
--- /dev/null
+++ b/libs/three/src/core/InstancedBufferAttribute.js
@@ -0,0 +1,39 @@
+import { BufferAttribute } from './BufferAttribute.js';
+
+class InstancedBufferAttribute extends BufferAttribute {
+
+ constructor( array, itemSize, normalized, meshPerAttribute = 1 ) {
+
+ super( array, itemSize, normalized );
+
+ this.isInstancedBufferAttribute = true;
+
+ 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;
+
+ }
+
+}
+
+export { InstancedBufferAttribute };
diff --git a/libs/three/src/core/InstancedBufferGeometry.js b/libs/three/src/core/InstancedBufferGeometry.js
new file mode 100644
index 000000000..0282b46f0
--- /dev/null
+++ b/libs/three/src/core/InstancedBufferGeometry.js
@@ -0,0 +1,40 @@
+import { BufferGeometry } from './BufferGeometry.js';
+
+class InstancedBufferGeometry extends BufferGeometry {
+
+ constructor() {
+
+ super();
+
+ this.isInstancedBufferGeometry = true;
+
+ this.type = 'InstancedBufferGeometry';
+ this.instanceCount = Infinity;
+
+ }
+
+ copy( source ) {
+
+ super.copy( source );
+
+ this.instanceCount = source.instanceCount;
+
+ return this;
+
+ }
+
+ toJSON() {
+
+ const data = super.toJSON();
+
+ data.instanceCount = this.instanceCount;
+
+ data.isInstancedBufferGeometry = true;
+
+ return data;
+
+ }
+
+}
+
+export { InstancedBufferGeometry };
diff --git a/libs/three/src/core/InstancedInterleavedBuffer.js b/libs/three/src/core/InstancedInterleavedBuffer.js
new file mode 100644
index 000000000..7f667161e
--- /dev/null
+++ b/libs/three/src/core/InstancedInterleavedBuffer.js
@@ -0,0 +1,48 @@
+import { InterleavedBuffer } from './InterleavedBuffer.js';
+
+class InstancedInterleavedBuffer extends InterleavedBuffer {
+
+ constructor( array, stride, meshPerAttribute = 1 ) {
+
+ super( array, stride );
+
+ this.isInstancedInterleavedBuffer = true;
+
+ 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;
+
+ }
+
+}
+
+export { InstancedInterleavedBuffer };
diff --git a/libs/three/src/core/InterleavedBuffer.js b/libs/three/src/core/InterleavedBuffer.js
new file mode 100644
index 000000000..78461a1cc
--- /dev/null
+++ b/libs/three/src/core/InterleavedBuffer.js
@@ -0,0 +1,165 @@
+import * as MathUtils from '../math/MathUtils.js';
+import { StaticDrawUsage } from '../constants.js';
+
+class InterleavedBuffer {
+
+ constructor( array, stride ) {
+
+ this.isInterleavedBuffer = true;
+
+ this.array = array;
+ this.stride = stride;
+ this.count = array !== undefined ? array.length / stride : 0;
+
+ this.usage = StaticDrawUsage;
+ this._updateRange = { offset: 0, count: - 1 };
+ this.updateRanges = [];
+
+ this.version = 0;
+
+ this.uuid = MathUtils.generateUUID();
+
+ }
+
+ onUploadCallback() {}
+
+ set needsUpdate( value ) {
+
+ if ( value === true ) this.version ++;
+
+ }
+
+ get updateRange() {
+
+ console.warn( 'THREE.InterleavedBuffer: updateRange() is deprecated and will be removed in r169. Use addUpdateRange() instead.' ); // @deprecated, r159
+ return this._updateRange;
+
+ }
+
+ setUsage( value ) {
+
+ this.usage = value;
+
+ return this;
+
+ }
+
+ addUpdateRange( start, count ) {
+
+ this.updateRanges.push( { start, count } );
+
+ }
+
+ clearUpdateRanges() {
+
+ this.updateRanges.length = 0;
+
+ }
+
+ 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 = 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 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 = MathUtils.generateUUID();
+
+ }
+
+ if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) {
+
+ data.arrayBuffers[ this.array.buffer._uuid ] = Array.from( new Uint32Array( this.array.buffer ) );
+
+ }
+
+ //
+
+ return {
+ uuid: this.uuid,
+ buffer: this.array.buffer._uuid,
+ type: this.array.constructor.name,
+ stride: this.stride
+ };
+
+ }
+
+}
+
+export { InterleavedBuffer };
diff --git a/libs/three/src/core/InterleavedBufferAttribute.js b/libs/three/src/core/InterleavedBufferAttribute.js
new file mode 100644
index 000000000..b24fe14ba
--- /dev/null
+++ b/libs/three/src/core/InterleavedBufferAttribute.js
@@ -0,0 +1,331 @@
+import { Vector3 } from '../math/Vector3.js';
+import { BufferAttribute } from './BufferAttribute.js';
+import { denormalize, normalize } from '../math/MathUtils.js';
+
+const _vector = /*@__PURE__*/ new Vector3();
+
+class InterleavedBufferAttribute {
+
+ constructor( interleavedBuffer, itemSize, offset, normalized = false ) {
+
+ this.isInterleavedBufferAttribute = true;
+
+ this.name = '';
+
+ this.data = interleavedBuffer;
+ this.itemSize = itemSize;
+ this.offset = offset;
+
+ this.normalized = normalized;
+
+ }
+
+ 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.fromBufferAttribute( this, i );
+
+ _vector.applyMatrix4( m );
+
+ this.setXYZ( i, _vector.x, _vector.y, _vector.z );
+
+ }
+
+ return this;
+
+ }
+
+ applyNormalMatrix( m ) {
+
+ for ( let i = 0, l = this.count; i < l; i ++ ) {
+
+ _vector.fromBufferAttribute( this, i );
+
+ _vector.applyNormalMatrix( m );
+
+ this.setXYZ( i, _vector.x, _vector.y, _vector.z );
+
+ }
+
+ return this;
+
+ }
+
+ transformDirection( m ) {
+
+ for ( let i = 0, l = this.count; i < l; i ++ ) {
+
+ _vector.fromBufferAttribute( this, i );
+
+ _vector.transformDirection( m );
+
+ this.setXYZ( i, _vector.x, _vector.y, _vector.z );
+
+ }
+
+ return this;
+
+ }
+
+ setX( index, x ) {
+
+ if ( this.normalized ) x = normalize( x, this.array );
+
+ this.data.array[ index * this.data.stride + this.offset ] = x;
+
+ return this;
+
+ }
+
+ setY( index, y ) {
+
+ if ( this.normalized ) y = normalize( y, this.array );
+
+ this.data.array[ index * this.data.stride + this.offset + 1 ] = y;
+
+ return this;
+
+ }
+
+ setZ( index, z ) {
+
+ if ( this.normalized ) z = normalize( z, this.array );
+
+ this.data.array[ index * this.data.stride + this.offset + 2 ] = z;
+
+ return this;
+
+ }
+
+ setW( index, w ) {
+
+ if ( this.normalized ) w = normalize( w, this.array );
+
+ this.data.array[ index * this.data.stride + this.offset + 3 ] = w;
+
+ return this;
+
+ }
+
+ getX( index ) {
+
+ let x = this.data.array[ index * this.data.stride + this.offset ];
+
+ if ( this.normalized ) x = denormalize( x, this.array );
+
+ return x;
+
+ }
+
+ getY( index ) {
+
+ let y = this.data.array[ index * this.data.stride + this.offset + 1 ];
+
+ if ( this.normalized ) y = denormalize( y, this.array );
+
+ return y;
+
+ }
+
+ getZ( index ) {
+
+ let z = this.data.array[ index * this.data.stride + this.offset + 2 ];
+
+ if ( this.normalized ) z = denormalize( z, this.array );
+
+ return z;
+
+ }
+
+ getW( index ) {
+
+ let w = this.data.array[ index * this.data.stride + this.offset + 3 ];
+
+ if ( this.normalized ) w = denormalize( w, this.array );
+
+ return w;
+
+ }
+
+ setXY( index, x, y ) {
+
+ index = index * this.data.stride + this.offset;
+
+ if ( this.normalized ) {
+
+ x = normalize( x, this.array );
+ y = normalize( y, this.array );
+
+ }
+
+ 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;
+
+ if ( this.normalized ) {
+
+ x = normalize( x, this.array );
+ y = normalize( y, this.array );
+ z = normalize( z, this.array );
+
+ }
+
+ 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;
+
+ if ( this.normalized ) {
+
+ x = normalize( x, this.array );
+ y = normalize( y, this.array );
+ z = normalize( z, this.array );
+ w = normalize( w, this.array );
+
+ }
+
+ 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 interleaved buffer attribute will de-interleave 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 interleaved buffer attribute will de-interleave 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 ] );
+
+ }
+
+ }
+
+ // de-interleave 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 interleaved attribute
+
+ 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
+ };
+
+ }
+
+ }
+
+}
+
+
+export { InterleavedBufferAttribute };
diff --git a/libs/three/src/core/Layers.js b/libs/three/src/core/Layers.js
new file mode 100644
index 000000000..568afe924
--- /dev/null
+++ b/libs/three/src/core/Layers.js
@@ -0,0 +1,60 @@
+class Layers {
+
+ constructor() {
+
+ this.mask = 1 | 0;
+
+ }
+
+ set( channel ) {
+
+ this.mask = ( 1 << channel | 0 ) >>> 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;
+
+ }
+
+ isEnabled( channel ) {
+
+ return ( this.mask & ( 1 << channel | 0 ) ) !== 0;
+
+ }
+
+}
+
+
+export { Layers };
diff --git a/libs/three/src/core/Object3D.js b/libs/three/src/core/Object3D.js
new file mode 100644
index 000000000..c7de56045
--- /dev/null
+++ b/libs/three/src/core/Object3D.js
@@ -0,0 +1,1008 @@
+import { Quaternion } from '../math/Quaternion.js';
+import { Vector3 } from '../math/Vector3.js';
+import { Matrix4 } from '../math/Matrix4.js';
+import { EventDispatcher } from './EventDispatcher.js';
+import { Euler } from '../math/Euler.js';
+import { Layers } from './Layers.js';
+import { Matrix3 } from '../math/Matrix3.js';
+import * as MathUtils from '../math/MathUtils.js';
+
+let _object3DId = 0;
+
+const _v1 = /*@__PURE__*/ new Vector3();
+const _q1 = /*@__PURE__*/ new Quaternion();
+const _m1 = /*@__PURE__*/ new Matrix4();
+const _target = /*@__PURE__*/ new Vector3();
+
+const _position = /*@__PURE__*/ new Vector3();
+const _scale = /*@__PURE__*/ new Vector3();
+const _quaternion = /*@__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();
+
+ this.isObject3D = true;
+
+ Object.defineProperty( this, 'id', { value: _object3DId ++ } );
+
+ this.uuid = MathUtils.generateUUID();
+
+ this.name = '';
+ this.type = 'Object3D';
+
+ this.parent = null;
+ this.children = [];
+
+ this.up = Object3D.DEFAULT_UP.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.DEFAULT_MATRIX_AUTO_UPDATE;
+
+ this.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE; // checked by the renderer
+ 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 = {};
+
+ }
+
+ onBeforeShadow( /* renderer, object, camera, shadowCamera, geometry, depthMaterial, group */ ) {}
+
+ onAfterShadow( /* renderer, object, camera, shadowCamera, geometry, depthMaterial, group */ ) {}
+
+ onBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {}
+
+ onAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {}
+
+ 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.copy( axis ).applyQuaternion( this.quaternion );
+
+ this.position.add( _v1.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 ) {
+
+ this.updateWorldMatrix( true, false );
+
+ return vector.applyMatrix4( this.matrixWorld );
+
+ }
+
+ worldToLocal( vector ) {
+
+ this.updateWorldMatrix( true, false );
+
+ return vector.applyMatrix4( _m1.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.setFromMatrixPosition( this.matrixWorld );
+
+ if ( this.isCamera || this.isLight ) {
+
+ _m1.lookAt( _position, _target, this.up );
+
+ } else {
+
+ _m1.lookAt( _target, _position, this.up );
+
+ }
+
+ this.quaternion.setFromRotationMatrix( _m1 );
+
+ if ( parent ) {
+
+ _m1.extractRotation( parent.matrixWorld );
+ _q1.setFromRotationMatrix( _m1 );
+ 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() {
+
+ return this.remove( ... this.children );
+
+ }
+
+ attach( object ) {
+
+ // adds object as a child of this, while maintaining the object's world transform
+
+ // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s)
+
+ this.updateWorldMatrix( true, false );
+
+ _m1.copy( this.matrixWorld ).invert();
+
+ if ( object.parent !== null ) {
+
+ object.parent.updateWorldMatrix( true, false );
+
+ _m1.multiply( object.parent.matrixWorld );
+
+ }
+
+ object.applyMatrix4( _m1 );
+
+ 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;
+
+ }
+
+ getObjectsByProperty( name, value, result = [] ) {
+
+ if ( this[ name ] === value ) result.push( this );
+
+ const children = this.children;
+
+ for ( let i = 0, l = children.length; i < l; i ++ ) {
+
+ children[ i ].getObjectsByProperty( name, value, result );
+
+ }
+
+ return result;
+
+ }
+
+ getWorldPosition( target ) {
+
+ this.updateWorldMatrix( true, false );
+
+ return target.setFromMatrixPosition( this.matrixWorld );
+
+ }
+
+ getWorldQuaternion( target ) {
+
+ this.updateWorldMatrix( true, false );
+
+ this.matrixWorld.decompose( _position, target, _scale );
+
+ return target;
+
+ }
+
+ getWorldScale( target ) {
+
+ this.updateWorldMatrix( true, false );
+
+ this.matrixWorld.decompose( _position, _quaternion, 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( /* raycaster, intersects */ ) {}
+
+ 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 ++ ) {
+
+ const child = children[ i ];
+
+ if ( child.matrixWorldAutoUpdate === true || force === true ) {
+
+ child.updateMatrixWorld( force );
+
+ }
+
+ }
+
+ }
+
+ updateWorldMatrix( updateParents, updateChildren ) {
+
+ const parent = this.parent;
+
+ if ( updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true ) {
+
+ 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 ++ ) {
+
+ const child = children[ i ];
+
+ if ( child.matrixWorldAutoUpdate === true ) {
+
+ child.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: {},
+ nodes: {}
+ };
+
+ output.metadata = {
+ version: 4.6,
+ 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 ( Object.keys( this.userData ).length > 0 ) object.userData = this.userData;
+
+ object.layers = this.layers.mask;
+ object.matrix = this.matrix.toArray();
+ object.up = this.up.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();
+
+ }
+
+ if ( this.isBatchedMesh ) {
+
+ object.type = 'BatchedMesh';
+ object.perObjectFrustumCulled = this.perObjectFrustumCulled;
+ object.sortObjects = this.sortObjects;
+
+ object.drawRanges = this._drawRanges;
+ object.reservedRanges = this._reservedRanges;
+
+ object.visibility = this._visibility;
+ object.active = this._active;
+ object.bounds = this._bounds.map( bound => ( {
+ boxInitialized: bound.boxInitialized,
+ boxMin: bound.box.min.toArray(),
+ boxMax: bound.box.max.toArray(),
+
+ sphereInitialized: bound.sphereInitialized,
+ sphereRadius: bound.sphere.radius,
+ sphereCenter: bound.sphere.center.toArray()
+ } ) );
+
+ object.maxGeometryCount = this._maxGeometryCount;
+ object.maxVertexCount = this._maxVertexCount;
+ object.maxIndexCount = this._maxIndexCount;
+
+ object.geometryInitialized = this._geometryInitialized;
+ object.geometryCount = this._geometryCount;
+
+ object.matricesTexture = this._matricesTexture.toJSON( meta );
+
+ if ( this.boundingSphere !== null ) {
+
+ object.boundingSphere = {
+ center: object.boundingSphere.center.toArray(),
+ radius: object.boundingSphere.radius
+ };
+
+ }
+
+ if ( this.boundingBox !== null ) {
+
+ object.boundingBox = {
+ min: object.boundingBox.min.toArray(),
+ max: object.boundingBox.max.toArray()
+ };
+
+ }
+
+ }
+
+ //
+
+ 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 && this.environment.isRenderTargetTexture !== true ) {
+
+ 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 );
+ const nodes = extractFromCache( meta.nodes );
+
+ 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;
+ if ( nodes.length > 0 ) output.nodes = nodes;
+
+ }
+
+ 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.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate;
+ 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.animations = source.animations.slice();
+
+ 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.DEFAULT_UP = /*@__PURE__*/ new Vector3( 0, 1, 0 );
+Object3D.DEFAULT_MATRIX_AUTO_UPDATE = true;
+Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true;
+
+export { Object3D };
diff --git a/libs/three/src/core/Raycaster.js b/libs/three/src/core/Raycaster.js
new file mode 100644
index 000000000..c4f0c3712
--- /dev/null
+++ b/libs/three/src/core/Raycaster.js
@@ -0,0 +1,110 @@
+import { Ray } from '../math/Ray.js';
+import { Layers } from './Layers.js';
+
+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.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.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 = true, intersects = [] ) {
+
+ intersectObject( object, this, intersects, recursive );
+
+ intersects.sort( ascSort );
+
+ return intersects;
+
+ }
+
+ intersectObjects( objects, recursive = true, 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 );
+
+ }
+
+ }
+
+}
+
+export { Raycaster };
diff --git a/libs/three/src/core/RenderTarget.js b/libs/three/src/core/RenderTarget.js
new file mode 100644
index 000000000..675fa29b4
--- /dev/null
+++ b/libs/three/src/core/RenderTarget.js
@@ -0,0 +1,131 @@
+import { EventDispatcher } from './EventDispatcher.js';
+import { Texture } from '../textures/Texture.js';
+import { LinearFilter, NoColorSpace, SRGBColorSpace, sRGBEncoding } from '../constants.js';
+import { Vector4 } from '../math/Vector4.js';
+import { Source } from '../textures/Source.js';
+import { warnOnce } from '../utils.js';
+
+/*
+ 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 RenderTarget extends EventDispatcher {
+
+ constructor( width = 1, height = 1, options = {} ) {
+
+ super();
+
+ this.isRenderTarget = true;
+
+ 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 );
+
+ const image = { width: width, height: height, depth: 1 };
+
+ if ( options.encoding !== undefined ) {
+
+ // @deprecated, r152
+ warnOnce( 'THREE.WebGLRenderTarget: option.encoding has been replaced by option.colorSpace.' );
+ options.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace;
+
+ }
+
+ options = Object.assign( {
+ generateMipmaps: false,
+ internalFormat: null,
+ minFilter: LinearFilter,
+ depthBuffer: true,
+ stencilBuffer: false,
+ depthTexture: null,
+ samples: 0
+ }, options );
+
+ this.texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace );
+ this.texture.isRenderTargetTexture = true;
+
+ this.texture.flipY = false;
+ this.texture.generateMipmaps = options.generateMipmaps;
+ this.texture.internalFormat = options.internalFormat;
+
+ this.depthBuffer = options.depthBuffer;
+ this.stencilBuffer = options.stencilBuffer;
+
+ this.depthTexture = options.depthTexture;
+
+ this.samples = options.samples;
+
+ }
+
+ 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.scissor.copy( source.scissor );
+ this.scissorTest = source.scissorTest;
+
+ this.viewport.copy( source.viewport );
+
+ this.texture = source.texture.clone();
+ this.texture.isRenderTargetTexture = true;
+
+ // ensure image object is not shared, see #20328
+
+ const image = Object.assign( {}, source.texture.image );
+ this.texture.source = new Source( image );
+
+ this.depthBuffer = source.depthBuffer;
+ this.stencilBuffer = source.stencilBuffer;
+
+ if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone();
+
+ this.samples = source.samples;
+
+ return this;
+
+ }
+
+ dispose() {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+}
+
+export { RenderTarget };
diff --git a/libs/three/src/core/Uniform.js b/libs/three/src/core/Uniform.js
new file mode 100644
index 000000000..a2d421c40
--- /dev/null
+++ b/libs/three/src/core/Uniform.js
@@ -0,0 +1,17 @@
+class Uniform {
+
+ constructor( value ) {
+
+ this.value = value;
+
+ }
+
+ clone() {
+
+ return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() );
+
+ }
+
+}
+
+export { Uniform };
diff --git a/libs/three/src/core/UniformsGroup.js b/libs/three/src/core/UniformsGroup.js
new file mode 100644
index 000000000..7fa592ba4
--- /dev/null
+++ b/libs/three/src/core/UniformsGroup.js
@@ -0,0 +1,98 @@
+import { EventDispatcher } from './EventDispatcher.js';
+import { StaticDrawUsage } from '../constants.js';
+
+let _id = 0;
+
+class UniformsGroup extends EventDispatcher {
+
+ constructor() {
+
+ super();
+
+ this.isUniformsGroup = true;
+
+ Object.defineProperty( this, 'id', { value: _id ++ } );
+
+ this.name = '';
+
+ this.usage = StaticDrawUsage;
+ this.uniforms = [];
+
+ }
+
+ add( uniform ) {
+
+ this.uniforms.push( uniform );
+
+ return this;
+
+ }
+
+ remove( uniform ) {
+
+ const index = this.uniforms.indexOf( uniform );
+
+ if ( index !== - 1 ) this.uniforms.splice( index, 1 );
+
+ return this;
+
+ }
+
+ setName( name ) {
+
+ this.name = name;
+
+ return this;
+
+ }
+
+ setUsage( value ) {
+
+ this.usage = value;
+
+ return this;
+
+ }
+
+ dispose() {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ return this;
+
+ }
+
+ copy( source ) {
+
+ this.name = source.name;
+ this.usage = source.usage;
+
+ const uniformsSource = source.uniforms;
+
+ this.uniforms.length = 0;
+
+ for ( let i = 0, l = uniformsSource.length; i < l; i ++ ) {
+
+ const uniforms = Array.isArray( uniformsSource[ i ] ) ? uniformsSource[ i ] : [ uniformsSource[ i ] ];
+
+ for ( let j = 0; j < uniforms.length; j ++ ) {
+
+ this.uniforms.push( uniforms[ j ].clone() );
+
+ }
+
+ }
+
+ return this;
+
+ }
+
+ clone() {
+
+ return new this.constructor().copy( this );
+
+ }
+
+}
+
+export { UniformsGroup };
diff --git a/libs/three/src/extras/DataUtils.js b/libs/three/src/extras/DataUtils.js
new file mode 100644
index 000000000..3234f34c0
--- /dev/null
+++ b/libs/three/src/extras/DataUtils.js
@@ -0,0 +1,176 @@
+import { clamp } from '../math/MathUtils.js';
+
+// Fast Half Float Conversions, http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
+
+const _tables = /*@__PURE__*/ _generateTables();
+
+function _generateTables() {
+
+ // float32 to float16 helpers
+
+ const buffer = new ArrayBuffer( 4 );
+ const floatView = new Float32Array( buffer );
+ const uint32View = new Uint32Array( buffer );
+
+ const baseTable = new Uint32Array( 512 );
+ const shiftTable = new Uint32Array( 512 );
+
+ for ( let i = 0; i < 256; ++ i ) {
+
+ const e = i - 127;
+
+ // very small number (0, -0)
+
+ if ( e < - 27 ) {
+
+ baseTable[ i ] = 0x0000;
+ baseTable[ i | 0x100 ] = 0x8000;
+ shiftTable[ i ] = 24;
+ shiftTable[ i | 0x100 ] = 24;
+
+ // small number (denorm)
+
+ } else if ( e < - 14 ) {
+
+ baseTable[ i ] = 0x0400 >> ( - e - 14 );
+ baseTable[ i | 0x100 ] = ( 0x0400 >> ( - e - 14 ) ) | 0x8000;
+ shiftTable[ i ] = - e - 1;
+ shiftTable[ i | 0x100 ] = - e - 1;
+
+ // normal number
+
+ } else if ( e <= 15 ) {
+
+ baseTable[ i ] = ( e + 15 ) << 10;
+ baseTable[ i | 0x100 ] = ( ( e + 15 ) << 10 ) | 0x8000;
+ shiftTable[ i ] = 13;
+ shiftTable[ i | 0x100 ] = 13;
+
+ // large number (Infinity, -Infinity)
+
+ } else if ( e < 128 ) {
+
+ baseTable[ i ] = 0x7c00;
+ baseTable[ i | 0x100 ] = 0xfc00;
+ shiftTable[ i ] = 24;
+ shiftTable[ i | 0x100 ] = 24;
+
+ // stay (NaN, Infinity, -Infinity)
+
+ } else {
+
+ baseTable[ i ] = 0x7c00;
+ baseTable[ i | 0x100 ] = 0xfc00;
+ shiftTable[ i ] = 13;
+ shiftTable[ i | 0x100 ] = 13;
+
+ }
+
+ }
+
+ // float16 to float32 helpers
+
+ const mantissaTable = new Uint32Array( 2048 );
+ const exponentTable = new Uint32Array( 64 );
+ const offsetTable = new Uint32Array( 64 );
+
+ for ( let i = 1; i < 1024; ++ i ) {
+
+ let m = i << 13; // zero pad mantissa bits
+ let e = 0; // zero exponent
+
+ // normalized
+ while ( ( m & 0x00800000 ) === 0 ) {
+
+ m <<= 1;
+ e -= 0x00800000; // decrement exponent
+
+ }
+
+ m &= ~ 0x00800000; // clear leading 1 bit
+ e += 0x38800000; // adjust bias
+
+ mantissaTable[ i ] = m | e;
+
+ }
+
+ for ( let i = 1024; i < 2048; ++ i ) {
+
+ mantissaTable[ i ] = 0x38000000 + ( ( i - 1024 ) << 13 );
+
+ }
+
+ for ( let i = 1; i < 31; ++ i ) {
+
+ exponentTable[ i ] = i << 23;
+
+ }
+
+ exponentTable[ 31 ] = 0x47800000;
+ exponentTable[ 32 ] = 0x80000000;
+
+ for ( let i = 33; i < 63; ++ i ) {
+
+ exponentTable[ i ] = 0x80000000 + ( ( i - 32 ) << 23 );
+
+ }
+
+ exponentTable[ 63 ] = 0xc7800000;
+
+ for ( let i = 1; i < 64; ++ i ) {
+
+ if ( i !== 32 ) {
+
+ offsetTable[ i ] = 1024;
+
+ }
+
+ }
+
+ return {
+ floatView: floatView,
+ uint32View: uint32View,
+ baseTable: baseTable,
+ shiftTable: shiftTable,
+ mantissaTable: mantissaTable,
+ exponentTable: exponentTable,
+ offsetTable: offsetTable
+ };
+
+}
+
+// float32 to float16
+
+function toHalfFloat( val ) {
+
+ if ( Math.abs( val ) > 65504 ) console.warn( 'THREE.DataUtils.toHalfFloat(): Value out of range.' );
+
+ val = clamp( val, - 65504, 65504 );
+
+ _tables.floatView[ 0 ] = val;
+ const f = _tables.uint32View[ 0 ];
+ const e = ( f >> 23 ) & 0x1ff;
+ return _tables.baseTable[ e ] + ( ( f & 0x007fffff ) >> _tables.shiftTable[ e ] );
+
+}
+
+// float16 to float32
+
+function fromHalfFloat( val ) {
+
+ const m = val >> 10;
+ _tables.uint32View[ 0 ] = _tables.mantissaTable[ _tables.offsetTable[ m ] + ( val & 0x3ff ) ] + _tables.exponentTable[ m ];
+ return _tables.floatView[ 0 ];
+
+}
+
+const DataUtils = {
+ toHalfFloat: toHalfFloat,
+ fromHalfFloat: fromHalfFloat,
+};
+
+export {
+ toHalfFloat,
+ fromHalfFloat,
+ DataUtils
+};
diff --git a/libs/three/src/extras/Earcut.js b/libs/three/src/extras/Earcut.js
new file mode 100644
index 000000000..0a824aca2
--- /dev/null
+++ b/libs/three/src/extras/Earcut.js
@@ -0,0 +1,789 @@
+/**
+ * Port from https://github.com/mapbox/earcut (v2.2.4)
+ */
+
+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 ? 32767 / invSize : 0;
+
+ }
+
+ earcutLinked( outerNode, triangles, dim, minX, minY, invSize, 0 );
+
+ 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 | 0 );
+ triangles.push( ear.i / dim | 0 );
+ triangles.push( next.i / dim | 0 );
+
+ 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
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
+
+ // triangle bbox; min & max are calculated like this for speed
+ const x0 = ax < bx ? ( ax < cx ? ax : cx ) : ( bx < cx ? bx : cx ),
+ y0 = ay < by ? ( ay < cy ? ay : cy ) : ( by < cy ? by : cy ),
+ x1 = ax > bx ? ( ax > cx ? ax : cx ) : ( bx > cx ? bx : cx ),
+ y1 = ay > by ? ( ay > cy ? ay : cy ) : ( by > cy ? by : cy );
+
+ let p = c.next;
+ while ( p !== a ) {
+
+ if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 &&
+ pointInTriangle( ax, ay, bx, by, cx, cy, 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
+
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
+
+ // triangle bbox; min & max are calculated like this for speed
+ const x0 = ax < bx ? ( ax < cx ? ax : cx ) : ( bx < cx ? bx : cx ),
+ y0 = ay < by ? ( ay < cy ? ay : cy ) : ( by < cy ? by : cy ),
+ x1 = ax > bx ? ( ax > cx ? ax : cx ) : ( bx > cx ? bx : cx ),
+ y1 = ay > by ? ( ay > cy ? ay : cy ) : ( by > cy ? by : cy );
+
+ // z-order range for the current triangle bbox;
+ const minZ = zOrder( x0, y0, minX, minY, invSize ),
+ maxZ = zOrder( x1, y1, 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.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
+ pointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false;
+ p = p.prevZ;
+
+ if ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
+ pointInTriangle( ax, ay, bx, by, cx, cy, 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.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
+ pointInTriangle( ax, ay, bx, by, cx, cy, 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.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
+ pointInTriangle( ax, ay, bx, by, cx, cy, 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 | 0 );
+ triangles.push( p.i / dim | 0 );
+ triangles.push( b.i / dim | 0 );
+
+ // 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, 0 );
+ earcutLinked( c, triangles, dim, minX, minY, invSize, 0 );
+ 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 ++ ) {
+
+ outerNode = eliminateHole( queue[ i ], outerNode );
+
+ }
+
+ return outerNode;
+
+}
+
+function compareX( a, b ) {
+
+ return a.x - b.x;
+
+}
+
+// find a bridge between vertices that connects hole with an outer ring and link it
+function eliminateHole( hole, outerNode ) {
+
+ const bridge = findHoleBridge( hole, outerNode );
+ if ( ! bridge ) {
+
+ return outerNode;
+
+ }
+
+ const bridgeReverse = splitPolygon( bridge, hole );
+
+ // filter collinear points around the cuts
+ filterPoints( bridgeReverse, bridgeReverse.next );
+ return filterPoints( bridge, bridge.next );
+
+}
+
+// David Eberly's algorithm for finding a bridge between hole and outer polygon
+function findHoleBridge( hole, outerNode ) {
+
+ let p = outerNode,
+ qx = - Infinity,
+ m;
+
+ const hx = hole.x, hy = hole.y;
+
+ // 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;
+ m = p.x < p.next.x ? p : p.next;
+ if ( x === hx ) return m; // hole touches outer segment; pick leftmost endpoint
+
+ }
+
+ }
+
+ p = p.next;
+
+ } while ( p !== outerNode );
+
+ if ( ! m ) return null;
+
+ // 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 === 0 ) 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 = ( x - minX ) * invSize | 0;
+ y = ( y - minY ) * invSize | 0;
+
+ 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 ) &&
+ ( ax - px ) * ( by - py ) >= ( bx - px ) * ( ay - py ) &&
+ ( bx - px ) * ( cy - py ) >= ( cx - px ) * ( by - py );
+
+}
+
+// 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 = 0;
+
+ // 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;
+
+}
+
+export { Earcut };
diff --git a/libs/three/src/extras/ImageUtils.js b/libs/three/src/extras/ImageUtils.js
new file mode 100644
index 000000000..ceddc4d9d
--- /dev/null
+++ b/libs/three/src/extras/ImageUtils.js
@@ -0,0 +1,129 @@
+import { createElementNS } from '../utils.js';
+import { SRGBToLinear } from '../math/ColorManagement.js';
+
+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 = createElementNS( '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' );
+
+ }
+
+ }
+
+ static sRGBToLinear( image ) {
+
+ if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
+ ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
+ ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
+
+ const canvas = createElementNS( 'canvas' );
+
+ canvas.width = image.width;
+ canvas.height = image.height;
+
+ const context = canvas.getContext( '2d' );
+ context.drawImage( image, 0, 0, image.width, image.height );
+
+ const imageData = context.getImageData( 0, 0, image.width, image.height );
+ const data = imageData.data;
+
+ for ( let i = 0; i < data.length; i ++ ) {
+
+ data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255;
+
+ }
+
+ context.putImageData( imageData, 0, 0 );
+
+ return canvas;
+
+ } else if ( image.data ) {
+
+ const data = image.data.slice( 0 );
+
+ for ( let i = 0; i < data.length; i ++ ) {
+
+ if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) {
+
+ data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 );
+
+ } else {
+
+ // assuming float
+
+ data[ i ] = SRGBToLinear( data[ i ] );
+
+ }
+
+ }
+
+ return {
+ data: data,
+ width: image.width,
+ height: image.height
+ };
+
+ } else {
+
+ console.warn( 'THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' );
+ return image;
+
+ }
+
+ }
+
+}
+
+export { ImageUtils };
diff --git a/libs/three/src/extras/PMREMGenerator.js b/libs/three/src/extras/PMREMGenerator.js
new file mode 100644
index 000000000..aa8a37932
--- /dev/null
+++ b/libs/three/src/extras/PMREMGenerator.js
@@ -0,0 +1,906 @@
+import {
+ CubeReflectionMapping,
+ CubeRefractionMapping,
+ CubeUVReflectionMapping,
+ LinearFilter,
+ NoToneMapping,
+ NoBlending,
+ RGBAFormat,
+ HalfFloatType,
+ BackSide,
+ LinearSRGBColorSpace
+} from '../constants.js';
+
+import { BufferAttribute } from '../core/BufferAttribute.js';
+import { BufferGeometry } from '../core/BufferGeometry.js';
+import { Mesh } from '../objects/Mesh.js';
+import { OrthographicCamera } from '../cameras/OrthographicCamera.js';
+import { PerspectiveCamera } from '../cameras/PerspectiveCamera.js';
+import { ShaderMaterial } from '../materials/ShaderMaterial.js';
+import { Vector3 } from '../math/Vector3.js';
+import { Color } from '../math/Color.js';
+import { WebGLRenderTarget } from '../renderers/WebGLRenderTarget.js';
+import { MeshBasicMaterial } from '../materials/MeshBasicMaterial.js';
+import { BoxGeometry } from '../geometries/BoxGeometry.js';
+
+const LOD_MIN = 4;
+
+// 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 ];
+
+// 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 _flatCamera = /*@__PURE__*/ new OrthographicCamera();
+const _clearColor = /*@__PURE__*/ new Color();
+let _oldTarget = null;
+let _oldActiveCubeFace = 0;
+let _oldActiveMipmapLevel = 0;
+
+// 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._lodMax = 0;
+ this._cubeSize = 0;
+ this._lodPlanes = [];
+ this._sizeLods = [];
+ this._sigmas = [];
+
+ this._blurMaterial = null;
+ this._cubemapMaterial = null;
+ this._equirectMaterial = 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();
+ _oldActiveCubeFace = this._renderer.getActiveCubeFace();
+ _oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();
+
+ this._setSize( 256 );
+
+ const cubeUVRenderTarget = this._allocateTargets();
+ cubeUVRenderTarget.depthBuffer = true;
+
+ 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
+ * or HDR. The ideal input image size is 1k (1024 x 512),
+ * as this matches best with the 256 x 256 cubemap output.
+ */
+ fromEquirectangular( equirectangular, renderTarget = null ) {
+
+ return this._fromTexture( equirectangular, renderTarget );
+
+ }
+
+ /**
+ * Generates a PMREM from an cubemap texture, which can be either LDR
+ * or HDR. The ideal input cube size is 256 x 256,
+ * as this matches best with the 256 x 256 cubemap output.
+ */
+ fromCubemap( cubemap, renderTarget = null ) {
+
+ return this._fromTexture( cubemap, renderTarget );
+
+ }
+
+ /**
+ * 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._cubemapMaterial === null ) {
+
+ this._cubemapMaterial = _getCubemapMaterial();
+ this._compileMaterial( this._cubemapMaterial );
+
+ }
+
+ }
+
+ /**
+ * 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._equirectMaterial === null ) {
+
+ this._equirectMaterial = _getEquirectMaterial();
+ this._compileMaterial( this._equirectMaterial );
+
+ }
+
+ }
+
+ /**
+ * 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._dispose();
+
+ if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose();
+ if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose();
+
+ }
+
+ // private interface
+
+ _setSize( cubeSize ) {
+
+ this._lodMax = Math.floor( Math.log2( cubeSize ) );
+ this._cubeSize = Math.pow( 2, this._lodMax );
+
+ }
+
+ _dispose() {
+
+ if ( this._blurMaterial !== null ) this._blurMaterial.dispose();
+
+ if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose();
+
+ for ( let i = 0; i < this._lodPlanes.length; i ++ ) {
+
+ this._lodPlanes[ i ].dispose();
+
+ }
+
+ }
+
+ _cleanup( outputTarget ) {
+
+ this._renderer.setRenderTarget( _oldTarget, _oldActiveCubeFace, _oldActiveMipmapLevel );
+ outputTarget.scissorTest = false;
+ _setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height );
+
+ }
+
+ _fromTexture( texture, renderTarget ) {
+
+ if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) {
+
+ this._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) );
+
+ } else { // Equirectangular
+
+ this._setSize( texture.image.width / 4 );
+
+ }
+
+ _oldTarget = this._renderer.getRenderTarget();
+ _oldActiveCubeFace = this._renderer.getActiveCubeFace();
+ _oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();
+
+ const cubeUVRenderTarget = renderTarget || this._allocateTargets();
+ this._textureToCubeUV( texture, cubeUVRenderTarget );
+ this._applyPMREM( cubeUVRenderTarget );
+ this._cleanup( cubeUVRenderTarget );
+
+ return cubeUVRenderTarget;
+
+ }
+
+ _allocateTargets() {
+
+ const width = 3 * Math.max( this._cubeSize, 16 * 7 );
+ const height = 4 * this._cubeSize;
+
+ const params = {
+ magFilter: LinearFilter,
+ minFilter: LinearFilter,
+ generateMipmaps: false,
+ type: HalfFloatType,
+ format: RGBAFormat,
+ colorSpace: LinearSRGBColorSpace,
+ depthBuffer: false
+ };
+
+ const cubeUVRenderTarget = _createRenderTarget( width, height, params );
+
+ if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) {
+
+ if ( this._pingPongRenderTarget !== null ) {
+
+ this._dispose();
+
+ }
+
+ this._pingPongRenderTarget = _createRenderTarget( width, height, params );
+
+ const { _lodMax } = this;
+ ( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) );
+
+ this._blurMaterial = _getBlurShader( _lodMax, width, height );
+
+ }
+
+ return cubeUVRenderTarget;
+
+ }
+
+ _compileMaterial( material ) {
+
+ const tmpMesh = new Mesh( this._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 toneMapping = renderer.toneMapping;
+ renderer.getClearColor( _clearColor );
+
+ renderer.toneMapping = NoToneMapping;
+ 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 ] );
+
+ }
+
+ const size = this._cubeSize;
+
+ _setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size );
+
+ renderer.setRenderTarget( cubeUVRenderTarget );
+
+ if ( useSolidColor ) {
+
+ renderer.render( backgroundBox, cubeCamera );
+
+ }
+
+ renderer.render( scene, cubeCamera );
+
+ }
+
+ backgroundBox.geometry.dispose();
+ backgroundBox.material.dispose();
+
+ renderer.toneMapping = toneMapping;
+ renderer.autoClear = originalAutoClear;
+ scene.background = background;
+
+ }
+
+ _textureToCubeUV( texture, cubeUVRenderTarget ) {
+
+ const renderer = this._renderer;
+
+ const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping );
+
+ if ( isCubeTexture ) {
+
+ if ( this._cubemapMaterial === null ) {
+
+ this._cubemapMaterial = _getCubemapMaterial();
+
+ }
+
+ this._cubemapMaterial.uniforms.flipEnvMap.value = ( texture.isRenderTargetTexture === false ) ? - 1 : 1;
+
+ } else {
+
+ if ( this._equirectMaterial === null ) {
+
+ this._equirectMaterial = _getEquirectMaterial();
+
+ }
+
+ }
+
+ const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial;
+ const mesh = new Mesh( this._lodPlanes[ 0 ], material );
+
+ const uniforms = material.uniforms;
+
+ uniforms[ 'envMap' ].value = texture;
+
+ const size = this._cubeSize;
+
+ _setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size );
+
+ 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 < this._lodPlanes.length; i ++ ) {
+
+ const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._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( this._lodPlanes[ lodOut ], blurMaterial );
+ const blurUniforms = blurMaterial.uniforms;
+
+ const pixels = this._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;
+
+ }
+
+ const { _lodMax } = this;
+ blurUniforms[ 'dTheta' ].value = radiansPerPixel;
+ blurUniforms[ 'mipInt' ].value = _lodMax - lodIn;
+
+ const outputSize = this._sizeLods[ lodOut ];
+ const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 );
+ const y = 4 * ( this._cubeSize - outputSize );
+
+ _setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize );
+ renderer.setRenderTarget( targetOut );
+ renderer.render( blurMesh, _flatCamera );
+
+ }
+
+}
+
+
+
+function _createPlanes( lodMax ) {
+
+ const lodPlanes = [];
+ const sizeLods = [];
+ const sigmas = [];
+
+ let lod = lodMax;
+
+ const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length;
+
+ for ( let i = 0; i < totalLods; i ++ ) {
+
+ const sizeLod = Math.pow( 2, lod );
+ sizeLods.push( sizeLod );
+ let sigma = 1.0 / sizeLod;
+
+ if ( i > lodMax - LOD_MIN ) {
+
+ sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ];
+
+ } else if ( i === 0 ) {
+
+ sigma = 0;
+
+ }
+
+ sigmas.push( sigma );
+
+ const texelSize = 1.0 / ( sizeLod - 2 );
+ const min = - texelSize;
+ const max = 1 + texelSize;
+ 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( width, height, params ) {
+
+ const cubeUVRenderTarget = new WebGLRenderTarget( width, height, 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( lodMax, width, height ) {
+
+ const weights = new Float32Array( MAX_SAMPLES );
+ const poleAxis = new Vector3( 0, 1, 0 );
+ const shaderMaterial = new ShaderMaterial( {
+
+ name: 'SphericalGaussianBlur',
+
+ defines: {
+ 'n': MAX_SAMPLES,
+ 'CUBEUV_TEXEL_WIDTH': 1.0 / width,
+ 'CUBEUV_TEXEL_HEIGHT': 1.0 / height,
+ 'CUBEUV_MAX_MIP': `${lodMax}.0`,
+ },
+
+ uniforms: {
+ 'envMap': { value: null },
+ 'samples': { value: 1 },
+ 'weights': { value: weights },
+ 'latitudinal': { value: false },
+ 'dTheta': { value: 0 },
+ 'mipInt': { value: 0 },
+ 'poleAxis': { value: poleAxis }
+ },
+
+ 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;
+
+ #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 );
+
+ }
+
+ }
+ `,
+
+ blending: NoBlending,
+ depthTest: false,
+ depthWrite: false
+
+ } );
+
+ return shaderMaterial;
+
+}
+
+function _getEquirectMaterial() {
+
+ return new ShaderMaterial( {
+
+ name: 'EquirectangularToCubeUV',
+
+ uniforms: {
+ 'envMap': { value: null }
+ },
+
+ vertexShader: _getCommonVertexShader(),
+
+ fragmentShader: /* glsl */`
+
+ precision mediump float;
+ precision mediump int;
+
+ varying vec3 vOutputDirection;
+
+ uniform sampler2D envMap;
+
+ #include
+
+ void main() {
+
+ vec3 outputDirection = normalize( vOutputDirection );
+ vec2 uv = equirectUv( outputDirection );
+
+ gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 );
+
+ }
+ `,
+
+ blending: NoBlending,
+ depthTest: false,
+ depthWrite: false
+
+ } );
+
+}
+
+function _getCubemapMaterial() {
+
+ return new ShaderMaterial( {
+
+ name: 'CubemapToCubeUV',
+
+ uniforms: {
+ 'envMap': { value: null },
+ 'flipEnvMap': { value: - 1 }
+ },
+
+ vertexShader: _getCommonVertexShader(),
+
+ fragmentShader: /* glsl */`
+
+ precision mediump float;
+ precision mediump int;
+
+ uniform float flipEnvMap;
+
+ varying vec3 vOutputDirection;
+
+ uniform samplerCube envMap;
+
+ void main() {
+
+ gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) );
+
+ }
+ `,
+
+ blending: NoBlending,
+ depthTest: false,
+ depthWrite: false
+
+ } );
+
+}
+
+function _getCommonVertexShader() {
+
+ return /* glsl */`
+
+ precision mediump float;
+ precision mediump int;
+
+ 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 );
+
+ }
+ `;
+
+}
+
+export { PMREMGenerator };
diff --git a/libs/three/src/extras/ShapeUtils.js b/libs/three/src/extras/ShapeUtils.js
new file mode 100644
index 000000000..b1e090531
--- /dev/null
+++ b/libs/three/src/extras/ShapeUtils.js
@@ -0,0 +1,92 @@
+import { Earcut } from './Earcut.js';
+
+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 );
+
+ }
+
+}
+
+export { ShapeUtils };
diff --git a/libs/three/src/extras/core/Curve.js b/libs/three/src/extras/core/Curve.js
new file mode 100644
index 000000000..b1b1f46fb
--- /dev/null
+++ b/libs/three/src/extras/core/Curve.js
@@ -0,0 +1,416 @@
+import * as MathUtils from '../../math/MathUtils.js';
+import { Vector2 } from '../../math/Vector2.js';
+import { Vector3 } from '../../math/Vector3.js';
+import { Matrix4 } from '../../math/Matrix4.js';
+
+/**
+ * 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() );
+
+ }
+
+ // 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() {
+
+ return new this.constructor().copy( this );
+
+ }
+
+ copy( source ) {
+
+ this.arcLengthDivisions = source.arcLengthDivisions;
+
+ return this;
+
+ }
+
+ toJSON() {
+
+ const data = {
+ metadata: {
+ version: 4.6,
+ type: 'Curve',
+ generator: 'Curve.toJSON'
+ }
+ };
+
+ data.arcLengthDivisions = this.arcLengthDivisions;
+ data.type = this.type;
+
+ return data;
+
+ }
+
+ fromJSON( json ) {
+
+ this.arcLengthDivisions = json.arcLengthDivisions;
+
+ return this;
+
+ }
+
+}
+
+
+export { Curve };
diff --git a/libs/three/src/extras/core/CurvePath.js b/libs/three/src/extras/core/CurvePath.js
new file mode 100644
index 000000000..bc05ccc61
--- /dev/null
+++ b/libs/three/src/extras/core/CurvePath.js
@@ -0,0 +1,255 @@
+import { Curve } from './Curve.js';
+import * as Curves from '../curves/Curves.js';
+
+/**************************************************************
+ * 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 ) ) {
+
+ const lineType = ( startPoint.isVector2 === true ) ? 'LineCurve' : 'LineCurve3';
+ this.curves.push( new Curves[ lineType ]( endPoint, startPoint ) );
+
+ }
+
+ return this;
+
+ }
+
+ // 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, optionalTarget ) {
+
+ 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, optionalTarget );
+
+ }
+
+ 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;
+
+ }
+
+}
+
+
+export { CurvePath };
diff --git a/libs/three/src/extras/core/Interpolations.js b/libs/three/src/extras/core/Interpolations.js
new file mode 100644
index 000000000..a1e06c27d
--- /dev/null
+++ b/libs/three/src/extras/core/Interpolations.js
@@ -0,0 +1,79 @@
+/**
+ * Bezier Curves formulas obtained from
+ * https://en.wikipedia.org/wiki/B%C3%A9zier_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 );
+
+}
+
+export { CatmullRom, QuadraticBezier, CubicBezier };
diff --git a/libs/three/src/extras/core/Path.js b/libs/three/src/extras/core/Path.js
new file mode 100644
index 000000000..d0ccad8a5
--- /dev/null
+++ b/libs/three/src/extras/core/Path.js
@@ -0,0 +1,196 @@
+import { Vector2 } from '../../math/Vector2.js';
+import { CurvePath } from './CurvePath.js';
+import { EllipseCurve } from '../curves/EllipseCurve.js';
+import { SplineCurve } from '../curves/SplineCurve.js';
+import { CubicBezierCurve } from '../curves/CubicBezierCurve.js';
+import { QuadraticBezierCurve } from '../curves/QuadraticBezierCurve.js';
+import { LineCurve } from '../curves/LineCurve.js';
+
+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;
+
+ }
+
+}
+
+
+export { Path };
diff --git a/libs/three/src/extras/core/Shape.js b/libs/three/src/extras/core/Shape.js
new file mode 100644
index 000000000..334bb69f4
--- /dev/null
+++ b/libs/three/src/extras/core/Shape.js
@@ -0,0 +1,102 @@
+import { Path } from './Path.js';
+import * as MathUtils from '../../math/MathUtils.js';
+
+class Shape extends Path {
+
+ constructor( points ) {
+
+ super( points );
+
+ this.uuid = MathUtils.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;
+
+ }
+
+}
+
+
+export { Shape };
diff --git a/libs/three/src/extras/core/ShapePath.js b/libs/three/src/extras/core/ShapePath.js
new file mode 100644
index 000000000..036a6a566
--- /dev/null
+++ b/libs/three/src/extras/core/ShapePath.js
@@ -0,0 +1,291 @@
+import { Color } from '../../math/Color.js';
+import { Path } from './Path.js';
+import { Shape } from './Shape.js';
+import { ShapeUtils } from '../ShapeUtils.js';
+
+class ShapePath {
+
+ constructor() {
+
+ this.type = 'ShapePath';
+
+ this.color = new Color();
+
+ this.subPaths = [];
+ this.currentPath = null;
+
+ }
+
+ moveTo( x, y ) {
+
+ this.currentPath = new Path();
+ this.subPaths.push( this.currentPath );
+ this.currentPath.moveTo( x, y );
+
+ return this;
+
+ }
+
+ lineTo( x, y ) {
+
+ this.currentPath.lineTo( x, y );
+
+ return this;
+
+ }
+
+ quadraticCurveTo( aCPx, aCPy, aX, aY ) {
+
+ this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY );
+
+ return this;
+
+ }
+
+ bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {
+
+ this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY );
+
+ return this;
+
+ }
+
+ splineThru( pts ) {
+
+ this.currentPath.splineThru( pts );
+
+ return this;
+
+ }
+
+ toShapes( isCCW ) {
+
+ 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 [];
+
+ 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;
+ let toChange = 0;
+
+ 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 ++;
+
+ if ( hole_unassigned ) {
+
+ hole_unassigned = false;
+ betterShapeHoles[ s2Idx ].push( ho );
+
+ } else {
+
+ ambiguous = true;
+
+ }
+
+ }
+
+ }
+
+ if ( hole_unassigned ) {
+
+ betterShapeHoles[ sIdx ].push( ho );
+
+ }
+
+ }
+
+ }
+
+ if ( toChange > 0 && ambiguous === false ) {
+
+ 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;
+
+ }
+
+}
+
+
+export { ShapePath };
diff --git a/libs/three/src/extras/curves/ArcCurve.js b/libs/three/src/extras/curves/ArcCurve.js
new file mode 100644
index 000000000..edb84f463
--- /dev/null
+++ b/libs/three/src/extras/curves/ArcCurve.js
@@ -0,0 +1,17 @@
+import { EllipseCurve } from './EllipseCurve.js';
+
+class ArcCurve extends EllipseCurve {
+
+ constructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
+
+ super( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
+
+ this.isArcCurve = true;
+
+ this.type = 'ArcCurve';
+
+ }
+
+}
+
+export { ArcCurve };
diff --git a/libs/three/src/extras/curves/CatmullRomCurve3.js b/libs/three/src/extras/curves/CatmullRomCurve3.js
new file mode 100644
index 000000000..7b6c3ce4b
--- /dev/null
+++ b/libs/three/src/extras/curves/CatmullRomCurve3.js
@@ -0,0 +1,255 @@
+import { Vector3 } from '../../math/Vector3.js';
+import { Curve } from '../core/Curve.js';
+
+/**
+ * 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 = /*@__PURE__*/ new Vector3();
+const px = /*@__PURE__*/ new CubicPoly();
+const py = /*@__PURE__*/ new CubicPoly();
+const pz = /*@__PURE__*/ new CubicPoly();
+
+class CatmullRomCurve3 extends Curve {
+
+ constructor( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) {
+
+ super();
+
+ this.isCatmullRomCurve3 = true;
+
+ 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;
+
+ }
+
+}
+
+export { CatmullRomCurve3 };
diff --git a/libs/three/src/extras/curves/CubicBezierCurve.js b/libs/three/src/extras/curves/CubicBezierCurve.js
new file mode 100644
index 000000000..a2d45f53f
--- /dev/null
+++ b/libs/three/src/extras/curves/CubicBezierCurve.js
@@ -0,0 +1,78 @@
+import { Curve } from '../core/Curve.js';
+import { CubicBezier } from '../core/Interpolations.js';
+import { Vector2 } from '../../math/Vector2.js';
+
+class CubicBezierCurve extends Curve {
+
+ constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) {
+
+ super();
+
+ this.isCubicBezierCurve = true;
+
+ 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;
+
+ }
+
+}
+
+export { CubicBezierCurve };
diff --git a/libs/three/src/extras/curves/CubicBezierCurve3.js b/libs/three/src/extras/curves/CubicBezierCurve3.js
new file mode 100644
index 000000000..c5afdf26c
--- /dev/null
+++ b/libs/three/src/extras/curves/CubicBezierCurve3.js
@@ -0,0 +1,79 @@
+import { Curve } from '../core/Curve.js';
+import { CubicBezier } from '../core/Interpolations.js';
+import { Vector3 } from '../../math/Vector3.js';
+
+class CubicBezierCurve3 extends Curve {
+
+ constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) {
+
+ super();
+
+ this.isCubicBezierCurve3 = true;
+
+ 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;
+
+ }
+
+}
+
+export { CubicBezierCurve3 };
diff --git a/libs/three/src/extras/curves/Curves.js b/libs/three/src/extras/curves/Curves.js
new file mode 100644
index 000000000..c984a8539
--- /dev/null
+++ b/libs/three/src/extras/curves/Curves.js
@@ -0,0 +1,10 @@
+export { ArcCurve } from './ArcCurve.js';
+export { CatmullRomCurve3 } from './CatmullRomCurve3.js';
+export { CubicBezierCurve } from './CubicBezierCurve.js';
+export { CubicBezierCurve3 } from './CubicBezierCurve3.js';
+export { EllipseCurve } from './EllipseCurve.js';
+export { LineCurve } from './LineCurve.js';
+export { LineCurve3 } from './LineCurve3.js';
+export { QuadraticBezierCurve } from './QuadraticBezierCurve.js';
+export { QuadraticBezierCurve3 } from './QuadraticBezierCurve3.js';
+export { SplineCurve } from './SplineCurve.js';
diff --git a/libs/three/src/extras/curves/EllipseCurve.js b/libs/three/src/extras/curves/EllipseCurve.js
new file mode 100644
index 000000000..583364819
--- /dev/null
+++ b/libs/three/src/extras/curves/EllipseCurve.js
@@ -0,0 +1,156 @@
+import { Curve } from '../core/Curve.js';
+import { Vector2 } from '../../math/Vector2.js';
+
+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.isEllipseCurve = true;
+
+ 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;
+
+ }
+
+}
+
+export { EllipseCurve };
diff --git a/libs/three/src/extras/curves/LineCurve.js b/libs/three/src/extras/curves/LineCurve.js
new file mode 100644
index 000000000..0a636898c
--- /dev/null
+++ b/libs/three/src/extras/curves/LineCurve.js
@@ -0,0 +1,92 @@
+import { Vector2 } from '../../math/Vector2.js';
+import { Curve } from '../core/Curve.js';
+
+class LineCurve extends Curve {
+
+ constructor( v1 = new Vector2(), v2 = new Vector2() ) {
+
+ super();
+
+ this.isLineCurve = true;
+
+ 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 = new Vector2() ) {
+
+ return optionalTarget.subVectors( this.v2, this.v1 ).normalize();
+
+ }
+
+ getTangentAt( u, optionalTarget ) {
+
+ return this.getTangent( 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;
+
+ }
+
+}
+
+export { LineCurve };
diff --git a/libs/three/src/extras/curves/LineCurve3.js b/libs/three/src/extras/curves/LineCurve3.js
new file mode 100644
index 000000000..cc49b52a0
--- /dev/null
+++ b/libs/three/src/extras/curves/LineCurve3.js
@@ -0,0 +1,92 @@
+import { Vector3 } from '../../math/Vector3.js';
+import { Curve } from '../core/Curve.js';
+
+class LineCurve3 extends Curve {
+
+ constructor( v1 = new Vector3(), v2 = new Vector3() ) {
+
+ super();
+
+ this.isLineCurve3 = true;
+
+ this.type = 'LineCurve3';
+
+ 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 );
+
+ }
+
+ getTangent( t, optionalTarget = new Vector3() ) {
+
+ return optionalTarget.subVectors( this.v2, this.v1 ).normalize();
+
+ }
+
+ getTangentAt( u, optionalTarget ) {
+
+ return this.getTangent( 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;
+
+ }
+
+}
+
+export { LineCurve3 };
diff --git a/libs/three/src/extras/curves/QuadraticBezierCurve.js b/libs/three/src/extras/curves/QuadraticBezierCurve.js
new file mode 100644
index 000000000..6ed67d988
--- /dev/null
+++ b/libs/three/src/extras/curves/QuadraticBezierCurve.js
@@ -0,0 +1,74 @@
+import { Curve } from '../core/Curve.js';
+import { QuadraticBezier } from '../core/Interpolations.js';
+import { Vector2 } from '../../math/Vector2.js';
+
+class QuadraticBezierCurve extends Curve {
+
+ constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) {
+
+ super();
+
+ this.isQuadraticBezierCurve = true;
+
+ 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;
+
+ }
+
+}
+
+export { QuadraticBezierCurve };
diff --git a/libs/three/src/extras/curves/QuadraticBezierCurve3.js b/libs/three/src/extras/curves/QuadraticBezierCurve3.js
new file mode 100644
index 000000000..bdc882cc2
--- /dev/null
+++ b/libs/three/src/extras/curves/QuadraticBezierCurve3.js
@@ -0,0 +1,75 @@
+import { Curve } from '../core/Curve.js';
+import { QuadraticBezier } from '../core/Interpolations.js';
+import { Vector3 } from '../../math/Vector3.js';
+
+class QuadraticBezierCurve3 extends Curve {
+
+ constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) {
+
+ super();
+
+ this.isQuadraticBezierCurve3 = true;
+
+ 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;
+
+ }
+
+}
+
+export { QuadraticBezierCurve3 };
diff --git a/libs/three/src/extras/curves/SplineCurve.js b/libs/three/src/extras/curves/SplineCurve.js
new file mode 100644
index 000000000..073b9e46b
--- /dev/null
+++ b/libs/three/src/extras/curves/SplineCurve.js
@@ -0,0 +1,97 @@
+import { Curve } from '../core/Curve.js';
+import { CatmullRom } from '../core/Interpolations.js';
+import { Vector2 } from '../../math/Vector2.js';
+
+class SplineCurve extends Curve {
+
+ constructor( points = [] ) {
+
+ super();
+
+ this.isSplineCurve = true;
+
+ 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;
+
+ }
+
+}
+
+export { SplineCurve };
diff --git a/libs/three/src/geometries/BoxGeometry.js b/libs/three/src/geometries/BoxGeometry.js
new file mode 100644
index 000000000..486a3cd9d
--- /dev/null
+++ b/libs/three/src/geometries/BoxGeometry.js
@@ -0,0 +1,180 @@
+import { BufferGeometry } from '../core/BufferGeometry.js';
+import { Float32BufferAttribute } from '../core/BufferAttribute.js';
+import { Vector3 } from '../math/Vector3.js';
+
+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;
+
+ }
+
+ }
+
+ copy( source ) {
+
+ super.copy( source );
+
+ this.parameters = Object.assign( {}, source.parameters );
+
+ return this;
+
+ }
+
+ static fromJSON( data ) {
+
+ return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments );
+
+ }
+
+}
+
+export { BoxGeometry };
diff --git a/libs/three/src/geometries/CapsuleGeometry.js b/libs/three/src/geometries/CapsuleGeometry.js
new file mode 100644
index 000000000..15172e85a
--- /dev/null
+++ b/libs/three/src/geometries/CapsuleGeometry.js
@@ -0,0 +1,33 @@
+import { Path } from '../extras/core/Path.js';
+import { LatheGeometry } from './LatheGeometry.js';
+
+class CapsuleGeometry extends LatheGeometry {
+
+ constructor( radius = 1, length = 1, capSegments = 4, radialSegments = 8 ) {
+
+ const path = new Path();
+ path.absarc( 0, - length / 2, radius, Math.PI * 1.5, 0 );
+ path.absarc( 0, length / 2, radius, 0, Math.PI * 0.5 );
+
+ super( path.getPoints( capSegments ), radialSegments );
+
+ this.type = 'CapsuleGeometry';
+
+ this.parameters = {
+ radius: radius,
+ length: length,
+ capSegments: capSegments,
+ radialSegments: radialSegments,
+ };
+
+ }
+
+ static fromJSON( data ) {
+
+ return new CapsuleGeometry( data.radius, data.length, data.capSegments, data.radialSegments );
+
+ }
+
+}
+
+export { CapsuleGeometry };
diff --git a/libs/three/src/geometries/CircleGeometry.js b/libs/three/src/geometries/CircleGeometry.js
new file mode 100644
index 000000000..63e912126
--- /dev/null
+++ b/libs/three/src/geometries/CircleGeometry.js
@@ -0,0 +1,101 @@
+import { BufferGeometry } from '../core/BufferGeometry.js';
+import { Float32BufferAttribute } from '../core/BufferAttribute.js';
+import { Vector3 } from '../math/Vector3.js';
+import { Vector2 } from '../math/Vector2.js';
+
+class CircleGeometry extends BufferGeometry {
+
+ constructor( radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2 ) {
+
+ super();
+
+ this.type = 'CircleGeometry';
+
+ this.parameters = {
+ radius: radius,
+ segments: segments,
+ thetaStart: thetaStart,
+ thetaLength: thetaLength
+ };
+
+ segments = Math.max( 3, segments );
+
+ // 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 ) );
+
+ }
+
+ copy( source ) {
+
+ super.copy( source );
+
+ this.parameters = Object.assign( {}, source.parameters );
+
+ return this;
+
+ }
+
+ static fromJSON( data ) {
+
+ return new CircleGeometry( data.radius, data.segments, data.thetaStart, data.thetaLength );
+
+ }
+
+}
+
+
+export { CircleGeometry };
diff --git a/libs/three/src/geometries/ConeGeometry.js b/libs/three/src/geometries/ConeGeometry.js
new file mode 100644
index 000000000..912b388f6
--- /dev/null
+++ b/libs/three/src/geometries/ConeGeometry.js
@@ -0,0 +1,31 @@
+import { CylinderGeometry } from './CylinderGeometry.js';
+
+class ConeGeometry extends CylinderGeometry {
+
+ constructor( radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) {
+
+ 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
+ };
+
+ }
+
+ static fromJSON( data ) {
+
+ return new ConeGeometry( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength );
+
+ }
+
+}
+
+export { ConeGeometry };
diff --git a/libs/three/src/geometries/CylinderGeometry.js b/libs/three/src/geometries/CylinderGeometry.js
new file mode 100644
index 000000000..f27e14e7b
--- /dev/null
+++ b/libs/three/src/geometries/CylinderGeometry.js
@@ -0,0 +1,286 @@
+import { BufferGeometry } from '../core/BufferGeometry.js';
+import { Float32BufferAttribute } from '../core/BufferAttribute.js';
+import { Vector3 } from '../math/Vector3.js';
+import { Vector2 } from '../math/Vector2.js';
+
+class CylinderGeometry extends BufferGeometry {
+
+ constructor( radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) {
+
+ super();
+
+ this.type = 'CylinderGeometry';
+
+ this.parameters = {
+ radiusTop: radiusTop,
+ radiusBottom: radiusBottom,
+ height: height,
+ radialSegments: radialSegments,
+ heightSegments: heightSegments,
+ openEnded: openEnded,
+ thetaStart: thetaStart,
+ thetaLength: thetaLength
+ };
+
+ const scope = this;
+
+ radialSegments = Math.floor( radialSegments );
+ heightSegments = Math.floor( heightSegments );
+
+ // 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;
+
+ }
+
+ }
+
+ copy( source ) {
+
+ super.copy( source );
+
+ this.parameters = Object.assign( {}, source.parameters );
+
+ return this;
+
+ }
+
+ static fromJSON( data ) {
+
+ return new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength );
+
+ }
+
+}
+
+
+export { CylinderGeometry };
diff --git a/libs/three/src/geometries/DodecahedronGeometry.js b/libs/three/src/geometries/DodecahedronGeometry.js
new file mode 100644
index 000000000..3434df937
--- /dev/null
+++ b/libs/three/src/geometries/DodecahedronGeometry.js
@@ -0,0 +1,66 @@
+import { PolyhedronGeometry } from './PolyhedronGeometry.js';
+
+class DodecahedronGeometry extends PolyhedronGeometry {
+
+ constructor( radius = 1, detail = 0 ) {
+
+ 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 = 'DodecahedronGeometry';
+
+ this.parameters = {
+ radius: radius,
+ detail: detail
+ };
+
+ }
+
+ static fromJSON( data ) {
+
+ return new DodecahedronGeometry( data.radius, data.detail );
+
+ }
+
+}
+
+
+export { DodecahedronGeometry };
diff --git a/libs/three/src/geometries/EdgesGeometry.js b/libs/three/src/geometries/EdgesGeometry.js
new file mode 100644
index 000000000..795a7c1cc
--- /dev/null
+++ b/libs/three/src/geometries/EdgesGeometry.js
@@ -0,0 +1,152 @@
+import { BufferGeometry } from '../core/BufferGeometry.js';
+import { Float32BufferAttribute } from '../core/BufferAttribute.js';
+import * as MathUtils from '../math/MathUtils.js';
+import { Triangle } from '../math/Triangle.js';
+import { Vector3 } from '../math/Vector3.js';
+
+const _v0 = /*@__PURE__*/ new Vector3();
+const _v1 = /*@__PURE__*/ new Vector3();
+const _normal = /*@__PURE__*/ new Vector3();
+const _triangle = /*@__PURE__*/ new Triangle();
+
+class EdgesGeometry extends BufferGeometry {
+
+ constructor( geometry = null, thresholdAngle = 1 ) {
+
+ super();
+
+ this.type = 'EdgesGeometry';
+
+ this.parameters = {
+ geometry: geometry,
+ thresholdAngle: thresholdAngle
+ };
+
+ if ( geometry !== null ) {
+
+ const precisionPoints = 4;
+ const precision = Math.pow( 10, precisionPoints );
+ const thresholdDot = Math.cos( MathUtils.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.fromBufferAttribute( positionAttr, index1 );
+
+ vertices.push( _v0.x, _v0.y, _v0.z );
+ vertices.push( _v1.x, _v1.y, _v1.z );
+
+ }
+
+ }
+
+ this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
+
+ }
+
+ }
+
+ copy( source ) {
+
+ super.copy( source );
+
+ this.parameters = Object.assign( {}, source.parameters );
+
+ return this;
+
+ }
+
+}
+
+export { EdgesGeometry };
diff --git a/libs/three/src/geometries/ExtrudeGeometry.js b/libs/three/src/geometries/ExtrudeGeometry.js
new file mode 100644
index 000000000..a27b67da8
--- /dev/null
+++ b/libs/three/src/geometries/ExtrudeGeometry.js
@@ -0,0 +1,814 @@
+/**
+ * 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: