4 releases (2 breaking)

0.8.0 Apr 20, 2023
0.7.0 Apr 17, 2023
0.6.1 Sep 20, 2019
0.6.0 Sep 20, 2019

#944 in Parser implementations

Download history 2/week @ 2024-07-22 15/week @ 2024-07-29 8/week @ 2024-08-05 4/week @ 2024-08-12 13/week @ 2024-08-19 27/week @ 2024-08-26 9/week @ 2024-09-02 13/week @ 2024-09-09 5/week @ 2024-09-16 52/week @ 2024-09-23 24/week @ 2024-09-30 4/week @ 2024-10-07 22/week @ 2024-10-14 15/week @ 2024-10-21 2/week @ 2024-10-28 28/week @ 2024-11-04

67 downloads per month
Used in bevy_blender

MIT license

1.5MB
2K SLoC

Blend - A crate for parsing .blend files from Blender

Documentation

Example

use blend::Blend;

/// Prints the name and position of every object
fn main() {
    let blend = Blend::from_path("examples/blend_files/3_5.blend").expect("error loading blend file");

    for obj in blend.instances_with_code(*b"OB") {
        let loc = obj.get_f32_vec("loc");
        let name = obj.get("id").get_string("name");

        println!("\"{}\" at {:?}", name, loc);
    }
}

The .blend file

To use this crate to its full extent it is useful to understand how the .blend file works internally. A simplified overview: Blender creates the save file by dumping its memory to the disk, this means that a .blend file is a list of C-like structs which can contain primitives, arrays, pointers and other structs. The following is how a Camera is defined in Blender's memory (in Rust-like syntax):

struct Camera {
    id: ID {
        name: [u8; 66] = "CACamera"
        //[... other ommited properties ...]
    },
    adt: *AnimData = null,
    type: u8 = 0,
    dtx: u8 = 0,
    flag: f32 = 4,
    passepartalpha: f32 = 0.5,
    clipsta: f32 = 0.1,
    clipend: f32 = 100,
    lens: f32 = 50,
    ortho_scale: f32 = 7.3142858,
    drawsize: f32 = 1,
    sensor_x: f32 = 36,
    sensor_y: f32 = 24,
    shiftx: f32 = 0,
    shifty: f32 = 0,
    YF_dofdist: f32 = 0,
    ipo: *Ipo = null,
    dof_ob: *Object = null,
    //[... other ommited properties ...]
}

Other concepts are explained in the docs for methods where knowing these concepts is necessary.

Learn more

Documentation on the .blend file is a bit sparse, but the most common source is the Mystery of the Blend and a personal recommendation is to get it from the official Blender repository and apply the following patch.

This crate

This crate provides a parser and a runtime for these structures which means you can access them as if they were simple objects in memory. The aim here is to abstract most of the noise into a single coherent interface. For example: you can access a struct through both a non-primitive value field and a non-null pointer field using the same method call (Instance::get). Other abstractions are provided where possible: the .blend file has at least 3 ways of defining lists of things, this crate provides a single method that unifies all of those.

This crate is also lazy. While some work has to be done upfront to find the structs and their type definitions, the binary data of the blocks is not parsed until you actually access it.

Usage tips

Knowing what to read from the file can be a bit of a challenge. A simple .blend file has over 400 "blocks" and each can represent one or more structs. If you don't know what you want to access exactly the print_blend example can be helpful. You can use it to save an entire .blend file as text to disk. You can also print single struct instances if you know somewhat what you need.

It's also important to note that when printing an Instance if one of their properties is a list, elements other than the first are skipped. If you need to see the entire list simply access it and print its members individually.

The Display implementation for Instance is a bit unpolished so larger .blend files might cause a stack overflow but that can be fixed by running the code in release mode. If you find something that breaks formatting please open an issue.

Supported versions

As the .blend file is self-describing it should possible to parse files from every Blender version (tests were done on files from version 2.72 to 2.80). Some things are assumed to always be true though: the type int for example is always considered equivalent to Rust's i32 but there is nothing in the file specification that guarantees this. There is very little reason to believe Blender would change its primitive types though.

Warnings

This crate is meant to be used with trusted .blend files, while no unsafety or undefined behaviour is expected from reading a malicious file, it is probably possible to craft a .blend file that causes a panic at runtime.

Due to some quirks of the specification, parts of the library are a bit liberal in what kind of conversions it allows. For example: any block with the correct size can be parsed as a list of floats. Why? Because some blocks are actual arrays of floats but we don't have enough type information to be sure of this. This means it is up to the user to decide what they want when accessing the data.

This crate is also somewhat panic happy. While it should be always possible to check if the field you are accessing exists, is valid, contains a particular type of data, etc, you are meant to know what you are accessing ahead of time so almost none of the functions will return a Result::Err or Option::None on bad inputs.

Finally, this was developed to facilitate game development but should be useful for any use case.

Limitations

This crate does not support compressed .blend files and it also does not support writing .blend files. To solve the first you can uncompress the file before passing the data to blend::Blend::from_data see the print_blend example to see how. The second one is a bit harder due to the way the code is organized, but PRs are welcome!

GLOB, REND and TEST blocks are not fully supported. Parts of the code already supports these blocks but they are not fully implemented as I haven't found a use-case for them. Open an issue if you would like support for these!

Dependencies