|
@@ -1,5 +1,8 @@
|
|
|
using BabylonExport.Entities;
|
|
|
using GLTFExport.Entities;
|
|
|
+using System;
|
|
|
+using System.Drawing;
|
|
|
+using System.IO;
|
|
|
|
|
|
namespace Max2Babylon
|
|
|
{
|
|
@@ -23,6 +26,7 @@ namespace Max2Babylon
|
|
|
RaiseMessage("GLTFExporter.Material | babylonMaterial.alpha=" + babylonMaterial.alpha, 3);
|
|
|
RaiseMessage("GLTFExporter.Material | babylonMaterial.backFaceCulling=" + babylonMaterial.backFaceCulling, 3);
|
|
|
RaiseMessage("GLTFExporter.Material | babylonMaterial.wireframe=" + babylonMaterial.wireframe, 3);
|
|
|
+ RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.specularPower=" + babylonStandardMaterial.specularPower, 3);
|
|
|
|
|
|
// Ambient
|
|
|
for (int i = 0; i < babylonStandardMaterial.ambient.Length; i++)
|
|
@@ -113,28 +117,126 @@ namespace Max2Babylon
|
|
|
var gltfPbrMetallicRoughness = new GLTFPBRMetallicRoughness();
|
|
|
gltfMaterial.pbrMetallicRoughness = gltfPbrMetallicRoughness;
|
|
|
|
|
|
- // TODO - Retreive diffuse or albedo?
|
|
|
+ // --- Global ---
|
|
|
+
|
|
|
+ SpecularGlossiness _specularGlossiness = new SpecularGlossiness
|
|
|
+ {
|
|
|
+ diffuse = new BabylonColor3(babylonStandardMaterial.diffuse),
|
|
|
+ opacity = babylonMaterial.alpha,
|
|
|
+ specular = new BabylonColor3(babylonStandardMaterial.specular),
|
|
|
+ glossiness = babylonStandardMaterial.specularPower / 256
|
|
|
+ };
|
|
|
+
|
|
|
+ MetallicRoughness _metallicRoughness = ConvertToMetallicRoughness(_specularGlossiness, true);
|
|
|
+
|
|
|
// Base color
|
|
|
- var babylonDiffuseColor = babylonStandardMaterial.diffuse;
|
|
|
gltfPbrMetallicRoughness.baseColorFactor = new float[4]
|
|
|
{
|
|
|
- babylonDiffuseColor[0],
|
|
|
- babylonDiffuseColor[1],
|
|
|
- babylonDiffuseColor[2],
|
|
|
- babylonMaterial.alpha
|
|
|
+ _metallicRoughness.baseColor.r,
|
|
|
+ _metallicRoughness.baseColor.g,
|
|
|
+ _metallicRoughness.baseColor.b,
|
|
|
+ _metallicRoughness.opacity
|
|
|
};
|
|
|
- gltfPbrMetallicRoughness.baseColorTexture = ExportTexture(babylonStandardMaterial.diffuseTexture, gltf);
|
|
|
-
|
|
|
- // TODO - Metallic roughness
|
|
|
- gltfPbrMetallicRoughness.metallicFactor = 0; // Non metal
|
|
|
- // TODO - roughnessFactor
|
|
|
- // TODO - metallicRoughnessTexture
|
|
|
+
|
|
|
+ // Metallic roughness
|
|
|
+ gltfPbrMetallicRoughness.metallicFactor = _metallicRoughness.metallic;
|
|
|
+ gltfPbrMetallicRoughness.roughnessFactor = _metallicRoughness.roughness;
|
|
|
+
|
|
|
+
|
|
|
+ // --- Textures ---
|
|
|
+
|
|
|
+ if (babylonStandardMaterial.diffuseTexture != null)
|
|
|
+ {
|
|
|
+ Func<string, Bitmap> loadTexture = delegate (string textureName)
|
|
|
+ {
|
|
|
+ var pathDiffuse = Path.Combine(gltf.OutputPath, textureName);
|
|
|
+ if (File.Exists(pathDiffuse))
|
|
|
+ {
|
|
|
+ return new Bitmap(pathDiffuse);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ RaiseWarning(string.Format("GLTFExporter.Material | Texture {0} not found.", textureName), 2);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ Bitmap diffuseBitmap = loadTexture(babylonStandardMaterial.diffuseTexture.name);
|
|
|
+
|
|
|
+ if (diffuseBitmap != null)
|
|
|
+ {
|
|
|
+ Bitmap specularBitmap = null;
|
|
|
+ if (babylonStandardMaterial.specularTexture != null)
|
|
|
+ {
|
|
|
+ specularBitmap = loadTexture(babylonStandardMaterial.specularTexture.name);
|
|
|
+ }
|
|
|
+
|
|
|
+ Bitmap opacityBitmap = null;
|
|
|
+ if (babylonStandardMaterial.diffuseTexture.hasAlpha == false && babylonStandardMaterial.opacityTexture != null)
|
|
|
+ {
|
|
|
+ opacityBitmap = loadTexture(babylonStandardMaterial.opacityTexture.name);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Retreive dimension from diffuse map
|
|
|
+ var width = diffuseBitmap.Width;
|
|
|
+ var height = diffuseBitmap.Height;
|
|
|
+
|
|
|
+ // Create base color and metallic+roughness maps
|
|
|
+ Bitmap baseColorBitmap = new Bitmap(width, height);
|
|
|
+ Bitmap metallicRoughnessBitmap = new Bitmap(width, height);
|
|
|
+ for (int x = 0; x < width; x++)
|
|
|
+ {
|
|
|
+ for (int y = 0; y < height; y++)
|
|
|
+ {
|
|
|
+ var diffuse = diffuseBitmap.GetPixel(x, y);
|
|
|
+ SpecularGlossiness specularGlossinessTexture = new SpecularGlossiness
|
|
|
+ {
|
|
|
+ diffuse = new BabylonColor3(diffuse),
|
|
|
+ opacity = babylonStandardMaterial.diffuseTexture.hasAlpha? diffuse.A / 255.0f :
|
|
|
+ opacityBitmap != null && babylonStandardMaterial.opacityTexture.getAlphaFromRGB ? opacityBitmap.GetPixel(x, y).R / 255.0f :
|
|
|
+ opacityBitmap != null && babylonStandardMaterial.opacityTexture.getAlphaFromRGB == false ? opacityBitmap.GetPixel(x, y).A / 255.0f :
|
|
|
+ 1,
|
|
|
+ specular = specularBitmap != null ? new BabylonColor3(specularBitmap.GetPixel(x, y)) :
|
|
|
+ new BabylonColor3(),
|
|
|
+ glossiness = babylonStandardMaterial.useGlossinessFromSpecularMapAlpha && specularBitmap != null ? specularBitmap.GetPixel(x, y).A / 255.0f :
|
|
|
+ babylonStandardMaterial.specularPower / 256.0f
|
|
|
+ };
|
|
|
+
|
|
|
+ var displayPrints = x == width / 2 && y == height / 2;
|
|
|
+ MetallicRoughness metallicRoughnessTexture = ConvertToMetallicRoughness(specularGlossinessTexture, displayPrints);
|
|
|
+
|
|
|
+ Color colorBase = Color.FromArgb(
|
|
|
+ (int)(metallicRoughnessTexture.opacity * 255),
|
|
|
+ (int)(metallicRoughnessTexture.baseColor.r * 255),
|
|
|
+ (int)(metallicRoughnessTexture.baseColor.g * 255),
|
|
|
+ (int)(metallicRoughnessTexture.baseColor.b * 255)
|
|
|
+ );
|
|
|
+ baseColorBitmap.SetPixel(x, y, colorBase);
|
|
|
+
|
|
|
+ // The metalness values are sampled from the B channel.
|
|
|
+ // The roughness values are sampled from the G channel.
|
|
|
+ // These values are linear. If other channels are present (R or A), they are ignored for metallic-roughness calculations.
|
|
|
+ Color colorMetallicRoughness = Color.FromArgb(
|
|
|
+ 0,
|
|
|
+ (int)(metallicRoughnessTexture.roughness * 255),
|
|
|
+ (int)(metallicRoughnessTexture.metallic * 255)
|
|
|
+ );
|
|
|
+ metallicRoughnessBitmap.SetPixel(x, y, colorMetallicRoughness);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Export maps and textures
|
|
|
+ gltfPbrMetallicRoughness.baseColorTexture = ExportBitmapTexture(babylonStandardMaterial.diffuseTexture, baseColorBitmap, babylonMaterial.name + "_baseColor" + ".png", gltf);
|
|
|
+ gltfPbrMetallicRoughness.metallicRoughnessTexture = ExportBitmapTexture(babylonStandardMaterial.diffuseTexture, metallicRoughnessBitmap, babylonMaterial.name + "_metallicRoughness" + ".jpg", gltf);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void getAlphaMode(BabylonStandardMaterial babylonMaterial, out string alphaMode, out float? alphaCutoff)
|
|
|
{
|
|
|
- if (babylonMaterial.diffuseTexture != null && babylonMaterial.diffuseTexture.hasAlpha)
|
|
|
+ if ((babylonMaterial.diffuseTexture != null && babylonMaterial.diffuseTexture.hasAlpha) ||
|
|
|
+ babylonMaterial.opacityTexture != null)
|
|
|
{
|
|
|
// TODO - Babylon standard material is assumed to useAlphaFromDiffuseTexture. If not, the alpha mode is a mask.
|
|
|
alphaMode = GLTFMaterial.AlphaMode.BLEND.ToString();
|
|
@@ -146,5 +248,91 @@ namespace Max2Babylon
|
|
|
}
|
|
|
alphaCutoff = null;
|
|
|
}
|
|
|
+
|
|
|
+ BabylonColor3 dielectricSpecular = new BabylonColor3(0.04f, 0.04f, 0.04f);
|
|
|
+ const float epsilon = 1e-6f;
|
|
|
+
|
|
|
+ private MetallicRoughness ConvertToMetallicRoughness(SpecularGlossiness specularGlossiness, bool displayPrints = false)
|
|
|
+ {
|
|
|
+ var diffuse = specularGlossiness.diffuse;
|
|
|
+ var opacity = specularGlossiness.opacity;
|
|
|
+ var specular = specularGlossiness.specular;
|
|
|
+ var glossiness = specularGlossiness.glossiness;
|
|
|
+
|
|
|
+ var oneMinusSpecularStrength = 1 - specular.getMaxComponent();
|
|
|
+ var metallic = solveMetallic(diffuse.getPerceivedBrightness(), specular.getPerceivedBrightness(), oneMinusSpecularStrength);
|
|
|
+
|
|
|
+ var diffuseScaleFactor = oneMinusSpecularStrength / (1 - dielectricSpecular.r) / Math.Max(1 - metallic, epsilon);
|
|
|
+ var baseColorFromDiffuse = diffuse.scale(diffuseScaleFactor);
|
|
|
+ var baseColorFromSpecular = specular.subtract(dielectricSpecular.scale(1 - metallic)).scale(1 / Math.Max(metallic, epsilon));
|
|
|
+ var baseColor = BabylonColor3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic).clamp();
|
|
|
+ //var baseColor = baseColorFromDiffuse.clamp();
|
|
|
+
|
|
|
+ if (displayPrints)
|
|
|
+ {
|
|
|
+ RaiseMessage("-----------------------", 3);
|
|
|
+ RaiseMessage("diffuse=" + diffuse, 3);
|
|
|
+ RaiseMessage("opacity=" + opacity, 3);
|
|
|
+ RaiseMessage("specular=" + specular, 3);
|
|
|
+ RaiseMessage("glossiness=" + glossiness, 3);
|
|
|
+
|
|
|
+ RaiseMessage("oneMinusSpecularStrength=" + oneMinusSpecularStrength, 3);
|
|
|
+ RaiseMessage("metallic=" + metallic, 3);
|
|
|
+ RaiseMessage("diffuseScaleFactor=" + diffuseScaleFactor, 3);
|
|
|
+ RaiseMessage("baseColorFromDiffuse=" + baseColorFromDiffuse, 3);
|
|
|
+ RaiseMessage("baseColorFromSpecular=" + baseColorFromSpecular, 3);
|
|
|
+ RaiseMessage("metallic * metallic=" + metallic * metallic, 3);
|
|
|
+ RaiseMessage("baseColor=" + baseColor, 3);
|
|
|
+ RaiseMessage("-----------------------", 3);
|
|
|
+ }
|
|
|
+
|
|
|
+ return new MetallicRoughness
|
|
|
+ {
|
|
|
+ baseColor = baseColor,
|
|
|
+ opacity = opacity,
|
|
|
+ metallic = metallic,
|
|
|
+ roughness = 1 - glossiness
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ private float solveMetallic(float diffuse, float specular, float oneMinusSpecularStrength)
|
|
|
+ {
|
|
|
+ if (specular < dielectricSpecular.r)
|
|
|
+ {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ var a = dielectricSpecular.r;
|
|
|
+ var b = diffuse * oneMinusSpecularStrength / (1 - dielectricSpecular.r) + specular - 2 * dielectricSpecular.r;
|
|
|
+ var c = dielectricSpecular.r - specular;
|
|
|
+ var D = b * b - 4 * a * c;
|
|
|
+ return ClampScalar((float)(-b + Math.Sqrt(D)) / (2 * a), 0, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the value itself if it's between min and max.
|
|
|
+ * Returns min if the value is lower than min.
|
|
|
+ * Returns max if the value is greater than max.
|
|
|
+ */
|
|
|
+ private static float ClampScalar(float value, float min = 0, float max = 1)
|
|
|
+ {
|
|
|
+ return Math.Min(max, Math.Max(min, value));
|
|
|
+ }
|
|
|
+
|
|
|
+ private class SpecularGlossiness
|
|
|
+ {
|
|
|
+ public BabylonColor3 diffuse;
|
|
|
+ public float opacity;
|
|
|
+ public BabylonColor3 specular;
|
|
|
+ public float glossiness;
|
|
|
+ }
|
|
|
+
|
|
|
+ private class MetallicRoughness
|
|
|
+ {
|
|
|
+ public BabylonColor3 baseColor;
|
|
|
+ public float opacity;
|
|
|
+ public float metallic;
|
|
|
+ public float roughness;
|
|
|
+ }
|
|
|
}
|
|
|
}
|