Home > Net >  How to edit gltf data in the gltf rust crate?
How to edit gltf data in the gltf rust crate?

Time:07-17

I am learning rust by writing a terminal utility I need.

This tool needs to take a gltf file and apply a linear transformation to the nodes and to the buffers.

Right now I am trying to scale down the inverse bind matrix data.

To that effect I am using this crate:
https://docs.rs/gltf/latest/gltf/

With the data loaded I am trying reach the binary data like this:

use gltf::Gltf;

fn main() -> Result<(), gltf::Error>
{
    let gltf = Gltf::open("Assets/werewolf_animated.gltf")?;

    for skin in gltf.skins() {
        for joint in skin.joints()
        {
            println!(
                "Skin #{} has {} joint",
                skin.index(),
                joint.index(),
            );
        }

        for ibm_accessor in skin.inverse_bind_matrices()
        {
            println!("Accessor #{}", ibm_accessor.index());
            println!("offset: {}", ibm_accessor.offset());
            println!("count: {}", ibm_accessor.count());
            println!("data_type: {:?}", ibm_accessor.data_type());
            println!("dimensions: {:?}", ibm_accessor.dimensions());

            println!("buffer: {:?}", ibm_accessor.view().unwrap().buffer().source());
      
        }
    }

    Ok(())
}

If this were C or C at this stage I would have a float* and I would be able to start reading and writing to the binary data, treating it as a float array. But this is rust.

Any suggestions as to how I can start tampering with the data under ibm_accessor.view().unwrap().buffer().source()?

CodePudding user response:

Disclaimer: I have never used the glTF data structure or the glft crate before. However, I found this article, as well as this documentation suggesting that Source data may be encoded as Base64.

Note: We will need to use the data_url helper library

According to the documentation, Source is an enum that has two variants: Bin and Uri(&'a str).

The idiomatic way of handling the enum would be to match on it:

// In the import section
use data_url::DataUrl;

// In your code
let source = ibm_accessor.view().unwrap().buffer().source();

match source {
    Bin => {
        // This case seems to only apply to .glb (binary glTF) files.
        // If it doesn't apply, maybe skip it?
        continue;
    },
    Uri(data) => {
        // Here we can handle the data which is of type `&str`
        // As mentioned in the linked documentation, this is
        // probably (?) a `data:` Base64 encoded URI, something like
        // data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=
        // Mozilla/SimonSapin (well-known Rust dev) wrote this library
        // to handle `data:` URIs: https://docs.rs/data-url/latest/data_url/
        // let's use that.
        let url = match DataUrl::process(data) {
            Ok(url) => url,
            Err(_) => {
                // This is probably not a `data` URI? Maybe it's a https:// link or something? 
                continue;
            }
        };
        
        let (body, _fragment) = match url.decode_to_vec() {
            Ok(res) => res,
            Err(_) => {
                // The base64 was malformed!
                panic!("Or handle this error somehow?");
            }
        };
        // `body` is now a `Vec<u8>` (vector of bytes) which you can loop over.
    }
}

data_url Proof-Of-Concept

Just to prove to myself what the data_url crate does, I did this small POC

Carto.toml

[package]
name = "data-url-test"
version = "0.1.0"
edition = "2021"

[dependencies]
data-url = "0.1.1"

main.rs

use data_url::{DataUrl, mime};

fn main() {
    // URL is from https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md
    let data = "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=";
    let url = match DataUrl::process(data) {
        Ok(url) => url,
        Err(e) => {
            eprintln!("Error {:#?}", e);
            return;
        }
    };
    let (body, _fragment) = match url.decode_to_vec() {
        Ok(res) => res,
        Err(_) => {
            // The base64 was malformed!
            panic!("Or handle this error somehow?");
        }
    };
    println!("body {:?}", body);
}
  • Related