@onerjs/serializers 8.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/OBJ/index.d.ts +1 -0
- package/OBJ/index.js +2 -0
- package/OBJ/index.js.map +1 -0
- package/OBJ/objSerializer.d.ts +21 -0
- package/OBJ/objSerializer.js +171 -0
- package/OBJ/objSerializer.js.map +1 -0
- package/USDZ/index.d.ts +1 -0
- package/USDZ/index.js +3 -0
- package/USDZ/index.js.map +1 -0
- package/USDZ/usdzExporter.d.ts +50 -0
- package/USDZ/usdzExporter.js +589 -0
- package/USDZ/usdzExporter.js.map +1 -0
- package/exportUtils.d.ts +12 -0
- package/exportUtils.js +37 -0
- package/exportUtils.js.map +1 -0
- package/glTF/2.0/Extensions/EXT_materials_diffuse_roughness.d.ts +24 -0
- package/glTF/2.0/Extensions/EXT_materials_diffuse_roughness.js +61 -0
- package/glTF/2.0/Extensions/EXT_materials_diffuse_roughness.js.map +1 -0
- package/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.d.ts +36 -0
- package/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.js +109 -0
- package/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_draco_mesh_compression.d.ts +32 -0
- package/glTF/2.0/Extensions/KHR_draco_mesh_compression.js +135 -0
- package/glTF/2.0/Extensions/KHR_draco_mesh_compression.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_lights_punctual.d.ts +39 -0
- package/glTF/2.0/Extensions/KHR_lights_punctual.js +145 -0
- package/glTF/2.0/Extensions/KHR_lights_punctual.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_materials_anisotropy.d.ts +24 -0
- package/glTF/2.0/Extensions/KHR_materials_anisotropy.js +62 -0
- package/glTF/2.0/Extensions/KHR_materials_anisotropy.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_materials_clearcoat.d.ts +24 -0
- package/glTF/2.0/Extensions/KHR_materials_clearcoat.js +85 -0
- package/glTF/2.0/Extensions/KHR_materials_clearcoat.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.d.ts +40 -0
- package/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.js +118 -0
- package/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_materials_dispersion.d.ts +31 -0
- package/glTF/2.0/Extensions/KHR_materials_dispersion.js +63 -0
- package/glTF/2.0/Extensions/KHR_materials_dispersion.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_materials_emissive_strength.d.ts +27 -0
- package/glTF/2.0/Extensions/KHR_materials_emissive_strength.js +54 -0
- package/glTF/2.0/Extensions/KHR_materials_emissive_strength.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_materials_ior.d.ts +29 -0
- package/glTF/2.0/Extensions/KHR_materials_ior.js +55 -0
- package/glTF/2.0/Extensions/KHR_materials_ior.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_materials_iridescence.d.ts +24 -0
- package/glTF/2.0/Extensions/KHR_materials_iridescence.js +69 -0
- package/glTF/2.0/Extensions/KHR_materials_iridescence.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_materials_sheen.d.ts +24 -0
- package/glTF/2.0/Extensions/KHR_materials_sheen.js +66 -0
- package/glTF/2.0/Extensions/KHR_materials_sheen.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_materials_specular.d.ts +41 -0
- package/glTF/2.0/Extensions/KHR_materials_specular.js +94 -0
- package/glTF/2.0/Extensions/KHR_materials_specular.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_materials_transmission.d.ts +41 -0
- package/glTF/2.0/Extensions/KHR_materials_transmission.js +92 -0
- package/glTF/2.0/Extensions/KHR_materials_transmission.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_materials_unlit.d.ts +20 -0
- package/glTF/2.0/Extensions/KHR_materials_unlit.js +46 -0
- package/glTF/2.0/Extensions/KHR_materials_unlit.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_materials_volume.d.ts +40 -0
- package/glTF/2.0/Extensions/KHR_materials_volume.js +96 -0
- package/glTF/2.0/Extensions/KHR_materials_volume.js.map +1 -0
- package/glTF/2.0/Extensions/KHR_texture_transform.d.ts +21 -0
- package/glTF/2.0/Extensions/KHR_texture_transform.js +93 -0
- package/glTF/2.0/Extensions/KHR_texture_transform.js.map +1 -0
- package/glTF/2.0/Extensions/index.d.ts +17 -0
- package/glTF/2.0/Extensions/index.js +18 -0
- package/glTF/2.0/Extensions/index.js.map +1 -0
- package/glTF/2.0/bufferManager.d.ts +68 -0
- package/glTF/2.0/bufferManager.js +154 -0
- package/glTF/2.0/bufferManager.js.map +1 -0
- package/glTF/2.0/dataWriter.d.ts +20 -0
- package/glTF/2.0/dataWriter.js +83 -0
- package/glTF/2.0/dataWriter.js.map +1 -0
- package/glTF/2.0/glTFAnimation.d.ts +191 -0
- package/glTF/2.0/glTFAnimation.js +786 -0
- package/glTF/2.0/glTFAnimation.js.map +1 -0
- package/glTF/2.0/glTFData.d.ts +21 -0
- package/glTF/2.0/glTFData.js +30 -0
- package/glTF/2.0/glTFData.js.map +1 -0
- package/glTF/2.0/glTFExporter.d.ts +109 -0
- package/glTF/2.0/glTFExporter.js +1166 -0
- package/glTF/2.0/glTFExporter.js.map +1 -0
- package/glTF/2.0/glTFExporterExtension.d.ts +77 -0
- package/glTF/2.0/glTFExporterExtension.js +4 -0
- package/glTF/2.0/glTFExporterExtension.js.map +1 -0
- package/glTF/2.0/glTFMaterialExporter.d.ts +99 -0
- package/glTF/2.0/glTFMaterialExporter.js +778 -0
- package/glTF/2.0/glTFMaterialExporter.js.map +1 -0
- package/glTF/2.0/glTFMorphTargetsUtilities.d.ts +14 -0
- package/glTF/2.0/glTFMorphTargetsUtilities.js +149 -0
- package/glTF/2.0/glTFMorphTargetsUtilities.js.map +1 -0
- package/glTF/2.0/glTFSerializer.d.ts +78 -0
- package/glTF/2.0/glTFSerializer.js +39 -0
- package/glTF/2.0/glTFSerializer.js.map +1 -0
- package/glTF/2.0/glTFUtilities.d.ts +89 -0
- package/glTF/2.0/glTFUtilities.js +349 -0
- package/glTF/2.0/glTFUtilities.js.map +1 -0
- package/glTF/2.0/index.d.ts +4 -0
- package/glTF/2.0/index.js +6 -0
- package/glTF/2.0/index.js.map +1 -0
- package/glTF/glTFFileExporter.d.ts +20 -0
- package/glTF/glTFFileExporter.js +4 -0
- package/glTF/glTFFileExporter.js.map +1 -0
- package/glTF/index.d.ts +2 -0
- package/glTF/index.js +4 -0
- package/glTF/index.js.map +1 -0
- package/index.d.ts +4 -0
- package/index.js +6 -0
- package/index.js.map +1 -0
- package/legacy/legacy-glTF2Serializer.d.ts +2 -0
- package/legacy/legacy-glTF2Serializer.js +45 -0
- package/legacy/legacy-glTF2Serializer.js.map +1 -0
- package/legacy/legacy-objSerializer.d.ts +1 -0
- package/legacy/legacy-objSerializer.js +14 -0
- package/legacy/legacy-objSerializer.js.map +1 -0
- package/legacy/legacy-stlSerializer.d.ts +1 -0
- package/legacy/legacy-stlSerializer.js +14 -0
- package/legacy/legacy-stlSerializer.js.map +1 -0
- package/legacy/legacy-usdzSerializer.d.ts +1 -0
- package/legacy/legacy-usdzSerializer.js +14 -0
- package/legacy/legacy-usdzSerializer.js.map +1 -0
- package/legacy/legacy.d.ts +5 -0
- package/legacy/legacy.js +8 -0
- package/legacy/legacy.js.map +1 -0
- package/license.md +71 -0
- package/package.json +49 -0
- package/readme.md +29 -0
- package/stl/index.d.ts +1 -0
- package/stl/index.js +2 -0
- package/stl/index.js.map +1 -0
- package/stl/stlSerializer.d.ts +20 -0
- package/stl/stlSerializer.js +135 -0
- package/stl/stlSerializer.js.map +1 -0
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/prefer-promise-reject-errors */
|
|
2
|
+
/* eslint-disable github/no-then */
|
|
3
|
+
/* eslint-disable babylonjs/available */
|
|
4
|
+
import { Color3 } from "@babylonjs/core/Maths/math.color.js";
|
|
5
|
+
import { Scalar } from "@babylonjs/core/Maths/math.scalar.js";
|
|
6
|
+
import { Tools } from "@babylonjs/core/Misc/tools.js";
|
|
7
|
+
import { GetTextureDataAsync, TextureTools } from "@babylonjs/core/Misc/textureTools.js";
|
|
8
|
+
import { Texture } from "@babylonjs/core/Materials/Textures/texture.js";
|
|
9
|
+
import { RawTexture } from "@babylonjs/core/Materials/Textures/rawTexture.js";
|
|
10
|
+
import { Constants } from "@babylonjs/core/Engines/constants.js";
|
|
11
|
+
import { DumpTools } from "@babylonjs/core/Misc/dumpTools.js";
|
|
12
|
+
import { SpecularPowerToRoughness } from "@babylonjs/core/Helpers/materialConversionHelper.js";
|
|
13
|
+
const Epsilon = 1e-6;
|
|
14
|
+
const DielectricSpecular = new Color3(0.04, 0.04, 0.04);
|
|
15
|
+
const MaxSpecularPower = 1024;
|
|
16
|
+
const White = Color3.White();
|
|
17
|
+
const Black = Color3.Black();
|
|
18
|
+
function GetFileExtensionFromMimeType(mimeType) {
|
|
19
|
+
switch (mimeType) {
|
|
20
|
+
case "image/jpeg" /* ImageMimeType.JPEG */:
|
|
21
|
+
return ".jpg";
|
|
22
|
+
case "image/png" /* ImageMimeType.PNG */:
|
|
23
|
+
return ".png";
|
|
24
|
+
case "image/webp" /* ImageMimeType.WEBP */:
|
|
25
|
+
return ".webp";
|
|
26
|
+
case "image/avif" /* ImageMimeType.AVIF */:
|
|
27
|
+
return ".avif";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Computes the metallic factor from specular glossiness values.
|
|
32
|
+
* @param diffuse diffused value
|
|
33
|
+
* @param specular specular value
|
|
34
|
+
* @param oneMinusSpecularStrength one minus the specular strength
|
|
35
|
+
* @returns metallic value
|
|
36
|
+
* @internal
|
|
37
|
+
*/
|
|
38
|
+
export function _SolveMetallic(diffuse, specular, oneMinusSpecularStrength) {
|
|
39
|
+
if (specular < DielectricSpecular.r) {
|
|
40
|
+
DielectricSpecular;
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
const a = DielectricSpecular.r;
|
|
44
|
+
const b = (diffuse * oneMinusSpecularStrength) / (1.0 - DielectricSpecular.r) + specular - 2.0 * DielectricSpecular.r;
|
|
45
|
+
const c = DielectricSpecular.r - specular;
|
|
46
|
+
const d = b * b - 4.0 * a * c;
|
|
47
|
+
return Scalar.Clamp((-b + Math.sqrt(d)) / (2.0 * a), 0, 1);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Computes the metallic/roughness factors from a Standard Material.
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
export function _ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial) {
|
|
54
|
+
const diffuse = babylonStandardMaterial.diffuseColor.toLinearSpace(babylonStandardMaterial.getScene().getEngine().useExactSrgbConversions).scale(0.5);
|
|
55
|
+
const opacity = babylonStandardMaterial.alpha;
|
|
56
|
+
const specularPower = Scalar.Clamp(babylonStandardMaterial.specularPower, 0, MaxSpecularPower);
|
|
57
|
+
const roughness = SpecularPowerToRoughness(specularPower);
|
|
58
|
+
const glTFPbrMetallicRoughness = {
|
|
59
|
+
baseColorFactor: [diffuse.r, diffuse.g, diffuse.b, opacity],
|
|
60
|
+
metallicFactor: 0,
|
|
61
|
+
roughnessFactor: roughness,
|
|
62
|
+
};
|
|
63
|
+
return glTFPbrMetallicRoughness;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Sets the glTF alpha mode to a glTF material from the Babylon Material
|
|
67
|
+
* @param glTFMaterial glTF material
|
|
68
|
+
* @param babylonMaterial Babylon material
|
|
69
|
+
*/
|
|
70
|
+
function SetAlphaMode(glTFMaterial, babylonMaterial) {
|
|
71
|
+
if (babylonMaterial.needAlphaBlending()) {
|
|
72
|
+
glTFMaterial.alphaMode = "BLEND" /* MaterialAlphaMode.BLEND */;
|
|
73
|
+
}
|
|
74
|
+
else if (babylonMaterial.needAlphaTesting()) {
|
|
75
|
+
glTFMaterial.alphaMode = "MASK" /* MaterialAlphaMode.MASK */;
|
|
76
|
+
glTFMaterial.alphaCutoff = babylonMaterial.alphaCutOff;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function CreateWhiteTexture(width, height, scene) {
|
|
80
|
+
const data = new Uint8Array(width * height * 4);
|
|
81
|
+
for (let i = 0; i < data.length; i = i + 4) {
|
|
82
|
+
data[i] = data[i + 1] = data[i + 2] = data[i + 3] = 0xff;
|
|
83
|
+
}
|
|
84
|
+
const rawTexture = RawTexture.CreateRGBATexture(data, width, height, scene);
|
|
85
|
+
return rawTexture;
|
|
86
|
+
}
|
|
87
|
+
function ConvertPixelArrayToFloat32(pixels) {
|
|
88
|
+
if (pixels instanceof Uint8Array) {
|
|
89
|
+
const length = pixels.length;
|
|
90
|
+
const buffer = new Float32Array(pixels.length);
|
|
91
|
+
for (let i = 0; i < length; ++i) {
|
|
92
|
+
buffer[i] = pixels[i] / 255;
|
|
93
|
+
}
|
|
94
|
+
return buffer;
|
|
95
|
+
}
|
|
96
|
+
else if (pixels instanceof Float32Array) {
|
|
97
|
+
return pixels;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
throw new Error("Unsupported pixel format!");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Utility methods for working with glTF material conversion properties.
|
|
105
|
+
* @internal
|
|
106
|
+
*/
|
|
107
|
+
export class GLTFMaterialExporter {
|
|
108
|
+
constructor(_exporter) {
|
|
109
|
+
this._exporter = _exporter;
|
|
110
|
+
// Mapping to store textures
|
|
111
|
+
this._textureMap = new Map();
|
|
112
|
+
// Mapping of internal textures to images to avoid exporting duplicate images
|
|
113
|
+
this._internalTextureToImage = {};
|
|
114
|
+
}
|
|
115
|
+
getTextureInfo(babylonTexture) {
|
|
116
|
+
return babylonTexture ? (this._textureMap.get(babylonTexture) ?? null) : null;
|
|
117
|
+
}
|
|
118
|
+
async exportStandardMaterialAsync(babylonStandardMaterial, mimeType, hasUVs) {
|
|
119
|
+
const pbrMetallicRoughness = _ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
|
|
120
|
+
const material = { name: babylonStandardMaterial.name };
|
|
121
|
+
if (babylonStandardMaterial.backFaceCulling != null && !babylonStandardMaterial.backFaceCulling) {
|
|
122
|
+
if (!babylonStandardMaterial.twoSidedLighting) {
|
|
123
|
+
Tools.Warn(babylonStandardMaterial.name + ": Back-face culling disabled and two-sided lighting disabled is not supported in glTF.");
|
|
124
|
+
}
|
|
125
|
+
material.doubleSided = true;
|
|
126
|
+
}
|
|
127
|
+
if (hasUVs) {
|
|
128
|
+
const promises = [];
|
|
129
|
+
const diffuseTexture = babylonStandardMaterial.diffuseTexture;
|
|
130
|
+
if (diffuseTexture) {
|
|
131
|
+
promises.push(this.exportTextureAsync(diffuseTexture, mimeType).then((textureInfo) => {
|
|
132
|
+
if (textureInfo) {
|
|
133
|
+
pbrMetallicRoughness.baseColorTexture = textureInfo;
|
|
134
|
+
}
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
const bumpTexture = babylonStandardMaterial.bumpTexture;
|
|
138
|
+
if (bumpTexture) {
|
|
139
|
+
promises.push(this.exportTextureAsync(bumpTexture, mimeType).then((textureInfo) => {
|
|
140
|
+
if (textureInfo) {
|
|
141
|
+
material.normalTexture = textureInfo;
|
|
142
|
+
if (bumpTexture.level !== 1) {
|
|
143
|
+
material.normalTexture.scale = bumpTexture.level;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
const emissiveTexture = babylonStandardMaterial.emissiveTexture;
|
|
149
|
+
if (emissiveTexture) {
|
|
150
|
+
material.emissiveFactor = [1.0, 1.0, 1.0];
|
|
151
|
+
promises.push(this.exportTextureAsync(emissiveTexture, mimeType).then((textureInfo) => {
|
|
152
|
+
if (textureInfo) {
|
|
153
|
+
material.emissiveTexture = textureInfo;
|
|
154
|
+
}
|
|
155
|
+
}));
|
|
156
|
+
}
|
|
157
|
+
const ambientTexture = babylonStandardMaterial.ambientTexture;
|
|
158
|
+
if (ambientTexture) {
|
|
159
|
+
promises.push(this.exportTextureAsync(ambientTexture, mimeType).then((textureInfo) => {
|
|
160
|
+
if (textureInfo) {
|
|
161
|
+
const occlusionTexture = {
|
|
162
|
+
index: textureInfo.index,
|
|
163
|
+
};
|
|
164
|
+
material.occlusionTexture = occlusionTexture;
|
|
165
|
+
}
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
if (promises.length > 0) {
|
|
169
|
+
this._exporter._materialNeedsUVsSet.add(babylonStandardMaterial);
|
|
170
|
+
await Promise.all(promises);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (babylonStandardMaterial.alpha < 1.0 || babylonStandardMaterial.opacityTexture) {
|
|
174
|
+
if (babylonStandardMaterial.alphaMode === Constants.ALPHA_COMBINE) {
|
|
175
|
+
material.alphaMode = "BLEND" /* MaterialAlphaMode.BLEND */;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
Tools.Warn(babylonStandardMaterial.name + ": glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (babylonStandardMaterial.emissiveColor && !babylonStandardMaterial.emissiveColor.equalsWithEpsilon(Black, Epsilon)) {
|
|
182
|
+
material.emissiveFactor = babylonStandardMaterial.emissiveColor.asArray();
|
|
183
|
+
}
|
|
184
|
+
material.pbrMetallicRoughness = pbrMetallicRoughness;
|
|
185
|
+
SetAlphaMode(material, babylonStandardMaterial);
|
|
186
|
+
await this._finishMaterialAsync(material, babylonStandardMaterial, mimeType);
|
|
187
|
+
const materials = this._exporter._materials;
|
|
188
|
+
materials.push(material);
|
|
189
|
+
return materials.length - 1;
|
|
190
|
+
}
|
|
191
|
+
async _finishMaterialAsync(glTFMaterial, babylonMaterial, mimeType) {
|
|
192
|
+
const textures = this._exporter._extensionsPostExportMaterialAdditionalTextures("exportMaterial", glTFMaterial, babylonMaterial);
|
|
193
|
+
const promises = [];
|
|
194
|
+
for (const texture of textures) {
|
|
195
|
+
promises.push(this.exportTextureAsync(texture, mimeType));
|
|
196
|
+
}
|
|
197
|
+
await Promise.all(promises);
|
|
198
|
+
await this._exporter._extensionsPostExportMaterialAsync("exportMaterial", glTFMaterial, babylonMaterial);
|
|
199
|
+
}
|
|
200
|
+
async _getImageDataAsync(buffer, width, height, mimeType) {
|
|
201
|
+
return await DumpTools.DumpDataAsync(width, height, buffer, mimeType, undefined, false, true);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Resizes the two source textures to the same dimensions. If a texture is null, a default white texture is generated. If both textures are null, returns null
|
|
205
|
+
* @param texture1 first texture to resize
|
|
206
|
+
* @param texture2 second texture to resize
|
|
207
|
+
* @param scene babylonjs scene
|
|
208
|
+
* @returns resized textures or null
|
|
209
|
+
*/
|
|
210
|
+
_resizeTexturesToSameDimensions(texture1, texture2, scene) {
|
|
211
|
+
const texture1Size = texture1 ? texture1.getSize() : { width: 0, height: 0 };
|
|
212
|
+
const texture2Size = texture2 ? texture2.getSize() : { width: 0, height: 0 };
|
|
213
|
+
let resizedTexture1;
|
|
214
|
+
let resizedTexture2;
|
|
215
|
+
if (texture1Size.width < texture2Size.width) {
|
|
216
|
+
if (texture1 && texture1 instanceof Texture) {
|
|
217
|
+
resizedTexture1 = TextureTools.CreateResizedCopy(texture1, texture2Size.width, texture2Size.height, true);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
resizedTexture1 = CreateWhiteTexture(texture2Size.width, texture2Size.height, scene);
|
|
221
|
+
}
|
|
222
|
+
resizedTexture2 = texture2;
|
|
223
|
+
}
|
|
224
|
+
else if (texture1Size.width > texture2Size.width) {
|
|
225
|
+
if (texture2 && texture2 instanceof Texture) {
|
|
226
|
+
resizedTexture2 = TextureTools.CreateResizedCopy(texture2, texture1Size.width, texture1Size.height, true);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
resizedTexture2 = CreateWhiteTexture(texture1Size.width, texture1Size.height, scene);
|
|
230
|
+
}
|
|
231
|
+
resizedTexture1 = texture1;
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
resizedTexture1 = texture1;
|
|
235
|
+
resizedTexture2 = texture2;
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
texture1: resizedTexture1,
|
|
239
|
+
texture2: resizedTexture2,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Convert Specular Glossiness Textures to Metallic Roughness
|
|
244
|
+
* See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
|
|
245
|
+
* @see https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-.js/babylon.pbrUtilities.js
|
|
246
|
+
* @param diffuseTexture texture used to store diffuse information
|
|
247
|
+
* @param specularGlossinessTexture texture used to store specular and glossiness information
|
|
248
|
+
* @param factors specular glossiness material factors
|
|
249
|
+
* @param mimeType the mime type to use for the texture
|
|
250
|
+
* @returns pbr metallic roughness interface or null
|
|
251
|
+
*/
|
|
252
|
+
async _convertSpecularGlossinessTexturesToMetallicRoughnessAsync(diffuseTexture, specularGlossinessTexture, factors, mimeType) {
|
|
253
|
+
const promises = new Array();
|
|
254
|
+
if (!(diffuseTexture || specularGlossinessTexture)) {
|
|
255
|
+
return await Promise.reject("diffuse and specular glossiness textures are not defined!");
|
|
256
|
+
}
|
|
257
|
+
const scene = diffuseTexture ? diffuseTexture.getScene() : specularGlossinessTexture ? specularGlossinessTexture.getScene() : null;
|
|
258
|
+
if (scene) {
|
|
259
|
+
const resizedTextures = this._resizeTexturesToSameDimensions(diffuseTexture, specularGlossinessTexture, scene);
|
|
260
|
+
const diffuseSize = resizedTextures.texture1?.getSize();
|
|
261
|
+
let diffuseBuffer;
|
|
262
|
+
let specularGlossinessBuffer;
|
|
263
|
+
const width = diffuseSize.width;
|
|
264
|
+
const height = diffuseSize.height;
|
|
265
|
+
const diffusePixels = await resizedTextures.texture1.readPixels();
|
|
266
|
+
const specularPixels = await resizedTextures.texture2.readPixels();
|
|
267
|
+
if (diffusePixels) {
|
|
268
|
+
diffuseBuffer = ConvertPixelArrayToFloat32(diffusePixels);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
return await Promise.reject("Failed to retrieve pixels from diffuse texture!");
|
|
272
|
+
}
|
|
273
|
+
if (specularPixels) {
|
|
274
|
+
specularGlossinessBuffer = ConvertPixelArrayToFloat32(specularPixels);
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
return await Promise.reject("Failed to retrieve pixels from specular glossiness texture!");
|
|
278
|
+
}
|
|
279
|
+
const byteLength = specularGlossinessBuffer.byteLength;
|
|
280
|
+
const metallicRoughnessBuffer = new Uint8Array(byteLength);
|
|
281
|
+
const baseColorBuffer = new Uint8Array(byteLength);
|
|
282
|
+
const strideSize = 4;
|
|
283
|
+
const maxBaseColor = Black;
|
|
284
|
+
let maxMetallic = 0;
|
|
285
|
+
let maxRoughness = 0;
|
|
286
|
+
for (let h = 0; h < height; ++h) {
|
|
287
|
+
for (let w = 0; w < width; ++w) {
|
|
288
|
+
const offset = (width * h + w) * strideSize;
|
|
289
|
+
const diffuseColor = new Color3(diffuseBuffer[offset], diffuseBuffer[offset + 1], diffuseBuffer[offset + 2])
|
|
290
|
+
.toLinearSpace(scene.getEngine().useExactSrgbConversions)
|
|
291
|
+
.multiply(factors.diffuseColor);
|
|
292
|
+
const specularColor = new Color3(specularGlossinessBuffer[offset], specularGlossinessBuffer[offset + 1], specularGlossinessBuffer[offset + 2])
|
|
293
|
+
.toLinearSpace(scene.getEngine().useExactSrgbConversions)
|
|
294
|
+
.multiply(factors.specularColor);
|
|
295
|
+
const glossiness = specularGlossinessBuffer[offset + 3] * factors.glossiness;
|
|
296
|
+
const specularGlossiness = {
|
|
297
|
+
diffuseColor: diffuseColor,
|
|
298
|
+
specularColor: specularColor,
|
|
299
|
+
glossiness: glossiness,
|
|
300
|
+
};
|
|
301
|
+
const metallicRoughness = this._convertSpecularGlossinessToMetallicRoughness(specularGlossiness);
|
|
302
|
+
maxBaseColor.r = Math.max(maxBaseColor.r, metallicRoughness.baseColor.r);
|
|
303
|
+
maxBaseColor.g = Math.max(maxBaseColor.g, metallicRoughness.baseColor.g);
|
|
304
|
+
maxBaseColor.b = Math.max(maxBaseColor.b, metallicRoughness.baseColor.b);
|
|
305
|
+
maxMetallic = Math.max(maxMetallic, metallicRoughness.metallic);
|
|
306
|
+
maxRoughness = Math.max(maxRoughness, metallicRoughness.roughness);
|
|
307
|
+
baseColorBuffer[offset] = metallicRoughness.baseColor.r * 255;
|
|
308
|
+
baseColorBuffer[offset + 1] = metallicRoughness.baseColor.g * 255;
|
|
309
|
+
baseColorBuffer[offset + 2] = metallicRoughness.baseColor.b * 255;
|
|
310
|
+
baseColorBuffer[offset + 3] = resizedTextures.texture1.hasAlpha ? diffuseBuffer[offset + 3] * 255 : 255;
|
|
311
|
+
metallicRoughnessBuffer[offset] = 0;
|
|
312
|
+
metallicRoughnessBuffer[offset + 1] = metallicRoughness.roughness * 255;
|
|
313
|
+
metallicRoughnessBuffer[offset + 2] = metallicRoughness.metallic * 255;
|
|
314
|
+
metallicRoughnessBuffer[offset + 3] = 255;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Retrieves the metallic roughness factors from the maximum texture values.
|
|
318
|
+
const metallicRoughnessFactors = {
|
|
319
|
+
baseColor: maxBaseColor,
|
|
320
|
+
metallic: maxMetallic,
|
|
321
|
+
roughness: maxRoughness,
|
|
322
|
+
};
|
|
323
|
+
let writeOutMetallicRoughnessTexture = false;
|
|
324
|
+
let writeOutBaseColorTexture = false;
|
|
325
|
+
for (let h = 0; h < height; ++h) {
|
|
326
|
+
for (let w = 0; w < width; ++w) {
|
|
327
|
+
const destinationOffset = (width * h + w) * strideSize;
|
|
328
|
+
baseColorBuffer[destinationOffset] /= metallicRoughnessFactors.baseColor.r > Epsilon ? metallicRoughnessFactors.baseColor.r : 1;
|
|
329
|
+
baseColorBuffer[destinationOffset + 1] /= metallicRoughnessFactors.baseColor.g > Epsilon ? metallicRoughnessFactors.baseColor.g : 1;
|
|
330
|
+
baseColorBuffer[destinationOffset + 2] /= metallicRoughnessFactors.baseColor.b > Epsilon ? metallicRoughnessFactors.baseColor.b : 1;
|
|
331
|
+
const linearBaseColorPixel = Color3.FromInts(baseColorBuffer[destinationOffset], baseColorBuffer[destinationOffset + 1], baseColorBuffer[destinationOffset + 2]);
|
|
332
|
+
const sRGBBaseColorPixel = linearBaseColorPixel.toGammaSpace(scene.getEngine().useExactSrgbConversions);
|
|
333
|
+
baseColorBuffer[destinationOffset] = sRGBBaseColorPixel.r * 255;
|
|
334
|
+
baseColorBuffer[destinationOffset + 1] = sRGBBaseColorPixel.g * 255;
|
|
335
|
+
baseColorBuffer[destinationOffset + 2] = sRGBBaseColorPixel.b * 255;
|
|
336
|
+
if (!sRGBBaseColorPixel.equalsWithEpsilon(White, Epsilon)) {
|
|
337
|
+
writeOutBaseColorTexture = true;
|
|
338
|
+
}
|
|
339
|
+
metallicRoughnessBuffer[destinationOffset + 1] /= metallicRoughnessFactors.roughness > Epsilon ? metallicRoughnessFactors.roughness : 1;
|
|
340
|
+
metallicRoughnessBuffer[destinationOffset + 2] /= metallicRoughnessFactors.metallic > Epsilon ? metallicRoughnessFactors.metallic : 1;
|
|
341
|
+
const metallicRoughnessPixel = Color3.FromInts(255, metallicRoughnessBuffer[destinationOffset + 1], metallicRoughnessBuffer[destinationOffset + 2]);
|
|
342
|
+
if (!metallicRoughnessPixel.equalsWithEpsilon(White, Epsilon)) {
|
|
343
|
+
writeOutMetallicRoughnessTexture = true;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (writeOutMetallicRoughnessTexture) {
|
|
348
|
+
promises.push(this._getImageDataAsync(metallicRoughnessBuffer, width, height, mimeType).then((data) => {
|
|
349
|
+
metallicRoughnessFactors.metallicRoughnessTextureData = data;
|
|
350
|
+
}));
|
|
351
|
+
}
|
|
352
|
+
if (writeOutBaseColorTexture) {
|
|
353
|
+
promises.push(this._getImageDataAsync(baseColorBuffer, width, height, mimeType).then((data) => {
|
|
354
|
+
metallicRoughnessFactors.baseColorTextureData = data;
|
|
355
|
+
}));
|
|
356
|
+
}
|
|
357
|
+
return await Promise.all(promises).then(() => {
|
|
358
|
+
return metallicRoughnessFactors;
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
return await Promise.reject("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Scene from textures is missing!");
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Converts specular glossiness material properties to metallic roughness
|
|
367
|
+
* @param specularGlossiness interface with specular glossiness material properties
|
|
368
|
+
* @returns interface with metallic roughness material properties
|
|
369
|
+
*/
|
|
370
|
+
_convertSpecularGlossinessToMetallicRoughness(specularGlossiness) {
|
|
371
|
+
const diffusePerceivedBrightness = this._getPerceivedBrightness(specularGlossiness.diffuseColor);
|
|
372
|
+
const specularPerceivedBrightness = this._getPerceivedBrightness(specularGlossiness.specularColor);
|
|
373
|
+
const oneMinusSpecularStrength = 1 - this._getMaxComponent(specularGlossiness.specularColor);
|
|
374
|
+
const metallic = _SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
|
|
375
|
+
const baseColorFromDiffuse = specularGlossiness.diffuseColor.scale(oneMinusSpecularStrength / (1.0 - DielectricSpecular.r) / Math.max(1 - metallic));
|
|
376
|
+
const baseColorFromSpecular = specularGlossiness.specularColor.subtract(DielectricSpecular.scale(1 - metallic)).scale(1 / Math.max(metallic));
|
|
377
|
+
let baseColor = Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
|
|
378
|
+
baseColor = baseColor.clampToRef(0, 1, baseColor);
|
|
379
|
+
const metallicRoughness = {
|
|
380
|
+
baseColor: baseColor,
|
|
381
|
+
metallic: metallic,
|
|
382
|
+
roughness: 1 - specularGlossiness.glossiness,
|
|
383
|
+
};
|
|
384
|
+
return metallicRoughness;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Calculates the surface reflectance, independent of lighting conditions
|
|
388
|
+
* @param color Color source to calculate brightness from
|
|
389
|
+
* @returns number representing the perceived brightness, or zero if color is undefined
|
|
390
|
+
*/
|
|
391
|
+
_getPerceivedBrightness(color) {
|
|
392
|
+
if (color) {
|
|
393
|
+
return Math.sqrt(0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b);
|
|
394
|
+
}
|
|
395
|
+
return 0;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Returns the maximum color component value
|
|
399
|
+
* @param color
|
|
400
|
+
* @returns maximum color component value, or zero if color is null or undefined
|
|
401
|
+
*/
|
|
402
|
+
_getMaxComponent(color) {
|
|
403
|
+
if (color) {
|
|
404
|
+
return Math.max(color.r, Math.max(color.g, color.b));
|
|
405
|
+
}
|
|
406
|
+
return 0;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Convert a PBRMaterial (Metallic/Roughness) to Metallic Roughness factors
|
|
410
|
+
* @param babylonPBRMaterial BJS PBR Metallic Roughness Material
|
|
411
|
+
* @param mimeType mime type to use for the textures
|
|
412
|
+
* @param glTFPbrMetallicRoughness glTF PBR Metallic Roughness interface
|
|
413
|
+
* @param hasUVs specifies if texture coordinates are present on the submesh to determine if textures should be applied
|
|
414
|
+
* @returns glTF PBR Metallic Roughness factors
|
|
415
|
+
*/
|
|
416
|
+
async _convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasUVs) {
|
|
417
|
+
const promises = [];
|
|
418
|
+
const metallicRoughness = {
|
|
419
|
+
baseColor: babylonPBRMaterial._albedoColor,
|
|
420
|
+
metallic: babylonPBRMaterial._metallic,
|
|
421
|
+
roughness: babylonPBRMaterial._roughness,
|
|
422
|
+
};
|
|
423
|
+
if (hasUVs) {
|
|
424
|
+
const albedoTexture = babylonPBRMaterial._albedoTexture;
|
|
425
|
+
if (albedoTexture) {
|
|
426
|
+
promises.push(this.exportTextureAsync(babylonPBRMaterial._albedoTexture, mimeType).then((glTFTexture) => {
|
|
427
|
+
if (glTFTexture) {
|
|
428
|
+
glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
|
|
429
|
+
}
|
|
430
|
+
}));
|
|
431
|
+
}
|
|
432
|
+
const metallicTexture = babylonPBRMaterial._metallicTexture;
|
|
433
|
+
if (metallicTexture) {
|
|
434
|
+
promises.push(this.exportTextureAsync(metallicTexture, mimeType).then((glTFTexture) => {
|
|
435
|
+
if (glTFTexture) {
|
|
436
|
+
glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
|
|
437
|
+
}
|
|
438
|
+
}));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (promises.length > 0) {
|
|
442
|
+
this._exporter._materialNeedsUVsSet.add(babylonPBRMaterial);
|
|
443
|
+
await Promise.all(promises);
|
|
444
|
+
}
|
|
445
|
+
return metallicRoughness;
|
|
446
|
+
}
|
|
447
|
+
_getTextureSampler(texture) {
|
|
448
|
+
const sampler = {};
|
|
449
|
+
if (!texture || !(texture instanceof Texture)) {
|
|
450
|
+
return sampler;
|
|
451
|
+
}
|
|
452
|
+
const wrapS = this._getGLTFTextureWrapMode(texture.wrapU);
|
|
453
|
+
if (wrapS !== 10497 /* TextureWrapMode.REPEAT */) {
|
|
454
|
+
sampler.wrapS = wrapS;
|
|
455
|
+
}
|
|
456
|
+
const wrapT = this._getGLTFTextureWrapMode(texture.wrapV);
|
|
457
|
+
if (wrapT !== 10497 /* TextureWrapMode.REPEAT */) {
|
|
458
|
+
sampler.wrapT = wrapT;
|
|
459
|
+
}
|
|
460
|
+
switch (texture.samplingMode) {
|
|
461
|
+
case Texture.LINEAR_LINEAR: {
|
|
462
|
+
sampler.magFilter = 9729 /* TextureMagFilter.LINEAR */;
|
|
463
|
+
sampler.minFilter = 9729 /* TextureMinFilter.LINEAR */;
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
case Texture.LINEAR_NEAREST: {
|
|
467
|
+
sampler.magFilter = 9729 /* TextureMagFilter.LINEAR */;
|
|
468
|
+
sampler.minFilter = 9728 /* TextureMinFilter.NEAREST */;
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
case Texture.NEAREST_LINEAR: {
|
|
472
|
+
sampler.magFilter = 9728 /* TextureMagFilter.NEAREST */;
|
|
473
|
+
sampler.minFilter = 9729 /* TextureMinFilter.LINEAR */;
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
case Texture.NEAREST_LINEAR_MIPLINEAR: {
|
|
477
|
+
sampler.magFilter = 9728 /* TextureMagFilter.NEAREST */;
|
|
478
|
+
sampler.minFilter = 9987 /* TextureMinFilter.LINEAR_MIPMAP_LINEAR */;
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
case Texture.NEAREST_NEAREST: {
|
|
482
|
+
sampler.magFilter = 9728 /* TextureMagFilter.NEAREST */;
|
|
483
|
+
sampler.minFilter = 9728 /* TextureMinFilter.NEAREST */;
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
case Texture.NEAREST_LINEAR_MIPNEAREST: {
|
|
487
|
+
sampler.magFilter = 9728 /* TextureMagFilter.NEAREST */;
|
|
488
|
+
sampler.minFilter = 9985 /* TextureMinFilter.LINEAR_MIPMAP_NEAREST */;
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
case Texture.LINEAR_NEAREST_MIPNEAREST: {
|
|
492
|
+
sampler.magFilter = 9729 /* TextureMagFilter.LINEAR */;
|
|
493
|
+
sampler.minFilter = 9984 /* TextureMinFilter.NEAREST_MIPMAP_NEAREST */;
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
case Texture.LINEAR_NEAREST_MIPLINEAR: {
|
|
497
|
+
sampler.magFilter = 9729 /* TextureMagFilter.LINEAR */;
|
|
498
|
+
sampler.minFilter = 9986 /* TextureMinFilter.NEAREST_MIPMAP_LINEAR */;
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
case Texture.NEAREST_NEAREST_MIPLINEAR: {
|
|
502
|
+
sampler.magFilter = 9728 /* TextureMagFilter.NEAREST */;
|
|
503
|
+
sampler.minFilter = 9986 /* TextureMinFilter.NEAREST_MIPMAP_LINEAR */;
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
case Texture.LINEAR_LINEAR_MIPLINEAR: {
|
|
507
|
+
sampler.magFilter = 9729 /* TextureMagFilter.LINEAR */;
|
|
508
|
+
sampler.minFilter = 9987 /* TextureMinFilter.LINEAR_MIPMAP_LINEAR */;
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
case Texture.LINEAR_LINEAR_MIPNEAREST: {
|
|
512
|
+
sampler.magFilter = 9729 /* TextureMagFilter.LINEAR */;
|
|
513
|
+
sampler.minFilter = 9985 /* TextureMinFilter.LINEAR_MIPMAP_NEAREST */;
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
case Texture.NEAREST_NEAREST_MIPNEAREST: {
|
|
517
|
+
sampler.magFilter = 9728 /* TextureMagFilter.NEAREST */;
|
|
518
|
+
sampler.minFilter = 9984 /* TextureMinFilter.NEAREST_MIPMAP_NEAREST */;
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return sampler;
|
|
523
|
+
}
|
|
524
|
+
_getGLTFTextureWrapMode(wrapMode) {
|
|
525
|
+
switch (wrapMode) {
|
|
526
|
+
case Texture.WRAP_ADDRESSMODE: {
|
|
527
|
+
return 10497 /* TextureWrapMode.REPEAT */;
|
|
528
|
+
}
|
|
529
|
+
case Texture.CLAMP_ADDRESSMODE: {
|
|
530
|
+
return 33071 /* TextureWrapMode.CLAMP_TO_EDGE */;
|
|
531
|
+
}
|
|
532
|
+
case Texture.MIRROR_ADDRESSMODE: {
|
|
533
|
+
return 33648 /* TextureWrapMode.MIRRORED_REPEAT */;
|
|
534
|
+
}
|
|
535
|
+
default: {
|
|
536
|
+
Tools.Error(`Unsupported Texture Wrap Mode ${wrapMode}!`);
|
|
537
|
+
return 10497 /* TextureWrapMode.REPEAT */;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Convert a PBRMaterial (Specular/Glossiness) to Metallic Roughness factors
|
|
543
|
+
* @param babylonPBRMaterial BJS PBR Metallic Roughness Material
|
|
544
|
+
* @param mimeType mime type to use for the textures
|
|
545
|
+
* @param pbrMetallicRoughness glTF PBR Metallic Roughness interface
|
|
546
|
+
* @param hasUVs specifies if texture coordinates are present on the submesh to determine if textures should be applied
|
|
547
|
+
* @returns glTF PBR Metallic Roughness factors
|
|
548
|
+
*/
|
|
549
|
+
async _convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, pbrMetallicRoughness, hasUVs) {
|
|
550
|
+
const specGloss = {
|
|
551
|
+
diffuseColor: babylonPBRMaterial._albedoColor,
|
|
552
|
+
specularColor: babylonPBRMaterial._reflectivityColor,
|
|
553
|
+
glossiness: babylonPBRMaterial._microSurface,
|
|
554
|
+
};
|
|
555
|
+
const albedoTexture = babylonPBRMaterial._albedoTexture;
|
|
556
|
+
const reflectivityTexture = babylonPBRMaterial._reflectivityTexture;
|
|
557
|
+
const useMicrosurfaceFromReflectivityMapAlpha = babylonPBRMaterial._useMicroSurfaceFromReflectivityMapAlpha;
|
|
558
|
+
if (reflectivityTexture && !useMicrosurfaceFromReflectivityMapAlpha) {
|
|
559
|
+
return await Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
|
|
560
|
+
}
|
|
561
|
+
if ((albedoTexture || reflectivityTexture) && hasUVs) {
|
|
562
|
+
this._exporter._materialNeedsUVsSet.add(babylonPBRMaterial);
|
|
563
|
+
const samplerIndex = this._exportTextureSampler(albedoTexture || reflectivityTexture);
|
|
564
|
+
const metallicRoughnessFactors = await this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(albedoTexture, reflectivityTexture, specGloss, mimeType);
|
|
565
|
+
const textures = this._exporter._textures;
|
|
566
|
+
if (metallicRoughnessFactors.baseColorTextureData) {
|
|
567
|
+
const imageIndex = this._exportImage(`baseColor${textures.length}`, mimeType, metallicRoughnessFactors.baseColorTextureData);
|
|
568
|
+
pbrMetallicRoughness.baseColorTexture = this._exportTextureInfo(imageIndex, samplerIndex, albedoTexture?.coordinatesIndex);
|
|
569
|
+
}
|
|
570
|
+
if (metallicRoughnessFactors.metallicRoughnessTextureData) {
|
|
571
|
+
const imageIndex = this._exportImage(`metallicRoughness${textures.length}`, mimeType, metallicRoughnessFactors.metallicRoughnessTextureData);
|
|
572
|
+
pbrMetallicRoughness.metallicRoughnessTexture = this._exportTextureInfo(imageIndex, samplerIndex, reflectivityTexture?.coordinatesIndex);
|
|
573
|
+
}
|
|
574
|
+
return metallicRoughnessFactors;
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
return this._convertSpecularGlossinessToMetallicRoughness(specGloss);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
async exportPBRMaterialAsync(babylonPBRMaterial, mimeType, hasUVs) {
|
|
581
|
+
const glTFPbrMetallicRoughness = {};
|
|
582
|
+
const glTFMaterial = {
|
|
583
|
+
name: babylonPBRMaterial.name,
|
|
584
|
+
};
|
|
585
|
+
const useMetallicRoughness = babylonPBRMaterial.isMetallicWorkflow();
|
|
586
|
+
if (useMetallicRoughness) {
|
|
587
|
+
const albedoColor = babylonPBRMaterial._albedoColor;
|
|
588
|
+
const alpha = babylonPBRMaterial.alpha;
|
|
589
|
+
if (albedoColor) {
|
|
590
|
+
glTFPbrMetallicRoughness.baseColorFactor = [albedoColor.r, albedoColor.g, albedoColor.b, alpha];
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
const metallicRoughness = useMetallicRoughness
|
|
594
|
+
? await this._convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasUVs)
|
|
595
|
+
: await this._convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasUVs);
|
|
596
|
+
await this._setMetallicRoughnessPbrMaterialAsync(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasUVs);
|
|
597
|
+
await this._finishMaterialAsync(glTFMaterial, babylonPBRMaterial, mimeType);
|
|
598
|
+
const materials = this._exporter._materials;
|
|
599
|
+
materials.push(glTFMaterial);
|
|
600
|
+
return materials.length - 1;
|
|
601
|
+
}
|
|
602
|
+
async _setMetallicRoughnessPbrMaterialAsync(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasUVs) {
|
|
603
|
+
SetAlphaMode(glTFMaterial, babylonPBRMaterial);
|
|
604
|
+
if (!metallicRoughness.baseColor.equalsWithEpsilon(White, Epsilon) || !Scalar.WithinEpsilon(babylonPBRMaterial.alpha, 1, Epsilon)) {
|
|
605
|
+
glTFPbrMetallicRoughness.baseColorFactor = [metallicRoughness.baseColor.r, metallicRoughness.baseColor.g, metallicRoughness.baseColor.b, babylonPBRMaterial.alpha];
|
|
606
|
+
}
|
|
607
|
+
if (metallicRoughness.metallic != null && metallicRoughness.metallic !== 1) {
|
|
608
|
+
glTFPbrMetallicRoughness.metallicFactor = metallicRoughness.metallic;
|
|
609
|
+
}
|
|
610
|
+
if (metallicRoughness.roughness != null && metallicRoughness.roughness !== 1) {
|
|
611
|
+
glTFPbrMetallicRoughness.roughnessFactor = metallicRoughness.roughness;
|
|
612
|
+
}
|
|
613
|
+
if (babylonPBRMaterial.backFaceCulling != null && !babylonPBRMaterial.backFaceCulling) {
|
|
614
|
+
if (!babylonPBRMaterial._twoSidedLighting) {
|
|
615
|
+
Tools.Warn(babylonPBRMaterial.name + ": Back-face culling disabled and two-sided lighting disabled is not supported in glTF.");
|
|
616
|
+
}
|
|
617
|
+
glTFMaterial.doubleSided = true;
|
|
618
|
+
}
|
|
619
|
+
if (hasUVs) {
|
|
620
|
+
const promises = [];
|
|
621
|
+
const bumpTexture = babylonPBRMaterial._bumpTexture;
|
|
622
|
+
if (bumpTexture) {
|
|
623
|
+
promises.push(this.exportTextureAsync(bumpTexture, mimeType).then((glTFTexture) => {
|
|
624
|
+
if (glTFTexture) {
|
|
625
|
+
glTFMaterial.normalTexture = glTFTexture;
|
|
626
|
+
if (bumpTexture.level !== 1) {
|
|
627
|
+
glTFMaterial.normalTexture.scale = bumpTexture.level;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}));
|
|
631
|
+
}
|
|
632
|
+
const ambientTexture = babylonPBRMaterial._ambientTexture;
|
|
633
|
+
if (ambientTexture) {
|
|
634
|
+
promises.push(this.exportTextureAsync(ambientTexture, mimeType).then((glTFTexture) => {
|
|
635
|
+
if (glTFTexture) {
|
|
636
|
+
const occlusionTexture = {
|
|
637
|
+
index: glTFTexture.index,
|
|
638
|
+
texCoord: glTFTexture.texCoord,
|
|
639
|
+
extensions: glTFTexture.extensions,
|
|
640
|
+
};
|
|
641
|
+
glTFMaterial.occlusionTexture = occlusionTexture;
|
|
642
|
+
const ambientTextureStrength = babylonPBRMaterial._ambientTextureStrength;
|
|
643
|
+
if (ambientTextureStrength) {
|
|
644
|
+
occlusionTexture.strength = ambientTextureStrength;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}));
|
|
648
|
+
}
|
|
649
|
+
const emissiveTexture = babylonPBRMaterial._emissiveTexture;
|
|
650
|
+
if (emissiveTexture) {
|
|
651
|
+
promises.push(this.exportTextureAsync(emissiveTexture, mimeType).then((glTFTexture) => {
|
|
652
|
+
if (glTFTexture) {
|
|
653
|
+
glTFMaterial.emissiveTexture = glTFTexture;
|
|
654
|
+
}
|
|
655
|
+
}));
|
|
656
|
+
}
|
|
657
|
+
if (promises.length > 0) {
|
|
658
|
+
this._exporter._materialNeedsUVsSet.add(babylonPBRMaterial);
|
|
659
|
+
await Promise.all(promises);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
const emissiveColor = babylonPBRMaterial._emissiveColor;
|
|
663
|
+
if (!emissiveColor.equalsWithEpsilon(Black, Epsilon)) {
|
|
664
|
+
glTFMaterial.emissiveFactor = emissiveColor.asArray();
|
|
665
|
+
}
|
|
666
|
+
glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
|
|
667
|
+
}
|
|
668
|
+
async exportTextureAsync(babylonTexture, mimeType) {
|
|
669
|
+
const extensionPromise = this._exporter._extensionsPreExportTextureAsync("exporter", babylonTexture, mimeType);
|
|
670
|
+
if (!extensionPromise) {
|
|
671
|
+
return await this._exportTextureInfoAsync(babylonTexture, mimeType);
|
|
672
|
+
}
|
|
673
|
+
return await extensionPromise.then(async (texture) => {
|
|
674
|
+
if (!texture) {
|
|
675
|
+
return await this._exportTextureInfoAsync(babylonTexture, mimeType);
|
|
676
|
+
}
|
|
677
|
+
return await this._exportTextureInfoAsync(texture, mimeType);
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
async _exportTextureInfoAsync(babylonTexture, mimeType) {
|
|
681
|
+
let textureInfo = this._textureMap.get(babylonTexture);
|
|
682
|
+
if (!textureInfo) {
|
|
683
|
+
const pixels = await GetTextureDataAsync(babylonTexture).catch(() => null);
|
|
684
|
+
if (!pixels) {
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
const samplerIndex = this._exportTextureSampler(babylonTexture);
|
|
688
|
+
// Preserve texture mime type if defined
|
|
689
|
+
const textureMimeType = babylonTexture.mimeType;
|
|
690
|
+
if (textureMimeType) {
|
|
691
|
+
switch (textureMimeType) {
|
|
692
|
+
case "image/jpeg":
|
|
693
|
+
case "image/png":
|
|
694
|
+
case "image/webp":
|
|
695
|
+
mimeType = textureMimeType;
|
|
696
|
+
break;
|
|
697
|
+
default:
|
|
698
|
+
Tools.Warn(`Unsupported media type: ${textureMimeType}. Exporting texture as PNG.`);
|
|
699
|
+
// Will later fallback to default mime type, image/png, from Canvas API
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
const internalTextureToImage = this._internalTextureToImage;
|
|
704
|
+
const internalTextureUniqueId = babylonTexture.getInternalTexture().uniqueId;
|
|
705
|
+
internalTextureToImage[internalTextureUniqueId] || (internalTextureToImage[internalTextureUniqueId] = {});
|
|
706
|
+
let imageIndexPromise = internalTextureToImage[internalTextureUniqueId][mimeType];
|
|
707
|
+
if (imageIndexPromise === undefined) {
|
|
708
|
+
const size = babylonTexture.getSize();
|
|
709
|
+
imageIndexPromise = (async () => {
|
|
710
|
+
const data = await this._getImageDataAsync(pixels, size.width, size.height, mimeType);
|
|
711
|
+
return this._exportImage(babylonTexture.name, mimeType, data);
|
|
712
|
+
})();
|
|
713
|
+
internalTextureToImage[internalTextureUniqueId][mimeType] = imageIndexPromise;
|
|
714
|
+
}
|
|
715
|
+
textureInfo = this._exportTextureInfo(await imageIndexPromise, samplerIndex, babylonTexture.coordinatesIndex);
|
|
716
|
+
this._textureMap.set(babylonTexture, textureInfo);
|
|
717
|
+
this._exporter._extensionsPostExportTextures("exporter", textureInfo, babylonTexture);
|
|
718
|
+
}
|
|
719
|
+
return textureInfo;
|
|
720
|
+
}
|
|
721
|
+
_exportImage(name, mimeType, data) {
|
|
722
|
+
const images = this._exporter._images;
|
|
723
|
+
let image;
|
|
724
|
+
if (this._exporter._shouldUseGlb) {
|
|
725
|
+
image = {
|
|
726
|
+
name: name,
|
|
727
|
+
mimeType: mimeType,
|
|
728
|
+
bufferView: undefined, // Will be updated later by BufferManager
|
|
729
|
+
};
|
|
730
|
+
const bufferView = this._exporter._bufferManager.createBufferView(new Uint8Array(data));
|
|
731
|
+
this._exporter._bufferManager.setBufferView(image, bufferView);
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
// Build a unique URI
|
|
735
|
+
const baseName = name.replace(/\.\/|\/|\.\\|\\/g, "_");
|
|
736
|
+
const extension = GetFileExtensionFromMimeType(mimeType);
|
|
737
|
+
let fileName = baseName + extension;
|
|
738
|
+
if (images.some((image) => image.uri === fileName)) {
|
|
739
|
+
fileName = `${baseName}_${Tools.RandomId()}${extension}`;
|
|
740
|
+
}
|
|
741
|
+
image = {
|
|
742
|
+
name: name,
|
|
743
|
+
uri: fileName,
|
|
744
|
+
};
|
|
745
|
+
this._exporter._imageData[fileName] = { data: data, mimeType: mimeType }; // Save image data to be written to file later
|
|
746
|
+
}
|
|
747
|
+
images.push(image);
|
|
748
|
+
return images.length - 1;
|
|
749
|
+
}
|
|
750
|
+
_exportTextureInfo(imageIndex, samplerIndex, coordinatesIndex) {
|
|
751
|
+
const textures = this._exporter._textures;
|
|
752
|
+
let textureIndex = textures.findIndex((t) => t.sampler == samplerIndex && t.source === imageIndex);
|
|
753
|
+
if (textureIndex === -1) {
|
|
754
|
+
textureIndex = textures.length;
|
|
755
|
+
textures.push({
|
|
756
|
+
source: imageIndex,
|
|
757
|
+
sampler: samplerIndex,
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
const textureInfo = { index: textureIndex };
|
|
761
|
+
if (coordinatesIndex) {
|
|
762
|
+
textureInfo.texCoord = coordinatesIndex;
|
|
763
|
+
}
|
|
764
|
+
return textureInfo;
|
|
765
|
+
}
|
|
766
|
+
_exportTextureSampler(texture) {
|
|
767
|
+
const sampler = this._getTextureSampler(texture);
|
|
768
|
+
// if a pre-existing sampler with identical parameters exists, then reuse the previous sampler
|
|
769
|
+
const samplers = this._exporter._samplers;
|
|
770
|
+
const samplerIndex = samplers.findIndex((s) => s.minFilter === sampler.minFilter && s.magFilter === sampler.magFilter && s.wrapS === sampler.wrapS && s.wrapT === sampler.wrapT);
|
|
771
|
+
if (samplerIndex !== -1) {
|
|
772
|
+
return samplerIndex;
|
|
773
|
+
}
|
|
774
|
+
samplers.push(sampler);
|
|
775
|
+
return samplers.length - 1;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
//# sourceMappingURL=glTFMaterialExporter.js.map
|