@luma.gl/gltf 9.3.0-alpha.4 → 9.3.0-alpha.8
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/dist/dist.dev.js +1305 -327
- package/dist/dist.min.js +98 -46
- package/dist/gltf/animations/animations.d.ts +16 -4
- package/dist/gltf/animations/animations.d.ts.map +1 -1
- package/dist/gltf/animations/interpolate.d.ts +4 -3
- package/dist/gltf/animations/interpolate.d.ts.map +1 -1
- package/dist/gltf/animations/interpolate.js +27 -36
- package/dist/gltf/animations/interpolate.js.map +1 -1
- package/dist/gltf/create-gltf-model.d.ts +15 -1
- package/dist/gltf/create-gltf-model.d.ts.map +1 -1
- package/dist/gltf/create-gltf-model.js +154 -48
- package/dist/gltf/create-gltf-model.js.map +1 -1
- package/dist/gltf/create-scenegraph-from-gltf.d.ts +37 -2
- package/dist/gltf/create-scenegraph-from-gltf.d.ts.map +1 -1
- package/dist/gltf/create-scenegraph-from-gltf.js +74 -6
- package/dist/gltf/create-scenegraph-from-gltf.js.map +1 -1
- package/dist/gltf/gltf-animator.d.ts +26 -0
- package/dist/gltf/gltf-animator.d.ts.map +1 -1
- package/dist/gltf/gltf-animator.js +22 -19
- package/dist/gltf/gltf-animator.js.map +1 -1
- package/dist/gltf/gltf-extension-support.d.ts +10 -0
- package/dist/gltf/gltf-extension-support.d.ts.map +1 -0
- package/dist/gltf/gltf-extension-support.js +173 -0
- package/dist/gltf/gltf-extension-support.js.map +1 -0
- package/dist/index.cjs +1247 -294
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/parsers/parse-gltf-animations.d.ts +1 -0
- package/dist/parsers/parse-gltf-animations.d.ts.map +1 -1
- package/dist/parsers/parse-gltf-animations.js +73 -28
- package/dist/parsers/parse-gltf-animations.js.map +1 -1
- package/dist/parsers/parse-gltf-lights.d.ts.map +1 -1
- package/dist/parsers/parse-gltf-lights.js +112 -18
- package/dist/parsers/parse-gltf-lights.js.map +1 -1
- package/dist/parsers/parse-gltf.d.ts +19 -2
- package/dist/parsers/parse-gltf.d.ts.map +1 -1
- package/dist/parsers/parse-gltf.js +101 -61
- package/dist/parsers/parse-gltf.js.map +1 -1
- package/dist/parsers/parse-pbr-material.d.ts +115 -2
- package/dist/parsers/parse-pbr-material.d.ts.map +1 -1
- package/dist/parsers/parse-pbr-material.js +565 -54
- package/dist/parsers/parse-pbr-material.js.map +1 -1
- package/dist/pbr/pbr-environment.d.ts +6 -0
- package/dist/pbr/pbr-environment.d.ts.map +1 -1
- package/dist/pbr/pbr-environment.js +15 -12
- package/dist/pbr/pbr-environment.js.map +1 -1
- package/dist/pbr/pbr-material.d.ts +13 -3
- package/dist/pbr/pbr-material.d.ts.map +1 -1
- package/dist/webgl-to-webgpu/convert-webgl-attribute.d.ts +12 -1
- package/dist/webgl-to-webgpu/convert-webgl-attribute.d.ts.map +1 -1
- package/dist/webgl-to-webgpu/convert-webgl-attribute.js +3 -0
- package/dist/webgl-to-webgpu/convert-webgl-attribute.js.map +1 -1
- package/dist/webgl-to-webgpu/convert-webgl-sampler.d.ts +11 -5
- package/dist/webgl-to-webgpu/convert-webgl-sampler.d.ts.map +1 -1
- package/dist/webgl-to-webgpu/convert-webgl-sampler.js +16 -12
- package/dist/webgl-to-webgpu/convert-webgl-sampler.js.map +1 -1
- package/dist/webgl-to-webgpu/convert-webgl-topology.d.ts +2 -9
- package/dist/webgl-to-webgpu/convert-webgl-topology.d.ts.map +1 -1
- package/dist/webgl-to-webgpu/convert-webgl-topology.js +2 -14
- package/dist/webgl-to-webgpu/convert-webgl-topology.js.map +1 -1
- package/dist/webgl-to-webgpu/gltf-webgl-constants.d.ts +27 -0
- package/dist/webgl-to-webgpu/gltf-webgl-constants.d.ts.map +1 -0
- package/dist/webgl-to-webgpu/gltf-webgl-constants.js +34 -0
- package/dist/webgl-to-webgpu/gltf-webgl-constants.js.map +1 -0
- package/package.json +8 -9
- package/src/gltf/animations/animations.ts +17 -5
- package/src/gltf/animations/interpolate.ts +49 -68
- package/src/gltf/create-gltf-model.ts +214 -48
- package/src/gltf/create-scenegraph-from-gltf.ts +131 -12
- package/src/gltf/gltf-animator.ts +34 -25
- package/src/gltf/gltf-extension-support.ts +214 -0
- package/src/index.ts +10 -2
- package/src/parsers/parse-gltf-animations.ts +94 -33
- package/src/parsers/parse-gltf-lights.ts +147 -20
- package/src/parsers/parse-gltf.ts +170 -90
- package/src/parsers/parse-pbr-material.ts +865 -80
- package/src/pbr/pbr-environment.ts +38 -15
- package/src/pbr/pbr-material.ts +18 -3
- package/src/webgl-to-webgpu/convert-webgl-attribute.ts +12 -1
- package/src/webgl-to-webgpu/convert-webgl-sampler.ts +38 -29
- package/src/webgl-to-webgpu/convert-webgl-topology.ts +2 -14
- package/src/webgl-to-webgpu/gltf-webgl-constants.ts +35 -0
- package/dist/utils/deep-copy.d.ts +0 -3
- package/dist/utils/deep-copy.d.ts.map +0 -1
- package/dist/utils/deep-copy.js +0 -21
- package/dist/utils/deep-copy.js.map +0 -1
- package/src/utils/deep-copy.ts +0 -22
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import type {Device,
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
5
|
+
import type {Device, SamplerProps, TextureFormat, TypedArray} from '@luma.gl/core';
|
|
6
|
+
import {Texture, log, textureFormatDecoder} from '@luma.gl/core';
|
|
7
|
+
import type {GLTFPostprocessed, GLTFSampler} from '@loaders.gl/gltf';
|
|
8
8
|
|
|
9
|
-
import {log} from '@luma.gl/core';
|
|
10
9
|
import {type ParsedPBRMaterial} from '../pbr/pbr-material';
|
|
11
10
|
import {type PBREnvironment} from '../pbr/pbr-environment';
|
|
12
11
|
import {type PBRMaterialBindings} from '@luma.gl/shadertools';
|
|
12
|
+
import {GLEnum} from '../webgl-to-webgpu/gltf-webgl-constants';
|
|
13
13
|
import {convertSampler} from '../webgl-to-webgpu/convert-webgl-sampler';
|
|
14
14
|
|
|
15
15
|
// TODO - synchronize the GLTF... types with loaders.gl
|
|
@@ -19,6 +19,7 @@ import {convertSampler} from '../webgl-to-webgpu/convert-webgl-sampler';
|
|
|
19
19
|
|
|
20
20
|
type GLTFTexture = {
|
|
21
21
|
id: string;
|
|
22
|
+
index?: number;
|
|
22
23
|
texture: {source: {image: any}; sampler: {parameters: any}};
|
|
23
24
|
uniformName?: string;
|
|
24
25
|
// is this on all textures?
|
|
@@ -36,16 +37,114 @@ type GLTFPBRMetallicRoughness = {
|
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
type GLTFPBRMaterial = {
|
|
40
|
+
extensions?: GLTFMaterialExtensions;
|
|
39
41
|
unlit?: boolean;
|
|
40
42
|
pbrMetallicRoughness?: GLTFPBRMetallicRoughness;
|
|
41
43
|
normalTexture?: GLTFTexture;
|
|
42
44
|
occlusionTexture?: GLTFTexture;
|
|
43
45
|
emissiveTexture?: GLTFTexture;
|
|
44
46
|
emissiveFactor?: [number, number, number];
|
|
45
|
-
alphaMode?: 'MASK' | 'BLEND';
|
|
47
|
+
alphaMode?: 'OPAQUE' | 'MASK' | 'BLEND';
|
|
48
|
+
doubleSided?: boolean;
|
|
46
49
|
alphaCutoff?: number;
|
|
47
50
|
};
|
|
48
51
|
|
|
52
|
+
type GLTFMaterialSpecularExtension = {
|
|
53
|
+
specularFactor?: number;
|
|
54
|
+
specularTexture?: GLTFTexture;
|
|
55
|
+
specularColorFactor?: [number, number, number];
|
|
56
|
+
specularColorTexture?: GLTFTexture;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
type GLTFMaterialIorExtension = {
|
|
60
|
+
ior?: number;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type GLTFMaterialTransmissionExtension = {
|
|
64
|
+
transmissionFactor?: number;
|
|
65
|
+
transmissionTexture?: GLTFTexture;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
type GLTFMaterialVolumeExtension = {
|
|
69
|
+
thicknessFactor?: number;
|
|
70
|
+
thicknessTexture?: GLTFTexture;
|
|
71
|
+
attenuationDistance?: number;
|
|
72
|
+
attenuationColor?: [number, number, number];
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
type GLTFMaterialClearcoatExtension = {
|
|
76
|
+
clearcoatFactor?: number;
|
|
77
|
+
clearcoatTexture?: GLTFTexture;
|
|
78
|
+
clearcoatRoughnessFactor?: number;
|
|
79
|
+
clearcoatRoughnessTexture?: GLTFTexture;
|
|
80
|
+
clearcoatNormalTexture?: GLTFTexture;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
type GLTFMaterialSheenExtension = {
|
|
84
|
+
sheenColorFactor?: [number, number, number];
|
|
85
|
+
sheenColorTexture?: GLTFTexture;
|
|
86
|
+
sheenRoughnessFactor?: number;
|
|
87
|
+
sheenRoughnessTexture?: GLTFTexture;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
type GLTFMaterialIridescenceExtension = {
|
|
91
|
+
iridescenceFactor?: number;
|
|
92
|
+
iridescenceTexture?: GLTFTexture;
|
|
93
|
+
iridescenceIor?: number;
|
|
94
|
+
iridescenceThicknessMinimum?: number;
|
|
95
|
+
iridescenceThicknessMaximum?: number;
|
|
96
|
+
iridescenceThicknessTexture?: GLTFTexture;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
type GLTFMaterialAnisotropyExtension = {
|
|
100
|
+
anisotropyStrength?: number;
|
|
101
|
+
anisotropyRotation?: number;
|
|
102
|
+
anisotropyTexture?: GLTFTexture;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
type GLTFMaterialEmissiveStrengthExtension = {
|
|
106
|
+
emissiveStrength?: number;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
type GLTFMaterialExtensions = {
|
|
110
|
+
KHR_materials_unlit?: Record<string, never>;
|
|
111
|
+
KHR_materials_specular?: GLTFMaterialSpecularExtension;
|
|
112
|
+
KHR_materials_ior?: GLTFMaterialIorExtension;
|
|
113
|
+
KHR_materials_transmission?: GLTFMaterialTransmissionExtension;
|
|
114
|
+
KHR_materials_volume?: GLTFMaterialVolumeExtension;
|
|
115
|
+
KHR_materials_clearcoat?: GLTFMaterialClearcoatExtension;
|
|
116
|
+
KHR_materials_sheen?: GLTFMaterialSheenExtension;
|
|
117
|
+
KHR_materials_iridescence?: GLTFMaterialIridescenceExtension;
|
|
118
|
+
KHR_materials_anisotropy?: GLTFMaterialAnisotropyExtension;
|
|
119
|
+
KHR_materials_emissive_strength?: GLTFMaterialEmissiveStrengthExtension;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
type TextureEnabledUniformName =
|
|
123
|
+
| 'baseColorMapEnabled'
|
|
124
|
+
| 'normalMapEnabled'
|
|
125
|
+
| 'emissiveMapEnabled'
|
|
126
|
+
| 'metallicRoughnessMapEnabled'
|
|
127
|
+
| 'occlusionMapEnabled'
|
|
128
|
+
| 'specularColorMapEnabled'
|
|
129
|
+
| 'specularIntensityMapEnabled'
|
|
130
|
+
| 'transmissionMapEnabled'
|
|
131
|
+
| 'clearcoatMapEnabled'
|
|
132
|
+
| 'clearcoatRoughnessMapEnabled'
|
|
133
|
+
| 'sheenColorMapEnabled'
|
|
134
|
+
| 'sheenRoughnessMapEnabled'
|
|
135
|
+
| 'iridescenceMapEnabled'
|
|
136
|
+
| 'anisotropyMapEnabled';
|
|
137
|
+
|
|
138
|
+
type TextureFeatureOptions = {
|
|
139
|
+
define?: string;
|
|
140
|
+
enabledUniformName?: TextureEnabledUniformName;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
type TextureParseOptions = {
|
|
144
|
+
featureOptions?: TextureFeatureOptions;
|
|
145
|
+
gltf?: GLTFPostprocessed;
|
|
146
|
+
};
|
|
147
|
+
|
|
49
148
|
export type ParsePBRMaterialOptions = {
|
|
50
149
|
/** Debug PBR shader */
|
|
51
150
|
pbrDebug?: boolean;
|
|
@@ -55,6 +154,10 @@ export type ParsePBRMaterialOptions = {
|
|
|
55
154
|
useTangents?: boolean;
|
|
56
155
|
/** provide an image based (texture cube) lighting environment */
|
|
57
156
|
imageBasedLightingEnvironment?: PBREnvironment;
|
|
157
|
+
/** parent post-processed glTF, used to resolve extension texture infos */
|
|
158
|
+
gltf?: GLTFPostprocessed;
|
|
159
|
+
/** run primitive-attribute diagnostics such as missing TEXCOORD_0 / NORMAL */
|
|
160
|
+
validateAttributes?: boolean;
|
|
58
161
|
};
|
|
59
162
|
|
|
60
163
|
/**
|
|
@@ -93,7 +196,8 @@ export function parsePBRMaterial(
|
|
|
93
196
|
imageBasedLightingEnvironment.diffuseEnvSampler.texture;
|
|
94
197
|
parsedMaterial.bindings.pbr_specularEnvSampler =
|
|
95
198
|
imageBasedLightingEnvironment.specularEnvSampler.texture;
|
|
96
|
-
parsedMaterial.bindings.
|
|
199
|
+
parsedMaterial.bindings.pbr_brdfLUT = imageBasedLightingEnvironment.brdfLutTexture.texture;
|
|
200
|
+
parsedMaterial.uniforms.IBLenabled = true;
|
|
97
201
|
parsedMaterial.uniforms.scaleIBLAmbient = [1, 1];
|
|
98
202
|
}
|
|
99
203
|
|
|
@@ -107,112 +211,224 @@ export function parsePBRMaterial(
|
|
|
107
211
|
if (attributes['NORMAL']) parsedMaterial.defines['HAS_NORMALS'] = true;
|
|
108
212
|
if (attributes['TANGENT'] && options?.useTangents) parsedMaterial.defines['HAS_TANGENTS'] = true;
|
|
109
213
|
if (attributes['TEXCOORD_0']) parsedMaterial.defines['HAS_UV'] = true;
|
|
214
|
+
if (attributes['JOINTS_0'] && attributes['WEIGHTS_0']) parsedMaterial.defines['HAS_SKIN'] = true;
|
|
110
215
|
if (attributes['COLOR_0']) parsedMaterial.defines['HAS_COLORS'] = true;
|
|
111
216
|
|
|
112
217
|
if (options?.imageBasedLightingEnvironment) parsedMaterial.defines['USE_IBL'] = true;
|
|
113
218
|
if (options?.lights) parsedMaterial.defines['USE_LIGHTS'] = true;
|
|
114
219
|
|
|
115
220
|
if (material) {
|
|
116
|
-
|
|
221
|
+
if (options.validateAttributes !== false) {
|
|
222
|
+
warnOnMissingExpectedAttributes(material, attributes);
|
|
223
|
+
}
|
|
224
|
+
parseMaterial(device, material, parsedMaterial, options.gltf);
|
|
117
225
|
}
|
|
118
226
|
|
|
119
227
|
return parsedMaterial;
|
|
120
228
|
}
|
|
121
229
|
|
|
230
|
+
function warnOnMissingExpectedAttributes(
|
|
231
|
+
material: GLTFPBRMaterial,
|
|
232
|
+
attributes: Record<string, any>
|
|
233
|
+
): void {
|
|
234
|
+
const uvDependentTextureSlots = getUvDependentTextureSlots(material);
|
|
235
|
+
if (uvDependentTextureSlots.length > 0 && !attributes['TEXCOORD_0']) {
|
|
236
|
+
log.warn(
|
|
237
|
+
`glTF material uses ${uvDependentTextureSlots.join(', ')} but primitive is missing TEXCOORD_0; textured shading will sample the default UV coordinates`
|
|
238
|
+
)();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const isUnlitMaterial = Boolean(material.unlit || material.extensions?.KHR_materials_unlit);
|
|
242
|
+
if (isUnlitMaterial || attributes['NORMAL']) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const missingNormalReason = material.normalTexture
|
|
247
|
+
? 'lit PBR shading with normalTexture'
|
|
248
|
+
: 'lit PBR shading';
|
|
249
|
+
log.warn(
|
|
250
|
+
`glTF primitive is missing NORMAL while using ${missingNormalReason}; shading will fall back to geometric normals`
|
|
251
|
+
)();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function getUvDependentTextureSlots(material: GLTFPBRMaterial): string[] {
|
|
255
|
+
const uvDependentTextureSlots: string[] = [];
|
|
256
|
+
|
|
257
|
+
if (material.pbrMetallicRoughness?.baseColorTexture) {
|
|
258
|
+
uvDependentTextureSlots.push('baseColorTexture');
|
|
259
|
+
}
|
|
260
|
+
if (material.pbrMetallicRoughness?.metallicRoughnessTexture) {
|
|
261
|
+
uvDependentTextureSlots.push('metallicRoughnessTexture');
|
|
262
|
+
}
|
|
263
|
+
if (material.normalTexture) {
|
|
264
|
+
uvDependentTextureSlots.push('normalTexture');
|
|
265
|
+
}
|
|
266
|
+
if (material.occlusionTexture) {
|
|
267
|
+
uvDependentTextureSlots.push('occlusionTexture');
|
|
268
|
+
}
|
|
269
|
+
if (material.emissiveTexture) {
|
|
270
|
+
uvDependentTextureSlots.push('emissiveTexture');
|
|
271
|
+
}
|
|
272
|
+
if (material.extensions?.KHR_materials_specular?.specularTexture) {
|
|
273
|
+
uvDependentTextureSlots.push('KHR_materials_specular.specularTexture');
|
|
274
|
+
}
|
|
275
|
+
if (material.extensions?.KHR_materials_specular?.specularColorTexture) {
|
|
276
|
+
uvDependentTextureSlots.push('KHR_materials_specular.specularColorTexture');
|
|
277
|
+
}
|
|
278
|
+
if (material.extensions?.KHR_materials_transmission?.transmissionTexture) {
|
|
279
|
+
uvDependentTextureSlots.push('KHR_materials_transmission.transmissionTexture');
|
|
280
|
+
}
|
|
281
|
+
if (material.extensions?.KHR_materials_clearcoat?.clearcoatTexture) {
|
|
282
|
+
uvDependentTextureSlots.push('KHR_materials_clearcoat.clearcoatTexture');
|
|
283
|
+
}
|
|
284
|
+
if (material.extensions?.KHR_materials_clearcoat?.clearcoatRoughnessTexture) {
|
|
285
|
+
uvDependentTextureSlots.push('KHR_materials_clearcoat.clearcoatRoughnessTexture');
|
|
286
|
+
}
|
|
287
|
+
if (material.extensions?.KHR_materials_sheen?.sheenColorTexture) {
|
|
288
|
+
uvDependentTextureSlots.push('KHR_materials_sheen.sheenColorTexture');
|
|
289
|
+
}
|
|
290
|
+
if (material.extensions?.KHR_materials_sheen?.sheenRoughnessTexture) {
|
|
291
|
+
uvDependentTextureSlots.push('KHR_materials_sheen.sheenRoughnessTexture');
|
|
292
|
+
}
|
|
293
|
+
if (material.extensions?.KHR_materials_iridescence?.iridescenceTexture) {
|
|
294
|
+
uvDependentTextureSlots.push('KHR_materials_iridescence.iridescenceTexture');
|
|
295
|
+
}
|
|
296
|
+
if (material.extensions?.KHR_materials_anisotropy?.anisotropyTexture) {
|
|
297
|
+
uvDependentTextureSlots.push('KHR_materials_anisotropy.anisotropyTexture');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return uvDependentTextureSlots;
|
|
301
|
+
}
|
|
302
|
+
|
|
122
303
|
/** Parse GLTF material record */
|
|
123
304
|
function parseMaterial(
|
|
124
305
|
device: Device,
|
|
125
306
|
material: GLTFPBRMaterial,
|
|
126
|
-
parsedMaterial: ParsedPBRMaterial
|
|
307
|
+
parsedMaterial: ParsedPBRMaterial,
|
|
308
|
+
gltf?: GLTFPostprocessed
|
|
127
309
|
): void {
|
|
128
|
-
parsedMaterial.uniforms.unlit = Boolean(
|
|
310
|
+
parsedMaterial.uniforms.unlit = Boolean(
|
|
311
|
+
material.unlit || material.extensions?.KHR_materials_unlit
|
|
312
|
+
);
|
|
129
313
|
|
|
130
314
|
if (material.pbrMetallicRoughness) {
|
|
131
|
-
parsePbrMetallicRoughness(device, material.pbrMetallicRoughness, parsedMaterial);
|
|
315
|
+
parsePbrMetallicRoughness(device, material.pbrMetallicRoughness, parsedMaterial, gltf);
|
|
132
316
|
}
|
|
133
317
|
if (material.normalTexture) {
|
|
134
|
-
addTexture(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
);
|
|
318
|
+
addTexture(device, material.normalTexture, 'pbr_normalSampler', parsedMaterial, {
|
|
319
|
+
featureOptions: {
|
|
320
|
+
define: 'HAS_NORMALMAP',
|
|
321
|
+
enabledUniformName: 'normalMapEnabled'
|
|
322
|
+
},
|
|
323
|
+
gltf
|
|
324
|
+
});
|
|
141
325
|
|
|
142
326
|
const {scale = 1} = material.normalTexture;
|
|
143
327
|
parsedMaterial.uniforms.normalScale = scale;
|
|
144
328
|
}
|
|
145
329
|
if (material.occlusionTexture) {
|
|
146
|
-
addTexture(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
);
|
|
330
|
+
addTexture(device, material.occlusionTexture, 'pbr_occlusionSampler', parsedMaterial, {
|
|
331
|
+
featureOptions: {
|
|
332
|
+
define: 'HAS_OCCLUSIONMAP',
|
|
333
|
+
enabledUniformName: 'occlusionMapEnabled'
|
|
334
|
+
},
|
|
335
|
+
gltf
|
|
336
|
+
});
|
|
153
337
|
|
|
154
338
|
const {strength = 1} = material.occlusionTexture;
|
|
155
339
|
parsedMaterial.uniforms.occlusionStrength = strength;
|
|
156
340
|
}
|
|
341
|
+
parsedMaterial.uniforms.emissiveFactor = material.emissiveFactor || [0, 0, 0];
|
|
157
342
|
if (material.emissiveTexture) {
|
|
158
|
-
addTexture(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
);
|
|
165
|
-
parsedMaterial.uniforms.emissiveFactor = material.emissiveFactor || [0, 0, 0];
|
|
343
|
+
addTexture(device, material.emissiveTexture, 'pbr_emissiveSampler', parsedMaterial, {
|
|
344
|
+
featureOptions: {
|
|
345
|
+
define: 'HAS_EMISSIVEMAP',
|
|
346
|
+
enabledUniformName: 'emissiveMapEnabled'
|
|
347
|
+
},
|
|
348
|
+
gltf
|
|
349
|
+
});
|
|
166
350
|
}
|
|
167
351
|
|
|
168
|
-
|
|
169
|
-
|
|
352
|
+
parseMaterialExtensions(device, material.extensions, parsedMaterial, gltf);
|
|
353
|
+
|
|
354
|
+
switch (material.alphaMode || 'OPAQUE') {
|
|
355
|
+
case 'OPAQUE':
|
|
356
|
+
break;
|
|
357
|
+
case 'MASK': {
|
|
170
358
|
const {alphaCutoff = 0.5} = material;
|
|
171
359
|
parsedMaterial.defines['ALPHA_CUTOFF'] = true;
|
|
360
|
+
parsedMaterial.uniforms.alphaCutoffEnabled = true;
|
|
172
361
|
parsedMaterial.uniforms.alphaCutoff = alphaCutoff;
|
|
173
362
|
break;
|
|
363
|
+
}
|
|
174
364
|
case 'BLEND':
|
|
175
365
|
log.warn('glTF BLEND alphaMode might not work well because it requires mesh sorting')();
|
|
366
|
+
applyAlphaBlendParameters(parsedMaterial);
|
|
176
367
|
|
|
177
|
-
|
|
178
|
-
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
179
371
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
372
|
+
function applyAlphaBlendParameters(parsedMaterial: ParsedPBRMaterial): void {
|
|
373
|
+
parsedMaterial.parameters.blend = true;
|
|
374
|
+
parsedMaterial.parameters.blendColorOperation = 'add';
|
|
375
|
+
parsedMaterial.parameters.blendColorSrcFactor = 'src-alpha';
|
|
376
|
+
parsedMaterial.parameters.blendColorDstFactor = 'one-minus-src-alpha';
|
|
377
|
+
parsedMaterial.parameters.blendAlphaOperation = 'add';
|
|
378
|
+
parsedMaterial.parameters.blendAlphaSrcFactor = 'one';
|
|
379
|
+
parsedMaterial.parameters.blendAlphaDstFactor = 'one-minus-src-alpha';
|
|
183
380
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
381
|
+
parsedMaterial.glParameters['blend'] = true;
|
|
382
|
+
parsedMaterial.glParameters['blendEquation'] = GLEnum.FUNC_ADD;
|
|
383
|
+
parsedMaterial.glParameters['blendFunc'] = [
|
|
384
|
+
GLEnum.SRC_ALPHA,
|
|
385
|
+
GLEnum.ONE_MINUS_SRC_ALPHA,
|
|
386
|
+
GLEnum.ONE,
|
|
387
|
+
GLEnum.ONE_MINUS_SRC_ALPHA
|
|
388
|
+
];
|
|
389
|
+
}
|
|
187
390
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
];
|
|
391
|
+
function applyTransmissionBlendApproximation(parsedMaterial: ParsedPBRMaterial): void {
|
|
392
|
+
parsedMaterial.parameters.blend = true;
|
|
393
|
+
parsedMaterial.parameters.depthWriteEnabled = false;
|
|
394
|
+
parsedMaterial.parameters.blendColorOperation = 'add';
|
|
395
|
+
parsedMaterial.parameters.blendColorSrcFactor = 'one';
|
|
396
|
+
parsedMaterial.parameters.blendColorDstFactor = 'one-minus-src-alpha';
|
|
397
|
+
parsedMaterial.parameters.blendAlphaOperation = 'add';
|
|
398
|
+
parsedMaterial.parameters.blendAlphaSrcFactor = 'one';
|
|
399
|
+
parsedMaterial.parameters.blendAlphaDstFactor = 'one-minus-src-alpha';
|
|
198
400
|
|
|
199
|
-
|
|
200
|
-
|
|
401
|
+
parsedMaterial.glParameters['blend'] = true;
|
|
402
|
+
parsedMaterial.glParameters['depthMask'] = false;
|
|
403
|
+
parsedMaterial.glParameters['blendEquation'] = GLEnum.FUNC_ADD;
|
|
404
|
+
parsedMaterial.glParameters['blendFunc'] = [
|
|
405
|
+
GLEnum.ONE,
|
|
406
|
+
GLEnum.ONE_MINUS_SRC_ALPHA,
|
|
407
|
+
GLEnum.ONE,
|
|
408
|
+
GLEnum.ONE_MINUS_SRC_ALPHA
|
|
409
|
+
];
|
|
201
410
|
}
|
|
202
411
|
|
|
203
412
|
/** Parse GLTF material sub record */
|
|
204
413
|
function parsePbrMetallicRoughness(
|
|
205
414
|
device: Device,
|
|
206
415
|
pbrMetallicRoughness: GLTFPBRMetallicRoughness,
|
|
207
|
-
parsedMaterial: ParsedPBRMaterial
|
|
416
|
+
parsedMaterial: ParsedPBRMaterial,
|
|
417
|
+
gltf?: GLTFPostprocessed
|
|
208
418
|
): void {
|
|
209
419
|
if (pbrMetallicRoughness.baseColorTexture) {
|
|
210
420
|
addTexture(
|
|
211
421
|
device,
|
|
212
422
|
pbrMetallicRoughness.baseColorTexture,
|
|
213
423
|
'pbr_baseColorSampler',
|
|
214
|
-
|
|
215
|
-
|
|
424
|
+
parsedMaterial,
|
|
425
|
+
{
|
|
426
|
+
featureOptions: {
|
|
427
|
+
define: 'HAS_BASECOLORMAP',
|
|
428
|
+
enabledUniformName: 'baseColorMapEnabled'
|
|
429
|
+
},
|
|
430
|
+
gltf
|
|
431
|
+
}
|
|
216
432
|
);
|
|
217
433
|
}
|
|
218
434
|
parsedMaterial.uniforms.baseColorFactor = pbrMetallicRoughness.baseColorFactor || [1, 1, 1, 1];
|
|
@@ -222,30 +438,362 @@ function parsePbrMetallicRoughness(
|
|
|
222
438
|
device,
|
|
223
439
|
pbrMetallicRoughness.metallicRoughnessTexture,
|
|
224
440
|
'pbr_metallicRoughnessSampler',
|
|
225
|
-
|
|
226
|
-
|
|
441
|
+
parsedMaterial,
|
|
442
|
+
{
|
|
443
|
+
featureOptions: {
|
|
444
|
+
define: 'HAS_METALROUGHNESSMAP',
|
|
445
|
+
enabledUniformName: 'metallicRoughnessMapEnabled'
|
|
446
|
+
},
|
|
447
|
+
gltf
|
|
448
|
+
}
|
|
227
449
|
);
|
|
228
450
|
}
|
|
229
451
|
const {metallicFactor = 1, roughnessFactor = 1} = pbrMetallicRoughness;
|
|
230
452
|
parsedMaterial.uniforms.metallicRoughnessValues = [metallicFactor, roughnessFactor];
|
|
231
453
|
}
|
|
232
454
|
|
|
455
|
+
function parseMaterialExtensions(
|
|
456
|
+
device: Device,
|
|
457
|
+
extensions: GLTFMaterialExtensions | undefined,
|
|
458
|
+
parsedMaterial: ParsedPBRMaterial,
|
|
459
|
+
gltf?: GLTFPostprocessed
|
|
460
|
+
): void {
|
|
461
|
+
if (!extensions) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (hasMaterialExtensionShading(extensions)) {
|
|
466
|
+
parsedMaterial.defines['USE_MATERIAL_EXTENSIONS'] = true;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
parseSpecularExtension(device, extensions.KHR_materials_specular, parsedMaterial, gltf);
|
|
470
|
+
parseIorExtension(extensions.KHR_materials_ior, parsedMaterial);
|
|
471
|
+
parseTransmissionExtension(device, extensions.KHR_materials_transmission, parsedMaterial, gltf);
|
|
472
|
+
parseVolumeExtension(device, extensions.KHR_materials_volume, parsedMaterial, gltf);
|
|
473
|
+
parseClearcoatExtension(device, extensions.KHR_materials_clearcoat, parsedMaterial, gltf);
|
|
474
|
+
parseSheenExtension(device, extensions.KHR_materials_sheen, parsedMaterial, gltf);
|
|
475
|
+
parseIridescenceExtension(device, extensions.KHR_materials_iridescence, parsedMaterial, gltf);
|
|
476
|
+
parseAnisotropyExtension(device, extensions.KHR_materials_anisotropy, parsedMaterial, gltf);
|
|
477
|
+
parseEmissiveStrengthExtension(extensions.KHR_materials_emissive_strength, parsedMaterial);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function hasMaterialExtensionShading(extensions: GLTFMaterialExtensions): boolean {
|
|
481
|
+
return Boolean(
|
|
482
|
+
extensions.KHR_materials_specular ||
|
|
483
|
+
extensions.KHR_materials_ior ||
|
|
484
|
+
extensions.KHR_materials_transmission ||
|
|
485
|
+
extensions.KHR_materials_volume ||
|
|
486
|
+
extensions.KHR_materials_clearcoat ||
|
|
487
|
+
extensions.KHR_materials_sheen ||
|
|
488
|
+
extensions.KHR_materials_iridescence ||
|
|
489
|
+
extensions.KHR_materials_anisotropy
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function parseSpecularExtension(
|
|
494
|
+
device: Device,
|
|
495
|
+
extension: GLTFMaterialSpecularExtension | undefined,
|
|
496
|
+
parsedMaterial: ParsedPBRMaterial,
|
|
497
|
+
gltf?: GLTFPostprocessed
|
|
498
|
+
): void {
|
|
499
|
+
if (!extension) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (extension.specularColorFactor) {
|
|
504
|
+
parsedMaterial.uniforms.specularColorFactor = extension.specularColorFactor;
|
|
505
|
+
}
|
|
506
|
+
if (extension.specularFactor !== undefined) {
|
|
507
|
+
parsedMaterial.uniforms.specularIntensityFactor = extension.specularFactor;
|
|
508
|
+
}
|
|
509
|
+
if (extension.specularColorTexture) {
|
|
510
|
+
addTexture(device, extension.specularColorTexture, 'pbr_specularColorSampler', parsedMaterial, {
|
|
511
|
+
featureOptions: {
|
|
512
|
+
define: 'HAS_SPECULARCOLORMAP',
|
|
513
|
+
enabledUniformName: 'specularColorMapEnabled'
|
|
514
|
+
},
|
|
515
|
+
gltf
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
if (extension.specularTexture) {
|
|
519
|
+
addTexture(device, extension.specularTexture, 'pbr_specularIntensitySampler', parsedMaterial, {
|
|
520
|
+
featureOptions: {
|
|
521
|
+
define: 'HAS_SPECULARINTENSITYMAP',
|
|
522
|
+
enabledUniformName: 'specularIntensityMapEnabled'
|
|
523
|
+
},
|
|
524
|
+
gltf
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function parseIorExtension(
|
|
530
|
+
extension: GLTFMaterialIorExtension | undefined,
|
|
531
|
+
parsedMaterial: ParsedPBRMaterial
|
|
532
|
+
): void {
|
|
533
|
+
if (extension?.ior !== undefined) {
|
|
534
|
+
parsedMaterial.uniforms.ior = extension.ior;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function parseTransmissionExtension(
|
|
539
|
+
device: Device,
|
|
540
|
+
extension: GLTFMaterialTransmissionExtension | undefined,
|
|
541
|
+
parsedMaterial: ParsedPBRMaterial,
|
|
542
|
+
gltf?: GLTFPostprocessed
|
|
543
|
+
): void {
|
|
544
|
+
if (!extension) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (extension.transmissionFactor !== undefined) {
|
|
549
|
+
parsedMaterial.uniforms.transmissionFactor = extension.transmissionFactor;
|
|
550
|
+
}
|
|
551
|
+
if (extension.transmissionTexture) {
|
|
552
|
+
addTexture(device, extension.transmissionTexture, 'pbr_transmissionSampler', parsedMaterial, {
|
|
553
|
+
featureOptions: {
|
|
554
|
+
define: 'HAS_TRANSMISSIONMAP',
|
|
555
|
+
enabledUniformName: 'transmissionMapEnabled'
|
|
556
|
+
},
|
|
557
|
+
gltf
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if ((extension.transmissionFactor ?? 0) > 0 || extension.transmissionTexture) {
|
|
562
|
+
log.warn(
|
|
563
|
+
'KHR_materials_transmission uses a premultiplied-alpha blending approximation and may require mesh sorting'
|
|
564
|
+
)();
|
|
565
|
+
applyTransmissionBlendApproximation(parsedMaterial);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function parseVolumeExtension(
|
|
570
|
+
device: Device,
|
|
571
|
+
extension: GLTFMaterialVolumeExtension | undefined,
|
|
572
|
+
parsedMaterial: ParsedPBRMaterial,
|
|
573
|
+
gltf?: GLTFPostprocessed
|
|
574
|
+
): void {
|
|
575
|
+
if (!extension) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (extension.thicknessFactor !== undefined) {
|
|
580
|
+
parsedMaterial.uniforms.thicknessFactor = extension.thicknessFactor;
|
|
581
|
+
}
|
|
582
|
+
if (extension.thicknessTexture) {
|
|
583
|
+
addTexture(device, extension.thicknessTexture, 'pbr_thicknessSampler', parsedMaterial, {
|
|
584
|
+
featureOptions: {
|
|
585
|
+
define: 'HAS_THICKNESSMAP'
|
|
586
|
+
},
|
|
587
|
+
gltf
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
if (extension.attenuationDistance !== undefined) {
|
|
591
|
+
parsedMaterial.uniforms.attenuationDistance = extension.attenuationDistance;
|
|
592
|
+
}
|
|
593
|
+
if (extension.attenuationColor) {
|
|
594
|
+
parsedMaterial.uniforms.attenuationColor = extension.attenuationColor;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function parseClearcoatExtension(
|
|
599
|
+
device: Device,
|
|
600
|
+
extension: GLTFMaterialClearcoatExtension | undefined,
|
|
601
|
+
parsedMaterial: ParsedPBRMaterial,
|
|
602
|
+
gltf?: GLTFPostprocessed
|
|
603
|
+
): void {
|
|
604
|
+
if (!extension) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (extension.clearcoatFactor !== undefined) {
|
|
609
|
+
parsedMaterial.uniforms.clearcoatFactor = extension.clearcoatFactor;
|
|
610
|
+
}
|
|
611
|
+
if (extension.clearcoatRoughnessFactor !== undefined) {
|
|
612
|
+
parsedMaterial.uniforms.clearcoatRoughnessFactor = extension.clearcoatRoughnessFactor;
|
|
613
|
+
}
|
|
614
|
+
if (extension.clearcoatTexture) {
|
|
615
|
+
addTexture(device, extension.clearcoatTexture, 'pbr_clearcoatSampler', parsedMaterial, {
|
|
616
|
+
featureOptions: {
|
|
617
|
+
define: 'HAS_CLEARCOATMAP',
|
|
618
|
+
enabledUniformName: 'clearcoatMapEnabled'
|
|
619
|
+
},
|
|
620
|
+
gltf
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
if (extension.clearcoatRoughnessTexture) {
|
|
624
|
+
addTexture(
|
|
625
|
+
device,
|
|
626
|
+
extension.clearcoatRoughnessTexture,
|
|
627
|
+
'pbr_clearcoatRoughnessSampler',
|
|
628
|
+
parsedMaterial,
|
|
629
|
+
{
|
|
630
|
+
featureOptions: {
|
|
631
|
+
define: 'HAS_CLEARCOATROUGHNESSMAP',
|
|
632
|
+
enabledUniformName: 'clearcoatRoughnessMapEnabled'
|
|
633
|
+
},
|
|
634
|
+
gltf
|
|
635
|
+
}
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
if (extension.clearcoatNormalTexture) {
|
|
639
|
+
addTexture(
|
|
640
|
+
device,
|
|
641
|
+
extension.clearcoatNormalTexture,
|
|
642
|
+
'pbr_clearcoatNormalSampler',
|
|
643
|
+
parsedMaterial,
|
|
644
|
+
{
|
|
645
|
+
featureOptions: {
|
|
646
|
+
define: 'HAS_CLEARCOATNORMALMAP'
|
|
647
|
+
},
|
|
648
|
+
gltf
|
|
649
|
+
}
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function parseSheenExtension(
|
|
655
|
+
device: Device,
|
|
656
|
+
extension: GLTFMaterialSheenExtension | undefined,
|
|
657
|
+
parsedMaterial: ParsedPBRMaterial,
|
|
658
|
+
gltf?: GLTFPostprocessed
|
|
659
|
+
): void {
|
|
660
|
+
if (!extension) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (extension.sheenColorFactor) {
|
|
665
|
+
parsedMaterial.uniforms.sheenColorFactor = extension.sheenColorFactor;
|
|
666
|
+
}
|
|
667
|
+
if (extension.sheenRoughnessFactor !== undefined) {
|
|
668
|
+
parsedMaterial.uniforms.sheenRoughnessFactor = extension.sheenRoughnessFactor;
|
|
669
|
+
}
|
|
670
|
+
if (extension.sheenColorTexture) {
|
|
671
|
+
addTexture(device, extension.sheenColorTexture, 'pbr_sheenColorSampler', parsedMaterial, {
|
|
672
|
+
featureOptions: {
|
|
673
|
+
define: 'HAS_SHEENCOLORMAP',
|
|
674
|
+
enabledUniformName: 'sheenColorMapEnabled'
|
|
675
|
+
},
|
|
676
|
+
gltf
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
if (extension.sheenRoughnessTexture) {
|
|
680
|
+
addTexture(
|
|
681
|
+
device,
|
|
682
|
+
extension.sheenRoughnessTexture,
|
|
683
|
+
'pbr_sheenRoughnessSampler',
|
|
684
|
+
parsedMaterial,
|
|
685
|
+
{
|
|
686
|
+
featureOptions: {
|
|
687
|
+
define: 'HAS_SHEENROUGHNESSMAP',
|
|
688
|
+
enabledUniformName: 'sheenRoughnessMapEnabled'
|
|
689
|
+
},
|
|
690
|
+
gltf
|
|
691
|
+
}
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function parseIridescenceExtension(
|
|
697
|
+
device: Device,
|
|
698
|
+
extension: GLTFMaterialIridescenceExtension | undefined,
|
|
699
|
+
parsedMaterial: ParsedPBRMaterial,
|
|
700
|
+
gltf?: GLTFPostprocessed
|
|
701
|
+
): void {
|
|
702
|
+
if (!extension) {
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if (extension.iridescenceFactor !== undefined) {
|
|
707
|
+
parsedMaterial.uniforms.iridescenceFactor = extension.iridescenceFactor;
|
|
708
|
+
}
|
|
709
|
+
if (extension.iridescenceIor !== undefined) {
|
|
710
|
+
parsedMaterial.uniforms.iridescenceIor = extension.iridescenceIor;
|
|
711
|
+
}
|
|
712
|
+
if (
|
|
713
|
+
extension.iridescenceThicknessMinimum !== undefined ||
|
|
714
|
+
extension.iridescenceThicknessMaximum !== undefined
|
|
715
|
+
) {
|
|
716
|
+
parsedMaterial.uniforms.iridescenceThicknessRange = [
|
|
717
|
+
extension.iridescenceThicknessMinimum ?? 100,
|
|
718
|
+
extension.iridescenceThicknessMaximum ?? 400
|
|
719
|
+
];
|
|
720
|
+
}
|
|
721
|
+
if (extension.iridescenceTexture) {
|
|
722
|
+
addTexture(device, extension.iridescenceTexture, 'pbr_iridescenceSampler', parsedMaterial, {
|
|
723
|
+
featureOptions: {
|
|
724
|
+
define: 'HAS_IRIDESCENCEMAP',
|
|
725
|
+
enabledUniformName: 'iridescenceMapEnabled'
|
|
726
|
+
},
|
|
727
|
+
gltf
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
if (extension.iridescenceThicknessTexture) {
|
|
731
|
+
addTexture(
|
|
732
|
+
device,
|
|
733
|
+
extension.iridescenceThicknessTexture,
|
|
734
|
+
'pbr_iridescenceThicknessSampler',
|
|
735
|
+
parsedMaterial,
|
|
736
|
+
{
|
|
737
|
+
featureOptions: {
|
|
738
|
+
define: 'HAS_IRIDESCENCETHICKNESSMAP'
|
|
739
|
+
},
|
|
740
|
+
gltf
|
|
741
|
+
}
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function parseAnisotropyExtension(
|
|
747
|
+
device: Device,
|
|
748
|
+
extension: GLTFMaterialAnisotropyExtension | undefined,
|
|
749
|
+
parsedMaterial: ParsedPBRMaterial,
|
|
750
|
+
gltf?: GLTFPostprocessed
|
|
751
|
+
): void {
|
|
752
|
+
if (!extension) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (extension.anisotropyStrength !== undefined) {
|
|
757
|
+
parsedMaterial.uniforms.anisotropyStrength = extension.anisotropyStrength;
|
|
758
|
+
}
|
|
759
|
+
if (extension.anisotropyRotation !== undefined) {
|
|
760
|
+
parsedMaterial.uniforms.anisotropyRotation = extension.anisotropyRotation;
|
|
761
|
+
}
|
|
762
|
+
if (extension.anisotropyTexture) {
|
|
763
|
+
addTexture(device, extension.anisotropyTexture, 'pbr_anisotropySampler', parsedMaterial, {
|
|
764
|
+
featureOptions: {
|
|
765
|
+
define: 'HAS_ANISOTROPYMAP',
|
|
766
|
+
enabledUniformName: 'anisotropyMapEnabled'
|
|
767
|
+
},
|
|
768
|
+
gltf
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
function parseEmissiveStrengthExtension(
|
|
774
|
+
extension: GLTFMaterialEmissiveStrengthExtension | undefined,
|
|
775
|
+
parsedMaterial: ParsedPBRMaterial
|
|
776
|
+
): void {
|
|
777
|
+
if (extension?.emissiveStrength !== undefined) {
|
|
778
|
+
parsedMaterial.uniforms.emissiveStrength = extension.emissiveStrength;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
233
782
|
/** Create a texture from a glTF texture/sampler/image combo and add it to bindings */
|
|
234
783
|
function addTexture(
|
|
235
784
|
device: Device,
|
|
236
785
|
gltfTexture: GLTFTexture,
|
|
237
786
|
uniformName: keyof PBRMaterialBindings,
|
|
238
|
-
|
|
239
|
-
|
|
787
|
+
parsedMaterial: ParsedPBRMaterial,
|
|
788
|
+
textureParseOptions: TextureParseOptions = {}
|
|
240
789
|
): void {
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
textureOptions = {data: image};
|
|
790
|
+
const {featureOptions = {}, gltf} = textureParseOptions;
|
|
791
|
+
const {define, enabledUniformName} = featureOptions;
|
|
792
|
+
const resolvedTextureInfo = resolveTextureInfo(gltfTexture, gltf);
|
|
793
|
+
const image = resolvedTextureInfo.texture?.source?.image;
|
|
794
|
+
if (!image) {
|
|
795
|
+
log.warn(`Skipping unresolved glTF texture for ${String(uniformName)}`)();
|
|
796
|
+
return;
|
|
249
797
|
}
|
|
250
798
|
|
|
251
799
|
const gltfSampler = {
|
|
@@ -253,20 +801,251 @@ function addTexture(
|
|
|
253
801
|
wrapT: 10497, // default REPEAT T (V) wrapping mode.
|
|
254
802
|
minFilter: 9729, // default LINEAR filtering
|
|
255
803
|
magFilter: 9729, // default LINEAR filtering
|
|
256
|
-
...
|
|
804
|
+
...resolvedTextureInfo?.texture?.sampler
|
|
257
805
|
} as GLTFSampler;
|
|
258
806
|
|
|
259
|
-
const
|
|
260
|
-
id:
|
|
261
|
-
sampler: convertSampler(gltfSampler)
|
|
262
|
-
|
|
263
|
-
|
|
807
|
+
const baseOptions = {
|
|
808
|
+
id: resolvedTextureInfo.uniformName || resolvedTextureInfo.id,
|
|
809
|
+
sampler: convertSampler(gltfSampler)
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
let texture: Texture;
|
|
813
|
+
|
|
814
|
+
if (image.compressed) {
|
|
815
|
+
texture = createCompressedTexture(device, image, baseOptions);
|
|
816
|
+
} else {
|
|
817
|
+
const {width, height} = device.getExternalImageSize(image);
|
|
818
|
+
texture = device.createTexture({
|
|
819
|
+
...baseOptions,
|
|
820
|
+
width,
|
|
821
|
+
height,
|
|
822
|
+
data: image
|
|
823
|
+
});
|
|
824
|
+
}
|
|
264
825
|
|
|
265
826
|
parsedMaterial.bindings[uniformName] = texture;
|
|
266
827
|
if (define) parsedMaterial.defines[define] = true;
|
|
828
|
+
if (enabledUniformName) {
|
|
829
|
+
parsedMaterial.uniforms[enabledUniformName] = true;
|
|
830
|
+
}
|
|
267
831
|
parsedMaterial.generatedTextures.push(texture);
|
|
268
832
|
}
|
|
269
833
|
|
|
834
|
+
function resolveTextureInfo(gltfTexture: GLTFTexture, gltf?: GLTFPostprocessed): GLTFTexture {
|
|
835
|
+
if (gltfTexture.texture || gltfTexture.index === undefined || !gltf?.textures) {
|
|
836
|
+
return gltfTexture;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
const resolvedTextureEntry = gltf.textures[gltfTexture.index] as
|
|
840
|
+
| Partial<GLTFTexture>
|
|
841
|
+
| GLTFTexture['texture']
|
|
842
|
+
| undefined;
|
|
843
|
+
if (!resolvedTextureEntry) {
|
|
844
|
+
return gltfTexture;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if ('texture' in resolvedTextureEntry && resolvedTextureEntry.texture) {
|
|
848
|
+
return {
|
|
849
|
+
...resolvedTextureEntry,
|
|
850
|
+
...gltfTexture,
|
|
851
|
+
texture: resolvedTextureEntry.texture
|
|
852
|
+
} as GLTFTexture;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (!('source' in resolvedTextureEntry)) {
|
|
856
|
+
return gltfTexture;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
return {
|
|
860
|
+
...gltfTexture,
|
|
861
|
+
texture: resolvedTextureEntry
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/** One mip level as produced by loaders.gl compressed texture parsers */
|
|
866
|
+
export type CompressedMipLevel = {
|
|
867
|
+
data: TypedArray;
|
|
868
|
+
width: number;
|
|
869
|
+
height: number;
|
|
870
|
+
textureFormat?: TextureFormat;
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Compressed image from current loaders.gl releases.
|
|
875
|
+
* - `mipmaps` is a boolean (true), NOT an array
|
|
876
|
+
* - `data` is an Array of TextureLevel-like objects
|
|
877
|
+
* - Per-level `textureFormat` is already a luma.gl TextureFormat
|
|
878
|
+
* - Top-level `width`/`height` may be undefined
|
|
879
|
+
*/
|
|
880
|
+
export type CompressedImageDataArray = {
|
|
881
|
+
compressed: true;
|
|
882
|
+
mipmaps?: boolean;
|
|
883
|
+
width?: number;
|
|
884
|
+
height?: number;
|
|
885
|
+
data: CompressedMipLevel[];
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Hypothetical future format where `mipmaps` is an actual array.
|
|
890
|
+
* Kept for forward compatibility.
|
|
891
|
+
*/
|
|
892
|
+
export type CompressedImageMipmapArray = {
|
|
893
|
+
compressed: true;
|
|
894
|
+
width?: number;
|
|
895
|
+
height?: number;
|
|
896
|
+
mipmaps: CompressedMipLevel[];
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
/** Union of all known loaders.gl compressed image shapes */
|
|
900
|
+
export type CompressedImage = CompressedImageDataArray | CompressedImageMipmapArray;
|
|
901
|
+
|
|
902
|
+
function createCompressedTextureFallback(
|
|
903
|
+
device: Device,
|
|
904
|
+
baseOptions: {id: string; sampler: SamplerProps}
|
|
905
|
+
): Texture {
|
|
906
|
+
return device.createTexture({
|
|
907
|
+
...baseOptions,
|
|
908
|
+
format: 'rgba8unorm',
|
|
909
|
+
width: 1,
|
|
910
|
+
height: 1,
|
|
911
|
+
mipLevels: 1
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function resolveCompressedTextureFormat(level: CompressedMipLevel): TextureFormat | undefined {
|
|
916
|
+
return level.textureFormat;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Maximum mip levels that can be filled for a compressed texture.
|
|
921
|
+
* texStorage2D allocates level i at (baseW >> i) × (baseH >> i).
|
|
922
|
+
* Compressed formats can't upload data for levels smaller than one block,
|
|
923
|
+
* so we stop before either dimension drops below the block size.
|
|
924
|
+
*/
|
|
925
|
+
function getMaxCompressedMipLevels(
|
|
926
|
+
baseWidth: number,
|
|
927
|
+
baseHeight: number,
|
|
928
|
+
format: TextureFormat
|
|
929
|
+
): number {
|
|
930
|
+
const {blockWidth = 1, blockHeight = 1} = textureFormatDecoder.getInfo(format);
|
|
931
|
+
let count = 1;
|
|
932
|
+
for (let i = 1; ; i++) {
|
|
933
|
+
const w = Math.max(1, baseWidth >> i);
|
|
934
|
+
const h = Math.max(1, baseHeight >> i);
|
|
935
|
+
if (w < blockWidth || h < blockHeight) break;
|
|
936
|
+
count++;
|
|
937
|
+
}
|
|
938
|
+
return count;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Create a texture from compressed image data produced by loaders.gl.
|
|
943
|
+
* Handles current loaders.gl compressed image layouts:
|
|
944
|
+
*
|
|
945
|
+
* current: {compressed, mipmaps: true, data: [{data, width, height, textureFormat}, ...]}
|
|
946
|
+
* forward: {compressed, mipmaps: [{data, width, height, textureFormat}, ...]}
|
|
947
|
+
*/
|
|
948
|
+
export function createCompressedTexture(
|
|
949
|
+
device: Device,
|
|
950
|
+
image: CompressedImage,
|
|
951
|
+
baseOptions: {id: string; sampler: SamplerProps}
|
|
952
|
+
): Texture {
|
|
953
|
+
// Normalize mip levels from all known loaders.gl formats
|
|
954
|
+
let levels: CompressedMipLevel[];
|
|
955
|
+
|
|
956
|
+
if (Array.isArray((image as any).data) && (image as any).data[0]?.data) {
|
|
957
|
+
// loaders.gl current format: image.data is Array of mip-level objects
|
|
958
|
+
levels = (image as CompressedImageDataArray).data;
|
|
959
|
+
} else if ('mipmaps' in image && Array.isArray((image as CompressedImageMipmapArray).mipmaps)) {
|
|
960
|
+
// Hypothetical future format: image.mipmaps is an Array
|
|
961
|
+
levels = (image as CompressedImageMipmapArray).mipmaps;
|
|
962
|
+
} else {
|
|
963
|
+
levels = [];
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
if (levels.length === 0 || !levels[0]?.data) {
|
|
967
|
+
log.warn(
|
|
968
|
+
'createCompressedTexture: compressed image has no valid mip levels, creating fallback'
|
|
969
|
+
)();
|
|
970
|
+
return createCompressedTextureFallback(device, baseOptions);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
const baseLevel = levels[0];
|
|
974
|
+
const baseWidth = baseLevel.width ?? (image as any).width ?? 0;
|
|
975
|
+
const baseHeight = baseLevel.height ?? (image as any).height ?? 0;
|
|
976
|
+
|
|
977
|
+
if (baseWidth <= 0 || baseHeight <= 0) {
|
|
978
|
+
log.warn('createCompressedTexture: base level has invalid dimensions, creating fallback')();
|
|
979
|
+
return createCompressedTextureFallback(device, baseOptions);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
const format = resolveCompressedTextureFormat(baseLevel);
|
|
983
|
+
|
|
984
|
+
if (!format) {
|
|
985
|
+
log.warn('createCompressedTexture: compressed image has no textureFormat, creating fallback')();
|
|
986
|
+
return createCompressedTextureFallback(device, baseOptions);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// Validate mip levels: truncate chain at first invalid level.
|
|
990
|
+
// Levels must be contiguous, so we stop at the first level that has
|
|
991
|
+
// a format mismatch, missing data, non-positive dimensions, or
|
|
992
|
+
// dimensions that don't match what texStorage2D will allocate.
|
|
993
|
+
//
|
|
994
|
+
// For block-compressed formats (ASTC, BC, ETC2), texStorage2D allocates
|
|
995
|
+
// mip levels down to 1×1 texels, but compressed data can't be smaller
|
|
996
|
+
// than one block (e.g. 4×4 for ASTC-4x4). Cap the chain so we never
|
|
997
|
+
// try to upload data whose block-aligned size exceeds the allocated level.
|
|
998
|
+
const maxMipLevels = getMaxCompressedMipLevels(baseWidth, baseHeight, format);
|
|
999
|
+
const levelLimit = Math.min(levels.length, maxMipLevels);
|
|
1000
|
+
|
|
1001
|
+
let validLevelCount = 1;
|
|
1002
|
+
for (let i = 1; i < levelLimit; i++) {
|
|
1003
|
+
const level = levels[i];
|
|
1004
|
+
if (!level.data || level.width <= 0 || level.height <= 0) {
|
|
1005
|
+
log.warn(`createCompressedTexture: mip level ${i} has invalid data/dimensions, truncating`)();
|
|
1006
|
+
break;
|
|
1007
|
+
}
|
|
1008
|
+
const levelFormat = resolveCompressedTextureFormat(level);
|
|
1009
|
+
if (levelFormat && levelFormat !== format) {
|
|
1010
|
+
log.warn(
|
|
1011
|
+
`createCompressedTexture: mip level ${i} format '${levelFormat}' differs from base '${format}', truncating`
|
|
1012
|
+
)();
|
|
1013
|
+
break;
|
|
1014
|
+
}
|
|
1015
|
+
const expectedW = Math.max(1, baseWidth >> i);
|
|
1016
|
+
const expectedH = Math.max(1, baseHeight >> i);
|
|
1017
|
+
if (level.width !== expectedW || level.height !== expectedH) {
|
|
1018
|
+
log.warn(
|
|
1019
|
+
`createCompressedTexture: mip level ${i} dimensions ${level.width}x${level.height} ` +
|
|
1020
|
+
`don't match expected ${expectedW}x${expectedH}, truncating`
|
|
1021
|
+
)();
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
1024
|
+
validLevelCount++;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
const texture = device.createTexture({
|
|
1028
|
+
...baseOptions,
|
|
1029
|
+
format,
|
|
1030
|
+
usage: Texture.TEXTURE | Texture.COPY_DST,
|
|
1031
|
+
width: baseWidth,
|
|
1032
|
+
height: baseHeight,
|
|
1033
|
+
mipLevels: validLevelCount,
|
|
1034
|
+
data: baseLevel.data
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
// Upload additional validated mip levels
|
|
1038
|
+
for (let i = 1; i < validLevelCount; i++) {
|
|
1039
|
+
texture.writeData(levels[i].data, {
|
|
1040
|
+
width: levels[i].width,
|
|
1041
|
+
height: levels[i].height,
|
|
1042
|
+
mipLevel: i
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
return texture;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
270
1049
|
/*
|
|
271
1050
|
/**
|
|
272
1051
|
* Parses a GLTF material definition into uniforms and parameters for the PBR shader module
|
|
@@ -312,7 +1091,7 @@ export class PBRMaterialParser {
|
|
|
312
1091
|
if (imageBasedLightingEnvironment) {
|
|
313
1092
|
this.bindings.pbr_diffuseEnvSampler = imageBasedLightingEnvironment.getDiffuseEnvSampler();
|
|
314
1093
|
this.bindings.pbr_specularEnvSampler = imageBasedLightingEnvironment.getSpecularEnvSampler();
|
|
315
|
-
this.bindings.
|
|
1094
|
+
this.bindings.pbr_brdfLUT = imageBasedLightingEnvironment.getBrdfTexture();
|
|
316
1095
|
this.uniforms.scaleIBLAmbient = [1, 1];
|
|
317
1096
|
}
|
|
318
1097
|
|
|
@@ -382,8 +1161,13 @@ export class PBRMaterialParser {
|
|
|
382
1161
|
log.warn('BLEND alphaMode might not work well because it requires mesh sorting')();
|
|
383
1162
|
Object.assign(this.parameters, {
|
|
384
1163
|
blend: true,
|
|
385
|
-
blendEquation:
|
|
386
|
-
blendFunc: [
|
|
1164
|
+
blendEquation: GLEnum.FUNC_ADD,
|
|
1165
|
+
blendFunc: [
|
|
1166
|
+
GLEnum.SRC_ALPHA,
|
|
1167
|
+
GLEnum.ONE_MINUS_SRC_ALPHA,
|
|
1168
|
+
GLEnum.ONE,
|
|
1169
|
+
GLEnum.ONE_MINUS_SRC_ALPHA
|
|
1170
|
+
]
|
|
387
1171
|
});
|
|
388
1172
|
}
|
|
389
1173
|
}
|
|
@@ -420,7 +1204,8 @@ export class PBRMaterialParser {
|
|
|
420
1204
|
if (image.compressed) {
|
|
421
1205
|
textureOptions = image;
|
|
422
1206
|
specialTextureParameters = {
|
|
423
|
-
[
|
|
1207
|
+
[GLEnum.TEXTURE_MIN_FILTER]:
|
|
1208
|
+
image.data.length > 1 ? GLEnum.LINEAR_MIPMAP_NEAREST : GLEnum.LINEAR
|
|
424
1209
|
};
|
|
425
1210
|
} else {
|
|
426
1211
|
// Texture2D accepts a promise that returns an image as data (Async Textures)
|
|
@@ -434,7 +1219,7 @@ export class PBRMaterialParser {
|
|
|
434
1219
|
...specialTextureParameters
|
|
435
1220
|
},
|
|
436
1221
|
pixelStore: {
|
|
437
|
-
[
|
|
1222
|
+
[GLEnum.UNPACK_FLIP_Y_WEBGL]: false
|
|
438
1223
|
},
|
|
439
1224
|
...textureOptions
|
|
440
1225
|
});
|