@2112-lab/central-plant 0.3.16 → 0.3.17

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.
@@ -18160,19 +18160,700 @@ function addPrimitiveAttributes( geometry, primitiveDef, parser ) {
18160
18160
 
18161
18161
  }
18162
18162
 
18163
+ const _taskCache = new WeakMap();
18164
+
18165
+ /**
18166
+ * A loader for the Draco format.
18167
+ *
18168
+ * [Draco]{@link https://google.github.io/draco/} is an open source library for compressing
18169
+ * and decompressing 3D meshes and point clouds. Compressed geometry can be significantly smaller,
18170
+ * at the cost of additional decoding time on the client device.
18171
+ *
18172
+ * Standalone Draco files have a `.drc` extension, and contain vertex positions, normals, colors,
18173
+ * and other attributes. Draco files do not contain materials, textures, animation, or node hierarchies –
18174
+ * to use these features, embed Draco geometry inside of a glTF file. A normal glTF file can be converted
18175
+ * to a Draco-compressed glTF file using [glTF-Pipeline]{@link https://github.com/CesiumGS/gltf-pipeline}.
18176
+ * When using Draco with glTF, an instance of `DRACOLoader` will be used internally by {@link GLTFLoader}.
18177
+ *
18178
+ * It is recommended to create one DRACOLoader instance and reuse it to avoid loading and creating
18179
+ * multiple decoder instances.
18180
+ *
18181
+ * `DRACOLoader` will automatically use either the JS or the WASM decoding library, based on
18182
+ * browser capabilities.
18183
+ *
18184
+ * ```js
18185
+ * const loader = new DRACOLoader();
18186
+ * loader.setDecoderPath( '/examples/jsm/libs/draco/' );
18187
+ *
18188
+ * const geometry = await dracoLoader.loadAsync( 'models/draco/bunny.drc' );
18189
+ * geometry.computeVertexNormals(); // optional
18190
+ *
18191
+ * dracoLoader.dispose();
18192
+ * ```
18193
+ *
18194
+ * @augments Loader
18195
+ * @three_import import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
18196
+ */
18197
+ class DRACOLoader extends THREE.Loader {
18198
+
18199
+ /**
18200
+ * Constructs a new Draco loader.
18201
+ *
18202
+ * @param {LoadingManager} [manager] - The loading manager.
18203
+ */
18204
+ constructor( manager ) {
18205
+
18206
+ super( manager );
18207
+
18208
+ this.decoderPath = '';
18209
+ this.decoderConfig = {};
18210
+ this.decoderBinary = null;
18211
+ this.decoderPending = null;
18212
+
18213
+ this.workerLimit = 4;
18214
+ this.workerPool = [];
18215
+ this.workerNextTaskID = 1;
18216
+ this.workerSourceURL = '';
18217
+
18218
+ this.defaultAttributeIDs = {
18219
+ position: 'POSITION',
18220
+ normal: 'NORMAL',
18221
+ color: 'COLOR',
18222
+ uv: 'TEX_COORD'
18223
+ };
18224
+ this.defaultAttributeTypes = {
18225
+ position: 'Float32Array',
18226
+ normal: 'Float32Array',
18227
+ color: 'Float32Array',
18228
+ uv: 'Float32Array'
18229
+ };
18230
+
18231
+ }
18232
+
18233
+ /**
18234
+ * Provides configuration for the decoder libraries. Configuration cannot be changed after decoding begins.
18235
+ *
18236
+ * @param {string} path - The decoder path.
18237
+ * @return {DRACOLoader} A reference to this loader.
18238
+ */
18239
+ setDecoderPath( path ) {
18240
+
18241
+ this.decoderPath = path;
18242
+
18243
+ return this;
18244
+
18245
+ }
18246
+
18247
+ /**
18248
+ * Provides configuration for the decoder libraries. Configuration cannot be changed after decoding begins.
18249
+ *
18250
+ * @param {{type:('js'|'wasm')}} config - The decoder config.
18251
+ * @return {DRACOLoader} A reference to this loader.
18252
+ */
18253
+ setDecoderConfig( config ) {
18254
+
18255
+ this.decoderConfig = config;
18256
+
18257
+ return this;
18258
+
18259
+ }
18260
+
18261
+ /**
18262
+ * Sets the maximum number of Web Workers to be used during decoding.
18263
+ * A lower limit may be preferable if workers are also for other tasks in the application.
18264
+ *
18265
+ * @param {number} workerLimit - The worker limit.
18266
+ * @return {DRACOLoader} A reference to this loader.
18267
+ */
18268
+ setWorkerLimit( workerLimit ) {
18269
+
18270
+ this.workerLimit = workerLimit;
18271
+
18272
+ return this;
18273
+
18274
+ }
18275
+
18276
+ /**
18277
+ * Starts loading from the given URL and passes the loaded Draco asset
18278
+ * to the `onLoad()` callback.
18279
+ *
18280
+ * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
18281
+ * @param {function(BufferGeometry)} onLoad - Executed when the loading process has been finished.
18282
+ * @param {onProgressCallback} onProgress - Executed while the loading is in progress.
18283
+ * @param {onErrorCallback} onError - Executed when errors occur.
18284
+ */
18285
+ load( url, onLoad, onProgress, onError ) {
18286
+
18287
+ const loader = new THREE.FileLoader( this.manager );
18288
+
18289
+ loader.setPath( this.path );
18290
+ loader.setResponseType( 'arraybuffer' );
18291
+ loader.setRequestHeader( this.requestHeader );
18292
+ loader.setWithCredentials( this.withCredentials );
18293
+
18294
+ loader.load( url, ( buffer ) => {
18295
+
18296
+ this.parse( buffer, onLoad, onError );
18297
+
18298
+ }, onProgress, onError );
18299
+
18300
+ }
18301
+
18302
+ /**
18303
+ * Parses the given Draco data.
18304
+ *
18305
+ * @param {ArrayBuffer} buffer - The raw Draco data as an array buffer.
18306
+ * @param {function(BufferGeometry)} onLoad - Executed when the loading/parsing process has been finished.
18307
+ * @param {onErrorCallback} onError - Executed when errors occur.
18308
+ */
18309
+ parse( buffer, onLoad, onError = ()=>{} ) {
18310
+
18311
+ this.decodeDracoFile( buffer, onLoad, null, null, THREE.SRGBColorSpace, onError ).catch( onError );
18312
+
18313
+ }
18314
+
18315
+ //
18316
+
18317
+ decodeDracoFile( buffer, callback, attributeIDs, attributeTypes, vertexColorSpace = THREE.LinearSRGBColorSpace, onError = () => {} ) {
18318
+
18319
+ const taskConfig = {
18320
+ attributeIDs: attributeIDs || this.defaultAttributeIDs,
18321
+ attributeTypes: attributeTypes || this.defaultAttributeTypes,
18322
+ useUniqueIDs: !! attributeIDs,
18323
+ vertexColorSpace: vertexColorSpace,
18324
+ };
18325
+
18326
+ return this.decodeGeometry( buffer, taskConfig ).then( callback ).catch( onError );
18327
+
18328
+ }
18329
+
18330
+ decodeGeometry( buffer, taskConfig ) {
18331
+
18332
+ const taskKey = JSON.stringify( taskConfig );
18333
+
18334
+ // Check for an existing task using this buffer. A transferred buffer cannot be transferred
18335
+ // again from this thread.
18336
+ if ( _taskCache.has( buffer ) ) {
18337
+
18338
+ const cachedTask = _taskCache.get( buffer );
18339
+
18340
+ if ( cachedTask.key === taskKey ) {
18341
+
18342
+ return cachedTask.promise;
18343
+
18344
+ } else if ( buffer.byteLength === 0 ) {
18345
+
18346
+ // Technically, it would be possible to wait for the previous task to complete,
18347
+ // transfer the buffer back, and decode again with the second configuration. That
18348
+ // is complex, and I don't know of any reason to decode a Draco buffer twice in
18349
+ // different ways, so this is left unimplemented.
18350
+ throw new Error(
18351
+
18352
+ 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' +
18353
+ 'settings. Buffer has already been transferred.'
18354
+
18355
+ );
18356
+
18357
+ }
18358
+
18359
+ }
18360
+
18361
+ //
18362
+
18363
+ let worker;
18364
+ const taskID = this.workerNextTaskID ++;
18365
+ const taskCost = buffer.byteLength;
18366
+
18367
+ // Obtain a worker and assign a task, and construct a geometry instance
18368
+ // when the task completes.
18369
+ const geometryPending = this._getWorker( taskID, taskCost )
18370
+ .then( ( _worker ) => {
18371
+
18372
+ worker = _worker;
18373
+
18374
+ return new Promise( ( resolve, reject ) => {
18375
+
18376
+ worker._callbacks[ taskID ] = { resolve, reject };
18377
+
18378
+ worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] );
18379
+
18380
+ // this.debug();
18381
+
18382
+ } );
18383
+
18384
+ } )
18385
+ .then( ( message ) => this._createGeometry( message.geometry ) );
18386
+
18387
+ // Remove task from the task list.
18388
+ // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
18389
+ geometryPending
18390
+ .catch( () => true )
18391
+ .then( () => {
18392
+
18393
+ if ( worker && taskID ) {
18394
+
18395
+ this._releaseTask( worker, taskID );
18396
+
18397
+ // this.debug();
18398
+
18399
+ }
18400
+
18401
+ } );
18402
+
18403
+ // Cache the task result.
18404
+ _taskCache.set( buffer, {
18405
+
18406
+ key: taskKey,
18407
+ promise: geometryPending
18408
+
18409
+ } );
18410
+
18411
+ return geometryPending;
18412
+
18413
+ }
18414
+
18415
+ _createGeometry( geometryData ) {
18416
+
18417
+ const geometry = new THREE.BufferGeometry();
18418
+
18419
+ if ( geometryData.index ) {
18420
+
18421
+ geometry.setIndex( new THREE.BufferAttribute( geometryData.index.array, 1 ) );
18422
+
18423
+ }
18424
+
18425
+ for ( let i = 0; i < geometryData.attributes.length; i ++ ) {
18426
+
18427
+ const result = geometryData.attributes[ i ];
18428
+ const name = result.name;
18429
+ const array = result.array;
18430
+ const itemSize = result.itemSize;
18431
+
18432
+ const attribute = new THREE.BufferAttribute( array, itemSize );
18433
+
18434
+ if ( name === 'color' ) {
18435
+
18436
+ this._assignVertexColorSpace( attribute, result.vertexColorSpace );
18437
+
18438
+ attribute.normalized = ( array instanceof Float32Array ) === false;
18439
+
18440
+ }
18441
+
18442
+ geometry.setAttribute( name, attribute );
18443
+
18444
+ }
18445
+
18446
+ return geometry;
18447
+
18448
+ }
18449
+
18450
+ _assignVertexColorSpace( attribute, inputColorSpace ) {
18451
+
18452
+ // While .drc files do not specify colorspace, the only 'official' tooling
18453
+ // is PLY and OBJ converters, which use sRGB. We'll assume sRGB when a .drc
18454
+ // file is passed into .load() or .parse(). GLTFLoader uses internal APIs
18455
+ // to decode geometry, and vertex colors are already Linear-sRGB in there.
18456
+
18457
+ if ( inputColorSpace !== THREE.SRGBColorSpace ) return;
18458
+
18459
+ const _color = new THREE.Color();
18460
+
18461
+ for ( let i = 0, il = attribute.count; i < il; i ++ ) {
18462
+
18463
+ _color.fromBufferAttribute( attribute, i );
18464
+ THREE.ColorManagement.colorSpaceToWorking( _color, THREE.SRGBColorSpace );
18465
+ attribute.setXYZ( i, _color.r, _color.g, _color.b );
18466
+
18467
+ }
18468
+
18469
+ }
18470
+
18471
+ _loadLibrary( url, responseType ) {
18472
+
18473
+ const loader = new THREE.FileLoader( this.manager );
18474
+ loader.setPath( this.decoderPath );
18475
+ loader.setResponseType( responseType );
18476
+ loader.setWithCredentials( this.withCredentials );
18477
+
18478
+ return new Promise( ( resolve, reject ) => {
18479
+
18480
+ loader.load( url, resolve, undefined, reject );
18481
+
18482
+ } );
18483
+
18484
+ }
18485
+
18486
+ preload() {
18487
+
18488
+ this._initDecoder();
18489
+
18490
+ return this;
18491
+
18492
+ }
18493
+
18494
+ _initDecoder() {
18495
+
18496
+ if ( this.decoderPending ) return this.decoderPending;
18497
+
18498
+ const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js';
18499
+ const librariesPending = [];
18500
+
18501
+ if ( useJS ) {
18502
+
18503
+ librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) );
18504
+
18505
+ } else {
18506
+
18507
+ librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) );
18508
+ librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) );
18509
+
18510
+ }
18511
+
18512
+ this.decoderPending = Promise.all( librariesPending )
18513
+ .then( ( libraries ) => {
18514
+
18515
+ const jsContent = libraries[ 0 ];
18516
+
18517
+ if ( ! useJS ) {
18518
+
18519
+ this.decoderConfig.wasmBinary = libraries[ 1 ];
18520
+
18521
+ }
18522
+
18523
+ const fn = DRACOWorker.toString();
18524
+
18525
+ const body = [
18526
+ '/* draco decoder */',
18527
+ jsContent,
18528
+ '',
18529
+ '/* worker */',
18530
+ fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
18531
+ ].join( '\n' );
18532
+
18533
+ this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
18534
+
18535
+ } );
18536
+
18537
+ return this.decoderPending;
18538
+
18539
+ }
18540
+
18541
+ _getWorker( taskID, taskCost ) {
18542
+
18543
+ return this._initDecoder().then( () => {
18544
+
18545
+ if ( this.workerPool.length < this.workerLimit ) {
18546
+
18547
+ const worker = new Worker( this.workerSourceURL );
18548
+
18549
+ worker._callbacks = {};
18550
+ worker._taskCosts = {};
18551
+ worker._taskLoad = 0;
18552
+
18553
+ worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } );
18554
+
18555
+ worker.onmessage = function ( e ) {
18556
+
18557
+ const message = e.data;
18558
+
18559
+ switch ( message.type ) {
18560
+
18561
+ case 'decode':
18562
+ worker._callbacks[ message.id ].resolve( message );
18563
+ break;
18564
+
18565
+ case 'error':
18566
+ worker._callbacks[ message.id ].reject( message );
18567
+ break;
18568
+
18569
+ default:
18570
+ console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' );
18571
+
18572
+ }
18573
+
18574
+ };
18575
+
18576
+ this.workerPool.push( worker );
18577
+
18578
+ } else {
18579
+
18580
+ this.workerPool.sort( function ( a, b ) {
18581
+
18582
+ return a._taskLoad > b._taskLoad ? - 1 : 1;
18583
+
18584
+ } );
18585
+
18586
+ }
18587
+
18588
+ const worker = this.workerPool[ this.workerPool.length - 1 ];
18589
+ worker._taskCosts[ taskID ] = taskCost;
18590
+ worker._taskLoad += taskCost;
18591
+ return worker;
18592
+
18593
+ } );
18594
+
18595
+ }
18596
+
18597
+ _releaseTask( worker, taskID ) {
18598
+
18599
+ worker._taskLoad -= worker._taskCosts[ taskID ];
18600
+ delete worker._callbacks[ taskID ];
18601
+ delete worker._taskCosts[ taskID ];
18602
+
18603
+ }
18604
+
18605
+ debug() {
18606
+
18607
+ console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) );
18608
+
18609
+ }
18610
+
18611
+ dispose() {
18612
+
18613
+ for ( let i = 0; i < this.workerPool.length; ++ i ) {
18614
+
18615
+ this.workerPool[ i ].terminate();
18616
+
18617
+ }
18618
+
18619
+ this.workerPool.length = 0;
18620
+
18621
+ if ( this.workerSourceURL !== '' ) {
18622
+
18623
+ URL.revokeObjectURL( this.workerSourceURL );
18624
+
18625
+ }
18626
+
18627
+ return this;
18628
+
18629
+ }
18630
+
18631
+ }
18632
+
18633
+ /* WEB WORKER */
18634
+
18635
+ function DRACOWorker() {
18636
+
18637
+ let decoderConfig;
18638
+ let decoderPending;
18639
+
18640
+ onmessage = function ( e ) {
18641
+
18642
+ const message = e.data;
18643
+
18644
+ switch ( message.type ) {
18645
+
18646
+ case 'init':
18647
+ decoderConfig = message.decoderConfig;
18648
+ decoderPending = new Promise( function ( resolve/*, reject*/ ) {
18649
+
18650
+ decoderConfig.onModuleLoaded = function ( draco ) {
18651
+
18652
+ // Module is Promise-like. Wrap before resolving to avoid loop.
18653
+ resolve( { draco: draco } );
18654
+
18655
+ };
18656
+
18657
+ DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef
18658
+
18659
+ } );
18660
+ break;
18661
+
18662
+ case 'decode':
18663
+ const buffer = message.buffer;
18664
+ const taskConfig = message.taskConfig;
18665
+ decoderPending.then( ( module ) => {
18666
+
18667
+ const draco = module.draco;
18668
+ const decoder = new draco.Decoder();
18669
+
18670
+ try {
18671
+
18672
+ const geometry = decodeGeometry( draco, decoder, new Int8Array( buffer ), taskConfig );
18673
+
18674
+ const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer );
18675
+
18676
+ if ( geometry.index ) buffers.push( geometry.index.array.buffer );
18677
+
18678
+ self.postMessage( { type: 'decode', id: message.id, geometry }, buffers );
18679
+
18680
+ } catch ( error ) {
18681
+
18682
+ console.error( error );
18683
+
18684
+ self.postMessage( { type: 'error', id: message.id, error: error.message } );
18685
+
18686
+ } finally {
18687
+
18688
+ draco.destroy( decoder );
18689
+
18690
+ }
18691
+
18692
+ } );
18693
+ break;
18694
+
18695
+ }
18696
+
18697
+ };
18698
+
18699
+ function decodeGeometry( draco, decoder, array, taskConfig ) {
18700
+
18701
+ const attributeIDs = taskConfig.attributeIDs;
18702
+ const attributeTypes = taskConfig.attributeTypes;
18703
+
18704
+ let dracoGeometry;
18705
+ let decodingStatus;
18706
+
18707
+ const geometryType = decoder.GetEncodedGeometryType( array );
18708
+
18709
+ if ( geometryType === draco.TRIANGULAR_MESH ) {
18710
+
18711
+ dracoGeometry = new draco.Mesh();
18712
+ decodingStatus = decoder.DecodeArrayToMesh( array, array.byteLength, dracoGeometry );
18713
+
18714
+ } else if ( geometryType === draco.POINT_CLOUD ) {
18715
+
18716
+ dracoGeometry = new draco.PointCloud();
18717
+ decodingStatus = decoder.DecodeArrayToPointCloud( array, array.byteLength, dracoGeometry );
18718
+
18719
+ } else {
18720
+
18721
+ throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' );
18722
+
18723
+ }
18724
+
18725
+ if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) {
18726
+
18727
+ throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
18728
+
18729
+ }
18730
+
18731
+ const geometry = { index: null, attributes: [] };
18732
+
18733
+ // Gather all vertex attributes.
18734
+ for ( const attributeName in attributeIDs ) {
18735
+
18736
+ const attributeType = self[ attributeTypes[ attributeName ] ];
18737
+
18738
+ let attribute;
18739
+ let attributeID;
18740
+
18741
+ // A Draco file may be created with default vertex attributes, whose attribute IDs
18742
+ // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively,
18743
+ // a Draco file may contain a custom set of attributes, identified by known unique
18744
+ // IDs. glTF files always do the latter, and `.drc` files typically do the former.
18745
+ if ( taskConfig.useUniqueIDs ) {
18746
+
18747
+ attributeID = attributeIDs[ attributeName ];
18748
+ attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID );
18749
+
18750
+ } else {
18751
+
18752
+ attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] );
18753
+
18754
+ if ( attributeID === - 1 ) continue;
18755
+
18756
+ attribute = decoder.GetAttribute( dracoGeometry, attributeID );
18757
+
18758
+ }
18759
+
18760
+ const attributeResult = decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute );
18761
+
18762
+ if ( attributeName === 'color' ) {
18763
+
18764
+ attributeResult.vertexColorSpace = taskConfig.vertexColorSpace;
18765
+
18766
+ }
18767
+
18768
+ geometry.attributes.push( attributeResult );
18769
+
18770
+ }
18771
+
18772
+ // Add index.
18773
+ if ( geometryType === draco.TRIANGULAR_MESH ) {
18774
+
18775
+ geometry.index = decodeIndex( draco, decoder, dracoGeometry );
18776
+
18777
+ }
18778
+
18779
+ draco.destroy( dracoGeometry );
18780
+
18781
+ return geometry;
18782
+
18783
+ }
18784
+
18785
+ function decodeIndex( draco, decoder, dracoGeometry ) {
18786
+
18787
+ const numFaces = dracoGeometry.num_faces();
18788
+ const numIndices = numFaces * 3;
18789
+ const byteLength = numIndices * 4;
18790
+
18791
+ const ptr = draco._malloc( byteLength );
18792
+ decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr );
18793
+ const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice();
18794
+ draco._free( ptr );
18795
+
18796
+ return { array: index, itemSize: 1 };
18797
+
18798
+ }
18799
+
18800
+ function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
18801
+
18802
+ const numComponents = attribute.num_components();
18803
+ const numPoints = dracoGeometry.num_points();
18804
+ const numValues = numPoints * numComponents;
18805
+ const byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
18806
+ const dataType = getDracoDataType( draco, attributeType );
18807
+
18808
+ const ptr = draco._malloc( byteLength );
18809
+ decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr );
18810
+ const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice();
18811
+ draco._free( ptr );
18812
+
18813
+ return {
18814
+ name: attributeName,
18815
+ array: array,
18816
+ itemSize: numComponents
18817
+ };
18818
+
18819
+ }
18820
+
18821
+ function getDracoDataType( draco, attributeType ) {
18822
+
18823
+ switch ( attributeType ) {
18824
+
18825
+ case Float32Array: return draco.DT_FLOAT32;
18826
+ case Int8Array: return draco.DT_INT8;
18827
+ case Int16Array: return draco.DT_INT16;
18828
+ case Int32Array: return draco.DT_INT32;
18829
+ case Uint8Array: return draco.DT_UINT8;
18830
+ case Uint16Array: return draco.DT_UINT16;
18831
+ case Uint32Array: return draco.DT_UINT32;
18832
+
18833
+ }
18834
+
18835
+ }
18836
+
18837
+ }
18838
+
18163
18839
  var ModelPreloader = /*#__PURE__*/function () {
18164
18840
  function ModelPreloader() {
18165
18841
  _classCallCheck(this, ModelPreloader);
18166
18842
  this.modelCache = new Map(); // Cache for loaded models
18167
18843
  this.loadingPromises = new Map(); // Track ongoing loads to prevent duplicates
18168
18844
  this.gltfLoader = new GLTFLoader();
18845
+
18846
+ // Setup DRACO decoder for compressed GLB files
18847
+ this.dracoLoader = new DRACOLoader();
18848
+ this.dracoLoader.setDecoderPath('/draco/');
18849
+ this.gltfLoader.setDRACOLoader(this.dracoLoader);
18169
18850
  this.isPreloading = false;
18170
18851
  this.preloadingPromise = null;
18171
18852
  this.componentDictionary = null;
18172
18853
  this.modelsBasePath = '/library/models/'; // Default local path
18173
18854
  this.urlResolver = null; // Optional function to resolve model URLs (for S3 authentication)
18174
18855
 
18175
- console.log('🚀 ModelPreloader initialized with GLB support');
18856
+ console.log('🚀 ModelPreloader initialized with GLB + DRACO support');
18176
18857
  }
18177
18858
 
18178
18859
  /**
@@ -28065,6 +28746,22 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
28065
28746
  this.pathfinder = new pathfinder.Pathfinder(this.pathfinderConfig);
28066
28747
  this.sceneViewer.pathfinder = this.pathfinder;
28067
28748
 
28749
+ // ── Early exit if no connections ───────────────────────────────────
28750
+ // Skip expensive bounding box computation when there's nothing to connect
28751
+ if (!(!connections || connections.length === 0)) {
28752
+ _context.n = 1;
28753
+ break;
28754
+ }
28755
+ console.log("\u23ED\uFE0F Skipping pathfinding - no connections [".concat(context, "]"));
28756
+ return _context.a(2, {
28757
+ paths: [],
28758
+ gateways: [],
28759
+ rewiredConnections: [],
28760
+ metrics: {
28761
+ total: 0
28762
+ }
28763
+ });
28764
+ case 1:
28068
28765
  // ── Stage 1: Input Generation ──────────────────────────────────────
28069
28766
  inputGenStart = performance.now(); // Ensure all matrices are up to date before bounding box calculations (for all pathfinding executions)
28070
28767
  this.sceneViewer.scene.updateMatrixWorld(true);
@@ -30791,6 +31488,7 @@ var ModelManager = /*#__PURE__*/function () {
30791
31488
  _context2.n = 1;
30792
31489
  break;
30793
31490
  }
31491
+ console.log("\uD83C\uDFAF GLB cache HIT: ".concat(modelKey));
30794
31492
  return _context2.a(2, gltfScene);
30795
31493
  case 1:
30796
31494
  // Check if preloading is in progress
@@ -30799,6 +31497,7 @@ var ModelManager = /*#__PURE__*/function () {
30799
31497
  _context2.n = 6;
30800
31498
  break;
30801
31499
  }
31500
+ console.log("\u23F3 Waiting for preloader: ".concat(modelKey));
30802
31501
  _context2.p = 2;
30803
31502
  _context2.n = 3;
30804
31503
  return modelPreloader.preloadingPromise;
@@ -30808,6 +31507,7 @@ var ModelManager = /*#__PURE__*/function () {
30808
31507
  _context2.n = 4;
30809
31508
  break;
30810
31509
  }
31510
+ console.log("\uD83C\uDFAF GLB cache HIT (after preload): ".concat(modelKey));
30811
31511
  return _context2.a(2, gltfScene);
30812
31512
  case 4:
30813
31513
  _context2.n = 6;
@@ -31135,7 +31835,7 @@ var ModelManager = /*#__PURE__*/function () {
31135
31835
  value: (function () {
31136
31836
  var _replaceWithGLBModels = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(libraryObjectsToReplace) {
31137
31837
  var _this3 = this;
31138
- var glbLoadingPromises;
31838
+ var startTime, glbLoadingPromises;
31139
31839
  return _regenerator().w(function (_context5) {
31140
31840
  while (1) switch (_context5.n) {
31141
31841
  case 0:
@@ -31145,23 +31845,29 @@ var ModelManager = /*#__PURE__*/function () {
31145
31845
  }
31146
31846
  return _context5.a(2);
31147
31847
  case 1:
31848
+ startTime = performance.now();
31148
31849
  console.log("\uD83D\uDD04 Replacing ".concat(libraryObjectsToReplace.length, " objects with GLB models..."));
31149
31850
  glbLoadingPromises = libraryObjectsToReplace.map(function (_ref, index) {
31150
31851
  var basicObject = _ref.basicObject,
31151
31852
  jsonData = _ref.jsonData,
31152
31853
  componentData = _ref.componentData;
31854
+ var modelStart = performance.now();
31153
31855
  return _this3.loadLibraryModel(basicObject, jsonData, componentData).then(function (libraryModel) {
31856
+ var _jsonData$userData;
31857
+ console.log("\u23F1\uFE0F GLB loaded: ".concat(((_jsonData$userData = jsonData.userData) === null || _jsonData$userData === void 0 ? void 0 : _jsonData$userData.libraryId) || jsonData.uuid, " in ").concat((performance.now() - modelStart).toFixed(0), "ms"));
31154
31858
  libraryObjectsToReplace[index].glbModel = libraryModel;
31155
31859
  return libraryModel;
31156
31860
  }).catch(function (error) {
31157
- var _jsonData$userData;
31158
- console.error("Failed to load ".concat((_jsonData$userData = jsonData.userData) === null || _jsonData$userData === void 0 ? void 0 : _jsonData$userData.libraryId, " GLB model:"), error);
31861
+ var _jsonData$userData2;
31862
+ console.error("Failed to load ".concat((_jsonData$userData2 = jsonData.userData) === null || _jsonData$userData2 === void 0 ? void 0 : _jsonData$userData2.libraryId, " GLB model:"), error);
31159
31863
  return basicObject;
31160
31864
  });
31161
31865
  });
31162
31866
  _context5.n = 2;
31163
31867
  return Promise.all(glbLoadingPromises);
31164
31868
  case 2:
31869
+ console.log("\u23F1\uFE0F All GLB models loaded in ".concat((performance.now() - startTime).toFixed(0), "ms"));
31870
+
31165
31871
  // Update world bounding boxes for loaded models and propagate to the live Three.js objects
31166
31872
  libraryObjectsToReplace.forEach(function (_ref2) {
31167
31873
  var jsonData = _ref2.jsonData,
@@ -32547,9 +33253,13 @@ var SceneOperationsManager = /*#__PURE__*/function () {
32547
33253
  value: (function () {
32548
33254
  var _loadSceneData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(data) {
32549
33255
  var isImported,
33256
+ timers,
33257
+ totalStart,
33258
+ phaseStart,
32550
33259
  _yield$this$_createBa,
32551
33260
  crosscubeTextureSet,
32552
33261
  libraryObjectsToReplace,
33262
+ totalTime,
32553
33263
  _args4 = arguments,
32554
33264
  _t3;
32555
33265
  return _regenerator().w(function (_context4) {
@@ -32557,13 +33267,20 @@ var SceneOperationsManager = /*#__PURE__*/function () {
32557
33267
  case 0:
32558
33268
  isImported = _args4.length > 1 && _args4[1] !== undefined ? _args4[1] : true;
32559
33269
  this.sceneViewer;
33270
+ timers = {};
33271
+ totalStart = performance.now();
32560
33272
  _context4.p = 1;
32561
33273
  console.log("Loading scene data (".concat(isImported ? 'imported' : 'default', ")..."));
32562
33274
 
32563
33275
  // Phase 1: Prepare scene
33276
+ phaseStart = performance.now();
32564
33277
  _context4.n = 2;
32565
33278
  return this._prepareSceneForLoading(data, isImported);
32566
33279
  case 2:
33280
+ timers.phase1_prepare = performance.now() - phaseStart;
33281
+
33282
+ // Phase 2: Load dependencies and create basic objects
33283
+ phaseStart = performance.now();
32567
33284
  _context4.n = 3;
32568
33285
  return this._createBasicSceneObjects(data);
32569
33286
  case 3:
@@ -32573,17 +33290,33 @@ var SceneOperationsManager = /*#__PURE__*/function () {
32573
33290
  _yield$this$_createBa.materials;
32574
33291
  crosscubeTextureSet = _yield$this$_createBa.crosscubeTextureSet;
32575
33292
  libraryObjectsToReplace = _yield$this$_createBa.libraryObjectsToReplace;
33293
+ timers.phase2_createObjects = performance.now() - phaseStart;
33294
+
33295
+ // Phase 3: Replace basic objects with GLB models (moved before pathfinding)
33296
+ phaseStart = performance.now();
32576
33297
  _context4.n = 4;
32577
33298
  return this.modelManager.replaceWithGLBModels(libraryObjectsToReplace);
32578
33299
  case 4:
33300
+ timers.phase3_glbModels = performance.now() - phaseStart;
33301
+
33302
+ // Phase 4: Setup pathfinding (moved after GLB loading for consistent bounding boxes)
33303
+ phaseStart = performance.now();
32579
33304
  _context4.n = 5;
32580
33305
  return this._setupPathfinding(data, crosscubeTextureSet);
32581
33306
  case 5:
33307
+ timers.phase4_pathfinding = performance.now() - phaseStart;
33308
+
32582
33309
  // Phase 5: Finalize scene
33310
+ phaseStart = performance.now();
32583
33311
  this._finalizeScene(data, crosscubeTextureSet, isImported);
33312
+ timers.phase5_finalize = performance.now() - phaseStart;
32584
33313
 
32585
33314
  // Phase 6: Load behaviors (after GLB models are present so output objects can be resolved)
33315
+ phaseStart = performance.now();
32586
33316
  this._processBehaviors(data);
33317
+ timers.phase6_behaviors = performance.now() - phaseStart;
33318
+ totalTime = performance.now() - totalStart;
33319
+ console.log("\u23F1\uFE0F Scene Loading Performance:\n Phase 1 (Prepare) : ".concat(timers.phase1_prepare.toFixed(0), "ms\n Phase 2 (Create Objects): ").concat(timers.phase2_createObjects.toFixed(0), "ms\n Phase 3 (GLB Models) : ").concat(timers.phase3_glbModels.toFixed(0), "ms\n Phase 4 (Pathfinding) : ").concat(timers.phase4_pathfinding.toFixed(0), "ms\n Phase 5 (Finalize) : ").concat(timers.phase5_finalize.toFixed(0), "ms\n Phase 6 (Behaviors) : ").concat(timers.phase6_behaviors.toFixed(0), "ms\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Total : ").concat(totalTime.toFixed(0), "ms"));
32587
33320
  console.log('✅ Scene loaded successfully');
32588
33321
 
32589
33322
  // Notify UI components (e.g. SceneHierarchy) that the scene is fully loaded
@@ -38089,7 +38822,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
38089
38822
  * Initialize the CentralPlant manager
38090
38823
  *
38091
38824
  * @constructor
38092
- * @version 0.3.16
38825
+ * @version 0.3.17
38093
38826
  * @updated 2025-10-22
38094
38827
  *
38095
38828
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.