@luminocity/lemonate-engine 26.4.8 → 26.5.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.
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@luminocity/lemonate-gateway'), require('three'), require('deep-diff'), require('opentype.js'), require('three/webgpu'), require('@gltf-transform/core'), require('@gltf-transform/extensions')) :
3
- typeof define === 'function' && define.amd ? define(['exports', '@luminocity/lemonate-gateway', 'three', 'deep-diff', 'opentype.js', 'three/webgpu', '@gltf-transform/core', '@gltf-transform/extensions'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["lemonate-engine"] = {}, global["@luminocity/lemonate-gateway"], global.three, global["deep-diff"], global.opentype.js, global["three/webgpu"], global["@gltf-transform/core"], global["@gltf-transform/extensions"]));
5
- })(this, (function (exports, lemonateGateway, THREE, deepDiff$1, opentype, webgpu, core, extensions) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@luminocity/lemonate-gateway'), require('three'), require('deep-diff'), require('opentype.js'), require('three/tsl'), require('three/webgpu'), require('@gltf-transform/core'), require('@gltf-transform/extensions')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', '@luminocity/lemonate-gateway', 'three', 'deep-diff', 'opentype.js', 'three/tsl', 'three/webgpu', '@gltf-transform/core', '@gltf-transform/extensions'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["lemonate-engine"] = {}, global["@luminocity/lemonate-gateway"], global.three, global["deep-diff"], global.opentype.js, global["three/tsl"], global["three/webgpu"], global["@gltf-transform/core"], global["@gltf-transform/extensions"]));
5
+ })(this, (function (exports, lemonateGateway, THREE, deepDiff$1, opentype, tsl, webgpu, core, extensions) { 'use strict';
6
6
 
7
7
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
8
8
  function _interopNamespaceDefault(e) {
@@ -6204,8 +6204,7 @@
6204
6204
  for ( const name in morphAttributes ) {
6205
6205
 
6206
6206
  const numMorphTargets = morphAttributes[ name ][ 0 ].length;
6207
-
6208
- if ( numMorphTargets === 0 ) break;
6207
+ if ( numMorphTargets === 0 ) continue;
6209
6208
 
6210
6209
  mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {};
6211
6210
  mergedGeometry.morphAttributes[ name ] = [];
@@ -11407,12 +11406,12 @@
11407
11406
  }
11408
11407
  }
11409
11408
 
11410
- var engine$1 = "26.4.8";
11409
+ var engine$1 = "26.5.0";
11411
11410
  var bullet = "3.26";
11412
11411
  var lua = "5.4.3";
11413
11412
  var packageVersionInfo = {
11414
11413
  engine: engine$1,
11415
- "three.js": "0.182.0",
11414
+ "three.js": "0.184.0",
11416
11415
  bullet: bullet,
11417
11416
  lua: lua
11418
11417
  };
@@ -11510,7 +11509,7 @@
11510
11509
 
11511
11510
  s += chunk; len += chunk.length;
11512
11511
  p += chunkSize;
11513
- chunk += String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) );
11512
+ chunk = String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) );
11514
11513
 
11515
11514
  }
11516
11515
 
@@ -17945,7 +17944,7 @@
17945
17944
  webGlRenderer;
17946
17945
  connections = [];
17947
17946
  dependencies = new Map();
17948
- clock;
17947
+ timer = new THREE.Timer();
17949
17948
  nameField;
17950
17949
  name = "";
17951
17950
  extendBaseShader = false;
@@ -17965,12 +17964,7 @@
17965
17964
  this.engine = engine;
17966
17965
  this.renderer = this.engine.renderer;
17967
17966
  this.webGlRenderer = this.renderer.webGlRenderer;
17968
- this.connections = [];
17969
- this.dependencies = new Map();
17970
- this.clock = new THREE.Clock();
17971
17967
  this.nameField = this.block.getField("Name");
17972
- this.extendBaseShader = false;
17973
- this.blendMode = null;
17974
17968
  }
17975
17969
  dispose() {
17976
17970
  if (this.fsQuad) {
@@ -18109,7 +18103,7 @@
18109
18103
  this.uniforms.aspect.value = width / height;
18110
18104
  }
18111
18105
  _getDeltaTime() {
18112
- return this.clock.getDelta();
18106
+ return this.timer.getDelta();
18113
18107
  }
18114
18108
  _allocateTarget(name, width, height, needsDepthTexture) {
18115
18109
  return this.composer._allocateTarget(this, name, width, height, needsDepthTexture);
@@ -49158,7 +49152,7 @@
49158
49152
  /**
49159
49153
  * A loader for the glTF 2.0 format.
49160
49154
  *
49161
- * [glTF](https://www.khronos.org/gltf/} (GL Transmission Format) is an [open format specification]{@link https://github.com/KhronosGroup/glTF/tree/main/specification/2.0)
49155
+ * [glTF](https://www.khronos.org/gltf/) (GL Transmission Format) is an [open format specification]{@link https://github.com/KhronosGroup/glTF/tree/main/specification/2.0)
49162
49156
  * for efficient delivery and loading of 3D content. Assets may be provided either in JSON (.gltf) or binary (.glb)
49163
49157
  * format. External files store textures (.jpg, .png) and additional binary data (.bin). A glTF asset may deliver
49164
49158
  * one or more scenes, including meshes, materials, textures, skins, skeletons, morph targets, animations, lights,
@@ -49170,8 +49164,11 @@
49170
49164
  *
49171
49165
  * `GLTFLoader` supports the following glTF 2.0 extensions:
49172
49166
  * - KHR_draco_mesh_compression
49167
+ * - KHR_lights_punctual
49168
+ * - KHR_materials_anisotropy
49173
49169
  * - KHR_materials_clearcoat
49174
49170
  * - KHR_materials_dispersion
49171
+ * - KHR_materials_emissive_strength
49175
49172
  * - KHR_materials_ior
49176
49173
  * - KHR_materials_specular
49177
49174
  * - KHR_materials_transmission
@@ -49179,12 +49176,14 @@
49179
49176
  * - KHR_materials_unlit
49180
49177
  * - KHR_materials_volume
49181
49178
  * - KHR_mesh_quantization
49182
- * - KHR_lights_punctual
49179
+ * - KHR_meshopt_compression
49183
49180
  * - KHR_texture_basisu
49184
49181
  * - KHR_texture_transform
49185
- * - EXT_texture_webp
49182
+ * - EXT_materials_bump
49186
49183
  * - EXT_meshopt_compression
49187
49184
  * - EXT_mesh_gpu_instancing
49185
+ * - EXT_texture_avif
49186
+ * - EXT_texture_webp
49188
49187
  *
49189
49188
  * The following glTF 2.0 extension is supported by an external user plugin:
49190
49189
  * - [KHR_materials_variants](https://github.com/takahirox/three-gltf-extensions)
@@ -49316,7 +49315,13 @@
49316
49315
 
49317
49316
  this.register( function ( parser ) {
49318
49317
 
49319
- return new GLTFMeshoptCompression( parser );
49318
+ return new GLTFMeshoptCompression( parser, EXTENSIONS.EXT_MESHOPT_COMPRESSION );
49319
+
49320
+ } );
49321
+
49322
+ this.register( function ( parser ) {
49323
+
49324
+ return new GLTFMeshoptCompression( parser, EXTENSIONS.KHR_MESHOPT_COMPRESSION );
49320
49325
 
49321
49326
  } );
49322
49327
 
@@ -49495,7 +49500,7 @@
49495
49500
  }
49496
49501
 
49497
49502
  /**
49498
- * Parses the given FBX data and returns the resulting group.
49503
+ * Parses the given glTF data and returns the resulting group.
49499
49504
  *
49500
49505
  * @param {string|ArrayBuffer} data - The raw glTF data.
49501
49506
  * @param {string} path - The URL base path.
@@ -49687,6 +49692,20 @@
49687
49692
  /********** EXTENSIONS ***********/
49688
49693
  /*********************************/
49689
49694
 
49695
+ function getMaterialExtension( parser, materialIndex, extensionName ) {
49696
+
49697
+ const materialDef = parser.json.materials[ materialIndex ];
49698
+
49699
+ if ( materialDef.extensions && materialDef.extensions[ extensionName ] ) {
49700
+
49701
+ return materialDef.extensions[ extensionName ];
49702
+
49703
+ }
49704
+
49705
+ return null;
49706
+
49707
+ }
49708
+
49690
49709
  const EXTENSIONS = {
49691
49710
  KHR_BINARY_GLTF: 'KHR_binary_glTF',
49692
49711
  KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression',
@@ -49709,6 +49728,7 @@
49709
49728
  EXT_TEXTURE_WEBP: 'EXT_texture_webp',
49710
49729
  EXT_TEXTURE_AVIF: 'EXT_texture_avif',
49711
49730
  EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression',
49731
+ KHR_MESHOPT_COMPRESSION: 'KHR_meshopt_compression',
49712
49732
  EXT_MESH_GPU_INSTANCING: 'EXT_mesh_gpu_instancing'
49713
49733
  };
49714
49734
 
@@ -49923,20 +49943,13 @@
49923
49943
 
49924
49944
  extendMaterialParams( materialIndex, materialParams ) {
49925
49945
 
49926
- const parser = this.parser;
49927
- const materialDef = parser.json.materials[ materialIndex ];
49928
-
49929
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
49946
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
49930
49947
 
49931
- return Promise.resolve();
49948
+ if ( extension === null ) return Promise.resolve();
49932
49949
 
49933
- }
49934
-
49935
- const emissiveStrength = materialDef.extensions[ this.name ].emissiveStrength;
49936
-
49937
- if ( emissiveStrength !== undefined ) {
49950
+ if ( extension.emissiveStrength !== undefined ) {
49938
49951
 
49939
- materialParams.emissiveIntensity = emissiveStrength;
49952
+ materialParams.emissiveIntensity = extension.emissiveStrength;
49940
49953
 
49941
49954
  }
49942
49955
 
@@ -49964,30 +49977,20 @@
49964
49977
 
49965
49978
  getMaterialType( materialIndex ) {
49966
49979
 
49967
- const parser = this.parser;
49968
- const materialDef = parser.json.materials[ materialIndex ];
49969
-
49970
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
49980
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
49971
49981
 
49972
- return THREE.MeshPhysicalMaterial;
49982
+ return extension !== null ? THREE.MeshPhysicalMaterial : null;
49973
49983
 
49974
49984
  }
49975
49985
 
49976
49986
  extendMaterialParams( materialIndex, materialParams ) {
49977
49987
 
49978
- const parser = this.parser;
49979
- const materialDef = parser.json.materials[ materialIndex ];
49980
-
49981
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
49988
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
49982
49989
 
49983
- return Promise.resolve();
49984
-
49985
- }
49990
+ if ( extension === null ) return Promise.resolve();
49986
49991
 
49987
49992
  const pending = [];
49988
49993
 
49989
- const extension = materialDef.extensions[ this.name ];
49990
-
49991
49994
  if ( extension.clearcoatFactor !== undefined ) {
49992
49995
 
49993
49996
  materialParams.clearcoat = extension.clearcoatFactor;
@@ -49996,7 +49999,7 @@
49996
49999
 
49997
50000
  if ( extension.clearcoatTexture !== undefined ) {
49998
50001
 
49999
- pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) );
50002
+ pending.push( this.parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) );
50000
50003
 
50001
50004
  }
50002
50005
 
@@ -50008,13 +50011,13 @@
50008
50011
 
50009
50012
  if ( extension.clearcoatRoughnessTexture !== undefined ) {
50010
50013
 
50011
- pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) );
50014
+ pending.push( this.parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) );
50012
50015
 
50013
50016
  }
50014
50017
 
50015
50018
  if ( extension.clearcoatNormalTexture !== undefined ) {
50016
50019
 
50017
- pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) );
50020
+ pending.push( this.parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) );
50018
50021
 
50019
50022
  if ( extension.clearcoatNormalTexture.scale !== undefined ) {
50020
50023
 
@@ -50050,27 +50053,17 @@
50050
50053
 
50051
50054
  getMaterialType( materialIndex ) {
50052
50055
 
50053
- const parser = this.parser;
50054
- const materialDef = parser.json.materials[ materialIndex ];
50055
-
50056
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
50056
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50057
50057
 
50058
- return THREE.MeshPhysicalMaterial;
50058
+ return extension !== null ? THREE.MeshPhysicalMaterial : null;
50059
50059
 
50060
50060
  }
50061
50061
 
50062
50062
  extendMaterialParams( materialIndex, materialParams ) {
50063
50063
 
50064
- const parser = this.parser;
50065
- const materialDef = parser.json.materials[ materialIndex ];
50064
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50066
50065
 
50067
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
50068
-
50069
- return Promise.resolve();
50070
-
50071
- }
50072
-
50073
- const extension = materialDef.extensions[ this.name ];
50066
+ if ( extension === null ) return Promise.resolve();
50074
50067
 
50075
50068
  materialParams.dispersion = extension.dispersion !== undefined ? extension.dispersion : 0;
50076
50069
 
@@ -50098,30 +50091,20 @@
50098
50091
 
50099
50092
  getMaterialType( materialIndex ) {
50100
50093
 
50101
- const parser = this.parser;
50102
- const materialDef = parser.json.materials[ materialIndex ];
50094
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50103
50095
 
50104
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
50105
-
50106
- return THREE.MeshPhysicalMaterial;
50096
+ return extension !== null ? THREE.MeshPhysicalMaterial : null;
50107
50097
 
50108
50098
  }
50109
50099
 
50110
50100
  extendMaterialParams( materialIndex, materialParams ) {
50111
50101
 
50112
- const parser = this.parser;
50113
- const materialDef = parser.json.materials[ materialIndex ];
50114
-
50115
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
50116
-
50117
- return Promise.resolve();
50102
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50118
50103
 
50119
- }
50104
+ if ( extension === null ) return Promise.resolve();
50120
50105
 
50121
50106
  const pending = [];
50122
50107
 
50123
- const extension = materialDef.extensions[ this.name ];
50124
-
50125
50108
  if ( extension.iridescenceFactor !== undefined ) {
50126
50109
 
50127
50110
  materialParams.iridescence = extension.iridescenceFactor;
@@ -50130,7 +50113,7 @@
50130
50113
 
50131
50114
  if ( extension.iridescenceTexture !== undefined ) {
50132
50115
 
50133
- pending.push( parser.assignTexture( materialParams, 'iridescenceMap', extension.iridescenceTexture ) );
50116
+ pending.push( this.parser.assignTexture( materialParams, 'iridescenceMap', extension.iridescenceTexture ) );
50134
50117
 
50135
50118
  }
50136
50119
 
@@ -50160,7 +50143,7 @@
50160
50143
 
50161
50144
  if ( extension.iridescenceThicknessTexture !== undefined ) {
50162
50145
 
50163
- pending.push( parser.assignTexture( materialParams, 'iridescenceThicknessMap', extension.iridescenceThicknessTexture ) );
50146
+ pending.push( this.parser.assignTexture( materialParams, 'iridescenceThicknessMap', extension.iridescenceThicknessTexture ) );
50164
50147
 
50165
50148
  }
50166
50149
 
@@ -50188,25 +50171,17 @@
50188
50171
 
50189
50172
  getMaterialType( materialIndex ) {
50190
50173
 
50191
- const parser = this.parser;
50192
- const materialDef = parser.json.materials[ materialIndex ];
50193
-
50194
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
50174
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50195
50175
 
50196
- return THREE.MeshPhysicalMaterial;
50176
+ return extension !== null ? THREE.MeshPhysicalMaterial : null;
50197
50177
 
50198
50178
  }
50199
50179
 
50200
50180
  extendMaterialParams( materialIndex, materialParams ) {
50201
50181
 
50202
- const parser = this.parser;
50203
- const materialDef = parser.json.materials[ materialIndex ];
50204
-
50205
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
50182
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50206
50183
 
50207
- return Promise.resolve();
50208
-
50209
- }
50184
+ if ( extension === null ) return Promise.resolve();
50210
50185
 
50211
50186
  const pending = [];
50212
50187
 
@@ -50214,8 +50189,6 @@
50214
50189
  materialParams.sheenRoughness = 0;
50215
50190
  materialParams.sheen = 1;
50216
50191
 
50217
- const extension = materialDef.extensions[ this.name ];
50218
-
50219
50192
  if ( extension.sheenColorFactor !== undefined ) {
50220
50193
 
50221
50194
  const colorFactor = extension.sheenColorFactor;
@@ -50231,13 +50204,13 @@
50231
50204
 
50232
50205
  if ( extension.sheenColorTexture !== undefined ) {
50233
50206
 
50234
- pending.push( parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture, THREE.SRGBColorSpace ) );
50207
+ pending.push( this.parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture, THREE.SRGBColorSpace ) );
50235
50208
 
50236
50209
  }
50237
50210
 
50238
50211
  if ( extension.sheenRoughnessTexture !== undefined ) {
50239
50212
 
50240
- pending.push( parser.assignTexture( materialParams, 'sheenRoughnessMap', extension.sheenRoughnessTexture ) );
50213
+ pending.push( this.parser.assignTexture( materialParams, 'sheenRoughnessMap', extension.sheenRoughnessTexture ) );
50241
50214
 
50242
50215
  }
50243
50216
 
@@ -50266,30 +50239,20 @@
50266
50239
 
50267
50240
  getMaterialType( materialIndex ) {
50268
50241
 
50269
- const parser = this.parser;
50270
- const materialDef = parser.json.materials[ materialIndex ];
50271
-
50272
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
50242
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50273
50243
 
50274
- return THREE.MeshPhysicalMaterial;
50244
+ return extension !== null ? THREE.MeshPhysicalMaterial : null;
50275
50245
 
50276
50246
  }
50277
50247
 
50278
50248
  extendMaterialParams( materialIndex, materialParams ) {
50279
50249
 
50280
- const parser = this.parser;
50281
- const materialDef = parser.json.materials[ materialIndex ];
50282
-
50283
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
50250
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50284
50251
 
50285
- return Promise.resolve();
50286
-
50287
- }
50252
+ if ( extension === null ) return Promise.resolve();
50288
50253
 
50289
50254
  const pending = [];
50290
50255
 
50291
- const extension = materialDef.extensions[ this.name ];
50292
-
50293
50256
  if ( extension.transmissionFactor !== undefined ) {
50294
50257
 
50295
50258
  materialParams.transmission = extension.transmissionFactor;
@@ -50298,7 +50261,7 @@
50298
50261
 
50299
50262
  if ( extension.transmissionTexture !== undefined ) {
50300
50263
 
50301
- pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) );
50264
+ pending.push( this.parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) );
50302
50265
 
50303
50266
  }
50304
50267
 
@@ -50326,35 +50289,25 @@
50326
50289
 
50327
50290
  getMaterialType( materialIndex ) {
50328
50291
 
50329
- const parser = this.parser;
50330
- const materialDef = parser.json.materials[ materialIndex ];
50331
-
50332
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
50292
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50333
50293
 
50334
- return THREE.MeshPhysicalMaterial;
50294
+ return extension !== null ? THREE.MeshPhysicalMaterial : null;
50335
50295
 
50336
50296
  }
50337
50297
 
50338
50298
  extendMaterialParams( materialIndex, materialParams ) {
50339
50299
 
50340
- const parser = this.parser;
50341
- const materialDef = parser.json.materials[ materialIndex ];
50342
-
50343
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
50300
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50344
50301
 
50345
- return Promise.resolve();
50346
-
50347
- }
50302
+ if ( extension === null ) return Promise.resolve();
50348
50303
 
50349
50304
  const pending = [];
50350
50305
 
50351
- const extension = materialDef.extensions[ this.name ];
50352
-
50353
50306
  materialParams.thickness = extension.thicknessFactor !== undefined ? extension.thicknessFactor : 0;
50354
50307
 
50355
50308
  if ( extension.thicknessTexture !== undefined ) {
50356
50309
 
50357
- pending.push( parser.assignTexture( materialParams, 'thicknessMap', extension.thicknessTexture ) );
50310
+ pending.push( this.parser.assignTexture( materialParams, 'thicknessMap', extension.thicknessTexture ) );
50358
50311
 
50359
50312
  }
50360
50313
 
@@ -50387,30 +50340,22 @@
50387
50340
 
50388
50341
  getMaterialType( materialIndex ) {
50389
50342
 
50390
- const parser = this.parser;
50391
- const materialDef = parser.json.materials[ materialIndex ];
50392
-
50393
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
50343
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50394
50344
 
50395
- return THREE.MeshPhysicalMaterial;
50345
+ return extension !== null ? THREE.MeshPhysicalMaterial : null;
50396
50346
 
50397
50347
  }
50398
50348
 
50399
50349
  extendMaterialParams( materialIndex, materialParams ) {
50400
50350
 
50401
- const parser = this.parser;
50402
- const materialDef = parser.json.materials[ materialIndex ];
50403
-
50404
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
50405
-
50406
- return Promise.resolve();
50351
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50407
50352
 
50408
- }
50409
-
50410
- const extension = materialDef.extensions[ this.name ];
50353
+ if ( extension === null ) return Promise.resolve();
50411
50354
 
50412
50355
  materialParams.ior = extension.ior !== undefined ? extension.ior : 1.5;
50413
50356
 
50357
+ if ( materialParams.ior === 0 ) materialParams.ior = 1000; // see #26167
50358
+
50414
50359
  return Promise.resolve();
50415
50360
 
50416
50361
  }
@@ -50435,35 +50380,25 @@
50435
50380
 
50436
50381
  getMaterialType( materialIndex ) {
50437
50382
 
50438
- const parser = this.parser;
50439
- const materialDef = parser.json.materials[ materialIndex ];
50440
-
50441
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
50383
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50442
50384
 
50443
- return THREE.MeshPhysicalMaterial;
50385
+ return extension !== null ? THREE.MeshPhysicalMaterial : null;
50444
50386
 
50445
50387
  }
50446
50388
 
50447
50389
  extendMaterialParams( materialIndex, materialParams ) {
50448
50390
 
50449
- const parser = this.parser;
50450
- const materialDef = parser.json.materials[ materialIndex ];
50451
-
50452
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
50453
-
50454
- return Promise.resolve();
50391
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50455
50392
 
50456
- }
50393
+ if ( extension === null ) return Promise.resolve();
50457
50394
 
50458
50395
  const pending = [];
50459
50396
 
50460
- const extension = materialDef.extensions[ this.name ];
50461
-
50462
50397
  materialParams.specularIntensity = extension.specularFactor !== undefined ? extension.specularFactor : 1.0;
50463
50398
 
50464
50399
  if ( extension.specularTexture !== undefined ) {
50465
50400
 
50466
- pending.push( parser.assignTexture( materialParams, 'specularIntensityMap', extension.specularTexture ) );
50401
+ pending.push( this.parser.assignTexture( materialParams, 'specularIntensityMap', extension.specularTexture ) );
50467
50402
 
50468
50403
  }
50469
50404
 
@@ -50472,7 +50407,7 @@
50472
50407
 
50473
50408
  if ( extension.specularColorTexture !== undefined ) {
50474
50409
 
50475
- pending.push( parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture, THREE.SRGBColorSpace ) );
50410
+ pending.push( this.parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture, THREE.SRGBColorSpace ) );
50476
50411
 
50477
50412
  }
50478
50413
 
@@ -50501,35 +50436,25 @@
50501
50436
 
50502
50437
  getMaterialType( materialIndex ) {
50503
50438
 
50504
- const parser = this.parser;
50505
- const materialDef = parser.json.materials[ materialIndex ];
50439
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50506
50440
 
50507
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
50508
-
50509
- return THREE.MeshPhysicalMaterial;
50441
+ return extension !== null ? THREE.MeshPhysicalMaterial : null;
50510
50442
 
50511
50443
  }
50512
50444
 
50513
50445
  extendMaterialParams( materialIndex, materialParams ) {
50514
50446
 
50515
- const parser = this.parser;
50516
- const materialDef = parser.json.materials[ materialIndex ];
50517
-
50518
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
50447
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50519
50448
 
50520
- return Promise.resolve();
50521
-
50522
- }
50449
+ if ( extension === null ) return Promise.resolve();
50523
50450
 
50524
50451
  const pending = [];
50525
50452
 
50526
- const extension = materialDef.extensions[ this.name ];
50527
-
50528
50453
  materialParams.bumpScale = extension.bumpFactor !== undefined ? extension.bumpFactor : 1.0;
50529
50454
 
50530
50455
  if ( extension.bumpTexture !== undefined ) {
50531
50456
 
50532
- pending.push( parser.assignTexture( materialParams, 'bumpMap', extension.bumpTexture ) );
50457
+ pending.push( this.parser.assignTexture( materialParams, 'bumpMap', extension.bumpTexture ) );
50533
50458
 
50534
50459
  }
50535
50460
 
@@ -50557,30 +50482,20 @@
50557
50482
 
50558
50483
  getMaterialType( materialIndex ) {
50559
50484
 
50560
- const parser = this.parser;
50561
- const materialDef = parser.json.materials[ materialIndex ];
50562
-
50563
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
50485
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50564
50486
 
50565
- return THREE.MeshPhysicalMaterial;
50487
+ return extension !== null ? THREE.MeshPhysicalMaterial : null;
50566
50488
 
50567
50489
  }
50568
50490
 
50569
50491
  extendMaterialParams( materialIndex, materialParams ) {
50570
50492
 
50571
- const parser = this.parser;
50572
- const materialDef = parser.json.materials[ materialIndex ];
50573
-
50574
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
50493
+ const extension = getMaterialExtension( this.parser, materialIndex, this.name );
50575
50494
 
50576
- return Promise.resolve();
50577
-
50578
- }
50495
+ if ( extension === null ) return Promise.resolve();
50579
50496
 
50580
50497
  const pending = [];
50581
50498
 
50582
- const extension = materialDef.extensions[ this.name ];
50583
-
50584
50499
  if ( extension.anisotropyStrength !== undefined ) {
50585
50500
 
50586
50501
  materialParams.anisotropy = extension.anisotropyStrength;
@@ -50595,7 +50510,7 @@
50595
50510
 
50596
50511
  if ( extension.anisotropyTexture !== undefined ) {
50597
50512
 
50598
- pending.push( parser.assignTexture( materialParams, 'anisotropyMap', extension.anisotropyTexture ) );
50513
+ pending.push( this.parser.assignTexture( materialParams, 'anisotropyMap', extension.anisotropyTexture ) );
50599
50514
 
50600
50515
  }
50601
50516
 
@@ -50761,9 +50676,9 @@
50761
50676
  */
50762
50677
  class GLTFMeshoptCompression {
50763
50678
 
50764
- constructor( parser ) {
50679
+ constructor( parser, name ) {
50765
50680
 
50766
- this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION;
50681
+ this.name = name;
50767
50682
  this.parser = parser;
50768
50683
 
50769
50684
  }
@@ -51737,7 +51652,7 @@
51737
51652
  let isFirefox = false;
51738
51653
  let firefoxVersion = -1;
51739
51654
 
51740
- if ( typeof navigator !== 'undefined' ) {
51655
+ if ( typeof navigator !== 'undefined' && typeof navigator.userAgent !== 'undefined' ) {
51741
51656
 
51742
51657
  const userAgent = navigator.userAgent;
51743
51658
 
@@ -53394,6 +53309,28 @@
53394
53309
 
53395
53310
  }
53396
53311
 
53312
+ // Reconstruct pivot from container pattern created by GLTFExporter
53313
+ // The container has position+pivot, rotation, scale; child has -pivot offset and mesh
53314
+ if ( node.userData.pivot !== undefined && children.length > 0 ) {
53315
+
53316
+ const pivot = node.userData.pivot;
53317
+ const pivotChild = children[ 0 ];
53318
+
53319
+ // Set pivot on container and adjust transforms
53320
+ node.pivot = new THREE.Vector3().fromArray( pivot );
53321
+
53322
+ // Adjust container position: stored as position + pivot, so subtract pivot
53323
+ node.position.x -= pivot[ 0 ];
53324
+ node.position.y -= pivot[ 1 ];
53325
+ node.position.z -= pivot[ 2 ];
53326
+
53327
+ // Remove the child's -pivot offset since pivot now handles it
53328
+ pivotChild.position.set( 0, 0, 0 );
53329
+
53330
+ delete node.userData.pivot;
53331
+
53332
+ }
53333
+
53397
53334
  return node;
53398
53335
 
53399
53336
  } );
@@ -53585,7 +53522,20 @@
53585
53522
 
53586
53523
  for ( let i = 0, il = nodes.length; i < il; i ++ ) {
53587
53524
 
53588
- scene.add( nodes[ i ] );
53525
+ const node = nodes[ i ];
53526
+
53527
+ // If the node already has a parent, it means it's being reused across multiple scenes.
53528
+ // Clone it to avoid the second scene's add() removing it from the first scene.
53529
+ // See: https://github.com/mrdoob/three.js/issues/27993
53530
+ if ( node.parent !== null ) {
53531
+
53532
+ scene.add( clone( node ) );
53533
+
53534
+ } else {
53535
+
53536
+ scene.add( node );
53537
+
53538
+ }
53589
53539
 
53590
53540
  }
53591
53541
 
@@ -53636,17 +53586,28 @@
53636
53586
  const targetName = node.name ? node.name : node.uuid;
53637
53587
  const targetNames = [];
53638
53588
 
53589
+ function collectMorphTargets( object ) {
53590
+
53591
+ if ( object.morphTargetInfluences ) {
53592
+
53593
+ targetNames.push( object.name ? object.name : object.uuid );
53594
+
53595
+ }
53596
+
53597
+ }
53598
+
53599
+
53639
53600
  if ( PATH_PROPERTIES$1[ target.path ] === PATH_PROPERTIES$1.weights ) {
53640
53601
 
53641
- node.traverse( function ( object ) {
53602
+ collectMorphTargets( node );
53642
53603
 
53643
- if ( object.morphTargetInfluences ) {
53604
+ // for multi-primitive meshes, the node is a Group containing the sub-meshes
53644
53605
 
53645
- targetNames.push( object.name ? object.name : object.uuid );
53606
+ if ( node.isGroup ) {
53646
53607
 
53647
- }
53608
+ node.children.forEach( collectMorphTargets );
53648
53609
 
53649
- } );
53610
+ }
53650
53611
 
53651
53612
  } else {
53652
53613
 
@@ -57174,7 +57135,8 @@
57174
57135
 
57175
57136
  }
57176
57137
 
57177
- // the transparency handling is implemented based on Blender/Unity's approach: https://github.com/sobotka/blender-addons/blob/7d80f2f97161fc8e353a657b179b9aa1f8e5280b/io_scene_fbx/import_fbx.py#L1444-L1459
57138
+ // the transparency handling is implemented based on Blender's approach:
57139
+ // https://github.com/blender/blender/blob/main/scripts/addons_core/io_scene_fbx/import_fbx.py
57178
57140
 
57179
57141
  parameters.opacity = 1 - ( materialNode.TransparencyFactor ? parseFloat( materialNode.TransparencyFactor.value ) : 0 );
57180
57142
 
@@ -57184,7 +57146,10 @@
57184
57146
 
57185
57147
  if ( parameters.opacity === null ) {
57186
57148
 
57187
- parameters.opacity = 1 - ( materialNode.TransparentColor ? parseFloat( materialNode.TransparentColor.value[ 0 ] ) : 0 );
57149
+ // Default to opaque. Some exporters (e.g. 3ds Max) define TransparentColor
57150
+ // as white (1,1,1) without intending transparency, which makes the Unity-style
57151
+ // fallback of `1 - TransparentColor.r` produce incorrect zero opacity.
57152
+ parameters.opacity = 1;
57188
57153
 
57189
57154
  }
57190
57155
 
@@ -57397,8 +57362,6 @@
57397
57362
  indices: [],
57398
57363
  weights: [],
57399
57364
  transformLink: new THREE.Matrix4().fromArray( boneNode.TransformLink.a ),
57400
- // transform: new Matrix4().fromArray( boneNode.Transform.a ),
57401
- // linkMode: boneNode.Mode,
57402
57365
 
57403
57366
  };
57404
57367
 
@@ -57491,8 +57454,6 @@
57491
57454
 
57492
57455
  } );
57493
57456
 
57494
- this.bindSkeleton( deformers.skeletons, geometryMap, modelMap );
57495
-
57496
57457
  this.addGlobalSceneSettings();
57497
57458
 
57498
57459
  sceneGraph.traverse( function ( node ) {
@@ -57515,6 +57476,64 @@
57515
57476
 
57516
57477
  } );
57517
57478
 
57479
+ // Like Blender's FBX importer, use the BindPose section to set the
57480
+ // rest pose for bones that are not part of a skin cluster. The BindPose
57481
+ // provides a more authoritative rest pose than the Lcl properties which
57482
+ // may represent an animation frame rather than the true rest state.
57483
+ // Bones WITH clusters will get their bind pose from TransformLink
57484
+ // (set via bindSkeleton below), which takes priority.
57485
+ const bindPoseMatrices = this.parsePoseNodes();
57486
+ const clusterBoneIDs = new Set();
57487
+
57488
+ for ( const ID in deformers.skeletons ) {
57489
+
57490
+ deformers.skeletons[ ID ].rawBones.forEach( function ( _, i ) {
57491
+
57492
+ const bone = deformers.skeletons[ ID ].bones[ i ];
57493
+ if ( bone ) clusterBoneIDs.add( bone.ID );
57494
+
57495
+ } );
57496
+
57497
+ }
57498
+
57499
+ const tempMatrix = new THREE.Matrix4();
57500
+
57501
+ sceneGraph.traverse( function ( node ) {
57502
+
57503
+ if ( node.isBone && node.ID !== undefined && ! clusterBoneIDs.has( node.ID ) ) {
57504
+
57505
+ const bindPose = bindPoseMatrices[ node.ID ];
57506
+
57507
+ if ( bindPose !== undefined ) {
57508
+
57509
+ if ( node.parent ) {
57510
+
57511
+ tempMatrix.copy( node.parent.matrixWorld ).invert();
57512
+ tempMatrix.multiply( bindPose );
57513
+
57514
+ } else {
57515
+
57516
+ tempMatrix.copy( bindPose );
57517
+
57518
+ }
57519
+
57520
+ tempMatrix.decompose( node.position, node.quaternion, node.scale );
57521
+ node.updateMatrix();
57522
+ node.matrixWorld.copy( bindPose );
57523
+
57524
+ }
57525
+
57526
+ }
57527
+
57528
+ } );
57529
+
57530
+ // Bind skeletons after transforms are applied so that bind matrices
57531
+ // are computed from the final scene state. This ensures the rest pose
57532
+ // is correct even when the FBX file's Cluster TransformLink matrices
57533
+ // differ from the reconstructed bone transforms (common in files
57534
+ // without a BindPose section).
57535
+ this.bindSkeleton( deformers.skeletons, geometryMap, modelMap );
57536
+
57518
57537
  const animations = new AnimationParser().parse();
57519
57538
 
57520
57539
  // if all the models where already combined in a single group, just return that
@@ -57527,6 +57546,24 @@
57527
57546
 
57528
57547
  sceneGraph.animations = animations;
57529
57548
 
57549
+ // Apply coordinate system correction. FBX files can use different
57550
+ // up-axis conventions (Y-up or Z-up). Three.js uses Y-up, so rotate
57551
+ // the scene when the file uses Z-up (UpAxis === 2).
57552
+
57553
+ if ( 'GlobalSettings' in fbxTree && 'UpAxis' in fbxTree.GlobalSettings ) {
57554
+
57555
+ const upAxis = fbxTree.GlobalSettings.UpAxis.value;
57556
+
57557
+ if ( upAxis === 2 ) {
57558
+
57559
+ console.warn( 'THREE.FBXLoader: You are loading an asset with a Z-UP coordinate system. The loader just rotates the asset to transform it into Y-UP. The vertex data are not converted.' );
57560
+
57561
+ sceneGraph.rotation.set( - Math.PI / 2, 0, 0 );
57562
+
57563
+ }
57564
+
57565
+ }
57566
+
57530
57567
  }
57531
57568
 
57532
57569
  // parse nodes in FBXTree.Objects.Model
@@ -57809,21 +57846,24 @@
57809
57846
 
57810
57847
  case 2: // Spot
57811
57848
  let angle = Math.PI / 3;
57849
+ let penumbra = 0;
57812
57850
 
57813
- if ( lightAttribute.InnerAngle !== undefined ) {
57851
+ if ( lightAttribute.OuterAngle !== undefined ) {
57814
57852
 
57815
- angle = THREE.MathUtils.degToRad( lightAttribute.InnerAngle.value );
57853
+ angle = THREE.MathUtils.degToRad( lightAttribute.OuterAngle.value );
57816
57854
 
57817
- }
57855
+ if ( lightAttribute.InnerAngle !== undefined ) {
57818
57856
 
57819
- let penumbra = 0;
57820
- if ( lightAttribute.OuterAngle !== undefined ) {
57857
+ penumbra = 1 - ( lightAttribute.InnerAngle.value / lightAttribute.OuterAngle.value );
57858
+ penumbra = Math.max( 0, penumbra ); // penumbra must be in the range [0,1]
57859
+
57860
+ }
57861
+
57862
+ } else if ( lightAttribute.InnerAngle !== undefined ) {
57821
57863
 
57822
- // TODO: this is not correct - FBX calculates outer and inner angle in degrees
57823
- // with OuterAngle > InnerAngle && OuterAngle <= Math.PI
57824
- // while three.js uses a penumbra between (0, 1) to attenuate the inner angle
57825
- penumbra = THREE.MathUtils.degToRad( lightAttribute.OuterAngle.value );
57826
- penumbra = Math.max( penumbra, 1 );
57864
+ // fallback if only InnerAngle is defined
57865
+
57866
+ angle = THREE.MathUtils.degToRad( lightAttribute.InnerAngle.value );
57827
57867
 
57828
57868
  }
57829
57869
 
@@ -58033,12 +58073,30 @@
58033
58073
 
58034
58074
  bindSkeleton( skeletons, geometryMap, modelMap ) {
58035
58075
 
58036
- const bindMatrices = this.parsePoseNodes();
58037
-
58038
58076
  for ( const ID in skeletons ) {
58039
58077
 
58040
58078
  const skeleton = skeletons[ ID ];
58041
58079
 
58080
+ // Compute bone inverses from TransformLink rather than from the
58081
+ // bones' current matrixWorld. The TransformLink matrices represent
58082
+ // each bone's global transform at the time the skin weights were
58083
+ // painted, which may differ from the scene-reconstructed transforms.
58084
+ const boneInverses = [];
58085
+
58086
+ for ( let i = 0, l = skeleton.bones.length; i < l; i ++ ) {
58087
+
58088
+ const inverse = new THREE.Matrix4();
58089
+
58090
+ if ( skeleton.bones[ i ] && skeleton.rawBones[ i ] ) {
58091
+
58092
+ inverse.copy( skeleton.rawBones[ i ].transformLink ).invert();
58093
+
58094
+ }
58095
+
58096
+ boneInverses.push( inverse );
58097
+
58098
+ }
58099
+
58042
58100
  const parents = connections.get( parseInt( skeleton.ID ) ).parents;
58043
58101
 
58044
58102
  parents.forEach( function ( parent ) {
@@ -58054,7 +58112,16 @@
58054
58112
 
58055
58113
  const model = modelMap.get( geoConnParent.ID );
58056
58114
 
58057
- model.bind( new THREE.Skeleton( skeleton.bones ), bindMatrices[ geoConnParent.ID ] );
58115
+ // Use the mesh's current matrixWorld as bind matrix.
58116
+ // The BindPose section is intentionally not used here
58117
+ // since it may contain scale/rotation from the model
58118
+ // hierarchy that is inconsistent with the TransformLink-
58119
+ // based bone inverses. Always provide a bind matrix to
58120
+ // prevent bind() from calling calculateInverses() which
58121
+ // would overwrite the bone inverses computed above.
58122
+ model.updateMatrixWorld( true );
58123
+
58124
+ model.bind( new THREE.Skeleton( skeleton.bones, boneInverses ), model.matrixWorld );
58058
58125
 
58059
58126
  }
58060
58127
 
@@ -58068,6 +58135,7 @@
58068
58135
 
58069
58136
  }
58070
58137
 
58138
+ // Parse BindPose nodes and return a map of node ID to bind matrix.
58071
58139
  parsePoseNodes() {
58072
58140
 
58073
58141
  const bindMatrices = {};
@@ -58830,6 +58898,13 @@
58830
58898
  parentGeo.morphAttributes.position = [];
58831
58899
  // parentGeo.morphAttributes.normal = []; // not implemented
58832
58900
 
58901
+ // Morph attribute positions are stored as deltas (morphTargetsRelative = true), so the
58902
+ // translation component of the geometric transform must not be applied to them — only the
58903
+ // rotation/scale part. Otherwise every delta gets the geometric translation added, which
58904
+ // shifts morphed vertices away from their intended position by `weight * translation` as
58905
+ // the influence increases.
58906
+ const morphPreTransform = preTransform.clone().setPosition( 0, 0, 0 );
58907
+
58833
58908
  const scope = this;
58834
58909
  morphTargets.forEach( function ( morphTarget ) {
58835
58910
 
@@ -58839,7 +58914,7 @@
58839
58914
 
58840
58915
  if ( morphGeoNode !== undefined ) {
58841
58916
 
58842
- scope.genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, preTransform, rawTarget.name );
58917
+ scope.genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, morphPreTransform, rawTarget.name );
58843
58918
 
58844
58919
  }
58845
58920
 
@@ -59234,11 +59309,15 @@
59234
59309
 
59235
59310
  if ( layerCurveNodes[ i ] === undefined ) {
59236
59311
 
59237
- const modelID = connections.get( child.ID ).parents.filter( function ( parent ) {
59312
+ const filteredParents = connections.get( child.ID ).parents.filter( function ( parent ) {
59238
59313
 
59239
59314
  return parent.relationship !== undefined;
59240
59315
 
59241
- } )[ 0 ].ID;
59316
+ } );
59317
+
59318
+ if ( filteredParents.length === 0 ) return;
59319
+
59320
+ const modelID = filteredParents[ 0 ].ID;
59242
59321
 
59243
59322
  if ( modelID !== undefined ) {
59244
59323
 
@@ -59267,7 +59346,13 @@
59267
59346
 
59268
59347
  node.transform = child.matrix;
59269
59348
 
59270
- if ( child.userData.transformData ) node.eulerOrder = child.userData.transformData.eulerOrder;
59349
+ if ( child.userData.transformData ) {
59350
+
59351
+ node.eulerOrder = child.userData.transformData.eulerOrder;
59352
+
59353
+ if ( child.userData.transformData.rotation ) node.initialRotation = child.userData.transformData.rotation;
59354
+
59355
+ }
59271
59356
 
59272
59357
  }
59273
59358
 
@@ -59292,11 +59377,15 @@
59292
59377
 
59293
59378
  if ( layerCurveNodes[ i ] === undefined ) {
59294
59379
 
59295
- const deformerID = connections.get( child.ID ).parents.filter( function ( parent ) {
59380
+ const filteredParents = connections.get( child.ID ).parents.filter( function ( parent ) {
59296
59381
 
59297
59382
  return parent.relationship !== undefined;
59298
59383
 
59299
- } )[ 0 ].ID;
59384
+ } );
59385
+
59386
+ if ( filteredParents.length === 0 ) return;
59387
+
59388
+ const deformerID = filteredParents[ 0 ].ID;
59300
59389
 
59301
59390
  const morpherID = connections.get( deformerID ).parents[ 0 ].ID;
59302
59391
  const geoID = connections.get( morpherID ).parents[ 0 ].ID;
@@ -59407,7 +59496,7 @@
59407
59496
 
59408
59497
  if ( rawTracks.R !== undefined && Object.keys( rawTracks.R.curves ).length > 0 ) {
59409
59498
 
59410
- const rotationTrack = this.generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, rawTracks.preRotation, rawTracks.postRotation, rawTracks.eulerOrder );
59499
+ const rotationTrack = this.generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, rawTracks.preRotation, rawTracks.postRotation, rawTracks.eulerOrder, rawTracks.initialRotation );
59411
59500
  if ( rotationTrack !== undefined ) tracks.push( rotationTrack );
59412
59501
 
59413
59502
  }
@@ -59439,17 +59528,33 @@
59439
59528
 
59440
59529
  }
59441
59530
 
59442
- generateRotationTrack( modelName, curves, preRotation, postRotation, eulerOrder ) {
59531
+ generateRotationTrack( modelName, curves, preRotation, postRotation, eulerOrder, initialRotation ) {
59443
59532
 
59444
59533
  let times;
59445
59534
  let values;
59446
59535
 
59447
- if ( curves.x !== undefined && curves.y !== undefined && curves.z !== undefined ) {
59536
+ if ( curves.x !== undefined || curves.y !== undefined || curves.z !== undefined ) {
59448
59537
 
59449
- const result = this.interpolateRotations( curves.x, curves.y, curves.z, eulerOrder );
59538
+ // Get merged, sorted, unique times from all available curves
59539
+ const mergedTimes = this.getTimesForAllAxes( curves );
59450
59540
 
59451
- times = result[ 0 ];
59452
- values = result[ 1 ];
59541
+ if ( mergedTimes.length > 0 ) {
59542
+
59543
+ const initialRot = initialRotation || [ 0, 0, 0 ];
59544
+
59545
+ // Synchronize all curves to the merged time array.
59546
+ // Missing axes are filled with constant values from the initial rotation (Lcl Rotation).
59547
+ // Existing curves at different times are linearly interpolated.
59548
+ const syncX = this.synchronizeCurve( curves.x, mergedTimes, initialRot[ 0 ] );
59549
+ const syncY = this.synchronizeCurve( curves.y, mergedTimes, initialRot[ 1 ] );
59550
+ const syncZ = this.synchronizeCurve( curves.z, mergedTimes, initialRot[ 2 ] );
59551
+
59552
+ const result = this.interpolateRotations( syncX, syncY, syncZ, eulerOrder );
59553
+
59554
+ times = result[ 0 ];
59555
+ values = result[ 1 ];
59556
+
59557
+ }
59453
59558
 
59454
59559
  }
59455
59560
 
@@ -59481,7 +59586,7 @@
59481
59586
 
59482
59587
  const quaternionValues = [];
59483
59588
 
59484
- if ( ! values || ! times ) return new THREE.QuaternionKeyframeTrack( modelName + '.quaternion', [ 0 ], [ 0 ] );
59589
+ if ( ! values || ! times ) return undefined;
59485
59590
 
59486
59591
  for ( let i = 0; i < values.length; i += 3 ) {
59487
59592
 
@@ -59634,6 +59739,62 @@
59634
59739
 
59635
59740
  }
59636
59741
 
59742
+ // Synchronize a curve to a target time array using linear interpolation.
59743
+ // If the curve is undefined (axis not animated), returns constant values from initialValue.
59744
+ synchronizeCurve( curve, targetTimes, initialValue ) {
59745
+
59746
+ if ( curve === undefined ) {
59747
+
59748
+ return { times: targetTimes, values: targetTimes.map( () => initialValue ) };
59749
+
59750
+ }
59751
+
59752
+ // If the curve already has the same number of keyframes as the target, assume times match
59753
+ if ( curve.times.length === targetTimes.length ) return curve;
59754
+
59755
+ // Linearly interpolate curve values at each target time
59756
+ const values = [];
59757
+
59758
+ for ( let i = 0; i < targetTimes.length; i ++ ) {
59759
+
59760
+ values.push( this.sampleCurveValue( curve, targetTimes[ i ], initialValue ) );
59761
+
59762
+ }
59763
+
59764
+ return { times: targetTimes, values: values };
59765
+
59766
+ }
59767
+
59768
+ // Sample a single value from a curve at a given time using linear interpolation
59769
+ sampleCurveValue( curve, time, initialValue ) {
59770
+
59771
+ const times = curve.times;
59772
+ const values = curve.values;
59773
+
59774
+ // Before first keyframe
59775
+ if ( time <= times[ 0 ] ) return values[ 0 ];
59776
+
59777
+ // After last keyframe
59778
+ if ( time >= times[ times.length - 1 ] ) return values[ values.length - 1 ];
59779
+
59780
+ // Find surrounding keyframes and linearly interpolate
59781
+ for ( let i = 0; i < times.length - 1; i ++ ) {
59782
+
59783
+ if ( time >= times[ i ] && time <= times[ i + 1 ] ) {
59784
+
59785
+ if ( times[ i ] === time ) return values[ i ];
59786
+
59787
+ const alpha = ( time - times[ i ] ) / ( times[ i + 1 ] - times[ i ] );
59788
+ return values[ i ] * ( 1 - alpha ) + values[ i + 1 ] * alpha;
59789
+
59790
+ }
59791
+
59792
+ }
59793
+
59794
+ return initialValue;
59795
+
59796
+ }
59797
+
59637
59798
  // Rotations are defined as Euler angles which can have values of any size
59638
59799
  // These will be converted to quaternions which don't support values greater than
59639
59800
  // PI, so we'll interpolate large rotations
@@ -59703,7 +59864,7 @@
59703
59864
  const Q2 = new THREE.Quaternion().setFromEuler( E2 );
59704
59865
 
59705
59866
  // Check unroll
59706
- if ( Q1.dot( Q2 ) ) {
59867
+ if ( Q1.dot( Q2 ) < 0 ) {
59707
59868
 
59708
59869
  Q2.set( - Q2.x, - Q2.y, - Q2.z, - Q2.w );
59709
59870
 
@@ -61117,7 +61278,7 @@
61117
61278
 
61118
61279
  }
61119
61280
 
61120
- const t=0,n=2,u=1,y=2,S=0,E=1,X=10,it=0,ht=9,gt=15,xt=16,dt=22,Ft=37,Et=43,te=76,ae=83,ue=97,ye=100,de=103,Ae=109,We=122,He=123,qe=131,Je=132,Qe=133,Ze=134,en=137,nn=138,sn=139,an=140,rn=141,on=142,cn=145,Un=146,pn=148,xn=152,yn=153,bn=154,mn=155,dn=156,Dn=157,wn=158,Vn=165,Cn=166,ri=1000054e3,oi=1000054001,ci=1000054004,Ui=1000054005,_i=1000066e3,yi=1000066004;class Ci{constructor(t,e,n,i){this._dataView=void 0,this._littleEndian=void 0,this._offset=void 0,this._dataView=new DataView(t.buffer,t.byteOffset+e,n),this._littleEndian=i,this._offset=0;}_nextUint8(){const t=this._dataView.getUint8(this._offset);return this._offset+=1,t}_nextUint16(){const t=this._dataView.getUint16(this._offset,this._littleEndian);return this._offset+=2,t}_nextUint32(){const t=this._dataView.getUint32(this._offset,this._littleEndian);return this._offset+=4,t}_nextUint64(){const t=this._dataView.getUint32(this._offset,this._littleEndian)+2**32*this._dataView.getUint32(this._offset+4,this._littleEndian);return this._offset+=8,t}_nextInt32(){const t=this._dataView.getInt32(this._offset,this._littleEndian);return this._offset+=4,t}_nextUint8Array(t){const e=new Uint8Array(this._dataView.buffer,this._dataView.byteOffset+this._offset,t);return this._offset+=t,e}_skip(t){return this._offset+=t,this}_scan(t,e=0){const n=this._offset;let i=0;for(;this._dataView.getUint8(this._offset)!==e&&i<t;)i++,this._offset++;return i<t&&this._offset++,new Uint8Array(this._dataView.buffer,this._dataView.byteOffset+n,i)}}const Oi=[171,75,84,88,32,50,48,187,13,10,26,10];function Si(t){return (new TextDecoder).decode(t)}function Mi(t){const e=new Uint8Array(t.buffer,t.byteOffset,Oi.length);if(e[0]!==Oi[0]||e[1]!==Oi[1]||e[2]!==Oi[2]||e[3]!==Oi[3]||e[4]!==Oi[4]||e[5]!==Oi[5]||e[6]!==Oi[6]||e[7]!==Oi[7]||e[8]!==Oi[8]||e[9]!==Oi[9]||e[10]!==Oi[10]||e[11]!==Oi[11])throw new Error("Missing KTX 2.0 identifier.");const n={vkFormat:0,typeSize:1,pixelWidth:0,pixelHeight:0,pixelDepth:0,layerCount:0,faceCount:1,levelCount:0,supercompressionScheme:0,levels:[],dataFormatDescriptor:[{vendorId:0,descriptorType:0,versionNumber:2,colorModel:0,colorPrimaries:1,transferFunction:2,flags:0,texelBlockDimension:[0,0,0,0],bytesPlane:[0,0,0,0,0,0,0,0],samples:[]}],keyValue:{},globalData:null},i=17*Uint32Array.BYTES_PER_ELEMENT,s=new Ci(t,Oi.length,i,true);n.vkFormat=s._nextUint32(),n.typeSize=s._nextUint32(),n.pixelWidth=s._nextUint32(),n.pixelHeight=s._nextUint32(),n.pixelDepth=s._nextUint32(),n.layerCount=s._nextUint32(),n.faceCount=s._nextUint32(),n.levelCount=s._nextUint32(),n.supercompressionScheme=s._nextUint32();const a=s._nextUint32(),r=s._nextUint32(),o=s._nextUint32(),l=s._nextUint32(),f=s._nextUint64(),c=s._nextUint64(),U=3*Math.max(n.levelCount,1)*8,h=new Ci(t,Oi.length+i,U,true);for(let e=0,i=Math.max(n.levelCount,1);e<i;e++)n.levels.push({levelData:new Uint8Array(t.buffer,t.byteOffset+h._nextUint64(),h._nextUint64()),uncompressedByteLength:h._nextUint64()});const p=new Ci(t,a,r,true);p._skip(4);const _=p._nextUint16(),u=p._nextUint16(),g=p._nextUint16(),x=p._nextUint16(),y={vendorId:_,descriptorType:u,versionNumber:g,colorModel:p._nextUint8(),colorPrimaries:p._nextUint8(),transferFunction:p._nextUint8(),flags:p._nextUint8(),texelBlockDimension:[p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8()],bytesPlane:[p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8()],samples:[]},b=(x/4-6)/4;for(let t=0;t<b;t++){const e={bitOffset:p._nextUint16(),bitLength:p._nextUint8(),channelType:p._nextUint8(),samplePosition:[p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8()],sampleLower:Number.NEGATIVE_INFINITY,sampleUpper:Number.POSITIVE_INFINITY};64&e.channelType?(e.sampleLower=p._nextInt32(),e.sampleUpper=p._nextInt32()):(e.sampleLower=p._nextUint32(),e.sampleUpper=p._nextUint32()),y.samples[t]=e;}n.dataFormatDescriptor.length=0,n.dataFormatDescriptor.push(y);const m=new Ci(t,o,l,true);for(;m._offset<l;){const t=m._nextUint32(),e=m._scan(t),i=Si(e);if(n.keyValue[i]=m._nextUint8Array(t-e.byteLength-1),i.match(/^ktx/i)){const t=Si(n.keyValue[i]);n.keyValue[i]=t.substring(0,t.lastIndexOf("\0"));}m._skip(t%4?4-t%4:0);}if(c<=0)return n;const d=new Ci(t,f,c,true),D=d._nextUint16(),w=d._nextUint16(),v=d._nextUint32(),B=d._nextUint32(),L=d._nextUint32(),A=d._nextUint32(),k=[];for(let t=0,e=Math.max(n.levelCount,1);t<e;t++)k.push({imageFlags:d._nextUint32(),rgbSliceByteOffset:d._nextUint32(),rgbSliceByteLength:d._nextUint32(),alphaSliceByteOffset:d._nextUint32(),alphaSliceByteLength:d._nextUint32()});const I=f+d._offset,V=I+v,C=V+B,F=C+L,O=new Uint8Array(t.buffer,t.byteOffset+I,v),T=new Uint8Array(t.buffer,t.byteOffset+V,B),S=new Uint8Array(t.buffer,t.byteOffset+C,L),E=new Uint8Array(t.buffer,t.byteOffset+F,A);return n.globalData={endpointCount:D,selectorCount:w,imageDescs:k,endpointsData:O,selectorsData:T,tablesData:S,extendedData:E},n}
61281
+ const t=0,n=2,u=1,y=2,S=0,E=1,X=10,it=0,ht=9,gt=15,xt=16,dt=22,Ft=37,Et=43,te=76,ae=83,Ue=91,ue=97,ye=100,de=103,Ae=109,We=122,He=123,qe=131,Je=132,Qe=133,Ze=134,en=137,nn=138,sn=139,an=140,rn=141,on=142,cn=145,Un=146,pn=148,xn=152,yn=153,bn=154,mn=155,dn=156,Dn=157,wn=158,Vn=165,Cn=166,ri=1000054e3,oi=1000054001,ci=1000054004,Ui=1000054005,_i=1000066e3,yi=1000066004;class Ci{constructor(t,e,n,i){this._dataView=void 0,this._littleEndian=void 0,this._offset=void 0,this._dataView=new DataView(t.buffer,t.byteOffset+e,n),this._littleEndian=i,this._offset=0;}_nextUint8(){const t=this._dataView.getUint8(this._offset);return this._offset+=1,t}_nextUint16(){const t=this._dataView.getUint16(this._offset,this._littleEndian);return this._offset+=2,t}_nextUint32(){const t=this._dataView.getUint32(this._offset,this._littleEndian);return this._offset+=4,t}_nextUint64(){const t=this._dataView.getUint32(this._offset,this._littleEndian)+2**32*this._dataView.getUint32(this._offset+4,this._littleEndian);return this._offset+=8,t}_nextInt32(){const t=this._dataView.getInt32(this._offset,this._littleEndian);return this._offset+=4,t}_nextUint8Array(t){const e=new Uint8Array(this._dataView.buffer,this._dataView.byteOffset+this._offset,t);return this._offset+=t,e}_skip(t){return this._offset+=t,this}_scan(t,e=0){const n=this._offset;let i=0;for(;this._dataView.getUint8(this._offset)!==e&&i<t;)i++,this._offset++;return i<t&&this._offset++,new Uint8Array(this._dataView.buffer,this._dataView.byteOffset+n,i)}}const Oi=[171,75,84,88,32,50,48,187,13,10,26,10];function Si(t){return (new TextDecoder).decode(t)}function Mi(t){const e=new Uint8Array(t.buffer,t.byteOffset,Oi.length);if(e[0]!==Oi[0]||e[1]!==Oi[1]||e[2]!==Oi[2]||e[3]!==Oi[3]||e[4]!==Oi[4]||e[5]!==Oi[5]||e[6]!==Oi[6]||e[7]!==Oi[7]||e[8]!==Oi[8]||e[9]!==Oi[9]||e[10]!==Oi[10]||e[11]!==Oi[11])throw new Error("Missing KTX 2.0 identifier.");const n={vkFormat:0,typeSize:1,pixelWidth:0,pixelHeight:0,pixelDepth:0,layerCount:0,faceCount:1,levelCount:0,supercompressionScheme:0,levels:[],dataFormatDescriptor:[{vendorId:0,descriptorType:0,versionNumber:2,colorModel:0,colorPrimaries:1,transferFunction:2,flags:0,texelBlockDimension:[0,0,0,0],bytesPlane:[0,0,0,0,0,0,0,0],samples:[]}],keyValue:{},globalData:null},i=17*Uint32Array.BYTES_PER_ELEMENT,s=new Ci(t,Oi.length,i,true);n.vkFormat=s._nextUint32(),n.typeSize=s._nextUint32(),n.pixelWidth=s._nextUint32(),n.pixelHeight=s._nextUint32(),n.pixelDepth=s._nextUint32(),n.layerCount=s._nextUint32(),n.faceCount=s._nextUint32(),n.levelCount=s._nextUint32(),n.supercompressionScheme=s._nextUint32();const a=s._nextUint32(),r=s._nextUint32(),o=s._nextUint32(),l=s._nextUint32(),f=s._nextUint64(),c=s._nextUint64(),U=3*Math.max(n.levelCount,1)*8,h=new Ci(t,Oi.length+i,U,true);for(let e=0,i=Math.max(n.levelCount,1);e<i;e++)n.levels.push({levelData:new Uint8Array(t.buffer,t.byteOffset+h._nextUint64(),h._nextUint64()),uncompressedByteLength:h._nextUint64()});const p=new Ci(t,a,r,true);p._skip(4);const _=p._nextUint16(),u=p._nextUint16(),g=p._nextUint16(),x=p._nextUint16(),y={vendorId:_,descriptorType:u,versionNumber:g,colorModel:p._nextUint8(),colorPrimaries:p._nextUint8(),transferFunction:p._nextUint8(),flags:p._nextUint8(),texelBlockDimension:[p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8()],bytesPlane:[p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8()],samples:[]},b=(x/4-6)/4;for(let t=0;t<b;t++){const e={bitOffset:p._nextUint16(),bitLength:p._nextUint8(),channelType:p._nextUint8(),samplePosition:[p._nextUint8(),p._nextUint8(),p._nextUint8(),p._nextUint8()],sampleLower:Number.NEGATIVE_INFINITY,sampleUpper:Number.POSITIVE_INFINITY};64&e.channelType?(e.sampleLower=p._nextInt32(),e.sampleUpper=p._nextInt32()):(e.sampleLower=p._nextUint32(),e.sampleUpper=p._nextUint32()),y.samples[t]=e;}n.dataFormatDescriptor.length=0,n.dataFormatDescriptor.push(y);const m=new Ci(t,o,l,true);for(;m._offset<l;){const t=m._nextUint32(),e=m._scan(t),i=Si(e);if(n.keyValue[i]=m._nextUint8Array(t-e.byteLength-1),i.match(/^ktx/i)){const t=Si(n.keyValue[i]);n.keyValue[i]=t.substring(0,t.lastIndexOf("\0"));}m._skip(t%4?4-t%4:0);}if(c<=0)return n;const d=new Ci(t,f,c,true),D=d._nextUint16(),w=d._nextUint16(),v=d._nextUint32(),B=d._nextUint32(),L=d._nextUint32(),A=d._nextUint32(),k=[];for(let t=0,e=Math.max(n.levelCount,1);t<e;t++)k.push({imageFlags:d._nextUint32(),rgbSliceByteOffset:d._nextUint32(),rgbSliceByteLength:d._nextUint32(),alphaSliceByteOffset:d._nextUint32(),alphaSliceByteLength:d._nextUint32()});const I=f+d._offset,V=I+v,C=V+B,F=C+L,O=new Uint8Array(t.buffer,t.byteOffset+I,v),T=new Uint8Array(t.buffer,t.byteOffset+V,B),S=new Uint8Array(t.buffer,t.byteOffset+C,L),E=new Uint8Array(t.buffer,t.byteOffset+F,A);return n.globalData={endpointCount:D,selectorCount:w,imageDescs:k,endpointsData:O,selectorsData:T,tablesData:S,extendedData:E},n}
61121
61282
 
61122
61283
  var global$1 = (typeof global !== "undefined" ? global :
61123
61284
  typeof self !== "undefined" ? self :
@@ -63290,6 +63451,7 @@
63290
63451
  };
63291
63452
 
63292
63453
  if ( typeof navigator !== 'undefined' &&
63454
+ typeof navigator.platform !== 'undefined' && typeof navigator.userAgent !== 'undefined' &&
63293
63455
  navigator.platform.indexOf( 'Linux' ) >= 0 && navigator.userAgent.indexOf( 'Firefox' ) >= 0 &&
63294
63456
  this.workerConfig.astcSupported && this.workerConfig.etc2Supported &&
63295
63457
  this.workerConfig.bptcSupported && this.workerConfig.dxtSupported ) {
@@ -64002,6 +64164,8 @@
64002
64164
  [ ae ]: THREE.RGFormat,
64003
64165
  [ te ]: THREE.RedFormat,
64004
64166
 
64167
+ [ Ue ]: THREE.RGBAFormat,
64168
+
64005
64169
  [ Et ]: THREE.RGBAFormat,
64006
64170
  [ Ft ]: THREE.RGBAFormat,
64007
64171
  [ dt ]: THREE.RGFormat,
@@ -64031,8 +64195,8 @@
64031
64195
  [ Je ]: THREE.RGB_S3TC_DXT1_Format,
64032
64196
  [ qe ]: THREE.RGB_S3TC_DXT1_Format,
64033
64197
 
64034
- [ nn ]: THREE.RGBA_S3TC_DXT3_Format,
64035
- [ en ]: THREE.RGBA_S3TC_DXT3_Format,
64198
+ [ nn ]: THREE.RGBA_S3TC_DXT5_Format,
64199
+ [ en ]: THREE.RGBA_S3TC_DXT5_Format,
64036
64200
 
64037
64201
  [ an ]: THREE.SIGNED_RED_RGTC1_Format,
64038
64202
  [ sn ]: THREE.RED_RGTC1_Format,
@@ -64060,6 +64224,8 @@
64060
64224
  [ ae ]: THREE.HalfFloatType,
64061
64225
  [ te ]: THREE.HalfFloatType,
64062
64226
 
64227
+ [ Ue ]: THREE.UnsignedShortType,
64228
+
64063
64229
  [ Et ]: THREE.UnsignedByteType,
64064
64230
  [ Ft ]: THREE.UnsignedByteType,
64065
64231
  [ dt ]: THREE.UnsignedByteType,
@@ -64073,9 +64239,9 @@
64073
64239
  [ xn ]: THREE.UnsignedByteType,
64074
64240
  [ pn ]: THREE.UnsignedByteType,
64075
64241
  [ yn ]: THREE.UnsignedByteType,
64076
- [ yn ]: THREE.UnsignedByteType,
64077
- [ mn ]: THREE.UnsignedByteType,
64242
+ [ bn ]: THREE.UnsignedByteType,
64078
64243
  [ mn ]: THREE.UnsignedByteType,
64244
+ [ dn ]: THREE.UnsignedByteType,
64079
64245
 
64080
64246
  [ _i ]: THREE.HalfFloatType,
64081
64247
  [ wn ]: THREE.UnsignedByteType,
@@ -64187,7 +64353,7 @@
64187
64353
 
64188
64354
  );
64189
64355
 
64190
- } else if ( TYPE_MAP[ vkFormat ] === THREE.HalfFloatType ) {
64356
+ } else if ( TYPE_MAP[ vkFormat ] === THREE.HalfFloatType || TYPE_MAP[ vkFormat ] === THREE.UnsignedShortType ) {
64191
64357
 
64192
64358
  data = new Uint16Array(
64193
64359
 
@@ -64287,14 +64453,14 @@
64287
64453
  }
64288
64454
 
64289
64455
  // This file is part of meshoptimizer library and is distributed under the terms of MIT License.
64290
- // Copyright (C) 2016-2024, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
64456
+ // Copyright (C) 2016-2026, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
64291
64457
  var MeshoptDecoder = (function () {
64292
- // Built with clang version 18.1.2
64293
- // Built from meshoptimizer 0.22
64458
+ // Built with clang version 22.1.0-wasi-sdk
64459
+ // Built from meshoptimizer 1.1
64294
64460
  var wasm_base =
64295
- 'b9H79Tebbbe8Fv9Gbb9Gvuuuuueu9Giuuub9Geueu9Giuuueuikqbeeedddillviebeoweuec:q:Odkr;leDo9TW9T9VV95dbH9F9F939H79T9F9J9H229F9Jt9VV7bb8A9TW79O9V9Wt9F9KW9J9V9KW9wWVtW949c919M9MWVbeY9TW79O9V9Wt9F9KW9J9V9KW69U9KW949c919M9MWVbdE9TW79O9V9Wt9F9KW9J9V9KW69U9KW949tWG91W9U9JWbiL9TW79O9V9Wt9F9KW9J9V9KWS9P2tWV9p9JtblK9TW79O9V9Wt9F9KW9J9V9KWS9P2tWV9r919HtbvL9TW79O9V9Wt9F9KW9J9V9KWS9P2tWVT949Wbol79IV9Rbrq;w8Wqdbk;esezu8Jjjjjbcj;eb9Rgv8Kjjjjbc9:hodnadcefal0mbcuhoaiRbbc:Ge9hmbavaialfgrad9Radz1jjjbhwcj;abad9Uc;WFbGgocjdaocjd6EhDaicefhocbhqdnindndndnaeaq9nmbaDaeaq9RaqaDfae6Egkcsfglcl4cifcd4hxalc9WGgmTmecbhPawcjdfhsaohzinaraz9Rax6mvarazaxfgo9RcK6mvczhlcbhHinalgic9WfgOawcj;cbffhldndndndndnazaOco4fRbbaHcoG4ciGPlbedibkal9cb83ibalcwf9cb83ibxikalaoRblaoRbbgOco4gAaAciSgAE86bbawcj;cbfaifglcGfaoclfaAfgARbbaOcl4ciGgCaCciSgCE86bbalcVfaAaCfgARbbaOcd4ciGgCaCciSgCE86bbalc7faAaCfgARbbaOciGgOaOciSgOE86bbalctfaAaOfgARbbaoRbegOco4gCaCciSgCE86bbalc91faAaCfgARbbaOcl4ciGgCaCciSgCE86bbalc4faAaCfgARbbaOcd4ciGgCaCciSgCE86bbalc93faAaCfgARbbaOciGgOaOciSgOE86bbalc94faAaOfgARbbaoRbdgOco4gCaCciSgCE86bbalc95faAaCfgARbbaOcl4ciGgCaCciSgCE86bbalc96faAaCfgARbbaOcd4ciGgCaCciSgCE86bbalc97faAaCfgARbbaOciGgOaOciSgOE86bbalc98faAaOfgORbbaoRbigoco4gAaAciSgAE86bbalc99faOaAfgORbbaocl4ciGgAaAciSgAE86bbalc9:faOaAfgORbbaocd4ciGgAaAciSgAE86bbalcufaOaAfglRbbaociGgoaociSgoE86bbalaofhoxdkalaoRbwaoRbbgOcl4gAaAcsSgAE86bbawcj;cbfaifglcGfaocwfaAfgARbbaOcsGgOaOcsSgOE86bbalcVfaAaOfgORbbaoRbegAcl4gCaCcsSgCE86bbalc7faOaCfgORbbaAcsGgAaAcsSgAE86bbalctfaOaAfgORbbaoRbdgAcl4gCaCcsSgCE86bbalc91faOaCfgORbbaAcsGgAaAcsSgAE86bbalc4faOaAfgORbbaoRbigAcl4gCaCcsSgCE86bbalc93faOaCfgORbbaAcsGgAaAcsSgAE86bbalc94faOaAfgORbbaoRblgAcl4gCaCcsSgCE86bbalc95faOaCfgORbbaAcsGgAaAcsSgAE86bbalc96faOaAfgORbbaoRbvgAcl4gCaCcsSgCE86bbalc97faOaCfgORbbaAcsGgAaAcsSgAE86bbalc98faOaAfgORbbaoRbogAcl4gCaCcsSgCE86bbalc99faOaCfgORbbaAcsGgAaAcsSgAE86bbalc9:faOaAfgORbbaoRbrgocl4gAaAcsSgAE86bbalcufaOaAfglRbbaocsGgoaocsSgoE86bbalaofhoxekalao8Pbb83bbalcwfaocwf8Pbb83bbaoczfhokdnaiam9pmbaHcdfhHaiczfhlarao9RcL0mekkaiam6mvaoTmvdnakTmbawaPfRbbhHawcj;cbfhlashiakhOinaialRbbgzce4cbazceG9R7aHfgH86bbaiadfhialcefhlaOcufgOmbkkascefhsaohzaPcefgPad9hmbxikkcbc99arao9Radcaadca0ESEhoxlkaoaxad2fhCdnakmbadhlinaoTmlarao9Rax6mlaoaxfhoalcufglmbkaChoxekcbhmawcjdfhAinarao9Rax6miawamfRbbhHawcj;cbfhlaAhiakhOinaialRbbgzce4cbazceG9R7aHfgH86bbaiadfhialcefhlaOcufgOmbkaAcefhAaoaxfhoamcefgmad9hmbkaChokabaqad2fawcjdfakad2z1jjjb8Aawawcjdfakcufad2fadz1jjjb8Aakaqfhqaombkc9:hoxekc9:hokavcj;ebf8Kjjjjbaok;cseHu8Jjjjjbc;ae9Rgv8Kjjjjbc9:hodnaeci9UgrcHfal0mbcuhoaiRbbgwc;WeGc;Ge9hmbawcsGgwce0mbavc;abfcFecjez:jjjjb8AavcUf9cu83ibavc8Wf9cu83ibavcyf9cu83ibavcaf9cu83ibavcKf9cu83ibavczf9cu83ibav9cu83iwav9cu83ibaialfc9WfhDaicefgqarfhidnaeTmbcmcsawceSEhkcbhxcbhmcbhPcbhwcbhlindnaiaD9nmbc9:hoxikdndnaqRbbgoc;Ve0mbavc;abfalaocu7gscl4fcsGcitfgzydlhrazydbhzdnaocsGgHak9pmbavawasfcsGcdtfydbaxaHEhoaHThsdndnadcd9hmbabaPcetfgHaz87ebaHclfao87ebaHcdfar87ebxekabaPcdtfgHazBdbaHcwfaoBdbaHclfarBdbkaxasfhxcdhHavawcdtfaoBdbawasfhwcehsalhOxdkdndnaHcsSmbaHc987aHamffcefhoxekaicefhoai8SbbgHcFeGhsdndnaHcu9mmbaohixekaicvfhiascFbGhscrhHdninao8SbbgOcFbGaHtasVhsaOcu9kmeaocefhoaHcrfgHc8J9hmbxdkkaocefhikasce4cbasceG9R7amfhokdndnadcd9hmbabaPcetfgHaz87ebaHclfao87ebaHcdfar87ebxekabaPcdtfgHazBdbaHcwfaoBdbaHclfarBdbkcdhHavawcdtfaoBdbcehsawcefhwalhOaohmxekdnaocpe0mbaxcefgHavawaDaocsGfRbbgocl49RcsGcdtfydbaocz6gzEhravawao9RcsGcdtfydbaHazfgAaocsGgHEhoaHThCdndnadcd9hmbabaPcetfgHax87ebaHclfao87ebaHcdfar87ebxekabaPcdtfgHaxBdbaHcwfaoBdbaHclfarBdbkcdhsavawcdtfaxBdbavawcefgwcsGcdtfarBdbcihHavc;abfalcitfgOaxBdlaOarBdbavawazfgwcsGcdtfaoBdbalcefcsGhOawaCfhwaxhzaAaCfhxxekaxcbaiRbbgOEgzaoc;:eSgHfhraOcsGhCaOcl4hAdndnaOcs0mbarcefhoxekarhoavawaA9RcsGcdtfydbhrkdndnaCmbaocefhxxekaohxavawaO9RcsGcdtfydbhokdndnaHTmbaicefhHxekaicdfhHai8SbegscFeGhzdnascu9kmbaicofhXazcFbGhzcrhidninaH8SbbgscFbGaitazVhzascu9kmeaHcefhHaicrfgic8J9hmbkaXhHxekaHcefhHkazce4cbazceG9R7amfgmhzkdndnaAcsSmbaHhsxekaHcefhsaH8SbbgicFeGhrdnaicu9kmbaHcvfhXarcFbGhrcrhidninas8SbbgHcFbGaitarVhraHcu9kmeascefhsaicrfgic8J9hmbkaXhsxekascefhskarce4cbarceG9R7amfgmhrkdndnaCcsSmbashixekascefhias8SbbgocFeGhHdnaocu9kmbascvfhXaHcFbGhHcrhodninai8SbbgscFbGaotaHVhHascu9kmeaicefhiaocrfgoc8J9hmbkaXhixekaicefhikaHce4cbaHceG9R7amfgmhokdndnadcd9hmbabaPcetfgHaz87ebaHclfao87ebaHcdfar87ebxekabaPcdtfgHazBdbaHcwfaoBdbaHclfarBdbkcdhsavawcdtfazBdbavawcefgwcsGcdtfarBdbcihHavc;abfalcitfgXazBdlaXarBdbavawaOcz6aAcsSVfgwcsGcdtfaoBdbawaCTaCcsSVfhwalcefcsGhOkaqcefhqavc;abfaOcitfgOarBdlaOaoBdbavc;abfalasfcsGcitfgraoBdlarazBdbawcsGhwalaHfcsGhlaPcifgPae6mbkkcbc99aiaDSEhokavc;aef8Kjjjjbaok:flevu8Jjjjjbcz9Rhvc9:hodnaecvfal0mbcuhoaiRbbc;:eGc;qe9hmbav9cb83iwaicefhraialfc98fhwdnaeTmbdnadcdSmbcbhDindnaraw6mbc9:skarcefhoar8SbbglcFeGhidndnalcu9mmbaohrxekarcvfhraicFbGhicrhldninao8SbbgdcFbGaltaiVhiadcu9kmeaocefhoalcrfglc8J9hmbxdkkaocefhrkabaDcdtfaic8Etc8F91aicd47avcwfaiceGcdtVgoydbfglBdbaoalBdbaDcefgDae9hmbxdkkcbhDindnaraw6mbc9:skarcefhoar8SbbglcFeGhidndnalcu9mmbaohrxekarcvfhraicFbGhicrhldninao8SbbgdcFbGaltaiVhiadcu9kmeaocefhoalcrfglc8J9hmbxdkkaocefhrkabaDcetfaic8Etc8F91aicd47avcwfaiceGcdtVgoydbfgl87ebaoalBdbaDcefgDae9hmbkkcbc99arawSEhokaok:Lvoeue99dud99eud99dndnadcl9hmbaeTmeindndnabcdfgd8Sbb:Yab8Sbbgi:Ygl:l:tabcefgv8Sbbgo:Ygr:l:tgwJbb;:9cawawNJbbbbawawJbbbb9GgDEgq:mgkaqaicb9iEalMgwawNakaqaocb9iEarMgqaqNMM:r:vglNJbbbZJbbb:;aDEMgr:lJbbb9p9DTmbar:Ohixekcjjjj94hikadai86bbdndnaqalNJbbbZJbbb:;aqJbbbb9GEMgq:lJbbb9p9DTmbaq:Ohdxekcjjjj94hdkavad86bbdndnawalNJbbbZJbbb:;awJbbbb9GEMgw:lJbbb9p9DTmbaw:Ohdxekcjjjj94hdkabad86bbabclfhbaecufgembxdkkaeTmbindndnabclfgd8Ueb:Yab8Uebgi:Ygl:l:tabcdfgv8Uebgo:Ygr:l:tgwJb;:FSawawNJbbbbawawJbbbb9GgDEgq:mgkaqaicb9iEalMgwawNakaqaocb9iEarMgqaqNMM:r:vglNJbbbZJbbb:;aDEMgr:lJbbb9p9DTmbar:Ohixekcjjjj94hikadai87ebdndnaqalNJbbbZJbbb:;aqJbbbb9GEMgq:lJbbb9p9DTmbaq:Ohdxekcjjjj94hdkavad87ebdndnawalNJbbbZJbbb:;awJbbbb9GEMgw:lJbbb9p9DTmbaw:Ohdxekcjjjj94hdkabad87ebabcwfhbaecufgembkkk;oiliui99iue99dnaeTmbcbhiabhlindndnJ;Zl81Zalcof8UebgvciV:Y:vgoal8Ueb:YNgrJb;:FSNJbbbZJbbb:;arJbbbb9GEMgw:lJbbb9p9DTmbaw:OhDxekcjjjj94hDkalclf8Uebhqalcdf8UebhkabaiavcefciGfcetfaD87ebdndnaoak:YNgwJb;:FSNJbbbZJbbb:;awJbbbb9GEMgx:lJbbb9p9DTmbax:OhDxekcjjjj94hDkabaiavciGfgkcd7cetfaD87ebdndnaoaq:YNgoJb;:FSNJbbbZJbbb:;aoJbbbb9GEMgx:lJbbb9p9DTmbax:OhDxekcjjjj94hDkabaiavcufciGfcetfaD87ebdndnJbbjZararN:tawawN:taoaoN:tgrJbbbbarJbbbb9GE:rJb;:FSNJbbbZMgr:lJbbb9p9DTmbar:Ohvxekcjjjj94hvkabakcetfav87ebalcwfhlaiclfhiaecufgembkkk9mbdnadcd4ae2gdTmbinababydbgecwtcw91:Yaece91cjjj98Gcjjj;8if::NUdbabclfhbadcufgdmbkkk9teiucbcbydj1jjbgeabcifc98GfgbBdj1jjbdndnabZbcztgd9nmbcuhiabad9RcFFifcz4nbcuSmekaehikaik;LeeeudndnaeabVciGTmbabhixekdndnadcz9pmbabhixekabhiinaiaeydbBdbaiclfaeclfydbBdbaicwfaecwfydbBdbaicxfaecxfydbBdbaeczfheaiczfhiadc9Wfgdcs0mbkkadcl6mbinaiaeydbBdbaeclfheaiclfhiadc98fgdci0mbkkdnadTmbinaiaeRbb86bbaicefhiaecefheadcufgdmbkkabk;aeedudndnabciGTmbabhixekaecFeGc:b:c:ew2hldndnadcz9pmbabhixekabhiinaialBdbaicxfalBdbaicwfalBdbaiclfalBdbaiczfhiadc9Wfgdcs0mbkkadcl6mbinaialBdbaiclfhiadc98fgdci0mbkkdnadTmbinaiae86bbaicefhiadcufgdmbkkabkkkebcjwklzNbb'; // embed! base
64461
+ 'b9H79Tebbbe8Fv9Gbb9Gvuuuuueu9Giuuub9Geueu9Giuuueuixkbeeeddddillviebeoweuecj:Gdkr;Neqo9TW9T9VV95dbH9F9F939H79T9F9J9H229F9Jt9VV7bb8A9TW79O9V9Wt9F9KW9J9V9KW9wWVtW949c919M9MWVbeY9TW79O9V9Wt9F9KW9J9V9KW69U9KW949c919M9MWVbdE9TW79O9V9Wt9F9KW9J9V9KW69U9KW949tWG91W9U9JWbiL9TW79O9V9Wt9F9KW9J9V9KWS9P2tWV9p9JtblK9TW79O9V9Wt9F9KW9J9V9KWS9P2tWV9r919HtbvL9TW79O9V9Wt9F9KW9J9V9KWS9P2tWVT949WboY9TW79O9V9Wt9F9KW9J9V9KWS9P2tWVJ9V29VVbrl79IV9Rbwq:VZkdbk:XYi5ud9:du8Jjjjjbcj;kb9Rgv8Kjjjjbc9:hodnalTmbcuhoaiRbbgrc;WeGc:Ge9hmbarcsGgwce0mbc9:hoalcufadcd4cbawEgDadfgrcKcaawEgqaraq0Egk6mbaicefhxcj;abad9Uc;WFbGcjdadca0EhmaialfgPar9Rgoadfhsavaoadz:jjjjbgzceVhHcbhOdndninaeaO9nmeaPax9RaD6mdamaeaO9RaOamfgoae6EgAcsfglc9WGhCabaOad2fhXaAcethQaxaDfhiaOaeaoaeao6E9RhLalcl4cifcd4hKazcj;cbfaAfhYcbh8AazcjdfhEaHh3incbh5dnawTmbaxa8Acd4fRbbh5kcbh8Eazcj;cbfhqinaih8Fdndndndna5a8Ecet4ciGgoc9:fPdebdkaPa8F9RaA6mrazcj;cbfa8EaA2fa8FaAz:jjjjb8Aa8FaAfhixdkazcj;cbfa8EaA2fcbaAz:kjjjb8Aa8FhixekaPa8F9RaK6mva8FaKfhidnaCTmbaPai9RcK6mbaocdtc:q:G:cjbfcj:G:cjbawEhaczhrcbhlinargoc9Wfghaqfhrdndndndndndnaaa8Fahco4fRbbalcoG4ciGcdtfydbPDbedvivvvlvkar9cb83bwar9cb83bbxlkarcbaiRbdai8Xbb9c:c:qj:bw9:9c:q;c1:I1e:d9c:b:c:e1z9:gg9cjjjjjz:dg8J9qE86bbaqaofgrcGfcbaicdfa8J9c8N1:NfghRbbag9cjjjjjw:dg8J9qE86bbarcVfcbaha8J9c8M1:NfghRbbag9cjjjjjl:dg8J9qE86bbarc7fcbaha8J9c8L1:NfghRbbag9cjjjjjd:dg8J9qE86bbarctfcbaha8J9c8K1:NfghRbbag9cjjjjje:dg8J9qE86bbarc91fcbaha8J9c8J1:NfghRbbag9cjjjj;ab:dg8J9qE86bbarc4fcbaha8J9cg1:NfghRbbag9cjjjja:dg8J9qE86bbarc93fcbaha8J9ch1:NfghRbbag9cjjjjz:dgg9qE86bbarc94fcbahag9ca1:NfghRbbai8Xbe9c:c:qj:bw9:9c:q;c1:I1e:d9c:b:c:e1z9:gg9cjjjjjz:dg8J9qE86bbarc95fcbaha8J9c8N1:NfgiRbbag9cjjjjjw:dg8J9qE86bbarc96fcbaia8J9c8M1:NfgiRbbag9cjjjjjl:dg8J9qE86bbarc97fcbaia8J9c8L1:NfgiRbbag9cjjjjjd:dg8J9qE86bbarc98fcbaia8J9c8K1:NfgiRbbag9cjjjjje:dg8J9qE86bbarc99fcbaia8J9c8J1:NfgiRbbag9cjjjj;ab:dg8J9qE86bbarc9:fcbaia8J9cg1:NfgiRbbag9cjjjja:dg8J9qE86bbarcufcbaia8J9ch1:NfgiRbbag9cjjjjz:dgg9qE86bbaiag9ca1:NfhixikaraiRblaiRbbghco4g8Ka8KciSg8KE86bbaqaofgrcGfaiclfa8Kfg8KRbbahcl4ciGg8La8LciSg8LE86bbarcVfa8Ka8Lfg8KRbbahcd4ciGg8La8LciSg8LE86bbarc7fa8Ka8Lfg8KRbbahciGghahciSghE86bbarctfa8Kahfg8KRbbaiRbeghco4g8La8LciSg8LE86bbarc91fa8Ka8Lfg8KRbbahcl4ciGg8La8LciSg8LE86bbarc4fa8Ka8Lfg8KRbbahcd4ciGg8La8LciSg8LE86bbarc93fa8Ka8Lfg8KRbbahciGghahciSghE86bbarc94fa8Kahfg8KRbbaiRbdghco4g8La8LciSg8LE86bbarc95fa8Ka8Lfg8KRbbahcl4ciGg8La8LciSg8LE86bbarc96fa8Ka8Lfg8KRbbahcd4ciGg8La8LciSg8LE86bbarc97fa8Ka8Lfg8KRbbahciGghahciSghE86bbarc98fa8KahfghRbbaiRbigico4g8Ka8KciSg8KE86bbarc99faha8KfghRbbaicl4ciGg8Ka8KciSg8KE86bbarc9:faha8KfghRbbaicd4ciGg8Ka8KciSg8KE86bbarcufaha8KfgrRbbaiciGgiaiciSgiE86bbaraifhixdkaraiRbwaiRbbghcl4g8Ka8KcsSg8KE86bbaqaofgrcGfaicwfa8Kfg8KRbbahcsGghahcsSghE86bbarcVfa8KahfghRbbaiRbeg8Kcl4g8La8LcsSg8LE86bbarc7faha8LfghRbba8KcsGg8Ka8KcsSg8KE86bbarctfaha8KfghRbbaiRbdg8Kcl4g8La8LcsSg8LE86bbarc91faha8LfghRbba8KcsGg8Ka8KcsSg8KE86bbarc4faha8KfghRbbaiRbig8Kcl4g8La8LcsSg8LE86bbarc93faha8LfghRbba8KcsGg8Ka8KcsSg8KE86bbarc94faha8KfghRbbaiRblg8Kcl4g8La8LcsSg8LE86bbarc95faha8LfghRbba8KcsGg8Ka8KcsSg8KE86bbarc96faha8KfghRbbaiRbvg8Kcl4g8La8LcsSg8LE86bbarc97faha8LfghRbba8KcsGg8Ka8KcsSg8KE86bbarc98faha8KfghRbbaiRbog8Kcl4g8La8LcsSg8LE86bbarc99faha8LfghRbba8KcsGg8Ka8KcsSg8KE86bbarc9:faha8KfghRbbaiRbrgicl4g8Ka8KcsSg8KE86bbarcufaha8KfgrRbbaicsGgiaicsSgiE86bbaraifhixekarai8Pbw83bwarai8Pbb83bbaiczfhikdnaoaC9pmbalcdfhlaoczfhraPai9RcL0mekkaoaC6moaimexokaCmva8FTmvkaqaAfhqa8Ecefg8Ecl9hmbkdndndndnawTmbasa8Acd4fRbbgociGPlbedrbkaATmdaza8Afh8Fazcj;cbfhhcbh8EaEhaina8FRbbhraahocbhlinaoahalfRbbgqce4cbaqceG9R7arfgr86bbaoadfhoaAalcefgl9hmbkaacefhaa8Fcefh8FahaAfhha8Ecefg8Ecl9hmbxikkaATmeaza8Afhaazcj;cbfhhcbhoceh8EaYh8FinaEaofhlaa8Vbbhrcbhoinala8FaofRbbcwtahaofRbbgqVc;:FiGce4cbaqceG9R7arfgr87bbaladfhlaLaocefgofmbka8FaQfh8FcdhoaacdfhaahaQfhha8EceGhlcbh8EalmbxdkkaATmbaocl4h8Eaza8AfRbbhqcwhoa3hlinalRbbaotaqVhqalcefhlaocwfgoca9hmbkcbhhaEh8FaYhainazcj;cbfahfRbbhrcwhoaahlinalRbbaotarVhralaAfhlaocwfgoca9hmbkara8E94aq7hqcbhoa8Fhlinalaqao486bbalcefhlaocwfgoca9hmbka8Fadfh8FaacefhaahcefghaA9hmbkkaEclfhEa3clfh3a8Aclfg8Aad6mbkaXazcjdfaAad2z:jjjjb8AazazcjdfaAcufad2fadz:jjjjb8AaAaOfhOaihxaimbkc9:hoxdkcbc99aPax9RakSEhoxekc9:hokavcj;kbf8Kjjjjbaok:ysezu8Jjjjjbc;ae9Rgv8Kjjjjbc9:hodnalaeci9UgrcHf6mbcuhoaiRbbgwc;WeGc;Ge9hmbawcsGgDce0mbavc;abfcFecjez:kjjjb8Aav9cu83iUav9cu83i8Wav9cu83iyav9cu83iaav9cu83iKav9cu83izav9cu83iwav9cu83ibaialfc9WfhqaicefgwarfhldnaeTmbcmcsaDceSEhkcbhxcbhmcbhrcbhicbhoindnalaq9nmbc9:hoxikdndnawRbbgDc;Ve0mbavc;abfaoaDcu7gPcl4fcsGcitfgsydlhzasydbhHdndnaDcsGgsak9pmbavaiaPfcsGcdtfydbaxasEhDaxasTgOfhxxekdndnascsSmbcehOasc987asamffcefhDxekalcefhDal8SbbgscFeGhPdndnascu9mmbaDhlxekalcvfhlaPcFbGhPcrhsdninaD8SbbgOcFbGastaPVhPaOcu9kmeaDcefhDascrfgsc8J9hmbxdkkaDcefhlkcehOaPce4cbaPceG9R7amfhDkaDhmkavc;abfaocitfgsaDBdbasazBdlavaicdtfaDBdbavc;abfaocefcsGcitfgsaHBdbasaDBdlaocdfhoaOaifhidnadcd9hmbabarcetfgsaH87ebasclfaD87ebascdfaz87ebxdkabarcdtfgsaHBdbascwfaDBdbasclfazBdbxekdnaDcpe0mbavaiaqaDcsGfRbbgscl4gP9RcsGcdtfydbaxcefgOaPEhDavaias9RcsGcdtfydbaOaPTgzfgOascsGgPEhsaPThPdndnadcd9hmbabarcetfgHax87ebaHclfas87ebaHcdfaD87ebxekabarcdtfgHaxBdbaHcwfasBdbaHclfaDBdbkavaicdtfaxBdbavc;abfaocitfgHaDBdbaHaxBdlavaicefgicsGcdtfaDBdbavc;abfaocefcsGcitfgHasBdbaHaDBdlavaiazfgicsGcdtfasBdbavc;abfaocdfcsGcitfgDaxBdbaDasBdlaocifhoaiaPfhiaOaPfhxxekaxcbalRbbgsEgHaDc;:eSgDfhOascsGhAdndnascl4gCmbaOcefhzxekaOhzavaiaC9RcsGcdtfydbhOkdndnaAmbazcefhxxekazhxavaias9RcsGcdtfydbhzkdndnaDTmbalcefhDxekalcdfhDal8SbegPcFeGhsdnaPcu9kmbalcofhHascFbGhscrhldninaD8SbbgPcFbGaltasVhsaPcu9kmeaDcefhDalcrfglc8J9hmbkaHhDxekaDcefhDkasce4cbasceG9R7amfgmhHkdndnaCcsSmbaDhsxekaDcefhsaD8SbbglcFeGhPdnalcu9kmbaDcvfhOaPcFbGhPcrhldninas8SbbgDcFbGaltaPVhPaDcu9kmeascefhsalcrfglc8J9hmbkaOhsxekascefhskaPce4cbaPceG9R7amfgmhOkdndnaAcsSmbashlxekascefhlas8SbbgDcFeGhPdnaDcu9kmbascvfhzaPcFbGhPcrhDdninal8SbbgscFbGaDtaPVhPascu9kmealcefhlaDcrfgDc8J9hmbkazhlxekalcefhlkaPce4cbaPceG9R7amfgmhzkdndnadcd9hmbabarcetfgDaH87ebaDclfaz87ebaDcdfaO87ebxekabarcdtfgDaHBdbaDcwfazBdbaDclfaOBdbkavc;abfaocitfgDaOBdbaDaHBdlavaicdtfaHBdbavc;abfaocefcsGcitfgDazBdbaDaOBdlavaicefgicsGcdtfaOBdbavc;abfaocdfcsGcitfgDaHBdbaDazBdlavaiaCTaCcsSVfgicsGcdtfazBdbaiaATaAcsSVfhiaocifhokawcefhwaocsGhoaicsGhiarcifgrae6mbkkcbc99alaqSEhokavc;aef8Kjjjjbaok:clevu8Jjjjjbcz9Rhvdnalaecvf9pmbc9:skdnaiRbbc;:eGc;qeSmbcuskav9cb83iwaicefhoaialfc98fhrdnaeTmbdnadcdSmbcbhwindnaoar6mbc9:skaocefhlao8SbbgicFeGhddndnaicu9mmbalhoxekaocvfhoadcFbGhdcrhidninal8SbbgDcFbGaitadVhdaDcu9kmealcefhlaicrfgic8J9hmbxdkkalcefhokabawcdtfadc8Etc8F91adcd47avcwfadceGcdtVglydbfgiBdbalaiBdbawcefgwae9hmbxdkkcbhwindnaoar6mbc9:skaocefhlao8SbbgicFeGhddndnaicu9mmbalhoxekaocvfhoadcFbGhdcrhidninal8SbbgDcFbGaitadVhdaDcu9kmealcefhlaicrfgic8J9hmbxdkkalcefhokabawcetfadc8Etc8F91adcd47avcwfadceGcdtVglydbfgi87ebalaiBdbawcefgwae9hmbkkcbc99aoarSEk:Lvoeue99dud99eud99dndnadcl9hmbaeTmeindndnabcdfgd8Sbb:Yab8Sbbgi:Ygl:l:tabcefgv8Sbbgo:Ygr:l:tgwJbb;:9cawawNJbbbbawawJbbbb9GgDEgq:mgkaqaicb9iEalMgwawNakaqaocb9iEarMgqaqNMM:r:vglNJbbbZJbbb:;aDEMgr:lJbbb9p9DTmbar:Ohixekcjjjj94hikadai86bbdndnaqalNJbbbZJbbb:;aqJbbbb9GEMgq:lJbbb9p9DTmbaq:Ohdxekcjjjj94hdkavad86bbdndnawalNJbbbZJbbb:;awJbbbb9GEMgw:lJbbb9p9DTmbaw:Ohdxekcjjjj94hdkabad86bbabclfhbaecufgembxdkkaeTmbindndnabclfgd8Ueb:Yab8Uebgi:Ygl:l:tabcdfgv8Uebgo:Ygr:l:tgwJb;:FSawawNJbbbbawawJbbbb9GgDEgq:mgkaqaicb9iEalMgwawNakaqaocb9iEarMgqaqNMM:r:vglNJbbbZJbbb:;aDEMgr:lJbbb9p9DTmbar:Ohixekcjjjj94hikadai87ebdndnaqalNJbbbZJbbb:;aqJbbbb9GEMgq:lJbbb9p9DTmbaq:Ohdxekcjjjj94hdkavad87ebdndnawalNJbbbZJbbb:;awJbbbb9GEMgw:lJbbb9p9DTmbaw:Ohdxekcjjjj94hdkabad87ebabcwfhbaecufgembkkk:4ioiue99dud99dud99dnaeTmbcbhiabhlindndnal8Uebgv:YgoJ:ji:1Salcof8UebgrciVgw:Y:vgDNJbbbZJbbb:;avcu9kEMgq:lJbbb9p9DTmbaq:Ohkxekcjjjj94hkkalclf8Uebhvalcdf8UebhxalarcefciGcetfak87ebdndnax:YgqaDNJbbbZJbbb:;axcu9kEMgm:lJbbb9p9DTmbam:Ohxxekcjjjj94hxkabaiarciGgkfcd7cetfax87ebdndnav:YgmaDNJbbbZJbbb:;avcu9kEMgP:lJbbb9p9DTmbaP:Ohvxekcjjjj94hvkalarcufciGcetfav87ebdndnawaw2:ZgPaPMaoaoN:taqaqN:tamamN:tgoJbbbbaoJbbbb9GE:raDNJbbbZMgD:lJbbb9p9DTmbaD:Ohrxekcjjjj94hrkalakcetfar87ebalcwfhlaiclfhiaecufgembkkk9mbdnadcd4ae2gdTmbinababydbgecwtcw91:Yaece91cjjj98Gcjjj;8if::NUdbabclfhbadcufgdmbkkk:Tvirud99eudndnadcl9hmbaeTmeindndnabRbbgiabcefgl8Sbbgvabcdfgo8Sbbgrf9R:YJbbuJabcifgwRbbgdce4adVgDcd4aDVgDcl4aDVgD:Z:vgqNJbbbZMgk:lJbbb9p9DTmbak:Ohxxekcjjjj94hxkaoax86bbdndnaraif:YaqNJbbbZMgk:lJbbb9p9DTmbak:Ohoxekcjjjj94hokalao86bbdndnavaifar9R:YaqNJbbbZMgk:lJbbb9p9DTmbak:Ohixekcjjjj94hikabai86bbdndnaDadcetGadceGV:ZaqNJbbbZMgq:lJbbb9p9DTmbaq:Ohdxekcjjjj94hdkawad86bbabclfhbaecufgembxdkkaeTmbindndnab8Vebgiabcdfgl8Uebgvabclfgo8Uebgrf9R:YJbFu9habcofgw8Vebgdce4adVgDcd4aDVgDcl4aDVgDcw4aDVgD:Z:vgqNJbbbZMgk:lJbbb9p9DTmbak:Ohxxekcjjjj94hxkaoax87ebdndnaraif:YaqNJbbbZMgk:lJbbb9p9DTmbak:Ohoxekcjjjj94hokalao87ebdndnavaifar9R:YaqNJbbbZMgk:lJbbb9p9DTmbak:Ohixekcjjjj94hikabai87ebdndnaDadcetGadceGV:ZaqNJbbbZMgq:lJbbb9p9DTmbaq:Ohdxekcjjjj94hdkawad87ebabcwfhbaecufgembkkk9teiucbcbyd:K:G:cjbgeabcifc98GfgbBd:K:G:cjbdndnabZbcztgd9nmbcuhiabad9RcFFifcz4nbcuSmekaehikaik;LeeeudndnaeabVciGTmbabhixekdndnadcz9pmbabhixekabhiinaiaeydbBdbaiclfaeclfydbBdbaicwfaecwfydbBdbaicxfaecxfydbBdbaeczfheaiczfhiadc9Wfgdcs0mbkkadcl6mbinaiaeydbBdbaeclfheaiclfhiadc98fgdci0mbkkdnadTmbinaiaeRbb86bbaicefhiaecefheadcufgdmbkkabk;aeedudndnabciGTmbabhixekaecFeGc:b:c:ew2hldndnadcz9pmbabhixekabhiinaialBdbaicxfalBdbaicwfalBdbaiclfalBdbaiczfhiadc9Wfgdcs0mbkkadcl6mbinaialBdbaiclfhiadc98fgdci0mbkkdnadTmbinaiae86bbaicefhiadcufgdmbkkabkk83dbcj:Gdk8Kbbbbdbbblbbbwbbbbbbbebbbdbbblbbbwbbbbc:K:Gdkl8W:qbb'; // embed! base
64296
64462
  var wasm_simd =
64297
- 'b9H79TebbbeKl9Gbb9Gvuuuuueu9Giuuub9Geueuikqbbebeedddilve9Weeeviebeoweuec:q:6dkr;leDo9TW9T9VV95dbH9F9F939H79T9F9J9H229F9Jt9VV7bb8A9TW79O9V9Wt9F9KW9J9V9KW9wWVtW949c919M9MWVbdY9TW79O9V9Wt9F9KW9J9V9KW69U9KW949c919M9MWVblE9TW79O9V9Wt9F9KW9J9V9KW69U9KW949tWG91W9U9JWbvL9TW79O9V9Wt9F9KW9J9V9KWS9P2tWV9p9JtboK9TW79O9V9Wt9F9KW9J9V9KWS9P2tWV9r919HtbrL9TW79O9V9Wt9F9KW9J9V9KWS9P2tWVT949Wbwl79IV9RbDq:p9sqlbzik9:evu8Jjjjjbcz9Rhbcbheincbhdcbhiinabcwfadfaicjuaead4ceGglE86bbaialfhiadcefgdcw9hmbkaec:q:yjjbfai86bbaecitc:q1jjbfab8Piw83ibaecefgecjd9hmbkk:N8JlHud97euo978Jjjjjbcj;kb9Rgv8Kjjjjbc9:hodnadcefal0mbcuhoaiRbbc:Ge9hmbavaialfgrad9Rad;8qbbcj;abad9UhlaicefhodnaeTmbadTmbalc;WFbGglcjdalcjd6EhwcbhDinawaeaD9RaDawfae6Egqcsfglc9WGgkci2hxakcethmalcl4cifcd4hPabaDad2fhsakc;ab6hzcbhHincbhOaohAdndninaraA9RaP6meavcj;cbfaOak2fhCaAaPfhocbhidnazmbarao9Rc;Gb6mbcbhlinaCalfhidndndndndnaAalco4fRbbgXciGPlbedibkaipxbbbbbbbbbbbbbbbbpklbxikaiaopbblaopbbbgQclp:meaQpmbzeHdOiAlCvXoQrLgQcdp:meaQpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogLpxiiiiiiiiiiiiiiiip8JgQp5b9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibaKc:q:yjjbfpbbbgYaYpmbbbbbbbbbbbbbbbbaQp5e9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibp9UpmbedilvorzHOACXQLpPaLaQp9spklbaoclfaYpQbfaKc:q:yjjbfRbbfhoxdkaiaopbbwaopbbbgQclp:meaQpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogLpxssssssssssssssssp8JgQp5b9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibaKc:q:yjjbfpbbbgYaYpmbbbbbbbbbbbbbbbbaQp5e9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibp9UpmbedilvorzHOACXQLpPaLaQp9spklbaocwfaYpQbfaKc:q:yjjbfRbbfhoxekaiaopbbbpklbaoczfhokdndndndndnaXcd4ciGPlbedibkaipxbbbbbbbbbbbbbbbbpklzxikaiaopbblaopbbbgQclp:meaQpmbzeHdOiAlCvXoQrLgQcdp:meaQpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogLpxiiiiiiiiiiiiiiiip8JgQp5b9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibaKc:q:yjjbfpbbbgYaYpmbbbbbbbbbbbbbbbbaQp5e9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibp9UpmbedilvorzHOACXQLpPaLaQp9spklzaoclfaYpQbfaKc:q:yjjbfRbbfhoxdkaiaopbbwaopbbbgQclp:meaQpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogLpxssssssssssssssssp8JgQp5b9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibaKc:q:yjjbfpbbbgYaYpmbbbbbbbbbbbbbbbbaQp5e9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibp9UpmbedilvorzHOACXQLpPaLaQp9spklzaocwfaYpQbfaKc:q:yjjbfRbbfhoxekaiaopbbbpklzaoczfhokdndndndndnaXcl4ciGPlbedibkaipxbbbbbbbbbbbbbbbbpklaxikaiaopbblaopbbbgQclp:meaQpmbzeHdOiAlCvXoQrLgQcdp:meaQpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogLpxiiiiiiiiiiiiiiiip8JgQp5b9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibaKc:q:yjjbfpbbbgYaYpmbbbbbbbbbbbbbbbbaQp5e9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibp9UpmbedilvorzHOACXQLpPaLaQp9spklaaoclfaYpQbfaKc:q:yjjbfRbbfhoxdkaiaopbbwaopbbbgQclp:meaQpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogLpxssssssssssssssssp8JgQp5b9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibaKc:q:yjjbfpbbbgYaYpmbbbbbbbbbbbbbbbbaQp5e9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibp9UpmbedilvorzHOACXQLpPaLaQp9spklaaocwfaYpQbfaKc:q:yjjbfRbbfhoxekaiaopbbbpklaaoczfhokdndndndndnaXco4Plbedibkaipxbbbbbbbbbbbbbbbbpkl8WxikaiaopbblaopbbbgQclp:meaQpmbzeHdOiAlCvXoQrLgQcdp:meaQpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogLpxiiiiiiiiiiiiiiiip8JgQp5b9cjF;8;4;W;G;ab9:9cU1:NgXcitc:q1jjbfpbibaXc:q:yjjbfpbbbgYaYpmbbbbbbbbbbbbbbbbaQp5e9cjF;8;4;W;G;ab9:9cU1:NgXcitc:q1jjbfpbibp9UpmbedilvorzHOACXQLpPaLaQp9spkl8WaoclfaYpQbfaXc:q:yjjbfRbbfhoxdkaiaopbbwaopbbbgQclp:meaQpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogLpxssssssssssssssssp8JgQp5b9cjF;8;4;W;G;ab9:9cU1:NgXcitc:q1jjbfpbibaXc:q:yjjbfpbbbgYaYpmbbbbbbbbbbbbbbbbaQp5e9cjF;8;4;W;G;ab9:9cU1:NgXcitc:q1jjbfpbibp9UpmbedilvorzHOACXQLpPaLaQp9spkl8WaocwfaYpQbfaXc:q:yjjbfRbbfhoxekaiaopbbbpkl8Waoczfhokalc;abfhialcjefak0meaihlarao9Rc;Fb0mbkkdnaiak9pmbaici4hlinarao9RcK6miaCaifhXdndndndndnaAaico4fRbbalcoG4ciGPlbedibkaXpxbbbbbbbbbbbbbbbbpkbbxikaXaopbblaopbbbgQclp:meaQpmbzeHdOiAlCvXoQrLgQcdp:meaQpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogLpxiiiiiiiiiiiiiiiip8JgQp5b9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibaKc:q:yjjbfpbbbgYaYpmbbbbbbbbbbbbbbbbaQp5e9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibp9UpmbedilvorzHOACXQLpPaLaQp9spkbbaoclfaYpQbfaKc:q:yjjbfRbbfhoxdkaXaopbbwaopbbbgQclp:meaQpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogLpxssssssssssssssssp8JgQp5b9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibaKc:q:yjjbfpbbbgYaYpmbbbbbbbbbbbbbbbbaQp5e9cjF;8;4;W;G;ab9:9cU1:NgKcitc:q1jjbfpbibp9UpmbedilvorzHOACXQLpPaLaQp9spkbbaocwfaYpQbfaKc:q:yjjbfRbbfhoxekaXaopbbbpkbbaoczfhokalcdfhlaiczfgiak6mbkkaoTmeaohAaOcefgOclSmdxbkkc9:hoxlkdnakTmbavcjdfaHfhiavaHfpbdbhYcbhXinaiavcj;cbfaXfglpblbgLcep9TaLpxeeeeeeeeeeeeeeeegQp9op9Hp9rgLalakfpblbg8Acep9Ta8AaQp9op9Hp9rg8ApmbzeHdOiAlCvXoQrLgEalamfpblbg3cep9Ta3aQp9op9Hp9rg3alaxfpblbg5cep9Ta5aQp9op9Hp9rg5pmbzeHdOiAlCvXoQrLg8EpmbezHdiOAlvCXorQLgQaQpmbedibedibedibediaYp9UgYp9AdbbaiadfglaYaQaQpmlvorlvorlvorlvorp9UgYp9AdbbaladfglaYaQaQpmwDqkwDqkwDqkwDqkp9UgYp9AdbbaladfglaYaQaQpmxmPsxmPsxmPsxmPsp9UgYp9AdbbaladfglaYaEa8EpmwDKYqk8AExm35Ps8E8FgQaQpmbedibedibedibedip9UgYp9AdbbaladfglaYaQaQpmlvorlvorlvorlvorp9UgYp9AdbbaladfglaYaQaQpmwDqkwDqkwDqkwDqkp9UgYp9AdbbaladfglaYaQaQpmxmPsxmPsxmPsxmPsp9UgYp9AdbbaladfglaYaLa8ApmwKDYq8AkEx3m5P8Es8FgLa3a5pmwKDYq8AkEx3m5P8Es8Fg8ApmbezHdiOAlvCXorQLgQaQpmbedibedibedibedip9UgYp9AdbbaladfglaYaQaQpmlvorlvorlvorlvorp9UgYp9AdbbaladfglaYaQaQpmwDqkwDqkwDqkwDqkp9UgYp9AdbbaladfglaYaQaQpmxmPsxmPsxmPsxmPsp9UgYp9AdbbaladfglaYaLa8ApmwDKYqk8AExm35Ps8E8FgQaQpmbedibedibedibedip9UgYp9AdbbaladfglaYaQaQpmlvorlvorlvorlvorp9UgYp9AdbbaladfglaYaQaQpmwDqkwDqkwDqkwDqkp9UgYp9AdbbaladfglaYaQaQpmxmPsxmPsxmPsxmPsp9UgYp9AdbbaladfhiaXczfgXak6mbkkaHclfgHad6mbkasavcjdfaqad2;8qbbavavcjdfaqcufad2fad;8qbbaqaDfgDae6mbkkcbc99arao9Radcaadca0ESEhokavcj;kbf8Kjjjjbaokwbz:bjjjbk::seHu8Jjjjjbc;ae9Rgv8Kjjjjbc9:hodnaeci9UgrcHfal0mbcuhoaiRbbgwc;WeGc;Ge9hmbawcsGgwce0mbavc;abfcFecje;8kbavcUf9cu83ibavc8Wf9cu83ibavcyf9cu83ibavcaf9cu83ibavcKf9cu83ibavczf9cu83ibav9cu83iwav9cu83ibaialfc9WfhDaicefgqarfhidnaeTmbcmcsawceSEhkcbhxcbhmcbhPcbhwcbhlindnaiaD9nmbc9:hoxikdndnaqRbbgoc;Ve0mbavc;abfalaocu7gscl4fcsGcitfgzydlhrazydbhzdnaocsGgHak9pmbavawasfcsGcdtfydbaxaHEhoaHThsdndnadcd9hmbabaPcetfgHaz87ebaHclfao87ebaHcdfar87ebxekabaPcdtfgHazBdbaHcwfaoBdbaHclfarBdbkaxasfhxcdhHavawcdtfaoBdbawasfhwcehsalhOxdkdndnaHcsSmbaHc987aHamffcefhoxekaicefhoai8SbbgHcFeGhsdndnaHcu9mmbaohixekaicvfhiascFbGhscrhHdninao8SbbgOcFbGaHtasVhsaOcu9kmeaocefhoaHcrfgHc8J9hmbxdkkaocefhikasce4cbasceG9R7amfhokdndnadcd9hmbabaPcetfgHaz87ebaHclfao87ebaHcdfar87ebxekabaPcdtfgHazBdbaHcwfaoBdbaHclfarBdbkcdhHavawcdtfaoBdbcehsawcefhwalhOaohmxekdnaocpe0mbaxcefgHavawaDaocsGfRbbgocl49RcsGcdtfydbaocz6gzEhravawao9RcsGcdtfydbaHazfgAaocsGgHEhoaHThCdndnadcd9hmbabaPcetfgHax87ebaHclfao87ebaHcdfar87ebxekabaPcdtfgHaxBdbaHcwfaoBdbaHclfarBdbkcdhsavawcdtfaxBdbavawcefgwcsGcdtfarBdbcihHavc;abfalcitfgOaxBdlaOarBdbavawazfgwcsGcdtfaoBdbalcefcsGhOawaCfhwaxhzaAaCfhxxekaxcbaiRbbgOEgzaoc;:eSgHfhraOcsGhCaOcl4hAdndnaOcs0mbarcefhoxekarhoavawaA9RcsGcdtfydbhrkdndnaCmbaocefhxxekaohxavawaO9RcsGcdtfydbhokdndnaHTmbaicefhHxekaicdfhHai8SbegscFeGhzdnascu9kmbaicofhXazcFbGhzcrhidninaH8SbbgscFbGaitazVhzascu9kmeaHcefhHaicrfgic8J9hmbkaXhHxekaHcefhHkazce4cbazceG9R7amfgmhzkdndnaAcsSmbaHhsxekaHcefhsaH8SbbgicFeGhrdnaicu9kmbaHcvfhXarcFbGhrcrhidninas8SbbgHcFbGaitarVhraHcu9kmeascefhsaicrfgic8J9hmbkaXhsxekascefhskarce4cbarceG9R7amfgmhrkdndnaCcsSmbashixekascefhias8SbbgocFeGhHdnaocu9kmbascvfhXaHcFbGhHcrhodninai8SbbgscFbGaotaHVhHascu9kmeaicefhiaocrfgoc8J9hmbkaXhixekaicefhikaHce4cbaHceG9R7amfgmhokdndnadcd9hmbabaPcetfgHaz87ebaHclfao87ebaHcdfar87ebxekabaPcdtfgHazBdbaHcwfaoBdbaHclfarBdbkcdhsavawcdtfazBdbavawcefgwcsGcdtfarBdbcihHavc;abfalcitfgXazBdlaXarBdbavawaOcz6aAcsSVfgwcsGcdtfaoBdbawaCTaCcsSVfhwalcefcsGhOkaqcefhqavc;abfaOcitfgOarBdlaOaoBdbavc;abfalasfcsGcitfgraoBdlarazBdbawcsGhwalaHfcsGhlaPcifgPae6mbkkcbc99aiaDSEhokavc;aef8Kjjjjbaok:flevu8Jjjjjbcz9Rhvc9:hodnaecvfal0mbcuhoaiRbbc;:eGc;qe9hmbav9cb83iwaicefhraialfc98fhwdnaeTmbdnadcdSmbcbhDindnaraw6mbc9:skarcefhoar8SbbglcFeGhidndnalcu9mmbaohrxekarcvfhraicFbGhicrhldninao8SbbgdcFbGaltaiVhiadcu9kmeaocefhoalcrfglc8J9hmbxdkkaocefhrkabaDcdtfaic8Etc8F91aicd47avcwfaiceGcdtVgoydbfglBdbaoalBdbaDcefgDae9hmbxdkkcbhDindnaraw6mbc9:skarcefhoar8SbbglcFeGhidndnalcu9mmbaohrxekarcvfhraicFbGhicrhldninao8SbbgdcFbGaltaiVhiadcu9kmeaocefhoalcrfglc8J9hmbxdkkaocefhrkabaDcetfaic8Etc8F91aicd47avcwfaiceGcdtVgoydbfgl87ebaoalBdbaDcefgDae9hmbkkcbc99arawSEhokaok:wPliuo97eue978Jjjjjbca9Rhiaec98Ghldndnadcl9hmbdnalTmbcbhvabhdinadadpbbbgocKp:RecKp:Sep;6egraocwp:RecKp:Sep;6earp;Geaoczp:RecKp:Sep;6egwp;Gep;Kep;LegDpxbbbbbbbbbbbbbbbbp:2egqarpxbbbjbbbjbbbjbbbjgkp9op9rp;Kegrpxbb;:9cbb;:9cbb;:9cbb;:9cararp;MeaDaDp;Meawaqawakp9op9rp;Kegrarp;Mep;Kep;Kep;Jep;Negwp;Mepxbbn0bbn0bbn0bbn0gqp;KepxFbbbFbbbFbbbFbbbp9oaopxbbbFbbbFbbbFbbbFp9op9qarawp;Meaqp;Kecwp:RepxbFbbbFbbbFbbbFbbp9op9qaDawp;Meaqp;Keczp:RepxbbFbbbFbbbFbbbFbp9op9qpkbbadczfhdavclfgval6mbkkalaeSmeaipxbbbbbbbbbbbbbbbbgqpklbaiabalcdtfgdaeciGglcdtgv;8qbbdnalTmbaiaipblbgocKp:RecKp:Sep;6egraocwp:RecKp:Sep;6earp;Geaoczp:RecKp:Sep;6egwp;Gep;Kep;LegDaqp:2egqarpxbbbjbbbjbbbjbbbjgkp9op9rp;Kegrpxbb;:9cbb;:9cbb;:9cbb;:9cararp;MeaDaDp;Meawaqawakp9op9rp;Kegrarp;Mep;Kep;Kep;Jep;Negwp;Mepxbbn0bbn0bbn0bbn0gqp;KepxFbbbFbbbFbbbFbbbp9oaopxbbbFbbbFbbbFbbbFp9op9qarawp;Meaqp;Kecwp:RepxbFbbbFbbbFbbbFbbp9op9qaDawp;Meaqp;Keczp:RepxbbFbbbFbbbFbbbFbp9op9qpklbkadaiav;8qbbskdnalTmbcbhvabhdinadczfgxaxpbbbgopxbbbbbbFFbbbbbbFFgkp9oadpbbbgDaopmbediwDqkzHOAKY8AEgwczp:Reczp:Sep;6egraDaopmlvorxmPsCXQL358E8FpxFubbFubbFubbFubbp9op;6eawczp:Sep;6egwp;Gearp;Gep;Kep;Legopxbbbbbbbbbbbbbbbbp:2egqarpxbbbjbbbjbbbjbbbjgmp9op9rp;Kegrpxb;:FSb;:FSb;:FSb;:FSararp;Meaoaop;Meawaqawamp9op9rp;Kegrarp;Mep;Kep;Kep;Jep;Negwp;Mepxbbn0bbn0bbn0bbn0gqp;KepxFFbbFFbbFFbbFFbbp9oaoawp;Meaqp;Keczp:Rep9qgoarawp;Meaqp;KepxFFbbFFbbFFbbFFbbp9ogrpmwDKYqk8AExm35Ps8E8Fp9qpkbbadaDakp9oaoarpmbezHdiOAlvCXorQLp9qpkbbadcafhdavclfgval6mbkkalaeSmbaiaeciGgvcitgdfcbcaad9R;8kbaiabalcitfglad;8qbbdnavTmbaiaipblzgopxbbbbbbFFbbbbbbFFgkp9oaipblbgDaopmbediwDqkzHOAKY8AEgwczp:Reczp:Sep;6egraDaopmlvorxmPsCXQL358E8FpxFubbFubbFubbFubbp9op;6eawczp:Sep;6egwp;Gearp;Gep;Kep;Legopxbbbbbbbbbbbbbbbbp:2egqarpxbbbjbbbjbbbjbbbjgmp9op9rp;Kegrpxb;:FSb;:FSb;:FSb;:FSararp;Meaoaop;Meawaqawamp9op9rp;Kegrarp;Mep;Kep;Kep;Jep;Negwp;Mepxbbn0bbn0bbn0bbn0gqp;KepxFFbbFFbbFFbbFFbbp9oaoawp;Meaqp;Keczp:Rep9qgoarawp;Meaqp;KepxFFbbFFbbFFbbFFbbp9ogrpmwDKYqk8AExm35Ps8E8Fp9qpklzaiaDakp9oaoarpmbezHdiOAlvCXorQLp9qpklbkalaiad;8qbbkk;4wllue97euv978Jjjjjbc8W9Rhidnaec98GglTmbcbhvabhoinaiaopbbbgraoczfgwpbbbgDpmlvorxmPsCXQL358E8Fgqczp:Segkclp:RepklbaopxbbjZbbjZbbjZbbjZpx;Zl81Z;Zl81Z;Zl81Z;Zl81Zakpxibbbibbbibbbibbbp9qp;6ep;NegkaraDpmbediwDqkzHOAKY8AEgrczp:Reczp:Sep;6ep;MegDaDp;Meakarczp:Sep;6ep;Megxaxp;Meakaqczp:Reczp:Sep;6ep;Megqaqp;Mep;Kep;Kep;Lepxbbbbbbbbbbbbbbbbp:4ep;Jepxb;:FSb;:FSb;:FSb;:FSgkp;Mepxbbn0bbn0bbn0bbn0grp;KepxFFbbFFbbFFbbFFbbgmp9oaxakp;Mearp;Keczp:Rep9qgxaDakp;Mearp;Keamp9oaqakp;Mearp;Keczp:Rep9qgkpmbezHdiOAlvCXorQLgrp5baipblbpEb:T:j83ibaocwfarp5eaipblbpEe:T:j83ibawaxakpmwDKYqk8AExm35Ps8E8Fgkp5baipblbpEd:T:j83ibaocKfakp5eaipblbpEi:T:j83ibaocafhoavclfgval6mbkkdnalaeSmbaiaeciGgvcitgofcbcaao9R;8kbaiabalcitfgwao;8qbbdnavTmbaiaipblbgraipblzgDpmlvorxmPsCXQL358E8Fgqczp:Segkclp:RepklaaipxbbjZbbjZbbjZbbjZpx;Zl81Z;Zl81Z;Zl81Z;Zl81Zakpxibbbibbbibbbibbbp9qp;6ep;NegkaraDpmbediwDqkzHOAKY8AEgrczp:Reczp:Sep;6ep;MegDaDp;Meakarczp:Sep;6ep;Megxaxp;Meakaqczp:Reczp:Sep;6ep;Megqaqp;Mep;Kep;Kep;Lepxbbbbbbbbbbbbbbbbp:4ep;Jepxb;:FSb;:FSb;:FSb;:FSgkp;Mepxbbn0bbn0bbn0bbn0grp;KepxFFbbFFbbFFbbFFbbgmp9oaxakp;Mearp;Keczp:Rep9qgxaDakp;Mearp;Keamp9oaqakp;Mearp;Keczp:Rep9qgkpmbezHdiOAlvCXorQLgrp5baipblapEb:T:j83ibaiarp5eaipblapEe:T:j83iwaiaxakpmwDKYqk8AExm35Ps8E8Fgkp5baipblapEd:T:j83izaiakp5eaipblapEi:T:j83iKkawaiao;8qbbkk:Pddiue978Jjjjjbc;ab9Rhidnadcd4ae2glc98GgvTmbcbheabhdinadadpbbbgocwp:Recwp:Sep;6eaocep:SepxbbjFbbjFbbjFbbjFp9opxbbjZbbjZbbjZbbjZp:Uep;Mepkbbadczfhdaeclfgeav6mbkkdnavalSmbaialciGgecdtgdVcbc;abad9R;8kbaiabavcdtfgvad;8qbbdnaeTmbaiaipblbgocwp:Recwp:Sep;6eaocep:SepxbbjFbbjFbbjFbbjFp9opxbbjZbbjZbbjZbbjZp:Uep;Mepklbkavaiad;8qbbkk9teiucbcbydj1jjbgeabcifc98GfgbBdj1jjbdndnabZbcztgd9nmbcuhiabad9RcFFifcz4nbcuSmekaehikaikkkebcjwklz:Dbb'; // embed! simd
64463
+ 'b9H79TebbbeKl9Gbb9Gvuuuuueu9Giuuub9Geueuixkbbebeeddddilve9Weeeviebeoweuecj:Gdkr;Neqo9TW9T9VV95dbH9F9F939H79T9F9J9H229F9Jt9VV7bb8A9TW79O9V9Wt9F9KW9J9V9KW9wWVtW949c919M9MWVbdY9TW79O9V9Wt9F9KW9J9V9KW69U9KW949c919M9MWVblE9TW79O9V9Wt9F9KW9J9V9KW69U9KW949tWG91W9U9JWbvL9TW79O9V9Wt9F9KW9J9V9KWS9P2tWV9p9JtboK9TW79O9V9Wt9F9KW9J9V9KWS9P2tWV9r919HtbrL9TW79O9V9Wt9F9KW9J9V9KWS9P2tWVT949WbwY9TW79O9V9Wt9F9KW9J9V9KWS9P2tWVJ9V29VVbDl79IV9Rbqq:W9Dklbzik94evu8Jjjjjbcz9Rhbcbheincbhdcbhiinabcwfadfaicjuaead4ceGglE86bbaialfhiadcefgdcw9hmbkaeai86b:q:W:cjbaecitab8Piw83i:q:G:cjbaecefgecjd9hmbkk:JBl8Aud97dur978Jjjjjbcj;kb9Rgv8Kjjjjbc9:hodnalTmbcuhoaiRbbgrc;WeGc:Ge9hmbarcsGgwce0mbc9:hoalcufadcd4cbawEgDadfgrcKcaawEgqaraq0Egk6mbaialfgxar9RhodnadTgmmbavaoad;8qbbkaicefhPcj;abad9Uc;WFbGcjdadca0EhsdndndnadTmbaoadfhzcbhHinaeaH9nmdaxaP9RaD6miabaHad2fhOaPaDfhAasaeaH9RaHasfae6EgCcsfgocl4cifcd4hXavcj;cbfaoc9WGgQcetfhLavcj;cbfaQci2fhKavcj;cbfaQfhYcbh8Aaoc;ab6hEincbh3dnawTmbaPa8Acd4fRbbh3kcbh5avcj;cbfh8Eindndndndna3a5cet4ciGgoc9:fPdebdkaxaA9RaQ6mwdnaQTmbavcj;cbfa5aQ2faAaQ;8qbbkaAaCfhAxdkaQTmeavcj;cbfa5aQ2fcbaQ;8kbxekaxaA9RaX6moaoclVcbawEhraAaXfhocbhidnaEmbaxao9Rc;Gb6mbcbhlina8EalfhidndndndndndnaAalco4fRbbgqciGarfPDbedibledibkaipxbbbbbbbbbbbbbbbbpklbxlkaiaopbblaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLg8Fcdp:mea8FpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogapxiiiiiiiiiiiiiiiip8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbahaoclffagRb:q:W:cjbfhoxikaiaopbbwaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogapxssssssssssssssssp8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbahaocwffagRb:q:W:cjbfhoxdkaiaopbbbpklbaoczfhoxekaiaopbbdaoRbbghcitpbi:q:G:cjbahRb:q:W:cjbghpsaoRbeggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPpklbahaocdffagRb:q:W:cjbfhokdndndndndndnaqcd4ciGarfPDbedibledibkaiczfpxbbbbbbbbbbbbbbbbpklbxlkaiczfaopbblaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLg8Fcdp:mea8FpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogapxiiiiiiiiiiiiiiiip8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbahaoclffagRb:q:W:cjbfhoxikaiczfaopbbwaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogapxssssssssssssssssp8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbahaocwffagRb:q:W:cjbfhoxdkaiczfaopbbbpklbaoczfhoxekaiczfaopbbdaoRbbghcitpbi:q:G:cjbahRb:q:W:cjbghpsaoRbeggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPpklbahaocdffagRb:q:W:cjbfhokdndndndndndnaqcl4ciGarfPDbedibledibkaicafpxbbbbbbbbbbbbbbbbpklbxlkaicafaopbblaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLg8Fcdp:mea8FpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogapxiiiiiiiiiiiiiiiip8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbahaoclffagRb:q:W:cjbfhoxikaicafaopbbwaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogapxssssssssssssssssp8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbahaocwffagRb:q:W:cjbfhoxdkaicafaopbbbpklbaoczfhoxekaicafaopbbdaoRbbghcitpbi:q:G:cjbahRb:q:W:cjbghpsaoRbeggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPpklbahaocdffagRb:q:W:cjbfhokdndndndndndnaqco4arfPDbedibledibkaic8Wfpxbbbbbbbbbbbbbbbbpklbxlkaic8Wfaopbblaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLg8Fcdp:mea8FpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogapxiiiiiiiiiiiiiiiip8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Ngicitpbi:q:G:cjbaiRb:q:W:cjbgipsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Ngqcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbaiaoclffaqRb:q:W:cjbfhoxikaic8Wfaopbbwaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogapxssssssssssssssssp8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Ngicitpbi:q:G:cjbaiRb:q:W:cjbgipsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Ngqcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbaiaocwffaqRb:q:W:cjbfhoxdkaic8Wfaopbbbpklbaoczfhoxekaic8WfaopbbdaoRbbgicitpbi:q:G:cjbaiRb:q:W:cjbgipsaoRbegqcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPpklbaiaocdffaqRb:q:W:cjbfhokalc;abfhialcjefaQ0meaihlaxao9Rc;Fb0mbkkdnaiaQ9pmbaici4hlinaxao9RcK6mwa8EaifhqdndndndndndnaAaico4fRbbalcoG4ciGarfPDbedibledibkaqpxbbbbbbbbbbbbbbbbpkbbxlkaqaopbblaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLg8Fcdp:mea8FpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogapxiiiiiiiiiiiiiiiip8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spkbbahaoclffagRb:q:W:cjbfhoxikaqaopbbwaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogapxssssssssssssssssp8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spkbbahaocwffagRb:q:W:cjbfhoxdkaqaopbbbpkbbaoczfhoxekaqaopbbdaoRbbghcitpbi:q:G:cjbahRb:q:W:cjbghpsaoRbeggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPpkbbahaocdffagRb:q:W:cjbfhokalcdfhlaiczfgiaQ6mbkkaohAaoTmoka8EaQfh8Ea5cefg5cl9hmbkdndndndnawTmbaza8Acd4fRbbglciGPlbedwbkaQTmdavcjdfa8Afhlava8Afpbdbh8Jcbhoinalavcj;cbfaofpblbg8KaYaofpblbg8LpmbzeHdOiAlCvXoQrLg8MaLaofpblbg8NaKaofpblbgypmbzeHdOiAlCvXoQrLg8PpmbezHdiOAlvCXorQLg8Fcep9Ta8Fpxeeeeeeeeeeeeeeeegap9op9Hp9rg8Fa8Jp9Ug8Jp9Abbbaladfgla8Ja8Fa8Fpmlvorlvorlvorlvorp9Ug8Jp9Abbbaladfgla8Ja8Fa8FpmwDqkwDqkwDqkwDqkp9Ug8Jp9Abbbaladfgla8Ja8Fa8FpmxmPsxmPsxmPsxmPsp9Ug8Jp9Abbbaladfgla8Ja8Ma8PpmwDKYqk8AExm35Ps8E8Fg8Fcep9Ta8Faap9op9Hp9rg8Fp9Ug8Jp9Abbbaladfgla8Ja8Fa8Fpmlvorlvorlvorlvorp9Ug8Jp9Abbbaladfgla8Ja8Fa8FpmwDqkwDqkwDqkwDqkp9Ug8Jp9Abbbaladfgla8Ja8Fa8FpmxmPsxmPsxmPsxmPsp9Ug8Jp9Abbbaladfgla8Ja8Ka8LpmwKDYq8AkEx3m5P8Es8Fg8Ka8NaypmwKDYq8AkEx3m5P8Es8Fg8LpmbezHdiOAlvCXorQLg8Fcep9Ta8Faap9op9Hp9rg8Fp9Ug8Jp9Abbbaladfgla8Ja8Fa8Fpmlvorlvorlvorlvorp9Ug8Jp9Abbbaladfgla8Ja8Fa8FpmwDqkwDqkwDqkwDqkp9Ug8Jp9Abbbaladfgla8Ja8Fa8FpmxmPsxmPsxmPsxmPsp9Ug8Jp9Abbbaladfgla8Ja8Ka8LpmwDKYqk8AExm35Ps8E8Fg8Fcep9Ta8Faap9op9Hp9rg8Fp9Ugap9Abbbaladfglaaa8Fa8Fpmlvorlvorlvorlvorp9Ugap9Abbbaladfglaaa8Fa8FpmwDqkwDqkwDqkwDqkp9Ugap9Abbbaladfglaaa8Fa8FpmxmPsxmPsxmPsxmPsp9Ug8Jp9AbbbaladfhlaoczfgoaQ6mbxikkaQTmeavcjdfa8Afhlava8Afpbdbh8Jcbhoinalavcj;cbfaofpblbg8KaYaofpblbg8LpmbzeHdOiAlCvXoQrLg8MaLaofpblbg8NaKaofpblbgypmbzeHdOiAlCvXoQrLg8PpmbezHdiOAlvCXorQLg8Fcep:nea8Fpxebebebebebebebebgap9op:bep9rg8Fa8Jp:oeg8Jp9Abbbaladfgla8Ja8Fa8Fpmlvorlvorlvorlvorp:oeg8Jp9Abbbaladfgla8Ja8Fa8FpmwDqkwDqkwDqkwDqkp:oeg8Jp9Abbbaladfgla8Ja8Fa8FpmxmPsxmPsxmPsxmPsp:oeg8Jp9Abbbaladfgla8Ja8Ma8PpmwDKYqk8AExm35Ps8E8Fg8Fcep:nea8Faap9op:bep9rg8Fp:oeg8Jp9Abbbaladfgla8Ja8Fa8Fpmlvorlvorlvorlvorp:oeg8Jp9Abbbaladfgla8Ja8Fa8FpmwDqkwDqkwDqkwDqkp:oeg8Jp9Abbbaladfgla8Ja8Fa8FpmxmPsxmPsxmPsxmPsp:oeg8Jp9Abbbaladfgla8Ja8Ka8LpmwKDYq8AkEx3m5P8Es8Fg8Ka8NaypmwKDYq8AkEx3m5P8Es8Fg8LpmbezHdiOAlvCXorQLg8Fcep:nea8Faap9op:bep9rg8Fp:oeg8Jp9Abbbaladfgla8Ja8Fa8Fpmlvorlvorlvorlvorp:oeg8Jp9Abbbaladfgla8Ja8Fa8FpmwDqkwDqkwDqkwDqkp:oeg8Jp9Abbbaladfgla8Ja8Fa8FpmxmPsxmPsxmPsxmPsp:oeg8Jp9Abbbaladfgla8Ja8Ka8LpmwDKYqk8AExm35Ps8E8Fg8Fcep:nea8Faap9op:bep9rg8Fp:oegap9Abbbaladfglaaa8Fa8Fpmlvorlvorlvorlvorp:oegap9Abbbaladfglaaa8Fa8FpmwDqkwDqkwDqkwDqkp:oegap9Abbbaladfglaaa8Fa8FpmxmPsxmPsxmPsxmPsp:oeg8Jp9AbbbaladfhlaoczfgoaQ6mbxdkkaQTmbcbhocbalcl4gl9Rc8FGhiavcjdfa8Afhrava8Afpbdbhainaravcj;cbfaofpblbg8JaYaofpblbg8KpmbzeHdOiAlCvXoQrLg8LaLaofpblbg8MaKaofpblbg8NpmbzeHdOiAlCvXoQrLgypmbezHdiOAlvCXorQLg8Faip:Rea8Falp:Tep9qg8Faap9rgap9Abbbaradfgraaa8Fa8Fpmlvorlvorlvorlvorp9rgap9Abbbaradfgraaa8Fa8FpmwDqkwDqkwDqkwDqkp9rgap9Abbbaradfgraaa8Fa8FpmxmPsxmPsxmPsxmPsp9rgap9Abbbaradfgraaa8LaypmwDKYqk8AExm35Ps8E8Fg8Faip:Rea8Falp:Tep9qg8Fp9rgap9Abbbaradfgraaa8Fa8Fpmlvorlvorlvorlvorp9rgap9Abbbaradfgraaa8Fa8FpmwDqkwDqkwDqkwDqkp9rgap9Abbbaradfgraaa8Fa8FpmxmPsxmPsxmPsxmPsp9rgap9Abbbaradfgraaa8Ja8KpmwKDYq8AkEx3m5P8Es8Fg8Ja8Ma8NpmwKDYq8AkEx3m5P8Es8Fg8KpmbezHdiOAlvCXorQLg8Faip:Rea8Falp:Tep9qg8Fp9rgap9Abbbaradfgraaa8Fa8Fpmlvorlvorlvorlvorp9rgap9Abbbaradfgraaa8Fa8FpmwDqkwDqkwDqkwDqkp9rgap9Abbbaradfgraaa8Fa8FpmxmPsxmPsxmPsxmPsp9rgap9Abbbaradfgraaa8Ja8KpmwDKYqk8AExm35Ps8E8Fg8Faip:Rea8Falp:Tep9qg8Fp9rgap9Abbbaradfgraaa8Fa8Fpmlvorlvorlvorlvorp9rgap9Abbbaradfgraaa8Fa8FpmwDqkwDqkwDqkwDqkp9rgap9Abbbaradfgraaa8Fa8FpmxmPsxmPsxmPsxmPsp9rgap9AbbbaradfhraoczfgoaQ6mbkka8Aclfg8Aad6mbkdnaCad2goTmbaOavcjdfao;8qbbkdnammbavavcjdfaCcufad2fad;8qbbkaCaHfhHc9:hoaAhPaAmbxlkkaeTmbaDalfhrcbhocuhlinaralaD9RglfaD6mdasaeao9Raoasfae6Eaofgoae6mbkaial9RhPkcbc99axaP9RakSEhoxekc9:hokavcj;kbf8Kjjjjbaokwbz:bjjjbkNsezu8Jjjjjbc;ae9Rgv8Kjjjjbc9:hodnalaeci9UgrcHf6mbcuhoaiRbbgwc;WeGc;Ge9hmbawcsGgDce0mbavc;abfcFecje;8kbav9cu83iUav9cu83i8Wav9cu83iyav9cu83iaav9cu83iKav9cu83izav9cu83iwav9cu83ibaialfc9WfhqaicefgwarfhldnaeTmbcmcsaDceSEhkcbhxcbhmcbhrcbhicbhoindnalaq9nmbc9:hoxikdndnawRbbgDc;Ve0mbavc;abfaoaDcu7gPcl4fcsGcitfgsydlhzasydbhHdndnaDcsGgsak9pmbavaiaPfcsGcdtfydbaxasEhDaxasTgOfhxxekdndnascsSmbcehOasc987asamffcefhDxekalcefhDal8SbbgscFeGhPdndnascu9mmbaDhlxekalcvfhlaPcFbGhPcrhsdninaD8SbbgOcFbGastaPVhPaOcu9kmeaDcefhDascrfgsc8J9hmbxdkkaDcefhlkcehOaPce4cbaPceG9R7amfhDkaDhmkavc;abfaocitfgsaDBdbasazBdlavaicdtfaDBdbavc;abfaocefcsGcitfgsaHBdbasaDBdlaocdfhoaOaifhidnadcd9hmbabarcetfgsaH87ebasclfaD87ebascdfaz87ebxdkabarcdtfgsaHBdbascwfaDBdbasclfazBdbxekdnaDcpe0mbavaiaqaDcsGfRbbgscl4gP9RcsGcdtfydbaxcefgOaPEhDavaias9RcsGcdtfydbaOaPTgzfgOascsGgPEhsaPThPdndnadcd9hmbabarcetfgHax87ebaHclfas87ebaHcdfaD87ebxekabarcdtfgHaxBdbaHcwfasBdbaHclfaDBdbkavaicdtfaxBdbavc;abfaocitfgHaDBdbaHaxBdlavaicefgicsGcdtfaDBdbavc;abfaocefcsGcitfgHasBdbaHaDBdlavaiazfgicsGcdtfasBdbavc;abfaocdfcsGcitfgDaxBdbaDasBdlaocifhoaiaPfhiaOaPfhxxekaxcbalRbbgsEgHaDc;:eSgDfhOascsGhAdndnascl4gCmbaOcefhzxekaOhzavaiaC9RcsGcdtfydbhOkdndnaAmbazcefhxxekazhxavaias9RcsGcdtfydbhzkdndnaDTmbalcefhDxekalcdfhDal8SbegPcFeGhsdnaPcu9kmbalcofhHascFbGhscrhldninaD8SbbgPcFbGaltasVhsaPcu9kmeaDcefhDalcrfglc8J9hmbkaHhDxekaDcefhDkasce4cbasceG9R7amfgmhHkdndnaCcsSmbaDhsxekaDcefhsaD8SbbglcFeGhPdnalcu9kmbaDcvfhOaPcFbGhPcrhldninas8SbbgDcFbGaltaPVhPaDcu9kmeascefhsalcrfglc8J9hmbkaOhsxekascefhskaPce4cbaPceG9R7amfgmhOkdndnaAcsSmbashlxekascefhlas8SbbgDcFeGhPdnaDcu9kmbascvfhzaPcFbGhPcrhDdninal8SbbgscFbGaDtaPVhPascu9kmealcefhlaDcrfgDc8J9hmbkazhlxekalcefhlkaPce4cbaPceG9R7amfgmhzkdndnadcd9hmbabarcetfgDaH87ebaDclfaz87ebaDcdfaO87ebxekabarcdtfgDaHBdbaDcwfazBdbaDclfaOBdbkavc;abfaocitfgDaOBdbaDaHBdlavaicdtfaHBdbavc;abfaocefcsGcitfgDazBdbaDaOBdlavaicefgicsGcdtfaOBdbavc;abfaocdfcsGcitfgDaHBdbaDazBdlavaiaCTaCcsSVfgicsGcdtfazBdbaiaATaAcsSVfhiaocifhokawcefhwaocsGhoaicsGhiarcifgrae6mbkkcbc99alaqSEhokavc;aef8Kjjjjbaok:clevu8Jjjjjbcz9Rhvdnalaecvf9pmbc9:skdnaiRbbc;:eGc;qeSmbcuskav9cb83iwaicefhoaialfc98fhrdnaeTmbdnadcdSmbcbhwindnaoar6mbc9:skaocefhlao8SbbgicFeGhddndnaicu9mmbalhoxekaocvfhoadcFbGhdcrhidninal8SbbgDcFbGaitadVhdaDcu9kmealcefhlaicrfgic8J9hmbxdkkalcefhokabawcdtfadc8Etc8F91adcd47avcwfadceGcdtVglydbfgiBdbalaiBdbawcefgwae9hmbxdkkcbhwindnaoar6mbc9:skaocefhlao8SbbgicFeGhddndnaicu9mmbalhoxekaocvfhoadcFbGhdcrhidninal8SbbgDcFbGaitadVhdaDcu9kmealcefhlaicrfgic8J9hmbxdkkalcefhokabawcetfadc8Etc8F91adcd47avcwfadceGcdtVglydbfgi87ebalaiBdbawcefgwae9hmbkkcbc99aoarSEk;Toio97eue97aec98Ghedndnadcl9hmbaeTmecbhdinababpbbbgicKp:RecKp:Sep;6eglaicwp:RecKp:Sep;6ealp;Geaiczp:RecKp:Sep;6egvp;Gep;Kep;Legopxbbbbbbbbbbbbbbbbp:2egralpxbbbjbbbjbbbjbbbjgwp9op9rp;Keglpxbb;:9cbb;:9cbb;:9cbb;:9calalp;Meaoaop;Meavaravawp9op9rp;Keglalp;Mep;Kep;Kep;Jep;Negvp;Mepxbbn0bbn0bbn0bbn0grp;KepxFbbbFbbbFbbbFbbbp9oaipxbbbFbbbFbbbFbbbFp9op9qalavp;Mearp;Kecwp:RepxbFbbbFbbbFbbbFbbp9op9qaoavp;Mearp;Keczp:RepxbbFbbbFbbbFbbbFbp9op9qpkbbabczfhbadclfgdae6mbxdkkaeTmbcbhdinabczfgDaDpbbbgipxbbbbbbFFbbbbbbFFgwp9oabpbbbgoaipmbediwDqkzHOAKY8AEgvczp:Reczp:Sep;6eglaoaipmlvorxmPsCXQL358E8FpxFubbFubbFubbFubbp9op;6eavczp:Sep;6egvp;Gealp;Gep;Kep;Legipxbbbbbbbbbbbbbbbbp:2egralpxbbbjbbbjbbbjbbbjgqp9op9rp;Keglpxb;:FSb;:FSb;:FSb;:FSalalp;Meaiaip;Meavaravaqp9op9rp;Keglalp;Mep;Kep;Kep;Jep;Negvp;Mepxbbn0bbn0bbn0bbn0grp;KepxFFbbFFbbFFbbFFbbp9oaiavp;Mearp;Keczp:Rep9qgialavp;Mearp;KepxFFbbFFbbFFbbFFbbp9oglpmwDKYqk8AExm35Ps8E8Fp9qpkbbabaoawp9oaialpmbezHdiOAlvCXorQLp9qpkbbabcafhbadclfgdae6mbkkk;2ileue97euo97dnaec98GgiTmbcbheinabcKfpx:ji:1S:ji:1S:ji:1S:ji:1SabpbbbglabczfgvpbbbgopmlvorxmPsCXQL358E8Fgrczp:Segwpxibbbibbbibbbibbbp9qp;6egDp;NegqaDaDp;MegDaDp;KealaopmbediwDqkzHOAKY8AEgDczp:Reczp:Sep;6eglalp;MeaDczp:Sep;6egoaop;Mearczp:Reczp:Sep;6egrarp;Mep;Kep;Kep;Lepxbbbbbbbbbbbbbbbbp:4ep;Jep;Mepxbbn0bbn0bbn0bbn0gDp;KepxFFbbFFbbFFbbFFbbgkp9oaqaop;MeaDp;Keczp:Rep9qgoaqalp;MeaDp;Keakp9oaqarp;MeaDp;Keczp:Rep9qgDpmwDKYqk8AExm35Ps8E8Fglp5eawclp:RegqpEi:T:j83ibavalp5baqpEd:T:j83ibabcwfaoaDpmbezHdiOAlvCXorQLgDp5eaqpEe:T:j83ibabaDp5baqpEb:T:j83ibabcafhbaeclfgeai6mbkkkuee97dnadcd4ae2c98GgeTmbcbhdinababpbbbgicwp:Recwp:Sep;6eaicep:SepxbbjFbbjFbbjFbbjFp9opxbbjZbbjZbbjZbbjZp:Uep;Mepkbbabczfhbadclfgdae6mbkkk:Sodw97euaec98Ghedndnadcl9hmbaeTmecbhdinabpxbbuJbbuJbbuJbbuJabpbbbgicKp:TeglaicYp:Tep9qgvcdp:Teavp9qgvclp:Teavp9qgop;6ep;Negvaicwp:RecKp:SegraipxFbbbFbbbFbbbFbbbgwp9ogDp:Uep;6ep;Mepxbbn0bbn0bbn0bbn0gqp;Kecwp:RepxbFbbbFbbbFbbbFbbp9oavaDarp:Xeaiczp:RecKp:Segip:Uep;6ep;Meaqp;Keawp9op9qavaDaraip:Uep:Xep;6ep;Meaqp;Keczp:RepxbbFbbbFbbbFbbbFbp9op9qavaoalcep:Rep9oalpxebbbebbbebbbebbbp9op9qp;6ep;Meaqp;KecKp:Rep9qpkbbabczfhbadclfgdae6mbxdkkaeTmbcbhdinabczfgkpxbFu9hbFu9hbFu9hbFu9habpbbbglakpbbbgrpmlvorxmPsCXQL358E8Fgvczp:TegqavcHp:Tep9qgicdp:Teaip9qgiclp:Teaip9qgicwp:Teaip9qgop;6ep;NegialarpmbediwDqkzHOAKY8AEgDpxFFbbFFbbFFbbFFbbglp9ograDczp:Segwp:Ueavczp:Reczp:SegDp:Xep;6ep;Mepxbbn0bbn0bbn0bbn0gvp;Kealp9oaiarawaDp:Uep:Xep;6ep;Meavp;Keczp:Rep9qgwaiaoaqcep:Rep9oaqpxebbbebbbebbbebbbp9op9qp;6ep;Meavp;Keczp:ReaiaDarp:Uep;6ep;Meavp;Kealp9op9qgipmwDKYqk8AExm35Ps8E8FpkbbabawaipmbezHdiOAlvCXorQLpkbbabcafhbadclfgdae6mbkkk9teiucbcbydj:G:cjbgeabcifc98GfgbBdj:G:cjbdndnabZbcztgd9nmbcuhiabad9RcFFifcz4nbcuSmekaehikaikkxebcj:Gdklz:zbb'; // embed! simd
64298
64464
 
64299
64465
  var detector = new Uint8Array([
64300
64466
  0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 3, 3, 2, 0, 0, 5, 3, 1, 0, 1, 12, 1, 0, 10, 22, 2, 12, 0, 65, 0, 65, 0, 65, 0, 252, 10, 0, 0,
@@ -64356,6 +64522,7 @@
64356
64522
  OCTAHEDRAL: 'meshopt_decodeFilterOct',
64357
64523
  QUATERNION: 'meshopt_decodeFilterQuat',
64358
64524
  EXPONENTIAL: 'meshopt_decodeFilterExp',
64525
+ COLOR: 'meshopt_decodeFilterColor',
64359
64526
  };
64360
64527
 
64361
64528
  var decoders = {
@@ -64434,10 +64601,10 @@
64434
64601
 
64435
64602
  function workerProcess(event) {
64436
64603
  var data = event.data;
64437
- if (!data.id) {
64438
- return self.close();
64439
- }
64440
64604
  self.ready.then(function (instance) {
64605
+ if (!data.id) {
64606
+ return self.close();
64607
+ }
64441
64608
  try {
64442
64609
  var target = new Uint8Array(data.count * data.size);
64443
64610
  decode(instance, instance.exports[data.mode], target, data.count, data.size, data.source, instance.exports[data.filter]);
@@ -66358,6 +66525,584 @@
66358
66525
  _Stats.Panel = Panel;
66359
66526
  let Stats = _Stats;
66360
66527
 
66528
+ // Limitations
66529
+ // - VSM shadows not supported
66530
+ // - MRT not supported
66531
+ // - Transmission not supported
66532
+ // - WebGPU postprocessing stack not supported
66533
+ // - Storage textures not supported
66534
+ // - Fog / environment do not automatically update - must call "dispose"
66535
+ // - instanced mesh geometry cannot be shared
66536
+ // - Node materials cannot be used with "compile" function
66537
+
66538
+ // hash any object parameters that will impact the resulting shader so we can force
66539
+ // a program update
66540
+ function getObjectHash( object ) {
66541
+
66542
+ return '' + object.receiveShadow;
66543
+
66544
+ }
66545
+
66546
+ // Mirrors WebGLUniforms.seqWithValue from WebGLRenderer
66547
+ function generateUniformsList( program, uniforms ) {
66548
+
66549
+ const progUniforms = program.getUniforms();
66550
+ const uniformsList = [];
66551
+
66552
+ for ( let i = 0; i < progUniforms.seq.length; i ++ ) {
66553
+
66554
+ const u = progUniforms.seq[ i ];
66555
+ if ( u.id in uniforms ) uniformsList.push( u );
66556
+
66557
+ }
66558
+
66559
+ return uniformsList;
66560
+
66561
+ }
66562
+
66563
+ // overrides shadow nodes to use the built in shadow textures
66564
+ class WebGLNodeBuilder extends webgpu.GLSLNodeBuilder {
66565
+
66566
+ addNode( node ) {
66567
+
66568
+ if ( node.isShadowNode ) {
66569
+
66570
+ node.setupRenderTarget = shadow => {
66571
+
66572
+ return { shadowMap: shadow.map, depthTexture: shadow.map.depthTexture };
66573
+
66574
+ };
66575
+
66576
+ node.updateBefore = () => {
66577
+
66578
+ // no need to rerender shadows since WebGLRenderer is handling it
66579
+
66580
+ };
66581
+
66582
+ }
66583
+
66584
+ super.addNode( node );
66585
+
66586
+ }
66587
+
66588
+ }
66589
+
66590
+ // produce and update reusable nodes for a scene
66591
+ class SceneContext {
66592
+
66593
+ constructor( renderer, scene ) {
66594
+
66595
+ // TODO: can / should we update the fog and environment node every frame for recompile?
66596
+ this.renderer = renderer;
66597
+ this.scene = scene;
66598
+ this.lightsNode = renderer.lighting.getNode( scene );
66599
+ this.fogNode = null;
66600
+ this.environmentNode = null;
66601
+ this.prevFog = null;
66602
+ this.prevEnvironment = null;
66603
+
66604
+ }
66605
+
66606
+ getCacheKey() {
66607
+
66608
+ const { lightsNode, environmentNode, fogNode } = this;
66609
+ const lightsHash = lightsNode.getCacheKey();
66610
+ const envHash = environmentNode ? environmentNode.getCacheKey : 0;
66611
+ const fogHash = fogNode ? fogNode.getCacheKey() : 0;
66612
+ return webgpu.NodeUtils.hashArray( [ lightsHash, envHash, fogHash ] );
66613
+
66614
+ }
66615
+
66616
+ update() {
66617
+
66618
+ const { scene, lightsNode } = this;
66619
+
66620
+ // update lighting
66621
+ const sceneLights = [];
66622
+ scene.traverse( object => {
66623
+
66624
+ if ( object.isLight ) {
66625
+
66626
+ sceneLights.push( object );
66627
+
66628
+ }
66629
+
66630
+ } );
66631
+
66632
+ lightsNode.setLights( sceneLights );
66633
+
66634
+ // update fog
66635
+ if ( this.prevFog !== scene.fog ) {
66636
+
66637
+ this.fogNode = this.getFogNode();
66638
+ this.prevFog = scene.fog;
66639
+
66640
+ }
66641
+
66642
+ // update environment
66643
+ if ( this.prevEnvironment !== scene.environment ) {
66644
+
66645
+ this.environmentNode = this.getEnvironmentNode();
66646
+ this.prevEnvironment = scene.environment;
66647
+
66648
+ }
66649
+
66650
+ }
66651
+
66652
+ getFogNode() {
66653
+
66654
+ const { scene } = this;
66655
+ if ( scene.fog && scene.fog.isFogExp2 ) {
66656
+
66657
+ const color = tsl.reference( 'color', 'color', scene.fog );
66658
+ const density = tsl.reference( 'density', 'float', scene.fog );
66659
+ return tsl.fog( color, tsl.densityFogFactor( density ) );
66660
+
66661
+ } else if ( scene.fog && scene.fog.isFog ) {
66662
+
66663
+ const color = tsl.reference( 'color', 'color', scene.fog );
66664
+ const near = tsl.reference( 'near', 'float', scene.fog );
66665
+ const far = tsl.reference( 'far', 'float', scene.fog );
66666
+ return tsl.fog( color, tsl.rangeFogFactor( near, far ) );
66667
+
66668
+ } else {
66669
+
66670
+ return null;
66671
+
66672
+ }
66673
+
66674
+ }
66675
+
66676
+ getEnvironmentNode() {
66677
+
66678
+ const { scene } = this;
66679
+ if ( scene.environment && scene.environment.isCubeTexture ) {
66680
+
66681
+ return tsl.cubeTexture( scene.environment );
66682
+
66683
+ } else if ( scene.environment && scene.environment.isTexture ) {
66684
+
66685
+ return tsl.texture( scene.environment );
66686
+
66687
+ } else {
66688
+
66689
+ return null;
66690
+
66691
+ }
66692
+
66693
+ }
66694
+
66695
+ }
66696
+
66697
+ class RendererProxy {
66698
+
66699
+ constructor( renderer ) {
66700
+
66701
+ const backend = {
66702
+ isWebGPUBackend: false,
66703
+ extensions: renderer.extensions,
66704
+ gl: renderer.getContext(),
66705
+ capabilities: null,
66706
+ };
66707
+
66708
+ backend.capabilities = new webgpu.WebGLCapabilities( backend );
66709
+
66710
+ this.contextNode = tsl.context();
66711
+ this.inspector = new webgpu.InspectorBase();
66712
+ this.library = new webgpu.BasicNodeLibrary();
66713
+ this.lighting = new webgpu.Lighting();
66714
+ this.backend = backend;
66715
+
66716
+ const self = this;
66717
+ return new Proxy( renderer, {
66718
+
66719
+ get( target, property ) {
66720
+
66721
+ return Reflect.get( property in self ? self : target, property );
66722
+
66723
+ },
66724
+
66725
+ set( target, property, value ) {
66726
+
66727
+ return Reflect.set( property in self ? self : target, property, value );
66728
+
66729
+ }
66730
+
66731
+ } );
66732
+
66733
+ }
66734
+
66735
+ hasInitialized() {
66736
+
66737
+ return true;
66738
+
66739
+ }
66740
+
66741
+ getMRT() {
66742
+
66743
+ return null;
66744
+
66745
+ }
66746
+
66747
+ hasCompatibility( name ) {
66748
+
66749
+ if ( name === THREE.Compatibility.TEXTURE_COMPARE ) {
66750
+
66751
+ return true;
66752
+
66753
+ }
66754
+
66755
+ return false;
66756
+
66757
+ }
66758
+
66759
+ getCacheKey() {
66760
+
66761
+ return this.toneMapping + this.outputColorSpace;
66762
+
66763
+ }
66764
+
66765
+ }
66766
+
66767
+ /**
66768
+ * Compatibility loader and builder for TSL Node materials in WebGLRenderer.
66769
+ */
66770
+ class WebGLNodesHandler {
66771
+
66772
+ /**
66773
+ * Constructs a new WebGL node adapter.
66774
+ */
66775
+ constructor() {
66776
+
66777
+ this.renderer = null;
66778
+ this.nodeFrame = new webgpu.NodeFrame();
66779
+ this.sceneContexts = new WeakMap();
66780
+ this.programCache = new Map();
66781
+ this.renderStack = [];
66782
+
66783
+ const self = this;
66784
+ this.onDisposeMaterialCallback = function () {
66785
+
66786
+ // dispose of all the uniform groups
66787
+ const { programCache } = self;
66788
+ if ( programCache.has( this ) ) {
66789
+
66790
+ self.programCache.get( this ).forEach( ( { uniformsGroups } ) => {
66791
+
66792
+ uniformsGroups.forEach( u => u.dispose() );
66793
+
66794
+ } );
66795
+
66796
+ self.programCache.delete( this );
66797
+
66798
+ }
66799
+
66800
+ this.removeEventListener( 'dispose', self.onDisposeMaterialCallback );
66801
+
66802
+ };
66803
+
66804
+ this.getOutputCallback = function ( outputNode ) {
66805
+
66806
+ // apply tone mapping and color spaces to the output
66807
+ const { outputColorSpace, toneMapping } = self.renderer;
66808
+ outputNode = outputNode.toneMapping( toneMapping );
66809
+ outputNode = tsl.workingToColorSpace( outputNode, outputColorSpace );
66810
+
66811
+ return outputNode;
66812
+
66813
+ };
66814
+
66815
+ this.onBeforeRenderCallback = function ( renderer, scene, camera, geometry, object ) {
66816
+
66817
+ // update node frame references for update nodes
66818
+ const { nodeFrame } = self;
66819
+ nodeFrame.material = this;
66820
+ nodeFrame.object = object;
66821
+
66822
+ // increment "frame" here to force uniform buffers to update for the material, which otherwise only get
66823
+ // updated once per frame.
66824
+ renderer.info.render.frame ++;
66825
+
66826
+ // update the uniform groups and nodes for the program if they're available before rendering
66827
+ if ( renderer.properties.has( this ) ) {
66828
+
66829
+ const currentProgram = renderer.properties.get( this ).currentProgram;
66830
+ const programs = self.programCache.get( this );
66831
+ if ( programs && programs.has( currentProgram ) ) {
66832
+
66833
+ // update the nodes for the current object
66834
+ const { updateNodes } = programs.get( currentProgram );
66835
+ self.updateNodes( updateNodes );
66836
+
66837
+ }
66838
+
66839
+ }
66840
+
66841
+ const objectHash = getObjectHash( object );
66842
+ if ( this.prevObjectHash !== objectHash ) {
66843
+
66844
+ this.prevObjectHash = objectHash;
66845
+ this.needsUpdate = true;
66846
+
66847
+ }
66848
+
66849
+ };
66850
+
66851
+ this.customProgramCacheKeyCallback = function () {
66852
+
66853
+ const { renderStack, renderer, nodeFrame } = self;
66854
+ const sceneHash = renderStack[ renderStack.length - 1 ].sceneContext.getCacheKey();
66855
+ const materialHash = this.constructor.prototype.customProgramCacheKey.call( this );
66856
+ const rendererHash = renderer.getCacheKey();
66857
+
66858
+ return materialHash + sceneHash + rendererHash + getObjectHash( nodeFrame.object );
66859
+
66860
+ };
66861
+
66862
+ }
66863
+
66864
+ setRenderer( renderer ) {
66865
+
66866
+ const rendererProxy = new RendererProxy( renderer );
66867
+ this.nodeFrame.renderer = rendererProxy;
66868
+ this.renderer = rendererProxy;
66869
+
66870
+ }
66871
+
66872
+ onUpdateProgram( material, program, materialProperties ) {
66873
+
66874
+ const { programCache } = this;
66875
+ if ( ! programCache.has( material ) ) {
66876
+
66877
+ programCache.set( material, new Map() );
66878
+
66879
+ }
66880
+
66881
+ const programs = programCache.get( material );
66882
+ if ( ! programs.has( program ) ) {
66883
+
66884
+ const builder = material._latestBuilder;
66885
+ const uniforms = materialProperties.uniforms;
66886
+ programs.set( program, {
66887
+ uniformsGroups: this.collectUniformsGroups( builder ),
66888
+ uniforms: uniforms,
66889
+ uniformsList: generateUniformsList( program, uniforms ),
66890
+ updateNodes: builder.updateNodes,
66891
+ } );
66892
+
66893
+ }
66894
+
66895
+ const { uniformsGroups, uniforms, uniformsList, updateNodes } = programs.get( program );
66896
+ material.uniformsGroups = uniformsGroups;
66897
+ materialProperties.uniforms = uniforms;
66898
+ materialProperties.uniformsList = uniformsList;
66899
+ this.updateNodes( updateNodes );
66900
+
66901
+ }
66902
+
66903
+
66904
+ renderStart( scene, camera ) {
66905
+
66906
+ const { nodeFrame, renderStack, renderer, sceneContexts } = this;
66907
+ nodeFrame.update();
66908
+ nodeFrame.camera = camera;
66909
+ nodeFrame.scene = scene;
66910
+ nodeFrame.frameId ++;
66911
+
66912
+ let sceneContext = sceneContexts.get( scene );
66913
+ if ( ! sceneContext ) {
66914
+
66915
+ sceneContext = new SceneContext( renderer, scene );
66916
+ sceneContexts.set( scene, sceneContext );
66917
+
66918
+ }
66919
+
66920
+ sceneContext.update();
66921
+ renderStack.push( { sceneContext, camera } );
66922
+
66923
+ // ensure all node material callbacks are initialized before
66924
+ // traversal and build
66925
+ const {
66926
+ customProgramCacheKeyCallback,
66927
+ onBeforeRenderCallback,
66928
+ } = this;
66929
+
66930
+ scene.traverse( object => {
66931
+
66932
+ if ( object.material && object.material.isNodeMaterial ) {
66933
+
66934
+ object.material.customProgramCacheKey = customProgramCacheKeyCallback;
66935
+ object.material.onBeforeRender = onBeforeRenderCallback;
66936
+
66937
+ }
66938
+
66939
+ } );
66940
+
66941
+ }
66942
+
66943
+ renderEnd() {
66944
+
66945
+ const { nodeFrame, renderStack } = this;
66946
+
66947
+ renderStack.pop();
66948
+
66949
+ const frame = renderStack[ renderStack.length - 1 ];
66950
+ if ( frame ) {
66951
+
66952
+ const { camera, sceneContext } = frame;
66953
+ nodeFrame.camera = camera;
66954
+ nodeFrame.scene = sceneContext.scene;
66955
+
66956
+ }
66957
+
66958
+ }
66959
+
66960
+ build( material, object, parameters ) {
66961
+
66962
+ const {
66963
+ nodeFrame,
66964
+ renderer,
66965
+ getOutputCallback,
66966
+ onDisposeMaterialCallback,
66967
+ renderStack,
66968
+ } = this;
66969
+
66970
+ const {
66971
+ camera,
66972
+ sceneContext,
66973
+ } = renderStack[ renderStack.length - 1 ];
66974
+
66975
+ const {
66976
+ fogNode,
66977
+ environmentNode,
66978
+ lightsNode,
66979
+ scene,
66980
+ } = sceneContext;
66981
+
66982
+ // prepare the frame
66983
+ nodeFrame.material = material;
66984
+ nodeFrame.object = object;
66985
+
66986
+ // create & run the builder
66987
+ const builder = new WebGLNodeBuilder( object, renderer );
66988
+ builder.scene = scene;
66989
+ builder.camera = camera;
66990
+ builder.material = material;
66991
+ builder.fogNode = fogNode;
66992
+ builder.environmentNode = environmentNode;
66993
+ builder.lightsNode = lightsNode;
66994
+ builder.context.getOutput = getOutputCallback;
66995
+ builder.build();
66996
+
66997
+ // update the shader parameters and geometry for program creation and rendering
66998
+ this.updateShaderParameters( builder, parameters );
66999
+ this.updateGeometryAttributes( builder, object.geometry );
67000
+
67001
+ // reset node frame settings to account for any intermediate renders
67002
+ nodeFrame.material = material;
67003
+ nodeFrame.object = object;
67004
+
67005
+ // set up callbacks for uniforms and node updates
67006
+ material._latestBuilder = builder;
67007
+ material.addEventListener( 'dispose', onDisposeMaterialCallback );
67008
+ this.updateNodes( builder.updateNodes );
67009
+
67010
+ }
67011
+
67012
+ updateGeometryAttributes( builder, geometry ) {
67013
+
67014
+ // TODO: this may cause issues if the material / geometry is used in multiple places
67015
+
67016
+ // add instancing attributes
67017
+ builder.bufferAttributes.forEach( v => {
67018
+
67019
+ geometry.setAttribute( v.name, v.node.attribute );
67020
+
67021
+ } );
67022
+
67023
+ // force WebGLAttributes & WebGLBindingStates to refresh
67024
+ // could be fixed by running "build" sooner? Or calling "WebGLAttributes" separately for those
67025
+ // associated with a material?
67026
+ queueMicrotask( () => geometry.dispose() );
67027
+
67028
+ }
67029
+
67030
+ updateShaderParameters( builder, parameters ) {
67031
+
67032
+ // set up shaders
67033
+ parameters.isRawShaderMaterial = true;
67034
+ parameters.glslVersion = THREE.GLSL3;
67035
+ parameters.vertexShader = builder.vertexShader.replace( /#version 300 es/, '' );
67036
+ parameters.fragmentShader = builder.fragmentShader.replace( /#version 300 es/, '' );
67037
+
67038
+ // add uniforms accessed by WebGLRenderer
67039
+ parameters.uniforms = {
67040
+ fogColor: { value: new THREE.Color() },
67041
+ fogNear: { value: 0 },
67042
+ fogFar: { value: 0 },
67043
+ envMapIntensity: { value: 0 },
67044
+ ...THREE.UniformsUtils.clone( THREE.UniformsLib.lights )
67045
+ };
67046
+
67047
+ // init uniforms
67048
+ const builderUniforms = [ ...builder.uniforms.vertex, ...builder.uniforms.fragment ];
67049
+ for ( const uniform of builderUniforms ) {
67050
+
67051
+ parameters.uniforms[ uniform.name ] = uniform.node;
67052
+
67053
+ }
67054
+
67055
+ }
67056
+
67057
+ collectUniformsGroups( builder ) {
67058
+
67059
+ // create UniformsGroups for regular grouped uniforms
67060
+ const uniformsGroups = [];
67061
+ for ( const key in builder.uniformGroups ) {
67062
+
67063
+ const { uniforms } = builder.uniformGroups[ key ];
67064
+ const group = new THREE.UniformsGroup();
67065
+ group.name = key;
67066
+ group.uniforms = uniforms.map( node => node.nodeUniform );
67067
+ uniformsGroups.push( group );
67068
+
67069
+ }
67070
+
67071
+ // init uniforms
67072
+ const builderUniforms = [ ...builder.uniforms.vertex, ...builder.uniforms.fragment ];
67073
+ for ( const uniform of builderUniforms ) {
67074
+
67075
+ if ( uniform.type === 'buffer' ) {
67076
+
67077
+ // buffer uniforms are all nested in groups
67078
+ const group = new THREE.UniformsGroup();
67079
+ group.name = uniform.node.name;
67080
+ group.uniforms = [ uniform ];
67081
+ uniformsGroups.push( group );
67082
+
67083
+ }
67084
+
67085
+ }
67086
+
67087
+ return uniformsGroups;
67088
+
67089
+ }
67090
+
67091
+ updateNodes( updateNodes ) {
67092
+
67093
+ // update nodes for render
67094
+ const { nodeFrame } = this;
67095
+ nodeFrame.renderId ++;
67096
+ for ( const node of updateNodes ) {
67097
+
67098
+ nodeFrame.updateNode( node );
67099
+
67100
+ }
67101
+
67102
+ }
67103
+
67104
+ }
67105
+
66361
67106
  // -----------------------------------------------------------------------------------------------------------
66362
67107
  // __ ______ __ __ ______ __ __ ______ ______ ______
66363
67108
  // /\ \ /\ ___\ /\ "-./ \ /\ __ \ /\ "-.\ \ /\ __ \ /\__ _\ /\ ___\
@@ -68277,7 +69022,7 @@
68277
69022
  options;
68278
69023
  renderView = null;
68279
69024
  webGlRenderer;
68280
- //webGpuRenderer: WebGPURenderer;
69025
+ webGlNodesHandler;
68281
69026
  raycaster;
68282
69027
  renderers = {};
68283
69028
  requestFrameFuncs = [];
@@ -68354,7 +69099,8 @@
68354
69099
  this.layers.push(`Layer ${i + 1}`);
68355
69100
  }
68356
69101
  this.webGlRenderer = this._createWebGlRenderer(options);
68357
- //this.webGpuRenderer = this._createWebGpuRenderer();
69102
+ this.webGlNodesHandler = new WebGLNodesHandler();
69103
+ this.webGlRenderer.setNodesHandler(this.webGlNodesHandler);
68358
69104
  this.setDevicePixelRatio();
68359
69105
  this.setShadowType(ShadowMode.Pcf);
68360
69106
  this.setRendererSettings();
@@ -68427,22 +69173,6 @@
68427
69173
  webGlRenderer.setSize(this.width, this.height);
68428
69174
  return webGlRenderer;
68429
69175
  }
68430
- _createWebGpuRenderer() {
68431
- const gl = this.webGlRenderer.getContext();
68432
- if (!gl) {
68433
- throw new Error('Renderer._createWebGpuRenderer: WebGL2 context required.');
68434
- }
68435
- const webGpuRenderer = new webgpu.WebGPURenderer({
68436
- forceWebGL: true,
68437
- canvas: this.webGlRenderer.domElement,
68438
- context: gl,
68439
- depth: true,
68440
- antialias: false,
68441
- alpha: true
68442
- });
68443
- webGpuRenderer.setSize(this.width, this.height);
68444
- return webGpuRenderer;
68445
- }
68446
69176
  async init() {
68447
69177
  try {
68448
69178
  await loadFontFace("OpenSansRegular", openSansRegularUrl);
@@ -68455,9 +69185,6 @@
68455
69185
  getWebGLRenderer() {
68456
69186
  return this.webGlRenderer;
68457
69187
  }
68458
- /*getWebGPURenderer(): WebGPURenderer {
68459
- return this.webGpuRenderer;
68460
- }*/
68461
69188
  hookEvents() {
68462
69189
  if (this.renderView) {
68463
69190
  // We have to remember the pressed buttons of the pointer controller because we will only ever receive a single
@@ -76666,6 +77393,22 @@
76666
77393
 
76667
77394
  }
76668
77395
 
77396
+ toJSON() {
77397
+
77398
+ const data = super.toJSON();
77399
+ return data;
77400
+
77401
+ }
77402
+
77403
+ static fromJSON( data ) {
77404
+
77405
+ const options = data.options;
77406
+
77407
+ options.font = new Font( options.font.data );
77408
+ return new TextGeometry( options.text, options );
77409
+
77410
+ }
77411
+
76669
77412
  }
76670
77413
 
76671
77414
  var glyphs = {
@@ -80750,6 +81493,16 @@
80750
81493
  * sky.scale.setScalar( 10000 );
80751
81494
  * scene.add( sky );
80752
81495
  * ```
81496
+ *
81497
+ * It can be useful to hide the sun disc when generating an environment map to avoid artifacts
81498
+ *
81499
+ * ```js
81500
+ * // disable before rendering environment map
81501
+ * sky.material.uniforms.showSunDisc.value = false;
81502
+ * // ...
81503
+ * // re-enable before scene sky box rendering
81504
+ * sky.material.uniforms.showSunDisc.value = true;
81505
+ * ```
80753
81506
  *
80754
81507
  * @augments Mesh
80755
81508
  * @three_import import { Sky } from 'three/addons/objects/Sky.js';
@@ -80797,7 +81550,14 @@
80797
81550
  'mieCoefficient': { value: 0.005 },
80798
81551
  'mieDirectionalG': { value: 0.8 },
80799
81552
  'sunPosition': { value: new THREE.Vector3() },
80800
- 'up': { value: new THREE.Vector3( 0, 1, 0 ) }
81553
+ 'up': { value: new THREE.Vector3( 0, 1, 0 ) },
81554
+ 'cloudScale': { value: 0.0002 },
81555
+ 'cloudSpeed': { value: 0.0001 },
81556
+ 'cloudCoverage': { value: 0.4 },
81557
+ 'cloudDensity': { value: 0.4 },
81558
+ 'cloudElevation': { value: 0.5 },
81559
+ 'showSunDisc': { value: 1 },
81560
+ 'time': { value: 0.0 }
80801
81561
  },
80802
81562
 
80803
81563
  vertexShader: /* glsl */`
@@ -80875,13 +81635,46 @@
80875
81635
  fragmentShader: /* glsl */`
80876
81636
  varying vec3 vWorldPosition;
80877
81637
  varying vec3 vSunDirection;
80878
- varying float vSunfade;
80879
81638
  varying vec3 vBetaR;
80880
81639
  varying vec3 vBetaM;
80881
81640
  varying float vSunE;
80882
81641
 
80883
81642
  uniform float mieDirectionalG;
80884
81643
  uniform vec3 up;
81644
+ uniform float cloudScale;
81645
+ uniform float cloudSpeed;
81646
+ uniform float cloudCoverage;
81647
+ uniform float cloudDensity;
81648
+ uniform float cloudElevation;
81649
+ uniform float showSunDisc;
81650
+ uniform float time;
81651
+
81652
+ // Cloud noise functions
81653
+ float hash( vec2 p ) {
81654
+ return fract( sin( dot( p, vec2( 127.1, 311.7 ) ) ) * 43758.5453123 );
81655
+ }
81656
+
81657
+ float noise( vec2 p ) {
81658
+ vec2 i = floor( p );
81659
+ vec2 f = fract( p );
81660
+ f = f * f * ( 3.0 - 2.0 * f );
81661
+ float a = hash( i );
81662
+ float b = hash( i + vec2( 1.0, 0.0 ) );
81663
+ float c = hash( i + vec2( 0.0, 1.0 ) );
81664
+ float d = hash( i + vec2( 1.0, 1.0 ) );
81665
+ return mix( mix( a, b, f.x ), mix( c, d, f.x ), f.y );
81666
+ }
81667
+
81668
+ float fbm( vec2 p ) {
81669
+ float value = 0.0;
81670
+ float amplitude = 0.5;
81671
+ for ( int i = 0; i < 5; i ++ ) {
81672
+ value += amplitude * noise( p );
81673
+ p *= 2.0;
81674
+ amplitude *= 0.5;
81675
+ }
81676
+ return value;
81677
+ }
80885
81678
 
80886
81679
  // constants for atmospheric scattering
80887
81680
  const float pi = 3.141592653589793238462643383279502884197169;
@@ -80943,14 +81736,48 @@
80943
81736
  vec3 L0 = vec3( 0.1 ) * Fex;
80944
81737
 
80945
81738
  // composition + solar disc
80946
- float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta );
80947
- L0 += ( vSunE * 19000.0 * Fex ) * sundisk;
81739
+ float sundisc = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta ) * showSunDisc;
81740
+ L0 += ( vSunE * 19000.0 * Fex ) * sundisc;
80948
81741
 
80949
81742
  vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 );
80950
81743
 
80951
- vec3 retColor = pow( texColor, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) );
81744
+ // Clouds
81745
+ if ( direction.y > 0.0 && cloudCoverage > 0.0 ) {
81746
+
81747
+ // Project to cloud plane (higher elevation = clouds appear lower/closer)
81748
+ float elevation = mix( 1.0, 0.1, cloudElevation );
81749
+ vec2 cloudUV = direction.xz / ( direction.y * elevation );
81750
+ cloudUV *= cloudScale;
81751
+ cloudUV += time * cloudSpeed;
81752
+
81753
+ // Multi-octave noise for fluffy clouds
81754
+ float cloudNoise = fbm( cloudUV * 1000.0 );
81755
+ cloudNoise += 0.5 * fbm( cloudUV * 2000.0 + 3.7 );
81756
+ cloudNoise = cloudNoise * 0.5 + 0.5;
81757
+
81758
+ // Apply coverage threshold
81759
+ float cloudMask = smoothstep( 1.0 - cloudCoverage, 1.0 - cloudCoverage + 0.3, cloudNoise );
81760
+
81761
+ // Fade clouds near horizon (adjusted by elevation)
81762
+ float horizonFade = smoothstep( 0.0, 0.1 + 0.2 * cloudElevation, direction.y );
81763
+ cloudMask *= horizonFade;
81764
+
81765
+ // Cloud lighting based on sun position
81766
+ float sunInfluence = dot( direction, vSunDirection ) * 0.5 + 0.5;
81767
+ float daylight = max( 0.0, vSunDirection.y * 2.0 );
80952
81768
 
80953
- gl_FragColor = vec4( retColor, 1.0 );
81769
+ // Base cloud color affected by atmosphere
81770
+ vec3 atmosphereColor = Lin * 0.04;
81771
+ vec3 cloudColor = mix( vec3( 0.3 ), vec3( 1.0 ), daylight );
81772
+ cloudColor = mix( cloudColor, atmosphereColor + vec3( 1.0 ), sunInfluence * 0.5 );
81773
+ cloudColor *= vSunE * 0.00002;
81774
+
81775
+ // Blend clouds with sky
81776
+ texColor = mix( texColor, cloudColor, cloudMask * cloudDensity );
81777
+
81778
+ }
81779
+
81780
+ gl_FragColor = vec4( texColor, 1.0 );
80954
81781
 
80955
81782
  #include <tonemapping_fragment>
80956
81783
  #include <colorspace_fragment>
@@ -81009,12 +81836,24 @@
81009
81836
  const mieDirectionalG = this.getFieldValue("MieDirectionalG");
81010
81837
  const inclination = this.getFieldValue("Inclination");
81011
81838
  const azimuth = this.getFieldValue("Azimuth");
81839
+ const showSunDisc = this.getFieldValue("ShowSunDisc");
81840
+ const cloudScale = this.getFieldValue("CloudScale");
81841
+ const cloudSpeed = this.getFieldValue("CloudSpeed");
81842
+ const cloudCoverage = this.getFieldValue("CloudCoverage");
81843
+ const cloudDensity = this.getFieldValue("CloudDensity");
81844
+ const cloudElevation = this.getFieldValue("CloudElevation");
81012
81845
  await this.recreateObject(async () => {
81013
81846
  this.mesh = new Sky();
81014
81847
  this.mesh.scale.setScalar(size);
81015
81848
  this.inclination = inclination;
81016
81849
  this.azimuth = azimuth;
81017
81850
  this.size = size;
81851
+ this.setUniformValue("cloudScale", cloudScale / 100);
81852
+ this.setUniformValue("cloudSpeed", cloudSpeed / 100);
81853
+ this.setUniformValue("cloudCoverage", cloudCoverage);
81854
+ this.setUniformValue("cloudDensity", cloudDensity);
81855
+ this.setUniformValue("cloudElevation", cloudElevation);
81856
+ this.setUniformValue("showSunDisc", showSunDisc);
81018
81857
  this.setUniformValue("turbidity", turbidity);
81019
81858
  this.setUniformValue("rayleigh", rayleigh);
81020
81859
  this.setUniformValue("mieCoefficient", mieCoefficient);
@@ -81044,6 +81883,9 @@
81044
81883
  console.log("Could not find object to set uniform on");
81045
81884
  }
81046
81885
  }
81886
+ update(deltaTimeMs) {
81887
+ this.setUniformValue("time", performance.now() * 0.001);
81888
+ }
81047
81889
  updateEvent(field, value, type, oldValue) {
81048
81890
  if (super.updateEvent(field, value, type, oldValue))
81049
81891
  return true;
@@ -81075,6 +81917,24 @@
81075
81917
  this.azimuth = value;
81076
81918
  this.setSunPosition(this.inclination, this.azimuth);
81077
81919
  break;
81920
+ case "ShowSunDisc":
81921
+ this.setUniformValue("showSunDisc", value);
81922
+ break;
81923
+ case "CloudScale":
81924
+ this.setUniformValue("cloudScale", value / 100);
81925
+ break;
81926
+ case "CloudSpeed":
81927
+ this.setUniformValue("cloudSpeed", value / 100);
81928
+ break;
81929
+ case "CloudCoverage":
81930
+ this.setUniformValue("cloudCoverage", value);
81931
+ break;
81932
+ case "CloudDensity":
81933
+ this.setUniformValue("cloudDensity", value);
81934
+ break;
81935
+ case "CloudElevation":
81936
+ this.setUniformValue("cloudElevation", value);
81937
+ break;
81078
81938
  default:
81079
81939
  return false;
81080
81940
  }
@@ -84035,7 +84895,7 @@
84035
84895
  particleCount;
84036
84896
  width;
84037
84897
  height;
84038
- time = new THREE.Clock();
84898
+ timer = new THREE.Timer();
84039
84899
  elapsedTime = 0;
84040
84900
  timeSinceLastSpawn = 0;
84041
84901
  isRunning = true;
@@ -84627,7 +85487,7 @@
84627
85487
  this.elapsedTime = 0;
84628
85488
  this.timeSinceLastSpawn = this.spawnRate; // force spawn at the next update
84629
85489
  this.emissionCounter = 0;
84630
- this.time.start(); // reset the simulation clock
85490
+ this.timer.reset(); // reset the simulation clock
84631
85491
  }
84632
85492
  killAllParticles() {
84633
85493
  this.simulation.killAllParticles();
@@ -84653,7 +85513,7 @@
84653
85513
  render(delta) {
84654
85514
  if (!this.isRunning || this.isPaused)
84655
85515
  return;
84656
- delta ??= this.time.getDelta();
85516
+ delta ??= this.timer.getDelta();
84657
85517
  if (this.duration !== -1 && this.elapsedTime > this.duration) {
84658
85518
  this.killAllParticles();
84659
85519
  this.hideParticles();
@@ -85966,7 +86826,7 @@
85966
86826
  if (!this.stereoPanner) {
85967
86827
  this.stereoPanner = this.audio.context.createStereoPanner();
85968
86828
  }
85969
- this.audio.filters = [this.stereoPanner];
86829
+ this.audio.setFilters([this.stereoPanner]);
85970
86830
  }
85971
86831
  }
85972
86832
  enumerateItemChildren() {
@@ -86148,7 +87008,7 @@
86148
87008
  _setAudioProps() {
86149
87009
  if (!this.audio)
86150
87010
  return;
86151
- this.audio.loop = this.getFieldValue("Loop");
87011
+ this.audio.setLoop(this.getFieldValue("Loop"));
86152
87012
  const loopSection = this.getFieldValue("LoopSection");
86153
87013
  if (loopSection) {
86154
87014
  this.audio.setLoopStart(this.getFieldValue("LoopStart"));
@@ -111080,6 +111940,7 @@
111080
111940
  * - KHR_texture_transform
111081
111941
  * - EXT_materials_bump
111082
111942
  * - EXT_mesh_gpu_instancing
111943
+ * - EXT_texture_webp
111083
111944
  *
111084
111945
  * The following glTF 2.0 extension is supported by an external user plugin:
111085
111946
  *
@@ -112522,15 +113383,30 @@
112522
113383
 
112523
113384
  }
112524
113385
 
112525
- let mimeType = map.userData.mimeType;
113386
+ const mimeType = map.userData.mimeType;
112526
113387
 
112527
- if ( mimeType === 'image/webp' ) mimeType = 'image/png';
113388
+ const imageIndex = this.processImage( map.image, map.format, map.flipY, mimeType );
112528
113389
 
112529
113390
  const textureDef = {
112530
- sampler: this.processSampler( map ),
112531
- source: this.processImage( map.image, map.format, map.flipY, mimeType )
113391
+ sampler: this.processSampler( map )
112532
113392
  };
112533
113393
 
113394
+ if ( mimeType === 'image/webp' ) {
113395
+
113396
+ textureDef.extensions = textureDef.extensions || {};
113397
+ textureDef.extensions[ 'EXT_texture_webp' ] = {
113398
+ source: imageIndex
113399
+ };
113400
+
113401
+ this.extensionsUsed[ 'EXT_texture_webp' ] = true;
113402
+ this.extensionsRequired[ 'EXT_texture_webp' ] = true;
113403
+
113404
+ } else {
113405
+
113406
+ textureDef.source = imageIndex;
113407
+
113408
+ }
113409
+
112534
113410
  if ( map.name ) textureDef.name = map.name;
112535
113411
 
112536
113412
  await this._invokeAllAsync( async function ( ext ) {
@@ -112821,7 +113697,7 @@
112821
113697
  const validVertexAttributes =
112822
113698
  /^(POSITION|NORMAL|TANGENT|TEXCOORD_\d+|COLOR_\d+|JOINTS_\d+|WEIGHTS_\d+)$/;
112823
113699
 
112824
- if ( ! validVertexAttributes.test( attributeName ) ) attributeName = '_' + attributeName;
113700
+ if ( ! validVertexAttributes.test( attributeName ) && ! attributeName.startsWith( '_' ) ) attributeName = '_' + attributeName;
112825
113701
 
112826
113702
  if ( cache.attributes.has( this.getUID( attribute ) ) ) {
112827
113703
 
@@ -112841,12 +113717,12 @@
112841
113717
  ! ( array instanceof Uint8Array ) ) {
112842
113718
 
112843
113719
  console.warn( 'GLTFExporter: Attribute "skinIndex" converted to type UNSIGNED_SHORT.' );
112844
- modifiedAttribute = new THREE.BufferAttribute( new Uint16Array( array ), attribute.itemSize, attribute.normalized );
113720
+ modifiedAttribute = GLTFExporter.Utils.toTypedBufferAttribute( attribute, Uint16Array );
112845
113721
 
112846
113722
  } else if ( ( array instanceof Uint32Array || array instanceof Int32Array ) && ! attributeName.startsWith( '_' ) ) {
112847
113723
 
112848
113724
  console.warn( `GLTFExporter: Attribute "${ attributeName }" converted to type FLOAT.` );
112849
- modifiedAttribute = GLTFExporter.Utils.toFloat32BufferAttribute( attribute );
113725
+ modifiedAttribute = GLTFExporter.Utils.toTypedBufferAttribute( attribute, Float32Array );
112850
113726
 
112851
113727
  }
112852
113728
 
@@ -113344,6 +114220,13 @@
113344
114220
 
113345
114221
  if ( ! json.nodes ) json.nodes = [];
113346
114222
 
114223
+ // Handle pivot by creating a container node
114224
+ if ( object.pivot !== null ) {
114225
+
114226
+ return await this._processNodeWithPivotAsync( object );
114227
+
114228
+ }
114229
+
113347
114230
  const nodeDef = {};
113348
114231
 
113349
114232
  if ( options.trs ) {
@@ -113440,6 +114323,126 @@
113440
114323
 
113441
114324
  }
113442
114325
 
114326
+ /**
114327
+ * Process Object3D node with pivot using container approach
114328
+ * @param {THREE.Object3D} object Object3D with pivot
114329
+ * @return {Promise<number>} Index of the container node
114330
+ */
114331
+ async _processNodeWithPivotAsync( object ) {
114332
+
114333
+ const json = this.json;
114334
+ const options = this.options;
114335
+ const nodeMap = this.nodeMap;
114336
+
114337
+ const pivot = object.pivot;
114338
+
114339
+ // Container node: holds position + pivot offset, rotation, scale
114340
+ // Animations will target this node
114341
+ const containerDef = {};
114342
+
114343
+ const rotation = object.quaternion.toArray();
114344
+ const position = [
114345
+ object.position.x + pivot.x,
114346
+ object.position.y + pivot.y,
114347
+ object.position.z + pivot.z
114348
+ ];
114349
+ const scale = object.scale.toArray();
114350
+
114351
+ if ( ! equalArray( rotation, [ 0, 0, 0, 1 ] ) ) {
114352
+
114353
+ containerDef.rotation = rotation;
114354
+
114355
+ }
114356
+
114357
+ if ( ! equalArray( position, [ 0, 0, 0 ] ) ) {
114358
+
114359
+ containerDef.translation = position;
114360
+
114361
+ }
114362
+
114363
+ if ( ! equalArray( scale, [ 1, 1, 1 ] ) ) {
114364
+
114365
+ containerDef.scale = scale;
114366
+
114367
+ }
114368
+
114369
+ // Store pivot in extras for round-trip reconstruction
114370
+ containerDef.extras = { pivot: pivot.toArray() };
114371
+
114372
+ if ( object.name !== '' ) containerDef.name = String( object.name );
114373
+
114374
+ this.serializeUserData( object, containerDef );
114375
+
114376
+ const containerIndex = json.nodes.push( containerDef ) - 1;
114377
+
114378
+ // Map original object to container so animations target it
114379
+ nodeMap.set( object, containerIndex );
114380
+
114381
+ // Child node: holds mesh with -pivot offset
114382
+ const childDef = {};
114383
+
114384
+ const childPosition = [ - pivot.x, - pivot.y, - pivot.z ];
114385
+
114386
+ if ( ! equalArray( childPosition, [ 0, 0, 0 ] ) ) {
114387
+
114388
+ childDef.translation = childPosition;
114389
+
114390
+ }
114391
+
114392
+ if ( object.isMesh || object.isLine || object.isPoints ) {
114393
+
114394
+ const meshIndex = await this.processMeshAsync( object );
114395
+
114396
+ if ( meshIndex !== null ) childDef.mesh = meshIndex;
114397
+
114398
+ } else if ( object.isCamera ) {
114399
+
114400
+ childDef.camera = this.processCamera( object );
114401
+
114402
+ }
114403
+
114404
+ if ( object.isSkinnedMesh ) this.skins.push( object );
114405
+
114406
+ const childIndex = json.nodes.push( childDef ) - 1;
114407
+
114408
+ // Build children array for container
114409
+ const containerChildren = [ childIndex ];
114410
+
114411
+ // Process object's children as children of the child node
114412
+ if ( object.children.length > 0 ) {
114413
+
114414
+ const grandchildren = [];
114415
+
114416
+ for ( let i = 0, l = object.children.length; i < l; i ++ ) {
114417
+
114418
+ const child = object.children[ i ];
114419
+
114420
+ if ( child.visible || options.onlyVisible === false ) {
114421
+
114422
+ const childNodeIndex = await this.processNodeAsync( child );
114423
+
114424
+ if ( childNodeIndex !== null ) grandchildren.push( childNodeIndex );
114425
+
114426
+ }
114427
+
114428
+ }
114429
+
114430
+ if ( grandchildren.length > 0 ) childDef.children = grandchildren;
114431
+
114432
+ }
114433
+
114434
+ containerDef.children = containerChildren;
114435
+
114436
+ await this._invokeAllAsync( function ( ext ) {
114437
+
114438
+ ext.writeNode && ext.writeNode( object, containerDef );
114439
+
114440
+ } );
114441
+
114442
+ return containerIndex;
114443
+
114444
+ }
114445
+
113443
114446
  /**
113444
114447
  * Process Scene
113445
114448
  * @param {Scene} scene Scene to process
@@ -114527,9 +115530,9 @@
114527
115530
 
114528
115531
  },
114529
115532
 
114530
- toFloat32BufferAttribute: function ( srcAttribute ) {
115533
+ toTypedBufferAttribute: function ( srcAttribute, TypedArray ) {
114531
115534
 
114532
- const dstAttribute = new THREE.BufferAttribute( new Float32Array( srcAttribute.count * srcAttribute.itemSize ), srcAttribute.itemSize, false );
115535
+ const dstAttribute = new THREE.BufferAttribute( new TypedArray( srcAttribute.count * srcAttribute.itemSize ), srcAttribute.itemSize, false );
114533
115536
 
114534
115537
  if ( ! srcAttribute.normalized && ! srcAttribute.isInterleavedBufferAttribute ) {
114535
115538