vpenades / SharpGLTF

glTF reader and writer for .NET Standard
MIT License
470 stars 75 forks source link

Image Name null but extension present #27

Closed nodermatt closed 4 years ago

nodermatt commented 4 years ago

When I load the Avocado gltf from Khronos Group I can see all three images that are referenced in the model. However, the image Name attribute is null, even though the file extension is correct. Is this intended?

image

https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Avocado/glTF

vpenades commented 4 years ago

Hi

Image.Name is just an optional name you can give to the glTF object, it is NOT the path of the image.

I guess what you want is to get the texture file of the Image, to do that you need to call: Image.GetImageContent(); that will give you an Byte Array segment containing the compressed image. With it you can initialize a MemoryStream to read the it.

nodermatt commented 4 years ago

Thanks @vpenades I saw the GetImageContent method but I was more interested in the actual filename (for now) If it is optional, Can I set the Name of the Image object to the path of the texture?

I am trying something like this:

foreach (var image in model.LogicalImages)
            {
               // direct access
                Console.WriteLine("- " + image.FileExtension);
                // via model (both point to same Image object)
                Console.WriteLine(model.LogicalImages[image.LogicalIndex].Name);
            }
  "images": [
    {
      "uri": "Avocado_baseColor.png"
    },
    {
      "uri": "Avocado_roughnessMetallic.png"
    },
    {
      "uri": "Avocado_normal.png"
    }
  ]
vpenades commented 4 years ago

Keep in mind that glTF supports three different ways of storing images:

In particular, GLB files don't use URIs at all, which makes things a bit weird, because a glTF file that is converted to GLB will loose the URI references.

This made me think that keeping the URIs around would mislead developers from using GetImageContent() API call, which is the right and safe way to load an image, and works for all cases.

If you need the URI name for something else, I could reintroduce the URI property, but I would use a different property name to explicitly state that it is for informational purposes and it should not be used to load the image, something like: "InformationalUriPath"

Can I set the Name of the Image object to the path of the texture?

Yes, the Name property is inherited from LogicalChildOfRoot and it is also available in many glTF objects. and it's used only to.... give names to these objects. Setting the Image.Name as the path of the texture can be useful in cases where you convert a glTF to GLB, because in these cases, the URI will be lost, but the Name will be kept.

vpenades commented 4 years ago

@nodermatt I am considering adding a static API to query for all the external files used by a given glTF file, the method would look like this:

static IEnumerable<String> GetExternalFiles(String gltfFilePath);

This method would return absolute paths for both .BIN and texture files from a given glTF file.

Such a method would suit your needs?

nodermatt commented 4 years ago

@vpenades Thanks for the suggestion, I think that would be neat. Note that I merely used the library for some early experiments with glTF and didn't need it anymore lately. I wouldn't want you to go through a hassle to implement this feature if I don't use it anymore.

vpenades commented 4 years ago

No worries, I also need it for something else.

vpenades commented 4 years ago

I've added a string[] GetSatellitePaths(string filePath) to retrieve the file dependencies (both binary blobs and texture images)

You can see the core here.

ptasev commented 4 years ago

I'm writing a converter from gltf to a custom format. I need a way to get the URI if it is a file name. Is there an API to do that currently? I can't use GetSatellitePaths since I don't know which gltf Image those paths belong to.

vpenades commented 4 years ago

There's no direct way of getting the image URIs when they're files.

This is by design, because there's three different ways of storing images within a glTF:

So for convenience, I wanted to expose a single API to retrieve the images, regardless of how they were originally encoded.

If what you need is to write the images back to files (disregarding how they were named originally), you can do this:

var model = ModelRoot.Load(gltfFile);

var index = 0; // we will retrieve the first image

// this object holds the original image as a byte array.
var image = model.LogicalImages[ index ].MemoryImage;

// set the name of the file to save, using the provided extension.
var imagePath = System.IO.Path.ChangeExtension($"image-{index}", image.FileExtension);

// save the image bytes back to a file.
System.IO.File.WriteAllBytes(imagePath, image.GetBuffer().ToArray());

This way of getting the images is convenient because it works in all cases, regardless of how the image was originally stored.