///
module BABYLON.GLTF2 {
/**
* Interface for storing specular glossiness factors
* @hidden
*/
interface _IPBRSpecularGlossiness {
/**
* Represents the linear diffuse factors of the material
*/
diffuseColor: BABYLON.Color3;
/**
* Represents the linear specular factors of the material
*/
specularColor: BABYLON.Color3;
/**
* Represents the smoothness of the material
*/
glossiness: number;
}
/**
* Interface for storing metallic roughness factors
* @hidden
*/
interface _IPBRMetallicRoughness {
/**
* Represents the albedo color of the material
*/
baseColor: BABYLON.Color3;
/**
* Represents the metallness of the material
*/
metallic: number;
/**
* Represents the roughness of the material
*/
roughness: number;
/**
* The metallic roughness texture as a base64 string
*/
metallicRoughnessTextureBase64?: Nullable;
/**
* The base color texture as a base64 string
*/
baseColorTextureBase64?: Nullable;
}
/**
* Utility methods for working with glTF material conversion properties. This class should only be used internally
* @hidden
*/
export class _GLTFMaterial {
/**
* Represents the dielectric specular values for R, G and B
*/
private static readonly _dielectricSpecular: Color3 = new Color3(0.04, 0.04, 0.04);
/**
* Allows the maximum specular power to be defined for material calculations
*/
private static _maxSpecularPower = 1024;
/**
* Numeric tolerance value
*/
private static _epsilon = 1e-6;
/**
* Specifies if two colors are approximately equal in value
* @param color1 first color to compare to
* @param color2 second color to compare to
* @param epsilon threshold value
*/
private static FuzzyEquals(color1: Color3, color2: Color3, epsilon: number): boolean {
return Scalar.WithinEpsilon(color1.r, color2.r, epsilon) &&
Scalar.WithinEpsilon(color1.g, color2.g, epsilon) &&
Scalar.WithinEpsilon(color1.b, color2.b, epsilon);
}
/**
* Gets the materials from a Babylon scene and converts them to glTF materials
* @param scene babylonjs scene
* @param mimeType texture mime type
* @param images array of images
* @param textures array of textures
* @param materials array of materials
* @param imageData mapping of texture names to base64 textures
* @param hasTextureCoords specifies if texture coordinates are present on the material
*/
public static _ConvertMaterialsToGLTF(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
for (let babylonMaterial of babylonMaterials) {
if (babylonMaterial instanceof StandardMaterial) {
_GLTFMaterial._ConvertStandardMaterial(babylonMaterial, mimeType, images, textures, samplers, materials, imageData, hasTextureCoords);
}
else if (babylonMaterial instanceof PBRMetallicRoughnessMaterial) {
_GLTFMaterial._ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, samplers, materials, imageData, hasTextureCoords);
}
else if (babylonMaterial instanceof PBRMaterial) {
_GLTFMaterial._ConvertPBRMaterial(babylonMaterial, mimeType, images, textures, samplers, materials, imageData, hasTextureCoords);
}
else {
Tools.Error("Unsupported material type: " + babylonMaterial.name);
}
}
}
/**
* Makes a copy of the glTF material without the texture parameters
* @param originalMaterial original glTF material
* @returns glTF material without texture parameters
*/
public static _StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial {
let newMaterial: IMaterial = {};
if (originalMaterial) {
newMaterial.name = originalMaterial.name;
newMaterial.doubleSided = originalMaterial.doubleSided;
newMaterial.alphaMode = originalMaterial.alphaMode;
newMaterial.alphaCutoff = originalMaterial.alphaCutoff;
newMaterial.emissiveFactor = originalMaterial.emissiveFactor;
const originalPBRMetallicRoughness = originalMaterial.pbrMetallicRoughness;
if (originalPBRMetallicRoughness) {
newMaterial.pbrMetallicRoughness = {};
newMaterial.pbrMetallicRoughness.baseColorFactor = originalPBRMetallicRoughness.baseColorFactor;
newMaterial.pbrMetallicRoughness.metallicFactor = originalPBRMetallicRoughness.metallicFactor;
newMaterial.pbrMetallicRoughness.roughnessFactor = originalPBRMetallicRoughness.roughnessFactor;
}
}
return newMaterial;
}
/**
* Specifies if the material has any texture parameters present
* @param material glTF Material
* @returns boolean specifying if texture parameters are present
*/
public static _HasTexturesPresent(material: IMaterial): boolean {
if (material.emissiveTexture || material.normalTexture || material.occlusionTexture) {
return true;
}
const pbrMat = material.pbrMetallicRoughness;
if (pbrMat) {
if (pbrMat.baseColorTexture || pbrMat.metallicRoughnessTexture) {
return true;
}
}
return false;
}
/**
* Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material
* @param babylonStandardMaterial
* @returns glTF Metallic Roughness Material representation
*/
public static _ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness {
const P0 = new BABYLON.Vector2(0, 1);
const P1 = new BABYLON.Vector2(0, 0.1);
const P2 = new BABYLON.Vector2(0, 0.1);
const P3 = new BABYLON.Vector2(1300, 0.1);
/**
* Given the control points, solve for x based on a given t for a cubic bezier curve
* @param t a value between 0 and 1
* @param p0 first control point
* @param p1 second control point
* @param p2 third control point
* @param p3 fourth control point
* @returns number result of cubic bezier curve at the specified t
*/
function _cubicBezierCurve(t: number, p0: number, p1: number, p2: number, p3: number): number {
return (
(1 - t) * (1 - t) * (1 - t) * p0 +
3 * (1 - t) * (1 - t) * t * p1 +
3 * (1 - t) * t * t * p2 +
t * t * t * p3
);
}
/**
* Evaluates a specified specular power value to determine the appropriate roughness value,
* based on a pre-defined cubic bezier curve with specular on the abscissa axis (x-axis)
* and roughness on the ordinant axis (y-axis)
* @param specularPower specular power of standard material
* @returns Number representing the roughness value
*/
function _solveForRoughness(specularPower: number): number {
var t = Math.pow(specularPower / P3.x, 0.333333);
return _cubicBezierCurve(t, P0.y, P1.y, P2.y, P3.y);
}
let diffuse = babylonStandardMaterial.diffuseColor.toLinearSpace().scale(0.5);
let opacity = babylonStandardMaterial.alpha;
let specularPower = Scalar.Clamp(babylonStandardMaterial.specularPower, 0, this._maxSpecularPower);
const roughness = _solveForRoughness(specularPower);
const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {
baseColorFactor: [
diffuse.r,
diffuse.g,
diffuse.b,
opacity
],
metallicFactor: 0,
roughnessFactor: roughness,
};
return glTFPbrMetallicRoughness;
}
/**
* Computes the metallic factor
* @param diffuse diffused value
* @param specular specular value
* @param oneMinusSpecularStrength one minus the specular strength
* @returns metallic value
*/
public static _SolveMetallic(diffuse: number, specular: number, oneMinusSpecularStrength: number): number {
if (specular < _GLTFMaterial._dielectricSpecular.r) {
_GLTFMaterial._dielectricSpecular
return 0;
}
const a = _GLTFMaterial._dielectricSpecular.r;
const b = diffuse * oneMinusSpecularStrength / (1.0 - _GLTFMaterial._dielectricSpecular.r) + specular - 2.0 * _GLTFMaterial._dielectricSpecular.r;
const c = _GLTFMaterial._dielectricSpecular.r - specular;
const D = b * b - 4.0 * a * c;
return BABYLON.Scalar.Clamp((-b + Math.sqrt(D)) / (2.0 * a), 0, 1);
}
/**
* Gets the glTF alpha mode from the Babylon Material
* @param babylonMaterial Babylon Material
* @returns The Babylon alpha mode value
*/
public static _GetAlphaMode(babylonMaterial: Material): Nullable {
if (babylonMaterial instanceof StandardMaterial) {
const babylonStandardMaterial = babylonMaterial as StandardMaterial;
if ((babylonStandardMaterial.alpha != 1.0) ||
(babylonStandardMaterial.diffuseTexture != null && babylonStandardMaterial.diffuseTexture.hasAlpha) ||
(babylonStandardMaterial.opacityTexture != null)) {
return MaterialAlphaMode.BLEND;
}
else {
return MaterialAlphaMode.OPAQUE;
}
}
else if (babylonMaterial instanceof PBRMetallicRoughnessMaterial) {
const babylonPBRMetallicRoughness = babylonMaterial as PBRMetallicRoughnessMaterial;
switch (babylonPBRMetallicRoughness.transparencyMode) {
case PBRMaterial.PBRMATERIAL_OPAQUE: {
return MaterialAlphaMode.OPAQUE;
}
case PBRMaterial.PBRMATERIAL_ALPHABLEND: {
return MaterialAlphaMode.BLEND;
}
case PBRMaterial.PBRMATERIAL_ALPHATEST: {
return MaterialAlphaMode.MASK;
}
case PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND: {
Tools.Warn(babylonMaterial.name + ": GLTF Exporter | Alpha test and blend mode not supported in glTF. Alpha blend used instead.");
return MaterialAlphaMode.BLEND;
}
default: {
Tools.Error("Unsupported alpha mode " + babylonPBRMetallicRoughness.transparencyMode);
return null;
}
}
}
else if (babylonMaterial instanceof PBRMaterial) {
const babylonPBRMaterial = babylonMaterial as PBRMaterial;
switch (babylonPBRMaterial.transparencyMode) {
case PBRMaterial.PBRMATERIAL_OPAQUE: {
return MaterialAlphaMode.OPAQUE;
}
case PBRMaterial.PBRMATERIAL_ALPHABLEND: {
return MaterialAlphaMode.BLEND;
}
case PBRMaterial.PBRMATERIAL_ALPHATEST: {
return MaterialAlphaMode.MASK;
}
case PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND: {
Tools.Warn(babylonMaterial.name + ": GLTF Exporter | Alpha test and blend mode not supported in glTF. Alpha blend used instead.");
return MaterialAlphaMode.BLEND;
}
default: {
Tools.Error("Unsupported alpha mode " + babylonPBRMaterial.transparencyMode);
return null;
}
}
}
else {
Tools.Error("Unsupported Babylon material type");
return null;
}
}
/**
* Converts a Babylon Standard Material to a glTF Material
* @param babylonStandardMaterial BJS Standard Material
* @param mimeType mime type to use for the textures
* @param images array of glTF image interfaces
* @param textures array of glTF texture interfaces
* @param materials array of glTF material interfaces
* @param imageData map of image file name to data
* @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
*/
public static _ConvertStandardMaterial(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
const glTFPbrMetallicRoughness = _GLTFMaterial._ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
const glTFMaterial: IMaterial = { name: babylonStandardMaterial.name };
if (babylonStandardMaterial.backFaceCulling != null && !babylonStandardMaterial.backFaceCulling) {
if (!babylonStandardMaterial.twoSidedLighting) {
Tools.Warn(babylonStandardMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
}
glTFMaterial.doubleSided = true;
}
if (hasTextureCoords) {
if (babylonStandardMaterial.diffuseTexture) {
const glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, samplers, imageData);
if (glTFTexture != null) {
glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
}
}
if (babylonStandardMaterial.bumpTexture) {
const glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.bumpTexture, mimeType, images, textures, samplers, imageData)
if (glTFTexture) {
glTFMaterial.normalTexture = glTFTexture;
if (babylonStandardMaterial.bumpTexture.level !== 1) {
glTFMaterial.normalTexture.scale = babylonStandardMaterial.bumpTexture.level;
}
}
}
if (babylonStandardMaterial.emissiveTexture) {
const glTFEmissiveTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.emissiveTexture, mimeType, images, textures, samplers, imageData)
if (glTFEmissiveTexture) {
glTFMaterial.emissiveTexture = glTFEmissiveTexture;
}
glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
}
if (babylonStandardMaterial.ambientTexture) {
const glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, samplers, imageData);
if (glTFTexture) {
const occlusionTexture: IMaterialOcclusionTextureInfo = {
index: glTFTexture.index
};
glTFMaterial.occlusionTexture = occlusionTexture;
occlusionTexture.strength = 1.0;
}
}
}
if (babylonStandardMaterial.alpha < 1.0 || babylonStandardMaterial.opacityTexture) {
if (babylonStandardMaterial.alphaMode === Engine.ALPHA_COMBINE) {
glTFMaterial.alphaMode = GLTF2.MaterialAlphaMode.BLEND;
}
else {
Tools.Warn(babylonStandardMaterial.name + ": glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
}
}
if (babylonStandardMaterial.emissiveColor && !this.FuzzyEquals(babylonStandardMaterial.emissiveColor, Color3.Black(), this._epsilon)) {
glTFMaterial.emissiveFactor = babylonStandardMaterial.emissiveColor.asArray();
}
glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
materials.push(glTFMaterial);
}
/**
* Converts a Babylon PBR Metallic Roughness Material to a glTF Material
* @param babylonPBRMetalRoughMaterial BJS PBR Metallic Roughness Material
* @param mimeType mime type to use for the textures
* @param images array of glTF image interfaces
* @param textures array of glTF texture interfaces
* @param materials array of glTF material interfaces
* @param imageData map of image file name to data
* @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
*/
public static _ConvertPBRMetallicRoughnessMaterial(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {};
if (babylonPBRMetalRoughMaterial.baseColor) {
glTFPbrMetallicRoughness.baseColorFactor = [
babylonPBRMetalRoughMaterial.baseColor.r,
babylonPBRMetalRoughMaterial.baseColor.g,
babylonPBRMetalRoughMaterial.baseColor.b,
babylonPBRMetalRoughMaterial.alpha
];
}
if (babylonPBRMetalRoughMaterial.metallic != null && babylonPBRMetalRoughMaterial.metallic !== 1) {
glTFPbrMetallicRoughness.metallicFactor = babylonPBRMetalRoughMaterial.metallic;
}
if (babylonPBRMetalRoughMaterial.roughness != null && babylonPBRMetalRoughMaterial.roughness !== 1) {
glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMetalRoughMaterial.roughness;
}
const glTFMaterial: IMaterial = {
name: babylonPBRMetalRoughMaterial.name
};
if (babylonPBRMetalRoughMaterial.doubleSided) {
glTFMaterial.doubleSided = babylonPBRMetalRoughMaterial.doubleSided;
}
if (hasTextureCoords) {
if (babylonPBRMetalRoughMaterial.baseTexture != null) {
const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, samplers, imageData);
if (glTFTexture != null) {
glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
}
}
if (babylonPBRMetalRoughMaterial.normalTexture) {
const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, samplers, imageData);
if (glTFTexture) {
glTFMaterial.normalTexture = glTFTexture;
if (babylonPBRMetalRoughMaterial.normalTexture.level !== 1) {
glTFMaterial.normalTexture.scale = babylonPBRMetalRoughMaterial.normalTexture.level;
}
}
}
if (babylonPBRMetalRoughMaterial.occlusionTexture) {
const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, samplers, imageData);
if (glTFTexture) {
glTFMaterial.occlusionTexture = glTFTexture;
if (babylonPBRMetalRoughMaterial.occlusionStrength != null) {
glTFMaterial.occlusionTexture.strength = babylonPBRMetalRoughMaterial.occlusionStrength;
}
}
}
if (babylonPBRMetalRoughMaterial.emissiveTexture) {
const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.emissiveTexture, mimeType, images, textures, samplers, imageData);
if (glTFTexture != null) {
glTFMaterial.emissiveTexture = glTFTexture;
}
}
}
if (this.FuzzyEquals(babylonPBRMetalRoughMaterial.emissiveColor, Color3.Black(), this._epsilon)) {
glTFMaterial.emissiveFactor = babylonPBRMetalRoughMaterial.emissiveColor.asArray();
}
if (babylonPBRMetalRoughMaterial.transparencyMode != null) {
const alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMetalRoughMaterial);
if (alphaMode) {
if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
glTFMaterial.alphaMode = alphaMode;
if (alphaMode === MaterialAlphaMode.MASK) {
glTFMaterial.alphaCutoff = babylonPBRMetalRoughMaterial.alphaCutOff;
}
}
}
}
glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
materials.push(glTFMaterial);
}
/**
* Converts an image typed array buffer to a base64 image
* @param buffer typed array buffer
* @param width width of the image
* @param height height of the image
* @param mimeType mimetype of the image
* @returns base64 image string
*/
private static _CreateBase64FromCanvas(buffer: Uint8ClampedArray | Float32Array, width: number, height: number, mimeType: ImageMimeType): string {
const imageCanvas = document.createElement('canvas');
imageCanvas.width = width;
imageCanvas.height = height;
imageCanvas.id = "WriteCanvas";
const ctx = imageCanvas.getContext('2d') as CanvasRenderingContext2D;
const imgData = ctx.createImageData(width, height);
imgData.data.set(buffer);
ctx.putImageData(imgData, 0, 0);
return imageCanvas.toDataURL(mimeType);
}
/**
* Generates a white texture based on the specified width and height
* @param width width of the texture in pixels
* @param height height of the texture in pixels
* @param scene babylonjs scene
* @returns white texture
*/
private static _CreateWhiteTexture(width: number, height: number, scene: Scene): Texture {
const data = new Uint8Array(width * height * 4);
for (let i = 0; i < data.length; i = i + 4) {
data[i] = data[i + 1] = data[i + 2] = data[i + 3] = 0xFF;
}
const rawTexture = RawTexture.CreateRGBATexture(data, width, height, scene);
return rawTexture;
}
/**
* 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
* @param texture1 first texture to resize
* @param texture2 second texture to resize
* @param scene babylonjs scene
* @returns resized textures or null
*/
private static _ResizeTexturesToSameDimensions(texture1: BaseTexture, texture2: BaseTexture, scene: Scene): { "texture1": BaseTexture, "texture2": BaseTexture } {
let texture1Size = texture1 ? texture1.getSize() : { width: 0, height: 0 };
let texture2Size = texture2 ? texture2.getSize() : { width: 0, height: 0 };
let resizedTexture1;
let resizedTexture2;
if (texture1Size.width < texture2Size.width) {
if (texture1) {
resizedTexture1 = TextureTools.CreateResizedCopy(texture1 as Texture, texture2Size.width, texture2Size.height, true);
}
else {
resizedTexture1 = this._CreateWhiteTexture(texture2Size.width, texture2Size.height, scene);
}
resizedTexture2 = texture2;
}
else if (texture1Size.width > texture2Size.width) {
if (texture2) {
resizedTexture2 = TextureTools.CreateResizedCopy(texture2 as Texture, texture1Size.width, texture1Size.height, true);
}
else {
resizedTexture2 = this._CreateWhiteTexture(texture1Size.width, texture1Size.height, scene);
}
resizedTexture1 = texture1;
}
else {
resizedTexture1 = texture1;
resizedTexture2 = texture2;
}
return {
"texture1": resizedTexture1,
"texture2": resizedTexture2
}
}
/**
* Convert Specular Glossiness Textures to Metallic Roughness
* See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
* @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
* @param diffuseTexture texture used to store diffuse information
* @param specularGlossinessTexture texture used to store specular and glossiness information
* @param factors specular glossiness material factors
* @param mimeType the mime type to use for the texture
* @returns pbr metallic roughness interface or null
*/
private static _ConvertSpecularGlossinessTexturesToMetallicRoughness(diffuseTexture: BaseTexture, specularGlossinessTexture: BaseTexture, factors: _IPBRSpecularGlossiness, mimeType: ImageMimeType): Nullable<_IPBRMetallicRoughness> {
if (!(diffuseTexture || specularGlossinessTexture)) {
return null;
}
const scene = diffuseTexture ? diffuseTexture.getScene() : specularGlossinessTexture.getScene();
if (!scene) {
Tools.Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Scene from textures is missing!");
return null;
}
const resizedTextures = this._ResizeTexturesToSameDimensions(diffuseTexture, specularGlossinessTexture, scene);
let diffuseSize = resizedTextures.texture1.getSize();
let diffuseBuffer: Uint8Array;
let specularGlossinessBuffer: Uint8Array;
const width = diffuseSize.width;
const height = diffuseSize.height;
let pixels = (resizedTextures.texture1.readPixels());
if (pixels instanceof Uint8Array) {
diffuseBuffer = (resizedTextures.texture1.readPixels()) as Uint8Array;
}
else {
Tools.Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture1.name);
return null;
}
pixels = resizedTextures.texture2.readPixels();
if (pixels instanceof Uint8Array) {
specularGlossinessBuffer = (resizedTextures.texture2.readPixels()) as Uint8Array;
}
else {
Tools.Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture2.name);
return null;
}
const byteLength = specularGlossinessBuffer.byteLength;
const metallicRoughnessBuffer = new Uint8Array(byteLength);
const baseColorBuffer = new Uint8Array(byteLength);
const strideSize = 4;
const maxBaseColor = Color3.Black();
let maxMetallic = 0;
let maxRoughness = 0;
for (let h = 0; h < height; ++h) {
for (let w = 0; w < width; ++w) {
const offset = (width * h + w) * strideSize;
const diffuseColor = Color3.FromInts(diffuseBuffer[offset], diffuseBuffer[offset + 1], diffuseBuffer[offset + 2]).toLinearSpace().multiply(factors.diffuseColor);
const specularColor = Color3.FromInts(specularGlossinessBuffer[offset], specularGlossinessBuffer[offset + 1], specularGlossinessBuffer[offset + 2]).toLinearSpace().multiply(factors.specularColor);
const glossiness = (specularGlossinessBuffer[offset + 3] / 255) * factors.glossiness;
const specularGlossiness: _IPBRSpecularGlossiness = {
diffuseColor: diffuseColor,
specularColor: specularColor,
glossiness: glossiness
};
const metallicRoughness = this._ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
maxBaseColor.r = Math.max(maxBaseColor.r, metallicRoughness.baseColor.r);
maxBaseColor.g = Math.max(maxBaseColor.g, metallicRoughness.baseColor.g);
maxBaseColor.b = Math.max(maxBaseColor.b, metallicRoughness.baseColor.b);
maxMetallic = Math.max(maxMetallic, metallicRoughness.metallic);
maxRoughness = Math.max(maxRoughness, metallicRoughness.roughness);
baseColorBuffer[offset] = metallicRoughness.baseColor.r * 255;
baseColorBuffer[offset + 1] = metallicRoughness.baseColor.g * 255;
baseColorBuffer[offset + 2] = metallicRoughness.baseColor.b * 255;
baseColorBuffer[offset + 3] = resizedTextures.texture1.hasAlpha ? diffuseBuffer[offset + 3] : 255;
metallicRoughnessBuffer[offset] = 0;
metallicRoughnessBuffer[offset + 1] = metallicRoughness.roughness * 255;
metallicRoughnessBuffer[offset + 2] = metallicRoughness.metallic * 255;
metallicRoughnessBuffer[offset + 3] = 255;
}
}
// Retrieves the metallic roughness factors from the maximum texture values.
const metallicRoughnessFactors: _IPBRMetallicRoughness = {
baseColor: maxBaseColor,
metallic: maxMetallic,
roughness: maxRoughness
};
let writeOutMetallicRoughnessTexture = false;
let writeOutBaseColorTexture = false;
for (let h = 0; h < height; ++h) {
for (let w = 0; w < width; ++w) {
const destinationOffset = (width * h + w) * strideSize;
baseColorBuffer[destinationOffset] /= metallicRoughnessFactors.baseColor.r > this._epsilon ? metallicRoughnessFactors.baseColor.r : 1;
baseColorBuffer[destinationOffset + 1] /= metallicRoughnessFactors.baseColor.g > this._epsilon ? metallicRoughnessFactors.baseColor.g : 1;
baseColorBuffer[destinationOffset + 2] /= metallicRoughnessFactors.baseColor.b > this._epsilon ? metallicRoughnessFactors.baseColor.b : 1;
const linearBaseColorPixel = Color3.FromInts(baseColorBuffer[destinationOffset], baseColorBuffer[destinationOffset + 1], baseColorBuffer[destinationOffset + 2]);
const sRGBBaseColorPixel = linearBaseColorPixel.toGammaSpace();
baseColorBuffer[destinationOffset] = sRGBBaseColorPixel.r * 255;
baseColorBuffer[destinationOffset + 1] = sRGBBaseColorPixel.g * 255;
baseColorBuffer[destinationOffset + 2] = sRGBBaseColorPixel.b * 255;
if (!this.FuzzyEquals(sRGBBaseColorPixel, Color3.White(), this._epsilon)) {
writeOutBaseColorTexture = true;
}
metallicRoughnessBuffer[destinationOffset + 1] /= metallicRoughnessFactors.roughness > this._epsilon ? metallicRoughnessFactors.roughness : 1;
metallicRoughnessBuffer[destinationOffset + 2] /= metallicRoughnessFactors.metallic > this._epsilon ? metallicRoughnessFactors.metallic : 1;
const metallicRoughnessPixel = Color3.FromInts(255, metallicRoughnessBuffer[destinationOffset + 1], metallicRoughnessBuffer[destinationOffset + 2]);
if (!this.FuzzyEquals(metallicRoughnessPixel, Color3.White(), this._epsilon)) {
writeOutMetallicRoughnessTexture = true;
}
}
}
if (writeOutMetallicRoughnessTexture) {
const metallicRoughnessBase64 = this._CreateBase64FromCanvas(metallicRoughnessBuffer, width, height, mimeType);
metallicRoughnessFactors.metallicRoughnessTextureBase64 = metallicRoughnessBase64;
}
if (writeOutBaseColorTexture) {
const baseColorBase64 = this._CreateBase64FromCanvas(baseColorBuffer, width, height, mimeType);
metallicRoughnessFactors.baseColorTextureBase64 = baseColorBase64;
}
return metallicRoughnessFactors;
}
/**
* Converts specular glossiness material properties to metallic roughness
* @param specularGlossiness interface with specular glossiness material properties
* @returns interface with metallic roughness material properties
*/
private static _ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness: _IPBRSpecularGlossiness): _IPBRMetallicRoughness {
const diffusePerceivedBrightness = _GLTFMaterial._GetPerceivedBrightness(specularGlossiness.diffuseColor);
const specularPerceivedBrightness = _GLTFMaterial._GetPerceivedBrightness(specularGlossiness.specularColor);
const oneMinusSpecularStrength = 1 - _GLTFMaterial._GetMaxComponent(specularGlossiness.specularColor);
const metallic = _GLTFMaterial._SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
const baseColorFromDiffuse = specularGlossiness.diffuseColor.scale(oneMinusSpecularStrength / (1.0 - this._dielectricSpecular.r) / Math.max(1 - metallic, this._epsilon));
const baseColorFromSpecular = specularGlossiness.specularColor.subtract(this._dielectricSpecular.scale(1 - metallic)).scale(1 / Math.max(metallic, this._epsilon));
let baseColor = Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
baseColor = baseColor.clampToRef(0, 1, baseColor);
const metallicRoughness: _IPBRMetallicRoughness = {
baseColor: baseColor,
metallic: metallic,
roughness: 1 - specularGlossiness.glossiness
}
return metallicRoughness;
}
/**
* Calculates the surface reflectance, independent of lighting conditions
* @param color Color source to calculate brightness from
* @returns number representing the perceived brightness, or zero if color is undefined
*/
private static _GetPerceivedBrightness(color: Color3): number {
if (color) {
return Math.sqrt(0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b);
}
return 0;
}
/**
* Returns the maximum color component value
* @param color
* @returns maximum color component value, or zero if color is null or undefined
*/
private static _GetMaxComponent(color: Color3): number {
if (color) {
return Math.max(color.r, Math.max(color.g, color.b));
}
return 0;
}
/**
* Convert a PBRMaterial (Metallic/Roughness) to Metallic Roughness factors
* @param babylonPBRMaterial BJS PBR Metallic Roughness Material
* @param mimeType mime type to use for the textures
* @param images array of glTF image interfaces
* @param textures array of glTF texture interfaces
* @param glTFPbrMetallicRoughness glTF PBR Metallic Roughness interface
* @param imageData map of image file name to data
* @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
* @returns glTF PBR Metallic Roughness factors
*/
private static _ConvertMetalRoughFactorsToMetallicRoughness(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness, imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean): _IPBRMetallicRoughness {
const metallicRoughness = {
baseColor: babylonPBRMaterial.albedoColor,
metallic: babylonPBRMaterial.metallic,
roughness: babylonPBRMaterial.roughness
};
if (hasTextureCoords) {
if (babylonPBRMaterial.albedoTexture) {
const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.albedoTexture, mimeType, images, textures, samplers, imageData);
if (glTFTexture) {
glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
}
}
if (babylonPBRMaterial.metallicTexture) {
const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.metallicTexture, mimeType, images, textures, samplers, imageData);
if (glTFTexture != null) {
glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
}
}
}
return metallicRoughness;
}
private static _GetGLTFTextureSampler(texture: BaseTexture): ISampler {
const sampler = _GLTFMaterial._GetGLTFTextureWrapModesSampler(texture);
let samplingMode = texture instanceof Texture ? (texture as Texture).samplingMode : null;
if (samplingMode != null) {
switch (samplingMode) {
case Texture.LINEAR_LINEAR: {
sampler.magFilter = TextureMagFilter.LINEAR;
sampler.minFilter = TextureMinFilter.LINEAR;
break;
}
case Texture.LINEAR_NEAREST: {
sampler.magFilter = TextureMagFilter.LINEAR;
sampler.minFilter = TextureMinFilter.NEAREST;
break;
}
case Texture.NEAREST_LINEAR: {
sampler.magFilter = TextureMagFilter.NEAREST;
sampler.minFilter = TextureMinFilter.LINEAR;
break;
}
case Texture.NEAREST_LINEAR_MIPLINEAR: {
sampler.magFilter = TextureMagFilter.NEAREST;
sampler.minFilter = TextureMinFilter.LINEAR_MIPMAP_LINEAR;
break;
}
case Texture.NEAREST_NEAREST: {
sampler.magFilter = TextureMagFilter.NEAREST;
sampler.minFilter = TextureMinFilter.NEAREST;
break;
}
case Texture.NEAREST_LINEAR_MIPNEAREST: {
sampler.magFilter = TextureMagFilter.NEAREST;
sampler.minFilter = TextureMinFilter.LINEAR_MIPMAP_NEAREST;
break;
}
case Texture.LINEAR_NEAREST_MIPNEAREST: {
sampler.magFilter = TextureMagFilter.LINEAR;
sampler.minFilter = TextureMinFilter.NEAREST_MIPMAP_NEAREST;
break;
}
case Texture.LINEAR_NEAREST_MIPLINEAR: {
sampler.magFilter = TextureMagFilter.LINEAR;
sampler.minFilter = TextureMinFilter.NEAREST_MIPMAP_LINEAR;
break;
}
case Texture.NEAREST_NEAREST_MIPLINEAR: {
sampler.magFilter = TextureMagFilter.NEAREST;
sampler.minFilter = TextureMinFilter.NEAREST_MIPMAP_LINEAR;
break;
}
case Texture.LINEAR_LINEAR_MIPLINEAR: {
sampler.magFilter = TextureMagFilter.LINEAR;
sampler.minFilter = TextureMinFilter.LINEAR_MIPMAP_LINEAR;
break;
}
case Texture.LINEAR_LINEAR_MIPNEAREST: {
sampler.magFilter = TextureMagFilter.LINEAR;
sampler.minFilter = TextureMinFilter.LINEAR_MIPMAP_NEAREST;
break;
}
case Texture.NEAREST_NEAREST_MIPNEAREST: {
sampler.magFilter = TextureMagFilter.NEAREST;
sampler.minFilter = TextureMinFilter.NEAREST_MIPMAP_NEAREST;
break;
}
}
}
return sampler;
}
private static _GetGLTFTextureWrapMode(wrapMode: number): TextureWrapMode {
switch (wrapMode) {
case Texture.WRAP_ADDRESSMODE: {
return TextureWrapMode.REPEAT;
}
case Texture.CLAMP_ADDRESSMODE: {
return TextureWrapMode.CLAMP_TO_EDGE;
}
case Texture.MIRROR_ADDRESSMODE: {
return TextureWrapMode.MIRRORED_REPEAT;
}
default: {
Tools.Error(`Unsupported Texture Wrap Mode ${wrapMode}!`);
return TextureWrapMode.REPEAT;
}
}
}
private static _GetGLTFTextureWrapModesSampler(texture: BaseTexture): ISampler {
let wrapS = _GLTFMaterial._GetGLTFTextureWrapMode(texture instanceof Texture ? (texture as Texture).wrapU : Texture.WRAP_ADDRESSMODE);
let wrapT = _GLTFMaterial._GetGLTFTextureWrapMode(texture instanceof Texture ? (texture as Texture).wrapV : Texture.WRAP_ADDRESSMODE);
if (wrapS === TextureWrapMode.REPEAT && wrapT === TextureWrapMode.REPEAT) { // default wrapping mode in glTF, so omitting
return {};
}
return { wrapS: wrapS, wrapT: wrapT };
}
/**
* Convert a PBRMaterial (Specular/Glossiness) to Metallic Roughness factors
* @param babylonPBRMaterial BJS PBR Metallic Roughness Material
* @param mimeType mime type to use for the textures
* @param images array of glTF image interfaces
* @param textures array of glTF texture interfaces
* @param glTFPbrMetallicRoughness glTF PBR Metallic Roughness interface
* @param imageData map of image file name to data
* @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
* @returns glTF PBR Metallic Roughness factors
*/
private static _ConvertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness, imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean): Nullable<_IPBRMetallicRoughness> {
const specGloss: _IPBRSpecularGlossiness = {
diffuseColor: babylonPBRMaterial.albedoColor || Color3.White(),
specularColor: babylonPBRMaterial.reflectivityColor || Color3.White(),
glossiness: babylonPBRMaterial.microSurface || 1,
};
let samplerIndex: Nullable = null;
const sampler = this._GetGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
samplers.push(sampler);
samplerIndex = samplers.length - 1;
}
if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
Tools.Error("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture currently not supported");
return null;
}
let metallicRoughnessFactors = this._ConvertSpecularGlossinessTexturesToMetallicRoughness(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType);
if (!metallicRoughnessFactors) {
metallicRoughnessFactors = this._ConvertSpecularGlossinessToMetallicRoughness(specGloss);
}
else {
if (hasTextureCoords) {
if (metallicRoughnessFactors.baseColorTextureBase64) {
const glTFBaseColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, images, textures, babylonPBRMaterial.albedoTexture.coordinatesIndex, samplerIndex, imageData);
if (glTFBaseColorTexture != null) {
glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
}
}
if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
const glTFMRColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, images, textures, babylonPBRMaterial.reflectivityTexture.coordinatesIndex, samplerIndex, imageData);
if (glTFMRColorTexture != null) {
glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
}
}
}
}
return metallicRoughnessFactors
}
/**
* Converts a Babylon PBR Metallic Roughness Material to a glTF Material
* @param babylonPBRMaterial BJS PBR Metallic Roughness Material
* @param mimeType mime type to use for the textures
* @param images array of glTF image interfaces
* @param textures array of glTF texture interfaces
* @param materials array of glTF material interfaces
* @param imageData map of image file name to data
* @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
*/
public static _ConvertPBRMaterial(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {};
let metallicRoughness: Nullable<_IPBRMetallicRoughness>;
const glTFMaterial: IMaterial = {
name: babylonPBRMaterial.name
};
const useMetallicRoughness = babylonPBRMaterial.isMetallicWorkflow();
if (useMetallicRoughness) {
metallicRoughness = this._ConvertMetalRoughFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, images, textures, samplers, glTFPbrMetallicRoughness, imageData, hasTextureCoords);
}
else {
metallicRoughness = this._ConvertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, images, textures, samplers, glTFPbrMetallicRoughness, imageData, hasTextureCoords);
}
if (metallicRoughness) {
if (!(this.FuzzyEquals(metallicRoughness.baseColor, Color3.White(), this._epsilon) && babylonPBRMaterial.alpha >= this._epsilon)) {
glTFPbrMetallicRoughness.baseColorFactor = [
metallicRoughness.baseColor.r,
metallicRoughness.baseColor.g,
metallicRoughness.baseColor.b,
babylonPBRMaterial.alpha
];
}
if (metallicRoughness.metallic != null && metallicRoughness.metallic !== 1) {
glTFPbrMetallicRoughness.metallicFactor = metallicRoughness.metallic;
}
if (metallicRoughness.roughness != null && metallicRoughness.roughness !== 1) {
glTFPbrMetallicRoughness.roughnessFactor = metallicRoughness.roughness;
}
if (babylonPBRMaterial.backFaceCulling != null && !babylonPBRMaterial.backFaceCulling) {
if (!babylonPBRMaterial.twoSidedLighting) {
Tools.Warn(babylonPBRMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
}
glTFMaterial.doubleSided = true;
}
if (hasTextureCoords) {
if (babylonPBRMaterial.bumpTexture) {
const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.bumpTexture, mimeType, images, textures, samplers, imageData);
if (glTFTexture) {
glTFMaterial.normalTexture = glTFTexture;
if (babylonPBRMaterial.bumpTexture.level !== 1) {
glTFMaterial.normalTexture.scale = babylonPBRMaterial.bumpTexture.level;
}
}
}
if (babylonPBRMaterial.ambientTexture) {
const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.ambientTexture, mimeType, images, textures, samplers, imageData);
if (glTFTexture) {
let occlusionTexture: IMaterialOcclusionTextureInfo = {
index: glTFTexture.index
};
glTFMaterial.occlusionTexture = occlusionTexture;
if (babylonPBRMaterial.ambientTextureStrength) {
occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
}
}
}
if (babylonPBRMaterial.emissiveTexture) {
const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, samplers, imageData);
if (glTFTexture != null) {
glTFMaterial.emissiveTexture = glTFTexture;
}
}
}
if (!this.FuzzyEquals(babylonPBRMaterial.emissiveColor, Color3.Black(), this._epsilon)) {
glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
}
if (babylonPBRMaterial.transparencyMode != null) {
const alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMaterial);
if (alphaMode) {
if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
glTFMaterial.alphaMode = alphaMode;
if (alphaMode === MaterialAlphaMode.MASK) {
glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
}
}
}
}
glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
materials.push(glTFMaterial);
}
}
private static GetPixelsFromTexture(babylonTexture: Texture): Uint8Array | Float32Array {
let pixels = babylonTexture.textureType === Engine.TEXTURETYPE_UNSIGNED_INT ? babylonTexture.readPixels() as Uint8Array : babylonTexture.readPixels() as Float32Array;
return pixels;
}
/**
* Extracts a texture from a Babylon texture into file data and glTF data
* @param babylonTexture Babylon texture to extract
* @param mimeType Mime Type of the babylonTexture
* @param images Array of glTF images
* @param textures Array of glTF textures
* @param imageData map of image file name and data
* @return glTF texture info, or null if the texture format is not supported
*/
private static _ExportTexture(babylonTexture: BaseTexture, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }): Nullable {
const sampler = _GLTFMaterial._GetGLTFTextureSampler(babylonTexture);
let samplerIndex: Nullable = null;
// if a pre-existing sampler with identical parameters exists, then reuse the previous sampler
let foundSamplerIndex: Nullable = null;
for (let i = 0; i < samplers.length; ++i) {
let s = samplers[i];
if (s.minFilter === sampler.minFilter && s.magFilter === sampler.magFilter &&
s.wrapS === sampler.wrapS && s.wrapT === sampler.wrapT) {
foundSamplerIndex = i;
break;
}
}
if (foundSamplerIndex == null) {
samplers.push(sampler);
samplerIndex = samplers.length - 1;
}
else {
samplerIndex = foundSamplerIndex;
}
let textureName = "texture_" + (textures.length - 1).toString();
let textureData = babylonTexture.getInternalTexture();
if (textureData != null) {
textureName = textureData.url || textureName;
}
textureName = Tools.GetFilename(textureName);
const baseFile = textureName.split('.')[0];
let extension = "";
if (mimeType === ImageMimeType.JPEG) {
extension = ".jpg";
}
else if (mimeType === ImageMimeType.PNG) {
extension = ".png";
}
else {
Tools.Error("Unsupported mime type " + mimeType);
return null;
}
textureName = baseFile + extension;
const pixels = _GLTFMaterial.GetPixelsFromTexture(babylonTexture as Texture);
const size = babylonTexture.getSize();
const base64Data = this._CreateBase64FromCanvas(pixels, size.width, size.height, mimeType);
return this._GetTextureInfoFromBase64(base64Data, textureName, mimeType, images, textures, babylonTexture.coordinatesIndex, samplerIndex, imageData);
}
/**
* Builds a texture from base64 string
* @param base64Texture base64 texture string
* @param textureName Name to use for the texture
* @param mimeType image mime type for the texture
* @param images array of images
* @param textures array of textures
* @param imageData map of image data
* @returns glTF texture info, or null if the texture format is not supported
*/
private static _GetTextureInfoFromBase64(base64Texture: string, textureName: string, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], texCoordIndex: number, samplerIndex: Nullable, imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }): Nullable {
let textureInfo: Nullable = null;
const glTFTexture: ITexture = {
source: images.length,
name: textureName
};
if (samplerIndex != null) {
glTFTexture.sampler = samplerIndex;
}
const binStr = atob(base64Texture.split(',')[1]);
let arrBuff = new ArrayBuffer(binStr.length);
const arr = new Uint8Array(arrBuff);
for (let i = 0, length = binStr.length; i < length; ++i) {
arr[i] = binStr.charCodeAt(i);
}
const imageValues = { data: arr, mimeType: mimeType };
imageData[textureName] = imageValues;
if (mimeType === ImageMimeType.JPEG || mimeType === ImageMimeType.PNG) {
const glTFImage: IImage = {
uri: textureName
}
let foundIndex: Nullable = null;
for (let i = 0; i < images.length; ++i) {
if (images[i].uri === textureName) {
foundIndex = i;
break;
}
}
if (foundIndex == null) {
images.push(glTFImage);
glTFTexture.source = images.length - 1;
}
else {
glTFTexture.source = foundIndex;
}
textures.push(glTFTexture);
textureInfo = {
index: textures.length - 1
}
if (texCoordIndex) {
textureInfo.texCoord = texCoordIndex;
}
}
return textureInfo;
}
}
}