Ver código fonte

Optimize base color texture file format (png only if has alpha, jpg otherwise)
Add option to export images as binary
Fix buffer uri value
Fix computation of minimal dimensions of several bitmaps

noalak 8 anos atrás
pai
commit
81d8781390

+ 6 - 2
Exporters/3ds Max/GltfExport.Entities/GLTF.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.IO;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
 
 
 namespace GLTFExport.Entities
 namespace GLTFExport.Entities
@@ -45,7 +46,8 @@ namespace GLTFExport.Entities
         [DataMember(EmitDefaultValue = false)]
         [DataMember(EmitDefaultValue = false)]
         public GLTFSampler[] samplers { get; set; }
         public GLTFSampler[] samplers { get; set; }
 
 
-        public string OutputPath { get; private set; }
+        public string OutputFolder { get; private set; }
+        public string OutputFile { get; private set; }
 
 
         public List<GLTFNode> NodesList { get; private set; }
         public List<GLTFNode> NodesList { get; private set; }
         public List<GLTFCamera> CamerasList { get; private set; }
         public List<GLTFCamera> CamerasList { get; private set; }
@@ -63,10 +65,12 @@ namespace GLTFExport.Entities
         public GLTFBufferView bufferViewFloatVec3;
         public GLTFBufferView bufferViewFloatVec3;
         public GLTFBufferView bufferViewFloatVec4;
         public GLTFBufferView bufferViewFloatVec4;
         public GLTFBufferView bufferViewFloatVec2;
         public GLTFBufferView bufferViewFloatVec2;
+        public GLTFBufferView bufferViewImage;
 
 
         public GLTF(string outputPath)
         public GLTF(string outputPath)
         {
         {
-            OutputPath = outputPath;
+            OutputFolder = Path.GetDirectoryName(outputPath);
+            OutputFile = Path.GetFileNameWithoutExtension(outputPath);
 
 
             NodesList = new List<GLTFNode>();
             NodesList = new List<GLTFNode>();
             CamerasList = new List<GLTFCamera>();
             CamerasList = new List<GLTFCamera>();

+ 2 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFImage.cs

@@ -13,5 +13,7 @@ namespace GLTFExport.Entities
 
 
         [DataMember(EmitDefaultValue = false)]
         [DataMember(EmitDefaultValue = false)]
         public int? bufferView { get; set; }
         public int? bufferView { get; set; }
+
+        public string FileExtension;
     }
     }
 }
 }

+ 9 - 3
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.GLTFExporter.Material.cs

@@ -128,7 +128,7 @@ namespace Max2Babylon
                 };
                 };
 
 
                 MetallicRoughness _metallicRoughness = ConvertToMetallicRoughness(_specularGlossiness, true);
                 MetallicRoughness _metallicRoughness = ConvertToMetallicRoughness(_specularGlossiness, true);
-
+                
                 // Base color
                 // Base color
                 gltfPbrMetallicRoughness.baseColorFactor = new float[4]
                 gltfPbrMetallicRoughness.baseColorFactor = new float[4]
                 {
                 {
@@ -149,7 +149,7 @@ namespace Max2Babylon
                 {
                 {
                     Func<string, Bitmap> loadTextureFromOutput = delegate (string textureName)
                     Func<string, Bitmap> loadTextureFromOutput = delegate (string textureName)
                     {
                     {
-                        return LoadTexture(Path.Combine(gltf.OutputPath, textureName));
+                        return LoadTexture(Path.Combine(gltf.OutputFolder, textureName));
                     };
                     };
 
 
                     Bitmap diffuseBitmap = loadTextureFromOutput(babylonStandardMaterial.diffuseTexture.name);
                     Bitmap diffuseBitmap = loadTextureFromOutput(babylonStandardMaterial.diffuseTexture.name);
@@ -180,6 +180,7 @@ namespace Max2Babylon
                         // Create base color and metallic+roughness maps
                         // Create base color and metallic+roughness maps
                         Bitmap baseColorBitmap = new Bitmap(width, height);
                         Bitmap baseColorBitmap = new Bitmap(width, height);
                         Bitmap metallicRoughnessBitmap = new Bitmap(width, height);
                         Bitmap metallicRoughnessBitmap = new Bitmap(width, height);
+                        var hasAlpha = false;
                         for (int x = 0; x < width; x++)
                         for (int x = 0; x < width; x++)
                         {
                         {
                             for (int y = 0; y < height; y++)
                             for (int y = 0; y < height; y++)
@@ -208,6 +209,10 @@ namespace Max2Babylon
                                     (int)(metallicRoughnessTexture.baseColor.b * 255)
                                     (int)(metallicRoughnessTexture.baseColor.b * 255)
                                 );
                                 );
                                 baseColorBitmap.SetPixel(x, y, colorBase);
                                 baseColorBitmap.SetPixel(x, y, colorBase);
+                                if (metallicRoughnessTexture.opacity != 1)
+                                {
+                                    hasAlpha = true;
+                                }
 
 
                                 // The metalness values are sampled from the B channel.
                                 // The metalness values are sampled from the B channel.
                                 // The roughness values are sampled from the G channel.
                                 // The roughness values are sampled from the G channel.
@@ -222,7 +227,8 @@ namespace Max2Babylon
                         }
                         }
 
 
                         // Export maps and textures
                         // Export maps and textures
-                        gltfPbrMetallicRoughness.baseColorTexture = ExportBitmapTexture(babylonStandardMaterial.diffuseTexture, baseColorBitmap, babylonMaterial.name + "_baseColor" + ".png", gltf);
+                        var baseColorFileName = babylonMaterial.name + "_baseColor" + (hasAlpha ? ".png" : ".jpg");
+                        gltfPbrMetallicRoughness.baseColorTexture = ExportBitmapTexture(babylonStandardMaterial.diffuseTexture, baseColorBitmap, baseColorFileName, gltf);
                         gltfPbrMetallicRoughness.metallicRoughnessTexture = ExportBitmapTexture(babylonStandardMaterial.diffuseTexture, metallicRoughnessBitmap, babylonMaterial.name + "_metallicRoughness" + ".jpg", gltf);
                         gltfPbrMetallicRoughness.metallicRoughnessTexture = ExportBitmapTexture(babylonStandardMaterial.diffuseTexture, metallicRoughnessBitmap, babylonMaterial.name + "_metallicRoughness" + ".jpg", gltf);
                     }
                     }
                 }
                 }

+ 1 - 1
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.GLTFExporter.Mesh.cs

@@ -97,7 +97,7 @@ namespace Max2Babylon
             {
             {
                 buffer = new GLTFBuffer
                 buffer = new GLTFBuffer
                 {
                 {
-                    uri = gltfMesh.name + ".bin"
+                    uri = gltf.OutputFile + ".bin"
                 };
                 };
                 buffer.index = gltf.BuffersList.Count;
                 buffer.index = gltf.BuffersList.Count;
                 gltf.BuffersList.Add(buffer);
                 gltf.BuffersList.Add(buffer);

+ 12 - 2
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.GLTFExporter.Texture.cs

@@ -21,9 +21,10 @@ namespace Max2Babylon
             // Copy image to output
             // Copy image to output
             if (CopyTexturesToOutput)
             if (CopyTexturesToOutput)
             {
             {
-                var absolutePath = Path.Combine(gltf.OutputPath, name);
+                var absolutePath = Path.Combine(gltf.OutputFolder, name);
+                var imageFormat = Path.GetExtension(name) == ".jpg" ? System.Drawing.Imaging.ImageFormat.Jpeg : System.Drawing.Imaging.ImageFormat.Png;
                 RaiseMessage($"GLTFExporter.Texture | write image '{name}' to '{absolutePath}'", 1);
                 RaiseMessage($"GLTFExporter.Texture | write image '{name}' to '{absolutePath}'", 1);
-                bitmap.Save(absolutePath);
+                bitmap.Save(absolutePath, imageFormat);
             }
             }
 
 
             return ExportTexture(babylonTexture, gltf, name);
             return ExportTexture(babylonTexture, gltf, name);
@@ -76,6 +77,15 @@ namespace Max2Babylon
 
 
             gltfImage.index = gltf.ImagesList.Count;
             gltfImage.index = gltf.ImagesList.Count;
             gltf.ImagesList.Add(gltfImage);
             gltf.ImagesList.Add(gltfImage);
+            switch (Path.GetExtension(name))
+            {
+                case ".jpg":
+                    gltfImage.FileExtension = "jpeg";
+                    break;
+                case ".png":
+                    gltfImage.FileExtension = "png";
+                    break;
+            }
 
 
 
 
             // --------------------------
             // --------------------------

+ 74 - 4
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.GLTFExporter.cs

@@ -3,6 +3,7 @@ using GLTFExport.Entities;
 using Newtonsoft.Json;
 using Newtonsoft.Json;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Drawing;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Text;
 using System.Text;
@@ -16,7 +17,7 @@ namespace Max2Babylon
 
 
         private List<BabylonNode> babylonNodes;
         private List<BabylonNode> babylonNodes;
 
 
-        public void ExportGltf(BabylonScene babylonScene, string outputFile, bool generateBinary)
+        public void ExportGltf(BabylonScene babylonScene, string outputFile, bool generateBinary, bool exportGltfImagesAsBinary)
         {
         {
             RaiseMessage("GLTFExporter | Export outputFile=" + outputFile + " generateBinary=" + generateBinary);
             RaiseMessage("GLTFExporter | Export outputFile=" + outputFile + " generateBinary=" + generateBinary);
             RaiseMessage("GLTFExporter | Exportation started", Color.Blue);
             RaiseMessage("GLTFExporter | Exportation started", Color.Blue);
@@ -29,7 +30,7 @@ namespace Max2Babylon
             initBabylonNodes(babylonScene);
             initBabylonNodes(babylonScene);
             babylonMaterialsToExport = new List<BabylonMaterial>();
             babylonMaterialsToExport = new List<BabylonMaterial>();
 
 
-            var gltf = new GLTF(Path.GetDirectoryName(outputFile));
+            var gltf = new GLTF(outputFile);
 
 
             // Asset
             // Asset
             gltf.asset = new GLTFAsset
             gltf.asset = new GLTFAsset
@@ -96,11 +97,18 @@ namespace Max2Babylon
                 CheckCancelled();
                 CheckCancelled();
             };
             };
             RaiseMessage(string.Format("GLTFExporter | Nb materials exported: {0}", gltf.MaterialsList.Count), Color.Gray, 1);
             RaiseMessage(string.Format("GLTFExporter | Nb materials exported: {0}", gltf.MaterialsList.Count), Color.Gray, 1);
+            
+            if (exportGltfImagesAsBinary)
+            {
+                SwitchImagesFromUriToBinary(gltf);
+            }
 
 
-            // Output
-            RaiseMessage("GLTFExporter | Saving to output file");
             // Cast lists to arrays
             // Cast lists to arrays
             gltf.Prepare();
             gltf.Prepare();
+
+            // Output
+            RaiseMessage("GLTFExporter | Saving to output file");
+            
             string outputGltfFile = Path.ChangeExtension(outputFile, "gltf");
             string outputGltfFile = Path.ChangeExtension(outputFile, "gltf");
             File.WriteAllText(outputGltfFile, gltfToJson(gltf));
             File.WriteAllText(outputGltfFile, gltfToJson(gltf));
 
 
@@ -137,6 +145,17 @@ namespace Max2Babylon
                 {
                 {
                     gltfBuffer.uri = null;
                     gltfBuffer.uri = null;
                 }
                 }
+                // Switch images to binary if not already done
+                // TODO - make it optional
+                if (!exportGltfImagesAsBinary)
+                {
+                    var imageBufferViews = SwitchImagesFromUriToBinary(gltf);
+                    imageBufferViews.ForEach(imageBufferView =>
+                    {
+                        imageBufferView.Buffer.bytesList.AddRange(imageBufferView.bytesList);
+                    });
+                }
+                gltf.Prepare();
                 // Serialize gltf data to JSON string then convert it to bytes
                 // Serialize gltf data to JSON string then convert it to bytes
                 byte[] chunkDataJson = Encoding.ASCII.GetBytes(gltfToJson(gltf));
                 byte[] chunkDataJson = Encoding.ASCII.GetBytes(gltfToJson(gltf));
                 // JSON chunk must be padded with trailing Space chars (0x20) to satisfy alignment requirements 
                 // JSON chunk must be padded with trailing Space chars (0x20) to satisfy alignment requirements 
@@ -317,5 +336,56 @@ namespace Max2Babylon
             }
             }
             return sb.ToString();
             return sb.ToString();
         }
         }
+
+        private List<GLTFBufferView> SwitchImagesFromUriToBinary(GLTF gltf)
+        {
+            var imageBufferViews = new List<GLTFBufferView>();
+
+            foreach (GLTFImage gltfImage in gltf.ImagesList)
+            {
+                var path = Path.Combine(gltf.OutputFolder, gltfImage.uri);
+                using (Image image = Image.FromFile(path))
+                {
+                    using (MemoryStream m = new MemoryStream())
+                    {
+                        var imageFormat = gltfImage.FileExtension == "jpeg" ? System.Drawing.Imaging.ImageFormat.Jpeg : System.Drawing.Imaging.ImageFormat.Png;
+                        image.Save(m, imageFormat);
+                        byte[] imageBytes = m.ToArray();
+
+                        // JSON chunk must be padded with trailing Space chars (0x20) to satisfy alignment requirements 
+                        var nbSpaceToAdd = imageBytes.Length % 4 == 0 ? 0 : (4 - imageBytes.Length % 4);
+                        var imageBytesList = new List<byte>(imageBytes);
+                        for (int i = 0; i < nbSpaceToAdd; i++)
+                        {
+                            imageBytesList.Add(0x00);
+                        }
+                        imageBytes = imageBytesList.ToArray();
+
+                        // BufferView - Image
+                        var buffer = gltf.buffer;
+                        var bufferViewImage = new GLTFBufferView
+                        {
+                            name = "bufferViewImage",
+                            buffer = buffer.index,
+                            Buffer = buffer,
+                            byteOffset = buffer.byteLength
+                        };
+                        bufferViewImage.index = gltf.BufferViewsList.Count;
+                        gltf.BufferViewsList.Add(bufferViewImage);
+                        imageBufferViews.Add(bufferViewImage);
+
+
+                        gltfImage.uri = null;
+                        gltfImage.bufferView = bufferViewImage.index;
+                        gltfImage.mimeType = "image/" + gltfImage.FileExtension;
+
+                        bufferViewImage.bytesList.AddRange(imageBytes);
+                        bufferViewImage.byteLength += imageBytes.Length;
+                        bufferViewImage.Buffer.byteLength += imageBytes.Length;
+                    }
+                }
+            }
+            return imageBufferViews;
+        }
     }
     }
 }
 }

+ 2 - 2
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.cs

@@ -77,7 +77,7 @@ namespace Max2Babylon
             }
             }
         }
         }
 
 
-        public async Task ExportAsync(string outputFile, bool generateManifest, bool onlySelected, bool generateBinary, bool exportGltf, Form callerForm)
+        public async Task ExportAsync(string outputFile, bool generateManifest, bool onlySelected, bool generateBinary, bool exportGltf, bool exportGltfImagesAsBinary, Form callerForm)
         {
         {
             var gameConversionManger = Loader.Global.ConversionManager;
             var gameConversionManger = Loader.Global.ConversionManager;
             gameConversionManger.CoordSystem = Autodesk.Max.IGameConversionManager.CoordSystem.D3d;
             gameConversionManger.CoordSystem = Autodesk.Max.IGameConversionManager.CoordSystem.D3d;
@@ -307,7 +307,7 @@ namespace Max2Babylon
             // Export glTF
             // Export glTF
             if (exportGltf)
             if (exportGltf)
             {
             {
-                ExportGltf(babylonScene, outputFile, generateBinary);
+                ExportGltf(babylonScene, outputFile, generateBinary, exportGltfImagesAsBinary);
             }
             }
 
 
             watch.Stop();
             watch.Stop();

+ 15 - 0
Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.Designer.cs

@@ -48,6 +48,7 @@
             this.butExportAndRun = new System.Windows.Forms.Button();
             this.butExportAndRun = new System.Windows.Forms.Button();
             this.butClose = new System.Windows.Forms.Button();
             this.butClose = new System.Windows.Forms.Button();
             this.chkGltf = new System.Windows.Forms.CheckBox();
             this.chkGltf = new System.Windows.Forms.CheckBox();
+            this.chkGltfImagesAsBinary = new System.Windows.Forms.CheckBox();
             ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit();
             ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit();
             this.groupBox1.SuspendLayout();
             this.groupBox1.SuspendLayout();
             this.SuspendLayout();
             this.SuspendLayout();
@@ -180,6 +181,7 @@
             // 
             // 
             this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
             | System.Windows.Forms.AnchorStyles.Right)));
+            this.groupBox1.Controls.Add(this.chkGltfImagesAsBinary);
             this.groupBox1.Controls.Add(this.chkBinary);
             this.groupBox1.Controls.Add(this.chkBinary);
             this.groupBox1.Controls.Add(this.chkOnlySelected);
             this.groupBox1.Controls.Add(this.chkOnlySelected);
             this.groupBox1.Controls.Add(this.chkAutoSave);
             this.groupBox1.Controls.Add(this.chkAutoSave);
@@ -277,6 +279,18 @@
             this.chkGltf.UseVisualStyleBackColor = true;
             this.chkGltf.UseVisualStyleBackColor = true;
             this.chkGltf.CheckedChanged += new System.EventHandler(this.chkGltf_CheckedChanged);
             this.chkGltf.CheckedChanged += new System.EventHandler(this.chkGltf_CheckedChanged);
             // 
             // 
+            // chkGltfImageAsBinary
+            // 
+            this.chkGltfImagesAsBinary.AutoSize = true;
+            this.chkGltfImagesAsBinary.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.chkGltfImagesAsBinary.Location = new System.Drawing.Point(166, 127);
+            this.chkGltfImagesAsBinary.Name = "chkGltfImagesAsBinary";
+            this.chkGltfImagesAsBinary.Size = new System.Drawing.Size(158, 17);
+            this.chkGltfImagesAsBinary.TabIndex = 18;
+            this.chkGltfImagesAsBinary.Text = "Export glTF images as binary";
+            this.chkGltfImagesAsBinary.UseVisualStyleBackColor = true;
+            this.chkGltfImagesAsBinary.CheckedChanged += new System.EventHandler(this.checkGltfImagesAsBinary_CheckedChanged);
+            // 
             // ExporterForm
             // ExporterForm
             // 
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -330,5 +344,6 @@
         private System.Windows.Forms.Button butClose;
         private System.Windows.Forms.Button butClose;
         private System.Windows.Forms.CheckBox chkBinary;
         private System.Windows.Forms.CheckBox chkBinary;
         private System.Windows.Forms.CheckBox chkGltf;
         private System.Windows.Forms.CheckBox chkGltf;
+        private System.Windows.Forms.CheckBox chkGltfImagesAsBinary;
     }
     }
 }
 }

+ 8 - 1
Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.cs

@@ -33,6 +33,7 @@ namespace Max2Babylon
             Tools.PrepareCheckBox(chkOnlySelected, Loader.Core.RootNode, "babylonjs_onlySelected");
             Tools.PrepareCheckBox(chkOnlySelected, Loader.Core.RootNode, "babylonjs_onlySelected");
             Tools.PrepareCheckBox(chkBinary, Loader.Core.RootNode, "babylonjs_binary");
             Tools.PrepareCheckBox(chkBinary, Loader.Core.RootNode, "babylonjs_binary");
             Tools.PrepareCheckBox(chkGltf, Loader.Core.RootNode, "babylonjs_exportGltf");
             Tools.PrepareCheckBox(chkGltf, Loader.Core.RootNode, "babylonjs_exportGltf");
+            Tools.PrepareCheckBox(chkGltfImagesAsBinary, Loader.Core.RootNode, "babylonjs_exportGltfImagesAsBinary");
         }
         }
 
 
         private void butBrowse_Click(object sender, EventArgs e)
         private void butBrowse_Click(object sender, EventArgs e)
@@ -57,6 +58,7 @@ namespace Max2Babylon
             Tools.UpdateCheckBox(chkOnlySelected, Loader.Core.RootNode, "babylonjs_onlySelected");
             Tools.UpdateCheckBox(chkOnlySelected, Loader.Core.RootNode, "babylonjs_onlySelected");
             Tools.UpdateCheckBox(chkBinary, Loader.Core.RootNode, "babylonjs_binary");
             Tools.UpdateCheckBox(chkBinary, Loader.Core.RootNode, "babylonjs_binary");
             Tools.UpdateCheckBox(chkGltf, Loader.Core.RootNode, "babylonjs_exportGltf");
             Tools.UpdateCheckBox(chkGltf, Loader.Core.RootNode, "babylonjs_exportGltf");
+            Tools.UpdateCheckBox(chkGltfImagesAsBinary, Loader.Core.RootNode, "babylonjs_exportGltfImagesAsBinary");
 
 
             Loader.Core.RootNode.SetLocalData(txtFilename.Text);
             Loader.Core.RootNode.SetLocalData(txtFilename.Text);
 
 
@@ -123,7 +125,7 @@ namespace Max2Babylon
                 exporter.AutoSave3dsMaxFile = chkAutoSave.Checked;
                 exporter.AutoSave3dsMaxFile = chkAutoSave.Checked;
                 exporter.ExportHiddenObjects = chkHidden.Checked;
                 exporter.ExportHiddenObjects = chkHidden.Checked;
                 exporter.CopyTexturesToOutput = chkCopyTextures.Checked;
                 exporter.CopyTexturesToOutput = chkCopyTextures.Checked;
-                await exporter.ExportAsync(txtFilename.Text, chkManifest.Checked, chkOnlySelected.Checked, chkBinary.Checked, chkGltf.Checked, this);
+                await exporter.ExportAsync(txtFilename.Text, chkManifest.Checked, chkOnlySelected.Checked, chkBinary.Checked, chkGltf.Checked, chkGltfImagesAsBinary.Checked, this);
             }
             }
             catch (OperationCanceledException)
             catch (OperationCanceledException)
             {
             {
@@ -232,5 +234,10 @@ namespace Max2Babylon
         {
         {
 
 
         }
         }
+
+        private void checkGltfImagesAsBinary_CheckedChanged(object sender, EventArgs e)
+        {
+
+        }
     }
     }
 }
 }