use oxideav_obj::obj::{SerializeOptions, parse_obj, serialize_obj_with_options};
fn count(haystack: &str, needle: &str) -> usize {
haystack.matches(needle).count()
}
fn scene_from(src: &str) -> oxideav_mesh3d::Scene3D {
parse_obj(src).expect("parse_obj")
}
#[test]
fn maplib_single_file_round_trips() {
let src = "\
maplib textures.map
v 0 0 0
v 1 0 0
v 0 1 0
usemap MyTex
f 1 2 3
";
let scene = scene_from(src);
let libs = scene
.extras
.get("obj:maplibs")
.and_then(|v| v.as_array())
.expect("maplibs");
assert_eq!(libs.len(), 1);
assert_eq!(libs[0].as_str(), Some("textures.map"));
let mesh = &scene.meshes[0];
let prim = &mesh.primitives[0];
assert_eq!(
prim.extras.get("obj:usemap").and_then(|v| v.as_str()),
Some("MyTex"),
);
let bytes = serialize_obj_with_options(&scene, &SerializeOptions::default()).unwrap();
let text = std::str::from_utf8(&bytes).unwrap();
assert!(text.contains("maplib textures.map"));
assert!(text.contains("usemap MyTex"));
let scene2 = scene_from(text);
let libs2 = scene2
.extras
.get("obj:maplibs")
.and_then(|v| v.as_array())
.unwrap();
assert_eq!(libs2.len(), 1);
let prim2 = &scene2.meshes[0].primitives[0];
assert_eq!(
prim2.extras.get("obj:usemap").and_then(|v| v.as_str()),
Some("MyTex"),
);
}
#[test]
fn maplib_multi_file_preserves_order() {
let src = "\
maplib a.map b.map c.map
v 0 0 0
v 1 0 0
v 0 1 0
f 1 2 3
";
let scene = scene_from(src);
let libs: Vec<&str> = scene
.extras
.get("obj:maplibs")
.and_then(|v| v.as_array())
.unwrap()
.iter()
.filter_map(|v| v.as_str())
.collect();
assert_eq!(libs, vec!["a.map", "b.map", "c.map"]);
}
#[test]
fn maplib_dedupes_repeated_entries() {
let src = "\
maplib a.map b.map
maplib a.map c.map
v 0 0 0
v 1 0 0
v 0 1 0
f 1 2 3
";
let scene = scene_from(src);
let libs: Vec<&str> = scene
.extras
.get("obj:maplibs")
.and_then(|v| v.as_array())
.unwrap()
.iter()
.filter_map(|v| v.as_str())
.collect();
assert_eq!(libs, vec!["a.map", "b.map", "c.map"]);
let bytes = serialize_obj_with_options(&scene, &SerializeOptions::default()).unwrap();
let text = std::str::from_utf8(&bytes).unwrap();
assert_eq!(count(text, "maplib a.map\n"), 1);
assert_eq!(count(text, "maplib b.map\n"), 1);
assert_eq!(count(text, "maplib c.map\n"), 1);
}
#[test]
fn usemap_off_round_trips_verbatim() {
let src = "\
v 0 0 0
v 1 0 0
v 0 1 0
usemap off
f 1 2 3
";
let scene = scene_from(src);
let prim = &scene.meshes[0].primitives[0];
assert_eq!(
prim.extras.get("obj:usemap").and_then(|v| v.as_str()),
Some("off"),
);
let bytes = serialize_obj_with_options(&scene, &SerializeOptions::default()).unwrap();
let text = std::str::from_utf8(&bytes).unwrap();
assert!(text.contains("usemap off"));
}
#[test]
fn usemap_mid_stream_switch_splits_primitive() {
let src = "\
v 0 0 0
v 1 0 0
v 0 1 0
v 1 1 0
usemap TexA
f 1 2 3
usemap TexB
f 2 4 3
";
let scene = scene_from(src);
let mesh = &scene.meshes[0];
assert_eq!(mesh.primitives.len(), 2);
assert_eq!(
mesh.primitives[0]
.extras
.get("obj:usemap")
.and_then(|v| v.as_str()),
Some("TexA"),
);
assert_eq!(
mesh.primitives[1]
.extras
.get("obj:usemap")
.and_then(|v| v.as_str()),
Some("TexB"),
);
}
#[test]
fn usemap_inherits_across_usemtl_switch() {
let src = "\
v 0 0 0
v 1 0 0
v 0 1 0
v 1 1 0
usemap TexA
usemtl MatA
f 1 2 3
usemtl MatB
f 2 4 3
";
let scene = scene_from(src);
let mesh = &scene.meshes[0];
assert_eq!(mesh.primitives.len(), 2);
assert_eq!(
mesh.primitives[0]
.extras
.get("obj:usemtl")
.and_then(|v| v.as_str()),
Some("MatA"),
);
assert_eq!(
mesh.primitives[1]
.extras
.get("obj:usemtl")
.and_then(|v| v.as_str()),
Some("MatB"),
);
assert_eq!(
mesh.primitives[0]
.extras
.get("obj:usemap")
.and_then(|v| v.as_str()),
Some("TexA"),
);
assert_eq!(
mesh.primitives[1]
.extras
.get("obj:usemap")
.and_then(|v| v.as_str()),
Some("TexA"),
);
}
#[test]
fn full_round_trip_preserves_both() {
let src = "\
maplib lib1.map lib2.map
v 0 0 0
v 1 0 0
v 0 1 0
v 1 1 0
usemap Tex1
f 1 2 3
usemap Tex2
f 2 4 3
usemap off
f 1 3 4
";
let scene1 = scene_from(src);
let bytes = serialize_obj_with_options(&scene1, &SerializeOptions::default()).unwrap();
let text = std::str::from_utf8(&bytes).unwrap();
let scene2 = scene_from(text);
let libs: Vec<&str> = scene2
.extras
.get("obj:maplibs")
.and_then(|v| v.as_array())
.unwrap()
.iter()
.filter_map(|v| v.as_str())
.collect();
assert_eq!(libs, vec!["lib1.map", "lib2.map"]);
let mesh = &scene2.meshes[0];
assert_eq!(mesh.primitives.len(), 3);
let usemaps: Vec<&str> = mesh
.primitives
.iter()
.filter_map(|p| p.extras.get("obj:usemap").and_then(|v| v.as_str()))
.collect();
assert_eq!(usemaps, vec!["Tex1", "Tex2", "off"]);
}
#[test]
fn no_usemap_directive_means_no_extras_key() {
let src = "\
v 0 0 0
v 1 0 0
v 0 1 0
f 1 2 3
";
let scene = scene_from(src);
assert!(!scene.extras.contains_key("obj:maplibs"));
let prim = &scene.meshes[0].primitives[0];
assert!(!prim.extras.contains_key("obj:usemap"));
let bytes = serialize_obj_with_options(&scene, &SerializeOptions::default()).unwrap();
let text = std::str::from_utf8(&bytes).unwrap();
assert!(!text.contains("maplib"));
assert!(!text.contains("usemap"));
}