three-cad-viewer 4.1.2 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/Readme.md +12 -5
  2. package/dist/camera/camera.d.ts +14 -2
  3. package/dist/core/studio-manager.d.ts +91 -0
  4. package/dist/core/types.d.ts +260 -9
  5. package/dist/core/viewer-state.d.ts +28 -2
  6. package/dist/core/viewer.d.ts +200 -6
  7. package/dist/index.d.ts +7 -2
  8. package/dist/rendering/environment.d.ts +239 -0
  9. package/dist/rendering/light-detection.d.ts +44 -0
  10. package/dist/rendering/material-factory.d.ts +77 -2
  11. package/dist/rendering/material-presets.d.ts +32 -0
  12. package/dist/rendering/room-environment.d.ts +13 -0
  13. package/dist/rendering/studio-composer.d.ts +130 -0
  14. package/dist/rendering/studio-floor.d.ts +53 -0
  15. package/dist/rendering/texture-cache.d.ts +142 -0
  16. package/dist/rendering/triplanar.d.ts +37 -0
  17. package/dist/scene/animation.d.ts +1 -1
  18. package/dist/scene/clipping.d.ts +31 -0
  19. package/dist/scene/nestedgroup.d.ts +64 -27
  20. package/dist/scene/objectgroup.d.ts +47 -0
  21. package/dist/three-cad-viewer.css +339 -29
  22. package/dist/three-cad-viewer.esm.js +27567 -11874
  23. package/dist/three-cad-viewer.esm.js.map +1 -1
  24. package/dist/three-cad-viewer.esm.min.js +10 -4
  25. package/dist/three-cad-viewer.js +27486 -11787
  26. package/dist/three-cad-viewer.min.js +10 -4
  27. package/dist/ui/display.d.ts +147 -0
  28. package/dist/utils/decode-instances.d.ts +60 -0
  29. package/dist/utils/utils.d.ts +10 -0
  30. package/package.json +4 -2
  31. package/src/_version.ts +1 -1
  32. package/src/camera/camera.ts +27 -10
  33. package/src/core/studio-manager.ts +682 -0
  34. package/src/core/types.ts +328 -9
  35. package/src/core/viewer-state.ts +84 -4
  36. package/src/core/viewer.ts +453 -22
  37. package/src/index.ts +25 -1
  38. package/src/rendering/environment.ts +840 -0
  39. package/src/rendering/light-detection.ts +327 -0
  40. package/src/rendering/material-factory.ts +456 -2
  41. package/src/rendering/material-presets.ts +303 -0
  42. package/src/rendering/raycast.ts +2 -2
  43. package/src/rendering/room-environment.ts +192 -0
  44. package/src/rendering/studio-composer.ts +577 -0
  45. package/src/rendering/studio-floor.ts +108 -0
  46. package/src/rendering/texture-cache.ts +1020 -0
  47. package/src/rendering/triplanar.ts +329 -0
  48. package/src/scene/animation.ts +3 -2
  49. package/src/scene/clipping.ts +59 -0
  50. package/src/scene/nestedgroup.ts +399 -0
  51. package/src/scene/objectgroup.ts +186 -11
  52. package/src/scene/orientation.ts +12 -0
  53. package/src/scene/render-shape.ts +55 -21
  54. package/src/types/n8ao.d.ts +28 -0
  55. package/src/ui/display.ts +1032 -27
  56. package/src/ui/index.html +181 -44
  57. package/src/utils/decode-instances.ts +233 -0
  58. package/src/utils/utils.ts +33 -20
@@ -10,16 +10,27 @@ import { gpuTracker } from "../utils/gpu-tracker.js";
10
10
  import type {
11
11
  ZebraColorScheme,
12
12
  ZebraMappingMode,
13
+ StudioTextureMapping,
13
14
  Shapes,
14
15
  ColorValue,
15
16
  ColoredMaterial,
17
+ MaterialAppearance,
18
+ MaterialXMaterial,
19
+ TextureEntry,
16
20
  } from "../core/types";
21
+ import { isMaterialXMaterial } from "../core/types";
22
+ import { MATERIAL_PRESETS } from "../rendering/material-presets.js";
23
+ import { logger } from "../utils/logger.js";
24
+ import { TextureCache } from "../rendering/texture-cache.js";
25
+ import type { TextureCacheInterface } from "../rendering/material-factory.js";
26
+ import { applyTriplanarMapping } from "../rendering/triplanar.js";
17
27
 
18
28
  interface ShapeData {
19
29
  vertices: Float32Array | number[][];
20
30
  normals: Float32Array | number[][];
21
31
  triangles: Uint32Array | number[][];
22
32
  edges?: Float32Array | number[][];
33
+ uvs?: Float32Array | number[];
23
34
  }
24
35
 
25
36
  interface EdgeData {
@@ -57,6 +68,7 @@ interface ShapeEntry {
57
68
  geomtype?: number | null;
58
69
  subtype?: string | null;
59
70
  texture?: { image: TextureData; width: number; height: number };
71
+ material?: string;
60
72
  }
61
73
 
62
74
  interface ShapeTree {
@@ -113,6 +125,32 @@ class CompoundGroup extends THREE.Group {
113
125
  *
114
126
  * @internal - This is an internal class used by Viewer
115
127
  */
128
+
129
+ /** Texture field names on MaterialAppearance that require UV coordinates. */
130
+ const TEXTURE_FIELDS = [
131
+ "map", "normalMap", "aoMap",
132
+ "metalnessMap", "roughnessMap", "emissiveMap", "transmissionMap",
133
+ "clearcoatMap", "clearcoatRoughnessMap", "clearcoatNormalMap",
134
+ "thicknessMap", "specularIntensityMap", "specularColorMap",
135
+ "sheenColorMap", "sheenRoughnessMap", "anisotropyMap",
136
+ ] as const;
137
+
138
+ /** Check whether a resolved MaterialAppearance references any texture. */
139
+ function materialHasTexture(def: MaterialAppearance): boolean {
140
+ for (const f of TEXTURE_FIELDS) {
141
+ if ((def as Record<string, unknown>)[f]) return true;
142
+ }
143
+ return false;
144
+ }
145
+
146
+ /** Check whether a threejs-materials entry has texture references in its properties. */
147
+ function materialXHasTextures(entry: MaterialXMaterial): boolean {
148
+ for (const [, prop] of Object.entries(entry.properties)) {
149
+ if (prop.texture) return true;
150
+ }
151
+ return false;
152
+ }
153
+
116
154
  class NestedGroup {
117
155
  shapes!: Shapes;
118
156
  width: number;
@@ -134,6 +172,16 @@ class NestedGroup {
134
172
  groups!: GroupsMap; // Initialized to {} in constructor
135
173
  clipPlanes: THREE.Plane[] | null;
136
174
  materialFactory: MaterialFactory;
175
+ texturesTable: Record<string, TextureEntry> | null;
176
+ materialsTable: Record<string, string | MaterialXMaterial | MaterialAppearance> | null;
177
+ resolvedMaterials: Map<string, MaterialAppearance>;
178
+ /** Cache for threejs-materials entries resolved from the materials table */
179
+ resolvedMaterialX: Map<string, MaterialXMaterial>;
180
+ private _textureCache: TextureCache | null;
181
+ private _studioMaterialCache: Map<string, THREE.MeshPhysicalMaterial | THREE.MeshBasicMaterial>;
182
+ /** Sharing keys of materials that have textures (for UV generation on cache hits) */
183
+ private _texturedMaterialKeys: Set<string>;
184
+ private _isStudioMode: boolean;
137
185
 
138
186
  /**
139
187
  * Create a NestedGroup for rendering CAD geometry.
@@ -181,6 +229,15 @@ class NestedGroup {
181
229
 
182
230
  this.clipPlanes = null;
183
231
 
232
+ this.texturesTable = null;
233
+ this.materialsTable = null;
234
+ this.resolvedMaterials = new Map();
235
+ this.resolvedMaterialX = new Map();
236
+ this._textureCache = null;
237
+ this._studioMaterialCache = new Map();
238
+ this._texturedMaterialKeys = new Set();
239
+ this._isStudioMode = false;
240
+
184
241
  this.materialFactory = new MaterialFactory({
185
242
  defaultOpacity: opacity,
186
243
  metalness: metalness,
@@ -202,6 +259,111 @@ class NestedGroup {
202
259
  deepDispose(this.rootGroup);
203
260
  this.rootGroup = null;
204
261
  }
262
+ this._disposeStudioResources();
263
+ this.resolvedMaterials.clear();
264
+ this.resolvedMaterialX.clear();
265
+ this.texturesTable = null;
266
+ this.materialsTable = null;
267
+ }
268
+
269
+ /**
270
+ * Resolve a material tag to its definition.
271
+ *
272
+ * Returns either a MaterialAppearance (for builtin presets) or a
273
+ * MaterialXMaterial (for threejs-materials entries). The caller must check the
274
+ * return type to determine which factory method to use.
275
+ *
276
+ * Resolution order:
277
+ * 1. Check caches (resolvedMaterials / resolvedMaterialX)
278
+ * 2. Look up in root-level `materials` table:
279
+ * - string starting with "builtin:" → MATERIAL_PRESETS lookup
280
+ * - object with `properties` key → threejs-materials entry
281
+ * 3. Direct lookup in MATERIAL_PRESETS by tag name
282
+ * 4. No match → warning, return null
283
+ *
284
+ * @param tag - The material tag from a leaf node
285
+ * @param objectPath - The object path (for warning messages)
286
+ * @returns Resolved material definition or null if not found
287
+ */
288
+ resolveMaterialTag(
289
+ tag: string,
290
+ objectPath: string,
291
+ ): MaterialAppearance | MaterialXMaterial | null {
292
+ // Empty string is equivalent to no tag -- skip silently
293
+ if (tag === "") {
294
+ return null;
295
+ }
296
+
297
+ // Check caches
298
+ const cachedPreset = this.resolvedMaterials.get(tag);
299
+ if (cachedPreset !== undefined) return cachedPreset;
300
+
301
+ const cachedMX = this.resolvedMaterialX.get(tag);
302
+ if (cachedMX !== undefined) return cachedMX;
303
+
304
+ // 1. Look up in user-defined materials table
305
+ if (this.materialsTable && tag in this.materialsTable) {
306
+ const entry = this.materialsTable[tag];
307
+
308
+ // String entry: "builtin:<preset-name>"
309
+ if (typeof entry === "string") {
310
+ if (entry.startsWith("builtin:")) {
311
+ const presetName = entry.slice(8);
312
+ const preset = MATERIAL_PRESETS[presetName];
313
+ if (preset) {
314
+ const resolved = { ...preset };
315
+ this.resolvedMaterials.set(tag, resolved);
316
+ return resolved;
317
+ }
318
+ logger.warn(
319
+ `Unknown builtin preset '${presetName}' referenced by '${tag}' on '${objectPath}'`,
320
+ );
321
+ return null;
322
+ }
323
+ logger.warn(
324
+ `Invalid material string '${entry}' for tag '${tag}' (expected "builtin:" prefix)`,
325
+ );
326
+ return null;
327
+ }
328
+
329
+ // MaterialXMaterial entry: object with `properties` key
330
+ if (isMaterialXMaterial(entry)) {
331
+ this.resolvedMaterialX.set(tag, entry);
332
+ return entry;
333
+ }
334
+
335
+ // MaterialAppearance entry: object with `builtin` key (preset + overrides)
336
+ if (typeof entry === "object" && "builtin" in entry) {
337
+ const appearance = entry as MaterialAppearance;
338
+ const presetName = appearance.builtin!;
339
+ const preset = MATERIAL_PRESETS[presetName];
340
+ if (!preset) {
341
+ logger.warn(
342
+ `Unknown builtin preset '${presetName}' referenced by '${tag}' on '${objectPath}'`,
343
+ );
344
+ return null;
345
+ }
346
+ const resolved: MaterialAppearance = { ...preset, ...appearance };
347
+ this.resolvedMaterials.set(tag, resolved);
348
+ return resolved;
349
+ }
350
+
351
+ // Should not happen with current type, but guard anyway
352
+ logger.warn(`Unrecognised material entry for tag '${tag}' on '${objectPath}'`);
353
+ return null;
354
+ }
355
+
356
+ // 2. Direct lookup in built-in presets (leaf tag matches preset name)
357
+ const preset = MATERIAL_PRESETS[tag];
358
+ if (preset) {
359
+ const resolved = { ...preset };
360
+ this.resolvedMaterials.set(tag, resolved);
361
+ return resolved;
362
+ }
363
+
364
+ // 3. No match
365
+ logger.warn(`Unknown material tag '${tag}' on object '${objectPath}'`);
366
+ return null;
205
367
  }
206
368
 
207
369
  /**
@@ -469,6 +631,15 @@ class NestedGroup {
469
631
  new THREE.BufferAttribute(normals, 3),
470
632
  );
471
633
  shapeGeometry.setIndex(new THREE.BufferAttribute(triangles, 1));
634
+ if (shape.uvs && shape.uvs.length > 0) {
635
+ const uvArray = shape.uvs instanceof Float32Array
636
+ ? shape.uvs
637
+ : new Float32Array(shape.uvs);
638
+ shapeGeometry.setAttribute(
639
+ "uv",
640
+ new THREE.BufferAttribute(uvArray, 2),
641
+ );
642
+ }
472
643
  group.shapeGeometry = shapeGeometry;
473
644
 
474
645
  frontMaterial = this.materialFactory.createFrontFaceMaterial(
@@ -798,12 +969,21 @@ class NestedGroup {
798
969
  group.add(this.renderLoop(shape));
799
970
  } else {
800
971
  const entry = shape as ShapeEntry;
972
+ // Propagate material tag from shapes data to local ShapeEntry
973
+ const materialTag = (shape as Shapes).material;
974
+ if (materialTag != null) {
975
+ entry.material = materialTag;
976
+ }
801
977
  const has_texture = entry.texture != null;
802
978
  const texture = has_texture ? entry.texture!.image : null;
803
979
  const width = has_texture ? entry.texture!.width : null;
804
980
  const height = has_texture ? entry.texture!.height : null;
805
981
  const objectGroup = _render(entry, texture, width, height);
806
982
  this.groups[entry.id] = objectGroup;
983
+ // Store material tag on ObjectGroup for Studio mode lookup
984
+ if (entry.material !== undefined && entry.material !== null) {
985
+ objectGroup.materialTag = entry.material;
986
+ }
807
987
  group.add(objectGroup);
808
988
  }
809
989
  }
@@ -817,6 +997,10 @@ class NestedGroup {
817
997
  if (this.shapes.format == "GDS") {
818
998
  this.instances = this.shapes.instances || null;
819
999
  }
1000
+ this.texturesTable = this.shapes.textures || null;
1001
+ this.materialsTable = this.shapes.materials || null;
1002
+ this.resolvedMaterials.clear();
1003
+ this.resolvedMaterialX.clear();
820
1004
  this.rootGroup = this.renderLoop(this.shapes);
821
1005
  return this.rootGroup;
822
1006
  }
@@ -1018,6 +1202,221 @@ class NestedGroup {
1018
1202
  setZebraMappingMode(flag: ZebraMappingMode): void {
1019
1203
  this._traverse("setZebraMappingMode", flag);
1020
1204
  }
1205
+
1206
+ // ===========================================================================
1207
+ // Studio Mode
1208
+ // ===========================================================================
1209
+
1210
+ /**
1211
+ * Enter Studio mode: build and apply studio materials to all ObjectGroups.
1212
+ *
1213
+ * Material resolution per ObjectGroup:
1214
+ * 1. Resolve the material tag via `resolveMaterialTag()`
1215
+ * - MaterialXMaterial → `createStudioMaterialFromMaterialX`
1216
+ * - MaterialAppearance → `createStudioMaterial` (builtin presets)
1217
+ * - null (no tag) → fallback plastic-glossy preset tinted with CAD color
1218
+ * 2. Cache by sharing key for reuse across objects with the same tag+color
1219
+ * 3. Clone BackSide variant for renderback objects
1220
+ * 4. Auto-generate box-projected UVs when textured but geometry has no UVs
1221
+ */
1222
+ async enterStudioMode(textureMapping: StudioTextureMapping = "triplanar"): Promise<string[]> {
1223
+ // Create TextureCache lazily
1224
+ if (!this._textureCache) {
1225
+ this._textureCache = new TextureCache();
1226
+ }
1227
+ this._textureCache.setTexturesTable(this.texturesTable ?? undefined);
1228
+
1229
+ // Track material tags that failed to resolve
1230
+ const unresolvedTags = new Set<string>();
1231
+
1232
+ // Iterate all ObjectGroups with front meshes
1233
+ for (const path in this.groups) {
1234
+ const obj = this.groups[path];
1235
+ if (!(obj instanceof ObjectGroup)) continue;
1236
+ if (!obj.front) continue;
1237
+
1238
+ // Determine material tag, leaf color, and leaf alpha
1239
+ const tag = obj.materialTag || "";
1240
+ const leafColor = obj.originalColor
1241
+ ? "#" + obj.originalColor.getHexString()
1242
+ : "#707070";
1243
+ const leafAlpha = obj.alpha;
1244
+
1245
+ // Compute sharing key
1246
+ const sharingKey = `${tag}:${leafColor}:${leafAlpha}`;
1247
+
1248
+ // Check cached material for this key
1249
+ let studioMaterial = this._studioMaterialCache.get(sharingKey);
1250
+
1251
+ if (!studioMaterial) {
1252
+ // Resolve the tag
1253
+ const resolved = tag ? this.resolveMaterialTag(tag, path) : null;
1254
+ if (tag && !resolved) {
1255
+ unresolvedTags.add(tag);
1256
+ }
1257
+
1258
+ // Per-object try/catch: a single failure should not abort the rest
1259
+ try {
1260
+ if (resolved && isMaterialXMaterial(resolved)) {
1261
+ // --- threejs-materials path ---
1262
+ studioMaterial = await this.materialFactory.createStudioMaterialFromMaterialX(
1263
+ resolved.properties,
1264
+ resolved.textureRepeat,
1265
+ this._textureCache as TextureCacheInterface,
1266
+ );
1267
+ if (materialXHasTextures(resolved)) {
1268
+ this._texturedMaterialKeys.add(sharingKey);
1269
+ }
1270
+ } else {
1271
+ // --- Builtin preset path (or fallback) ---
1272
+ let materialDef: MaterialAppearance;
1273
+ if (resolved) {
1274
+ materialDef = resolved;
1275
+ } else if (leafAlpha < 1) {
1276
+ // Fallback for transparent objects: acrylic-clear with
1277
+ // transmission matching the CAD alpha, tinted with CAD color
1278
+ const { color: _, ...acrylicClear } = MATERIAL_PRESETS["acrylic-clear"];
1279
+ materialDef = { ...acrylicClear, transmission: 1 - leafAlpha };
1280
+ } else {
1281
+ // Fallback: plastic-glossy tinted with CAD color
1282
+ const { color: _, ...plasticGlossy } = MATERIAL_PRESETS["plastic-glossy"];
1283
+ materialDef = plasticGlossy;
1284
+ }
1285
+ studioMaterial = await this.materialFactory.createStudioMaterial({
1286
+ materialDef,
1287
+ fallbackColor: leafColor,
1288
+ fallbackAlpha: leafAlpha,
1289
+ textureCache: this._textureCache as TextureCacheInterface,
1290
+ });
1291
+ if (materialHasTexture(materialDef)) {
1292
+ this._texturedMaterialKeys.add(sharingKey);
1293
+ }
1294
+ }
1295
+ } catch (err) {
1296
+ logger.warn(
1297
+ `Studio material creation failed for "${path}" (tag="${tag}"), skipping`,
1298
+ err,
1299
+ );
1300
+ continue;
1301
+ }
1302
+
1303
+ this._studioMaterialCache.set(sharingKey, studioMaterial);
1304
+ }
1305
+
1306
+ // Triplanar mapping for textured materials.
1307
+ // "triplanar" mode: always use triplanar for textured materials
1308
+ // "parametric" mode: triplanar only when geometry has no UVs (fallback)
1309
+ const textured = this._texturedMaterialKeys.has(sharingKey);
1310
+ const hasUVs = obj.shapeGeometry?.getAttribute("uv") != null;
1311
+ const needsTriplanar =
1312
+ textured &&
1313
+ obj.shapeGeometry != null &&
1314
+ (textureMapping === "triplanar" || !hasUVs);
1315
+
1316
+ if (textured) {
1317
+ logger.debug(`Studio "${path}": ${needsTriplanar ? "using triplanar" : "using parametric UVs"}`);
1318
+ }
1319
+
1320
+ if (needsTriplanar && studioMaterial instanceof THREE.MeshPhysicalMaterial) {
1321
+ const triKey = `${sharingKey}:tri:${path}`;
1322
+ let triMat = this._studioMaterialCache.get(triKey);
1323
+ if (!triMat) {
1324
+ triMat = studioMaterial.clone();
1325
+ applyTriplanarMapping(triMat as THREE.MeshPhysicalMaterial, obj.shapeGeometry!);
1326
+ this._studioMaterialCache.set(triKey, triMat);
1327
+ }
1328
+ studioMaterial = triMat;
1329
+ }
1330
+
1331
+ // Build back-face variant if needed
1332
+ let studioBack: THREE.MeshPhysicalMaterial | null = null;
1333
+ if (obj.renderback && studioMaterial instanceof THREE.MeshPhysicalMaterial) {
1334
+ const backKey = needsTriplanar
1335
+ ? `${sharingKey}:tri:${path}:back`
1336
+ : `${sharingKey}:back`;
1337
+ let cachedBack = this._studioMaterialCache.get(backKey);
1338
+ if (!cachedBack) {
1339
+ cachedBack = studioMaterial.clone();
1340
+ cachedBack.side = THREE.BackSide;
1341
+ if (needsTriplanar && obj.shapeGeometry) {
1342
+ applyTriplanarMapping(cachedBack as THREE.MeshPhysicalMaterial, obj.shapeGeometry);
1343
+ }
1344
+ this._studioMaterialCache.set(backKey, cachedBack);
1345
+ }
1346
+ studioBack = cachedBack as THREE.MeshPhysicalMaterial;
1347
+ }
1348
+
1349
+ // Apply to ObjectGroup
1350
+ obj.enterStudioMode(
1351
+ studioMaterial instanceof THREE.MeshPhysicalMaterial ? studioMaterial : null,
1352
+ studioBack,
1353
+ );
1354
+ }
1355
+
1356
+ this._isStudioMode = true;
1357
+ return [...unresolvedTags];
1358
+ }
1359
+
1360
+ /**
1361
+ * Leave Studio mode: restore CAD materials on all ObjectGroups.
1362
+ * Does NOT clear the material cache (allows fast re-entry).
1363
+ */
1364
+ leaveStudioMode(): void {
1365
+ for (const path in this.groups) {
1366
+ const obj = this.groups[path];
1367
+ if (!(obj instanceof ObjectGroup)) continue;
1368
+ obj.leaveStudioMode();
1369
+ }
1370
+ this._isStudioMode = false;
1371
+ }
1372
+
1373
+ /**
1374
+ * Clear cached Studio materials so they are rebuilt on next enterStudioMode.
1375
+ */
1376
+ clearStudioMaterialCache(): void {
1377
+ for (const [, material] of this._studioMaterialCache) {
1378
+ material.dispose();
1379
+ }
1380
+ this._studioMaterialCache.clear();
1381
+ this._texturedMaterialKeys.clear();
1382
+ }
1383
+
1384
+ /**
1385
+ * Set edge visibility across all ObjectGroups while in Studio mode.
1386
+ * @param visible - Whether edges should be visible
1387
+ */
1388
+ setStudioShowEdges(visible: boolean): void {
1389
+ for (const path in this.groups) {
1390
+ const obj = this.groups[path];
1391
+ if (!(obj instanceof ObjectGroup)) continue;
1392
+ obj.setStudioShowEdges(visible);
1393
+ }
1394
+ }
1395
+
1396
+ /**
1397
+ * Dispose all Studio mode resources (material cache + texture cache).
1398
+ */
1399
+ private _disposeStudioResources(): void {
1400
+ // Leave studio mode if still active
1401
+ if (this._isStudioMode) {
1402
+ this.leaveStudioMode();
1403
+ }
1404
+
1405
+ // Dispose cached studio materials
1406
+ for (const [, material] of this._studioMaterialCache) {
1407
+ material.dispose();
1408
+ }
1409
+ this._studioMaterialCache.clear();
1410
+ this._texturedMaterialKeys.clear();
1411
+
1412
+ // Dispose texture cache
1413
+ if (this._textureCache) {
1414
+ this._textureCache.disposeFull();
1415
+ this._textureCache = null;
1416
+ }
1417
+
1418
+ this._isStudioMode = false;
1419
+ }
1021
1420
  }
1022
1421
 
1023
1422
  /**