1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
use std::process::Command;

static EXPORT_BLENDER_DATA: &'static str = r#"
import bpy

bpy.context.view_layer.objects.active = None

# Get the objects at the beginning so that we don't iterate over new ones that we
# generate such as ik-to-fk converted rigs
objects = list(bpy.context.scene.objects)
for obj in objects:
    bpy.context.view_layer.objects.active = obj
    if obj.type == 'MESH':
      bpy.ops.import_export.mesh2json()
    if obj.type == 'ARMATURE':
      bpy.ops.rigging.iktofk()
      bpy.ops.import_export.armature2json()
"#;

/// Write the meshes and armatures from a vector of Blender filenames to stdout.
///
/// You'll typically use something like
///
/// ```ignore
///     blender_mesh::parse_meshes_from_blender_stdout
///     blender_armature::parse_meshes_from_blender_stdout
/// ```
///
/// to parse the exported data into the data structures that you need.
///
/// TODO: Integration test this
pub fn export_blender_data(blender_files: &Vec<String>) -> Result<String, String> {
    let mut blender_process = Command::new("blender");
    let blender_process = blender_process.arg("--background");

    for blender_file in blender_files {
        blender_process
            .arg("-noaudio")
            .args(&["--python-expr", &open_blender_file(blender_file)])
            .args(&["--python-expr", &EXPORT_BLENDER_DATA]);
    }

    let output = blender_process.output().unwrap();

    if output.stderr.len() > 0 {
        return Err(String::from_utf8(output.stderr).expect("Blender stderr"));
    }

    Ok(String::from_utf8(output.stdout).expect("Blender stdout"))
}

fn open_blender_file(file: &str) -> String {
    format!(
        r#"
import bpy
bpy.ops.wm.open_mainfile(filepath="{}")
"#,
        file
    )
}