@threepipe/plugin-gltf-transform 0.2.2 → 0.3.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 (47) hide show
  1. package/CHANGELOG.md +22 -3
  2. package/README.md +2 -2
  3. package/dist/GLTFDracoExportPlugin.d.ts +2 -44
  4. package/dist/GLTFDracoExportPlugin.d.ts.map +1 -1
  5. package/dist/GLTFDracoExporter.d.ts +5 -40
  6. package/dist/GLTFDracoExporter.d.ts.map +1 -1
  7. package/dist/GLTFDracoExporterBase.d.ts +49 -0
  8. package/dist/GLTFDracoExporterBase.d.ts.map +1 -0
  9. package/dist/GLTFSpecGlossinessConverterPlugin.d.ts +10 -31
  10. package/dist/GLTFSpecGlossinessConverterPlugin.d.ts.map +1 -1
  11. package/dist/GLTFSpecGlossinessConverterPluginBase.d.ts +45 -0
  12. package/dist/GLTFSpecGlossinessConverterPluginBase.d.ts.map +1 -0
  13. package/dist/dependencies.txt +12 -12
  14. package/dist/index.d.ts +5 -3
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +119 -136
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +117 -134
  19. package/dist/index.mjs.map +1 -1
  20. package/docs/assets/hierarchy.js +1 -1
  21. package/docs/assets/icons.js +1 -1
  22. package/docs/assets/icons.svg +1 -1
  23. package/docs/assets/main.js +2 -2
  24. package/docs/assets/navigation.js +1 -1
  25. package/docs/assets/search.js +1 -1
  26. package/docs/assets/style.css +3 -3
  27. package/docs/classes/GLTFDracoExportPlugin.html +7 -6
  28. package/docs/classes/GLTFDracoExporter.html +15 -14
  29. package/docs/classes/GLTFDracoExporterBase.html +37 -0
  30. package/docs/classes/GLTFSpecGlossinessConverterPlugin.html +16 -14
  31. package/docs/classes/GLTFSpecGlossinessConverterPluginBase.html +37 -0
  32. package/docs/classes/GLTFViewerConfigExtensionGP.html +5 -0
  33. package/docs/enums/EncoderMethod.html +2 -2
  34. package/docs/functions/createGenericExtensionClass.html +1 -1
  35. package/docs/functions/gltfKhrPbrSpecularGlossinessConverter.html +2 -1
  36. package/docs/functions/gltfKhrPbrSpecularGlossinessConverterBase.html +1 -0
  37. package/docs/hierarchy.html +1 -1
  38. package/docs/index.html +1 -1
  39. package/package.json +24 -2
  40. package/src/GLTFDracoExportPlugin.ts +2 -2
  41. package/src/GLTFDracoExporter.ts +10 -417
  42. package/src/GLTFDracoExporterBase.ts +427 -0
  43. package/src/GLTFSpecGlossinessConverterPlugin.ts +14 -115
  44. package/src/GLTFSpecGlossinessConverterPluginBase.ts +129 -0
  45. package/src/index.ts +3 -1
  46. package/tsconfig.json +1 -0
  47. package/docs/.nojekyll +0 -1
@@ -0,0 +1,427 @@
1
+ import {
2
+ Extension,
3
+ ExtensionProperty,
4
+ GLTF,
5
+ Graph,
6
+ PlatformIO,
7
+ Property,
8
+ PropertyType,
9
+ ReaderContext,
10
+ Texture,
11
+ TextureChannel,
12
+ TextureInfo,
13
+ WriterContext,
14
+ } from '@gltf-transform/core'
15
+ import {ALL_EXTENSIONS, KHRDracoMeshCompression} from '@gltf-transform/extensions'
16
+
17
+ // From the public API: @gltf-transform/extensions does not export its encoder module path.
18
+ export type EncoderOptions = Parameters<KHRDracoMeshCompression['setEncoderOptions']>[0]
19
+ import {DRACOLoader2, GLTFExporter2, GLTFExporter2Options, GLTFViewerConfigExtension, IExportWriter} from 'threepipe'
20
+
21
+ /**
22
+ * GLTF Draco Exporter Base
23
+ *
24
+ * Extension of GLTFExporter2 that runs the output through gltf-transform for draco compression.
25
+ * Takes a PlatformIO instance as a constructor parameter, allowing use with both WebIO (browser) and NodeIO (Node.js).
26
+ */
27
+ export class GLTFDracoExporterBase extends GLTFExporter2 implements IExportWriter {
28
+ public loader?: DRACOLoader2 // required for loading draco libs.
29
+ protected _io: PlatformIO
30
+ private _loadedLibs = false
31
+ private _encoderOptions: EncoderOptions
32
+
33
+ constructor(io: PlatformIO, encoderOptions?: EncoderOptions, loader?: DRACOLoader2) {
34
+ super()
35
+ encoderOptions = encoderOptions || {
36
+ method: KHRDracoMeshCompression.EncoderMethod.EDGEBREAKER,
37
+ encodeSpeed: 5,
38
+ }
39
+ this._io = io.registerExtensions(ALL_EXTENSIONS)
40
+ this._encoderOptions = encoderOptions
41
+
42
+ if (loader) {
43
+ this.loader = loader
44
+ this.loader.preload(true, true)
45
+ }
46
+
47
+ }
48
+
49
+ preload(): this {
50
+ this._loadLibs()
51
+ return this
52
+ }
53
+
54
+ private async _loadLibs() {
55
+ if (this._loadedLibs || !this.loader) return
56
+
57
+ const libs = await Promise.all([
58
+ this.loader.initEncoder(),
59
+ this.loader.initDecoder(),
60
+ ])
61
+ this._io.registerDependencies({
62
+ ['draco3d.encoder']: libs[0],
63
+ ['draco3d.decoder']: libs[1], // only required if we are loading a draco compressed gltf
64
+ })
65
+ this._loadedLibs = true
66
+
67
+ }
68
+
69
+ async parseAsync(obj: any, {compress = false, dracoOptions, ...options}: {compress: boolean, dracoOptions?: EncoderOptions} & GLTFExporter2Options, throwOnError = false): Promise<Blob> {
70
+ if (!this.loader) {
71
+ console.error('GLTFDracoExporter: No DRACOLoader2 instance provided')
72
+ return super.parseAsync(obj, options)
73
+ }
74
+ await this._loadLibs()
75
+
76
+ const ops = {...options}
77
+ if (compress) {
78
+ // externalImagesInExtras: this is required because gltf-transform doesn't support external images in glb
79
+ // see https://github.com/donmccurdy/glTF-Transform/discussions/644
80
+ ops.externalImagesInExtras = true
81
+ }
82
+
83
+ const uncompressed = await new Promise((resolve, reject) => this.parse(obj, resolve, reject, ops)) as any
84
+
85
+ const uncompressedBlob = await super.parseAsync(uncompressed, ops)
86
+ if (!compress) return uncompressedBlob
87
+
88
+ try {
89
+ if (!uncompressed) throw new Error('GLTFDracoExporter: gltf is null')
90
+
91
+ let gltf = uncompressed
92
+
93
+ const bytes = (gltf as ArrayBuffer).byteLength || Infinity
94
+
95
+ const iDocument = await (typeof gltf === 'object' && !(gltf as any).byteLength ? this._io.readJSON({
96
+ json: gltf as GLTF.IGLTF,
97
+ resources: {},
98
+ }) : this._io.readBinary(new Uint8Array(gltf as ArrayBuffer)))
99
+
100
+ // iDocument.createExtension(GLTFViewerConfigExtensionGP)
101
+ iDocument.createExtension(KHRDracoMeshCompression)
102
+ .setRequired(true)
103
+ .setEncoderOptions({...this._encoderOptions, ...dracoOptions ?? {}})
104
+
105
+ if (ops.exportExt === 'glb') {
106
+ gltf = await this._io.writeBinary(iDocument)
107
+ if (isFinite(bytes)) {
108
+ console.log('DRACO Compression ratio: ' + ((gltf as ArrayBuffer).byteLength / bytes).toFixed(5))
109
+ }
110
+ } else {
111
+ const jDoc = await this._io.writeJSON(iDocument)
112
+ gltf = jDoc.json
113
+ if (Object.values(jDoc.resources).filter(v => v).length > 0) {
114
+ console.warn('DRACOExporter: extra resources in resources not supported properly')
115
+ ;(gltf as any).resources = jDoc.resources
116
+ }
117
+ }
118
+
119
+ gltf.__isGLTFOutput = true
120
+ const blob = await super.parseAsync(gltf, ops) as any // this will just convert it to blob because __isGLTFOutput is set (checked in GLTFExporter2)
121
+ if (!blob) throw new Error('GLTFDracoExporter: blob is null')
122
+ blob.ext = 'glb'
123
+ ;(blob as any).__uncompressed = uncompressedBlob
124
+ return blob
125
+ } catch (e) {
126
+ if (throwOnError) throw e
127
+ console.error('Unable to compress glb with DRACO extension, fallback to uncompressed')
128
+ console.error(e)
129
+ return uncompressedBlob
130
+ }
131
+ }
132
+
133
+ addExtension(...extension: (typeof Extension)[]): this {
134
+ this._io.registerExtensions(extension)
135
+ return this
136
+ }
137
+ createAndAddExtension(name: string, textures?: Record<string, string|number>): this {
138
+ return this.addExtension(createGenericExtensionClass(name, textures))
139
+ }
140
+ createAndAddExtensions(extensions: [string, Record<string, string|number>|undefined][]): this {
141
+ return this.addExtension(...extensions.map(e=> createGenericExtensionClass(e[0], e[1])))
142
+ }
143
+ }
144
+
145
+ declare module 'threepipe'{
146
+ interface GLTFExporter2Options {
147
+ compress?: boolean
148
+ dracoOptions?: EncoderOptions
149
+ }
150
+ }
151
+
152
+ // for @gltf-transform/core
153
+ class ViewerJSONExtensionProperty extends ExtensionProperty {
154
+ readonly extensionName: string = GLTFViewerConfigExtension.ViewerConfigGLTFExtension
155
+ readonly parentTypes: string[] = [PropertyType.SCENE]
156
+ readonly propertyType: string = 'ViewerJSON'
157
+
158
+ // eslint-disable-next-line @typescript-eslint/naming-convention
159
+ protected init(): void {return}
160
+
161
+ }
162
+ export class GLTFViewerConfigExtensionGP extends Extension {
163
+ public readonly extensionName = GLTFViewerConfigExtension.ViewerConfigGLTFExtension
164
+ public static readonly EXTENSION_NAME = GLTFViewerConfigExtension.ViewerConfigGLTFExtension
165
+ private _viewerConfig: any = {}
166
+ // private _texturesRef: [any, Texture][] = []
167
+
168
+ read(context: ReaderContext): this {
169
+ this._viewerConfig = {}
170
+ context.jsonDoc.json.scenes?.forEach((sceneDef, sceneIndex)=>{
171
+ if (sceneDef.extensions && sceneDef.extensions[GLTFViewerConfigExtension.ViewerConfigGLTFExtension]) {
172
+ const prop = new ViewerJSONExtensionProperty(this.document.getGraph())
173
+ context.scenes[sceneIndex].setExtension(GLTFViewerConfigExtension.ViewerConfigGLTFExtension, prop)
174
+ this._viewerConfig = sceneDef.extensions[GLTFViewerConfigExtension.ViewerConfigGLTFExtension] as any
175
+ // prop.setExtras()
176
+
177
+ /*
178
+ const buffers = [] as any[]
179
+
180
+ Object.values(viewerConfig.resources).forEach((res: any) => {
181
+ Object.values(res).forEach((item: any) => {
182
+ if (!item.url) return
183
+ if (item.url.data?.image !== null) {
184
+ buffers.push(item.url)
185
+ }
186
+ })
187
+ })
188
+ const jsonDoc = context.jsonDoc
189
+ console.log(buffers)
190
+ for (const buffer of buffers) {
191
+ const img = buffer.data.image as number
192
+ const imageDef = jsonDoc.json.images![img]
193
+ const bufferViewDef = jsonDoc.json.bufferViews![imageDef.bufferView!]
194
+ const bufferDef = jsonDoc.json.buffers![bufferViewDef.buffer]
195
+ const bufferData = bufferDef.uri ? jsonDoc.resources[bufferDef.uri] : jsonDoc.resources[GLB_BUFFER]
196
+ const byteOffset = bufferViewDef.byteOffset || 0
197
+ const byteLength = bufferViewDef.byteLength
198
+ const imageData = bufferData.slice(byteOffset, byteOffset + byteLength)
199
+ const texture = this.document.createTexture(imageDef.name)
200
+ texture.setImage(imageData)
201
+ this._texturesRef.push([buffer, texture])
202
+ }
203
+ */
204
+
205
+
206
+ }
207
+ })
208
+ return this
209
+ }
210
+
211
+ write(context: WriterContext): this {
212
+ this.document.getRoot().listScenes().forEach((scene)=>{
213
+ const prop = scene.getExtension(GLTFViewerConfigExtension.ViewerConfigGLTFExtension)
214
+ if (prop) {
215
+ const sceneDef = context.jsonDoc.json.scenes?.[context.jsonDoc.json.scene || 0] // todo: get proper scene index, if working with multiple scenes
216
+ if (sceneDef && Object.keys(this._viewerConfig).length > 0) {
217
+ sceneDef.extensions = sceneDef.extensions || {}
218
+
219
+ /*
220
+ console.log(context.jsonDoc.json.images)
221
+ for (const [buffer, texture] of this._texturesRef) {
222
+ const imageDef = context.createPropertyDef(texture) as GLTF.IImage
223
+ context.createImageData(imageDef, texture.getImage()!, texture)
224
+ buffer.data.image = context.jsonDoc.json.images!.push(imageDef) - 1
225
+ context.imageIndexMap.set(texture, buffer.data.image)
226
+ }
227
+ console.log(context.jsonDoc.json)
228
+ */
229
+
230
+ sceneDef.extensions[GLTFViewerConfigExtension.ViewerConfigGLTFExtension] = this._viewerConfig
231
+
232
+ // this._texturesRef = []
233
+ this._viewerConfig = {}
234
+
235
+ }
236
+ }
237
+ })
238
+
239
+ return this
240
+ }
241
+
242
+ required = true
243
+ }
244
+
245
+ class GenericExtensionProperty extends ExtensionProperty<any> {
246
+ readonly extensionName: string
247
+ readonly parentTypes: string[] = [PropertyType.MATERIAL, PropertyType.MESH, PropertyType.NODE, PropertyType.SCENE]
248
+ readonly propertyType: string = 'GenericExtension'
249
+ textures: Record<string, [TextureInfo, Texture|null]> = {}
250
+
251
+ addTexture(key: string, texInfo: TextureInfo, texture: Texture | null, channels = 0x1111) {
252
+ this.setRef(key, texture, {channels})
253
+ this.textures[key] = [texInfo, texture]
254
+ }
255
+
256
+ constructor(graph: Graph<Property>, name: string, extensionName: string) {
257
+ super(graph, name)
258
+ this.extensionName = extensionName
259
+ }
260
+
261
+ // eslint-disable-next-line @typescript-eslint/naming-convention
262
+ protected init(): void {return}
263
+ }
264
+
265
+ // see transmission extension for reference
266
+ abstract class GenericExtension extends Extension {
267
+ abstract readonly extensionName: string
268
+
269
+ textureChannels: Record<string, number> = {}
270
+
271
+ read(context: ReaderContext): this {
272
+ const jsonDoc = context.jsonDoc
273
+ // console.log(jsonDoc)
274
+ const materialDefs = jsonDoc.json.materials || []
275
+ const textureDefs = jsonDoc.json.textures || []
276
+ materialDefs.forEach((materialDef, materialIndex) => {
277
+ if (materialDef.extensions && materialDef.extensions[this.extensionName]) {
278
+ const paramsExt = new GenericExtensionProperty(this.document.getGraph(), '', this.extensionName)
279
+ context.materials[materialIndex].setExtension(this.extensionName, paramsExt)
280
+ const paramsExtDef = materialDef.extensions[this.extensionName] as Record<string, any>
281
+ const paramsExtDef2 = {...paramsExtDef}
282
+
283
+ for (const [key, value] of Object.entries(paramsExtDef2)) {
284
+ if (typeof value?.index === 'number') { // this is a texture...
285
+ const textureInfoDef = value
286
+ const source = textureDefs[textureInfoDef.index]?.source
287
+ if (typeof source !== 'number') {
288
+ console.warn('GLTF Pipeline: source texture not found for texture info', textureInfoDef)
289
+ continue
290
+ }
291
+ const texture = context.textures[source]
292
+ const texInfo = new TextureInfo(this.document.getGraph())
293
+ const channels = this.textureChannels[key] ?? 0x1111
294
+ paramsExt.addTexture(key, texInfo, texture, channels)
295
+ context.setTextureInfo(texInfo, textureInfoDef)
296
+ delete paramsExtDef2[key]
297
+ }
298
+ }
299
+
300
+ paramsExt.setExtras(paramsExtDef2)
301
+ // console.log({...paramsExtDef})
302
+ }
303
+ })
304
+ const meshDefs = jsonDoc.json.meshes || []
305
+ meshDefs.forEach((meshDef, meshIndex) => {
306
+ if (meshDef.extensions && meshDef.extensions[this.extensionName]) {
307
+ const paramsExt = new GenericExtensionProperty(this.document.getGraph(), '', this.extensionName)
308
+ context.meshes[meshIndex].setExtension(this.extensionName, paramsExt)
309
+ const paramsExtDef = meshDef.extensions[this.extensionName] as Record<string, any>
310
+ paramsExt.setExtras(paramsExtDef)
311
+ }
312
+ })
313
+ const nodeDefs = jsonDoc.json.nodes || []
314
+ nodeDefs.forEach((nodeDef, nodeIndex) => {
315
+ if (nodeDef.extensions && nodeDef.extensions[this.extensionName]) {
316
+ const paramsExt = new GenericExtensionProperty(this.document.getGraph(), '', this.extensionName)
317
+ context.nodes[nodeIndex].setExtension(this.extensionName, paramsExt)
318
+ const paramsExtDef = nodeDef.extensions[this.extensionName] as Record<string, any>
319
+ paramsExt.setExtras(paramsExtDef)
320
+ // console.log(paramsExtDef)
321
+ }
322
+ })
323
+ const sceneDefs = jsonDoc.json.scenes || []
324
+ sceneDefs.forEach((sceneDef, sceneIndex) => {
325
+ if (sceneDef.extensions && sceneDef.extensions[this.extensionName]) {
326
+ const paramsExt = new GenericExtensionProperty(this.document.getGraph(), '', this.extensionName)
327
+ context.scenes[sceneIndex].setExtension(this.extensionName, paramsExt)
328
+ const paramsExtDef = sceneDef.extensions[this.extensionName] as Record<string, any>
329
+ paramsExt.setExtras(paramsExtDef)
330
+ // console.log(paramsExtDef)
331
+ }
332
+ })
333
+
334
+ return this
335
+ }
336
+
337
+ write(context: WriterContext): this {
338
+ const jsonDoc = context.jsonDoc
339
+ this.document.getRoot()
340
+ .listMaterials()
341
+ .forEach((material) => {
342
+ const paramsExt = material.getExtension<GenericExtensionProperty>(this.extensionName)
343
+ // console.log(paramsExt)
344
+ if (paramsExt) {
345
+ const materialIndex = context.materialIndexMap.get(material)!
346
+ const materialDef = jsonDoc.json.materials![materialIndex]
347
+ materialDef.extensions = materialDef.extensions || {}
348
+ const extensionDef = paramsExt.getExtras()
349
+ const extensionDef2 = {...extensionDef}
350
+
351
+ // console.log(paramsExt.textures)
352
+ for (const [key, value] of Object.entries(paramsExt.textures)) {
353
+ const textureInfo = value[0]
354
+ const textureLink = value[1]
355
+ const texture = textureLink
356
+
357
+ if (texture)
358
+ extensionDef2[key] = context.createTextureInfoDef(texture, textureInfo)
359
+
360
+ // console.log(texture)
361
+
362
+ }
363
+ // console.log(extensionDef2)
364
+
365
+ materialDef.extensions[this.extensionName] = extensionDef2
366
+ }
367
+ })
368
+ this.document.getRoot()
369
+ .listMeshes()
370
+ .forEach((mesh) => {
371
+ const paramsExt = mesh.getExtension<GenericExtensionProperty>(this.extensionName)
372
+ if (paramsExt) {
373
+ const meshIndex = context.meshIndexMap.get(mesh)!
374
+ const meshDef = jsonDoc.json.meshes![meshIndex]
375
+ meshDef.extensions = meshDef.extensions || {}
376
+ meshDef.extensions[this.extensionName] = paramsExt.getExtras()
377
+ }
378
+ })
379
+ this.document.getRoot()
380
+ .listNodes()
381
+ .forEach((node) => {
382
+ const paramsExt = node.getExtension<GenericExtensionProperty>(this.extensionName)
383
+ if (paramsExt) {
384
+ const nodeIndex = context.nodeIndexMap.get(node)!
385
+ const nodeDef = jsonDoc.json.nodes![nodeIndex]
386
+ nodeDef.extensions = nodeDef.extensions || {}
387
+ nodeDef.extensions[this.extensionName] = paramsExt.getExtras()
388
+ }
389
+ })
390
+ this.document.getRoot()
391
+ .listScenes()
392
+ .forEach((scene) => {
393
+ const paramsExt = scene.getExtension<GenericExtensionProperty>(this.extensionName)
394
+ if (paramsExt) {
395
+ const sceneIndex = context.jsonDoc.json.scene || 0 // todo: get proper scene index, if working with multiple scenes, this will do the default one.
396
+ const sceneDef = jsonDoc.json.scenes![sceneIndex]
397
+ if (!sceneDef) return
398
+ sceneDef.extensions = sceneDef.extensions || {}
399
+ sceneDef.extensions[this.extensionName] = paramsExt.getExtras()
400
+ }
401
+ })
402
+
403
+ return this
404
+ }
405
+
406
+ }
407
+
408
+ function stringToChannel(s: string) {
409
+ let r = 0
410
+ if (s.includes('R')) r |= TextureChannel.R
411
+ if (s.includes('G')) r |= TextureChannel.G
412
+ if (s.includes('B')) r |= TextureChannel.B
413
+ if (s.includes('A')) r |= TextureChannel.A
414
+ return r
415
+ }
416
+
417
+ export function createGenericExtensionClass(name: string, textures?: Record<string, string|number>): typeof GenericExtension {
418
+ return class extends GenericExtension {
419
+ public static readonly EXTENSION_NAME = name
420
+ readonly extensionName = name
421
+ textureChannels: Record<string, number> = !textures ? {} : Object.fromEntries(
422
+ Object.entries(textures)
423
+ .map(([k, v])=>
424
+ [k, typeof v === 'number' ? v : stringToChannel(v)])
425
+ )
426
+ }
427
+ }
@@ -1,126 +1,25 @@
1
- import {AViewerPluginSync, DRACOLoader2, GLTFBinaryExtension, GLTFLoader2, GLTFParser, ThreeViewer} from 'threepipe'
2
- import {Extension, WebIO} from '@gltf-transform/core'
3
- import {ALL_EXTENSIONS} from '@gltf-transform/extensions'
4
- import {GLTFDracoExportPlugin} from './GLTFDracoExportPlugin'
5
- import {metalRough} from '@gltf-transform/functions'
1
+ import {PlatformIO, WebIO} from '@gltf-transform/core'
2
+ import {GLTFSpecGlossinessConverterPluginBase, gltfKhrPbrSpecularGlossinessConverterBase} from './GLTFSpecGlossinessConverterPluginBase'
3
+
4
+ // Re-export base for CLI consumers
5
+ export {GLTFSpecGlossinessConverterPluginBase, gltfKhrPbrSpecularGlossinessConverterBase} from './GLTFSpecGlossinessConverterPluginBase'
6
6
 
7
7
  /**
8
8
  * GLTFSpecGlossinessConverterPlugin
9
9
  *
10
- * Plugin that adds a gltf loader extension that automatically converts GLTF files with specular glossiness materials ([KHR_materials_pbrSpecularGlossiness](https://kcoley.github.io/glTF/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/)) to metallic roughness during import.
10
+ * Browser-specific plugin that uses WebIO for spec-gloss to metal-rough conversion.
11
11
  *
12
12
  * To use this plugin, simply add it to the viewer and import a file with specular glossiness materials.
13
13
  * If `confirm` is set to true, a confirmation dialog will be shown before the conversion.
14
14
  */
15
- export class GLTFSpecGlossinessConverterPlugin extends AViewerPluginSync {
16
- enabled = true
17
-
18
- static readonly PluginType = 'GLTFSpecGlossinessConverterPlugin'
19
- toJSON: any = undefined
20
-
21
- // dependencies = [GLTFDracoExportPlugin]
22
-
23
- /**
24
- * Whether to show the confirmation dialog when loading a GLTF file with specular glossiness materials.
25
- * If set to false, the conversion will be done automatically without confirmation.
26
- * To disable the conversion entirely, disable the plugin.
27
- */
28
- confirm = true
29
- /**
30
- * The message to show in the confirmation dialog when loading a GLTF file with specular glossiness materials.
31
- */
32
- confirmMessage = 'GLTF Load: This file includes specular glossiness materials, do you want to convert it to metallic roughness before load?'
33
-
34
- private _loaderCreate({loader}: {loader: GLTFLoader2}) {
35
- if (!loader.isGLTFLoader2) return
36
-
37
- const ep = this._viewer?.getPlugin(GLTFDracoExportPlugin)
38
- if (!ep) {
39
- console.error('GLTFSpecGlossinessConverterPlugin requires GLTFDracoExportPlugin in the plugin for extra extensions')
40
- }
41
- loader.register(gltfKhrPbrSpecularGlossinessConverter(async(m)=>!this.enabled ? false : !this.confirm ? true : this._viewer?.dialog.confirm(this.confirmMessage || m) ?? true, ep?.gltfTransformExtensions))
42
- }
43
-
44
- constructor() {
45
- super()
46
- this._loaderCreate = this._loaderCreate.bind(this)
47
- }
48
-
49
- onAdded(v: ThreeViewer) {
50
- super.onAdded(v)
51
- v.assetManager.importer.addEventListener('loaderCreate', this._loaderCreate as any)
52
- }
53
-
54
- onRemove(v: ThreeViewer) {
55
- v.assetManager.importer.removeEventListener('loaderCreate', this._loaderCreate as any)
56
- return super.onRemove(v)
15
+ export class GLTFSpecGlossinessConverterPlugin extends GLTFSpecGlossinessConverterPluginBase {
16
+ protected _createIO(): PlatformIO {
17
+ return new WebIO()
57
18
  }
58
19
  }
59
20
 
60
-
61
- export const gltfKhrPbrSpecularGlossinessConverter = (confirm?: (s: string)=>Promise<boolean>, extensions?: (typeof Extension)[])=>(parser: GLTFParser)=>({
62
- name: 'KHR_materials_pbrSpecularGlossiness',
63
- beforeRoot: async() => {
64
- try {
65
- if (!parser.json.extensionsUsed || !parser.json.extensionsUsed.includes('KHR_materials_pbrSpecularGlossiness')) return
66
- const doConfirm = parser.importOptions?.confirmSpecGlossConversion ?? true
67
- if (doConfirm && confirm && !await confirm('Convert KHR_materials_pbrSpecularGlossiness to KHR_materials_pbrMetallicRoughness?')) return
68
-
69
- const json = parser.json
70
- const io = new WebIO().registerExtensions(ALL_EXTENSIONS).registerExtensions(extensions ?? [])
71
-
72
- if (parser.extensions.KHR_draco_mesh_compression) {
73
- const dracoLoader = parser.extensions.KHR_draco_mesh_compression.dracoLoader as DRACOLoader2
74
- if (!dracoLoader.isDRACOLoader2) {
75
- console.error('gltfKhrPbrSpecularGlossinessConverter: DRACOLoader2 required')
76
- return
77
- }
78
- const decoder = await dracoLoader.initDecoder()
79
- io.registerDependencies({
80
- // 'draco3d.encoder': libs[0],
81
- ['draco3d.decoder']: decoder,
82
- })
83
- }
84
-
85
- const document = await io.readJSON({
86
- json, resources: {
87
- ['@glb.bin']: new Uint8Array(parser.extensions.KHR_binary_glTF?.body),
88
- },
89
- })
90
-
91
-
92
- // Convert materials.
93
- await document.transform(metalRough())
94
-
95
- // no need to compress
96
- if (parser.extensions.KHR_draco_mesh_compression) {
97
- document.getRoot().listExtensionsUsed().find(e => e.extensionName === 'KHR_draco_mesh_compression')?.dispose()
98
- }
99
-
100
- // Write back to GLB.
101
- const res = await io.writeBinary(document)
102
- const binaryExtension = new GLTFBinaryExtension(res.buffer as any)
103
- parser.extensions.KHR_binary_glTF = binaryExtension
104
- parser.json = JSON.parse(binaryExtension.content)
105
-
106
- // this doesn't work for some reason
107
- // const res = await io.writeJSON(document, {format: Format.GLB})
108
- // parser.extensions.KHR_binary_glTF.data = res.resources['@glb.bin'].buffer as any
109
- // parser.extensions.KHR_binary_glTF.content = res.json
110
- // parser.json = res.json
111
- } catch (e) {
112
- console.error(e)
113
- return
114
- }
115
- },
116
- })
117
-
118
- declare module 'threepipe'{
119
- interface ImportAddOptions{
120
- /**
121
- * Whether to confirm the conversion of specular glossiness materials to metallic roughness.
122
- * @default true
123
- */
124
- confirmSpecGlossConversion?: boolean
125
- }
126
- }
21
+ /**
22
+ * @deprecated Use {@link gltfKhrPbrSpecularGlossinessConverterBase} with an IO factory instead.
23
+ */
24
+ export const gltfKhrPbrSpecularGlossinessConverter = (confirm?: (s: string)=>Promise<boolean>, extensions?: (typeof import('@gltf-transform/core').Extension)[]) =>
25
+ gltfKhrPbrSpecularGlossinessConverterBase(() => new WebIO(), confirm, extensions)