@threepipe/plugin-gltf-transform 0.1.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.
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@threepipe/plugin-gltf-transform",
3
+ "description": "Utility plugins for threepipe using gltf-transform to optimize/compress glTF files.",
4
+ "version": "0.1.0",
5
+ "dependencies": {},
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "module": "dist/index.mjs",
9
+ "types": "dist/index.d.ts",
10
+ "files": [
11
+ "dist",
12
+ "src"
13
+ ],
14
+ "author": "repalash <palash@shaders.app>",
15
+ "license": "Apache-2.0",
16
+ "keywords": [
17
+ "three",
18
+ "three.js",
19
+ "threepipe",
20
+ "vite",
21
+ "plugin"
22
+ ],
23
+ "bugs": {
24
+ "url": "https://github.com/repalash/threepipe/issues"
25
+ },
26
+ "homepage": "https://github.com/repalash/threepipe#readme",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git://github.com/repalash/threepipe.git"
30
+ },
31
+ "peerDependencies": {
32
+ "threepipe": "^0.0.32"
33
+ }
34
+ }
@@ -0,0 +1,161 @@
1
+ import {
2
+ AssetExporterPlugin,
3
+ AViewerPluginSync,
4
+ ClearcoatTintPlugin,
5
+ CustomBumpMapPlugin,
6
+ DRACOLoader2,
7
+ generateUUID,
8
+ GLTFLightExtrasExtension,
9
+ GLTFMaterialExtrasExtension,
10
+ GLTFMaterialsAlphaMapExtension,
11
+ GLTFMaterialsBumpMapExtension,
12
+ GLTFMaterialsDisplacementMapExtension,
13
+ GLTFMaterialsLightMapExtension,
14
+ GLTFObject3DExtrasExtension,
15
+ NoiseBumpMaterialPlugin,
16
+ ThreeViewer,
17
+ } from 'threepipe'
18
+ import {GLTFDracoExporter} from './GLTFDracoExporter'
19
+ import {UiObjectConfig} from 'uiconfig.js'
20
+ import {EncoderOptions} from '@gltf-transform/extensions/dist/khr-draco-mesh-compression/encoder'
21
+
22
+ export enum EncoderMethod {
23
+ EDGEBREAKER = 1,
24
+ SEQUENTIAL = 0
25
+ }
26
+
27
+ /**
28
+ * GLTF Draco Export Plugin
29
+ *
30
+ * Overloads the default gltf exporter in the asset manager with GLTFDracoExporter. When exporting with compress = true, the output will be compressed.
31
+ * Note - Only `glb` supported right now.
32
+ *
33
+ * @category Plugins
34
+ */
35
+ export class GLTFDracoExportPlugin extends AViewerPluginSync<''> {
36
+ public static readonly PluginType = 'GLTFDracoExportPlugin'
37
+ enabled = true
38
+
39
+ onAdded(viewer: ThreeViewer): void {
40
+ super.onAdded(viewer)
41
+ const importer = viewer.assetManager.importer
42
+ const exporter = viewer.assetManager.exporter
43
+
44
+ const glbExporter = exporter.getExporter('glb')
45
+ if (glbExporter) exporter.removeExporter(glbExporter)
46
+
47
+ // todo remove exporter and add back the old one on plugin remove.
48
+ exporter.addExporter({
49
+ ...glbExporter || {
50
+ ext: ['glb', 'gltf'],
51
+ extensions: [],
52
+ }, // for extensions
53
+ ctor: (_, _exporter) => {
54
+ const tempFile = generateUUID() + '.drc' // dummy
55
+ const ex = new GLTFDracoExporter({},
56
+ // todo unregister on dispose
57
+ importer.registerFile(tempFile) as DRACOLoader2)
58
+ ex.setup(viewer, _exporter.extensions)
59
+ ex.createAndAddExtension(GLTFMaterialsBumpMapExtension.WebGiMaterialsBumpMapExtension, {
60
+ bumpTexture: 'R',
61
+ })
62
+ ex.createAndAddExtension(GLTFMaterialsLightMapExtension.WebGiMaterialsLightMapExtension, {
63
+ lightMapTexture: 'RGB',
64
+ })
65
+ ex.createAndAddExtension(GLTFMaterialsAlphaMapExtension.WebGiMaterialsAlphaMapExtension, {
66
+ alphaTexture: 'G',
67
+ })
68
+ ex.createAndAddExtension(GLTFMaterialsDisplacementMapExtension.WebGiMaterialsDisplacementMapExtension, {
69
+ displacementTexture: 'R',
70
+ })
71
+ ex.createAndAddExtension(CustomBumpMapPlugin.CUSTOM_BUMP_MAP_GLTF_EXTENSION, {
72
+ customBumpMap: 'RGB',
73
+ })
74
+ ex.createAndAddExtension(GLTFLightExtrasExtension.WebGiLightExtrasExtension)
75
+ ex.createAndAddExtension(GLTFObject3DExtrasExtension.WebGiObject3DExtrasExtension)
76
+ ex.createAndAddExtension(GLTFMaterialExtrasExtension.WebGiMaterialExtrasExtension)
77
+ ex.createAndAddExtension(ClearcoatTintPlugin.CLEARCOAT_TINT_GLTF_EXTENSION)
78
+ ex.createAndAddExtension(NoiseBumpMaterialPlugin.NOISE_BUMP_MATERIAL_GLTF_EXTENSION)
79
+ // todo port
80
+ // DiamondMaterialExtension
81
+ // AnimationMarkersExtension
82
+ // AnisotropyMaterialExtension
83
+ // ThinFilmLayerMaterialExtension
84
+ // TriplanarMappingMaterialExtension
85
+ // SSBevelMaterialExtension
86
+ return ex
87
+ },
88
+ })
89
+
90
+ // for ui
91
+ const exportPlugin = viewer.getPlugin(AssetExporterPlugin)
92
+ if (exportPlugin) {
93
+ Object.assign(exportPlugin.exportOptions, {
94
+ compress: false,
95
+ dracoOptions: {
96
+ encodeSpeed: 5,
97
+ method: EncoderMethod.EDGEBREAKER,
98
+ quantizationVolume: 'mesh',
99
+ quantizationBits: {
100
+ ['POSITION']: 14,
101
+ ['NORMAL']: 10,
102
+ ['COLOR']: 8,
103
+ ['TEX_COORD']: 12,
104
+ ['GENERIC']: 12,
105
+ },
106
+ } as EncoderOptions,
107
+ })
108
+ const exportOptions = exportPlugin.uiConfig.children?.find(c => (c as UiObjectConfig).label === 'GLB Export') as UiObjectConfig
109
+ if (exportOptions) {
110
+ exportOptions.children = [this._makeUi(exportPlugin), ...exportOptions.children || []]
111
+ } else {
112
+ console.warn('GLTFDracoExportPlugin: Unable to setup UI')
113
+ }
114
+ }
115
+ }
116
+
117
+ protected _makeUi = (exporter: AssetExporterPlugin)=>[
118
+ {
119
+ type: 'checkbox',
120
+ label: 'DRACO Compress',
121
+ property: [exporter.exportOptions, 'compress'],
122
+ onChange: ()=>exporter.uiConfig.uiRefresh?.(true),
123
+ },
124
+ {
125
+ type: 'folder',
126
+ hidden: ()=>!exporter.exportOptions.compress,
127
+ label: 'DRACO Options',
128
+ children: [
129
+ {
130
+ type: 'slider',
131
+ label: 'Encode Speed',
132
+ bounds: [1, 10],
133
+ property: [exporter.exportOptions.dracoOptions, 'encodeSpeed'],
134
+ },
135
+ {
136
+ type: 'dropdown',
137
+ label: 'Encoder Method',
138
+ property: [exporter.exportOptions.dracoOptions, 'method'],
139
+ children: Object.entries(EncoderMethod).map(([k, v]) => ({label: k, value: v})),
140
+ },
141
+ {
142
+ type: 'dropdown',
143
+ label: 'Quantization Volume',
144
+ property: [exporter.exportOptions.dracoOptions, 'quantizationVolume'],
145
+ children: ['mesh', 'scene', 'bbox'].map(v => ({label: v})),
146
+ },
147
+ {
148
+ type: 'folder',
149
+ label: 'Quantization Bits',
150
+ children: Object.keys(exporter.exportOptions.dracoOptions?.quantizationBits || {}).map(k => ({
151
+ type: 'slider',
152
+ label: k,
153
+ bounds: [1, 16],
154
+ stepSize: 1,
155
+ property: [exporter.exportOptions.dracoOptions?.quantizationBits, k],
156
+ })),
157
+ },
158
+ ],
159
+ },
160
+ ]
161
+ }
@@ -0,0 +1,418 @@
1
+ import {
2
+ Extension,
3
+ ExtensionProperty,
4
+ GLTF,
5
+ Graph,
6
+ Property,
7
+ PropertyType,
8
+ ReaderContext,
9
+ Texture,
10
+ TextureChannel,
11
+ TextureInfo,
12
+ WebIO,
13
+ WriterContext,
14
+ } from '@gltf-transform/core'
15
+ import {EncoderOptions} from '@gltf-transform/extensions/dist/khr-draco-mesh-compression/encoder'
16
+ import {ALL_EXTENSIONS, KHRDracoMeshCompression} from '@gltf-transform/extensions'
17
+ import {DRACOLoader2, GLTFExporter2, GLTFExporter2Options, GLTFViewerConfigExtension, IExportParser} from 'threepipe'
18
+
19
+ /**
20
+ * GLTF Draco Exporter
21
+ *
22
+ * Extension of GLTFExporter2 that runs the output through gltf-transform for draco compression.
23
+ */
24
+ export class GLTFDracoExporter extends GLTFExporter2 implements IExportParser {
25
+ public loader?: DRACOLoader2 // required for loading draco libs.
26
+ private _io: WebIO
27
+ private _loadedLibs = false
28
+ private _encoderOptions: EncoderOptions
29
+
30
+ constructor(encoderOptions?: EncoderOptions, loader?: DRACOLoader2) {
31
+ super()
32
+ encoderOptions = encoderOptions || {
33
+ method: KHRDracoMeshCompression.EncoderMethod.EDGEBREAKER,
34
+ encodeSpeed: 5,
35
+ }
36
+ this._io = new WebIO().registerExtensions(ALL_EXTENSIONS)
37
+ .registerExtensions([
38
+ GLTFViewerConfigExtensionGP,
39
+ ])
40
+ this._encoderOptions = encoderOptions
41
+
42
+ if (loader) {
43
+ this.loader = loader
44
+ this.loader.setDecoderConfig({type: 'js'}) // todo: hack for now.
45
+ this.loader.preload(true, true)
46
+ }
47
+
48
+ }
49
+
50
+ preload(): this {
51
+ this._loadLibs()
52
+ return this
53
+ }
54
+
55
+ private async _loadLibs() {
56
+ if (this._loadedLibs || !this.loader) return
57
+
58
+ const libs = await Promise.all([
59
+ this.loader.initEncoder(),
60
+ this.loader.initDecoder(),
61
+ ])
62
+ this._io.registerDependencies({
63
+ ['draco3d.encoder']: libs[0],
64
+ ['draco3d.decoder']: libs[1], // only required if we are loading a draco compressed gltf
65
+ })
66
+ this._loadedLibs = true
67
+
68
+ }
69
+
70
+ async parseAsync(obj: any, {compress = false, dracoOptions, ...options}: {compress: boolean, dracoOptions?: EncoderOptions} & GLTFExporter2Options): Promise<Blob> {
71
+ if (!this.loader) {
72
+ console.error('GLTFDracoExporter: No DRACOLoader2 instance provided')
73
+ return super.parseAsync(obj, options)
74
+ }
75
+ await this._loadLibs()
76
+
77
+ const ops = {...options}
78
+ if (compress) {
79
+ // externalImagesInExtras: this is required because gltf-transform doesn't support external images in glb
80
+ // see https://github.com/donmccurdy/glTF-Transform/discussions/644
81
+ ops.externalImagesInExtras = true
82
+ }
83
+
84
+ const uncompressed = await new Promise((resolve, reject) => this.parse(obj, resolve, reject, ops)) as any
85
+
86
+ const uncompressedBlob = await super.parseAsync(uncompressed, ops)
87
+ if (!compress) return uncompressedBlob
88
+
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
+ }
126
+
127
+ addExtension(extension: typeof Extension): this {
128
+ this._io.registerExtensions([extension])
129
+ return this
130
+ }
131
+ createAndAddExtension(name: string, textures?: Record<string, string|number>): this {
132
+ return this.addExtension(createGenericExtensionClass(name, textures))
133
+ }
134
+ }
135
+
136
+ declare module 'threepipe'{
137
+ interface GLTFExporter2Options {
138
+ compress?: boolean
139
+ dracoOptions?: EncoderOptions
140
+ }
141
+ }
142
+
143
+ // for @gltf-transform/core
144
+ class ViewerJSONExtensionProperty extends ExtensionProperty {
145
+ readonly extensionName: string = GLTFViewerConfigExtension.ViewerConfigGLTFExtension
146
+ readonly parentTypes: string[] = [PropertyType.SCENE]
147
+ readonly propertyType: string = 'ViewerJSON'
148
+
149
+ // eslint-disable-next-line @typescript-eslint/naming-convention
150
+ protected init(): void {return}
151
+
152
+ }
153
+ class GLTFViewerConfigExtensionGP extends Extension {
154
+ public readonly extensionName = GLTFViewerConfigExtension.ViewerConfigGLTFExtension
155
+ public static readonly EXTENSION_NAME = GLTFViewerConfigExtension.ViewerConfigGLTFExtension
156
+ private _viewerConfig: any = {}
157
+ // private _texturesRef: [any, Texture][] = []
158
+
159
+ read(context: ReaderContext): this {
160
+ this._viewerConfig = {}
161
+ context.jsonDoc.json.scenes?.forEach((sceneDef, sceneIndex)=>{
162
+ if (sceneDef.extensions && sceneDef.extensions[GLTFViewerConfigExtension.ViewerConfigGLTFExtension]) {
163
+ const prop = new ViewerJSONExtensionProperty(this.document.getGraph())
164
+ context.scenes[sceneIndex].setExtension(GLTFViewerConfigExtension.ViewerConfigGLTFExtension, prop)
165
+ this._viewerConfig = sceneDef.extensions[GLTFViewerConfigExtension.ViewerConfigGLTFExtension] as any
166
+ // prop.setExtras()
167
+
168
+ /*
169
+ const buffers = [] as any[]
170
+
171
+ Object.values(viewerConfig.resources).forEach((res: any) => {
172
+ Object.values(res).forEach((item: any) => {
173
+ if (!item.url) return
174
+ if (item.url.data?.image !== null) {
175
+ buffers.push(item.url)
176
+ }
177
+ })
178
+ })
179
+ const jsonDoc = context.jsonDoc
180
+ console.log(buffers)
181
+ for (const buffer of buffers) {
182
+ const img = buffer.data.image as number
183
+ const imageDef = jsonDoc.json.images![img]
184
+ const bufferViewDef = jsonDoc.json.bufferViews![imageDef.bufferView!]
185
+ const bufferDef = jsonDoc.json.buffers![bufferViewDef.buffer]
186
+ const bufferData = bufferDef.uri ? jsonDoc.resources[bufferDef.uri] : jsonDoc.resources[GLB_BUFFER]
187
+ const byteOffset = bufferViewDef.byteOffset || 0
188
+ const byteLength = bufferViewDef.byteLength
189
+ const imageData = bufferData.slice(byteOffset, byteOffset + byteLength)
190
+ const texture = this.document.createTexture(imageDef.name)
191
+ texture.setImage(imageData)
192
+ this._texturesRef.push([buffer, texture])
193
+ }
194
+ */
195
+
196
+
197
+ }
198
+ })
199
+ return this
200
+ }
201
+
202
+ write(context: WriterContext): this {
203
+ this.document.getRoot().listScenes().forEach((scene)=>{
204
+ const prop = scene.getExtension(GLTFViewerConfigExtension.ViewerConfigGLTFExtension)
205
+ if (prop) {
206
+ const sceneDef = context.jsonDoc.json.scenes?.[context.jsonDoc.json.scene || 0] // todo: get proper scene index, if working with multiple scenes
207
+ if (sceneDef && Object.keys(this._viewerConfig).length > 0) {
208
+ sceneDef.extensions = sceneDef.extensions || {}
209
+
210
+ /*
211
+ console.log(context.jsonDoc.json.images)
212
+ for (const [buffer, texture] of this._texturesRef) {
213
+ const imageDef = context.createPropertyDef(texture) as GLTF.IImage
214
+ context.createImageData(imageDef, texture.getImage()!, texture)
215
+ buffer.data.image = context.jsonDoc.json.images!.push(imageDef) - 1
216
+ context.imageIndexMap.set(texture, buffer.data.image)
217
+ }
218
+ console.log(context.jsonDoc.json)
219
+ */
220
+
221
+ sceneDef.extensions[GLTFViewerConfigExtension.ViewerConfigGLTFExtension] = this._viewerConfig
222
+
223
+ // this._texturesRef = []
224
+ this._viewerConfig = {}
225
+
226
+ }
227
+ }
228
+ })
229
+
230
+ return this
231
+ }
232
+
233
+ required = true
234
+ }
235
+
236
+ class GenericExtensionProperty extends ExtensionProperty<any> {
237
+ readonly extensionName: string
238
+ readonly parentTypes: string[] = [PropertyType.MATERIAL, PropertyType.MESH, PropertyType.NODE, PropertyType.SCENE]
239
+ readonly propertyType: string = 'GenericExtension'
240
+ textures: Record<string, [TextureInfo, Texture|null]> = {}
241
+
242
+ addTexture(key: string, texInfo: TextureInfo, texture: Texture | null, channels = 0x1111) {
243
+ this.setRef(key, texture, {channels})
244
+ this.textures[key] = [texInfo, texture]
245
+ }
246
+
247
+ constructor(graph: Graph<Property>, name: string, extensionName: string) {
248
+ super(graph, name)
249
+ this.extensionName = extensionName
250
+ }
251
+
252
+ // eslint-disable-next-line @typescript-eslint/naming-convention
253
+ protected init(): void {return}
254
+ }
255
+
256
+ // see transmission extension for reference
257
+ abstract class GenericExtension extends Extension {
258
+ abstract readonly extensionName: string
259
+
260
+ textureChannels: Record<string, number> = {}
261
+
262
+ read(context: ReaderContext): this {
263
+ const jsonDoc = context.jsonDoc
264
+ // console.log(jsonDoc)
265
+ const materialDefs = jsonDoc.json.materials || []
266
+ const textureDefs = jsonDoc.json.textures || []
267
+ materialDefs.forEach((materialDef, materialIndex) => {
268
+ if (materialDef.extensions && materialDef.extensions[this.extensionName]) {
269
+ const paramsExt = new GenericExtensionProperty(this.document.getGraph(), '', this.extensionName)
270
+ context.materials[materialIndex].setExtension(this.extensionName, paramsExt)
271
+ const paramsExtDef = materialDef.extensions[this.extensionName] as Record<string, any>
272
+ const paramsExtDef2 = {...paramsExtDef}
273
+
274
+ for (const [key, value] of Object.entries(paramsExtDef2)) {
275
+ if (typeof value?.index === 'number') { // this is a texture...
276
+ const textureInfoDef = value
277
+ const source = textureDefs[textureInfoDef.index]?.source
278
+ if (typeof source !== 'number') {
279
+ console.warn('GLTF Pipeline: source texture not found for texture info', textureInfoDef)
280
+ continue
281
+ }
282
+ const texture = context.textures[source]
283
+ const texInfo = new TextureInfo(this.document.getGraph())
284
+ const channels = this.textureChannels[key] ?? 0x1111
285
+ paramsExt.addTexture(key, texInfo, texture, channels)
286
+ context.setTextureInfo(texInfo, textureInfoDef)
287
+ delete paramsExtDef2[key]
288
+ }
289
+ }
290
+
291
+ paramsExt.setExtras(paramsExtDef2)
292
+ // console.log({...paramsExtDef})
293
+ }
294
+ })
295
+ const meshDefs = jsonDoc.json.meshes || []
296
+ meshDefs.forEach((meshDef, meshIndex) => {
297
+ if (meshDef.extensions && meshDef.extensions[this.extensionName]) {
298
+ const paramsExt = new GenericExtensionProperty(this.document.getGraph(), '', this.extensionName)
299
+ context.meshes[meshIndex].setExtension(this.extensionName, paramsExt)
300
+ const paramsExtDef = meshDef.extensions[this.extensionName] as Record<string, any>
301
+ paramsExt.setExtras(paramsExtDef)
302
+ }
303
+ })
304
+ const nodeDefs = jsonDoc.json.nodes || []
305
+ nodeDefs.forEach((nodeDef, nodeIndex) => {
306
+ if (nodeDef.extensions && nodeDef.extensions[this.extensionName]) {
307
+ const paramsExt = new GenericExtensionProperty(this.document.getGraph(), '', this.extensionName)
308
+ context.nodes[nodeIndex].setExtension(this.extensionName, paramsExt)
309
+ const paramsExtDef = nodeDef.extensions[this.extensionName] as Record<string, any>
310
+ paramsExt.setExtras(paramsExtDef)
311
+ // console.log(paramsExtDef)
312
+ }
313
+ })
314
+ const sceneDefs = jsonDoc.json.scenes || []
315
+ sceneDefs.forEach((sceneDef, sceneIndex) => {
316
+ if (sceneDef.extensions && sceneDef.extensions[this.extensionName]) {
317
+ const paramsExt = new GenericExtensionProperty(this.document.getGraph(), '', this.extensionName)
318
+ context.scenes[sceneIndex].setExtension(this.extensionName, paramsExt)
319
+ const paramsExtDef = sceneDef.extensions[this.extensionName] as Record<string, any>
320
+ paramsExt.setExtras(paramsExtDef)
321
+ // console.log(paramsExtDef)
322
+ }
323
+ })
324
+
325
+ return this
326
+ }
327
+
328
+ write(context: WriterContext): this {
329
+ const jsonDoc = context.jsonDoc
330
+ this.document.getRoot()
331
+ .listMaterials()
332
+ .forEach((material) => {
333
+ const paramsExt = material.getExtension<GenericExtensionProperty>(this.extensionName)
334
+ // console.log(paramsExt)
335
+ if (paramsExt) {
336
+ const materialIndex = context.materialIndexMap.get(material)!
337
+ const materialDef = jsonDoc.json.materials![materialIndex]
338
+ materialDef.extensions = materialDef.extensions || {}
339
+ const extensionDef = paramsExt.getExtras()
340
+ const extensionDef2 = {...extensionDef}
341
+
342
+ // console.log(paramsExt.textures)
343
+ for (const [key, value] of Object.entries(paramsExt.textures)) {
344
+ const textureInfo = value[0]
345
+ const textureLink = value[1]
346
+ const texture = textureLink
347
+
348
+ if (texture)
349
+ extensionDef2[key] = context.createTextureInfoDef(texture, textureInfo)
350
+
351
+ // console.log(texture)
352
+
353
+ }
354
+ // console.log(extensionDef2)
355
+
356
+ materialDef.extensions[this.extensionName] = extensionDef2
357
+ }
358
+ })
359
+ this.document.getRoot()
360
+ .listMeshes()
361
+ .forEach((mesh) => {
362
+ const paramsExt = mesh.getExtension<GenericExtensionProperty>(this.extensionName)
363
+ if (paramsExt) {
364
+ const meshIndex = context.meshIndexMap.get(mesh)!
365
+ const meshDef = jsonDoc.json.meshes![meshIndex]
366
+ meshDef.extensions = meshDef.extensions || {}
367
+ meshDef.extensions[this.extensionName] = paramsExt.getExtras()
368
+ }
369
+ })
370
+ this.document.getRoot()
371
+ .listNodes()
372
+ .forEach((node) => {
373
+ const paramsExt = node.getExtension<GenericExtensionProperty>(this.extensionName)
374
+ if (paramsExt) {
375
+ const nodeIndex = context.nodeIndexMap.get(node)!
376
+ const nodeDef = jsonDoc.json.nodes![nodeIndex]
377
+ nodeDef.extensions = nodeDef.extensions || {}
378
+ nodeDef.extensions[this.extensionName] = paramsExt.getExtras()
379
+ }
380
+ })
381
+ this.document.getRoot()
382
+ .listScenes()
383
+ .forEach((scene) => {
384
+ const paramsExt = scene.getExtension<GenericExtensionProperty>(this.extensionName)
385
+ if (paramsExt) {
386
+ const sceneIndex = context.jsonDoc.json.scene || 0 // todo: get proper scene index, if working with multiple scenes, this will do the default one.
387
+ const sceneDef = jsonDoc.json.scenes![sceneIndex]
388
+ if (!sceneDef) return
389
+ sceneDef.extensions = sceneDef.extensions || {}
390
+ sceneDef.extensions[this.extensionName] = paramsExt.getExtras()
391
+ }
392
+ })
393
+
394
+ return this
395
+ }
396
+
397
+ }
398
+
399
+ function stringToChannel(s: string) {
400
+ let r = 0
401
+ if (s.includes('R')) r |= TextureChannel.R
402
+ if (s.includes('G')) r |= TextureChannel.G
403
+ if (s.includes('B')) r |= TextureChannel.B
404
+ if (s.includes('A')) r |= TextureChannel.A
405
+ return r
406
+ }
407
+
408
+ export function createGenericExtensionClass(name: string, textures?: Record<string, string|number>): typeof GenericExtension {
409
+ return class extends GenericExtension {
410
+ public static readonly EXTENSION_NAME = name
411
+ readonly extensionName = name
412
+ textureChannels: Record<string, number> = !textures ? {} : Object.fromEntries(
413
+ Object.entries(textures)
414
+ .map(([k, v])=>
415
+ [k, typeof v === 'number' ? v : stringToChannel(v)])
416
+ )
417
+ }
418
+ }
@@ -0,0 +1,40 @@
1
+ declare module '*.txt' {
2
+ const content: string
3
+ export default content
4
+ }
5
+ declare module '*.glsl' {
6
+ const content: string
7
+ export default content
8
+ }
9
+ declare module '*.vert' {
10
+ const content: string
11
+ export default content
12
+ }
13
+ declare module '*.frag' {
14
+ const content: string
15
+ export default content
16
+ }
17
+ declare module '*.module.scss' {
18
+ const content: any
19
+ export default content
20
+ export const stylesheet: string
21
+ }
22
+ declare module '*.module.css' {
23
+ const content: any
24
+ export default content
25
+ export const stylesheet: string
26
+ }
27
+ declare module '*.css' {
28
+ const content: string
29
+ export default content
30
+ }
31
+ declare module '*.css?inline' { // for vite
32
+ const content: string
33
+ export default content
34
+ }
35
+
36
+ // export {}
37
+
38
+ // hack for typedoc
39
+ // eslint-disable-next-line @typescript-eslint/naming-convention
40
+ // declare type OffscreenCanvas = HTMLCanvasElement
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {GLTFDracoExporter, createGenericExtensionClass} from './GLTFDracoExporter'
2
+ export {GLTFDracoExportPlugin} from './GLTFDracoExportPlugin'