@lingbi/studio 0.3.2 → 0.3.3

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.
package/index.js CHANGED
@@ -5,6 +5,10 @@ var ObjImportFailureCode$1 = /* @__PURE__ */ ((ObjImportFailureCode2) => {
5
5
  ObjImportFailureCode2["RenderFailed"] = "render-failed";
6
6
  return ObjImportFailureCode2;
7
7
  })(ObjImportFailureCode$1 || {});
8
+ var ObjImportWarningCode$1 = /* @__PURE__ */ ((ObjImportWarningCode2) => {
9
+ ObjImportWarningCode2["IncompleteMaterial"] = "incomplete-material";
10
+ return ObjImportWarningCode2;
11
+ })(ObjImportWarningCode$1 || {});
8
12
  const REVISION = "185";
9
13
  const CullFaceNone = 0;
10
14
  const CullFaceBack = 1;
@@ -7778,8 +7782,8 @@ class Color {
7778
7782
  * @return {number} The hexadecimal value.
7779
7783
  */
7780
7784
  getHex(colorSpace = SRGBColorSpace) {
7781
- ColorManagement.workingToColorSpace(_color$1.copy(this), colorSpace);
7782
- return Math.round(clamp(_color$1.r * 255, 0, 255)) * 65536 + Math.round(clamp(_color$1.g * 255, 0, 255)) * 256 + Math.round(clamp(_color$1.b * 255, 0, 255));
7785
+ ColorManagement.workingToColorSpace(_color$2.copy(this), colorSpace);
7786
+ return Math.round(clamp(_color$2.r * 255, 0, 255)) * 65536 + Math.round(clamp(_color$2.g * 255, 0, 255)) * 256 + Math.round(clamp(_color$2.b * 255, 0, 255));
7783
7787
  }
7784
7788
  /**
7785
7789
  * Returns the hexadecimal value of this color as a string (for example, 'FFFFFF').
@@ -7799,8 +7803,8 @@ class Color {
7799
7803
  * @return {{h:number,s:number,l:number}} The HSL representation of this color.
7800
7804
  */
7801
7805
  getHSL(target, colorSpace = ColorManagement.workingColorSpace) {
7802
- ColorManagement.workingToColorSpace(_color$1.copy(this), colorSpace);
7803
- const r = _color$1.r, g = _color$1.g, b = _color$1.b;
7806
+ ColorManagement.workingToColorSpace(_color$2.copy(this), colorSpace);
7807
+ const r = _color$2.r, g = _color$2.g, b = _color$2.b;
7804
7808
  const max = Math.max(r, g, b);
7805
7809
  const min = Math.min(r, g, b);
7806
7810
  let hue, saturation;
@@ -7837,10 +7841,10 @@ class Color {
7837
7841
  * @return {Color} The RGB representation of this color.
7838
7842
  */
7839
7843
  getRGB(target, colorSpace = ColorManagement.workingColorSpace) {
7840
- ColorManagement.workingToColorSpace(_color$1.copy(this), colorSpace);
7841
- target.r = _color$1.r;
7842
- target.g = _color$1.g;
7843
- target.b = _color$1.b;
7844
+ ColorManagement.workingToColorSpace(_color$2.copy(this), colorSpace);
7845
+ target.r = _color$2.r;
7846
+ target.g = _color$2.g;
7847
+ target.b = _color$2.b;
7844
7848
  return target;
7845
7849
  }
7846
7850
  /**
@@ -7850,8 +7854,8 @@ class Color {
7850
7854
  * @return {string} The CSS representation of this color.
7851
7855
  */
7852
7856
  getStyle(colorSpace = SRGBColorSpace) {
7853
- ColorManagement.workingToColorSpace(_color$1.copy(this), colorSpace);
7854
- const r = _color$1.r, g = _color$1.g, b = _color$1.b;
7857
+ ColorManagement.workingToColorSpace(_color$2.copy(this), colorSpace);
7858
+ const r = _color$2.r, g = _color$2.g, b = _color$2.b;
7855
7859
  if (colorSpace !== SRGBColorSpace) {
7856
7860
  return `color(${colorSpace} ${r.toFixed(3)} ${g.toFixed(3)} ${b.toFixed(3)})`;
7857
7861
  }
@@ -8085,7 +8089,7 @@ class Color {
8085
8089
  yield this.b;
8086
8090
  }
8087
8091
  }
8088
- const _color$1 = /* @__PURE__ */ new Color();
8092
+ const _color$2 = /* @__PURE__ */ new Color();
8089
8093
  Color.NAMES = _colorKeywords;
8090
8094
  class Scene extends Object3D {
8091
8095
  /**
@@ -9468,6 +9472,42 @@ class BufferAttribute extends EventDispatcher {
9468
9472
  this.dispatchEvent({ type: "dispose" });
9469
9473
  }
9470
9474
  }
9475
+ class Int8BufferAttribute extends BufferAttribute {
9476
+ /**
9477
+ * Constructs a new buffer attribute.
9478
+ *
9479
+ * @param {(Array<number>|Int8Array)} array - The array holding the attribute data.
9480
+ * @param {number} itemSize - The item size.
9481
+ * @param {boolean} [normalized=false] - Whether the data are normalized or not.
9482
+ */
9483
+ constructor(array, itemSize, normalized) {
9484
+ super(new Int8Array(array), itemSize, normalized);
9485
+ }
9486
+ }
9487
+ class Uint8BufferAttribute extends BufferAttribute {
9488
+ /**
9489
+ * Constructs a new buffer attribute.
9490
+ *
9491
+ * @param {(Array<number>|Uint8Array)} array - The array holding the attribute data.
9492
+ * @param {number} itemSize - The item size.
9493
+ * @param {boolean} [normalized=false] - Whether the data are normalized or not.
9494
+ */
9495
+ constructor(array, itemSize, normalized) {
9496
+ super(new Uint8Array(array), itemSize, normalized);
9497
+ }
9498
+ }
9499
+ class Int16BufferAttribute extends BufferAttribute {
9500
+ /**
9501
+ * Constructs a new buffer attribute.
9502
+ *
9503
+ * @param {(Array<number>|Int16Array)} array - The array holding the attribute data.
9504
+ * @param {number} itemSize - The item size.
9505
+ * @param {boolean} [normalized=false] - Whether the data are normalized or not.
9506
+ */
9507
+ constructor(array, itemSize, normalized) {
9508
+ super(new Int16Array(array), itemSize, normalized);
9509
+ }
9510
+ }
9471
9511
  class Uint16BufferAttribute extends BufferAttribute {
9472
9512
  /**
9473
9513
  * Constructs a new buffer attribute.
@@ -9480,6 +9520,18 @@ class Uint16BufferAttribute extends BufferAttribute {
9480
9520
  super(new Uint16Array(array), itemSize, normalized);
9481
9521
  }
9482
9522
  }
9523
+ class Int32BufferAttribute extends BufferAttribute {
9524
+ /**
9525
+ * Constructs a new buffer attribute.
9526
+ *
9527
+ * @param {(Array<number>|Int32Array)} array - The array holding the attribute data.
9528
+ * @param {number} itemSize - The item size.
9529
+ * @param {boolean} [normalized=false] - Whether the data are normalized or not.
9530
+ */
9531
+ constructor(array, itemSize, normalized) {
9532
+ super(new Int32Array(array), itemSize, normalized);
9533
+ }
9534
+ }
9483
9535
  class Uint32BufferAttribute extends BufferAttribute {
9484
9536
  /**
9485
9537
  * Constructs a new buffer attribute.
@@ -29535,6 +29587,179 @@ class CanvasController {
29535
29587
  }
29536
29588
  }
29537
29589
  }
29590
+ class ObjResourceList {
29591
+ mtlUrl;
29592
+ objUrl;
29593
+ mtlFileName;
29594
+ textureUrls = /* @__PURE__ */ new Set();
29595
+ textureUrlsByFileName = /* @__PURE__ */ new Map();
29596
+ constructor(fileUrls) {
29597
+ let mtlFileName;
29598
+ let mtlUrl;
29599
+ let objUrl;
29600
+ const seenFileNames = /* @__PURE__ */ new Set();
29601
+ const seenUrls = /* @__PURE__ */ new Set();
29602
+ for (const fileUrl of fileUrls) {
29603
+ const resource = this.createResourceEntry(fileUrl);
29604
+ if (seenUrls.has(resource.url) || seenFileNames.has(resource.fileName)) {
29605
+ throw new Error("The OBJ resource list contains a duplicate resource.");
29606
+ }
29607
+ seenUrls.add(resource.url);
29608
+ seenFileNames.add(resource.fileName);
29609
+ if (this.isObjUrl(resource.url)) {
29610
+ if (objUrl !== void 0) {
29611
+ throw new Error("The OBJ resource list must contain exactly one OBJ file.");
29612
+ }
29613
+ objUrl = resource.url;
29614
+ continue;
29615
+ }
29616
+ if (this.isMtlUrl(resource.url)) {
29617
+ if (mtlUrl !== void 0) {
29618
+ throw new Error("The OBJ resource list cannot contain multiple MTL files.");
29619
+ }
29620
+ mtlFileName = resource.fileName;
29621
+ mtlUrl = resource.url;
29622
+ continue;
29623
+ }
29624
+ this.textureUrls.add(resource.url);
29625
+ this.textureUrlsByFileName.set(resource.fileName, resource.url);
29626
+ }
29627
+ if (objUrl === void 0) {
29628
+ throw new Error("The OBJ resource list must contain an OBJ file.");
29629
+ }
29630
+ this.mtlFileName = mtlFileName;
29631
+ this.mtlUrl = mtlUrl;
29632
+ this.objUrl = objUrl;
29633
+ }
29634
+ matchesMaterialLibraries(materialReferences) {
29635
+ if (materialReferences.length === 0) {
29636
+ return true;
29637
+ }
29638
+ const mtlUrl = this.mtlUrl;
29639
+ const mtlFileName = this.mtlFileName;
29640
+ const [materialReference, ...remainingReferences] = materialReferences;
29641
+ if (materialReference === void 0 || remainingReferences.length > 0 || mtlUrl === void 0 || mtlFileName === void 0) {
29642
+ return false;
29643
+ }
29644
+ const referenceUrl = new URL(materialReference, this.objUrl).href;
29645
+ if (referenceUrl === mtlUrl || this.getFileName(new URL(referenceUrl)) === mtlFileName) {
29646
+ return true;
29647
+ }
29648
+ return false;
29649
+ }
29650
+ resolveTextureUrl(materialBaseUrl, textureUrl) {
29651
+ const resolvedUrl = new URL(textureUrl, materialBaseUrl).href;
29652
+ if (this.textureUrls.has(resolvedUrl)) {
29653
+ return resolvedUrl;
29654
+ }
29655
+ const mappedUrl = this.textureUrlsByFileName.get(this.getFileName(new URL(resolvedUrl)));
29656
+ if (mappedUrl === void 0) {
29657
+ throw new Error("The MTL texture is not present in the resource list.");
29658
+ }
29659
+ return mappedUrl;
29660
+ }
29661
+ createResourceEntry(fileUrl) {
29662
+ const url = new URL(fileUrl);
29663
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
29664
+ throw new Error("The OBJ resource list only accepts HTTP URLs.");
29665
+ }
29666
+ return {
29667
+ fileName: this.getFileName(url),
29668
+ url: url.href
29669
+ };
29670
+ }
29671
+ getFileName(url) {
29672
+ const fileName = decodeURIComponent(url.pathname.slice(url.pathname.lastIndexOf("/") + 1));
29673
+ if (fileName.length === 0) {
29674
+ throw new Error("The OBJ resource URL must include a file name.");
29675
+ }
29676
+ return fileName;
29677
+ }
29678
+ isMtlUrl(url) {
29679
+ return new URL(url).pathname.toLowerCase().endsWith(".mtl");
29680
+ }
29681
+ isObjUrl(url) {
29682
+ return new URL(url).pathname.toLowerCase().endsWith(".obj");
29683
+ }
29684
+ }
29685
+ class PlyResourceList {
29686
+ plyUrl;
29687
+ resourceUrlsByFileName = /* @__PURE__ */ new Map();
29688
+ constructor(fileUrls) {
29689
+ let plyUrl;
29690
+ const seenFileNames = /* @__PURE__ */ new Set();
29691
+ const seenUrls = /* @__PURE__ */ new Set();
29692
+ for (const fileUrl of fileUrls) {
29693
+ const resource = this.createResourceEntry(fileUrl);
29694
+ if (seenUrls.has(resource.url) || seenFileNames.has(resource.fileName)) {
29695
+ throw new Error("The PLY resource list contains a duplicate resource.");
29696
+ }
29697
+ seenUrls.add(resource.url);
29698
+ seenFileNames.add(resource.fileName);
29699
+ this.resourceUrlsByFileName.set(resource.fileName, resource.url);
29700
+ if (this.isPlyUrl(resource.url)) {
29701
+ if (plyUrl !== void 0) {
29702
+ throw new Error("The PLY resource list must contain exactly one PLY file.");
29703
+ }
29704
+ plyUrl = resource.url;
29705
+ }
29706
+ }
29707
+ if (plyUrl === void 0) {
29708
+ throw new Error("The PLY resource list must contain a PLY file.");
29709
+ }
29710
+ this.plyUrl = plyUrl;
29711
+ }
29712
+ resolveTextureUrl(reference) {
29713
+ const normalizedReference = reference.replaceAll("\\", "/");
29714
+ const resolvedUrl = new URL(normalizedReference, this.plyUrl);
29715
+ const mappedUrl = this.resourceUrlsByFileName.get(this.getFileName(resolvedUrl));
29716
+ return mappedUrl ?? resolvedUrl.href;
29717
+ }
29718
+ createResourceEntry(fileUrl) {
29719
+ const url = new URL(fileUrl);
29720
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
29721
+ throw new Error("The PLY resource list only accepts HTTP URLs.");
29722
+ }
29723
+ return {
29724
+ fileName: this.getFileName(url),
29725
+ url: url.href
29726
+ };
29727
+ }
29728
+ getFileName(url) {
29729
+ const fileName = decodeURIComponent(url.pathname.slice(url.pathname.lastIndexOf("/") + 1));
29730
+ if (fileName.length === 0) {
29731
+ throw new Error("The PLY resource URL must include a file name.");
29732
+ }
29733
+ return fileName;
29734
+ }
29735
+ isPlyUrl(url) {
29736
+ return new URL(url).pathname.toLowerCase().endsWith(".ply");
29737
+ }
29738
+ }
29739
+ class ModelResourceList {
29740
+ resources;
29741
+ constructor(fileUrls) {
29742
+ let modelFileCount = 0;
29743
+ let modelFormat;
29744
+ for (const fileUrl of fileUrls) {
29745
+ const path = new URL(fileUrl).pathname.toLowerCase();
29746
+ if (path.endsWith(".obj")) {
29747
+ modelFileCount += 1;
29748
+ modelFormat = "obj";
29749
+ } else if (path.endsWith(".ply")) {
29750
+ modelFileCount += 1;
29751
+ modelFormat = "ply";
29752
+ }
29753
+ }
29754
+ if (modelFileCount !== 1 || modelFormat === void 0) {
29755
+ throw new Error("A model resource list must contain exactly one OBJ or PLY file.");
29756
+ }
29757
+ this.resources = modelFormat === "obj" ? new ObjResourceList(fileUrls) : new PlyResourceList(fileUrls);
29758
+ }
29759
+ isObj() {
29760
+ return this.resources instanceof ObjResourceList;
29761
+ }
29762
+ }
29538
29763
  class MTLLoader extends Loader {
29539
29764
  constructor(manager) {
29540
29765
  super(manager);
@@ -29840,7 +30065,7 @@ const _vB = new Vector3();
29840
30065
  const _vC = new Vector3();
29841
30066
  const _ab = new Vector3();
29842
30067
  const _cb = new Vector3();
29843
- const _color = new Color();
30068
+ const _color$1 = new Color();
29844
30069
  function ParserState() {
29845
30070
  const state = {
29846
30071
  objects: [],
@@ -30151,13 +30376,13 @@ class OBJLoader extends Loader {
30151
30376
  parseFloat(data[3])
30152
30377
  );
30153
30378
  if (data.length >= 7) {
30154
- _color.setRGB(
30379
+ _color$1.setRGB(
30155
30380
  parseFloat(data[4]),
30156
30381
  parseFloat(data[5]),
30157
30382
  parseFloat(data[6]),
30158
30383
  SRGBColorSpace
30159
30384
  );
30160
- state.colors.push(_color.r, _color.g, _color.b);
30385
+ state.colors.push(_color$1.r, _color$1.g, _color$1.b);
30161
30386
  } else {
30162
30387
  state.colors.push(void 0, void 0, void 0);
30163
30388
  }
@@ -30464,8 +30689,8 @@ class Model {
30464
30689
  return Array.isArray(values);
30465
30690
  }
30466
30691
  }
30467
- const DEFAULT_MODEL_COLOR = 10265519;
30468
- const DEFAULT_POINT_SIZE = 3;
30692
+ const DEFAULT_MODEL_COLOR$1 = 10265519;
30693
+ const DEFAULT_POINT_SIZE$1 = 3;
30469
30694
  class TextureLoadMonitor {
30470
30695
  completionResolver;
30471
30696
  completionPromise;
@@ -30573,24 +30798,33 @@ class ObjLoader {
30573
30798
  }
30574
30799
  let materialContext;
30575
30800
  let model;
30801
+ let warnings = [];
30576
30802
  try {
30577
30803
  const objSource = await this.loadText(source.objUrl, signal);
30578
30804
  this.assertNotCancelled(signal);
30579
30805
  const materialReferences = this.getMaterialLibraryReferences(objSource);
30580
- resources?.assertMaterialLibraries(materialReferences);
30581
- const materialUrl = resources === void 0 ? this.getMaterialUrl(source, materialReferences) : resources.mtlUrl;
30806
+ const hasMatchingMaterialLibrary = resources === void 0 || resources.matchesMaterialLibraries(materialReferences);
30807
+ if (materialReferences.length > 0 && !hasMatchingMaterialLibrary) {
30808
+ warnings = [this.createIncompleteMaterialWarning()];
30809
+ }
30810
+ const materialUrl = resources === void 0 ? this.getMaterialUrl(source, materialReferences) : hasMatchingMaterialLibrary ? resources.mtlUrl : void 0;
30582
30811
  if (materialUrl !== void 0) {
30583
- materialContext = await this.loadMaterialContext(
30584
- materialUrl,
30585
- source.textureUrls,
30586
- signal,
30587
- resources
30588
- );
30812
+ try {
30813
+ materialContext = await this.loadMaterialContext(
30814
+ materialUrl,
30815
+ source.textureUrls,
30816
+ signal,
30817
+ resources
30818
+ );
30819
+ } catch {
30820
+ this.assertNotCancelled(signal);
30821
+ warnings = [this.createIncompleteMaterialWarning()];
30822
+ }
30589
30823
  }
30590
30824
  this.assertNotCancelled(signal);
30591
- const group = this.parseObj(objSource, materialContext?.creator);
30592
- const additionalMaterials = this.getMaterialArray(materialContext);
30593
- const additionalTextures = this.getTextureArray(materialContext);
30825
+ let group = this.parseObj(objSource, materialContext?.creator);
30826
+ let additionalMaterials = this.getMaterialArray(materialContext);
30827
+ let additionalTextures = this.getTextureArray(materialContext);
30594
30828
  model = new Model(group, additionalMaterials, additionalTextures);
30595
30829
  if (!this.hasRenderablePrimitive(group) || !model.hasFiniteBounds()) {
30596
30830
  model.dispose();
@@ -30599,22 +30833,37 @@ class ObjLoader {
30599
30833
  success: false
30600
30834
  };
30601
30835
  }
30602
- if (materialContext === void 0) {
30603
- this.normalizeMaterials(group);
30604
- } else {
30836
+ if (materialContext !== void 0) {
30605
30837
  const textureLoadOutcome = await materialContext.monitor.wait(signal);
30606
- if (textureLoadOutcome !== "succeeded") {
30838
+ if (textureLoadOutcome === "cancelled") {
30839
+ this.assertNotCancelled(signal);
30840
+ }
30841
+ if (textureLoadOutcome === "failed") {
30607
30842
  model.dispose();
30608
- return {
30609
- code: this.getTextureFailureCode(textureLoadOutcome),
30610
- success: false
30611
- };
30843
+ model = void 0;
30844
+ materialContext = void 0;
30845
+ warnings = [this.createIncompleteMaterialWarning()];
30846
+ group = this.parseObj(objSource, void 0);
30847
+ additionalMaterials = [];
30848
+ additionalTextures = [];
30849
+ model = new Model(group, additionalMaterials, additionalTextures);
30850
+ if (!this.hasRenderablePrimitive(group) || !model.hasFiniteBounds()) {
30851
+ model.dispose();
30852
+ return {
30853
+ code: ObjImportFailureCode$1.InvalidSource,
30854
+ success: false
30855
+ };
30856
+ }
30612
30857
  }
30613
30858
  }
30859
+ if (materialContext === void 0) {
30860
+ this.normalizeMaterials(group);
30861
+ }
30614
30862
  this.assertNotCancelled(signal);
30615
30863
  return {
30616
30864
  model,
30617
- success: true
30865
+ success: true,
30866
+ ...warnings.length > 0 ? { warnings } : {}
30618
30867
  };
30619
30868
  } catch {
30620
30869
  this.disposeFailedModel(model);
@@ -30704,9 +30953,6 @@ class ObjLoader {
30704
30953
  }
30705
30954
  return objectTree;
30706
30955
  }
30707
- getTextureFailureCode(outcome) {
30708
- return outcome === "cancelled" ? ObjImportFailureCode$1.Cancelled : ObjImportFailureCode$1.InvalidSource;
30709
- }
30710
30956
  getMaterialLibraryReferences(objSource) {
30711
30957
  const materialReferences = [];
30712
30958
  for (const line of objSource.split(/\r?\n/u)) {
@@ -30717,6 +30963,11 @@ class ObjLoader {
30717
30963
  }
30718
30964
  return materialReferences;
30719
30965
  }
30966
+ createIncompleteMaterialWarning() {
30967
+ return {
30968
+ code: ObjImportWarningCode$1.IncompleteMaterial
30969
+ };
30970
+ }
30720
30971
  getMaterialTextureReferences(materialSource, creator) {
30721
30972
  const textureReferences = [];
30722
30973
  for (const line of materialSource.split(/\r?\n/u)) {
@@ -30776,14 +31027,14 @@ class ObjLoader {
30776
31027
  side: DoubleSide
30777
31028
  });
30778
31029
  const creator = materialLoader.parse(materialSource, materialBaseUrl);
30779
- const pbrMaterialReferences = this.parsePbrMaterialReferences(materialSource);
30780
- if (resources !== void 0) {
30781
- this.assertMaterialTextures(resources, materialSource, materialBaseUrl, creator);
30782
- }
30783
- this.validateTextureOverrides(textureUrls);
30784
- this.removeLegacyPbrTextureMappings(creator);
30785
31030
  const monitor = new TextureLoadMonitor(manager, creator);
30786
31031
  try {
31032
+ const pbrMaterialReferences = this.parsePbrMaterialReferences(materialSource);
31033
+ if (resources !== void 0) {
31034
+ this.assertMaterialTextures(resources, materialSource, materialBaseUrl, creator);
31035
+ }
31036
+ this.validateTextureOverrides(textureUrls);
31037
+ this.removeLegacyPbrTextureMappings(creator);
30787
31038
  creator.preload();
30788
31039
  this.replaceMaterialsWithPbr(creator, pbrMaterialReferences, materialBaseUrl);
30789
31040
  monitor.finishScheduling();
@@ -30817,21 +31068,21 @@ class ObjLoader {
30817
31068
  for (const object of this.getObjectTree(group)) {
30818
31069
  if (this.isMesh(object)) {
30819
31070
  meshMaterial ??= new MeshBasicMaterial({
30820
- color: DEFAULT_MODEL_COLOR,
31071
+ color: DEFAULT_MODEL_COLOR$1,
30821
31072
  side: DoubleSide
30822
31073
  });
30823
31074
  this.collectMaterials(object.material, materialsToDispose);
30824
31075
  object.material = meshMaterial;
30825
31076
  } else if (this.isLine(object)) {
30826
31077
  lineMaterial ??= new LineBasicMaterial({
30827
- color: DEFAULT_MODEL_COLOR
31078
+ color: DEFAULT_MODEL_COLOR$1
30828
31079
  });
30829
31080
  this.collectMaterials(object.material, materialsToDispose);
30830
31081
  object.material = lineMaterial;
30831
31082
  } else if (this.isPoints(object)) {
30832
31083
  pointMaterial ??= new PointsMaterial({
30833
- color: DEFAULT_MODEL_COLOR,
30834
- size: DEFAULT_POINT_SIZE,
31084
+ color: DEFAULT_MODEL_COLOR$1,
31085
+ size: DEFAULT_POINT_SIZE$1,
30835
31086
  sizeAttenuation: false
30836
31087
  });
30837
31088
  this.collectMaterials(object.material, materialsToDispose);
@@ -31019,99 +31270,868 @@ class ObjLoader {
31019
31270
  }
31020
31271
  }
31021
31272
  }
31022
- class ObjResourceList {
31023
- mtlUrl;
31024
- objUrl;
31025
- mtlFileName;
31026
- textureUrls = /* @__PURE__ */ new Set();
31027
- textureUrlsByFileName = /* @__PURE__ */ new Map();
31028
- constructor(fileUrls) {
31029
- let mtlFileName;
31030
- let mtlUrl;
31031
- let objUrl;
31032
- const seenFileNames = /* @__PURE__ */ new Set();
31033
- const seenUrls = /* @__PURE__ */ new Set();
31034
- for (const fileUrl of fileUrls) {
31035
- const resource = this.createResourceEntry(fileUrl);
31036
- if (seenUrls.has(resource.url) || seenFileNames.has(resource.fileName)) {
31037
- throw new Error("The OBJ resource list contains a duplicate resource.");
31038
- }
31039
- seenUrls.add(resource.url);
31040
- seenFileNames.add(resource.fileName);
31041
- if (this.isObjUrl(resource.url)) {
31042
- if (objUrl !== void 0) {
31043
- throw new Error("The OBJ resource list must contain exactly one OBJ file.");
31044
- }
31045
- objUrl = resource.url;
31046
- continue;
31047
- }
31048
- if (this.isMtlUrl(resource.url)) {
31049
- if (mtlUrl !== void 0) {
31050
- throw new Error("The OBJ resource list cannot contain multiple MTL files.");
31273
+ const _color = new Color();
31274
+ class PLYLoader extends Loader {
31275
+ /**
31276
+ * Constructs a new PLY loader.
31277
+ *
31278
+ * @param {LoadingManager} [manager] - The loading manager.
31279
+ */
31280
+ constructor(manager) {
31281
+ super(manager);
31282
+ this.propertyNameMapping = {};
31283
+ this.customPropertyMapping = {};
31284
+ }
31285
+ /**
31286
+ * Starts loading from the given URL and passes the loaded PLY asset
31287
+ * to the `onLoad()` callback.
31288
+ *
31289
+ * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
31290
+ * @param {function(BufferGeometry)} onLoad - Executed when the loading process has been finished.
31291
+ * @param {onProgressCallback} onProgress - Executed while the loading is in progress.
31292
+ * @param {onErrorCallback} onError - Executed when errors occur.
31293
+ */
31294
+ load(url, onLoad, onProgress, onError) {
31295
+ const scope = this;
31296
+ const loader = new FileLoader(this.manager);
31297
+ loader.setPath(this.path);
31298
+ loader.setResponseType("arraybuffer");
31299
+ loader.setRequestHeader(this.requestHeader);
31300
+ loader.setWithCredentials(this.withCredentials);
31301
+ loader.load(url, function(text) {
31302
+ try {
31303
+ onLoad(scope.parse(text));
31304
+ } catch (e) {
31305
+ if (onError) {
31306
+ onError(e);
31307
+ } else {
31308
+ console.error(e);
31051
31309
  }
31052
- mtlFileName = resource.fileName;
31053
- mtlUrl = resource.url;
31054
- continue;
31310
+ scope.manager.itemError(url);
31311
+ }
31312
+ }, onProgress, onError);
31313
+ }
31314
+ /**
31315
+ * Sets a property name mapping that maps default property names
31316
+ * to custom ones. For example, the following maps the properties
31317
+ * “diffuse_(red|green|blue)” in the file to standard color names.
31318
+ *
31319
+ * ```js
31320
+ * loader.setPropertyNameMapping( {
31321
+ * diffuse_red: 'red',
31322
+ * diffuse_green: 'green',
31323
+ * diffuse_blue: 'blue'
31324
+ * } );
31325
+ * ```
31326
+ *
31327
+ * @param {Object} mapping - The mapping dictionary.
31328
+ */
31329
+ setPropertyNameMapping(mapping) {
31330
+ this.propertyNameMapping = mapping;
31331
+ }
31332
+ /**
31333
+ * Custom properties outside of the defaults for position, uv, normal
31334
+ * and color attributes can be added using the setCustomPropertyNameMapping method.
31335
+ * For example, the following maps the element properties “custom_property_a”
31336
+ * and “custom_property_b” to an attribute “customAttribute” with an item size of 2.
31337
+ * Attribute item sizes are set from the number of element properties in the property array.
31338
+ *
31339
+ * ```js
31340
+ * loader.setCustomPropertyNameMapping( {
31341
+ * customAttribute: ['custom_property_a', 'custom_property_b'],
31342
+ * } );
31343
+ * ```
31344
+ * @param {Object} mapping - The mapping dictionary.
31345
+ */
31346
+ setCustomPropertyNameMapping(mapping) {
31347
+ this.customPropertyMapping = mapping;
31348
+ }
31349
+ /**
31350
+ * Parses the given PLY data and returns the resulting geometry.
31351
+ *
31352
+ * @param {ArrayBuffer} data - The raw PLY data as an array buffer.
31353
+ * @return {BufferGeometry} The parsed geometry.
31354
+ */
31355
+ parse(data) {
31356
+ function parseHeader(data2, headerLength = 0) {
31357
+ const patternHeader = /^ply([\s\S]*)end_header(\r\n|\r|\n)/;
31358
+ let headerText = "";
31359
+ const result = patternHeader.exec(data2);
31360
+ if (result !== null) {
31361
+ headerText = result[1];
31362
+ }
31363
+ const header = {
31364
+ comments: [],
31365
+ elements: [],
31366
+ headerLength,
31367
+ objInfo: ""
31368
+ };
31369
+ const lines = headerText.split(/\r\n|\r|\n/);
31370
+ let currentElement;
31371
+ function make_ply_element_property(propertyValues, propertyNameMapping) {
31372
+ const property = { type: propertyValues[0] };
31373
+ if (property.type === "list") {
31374
+ property.name = propertyValues[3];
31375
+ property.countType = propertyValues[1];
31376
+ property.itemType = propertyValues[2];
31377
+ } else {
31378
+ property.name = propertyValues[1];
31379
+ }
31380
+ if (property.name in propertyNameMapping) {
31381
+ property.name = propertyNameMapping[property.name];
31382
+ }
31383
+ return property;
31384
+ }
31385
+ for (let i = 0; i < lines.length; i++) {
31386
+ let line = lines[i];
31387
+ line = line.trim();
31388
+ if (line === "") continue;
31389
+ const lineValues = line.split(/\s+/);
31390
+ const lineType = lineValues.shift();
31391
+ line = lineValues.join(" ");
31392
+ switch (lineType) {
31393
+ case "format":
31394
+ header.format = lineValues[0];
31395
+ header.version = lineValues[1];
31396
+ break;
31397
+ case "comment":
31398
+ header.comments.push(line);
31399
+ break;
31400
+ case "element":
31401
+ if (currentElement !== void 0) {
31402
+ header.elements.push(currentElement);
31403
+ }
31404
+ currentElement = {};
31405
+ currentElement.name = lineValues[0];
31406
+ currentElement.count = parseInt(lineValues[1]);
31407
+ currentElement.properties = [];
31408
+ break;
31409
+ case "property":
31410
+ currentElement.properties.push(make_ply_element_property(lineValues, scope.propertyNameMapping));
31411
+ break;
31412
+ case "obj_info":
31413
+ header.objInfo = line;
31414
+ break;
31415
+ default:
31416
+ console.log("unhandled", lineType, lineValues);
31417
+ }
31418
+ }
31419
+ if (currentElement !== void 0) {
31420
+ header.elements.push(currentElement);
31421
+ }
31422
+ return header;
31423
+ }
31424
+ function parseASCIINumber(n, type) {
31425
+ switch (type) {
31426
+ case "char":
31427
+ case "uchar":
31428
+ case "short":
31429
+ case "ushort":
31430
+ case "int":
31431
+ case "uint":
31432
+ case "int8":
31433
+ case "uint8":
31434
+ case "int16":
31435
+ case "uint16":
31436
+ case "int32":
31437
+ case "uint32":
31438
+ return parseInt(n);
31439
+ case "float":
31440
+ case "double":
31441
+ case "float32":
31442
+ case "float64":
31443
+ return parseFloat(n);
31444
+ }
31445
+ }
31446
+ function parseASCIIElement(properties, tokens) {
31447
+ const element = {};
31448
+ for (let i = 0; i < properties.length; i++) {
31449
+ if (tokens.empty()) return null;
31450
+ if (properties[i].type === "list") {
31451
+ const list = [];
31452
+ const n = parseASCIINumber(tokens.next(), properties[i].countType);
31453
+ for (let j = 0; j < n; j++) {
31454
+ if (tokens.empty()) return null;
31455
+ list.push(parseASCIINumber(tokens.next(), properties[i].itemType));
31456
+ }
31457
+ element[properties[i].name] = list;
31458
+ } else {
31459
+ element[properties[i].name] = parseASCIINumber(tokens.next(), properties[i].type);
31460
+ }
31461
+ }
31462
+ return element;
31463
+ }
31464
+ function createBuffer() {
31465
+ const buffer = {
31466
+ indices: [],
31467
+ vertices: [],
31468
+ normals: [],
31469
+ uvs: [],
31470
+ faceVertexUvs: [],
31471
+ colors: [],
31472
+ faceVertexColors: [],
31473
+ descriptors: {}
31474
+ };
31475
+ for (const customProperty of Object.keys(scope.customPropertyMapping)) {
31476
+ buffer[customProperty] = [];
31477
+ }
31478
+ return buffer;
31479
+ }
31480
+ function getBufferAttributeClass(type) {
31481
+ switch (type) {
31482
+ case "int8":
31483
+ case "char":
31484
+ return Int8BufferAttribute;
31485
+ case "uint8":
31486
+ case "uchar":
31487
+ return Uint8BufferAttribute;
31488
+ case "int16":
31489
+ case "short":
31490
+ return Int16BufferAttribute;
31491
+ case "uint16":
31492
+ case "ushort":
31493
+ return Uint16BufferAttribute;
31494
+ case "int32":
31495
+ case "int":
31496
+ return Int32BufferAttribute;
31497
+ case "uint32":
31498
+ case "uint":
31499
+ return Uint32BufferAttribute;
31500
+ case "float32":
31501
+ case "float":
31502
+ return Float32BufferAttribute;
31503
+ case "float64":
31504
+ case "double":
31505
+ return Float64BufferAttribute;
31506
+ }
31507
+ }
31508
+ function getColorScale(type) {
31509
+ switch (type) {
31510
+ case "uchar":
31511
+ case "uint8":
31512
+ return 1 / 255;
31513
+ case "ushort":
31514
+ case "uint16":
31515
+ return 1 / 65535;
31516
+ case "float":
31517
+ case "float32":
31518
+ case "double":
31519
+ case "float64":
31520
+ return 1;
31521
+ default:
31522
+ return 1 / 255;
31055
31523
  }
31056
- this.textureUrls.add(resource.url);
31057
- this.textureUrlsByFileName.set(resource.fileName, resource.url);
31058
31524
  }
31059
- if (objUrl === void 0) {
31060
- throw new Error("The OBJ resource list must contain an OBJ file.");
31525
+ function isFloatType(type) {
31526
+ return type === "float" || type === "float32" || type === "double" || type === "float64";
31061
31527
  }
31062
- this.mtlFileName = mtlFileName;
31063
- this.mtlUrl = mtlUrl;
31064
- this.objUrl = objUrl;
31528
+ function getAttributeDescriptor(properties) {
31529
+ function findProperty(names) {
31530
+ for (const name of names) {
31531
+ const property = properties.find((p) => p.name === name);
31532
+ if (property) return property;
31533
+ }
31534
+ return null;
31535
+ }
31536
+ const x = findProperty(["x", "px", "posx"]);
31537
+ const y = findProperty(["y", "py", "posy"]);
31538
+ const z = findProperty(["z", "pz", "posz"]);
31539
+ const nx = findProperty(["nx", "normalx"]);
31540
+ const ny = findProperty(["ny", "normaly"]);
31541
+ const nz = findProperty(["nz", "normalz"]);
31542
+ const s = findProperty(["s", "u", "texture_u", "tx"]);
31543
+ const t = findProperty(["t", "v", "texture_v", "ty"]);
31544
+ const r = findProperty(["red", "diffuse_red", "r", "diffuse_r"]);
31545
+ const g = findProperty(["green", "diffuse_green", "g", "diffuse_g"]);
31546
+ const b = findProperty(["blue", "diffuse_blue", "b", "diffuse_b"]);
31547
+ const texcoord = findProperty(["texcoord"]);
31548
+ const custom = {};
31549
+ for (const customAttr of Object.keys(scope.customPropertyMapping)) {
31550
+ const propNames = scope.customPropertyMapping[customAttr];
31551
+ const matched = propNames.map((name) => properties.find((p) => p.name === name));
31552
+ const types = matched.filter((p) => p).map((p) => p.type);
31553
+ const uniform = types.length > 0 && types.every((type) => type === types[0]);
31554
+ custom[customAttr] = {
31555
+ type: uniform ? types[0] : "float32",
31556
+ usage: matched.every((p) => p !== void 0)
31557
+ };
31558
+ }
31559
+ return {
31560
+ position: {
31561
+ names: [x ? x.name : "x", y ? y.name : "y", z ? z.name : "z"],
31562
+ type: x ? x.type : "float32",
31563
+ usage: !!(x && y && z)
31564
+ },
31565
+ normal: {
31566
+ names: [nx ? nx.name : "nx", ny ? ny.name : "ny", nz ? nz.name : "nz"],
31567
+ type: nx ? nx.type : "float32",
31568
+ usage: !!(nx && ny && nz)
31569
+ },
31570
+ uv: {
31571
+ names: [s ? s.name : "s", t ? t.name : "t"],
31572
+ type: s ? s.type : "float32",
31573
+ usage: !!(s && t)
31574
+ },
31575
+ texcoord: {
31576
+ type: texcoord ? texcoord.itemType : "float32",
31577
+ usage: !!texcoord
31578
+ },
31579
+ color: {
31580
+ names: [r ? r.name : "red", g ? g.name : "green", b ? b.name : "blue"],
31581
+ type: r ? r.type : "uchar",
31582
+ usage: !!(r && g && b)
31583
+ },
31584
+ custom
31585
+ };
31586
+ }
31587
+ function parseASCII(data2, header) {
31588
+ const buffer = createBuffer();
31589
+ const patternBody = /end_header\s+(\S[\s\S]*\S|\S)\s*$/;
31590
+ let body, matches;
31591
+ if ((matches = patternBody.exec(data2)) !== null) {
31592
+ body = matches[1].split(/\s+/);
31593
+ } else {
31594
+ body = [];
31595
+ }
31596
+ const tokens = new ArrayStream(body);
31597
+ loop: for (let i = 0; i < header.elements.length; i++) {
31598
+ const elementDesc = header.elements[i];
31599
+ const attributeDescriptor = getAttributeDescriptor(elementDesc.properties);
31600
+ buffer.descriptors[elementDesc.name] = attributeDescriptor;
31601
+ for (let j = 0; j < elementDesc.count; j++) {
31602
+ const element = parseASCIIElement(elementDesc.properties, tokens);
31603
+ if (!element) break loop;
31604
+ handleElement(buffer, elementDesc.name, element, attributeDescriptor);
31605
+ }
31606
+ }
31607
+ return postProcess(buffer);
31608
+ }
31609
+ function postProcess(buffer) {
31610
+ let geometry2 = new BufferGeometry();
31611
+ const vertexDescriptor = buffer.descriptors.vertex;
31612
+ if (buffer.indices.length > 0) {
31613
+ geometry2.setIndex(buffer.indices);
31614
+ }
31615
+ const PositionClass = getBufferAttributeClass(vertexDescriptor ? vertexDescriptor.position.type : "float32");
31616
+ geometry2.setAttribute("position", new PositionClass(buffer.vertices, 3));
31617
+ if (buffer.normals.length > 0) {
31618
+ const NormalClass = getBufferAttributeClass(vertexDescriptor.normal.type);
31619
+ geometry2.setAttribute("normal", new NormalClass(buffer.normals, 3));
31620
+ }
31621
+ if (buffer.uvs.length > 0) {
31622
+ const UvClass = getBufferAttributeClass(vertexDescriptor.uv.type);
31623
+ geometry2.setAttribute("uv", new UvClass(buffer.uvs, 2));
31624
+ }
31625
+ if (buffer.colors.length > 0) {
31626
+ const colorType = vertexDescriptor.color.type;
31627
+ const normalized = !isFloatType(colorType);
31628
+ const ColorClass = getBufferAttributeClass(colorType);
31629
+ geometry2.setAttribute("color", new ColorClass(buffer.colors, 3, normalized));
31630
+ }
31631
+ if (buffer.faceVertexUvs.length > 0 || buffer.faceVertexColors.length > 0) {
31632
+ geometry2 = geometry2.toNonIndexed();
31633
+ if (buffer.faceVertexUvs.length > 0) {
31634
+ const UvClass = getBufferAttributeClass(buffer.descriptors.face.texcoord.type);
31635
+ geometry2.setAttribute("uv", new UvClass(buffer.faceVertexUvs, 2));
31636
+ }
31637
+ if (buffer.faceVertexColors.length > 0) {
31638
+ const colorType = buffer.descriptors.face.color.type;
31639
+ const normalized = !isFloatType(colorType);
31640
+ const ColorClass = getBufferAttributeClass(colorType);
31641
+ geometry2.setAttribute("color", new ColorClass(buffer.faceVertexColors, 3, normalized));
31642
+ }
31643
+ }
31644
+ for (const customProperty of Object.keys(scope.customPropertyMapping)) {
31645
+ if (buffer[customProperty].length > 0) {
31646
+ const CustomClass = getBufferAttributeClass(vertexDescriptor.custom[customProperty].type);
31647
+ geometry2.setAttribute(customProperty, new CustomClass(buffer[customProperty], scope.customPropertyMapping[customProperty].length));
31648
+ }
31649
+ }
31650
+ geometry2.computeBoundingSphere();
31651
+ return geometry2;
31652
+ }
31653
+ function handleElement(buffer, elementName, element, attributeDescriptor) {
31654
+ if (elementName === "vertex") {
31655
+ const { position, normal, uv, color } = attributeDescriptor;
31656
+ if (position.usage) {
31657
+ buffer.vertices.push(
31658
+ element[position.names[0]],
31659
+ element[position.names[1]],
31660
+ element[position.names[2]]
31661
+ );
31662
+ }
31663
+ if (normal.usage) {
31664
+ buffer.normals.push(
31665
+ element[normal.names[0]],
31666
+ element[normal.names[1]],
31667
+ element[normal.names[2]]
31668
+ );
31669
+ }
31670
+ if (uv.usage) {
31671
+ buffer.uvs.push(
31672
+ element[uv.names[0]],
31673
+ element[uv.names[1]]
31674
+ );
31675
+ }
31676
+ if (color.usage) {
31677
+ const scale = getColorScale(color.type);
31678
+ const isFloat = isFloatType(color.type);
31679
+ _color.setRGB(
31680
+ element[color.names[0]] * scale,
31681
+ element[color.names[1]] * scale,
31682
+ element[color.names[2]] * scale,
31683
+ SRGBColorSpace
31684
+ );
31685
+ const invScale = 1 / scale;
31686
+ buffer.colors.push(
31687
+ isFloat ? _color.r : Math.round(_color.r * invScale),
31688
+ isFloat ? _color.g : Math.round(_color.g * invScale),
31689
+ isFloat ? _color.b : Math.round(_color.b * invScale)
31690
+ );
31691
+ }
31692
+ for (const customProperty of Object.keys(scope.customPropertyMapping)) {
31693
+ for (const elementProperty of scope.customPropertyMapping[customProperty]) {
31694
+ buffer[customProperty].push(element[elementProperty]);
31695
+ }
31696
+ }
31697
+ } else if (elementName === "face") {
31698
+ const vertex_indices = element.vertex_indices || element.vertex_index;
31699
+ const texcoord = element.texcoord;
31700
+ if (vertex_indices.length === 3) {
31701
+ buffer.indices.push(vertex_indices[0], vertex_indices[1], vertex_indices[2]);
31702
+ if (texcoord && texcoord.length === 6) {
31703
+ buffer.faceVertexUvs.push(texcoord[0], texcoord[1]);
31704
+ buffer.faceVertexUvs.push(texcoord[2], texcoord[3]);
31705
+ buffer.faceVertexUvs.push(texcoord[4], texcoord[5]);
31706
+ }
31707
+ } else if (vertex_indices.length === 4) {
31708
+ buffer.indices.push(vertex_indices[0], vertex_indices[1], vertex_indices[3]);
31709
+ buffer.indices.push(vertex_indices[1], vertex_indices[2], vertex_indices[3]);
31710
+ }
31711
+ const { color } = attributeDescriptor;
31712
+ if (color.usage) {
31713
+ const scale = getColorScale(color.type);
31714
+ _color.setRGB(
31715
+ element[color.names[0]] * scale,
31716
+ element[color.names[1]] * scale,
31717
+ element[color.names[2]] * scale,
31718
+ SRGBColorSpace
31719
+ );
31720
+ const invScale = 1 / scale;
31721
+ const r = _color.r * invScale;
31722
+ const g = _color.g * invScale;
31723
+ const b = _color.b * invScale;
31724
+ buffer.faceVertexColors.push(r, g, b);
31725
+ buffer.faceVertexColors.push(r, g, b);
31726
+ buffer.faceVertexColors.push(r, g, b);
31727
+ }
31728
+ }
31729
+ }
31730
+ function binaryReadElement(at, properties) {
31731
+ const element = {};
31732
+ let read = 0;
31733
+ for (let i = 0; i < properties.length; i++) {
31734
+ const property = properties[i];
31735
+ const valueReader = property.valueReader;
31736
+ if (property.type === "list") {
31737
+ const list = [];
31738
+ const n = property.countReader.read(at + read);
31739
+ read += property.countReader.size;
31740
+ for (let j = 0; j < n; j++) {
31741
+ list.push(valueReader.read(at + read));
31742
+ read += valueReader.size;
31743
+ }
31744
+ element[property.name] = list;
31745
+ } else {
31746
+ element[property.name] = valueReader.read(at + read);
31747
+ read += valueReader.size;
31748
+ }
31749
+ }
31750
+ return [element, read];
31751
+ }
31752
+ function setPropertyBinaryReaders(properties, body, little_endian) {
31753
+ function getBinaryReader(dataview, type, little_endian2) {
31754
+ switch (type) {
31755
+ // correspondences for non-specific length types here match rply:
31756
+ case "int8":
31757
+ case "char":
31758
+ return { read: (at) => {
31759
+ return dataview.getInt8(at);
31760
+ }, size: 1 };
31761
+ case "uint8":
31762
+ case "uchar":
31763
+ return { read: (at) => {
31764
+ return dataview.getUint8(at);
31765
+ }, size: 1 };
31766
+ case "int16":
31767
+ case "short":
31768
+ return { read: (at) => {
31769
+ return dataview.getInt16(at, little_endian2);
31770
+ }, size: 2 };
31771
+ case "uint16":
31772
+ case "ushort":
31773
+ return { read: (at) => {
31774
+ return dataview.getUint16(at, little_endian2);
31775
+ }, size: 2 };
31776
+ case "int32":
31777
+ case "int":
31778
+ return { read: (at) => {
31779
+ return dataview.getInt32(at, little_endian2);
31780
+ }, size: 4 };
31781
+ case "uint32":
31782
+ case "uint":
31783
+ return { read: (at) => {
31784
+ return dataview.getUint32(at, little_endian2);
31785
+ }, size: 4 };
31786
+ case "float32":
31787
+ case "float":
31788
+ return { read: (at) => {
31789
+ return dataview.getFloat32(at, little_endian2);
31790
+ }, size: 4 };
31791
+ case "float64":
31792
+ case "double":
31793
+ return { read: (at) => {
31794
+ return dataview.getFloat64(at, little_endian2);
31795
+ }, size: 8 };
31796
+ }
31797
+ }
31798
+ for (let i = 0, l = properties.length; i < l; i++) {
31799
+ const property = properties[i];
31800
+ if (property.type === "list") {
31801
+ property.countReader = getBinaryReader(body, property.countType, little_endian);
31802
+ property.valueReader = getBinaryReader(body, property.itemType, little_endian);
31803
+ } else {
31804
+ property.valueReader = getBinaryReader(body, property.type, little_endian);
31805
+ }
31806
+ }
31807
+ }
31808
+ function parseBinary(data2, header) {
31809
+ const buffer = createBuffer();
31810
+ const little_endian = header.format === "binary_little_endian";
31811
+ const body = new DataView(data2, header.headerLength);
31812
+ let result, loc = 0;
31813
+ for (let currentElement = 0; currentElement < header.elements.length; currentElement++) {
31814
+ const elementDesc = header.elements[currentElement];
31815
+ const properties = elementDesc.properties;
31816
+ const attributeDescriptor = getAttributeDescriptor(properties);
31817
+ buffer.descriptors[elementDesc.name] = attributeDescriptor;
31818
+ setPropertyBinaryReaders(properties, body, little_endian);
31819
+ for (let currentElementCount = 0; currentElementCount < elementDesc.count; currentElementCount++) {
31820
+ result = binaryReadElement(loc, properties);
31821
+ loc += result[1];
31822
+ const element = result[0];
31823
+ handleElement(buffer, elementDesc.name, element, attributeDescriptor);
31824
+ }
31825
+ }
31826
+ return postProcess(buffer);
31827
+ }
31828
+ function extractHeaderText(bytes) {
31829
+ let i = 0;
31830
+ let cont = true;
31831
+ let line = "";
31832
+ const lines = [];
31833
+ const startLine = new TextDecoder().decode(bytes.subarray(0, 5));
31834
+ const hasCRNL = /^ply\r\n/.test(startLine);
31835
+ do {
31836
+ const c = String.fromCharCode(bytes[i++]);
31837
+ if (c !== "\n" && c !== "\r") {
31838
+ line += c;
31839
+ } else {
31840
+ if (line === "end_header") cont = false;
31841
+ if (line !== "") {
31842
+ lines.push(line);
31843
+ line = "";
31844
+ }
31845
+ }
31846
+ } while (cont && i < bytes.length);
31847
+ if (hasCRNL === true) i++;
31848
+ return { headerText: lines.join("\r") + "\r", headerLength: i };
31849
+ }
31850
+ let geometry;
31851
+ const scope = this;
31852
+ if (data instanceof ArrayBuffer) {
31853
+ const bytes = new Uint8Array(data);
31854
+ const { headerText, headerLength } = extractHeaderText(bytes);
31855
+ const header = parseHeader(headerText, headerLength);
31856
+ if (header.format === "ascii") {
31857
+ const text = new TextDecoder().decode(bytes);
31858
+ geometry = parseASCII(text, header);
31859
+ } else {
31860
+ geometry = parseBinary(data, header);
31861
+ }
31862
+ } else {
31863
+ geometry = parseASCII(data, parseHeader(data));
31864
+ }
31865
+ return geometry;
31065
31866
  }
31066
- assertMaterialLibraries(materialReferences) {
31067
- if (materialReferences.length === 0) {
31867
+ }
31868
+ class Float64BufferAttribute extends BufferAttribute {
31869
+ constructor(array, itemSize, normalized) {
31870
+ super(new Float64Array(array), itemSize, normalized);
31871
+ }
31872
+ }
31873
+ class ArrayStream {
31874
+ constructor(arr) {
31875
+ this.arr = arr;
31876
+ this.i = 0;
31877
+ }
31878
+ empty() {
31879
+ return this.i >= this.arr.length;
31880
+ }
31881
+ next() {
31882
+ return this.arr[this.i++];
31883
+ }
31884
+ }
31885
+ class PlyTextureLoadOperation {
31886
+ abortListener;
31887
+ loader;
31888
+ signal;
31889
+ url;
31890
+ resolveResult;
31891
+ settled = false;
31892
+ texture;
31893
+ constructor(loader, url, signal) {
31894
+ this.abortListener = this.handleAbort.bind(this);
31895
+ this.loader = loader;
31896
+ this.signal = signal;
31897
+ this.url = url;
31898
+ }
31899
+ start() {
31900
+ return new Promise(this.begin.bind(this));
31901
+ }
31902
+ begin(resolve) {
31903
+ this.resolveResult = resolve;
31904
+ this.signal.addEventListener("abort", this.abortListener, { once: true });
31905
+ if (this.signal.aborted) {
31906
+ this.handleAbort();
31068
31907
  return;
31069
31908
  }
31070
- const mtlUrl = this.mtlUrl;
31071
- const mtlFileName = this.mtlFileName;
31072
- const [materialReference, ...remainingReferences] = materialReferences;
31073
- if (materialReference === void 0 || remainingReferences.length > 0 || mtlUrl === void 0 || mtlFileName === void 0) {
31074
- throw new Error("The OBJ material library is not present in the resource list.");
31909
+ try {
31910
+ const requestedTexture = this.loader.load(
31911
+ this.url,
31912
+ this.handleLoad.bind(this),
31913
+ void 0,
31914
+ this.handleError.bind(this)
31915
+ );
31916
+ if (this.settled) {
31917
+ if (this.texture !== requestedTexture) {
31918
+ requestedTexture.dispose();
31919
+ }
31920
+ return;
31921
+ }
31922
+ this.texture = requestedTexture;
31923
+ } catch {
31924
+ this.handleError();
31075
31925
  }
31076
- const referenceUrl = new URL(materialReference, this.objUrl).href;
31077
- if (referenceUrl === mtlUrl || this.getFileName(new URL(referenceUrl)) === mtlFileName) {
31926
+ }
31927
+ finish(result) {
31928
+ if (this.settled) {
31078
31929
  return;
31079
31930
  }
31080
- throw new Error("The OBJ material library does not match the resource list.");
31931
+ this.settled = true;
31932
+ this.signal.removeEventListener("abort", this.abortListener);
31933
+ this.resolveResult?.(result);
31934
+ this.resolveResult = void 0;
31081
31935
  }
31082
- resolveTextureUrl(materialBaseUrl, textureUrl) {
31083
- const resolvedUrl = new URL(textureUrl, materialBaseUrl).href;
31084
- if (this.textureUrls.has(resolvedUrl)) {
31085
- return resolvedUrl;
31936
+ handleAbort() {
31937
+ this.texture?.dispose();
31938
+ this.texture = void 0;
31939
+ this.finish({ outcome: "cancelled" });
31940
+ }
31941
+ handleError() {
31942
+ this.texture?.dispose();
31943
+ this.texture = void 0;
31944
+ this.finish({ outcome: "failed" });
31945
+ }
31946
+ handleLoad(texture) {
31947
+ if (this.settled) {
31948
+ texture.dispose();
31949
+ return;
31086
31950
  }
31087
- const mappedUrl = this.textureUrlsByFileName.get(this.getFileName(new URL(resolvedUrl)));
31088
- if (mappedUrl === void 0) {
31089
- throw new Error("The MTL texture is not present in the resource list.");
31951
+ this.texture = texture;
31952
+ this.finish({
31953
+ outcome: "succeeded",
31954
+ texture
31955
+ });
31956
+ }
31957
+ }
31958
+ class PlyTextureLoader {
31959
+ loader;
31960
+ constructor(loader = new TextureLoader()) {
31961
+ this.loader = loader;
31962
+ }
31963
+ load(url, signal) {
31964
+ return new PlyTextureLoadOperation(this.loader, url, signal).start();
31965
+ }
31966
+ }
31967
+ const DEFAULT_MODEL_COLOR = 10265519;
31968
+ const DEFAULT_POINT_SIZE = 3;
31969
+ const MAX_HEADER_BYTES = 65536;
31970
+ class PlyLoader {
31971
+ parser;
31972
+ textureLoader;
31973
+ constructor(parser = new PLYLoader(), textureLoader = new PlyTextureLoader()) {
31974
+ this.parser = parser;
31975
+ this.textureLoader = textureLoader;
31976
+ }
31977
+ async load(resources, signal) {
31978
+ if (signal.aborted) {
31979
+ return {
31980
+ code: ObjImportFailureCode$1.Cancelled,
31981
+ success: false
31982
+ };
31090
31983
  }
31091
- return mappedUrl;
31984
+ const source = await this.fetchSource(resources.plyUrl, signal);
31985
+ if (!(source instanceof ArrayBuffer)) {
31986
+ return source;
31987
+ }
31988
+ let geometry;
31989
+ let metadata;
31990
+ try {
31991
+ metadata = this.parseMetadata(source);
31992
+ geometry = this.parser.parse(source);
31993
+ } catch {
31994
+ return {
31995
+ code: ObjImportFailureCode$1.InvalidSource,
31996
+ success: false
31997
+ };
31998
+ }
31999
+ if (this.isCancelled(signal)) {
32000
+ geometry.dispose();
32001
+ return {
32002
+ code: ObjImportFailureCode$1.Cancelled,
32003
+ success: false
32004
+ };
32005
+ }
32006
+ if (!geometry.hasAttribute("position")) {
32007
+ geometry.dispose();
32008
+ return {
32009
+ code: ObjImportFailureCode$1.InvalidSource,
32010
+ success: false
32011
+ };
32012
+ }
32013
+ const modelResult = metadata.hasFaces ? await this.createMeshModel(geometry, metadata.textureReference, resources, signal) : this.createPointsModel(geometry);
32014
+ if ("success" in modelResult) {
32015
+ return modelResult;
32016
+ }
32017
+ if (!modelResult.model.hasFiniteBounds()) {
32018
+ modelResult.model.dispose();
32019
+ return {
32020
+ code: ObjImportFailureCode$1.InvalidSource,
32021
+ success: false
32022
+ };
32023
+ }
32024
+ return {
32025
+ success: true,
32026
+ ...modelResult.warnings.length === 0 ? {} : { warnings: modelResult.warnings },
32027
+ model: modelResult.model
32028
+ };
31092
32029
  }
31093
- createResourceEntry(fileUrl) {
31094
- const url = new URL(fileUrl);
31095
- if (url.protocol !== "http:" && url.protocol !== "https:") {
31096
- throw new Error("The OBJ resource list only accepts HTTP URLs.");
32030
+ createMeshMaterial(geometry, texture) {
32031
+ if (texture !== void 0) {
32032
+ texture.colorSpace = SRGBColorSpace;
31097
32033
  }
32034
+ return new MeshStandardMaterial({
32035
+ color: texture !== void 0 || geometry.hasAttribute("color") ? 16777215 : DEFAULT_MODEL_COLOR,
32036
+ map: texture ?? null,
32037
+ side: DoubleSide,
32038
+ vertexColors: geometry.hasAttribute("color")
32039
+ });
32040
+ }
32041
+ async createMeshModel(geometry, textureReference, resources, signal) {
32042
+ if (!geometry.hasAttribute("normal")) {
32043
+ geometry.computeVertexNormals();
32044
+ }
32045
+ const warnings = [];
32046
+ let texture;
32047
+ if (textureReference !== void 0 && geometry.hasAttribute("uv")) {
32048
+ const textureResult = await this.textureLoader.load(
32049
+ resources.resolveTextureUrl(textureReference),
32050
+ signal
32051
+ );
32052
+ if (textureResult.outcome === "cancelled") {
32053
+ geometry.dispose();
32054
+ return {
32055
+ code: ObjImportFailureCode$1.Cancelled,
32056
+ success: false
32057
+ };
32058
+ }
32059
+ if (textureResult.outcome === "succeeded") {
32060
+ texture = textureResult.texture;
32061
+ } else {
32062
+ warnings.push({ code: ObjImportWarningCode$1.IncompleteMaterial });
32063
+ }
32064
+ } else if (textureReference !== void 0) {
32065
+ warnings.push({ code: ObjImportWarningCode$1.IncompleteMaterial });
32066
+ }
32067
+ if (signal.aborted) {
32068
+ texture?.dispose();
32069
+ geometry.dispose();
32070
+ return {
32071
+ code: ObjImportFailureCode$1.Cancelled,
32072
+ success: false
32073
+ };
32074
+ }
32075
+ const material = this.createMeshMaterial(geometry, texture);
31098
32076
  return {
31099
- fileName: this.getFileName(url),
31100
- url: url.href
32077
+ model: new Model(new Mesh(geometry, material)),
32078
+ warnings
31101
32079
  };
31102
32080
  }
31103
- getFileName(url) {
31104
- const fileName = decodeURIComponent(url.pathname.slice(url.pathname.lastIndexOf("/") + 1));
31105
- if (fileName.length === 0) {
31106
- throw new Error("The OBJ resource URL must include a file name.");
32081
+ createPointsModel(geometry) {
32082
+ const material = new PointsMaterial({
32083
+ color: geometry.hasAttribute("color") ? 16777215 : DEFAULT_MODEL_COLOR,
32084
+ size: DEFAULT_POINT_SIZE,
32085
+ sizeAttenuation: false,
32086
+ vertexColors: geometry.hasAttribute("color")
32087
+ });
32088
+ return {
32089
+ model: new Model(new Points(geometry, material)),
32090
+ warnings: []
32091
+ };
32092
+ }
32093
+ async fetchSource(url, signal) {
32094
+ try {
32095
+ const response = await fetch(url, { signal });
32096
+ if (!response.ok) {
32097
+ return {
32098
+ code: ObjImportFailureCode$1.InvalidSource,
32099
+ success: false
32100
+ };
32101
+ }
32102
+ return await response.arrayBuffer();
32103
+ } catch {
32104
+ return {
32105
+ code: signal.aborted ? ObjImportFailureCode$1.Cancelled : ObjImportFailureCode$1.InvalidSource,
32106
+ success: false
32107
+ };
31107
32108
  }
31108
- return fileName;
31109
32109
  }
31110
- isMtlUrl(url) {
31111
- return new URL(url).pathname.toLowerCase().endsWith(".mtl");
32110
+ parseMetadata(source) {
32111
+ const headerLength = Math.min(source.byteLength, MAX_HEADER_BYTES);
32112
+ const header = new TextDecoder().decode(new Uint8Array(source, 0, headerLength));
32113
+ const endHeaderMatch = /end_header(?:\r\n|\r|\n)/u.exec(header);
32114
+ if (endHeaderMatch === null) {
32115
+ throw new Error("The PLY header is incomplete.");
32116
+ }
32117
+ const headerSource = header.slice(0, endHeaderMatch.index);
32118
+ const faceCount = /^element\s+face\s+(\d+)\s*$/imu.exec(headerSource)?.[1];
32119
+ const rawTextureReference = /^comment\s+TextureFile\s+(.+?)\s*$/imu.exec(headerSource)?.[1];
32120
+ return {
32121
+ hasFaces: faceCount !== void 0 && Number.parseInt(faceCount, 10) > 0,
32122
+ textureReference: rawTextureReference === void 0 ? void 0 : this.removeSurroundingQuotes(rawTextureReference.trim())
32123
+ };
31112
32124
  }
31113
- isObjUrl(url) {
31114
- return new URL(url).pathname.toLowerCase().endsWith(".obj");
32125
+ isCancelled(signal) {
32126
+ return signal.aborted;
32127
+ }
32128
+ removeSurroundingQuotes(value) {
32129
+ const firstCharacter = value.at(0);
32130
+ const lastCharacter = value.at(-1);
32131
+ if (value.length >= 2 && (firstCharacter === '"' && lastCharacter === '"' || firstCharacter === "'" && lastCharacter === "'")) {
32132
+ return value.slice(1, -1);
32133
+ }
32134
+ return value;
31115
32135
  }
31116
32136
  }
31117
32137
  const DEFAULT_BACKGROUND_COLOR = 3355443;
@@ -31131,6 +32151,7 @@ class Renderer {
31131
32151
  frameHandle;
31132
32152
  hemisphereLight;
31133
32153
  modelLoader;
32154
+ plyLoader;
31134
32155
  previousScissor = new Vector4();
31135
32156
  previousViewport = new Vector4();
31136
32157
  renderer;
@@ -31172,6 +32193,7 @@ class Renderer {
31172
32193
  this.fillLight.position.set(4, 2, 3);
31173
32194
  this.scene.add(this.hemisphereLight, this.directionalLight, this.fillLight);
31174
32195
  this.modelLoader = new ObjLoader();
32196
+ this.plyLoader = new PlyLoader();
31175
32197
  controller = new CameraController(this.camera, this.requestRender.bind(this));
31176
32198
  this.controller = controller;
31177
32199
  viewCube = new ViewCube(this.requestRender.bind(this));
@@ -31285,6 +32307,30 @@ class Renderer {
31285
32307
  stone: source
31286
32308
  });
31287
32309
  }
32310
+ importModelResourceScene(source) {
32311
+ this.assertNotDisposed();
32312
+ if (this.contextLost) {
32313
+ return Promise.resolve({
32314
+ code: ObjImportFailureCode$1.RenderFailed,
32315
+ success: false
32316
+ });
32317
+ }
32318
+ try {
32319
+ const resources = {
32320
+ base: source.base === void 0 ? void 0 : new ModelResourceList(source.base.fileUrls),
32321
+ stone: source.stone === void 0 ? void 0 : new ModelResourceList(source.stone.fileUrls)
32322
+ };
32323
+ if (this.containsOnlyObjResources(resources)) {
32324
+ return this.importObjResourceScene(source);
32325
+ }
32326
+ return this.completeSceneImport(this.startImportSession(), resources);
32327
+ } catch {
32328
+ return Promise.resolve({
32329
+ code: ObjImportFailureCode$1.InvalidSource,
32330
+ success: false
32331
+ });
32332
+ }
32333
+ }
31288
32334
  importObjResources(source) {
31289
32335
  this.assertNotDisposed();
31290
32336
  if (this.contextLost) {
@@ -31378,6 +32424,12 @@ class Renderer {
31378
32424
  window.cancelAnimationFrame(this.frameHandle);
31379
32425
  this.frameHandle = void 0;
31380
32426
  }
32427
+ containsOnlyObjResources(source) {
32428
+ const resources = [source.stone, source.base];
32429
+ return resources.every(
32430
+ (resource) => resource === void 0 || resource instanceof ObjResourceList || resource.isObj()
32431
+ );
32432
+ }
31381
32433
  async completeSceneImport(session, source) {
31382
32434
  const stoneSource = source.stone;
31383
32435
  const baseSource = source.base;
@@ -31399,7 +32451,7 @@ class Renderer {
31399
32451
  }
31400
32452
  const secondCandidate = await this.loadModel(session, baseSource);
31401
32453
  if (this.isImportResult(secondCandidate)) {
31402
- this.releaseModels(candidateModels);
32454
+ this.releaseLoadedModels(candidateModels);
31403
32455
  return secondCandidate;
31404
32456
  }
31405
32457
  candidateModels.push(secondCandidate);
@@ -31415,7 +32467,14 @@ class Renderer {
31415
32467
  async loadModel(session, source) {
31416
32468
  let result;
31417
32469
  try {
31418
- result = source instanceof ObjResourceList ? await this.modelLoader.loadResources(source, session.controller.signal) : await this.modelLoader.load(source, session.controller.signal);
32470
+ if (source instanceof ModelResourceList) {
32471
+ result = source.resources instanceof ObjResourceList ? await this.modelLoader.loadResources(
32472
+ source.resources,
32473
+ session.controller.signal
32474
+ ) : await this.plyLoader.load(source.resources, session.controller.signal);
32475
+ } else {
32476
+ result = source instanceof ObjResourceList ? await this.modelLoader.loadResources(source, session.controller.signal) : await this.modelLoader.load(source, session.controller.signal);
32477
+ }
31419
32478
  } catch {
31420
32479
  return this.completeUnexpectedImportFailure(session);
31421
32480
  }
@@ -31450,7 +32509,10 @@ class Renderer {
31450
32509
  success: false
31451
32510
  };
31452
32511
  }
31453
- return result.model;
32512
+ return {
32513
+ model: result.model,
32514
+ warnings: result.warnings ?? []
32515
+ };
31454
32516
  }
31455
32517
  completeUnexpectedImportFailure(session) {
31456
32518
  const invalidationCode = session.invalidationCode;
@@ -31525,6 +32587,9 @@ class Renderer {
31525
32587
  throw errors[0];
31526
32588
  }
31527
32589
  }
32590
+ releaseLoadedModels(models) {
32591
+ this.releaseModels(models.map(({ model }) => model));
32592
+ }
31528
32593
  releaseImportedModel(result) {
31529
32594
  if (result.model !== void 0) {
31530
32595
  this.releaseModel(result.model);
@@ -31577,7 +32642,12 @@ class Renderer {
31577
32642
  this.renderer.setScissorTest(previousScissorTest);
31578
32643
  }
31579
32644
  }
31580
- replaceActiveModels(session, models) {
32645
+ replaceActiveModels(session, loadedModels) {
32646
+ const [firstLoadedModel, ...remainingLoadedModels] = loadedModels;
32647
+ const models = [
32648
+ firstLoadedModel.model,
32649
+ ...remainingLoadedModels.map(({ model }) => model)
32650
+ ];
31581
32651
  const invalidationCode = session.invalidationCode;
31582
32652
  if (invalidationCode !== void 0) {
31583
32653
  this.finishImportSession(session);
@@ -31607,10 +32677,16 @@ class Renderer {
31607
32677
  this.activeModels = [...models];
31608
32678
  this.releaseModels(previousModels);
31609
32679
  this.requestRender();
32680
+ const warnings = this.mergeWarnings(loadedModels);
31610
32681
  return {
31611
- success: true
32682
+ success: true,
32683
+ ...warnings === void 0 ? {} : { warnings }
31612
32684
  };
31613
32685
  }
32686
+ mergeWarnings(models) {
32687
+ const warnings = models.flatMap(({ warnings: modelWarnings }) => modelWarnings);
32688
+ return warnings.length > 0 ? warnings : void 0;
32689
+ }
31614
32690
  mergeBounds(models) {
31615
32691
  const [firstModel, ...remainingModels] = models;
31616
32692
  let minX = firstModel.bounds.min.x;
@@ -31694,6 +32770,10 @@ var ObjImportFailureCode = /* @__PURE__ */ ((ObjImportFailureCode2) => {
31694
32770
  ObjImportFailureCode2["Unavailable"] = "unavailable";
31695
32771
  return ObjImportFailureCode2;
31696
32772
  })(ObjImportFailureCode || {});
32773
+ var ObjImportWarningCode = /* @__PURE__ */ ((ObjImportWarningCode2) => {
32774
+ ObjImportWarningCode2["IncompleteMaterial"] = "incomplete-material";
32775
+ return ObjImportWarningCode2;
32776
+ })(ObjImportWarningCode || {});
31697
32777
  class Application {
31698
32778
  disposed = false;
31699
32779
  renderer;
@@ -31708,6 +32788,10 @@ class Application {
31708
32788
  this.assertNotDisposed();
31709
32789
  return this.mapAsyncObjImportResult(this.getRenderer().importObj(source));
31710
32790
  }
32791
+ importModelResourceScene(source) {
32792
+ this.assertNotDisposed();
32793
+ return this.mapAsyncObjImportResult(this.getRenderer().importModelResourceScene(source));
32794
+ }
31711
32795
  importObjResources(source) {
31712
32796
  this.assertNotDisposed();
31713
32797
  return this.mapAsyncObjImportResult(this.getRenderer().importObjResources(source));
@@ -31754,8 +32838,10 @@ class Application {
31754
32838
  }
31755
32839
  mapObjImportResult(result) {
31756
32840
  if (result.success) {
32841
+ const warnings = this.mapWarnings(result.warnings);
31757
32842
  return {
31758
- success: true
32843
+ success: true,
32844
+ ...warnings === void 0 ? {} : { warnings }
31759
32845
  };
31760
32846
  }
31761
32847
  switch (result.code) {
@@ -31781,8 +32867,17 @@ class Application {
31781
32867
  async mapAsyncObjImportResult(resultPromise) {
31782
32868
  return this.mapObjImportResult(await resultPromise);
31783
32869
  }
32870
+ mapWarnings(warnings) {
32871
+ if (warnings === void 0 || warnings.length === 0) {
32872
+ return void 0;
32873
+ }
32874
+ return warnings.map(() => ({
32875
+ code: ObjImportWarningCode.IncompleteMaterial
32876
+ }));
32877
+ }
31784
32878
  }
31785
32879
  export {
31786
32880
  Application,
31787
- ObjImportFailureCode
32881
+ ObjImportFailureCode,
32882
+ ObjImportWarningCode
31788
32883
  };