diff --git a/.gitignore b/.gitignore index ccb41cc..e504091 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ Cargo.lock /.idea /parsable_macro_derive/target +/tests/test-*.obj diff --git a/Cargo.toml b/Cargo.toml index 1fa37e5..ca5fb10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,8 @@ chrono = {version = "^0.4", optional = true} fasthash = {version = "^0.4", optional = true} libfj_parsable_macro_derive = {version = "0.5.3", optional = true} #libfj_parsable_macro_derive = {path = "./parsable_macro_derive", optional = true} +obj = {version = "^0.10", optional = true} +genmesh = {version = "^0.6", optional = true} [dev-dependencies] tokio = { version = "1.4.0", features = ["macros"]} @@ -32,3 +34,4 @@ simple = ["ureq"] robocraft = ["reqwest"] cardlife = ["reqwest"] techblox = ["chrono", "fasthash", "libfj_parsable_macro_derive"] +convert = ["obj", "genmesh"] diff --git a/src/convert/mod.rs b/src/convert/mod.rs new file mode 100644 index 0000000..5c267c8 --- /dev/null +++ b/src/convert/mod.rs @@ -0,0 +1,8 @@ +//! Conversion utility functions. +//! +//! This will contain ways to convert data from one game to another as well as into standard formats. + +#[cfg(feature = "robocraft")] +mod robocraft_3d; +#[cfg(feature = "robocraft")] +pub use robocraft_3d::cubes_to_model; diff --git a/src/convert/robocraft_3d.rs b/src/convert/robocraft_3d.rs new file mode 100644 index 0000000..ca4466e --- /dev/null +++ b/src/convert/robocraft_3d.rs @@ -0,0 +1,65 @@ +use genmesh::{generators::Cube, Quad, MapToVertices, Vertices}; +use obj; +use crate::robocraft; + +/// Convert a Robocraft robot to a 3D model in Wavefront OBJ format. +pub fn cubes_to_model(robot: robocraft::Cubes) -> obj::Obj { + let mut positions = Vec::<[f32; 3]>::new(); + let mut normals = Vec::<[f32; 3]>::new(); + let mut objects = Vec::::new(); + let mut last = 0; + for cube in robot.into_iter() { + positions.extend::>( + Cube::new().vertex(|v| + [(v.pos.x * 0.5) + (cube.x as f32), (v.pos.y * 0.5) + (cube.y as f32), (v.pos.z * 0.5) + (cube.z as f32)]) + .vertices() + .collect() + ); + normals.extend::>( + Cube::new().vertex(|v| + [(v.normal.x * 0.5) + (cube.x as f32), (v.normal.y * 0.5) + (cube.y as f32), (v.normal.z * 0.5) + (cube.z as f32)]) + .vertices() + .collect() + ); + let polys = Cube::new().vertex(|_| {last+=1; return last-1;}) + .map(|Quad{x: v0, y: v1, z: v2, w: v3}| + obj::SimplePolygon(vec![ + obj::IndexTuple(v0, Some(0), Some(v0)), + obj::IndexTuple(v1, Some(0), Some(v1)), + obj::IndexTuple(v2, Some(0), Some(v2)), + obj::IndexTuple(v3, Some(0), Some(v3)) + ]) + /*obj::SimplePolygon(vec![ + obj::IndexTuple(v0, None, None), + obj::IndexTuple(v1, None, None), + obj::IndexTuple(v2, None, None), + obj::IndexTuple(v3, None, None) + ])*/ + ).collect(); + objects.push( + obj::Object{ + name: format!("Cube-ID{}-NUM{}", cube.id, objects.len()), + groups: vec![ + obj::Group { + name: format!("Cube-ID{}-NUM{}-0", cube.id, objects.len()), + index: 0, + material: None, + polys: polys + }, + ] + } + ); + } + println!("Last (index): {}, Vertices (len): {}", last, positions.len()); + + obj::Obj{ + data: obj::ObjData { + position: positions, + texture: vec![[0.0, 0.0]], + normal: normals, + objects: objects, + material_libs: Vec::new(), + }, + path: std::path::PathBuf::new(), + } +} diff --git a/src/lib.rs b/src/lib.rs index 667fb6a..4172488 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,3 +11,5 @@ pub mod robocraft; pub mod robocraft_simple; #[cfg(feature = "techblox")] pub mod techblox; +#[cfg(feature = "convert")] +pub mod convert; diff --git a/tests/convert_3d.rs b/tests/convert_3d.rs new file mode 100644 index 0000000..4c73acb --- /dev/null +++ b/tests/convert_3d.rs @@ -0,0 +1,20 @@ +#[cfg(all(feature = "robocraft", feature = "convert"))] +use libfj::convert::cubes_to_model; +#[cfg(all(feature = "robocraft", feature = "convert"))] +use libfj::robocraft; + +#[cfg(all(feature = "robocraft", feature = "convert"))] +#[tokio::test] +async fn convert_to_obj() -> Result<(), ()> { + let api = robocraft::FactoryAPI::new(); + let result = api.list().await; + assert!(result.is_ok()); + let robot = api.get(result.unwrap().response.roboshop_items[0].item_id).await; + assert!(robot.is_ok()); + let cubes = robot.unwrap(); + let obj = cubes_to_model(robocraft::Cubes::from(cubes.clone())); + let save_result = obj.save(format!("tests/test-{}.obj", cubes.response.item_id)); + //save_result.unwrap(); + assert!(save_result.is_ok()); + Ok(()) +}