@combeenation/3d-viewer 14.0.1-rc1 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +9 -9
  2. package/dist/lib-cjs/buildinfo.json +3 -3
  3. package/dist/lib-cjs/commonjs.tsconfig.tsbuildinfo +1 -1
  4. package/dist/lib-cjs/index.d.ts +51 -62
  5. package/dist/lib-cjs/index.js +84 -94
  6. package/dist/lib-cjs/index.js.map +1 -1
  7. package/dist/lib-cjs/internal/cbn-custom-babylon-loader-plugin.d.ts +10 -10
  8. package/dist/lib-cjs/internal/cbn-custom-babylon-loader-plugin.js +131 -131
  9. package/dist/lib-cjs/internal/cbn-custom-babylon-loader-plugin.js.map +1 -1
  10. package/dist/lib-cjs/internal/cloning-helper.d.ts +19 -19
  11. package/dist/lib-cjs/internal/cloning-helper.js +163 -163
  12. package/dist/lib-cjs/internal/device-helper.d.ts +9 -9
  13. package/dist/lib-cjs/internal/device-helper.js +24 -24
  14. package/dist/lib-cjs/internal/geometry-helper.d.ts +21 -21
  15. package/dist/lib-cjs/internal/geometry-helper.js +145 -145
  16. package/dist/lib-cjs/internal/metadata-helper.d.ts +26 -26
  17. package/dist/lib-cjs/internal/metadata-helper.js +50 -50
  18. package/dist/lib-cjs/internal/paintable-helper.d.ts +40 -40
  19. package/dist/lib-cjs/internal/paintable-helper.js +234 -286
  20. package/dist/lib-cjs/internal/paintable-helper.js.map +1 -1
  21. package/dist/lib-cjs/internal/svg-helper.d.ts +4 -0
  22. package/dist/lib-cjs/internal/svg-helper.js +67 -0
  23. package/dist/lib-cjs/internal/svg-helper.js.map +1 -0
  24. package/dist/lib-cjs/internal/tags-helper.d.ts +12 -12
  25. package/dist/lib-cjs/internal/tags-helper.js +39 -37
  26. package/dist/lib-cjs/internal/tags-helper.js.map +1 -1
  27. package/dist/lib-cjs/internal/texture-parameter-helper.d.ts +37 -0
  28. package/dist/lib-cjs/internal/texture-parameter-helper.js +287 -0
  29. package/dist/lib-cjs/internal/texture-parameter-helper.js.map +1 -0
  30. package/dist/lib-cjs/manager/camera-manager.d.ts +110 -110
  31. package/dist/lib-cjs/manager/camera-manager.js +209 -206
  32. package/dist/lib-cjs/manager/camera-manager.js.map +1 -1
  33. package/dist/lib-cjs/manager/debug-manager.d.ts +60 -60
  34. package/dist/lib-cjs/manager/debug-manager.js +217 -217
  35. package/dist/lib-cjs/manager/event-manager.d.ts +52 -52
  36. package/dist/lib-cjs/manager/event-manager.js +71 -71
  37. package/dist/lib-cjs/manager/gltf-export-manager.d.ts +75 -84
  38. package/dist/lib-cjs/manager/gltf-export-manager.js +286 -290
  39. package/dist/lib-cjs/manager/gltf-export-manager.js.map +1 -1
  40. package/dist/lib-cjs/manager/material-manager.d.ts +35 -35
  41. package/dist/lib-cjs/manager/material-manager.js +125 -125
  42. package/dist/lib-cjs/manager/model-manager.d.ts +145 -145
  43. package/dist/lib-cjs/manager/model-manager.js +382 -382
  44. package/dist/lib-cjs/manager/parameter-manager.d.ts +228 -210
  45. package/dist/lib-cjs/manager/parameter-manager.js +573 -514
  46. package/dist/lib-cjs/manager/parameter-manager.js.map +1 -1
  47. package/dist/lib-cjs/manager/scene-manager.d.ts +45 -45
  48. package/dist/lib-cjs/manager/scene-manager.js +64 -64
  49. package/dist/lib-cjs/manager/texture-manager.d.ts +12 -12
  50. package/dist/lib-cjs/manager/texture-manager.js +43 -43
  51. package/dist/lib-cjs/viewer-error.d.ts +49 -48
  52. package/dist/lib-cjs/viewer-error.js +61 -60
  53. package/dist/lib-cjs/viewer-error.js.map +1 -1
  54. package/dist/lib-cjs/viewer.d.ts +115 -115
  55. package/dist/lib-cjs/viewer.js +217 -217
  56. package/dist/lib-cjs/viewer.js.map +1 -1
  57. package/package.json +94 -91
  58. package/src/buildinfo.json +3 -3
  59. package/src/dev.ts +47 -47
  60. package/src/global-types.d.ts +39 -39
  61. package/src/index.ts +71 -81
  62. package/src/internal/cbn-custom-babylon-loader-plugin.ts +159 -159
  63. package/src/internal/cloning-helper.ts +225 -225
  64. package/src/internal/device-helper.ts +25 -25
  65. package/src/internal/geometry-helper.ts +181 -181
  66. package/src/internal/metadata-helper.ts +63 -63
  67. package/src/internal/paintable-helper.ts +258 -310
  68. package/src/internal/svg-helper.ts +52 -0
  69. package/src/internal/tags-helper.ts +43 -41
  70. package/src/internal/texture-parameter-helper.ts +353 -0
  71. package/src/manager/camera-manager.ts +368 -365
  72. package/src/manager/debug-manager.ts +245 -245
  73. package/src/manager/event-manager.ts +72 -72
  74. package/src/manager/gltf-export-manager.ts +356 -357
  75. package/src/manager/material-manager.ts +135 -135
  76. package/src/manager/model-manager.ts +458 -458
  77. package/src/manager/parameter-manager.ts +730 -652
  78. package/src/manager/scene-manager.ts +101 -101
  79. package/src/manager/texture-manager.ts +32 -32
  80. package/src/viewer-error.ts +69 -68
  81. package/src/viewer.ts +290 -290
@@ -1,41 +1,43 @@
1
- import { Material, Node } from '../index';
2
- import { Tags } from '@babylonjs/core/Misc/tags';
3
-
4
- /**
5
- * This is a wrapper around Babylon.js Tags API, as the API is really clunky to use
6
- */
7
-
8
- type TagTarget = Node | Material;
9
-
10
- export function hasTag(object: TagTarget, tag: string): boolean {
11
- return Tags.MatchesQuery(object, tag);
12
- }
13
-
14
- export function getTags(object: TagTarget): string[] {
15
- const tagsObject = Tags.GetTags(object, false) ?? {};
16
- const tags = Object.keys(tagsObject);
17
-
18
- return tags;
19
- }
20
-
21
- export function setTags(object: TagTarget, tags: string[]): void {
22
- const tagsString = tags.join(' ');
23
- Tags.AddTagsTo(object, tagsString);
24
- }
25
-
26
- export function deleteAllTags(object: TagTarget): void {
27
- const curTags = Tags.GetTags(object);
28
- if (curTags) {
29
- Tags.RemoveTagsFrom(object, curTags);
30
- }
31
- }
32
-
33
- export function setTagsAsString(object: TagTarget, tagsString: string): void {
34
- Tags.AddTagsTo(object, tagsString);
35
- }
36
-
37
- export function cloneTags(sourceObject: TagTarget, destinationObject: TagTarget): void {
38
- if (Tags.HasTags(sourceObject)) {
39
- Tags.AddTagsTo(destinationObject, Tags.GetTags(sourceObject, true));
40
- }
41
- }
1
+ import { Material, Node } from '../index';
2
+ import { Tags } from '@babylonjs/core/Misc/tags';
3
+
4
+ /**
5
+ * This is a wrapper around Babylon.js Tags API, as the API is really clunky to use
6
+ */
7
+
8
+ type TagTarget = Node | Material;
9
+
10
+ export function hasTag(object: TagTarget, tag: string): boolean {
11
+ return Tags.MatchesQuery(object, tag);
12
+ }
13
+
14
+ export function getTags(object: TagTarget): string[] {
15
+ const tagsObject = Tags.GetTags(object, false) ?? {};
16
+ const tags = Object.keys(tagsObject);
17
+
18
+ return tags;
19
+ }
20
+
21
+ export function setTags(object: TagTarget, tags: string[]): void {
22
+ deleteAllTags(object);
23
+ const tagsString = tags.join(' ');
24
+ Tags.AddTagsTo(object, tagsString);
25
+ }
26
+
27
+ export function setTagsAsString(object: TagTarget, tagsString: string): void {
28
+ deleteAllTags(object);
29
+ Tags.AddTagsTo(object, tagsString);
30
+ }
31
+
32
+ export function cloneTags(sourceObject: TagTarget, destinationObject: TagTarget): void {
33
+ if (Tags.HasTags(sourceObject)) {
34
+ Tags.AddTagsTo(destinationObject, Tags.GetTags(sourceObject, true));
35
+ }
36
+ }
37
+
38
+ export function deleteAllTags(object: TagTarget): void {
39
+ const curTags = Tags.GetTags(object);
40
+ if (curTags) {
41
+ Tags.RemoveTagsFrom(object, curTags);
42
+ }
43
+ }
@@ -0,0 +1,353 @@
1
+ import { BaseTexture, Material, PBRMaterial, Scene, Texture, ViewerError, ViewerErrorIds } from '..';
2
+ import { BuiltInParameter, ParameterManager } from '../manager/parameter-manager';
3
+ import { embedAssets } from './svg-helper';
4
+ import isSvg from 'is-svg';
5
+
6
+ /**
7
+ * Texture parameters are a combination of the channel (e.g. albedo, bump, AO) and the "sub" parameter (e.g. uScale).
8
+ * Each parameter is available in each texture, that's why these textures parameter are defined as dotted path
9
+ * `albedoTexture.uScale`
10
+ * - channel: albedoTexture
11
+ * - parameter: uScale
12
+ * The consumer doesn't have to know this as there is `BuiltInParameters.createTextureParameter(channel, parameter)`,
13
+ * which composes this string internally
14
+ */
15
+
16
+ export const BuiltInTextureParameter = {
17
+ image: 'image',
18
+ uOffset: 'uOffset',
19
+ vOffset: 'vOffset',
20
+ uScale: 'uScale',
21
+ vScale: 'vScale',
22
+ uAng: 'uAng',
23
+ vAng: 'vAng',
24
+ wAng: 'wAng',
25
+ clampU: 'clampU',
26
+ clampV: 'clampV',
27
+ uvSet: 'uvSet',
28
+ };
29
+ export type BuiltInTextureParameterKeys = keyof typeof BuiltInTextureParameter;
30
+
31
+ export const ParameterTextureChannels = {
32
+ albedoTexture: 'albedoTexture',
33
+ metallicRoughnessTexture: 'metallicRoughnessTexture',
34
+ bumpTexture: 'bumpTexture',
35
+ emissiveTexture: 'emissiveTexture',
36
+ opacityTexture: 'opacityTexture',
37
+ ambientTexture: 'ambientTexture',
38
+ lightmapTexture: 'lightmapTexture',
39
+ detailmapTexture: 'detailmapTexture',
40
+ };
41
+ export type ParameterTextureChannelsKeys = keyof typeof ParameterTextureChannels;
42
+
43
+ export function createBuiltInTextureParameter(parameterManager: ParameterManager, scene: Scene): void {
44
+ // create parameter observer for each channel
45
+ (Object.values(ParameterTextureChannels) as ParameterTextureChannelsKeys[]).forEach(channel => {
46
+ parameterManager.setParameterObserver(
47
+ `${channel}.${BuiltInTextureParameter.image}`,
48
+ async ({ newValue, materials }) => {
49
+ const image = ParameterManager.parseString(newValue);
50
+ let url = image;
51
+
52
+ const imageIsSvg = isSvg(image);
53
+ if (imageIsSvg) {
54
+ // NOTE: image can also be provided as "svg", which is more or less the successor of the "paintable"
55
+ // parameter. It misses the UV parameters, but we now have dedicated parameters for this.
56
+ // We used "Dynamic Textures" for paintables because we didn't know better I guess?
57
+ // However the rescaling functionality of "Dynamic Textures" is not required and therefore it can be
58
+ // implemented as a plain "Texture"
59
+ const svgWithAssetsEmbedded = await embedAssets(image);
60
+ // convert into a base64 string, as this can be directly interpreted as "url", at least for Babylon.js
61
+ url = 'data:image/svg+xml;base64,' + btoa(svgWithAssetsEmbedded);
62
+ } else if (image.includes('<svg') && image.includes('</svg>')) {
63
+ // seems like the user tried to use a SVG string, as <svg> tags are used
64
+ // inform the user that this is not a valid SVG string
65
+ throw new ViewerError({
66
+ id: ViewerErrorIds.InvalidParameterValue,
67
+ message: `Invalid value for parameter "image" given:\nsource string is no valid SVG string\nGiven value: ${image}`,
68
+ });
69
+ }
70
+
71
+ for (const material of materials) {
72
+ const pbrMaterial = _assertAndConvertPBRMaterial(material, channel, BuiltInTextureParameter.image);
73
+ const texture = _getTextureFromParameterChannel(pbrMaterial, channel);
74
+
75
+ if (texture) {
76
+ // create a clone of the texture, in this way we can load the image before assigning it to the material
77
+ // channel
78
+ const clonedTexture = texture.clone();
79
+ // update texture and await loading time right away
80
+ await new Promise<void>(resolve => clonedTexture.updateURL(url, undefined, resolve));
81
+
82
+ _assignTextureParameterChannel(clonedTexture, pbrMaterial, channel);
83
+
84
+ // dispose old texture
85
+ texture.dispose();
86
+ } else {
87
+ // no texture, or wrong type => create texture from scratch
88
+ // first we check if some settings were provided in the material definition
89
+ const addMatSettings = await window.Cbn?.Assets.getMaterial(pbrMaterial.id);
90
+ const textureSettings = addMatSettings ? _getTextureObjFromParameterChannel(addMatSettings, channel) : {};
91
+
92
+ // overwrite the name with the url, as this is what the texture parser expects
93
+ (textureSettings as any).name = url;
94
+ // use parser instead of constructor to keep the original texture settings
95
+ const newTexture = Texture.Parse(textureSettings, scene, '') as Texture;
96
+
97
+ if (!newTexture) {
98
+ throw new ViewerError({
99
+ id: ViewerErrorIds.TextureCouldNotBeParsed,
100
+ message: `Texture with url "${url}" could not be parsed`,
101
+ });
102
+ }
103
+
104
+ // await loading of texture
105
+ await new Promise<void>(resolve => BaseTexture.WhenAllReady([newTexture], resolve));
106
+ _assignTextureParameterChannel(newTexture, pbrMaterial, channel);
107
+
108
+ // apply texture settings, which have been set before the texture creation
109
+ await parameterManager.applyTextureSettingsParameter(pbrMaterial, channel);
110
+ }
111
+ }
112
+ }
113
+ );
114
+ parameterManager.setParameterObserver(
115
+ `${channel}.${BuiltInTextureParameter.uOffset}`,
116
+ async ({ newValue, materials }) => {
117
+ const uOffset = ParameterManager.parseNumber(newValue);
118
+
119
+ for (const material of materials) {
120
+ const pbrMaterial = _assertAndConvertPBRMaterial(material, channel, BuiltInTextureParameter.uOffset);
121
+
122
+ _setUVTextureSetting('uOffset', uOffset, pbrMaterial, channel);
123
+ }
124
+ }
125
+ );
126
+ parameterManager.setParameterObserver(
127
+ `${channel}.${BuiltInTextureParameter.vOffset}`,
128
+ async ({ newValue, materials }) => {
129
+ const vOffset = ParameterManager.parseNumber(newValue);
130
+
131
+ for (const material of materials) {
132
+ const pbrMaterial = _assertAndConvertPBRMaterial(material, channel, BuiltInTextureParameter.vOffset);
133
+
134
+ _setUVTextureSetting('vOffset', vOffset, pbrMaterial, channel);
135
+ }
136
+ }
137
+ );
138
+ parameterManager.setParameterObserver(
139
+ `${channel}.${BuiltInTextureParameter.uScale}`,
140
+ async ({ newValue, materials }) => {
141
+ const uScale = ParameterManager.parseNumber(newValue);
142
+
143
+ for (const material of materials) {
144
+ const pbrMaterial = _assertAndConvertPBRMaterial(material, channel, BuiltInTextureParameter.uScale);
145
+
146
+ _setUVTextureSetting('uScale', uScale, pbrMaterial, channel);
147
+ }
148
+ }
149
+ );
150
+ parameterManager.setParameterObserver(
151
+ `${channel}.${BuiltInTextureParameter.vScale}`,
152
+ async ({ newValue, materials }) => {
153
+ const vScale = ParameterManager.parseNumber(newValue);
154
+
155
+ for (const material of materials) {
156
+ const pbrMaterial = _assertAndConvertPBRMaterial(material, channel, BuiltInTextureParameter.vScale);
157
+
158
+ _setUVTextureSetting('vScale', vScale, pbrMaterial, channel);
159
+ }
160
+ }
161
+ );
162
+ parameterManager.setParameterObserver(
163
+ `${channel}.${BuiltInTextureParameter.uAng}`,
164
+ async ({ newValue, materials }) => {
165
+ const uAng = ParameterManager.parseNumber(newValue);
166
+
167
+ for (const material of materials) {
168
+ const pbrMaterial = _assertAndConvertPBRMaterial(material, channel, BuiltInTextureParameter.uAng);
169
+
170
+ _setUVTextureSetting('uAng', uAng, pbrMaterial, channel);
171
+ }
172
+ }
173
+ );
174
+ parameterManager.setParameterObserver(
175
+ `${channel}.${BuiltInTextureParameter.vAng}`,
176
+ async ({ newValue, materials }) => {
177
+ const vAng = ParameterManager.parseNumber(newValue);
178
+
179
+ for (const material of materials) {
180
+ const pbrMaterial = _assertAndConvertPBRMaterial(material, channel, BuiltInTextureParameter.vAng);
181
+
182
+ _setUVTextureSetting('vAng', vAng, pbrMaterial, channel);
183
+ }
184
+ }
185
+ );
186
+ parameterManager.setParameterObserver(
187
+ `${channel}.${BuiltInTextureParameter.wAng}`,
188
+ async ({ newValue, materials }) => {
189
+ const wAng = ParameterManager.parseNumber(newValue);
190
+
191
+ for (const material of materials) {
192
+ const pbrMaterial = _assertAndConvertPBRMaterial(material, channel, BuiltInTextureParameter.wAng);
193
+
194
+ _setUVTextureSetting('wAng', wAng, pbrMaterial, channel);
195
+ }
196
+ }
197
+ );
198
+ parameterManager.setParameterObserver(
199
+ `${channel}.${BuiltInTextureParameter.clampU}`,
200
+ async ({ newValue, materials }) => {
201
+ const clampU = ParameterManager.parseBoolean(newValue);
202
+
203
+ for (const material of materials) {
204
+ const pbrMaterial = _assertAndConvertPBRMaterial(material, channel, BuiltInTextureParameter.clampU);
205
+
206
+ // we don't use _setUVTextureSetting here as the parameter ("clampU" - boolean) doesn't align with the actual
207
+ // texture setting ("wrapU" - enumeration)
208
+ const texture = _getTextureFromParameterChannel(pbrMaterial, channel);
209
+ if (texture) {
210
+ texture.wrapU = clampU ? Texture.CLAMP_ADDRESSMODE : Texture.WRAP_ADDRESSMODE;
211
+ }
212
+ }
213
+ }
214
+ );
215
+ parameterManager.setParameterObserver(
216
+ `${channel}.${BuiltInTextureParameter.clampV}`,
217
+ async ({ newValue, materials }) => {
218
+ const clampV = ParameterManager.parseBoolean(newValue);
219
+
220
+ for (const material of materials) {
221
+ const pbrMaterial = _assertAndConvertPBRMaterial(material, channel, BuiltInTextureParameter.clampV);
222
+
223
+ const texture = _getTextureFromParameterChannel(pbrMaterial, channel);
224
+ if (texture) {
225
+ texture.wrapV = clampV ? Texture.CLAMP_ADDRESSMODE : Texture.WRAP_ADDRESSMODE;
226
+ }
227
+ }
228
+ }
229
+ );
230
+ parameterManager.setParameterObserver(
231
+ `${channel}.${BuiltInTextureParameter.uvSet}`,
232
+ async ({ newValue, materials }) => {
233
+ const uvSet = ParameterManager.parseNumber(newValue);
234
+
235
+ for (const material of materials) {
236
+ const pbrMaterial = _assertAndConvertPBRMaterial(material, channel, BuiltInTextureParameter.uvSet);
237
+
238
+ const texture = _getTextureFromParameterChannel(pbrMaterial, channel);
239
+ if (texture) {
240
+ texture.coordinatesIndex = uvSet;
241
+ }
242
+ }
243
+ }
244
+ );
245
+ });
246
+
247
+ // this parameter is required for activating the detailmap texture, as only assigning the texture is not enough
248
+ parameterManager.setParameterObserver(BuiltInParameter.UseDetailmap, async ({ newValue, materials }) => {
249
+ const useDetailmap = ParameterManager.parseBoolean(newValue);
250
+
251
+ for (const material of materials) {
252
+ const materialCls = material.getClassName();
253
+ if (materialCls !== 'PBRMaterial') {
254
+ throw new Error(`Enabling detailmap for material of instance "${materialCls}" not implemented`);
255
+ }
256
+
257
+ const pbrMaterial = material as PBRMaterial;
258
+ pbrMaterial.detailMap.isEnabled = useDetailmap;
259
+ }
260
+ });
261
+ }
262
+
263
+ /**
264
+ * Help function for casting a material into a PBRMaterial type.\
265
+ * Only works if the class name confirms that this is a PBR material, throws an error for any other material type.
266
+ *
267
+ * @param channel only used for error message
268
+ * @param parameter only used for error message
269
+ */
270
+ function _assertAndConvertPBRMaterial(material: Material, channel: string, parameter: string): PBRMaterial {
271
+ const materialCls = material.getClassName();
272
+ if (materialCls !== 'PBRMaterial') {
273
+ throw new Error(`Setting ${channel} ${parameter} for material of instance "${materialCls}" not implemented`);
274
+ }
275
+
276
+ return material as PBRMaterial;
277
+ }
278
+
279
+ /**
280
+ * Help function for adjusting "simple" UV texture settings, as described in the typing of `setting`
281
+ */
282
+ function _setUVTextureSetting(
283
+ setting: keyof Pick<Texture, 'uOffset' | 'vOffset' | 'uScale' | 'vScale' | 'uAng' | 'vAng' | 'wAng'>,
284
+ value: number,
285
+ pbrMaterial: PBRMaterial,
286
+ channel: ParameterTextureChannelsKeys
287
+ ): void {
288
+ const texture = _getTextureFromParameterChannel(pbrMaterial, channel);
289
+
290
+ if (texture) {
291
+ texture[setting] = value;
292
+ } else {
293
+ // this is fine, the texture may be created in a later step
294
+ // texture settings are applied automatically after the creation is done
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Get the runtime texture object from a certain channel in the material.\
300
+ * Only returns the texture if it's from type "Texture".
301
+ */
302
+ function _getTextureFromParameterChannel(
303
+ pbrMaterial: PBRMaterial,
304
+ channel: ParameterTextureChannelsKeys
305
+ ): Texture | undefined {
306
+ let baseTexture: BaseTexture | null;
307
+
308
+ if (channel === 'metallicRoughnessTexture') {
309
+ baseTexture = pbrMaterial.metallicTexture;
310
+ } else if (channel === 'detailmapTexture') {
311
+ baseTexture = pbrMaterial.detailMap.texture;
312
+ } else {
313
+ // most times it's a direct assignment, special cases are handled above
314
+ baseTexture = pbrMaterial[channel];
315
+ }
316
+
317
+ // only return "Texture" types
318
+ return baseTexture instanceof Texture ? baseTexture : undefined;
319
+ }
320
+
321
+ /**
322
+ * Assign runtime texture to the dedicated channel in the PBR material
323
+ */
324
+ function _assignTextureParameterChannel(
325
+ texture: Texture,
326
+ pbrMaterial: PBRMaterial,
327
+ channel: ParameterTextureChannelsKeys
328
+ ): void {
329
+ if (channel === 'metallicRoughnessTexture') {
330
+ pbrMaterial.metallicTexture = texture;
331
+ } else if (channel === 'detailmapTexture') {
332
+ pbrMaterial.detailMap.texture = texture;
333
+ } else {
334
+ pbrMaterial[channel] = texture;
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Similar to `_getTextureFromParameterChannel`, whereas the input is a plain JSON object instead of a runtime material
340
+ */
341
+ function _getTextureObjFromParameterChannel(materialObj: object, channel: ParameterTextureChannelsKeys): object {
342
+ let textureObj: object | undefined;
343
+
344
+ if (channel === 'metallicRoughnessTexture') {
345
+ textureObj = (materialObj as any).metallicTexture;
346
+ } else if (channel === 'detailmapTexture') {
347
+ textureObj = (materialObj as any).detailMap?.texture;
348
+ } else {
349
+ textureObj = (materialObj as any)[channel];
350
+ }
351
+
352
+ return textureObj ?? {};
353
+ }