blend_rs/blend/
mod.rs

1use std::str::Utf8Error;
2
3use blend_inspect_rs::Address;
4
5pub use blend_inspect_rs::{Version, Endianness};
6pub use reader::{read, Reader, ReadError, StructIter};
7
8pub mod traverse;
9
10mod reader;
11
12pub trait GeneratedBlendStruct : Sized {
13    const BLEND_VERSION: Version;
14    const BLEND_POINTER_SIZE: usize;
15    const BLEND_ENDIANNESS: Endianness;
16    const STRUCT_NAME: &'static str;
17    const STRUCT_INDEX: usize;
18    const STRUCT_TYPE_INDEX: usize;
19    const IS_SYNTHETIC: bool;
20}
21
22pub trait PointerLike<T> : Sized
23where T: PointerTarget<T> {
24
25    #[feature(generic_associated_types)] //TODO: Switch to stable rust as soon as GATs are released.
26    type Pointer<A: PointerTarget<A>>: PointerLike<A>;
27
28    fn as_instance_of<B: PointerTarget<B>>(&self) -> Self::Pointer<B>;
29
30    fn address(&self) -> Option<Address>;
31
32    fn is_valid(&self) -> bool;
33
34    fn is_invalid(&self) -> bool {
35        !self.is_valid()
36    }
37}
38
39pub trait PointerTarget<T> : Sized {
40
41}
42
43impl PointerTarget<i8> for i8 {}
44impl PointerTarget<u8> for u8 {}
45impl PointerTarget<i16> for i16 {}
46impl PointerTarget<u16> for u16 {}
47impl PointerTarget<i32> for i32 {}
48impl PointerTarget<i64> for i64 {}
49impl PointerTarget<u64> for u64 {}
50impl PointerTarget<u32> for u32 {}
51impl PointerTarget<f32> for f32 {}
52impl PointerTarget<f64> for f64 {}
53
54pub trait StringLike {
55
56    fn to_str(&self) -> Result<&str, Utf8Error>;
57
58    fn to_str_unchecked(&self) -> &str {
59        self.to_str().expect("Failed to extract &str!")
60    }
61
62    fn to_string(&self) -> Result<String, Utf8Error> {
63        self.to_str().map(|value| String::from(value))
64    }
65
66    fn to_string_unchecked(&self) -> String {
67        self.to_string().expect("Failed to extract String!")
68    }
69}
70
71impl <A> StringLike for A
72where A: AsRef<[i8]> {
73
74    fn to_str(&self) -> Result<&str, Utf8Error> {
75        let self_ref = self.as_ref();
76        if !self_ref.is_empty() {
77            let slice: &[u8] = unsafe {
78                core::slice::from_raw_parts(self_ref.as_ptr() as *const u8, self_ref.len())
79            };
80            let null = slice.iter()
81                .position(|element| *element == 0x00)
82                .unwrap_or(slice.len());
83            std::str::from_utf8(&slice[0..null])
84        }
85        else {
86            Ok("")
87        }
88    }
89}
90
91pub trait NameLike {
92
93    const NAME_PREFIXES: [&'static str; 17] = [
94        "OB", "ME", "WM", "IM", "SN",
95        "WS", "BR", "SC", "PL", "OB",
96        "GR", "CA", "LA", "ME", "WO",
97        "LS", "MA",
98    ];
99
100    fn to_name_str(&self) -> Result<&str, Utf8Error>;
101
102    fn to_name_string(&self) -> Result<String, Utf8Error> {
103        self.to_name_str().map(|value| String::from(value))
104    }
105
106    fn to_name_str_unchecked(&self) -> &str {
107        self.to_name_str().expect("Failed to convert to name!")
108    }
109
110    fn to_name_string_unchecked(&self) -> String {
111        self.to_name_string().expect("Failed to convert to name!")
112    }
113}
114
115impl <A> NameLike for A
116where A: StringLike {
117
118    fn to_name_str(&self) -> Result<&str, Utf8Error> {
119        self.to_str().map(|value| {
120            if Self::NAME_PREFIXES.contains(&&value[0..2]) {
121                &value[2..]
122            }
123            else {
124                &value
125            }
126        })
127    }
128}
129
130#[cfg(test)]
131mod test {
132    use crate::blend::{read, PointerLike, NameLike, StringLike};
133    use crate::blend::traverse::Named;
134    use crate::blender3_2::{bNode, bNodeSocket, bNodeTree, Image, Link, Material, Mesh, MLoop, MVert, Object};
135
136    #[test]
137    fn test() {
138
139        let blend_data = std::fs::read("examples/example-3.2.blend").unwrap();
140        let reader = read(&blend_data).unwrap();
141
142        let cube: &Object = reader.iter::<Object>().unwrap()
143            .find(|object| object.id.name.to_name_str_unchecked() == "Cube")
144            .unwrap();
145
146        println!("Object: {}", cube.id.get_name());
147
148        let parent = reader.deref(&cube.parent).unwrap().first().unwrap();
149
150        println!("Parent: {}", parent.id.get_name());
151
152        let mesh = reader.deref_single(&cube.data.as_instance_of::<Mesh>())
153            .unwrap();
154
155        println!("Mesh: {}", mesh.id.get_name());
156
157        let mesh_loop: Vec<&MLoop> = reader.deref(&mesh.mloop).unwrap().collect();
158        let mesh_vertices: Vec<&MVert> = reader.deref(&mesh.mvert).unwrap().collect();
159
160        let mesh_polygon = reader.deref(&mesh.mpoly).unwrap();
161        let _vertices = mesh_polygon
162            .map(|polygon| {
163                (polygon.loopstart..polygon.loopstart + polygon.totloop).into_iter().map(|loop_index| {
164                    mesh_vertices[mesh_loop[loop_index as usize].v as usize].co
165                })
166            })
167            .flatten()
168            .collect::<Vec<[f32; 3]>>();
169
170        let mat = reader.deref(&mesh.mat.as_instance_of::<Link>())
171            .map(|links| {
172                let link = links.first().unwrap();
173                reader.deref(&link.next.as_instance_of::<Material>()).unwrap()
174            })
175            .unwrap()
176            .first()
177            .unwrap();
178
179        println!("Material: {}, use_nodes: {}", mat.id.get_name(), &mat.use_nodes);
180
181        let tree: &bNodeTree = reader.deref_single(&mat.nodetree)
182            .unwrap();
183
184        let node = reader.deref_single(&tree.nodes.last.as_instance_of::<bNode>()) // FIXME: `last` is improper.
185            .unwrap();
186
187        let base_color_socket = reader.deref_single(&node.inputs.first.as_instance_of::<bNodeSocket>())
188            .unwrap();
189
190        let link = reader.deref_single(&base_color_socket.link)
191            .unwrap();
192
193        let tex_node = reader.deref_single(&link.fromnode)
194            .unwrap();
195
196        let tex_image = reader.deref_single(&tex_node.id.as_instance_of::<Image>())
197            .unwrap();
198
199        let image_packed_file = reader.deref_single(&tex_image.packedfile)
200            .unwrap();
201
202        let data = reader.deref_raw_range(&image_packed_file.data, 0..image_packed_file.size as usize)
203            .unwrap();
204
205        std::fs::write("/tmp/texture.jpg", data)
206            .unwrap();
207
208        let nodes = reader.traverse_double_linked(&tree.nodes.first.as_instance_of::<bNode>())
209            .unwrap()
210            .find(|node: &bNode| node.idname.to_str_unchecked() == "ShaderNodeTexImage")
211            .unwrap();
212
213        println!("Node: {}", nodes.get_name());
214        println!("Node: {}", nodes.idname.to_name_str_unchecked());
215
216        // nodes.for_each(|node| {
217        //     println!("Node: {}", node.name.to_name_str_unchecked());
218        // });
219    }
220}