@lingbi/studio 0.2.0 → 0.3.1

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/Application.d.ts CHANGED
@@ -1,13 +1,18 @@
1
1
  import type { ObjImportResult } from './ObjImportResult';
2
2
  import type { ObjImportSource } from './ObjImportSource';
3
+ import type { ObjResourceListSource } from './ObjResourceListSource';
4
+ import type { ObjResourceSceneSource } from './ObjResourceSceneSource';
3
5
  import type { ObjSceneSource } from './ObjSceneSource';
4
6
  export declare class Application {
5
7
  private disposed;
6
8
  private renderer;
7
9
  attach(canvas: HTMLCanvasElement): void;
8
10
  importObj(source: ObjImportSource): Promise<ObjImportResult>;
11
+ importObjResources(source: ObjResourceListSource): Promise<ObjImportResult>;
12
+ importObjResourceScene(source: ObjResourceSceneSource): Promise<ObjImportResult>;
9
13
  importObjScene(source: ObjSceneSource): Promise<ObjImportResult>;
10
14
  resetView(): void;
15
+ takeScreenshot(): Promise<Blob>;
11
16
  dispose(): void;
12
17
  private assertNotDisposed;
13
18
  private getRenderer;
@@ -0,0 +1,3 @@
1
+ export interface ObjResourceListSource {
2
+ readonly fileUrls: readonly string[];
3
+ }
@@ -0,0 +1,5 @@
1
+ import type { ObjResourceListSource } from './ObjResourceListSource';
2
+ export interface ObjResourceSceneSource {
3
+ readonly stone?: ObjResourceListSource;
4
+ readonly base?: ObjResourceListSource;
5
+ }
package/index.d.ts CHANGED
@@ -2,4 +2,6 @@ export { Application } from './Application';
2
2
  export type { ObjImportResult } from './ObjImportResult';
3
3
  export { ObjImportFailureCode } from './ObjImportResult';
4
4
  export type { ObjImportSource } from './ObjImportSource';
5
+ export type { ObjResourceListSource } from './ObjResourceListSource';
6
+ export type { ObjResourceSceneSource } from './ObjResourceSceneSource';
5
7
  export type { ObjSceneSource } from './ObjSceneSource';
package/index.js CHANGED
@@ -13983,6 +13983,91 @@ class RawShaderMaterial extends ShaderMaterial {
13983
13983
  this.type = "RawShaderMaterial";
13984
13984
  }
13985
13985
  }
13986
+ class MeshStandardMaterial extends Material {
13987
+ /**
13988
+ * Constructs a new mesh standard material.
13989
+ *
13990
+ * @param {Object} [parameters] - An object with one or more properties
13991
+ * defining the material's appearance. Any property of the material
13992
+ * (including any property from inherited materials) can be passed
13993
+ * in here. Color values can be passed any type of value accepted
13994
+ * by {@link Color#set}.
13995
+ */
13996
+ constructor(parameters) {
13997
+ super();
13998
+ this.isMeshStandardMaterial = true;
13999
+ this.type = "MeshStandardMaterial";
14000
+ this.defines = { "STANDARD": "" };
14001
+ this.color = new Color(16777215);
14002
+ this.roughness = 1;
14003
+ this.metalness = 0;
14004
+ this.map = null;
14005
+ this.lightMap = null;
14006
+ this.lightMapIntensity = 1;
14007
+ this.aoMap = null;
14008
+ this.aoMapIntensity = 1;
14009
+ this.emissive = new Color(0);
14010
+ this.emissiveIntensity = 1;
14011
+ this.emissiveMap = null;
14012
+ this.bumpMap = null;
14013
+ this.bumpScale = 1;
14014
+ this.normalMap = null;
14015
+ this.normalMapType = TangentSpaceNormalMap;
14016
+ this.normalScale = new Vector2(1, 1);
14017
+ this.displacementMap = null;
14018
+ this.displacementScale = 1;
14019
+ this.displacementBias = 0;
14020
+ this.roughnessMap = null;
14021
+ this.metalnessMap = null;
14022
+ this.alphaMap = null;
14023
+ this.envMap = null;
14024
+ this.envMapRotation = new Euler();
14025
+ this.envMapIntensity = 1;
14026
+ this.wireframe = false;
14027
+ this.wireframeLinewidth = 1;
14028
+ this.wireframeLinecap = "round";
14029
+ this.wireframeLinejoin = "round";
14030
+ this.flatShading = false;
14031
+ this.fog = true;
14032
+ this.setValues(parameters);
14033
+ }
14034
+ copy(source) {
14035
+ super.copy(source);
14036
+ this.defines = { "STANDARD": "" };
14037
+ this.color.copy(source.color);
14038
+ this.roughness = source.roughness;
14039
+ this.metalness = source.metalness;
14040
+ this.map = source.map;
14041
+ this.lightMap = source.lightMap;
14042
+ this.lightMapIntensity = source.lightMapIntensity;
14043
+ this.aoMap = source.aoMap;
14044
+ this.aoMapIntensity = source.aoMapIntensity;
14045
+ this.emissive.copy(source.emissive);
14046
+ this.emissiveMap = source.emissiveMap;
14047
+ this.emissiveIntensity = source.emissiveIntensity;
14048
+ this.bumpMap = source.bumpMap;
14049
+ this.bumpScale = source.bumpScale;
14050
+ this.normalMap = source.normalMap;
14051
+ this.normalMapType = source.normalMapType;
14052
+ this.normalScale.copy(source.normalScale);
14053
+ this.displacementMap = source.displacementMap;
14054
+ this.displacementScale = source.displacementScale;
14055
+ this.displacementBias = source.displacementBias;
14056
+ this.roughnessMap = source.roughnessMap;
14057
+ this.metalnessMap = source.metalnessMap;
14058
+ this.alphaMap = source.alphaMap;
14059
+ this.envMap = source.envMap;
14060
+ this.envMapRotation.copy(source.envMapRotation);
14061
+ this.envMapIntensity = source.envMapIntensity;
14062
+ this.wireframe = source.wireframe;
14063
+ this.wireframeLinewidth = source.wireframeLinewidth;
14064
+ this.wireframeLinecap = source.wireframeLinecap;
14065
+ this.wireframeLinejoin = source.wireframeLinejoin;
14066
+ this.flatShading = source.flatShading;
14067
+ this.fog = source.fog;
14068
+ return this;
14069
+ }
14070
+ }
13986
14071
  class MeshPhongMaterial extends Material {
13987
14072
  /**
13988
14073
  * Constructs a new mesh phong material.
@@ -30474,6 +30559,12 @@ class ObjLoader {
30474
30559
  this.parser = parser;
30475
30560
  }
30476
30561
  async load(source, signal) {
30562
+ return this.loadInternal(source, signal);
30563
+ }
30564
+ async loadResources(resources, signal) {
30565
+ return this.loadInternal(this.createResourceSource(resources), signal, resources);
30566
+ }
30567
+ async loadInternal(source, signal, resources) {
30477
30568
  if (this.hasEmptyRequiredUrl(source)) {
30478
30569
  return {
30479
30570
  code: ObjImportFailureCode$1.EmptySource,
@@ -30485,12 +30576,15 @@ class ObjLoader {
30485
30576
  try {
30486
30577
  const objSource = await this.loadText(source.objUrl, signal);
30487
30578
  this.assertNotCancelled(signal);
30488
- const materialUrl = this.getMaterialUrl(source, objSource);
30579
+ const materialReferences = this.getMaterialLibraryReferences(objSource);
30580
+ resources?.assertMaterialLibraries(materialReferences);
30581
+ const materialUrl = resources === void 0 ? this.getMaterialUrl(source, materialReferences) : resources.mtlUrl;
30489
30582
  if (materialUrl !== void 0) {
30490
30583
  materialContext = await this.loadMaterialContext(
30491
30584
  materialUrl,
30492
30585
  source.textureUrls,
30493
- signal
30586
+ signal,
30587
+ resources
30494
30588
  );
30495
30589
  }
30496
30590
  this.assertNotCancelled(signal);
@@ -30538,6 +30632,11 @@ class ObjLoader {
30538
30632
  throw new Error("The OBJ import was cancelled.");
30539
30633
  }
30540
30634
  }
30635
+ assertMaterialTextures(resources, materialSource, materialBaseUrl, creator) {
30636
+ for (const textureReference of this.getMaterialTextureReferences(materialSource, creator)) {
30637
+ resources.resolveTextureUrl(materialBaseUrl, textureReference);
30638
+ }
30639
+ }
30541
30640
  collectMaterials(material, materials) {
30542
30641
  if (Array.isArray(material)) {
30543
30642
  for (const item of material) {
@@ -30547,6 +30646,17 @@ class ObjLoader {
30547
30646
  }
30548
30647
  materials.add(material);
30549
30648
  }
30649
+ createResourceSource(resources) {
30650
+ if (resources.mtlUrl === void 0) {
30651
+ return {
30652
+ objUrl: resources.objUrl
30653
+ };
30654
+ }
30655
+ return {
30656
+ mtlUrl: resources.mtlUrl,
30657
+ objUrl: resources.objUrl
30658
+ };
30659
+ }
30550
30660
  disposeFailedModel(model) {
30551
30661
  if (model === void 0) {
30552
30662
  return;
@@ -30570,6 +30680,11 @@ class ObjLoader {
30570
30680
  const resourceOwner = new Model(new Group(), [...materials]);
30571
30681
  resourceOwner.dispose();
30572
30682
  }
30683
+ disposeMaterialsWithoutTextures(materials) {
30684
+ for (const material of materials) {
30685
+ material.dispose();
30686
+ }
30687
+ }
30573
30688
  getMaterialArray(context) {
30574
30689
  if (context === void 0) {
30575
30690
  return [];
@@ -30592,20 +30707,33 @@ class ObjLoader {
30592
30707
  getTextureFailureCode(outcome) {
30593
30708
  return outcome === "cancelled" ? ObjImportFailureCode$1.Cancelled : ObjImportFailureCode$1.InvalidSource;
30594
30709
  }
30595
- getMaterialLibraryReference(objSource) {
30710
+ getMaterialLibraryReferences(objSource) {
30711
+ const materialReferences = [];
30596
30712
  for (const line of objSource.split(/\r?\n/u)) {
30597
30713
  const materialReference = /^\s*mtllib\s+(.+?)\s*$/u.exec(line)?.[1];
30598
30714
  if (materialReference !== void 0) {
30599
- return materialReference;
30715
+ materialReferences.push(materialReference);
30600
30716
  }
30601
30717
  }
30602
- return void 0;
30718
+ return materialReferences;
30719
+ }
30720
+ getMaterialTextureReferences(materialSource, creator) {
30721
+ const textureReferences = [];
30722
+ for (const line of materialSource.split(/\r?\n/u)) {
30723
+ const textureValue = /^\s*(?:map_\S+|bump|decal|disp|norm|refl)\s+(.+?)\s*$/iu.exec(
30724
+ line
30725
+ )?.[1];
30726
+ if (textureValue !== void 0) {
30727
+ textureReferences.push(creator.getTextureParams(textureValue, {}).url);
30728
+ }
30729
+ }
30730
+ return textureReferences;
30603
30731
  }
30604
- getMaterialUrl(source, objSource) {
30732
+ getMaterialUrl(source, materialReferences) {
30605
30733
  if (source.mtlUrl !== void 0) {
30606
30734
  return source.mtlUrl;
30607
30735
  }
30608
- const materialReference = this.getMaterialLibraryReference(objSource);
30736
+ const materialReference = materialReferences[0];
30609
30737
  if (materialReference === void 0) {
30610
30738
  return void 0;
30611
30739
  }
@@ -30635,21 +30763,29 @@ class ObjLoader {
30635
30763
  isPoints(object) {
30636
30764
  return object instanceof Points;
30637
30765
  }
30638
- async loadMaterialContext(mtlUrl, textureUrls, signal) {
30766
+ async loadMaterialContext(mtlUrl, textureUrls, signal, resources) {
30639
30767
  const materialSource = await this.loadText(mtlUrl, signal);
30640
30768
  this.assertNotCancelled(signal);
30641
30769
  const materialBaseUrl = this.getResourceDirectoryUrl(mtlUrl);
30642
30770
  const manager = new LoadingManager();
30643
- manager.setURLModifier(this.resolveTextureUrl.bind(this, textureUrls, materialBaseUrl));
30771
+ manager.setURLModifier(
30772
+ resources === void 0 ? this.resolveTextureUrl.bind(this, textureUrls, materialBaseUrl) : resources.resolveTextureUrl.bind(resources, materialBaseUrl)
30773
+ );
30644
30774
  const materialLoader = new MTLLoader(manager);
30645
30775
  materialLoader.setMaterialOptions({
30646
30776
  side: DoubleSide
30647
30777
  });
30648
30778
  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
+ }
30649
30783
  this.validateTextureOverrides(textureUrls);
30784
+ this.removeLegacyPbrTextureMappings(creator);
30650
30785
  const monitor = new TextureLoadMonitor(manager, creator);
30651
30786
  try {
30652
30787
  creator.preload();
30788
+ this.replaceMaterialsWithPbr(creator, pbrMaterialReferences, materialBaseUrl);
30653
30789
  monitor.finishScheduling();
30654
30790
  } catch (error2) {
30655
30791
  monitor.finishScheduling();
@@ -30704,6 +30840,77 @@ class ObjLoader {
30704
30840
  }
30705
30841
  this.disposeMaterials(materialsToDispose);
30706
30842
  }
30843
+ createPbrMaterial(source, references, creator, materialBaseUrl) {
30844
+ const material = new MeshStandardMaterial();
30845
+ material.alphaMap = source.alphaMap;
30846
+ material.alphaTest = source.alphaTest;
30847
+ material.color.copy(source.color);
30848
+ material.displacementBias = source.displacementBias;
30849
+ material.displacementMap = source.displacementMap;
30850
+ material.displacementScale = source.displacementScale;
30851
+ material.emissive.copy(source.emissive);
30852
+ material.emissiveIntensity = source.emissiveIntensity;
30853
+ material.emissiveMap = source.emissiveMap;
30854
+ material.map = source.map;
30855
+ material.name = source.name;
30856
+ material.opacity = source.opacity;
30857
+ material.side = source.side;
30858
+ material.transparent = source.transparent;
30859
+ material.vertexColors = source.vertexColors;
30860
+ if (material.map !== null) {
30861
+ material.map.colorSpace = SRGBColorSpace;
30862
+ }
30863
+ if (material.emissiveMap !== null) {
30864
+ material.emissiveMap.colorSpace = SRGBColorSpace;
30865
+ }
30866
+ const roughnessReference = references.roughness ?? references.roughnessFallback;
30867
+ if (roughnessReference !== void 0) {
30868
+ material.roughnessMap = this.loadPbrTexture(
30869
+ creator,
30870
+ materialBaseUrl,
30871
+ roughnessReference
30872
+ ).texture;
30873
+ }
30874
+ if (references.metalness !== void 0) {
30875
+ material.metalnessMap = this.loadPbrTexture(
30876
+ creator,
30877
+ materialBaseUrl,
30878
+ references.metalness
30879
+ ).texture;
30880
+ }
30881
+ const normalReference = references.normal ?? references.legacyNormal;
30882
+ if (normalReference !== void 0) {
30883
+ const normalTexture = this.loadPbrTexture(creator, materialBaseUrl, normalReference);
30884
+ material.normalMap = normalTexture.texture;
30885
+ if (normalTexture.bumpScale !== void 0) {
30886
+ material.normalScale.set(normalTexture.bumpScale, normalTexture.bumpScale);
30887
+ }
30888
+ }
30889
+ if (references.bump !== void 0) {
30890
+ const bumpTexture = this.loadPbrTexture(creator, materialBaseUrl, references.bump);
30891
+ material.bumpMap = bumpTexture.texture;
30892
+ if (bumpTexture.bumpScale !== void 0) {
30893
+ material.bumpScale = bumpTexture.bumpScale;
30894
+ }
30895
+ }
30896
+ return material;
30897
+ }
30898
+ loadPbrTexture(creator, materialBaseUrl, reference) {
30899
+ const materialParameters = {};
30900
+ const textureParameters = creator.getTextureParams(reference, materialParameters);
30901
+ const texture = creator.loadTexture(
30902
+ this.resolveResourceUrl(textureParameters.url, materialBaseUrl)
30903
+ );
30904
+ texture.colorSpace = NoColorSpace;
30905
+ texture.offset.copy(textureParameters.offset);
30906
+ texture.repeat.copy(textureParameters.scale);
30907
+ texture.wrapS = creator.wrap;
30908
+ texture.wrapT = creator.wrap;
30909
+ return {
30910
+ bumpScale: materialParameters.bumpScale,
30911
+ texture
30912
+ };
30913
+ }
30707
30914
  parseObj(source, materials) {
30708
30915
  if (materials === void 0) {
30709
30916
  return this.parser.parse(source);
@@ -30712,6 +30919,78 @@ class ObjLoader {
30712
30919
  parser.setMaterials(materials);
30713
30920
  return parser.parse(source);
30714
30921
  }
30922
+ parsePbrMaterialReferences(materialSource) {
30923
+ const referencesByMaterial = {};
30924
+ let currentReferences;
30925
+ for (const line of materialSource.split(/\r?\n/u)) {
30926
+ const materialName = /^\s*newmtl\s+(.+?)\s*$/iu.exec(line)?.[1];
30927
+ if (materialName !== void 0) {
30928
+ currentReferences = {
30929
+ bump: void 0,
30930
+ legacyNormal: void 0,
30931
+ metalness: void 0,
30932
+ normal: void 0,
30933
+ roughness: void 0,
30934
+ roughnessFallback: void 0
30935
+ };
30936
+ referencesByMaterial[materialName] = currentReferences;
30937
+ continue;
30938
+ }
30939
+ if (currentReferences === void 0) {
30940
+ continue;
30941
+ }
30942
+ const textureReference = /^\s*(map_pr|map_ns|map_pm|map_bump|bump|norm)\s+(.+?)\s*$/iu.exec(line);
30943
+ if (textureReference === null) {
30944
+ continue;
30945
+ }
30946
+ const [, directive = "", value = ""] = textureReference;
30947
+ switch (directive.toLowerCase()) {
30948
+ case "bump":
30949
+ currentReferences.bump = value;
30950
+ break;
30951
+ case "map_bump":
30952
+ currentReferences.legacyNormal = value;
30953
+ break;
30954
+ case "map_pm":
30955
+ currentReferences.metalness = value;
30956
+ break;
30957
+ case "map_ns":
30958
+ currentReferences.roughnessFallback = value;
30959
+ break;
30960
+ case "map_pr":
30961
+ currentReferences.roughness = value;
30962
+ break;
30963
+ case "norm":
30964
+ currentReferences.normal = value;
30965
+ break;
30966
+ }
30967
+ }
30968
+ return referencesByMaterial;
30969
+ }
30970
+ removeLegacyPbrTextureMappings(creator) {
30971
+ for (const materialInfo of Object.values(creator.materialsInfo)) {
30972
+ delete materialInfo.bump;
30973
+ delete materialInfo.map_bump;
30974
+ delete materialInfo.norm;
30975
+ }
30976
+ }
30977
+ replaceMaterialsWithPbr(creator, referencesByMaterial, materialBaseUrl) {
30978
+ const replacedMaterials = /* @__PURE__ */ new Set();
30979
+ for (const [materialName, references] of Object.entries(referencesByMaterial)) {
30980
+ const sourceMaterial = creator.materials[materialName];
30981
+ if (!(sourceMaterial instanceof MeshPhongMaterial)) {
30982
+ throw new Error("The MTL loader did not create a phong material.");
30983
+ }
30984
+ creator.materials[materialName] = this.createPbrMaterial(
30985
+ sourceMaterial,
30986
+ references,
30987
+ creator,
30988
+ materialBaseUrl
30989
+ );
30990
+ replacedMaterials.add(sourceMaterial);
30991
+ }
30992
+ this.disposeMaterialsWithoutTextures(replacedMaterials);
30993
+ }
30715
30994
  resolveResourceUrl(reference, baseUrl) {
30716
30995
  return new URL(reference, this.toAbsoluteUrl(baseUrl)).href;
30717
30996
  }
@@ -30740,6 +31019,101 @@ class ObjLoader {
30740
31019
  }
30741
31020
  }
30742
31021
  }
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.");
31051
+ }
31052
+ mtlFileName = resource.fileName;
31053
+ mtlUrl = resource.url;
31054
+ continue;
31055
+ }
31056
+ this.textureUrls.add(resource.url);
31057
+ this.textureUrlsByFileName.set(resource.fileName, resource.url);
31058
+ }
31059
+ if (objUrl === void 0) {
31060
+ throw new Error("The OBJ resource list must contain an OBJ file.");
31061
+ }
31062
+ this.mtlFileName = mtlFileName;
31063
+ this.mtlUrl = mtlUrl;
31064
+ this.objUrl = objUrl;
31065
+ }
31066
+ assertMaterialLibraries(materialReferences) {
31067
+ if (materialReferences.length === 0) {
31068
+ return;
31069
+ }
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.");
31075
+ }
31076
+ const referenceUrl = new URL(materialReference, this.objUrl).href;
31077
+ if (referenceUrl === mtlUrl || this.getFileName(new URL(referenceUrl)) === mtlFileName) {
31078
+ return;
31079
+ }
31080
+ throw new Error("The OBJ material library does not match the resource list.");
31081
+ }
31082
+ resolveTextureUrl(materialBaseUrl, textureUrl) {
31083
+ const resolvedUrl = new URL(textureUrl, materialBaseUrl).href;
31084
+ if (this.textureUrls.has(resolvedUrl)) {
31085
+ return resolvedUrl;
31086
+ }
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.");
31090
+ }
31091
+ return mappedUrl;
31092
+ }
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.");
31097
+ }
31098
+ return {
31099
+ fileName: this.getFileName(url),
31100
+ url: url.href
31101
+ };
31102
+ }
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.");
31107
+ }
31108
+ return fileName;
31109
+ }
31110
+ isMtlUrl(url) {
31111
+ return new URL(url).pathname.toLowerCase().endsWith(".mtl");
31112
+ }
31113
+ isObjUrl(url) {
31114
+ return new URL(url).pathname.toLowerCase().endsWith(".obj");
31115
+ }
31116
+ }
30743
31117
  const DEFAULT_BACKGROUND_COLOR = 3355443;
30744
31118
  class Renderer {
30745
31119
  activeModels = [];
@@ -30911,6 +31285,47 @@ class Renderer {
30911
31285
  stone: source
30912
31286
  });
30913
31287
  }
31288
+ importObjResources(source) {
31289
+ this.assertNotDisposed();
31290
+ if (this.contextLost) {
31291
+ return Promise.resolve({
31292
+ code: ObjImportFailureCode$1.RenderFailed,
31293
+ success: false
31294
+ });
31295
+ }
31296
+ const session = this.startImportSession();
31297
+ try {
31298
+ return this.completeResourceListImport(session, new ObjResourceList(source.fileUrls));
31299
+ } catch {
31300
+ this.finishImportSession(session);
31301
+ return Promise.resolve({
31302
+ code: ObjImportFailureCode$1.InvalidSource,
31303
+ success: false
31304
+ });
31305
+ }
31306
+ }
31307
+ importObjResourceScene(source) {
31308
+ this.assertNotDisposed();
31309
+ if (this.contextLost) {
31310
+ return Promise.resolve({
31311
+ code: ObjImportFailureCode$1.RenderFailed,
31312
+ success: false
31313
+ });
31314
+ }
31315
+ const session = this.startImportSession();
31316
+ try {
31317
+ return this.completeSceneImport(session, {
31318
+ base: source.base === void 0 ? void 0 : new ObjResourceList(source.base.fileUrls),
31319
+ stone: source.stone === void 0 ? void 0 : new ObjResourceList(source.stone.fileUrls)
31320
+ });
31321
+ } catch {
31322
+ this.finishImportSession(session);
31323
+ return Promise.resolve({
31324
+ code: ObjImportFailureCode$1.InvalidSource,
31325
+ success: false
31326
+ });
31327
+ }
31328
+ }
30914
31329
  importObjScene(source) {
30915
31330
  this.assertNotDisposed();
30916
31331
  if (this.contextLost) {
@@ -30928,6 +31343,28 @@ class Renderer {
30928
31343
  this.controller.reset();
30929
31344
  this.requestRender();
30930
31345
  }
31346
+ takeScreenshot() {
31347
+ this.assertNotDisposed();
31348
+ if (this.contextLost) {
31349
+ return Promise.reject(new Error("The WebGL context is unavailable."));
31350
+ }
31351
+ try {
31352
+ this.renderScene();
31353
+ } catch (error2) {
31354
+ return Promise.reject(
31355
+ new Error("The screenshot could not be rendered.", { cause: error2 })
31356
+ );
31357
+ }
31358
+ return new Promise((resolve, reject) => {
31359
+ this.canvas.toBlob((blob) => {
31360
+ if (blob === null) {
31361
+ reject(new Error("The screenshot could not be encoded."));
31362
+ return;
31363
+ }
31364
+ resolve(blob);
31365
+ }, "image/png");
31366
+ });
31367
+ }
30931
31368
  assertNotDisposed() {
30932
31369
  if (this.disposed) {
30933
31370
  throw new Error("The renderer has been disposed.");
@@ -30951,7 +31388,7 @@ class Renderer {
30951
31388
  success: false
30952
31389
  };
30953
31390
  }
30954
- const firstCandidate = await this.loadSceneModel(session, firstSource);
31391
+ const firstCandidate = await this.loadModel(session, firstSource);
30955
31392
  if (this.isImportResult(firstCandidate)) {
30956
31393
  return firstCandidate;
30957
31394
  }
@@ -30959,7 +31396,7 @@ class Renderer {
30959
31396
  if (stoneSource === void 0 || baseSource === void 0) {
30960
31397
  return this.replaceActiveModels(session, candidateModels);
30961
31398
  }
30962
- const secondCandidate = await this.loadSceneModel(session, baseSource);
31399
+ const secondCandidate = await this.loadModel(session, baseSource);
30963
31400
  if (this.isImportResult(secondCandidate)) {
30964
31401
  this.releaseModels(candidateModels);
30965
31402
  return secondCandidate;
@@ -30967,10 +31404,17 @@ class Renderer {
30967
31404
  candidateModels.push(secondCandidate);
30968
31405
  return this.replaceActiveModels(session, candidateModels);
30969
31406
  }
30970
- async loadSceneModel(session, source) {
31407
+ async completeResourceListImport(session, resources) {
31408
+ const candidate = await this.loadModel(session, resources);
31409
+ if (this.isImportResult(candidate)) {
31410
+ return candidate;
31411
+ }
31412
+ return this.replaceActiveModels(session, [candidate]);
31413
+ }
31414
+ async loadModel(session, source) {
30971
31415
  let result;
30972
31416
  try {
30973
- result = await this.modelLoader.load(source, session.controller.signal);
31417
+ result = source instanceof ObjResourceList ? await this.modelLoader.loadResources(source, session.controller.signal) : await this.modelLoader.load(source, session.controller.signal);
30974
31418
  } catch {
30975
31419
  return this.completeUnexpectedImportFailure(session);
30976
31420
  }
@@ -31098,13 +31542,16 @@ class Renderer {
31098
31542
  return;
31099
31543
  }
31100
31544
  const animationActive = this.controller.updateAnimation(timestamp);
31101
- this.viewCube.update(this.camera);
31102
- this.renderer.render(this.scene, this.camera);
31103
- this.renderViewCube();
31545
+ this.renderScene();
31104
31546
  if (animationActive) {
31105
31547
  this.requestRender();
31106
31548
  }
31107
31549
  }
31550
+ renderScene() {
31551
+ this.viewCube.update(this.camera);
31552
+ this.renderer.render(this.scene, this.camera);
31553
+ this.renderViewCube();
31554
+ }
31108
31555
  renderViewCube() {
31109
31556
  const viewport = this.viewCube.getViewport();
31110
31557
  const previousAutoClear = this.renderer.autoClear;
@@ -31256,6 +31703,14 @@ class Application {
31256
31703
  this.assertNotDisposed();
31257
31704
  return this.mapAsyncObjImportResult(this.getRenderer().importObj(source));
31258
31705
  }
31706
+ importObjResources(source) {
31707
+ this.assertNotDisposed();
31708
+ return this.mapAsyncObjImportResult(this.getRenderer().importObjResources(source));
31709
+ }
31710
+ importObjResourceScene(source) {
31711
+ this.assertNotDisposed();
31712
+ return this.mapAsyncObjImportResult(this.getRenderer().importObjResourceScene(source));
31713
+ }
31259
31714
  importObjScene(source) {
31260
31715
  this.assertNotDisposed();
31261
31716
  return this.mapAsyncObjImportResult(this.getRenderer().importObjScene(source));
@@ -31264,6 +31719,10 @@ class Application {
31264
31719
  this.assertNotDisposed();
31265
31720
  this.getRenderer().resetCamera();
31266
31721
  }
31722
+ takeScreenshot() {
31723
+ this.assertNotDisposed();
31724
+ return this.getRenderer().takeScreenshot();
31725
+ }
31267
31726
  dispose() {
31268
31727
  if (this.disposed) {
31269
31728
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lingbi/studio",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",