@2112-lab/central-plant 0.1.13 → 0.1.15

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.
@@ -2,6 +2,7 @@ import { createClass as _createClass, typeof as _typeof, classCallCheck as _clas
2
2
  import * as THREE from 'three';
3
3
  import { GLTFLoader } from '../../node_modules/three/examples/jsm/loaders/GLTFLoader.js';
4
4
  import { OBJLoader } from '../../node_modules/three/examples/jsm/loaders/OBJLoader.js';
5
+ import ObjProcessor from './objProcessor.js';
5
6
 
6
7
  var ModelPreloader = /*#__PURE__*/function () {
7
8
  function ModelPreloader() {
@@ -10,10 +11,12 @@ var ModelPreloader = /*#__PURE__*/function () {
10
11
  this.loadingPromises = new Map(); // Track ongoing loads to prevent duplicates
11
12
  this.gltfLoader = new GLTFLoader();
12
13
  this.objLoader = new OBJLoader();
14
+ this.objProcessor = new ObjProcessor(); // Initialize OBJ processor
13
15
  this.isPreloading = false;
14
16
  this.preloadingPromise = null;
15
17
  this.componentDictionary = null;
16
18
  console.log('🚀 ModelPreloader initialized with GLB and OBJ support');
19
+ console.log('🔧 OBJ Processor integrated for indexed BufferGeometry conversion');
17
20
  }
18
21
 
19
22
  /**
@@ -119,93 +122,110 @@ var ModelPreloader = /*#__PURE__*/function () {
119
122
  }, {
120
123
  key: "preloadSingleModel",
121
124
  value: (function () {
122
- var _preloadSingleModel = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(modelKey) {
125
+ var _preloadSingleModel = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(modelKey) {
123
126
  var _this2 = this;
124
127
  var modelType,
125
128
  loadPromise,
126
- _args2 = arguments;
127
- return _regenerator().w(function (_context2) {
128
- while (1) switch (_context2.n) {
129
+ _args3 = arguments;
130
+ return _regenerator().w(function (_context3) {
131
+ while (1) switch (_context3.n) {
129
132
  case 0:
130
- modelType = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : 'glb';
133
+ modelType = _args3.length > 1 && _args3[1] !== undefined ? _args3[1] : 'glb';
131
134
  if (!this.modelCache.has(modelKey)) {
132
- _context2.n = 1;
135
+ _context3.n = 1;
133
136
  break;
134
137
  }
135
138
  console.log("\uD83C\uDFAF Model ".concat(modelKey, " already cached, skipping"));
136
- return _context2.a(2, this.modelCache.get(modelKey));
139
+ return _context3.a(2, this.modelCache.get(modelKey));
137
140
  case 1:
138
141
  if (!this.loadingPromises.has(modelKey)) {
139
- _context2.n = 2;
142
+ _context3.n = 2;
140
143
  break;
141
144
  }
142
145
  console.log("\u23F3 Model ".concat(modelKey, " already loading, waiting for completion"));
143
- return _context2.a(2, this.loadingPromises.get(modelKey));
146
+ return _context3.a(2, this.loadingPromises.get(modelKey));
144
147
  case 2:
145
148
  console.log("\uD83D\uDD04 Starting preload of ".concat(modelType.toUpperCase(), " model: ").concat(modelKey));
146
149
  loadPromise = new Promise(function (resolve, reject) {
147
150
  var modelPath = "/library/models/".concat(modelKey);
148
151
  if (modelType === 'obj') {
149
- // Load OBJ model
150
- _this2.objLoader.load(modelPath, function (object) {
151
- console.log("\u2705 Successfully preloaded OBJ model: ".concat(modelKey));
152
-
153
- // Apply double-sided materials to OBJ models with both solid and wireframe
154
- object.traverse(function (child) {
155
- if (child.isMesh) {
156
- // Create a solid material first
157
- var solidMaterial = new THREE.MeshStandardMaterial({
158
- color: child.material.color || 0x888888,
159
- side: THREE.DoubleSide,
160
- wireframe: false
161
- });
162
-
163
- // Set the solid material
164
- child.material = solidMaterial;
165
-
166
- // Create wireframe overlay by cloning the geometry
167
- var wireframeGeometry = child.geometry.clone();
168
- var wireframeMaterial = new THREE.MeshBasicMaterial({
169
- color: 0x000000,
170
- wireframe: true,
171
- transparent: true,
172
- opacity: 0.8
173
- });
174
-
175
- // Create wireframe mesh with same geometry
176
- var wireframeMesh = new THREE.Mesh(wireframeGeometry, wireframeMaterial);
177
- wireframeMesh.name = child.name + '_wireframe';
178
-
179
- // Position wireframe slightly forward to avoid z-fighting
180
- wireframeMesh.position.copy(child.position);
181
- wireframeMesh.rotation.copy(child.rotation);
182
- wireframeMesh.scale.copy(child.scale);
183
- wireframeMesh.scale.multiplyScalar(1.001); // Slightly larger to sit on top
184
-
185
- // Add wireframe as sibling to the parent
186
- if (child.parent) {
187
- child.parent.add(wireframeMesh);
152
+ // Load OBJ model with advanced processing
153
+ _this2.objLoader.load(modelPath, /*#__PURE__*/function () {
154
+ var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(object) {
155
+ var processedResult, modelData, _t;
156
+ return _regenerator().w(function (_context2) {
157
+ while (1) switch (_context2.n) {
158
+ case 0:
159
+ _context2.p = 0;
160
+ console.log("\u2705 Successfully loaded OBJ model: ".concat(modelKey));
161
+
162
+ // Process OBJ with the new processor for indexed BufferGeometry
163
+ _context2.n = 1;
164
+ return _this2.objProcessor.processObjObject(object, modelKey, true);
165
+ case 1:
166
+ processedResult = _context2.v;
167
+ if (!processedResult) {
168
+ _context2.n = 2;
169
+ break;
170
+ }
171
+ console.log("\uD83D\uDD27 OBJ processed to indexed BufferGeometry: ".concat(modelKey));
172
+ console.log("\uD83D\uDCCA Geometry stats:", processedResult.stats);
173
+
174
+ // Apply double-sided materials to original OBJ for compatibility
175
+ object.traverse(function (child) {
176
+ if (child.isMesh) {
177
+ var solidMaterial = new THREE.MeshStandardMaterial({
178
+ color: child.material.color || 0x888888,
179
+ side: THREE.DoubleSide,
180
+ wireframe: false
181
+ });
182
+ child.material = solidMaterial;
183
+ }
184
+ });
185
+
186
+ // Store both the original object and processed data
187
+ modelData = {
188
+ originalObject: object,
189
+ processedGeometry: processedResult.geometry,
190
+ geometryStats: processedResult.stats,
191
+ materials: processedResult.materials,
192
+ isObjModel: true
193
+ }; // Cache the model data
194
+ _this2.modelCache.set(modelKey, modelData);
195
+ _this2.loadingPromises.delete(modelKey);
196
+ resolve(modelData);
197
+ _context2.n = 3;
198
+ break;
199
+ case 2:
200
+ throw new Error('Failed to process OBJ to indexed geometry');
201
+ case 3:
202
+ _context2.n = 5;
203
+ break;
204
+ case 4:
205
+ _context2.p = 4;
206
+ _t = _context2.v;
207
+ console.error("\u274C Error processing OBJ model ".concat(modelKey, ":"), _t);
208
+ // Fallback: cache the original object without processing
209
+ _this2.modelCache.set(modelKey, object);
210
+ _this2.loadingPromises.delete(modelKey);
211
+ resolve(object);
212
+ case 5:
213
+ return _context2.a(2);
188
214
  }
189
- }
190
- });
191
-
192
- // Cache the object for future use
193
- _this2.modelCache.set(modelKey, object);
194
- // Remove from loading promises
195
- _this2.loadingPromises.delete(modelKey);
196
- resolve(object);
197
- }, function (progress) {
198
- // Optional: track loading progress
215
+ }, _callee2, null, [[0, 4]]);
216
+ }));
217
+ return function (_x3) {
218
+ return _ref.apply(this, arguments);
219
+ };
220
+ }(), function (progress) {
199
221
  if (progress.lengthComputable) {
200
222
  var percentage = progress.loaded / progress.total * 100;
201
223
  if (percentage % 25 === 0) {
202
- // Log every 25%
203
224
  console.log("\uD83D\uDCCA Loading OBJ ".concat(modelKey, ": ").concat(percentage.toFixed(0), "%"));
204
225
  }
205
226
  }
206
227
  }, function (error) {
207
228
  console.error("\u274C Failed to preload OBJ model ".concat(modelKey, ":"), error);
208
- // Remove from loading promises
209
229
  _this2.loadingPromises.delete(modelKey);
210
230
  reject(error);
211
231
  });
@@ -236,9 +256,9 @@ var ModelPreloader = /*#__PURE__*/function () {
236
256
  }
237
257
  }); // Cache the loading promise
238
258
  this.loadingPromises.set(modelKey, loadPromise);
239
- return _context2.a(2, loadPromise);
259
+ return _context3.a(2, loadPromise);
240
260
  }
241
- }, _callee2, this);
261
+ }, _callee3, this);
242
262
  }));
243
263
  function preloadSingleModel(_x2) {
244
264
  return _preloadSingleModel.apply(this, arguments);
@@ -248,18 +268,42 @@ var ModelPreloader = /*#__PURE__*/function () {
248
268
  /**
249
269
  * Get a cached model (cloned for use)
250
270
  * @param {string} modelKey - The model key
271
+ * @param {boolean} useIndexedGeometry - Whether to use indexed geometry for OBJ models
251
272
  * @returns {THREE.Object3D|null} Cloned model or null if not found
252
273
  */
253
274
  )
254
275
  }, {
255
276
  key: "getCachedModel",
256
277
  value: function getCachedModel(modelKey) {
278
+ var useIndexedGeometry = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
257
279
  if (this.modelCache.has(modelKey)) {
258
280
  console.log("\uD83C\uDFAF Returning cached model: ".concat(modelKey));
259
- var clonedModel = this.modelCache.get(modelKey).clone();
281
+ var cachedData = this.modelCache.get(modelKey);
282
+
283
+ // Handle OBJ models with processed geometry
284
+ if (cachedData && cachedData.isObjModel && useIndexedGeometry && cachedData.processedGeometry) {
285
+ console.log("\uD83D\uDD27 Using indexed BufferGeometry for OBJ: ".concat(modelKey));
286
+
287
+ // Create a mesh from the processed indexed geometry
288
+ var mesh = new THREE.Mesh(cachedData.processedGeometry.clone(), cachedData.materials.clone());
289
+
290
+ // Add metadata
291
+ mesh.userData.modelKey = modelKey;
292
+ mesh.userData.geometryStats = cachedData.geometryStats;
293
+ mesh.userData.isIndexedGeometry = true;
294
+ mesh.userData.originalSizeKB = cachedData.geometryStats.sizeKB;
295
+ return mesh;
296
+ }
297
+
298
+ // Handle regular models or OBJ models without indexed geometry
299
+ var modelToClone = cachedData.isObjModel ? cachedData.originalObject : cachedData;
300
+ var clonedModel = modelToClone.clone();
260
301
 
261
- // Determine model type from the modelKey extension
262
- modelKey.toLowerCase().endsWith('.obj') ? 'obj' : 'glb';
302
+ // Add OBJ stats if available
303
+ if (cachedData.isObjModel && cachedData.geometryStats) {
304
+ clonedModel.userData.geometryStats = cachedData.geometryStats;
305
+ clonedModel.userData.isObjModel = true;
306
+ }
263
307
  return clonedModel;
264
308
  }
265
309
  console.warn("\u26A0\uFE0F Model ".concat(modelKey, " not found in cache"));
@@ -367,41 +411,41 @@ var ModelPreloader = /*#__PURE__*/function () {
367
411
  }, {
368
412
  key: "reloadModel",
369
413
  value: (function () {
370
- var _reloadModel = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(modelKey) {
414
+ var _reloadModel = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(modelKey) {
371
415
  var modelType,
372
416
  _i,
373
417
  _Object$keys,
374
418
  componentType,
375
419
  component,
376
- _args3 = arguments;
377
- return _regenerator().w(function (_context3) {
378
- while (1) switch (_context3.n) {
420
+ _args4 = arguments;
421
+ return _regenerator().w(function (_context4) {
422
+ while (1) switch (_context4.n) {
379
423
  case 0:
380
- modelType = _args3.length > 1 && _args3[1] !== undefined ? _args3[1] : null;
424
+ modelType = _args4.length > 1 && _args4[1] !== undefined ? _args4[1] : null;
381
425
  console.log("\uD83D\uDD04 Force reloading model: ".concat(modelKey));
382
426
 
383
427
  // Try to determine model type from component dictionary if not provided
384
428
  if (!(!modelType && this.componentDictionary)) {
385
- _context3.n = 3;
429
+ _context4.n = 3;
386
430
  break;
387
431
  }
388
432
  _i = 0, _Object$keys = Object.keys(this.componentDictionary);
389
433
  case 1:
390
434
  if (!(_i < _Object$keys.length)) {
391
- _context3.n = 3;
435
+ _context4.n = 3;
392
436
  break;
393
437
  }
394
438
  componentType = _Object$keys[_i];
395
439
  component = this.componentDictionary[componentType];
396
440
  if (!(component && component.modelKey === modelKey)) {
397
- _context3.n = 2;
441
+ _context4.n = 2;
398
442
  break;
399
443
  }
400
444
  modelType = component.modelType || 'glb';
401
- return _context3.a(3, 3);
445
+ return _context4.a(3, 3);
402
446
  case 2:
403
447
  _i++;
404
- _context3.n = 1;
448
+ _context4.n = 1;
405
449
  break;
406
450
  case 3:
407
451
  // Default to GLB if still not determined
@@ -412,11 +456,11 @@ var ModelPreloader = /*#__PURE__*/function () {
412
456
  this.loadingPromises.delete(modelKey);
413
457
 
414
458
  // Preload again
415
- return _context3.a(2, this.preloadSingleModel(modelKey, modelType));
459
+ return _context4.a(2, this.preloadSingleModel(modelKey, modelType));
416
460
  }
417
- }, _callee3, this);
461
+ }, _callee4, this);
418
462
  }));
419
- function reloadModel(_x3) {
463
+ function reloadModel(_x4) {
420
464
  return _reloadModel.apply(this, arguments);
421
465
  }
422
466
  return reloadModel;
@@ -433,7 +477,42 @@ var ModelPreloader = /*#__PURE__*/function () {
433
477
  // Dispose of all cached models
434
478
  this.modelCache.forEach(function (model, modelKey) {
435
479
  console.log("\uD83D\uDDD1\uFE0F Disposing cached model: ".concat(modelKey));
436
- if (model && model.traverse) {
480
+ if (model && model.isObjModel) {
481
+ // Handle OBJ model data structure
482
+ if (model.originalObject && model.originalObject.traverse) {
483
+ model.originalObject.traverse(function (child) {
484
+ if (child.geometry) {
485
+ child.geometry.dispose();
486
+ }
487
+ if (child.material) {
488
+ if (Array.isArray(child.material)) {
489
+ child.material.forEach(function (material) {
490
+ return material.dispose();
491
+ });
492
+ } else {
493
+ child.material.dispose();
494
+ }
495
+ }
496
+ });
497
+ }
498
+
499
+ // Dispose processed geometry
500
+ if (model.processedGeometry) {
501
+ model.processedGeometry.dispose();
502
+ }
503
+
504
+ // Dispose materials
505
+ if (model.materials) {
506
+ if (Array.isArray(model.materials)) {
507
+ model.materials.forEach(function (material) {
508
+ return material.dispose();
509
+ });
510
+ } else {
511
+ model.materials.dispose();
512
+ }
513
+ }
514
+ } else if (model && model.traverse) {
515
+ // Handle regular model
437
516
  model.traverse(function (child) {
438
517
  if (child.geometry) {
439
518
  child.geometry.dispose();
@@ -454,6 +533,9 @@ var ModelPreloader = /*#__PURE__*/function () {
454
533
  this.loadingPromises.clear();
455
534
  this.isPreloading = false;
456
535
  this.preloadingPromise = null;
536
+
537
+ // Clear OBJ processor caches
538
+ this.objProcessor.clearCaches();
457
539
  console.log('✅ Model preloader cache cleared');
458
540
  }
459
541
 
@@ -562,13 +644,15 @@ var ModelPreloader = /*#__PURE__*/function () {
562
644
  totalModels: this.modelCache.size,
563
645
  loadingModels: this.loadingPromises.size,
564
646
  isPreloading: this.isPreloading,
565
- memoryUsage: 0
647
+ memoryUsage: 0,
648
+ objProcessorStats: this.objProcessor.getStatus()
566
649
  };
567
650
 
568
651
  // Estimate memory usage (rough calculation)
569
652
  this.modelCache.forEach(function (model) {
570
653
  if (model && model.traverse) {
571
- model.traverse(function (child) {
654
+ var modelToAnalyze = model.isObjModel ? model.originalObject : model;
655
+ modelToAnalyze.traverse(function (child) {
572
656
  if (child.geometry) {
573
657
  var _child$geometry$attri;
574
658
  stats.memoryUsage += (((_child$geometry$attri = child.geometry.attributes.position) === null || _child$geometry$attri === void 0 ? void 0 : _child$geometry$attri.count) || 0) * 12; // Rough estimate
@@ -578,6 +662,82 @@ var ModelPreloader = /*#__PURE__*/function () {
578
662
  });
579
663
  return stats;
580
664
  }
665
+
666
+ /**
667
+ * Get all OBJ geometry statistics
668
+ * @returns {Array} Array of OBJ geometry statistics
669
+ */
670
+ }, {
671
+ key: "getObjGeometryStats",
672
+ value: function getObjGeometryStats() {
673
+ return this.objProcessor.getAllGeometryStats();
674
+ }
675
+
676
+ /**
677
+ * Get OBJ processor memory usage
678
+ * @returns {Object} Memory usage statistics
679
+ */
680
+ }, {
681
+ key: "getObjMemoryUsage",
682
+ value: function getObjMemoryUsage() {
683
+ return this.objProcessor.getMemoryUsage();
684
+ }
685
+
686
+ /**
687
+ * Create a custom mesh from indexed OBJ geometry
688
+ * @param {string} modelKey - The model key (must be an OBJ)
689
+ * @param {THREE.Material} material - Optional material to use
690
+ * @returns {THREE.Mesh|null} The created mesh or null if not found
691
+ */
692
+ }, {
693
+ key: "createCustomObjMesh",
694
+ value: function createCustomObjMesh(modelKey) {
695
+ var material = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
696
+ return this.objProcessor.createCustomMesh(modelKey, material);
697
+ }
698
+
699
+ /**
700
+ * Get indexed geometry directly from processor
701
+ * @param {string} modelKey - The model key
702
+ * @returns {THREE.BufferGeometry|null} The indexed geometry or null
703
+ */
704
+ }, {
705
+ key: "getIndexedGeometry",
706
+ value: function getIndexedGeometry(modelKey) {
707
+ var result = this.objProcessor.indexedGeometryCache.get(modelKey);
708
+ return result ? result.geometry.clone() : null;
709
+ }
710
+
711
+ /**
712
+ * Force reload and reprocess an OBJ model
713
+ * @param {string} modelKey - The model key to reload
714
+ * @returns {Promise} Promise that resolves with the reloaded model
715
+ */
716
+ }, {
717
+ key: "reloadObjModel",
718
+ value: (function () {
719
+ var _reloadObjModel = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(modelKey) {
720
+ return _regenerator().w(function (_context5) {
721
+ while (1) switch (_context5.n) {
722
+ case 0:
723
+ console.log("\uD83D\uDD04 Force reloading OBJ model: ".concat(modelKey));
724
+
725
+ // Clear from both caches
726
+ this.modelCache.delete(modelKey);
727
+ this.loadingPromises.delete(modelKey);
728
+ this.objProcessor.indexedGeometryCache.delete(modelKey);
729
+ this.objProcessor.geometrySizeCache.delete(modelKey);
730
+
731
+ // Reload with OBJ processing
732
+ return _context5.a(2, this.preloadSingleModel(modelKey, 'obj'));
733
+ }
734
+ }, _callee5, this);
735
+ }));
736
+ function reloadObjModel(_x5) {
737
+ return _reloadObjModel.apply(this, arguments);
738
+ }
739
+ return reloadObjModel;
740
+ }())
581
741
  }]);
582
742
  }(); // Create singleton instance
583
743
  var modelPreloader = new ModelPreloader();