|
@@ -1,6 +1,50 @@
|
|
|
/// <reference path="../../../../dist/babylon.glTF2Interface.d.ts"/>
|
|
|
|
|
|
module BABYLON.GLTF2 {
|
|
|
+ /**
|
|
|
+ * Interface for storing specular glossiness factors.
|
|
|
+ */
|
|
|
+ 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.
|
|
|
+ */
|
|
|
+ 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<string>;
|
|
|
+ /**
|
|
|
+ * The base color texture as a base64 string.
|
|
|
+ */
|
|
|
+ baseColorTextureBase64?: Nullable<string>;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Utility methods for working with glTF material conversion properties. This class should only be used internally.
|
|
|
*/
|
|
@@ -8,39 +52,54 @@ module BABYLON.GLTF2 {
|
|
|
/**
|
|
|
* Represents the dielectric specular values for R, G and B.
|
|
|
*/
|
|
|
- private static readonly dielectricSpecular: Color3 = new Color3(0.04, 0.04, 0.04);
|
|
|
+ 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;
|
|
|
+ private static _maxSpecularPower = 1024;
|
|
|
|
|
|
- private static epsilon = 1e-6;
|
|
|
+ /**
|
|
|
+ * 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
|
|
|
- * @param mimeType
|
|
|
- * @param images
|
|
|
- * @param textures
|
|
|
- * @param materials
|
|
|
- * @param imageData
|
|
|
- * @param hasTextureCoords
|
|
|
+ * @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[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
|
|
|
+ public static _ConvertMaterialsToGLTF(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
|
|
|
for (let i = 0; i < babylonMaterials.length; ++i) {
|
|
|
const babylonMaterial = babylonMaterials[i];
|
|
|
if (babylonMaterial instanceof StandardMaterial) {
|
|
|
- _GLTFMaterial.ConvertStandardMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
|
|
|
+ _GLTFMaterial._ConvertStandardMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
|
|
|
}
|
|
|
else if (babylonMaterial instanceof PBRMetallicRoughnessMaterial) {
|
|
|
- _GLTFMaterial.ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
|
|
|
+ _GLTFMaterial._ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
|
|
|
}
|
|
|
else if (babylonMaterial instanceof PBRMaterial) {
|
|
|
- _GLTFMaterial.ConvertPBRMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
|
|
|
+ _GLTFMaterial._ConvertPBRMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
|
|
|
}
|
|
|
else {
|
|
|
- Tools.Error("Unsupported material type: " + babylonMaterial.name);
|
|
|
+ throw new Error("Unsupported material type: " + babylonMaterial.name);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -50,7 +109,7 @@ module BABYLON.GLTF2 {
|
|
|
* @param originalMaterial - original glTF material.
|
|
|
* @returns glTF material without texture parameters
|
|
|
*/
|
|
|
- public static StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial {
|
|
|
+ public static _StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial {
|
|
|
let newMaterial: IMaterial = {};
|
|
|
if (originalMaterial) {
|
|
|
newMaterial.name = originalMaterial.name;
|
|
@@ -74,7 +133,7 @@ module BABYLON.GLTF2 {
|
|
|
* @param material - glTF Material.
|
|
|
* @returns boolean specifying if texture parameters are present
|
|
|
*/
|
|
|
- public static HasTexturesPresent(material: IMaterial): boolean {
|
|
|
+ public static _HasTexturesPresent(material: IMaterial): boolean {
|
|
|
if (material.emissiveTexture || material.normalTexture || material.occlusionTexture) {
|
|
|
return true;
|
|
|
}
|
|
@@ -93,7 +152,7 @@ module BABYLON.GLTF2 {
|
|
|
* @param babylonStandardMaterial
|
|
|
* @returns - glTF Metallic Roughness Material representation
|
|
|
*/
|
|
|
- public static ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness {
|
|
|
+ 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);
|
|
@@ -108,7 +167,7 @@ module BABYLON.GLTF2 {
|
|
|
* @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 {
|
|
|
+ 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 +
|
|
@@ -124,16 +183,16 @@ module BABYLON.GLTF2 {
|
|
|
* @param specularPower - specular power of standard material.
|
|
|
* @returns - Number representing the roughness value.
|
|
|
*/
|
|
|
- function solveForRoughness(specularPower: number): number {
|
|
|
+ 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);
|
|
|
+ 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);
|
|
|
+ let specularPower = Scalar.Clamp(babylonStandardMaterial.specularPower, 0, this._maxSpecularPower);
|
|
|
|
|
|
- const roughness = solveForRoughness(specularPower);
|
|
|
+ const roughness = _solveForRoughness(specularPower);
|
|
|
|
|
|
const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {
|
|
|
baseColorFactor: [
|
|
@@ -156,15 +215,15 @@ module BABYLON.GLTF2 {
|
|
|
* @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
|
|
|
+ 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 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);
|
|
|
}
|
|
@@ -174,7 +233,7 @@ module BABYLON.GLTF2 {
|
|
|
* @param babylonMaterial - Babylon Material
|
|
|
* @returns - The Babylon alpha mode value
|
|
|
*/
|
|
|
- public static GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode {
|
|
|
+ public static _GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode {
|
|
|
if (babylonMaterial instanceof StandardMaterial) {
|
|
|
const babylonStandardMaterial = babylonMaterial as StandardMaterial;
|
|
|
if ((babylonStandardMaterial.alpha != 1.0) ||
|
|
@@ -245,12 +304,12 @@ module BABYLON.GLTF2 {
|
|
|
* @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[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
|
|
|
+ public static _ConvertStandardMaterial(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
|
|
|
Tools.Warn(babylonStandardMaterial.name + ": Standard Material is currently not fully supported/implemented in glTF serializer");
|
|
|
- const glTFPbrMetallicRoughness = _GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
|
|
|
+ const glTFPbrMetallicRoughness = _GLTFMaterial._ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
|
|
|
|
|
|
const glTFMaterial: IMaterial = { name: babylonStandardMaterial.name };
|
|
|
- if (babylonStandardMaterial.backFaceCulling) {
|
|
|
+ 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.");
|
|
|
}
|
|
@@ -258,26 +317,26 @@ module BABYLON.GLTF2 {
|
|
|
}
|
|
|
if (hasTextureCoords) {
|
|
|
if (babylonStandardMaterial.diffuseTexture) {
|
|
|
- const glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, imageData);
|
|
|
+ const glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, imageData);
|
|
|
if (glTFTexture != null) {
|
|
|
glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
|
|
|
}
|
|
|
}
|
|
|
if (babylonStandardMaterial.bumpTexture) {
|
|
|
- const glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.bumpTexture, mimeType, images, textures, imageData)
|
|
|
+ const glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.bumpTexture, mimeType, images, textures, imageData)
|
|
|
if (glTFTexture) {
|
|
|
glTFMaterial.normalTexture = glTFTexture;
|
|
|
}
|
|
|
}
|
|
|
if (babylonStandardMaterial.emissiveTexture) {
|
|
|
- const glTFEmissiveTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.emissiveTexture, mimeType, images, textures, imageData)
|
|
|
+ const glTFEmissiveTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.emissiveTexture, mimeType, images, textures, 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, imageData);
|
|
|
+ const glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
|
|
|
if (glTFTexture) {
|
|
|
const occlusionTexture: IMaterialOcclusionTextureInfo = {
|
|
|
index: glTFTexture.index
|
|
@@ -297,7 +356,7 @@ module BABYLON.GLTF2 {
|
|
|
Tools.Warn(babylonStandardMaterial.name + ": glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
|
|
|
}
|
|
|
}
|
|
|
- if (babylonStandardMaterial.emissiveColor) {
|
|
|
+ if (babylonStandardMaterial.emissiveColor && !this.FuzzyEquals(babylonStandardMaterial.emissiveColor, Color3.Black(), this._epsilon)) {
|
|
|
glTFMaterial.emissiveFactor = babylonStandardMaterial.emissiveColor.asArray();
|
|
|
}
|
|
|
|
|
@@ -316,7 +375,7 @@ module BABYLON.GLTF2 {
|
|
|
* @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[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
|
|
|
+ public static _ConvertPBRMetallicRoughnessMaterial(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
|
|
|
const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {};
|
|
|
|
|
|
if (babylonPBRMetalRoughMaterial.baseColor) {
|
|
@@ -328,10 +387,10 @@ module BABYLON.GLTF2 {
|
|
|
];
|
|
|
}
|
|
|
|
|
|
- if (babylonPBRMetalRoughMaterial.metallic != null) {
|
|
|
+ if (babylonPBRMetalRoughMaterial.metallic != null && babylonPBRMetalRoughMaterial.metallic !== 1) {
|
|
|
glTFPbrMetallicRoughness.metallicFactor = babylonPBRMetalRoughMaterial.metallic;
|
|
|
}
|
|
|
- if (babylonPBRMetalRoughMaterial.roughness != null) {
|
|
|
+ if (babylonPBRMetalRoughMaterial.roughness != null && babylonPBRMetalRoughMaterial.roughness !== 1) {
|
|
|
glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMetalRoughMaterial.roughness;
|
|
|
}
|
|
|
|
|
@@ -344,19 +403,19 @@ module BABYLON.GLTF2 {
|
|
|
|
|
|
if (hasTextureCoords) {
|
|
|
if (babylonPBRMetalRoughMaterial.baseTexture != null) {
|
|
|
- const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, imageData);
|
|
|
+ const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, imageData);
|
|
|
if (glTFTexture != null) {
|
|
|
glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
|
|
|
}
|
|
|
}
|
|
|
if (babylonPBRMetalRoughMaterial.normalTexture) {
|
|
|
- const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, imageData);
|
|
|
+ const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, imageData);
|
|
|
if (glTFTexture) {
|
|
|
glTFMaterial.normalTexture = glTFTexture;
|
|
|
}
|
|
|
}
|
|
|
if (babylonPBRMetalRoughMaterial.occlusionTexture) {
|
|
|
- const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, imageData);
|
|
|
+ const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, imageData);
|
|
|
if (glTFTexture) {
|
|
|
glTFMaterial.occlusionTexture = glTFTexture;
|
|
|
if (babylonPBRMetalRoughMaterial.occlusionStrength != null) {
|
|
@@ -365,18 +424,18 @@ module BABYLON.GLTF2 {
|
|
|
}
|
|
|
}
|
|
|
if (babylonPBRMetalRoughMaterial.emissiveTexture) {
|
|
|
- const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.emissiveTexture, mimeType, images, textures, imageData);
|
|
|
+ const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.emissiveTexture, mimeType, images, textures, imageData);
|
|
|
if (glTFTexture != null) {
|
|
|
glTFMaterial.emissiveTexture = glTFTexture;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (babylonPBRMetalRoughMaterial.emissiveColor.equalsFloats(0.0, 0.0, 0.0)) {
|
|
|
+ if (this.FuzzyEquals(babylonPBRMetalRoughMaterial.emissiveColor, Color3.Black(), this._epsilon)) {
|
|
|
glTFMaterial.emissiveFactor = babylonPBRMetalRoughMaterial.emissiveColor.asArray();
|
|
|
}
|
|
|
if (babylonPBRMetalRoughMaterial.transparencyMode != null) {
|
|
|
- const alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMetalRoughMaterial);
|
|
|
+ const alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMetalRoughMaterial);
|
|
|
|
|
|
if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
|
|
|
glTFMaterial.alphaMode = alphaMode;
|
|
@@ -392,12 +451,255 @@ module BABYLON.GLTF2 {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * 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, width: number, height: number, mimeType: ImageMimeType): string {
|
|
|
+ const imageCanvas = document.createElement('canvas');
|
|
|
+ imageCanvas.id = "WriteCanvas";
|
|
|
+ const ctx = imageCanvas.getContext('2d') as CanvasRenderingContext2D;
|
|
|
+ imageCanvas.width = width;
|
|
|
+ imageCanvas.height = height;
|
|
|
+
|
|
|
+ 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) {
|
|
|
+ data[i] = 255;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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) {
|
|
|
+ throw new Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Scene from textures is missing!");
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 {
|
|
|
+ throw new Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture1.name);
|
|
|
+ }
|
|
|
+ pixels = resizedTextures.texture2.readPixels();
|
|
|
+
|
|
|
+ if (pixels instanceof Uint8Array) {
|
|
|
+ specularGlossinessBuffer = (resizedTextures.texture2.readPixels()) as Uint8Array;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ throw new Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture2.name);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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]).multiply(factors.diffuseColor);
|
|
|
+ const specularColor = Color3.FromInts(specularGlossinessBuffer[offset], specularGlossinessBuffer[offset + 1], specularGlossinessBuffer[offset + 2]).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] = 255;
|
|
|
+ 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 baseColorPixel = Color3.FromArray([baseColorBuffer[destinationOffset], baseColorBuffer[destinationOffset + 1], baseColorBuffer[destinationOffset + 2]]);
|
|
|
+
|
|
|
+ if (!this.FuzzyEquals(baseColorPixel, 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.FromArray([metallicRoughnessBuffer[destinationOffset], 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.
|
|
|
*/
|
|
|
- public static GetPerceivedBrightness(color: Color3): number {
|
|
|
+ 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);
|
|
|
}
|
|
@@ -409,7 +711,7 @@ module BABYLON.GLTF2 {
|
|
|
* @param color
|
|
|
* @returns maximum color component value, or zero if color is null or undefined.
|
|
|
*/
|
|
|
- public static GetMaxComponent(color: Color3): number {
|
|
|
+ private static _GetMaxComponent(color: Color3): number {
|
|
|
if (color) {
|
|
|
return Math.max(color.r, Math.max(color.g, color.b));
|
|
|
}
|
|
@@ -426,115 +728,118 @@ module BABYLON.GLTF2 {
|
|
|
* @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[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
|
|
|
+ public static _ConvertPBRMaterial(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], 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 (babylonPBRMaterial) {
|
|
|
- if (useMetallicRoughness) {
|
|
|
- glTFPbrMetallicRoughness.baseColorFactor = [
|
|
|
- babylonPBRMaterial.albedoColor.r,
|
|
|
- babylonPBRMaterial.albedoColor.g,
|
|
|
- babylonPBRMaterial.albedoColor.b,
|
|
|
- babylonPBRMaterial.alpha
|
|
|
- ];
|
|
|
- if (babylonPBRMaterial.metallic != null) {
|
|
|
- if (babylonPBRMaterial.metallic !== 1) {
|
|
|
- glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
|
|
|
- }
|
|
|
- }
|
|
|
- if (babylonPBRMaterial.roughness != null) {
|
|
|
- if (babylonPBRMaterial.roughness !== 1) {
|
|
|
- glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
|
|
|
- }
|
|
|
- }
|
|
|
+ if (!useMetallicRoughness) {
|
|
|
+ const specGloss: _IPBRSpecularGlossiness = {
|
|
|
+ diffuseColor: babylonPBRMaterial.albedoColor || Color3.White(),
|
|
|
+ specularColor: babylonPBRMaterial.reflectivityColor || Color3.White(),
|
|
|
+ glossiness: babylonPBRMaterial.microSurface || 1,
|
|
|
+ };
|
|
|
+ if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
|
|
|
+ throw new Error("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture currently not supported");
|
|
|
+ }
|
|
|
+
|
|
|
+ metallicRoughness = this._ConvertSpecularGlossinessTexturesToMetallicRoughness(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType);
|
|
|
+
|
|
|
+ if (!metallicRoughness) {
|
|
|
+ metallicRoughness = this._ConvertSpecularGlossinessToMetallicRoughness(specGloss);
|
|
|
}
|
|
|
else {
|
|
|
- const diffuseColor = babylonPBRMaterial.albedoColor || Color3.Black();
|
|
|
- const specularColor = babylonPBRMaterial.reflectionColor || Color3.Black();
|
|
|
- const diffusePerceivedBrightness = _GLTFMaterial.GetPerceivedBrightness(diffuseColor);
|
|
|
- const specularPerceivedBrightness = _GLTFMaterial.GetPerceivedBrightness(specularColor);
|
|
|
- const oneMinusSpecularStrength = 1 - _GLTFMaterial.GetMaxComponent(babylonPBRMaterial.reflectionColor);
|
|
|
- const metallic = _GLTFMaterial.SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
|
|
|
- const glossiness = babylonPBRMaterial.microSurface || 0;
|
|
|
- const baseColorFromDiffuse = diffuseColor.scale(oneMinusSpecularStrength / (1.0 - this.dielectricSpecular.r) / Math.max(1 - metallic, this.epsilon));
|
|
|
- const baseColorFromSpecular = 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);
|
|
|
-
|
|
|
- glTFPbrMetallicRoughness.baseColorFactor = [
|
|
|
- baseColor.r,
|
|
|
- baseColor.g,
|
|
|
- baseColor.b,
|
|
|
- babylonPBRMaterial.alpha
|
|
|
- ];
|
|
|
- if (metallic !== 1) {
|
|
|
- glTFPbrMetallicRoughness.metallicFactor = metallic;
|
|
|
- }
|
|
|
- if (glossiness) {
|
|
|
- glTFPbrMetallicRoughness.roughnessFactor = 1 - glossiness;
|
|
|
- }
|
|
|
- }
|
|
|
- if (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.albedoTexture) {
|
|
|
- const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.albedoTexture, mimeType, images, textures, imageData);
|
|
|
- if (glTFTexture) {
|
|
|
- glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
|
|
|
+ if (metallicRoughness.baseColorTextureBase64) {
|
|
|
+ const glTFBaseColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughness.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, images, textures, imageData);
|
|
|
+ if (glTFBaseColorTexture != null) {
|
|
|
+ glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
|
|
|
}
|
|
|
}
|
|
|
- if (babylonPBRMaterial.bumpTexture) {
|
|
|
- const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.bumpTexture, mimeType, images, textures, imageData);
|
|
|
- if (glTFTexture) {
|
|
|
- glTFMaterial.normalTexture = glTFTexture;
|
|
|
+ if (metallicRoughness.metallicRoughnessTextureBase64) {
|
|
|
+ const glTFMRColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughness.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, images, textures, imageData);
|
|
|
+ if (glTFMRColorTexture != null) {
|
|
|
+ glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
|
|
|
}
|
|
|
}
|
|
|
- if (babylonPBRMaterial.ambientTexture) {
|
|
|
- const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.ambientTexture, mimeType, images, textures, imageData);
|
|
|
- if (glTFTexture) {
|
|
|
- let occlusionTexture: IMaterialOcclusionTextureInfo = {
|
|
|
- index: glTFTexture.index
|
|
|
- };
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- glTFMaterial.occlusionTexture = occlusionTexture;
|
|
|
+ if (!(this.FuzzyEquals(babylonPBRMaterial.albedoColor, Color3.White(), this._epsilon) && babylonPBRMaterial.alpha >= this._epsilon)) {
|
|
|
+ glTFPbrMetallicRoughness.baseColorFactor = [
|
|
|
+ babylonPBRMaterial.albedoColor.r,
|
|
|
+ babylonPBRMaterial.albedoColor.g,
|
|
|
+ babylonPBRMaterial.albedoColor.b,
|
|
|
+ babylonPBRMaterial.alpha
|
|
|
+ ];
|
|
|
+ }
|
|
|
|
|
|
- if (babylonPBRMaterial.ambientTextureStrength) {
|
|
|
- occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
|
|
|
- }
|
|
|
- }
|
|
|
+
|
|
|
+ if (babylonPBRMaterial.metallic != null && babylonPBRMaterial.metallic !== 1) {
|
|
|
+ glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
|
|
|
+ }
|
|
|
+ if (babylonPBRMaterial.roughness != null && babylonPBRMaterial.roughness !== 1) {
|
|
|
+ glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.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 (useMetallicRoughness && babylonPBRMaterial.albedoTexture) {
|
|
|
+ const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.albedoTexture, mimeType, images, textures, imageData);
|
|
|
+ if (glTFTexture) {
|
|
|
+ glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
|
|
|
}
|
|
|
- if (babylonPBRMaterial.emissiveTexture) {
|
|
|
- const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, imageData);
|
|
|
- if (glTFTexture != null) {
|
|
|
- glTFMaterial.emissiveTexture = glTFTexture;
|
|
|
- }
|
|
|
+ }
|
|
|
+ if (babylonPBRMaterial.bumpTexture) {
|
|
|
+ const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.bumpTexture, mimeType, images, textures, imageData);
|
|
|
+ if (glTFTexture) {
|
|
|
+ glTFMaterial.normalTexture = glTFTexture;
|
|
|
}
|
|
|
- if (babylonPBRMaterial.metallicTexture) {
|
|
|
- const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.metallicTexture, mimeType, images, textures, imageData);
|
|
|
- if (glTFTexture != null) {
|
|
|
- glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
|
|
|
+ }
|
|
|
+ if (babylonPBRMaterial.ambientTexture) {
|
|
|
+ const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.ambientTexture, mimeType, images, textures, imageData);
|
|
|
+ if (glTFTexture) {
|
|
|
+ let occlusionTexture: IMaterialOcclusionTextureInfo = {
|
|
|
+ index: glTFTexture.index
|
|
|
+ };
|
|
|
+
|
|
|
+ glTFMaterial.occlusionTexture = occlusionTexture;
|
|
|
+
|
|
|
+ if (babylonPBRMaterial.ambientTextureStrength) {
|
|
|
+ occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- if (!babylonPBRMaterial.emissiveColor.equalsFloats(0.0, 0.0, 0.0)) {
|
|
|
- glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
|
|
|
+ if (babylonPBRMaterial.emissiveTexture) {
|
|
|
+ const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, imageData);
|
|
|
+ if (glTFTexture != null) {
|
|
|
+ glTFMaterial.emissiveTexture = glTFTexture;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (babylonPBRMaterial.metallicTexture) {
|
|
|
+ const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.metallicTexture, mimeType, images, textures, imageData);
|
|
|
+ if (glTFTexture != null) {
|
|
|
+ glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
|
|
|
+ }
|
|
|
}
|
|
|
- if (babylonPBRMaterial.transparencyMode != null) {
|
|
|
- const alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
|
|
|
+ }
|
|
|
+ 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 !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
|
|
|
- glTFMaterial.alphaMode = alphaMode;
|
|
|
- if (alphaMode === MaterialAlphaMode.BLEND) {
|
|
|
- glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
|
|
|
- }
|
|
|
+ if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
|
|
|
+ glTFMaterial.alphaMode = alphaMode;
|
|
|
+ if (alphaMode === MaterialAlphaMode.BLEND) {
|
|
|
+ glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -550,20 +855,14 @@ module BABYLON.GLTF2 {
|
|
|
* @param images - Array of glTF images.
|
|
|
* @param textures - Array of glTF textures.
|
|
|
* @param imageData - map of image file name and data.
|
|
|
- * @return - glTF texture, or null if the texture format is not supported.
|
|
|
+ * @return - glTF texture info, or null if the texture format is not supported.
|
|
|
*/
|
|
|
- public static ExportTexture(babylonTexture: BaseTexture, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }): Nullable<ITextureInfo> {
|
|
|
- let textureInfo: Nullable<ITextureInfo> = null;
|
|
|
-
|
|
|
- let glTFTexture = {
|
|
|
- source: images.length
|
|
|
- };
|
|
|
-
|
|
|
+ private static _ExportTexture(babylonTexture: BaseTexture, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }): Nullable<ITextureInfo> {
|
|
|
let textureName = "texture_" + (textures.length - 1).toString();
|
|
|
let textureData = babylonTexture.getInternalTexture();
|
|
|
|
|
|
if (textureData != null) {
|
|
|
- textureName = textureData.url;
|
|
|
+ textureName = textureData.url || textureName;
|
|
|
}
|
|
|
|
|
|
textureName = Tools.GetFilename(textureName);
|
|
@@ -578,28 +877,38 @@ module BABYLON.GLTF2 {
|
|
|
extension = ".png";
|
|
|
}
|
|
|
else {
|
|
|
- Tools.Error("Unsupported mime type " + mimeType);
|
|
|
+ throw new Error("Unsupported mime type " + mimeType);
|
|
|
}
|
|
|
textureName = baseFile + extension;
|
|
|
|
|
|
|
|
|
const pixels = babylonTexture.readPixels() as Uint8Array;
|
|
|
|
|
|
- const imageCanvas = document.createElement('canvas');
|
|
|
- imageCanvas.id = "ImageCanvas";
|
|
|
-
|
|
|
- const ctx = <CanvasRenderingContext2D>imageCanvas.getContext('2d');
|
|
|
const size = babylonTexture.getSize();
|
|
|
- imageCanvas.width = size.width;
|
|
|
- imageCanvas.height = size.height;
|
|
|
|
|
|
- const imgData = ctx.createImageData(size.width, size.height);
|
|
|
+ const base64Data = this._CreateBase64FromCanvas(pixels, size.width, size.height, mimeType);
|
|
|
|
|
|
+ return this._GetTextureInfoFromBase64(base64Data, textureName, mimeType, images, textures, imageData);
|
|
|
+ }
|
|
|
|
|
|
- imgData.data.set(pixels);
|
|
|
- ctx.putImageData(imgData, 0, 0);
|
|
|
- const base64Data = imageCanvas.toDataURL(mimeType);
|
|
|
- const binStr = atob(base64Data.split(',')[1]);
|
|
|
+ /**
|
|
|
+ * 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[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }): Nullable<ITextureInfo> {
|
|
|
+ let textureInfo: Nullable<ITextureInfo> = null;
|
|
|
+
|
|
|
+ const glTFTexture = {
|
|
|
+ source: images.length
|
|
|
+ };
|
|
|
+
|
|
|
+ const binStr = atob(base64Texture.split(',')[1]);
|
|
|
const arr = new Uint8Array(binStr.length);
|
|
|
for (let i = 0; i < binStr.length; ++i) {
|
|
|
arr[i] = binStr.charCodeAt(i);
|
|
@@ -607,7 +916,7 @@ module BABYLON.GLTF2 {
|
|
|
const imageValues = { data: arr, mimeType: mimeType };
|
|
|
|
|
|
imageData[textureName] = imageValues;
|
|
|
- if (mimeType === ImageMimeType.JPEG) {
|
|
|
+ if (mimeType === ImageMimeType.JPEG || mimeType === ImageMimeType.PNG) {
|
|
|
const glTFImage: IImage = {
|
|
|
uri: textureName
|
|
|
}
|