De-Duplicate textures when creating GLB

I have a scene which re-uses the same texture file for multiple meshes in the scene. I create only 1 instance of the texture, and point multiple nodes / meshes at it; but the more instances of the texture I apply, the (linearly) bigger the file gets. Is there a way to signal to Aspose that you want the same texture instance to be re-used between nodes so that the GLB only contains 1 instance of the texture data?

@OBCad

Would you kindly share your sample source file(s) along with complete sample code snippet. We will test the scenario in our environment and address it accordingly.

Here is the code that verifies that multiple uses of the same texture leads to multiple copies of that texture emitted in a GLB. It creates 2 scenes in parallel, adding an identical mesh to each scene 4 times, and saving the intermediary results. The second scene gets the same texture/material applied to each node, and shows how every time I add a mesh which references the same material, that material’s texture gets duplicated in the resulting file.

using System;
using Aspose.ThreeD;
using Aspose.ThreeD.Utilities;
using Aspose.ThreeD.Entities;
using System.Drawing;
using Aspose.ThreeD.Shading;
using System.IO;
namespace Aspose3dTest
{
  class Program
  {
    static void Main(string[] args)
    {
      //Console.WriteLine("Setting license");
      Aspose.ThreeD.License license = new Aspose.ThreeD.License();
      license.SetLicense("Aspose.3D.lic");
      System.Reflection.Assembly asm = System.Reflection.Assembly.GetAssembly(typeof(Aspose.ThreeD.Scene));
      Console.WriteLine("Aspose.3d Version: " + asm.FullName);
      asm = System.Reflection.Assembly.GetAssembly(typeof(System.Int32));
      Console.WriteLine(".Net Core Version: " + asm.FullName);
      //create the texture image
      int textureSize = 500;
      System.Drawing.Bitmap textureBitmap = new Bitmap(textureSize, textureSize);
      Random rnd = new Random();
      for (int x = 0; x < textureSize; x++)
        for (int y = 0; y < textureSize; y++)
          textureBitmap.SetPixel(x, y, Color.FromArgb(rnd.Next()));
      byte[] textureData = default(byte[]);
      using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
      {
        textureBitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
        textureData = ms.ToArray();
      }
      System.IO.File.WriteAllBytes("TheTexture.jpg", textureData);
      int textureFileSize = (int)new FileInfo("TheTexture.jpg").Length;
      Console.WriteLine($"Size of texture in bytes: {textureFileSize}");
      var texture = new Texture("TheTexture")
      {
        Content = textureData,
        FileName = "TheTexture.jpg"
      };
      Scene sceneNoTexture = new Scene();
      Scene sceneWithTexture = new Scene();
      var GLTFSaveOptions = new Aspose.ThreeD.Formats.GLTFSaveOptions(Aspose.ThreeD.FileContentType.Binary)
      {
        EmbedAssets = true,
        SaveExtras = true
      };

      var material = new Aspose.ThreeD.Shading.PbrMaterial(System.Drawing.Color.White);
      material.SetTexture(Aspose.ThreeD.Shading.Material.MapDiffuse, texture);
      Console.WriteLine("Meshes\t!Text\tText\tDelta\tImageCopiesInFile");
      for (int meshCount = 1; meshCount < 5; meshCount++)
      {
        Console.Write($"{meshCount}\t");
        Vector3 randomTranslation = new Vector3(rnd.NextDouble() * 20, rnd.NextDouble() * 20, rnd.NextDouble() * 20);
        var node = sceneNoTexture.RootNode.CreateChildNode(meshCount.ToString());
        node.Transform.Translation = randomTranslation;
        node.Entity = MakeBoxMesh();
        var noTextureFileName = $"noTexture{meshCount}.glb";
        sceneNoTexture.Save(noTextureFileName, GLTFSaveOptions);
        int noTextureSize = (int)new FileInfo(noTextureFileName).Length;
        Console.Write($"{noTextureSize}\t");
        node = sceneWithTexture.RootNode.CreateChildNode(meshCount.ToString());
        node.Transform.Translation = randomTranslation;
        node.Entity = MakeBoxMesh();
        node.Material = material;
        var withTextureFileName = $"withTexture{meshCount}.glb";
        sceneWithTexture.Save(withTextureFileName, GLTFSaveOptions);
        int withTextureSize = (int)new FileInfo(withTextureFileName).Length;
        Console.Write($"{withTextureSize}\t");
        int delta = withTextureSize - noTextureSize;
        int imageCopiesInFile = delta / textureFileSize;
        Console.WriteLine($"{delta}\t{imageCopiesInFile}");
      }
    }

    private static Mesh MakeBoxMesh()
    {
      var box = new Mesh();
      var controlPoints = box.ControlPoints;
      controlPoints.Add(new Vector4(-5.0, 0.0, 5.0, 1.0));
      controlPoints.Add(new Vector4(5.0, 0.0, 5.0, 1.0));
      controlPoints.Add(new Vector4(5.0, 10.0, 5.0, 1.0));
      controlPoints.Add(new Vector4(-5.0, 10.0, 5.0, 1.0));
      controlPoints.Add(new Vector4(-5.0, 0.0, -5.0, 1.0));
      controlPoints.Add(new Vector4(5.0, 0.0, -5.0, 1.0));
      controlPoints.Add(new Vector4(5.0, 10.0, -5.0, 1.0));
      controlPoints.Add(new Vector4(-5.0, 10.0, -5.0, 1.0));
      box.CreatePolygon(0, 1, 2, 3);
      box.CreatePolygon(1, 5, 6, 2);
      box.CreatePolygon(5, 4, 7, 6);
      box.CreatePolygon(4, 0, 3, 7);
      box.CreatePolygon(0, 4, 5, 1);
      box.CreatePolygon(3, 2, 6, 7);
      box.VertexElements.Add(PolygonModifier.GenerateNormal(box));
      box.VertexElements.Add(PolygonModifier.GenerateUV(box));
      return box;
    }
  }
}

And here is the output:

Aspose.3d Version: Aspose.3D, Version=20.6.0.0, Culture=neutral, PublicKeyToken=f071c641d0b4582b
.Net Core Version: System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e


Size of texture in bytes: 152943
Meshes	!Text	Text	Delta	ImageCopiesInFile
1		2116	155588	153472	1
2		3936	310824	306888	2
3		5756	466056	460300	3
4		7580	621296	613716	4

@OBCad

We have logged an investigation ticket as THREEDNET-810 in our issue tracking system for the sake of further analysis. We will look into its details and keep you posted with the status of ticket resolution. Please be patient and give us some time.

We are sorry for the inconvenience.

@OBCad

We would like to share with you that the issue has been resolved in Aspose.3D for .NET 21.1.1 hotfix.

Great, thanks so much! I just verified that the hotfix does work. Is there a way to access this hotfix via nuget? If not, when will it be rolled into the next nuget package?

@OBCad

Thanks for your feedback.

The hotfix is not uploaded over NuGet however, this fix will be a part of 21.2v of the API which will be released next month (Feb 2021).