jbilcke-hf HF staff commited on
Commit
a28eca3
·
verified ·
1 Parent(s): 48f9320

Upload 372 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. index.html +740 -18
  2. libs/gradio-client.js/1.10.0/gradio-client.js +8 -0
  3. libs/three.js/0.172.0/jsm/Addons.js +287 -0
  4. libs/three.js/0.172.0/jsm/animation/AnimationClipCreator.js +116 -0
  5. libs/three.js/0.172.0/jsm/animation/CCDIKSolver.js +485 -0
  6. libs/three.js/0.172.0/jsm/capabilities/WebGL.js +114 -0
  7. libs/three.js/0.172.0/jsm/capabilities/WebGPU.js +48 -0
  8. libs/three.js/0.172.0/jsm/controls/ArcballControls.js +3253 -0
  9. libs/three.js/0.172.0/jsm/controls/DragControls.js +410 -0
  10. libs/three.js/0.172.0/jsm/controls/FirstPersonControls.js +337 -0
  11. libs/three.js/0.172.0/jsm/controls/FlyControls.js +332 -0
  12. libs/three.js/0.172.0/jsm/controls/MapControls.js +28 -0
  13. libs/three.js/0.172.0/jsm/controls/OrbitControls.js +1556 -0
  14. libs/three.js/0.172.0/jsm/controls/PointerLockControls.js +168 -0
  15. libs/three.js/0.172.0/jsm/controls/TrackballControls.js +849 -0
  16. libs/three.js/0.172.0/jsm/controls/TransformControls.js +1624 -0
  17. libs/three.js/0.172.0/jsm/csm/CSM.js +384 -0
  18. libs/three.js/0.172.0/jsm/csm/CSMFrustum.js +155 -0
  19. libs/three.js/0.172.0/jsm/csm/CSMHelper.js +195 -0
  20. libs/three.js/0.172.0/jsm/csm/CSMShader.js +295 -0
  21. libs/three.js/0.172.0/jsm/csm/CSMShadowNode.js +442 -0
  22. libs/three.js/0.172.0/jsm/curves/CurveExtras.js +422 -0
  23. libs/three.js/0.172.0/jsm/curves/NURBSCurve.js +111 -0
  24. libs/three.js/0.172.0/jsm/curves/NURBSSurface.js +52 -0
  25. libs/three.js/0.172.0/jsm/curves/NURBSUtils.js +545 -0
  26. libs/three.js/0.172.0/jsm/curves/NURBSVolume.js +62 -0
  27. libs/three.js/0.172.0/jsm/effects/AnaglyphEffect.js +147 -0
  28. libs/three.js/0.172.0/jsm/effects/AsciiEffect.js +263 -0
  29. libs/three.js/0.172.0/jsm/effects/OutlineEffect.js +539 -0
  30. libs/three.js/0.172.0/jsm/effects/ParallaxBarrierEffect.js +125 -0
  31. libs/three.js/0.172.0/jsm/effects/PeppersGhostEffect.js +153 -0
  32. libs/three.js/0.172.0/jsm/effects/StereoEffect.js +60 -0
  33. libs/three.js/0.172.0/jsm/environments/DebugEnvironment.js +52 -0
  34. libs/three.js/0.172.0/jsm/environments/RoomEnvironment.js +144 -0
  35. libs/three.js/0.172.0/jsm/exporters/DRACOExporter.js +269 -0
  36. libs/three.js/0.172.0/jsm/exporters/EXRExporter.js +587 -0
  37. libs/three.js/0.172.0/jsm/exporters/GLTFExporter.js +3460 -0
  38. libs/three.js/0.172.0/jsm/exporters/KTX2Exporter.js +323 -0
  39. libs/three.js/0.172.0/jsm/exporters/OBJExporter.js +288 -0
  40. libs/three.js/0.172.0/jsm/exporters/PLYExporter.js +530 -0
  41. libs/three.js/0.172.0/jsm/exporters/STLExporter.js +199 -0
  42. libs/three.js/0.172.0/jsm/exporters/USDZExporter.js +782 -0
  43. libs/three.js/0.172.0/jsm/geometries/BoxLineGeometry.js +69 -0
  44. libs/three.js/0.172.0/jsm/geometries/ConvexGeometry.js +53 -0
  45. libs/three.js/0.172.0/jsm/geometries/DecalGeometry.js +408 -0
  46. libs/three.js/0.172.0/jsm/geometries/ParametricGeometries.js +254 -0
  47. libs/three.js/0.172.0/jsm/geometries/ParametricGeometry.js +139 -0
  48. libs/three.js/0.172.0/jsm/geometries/RoundedBoxGeometry.js +155 -0
  49. libs/three.js/0.172.0/jsm/geometries/TeapotGeometry.js +704 -0
  50. libs/three.js/0.172.0/jsm/geometries/TextGeometry.js +54 -0
index.html CHANGED
@@ -1,19 +1,741 @@
1
- <!doctype html>
2
  <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
  <html>
3
+ <head>
4
+ <title>Text to Terrain Generator</title>
5
+ <style>
6
+ body { margin: 0; }
7
+ canvas { display: block; }
8
+ #prompt-input {
9
+ position: absolute;
10
+ top: 10px;
11
+ left: 10px;
12
+ padding: 8px;
13
+ width: 200px;
14
+ border: 1px solid #ccc;
15
+ border-radius: 4px;
16
+ font-size: 14px;
17
+ }
18
+ #camera-toggle {
19
+ position: absolute;
20
+ top: 10px;
21
+ right: 10px;
22
+ padding: 8px 16px;
23
+ background-color: #3498db;
24
+ color: white;
25
+ border: none;
26
+ border-radius: 4px;
27
+ cursor: pointer;
28
+ font-size: 14px;
29
+ transition: background-color 0.3s;
30
+ }
31
+ #camera-toggle:hover {
32
+ background-color: #2980b9;
33
+ }
34
+ #heightmap {
35
+ display: none;
36
+ }
37
+ #loadingOverlay {
38
+ display: none;
39
+ position: fixed;
40
+ top: 0;
41
+ left: 0;
42
+ width: 100%;
43
+ height: 100%;
44
+ background-color: rgba(0, 0, 0, 0.7);
45
+ z-index: 9999;
46
+ justify-content: center;
47
+ align-items: center;
48
+ }
49
+ .loader {
50
+ border: 5px solid #f3f3f3;
51
+ border-top: 5px solid #3498db;
52
+ border-radius: 50%;
53
+ width: 50px;
54
+ height: 50px;
55
+ animation: spin 1s linear infinite;
56
+ }
57
+ @keyframes spin {
58
+ 0% { transform: rotate(0deg); }
59
+ 100% { transform: rotate(360deg); }
60
+ }
61
+ </style>
62
+ </head>
63
+ <body>
64
+ <input type="text" id="prompt-input" placeholder="Countryside house with trees..">
65
+ <button id="camera-toggle">Toggle Camera</button>
66
+ <div id="loadingOverlay">
67
+ <div class="loader"></div>
68
+ </div>
69
+ <canvas id="heightmap" width="256" height="256"></canvas>
70
+ <script type="importmap">
71
+ {
72
+ "imports": {
73
+ "three": "/libs/three.js/0.172.0/three.module.min.js",
74
+ "three/addons/": "/libs/three.js/0.172.0/jsm/",
75
+ "@gradio/client": "/libs/gradio-client.js/1.10.0/gradio-client.js"
76
+ }
77
+ }
78
+ </script>
79
+ <script type="module">
80
+ import * as THREE from 'three';
81
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
82
+ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
83
+ import { Client } from "@gradio/client";
84
+
85
+
86
+ let isLoading = false;
87
+ let lastPromptInputValue = "";
88
+ let isDragging = false;
89
+ let mouseDownPosition = new THREE.Vector2();
90
+ const objectCountByType = new Map();
91
+ let terrainInstances;
92
+ let objectInstances = new Map();
93
+ const TILE_SIZE = 4;
94
+ const GRID_SIZE = 10;
95
+ let currentTileType = null;
96
+ let hoveredTileIndex = -1;
97
+ const raycaster = new THREE.Raycaster();
98
+ const mouse = new THREE.Vector2();
99
+ let highlightMesh = null;
100
+ const tileTypes = new Map();
101
+ const gltfLoader = new GLTFLoader();
102
+
103
+ // Scene setup
104
+ const scene = new THREE.Scene();
105
+ scene.background = new THREE.Color(0x87CEEB);
106
+
107
+ // Camera setup with both perspectives
108
+ const frustumSize = 40;
109
+ const aspect = window.innerWidth / window.innerHeight;
110
+
111
+ const orthoCamera = new THREE.OrthographicCamera(
112
+ -20, 20, 20, -20,
113
+ 0.1, 2000
114
+ );
115
+ orthoCamera.position.set(20, 20, -20);
116
+ orthoCamera.lookAt(0, 0, 0);
117
+
118
+ const perspCamera = new THREE.PerspectiveCamera(
119
+ 75, window.innerWidth / window.innerHeight, 0.1, 2000
120
+ );
121
+ perspCamera.position.set(20, 20, -20);
122
+ perspCamera.lookAt(0, 0, 0);
123
+
124
+ let currentCamera = orthoCamera;
125
+ let isOrthographic = true;
126
+
127
+ // Renderer setup
128
+ const renderer = new THREE.WebGLRenderer({ antialias: true });
129
+ renderer.setSize(window.innerWidth, window.innerHeight);
130
+ renderer.shadowMap.enabled = true;
131
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
132
+ document.body.appendChild(renderer.domElement);
133
+
134
+ // Controls
135
+ let controls = new OrbitControls(currentCamera, renderer.domElement);
136
+ controls.enableDamping = true;
137
+
138
+ // Lighting
139
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 4.5); // Increased from 1.5 to 4.5
140
+ directionalLight.position.set(20, 30, 20);
141
+ directionalLight.castShadow = true;
142
+
143
+ // Improve shadow quality
144
+ directionalLight.shadow.mapSize.width = 2048;
145
+ directionalLight.shadow.mapSize.height = 2048;
146
+ directionalLight.shadow.camera.near = 0.1;
147
+ directionalLight.shadow.camera.far = 100;
148
+ directionalLight.shadow.camera.left = -30;
149
+ directionalLight.shadow.camera.right = 30;
150
+ directionalLight.shadow.camera.top = 30;
151
+ directionalLight.shadow.camera.bottom = -30;
152
+ directionalLight.shadow.bias = -0.001;
153
+
154
+ // Add a brighter ambient light
155
+ scene.add(new THREE.AmbientLight(0x404040, 1.2));
156
+
157
+ // Add lighter fog
158
+ scene.fog = new THREE.FogExp2(0x87CEEB, 0.015);
159
+
160
+ // Enable shadow mapping in the renderer
161
+ renderer.shadowMap.enabled = true;
162
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
163
+
164
+ scene.add(directionalLight);
165
+
166
+
167
+ // Height map generation
168
+ const canvasH = document.getElementById('heightmap');
169
+ const ctx = canvasH.getContext('2d');
170
+ const heightMap = new THREE.CanvasTexture(canvasH);
171
+
172
+ function createSeamlessNoise(size, octaves) {
173
+ const noise = new Array(size * size).fill(0);
174
+
175
+ function smoothStep(t) {
176
+ return t * t * (3 - 2 * t);
177
+ }
178
+
179
+ function interpolate(a, b, t) {
180
+ return a + (b - a) * smoothStep(t);
181
+ }
182
+
183
+ for (let octave = 0; octave < octaves; octave++) {
184
+ const frequency = 1 << octave;
185
+ const amplitude = 1 / (1 << octave);
186
+
187
+ const grid = new Array((frequency + 1) * (frequency + 1));
188
+ for (let i = 0; i <= frequency; i++) {
189
+ for (let j = 0; j <= frequency; j++) {
190
+ grid[i * (frequency + 1) + j] = Math.random();
191
+ }
192
+ }
193
+
194
+ for (let i = 0; i <= frequency; i++) {
195
+ grid[i * (frequency + 1) + frequency] = grid[i * (frequency + 1)];
196
+ }
197
+ for (let j = 0; j <= frequency; j++) {
198
+ grid[frequency * (frequency + 1) + j] = grid[j];
199
+ }
200
+ grid[frequency * (frequency + 1) + frequency] = grid[0];
201
+
202
+ const cellSize = size / frequency;
203
+ for (let y = 0; y < size; y++) {
204
+ for (let x = 0; x < size; x++) {
205
+ const gridX = Math.floor(x / cellSize);
206
+ const gridY = Math.floor(y / cellSize);
207
+ const fracX = (x % cellSize) / cellSize;
208
+ const fracY = (y % cellSize) / cellSize;
209
+
210
+ const v1 = grid[gridY * (frequency + 1) + gridX];
211
+ const v2 = grid[gridY * (frequency + 1) + (gridX + 1)];
212
+ const v3 = grid[(gridY + 1) * (frequency + 1) + gridX];
213
+ const v4 = grid[(gridY + 1) * (frequency + 1) + (gridX + 1)];
214
+
215
+ const i1 = interpolate(v1, v2, fracX);
216
+ const i2 = interpolate(v3, v4, fracX);
217
+ const value = interpolate(i1, i2, fracY);
218
+
219
+ noise[y * size + x] += value * amplitude;
220
+ }
221
+ }
222
+ }
223
+
224
+ return noise;
225
+ }
226
+
227
+ function createHeightMap() {
228
+ const size = 256;
229
+ const noise = createSeamlessNoise(size, 8);
230
+
231
+ const imageData = ctx.createImageData(size, size);
232
+ let min = Infinity, max = -Infinity;
233
+
234
+ for (let i = 0; i < noise.length; i++) {
235
+ min = Math.min(min, noise[i]);
236
+ max = Math.max(max, noise[i]);
237
+ }
238
+
239
+ for (let i = 0; i < noise.length; i++) {
240
+ const normalized = Math.floor(((noise[i] - min) / (max - min)) * 255);
241
+ imageData.data[i * 4] = normalized;
242
+ imageData.data[i * 4 + 1] = normalized;
243
+ imageData.data[i * 4 + 2] = normalized;
244
+ imageData.data[i * 4 + 3] = 255;
245
+ }
246
+
247
+ ctx.putImageData(imageData, 0, 0);
248
+ heightMap.needsUpdate = true;
249
+
250
+ if (terrainInstances?.material) {
251
+ terrainInstances.material.displacementMap = heightMap;
252
+ terrainInstances.material.needsUpdate = true;
253
+ }
254
+ }
255
+
256
+ // Create base terrain type
257
+ function createBaseTileType() {
258
+ const geometry = new THREE.PlaneGeometry(TILE_SIZE, TILE_SIZE, 64, 64);
259
+ const material = new THREE.MeshStandardMaterial({
260
+ color: 0x46732a,
261
+ displacementMap: heightMap,
262
+ displacementScale: 0.8,
263
+ flatShading: false,
264
+ side: THREE.DoubleSide,
265
+ shadowSide: THREE.DoubleSide
266
+ });
267
+
268
+ tileTypes.set('base', { geometry, material });
269
+ }
270
+
271
+ function createHighlightMesh() {
272
+ const geometry = new THREE.PlaneGeometry(TILE_SIZE, TILE_SIZE);
273
+ const edges = new THREE.EdgesGeometry(geometry);
274
+ const material = new THREE.LineBasicMaterial({
275
+ color: 0xffff00,
276
+ linewidth: 2,
277
+ transparent: true,
278
+ opacity: 0.9
279
+ });
280
+
281
+ highlightMesh = new THREE.LineSegments(edges, material);
282
+ highlightMesh.rotation.x = -Math.PI / 2;
283
+ highlightMesh.visible = false;
284
+ scene.add(highlightMesh);
285
+ }
286
+
287
+ function initializeTerrain() {
288
+ createBaseTileType();
289
+ createHighlightMesh();
290
+
291
+ const baseTile = tileTypes.get('base');
292
+ const instanceCount = GRID_SIZE * GRID_SIZE;
293
+ terrainInstances = new THREE.InstancedMesh(
294
+ baseTile.geometry,
295
+ baseTile.material,
296
+ instanceCount
297
+ );
298
+ terrainInstances.castShadow = true;
299
+ terrainInstances.receiveShadow = true;
300
+
301
+ const matrix = new THREE.Matrix4();
302
+ const position = new THREE.Vector3();
303
+ const rotation = new THREE.Euler();
304
+ const quaternion = new THREE.Quaternion();
305
+ const scale = new THREE.Vector3(1, 1, 1);
306
+
307
+ let index = 0;
308
+ const offset = (GRID_SIZE * TILE_SIZE) / 2 - TILE_SIZE / 2;
309
+
310
+ for (let x = 0; x < GRID_SIZE; x++) {
311
+ for (let z = 0; z < GRID_SIZE; z++) {
312
+ position.set(
313
+ x * TILE_SIZE - offset,
314
+ 0,
315
+ z * TILE_SIZE - offset
316
+ );
317
+ rotation.set(-Math.PI / 2, 0, 0);
318
+ quaternion.setFromEuler(rotation);
319
+
320
+ matrix.compose(position, quaternion, scale);
321
+ terrainInstances.setMatrixAt(index, matrix);
322
+ index++;
323
+ }
324
+ }
325
+
326
+ scene.add(terrainInstances);
327
+ }
328
+
329
+ async function createGLBTileType(modelUrl, typeName) {
330
+ return new Promise((resolve, reject) => {
331
+ gltfLoader.load(modelUrl, (gltf) => {
332
+ const model = gltf.scene;
333
+
334
+ const box = new THREE.Box3().setFromObject(model);
335
+ const size = box.getSize(new THREE.Vector3());
336
+
337
+ const geometry = new THREE.BoxGeometry(1, 1, 1);
338
+
339
+ const material = new THREE.MeshStandardMaterial({ visible: false });
340
+
341
+ tileTypes.set(typeName, {
342
+ geometry,
343
+ material,
344
+ model: model.clone()
345
+ });
346
+
347
+ resolve();
348
+ }, undefined, reject);
349
+ });
350
+ }
351
+
352
+
353
+ // First, create a function to find the Y position of the widest cross-section
354
+ function findWidestCrossSection(mesh) {
355
+ // Track the maximum width/depth we find and its Y position
356
+ let maxArea = 0;
357
+ let maxAreaY = 0;
358
+
359
+ // Get all vertices from the mesh and its children
360
+ const vertices = [];
361
+ mesh.traverse((child) => {
362
+ if (child.isMesh && child.geometry) {
363
+ const positions = child.geometry.attributes.position;
364
+ const vertexCount = positions.count;
365
+
366
+ // Transform vertices to world space
367
+ const matrix = child.matrixWorld;
368
+ for (let i = 0; i < vertexCount; i++) {
369
+ const vertex = new THREE.Vector3();
370
+ vertex.fromBufferAttribute(positions, i);
371
+ vertex.applyMatrix4(matrix);
372
+ vertices.push(vertex);
373
+ }
374
+ }
375
+ });
376
+
377
+ if (vertices.length === 0) return 0;
378
+
379
+ // Find Y range to analyze
380
+ const yValues = vertices.map(v => v.y);
381
+ const minY = Math.min(...yValues);
382
+ const maxY = Math.max(...yValues);
383
+
384
+ // Sample Y positions at regular intervals
385
+ const steps = 128; // Number of cross-sections to check
386
+ const yStep = (maxY - minY) / steps;
387
+
388
+ for (let i = 0; i <= steps; i++) {
389
+ const currentY = minY + (i * yStep);
390
+
391
+ // Find vertices near this Y level (within small threshold)
392
+ const threshold = yStep / 2;
393
+ const sectionVertices = vertices.filter(v =>
394
+ Math.abs(v.y - currentY) < threshold
395
+ );
396
+
397
+ if (sectionVertices.length > 0) {
398
+ // Calculate bounding area of this cross-section
399
+ const xValues = sectionVertices.map(v => v.x);
400
+ const zValues = sectionVertices.map(v => v.z);
401
+
402
+ const width = Math.max(...xValues) - Math.min(...xValues);
403
+ const depth = Math.max(...zValues) - Math.min(...zValues);
404
+ const area = width * depth;
405
+
406
+ if (area > maxArea) {
407
+ maxArea = area;
408
+ maxAreaY = currentY;
409
+ }
410
+ }
411
+ }
412
+
413
+ return maxAreaY;
414
+ }
415
+
416
+
417
+ function replaceTileInstance(instanceIndex, newTypeName) {
418
+ const newType = tileTypes.get(newTypeName);
419
+ if (!newType) return;
420
+
421
+ // Get original transformation matrix from terrain instance
422
+ const matrix = new THREE.Matrix4();
423
+ terrainInstances.getMatrixAt(instanceIndex, matrix);
424
+
425
+ // Extract position from matrix
426
+ const position = new THREE.Vector3();
427
+ const rotation = new THREE.Quaternion();
428
+ const scale = new THREE.Vector3();
429
+ matrix.decompose(position, rotation, scale);
430
+
431
+ // Check if there's an existing object of the same type
432
+ if (objectInstances.has(instanceIndex)) {
433
+ const existing = objectInstances.get(instanceIndex);
434
+ if (existing.typeName === newTypeName) {
435
+ // Rotate existing object's rotation group by 90 degrees
436
+ const currentRotationY = (existing.rotationY || 0) + Math.PI / 2;
437
+ const normalizedRotation = currentRotationY % (Math.PI * 2);
438
+
439
+ existing.rotationGroup.rotation.y = (Math.PI / 4) + normalizedRotation;
440
+ existing.rotationY = normalizedRotation;
441
+ return; // Exit early as we just rotated the existing object
442
+ } else {
443
+ // Remove existing object if it's a different type
444
+ scene.remove(existing.rotationGroup);
445
+ }
446
+ }
447
+
448
+ // Create new mesh from model
449
+ const newMesh = newType.model.clone();
450
+
451
+ // Create hierarchy of groups for different transformations
452
+ const rotationGroup = new THREE.Group(); // Handles user-controlled Y rotation
453
+ const orientationGroup = new THREE.Group(); // Handles initial orientation
454
+
455
+ // Build hierarchy
456
+ scene.add(rotationGroup);
457
+ rotationGroup.add(orientationGroup);
458
+ orientationGroup.add(newMesh);
459
+
460
+ // Position the top-level group at the tile location
461
+ rotationGroup.position.copy(position);
462
+
463
+ // Enable shadows
464
+ newMesh.traverse((child) => {
465
+ if (child.isMesh) {
466
+ child.castShadow = true;
467
+ child.receiveShadow = true;
468
+ }
469
+ if (child.material) {
470
+ child.material.shadowSide = THREE.DoubleSide;
471
+ child.material.needsUpdate = true;
472
+ }
473
+ });
474
+
475
+
476
+ // Create a bounding box to calculate the mesh's dimensions
477
+ const bbox = new THREE.Box3().setFromObject(newMesh);
478
+ const meshCenter = bbox.getCenter(new THREE.Vector3());
479
+
480
+ // Center the mesh on its local origin
481
+ newMesh.position.sub(meshCenter);
482
+
483
+ // Apply initial orientation using clean, separate rotations
484
+ orientationGroup.rotation.x = 0.6; // fix mesh generation tiling
485
+ orientationGroup.rotation.y = 0;
486
+ orientationGroup.rotation.z = 0;
487
+
488
+ // Find the Y position of widest cross-section and adjust position
489
+ const baseY = findWidestCrossSection(newMesh);
490
+ newMesh.position.y -= baseY;
491
+ newMesh.position.y += 1.2;
492
+
493
+ rotationGroup.rotation.y = Math.PI / 4;
494
+
495
+ // Apply scale to the mesh
496
+ newMesh.scale.set(5.6, 5.6, 5.6);
497
+
498
+ // Adjust XZ position to ensure centering after rotation
499
+ bbox.setFromObject(newMesh);
500
+ const rotatedCenter = bbox.getCenter(new THREE.Vector3());
501
+ //newMesh.position.x -= 1.0;//rotatedCenter.x;
502
+ //newMesh.position.z -= 0;//rotatedCenter.z;
503
+
504
+
505
+ // Add new object to scene and track it with metadata
506
+ objectInstances.set(instanceIndex, {
507
+ rotationGroup: rotationGroup,
508
+ orientationGroup: orientationGroup,
509
+ mesh: newMesh,
510
+ typeName: newTypeName,
511
+ rotationY: 0 // Start with 0 rotation for the group
512
+ });
513
+
514
+ const currentCount = objectCountByType.get(newTypeName) || 0;
515
+ objectCountByType.set(newTypeName, currentCount + 1);
516
+ }
517
+
518
+ let gradioClient = null;
519
+
520
+ async function initializeGradioClient() {
521
+ try {
522
+ gradioClient = await Client.connect("jbilcke-hf/text-to-3d");
523
+ } catch (error) {
524
+ console.error("Failed to connect to Gradio:", error);
525
+ }
526
+ }
527
+
528
+ async function generateTile(prompt) {
529
+ if (!gradioClient) {
530
+ await initializeGradioClient();
531
+ }
532
+
533
+ if (!gradioClient) {
534
+ console.error("Gradio client not initialized");
535
+ return;
536
+ }
537
+
538
+ if (prompt === lastPromptInputValue) {
539
+ return;
540
+ }
541
+
542
+ lastPromptInputValue = prompt;
543
+
544
+ try {
545
+ isLoading = true;
546
+ loadingOverlay.style.display = 'flex';
547
+
548
+
549
+ const result = await gradioClient.predict("/generate", {
550
+ prompt: `isometric videogame asset, ${prompt.trim()}`,
551
+ });
552
+
553
+ const modelUrl = result.data[0].url;
554
+
555
+
556
+ //const modelUrl = "/models/eiffel_tower.glb";
557
+ const tileTypeName = `custom_${Date.now()}`;
558
+ await createGLBTileType(modelUrl, tileTypeName);
559
+
560
+ // Instead of random placement, store the current tile type
561
+ currentTileType = tileTypeName;
562
+
563
+ // Optional: place one instance to show the new type
564
+ replaceTileInstance(Math.floor(GRID_SIZE * GRID_SIZE / 2), tileTypeName);
565
+
566
+ } catch (error) {
567
+ console.error("Failed to generate/load terrain:", error);
568
+ } finally {
569
+ isLoading = false;
570
+ loadingOverlay.style.display = 'none';
571
+ }
572
+ }
573
+
574
+
575
+ // Handle window resize
576
+ window.addEventListener('resize', () => {
577
+ const aspect = window.innerWidth / window.innerHeight;
578
+ const frustumSize = 40;
579
+ camera.left = -frustumSize * aspect / 2;
580
+ camera.right = frustumSize * aspect / 2;
581
+ camera.top = frustumSize / 2;
582
+ camera.bottom = -frustumSize / 2;
583
+ camera.updateProjectionMatrix();
584
+ renderer.setSize(window.innerWidth, window.innerHeight);
585
+ });
586
+
587
+ // Text input handling
588
+ const promptInput = document.getElementById('prompt-input');
589
+
590
+ // Event listeners
591
+ promptInput.addEventListener('keypress', async (e) => {
592
+ if (e.key === 'Enter') {
593
+ await generateTile(promptInput.value);
594
+ }
595
+ });
596
+
597
+ promptInput.addEventListener('blur', async () => {
598
+ await generateTile(promptInput.value);
599
+ });
600
+
601
+ canvasH.addEventListener('click', createHeightMap);
602
+
603
+ // Initialize and start
604
+ createHeightMap();
605
+ initializeTerrain();
606
+
607
+
608
+ function onMouseDown(event) {
609
+ if (event.button === 0) { // Left click
610
+ mouseDownPosition.x = event.clientX;
611
+ mouseDownPosition.y = event.clientY;
612
+ isDragging = false;
613
+ }
614
+ }
615
+
616
+
617
+
618
+ // Update raycasting to use current camera
619
+ function onMouseMove(event) {
620
+ if (event.buttons === 1) {
621
+ const deltaX = Math.abs(event.clientX - mouseDownPosition.x);
622
+ const deltaY = Math.abs(event.clientY - mouseDownPosition.y);
623
+ if (deltaX > 5 || deltaY > 5) {
624
+ isDragging = true;
625
+ }
626
+ }
627
+
628
+ mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
629
+ mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
630
+
631
+ raycaster.setFromCamera(mouse, currentCamera);
632
+ const intersects = raycaster.intersectObject(terrainInstances);
633
+
634
+ if (intersects.length > 0) {
635
+ const instanceId = intersects[0].instanceId;
636
+
637
+ if (instanceId !== hoveredTileIndex) {
638
+ hoveredTileIndex = instanceId;
639
+
640
+ const matrix = new THREE.Matrix4();
641
+ terrainInstances.getMatrixAt(instanceId, matrix);
642
+ const position = new THREE.Vector3();
643
+ matrix.decompose(position, new THREE.Quaternion(), new THREE.Vector3());
644
+
645
+ highlightMesh.position.set(position.x, 0.4, position.z);
646
+ highlightMesh.visible = true;
647
+ }
648
+ } else {
649
+ hoveredTileIndex = -1;
650
+ highlightMesh.visible = false;
651
+ }
652
+ }
653
+
654
+ function onMouseUp(event) {
655
+ if (event.button === 0) { // Left click
656
+ if (!isDragging && hoveredTileIndex !== -1 && currentTileType) {
657
+ replaceTileInstance(hoveredTileIndex, currentTileType);
658
+ }
659
+ }
660
+ }
661
+
662
+ function onClick(event) {
663
+ if (event.button === 2) { // Right click
664
+ if (hoveredTileIndex !== -1 && objectInstances.has(hoveredTileIndex)) {
665
+ const existing = objectInstances.get(hoveredTileIndex);
666
+ const typeName = existing.typeName;
667
+ if (typeName) {
668
+ const currentCount = objectCountByType.get(typeName) || 0;
669
+ if (currentCount > 1) {
670
+ objectCountByType.set(typeName, currentCount - 1);
671
+ } else {
672
+ objectCountByType.delete(typeName);
673
+ }
674
+ }
675
+ scene.remove(existing.rotationGroup);
676
+ objectInstances.delete(hoveredTileIndex);
677
+ }
678
+ }
679
+ }
680
+
681
+
682
+ // Add camera toggle button handler
683
+ const cameraToggle = document.getElementById('camera-toggle');
684
+ cameraToggle.addEventListener('click', () => {
685
+ isOrthographic = !isOrthographic;
686
+ currentCamera = isOrthographic ? orthoCamera : perspCamera;
687
+
688
+ // Update controls
689
+ controls.dispose();
690
+ controls = new OrbitControls(currentCamera, renderer.domElement);
691
+ controls.enableDamping = true;
692
+
693
+ // Sync camera positions
694
+ const oldPos = isOrthographic ? perspCamera.position : orthoCamera.position;
695
+ currentCamera.position.copy(oldPos);
696
+ currentCamera.lookAt(controls.target);
697
+
698
+ // Update projection if needed
699
+ if (!isOrthographic) {
700
+ perspCamera.aspect = window.innerWidth / window.innerHeight;
701
+ perspCamera.updateProjectionMatrix();
702
+ }
703
+ });
704
+
705
+ // Update window resize handler
706
+ window.addEventListener('resize', () => {
707
+ const aspect = window.innerWidth / window.innerHeight;
708
+ if (isOrthographic) {
709
+ const frustumSize = 40;
710
+ orthoCamera.left = -frustumSize * aspect / 2;
711
+ orthoCamera.right = frustumSize * aspect / 2;
712
+ orthoCamera.top = frustumSize / 2;
713
+ orthoCamera.bottom = -frustumSize / 2;
714
+ orthoCamera.updateProjectionMatrix();
715
+ } else {
716
+ perspCamera.aspect = aspect;
717
+ perspCamera.updateProjectionMatrix();
718
+ }
719
+ renderer.setSize(window.innerWidth, window.innerHeight);
720
+ });
721
+
722
+ renderer.domElement.removeEventListener('click', onClick);
723
+ renderer.domElement.addEventListener('mousedown', onMouseDown);
724
+ renderer.domElement.addEventListener('mousemove', onMouseMove);
725
+ renderer.domElement.addEventListener('mouseup', onMouseUp);
726
+ renderer.domElement.addEventListener('contextmenu', (event) => {
727
+ event.preventDefault();
728
+ onClick(event);
729
+ });
730
+
731
+
732
+ function animate() {
733
+ requestAnimationFrame(animate);
734
+ controls.update();
735
+ renderer.render(scene, currentCamera);
736
+ }
737
+
738
+ animate();
739
+ </script>
740
+ </body>
741
+ </html>
libs/gradio-client.js/1.10.0/gradio-client.js ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.37.0.
3
+ * Original file: /npm/@gradio/[email protected]/dist/index.js
4
+ *
5
+ * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
6
+ */
7
+ var t="undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{};function e(){throw new Error("setTimeout has not been defined")}function n(){throw new Error("clearTimeout has not been defined")}var r=e,i=n;function o(t){if(r===setTimeout)return setTimeout(t,0);if((r===e||!r)&&setTimeout)return r=setTimeout,setTimeout(t,0);try{return r(t,0)}catch(e){try{return r.call(null,t,0)}catch(e){return r.call(this,t,0)}}}"function"==typeof t.setTimeout&&(r=setTimeout),"function"==typeof t.clearTimeout&&(i=clearTimeout);var s,a=[],u=!1,c=-1;function h(){u&&s&&(u=!1,s.length?a=s.concat(a):c=-1,a.length&&l())}function l(){if(!u){var t=o(h);u=!0;for(var e=a.length;e;){for(s=a,a=[];++c<e;)s&&s[c].run();c=-1,e=a.length}s=null,u=!1,function(t){if(i===clearTimeout)return clearTimeout(t);if((i===n||!i)&&clearTimeout)return i=clearTimeout,clearTimeout(t);try{return i(t)}catch(e){try{return i.call(null,t)}catch(e){return i.call(this,t)}}}(t)}}function f(t,e){this.fun=t,this.array=e}f.prototype.run=function(){this.fun.apply(null,this.array)};function p(){}var d=p,g=p,w=p,m=p,_=p,y=p,v=p;var b=t.performance||{},E=b.now||b.mozNow||b.msNow||b.oNow||b.webkitNow||function(){return(new Date).getTime()};var S=new Date;var A={nextTick:function(t){var e=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)e[n-1]=arguments[n];a.push(new f(t,e)),1!==a.length||u||o(l)},title:"browser",browser:!0,env:{},argv:[],version:"",versions:{},on:d,addListener:g,once:w,off:m,removeListener:_,removeAllListeners:y,emit:v,binding:function(t){throw new Error("process.binding is not supported")},cwd:function(){return"/"},chdir:function(t){throw new Error("process.chdir is not supported")},umask:function(){return 0},hrtime:function(t){var e=.001*E.call(b),n=Math.floor(e),r=Math.floor(e%1*1e9);return t&&(n-=t[0],(r-=t[1])<0&&(n--,r+=1e9)),[n,r]},platform:"browser",release:{},config:{},uptime:function(){return(new Date-S)/1e3}},T=[],P=[],R="undefined"!=typeof Uint8Array?Uint8Array:Array,x=!1;function k(){x=!0;for(var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",e=0;e<64;++e)T[e]=t[e],P[t.charCodeAt(e)]=e;P["-".charCodeAt(0)]=62,P["_".charCodeAt(0)]=63}function $(t,e,n){for(var r,i,o=[],s=e;s<n;s+=3)r=(t[s]<<16)+(t[s+1]<<8)+t[s+2],o.push(T[(i=r)>>18&63]+T[i>>12&63]+T[i>>6&63]+T[63&i]);return o.join("")}function U(t){var e;x||k();for(var n=t.length,r=n%3,i="",o=[],s=16383,a=0,u=n-r;a<u;a+=s)o.push($(t,a,a+s>u?u:a+s));return 1===r?(e=t[n-1],i+=T[e>>2],i+=T[e<<4&63],i+="=="):2===r&&(e=(t[n-2]<<8)+t[n-1],i+=T[e>>10],i+=T[e>>4&63],i+=T[e<<2&63],i+="="),o.push(i),o.join("")}function O(t,e,n,r,i){var o,s,a=8*i-r-1,u=(1<<a)-1,c=u>>1,h=-7,l=n?i-1:0,f=n?-1:1,p=t[e+l];for(l+=f,o=p&(1<<-h)-1,p>>=-h,h+=a;h>0;o=256*o+t[e+l],l+=f,h-=8);for(s=o&(1<<-h)-1,o>>=-h,h+=r;h>0;s=256*s+t[e+l],l+=f,h-=8);if(0===o)o=1-c;else{if(o===u)return s?NaN:1/0*(p?-1:1);s+=Math.pow(2,r),o-=c}return(p?-1:1)*s*Math.pow(2,o-r)}function B(t,e,n,r,i,o){var s,a,u,c=8*o-i-1,h=(1<<c)-1,l=h>>1,f=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,p=r?0:o-1,d=r?1:-1,g=e<0||0===e&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(a=isNaN(e)?1:0,s=h):(s=Math.floor(Math.log(e)/Math.LN2),e*(u=Math.pow(2,-s))<1&&(s--,u*=2),(e+=s+l>=1?f/u:f*Math.pow(2,1-l))*u>=2&&(s++,u/=2),s+l>=h?(a=0,s=h):s+l>=1?(a=(e*u-1)*Math.pow(2,i),s+=l):(a=e*Math.pow(2,l-1)*Math.pow(2,i),s=0));i>=8;t[n+p]=255&a,p+=d,a/=256,i-=8);for(s=s<<i|a,c+=i;c>0;t[n+p]=255&s,p+=d,s/=256,c-=8);t[n+p-d]|=128*g}var C={}.toString,D=Array.isArray||function(t){return"[object Array]"==C.call(t)};function I(){return j.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function N(t,e){if(I()<e)throw new RangeError("Invalid typed array length");return j.TYPED_ARRAY_SUPPORT?(t=new Uint8Array(e)).__proto__=j.prototype:(null===t&&(t=new j(e)),t.length=e),t}function j(t,e,n){if(!(j.TYPED_ARRAY_SUPPORT||this instanceof j))return new j(t,e,n);if("number"==typeof t){if("string"==typeof e)throw new Error("If encoding is specified then the first argument must be a string");return q(this,t)}return L(this,t,e,n)}function L(t,e,n,r){if("number"==typeof e)throw new TypeError('"value" argument must not be a number');return"undefined"!=typeof ArrayBuffer&&e instanceof ArrayBuffer?function(t,e,n,r){if(e.byteLength,n<0||e.byteLength<n)throw new RangeError("'offset' is out of bounds");if(e.byteLength<n+(r||0))throw new RangeError("'length' is out of bounds");e=void 0===n&&void 0===r?new Uint8Array(e):void 0===r?new Uint8Array(e,n):new Uint8Array(e,n,r);j.TYPED_ARRAY_SUPPORT?(t=e).__proto__=j.prototype:t=Y(t,e);return t}(t,e,n,r):"string"==typeof e?function(t,e,n){"string"==typeof n&&""!==n||(n="utf8");if(!j.isEncoding(n))throw new TypeError('"encoding" must be a valid string encoding');var r=0|W(e,n);t=N(t,r);var i=t.write(e,n);i!==r&&(t=t.slice(0,i));return t}(t,e,n):function(t,e){if(F(e)){var n=0|M(e.length);return 0===(t=N(t,n)).length||e.copy(t,0,0,n),t}if(e){if("undefined"!=typeof ArrayBuffer&&e.buffer instanceof ArrayBuffer||"length"in e)return"number"!=typeof e.length||(r=e.length)!=r?N(t,0):Y(t,e);if("Buffer"===e.type&&D(e.data))return Y(t,e.data)}var r;throw new TypeError("First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.")}(t,e)}function z(t){if("number"!=typeof t)throw new TypeError('"size" argument must be a number');if(t<0)throw new RangeError('"size" argument must not be negative')}function q(t,e){if(z(e),t=N(t,e<0?0:0|M(e)),!j.TYPED_ARRAY_SUPPORT)for(var n=0;n<e;++n)t[n]=0;return t}function Y(t,e){var n=e.length<0?0:0|M(e.length);t=N(t,n);for(var r=0;r<n;r+=1)t[r]=255&e[r];return t}function M(t){if(t>=I())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+I().toString(16)+" bytes");return 0|t}function F(t){return!(null==t||!t._isBuffer)}function W(t,e){if(F(t))return t.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(t)||t instanceof ArrayBuffer))return t.byteLength;"string"!=typeof t&&(t=""+t);var n=t.length;if(0===n)return 0;for(var r=!1;;)switch(e){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return _t(t).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return yt(t).length;default:if(r)return _t(t).length;e=(""+e).toLowerCase(),r=!0}}function J(t,e,n){var r=!1;if((void 0===e||e<0)&&(e=0),e>this.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(e>>>=0))return"";for(t||(t="utf8");;)switch(t){case"hex":return at(this,e,n);case"utf8":case"utf-8":return rt(this,e,n);case"ascii":return ot(this,e,n);case"latin1":case"binary":return st(this,e,n);case"base64":return nt(this,e,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return ut(this,e,n);default:if(r)throw new TypeError("Unknown encoding: "+t);t=(t+"").toLowerCase(),r=!0}}function G(t,e,n){var r=t[e];t[e]=t[n],t[n]=r}function H(t,e,n,r,i){if(0===t.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=i?0:t.length-1),n<0&&(n=t.length+n),n>=t.length){if(i)return-1;n=t.length-1}else if(n<0){if(!i)return-1;n=0}if("string"==typeof e&&(e=j.from(e,r)),F(e))return 0===e.length?-1:V(t,e,n,r,i);if("number"==typeof e)return e&=255,j.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(t,e,n):Uint8Array.prototype.lastIndexOf.call(t,e,n):V(t,[e],n,r,i);throw new TypeError("val must be string, number or Buffer")}function V(t,e,n,r,i){var o,s=1,a=t.length,u=e.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(t.length<2||e.length<2)return-1;s=2,a/=2,u/=2,n/=2}function c(t,e){return 1===s?t[e]:t.readUInt16BE(e*s)}if(i){var h=-1;for(o=n;o<a;o++)if(c(t,o)===c(e,-1===h?0:o-h)){if(-1===h&&(h=o),o-h+1===u)return h*s}else-1!==h&&(o-=o-h),h=-1}else for(n+u>a&&(n=a-u),o=n;o>=0;o--){for(var l=!0,f=0;f<u;f++)if(c(t,o+f)!==c(e,f)){l=!1;break}if(l)return o}return-1}function Z(t,e,n,r){n=Number(n)||0;var i=t.length-n;r?(r=Number(r))>i&&(r=i):r=i;var o=e.length;if(o%2!=0)throw new TypeError("Invalid hex string");r>o/2&&(r=o/2);for(var s=0;s<r;++s){var a=parseInt(e.substr(2*s,2),16);if(isNaN(a))return s;t[n+s]=a}return s}function K(t,e,n,r){return vt(_t(e,t.length-n),t,n,r)}function Q(t,e,n,r){return vt(function(t){for(var e=[],n=0;n<t.length;++n)e.push(255&t.charCodeAt(n));return e}(e),t,n,r)}function X(t,e,n,r){return Q(t,e,n,r)}function tt(t,e,n,r){return vt(yt(e),t,n,r)}function et(t,e,n,r){return vt(function(t,e){for(var n,r,i,o=[],s=0;s<t.length&&!((e-=2)<0);++s)r=(n=t.charCodeAt(s))>>8,i=n%256,o.push(i),o.push(r);return o}(e,t.length-n),t,n,r)}function nt(t,e,n){return 0===e&&n===t.length?U(t):U(t.slice(e,n))}function rt(t,e,n){n=Math.min(t.length,n);for(var r=[],i=e;i<n;){var o,s,a,u,c=t[i],h=null,l=c>239?4:c>223?3:c>191?2:1;if(i+l<=n)switch(l){case 1:c<128&&(h=c);break;case 2:128==(192&(o=t[i+1]))&&(u=(31&c)<<6|63&o)>127&&(h=u);break;case 3:o=t[i+1],s=t[i+2],128==(192&o)&&128==(192&s)&&(u=(15&c)<<12|(63&o)<<6|63&s)>2047&&(u<55296||u>57343)&&(h=u);break;case 4:o=t[i+1],s=t[i+2],a=t[i+3],128==(192&o)&&128==(192&s)&&128==(192&a)&&(u=(15&c)<<18|(63&o)<<12|(63&s)<<6|63&a)>65535&&u<1114112&&(h=u)}null===h?(h=65533,l=1):h>65535&&(h-=65536,r.push(h>>>10&1023|55296),h=56320|1023&h),r.push(h),i+=l}return function(t){var e=t.length;if(e<=it)return String.fromCharCode.apply(String,t);var n="",r=0;for(;r<e;)n+=String.fromCharCode.apply(String,t.slice(r,r+=it));return n}(r)}j.TYPED_ARRAY_SUPPORT=void 0===t.TYPED_ARRAY_SUPPORT||t.TYPED_ARRAY_SUPPORT,I(),j.poolSize=8192,j._augment=function(t){return t.__proto__=j.prototype,t},j.from=function(t,e,n){return L(null,t,e,n)},j.TYPED_ARRAY_SUPPORT&&(j.prototype.__proto__=Uint8Array.prototype,j.__proto__=Uint8Array,"undefined"!=typeof Symbol&&Symbol.species&&j[Symbol.species]),j.alloc=function(t,e,n){return function(t,e,n,r){return z(e),e<=0?N(t,e):void 0!==n?"string"==typeof r?N(t,e).fill(n,r):N(t,e).fill(n):N(t,e)}(null,t,e,n)},j.allocUnsafe=function(t){return q(null,t)},j.allocUnsafeSlow=function(t){return q(null,t)},j.isBuffer=function(t){return null!=t&&(!!t._isBuffer||bt(t)||function(t){return"function"==typeof t.readFloatLE&&"function"==typeof t.slice&&bt(t.slice(0,0))}(t))},j.compare=function(t,e){if(!F(t)||!F(e))throw new TypeError("Arguments must be Buffers");if(t===e)return 0;for(var n=t.length,r=e.length,i=0,o=Math.min(n,r);i<o;++i)if(t[i]!==e[i]){n=t[i],r=e[i];break}return n<r?-1:r<n?1:0},j.isEncoding=function(t){switch(String(t).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"latin1":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},j.concat=function(t,e){if(!D(t))throw new TypeError('"list" argument must be an Array of Buffers');if(0===t.length)return j.alloc(0);var n;if(void 0===e)for(e=0,n=0;n<t.length;++n)e+=t[n].length;var r=j.allocUnsafe(e),i=0;for(n=0;n<t.length;++n){var o=t[n];if(!F(o))throw new TypeError('"list" argument must be an Array of Buffers');o.copy(r,i),i+=o.length}return r},j.byteLength=W,j.prototype._isBuffer=!0,j.prototype.swap16=function(){var t=this.length;if(t%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(var e=0;e<t;e+=2)G(this,e,e+1);return this},j.prototype.swap32=function(){var t=this.length;if(t%4!=0)throw new RangeError("Buffer size must be a multiple of 32-bits");for(var e=0;e<t;e+=4)G(this,e,e+3),G(this,e+1,e+2);return this},j.prototype.swap64=function(){var t=this.length;if(t%8!=0)throw new RangeError("Buffer size must be a multiple of 64-bits");for(var e=0;e<t;e+=8)G(this,e,e+7),G(this,e+1,e+6),G(this,e+2,e+5),G(this,e+3,e+4);return this},j.prototype.toString=function(){var t=0|this.length;return 0===t?"":0===arguments.length?rt(this,0,t):J.apply(this,arguments)},j.prototype.equals=function(t){if(!F(t))throw new TypeError("Argument must be a Buffer");return this===t||0===j.compare(this,t)},j.prototype.inspect=function(){var t="";return this.length>0&&(t=this.toString("hex",0,50).match(/.{2}/g).join(" "),this.length>50&&(t+=" ... ")),"<Buffer "+t+">"},j.prototype.compare=function(t,e,n,r,i){if(!F(t))throw new TypeError("Argument must be a Buffer");if(void 0===e&&(e=0),void 0===n&&(n=t?t.length:0),void 0===r&&(r=0),void 0===i&&(i=this.length),e<0||n>t.length||r<0||i>this.length)throw new RangeError("out of range index");if(r>=i&&e>=n)return 0;if(r>=i)return-1;if(e>=n)return 1;if(this===t)return 0;for(var o=(i>>>=0)-(r>>>=0),s=(n>>>=0)-(e>>>=0),a=Math.min(o,s),u=this.slice(r,i),c=t.slice(e,n),h=0;h<a;++h)if(u[h]!==c[h]){o=u[h],s=c[h];break}return o<s?-1:s<o?1:0},j.prototype.includes=function(t,e,n){return-1!==this.indexOf(t,e,n)},j.prototype.indexOf=function(t,e,n){return H(this,t,e,n,!0)},j.prototype.lastIndexOf=function(t,e,n){return H(this,t,e,n,!1)},j.prototype.write=function(t,e,n,r){if(void 0===e)r="utf8",n=this.length,e=0;else if(void 0===n&&"string"==typeof e)r=e,n=this.length,e=0;else{if(!isFinite(e))throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported");e|=0,isFinite(n)?(n|=0,void 0===r&&(r="utf8")):(r=n,n=void 0)}var i=this.length-e;if((void 0===n||n>i)&&(n=i),t.length>0&&(n<0||e<0)||e>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var o=!1;;)switch(r){case"hex":return Z(this,t,e,n);case"utf8":case"utf-8":return K(this,t,e,n);case"ascii":return Q(this,t,e,n);case"latin1":case"binary":return X(this,t,e,n);case"base64":return tt(this,t,e,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return et(this,t,e,n);default:if(o)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),o=!0}},j.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var it=4096;function ot(t,e,n){var r="";n=Math.min(t.length,n);for(var i=e;i<n;++i)r+=String.fromCharCode(127&t[i]);return r}function st(t,e,n){var r="";n=Math.min(t.length,n);for(var i=e;i<n;++i)r+=String.fromCharCode(t[i]);return r}function at(t,e,n){var r=t.length;(!e||e<0)&&(e=0),(!n||n<0||n>r)&&(n=r);for(var i="",o=e;o<n;++o)i+=mt(t[o]);return i}function ut(t,e,n){for(var r=t.slice(e,n),i="",o=0;o<r.length;o+=2)i+=String.fromCharCode(r[o]+256*r[o+1]);return i}function ct(t,e,n){if(t%1!=0||t<0)throw new RangeError("offset is not uint");if(t+e>n)throw new RangeError("Trying to access beyond buffer length")}function ht(t,e,n,r,i,o){if(!F(t))throw new TypeError('"buffer" argument must be a Buffer instance');if(e>i||e<o)throw new RangeError('"value" argument is out of bounds');if(n+r>t.length)throw new RangeError("Index out of range")}function lt(t,e,n,r){e<0&&(e=65535+e+1);for(var i=0,o=Math.min(t.length-n,2);i<o;++i)t[n+i]=(e&255<<8*(r?i:1-i))>>>8*(r?i:1-i)}function ft(t,e,n,r){e<0&&(e=4294967295+e+1);for(var i=0,o=Math.min(t.length-n,4);i<o;++i)t[n+i]=e>>>8*(r?i:3-i)&255}function pt(t,e,n,r,i,o){if(n+r>t.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function dt(t,e,n,r,i){return i||pt(t,0,n,4),B(t,e,n,r,23,4),n+4}function gt(t,e,n,r,i){return i||pt(t,0,n,8),B(t,e,n,r,52,8),n+8}j.prototype.slice=function(t,e){var n,r=this.length;if((t=~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),(e=void 0===e?r:~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),e<t&&(e=t),j.TYPED_ARRAY_SUPPORT)(n=this.subarray(t,e)).__proto__=j.prototype;else{var i=e-t;n=new j(i,void 0);for(var o=0;o<i;++o)n[o]=this[o+t]}return n},j.prototype.readUIntLE=function(t,e,n){t|=0,e|=0,n||ct(t,e,this.length);for(var r=this[t],i=1,o=0;++o<e&&(i*=256);)r+=this[t+o]*i;return r},j.prototype.readUIntBE=function(t,e,n){t|=0,e|=0,n||ct(t,e,this.length);for(var r=this[t+--e],i=1;e>0&&(i*=256);)r+=this[t+--e]*i;return r},j.prototype.readUInt8=function(t,e){return e||ct(t,1,this.length),this[t]},j.prototype.readUInt16LE=function(t,e){return e||ct(t,2,this.length),this[t]|this[t+1]<<8},j.prototype.readUInt16BE=function(t,e){return e||ct(t,2,this.length),this[t]<<8|this[t+1]},j.prototype.readUInt32LE=function(t,e){return e||ct(t,4,this.length),(this[t]|this[t+1]<<8|this[t+2]<<16)+16777216*this[t+3]},j.prototype.readUInt32BE=function(t,e){return e||ct(t,4,this.length),16777216*this[t]+(this[t+1]<<16|this[t+2]<<8|this[t+3])},j.prototype.readIntLE=function(t,e,n){t|=0,e|=0,n||ct(t,e,this.length);for(var r=this[t],i=1,o=0;++o<e&&(i*=256);)r+=this[t+o]*i;return r>=(i*=128)&&(r-=Math.pow(2,8*e)),r},j.prototype.readIntBE=function(t,e,n){t|=0,e|=0,n||ct(t,e,this.length);for(var r=e,i=1,o=this[t+--r];r>0&&(i*=256);)o+=this[t+--r]*i;return o>=(i*=128)&&(o-=Math.pow(2,8*e)),o},j.prototype.readInt8=function(t,e){return e||ct(t,1,this.length),128&this[t]?-1*(255-this[t]+1):this[t]},j.prototype.readInt16LE=function(t,e){e||ct(t,2,this.length);var n=this[t]|this[t+1]<<8;return 32768&n?4294901760|n:n},j.prototype.readInt16BE=function(t,e){e||ct(t,2,this.length);var n=this[t+1]|this[t]<<8;return 32768&n?4294901760|n:n},j.prototype.readInt32LE=function(t,e){return e||ct(t,4,this.length),this[t]|this[t+1]<<8|this[t+2]<<16|this[t+3]<<24},j.prototype.readInt32BE=function(t,e){return e||ct(t,4,this.length),this[t]<<24|this[t+1]<<16|this[t+2]<<8|this[t+3]},j.prototype.readFloatLE=function(t,e){return e||ct(t,4,this.length),O(this,t,!0,23,4)},j.prototype.readFloatBE=function(t,e){return e||ct(t,4,this.length),O(this,t,!1,23,4)},j.prototype.readDoubleLE=function(t,e){return e||ct(t,8,this.length),O(this,t,!0,52,8)},j.prototype.readDoubleBE=function(t,e){return e||ct(t,8,this.length),O(this,t,!1,52,8)},j.prototype.writeUIntLE=function(t,e,n,r){(t=+t,e|=0,n|=0,r)||ht(this,t,e,n,Math.pow(2,8*n)-1,0);var i=1,o=0;for(this[e]=255&t;++o<n&&(i*=256);)this[e+o]=t/i&255;return e+n},j.prototype.writeUIntBE=function(t,e,n,r){(t=+t,e|=0,n|=0,r)||ht(this,t,e,n,Math.pow(2,8*n)-1,0);var i=n-1,o=1;for(this[e+i]=255&t;--i>=0&&(o*=256);)this[e+i]=t/o&255;return e+n},j.prototype.writeUInt8=function(t,e,n){return t=+t,e|=0,n||ht(this,t,e,1,255,0),j.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),this[e]=255&t,e+1},j.prototype.writeUInt16LE=function(t,e,n){return t=+t,e|=0,n||ht(this,t,e,2,65535,0),j.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8):lt(this,t,e,!0),e+2},j.prototype.writeUInt16BE=function(t,e,n){return t=+t,e|=0,n||ht(this,t,e,2,65535,0),j.TYPED_ARRAY_SUPPORT?(this[e]=t>>>8,this[e+1]=255&t):lt(this,t,e,!1),e+2},j.prototype.writeUInt32LE=function(t,e,n){return t=+t,e|=0,n||ht(this,t,e,4,4294967295,0),j.TYPED_ARRAY_SUPPORT?(this[e+3]=t>>>24,this[e+2]=t>>>16,this[e+1]=t>>>8,this[e]=255&t):ft(this,t,e,!0),e+4},j.prototype.writeUInt32BE=function(t,e,n){return t=+t,e|=0,n||ht(this,t,e,4,4294967295,0),j.TYPED_ARRAY_SUPPORT?(this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t):ft(this,t,e,!1),e+4},j.prototype.writeIntLE=function(t,e,n,r){if(t=+t,e|=0,!r){var i=Math.pow(2,8*n-1);ht(this,t,e,n,i-1,-i)}var o=0,s=1,a=0;for(this[e]=255&t;++o<n&&(s*=256);)t<0&&0===a&&0!==this[e+o-1]&&(a=1),this[e+o]=(t/s|0)-a&255;return e+n},j.prototype.writeIntBE=function(t,e,n,r){if(t=+t,e|=0,!r){var i=Math.pow(2,8*n-1);ht(this,t,e,n,i-1,-i)}var o=n-1,s=1,a=0;for(this[e+o]=255&t;--o>=0&&(s*=256);)t<0&&0===a&&0!==this[e+o+1]&&(a=1),this[e+o]=(t/s|0)-a&255;return e+n},j.prototype.writeInt8=function(t,e,n){return t=+t,e|=0,n||ht(this,t,e,1,127,-128),j.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),t<0&&(t=255+t+1),this[e]=255&t,e+1},j.prototype.writeInt16LE=function(t,e,n){return t=+t,e|=0,n||ht(this,t,e,2,32767,-32768),j.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8):lt(this,t,e,!0),e+2},j.prototype.writeInt16BE=function(t,e,n){return t=+t,e|=0,n||ht(this,t,e,2,32767,-32768),j.TYPED_ARRAY_SUPPORT?(this[e]=t>>>8,this[e+1]=255&t):lt(this,t,e,!1),e+2},j.prototype.writeInt32LE=function(t,e,n){return t=+t,e|=0,n||ht(this,t,e,4,2147483647,-2147483648),j.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8,this[e+2]=t>>>16,this[e+3]=t>>>24):ft(this,t,e,!0),e+4},j.prototype.writeInt32BE=function(t,e,n){return t=+t,e|=0,n||ht(this,t,e,4,2147483647,-2147483648),t<0&&(t=4294967295+t+1),j.TYPED_ARRAY_SUPPORT?(this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t):ft(this,t,e,!1),e+4},j.prototype.writeFloatLE=function(t,e,n){return dt(this,t,e,!0,n)},j.prototype.writeFloatBE=function(t,e,n){return dt(this,t,e,!1,n)},j.prototype.writeDoubleLE=function(t,e,n){return gt(this,t,e,!0,n)},j.prototype.writeDoubleBE=function(t,e,n){return gt(this,t,e,!1,n)},j.prototype.copy=function(t,e,n,r){if(n||(n=0),r||0===r||(r=this.length),e>=t.length&&(e=t.length),e||(e=0),r>0&&r<n&&(r=n),r===n)return 0;if(0===t.length||0===this.length)return 0;if(e<0)throw new RangeError("targetStart out of bounds");if(n<0||n>=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),t.length-e<r-n&&(r=t.length-e+n);var i,o=r-n;if(this===t&&n<e&&e<r)for(i=o-1;i>=0;--i)t[i+e]=this[i+n];else if(o<1e3||!j.TYPED_ARRAY_SUPPORT)for(i=0;i<o;++i)t[i+e]=this[i+n];else Uint8Array.prototype.set.call(t,this.subarray(n,n+o),e);return o},j.prototype.fill=function(t,e,n,r){if("string"==typeof t){if("string"==typeof e?(r=e,e=0,n=this.length):"string"==typeof n&&(r=n,n=this.length),1===t.length){var i=t.charCodeAt(0);i<256&&(t=i)}if(void 0!==r&&"string"!=typeof r)throw new TypeError("encoding must be a string");if("string"==typeof r&&!j.isEncoding(r))throw new TypeError("Unknown encoding: "+r)}else"number"==typeof t&&(t&=255);if(e<0||this.length<e||this.length<n)throw new RangeError("Out of range index");if(n<=e)return this;var o;if(e>>>=0,n=void 0===n?this.length:n>>>0,t||(t=0),"number"==typeof t)for(o=e;o<n;++o)this[o]=t;else{var s=F(t)?t:_t(new j(t,r).toString()),a=s.length;for(o=0;o<n-e;++o)this[o+e]=s[o%a]}return this};var wt=/[^+\/0-9A-Za-z-_]/g;function mt(t){return t<16?"0"+t.toString(16):t.toString(16)}function _t(t,e){var n;e=e||1/0;for(var r=t.length,i=null,o=[],s=0;s<r;++s){if((n=t.charCodeAt(s))>55295&&n<57344){if(!i){if(n>56319){(e-=3)>-1&&o.push(239,191,189);continue}if(s+1===r){(e-=3)>-1&&o.push(239,191,189);continue}i=n;continue}if(n<56320){(e-=3)>-1&&o.push(239,191,189),i=n;continue}n=65536+(i-55296<<10|n-56320)}else i&&(e-=3)>-1&&o.push(239,191,189);if(i=null,n<128){if((e-=1)<0)break;o.push(n)}else if(n<2048){if((e-=2)<0)break;o.push(n>>6|192,63&n|128)}else if(n<65536){if((e-=3)<0)break;o.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((e-=4)<0)break;o.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return o}function yt(t){return function(t){var e,n,r,i,o,s;x||k();var a=t.length;if(a%4>0)throw new Error("Invalid string. Length must be a multiple of 4");o="="===t[a-2]?2:"="===t[a-1]?1:0,s=new R(3*a/4-o),r=o>0?a-4:a;var u=0;for(e=0,n=0;e<r;e+=4,n+=3)i=P[t.charCodeAt(e)]<<18|P[t.charCodeAt(e+1)]<<12|P[t.charCodeAt(e+2)]<<6|P[t.charCodeAt(e+3)],s[u++]=i>>16&255,s[u++]=i>>8&255,s[u++]=255&i;return 2===o?(i=P[t.charCodeAt(e)]<<2|P[t.charCodeAt(e+1)]>>4,s[u++]=255&i):1===o&&(i=P[t.charCodeAt(e)]<<10|P[t.charCodeAt(e+1)]<<4|P[t.charCodeAt(e+2)]>>2,s[u++]=i>>8&255,s[u++]=255&i),s}(function(t){if((t=function(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")}(t).replace(wt,"")).length<2)return"";for(;t.length%4!=0;)t+="=";return t}(t))}function vt(t,e,n,r){for(var i=0;i<r&&!(i+n>=e.length||i>=t.length);++i)e[i+n]=t[i];return i}function bt(t){return!!t.constructor&&"function"==typeof t.constructor.isBuffer&&t.constructor.isBuffer(t)}var Et,St=Object.defineProperty,At=(t,e,n)=>(((t,e,n)=>{e in t?St(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n})(t,"symbol"!=typeof e?e+"":e,n),n),Tt=(t,e,n)=>{if(!e.has(t))throw TypeError("Cannot "+n)},Pt=(t,e,n)=>(Tt(t,e,"read from private field"),n?n.call(t):e.get(t)),Rt=new Intl.Collator(0,{numeric:1}).compare;function xt(t,e,n){return t=t.split("."),e=e.split("."),Rt(t[0],e[0])||Rt(t[1],e[1])||(e[2]=e.slice(2).join("."),(n=/[.-]/.test(t[2]=t.slice(2).join(".")))==/[.-]/.test(e[2])?Rt(t[2],e[2]):n?-1:1)}const kt="queue/data",$t="upload",Ut="This application is currently busy. Please try again. ",Ot="Connection errored out. ",Bt="Could not resolve app config. ",Ct="Space metadata could not be loaded. ",Dt="Invalid credentials. Could not login. ",It="Root URL not found in client config";function Nt(t,e,n){return e.startsWith("http://")||e.startsWith("https://")?n?t:e:t+e}async function jt(t,e,n){try{const r=await fetch(`https://huggingface.co/api/spaces/${t}/jwt`,{headers:{Authorization:`Bearer ${e}`,...n?{Cookie:n}:{}}});return(await r.json()).token||!1}catch(t){return!1}}async function Lt(t){var e;const n=this.options.hf_token?{Authorization:`Bearer ${this.options.hf_token}`}:{};if(n["Content-Type"]="application/json","undefined"!=typeof window&&window.gradio_config&&"http://localhost:9876"!==location.origin&&!window.gradio_config.dev_mode){const e=window.gradio_config.root,n=window.gradio_config;let r=Nt(t,n.root,!1);return n.root=r,{...n,path:e}}if(t){const r=Gt(t,"config"),i=await this.fetch(r,{headers:n,credentials:"include"});if(401===(null==i?void 0:i.status)&&!this.options.auth)throw new Error("Login credentials are required to access this space.");if(401===(null==i?void 0:i.status)&&this.options.auth)throw new Error(Dt);if(200===(null==i?void 0:i.status)){let n=await i.json();return n.path=n.path??"",n.root=t,null==(e=n.dependencies)||e.forEach(((t,e)=>{void 0===t.id&&(t.id=e)})),n}if(401===(null==i?void 0:i.status))throw new Error("Not authorized to access this space. ");throw new Error(Bt)}throw new Error(Bt)}async function zt(){const{http_protocol:t,host:e}=await Jt(this.app_reference,this.options.hf_token);try{if(this.options.auth){const n=await qt(t,e,this.options.auth,this.fetch,this.options.hf_token);n&&this.set_cookies(n)}}catch(t){throw Error(t.message)}}async function qt(t,e,n,r,i){const o=new FormData;o.append("username",null==n?void 0:n[0]),o.append("password",null==n?void 0:n[1]);let s={};i&&(s.Authorization=`Bearer ${i}`);const a=await r(`${t}//${e}/login`,{headers:s,method:"POST",body:o,credentials:"include"});if(200===a.status)return a.headers.get("set-cookie");throw 401===a.status?new Error(Dt):new Error(Ct)}function Yt(t){if(t.startsWith("http")){const{protocol:e,host:n,pathname:r}=new URL(t);return{ws_protocol:"https:"===e?"wss":"ws",http_protocol:e,host:n+("/"!==r?r:"")}}return t.startsWith("file:")?{ws_protocol:"ws",http_protocol:"http:",host:"lite.local"}:{ws_protocol:"wss",http_protocol:"https:",host:new URL(t).host}}const Mt=t=>{let e=[];return t.split(/,(?=\s*[^\s=;]+=[^\s=;]+)/).forEach((t=>{const[n,r]=t.split(";")[0].split("=");n&&r&&e.push(`${n.trim()}=${r.trim()}`)})),e},Ft=/^[a-zA-Z0-9_\-\.]+\/[a-zA-Z0-9_\-\.]+$/,Wt=/.*hf\.space\/{0,1}.*$/;async function Jt(t,e){const n={};e&&(n.Authorization=`Bearer ${e}`);const r=t.trim().replace(/\/$/,"");if(Ft.test(r))try{const e=await fetch(`https://huggingface.co/api/spaces/${r}/host`,{headers:n});return{space_id:t,...Yt((await e.json()).host)}}catch(t){throw new Error(Ct)}if(Wt.test(r)){const{ws_protocol:t,http_protocol:e,host:n}=Yt(r);return{space_id:n.split("/")[0].replace(".hf.space",""),ws_protocol:t,http_protocol:e,host:n}}return{space_id:!1,...Yt(r)}}const Gt=(...t)=>{try{return t.reduce(((t,e)=>(t=t.replace(/\/+$/,""),e=e.replace(/^\/+/,""),new URL(e,t+"/").toString())))}catch(t){throw new Error("Invalid URL. A full URL path is required.")}};function Ht(t,e,n,r){if("Api"===e)return t.type;switch(null==t?void 0:t.type){case"string":return"string";case"boolean":return"boolean";case"number":return"number"}return"JSONSerializable"===n||"StringSerializable"===n?"any":"ListStringSerializable"===n?"string[]":"Image"===e?"parameter"===r?"Blob | File | Buffer":"string":"FileSerializable"===n?"array"===(null==t?void 0:t.type)?"parameter"===r?"(Blob | File | Buffer)[]":"{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}[]":"parameter"===r?"Blob | File | Buffer":"{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}":"GallerySerializable"===n?"parameter"===r?"[(Blob | File | Buffer), (string | null)][]":"[{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}, (string | null))][]":void 0}function Vt(t,e){return"GallerySerializable"===e?"array of [file, label] tuples":"ListStringSerializable"===e?"array of strings":"FileSerializable"===e?"array of files or single file":null==t?void 0:t.description}function Zt(t,e){const n=!0;switch(t.msg){case"send_data":return{type:"data"};case"send_hash":return{type:"hash"};case"queue_full":return{type:"update",status:{queue:n,message:Ut,stage:"error",code:t.code,success:t.success}};case"heartbeat":return{type:"heartbeat"};case"unexpected_error":return{type:"unexpected_error",status:{queue:n,message:t.message,stage:"error",success:!1}};case"estimation":return{type:"update",status:{queue:n,stage:e||"pending",code:t.code,size:t.queue_size,position:t.rank,eta:t.rank_eta,success:t.success}};case"progress":return{type:"update",status:{queue:n,stage:"pending",code:t.code,progress_data:t.progress_data,success:t.success}};case"log":return{type:"log",data:t};case"process_generating":return{type:"generating",status:{queue:n,message:t.success?null:t.output.error,stage:t.success?"generating":"error",code:t.code,progress_data:t.progress_data,eta:t.average_duration,changed_state_ids:t.success?t.output.changed_state_ids:void 0},data:t.success?t.output:null};case"process_streaming":return{type:"streaming",status:{queue:n,message:t.output.error,stage:"streaming",time_limit:t.time_limit,code:t.code,progress_data:t.progress_data,eta:t.eta},data:t.output};case"process_completed":return"error"in t.output?{type:"update",status:{queue:n,title:t.output.title,message:t.output.error,visible:t.output.visible,duration:t.output.duration,stage:"error",code:t.code,success:t.success}}:{type:"complete",status:{queue:n,message:t.success?void 0:t.output.error,stage:t.success?"complete":"error",code:t.code,progress_data:t.progress_data,changed_state_ids:t.success?t.output.changed_state_ids:void 0},data:t.success?t.output:null};case"process_starts":return{type:"update",status:{queue:n,stage:"pending",code:t.code,size:t.rank,position:0,success:t.success,eta:t.eta},original_msg:"process_starts"}}return{type:"none",status:{stage:"error",queue:n}}}async function Kt(){if(this.api_info)return this.api_info;const{hf_token:t}=this.options,{config:e}=this,n={"Content-Type":"application/json"};if(t&&(n.Authorization=`Bearer ${t}`),e)try{let t,r;if("undefined"!=typeof window&&window.gradio_api_info)r=window.gradio_api_info;else{if(xt((null==e?void 0:e.version)||"2.0.0","3.30")<0)t=await this.fetch("https://gradio-space-api-fetcher-v2.hf.space/api",{method:"POST",body:JSON.stringify({serialize:!1,config:JSON.stringify(e)}),headers:n,credentials:"include"});else{const r=Gt(e.root,this.api_prefix,"info");t=await this.fetch(r,{headers:n,credentials:"include"})}if(!t.ok)throw new Error(Ot);r=await t.json()}return"api"in r&&(r=r.api),r.named_endpoints["/predict"]&&!r.unnamed_endpoints[0]&&(r.unnamed_endpoints[0]=r.named_endpoints["/predict"]),function(t,e,n){const r={named_endpoints:{},unnamed_endpoints:{}};return Object.keys(t).forEach((i=>{"named_endpoints"!==i&&"unnamed_endpoints"!==i||(r[i]={},Object.entries(t[i]).forEach((([t,{parameters:o,returns:s}])=>{var a,u,c,h;const l=(null==(a=e.dependencies.find((e=>e.api_name===t||e.api_name===t.replace("/",""))))?void 0:a.id)||n[t.replace("/","")]||-1,f=-1!==l?null==(u=e.dependencies.find((t=>t.id==l)))?void 0:u.types:{generator:!1,cancel:!1};if(-1!==l&&(null==(h=null==(c=e.dependencies.find((t=>t.id==l)))?void 0:c.inputs)?void 0:h.length)!==o.length){const t=e.dependencies.find((t=>t.id==l)).inputs.map((t=>{var n;return null==(n=e.components.find((e=>e.id===t)))?void 0:n.type}));try{t.forEach(((t,e)=>{if("state"===t){const t={component:"state",example:null,parameter_default:null,parameter_has_default:!0,parameter_name:null,hidden:!0};o.splice(e,0,t)}}))}catch(t){console.error(t)}}const p=(t,e,n,r)=>({...t,description:Vt(null==t?void 0:t.type,n),type:Ht(null==t?void 0:t.type,e,n,r)||""});r[i][t]={parameters:o.map((t=>p(t,null==t?void 0:t.component,null==t?void 0:t.serializer,"parameter"))),returns:s.map((t=>p(t,null==t?void 0:t.component,null==t?void 0:t.serializer,"return"))),type:f}})))})),r}(r,e,this.api_map)}catch(t){t.message}}async function Qt(t,e,n){var r;const i={};(null==(r=null==this?void 0:this.options)?void 0:r.hf_token)&&(i.Authorization=`Bearer ${this.options.hf_token}`);const o=[];let s;for(let r=0;r<e.length;r+=1e3){const a=e.slice(r,r+1e3),u=new FormData;a.forEach((t=>{u.append("files",t)}));try{const e=n?`${t}${this.api_prefix}/${$t}?upload_id=${n}`:`${t}${this.api_prefix}/${$t}`;s=await this.fetch(e,{method:"POST",body:u,headers:i,credentials:"include"})}catch(t){throw new Error(Ot+t.message)}if(!s.ok){const t=await s.text();return{error:`HTTP ${s.status}: ${t}`}}const c=await s.json();c&&o.push(...c)}return{files:o}}async function Xt(t,e,n,r){let i=(Array.isArray(t)?t:[t]).map((t=>t.blob));const o=i.filter((t=>t.size>(r??1/0)));if(o.length)throw new Error(`File size exceeds the maximum allowed size of ${r} bytes: ${o.map((t=>t.name)).join(", ")}`);return await Promise.all(await this.upload_files(e,i,n).then((async n=>{if(n.error)throw new Error(n.error);return n.files?n.files.map(((n,r)=>new ee({...t[r],path:n,url:`${e}${this.api_prefix}/file=${n}`}))):[]})))}async function te(t,e){return t.map((t=>new ee({path:t.name,orig_name:t.name,blob:t,size:t.size,mime_type:t.type,is_stream:e})))}class ee{constructor({path:t,url:e,orig_name:n,size:r,blob:i,is_stream:o,mime_type:s,alt_text:a,b64:u}){At(this,"path"),At(this,"url"),At(this,"orig_name"),At(this,"size"),At(this,"blob"),At(this,"is_stream"),At(this,"mime_type"),At(this,"alt_text"),At(this,"b64"),At(this,"meta",{_type:"gradio.FileData"}),this.path=t,this.url=e,this.orig_name=n,this.size=r,this.blob=e?void 0:i,this.is_stream=o,this.mime_type=s,this.alt_text=a,this.b64=u}}class ne{constructor(t,e){At(this,"type"),At(this,"command"),At(this,"meta"),At(this,"fileData"),this.type="command",this.command=t,this.meta=e}}const re=void 0!==A&&A.versions&&A.versions.node;function ie(t,e,n){for(;n.length>1;){const e=n.shift();if("string"!=typeof e&&"number"!=typeof e)throw new Error("Invalid key type");t=t[e]}const r=n.shift();if("string"!=typeof r&&"number"!=typeof r)throw new Error("Invalid key type");t[r]=e}async function oe(t,e=void 0,n=[],r=!1,i=void 0){if(Array.isArray(t)){let o=[];return await Promise.all(t.map((async(s,a)=>{var u;let c=n.slice();c.push(String(a));const h=await oe(t[a],r?(null==(u=null==i?void 0:i.parameters[a])?void 0:u.component)||void 0:e,c,!1,i);o=o.concat(h)}))),o}if(globalThis.Buffer&&t instanceof globalThis.Buffer||t instanceof Blob)return[{path:n,blob:new Blob([t]),type:e}];if("object"==typeof t&&null!==t){let e=[];for(const r of Object.keys(t)){const o=[...n,r],s=t[r];e=e.concat(await oe(s,void 0,o,!1,i))}return e}return[]}function se(t){if("string"==typeof t){if(t.startsWith("http://")||t.startsWith("https://"))return{path:t,url:t,orig_name:t.split("/").pop()??"unknown",meta:{_type:"gradio.FileData"}};if(re)return new ne("upload_file",{path:t,name:t,orig_path:t})}else{if("undefined"!=typeof File&&t instanceof File)return new Blob([t]);if(t instanceof j)return new Blob([t]);if(t instanceof Blob)return t}throw new Error("Invalid input: must be a URL, File, Blob, or Buffer object.")}function ae(t,e,n,r,i=!1){if("input"===r&&!i)throw new Error("Invalid code path. Cannot skip state inputs for input.");if("output"===r&&i)return t;let o=[],s=0;const a="input"===r?e.inputs:e.outputs;for(let e=0;e<a.length;e++){const r=a[e],u=n.find((t=>t.id===r));if("state"!==(null==u?void 0:u.type)){const e=t[s];o.push(e),s++}else{if(!i){s++;continue}if(t.length===a.length){const e=t[s];o.push(e),s++}else o.push(null)}}return o}async function ue(t,e,n){const r=this;await async function(t,e){var n,r;if(!(null==(n=t.config)?void 0:n.root)&&!(null==(r=t.config)?void 0:r.root_url))throw new Error(It);await ce(t,e)}(r,e);const i=await oe(e,void 0,[],!0,n);return(await Promise.all(i.map((async({path:e,blob:n,type:i})=>{if(!n)return{path:e,type:i};const o=await r.upload_files(t,[n]);return{path:e,file_url:o.files&&o.files[0],type:i,name:"undefined"!=typeof File&&n instanceof File?null==n?void 0:n.name:void 0}})))).forEach((({path:t,file_url:n,type:r,name:i})=>{if("Gallery"===r)ie(e,n,t);else if(n){const r=new ee({path:n,orig_name:i});ie(e,r,t)}})),e}async function ce(t,e,n=[]){for(const r in e)e[r]instanceof ne?await he(t,e,r):"object"==typeof e[r]&&null!==e[r]&&await ce(t,e[r],[...n,r])}async function he(t,e,n){var r,i;let o=e[n];const s=(null==(r=t.config)?void 0:r.root)||(null==(i=t.config)?void 0:i.root_url);if(!s)throw new Error(It);try{let r,i;if(void 0===A||!A.versions||!A.versions.node)throw new Error("File system access is only available in Node.js environments");{const t=await import("/npm/fs/promises/+esm");i=(await import("/npm/path/+esm")).resolve(A.cwd(),o.meta.path),r=await t.readFile(i)}const a=new Blob([r],{type:"application/octet-stream"}),u=await t.upload_files(s,[a]),c=u.files&&u.files[0];if(c){const t=new ee({path:c,orig_name:o.meta.name||""});e[n]=t}}catch(t){console.error("Error uploading file",t)}}async function le(t,e,n){const r={"Content-Type":"application/json"};this.options.hf_token&&(r.Authorization=`Bearer ${this.options.hf_token}`);try{var i=await this.fetch(t,{method:"POST",body:JSON.stringify(e),headers:{...r,...n},credentials:"include"})}catch(t){return[{error:Ot},500]}let o,s;try{o=await i.json(),s=i.status}catch(t){o={error:`Could not parse server response: ${t}`},s=500}return[o,s]}async function fe(t,e={}){let n=!1,r=!1;if(!this.config)throw new Error("Could not resolve app config");if("number"==typeof t)this.config.dependencies.find((e=>e.id==t));else{const e=t.replace(/^\//,"");this.config.dependencies.find((t=>t.id==this.api_map[e]))}return new Promise((async(i,o)=>{const s=this.submit(t,e,null,null,!0);let a;for await(const t of s)"data"===t.type&&(r&&i(a),n=!0,a=t),"status"===t.type&&("error"===t.stage&&o(t),"complete"===t.stage&&(r=!0,n&&i(a)))}))}async function pe(t,e,n){let r,i,o="subdomain"===e?`https://huggingface.co/api/spaces/by-subdomain/${t}`:`https://huggingface.co/api/spaces/${t}`;try{if(r=await fetch(o),i=r.status,200!==i)throw new Error;r=await r.json()}catch(t){return void n({status:"error",load_status:"error",message:"Could not get space status. ",detail:"NOT_FOUND"})}if(!r||200!==i)return;const{runtime:{stage:s},id:a}=r;switch(s){case"STOPPED":case"SLEEPING":n({status:"sleeping",load_status:"pending",message:"Space is asleep. Waking it up...",detail:s}),setTimeout((()=>{pe(t,e,n)}),1e3);break;case"PAUSED":n({status:"paused",load_status:"error",message:"This space has been paused by the author. If you would like to try this demo, consider duplicating the space.",detail:s,discussions_enabled:await we(a)});break;case"RUNNING":case"RUNNING_BUILDING":n({status:"running",load_status:"complete",message:"Space is running.",detail:s});break;case"BUILDING":n({status:"building",load_status:"pending",message:"Space is building...",detail:s}),setTimeout((()=>{pe(t,e,n)}),1e3);break;case"APP_STARTING":n({status:"starting",load_status:"pending",message:"Space is starting...",detail:s}),setTimeout((()=>{pe(t,e,n)}),1e3);break;default:n({status:"space_error",load_status:"error",message:"This space is experiencing an issue.",detail:s,discussions_enabled:await we(a)})}}const de=async(t,e)=>{let n=0;return new Promise((r=>{pe(t,Ft.test(t)?"space_name":"subdomain",(i=>{e(i),"running"===i.status||"error"===i.status||"paused"===i.status||"space_error"===i.status?r():"sleeping"!==i.status&&"building"!==i.status||(n<12?(n++,setTimeout((()=>{de(t,e).then(r)}),5e3)):r())}))}))},ge=/^(?=[^]*\b[dD]iscussions{0,1}\b)(?=[^]*\b[dD]isabled\b)[^]*$/;async function we(t){try{const e=await fetch(`https://huggingface.co/api/spaces/${t}/discussions`,{method:"HEAD"}),n=e.headers.get("x-error-message");return!(!e.ok||n&&ge.test(n))}catch(t){return!1}}const me=["cpu-basic","cpu-upgrade","cpu-xl","t4-small","t4-medium","a10g-small","a10g-large","a10g-largex2","a10g-largex4","a100-large","zero-a10g","h100","h100x8"];async function _e(t,e){const{hf_token:n,private:r,hardware:i,timeout:o,auth:s}=e;if(i&&!me.includes(i))throw new Error(`Invalid hardware type provided. Valid types are: ${me.map((t=>`"${t}"`)).join(",")}.`);const{http_protocol:a,host:u}=await Jt(t,n);let c=null;if(s){const t=await qt(a,u,s,fetch);t&&(c=Mt(t))}const h={Authorization:`Bearer ${n}`,"Content-Type":"application/json",...c?{Cookie:c.join("; ")}:{}},l=(await(await fetch("https://huggingface.co/api/whoami-v2",{headers:h})).json()).name,f=t.split("/")[1],p={repository:`${l}/${f}`};let d;r&&(p.private=!0);try{i||(d=await async function(t,e){const n={};e&&(n.Authorization=`Bearer ${e}`);try{const e=await fetch(`https://huggingface.co/api/spaces/${t}/runtime`,{headers:n});if(200!==e.status)throw new Error("Space hardware could not be obtained.");const{hardware:r}=await e.json();return r.current}catch(t){throw new Error(t.message)}}(t,n))}catch(t){throw Error(Ct+t.message)}const g=i||d||"cpu-basic";p.hardware=g;try{const r=await fetch(`https://huggingface.co/api/spaces/${t}/duplicate`,{method:"POST",headers:h,body:JSON.stringify(p)});if(409===r.status)try{return await xe.connect(`${l}/${f}`,e)}catch(t){throw console.error("Failed to connect Client instance:",t),t}else if(200!==r.status)throw new Error(r.statusText);const i=await r.json();return await async function(t,e,n){const r={};n&&(r.Authorization=`Bearer ${n}`);const i={seconds:e};try{const e=await fetch(`https://huggingface.co/api/spaces/${t}/sleeptime`,{method:"POST",headers:{"Content-Type":"application/json",...r},body:JSON.stringify(i)});if(200!==e.status)throw new Error("Could not set sleep timeout on duplicated Space. Please visit *ADD HF LINK TO SETTINGS* to set a timeout manually to reduce billing charges.");return await e.json()}catch(t){throw new Error(t.message)}}(`${l}/${f}`,o||300,n),await xe.connect(function(t){const e=/https:\/\/huggingface.co\/spaces\/([^/]+\/[^/]+)/,n=t.match(e);if(n)return n[1]}(i.url),e)}catch(t){throw new Error(t)}}class ye extends TransformStream{constructor(t={allowCR:!1}){super({transform:(e,n)=>{for(e=Pt(this,Et)+e;;){const r=e.indexOf("\n"),i=t.allowCR?e.indexOf("\r"):-1;if(-1!==i&&i!==e.length-1&&(-1===r||r-1>i)){n.enqueue(e.slice(0,i)),e=e.slice(i+1);continue}if(-1===r)break;const o="\r"===e[r-1]?r-1:r;n.enqueue(e.slice(0,o)),e=e.slice(r+1)}var r,i,o,s;o=e,Tt(r=this,i=Et,"write to private field"),s?s.call(r,o):i.set(r,o)},flush:e=>{if(""===Pt(this,Et))return;const n=t.allowCR&&Pt(this,Et).endsWith("\r")?Pt(this,Et).slice(0,-1):Pt(this,Et);e.enqueue(n)}}),((t,e,n)=>{if(e.has(t))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(t):e.set(t,n)})(this,Et,"")}}function ve(t){let e=/[:]\s*/.exec(t),n=e&&e.index;if(n)return[t.substring(0,n),t.substring(n+e[0].length)]}function be(t,e,n){t.get(e)||t.set(e,n)}async function*Ee(t,e){if(!t.body)return;let n,r,i=function(t){let e=new TextDecoderStream,n=new ye({allowCR:!0});return t.pipeThrough(e).pipeThrough(n)}(t.body).getReader();for(;;){if(e&&e.aborted)return i.cancel();if(n=await i.read(),n.done)return;if(!n.value){r&&(yield r),r=void 0;continue}let[t,o]=ve(n.value)||[];t&&("data"===t?(r||(r={}),r[t]=r[t]?r[t]+"\n"+o:o):"event"===t?(r||(r={}),r[t]=o):"id"===t?(r||(r={}),r[t]=+o||o):"retry"===t&&(r||(r={}),r[t]=+o||void 0))}}async function Se(){let{event_callbacks:t,unclosed_events:e,pending_stream_messages:n,stream_status:r,config:i,jwt:o}=this;const s=this;if(!i)throw new Error("Could not resolve app config");r.open=!0;let a=null,u=new URLSearchParams({session_hash:this.session_hash}).toString(),c=new URL(`${i.root}${this.api_prefix}/${kt}?${u}`);o&&c.searchParams.set("__sign",o),a=this.stream(c),a?(a.onmessage=async function(o){let a=JSON.parse(o.data);if("close_stream"===a.msg)return void Ae(r,s.abort_controller);const u=a.event_id;if(u)if(t[u]&&i){"process_completed"===a.msg&&["sse","sse_v1","sse_v2","sse_v2.1","sse_v3"].includes(i.protocol)&&e.delete(u);let n=t[u];"undefined"!=typeof window&&"undefined"!=typeof document?setTimeout(n,0,a):n(a)}else n[u]||(n[u]=[]),n[u].push(a);else await Promise.all(Object.keys(t).map((e=>t[e](a))))},a.onerror=async function(){await Promise.all(Object.keys(t).map((e=>t[e]({msg:"unexpected_error",message:Ot}))))}):console.warn("Cannot connect to SSE endpoint: "+c.toString())}function Ae(t,e){t&&(t.open=!1,null==e||e.abort())}function Te(t,e,n){!t[e]?(t[e]=[],n.data.forEach(((n,r)=>{t[e][r]=n}))):n.data.forEach(((r,i)=>{let o=(s=t[e][i],r.forEach((([t,e,n])=>{s=function(t,e,n,r){if(0===e.length){if("replace"===n)return r;if("append"===n)return t+r;throw new Error(`Unsupported action: ${n}`)}let i=t;for(let t=0;t<e.length-1;t++)i=i[e[t]];const o=e[e.length-1];switch(n){case"replace":i[o]=r;break;case"append":i[o]+=r;break;case"add":Array.isArray(i)?i.splice(Number(o),0,r):i[o]=r;break;case"delete":Array.isArray(i)?i.splice(Number(o),1):delete i[o];break;default:throw new Error(`Unknown action: ${n}`)}return t}(s,e,t,n)})),s);var s;t[e][i]=o,n.data[i]=o}))}function Pe(t,e={}){const n={close:()=>{console.warn("Method not implemented.")},onerror:null,onmessage:null,onopen:null,readyState:0,url:t.toString(),withCredentials:!1,CONNECTING:0,OPEN:1,CLOSED:2,addEventListener:()=>{throw new Error("Method not implemented.")},dispatchEvent:()=>{throw new Error("Method not implemented.")},removeEventListener:()=>{throw new Error("Method not implemented.")}};return async function(t,e){let n=new Request(t,e);be(n.headers,"Accept","text/event-stream"),be(n.headers,"Content-Type","application/json");let r=await fetch(n);if(!r.ok)throw r;return Ee(r,n.signal)}(t,e).then((async t=>{n.readyState=n.OPEN;try{for await(const e of t)n.onmessage&&n.onmessage(e);n.readyState=n.CLOSED}catch(t){n.onerror&&n.onerror(t),n.readyState=n.CLOSED}})).catch((t=>{console.error(t),n.onerror&&n.onerror(t),n.readyState=n.CLOSED})),n}function Re(t,e={},n,r,i){var o;try{let s=function(t){(i||M[t.type])&&h(t)},a=function(){for(G=!0;V.length>0;)V.shift()({value:void 0,done:!0})},u=function(t){G||(V.length>0?V.shift()(t):H.push(t))},c=function(t){u(function(t){return{then:(e,n)=>n(t)}}(t)),a()},h=function(t){u({value:t,done:!1})},l=function(){return H.length>0?Promise.resolve(H.shift()):G?Promise.resolve({value:void 0,done:!0}):new Promise((t=>V.push(t)))};const{hf_token:f}=this.options,{fetch:p,app_reference:d,config:g,session_hash:w,api_info:m,api_map:_,stream_status:y,pending_stream_messages:v,pending_diff_streams:b,event_callbacks:E,unclosed_events:S,post_data:A,options:T,api_prefix:P}=this,R=this;if(!m)throw new Error("No API found");if(!g)throw new Error("Could not resolve app config");let x,k,{fn_index:$,endpoint_info:U,dependency:O}=function(t,e,n,r){let i,o,s;if("number"==typeof e)i=e,o=t.unnamed_endpoints[i],s=r.dependencies.find((t=>t.id==e));else{const a=e.replace(/^\//,"");i=n[a],o=t.named_endpoints[e.trim()],s=r.dependencies.find((t=>t.id==n[a]))}if("number"!=typeof i)throw new Error("There is no endpoint matching that name of fn_index matching that number.");return{fn_index:i,endpoint_info:o,dependency:s}}(m,t,_,g),B=((t=[],e)=>{const n=e?e.parameters:[];if(Array.isArray(t))return t.length>n.length&&console.warn("Too many arguments provided for the endpoint."),t;const r=[],i=Object.keys(t);return n.forEach(((e,n)=>{if(t.hasOwnProperty(e.parameter_name))r[n]=t[e.parameter_name];else{if(!e.parameter_has_default)throw new Error(`No value provided for required parameter: ${e.parameter_name}`);r[n]=e.parameter_default}})),i.forEach((t=>{if(!n.some((e=>e.parameter_name===t)))throw new Error(`Parameter \`${t}\` is not a valid keyword argument. Please refer to the API for usage.`)})),r.forEach(((t,e)=>{if(void 0===t&&!n[e].parameter_has_default)throw new Error(`No value provided for required parameter: ${n[e].parameter_name}`)})),r})(e,U),C=g.protocol??"ws",D="",I=()=>D;const N="number"==typeof t?"/predict":t;let j,L=null,z=!1,q={},Y="undefined"!=typeof window&&"undefined"!=typeof document?new URLSearchParams(window.location.search).toString():"";const M=(null==(o=null==T?void 0:T.events)?void 0:o.reduce(((t,e)=>(t[e]=!0,t)),{}))||{};async function F(){const t={stage:"complete",queue:!1,time:new Date};z=t,s({...t,type:"status",endpoint:N,fn_index:$});let e={},n={};"ws"===C?(x&&0===x.readyState?x.addEventListener("open",(()=>{x.close()})):x.close(),e={fn_index:$,session_hash:w}):(Ae(y,R.abort_controller),a(),e={event_id:L},n={event_id:L,session_hash:w,fn_index:$});try{if(!g)throw new Error("Could not resolve app config");"event_id"in n&&await p(`${g.root}${P}/cancel`,{headers:{"Content-Type":"application/json"},method:"POST",body:JSON.stringify(n)}),await p(`${g.root}${P}/reset`,{headers:{"Content-Type":"application/json"},method:"POST",body:JSON.stringify(e)})}catch(t){console.warn("The `/reset` endpoint could not be called. Subsequent endpoint results may be unreliable.")}}const W=async t=>{await this._resolve_hearbeat(t)};async function J(t){if(!g)return;let e=t.render_id;g.components=[...g.components.filter((t=>t.props.rendered_in!==e)),...t.components],g.dependencies=[...g.dependencies.filter((t=>t.rendered_in!==e)),...t.dependencies];const n=g.components.some((t=>"state"===t.type)),r=g.dependencies.some((t=>t.targets.some((t=>"unload"===t[1]))));g.connect_heartbeat=n||r,await W(g),s({type:"render",data:t,endpoint:N,fn_index:$})}this.handle_blob(g.root,B,U).then((async t=>{var e;let i=ae(t,O,g.components,"input",!0);if(j={data:i||[],event_data:n,fn_index:$,trigger_id:r},function(t,e){var n,r;let i=null==(r=null==(n=null==e?void 0:e.dependencies)?void 0:n.find((e=>e.id==t)))?void 0:r.queue;return null!=i?!i:!e.enable_queue}($,g))s({type:"status",endpoint:N,stage:"pending",queue:!1,fn_index:$,time:new Date}),A(`${g.root}${P}/run${N.startsWith("/")?N:`/${N}`}${Y?"?"+Y:""}`,{...j,session_hash:w}).then((([t,e])=>{const i=t.data;200==e?(s({type:"data",endpoint:N,fn_index:$,data:ae(i,O,g.components,"output",T.with_null_state),time:new Date,event_data:n,trigger_id:r}),t.render_config&&J(t.render_config),s({type:"status",endpoint:N,fn_index:$,stage:"complete",eta:t.average_duration,queue:!1,time:new Date})):s({type:"status",stage:"error",endpoint:N,fn_index:$,message:t.error,queue:!1,time:new Date})})).catch((t=>{s({type:"status",stage:"error",message:t.message,endpoint:N,fn_index:$,queue:!1,time:new Date})}));else if("ws"==C){const{ws_protocol:t,host:e}=await Jt(d,f);s({type:"status",stage:"pending",queue:!0,endpoint:N,fn_index:$,time:new Date});let i=new URL(`${t}://${Nt(e,g.path,!0)}/queue/join${Y?"?"+Y:""}`);this.jwt&&i.searchParams.set("__sign",this.jwt),x=new WebSocket(i),x.onclose=t=>{t.wasClean||s({type:"status",stage:"error",broken:!0,message:Ot,queue:!0,endpoint:N,fn_index:$,time:new Date})},x.onmessage=function(t){const e=JSON.parse(t.data),{type:i,status:o,data:a}=Zt(e,q[$]);if("update"===i&&o&&!z)s({type:"status",endpoint:N,fn_index:$,time:new Date,...o}),"error"===o.stage&&x.close();else{if("hash"===i)return void x.send(JSON.stringify({fn_index:$,session_hash:w}));"data"===i?x.send(JSON.stringify({...j,session_hash:w})):"complete"===i?z=o:"log"===i?s({type:"log",title:a.title,log:a.log,level:a.level,endpoint:N,duration:a.duration,visible:a.visible,fn_index:$}):"generating"===i&&s({type:"status",time:new Date,...o,stage:null==o?void 0:o.stage,queue:!0,endpoint:N,fn_index:$})}a&&(s({type:"data",time:new Date,data:ae(a.data,O,g.components,"output",T.with_null_state),endpoint:N,fn_index:$,event_data:n,trigger_id:r}),z&&(s({type:"status",time:new Date,...z,stage:null==o?void 0:o.stage,queue:!0,endpoint:N,fn_index:$}),x.close()))},xt(g.version||"2.0.0","3.6")<0&&addEventListener("open",(()=>x.send(JSON.stringify({hash:w}))))}else if("sse"==C){s({type:"status",stage:"pending",queue:!0,endpoint:N,fn_index:$,time:new Date});var o=new URLSearchParams({fn_index:$.toString(),session_hash:w}).toString();let t=new URL(`${g.root}${P}/${kt}?${Y?Y+"&":""}${o}`);if(this.jwt&&t.searchParams.set("__sign",this.jwt),k=this.stream(t),!k)return Promise.reject(new Error("Cannot connect to SSE endpoint: "+t.toString()));k.onmessage=async function(t){const e=JSON.parse(t.data),{type:i,status:o,data:u}=Zt(e,q[$]);if("update"===i&&o&&!z)s({type:"status",endpoint:N,fn_index:$,time:new Date,...o}),"error"===o.stage&&(null==k||k.close(),a());else if("data"===i){let[t,e]=await A(`${g.root}${P}/queue/data`,{...j,session_hash:w,event_id:L});200!==e&&(s({type:"status",stage:"error",message:Ot,queue:!0,endpoint:N,fn_index:$,time:new Date}),null==k||k.close(),a())}else"complete"===i?z=o:"log"===i?s({type:"log",title:u.title,log:u.log,level:u.level,endpoint:N,duration:u.duration,visible:u.visible,fn_index:$}):"generating"!==i&&"streaming"!==i||s({type:"status",time:new Date,...o,stage:null==o?void 0:o.stage,queue:!0,endpoint:N,fn_index:$});u&&(s({type:"data",time:new Date,data:ae(u.data,O,g.components,"output",T.with_null_state),endpoint:N,fn_index:$,event_data:n,trigger_id:r}),z&&(s({type:"status",time:new Date,...z,stage:null==o?void 0:o.stage,queue:!0,endpoint:N,fn_index:$}),null==k||k.close(),a()))}}else if("sse_v1"==C||"sse_v2"==C||"sse_v2.1"==C||"sse_v3"==C){s({type:"status",stage:"pending",queue:!0,endpoint:N,fn_index:$,time:new Date});let t="";"undefined"!=typeof window&&"undefined"!=typeof document&&(t=null==(e=null==window?void 0:window.location)?void 0:e.hostname);let n="dev.spaces.huggingface.tech";const r=t.includes(".dev.")?`https://moon-${t.split(".")[1]}.${n}`:"https://huggingface.co",i="undefined"!=typeof window&&"undefined"!=typeof document&&window.parent!=window&&window.supports_zerogpu_headers?function(t,e){return new Promise(((n,r)=>{const i=new MessageChannel;i.port1.onmessage=({data:t})=>{i.port1.close(),n(t)},window.parent.postMessage(t,e,[i.port2])}))}("zerogpu-headers",r):Promise.resolve(null);i.then((t=>A(`${g.root}${P}/queue/join?${Y}`,{...j,session_hash:w},t))).then((async([t,e])=>{if(503===e)s({type:"status",stage:"error",message:Ut,queue:!0,endpoint:N,fn_index:$,time:new Date});else if(200!==e)s({type:"status",stage:"error",message:Ot,queue:!0,endpoint:N,fn_index:$,time:new Date});else{L=t.event_id,D=L;let e=async function(t){try{const{type:e,status:n,data:r,original_msg:i}=Zt(t,q[$]);if("heartbeat"==e)return;if("update"===e&&n&&!z)s({type:"status",endpoint:N,fn_index:$,time:new Date,original_msg:i,...n});else if("complete"===e)z=n;else if("unexpected_error"==e)console.error("Unexpected error",null==n?void 0:n.message),s({type:"status",stage:"error",message:(null==n?void 0:n.message)||"An Unexpected Error Occurred!",queue:!0,endpoint:N,fn_index:$,time:new Date});else{if("log"===e)return void s({type:"log",title:r.title,log:r.log,level:r.level,endpoint:N,duration:r.duration,visible:r.visible,fn_index:$});"generating"!==e&&"streaming"!==e||(s({type:"status",time:new Date,...n,stage:null==n?void 0:n.stage,queue:!0,endpoint:N,fn_index:$}),r&&"stream"!==O.connection&&["sse_v2","sse_v2.1","sse_v3"].includes(C)&&Te(b,L,r))}r&&(s({type:"data",time:new Date,data:ae(r.data,O,g.components,"output",T.with_null_state),endpoint:N,fn_index:$}),r.render_config&&await J(r.render_config),z&&(s({type:"status",time:new Date,...z,stage:null==n?void 0:n.stage,queue:!0,endpoint:N,fn_index:$}),a())),"complete"!==(null==n?void 0:n.stage)&&"error"!==(null==n?void 0:n.stage)||(E[L]&&delete E[L],L in b&&delete b[L])}catch(t){console.error("Unexpected client exception",t),s({type:"status",stage:"error",message:"An Unexpected Error Occurred!",queue:!0,endpoint:N,fn_index:$,time:new Date}),["sse_v2","sse_v2.1","sse_v3"].includes(C)&&(Ae(y,R.abort_controller),y.open=!1,a())}};L in v&&(v[L].forEach((t=>e(t))),delete v[L]),E[L]=e,S.add(L),y.open||await this.open_stream()}}))}}));let G=!1;const H=[],V=[],Z={[Symbol.asyncIterator]:()=>Z,next:l,throw:async t=>(c(t),l()),return:async()=>(a(),l()),cancel:F,event_id:I};return Z}catch(K){throw console.error("Submit function encountered an error:",K),K}}Et=new WeakMap;class xe{constructor(t,e={events:["data"]}){At(this,"app_reference"),At(this,"options"),At(this,"config"),At(this,"api_prefix",""),At(this,"api_info"),At(this,"api_map",{}),At(this,"session_hash",Math.random().toString(36).substring(2)),At(this,"jwt",!1),At(this,"last_status",{}),At(this,"cookies",null),At(this,"stream_status",{open:!1}),At(this,"pending_stream_messages",{}),At(this,"pending_diff_streams",{}),At(this,"event_callbacks",{}),At(this,"unclosed_events",new Set),At(this,"heartbeat_event",null),At(this,"abort_controller",null),At(this,"stream_instance",null),At(this,"current_payload"),At(this,"ws_map",{}),At(this,"view_api"),At(this,"upload_files"),At(this,"upload"),At(this,"handle_blob"),At(this,"post_data"),At(this,"submit"),At(this,"predict"),At(this,"open_stream"),At(this,"resolve_config"),At(this,"resolve_cookies"),this.app_reference=t,e.events||(e.events=["data"]),this.options=e,this.current_payload={},this.view_api=Kt.bind(this),this.upload_files=Qt.bind(this),this.handle_blob=ue.bind(this),this.post_data=le.bind(this),this.submit=Re.bind(this),this.predict=fe.bind(this),this.open_stream=Se.bind(this),this.resolve_config=Lt.bind(this),this.resolve_cookies=zt.bind(this),this.upload=Xt.bind(this),this.fetch=this.fetch.bind(this),this.handle_space_success=this.handle_space_success.bind(this),this.stream=this.stream.bind(this)}fetch(t,e){const n=new Headers((null==e?void 0:e.headers)||{});if(this&&this.cookies&&n.append("Cookie",this.cookies),this&&this.options.headers)for(const t in this.options.headers)n.append(t,this.options.headers[t]);return fetch(t,{...e,headers:n})}stream(t){const e=new Headers;if(this&&this.cookies&&e.append("Cookie",this.cookies),this&&this.options.headers)for(const t in this.options.headers)e.append(t,this.options.headers[t]);return this.abort_controller=new AbortController,this.stream_instance=Pe(t.toString(),{credentials:"include",headers:e,signal:this.abort_controller.signal}),this.stream_instance}async init(){var e;if(!("undefined"!=typeof window&&"WebSocket"in window||t.WebSocket)){const e=await import("/npm/@gradio/[email protected]/dist/wrapper-CviSselG.js/+esm");t.WebSocket=e.WebSocket}this.options.auth&&await this.resolve_cookies(),await this._resolve_config().then((({config:t})=>this._resolve_hearbeat(t))),this.api_info=await this.view_api(),this.api_map=function(t){let e={};return t.forEach((({api_name:t,id:n})=>{t&&(e[t]=n)})),e}((null==(e=this.config)?void 0:e.dependencies)||[])}async _resolve_hearbeat(t){if(t&&(this.config=t,this.api_prefix=t.api_prefix||"",this.config&&this.config.connect_heartbeat&&this.config.space_id&&this.options.hf_token&&(this.jwt=await jt(this.config.space_id,this.options.hf_token,this.cookies))),t.space_id&&this.options.hf_token&&(this.jwt=await jt(t.space_id,this.options.hf_token)),this.config&&this.config.connect_heartbeat){const t=new URL(`${this.config.root}${this.api_prefix}/heartbeat/${this.session_hash}`);this.jwt&&t.searchParams.set("__sign",this.jwt),this.heartbeat_event||(this.heartbeat_event=this.stream(t))}}static async connect(t,e={events:["data"]}){const n=new this(t,e);return await n.init(),n}close(){Ae(this.stream_status,this.abort_controller)}set_current_payload(t){this.current_payload=t}static async duplicate(t,e={events:["data"]}){return _e(t,e)}async _resolve_config(){const{http_protocol:t,host:e,space_id:n}=await Jt(this.app_reference,this.options.hf_token),{status_callback:r}=this.options;let i;n&&r&&await de(n,r);try{if(i=await this.resolve_config(`${t}//${e}`),!i)throw new Error(Bt);return this.config_success(i)}catch(t){if(!n||!r)throw r&&r({status:"error",message:"Could not load this space.",load_status:"error",detail:"NOT_FOUND"}),Error(t);pe(n,Ft.test(n)?"space_name":"subdomain",this.handle_space_success)}}async config_success(t){if(this.config=t,this.api_prefix=t.api_prefix||"","undefined"!=typeof window&&"undefined"!=typeof document&&"https:"===window.location.protocol&&(this.config.root=this.config.root.replace("http://","https://")),this.config.auth_required)return this.prepare_return_obj();try{this.api_info=await this.view_api()}catch(t){console.error("Could not get API info. "+t.message)}return this.prepare_return_obj()}async handle_space_success(t){var e;if(!this)throw new Error(Bt);const{status_callback:n}=this.options;if(n&&n(t),"running"===t.status)try{if(this.config=await this._resolve_config(),this.api_prefix=(null==(e=null==this?void 0:this.config)?void 0:e.api_prefix)||"",!this.config)throw new Error(Bt);return await this.config_success(this.config)}catch(t){throw n&&n({status:"error",message:"Could not load this space.",load_status:"error",detail:"NOT_FOUND"}),t}}async component_server(t,e,n){var r;if(!this.config)throw new Error(Bt);const i={},{hf_token:o}=this.options,{session_hash:s}=this;let a;o&&(i.Authorization=`Bearer ${this.options.hf_token}`);let u,c=this.config.components.find((e=>e.id===t));if(a=(null==(r=null==c?void 0:c.props)?void 0:r.root_url)?c.props.root_url:this.config.root,"binary"in n){u=new FormData;for(const t in n.data)"binary"!==t&&u.append(t,n.data[t]);u.set("component_id",t.toString()),u.set("fn_name",e),u.set("session_hash",s)}else u=JSON.stringify({data:n,component_id:t,fn_name:e,session_hash:s}),i["Content-Type"]="application/json";o&&(i.Authorization=`Bearer ${o}`);try{const t=await this.fetch(`${a}${this.api_prefix}/component_server/`,{method:"POST",body:u,headers:i,credentials:"include"});if(!t.ok)throw new Error("Could not connect to component server: "+t.statusText);return await t.json()}catch(t){console.warn(t)}}set_cookies(t){this.cookies=Mt(t).join("; ")}prepare_return_obj(){return{config:this.config,predict:this.predict,submit:this.submit,view_api:this.view_api,component_server:this.component_server}}async connect_ws(t){return new Promise(((e,n)=>{let r;try{r=new WebSocket(t)}catch(e){return void(this.ws_map[t]="failed")}r.onopen=()=>{e()},r.onerror=n=>{console.error("WebSocket error:",n),this.close_ws(t),this.ws_map[t]="failed",e()},r.onclose=()=>{delete this.ws_map[t],this.ws_map[t]="failed"},r.onmessage=t=>{},this.ws_map[t]=r}))}async send_ws_message(t,e){t in this.ws_map||await this.connect_ws(t);const n=this.ws_map[t];n instanceof WebSocket?n.send(JSON.stringify(e)):this.post_data(t,e)}async close_ws(t){if(t in this.ws_map){const e=this.ws_map[t];e instanceof WebSocket&&(e.close(),delete this.ws_map[t])}}}async function ke(t,e={events:["data"]}){return await xe.connect(t,e)}async function $e(t,e){return await xe.duplicate(t,e)}export{xe as Client,ee as FileData,ke as client,$e as duplicate,se as handle_file,fe as predict,te as prepare_files,Re as submit,Xt as upload,Qt as upload_files};export default null;
8
+ //# sourceMappingURL=/sm/4d92fd0f682c91bdc5f31b37e659b6bd0cf5b64ff6c4edc2d0c0cb3f4bfa1850.map
libs/three.js/0.172.0/jsm/Addons.js ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export * from './animation/AnimationClipCreator.js';
2
+ export * from './animation/CCDIKSolver.js';
3
+
4
+ export { default as WebGL } from './capabilities/WebGL.js';
5
+
6
+ export * from './controls/ArcballControls.js';
7
+ export * from './controls/DragControls.js';
8
+ export * from './controls/FirstPersonControls.js';
9
+ export * from './controls/FlyControls.js';
10
+ export * from './controls/MapControls.js';
11
+ export * from './controls/OrbitControls.js';
12
+ export * from './controls/PointerLockControls.js';
13
+ export * from './controls/TrackballControls.js';
14
+ export * from './controls/TransformControls.js';
15
+
16
+ export * from './csm/CSM.js';
17
+ export * from './csm/CSMFrustum.js';
18
+ export * from './csm/CSMHelper.js';
19
+ export * from './csm/CSMShader.js';
20
+
21
+ export * as Curves from './curves/CurveExtras.js';
22
+ export * from './curves/NURBSCurve.js';
23
+ export * from './curves/NURBSSurface.js';
24
+ export * from './curves/NURBSVolume.js';
25
+ export * as NURBSUtils from './curves/NURBSUtils.js';
26
+
27
+ export * from './effects/AnaglyphEffect.js';
28
+ export * from './effects/AsciiEffect.js';
29
+ export * from './effects/OutlineEffect.js';
30
+ export * from './effects/ParallaxBarrierEffect.js';
31
+ export * from './effects/PeppersGhostEffect.js';
32
+ export * from './effects/StereoEffect.js';
33
+
34
+ export * from './environments/DebugEnvironment.js';
35
+ export * from './environments/RoomEnvironment.js';
36
+
37
+ export * from './exporters/DRACOExporter.js';
38
+ export * from './exporters/EXRExporter.js';
39
+ export * from './exporters/GLTFExporter.js';
40
+ export * from './exporters/KTX2Exporter.js';
41
+ export * from './exporters/OBJExporter.js';
42
+ export * from './exporters/PLYExporter.js';
43
+ export * from './exporters/STLExporter.js';
44
+ export * from './exporters/USDZExporter.js';
45
+
46
+ export * from './geometries/BoxLineGeometry.js';
47
+ export * from './geometries/ConvexGeometry.js';
48
+ export * from './geometries/DecalGeometry.js';
49
+ export * from './geometries/ParametricGeometries.js';
50
+ export * from './geometries/ParametricGeometry.js';
51
+ export * from './geometries/RoundedBoxGeometry.js';
52
+ export * from './geometries/TeapotGeometry.js';
53
+ export * from './geometries/TextGeometry.js';
54
+
55
+ export * from './helpers/LightProbeHelper.js';
56
+ export * from './helpers/OctreeHelper.js';
57
+ export * from './helpers/PositionalAudioHelper.js';
58
+ export * from './helpers/RectAreaLightHelper.js';
59
+ export * from './helpers/TextureHelper.js';
60
+ export * from './helpers/VertexNormalsHelper.js';
61
+ export * from './helpers/VertexTangentsHelper.js';
62
+ export * from './helpers/ViewHelper.js';
63
+
64
+ export * from './interactive/HTMLMesh.js';
65
+ export * from './interactive/InteractiveGroup.js';
66
+ export * from './interactive/SelectionBox.js';
67
+ export * from './interactive/SelectionHelper.js';
68
+
69
+ export * from './lights/LightProbeGenerator.js';
70
+ export * from './lights/RectAreaLightTexturesLib.js';
71
+ export * from './lights/RectAreaLightUniformsLib.js';
72
+
73
+ export * from './lines/Line2.js';
74
+ export * from './lines/LineGeometry.js';
75
+ export * from './lines/LineMaterial.js';
76
+ export * from './lines/LineSegments2.js';
77
+ export * from './lines/LineSegmentsGeometry.js';
78
+ export * from './lines/Wireframe.js';
79
+ export * from './lines/WireframeGeometry2.js';
80
+
81
+ export * from './loaders/3DMLoader.js';
82
+ export * from './loaders/3MFLoader.js';
83
+ export * from './loaders/AMFLoader.js';
84
+ export * from './loaders/BVHLoader.js';
85
+ export * from './loaders/ColladaLoader.js';
86
+ export * from './loaders/DDSLoader.js';
87
+ export * from './loaders/DRACOLoader.js';
88
+ export * from './loaders/EXRLoader.js';
89
+ export * from './loaders/FBXLoader.js';
90
+ export * from './loaders/FontLoader.js';
91
+ export * from './loaders/GCodeLoader.js';
92
+ export * from './loaders/GLTFLoader.js';
93
+ export * from './loaders/HDRCubeTextureLoader.js';
94
+ export * from './loaders/IESLoader.js';
95
+ export * from './loaders/KMZLoader.js';
96
+ export * from './loaders/KTX2Loader.js';
97
+ export * from './loaders/KTXLoader.js';
98
+ export * from './loaders/LDrawLoader.js';
99
+ export * from './loaders/LUT3dlLoader.js';
100
+ export * from './loaders/LUTCubeLoader.js';
101
+ export * from './loaders/LWOLoader.js';
102
+ export * from './loaders/LottieLoader.js';
103
+ export * from './loaders/MD2Loader.js';
104
+ export * from './loaders/MDDLoader.js';
105
+ export * from './loaders/MTLLoader.js';
106
+ export * from './loaders/NRRDLoader.js';
107
+ export * from './loaders/OBJLoader.js';
108
+ export * from './loaders/PCDLoader.js';
109
+ export * from './loaders/PDBLoader.js';
110
+ export * from './loaders/PLYLoader.js';
111
+ export * from './loaders/PVRLoader.js';
112
+ export * from './loaders/RGBELoader.js';
113
+ export * from './loaders/UltraHDRLoader.js';
114
+ export * from './loaders/RGBMLoader.js';
115
+ export * from './loaders/STLLoader.js';
116
+ export * from './loaders/SVGLoader.js';
117
+ export * from './loaders/TDSLoader.js';
118
+ export * from './loaders/TGALoader.js';
119
+ export * from './loaders/TIFFLoader.js';
120
+ export * from './loaders/TTFLoader.js';
121
+ export * from './loaders/USDZLoader.js';
122
+ export * from './loaders/VOXLoader.js';
123
+ export * from './loaders/VRMLLoader.js';
124
+ export * from './loaders/VTKLoader.js';
125
+ export * from './loaders/XYZLoader.js';
126
+
127
+ export * from './materials/MeshGouraudMaterial.js';
128
+ export * from './materials/LDrawConditionalLineMaterial.js';
129
+ export * from './materials/MeshPostProcessingMaterial.js';
130
+
131
+ export * from './math/Capsule.js';
132
+ export * from './math/ColorConverter.js';
133
+ export * from './math/ConvexHull.js';
134
+ export * from './math/ImprovedNoise.js';
135
+ export * from './math/Lut.js';
136
+ export * from './math/MeshSurfaceSampler.js';
137
+ export * from './math/OBB.js';
138
+ export * from './math/Octree.js';
139
+ export * from './math/SimplexNoise.js';
140
+
141
+ export * from './misc/ConvexObjectBreaker.js';
142
+ export * from './misc/GPUComputationRenderer.js';
143
+ export * from './misc/Gyroscope.js';
144
+ export * from './misc/MD2Character.js';
145
+ export * from './misc/MD2CharacterComplex.js';
146
+ export * from './misc/MorphAnimMesh.js';
147
+ export * from './misc/MorphBlendMesh.js';
148
+ export * from './misc/ProgressiveLightMap.js';
149
+ export * from './misc/RollerCoaster.js';
150
+ export * from './misc/Timer.js';
151
+ export * from './misc/TubePainter.js';
152
+ export * from './misc/Volume.js';
153
+ export * from './misc/VolumeSlice.js';
154
+
155
+ export * from './modifiers/CurveModifier.js';
156
+ export * from './modifiers/EdgeSplitModifier.js';
157
+ export * from './modifiers/SimplifyModifier.js';
158
+ export * from './modifiers/TessellateModifier.js';
159
+
160
+ export * from './objects/GroundedSkybox.js';
161
+ export * from './objects/Lensflare.js';
162
+ export * from './objects/MarchingCubes.js';
163
+ export * from './objects/Reflector.js';
164
+ export * from './objects/ReflectorForSSRPass.js';
165
+ export * from './objects/Refractor.js';
166
+ export * from './objects/ShadowMesh.js';
167
+ export * from './objects/Sky.js';
168
+ export * from './objects/Water.js';
169
+ export { Water as Water2 } from './objects/Water2.js';
170
+
171
+ export * from './physics/AmmoPhysics.js';
172
+ export * from './physics/RapierPhysics.js';
173
+
174
+ export * from './postprocessing/AfterimagePass.js';
175
+ export * from './postprocessing/BloomPass.js';
176
+ export * from './postprocessing/BokehPass.js';
177
+ export * from './postprocessing/ClearPass.js';
178
+ export * from './postprocessing/CubeTexturePass.js';
179
+ export * from './postprocessing/DotScreenPass.js';
180
+ export * from './postprocessing/EffectComposer.js';
181
+ export * from './postprocessing/FilmPass.js';
182
+ export * from './postprocessing/GlitchPass.js';
183
+ export * from './postprocessing/GTAOPass.js';
184
+ export * from './postprocessing/HalftonePass.js';
185
+ export * from './postprocessing/LUTPass.js';
186
+ export * from './postprocessing/MaskPass.js';
187
+ export * from './postprocessing/OutlinePass.js';
188
+ export * from './postprocessing/OutputPass.js';
189
+ export * from './postprocessing/Pass.js';
190
+ export * from './postprocessing/RenderPass.js';
191
+ export * from './postprocessing/RenderPixelatedPass.js';
192
+ export * from './postprocessing/SAOPass.js';
193
+ export * from './postprocessing/SMAAPass.js';
194
+ export * from './postprocessing/SSAARenderPass.js';
195
+ export * from './postprocessing/SSAOPass.js';
196
+ export * from './postprocessing/SSRPass.js';
197
+ export * from './postprocessing/SavePass.js';
198
+ export * from './postprocessing/ShaderPass.js';
199
+ export * from './postprocessing/TAARenderPass.js';
200
+ export * from './postprocessing/TexturePass.js';
201
+ export * from './postprocessing/UnrealBloomPass.js';
202
+
203
+ export * from './renderers/CSS2DRenderer.js';
204
+ export * from './renderers/CSS3DRenderer.js';
205
+ export * from './renderers/Projector.js';
206
+ export * from './renderers/SVGRenderer.js';
207
+
208
+ export * from './shaders/ACESFilmicToneMappingShader.js';
209
+ export * from './shaders/AfterimageShader.js';
210
+ export * from './shaders/BasicShader.js';
211
+ export * from './shaders/BleachBypassShader.js';
212
+ export * from './shaders/BlendShader.js';
213
+ export * from './shaders/BokehShader.js';
214
+ export { BokehShader as BokehShader2 } from './shaders/BokehShader2.js';
215
+ export * from './shaders/BrightnessContrastShader.js';
216
+ export * from './shaders/ColorCorrectionShader.js';
217
+ export * from './shaders/ColorifyShader.js';
218
+ export * from './shaders/ConvolutionShader.js';
219
+ export * from './shaders/CopyShader.js';
220
+ export * from './shaders/DOFMipMapShader.js';
221
+ export * from './shaders/DepthLimitedBlurShader.js';
222
+ export * from './shaders/DigitalGlitch.js';
223
+ export * from './shaders/DotScreenShader.js';
224
+ export * from './shaders/ExposureShader.js';
225
+ export * from './shaders/FXAAShader.js';
226
+ export * from './shaders/FilmShader.js';
227
+ export * from './shaders/FocusShader.js';
228
+ export * from './shaders/FreiChenShader.js';
229
+ export * from './shaders/GammaCorrectionShader.js';
230
+ export * from './shaders/GodRaysShader.js';
231
+ export * from './shaders/GTAOShader.js';
232
+ export * from './shaders/HalftoneShader.js';
233
+ export * from './shaders/HorizontalBlurShader.js';
234
+ export * from './shaders/HorizontalTiltShiftShader.js';
235
+ export * from './shaders/HueSaturationShader.js';
236
+ export * from './shaders/KaleidoShader.js';
237
+ export * from './shaders/LuminosityHighPassShader.js';
238
+ export * from './shaders/LuminosityShader.js';
239
+ export * from './shaders/MirrorShader.js';
240
+ export * from './shaders/NormalMapShader.js';
241
+ export * from './shaders/OutputShader.js';
242
+ export * from './shaders/RGBShiftShader.js';
243
+ export * from './shaders/SAOShader.js';
244
+ export * from './shaders/SMAAShader.js';
245
+ export * from './shaders/SSAOShader.js';
246
+ export * from './shaders/SSRShader.js';
247
+ export * from './shaders/SepiaShader.js';
248
+ export * from './shaders/SobelOperatorShader.js';
249
+ export * from './shaders/SubsurfaceScatteringShader.js';
250
+ export * from './shaders/TechnicolorShader.js';
251
+ export * from './shaders/ToonShader.js';
252
+ export * from './shaders/TriangleBlurShader.js';
253
+ export * from './shaders/UnpackDepthRGBAShader.js';
254
+ export * from './shaders/VelocityShader.js';
255
+ export * from './shaders/VerticalBlurShader.js';
256
+ export * from './shaders/VerticalTiltShiftShader.js';
257
+ export * from './shaders/VignetteShader.js';
258
+ export * from './shaders/VolumeShader.js';
259
+ export * from './shaders/WaterRefractionShader.js';
260
+
261
+ export * from './textures/FlakesTexture.js';
262
+
263
+ export * as BufferGeometryUtils from './utils/BufferGeometryUtils.js';
264
+ export * as CameraUtils from './utils/CameraUtils.js';
265
+ export * as GeometryCompressionUtils from './utils/GeometryCompressionUtils.js';
266
+ export * as GeometryUtils from './utils/GeometryUtils.js';
267
+ export * from './utils/LDrawUtils.js';
268
+ export * as SceneUtils from './utils/SceneUtils.js';
269
+ export * from './utils/ShadowMapViewer.js';
270
+ export * as SkeletonUtils from './utils/SkeletonUtils.js';
271
+ export * as SortUtils from './utils/SortUtils.js';
272
+ export * from './utils/WebGLTextureUtils.js';
273
+ export * from './utils/UVsDebug.js';
274
+ export * from './utils/WorkerPool.js';
275
+
276
+ export * from './webxr/ARButton.js';
277
+ export * from './webxr/OculusHandModel.js';
278
+ export * from './webxr/OculusHandPointerModel.js';
279
+ export * from './webxr/Text2D.js';
280
+ export * from './webxr/VRButton.js';
281
+ export * from './webxr/XRButton.js';
282
+ export * from './webxr/XRControllerModelFactory.js';
283
+ export * from './webxr/XREstimatedLight.js';
284
+ export * from './webxr/XRHandMeshModel.js';
285
+ export * from './webxr/XRHandModelFactory.js';
286
+ export * from './webxr/XRHandPrimitiveModel.js';
287
+ export * from './webxr/XRPlanes.js';
libs/three.js/0.172.0/jsm/animation/AnimationClipCreator.js ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ AnimationClip,
3
+ BooleanKeyframeTrack,
4
+ ColorKeyframeTrack,
5
+ NumberKeyframeTrack,
6
+ Vector3,
7
+ VectorKeyframeTrack
8
+ } from 'three';
9
+
10
+ class AnimationClipCreator {
11
+
12
+ static CreateRotationAnimation( period, axis = 'x' ) {
13
+
14
+ const times = [ 0, period ], values = [ 0, 360 ];
15
+
16
+ const trackName = '.rotation[' + axis + ']';
17
+
18
+ const track = new NumberKeyframeTrack( trackName, times, values );
19
+
20
+ return new AnimationClip( null, period, [ track ] );
21
+
22
+ }
23
+
24
+ static CreateScaleAxisAnimation( period, axis = 'x' ) {
25
+
26
+ const times = [ 0, period ], values = [ 0, 1 ];
27
+
28
+ const trackName = '.scale[' + axis + ']';
29
+
30
+ const track = new NumberKeyframeTrack( trackName, times, values );
31
+
32
+ return new AnimationClip( null, period, [ track ] );
33
+
34
+ }
35
+
36
+ static CreateShakeAnimation( duration, shakeScale ) {
37
+
38
+ const times = [], values = [], tmp = new Vector3();
39
+
40
+ for ( let i = 0; i < duration * 10; i ++ ) {
41
+
42
+ times.push( i / 10 );
43
+
44
+ tmp.set( Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0 ).
45
+ multiply( shakeScale ).
46
+ toArray( values, values.length );
47
+
48
+ }
49
+
50
+ const trackName = '.position';
51
+
52
+ const track = new VectorKeyframeTrack( trackName, times, values );
53
+
54
+ return new AnimationClip( null, duration, [ track ] );
55
+
56
+ }
57
+
58
+ static CreatePulsationAnimation( duration, pulseScale ) {
59
+
60
+ const times = [], values = [], tmp = new Vector3();
61
+
62
+ for ( let i = 0; i < duration * 10; i ++ ) {
63
+
64
+ times.push( i / 10 );
65
+
66
+ const scaleFactor = Math.random() * pulseScale;
67
+ tmp.set( scaleFactor, scaleFactor, scaleFactor ).
68
+ toArray( values, values.length );
69
+
70
+ }
71
+
72
+ const trackName = '.scale';
73
+
74
+ const track = new VectorKeyframeTrack( trackName, times, values );
75
+
76
+ return new AnimationClip( null, duration, [ track ] );
77
+
78
+ }
79
+
80
+ static CreateVisibilityAnimation( duration ) {
81
+
82
+ const times = [ 0, duration / 2, duration ], values = [ true, false, true ];
83
+
84
+ const trackName = '.visible';
85
+
86
+ const track = new BooleanKeyframeTrack( trackName, times, values );
87
+
88
+ return new AnimationClip( null, duration, [ track ] );
89
+
90
+ }
91
+
92
+ static CreateMaterialColorAnimation( duration, colors ) {
93
+
94
+ const times = [], values = [],
95
+ timeStep = ( colors.length > 1 ) ? duration / ( colors.length - 1 ) : 0;
96
+
97
+ for ( let i = 0; i < colors.length; i ++ ) {
98
+
99
+ times.push( i * timeStep );
100
+
101
+ const color = colors[ i ];
102
+ values.push( color.r, color.g, color.b );
103
+
104
+ }
105
+
106
+ const trackName = '.material.color';
107
+
108
+ const track = new ColorKeyframeTrack( trackName, times, values );
109
+
110
+ return new AnimationClip( null, duration, [ track ] );
111
+
112
+ }
113
+
114
+ }
115
+
116
+ export { AnimationClipCreator };
libs/three.js/0.172.0/jsm/animation/CCDIKSolver.js ADDED
@@ -0,0 +1,485 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ BufferAttribute,
3
+ BufferGeometry,
4
+ Color,
5
+ Line,
6
+ LineBasicMaterial,
7
+ Matrix4,
8
+ Mesh,
9
+ MeshBasicMaterial,
10
+ Object3D,
11
+ Quaternion,
12
+ SphereGeometry,
13
+ Vector3
14
+ } from 'three';
15
+
16
+ const _q = new Quaternion();
17
+ const _targetPos = new Vector3();
18
+ const _targetVec = new Vector3();
19
+ const _effectorPos = new Vector3();
20
+ const _effectorVec = new Vector3();
21
+ const _linkPos = new Vector3();
22
+ const _invLinkQ = new Quaternion();
23
+ const _linkScale = new Vector3();
24
+ const _axis = new Vector3();
25
+ const _vector = new Vector3();
26
+ const _matrix = new Matrix4();
27
+
28
+
29
+ /**
30
+ * CCD Algorithm
31
+ * - https://web.archive.org/web/20221206080850/https://sites.google.com/site/auraliusproject/ccd-algorithm
32
+ *
33
+ * // ik parameter example
34
+ * //
35
+ * // target, effector, index in links are bone index in skeleton.bones.
36
+ * // the bones relation should be
37
+ * // <-- parent child -->
38
+ * // links[ n ], links[ n - 1 ], ..., links[ 0 ], effector
39
+ * iks = [ {
40
+ * target: 1,
41
+ * effector: 2,
42
+ * links: [ { index: 5, limitation: new Vector3( 1, 0, 0 ) }, { index: 4, enabled: false }, { index : 3 } ],
43
+ * iteration: 10,
44
+ * minAngle: 0.0,
45
+ * maxAngle: 1.0,
46
+ * } ];
47
+ */
48
+
49
+ class CCDIKSolver {
50
+
51
+ /**
52
+ * @param {THREE.SkinnedMesh} mesh
53
+ * @param {Array<Object>} iks
54
+ */
55
+ constructor( mesh, iks = [] ) {
56
+
57
+ this.mesh = mesh;
58
+ this.iks = iks;
59
+
60
+ this._valid();
61
+
62
+ }
63
+
64
+ /**
65
+ * Update all IK bones.
66
+ *
67
+ * @return {CCDIKSolver}
68
+ */
69
+ update() {
70
+
71
+ const iks = this.iks;
72
+
73
+ for ( let i = 0, il = iks.length; i < il; i ++ ) {
74
+
75
+ this.updateOne( iks[ i ] );
76
+
77
+ }
78
+
79
+ return this;
80
+
81
+ }
82
+
83
+ /**
84
+ * Update one IK bone
85
+ *
86
+ * @param {Object} ik parameter
87
+ * @return {CCDIKSolver}
88
+ */
89
+ updateOne( ik ) {
90
+
91
+ const bones = this.mesh.skeleton.bones;
92
+
93
+ // for reference overhead reduction in loop
94
+ const math = Math;
95
+
96
+ const effector = bones[ ik.effector ];
97
+ const target = bones[ ik.target ];
98
+
99
+ // don't use getWorldPosition() here for the performance
100
+ // because it calls updateMatrixWorld( true ) inside.
101
+ _targetPos.setFromMatrixPosition( target.matrixWorld );
102
+
103
+ const links = ik.links;
104
+ const iteration = ik.iteration !== undefined ? ik.iteration : 1;
105
+
106
+ for ( let i = 0; i < iteration; i ++ ) {
107
+
108
+ let rotated = false;
109
+
110
+ for ( let j = 0, jl = links.length; j < jl; j ++ ) {
111
+
112
+ const link = bones[ links[ j ].index ];
113
+
114
+ // skip this link and following links
115
+ if ( links[ j ].enabled === false ) break;
116
+
117
+ const limitation = links[ j ].limitation;
118
+ const rotationMin = links[ j ].rotationMin;
119
+ const rotationMax = links[ j ].rotationMax;
120
+
121
+ // don't use getWorldPosition/Quaternion() here for the performance
122
+ // because they call updateMatrixWorld( true ) inside.
123
+ link.matrixWorld.decompose( _linkPos, _invLinkQ, _linkScale );
124
+ _invLinkQ.invert();
125
+ _effectorPos.setFromMatrixPosition( effector.matrixWorld );
126
+
127
+ // work in link world
128
+ _effectorVec.subVectors( _effectorPos, _linkPos );
129
+ _effectorVec.applyQuaternion( _invLinkQ );
130
+ _effectorVec.normalize();
131
+
132
+ _targetVec.subVectors( _targetPos, _linkPos );
133
+ _targetVec.applyQuaternion( _invLinkQ );
134
+ _targetVec.normalize();
135
+
136
+ let angle = _targetVec.dot( _effectorVec );
137
+
138
+ if ( angle > 1.0 ) {
139
+
140
+ angle = 1.0;
141
+
142
+ } else if ( angle < - 1.0 ) {
143
+
144
+ angle = - 1.0;
145
+
146
+ }
147
+
148
+ angle = math.acos( angle );
149
+
150
+ // skip if changing angle is too small to prevent vibration of bone
151
+ if ( angle < 1e-5 ) continue;
152
+
153
+ if ( ik.minAngle !== undefined && angle < ik.minAngle ) {
154
+
155
+ angle = ik.minAngle;
156
+
157
+ }
158
+
159
+ if ( ik.maxAngle !== undefined && angle > ik.maxAngle ) {
160
+
161
+ angle = ik.maxAngle;
162
+
163
+ }
164
+
165
+ _axis.crossVectors( _effectorVec, _targetVec );
166
+ _axis.normalize();
167
+
168
+ _q.setFromAxisAngle( _axis, angle );
169
+ link.quaternion.multiply( _q );
170
+
171
+ // TODO: re-consider the limitation specification
172
+ if ( limitation !== undefined ) {
173
+
174
+ let c = link.quaternion.w;
175
+
176
+ if ( c > 1.0 ) c = 1.0;
177
+
178
+ const c2 = math.sqrt( 1 - c * c );
179
+ link.quaternion.set( limitation.x * c2,
180
+ limitation.y * c2,
181
+ limitation.z * c2,
182
+ c );
183
+
184
+ }
185
+
186
+ if ( rotationMin !== undefined ) {
187
+
188
+ link.rotation.setFromVector3( _vector.setFromEuler( link.rotation ).max( rotationMin ) );
189
+
190
+ }
191
+
192
+ if ( rotationMax !== undefined ) {
193
+
194
+ link.rotation.setFromVector3( _vector.setFromEuler( link.rotation ).min( rotationMax ) );
195
+
196
+ }
197
+
198
+ link.updateMatrixWorld( true );
199
+
200
+ rotated = true;
201
+
202
+ }
203
+
204
+ if ( ! rotated ) break;
205
+
206
+ }
207
+
208
+ return this;
209
+
210
+ }
211
+
212
+ /**
213
+ * Creates Helper
214
+ *
215
+ * @param {number} sphereSize
216
+ * @return {CCDIKHelper}
217
+ */
218
+ createHelper( sphereSize ) {
219
+
220
+ return new CCDIKHelper( this.mesh, this.iks, sphereSize );
221
+
222
+ }
223
+
224
+ // private methods
225
+
226
+ _valid() {
227
+
228
+ const iks = this.iks;
229
+ const bones = this.mesh.skeleton.bones;
230
+
231
+ for ( let i = 0, il = iks.length; i < il; i ++ ) {
232
+
233
+ const ik = iks[ i ];
234
+ const effector = bones[ ik.effector ];
235
+ const links = ik.links;
236
+ let link0, link1;
237
+
238
+ link0 = effector;
239
+
240
+ for ( let j = 0, jl = links.length; j < jl; j ++ ) {
241
+
242
+ link1 = bones[ links[ j ].index ];
243
+
244
+ if ( link0.parent !== link1 ) {
245
+
246
+ console.warn( 'THREE.CCDIKSolver: bone ' + link0.name + ' is not the child of bone ' + link1.name );
247
+
248
+ }
249
+
250
+ link0 = link1;
251
+
252
+ }
253
+
254
+ }
255
+
256
+ }
257
+
258
+ }
259
+
260
+ function getPosition( bone, matrixWorldInv ) {
261
+
262
+ return _vector
263
+ .setFromMatrixPosition( bone.matrixWorld )
264
+ .applyMatrix4( matrixWorldInv );
265
+
266
+ }
267
+
268
+ function setPositionOfBoneToAttributeArray( array, index, bone, matrixWorldInv ) {
269
+
270
+ const v = getPosition( bone, matrixWorldInv );
271
+
272
+ array[ index * 3 + 0 ] = v.x;
273
+ array[ index * 3 + 1 ] = v.y;
274
+ array[ index * 3 + 2 ] = v.z;
275
+
276
+ }
277
+
278
+ /**
279
+ * Visualize IK bones
280
+ *
281
+ * @param {SkinnedMesh} mesh
282
+ * @param {Array<Object>} iks
283
+ * @param {number} sphereSize
284
+ */
285
+ class CCDIKHelper extends Object3D {
286
+
287
+ constructor( mesh, iks = [], sphereSize = 0.25 ) {
288
+
289
+ super();
290
+
291
+ this.root = mesh;
292
+ this.iks = iks;
293
+
294
+ this.matrix.copy( mesh.matrixWorld );
295
+ this.matrixAutoUpdate = false;
296
+
297
+ this.sphereGeometry = new SphereGeometry( sphereSize, 16, 8 );
298
+
299
+ this.targetSphereMaterial = new MeshBasicMaterial( {
300
+ color: new Color( 0xff8888 ),
301
+ depthTest: false,
302
+ depthWrite: false,
303
+ transparent: true
304
+ } );
305
+
306
+ this.effectorSphereMaterial = new MeshBasicMaterial( {
307
+ color: new Color( 0x88ff88 ),
308
+ depthTest: false,
309
+ depthWrite: false,
310
+ transparent: true
311
+ } );
312
+
313
+ this.linkSphereMaterial = new MeshBasicMaterial( {
314
+ color: new Color( 0x8888ff ),
315
+ depthTest: false,
316
+ depthWrite: false,
317
+ transparent: true
318
+ } );
319
+
320
+ this.lineMaterial = new LineBasicMaterial( {
321
+ color: new Color( 0xff0000 ),
322
+ depthTest: false,
323
+ depthWrite: false,
324
+ transparent: true
325
+ } );
326
+
327
+ this._init();
328
+
329
+ }
330
+
331
+ /**
332
+ * Updates IK bones visualization.
333
+ *
334
+ * @param {Boolean} force
335
+ */
336
+ updateMatrixWorld( force ) {
337
+
338
+ const mesh = this.root;
339
+
340
+ if ( this.visible ) {
341
+
342
+ let offset = 0;
343
+
344
+ const iks = this.iks;
345
+ const bones = mesh.skeleton.bones;
346
+
347
+ _matrix.copy( mesh.matrixWorld ).invert();
348
+
349
+ for ( let i = 0, il = iks.length; i < il; i ++ ) {
350
+
351
+ const ik = iks[ i ];
352
+
353
+ const targetBone = bones[ ik.target ];
354
+ const effectorBone = bones[ ik.effector ];
355
+
356
+ const targetMesh = this.children[ offset ++ ];
357
+ const effectorMesh = this.children[ offset ++ ];
358
+
359
+ targetMesh.position.copy( getPosition( targetBone, _matrix ) );
360
+ effectorMesh.position.copy( getPosition( effectorBone, _matrix ) );
361
+
362
+ for ( let j = 0, jl = ik.links.length; j < jl; j ++ ) {
363
+
364
+ const link = ik.links[ j ];
365
+ const linkBone = bones[ link.index ];
366
+
367
+ const linkMesh = this.children[ offset ++ ];
368
+
369
+ linkMesh.position.copy( getPosition( linkBone, _matrix ) );
370
+
371
+ }
372
+
373
+ const line = this.children[ offset ++ ];
374
+ const array = line.geometry.attributes.position.array;
375
+
376
+ setPositionOfBoneToAttributeArray( array, 0, targetBone, _matrix );
377
+ setPositionOfBoneToAttributeArray( array, 1, effectorBone, _matrix );
378
+
379
+ for ( let j = 0, jl = ik.links.length; j < jl; j ++ ) {
380
+
381
+ const link = ik.links[ j ];
382
+ const linkBone = bones[ link.index ];
383
+ setPositionOfBoneToAttributeArray( array, j + 2, linkBone, _matrix );
384
+
385
+ }
386
+
387
+ line.geometry.attributes.position.needsUpdate = true;
388
+
389
+ }
390
+
391
+ }
392
+
393
+ this.matrix.copy( mesh.matrixWorld );
394
+
395
+ super.updateMatrixWorld( force );
396
+
397
+ }
398
+
399
+ /**
400
+ * Frees the GPU-related resources allocated by this instance. Call this method whenever this instance is no longer used in your app.
401
+ */
402
+ dispose() {
403
+
404
+ this.sphereGeometry.dispose();
405
+
406
+ this.targetSphereMaterial.dispose();
407
+ this.effectorSphereMaterial.dispose();
408
+ this.linkSphereMaterial.dispose();
409
+ this.lineMaterial.dispose();
410
+
411
+ const children = this.children;
412
+
413
+ for ( let i = 0; i < children.length; i ++ ) {
414
+
415
+ const child = children[ i ];
416
+
417
+ if ( child.isLine ) child.geometry.dispose();
418
+
419
+ }
420
+
421
+ }
422
+
423
+ // private method
424
+
425
+ _init() {
426
+
427
+ const scope = this;
428
+ const iks = this.iks;
429
+
430
+ function createLineGeometry( ik ) {
431
+
432
+ const geometry = new BufferGeometry();
433
+ const vertices = new Float32Array( ( 2 + ik.links.length ) * 3 );
434
+ geometry.setAttribute( 'position', new BufferAttribute( vertices, 3 ) );
435
+
436
+ return geometry;
437
+
438
+ }
439
+
440
+ function createTargetMesh() {
441
+
442
+ return new Mesh( scope.sphereGeometry, scope.targetSphereMaterial );
443
+
444
+ }
445
+
446
+ function createEffectorMesh() {
447
+
448
+ return new Mesh( scope.sphereGeometry, scope.effectorSphereMaterial );
449
+
450
+ }
451
+
452
+ function createLinkMesh() {
453
+
454
+ return new Mesh( scope.sphereGeometry, scope.linkSphereMaterial );
455
+
456
+ }
457
+
458
+ function createLine( ik ) {
459
+
460
+ return new Line( createLineGeometry( ik ), scope.lineMaterial );
461
+
462
+ }
463
+
464
+ for ( let i = 0, il = iks.length; i < il; i ++ ) {
465
+
466
+ const ik = iks[ i ];
467
+
468
+ this.add( createTargetMesh() );
469
+ this.add( createEffectorMesh() );
470
+
471
+ for ( let j = 0, jl = ik.links.length; j < jl; j ++ ) {
472
+
473
+ this.add( createLinkMesh() );
474
+
475
+ }
476
+
477
+ this.add( createLine( ik ) );
478
+
479
+ }
480
+
481
+ }
482
+
483
+ }
484
+
485
+ export { CCDIKSolver, CCDIKHelper };
libs/three.js/0.172.0/jsm/capabilities/WebGL.js ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class WebGL {
2
+
3
+ static isWebGL2Available() {
4
+
5
+ try {
6
+
7
+ const canvas = document.createElement( 'canvas' );
8
+ return !! ( window.WebGL2RenderingContext && canvas.getContext( 'webgl2' ) );
9
+
10
+ } catch ( e ) {
11
+
12
+ return false;
13
+
14
+ }
15
+
16
+ }
17
+
18
+ static isColorSpaceAvailable( colorSpace ) {
19
+
20
+ try {
21
+
22
+ const canvas = document.createElement( 'canvas' );
23
+ const ctx = window.WebGL2RenderingContext && canvas.getContext( 'webgl2' );
24
+ ctx.drawingBufferColorSpace = colorSpace;
25
+ return ctx.drawingBufferColorSpace === colorSpace; // deepscan-disable-line SAME_OPERAND_VALUE
26
+
27
+ } catch ( e ) {
28
+
29
+ return false;
30
+
31
+ }
32
+
33
+ }
34
+
35
+ static getWebGL2ErrorMessage() {
36
+
37
+ return this.getErrorMessage( 2 );
38
+
39
+ }
40
+
41
+ static getErrorMessage( version ) {
42
+
43
+ const names = {
44
+ 1: 'WebGL',
45
+ 2: 'WebGL 2'
46
+ };
47
+
48
+ const contexts = {
49
+ 1: window.WebGLRenderingContext,
50
+ 2: window.WebGL2RenderingContext
51
+ };
52
+
53
+ let message = 'Your $0 does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation" style="color:#000">$1</a>';
54
+
55
+ const element = document.createElement( 'div' );
56
+ element.id = 'webglmessage';
57
+ element.style.fontFamily = 'monospace';
58
+ element.style.fontSize = '13px';
59
+ element.style.fontWeight = 'normal';
60
+ element.style.textAlign = 'center';
61
+ element.style.background = '#fff';
62
+ element.style.color = '#000';
63
+ element.style.padding = '1.5em';
64
+ element.style.width = '400px';
65
+ element.style.margin = '5em auto 0';
66
+
67
+ if ( contexts[ version ] ) {
68
+
69
+ message = message.replace( '$0', 'graphics card' );
70
+
71
+ } else {
72
+
73
+ message = message.replace( '$0', 'browser' );
74
+
75
+ }
76
+
77
+ message = message.replace( '$1', names[ version ] );
78
+
79
+ element.innerHTML = message;
80
+
81
+ return element;
82
+
83
+ }
84
+
85
+ // @deprecated, r168
86
+
87
+ static isWebGLAvailable() {
88
+
89
+ console.warn( 'isWebGLAvailable() has been deprecated and will be removed in r178. Use isWebGL2Available() instead.' );
90
+
91
+ try {
92
+
93
+ const canvas = document.createElement( 'canvas' );
94
+ return !! ( window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ) );
95
+
96
+ } catch ( e ) {
97
+
98
+ return false;
99
+
100
+ }
101
+
102
+ }
103
+
104
+ static getWebGLErrorMessage() {
105
+
106
+ console.warn( 'getWebGLErrorMessage() has been deprecated and will be removed in r178. Use getWebGL2ErrorMessage() instead.' );
107
+
108
+ return this.getErrorMessage( 1 );
109
+
110
+ }
111
+
112
+ }
113
+
114
+ export default WebGL;
libs/three.js/0.172.0/jsm/capabilities/WebGPU.js ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ let isAvailable = ( typeof navigator !== 'undefined' && navigator.gpu !== undefined );
2
+
3
+ if ( typeof window !== 'undefined' && isAvailable ) {
4
+
5
+ isAvailable = await navigator.gpu.requestAdapter();
6
+
7
+ }
8
+
9
+ class WebGPU {
10
+
11
+ static isAvailable() {
12
+
13
+ return Boolean( isAvailable );
14
+
15
+ }
16
+
17
+ static getStaticAdapter() {
18
+
19
+ return isAvailable;
20
+
21
+ }
22
+
23
+ static getErrorMessage() {
24
+
25
+ const message = 'Your browser does not support <a href="https://gpuweb.github.io/gpuweb/" style="color:blue">WebGPU</a> yet';
26
+
27
+ const element = document.createElement( 'div' );
28
+ element.id = 'webgpumessage';
29
+ element.style.fontFamily = 'monospace';
30
+ element.style.fontSize = '13px';
31
+ element.style.fontWeight = 'normal';
32
+ element.style.textAlign = 'center';
33
+ element.style.background = '#fff';
34
+ element.style.color = '#000';
35
+ element.style.padding = '1.5em';
36
+ element.style.maxWidth = '400px';
37
+ element.style.margin = '5em auto 0';
38
+
39
+ element.innerHTML = message;
40
+
41
+ return element;
42
+
43
+ }
44
+
45
+ }
46
+
47
+
48
+ export default WebGPU;
libs/three.js/0.172.0/jsm/controls/ArcballControls.js ADDED
@@ -0,0 +1,3253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Controls,
3
+ GridHelper,
4
+ EllipseCurve,
5
+ BufferGeometry,
6
+ Line,
7
+ LineBasicMaterial,
8
+ Raycaster,
9
+ Group,
10
+ Box3,
11
+ Sphere,
12
+ Quaternion,
13
+ Vector2,
14
+ Vector3,
15
+ Matrix4,
16
+ MathUtils
17
+ } from 'three';
18
+
19
+ //trackball state
20
+ const STATE = {
21
+
22
+ IDLE: Symbol(),
23
+ ROTATE: Symbol(),
24
+ PAN: Symbol(),
25
+ SCALE: Symbol(),
26
+ FOV: Symbol(),
27
+ FOCUS: Symbol(),
28
+ ZROTATE: Symbol(),
29
+ TOUCH_MULTI: Symbol(),
30
+ ANIMATION_FOCUS: Symbol(),
31
+ ANIMATION_ROTATE: Symbol()
32
+
33
+ };
34
+
35
+ const INPUT = {
36
+
37
+ NONE: Symbol(),
38
+ ONE_FINGER: Symbol(),
39
+ ONE_FINGER_SWITCHED: Symbol(),
40
+ TWO_FINGER: Symbol(),
41
+ MULT_FINGER: Symbol(),
42
+ CURSOR: Symbol()
43
+
44
+ };
45
+
46
+ //cursor center coordinates
47
+ const _center = {
48
+
49
+ x: 0,
50
+ y: 0
51
+
52
+ };
53
+
54
+ //transformation matrices for gizmos and camera
55
+ const _transformation = {
56
+
57
+ camera: new Matrix4(),
58
+ gizmos: new Matrix4()
59
+
60
+ };
61
+
62
+ //events
63
+ const _changeEvent = { type: 'change' };
64
+ const _startEvent = { type: 'start' };
65
+ const _endEvent = { type: 'end' };
66
+
67
+ const _raycaster = new Raycaster();
68
+ const _offset = new Vector3();
69
+
70
+ const _gizmoMatrixStateTemp = new Matrix4();
71
+ const _cameraMatrixStateTemp = new Matrix4();
72
+ const _scalePointTemp = new Vector3();
73
+ /**
74
+ *
75
+ * @param {Camera} camera Virtual camera used in the scene
76
+ * @param {HTMLElement} domElement Renderer's dom element
77
+ * @param {Scene} scene The scene to be rendered
78
+ */
79
+ class ArcballControls extends Controls {
80
+
81
+ constructor( camera, domElement = null, scene = null ) {
82
+
83
+ super( camera, domElement );
84
+
85
+ this.scene = scene;
86
+ this.target = new Vector3();
87
+ this._currentTarget = new Vector3();
88
+ this.radiusFactor = 0.67;
89
+
90
+ this.mouseActions = [];
91
+ this._mouseOp = null;
92
+
93
+
94
+ //global vectors and matrices that are used in some operations to avoid creating new objects every time (e.g. every time cursor moves)
95
+ this._v2_1 = new Vector2();
96
+ this._v3_1 = new Vector3();
97
+ this._v3_2 = new Vector3();
98
+
99
+ this._m4_1 = new Matrix4();
100
+ this._m4_2 = new Matrix4();
101
+
102
+ this._quat = new Quaternion();
103
+
104
+ //transformation matrices
105
+ this._translationMatrix = new Matrix4(); //matrix for translation operation
106
+ this._rotationMatrix = new Matrix4(); //matrix for rotation operation
107
+ this._scaleMatrix = new Matrix4(); //matrix for scaling operation
108
+
109
+ this._rotationAxis = new Vector3(); //axis for rotate operation
110
+
111
+
112
+ //camera state
113
+ this._cameraMatrixState = new Matrix4();
114
+ this._cameraProjectionState = new Matrix4();
115
+
116
+ this._fovState = 1;
117
+ this._upState = new Vector3();
118
+ this._zoomState = 1;
119
+ this._nearPos = 0;
120
+ this._farPos = 0;
121
+
122
+ this._gizmoMatrixState = new Matrix4();
123
+
124
+ //initial values
125
+ this._up0 = new Vector3();
126
+ this._zoom0 = 1;
127
+ this._fov0 = 0;
128
+ this._initialNear = 0;
129
+ this._nearPos0 = 0;
130
+ this._initialFar = 0;
131
+ this._farPos0 = 0;
132
+ this._cameraMatrixState0 = new Matrix4();
133
+ this._gizmoMatrixState0 = new Matrix4();
134
+
135
+ //pointers array
136
+ this._button = - 1;
137
+ this._touchStart = [];
138
+ this._touchCurrent = [];
139
+ this._input = INPUT.NONE;
140
+
141
+ //two fingers touch interaction
142
+ this._switchSensibility = 32; //minimum movement to be performed to fire single pan start after the second finger has been released
143
+ this._startFingerDistance = 0; //distance between two fingers
144
+ this._currentFingerDistance = 0;
145
+ this._startFingerRotation = 0; //amount of rotation performed with two fingers
146
+ this._currentFingerRotation = 0;
147
+
148
+ //double tap
149
+ this._devPxRatio = 0;
150
+ this._downValid = true;
151
+ this._nclicks = 0;
152
+ this._downEvents = [];
153
+ this._downStart = 0; //pointerDown time
154
+ this._clickStart = 0; //first click time
155
+ this._maxDownTime = 250;
156
+ this._maxInterval = 300;
157
+ this._posThreshold = 24;
158
+ this._movementThreshold = 24;
159
+
160
+ //cursor positions
161
+ this._currentCursorPosition = new Vector3();
162
+ this._startCursorPosition = new Vector3();
163
+
164
+ //grid
165
+ this._grid = null; //grid to be visualized during pan operation
166
+ this._gridPosition = new Vector3();
167
+
168
+ //gizmos
169
+ this._gizmos = new Group();
170
+ this._curvePts = 128;
171
+
172
+
173
+ //animations
174
+ this._timeStart = - 1; //initial time
175
+ this._animationId = - 1;
176
+
177
+ //focus animation
178
+ this.focusAnimationTime = 500; //duration of focus animation in ms
179
+
180
+ //rotate animation
181
+ this._timePrev = 0; //time at which previous rotate operation has been detected
182
+ this._timeCurrent = 0; //time at which current rotate operation has been detected
183
+ this._anglePrev = 0; //angle of previous rotation
184
+ this._angleCurrent = 0; //angle of current rotation
185
+ this._cursorPosPrev = new Vector3(); //cursor position when previous rotate operation has been detected
186
+ this._cursorPosCurr = new Vector3();//cursor position when current rotate operation has been detected
187
+ this._wPrev = 0; //angular velocity of the previous rotate operation
188
+ this._wCurr = 0; //angular velocity of the current rotate operation
189
+
190
+
191
+ //parameters
192
+ this.adjustNearFar = false;
193
+ this.scaleFactor = 1.1; //zoom/distance multiplier
194
+ this.dampingFactor = 25;
195
+ this.wMax = 20; //maximum angular velocity allowed
196
+ this.enableAnimations = true; //if animations should be performed
197
+ this.enableGrid = false; //if grid should be showed during pan operation
198
+ this.cursorZoom = false; //if wheel zoom should be cursor centered
199
+ this.minFov = 5;
200
+ this.maxFov = 90;
201
+ this.rotateSpeed = 1;
202
+
203
+ this.enablePan = true;
204
+ this.enableRotate = true;
205
+ this.enableZoom = true;
206
+ this.enableGizmos = true;
207
+ this.enableFocus = true;
208
+
209
+ this.minDistance = 0;
210
+ this.maxDistance = Infinity;
211
+ this.minZoom = 0;
212
+ this.maxZoom = Infinity;
213
+
214
+ //trackball parameters
215
+ this._tbRadius = 1;
216
+
217
+ //FSA
218
+ this._state = STATE.IDLE;
219
+
220
+ this.setCamera( camera );
221
+
222
+ if ( this.scene != null ) {
223
+
224
+ this.scene.add( this._gizmos );
225
+
226
+ }
227
+
228
+ this.initializeMouseActions();
229
+
230
+ // event listeners
231
+
232
+ this._onContextMenu = onContextMenu.bind( this );
233
+ this._onWheel = onWheel.bind( this );
234
+ this._onPointerUp = onPointerUp.bind( this );
235
+ this._onPointerMove = onPointerMove.bind( this );
236
+ this._onPointerDown = onPointerDown.bind( this );
237
+ this._onPointerCancel = onPointerCancel.bind( this );
238
+ this._onWindowResize = onWindowResize.bind( this );
239
+
240
+ if ( domElement !== null ) {
241
+
242
+ this.connect();
243
+
244
+ }
245
+
246
+ }
247
+
248
+ connect() {
249
+
250
+ this.domElement.style.touchAction = 'none';
251
+ this._devPxRatio = window.devicePixelRatio;
252
+
253
+ this.domElement.addEventListener( 'contextmenu', this._onContextMenu );
254
+ this.domElement.addEventListener( 'wheel', this._onWheel );
255
+ this.domElement.addEventListener( 'pointerdown', this._onPointerDown );
256
+ this.domElement.addEventListener( 'pointercancel', this._onPointerCancel );
257
+
258
+ window.addEventListener( 'resize', this._onWindowResize );
259
+
260
+ }
261
+
262
+ disconnect() {
263
+
264
+ this.domElement.removeEventListener( 'pointerdown', this._onPointerDown );
265
+ this.domElement.removeEventListener( 'pointercancel', this._onPointerCancel );
266
+ this.domElement.removeEventListener( 'wheel', this._onWheel );
267
+ this.domElement.removeEventListener( 'contextmenu', this._onContextMenu );
268
+
269
+ window.removeEventListener( 'pointermove', this._onPointerMove );
270
+ window.removeEventListener( 'pointerup', this._onPointerUp );
271
+
272
+ window.removeEventListener( 'resize', this._onWindowResize );
273
+
274
+ }
275
+
276
+ onSinglePanStart( event, operation ) {
277
+
278
+ if ( this.enabled ) {
279
+
280
+ this.dispatchEvent( _startEvent );
281
+
282
+ this.setCenter( event.clientX, event.clientY );
283
+
284
+ switch ( operation ) {
285
+
286
+ case 'PAN':
287
+
288
+ if ( ! this.enablePan ) {
289
+
290
+ return;
291
+
292
+ }
293
+
294
+ if ( this._animationId != - 1 ) {
295
+
296
+ cancelAnimationFrame( this._animationId );
297
+ this._animationId = - 1;
298
+ this._timeStart = - 1;
299
+
300
+ this.activateGizmos( false );
301
+ this.dispatchEvent( _changeEvent );
302
+
303
+ }
304
+
305
+ this.updateTbState( STATE.PAN, true );
306
+ this._startCursorPosition.copy( this.unprojectOnTbPlane( this.object, _center.x, _center.y, this.domElement ) );
307
+ if ( this.enableGrid ) {
308
+
309
+ this.drawGrid();
310
+ this.dispatchEvent( _changeEvent );
311
+
312
+ }
313
+
314
+ break;
315
+
316
+ case 'ROTATE':
317
+
318
+ if ( ! this.enableRotate ) {
319
+
320
+ return;
321
+
322
+ }
323
+
324
+ if ( this._animationId != - 1 ) {
325
+
326
+ cancelAnimationFrame( this._animationId );
327
+ this._animationId = - 1;
328
+ this._timeStart = - 1;
329
+
330
+ }
331
+
332
+ this.updateTbState( STATE.ROTATE, true );
333
+ this._startCursorPosition.copy( this.unprojectOnTbSurface( this.object, _center.x, _center.y, this.domElement, this._tbRadius ) );
334
+ this.activateGizmos( true );
335
+ if ( this.enableAnimations ) {
336
+
337
+ this._timePrev = this._timeCurrent = performance.now();
338
+ this._angleCurrent = this._anglePrev = 0;
339
+ this._cursorPosPrev.copy( this._startCursorPosition );
340
+ this._cursorPosCurr.copy( this._cursorPosPrev );
341
+ this._wCurr = 0;
342
+ this._wPrev = this._wCurr;
343
+
344
+ }
345
+
346
+ this.dispatchEvent( _changeEvent );
347
+ break;
348
+
349
+ case 'FOV':
350
+
351
+ if ( ! this.object.isPerspectiveCamera || ! this.enableZoom ) {
352
+
353
+ return;
354
+
355
+ }
356
+
357
+ if ( this._animationId != - 1 ) {
358
+
359
+ cancelAnimationFrame( this._animationId );
360
+ this._animationId = - 1;
361
+ this._timeStart = - 1;
362
+
363
+ this.activateGizmos( false );
364
+ this.dispatchEvent( _changeEvent );
365
+
366
+ }
367
+
368
+ this.updateTbState( STATE.FOV, true );
369
+ this._startCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
370
+ this._currentCursorPosition.copy( this._startCursorPosition );
371
+ break;
372
+
373
+ case 'ZOOM':
374
+
375
+ if ( ! this.enableZoom ) {
376
+
377
+ return;
378
+
379
+ }
380
+
381
+ if ( this._animationId != - 1 ) {
382
+
383
+ cancelAnimationFrame( this._animationId );
384
+ this._animationId = - 1;
385
+ this._timeStart = - 1;
386
+
387
+ this.activateGizmos( false );
388
+ this.dispatchEvent( _changeEvent );
389
+
390
+ }
391
+
392
+ this.updateTbState( STATE.SCALE, true );
393
+ this._startCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
394
+ this._currentCursorPosition.copy( this._startCursorPosition );
395
+ break;
396
+
397
+ }
398
+
399
+ }
400
+
401
+ }
402
+
403
+ onSinglePanMove( event, opState ) {
404
+
405
+ if ( this.enabled ) {
406
+
407
+ const restart = opState != this._state;
408
+ this.setCenter( event.clientX, event.clientY );
409
+
410
+ switch ( opState ) {
411
+
412
+ case STATE.PAN:
413
+
414
+ if ( this.enablePan ) {
415
+
416
+ if ( restart ) {
417
+
418
+ //switch to pan operation
419
+
420
+ this.dispatchEvent( _endEvent );
421
+ this.dispatchEvent( _startEvent );
422
+
423
+ this.updateTbState( opState, true );
424
+ this._startCursorPosition.copy( this.unprojectOnTbPlane( this.object, _center.x, _center.y, this.domElement ) );
425
+ if ( this.enableGrid ) {
426
+
427
+ this.drawGrid();
428
+
429
+ }
430
+
431
+ this.activateGizmos( false );
432
+
433
+ } else {
434
+
435
+ //continue with pan operation
436
+ this._currentCursorPosition.copy( this.unprojectOnTbPlane( this.object, _center.x, _center.y, this.domElement ) );
437
+ this.applyTransformMatrix( this.pan( this._startCursorPosition, this._currentCursorPosition ) );
438
+
439
+ }
440
+
441
+ }
442
+
443
+ break;
444
+
445
+ case STATE.ROTATE:
446
+
447
+ if ( this.enableRotate ) {
448
+
449
+ if ( restart ) {
450
+
451
+ //switch to rotate operation
452
+
453
+ this.dispatchEvent( _endEvent );
454
+ this.dispatchEvent( _startEvent );
455
+
456
+ this.updateTbState( opState, true );
457
+ this._startCursorPosition.copy( this.unprojectOnTbSurface( this.object, _center.x, _center.y, this.domElement, this._tbRadius ) );
458
+
459
+ if ( this.enableGrid ) {
460
+
461
+ this.disposeGrid();
462
+
463
+ }
464
+
465
+ this.activateGizmos( true );
466
+
467
+ } else {
468
+
469
+ //continue with rotate operation
470
+ this._currentCursorPosition.copy( this.unprojectOnTbSurface( this.object, _center.x, _center.y, this.domElement, this._tbRadius ) );
471
+
472
+ const distance = this._startCursorPosition.distanceTo( this._currentCursorPosition );
473
+ const angle = this._startCursorPosition.angleTo( this._currentCursorPosition );
474
+ const amount = Math.max( distance / this._tbRadius, angle ) * this.rotateSpeed; //effective rotation angle
475
+
476
+ this.applyTransformMatrix( this.rotate( this.calculateRotationAxis( this._startCursorPosition, this._currentCursorPosition ), amount ) );
477
+
478
+ if ( this.enableAnimations ) {
479
+
480
+ this._timePrev = this._timeCurrent;
481
+ this._timeCurrent = performance.now();
482
+ this._anglePrev = this._angleCurrent;
483
+ this._angleCurrent = amount;
484
+ this._cursorPosPrev.copy( this._cursorPosCurr );
485
+ this._cursorPosCurr.copy( this._currentCursorPosition );
486
+ this._wPrev = this._wCurr;
487
+ this._wCurr = this.calculateAngularSpeed( this._anglePrev, this._angleCurrent, this._timePrev, this._timeCurrent );
488
+
489
+ }
490
+
491
+ }
492
+
493
+ }
494
+
495
+ break;
496
+
497
+ case STATE.SCALE:
498
+
499
+ if ( this.enableZoom ) {
500
+
501
+ if ( restart ) {
502
+
503
+ //switch to zoom operation
504
+
505
+ this.dispatchEvent( _endEvent );
506
+ this.dispatchEvent( _startEvent );
507
+
508
+ this.updateTbState( opState, true );
509
+ this._startCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
510
+ this._currentCursorPosition.copy( this._startCursorPosition );
511
+
512
+ if ( this.enableGrid ) {
513
+
514
+ this.disposeGrid();
515
+
516
+ }
517
+
518
+ this.activateGizmos( false );
519
+
520
+ } else {
521
+
522
+ //continue with zoom operation
523
+ const screenNotches = 8; //how many wheel notches corresponds to a full screen pan
524
+ this._currentCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
525
+
526
+ const movement = this._currentCursorPosition.y - this._startCursorPosition.y;
527
+
528
+ let size = 1;
529
+
530
+ if ( movement < 0 ) {
531
+
532
+ size = 1 / ( Math.pow( this.scaleFactor, - movement * screenNotches ) );
533
+
534
+ } else if ( movement > 0 ) {
535
+
536
+ size = Math.pow( this.scaleFactor, movement * screenNotches );
537
+
538
+ }
539
+
540
+ this._v3_1.setFromMatrixPosition( this._gizmoMatrixState );
541
+
542
+ this.applyTransformMatrix( this.scale( size, this._v3_1 ) );
543
+
544
+ }
545
+
546
+ }
547
+
548
+ break;
549
+
550
+ case STATE.FOV:
551
+
552
+ if ( this.enableZoom && this.object.isPerspectiveCamera ) {
553
+
554
+ if ( restart ) {
555
+
556
+ //switch to fov operation
557
+
558
+ this.dispatchEvent( _endEvent );
559
+ this.dispatchEvent( _startEvent );
560
+
561
+ this.updateTbState( opState, true );
562
+ this._startCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
563
+ this._currentCursorPosition.copy( this._startCursorPosition );
564
+
565
+ if ( this.enableGrid ) {
566
+
567
+ this.disposeGrid();
568
+
569
+ }
570
+
571
+ this.activateGizmos( false );
572
+
573
+ } else {
574
+
575
+ //continue with fov operation
576
+ const screenNotches = 8; //how many wheel notches corresponds to a full screen pan
577
+ this._currentCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
578
+
579
+ const movement = this._currentCursorPosition.y - this._startCursorPosition.y;
580
+
581
+ let size = 1;
582
+
583
+ if ( movement < 0 ) {
584
+
585
+ size = 1 / ( Math.pow( this.scaleFactor, - movement * screenNotches ) );
586
+
587
+ } else if ( movement > 0 ) {
588
+
589
+ size = Math.pow( this.scaleFactor, movement * screenNotches );
590
+
591
+ }
592
+
593
+ this._v3_1.setFromMatrixPosition( this._cameraMatrixState );
594
+ const x = this._v3_1.distanceTo( this._gizmos.position );
595
+ let xNew = x / size; //distance between camera and gizmos if scale(size, scalepoint) would be performed
596
+
597
+ //check min and max distance
598
+ xNew = MathUtils.clamp( xNew, this.minDistance, this.maxDistance );
599
+
600
+ const y = x * Math.tan( MathUtils.DEG2RAD * this._fovState * 0.5 );
601
+
602
+ //calculate new fov
603
+ let newFov = MathUtils.RAD2DEG * ( Math.atan( y / xNew ) * 2 );
604
+
605
+ //check min and max fov
606
+ newFov = MathUtils.clamp( newFov, this.minFov, this.maxFov );
607
+
608
+ const newDistance = y / Math.tan( MathUtils.DEG2RAD * ( newFov / 2 ) );
609
+ size = x / newDistance;
610
+ this._v3_2.setFromMatrixPosition( this._gizmoMatrixState );
611
+
612
+ this.setFov( newFov );
613
+ this.applyTransformMatrix( this.scale( size, this._v3_2, false ) );
614
+
615
+ //adjusting distance
616
+ _offset.copy( this._gizmos.position ).sub( this.object.position ).normalize().multiplyScalar( newDistance / x );
617
+ this._m4_1.makeTranslation( _offset.x, _offset.y, _offset.z );
618
+
619
+ }
620
+
621
+ }
622
+
623
+ break;
624
+
625
+ }
626
+
627
+ this.dispatchEvent( _changeEvent );
628
+
629
+ }
630
+
631
+ }
632
+
633
+ onSinglePanEnd() {
634
+
635
+ if ( this._state == STATE.ROTATE ) {
636
+
637
+
638
+ if ( ! this.enableRotate ) {
639
+
640
+ return;
641
+
642
+ }
643
+
644
+ if ( this.enableAnimations ) {
645
+
646
+ //perform rotation animation
647
+ const deltaTime = ( performance.now() - this._timeCurrent );
648
+ if ( deltaTime < 120 ) {
649
+
650
+ const w = Math.abs( ( this._wPrev + this._wCurr ) / 2 );
651
+
652
+ const self = this;
653
+ this._animationId = window.requestAnimationFrame( function ( t ) {
654
+
655
+ self.updateTbState( STATE.ANIMATION_ROTATE, true );
656
+ const rotationAxis = self.calculateRotationAxis( self._cursorPosPrev, self._cursorPosCurr );
657
+
658
+ self.onRotationAnim( t, rotationAxis, Math.min( w, self.wMax ) );
659
+
660
+ } );
661
+
662
+ } else {
663
+
664
+ //cursor has been standing still for over 120 ms since last movement
665
+ this.updateTbState( STATE.IDLE, false );
666
+ this.activateGizmos( false );
667
+ this.dispatchEvent( _changeEvent );
668
+
669
+ }
670
+
671
+ } else {
672
+
673
+ this.updateTbState( STATE.IDLE, false );
674
+ this.activateGizmos( false );
675
+ this.dispatchEvent( _changeEvent );
676
+
677
+ }
678
+
679
+ } else if ( this._state == STATE.PAN || this._state == STATE.IDLE ) {
680
+
681
+ this.updateTbState( STATE.IDLE, false );
682
+
683
+ if ( this.enableGrid ) {
684
+
685
+ this.disposeGrid();
686
+
687
+ }
688
+
689
+ this.activateGizmos( false );
690
+ this.dispatchEvent( _changeEvent );
691
+
692
+
693
+ }
694
+
695
+ this.dispatchEvent( _endEvent );
696
+
697
+ }
698
+
699
+ onDoubleTap( event ) {
700
+
701
+ if ( this.enabled && this.enablePan && this.enableFocus && this.scene != null ) {
702
+
703
+ this.dispatchEvent( _startEvent );
704
+
705
+ this.setCenter( event.clientX, event.clientY );
706
+ const hitP = this.unprojectOnObj( this.getCursorNDC( _center.x, _center.y, this.domElement ), this.object );
707
+
708
+ if ( hitP != null && this.enableAnimations ) {
709
+
710
+ const self = this;
711
+ if ( this._animationId != - 1 ) {
712
+
713
+ window.cancelAnimationFrame( this._animationId );
714
+
715
+ }
716
+
717
+ this._timeStart = - 1;
718
+ this._animationId = window.requestAnimationFrame( function ( t ) {
719
+
720
+ self.updateTbState( STATE.ANIMATION_FOCUS, true );
721
+ self.onFocusAnim( t, hitP, self._cameraMatrixState, self._gizmoMatrixState );
722
+
723
+ } );
724
+
725
+ } else if ( hitP != null && ! this.enableAnimations ) {
726
+
727
+ this.updateTbState( STATE.FOCUS, true );
728
+ this.focus( hitP, this.scaleFactor );
729
+ this.updateTbState( STATE.IDLE, false );
730
+ this.dispatchEvent( _changeEvent );
731
+
732
+ }
733
+
734
+ }
735
+
736
+ this.dispatchEvent( _endEvent );
737
+
738
+ }
739
+
740
+ onDoublePanStart() {
741
+
742
+ if ( this.enabled && this.enablePan ) {
743
+
744
+ this.dispatchEvent( _startEvent );
745
+
746
+ this.updateTbState( STATE.PAN, true );
747
+
748
+ this.setCenter( ( this._touchCurrent[ 0 ].clientX + this._touchCurrent[ 1 ].clientX ) / 2, ( this._touchCurrent[ 0 ].clientY + this._touchCurrent[ 1 ].clientY ) / 2 );
749
+ this._startCursorPosition.copy( this.unprojectOnTbPlane( this.object, _center.x, _center.y, this.domElement, true ) );
750
+ this._currentCursorPosition.copy( this._startCursorPosition );
751
+
752
+ this.activateGizmos( false );
753
+
754
+ }
755
+
756
+ }
757
+
758
+ onDoublePanMove() {
759
+
760
+ if ( this.enabled && this.enablePan ) {
761
+
762
+ this.setCenter( ( this._touchCurrent[ 0 ].clientX + this._touchCurrent[ 1 ].clientX ) / 2, ( this._touchCurrent[ 0 ].clientY + this._touchCurrent[ 1 ].clientY ) / 2 );
763
+
764
+ if ( this._state != STATE.PAN ) {
765
+
766
+ this.updateTbState( STATE.PAN, true );
767
+ this._startCursorPosition.copy( this._currentCursorPosition );
768
+
769
+ }
770
+
771
+ this._currentCursorPosition.copy( this.unprojectOnTbPlane( this.object, _center.x, _center.y, this.domElement, true ) );
772
+ this.applyTransformMatrix( this.pan( this._startCursorPosition, this._currentCursorPosition, true ) );
773
+ this.dispatchEvent( _changeEvent );
774
+
775
+ }
776
+
777
+ }
778
+
779
+ onDoublePanEnd() {
780
+
781
+ this.updateTbState( STATE.IDLE, false );
782
+ this.dispatchEvent( _endEvent );
783
+
784
+ }
785
+
786
+ onRotateStart() {
787
+
788
+ if ( this.enabled && this.enableRotate ) {
789
+
790
+ this.dispatchEvent( _startEvent );
791
+
792
+ this.updateTbState( STATE.ZROTATE, true );
793
+
794
+ //this._startFingerRotation = event.rotation;
795
+
796
+ this._startFingerRotation = this.getAngle( this._touchCurrent[ 1 ], this._touchCurrent[ 0 ] ) + this.getAngle( this._touchStart[ 1 ], this._touchStart[ 0 ] );
797
+ this._currentFingerRotation = this._startFingerRotation;
798
+
799
+ this.object.getWorldDirection( this._rotationAxis ); //rotation axis
800
+
801
+ if ( ! this.enablePan && ! this.enableZoom ) {
802
+
803
+ this.activateGizmos( true );
804
+
805
+ }
806
+
807
+ }
808
+
809
+ }
810
+
811
+ onRotateMove() {
812
+
813
+ if ( this.enabled && this.enableRotate ) {
814
+
815
+ this.setCenter( ( this._touchCurrent[ 0 ].clientX + this._touchCurrent[ 1 ].clientX ) / 2, ( this._touchCurrent[ 0 ].clientY + this._touchCurrent[ 1 ].clientY ) / 2 );
816
+ let rotationPoint;
817
+
818
+ if ( this._state != STATE.ZROTATE ) {
819
+
820
+ this.updateTbState( STATE.ZROTATE, true );
821
+ this._startFingerRotation = this._currentFingerRotation;
822
+
823
+ }
824
+
825
+ //this._currentFingerRotation = event.rotation;
826
+ this._currentFingerRotation = this.getAngle( this._touchCurrent[ 1 ], this._touchCurrent[ 0 ] ) + this.getAngle( this._touchStart[ 1 ], this._touchStart[ 0 ] );
827
+
828
+ if ( ! this.enablePan ) {
829
+
830
+ rotationPoint = new Vector3().setFromMatrixPosition( this._gizmoMatrixState );
831
+
832
+ } else {
833
+
834
+ this._v3_2.setFromMatrixPosition( this._gizmoMatrixState );
835
+ rotationPoint = this.unprojectOnTbPlane( this.object, _center.x, _center.y, this.domElement ).applyQuaternion( this.object.quaternion ).multiplyScalar( 1 / this.object.zoom ).add( this._v3_2 );
836
+
837
+ }
838
+
839
+ const amount = MathUtils.DEG2RAD * ( this._startFingerRotation - this._currentFingerRotation );
840
+
841
+ this.applyTransformMatrix( this.zRotate( rotationPoint, amount ) );
842
+ this.dispatchEvent( _changeEvent );
843
+
844
+ }
845
+
846
+ }
847
+
848
+ onRotateEnd() {
849
+
850
+ this.updateTbState( STATE.IDLE, false );
851
+ this.activateGizmos( false );
852
+ this.dispatchEvent( _endEvent );
853
+
854
+ }
855
+
856
+ onPinchStart() {
857
+
858
+ if ( this.enabled && this.enableZoom ) {
859
+
860
+ this.dispatchEvent( _startEvent );
861
+ this.updateTbState( STATE.SCALE, true );
862
+
863
+ this._startFingerDistance = this.calculatePointersDistance( this._touchCurrent[ 0 ], this._touchCurrent[ 1 ] );
864
+ this._currentFingerDistance = this._startFingerDistance;
865
+
866
+ this.activateGizmos( false );
867
+
868
+ }
869
+
870
+ }
871
+
872
+ onPinchMove() {
873
+
874
+ if ( this.enabled && this.enableZoom ) {
875
+
876
+ this.setCenter( ( this._touchCurrent[ 0 ].clientX + this._touchCurrent[ 1 ].clientX ) / 2, ( this._touchCurrent[ 0 ].clientY + this._touchCurrent[ 1 ].clientY ) / 2 );
877
+ const minDistance = 12; //minimum distance between fingers (in css pixels)
878
+
879
+ if ( this._state != STATE.SCALE ) {
880
+
881
+ this._startFingerDistance = this._currentFingerDistance;
882
+ this.updateTbState( STATE.SCALE, true );
883
+
884
+ }
885
+
886
+ this._currentFingerDistance = Math.max( this.calculatePointersDistance( this._touchCurrent[ 0 ], this._touchCurrent[ 1 ] ), minDistance * this._devPxRatio );
887
+ const amount = this._currentFingerDistance / this._startFingerDistance;
888
+
889
+ let scalePoint;
890
+
891
+ if ( ! this.enablePan ) {
892
+
893
+ scalePoint = this._gizmos.position;
894
+
895
+ } else {
896
+
897
+ if ( this.object.isOrthographicCamera ) {
898
+
899
+ scalePoint = this.unprojectOnTbPlane( this.object, _center.x, _center.y, this.domElement )
900
+ .applyQuaternion( this.object.quaternion )
901
+ .multiplyScalar( 1 / this.object.zoom )
902
+ .add( this._gizmos.position );
903
+
904
+ } else if ( this.object.isPerspectiveCamera ) {
905
+
906
+ scalePoint = this.unprojectOnTbPlane( this.object, _center.x, _center.y, this.domElement )
907
+ .applyQuaternion( this.object.quaternion )
908
+ .add( this._gizmos.position );
909
+
910
+ }
911
+
912
+ }
913
+
914
+ this.applyTransformMatrix( this.scale( amount, scalePoint ) );
915
+ this.dispatchEvent( _changeEvent );
916
+
917
+ }
918
+
919
+ }
920
+
921
+ onPinchEnd() {
922
+
923
+ this.updateTbState( STATE.IDLE, false );
924
+ this.dispatchEvent( _endEvent );
925
+
926
+ }
927
+
928
+ onTriplePanStart() {
929
+
930
+ if ( this.enabled && this.enableZoom ) {
931
+
932
+ this.dispatchEvent( _startEvent );
933
+
934
+ this.updateTbState( STATE.SCALE, true );
935
+
936
+ //const center = event.center;
937
+ let clientX = 0;
938
+ let clientY = 0;
939
+ const nFingers = this._touchCurrent.length;
940
+
941
+ for ( let i = 0; i < nFingers; i ++ ) {
942
+
943
+ clientX += this._touchCurrent[ i ].clientX;
944
+ clientY += this._touchCurrent[ i ].clientY;
945
+
946
+ }
947
+
948
+ this.setCenter( clientX / nFingers, clientY / nFingers );
949
+
950
+ this._startCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
951
+ this._currentCursorPosition.copy( this._startCursorPosition );
952
+
953
+ }
954
+
955
+ }
956
+
957
+ onTriplePanMove() {
958
+
959
+ if ( this.enabled && this.enableZoom ) {
960
+
961
+ // fov / 2
962
+ // |\
963
+ // | \
964
+ // | \
965
+ // x | \
966
+ // | \
967
+ // | \
968
+ // | _ _ _\
969
+ // y
970
+
971
+ //const center = event.center;
972
+ let clientX = 0;
973
+ let clientY = 0;
974
+ const nFingers = this._touchCurrent.length;
975
+
976
+ for ( let i = 0; i < nFingers; i ++ ) {
977
+
978
+ clientX += this._touchCurrent[ i ].clientX;
979
+ clientY += this._touchCurrent[ i ].clientY;
980
+
981
+ }
982
+
983
+ this.setCenter( clientX / nFingers, clientY / nFingers );
984
+
985
+ const screenNotches = 8; //how many wheel notches corresponds to a full screen pan
986
+ this._currentCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
987
+
988
+ const movement = this._currentCursorPosition.y - this._startCursorPosition.y;
989
+
990
+ let size = 1;
991
+
992
+ if ( movement < 0 ) {
993
+
994
+ size = 1 / ( Math.pow( this.scaleFactor, - movement * screenNotches ) );
995
+
996
+ } else if ( movement > 0 ) {
997
+
998
+ size = Math.pow( this.scaleFactor, movement * screenNotches );
999
+
1000
+ }
1001
+
1002
+ this._v3_1.setFromMatrixPosition( this._cameraMatrixState );
1003
+ const x = this._v3_1.distanceTo( this._gizmos.position );
1004
+ let xNew = x / size; //distance between camera and gizmos if scale(size, scalepoint) would be performed
1005
+
1006
+ //check min and max distance
1007
+ xNew = MathUtils.clamp( xNew, this.minDistance, this.maxDistance );
1008
+
1009
+ const y = x * Math.tan( MathUtils.DEG2RAD * this._fovState * 0.5 );
1010
+
1011
+ //calculate new fov
1012
+ let newFov = MathUtils.RAD2DEG * ( Math.atan( y / xNew ) * 2 );
1013
+
1014
+ //check min and max fov
1015
+ newFov = MathUtils.clamp( newFov, this.minFov, this.maxFov );
1016
+
1017
+ const newDistance = y / Math.tan( MathUtils.DEG2RAD * ( newFov / 2 ) );
1018
+ size = x / newDistance;
1019
+ this._v3_2.setFromMatrixPosition( this._gizmoMatrixState );
1020
+
1021
+ this.setFov( newFov );
1022
+ this.applyTransformMatrix( this.scale( size, this._v3_2, false ) );
1023
+
1024
+ //adjusting distance
1025
+ _offset.copy( this._gizmos.position ).sub( this.object.position ).normalize().multiplyScalar( newDistance / x );
1026
+ this._m4_1.makeTranslation( _offset.x, _offset.y, _offset.z );
1027
+
1028
+ this.dispatchEvent( _changeEvent );
1029
+
1030
+ }
1031
+
1032
+ }
1033
+
1034
+ onTriplePanEnd() {
1035
+
1036
+ this.updateTbState( STATE.IDLE, false );
1037
+ this.dispatchEvent( _endEvent );
1038
+ //this.dispatchEvent( _changeEvent );
1039
+
1040
+ }
1041
+
1042
+ /**
1043
+ * Set _center's x/y coordinates
1044
+ * @param {Number} clientX
1045
+ * @param {Number} clientY
1046
+ */
1047
+ setCenter( clientX, clientY ) {
1048
+
1049
+ _center.x = clientX;
1050
+ _center.y = clientY;
1051
+
1052
+ }
1053
+
1054
+ /**
1055
+ * Set default mouse actions
1056
+ */
1057
+ initializeMouseActions() {
1058
+
1059
+ this.setMouseAction( 'PAN', 0, 'CTRL' );
1060
+ this.setMouseAction( 'PAN', 2 );
1061
+
1062
+ this.setMouseAction( 'ROTATE', 0 );
1063
+
1064
+ this.setMouseAction( 'ZOOM', 'WHEEL' );
1065
+ this.setMouseAction( 'ZOOM', 1 );
1066
+
1067
+ this.setMouseAction( 'FOV', 'WHEEL', 'SHIFT' );
1068
+ this.setMouseAction( 'FOV', 1, 'SHIFT' );
1069
+
1070
+
1071
+ }
1072
+
1073
+ /**
1074
+ * Compare two mouse actions
1075
+ * @param {Object} action1
1076
+ * @param {Object} action2
1077
+ * @returns {Boolean} True if action1 and action 2 are the same mouse action, false otherwise
1078
+ */
1079
+ compareMouseAction( action1, action2 ) {
1080
+
1081
+ if ( action1.operation == action2.operation ) {
1082
+
1083
+ if ( action1.mouse == action2.mouse && action1.key == action2.key ) {
1084
+
1085
+ return true;
1086
+
1087
+ } else {
1088
+
1089
+ return false;
1090
+
1091
+ }
1092
+
1093
+ } else {
1094
+
1095
+ return false;
1096
+
1097
+ }
1098
+
1099
+ }
1100
+
1101
+ /**
1102
+ * Set a new mouse action by specifying the operation to be performed and a mouse/key combination. In case of conflict, replaces the existing one
1103
+ * @param {String} operation The operation to be performed ('PAN', 'ROTATE', 'ZOOM', 'FOV)
1104
+ * @param {*} mouse A mouse button (0, 1, 2) or 'WHEEL' for wheel notches
1105
+ * @param {*} key The keyboard modifier ('CTRL', 'SHIFT') or null if key is not needed
1106
+ * @returns {Boolean} True if the mouse action has been successfully added, false otherwise
1107
+ */
1108
+ setMouseAction( operation, mouse, key = null ) {
1109
+
1110
+ const operationInput = [ 'PAN', 'ROTATE', 'ZOOM', 'FOV' ];
1111
+ const mouseInput = [ 0, 1, 2, 'WHEEL' ];
1112
+ const keyInput = [ 'CTRL', 'SHIFT', null ];
1113
+ let state;
1114
+
1115
+ if ( ! operationInput.includes( operation ) || ! mouseInput.includes( mouse ) || ! keyInput.includes( key ) ) {
1116
+
1117
+ //invalid parameters
1118
+ return false;
1119
+
1120
+ }
1121
+
1122
+ if ( mouse == 'WHEEL' ) {
1123
+
1124
+ if ( operation != 'ZOOM' && operation != 'FOV' ) {
1125
+
1126
+ //cannot associate 2D operation to 1D input
1127
+ return false;
1128
+
1129
+ }
1130
+
1131
+ }
1132
+
1133
+ switch ( operation ) {
1134
+
1135
+ case 'PAN':
1136
+
1137
+ state = STATE.PAN;
1138
+ break;
1139
+
1140
+ case 'ROTATE':
1141
+
1142
+ state = STATE.ROTATE;
1143
+ break;
1144
+
1145
+ case 'ZOOM':
1146
+
1147
+ state = STATE.SCALE;
1148
+ break;
1149
+
1150
+ case 'FOV':
1151
+
1152
+ state = STATE.FOV;
1153
+ break;
1154
+
1155
+ }
1156
+
1157
+ const action = {
1158
+
1159
+ operation: operation,
1160
+ mouse: mouse,
1161
+ key: key,
1162
+ state: state
1163
+
1164
+ };
1165
+
1166
+ for ( let i = 0; i < this.mouseActions.length; i ++ ) {
1167
+
1168
+ if ( this.mouseActions[ i ].mouse == action.mouse && this.mouseActions[ i ].key == action.key ) {
1169
+
1170
+ this.mouseActions.splice( i, 1, action );
1171
+ return true;
1172
+
1173
+ }
1174
+
1175
+ }
1176
+
1177
+ this.mouseActions.push( action );
1178
+ return true;
1179
+
1180
+ }
1181
+
1182
+ /**
1183
+ * Remove a mouse action by specifying its mouse/key combination
1184
+ * @param {*} mouse A mouse button (0, 1, 2) or 'WHEEL' for wheel notches
1185
+ * @param {*} key The keyboard modifier ('CTRL', 'SHIFT') or null if key is not needed
1186
+ * @returns {Boolean} True if the operation has been successfully removed, false otherwise
1187
+ */
1188
+ unsetMouseAction( mouse, key = null ) {
1189
+
1190
+ for ( let i = 0; i < this.mouseActions.length; i ++ ) {
1191
+
1192
+ if ( this.mouseActions[ i ].mouse == mouse && this.mouseActions[ i ].key == key ) {
1193
+
1194
+ this.mouseActions.splice( i, 1 );
1195
+ return true;
1196
+
1197
+ }
1198
+
1199
+ }
1200
+
1201
+ return false;
1202
+
1203
+ }
1204
+
1205
+ /**
1206
+ * Return the operation associated to a mouse/keyboard combination
1207
+ * @param {0|1|2|'WHEEL'} mouse Mouse button index (0, 1, 2) or 'WHEEL' for wheel notches
1208
+ * @param {'CTRL'|'SHIFT'|null} key Keyboard modifier
1209
+ * @returns {string|null} The operation if it has been found, null otherwise
1210
+ */
1211
+ getOpFromAction( mouse, key ) {
1212
+
1213
+ let action;
1214
+
1215
+ for ( let i = 0; i < this.mouseActions.length; i ++ ) {
1216
+
1217
+ action = this.mouseActions[ i ];
1218
+ if ( action.mouse == mouse && action.key == key ) {
1219
+
1220
+ return action.operation;
1221
+
1222
+ }
1223
+
1224
+ }
1225
+
1226
+ if ( key != null ) {
1227
+
1228
+ for ( let i = 0; i < this.mouseActions.length; i ++ ) {
1229
+
1230
+ action = this.mouseActions[ i ];
1231
+ if ( action.mouse == mouse && action.key == null ) {
1232
+
1233
+ return action.operation;
1234
+
1235
+ }
1236
+
1237
+ }
1238
+
1239
+ }
1240
+
1241
+ return null;
1242
+
1243
+ }
1244
+
1245
+ /**
1246
+ * Get the operation associated to mouse and key combination and returns the corresponding FSA state
1247
+ * @param {0|1|2} mouse Mouse button index (0, 1, 2)
1248
+ * @param {'CTRL'|'SHIFT'|null} key Keyboard modifier
1249
+ * @returns {STATE|null} The FSA state obtained from the operation associated to mouse/keyboard combination
1250
+ */
1251
+ getOpStateFromAction( mouse, key ) {
1252
+
1253
+ let action;
1254
+
1255
+ for ( let i = 0; i < this.mouseActions.length; i ++ ) {
1256
+
1257
+ action = this.mouseActions[ i ];
1258
+ if ( action.mouse == mouse && action.key == key ) {
1259
+
1260
+ return action.state;
1261
+
1262
+ }
1263
+
1264
+ }
1265
+
1266
+ if ( key != null ) {
1267
+
1268
+ for ( let i = 0; i < this.mouseActions.length; i ++ ) {
1269
+
1270
+ action = this.mouseActions[ i ];
1271
+ if ( action.mouse == mouse && action.key == null ) {
1272
+
1273
+ return action.state;
1274
+
1275
+ }
1276
+
1277
+ }
1278
+
1279
+ }
1280
+
1281
+ return null;
1282
+
1283
+ }
1284
+
1285
+ /**
1286
+ * Calculate the angle between two pointers
1287
+ * @param {PointerEvent} p1
1288
+ * @param {PointerEvent} p2
1289
+ * @returns {Number} The angle between two pointers in degrees
1290
+ */
1291
+ getAngle( p1, p2 ) {
1292
+
1293
+ return Math.atan2( p2.clientY - p1.clientY, p2.clientX - p1.clientX ) * 180 / Math.PI;
1294
+
1295
+ }
1296
+
1297
+ /**
1298
+ * Update a PointerEvent inside current pointerevents array
1299
+ * @param {PointerEvent} event
1300
+ */
1301
+ updateTouchEvent( event ) {
1302
+
1303
+ for ( let i = 0; i < this._touchCurrent.length; i ++ ) {
1304
+
1305
+ if ( this._touchCurrent[ i ].pointerId == event.pointerId ) {
1306
+
1307
+ this._touchCurrent.splice( i, 1, event );
1308
+ break;
1309
+
1310
+ }
1311
+
1312
+ }
1313
+
1314
+ }
1315
+
1316
+ /**
1317
+ * Apply a transformation matrix, to the camera and gizmos
1318
+ * @param {Object} transformation Object containing matrices to apply to camera and gizmos
1319
+ */
1320
+ applyTransformMatrix( transformation ) {
1321
+
1322
+ if ( transformation.camera != null ) {
1323
+
1324
+ this._m4_1.copy( this._cameraMatrixState ).premultiply( transformation.camera );
1325
+ this._m4_1.decompose( this.object.position, this.object.quaternion, this.object.scale );
1326
+ this.object.updateMatrix();
1327
+
1328
+ //update camera up vector
1329
+ if ( this._state == STATE.ROTATE || this._state == STATE.ZROTATE || this._state == STATE.ANIMATION_ROTATE ) {
1330
+
1331
+ this.object.up.copy( this._upState ).applyQuaternion( this.object.quaternion );
1332
+
1333
+ }
1334
+
1335
+ }
1336
+
1337
+ if ( transformation.gizmos != null ) {
1338
+
1339
+ this._m4_1.copy( this._gizmoMatrixState ).premultiply( transformation.gizmos );
1340
+ this._m4_1.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
1341
+ this._gizmos.updateMatrix();
1342
+
1343
+ }
1344
+
1345
+ if ( this._state == STATE.SCALE || this._state == STATE.FOCUS || this._state == STATE.ANIMATION_FOCUS ) {
1346
+
1347
+ this._tbRadius = this.calculateTbRadius( this.object );
1348
+
1349
+ if ( this.adjustNearFar ) {
1350
+
1351
+ const cameraDistance = this.object.position.distanceTo( this._gizmos.position );
1352
+
1353
+ const bb = new Box3();
1354
+ bb.setFromObject( this._gizmos );
1355
+ const sphere = new Sphere();
1356
+ bb.getBoundingSphere( sphere );
1357
+
1358
+ const adjustedNearPosition = Math.max( this._nearPos0, sphere.radius + sphere.center.length() );
1359
+ const regularNearPosition = cameraDistance - this._initialNear;
1360
+
1361
+ const minNearPos = Math.min( adjustedNearPosition, regularNearPosition );
1362
+ this.object.near = cameraDistance - minNearPos;
1363
+
1364
+
1365
+ const adjustedFarPosition = Math.min( this._farPos0, - sphere.radius + sphere.center.length() );
1366
+ const regularFarPosition = cameraDistance - this._initialFar;
1367
+
1368
+ const minFarPos = Math.min( adjustedFarPosition, regularFarPosition );
1369
+ this.object.far = cameraDistance - minFarPos;
1370
+
1371
+ this.object.updateProjectionMatrix();
1372
+
1373
+ } else {
1374
+
1375
+ let update = false;
1376
+
1377
+ if ( this.object.near != this._initialNear ) {
1378
+
1379
+ this.object.near = this._initialNear;
1380
+ update = true;
1381
+
1382
+ }
1383
+
1384
+ if ( this.object.far != this._initialFar ) {
1385
+
1386
+ this.object.far = this._initialFar;
1387
+ update = true;
1388
+
1389
+ }
1390
+
1391
+ if ( update ) {
1392
+
1393
+ this.object.updateProjectionMatrix();
1394
+
1395
+ }
1396
+
1397
+ }
1398
+
1399
+ }
1400
+
1401
+ }
1402
+
1403
+ /**
1404
+ * Calculate the angular speed
1405
+ *
1406
+ * @param {Number} p0 Position at t0
1407
+ * @param {Number} p1 Position at t1
1408
+ * @param {Number} t0 Initial time in milliseconds
1409
+ * @param {Number} t1 Ending time in milliseconds
1410
+ * @returns {Number}
1411
+ */
1412
+ calculateAngularSpeed( p0, p1, t0, t1 ) {
1413
+
1414
+ const s = p1 - p0;
1415
+ const t = ( t1 - t0 ) / 1000;
1416
+ if ( t == 0 ) {
1417
+
1418
+ return 0;
1419
+
1420
+ }
1421
+
1422
+ return s / t;
1423
+
1424
+ }
1425
+
1426
+ /**
1427
+ * Calculate the distance between two pointers
1428
+ * @param {PointerEvent} p0 The first pointer
1429
+ * @param {PointerEvent} p1 The second pointer
1430
+ * @returns {number} The distance between the two pointers
1431
+ */
1432
+ calculatePointersDistance( p0, p1 ) {
1433
+
1434
+ return Math.sqrt( Math.pow( p1.clientX - p0.clientX, 2 ) + Math.pow( p1.clientY - p0.clientY, 2 ) );
1435
+
1436
+ }
1437
+
1438
+ /**
1439
+ * Calculate the rotation axis as the vector perpendicular between two vectors
1440
+ * @param {Vector3} vec1 The first vector
1441
+ * @param {Vector3} vec2 The second vector
1442
+ * @returns {Vector3} The normalized rotation axis
1443
+ */
1444
+ calculateRotationAxis( vec1, vec2 ) {
1445
+
1446
+ this._rotationMatrix.extractRotation( this._cameraMatrixState );
1447
+ this._quat.setFromRotationMatrix( this._rotationMatrix );
1448
+
1449
+ this._rotationAxis.crossVectors( vec1, vec2 ).applyQuaternion( this._quat );
1450
+ return this._rotationAxis.normalize().clone();
1451
+
1452
+ }
1453
+
1454
+ /**
1455
+ * Calculate the trackball radius so that gizmo's diameter will be 2/3 of the minimum side of the camera frustum
1456
+ * @param {Camera} camera
1457
+ * @returns {Number} The trackball radius
1458
+ */
1459
+ calculateTbRadius( camera ) {
1460
+
1461
+ const distance = camera.position.distanceTo( this._gizmos.position );
1462
+
1463
+ if ( camera.type == 'PerspectiveCamera' ) {
1464
+
1465
+ const halfFovV = MathUtils.DEG2RAD * camera.fov * 0.5; //vertical fov/2 in radians
1466
+ const halfFovH = Math.atan( ( camera.aspect ) * Math.tan( halfFovV ) ); //horizontal fov/2 in radians
1467
+ return Math.tan( Math.min( halfFovV, halfFovH ) ) * distance * this.radiusFactor;
1468
+
1469
+ } else if ( camera.type == 'OrthographicCamera' ) {
1470
+
1471
+ return Math.min( camera.top, camera.right ) * this.radiusFactor;
1472
+
1473
+ }
1474
+
1475
+ }
1476
+
1477
+ /**
1478
+ * Focus operation consist of positioning the point of interest in front of the camera and a slightly zoom in
1479
+ * @param {Vector3} point The point of interest
1480
+ * @param {Number} size Scale factor
1481
+ * @param {Number} amount Amount of operation to be completed (used for focus animations, default is complete full operation)
1482
+ */
1483
+ focus( point, size, amount = 1 ) {
1484
+
1485
+ //move center of camera (along with gizmos) towards point of interest
1486
+ _offset.copy( point ).sub( this._gizmos.position ).multiplyScalar( amount );
1487
+ this._translationMatrix.makeTranslation( _offset.x, _offset.y, _offset.z );
1488
+
1489
+ _gizmoMatrixStateTemp.copy( this._gizmoMatrixState );
1490
+ this._gizmoMatrixState.premultiply( this._translationMatrix );
1491
+ this._gizmoMatrixState.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
1492
+
1493
+ _cameraMatrixStateTemp.copy( this._cameraMatrixState );
1494
+ this._cameraMatrixState.premultiply( this._translationMatrix );
1495
+ this._cameraMatrixState.decompose( this.object.position, this.object.quaternion, this.object.scale );
1496
+
1497
+ //apply zoom
1498
+ if ( this.enableZoom ) {
1499
+
1500
+ this.applyTransformMatrix( this.scale( size, this._gizmos.position ) );
1501
+
1502
+ }
1503
+
1504
+ this._gizmoMatrixState.copy( _gizmoMatrixStateTemp );
1505
+ this._cameraMatrixState.copy( _cameraMatrixStateTemp );
1506
+
1507
+ }
1508
+
1509
+ /**
1510
+ * Draw a grid and add it to the scene
1511
+ */
1512
+ drawGrid() {
1513
+
1514
+ if ( this.scene != null ) {
1515
+
1516
+ const color = 0x888888;
1517
+ const multiplier = 3;
1518
+ let size, divisions, maxLength, tick;
1519
+
1520
+ if ( this.object.isOrthographicCamera ) {
1521
+
1522
+ const width = this.object.right - this.object.left;
1523
+ const height = this.object.bottom - this.object.top;
1524
+
1525
+ maxLength = Math.max( width, height );
1526
+ tick = maxLength / 20;
1527
+
1528
+ size = maxLength / this.object.zoom * multiplier;
1529
+ divisions = size / tick * this.object.zoom;
1530
+
1531
+ } else if ( this.object.isPerspectiveCamera ) {
1532
+
1533
+ const distance = this.object.position.distanceTo( this._gizmos.position );
1534
+ const halfFovV = MathUtils.DEG2RAD * this.object.fov * 0.5;
1535
+ const halfFovH = Math.atan( ( this.object.aspect ) * Math.tan( halfFovV ) );
1536
+
1537
+ maxLength = Math.tan( Math.max( halfFovV, halfFovH ) ) * distance * 2;
1538
+ tick = maxLength / 20;
1539
+
1540
+ size = maxLength * multiplier;
1541
+ divisions = size / tick;
1542
+
1543
+ }
1544
+
1545
+ if ( this._grid == null ) {
1546
+
1547
+ this._grid = new GridHelper( size, divisions, color, color );
1548
+ this._grid.position.copy( this._gizmos.position );
1549
+ this._gridPosition.copy( this._grid.position );
1550
+ this._grid.quaternion.copy( this.object.quaternion );
1551
+ this._grid.rotateX( Math.PI * 0.5 );
1552
+
1553
+ this.scene.add( this._grid );
1554
+
1555
+ }
1556
+
1557
+ }
1558
+
1559
+ }
1560
+
1561
+ /**
1562
+ * Remove all listeners, stop animations and clean scene
1563
+ */
1564
+ dispose() {
1565
+
1566
+ if ( this._animationId != - 1 ) {
1567
+
1568
+ window.cancelAnimationFrame( this._animationId );
1569
+
1570
+ }
1571
+
1572
+ this.disconnect();
1573
+
1574
+ if ( this.scene !== null ) this.scene.remove( this._gizmos );
1575
+ this.disposeGrid();
1576
+
1577
+ }
1578
+
1579
+ /**
1580
+ * remove the grid from the scene
1581
+ */
1582
+ disposeGrid() {
1583
+
1584
+ if ( this._grid != null && this.scene != null ) {
1585
+
1586
+ this.scene.remove( this._grid );
1587
+ this._grid = null;
1588
+
1589
+ }
1590
+
1591
+ }
1592
+
1593
+ /**
1594
+ * Compute the easing out cubic function for ease out effect in animation
1595
+ * @param {Number} t The absolute progress of the animation in the bound of 0 (beginning of the) and 1 (ending of animation)
1596
+ * @returns {Number} Result of easing out cubic at time t
1597
+ */
1598
+ easeOutCubic( t ) {
1599
+
1600
+ return 1 - Math.pow( 1 - t, 3 );
1601
+
1602
+ }
1603
+
1604
+ /**
1605
+ * Make rotation gizmos more or less visible
1606
+ * @param {Boolean} isActive If true, make gizmos more visible
1607
+ */
1608
+ activateGizmos( isActive ) {
1609
+
1610
+ const gizmoX = this._gizmos.children[ 0 ];
1611
+ const gizmoY = this._gizmos.children[ 1 ];
1612
+ const gizmoZ = this._gizmos.children[ 2 ];
1613
+
1614
+ if ( isActive ) {
1615
+
1616
+ gizmoX.material.setValues( { opacity: 1 } );
1617
+ gizmoY.material.setValues( { opacity: 1 } );
1618
+ gizmoZ.material.setValues( { opacity: 1 } );
1619
+
1620
+ } else {
1621
+
1622
+ gizmoX.material.setValues( { opacity: 0.6 } );
1623
+ gizmoY.material.setValues( { opacity: 0.6 } );
1624
+ gizmoZ.material.setValues( { opacity: 0.6 } );
1625
+
1626
+ }
1627
+
1628
+ }
1629
+
1630
+ /**
1631
+ * Calculate the cursor position in NDC
1632
+ *
1633
+ * @param {number} cursorX Cursor horizontal coordinate within the canvas
1634
+ * @param {number} cursorY Cursor vertical coordinate within the canvas
1635
+ * @param {HTMLElement} canvas The canvas where the renderer draws its output
1636
+ * @returns {Vector2} Cursor normalized position inside the canvas
1637
+ */
1638
+ getCursorNDC( cursorX, cursorY, canvas ) {
1639
+
1640
+ const canvasRect = canvas.getBoundingClientRect();
1641
+ this._v2_1.setX( ( ( cursorX - canvasRect.left ) / canvasRect.width ) * 2 - 1 );
1642
+ this._v2_1.setY( ( ( canvasRect.bottom - cursorY ) / canvasRect.height ) * 2 - 1 );
1643
+ return this._v2_1.clone();
1644
+
1645
+ }
1646
+
1647
+ /**
1648
+ * Calculate the cursor position inside the canvas x/y coordinates with the origin being in the center of the canvas
1649
+ *
1650
+ * @param {Number} cursorX Cursor horizontal coordinate within the canvas
1651
+ * @param {Number} cursorY Cursor vertical coordinate within the canvas
1652
+ * @param {HTMLElement} canvas The canvas where the renderer draws its output
1653
+ * @returns {Vector2} Cursor position inside the canvas
1654
+ */
1655
+ getCursorPosition( cursorX, cursorY, canvas ) {
1656
+
1657
+ this._v2_1.copy( this.getCursorNDC( cursorX, cursorY, canvas ) );
1658
+ this._v2_1.x *= ( this.object.right - this.object.left ) * 0.5;
1659
+ this._v2_1.y *= ( this.object.top - this.object.bottom ) * 0.5;
1660
+ return this._v2_1.clone();
1661
+
1662
+ }
1663
+
1664
+ /**
1665
+ * Set the camera to be controlled
1666
+ * @param {Camera} camera The virtual camera to be controlled
1667
+ */
1668
+ setCamera( camera ) {
1669
+
1670
+ camera.lookAt( this.target );
1671
+ camera.updateMatrix();
1672
+
1673
+ //setting state
1674
+ if ( camera.type == 'PerspectiveCamera' ) {
1675
+
1676
+ this._fov0 = camera.fov;
1677
+ this._fovState = camera.fov;
1678
+
1679
+ }
1680
+
1681
+ this._cameraMatrixState0.copy( camera.matrix );
1682
+ this._cameraMatrixState.copy( this._cameraMatrixState0 );
1683
+ this._cameraProjectionState.copy( camera.projectionMatrix );
1684
+ this._zoom0 = camera.zoom;
1685
+ this._zoomState = this._zoom0;
1686
+
1687
+ this._initialNear = camera.near;
1688
+ this._nearPos0 = camera.position.distanceTo( this.target ) - camera.near;
1689
+ this._nearPos = this._initialNear;
1690
+
1691
+ this._initialFar = camera.far;
1692
+ this._farPos0 = camera.position.distanceTo( this.target ) - camera.far;
1693
+ this._farPos = this._initialFar;
1694
+
1695
+ this._up0.copy( camera.up );
1696
+ this._upState.copy( camera.up );
1697
+
1698
+ this.object = camera;
1699
+ this.object.updateProjectionMatrix();
1700
+
1701
+ //making gizmos
1702
+ this._tbRadius = this.calculateTbRadius( camera );
1703
+ this.makeGizmos( this.target, this._tbRadius );
1704
+
1705
+ }
1706
+
1707
+ /**
1708
+ * Set gizmos visibility
1709
+ * @param {Boolean} value Value of gizmos visibility
1710
+ */
1711
+ setGizmosVisible( value ) {
1712
+
1713
+ this._gizmos.visible = value;
1714
+ this.dispatchEvent( _changeEvent );
1715
+
1716
+ }
1717
+
1718
+ /**
1719
+ * Set gizmos radius factor and redraws gizmos
1720
+ * @param {Float} value Value of radius factor
1721
+ */
1722
+ setTbRadius( value ) {
1723
+
1724
+ this.radiusFactor = value;
1725
+ this._tbRadius = this.calculateTbRadius( this.object );
1726
+
1727
+ const curve = new EllipseCurve( 0, 0, this._tbRadius, this._tbRadius );
1728
+ const points = curve.getPoints( this._curvePts );
1729
+ const curveGeometry = new BufferGeometry().setFromPoints( points );
1730
+
1731
+
1732
+ for ( const gizmo in this._gizmos.children ) {
1733
+
1734
+ this._gizmos.children[ gizmo ].geometry = curveGeometry;
1735
+
1736
+ }
1737
+
1738
+ this.dispatchEvent( _changeEvent );
1739
+
1740
+ }
1741
+
1742
+ /**
1743
+ * Creates the rotation gizmos matching trackball center and radius
1744
+ * @param {Vector3} tbCenter The trackball center
1745
+ * @param {number} tbRadius The trackball radius
1746
+ */
1747
+ makeGizmos( tbCenter, tbRadius ) {
1748
+
1749
+ const curve = new EllipseCurve( 0, 0, tbRadius, tbRadius );
1750
+ const points = curve.getPoints( this._curvePts );
1751
+
1752
+ //geometry
1753
+ const curveGeometry = new BufferGeometry().setFromPoints( points );
1754
+
1755
+ //material
1756
+ const curveMaterialX = new LineBasicMaterial( { color: 0xff8080, fog: false, transparent: true, opacity: 0.6 } );
1757
+ const curveMaterialY = new LineBasicMaterial( { color: 0x80ff80, fog: false, transparent: true, opacity: 0.6 } );
1758
+ const curveMaterialZ = new LineBasicMaterial( { color: 0x8080ff, fog: false, transparent: true, opacity: 0.6 } );
1759
+
1760
+ //line
1761
+ const gizmoX = new Line( curveGeometry, curveMaterialX );
1762
+ const gizmoY = new Line( curveGeometry, curveMaterialY );
1763
+ const gizmoZ = new Line( curveGeometry, curveMaterialZ );
1764
+
1765
+ const rotation = Math.PI * 0.5;
1766
+ gizmoX.rotation.x = rotation;
1767
+ gizmoY.rotation.y = rotation;
1768
+
1769
+
1770
+ //setting state
1771
+ this._gizmoMatrixState0.identity().setPosition( tbCenter );
1772
+ this._gizmoMatrixState.copy( this._gizmoMatrixState0 );
1773
+
1774
+ if ( this.object.zoom !== 1 ) {
1775
+
1776
+ //adapt gizmos size to camera zoom
1777
+ const size = 1 / this.object.zoom;
1778
+ this._scaleMatrix.makeScale( size, size, size );
1779
+ this._translationMatrix.makeTranslation( - tbCenter.x, - tbCenter.y, - tbCenter.z );
1780
+
1781
+ this._gizmoMatrixState.premultiply( this._translationMatrix ).premultiply( this._scaleMatrix );
1782
+ this._translationMatrix.makeTranslation( tbCenter.x, tbCenter.y, tbCenter.z );
1783
+ this._gizmoMatrixState.premultiply( this._translationMatrix );
1784
+
1785
+ }
1786
+
1787
+ this._gizmoMatrixState.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
1788
+
1789
+ //
1790
+
1791
+ this._gizmos.traverse( function ( object ) {
1792
+
1793
+ if ( object.isLine ) {
1794
+
1795
+ object.geometry.dispose();
1796
+ object.material.dispose();
1797
+
1798
+ }
1799
+
1800
+ } );
1801
+
1802
+ this._gizmos.clear();
1803
+
1804
+ //
1805
+
1806
+ this._gizmos.add( gizmoX );
1807
+ this._gizmos.add( gizmoY );
1808
+ this._gizmos.add( gizmoZ );
1809
+
1810
+ }
1811
+
1812
+ /**
1813
+ * Perform animation for focus operation
1814
+ * @param {Number} time Instant in which this function is called as performance.now()
1815
+ * @param {Vector3} point Point of interest for focus operation
1816
+ * @param {Matrix4} cameraMatrix Camera matrix
1817
+ * @param {Matrix4} gizmoMatrix Gizmos matrix
1818
+ */
1819
+ onFocusAnim( time, point, cameraMatrix, gizmoMatrix ) {
1820
+
1821
+ if ( this._timeStart == - 1 ) {
1822
+
1823
+ //animation start
1824
+ this._timeStart = time;
1825
+
1826
+ }
1827
+
1828
+ if ( this._state == STATE.ANIMATION_FOCUS ) {
1829
+
1830
+ const deltaTime = time - this._timeStart;
1831
+ const animTime = deltaTime / this.focusAnimationTime;
1832
+
1833
+ this._gizmoMatrixState.copy( gizmoMatrix );
1834
+
1835
+ if ( animTime >= 1 ) {
1836
+
1837
+ //animation end
1838
+
1839
+ this._gizmoMatrixState.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
1840
+
1841
+ this.focus( point, this.scaleFactor );
1842
+
1843
+ this._timeStart = - 1;
1844
+ this.updateTbState( STATE.IDLE, false );
1845
+ this.activateGizmos( false );
1846
+
1847
+ this.dispatchEvent( _changeEvent );
1848
+
1849
+ } else {
1850
+
1851
+ const amount = this.easeOutCubic( animTime );
1852
+ const size = ( ( 1 - amount ) + ( this.scaleFactor * amount ) );
1853
+
1854
+ this._gizmoMatrixState.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
1855
+ this.focus( point, size, amount );
1856
+
1857
+ this.dispatchEvent( _changeEvent );
1858
+ const self = this;
1859
+ this._animationId = window.requestAnimationFrame( function ( t ) {
1860
+
1861
+ self.onFocusAnim( t, point, cameraMatrix, gizmoMatrix.clone() );
1862
+
1863
+ } );
1864
+
1865
+ }
1866
+
1867
+ } else {
1868
+
1869
+ //interrupt animation
1870
+
1871
+ this._animationId = - 1;
1872
+ this._timeStart = - 1;
1873
+
1874
+ }
1875
+
1876
+ }
1877
+
1878
+ /**
1879
+ * Perform animation for rotation operation
1880
+ * @param {Number} time Instant in which this function is called as performance.now()
1881
+ * @param {Vector3} rotationAxis Rotation axis
1882
+ * @param {number} w0 Initial angular velocity
1883
+ */
1884
+ onRotationAnim( time, rotationAxis, w0 ) {
1885
+
1886
+ if ( this._timeStart == - 1 ) {
1887
+
1888
+ //animation start
1889
+ this._anglePrev = 0;
1890
+ this._angleCurrent = 0;
1891
+ this._timeStart = time;
1892
+
1893
+ }
1894
+
1895
+ if ( this._state == STATE.ANIMATION_ROTATE ) {
1896
+
1897
+ //w = w0 + alpha * t
1898
+ const deltaTime = ( time - this._timeStart ) / 1000;
1899
+ const w = w0 + ( ( - this.dampingFactor ) * deltaTime );
1900
+
1901
+ if ( w > 0 ) {
1902
+
1903
+ //tetha = 0.5 * alpha * t^2 + w0 * t + tetha0
1904
+ this._angleCurrent = 0.5 * ( - this.dampingFactor ) * Math.pow( deltaTime, 2 ) + w0 * deltaTime + 0;
1905
+ this.applyTransformMatrix( this.rotate( rotationAxis, this._angleCurrent ) );
1906
+ this.dispatchEvent( _changeEvent );
1907
+ const self = this;
1908
+ this._animationId = window.requestAnimationFrame( function ( t ) {
1909
+
1910
+ self.onRotationAnim( t, rotationAxis, w0 );
1911
+
1912
+ } );
1913
+
1914
+ } else {
1915
+
1916
+ this._animationId = - 1;
1917
+ this._timeStart = - 1;
1918
+
1919
+ this.updateTbState( STATE.IDLE, false );
1920
+ this.activateGizmos( false );
1921
+
1922
+ this.dispatchEvent( _changeEvent );
1923
+
1924
+ }
1925
+
1926
+ } else {
1927
+
1928
+ //interrupt animation
1929
+
1930
+ this._animationId = - 1;
1931
+ this._timeStart = - 1;
1932
+
1933
+ if ( this._state != STATE.ROTATE ) {
1934
+
1935
+ this.activateGizmos( false );
1936
+ this.dispatchEvent( _changeEvent );
1937
+
1938
+ }
1939
+
1940
+ }
1941
+
1942
+ }
1943
+
1944
+
1945
+ /**
1946
+ * Perform pan operation moving camera between two points
1947
+ *
1948
+ * @param {Vector3} p0 Initial point
1949
+ * @param {Vector3} p1 Ending point
1950
+ * @param {Boolean} [adjust=false] If movement should be adjusted considering camera distance (Perspective only)
1951
+ * @returns {Object}
1952
+ */
1953
+ pan( p0, p1, adjust = false ) {
1954
+
1955
+ const movement = p0.clone().sub( p1 );
1956
+
1957
+ if ( this.object.isOrthographicCamera ) {
1958
+
1959
+ //adjust movement amount
1960
+ movement.multiplyScalar( 1 / this.object.zoom );
1961
+
1962
+ } else if ( this.object.isPerspectiveCamera && adjust ) {
1963
+
1964
+ //adjust movement amount
1965
+ this._v3_1.setFromMatrixPosition( this._cameraMatrixState0 ); //camera's initial position
1966
+ this._v3_2.setFromMatrixPosition( this._gizmoMatrixState0 ); //gizmo's initial position
1967
+ const distanceFactor = this._v3_1.distanceTo( this._v3_2 ) / this.object.position.distanceTo( this._gizmos.position );
1968
+ movement.multiplyScalar( 1 / distanceFactor );
1969
+
1970
+ }
1971
+
1972
+ this._v3_1.set( movement.x, movement.y, 0 ).applyQuaternion( this.object.quaternion );
1973
+
1974
+ this._m4_1.makeTranslation( this._v3_1.x, this._v3_1.y, this._v3_1.z );
1975
+
1976
+ this.setTransformationMatrices( this._m4_1, this._m4_1 );
1977
+ return _transformation;
1978
+
1979
+ }
1980
+
1981
+ /**
1982
+ * Reset trackball
1983
+ */
1984
+ reset() {
1985
+
1986
+ this.object.zoom = this._zoom0;
1987
+
1988
+ if ( this.object.isPerspectiveCamera ) {
1989
+
1990
+ this.object.fov = this._fov0;
1991
+
1992
+ }
1993
+
1994
+ this.object.near = this._nearPos;
1995
+ this.object.far = this._farPos;
1996
+ this._cameraMatrixState.copy( this._cameraMatrixState0 );
1997
+ this._cameraMatrixState.decompose( this.object.position, this.object.quaternion, this.object.scale );
1998
+ this.object.up.copy( this._up0 );
1999
+
2000
+ this.object.updateMatrix();
2001
+ this.object.updateProjectionMatrix();
2002
+
2003
+ this._gizmoMatrixState.copy( this._gizmoMatrixState0 );
2004
+ this._gizmoMatrixState0.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
2005
+ this._gizmos.updateMatrix();
2006
+
2007
+ this._tbRadius = this.calculateTbRadius( this.object );
2008
+ this.makeGizmos( this._gizmos.position, this._tbRadius );
2009
+
2010
+ this.object.lookAt( this._gizmos.position );
2011
+
2012
+ this.updateTbState( STATE.IDLE, false );
2013
+
2014
+ this.dispatchEvent( _changeEvent );
2015
+
2016
+ }
2017
+
2018
+ /**
2019
+ * Rotate the camera around an axis passing by trackball's center
2020
+ * @param {Vector3} axis Rotation axis
2021
+ * @param {number} angle Angle in radians
2022
+ * @returns {Object} Object with 'camera' field containing transformation matrix resulting from the operation to be applied to the camera
2023
+ */
2024
+ rotate( axis, angle ) {
2025
+
2026
+ const point = this._gizmos.position; //rotation center
2027
+ this._translationMatrix.makeTranslation( - point.x, - point.y, - point.z );
2028
+ this._rotationMatrix.makeRotationAxis( axis, - angle );
2029
+
2030
+ //rotate camera
2031
+ this._m4_1.makeTranslation( point.x, point.y, point.z );
2032
+ this._m4_1.multiply( this._rotationMatrix );
2033
+ this._m4_1.multiply( this._translationMatrix );
2034
+
2035
+ this.setTransformationMatrices( this._m4_1 );
2036
+
2037
+ return _transformation;
2038
+
2039
+ }
2040
+
2041
+ copyState() {
2042
+
2043
+ let state;
2044
+ if ( this.object.isOrthographicCamera ) {
2045
+
2046
+ state = JSON.stringify( {
2047
+ arcballState: {
2048
+ cameraFar: this.object.far,
2049
+ cameraMatrix: this.object.matrix,
2050
+ cameraNear: this.object.near,
2051
+ cameraUp: this.object.up,
2052
+ cameraZoom: this.object.zoom,
2053
+ gizmoMatrix: this._gizmos.matrix
2054
+
2055
+ }
2056
+ } );
2057
+
2058
+ } else if ( this.object.isPerspectiveCamera ) {
2059
+
2060
+ state = JSON.stringify( {
2061
+ arcballState: {
2062
+ cameraFar: this.object.far,
2063
+ cameraFov: this.object.fov,
2064
+ cameraMatrix: this.object.matrix,
2065
+ cameraNear: this.object.near,
2066
+ cameraUp: this.object.up,
2067
+ cameraZoom: this.object.zoom,
2068
+ gizmoMatrix: this._gizmos.matrix
2069
+
2070
+ }
2071
+ } );
2072
+
2073
+ }
2074
+
2075
+ navigator.clipboard.writeText( state );
2076
+
2077
+ }
2078
+
2079
+ pasteState() {
2080
+
2081
+ const self = this;
2082
+ navigator.clipboard.readText().then( function resolved( value ) {
2083
+
2084
+ self.setStateFromJSON( value );
2085
+
2086
+ } );
2087
+
2088
+ }
2089
+
2090
+ /**
2091
+ * Save the current state of the control. This can later be recover with .reset
2092
+ */
2093
+ saveState() {
2094
+
2095
+ this._cameraMatrixState0.copy( this.object.matrix );
2096
+ this._gizmoMatrixState0.copy( this._gizmos.matrix );
2097
+ this._nearPos = this.object.near;
2098
+ this._farPos = this.object.far;
2099
+ this._zoom0 = this.object.zoom;
2100
+ this._up0.copy( this.object.up );
2101
+
2102
+ if ( this.object.isPerspectiveCamera ) {
2103
+
2104
+ this._fov0 = this.object.fov;
2105
+
2106
+ }
2107
+
2108
+ }
2109
+
2110
+ /**
2111
+ * Perform uniform scale operation around a given point
2112
+ * @param {Number} size Scale factor
2113
+ * @param {Vector3} point Point around which scale
2114
+ * @param {Boolean} scaleGizmos If gizmos should be scaled (Perspective only)
2115
+ * @returns {Object} Object with 'camera' and 'gizmo' fields containing transformation matrices resulting from the operation to be applied to the camera and gizmos
2116
+ */
2117
+ scale( size, point, scaleGizmos = true ) {
2118
+
2119
+ _scalePointTemp.copy( point );
2120
+ let sizeInverse = 1 / size;
2121
+
2122
+ if ( this.object.isOrthographicCamera ) {
2123
+
2124
+ //camera zoom
2125
+ this.object.zoom = this._zoomState;
2126
+ this.object.zoom *= size;
2127
+
2128
+ //check min and max zoom
2129
+ if ( this.object.zoom > this.maxZoom ) {
2130
+
2131
+ this.object.zoom = this.maxZoom;
2132
+ sizeInverse = this._zoomState / this.maxZoom;
2133
+
2134
+ } else if ( this.object.zoom < this.minZoom ) {
2135
+
2136
+ this.object.zoom = this.minZoom;
2137
+ sizeInverse = this._zoomState / this.minZoom;
2138
+
2139
+ }
2140
+
2141
+ this.object.updateProjectionMatrix();
2142
+
2143
+ this._v3_1.setFromMatrixPosition( this._gizmoMatrixState ); //gizmos position
2144
+
2145
+ //scale gizmos so they appear in the same spot having the same dimension
2146
+ this._scaleMatrix.makeScale( sizeInverse, sizeInverse, sizeInverse );
2147
+ this._translationMatrix.makeTranslation( - this._v3_1.x, - this._v3_1.y, - this._v3_1.z );
2148
+
2149
+ this._m4_2.makeTranslation( this._v3_1.x, this._v3_1.y, this._v3_1.z ).multiply( this._scaleMatrix );
2150
+ this._m4_2.multiply( this._translationMatrix );
2151
+
2152
+
2153
+ //move camera and gizmos to obtain pinch effect
2154
+ _scalePointTemp.sub( this._v3_1 );
2155
+
2156
+ const amount = _scalePointTemp.clone().multiplyScalar( sizeInverse );
2157
+ _scalePointTemp.sub( amount );
2158
+
2159
+ this._m4_1.makeTranslation( _scalePointTemp.x, _scalePointTemp.y, _scalePointTemp.z );
2160
+ this._m4_2.premultiply( this._m4_1 );
2161
+
2162
+ this.setTransformationMatrices( this._m4_1, this._m4_2 );
2163
+ return _transformation;
2164
+
2165
+ } else if ( this.object.isPerspectiveCamera ) {
2166
+
2167
+ this._v3_1.setFromMatrixPosition( this._cameraMatrixState );
2168
+ this._v3_2.setFromMatrixPosition( this._gizmoMatrixState );
2169
+
2170
+ //move camera
2171
+ let distance = this._v3_1.distanceTo( _scalePointTemp );
2172
+ let amount = distance - ( distance * sizeInverse );
2173
+
2174
+ //check min and max distance
2175
+ const newDistance = distance - amount;
2176
+ if ( newDistance < this.minDistance ) {
2177
+
2178
+ sizeInverse = this.minDistance / distance;
2179
+ amount = distance - ( distance * sizeInverse );
2180
+
2181
+ } else if ( newDistance > this.maxDistance ) {
2182
+
2183
+ sizeInverse = this.maxDistance / distance;
2184
+ amount = distance - ( distance * sizeInverse );
2185
+
2186
+ }
2187
+
2188
+ _offset.copy( _scalePointTemp ).sub( this._v3_1 ).normalize().multiplyScalar( amount );
2189
+
2190
+ this._m4_1.makeTranslation( _offset.x, _offset.y, _offset.z );
2191
+
2192
+
2193
+ if ( scaleGizmos ) {
2194
+
2195
+ //scale gizmos so they appear in the same spot having the same dimension
2196
+ const pos = this._v3_2;
2197
+
2198
+ distance = pos.distanceTo( _scalePointTemp );
2199
+ amount = distance - ( distance * sizeInverse );
2200
+ _offset.copy( _scalePointTemp ).sub( this._v3_2 ).normalize().multiplyScalar( amount );
2201
+
2202
+ this._translationMatrix.makeTranslation( pos.x, pos.y, pos.z );
2203
+ this._scaleMatrix.makeScale( sizeInverse, sizeInverse, sizeInverse );
2204
+
2205
+ this._m4_2.makeTranslation( _offset.x, _offset.y, _offset.z ).multiply( this._translationMatrix );
2206
+ this._m4_2.multiply( this._scaleMatrix );
2207
+
2208
+ this._translationMatrix.makeTranslation( - pos.x, - pos.y, - pos.z );
2209
+
2210
+ this._m4_2.multiply( this._translationMatrix );
2211
+ this.setTransformationMatrices( this._m4_1, this._m4_2 );
2212
+
2213
+
2214
+ } else {
2215
+
2216
+ this.setTransformationMatrices( this._m4_1 );
2217
+
2218
+ }
2219
+
2220
+ return _transformation;
2221
+
2222
+ }
2223
+
2224
+ }
2225
+
2226
+ /**
2227
+ * Set camera fov
2228
+ * @param {Number} value fov to be set
2229
+ */
2230
+ setFov( value ) {
2231
+
2232
+ if ( this.object.isPerspectiveCamera ) {
2233
+
2234
+ this.object.fov = MathUtils.clamp( value, this.minFov, this.maxFov );
2235
+ this.object.updateProjectionMatrix();
2236
+
2237
+ }
2238
+
2239
+ }
2240
+
2241
+ /**
2242
+ * Set values in transformation object
2243
+ *
2244
+ * @param {Matrix4} [camera=null] Transformation to be applied to the camera
2245
+ * @param {Matrix4} [gizmos=null] Transformation to be applied to gizmos
2246
+ */
2247
+ setTransformationMatrices( camera = null, gizmos = null ) {
2248
+
2249
+ if ( camera != null ) {
2250
+
2251
+ if ( _transformation.camera != null ) {
2252
+
2253
+ _transformation.camera.copy( camera );
2254
+
2255
+ } else {
2256
+
2257
+ _transformation.camera = camera.clone();
2258
+
2259
+ }
2260
+
2261
+ } else {
2262
+
2263
+ _transformation.camera = null;
2264
+
2265
+ }
2266
+
2267
+ if ( gizmos != null ) {
2268
+
2269
+ if ( _transformation.gizmos != null ) {
2270
+
2271
+ _transformation.gizmos.copy( gizmos );
2272
+
2273
+ } else {
2274
+
2275
+ _transformation.gizmos = gizmos.clone();
2276
+
2277
+ }
2278
+
2279
+ } else {
2280
+
2281
+ _transformation.gizmos = null;
2282
+
2283
+ }
2284
+
2285
+ }
2286
+
2287
+ /**
2288
+ * Rotate camera around its direction axis passing by a given point by a given angle
2289
+ *
2290
+ * @param {Vector3} point The point where the rotation axis is passing trough
2291
+ * @param {Number} angle Angle in radians
2292
+ * @returns {Object} The computed transformation matrix
2293
+ */
2294
+ zRotate( point, angle ) {
2295
+
2296
+ this._rotationMatrix.makeRotationAxis( this._rotationAxis, angle );
2297
+ this._translationMatrix.makeTranslation( - point.x, - point.y, - point.z );
2298
+
2299
+ this._m4_1.makeTranslation( point.x, point.y, point.z );
2300
+ this._m4_1.multiply( this._rotationMatrix );
2301
+ this._m4_1.multiply( this._translationMatrix );
2302
+
2303
+ this._v3_1.setFromMatrixPosition( this._gizmoMatrixState ).sub( point ); //vector from rotation center to gizmos position
2304
+ this._v3_2.copy( this._v3_1 ).applyAxisAngle( this._rotationAxis, angle ); //apply rotation
2305
+ this._v3_2.sub( this._v3_1 );
2306
+
2307
+ this._m4_2.makeTranslation( this._v3_2.x, this._v3_2.y, this._v3_2.z );
2308
+
2309
+ this.setTransformationMatrices( this._m4_1, this._m4_2 );
2310
+ return _transformation;
2311
+
2312
+ }
2313
+
2314
+
2315
+ getRaycaster() {
2316
+
2317
+ return _raycaster;
2318
+
2319
+ }
2320
+
2321
+
2322
+ /**
2323
+ * Unproject the cursor on the 3D object surface
2324
+ *
2325
+ * @param {Vector2} cursor Cursor coordinates in NDC
2326
+ * @param {Camera} camera Virtual camera
2327
+ * @returns {Vector3|null} The point of intersection with the model, if exist, null otherwise
2328
+ */
2329
+ unprojectOnObj( cursor, camera ) {
2330
+
2331
+ const raycaster = this.getRaycaster();
2332
+ raycaster.near = camera.near;
2333
+ raycaster.far = camera.far;
2334
+ raycaster.setFromCamera( cursor, camera );
2335
+
2336
+ const intersect = raycaster.intersectObjects( this.scene.children, true );
2337
+
2338
+ for ( let i = 0; i < intersect.length; i ++ ) {
2339
+
2340
+ if ( intersect[ i ].object.uuid != this._gizmos.uuid && intersect[ i ].face != null ) {
2341
+
2342
+ return intersect[ i ].point.clone();
2343
+
2344
+ }
2345
+
2346
+ }
2347
+
2348
+ return null;
2349
+
2350
+ }
2351
+
2352
+ /**
2353
+ * Unproject the cursor on the trackball surface
2354
+ * @param {Camera} camera The virtual camera
2355
+ * @param {Number} cursorX Cursor horizontal coordinate on screen
2356
+ * @param {Number} cursorY Cursor vertical coordinate on screen
2357
+ * @param {HTMLElement} canvas The canvas where the renderer draws its output
2358
+ * @param {number} tbRadius The trackball radius
2359
+ * @returns {Vector3} The unprojected point on the trackball surface
2360
+ */
2361
+ unprojectOnTbSurface( camera, cursorX, cursorY, canvas, tbRadius ) {
2362
+
2363
+ if ( camera.type == 'OrthographicCamera' ) {
2364
+
2365
+ this._v2_1.copy( this.getCursorPosition( cursorX, cursorY, canvas ) );
2366
+ this._v3_1.set( this._v2_1.x, this._v2_1.y, 0 );
2367
+
2368
+ const x2 = Math.pow( this._v2_1.x, 2 );
2369
+ const y2 = Math.pow( this._v2_1.y, 2 );
2370
+ const r2 = Math.pow( this._tbRadius, 2 );
2371
+
2372
+ if ( x2 + y2 <= r2 * 0.5 ) {
2373
+
2374
+ //intersection with sphere
2375
+ this._v3_1.setZ( Math.sqrt( r2 - ( x2 + y2 ) ) );
2376
+
2377
+ } else {
2378
+
2379
+ //intersection with hyperboloid
2380
+ this._v3_1.setZ( ( r2 * 0.5 ) / ( Math.sqrt( x2 + y2 ) ) );
2381
+
2382
+ }
2383
+
2384
+ return this._v3_1;
2385
+
2386
+ } else if ( camera.type == 'PerspectiveCamera' ) {
2387
+
2388
+ //unproject cursor on the near plane
2389
+ this._v2_1.copy( this.getCursorNDC( cursorX, cursorY, canvas ) );
2390
+
2391
+ this._v3_1.set( this._v2_1.x, this._v2_1.y, - 1 );
2392
+ this._v3_1.applyMatrix4( camera.projectionMatrixInverse );
2393
+
2394
+ const rayDir = this._v3_1.clone().normalize(); //unprojected ray direction
2395
+ const cameraGizmoDistance = camera.position.distanceTo( this._gizmos.position );
2396
+ const radius2 = Math.pow( tbRadius, 2 );
2397
+
2398
+ // camera
2399
+ // |\
2400
+ // | \
2401
+ // | \
2402
+ // h | \
2403
+ // | \
2404
+ // | \
2405
+ // _ _ | _ _ _\ _ _ near plane
2406
+ // l
2407
+
2408
+ const h = this._v3_1.z;
2409
+ const l = Math.sqrt( Math.pow( this._v3_1.x, 2 ) + Math.pow( this._v3_1.y, 2 ) );
2410
+
2411
+ if ( l == 0 ) {
2412
+
2413
+ //ray aligned with camera
2414
+ rayDir.set( this._v3_1.x, this._v3_1.y, tbRadius );
2415
+ return rayDir;
2416
+
2417
+ }
2418
+
2419
+ const m = h / l;
2420
+ const q = cameraGizmoDistance;
2421
+
2422
+ /*
2423
+ * calculate intersection point between unprojected ray and trackball surface
2424
+ *|y = m * x + q
2425
+ *|x^2 + y^2 = r^2
2426
+ *
2427
+ * (m^2 + 1) * x^2 + (2 * m * q) * x + q^2 - r^2 = 0
2428
+ */
2429
+ let a = Math.pow( m, 2 ) + 1;
2430
+ let b = 2 * m * q;
2431
+ let c = Math.pow( q, 2 ) - radius2;
2432
+ let delta = Math.pow( b, 2 ) - ( 4 * a * c );
2433
+
2434
+ if ( delta >= 0 ) {
2435
+
2436
+ //intersection with sphere
2437
+ this._v2_1.setX( ( - b - Math.sqrt( delta ) ) / ( 2 * a ) );
2438
+ this._v2_1.setY( m * this._v2_1.x + q );
2439
+
2440
+ const angle = MathUtils.RAD2DEG * this._v2_1.angle();
2441
+
2442
+ if ( angle >= 45 ) {
2443
+
2444
+ //if angle between intersection point and X' axis is >= 45°, return that point
2445
+ //otherwise, calculate intersection point with hyperboloid
2446
+
2447
+ const rayLength = Math.sqrt( Math.pow( this._v2_1.x, 2 ) + Math.pow( ( cameraGizmoDistance - this._v2_1.y ), 2 ) );
2448
+ rayDir.multiplyScalar( rayLength );
2449
+ rayDir.z += cameraGizmoDistance;
2450
+ return rayDir;
2451
+
2452
+ }
2453
+
2454
+ }
2455
+
2456
+ //intersection with hyperboloid
2457
+ /*
2458
+ *|y = m * x + q
2459
+ *|y = (1 / x) * (r^2 / 2)
2460
+ *
2461
+ * m * x^2 + q * x - r^2 / 2 = 0
2462
+ */
2463
+
2464
+ a = m;
2465
+ b = q;
2466
+ c = - radius2 * 0.5;
2467
+ delta = Math.pow( b, 2 ) - ( 4 * a * c );
2468
+ this._v2_1.setX( ( - b - Math.sqrt( delta ) ) / ( 2 * a ) );
2469
+ this._v2_1.setY( m * this._v2_1.x + q );
2470
+
2471
+ const rayLength = Math.sqrt( Math.pow( this._v2_1.x, 2 ) + Math.pow( ( cameraGizmoDistance - this._v2_1.y ), 2 ) );
2472
+
2473
+ rayDir.multiplyScalar( rayLength );
2474
+ rayDir.z += cameraGizmoDistance;
2475
+ return rayDir;
2476
+
2477
+ }
2478
+
2479
+ }
2480
+
2481
+
2482
+ /**
2483
+ * Unproject the cursor on the plane passing through the center of the trackball orthogonal to the camera
2484
+ * @param {Camera} camera The virtual camera
2485
+ * @param {Number} cursorX Cursor horizontal coordinate on screen
2486
+ * @param {Number} cursorY Cursor vertical coordinate on screen
2487
+ * @param {HTMLElement} canvas The canvas where the renderer draws its output
2488
+ * @param {Boolean} initialDistance If initial distance between camera and gizmos should be used for calculations instead of current (Perspective only)
2489
+ * @returns {Vector3} The unprojected point on the trackball plane
2490
+ */
2491
+ unprojectOnTbPlane( camera, cursorX, cursorY, canvas, initialDistance = false ) {
2492
+
2493
+ if ( camera.type == 'OrthographicCamera' ) {
2494
+
2495
+ this._v2_1.copy( this.getCursorPosition( cursorX, cursorY, canvas ) );
2496
+ this._v3_1.set( this._v2_1.x, this._v2_1.y, 0 );
2497
+
2498
+ return this._v3_1.clone();
2499
+
2500
+ } else if ( camera.type == 'PerspectiveCamera' ) {
2501
+
2502
+ this._v2_1.copy( this.getCursorNDC( cursorX, cursorY, canvas ) );
2503
+
2504
+ //unproject cursor on the near plane
2505
+ this._v3_1.set( this._v2_1.x, this._v2_1.y, - 1 );
2506
+ this._v3_1.applyMatrix4( camera.projectionMatrixInverse );
2507
+
2508
+ const rayDir = this._v3_1.clone().normalize(); //unprojected ray direction
2509
+
2510
+ // camera
2511
+ // |\
2512
+ // | \
2513
+ // | \
2514
+ // h | \
2515
+ // | \
2516
+ // | \
2517
+ // _ _ | _ _ _\ _ _ near plane
2518
+ // l
2519
+
2520
+ const h = this._v3_1.z;
2521
+ const l = Math.sqrt( Math.pow( this._v3_1.x, 2 ) + Math.pow( this._v3_1.y, 2 ) );
2522
+ let cameraGizmoDistance;
2523
+
2524
+ if ( initialDistance ) {
2525
+
2526
+ cameraGizmoDistance = this._v3_1.setFromMatrixPosition( this._cameraMatrixState0 ).distanceTo( this._v3_2.setFromMatrixPosition( this._gizmoMatrixState0 ) );
2527
+
2528
+ } else {
2529
+
2530
+ cameraGizmoDistance = camera.position.distanceTo( this._gizmos.position );
2531
+
2532
+ }
2533
+
2534
+ /*
2535
+ * calculate intersection point between unprojected ray and the plane
2536
+ *|y = mx + q
2537
+ *|y = 0
2538
+ *
2539
+ * x = -q/m
2540
+ */
2541
+ if ( l == 0 ) {
2542
+
2543
+ //ray aligned with camera
2544
+ rayDir.set( 0, 0, 0 );
2545
+ return rayDir;
2546
+
2547
+ }
2548
+
2549
+ const m = h / l;
2550
+ const q = cameraGizmoDistance;
2551
+ const x = - q / m;
2552
+
2553
+ const rayLength = Math.sqrt( Math.pow( q, 2 ) + Math.pow( x, 2 ) );
2554
+ rayDir.multiplyScalar( rayLength );
2555
+ rayDir.z = 0;
2556
+ return rayDir;
2557
+
2558
+ }
2559
+
2560
+ }
2561
+
2562
+ /**
2563
+ * Update camera and gizmos state
2564
+ */
2565
+ updateMatrixState() {
2566
+
2567
+ //update camera and gizmos state
2568
+ this._cameraMatrixState.copy( this.object.matrix );
2569
+ this._gizmoMatrixState.copy( this._gizmos.matrix );
2570
+
2571
+ if ( this.object.isOrthographicCamera ) {
2572
+
2573
+ this._cameraProjectionState.copy( this.object.projectionMatrix );
2574
+ this.object.updateProjectionMatrix();
2575
+ this._zoomState = this.object.zoom;
2576
+
2577
+ } else if ( this.object.isPerspectiveCamera ) {
2578
+
2579
+ this._fovState = this.object.fov;
2580
+
2581
+ }
2582
+
2583
+ }
2584
+
2585
+ /**
2586
+ * Update the trackball FSA
2587
+ * @param {STATE} newState New state of the FSA
2588
+ * @param {Boolean} updateMatrices If matrices state should be updated
2589
+ */
2590
+ updateTbState( newState, updateMatrices ) {
2591
+
2592
+ this._state = newState;
2593
+ if ( updateMatrices ) {
2594
+
2595
+ this.updateMatrixState();
2596
+
2597
+ }
2598
+
2599
+ }
2600
+
2601
+ update() {
2602
+
2603
+ const EPS = 0.000001;
2604
+
2605
+ if ( this.target.equals( this._currentTarget ) === false ) {
2606
+
2607
+ this._gizmos.position.copy( this.target ); //for correct radius calculation
2608
+ this._tbRadius = this.calculateTbRadius( this.object );
2609
+ this.makeGizmos( this.target, this._tbRadius );
2610
+ this._currentTarget.copy( this.target );
2611
+
2612
+ }
2613
+
2614
+ //check min/max parameters
2615
+ if ( this.object.isOrthographicCamera ) {
2616
+
2617
+ //check zoom
2618
+ if ( this.object.zoom > this.maxZoom || this.object.zoom < this.minZoom ) {
2619
+
2620
+ const newZoom = MathUtils.clamp( this.object.zoom, this.minZoom, this.maxZoom );
2621
+ this.applyTransformMatrix( this.scale( newZoom / this.object.zoom, this._gizmos.position, true ) );
2622
+
2623
+ }
2624
+
2625
+ } else if ( this.object.isPerspectiveCamera ) {
2626
+
2627
+ //check distance
2628
+ const distance = this.object.position.distanceTo( this._gizmos.position );
2629
+
2630
+ if ( distance > this.maxDistance + EPS || distance < this.minDistance - EPS ) {
2631
+
2632
+ const newDistance = MathUtils.clamp( distance, this.minDistance, this.maxDistance );
2633
+ this.applyTransformMatrix( this.scale( newDistance / distance, this._gizmos.position ) );
2634
+ this.updateMatrixState();
2635
+
2636
+ }
2637
+
2638
+ //check fov
2639
+ if ( this.object.fov < this.minFov || this.object.fov > this.maxFov ) {
2640
+
2641
+ this.object.fov = MathUtils.clamp( this.object.fov, this.minFov, this.maxFov );
2642
+ this.object.updateProjectionMatrix();
2643
+
2644
+ }
2645
+
2646
+ const oldRadius = this._tbRadius;
2647
+ this._tbRadius = this.calculateTbRadius( this.object );
2648
+
2649
+ if ( oldRadius < this._tbRadius - EPS || oldRadius > this._tbRadius + EPS ) {
2650
+
2651
+ const scale = ( this._gizmos.scale.x + this._gizmos.scale.y + this._gizmos.scale.z ) / 3;
2652
+ const newRadius = this._tbRadius / scale;
2653
+ const curve = new EllipseCurve( 0, 0, newRadius, newRadius );
2654
+ const points = curve.getPoints( this._curvePts );
2655
+ const curveGeometry = new BufferGeometry().setFromPoints( points );
2656
+
2657
+ for ( const gizmo in this._gizmos.children ) {
2658
+
2659
+ this._gizmos.children[ gizmo ].geometry = curveGeometry;
2660
+
2661
+ }
2662
+
2663
+ }
2664
+
2665
+ }
2666
+
2667
+ this.object.lookAt( this._gizmos.position );
2668
+
2669
+ }
2670
+
2671
+ setStateFromJSON( json ) {
2672
+
2673
+ const state = JSON.parse( json );
2674
+
2675
+ if ( state.arcballState != undefined ) {
2676
+
2677
+ this._cameraMatrixState.fromArray( state.arcballState.cameraMatrix.elements );
2678
+ this._cameraMatrixState.decompose( this.object.position, this.object.quaternion, this.object.scale );
2679
+
2680
+ this.object.up.copy( state.arcballState.cameraUp );
2681
+ this.object.near = state.arcballState.cameraNear;
2682
+ this.object.far = state.arcballState.cameraFar;
2683
+
2684
+ this.object.zoom = state.arcballState.cameraZoom;
2685
+
2686
+ if ( this.object.isPerspectiveCamera ) {
2687
+
2688
+ this.object.fov = state.arcballState.cameraFov;
2689
+
2690
+ }
2691
+
2692
+ this._gizmoMatrixState.fromArray( state.arcballState.gizmoMatrix.elements );
2693
+ this._gizmoMatrixState.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
2694
+
2695
+ this.object.updateMatrix();
2696
+ this.object.updateProjectionMatrix();
2697
+
2698
+ this._gizmos.updateMatrix();
2699
+
2700
+ this._tbRadius = this.calculateTbRadius( this.object );
2701
+ const gizmoTmp = new Matrix4().copy( this._gizmoMatrixState0 );
2702
+ this.makeGizmos( this._gizmos.position, this._tbRadius );
2703
+ this._gizmoMatrixState0.copy( gizmoTmp );
2704
+
2705
+ this.object.lookAt( this._gizmos.position );
2706
+ this.updateTbState( STATE.IDLE, false );
2707
+
2708
+ this.dispatchEvent( _changeEvent );
2709
+
2710
+ }
2711
+
2712
+ }
2713
+
2714
+ }
2715
+
2716
+ //listeners
2717
+
2718
+ function onWindowResize() {
2719
+
2720
+ const scale = ( this._gizmos.scale.x + this._gizmos.scale.y + this._gizmos.scale.z ) / 3;
2721
+ this._tbRadius = this.calculateTbRadius( this.object );
2722
+
2723
+ const newRadius = this._tbRadius / scale;
2724
+ const curve = new EllipseCurve( 0, 0, newRadius, newRadius );
2725
+ const points = curve.getPoints( this._curvePts );
2726
+ const curveGeometry = new BufferGeometry().setFromPoints( points );
2727
+
2728
+
2729
+ for ( const gizmo in this._gizmos.children ) {
2730
+
2731
+ this._gizmos.children[ gizmo ].geometry = curveGeometry;
2732
+
2733
+ }
2734
+
2735
+ this.dispatchEvent( _changeEvent );
2736
+
2737
+ }
2738
+
2739
+ function onContextMenu( event ) {
2740
+
2741
+ if ( ! this.enabled ) {
2742
+
2743
+ return;
2744
+
2745
+ }
2746
+
2747
+ for ( let i = 0; i < this.mouseActions.length; i ++ ) {
2748
+
2749
+ if ( this.mouseActions[ i ].mouse == 2 ) {
2750
+
2751
+ //prevent only if button 2 is actually used
2752
+ event.preventDefault();
2753
+ break;
2754
+
2755
+ }
2756
+
2757
+ }
2758
+
2759
+ }
2760
+
2761
+ function onPointerCancel() {
2762
+
2763
+ this._touchStart.splice( 0, this._touchStart.length );
2764
+ this._touchCurrent.splice( 0, this._touchCurrent.length );
2765
+ this._input = INPUT.NONE;
2766
+
2767
+ }
2768
+
2769
+ function onPointerDown( event ) {
2770
+
2771
+ if ( event.button == 0 && event.isPrimary ) {
2772
+
2773
+ this._downValid = true;
2774
+ this._downEvents.push( event );
2775
+ this._downStart = performance.now();
2776
+
2777
+ } else {
2778
+
2779
+ this._downValid = false;
2780
+
2781
+ }
2782
+
2783
+ if ( event.pointerType == 'touch' && this._input != INPUT.CURSOR ) {
2784
+
2785
+ this._touchStart.push( event );
2786
+ this._touchCurrent.push( event );
2787
+
2788
+ switch ( this._input ) {
2789
+
2790
+ case INPUT.NONE:
2791
+
2792
+ //singleStart
2793
+ this._input = INPUT.ONE_FINGER;
2794
+ this.onSinglePanStart( event, 'ROTATE' );
2795
+
2796
+ window.addEventListener( 'pointermove', this._onPointerMove );
2797
+ window.addEventListener( 'pointerup', this._onPointerUp );
2798
+
2799
+ break;
2800
+
2801
+ case INPUT.ONE_FINGER:
2802
+ case INPUT.ONE_FINGER_SWITCHED:
2803
+
2804
+ //doubleStart
2805
+ this._input = INPUT.TWO_FINGER;
2806
+
2807
+ this.onRotateStart();
2808
+ this.onPinchStart();
2809
+ this.onDoublePanStart();
2810
+
2811
+ break;
2812
+
2813
+ case INPUT.TWO_FINGER:
2814
+
2815
+ //multipleStart
2816
+ this._input = INPUT.MULT_FINGER;
2817
+ this.onTriplePanStart( event );
2818
+ break;
2819
+
2820
+ }
2821
+
2822
+ } else if ( event.pointerType != 'touch' && this._input == INPUT.NONE ) {
2823
+
2824
+ let modifier = null;
2825
+
2826
+ if ( event.ctrlKey || event.metaKey ) {
2827
+
2828
+ modifier = 'CTRL';
2829
+
2830
+ } else if ( event.shiftKey ) {
2831
+
2832
+ modifier = 'SHIFT';
2833
+
2834
+ }
2835
+
2836
+ this._mouseOp = this.getOpFromAction( event.button, modifier );
2837
+ if ( this._mouseOp != null ) {
2838
+
2839
+ window.addEventListener( 'pointermove', this._onPointerMove );
2840
+ window.addEventListener( 'pointerup', this._onPointerUp );
2841
+
2842
+ //singleStart
2843
+ this._input = INPUT.CURSOR;
2844
+ this._button = event.button;
2845
+ this.onSinglePanStart( event, this._mouseOp );
2846
+
2847
+ }
2848
+
2849
+ }
2850
+
2851
+ }
2852
+
2853
+ function onPointerMove( event ) {
2854
+
2855
+ if ( event.pointerType == 'touch' && this._input != INPUT.CURSOR ) {
2856
+
2857
+ switch ( this._input ) {
2858
+
2859
+ case INPUT.ONE_FINGER:
2860
+
2861
+ //singleMove
2862
+ this.updateTouchEvent( event );
2863
+
2864
+ this.onSinglePanMove( event, STATE.ROTATE );
2865
+ break;
2866
+
2867
+ case INPUT.ONE_FINGER_SWITCHED:
2868
+
2869
+ const movement = this.calculatePointersDistance( this._touchCurrent[ 0 ], event ) * this._devPxRatio;
2870
+
2871
+ if ( movement >= this._switchSensibility ) {
2872
+
2873
+ //singleMove
2874
+ this._input = INPUT.ONE_FINGER;
2875
+ this.updateTouchEvent( event );
2876
+
2877
+ this.onSinglePanStart( event, 'ROTATE' );
2878
+ break;
2879
+
2880
+ }
2881
+
2882
+ break;
2883
+
2884
+ case INPUT.TWO_FINGER:
2885
+
2886
+ //rotate/pan/pinchMove
2887
+ this.updateTouchEvent( event );
2888
+
2889
+ this.onRotateMove();
2890
+ this.onPinchMove();
2891
+ this.onDoublePanMove();
2892
+
2893
+ break;
2894
+
2895
+ case INPUT.MULT_FINGER:
2896
+
2897
+ //multMove
2898
+ this.updateTouchEvent( event );
2899
+
2900
+ this.onTriplePanMove( event );
2901
+ break;
2902
+
2903
+ }
2904
+
2905
+ } else if ( event.pointerType != 'touch' && this._input == INPUT.CURSOR ) {
2906
+
2907
+ let modifier = null;
2908
+
2909
+ if ( event.ctrlKey || event.metaKey ) {
2910
+
2911
+ modifier = 'CTRL';
2912
+
2913
+ } else if ( event.shiftKey ) {
2914
+
2915
+ modifier = 'SHIFT';
2916
+
2917
+ }
2918
+
2919
+ const mouseOpState = this.getOpStateFromAction( this._button, modifier );
2920
+
2921
+ if ( mouseOpState != null ) {
2922
+
2923
+ this.onSinglePanMove( event, mouseOpState );
2924
+
2925
+ }
2926
+
2927
+ }
2928
+
2929
+ //checkDistance
2930
+ if ( this._downValid ) {
2931
+
2932
+ const movement = this.calculatePointersDistance( this._downEvents[ this._downEvents.length - 1 ], event ) * this._devPxRatio;
2933
+ if ( movement > this._movementThreshold ) {
2934
+
2935
+ this._downValid = false;
2936
+
2937
+ }
2938
+
2939
+ }
2940
+
2941
+ }
2942
+
2943
+ function onPointerUp( event ) {
2944
+
2945
+ if ( event.pointerType == 'touch' && this._input != INPUT.CURSOR ) {
2946
+
2947
+ const nTouch = this._touchCurrent.length;
2948
+
2949
+ for ( let i = 0; i < nTouch; i ++ ) {
2950
+
2951
+ if ( this._touchCurrent[ i ].pointerId == event.pointerId ) {
2952
+
2953
+ this._touchCurrent.splice( i, 1 );
2954
+ this._touchStart.splice( i, 1 );
2955
+ break;
2956
+
2957
+ }
2958
+
2959
+ }
2960
+
2961
+ switch ( this._input ) {
2962
+
2963
+ case INPUT.ONE_FINGER:
2964
+ case INPUT.ONE_FINGER_SWITCHED:
2965
+
2966
+ //singleEnd
2967
+ window.removeEventListener( 'pointermove', this._onPointerMove );
2968
+ window.removeEventListener( 'pointerup', this._onPointerUp );
2969
+
2970
+ this._input = INPUT.NONE;
2971
+ this.onSinglePanEnd();
2972
+
2973
+ break;
2974
+
2975
+ case INPUT.TWO_FINGER:
2976
+
2977
+ //doubleEnd
2978
+ this.onDoublePanEnd( event );
2979
+ this.onPinchEnd( event );
2980
+ this.onRotateEnd( event );
2981
+
2982
+ //switching to singleStart
2983
+ this._input = INPUT.ONE_FINGER_SWITCHED;
2984
+
2985
+ break;
2986
+
2987
+ case INPUT.MULT_FINGER:
2988
+
2989
+ if ( this._touchCurrent.length == 0 ) {
2990
+
2991
+ window.removeEventListener( 'pointermove', this._onPointerMove );
2992
+ window.removeEventListener( 'pointerup', this._onPointerUp );
2993
+
2994
+ //multCancel
2995
+ this._input = INPUT.NONE;
2996
+ this.onTriplePanEnd();
2997
+
2998
+ }
2999
+
3000
+ break;
3001
+
3002
+ }
3003
+
3004
+ } else if ( event.pointerType != 'touch' && this._input == INPUT.CURSOR ) {
3005
+
3006
+ window.removeEventListener( 'pointermove', this._onPointerMove );
3007
+ window.removeEventListener( 'pointerup', this._onPointerUp );
3008
+
3009
+ this._input = INPUT.NONE;
3010
+ this.onSinglePanEnd();
3011
+ this._button = - 1;
3012
+
3013
+ }
3014
+
3015
+ if ( event.isPrimary ) {
3016
+
3017
+ if ( this._downValid ) {
3018
+
3019
+ const downTime = event.timeStamp - this._downEvents[ this._downEvents.length - 1 ].timeStamp;
3020
+
3021
+ if ( downTime <= this._maxDownTime ) {
3022
+
3023
+ if ( this._nclicks == 0 ) {
3024
+
3025
+ //first valid click detected
3026
+ this._nclicks = 1;
3027
+ this._clickStart = performance.now();
3028
+
3029
+ } else {
3030
+
3031
+ const clickInterval = event.timeStamp - this._clickStart;
3032
+ const movement = this.calculatePointersDistance( this._downEvents[ 1 ], this._downEvents[ 0 ] ) * this._devPxRatio;
3033
+
3034
+ if ( clickInterval <= this._maxInterval && movement <= this._posThreshold ) {
3035
+
3036
+ //second valid click detected
3037
+ //fire double tap and reset values
3038
+ this._nclicks = 0;
3039
+ this._downEvents.splice( 0, this._downEvents.length );
3040
+ this.onDoubleTap( event );
3041
+
3042
+ } else {
3043
+
3044
+ //new 'first click'
3045
+ this._nclicks = 1;
3046
+ this._downEvents.shift();
3047
+ this._clickStart = performance.now();
3048
+
3049
+ }
3050
+
3051
+ }
3052
+
3053
+ } else {
3054
+
3055
+ this._downValid = false;
3056
+ this._nclicks = 0;
3057
+ this._downEvents.splice( 0, this._downEvents.length );
3058
+
3059
+ }
3060
+
3061
+ } else {
3062
+
3063
+ this._nclicks = 0;
3064
+ this._downEvents.splice( 0, this._downEvents.length );
3065
+
3066
+ }
3067
+
3068
+ }
3069
+
3070
+ }
3071
+
3072
+ function onWheel( event ) {
3073
+
3074
+ if ( this.enabled && this.enableZoom ) {
3075
+
3076
+ let modifier = null;
3077
+
3078
+ if ( event.ctrlKey || event.metaKey ) {
3079
+
3080
+ modifier = 'CTRL';
3081
+
3082
+ } else if ( event.shiftKey ) {
3083
+
3084
+ modifier = 'SHIFT';
3085
+
3086
+ }
3087
+
3088
+ const mouseOp = this.getOpFromAction( 'WHEEL', modifier );
3089
+
3090
+ if ( mouseOp != null ) {
3091
+
3092
+ event.preventDefault();
3093
+ this.dispatchEvent( _startEvent );
3094
+
3095
+ const notchDeltaY = 125; //distance of one notch of mouse wheel
3096
+ let sgn = event.deltaY / notchDeltaY;
3097
+
3098
+ let size = 1;
3099
+
3100
+ if ( sgn > 0 ) {
3101
+
3102
+ size = 1 / this.scaleFactor;
3103
+
3104
+ } else if ( sgn < 0 ) {
3105
+
3106
+ size = this.scaleFactor;
3107
+
3108
+ }
3109
+
3110
+ switch ( mouseOp ) {
3111
+
3112
+ case 'ZOOM':
3113
+
3114
+ this.updateTbState( STATE.SCALE, true );
3115
+
3116
+ if ( sgn > 0 ) {
3117
+
3118
+ size = 1 / ( Math.pow( this.scaleFactor, sgn ) );
3119
+
3120
+ } else if ( sgn < 0 ) {
3121
+
3122
+ size = Math.pow( this.scaleFactor, - sgn );
3123
+
3124
+ }
3125
+
3126
+ if ( this.cursorZoom && this.enablePan ) {
3127
+
3128
+ let scalePoint;
3129
+
3130
+ if ( this.object.isOrthographicCamera ) {
3131
+
3132
+ scalePoint = this.unprojectOnTbPlane( this.object, event.clientX, event.clientY, this.domElement ).applyQuaternion( this.object.quaternion ).multiplyScalar( 1 / this.object.zoom ).add( this._gizmos.position );
3133
+
3134
+ } else if ( this.object.isPerspectiveCamera ) {
3135
+
3136
+ scalePoint = this.unprojectOnTbPlane( this.object, event.clientX, event.clientY, this.domElement ).applyQuaternion( this.object.quaternion ).add( this._gizmos.position );
3137
+
3138
+ }
3139
+
3140
+ this.applyTransformMatrix( this.scale( size, scalePoint ) );
3141
+
3142
+ } else {
3143
+
3144
+ this.applyTransformMatrix( this.scale( size, this._gizmos.position ) );
3145
+
3146
+ }
3147
+
3148
+ if ( this._grid != null ) {
3149
+
3150
+ this.disposeGrid();
3151
+ this.drawGrid();
3152
+
3153
+ }
3154
+
3155
+ this.updateTbState( STATE.IDLE, false );
3156
+
3157
+ this.dispatchEvent( _changeEvent );
3158
+ this.dispatchEvent( _endEvent );
3159
+
3160
+ break;
3161
+
3162
+ case 'FOV':
3163
+
3164
+ if ( this.object.isPerspectiveCamera ) {
3165
+
3166
+ this.updateTbState( STATE.FOV, true );
3167
+
3168
+
3169
+ //Vertigo effect
3170
+
3171
+ // fov / 2
3172
+ // |\
3173
+ // | \
3174
+ // | \
3175
+ // x | \
3176
+ // | \
3177
+ // | \
3178
+ // | _ _ _\
3179
+ // y
3180
+
3181
+ //check for iOs shift shortcut
3182
+ if ( event.deltaX != 0 ) {
3183
+
3184
+ sgn = event.deltaX / notchDeltaY;
3185
+
3186
+ size = 1;
3187
+
3188
+ if ( sgn > 0 ) {
3189
+
3190
+ size = 1 / ( Math.pow( this.scaleFactor, sgn ) );
3191
+
3192
+ } else if ( sgn < 0 ) {
3193
+
3194
+ size = Math.pow( this.scaleFactor, - sgn );
3195
+
3196
+ }
3197
+
3198
+ }
3199
+
3200
+ this._v3_1.setFromMatrixPosition( this._cameraMatrixState );
3201
+ const x = this._v3_1.distanceTo( this._gizmos.position );
3202
+ let xNew = x / size; //distance between camera and gizmos if scale(size, scalepoint) would be performed
3203
+
3204
+ //check min and max distance
3205
+ xNew = MathUtils.clamp( xNew, this.minDistance, this.maxDistance );
3206
+
3207
+ const y = x * Math.tan( MathUtils.DEG2RAD * this.object.fov * 0.5 );
3208
+
3209
+ //calculate new fov
3210
+ let newFov = MathUtils.RAD2DEG * ( Math.atan( y / xNew ) * 2 );
3211
+
3212
+ //check min and max fov
3213
+ if ( newFov > this.maxFov ) {
3214
+
3215
+ newFov = this.maxFov;
3216
+
3217
+ } else if ( newFov < this.minFov ) {
3218
+
3219
+ newFov = this.minFov;
3220
+
3221
+ }
3222
+
3223
+ const newDistance = y / Math.tan( MathUtils.DEG2RAD * ( newFov / 2 ) );
3224
+ size = x / newDistance;
3225
+
3226
+ this.setFov( newFov );
3227
+ this.applyTransformMatrix( this.scale( size, this._gizmos.position, false ) );
3228
+
3229
+ }
3230
+
3231
+ if ( this._grid != null ) {
3232
+
3233
+ this.disposeGrid();
3234
+ this.drawGrid();
3235
+
3236
+ }
3237
+
3238
+ this.updateTbState( STATE.IDLE, false );
3239
+
3240
+ this.dispatchEvent( _changeEvent );
3241
+ this.dispatchEvent( _endEvent );
3242
+
3243
+ break;
3244
+
3245
+ }
3246
+
3247
+ }
3248
+
3249
+ }
3250
+
3251
+ }
3252
+
3253
+ export { ArcballControls };
libs/three.js/0.172.0/jsm/controls/DragControls.js ADDED
@@ -0,0 +1,410 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Controls,
3
+ Matrix4,
4
+ Plane,
5
+ Raycaster,
6
+ Vector2,
7
+ Vector3,
8
+ MOUSE,
9
+ TOUCH
10
+ } from 'three';
11
+
12
+ const _plane = new Plane();
13
+
14
+ const _pointer = new Vector2();
15
+ const _offset = new Vector3();
16
+ const _diff = new Vector2();
17
+ const _previousPointer = new Vector2();
18
+ const _intersection = new Vector3();
19
+ const _worldPosition = new Vector3();
20
+ const _inverseMatrix = new Matrix4();
21
+
22
+ const _up = new Vector3();
23
+ const _right = new Vector3();
24
+
25
+ let _selected = null, _hovered = null;
26
+ const _intersections = [];
27
+
28
+ const STATE = {
29
+ NONE: - 1,
30
+ PAN: 0,
31
+ ROTATE: 1
32
+ };
33
+
34
+ class DragControls extends Controls {
35
+
36
+ constructor( objects, camera, domElement = null ) {
37
+
38
+ super( camera, domElement );
39
+
40
+ this.objects = objects;
41
+
42
+ this.recursive = true;
43
+ this.transformGroup = false;
44
+ this.rotateSpeed = 1;
45
+
46
+ this.raycaster = new Raycaster();
47
+
48
+ // interaction
49
+
50
+ this.mouseButtons = { LEFT: MOUSE.PAN, MIDDLE: MOUSE.PAN, RIGHT: MOUSE.ROTATE };
51
+ this.touches = { ONE: TOUCH.PAN };
52
+
53
+ // event listeners
54
+
55
+ this._onPointerMove = onPointerMove.bind( this );
56
+ this._onPointerDown = onPointerDown.bind( this );
57
+ this._onPointerCancel = onPointerCancel.bind( this );
58
+ this._onContextMenu = onContextMenu.bind( this );
59
+
60
+ //
61
+
62
+ if ( domElement !== null ) {
63
+
64
+ this.connect();
65
+
66
+ }
67
+
68
+ }
69
+
70
+ connect() {
71
+
72
+ this.domElement.addEventListener( 'pointermove', this._onPointerMove );
73
+ this.domElement.addEventListener( 'pointerdown', this._onPointerDown );
74
+ this.domElement.addEventListener( 'pointerup', this._onPointerCancel );
75
+ this.domElement.addEventListener( 'pointerleave', this._onPointerCancel );
76
+ this.domElement.addEventListener( 'contextmenu', this._onContextMenu );
77
+
78
+ this.domElement.style.touchAction = 'none'; // disable touch scroll
79
+
80
+ }
81
+
82
+ disconnect() {
83
+
84
+ this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
85
+ this.domElement.removeEventListener( 'pointerdown', this._onPointerDown );
86
+ this.domElement.removeEventListener( 'pointerup', this._onPointerCancel );
87
+ this.domElement.removeEventListener( 'pointerleave', this._onPointerCancel );
88
+ this.domElement.removeEventListener( 'contextmenu', this._onContextMenu );
89
+
90
+ this.domElement.style.touchAction = 'auto';
91
+ this.domElement.style.cursor = '';
92
+
93
+ }
94
+
95
+ dispose() {
96
+
97
+ this.disconnect();
98
+
99
+ }
100
+
101
+ _updatePointer( event ) {
102
+
103
+ const rect = this.domElement.getBoundingClientRect();
104
+
105
+ _pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
106
+ _pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
107
+
108
+ }
109
+
110
+ _updateState( event ) {
111
+
112
+ // determine action
113
+
114
+ let action;
115
+
116
+ if ( event.pointerType === 'touch' ) {
117
+
118
+ action = this.touches.ONE;
119
+
120
+ } else {
121
+
122
+ switch ( event.button ) {
123
+
124
+ case 0:
125
+
126
+ action = this.mouseButtons.LEFT;
127
+ break;
128
+
129
+ case 1:
130
+
131
+ action = this.mouseButtons.MIDDLE;
132
+ break;
133
+
134
+ case 2:
135
+
136
+ action = this.mouseButtons.RIGHT;
137
+ break;
138
+
139
+ default:
140
+
141
+ action = null;
142
+
143
+ }
144
+
145
+ }
146
+
147
+ // determine state
148
+
149
+ switch ( action ) {
150
+
151
+ case MOUSE.PAN:
152
+ case TOUCH.PAN:
153
+
154
+ this.state = STATE.PAN;
155
+
156
+ break;
157
+
158
+ case MOUSE.ROTATE:
159
+ case TOUCH.ROTATE:
160
+
161
+ this.state = STATE.ROTATE;
162
+
163
+ break;
164
+
165
+ default:
166
+
167
+ this.state = STATE.NONE;
168
+
169
+ }
170
+
171
+ }
172
+
173
+ getRaycaster() {
174
+
175
+ console.warn( 'THREE.DragControls: getRaycaster() has been deprecated. Use controls.raycaster instead.' ); // @deprecated r169
176
+
177
+ return this.raycaster;
178
+
179
+ }
180
+
181
+ setObjects( objects ) {
182
+
183
+ console.warn( 'THREE.DragControls: setObjects() has been deprecated. Use controls.objects instead.' ); // @deprecated r169
184
+
185
+ this.objects = objects;
186
+
187
+ }
188
+
189
+ getObjects() {
190
+
191
+ console.warn( 'THREE.DragControls: getObjects() has been deprecated. Use controls.objects instead.' ); // @deprecated r169
192
+
193
+ return this.objects;
194
+
195
+ }
196
+
197
+ activate() {
198
+
199
+ console.warn( 'THREE.DragControls: activate() has been renamed to connect().' ); // @deprecated r169
200
+ this.connect();
201
+
202
+ }
203
+
204
+ deactivate() {
205
+
206
+ console.warn( 'THREE.DragControls: deactivate() has been renamed to disconnect().' ); // @deprecated r169
207
+ this.disconnect();
208
+
209
+ }
210
+
211
+ set mode( value ) {
212
+
213
+ console.warn( 'THREE.DragControls: The .mode property has been removed. Define the type of transformation via the .mouseButtons or .touches properties.' ); // @deprecated r169
214
+
215
+ }
216
+
217
+ get mode() {
218
+
219
+ console.warn( 'THREE.DragControls: The .mode property has been removed. Define the type of transformation via the .mouseButtons or .touches properties.' ); // @deprecated r169
220
+
221
+ }
222
+
223
+ }
224
+
225
+ function onPointerMove( event ) {
226
+
227
+ const camera = this.object;
228
+ const domElement = this.domElement;
229
+ const raycaster = this.raycaster;
230
+
231
+ if ( this.enabled === false ) return;
232
+
233
+ this._updatePointer( event );
234
+
235
+ raycaster.setFromCamera( _pointer, camera );
236
+
237
+ if ( _selected ) {
238
+
239
+ if ( this.state === STATE.PAN ) {
240
+
241
+ if ( raycaster.ray.intersectPlane( _plane, _intersection ) ) {
242
+
243
+ _selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
244
+
245
+ }
246
+
247
+ } else if ( this.state === STATE.ROTATE ) {
248
+
249
+ _diff.subVectors( _pointer, _previousPointer ).multiplyScalar( this.rotateSpeed );
250
+ _selected.rotateOnWorldAxis( _up, _diff.x );
251
+ _selected.rotateOnWorldAxis( _right.normalize(), - _diff.y );
252
+
253
+ }
254
+
255
+ this.dispatchEvent( { type: 'drag', object: _selected } );
256
+
257
+ _previousPointer.copy( _pointer );
258
+
259
+ } else {
260
+
261
+ // hover support
262
+
263
+ if ( event.pointerType === 'mouse' || event.pointerType === 'pen' ) {
264
+
265
+ _intersections.length = 0;
266
+
267
+ raycaster.setFromCamera( _pointer, camera );
268
+ raycaster.intersectObjects( this.objects, this.recursive, _intersections );
269
+
270
+ if ( _intersections.length > 0 ) {
271
+
272
+ const object = _intersections[ 0 ].object;
273
+
274
+ _plane.setFromNormalAndCoplanarPoint( camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
275
+
276
+ if ( _hovered !== object && _hovered !== null ) {
277
+
278
+ this.dispatchEvent( { type: 'hoveroff', object: _hovered } );
279
+
280
+ domElement.style.cursor = 'auto';
281
+ _hovered = null;
282
+
283
+ }
284
+
285
+ if ( _hovered !== object ) {
286
+
287
+ this.dispatchEvent( { type: 'hoveron', object: object } );
288
+
289
+ domElement.style.cursor = 'pointer';
290
+ _hovered = object;
291
+
292
+ }
293
+
294
+ } else {
295
+
296
+ if ( _hovered !== null ) {
297
+
298
+ this.dispatchEvent( { type: 'hoveroff', object: _hovered } );
299
+
300
+ domElement.style.cursor = 'auto';
301
+ _hovered = null;
302
+
303
+ }
304
+
305
+ }
306
+
307
+ }
308
+
309
+ }
310
+
311
+ _previousPointer.copy( _pointer );
312
+
313
+ }
314
+
315
+ function onPointerDown( event ) {
316
+
317
+ const camera = this.object;
318
+ const domElement = this.domElement;
319
+ const raycaster = this.raycaster;
320
+
321
+ if ( this.enabled === false ) return;
322
+
323
+ this._updatePointer( event );
324
+ this._updateState( event );
325
+
326
+ _intersections.length = 0;
327
+
328
+ raycaster.setFromCamera( _pointer, camera );
329
+ raycaster.intersectObjects( this.objects, this.recursive, _intersections );
330
+
331
+ if ( _intersections.length > 0 ) {
332
+
333
+ if ( this.transformGroup === true ) {
334
+
335
+ // look for the outermost group in the object's upper hierarchy
336
+
337
+ _selected = findGroup( _intersections[ 0 ].object );
338
+
339
+ } else {
340
+
341
+ _selected = _intersections[ 0 ].object;
342
+
343
+ }
344
+
345
+ _plane.setFromNormalAndCoplanarPoint( camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
346
+
347
+ if ( raycaster.ray.intersectPlane( _plane, _intersection ) ) {
348
+
349
+ if ( this.state === STATE.PAN ) {
350
+
351
+ _inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
352
+ _offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
353
+
354
+ } else if ( this.state === STATE.ROTATE ) {
355
+
356
+ // the controls only support Y+ up
357
+ _up.set( 0, 1, 0 ).applyQuaternion( camera.quaternion ).normalize();
358
+ _right.set( 1, 0, 0 ).applyQuaternion( camera.quaternion ).normalize();
359
+
360
+ }
361
+
362
+ }
363
+
364
+ domElement.style.cursor = 'move';
365
+
366
+ this.dispatchEvent( { type: 'dragstart', object: _selected } );
367
+
368
+ }
369
+
370
+ _previousPointer.copy( _pointer );
371
+
372
+ }
373
+
374
+ function onPointerCancel() {
375
+
376
+ if ( this.enabled === false ) return;
377
+
378
+ if ( _selected ) {
379
+
380
+ this.dispatchEvent( { type: 'dragend', object: _selected } );
381
+
382
+ _selected = null;
383
+
384
+ }
385
+
386
+ this.domElement.style.cursor = _hovered ? 'pointer' : 'auto';
387
+
388
+ this.state = STATE.NONE;
389
+
390
+ }
391
+
392
+ function onContextMenu( event ) {
393
+
394
+ if ( this.enabled === false ) return;
395
+
396
+ event.preventDefault();
397
+
398
+ }
399
+
400
+ function findGroup( obj, group = null ) {
401
+
402
+ if ( obj.isGroup ) group = obj;
403
+
404
+ if ( obj.parent === null ) return group;
405
+
406
+ return findGroup( obj.parent, group );
407
+
408
+ }
409
+
410
+ export { DragControls };
libs/three.js/0.172.0/jsm/controls/FirstPersonControls.js ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Controls,
3
+ MathUtils,
4
+ Spherical,
5
+ Vector3
6
+ } from 'three';
7
+
8
+ const _lookDirection = new Vector3();
9
+ const _spherical = new Spherical();
10
+ const _target = new Vector3();
11
+ const _targetPosition = new Vector3();
12
+
13
+ class FirstPersonControls extends Controls {
14
+
15
+ constructor( object, domElement = null ) {
16
+
17
+ super( object, domElement );
18
+
19
+ // API
20
+
21
+ this.movementSpeed = 1.0;
22
+ this.lookSpeed = 0.005;
23
+
24
+ this.lookVertical = true;
25
+ this.autoForward = false;
26
+
27
+ this.activeLook = true;
28
+
29
+ this.heightSpeed = false;
30
+ this.heightCoef = 1.0;
31
+ this.heightMin = 0.0;
32
+ this.heightMax = 1.0;
33
+
34
+ this.constrainVertical = false;
35
+ this.verticalMin = 0;
36
+ this.verticalMax = Math.PI;
37
+
38
+ this.mouseDragOn = false;
39
+
40
+ // internals
41
+
42
+ this._autoSpeedFactor = 0.0;
43
+
44
+ this._pointerX = 0;
45
+ this._pointerY = 0;
46
+
47
+ this._moveForward = false;
48
+ this._moveBackward = false;
49
+ this._moveLeft = false;
50
+ this._moveRight = false;
51
+
52
+ this._viewHalfX = 0;
53
+ this._viewHalfY = 0;
54
+
55
+ this._lat = 0;
56
+ this._lon = 0;
57
+
58
+ // event listeners
59
+
60
+ this._onPointerMove = onPointerMove.bind( this );
61
+ this._onPointerDown = onPointerDown.bind( this );
62
+ this._onPointerUp = onPointerUp.bind( this );
63
+ this._onContextMenu = onContextMenu.bind( this );
64
+ this._onKeyDown = onKeyDown.bind( this );
65
+ this._onKeyUp = onKeyUp.bind( this );
66
+
67
+ //
68
+
69
+ if ( domElement !== null ) {
70
+
71
+ this.connect();
72
+
73
+ this.handleResize();
74
+
75
+ }
76
+
77
+ this._setOrientation();
78
+
79
+ }
80
+
81
+ connect() {
82
+
83
+ window.addEventListener( 'keydown', this._onKeyDown );
84
+ window.addEventListener( 'keyup', this._onKeyUp );
85
+
86
+ this.domElement.addEventListener( 'pointermove', this._onPointerMove );
87
+ this.domElement.addEventListener( 'pointerdown', this._onPointerDown );
88
+ this.domElement.addEventListener( 'pointerup', this._onPointerUp );
89
+ this.domElement.addEventListener( 'contextmenu', this._onContextMenu );
90
+
91
+ }
92
+
93
+ disconnect() {
94
+
95
+ window.removeEventListener( 'keydown', this._onKeyDown );
96
+ window.removeEventListener( 'keyup', this._onKeyUp );
97
+
98
+ this.domElement.removeEventListener( 'pointerdown', this._onPointerMove );
99
+ this.domElement.removeEventListener( 'pointermove', this._onPointerDown );
100
+ this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
101
+ this.domElement.removeEventListener( 'contextmenu', this._onContextMenu );
102
+
103
+ }
104
+
105
+ dispose() {
106
+
107
+ this.disconnect();
108
+
109
+ }
110
+
111
+ handleResize() {
112
+
113
+ if ( this.domElement === document ) {
114
+
115
+ this._viewHalfX = window.innerWidth / 2;
116
+ this._viewHalfY = window.innerHeight / 2;
117
+
118
+ } else {
119
+
120
+ this._viewHalfX = this.domElement.offsetWidth / 2;
121
+ this._viewHalfY = this.domElement.offsetHeight / 2;
122
+
123
+ }
124
+
125
+ }
126
+
127
+ lookAt( x, y, z ) {
128
+
129
+ if ( x.isVector3 ) {
130
+
131
+ _target.copy( x );
132
+
133
+ } else {
134
+
135
+ _target.set( x, y, z );
136
+
137
+ }
138
+
139
+ this.object.lookAt( _target );
140
+
141
+ this._setOrientation();
142
+
143
+ return this;
144
+
145
+ }
146
+
147
+ update( delta ) {
148
+
149
+ if ( this.enabled === false ) return;
150
+
151
+ if ( this.heightSpeed ) {
152
+
153
+ const y = MathUtils.clamp( this.object.position.y, this.heightMin, this.heightMax );
154
+ const heightDelta = y - this.heightMin;
155
+
156
+ this._autoSpeedFactor = delta * ( heightDelta * this.heightCoef );
157
+
158
+ } else {
159
+
160
+ this._autoSpeedFactor = 0.0;
161
+
162
+ }
163
+
164
+ const actualMoveSpeed = delta * this.movementSpeed;
165
+
166
+ if ( this._moveForward || ( this.autoForward && ! this._moveBackward ) ) this.object.translateZ( - ( actualMoveSpeed + this._autoSpeedFactor ) );
167
+ if ( this._moveBackward ) this.object.translateZ( actualMoveSpeed );
168
+
169
+ if ( this._moveLeft ) this.object.translateX( - actualMoveSpeed );
170
+ if ( this._moveRight ) this.object.translateX( actualMoveSpeed );
171
+
172
+ if ( this._moveUp ) this.object.translateY( actualMoveSpeed );
173
+ if ( this._moveDown ) this.object.translateY( - actualMoveSpeed );
174
+
175
+ let actualLookSpeed = delta * this.lookSpeed;
176
+
177
+ if ( ! this.activeLook ) {
178
+
179
+ actualLookSpeed = 0;
180
+
181
+ }
182
+
183
+ let verticalLookRatio = 1;
184
+
185
+ if ( this.constrainVertical ) {
186
+
187
+ verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin );
188
+
189
+ }
190
+
191
+ this._lon -= this._pointerX * actualLookSpeed;
192
+ if ( this.lookVertical ) this._lat -= this._pointerY * actualLookSpeed * verticalLookRatio;
193
+
194
+ this._lat = Math.max( - 85, Math.min( 85, this._lat ) );
195
+
196
+ let phi = MathUtils.degToRad( 90 - this._lat );
197
+ const theta = MathUtils.degToRad( this._lon );
198
+
199
+ if ( this.constrainVertical ) {
200
+
201
+ phi = MathUtils.mapLinear( phi, 0, Math.PI, this.verticalMin, this.verticalMax );
202
+
203
+ }
204
+
205
+ const position = this.object.position;
206
+
207
+ _targetPosition.setFromSphericalCoords( 1, phi, theta ).add( position );
208
+
209
+ this.object.lookAt( _targetPosition );
210
+
211
+ }
212
+
213
+ _setOrientation() {
214
+
215
+ const quaternion = this.object.quaternion;
216
+
217
+ _lookDirection.set( 0, 0, - 1 ).applyQuaternion( quaternion );
218
+ _spherical.setFromVector3( _lookDirection );
219
+
220
+ this._lat = 90 - MathUtils.radToDeg( _spherical.phi );
221
+ this._lon = MathUtils.radToDeg( _spherical.theta );
222
+
223
+ }
224
+
225
+ }
226
+
227
+ function onPointerDown( event ) {
228
+
229
+ if ( this.domElement !== document ) {
230
+
231
+ this.domElement.focus();
232
+
233
+ }
234
+
235
+ if ( this.activeLook ) {
236
+
237
+ switch ( event.button ) {
238
+
239
+ case 0: this._moveForward = true; break;
240
+ case 2: this._moveBackward = true; break;
241
+
242
+ }
243
+
244
+ }
245
+
246
+ this.mouseDragOn = true;
247
+
248
+ }
249
+
250
+ function onPointerUp( event ) {
251
+
252
+ if ( this.activeLook ) {
253
+
254
+ switch ( event.button ) {
255
+
256
+ case 0: this._moveForward = false; break;
257
+ case 2: this._moveBackward = false; break;
258
+
259
+ }
260
+
261
+ }
262
+
263
+ this.mouseDragOn = false;
264
+
265
+ }
266
+
267
+ function onPointerMove( event ) {
268
+
269
+ if ( this.domElement === document ) {
270
+
271
+ this._pointerX = event.pageX - this._viewHalfX;
272
+ this._pointerY = event.pageY - this._viewHalfY;
273
+
274
+ } else {
275
+
276
+ this._pointerX = event.pageX - this.domElement.offsetLeft - this._viewHalfX;
277
+ this._pointerY = event.pageY - this.domElement.offsetTop - this._viewHalfY;
278
+
279
+ }
280
+
281
+ }
282
+
283
+ function onKeyDown( event ) {
284
+
285
+ switch ( event.code ) {
286
+
287
+ case 'ArrowUp':
288
+ case 'KeyW': this._moveForward = true; break;
289
+
290
+ case 'ArrowLeft':
291
+ case 'KeyA': this._moveLeft = true; break;
292
+
293
+ case 'ArrowDown':
294
+ case 'KeyS': this._moveBackward = true; break;
295
+
296
+ case 'ArrowRight':
297
+ case 'KeyD': this._moveRight = true; break;
298
+
299
+ case 'KeyR': this._moveUp = true; break;
300
+ case 'KeyF': this._moveDown = true; break;
301
+
302
+ }
303
+
304
+ }
305
+
306
+ function onKeyUp( event ) {
307
+
308
+ switch ( event.code ) {
309
+
310
+ case 'ArrowUp':
311
+ case 'KeyW': this._moveForward = false; break;
312
+
313
+ case 'ArrowLeft':
314
+ case 'KeyA': this._moveLeft = false; break;
315
+
316
+ case 'ArrowDown':
317
+ case 'KeyS': this._moveBackward = false; break;
318
+
319
+ case 'ArrowRight':
320
+ case 'KeyD': this._moveRight = false; break;
321
+
322
+ case 'KeyR': this._moveUp = false; break;
323
+ case 'KeyF': this._moveDown = false; break;
324
+
325
+ }
326
+
327
+ }
328
+
329
+ function onContextMenu( event ) {
330
+
331
+ if ( this.enabled === false ) return;
332
+
333
+ event.preventDefault();
334
+
335
+ }
336
+
337
+ export { FirstPersonControls };
libs/three.js/0.172.0/jsm/controls/FlyControls.js ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Controls,
3
+ Quaternion,
4
+ Vector3
5
+ } from 'three';
6
+
7
+ const _changeEvent = { type: 'change' };
8
+
9
+ const _EPS = 0.000001;
10
+ const _tmpQuaternion = new Quaternion();
11
+
12
+ class FlyControls extends Controls {
13
+
14
+ constructor( object, domElement = null ) {
15
+
16
+ super( object, domElement );
17
+
18
+ this.movementSpeed = 1.0;
19
+ this.rollSpeed = 0.005;
20
+
21
+ this.dragToLook = false;
22
+ this.autoForward = false;
23
+
24
+ // internals
25
+
26
+ this._moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 };
27
+ this._moveVector = new Vector3( 0, 0, 0 );
28
+ this._rotationVector = new Vector3( 0, 0, 0 );
29
+ this._lastQuaternion = new Quaternion();
30
+ this._lastPosition = new Vector3();
31
+ this._status = 0;
32
+
33
+ // event listeners
34
+
35
+ this._onKeyDown = onKeyDown.bind( this );
36
+ this._onKeyUp = onKeyUp.bind( this );
37
+ this._onPointerMove = onPointerMove.bind( this );
38
+ this._onPointerDown = onPointerDown.bind( this );
39
+ this._onPointerUp = onPointerUp.bind( this );
40
+ this._onPointerCancel = onPointerCancel.bind( this );
41
+ this._onContextMenu = onContextMenu.bind( this );
42
+
43
+ //
44
+
45
+ if ( domElement !== null ) {
46
+
47
+ this.connect();
48
+
49
+ }
50
+
51
+ }
52
+
53
+ connect() {
54
+
55
+ window.addEventListener( 'keydown', this._onKeyDown );
56
+ window.addEventListener( 'keyup', this._onKeyUp );
57
+
58
+ this.domElement.addEventListener( 'pointermove', this._onPointerMove );
59
+ this.domElement.addEventListener( 'pointerdown', this._onPointerDown );
60
+ this.domElement.addEventListener( 'pointerup', this._onPointerUp );
61
+ this.domElement.addEventListener( 'pointercancel', this._onPointerCancel );
62
+ this.domElement.addEventListener( 'contextmenu', this._onContextMenu );
63
+
64
+ }
65
+
66
+ disconnect() {
67
+
68
+ window.removeEventListener( 'keydown', this._onKeyDown );
69
+ window.removeEventListener( 'keyup', this._onKeyUp );
70
+
71
+ this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
72
+ this.domElement.removeEventListener( 'pointerdown', this._onPointerDown );
73
+ this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
74
+ this.domElement.removeEventListener( 'pointercancel', this._onPointerCancel );
75
+ this.domElement.removeEventListener( 'contextmenu', this._onContextMenu );
76
+
77
+ }
78
+
79
+ dispose() {
80
+
81
+ this.disconnect();
82
+
83
+ }
84
+
85
+ update( delta ) {
86
+
87
+ if ( this.enabled === false ) return;
88
+
89
+ const object = this.object;
90
+
91
+ const moveMult = delta * this.movementSpeed;
92
+ const rotMult = delta * this.rollSpeed;
93
+
94
+ object.translateX( this._moveVector.x * moveMult );
95
+ object.translateY( this._moveVector.y * moveMult );
96
+ object.translateZ( this._moveVector.z * moveMult );
97
+
98
+ _tmpQuaternion.set( this._rotationVector.x * rotMult, this._rotationVector.y * rotMult, this._rotationVector.z * rotMult, 1 ).normalize();
99
+ object.quaternion.multiply( _tmpQuaternion );
100
+
101
+ if (
102
+ this._lastPosition.distanceToSquared( object.position ) > _EPS ||
103
+ 8 * ( 1 - this._lastQuaternion.dot( object.quaternion ) ) > _EPS
104
+ ) {
105
+
106
+ this.dispatchEvent( _changeEvent );
107
+ this._lastQuaternion.copy( object.quaternion );
108
+ this._lastPosition.copy( object.position );
109
+
110
+ }
111
+
112
+ }
113
+
114
+ // private
115
+
116
+ _updateMovementVector() {
117
+
118
+ const forward = ( this._moveState.forward || ( this.autoForward && ! this._moveState.back ) ) ? 1 : 0;
119
+
120
+ this._moveVector.x = ( - this._moveState.left + this._moveState.right );
121
+ this._moveVector.y = ( - this._moveState.down + this._moveState.up );
122
+ this._moveVector.z = ( - forward + this._moveState.back );
123
+
124
+ //console.log( 'move:', [ this._moveVector.x, this._moveVector.y, this._moveVector.z ] );
125
+
126
+ }
127
+
128
+ _updateRotationVector() {
129
+
130
+ this._rotationVector.x = ( - this._moveState.pitchDown + this._moveState.pitchUp );
131
+ this._rotationVector.y = ( - this._moveState.yawRight + this._moveState.yawLeft );
132
+ this._rotationVector.z = ( - this._moveState.rollRight + this._moveState.rollLeft );
133
+
134
+ //console.log( 'rotate:', [ this._rotationVector.x, this._rotationVector.y, this._rotationVector.z ] );
135
+
136
+ }
137
+
138
+ _getContainerDimensions() {
139
+
140
+ if ( this.domElement != document ) {
141
+
142
+ return {
143
+ size: [ this.domElement.offsetWidth, this.domElement.offsetHeight ],
144
+ offset: [ this.domElement.offsetLeft, this.domElement.offsetTop ]
145
+ };
146
+
147
+ } else {
148
+
149
+ return {
150
+ size: [ window.innerWidth, window.innerHeight ],
151
+ offset: [ 0, 0 ]
152
+ };
153
+
154
+ }
155
+
156
+ }
157
+
158
+ }
159
+
160
+ function onKeyDown( event ) {
161
+
162
+ if ( event.altKey || this.enabled === false ) {
163
+
164
+ return;
165
+
166
+ }
167
+
168
+ switch ( event.code ) {
169
+
170
+ case 'ShiftLeft':
171
+ case 'ShiftRight': this.movementSpeedMultiplier = .1; break;
172
+
173
+ case 'KeyW': this._moveState.forward = 1; break;
174
+ case 'KeyS': this._moveState.back = 1; break;
175
+
176
+ case 'KeyA': this._moveState.left = 1; break;
177
+ case 'KeyD': this._moveState.right = 1; break;
178
+
179
+ case 'KeyR': this._moveState.up = 1; break;
180
+ case 'KeyF': this._moveState.down = 1; break;
181
+
182
+ case 'ArrowUp': this._moveState.pitchUp = 1; break;
183
+ case 'ArrowDown': this._moveState.pitchDown = 1; break;
184
+
185
+ case 'ArrowLeft': this._moveState.yawLeft = 1; break;
186
+ case 'ArrowRight': this._moveState.yawRight = 1; break;
187
+
188
+ case 'KeyQ': this._moveState.rollLeft = 1; break;
189
+ case 'KeyE': this._moveState.rollRight = 1; break;
190
+
191
+ }
192
+
193
+ this._updateMovementVector();
194
+ this._updateRotationVector();
195
+
196
+ }
197
+
198
+ function onKeyUp( event ) {
199
+
200
+ if ( this.enabled === false ) return;
201
+
202
+ switch ( event.code ) {
203
+
204
+ case 'ShiftLeft':
205
+ case 'ShiftRight': this.movementSpeedMultiplier = 1; break;
206
+
207
+ case 'KeyW': this._moveState.forward = 0; break;
208
+ case 'KeyS': this._moveState.back = 0; break;
209
+
210
+ case 'KeyA': this._moveState.left = 0; break;
211
+ case 'KeyD': this._moveState.right = 0; break;
212
+
213
+ case 'KeyR': this._moveState.up = 0; break;
214
+ case 'KeyF': this._moveState.down = 0; break;
215
+
216
+ case 'ArrowUp': this._moveState.pitchUp = 0; break;
217
+ case 'ArrowDown': this._moveState.pitchDown = 0; break;
218
+
219
+ case 'ArrowLeft': this._moveState.yawLeft = 0; break;
220
+ case 'ArrowRight': this._moveState.yawRight = 0; break;
221
+
222
+ case 'KeyQ': this._moveState.rollLeft = 0; break;
223
+ case 'KeyE': this._moveState.rollRight = 0; break;
224
+
225
+ }
226
+
227
+ this._updateMovementVector();
228
+ this._updateRotationVector();
229
+
230
+ }
231
+
232
+ function onPointerDown( event ) {
233
+
234
+ if ( this.enabled === false ) return;
235
+
236
+ if ( this.dragToLook ) {
237
+
238
+ this._status ++;
239
+
240
+ } else {
241
+
242
+ switch ( event.button ) {
243
+
244
+ case 0: this._moveState.forward = 1; break;
245
+ case 2: this._moveState.back = 1; break;
246
+
247
+ }
248
+
249
+ this._updateMovementVector();
250
+
251
+ }
252
+
253
+ }
254
+
255
+ function onPointerMove( event ) {
256
+
257
+ if ( this.enabled === false ) return;
258
+
259
+ if ( ! this.dragToLook || this._status > 0 ) {
260
+
261
+ const container = this._getContainerDimensions();
262
+ const halfWidth = container.size[ 0 ] / 2;
263
+ const halfHeight = container.size[ 1 ] / 2;
264
+
265
+ this._moveState.yawLeft = - ( ( event.pageX - container.offset[ 0 ] ) - halfWidth ) / halfWidth;
266
+ this._moveState.pitchDown = ( ( event.pageY - container.offset[ 1 ] ) - halfHeight ) / halfHeight;
267
+
268
+ this._updateRotationVector();
269
+
270
+ }
271
+
272
+ }
273
+
274
+ function onPointerUp( event ) {
275
+
276
+ if ( this.enabled === false ) return;
277
+
278
+ if ( this.dragToLook ) {
279
+
280
+ this._status --;
281
+
282
+ this._moveState.yawLeft = this._moveState.pitchDown = 0;
283
+
284
+ } else {
285
+
286
+ switch ( event.button ) {
287
+
288
+ case 0: this._moveState.forward = 0; break;
289
+ case 2: this._moveState.back = 0; break;
290
+
291
+ }
292
+
293
+ this._updateMovementVector();
294
+
295
+ }
296
+
297
+ this._updateRotationVector();
298
+
299
+ }
300
+
301
+ function onPointerCancel() {
302
+
303
+ if ( this.enabled === false ) return;
304
+
305
+ if ( this.dragToLook ) {
306
+
307
+ this._status = 0;
308
+
309
+ this._moveState.yawLeft = this._moveState.pitchDown = 0;
310
+
311
+ } else {
312
+
313
+ this._moveState.forward = 0;
314
+ this._moveState.back = 0;
315
+
316
+ this._updateMovementVector();
317
+
318
+ }
319
+
320
+ this._updateRotationVector();
321
+
322
+ }
323
+
324
+ function onContextMenu( event ) {
325
+
326
+ if ( this.enabled === false ) return;
327
+
328
+ event.preventDefault();
329
+
330
+ }
331
+
332
+ export { FlyControls };
libs/three.js/0.172.0/jsm/controls/MapControls.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { MOUSE, TOUCH } from 'three';
2
+
3
+ import { OrbitControls } from './OrbitControls.js';
4
+
5
+ // MapControls performs orbiting, dollying (zooming), and panning.
6
+ // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
7
+ //
8
+ // Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
9
+ // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
10
+ // Pan - left mouse, or arrow keys / touch: one-finger move
11
+
12
+ class MapControls extends OrbitControls {
13
+
14
+ constructor( object, domElement ) {
15
+
16
+ super( object, domElement );
17
+
18
+ this.screenSpacePanning = false; // pan orthogonal to world-space direction camera.up
19
+
20
+ this.mouseButtons = { LEFT: MOUSE.PAN, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.ROTATE };
21
+
22
+ this.touches = { ONE: TOUCH.PAN, TWO: TOUCH.DOLLY_ROTATE };
23
+
24
+ }
25
+
26
+ }
27
+
28
+ export { MapControls };
libs/three.js/0.172.0/jsm/controls/OrbitControls.js ADDED
@@ -0,0 +1,1556 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Controls,
3
+ MOUSE,
4
+ Quaternion,
5
+ Spherical,
6
+ TOUCH,
7
+ Vector2,
8
+ Vector3,
9
+ Plane,
10
+ Ray,
11
+ MathUtils
12
+ } from 'three';
13
+
14
+ // OrbitControls performs orbiting, dollying (zooming), and panning.
15
+ // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
16
+ //
17
+ // Orbit - left mouse / touch: one-finger move
18
+ // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
19
+ // Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
20
+
21
+ const _changeEvent = { type: 'change' };
22
+ const _startEvent = { type: 'start' };
23
+ const _endEvent = { type: 'end' };
24
+ const _ray = new Ray();
25
+ const _plane = new Plane();
26
+ const _TILT_LIMIT = Math.cos( 70 * MathUtils.DEG2RAD );
27
+
28
+ const _v = new Vector3();
29
+ const _twoPI = 2 * Math.PI;
30
+
31
+ const _STATE = {
32
+ NONE: - 1,
33
+ ROTATE: 0,
34
+ DOLLY: 1,
35
+ PAN: 2,
36
+ TOUCH_ROTATE: 3,
37
+ TOUCH_PAN: 4,
38
+ TOUCH_DOLLY_PAN: 5,
39
+ TOUCH_DOLLY_ROTATE: 6
40
+ };
41
+ const _EPS = 0.000001;
42
+
43
+ class OrbitControls extends Controls {
44
+
45
+ constructor( object, domElement = null ) {
46
+
47
+ super( object, domElement );
48
+
49
+ this.state = _STATE.NONE;
50
+
51
+ // Set to false to disable this control
52
+ this.enabled = true;
53
+
54
+ // "target" sets the location of focus, where the object orbits around
55
+ this.target = new Vector3();
56
+
57
+ // Sets the 3D cursor (similar to Blender), from which the maxTargetRadius takes effect
58
+ this.cursor = new Vector3();
59
+
60
+ // How far you can dolly in and out ( PerspectiveCamera only )
61
+ this.minDistance = 0;
62
+ this.maxDistance = Infinity;
63
+
64
+ // How far you can zoom in and out ( OrthographicCamera only )
65
+ this.minZoom = 0;
66
+ this.maxZoom = Infinity;
67
+
68
+ // Limit camera target within a spherical area around the cursor
69
+ this.minTargetRadius = 0;
70
+ this.maxTargetRadius = Infinity;
71
+
72
+ // How far you can orbit vertically, upper and lower limits.
73
+ // Range is 0 to Math.PI radians.
74
+ this.minPolarAngle = 0; // radians
75
+ this.maxPolarAngle = Math.PI; // radians
76
+
77
+ // How far you can orbit horizontally, upper and lower limits.
78
+ // If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI )
79
+ this.minAzimuthAngle = - Infinity; // radians
80
+ this.maxAzimuthAngle = Infinity; // radians
81
+
82
+ // Set to true to enable damping (inertia)
83
+ // If damping is enabled, you must call controls.update() in your animation loop
84
+ this.enableDamping = false;
85
+ this.dampingFactor = 0.05;
86
+
87
+ // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
88
+ // Set to false to disable zooming
89
+ this.enableZoom = true;
90
+ this.zoomSpeed = 1.0;
91
+
92
+ // Set to false to disable rotating
93
+ this.enableRotate = true;
94
+ this.rotateSpeed = 1.0;
95
+ this.keyRotateSpeed = 1.0;
96
+
97
+ // Set to false to disable panning
98
+ this.enablePan = true;
99
+ this.panSpeed = 1.0;
100
+ this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up
101
+ this.keyPanSpeed = 7.0; // pixels moved per arrow key push
102
+ this.zoomToCursor = false;
103
+
104
+ // Set to true to automatically rotate around the target
105
+ // If auto-rotate is enabled, you must call controls.update() in your animation loop
106
+ this.autoRotate = false;
107
+ this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60
108
+
109
+ // The four arrow keys
110
+ this.keys = { LEFT: 'ArrowLeft', UP: 'ArrowUp', RIGHT: 'ArrowRight', BOTTOM: 'ArrowDown' };
111
+
112
+ // Mouse buttons
113
+ this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
114
+
115
+ // Touch fingers
116
+ this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN };
117
+
118
+ // for reset
119
+ this.target0 = this.target.clone();
120
+ this.position0 = this.object.position.clone();
121
+ this.zoom0 = this.object.zoom;
122
+
123
+ // the target DOM element for key events
124
+ this._domElementKeyEvents = null;
125
+
126
+ // internals
127
+
128
+ this._lastPosition = new Vector3();
129
+ this._lastQuaternion = new Quaternion();
130
+ this._lastTargetPosition = new Vector3();
131
+
132
+ // so camera.up is the orbit axis
133
+ this._quat = new Quaternion().setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) );
134
+ this._quatInverse = this._quat.clone().invert();
135
+
136
+ // current position in spherical coordinates
137
+ this._spherical = new Spherical();
138
+ this._sphericalDelta = new Spherical();
139
+
140
+ this._scale = 1;
141
+ this._panOffset = new Vector3();
142
+
143
+ this._rotateStart = new Vector2();
144
+ this._rotateEnd = new Vector2();
145
+ this._rotateDelta = new Vector2();
146
+
147
+ this._panStart = new Vector2();
148
+ this._panEnd = new Vector2();
149
+ this._panDelta = new Vector2();
150
+
151
+ this._dollyStart = new Vector2();
152
+ this._dollyEnd = new Vector2();
153
+ this._dollyDelta = new Vector2();
154
+
155
+ this._dollyDirection = new Vector3();
156
+ this._mouse = new Vector2();
157
+ this._performCursorZoom = false;
158
+
159
+ this._pointers = [];
160
+ this._pointerPositions = {};
161
+
162
+ this._controlActive = false;
163
+
164
+ // event listeners
165
+
166
+ this._onPointerMove = onPointerMove.bind( this );
167
+ this._onPointerDown = onPointerDown.bind( this );
168
+ this._onPointerUp = onPointerUp.bind( this );
169
+ this._onContextMenu = onContextMenu.bind( this );
170
+ this._onMouseWheel = onMouseWheel.bind( this );
171
+ this._onKeyDown = onKeyDown.bind( this );
172
+
173
+ this._onTouchStart = onTouchStart.bind( this );
174
+ this._onTouchMove = onTouchMove.bind( this );
175
+
176
+ this._onMouseDown = onMouseDown.bind( this );
177
+ this._onMouseMove = onMouseMove.bind( this );
178
+
179
+ this._interceptControlDown = interceptControlDown.bind( this );
180
+ this._interceptControlUp = interceptControlUp.bind( this );
181
+
182
+ //
183
+
184
+ if ( this.domElement !== null ) {
185
+
186
+ this.connect();
187
+
188
+ }
189
+
190
+ this.update();
191
+
192
+ }
193
+
194
+ connect() {
195
+
196
+ this.domElement.addEventListener( 'pointerdown', this._onPointerDown );
197
+ this.domElement.addEventListener( 'pointercancel', this._onPointerUp );
198
+
199
+ this.domElement.addEventListener( 'contextmenu', this._onContextMenu );
200
+ this.domElement.addEventListener( 'wheel', this._onMouseWheel, { passive: false } );
201
+
202
+ const document = this.domElement.getRootNode(); // offscreen canvas compatibility
203
+ document.addEventListener( 'keydown', this._interceptControlDown, { passive: true, capture: true } );
204
+
205
+ this.domElement.style.touchAction = 'none'; // disable touch scroll
206
+
207
+ }
208
+
209
+ disconnect() {
210
+
211
+ this.domElement.removeEventListener( 'pointerdown', this._onPointerDown );
212
+ this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
213
+ this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
214
+ this.domElement.removeEventListener( 'pointercancel', this._onPointerUp );
215
+
216
+ this.domElement.removeEventListener( 'wheel', this._onMouseWheel );
217
+ this.domElement.removeEventListener( 'contextmenu', this._onContextMenu );
218
+
219
+ this.stopListenToKeyEvents();
220
+
221
+ const document = this.domElement.getRootNode(); // offscreen canvas compatibility
222
+ document.removeEventListener( 'keydown', this._interceptControlDown, { capture: true } );
223
+
224
+ this.domElement.style.touchAction = 'auto';
225
+
226
+ }
227
+
228
+ dispose() {
229
+
230
+ this.disconnect();
231
+
232
+ }
233
+
234
+ getPolarAngle() {
235
+
236
+ return this._spherical.phi;
237
+
238
+ }
239
+
240
+ getAzimuthalAngle() {
241
+
242
+ return this._spherical.theta;
243
+
244
+ }
245
+
246
+ getDistance() {
247
+
248
+ return this.object.position.distanceTo( this.target );
249
+
250
+ }
251
+
252
+ listenToKeyEvents( domElement ) {
253
+
254
+ domElement.addEventListener( 'keydown', this._onKeyDown );
255
+ this._domElementKeyEvents = domElement;
256
+
257
+ }
258
+
259
+ stopListenToKeyEvents() {
260
+
261
+ if ( this._domElementKeyEvents !== null ) {
262
+
263
+ this._domElementKeyEvents.removeEventListener( 'keydown', this._onKeyDown );
264
+ this._domElementKeyEvents = null;
265
+
266
+ }
267
+
268
+ }
269
+
270
+ saveState() {
271
+
272
+ this.target0.copy( this.target );
273
+ this.position0.copy( this.object.position );
274
+ this.zoom0 = this.object.zoom;
275
+
276
+ }
277
+
278
+ reset() {
279
+
280
+ this.target.copy( this.target0 );
281
+ this.object.position.copy( this.position0 );
282
+ this.object.zoom = this.zoom0;
283
+
284
+ this.object.updateProjectionMatrix();
285
+ this.dispatchEvent( _changeEvent );
286
+
287
+ this.update();
288
+
289
+ this.state = _STATE.NONE;
290
+
291
+ }
292
+
293
+ update( deltaTime = null ) {
294
+
295
+ const position = this.object.position;
296
+
297
+ _v.copy( position ).sub( this.target );
298
+
299
+ // rotate offset to "y-axis-is-up" space
300
+ _v.applyQuaternion( this._quat );
301
+
302
+ // angle from z-axis around y-axis
303
+ this._spherical.setFromVector3( _v );
304
+
305
+ if ( this.autoRotate && this.state === _STATE.NONE ) {
306
+
307
+ this._rotateLeft( this._getAutoRotationAngle( deltaTime ) );
308
+
309
+ }
310
+
311
+ if ( this.enableDamping ) {
312
+
313
+ this._spherical.theta += this._sphericalDelta.theta * this.dampingFactor;
314
+ this._spherical.phi += this._sphericalDelta.phi * this.dampingFactor;
315
+
316
+ } else {
317
+
318
+ this._spherical.theta += this._sphericalDelta.theta;
319
+ this._spherical.phi += this._sphericalDelta.phi;
320
+
321
+ }
322
+
323
+ // restrict theta to be between desired limits
324
+
325
+ let min = this.minAzimuthAngle;
326
+ let max = this.maxAzimuthAngle;
327
+
328
+ if ( isFinite( min ) && isFinite( max ) ) {
329
+
330
+ if ( min < - Math.PI ) min += _twoPI; else if ( min > Math.PI ) min -= _twoPI;
331
+
332
+ if ( max < - Math.PI ) max += _twoPI; else if ( max > Math.PI ) max -= _twoPI;
333
+
334
+ if ( min <= max ) {
335
+
336
+ this._spherical.theta = Math.max( min, Math.min( max, this._spherical.theta ) );
337
+
338
+ } else {
339
+
340
+ this._spherical.theta = ( this._spherical.theta > ( min + max ) / 2 ) ?
341
+ Math.max( min, this._spherical.theta ) :
342
+ Math.min( max, this._spherical.theta );
343
+
344
+ }
345
+
346
+ }
347
+
348
+ // restrict phi to be between desired limits
349
+ this._spherical.phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, this._spherical.phi ) );
350
+
351
+ this._spherical.makeSafe();
352
+
353
+
354
+ // move target to panned location
355
+
356
+ if ( this.enableDamping === true ) {
357
+
358
+ this.target.addScaledVector( this._panOffset, this.dampingFactor );
359
+
360
+ } else {
361
+
362
+ this.target.add( this._panOffset );
363
+
364
+ }
365
+
366
+ // Limit the target distance from the cursor to create a sphere around the center of interest
367
+ this.target.sub( this.cursor );
368
+ this.target.clampLength( this.minTargetRadius, this.maxTargetRadius );
369
+ this.target.add( this.cursor );
370
+
371
+ let zoomChanged = false;
372
+ // adjust the camera position based on zoom only if we're not zooming to the cursor or if it's an ortho camera
373
+ // we adjust zoom later in these cases
374
+ if ( this.zoomToCursor && this._performCursorZoom || this.object.isOrthographicCamera ) {
375
+
376
+ this._spherical.radius = this._clampDistance( this._spherical.radius );
377
+
378
+ } else {
379
+
380
+ const prevRadius = this._spherical.radius;
381
+ this._spherical.radius = this._clampDistance( this._spherical.radius * this._scale );
382
+ zoomChanged = prevRadius != this._spherical.radius;
383
+
384
+ }
385
+
386
+ _v.setFromSpherical( this._spherical );
387
+
388
+ // rotate offset back to "camera-up-vector-is-up" space
389
+ _v.applyQuaternion( this._quatInverse );
390
+
391
+ position.copy( this.target ).add( _v );
392
+
393
+ this.object.lookAt( this.target );
394
+
395
+ if ( this.enableDamping === true ) {
396
+
397
+ this._sphericalDelta.theta *= ( 1 - this.dampingFactor );
398
+ this._sphericalDelta.phi *= ( 1 - this.dampingFactor );
399
+
400
+ this._panOffset.multiplyScalar( 1 - this.dampingFactor );
401
+
402
+ } else {
403
+
404
+ this._sphericalDelta.set( 0, 0, 0 );
405
+
406
+ this._panOffset.set( 0, 0, 0 );
407
+
408
+ }
409
+
410
+ // adjust camera position
411
+ if ( this.zoomToCursor && this._performCursorZoom ) {
412
+
413
+ let newRadius = null;
414
+ if ( this.object.isPerspectiveCamera ) {
415
+
416
+ // move the camera down the pointer ray
417
+ // this method avoids floating point error
418
+ const prevRadius = _v.length();
419
+ newRadius = this._clampDistance( prevRadius * this._scale );
420
+
421
+ const radiusDelta = prevRadius - newRadius;
422
+ this.object.position.addScaledVector( this._dollyDirection, radiusDelta );
423
+ this.object.updateMatrixWorld();
424
+
425
+ zoomChanged = !! radiusDelta;
426
+
427
+ } else if ( this.object.isOrthographicCamera ) {
428
+
429
+ // adjust the ortho camera position based on zoom changes
430
+ const mouseBefore = new Vector3( this._mouse.x, this._mouse.y, 0 );
431
+ mouseBefore.unproject( this.object );
432
+
433
+ const prevZoom = this.object.zoom;
434
+ this.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / this._scale ) );
435
+ this.object.updateProjectionMatrix();
436
+
437
+ zoomChanged = prevZoom !== this.object.zoom;
438
+
439
+ const mouseAfter = new Vector3( this._mouse.x, this._mouse.y, 0 );
440
+ mouseAfter.unproject( this.object );
441
+
442
+ this.object.position.sub( mouseAfter ).add( mouseBefore );
443
+ this.object.updateMatrixWorld();
444
+
445
+ newRadius = _v.length();
446
+
447
+ } else {
448
+
449
+ console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled.' );
450
+ this.zoomToCursor = false;
451
+
452
+ }
453
+
454
+ // handle the placement of the target
455
+ if ( newRadius !== null ) {
456
+
457
+ if ( this.screenSpacePanning ) {
458
+
459
+ // position the orbit target in front of the new camera position
460
+ this.target.set( 0, 0, - 1 )
461
+ .transformDirection( this.object.matrix )
462
+ .multiplyScalar( newRadius )
463
+ .add( this.object.position );
464
+
465
+ } else {
466
+
467
+ // get the ray and translation plane to compute target
468
+ _ray.origin.copy( this.object.position );
469
+ _ray.direction.set( 0, 0, - 1 ).transformDirection( this.object.matrix );
470
+
471
+ // if the camera is 20 degrees above the horizon then don't adjust the focus target to avoid
472
+ // extremely large values
473
+ if ( Math.abs( this.object.up.dot( _ray.direction ) ) < _TILT_LIMIT ) {
474
+
475
+ this.object.lookAt( this.target );
476
+
477
+ } else {
478
+
479
+ _plane.setFromNormalAndCoplanarPoint( this.object.up, this.target );
480
+ _ray.intersectPlane( _plane, this.target );
481
+
482
+ }
483
+
484
+ }
485
+
486
+ }
487
+
488
+ } else if ( this.object.isOrthographicCamera ) {
489
+
490
+ const prevZoom = this.object.zoom;
491
+ this.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / this._scale ) );
492
+
493
+ if ( prevZoom !== this.object.zoom ) {
494
+
495
+ this.object.updateProjectionMatrix();
496
+ zoomChanged = true;
497
+
498
+ }
499
+
500
+ }
501
+
502
+ this._scale = 1;
503
+ this._performCursorZoom = false;
504
+
505
+ // update condition is:
506
+ // min(camera displacement, camera rotation in radians)^2 > EPS
507
+ // using small-angle approximation cos(x/2) = 1 - x^2 / 8
508
+
509
+ if ( zoomChanged ||
510
+ this._lastPosition.distanceToSquared( this.object.position ) > _EPS ||
511
+ 8 * ( 1 - this._lastQuaternion.dot( this.object.quaternion ) ) > _EPS ||
512
+ this._lastTargetPosition.distanceToSquared( this.target ) > _EPS ) {
513
+
514
+ this.dispatchEvent( _changeEvent );
515
+
516
+ this._lastPosition.copy( this.object.position );
517
+ this._lastQuaternion.copy( this.object.quaternion );
518
+ this._lastTargetPosition.copy( this.target );
519
+
520
+ return true;
521
+
522
+ }
523
+
524
+ return false;
525
+
526
+ }
527
+
528
+ _getAutoRotationAngle( deltaTime ) {
529
+
530
+ if ( deltaTime !== null ) {
531
+
532
+ return ( _twoPI / 60 * this.autoRotateSpeed ) * deltaTime;
533
+
534
+ } else {
535
+
536
+ return _twoPI / 60 / 60 * this.autoRotateSpeed;
537
+
538
+ }
539
+
540
+ }
541
+
542
+ _getZoomScale( delta ) {
543
+
544
+ const normalizedDelta = Math.abs( delta * 0.01 );
545
+ return Math.pow( 0.95, this.zoomSpeed * normalizedDelta );
546
+
547
+ }
548
+
549
+ _rotateLeft( angle ) {
550
+
551
+ this._sphericalDelta.theta -= angle;
552
+
553
+ }
554
+
555
+ _rotateUp( angle ) {
556
+
557
+ this._sphericalDelta.phi -= angle;
558
+
559
+ }
560
+
561
+ _panLeft( distance, objectMatrix ) {
562
+
563
+ _v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
564
+ _v.multiplyScalar( - distance );
565
+
566
+ this._panOffset.add( _v );
567
+
568
+ }
569
+
570
+ _panUp( distance, objectMatrix ) {
571
+
572
+ if ( this.screenSpacePanning === true ) {
573
+
574
+ _v.setFromMatrixColumn( objectMatrix, 1 );
575
+
576
+ } else {
577
+
578
+ _v.setFromMatrixColumn( objectMatrix, 0 );
579
+ _v.crossVectors( this.object.up, _v );
580
+
581
+ }
582
+
583
+ _v.multiplyScalar( distance );
584
+
585
+ this._panOffset.add( _v );
586
+
587
+ }
588
+
589
+ // deltaX and deltaY are in pixels; right and down are positive
590
+ _pan( deltaX, deltaY ) {
591
+
592
+ const element = this.domElement;
593
+
594
+ if ( this.object.isPerspectiveCamera ) {
595
+
596
+ // perspective
597
+ const position = this.object.position;
598
+ _v.copy( position ).sub( this.target );
599
+ let targetDistance = _v.length();
600
+
601
+ // half of the fov is center to top of screen
602
+ targetDistance *= Math.tan( ( this.object.fov / 2 ) * Math.PI / 180.0 );
603
+
604
+ // we use only clientHeight here so aspect ratio does not distort speed
605
+ this._panLeft( 2 * deltaX * targetDistance / element.clientHeight, this.object.matrix );
606
+ this._panUp( 2 * deltaY * targetDistance / element.clientHeight, this.object.matrix );
607
+
608
+ } else if ( this.object.isOrthographicCamera ) {
609
+
610
+ // orthographic
611
+ this._panLeft( deltaX * ( this.object.right - this.object.left ) / this.object.zoom / element.clientWidth, this.object.matrix );
612
+ this._panUp( deltaY * ( this.object.top - this.object.bottom ) / this.object.zoom / element.clientHeight, this.object.matrix );
613
+
614
+ } else {
615
+
616
+ // camera neither orthographic nor perspective
617
+ console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
618
+ this.enablePan = false;
619
+
620
+ }
621
+
622
+ }
623
+
624
+ _dollyOut( dollyScale ) {
625
+
626
+ if ( this.object.isPerspectiveCamera || this.object.isOrthographicCamera ) {
627
+
628
+ this._scale /= dollyScale;
629
+
630
+ } else {
631
+
632
+ console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
633
+ this.enableZoom = false;
634
+
635
+ }
636
+
637
+ }
638
+
639
+ _dollyIn( dollyScale ) {
640
+
641
+ if ( this.object.isPerspectiveCamera || this.object.isOrthographicCamera ) {
642
+
643
+ this._scale *= dollyScale;
644
+
645
+ } else {
646
+
647
+ console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
648
+ this.enableZoom = false;
649
+
650
+ }
651
+
652
+ }
653
+
654
+ _updateZoomParameters( x, y ) {
655
+
656
+ if ( ! this.zoomToCursor ) {
657
+
658
+ return;
659
+
660
+ }
661
+
662
+ this._performCursorZoom = true;
663
+
664
+ const rect = this.domElement.getBoundingClientRect();
665
+ const dx = x - rect.left;
666
+ const dy = y - rect.top;
667
+ const w = rect.width;
668
+ const h = rect.height;
669
+
670
+ this._mouse.x = ( dx / w ) * 2 - 1;
671
+ this._mouse.y = - ( dy / h ) * 2 + 1;
672
+
673
+ this._dollyDirection.set( this._mouse.x, this._mouse.y, 1 ).unproject( this.object ).sub( this.object.position ).normalize();
674
+
675
+ }
676
+
677
+ _clampDistance( dist ) {
678
+
679
+ return Math.max( this.minDistance, Math.min( this.maxDistance, dist ) );
680
+
681
+ }
682
+
683
+ //
684
+ // event callbacks - update the object state
685
+ //
686
+
687
+ _handleMouseDownRotate( event ) {
688
+
689
+ this._rotateStart.set( event.clientX, event.clientY );
690
+
691
+ }
692
+
693
+ _handleMouseDownDolly( event ) {
694
+
695
+ this._updateZoomParameters( event.clientX, event.clientX );
696
+ this._dollyStart.set( event.clientX, event.clientY );
697
+
698
+ }
699
+
700
+ _handleMouseDownPan( event ) {
701
+
702
+ this._panStart.set( event.clientX, event.clientY );
703
+
704
+ }
705
+
706
+ _handleMouseMoveRotate( event ) {
707
+
708
+ this._rotateEnd.set( event.clientX, event.clientY );
709
+
710
+ this._rotateDelta.subVectors( this._rotateEnd, this._rotateStart ).multiplyScalar( this.rotateSpeed );
711
+
712
+ const element = this.domElement;
713
+
714
+ this._rotateLeft( _twoPI * this._rotateDelta.x / element.clientHeight ); // yes, height
715
+
716
+ this._rotateUp( _twoPI * this._rotateDelta.y / element.clientHeight );
717
+
718
+ this._rotateStart.copy( this._rotateEnd );
719
+
720
+ this.update();
721
+
722
+ }
723
+
724
+ _handleMouseMoveDolly( event ) {
725
+
726
+ this._dollyEnd.set( event.clientX, event.clientY );
727
+
728
+ this._dollyDelta.subVectors( this._dollyEnd, this._dollyStart );
729
+
730
+ if ( this._dollyDelta.y > 0 ) {
731
+
732
+ this._dollyOut( this._getZoomScale( this._dollyDelta.y ) );
733
+
734
+ } else if ( this._dollyDelta.y < 0 ) {
735
+
736
+ this._dollyIn( this._getZoomScale( this._dollyDelta.y ) );
737
+
738
+ }
739
+
740
+ this._dollyStart.copy( this._dollyEnd );
741
+
742
+ this.update();
743
+
744
+ }
745
+
746
+ _handleMouseMovePan( event ) {
747
+
748
+ this._panEnd.set( event.clientX, event.clientY );
749
+
750
+ this._panDelta.subVectors( this._panEnd, this._panStart ).multiplyScalar( this.panSpeed );
751
+
752
+ this._pan( this._panDelta.x, this._panDelta.y );
753
+
754
+ this._panStart.copy( this._panEnd );
755
+
756
+ this.update();
757
+
758
+ }
759
+
760
+ _handleMouseWheel( event ) {
761
+
762
+ this._updateZoomParameters( event.clientX, event.clientY );
763
+
764
+ if ( event.deltaY < 0 ) {
765
+
766
+ this._dollyIn( this._getZoomScale( event.deltaY ) );
767
+
768
+ } else if ( event.deltaY > 0 ) {
769
+
770
+ this._dollyOut( this._getZoomScale( event.deltaY ) );
771
+
772
+ }
773
+
774
+ this.update();
775
+
776
+ }
777
+
778
+ _handleKeyDown( event ) {
779
+
780
+ let needsUpdate = false;
781
+
782
+ switch ( event.code ) {
783
+
784
+ case this.keys.UP:
785
+
786
+ if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
787
+
788
+ if ( this.enableRotate ) {
789
+
790
+ this._rotateUp( _twoPI * this.keyRotateSpeed / this.domElement.clientHeight );
791
+
792
+ }
793
+
794
+ } else {
795
+
796
+ if ( this.enablePan ) {
797
+
798
+ this._pan( 0, this.keyPanSpeed );
799
+
800
+ }
801
+
802
+ }
803
+
804
+ needsUpdate = true;
805
+ break;
806
+
807
+ case this.keys.BOTTOM:
808
+
809
+ if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
810
+
811
+ if ( this.enableRotate ) {
812
+
813
+ this._rotateUp( - _twoPI * this.keyRotateSpeed / this.domElement.clientHeight );
814
+
815
+ }
816
+
817
+ } else {
818
+
819
+ if ( this.enablePan ) {
820
+
821
+ this._pan( 0, - this.keyPanSpeed );
822
+
823
+ }
824
+
825
+ }
826
+
827
+ needsUpdate = true;
828
+ break;
829
+
830
+ case this.keys.LEFT:
831
+
832
+ if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
833
+
834
+ if ( this.enableRotate ) {
835
+
836
+ this._rotateLeft( _twoPI * this.keyRotateSpeed / this.domElement.clientHeight );
837
+
838
+ }
839
+
840
+ } else {
841
+
842
+ if ( this.enablePan ) {
843
+
844
+ this._pan( this.keyPanSpeed, 0 );
845
+
846
+ }
847
+
848
+ }
849
+
850
+ needsUpdate = true;
851
+ break;
852
+
853
+ case this.keys.RIGHT:
854
+
855
+ if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
856
+
857
+ if ( this.enableRotate ) {
858
+
859
+ this._rotateLeft( - _twoPI * this.keyRotateSpeed / this.domElement.clientHeight );
860
+
861
+ }
862
+
863
+ } else {
864
+
865
+ if ( this.enablePan ) {
866
+
867
+ this._pan( - this.keyPanSpeed, 0 );
868
+
869
+ }
870
+
871
+ }
872
+
873
+ needsUpdate = true;
874
+ break;
875
+
876
+ }
877
+
878
+ if ( needsUpdate ) {
879
+
880
+ // prevent the browser from scrolling on cursor keys
881
+ event.preventDefault();
882
+
883
+ this.update();
884
+
885
+ }
886
+
887
+
888
+ }
889
+
890
+ _handleTouchStartRotate( event ) {
891
+
892
+ if ( this._pointers.length === 1 ) {
893
+
894
+ this._rotateStart.set( event.pageX, event.pageY );
895
+
896
+ } else {
897
+
898
+ const position = this._getSecondPointerPosition( event );
899
+
900
+ const x = 0.5 * ( event.pageX + position.x );
901
+ const y = 0.5 * ( event.pageY + position.y );
902
+
903
+ this._rotateStart.set( x, y );
904
+
905
+ }
906
+
907
+ }
908
+
909
+ _handleTouchStartPan( event ) {
910
+
911
+ if ( this._pointers.length === 1 ) {
912
+
913
+ this._panStart.set( event.pageX, event.pageY );
914
+
915
+ } else {
916
+
917
+ const position = this._getSecondPointerPosition( event );
918
+
919
+ const x = 0.5 * ( event.pageX + position.x );
920
+ const y = 0.5 * ( event.pageY + position.y );
921
+
922
+ this._panStart.set( x, y );
923
+
924
+ }
925
+
926
+ }
927
+
928
+ _handleTouchStartDolly( event ) {
929
+
930
+ const position = this._getSecondPointerPosition( event );
931
+
932
+ const dx = event.pageX - position.x;
933
+ const dy = event.pageY - position.y;
934
+
935
+ const distance = Math.sqrt( dx * dx + dy * dy );
936
+
937
+ this._dollyStart.set( 0, distance );
938
+
939
+ }
940
+
941
+ _handleTouchStartDollyPan( event ) {
942
+
943
+ if ( this.enableZoom ) this._handleTouchStartDolly( event );
944
+
945
+ if ( this.enablePan ) this._handleTouchStartPan( event );
946
+
947
+ }
948
+
949
+ _handleTouchStartDollyRotate( event ) {
950
+
951
+ if ( this.enableZoom ) this._handleTouchStartDolly( event );
952
+
953
+ if ( this.enableRotate ) this._handleTouchStartRotate( event );
954
+
955
+ }
956
+
957
+ _handleTouchMoveRotate( event ) {
958
+
959
+ if ( this._pointers.length == 1 ) {
960
+
961
+ this._rotateEnd.set( event.pageX, event.pageY );
962
+
963
+ } else {
964
+
965
+ const position = this._getSecondPointerPosition( event );
966
+
967
+ const x = 0.5 * ( event.pageX + position.x );
968
+ const y = 0.5 * ( event.pageY + position.y );
969
+
970
+ this._rotateEnd.set( x, y );
971
+
972
+ }
973
+
974
+ this._rotateDelta.subVectors( this._rotateEnd, this._rotateStart ).multiplyScalar( this.rotateSpeed );
975
+
976
+ const element = this.domElement;
977
+
978
+ this._rotateLeft( _twoPI * this._rotateDelta.x / element.clientHeight ); // yes, height
979
+
980
+ this._rotateUp( _twoPI * this._rotateDelta.y / element.clientHeight );
981
+
982
+ this._rotateStart.copy( this._rotateEnd );
983
+
984
+ }
985
+
986
+ _handleTouchMovePan( event ) {
987
+
988
+ if ( this._pointers.length === 1 ) {
989
+
990
+ this._panEnd.set( event.pageX, event.pageY );
991
+
992
+ } else {
993
+
994
+ const position = this._getSecondPointerPosition( event );
995
+
996
+ const x = 0.5 * ( event.pageX + position.x );
997
+ const y = 0.5 * ( event.pageY + position.y );
998
+
999
+ this._panEnd.set( x, y );
1000
+
1001
+ }
1002
+
1003
+ this._panDelta.subVectors( this._panEnd, this._panStart ).multiplyScalar( this.panSpeed );
1004
+
1005
+ this._pan( this._panDelta.x, this._panDelta.y );
1006
+
1007
+ this._panStart.copy( this._panEnd );
1008
+
1009
+ }
1010
+
1011
+ _handleTouchMoveDolly( event ) {
1012
+
1013
+ const position = this._getSecondPointerPosition( event );
1014
+
1015
+ const dx = event.pageX - position.x;
1016
+ const dy = event.pageY - position.y;
1017
+
1018
+ const distance = Math.sqrt( dx * dx + dy * dy );
1019
+
1020
+ this._dollyEnd.set( 0, distance );
1021
+
1022
+ this._dollyDelta.set( 0, Math.pow( this._dollyEnd.y / this._dollyStart.y, this.zoomSpeed ) );
1023
+
1024
+ this._dollyOut( this._dollyDelta.y );
1025
+
1026
+ this._dollyStart.copy( this._dollyEnd );
1027
+
1028
+ const centerX = ( event.pageX + position.x ) * 0.5;
1029
+ const centerY = ( event.pageY + position.y ) * 0.5;
1030
+
1031
+ this._updateZoomParameters( centerX, centerY );
1032
+
1033
+ }
1034
+
1035
+ _handleTouchMoveDollyPan( event ) {
1036
+
1037
+ if ( this.enableZoom ) this._handleTouchMoveDolly( event );
1038
+
1039
+ if ( this.enablePan ) this._handleTouchMovePan( event );
1040
+
1041
+ }
1042
+
1043
+ _handleTouchMoveDollyRotate( event ) {
1044
+
1045
+ if ( this.enableZoom ) this._handleTouchMoveDolly( event );
1046
+
1047
+ if ( this.enableRotate ) this._handleTouchMoveRotate( event );
1048
+
1049
+ }
1050
+
1051
+ // pointers
1052
+
1053
+ _addPointer( event ) {
1054
+
1055
+ this._pointers.push( event.pointerId );
1056
+
1057
+ }
1058
+
1059
+ _removePointer( event ) {
1060
+
1061
+ delete this._pointerPositions[ event.pointerId ];
1062
+
1063
+ for ( let i = 0; i < this._pointers.length; i ++ ) {
1064
+
1065
+ if ( this._pointers[ i ] == event.pointerId ) {
1066
+
1067
+ this._pointers.splice( i, 1 );
1068
+ return;
1069
+
1070
+ }
1071
+
1072
+ }
1073
+
1074
+ }
1075
+
1076
+ _isTrackingPointer( event ) {
1077
+
1078
+ for ( let i = 0; i < this._pointers.length; i ++ ) {
1079
+
1080
+ if ( this._pointers[ i ] == event.pointerId ) return true;
1081
+
1082
+ }
1083
+
1084
+ return false;
1085
+
1086
+ }
1087
+
1088
+ _trackPointer( event ) {
1089
+
1090
+ let position = this._pointerPositions[ event.pointerId ];
1091
+
1092
+ if ( position === undefined ) {
1093
+
1094
+ position = new Vector2();
1095
+ this._pointerPositions[ event.pointerId ] = position;
1096
+
1097
+ }
1098
+
1099
+ position.set( event.pageX, event.pageY );
1100
+
1101
+ }
1102
+
1103
+ _getSecondPointerPosition( event ) {
1104
+
1105
+ const pointerId = ( event.pointerId === this._pointers[ 0 ] ) ? this._pointers[ 1 ] : this._pointers[ 0 ];
1106
+
1107
+ return this._pointerPositions[ pointerId ];
1108
+
1109
+ }
1110
+
1111
+ //
1112
+
1113
+ _customWheelEvent( event ) {
1114
+
1115
+ const mode = event.deltaMode;
1116
+
1117
+ // minimal wheel event altered to meet delta-zoom demand
1118
+ const newEvent = {
1119
+ clientX: event.clientX,
1120
+ clientY: event.clientY,
1121
+ deltaY: event.deltaY,
1122
+ };
1123
+
1124
+ switch ( mode ) {
1125
+
1126
+ case 1: // LINE_MODE
1127
+ newEvent.deltaY *= 16;
1128
+ break;
1129
+
1130
+ case 2: // PAGE_MODE
1131
+ newEvent.deltaY *= 100;
1132
+ break;
1133
+
1134
+ }
1135
+
1136
+ // detect if event was triggered by pinching
1137
+ if ( event.ctrlKey && ! this._controlActive ) {
1138
+
1139
+ newEvent.deltaY *= 10;
1140
+
1141
+ }
1142
+
1143
+ return newEvent;
1144
+
1145
+ }
1146
+
1147
+ }
1148
+
1149
+ function onPointerDown( event ) {
1150
+
1151
+ if ( this.enabled === false ) return;
1152
+
1153
+ if ( this._pointers.length === 0 ) {
1154
+
1155
+ this.domElement.setPointerCapture( event.pointerId );
1156
+
1157
+ this.domElement.addEventListener( 'pointermove', this._onPointerMove );
1158
+ this.domElement.addEventListener( 'pointerup', this._onPointerUp );
1159
+
1160
+ }
1161
+
1162
+ //
1163
+
1164
+ if ( this._isTrackingPointer( event ) ) return;
1165
+
1166
+ //
1167
+
1168
+ this._addPointer( event );
1169
+
1170
+ if ( event.pointerType === 'touch' ) {
1171
+
1172
+ this._onTouchStart( event );
1173
+
1174
+ } else {
1175
+
1176
+ this._onMouseDown( event );
1177
+
1178
+ }
1179
+
1180
+ }
1181
+
1182
+ function onPointerMove( event ) {
1183
+
1184
+ if ( this.enabled === false ) return;
1185
+
1186
+ if ( event.pointerType === 'touch' ) {
1187
+
1188
+ this._onTouchMove( event );
1189
+
1190
+ } else {
1191
+
1192
+ this._onMouseMove( event );
1193
+
1194
+ }
1195
+
1196
+ }
1197
+
1198
+ function onPointerUp( event ) {
1199
+
1200
+ this._removePointer( event );
1201
+
1202
+ switch ( this._pointers.length ) {
1203
+
1204
+ case 0:
1205
+
1206
+ this.domElement.releasePointerCapture( event.pointerId );
1207
+
1208
+ this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
1209
+ this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
1210
+
1211
+ this.dispatchEvent( _endEvent );
1212
+
1213
+ this.state = _STATE.NONE;
1214
+
1215
+ break;
1216
+
1217
+ case 1:
1218
+
1219
+ const pointerId = this._pointers[ 0 ];
1220
+ const position = this._pointerPositions[ pointerId ];
1221
+
1222
+ // minimal placeholder event - allows state correction on pointer-up
1223
+ this._onTouchStart( { pointerId: pointerId, pageX: position.x, pageY: position.y } );
1224
+
1225
+ break;
1226
+
1227
+ }
1228
+
1229
+ }
1230
+
1231
+ function onMouseDown( event ) {
1232
+
1233
+ let mouseAction;
1234
+
1235
+ switch ( event.button ) {
1236
+
1237
+ case 0:
1238
+
1239
+ mouseAction = this.mouseButtons.LEFT;
1240
+ break;
1241
+
1242
+ case 1:
1243
+
1244
+ mouseAction = this.mouseButtons.MIDDLE;
1245
+ break;
1246
+
1247
+ case 2:
1248
+
1249
+ mouseAction = this.mouseButtons.RIGHT;
1250
+ break;
1251
+
1252
+ default:
1253
+
1254
+ mouseAction = - 1;
1255
+
1256
+ }
1257
+
1258
+ switch ( mouseAction ) {
1259
+
1260
+ case MOUSE.DOLLY:
1261
+
1262
+ if ( this.enableZoom === false ) return;
1263
+
1264
+ this._handleMouseDownDolly( event );
1265
+
1266
+ this.state = _STATE.DOLLY;
1267
+
1268
+ break;
1269
+
1270
+ case MOUSE.ROTATE:
1271
+
1272
+ if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
1273
+
1274
+ if ( this.enablePan === false ) return;
1275
+
1276
+ this._handleMouseDownPan( event );
1277
+
1278
+ this.state = _STATE.PAN;
1279
+
1280
+ } else {
1281
+
1282
+ if ( this.enableRotate === false ) return;
1283
+
1284
+ this._handleMouseDownRotate( event );
1285
+
1286
+ this.state = _STATE.ROTATE;
1287
+
1288
+ }
1289
+
1290
+ break;
1291
+
1292
+ case MOUSE.PAN:
1293
+
1294
+ if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
1295
+
1296
+ if ( this.enableRotate === false ) return;
1297
+
1298
+ this._handleMouseDownRotate( event );
1299
+
1300
+ this.state = _STATE.ROTATE;
1301
+
1302
+ } else {
1303
+
1304
+ if ( this.enablePan === false ) return;
1305
+
1306
+ this._handleMouseDownPan( event );
1307
+
1308
+ this.state = _STATE.PAN;
1309
+
1310
+ }
1311
+
1312
+ break;
1313
+
1314
+ default:
1315
+
1316
+ this.state = _STATE.NONE;
1317
+
1318
+ }
1319
+
1320
+ if ( this.state !== _STATE.NONE ) {
1321
+
1322
+ this.dispatchEvent( _startEvent );
1323
+
1324
+ }
1325
+
1326
+ }
1327
+
1328
+ function onMouseMove( event ) {
1329
+
1330
+ switch ( this.state ) {
1331
+
1332
+ case _STATE.ROTATE:
1333
+
1334
+ if ( this.enableRotate === false ) return;
1335
+
1336
+ this._handleMouseMoveRotate( event );
1337
+
1338
+ break;
1339
+
1340
+ case _STATE.DOLLY:
1341
+
1342
+ if ( this.enableZoom === false ) return;
1343
+
1344
+ this._handleMouseMoveDolly( event );
1345
+
1346
+ break;
1347
+
1348
+ case _STATE.PAN:
1349
+
1350
+ if ( this.enablePan === false ) return;
1351
+
1352
+ this._handleMouseMovePan( event );
1353
+
1354
+ break;
1355
+
1356
+ }
1357
+
1358
+ }
1359
+
1360
+ function onMouseWheel( event ) {
1361
+
1362
+ if ( this.enabled === false || this.enableZoom === false || this.state !== _STATE.NONE ) return;
1363
+
1364
+ event.preventDefault();
1365
+
1366
+ this.dispatchEvent( _startEvent );
1367
+
1368
+ this._handleMouseWheel( this._customWheelEvent( event ) );
1369
+
1370
+ this.dispatchEvent( _endEvent );
1371
+
1372
+ }
1373
+
1374
+ function onKeyDown( event ) {
1375
+
1376
+ if ( this.enabled === false ) return;
1377
+
1378
+ this._handleKeyDown( event );
1379
+
1380
+ }
1381
+
1382
+ function onTouchStart( event ) {
1383
+
1384
+ this._trackPointer( event );
1385
+
1386
+ switch ( this._pointers.length ) {
1387
+
1388
+ case 1:
1389
+
1390
+ switch ( this.touches.ONE ) {
1391
+
1392
+ case TOUCH.ROTATE:
1393
+
1394
+ if ( this.enableRotate === false ) return;
1395
+
1396
+ this._handleTouchStartRotate( event );
1397
+
1398
+ this.state = _STATE.TOUCH_ROTATE;
1399
+
1400
+ break;
1401
+
1402
+ case TOUCH.PAN:
1403
+
1404
+ if ( this.enablePan === false ) return;
1405
+
1406
+ this._handleTouchStartPan( event );
1407
+
1408
+ this.state = _STATE.TOUCH_PAN;
1409
+
1410
+ break;
1411
+
1412
+ default:
1413
+
1414
+ this.state = _STATE.NONE;
1415
+
1416
+ }
1417
+
1418
+ break;
1419
+
1420
+ case 2:
1421
+
1422
+ switch ( this.touches.TWO ) {
1423
+
1424
+ case TOUCH.DOLLY_PAN:
1425
+
1426
+ if ( this.enableZoom === false && this.enablePan === false ) return;
1427
+
1428
+ this._handleTouchStartDollyPan( event );
1429
+
1430
+ this.state = _STATE.TOUCH_DOLLY_PAN;
1431
+
1432
+ break;
1433
+
1434
+ case TOUCH.DOLLY_ROTATE:
1435
+
1436
+ if ( this.enableZoom === false && this.enableRotate === false ) return;
1437
+
1438
+ this._handleTouchStartDollyRotate( event );
1439
+
1440
+ this.state = _STATE.TOUCH_DOLLY_ROTATE;
1441
+
1442
+ break;
1443
+
1444
+ default:
1445
+
1446
+ this.state = _STATE.NONE;
1447
+
1448
+ }
1449
+
1450
+ break;
1451
+
1452
+ default:
1453
+
1454
+ this.state = _STATE.NONE;
1455
+
1456
+ }
1457
+
1458
+ if ( this.state !== _STATE.NONE ) {
1459
+
1460
+ this.dispatchEvent( _startEvent );
1461
+
1462
+ }
1463
+
1464
+ }
1465
+
1466
+ function onTouchMove( event ) {
1467
+
1468
+ this._trackPointer( event );
1469
+
1470
+ switch ( this.state ) {
1471
+
1472
+ case _STATE.TOUCH_ROTATE:
1473
+
1474
+ if ( this.enableRotate === false ) return;
1475
+
1476
+ this._handleTouchMoveRotate( event );
1477
+
1478
+ this.update();
1479
+
1480
+ break;
1481
+
1482
+ case _STATE.TOUCH_PAN:
1483
+
1484
+ if ( this.enablePan === false ) return;
1485
+
1486
+ this._handleTouchMovePan( event );
1487
+
1488
+ this.update();
1489
+
1490
+ break;
1491
+
1492
+ case _STATE.TOUCH_DOLLY_PAN:
1493
+
1494
+ if ( this.enableZoom === false && this.enablePan === false ) return;
1495
+
1496
+ this._handleTouchMoveDollyPan( event );
1497
+
1498
+ this.update();
1499
+
1500
+ break;
1501
+
1502
+ case _STATE.TOUCH_DOLLY_ROTATE:
1503
+
1504
+ if ( this.enableZoom === false && this.enableRotate === false ) return;
1505
+
1506
+ this._handleTouchMoveDollyRotate( event );
1507
+
1508
+ this.update();
1509
+
1510
+ break;
1511
+
1512
+ default:
1513
+
1514
+ this.state = _STATE.NONE;
1515
+
1516
+ }
1517
+
1518
+ }
1519
+
1520
+ function onContextMenu( event ) {
1521
+
1522
+ if ( this.enabled === false ) return;
1523
+
1524
+ event.preventDefault();
1525
+
1526
+ }
1527
+
1528
+ function interceptControlDown( event ) {
1529
+
1530
+ if ( event.key === 'Control' ) {
1531
+
1532
+ this._controlActive = true;
1533
+
1534
+ const document = this.domElement.getRootNode(); // offscreen canvas compatibility
1535
+
1536
+ document.addEventListener( 'keyup', this._interceptControlUp, { passive: true, capture: true } );
1537
+
1538
+ }
1539
+
1540
+ }
1541
+
1542
+ function interceptControlUp( event ) {
1543
+
1544
+ if ( event.key === 'Control' ) {
1545
+
1546
+ this._controlActive = false;
1547
+
1548
+ const document = this.domElement.getRootNode(); // offscreen canvas compatibility
1549
+
1550
+ document.removeEventListener( 'keyup', this._interceptControlUp, { passive: true, capture: true } );
1551
+
1552
+ }
1553
+
1554
+ }
1555
+
1556
+ export { OrbitControls };
libs/three.js/0.172.0/jsm/controls/PointerLockControls.js ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Controls,
3
+ Euler,
4
+ Vector3
5
+ } from 'three';
6
+
7
+ const _euler = new Euler( 0, 0, 0, 'YXZ' );
8
+ const _vector = new Vector3();
9
+
10
+ const _changeEvent = { type: 'change' };
11
+ const _lockEvent = { type: 'lock' };
12
+ const _unlockEvent = { type: 'unlock' };
13
+
14
+ const _PI_2 = Math.PI / 2;
15
+
16
+ class PointerLockControls extends Controls {
17
+
18
+ constructor( camera, domElement = null ) {
19
+
20
+ super( camera, domElement );
21
+
22
+ this.isLocked = false;
23
+
24
+ // Set to constrain the pitch of the camera
25
+ // Range is 0 to Math.PI radians
26
+ this.minPolarAngle = 0; // radians
27
+ this.maxPolarAngle = Math.PI; // radians
28
+
29
+ this.pointerSpeed = 1.0;
30
+
31
+ // event listeners
32
+
33
+ this._onMouseMove = onMouseMove.bind( this );
34
+ this._onPointerlockChange = onPointerlockChange.bind( this );
35
+ this._onPointerlockError = onPointerlockError.bind( this );
36
+
37
+ if ( this.domElement !== null ) {
38
+
39
+ this.connect();
40
+
41
+ }
42
+
43
+ }
44
+
45
+ connect() {
46
+
47
+ this.domElement.ownerDocument.addEventListener( 'mousemove', this._onMouseMove );
48
+ this.domElement.ownerDocument.addEventListener( 'pointerlockchange', this._onPointerlockChange );
49
+ this.domElement.ownerDocument.addEventListener( 'pointerlockerror', this._onPointerlockError );
50
+
51
+ }
52
+
53
+ disconnect() {
54
+
55
+ this.domElement.ownerDocument.removeEventListener( 'mousemove', this._onMouseMove );
56
+ this.domElement.ownerDocument.removeEventListener( 'pointerlockchange', this._onPointerlockChange );
57
+ this.domElement.ownerDocument.removeEventListener( 'pointerlockerror', this._onPointerlockError );
58
+
59
+ }
60
+
61
+ dispose() {
62
+
63
+ this.disconnect();
64
+
65
+ }
66
+
67
+ getObject() {
68
+
69
+ console.warn( 'THREE.PointerLockControls: getObject() has been deprecated. Use controls.object instead.' ); // @deprecated r169
70
+
71
+ return this.object;
72
+
73
+ }
74
+
75
+ getDirection( v ) {
76
+
77
+ return v.set( 0, 0, - 1 ).applyQuaternion( this.object.quaternion );
78
+
79
+ }
80
+
81
+ moveForward( distance ) {
82
+
83
+ if ( this.enabled === false ) return;
84
+
85
+ // move forward parallel to the xz-plane
86
+ // assumes camera.up is y-up
87
+
88
+ const camera = this.object;
89
+
90
+ _vector.setFromMatrixColumn( camera.matrix, 0 );
91
+
92
+ _vector.crossVectors( camera.up, _vector );
93
+
94
+ camera.position.addScaledVector( _vector, distance );
95
+
96
+ }
97
+
98
+ moveRight( distance ) {
99
+
100
+ if ( this.enabled === false ) return;
101
+
102
+ const camera = this.object;
103
+
104
+ _vector.setFromMatrixColumn( camera.matrix, 0 );
105
+
106
+ camera.position.addScaledVector( _vector, distance );
107
+
108
+ }
109
+
110
+ lock() {
111
+
112
+ this.domElement.requestPointerLock();
113
+
114
+ }
115
+
116
+ unlock() {
117
+
118
+ this.domElement.ownerDocument.exitPointerLock();
119
+
120
+ }
121
+
122
+ }
123
+
124
+ // event listeners
125
+
126
+ function onMouseMove( event ) {
127
+
128
+ if ( this.enabled === false || this.isLocked === false ) return;
129
+
130
+ const camera = this.object;
131
+ _euler.setFromQuaternion( camera.quaternion );
132
+
133
+ _euler.y -= event.movementX * 0.002 * this.pointerSpeed;
134
+ _euler.x -= event.movementY * 0.002 * this.pointerSpeed;
135
+
136
+ _euler.x = Math.max( _PI_2 - this.maxPolarAngle, Math.min( _PI_2 - this.minPolarAngle, _euler.x ) );
137
+
138
+ camera.quaternion.setFromEuler( _euler );
139
+
140
+ this.dispatchEvent( _changeEvent );
141
+
142
+ }
143
+
144
+ function onPointerlockChange() {
145
+
146
+ if ( this.domElement.ownerDocument.pointerLockElement === this.domElement ) {
147
+
148
+ this.dispatchEvent( _lockEvent );
149
+
150
+ this.isLocked = true;
151
+
152
+ } else {
153
+
154
+ this.dispatchEvent( _unlockEvent );
155
+
156
+ this.isLocked = false;
157
+
158
+ }
159
+
160
+ }
161
+
162
+ function onPointerlockError() {
163
+
164
+ console.error( 'THREE.PointerLockControls: Unable to use Pointer Lock API' );
165
+
166
+ }
167
+
168
+ export { PointerLockControls };
libs/three.js/0.172.0/jsm/controls/TrackballControls.js ADDED
@@ -0,0 +1,849 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Controls,
3
+ MathUtils,
4
+ MOUSE,
5
+ Quaternion,
6
+ Vector2,
7
+ Vector3
8
+ } from 'three';
9
+
10
+ const _changeEvent = { type: 'change' };
11
+ const _startEvent = { type: 'start' };
12
+ const _endEvent = { type: 'end' };
13
+
14
+ const _EPS = 0.000001;
15
+ const _STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
16
+
17
+ const _v2 = new Vector2();
18
+ const _mouseChange = new Vector2();
19
+ const _objectUp = new Vector3();
20
+ const _pan = new Vector3();
21
+ const _axis = new Vector3();
22
+ const _quaternion = new Quaternion();
23
+ const _eyeDirection = new Vector3();
24
+ const _objectUpDirection = new Vector3();
25
+ const _objectSidewaysDirection = new Vector3();
26
+ const _moveDirection = new Vector3();
27
+
28
+ class TrackballControls extends Controls {
29
+
30
+ constructor( object, domElement = null ) {
31
+
32
+ super( object, domElement );
33
+
34
+ // API
35
+
36
+ this.enabled = true;
37
+
38
+ this.screen = { left: 0, top: 0, width: 0, height: 0 };
39
+
40
+ this.rotateSpeed = 1.0;
41
+ this.zoomSpeed = 1.2;
42
+ this.panSpeed = 0.3;
43
+
44
+ this.noRotate = false;
45
+ this.noZoom = false;
46
+ this.noPan = false;
47
+
48
+ this.staticMoving = false;
49
+ this.dynamicDampingFactor = 0.2;
50
+
51
+ this.minDistance = 0;
52
+ this.maxDistance = Infinity;
53
+
54
+ this.minZoom = 0;
55
+ this.maxZoom = Infinity;
56
+
57
+ this.keys = [ 'KeyA' /*A*/, 'KeyS' /*S*/, 'KeyD' /*D*/ ];
58
+
59
+ this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
60
+ this.state = _STATE.NONE;
61
+ this.keyState = _STATE.NONE;
62
+
63
+ this.target = new Vector3();
64
+
65
+ // internals
66
+
67
+ this._lastPosition = new Vector3();
68
+ this._lastZoom = 1;
69
+ this._touchZoomDistanceStart = 0;
70
+ this._touchZoomDistanceEnd = 0;
71
+ this._lastAngle = 0;
72
+
73
+ this._eye = new Vector3();
74
+
75
+ this._movePrev = new Vector2();
76
+ this._moveCurr = new Vector2();
77
+
78
+ this._lastAxis = new Vector3();
79
+
80
+ this._zoomStart = new Vector2();
81
+ this._zoomEnd = new Vector2();
82
+
83
+ this._panStart = new Vector2();
84
+ this._panEnd = new Vector2();
85
+
86
+ this._pointers = [];
87
+ this._pointerPositions = {};
88
+
89
+ // event listeners
90
+
91
+ this._onPointerMove = onPointerMove.bind( this );
92
+ this._onPointerDown = onPointerDown.bind( this );
93
+ this._onPointerUp = onPointerUp.bind( this );
94
+ this._onPointerCancel = onPointerCancel.bind( this );
95
+ this._onContextMenu = onContextMenu.bind( this );
96
+ this._onMouseWheel = onMouseWheel.bind( this );
97
+ this._onKeyDown = onKeyDown.bind( this );
98
+ this._onKeyUp = onKeyUp.bind( this );
99
+
100
+ this._onTouchStart = onTouchStart.bind( this );
101
+ this._onTouchMove = onTouchMove.bind( this );
102
+ this._onTouchEnd = onTouchEnd.bind( this );
103
+
104
+ this._onMouseDown = onMouseDown.bind( this );
105
+ this._onMouseMove = onMouseMove.bind( this );
106
+ this._onMouseUp = onMouseUp.bind( this );
107
+
108
+ // for reset
109
+
110
+ this._target0 = this.target.clone();
111
+ this._position0 = this.object.position.clone();
112
+ this._up0 = this.object.up.clone();
113
+ this._zoom0 = this.object.zoom;
114
+
115
+ if ( domElement !== null ) {
116
+
117
+ this.connect();
118
+
119
+ this.handleResize();
120
+
121
+ }
122
+
123
+ // force an update at start
124
+ this.update();
125
+
126
+ }
127
+
128
+ connect() {
129
+
130
+ window.addEventListener( 'keydown', this._onKeyDown );
131
+ window.addEventListener( 'keyup', this._onKeyUp );
132
+
133
+ this.domElement.addEventListener( 'pointerdown', this._onPointerDown );
134
+ this.domElement.addEventListener( 'pointercancel', this._onPointerCancel );
135
+ this.domElement.addEventListener( 'wheel', this._onMouseWheel, { passive: false } );
136
+ this.domElement.addEventListener( 'contextmenu', this._onContextMenu );
137
+
138
+ this.domElement.style.touchAction = 'none'; // disable touch scroll
139
+
140
+ }
141
+
142
+ disconnect() {
143
+
144
+ window.removeEventListener( 'keydown', this._onKeyDown );
145
+ window.removeEventListener( 'keyup', this._onKeyUp );
146
+
147
+ this.domElement.removeEventListener( 'pointerdown', this._onPointerDown );
148
+ this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
149
+ this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
150
+ this.domElement.removeEventListener( 'pointercancel', this._onPointerCancel );
151
+ this.domElement.removeEventListener( 'wheel', this._onMouseWheel );
152
+ this.domElement.removeEventListener( 'contextmenu', this._onContextMenu );
153
+
154
+ this.domElement.style.touchAction = 'auto'; // disable touch scroll
155
+
156
+ }
157
+
158
+ dispose() {
159
+
160
+ this.disconnect();
161
+
162
+ }
163
+
164
+ handleResize() {
165
+
166
+ const box = this.domElement.getBoundingClientRect();
167
+ // adjustments come from similar code in the jquery offset() function
168
+ const d = this.domElement.ownerDocument.documentElement;
169
+
170
+ this.screen.left = box.left + window.pageXOffset - d.clientLeft;
171
+ this.screen.top = box.top + window.pageYOffset - d.clientTop;
172
+ this.screen.width = box.width;
173
+ this.screen.height = box.height;
174
+
175
+ }
176
+
177
+ update() {
178
+
179
+ this._eye.subVectors( this.object.position, this.target );
180
+
181
+ if ( ! this.noRotate ) {
182
+
183
+ this._rotateCamera();
184
+
185
+ }
186
+
187
+ if ( ! this.noZoom ) {
188
+
189
+ this._zoomCamera();
190
+
191
+ }
192
+
193
+ if ( ! this.noPan ) {
194
+
195
+ this._panCamera();
196
+
197
+ }
198
+
199
+ this.object.position.addVectors( this.target, this._eye );
200
+
201
+ if ( this.object.isPerspectiveCamera ) {
202
+
203
+ this._checkDistances();
204
+
205
+ this.object.lookAt( this.target );
206
+
207
+ if ( this._lastPosition.distanceToSquared( this.object.position ) > _EPS ) {
208
+
209
+ this.dispatchEvent( _changeEvent );
210
+
211
+ this._lastPosition.copy( this.object.position );
212
+
213
+ }
214
+
215
+ } else if ( this.object.isOrthographicCamera ) {
216
+
217
+ this.object.lookAt( this.target );
218
+
219
+ if ( this._lastPosition.distanceToSquared( this.object.position ) > _EPS || this._lastZoom !== this.object.zoom ) {
220
+
221
+ this.dispatchEvent( _changeEvent );
222
+
223
+ this._lastPosition.copy( this.object.position );
224
+ this._lastZoom = this.object.zoom;
225
+
226
+ }
227
+
228
+ } else {
229
+
230
+ console.warn( 'THREE.TrackballControls: Unsupported camera type.' );
231
+
232
+ }
233
+
234
+ }
235
+
236
+ reset() {
237
+
238
+ this.state = _STATE.NONE;
239
+ this.keyState = _STATE.NONE;
240
+
241
+ this.target.copy( this._target0 );
242
+ this.object.position.copy( this._position0 );
243
+ this.object.up.copy( this._up0 );
244
+ this.object.zoom = this._zoom0;
245
+
246
+ this.object.updateProjectionMatrix();
247
+
248
+ this._eye.subVectors( this.object.position, this.target );
249
+
250
+ this.object.lookAt( this.target );
251
+
252
+ this.dispatchEvent( _changeEvent );
253
+
254
+ this._lastPosition.copy( this.object.position );
255
+ this._lastZoom = this.object.zoom;
256
+
257
+ }
258
+
259
+ _panCamera() {
260
+
261
+ _mouseChange.copy( this._panEnd ).sub( this._panStart );
262
+
263
+ if ( _mouseChange.lengthSq() ) {
264
+
265
+ if ( this.object.isOrthographicCamera ) {
266
+
267
+ const scale_x = ( this.object.right - this.object.left ) / this.object.zoom / this.domElement.clientWidth;
268
+ const scale_y = ( this.object.top - this.object.bottom ) / this.object.zoom / this.domElement.clientWidth;
269
+
270
+ _mouseChange.x *= scale_x;
271
+ _mouseChange.y *= scale_y;
272
+
273
+ }
274
+
275
+ _mouseChange.multiplyScalar( this._eye.length() * this.panSpeed );
276
+
277
+ _pan.copy( this._eye ).cross( this.object.up ).setLength( _mouseChange.x );
278
+ _pan.add( _objectUp.copy( this.object.up ).setLength( _mouseChange.y ) );
279
+
280
+ this.object.position.add( _pan );
281
+ this.target.add( _pan );
282
+
283
+ if ( this.staticMoving ) {
284
+
285
+ this._panStart.copy( this._panEnd );
286
+
287
+ } else {
288
+
289
+ this._panStart.add( _mouseChange.subVectors( this._panEnd, this._panStart ).multiplyScalar( this.dynamicDampingFactor ) );
290
+
291
+ }
292
+
293
+ }
294
+
295
+ }
296
+
297
+ _rotateCamera() {
298
+
299
+ _moveDirection.set( this._moveCurr.x - this._movePrev.x, this._moveCurr.y - this._movePrev.y, 0 );
300
+ let angle = _moveDirection.length();
301
+
302
+ if ( angle ) {
303
+
304
+ this._eye.copy( this.object.position ).sub( this.target );
305
+
306
+ _eyeDirection.copy( this._eye ).normalize();
307
+ _objectUpDirection.copy( this.object.up ).normalize();
308
+ _objectSidewaysDirection.crossVectors( _objectUpDirection, _eyeDirection ).normalize();
309
+
310
+ _objectUpDirection.setLength( this._moveCurr.y - this._movePrev.y );
311
+ _objectSidewaysDirection.setLength( this._moveCurr.x - this._movePrev.x );
312
+
313
+ _moveDirection.copy( _objectUpDirection.add( _objectSidewaysDirection ) );
314
+
315
+ _axis.crossVectors( _moveDirection, this._eye ).normalize();
316
+
317
+ angle *= this.rotateSpeed;
318
+ _quaternion.setFromAxisAngle( _axis, angle );
319
+
320
+ this._eye.applyQuaternion( _quaternion );
321
+ this.object.up.applyQuaternion( _quaternion );
322
+
323
+ this._lastAxis.copy( _axis );
324
+ this._lastAngle = angle;
325
+
326
+ } else if ( ! this.staticMoving && this._lastAngle ) {
327
+
328
+ this._lastAngle *= Math.sqrt( 1.0 - this.dynamicDampingFactor );
329
+ this._eye.copy( this.object.position ).sub( this.target );
330
+ _quaternion.setFromAxisAngle( this._lastAxis, this._lastAngle );
331
+ this._eye.applyQuaternion( _quaternion );
332
+ this.object.up.applyQuaternion( _quaternion );
333
+
334
+ }
335
+
336
+ this._movePrev.copy( this._moveCurr );
337
+
338
+ }
339
+
340
+ _zoomCamera() {
341
+
342
+ let factor;
343
+
344
+ if ( this.state === _STATE.TOUCH_ZOOM_PAN ) {
345
+
346
+ factor = this._touchZoomDistanceStart / this._touchZoomDistanceEnd;
347
+ this._touchZoomDistanceStart = this._touchZoomDistanceEnd;
348
+
349
+ if ( this.object.isPerspectiveCamera ) {
350
+
351
+ this._eye.multiplyScalar( factor );
352
+
353
+ } else if ( this.object.isOrthographicCamera ) {
354
+
355
+ this.object.zoom = MathUtils.clamp( this.object.zoom / factor, this.minZoom, this.maxZoom );
356
+
357
+ if ( this._lastZoom !== this.object.zoom ) {
358
+
359
+ this.object.updateProjectionMatrix();
360
+
361
+ }
362
+
363
+ } else {
364
+
365
+ console.warn( 'THREE.TrackballControls: Unsupported camera type' );
366
+
367
+ }
368
+
369
+ } else {
370
+
371
+ factor = 1.0 + ( this._zoomEnd.y - this._zoomStart.y ) * this.zoomSpeed;
372
+
373
+ if ( factor !== 1.0 && factor > 0.0 ) {
374
+
375
+ if ( this.object.isPerspectiveCamera ) {
376
+
377
+ this._eye.multiplyScalar( factor );
378
+
379
+ } else if ( this.object.isOrthographicCamera ) {
380
+
381
+ this.object.zoom = MathUtils.clamp( this.object.zoom / factor, this.minZoom, this.maxZoom );
382
+
383
+ if ( this._lastZoom !== this.object.zoom ) {
384
+
385
+ this.object.updateProjectionMatrix();
386
+
387
+ }
388
+
389
+ } else {
390
+
391
+ console.warn( 'THREE.TrackballControls: Unsupported camera type' );
392
+
393
+ }
394
+
395
+ }
396
+
397
+ if ( this.staticMoving ) {
398
+
399
+ this._zoomStart.copy( this._zoomEnd );
400
+
401
+ } else {
402
+
403
+ this._zoomStart.y += ( this._zoomEnd.y - this._zoomStart.y ) * this.dynamicDampingFactor;
404
+
405
+ }
406
+
407
+ }
408
+
409
+ }
410
+
411
+ _getMouseOnScreen( pageX, pageY ) {
412
+
413
+ _v2.set(
414
+ ( pageX - this.screen.left ) / this.screen.width,
415
+ ( pageY - this.screen.top ) / this.screen.height
416
+ );
417
+
418
+ return _v2;
419
+
420
+ }
421
+
422
+ _getMouseOnCircle( pageX, pageY ) {
423
+
424
+ _v2.set(
425
+ ( ( pageX - this.screen.width * 0.5 - this.screen.left ) / ( this.screen.width * 0.5 ) ),
426
+ ( ( this.screen.height + 2 * ( this.screen.top - pageY ) ) / this.screen.width ) // screen.width intentional
427
+ );
428
+
429
+ return _v2;
430
+
431
+ }
432
+
433
+ _addPointer( event ) {
434
+
435
+ this._pointers.push( event );
436
+
437
+ }
438
+
439
+ _removePointer( event ) {
440
+
441
+ delete this._pointerPositions[ event.pointerId ];
442
+
443
+ for ( let i = 0; i < this._pointers.length; i ++ ) {
444
+
445
+ if ( this._pointers[ i ].pointerId == event.pointerId ) {
446
+
447
+ this._pointers.splice( i, 1 );
448
+ return;
449
+
450
+ }
451
+
452
+ }
453
+
454
+ }
455
+
456
+ _trackPointer( event ) {
457
+
458
+ let position = this._pointerPositions[ event.pointerId ];
459
+
460
+ if ( position === undefined ) {
461
+
462
+ position = new Vector2();
463
+ this._pointerPositions[ event.pointerId ] = position;
464
+
465
+ }
466
+
467
+ position.set( event.pageX, event.pageY );
468
+
469
+ }
470
+
471
+ _getSecondPointerPosition( event ) {
472
+
473
+ const pointer = ( event.pointerId === this._pointers[ 0 ].pointerId ) ? this._pointers[ 1 ] : this._pointers[ 0 ];
474
+
475
+ return this._pointerPositions[ pointer.pointerId ];
476
+
477
+ }
478
+
479
+ _checkDistances() {
480
+
481
+ if ( ! this.noZoom || ! this.noPan ) {
482
+
483
+ if ( this._eye.lengthSq() > this.maxDistance * this.maxDistance ) {
484
+
485
+ this.object.position.addVectors( this.target, this._eye.setLength( this.maxDistance ) );
486
+ this._zoomStart.copy( this._zoomEnd );
487
+
488
+ }
489
+
490
+ if ( this._eye.lengthSq() < this.minDistance * this.minDistance ) {
491
+
492
+ this.object.position.addVectors( this.target, this._eye.setLength( this.minDistance ) );
493
+ this._zoomStart.copy( this._zoomEnd );
494
+
495
+ }
496
+
497
+ }
498
+
499
+ }
500
+
501
+ }
502
+
503
+ function onPointerDown( event ) {
504
+
505
+ if ( this.enabled === false ) return;
506
+
507
+ if ( this._pointers.length === 0 ) {
508
+
509
+ this.domElement.setPointerCapture( event.pointerId );
510
+
511
+ this.domElement.addEventListener( 'pointermove', this._onPointerMove );
512
+ this.domElement.addEventListener( 'pointerup', this._onPointerUp );
513
+
514
+ }
515
+
516
+ //
517
+
518
+ this._addPointer( event );
519
+
520
+ if ( event.pointerType === 'touch' ) {
521
+
522
+ this._onTouchStart( event );
523
+
524
+ } else {
525
+
526
+ this._onMouseDown( event );
527
+
528
+ }
529
+
530
+ }
531
+
532
+ function onPointerMove( event ) {
533
+
534
+ if ( this.enabled === false ) return;
535
+
536
+ if ( event.pointerType === 'touch' ) {
537
+
538
+ this._onTouchMove( event );
539
+
540
+ } else {
541
+
542
+ this._onMouseMove( event );
543
+
544
+ }
545
+
546
+ }
547
+
548
+ function onPointerUp( event ) {
549
+
550
+ if ( this.enabled === false ) return;
551
+
552
+ if ( event.pointerType === 'touch' ) {
553
+
554
+ this._onTouchEnd( event );
555
+
556
+ } else {
557
+
558
+ this._onMouseUp();
559
+
560
+ }
561
+
562
+ //
563
+
564
+ this._removePointer( event );
565
+
566
+ if ( this._pointers.length === 0 ) {
567
+
568
+ this.domElement.releasePointerCapture( event.pointerId );
569
+
570
+ this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
571
+ this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
572
+
573
+ }
574
+
575
+ }
576
+
577
+ function onPointerCancel( event ) {
578
+
579
+ this._removePointer( event );
580
+
581
+ }
582
+
583
+ function onKeyUp() {
584
+
585
+ if ( this.enabled === false ) return;
586
+
587
+ this.keyState = _STATE.NONE;
588
+
589
+ window.addEventListener( 'keydown', this._onKeyDown );
590
+
591
+ }
592
+
593
+ function onKeyDown( event ) {
594
+
595
+ if ( this.enabled === false ) return;
596
+
597
+ window.removeEventListener( 'keydown', this._onKeyDown );
598
+
599
+ if ( this.keyState !== _STATE.NONE ) {
600
+
601
+ return;
602
+
603
+ } else if ( event.code === this.keys[ _STATE.ROTATE ] && ! this.noRotate ) {
604
+
605
+ this.keyState = _STATE.ROTATE;
606
+
607
+ } else if ( event.code === this.keys[ _STATE.ZOOM ] && ! this.noZoom ) {
608
+
609
+ this.keyState = _STATE.ZOOM;
610
+
611
+ } else if ( event.code === this.keys[ _STATE.PAN ] && ! this.noPan ) {
612
+
613
+ this.keyState = _STATE.PAN;
614
+
615
+ }
616
+
617
+ }
618
+
619
+ function onMouseDown( event ) {
620
+
621
+ let mouseAction;
622
+
623
+ switch ( event.button ) {
624
+
625
+ case 0:
626
+ mouseAction = this.mouseButtons.LEFT;
627
+ break;
628
+
629
+ case 1:
630
+ mouseAction = this.mouseButtons.MIDDLE;
631
+ break;
632
+
633
+ case 2:
634
+ mouseAction = this.mouseButtons.RIGHT;
635
+ break;
636
+
637
+ default:
638
+ mouseAction = - 1;
639
+
640
+ }
641
+
642
+ switch ( mouseAction ) {
643
+
644
+ case MOUSE.DOLLY:
645
+ this.state = _STATE.ZOOM;
646
+ break;
647
+
648
+ case MOUSE.ROTATE:
649
+ this.state = _STATE.ROTATE;
650
+ break;
651
+
652
+ case MOUSE.PAN:
653
+ this.state = _STATE.PAN;
654
+ break;
655
+
656
+ default:
657
+ this.state = _STATE.NONE;
658
+
659
+ }
660
+
661
+ const state = ( this.keyState !== _STATE.NONE ) ? this.keyState : this.state;
662
+
663
+ if ( state === _STATE.ROTATE && ! this.noRotate ) {
664
+
665
+ this._moveCurr.copy( this._getMouseOnCircle( event.pageX, event.pageY ) );
666
+ this._movePrev.copy( this._moveCurr );
667
+
668
+ } else if ( state === _STATE.ZOOM && ! this.noZoom ) {
669
+
670
+ this._zoomStart.copy( this._getMouseOnScreen( event.pageX, event.pageY ) );
671
+ this._zoomEnd.copy( this._zoomStart );
672
+
673
+ } else if ( state === _STATE.PAN && ! this.noPan ) {
674
+
675
+ this._panStart.copy( this._getMouseOnScreen( event.pageX, event.pageY ) );
676
+ this._panEnd.copy( this._panStart );
677
+
678
+ }
679
+
680
+ this.dispatchEvent( _startEvent );
681
+
682
+ }
683
+
684
+ function onMouseMove( event ) {
685
+
686
+ const state = ( this.keyState !== _STATE.NONE ) ? this.keyState : this.state;
687
+
688
+ if ( state === _STATE.ROTATE && ! this.noRotate ) {
689
+
690
+ this._movePrev.copy( this._moveCurr );
691
+ this._moveCurr.copy( this._getMouseOnCircle( event.pageX, event.pageY ) );
692
+
693
+ } else if ( state === _STATE.ZOOM && ! this.noZoom ) {
694
+
695
+ this._zoomEnd.copy( this._getMouseOnScreen( event.pageX, event.pageY ) );
696
+
697
+ } else if ( state === _STATE.PAN && ! this.noPan ) {
698
+
699
+ this._panEnd.copy( this._getMouseOnScreen( event.pageX, event.pageY ) );
700
+
701
+ }
702
+
703
+ }
704
+
705
+ function onMouseUp() {
706
+
707
+ this.state = _STATE.NONE;
708
+
709
+ this.dispatchEvent( _endEvent );
710
+
711
+ }
712
+
713
+ function onMouseWheel( event ) {
714
+
715
+ if ( this.enabled === false ) return;
716
+
717
+ if ( this.noZoom === true ) return;
718
+
719
+ event.preventDefault();
720
+
721
+ switch ( event.deltaMode ) {
722
+
723
+ case 2:
724
+ // Zoom in pages
725
+ this._zoomStart.y -= event.deltaY * 0.025;
726
+ break;
727
+
728
+ case 1:
729
+ // Zoom in lines
730
+ this._zoomStart.y -= event.deltaY * 0.01;
731
+ break;
732
+
733
+ default:
734
+ // undefined, 0, assume pixels
735
+ this._zoomStart.y -= event.deltaY * 0.00025;
736
+ break;
737
+
738
+ }
739
+
740
+ this.dispatchEvent( _startEvent );
741
+ this.dispatchEvent( _endEvent );
742
+
743
+ }
744
+
745
+ function onContextMenu( event ) {
746
+
747
+ if ( this.enabled === false ) return;
748
+
749
+ event.preventDefault();
750
+
751
+ }
752
+
753
+ function onTouchStart( event ) {
754
+
755
+ this._trackPointer( event );
756
+
757
+ switch ( this._pointers.length ) {
758
+
759
+ case 1:
760
+ this.state = _STATE.TOUCH_ROTATE;
761
+ this._moveCurr.copy( this._getMouseOnCircle( this._pointers[ 0 ].pageX, this._pointers[ 0 ].pageY ) );
762
+ this._movePrev.copy( this._moveCurr );
763
+ break;
764
+
765
+ default: // 2 or more
766
+ this.state = _STATE.TOUCH_ZOOM_PAN;
767
+ const dx = this._pointers[ 0 ].pageX - this._pointers[ 1 ].pageX;
768
+ const dy = this._pointers[ 0 ].pageY - this._pointers[ 1 ].pageY;
769
+ this._touchZoomDistanceEnd = this._touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
770
+
771
+ const x = ( this._pointers[ 0 ].pageX + this._pointers[ 1 ].pageX ) / 2;
772
+ const y = ( this._pointers[ 0 ].pageY + this._pointers[ 1 ].pageY ) / 2;
773
+ this._panStart.copy( this._getMouseOnScreen( x, y ) );
774
+ this._panEnd.copy( this._panStart );
775
+ break;
776
+
777
+ }
778
+
779
+ this.dispatchEvent( _startEvent );
780
+
781
+ }
782
+
783
+ function onTouchMove( event ) {
784
+
785
+ this._trackPointer( event );
786
+
787
+ switch ( this._pointers.length ) {
788
+
789
+ case 1:
790
+ this._movePrev.copy( this._moveCurr );
791
+ this._moveCurr.copy( this._getMouseOnCircle( event.pageX, event.pageY ) );
792
+ break;
793
+
794
+ default: // 2 or more
795
+
796
+ const position = this._getSecondPointerPosition( event );
797
+
798
+ const dx = event.pageX - position.x;
799
+ const dy = event.pageY - position.y;
800
+ this._touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
801
+
802
+ const x = ( event.pageX + position.x ) / 2;
803
+ const y = ( event.pageY + position.y ) / 2;
804
+ this._panEnd.copy( this._getMouseOnScreen( x, y ) );
805
+ break;
806
+
807
+ }
808
+
809
+ }
810
+
811
+ function onTouchEnd( event ) {
812
+
813
+ switch ( this._pointers.length ) {
814
+
815
+ case 0:
816
+ this.state = _STATE.NONE;
817
+ break;
818
+
819
+ case 1:
820
+ this.state = _STATE.TOUCH_ROTATE;
821
+ this._moveCurr.copy( this._getMouseOnCircle( event.pageX, event.pageY ) );
822
+ this._movePrev.copy( this._moveCurr );
823
+ break;
824
+
825
+ case 2:
826
+ this.state = _STATE.TOUCH_ZOOM_PAN;
827
+
828
+ for ( let i = 0; i < this._pointers.length; i ++ ) {
829
+
830
+ if ( this._pointers[ i ].pointerId !== event.pointerId ) {
831
+
832
+ const position = this._pointerPositions[ this._pointers[ i ].pointerId ];
833
+ this._moveCurr.copy( this._getMouseOnCircle( position.x, position.y ) );
834
+ this._movePrev.copy( this._moveCurr );
835
+ break;
836
+
837
+ }
838
+
839
+ }
840
+
841
+ break;
842
+
843
+ }
844
+
845
+ this.dispatchEvent( _endEvent );
846
+
847
+ }
848
+
849
+ export { TrackballControls };
libs/three.js/0.172.0/jsm/controls/TransformControls.js ADDED
@@ -0,0 +1,1624 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ BoxGeometry,
3
+ BufferGeometry,
4
+ Controls,
5
+ CylinderGeometry,
6
+ DoubleSide,
7
+ Euler,
8
+ Float32BufferAttribute,
9
+ Line,
10
+ LineBasicMaterial,
11
+ Matrix4,
12
+ Mesh,
13
+ MeshBasicMaterial,
14
+ Object3D,
15
+ OctahedronGeometry,
16
+ PlaneGeometry,
17
+ Quaternion,
18
+ Raycaster,
19
+ SphereGeometry,
20
+ TorusGeometry,
21
+ Vector3
22
+ } from 'three';
23
+
24
+ const _raycaster = new Raycaster();
25
+
26
+ const _tempVector = new Vector3();
27
+ const _tempVector2 = new Vector3();
28
+ const _tempQuaternion = new Quaternion();
29
+ const _unit = {
30
+ X: new Vector3( 1, 0, 0 ),
31
+ Y: new Vector3( 0, 1, 0 ),
32
+ Z: new Vector3( 0, 0, 1 )
33
+ };
34
+
35
+ const _changeEvent = { type: 'change' };
36
+ const _mouseDownEvent = { type: 'mouseDown', mode: null };
37
+ const _mouseUpEvent = { type: 'mouseUp', mode: null };
38
+ const _objectChangeEvent = { type: 'objectChange' };
39
+
40
+ class TransformControls extends Controls {
41
+
42
+ constructor( camera, domElement = null ) {
43
+
44
+ super( undefined, domElement );
45
+
46
+ const root = new TransformControlsRoot( this );
47
+ this._root = root;
48
+
49
+ const gizmo = new TransformControlsGizmo();
50
+ this._gizmo = gizmo;
51
+ root.add( gizmo );
52
+
53
+ const plane = new TransformControlsPlane();
54
+ this._plane = plane;
55
+ root.add( plane );
56
+
57
+ const scope = this;
58
+
59
+ // Defined getter, setter and store for a property
60
+ function defineProperty( propName, defaultValue ) {
61
+
62
+ let propValue = defaultValue;
63
+
64
+ Object.defineProperty( scope, propName, {
65
+
66
+ get: function () {
67
+
68
+ return propValue !== undefined ? propValue : defaultValue;
69
+
70
+ },
71
+
72
+ set: function ( value ) {
73
+
74
+ if ( propValue !== value ) {
75
+
76
+ propValue = value;
77
+ plane[ propName ] = value;
78
+ gizmo[ propName ] = value;
79
+
80
+ scope.dispatchEvent( { type: propName + '-changed', value: value } );
81
+ scope.dispatchEvent( _changeEvent );
82
+
83
+ }
84
+
85
+ }
86
+
87
+ } );
88
+
89
+ scope[ propName ] = defaultValue;
90
+ plane[ propName ] = defaultValue;
91
+ gizmo[ propName ] = defaultValue;
92
+
93
+ }
94
+
95
+ // Define properties with getters/setter
96
+ // Setting the defined property will automatically trigger change event
97
+ // Defined properties are passed down to gizmo and plane
98
+
99
+ defineProperty( 'camera', camera );
100
+ defineProperty( 'object', undefined );
101
+ defineProperty( 'enabled', true );
102
+ defineProperty( 'axis', null );
103
+ defineProperty( 'mode', 'translate' );
104
+ defineProperty( 'translationSnap', null );
105
+ defineProperty( 'rotationSnap', null );
106
+ defineProperty( 'scaleSnap', null );
107
+ defineProperty( 'space', 'world' );
108
+ defineProperty( 'size', 1 );
109
+ defineProperty( 'dragging', false );
110
+ defineProperty( 'showX', true );
111
+ defineProperty( 'showY', true );
112
+ defineProperty( 'showZ', true );
113
+ defineProperty( 'minX', - Infinity );
114
+ defineProperty( 'maxX', Infinity );
115
+ defineProperty( 'minY', - Infinity );
116
+ defineProperty( 'maxY', Infinity );
117
+ defineProperty( 'minZ', - Infinity );
118
+ defineProperty( 'maxZ', Infinity );
119
+
120
+ // Reusable utility variables
121
+
122
+ const worldPosition = new Vector3();
123
+ const worldPositionStart = new Vector3();
124
+ const worldQuaternion = new Quaternion();
125
+ const worldQuaternionStart = new Quaternion();
126
+ const cameraPosition = new Vector3();
127
+ const cameraQuaternion = new Quaternion();
128
+ const pointStart = new Vector3();
129
+ const pointEnd = new Vector3();
130
+ const rotationAxis = new Vector3();
131
+ const rotationAngle = 0;
132
+ const eye = new Vector3();
133
+
134
+ // TODO: remove properties unused in plane and gizmo
135
+
136
+ defineProperty( 'worldPosition', worldPosition );
137
+ defineProperty( 'worldPositionStart', worldPositionStart );
138
+ defineProperty( 'worldQuaternion', worldQuaternion );
139
+ defineProperty( 'worldQuaternionStart', worldQuaternionStart );
140
+ defineProperty( 'cameraPosition', cameraPosition );
141
+ defineProperty( 'cameraQuaternion', cameraQuaternion );
142
+ defineProperty( 'pointStart', pointStart );
143
+ defineProperty( 'pointEnd', pointEnd );
144
+ defineProperty( 'rotationAxis', rotationAxis );
145
+ defineProperty( 'rotationAngle', rotationAngle );
146
+ defineProperty( 'eye', eye );
147
+
148
+ this._offset = new Vector3();
149
+ this._startNorm = new Vector3();
150
+ this._endNorm = new Vector3();
151
+ this._cameraScale = new Vector3();
152
+
153
+ this._parentPosition = new Vector3();
154
+ this._parentQuaternion = new Quaternion();
155
+ this._parentQuaternionInv = new Quaternion();
156
+ this._parentScale = new Vector3();
157
+
158
+ this._worldScaleStart = new Vector3();
159
+ this._worldQuaternionInv = new Quaternion();
160
+ this._worldScale = new Vector3();
161
+
162
+ this._positionStart = new Vector3();
163
+ this._quaternionStart = new Quaternion();
164
+ this._scaleStart = new Vector3();
165
+
166
+ this._getPointer = getPointer.bind( this );
167
+ this._onPointerDown = onPointerDown.bind( this );
168
+ this._onPointerHover = onPointerHover.bind( this );
169
+ this._onPointerMove = onPointerMove.bind( this );
170
+ this._onPointerUp = onPointerUp.bind( this );
171
+
172
+ if ( domElement !== null ) {
173
+
174
+ this.connect();
175
+
176
+ }
177
+
178
+ }
179
+
180
+ connect() {
181
+
182
+ this.domElement.addEventListener( 'pointerdown', this._onPointerDown );
183
+ this.domElement.addEventListener( 'pointermove', this._onPointerHover );
184
+ this.domElement.addEventListener( 'pointerup', this._onPointerUp );
185
+
186
+ this.domElement.style.touchAction = 'none'; // disable touch scroll
187
+
188
+ }
189
+
190
+ disconnect() {
191
+
192
+ this.domElement.removeEventListener( 'pointerdown', this._onPointerDown );
193
+ this.domElement.removeEventListener( 'pointermove', this._onPointerHover );
194
+ this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
195
+ this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
196
+
197
+ this.domElement.style.touchAction = 'auto';
198
+
199
+ }
200
+
201
+ getHelper() {
202
+
203
+ return this._root;
204
+
205
+ }
206
+
207
+ pointerHover( pointer ) {
208
+
209
+ if ( this.object === undefined || this.dragging === true ) return;
210
+
211
+ if ( pointer !== null ) _raycaster.setFromCamera( pointer, this.camera );
212
+
213
+ const intersect = intersectObjectWithRay( this._gizmo.picker[ this.mode ], _raycaster );
214
+
215
+ if ( intersect ) {
216
+
217
+ this.axis = intersect.object.name;
218
+
219
+ } else {
220
+
221
+ this.axis = null;
222
+
223
+ }
224
+
225
+ }
226
+
227
+ pointerDown( pointer ) {
228
+
229
+ if ( this.object === undefined || this.dragging === true || ( pointer != null && pointer.button !== 0 ) ) return;
230
+
231
+ if ( this.axis !== null ) {
232
+
233
+ if ( pointer !== null ) _raycaster.setFromCamera( pointer, this.camera );
234
+
235
+ const planeIntersect = intersectObjectWithRay( this._plane, _raycaster, true );
236
+
237
+ if ( planeIntersect ) {
238
+
239
+ this.object.updateMatrixWorld();
240
+ this.object.parent.updateMatrixWorld();
241
+
242
+ this._positionStart.copy( this.object.position );
243
+ this._quaternionStart.copy( this.object.quaternion );
244
+ this._scaleStart.copy( this.object.scale );
245
+
246
+ this.object.matrixWorld.decompose( this.worldPositionStart, this.worldQuaternionStart, this._worldScaleStart );
247
+
248
+ this.pointStart.copy( planeIntersect.point ).sub( this.worldPositionStart );
249
+
250
+ }
251
+
252
+ this.dragging = true;
253
+ _mouseDownEvent.mode = this.mode;
254
+ this.dispatchEvent( _mouseDownEvent );
255
+
256
+ }
257
+
258
+ }
259
+
260
+ pointerMove( pointer ) {
261
+
262
+ const axis = this.axis;
263
+ const mode = this.mode;
264
+ const object = this.object;
265
+ let space = this.space;
266
+
267
+ if ( mode === 'scale' ) {
268
+
269
+ space = 'local';
270
+
271
+ } else if ( axis === 'E' || axis === 'XYZE' || axis === 'XYZ' ) {
272
+
273
+ space = 'world';
274
+
275
+ }
276
+
277
+ if ( object === undefined || axis === null || this.dragging === false || ( pointer !== null && pointer.button !== - 1 ) ) return;
278
+
279
+ if ( pointer !== null ) _raycaster.setFromCamera( pointer, this.camera );
280
+
281
+ const planeIntersect = intersectObjectWithRay( this._plane, _raycaster, true );
282
+
283
+ if ( ! planeIntersect ) return;
284
+
285
+ this.pointEnd.copy( planeIntersect.point ).sub( this.worldPositionStart );
286
+
287
+ if ( mode === 'translate' ) {
288
+
289
+ // Apply translate
290
+
291
+ this._offset.copy( this.pointEnd ).sub( this.pointStart );
292
+
293
+ if ( space === 'local' && axis !== 'XYZ' ) {
294
+
295
+ this._offset.applyQuaternion( this._worldQuaternionInv );
296
+
297
+ }
298
+
299
+ if ( axis.indexOf( 'X' ) === - 1 ) this._offset.x = 0;
300
+ if ( axis.indexOf( 'Y' ) === - 1 ) this._offset.y = 0;
301
+ if ( axis.indexOf( 'Z' ) === - 1 ) this._offset.z = 0;
302
+
303
+ if ( space === 'local' && axis !== 'XYZ' ) {
304
+
305
+ this._offset.applyQuaternion( this._quaternionStart ).divide( this._parentScale );
306
+
307
+ } else {
308
+
309
+ this._offset.applyQuaternion( this._parentQuaternionInv ).divide( this._parentScale );
310
+
311
+ }
312
+
313
+ object.position.copy( this._offset ).add( this._positionStart );
314
+
315
+ // Apply translation snap
316
+
317
+ if ( this.translationSnap ) {
318
+
319
+ if ( space === 'local' ) {
320
+
321
+ object.position.applyQuaternion( _tempQuaternion.copy( this._quaternionStart ).invert() );
322
+
323
+ if ( axis.search( 'X' ) !== - 1 ) {
324
+
325
+ object.position.x = Math.round( object.position.x / this.translationSnap ) * this.translationSnap;
326
+
327
+ }
328
+
329
+ if ( axis.search( 'Y' ) !== - 1 ) {
330
+
331
+ object.position.y = Math.round( object.position.y / this.translationSnap ) * this.translationSnap;
332
+
333
+ }
334
+
335
+ if ( axis.search( 'Z' ) !== - 1 ) {
336
+
337
+ object.position.z = Math.round( object.position.z / this.translationSnap ) * this.translationSnap;
338
+
339
+ }
340
+
341
+ object.position.applyQuaternion( this._quaternionStart );
342
+
343
+ }
344
+
345
+ if ( space === 'world' ) {
346
+
347
+ if ( object.parent ) {
348
+
349
+ object.position.add( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) );
350
+
351
+ }
352
+
353
+ if ( axis.search( 'X' ) !== - 1 ) {
354
+
355
+ object.position.x = Math.round( object.position.x / this.translationSnap ) * this.translationSnap;
356
+
357
+ }
358
+
359
+ if ( axis.search( 'Y' ) !== - 1 ) {
360
+
361
+ object.position.y = Math.round( object.position.y / this.translationSnap ) * this.translationSnap;
362
+
363
+ }
364
+
365
+ if ( axis.search( 'Z' ) !== - 1 ) {
366
+
367
+ object.position.z = Math.round( object.position.z / this.translationSnap ) * this.translationSnap;
368
+
369
+ }
370
+
371
+ if ( object.parent ) {
372
+
373
+ object.position.sub( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) );
374
+
375
+ }
376
+
377
+ }
378
+
379
+ }
380
+
381
+ object.position.x = Math.max( this.minX, Math.min( this.maxX, object.position.x ) );
382
+ object.position.y = Math.max( this.minY, Math.min( this.maxY, object.position.y ) );
383
+ object.position.z = Math.max( this.minZ, Math.min( this.maxZ, object.position.z ) );
384
+
385
+ } else if ( mode === 'scale' ) {
386
+
387
+ if ( axis.search( 'XYZ' ) !== - 1 ) {
388
+
389
+ let d = this.pointEnd.length() / this.pointStart.length();
390
+
391
+ if ( this.pointEnd.dot( this.pointStart ) < 0 ) d *= - 1;
392
+
393
+ _tempVector2.set( d, d, d );
394
+
395
+ } else {
396
+
397
+ _tempVector.copy( this.pointStart );
398
+ _tempVector2.copy( this.pointEnd );
399
+
400
+ _tempVector.applyQuaternion( this._worldQuaternionInv );
401
+ _tempVector2.applyQuaternion( this._worldQuaternionInv );
402
+
403
+ _tempVector2.divide( _tempVector );
404
+
405
+ if ( axis.search( 'X' ) === - 1 ) {
406
+
407
+ _tempVector2.x = 1;
408
+
409
+ }
410
+
411
+ if ( axis.search( 'Y' ) === - 1 ) {
412
+
413
+ _tempVector2.y = 1;
414
+
415
+ }
416
+
417
+ if ( axis.search( 'Z' ) === - 1 ) {
418
+
419
+ _tempVector2.z = 1;
420
+
421
+ }
422
+
423
+ }
424
+
425
+ // Apply scale
426
+
427
+ object.scale.copy( this._scaleStart ).multiply( _tempVector2 );
428
+
429
+ if ( this.scaleSnap ) {
430
+
431
+ if ( axis.search( 'X' ) !== - 1 ) {
432
+
433
+ object.scale.x = Math.round( object.scale.x / this.scaleSnap ) * this.scaleSnap || this.scaleSnap;
434
+
435
+ }
436
+
437
+ if ( axis.search( 'Y' ) !== - 1 ) {
438
+
439
+ object.scale.y = Math.round( object.scale.y / this.scaleSnap ) * this.scaleSnap || this.scaleSnap;
440
+
441
+ }
442
+
443
+ if ( axis.search( 'Z' ) !== - 1 ) {
444
+
445
+ object.scale.z = Math.round( object.scale.z / this.scaleSnap ) * this.scaleSnap || this.scaleSnap;
446
+
447
+ }
448
+
449
+ }
450
+
451
+ } else if ( mode === 'rotate' ) {
452
+
453
+ this._offset.copy( this.pointEnd ).sub( this.pointStart );
454
+
455
+ const ROTATION_SPEED = 20 / this.worldPosition.distanceTo( _tempVector.setFromMatrixPosition( this.camera.matrixWorld ) );
456
+
457
+ let _inPlaneRotation = false;
458
+
459
+ if ( axis === 'XYZE' ) {
460
+
461
+ this.rotationAxis.copy( this._offset ).cross( this.eye ).normalize();
462
+ this.rotationAngle = this._offset.dot( _tempVector.copy( this.rotationAxis ).cross( this.eye ) ) * ROTATION_SPEED;
463
+
464
+ } else if ( axis === 'X' || axis === 'Y' || axis === 'Z' ) {
465
+
466
+ this.rotationAxis.copy( _unit[ axis ] );
467
+
468
+ _tempVector.copy( _unit[ axis ] );
469
+
470
+ if ( space === 'local' ) {
471
+
472
+ _tempVector.applyQuaternion( this.worldQuaternion );
473
+
474
+ }
475
+
476
+ _tempVector.cross( this.eye );
477
+
478
+ // When _tempVector is 0 after cross with this.eye the vectors are parallel and should use in-plane rotation logic.
479
+ if ( _tempVector.length() === 0 ) {
480
+
481
+ _inPlaneRotation = true;
482
+
483
+ } else {
484
+
485
+ this.rotationAngle = this._offset.dot( _tempVector.normalize() ) * ROTATION_SPEED;
486
+
487
+ }
488
+
489
+
490
+ }
491
+
492
+ if ( axis === 'E' || _inPlaneRotation ) {
493
+
494
+ this.rotationAxis.copy( this.eye );
495
+ this.rotationAngle = this.pointEnd.angleTo( this.pointStart );
496
+
497
+ this._startNorm.copy( this.pointStart ).normalize();
498
+ this._endNorm.copy( this.pointEnd ).normalize();
499
+
500
+ this.rotationAngle *= ( this._endNorm.cross( this._startNorm ).dot( this.eye ) < 0 ? 1 : - 1 );
501
+
502
+ }
503
+
504
+ // Apply rotation snap
505
+
506
+ if ( this.rotationSnap ) this.rotationAngle = Math.round( this.rotationAngle / this.rotationSnap ) * this.rotationSnap;
507
+
508
+ // Apply rotate
509
+ if ( space === 'local' && axis !== 'E' && axis !== 'XYZE' ) {
510
+
511
+ object.quaternion.copy( this._quaternionStart );
512
+ object.quaternion.multiply( _tempQuaternion.setFromAxisAngle( this.rotationAxis, this.rotationAngle ) ).normalize();
513
+
514
+ } else {
515
+
516
+ this.rotationAxis.applyQuaternion( this._parentQuaternionInv );
517
+ object.quaternion.copy( _tempQuaternion.setFromAxisAngle( this.rotationAxis, this.rotationAngle ) );
518
+ object.quaternion.multiply( this._quaternionStart ).normalize();
519
+
520
+ }
521
+
522
+ }
523
+
524
+ this.dispatchEvent( _changeEvent );
525
+ this.dispatchEvent( _objectChangeEvent );
526
+
527
+ }
528
+
529
+ pointerUp( pointer ) {
530
+
531
+ if ( pointer !== null && pointer.button !== 0 ) return;
532
+
533
+ if ( this.dragging && ( this.axis !== null ) ) {
534
+
535
+ _mouseUpEvent.mode = this.mode;
536
+ this.dispatchEvent( _mouseUpEvent );
537
+
538
+ }
539
+
540
+ this.dragging = false;
541
+ this.axis = null;
542
+
543
+ }
544
+
545
+ dispose() {
546
+
547
+ this.disconnect();
548
+
549
+ this._root.dispose();
550
+
551
+ }
552
+
553
+ // Set current object
554
+ attach( object ) {
555
+
556
+ this.object = object;
557
+ this._root.visible = true;
558
+
559
+ return this;
560
+
561
+ }
562
+
563
+ // Detach from object
564
+ detach() {
565
+
566
+ this.object = undefined;
567
+ this.axis = null;
568
+
569
+ this._root.visible = false;
570
+
571
+ return this;
572
+
573
+ }
574
+
575
+ reset() {
576
+
577
+ if ( ! this.enabled ) return;
578
+
579
+ if ( this.dragging ) {
580
+
581
+ this.object.position.copy( this._positionStart );
582
+ this.object.quaternion.copy( this._quaternionStart );
583
+ this.object.scale.copy( this._scaleStart );
584
+
585
+ this.dispatchEvent( _changeEvent );
586
+ this.dispatchEvent( _objectChangeEvent );
587
+
588
+ this.pointStart.copy( this.pointEnd );
589
+
590
+ }
591
+
592
+ }
593
+
594
+ getRaycaster() {
595
+
596
+ return _raycaster;
597
+
598
+ }
599
+
600
+ // TODO: deprecate
601
+
602
+ getMode() {
603
+
604
+ return this.mode;
605
+
606
+ }
607
+
608
+ setMode( mode ) {
609
+
610
+ this.mode = mode;
611
+
612
+ }
613
+
614
+ setTranslationSnap( translationSnap ) {
615
+
616
+ this.translationSnap = translationSnap;
617
+
618
+ }
619
+
620
+ setRotationSnap( rotationSnap ) {
621
+
622
+ this.rotationSnap = rotationSnap;
623
+
624
+ }
625
+
626
+ setScaleSnap( scaleSnap ) {
627
+
628
+ this.scaleSnap = scaleSnap;
629
+
630
+ }
631
+
632
+ setSize( size ) {
633
+
634
+ this.size = size;
635
+
636
+ }
637
+
638
+ setSpace( space ) {
639
+
640
+ this.space = space;
641
+
642
+ }
643
+
644
+ }
645
+
646
+ // mouse / touch event handlers
647
+
648
+ function getPointer( event ) {
649
+
650
+ if ( this.domElement.ownerDocument.pointerLockElement ) {
651
+
652
+ return {
653
+ x: 0,
654
+ y: 0,
655
+ button: event.button
656
+ };
657
+
658
+ } else {
659
+
660
+ const rect = this.domElement.getBoundingClientRect();
661
+
662
+ return {
663
+ x: ( event.clientX - rect.left ) / rect.width * 2 - 1,
664
+ y: - ( event.clientY - rect.top ) / rect.height * 2 + 1,
665
+ button: event.button
666
+ };
667
+
668
+ }
669
+
670
+ }
671
+
672
+ function onPointerHover( event ) {
673
+
674
+ if ( ! this.enabled ) return;
675
+
676
+ switch ( event.pointerType ) {
677
+
678
+ case 'mouse':
679
+ case 'pen':
680
+ this.pointerHover( this._getPointer( event ) );
681
+ break;
682
+
683
+ }
684
+
685
+ }
686
+
687
+ function onPointerDown( event ) {
688
+
689
+ if ( ! this.enabled ) return;
690
+
691
+ if ( ! document.pointerLockElement ) {
692
+
693
+ this.domElement.setPointerCapture( event.pointerId );
694
+
695
+ }
696
+
697
+ this.domElement.addEventListener( 'pointermove', this._onPointerMove );
698
+
699
+ this.pointerHover( this._getPointer( event ) );
700
+ this.pointerDown( this._getPointer( event ) );
701
+
702
+ }
703
+
704
+ function onPointerMove( event ) {
705
+
706
+ if ( ! this.enabled ) return;
707
+
708
+ this.pointerMove( this._getPointer( event ) );
709
+
710
+ }
711
+
712
+ function onPointerUp( event ) {
713
+
714
+ if ( ! this.enabled ) return;
715
+
716
+ this.domElement.releasePointerCapture( event.pointerId );
717
+
718
+ this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
719
+
720
+ this.pointerUp( this._getPointer( event ) );
721
+
722
+ }
723
+
724
+ function intersectObjectWithRay( object, raycaster, includeInvisible ) {
725
+
726
+ const allIntersections = raycaster.intersectObject( object, true );
727
+
728
+ for ( let i = 0; i < allIntersections.length; i ++ ) {
729
+
730
+ if ( allIntersections[ i ].object.visible || includeInvisible ) {
731
+
732
+ return allIntersections[ i ];
733
+
734
+ }
735
+
736
+ }
737
+
738
+ return false;
739
+
740
+ }
741
+
742
+ //
743
+
744
+ // Reusable utility variables
745
+
746
+ const _tempEuler = new Euler();
747
+ const _alignVector = new Vector3( 0, 1, 0 );
748
+ const _zeroVector = new Vector3( 0, 0, 0 );
749
+ const _lookAtMatrix = new Matrix4();
750
+ const _tempQuaternion2 = new Quaternion();
751
+ const _identityQuaternion = new Quaternion();
752
+ const _dirVector = new Vector3();
753
+ const _tempMatrix = new Matrix4();
754
+
755
+ const _unitX = new Vector3( 1, 0, 0 );
756
+ const _unitY = new Vector3( 0, 1, 0 );
757
+ const _unitZ = new Vector3( 0, 0, 1 );
758
+
759
+ const _v1 = new Vector3();
760
+ const _v2 = new Vector3();
761
+ const _v3 = new Vector3();
762
+
763
+ class TransformControlsRoot extends Object3D {
764
+
765
+ constructor( controls ) {
766
+
767
+ super();
768
+
769
+ this.isTransformControlsRoot = true;
770
+
771
+ this.controls = controls;
772
+ this.visible = false;
773
+
774
+ }
775
+
776
+ // updateMatrixWorld updates key transformation variables
777
+ updateMatrixWorld( force ) {
778
+
779
+ const controls = this.controls;
780
+
781
+ if ( controls.object !== undefined ) {
782
+
783
+ controls.object.updateMatrixWorld();
784
+
785
+ if ( controls.object.parent === null ) {
786
+
787
+ console.error( 'TransformControls: The attached 3D object must be a part of the scene graph.' );
788
+
789
+ } else {
790
+
791
+ controls.object.parent.matrixWorld.decompose( controls._parentPosition, controls._parentQuaternion, controls._parentScale );
792
+
793
+ }
794
+
795
+ controls.object.matrixWorld.decompose( controls.worldPosition, controls.worldQuaternion, controls._worldScale );
796
+
797
+ controls._parentQuaternionInv.copy( controls._parentQuaternion ).invert();
798
+ controls._worldQuaternionInv.copy( controls.worldQuaternion ).invert();
799
+
800
+ }
801
+
802
+ controls.camera.updateMatrixWorld();
803
+ controls.camera.matrixWorld.decompose( controls.cameraPosition, controls.cameraQuaternion, controls._cameraScale );
804
+
805
+ if ( controls.camera.isOrthographicCamera ) {
806
+
807
+ controls.camera.getWorldDirection( controls.eye ).negate();
808
+
809
+ } else {
810
+
811
+ controls.eye.copy( controls.cameraPosition ).sub( controls.worldPosition ).normalize();
812
+
813
+ }
814
+
815
+ super.updateMatrixWorld( force );
816
+
817
+ }
818
+
819
+ dispose() {
820
+
821
+ this.traverse( function ( child ) {
822
+
823
+ if ( child.geometry ) child.geometry.dispose();
824
+ if ( child.material ) child.material.dispose();
825
+
826
+ } );
827
+
828
+ }
829
+
830
+ }
831
+
832
+ class TransformControlsGizmo extends Object3D {
833
+
834
+ constructor() {
835
+
836
+ super();
837
+
838
+ this.isTransformControlsGizmo = true;
839
+
840
+ this.type = 'TransformControlsGizmo';
841
+
842
+ // shared materials
843
+
844
+ const gizmoMaterial = new MeshBasicMaterial( {
845
+ depthTest: false,
846
+ depthWrite: false,
847
+ fog: false,
848
+ toneMapped: false,
849
+ transparent: true
850
+ } );
851
+
852
+ const gizmoLineMaterial = new LineBasicMaterial( {
853
+ depthTest: false,
854
+ depthWrite: false,
855
+ fog: false,
856
+ toneMapped: false,
857
+ transparent: true
858
+ } );
859
+
860
+ // Make unique material for each axis/color
861
+
862
+ const matInvisible = gizmoMaterial.clone();
863
+ matInvisible.opacity = 0.15;
864
+
865
+ const matHelper = gizmoLineMaterial.clone();
866
+ matHelper.opacity = 0.5;
867
+
868
+ const matRed = gizmoMaterial.clone();
869
+ matRed.color.setHex( 0xff0000 );
870
+
871
+ const matGreen = gizmoMaterial.clone();
872
+ matGreen.color.setHex( 0x00ff00 );
873
+
874
+ const matBlue = gizmoMaterial.clone();
875
+ matBlue.color.setHex( 0x0000ff );
876
+
877
+ const matRedTransparent = gizmoMaterial.clone();
878
+ matRedTransparent.color.setHex( 0xff0000 );
879
+ matRedTransparent.opacity = 0.5;
880
+
881
+ const matGreenTransparent = gizmoMaterial.clone();
882
+ matGreenTransparent.color.setHex( 0x00ff00 );
883
+ matGreenTransparent.opacity = 0.5;
884
+
885
+ const matBlueTransparent = gizmoMaterial.clone();
886
+ matBlueTransparent.color.setHex( 0x0000ff );
887
+ matBlueTransparent.opacity = 0.5;
888
+
889
+ const matWhiteTransparent = gizmoMaterial.clone();
890
+ matWhiteTransparent.opacity = 0.25;
891
+
892
+ const matYellowTransparent = gizmoMaterial.clone();
893
+ matYellowTransparent.color.setHex( 0xffff00 );
894
+ matYellowTransparent.opacity = 0.25;
895
+
896
+ const matYellow = gizmoMaterial.clone();
897
+ matYellow.color.setHex( 0xffff00 );
898
+
899
+ const matGray = gizmoMaterial.clone();
900
+ matGray.color.setHex( 0x787878 );
901
+
902
+ // reusable geometry
903
+
904
+ const arrowGeometry = new CylinderGeometry( 0, 0.04, 0.1, 12 );
905
+ arrowGeometry.translate( 0, 0.05, 0 );
906
+
907
+ const scaleHandleGeometry = new BoxGeometry( 0.08, 0.08, 0.08 );
908
+ scaleHandleGeometry.translate( 0, 0.04, 0 );
909
+
910
+ const lineGeometry = new BufferGeometry();
911
+ lineGeometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 1, 0, 0 ], 3 ) );
912
+
913
+ const lineGeometry2 = new CylinderGeometry( 0.0075, 0.0075, 0.5, 3 );
914
+ lineGeometry2.translate( 0, 0.25, 0 );
915
+
916
+ function CircleGeometry( radius, arc ) {
917
+
918
+ const geometry = new TorusGeometry( radius, 0.0075, 3, 64, arc * Math.PI * 2 );
919
+ geometry.rotateY( Math.PI / 2 );
920
+ geometry.rotateX( Math.PI / 2 );
921
+ return geometry;
922
+
923
+ }
924
+
925
+ // Special geometry for transform helper. If scaled with position vector it spans from [0,0,0] to position
926
+
927
+ function TranslateHelperGeometry() {
928
+
929
+ const geometry = new BufferGeometry();
930
+
931
+ geometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 1, 1, 1 ], 3 ) );
932
+
933
+ return geometry;
934
+
935
+ }
936
+
937
+ // Gizmo definitions - custom hierarchy definitions for setupGizmo() function
938
+
939
+ const gizmoTranslate = {
940
+ X: [
941
+ [ new Mesh( arrowGeometry, matRed ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ]],
942
+ [ new Mesh( arrowGeometry, matRed ), [ - 0.5, 0, 0 ], [ 0, 0, Math.PI / 2 ]],
943
+ [ new Mesh( lineGeometry2, matRed ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ]]
944
+ ],
945
+ Y: [
946
+ [ new Mesh( arrowGeometry, matGreen ), [ 0, 0.5, 0 ]],
947
+ [ new Mesh( arrowGeometry, matGreen ), [ 0, - 0.5, 0 ], [ Math.PI, 0, 0 ]],
948
+ [ new Mesh( lineGeometry2, matGreen ) ]
949
+ ],
950
+ Z: [
951
+ [ new Mesh( arrowGeometry, matBlue ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ]],
952
+ [ new Mesh( arrowGeometry, matBlue ), [ 0, 0, - 0.5 ], [ - Math.PI / 2, 0, 0 ]],
953
+ [ new Mesh( lineGeometry2, matBlue ), null, [ Math.PI / 2, 0, 0 ]]
954
+ ],
955
+ XYZ: [
956
+ [ new Mesh( new OctahedronGeometry( 0.1, 0 ), matWhiteTransparent.clone() ), [ 0, 0, 0 ]]
957
+ ],
958
+ XY: [
959
+ [ new Mesh( new BoxGeometry( 0.15, 0.15, 0.01 ), matBlueTransparent.clone() ), [ 0.15, 0.15, 0 ]]
960
+ ],
961
+ YZ: [
962
+ [ new Mesh( new BoxGeometry( 0.15, 0.15, 0.01 ), matRedTransparent.clone() ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ]]
963
+ ],
964
+ XZ: [
965
+ [ new Mesh( new BoxGeometry( 0.15, 0.15, 0.01 ), matGreenTransparent.clone() ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ]]
966
+ ]
967
+ };
968
+
969
+ const pickerTranslate = {
970
+ X: [
971
+ [ new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0.3, 0, 0 ], [ 0, 0, - Math.PI / 2 ]],
972
+ [ new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ - 0.3, 0, 0 ], [ 0, 0, Math.PI / 2 ]]
973
+ ],
974
+ Y: [
975
+ [ new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, 0.3, 0 ]],
976
+ [ new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, - 0.3, 0 ], [ 0, 0, Math.PI ]]
977
+ ],
978
+ Z: [
979
+ [ new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, 0, 0.3 ], [ Math.PI / 2, 0, 0 ]],
980
+ [ new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, 0, - 0.3 ], [ - Math.PI / 2, 0, 0 ]]
981
+ ],
982
+ XYZ: [
983
+ [ new Mesh( new OctahedronGeometry( 0.2, 0 ), matInvisible ) ]
984
+ ],
985
+ XY: [
986
+ [ new Mesh( new BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), [ 0.15, 0.15, 0 ]]
987
+ ],
988
+ YZ: [
989
+ [ new Mesh( new BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ]]
990
+ ],
991
+ XZ: [
992
+ [ new Mesh( new BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ]]
993
+ ]
994
+ };
995
+
996
+ const helperTranslate = {
997
+ START: [
998
+ [ new Mesh( new OctahedronGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ]
999
+ ],
1000
+ END: [
1001
+ [ new Mesh( new OctahedronGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ]
1002
+ ],
1003
+ DELTA: [
1004
+ [ new Line( TranslateHelperGeometry(), matHelper ), null, null, null, 'helper' ]
1005
+ ],
1006
+ X: [
1007
+ [ new Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]
1008
+ ],
1009
+ Y: [
1010
+ [ new Line( lineGeometry, matHelper.clone() ), [ 0, - 1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ]
1011
+ ],
1012
+ Z: [
1013
+ [ new Line( lineGeometry, matHelper.clone() ), [ 0, 0, - 1e3 ], [ 0, - Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ]
1014
+ ]
1015
+ };
1016
+
1017
+ const gizmoRotate = {
1018
+ XYZE: [
1019
+ [ new Mesh( CircleGeometry( 0.5, 1 ), matGray ), null, [ 0, Math.PI / 2, 0 ]]
1020
+ ],
1021
+ X: [
1022
+ [ new Mesh( CircleGeometry( 0.5, 0.5 ), matRed ) ]
1023
+ ],
1024
+ Y: [
1025
+ [ new Mesh( CircleGeometry( 0.5, 0.5 ), matGreen ), null, [ 0, 0, - Math.PI / 2 ]]
1026
+ ],
1027
+ Z: [
1028
+ [ new Mesh( CircleGeometry( 0.5, 0.5 ), matBlue ), null, [ 0, Math.PI / 2, 0 ]]
1029
+ ],
1030
+ E: [
1031
+ [ new Mesh( CircleGeometry( 0.75, 1 ), matYellowTransparent ), null, [ 0, Math.PI / 2, 0 ]]
1032
+ ]
1033
+ };
1034
+
1035
+ const helperRotate = {
1036
+ AXIS: [
1037
+ [ new Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]
1038
+ ]
1039
+ };
1040
+
1041
+ const pickerRotate = {
1042
+ XYZE: [
1043
+ [ new Mesh( new SphereGeometry( 0.25, 10, 8 ), matInvisible ) ]
1044
+ ],
1045
+ X: [
1046
+ [ new Mesh( new TorusGeometry( 0.5, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, - Math.PI / 2, - Math.PI / 2 ]],
1047
+ ],
1048
+ Y: [
1049
+ [ new Mesh( new TorusGeometry( 0.5, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ]],
1050
+ ],
1051
+ Z: [
1052
+ [ new Mesh( new TorusGeometry( 0.5, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ]],
1053
+ ],
1054
+ E: [
1055
+ [ new Mesh( new TorusGeometry( 0.75, 0.1, 2, 24 ), matInvisible ) ]
1056
+ ]
1057
+ };
1058
+
1059
+ const gizmoScale = {
1060
+ X: [
1061
+ [ new Mesh( scaleHandleGeometry, matRed ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ]],
1062
+ [ new Mesh( lineGeometry2, matRed ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ]],
1063
+ [ new Mesh( scaleHandleGeometry, matRed ), [ - 0.5, 0, 0 ], [ 0, 0, Math.PI / 2 ]],
1064
+ ],
1065
+ Y: [
1066
+ [ new Mesh( scaleHandleGeometry, matGreen ), [ 0, 0.5, 0 ]],
1067
+ [ new Mesh( lineGeometry2, matGreen ) ],
1068
+ [ new Mesh( scaleHandleGeometry, matGreen ), [ 0, - 0.5, 0 ], [ 0, 0, Math.PI ]],
1069
+ ],
1070
+ Z: [
1071
+ [ new Mesh( scaleHandleGeometry, matBlue ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ]],
1072
+ [ new Mesh( lineGeometry2, matBlue ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ]],
1073
+ [ new Mesh( scaleHandleGeometry, matBlue ), [ 0, 0, - 0.5 ], [ - Math.PI / 2, 0, 0 ]]
1074
+ ],
1075
+ XY: [
1076
+ [ new Mesh( new BoxGeometry( 0.15, 0.15, 0.01 ), matBlueTransparent ), [ 0.15, 0.15, 0 ]]
1077
+ ],
1078
+ YZ: [
1079
+ [ new Mesh( new BoxGeometry( 0.15, 0.15, 0.01 ), matRedTransparent ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ]]
1080
+ ],
1081
+ XZ: [
1082
+ [ new Mesh( new BoxGeometry( 0.15, 0.15, 0.01 ), matGreenTransparent ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ]]
1083
+ ],
1084
+ XYZ: [
1085
+ [ new Mesh( new BoxGeometry( 0.1, 0.1, 0.1 ), matWhiteTransparent.clone() ) ],
1086
+ ]
1087
+ };
1088
+
1089
+ const pickerScale = {
1090
+ X: [
1091
+ [ new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0.3, 0, 0 ], [ 0, 0, - Math.PI / 2 ]],
1092
+ [ new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ - 0.3, 0, 0 ], [ 0, 0, Math.PI / 2 ]]
1093
+ ],
1094
+ Y: [
1095
+ [ new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, 0.3, 0 ]],
1096
+ [ new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, - 0.3, 0 ], [ 0, 0, Math.PI ]]
1097
+ ],
1098
+ Z: [
1099
+ [ new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, 0, 0.3 ], [ Math.PI / 2, 0, 0 ]],
1100
+ [ new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, 0, - 0.3 ], [ - Math.PI / 2, 0, 0 ]]
1101
+ ],
1102
+ XY: [
1103
+ [ new Mesh( new BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), [ 0.15, 0.15, 0 ]],
1104
+ ],
1105
+ YZ: [
1106
+ [ new Mesh( new BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ]],
1107
+ ],
1108
+ XZ: [
1109
+ [ new Mesh( new BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ]],
1110
+ ],
1111
+ XYZ: [
1112
+ [ new Mesh( new BoxGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 0, 0 ]],
1113
+ ]
1114
+ };
1115
+
1116
+ const helperScale = {
1117
+ X: [
1118
+ [ new Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]
1119
+ ],
1120
+ Y: [
1121
+ [ new Line( lineGeometry, matHelper.clone() ), [ 0, - 1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ]
1122
+ ],
1123
+ Z: [
1124
+ [ new Line( lineGeometry, matHelper.clone() ), [ 0, 0, - 1e3 ], [ 0, - Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ]
1125
+ ]
1126
+ };
1127
+
1128
+ // Creates an Object3D with gizmos described in custom hierarchy definition.
1129
+
1130
+ function setupGizmo( gizmoMap ) {
1131
+
1132
+ const gizmo = new Object3D();
1133
+
1134
+ for ( const name in gizmoMap ) {
1135
+
1136
+ for ( let i = gizmoMap[ name ].length; i --; ) {
1137
+
1138
+ const object = gizmoMap[ name ][ i ][ 0 ].clone();
1139
+ const position = gizmoMap[ name ][ i ][ 1 ];
1140
+ const rotation = gizmoMap[ name ][ i ][ 2 ];
1141
+ const scale = gizmoMap[ name ][ i ][ 3 ];
1142
+ const tag = gizmoMap[ name ][ i ][ 4 ];
1143
+
1144
+ // name and tag properties are essential for picking and updating logic.
1145
+ object.name = name;
1146
+ object.tag = tag;
1147
+
1148
+ if ( position ) {
1149
+
1150
+ object.position.set( position[ 0 ], position[ 1 ], position[ 2 ] );
1151
+
1152
+ }
1153
+
1154
+ if ( rotation ) {
1155
+
1156
+ object.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ] );
1157
+
1158
+ }
1159
+
1160
+ if ( scale ) {
1161
+
1162
+ object.scale.set( scale[ 0 ], scale[ 1 ], scale[ 2 ] );
1163
+
1164
+ }
1165
+
1166
+ object.updateMatrix();
1167
+
1168
+ const tempGeometry = object.geometry.clone();
1169
+ tempGeometry.applyMatrix4( object.matrix );
1170
+ object.geometry = tempGeometry;
1171
+ object.renderOrder = Infinity;
1172
+
1173
+ object.position.set( 0, 0, 0 );
1174
+ object.rotation.set( 0, 0, 0 );
1175
+ object.scale.set( 1, 1, 1 );
1176
+
1177
+ gizmo.add( object );
1178
+
1179
+ }
1180
+
1181
+ }
1182
+
1183
+ return gizmo;
1184
+
1185
+ }
1186
+
1187
+ // Gizmo creation
1188
+
1189
+ this.gizmo = {};
1190
+ this.picker = {};
1191
+ this.helper = {};
1192
+
1193
+ this.add( this.gizmo[ 'translate' ] = setupGizmo( gizmoTranslate ) );
1194
+ this.add( this.gizmo[ 'rotate' ] = setupGizmo( gizmoRotate ) );
1195
+ this.add( this.gizmo[ 'scale' ] = setupGizmo( gizmoScale ) );
1196
+ this.add( this.picker[ 'translate' ] = setupGizmo( pickerTranslate ) );
1197
+ this.add( this.picker[ 'rotate' ] = setupGizmo( pickerRotate ) );
1198
+ this.add( this.picker[ 'scale' ] = setupGizmo( pickerScale ) );
1199
+ this.add( this.helper[ 'translate' ] = setupGizmo( helperTranslate ) );
1200
+ this.add( this.helper[ 'rotate' ] = setupGizmo( helperRotate ) );
1201
+ this.add( this.helper[ 'scale' ] = setupGizmo( helperScale ) );
1202
+
1203
+ // Pickers should be hidden always
1204
+
1205
+ this.picker[ 'translate' ].visible = false;
1206
+ this.picker[ 'rotate' ].visible = false;
1207
+ this.picker[ 'scale' ].visible = false;
1208
+
1209
+ }
1210
+
1211
+ // updateMatrixWorld will update transformations and appearance of individual handles
1212
+
1213
+ updateMatrixWorld( force ) {
1214
+
1215
+ const space = ( this.mode === 'scale' ) ? 'local' : this.space; // scale always oriented to local rotation
1216
+
1217
+ const quaternion = ( space === 'local' ) ? this.worldQuaternion : _identityQuaternion;
1218
+
1219
+ // Show only gizmos for current transform mode
1220
+
1221
+ this.gizmo[ 'translate' ].visible = this.mode === 'translate';
1222
+ this.gizmo[ 'rotate' ].visible = this.mode === 'rotate';
1223
+ this.gizmo[ 'scale' ].visible = this.mode === 'scale';
1224
+
1225
+ this.helper[ 'translate' ].visible = this.mode === 'translate';
1226
+ this.helper[ 'rotate' ].visible = this.mode === 'rotate';
1227
+ this.helper[ 'scale' ].visible = this.mode === 'scale';
1228
+
1229
+
1230
+ let handles = [];
1231
+ handles = handles.concat( this.picker[ this.mode ].children );
1232
+ handles = handles.concat( this.gizmo[ this.mode ].children );
1233
+ handles = handles.concat( this.helper[ this.mode ].children );
1234
+
1235
+ for ( let i = 0; i < handles.length; i ++ ) {
1236
+
1237
+ const handle = handles[ i ];
1238
+
1239
+ // hide aligned to camera
1240
+
1241
+ handle.visible = true;
1242
+ handle.rotation.set( 0, 0, 0 );
1243
+ handle.position.copy( this.worldPosition );
1244
+
1245
+ let factor;
1246
+
1247
+ if ( this.camera.isOrthographicCamera ) {
1248
+
1249
+ factor = ( this.camera.top - this.camera.bottom ) / this.camera.zoom;
1250
+
1251
+ } else {
1252
+
1253
+ factor = this.worldPosition.distanceTo( this.cameraPosition ) * Math.min( 1.9 * Math.tan( Math.PI * this.camera.fov / 360 ) / this.camera.zoom, 7 );
1254
+
1255
+ }
1256
+
1257
+ handle.scale.set( 1, 1, 1 ).multiplyScalar( factor * this.size / 4 );
1258
+
1259
+ // TODO: simplify helpers and consider decoupling from gizmo
1260
+
1261
+ if ( handle.tag === 'helper' ) {
1262
+
1263
+ handle.visible = false;
1264
+
1265
+ if ( handle.name === 'AXIS' ) {
1266
+
1267
+ handle.visible = !! this.axis;
1268
+
1269
+ if ( this.axis === 'X' ) {
1270
+
1271
+ _tempQuaternion.setFromEuler( _tempEuler.set( 0, 0, 0 ) );
1272
+ handle.quaternion.copy( quaternion ).multiply( _tempQuaternion );
1273
+
1274
+ if ( Math.abs( _alignVector.copy( _unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
1275
+
1276
+ handle.visible = false;
1277
+
1278
+ }
1279
+
1280
+ }
1281
+
1282
+ if ( this.axis === 'Y' ) {
1283
+
1284
+ _tempQuaternion.setFromEuler( _tempEuler.set( 0, 0, Math.PI / 2 ) );
1285
+ handle.quaternion.copy( quaternion ).multiply( _tempQuaternion );
1286
+
1287
+ if ( Math.abs( _alignVector.copy( _unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
1288
+
1289
+ handle.visible = false;
1290
+
1291
+ }
1292
+
1293
+ }
1294
+
1295
+ if ( this.axis === 'Z' ) {
1296
+
1297
+ _tempQuaternion.setFromEuler( _tempEuler.set( 0, Math.PI / 2, 0 ) );
1298
+ handle.quaternion.copy( quaternion ).multiply( _tempQuaternion );
1299
+
1300
+ if ( Math.abs( _alignVector.copy( _unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
1301
+
1302
+ handle.visible = false;
1303
+
1304
+ }
1305
+
1306
+ }
1307
+
1308
+ if ( this.axis === 'XYZE' ) {
1309
+
1310
+ _tempQuaternion.setFromEuler( _tempEuler.set( 0, Math.PI / 2, 0 ) );
1311
+ _alignVector.copy( this.rotationAxis );
1312
+ handle.quaternion.setFromRotationMatrix( _lookAtMatrix.lookAt( _zeroVector, _alignVector, _unitY ) );
1313
+ handle.quaternion.multiply( _tempQuaternion );
1314
+ handle.visible = this.dragging;
1315
+
1316
+ }
1317
+
1318
+ if ( this.axis === 'E' ) {
1319
+
1320
+ handle.visible = false;
1321
+
1322
+ }
1323
+
1324
+
1325
+ } else if ( handle.name === 'START' ) {
1326
+
1327
+ handle.position.copy( this.worldPositionStart );
1328
+ handle.visible = this.dragging;
1329
+
1330
+ } else if ( handle.name === 'END' ) {
1331
+
1332
+ handle.position.copy( this.worldPosition );
1333
+ handle.visible = this.dragging;
1334
+
1335
+ } else if ( handle.name === 'DELTA' ) {
1336
+
1337
+ handle.position.copy( this.worldPositionStart );
1338
+ handle.quaternion.copy( this.worldQuaternionStart );
1339
+ _tempVector.set( 1e-10, 1e-10, 1e-10 ).add( this.worldPositionStart ).sub( this.worldPosition ).multiplyScalar( - 1 );
1340
+ _tempVector.applyQuaternion( this.worldQuaternionStart.clone().invert() );
1341
+ handle.scale.copy( _tempVector );
1342
+ handle.visible = this.dragging;
1343
+
1344
+ } else {
1345
+
1346
+ handle.quaternion.copy( quaternion );
1347
+
1348
+ if ( this.dragging ) {
1349
+
1350
+ handle.position.copy( this.worldPositionStart );
1351
+
1352
+ } else {
1353
+
1354
+ handle.position.copy( this.worldPosition );
1355
+
1356
+ }
1357
+
1358
+ if ( this.axis ) {
1359
+
1360
+ handle.visible = this.axis.search( handle.name ) !== - 1;
1361
+
1362
+ }
1363
+
1364
+ }
1365
+
1366
+ // If updating helper, skip rest of the loop
1367
+ continue;
1368
+
1369
+ }
1370
+
1371
+ // Align handles to current local or world rotation
1372
+
1373
+ handle.quaternion.copy( quaternion );
1374
+
1375
+ if ( this.mode === 'translate' || this.mode === 'scale' ) {
1376
+
1377
+ // Hide translate and scale axis facing the camera
1378
+
1379
+ const AXIS_HIDE_THRESHOLD = 0.99;
1380
+ const PLANE_HIDE_THRESHOLD = 0.2;
1381
+
1382
+ if ( handle.name === 'X' ) {
1383
+
1384
+ if ( Math.abs( _alignVector.copy( _unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_THRESHOLD ) {
1385
+
1386
+ handle.scale.set( 1e-10, 1e-10, 1e-10 );
1387
+ handle.visible = false;
1388
+
1389
+ }
1390
+
1391
+ }
1392
+
1393
+ if ( handle.name === 'Y' ) {
1394
+
1395
+ if ( Math.abs( _alignVector.copy( _unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_THRESHOLD ) {
1396
+
1397
+ handle.scale.set( 1e-10, 1e-10, 1e-10 );
1398
+ handle.visible = false;
1399
+
1400
+ }
1401
+
1402
+ }
1403
+
1404
+ if ( handle.name === 'Z' ) {
1405
+
1406
+ if ( Math.abs( _alignVector.copy( _unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_THRESHOLD ) {
1407
+
1408
+ handle.scale.set( 1e-10, 1e-10, 1e-10 );
1409
+ handle.visible = false;
1410
+
1411
+ }
1412
+
1413
+ }
1414
+
1415
+ if ( handle.name === 'XY' ) {
1416
+
1417
+ if ( Math.abs( _alignVector.copy( _unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_THRESHOLD ) {
1418
+
1419
+ handle.scale.set( 1e-10, 1e-10, 1e-10 );
1420
+ handle.visible = false;
1421
+
1422
+ }
1423
+
1424
+ }
1425
+
1426
+ if ( handle.name === 'YZ' ) {
1427
+
1428
+ if ( Math.abs( _alignVector.copy( _unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_THRESHOLD ) {
1429
+
1430
+ handle.scale.set( 1e-10, 1e-10, 1e-10 );
1431
+ handle.visible = false;
1432
+
1433
+ }
1434
+
1435
+ }
1436
+
1437
+ if ( handle.name === 'XZ' ) {
1438
+
1439
+ if ( Math.abs( _alignVector.copy( _unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_THRESHOLD ) {
1440
+
1441
+ handle.scale.set( 1e-10, 1e-10, 1e-10 );
1442
+ handle.visible = false;
1443
+
1444
+ }
1445
+
1446
+ }
1447
+
1448
+ } else if ( this.mode === 'rotate' ) {
1449
+
1450
+ // Align handles to current local or world rotation
1451
+
1452
+ _tempQuaternion2.copy( quaternion );
1453
+ _alignVector.copy( this.eye ).applyQuaternion( _tempQuaternion.copy( quaternion ).invert() );
1454
+
1455
+ if ( handle.name.search( 'E' ) !== - 1 ) {
1456
+
1457
+ handle.quaternion.setFromRotationMatrix( _lookAtMatrix.lookAt( this.eye, _zeroVector, _unitY ) );
1458
+
1459
+ }
1460
+
1461
+ if ( handle.name === 'X' ) {
1462
+
1463
+ _tempQuaternion.setFromAxisAngle( _unitX, Math.atan2( - _alignVector.y, _alignVector.z ) );
1464
+ _tempQuaternion.multiplyQuaternions( _tempQuaternion2, _tempQuaternion );
1465
+ handle.quaternion.copy( _tempQuaternion );
1466
+
1467
+ }
1468
+
1469
+ if ( handle.name === 'Y' ) {
1470
+
1471
+ _tempQuaternion.setFromAxisAngle( _unitY, Math.atan2( _alignVector.x, _alignVector.z ) );
1472
+ _tempQuaternion.multiplyQuaternions( _tempQuaternion2, _tempQuaternion );
1473
+ handle.quaternion.copy( _tempQuaternion );
1474
+
1475
+ }
1476
+
1477
+ if ( handle.name === 'Z' ) {
1478
+
1479
+ _tempQuaternion.setFromAxisAngle( _unitZ, Math.atan2( _alignVector.y, _alignVector.x ) );
1480
+ _tempQuaternion.multiplyQuaternions( _tempQuaternion2, _tempQuaternion );
1481
+ handle.quaternion.copy( _tempQuaternion );
1482
+
1483
+ }
1484
+
1485
+ }
1486
+
1487
+ // Hide disabled axes
1488
+ handle.visible = handle.visible && ( handle.name.indexOf( 'X' ) === - 1 || this.showX );
1489
+ handle.visible = handle.visible && ( handle.name.indexOf( 'Y' ) === - 1 || this.showY );
1490
+ handle.visible = handle.visible && ( handle.name.indexOf( 'Z' ) === - 1 || this.showZ );
1491
+ handle.visible = handle.visible && ( handle.name.indexOf( 'E' ) === - 1 || ( this.showX && this.showY && this.showZ ) );
1492
+
1493
+ // highlight selected axis
1494
+
1495
+ handle.material._color = handle.material._color || handle.material.color.clone();
1496
+ handle.material._opacity = handle.material._opacity || handle.material.opacity;
1497
+
1498
+ handle.material.color.copy( handle.material._color );
1499
+ handle.material.opacity = handle.material._opacity;
1500
+
1501
+ if ( this.enabled && this.axis ) {
1502
+
1503
+ if ( handle.name === this.axis ) {
1504
+
1505
+ handle.material.color.setHex( 0xffff00 );
1506
+ handle.material.opacity = 1.0;
1507
+
1508
+ } else if ( this.axis.split( '' ).some( function ( a ) {
1509
+
1510
+ return handle.name === a;
1511
+
1512
+ } ) ) {
1513
+
1514
+ handle.material.color.setHex( 0xffff00 );
1515
+ handle.material.opacity = 1.0;
1516
+
1517
+ }
1518
+
1519
+ }
1520
+
1521
+ }
1522
+
1523
+ super.updateMatrixWorld( force );
1524
+
1525
+ }
1526
+
1527
+ }
1528
+
1529
+ //
1530
+
1531
+ class TransformControlsPlane extends Mesh {
1532
+
1533
+ constructor() {
1534
+
1535
+ super(
1536
+ new PlaneGeometry( 100000, 100000, 2, 2 ),
1537
+ new MeshBasicMaterial( { visible: false, wireframe: true, side: DoubleSide, transparent: true, opacity: 0.1, toneMapped: false } )
1538
+ );
1539
+
1540
+ this.isTransformControlsPlane = true;
1541
+
1542
+ this.type = 'TransformControlsPlane';
1543
+
1544
+ }
1545
+
1546
+ updateMatrixWorld( force ) {
1547
+
1548
+ let space = this.space;
1549
+
1550
+ this.position.copy( this.worldPosition );
1551
+
1552
+ if ( this.mode === 'scale' ) space = 'local'; // scale always oriented to local rotation
1553
+
1554
+ _v1.copy( _unitX ).applyQuaternion( space === 'local' ? this.worldQuaternion : _identityQuaternion );
1555
+ _v2.copy( _unitY ).applyQuaternion( space === 'local' ? this.worldQuaternion : _identityQuaternion );
1556
+ _v3.copy( _unitZ ).applyQuaternion( space === 'local' ? this.worldQuaternion : _identityQuaternion );
1557
+
1558
+ // Align the plane for current transform mode, axis and space.
1559
+
1560
+ _alignVector.copy( _v2 );
1561
+
1562
+ switch ( this.mode ) {
1563
+
1564
+ case 'translate':
1565
+ case 'scale':
1566
+ switch ( this.axis ) {
1567
+
1568
+ case 'X':
1569
+ _alignVector.copy( this.eye ).cross( _v1 );
1570
+ _dirVector.copy( _v1 ).cross( _alignVector );
1571
+ break;
1572
+ case 'Y':
1573
+ _alignVector.copy( this.eye ).cross( _v2 );
1574
+ _dirVector.copy( _v2 ).cross( _alignVector );
1575
+ break;
1576
+ case 'Z':
1577
+ _alignVector.copy( this.eye ).cross( _v3 );
1578
+ _dirVector.copy( _v3 ).cross( _alignVector );
1579
+ break;
1580
+ case 'XY':
1581
+ _dirVector.copy( _v3 );
1582
+ break;
1583
+ case 'YZ':
1584
+ _dirVector.copy( _v1 );
1585
+ break;
1586
+ case 'XZ':
1587
+ _alignVector.copy( _v3 );
1588
+ _dirVector.copy( _v2 );
1589
+ break;
1590
+ case 'XYZ':
1591
+ case 'E':
1592
+ _dirVector.set( 0, 0, 0 );
1593
+ break;
1594
+
1595
+ }
1596
+
1597
+ break;
1598
+ case 'rotate':
1599
+ default:
1600
+ // special case for rotate
1601
+ _dirVector.set( 0, 0, 0 );
1602
+
1603
+ }
1604
+
1605
+ if ( _dirVector.length() === 0 ) {
1606
+
1607
+ // If in rotate mode, make the plane parallel to camera
1608
+ this.quaternion.copy( this.cameraQuaternion );
1609
+
1610
+ } else {
1611
+
1612
+ _tempMatrix.lookAt( _tempVector.set( 0, 0, 0 ), _dirVector, _alignVector );
1613
+
1614
+ this.quaternion.setFromRotationMatrix( _tempMatrix );
1615
+
1616
+ }
1617
+
1618
+ super.updateMatrixWorld( force );
1619
+
1620
+ }
1621
+
1622
+ }
1623
+
1624
+ export { TransformControls, TransformControlsGizmo, TransformControlsPlane };
libs/three.js/0.172.0/jsm/csm/CSM.js ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Vector2,
3
+ Vector3,
4
+ DirectionalLight,
5
+ MathUtils,
6
+ ShaderChunk,
7
+ Matrix4,
8
+ Box3
9
+ } from 'three';
10
+ import { CSMFrustum } from './CSMFrustum.js';
11
+ import { CSMShader } from './CSMShader.js';
12
+
13
+ const _cameraToLightMatrix = new Matrix4();
14
+ const _lightSpaceFrustum = new CSMFrustum( { webGL: true } );
15
+ const _center = new Vector3();
16
+ const _bbox = new Box3();
17
+ const _uniformArray = [];
18
+ const _logArray = [];
19
+ const _lightOrientationMatrix = new Matrix4();
20
+ const _lightOrientationMatrixInverse = new Matrix4();
21
+ const _up = new Vector3( 0, 1, 0 );
22
+
23
+ export class CSM {
24
+
25
+ constructor( data ) {
26
+
27
+ this.camera = data.camera;
28
+ this.parent = data.parent;
29
+ this.cascades = data.cascades || 3;
30
+ this.maxFar = data.maxFar || 100000;
31
+ this.mode = data.mode || 'practical';
32
+ this.shadowMapSize = data.shadowMapSize || 2048;
33
+ this.shadowBias = data.shadowBias || 0.000001;
34
+ this.lightDirection = data.lightDirection || new Vector3( 1, - 1, 1 ).normalize();
35
+ this.lightIntensity = data.lightIntensity || 3;
36
+ this.lightNear = data.lightNear || 1;
37
+ this.lightFar = data.lightFar || 2000;
38
+ this.lightMargin = data.lightMargin || 200;
39
+ this.customSplitsCallback = data.customSplitsCallback;
40
+ this.fade = false;
41
+ this.mainFrustum = new CSMFrustum( { webGL: true } );
42
+ this.frustums = [];
43
+ this.breaks = [];
44
+
45
+ this.lights = [];
46
+ this.shaders = new Map();
47
+
48
+ this.createLights();
49
+ this.updateFrustums();
50
+ this.injectInclude();
51
+
52
+ }
53
+
54
+ createLights() {
55
+
56
+ for ( let i = 0; i < this.cascades; i ++ ) {
57
+
58
+ const light = new DirectionalLight( 0xffffff, this.lightIntensity );
59
+ light.castShadow = true;
60
+ light.shadow.mapSize.width = this.shadowMapSize;
61
+ light.shadow.mapSize.height = this.shadowMapSize;
62
+
63
+ light.shadow.camera.near = this.lightNear;
64
+ light.shadow.camera.far = this.lightFar;
65
+ light.shadow.bias = this.shadowBias;
66
+
67
+ this.parent.add( light );
68
+ this.parent.add( light.target );
69
+ this.lights.push( light );
70
+
71
+ }
72
+
73
+ }
74
+
75
+ initCascades() {
76
+
77
+ const camera = this.camera;
78
+ camera.updateProjectionMatrix();
79
+ this.mainFrustum.setFromProjectionMatrix( camera.projectionMatrix, this.maxFar );
80
+ this.mainFrustum.split( this.breaks, this.frustums );
81
+
82
+ }
83
+
84
+ updateShadowBounds() {
85
+
86
+ const frustums = this.frustums;
87
+ for ( let i = 0; i < frustums.length; i ++ ) {
88
+
89
+ const light = this.lights[ i ];
90
+ const shadowCam = light.shadow.camera;
91
+ const frustum = this.frustums[ i ];
92
+
93
+ // Get the two points that represent that furthest points on the frustum assuming
94
+ // that's either the diagonal across the far plane or the diagonal across the whole
95
+ // frustum itself.
96
+ const nearVerts = frustum.vertices.near;
97
+ const farVerts = frustum.vertices.far;
98
+ const point1 = farVerts[ 0 ];
99
+ let point2;
100
+ if ( point1.distanceTo( farVerts[ 2 ] ) > point1.distanceTo( nearVerts[ 2 ] ) ) {
101
+
102
+ point2 = farVerts[ 2 ];
103
+
104
+ } else {
105
+
106
+ point2 = nearVerts[ 2 ];
107
+
108
+ }
109
+
110
+ let squaredBBWidth = point1.distanceTo( point2 );
111
+ if ( this.fade ) {
112
+
113
+ // expand the shadow extents by the fade margin if fade is enabled.
114
+ const camera = this.camera;
115
+ const far = Math.max( camera.far, this.maxFar );
116
+ const linearDepth = frustum.vertices.far[ 0 ].z / ( far - camera.near );
117
+ const margin = 0.25 * Math.pow( linearDepth, 2.0 ) * ( far - camera.near );
118
+
119
+ squaredBBWidth += margin;
120
+
121
+ }
122
+
123
+ shadowCam.left = - squaredBBWidth / 2;
124
+ shadowCam.right = squaredBBWidth / 2;
125
+ shadowCam.top = squaredBBWidth / 2;
126
+ shadowCam.bottom = - squaredBBWidth / 2;
127
+ shadowCam.updateProjectionMatrix();
128
+
129
+ }
130
+
131
+ }
132
+
133
+ getBreaks() {
134
+
135
+ const camera = this.camera;
136
+ const far = Math.min( camera.far, this.maxFar );
137
+ this.breaks.length = 0;
138
+
139
+ switch ( this.mode ) {
140
+
141
+ case 'uniform':
142
+ uniformSplit( this.cascades, camera.near, far, this.breaks );
143
+ break;
144
+ case 'logarithmic':
145
+ logarithmicSplit( this.cascades, camera.near, far, this.breaks );
146
+ break;
147
+ case 'practical':
148
+ practicalSplit( this.cascades, camera.near, far, 0.5, this.breaks );
149
+ break;
150
+ case 'custom':
151
+ if ( this.customSplitsCallback === undefined ) console.error( 'CSM: Custom split scheme callback not defined.' );
152
+ this.customSplitsCallback( this.cascades, camera.near, far, this.breaks );
153
+ break;
154
+
155
+ }
156
+
157
+ function uniformSplit( amount, near, far, target ) {
158
+
159
+ for ( let i = 1; i < amount; i ++ ) {
160
+
161
+ target.push( ( near + ( far - near ) * i / amount ) / far );
162
+
163
+ }
164
+
165
+ target.push( 1 );
166
+
167
+ }
168
+
169
+ function logarithmicSplit( amount, near, far, target ) {
170
+
171
+ for ( let i = 1; i < amount; i ++ ) {
172
+
173
+ target.push( ( near * ( far / near ) ** ( i / amount ) ) / far );
174
+
175
+ }
176
+
177
+ target.push( 1 );
178
+
179
+ }
180
+
181
+ function practicalSplit( amount, near, far, lambda, target ) {
182
+
183
+ _uniformArray.length = 0;
184
+ _logArray.length = 0;
185
+ logarithmicSplit( amount, near, far, _logArray );
186
+ uniformSplit( amount, near, far, _uniformArray );
187
+
188
+ for ( let i = 1; i < amount; i ++ ) {
189
+
190
+ target.push( MathUtils.lerp( _uniformArray[ i - 1 ], _logArray[ i - 1 ], lambda ) );
191
+
192
+ }
193
+
194
+ target.push( 1 );
195
+
196
+ }
197
+
198
+ }
199
+
200
+ update() {
201
+
202
+ const camera = this.camera;
203
+ const frustums = this.frustums;
204
+
205
+ // for each frustum we need to find its min-max box aligned with the light orientation
206
+ // the position in _lightOrientationMatrix does not matter, as we transform there and back
207
+ _lightOrientationMatrix.lookAt( new Vector3(), this.lightDirection, _up );
208
+ _lightOrientationMatrixInverse.copy( _lightOrientationMatrix ).invert();
209
+
210
+ for ( let i = 0; i < frustums.length; i ++ ) {
211
+
212
+ const light = this.lights[ i ];
213
+ const shadowCam = light.shadow.camera;
214
+ const texelWidth = ( shadowCam.right - shadowCam.left ) / this.shadowMapSize;
215
+ const texelHeight = ( shadowCam.top - shadowCam.bottom ) / this.shadowMapSize;
216
+ _cameraToLightMatrix.multiplyMatrices( _lightOrientationMatrixInverse, camera.matrixWorld );
217
+ frustums[ i ].toSpace( _cameraToLightMatrix, _lightSpaceFrustum );
218
+
219
+ const nearVerts = _lightSpaceFrustum.vertices.near;
220
+ const farVerts = _lightSpaceFrustum.vertices.far;
221
+ _bbox.makeEmpty();
222
+ for ( let j = 0; j < 4; j ++ ) {
223
+
224
+ _bbox.expandByPoint( nearVerts[ j ] );
225
+ _bbox.expandByPoint( farVerts[ j ] );
226
+
227
+ }
228
+
229
+ _bbox.getCenter( _center );
230
+ _center.z = _bbox.max.z + this.lightMargin;
231
+ _center.x = Math.floor( _center.x / texelWidth ) * texelWidth;
232
+ _center.y = Math.floor( _center.y / texelHeight ) * texelHeight;
233
+ _center.applyMatrix4( _lightOrientationMatrix );
234
+
235
+ light.position.copy( _center );
236
+ light.target.position.copy( _center );
237
+
238
+ light.target.position.x += this.lightDirection.x;
239
+ light.target.position.y += this.lightDirection.y;
240
+ light.target.position.z += this.lightDirection.z;
241
+
242
+ }
243
+
244
+ }
245
+
246
+ injectInclude() {
247
+
248
+ ShaderChunk.lights_fragment_begin = CSMShader.lights_fragment_begin;
249
+ ShaderChunk.lights_pars_begin = CSMShader.lights_pars_begin;
250
+
251
+ }
252
+
253
+ setupMaterial( material ) {
254
+
255
+ material.defines = material.defines || {};
256
+ material.defines.USE_CSM = 1;
257
+ material.defines.CSM_CASCADES = this.cascades;
258
+
259
+ if ( this.fade ) {
260
+
261
+ material.defines.CSM_FADE = '';
262
+
263
+ }
264
+
265
+ const breaksVec2 = [];
266
+ const scope = this;
267
+ const shaders = this.shaders;
268
+
269
+ material.onBeforeCompile = function ( shader ) {
270
+
271
+ const far = Math.min( scope.camera.far, scope.maxFar );
272
+ scope.getExtendedBreaks( breaksVec2 );
273
+
274
+ shader.uniforms.CSM_cascades = { value: breaksVec2 };
275
+ shader.uniforms.cameraNear = { value: scope.camera.near };
276
+ shader.uniforms.shadowFar = { value: far };
277
+
278
+ shaders.set( material, shader );
279
+
280
+ };
281
+
282
+ shaders.set( material, null );
283
+
284
+ }
285
+
286
+ updateUniforms() {
287
+
288
+ const far = Math.min( this.camera.far, this.maxFar );
289
+ const shaders = this.shaders;
290
+
291
+ shaders.forEach( function ( shader, material ) {
292
+
293
+ if ( shader !== null ) {
294
+
295
+ const uniforms = shader.uniforms;
296
+ this.getExtendedBreaks( uniforms.CSM_cascades.value );
297
+ uniforms.cameraNear.value = this.camera.near;
298
+ uniforms.shadowFar.value = far;
299
+
300
+ }
301
+
302
+ if ( ! this.fade && 'CSM_FADE' in material.defines ) {
303
+
304
+ delete material.defines.CSM_FADE;
305
+ material.needsUpdate = true;
306
+
307
+ } else if ( this.fade && ! ( 'CSM_FADE' in material.defines ) ) {
308
+
309
+ material.defines.CSM_FADE = '';
310
+ material.needsUpdate = true;
311
+
312
+ }
313
+
314
+ }, this );
315
+
316
+ }
317
+
318
+ getExtendedBreaks( target ) {
319
+
320
+ while ( target.length < this.breaks.length ) {
321
+
322
+ target.push( new Vector2() );
323
+
324
+ }
325
+
326
+ target.length = this.breaks.length;
327
+
328
+ for ( let i = 0; i < this.cascades; i ++ ) {
329
+
330
+ const amount = this.breaks[ i ];
331
+ const prev = this.breaks[ i - 1 ] || 0;
332
+ target[ i ].x = prev;
333
+ target[ i ].y = amount;
334
+
335
+ }
336
+
337
+ }
338
+
339
+ updateFrustums() {
340
+
341
+ this.getBreaks();
342
+ this.initCascades();
343
+ this.updateShadowBounds();
344
+ this.updateUniforms();
345
+
346
+ }
347
+
348
+ remove() {
349
+
350
+ for ( let i = 0; i < this.lights.length; i ++ ) {
351
+
352
+ this.parent.remove( this.lights[ i ].target );
353
+ this.parent.remove( this.lights[ i ] );
354
+
355
+ }
356
+
357
+ }
358
+
359
+ dispose() {
360
+
361
+ const shaders = this.shaders;
362
+ shaders.forEach( function ( shader, material ) {
363
+
364
+ delete material.onBeforeCompile;
365
+ delete material.defines.USE_CSM;
366
+ delete material.defines.CSM_CASCADES;
367
+ delete material.defines.CSM_FADE;
368
+
369
+ if ( shader !== null ) {
370
+
371
+ delete shader.uniforms.CSM_cascades;
372
+ delete shader.uniforms.cameraNear;
373
+ delete shader.uniforms.shadowFar;
374
+
375
+ }
376
+
377
+ material.needsUpdate = true;
378
+
379
+ } );
380
+ shaders.clear();
381
+
382
+ }
383
+
384
+ }
libs/three.js/0.172.0/jsm/csm/CSMFrustum.js ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Vector3, Matrix4 } from 'three';
2
+
3
+ const inverseProjectionMatrix = new Matrix4();
4
+
5
+ class CSMFrustum {
6
+
7
+ constructor( data ) {
8
+
9
+ data = data || {};
10
+
11
+ this.zNear = data.webGL === true ? - 1 : 0;
12
+
13
+ this.vertices = {
14
+ near: [
15
+ new Vector3(),
16
+ new Vector3(),
17
+ new Vector3(),
18
+ new Vector3()
19
+ ],
20
+ far: [
21
+ new Vector3(),
22
+ new Vector3(),
23
+ new Vector3(),
24
+ new Vector3()
25
+ ]
26
+ };
27
+
28
+ if ( data.projectionMatrix !== undefined ) {
29
+
30
+ this.setFromProjectionMatrix( data.projectionMatrix, data.maxFar || 10000 );
31
+
32
+ }
33
+
34
+ }
35
+
36
+ setFromProjectionMatrix( projectionMatrix, maxFar ) {
37
+
38
+ const zNear = this.zNear;
39
+ const isOrthographic = projectionMatrix.elements[ 2 * 4 + 3 ] === 0;
40
+
41
+ inverseProjectionMatrix.copy( projectionMatrix ).invert();
42
+
43
+ // 3 --- 0 vertices.near/far order
44
+ // | |
45
+ // 2 --- 1
46
+ // clip space spans from [-1, 1]
47
+
48
+ this.vertices.near[ 0 ].set( 1, 1, zNear );
49
+ this.vertices.near[ 1 ].set( 1, - 1, zNear );
50
+ this.vertices.near[ 2 ].set( - 1, - 1, zNear );
51
+ this.vertices.near[ 3 ].set( - 1, 1, zNear );
52
+ this.vertices.near.forEach( function ( v ) {
53
+
54
+ v.applyMatrix4( inverseProjectionMatrix );
55
+
56
+ } );
57
+
58
+ this.vertices.far[ 0 ].set( 1, 1, 1 );
59
+ this.vertices.far[ 1 ].set( 1, - 1, 1 );
60
+ this.vertices.far[ 2 ].set( - 1, - 1, 1 );
61
+ this.vertices.far[ 3 ].set( - 1, 1, 1 );
62
+ this.vertices.far.forEach( function ( v ) {
63
+
64
+ v.applyMatrix4( inverseProjectionMatrix );
65
+
66
+ const absZ = Math.abs( v.z );
67
+ if ( isOrthographic ) {
68
+
69
+ v.z *= Math.min( maxFar / absZ, 1.0 );
70
+
71
+ } else {
72
+
73
+ v.multiplyScalar( Math.min( maxFar / absZ, 1.0 ) );
74
+
75
+ }
76
+
77
+ } );
78
+
79
+ return this.vertices;
80
+
81
+ }
82
+
83
+ split( breaks, target ) {
84
+
85
+ while ( breaks.length > target.length ) {
86
+
87
+ target.push( new CSMFrustum() );
88
+
89
+ }
90
+
91
+ target.length = breaks.length;
92
+
93
+ for ( let i = 0; i < breaks.length; i ++ ) {
94
+
95
+ const cascade = target[ i ];
96
+
97
+ if ( i === 0 ) {
98
+
99
+ for ( let j = 0; j < 4; j ++ ) {
100
+
101
+ cascade.vertices.near[ j ].copy( this.vertices.near[ j ] );
102
+
103
+ }
104
+
105
+ } else {
106
+
107
+ for ( let j = 0; j < 4; j ++ ) {
108
+
109
+ cascade.vertices.near[ j ].lerpVectors( this.vertices.near[ j ], this.vertices.far[ j ], breaks[ i - 1 ] );
110
+
111
+ }
112
+
113
+ }
114
+
115
+ if ( i === breaks.length - 1 ) {
116
+
117
+ for ( let j = 0; j < 4; j ++ ) {
118
+
119
+ cascade.vertices.far[ j ].copy( this.vertices.far[ j ] );
120
+
121
+ }
122
+
123
+ } else {
124
+
125
+ for ( let j = 0; j < 4; j ++ ) {
126
+
127
+ cascade.vertices.far[ j ].lerpVectors( this.vertices.near[ j ], this.vertices.far[ j ], breaks[ i ] );
128
+
129
+ }
130
+
131
+ }
132
+
133
+ }
134
+
135
+ }
136
+
137
+ toSpace( cameraMatrix, target ) {
138
+
139
+ for ( let i = 0; i < 4; i ++ ) {
140
+
141
+ target.vertices.near[ i ]
142
+ .copy( this.vertices.near[ i ] )
143
+ .applyMatrix4( cameraMatrix );
144
+
145
+ target.vertices.far[ i ]
146
+ .copy( this.vertices.far[ i ] )
147
+ .applyMatrix4( cameraMatrix );
148
+
149
+ }
150
+
151
+ }
152
+
153
+ }
154
+
155
+ export { CSMFrustum };
libs/three.js/0.172.0/jsm/csm/CSMHelper.js ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Group,
3
+ Mesh,
4
+ LineSegments,
5
+ BufferGeometry,
6
+ LineBasicMaterial,
7
+ Box3Helper,
8
+ Box3,
9
+ PlaneGeometry,
10
+ MeshBasicMaterial,
11
+ BufferAttribute,
12
+ DoubleSide
13
+ } from 'three';
14
+
15
+ class CSMHelper extends Group {
16
+
17
+ constructor( csm ) {
18
+
19
+ super();
20
+ this.csm = csm;
21
+ this.displayFrustum = true;
22
+ this.displayPlanes = true;
23
+ this.displayShadowBounds = true;
24
+
25
+ const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );
26
+ const positions = new Float32Array( 24 );
27
+ const frustumGeometry = new BufferGeometry();
28
+ frustumGeometry.setIndex( new BufferAttribute( indices, 1 ) );
29
+ frustumGeometry.setAttribute( 'position', new BufferAttribute( positions, 3, false ) );
30
+ const frustumLines = new LineSegments( frustumGeometry, new LineBasicMaterial() );
31
+ this.add( frustumLines );
32
+
33
+ this.frustumLines = frustumLines;
34
+ this.cascadeLines = [];
35
+ this.cascadePlanes = [];
36
+ this.shadowLines = [];
37
+
38
+ }
39
+
40
+ updateVisibility() {
41
+
42
+ const displayFrustum = this.displayFrustum;
43
+ const displayPlanes = this.displayPlanes;
44
+ const displayShadowBounds = this.displayShadowBounds;
45
+
46
+ const frustumLines = this.frustumLines;
47
+ const cascadeLines = this.cascadeLines;
48
+ const cascadePlanes = this.cascadePlanes;
49
+ const shadowLines = this.shadowLines;
50
+ for ( let i = 0, l = cascadeLines.length; i < l; i ++ ) {
51
+
52
+ const cascadeLine = cascadeLines[ i ];
53
+ const cascadePlane = cascadePlanes[ i ];
54
+ const shadowLineGroup = shadowLines[ i ];
55
+
56
+ cascadeLine.visible = displayFrustum;
57
+ cascadePlane.visible = displayFrustum && displayPlanes;
58
+ shadowLineGroup.visible = displayShadowBounds;
59
+
60
+ }
61
+
62
+ frustumLines.visible = displayFrustum;
63
+
64
+ }
65
+
66
+ update() {
67
+
68
+ const csm = this.csm;
69
+ const camera = csm.camera;
70
+ const cascades = csm.cascades;
71
+ const mainFrustum = csm.mainFrustum;
72
+ const frustums = csm.frustums;
73
+ const lights = csm.lights;
74
+
75
+ const frustumLines = this.frustumLines;
76
+ const frustumLinePositions = frustumLines.geometry.getAttribute( 'position' );
77
+ const cascadeLines = this.cascadeLines;
78
+ const cascadePlanes = this.cascadePlanes;
79
+ const shadowLines = this.shadowLines;
80
+
81
+ if ( camera === null ) return;
82
+
83
+ this.position.copy( camera.position );
84
+ this.quaternion.copy( camera.quaternion );
85
+ this.scale.copy( camera.scale );
86
+ this.updateMatrixWorld( true );
87
+
88
+ while ( cascadeLines.length > cascades ) {
89
+
90
+ this.remove( cascadeLines.pop() );
91
+ this.remove( cascadePlanes.pop() );
92
+ this.remove( shadowLines.pop() );
93
+
94
+ }
95
+
96
+ while ( cascadeLines.length < cascades ) {
97
+
98
+ const cascadeLine = new Box3Helper( new Box3(), 0xffffff );
99
+ const planeMat = new MeshBasicMaterial( { transparent: true, opacity: 0.1, depthWrite: false, side: DoubleSide } );
100
+ const cascadePlane = new Mesh( new PlaneGeometry(), planeMat );
101
+ const shadowLineGroup = new Group();
102
+ const shadowLine = new Box3Helper( new Box3(), 0xffff00 );
103
+ shadowLineGroup.add( shadowLine );
104
+
105
+ this.add( cascadeLine );
106
+ this.add( cascadePlane );
107
+ this.add( shadowLineGroup );
108
+
109
+ cascadeLines.push( cascadeLine );
110
+ cascadePlanes.push( cascadePlane );
111
+ shadowLines.push( shadowLineGroup );
112
+
113
+ }
114
+
115
+ for ( let i = 0; i < cascades; i ++ ) {
116
+
117
+ const frustum = frustums[ i ];
118
+ const light = lights[ i ];
119
+ const shadowCam = light.shadow.camera;
120
+ const farVerts = frustum.vertices.far;
121
+
122
+ const cascadeLine = cascadeLines[ i ];
123
+ const cascadePlane = cascadePlanes[ i ];
124
+ const shadowLineGroup = shadowLines[ i ];
125
+ const shadowLine = shadowLineGroup.children[ 0 ];
126
+
127
+ cascadeLine.box.min.copy( farVerts[ 2 ] );
128
+ cascadeLine.box.max.copy( farVerts[ 0 ] );
129
+ cascadeLine.box.max.z += 1e-4;
130
+
131
+ cascadePlane.position.addVectors( farVerts[ 0 ], farVerts[ 2 ] );
132
+ cascadePlane.position.multiplyScalar( 0.5 );
133
+ cascadePlane.scale.subVectors( farVerts[ 0 ], farVerts[ 2 ] );
134
+ cascadePlane.scale.z = 1e-4;
135
+
136
+ this.remove( shadowLineGroup );
137
+ shadowLineGroup.position.copy( shadowCam.position );
138
+ shadowLineGroup.quaternion.copy( shadowCam.quaternion );
139
+ shadowLineGroup.scale.copy( shadowCam.scale );
140
+ shadowLineGroup.updateMatrixWorld( true );
141
+ this.attach( shadowLineGroup );
142
+
143
+ shadowLine.box.min.set( shadowCam.bottom, shadowCam.left, - shadowCam.far );
144
+ shadowLine.box.max.set( shadowCam.top, shadowCam.right, - shadowCam.near );
145
+
146
+ }
147
+
148
+ const nearVerts = mainFrustum.vertices.near;
149
+ const farVerts = mainFrustum.vertices.far;
150
+ frustumLinePositions.setXYZ( 0, farVerts[ 0 ].x, farVerts[ 0 ].y, farVerts[ 0 ].z );
151
+ frustumLinePositions.setXYZ( 1, farVerts[ 3 ].x, farVerts[ 3 ].y, farVerts[ 3 ].z );
152
+ frustumLinePositions.setXYZ( 2, farVerts[ 2 ].x, farVerts[ 2 ].y, farVerts[ 2 ].z );
153
+ frustumLinePositions.setXYZ( 3, farVerts[ 1 ].x, farVerts[ 1 ].y, farVerts[ 1 ].z );
154
+
155
+ frustumLinePositions.setXYZ( 4, nearVerts[ 0 ].x, nearVerts[ 0 ].y, nearVerts[ 0 ].z );
156
+ frustumLinePositions.setXYZ( 5, nearVerts[ 3 ].x, nearVerts[ 3 ].y, nearVerts[ 3 ].z );
157
+ frustumLinePositions.setXYZ( 6, nearVerts[ 2 ].x, nearVerts[ 2 ].y, nearVerts[ 2 ].z );
158
+ frustumLinePositions.setXYZ( 7, nearVerts[ 1 ].x, nearVerts[ 1 ].y, nearVerts[ 1 ].z );
159
+ frustumLinePositions.needsUpdate = true;
160
+
161
+ }
162
+
163
+ dispose() {
164
+
165
+ const frustumLines = this.frustumLines;
166
+ const cascadeLines = this.cascadeLines;
167
+ const cascadePlanes = this.cascadePlanes;
168
+ const shadowLines = this.shadowLines;
169
+
170
+ frustumLines.geometry.dispose();
171
+ frustumLines.material.dispose();
172
+
173
+ const cascades = this.csm.cascades;
174
+
175
+ for ( let i = 0; i < cascades; i ++ ) {
176
+
177
+ const cascadeLine = cascadeLines[ i ];
178
+ const cascadePlane = cascadePlanes[ i ];
179
+ const shadowLineGroup = shadowLines[ i ];
180
+ const shadowLine = shadowLineGroup.children[ 0 ];
181
+
182
+ cascadeLine.dispose(); // Box3Helper
183
+
184
+ cascadePlane.geometry.dispose();
185
+ cascadePlane.material.dispose();
186
+
187
+ shadowLine.dispose(); // Box3Helper
188
+
189
+ }
190
+
191
+ }
192
+
193
+ }
194
+
195
+ export { CSMHelper };
libs/three.js/0.172.0/jsm/csm/CSMShader.js ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ShaderChunk } from 'three';
2
+
3
+ const CSMShader = {
4
+ lights_fragment_begin: /* glsl */`
5
+ vec3 geometryPosition = - vViewPosition;
6
+ vec3 geometryNormal = normal;
7
+ vec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );
8
+
9
+ vec3 geometryClearcoatNormal = vec3( 0.0 );
10
+
11
+ #ifdef USE_CLEARCOAT
12
+
13
+ geometryClearcoatNormal = clearcoatNormal;
14
+
15
+ #endif
16
+
17
+ #ifdef USE_IRIDESCENCE
18
+ float dotNVi = saturate( dot( normal, geometryViewDir ) );
19
+ if ( material.iridescenceThickness == 0.0 ) {
20
+ material.iridescence = 0.0;
21
+ } else {
22
+ material.iridescence = saturate( material.iridescence );
23
+ }
24
+ if ( material.iridescence > 0.0 ) {
25
+ material.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor );
26
+ // Iridescence F0 approximation
27
+ material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi );
28
+ }
29
+ #endif
30
+
31
+ IncidentLight directLight;
32
+
33
+ #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )
34
+
35
+ PointLight pointLight;
36
+ #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0
37
+ PointLightShadow pointLightShadow;
38
+ #endif
39
+
40
+ #pragma unroll_loop_start
41
+ for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {
42
+
43
+ pointLight = pointLights[ i ];
44
+
45
+ getPointLightInfo( pointLight, geometryPosition, directLight );
46
+
47
+ #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
48
+ pointLightShadow = pointLightShadows[ i ];
49
+ directLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;
50
+ #endif
51
+
52
+ RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
53
+
54
+ }
55
+ #pragma unroll_loop_end
56
+
57
+ #endif
58
+
59
+ #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )
60
+
61
+ SpotLight spotLight;
62
+ vec4 spotColor;
63
+ vec3 spotLightCoord;
64
+ bool inSpotLightMap;
65
+
66
+ #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0
67
+ SpotLightShadow spotLightShadow;
68
+ #endif
69
+
70
+ #pragma unroll_loop_start
71
+ for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {
72
+
73
+ spotLight = spotLights[ i ];
74
+
75
+ getSpotLightInfo( spotLight, geometryPosition, directLight );
76
+
77
+ // spot lights are ordered [shadows with maps, shadows without maps, maps without shadows, none]
78
+ #if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )
79
+ #define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX
80
+ #elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
81
+ #define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS
82
+ #else
83
+ #define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )
84
+ #endif
85
+ #if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS )
86
+ spotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w;
87
+ inSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) );
88
+ spotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy );
89
+ directLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color;
90
+ #endif
91
+ #undef SPOT_LIGHT_MAP_INDEX
92
+
93
+ #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
94
+ spotLightShadow = spotLightShadows[ i ];
95
+ directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowIntensity, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;
96
+
97
+ #endif
98
+
99
+ RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
100
+
101
+ }
102
+ #pragma unroll_loop_end
103
+
104
+ #endif
105
+
106
+ #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) && defined( USE_CSM ) && defined( CSM_CASCADES )
107
+
108
+ DirectionalLight directionalLight;
109
+ float linearDepth = (vViewPosition.z) / (shadowFar - cameraNear);
110
+ #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
111
+ DirectionalLightShadow directionalLightShadow;
112
+ #endif
113
+
114
+ #if defined( USE_SHADOWMAP ) && defined( CSM_FADE )
115
+ vec2 cascade;
116
+ float cascadeCenter;
117
+ float closestEdge;
118
+ float margin;
119
+ float csmx;
120
+ float csmy;
121
+
122
+ #pragma unroll_loop_start
123
+ for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
124
+
125
+ directionalLight = directionalLights[ i ];
126
+ getDirectionalLightInfo( directionalLight, directLight );
127
+
128
+ #if ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
129
+ // NOTE: Depth gets larger away from the camera.
130
+ // cascade.x is closer, cascade.y is further
131
+ cascade = CSM_cascades[ i ];
132
+ cascadeCenter = ( cascade.x + cascade.y ) / 2.0;
133
+ closestEdge = linearDepth < cascadeCenter ? cascade.x : cascade.y;
134
+ margin = 0.25 * pow( closestEdge, 2.0 );
135
+ csmx = cascade.x - margin / 2.0;
136
+ csmy = cascade.y + margin / 2.0;
137
+ if( linearDepth >= csmx && ( linearDepth < csmy || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 ) ) {
138
+
139
+ float dist = min( linearDepth - csmx, csmy - linearDepth );
140
+ float ratio = clamp( dist / margin, 0.0, 1.0 );
141
+
142
+ vec3 prevColor = directLight.color;
143
+ directionalLightShadow = directionalLightShadows[ i ];
144
+ directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowIntensity, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
145
+
146
+ bool shouldFadeLastCascade = UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 && linearDepth > cascadeCenter;
147
+ directLight.color = mix( prevColor, directLight.color, shouldFadeLastCascade ? ratio : 1.0 );
148
+
149
+ ReflectedLight prevLight = reflectedLight;
150
+ RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
151
+
152
+ bool shouldBlend = UNROLLED_LOOP_INDEX != CSM_CASCADES - 1 || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 && linearDepth < cascadeCenter;
153
+ float blendRatio = shouldBlend ? ratio : 1.0;
154
+
155
+ reflectedLight.directDiffuse = mix( prevLight.directDiffuse, reflectedLight.directDiffuse, blendRatio );
156
+ reflectedLight.directSpecular = mix( prevLight.directSpecular, reflectedLight.directSpecular, blendRatio );
157
+ reflectedLight.indirectDiffuse = mix( prevLight.indirectDiffuse, reflectedLight.indirectDiffuse, blendRatio );
158
+ reflectedLight.indirectSpecular = mix( prevLight.indirectSpecular, reflectedLight.indirectSpecular, blendRatio );
159
+
160
+ }
161
+ #endif
162
+
163
+ }
164
+ #pragma unroll_loop_end
165
+ #elif defined (USE_SHADOWMAP)
166
+
167
+ #pragma unroll_loop_start
168
+ for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
169
+
170
+ directionalLight = directionalLights[ i ];
171
+ getDirectionalLightInfo( directionalLight, directLight );
172
+
173
+ #if ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
174
+
175
+ directionalLightShadow = directionalLightShadows[ i ];
176
+ if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y) directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowIntensity, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
177
+
178
+ if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && (linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1)) RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
179
+
180
+ #endif
181
+
182
+ }
183
+ #pragma unroll_loop_end
184
+
185
+ #elif ( NUM_DIR_LIGHT_SHADOWS > 0 )
186
+ // note: no loop here - all CSM lights are in fact one light only
187
+ getDirectionalLightInfo( directionalLights[0], directLight );
188
+ RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
189
+
190
+ #endif
191
+
192
+ #if ( NUM_DIR_LIGHTS > NUM_DIR_LIGHT_SHADOWS)
193
+ // compute the lights not casting shadows (if any)
194
+
195
+ #pragma unroll_loop_start
196
+ for ( int i = NUM_DIR_LIGHT_SHADOWS; i < NUM_DIR_LIGHTS; i ++ ) {
197
+
198
+ directionalLight = directionalLights[ i ];
199
+
200
+ getDirectionalLightInfo( directionalLight, directLight );
201
+
202
+ RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
203
+
204
+ }
205
+ #pragma unroll_loop_end
206
+
207
+ #endif
208
+
209
+ #endif
210
+
211
+
212
+ #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) && !defined( USE_CSM ) && !defined( CSM_CASCADES )
213
+
214
+ DirectionalLight directionalLight;
215
+ #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
216
+ DirectionalLightShadow directionalLightShadow;
217
+ #endif
218
+
219
+ #pragma unroll_loop_start
220
+ for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
221
+
222
+ directionalLight = directionalLights[ i ];
223
+
224
+ getDirectionalLightInfo( directionalLight, directLight );
225
+
226
+ #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
227
+ directionalLightShadow = directionalLightShadows[ i ];
228
+ directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowIntensity, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
229
+ #endif
230
+
231
+ RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
232
+
233
+ }
234
+ #pragma unroll_loop_end
235
+
236
+ #endif
237
+
238
+ #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )
239
+
240
+ RectAreaLight rectAreaLight;
241
+
242
+ #pragma unroll_loop_start
243
+ for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {
244
+
245
+ rectAreaLight = rectAreaLights[ i ];
246
+ RE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
247
+
248
+ }
249
+ #pragma unroll_loop_end
250
+
251
+ #endif
252
+
253
+ #if defined( RE_IndirectDiffuse )
254
+
255
+ vec3 iblIrradiance = vec3( 0.0 );
256
+
257
+ vec3 irradiance = getAmbientLightIrradiance( ambientLightColor );
258
+
259
+ #if defined( USE_LIGHT_PROBES )
260
+
261
+ irradiance += getLightProbeIrradiance( lightProbe, geometryNormal );
262
+
263
+ #endif
264
+
265
+ #if ( NUM_HEMI_LIGHTS > 0 )
266
+
267
+ #pragma unroll_loop_start
268
+ for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {
269
+
270
+ irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal );
271
+
272
+ }
273
+ #pragma unroll_loop_end
274
+
275
+ #endif
276
+
277
+ #endif
278
+
279
+ #if defined( RE_IndirectSpecular )
280
+
281
+ vec3 radiance = vec3( 0.0 );
282
+ vec3 clearcoatRadiance = vec3( 0.0 );
283
+
284
+ #endif
285
+ `,
286
+ lights_pars_begin: /* glsl */`
287
+ #if defined( USE_CSM ) && defined( CSM_CASCADES )
288
+ uniform vec2 CSM_cascades[CSM_CASCADES];
289
+ uniform float cameraNear;
290
+ uniform float shadowFar;
291
+ #endif
292
+ ` + ShaderChunk.lights_pars_begin
293
+ };
294
+
295
+ export { CSMShader };
libs/three.js/0.172.0/jsm/csm/CSMShadowNode.js ADDED
@@ -0,0 +1,442 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Vector2,
3
+ Vector3,
4
+ MathUtils,
5
+ Matrix4,
6
+ Box3,
7
+ Object3D,
8
+ WebGLCoordinateSystem,
9
+ ShadowBaseNode
10
+ } from 'three/webgpu';
11
+
12
+ import { CSMFrustum } from './CSMFrustum.js';
13
+ import { viewZToOrthographicDepth, reference, uniform, float, vec4, vec2, If, Fn, min, renderGroup, positionView, shadow } from 'three/tsl';
14
+
15
+ const _cameraToLightMatrix = new Matrix4();
16
+ const _lightSpaceFrustum = new CSMFrustum();
17
+ const _center = new Vector3();
18
+ const _bbox = new Box3();
19
+ const _uniformArray = [];
20
+ const _logArray = [];
21
+ const _lightDirection = new Vector3();
22
+ const _lightOrientationMatrix = new Matrix4();
23
+ const _lightOrientationMatrixInverse = new Matrix4();
24
+ const _up = new Vector3( 0, 1, 0 );
25
+
26
+ class LwLight extends Object3D {
27
+
28
+ constructor() {
29
+
30
+ super();
31
+
32
+ this.target = new Object3D();
33
+
34
+ }
35
+
36
+ }
37
+
38
+ class CSMShadowNode extends ShadowBaseNode {
39
+
40
+ constructor( light, data = {} ) {
41
+
42
+ super( light );
43
+
44
+ this.camera = null;
45
+ this.cascades = data.cascades || 3;
46
+ this.maxFar = data.maxFar || 100000;
47
+ this.mode = data.mode || 'practical';
48
+ this.lightMargin = data.lightMargin || 200;
49
+ this.customSplitsCallback = data.customSplitsCallback;
50
+
51
+ this.fade = false;
52
+
53
+ this.breaks = [];
54
+
55
+ this._cascades = [];
56
+ this.mainFrustum = null;
57
+ this.frustums = [];
58
+
59
+ this.lights = [];
60
+
61
+ this._shadowNodes = [];
62
+
63
+ }
64
+
65
+ init( { camera, renderer } ) {
66
+
67
+ this.camera = camera;
68
+
69
+ const data = { webGL: renderer.coordinateSystem === WebGLCoordinateSystem };
70
+ this.mainFrustum = new CSMFrustum( data );
71
+
72
+ const light = this.light;
73
+ const parent = light.parent;
74
+
75
+ for ( let i = 0; i < this.cascades; i ++ ) {
76
+
77
+ const lwLight = new LwLight();
78
+ lwLight.castShadow = true;
79
+
80
+ const lShadow = light.shadow.clone();
81
+ lShadow.bias = lShadow.bias * ( i + 1 );
82
+
83
+ this.lights.push( lwLight );
84
+
85
+ parent.add( lwLight );
86
+ parent.add( lwLight.target );
87
+
88
+ lwLight.shadow = lShadow;
89
+
90
+ this._shadowNodes.push( shadow( lwLight, lShadow ) );
91
+
92
+ this._cascades.push( new Vector2() );
93
+
94
+ }
95
+
96
+ this.updateFrustums();
97
+
98
+ }
99
+
100
+ initCascades() {
101
+
102
+ const camera = this.camera;
103
+ camera.updateProjectionMatrix();
104
+
105
+ this.mainFrustum.setFromProjectionMatrix( camera.projectionMatrix, this.maxFar );
106
+ this.mainFrustum.split( this.breaks, this.frustums );
107
+
108
+ }
109
+
110
+ getBreaks() {
111
+
112
+ const camera = this.camera;
113
+ const far = Math.min( camera.far, this.maxFar );
114
+
115
+ this.breaks.length = 0;
116
+
117
+ switch ( this.mode ) {
118
+
119
+ case 'uniform':
120
+ uniformSplit( this.cascades, camera.near, far, this.breaks );
121
+ break;
122
+
123
+ case 'logarithmic':
124
+ logarithmicSplit( this.cascades, camera.near, far, this.breaks );
125
+ break;
126
+
127
+ case 'practical':
128
+ practicalSplit( this.cascades, camera.near, far, 0.5, this.breaks );
129
+ break;
130
+
131
+ case 'custom':
132
+ if ( this.customSplitsCallback === undefined ) console.error( 'CSM: Custom split scheme callback not defined.' );
133
+ this.customSplitsCallback( this.cascades, camera.near, far, this.breaks );
134
+ break;
135
+
136
+ }
137
+
138
+ function uniformSplit( amount, near, far, target ) {
139
+
140
+ for ( let i = 1; i < amount; i ++ ) {
141
+
142
+ target.push( ( near + ( far - near ) * i / amount ) / far );
143
+
144
+ }
145
+
146
+ target.push( 1 );
147
+
148
+ }
149
+
150
+ function logarithmicSplit( amount, near, far, target ) {
151
+
152
+ for ( let i = 1; i < amount; i ++ ) {
153
+
154
+ target.push( ( near * ( far / near ) ** ( i / amount ) ) / far );
155
+
156
+ }
157
+
158
+ target.push( 1 );
159
+
160
+ }
161
+
162
+ function practicalSplit( amount, near, far, lambda, target ) {
163
+
164
+ _uniformArray.length = 0;
165
+ _logArray.length = 0;
166
+ logarithmicSplit( amount, near, far, _logArray );
167
+ uniformSplit( amount, near, far, _uniformArray );
168
+
169
+ for ( let i = 1; i < amount; i ++ ) {
170
+
171
+ target.push( MathUtils.lerp( _uniformArray[ i - 1 ], _logArray[ i - 1 ], lambda ) );
172
+
173
+ }
174
+
175
+ target.push( 1 );
176
+
177
+ }
178
+
179
+ }
180
+
181
+ setLightBreaks() {
182
+
183
+ for ( let i = 0, l = this.cascades; i < l; i ++ ) {
184
+
185
+ const amount = this.breaks[ i ];
186
+ const prev = this.breaks[ i - 1 ] || 0;
187
+
188
+ this._cascades[ i ].set( prev, amount );
189
+
190
+ }
191
+
192
+ }
193
+
194
+ updateShadowBounds() {
195
+
196
+ const frustums = this.frustums;
197
+
198
+ for ( let i = 0; i < frustums.length; i ++ ) {
199
+
200
+ const shadowCam = this.lights[ i ].shadow.camera;
201
+ const frustum = this.frustums[ i ];
202
+
203
+ // Get the two points that represent that furthest points on the frustum assuming
204
+ // that's either the diagonal across the far plane or the diagonal across the whole
205
+ // frustum itself.
206
+ const nearVerts = frustum.vertices.near;
207
+ const farVerts = frustum.vertices.far;
208
+ const point1 = farVerts[ 0 ];
209
+
210
+ let point2;
211
+
212
+ if ( point1.distanceTo( farVerts[ 2 ] ) > point1.distanceTo( nearVerts[ 2 ] ) ) {
213
+
214
+ point2 = farVerts[ 2 ];
215
+
216
+ } else {
217
+
218
+ point2 = nearVerts[ 2 ];
219
+
220
+ }
221
+
222
+ let squaredBBWidth = point1.distanceTo( point2 );
223
+
224
+ if ( this.fade ) {
225
+
226
+ // expand the shadow extents by the fade margin if fade is enabled.
227
+ const camera = this.camera;
228
+ const far = Math.max( camera.far, this.maxFar );
229
+ const linearDepth = frustum.vertices.far[ 0 ].z / ( far - camera.near );
230
+ const margin = 0.25 * Math.pow( linearDepth, 2.0 ) * ( far - camera.near );
231
+
232
+ squaredBBWidth += margin;
233
+
234
+ }
235
+
236
+ shadowCam.left = - squaredBBWidth / 2;
237
+ shadowCam.right = squaredBBWidth / 2;
238
+ shadowCam.top = squaredBBWidth / 2;
239
+ shadowCam.bottom = - squaredBBWidth / 2;
240
+ shadowCam.updateProjectionMatrix();
241
+
242
+ }
243
+
244
+ }
245
+
246
+ updateFrustums() {
247
+
248
+ this.getBreaks();
249
+ this.initCascades();
250
+ this.updateShadowBounds();
251
+ this.setLightBreaks();
252
+
253
+ }
254
+
255
+ setupFade() {
256
+
257
+ const cameraNear = reference( 'camera.near', 'float', this ).setGroup( renderGroup );
258
+ const cascades = reference( '_cascades', 'vec2', this ).setGroup( renderGroup ).label( 'cascades' );
259
+
260
+ const shadowFar = uniform( 'float' ).setGroup( renderGroup ).label( 'shadowFar' )
261
+ .onRenderUpdate( () => Math.min( this.maxFar, this.camera.far ) );
262
+
263
+ const linearDepth = viewZToOrthographicDepth( positionView.z, cameraNear, shadowFar ).toVar( 'linearDepth' );
264
+ const lastCascade = this.cascades - 1;
265
+
266
+ return Fn( ( builder ) => {
267
+
268
+ this.setupShadowPosition( builder );
269
+
270
+ const ret = vec4( 1, 1, 1, 1 ).toVar( 'shadowValue' );
271
+
272
+ const cascade = vec2().toVar( 'cascade' );
273
+ const cascadeCenter = float().toVar( 'cascadeCenter' );
274
+
275
+ const margin = float().toVar( 'margin' );
276
+
277
+ const csmX = float().toVar( 'csmX' );
278
+ const csmY = float().toVar( 'csmY' );
279
+
280
+ for ( let i = 0; i < this.cascades; i ++ ) {
281
+
282
+ const isLastCascade = i === lastCascade;
283
+
284
+ cascade.assign( cascades.element( i ) );
285
+
286
+ cascadeCenter.assign( cascade.x.add( cascade.y ).div( 2.0 ) );
287
+
288
+ const closestEdge = linearDepth.lessThan( cascadeCenter ).select( cascade.x, cascade.y );
289
+
290
+ margin.assign( float( 0.25 ).mul( closestEdge.pow( 2.0 ) ) );
291
+
292
+ csmX.assign( cascade.x.sub( margin.div( 2.0 ) ) );
293
+
294
+ if ( isLastCascade ) {
295
+
296
+ csmY.assign( cascade.y );
297
+
298
+ } else {
299
+
300
+ csmY.assign( cascade.y.add( margin.div( 2.0 ) ) );
301
+
302
+ }
303
+
304
+ const inRange = linearDepth.greaterThanEqual( csmX ).and( linearDepth.lessThanEqual( csmY ) );
305
+
306
+ If( inRange, () => {
307
+
308
+ const dist = min( linearDepth.sub( csmX ), csmY.sub( linearDepth ) ).toVar();
309
+
310
+ let ratio = dist.div( margin ).clamp( 0.0, 1.0 );
311
+
312
+ if ( i === 0 ) {
313
+
314
+ // don't fade at nearest edge
315
+ ratio = linearDepth.greaterThan( cascadeCenter ).select( ratio, 1 );
316
+
317
+ }
318
+
319
+ ret.subAssign( this._shadowNodes[ i ].oneMinus().mul( ratio ) );
320
+
321
+ } );
322
+
323
+ }
324
+
325
+ return ret;
326
+
327
+ } )();
328
+
329
+ }
330
+
331
+ setupStandard() {
332
+
333
+ const cameraNear = reference( 'camera.near', 'float', this ).setGroup( renderGroup );
334
+ const cascades = reference( '_cascades', 'vec2', this ).setGroup( renderGroup ).label( 'cascades' );
335
+
336
+ const shadowFar = uniform( 'float' ).setGroup( renderGroup ).label( 'shadowFar' )
337
+ .onRenderUpdate( () => Math.min( this.maxFar, this.camera.far ) );
338
+
339
+ const linearDepth = viewZToOrthographicDepth( positionView.z, cameraNear, shadowFar ).toVar( 'linearDepth' );
340
+
341
+ return Fn( ( builder ) => {
342
+
343
+ this.setupShadowPosition( builder );
344
+
345
+ const ret = vec4( 1, 1, 1, 1 ).toVar( 'shadowValue' );
346
+ const cascade = vec2().toVar( 'cascade' );
347
+
348
+ for ( let i = 0; i < this.cascades; i ++ ) {
349
+
350
+ cascade.assign( cascades.element( i ) );
351
+
352
+ If( linearDepth.greaterThanEqual( cascade.x ).and( linearDepth.lessThanEqual( cascade.y ) ), () => {
353
+
354
+ ret.assign( this._shadowNodes[ i ] );
355
+
356
+ } );
357
+
358
+ }
359
+
360
+ return ret;
361
+
362
+ } )();
363
+
364
+ }
365
+
366
+ setup( builder ) {
367
+
368
+ if ( this.camera === null ) this.init( builder );
369
+
370
+ return this.fade === true ? this.setupFade() : this.setupStandard();
371
+
372
+ }
373
+
374
+ updateBefore( /*builder*/ ) {
375
+
376
+ const light = this.light;
377
+ const camera = this.camera;
378
+ const frustums = this.frustums;
379
+
380
+ _lightDirection.subVectors( light.target.position, light.position ).normalize();
381
+
382
+ // for each frustum we need to find its min-max box aligned with the light orientation
383
+ // the position in _lightOrientationMatrix does not matter, as we transform there and back
384
+ _lightOrientationMatrix.lookAt( light.position, light.target.position, _up );
385
+ _lightOrientationMatrixInverse.copy( _lightOrientationMatrix ).invert();
386
+
387
+ for ( let i = 0; i < frustums.length; i ++ ) {
388
+
389
+ const lwLight = this.lights[ i ];
390
+ const shadow = lwLight.shadow;
391
+ const shadowCam = shadow.camera;
392
+ const texelWidth = ( shadowCam.right - shadowCam.left ) / shadow.mapSize.width;
393
+ const texelHeight = ( shadowCam.top - shadowCam.bottom ) / shadow.mapSize.height;
394
+
395
+ _cameraToLightMatrix.multiplyMatrices( _lightOrientationMatrixInverse, camera.matrixWorld );
396
+ frustums[ i ].toSpace( _cameraToLightMatrix, _lightSpaceFrustum );
397
+
398
+ const nearVerts = _lightSpaceFrustum.vertices.near;
399
+ const farVerts = _lightSpaceFrustum.vertices.far;
400
+
401
+ _bbox.makeEmpty();
402
+
403
+ for ( let j = 0; j < 4; j ++ ) {
404
+
405
+ _bbox.expandByPoint( nearVerts[ j ] );
406
+ _bbox.expandByPoint( farVerts[ j ] );
407
+
408
+ }
409
+
410
+ _bbox.getCenter( _center );
411
+ _center.z = _bbox.max.z + this.lightMargin;
412
+ _center.x = Math.floor( _center.x / texelWidth ) * texelWidth;
413
+ _center.y = Math.floor( _center.y / texelHeight ) * texelHeight;
414
+ _center.applyMatrix4( _lightOrientationMatrix );
415
+
416
+ lwLight.position.copy( _center );
417
+ lwLight.target.position.copy( _center );
418
+ lwLight.target.position.add( _lightDirection );
419
+
420
+ }
421
+
422
+ }
423
+
424
+ dispose() {
425
+
426
+ for ( let i = 0; i < this.lights.length; i ++ ) {
427
+
428
+ const light = this.lights[ i ];
429
+ const parent = light.parent;
430
+
431
+ parent.remove( light.target );
432
+ parent.remove( light );
433
+
434
+ }
435
+
436
+ super.dispose();
437
+
438
+ }
439
+
440
+ }
441
+
442
+ export { CSMShadowNode };
libs/three.js/0.172.0/jsm/curves/CurveExtras.js ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Curve,
3
+ Vector3
4
+ } from 'three';
5
+
6
+ /**
7
+ * A bunch of parametric curves
8
+ *
9
+ * Formulas collected from various sources
10
+ * http://mathworld.wolfram.com/HeartCurve.html
11
+ * http://en.wikipedia.org/wiki/Viviani%27s_curve
12
+ * http://www.mi.sanu.ac.rs/vismath/taylorapril2011/Taylor.pdf
13
+ * https://prideout.net/blog/old/blog/index.html@p=44.html
14
+ */
15
+
16
+ // GrannyKnot
17
+
18
+ class GrannyKnot extends Curve {
19
+
20
+ getPoint( t, optionalTarget = new Vector3() ) {
21
+
22
+ const point = optionalTarget;
23
+
24
+ t = 2 * Math.PI * t;
25
+
26
+ const x = - 0.22 * Math.cos( t ) - 1.28 * Math.sin( t ) - 0.44 * Math.cos( 3 * t ) - 0.78 * Math.sin( 3 * t );
27
+ const y = - 0.1 * Math.cos( 2 * t ) - 0.27 * Math.sin( 2 * t ) + 0.38 * Math.cos( 4 * t ) + 0.46 * Math.sin( 4 * t );
28
+ const z = 0.7 * Math.cos( 3 * t ) - 0.4 * Math.sin( 3 * t );
29
+
30
+ return point.set( x, y, z ).multiplyScalar( 20 );
31
+
32
+ }
33
+
34
+ }
35
+
36
+ // HeartCurve
37
+
38
+ class HeartCurve extends Curve {
39
+
40
+ constructor( scale = 5 ) {
41
+
42
+ super();
43
+
44
+ this.scale = scale;
45
+
46
+ }
47
+
48
+ getPoint( t, optionalTarget = new Vector3() ) {
49
+
50
+ const point = optionalTarget;
51
+
52
+ t *= 2 * Math.PI;
53
+
54
+ const x = 16 * Math.pow( Math.sin( t ), 3 );
55
+ const y = 13 * Math.cos( t ) - 5 * Math.cos( 2 * t ) - 2 * Math.cos( 3 * t ) - Math.cos( 4 * t );
56
+ const z = 0;
57
+
58
+ return point.set( x, y, z ).multiplyScalar( this.scale );
59
+
60
+ }
61
+
62
+ }
63
+
64
+ // Viviani's Curve
65
+
66
+ class VivianiCurve extends Curve {
67
+
68
+ constructor( scale = 70 ) {
69
+
70
+ super();
71
+
72
+ this.scale = scale;
73
+
74
+ }
75
+
76
+ getPoint( t, optionalTarget = new Vector3() ) {
77
+
78
+ const point = optionalTarget;
79
+
80
+ t = t * 4 * Math.PI; // normalized to 0..1
81
+ const a = this.scale / 2;
82
+
83
+ const x = a * ( 1 + Math.cos( t ) );
84
+ const y = a * Math.sin( t );
85
+ const z = 2 * a * Math.sin( t / 2 );
86
+
87
+ return point.set( x, y, z );
88
+
89
+ }
90
+
91
+ }
92
+
93
+ // KnotCurve
94
+
95
+ class KnotCurve extends Curve {
96
+
97
+ getPoint( t, optionalTarget = new Vector3() ) {
98
+
99
+ const point = optionalTarget;
100
+
101
+ t *= 2 * Math.PI;
102
+
103
+ const R = 10;
104
+ const s = 50;
105
+
106
+ const x = s * Math.sin( t );
107
+ const y = Math.cos( t ) * ( R + s * Math.cos( t ) );
108
+ const z = Math.sin( t ) * ( R + s * Math.cos( t ) );
109
+
110
+ return point.set( x, y, z );
111
+
112
+ }
113
+
114
+ }
115
+
116
+
117
+ // HelixCurve
118
+
119
+ class HelixCurve extends Curve {
120
+
121
+ getPoint( t, optionalTarget = new Vector3() ) {
122
+
123
+ const point = optionalTarget;
124
+
125
+ const a = 30; // radius
126
+ const b = 150; // height
127
+
128
+ const t2 = 2 * Math.PI * t * b / 30;
129
+
130
+ const x = Math.cos( t2 ) * a;
131
+ const y = Math.sin( t2 ) * a;
132
+ const z = b * t;
133
+
134
+ return point.set( x, y, z );
135
+
136
+ }
137
+
138
+ }
139
+
140
+ // TrefoilKnot
141
+
142
+ class TrefoilKnot extends Curve {
143
+
144
+ constructor( scale = 10 ) {
145
+
146
+ super();
147
+
148
+ this.scale = scale;
149
+
150
+ }
151
+
152
+ getPoint( t, optionalTarget = new Vector3() ) {
153
+
154
+ const point = optionalTarget;
155
+
156
+ t *= Math.PI * 2;
157
+
158
+ const x = ( 2 + Math.cos( 3 * t ) ) * Math.cos( 2 * t );
159
+ const y = ( 2 + Math.cos( 3 * t ) ) * Math.sin( 2 * t );
160
+ const z = Math.sin( 3 * t );
161
+
162
+ return point.set( x, y, z ).multiplyScalar( this.scale );
163
+
164
+ }
165
+
166
+ }
167
+
168
+ // TorusKnot
169
+
170
+ class TorusKnot extends Curve {
171
+
172
+ constructor( scale = 10 ) {
173
+
174
+ super();
175
+
176
+ this.scale = scale;
177
+
178
+ }
179
+
180
+ getPoint( t, optionalTarget = new Vector3() ) {
181
+
182
+ const point = optionalTarget;
183
+
184
+ const p = 3;
185
+ const q = 4;
186
+
187
+ t *= Math.PI * 2;
188
+
189
+ const x = ( 2 + Math.cos( q * t ) ) * Math.cos( p * t );
190
+ const y = ( 2 + Math.cos( q * t ) ) * Math.sin( p * t );
191
+ const z = Math.sin( q * t );
192
+
193
+ return point.set( x, y, z ).multiplyScalar( this.scale );
194
+
195
+ }
196
+
197
+ }
198
+
199
+ // CinquefoilKnot
200
+
201
+ class CinquefoilKnot extends Curve {
202
+
203
+ constructor( scale = 10 ) {
204
+
205
+ super();
206
+
207
+ this.scale = scale;
208
+
209
+ }
210
+
211
+ getPoint( t, optionalTarget = new Vector3() ) {
212
+
213
+ const point = optionalTarget;
214
+
215
+ const p = 2;
216
+ const q = 5;
217
+
218
+ t *= Math.PI * 2;
219
+
220
+ const x = ( 2 + Math.cos( q * t ) ) * Math.cos( p * t );
221
+ const y = ( 2 + Math.cos( q * t ) ) * Math.sin( p * t );
222
+ const z = Math.sin( q * t );
223
+
224
+ return point.set( x, y, z ).multiplyScalar( this.scale );
225
+
226
+ }
227
+
228
+ }
229
+
230
+
231
+ // TrefoilPolynomialKnot
232
+
233
+ class TrefoilPolynomialKnot extends Curve {
234
+
235
+ constructor( scale = 10 ) {
236
+
237
+ super();
238
+
239
+ this.scale = scale;
240
+
241
+ }
242
+
243
+ getPoint( t, optionalTarget = new Vector3() ) {
244
+
245
+ const point = optionalTarget;
246
+
247
+ t = t * 4 - 2;
248
+
249
+ const x = Math.pow( t, 3 ) - 3 * t;
250
+ const y = Math.pow( t, 4 ) - 4 * t * t;
251
+ const z = 1 / 5 * Math.pow( t, 5 ) - 2 * t;
252
+
253
+ return point.set( x, y, z ).multiplyScalar( this.scale );
254
+
255
+ }
256
+
257
+ }
258
+
259
+ function scaleTo( x, y, t ) {
260
+
261
+ const r = y - x;
262
+ return t * r + x;
263
+
264
+ }
265
+
266
+ // FigureEightPolynomialKnot
267
+
268
+ class FigureEightPolynomialKnot extends Curve {
269
+
270
+ constructor( scale = 1 ) {
271
+
272
+ super();
273
+
274
+ this.scale = scale;
275
+
276
+ }
277
+
278
+ getPoint( t, optionalTarget = new Vector3() ) {
279
+
280
+ const point = optionalTarget;
281
+
282
+ t = scaleTo( - 4, 4, t );
283
+
284
+ const x = 2 / 5 * t * ( t * t - 7 ) * ( t * t - 10 );
285
+ const y = Math.pow( t, 4 ) - 13 * t * t;
286
+ const z = 1 / 10 * t * ( t * t - 4 ) * ( t * t - 9 ) * ( t * t - 12 );
287
+
288
+ return point.set( x, y, z ).multiplyScalar( this.scale );
289
+
290
+ }
291
+
292
+ }
293
+
294
+ // DecoratedTorusKnot4a
295
+
296
+ class DecoratedTorusKnot4a extends Curve {
297
+
298
+ constructor( scale = 40 ) {
299
+
300
+ super();
301
+
302
+ this.scale = scale;
303
+
304
+ }
305
+
306
+ getPoint( t, optionalTarget = new Vector3() ) {
307
+
308
+ const point = optionalTarget;
309
+
310
+ t *= Math.PI * 2;
311
+
312
+ const x = Math.cos( 2 * t ) * ( 1 + 0.6 * ( Math.cos( 5 * t ) + 0.75 * Math.cos( 10 * t ) ) );
313
+ const y = Math.sin( 2 * t ) * ( 1 + 0.6 * ( Math.cos( 5 * t ) + 0.75 * Math.cos( 10 * t ) ) );
314
+ const z = 0.35 * Math.sin( 5 * t );
315
+
316
+ return point.set( x, y, z ).multiplyScalar( this.scale );
317
+
318
+ }
319
+
320
+ }
321
+
322
+ // DecoratedTorusKnot4b
323
+
324
+ class DecoratedTorusKnot4b extends Curve {
325
+
326
+ constructor( scale = 40 ) {
327
+
328
+ super();
329
+
330
+ this.scale = scale;
331
+
332
+ }
333
+
334
+ getPoint( t, optionalTarget = new Vector3() ) {
335
+
336
+ const point = optionalTarget;
337
+
338
+ const fi = t * Math.PI * 2;
339
+
340
+ const x = Math.cos( 2 * fi ) * ( 1 + 0.45 * Math.cos( 3 * fi ) + 0.4 * Math.cos( 9 * fi ) );
341
+ const y = Math.sin( 2 * fi ) * ( 1 + 0.45 * Math.cos( 3 * fi ) + 0.4 * Math.cos( 9 * fi ) );
342
+ const z = 0.2 * Math.sin( 9 * fi );
343
+
344
+ return point.set( x, y, z ).multiplyScalar( this.scale );
345
+
346
+ }
347
+
348
+ }
349
+
350
+
351
+ // DecoratedTorusKnot5a
352
+
353
+ class DecoratedTorusKnot5a extends Curve {
354
+
355
+ constructor( scale = 40 ) {
356
+
357
+ super();
358
+
359
+ this.scale = scale;
360
+
361
+ }
362
+
363
+ getPoint( t, optionalTarget = new Vector3() ) {
364
+
365
+ const point = optionalTarget;
366
+
367
+ const fi = t * Math.PI * 2;
368
+
369
+ const x = Math.cos( 3 * fi ) * ( 1 + 0.3 * Math.cos( 5 * fi ) + 0.5 * Math.cos( 10 * fi ) );
370
+ const y = Math.sin( 3 * fi ) * ( 1 + 0.3 * Math.cos( 5 * fi ) + 0.5 * Math.cos( 10 * fi ) );
371
+ const z = 0.2 * Math.sin( 20 * fi );
372
+
373
+ return point.set( x, y, z ).multiplyScalar( this.scale );
374
+
375
+ }
376
+
377
+ }
378
+
379
+ // DecoratedTorusKnot5c
380
+
381
+ class DecoratedTorusKnot5c extends Curve {
382
+
383
+ constructor( scale = 40 ) {
384
+
385
+ super();
386
+
387
+ this.scale = scale;
388
+
389
+ }
390
+
391
+ getPoint( t, optionalTarget = new Vector3() ) {
392
+
393
+ const point = optionalTarget;
394
+
395
+ const fi = t * Math.PI * 2;
396
+
397
+ const x = Math.cos( 4 * fi ) * ( 1 + 0.5 * ( Math.cos( 5 * fi ) + 0.4 * Math.cos( 20 * fi ) ) );
398
+ const y = Math.sin( 4 * fi ) * ( 1 + 0.5 * ( Math.cos( 5 * fi ) + 0.4 * Math.cos( 20 * fi ) ) );
399
+ const z = 0.35 * Math.sin( 15 * fi );
400
+
401
+ return point.set( x, y, z ).multiplyScalar( this.scale );
402
+
403
+ }
404
+
405
+ }
406
+
407
+ export {
408
+ GrannyKnot,
409
+ HeartCurve,
410
+ VivianiCurve,
411
+ KnotCurve,
412
+ HelixCurve,
413
+ TrefoilKnot,
414
+ TorusKnot,
415
+ CinquefoilKnot,
416
+ TrefoilPolynomialKnot,
417
+ FigureEightPolynomialKnot,
418
+ DecoratedTorusKnot4a,
419
+ DecoratedTorusKnot4b,
420
+ DecoratedTorusKnot5a,
421
+ DecoratedTorusKnot5c
422
+ };
libs/three.js/0.172.0/jsm/curves/NURBSCurve.js ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Curve,
3
+ Vector3,
4
+ Vector4
5
+ } from 'three';
6
+ import * as NURBSUtils from '../curves/NURBSUtils.js';
7
+
8
+ /**
9
+ * NURBS curve object
10
+ *
11
+ * Derives from Curve, overriding getPoint and getTangent.
12
+ *
13
+ * Implementation is based on (x, y [, z=0 [, w=1]]) control points with w=weight.
14
+ *
15
+ **/
16
+
17
+ class NURBSCurve extends Curve {
18
+
19
+ constructor(
20
+ degree,
21
+ knots /* array of reals */,
22
+ controlPoints /* array of Vector(2|3|4) */,
23
+ startKnot /* index in knots */,
24
+ endKnot /* index in knots */
25
+ ) {
26
+
27
+ super();
28
+
29
+ const knotsLength = knots ? knots.length - 1 : 0;
30
+ const pointsLength = controlPoints ? controlPoints.length : 0;
31
+
32
+ this.degree = degree;
33
+ this.knots = knots;
34
+ this.controlPoints = [];
35
+ // Used by periodic NURBS to remove hidden spans
36
+ this.startKnot = startKnot || 0;
37
+ this.endKnot = endKnot || knotsLength;
38
+
39
+ for ( let i = 0; i < pointsLength; ++ i ) {
40
+
41
+ // ensure Vector4 for control points
42
+ const point = controlPoints[ i ];
43
+ this.controlPoints[ i ] = new Vector4( point.x, point.y, point.z, point.w );
44
+
45
+ }
46
+
47
+ }
48
+
49
+ getPoint( t, optionalTarget = new Vector3() ) {
50
+
51
+ const point = optionalTarget;
52
+
53
+ const u = this.knots[ this.startKnot ] + t * ( this.knots[ this.endKnot ] - this.knots[ this.startKnot ] ); // linear mapping t->u
54
+
55
+ // following results in (wx, wy, wz, w) homogeneous point
56
+ const hpoint = NURBSUtils.calcBSplinePoint( this.degree, this.knots, this.controlPoints, u );
57
+
58
+ if ( hpoint.w !== 1.0 ) {
59
+
60
+ // project to 3D space: (wx, wy, wz, w) -> (x, y, z, 1)
61
+ hpoint.divideScalar( hpoint.w );
62
+
63
+ }
64
+
65
+ return point.set( hpoint.x, hpoint.y, hpoint.z );
66
+
67
+ }
68
+
69
+ getTangent( t, optionalTarget = new Vector3() ) {
70
+
71
+ const tangent = optionalTarget;
72
+
73
+ const u = this.knots[ 0 ] + t * ( this.knots[ this.knots.length - 1 ] - this.knots[ 0 ] );
74
+ const ders = NURBSUtils.calcNURBSDerivatives( this.degree, this.knots, this.controlPoints, u, 1 );
75
+ tangent.copy( ders[ 1 ] ).normalize();
76
+
77
+ return tangent;
78
+
79
+ }
80
+
81
+ toJSON() {
82
+
83
+ const data = super.toJSON();
84
+
85
+ data.degree = this.degree;
86
+ data.knots = [ ...this.knots ];
87
+ data.controlPoints = this.controlPoints.map( p => p.toArray() );
88
+ data.startKnot = this.startKnot;
89
+ data.endKnot = this.endKnot;
90
+
91
+ return data;
92
+
93
+ }
94
+
95
+ fromJSON( json ) {
96
+
97
+ super.fromJSON( json );
98
+
99
+ this.degree = json.degree;
100
+ this.knots = [ ...json.knots ];
101
+ this.controlPoints = json.controlPoints.map( p => new Vector4( p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] ) );
102
+ this.startKnot = json.startKnot;
103
+ this.endKnot = json.endKnot;
104
+
105
+ return this;
106
+
107
+ }
108
+
109
+ }
110
+
111
+ export { NURBSCurve };
libs/three.js/0.172.0/jsm/curves/NURBSSurface.js ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Vector4
3
+ } from 'three';
4
+ import * as NURBSUtils from '../curves/NURBSUtils.js';
5
+
6
+ /**
7
+ * NURBS surface object
8
+ *
9
+ * Implementation is based on (x, y [, z=0 [, w=1]]) control points with w=weight.
10
+ **/
11
+
12
+ class NURBSSurface {
13
+
14
+ constructor( degree1, degree2, knots1, knots2 /* arrays of reals */, controlPoints /* array^2 of Vector(2|3|4) */ ) {
15
+
16
+ this.degree1 = degree1;
17
+ this.degree2 = degree2;
18
+ this.knots1 = knots1;
19
+ this.knots2 = knots2;
20
+ this.controlPoints = [];
21
+
22
+ const len1 = knots1.length - degree1 - 1;
23
+ const len2 = knots2.length - degree2 - 1;
24
+
25
+ // ensure Vector4 for control points
26
+ for ( let i = 0; i < len1; ++ i ) {
27
+
28
+ this.controlPoints[ i ] = [];
29
+
30
+ for ( let j = 0; j < len2; ++ j ) {
31
+
32
+ const point = controlPoints[ i ][ j ];
33
+ this.controlPoints[ i ][ j ] = new Vector4( point.x, point.y, point.z, point.w );
34
+
35
+ }
36
+
37
+ }
38
+
39
+ }
40
+
41
+ getPoint( t1, t2, target ) {
42
+
43
+ const u = this.knots1[ 0 ] + t1 * ( this.knots1[ this.knots1.length - 1 ] - this.knots1[ 0 ] ); // linear mapping t1->u
44
+ const v = this.knots2[ 0 ] + t2 * ( this.knots2[ this.knots2.length - 1 ] - this.knots2[ 0 ] ); // linear mapping t2->u
45
+
46
+ NURBSUtils.calcSurfacePoint( this.degree1, this.degree2, this.knots1, this.knots2, this.controlPoints, u, v, target );
47
+
48
+ }
49
+
50
+ }
51
+
52
+ export { NURBSSurface };
libs/three.js/0.172.0/jsm/curves/NURBSUtils.js ADDED
@@ -0,0 +1,545 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Vector3,
3
+ Vector4
4
+ } from 'three';
5
+
6
+ /**
7
+ * NURBS utils
8
+ *
9
+ * See NURBSCurve and NURBSSurface.
10
+ **/
11
+
12
+
13
+ /**************************************************************
14
+ * NURBS Utils
15
+ **************************************************************/
16
+
17
+ /*
18
+ Finds knot vector span.
19
+
20
+ p : degree
21
+ u : parametric value
22
+ U : knot vector
23
+
24
+ returns the span
25
+ */
26
+ function findSpan( p, u, U ) {
27
+
28
+ const n = U.length - p - 1;
29
+
30
+ if ( u >= U[ n ] ) {
31
+
32
+ return n - 1;
33
+
34
+ }
35
+
36
+ if ( u <= U[ p ] ) {
37
+
38
+ return p;
39
+
40
+ }
41
+
42
+ let low = p;
43
+ let high = n;
44
+ let mid = Math.floor( ( low + high ) / 2 );
45
+
46
+ while ( u < U[ mid ] || u >= U[ mid + 1 ] ) {
47
+
48
+ if ( u < U[ mid ] ) {
49
+
50
+ high = mid;
51
+
52
+ } else {
53
+
54
+ low = mid;
55
+
56
+ }
57
+
58
+ mid = Math.floor( ( low + high ) / 2 );
59
+
60
+ }
61
+
62
+ return mid;
63
+
64
+ }
65
+
66
+
67
+ /*
68
+ Calculate basis functions. See The NURBS Book, page 70, algorithm A2.2
69
+
70
+ span : span in which u lies
71
+ u : parametric point
72
+ p : degree
73
+ U : knot vector
74
+
75
+ returns array[p+1] with basis functions values.
76
+ */
77
+ function calcBasisFunctions( span, u, p, U ) {
78
+
79
+ const N = [];
80
+ const left = [];
81
+ const right = [];
82
+ N[ 0 ] = 1.0;
83
+
84
+ for ( let j = 1; j <= p; ++ j ) {
85
+
86
+ left[ j ] = u - U[ span + 1 - j ];
87
+ right[ j ] = U[ span + j ] - u;
88
+
89
+ let saved = 0.0;
90
+
91
+ for ( let r = 0; r < j; ++ r ) {
92
+
93
+ const rv = right[ r + 1 ];
94
+ const lv = left[ j - r ];
95
+ const temp = N[ r ] / ( rv + lv );
96
+ N[ r ] = saved + rv * temp;
97
+ saved = lv * temp;
98
+
99
+ }
100
+
101
+ N[ j ] = saved;
102
+
103
+ }
104
+
105
+ return N;
106
+
107
+ }
108
+
109
+
110
+ /*
111
+ Calculate B-Spline curve points. See The NURBS Book, page 82, algorithm A3.1.
112
+
113
+ p : degree of B-Spline
114
+ U : knot vector
115
+ P : control points (x, y, z, w)
116
+ u : parametric point
117
+
118
+ returns point for given u
119
+ */
120
+ function calcBSplinePoint( p, U, P, u ) {
121
+
122
+ const span = findSpan( p, u, U );
123
+ const N = calcBasisFunctions( span, u, p, U );
124
+ const C = new Vector4( 0, 0, 0, 0 );
125
+
126
+ for ( let j = 0; j <= p; ++ j ) {
127
+
128
+ const point = P[ span - p + j ];
129
+ const Nj = N[ j ];
130
+ const wNj = point.w * Nj;
131
+ C.x += point.x * wNj;
132
+ C.y += point.y * wNj;
133
+ C.z += point.z * wNj;
134
+ C.w += point.w * Nj;
135
+
136
+ }
137
+
138
+ return C;
139
+
140
+ }
141
+
142
+
143
+ /*
144
+ Calculate basis functions derivatives. See The NURBS Book, page 72, algorithm A2.3.
145
+
146
+ span : span in which u lies
147
+ u : parametric point
148
+ p : degree
149
+ n : number of derivatives to calculate
150
+ U : knot vector
151
+
152
+ returns array[n+1][p+1] with basis functions derivatives
153
+ */
154
+ function calcBasisFunctionDerivatives( span, u, p, n, U ) {
155
+
156
+ const zeroArr = [];
157
+ for ( let i = 0; i <= p; ++ i )
158
+ zeroArr[ i ] = 0.0;
159
+
160
+ const ders = [];
161
+
162
+ for ( let i = 0; i <= n; ++ i )
163
+ ders[ i ] = zeroArr.slice( 0 );
164
+
165
+ const ndu = [];
166
+
167
+ for ( let i = 0; i <= p; ++ i )
168
+ ndu[ i ] = zeroArr.slice( 0 );
169
+
170
+ ndu[ 0 ][ 0 ] = 1.0;
171
+
172
+ const left = zeroArr.slice( 0 );
173
+ const right = zeroArr.slice( 0 );
174
+
175
+ for ( let j = 1; j <= p; ++ j ) {
176
+
177
+ left[ j ] = u - U[ span + 1 - j ];
178
+ right[ j ] = U[ span + j ] - u;
179
+
180
+ let saved = 0.0;
181
+
182
+ for ( let r = 0; r < j; ++ r ) {
183
+
184
+ const rv = right[ r + 1 ];
185
+ const lv = left[ j - r ];
186
+ ndu[ j ][ r ] = rv + lv;
187
+
188
+ const temp = ndu[ r ][ j - 1 ] / ndu[ j ][ r ];
189
+ ndu[ r ][ j ] = saved + rv * temp;
190
+ saved = lv * temp;
191
+
192
+ }
193
+
194
+ ndu[ j ][ j ] = saved;
195
+
196
+ }
197
+
198
+ for ( let j = 0; j <= p; ++ j ) {
199
+
200
+ ders[ 0 ][ j ] = ndu[ j ][ p ];
201
+
202
+ }
203
+
204
+ for ( let r = 0; r <= p; ++ r ) {
205
+
206
+ let s1 = 0;
207
+ let s2 = 1;
208
+
209
+ const a = [];
210
+ for ( let i = 0; i <= p; ++ i ) {
211
+
212
+ a[ i ] = zeroArr.slice( 0 );
213
+
214
+ }
215
+
216
+ a[ 0 ][ 0 ] = 1.0;
217
+
218
+ for ( let k = 1; k <= n; ++ k ) {
219
+
220
+ let d = 0.0;
221
+ const rk = r - k;
222
+ const pk = p - k;
223
+
224
+ if ( r >= k ) {
225
+
226
+ a[ s2 ][ 0 ] = a[ s1 ][ 0 ] / ndu[ pk + 1 ][ rk ];
227
+ d = a[ s2 ][ 0 ] * ndu[ rk ][ pk ];
228
+
229
+ }
230
+
231
+ const j1 = ( rk >= - 1 ) ? 1 : - rk;
232
+ const j2 = ( r - 1 <= pk ) ? k - 1 : p - r;
233
+
234
+ for ( let j = j1; j <= j2; ++ j ) {
235
+
236
+ a[ s2 ][ j ] = ( a[ s1 ][ j ] - a[ s1 ][ j - 1 ] ) / ndu[ pk + 1 ][ rk + j ];
237
+ d += a[ s2 ][ j ] * ndu[ rk + j ][ pk ];
238
+
239
+ }
240
+
241
+ if ( r <= pk ) {
242
+
243
+ a[ s2 ][ k ] = - a[ s1 ][ k - 1 ] / ndu[ pk + 1 ][ r ];
244
+ d += a[ s2 ][ k ] * ndu[ r ][ pk ];
245
+
246
+ }
247
+
248
+ ders[ k ][ r ] = d;
249
+
250
+ const j = s1;
251
+ s1 = s2;
252
+ s2 = j;
253
+
254
+ }
255
+
256
+ }
257
+
258
+ let r = p;
259
+
260
+ for ( let k = 1; k <= n; ++ k ) {
261
+
262
+ for ( let j = 0; j <= p; ++ j ) {
263
+
264
+ ders[ k ][ j ] *= r;
265
+
266
+ }
267
+
268
+ r *= p - k;
269
+
270
+ }
271
+
272
+ return ders;
273
+
274
+ }
275
+
276
+
277
+ /*
278
+ Calculate derivatives of a B-Spline. See The NURBS Book, page 93, algorithm A3.2.
279
+
280
+ p : degree
281
+ U : knot vector
282
+ P : control points
283
+ u : Parametric points
284
+ nd : number of derivatives
285
+
286
+ returns array[d+1] with derivatives
287
+ */
288
+ function calcBSplineDerivatives( p, U, P, u, nd ) {
289
+
290
+ const du = nd < p ? nd : p;
291
+ const CK = [];
292
+ const span = findSpan( p, u, U );
293
+ const nders = calcBasisFunctionDerivatives( span, u, p, du, U );
294
+ const Pw = [];
295
+
296
+ for ( let i = 0; i < P.length; ++ i ) {
297
+
298
+ const point = P[ i ].clone();
299
+ const w = point.w;
300
+
301
+ point.x *= w;
302
+ point.y *= w;
303
+ point.z *= w;
304
+
305
+ Pw[ i ] = point;
306
+
307
+ }
308
+
309
+ for ( let k = 0; k <= du; ++ k ) {
310
+
311
+ const point = Pw[ span - p ].clone().multiplyScalar( nders[ k ][ 0 ] );
312
+
313
+ for ( let j = 1; j <= p; ++ j ) {
314
+
315
+ point.add( Pw[ span - p + j ].clone().multiplyScalar( nders[ k ][ j ] ) );
316
+
317
+ }
318
+
319
+ CK[ k ] = point;
320
+
321
+ }
322
+
323
+ for ( let k = du + 1; k <= nd + 1; ++ k ) {
324
+
325
+ CK[ k ] = new Vector4( 0, 0, 0 );
326
+
327
+ }
328
+
329
+ return CK;
330
+
331
+ }
332
+
333
+
334
+ /*
335
+ Calculate "K over I"
336
+
337
+ returns k!/(i!(k-i)!)
338
+ */
339
+ function calcKoverI( k, i ) {
340
+
341
+ let nom = 1;
342
+
343
+ for ( let j = 2; j <= k; ++ j ) {
344
+
345
+ nom *= j;
346
+
347
+ }
348
+
349
+ let denom = 1;
350
+
351
+ for ( let j = 2; j <= i; ++ j ) {
352
+
353
+ denom *= j;
354
+
355
+ }
356
+
357
+ for ( let j = 2; j <= k - i; ++ j ) {
358
+
359
+ denom *= j;
360
+
361
+ }
362
+
363
+ return nom / denom;
364
+
365
+ }
366
+
367
+
368
+ /*
369
+ Calculate derivatives (0-nd) of rational curve. See The NURBS Book, page 127, algorithm A4.2.
370
+
371
+ Pders : result of function calcBSplineDerivatives
372
+
373
+ returns array with derivatives for rational curve.
374
+ */
375
+ function calcRationalCurveDerivatives( Pders ) {
376
+
377
+ const nd = Pders.length;
378
+ const Aders = [];
379
+ const wders = [];
380
+
381
+ for ( let i = 0; i < nd; ++ i ) {
382
+
383
+ const point = Pders[ i ];
384
+ Aders[ i ] = new Vector3( point.x, point.y, point.z );
385
+ wders[ i ] = point.w;
386
+
387
+ }
388
+
389
+ const CK = [];
390
+
391
+ for ( let k = 0; k < nd; ++ k ) {
392
+
393
+ const v = Aders[ k ].clone();
394
+
395
+ for ( let i = 1; i <= k; ++ i ) {
396
+
397
+ v.sub( CK[ k - i ].clone().multiplyScalar( calcKoverI( k, i ) * wders[ i ] ) );
398
+
399
+ }
400
+
401
+ CK[ k ] = v.divideScalar( wders[ 0 ] );
402
+
403
+ }
404
+
405
+ return CK;
406
+
407
+ }
408
+
409
+
410
+ /*
411
+ Calculate NURBS curve derivatives. See The NURBS Book, page 127, algorithm A4.2.
412
+
413
+ p : degree
414
+ U : knot vector
415
+ P : control points in homogeneous space
416
+ u : parametric points
417
+ nd : number of derivatives
418
+
419
+ returns array with derivatives.
420
+ */
421
+ function calcNURBSDerivatives( p, U, P, u, nd ) {
422
+
423
+ const Pders = calcBSplineDerivatives( p, U, P, u, nd );
424
+ return calcRationalCurveDerivatives( Pders );
425
+
426
+ }
427
+
428
+
429
+ /*
430
+ Calculate rational B-Spline surface point. See The NURBS Book, page 134, algorithm A4.3.
431
+
432
+ p, q : degrees of B-Spline surface
433
+ U, V : knot vectors
434
+ P : control points (x, y, z, w)
435
+ u, v : parametric values
436
+
437
+ returns point for given (u, v)
438
+ */
439
+ function calcSurfacePoint( p, q, U, V, P, u, v, target ) {
440
+
441
+ const uspan = findSpan( p, u, U );
442
+ const vspan = findSpan( q, v, V );
443
+ const Nu = calcBasisFunctions( uspan, u, p, U );
444
+ const Nv = calcBasisFunctions( vspan, v, q, V );
445
+ const temp = [];
446
+
447
+ for ( let l = 0; l <= q; ++ l ) {
448
+
449
+ temp[ l ] = new Vector4( 0, 0, 0, 0 );
450
+ for ( let k = 0; k <= p; ++ k ) {
451
+
452
+ const point = P[ uspan - p + k ][ vspan - q + l ].clone();
453
+ const w = point.w;
454
+ point.x *= w;
455
+ point.y *= w;
456
+ point.z *= w;
457
+ temp[ l ].add( point.multiplyScalar( Nu[ k ] ) );
458
+
459
+ }
460
+
461
+ }
462
+
463
+ const Sw = new Vector4( 0, 0, 0, 0 );
464
+ for ( let l = 0; l <= q; ++ l ) {
465
+
466
+ Sw.add( temp[ l ].multiplyScalar( Nv[ l ] ) );
467
+
468
+ }
469
+
470
+ Sw.divideScalar( Sw.w );
471
+ target.set( Sw.x, Sw.y, Sw.z );
472
+
473
+ }
474
+
475
+ /*
476
+ Calculate rational B-Spline volume point. See The NURBS Book, page 134, algorithm A4.3.
477
+
478
+ p, q, r : degrees of B-Splinevolume
479
+ U, V, W : knot vectors
480
+ P : control points (x, y, z, w)
481
+ u, v, w : parametric values
482
+
483
+ returns point for given (u, v, w)
484
+ */
485
+ function calcVolumePoint( p, q, r, U, V, W, P, u, v, w, target ) {
486
+
487
+ const uspan = findSpan( p, u, U );
488
+ const vspan = findSpan( q, v, V );
489
+ const wspan = findSpan( r, w, W );
490
+ const Nu = calcBasisFunctions( uspan, u, p, U );
491
+ const Nv = calcBasisFunctions( vspan, v, q, V );
492
+ const Nw = calcBasisFunctions( wspan, w, r, W );
493
+ const temp = [];
494
+
495
+ for ( let m = 0; m <= r; ++ m ) {
496
+
497
+ temp[ m ] = [];
498
+
499
+ for ( let l = 0; l <= q; ++ l ) {
500
+
501
+ temp[ m ][ l ] = new Vector4( 0, 0, 0, 0 );
502
+ for ( let k = 0; k <= p; ++ k ) {
503
+
504
+ const point = P[ uspan - p + k ][ vspan - q + l ][ wspan - r + m ].clone();
505
+ const w = point.w;
506
+ point.x *= w;
507
+ point.y *= w;
508
+ point.z *= w;
509
+ temp[ m ][ l ].add( point.multiplyScalar( Nu[ k ] ) );
510
+
511
+ }
512
+
513
+ }
514
+
515
+ }
516
+
517
+ const Sw = new Vector4( 0, 0, 0, 0 );
518
+ for ( let m = 0; m <= r; ++ m ) {
519
+
520
+ for ( let l = 0; l <= q; ++ l ) {
521
+
522
+ Sw.add( temp[ m ][ l ].multiplyScalar( Nw[ m ] ).multiplyScalar( Nv[ l ] ) );
523
+
524
+ }
525
+
526
+ }
527
+
528
+ Sw.divideScalar( Sw.w );
529
+ target.set( Sw.x, Sw.y, Sw.z );
530
+
531
+ }
532
+
533
+
534
+ export {
535
+ findSpan,
536
+ calcBasisFunctions,
537
+ calcBSplinePoint,
538
+ calcBasisFunctionDerivatives,
539
+ calcBSplineDerivatives,
540
+ calcKoverI,
541
+ calcRationalCurveDerivatives,
542
+ calcNURBSDerivatives,
543
+ calcSurfacePoint,
544
+ calcVolumePoint,
545
+ };
libs/three.js/0.172.0/jsm/curves/NURBSVolume.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Vector4
3
+ } from 'three';
4
+ import * as NURBSUtils from '../curves/NURBSUtils.js';
5
+
6
+ /**
7
+ * NURBS volume object
8
+ *
9
+ * Implementation is based on (x, y, z [, w=1]]) control points with w=weight.
10
+ **/
11
+
12
+ class NURBSVolume {
13
+
14
+ constructor( degree1, degree2, degree3, knots1, knots2, knots3 /* arrays of reals */, controlPoints /* array^3 of Vector(2|3|4) */ ) {
15
+
16
+ this.degree1 = degree1;
17
+ this.degree2 = degree2;
18
+ this.degree3 = degree3;
19
+ this.knots1 = knots1;
20
+ this.knots2 = knots2;
21
+ this.knots3 = knots3;
22
+ this.controlPoints = [];
23
+
24
+ const len1 = knots1.length - degree1 - 1;
25
+ const len2 = knots2.length - degree2 - 1;
26
+ const len3 = knots3.length - degree3 - 1;
27
+
28
+ // ensure Vector4 for control points
29
+ for ( let i = 0; i < len1; ++ i ) {
30
+
31
+ this.controlPoints[ i ] = [];
32
+
33
+ for ( let j = 0; j < len2; ++ j ) {
34
+
35
+ this.controlPoints[ i ][ j ] = [];
36
+
37
+ for ( let k = 0; k < len3; ++ k ) {
38
+
39
+ const point = controlPoints[ i ][ j ][ k ];
40
+ this.controlPoints[ i ][ j ][ k ] = new Vector4( point.x, point.y, point.z, point.w );
41
+
42
+ }
43
+
44
+ }
45
+
46
+ }
47
+
48
+ }
49
+
50
+ getPoint( t1, t2, t3, target ) {
51
+
52
+ const u = this.knots1[ 0 ] + t1 * ( this.knots1[ this.knots1.length - 1 ] - this.knots1[ 0 ] ); // linear mapping t1->u
53
+ const v = this.knots2[ 0 ] + t2 * ( this.knots2[ this.knots2.length - 1 ] - this.knots2[ 0 ] ); // linear mapping t2->v
54
+ const w = this.knots3[ 0 ] + t3 * ( this.knots3[ this.knots3.length - 1 ] - this.knots3[ 0 ] ); // linear mapping t3->w
55
+
56
+ NURBSUtils.calcVolumePoint( this.degree1, this.degree2, this.degree3, this.knots1, this.knots2, this.knots3, this.controlPoints, u, v, w, target );
57
+
58
+ }
59
+
60
+ }
61
+
62
+ export { NURBSVolume };
libs/three.js/0.172.0/jsm/effects/AnaglyphEffect.js ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ LinearFilter,
3
+ Matrix3,
4
+ NearestFilter,
5
+ RGBAFormat,
6
+ ShaderMaterial,
7
+ StereoCamera,
8
+ WebGLRenderTarget
9
+ } from 'three';
10
+ import { FullScreenQuad } from '../postprocessing/Pass.js';
11
+
12
+ class AnaglyphEffect {
13
+
14
+ constructor( renderer, width = 512, height = 512 ) {
15
+
16
+ // Dubois matrices from https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.7.6968&rep=rep1&type=pdf#page=4
17
+
18
+ this.colorMatrixLeft = new Matrix3().fromArray( [
19
+ 0.456100, - 0.0400822, - 0.0152161,
20
+ 0.500484, - 0.0378246, - 0.0205971,
21
+ 0.176381, - 0.0157589, - 0.00546856
22
+ ] );
23
+
24
+ this.colorMatrixRight = new Matrix3().fromArray( [
25
+ - 0.0434706, 0.378476, - 0.0721527,
26
+ - 0.0879388, 0.73364, - 0.112961,
27
+ - 0.00155529, - 0.0184503, 1.2264
28
+ ] );
29
+
30
+ const _stereo = new StereoCamera();
31
+
32
+ const _params = { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat };
33
+
34
+ const _renderTargetL = new WebGLRenderTarget( width, height, _params );
35
+ const _renderTargetR = new WebGLRenderTarget( width, height, _params );
36
+
37
+ const _material = new ShaderMaterial( {
38
+
39
+ uniforms: {
40
+
41
+ 'mapLeft': { value: _renderTargetL.texture },
42
+ 'mapRight': { value: _renderTargetR.texture },
43
+
44
+ 'colorMatrixLeft': { value: this.colorMatrixLeft },
45
+ 'colorMatrixRight': { value: this.colorMatrixRight }
46
+
47
+ },
48
+
49
+ vertexShader: [
50
+
51
+ 'varying vec2 vUv;',
52
+
53
+ 'void main() {',
54
+
55
+ ' vUv = vec2( uv.x, uv.y );',
56
+ ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
57
+
58
+ '}'
59
+
60
+ ].join( '\n' ),
61
+
62
+ fragmentShader: [
63
+
64
+ 'uniform sampler2D mapLeft;',
65
+ 'uniform sampler2D mapRight;',
66
+ 'varying vec2 vUv;',
67
+
68
+ 'uniform mat3 colorMatrixLeft;',
69
+ 'uniform mat3 colorMatrixRight;',
70
+
71
+ 'void main() {',
72
+
73
+ ' vec2 uv = vUv;',
74
+
75
+ ' vec4 colorL = texture2D( mapLeft, uv );',
76
+ ' vec4 colorR = texture2D( mapRight, uv );',
77
+
78
+ ' vec3 color = clamp(',
79
+ ' colorMatrixLeft * colorL.rgb +',
80
+ ' colorMatrixRight * colorR.rgb, 0., 1. );',
81
+
82
+ ' gl_FragColor = vec4(',
83
+ ' color.r, color.g, color.b,',
84
+ ' max( colorL.a, colorR.a ) );',
85
+
86
+ ' #include <tonemapping_fragment>',
87
+ ' #include <colorspace_fragment>',
88
+
89
+ '}'
90
+
91
+ ].join( '\n' )
92
+
93
+ } );
94
+
95
+ const _quad = new FullScreenQuad( _material );
96
+
97
+ this.setSize = function ( width, height ) {
98
+
99
+ renderer.setSize( width, height );
100
+
101
+ const pixelRatio = renderer.getPixelRatio();
102
+
103
+ _renderTargetL.setSize( width * pixelRatio, height * pixelRatio );
104
+ _renderTargetR.setSize( width * pixelRatio, height * pixelRatio );
105
+
106
+ };
107
+
108
+ this.render = function ( scene, camera ) {
109
+
110
+ const currentRenderTarget = renderer.getRenderTarget();
111
+
112
+ if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
113
+
114
+ if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
115
+
116
+ _stereo.update( camera );
117
+
118
+ renderer.setRenderTarget( _renderTargetL );
119
+ renderer.clear();
120
+ renderer.render( scene, _stereo.cameraL );
121
+
122
+ renderer.setRenderTarget( _renderTargetR );
123
+ renderer.clear();
124
+ renderer.render( scene, _stereo.cameraR );
125
+
126
+ renderer.setRenderTarget( null );
127
+ _quad.render( renderer );
128
+
129
+ renderer.setRenderTarget( currentRenderTarget );
130
+
131
+ };
132
+
133
+ this.dispose = function () {
134
+
135
+ _renderTargetL.dispose();
136
+ _renderTargetR.dispose();
137
+
138
+ _material.dispose();
139
+ _quad.dispose();
140
+
141
+ };
142
+
143
+ }
144
+
145
+ }
146
+
147
+ export { AnaglyphEffect };
libs/three.js/0.172.0/jsm/effects/AsciiEffect.js ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Ascii generation is based on https://github.com/hassadee/jsascii/blob/master/jsascii.js
3
+ *
4
+ * 16 April 2012 - @blurspline
5
+ */
6
+
7
+ class AsciiEffect {
8
+
9
+ constructor( renderer, charSet = ' .:-=+*#%@', options = {} ) {
10
+
11
+ // ' .,:;=|iI+hHOE#`$';
12
+ // darker bolder character set from https://github.com/saw/Canvas-ASCII-Art/
13
+ // ' .\'`^",:;Il!i~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$'.split('');
14
+
15
+ // Some ASCII settings
16
+
17
+ const fResolution = options[ 'resolution' ] || 0.15; // Higher for more details
18
+ const iScale = options[ 'scale' ] || 1;
19
+ const bColor = options[ 'color' ] || false; // nice but slows down rendering!
20
+ const bAlpha = options[ 'alpha' ] || false; // Transparency
21
+ const bBlock = options[ 'block' ] || false; // blocked characters. like good O dos
22
+ const bInvert = options[ 'invert' ] || false; // black is white, white is black
23
+ const strResolution = options[ 'strResolution' ] || 'low';
24
+
25
+ let width, height;
26
+
27
+ const domElement = document.createElement( 'div' );
28
+ domElement.style.cursor = 'default';
29
+
30
+ const oAscii = document.createElement( 'table' );
31
+ domElement.appendChild( oAscii );
32
+
33
+ let iWidth, iHeight;
34
+ let oImg;
35
+
36
+ this.setSize = function ( w, h ) {
37
+
38
+ width = w;
39
+ height = h;
40
+
41
+ renderer.setSize( w, h );
42
+
43
+ initAsciiSize();
44
+
45
+ };
46
+
47
+
48
+ this.render = function ( scene, camera ) {
49
+
50
+ renderer.render( scene, camera );
51
+ asciifyImage( oAscii );
52
+
53
+ };
54
+
55
+ this.domElement = domElement;
56
+
57
+
58
+ // Throw in ascii library from https://github.com/hassadee/jsascii/blob/master/jsascii.js (MIT License)
59
+
60
+ function initAsciiSize() {
61
+
62
+ iWidth = Math.floor( width * fResolution );
63
+ iHeight = Math.floor( height * fResolution );
64
+
65
+ oCanvas.width = iWidth;
66
+ oCanvas.height = iHeight;
67
+ // oCanvas.style.display = "none";
68
+ // oCanvas.style.width = iWidth;
69
+ // oCanvas.style.height = iHeight;
70
+
71
+ oImg = renderer.domElement;
72
+
73
+ if ( oImg.style.backgroundColor ) {
74
+
75
+ oAscii.rows[ 0 ].cells[ 0 ].style.backgroundColor = oImg.style.backgroundColor;
76
+ oAscii.rows[ 0 ].cells[ 0 ].style.color = oImg.style.color;
77
+
78
+ }
79
+
80
+ oAscii.cellSpacing = 0;
81
+ oAscii.cellPadding = 0;
82
+
83
+ const oStyle = oAscii.style;
84
+ oStyle.whiteSpace = 'pre';
85
+ oStyle.margin = '0px';
86
+ oStyle.padding = '0px';
87
+ oStyle.letterSpacing = fLetterSpacing + 'px';
88
+ oStyle.fontFamily = strFont;
89
+ oStyle.fontSize = fFontSize + 'px';
90
+ oStyle.lineHeight = fLineHeight + 'px';
91
+ oStyle.textAlign = 'left';
92
+ oStyle.textDecoration = 'none';
93
+
94
+ }
95
+
96
+
97
+ const aDefaultCharList = ( ' .,:;i1tfLCG08@' ).split( '' );
98
+ const aDefaultColorCharList = ( ' CGO08@' ).split( '' );
99
+ const strFont = 'courier new, monospace';
100
+
101
+ const oCanvasImg = renderer.domElement;
102
+
103
+ const oCanvas = document.createElement( 'canvas' );
104
+ if ( ! oCanvas.getContext ) {
105
+
106
+ return;
107
+
108
+ }
109
+
110
+ const oCtx = oCanvas.getContext( '2d' );
111
+ if ( ! oCtx.getImageData ) {
112
+
113
+ return;
114
+
115
+ }
116
+
117
+ let aCharList = ( bColor ? aDefaultColorCharList : aDefaultCharList );
118
+
119
+ if ( charSet ) aCharList = charSet;
120
+
121
+ // Setup dom
122
+
123
+ const fFontSize = ( 2 / fResolution ) * iScale;
124
+ const fLineHeight = ( 2 / fResolution ) * iScale;
125
+
126
+ // adjust letter-spacing for all combinations of scale and resolution to get it to fit the image width.
127
+
128
+ let fLetterSpacing = 0;
129
+
130
+ if ( strResolution == 'low' ) {
131
+
132
+ switch ( iScale ) {
133
+
134
+ case 1 : fLetterSpacing = - 1; break;
135
+ case 2 :
136
+ case 3 : fLetterSpacing = - 2.1; break;
137
+ case 4 : fLetterSpacing = - 3.1; break;
138
+ case 5 : fLetterSpacing = - 4.15; break;
139
+
140
+ }
141
+
142
+ }
143
+
144
+ if ( strResolution == 'medium' ) {
145
+
146
+ switch ( iScale ) {
147
+
148
+ case 1 : fLetterSpacing = 0; break;
149
+ case 2 : fLetterSpacing = - 1; break;
150
+ case 3 : fLetterSpacing = - 1.04; break;
151
+ case 4 :
152
+ case 5 : fLetterSpacing = - 2.1; break;
153
+
154
+ }
155
+
156
+ }
157
+
158
+ if ( strResolution == 'high' ) {
159
+
160
+ switch ( iScale ) {
161
+
162
+ case 1 :
163
+ case 2 : fLetterSpacing = 0; break;
164
+ case 3 :
165
+ case 4 :
166
+ case 5 : fLetterSpacing = - 1; break;
167
+
168
+ }
169
+
170
+ }
171
+
172
+
173
+ // can't get a span or div to flow like an img element, but a table works?
174
+
175
+
176
+ // convert img element to ascii
177
+
178
+ function asciifyImage( oAscii ) {
179
+
180
+ oCtx.clearRect( 0, 0, iWidth, iHeight );
181
+ oCtx.drawImage( oCanvasImg, 0, 0, iWidth, iHeight );
182
+ const oImgData = oCtx.getImageData( 0, 0, iWidth, iHeight ).data;
183
+
184
+ // Coloring loop starts now
185
+ let strChars = '';
186
+
187
+ // console.time('rendering');
188
+
189
+ for ( let y = 0; y < iHeight; y += 2 ) {
190
+
191
+ for ( let x = 0; x < iWidth; x ++ ) {
192
+
193
+ const iOffset = ( y * iWidth + x ) * 4;
194
+
195
+ const iRed = oImgData[ iOffset ];
196
+ const iGreen = oImgData[ iOffset + 1 ];
197
+ const iBlue = oImgData[ iOffset + 2 ];
198
+ const iAlpha = oImgData[ iOffset + 3 ];
199
+ let iCharIdx;
200
+
201
+ let fBrightness;
202
+
203
+ fBrightness = ( 0.3 * iRed + 0.59 * iGreen + 0.11 * iBlue ) / 255;
204
+ // fBrightness = (0.3*iRed + 0.5*iGreen + 0.3*iBlue) / 255;
205
+
206
+ if ( iAlpha == 0 ) {
207
+
208
+ // should calculate alpha instead, but quick hack :)
209
+ //fBrightness *= (iAlpha / 255);
210
+ fBrightness = 1;
211
+
212
+ }
213
+
214
+ iCharIdx = Math.floor( ( 1 - fBrightness ) * ( aCharList.length - 1 ) );
215
+
216
+ if ( bInvert ) {
217
+
218
+ iCharIdx = aCharList.length - iCharIdx - 1;
219
+
220
+ }
221
+
222
+ // good for debugging
223
+ //fBrightness = Math.floor(fBrightness * 10);
224
+ //strThisChar = fBrightness;
225
+
226
+ let strThisChar = aCharList[ iCharIdx ];
227
+
228
+ if ( strThisChar === undefined || strThisChar == ' ' )
229
+ strThisChar = '&nbsp;';
230
+
231
+ if ( bColor ) {
232
+
233
+ strChars += '<span style=\''
234
+ + 'color:rgb(' + iRed + ',' + iGreen + ',' + iBlue + ');'
235
+ + ( bBlock ? 'background-color:rgb(' + iRed + ',' + iGreen + ',' + iBlue + ');' : '' )
236
+ + ( bAlpha ? 'opacity:' + ( iAlpha / 255 ) + ';' : '' )
237
+ + '\'>' + strThisChar + '</span>';
238
+
239
+ } else {
240
+
241
+ strChars += strThisChar;
242
+
243
+ }
244
+
245
+ }
246
+
247
+ strChars += '<br/>';
248
+
249
+ }
250
+
251
+ oAscii.innerHTML = `<tr><td style="display:block;width:${width}px;height:${height}px;overflow:hidden">${strChars}</td></tr>`;
252
+
253
+ // console.timeEnd('rendering');
254
+
255
+ // return oAscii;
256
+
257
+ }
258
+
259
+ }
260
+
261
+ }
262
+
263
+ export { AsciiEffect };
libs/three.js/0.172.0/jsm/effects/OutlineEffect.js ADDED
@@ -0,0 +1,539 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ BackSide,
3
+ Color,
4
+ ShaderMaterial,
5
+ UniformsLib,
6
+ UniformsUtils
7
+ } from 'three';
8
+
9
+ /**
10
+ * Reference: https://en.wikipedia.org/wiki/Cel_shading
11
+ *
12
+ * API
13
+ *
14
+ * 1. Traditional
15
+ *
16
+ * const effect = new OutlineEffect( renderer );
17
+ *
18
+ * function render() {
19
+ *
20
+ * effect.render( scene, camera );
21
+ *
22
+ * }
23
+ *
24
+ * 2. VR compatible
25
+ *
26
+ * const effect = new OutlineEffect( renderer );
27
+ * let renderingOutline = false;
28
+ *
29
+ * scene.onAfterRender = function () {
30
+ *
31
+ * if ( renderingOutline ) return;
32
+ *
33
+ * renderingOutline = true;
34
+ *
35
+ * effect.renderOutline( scene, camera );
36
+ *
37
+ * renderingOutline = false;
38
+ *
39
+ * };
40
+ *
41
+ * function render() {
42
+ *
43
+ * renderer.render( scene, camera );
44
+ *
45
+ * }
46
+ *
47
+ * // How to set default outline parameters
48
+ * new OutlineEffect( renderer, {
49
+ * defaultThickness: 0.01,
50
+ * defaultColor: [ 0, 0, 0 ],
51
+ * defaultAlpha: 0.8,
52
+ * defaultKeepAlive: true // keeps outline material in cache even if material is removed from scene
53
+ * } );
54
+ *
55
+ * // How to set outline parameters for each material
56
+ * material.userData.outlineParameters = {
57
+ * thickness: 0.01,
58
+ * color: [ 0, 0, 0 ],
59
+ * alpha: 0.8,
60
+ * visible: true,
61
+ * keepAlive: true
62
+ * };
63
+ */
64
+
65
+ class OutlineEffect {
66
+
67
+ constructor( renderer, parameters = {} ) {
68
+
69
+ this.enabled = true;
70
+
71
+ const defaultThickness = parameters.defaultThickness !== undefined ? parameters.defaultThickness : 0.003;
72
+ const defaultColor = new Color().fromArray( parameters.defaultColor !== undefined ? parameters.defaultColor : [ 0, 0, 0 ] );
73
+ const defaultAlpha = parameters.defaultAlpha !== undefined ? parameters.defaultAlpha : 1.0;
74
+ const defaultKeepAlive = parameters.defaultKeepAlive !== undefined ? parameters.defaultKeepAlive : false;
75
+
76
+ // object.material.uuid -> outlineMaterial or
77
+ // object.material[ n ].uuid -> outlineMaterial
78
+ // save at the outline material creation and release
79
+ // if it's unused removeThresholdCount frames
80
+ // unless keepAlive is true.
81
+ const cache = {};
82
+
83
+ const removeThresholdCount = 60;
84
+
85
+ // outlineMaterial.uuid -> object.material or
86
+ // outlineMaterial.uuid -> object.material[ n ]
87
+ // save before render and release after render.
88
+ const originalMaterials = {};
89
+
90
+ // object.uuid -> originalOnBeforeRender
91
+ // save before render and release after render.
92
+ const originalOnBeforeRenders = {};
93
+
94
+ //this.cache = cache; // for debug
95
+
96
+ const uniformsOutline = {
97
+ outlineThickness: { value: defaultThickness },
98
+ outlineColor: { value: defaultColor },
99
+ outlineAlpha: { value: defaultAlpha }
100
+ };
101
+
102
+ const vertexShader = [
103
+ '#include <common>',
104
+ '#include <uv_pars_vertex>',
105
+ '#include <displacementmap_pars_vertex>',
106
+ '#include <fog_pars_vertex>',
107
+ '#include <morphtarget_pars_vertex>',
108
+ '#include <skinning_pars_vertex>',
109
+ '#include <logdepthbuf_pars_vertex>',
110
+ '#include <clipping_planes_pars_vertex>',
111
+
112
+ 'uniform float outlineThickness;',
113
+
114
+ 'vec4 calculateOutline( vec4 pos, vec3 normal, vec4 skinned ) {',
115
+ ' float thickness = outlineThickness;',
116
+ ' const float ratio = 1.0;', // TODO: support outline thickness ratio for each vertex
117
+ ' vec4 pos2 = projectionMatrix * modelViewMatrix * vec4( skinned.xyz + normal, 1.0 );',
118
+ // NOTE: subtract pos2 from pos because BackSide objectNormal is negative
119
+ ' vec4 norm = normalize( pos - pos2 );',
120
+ ' return pos + norm * thickness * pos.w * ratio;',
121
+ '}',
122
+
123
+ 'void main() {',
124
+
125
+ ' #include <uv_vertex>',
126
+
127
+ ' #include <beginnormal_vertex>',
128
+ ' #include <morphnormal_vertex>',
129
+ ' #include <skinbase_vertex>',
130
+ ' #include <skinnormal_vertex>',
131
+
132
+ ' #include <begin_vertex>',
133
+ ' #include <morphtarget_vertex>',
134
+ ' #include <skinning_vertex>',
135
+ ' #include <displacementmap_vertex>',
136
+ ' #include <project_vertex>',
137
+
138
+ ' vec3 outlineNormal = - objectNormal;', // the outline material is always rendered with BackSide
139
+
140
+ ' gl_Position = calculateOutline( gl_Position, outlineNormal, vec4( transformed, 1.0 ) );',
141
+
142
+ ' #include <logdepthbuf_vertex>',
143
+ ' #include <clipping_planes_vertex>',
144
+ ' #include <fog_vertex>',
145
+
146
+ '}',
147
+
148
+ ].join( '\n' );
149
+
150
+ const fragmentShader = [
151
+
152
+ '#include <common>',
153
+ '#include <fog_pars_fragment>',
154
+ '#include <logdepthbuf_pars_fragment>',
155
+ '#include <clipping_planes_pars_fragment>',
156
+
157
+ 'uniform vec3 outlineColor;',
158
+ 'uniform float outlineAlpha;',
159
+
160
+ 'void main() {',
161
+
162
+ ' #include <clipping_planes_fragment>',
163
+ ' #include <logdepthbuf_fragment>',
164
+
165
+ ' gl_FragColor = vec4( outlineColor, outlineAlpha );',
166
+
167
+ ' #include <tonemapping_fragment>',
168
+ ' #include <colorspace_fragment>',
169
+ ' #include <fog_fragment>',
170
+ ' #include <premultiplied_alpha_fragment>',
171
+
172
+ '}'
173
+
174
+ ].join( '\n' );
175
+
176
+ function createMaterial() {
177
+
178
+ return new ShaderMaterial( {
179
+ type: 'OutlineEffect',
180
+ uniforms: UniformsUtils.merge( [
181
+ UniformsLib[ 'fog' ],
182
+ UniformsLib[ 'displacementmap' ],
183
+ uniformsOutline
184
+ ] ),
185
+ vertexShader: vertexShader,
186
+ fragmentShader: fragmentShader,
187
+ side: BackSide
188
+ } );
189
+
190
+ }
191
+
192
+ function getOutlineMaterialFromCache( originalMaterial ) {
193
+
194
+ let data = cache[ originalMaterial.uuid ];
195
+
196
+ if ( data === undefined ) {
197
+
198
+ data = {
199
+ material: createMaterial(),
200
+ used: true,
201
+ keepAlive: defaultKeepAlive,
202
+ count: 0
203
+ };
204
+
205
+ cache[ originalMaterial.uuid ] = data;
206
+
207
+ }
208
+
209
+ data.used = true;
210
+
211
+ return data.material;
212
+
213
+ }
214
+
215
+ function getOutlineMaterial( originalMaterial ) {
216
+
217
+ const outlineMaterial = getOutlineMaterialFromCache( originalMaterial );
218
+
219
+ originalMaterials[ outlineMaterial.uuid ] = originalMaterial;
220
+
221
+ updateOutlineMaterial( outlineMaterial, originalMaterial );
222
+
223
+ return outlineMaterial;
224
+
225
+ }
226
+
227
+ function isCompatible( object ) {
228
+
229
+ const geometry = object.geometry;
230
+ const hasNormals = ( geometry !== undefined ) && ( geometry.attributes.normal !== undefined );
231
+
232
+ return ( object.isMesh === true && object.material !== undefined && hasNormals === true );
233
+
234
+ }
235
+
236
+ function setOutlineMaterial( object ) {
237
+
238
+ if ( isCompatible( object ) === false ) return;
239
+
240
+ if ( Array.isArray( object.material ) ) {
241
+
242
+ for ( let i = 0, il = object.material.length; i < il; i ++ ) {
243
+
244
+ object.material[ i ] = getOutlineMaterial( object.material[ i ] );
245
+
246
+ }
247
+
248
+ } else {
249
+
250
+ object.material = getOutlineMaterial( object.material );
251
+
252
+ }
253
+
254
+ originalOnBeforeRenders[ object.uuid ] = object.onBeforeRender;
255
+ object.onBeforeRender = onBeforeRender;
256
+
257
+ }
258
+
259
+ function restoreOriginalMaterial( object ) {
260
+
261
+ if ( isCompatible( object ) === false ) return;
262
+
263
+ if ( Array.isArray( object.material ) ) {
264
+
265
+ for ( let i = 0, il = object.material.length; i < il; i ++ ) {
266
+
267
+ object.material[ i ] = originalMaterials[ object.material[ i ].uuid ];
268
+
269
+ }
270
+
271
+ } else {
272
+
273
+ object.material = originalMaterials[ object.material.uuid ];
274
+
275
+ }
276
+
277
+ object.onBeforeRender = originalOnBeforeRenders[ object.uuid ];
278
+
279
+ }
280
+
281
+ function onBeforeRender( renderer, scene, camera, geometry, material ) {
282
+
283
+ const originalMaterial = originalMaterials[ material.uuid ];
284
+
285
+ // just in case
286
+ if ( originalMaterial === undefined ) return;
287
+
288
+ updateUniforms( material, originalMaterial );
289
+
290
+ }
291
+
292
+ function updateUniforms( material, originalMaterial ) {
293
+
294
+ const outlineParameters = originalMaterial.userData.outlineParameters;
295
+
296
+ material.uniforms.outlineAlpha.value = originalMaterial.opacity;
297
+
298
+ if ( outlineParameters !== undefined ) {
299
+
300
+ if ( outlineParameters.thickness !== undefined ) material.uniforms.outlineThickness.value = outlineParameters.thickness;
301
+ if ( outlineParameters.color !== undefined ) material.uniforms.outlineColor.value.fromArray( outlineParameters.color );
302
+ if ( outlineParameters.alpha !== undefined ) material.uniforms.outlineAlpha.value = outlineParameters.alpha;
303
+
304
+ }
305
+
306
+ if ( originalMaterial.displacementMap ) {
307
+
308
+ material.uniforms.displacementMap.value = originalMaterial.displacementMap;
309
+ material.uniforms.displacementScale.value = originalMaterial.displacementScale;
310
+ material.uniforms.displacementBias.value = originalMaterial.displacementBias;
311
+
312
+ }
313
+
314
+ }
315
+
316
+ function updateOutlineMaterial( material, originalMaterial ) {
317
+
318
+ if ( material.name === 'invisible' ) return;
319
+
320
+ const outlineParameters = originalMaterial.userData.outlineParameters;
321
+
322
+ material.fog = originalMaterial.fog;
323
+ material.toneMapped = originalMaterial.toneMapped;
324
+ material.premultipliedAlpha = originalMaterial.premultipliedAlpha;
325
+ material.displacementMap = originalMaterial.displacementMap;
326
+
327
+ if ( outlineParameters !== undefined ) {
328
+
329
+ if ( originalMaterial.visible === false ) {
330
+
331
+ material.visible = false;
332
+
333
+ } else {
334
+
335
+ material.visible = ( outlineParameters.visible !== undefined ) ? outlineParameters.visible : true;
336
+
337
+ }
338
+
339
+ material.transparent = ( outlineParameters.alpha !== undefined && outlineParameters.alpha < 1.0 ) ? true : originalMaterial.transparent;
340
+
341
+ if ( outlineParameters.keepAlive !== undefined ) cache[ originalMaterial.uuid ].keepAlive = outlineParameters.keepAlive;
342
+
343
+ } else {
344
+
345
+ material.transparent = originalMaterial.transparent;
346
+ material.visible = originalMaterial.visible;
347
+
348
+ }
349
+
350
+ if ( originalMaterial.wireframe === true || originalMaterial.depthTest === false ) material.visible = false;
351
+
352
+ if ( originalMaterial.clippingPlanes ) {
353
+
354
+ material.clipping = true;
355
+
356
+ material.clippingPlanes = originalMaterial.clippingPlanes;
357
+ material.clipIntersection = originalMaterial.clipIntersection;
358
+ material.clipShadows = originalMaterial.clipShadows;
359
+
360
+ }
361
+
362
+ material.version = originalMaterial.version; // update outline material if necessary
363
+
364
+ }
365
+
366
+ function cleanupCache() {
367
+
368
+ let keys;
369
+
370
+ // clear originalMaterials
371
+ keys = Object.keys( originalMaterials );
372
+
373
+ for ( let i = 0, il = keys.length; i < il; i ++ ) {
374
+
375
+ originalMaterials[ keys[ i ] ] = undefined;
376
+
377
+ }
378
+
379
+ // clear originalOnBeforeRenders
380
+ keys = Object.keys( originalOnBeforeRenders );
381
+
382
+ for ( let i = 0, il = keys.length; i < il; i ++ ) {
383
+
384
+ originalOnBeforeRenders[ keys[ i ] ] = undefined;
385
+
386
+ }
387
+
388
+ // remove unused outlineMaterial from cache
389
+ keys = Object.keys( cache );
390
+
391
+ for ( let i = 0, il = keys.length; i < il; i ++ ) {
392
+
393
+ const key = keys[ i ];
394
+
395
+ if ( cache[ key ].used === false ) {
396
+
397
+ cache[ key ].count ++;
398
+
399
+ if ( cache[ key ].keepAlive === false && cache[ key ].count > removeThresholdCount ) {
400
+
401
+ delete cache[ key ];
402
+
403
+ }
404
+
405
+ } else {
406
+
407
+ cache[ key ].used = false;
408
+ cache[ key ].count = 0;
409
+
410
+ }
411
+
412
+ }
413
+
414
+ }
415
+
416
+ this.render = function ( scene, camera ) {
417
+
418
+ if ( this.enabled === false ) {
419
+
420
+ renderer.render( scene, camera );
421
+ return;
422
+
423
+ }
424
+
425
+ const currentAutoClear = renderer.autoClear;
426
+ renderer.autoClear = this.autoClear;
427
+
428
+ renderer.render( scene, camera );
429
+
430
+ renderer.autoClear = currentAutoClear;
431
+
432
+ this.renderOutline( scene, camera );
433
+
434
+ };
435
+
436
+ this.renderOutline = function ( scene, camera ) {
437
+
438
+ const currentAutoClear = renderer.autoClear;
439
+ const currentSceneAutoUpdate = scene.matrixWorldAutoUpdate;
440
+ const currentSceneBackground = scene.background;
441
+ const currentShadowMapEnabled = renderer.shadowMap.enabled;
442
+
443
+ scene.matrixWorldAutoUpdate = false;
444
+ scene.background = null;
445
+ renderer.autoClear = false;
446
+ renderer.shadowMap.enabled = false;
447
+
448
+ scene.traverse( setOutlineMaterial );
449
+
450
+ renderer.render( scene, camera );
451
+
452
+ scene.traverse( restoreOriginalMaterial );
453
+
454
+ cleanupCache();
455
+
456
+ scene.matrixWorldAutoUpdate = currentSceneAutoUpdate;
457
+ scene.background = currentSceneBackground;
458
+ renderer.autoClear = currentAutoClear;
459
+ renderer.shadowMap.enabled = currentShadowMapEnabled;
460
+
461
+ };
462
+
463
+ /*
464
+ * See #9918
465
+ *
466
+ * The following property copies and wrapper methods enable
467
+ * OutlineEffect to be called from other *Effect, like
468
+ *
469
+ * effect = new StereoEffect( new OutlineEffect( renderer ) );
470
+ *
471
+ * function render () {
472
+ *
473
+ * effect.render( scene, camera );
474
+ *
475
+ * }
476
+ */
477
+ this.autoClear = renderer.autoClear;
478
+ this.domElement = renderer.domElement;
479
+ this.shadowMap = renderer.shadowMap;
480
+
481
+ this.clear = function ( color, depth, stencil ) {
482
+
483
+ renderer.clear( color, depth, stencil );
484
+
485
+ };
486
+
487
+ this.getPixelRatio = function () {
488
+
489
+ return renderer.getPixelRatio();
490
+
491
+ };
492
+
493
+ this.setPixelRatio = function ( value ) {
494
+
495
+ renderer.setPixelRatio( value );
496
+
497
+ };
498
+
499
+ this.getSize = function ( target ) {
500
+
501
+ return renderer.getSize( target );
502
+
503
+ };
504
+
505
+ this.setSize = function ( width, height, updateStyle ) {
506
+
507
+ renderer.setSize( width, height, updateStyle );
508
+
509
+ };
510
+
511
+ this.setViewport = function ( x, y, width, height ) {
512
+
513
+ renderer.setViewport( x, y, width, height );
514
+
515
+ };
516
+
517
+ this.setScissor = function ( x, y, width, height ) {
518
+
519
+ renderer.setScissor( x, y, width, height );
520
+
521
+ };
522
+
523
+ this.setScissorTest = function ( boolean ) {
524
+
525
+ renderer.setScissorTest( boolean );
526
+
527
+ };
528
+
529
+ this.setRenderTarget = function ( renderTarget ) {
530
+
531
+ renderer.setRenderTarget( renderTarget );
532
+
533
+ };
534
+
535
+ }
536
+
537
+ }
538
+
539
+ export { OutlineEffect };
libs/three.js/0.172.0/jsm/effects/ParallaxBarrierEffect.js ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ LinearFilter,
3
+ NearestFilter,
4
+ RGBAFormat,
5
+ ShaderMaterial,
6
+ StereoCamera,
7
+ WebGLRenderTarget
8
+ } from 'three';
9
+ import { FullScreenQuad } from '../postprocessing/Pass.js';
10
+
11
+ class ParallaxBarrierEffect {
12
+
13
+ constructor( renderer ) {
14
+
15
+ const _stereo = new StereoCamera();
16
+
17
+ const _params = { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat };
18
+
19
+ const _renderTargetL = new WebGLRenderTarget( 512, 512, _params );
20
+ const _renderTargetR = new WebGLRenderTarget( 512, 512, _params );
21
+
22
+ const _material = new ShaderMaterial( {
23
+
24
+ uniforms: {
25
+
26
+ 'mapLeft': { value: _renderTargetL.texture },
27
+ 'mapRight': { value: _renderTargetR.texture }
28
+
29
+ },
30
+
31
+ vertexShader: [
32
+
33
+ 'varying vec2 vUv;',
34
+
35
+ 'void main() {',
36
+
37
+ ' vUv = vec2( uv.x, uv.y );',
38
+ ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
39
+
40
+ '}'
41
+
42
+ ].join( '\n' ),
43
+
44
+ fragmentShader: [
45
+
46
+ 'uniform sampler2D mapLeft;',
47
+ 'uniform sampler2D mapRight;',
48
+ 'varying vec2 vUv;',
49
+
50
+ 'void main() {',
51
+
52
+ ' vec2 uv = vUv;',
53
+
54
+ ' if ( ( mod( gl_FragCoord.y, 2.0 ) ) > 1.00 ) {',
55
+
56
+ ' gl_FragColor = texture2D( mapLeft, uv );',
57
+
58
+ ' } else {',
59
+
60
+ ' gl_FragColor = texture2D( mapRight, uv );',
61
+
62
+ ' }',
63
+
64
+ ' #include <tonemapping_fragment>',
65
+ ' #include <colorspace_fragment>',
66
+
67
+ '}'
68
+
69
+ ].join( '\n' )
70
+
71
+ } );
72
+
73
+ const _quad = new FullScreenQuad( _material );
74
+
75
+ this.setSize = function ( width, height ) {
76
+
77
+ renderer.setSize( width, height );
78
+
79
+ const pixelRatio = renderer.getPixelRatio();
80
+
81
+ _renderTargetL.setSize( width * pixelRatio, height * pixelRatio );
82
+ _renderTargetR.setSize( width * pixelRatio, height * pixelRatio );
83
+
84
+ };
85
+
86
+ this.render = function ( scene, camera ) {
87
+
88
+ const currentRenderTarget = renderer.getRenderTarget();
89
+
90
+ if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
91
+
92
+ if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
93
+
94
+ _stereo.update( camera );
95
+
96
+ renderer.setRenderTarget( _renderTargetL );
97
+ renderer.clear();
98
+ renderer.render( scene, _stereo.cameraL );
99
+
100
+ renderer.setRenderTarget( _renderTargetR );
101
+ renderer.clear();
102
+ renderer.render( scene, _stereo.cameraR );
103
+
104
+ renderer.setRenderTarget( null );
105
+ _quad.render( renderer );
106
+
107
+ renderer.setRenderTarget( currentRenderTarget );
108
+
109
+ };
110
+
111
+ this.dispose = function () {
112
+
113
+ _renderTargetL.dispose();
114
+ _renderTargetR.dispose();
115
+
116
+ _material.dispose();
117
+ _quad.dispose();
118
+
119
+ };
120
+
121
+ }
122
+
123
+ }
124
+
125
+ export { ParallaxBarrierEffect };
libs/three.js/0.172.0/jsm/effects/PeppersGhostEffect.js ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ PerspectiveCamera,
3
+ Quaternion,
4
+ Vector3
5
+ } from 'three';
6
+
7
+ /**
8
+ * peppers ghost effect based on http://www.instructables.com/id/Reflective-Prism/?ALLSTEPS
9
+ */
10
+
11
+ class PeppersGhostEffect {
12
+
13
+ constructor( renderer ) {
14
+
15
+ const scope = this;
16
+
17
+ scope.cameraDistance = 15;
18
+ scope.reflectFromAbove = false;
19
+
20
+ // Internals
21
+ let _halfWidth, _width, _height;
22
+
23
+ const _cameraF = new PerspectiveCamera(); //front
24
+ const _cameraB = new PerspectiveCamera(); //back
25
+ const _cameraL = new PerspectiveCamera(); //left
26
+ const _cameraR = new PerspectiveCamera(); //right
27
+
28
+ const _position = new Vector3();
29
+ const _quaternion = new Quaternion();
30
+ const _scale = new Vector3();
31
+
32
+ // Initialization
33
+ renderer.autoClear = false;
34
+
35
+ this.setSize = function ( width, height ) {
36
+
37
+ _halfWidth = width / 2;
38
+ if ( width < height ) {
39
+
40
+ _width = width / 3;
41
+ _height = width / 3;
42
+
43
+ } else {
44
+
45
+ _width = height / 3;
46
+ _height = height / 3;
47
+
48
+ }
49
+
50
+ renderer.setSize( width, height );
51
+
52
+ };
53
+
54
+ this.render = function ( scene, camera ) {
55
+
56
+ if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
57
+
58
+ if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
59
+
60
+ camera.matrixWorld.decompose( _position, _quaternion, _scale );
61
+
62
+ // front
63
+ _cameraF.position.copy( _position );
64
+ _cameraF.quaternion.copy( _quaternion );
65
+ _cameraF.translateZ( scope.cameraDistance );
66
+ _cameraF.lookAt( scene.position );
67
+
68
+ // back
69
+ _cameraB.position.copy( _position );
70
+ _cameraB.quaternion.copy( _quaternion );
71
+ _cameraB.translateZ( - ( scope.cameraDistance ) );
72
+ _cameraB.lookAt( scene.position );
73
+ _cameraB.rotation.z += 180 * ( Math.PI / 180 );
74
+
75
+ // left
76
+ _cameraL.position.copy( _position );
77
+ _cameraL.quaternion.copy( _quaternion );
78
+ _cameraL.translateX( - ( scope.cameraDistance ) );
79
+ _cameraL.lookAt( scene.position );
80
+ _cameraL.rotation.x += 90 * ( Math.PI / 180 );
81
+
82
+ // right
83
+ _cameraR.position.copy( _position );
84
+ _cameraR.quaternion.copy( _quaternion );
85
+ _cameraR.translateX( scope.cameraDistance );
86
+ _cameraR.lookAt( scene.position );
87
+ _cameraR.rotation.x += 90 * ( Math.PI / 180 );
88
+
89
+
90
+ renderer.clear();
91
+ renderer.setScissorTest( true );
92
+
93
+ renderer.setScissor( _halfWidth - ( _width / 2 ), ( _height * 2 ), _width, _height );
94
+ renderer.setViewport( _halfWidth - ( _width / 2 ), ( _height * 2 ), _width, _height );
95
+
96
+ if ( scope.reflectFromAbove ) {
97
+
98
+ renderer.render( scene, _cameraB );
99
+
100
+ } else {
101
+
102
+ renderer.render( scene, _cameraF );
103
+
104
+ }
105
+
106
+ renderer.setScissor( _halfWidth - ( _width / 2 ), 0, _width, _height );
107
+ renderer.setViewport( _halfWidth - ( _width / 2 ), 0, _width, _height );
108
+
109
+ if ( scope.reflectFromAbove ) {
110
+
111
+ renderer.render( scene, _cameraF );
112
+
113
+ } else {
114
+
115
+ renderer.render( scene, _cameraB );
116
+
117
+ }
118
+
119
+ renderer.setScissor( _halfWidth - ( _width / 2 ) - _width, _height, _width, _height );
120
+ renderer.setViewport( _halfWidth - ( _width / 2 ) - _width, _height, _width, _height );
121
+
122
+ if ( scope.reflectFromAbove ) {
123
+
124
+ renderer.render( scene, _cameraR );
125
+
126
+ } else {
127
+
128
+ renderer.render( scene, _cameraL );
129
+
130
+ }
131
+
132
+ renderer.setScissor( _halfWidth + ( _width / 2 ), _height, _width, _height );
133
+ renderer.setViewport( _halfWidth + ( _width / 2 ), _height, _width, _height );
134
+
135
+ if ( scope.reflectFromAbove ) {
136
+
137
+ renderer.render( scene, _cameraL );
138
+
139
+ } else {
140
+
141
+ renderer.render( scene, _cameraR );
142
+
143
+ }
144
+
145
+ renderer.setScissorTest( false );
146
+
147
+ };
148
+
149
+ }
150
+
151
+ }
152
+
153
+ export { PeppersGhostEffect };
libs/three.js/0.172.0/jsm/effects/StereoEffect.js ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ StereoCamera,
3
+ Vector2
4
+ } from 'three';
5
+
6
+ class StereoEffect {
7
+
8
+ constructor( renderer ) {
9
+
10
+ const _stereo = new StereoCamera();
11
+ _stereo.aspect = 0.5;
12
+ const size = new Vector2();
13
+
14
+ this.setEyeSeparation = function ( eyeSep ) {
15
+
16
+ _stereo.eyeSep = eyeSep;
17
+
18
+ };
19
+
20
+ this.setSize = function ( width, height ) {
21
+
22
+ renderer.setSize( width, height );
23
+
24
+ };
25
+
26
+ this.render = function ( scene, camera ) {
27
+
28
+ if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
29
+
30
+ if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
31
+
32
+ _stereo.update( camera );
33
+
34
+ const currentAutoClear = renderer.autoClear;
35
+ renderer.getSize( size );
36
+
37
+ renderer.autoClear = false;
38
+ renderer.clear();
39
+
40
+ renderer.setScissorTest( true );
41
+
42
+ renderer.setScissor( 0, 0, size.width / 2, size.height );
43
+ renderer.setViewport( 0, 0, size.width / 2, size.height );
44
+ renderer.render( scene, _stereo.cameraL );
45
+
46
+ renderer.setScissor( size.width / 2, 0, size.width / 2, size.height );
47
+ renderer.setViewport( size.width / 2, 0, size.width / 2, size.height );
48
+ renderer.render( scene, _stereo.cameraR );
49
+
50
+ renderer.setScissorTest( false );
51
+
52
+ renderer.autoClear = currentAutoClear;
53
+
54
+ };
55
+
56
+ }
57
+
58
+ }
59
+
60
+ export { StereoEffect };
libs/three.js/0.172.0/jsm/environments/DebugEnvironment.js ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ BackSide,
3
+ BoxGeometry,
4
+ Mesh,
5
+ MeshLambertMaterial,
6
+ MeshStandardMaterial,
7
+ PointLight,
8
+ Scene,
9
+ } from 'three';
10
+
11
+ class DebugEnvironment extends Scene {
12
+
13
+ constructor() {
14
+
15
+ super();
16
+
17
+ const geometry = new BoxGeometry();
18
+ geometry.deleteAttribute( 'uv' );
19
+ const roomMaterial = new MeshStandardMaterial( { metalness: 0, side: BackSide } );
20
+ const room = new Mesh( geometry, roomMaterial );
21
+ room.scale.setScalar( 10 );
22
+ this.add( room );
23
+
24
+ const mainLight = new PointLight( 0xffffff, 50, 0, 2 );
25
+ this.add( mainLight );
26
+
27
+ const material1 = new MeshLambertMaterial( { color: 0xff0000, emissive: 0xffffff, emissiveIntensity: 10 } );
28
+
29
+ const light1 = new Mesh( geometry, material1 );
30
+ light1.position.set( - 5, 2, 0 );
31
+ light1.scale.set( 0.1, 1, 1 );
32
+ this.add( light1 );
33
+
34
+ const material2 = new MeshLambertMaterial( { color: 0x00ff00, emissive: 0xffffff, emissiveIntensity: 10 } );
35
+
36
+ const light2 = new Mesh( geometry, material2 );
37
+ light2.position.set( 0, 5, 0 );
38
+ light2.scale.set( 1, 0.1, 1 );
39
+ this.add( light2 );
40
+
41
+ const material3 = new MeshLambertMaterial( { color: 0x0000ff, emissive: 0xffffff, emissiveIntensity: 10 } );
42
+
43
+ const light3 = new Mesh( geometry, material3 );
44
+ light3.position.set( 2, 1, 5 );
45
+ light3.scale.set( 1.5, 2, 0.1 );
46
+ this.add( light3 );
47
+
48
+ }
49
+
50
+ }
51
+
52
+ export { DebugEnvironment };
libs/three.js/0.172.0/jsm/environments/RoomEnvironment.js ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * https://github.com/google/model-viewer/blob/master/packages/model-viewer/src/three-components/EnvironmentScene.ts
3
+ */
4
+
5
+ import {
6
+ BackSide,
7
+ BoxGeometry,
8
+ Mesh,
9
+ MeshBasicMaterial,
10
+ MeshStandardMaterial,
11
+ PointLight,
12
+ Scene,
13
+ } from 'three';
14
+
15
+ class RoomEnvironment extends Scene {
16
+
17
+ constructor() {
18
+
19
+ super();
20
+
21
+ const geometry = new BoxGeometry();
22
+ geometry.deleteAttribute( 'uv' );
23
+
24
+ const roomMaterial = new MeshStandardMaterial( { side: BackSide } );
25
+ const boxMaterial = new MeshStandardMaterial();
26
+
27
+ const mainLight = new PointLight( 0xffffff, 900, 28, 2 );
28
+ mainLight.position.set( 0.418, 16.199, 0.300 );
29
+ this.add( mainLight );
30
+
31
+ const room = new Mesh( geometry, roomMaterial );
32
+ room.position.set( - 0.757, 13.219, 0.717 );
33
+ room.scale.set( 31.713, 28.305, 28.591 );
34
+ this.add( room );
35
+
36
+ const box1 = new Mesh( geometry, boxMaterial );
37
+ box1.position.set( - 10.906, 2.009, 1.846 );
38
+ box1.rotation.set( 0, - 0.195, 0 );
39
+ box1.scale.set( 2.328, 7.905, 4.651 );
40
+ this.add( box1 );
41
+
42
+ const box2 = new Mesh( geometry, boxMaterial );
43
+ box2.position.set( - 5.607, - 0.754, - 0.758 );
44
+ box2.rotation.set( 0, 0.994, 0 );
45
+ box2.scale.set( 1.970, 1.534, 3.955 );
46
+ this.add( box2 );
47
+
48
+ const box3 = new Mesh( geometry, boxMaterial );
49
+ box3.position.set( 6.167, 0.857, 7.803 );
50
+ box3.rotation.set( 0, 0.561, 0 );
51
+ box3.scale.set( 3.927, 6.285, 3.687 );
52
+ this.add( box3 );
53
+
54
+ const box4 = new Mesh( geometry, boxMaterial );
55
+ box4.position.set( - 2.017, 0.018, 6.124 );
56
+ box4.rotation.set( 0, 0.333, 0 );
57
+ box4.scale.set( 2.002, 4.566, 2.064 );
58
+ this.add( box4 );
59
+
60
+ const box5 = new Mesh( geometry, boxMaterial );
61
+ box5.position.set( 2.291, - 0.756, - 2.621 );
62
+ box5.rotation.set( 0, - 0.286, 0 );
63
+ box5.scale.set( 1.546, 1.552, 1.496 );
64
+ this.add( box5 );
65
+
66
+ const box6 = new Mesh( geometry, boxMaterial );
67
+ box6.position.set( - 2.193, - 0.369, - 5.547 );
68
+ box6.rotation.set( 0, 0.516, 0 );
69
+ box6.scale.set( 3.875, 3.487, 2.986 );
70
+ this.add( box6 );
71
+
72
+
73
+ // -x right
74
+ const light1 = new Mesh( geometry, createAreaLightMaterial( 50 ) );
75
+ light1.position.set( - 16.116, 14.37, 8.208 );
76
+ light1.scale.set( 0.1, 2.428, 2.739 );
77
+ this.add( light1 );
78
+
79
+ // -x left
80
+ const light2 = new Mesh( geometry, createAreaLightMaterial( 50 ) );
81
+ light2.position.set( - 16.109, 18.021, - 8.207 );
82
+ light2.scale.set( 0.1, 2.425, 2.751 );
83
+ this.add( light2 );
84
+
85
+ // +x
86
+ const light3 = new Mesh( geometry, createAreaLightMaterial( 17 ) );
87
+ light3.position.set( 14.904, 12.198, - 1.832 );
88
+ light3.scale.set( 0.15, 4.265, 6.331 );
89
+ this.add( light3 );
90
+
91
+ // +z
92
+ const light4 = new Mesh( geometry, createAreaLightMaterial( 43 ) );
93
+ light4.position.set( - 0.462, 8.89, 14.520 );
94
+ light4.scale.set( 4.38, 5.441, 0.088 );
95
+ this.add( light4 );
96
+
97
+ // -z
98
+ const light5 = new Mesh( geometry, createAreaLightMaterial( 20 ) );
99
+ light5.position.set( 3.235, 11.486, - 12.541 );
100
+ light5.scale.set( 2.5, 2.0, 0.1 );
101
+ this.add( light5 );
102
+
103
+ // +y
104
+ const light6 = new Mesh( geometry, createAreaLightMaterial( 100 ) );
105
+ light6.position.set( 0.0, 20.0, 0.0 );
106
+ light6.scale.set( 1.0, 0.1, 1.0 );
107
+ this.add( light6 );
108
+
109
+ }
110
+
111
+ dispose() {
112
+
113
+ const resources = new Set();
114
+
115
+ this.traverse( ( object ) => {
116
+
117
+ if ( object.isMesh ) {
118
+
119
+ resources.add( object.geometry );
120
+ resources.add( object.material );
121
+
122
+ }
123
+
124
+ } );
125
+
126
+ for ( const resource of resources ) {
127
+
128
+ resource.dispose();
129
+
130
+ }
131
+
132
+ }
133
+
134
+ }
135
+
136
+ function createAreaLightMaterial( intensity ) {
137
+
138
+ const material = new MeshBasicMaterial();
139
+ material.color.setScalar( intensity );
140
+ return material;
141
+
142
+ }
143
+
144
+ export { RoomEnvironment };
libs/three.js/0.172.0/jsm/exporters/DRACOExporter.js ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Color, ColorManagement, SRGBColorSpace } from 'three';
2
+
3
+ /**
4
+ * Export draco compressed files from threejs geometry objects.
5
+ *
6
+ * Draco files are compressed and usually are smaller than conventional 3D file formats.
7
+ *
8
+ * The exporter receives a options object containing
9
+ * - decodeSpeed, indicates how to tune the encoder regarding decode speed (0 gives better speed but worst quality)
10
+ * - encodeSpeed, indicates how to tune the encoder parameters (0 gives better speed but worst quality)
11
+ * - encoderMethod
12
+ * - quantization, indicates the presision of each type of data stored in the draco file in the order (POSITION, NORMAL, COLOR, TEX_COORD, GENERIC)
13
+ * - exportUvs
14
+ * - exportNormals
15
+ * - exportColor
16
+ */
17
+
18
+ /* global DracoEncoderModule */
19
+
20
+ class DRACOExporter {
21
+
22
+ parse( object, options = {} ) {
23
+
24
+ options = Object.assign( {
25
+ decodeSpeed: 5,
26
+ encodeSpeed: 5,
27
+ encoderMethod: DRACOExporter.MESH_EDGEBREAKER_ENCODING,
28
+ quantization: [ 16, 8, 8, 8, 8 ],
29
+ exportUvs: true,
30
+ exportNormals: true,
31
+ exportColor: false,
32
+ }, options );
33
+
34
+ if ( DracoEncoderModule === undefined ) {
35
+
36
+ throw new Error( 'THREE.DRACOExporter: required the draco_encoder to work.' );
37
+
38
+ }
39
+
40
+ const geometry = object.geometry;
41
+
42
+ const dracoEncoder = DracoEncoderModule();
43
+ const encoder = new dracoEncoder.Encoder();
44
+ let builder;
45
+ let dracoObject;
46
+
47
+ if ( object.isMesh === true ) {
48
+
49
+ builder = new dracoEncoder.MeshBuilder();
50
+ dracoObject = new dracoEncoder.Mesh();
51
+
52
+ const vertices = geometry.getAttribute( 'position' );
53
+ builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
54
+
55
+ const faces = geometry.getIndex();
56
+
57
+ if ( faces !== null ) {
58
+
59
+ builder.AddFacesToMesh( dracoObject, faces.count / 3, faces.array );
60
+
61
+ } else {
62
+
63
+ const faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
64
+
65
+ for ( let i = 0; i < faces.length; i ++ ) {
66
+
67
+ faces[ i ] = i;
68
+
69
+ }
70
+
71
+ builder.AddFacesToMesh( dracoObject, vertices.count, faces );
72
+
73
+ }
74
+
75
+ if ( options.exportNormals === true ) {
76
+
77
+ const normals = geometry.getAttribute( 'normal' );
78
+
79
+ if ( normals !== undefined ) {
80
+
81
+ builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
82
+
83
+ }
84
+
85
+ }
86
+
87
+ if ( options.exportUvs === true ) {
88
+
89
+ const uvs = geometry.getAttribute( 'uv' );
90
+
91
+ if ( uvs !== undefined ) {
92
+
93
+ builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
94
+
95
+ }
96
+
97
+ }
98
+
99
+ if ( options.exportColor === true ) {
100
+
101
+ const colors = geometry.getAttribute( 'color' );
102
+
103
+ if ( colors !== undefined ) {
104
+
105
+ const array = createVertexColorSRGBArray( colors );
106
+
107
+ builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, array );
108
+
109
+ }
110
+
111
+ }
112
+
113
+ } else if ( object.isPoints === true ) {
114
+
115
+ builder = new dracoEncoder.PointCloudBuilder();
116
+ dracoObject = new dracoEncoder.PointCloud();
117
+
118
+ const vertices = geometry.getAttribute( 'position' );
119
+ builder.AddFloatAttribute( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
120
+
121
+ if ( options.exportColor === true ) {
122
+
123
+ const colors = geometry.getAttribute( 'color' );
124
+
125
+ if ( colors !== undefined ) {
126
+
127
+ const array = createVertexColorSRGBArray( colors );
128
+
129
+ builder.AddFloatAttribute( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, array );
130
+
131
+ }
132
+
133
+ }
134
+
135
+ } else {
136
+
137
+ throw new Error( 'DRACOExporter: Unsupported object type.' );
138
+
139
+ }
140
+
141
+ //Compress using draco encoder
142
+
143
+ const encodedData = new dracoEncoder.DracoInt8Array();
144
+
145
+ //Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression).
146
+
147
+ const encodeSpeed = ( options.encodeSpeed !== undefined ) ? options.encodeSpeed : 5;
148
+ const decodeSpeed = ( options.decodeSpeed !== undefined ) ? options.decodeSpeed : 5;
149
+
150
+ encoder.SetSpeedOptions( encodeSpeed, decodeSpeed );
151
+
152
+ // Sets the desired encoding method for a given geometry.
153
+
154
+ if ( options.encoderMethod !== undefined ) {
155
+
156
+ encoder.SetEncodingMethod( options.encoderMethod );
157
+
158
+ }
159
+
160
+ // Sets the quantization (number of bits used to represent) compression options for a named attribute.
161
+ // The attribute values will be quantized in a box defined by the maximum extent of the attribute values.
162
+ if ( options.quantization !== undefined ) {
163
+
164
+ for ( let i = 0; i < 5; i ++ ) {
165
+
166
+ if ( options.quantization[ i ] !== undefined ) {
167
+
168
+ encoder.SetAttributeQuantization( i, options.quantization[ i ] );
169
+
170
+ }
171
+
172
+ }
173
+
174
+ }
175
+
176
+ let length;
177
+
178
+ if ( object.isMesh === true ) {
179
+
180
+ length = encoder.EncodeMeshToDracoBuffer( dracoObject, encodedData );
181
+
182
+ } else {
183
+
184
+ length = encoder.EncodePointCloudToDracoBuffer( dracoObject, true, encodedData );
185
+
186
+ }
187
+
188
+ dracoEncoder.destroy( dracoObject );
189
+
190
+ if ( length === 0 ) {
191
+
192
+ throw new Error( 'THREE.DRACOExporter: Draco encoding failed.' );
193
+
194
+ }
195
+
196
+ //Copy encoded data to buffer.
197
+ const outputData = new Int8Array( new ArrayBuffer( length ) );
198
+
199
+ for ( let i = 0; i < length; i ++ ) {
200
+
201
+ outputData[ i ] = encodedData.GetValue( i );
202
+
203
+ }
204
+
205
+ dracoEncoder.destroy( encodedData );
206
+ dracoEncoder.destroy( encoder );
207
+ dracoEncoder.destroy( builder );
208
+
209
+ return outputData;
210
+
211
+ }
212
+
213
+ }
214
+
215
+ function createVertexColorSRGBArray( attribute ) {
216
+
217
+ // While .drc files do not specify colorspace, the only 'official' tooling
218
+ // is PLY and OBJ converters, which use sRGB. We'll assume sRGB is expected
219
+ // for .drc files, but note that Draco buffers embedded in glTF files will
220
+ // be Linear-sRGB instead.
221
+
222
+ const _color = new Color();
223
+
224
+ const count = attribute.count;
225
+ const itemSize = attribute.itemSize;
226
+ const array = new Float32Array( count * itemSize );
227
+
228
+ for ( let i = 0, il = count; i < il; i ++ ) {
229
+
230
+ _color.fromBufferAttribute( attribute, i );
231
+
232
+ ColorManagement.fromWorkingColorSpace( _color, SRGBColorSpace );
233
+
234
+ array[ i * itemSize ] = _color.r;
235
+ array[ i * itemSize + 1 ] = _color.g;
236
+ array[ i * itemSize + 2 ] = _color.b;
237
+
238
+ if ( itemSize === 4 ) {
239
+
240
+ array[ i * itemSize + 3 ] = attribute.getW( i );
241
+
242
+ }
243
+
244
+ }
245
+
246
+ return array;
247
+
248
+ }
249
+
250
+ // Encoder methods
251
+
252
+ DRACOExporter.MESH_EDGEBREAKER_ENCODING = 1;
253
+ DRACOExporter.MESH_SEQUENTIAL_ENCODING = 0;
254
+
255
+ // Geometry type
256
+
257
+ DRACOExporter.POINT_CLOUD = 0;
258
+ DRACOExporter.TRIANGULAR_MESH = 1;
259
+
260
+ // Attribute type
261
+
262
+ DRACOExporter.INVALID = - 1;
263
+ DRACOExporter.POSITION = 0;
264
+ DRACOExporter.NORMAL = 1;
265
+ DRACOExporter.COLOR = 2;
266
+ DRACOExporter.TEX_COORD = 3;
267
+ DRACOExporter.GENERIC = 4;
268
+
269
+ export { DRACOExporter };
libs/three.js/0.172.0/jsm/exporters/EXRExporter.js ADDED
@@ -0,0 +1,587 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author sciecode / https://github.com/sciecode
3
+ *
4
+ * EXR format references:
5
+ * https://www.openexr.com/documentation/openexrfilelayout.pdf
6
+ */
7
+
8
+ import {
9
+ FloatType,
10
+ HalfFloatType,
11
+ RGBAFormat,
12
+ DataUtils,
13
+ } from 'three';
14
+ import * as fflate from '../libs/fflate.module.js';
15
+
16
+ const textEncoder = new TextEncoder();
17
+
18
+ const NO_COMPRESSION = 0;
19
+ const ZIPS_COMPRESSION = 2;
20
+ const ZIP_COMPRESSION = 3;
21
+
22
+ class EXRExporter {
23
+
24
+ async parse( arg1, arg2, arg3 ) {
25
+
26
+ if ( ! arg1 || ! ( arg1.isWebGLRenderer || arg1.isWebGPURenderer || arg1.isDataTexture ) ) {
27
+
28
+ throw Error( 'EXRExporter.parse: Unsupported first parameter, expected instance of WebGLRenderer or DataTexture.' );
29
+
30
+ } else if ( arg1.isWebGLRenderer || arg1.isWebGPURenderer ) {
31
+
32
+ const renderer = arg1, renderTarget = arg2, options = arg3;
33
+
34
+ supportedRTT( renderTarget );
35
+
36
+ const info = buildInfoRTT( renderTarget, options ),
37
+ dataBuffer = await getPixelData( renderer, renderTarget, info ),
38
+ rawContentBuffer = reorganizeDataBuffer( dataBuffer, info ),
39
+ chunks = compressData( rawContentBuffer, info );
40
+
41
+ return fillData( chunks, info );
42
+
43
+ } else if ( arg1.isDataTexture ) {
44
+
45
+ const texture = arg1, options = arg2;
46
+
47
+ supportedDT( texture );
48
+
49
+ const info = buildInfoDT( texture, options ),
50
+ dataBuffer = texture.image.data,
51
+ rawContentBuffer = reorganizeDataBuffer( dataBuffer, info ),
52
+ chunks = compressData( rawContentBuffer, info );
53
+
54
+ return fillData( chunks, info );
55
+
56
+ }
57
+
58
+ }
59
+
60
+ }
61
+
62
+ function supportedRTT( renderTarget ) {
63
+
64
+ if ( ! renderTarget || ! renderTarget.isRenderTarget ) {
65
+
66
+ throw Error( 'EXRExporter.parse: Unsupported second parameter, expected instance of WebGLRenderTarget.' );
67
+
68
+ }
69
+
70
+ if ( renderTarget.isWebGLCubeRenderTarget || renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) {
71
+
72
+ throw Error( 'EXRExporter.parse: Unsupported render target type, expected instance of WebGLRenderTarget.' );
73
+
74
+ }
75
+
76
+ if ( renderTarget.texture.type !== FloatType && renderTarget.texture.type !== HalfFloatType ) {
77
+
78
+ throw Error( 'EXRExporter.parse: Unsupported WebGLRenderTarget texture type.' );
79
+
80
+ }
81
+
82
+ if ( renderTarget.texture.format !== RGBAFormat ) {
83
+
84
+ throw Error( 'EXRExporter.parse: Unsupported WebGLRenderTarget texture format, expected RGBAFormat.' );
85
+
86
+ }
87
+
88
+ }
89
+
90
+ function supportedDT( texture ) {
91
+
92
+ if ( texture.type !== FloatType && texture.type !== HalfFloatType ) {
93
+
94
+ throw Error( 'EXRExporter.parse: Unsupported DataTexture texture type.' );
95
+
96
+ }
97
+
98
+ if ( texture.format !== RGBAFormat ) {
99
+
100
+ throw Error( 'EXRExporter.parse: Unsupported DataTexture texture format, expected RGBAFormat.' );
101
+
102
+ }
103
+
104
+ if ( ! texture.image.data ) {
105
+
106
+ throw Error( 'EXRExporter.parse: Invalid DataTexture image data.' );
107
+
108
+ }
109
+
110
+ if ( texture.type === FloatType && texture.image.data.constructor.name !== 'Float32Array' ) {
111
+
112
+ throw Error( 'EXRExporter.parse: DataTexture image data doesn\'t match type, expected \'Float32Array\'.' );
113
+
114
+ }
115
+
116
+ if ( texture.type === HalfFloatType && texture.image.data.constructor.name !== 'Uint16Array' ) {
117
+
118
+ throw Error( 'EXRExporter.parse: DataTexture image data doesn\'t match type, expected \'Uint16Array\'.' );
119
+
120
+ }
121
+
122
+ }
123
+
124
+ function buildInfoRTT( renderTarget, options = {} ) {
125
+
126
+ const compressionSizes = {
127
+ 0: 1,
128
+ 2: 1,
129
+ 3: 16
130
+ };
131
+
132
+ const WIDTH = renderTarget.width,
133
+ HEIGHT = renderTarget.height,
134
+ TYPE = renderTarget.texture.type,
135
+ FORMAT = renderTarget.texture.format,
136
+ COMPRESSION = ( options.compression !== undefined ) ? options.compression : ZIP_COMPRESSION,
137
+ EXPORTER_TYPE = ( options.type !== undefined ) ? options.type : HalfFloatType,
138
+ OUT_TYPE = ( EXPORTER_TYPE === FloatType ) ? 2 : 1,
139
+ COMPRESSION_SIZE = compressionSizes[ COMPRESSION ],
140
+ NUM_CHANNELS = 4;
141
+
142
+ return {
143
+ width: WIDTH,
144
+ height: HEIGHT,
145
+ type: TYPE,
146
+ format: FORMAT,
147
+ compression: COMPRESSION,
148
+ blockLines: COMPRESSION_SIZE,
149
+ dataType: OUT_TYPE,
150
+ dataSize: 2 * OUT_TYPE,
151
+ numBlocks: Math.ceil( HEIGHT / COMPRESSION_SIZE ),
152
+ numInputChannels: 4,
153
+ numOutputChannels: NUM_CHANNELS,
154
+ };
155
+
156
+ }
157
+
158
+ function buildInfoDT( texture, options = {} ) {
159
+
160
+ const compressionSizes = {
161
+ 0: 1,
162
+ 2: 1,
163
+ 3: 16
164
+ };
165
+
166
+ const WIDTH = texture.image.width,
167
+ HEIGHT = texture.image.height,
168
+ TYPE = texture.type,
169
+ FORMAT = texture.format,
170
+ COMPRESSION = ( options.compression !== undefined ) ? options.compression : ZIP_COMPRESSION,
171
+ EXPORTER_TYPE = ( options.type !== undefined ) ? options.type : HalfFloatType,
172
+ OUT_TYPE = ( EXPORTER_TYPE === FloatType ) ? 2 : 1,
173
+ COMPRESSION_SIZE = compressionSizes[ COMPRESSION ],
174
+ NUM_CHANNELS = 4;
175
+
176
+ return {
177
+ width: WIDTH,
178
+ height: HEIGHT,
179
+ type: TYPE,
180
+ format: FORMAT,
181
+ compression: COMPRESSION,
182
+ blockLines: COMPRESSION_SIZE,
183
+ dataType: OUT_TYPE,
184
+ dataSize: 2 * OUT_TYPE,
185
+ numBlocks: Math.ceil( HEIGHT / COMPRESSION_SIZE ),
186
+ numInputChannels: 4,
187
+ numOutputChannels: NUM_CHANNELS,
188
+ };
189
+
190
+ }
191
+
192
+ async function getPixelData( renderer, rtt, info ) {
193
+
194
+ let dataBuffer;
195
+
196
+ if ( renderer.isWebGLRenderer ) {
197
+
198
+ if ( info.type === FloatType ) {
199
+
200
+ dataBuffer = new Float32Array( info.width * info.height * info.numInputChannels );
201
+
202
+ } else {
203
+
204
+ dataBuffer = new Uint16Array( info.width * info.height * info.numInputChannels );
205
+
206
+ }
207
+
208
+ await renderer.readRenderTargetPixelsAsync( rtt, 0, 0, info.width, info.height, dataBuffer );
209
+
210
+ } else {
211
+
212
+ dataBuffer = await renderer.readRenderTargetPixelsAsync( rtt, 0, 0, info.width, info.height );
213
+
214
+ }
215
+
216
+ return dataBuffer;
217
+
218
+ }
219
+
220
+ function reorganizeDataBuffer( inBuffer, info ) {
221
+
222
+ const w = info.width,
223
+ h = info.height,
224
+ dec = { r: 0, g: 0, b: 0, a: 0 },
225
+ offset = { value: 0 },
226
+ cOffset = ( info.numOutputChannels == 4 ) ? 1 : 0,
227
+ getValue = ( info.type == FloatType ) ? getFloat32 : getFloat16,
228
+ setValue = ( info.dataType == 1 ) ? setFloat16 : setFloat32,
229
+ outBuffer = new Uint8Array( info.width * info.height * info.numOutputChannels * info.dataSize ),
230
+ dv = new DataView( outBuffer.buffer );
231
+
232
+ for ( let y = 0; y < h; ++ y ) {
233
+
234
+ for ( let x = 0; x < w; ++ x ) {
235
+
236
+ const i = y * w * 4 + x * 4;
237
+
238
+ const r = getValue( inBuffer, i );
239
+ const g = getValue( inBuffer, i + 1 );
240
+ const b = getValue( inBuffer, i + 2 );
241
+ const a = getValue( inBuffer, i + 3 );
242
+
243
+ const line = ( h - y - 1 ) * w * ( 3 + cOffset ) * info.dataSize;
244
+
245
+ decodeLinear( dec, r, g, b, a );
246
+
247
+ offset.value = line + x * info.dataSize;
248
+ setValue( dv, dec.a, offset );
249
+
250
+ offset.value = line + ( cOffset ) * w * info.dataSize + x * info.dataSize;
251
+ setValue( dv, dec.b, offset );
252
+
253
+ offset.value = line + ( 1 + cOffset ) * w * info.dataSize + x * info.dataSize;
254
+ setValue( dv, dec.g, offset );
255
+
256
+ offset.value = line + ( 2 + cOffset ) * w * info.dataSize + x * info.dataSize;
257
+ setValue( dv, dec.r, offset );
258
+
259
+ }
260
+
261
+ }
262
+
263
+ return outBuffer;
264
+
265
+ }
266
+
267
+ function compressData( inBuffer, info ) {
268
+
269
+ let compress,
270
+ tmpBuffer,
271
+ sum = 0;
272
+
273
+ const chunks = { data: new Array(), totalSize: 0 },
274
+ size = info.width * info.numOutputChannels * info.blockLines * info.dataSize;
275
+
276
+ switch ( info.compression ) {
277
+
278
+ case 0:
279
+ compress = compressNONE;
280
+ break;
281
+
282
+ case 2:
283
+ case 3:
284
+ compress = compressZIP;
285
+ break;
286
+
287
+ }
288
+
289
+ if ( info.compression !== 0 ) {
290
+
291
+ tmpBuffer = new Uint8Array( size );
292
+
293
+ }
294
+
295
+ for ( let i = 0; i < info.numBlocks; ++ i ) {
296
+
297
+ const arr = inBuffer.subarray( size * i, size * ( i + 1 ) );
298
+
299
+ const block = compress( arr, tmpBuffer );
300
+
301
+ sum += block.length;
302
+
303
+ chunks.data.push( { dataChunk: block, size: block.length } );
304
+
305
+ }
306
+
307
+ chunks.totalSize = sum;
308
+
309
+ return chunks;
310
+
311
+ }
312
+
313
+ function compressNONE( data ) {
314
+
315
+ return data;
316
+
317
+ }
318
+
319
+ function compressZIP( data, tmpBuffer ) {
320
+
321
+ //
322
+ // Reorder the pixel data.
323
+ //
324
+
325
+ let t1 = 0,
326
+ t2 = Math.floor( ( data.length + 1 ) / 2 ),
327
+ s = 0;
328
+
329
+ const stop = data.length - 1;
330
+
331
+ while ( true ) {
332
+
333
+ if ( s > stop ) break;
334
+ tmpBuffer[ t1 ++ ] = data[ s ++ ];
335
+
336
+ if ( s > stop ) break;
337
+ tmpBuffer[ t2 ++ ] = data[ s ++ ];
338
+
339
+ }
340
+
341
+ //
342
+ // Predictor.
343
+ //
344
+
345
+ let p = tmpBuffer[ 0 ];
346
+
347
+ for ( let t = 1; t < tmpBuffer.length; t ++ ) {
348
+
349
+ const d = tmpBuffer[ t ] - p + ( 128 + 256 );
350
+ p = tmpBuffer[ t ];
351
+ tmpBuffer[ t ] = d;
352
+
353
+ }
354
+
355
+ const deflate = fflate.zlibSync( tmpBuffer );
356
+
357
+ return deflate;
358
+
359
+ }
360
+
361
+ function fillHeader( outBuffer, chunks, info ) {
362
+
363
+ const offset = { value: 0 };
364
+ const dv = new DataView( outBuffer.buffer );
365
+
366
+ setUint32( dv, 20000630, offset ); // magic
367
+ setUint32( dv, 2, offset ); // mask
368
+
369
+ // = HEADER =
370
+
371
+ setString( dv, 'compression', offset );
372
+ setString( dv, 'compression', offset );
373
+ setUint32( dv, 1, offset );
374
+ setUint8( dv, info.compression, offset );
375
+
376
+ setString( dv, 'screenWindowCenter', offset );
377
+ setString( dv, 'v2f', offset );
378
+ setUint32( dv, 8, offset );
379
+ setUint32( dv, 0, offset );
380
+ setUint32( dv, 0, offset );
381
+
382
+ setString( dv, 'screenWindowWidth', offset );
383
+ setString( dv, 'float', offset );
384
+ setUint32( dv, 4, offset );
385
+ setFloat32( dv, 1.0, offset );
386
+
387
+ setString( dv, 'pixelAspectRatio', offset );
388
+ setString( dv, 'float', offset );
389
+ setUint32( dv, 4, offset );
390
+ setFloat32( dv, 1.0, offset );
391
+
392
+ setString( dv, 'lineOrder', offset );
393
+ setString( dv, 'lineOrder', offset );
394
+ setUint32( dv, 1, offset );
395
+ setUint8( dv, 0, offset );
396
+
397
+ setString( dv, 'dataWindow', offset );
398
+ setString( dv, 'box2i', offset );
399
+ setUint32( dv, 16, offset );
400
+ setUint32( dv, 0, offset );
401
+ setUint32( dv, 0, offset );
402
+ setUint32( dv, info.width - 1, offset );
403
+ setUint32( dv, info.height - 1, offset );
404
+
405
+ setString( dv, 'displayWindow', offset );
406
+ setString( dv, 'box2i', offset );
407
+ setUint32( dv, 16, offset );
408
+ setUint32( dv, 0, offset );
409
+ setUint32( dv, 0, offset );
410
+ setUint32( dv, info.width - 1, offset );
411
+ setUint32( dv, info.height - 1, offset );
412
+
413
+ setString( dv, 'channels', offset );
414
+ setString( dv, 'chlist', offset );
415
+ setUint32( dv, info.numOutputChannels * 18 + 1, offset );
416
+
417
+ setString( dv, 'A', offset );
418
+ setUint32( dv, info.dataType, offset );
419
+ offset.value += 4;
420
+ setUint32( dv, 1, offset );
421
+ setUint32( dv, 1, offset );
422
+
423
+ setString( dv, 'B', offset );
424
+ setUint32( dv, info.dataType, offset );
425
+ offset.value += 4;
426
+ setUint32( dv, 1, offset );
427
+ setUint32( dv, 1, offset );
428
+
429
+ setString( dv, 'G', offset );
430
+ setUint32( dv, info.dataType, offset );
431
+ offset.value += 4;
432
+ setUint32( dv, 1, offset );
433
+ setUint32( dv, 1, offset );
434
+
435
+ setString( dv, 'R', offset );
436
+ setUint32( dv, info.dataType, offset );
437
+ offset.value += 4;
438
+ setUint32( dv, 1, offset );
439
+ setUint32( dv, 1, offset );
440
+
441
+ setUint8( dv, 0, offset );
442
+
443
+ // null-byte
444
+ setUint8( dv, 0, offset );
445
+
446
+ // = OFFSET TABLE =
447
+
448
+ let sum = offset.value + info.numBlocks * 8;
449
+
450
+ for ( let i = 0; i < chunks.data.length; ++ i ) {
451
+
452
+ setUint64( dv, sum, offset );
453
+
454
+ sum += chunks.data[ i ].size + 8;
455
+
456
+ }
457
+
458
+ }
459
+
460
+ function fillData( chunks, info ) {
461
+
462
+ const TableSize = info.numBlocks * 8,
463
+ HeaderSize = 259 + ( 18 * info.numOutputChannels ), // 259 + 18 * chlist
464
+ offset = { value: HeaderSize + TableSize },
465
+ outBuffer = new Uint8Array( HeaderSize + TableSize + chunks.totalSize + info.numBlocks * 8 ),
466
+ dv = new DataView( outBuffer.buffer );
467
+
468
+ fillHeader( outBuffer, chunks, info );
469
+
470
+ for ( let i = 0; i < chunks.data.length; ++ i ) {
471
+
472
+ const data = chunks.data[ i ].dataChunk;
473
+ const size = chunks.data[ i ].size;
474
+
475
+ setUint32( dv, i * info.blockLines, offset );
476
+ setUint32( dv, size, offset );
477
+
478
+ outBuffer.set( data, offset.value );
479
+ offset.value += size;
480
+
481
+ }
482
+
483
+ return outBuffer;
484
+
485
+ }
486
+
487
+ function decodeLinear( dec, r, g, b, a ) {
488
+
489
+ dec.r = r;
490
+ dec.g = g;
491
+ dec.b = b;
492
+ dec.a = a;
493
+
494
+ }
495
+
496
+ // function decodeSRGB( dec, r, g, b, a ) {
497
+
498
+ // dec.r = r > 0.04045 ? Math.pow( r * 0.9478672986 + 0.0521327014, 2.4 ) : r * 0.0773993808;
499
+ // dec.g = g > 0.04045 ? Math.pow( g * 0.9478672986 + 0.0521327014, 2.4 ) : g * 0.0773993808;
500
+ // dec.b = b > 0.04045 ? Math.pow( b * 0.9478672986 + 0.0521327014, 2.4 ) : b * 0.0773993808;
501
+ // dec.a = a;
502
+
503
+ // }
504
+
505
+
506
+ function setUint8( dv, value, offset ) {
507
+
508
+ dv.setUint8( offset.value, value );
509
+
510
+ offset.value += 1;
511
+
512
+ }
513
+
514
+ function setUint32( dv, value, offset ) {
515
+
516
+ dv.setUint32( offset.value, value, true );
517
+
518
+ offset.value += 4;
519
+
520
+ }
521
+
522
+ function setFloat16( dv, value, offset ) {
523
+
524
+ dv.setUint16( offset.value, DataUtils.toHalfFloat( value ), true );
525
+
526
+ offset.value += 2;
527
+
528
+ }
529
+
530
+ function setFloat32( dv, value, offset ) {
531
+
532
+ dv.setFloat32( offset.value, value, true );
533
+
534
+ offset.value += 4;
535
+
536
+ }
537
+
538
+ function setUint64( dv, value, offset ) {
539
+
540
+ dv.setBigUint64( offset.value, BigInt( value ), true );
541
+
542
+ offset.value += 8;
543
+
544
+ }
545
+
546
+ function setString( dv, string, offset ) {
547
+
548
+ const tmp = textEncoder.encode( string + '\0' );
549
+
550
+ for ( let i = 0; i < tmp.length; ++ i ) {
551
+
552
+ setUint8( dv, tmp[ i ], offset );
553
+
554
+ }
555
+
556
+ }
557
+
558
+ function decodeFloat16( binary ) {
559
+
560
+ const exponent = ( binary & 0x7C00 ) >> 10,
561
+ fraction = binary & 0x03FF;
562
+
563
+ return ( binary >> 15 ? - 1 : 1 ) * (
564
+ exponent ?
565
+ (
566
+ exponent === 0x1F ?
567
+ fraction ? NaN : Infinity :
568
+ Math.pow( 2, exponent - 15 ) * ( 1 + fraction / 0x400 )
569
+ ) :
570
+ 6.103515625e-5 * ( fraction / 0x400 )
571
+ );
572
+
573
+ }
574
+
575
+ function getFloat16( arr, i ) {
576
+
577
+ return decodeFloat16( arr[ i ] );
578
+
579
+ }
580
+
581
+ function getFloat32( arr, i ) {
582
+
583
+ return arr[ i ];
584
+
585
+ }
586
+
587
+ export { EXRExporter, NO_COMPRESSION, ZIP_COMPRESSION, ZIPS_COMPRESSION };
libs/three.js/0.172.0/jsm/exporters/GLTFExporter.js ADDED
@@ -0,0 +1,3460 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ BufferAttribute,
3
+ ClampToEdgeWrapping,
4
+ Color,
5
+ DoubleSide,
6
+ InterpolateDiscrete,
7
+ InterpolateLinear,
8
+ NoColorSpace,
9
+ LinearFilter,
10
+ LinearMipmapLinearFilter,
11
+ LinearMipmapNearestFilter,
12
+ MathUtils,
13
+ Matrix4,
14
+ MirroredRepeatWrapping,
15
+ NearestFilter,
16
+ NearestMipmapLinearFilter,
17
+ NearestMipmapNearestFilter,
18
+ PropertyBinding,
19
+ RGBAFormat,
20
+ RepeatWrapping,
21
+ Scene,
22
+ Source,
23
+ SRGBColorSpace,
24
+ CompressedTexture,
25
+ Vector3,
26
+ Quaternion,
27
+ REVISION
28
+ } from 'three';
29
+
30
+ /**
31
+ * The KHR_mesh_quantization extension allows these extra attribute component types
32
+ *
33
+ * @see https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_mesh_quantization/README.md#extending-mesh-attributes
34
+ */
35
+ const KHR_mesh_quantization_ExtraAttrTypes = {
36
+ POSITION: [
37
+ 'byte',
38
+ 'byte normalized',
39
+ 'unsigned byte',
40
+ 'unsigned byte normalized',
41
+ 'short',
42
+ 'short normalized',
43
+ 'unsigned short',
44
+ 'unsigned short normalized',
45
+ ],
46
+ NORMAL: [
47
+ 'byte normalized',
48
+ 'short normalized',
49
+ ],
50
+ TANGENT: [
51
+ 'byte normalized',
52
+ 'short normalized',
53
+ ],
54
+ TEXCOORD: [
55
+ 'byte',
56
+ 'byte normalized',
57
+ 'unsigned byte',
58
+ 'short',
59
+ 'short normalized',
60
+ 'unsigned short',
61
+ ],
62
+ };
63
+
64
+
65
+ class GLTFExporter {
66
+
67
+ constructor() {
68
+
69
+ this.textureUtils = null;
70
+
71
+ this.pluginCallbacks = [];
72
+
73
+ this.register( function ( writer ) {
74
+
75
+ return new GLTFLightExtension( writer );
76
+
77
+ } );
78
+
79
+ this.register( function ( writer ) {
80
+
81
+ return new GLTFMaterialsUnlitExtension( writer );
82
+
83
+ } );
84
+
85
+ this.register( function ( writer ) {
86
+
87
+ return new GLTFMaterialsTransmissionExtension( writer );
88
+
89
+ } );
90
+
91
+ this.register( function ( writer ) {
92
+
93
+ return new GLTFMaterialsVolumeExtension( writer );
94
+
95
+ } );
96
+
97
+ this.register( function ( writer ) {
98
+
99
+ return new GLTFMaterialsIorExtension( writer );
100
+
101
+ } );
102
+
103
+ this.register( function ( writer ) {
104
+
105
+ return new GLTFMaterialsSpecularExtension( writer );
106
+
107
+ } );
108
+
109
+ this.register( function ( writer ) {
110
+
111
+ return new GLTFMaterialsClearcoatExtension( writer );
112
+
113
+ } );
114
+
115
+ this.register( function ( writer ) {
116
+
117
+ return new GLTFMaterialsDispersionExtension( writer );
118
+
119
+ } );
120
+
121
+ this.register( function ( writer ) {
122
+
123
+ return new GLTFMaterialsIridescenceExtension( writer );
124
+
125
+ } );
126
+
127
+ this.register( function ( writer ) {
128
+
129
+ return new GLTFMaterialsSheenExtension( writer );
130
+
131
+ } );
132
+
133
+ this.register( function ( writer ) {
134
+
135
+ return new GLTFMaterialsAnisotropyExtension( writer );
136
+
137
+ } );
138
+
139
+ this.register( function ( writer ) {
140
+
141
+ return new GLTFMaterialsEmissiveStrengthExtension( writer );
142
+
143
+ } );
144
+
145
+ this.register( function ( writer ) {
146
+
147
+ return new GLTFMaterialsBumpExtension( writer );
148
+
149
+ } );
150
+
151
+ this.register( function ( writer ) {
152
+
153
+ return new GLTFMeshGpuInstancing( writer );
154
+
155
+ } );
156
+
157
+ }
158
+
159
+ register( callback ) {
160
+
161
+ if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) {
162
+
163
+ this.pluginCallbacks.push( callback );
164
+
165
+ }
166
+
167
+ return this;
168
+
169
+ }
170
+
171
+ unregister( callback ) {
172
+
173
+ if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) {
174
+
175
+ this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 );
176
+
177
+ }
178
+
179
+ return this;
180
+
181
+ }
182
+
183
+ setTextureUtils( utils ) {
184
+
185
+ this.textureUtils = utils;
186
+
187
+ return this;
188
+
189
+ }
190
+
191
+ /**
192
+ * Parse scenes and generate GLTF output
193
+ *
194
+ * @param {Scene|THREE.Scenes} input Scene or Array of THREE.Scenes
195
+ * @param {Function} onDone Callback on completed
196
+ * @param {Function} onError Callback on errors
197
+ * @param {Object} options options
198
+ */
199
+ parse( input, onDone, onError, options ) {
200
+
201
+ const writer = new GLTFWriter();
202
+ const plugins = [];
203
+
204
+ for ( let i = 0, il = this.pluginCallbacks.length; i < il; i ++ ) {
205
+
206
+ plugins.push( this.pluginCallbacks[ i ]( writer ) );
207
+
208
+ }
209
+
210
+ writer.setPlugins( plugins );
211
+ writer.setTextureUtils( this.textureUtils );
212
+ writer.writeAsync( input, onDone, options ).catch( onError );
213
+
214
+ }
215
+
216
+ parseAsync( input, options ) {
217
+
218
+ const scope = this;
219
+
220
+ return new Promise( function ( resolve, reject ) {
221
+
222
+ scope.parse( input, resolve, reject, options );
223
+
224
+ } );
225
+
226
+ }
227
+
228
+ }
229
+
230
+ //------------------------------------------------------------------------------
231
+ // Constants
232
+ //------------------------------------------------------------------------------
233
+
234
+ const WEBGL_CONSTANTS = {
235
+ POINTS: 0x0000,
236
+ LINES: 0x0001,
237
+ LINE_LOOP: 0x0002,
238
+ LINE_STRIP: 0x0003,
239
+ TRIANGLES: 0x0004,
240
+ TRIANGLE_STRIP: 0x0005,
241
+ TRIANGLE_FAN: 0x0006,
242
+
243
+ BYTE: 0x1400,
244
+ UNSIGNED_BYTE: 0x1401,
245
+ SHORT: 0x1402,
246
+ UNSIGNED_SHORT: 0x1403,
247
+ INT: 0x1404,
248
+ UNSIGNED_INT: 0x1405,
249
+ FLOAT: 0x1406,
250
+
251
+ ARRAY_BUFFER: 0x8892,
252
+ ELEMENT_ARRAY_BUFFER: 0x8893,
253
+
254
+ NEAREST: 0x2600,
255
+ LINEAR: 0x2601,
256
+ NEAREST_MIPMAP_NEAREST: 0x2700,
257
+ LINEAR_MIPMAP_NEAREST: 0x2701,
258
+ NEAREST_MIPMAP_LINEAR: 0x2702,
259
+ LINEAR_MIPMAP_LINEAR: 0x2703,
260
+
261
+ CLAMP_TO_EDGE: 33071,
262
+ MIRRORED_REPEAT: 33648,
263
+ REPEAT: 10497
264
+ };
265
+
266
+ const KHR_MESH_QUANTIZATION = 'KHR_mesh_quantization';
267
+
268
+ const THREE_TO_WEBGL = {};
269
+
270
+ THREE_TO_WEBGL[ NearestFilter ] = WEBGL_CONSTANTS.NEAREST;
271
+ THREE_TO_WEBGL[ NearestMipmapNearestFilter ] = WEBGL_CONSTANTS.NEAREST_MIPMAP_NEAREST;
272
+ THREE_TO_WEBGL[ NearestMipmapLinearFilter ] = WEBGL_CONSTANTS.NEAREST_MIPMAP_LINEAR;
273
+ THREE_TO_WEBGL[ LinearFilter ] = WEBGL_CONSTANTS.LINEAR;
274
+ THREE_TO_WEBGL[ LinearMipmapNearestFilter ] = WEBGL_CONSTANTS.LINEAR_MIPMAP_NEAREST;
275
+ THREE_TO_WEBGL[ LinearMipmapLinearFilter ] = WEBGL_CONSTANTS.LINEAR_MIPMAP_LINEAR;
276
+
277
+ THREE_TO_WEBGL[ ClampToEdgeWrapping ] = WEBGL_CONSTANTS.CLAMP_TO_EDGE;
278
+ THREE_TO_WEBGL[ RepeatWrapping ] = WEBGL_CONSTANTS.REPEAT;
279
+ THREE_TO_WEBGL[ MirroredRepeatWrapping ] = WEBGL_CONSTANTS.MIRRORED_REPEAT;
280
+
281
+ const PATH_PROPERTIES = {
282
+ scale: 'scale',
283
+ position: 'translation',
284
+ quaternion: 'rotation',
285
+ morphTargetInfluences: 'weights'
286
+ };
287
+
288
+ const DEFAULT_SPECULAR_COLOR = new Color();
289
+
290
+ // GLB constants
291
+ // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification
292
+
293
+ const GLB_HEADER_BYTES = 12;
294
+ const GLB_HEADER_MAGIC = 0x46546C67;
295
+ const GLB_VERSION = 2;
296
+
297
+ const GLB_CHUNK_PREFIX_BYTES = 8;
298
+ const GLB_CHUNK_TYPE_JSON = 0x4E4F534A;
299
+ const GLB_CHUNK_TYPE_BIN = 0x004E4942;
300
+
301
+ //------------------------------------------------------------------------------
302
+ // Utility functions
303
+ //------------------------------------------------------------------------------
304
+
305
+ /**
306
+ * Compare two arrays
307
+ * @param {Array} array1 Array 1 to compare
308
+ * @param {Array} array2 Array 2 to compare
309
+ * @return {Boolean} Returns true if both arrays are equal
310
+ */
311
+ function equalArray( array1, array2 ) {
312
+
313
+ return ( array1.length === array2.length ) && array1.every( function ( element, index ) {
314
+
315
+ return element === array2[ index ];
316
+
317
+ } );
318
+
319
+ }
320
+
321
+ /**
322
+ * Converts a string to an ArrayBuffer.
323
+ * @param {string} text
324
+ * @return {ArrayBuffer}
325
+ */
326
+ function stringToArrayBuffer( text ) {
327
+
328
+ return new TextEncoder().encode( text ).buffer;
329
+
330
+ }
331
+
332
+ /**
333
+ * Is identity matrix
334
+ *
335
+ * @param {Matrix4} matrix
336
+ * @returns {Boolean} Returns true, if parameter is identity matrix
337
+ */
338
+ function isIdentityMatrix( matrix ) {
339
+
340
+ return equalArray( matrix.elements, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] );
341
+
342
+ }
343
+
344
+ /**
345
+ * Get the min and max vectors from the given attribute
346
+ * @param {BufferAttribute} attribute Attribute to find the min/max in range from start to start + count
347
+ * @param {Integer} start
348
+ * @param {Integer} count
349
+ * @return {Object} Object containing the `min` and `max` values (As an array of attribute.itemSize components)
350
+ */
351
+ function getMinMax( attribute, start, count ) {
352
+
353
+ const output = {
354
+
355
+ min: new Array( attribute.itemSize ).fill( Number.POSITIVE_INFINITY ),
356
+ max: new Array( attribute.itemSize ).fill( Number.NEGATIVE_INFINITY )
357
+
358
+ };
359
+
360
+ for ( let i = start; i < start + count; i ++ ) {
361
+
362
+ for ( let a = 0; a < attribute.itemSize; a ++ ) {
363
+
364
+ let value;
365
+
366
+ if ( attribute.itemSize > 4 ) {
367
+
368
+ // no support for interleaved data for itemSize > 4
369
+
370
+ value = attribute.array[ i * attribute.itemSize + a ];
371
+
372
+ } else {
373
+
374
+ if ( a === 0 ) value = attribute.getX( i );
375
+ else if ( a === 1 ) value = attribute.getY( i );
376
+ else if ( a === 2 ) value = attribute.getZ( i );
377
+ else if ( a === 3 ) value = attribute.getW( i );
378
+
379
+ if ( attribute.normalized === true ) {
380
+
381
+ value = MathUtils.normalize( value, attribute.array );
382
+
383
+ }
384
+
385
+ }
386
+
387
+ output.min[ a ] = Math.min( output.min[ a ], value );
388
+ output.max[ a ] = Math.max( output.max[ a ], value );
389
+
390
+ }
391
+
392
+ }
393
+
394
+ return output;
395
+
396
+ }
397
+
398
+ /**
399
+ * Get the required size + padding for a buffer, rounded to the next 4-byte boundary.
400
+ * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment
401
+ *
402
+ * @param {Integer} bufferSize The size the original buffer.
403
+ * @returns {Integer} new buffer size with required padding.
404
+ *
405
+ */
406
+ function getPaddedBufferSize( bufferSize ) {
407
+
408
+ return Math.ceil( bufferSize / 4 ) * 4;
409
+
410
+ }
411
+
412
+ /**
413
+ * Returns a buffer aligned to 4-byte boundary.
414
+ *
415
+ * @param {ArrayBuffer} arrayBuffer Buffer to pad
416
+ * @param {Integer} paddingByte (Optional)
417
+ * @returns {ArrayBuffer} The same buffer if it's already aligned to 4-byte boundary or a new buffer
418
+ */
419
+ function getPaddedArrayBuffer( arrayBuffer, paddingByte = 0 ) {
420
+
421
+ const paddedLength = getPaddedBufferSize( arrayBuffer.byteLength );
422
+
423
+ if ( paddedLength !== arrayBuffer.byteLength ) {
424
+
425
+ const array = new Uint8Array( paddedLength );
426
+ array.set( new Uint8Array( arrayBuffer ) );
427
+
428
+ if ( paddingByte !== 0 ) {
429
+
430
+ for ( let i = arrayBuffer.byteLength; i < paddedLength; i ++ ) {
431
+
432
+ array[ i ] = paddingByte;
433
+
434
+ }
435
+
436
+ }
437
+
438
+ return array.buffer;
439
+
440
+ }
441
+
442
+ return arrayBuffer;
443
+
444
+ }
445
+
446
+ function getCanvas() {
447
+
448
+ if ( typeof document === 'undefined' && typeof OffscreenCanvas !== 'undefined' ) {
449
+
450
+ return new OffscreenCanvas( 1, 1 );
451
+
452
+ }
453
+
454
+ return document.createElement( 'canvas' );
455
+
456
+ }
457
+
458
+ function getToBlobPromise( canvas, mimeType ) {
459
+
460
+ if ( canvas.toBlob !== undefined ) {
461
+
462
+ return new Promise( ( resolve ) => canvas.toBlob( resolve, mimeType ) );
463
+
464
+ }
465
+
466
+ let quality;
467
+
468
+ // Blink's implementation of convertToBlob seems to default to a quality level of 100%
469
+ // Use the Blink default quality levels of toBlob instead so that file sizes are comparable.
470
+ if ( mimeType === 'image/jpeg' ) {
471
+
472
+ quality = 0.92;
473
+
474
+ } else if ( mimeType === 'image/webp' ) {
475
+
476
+ quality = 0.8;
477
+
478
+ }
479
+
480
+ return canvas.convertToBlob( {
481
+
482
+ type: mimeType,
483
+ quality: quality
484
+
485
+ } );
486
+
487
+ }
488
+
489
+ /**
490
+ * Writer
491
+ */
492
+ class GLTFWriter {
493
+
494
+ constructor() {
495
+
496
+ this.plugins = [];
497
+
498
+ this.options = {};
499
+ this.pending = [];
500
+ this.buffers = [];
501
+
502
+ this.byteOffset = 0;
503
+ this.buffers = [];
504
+ this.nodeMap = new Map();
505
+ this.skins = [];
506
+
507
+ this.extensionsUsed = {};
508
+ this.extensionsRequired = {};
509
+
510
+ this.uids = new Map();
511
+ this.uid = 0;
512
+
513
+ this.json = {
514
+ asset: {
515
+ version: '2.0',
516
+ generator: 'THREE.GLTFExporter r' + REVISION
517
+ }
518
+ };
519
+
520
+ this.cache = {
521
+ meshes: new Map(),
522
+ attributes: new Map(),
523
+ attributesNormalized: new Map(),
524
+ materials: new Map(),
525
+ textures: new Map(),
526
+ images: new Map()
527
+ };
528
+
529
+ this.textureUtils = null;
530
+
531
+ }
532
+
533
+ setPlugins( plugins ) {
534
+
535
+ this.plugins = plugins;
536
+
537
+ }
538
+
539
+ setTextureUtils( utils ) {
540
+
541
+ this.textureUtils = utils;
542
+
543
+ }
544
+
545
+ /**
546
+ * Parse scenes and generate GLTF output
547
+ *
548
+ * @param {Scene|THREE.Scenes} input Scene or Array of THREE.Scenes
549
+ * @param {Function} onDone Callback on completed
550
+ * @param {Object} options options
551
+ */
552
+ async writeAsync( input, onDone, options = {} ) {
553
+
554
+ this.options = Object.assign( {
555
+ // default options
556
+ binary: false,
557
+ trs: false,
558
+ onlyVisible: true,
559
+ maxTextureSize: Infinity,
560
+ animations: [],
561
+ includeCustomExtensions: false
562
+ }, options );
563
+
564
+ if ( this.options.animations.length > 0 ) {
565
+
566
+ // Only TRS properties, and not matrices, may be targeted by animation.
567
+ this.options.trs = true;
568
+
569
+ }
570
+
571
+ await this.processInputAsync( input );
572
+
573
+ await Promise.all( this.pending );
574
+
575
+ const writer = this;
576
+ const buffers = writer.buffers;
577
+ const json = writer.json;
578
+ options = writer.options;
579
+
580
+ const extensionsUsed = writer.extensionsUsed;
581
+ const extensionsRequired = writer.extensionsRequired;
582
+
583
+ // Merge buffers.
584
+ const blob = new Blob( buffers, { type: 'application/octet-stream' } );
585
+
586
+ // Declare extensions.
587
+ const extensionsUsedList = Object.keys( extensionsUsed );
588
+ const extensionsRequiredList = Object.keys( extensionsRequired );
589
+
590
+ if ( extensionsUsedList.length > 0 ) json.extensionsUsed = extensionsUsedList;
591
+ if ( extensionsRequiredList.length > 0 ) json.extensionsRequired = extensionsRequiredList;
592
+
593
+ // Update bytelength of the single buffer.
594
+ if ( json.buffers && json.buffers.length > 0 ) json.buffers[ 0 ].byteLength = blob.size;
595
+
596
+ if ( options.binary === true ) {
597
+
598
+ // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification
599
+
600
+ const reader = new FileReader();
601
+ reader.readAsArrayBuffer( blob );
602
+ reader.onloadend = function () {
603
+
604
+ // Binary chunk.
605
+ const binaryChunk = getPaddedArrayBuffer( reader.result );
606
+ const binaryChunkPrefix = new DataView( new ArrayBuffer( GLB_CHUNK_PREFIX_BYTES ) );
607
+ binaryChunkPrefix.setUint32( 0, binaryChunk.byteLength, true );
608
+ binaryChunkPrefix.setUint32( 4, GLB_CHUNK_TYPE_BIN, true );
609
+
610
+ // JSON chunk.
611
+ const jsonChunk = getPaddedArrayBuffer( stringToArrayBuffer( JSON.stringify( json ) ), 0x20 );
612
+ const jsonChunkPrefix = new DataView( new ArrayBuffer( GLB_CHUNK_PREFIX_BYTES ) );
613
+ jsonChunkPrefix.setUint32( 0, jsonChunk.byteLength, true );
614
+ jsonChunkPrefix.setUint32( 4, GLB_CHUNK_TYPE_JSON, true );
615
+
616
+ // GLB header.
617
+ const header = new ArrayBuffer( GLB_HEADER_BYTES );
618
+ const headerView = new DataView( header );
619
+ headerView.setUint32( 0, GLB_HEADER_MAGIC, true );
620
+ headerView.setUint32( 4, GLB_VERSION, true );
621
+ const totalByteLength = GLB_HEADER_BYTES
622
+ + jsonChunkPrefix.byteLength + jsonChunk.byteLength
623
+ + binaryChunkPrefix.byteLength + binaryChunk.byteLength;
624
+ headerView.setUint32( 8, totalByteLength, true );
625
+
626
+ const glbBlob = new Blob( [
627
+ header,
628
+ jsonChunkPrefix,
629
+ jsonChunk,
630
+ binaryChunkPrefix,
631
+ binaryChunk
632
+ ], { type: 'application/octet-stream' } );
633
+
634
+ const glbReader = new FileReader();
635
+ glbReader.readAsArrayBuffer( glbBlob );
636
+ glbReader.onloadend = function () {
637
+
638
+ onDone( glbReader.result );
639
+
640
+ };
641
+
642
+ };
643
+
644
+ } else {
645
+
646
+ if ( json.buffers && json.buffers.length > 0 ) {
647
+
648
+ const reader = new FileReader();
649
+ reader.readAsDataURL( blob );
650
+ reader.onloadend = function () {
651
+
652
+ const base64data = reader.result;
653
+ json.buffers[ 0 ].uri = base64data;
654
+ onDone( json );
655
+
656
+ };
657
+
658
+ } else {
659
+
660
+ onDone( json );
661
+
662
+ }
663
+
664
+ }
665
+
666
+
667
+ }
668
+
669
+ /**
670
+ * Serializes a userData.
671
+ *
672
+ * @param {THREE.Object3D|THREE.Material} object
673
+ * @param {Object} objectDef
674
+ */
675
+ serializeUserData( object, objectDef ) {
676
+
677
+ if ( Object.keys( object.userData ).length === 0 ) return;
678
+
679
+ const options = this.options;
680
+ const extensionsUsed = this.extensionsUsed;
681
+
682
+ try {
683
+
684
+ const json = JSON.parse( JSON.stringify( object.userData ) );
685
+
686
+ if ( options.includeCustomExtensions && json.gltfExtensions ) {
687
+
688
+ if ( objectDef.extensions === undefined ) objectDef.extensions = {};
689
+
690
+ for ( const extensionName in json.gltfExtensions ) {
691
+
692
+ objectDef.extensions[ extensionName ] = json.gltfExtensions[ extensionName ];
693
+ extensionsUsed[ extensionName ] = true;
694
+
695
+ }
696
+
697
+ delete json.gltfExtensions;
698
+
699
+ }
700
+
701
+ if ( Object.keys( json ).length > 0 ) objectDef.extras = json;
702
+
703
+ } catch ( error ) {
704
+
705
+ console.warn( 'THREE.GLTFExporter: userData of \'' + object.name + '\' ' +
706
+ 'won\'t be serialized because of JSON.stringify error - ' + error.message );
707
+
708
+ }
709
+
710
+ }
711
+
712
+ /**
713
+ * Returns ids for buffer attributes.
714
+ *
715
+ * @param {Object} attribute
716
+ * @param {boolean} [isRelativeCopy=false]
717
+ * @return {Integer}
718
+ */
719
+ getUID( attribute, isRelativeCopy = false ) {
720
+
721
+ if ( this.uids.has( attribute ) === false ) {
722
+
723
+ const uids = new Map();
724
+
725
+ uids.set( true, this.uid ++ );
726
+ uids.set( false, this.uid ++ );
727
+
728
+ this.uids.set( attribute, uids );
729
+
730
+ }
731
+
732
+ const uids = this.uids.get( attribute );
733
+
734
+ return uids.get( isRelativeCopy );
735
+
736
+ }
737
+
738
+ /**
739
+ * Checks if normal attribute values are normalized.
740
+ *
741
+ * @param {BufferAttribute} normal
742
+ * @returns {Boolean}
743
+ */
744
+ isNormalizedNormalAttribute( normal ) {
745
+
746
+ const cache = this.cache;
747
+
748
+ if ( cache.attributesNormalized.has( normal ) ) return false;
749
+
750
+ const v = new Vector3();
751
+
752
+ for ( let i = 0, il = normal.count; i < il; i ++ ) {
753
+
754
+ // 0.0005 is from glTF-validator
755
+ if ( Math.abs( v.fromBufferAttribute( normal, i ).length() - 1.0 ) > 0.0005 ) return false;
756
+
757
+ }
758
+
759
+ return true;
760
+
761
+ }
762
+
763
+ /**
764
+ * Creates normalized normal buffer attribute.
765
+ *
766
+ * @param {BufferAttribute} normal
767
+ * @returns {BufferAttribute}
768
+ *
769
+ */
770
+ createNormalizedNormalAttribute( normal ) {
771
+
772
+ const cache = this.cache;
773
+
774
+ if ( cache.attributesNormalized.has( normal ) ) return cache.attributesNormalized.get( normal );
775
+
776
+ const attribute = normal.clone();
777
+ const v = new Vector3();
778
+
779
+ for ( let i = 0, il = attribute.count; i < il; i ++ ) {
780
+
781
+ v.fromBufferAttribute( attribute, i );
782
+
783
+ if ( v.x === 0 && v.y === 0 && v.z === 0 ) {
784
+
785
+ // if values can't be normalized set (1, 0, 0)
786
+ v.setX( 1.0 );
787
+
788
+ } else {
789
+
790
+ v.normalize();
791
+
792
+ }
793
+
794
+ attribute.setXYZ( i, v.x, v.y, v.z );
795
+
796
+ }
797
+
798
+ cache.attributesNormalized.set( normal, attribute );
799
+
800
+ return attribute;
801
+
802
+ }
803
+
804
+ /**
805
+ * Applies a texture transform, if present, to the map definition. Requires
806
+ * the KHR_texture_transform extension.
807
+ *
808
+ * @param {Object} mapDef
809
+ * @param {THREE.Texture} texture
810
+ */
811
+ applyTextureTransform( mapDef, texture ) {
812
+
813
+ let didTransform = false;
814
+ const transformDef = {};
815
+
816
+ if ( texture.offset.x !== 0 || texture.offset.y !== 0 ) {
817
+
818
+ transformDef.offset = texture.offset.toArray();
819
+ didTransform = true;
820
+
821
+ }
822
+
823
+ if ( texture.rotation !== 0 ) {
824
+
825
+ transformDef.rotation = texture.rotation;
826
+ didTransform = true;
827
+
828
+ }
829
+
830
+ if ( texture.repeat.x !== 1 || texture.repeat.y !== 1 ) {
831
+
832
+ transformDef.scale = texture.repeat.toArray();
833
+ didTransform = true;
834
+
835
+ }
836
+
837
+ if ( didTransform ) {
838
+
839
+ mapDef.extensions = mapDef.extensions || {};
840
+ mapDef.extensions[ 'KHR_texture_transform' ] = transformDef;
841
+ this.extensionsUsed[ 'KHR_texture_transform' ] = true;
842
+
843
+ }
844
+
845
+ }
846
+
847
+ async buildMetalRoughTextureAsync( metalnessMap, roughnessMap ) {
848
+
849
+ if ( metalnessMap === roughnessMap ) return metalnessMap;
850
+
851
+ function getEncodingConversion( map ) {
852
+
853
+ if ( map.colorSpace === SRGBColorSpace ) {
854
+
855
+ return function SRGBToLinear( c ) {
856
+
857
+ return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 );
858
+
859
+ };
860
+
861
+ }
862
+
863
+ return function LinearToLinear( c ) {
864
+
865
+ return c;
866
+
867
+ };
868
+
869
+ }
870
+
871
+ if ( metalnessMap instanceof CompressedTexture ) {
872
+
873
+ metalnessMap = await this.decompressTextureAsync( metalnessMap );
874
+
875
+ }
876
+
877
+ if ( roughnessMap instanceof CompressedTexture ) {
878
+
879
+ roughnessMap = await this.decompressTextureAsync( roughnessMap );
880
+
881
+ }
882
+
883
+ const metalness = metalnessMap ? metalnessMap.image : null;
884
+ const roughness = roughnessMap ? roughnessMap.image : null;
885
+
886
+ const width = Math.max( metalness ? metalness.width : 0, roughness ? roughness.width : 0 );
887
+ const height = Math.max( metalness ? metalness.height : 0, roughness ? roughness.height : 0 );
888
+
889
+ const canvas = getCanvas();
890
+ canvas.width = width;
891
+ canvas.height = height;
892
+
893
+ const context = canvas.getContext( '2d', {
894
+ willReadFrequently: true,
895
+ } );
896
+ context.fillStyle = '#00ffff';
897
+ context.fillRect( 0, 0, width, height );
898
+
899
+ const composite = context.getImageData( 0, 0, width, height );
900
+
901
+ if ( metalness ) {
902
+
903
+ context.drawImage( metalness, 0, 0, width, height );
904
+
905
+ const convert = getEncodingConversion( metalnessMap );
906
+ const data = context.getImageData( 0, 0, width, height ).data;
907
+
908
+ for ( let i = 2; i < data.length; i += 4 ) {
909
+
910
+ composite.data[ i ] = convert( data[ i ] / 256 ) * 256;
911
+
912
+ }
913
+
914
+ }
915
+
916
+ if ( roughness ) {
917
+
918
+ context.drawImage( roughness, 0, 0, width, height );
919
+
920
+ const convert = getEncodingConversion( roughnessMap );
921
+ const data = context.getImageData( 0, 0, width, height ).data;
922
+
923
+ for ( let i = 1; i < data.length; i += 4 ) {
924
+
925
+ composite.data[ i ] = convert( data[ i ] / 256 ) * 256;
926
+
927
+ }
928
+
929
+ }
930
+
931
+ context.putImageData( composite, 0, 0 );
932
+
933
+ //
934
+
935
+ const reference = metalnessMap || roughnessMap;
936
+
937
+ const texture = reference.clone();
938
+
939
+ texture.source = new Source( canvas );
940
+ texture.colorSpace = NoColorSpace;
941
+ texture.channel = ( metalnessMap || roughnessMap ).channel;
942
+
943
+ if ( metalnessMap && roughnessMap && metalnessMap.channel !== roughnessMap.channel ) {
944
+
945
+ console.warn( 'THREE.GLTFExporter: UV channels for metalnessMap and roughnessMap textures must match.' );
946
+
947
+ }
948
+
949
+ console.warn( 'THREE.GLTFExporter: Merged metalnessMap and roughnessMap textures.' );
950
+
951
+ return texture;
952
+
953
+ }
954
+
955
+
956
+ async decompressTextureAsync( texture, maxTextureSize = Infinity ) {
957
+
958
+ if ( this.textureUtils === null ) {
959
+
960
+ throw new Error( 'THREE.GLTFExporter: setTextureUtils() must be called to process compressed textures.' );
961
+
962
+ }
963
+
964
+ return await this.textureUtils.decompress( texture, maxTextureSize );
965
+
966
+ }
967
+
968
+ /**
969
+ * Process a buffer to append to the default one.
970
+ * @param {ArrayBuffer} buffer
971
+ * @return {Integer}
972
+ */
973
+ processBuffer( buffer ) {
974
+
975
+ const json = this.json;
976
+ const buffers = this.buffers;
977
+
978
+ if ( ! json.buffers ) json.buffers = [ { byteLength: 0 } ];
979
+
980
+ // All buffers are merged before export.
981
+ buffers.push( buffer );
982
+
983
+ return 0;
984
+
985
+ }
986
+
987
+ /**
988
+ * Process and generate a BufferView
989
+ * @param {BufferAttribute} attribute
990
+ * @param {number} componentType
991
+ * @param {number} start
992
+ * @param {number} count
993
+ * @param {number} target (Optional) Target usage of the BufferView
994
+ * @return {Object}
995
+ */
996
+ processBufferView( attribute, componentType, start, count, target ) {
997
+
998
+ const json = this.json;
999
+
1000
+ if ( ! json.bufferViews ) json.bufferViews = [];
1001
+
1002
+ // Create a new dataview and dump the attribute's array into it
1003
+
1004
+ let componentSize;
1005
+
1006
+ switch ( componentType ) {
1007
+
1008
+ case WEBGL_CONSTANTS.BYTE:
1009
+ case WEBGL_CONSTANTS.UNSIGNED_BYTE:
1010
+
1011
+ componentSize = 1;
1012
+
1013
+ break;
1014
+
1015
+ case WEBGL_CONSTANTS.SHORT:
1016
+ case WEBGL_CONSTANTS.UNSIGNED_SHORT:
1017
+
1018
+ componentSize = 2;
1019
+
1020
+ break;
1021
+
1022
+ default:
1023
+
1024
+ componentSize = 4;
1025
+
1026
+ }
1027
+
1028
+ let byteStride = attribute.itemSize * componentSize;
1029
+
1030
+ if ( target === WEBGL_CONSTANTS.ARRAY_BUFFER ) {
1031
+
1032
+ // Each element of a vertex attribute MUST be aligned to 4-byte boundaries
1033
+ // inside a bufferView
1034
+ byteStride = Math.ceil( byteStride / 4 ) * 4;
1035
+
1036
+ }
1037
+
1038
+ const byteLength = getPaddedBufferSize( count * byteStride );
1039
+ const dataView = new DataView( new ArrayBuffer( byteLength ) );
1040
+ let offset = 0;
1041
+
1042
+ for ( let i = start; i < start + count; i ++ ) {
1043
+
1044
+ for ( let a = 0; a < attribute.itemSize; a ++ ) {
1045
+
1046
+ let value;
1047
+
1048
+ if ( attribute.itemSize > 4 ) {
1049
+
1050
+ // no support for interleaved data for itemSize > 4
1051
+
1052
+ value = attribute.array[ i * attribute.itemSize + a ];
1053
+
1054
+ } else {
1055
+
1056
+ if ( a === 0 ) value = attribute.getX( i );
1057
+ else if ( a === 1 ) value = attribute.getY( i );
1058
+ else if ( a === 2 ) value = attribute.getZ( i );
1059
+ else if ( a === 3 ) value = attribute.getW( i );
1060
+
1061
+ if ( attribute.normalized === true ) {
1062
+
1063
+ value = MathUtils.normalize( value, attribute.array );
1064
+
1065
+ }
1066
+
1067
+ }
1068
+
1069
+ if ( componentType === WEBGL_CONSTANTS.FLOAT ) {
1070
+
1071
+ dataView.setFloat32( offset, value, true );
1072
+
1073
+ } else if ( componentType === WEBGL_CONSTANTS.INT ) {
1074
+
1075
+ dataView.setInt32( offset, value, true );
1076
+
1077
+ } else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_INT ) {
1078
+
1079
+ dataView.setUint32( offset, value, true );
1080
+
1081
+ } else if ( componentType === WEBGL_CONSTANTS.SHORT ) {
1082
+
1083
+ dataView.setInt16( offset, value, true );
1084
+
1085
+ } else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ) {
1086
+
1087
+ dataView.setUint16( offset, value, true );
1088
+
1089
+ } else if ( componentType === WEBGL_CONSTANTS.BYTE ) {
1090
+
1091
+ dataView.setInt8( offset, value );
1092
+
1093
+ } else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_BYTE ) {
1094
+
1095
+ dataView.setUint8( offset, value );
1096
+
1097
+ }
1098
+
1099
+ offset += componentSize;
1100
+
1101
+ }
1102
+
1103
+ if ( ( offset % byteStride ) !== 0 ) {
1104
+
1105
+ offset += byteStride - ( offset % byteStride );
1106
+
1107
+ }
1108
+
1109
+ }
1110
+
1111
+ const bufferViewDef = {
1112
+
1113
+ buffer: this.processBuffer( dataView.buffer ),
1114
+ byteOffset: this.byteOffset,
1115
+ byteLength: byteLength
1116
+
1117
+ };
1118
+
1119
+ if ( target !== undefined ) bufferViewDef.target = target;
1120
+
1121
+ if ( target === WEBGL_CONSTANTS.ARRAY_BUFFER ) {
1122
+
1123
+ // Only define byteStride for vertex attributes.
1124
+ bufferViewDef.byteStride = byteStride;
1125
+
1126
+ }
1127
+
1128
+ this.byteOffset += byteLength;
1129
+
1130
+ json.bufferViews.push( bufferViewDef );
1131
+
1132
+ // @TODO Merge bufferViews where possible.
1133
+ const output = {
1134
+
1135
+ id: json.bufferViews.length - 1,
1136
+ byteLength: 0
1137
+
1138
+ };
1139
+
1140
+ return output;
1141
+
1142
+ }
1143
+
1144
+ /**
1145
+ * Process and generate a BufferView from an image Blob.
1146
+ * @param {Blob} blob
1147
+ * @return {Promise<Integer>}
1148
+ */
1149
+ processBufferViewImage( blob ) {
1150
+
1151
+ const writer = this;
1152
+ const json = writer.json;
1153
+
1154
+ if ( ! json.bufferViews ) json.bufferViews = [];
1155
+
1156
+ return new Promise( function ( resolve ) {
1157
+
1158
+ const reader = new FileReader();
1159
+ reader.readAsArrayBuffer( blob );
1160
+ reader.onloadend = function () {
1161
+
1162
+ const buffer = getPaddedArrayBuffer( reader.result );
1163
+
1164
+ const bufferViewDef = {
1165
+ buffer: writer.processBuffer( buffer ),
1166
+ byteOffset: writer.byteOffset,
1167
+ byteLength: buffer.byteLength
1168
+ };
1169
+
1170
+ writer.byteOffset += buffer.byteLength;
1171
+ resolve( json.bufferViews.push( bufferViewDef ) - 1 );
1172
+
1173
+ };
1174
+
1175
+ } );
1176
+
1177
+ }
1178
+
1179
+ /**
1180
+ * Process attribute to generate an accessor
1181
+ * @param {BufferAttribute} attribute Attribute to process
1182
+ * @param {THREE.BufferGeometry} geometry (Optional) Geometry used for truncated draw range
1183
+ * @param {Integer} start (Optional)
1184
+ * @param {Integer} count (Optional)
1185
+ * @return {Integer|null} Index of the processed accessor on the "accessors" array
1186
+ */
1187
+ processAccessor( attribute, geometry, start, count ) {
1188
+
1189
+ const json = this.json;
1190
+
1191
+ const types = {
1192
+
1193
+ 1: 'SCALAR',
1194
+ 2: 'VEC2',
1195
+ 3: 'VEC3',
1196
+ 4: 'VEC4',
1197
+ 9: 'MAT3',
1198
+ 16: 'MAT4'
1199
+
1200
+ };
1201
+
1202
+ let componentType;
1203
+
1204
+ // Detect the component type of the attribute array
1205
+ if ( attribute.array.constructor === Float32Array ) {
1206
+
1207
+ componentType = WEBGL_CONSTANTS.FLOAT;
1208
+
1209
+ } else if ( attribute.array.constructor === Int32Array ) {
1210
+
1211
+ componentType = WEBGL_CONSTANTS.INT;
1212
+
1213
+ } else if ( attribute.array.constructor === Uint32Array ) {
1214
+
1215
+ componentType = WEBGL_CONSTANTS.UNSIGNED_INT;
1216
+
1217
+ } else if ( attribute.array.constructor === Int16Array ) {
1218
+
1219
+ componentType = WEBGL_CONSTANTS.SHORT;
1220
+
1221
+ } else if ( attribute.array.constructor === Uint16Array ) {
1222
+
1223
+ componentType = WEBGL_CONSTANTS.UNSIGNED_SHORT;
1224
+
1225
+ } else if ( attribute.array.constructor === Int8Array ) {
1226
+
1227
+ componentType = WEBGL_CONSTANTS.BYTE;
1228
+
1229
+ } else if ( attribute.array.constructor === Uint8Array ) {
1230
+
1231
+ componentType = WEBGL_CONSTANTS.UNSIGNED_BYTE;
1232
+
1233
+ } else {
1234
+
1235
+ throw new Error( 'THREE.GLTFExporter: Unsupported bufferAttribute component type: ' + attribute.array.constructor.name );
1236
+
1237
+ }
1238
+
1239
+ if ( start === undefined ) start = 0;
1240
+ if ( count === undefined || count === Infinity ) count = attribute.count;
1241
+
1242
+ // Skip creating an accessor if the attribute doesn't have data to export
1243
+ if ( count === 0 ) return null;
1244
+
1245
+ const minMax = getMinMax( attribute, start, count );
1246
+ let bufferViewTarget;
1247
+
1248
+ // If geometry isn't provided, don't infer the target usage of the bufferView. For
1249
+ // animation samplers, target must not be set.
1250
+ if ( geometry !== undefined ) {
1251
+
1252
+ bufferViewTarget = attribute === geometry.index ? WEBGL_CONSTANTS.ELEMENT_ARRAY_BUFFER : WEBGL_CONSTANTS.ARRAY_BUFFER;
1253
+
1254
+ }
1255
+
1256
+ const bufferView = this.processBufferView( attribute, componentType, start, count, bufferViewTarget );
1257
+
1258
+ const accessorDef = {
1259
+
1260
+ bufferView: bufferView.id,
1261
+ byteOffset: bufferView.byteOffset,
1262
+ componentType: componentType,
1263
+ count: count,
1264
+ max: minMax.max,
1265
+ min: minMax.min,
1266
+ type: types[ attribute.itemSize ]
1267
+
1268
+ };
1269
+
1270
+ if ( attribute.normalized === true ) accessorDef.normalized = true;
1271
+ if ( ! json.accessors ) json.accessors = [];
1272
+
1273
+ return json.accessors.push( accessorDef ) - 1;
1274
+
1275
+ }
1276
+
1277
+ /**
1278
+ * Process image
1279
+ * @param {Image} image to process
1280
+ * @param {Integer} format of the image (RGBAFormat)
1281
+ * @param {Boolean} flipY before writing out the image
1282
+ * @param {String} mimeType export format
1283
+ * @return {Integer} Index of the processed texture in the "images" array
1284
+ */
1285
+ processImage( image, format, flipY, mimeType = 'image/png' ) {
1286
+
1287
+ if ( image !== null ) {
1288
+
1289
+ const writer = this;
1290
+ const cache = writer.cache;
1291
+ const json = writer.json;
1292
+ const options = writer.options;
1293
+ const pending = writer.pending;
1294
+
1295
+ if ( ! cache.images.has( image ) ) cache.images.set( image, {} );
1296
+
1297
+ const cachedImages = cache.images.get( image );
1298
+
1299
+ const key = mimeType + ':flipY/' + flipY.toString();
1300
+
1301
+ if ( cachedImages[ key ] !== undefined ) return cachedImages[ key ];
1302
+
1303
+ if ( ! json.images ) json.images = [];
1304
+
1305
+ const imageDef = { mimeType: mimeType };
1306
+
1307
+ const canvas = getCanvas();
1308
+
1309
+ canvas.width = Math.min( image.width, options.maxTextureSize );
1310
+ canvas.height = Math.min( image.height, options.maxTextureSize );
1311
+
1312
+ const ctx = canvas.getContext( '2d', {
1313
+ willReadFrequently: true,
1314
+ } );
1315
+
1316
+ if ( flipY === true ) {
1317
+
1318
+ ctx.translate( 0, canvas.height );
1319
+ ctx.scale( 1, - 1 );
1320
+
1321
+ }
1322
+
1323
+ if ( image.data !== undefined ) { // THREE.DataTexture
1324
+
1325
+ if ( format !== RGBAFormat ) {
1326
+
1327
+ console.error( 'GLTFExporter: Only RGBAFormat is supported.', format );
1328
+
1329
+ }
1330
+
1331
+ if ( image.width > options.maxTextureSize || image.height > options.maxTextureSize ) {
1332
+
1333
+ console.warn( 'GLTFExporter: Image size is bigger than maxTextureSize', image );
1334
+
1335
+ }
1336
+
1337
+ const data = new Uint8ClampedArray( image.height * image.width * 4 );
1338
+
1339
+ for ( let i = 0; i < data.length; i += 4 ) {
1340
+
1341
+ data[ i + 0 ] = image.data[ i + 0 ];
1342
+ data[ i + 1 ] = image.data[ i + 1 ];
1343
+ data[ i + 2 ] = image.data[ i + 2 ];
1344
+ data[ i + 3 ] = image.data[ i + 3 ];
1345
+
1346
+ }
1347
+
1348
+ ctx.putImageData( new ImageData( data, image.width, image.height ), 0, 0 );
1349
+
1350
+ } else {
1351
+
1352
+ if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
1353
+ ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
1354
+ ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ||
1355
+ ( typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas ) ) {
1356
+
1357
+ ctx.drawImage( image, 0, 0, canvas.width, canvas.height );
1358
+
1359
+ } else {
1360
+
1361
+ throw new Error( 'THREE.GLTFExporter: Invalid image type. Use HTMLImageElement, HTMLCanvasElement, ImageBitmap or OffscreenCanvas.' );
1362
+
1363
+ }
1364
+
1365
+ }
1366
+
1367
+ if ( options.binary === true ) {
1368
+
1369
+ pending.push(
1370
+
1371
+ getToBlobPromise( canvas, mimeType )
1372
+ .then( blob => writer.processBufferViewImage( blob ) )
1373
+ .then( bufferViewIndex => {
1374
+
1375
+ imageDef.bufferView = bufferViewIndex;
1376
+
1377
+ } )
1378
+
1379
+ );
1380
+
1381
+ } else {
1382
+
1383
+ if ( canvas.toDataURL !== undefined ) {
1384
+
1385
+ imageDef.uri = canvas.toDataURL( mimeType );
1386
+
1387
+ } else {
1388
+
1389
+ pending.push(
1390
+
1391
+ getToBlobPromise( canvas, mimeType )
1392
+ .then( blob => new FileReader().readAsDataURL( blob ) )
1393
+ .then( dataURL => {
1394
+
1395
+ imageDef.uri = dataURL;
1396
+
1397
+ } )
1398
+
1399
+ );
1400
+
1401
+ }
1402
+
1403
+ }
1404
+
1405
+ const index = json.images.push( imageDef ) - 1;
1406
+ cachedImages[ key ] = index;
1407
+ return index;
1408
+
1409
+ } else {
1410
+
1411
+ throw new Error( 'THREE.GLTFExporter: No valid image data found. Unable to process texture.' );
1412
+
1413
+ }
1414
+
1415
+ }
1416
+
1417
+ /**
1418
+ * Process sampler
1419
+ * @param {Texture} map Texture to process
1420
+ * @return {Integer} Index of the processed texture in the "samplers" array
1421
+ */
1422
+ processSampler( map ) {
1423
+
1424
+ const json = this.json;
1425
+
1426
+ if ( ! json.samplers ) json.samplers = [];
1427
+
1428
+ const samplerDef = {
1429
+ magFilter: THREE_TO_WEBGL[ map.magFilter ],
1430
+ minFilter: THREE_TO_WEBGL[ map.minFilter ],
1431
+ wrapS: THREE_TO_WEBGL[ map.wrapS ],
1432
+ wrapT: THREE_TO_WEBGL[ map.wrapT ]
1433
+ };
1434
+
1435
+ return json.samplers.push( samplerDef ) - 1;
1436
+
1437
+ }
1438
+
1439
+ /**
1440
+ * Process texture
1441
+ * @param {Texture} map Map to process
1442
+ * @return {Integer} Index of the processed texture in the "textures" array
1443
+ */
1444
+ async processTextureAsync( map ) {
1445
+
1446
+ const writer = this;
1447
+ const options = writer.options;
1448
+ const cache = this.cache;
1449
+ const json = this.json;
1450
+
1451
+ if ( cache.textures.has( map ) ) return cache.textures.get( map );
1452
+
1453
+ if ( ! json.textures ) json.textures = [];
1454
+
1455
+ // make non-readable textures (e.g. CompressedTexture) readable by blitting them into a new texture
1456
+ if ( map instanceof CompressedTexture ) {
1457
+
1458
+ map = await this.decompressTextureAsync( map, options.maxTextureSize );
1459
+
1460
+ }
1461
+
1462
+ let mimeType = map.userData.mimeType;
1463
+
1464
+ if ( mimeType === 'image/webp' ) mimeType = 'image/png';
1465
+
1466
+ const textureDef = {
1467
+ sampler: this.processSampler( map ),
1468
+ source: this.processImage( map.image, map.format, map.flipY, mimeType )
1469
+ };
1470
+
1471
+ if ( map.name ) textureDef.name = map.name;
1472
+
1473
+ await this._invokeAllAsync( async function ( ext ) {
1474
+
1475
+ ext.writeTexture && await ext.writeTexture( map, textureDef );
1476
+
1477
+ } );
1478
+
1479
+ const index = json.textures.push( textureDef ) - 1;
1480
+ cache.textures.set( map, index );
1481
+ return index;
1482
+
1483
+ }
1484
+
1485
+ /**
1486
+ * Process material
1487
+ * @param {THREE.Material} material Material to process
1488
+ * @return {Integer|null} Index of the processed material in the "materials" array
1489
+ */
1490
+ async processMaterialAsync( material ) {
1491
+
1492
+ const cache = this.cache;
1493
+ const json = this.json;
1494
+
1495
+ if ( cache.materials.has( material ) ) return cache.materials.get( material );
1496
+
1497
+ if ( material.isShaderMaterial ) {
1498
+
1499
+ console.warn( 'GLTFExporter: THREE.ShaderMaterial not supported.' );
1500
+ return null;
1501
+
1502
+ }
1503
+
1504
+ if ( ! json.materials ) json.materials = [];
1505
+
1506
+ // @QUESTION Should we avoid including any attribute that has the default value?
1507
+ const materialDef = { pbrMetallicRoughness: {} };
1508
+
1509
+ if ( material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true ) {
1510
+
1511
+ console.warn( 'GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.' );
1512
+
1513
+ }
1514
+
1515
+ // pbrMetallicRoughness.baseColorFactor
1516
+ const color = material.color.toArray().concat( [ material.opacity ] );
1517
+
1518
+ if ( ! equalArray( color, [ 1, 1, 1, 1 ] ) ) {
1519
+
1520
+ materialDef.pbrMetallicRoughness.baseColorFactor = color;
1521
+
1522
+ }
1523
+
1524
+ if ( material.isMeshStandardMaterial ) {
1525
+
1526
+ materialDef.pbrMetallicRoughness.metallicFactor = material.metalness;
1527
+ materialDef.pbrMetallicRoughness.roughnessFactor = material.roughness;
1528
+
1529
+ } else {
1530
+
1531
+ materialDef.pbrMetallicRoughness.metallicFactor = 0;
1532
+ materialDef.pbrMetallicRoughness.roughnessFactor = 1;
1533
+
1534
+ }
1535
+
1536
+ // pbrMetallicRoughness.metallicRoughnessTexture
1537
+ if ( material.metalnessMap || material.roughnessMap ) {
1538
+
1539
+ const metalRoughTexture = await this.buildMetalRoughTextureAsync( material.metalnessMap, material.roughnessMap );
1540
+
1541
+ const metalRoughMapDef = {
1542
+ index: await this.processTextureAsync( metalRoughTexture ),
1543
+ texCoord: metalRoughTexture.channel
1544
+ };
1545
+ this.applyTextureTransform( metalRoughMapDef, metalRoughTexture );
1546
+ materialDef.pbrMetallicRoughness.metallicRoughnessTexture = metalRoughMapDef;
1547
+
1548
+ }
1549
+
1550
+ // pbrMetallicRoughness.baseColorTexture
1551
+ if ( material.map ) {
1552
+
1553
+ const baseColorMapDef = {
1554
+ index: await this.processTextureAsync( material.map ),
1555
+ texCoord: material.map.channel
1556
+ };
1557
+ this.applyTextureTransform( baseColorMapDef, material.map );
1558
+ materialDef.pbrMetallicRoughness.baseColorTexture = baseColorMapDef;
1559
+
1560
+ }
1561
+
1562
+ if ( material.emissive ) {
1563
+
1564
+ const emissive = material.emissive;
1565
+ const maxEmissiveComponent = Math.max( emissive.r, emissive.g, emissive.b );
1566
+
1567
+ if ( maxEmissiveComponent > 0 ) {
1568
+
1569
+ materialDef.emissiveFactor = material.emissive.toArray();
1570
+
1571
+ }
1572
+
1573
+ // emissiveTexture
1574
+ if ( material.emissiveMap ) {
1575
+
1576
+ const emissiveMapDef = {
1577
+ index: await this.processTextureAsync( material.emissiveMap ),
1578
+ texCoord: material.emissiveMap.channel
1579
+ };
1580
+ this.applyTextureTransform( emissiveMapDef, material.emissiveMap );
1581
+ materialDef.emissiveTexture = emissiveMapDef;
1582
+
1583
+ }
1584
+
1585
+ }
1586
+
1587
+ // normalTexture
1588
+ if ( material.normalMap ) {
1589
+
1590
+ const normalMapDef = {
1591
+ index: await this.processTextureAsync( material.normalMap ),
1592
+ texCoord: material.normalMap.channel
1593
+ };
1594
+
1595
+ if ( material.normalScale && material.normalScale.x !== 1 ) {
1596
+
1597
+ // glTF normal scale is univariate. Ignore `y`, which may be flipped.
1598
+ // Context: https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
1599
+ normalMapDef.scale = material.normalScale.x;
1600
+
1601
+ }
1602
+
1603
+ this.applyTextureTransform( normalMapDef, material.normalMap );
1604
+ materialDef.normalTexture = normalMapDef;
1605
+
1606
+ }
1607
+
1608
+ // occlusionTexture
1609
+ if ( material.aoMap ) {
1610
+
1611
+ const occlusionMapDef = {
1612
+ index: await this.processTextureAsync( material.aoMap ),
1613
+ texCoord: material.aoMap.channel
1614
+ };
1615
+
1616
+ if ( material.aoMapIntensity !== 1.0 ) {
1617
+
1618
+ occlusionMapDef.strength = material.aoMapIntensity;
1619
+
1620
+ }
1621
+
1622
+ this.applyTextureTransform( occlusionMapDef, material.aoMap );
1623
+ materialDef.occlusionTexture = occlusionMapDef;
1624
+
1625
+ }
1626
+
1627
+ // alphaMode
1628
+ if ( material.transparent ) {
1629
+
1630
+ materialDef.alphaMode = 'BLEND';
1631
+
1632
+ } else {
1633
+
1634
+ if ( material.alphaTest > 0.0 ) {
1635
+
1636
+ materialDef.alphaMode = 'MASK';
1637
+ materialDef.alphaCutoff = material.alphaTest;
1638
+
1639
+ }
1640
+
1641
+ }
1642
+
1643
+ // doubleSided
1644
+ if ( material.side === DoubleSide ) materialDef.doubleSided = true;
1645
+ if ( material.name !== '' ) materialDef.name = material.name;
1646
+
1647
+ this.serializeUserData( material, materialDef );
1648
+
1649
+ await this._invokeAllAsync( async function ( ext ) {
1650
+
1651
+ ext.writeMaterialAsync && await ext.writeMaterialAsync( material, materialDef );
1652
+
1653
+ } );
1654
+
1655
+ const index = json.materials.push( materialDef ) - 1;
1656
+ cache.materials.set( material, index );
1657
+ return index;
1658
+
1659
+ }
1660
+
1661
+ /**
1662
+ * Process mesh
1663
+ * @param {THREE.Mesh} mesh Mesh to process
1664
+ * @return {Integer|null} Index of the processed mesh in the "meshes" array
1665
+ */
1666
+ async processMeshAsync( mesh ) {
1667
+
1668
+ const cache = this.cache;
1669
+ const json = this.json;
1670
+
1671
+ const meshCacheKeyParts = [ mesh.geometry.uuid ];
1672
+
1673
+ if ( Array.isArray( mesh.material ) ) {
1674
+
1675
+ for ( let i = 0, l = mesh.material.length; i < l; i ++ ) {
1676
+
1677
+ meshCacheKeyParts.push( mesh.material[ i ].uuid );
1678
+
1679
+ }
1680
+
1681
+ } else {
1682
+
1683
+ meshCacheKeyParts.push( mesh.material.uuid );
1684
+
1685
+ }
1686
+
1687
+ const meshCacheKey = meshCacheKeyParts.join( ':' );
1688
+
1689
+ if ( cache.meshes.has( meshCacheKey ) ) return cache.meshes.get( meshCacheKey );
1690
+
1691
+ const geometry = mesh.geometry;
1692
+
1693
+ let mode;
1694
+
1695
+ // Use the correct mode
1696
+ if ( mesh.isLineSegments ) {
1697
+
1698
+ mode = WEBGL_CONSTANTS.LINES;
1699
+
1700
+ } else if ( mesh.isLineLoop ) {
1701
+
1702
+ mode = WEBGL_CONSTANTS.LINE_LOOP;
1703
+
1704
+ } else if ( mesh.isLine ) {
1705
+
1706
+ mode = WEBGL_CONSTANTS.LINE_STRIP;
1707
+
1708
+ } else if ( mesh.isPoints ) {
1709
+
1710
+ mode = WEBGL_CONSTANTS.POINTS;
1711
+
1712
+ } else {
1713
+
1714
+ mode = mesh.material.wireframe ? WEBGL_CONSTANTS.LINES : WEBGL_CONSTANTS.TRIANGLES;
1715
+
1716
+ }
1717
+
1718
+ const meshDef = {};
1719
+ const attributes = {};
1720
+ const primitives = [];
1721
+ const targets = [];
1722
+
1723
+ // Conversion between attributes names in threejs and gltf spec
1724
+ const nameConversion = {
1725
+ uv: 'TEXCOORD_0',
1726
+ uv1: 'TEXCOORD_1',
1727
+ uv2: 'TEXCOORD_2',
1728
+ uv3: 'TEXCOORD_3',
1729
+ color: 'COLOR_0',
1730
+ skinWeight: 'WEIGHTS_0',
1731
+ skinIndex: 'JOINTS_0'
1732
+ };
1733
+
1734
+ const originalNormal = geometry.getAttribute( 'normal' );
1735
+
1736
+ if ( originalNormal !== undefined && ! this.isNormalizedNormalAttribute( originalNormal ) ) {
1737
+
1738
+ console.warn( 'THREE.GLTFExporter: Creating normalized normal attribute from the non-normalized one.' );
1739
+
1740
+ geometry.setAttribute( 'normal', this.createNormalizedNormalAttribute( originalNormal ) );
1741
+
1742
+ }
1743
+
1744
+ // @QUESTION Detect if .vertexColors = true?
1745
+ // For every attribute create an accessor
1746
+ let modifiedAttribute = null;
1747
+
1748
+ for ( let attributeName in geometry.attributes ) {
1749
+
1750
+ // Ignore morph target attributes, which are exported later.
1751
+ if ( attributeName.slice( 0, 5 ) === 'morph' ) continue;
1752
+
1753
+ const attribute = geometry.attributes[ attributeName ];
1754
+ attributeName = nameConversion[ attributeName ] || attributeName.toUpperCase();
1755
+
1756
+ // Prefix all geometry attributes except the ones specifically
1757
+ // listed in the spec; non-spec attributes are considered custom.
1758
+ const validVertexAttributes =
1759
+ /^(POSITION|NORMAL|TANGENT|TEXCOORD_\d+|COLOR_\d+|JOINTS_\d+|WEIGHTS_\d+)$/;
1760
+
1761
+ if ( ! validVertexAttributes.test( attributeName ) ) attributeName = '_' + attributeName;
1762
+
1763
+ if ( cache.attributes.has( this.getUID( attribute ) ) ) {
1764
+
1765
+ attributes[ attributeName ] = cache.attributes.get( this.getUID( attribute ) );
1766
+ continue;
1767
+
1768
+ }
1769
+
1770
+ // Enforce glTF vertex attribute requirements:
1771
+ // - JOINTS_0 must be UNSIGNED_BYTE or UNSIGNED_SHORT
1772
+ // - Only custom attributes may be INT or UNSIGNED_INT
1773
+ modifiedAttribute = null;
1774
+ const array = attribute.array;
1775
+
1776
+ if ( attributeName === 'JOINTS_0' &&
1777
+ ! ( array instanceof Uint16Array ) &&
1778
+ ! ( array instanceof Uint8Array ) ) {
1779
+
1780
+ console.warn( 'GLTFExporter: Attribute "skinIndex" converted to type UNSIGNED_SHORT.' );
1781
+ modifiedAttribute = new BufferAttribute( new Uint16Array( array ), attribute.itemSize, attribute.normalized );
1782
+
1783
+ } else if ( ( array instanceof Uint32Array || array instanceof Int32Array ) && ! attributeName.startsWith( '_' ) ) {
1784
+
1785
+ console.warn( `GLTFExporter: Attribute "${ attributeName }" converted to type FLOAT.` );
1786
+ modifiedAttribute = GLTFExporter.Utils.toFloat32BufferAttribute( attribute );
1787
+
1788
+ }
1789
+
1790
+ const accessor = this.processAccessor( modifiedAttribute || attribute, geometry );
1791
+
1792
+ if ( accessor !== null ) {
1793
+
1794
+ if ( ! attributeName.startsWith( '_' ) ) {
1795
+
1796
+ this.detectMeshQuantization( attributeName, attribute );
1797
+
1798
+ }
1799
+
1800
+ attributes[ attributeName ] = accessor;
1801
+ cache.attributes.set( this.getUID( attribute ), accessor );
1802
+
1803
+ }
1804
+
1805
+ }
1806
+
1807
+ if ( originalNormal !== undefined ) geometry.setAttribute( 'normal', originalNormal );
1808
+
1809
+ // Skip if no exportable attributes found
1810
+ if ( Object.keys( attributes ).length === 0 ) return null;
1811
+
1812
+ // Morph targets
1813
+ if ( mesh.morphTargetInfluences !== undefined && mesh.morphTargetInfluences.length > 0 ) {
1814
+
1815
+ const weights = [];
1816
+ const targetNames = [];
1817
+ const reverseDictionary = {};
1818
+
1819
+ if ( mesh.morphTargetDictionary !== undefined ) {
1820
+
1821
+ for ( const key in mesh.morphTargetDictionary ) {
1822
+
1823
+ reverseDictionary[ mesh.morphTargetDictionary[ key ] ] = key;
1824
+
1825
+ }
1826
+
1827
+ }
1828
+
1829
+ for ( let i = 0; i < mesh.morphTargetInfluences.length; ++ i ) {
1830
+
1831
+ const target = {};
1832
+ let warned = false;
1833
+
1834
+ for ( const attributeName in geometry.morphAttributes ) {
1835
+
1836
+ // glTF 2.0 morph supports only POSITION/NORMAL/TANGENT.
1837
+ // Three.js doesn't support TANGENT yet.
1838
+
1839
+ if ( attributeName !== 'position' && attributeName !== 'normal' ) {
1840
+
1841
+ if ( ! warned ) {
1842
+
1843
+ console.warn( 'GLTFExporter: Only POSITION and NORMAL morph are supported.' );
1844
+ warned = true;
1845
+
1846
+ }
1847
+
1848
+ continue;
1849
+
1850
+ }
1851
+
1852
+ const attribute = geometry.morphAttributes[ attributeName ][ i ];
1853
+ const gltfAttributeName = attributeName.toUpperCase();
1854
+
1855
+ // Three.js morph attribute has absolute values while the one of glTF has relative values.
1856
+ //
1857
+ // glTF 2.0 Specification:
1858
+ // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#morph-targets
1859
+
1860
+ const baseAttribute = geometry.attributes[ attributeName ];
1861
+
1862
+ if ( cache.attributes.has( this.getUID( attribute, true ) ) ) {
1863
+
1864
+ target[ gltfAttributeName ] = cache.attributes.get( this.getUID( attribute, true ) );
1865
+ continue;
1866
+
1867
+ }
1868
+
1869
+ // Clones attribute not to override
1870
+ const relativeAttribute = attribute.clone();
1871
+
1872
+ if ( ! geometry.morphTargetsRelative ) {
1873
+
1874
+ for ( let j = 0, jl = attribute.count; j < jl; j ++ ) {
1875
+
1876
+ for ( let a = 0; a < attribute.itemSize; a ++ ) {
1877
+
1878
+ if ( a === 0 ) relativeAttribute.setX( j, attribute.getX( j ) - baseAttribute.getX( j ) );
1879
+ if ( a === 1 ) relativeAttribute.setY( j, attribute.getY( j ) - baseAttribute.getY( j ) );
1880
+ if ( a === 2 ) relativeAttribute.setZ( j, attribute.getZ( j ) - baseAttribute.getZ( j ) );
1881
+ if ( a === 3 ) relativeAttribute.setW( j, attribute.getW( j ) - baseAttribute.getW( j ) );
1882
+
1883
+ }
1884
+
1885
+ }
1886
+
1887
+ }
1888
+
1889
+ target[ gltfAttributeName ] = this.processAccessor( relativeAttribute, geometry );
1890
+ cache.attributes.set( this.getUID( baseAttribute, true ), target[ gltfAttributeName ] );
1891
+
1892
+ }
1893
+
1894
+ targets.push( target );
1895
+
1896
+ weights.push( mesh.morphTargetInfluences[ i ] );
1897
+
1898
+ if ( mesh.morphTargetDictionary !== undefined ) targetNames.push( reverseDictionary[ i ] );
1899
+
1900
+ }
1901
+
1902
+ meshDef.weights = weights;
1903
+
1904
+ if ( targetNames.length > 0 ) {
1905
+
1906
+ meshDef.extras = {};
1907
+ meshDef.extras.targetNames = targetNames;
1908
+
1909
+ }
1910
+
1911
+ }
1912
+
1913
+ const isMultiMaterial = Array.isArray( mesh.material );
1914
+
1915
+ if ( isMultiMaterial && geometry.groups.length === 0 ) return null;
1916
+
1917
+ let didForceIndices = false;
1918
+
1919
+ if ( isMultiMaterial && geometry.index === null ) {
1920
+
1921
+ const indices = [];
1922
+
1923
+ for ( let i = 0, il = geometry.attributes.position.count; i < il; i ++ ) {
1924
+
1925
+ indices[ i ] = i;
1926
+
1927
+ }
1928
+
1929
+ geometry.setIndex( indices );
1930
+
1931
+ didForceIndices = true;
1932
+
1933
+ }
1934
+
1935
+ const materials = isMultiMaterial ? mesh.material : [ mesh.material ];
1936
+ const groups = isMultiMaterial ? geometry.groups : [ { materialIndex: 0, start: undefined, count: undefined } ];
1937
+
1938
+ for ( let i = 0, il = groups.length; i < il; i ++ ) {
1939
+
1940
+ const primitive = {
1941
+ mode: mode,
1942
+ attributes: attributes,
1943
+ };
1944
+
1945
+ this.serializeUserData( geometry, primitive );
1946
+
1947
+ if ( targets.length > 0 ) primitive.targets = targets;
1948
+
1949
+ if ( geometry.index !== null ) {
1950
+
1951
+ let cacheKey = this.getUID( geometry.index );
1952
+
1953
+ if ( groups[ i ].start !== undefined || groups[ i ].count !== undefined ) {
1954
+
1955
+ cacheKey += ':' + groups[ i ].start + ':' + groups[ i ].count;
1956
+
1957
+ }
1958
+
1959
+ if ( cache.attributes.has( cacheKey ) ) {
1960
+
1961
+ primitive.indices = cache.attributes.get( cacheKey );
1962
+
1963
+ } else {
1964
+
1965
+ primitive.indices = this.processAccessor( geometry.index, geometry, groups[ i ].start, groups[ i ].count );
1966
+ cache.attributes.set( cacheKey, primitive.indices );
1967
+
1968
+ }
1969
+
1970
+ if ( primitive.indices === null ) delete primitive.indices;
1971
+
1972
+ }
1973
+
1974
+ const material = await this.processMaterialAsync( materials[ groups[ i ].materialIndex ] );
1975
+
1976
+ if ( material !== null ) primitive.material = material;
1977
+
1978
+ primitives.push( primitive );
1979
+
1980
+ }
1981
+
1982
+ if ( didForceIndices === true ) {
1983
+
1984
+ geometry.setIndex( null );
1985
+
1986
+ }
1987
+
1988
+ meshDef.primitives = primitives;
1989
+
1990
+ if ( ! json.meshes ) json.meshes = [];
1991
+
1992
+ await this._invokeAllAsync( function ( ext ) {
1993
+
1994
+ ext.writeMesh && ext.writeMesh( mesh, meshDef );
1995
+
1996
+ } );
1997
+
1998
+ const index = json.meshes.push( meshDef ) - 1;
1999
+ cache.meshes.set( meshCacheKey, index );
2000
+ return index;
2001
+
2002
+ }
2003
+
2004
+ /**
2005
+ * If a vertex attribute with a
2006
+ * [non-standard data type](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview)
2007
+ * is used, it is checked whether it is a valid data type according to the
2008
+ * [KHR_mesh_quantization](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_mesh_quantization/README.md)
2009
+ * extension.
2010
+ * In this case the extension is automatically added to the list of used extensions.
2011
+ *
2012
+ * @param {string} attributeName
2013
+ * @param {THREE.BufferAttribute} attribute
2014
+ */
2015
+ detectMeshQuantization( attributeName, attribute ) {
2016
+
2017
+ if ( this.extensionsUsed[ KHR_MESH_QUANTIZATION ] ) return;
2018
+
2019
+ let attrType = undefined;
2020
+
2021
+ switch ( attribute.array.constructor ) {
2022
+
2023
+ case Int8Array:
2024
+
2025
+ attrType = 'byte';
2026
+
2027
+ break;
2028
+
2029
+ case Uint8Array:
2030
+
2031
+ attrType = 'unsigned byte';
2032
+
2033
+ break;
2034
+
2035
+ case Int16Array:
2036
+
2037
+ attrType = 'short';
2038
+
2039
+ break;
2040
+
2041
+ case Uint16Array:
2042
+
2043
+ attrType = 'unsigned short';
2044
+
2045
+ break;
2046
+
2047
+ default:
2048
+
2049
+ return;
2050
+
2051
+ }
2052
+
2053
+ if ( attribute.normalized ) attrType += ' normalized';
2054
+
2055
+ const attrNamePrefix = attributeName.split( '_', 1 )[ 0 ];
2056
+
2057
+ if ( KHR_mesh_quantization_ExtraAttrTypes[ attrNamePrefix ] && KHR_mesh_quantization_ExtraAttrTypes[ attrNamePrefix ].includes( attrType ) ) {
2058
+
2059
+ this.extensionsUsed[ KHR_MESH_QUANTIZATION ] = true;
2060
+ this.extensionsRequired[ KHR_MESH_QUANTIZATION ] = true;
2061
+
2062
+ }
2063
+
2064
+ }
2065
+
2066
+ /**
2067
+ * Process camera
2068
+ * @param {THREE.Camera} camera Camera to process
2069
+ * @return {Integer} Index of the processed mesh in the "camera" array
2070
+ */
2071
+ processCamera( camera ) {
2072
+
2073
+ const json = this.json;
2074
+
2075
+ if ( ! json.cameras ) json.cameras = [];
2076
+
2077
+ const isOrtho = camera.isOrthographicCamera;
2078
+
2079
+ const cameraDef = {
2080
+ type: isOrtho ? 'orthographic' : 'perspective'
2081
+ };
2082
+
2083
+ if ( isOrtho ) {
2084
+
2085
+ cameraDef.orthographic = {
2086
+ xmag: camera.right * 2,
2087
+ ymag: camera.top * 2,
2088
+ zfar: camera.far <= 0 ? 0.001 : camera.far,
2089
+ znear: camera.near < 0 ? 0 : camera.near
2090
+ };
2091
+
2092
+ } else {
2093
+
2094
+ cameraDef.perspective = {
2095
+ aspectRatio: camera.aspect,
2096
+ yfov: MathUtils.degToRad( camera.fov ),
2097
+ zfar: camera.far <= 0 ? 0.001 : camera.far,
2098
+ znear: camera.near < 0 ? 0 : camera.near
2099
+ };
2100
+
2101
+ }
2102
+
2103
+ // Question: Is saving "type" as name intentional?
2104
+ if ( camera.name !== '' ) cameraDef.name = camera.type;
2105
+
2106
+ return json.cameras.push( cameraDef ) - 1;
2107
+
2108
+ }
2109
+
2110
+ /**
2111
+ * Creates glTF animation entry from AnimationClip object.
2112
+ *
2113
+ * Status:
2114
+ * - Only properties listed in PATH_PROPERTIES may be animated.
2115
+ *
2116
+ * @param {THREE.AnimationClip} clip
2117
+ * @param {THREE.Object3D} root
2118
+ * @return {number|null}
2119
+ */
2120
+ processAnimation( clip, root ) {
2121
+
2122
+ const json = this.json;
2123
+ const nodeMap = this.nodeMap;
2124
+
2125
+ if ( ! json.animations ) json.animations = [];
2126
+
2127
+ clip = GLTFExporter.Utils.mergeMorphTargetTracks( clip.clone(), root );
2128
+
2129
+ const tracks = clip.tracks;
2130
+ const channels = [];
2131
+ const samplers = [];
2132
+
2133
+ for ( let i = 0; i < tracks.length; ++ i ) {
2134
+
2135
+ const track = tracks[ i ];
2136
+ const trackBinding = PropertyBinding.parseTrackName( track.name );
2137
+ let trackNode = PropertyBinding.findNode( root, trackBinding.nodeName );
2138
+ const trackProperty = PATH_PROPERTIES[ trackBinding.propertyName ];
2139
+
2140
+ if ( trackBinding.objectName === 'bones' ) {
2141
+
2142
+ if ( trackNode.isSkinnedMesh === true ) {
2143
+
2144
+ trackNode = trackNode.skeleton.getBoneByName( trackBinding.objectIndex );
2145
+
2146
+ } else {
2147
+
2148
+ trackNode = undefined;
2149
+
2150
+ }
2151
+
2152
+ }
2153
+
2154
+ if ( ! trackNode || ! trackProperty ) {
2155
+
2156
+ console.warn( 'THREE.GLTFExporter: Could not export animation track "%s".', track.name );
2157
+ continue;
2158
+
2159
+ }
2160
+
2161
+ const inputItemSize = 1;
2162
+ let outputItemSize = track.values.length / track.times.length;
2163
+
2164
+ if ( trackProperty === PATH_PROPERTIES.morphTargetInfluences ) {
2165
+
2166
+ outputItemSize /= trackNode.morphTargetInfluences.length;
2167
+
2168
+ }
2169
+
2170
+ let interpolation;
2171
+
2172
+ // @TODO export CubicInterpolant(InterpolateSmooth) as CUBICSPLINE
2173
+
2174
+ // Detecting glTF cubic spline interpolant by checking factory method's special property
2175
+ // GLTFCubicSplineInterpolant is a custom interpolant and track doesn't return
2176
+ // valid value from .getInterpolation().
2177
+ if ( track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline === true ) {
2178
+
2179
+ interpolation = 'CUBICSPLINE';
2180
+
2181
+ // itemSize of CUBICSPLINE keyframe is 9
2182
+ // (VEC3 * 3: inTangent, splineVertex, and outTangent)
2183
+ // but needs to be stored as VEC3 so dividing by 3 here.
2184
+ outputItemSize /= 3;
2185
+
2186
+ } else if ( track.getInterpolation() === InterpolateDiscrete ) {
2187
+
2188
+ interpolation = 'STEP';
2189
+
2190
+ } else {
2191
+
2192
+ interpolation = 'LINEAR';
2193
+
2194
+ }
2195
+
2196
+ samplers.push( {
2197
+ input: this.processAccessor( new BufferAttribute( track.times, inputItemSize ) ),
2198
+ output: this.processAccessor( new BufferAttribute( track.values, outputItemSize ) ),
2199
+ interpolation: interpolation
2200
+ } );
2201
+
2202
+ channels.push( {
2203
+ sampler: samplers.length - 1,
2204
+ target: {
2205
+ node: nodeMap.get( trackNode ),
2206
+ path: trackProperty
2207
+ }
2208
+ } );
2209
+
2210
+ }
2211
+
2212
+ json.animations.push( {
2213
+ name: clip.name || 'clip_' + json.animations.length,
2214
+ samplers: samplers,
2215
+ channels: channels
2216
+ } );
2217
+
2218
+ return json.animations.length - 1;
2219
+
2220
+ }
2221
+
2222
+ /**
2223
+ * @param {THREE.Object3D} object
2224
+ * @return {number|null}
2225
+ */
2226
+ processSkin( object ) {
2227
+
2228
+ const json = this.json;
2229
+ const nodeMap = this.nodeMap;
2230
+
2231
+ const node = json.nodes[ nodeMap.get( object ) ];
2232
+
2233
+ const skeleton = object.skeleton;
2234
+
2235
+ if ( skeleton === undefined ) return null;
2236
+
2237
+ const rootJoint = object.skeleton.bones[ 0 ];
2238
+
2239
+ if ( rootJoint === undefined ) return null;
2240
+
2241
+ const joints = [];
2242
+ const inverseBindMatrices = new Float32Array( skeleton.bones.length * 16 );
2243
+ const temporaryBoneInverse = new Matrix4();
2244
+
2245
+ for ( let i = 0; i < skeleton.bones.length; ++ i ) {
2246
+
2247
+ joints.push( nodeMap.get( skeleton.bones[ i ] ) );
2248
+ temporaryBoneInverse.copy( skeleton.boneInverses[ i ] );
2249
+ temporaryBoneInverse.multiply( object.bindMatrix ).toArray( inverseBindMatrices, i * 16 );
2250
+
2251
+ }
2252
+
2253
+ if ( json.skins === undefined ) json.skins = [];
2254
+
2255
+ json.skins.push( {
2256
+ inverseBindMatrices: this.processAccessor( new BufferAttribute( inverseBindMatrices, 16 ) ),
2257
+ joints: joints,
2258
+ skeleton: nodeMap.get( rootJoint )
2259
+ } );
2260
+
2261
+ const skinIndex = node.skin = json.skins.length - 1;
2262
+
2263
+ return skinIndex;
2264
+
2265
+ }
2266
+
2267
+ /**
2268
+ * Process Object3D node
2269
+ * @param {THREE.Object3D} object Object3D to processNodeAsync
2270
+ * @return {Integer} Index of the node in the nodes list
2271
+ */
2272
+ async processNodeAsync( object ) {
2273
+
2274
+ const json = this.json;
2275
+ const options = this.options;
2276
+ const nodeMap = this.nodeMap;
2277
+
2278
+ if ( ! json.nodes ) json.nodes = [];
2279
+
2280
+ const nodeDef = {};
2281
+
2282
+ if ( options.trs ) {
2283
+
2284
+ const rotation = object.quaternion.toArray();
2285
+ const position = object.position.toArray();
2286
+ const scale = object.scale.toArray();
2287
+
2288
+ if ( ! equalArray( rotation, [ 0, 0, 0, 1 ] ) ) {
2289
+
2290
+ nodeDef.rotation = rotation;
2291
+
2292
+ }
2293
+
2294
+ if ( ! equalArray( position, [ 0, 0, 0 ] ) ) {
2295
+
2296
+ nodeDef.translation = position;
2297
+
2298
+ }
2299
+
2300
+ if ( ! equalArray( scale, [ 1, 1, 1 ] ) ) {
2301
+
2302
+ nodeDef.scale = scale;
2303
+
2304
+ }
2305
+
2306
+ } else {
2307
+
2308
+ if ( object.matrixAutoUpdate ) {
2309
+
2310
+ object.updateMatrix();
2311
+
2312
+ }
2313
+
2314
+ if ( isIdentityMatrix( object.matrix ) === false ) {
2315
+
2316
+ nodeDef.matrix = object.matrix.elements;
2317
+
2318
+ }
2319
+
2320
+ }
2321
+
2322
+ // We don't export empty strings name because it represents no-name in Three.js.
2323
+ if ( object.name !== '' ) nodeDef.name = String( object.name );
2324
+
2325
+ this.serializeUserData( object, nodeDef );
2326
+
2327
+ if ( object.isMesh || object.isLine || object.isPoints ) {
2328
+
2329
+ const meshIndex = await this.processMeshAsync( object );
2330
+
2331
+ if ( meshIndex !== null ) nodeDef.mesh = meshIndex;
2332
+
2333
+ } else if ( object.isCamera ) {
2334
+
2335
+ nodeDef.camera = this.processCamera( object );
2336
+
2337
+ }
2338
+
2339
+ if ( object.isSkinnedMesh ) this.skins.push( object );
2340
+
2341
+ if ( object.children.length > 0 ) {
2342
+
2343
+ const children = [];
2344
+
2345
+ for ( let i = 0, l = object.children.length; i < l; i ++ ) {
2346
+
2347
+ const child = object.children[ i ];
2348
+
2349
+ if ( child.visible || options.onlyVisible === false ) {
2350
+
2351
+ const nodeIndex = await this.processNodeAsync( child );
2352
+
2353
+ if ( nodeIndex !== null ) children.push( nodeIndex );
2354
+
2355
+ }
2356
+
2357
+ }
2358
+
2359
+ if ( children.length > 0 ) nodeDef.children = children;
2360
+
2361
+ }
2362
+
2363
+ await this._invokeAllAsync( function ( ext ) {
2364
+
2365
+ ext.writeNode && ext.writeNode( object, nodeDef );
2366
+
2367
+ } );
2368
+
2369
+ const nodeIndex = json.nodes.push( nodeDef ) - 1;
2370
+ nodeMap.set( object, nodeIndex );
2371
+ return nodeIndex;
2372
+
2373
+ }
2374
+
2375
+ /**
2376
+ * Process Scene
2377
+ * @param {Scene} scene Scene to process
2378
+ */
2379
+ async processSceneAsync( scene ) {
2380
+
2381
+ const json = this.json;
2382
+ const options = this.options;
2383
+
2384
+ if ( ! json.scenes ) {
2385
+
2386
+ json.scenes = [];
2387
+ json.scene = 0;
2388
+
2389
+ }
2390
+
2391
+ const sceneDef = {};
2392
+
2393
+ if ( scene.name !== '' ) sceneDef.name = scene.name;
2394
+
2395
+ json.scenes.push( sceneDef );
2396
+
2397
+ const nodes = [];
2398
+
2399
+ for ( let i = 0, l = scene.children.length; i < l; i ++ ) {
2400
+
2401
+ const child = scene.children[ i ];
2402
+
2403
+ if ( child.visible || options.onlyVisible === false ) {
2404
+
2405
+ const nodeIndex = await this.processNodeAsync( child );
2406
+
2407
+ if ( nodeIndex !== null ) nodes.push( nodeIndex );
2408
+
2409
+ }
2410
+
2411
+ }
2412
+
2413
+ if ( nodes.length > 0 ) sceneDef.nodes = nodes;
2414
+
2415
+ this.serializeUserData( scene, sceneDef );
2416
+
2417
+ }
2418
+
2419
+ /**
2420
+ * Creates a Scene to hold a list of objects and parse it
2421
+ * @param {Array} objects List of objects to process
2422
+ */
2423
+ async processObjectsAsync( objects ) {
2424
+
2425
+ const scene = new Scene();
2426
+ scene.name = 'AuxScene';
2427
+
2428
+ for ( let i = 0; i < objects.length; i ++ ) {
2429
+
2430
+ // We push directly to children instead of calling `add` to prevent
2431
+ // modify the .parent and break its original scene and hierarchy
2432
+ scene.children.push( objects[ i ] );
2433
+
2434
+ }
2435
+
2436
+ await this.processSceneAsync( scene );
2437
+
2438
+ }
2439
+
2440
+ /**
2441
+ * @param {THREE.Object3D|Array<THREE.Object3D>} input
2442
+ */
2443
+ async processInputAsync( input ) {
2444
+
2445
+ const options = this.options;
2446
+
2447
+ input = input instanceof Array ? input : [ input ];
2448
+
2449
+ await this._invokeAllAsync( function ( ext ) {
2450
+
2451
+ ext.beforeParse && ext.beforeParse( input );
2452
+
2453
+ } );
2454
+
2455
+ const objectsWithoutScene = [];
2456
+
2457
+ for ( let i = 0; i < input.length; i ++ ) {
2458
+
2459
+ if ( input[ i ] instanceof Scene ) {
2460
+
2461
+ await this.processSceneAsync( input[ i ] );
2462
+
2463
+ } else {
2464
+
2465
+ objectsWithoutScene.push( input[ i ] );
2466
+
2467
+ }
2468
+
2469
+ }
2470
+
2471
+ if ( objectsWithoutScene.length > 0 ) {
2472
+
2473
+ await this.processObjectsAsync( objectsWithoutScene );
2474
+
2475
+ }
2476
+
2477
+ for ( let i = 0; i < this.skins.length; ++ i ) {
2478
+
2479
+ this.processSkin( this.skins[ i ] );
2480
+
2481
+ }
2482
+
2483
+ for ( let i = 0; i < options.animations.length; ++ i ) {
2484
+
2485
+ this.processAnimation( options.animations[ i ], input[ 0 ] );
2486
+
2487
+ }
2488
+
2489
+ await this._invokeAllAsync( function ( ext ) {
2490
+
2491
+ ext.afterParse && ext.afterParse( input );
2492
+
2493
+ } );
2494
+
2495
+ }
2496
+
2497
+ async _invokeAllAsync( func ) {
2498
+
2499
+ for ( let i = 0, il = this.plugins.length; i < il; i ++ ) {
2500
+
2501
+ await func( this.plugins[ i ] );
2502
+
2503
+ }
2504
+
2505
+ }
2506
+
2507
+ }
2508
+
2509
+ /**
2510
+ * Punctual Lights Extension
2511
+ *
2512
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
2513
+ */
2514
+ class GLTFLightExtension {
2515
+
2516
+ constructor( writer ) {
2517
+
2518
+ this.writer = writer;
2519
+ this.name = 'KHR_lights_punctual';
2520
+
2521
+ }
2522
+
2523
+ writeNode( light, nodeDef ) {
2524
+
2525
+ if ( ! light.isLight ) return;
2526
+
2527
+ if ( ! light.isDirectionalLight && ! light.isPointLight && ! light.isSpotLight ) {
2528
+
2529
+ console.warn( 'THREE.GLTFExporter: Only directional, point, and spot lights are supported.', light );
2530
+ return;
2531
+
2532
+ }
2533
+
2534
+ const writer = this.writer;
2535
+ const json = writer.json;
2536
+ const extensionsUsed = writer.extensionsUsed;
2537
+
2538
+ const lightDef = {};
2539
+
2540
+ if ( light.name ) lightDef.name = light.name;
2541
+
2542
+ lightDef.color = light.color.toArray();
2543
+
2544
+ lightDef.intensity = light.intensity;
2545
+
2546
+ if ( light.isDirectionalLight ) {
2547
+
2548
+ lightDef.type = 'directional';
2549
+
2550
+ } else if ( light.isPointLight ) {
2551
+
2552
+ lightDef.type = 'point';
2553
+
2554
+ if ( light.distance > 0 ) lightDef.range = light.distance;
2555
+
2556
+ } else if ( light.isSpotLight ) {
2557
+
2558
+ lightDef.type = 'spot';
2559
+
2560
+ if ( light.distance > 0 ) lightDef.range = light.distance;
2561
+
2562
+ lightDef.spot = {};
2563
+ lightDef.spot.innerConeAngle = ( 1.0 - light.penumbra ) * light.angle;
2564
+ lightDef.spot.outerConeAngle = light.angle;
2565
+
2566
+ }
2567
+
2568
+ if ( light.decay !== undefined && light.decay !== 2 ) {
2569
+
2570
+ console.warn( 'THREE.GLTFExporter: Light decay may be lost. glTF is physically-based, '
2571
+ + 'and expects light.decay=2.' );
2572
+
2573
+ }
2574
+
2575
+ if ( light.target
2576
+ && ( light.target.parent !== light
2577
+ || light.target.position.x !== 0
2578
+ || light.target.position.y !== 0
2579
+ || light.target.position.z !== - 1 ) ) {
2580
+
2581
+ console.warn( 'THREE.GLTFExporter: Light direction may be lost. For best results, '
2582
+ + 'make light.target a child of the light with position 0,0,-1.' );
2583
+
2584
+ }
2585
+
2586
+ if ( ! extensionsUsed[ this.name ] ) {
2587
+
2588
+ json.extensions = json.extensions || {};
2589
+ json.extensions[ this.name ] = { lights: [] };
2590
+ extensionsUsed[ this.name ] = true;
2591
+
2592
+ }
2593
+
2594
+ const lights = json.extensions[ this.name ].lights;
2595
+ lights.push( lightDef );
2596
+
2597
+ nodeDef.extensions = nodeDef.extensions || {};
2598
+ nodeDef.extensions[ this.name ] = { light: lights.length - 1 };
2599
+
2600
+ }
2601
+
2602
+ }
2603
+
2604
+ /**
2605
+ * Unlit Materials Extension
2606
+ *
2607
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
2608
+ */
2609
+ class GLTFMaterialsUnlitExtension {
2610
+
2611
+ constructor( writer ) {
2612
+
2613
+ this.writer = writer;
2614
+ this.name = 'KHR_materials_unlit';
2615
+
2616
+ }
2617
+
2618
+ async writeMaterialAsync( material, materialDef ) {
2619
+
2620
+ if ( ! material.isMeshBasicMaterial ) return;
2621
+
2622
+ const writer = this.writer;
2623
+ const extensionsUsed = writer.extensionsUsed;
2624
+
2625
+ materialDef.extensions = materialDef.extensions || {};
2626
+ materialDef.extensions[ this.name ] = {};
2627
+
2628
+ extensionsUsed[ this.name ] = true;
2629
+
2630
+ materialDef.pbrMetallicRoughness.metallicFactor = 0.0;
2631
+ materialDef.pbrMetallicRoughness.roughnessFactor = 0.9;
2632
+
2633
+ }
2634
+
2635
+ }
2636
+
2637
+ /**
2638
+ * Clearcoat Materials Extension
2639
+ *
2640
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat
2641
+ */
2642
+ class GLTFMaterialsClearcoatExtension {
2643
+
2644
+ constructor( writer ) {
2645
+
2646
+ this.writer = writer;
2647
+ this.name = 'KHR_materials_clearcoat';
2648
+
2649
+ }
2650
+
2651
+ async writeMaterialAsync( material, materialDef ) {
2652
+
2653
+ if ( ! material.isMeshPhysicalMaterial || material.clearcoat === 0 ) return;
2654
+
2655
+ const writer = this.writer;
2656
+ const extensionsUsed = writer.extensionsUsed;
2657
+
2658
+ const extensionDef = {};
2659
+
2660
+ extensionDef.clearcoatFactor = material.clearcoat;
2661
+
2662
+ if ( material.clearcoatMap ) {
2663
+
2664
+ const clearcoatMapDef = {
2665
+ index: await writer.processTextureAsync( material.clearcoatMap ),
2666
+ texCoord: material.clearcoatMap.channel
2667
+ };
2668
+ writer.applyTextureTransform( clearcoatMapDef, material.clearcoatMap );
2669
+ extensionDef.clearcoatTexture = clearcoatMapDef;
2670
+
2671
+ }
2672
+
2673
+ extensionDef.clearcoatRoughnessFactor = material.clearcoatRoughness;
2674
+
2675
+ if ( material.clearcoatRoughnessMap ) {
2676
+
2677
+ const clearcoatRoughnessMapDef = {
2678
+ index: await writer.processTextureAsync( material.clearcoatRoughnessMap ),
2679
+ texCoord: material.clearcoatRoughnessMap.channel
2680
+ };
2681
+ writer.applyTextureTransform( clearcoatRoughnessMapDef, material.clearcoatRoughnessMap );
2682
+ extensionDef.clearcoatRoughnessTexture = clearcoatRoughnessMapDef;
2683
+
2684
+ }
2685
+
2686
+ if ( material.clearcoatNormalMap ) {
2687
+
2688
+ const clearcoatNormalMapDef = {
2689
+ index: await writer.processTextureAsync( material.clearcoatNormalMap ),
2690
+ texCoord: material.clearcoatNormalMap.channel
2691
+ };
2692
+
2693
+ if ( material.clearcoatNormalScale.x !== 1 ) clearcoatNormalMapDef.scale = material.clearcoatNormalScale.x;
2694
+
2695
+ writer.applyTextureTransform( clearcoatNormalMapDef, material.clearcoatNormalMap );
2696
+ extensionDef.clearcoatNormalTexture = clearcoatNormalMapDef;
2697
+
2698
+ }
2699
+
2700
+ materialDef.extensions = materialDef.extensions || {};
2701
+ materialDef.extensions[ this.name ] = extensionDef;
2702
+
2703
+ extensionsUsed[ this.name ] = true;
2704
+
2705
+
2706
+ }
2707
+
2708
+ }
2709
+
2710
+ /**
2711
+ * Materials dispersion Extension
2712
+ *
2713
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_dispersion
2714
+ */
2715
+ class GLTFMaterialsDispersionExtension {
2716
+
2717
+ constructor( writer ) {
2718
+
2719
+ this.writer = writer;
2720
+ this.name = 'KHR_materials_dispersion';
2721
+
2722
+ }
2723
+
2724
+ async writeMaterialAsync( material, materialDef ) {
2725
+
2726
+ if ( ! material.isMeshPhysicalMaterial || material.dispersion === 0 ) return;
2727
+
2728
+ const writer = this.writer;
2729
+ const extensionsUsed = writer.extensionsUsed;
2730
+
2731
+ const extensionDef = {};
2732
+
2733
+ extensionDef.dispersion = material.dispersion;
2734
+
2735
+ materialDef.extensions = materialDef.extensions || {};
2736
+ materialDef.extensions[ this.name ] = extensionDef;
2737
+
2738
+ extensionsUsed[ this.name ] = true;
2739
+
2740
+ }
2741
+
2742
+ }
2743
+
2744
+ /**
2745
+ * Iridescence Materials Extension
2746
+ *
2747
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_iridescence
2748
+ */
2749
+ class GLTFMaterialsIridescenceExtension {
2750
+
2751
+ constructor( writer ) {
2752
+
2753
+ this.writer = writer;
2754
+ this.name = 'KHR_materials_iridescence';
2755
+
2756
+ }
2757
+
2758
+ async writeMaterialAsync( material, materialDef ) {
2759
+
2760
+ if ( ! material.isMeshPhysicalMaterial || material.iridescence === 0 ) return;
2761
+
2762
+ const writer = this.writer;
2763
+ const extensionsUsed = writer.extensionsUsed;
2764
+
2765
+ const extensionDef = {};
2766
+
2767
+ extensionDef.iridescenceFactor = material.iridescence;
2768
+
2769
+ if ( material.iridescenceMap ) {
2770
+
2771
+ const iridescenceMapDef = {
2772
+ index: await writer.processTextureAsync( material.iridescenceMap ),
2773
+ texCoord: material.iridescenceMap.channel
2774
+ };
2775
+ writer.applyTextureTransform( iridescenceMapDef, material.iridescenceMap );
2776
+ extensionDef.iridescenceTexture = iridescenceMapDef;
2777
+
2778
+ }
2779
+
2780
+ extensionDef.iridescenceIor = material.iridescenceIOR;
2781
+ extensionDef.iridescenceThicknessMinimum = material.iridescenceThicknessRange[ 0 ];
2782
+ extensionDef.iridescenceThicknessMaximum = material.iridescenceThicknessRange[ 1 ];
2783
+
2784
+ if ( material.iridescenceThicknessMap ) {
2785
+
2786
+ const iridescenceThicknessMapDef = {
2787
+ index: await writer.processTextureAsync( material.iridescenceThicknessMap ),
2788
+ texCoord: material.iridescenceThicknessMap.channel
2789
+ };
2790
+ writer.applyTextureTransform( iridescenceThicknessMapDef, material.iridescenceThicknessMap );
2791
+ extensionDef.iridescenceThicknessTexture = iridescenceThicknessMapDef;
2792
+
2793
+ }
2794
+
2795
+ materialDef.extensions = materialDef.extensions || {};
2796
+ materialDef.extensions[ this.name ] = extensionDef;
2797
+
2798
+ extensionsUsed[ this.name ] = true;
2799
+
2800
+ }
2801
+
2802
+ }
2803
+
2804
+ /**
2805
+ * Transmission Materials Extension
2806
+ *
2807
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission
2808
+ */
2809
+ class GLTFMaterialsTransmissionExtension {
2810
+
2811
+ constructor( writer ) {
2812
+
2813
+ this.writer = writer;
2814
+ this.name = 'KHR_materials_transmission';
2815
+
2816
+ }
2817
+
2818
+ async writeMaterialAsync( material, materialDef ) {
2819
+
2820
+ if ( ! material.isMeshPhysicalMaterial || material.transmission === 0 ) return;
2821
+
2822
+ const writer = this.writer;
2823
+ const extensionsUsed = writer.extensionsUsed;
2824
+
2825
+ const extensionDef = {};
2826
+
2827
+ extensionDef.transmissionFactor = material.transmission;
2828
+
2829
+ if ( material.transmissionMap ) {
2830
+
2831
+ const transmissionMapDef = {
2832
+ index: await writer.processTextureAsync( material.transmissionMap ),
2833
+ texCoord: material.transmissionMap.channel
2834
+ };
2835
+ writer.applyTextureTransform( transmissionMapDef, material.transmissionMap );
2836
+ extensionDef.transmissionTexture = transmissionMapDef;
2837
+
2838
+ }
2839
+
2840
+ materialDef.extensions = materialDef.extensions || {};
2841
+ materialDef.extensions[ this.name ] = extensionDef;
2842
+
2843
+ extensionsUsed[ this.name ] = true;
2844
+
2845
+ }
2846
+
2847
+ }
2848
+
2849
+ /**
2850
+ * Materials Volume Extension
2851
+ *
2852
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume
2853
+ */
2854
+ class GLTFMaterialsVolumeExtension {
2855
+
2856
+ constructor( writer ) {
2857
+
2858
+ this.writer = writer;
2859
+ this.name = 'KHR_materials_volume';
2860
+
2861
+ }
2862
+
2863
+ async writeMaterialAsync( material, materialDef ) {
2864
+
2865
+ if ( ! material.isMeshPhysicalMaterial || material.transmission === 0 ) return;
2866
+
2867
+ const writer = this.writer;
2868
+ const extensionsUsed = writer.extensionsUsed;
2869
+
2870
+ const extensionDef = {};
2871
+
2872
+ extensionDef.thicknessFactor = material.thickness;
2873
+
2874
+ if ( material.thicknessMap ) {
2875
+
2876
+ const thicknessMapDef = {
2877
+ index: await writer.processTextureAsync( material.thicknessMap ),
2878
+ texCoord: material.thicknessMap.channel
2879
+ };
2880
+ writer.applyTextureTransform( thicknessMapDef, material.thicknessMap );
2881
+ extensionDef.thicknessTexture = thicknessMapDef;
2882
+
2883
+ }
2884
+
2885
+ if ( material.attenuationDistance !== Infinity ) {
2886
+
2887
+ extensionDef.attenuationDistance = material.attenuationDistance;
2888
+
2889
+ }
2890
+
2891
+ extensionDef.attenuationColor = material.attenuationColor.toArray();
2892
+
2893
+ materialDef.extensions = materialDef.extensions || {};
2894
+ materialDef.extensions[ this.name ] = extensionDef;
2895
+
2896
+ extensionsUsed[ this.name ] = true;
2897
+
2898
+ }
2899
+
2900
+ }
2901
+
2902
+ /**
2903
+ * Materials ior Extension
2904
+ *
2905
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior
2906
+ */
2907
+ class GLTFMaterialsIorExtension {
2908
+
2909
+ constructor( writer ) {
2910
+
2911
+ this.writer = writer;
2912
+ this.name = 'KHR_materials_ior';
2913
+
2914
+ }
2915
+
2916
+ async writeMaterialAsync( material, materialDef ) {
2917
+
2918
+ if ( ! material.isMeshPhysicalMaterial || material.ior === 1.5 ) return;
2919
+
2920
+ const writer = this.writer;
2921
+ const extensionsUsed = writer.extensionsUsed;
2922
+
2923
+ const extensionDef = {};
2924
+
2925
+ extensionDef.ior = material.ior;
2926
+
2927
+ materialDef.extensions = materialDef.extensions || {};
2928
+ materialDef.extensions[ this.name ] = extensionDef;
2929
+
2930
+ extensionsUsed[ this.name ] = true;
2931
+
2932
+ }
2933
+
2934
+ }
2935
+
2936
+ /**
2937
+ * Materials specular Extension
2938
+ *
2939
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular
2940
+ */
2941
+ class GLTFMaterialsSpecularExtension {
2942
+
2943
+ constructor( writer ) {
2944
+
2945
+ this.writer = writer;
2946
+ this.name = 'KHR_materials_specular';
2947
+
2948
+ }
2949
+
2950
+ async writeMaterialAsync( material, materialDef ) {
2951
+
2952
+ if ( ! material.isMeshPhysicalMaterial || ( material.specularIntensity === 1.0 &&
2953
+ material.specularColor.equals( DEFAULT_SPECULAR_COLOR ) &&
2954
+ ! material.specularIntensityMap && ! material.specularColorMap ) ) return;
2955
+
2956
+ const writer = this.writer;
2957
+ const extensionsUsed = writer.extensionsUsed;
2958
+
2959
+ const extensionDef = {};
2960
+
2961
+ if ( material.specularIntensityMap ) {
2962
+
2963
+ const specularIntensityMapDef = {
2964
+ index: await writer.processTextureAsync( material.specularIntensityMap ),
2965
+ texCoord: material.specularIntensityMap.channel
2966
+ };
2967
+ writer.applyTextureTransform( specularIntensityMapDef, material.specularIntensityMap );
2968
+ extensionDef.specularTexture = specularIntensityMapDef;
2969
+
2970
+ }
2971
+
2972
+ if ( material.specularColorMap ) {
2973
+
2974
+ const specularColorMapDef = {
2975
+ index: await writer.processTextureAsync( material.specularColorMap ),
2976
+ texCoord: material.specularColorMap.channel
2977
+ };
2978
+ writer.applyTextureTransform( specularColorMapDef, material.specularColorMap );
2979
+ extensionDef.specularColorTexture = specularColorMapDef;
2980
+
2981
+ }
2982
+
2983
+ extensionDef.specularFactor = material.specularIntensity;
2984
+ extensionDef.specularColorFactor = material.specularColor.toArray();
2985
+
2986
+ materialDef.extensions = materialDef.extensions || {};
2987
+ materialDef.extensions[ this.name ] = extensionDef;
2988
+
2989
+ extensionsUsed[ this.name ] = true;
2990
+
2991
+ }
2992
+
2993
+ }
2994
+
2995
+ /**
2996
+ * Sheen Materials Extension
2997
+ *
2998
+ * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen
2999
+ */
3000
+ class GLTFMaterialsSheenExtension {
3001
+
3002
+ constructor( writer ) {
3003
+
3004
+ this.writer = writer;
3005
+ this.name = 'KHR_materials_sheen';
3006
+
3007
+ }
3008
+
3009
+ async writeMaterialAsync( material, materialDef ) {
3010
+
3011
+ if ( ! material.isMeshPhysicalMaterial || material.sheen == 0.0 ) return;
3012
+
3013
+ const writer = this.writer;
3014
+ const extensionsUsed = writer.extensionsUsed;
3015
+
3016
+ const extensionDef = {};
3017
+
3018
+ if ( material.sheenRoughnessMap ) {
3019
+
3020
+ const sheenRoughnessMapDef = {
3021
+ index: await writer.processTextureAsync( material.sheenRoughnessMap ),
3022
+ texCoord: material.sheenRoughnessMap.channel
3023
+ };
3024
+ writer.applyTextureTransform( sheenRoughnessMapDef, material.sheenRoughnessMap );
3025
+ extensionDef.sheenRoughnessTexture = sheenRoughnessMapDef;
3026
+
3027
+ }
3028
+
3029
+ if ( material.sheenColorMap ) {
3030
+
3031
+ const sheenColorMapDef = {
3032
+ index: await writer.processTextureAsync( material.sheenColorMap ),
3033
+ texCoord: material.sheenColorMap.channel
3034
+ };
3035
+ writer.applyTextureTransform( sheenColorMapDef, material.sheenColorMap );
3036
+ extensionDef.sheenColorTexture = sheenColorMapDef;
3037
+
3038
+ }
3039
+
3040
+ extensionDef.sheenRoughnessFactor = material.sheenRoughness;
3041
+ extensionDef.sheenColorFactor = material.sheenColor.toArray();
3042
+
3043
+ materialDef.extensions = materialDef.extensions || {};
3044
+ materialDef.extensions[ this.name ] = extensionDef;
3045
+
3046
+ extensionsUsed[ this.name ] = true;
3047
+
3048
+ }
3049
+
3050
+ }
3051
+
3052
+ /**
3053
+ * Anisotropy Materials Extension
3054
+ *
3055
+ * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_anisotropy
3056
+ */
3057
+ class GLTFMaterialsAnisotropyExtension {
3058
+
3059
+ constructor( writer ) {
3060
+
3061
+ this.writer = writer;
3062
+ this.name = 'KHR_materials_anisotropy';
3063
+
3064
+ }
3065
+
3066
+ async writeMaterialAsync( material, materialDef ) {
3067
+
3068
+ if ( ! material.isMeshPhysicalMaterial || material.anisotropy == 0.0 ) return;
3069
+
3070
+ const writer = this.writer;
3071
+ const extensionsUsed = writer.extensionsUsed;
3072
+
3073
+ const extensionDef = {};
3074
+
3075
+ if ( material.anisotropyMap ) {
3076
+
3077
+ const anisotropyMapDef = { index: await writer.processTextureAsync( material.anisotropyMap ) };
3078
+ writer.applyTextureTransform( anisotropyMapDef, material.anisotropyMap );
3079
+ extensionDef.anisotropyTexture = anisotropyMapDef;
3080
+
3081
+ }
3082
+
3083
+ extensionDef.anisotropyStrength = material.anisotropy;
3084
+ extensionDef.anisotropyRotation = material.anisotropyRotation;
3085
+
3086
+ materialDef.extensions = materialDef.extensions || {};
3087
+ materialDef.extensions[ this.name ] = extensionDef;
3088
+
3089
+ extensionsUsed[ this.name ] = true;
3090
+
3091
+ }
3092
+
3093
+ }
3094
+
3095
+ /**
3096
+ * Materials Emissive Strength Extension
3097
+ *
3098
+ * Specification: https://github.com/KhronosGroup/glTF/blob/5768b3ce0ef32bc39cdf1bef10b948586635ead3/extensions/2.0/Khronos/KHR_materials_emissive_strength/README.md
3099
+ */
3100
+ class GLTFMaterialsEmissiveStrengthExtension {
3101
+
3102
+ constructor( writer ) {
3103
+
3104
+ this.writer = writer;
3105
+ this.name = 'KHR_materials_emissive_strength';
3106
+
3107
+ }
3108
+
3109
+ async writeMaterialAsync( material, materialDef ) {
3110
+
3111
+ if ( ! material.isMeshStandardMaterial || material.emissiveIntensity === 1.0 ) return;
3112
+
3113
+ const writer = this.writer;
3114
+ const extensionsUsed = writer.extensionsUsed;
3115
+
3116
+ const extensionDef = {};
3117
+
3118
+ extensionDef.emissiveStrength = material.emissiveIntensity;
3119
+
3120
+ materialDef.extensions = materialDef.extensions || {};
3121
+ materialDef.extensions[ this.name ] = extensionDef;
3122
+
3123
+ extensionsUsed[ this.name ] = true;
3124
+
3125
+ }
3126
+
3127
+ }
3128
+
3129
+
3130
+ /**
3131
+ * Materials bump Extension
3132
+ *
3133
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/EXT_materials_bump
3134
+ */
3135
+ class GLTFMaterialsBumpExtension {
3136
+
3137
+ constructor( writer ) {
3138
+
3139
+ this.writer = writer;
3140
+ this.name = 'EXT_materials_bump';
3141
+
3142
+ }
3143
+
3144
+ async writeMaterialAsync( material, materialDef ) {
3145
+
3146
+ if ( ! material.isMeshStandardMaterial || (
3147
+ material.bumpScale === 1 &&
3148
+ ! material.bumpMap ) ) return;
3149
+
3150
+ const writer = this.writer;
3151
+ const extensionsUsed = writer.extensionsUsed;
3152
+
3153
+ const extensionDef = {};
3154
+
3155
+ if ( material.bumpMap ) {
3156
+
3157
+ const bumpMapDef = {
3158
+ index: await writer.processTextureAsync( material.bumpMap ),
3159
+ texCoord: material.bumpMap.channel
3160
+ };
3161
+ writer.applyTextureTransform( bumpMapDef, material.bumpMap );
3162
+ extensionDef.bumpTexture = bumpMapDef;
3163
+
3164
+ }
3165
+
3166
+ extensionDef.bumpFactor = material.bumpScale;
3167
+
3168
+ materialDef.extensions = materialDef.extensions || {};
3169
+ materialDef.extensions[ this.name ] = extensionDef;
3170
+
3171
+ extensionsUsed[ this.name ] = true;
3172
+
3173
+ }
3174
+
3175
+ }
3176
+
3177
+ /**
3178
+ * GPU Instancing Extension
3179
+ *
3180
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_mesh_gpu_instancing
3181
+ */
3182
+ class GLTFMeshGpuInstancing {
3183
+
3184
+ constructor( writer ) {
3185
+
3186
+ this.writer = writer;
3187
+ this.name = 'EXT_mesh_gpu_instancing';
3188
+
3189
+ }
3190
+
3191
+ writeNode( object, nodeDef ) {
3192
+
3193
+ if ( ! object.isInstancedMesh ) return;
3194
+
3195
+ const writer = this.writer;
3196
+
3197
+ const mesh = object;
3198
+
3199
+ const translationAttr = new Float32Array( mesh.count * 3 );
3200
+ const rotationAttr = new Float32Array( mesh.count * 4 );
3201
+ const scaleAttr = new Float32Array( mesh.count * 3 );
3202
+
3203
+ const matrix = new Matrix4();
3204
+ const position = new Vector3();
3205
+ const quaternion = new Quaternion();
3206
+ const scale = new Vector3();
3207
+
3208
+ for ( let i = 0; i < mesh.count; i ++ ) {
3209
+
3210
+ mesh.getMatrixAt( i, matrix );
3211
+ matrix.decompose( position, quaternion, scale );
3212
+
3213
+ position.toArray( translationAttr, i * 3 );
3214
+ quaternion.toArray( rotationAttr, i * 4 );
3215
+ scale.toArray( scaleAttr, i * 3 );
3216
+
3217
+ }
3218
+
3219
+ const attributes = {
3220
+ TRANSLATION: writer.processAccessor( new BufferAttribute( translationAttr, 3 ) ),
3221
+ ROTATION: writer.processAccessor( new BufferAttribute( rotationAttr, 4 ) ),
3222
+ SCALE: writer.processAccessor( new BufferAttribute( scaleAttr, 3 ) ),
3223
+ };
3224
+
3225
+ if ( mesh.instanceColor )
3226
+ attributes._COLOR_0 = writer.processAccessor( mesh.instanceColor );
3227
+
3228
+ nodeDef.extensions = nodeDef.extensions || {};
3229
+ nodeDef.extensions[ this.name ] = { attributes };
3230
+
3231
+ writer.extensionsUsed[ this.name ] = true;
3232
+ writer.extensionsRequired[ this.name ] = true;
3233
+
3234
+ }
3235
+
3236
+ }
3237
+
3238
+ /**
3239
+ * Static utility functions
3240
+ */
3241
+ GLTFExporter.Utils = {
3242
+
3243
+ insertKeyframe: function ( track, time ) {
3244
+
3245
+ const tolerance = 0.001; // 1ms
3246
+ const valueSize = track.getValueSize();
3247
+
3248
+ const times = new track.TimeBufferType( track.times.length + 1 );
3249
+ const values = new track.ValueBufferType( track.values.length + valueSize );
3250
+ const interpolant = track.createInterpolant( new track.ValueBufferType( valueSize ) );
3251
+
3252
+ let index;
3253
+
3254
+ if ( track.times.length === 0 ) {
3255
+
3256
+ times[ 0 ] = time;
3257
+
3258
+ for ( let i = 0; i < valueSize; i ++ ) {
3259
+
3260
+ values[ i ] = 0;
3261
+
3262
+ }
3263
+
3264
+ index = 0;
3265
+
3266
+ } else if ( time < track.times[ 0 ] ) {
3267
+
3268
+ if ( Math.abs( track.times[ 0 ] - time ) < tolerance ) return 0;
3269
+
3270
+ times[ 0 ] = time;
3271
+ times.set( track.times, 1 );
3272
+
3273
+ values.set( interpolant.evaluate( time ), 0 );
3274
+ values.set( track.values, valueSize );
3275
+
3276
+ index = 0;
3277
+
3278
+ } else if ( time > track.times[ track.times.length - 1 ] ) {
3279
+
3280
+ if ( Math.abs( track.times[ track.times.length - 1 ] - time ) < tolerance ) {
3281
+
3282
+ return track.times.length - 1;
3283
+
3284
+ }
3285
+
3286
+ times[ times.length - 1 ] = time;
3287
+ times.set( track.times, 0 );
3288
+
3289
+ values.set( track.values, 0 );
3290
+ values.set( interpolant.evaluate( time ), track.values.length );
3291
+
3292
+ index = times.length - 1;
3293
+
3294
+ } else {
3295
+
3296
+ for ( let i = 0; i < track.times.length; i ++ ) {
3297
+
3298
+ if ( Math.abs( track.times[ i ] - time ) < tolerance ) return i;
3299
+
3300
+ if ( track.times[ i ] < time && track.times[ i + 1 ] > time ) {
3301
+
3302
+ times.set( track.times.slice( 0, i + 1 ), 0 );
3303
+ times[ i + 1 ] = time;
3304
+ times.set( track.times.slice( i + 1 ), i + 2 );
3305
+
3306
+ values.set( track.values.slice( 0, ( i + 1 ) * valueSize ), 0 );
3307
+ values.set( interpolant.evaluate( time ), ( i + 1 ) * valueSize );
3308
+ values.set( track.values.slice( ( i + 1 ) * valueSize ), ( i + 2 ) * valueSize );
3309
+
3310
+ index = i + 1;
3311
+
3312
+ break;
3313
+
3314
+ }
3315
+
3316
+ }
3317
+
3318
+ }
3319
+
3320
+ track.times = times;
3321
+ track.values = values;
3322
+
3323
+ return index;
3324
+
3325
+ },
3326
+
3327
+ mergeMorphTargetTracks: function ( clip, root ) {
3328
+
3329
+ const tracks = [];
3330
+ const mergedTracks = {};
3331
+ const sourceTracks = clip.tracks;
3332
+
3333
+ for ( let i = 0; i < sourceTracks.length; ++ i ) {
3334
+
3335
+ let sourceTrack = sourceTracks[ i ];
3336
+ const sourceTrackBinding = PropertyBinding.parseTrackName( sourceTrack.name );
3337
+ const sourceTrackNode = PropertyBinding.findNode( root, sourceTrackBinding.nodeName );
3338
+
3339
+ if ( sourceTrackBinding.propertyName !== 'morphTargetInfluences' || sourceTrackBinding.propertyIndex === undefined ) {
3340
+
3341
+ // Tracks that don't affect morph targets, or that affect all morph targets together, can be left as-is.
3342
+ tracks.push( sourceTrack );
3343
+ continue;
3344
+
3345
+ }
3346
+
3347
+ if ( sourceTrack.createInterpolant !== sourceTrack.InterpolantFactoryMethodDiscrete
3348
+ && sourceTrack.createInterpolant !== sourceTrack.InterpolantFactoryMethodLinear ) {
3349
+
3350
+ if ( sourceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) {
3351
+
3352
+ // This should never happen, because glTF morph target animations
3353
+ // affect all targets already.
3354
+ throw new Error( 'THREE.GLTFExporter: Cannot merge tracks with glTF CUBICSPLINE interpolation.' );
3355
+
3356
+ }
3357
+
3358
+ console.warn( 'THREE.GLTFExporter: Morph target interpolation mode not yet supported. Using LINEAR instead.' );
3359
+
3360
+ sourceTrack = sourceTrack.clone();
3361
+ sourceTrack.setInterpolation( InterpolateLinear );
3362
+
3363
+ }
3364
+
3365
+ const targetCount = sourceTrackNode.morphTargetInfluences.length;
3366
+ const targetIndex = sourceTrackNode.morphTargetDictionary[ sourceTrackBinding.propertyIndex ];
3367
+
3368
+ if ( targetIndex === undefined ) {
3369
+
3370
+ throw new Error( 'THREE.GLTFExporter: Morph target name not found: ' + sourceTrackBinding.propertyIndex );
3371
+
3372
+ }
3373
+
3374
+ let mergedTrack;
3375
+
3376
+ // If this is the first time we've seen this object, create a new
3377
+ // track to store merged keyframe data for each morph target.
3378
+ if ( mergedTracks[ sourceTrackNode.uuid ] === undefined ) {
3379
+
3380
+ mergedTrack = sourceTrack.clone();
3381
+
3382
+ const values = new mergedTrack.ValueBufferType( targetCount * mergedTrack.times.length );
3383
+
3384
+ for ( let j = 0; j < mergedTrack.times.length; j ++ ) {
3385
+
3386
+ values[ j * targetCount + targetIndex ] = mergedTrack.values[ j ];
3387
+
3388
+ }
3389
+
3390
+ // We need to take into consideration the intended target node
3391
+ // of our original un-merged morphTarget animation.
3392
+ mergedTrack.name = ( sourceTrackBinding.nodeName || '' ) + '.morphTargetInfluences';
3393
+ mergedTrack.values = values;
3394
+
3395
+ mergedTracks[ sourceTrackNode.uuid ] = mergedTrack;
3396
+ tracks.push( mergedTrack );
3397
+
3398
+ continue;
3399
+
3400
+ }
3401
+
3402
+ const sourceInterpolant = sourceTrack.createInterpolant( new sourceTrack.ValueBufferType( 1 ) );
3403
+
3404
+ mergedTrack = mergedTracks[ sourceTrackNode.uuid ];
3405
+
3406
+ // For every existing keyframe of the merged track, write a (possibly
3407
+ // interpolated) value from the source track.
3408
+ for ( let j = 0; j < mergedTrack.times.length; j ++ ) {
3409
+
3410
+ mergedTrack.values[ j * targetCount + targetIndex ] = sourceInterpolant.evaluate( mergedTrack.times[ j ] );
3411
+
3412
+ }
3413
+
3414
+ // For every existing keyframe of the source track, write a (possibly
3415
+ // new) keyframe to the merged track. Values from the previous loop may
3416
+ // be written again, but keyframes are de-duplicated.
3417
+ for ( let j = 0; j < sourceTrack.times.length; j ++ ) {
3418
+
3419
+ const keyframeIndex = this.insertKeyframe( mergedTrack, sourceTrack.times[ j ] );
3420
+ mergedTrack.values[ keyframeIndex * targetCount + targetIndex ] = sourceTrack.values[ j ];
3421
+
3422
+ }
3423
+
3424
+ }
3425
+
3426
+ clip.tracks = tracks;
3427
+
3428
+ return clip;
3429
+
3430
+ },
3431
+
3432
+ toFloat32BufferAttribute: function ( srcAttribute ) {
3433
+
3434
+ const dstAttribute = new BufferAttribute( new Float32Array( srcAttribute.count * srcAttribute.itemSize ), srcAttribute.itemSize, false );
3435
+
3436
+ if ( ! srcAttribute.normalized && ! srcAttribute.isInterleavedBufferAttribute ) {
3437
+
3438
+ dstAttribute.array.set( srcAttribute.array );
3439
+
3440
+ return dstAttribute;
3441
+
3442
+ }
3443
+
3444
+ for ( let i = 0, il = srcAttribute.count; i < il; i ++ ) {
3445
+
3446
+ for ( let j = 0; j < srcAttribute.itemSize; j ++ ) {
3447
+
3448
+ dstAttribute.setComponent( i, j, srcAttribute.getComponent( i, j ) );
3449
+
3450
+ }
3451
+
3452
+ }
3453
+
3454
+ return dstAttribute;
3455
+
3456
+ }
3457
+
3458
+ };
3459
+
3460
+ export { GLTFExporter };
libs/three.js/0.172.0/jsm/exporters/KTX2Exporter.js ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ ColorManagement,
3
+ FloatType,
4
+ HalfFloatType,
5
+ UnsignedByteType,
6
+ RGBAFormat,
7
+ RGFormat,
8
+ RGIntegerFormat,
9
+ RedFormat,
10
+ RedIntegerFormat,
11
+ NoColorSpace,
12
+ LinearSRGBColorSpace,
13
+ SRGBColorSpace,
14
+ SRGBTransfer,
15
+ DataTexture,
16
+ REVISION,
17
+ } from 'three';
18
+
19
+ import {
20
+ write,
21
+ KTX2Container,
22
+ KHR_DF_CHANNEL_RGBSDA_ALPHA,
23
+ KHR_DF_CHANNEL_RGBSDA_BLUE,
24
+ KHR_DF_CHANNEL_RGBSDA_GREEN,
25
+ KHR_DF_CHANNEL_RGBSDA_RED,
26
+ KHR_DF_MODEL_RGBSDA,
27
+ KHR_DF_PRIMARIES_BT709,
28
+ KHR_DF_PRIMARIES_UNSPECIFIED,
29
+ KHR_DF_SAMPLE_DATATYPE_FLOAT,
30
+ KHR_DF_SAMPLE_DATATYPE_LINEAR,
31
+ KHR_DF_SAMPLE_DATATYPE_SIGNED,
32
+ KHR_DF_TRANSFER_LINEAR,
33
+ KHR_DF_TRANSFER_SRGB,
34
+ VK_FORMAT_R16_SFLOAT,
35
+ VK_FORMAT_R16G16_SFLOAT,
36
+ VK_FORMAT_R16G16B16A16_SFLOAT,
37
+ VK_FORMAT_R32_SFLOAT,
38
+ VK_FORMAT_R32G32_SFLOAT,
39
+ VK_FORMAT_R32G32B32A32_SFLOAT,
40
+ VK_FORMAT_R8_SRGB,
41
+ VK_FORMAT_R8_UNORM,
42
+ VK_FORMAT_R8G8_SRGB,
43
+ VK_FORMAT_R8G8_UNORM,
44
+ VK_FORMAT_R8G8B8A8_SRGB,
45
+ VK_FORMAT_R8G8B8A8_UNORM,
46
+ } from '../libs/ktx-parse.module.js';
47
+
48
+ /**
49
+ * References:
50
+ * - https://github.khronos.org/KTX-Specification/ktxspec.v2.html
51
+ * - https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html
52
+ * - https://github.com/donmccurdy/KTX-Parse
53
+ */
54
+
55
+ const VK_FORMAT_MAP = {
56
+
57
+ [ RGBAFormat ]: {
58
+ [ FloatType ]: {
59
+ [ NoColorSpace ]: VK_FORMAT_R32G32B32A32_SFLOAT,
60
+ [ LinearSRGBColorSpace ]: VK_FORMAT_R32G32B32A32_SFLOAT,
61
+ },
62
+ [ HalfFloatType ]: {
63
+ [ NoColorSpace ]: VK_FORMAT_R16G16B16A16_SFLOAT,
64
+ [ LinearSRGBColorSpace ]: VK_FORMAT_R16G16B16A16_SFLOAT,
65
+ },
66
+ [ UnsignedByteType ]: {
67
+ [ NoColorSpace ]: VK_FORMAT_R8G8B8A8_UNORM,
68
+ [ LinearSRGBColorSpace ]: VK_FORMAT_R8G8B8A8_UNORM,
69
+ [ SRGBColorSpace ]: VK_FORMAT_R8G8B8A8_SRGB,
70
+ },
71
+ },
72
+
73
+ [ RGFormat ]: {
74
+ [ FloatType ]: {
75
+ [ NoColorSpace ]: VK_FORMAT_R32G32_SFLOAT,
76
+ [ LinearSRGBColorSpace ]: VK_FORMAT_R32G32_SFLOAT,
77
+ },
78
+ [ HalfFloatType ]: {
79
+ [ NoColorSpace ]: VK_FORMAT_R16G16_SFLOAT,
80
+ [ LinearSRGBColorSpace ]: VK_FORMAT_R16G16_SFLOAT,
81
+ },
82
+ [ UnsignedByteType ]: {
83
+ [ NoColorSpace ]: VK_FORMAT_R8G8_UNORM,
84
+ [ LinearSRGBColorSpace ]: VK_FORMAT_R8G8_UNORM,
85
+ [ SRGBColorSpace ]: VK_FORMAT_R8G8_SRGB,
86
+ },
87
+ },
88
+
89
+ [ RedFormat ]: {
90
+ [ FloatType ]: {
91
+ [ NoColorSpace ]: VK_FORMAT_R32_SFLOAT,
92
+ [ LinearSRGBColorSpace ]: VK_FORMAT_R32_SFLOAT,
93
+ },
94
+ [ HalfFloatType ]: {
95
+ [ NoColorSpace ]: VK_FORMAT_R16_SFLOAT,
96
+ [ LinearSRGBColorSpace ]: VK_FORMAT_R16_SFLOAT,
97
+ },
98
+ [ UnsignedByteType ]: {
99
+ [ NoColorSpace ]: VK_FORMAT_R8_UNORM,
100
+ [ LinearSRGBColorSpace ]: VK_FORMAT_R8_UNORM,
101
+ [ SRGBColorSpace ]: VK_FORMAT_R8_SRGB,
102
+ },
103
+ },
104
+
105
+ };
106
+
107
+ const KHR_DF_CHANNEL_MAP = [
108
+
109
+ KHR_DF_CHANNEL_RGBSDA_RED,
110
+ KHR_DF_CHANNEL_RGBSDA_GREEN,
111
+ KHR_DF_CHANNEL_RGBSDA_BLUE,
112
+ KHR_DF_CHANNEL_RGBSDA_ALPHA,
113
+
114
+ ];
115
+
116
+ // TODO: sampleLower and sampleUpper may change based on color space.
117
+ const KHR_DF_CHANNEL_SAMPLE_LOWER_UPPER = {
118
+
119
+ [ FloatType ]: [ 0xbf800000, 0x3f800000 ],
120
+ [ HalfFloatType ]: [ 0xbf800000, 0x3f800000 ],
121
+ [ UnsignedByteType ]: [ 0, 255 ],
122
+
123
+ };
124
+
125
+ const ERROR_INPUT = 'THREE.KTX2Exporter: Supported inputs are DataTexture, Data3DTexture, or WebGLRenderer and WebGLRenderTarget.';
126
+ const ERROR_FORMAT = 'THREE.KTX2Exporter: Supported formats are RGBAFormat, RGFormat, or RedFormat.';
127
+ const ERROR_TYPE = 'THREE.KTX2Exporter: Supported types are FloatType, HalfFloatType, or UnsignedByteType."';
128
+ const ERROR_COLOR_SPACE = 'THREE.KTX2Exporter: Supported color spaces are SRGBColorSpace (UnsignedByteType only), LinearSRGBColorSpace, or NoColorSpace.';
129
+
130
+ export class KTX2Exporter {
131
+
132
+ async parse( arg1, arg2 ) {
133
+
134
+ let texture;
135
+
136
+ if ( arg1.isDataTexture || arg1.isData3DTexture ) {
137
+
138
+ texture = arg1;
139
+
140
+ } else if ( ( arg1.isWebGLRenderer || arg1.isWebGPURenderer ) && arg2.isRenderTarget ) {
141
+
142
+ texture = await toDataTexture( arg1, arg2 );
143
+
144
+ } else {
145
+
146
+ throw new Error( ERROR_INPUT );
147
+
148
+ }
149
+
150
+ if ( VK_FORMAT_MAP[ texture.format ] === undefined ) {
151
+
152
+ throw new Error( ERROR_FORMAT );
153
+
154
+ }
155
+
156
+ if ( VK_FORMAT_MAP[ texture.format ][ texture.type ] === undefined ) {
157
+
158
+ throw new Error( ERROR_TYPE );
159
+
160
+ }
161
+
162
+ if ( VK_FORMAT_MAP[ texture.format ][ texture.type ][ texture.colorSpace ] === undefined ) {
163
+
164
+ throw new Error( ERROR_COLOR_SPACE );
165
+
166
+ }
167
+
168
+ //
169
+
170
+ const array = texture.image.data;
171
+ const channelCount = getChannelCount( texture );
172
+ const container = new KTX2Container();
173
+
174
+ container.vkFormat = VK_FORMAT_MAP[ texture.format ][ texture.type ][ texture.colorSpace ];
175
+ container.typeSize = array.BYTES_PER_ELEMENT;
176
+ container.pixelWidth = texture.image.width;
177
+ container.pixelHeight = texture.image.height;
178
+
179
+ if ( texture.isData3DTexture ) {
180
+
181
+ container.pixelDepth = texture.image.depth;
182
+
183
+ }
184
+
185
+ //
186
+
187
+ const basicDesc = container.dataFormatDescriptor[ 0 ];
188
+
189
+ basicDesc.colorModel = KHR_DF_MODEL_RGBSDA;
190
+ basicDesc.colorPrimaries = texture.colorSpace === NoColorSpace
191
+ ? KHR_DF_PRIMARIES_UNSPECIFIED
192
+ : KHR_DF_PRIMARIES_BT709;
193
+ basicDesc.transferFunction = ColorManagement.getTransfer( texture.colorSpace ) === SRGBTransfer
194
+ ? KHR_DF_TRANSFER_SRGB
195
+ : KHR_DF_TRANSFER_LINEAR;
196
+
197
+ basicDesc.texelBlockDimension = [ 0, 0, 0, 0 ];
198
+
199
+ basicDesc.bytesPlane = [
200
+
201
+ container.typeSize * channelCount, 0, 0, 0, 0, 0, 0, 0,
202
+
203
+ ];
204
+
205
+ for ( let i = 0; i < channelCount; ++ i ) {
206
+
207
+ let channelType = KHR_DF_CHANNEL_MAP[ i ];
208
+
209
+ // Assign KHR_DF_SAMPLE_DATATYPE_LINEAR if the channel is linear _and_ differs from the transfer function.
210
+ if ( channelType === KHR_DF_CHANNEL_RGBSDA_ALPHA && basicDesc.transferFunction !== KHR_DF_TRANSFER_LINEAR ) {
211
+
212
+ channelType |= KHR_DF_SAMPLE_DATATYPE_LINEAR;
213
+
214
+ }
215
+
216
+ if ( texture.type === FloatType || texture.type === HalfFloatType ) {
217
+
218
+ channelType |= KHR_DF_SAMPLE_DATATYPE_FLOAT;
219
+ channelType |= KHR_DF_SAMPLE_DATATYPE_SIGNED;
220
+
221
+ }
222
+
223
+ basicDesc.samples.push( {
224
+
225
+ channelType: channelType,
226
+ bitOffset: i * array.BYTES_PER_ELEMENT * 8,
227
+ bitLength: array.BYTES_PER_ELEMENT * 8 - 1,
228
+ samplePosition: [ 0, 0, 0, 0 ],
229
+ sampleLower: KHR_DF_CHANNEL_SAMPLE_LOWER_UPPER[ texture.type ][ 0 ],
230
+ sampleUpper: KHR_DF_CHANNEL_SAMPLE_LOWER_UPPER[ texture.type ][ 1 ],
231
+
232
+ } );
233
+
234
+ }
235
+
236
+ //
237
+
238
+ container.levels = [ {
239
+
240
+ levelData: new Uint8Array( array.buffer, array.byteOffset, array.byteLength ),
241
+ uncompressedByteLength: array.byteLength,
242
+
243
+ } ];
244
+
245
+ //
246
+
247
+ container.keyValue[ 'KTXwriter' ] = `three.js ${ REVISION }`;
248
+
249
+ //
250
+
251
+ return write( container, { keepWriter: true } );
252
+
253
+ }
254
+
255
+ }
256
+
257
+ async function toDataTexture( renderer, rtt ) {
258
+
259
+ const channelCount = getChannelCount( rtt.texture );
260
+
261
+ let view;
262
+
263
+ if ( renderer.isWebGLRenderer ) {
264
+
265
+ if ( rtt.texture.type === FloatType ) {
266
+
267
+ view = new Float32Array( rtt.width * rtt.height * channelCount );
268
+
269
+ } else if ( rtt.texture.type === HalfFloatType ) {
270
+
271
+ view = new Uint16Array( rtt.width * rtt.height * channelCount );
272
+
273
+ } else if ( rtt.texture.type === UnsignedByteType ) {
274
+
275
+ view = new Uint8Array( rtt.width * rtt.height * channelCount );
276
+
277
+ } else {
278
+
279
+ throw new Error( ERROR_TYPE );
280
+
281
+ }
282
+
283
+ await renderer.readRenderTargetPixelsAsync( rtt, 0, 0, rtt.width, rtt.height, view );
284
+
285
+ } else {
286
+
287
+ view = await renderer.readRenderTargetPixelsAsync( rtt, 0, 0, rtt.width, rtt.height );
288
+
289
+ }
290
+
291
+ const texture = new DataTexture( view, rtt.width, rtt.height, rtt.texture.format, rtt.texture.type );
292
+
293
+ texture.colorSpace = rtt.texture.colorSpace;
294
+
295
+ return texture;
296
+
297
+ }
298
+
299
+ function getChannelCount( texture ) {
300
+
301
+ switch ( texture.format ) {
302
+
303
+ case RGBAFormat:
304
+
305
+ return 4;
306
+
307
+ case RGFormat:
308
+ case RGIntegerFormat:
309
+
310
+ return 2;
311
+
312
+ case RedFormat:
313
+ case RedIntegerFormat:
314
+
315
+ return 1;
316
+
317
+ default:
318
+
319
+ throw new Error( ERROR_FORMAT );
320
+
321
+ }
322
+
323
+ }
libs/three.js/0.172.0/jsm/exporters/OBJExporter.js ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Color,
3
+ ColorManagement,
4
+ Matrix3,
5
+ SRGBColorSpace,
6
+ Vector2,
7
+ Vector3
8
+ } from 'three';
9
+
10
+ class OBJExporter {
11
+
12
+ parse( object ) {
13
+
14
+ let output = '';
15
+
16
+ let indexVertex = 0;
17
+ let indexVertexUvs = 0;
18
+ let indexNormals = 0;
19
+
20
+ const vertex = new Vector3();
21
+ const color = new Color();
22
+ const normal = new Vector3();
23
+ const uv = new Vector2();
24
+
25
+ const face = [];
26
+
27
+ function parseMesh( mesh ) {
28
+
29
+ let nbVertex = 0;
30
+ let nbNormals = 0;
31
+ let nbVertexUvs = 0;
32
+
33
+ const geometry = mesh.geometry;
34
+
35
+ const normalMatrixWorld = new Matrix3();
36
+
37
+ // shortcuts
38
+ const vertices = geometry.getAttribute( 'position' );
39
+ const normals = geometry.getAttribute( 'normal' );
40
+ const uvs = geometry.getAttribute( 'uv' );
41
+ const indices = geometry.getIndex();
42
+
43
+ // name of the mesh object
44
+ output += 'o ' + mesh.name + '\n';
45
+
46
+ // name of the mesh material
47
+ if ( mesh.material && mesh.material.name ) {
48
+
49
+ output += 'usemtl ' + mesh.material.name + '\n';
50
+
51
+ }
52
+
53
+ // vertices
54
+
55
+ if ( vertices !== undefined ) {
56
+
57
+ for ( let i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
58
+
59
+ vertex.fromBufferAttribute( vertices, i );
60
+
61
+ // transform the vertex to world space
62
+ vertex.applyMatrix4( mesh.matrixWorld );
63
+
64
+ // transform the vertex to export format
65
+ output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
66
+
67
+ }
68
+
69
+ }
70
+
71
+ // uvs
72
+
73
+ if ( uvs !== undefined ) {
74
+
75
+ for ( let i = 0, l = uvs.count; i < l; i ++, nbVertexUvs ++ ) {
76
+
77
+ uv.fromBufferAttribute( uvs, i );
78
+
79
+ // transform the uv to export format
80
+ output += 'vt ' + uv.x + ' ' + uv.y + '\n';
81
+
82
+ }
83
+
84
+ }
85
+
86
+ // normals
87
+
88
+ if ( normals !== undefined ) {
89
+
90
+ normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
91
+
92
+ for ( let i = 0, l = normals.count; i < l; i ++, nbNormals ++ ) {
93
+
94
+ normal.fromBufferAttribute( normals, i );
95
+
96
+ // transform the normal to world space
97
+ normal.applyMatrix3( normalMatrixWorld ).normalize();
98
+
99
+ // transform the normal to export format
100
+ output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
101
+
102
+ }
103
+
104
+ }
105
+
106
+ // faces
107
+
108
+ if ( indices !== null ) {
109
+
110
+ for ( let i = 0, l = indices.count; i < l; i += 3 ) {
111
+
112
+ for ( let m = 0; m < 3; m ++ ) {
113
+
114
+ const j = indices.getX( i + m ) + 1;
115
+
116
+ face[ m ] = ( indexVertex + j ) + ( normals || uvs ? '/' + ( uvs ? ( indexVertexUvs + j ) : '' ) + ( normals ? '/' + ( indexNormals + j ) : '' ) : '' );
117
+
118
+ }
119
+
120
+ // transform the face to export format
121
+ output += 'f ' + face.join( ' ' ) + '\n';
122
+
123
+ }
124
+
125
+ } else {
126
+
127
+ for ( let i = 0, l = vertices.count; i < l; i += 3 ) {
128
+
129
+ for ( let m = 0; m < 3; m ++ ) {
130
+
131
+ const j = i + m + 1;
132
+
133
+ face[ m ] = ( indexVertex + j ) + ( normals || uvs ? '/' + ( uvs ? ( indexVertexUvs + j ) : '' ) + ( normals ? '/' + ( indexNormals + j ) : '' ) : '' );
134
+
135
+ }
136
+
137
+ // transform the face to export format
138
+ output += 'f ' + face.join( ' ' ) + '\n';
139
+
140
+ }
141
+
142
+ }
143
+
144
+ // update index
145
+ indexVertex += nbVertex;
146
+ indexVertexUvs += nbVertexUvs;
147
+ indexNormals += nbNormals;
148
+
149
+ }
150
+
151
+ function parseLine( line ) {
152
+
153
+ let nbVertex = 0;
154
+
155
+ const geometry = line.geometry;
156
+ const type = line.type;
157
+
158
+ // shortcuts
159
+ const vertices = geometry.getAttribute( 'position' );
160
+
161
+ // name of the line object
162
+ output += 'o ' + line.name + '\n';
163
+
164
+ if ( vertices !== undefined ) {
165
+
166
+ for ( let i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
167
+
168
+ vertex.fromBufferAttribute( vertices, i );
169
+
170
+ // transform the vertex to world space
171
+ vertex.applyMatrix4( line.matrixWorld );
172
+
173
+ // transform the vertex to export format
174
+ output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
175
+
176
+ }
177
+
178
+ }
179
+
180
+ if ( type === 'Line' ) {
181
+
182
+ output += 'l ';
183
+
184
+ for ( let j = 1, l = vertices.count; j <= l; j ++ ) {
185
+
186
+ output += ( indexVertex + j ) + ' ';
187
+
188
+ }
189
+
190
+ output += '\n';
191
+
192
+ }
193
+
194
+ if ( type === 'LineSegments' ) {
195
+
196
+ for ( let j = 1, k = j + 1, l = vertices.count; j < l; j += 2, k = j + 1 ) {
197
+
198
+ output += 'l ' + ( indexVertex + j ) + ' ' + ( indexVertex + k ) + '\n';
199
+
200
+ }
201
+
202
+ }
203
+
204
+ // update index
205
+ indexVertex += nbVertex;
206
+
207
+ }
208
+
209
+ function parsePoints( points ) {
210
+
211
+ let nbVertex = 0;
212
+
213
+ const geometry = points.geometry;
214
+
215
+ const vertices = geometry.getAttribute( 'position' );
216
+ const colors = geometry.getAttribute( 'color' );
217
+
218
+ output += 'o ' + points.name + '\n';
219
+
220
+ if ( vertices !== undefined ) {
221
+
222
+ for ( let i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
223
+
224
+ vertex.fromBufferAttribute( vertices, i );
225
+ vertex.applyMatrix4( points.matrixWorld );
226
+
227
+ output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z;
228
+
229
+ if ( colors !== undefined ) {
230
+
231
+ color.fromBufferAttribute( colors, i );
232
+
233
+ ColorManagement.fromWorkingColorSpace( color, SRGBColorSpace );
234
+
235
+ output += ' ' + color.r + ' ' + color.g + ' ' + color.b;
236
+
237
+ }
238
+
239
+ output += '\n';
240
+
241
+ }
242
+
243
+ output += 'p ';
244
+
245
+ for ( let j = 1, l = vertices.count; j <= l; j ++ ) {
246
+
247
+ output += ( indexVertex + j ) + ' ';
248
+
249
+ }
250
+
251
+ output += '\n';
252
+
253
+ }
254
+
255
+ // update index
256
+ indexVertex += nbVertex;
257
+
258
+ }
259
+
260
+ object.traverse( function ( child ) {
261
+
262
+ if ( child.isMesh === true ) {
263
+
264
+ parseMesh( child );
265
+
266
+ }
267
+
268
+ if ( child.isLine === true ) {
269
+
270
+ parseLine( child );
271
+
272
+ }
273
+
274
+ if ( child.isPoints === true ) {
275
+
276
+ parsePoints( child );
277
+
278
+ }
279
+
280
+ } );
281
+
282
+ return output;
283
+
284
+ }
285
+
286
+ }
287
+
288
+ export { OBJExporter };
libs/three.js/0.172.0/jsm/exporters/PLYExporter.js ADDED
@@ -0,0 +1,530 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Matrix3,
3
+ Vector3,
4
+ Color,
5
+ ColorManagement,
6
+ SRGBColorSpace
7
+ } from 'three';
8
+
9
+ /**
10
+ * https://github.com/gkjohnson/ply-exporter-js
11
+ *
12
+ * Usage:
13
+ * const exporter = new PLYExporter();
14
+ *
15
+ * // second argument is a list of options
16
+ * exporter.parse(mesh, data => console.log(data), { binary: true, excludeAttributes: [ 'color' ], littleEndian: true });
17
+ *
18
+ * Format Definition:
19
+ * http://paulbourke.net/dataformats/ply/
20
+ */
21
+
22
+ class PLYExporter {
23
+
24
+ parse( object, onDone, options = {} ) {
25
+
26
+ // Iterate over the valid meshes in the object
27
+ function traverseMeshes( cb ) {
28
+
29
+ object.traverse( function ( child ) {
30
+
31
+ if ( child.isMesh === true || child.isPoints ) {
32
+
33
+ const mesh = child;
34
+ const geometry = mesh.geometry;
35
+
36
+ if ( geometry.hasAttribute( 'position' ) === true ) {
37
+
38
+ cb( mesh, geometry );
39
+
40
+ }
41
+
42
+ }
43
+
44
+ } );
45
+
46
+ }
47
+
48
+ // Default options
49
+ const defaultOptions = {
50
+ binary: false,
51
+ excludeAttributes: [], // normal, uv, color, index
52
+ littleEndian: false
53
+ };
54
+
55
+ options = Object.assign( defaultOptions, options );
56
+
57
+ const excludeAttributes = options.excludeAttributes;
58
+ let includeIndices = true;
59
+ let includeNormals = false;
60
+ let includeColors = false;
61
+ let includeUVs = false;
62
+
63
+ // count the vertices, check which properties are used,
64
+ // and cache the BufferGeometry
65
+ let vertexCount = 0;
66
+ let faceCount = 0;
67
+
68
+ object.traverse( function ( child ) {
69
+
70
+ if ( child.isMesh === true ) {
71
+
72
+ const mesh = child;
73
+ const geometry = mesh.geometry;
74
+
75
+ const vertices = geometry.getAttribute( 'position' );
76
+ const normals = geometry.getAttribute( 'normal' );
77
+ const uvs = geometry.getAttribute( 'uv' );
78
+ const colors = geometry.getAttribute( 'color' );
79
+ const indices = geometry.getIndex();
80
+
81
+ if ( vertices === undefined ) {
82
+
83
+ return;
84
+
85
+ }
86
+
87
+ vertexCount += vertices.count;
88
+ faceCount += indices ? indices.count / 3 : vertices.count / 3;
89
+
90
+ if ( normals !== undefined ) includeNormals = true;
91
+
92
+ if ( uvs !== undefined ) includeUVs = true;
93
+
94
+ if ( colors !== undefined ) includeColors = true;
95
+
96
+ } else if ( child.isPoints ) {
97
+
98
+ const mesh = child;
99
+ const geometry = mesh.geometry;
100
+
101
+ const vertices = geometry.getAttribute( 'position' );
102
+ const normals = geometry.getAttribute( 'normal' );
103
+ const colors = geometry.getAttribute( 'color' );
104
+
105
+ vertexCount += vertices.count;
106
+
107
+ if ( normals !== undefined ) includeNormals = true;
108
+
109
+ if ( colors !== undefined ) includeColors = true;
110
+
111
+ includeIndices = false;
112
+
113
+ }
114
+
115
+ } );
116
+
117
+ const tempColor = new Color();
118
+ includeIndices = includeIndices && excludeAttributes.indexOf( 'index' ) === - 1;
119
+ includeNormals = includeNormals && excludeAttributes.indexOf( 'normal' ) === - 1;
120
+ includeColors = includeColors && excludeAttributes.indexOf( 'color' ) === - 1;
121
+ includeUVs = includeUVs && excludeAttributes.indexOf( 'uv' ) === - 1;
122
+
123
+
124
+ if ( includeIndices && faceCount !== Math.floor( faceCount ) ) {
125
+
126
+ // point cloud meshes will not have an index array and may not have a
127
+ // number of vertices that is divisible by 3 (and therefore representable
128
+ // as triangles)
129
+ console.error(
130
+
131
+ 'PLYExporter: Failed to generate a valid PLY file with triangle indices because the ' +
132
+ 'number of indices is not divisible by 3.'
133
+
134
+ );
135
+
136
+ return null;
137
+
138
+ }
139
+
140
+ const indexByteCount = 4;
141
+
142
+ let header =
143
+ 'ply\n' +
144
+ `format ${ options.binary ? ( options.littleEndian ? 'binary_little_endian' : 'binary_big_endian' ) : 'ascii' } 1.0\n` +
145
+ `element vertex ${vertexCount}\n` +
146
+
147
+ // position
148
+ 'property float x\n' +
149
+ 'property float y\n' +
150
+ 'property float z\n';
151
+
152
+ if ( includeNormals === true ) {
153
+
154
+ // normal
155
+ header +=
156
+ 'property float nx\n' +
157
+ 'property float ny\n' +
158
+ 'property float nz\n';
159
+
160
+ }
161
+
162
+ if ( includeUVs === true ) {
163
+
164
+ // uvs
165
+ header +=
166
+ 'property float s\n' +
167
+ 'property float t\n';
168
+
169
+ }
170
+
171
+ if ( includeColors === true ) {
172
+
173
+ // colors
174
+ header +=
175
+ 'property uchar red\n' +
176
+ 'property uchar green\n' +
177
+ 'property uchar blue\n';
178
+
179
+ }
180
+
181
+ if ( includeIndices === true ) {
182
+
183
+ // faces
184
+ header +=
185
+ `element face ${faceCount}\n` +
186
+ 'property list uchar int vertex_index\n';
187
+
188
+ }
189
+
190
+ header += 'end_header\n';
191
+
192
+
193
+ // Generate attribute data
194
+ const vertex = new Vector3();
195
+ const normalMatrixWorld = new Matrix3();
196
+ let result = null;
197
+
198
+ if ( options.binary === true ) {
199
+
200
+ // Binary File Generation
201
+ const headerBin = new TextEncoder().encode( header );
202
+
203
+ // 3 position values at 4 bytes
204
+ // 3 normal values at 4 bytes
205
+ // 3 color channels with 1 byte
206
+ // 2 uv values at 4 bytes
207
+ const vertexListLength = vertexCount * ( 4 * 3 + ( includeNormals ? 4 * 3 : 0 ) + ( includeColors ? 3 : 0 ) + ( includeUVs ? 4 * 2 : 0 ) );
208
+
209
+ // 1 byte shape descriptor
210
+ // 3 vertex indices at ${indexByteCount} bytes
211
+ const faceListLength = includeIndices ? faceCount * ( indexByteCount * 3 + 1 ) : 0;
212
+ const output = new DataView( new ArrayBuffer( headerBin.length + vertexListLength + faceListLength ) );
213
+ new Uint8Array( output.buffer ).set( headerBin, 0 );
214
+
215
+
216
+ let vOffset = headerBin.length;
217
+ let fOffset = headerBin.length + vertexListLength;
218
+ let writtenVertices = 0;
219
+ traverseMeshes( function ( mesh, geometry ) {
220
+
221
+ const vertices = geometry.getAttribute( 'position' );
222
+ const normals = geometry.getAttribute( 'normal' );
223
+ const uvs = geometry.getAttribute( 'uv' );
224
+ const colors = geometry.getAttribute( 'color' );
225
+ const indices = geometry.getIndex();
226
+
227
+ normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
228
+
229
+ for ( let i = 0, l = vertices.count; i < l; i ++ ) {
230
+
231
+ vertex.fromBufferAttribute( vertices, i );
232
+
233
+ vertex.applyMatrix4( mesh.matrixWorld );
234
+
235
+
236
+ // Position information
237
+ output.setFloat32( vOffset, vertex.x, options.littleEndian );
238
+ vOffset += 4;
239
+
240
+ output.setFloat32( vOffset, vertex.y, options.littleEndian );
241
+ vOffset += 4;
242
+
243
+ output.setFloat32( vOffset, vertex.z, options.littleEndian );
244
+ vOffset += 4;
245
+
246
+ // Normal information
247
+ if ( includeNormals === true ) {
248
+
249
+ if ( normals != null ) {
250
+
251
+ vertex.fromBufferAttribute( normals, i );
252
+
253
+ vertex.applyMatrix3( normalMatrixWorld ).normalize();
254
+
255
+ output.setFloat32( vOffset, vertex.x, options.littleEndian );
256
+ vOffset += 4;
257
+
258
+ output.setFloat32( vOffset, vertex.y, options.littleEndian );
259
+ vOffset += 4;
260
+
261
+ output.setFloat32( vOffset, vertex.z, options.littleEndian );
262
+ vOffset += 4;
263
+
264
+ } else {
265
+
266
+ output.setFloat32( vOffset, 0, options.littleEndian );
267
+ vOffset += 4;
268
+
269
+ output.setFloat32( vOffset, 0, options.littleEndian );
270
+ vOffset += 4;
271
+
272
+ output.setFloat32( vOffset, 0, options.littleEndian );
273
+ vOffset += 4;
274
+
275
+ }
276
+
277
+ }
278
+
279
+ // UV information
280
+ if ( includeUVs === true ) {
281
+
282
+ if ( uvs != null ) {
283
+
284
+ output.setFloat32( vOffset, uvs.getX( i ), options.littleEndian );
285
+ vOffset += 4;
286
+
287
+ output.setFloat32( vOffset, uvs.getY( i ), options.littleEndian );
288
+ vOffset += 4;
289
+
290
+ } else {
291
+
292
+ output.setFloat32( vOffset, 0, options.littleEndian );
293
+ vOffset += 4;
294
+
295
+ output.setFloat32( vOffset, 0, options.littleEndian );
296
+ vOffset += 4;
297
+
298
+ }
299
+
300
+ }
301
+
302
+ // Color information
303
+ if ( includeColors === true ) {
304
+
305
+ if ( colors != null ) {
306
+
307
+ tempColor.fromBufferAttribute( colors, i );
308
+
309
+ ColorManagement.fromWorkingColorSpace( tempColor, SRGBColorSpace );
310
+
311
+ output.setUint8( vOffset, Math.floor( tempColor.r * 255 ) );
312
+ vOffset += 1;
313
+
314
+ output.setUint8( vOffset, Math.floor( tempColor.g * 255 ) );
315
+ vOffset += 1;
316
+
317
+ output.setUint8( vOffset, Math.floor( tempColor.b * 255 ) );
318
+ vOffset += 1;
319
+
320
+ } else {
321
+
322
+ output.setUint8( vOffset, 255 );
323
+ vOffset += 1;
324
+
325
+ output.setUint8( vOffset, 255 );
326
+ vOffset += 1;
327
+
328
+ output.setUint8( vOffset, 255 );
329
+ vOffset += 1;
330
+
331
+ }
332
+
333
+ }
334
+
335
+ }
336
+
337
+ if ( includeIndices === true ) {
338
+
339
+ // Create the face list
340
+
341
+ if ( indices !== null ) {
342
+
343
+ for ( let i = 0, l = indices.count; i < l; i += 3 ) {
344
+
345
+ output.setUint8( fOffset, 3 );
346
+ fOffset += 1;
347
+
348
+ output.setUint32( fOffset, indices.getX( i + 0 ) + writtenVertices, options.littleEndian );
349
+ fOffset += indexByteCount;
350
+
351
+ output.setUint32( fOffset, indices.getX( i + 1 ) + writtenVertices, options.littleEndian );
352
+ fOffset += indexByteCount;
353
+
354
+ output.setUint32( fOffset, indices.getX( i + 2 ) + writtenVertices, options.littleEndian );
355
+ fOffset += indexByteCount;
356
+
357
+ }
358
+
359
+ } else {
360
+
361
+ for ( let i = 0, l = vertices.count; i < l; i += 3 ) {
362
+
363
+ output.setUint8( fOffset, 3 );
364
+ fOffset += 1;
365
+
366
+ output.setUint32( fOffset, writtenVertices + i, options.littleEndian );
367
+ fOffset += indexByteCount;
368
+
369
+ output.setUint32( fOffset, writtenVertices + i + 1, options.littleEndian );
370
+ fOffset += indexByteCount;
371
+
372
+ output.setUint32( fOffset, writtenVertices + i + 2, options.littleEndian );
373
+ fOffset += indexByteCount;
374
+
375
+ }
376
+
377
+ }
378
+
379
+ }
380
+
381
+
382
+ // Save the amount of verts we've already written so we can offset
383
+ // the face index on the next mesh
384
+ writtenVertices += vertices.count;
385
+
386
+ } );
387
+
388
+ result = output.buffer;
389
+
390
+ } else {
391
+
392
+ // Ascii File Generation
393
+ // count the number of vertices
394
+ let writtenVertices = 0;
395
+ let vertexList = '';
396
+ let faceList = '';
397
+
398
+ traverseMeshes( function ( mesh, geometry ) {
399
+
400
+ const vertices = geometry.getAttribute( 'position' );
401
+ const normals = geometry.getAttribute( 'normal' );
402
+ const uvs = geometry.getAttribute( 'uv' );
403
+ const colors = geometry.getAttribute( 'color' );
404
+ const indices = geometry.getIndex();
405
+
406
+ normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
407
+
408
+ // form each line
409
+ for ( let i = 0, l = vertices.count; i < l; i ++ ) {
410
+
411
+ vertex.fromBufferAttribute( vertices, i );
412
+
413
+ vertex.applyMatrix4( mesh.matrixWorld );
414
+
415
+
416
+ // Position information
417
+ let line =
418
+ vertex.x + ' ' +
419
+ vertex.y + ' ' +
420
+ vertex.z;
421
+
422
+ // Normal information
423
+ if ( includeNormals === true ) {
424
+
425
+ if ( normals != null ) {
426
+
427
+ vertex.fromBufferAttribute( normals, i );
428
+
429
+ vertex.applyMatrix3( normalMatrixWorld ).normalize();
430
+
431
+ line += ' ' +
432
+ vertex.x + ' ' +
433
+ vertex.y + ' ' +
434
+ vertex.z;
435
+
436
+ } else {
437
+
438
+ line += ' 0 0 0';
439
+
440
+ }
441
+
442
+ }
443
+
444
+ // UV information
445
+ if ( includeUVs === true ) {
446
+
447
+ if ( uvs != null ) {
448
+
449
+ line += ' ' +
450
+ uvs.getX( i ) + ' ' +
451
+ uvs.getY( i );
452
+
453
+ } else {
454
+
455
+ line += ' 0 0';
456
+
457
+ }
458
+
459
+ }
460
+
461
+ // Color information
462
+ if ( includeColors === true ) {
463
+
464
+ if ( colors != null ) {
465
+
466
+ tempColor.fromBufferAttribute( colors, i );
467
+
468
+ ColorManagement.fromWorkingColorSpace( tempColor, SRGBColorSpace );
469
+
470
+ line += ' ' +
471
+ Math.floor( tempColor.r * 255 ) + ' ' +
472
+ Math.floor( tempColor.g * 255 ) + ' ' +
473
+ Math.floor( tempColor.b * 255 );
474
+
475
+ } else {
476
+
477
+ line += ' 255 255 255';
478
+
479
+ }
480
+
481
+ }
482
+
483
+ vertexList += line + '\n';
484
+
485
+ }
486
+
487
+ // Create the face list
488
+ if ( includeIndices === true ) {
489
+
490
+ if ( indices !== null ) {
491
+
492
+ for ( let i = 0, l = indices.count; i < l; i += 3 ) {
493
+
494
+ faceList += `3 ${ indices.getX( i + 0 ) + writtenVertices }`;
495
+ faceList += ` ${ indices.getX( i + 1 ) + writtenVertices }`;
496
+ faceList += ` ${ indices.getX( i + 2 ) + writtenVertices }\n`;
497
+
498
+ }
499
+
500
+ } else {
501
+
502
+ for ( let i = 0, l = vertices.count; i < l; i += 3 ) {
503
+
504
+ faceList += `3 ${ writtenVertices + i } ${ writtenVertices + i + 1 } ${ writtenVertices + i + 2 }\n`;
505
+
506
+ }
507
+
508
+ }
509
+
510
+ faceCount += indices ? indices.count / 3 : vertices.count / 3;
511
+
512
+ }
513
+
514
+ writtenVertices += vertices.count;
515
+
516
+ } );
517
+
518
+ result = `${ header }${vertexList}${ includeIndices ? `${faceList}\n` : '\n' }`;
519
+
520
+ }
521
+
522
+ if ( typeof onDone === 'function' ) requestAnimationFrame( () => onDone( result ) );
523
+
524
+ return result;
525
+
526
+ }
527
+
528
+ }
529
+
530
+ export { PLYExporter };
libs/three.js/0.172.0/jsm/exporters/STLExporter.js ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Vector3 } from 'three';
2
+
3
+ /**
4
+ * Usage:
5
+ * const exporter = new STLExporter();
6
+ *
7
+ * // second argument is a list of options
8
+ * const data = exporter.parse( mesh, { binary: true } );
9
+ *
10
+ */
11
+
12
+ class STLExporter {
13
+
14
+ parse( scene, options = {} ) {
15
+
16
+ options = Object.assign( {
17
+ binary: false
18
+ }, options );
19
+
20
+ const binary = options.binary;
21
+
22
+ //
23
+
24
+ const objects = [];
25
+ let triangles = 0;
26
+
27
+ scene.traverse( function ( object ) {
28
+
29
+ if ( object.isMesh ) {
30
+
31
+ const geometry = object.geometry;
32
+
33
+ const index = geometry.index;
34
+ const positionAttribute = geometry.getAttribute( 'position' );
35
+
36
+ triangles += ( index !== null ) ? ( index.count / 3 ) : ( positionAttribute.count / 3 );
37
+
38
+ objects.push( {
39
+ object3d: object,
40
+ geometry: geometry
41
+ } );
42
+
43
+ }
44
+
45
+ } );
46
+
47
+ let output;
48
+ let offset = 80; // skip header
49
+
50
+ if ( binary === true ) {
51
+
52
+ const bufferLength = triangles * 2 + triangles * 3 * 4 * 4 + 80 + 4;
53
+ const arrayBuffer = new ArrayBuffer( bufferLength );
54
+ output = new DataView( arrayBuffer );
55
+ output.setUint32( offset, triangles, true ); offset += 4;
56
+
57
+ } else {
58
+
59
+ output = '';
60
+ output += 'solid exported\n';
61
+
62
+ }
63
+
64
+ const vA = new Vector3();
65
+ const vB = new Vector3();
66
+ const vC = new Vector3();
67
+ const cb = new Vector3();
68
+ const ab = new Vector3();
69
+ const normal = new Vector3();
70
+
71
+ for ( let i = 0, il = objects.length; i < il; i ++ ) {
72
+
73
+ const object = objects[ i ].object3d;
74
+ const geometry = objects[ i ].geometry;
75
+
76
+ const index = geometry.index;
77
+ const positionAttribute = geometry.getAttribute( 'position' );
78
+
79
+ if ( index !== null ) {
80
+
81
+ // indexed geometry
82
+
83
+ for ( let j = 0; j < index.count; j += 3 ) {
84
+
85
+ const a = index.getX( j + 0 );
86
+ const b = index.getX( j + 1 );
87
+ const c = index.getX( j + 2 );
88
+
89
+ writeFace( a, b, c, positionAttribute, object );
90
+
91
+ }
92
+
93
+ } else {
94
+
95
+ // non-indexed geometry
96
+
97
+ for ( let j = 0; j < positionAttribute.count; j += 3 ) {
98
+
99
+ const a = j + 0;
100
+ const b = j + 1;
101
+ const c = j + 2;
102
+
103
+ writeFace( a, b, c, positionAttribute, object );
104
+
105
+ }
106
+
107
+ }
108
+
109
+ }
110
+
111
+ if ( binary === false ) {
112
+
113
+ output += 'endsolid exported\n';
114
+
115
+ }
116
+
117
+ return output;
118
+
119
+ function writeFace( a, b, c, positionAttribute, object ) {
120
+
121
+ vA.fromBufferAttribute( positionAttribute, a );
122
+ vB.fromBufferAttribute( positionAttribute, b );
123
+ vC.fromBufferAttribute( positionAttribute, c );
124
+
125
+ if ( object.isSkinnedMesh === true ) {
126
+
127
+ object.applyBoneTransform( a, vA );
128
+ object.applyBoneTransform( b, vB );
129
+ object.applyBoneTransform( c, vC );
130
+
131
+ }
132
+
133
+ vA.applyMatrix4( object.matrixWorld );
134
+ vB.applyMatrix4( object.matrixWorld );
135
+ vC.applyMatrix4( object.matrixWorld );
136
+
137
+ writeNormal( vA, vB, vC );
138
+
139
+ writeVertex( vA );
140
+ writeVertex( vB );
141
+ writeVertex( vC );
142
+
143
+ if ( binary === true ) {
144
+
145
+ output.setUint16( offset, 0, true ); offset += 2;
146
+
147
+ } else {
148
+
149
+ output += '\t\tendloop\n';
150
+ output += '\tendfacet\n';
151
+
152
+ }
153
+
154
+ }
155
+
156
+ function writeNormal( vA, vB, vC ) {
157
+
158
+ cb.subVectors( vC, vB );
159
+ ab.subVectors( vA, vB );
160
+ cb.cross( ab ).normalize();
161
+
162
+ normal.copy( cb ).normalize();
163
+
164
+ if ( binary === true ) {
165
+
166
+ output.setFloat32( offset, normal.x, true ); offset += 4;
167
+ output.setFloat32( offset, normal.y, true ); offset += 4;
168
+ output.setFloat32( offset, normal.z, true ); offset += 4;
169
+
170
+ } else {
171
+
172
+ output += '\tfacet normal ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
173
+ output += '\t\touter loop\n';
174
+
175
+ }
176
+
177
+ }
178
+
179
+ function writeVertex( vertex ) {
180
+
181
+ if ( binary === true ) {
182
+
183
+ output.setFloat32( offset, vertex.x, true ); offset += 4;
184
+ output.setFloat32( offset, vertex.y, true ); offset += 4;
185
+ output.setFloat32( offset, vertex.z, true ); offset += 4;
186
+
187
+ } else {
188
+
189
+ output += '\t\t\tvertex ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
190
+
191
+ }
192
+
193
+ }
194
+
195
+ }
196
+
197
+ }
198
+
199
+ export { STLExporter };
libs/three.js/0.172.0/jsm/exporters/USDZExporter.js ADDED
@@ -0,0 +1,782 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ NoColorSpace,
3
+ DoubleSide,
4
+ Color,
5
+ } from 'three';
6
+
7
+ import {
8
+ strToU8,
9
+ zipSync,
10
+ } from '../libs/fflate.module.js';
11
+
12
+ class USDZExporter {
13
+
14
+ constructor() {
15
+
16
+ this.textureUtils = null;
17
+
18
+ }
19
+
20
+ setTextureUtils( utils ) {
21
+
22
+ this.textureUtils = utils;
23
+
24
+ }
25
+
26
+ parse( scene, onDone, onError, options ) {
27
+
28
+ this.parseAsync( scene, options ).then( onDone ).catch( onError );
29
+
30
+ }
31
+
32
+ async parseAsync( scene, options = {} ) {
33
+
34
+ options = Object.assign( {
35
+ ar: {
36
+ anchoring: { type: 'plane' },
37
+ planeAnchoring: { alignment: 'horizontal' }
38
+ },
39
+ includeAnchoringProperties: true,
40
+ quickLookCompatible: false,
41
+ maxTextureSize: 1024,
42
+ }, options );
43
+
44
+ const files = {};
45
+ const modelFileName = 'model.usda';
46
+
47
+ // model file should be first in USDZ archive so we init it here
48
+ files[ modelFileName ] = null;
49
+
50
+ let output = buildHeader();
51
+
52
+ output += buildSceneStart( options );
53
+
54
+ const materials = {};
55
+ const textures = {};
56
+
57
+ scene.traverseVisible( ( object ) => {
58
+
59
+ if ( object.isMesh ) {
60
+
61
+ const geometry = object.geometry;
62
+ const material = object.material;
63
+
64
+ if ( material.isMeshStandardMaterial ) {
65
+
66
+ const geometryFileName = 'geometries/Geometry_' + geometry.id + '.usda';
67
+
68
+ if ( ! ( geometryFileName in files ) ) {
69
+
70
+ const meshObject = buildMeshObject( geometry );
71
+ files[ geometryFileName ] = buildUSDFileAsString( meshObject );
72
+
73
+ }
74
+
75
+ if ( ! ( material.uuid in materials ) ) {
76
+
77
+ materials[ material.uuid ] = material;
78
+
79
+ }
80
+
81
+ output += buildXform( object, geometry, material );
82
+
83
+ } else {
84
+
85
+ console.warn( 'THREE.USDZExporter: Unsupported material type (USDZ only supports MeshStandardMaterial)', object );
86
+
87
+ }
88
+
89
+ } else if ( object.isCamera ) {
90
+
91
+ output += buildCamera( object );
92
+
93
+ }
94
+
95
+ } );
96
+
97
+
98
+ output += buildSceneEnd();
99
+
100
+ output += buildMaterials( materials, textures, options.quickLookCompatible );
101
+
102
+ files[ modelFileName ] = strToU8( output );
103
+ output = null;
104
+
105
+ for ( const id in textures ) {
106
+
107
+ let texture = textures[ id ];
108
+
109
+ if ( texture.isCompressedTexture === true ) {
110
+
111
+ if ( this.textureUtils === null ) {
112
+
113
+ throw new Error( 'THREE.USDZExporter: setTextureUtils() must be called to process compressed textures.' );
114
+
115
+ } else {
116
+
117
+ texture = await this.textureUtils.decompress( texture );
118
+
119
+ }
120
+
121
+ }
122
+
123
+ const canvas = imageToCanvas( texture.image, texture.flipY, options.maxTextureSize );
124
+ const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/png', 1 ) );
125
+
126
+ files[ `textures/Texture_${ id }.png` ] = new Uint8Array( await blob.arrayBuffer() );
127
+
128
+ }
129
+
130
+ // 64 byte alignment
131
+ // https://github.com/101arrowz/fflate/issues/39#issuecomment-777263109
132
+
133
+ let offset = 0;
134
+
135
+ for ( const filename in files ) {
136
+
137
+ const file = files[ filename ];
138
+ const headerSize = 34 + filename.length;
139
+
140
+ offset += headerSize;
141
+
142
+ const offsetMod64 = offset & 63;
143
+
144
+ if ( offsetMod64 !== 4 ) {
145
+
146
+ const padLength = 64 - offsetMod64;
147
+ const padding = new Uint8Array( padLength );
148
+
149
+ files[ filename ] = [ file, { extra: { 12345: padding } } ];
150
+
151
+ }
152
+
153
+ offset = file.length;
154
+
155
+ }
156
+
157
+ return zipSync( files, { level: 0 } );
158
+
159
+ }
160
+
161
+ }
162
+
163
+ function imageToCanvas( image, flipY, maxTextureSize ) {
164
+
165
+ if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
166
+ ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
167
+ ( typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas ) ||
168
+ ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
169
+
170
+ const scale = maxTextureSize / Math.max( image.width, image.height );
171
+
172
+ const canvas = document.createElement( 'canvas' );
173
+ canvas.width = image.width * Math.min( 1, scale );
174
+ canvas.height = image.height * Math.min( 1, scale );
175
+
176
+ const context = canvas.getContext( '2d' );
177
+
178
+ // TODO: We should be able to do this in the UsdTransform2d?
179
+
180
+ if ( flipY === true ) {
181
+
182
+ context.translate( 0, canvas.height );
183
+ context.scale( 1, - 1 );
184
+
185
+ }
186
+
187
+ context.drawImage( image, 0, 0, canvas.width, canvas.height );
188
+
189
+ return canvas;
190
+
191
+ } else {
192
+
193
+ throw new Error( 'THREE.USDZExporter: No valid image data found. Unable to process texture.' );
194
+
195
+ }
196
+
197
+ }
198
+
199
+ //
200
+
201
+ const PRECISION = 7;
202
+
203
+ function buildHeader() {
204
+
205
+ return `#usda 1.0
206
+ (
207
+ customLayerData = {
208
+ string creator = "Three.js USDZExporter"
209
+ }
210
+ defaultPrim = "Root"
211
+ metersPerUnit = 1
212
+ upAxis = "Y"
213
+ )
214
+
215
+ `;
216
+
217
+ }
218
+
219
+ function buildSceneStart( options ) {
220
+
221
+ const alignment = options.includeAnchoringProperties === true ? `
222
+ token preliminary:anchoring:type = "${options.ar.anchoring.type}"
223
+ token preliminary:planeAnchoring:alignment = "${options.ar.planeAnchoring.alignment}"
224
+ ` : '';
225
+ return `def Xform "Root"
226
+ {
227
+ def Scope "Scenes" (
228
+ kind = "sceneLibrary"
229
+ )
230
+ {
231
+ def Xform "Scene" (
232
+ customData = {
233
+ bool preliminary_collidesWithEnvironment = 0
234
+ string sceneName = "Scene"
235
+ }
236
+ sceneName = "Scene"
237
+ )
238
+ {${alignment}
239
+ `;
240
+
241
+ }
242
+
243
+ function buildSceneEnd() {
244
+
245
+ return `
246
+ }
247
+ }
248
+ }
249
+
250
+ `;
251
+
252
+ }
253
+
254
+ function buildUSDFileAsString( dataToInsert ) {
255
+
256
+ let output = buildHeader();
257
+ output += dataToInsert;
258
+ return strToU8( output );
259
+
260
+ }
261
+
262
+ // Xform
263
+
264
+ function buildXform( object, geometry, material ) {
265
+
266
+ const name = 'Object_' + object.id;
267
+ const transform = buildMatrix( object.matrixWorld );
268
+
269
+ if ( object.matrixWorld.determinant() < 0 ) {
270
+
271
+ console.warn( 'THREE.USDZExporter: USDZ does not support negative scales', object );
272
+
273
+ }
274
+
275
+ return `def Xform "${ name }" (
276
+ prepend references = @./geometries/Geometry_${ geometry.id }.usda@</Geometry>
277
+ prepend apiSchemas = ["MaterialBindingAPI"]
278
+ )
279
+ {
280
+ matrix4d xformOp:transform = ${ transform }
281
+ uniform token[] xformOpOrder = ["xformOp:transform"]
282
+
283
+ rel material:binding = </Materials/Material_${ material.id }>
284
+ }
285
+
286
+ `;
287
+
288
+ }
289
+
290
+ function buildMatrix( matrix ) {
291
+
292
+ const array = matrix.elements;
293
+
294
+ return `( ${ buildMatrixRow( array, 0 ) }, ${ buildMatrixRow( array, 4 ) }, ${ buildMatrixRow( array, 8 ) }, ${ buildMatrixRow( array, 12 ) } )`;
295
+
296
+ }
297
+
298
+ function buildMatrixRow( array, offset ) {
299
+
300
+ return `(${ array[ offset + 0 ] }, ${ array[ offset + 1 ] }, ${ array[ offset + 2 ] }, ${ array[ offset + 3 ] })`;
301
+
302
+ }
303
+
304
+ // Mesh
305
+
306
+ function buildMeshObject( geometry ) {
307
+
308
+ const mesh = buildMesh( geometry );
309
+ return `
310
+ def "Geometry"
311
+ {
312
+ ${mesh}
313
+ }
314
+ `;
315
+
316
+ }
317
+
318
+ function buildMesh( geometry ) {
319
+
320
+ const name = 'Geometry';
321
+ const attributes = geometry.attributes;
322
+ const count = attributes.position.count;
323
+
324
+ return `
325
+ def Mesh "${ name }"
326
+ {
327
+ int[] faceVertexCounts = [${ buildMeshVertexCount( geometry ) }]
328
+ int[] faceVertexIndices = [${ buildMeshVertexIndices( geometry ) }]
329
+ normal3f[] normals = [${ buildVector3Array( attributes.normal, count )}] (
330
+ interpolation = "vertex"
331
+ )
332
+ point3f[] points = [${ buildVector3Array( attributes.position, count )}]
333
+ ${ buildPrimvars( attributes ) }
334
+ uniform token subdivisionScheme = "none"
335
+ }
336
+ `;
337
+
338
+ }
339
+
340
+ function buildMeshVertexCount( geometry ) {
341
+
342
+ const count = geometry.index !== null ? geometry.index.count : geometry.attributes.position.count;
343
+
344
+ return Array( count / 3 ).fill( 3 ).join( ', ' );
345
+
346
+ }
347
+
348
+ function buildMeshVertexIndices( geometry ) {
349
+
350
+ const index = geometry.index;
351
+ const array = [];
352
+
353
+ if ( index !== null ) {
354
+
355
+ for ( let i = 0; i < index.count; i ++ ) {
356
+
357
+ array.push( index.getX( i ) );
358
+
359
+ }
360
+
361
+ } else {
362
+
363
+ const length = geometry.attributes.position.count;
364
+
365
+ for ( let i = 0; i < length; i ++ ) {
366
+
367
+ array.push( i );
368
+
369
+ }
370
+
371
+ }
372
+
373
+ return array.join( ', ' );
374
+
375
+ }
376
+
377
+ function buildVector3Array( attribute, count ) {
378
+
379
+ if ( attribute === undefined ) {
380
+
381
+ console.warn( 'USDZExporter: Normals missing.' );
382
+ return Array( count ).fill( '(0, 0, 0)' ).join( ', ' );
383
+
384
+ }
385
+
386
+ const array = [];
387
+
388
+ for ( let i = 0; i < attribute.count; i ++ ) {
389
+
390
+ const x = attribute.getX( i );
391
+ const y = attribute.getY( i );
392
+ const z = attribute.getZ( i );
393
+
394
+ array.push( `(${ x.toPrecision( PRECISION ) }, ${ y.toPrecision( PRECISION ) }, ${ z.toPrecision( PRECISION ) })` );
395
+
396
+ }
397
+
398
+ return array.join( ', ' );
399
+
400
+ }
401
+
402
+ function buildVector2Array( attribute ) {
403
+
404
+ const array = [];
405
+
406
+ for ( let i = 0; i < attribute.count; i ++ ) {
407
+
408
+ const x = attribute.getX( i );
409
+ const y = attribute.getY( i );
410
+
411
+ array.push( `(${ x.toPrecision( PRECISION ) }, ${ 1 - y.toPrecision( PRECISION ) })` );
412
+
413
+ }
414
+
415
+ return array.join( ', ' );
416
+
417
+ }
418
+
419
+ function buildPrimvars( attributes ) {
420
+
421
+ let string = '';
422
+
423
+ for ( let i = 0; i < 4; i ++ ) {
424
+
425
+ const id = ( i > 0 ? i : '' );
426
+ const attribute = attributes[ 'uv' + id ];
427
+
428
+ if ( attribute !== undefined ) {
429
+
430
+ string += `
431
+ texCoord2f[] primvars:st${ id } = [${ buildVector2Array( attribute )}] (
432
+ interpolation = "vertex"
433
+ )`;
434
+
435
+ }
436
+
437
+ }
438
+
439
+ // vertex colors
440
+
441
+ const colorAttribute = attributes.color;
442
+
443
+ if ( colorAttribute !== undefined ) {
444
+
445
+ const count = colorAttribute.count;
446
+
447
+ string += `
448
+ color3f[] primvars:displayColor = [${buildVector3Array( colorAttribute, count )}] (
449
+ interpolation = "vertex"
450
+ )`;
451
+
452
+ }
453
+
454
+ return string;
455
+
456
+ }
457
+
458
+ // Materials
459
+
460
+ function buildMaterials( materials, textures, quickLookCompatible = false ) {
461
+
462
+ const array = [];
463
+
464
+ for ( const uuid in materials ) {
465
+
466
+ const material = materials[ uuid ];
467
+
468
+ array.push( buildMaterial( material, textures, quickLookCompatible ) );
469
+
470
+ }
471
+
472
+ return `def "Materials"
473
+ {
474
+ ${ array.join( '' ) }
475
+ }
476
+
477
+ `;
478
+
479
+ }
480
+
481
+ function buildMaterial( material, textures, quickLookCompatible = false ) {
482
+
483
+ // https://graphics.pixar.com/usd/docs/UsdPreviewSurface-Proposal.html
484
+
485
+ const pad = ' ';
486
+ const inputs = [];
487
+ const samplers = [];
488
+
489
+ function buildTexture( texture, mapType, color ) {
490
+
491
+ const id = texture.source.id + '_' + texture.flipY;
492
+
493
+ textures[ id ] = texture;
494
+
495
+ const uv = texture.channel > 0 ? 'st' + texture.channel : 'st';
496
+
497
+ const WRAPPINGS = {
498
+ 1000: 'repeat', // RepeatWrapping
499
+ 1001: 'clamp', // ClampToEdgeWrapping
500
+ 1002: 'mirror' // MirroredRepeatWrapping
501
+ };
502
+
503
+ const repeat = texture.repeat.clone();
504
+ const offset = texture.offset.clone();
505
+ const rotation = texture.rotation;
506
+
507
+ // rotation is around the wrong point. after rotation we need to shift offset again so that we're rotating around the right spot
508
+ const xRotationOffset = Math.sin( rotation );
509
+ const yRotationOffset = Math.cos( rotation );
510
+
511
+ // texture coordinates start in the opposite corner, need to correct
512
+ offset.y = 1 - offset.y - repeat.y;
513
+
514
+ // turns out QuickLook is buggy and interprets texture repeat inverted/applies operations in a different order.
515
+ // Apple Feedback: FB10036297 and FB11442287
516
+ if ( quickLookCompatible ) {
517
+
518
+ // This is NOT correct yet in QuickLook, but comes close for a range of models.
519
+ // It becomes more incorrect the bigger the offset is
520
+
521
+ offset.x = offset.x / repeat.x;
522
+ offset.y = offset.y / repeat.y;
523
+
524
+ offset.x += xRotationOffset / repeat.x;
525
+ offset.y += yRotationOffset - 1;
526
+
527
+ } else {
528
+
529
+ // results match glTF results exactly. verified correct in usdview.
530
+ offset.x += xRotationOffset * repeat.x;
531
+ offset.y += ( 1 - yRotationOffset ) * repeat.y;
532
+
533
+ }
534
+
535
+ return `
536
+ def Shader "PrimvarReader_${ mapType }"
537
+ {
538
+ uniform token info:id = "UsdPrimvarReader_float2"
539
+ float2 inputs:fallback = (0.0, 0.0)
540
+ token inputs:varname = "${ uv }"
541
+ float2 outputs:result
542
+ }
543
+
544
+ def Shader "Transform2d_${ mapType }"
545
+ {
546
+ uniform token info:id = "UsdTransform2d"
547
+ token inputs:in.connect = </Materials/Material_${ material.id }/PrimvarReader_${ mapType }.outputs:result>
548
+ float inputs:rotation = ${ ( rotation * ( 180 / Math.PI ) ).toFixed( PRECISION ) }
549
+ float2 inputs:scale = ${ buildVector2( repeat ) }
550
+ float2 inputs:translation = ${ buildVector2( offset ) }
551
+ float2 outputs:result
552
+ }
553
+
554
+ def Shader "Texture_${ texture.id }_${ mapType }"
555
+ {
556
+ uniform token info:id = "UsdUVTexture"
557
+ asset inputs:file = @textures/Texture_${ id }.png@
558
+ float2 inputs:st.connect = </Materials/Material_${ material.id }/Transform2d_${ mapType }.outputs:result>
559
+ ${ color !== undefined ? 'float4 inputs:scale = ' + buildColor4( color ) : '' }
560
+ token inputs:sourceColorSpace = "${ texture.colorSpace === NoColorSpace ? 'raw' : 'sRGB' }"
561
+ token inputs:wrapS = "${ WRAPPINGS[ texture.wrapS ] }"
562
+ token inputs:wrapT = "${ WRAPPINGS[ texture.wrapT ] }"
563
+ float outputs:r
564
+ float outputs:g
565
+ float outputs:b
566
+ float3 outputs:rgb
567
+ ${ material.transparent || material.alphaTest > 0.0 ? 'float outputs:a' : '' }
568
+ }`;
569
+
570
+ }
571
+
572
+
573
+ if ( material.side === DoubleSide ) {
574
+
575
+ console.warn( 'THREE.USDZExporter: USDZ does not support double sided materials', material );
576
+
577
+ }
578
+
579
+ if ( material.map !== null ) {
580
+
581
+ inputs.push( `${ pad }color3f inputs:diffuseColor.connect = </Materials/Material_${ material.id }/Texture_${ material.map.id }_diffuse.outputs:rgb>` );
582
+
583
+ if ( material.transparent ) {
584
+
585
+ inputs.push( `${ pad }float inputs:opacity.connect = </Materials/Material_${ material.id }/Texture_${ material.map.id }_diffuse.outputs:a>` );
586
+
587
+ } else if ( material.alphaTest > 0.0 ) {
588
+
589
+ inputs.push( `${ pad }float inputs:opacity.connect = </Materials/Material_${ material.id }/Texture_${ material.map.id }_diffuse.outputs:a>` );
590
+ inputs.push( `${ pad }float inputs:opacityThreshold = ${material.alphaTest}` );
591
+
592
+ }
593
+
594
+ samplers.push( buildTexture( material.map, 'diffuse', material.color ) );
595
+
596
+ } else {
597
+
598
+ inputs.push( `${ pad }color3f inputs:diffuseColor = ${ buildColor( material.color ) }` );
599
+
600
+ }
601
+
602
+ if ( material.emissiveMap !== null ) {
603
+
604
+ inputs.push( `${ pad }color3f inputs:emissiveColor.connect = </Materials/Material_${ material.id }/Texture_${ material.emissiveMap.id }_emissive.outputs:rgb>` );
605
+
606
+ samplers.push( buildTexture( material.emissiveMap, 'emissive', new Color( material.emissive.r * material.emissiveIntensity, material.emissive.g * material.emissiveIntensity, material.emissive.b * material.emissiveIntensity ) ) );
607
+
608
+ } else if ( material.emissive.getHex() > 0 ) {
609
+
610
+ inputs.push( `${ pad }color3f inputs:emissiveColor = ${ buildColor( material.emissive ) }` );
611
+
612
+ }
613
+
614
+ if ( material.normalMap !== null ) {
615
+
616
+ inputs.push( `${ pad }normal3f inputs:normal.connect = </Materials/Material_${ material.id }/Texture_${ material.normalMap.id }_normal.outputs:rgb>` );
617
+
618
+ samplers.push( buildTexture( material.normalMap, 'normal' ) );
619
+
620
+ }
621
+
622
+ if ( material.aoMap !== null ) {
623
+
624
+ inputs.push( `${ pad }float inputs:occlusion.connect = </Materials/Material_${ material.id }/Texture_${ material.aoMap.id }_occlusion.outputs:r>` );
625
+
626
+ samplers.push( buildTexture( material.aoMap, 'occlusion', new Color( material.aoMapIntensity, material.aoMapIntensity, material.aoMapIntensity ) ) );
627
+
628
+ }
629
+
630
+ if ( material.roughnessMap !== null ) {
631
+
632
+ inputs.push( `${ pad }float inputs:roughness.connect = </Materials/Material_${ material.id }/Texture_${ material.roughnessMap.id }_roughness.outputs:g>` );
633
+
634
+ samplers.push( buildTexture( material.roughnessMap, 'roughness', new Color( material.roughness, material.roughness, material.roughness ) ) );
635
+
636
+ } else {
637
+
638
+ inputs.push( `${ pad }float inputs:roughness = ${ material.roughness }` );
639
+
640
+ }
641
+
642
+ if ( material.metalnessMap !== null ) {
643
+
644
+ inputs.push( `${ pad }float inputs:metallic.connect = </Materials/Material_${ material.id }/Texture_${ material.metalnessMap.id }_metallic.outputs:b>` );
645
+
646
+ samplers.push( buildTexture( material.metalnessMap, 'metallic', new Color( material.metalness, material.metalness, material.metalness ) ) );
647
+
648
+ } else {
649
+
650
+ inputs.push( `${ pad }float inputs:metallic = ${ material.metalness }` );
651
+
652
+ }
653
+
654
+ if ( material.alphaMap !== null ) {
655
+
656
+ inputs.push( `${pad}float inputs:opacity.connect = </Materials/Material_${material.id}/Texture_${material.alphaMap.id}_opacity.outputs:r>` );
657
+ inputs.push( `${pad}float inputs:opacityThreshold = 0.0001` );
658
+
659
+ samplers.push( buildTexture( material.alphaMap, 'opacity' ) );
660
+
661
+ } else {
662
+
663
+ inputs.push( `${pad}float inputs:opacity = ${material.opacity}` );
664
+
665
+ }
666
+
667
+ if ( material.isMeshPhysicalMaterial ) {
668
+
669
+ if ( material.clearcoatMap !== null ) {
670
+
671
+ inputs.push( `${pad}float inputs:clearcoat.connect = </Materials/Material_${material.id}/Texture_${material.clearcoatMap.id}_clearcoat.outputs:r>` );
672
+ samplers.push( buildTexture( material.clearcoatMap, 'clearcoat', new Color( material.clearcoat, material.clearcoat, material.clearcoat ) ) );
673
+
674
+ } else {
675
+
676
+ inputs.push( `${pad}float inputs:clearcoat = ${material.clearcoat}` );
677
+
678
+ }
679
+
680
+ if ( material.clearcoatRoughnessMap !== null ) {
681
+
682
+ inputs.push( `${pad}float inputs:clearcoatRoughness.connect = </Materials/Material_${material.id}/Texture_${material.clearcoatRoughnessMap.id}_clearcoatRoughness.outputs:g>` );
683
+ samplers.push( buildTexture( material.clearcoatRoughnessMap, 'clearcoatRoughness', new Color( material.clearcoatRoughness, material.clearcoatRoughness, material.clearcoatRoughness ) ) );
684
+
685
+ } else {
686
+
687
+ inputs.push( `${pad}float inputs:clearcoatRoughness = ${material.clearcoatRoughness}` );
688
+
689
+ }
690
+
691
+ inputs.push( `${ pad }float inputs:ior = ${ material.ior }` );
692
+
693
+ }
694
+
695
+ return `
696
+ def Material "Material_${ material.id }"
697
+ {
698
+ def Shader "PreviewSurface"
699
+ {
700
+ uniform token info:id = "UsdPreviewSurface"
701
+ ${ inputs.join( '\n' ) }
702
+ int inputs:useSpecularWorkflow = 0
703
+ token outputs:surface
704
+ }
705
+
706
+ token outputs:surface.connect = </Materials/Material_${ material.id }/PreviewSurface.outputs:surface>
707
+
708
+ ${ samplers.join( '\n' ) }
709
+
710
+ }
711
+ `;
712
+
713
+ }
714
+
715
+ function buildColor( color ) {
716
+
717
+ return `(${ color.r }, ${ color.g }, ${ color.b })`;
718
+
719
+ }
720
+
721
+ function buildColor4( color ) {
722
+
723
+ return `(${ color.r }, ${ color.g }, ${ color.b }, 1.0)`;
724
+
725
+ }
726
+
727
+ function buildVector2( vector ) {
728
+
729
+ return `(${ vector.x }, ${ vector.y })`;
730
+
731
+ }
732
+
733
+
734
+ function buildCamera( camera ) {
735
+
736
+ const name = camera.name ? camera.name : 'Camera_' + camera.id;
737
+
738
+ const transform = buildMatrix( camera.matrixWorld );
739
+
740
+ if ( camera.matrixWorld.determinant() < 0 ) {
741
+
742
+ console.warn( 'THREE.USDZExporter: USDZ does not support negative scales', camera );
743
+
744
+ }
745
+
746
+ if ( camera.isOrthographicCamera ) {
747
+
748
+ return `def Camera "${name}"
749
+ {
750
+ matrix4d xformOp:transform = ${ transform }
751
+ uniform token[] xformOpOrder = ["xformOp:transform"]
752
+
753
+ float2 clippingRange = (${ camera.near.toPrecision( PRECISION ) }, ${ camera.far.toPrecision( PRECISION ) })
754
+ float horizontalAperture = ${ ( ( Math.abs( camera.left ) + Math.abs( camera.right ) ) * 10 ).toPrecision( PRECISION ) }
755
+ float verticalAperture = ${ ( ( Math.abs( camera.top ) + Math.abs( camera.bottom ) ) * 10 ).toPrecision( PRECISION ) }
756
+ token projection = "orthographic"
757
+ }
758
+
759
+ `;
760
+
761
+ } else {
762
+
763
+ return `def Camera "${name}"
764
+ {
765
+ matrix4d xformOp:transform = ${ transform }
766
+ uniform token[] xformOpOrder = ["xformOp:transform"]
767
+
768
+ float2 clippingRange = (${ camera.near.toPrecision( PRECISION ) }, ${ camera.far.toPrecision( PRECISION ) })
769
+ float focalLength = ${ camera.getFocalLength().toPrecision( PRECISION ) }
770
+ float focusDistance = ${ camera.focus.toPrecision( PRECISION ) }
771
+ float horizontalAperture = ${ camera.getFilmWidth().toPrecision( PRECISION ) }
772
+ token projection = "perspective"
773
+ float verticalAperture = ${ camera.getFilmHeight().toPrecision( PRECISION ) }
774
+ }
775
+
776
+ `;
777
+
778
+ }
779
+
780
+ }
781
+
782
+ export { USDZExporter };
libs/three.js/0.172.0/jsm/geometries/BoxLineGeometry.js ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ BufferGeometry,
3
+ Float32BufferAttribute
4
+ } from 'three';
5
+
6
+ class BoxLineGeometry extends BufferGeometry {
7
+
8
+ constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) {
9
+
10
+ super();
11
+
12
+ widthSegments = Math.floor( widthSegments );
13
+ heightSegments = Math.floor( heightSegments );
14
+ depthSegments = Math.floor( depthSegments );
15
+
16
+ const widthHalf = width / 2;
17
+ const heightHalf = height / 2;
18
+ const depthHalf = depth / 2;
19
+
20
+ const segmentWidth = width / widthSegments;
21
+ const segmentHeight = height / heightSegments;
22
+ const segmentDepth = depth / depthSegments;
23
+
24
+ const vertices = [];
25
+
26
+ let x = - widthHalf;
27
+ let y = - heightHalf;
28
+ let z = - depthHalf;
29
+
30
+ for ( let i = 0; i <= widthSegments; i ++ ) {
31
+
32
+ vertices.push( x, - heightHalf, - depthHalf, x, heightHalf, - depthHalf );
33
+ vertices.push( x, heightHalf, - depthHalf, x, heightHalf, depthHalf );
34
+ vertices.push( x, heightHalf, depthHalf, x, - heightHalf, depthHalf );
35
+ vertices.push( x, - heightHalf, depthHalf, x, - heightHalf, - depthHalf );
36
+
37
+ x += segmentWidth;
38
+
39
+ }
40
+
41
+ for ( let i = 0; i <= heightSegments; i ++ ) {
42
+
43
+ vertices.push( - widthHalf, y, - depthHalf, widthHalf, y, - depthHalf );
44
+ vertices.push( widthHalf, y, - depthHalf, widthHalf, y, depthHalf );
45
+ vertices.push( widthHalf, y, depthHalf, - widthHalf, y, depthHalf );
46
+ vertices.push( - widthHalf, y, depthHalf, - widthHalf, y, - depthHalf );
47
+
48
+ y += segmentHeight;
49
+
50
+ }
51
+
52
+ for ( let i = 0; i <= depthSegments; i ++ ) {
53
+
54
+ vertices.push( - widthHalf, - heightHalf, z, - widthHalf, heightHalf, z );
55
+ vertices.push( - widthHalf, heightHalf, z, widthHalf, heightHalf, z );
56
+ vertices.push( widthHalf, heightHalf, z, widthHalf, - heightHalf, z );
57
+ vertices.push( widthHalf, - heightHalf, z, - widthHalf, - heightHalf, z );
58
+
59
+ z += segmentDepth;
60
+
61
+ }
62
+
63
+ this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
64
+
65
+ }
66
+
67
+ }
68
+
69
+ export { BoxLineGeometry };
libs/three.js/0.172.0/jsm/geometries/ConvexGeometry.js ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ BufferGeometry,
3
+ Float32BufferAttribute
4
+ } from 'three';
5
+ import { ConvexHull } from '../math/ConvexHull.js';
6
+
7
+ class ConvexGeometry extends BufferGeometry {
8
+
9
+ constructor( points = [] ) {
10
+
11
+ super();
12
+
13
+ // buffers
14
+
15
+ const vertices = [];
16
+ const normals = [];
17
+
18
+ const convexHull = new ConvexHull().setFromPoints( points );
19
+
20
+ // generate vertices and normals
21
+
22
+ const faces = convexHull.faces;
23
+
24
+ for ( let i = 0; i < faces.length; i ++ ) {
25
+
26
+ const face = faces[ i ];
27
+ let edge = face.edge;
28
+
29
+ // we move along a doubly-connected edge list to access all face points (see HalfEdge docs)
30
+
31
+ do {
32
+
33
+ const point = edge.head().point;
34
+
35
+ vertices.push( point.x, point.y, point.z );
36
+ normals.push( face.normal.x, face.normal.y, face.normal.z );
37
+
38
+ edge = edge.next;
39
+
40
+ } while ( edge !== face.edge );
41
+
42
+ }
43
+
44
+ // build geometry
45
+
46
+ this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
47
+ this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
48
+
49
+ }
50
+
51
+ }
52
+
53
+ export { ConvexGeometry };
libs/three.js/0.172.0/jsm/geometries/DecalGeometry.js ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ BufferGeometry,
3
+ Euler,
4
+ Float32BufferAttribute,
5
+ Matrix3,
6
+ Matrix4,
7
+ Mesh,
8
+ Vector3
9
+ } from 'three';
10
+
11
+ /**
12
+ * You can use this geometry to create a decal mesh, that serves different kinds of purposes.
13
+ * e.g. adding unique details to models, performing dynamic visual environmental changes or covering seams.
14
+ *
15
+ * Constructor parameter:
16
+ *
17
+ * mesh — Any mesh object
18
+ * position — Position of the decal projector
19
+ * orientation — Orientation of the decal projector
20
+ * size — Size of the decal projector
21
+ *
22
+ * reference: http://blog.wolfire.com/2009/06/how-to-project-decals/
23
+ *
24
+ */
25
+
26
+ class DecalGeometry extends BufferGeometry {
27
+
28
+ constructor( mesh = new Mesh(), position = new Vector3(), orientation = new Euler(), size = new Vector3( 1, 1, 1 ) ) {
29
+
30
+ super();
31
+
32
+ // buffers
33
+
34
+ const vertices = [];
35
+ const normals = [];
36
+ const uvs = [];
37
+
38
+ // helpers
39
+
40
+ const plane = new Vector3();
41
+
42
+ const normalMatrix = new Matrix3().getNormalMatrix( mesh.matrixWorld );
43
+
44
+ // this matrix represents the transformation of the decal projector
45
+
46
+ const projectorMatrix = new Matrix4();
47
+ projectorMatrix.makeRotationFromEuler( orientation );
48
+ projectorMatrix.setPosition( position );
49
+
50
+ const projectorMatrixInverse = new Matrix4();
51
+ projectorMatrixInverse.copy( projectorMatrix ).invert();
52
+
53
+ // generate buffers
54
+
55
+ generate();
56
+
57
+ // build geometry
58
+
59
+ this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
60
+ this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
61
+
62
+ if ( normals.length > 0 ) {
63
+
64
+ this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
65
+
66
+ }
67
+
68
+ //
69
+
70
+ function generate() {
71
+
72
+ let decalVertices = [];
73
+
74
+ const vertex = new Vector3();
75
+ const normal = new Vector3();
76
+
77
+ // handle different geometry types
78
+
79
+ const geometry = mesh.geometry;
80
+
81
+ const positionAttribute = geometry.attributes.position;
82
+ const normalAttribute = geometry.attributes.normal;
83
+
84
+ // first, create an array of 'DecalVertex' objects
85
+ // three consecutive 'DecalVertex' objects represent a single face
86
+ //
87
+ // this data structure will be later used to perform the clipping
88
+
89
+ if ( geometry.index !== null ) {
90
+
91
+ // indexed BufferGeometry
92
+
93
+ const index = geometry.index;
94
+
95
+ for ( let i = 0; i < index.count; i ++ ) {
96
+
97
+ vertex.fromBufferAttribute( positionAttribute, index.getX( i ) );
98
+
99
+ if ( normalAttribute ) {
100
+
101
+ normal.fromBufferAttribute( normalAttribute, index.getX( i ) );
102
+ pushDecalVertex( decalVertices, vertex, normal );
103
+
104
+ } else {
105
+
106
+ pushDecalVertex( decalVertices, vertex );
107
+
108
+ }
109
+
110
+ }
111
+
112
+ } else {
113
+
114
+ if ( positionAttribute === undefined ) return; // empty geometry
115
+
116
+ // non-indexed BufferGeometry
117
+
118
+ for ( let i = 0; i < positionAttribute.count; i ++ ) {
119
+
120
+ vertex.fromBufferAttribute( positionAttribute, i );
121
+
122
+ if ( normalAttribute ) {
123
+
124
+ normal.fromBufferAttribute( normalAttribute, i );
125
+ pushDecalVertex( decalVertices, vertex, normal );
126
+
127
+ } else {
128
+
129
+ pushDecalVertex( decalVertices, vertex );
130
+
131
+ }
132
+
133
+ }
134
+
135
+ }
136
+
137
+ // second, clip the geometry so that it doesn't extend out from the projector
138
+
139
+ decalVertices = clipGeometry( decalVertices, plane.set( 1, 0, 0 ) );
140
+ decalVertices = clipGeometry( decalVertices, plane.set( - 1, 0, 0 ) );
141
+ decalVertices = clipGeometry( decalVertices, plane.set( 0, 1, 0 ) );
142
+ decalVertices = clipGeometry( decalVertices, plane.set( 0, - 1, 0 ) );
143
+ decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, 1 ) );
144
+ decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, - 1 ) );
145
+
146
+ // third, generate final vertices, normals and uvs
147
+
148
+ for ( let i = 0; i < decalVertices.length; i ++ ) {
149
+
150
+ const decalVertex = decalVertices[ i ];
151
+
152
+ // create texture coordinates (we are still in projector space)
153
+
154
+ uvs.push(
155
+ 0.5 + ( decalVertex.position.x / size.x ),
156
+ 0.5 + ( decalVertex.position.y / size.y )
157
+ );
158
+
159
+ // transform the vertex back to world space
160
+
161
+ decalVertex.position.applyMatrix4( projectorMatrix );
162
+
163
+ // now create vertex and normal buffer data
164
+
165
+ vertices.push( decalVertex.position.x, decalVertex.position.y, decalVertex.position.z );
166
+
167
+ if ( decalVertex.normal !== null ) {
168
+
169
+ normals.push( decalVertex.normal.x, decalVertex.normal.y, decalVertex.normal.z );
170
+
171
+ }
172
+
173
+ }
174
+
175
+ }
176
+
177
+ function pushDecalVertex( decalVertices, vertex, normal = null ) {
178
+
179
+ // transform the vertex to world space, then to projector space
180
+
181
+ vertex.applyMatrix4( mesh.matrixWorld );
182
+ vertex.applyMatrix4( projectorMatrixInverse );
183
+
184
+ if ( normal ) {
185
+
186
+ normal.applyNormalMatrix( normalMatrix );
187
+ decalVertices.push( new DecalVertex( vertex.clone(), normal.clone() ) );
188
+
189
+ } else {
190
+
191
+ decalVertices.push( new DecalVertex( vertex.clone() ) );
192
+
193
+ }
194
+
195
+ }
196
+
197
+ function clipGeometry( inVertices, plane ) {
198
+
199
+ const outVertices = [];
200
+
201
+ const s = 0.5 * Math.abs( size.dot( plane ) );
202
+
203
+ // a single iteration clips one face,
204
+ // which consists of three consecutive 'DecalVertex' objects
205
+
206
+ for ( let i = 0; i < inVertices.length; i += 3 ) {
207
+
208
+ let total = 0;
209
+ let nV1;
210
+ let nV2;
211
+ let nV3;
212
+ let nV4;
213
+
214
+ const d1 = inVertices[ i + 0 ].position.dot( plane ) - s;
215
+ const d2 = inVertices[ i + 1 ].position.dot( plane ) - s;
216
+ const d3 = inVertices[ i + 2 ].position.dot( plane ) - s;
217
+
218
+ const v1Out = d1 > 0;
219
+ const v2Out = d2 > 0;
220
+ const v3Out = d3 > 0;
221
+
222
+ // calculate, how many vertices of the face lie outside of the clipping plane
223
+
224
+ total = ( v1Out ? 1 : 0 ) + ( v2Out ? 1 : 0 ) + ( v3Out ? 1 : 0 );
225
+
226
+ switch ( total ) {
227
+
228
+ case 0: {
229
+
230
+ // the entire face lies inside of the plane, no clipping needed
231
+
232
+ outVertices.push( inVertices[ i ] );
233
+ outVertices.push( inVertices[ i + 1 ] );
234
+ outVertices.push( inVertices[ i + 2 ] );
235
+ break;
236
+
237
+ }
238
+
239
+ case 1: {
240
+
241
+ // one vertex lies outside of the plane, perform clipping
242
+
243
+ if ( v1Out ) {
244
+
245
+ nV1 = inVertices[ i + 1 ];
246
+ nV2 = inVertices[ i + 2 ];
247
+ nV3 = clip( inVertices[ i ], nV1, plane, s );
248
+ nV4 = clip( inVertices[ i ], nV2, plane, s );
249
+
250
+ }
251
+
252
+ if ( v2Out ) {
253
+
254
+ nV1 = inVertices[ i ];
255
+ nV2 = inVertices[ i + 2 ];
256
+ nV3 = clip( inVertices[ i + 1 ], nV1, plane, s );
257
+ nV4 = clip( inVertices[ i + 1 ], nV2, plane, s );
258
+
259
+ outVertices.push( nV3 );
260
+ outVertices.push( nV2.clone() );
261
+ outVertices.push( nV1.clone() );
262
+
263
+ outVertices.push( nV2.clone() );
264
+ outVertices.push( nV3.clone() );
265
+ outVertices.push( nV4 );
266
+ break;
267
+
268
+ }
269
+
270
+ if ( v3Out ) {
271
+
272
+ nV1 = inVertices[ i ];
273
+ nV2 = inVertices[ i + 1 ];
274
+ nV3 = clip( inVertices[ i + 2 ], nV1, plane, s );
275
+ nV4 = clip( inVertices[ i + 2 ], nV2, plane, s );
276
+
277
+ }
278
+
279
+ outVertices.push( nV1.clone() );
280
+ outVertices.push( nV2.clone() );
281
+ outVertices.push( nV3 );
282
+
283
+ outVertices.push( nV4 );
284
+ outVertices.push( nV3.clone() );
285
+ outVertices.push( nV2.clone() );
286
+
287
+ break;
288
+
289
+ }
290
+
291
+ case 2: {
292
+
293
+ // two vertices lies outside of the plane, perform clipping
294
+
295
+ if ( ! v1Out ) {
296
+
297
+ nV1 = inVertices[ i ].clone();
298
+ nV2 = clip( nV1, inVertices[ i + 1 ], plane, s );
299
+ nV3 = clip( nV1, inVertices[ i + 2 ], plane, s );
300
+ outVertices.push( nV1 );
301
+ outVertices.push( nV2 );
302
+ outVertices.push( nV3 );
303
+
304
+ }
305
+
306
+ if ( ! v2Out ) {
307
+
308
+ nV1 = inVertices[ i + 1 ].clone();
309
+ nV2 = clip( nV1, inVertices[ i + 2 ], plane, s );
310
+ nV3 = clip( nV1, inVertices[ i ], plane, s );
311
+ outVertices.push( nV1 );
312
+ outVertices.push( nV2 );
313
+ outVertices.push( nV3 );
314
+
315
+ }
316
+
317
+ if ( ! v3Out ) {
318
+
319
+ nV1 = inVertices[ i + 2 ].clone();
320
+ nV2 = clip( nV1, inVertices[ i ], plane, s );
321
+ nV3 = clip( nV1, inVertices[ i + 1 ], plane, s );
322
+ outVertices.push( nV1 );
323
+ outVertices.push( nV2 );
324
+ outVertices.push( nV3 );
325
+
326
+ }
327
+
328
+ break;
329
+
330
+ }
331
+
332
+ case 3: {
333
+
334
+ // the entire face lies outside of the plane, so let's discard the corresponding vertices
335
+
336
+ break;
337
+
338
+ }
339
+
340
+ }
341
+
342
+ }
343
+
344
+ return outVertices;
345
+
346
+ }
347
+
348
+ function clip( v0, v1, p, s ) {
349
+
350
+ const d0 = v0.position.dot( p ) - s;
351
+ const d1 = v1.position.dot( p ) - s;
352
+
353
+ const s0 = d0 / ( d0 - d1 );
354
+
355
+ const position = new Vector3(
356
+ v0.position.x + s0 * ( v1.position.x - v0.position.x ),
357
+ v0.position.y + s0 * ( v1.position.y - v0.position.y ),
358
+ v0.position.z + s0 * ( v1.position.z - v0.position.z )
359
+ );
360
+
361
+ let normal = null;
362
+
363
+ if ( v0.normal !== null && v1.normal !== null ) {
364
+
365
+ normal = new Vector3(
366
+ v0.normal.x + s0 * ( v1.normal.x - v0.normal.x ),
367
+ v0.normal.y + s0 * ( v1.normal.y - v0.normal.y ),
368
+ v0.normal.z + s0 * ( v1.normal.z - v0.normal.z )
369
+ );
370
+
371
+ }
372
+
373
+ const v = new DecalVertex( position, normal );
374
+
375
+ // need to clip more values (texture coordinates)? do it this way:
376
+ // intersectpoint.value = a.value + s * ( b.value - a.value );
377
+
378
+ return v;
379
+
380
+ }
381
+
382
+ }
383
+
384
+ }
385
+
386
+ // helper
387
+
388
+ class DecalVertex {
389
+
390
+ constructor( position, normal = null ) {
391
+
392
+ this.position = position;
393
+ this.normal = normal;
394
+
395
+ }
396
+
397
+ clone() {
398
+
399
+ const position = this.position.clone();
400
+ const normal = ( this.normal !== null ) ? this.normal.clone() : null;
401
+
402
+ return new this.constructor( position, normal );
403
+
404
+ }
405
+
406
+ }
407
+
408
+ export { DecalGeometry, DecalVertex };
libs/three.js/0.172.0/jsm/geometries/ParametricGeometries.js ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Curve,
3
+ Vector3
4
+ } from 'three';
5
+
6
+ import { ParametricGeometry } from './ParametricGeometry.js';
7
+
8
+ /**
9
+ * Experimenting of primitive geometry creation using Surface Parametric equations
10
+ */
11
+
12
+ const ParametricGeometries = {
13
+
14
+ klein: function ( v, u, target ) {
15
+
16
+ u *= Math.PI;
17
+ v *= 2 * Math.PI;
18
+
19
+ u = u * 2;
20
+ let x, z;
21
+ if ( u < Math.PI ) {
22
+
23
+ x = 3 * Math.cos( u ) * ( 1 + Math.sin( u ) ) + ( 2 * ( 1 - Math.cos( u ) / 2 ) ) * Math.cos( u ) * Math.cos( v );
24
+ z = - 8 * Math.sin( u ) - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( u ) * Math.cos( v );
25
+
26
+ } else {
27
+
28
+ x = 3 * Math.cos( u ) * ( 1 + Math.sin( u ) ) + ( 2 * ( 1 - Math.cos( u ) / 2 ) ) * Math.cos( v + Math.PI );
29
+ z = - 8 * Math.sin( u );
30
+
31
+ }
32
+
33
+ const y = - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( v );
34
+
35
+ target.set( x, y, z );
36
+
37
+ },
38
+
39
+ plane: function ( width, height ) {
40
+
41
+ return function ( u, v, target ) {
42
+
43
+ const x = u * width;
44
+ const y = 0;
45
+ const z = v * height;
46
+
47
+ target.set( x, y, z );
48
+
49
+ };
50
+
51
+ },
52
+
53
+ mobius: function ( u, t, target ) {
54
+
55
+ // flat mobius strip
56
+ // http://www.wolframalpha.com/input/?i=M%C3%B6bius+strip+parametric+equations&lk=1&a=ClashPrefs_*Surface.MoebiusStrip.SurfaceProperty.ParametricEquations-
57
+ u = u - 0.5;
58
+ const v = 2 * Math.PI * t;
59
+
60
+ const a = 2;
61
+
62
+ const x = Math.cos( v ) * ( a + u * Math.cos( v / 2 ) );
63
+ const y = Math.sin( v ) * ( a + u * Math.cos( v / 2 ) );
64
+ const z = u * Math.sin( v / 2 );
65
+
66
+ target.set( x, y, z );
67
+
68
+ },
69
+
70
+ mobius3d: function ( u, t, target ) {
71
+
72
+ // volumetric mobius strip
73
+
74
+ u *= Math.PI;
75
+ t *= 2 * Math.PI;
76
+
77
+ u = u * 2;
78
+ const phi = u / 2;
79
+ const major = 2.25, a = 0.125, b = 0.65;
80
+
81
+ let x = a * Math.cos( t ) * Math.cos( phi ) - b * Math.sin( t ) * Math.sin( phi );
82
+ const z = a * Math.cos( t ) * Math.sin( phi ) + b * Math.sin( t ) * Math.cos( phi );
83
+ const y = ( major + x ) * Math.sin( u );
84
+ x = ( major + x ) * Math.cos( u );
85
+
86
+ target.set( x, y, z );
87
+
88
+ }
89
+
90
+ };
91
+
92
+
93
+ /*********************************************
94
+ *
95
+ * Parametric Replacement for TubeGeometry
96
+ *
97
+ *********************************************/
98
+
99
+ ParametricGeometries.TubeGeometry = class TubeGeometry extends ParametricGeometry {
100
+
101
+ constructor( path, segments = 64, radius = 1, segmentsRadius = 8, closed = false ) {
102
+
103
+ const numpoints = segments + 1;
104
+
105
+ const frames = path.computeFrenetFrames( segments, closed ),
106
+ tangents = frames.tangents,
107
+ normals = frames.normals,
108
+ binormals = frames.binormals;
109
+
110
+ const position = new Vector3();
111
+
112
+ function ParametricTube( u, v, target ) {
113
+
114
+ v *= 2 * Math.PI;
115
+
116
+ const i = Math.floor( u * ( numpoints - 1 ) );
117
+
118
+ path.getPointAt( u, position );
119
+
120
+ const normal = normals[ i ];
121
+ const binormal = binormals[ i ];
122
+
123
+ const cx = - radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
124
+ const cy = radius * Math.sin( v );
125
+
126
+ position.x += cx * normal.x + cy * binormal.x;
127
+ position.y += cx * normal.y + cy * binormal.y;
128
+ position.z += cx * normal.z + cy * binormal.z;
129
+
130
+ target.copy( position );
131
+
132
+ }
133
+
134
+ super( ParametricTube, segments, segmentsRadius );
135
+
136
+ // proxy internals
137
+
138
+ this.tangents = tangents;
139
+ this.normals = normals;
140
+ this.binormals = binormals;
141
+
142
+ this.path = path;
143
+ this.segments = segments;
144
+ this.radius = radius;
145
+ this.segmentsRadius = segmentsRadius;
146
+ this.closed = closed;
147
+
148
+ }
149
+
150
+ };
151
+
152
+
153
+ /*********************************************
154
+ *
155
+ * Parametric Replacement for TorusKnotGeometry
156
+ *
157
+ *********************************************/
158
+ ParametricGeometries.TorusKnotGeometry = class TorusKnotGeometry extends ParametricGeometries.TubeGeometry {
159
+
160
+ constructor( radius = 200, tube = 40, segmentsT = 64, segmentsR = 8, p = 2, q = 3 ) {
161
+
162
+ class TorusKnotCurve extends Curve {
163
+
164
+ getPoint( t, optionalTarget = new Vector3() ) {
165
+
166
+ const point = optionalTarget;
167
+
168
+ t *= Math.PI * 2;
169
+
170
+ const r = 0.5;
171
+
172
+ const x = ( 1 + r * Math.cos( q * t ) ) * Math.cos( p * t );
173
+ const y = ( 1 + r * Math.cos( q * t ) ) * Math.sin( p * t );
174
+ const z = r * Math.sin( q * t );
175
+
176
+ return point.set( x, y, z ).multiplyScalar( radius );
177
+
178
+ }
179
+
180
+ }
181
+
182
+ const segments = segmentsT;
183
+ const radiusSegments = segmentsR;
184
+ const extrudePath = new TorusKnotCurve();
185
+
186
+ super( extrudePath, segments, tube, radiusSegments, true, false );
187
+
188
+ this.radius = radius;
189
+ this.tube = tube;
190
+ this.segmentsT = segmentsT;
191
+ this.segmentsR = segmentsR;
192
+ this.p = p;
193
+ this.q = q;
194
+
195
+ }
196
+
197
+ };
198
+
199
+ /*********************************************
200
+ *
201
+ * Parametric Replacement for SphereGeometry
202
+ *
203
+ *********************************************/
204
+ ParametricGeometries.SphereGeometry = class SphereGeometry extends ParametricGeometry {
205
+
206
+ constructor( size, u, v ) {
207
+
208
+ function sphere( u, v, target ) {
209
+
210
+ u *= Math.PI;
211
+ v *= 2 * Math.PI;
212
+
213
+ const x = size * Math.sin( u ) * Math.cos( v );
214
+ const y = size * Math.sin( u ) * Math.sin( v );
215
+ const z = size * Math.cos( u );
216
+
217
+ target.set( x, y, z );
218
+
219
+ }
220
+
221
+ super( sphere, u, v );
222
+
223
+ }
224
+
225
+ };
226
+
227
+
228
+ /*********************************************
229
+ *
230
+ * Parametric Replacement for PlaneGeometry
231
+ *
232
+ *********************************************/
233
+
234
+ ParametricGeometries.PlaneGeometry = class PlaneGeometry extends ParametricGeometry {
235
+
236
+ constructor( width, depth, segmentsWidth, segmentsDepth ) {
237
+
238
+ function plane( u, v, target ) {
239
+
240
+ const x = u * width;
241
+ const y = 0;
242
+ const z = v * depth;
243
+
244
+ target.set( x, y, z );
245
+
246
+ }
247
+
248
+ super( plane, segmentsWidth, segmentsDepth );
249
+
250
+ }
251
+
252
+ };
253
+
254
+ export { ParametricGeometries };
libs/three.js/0.172.0/jsm/geometries/ParametricGeometry.js ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Parametric Surfaces Geometry
3
+ * based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html
4
+ */
5
+
6
+ import {
7
+ BufferGeometry,
8
+ Float32BufferAttribute,
9
+ Vector3
10
+ } from 'three';
11
+
12
+ class ParametricGeometry extends BufferGeometry {
13
+
14
+ constructor( func = ( u, v, target ) => target.set( u, v, Math.cos( u ) * Math.sin( v ) ), slices = 8, stacks = 8 ) {
15
+
16
+ super();
17
+
18
+ this.type = 'ParametricGeometry';
19
+
20
+ this.parameters = {
21
+ func: func,
22
+ slices: slices,
23
+ stacks: stacks
24
+ };
25
+
26
+ // buffers
27
+
28
+ const indices = [];
29
+ const vertices = [];
30
+ const normals = [];
31
+ const uvs = [];
32
+
33
+ const EPS = 0.00001;
34
+
35
+ const normal = new Vector3();
36
+
37
+ const p0 = new Vector3(), p1 = new Vector3();
38
+ const pu = new Vector3(), pv = new Vector3();
39
+
40
+ // generate vertices, normals and uvs
41
+
42
+ const sliceCount = slices + 1;
43
+
44
+ for ( let i = 0; i <= stacks; i ++ ) {
45
+
46
+ const v = i / stacks;
47
+
48
+ for ( let j = 0; j <= slices; j ++ ) {
49
+
50
+ const u = j / slices;
51
+
52
+ // vertex
53
+
54
+ func( u, v, p0 );
55
+ vertices.push( p0.x, p0.y, p0.z );
56
+
57
+ // normal
58
+
59
+ // approximate tangent vectors via finite differences
60
+
61
+ if ( u - EPS >= 0 ) {
62
+
63
+ func( u - EPS, v, p1 );
64
+ pu.subVectors( p0, p1 );
65
+
66
+ } else {
67
+
68
+ func( u + EPS, v, p1 );
69
+ pu.subVectors( p1, p0 );
70
+
71
+ }
72
+
73
+ if ( v - EPS >= 0 ) {
74
+
75
+ func( u, v - EPS, p1 );
76
+ pv.subVectors( p0, p1 );
77
+
78
+ } else {
79
+
80
+ func( u, v + EPS, p1 );
81
+ pv.subVectors( p1, p0 );
82
+
83
+ }
84
+
85
+ // cross product of tangent vectors returns surface normal
86
+
87
+ normal.crossVectors( pu, pv ).normalize();
88
+ normals.push( normal.x, normal.y, normal.z );
89
+
90
+ // uv
91
+
92
+ uvs.push( u, v );
93
+
94
+ }
95
+
96
+ }
97
+
98
+ // generate indices
99
+
100
+ for ( let i = 0; i < stacks; i ++ ) {
101
+
102
+ for ( let j = 0; j < slices; j ++ ) {
103
+
104
+ const a = i * sliceCount + j;
105
+ const b = i * sliceCount + j + 1;
106
+ const c = ( i + 1 ) * sliceCount + j + 1;
107
+ const d = ( i + 1 ) * sliceCount + j;
108
+
109
+ // faces one and two
110
+
111
+ indices.push( a, b, d );
112
+ indices.push( b, c, d );
113
+
114
+ }
115
+
116
+ }
117
+
118
+ // build geometry
119
+
120
+ this.setIndex( indices );
121
+ this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
122
+ this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
123
+ this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
124
+
125
+ }
126
+
127
+ copy( source ) {
128
+
129
+ super.copy( source );
130
+
131
+ this.parameters = Object.assign( {}, source.parameters );
132
+
133
+ return this;
134
+
135
+ }
136
+
137
+ }
138
+
139
+ export { ParametricGeometry };
libs/three.js/0.172.0/jsm/geometries/RoundedBoxGeometry.js ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ BoxGeometry,
3
+ Vector3
4
+ } from 'three';
5
+
6
+ const _tempNormal = new Vector3();
7
+
8
+ function getUv( faceDirVector, normal, uvAxis, projectionAxis, radius, sideLength ) {
9
+
10
+ const totArcLength = 2 * Math.PI * radius / 4;
11
+
12
+ // length of the planes between the arcs on each axis
13
+ const centerLength = Math.max( sideLength - 2 * radius, 0 );
14
+ const halfArc = Math.PI / 4;
15
+
16
+ // Get the vector projected onto the Y plane
17
+ _tempNormal.copy( normal );
18
+ _tempNormal[ projectionAxis ] = 0;
19
+ _tempNormal.normalize();
20
+
21
+ // total amount of UV space alloted to a single arc
22
+ const arcUvRatio = 0.5 * totArcLength / ( totArcLength + centerLength );
23
+
24
+ // the distance along one arc the point is at
25
+ const arcAngleRatio = 1.0 - ( _tempNormal.angleTo( faceDirVector ) / halfArc );
26
+
27
+ if ( Math.sign( _tempNormal[ uvAxis ] ) === 1 ) {
28
+
29
+ return arcAngleRatio * arcUvRatio;
30
+
31
+ } else {
32
+
33
+ // total amount of UV space alloted to the plane between the arcs
34
+ const lenUv = centerLength / ( totArcLength + centerLength );
35
+ return lenUv + arcUvRatio + arcUvRatio * ( 1.0 - arcAngleRatio );
36
+
37
+ }
38
+
39
+ }
40
+
41
+ class RoundedBoxGeometry extends BoxGeometry {
42
+
43
+ constructor( width = 1, height = 1, depth = 1, segments = 2, radius = 0.1 ) {
44
+
45
+ // ensure segments is odd so we have a plane connecting the rounded corners
46
+ segments = segments * 2 + 1;
47
+
48
+ // ensure radius isn't bigger than shortest side
49
+ radius = Math.min( width / 2, height / 2, depth / 2, radius );
50
+
51
+ super( 1, 1, 1, segments, segments, segments );
52
+
53
+ // if we just have one segment we're the same as a regular box
54
+ if ( segments === 1 ) return;
55
+
56
+ const geometry2 = this.toNonIndexed();
57
+
58
+ this.index = null;
59
+ this.attributes.position = geometry2.attributes.position;
60
+ this.attributes.normal = geometry2.attributes.normal;
61
+ this.attributes.uv = geometry2.attributes.uv;
62
+
63
+ //
64
+
65
+ const position = new Vector3();
66
+ const normal = new Vector3();
67
+
68
+ const box = new Vector3( width, height, depth ).divideScalar( 2 ).subScalar( radius );
69
+
70
+ const positions = this.attributes.position.array;
71
+ const normals = this.attributes.normal.array;
72
+ const uvs = this.attributes.uv.array;
73
+
74
+ const faceTris = positions.length / 6;
75
+ const faceDirVector = new Vector3();
76
+ const halfSegmentSize = 0.5 / segments;
77
+
78
+ for ( let i = 0, j = 0; i < positions.length; i += 3, j += 2 ) {
79
+
80
+ position.fromArray( positions, i );
81
+ normal.copy( position );
82
+ normal.x -= Math.sign( normal.x ) * halfSegmentSize;
83
+ normal.y -= Math.sign( normal.y ) * halfSegmentSize;
84
+ normal.z -= Math.sign( normal.z ) * halfSegmentSize;
85
+ normal.normalize();
86
+
87
+ positions[ i + 0 ] = box.x * Math.sign( position.x ) + normal.x * radius;
88
+ positions[ i + 1 ] = box.y * Math.sign( position.y ) + normal.y * radius;
89
+ positions[ i + 2 ] = box.z * Math.sign( position.z ) + normal.z * radius;
90
+
91
+ normals[ i + 0 ] = normal.x;
92
+ normals[ i + 1 ] = normal.y;
93
+ normals[ i + 2 ] = normal.z;
94
+
95
+ const side = Math.floor( i / faceTris );
96
+
97
+ switch ( side ) {
98
+
99
+ case 0: // right
100
+
101
+ // generate UVs along Z then Y
102
+ faceDirVector.set( 1, 0, 0 );
103
+ uvs[ j + 0 ] = getUv( faceDirVector, normal, 'z', 'y', radius, depth );
104
+ uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'z', radius, height );
105
+ break;
106
+
107
+ case 1: // left
108
+
109
+ // generate UVs along Z then Y
110
+ faceDirVector.set( - 1, 0, 0 );
111
+ uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'z', 'y', radius, depth );
112
+ uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'z', radius, height );
113
+ break;
114
+
115
+ case 2: // top
116
+
117
+ // generate UVs along X then Z
118
+ faceDirVector.set( 0, 1, 0 );
119
+ uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'z', radius, width );
120
+ uvs[ j + 1 ] = getUv( faceDirVector, normal, 'z', 'x', radius, depth );
121
+ break;
122
+
123
+ case 3: // bottom
124
+
125
+ // generate UVs along X then Z
126
+ faceDirVector.set( 0, - 1, 0 );
127
+ uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'z', radius, width );
128
+ uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'z', 'x', radius, depth );
129
+ break;
130
+
131
+ case 4: // front
132
+
133
+ // generate UVs along X then Y
134
+ faceDirVector.set( 0, 0, 1 );
135
+ uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'y', radius, width );
136
+ uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'x', radius, height );
137
+ break;
138
+
139
+ case 5: // back
140
+
141
+ // generate UVs along X then Y
142
+ faceDirVector.set( 0, 0, - 1 );
143
+ uvs[ j + 0 ] = getUv( faceDirVector, normal, 'x', 'y', radius, width );
144
+ uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'x', radius, height );
145
+ break;
146
+
147
+ }
148
+
149
+ }
150
+
151
+ }
152
+
153
+ }
154
+
155
+ export { RoundedBoxGeometry };
libs/three.js/0.172.0/jsm/geometries/TeapotGeometry.js ADDED
@@ -0,0 +1,704 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ BufferAttribute,
3
+ BufferGeometry,
4
+ Matrix4,
5
+ Vector3,
6
+ Vector4
7
+ } from 'three';
8
+
9
+ /**
10
+ * Tessellates the famous Utah teapot database by Martin Newell into triangles.
11
+ *
12
+ * Parameters: size = 50, segments = 10, bottom = true, lid = true, body = true,
13
+ * fitLid = true, blinn = true
14
+ *
15
+ * size is a relative scale: I've scaled the teapot to fit vertically between -1 and 1.
16
+ * Think of it as a "radius".
17
+ * segments - number of line segments to subdivide each patch edge;
18
+ * 1 is possible but gives degenerates, so two is the real minimum.
19
+ * bottom - boolean, if true (default) then the bottom patches are added. Some consider
20
+ * adding the bottom heresy, so set this to "false" to adhere to the One True Way.
21
+ * lid - to remove the lid and look inside, set to true.
22
+ * body - to remove the body and leave the lid, set this and "bottom" to false.
23
+ * fitLid - the lid is a tad small in the original. This stretches it a bit so you can't
24
+ * see the teapot's insides through the gap.
25
+ * blinn - Jim Blinn scaled the original data vertically by dividing by about 1.3 to look
26
+ * nicer. If you want to see the original teapot, similar to the real-world model, set
27
+ * this to false. True by default.
28
+ * See http://en.wikipedia.org/wiki/File:Original_Utah_Teapot.jpg for the original
29
+ * real-world teapot (from http://en.wikipedia.org/wiki/Utah_teapot).
30
+ *
31
+ * Note that the bottom (the last four patches) is not flat - blame Frank Crow, not me.
32
+ *
33
+ * The teapot should normally be rendered as a double sided object, since for some
34
+ * patches both sides can be seen, e.g., the gap around the lid and inside the spout.
35
+ *
36
+ * Segments 'n' determines the number of triangles output.
37
+ * Total triangles = 32*2*n*n - 8*n [degenerates at the top and bottom cusps are deleted]
38
+ *
39
+ * size_factor # triangles
40
+ * 1 56
41
+ * 2 240
42
+ * 3 552
43
+ * 4 992
44
+ *
45
+ * 10 6320
46
+ * 20 25440
47
+ * 30 57360
48
+ *
49
+ * Code converted from my ancient SPD software, http://tog.acm.org/resources/SPD/
50
+ * Created for the Udacity course "Interactive Rendering", http://bit.ly/ericity
51
+ * YouTube video on teapot history: https://www.youtube.com/watch?v=DxMfblPzFNc
52
+ *
53
+ * See https://en.wikipedia.org/wiki/Utah_teapot for the history of the teapot
54
+ *
55
+ */
56
+
57
+ class TeapotGeometry extends BufferGeometry {
58
+
59
+ constructor( size = 50, segments = 10, bottom = true, lid = true, body = true, fitLid = true, blinn = true ) {
60
+
61
+ // 32 * 4 * 4 Bezier spline patches
62
+ const teapotPatches = [
63
+ /*rim*/
64
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
65
+ 3, 16, 17, 18, 7, 19, 20, 21, 11, 22, 23, 24, 15, 25, 26, 27,
66
+ 18, 28, 29, 30, 21, 31, 32, 33, 24, 34, 35, 36, 27, 37, 38, 39,
67
+ 30, 40, 41, 0, 33, 42, 43, 4, 36, 44, 45, 8, 39, 46, 47, 12,
68
+ /*body*/
69
+ 12, 13, 14, 15, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
70
+ 15, 25, 26, 27, 51, 60, 61, 62, 55, 63, 64, 65, 59, 66, 67, 68,
71
+ 27, 37, 38, 39, 62, 69, 70, 71, 65, 72, 73, 74, 68, 75, 76, 77,
72
+ 39, 46, 47, 12, 71, 78, 79, 48, 74, 80, 81, 52, 77, 82, 83, 56,
73
+ 56, 57, 58, 59, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
74
+ 59, 66, 67, 68, 87, 96, 97, 98, 91, 99, 100, 101, 95, 102, 103, 104,
75
+ 68, 75, 76, 77, 98, 105, 106, 107, 101, 108, 109, 110, 104, 111, 112, 113,
76
+ 77, 82, 83, 56, 107, 114, 115, 84, 110, 116, 117, 88, 113, 118, 119, 92,
77
+ /*handle*/
78
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
79
+ 123, 136, 137, 120, 127, 138, 139, 124, 131, 140, 141, 128, 135, 142, 143, 132,
80
+ 132, 133, 134, 135, 144, 145, 146, 147, 148, 149, 150, 151, 68, 152, 153, 154,
81
+ 135, 142, 143, 132, 147, 155, 156, 144, 151, 157, 158, 148, 154, 159, 160, 68,
82
+ /*spout*/
83
+ 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
84
+ 164, 177, 178, 161, 168, 179, 180, 165, 172, 181, 182, 169, 176, 183, 184, 173,
85
+ 173, 174, 175, 176, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
86
+ 176, 183, 184, 173, 188, 197, 198, 185, 192, 199, 200, 189, 196, 201, 202, 193,
87
+ /*lid*/
88
+ 203, 203, 203, 203, 204, 205, 206, 207, 208, 208, 208, 208, 209, 210, 211, 212,
89
+ 203, 203, 203, 203, 207, 213, 214, 215, 208, 208, 208, 208, 212, 216, 217, 218,
90
+ 203, 203, 203, 203, 215, 219, 220, 221, 208, 208, 208, 208, 218, 222, 223, 224,
91
+ 203, 203, 203, 203, 221, 225, 226, 204, 208, 208, 208, 208, 224, 227, 228, 209,
92
+ 209, 210, 211, 212, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
93
+ 212, 216, 217, 218, 232, 241, 242, 243, 236, 244, 245, 246, 240, 247, 248, 249,
94
+ 218, 222, 223, 224, 243, 250, 251, 252, 246, 253, 254, 255, 249, 256, 257, 258,
95
+ 224, 227, 228, 209, 252, 259, 260, 229, 255, 261, 262, 233, 258, 263, 264, 237,
96
+ /*bottom*/
97
+ 265, 265, 265, 265, 266, 267, 268, 269, 270, 271, 272, 273, 92, 119, 118, 113,
98
+ 265, 265, 265, 265, 269, 274, 275, 276, 273, 277, 278, 279, 113, 112, 111, 104,
99
+ 265, 265, 265, 265, 276, 280, 281, 282, 279, 283, 284, 285, 104, 103, 102, 95,
100
+ 265, 265, 265, 265, 282, 286, 287, 266, 285, 288, 289, 270, 95, 94, 93, 92
101
+ ];
102
+
103
+ const teapotVertices = [
104
+ 1.4, 0, 2.4,
105
+ 1.4, - 0.784, 2.4,
106
+ 0.784, - 1.4, 2.4,
107
+ 0, - 1.4, 2.4,
108
+ 1.3375, 0, 2.53125,
109
+ 1.3375, - 0.749, 2.53125,
110
+ 0.749, - 1.3375, 2.53125,
111
+ 0, - 1.3375, 2.53125,
112
+ 1.4375, 0, 2.53125,
113
+ 1.4375, - 0.805, 2.53125,
114
+ 0.805, - 1.4375, 2.53125,
115
+ 0, - 1.4375, 2.53125,
116
+ 1.5, 0, 2.4,
117
+ 1.5, - 0.84, 2.4,
118
+ 0.84, - 1.5, 2.4,
119
+ 0, - 1.5, 2.4,
120
+ - 0.784, - 1.4, 2.4,
121
+ - 1.4, - 0.784, 2.4,
122
+ - 1.4, 0, 2.4,
123
+ - 0.749, - 1.3375, 2.53125,
124
+ - 1.3375, - 0.749, 2.53125,
125
+ - 1.3375, 0, 2.53125,
126
+ - 0.805, - 1.4375, 2.53125,
127
+ - 1.4375, - 0.805, 2.53125,
128
+ - 1.4375, 0, 2.53125,
129
+ - 0.84, - 1.5, 2.4,
130
+ - 1.5, - 0.84, 2.4,
131
+ - 1.5, 0, 2.4,
132
+ - 1.4, 0.784, 2.4,
133
+ - 0.784, 1.4, 2.4,
134
+ 0, 1.4, 2.4,
135
+ - 1.3375, 0.749, 2.53125,
136
+ - 0.749, 1.3375, 2.53125,
137
+ 0, 1.3375, 2.53125,
138
+ - 1.4375, 0.805, 2.53125,
139
+ - 0.805, 1.4375, 2.53125,
140
+ 0, 1.4375, 2.53125,
141
+ - 1.5, 0.84, 2.4,
142
+ - 0.84, 1.5, 2.4,
143
+ 0, 1.5, 2.4,
144
+ 0.784, 1.4, 2.4,
145
+ 1.4, 0.784, 2.4,
146
+ 0.749, 1.3375, 2.53125,
147
+ 1.3375, 0.749, 2.53125,
148
+ 0.805, 1.4375, 2.53125,
149
+ 1.4375, 0.805, 2.53125,
150
+ 0.84, 1.5, 2.4,
151
+ 1.5, 0.84, 2.4,
152
+ 1.75, 0, 1.875,
153
+ 1.75, - 0.98, 1.875,
154
+ 0.98, - 1.75, 1.875,
155
+ 0, - 1.75, 1.875,
156
+ 2, 0, 1.35,
157
+ 2, - 1.12, 1.35,
158
+ 1.12, - 2, 1.35,
159
+ 0, - 2, 1.35,
160
+ 2, 0, 0.9,
161
+ 2, - 1.12, 0.9,
162
+ 1.12, - 2, 0.9,
163
+ 0, - 2, 0.9,
164
+ - 0.98, - 1.75, 1.875,
165
+ - 1.75, - 0.98, 1.875,
166
+ - 1.75, 0, 1.875,
167
+ - 1.12, - 2, 1.35,
168
+ - 2, - 1.12, 1.35,
169
+ - 2, 0, 1.35,
170
+ - 1.12, - 2, 0.9,
171
+ - 2, - 1.12, 0.9,
172
+ - 2, 0, 0.9,
173
+ - 1.75, 0.98, 1.875,
174
+ - 0.98, 1.75, 1.875,
175
+ 0, 1.75, 1.875,
176
+ - 2, 1.12, 1.35,
177
+ - 1.12, 2, 1.35,
178
+ 0, 2, 1.35,
179
+ - 2, 1.12, 0.9,
180
+ - 1.12, 2, 0.9,
181
+ 0, 2, 0.9,
182
+ 0.98, 1.75, 1.875,
183
+ 1.75, 0.98, 1.875,
184
+ 1.12, 2, 1.35,
185
+ 2, 1.12, 1.35,
186
+ 1.12, 2, 0.9,
187
+ 2, 1.12, 0.9,
188
+ 2, 0, 0.45,
189
+ 2, - 1.12, 0.45,
190
+ 1.12, - 2, 0.45,
191
+ 0, - 2, 0.45,
192
+ 1.5, 0, 0.225,
193
+ 1.5, - 0.84, 0.225,
194
+ 0.84, - 1.5, 0.225,
195
+ 0, - 1.5, 0.225,
196
+ 1.5, 0, 0.15,
197
+ 1.5, - 0.84, 0.15,
198
+ 0.84, - 1.5, 0.15,
199
+ 0, - 1.5, 0.15,
200
+ - 1.12, - 2, 0.45,
201
+ - 2, - 1.12, 0.45,
202
+ - 2, 0, 0.45,
203
+ - 0.84, - 1.5, 0.225,
204
+ - 1.5, - 0.84, 0.225,
205
+ - 1.5, 0, 0.225,
206
+ - 0.84, - 1.5, 0.15,
207
+ - 1.5, - 0.84, 0.15,
208
+ - 1.5, 0, 0.15,
209
+ - 2, 1.12, 0.45,
210
+ - 1.12, 2, 0.45,
211
+ 0, 2, 0.45,
212
+ - 1.5, 0.84, 0.225,
213
+ - 0.84, 1.5, 0.225,
214
+ 0, 1.5, 0.225,
215
+ - 1.5, 0.84, 0.15,
216
+ - 0.84, 1.5, 0.15,
217
+ 0, 1.5, 0.15,
218
+ 1.12, 2, 0.45,
219
+ 2, 1.12, 0.45,
220
+ 0.84, 1.5, 0.225,
221
+ 1.5, 0.84, 0.225,
222
+ 0.84, 1.5, 0.15,
223
+ 1.5, 0.84, 0.15,
224
+ - 1.6, 0, 2.025,
225
+ - 1.6, - 0.3, 2.025,
226
+ - 1.5, - 0.3, 2.25,
227
+ - 1.5, 0, 2.25,
228
+ - 2.3, 0, 2.025,
229
+ - 2.3, - 0.3, 2.025,
230
+ - 2.5, - 0.3, 2.25,
231
+ - 2.5, 0, 2.25,
232
+ - 2.7, 0, 2.025,
233
+ - 2.7, - 0.3, 2.025,
234
+ - 3, - 0.3, 2.25,
235
+ - 3, 0, 2.25,
236
+ - 2.7, 0, 1.8,
237
+ - 2.7, - 0.3, 1.8,
238
+ - 3, - 0.3, 1.8,
239
+ - 3, 0, 1.8,
240
+ - 1.5, 0.3, 2.25,
241
+ - 1.6, 0.3, 2.025,
242
+ - 2.5, 0.3, 2.25,
243
+ - 2.3, 0.3, 2.025,
244
+ - 3, 0.3, 2.25,
245
+ - 2.7, 0.3, 2.025,
246
+ - 3, 0.3, 1.8,
247
+ - 2.7, 0.3, 1.8,
248
+ - 2.7, 0, 1.575,
249
+ - 2.7, - 0.3, 1.575,
250
+ - 3, - 0.3, 1.35,
251
+ - 3, 0, 1.35,
252
+ - 2.5, 0, 1.125,
253
+ - 2.5, - 0.3, 1.125,
254
+ - 2.65, - 0.3, 0.9375,
255
+ - 2.65, 0, 0.9375,
256
+ - 2, - 0.3, 0.9,
257
+ - 1.9, - 0.3, 0.6,
258
+ - 1.9, 0, 0.6,
259
+ - 3, 0.3, 1.35,
260
+ - 2.7, 0.3, 1.575,
261
+ - 2.65, 0.3, 0.9375,
262
+ - 2.5, 0.3, 1.125,
263
+ - 1.9, 0.3, 0.6,
264
+ - 2, 0.3, 0.9,
265
+ 1.7, 0, 1.425,
266
+ 1.7, - 0.66, 1.425,
267
+ 1.7, - 0.66, 0.6,
268
+ 1.7, 0, 0.6,
269
+ 2.6, 0, 1.425,
270
+ 2.6, - 0.66, 1.425,
271
+ 3.1, - 0.66, 0.825,
272
+ 3.1, 0, 0.825,
273
+ 2.3, 0, 2.1,
274
+ 2.3, - 0.25, 2.1,
275
+ 2.4, - 0.25, 2.025,
276
+ 2.4, 0, 2.025,
277
+ 2.7, 0, 2.4,
278
+ 2.7, - 0.25, 2.4,
279
+ 3.3, - 0.25, 2.4,
280
+ 3.3, 0, 2.4,
281
+ 1.7, 0.66, 0.6,
282
+ 1.7, 0.66, 1.425,
283
+ 3.1, 0.66, 0.825,
284
+ 2.6, 0.66, 1.425,
285
+ 2.4, 0.25, 2.025,
286
+ 2.3, 0.25, 2.1,
287
+ 3.3, 0.25, 2.4,
288
+ 2.7, 0.25, 2.4,
289
+ 2.8, 0, 2.475,
290
+ 2.8, - 0.25, 2.475,
291
+ 3.525, - 0.25, 2.49375,
292
+ 3.525, 0, 2.49375,
293
+ 2.9, 0, 2.475,
294
+ 2.9, - 0.15, 2.475,
295
+ 3.45, - 0.15, 2.5125,
296
+ 3.45, 0, 2.5125,
297
+ 2.8, 0, 2.4,
298
+ 2.8, - 0.15, 2.4,
299
+ 3.2, - 0.15, 2.4,
300
+ 3.2, 0, 2.4,
301
+ 3.525, 0.25, 2.49375,
302
+ 2.8, 0.25, 2.475,
303
+ 3.45, 0.15, 2.5125,
304
+ 2.9, 0.15, 2.475,
305
+ 3.2, 0.15, 2.4,
306
+ 2.8, 0.15, 2.4,
307
+ 0, 0, 3.15,
308
+ 0.8, 0, 3.15,
309
+ 0.8, - 0.45, 3.15,
310
+ 0.45, - 0.8, 3.15,
311
+ 0, - 0.8, 3.15,
312
+ 0, 0, 2.85,
313
+ 0.2, 0, 2.7,
314
+ 0.2, - 0.112, 2.7,
315
+ 0.112, - 0.2, 2.7,
316
+ 0, - 0.2, 2.7,
317
+ - 0.45, - 0.8, 3.15,
318
+ - 0.8, - 0.45, 3.15,
319
+ - 0.8, 0, 3.15,
320
+ - 0.112, - 0.2, 2.7,
321
+ - 0.2, - 0.112, 2.7,
322
+ - 0.2, 0, 2.7,
323
+ - 0.8, 0.45, 3.15,
324
+ - 0.45, 0.8, 3.15,
325
+ 0, 0.8, 3.15,
326
+ - 0.2, 0.112, 2.7,
327
+ - 0.112, 0.2, 2.7,
328
+ 0, 0.2, 2.7,
329
+ 0.45, 0.8, 3.15,
330
+ 0.8, 0.45, 3.15,
331
+ 0.112, 0.2, 2.7,
332
+ 0.2, 0.112, 2.7,
333
+ 0.4, 0, 2.55,
334
+ 0.4, - 0.224, 2.55,
335
+ 0.224, - 0.4, 2.55,
336
+ 0, - 0.4, 2.55,
337
+ 1.3, 0, 2.55,
338
+ 1.3, - 0.728, 2.55,
339
+ 0.728, - 1.3, 2.55,
340
+ 0, - 1.3, 2.55,
341
+ 1.3, 0, 2.4,
342
+ 1.3, - 0.728, 2.4,
343
+ 0.728, - 1.3, 2.4,
344
+ 0, - 1.3, 2.4,
345
+ - 0.224, - 0.4, 2.55,
346
+ - 0.4, - 0.224, 2.55,
347
+ - 0.4, 0, 2.55,
348
+ - 0.728, - 1.3, 2.55,
349
+ - 1.3, - 0.728, 2.55,
350
+ - 1.3, 0, 2.55,
351
+ - 0.728, - 1.3, 2.4,
352
+ - 1.3, - 0.728, 2.4,
353
+ - 1.3, 0, 2.4,
354
+ - 0.4, 0.224, 2.55,
355
+ - 0.224, 0.4, 2.55,
356
+ 0, 0.4, 2.55,
357
+ - 1.3, 0.728, 2.55,
358
+ - 0.728, 1.3, 2.55,
359
+ 0, 1.3, 2.55,
360
+ - 1.3, 0.728, 2.4,
361
+ - 0.728, 1.3, 2.4,
362
+ 0, 1.3, 2.4,
363
+ 0.224, 0.4, 2.55,
364
+ 0.4, 0.224, 2.55,
365
+ 0.728, 1.3, 2.55,
366
+ 1.3, 0.728, 2.55,
367
+ 0.728, 1.3, 2.4,
368
+ 1.3, 0.728, 2.4,
369
+ 0, 0, 0,
370
+ 1.425, 0, 0,
371
+ 1.425, 0.798, 0,
372
+ 0.798, 1.425, 0,
373
+ 0, 1.425, 0,
374
+ 1.5, 0, 0.075,
375
+ 1.5, 0.84, 0.075,
376
+ 0.84, 1.5, 0.075,
377
+ 0, 1.5, 0.075,
378
+ - 0.798, 1.425, 0,
379
+ - 1.425, 0.798, 0,
380
+ - 1.425, 0, 0,
381
+ - 0.84, 1.5, 0.075,
382
+ - 1.5, 0.84, 0.075,
383
+ - 1.5, 0, 0.075,
384
+ - 1.425, - 0.798, 0,
385
+ - 0.798, - 1.425, 0,
386
+ 0, - 1.425, 0,
387
+ - 1.5, - 0.84, 0.075,
388
+ - 0.84, - 1.5, 0.075,
389
+ 0, - 1.5, 0.075,
390
+ 0.798, - 1.425, 0,
391
+ 1.425, - 0.798, 0,
392
+ 0.84, - 1.5, 0.075,
393
+ 1.5, - 0.84, 0.075
394
+ ];
395
+
396
+ super();
397
+
398
+ // number of segments per patch
399
+ segments = Math.max( 2, Math.floor( segments ) );
400
+
401
+ // Jim Blinn scaled the teapot down in size by about 1.3 for
402
+ // some rendering tests. He liked the new proportions that he kept
403
+ // the data in this form. The model was distributed with these new
404
+ // proportions and became the norm. Trivia: comparing images of the
405
+ // real teapot and the computer model, the ratio for the bowl of the
406
+ // real teapot is more like 1.25, but since 1.3 is the traditional
407
+ // value given, we use it here.
408
+ const blinnScale = 1.3;
409
+
410
+ // scale the size to be the real scaling factor
411
+ const maxHeight = 3.15 * ( blinn ? 1 : blinnScale );
412
+
413
+ const maxHeight2 = maxHeight / 2;
414
+ const trueSize = size / maxHeight2;
415
+
416
+ // Number of elements depends on what is needed. Subtract degenerate
417
+ // triangles at tip of bottom and lid out in advance.
418
+ let numTriangles = bottom ? ( 8 * segments - 4 ) * segments : 0;
419
+ numTriangles += lid ? ( 16 * segments - 4 ) * segments : 0;
420
+ numTriangles += body ? 40 * segments * segments : 0;
421
+
422
+ const indices = new Uint32Array( numTriangles * 3 );
423
+
424
+ let numVertices = bottom ? 4 : 0;
425
+ numVertices += lid ? 8 : 0;
426
+ numVertices += body ? 20 : 0;
427
+ numVertices *= ( segments + 1 ) * ( segments + 1 );
428
+
429
+ const vertices = new Float32Array( numVertices * 3 );
430
+ const normals = new Float32Array( numVertices * 3 );
431
+ const uvs = new Float32Array( numVertices * 2 );
432
+
433
+ // Bezier form
434
+ const ms = new Matrix4();
435
+ ms.set(
436
+ - 1.0, 3.0, - 3.0, 1.0,
437
+ 3.0, - 6.0, 3.0, 0.0,
438
+ - 3.0, 3.0, 0.0, 0.0,
439
+ 1.0, 0.0, 0.0, 0.0 );
440
+
441
+ const g = [];
442
+
443
+ const sp = [];
444
+ const tp = [];
445
+ const dsp = [];
446
+ const dtp = [];
447
+
448
+ // M * G * M matrix, sort of see
449
+ // http://www.cs.helsinki.fi/group/goa/mallinnus/curves/surfaces.html
450
+ const mgm = [];
451
+
452
+ const vert = [];
453
+ const sdir = [];
454
+ const tdir = [];
455
+
456
+ const norm = new Vector3();
457
+
458
+ let tcoord;
459
+
460
+ let sval;
461
+ let tval;
462
+ let p;
463
+ let dsval = 0;
464
+ let dtval = 0;
465
+
466
+ const normOut = new Vector3();
467
+
468
+ const gmx = new Matrix4();
469
+ const tmtx = new Matrix4();
470
+
471
+ const vsp = new Vector4();
472
+ const vtp = new Vector4();
473
+ const vdsp = new Vector4();
474
+ const vdtp = new Vector4();
475
+
476
+ const vsdir = new Vector3();
477
+ const vtdir = new Vector3();
478
+
479
+ const mst = ms.clone();
480
+ mst.transpose();
481
+
482
+ // internal function: test if triangle has any matching vertices;
483
+ // if so, don't save triangle, since it won't display anything.
484
+ const notDegenerate = ( vtx1, vtx2, vtx3 ) => // if any vertex matches, return false
485
+ ! ( ( ( vertices[ vtx1 * 3 ] === vertices[ vtx2 * 3 ] ) &&
486
+ ( vertices[ vtx1 * 3 + 1 ] === vertices[ vtx2 * 3 + 1 ] ) &&
487
+ ( vertices[ vtx1 * 3 + 2 ] === vertices[ vtx2 * 3 + 2 ] ) ) ||
488
+ ( ( vertices[ vtx1 * 3 ] === vertices[ vtx3 * 3 ] ) &&
489
+ ( vertices[ vtx1 * 3 + 1 ] === vertices[ vtx3 * 3 + 1 ] ) &&
490
+ ( vertices[ vtx1 * 3 + 2 ] === vertices[ vtx3 * 3 + 2 ] ) ) || ( vertices[ vtx2 * 3 ] === vertices[ vtx3 * 3 ] ) &&
491
+ ( vertices[ vtx2 * 3 + 1 ] === vertices[ vtx3 * 3 + 1 ] ) &&
492
+ ( vertices[ vtx2 * 3 + 2 ] === vertices[ vtx3 * 3 + 2 ] ) );
493
+
494
+
495
+ for ( let i = 0; i < 3; i ++ ) {
496
+
497
+ mgm[ i ] = new Matrix4();
498
+
499
+ }
500
+
501
+ const minPatches = body ? 0 : 20;
502
+ const maxPatches = bottom ? 32 : 28;
503
+
504
+ const vertPerRow = segments + 1;
505
+
506
+ let surfCount = 0;
507
+
508
+ let vertCount = 0;
509
+ let normCount = 0;
510
+ let uvCount = 0;
511
+
512
+ let indexCount = 0;
513
+
514
+ for ( let surf = minPatches; surf < maxPatches; surf ++ ) {
515
+
516
+ // lid is in the middle of the data, patches 20-27,
517
+ // so ignore it for this part of the loop if the lid is not desired
518
+ if ( lid || ( surf < 20 || surf >= 28 ) ) {
519
+
520
+ // get M * G * M matrix for x,y,z
521
+ for ( let i = 0; i < 3; i ++ ) {
522
+
523
+ // get control patches
524
+ for ( let r = 0; r < 4; r ++ ) {
525
+
526
+ for ( let c = 0; c < 4; c ++ ) {
527
+
528
+ // transposed
529
+ g[ c * 4 + r ] = teapotVertices[ teapotPatches[ surf * 16 + r * 4 + c ] * 3 + i ];
530
+
531
+ // is the lid to be made larger, and is this a point on the lid
532
+ // that is X or Y?
533
+ if ( fitLid && ( surf >= 20 && surf < 28 ) && ( i !== 2 ) ) {
534
+
535
+ // increase XY size by 7.7%, found empirically. I don't
536
+ // increase Z so that the teapot will continue to fit in the
537
+ // space -1 to 1 for Y (Y is up for the final model).
538
+ g[ c * 4 + r ] *= 1.077;
539
+
540
+ }
541
+
542
+ // Blinn "fixed" the teapot by dividing Z by blinnScale, and that's the
543
+ // data we now use. The original teapot is taller. Fix it:
544
+ if ( ! blinn && ( i === 2 ) ) {
545
+
546
+ g[ c * 4 + r ] *= blinnScale;
547
+
548
+ }
549
+
550
+ }
551
+
552
+ }
553
+
554
+ gmx.set( g[ 0 ], g[ 1 ], g[ 2 ], g[ 3 ], g[ 4 ], g[ 5 ], g[ 6 ], g[ 7 ], g[ 8 ], g[ 9 ], g[ 10 ], g[ 11 ], g[ 12 ], g[ 13 ], g[ 14 ], g[ 15 ] );
555
+
556
+ tmtx.multiplyMatrices( gmx, ms );
557
+ mgm[ i ].multiplyMatrices( mst, tmtx );
558
+
559
+ }
560
+
561
+ // step along, get points, and output
562
+ for ( let sstep = 0; sstep <= segments; sstep ++ ) {
563
+
564
+ const s = sstep / segments;
565
+
566
+ for ( let tstep = 0; tstep <= segments; tstep ++ ) {
567
+
568
+ const t = tstep / segments;
569
+
570
+ // point from basis
571
+ // get power vectors and their derivatives
572
+ for ( p = 4, sval = tval = 1.0; p --; ) {
573
+
574
+ sp[ p ] = sval;
575
+ tp[ p ] = tval;
576
+ sval *= s;
577
+ tval *= t;
578
+
579
+ if ( p === 3 ) {
580
+
581
+ dsp[ p ] = dtp[ p ] = 0.0;
582
+ dsval = dtval = 1.0;
583
+
584
+ } else {
585
+
586
+ dsp[ p ] = dsval * ( 3 - p );
587
+ dtp[ p ] = dtval * ( 3 - p );
588
+ dsval *= s;
589
+ dtval *= t;
590
+
591
+ }
592
+
593
+ }
594
+
595
+ vsp.fromArray( sp );
596
+ vtp.fromArray( tp );
597
+ vdsp.fromArray( dsp );
598
+ vdtp.fromArray( dtp );
599
+
600
+ // do for x,y,z
601
+ for ( let i = 0; i < 3; i ++ ) {
602
+
603
+ // multiply power vectors times matrix to get value
604
+ tcoord = vsp.clone();
605
+ tcoord.applyMatrix4( mgm[ i ] );
606
+ vert[ i ] = tcoord.dot( vtp );
607
+
608
+ // get s and t tangent vectors
609
+ tcoord = vdsp.clone();
610
+ tcoord.applyMatrix4( mgm[ i ] );
611
+ sdir[ i ] = tcoord.dot( vtp );
612
+
613
+ tcoord = vsp.clone();
614
+ tcoord.applyMatrix4( mgm[ i ] );
615
+ tdir[ i ] = tcoord.dot( vdtp );
616
+
617
+ }
618
+
619
+ // find normal
620
+ vsdir.fromArray( sdir );
621
+ vtdir.fromArray( tdir );
622
+ norm.crossVectors( vtdir, vsdir );
623
+ norm.normalize();
624
+
625
+ // if X and Z length is 0, at the cusp, so point the normal up or down, depending on patch number
626
+ if ( vert[ 0 ] === 0 && vert[ 1 ] === 0 ) {
627
+
628
+ // if above the middle of the teapot, normal points up, else down
629
+ normOut.set( 0, vert[ 2 ] > maxHeight2 ? 1 : - 1, 0 );
630
+
631
+ } else {
632
+
633
+ // standard output: rotate on X axis
634
+ normOut.set( norm.x, norm.z, - norm.y );
635
+
636
+ }
637
+
638
+ // store it all
639
+ vertices[ vertCount ++ ] = trueSize * vert[ 0 ];
640
+ vertices[ vertCount ++ ] = trueSize * ( vert[ 2 ] - maxHeight2 );
641
+ vertices[ vertCount ++ ] = - trueSize * vert[ 1 ];
642
+
643
+ normals[ normCount ++ ] = normOut.x;
644
+ normals[ normCount ++ ] = normOut.y;
645
+ normals[ normCount ++ ] = normOut.z;
646
+
647
+ uvs[ uvCount ++ ] = 1 - t;
648
+ uvs[ uvCount ++ ] = 1 - s;
649
+
650
+ }
651
+
652
+ }
653
+
654
+ // save the faces
655
+ for ( let sstep = 0; sstep < segments; sstep ++ ) {
656
+
657
+ for ( let tstep = 0; tstep < segments; tstep ++ ) {
658
+
659
+ const v1 = surfCount * vertPerRow * vertPerRow + sstep * vertPerRow + tstep;
660
+ const v2 = v1 + 1;
661
+ const v3 = v2 + vertPerRow;
662
+ const v4 = v1 + vertPerRow;
663
+
664
+ // Normals and UVs cannot be shared. Without clone(), you can see the consequences
665
+ // of sharing if you call geometry.applyMatrix4( matrix ).
666
+ if ( notDegenerate( v1, v2, v3 ) ) {
667
+
668
+ indices[ indexCount ++ ] = v1;
669
+ indices[ indexCount ++ ] = v2;
670
+ indices[ indexCount ++ ] = v3;
671
+
672
+ }
673
+
674
+ if ( notDegenerate( v1, v3, v4 ) ) {
675
+
676
+ indices[ indexCount ++ ] = v1;
677
+ indices[ indexCount ++ ] = v3;
678
+ indices[ indexCount ++ ] = v4;
679
+
680
+ }
681
+
682
+ }
683
+
684
+ }
685
+
686
+ // increment only if a surface was used
687
+ surfCount ++;
688
+
689
+ }
690
+
691
+ }
692
+
693
+ this.setIndex( new BufferAttribute( indices, 1 ) );
694
+ this.setAttribute( 'position', new BufferAttribute( vertices, 3 ) );
695
+ this.setAttribute( 'normal', new BufferAttribute( normals, 3 ) );
696
+ this.setAttribute( 'uv', new BufferAttribute( uvs, 2 ) );
697
+
698
+ this.computeBoundingSphere();
699
+
700
+ }
701
+
702
+ }
703
+
704
+ export { TeapotGeometry };
libs/three.js/0.172.0/jsm/geometries/TextGeometry.js ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Text = 3D Text
3
+ *
4
+ * parameters = {
5
+ * font: <THREE.Font>, // font
6
+ *
7
+ * size: <float>, // size of the text
8
+ * depth: <float>, // thickness to extrude text
9
+ * curveSegments: <int>, // number of points on the curves
10
+ *
11
+ * bevelEnabled: <bool>, // turn on bevel
12
+ * bevelThickness: <float>, // how deep into text bevel goes
13
+ * bevelSize: <float>, // how far from text outline (including bevelOffset) is bevel
14
+ * bevelOffset: <float> // how far from text outline does bevel start
15
+ * }
16
+ */
17
+
18
+ import {
19
+ ExtrudeGeometry
20
+ } from 'three';
21
+
22
+ class TextGeometry extends ExtrudeGeometry {
23
+
24
+ constructor( text, parameters = {} ) {
25
+
26
+ const font = parameters.font;
27
+
28
+ if ( font === undefined ) {
29
+
30
+ super(); // generate default extrude geometry
31
+
32
+ } else {
33
+
34
+ const shapes = font.generateShapes( text, parameters.size );
35
+
36
+ // defaults
37
+
38
+ if ( parameters.depth === undefined ) parameters.depth = 50;
39
+ if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
40
+ if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
41
+ if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
42
+
43
+ super( shapes, parameters );
44
+
45
+ }
46
+
47
+ this.type = 'TextGeometry';
48
+
49
+ }
50
+
51
+ }
52
+
53
+
54
+ export { TextGeometry };