An unofficial collection of APIs used in FreeJam games and mods
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

118 lines
4.9KB

  1. use genmesh::{generators::Cube, Quad, MapToVertices, Vertices, Vertex};
  2. use obj;
  3. use cgmath::{Quaternion, Euler, Deg, Vector3};
  4. use crate::robocraft;
  5. const SCALE: f32 = 0.5;
  6. const ROTATIONS: [Euler<Deg<f32>>; 24] = [
  7. Euler{x: Deg(0.0), y: Deg(0.0), z: Deg(0.0)}, // 0
  8. Euler{x: Deg(0.0), y: Deg(0.0), z: Deg(90.0)},
  9. Euler{x: Deg(0.0), y: Deg(0.0), z: Deg(180.0)}, // 2
  10. Euler{x: Deg(0.0), y: Deg(0.0), z: Deg(-90.0)},
  11. Euler{x: Deg(0.0), y: Deg(90.0), z: Deg(0.0)}, // 4
  12. Euler{x: Deg(0.0), y: Deg(90.0), z: Deg(90.0)},
  13. Euler{x: Deg(-90.0), y: Deg(-90.0), z: Deg(0.0)}, // 6
  14. Euler{x: Deg(0.0), y: Deg(90.0), z: Deg(-90.0)},
  15. Euler{x: Deg(0.0), y: Deg(-90.0), z: Deg(90.0)}, // 8
  16. Euler{x: Deg(0.0), y: Deg(-90.0), z: Deg(-90.0)},
  17. Euler{x: Deg(90.0), y: Deg(-90.0), z: Deg(0.0)}, // 10
  18. Euler{x: Deg(90.0), y: Deg(90.0), z: Deg(0.0)},
  19. Euler{x: Deg(-90.0), y: Deg(90.0), z: Deg(0.0)}, // 12
  20. Euler{x: Deg(0.0), y: Deg(90.0), z: Deg(180.0)},
  21. Euler{x: Deg(0.0), y: Deg(180.0), z: Deg(0.0)}, // 14
  22. Euler{x: Deg(0.0), y: Deg(180.0), z: Deg(90.0)},
  23. Euler{x: Deg(0.0), y: Deg(180.0), z: Deg(0.0)}, // 16
  24. Euler{x: Deg(0.0), y: Deg(180.0), z: Deg(180.0)},
  25. Euler{x: Deg(0.0), y: Deg(-90.0), z: Deg(-90.0)}, // 18
  26. Euler{x: Deg(0.0), y: Deg(-90.0), z: Deg(0.0)},
  27. Euler{x: Deg(90.0), y: Deg(0.0), z: Deg(180.0)}, // 20
  28. Euler{x: Deg(90.0), y: Deg(180.0), z: Deg(0.0)},
  29. Euler{x: Deg(-90.0), y: Deg(0.0), z: Deg(0.0)}, // 22
  30. Euler{x: Deg(-90.0), y: Deg(180.0), z: Deg(0.0)}, // 23
  31. ];
  32. /// Convert a Robocraft robot to a 3D model in Wavefront OBJ format.
  33. pub fn cubes_to_model(robot: robocraft::Cubes) -> obj::Obj {
  34. cubes_to_model_with_lut(robot, default_model_lut)
  35. }
  36. /// Convert a Robocraft robot to a 3D model in Wavefront OBJ format using the provided lookup table function.
  37. pub fn cubes_to_model_with_lut<F: FnMut(u32) -> Vec<Quad<Vertex>>>(robot: robocraft::Cubes, mut lut: F) -> obj::Obj {
  38. let mut positions = Vec::<[f32; 3]>::new(); // vertex positions
  39. let mut normals = Vec::<[f32; 3]>::new(); // vertex normals
  40. let mut objects = Vec::<obj::Object>::new(); // blocks
  41. let mut last = 0;
  42. for cube in robot.into_iter() {
  43. // generate simple cube for every block
  44. // TODO rotate blocks
  45. let vertices = lut(cube.id); // Use lookup table to find correct id <-> block translation
  46. let rotation: Quaternion<_> = ROTATIONS[cube.orientation as usize].into();
  47. positions.extend::<Vec::<[f32; 3]>>(
  48. vertices.clone().into_iter().vertex(|v|
  49. {
  50. let rotated = rotation * Vector3{x: v.pos.x * SCALE, y: v.pos.y * SCALE, z: v.pos.z * SCALE};
  51. [rotated.x + (cube.x as f32), rotated.y + (cube.y as f32), rotated.z + (cube.z as f32)]
  52. })
  53. .vertices()
  54. .collect()
  55. );
  56. normals.extend::<Vec::<[f32; 3]>>(
  57. vertices.clone().into_iter().vertex(|v|
  58. {
  59. let rotated = rotation * Vector3{x: v.normal.x * SCALE, y: v.normal.y * SCALE, z: v.normal.z * SCALE};
  60. [rotated.x + (cube.x as f32), rotated.y + (cube.y as f32), rotated.z + (cube.z as f32)]
  61. })
  62. .vertices()
  63. .collect()
  64. );
  65. let polys = vertices.clone().into_iter().vertex(|_| {last+=1; return last-1;})
  66. .map(|Quad{x: v0, y: v1, z: v2, w: v3}|
  67. obj::SimplePolygon(vec![
  68. obj::IndexTuple(v0, Some(0), Some(v0)),
  69. obj::IndexTuple(v1, Some(0), Some(v1)),
  70. obj::IndexTuple(v2, Some(0), Some(v2)),
  71. obj::IndexTuple(v3, Some(0), Some(v3))
  72. ])
  73. /*obj::SimplePolygon(vec![
  74. obj::IndexTuple(v0, None, None),
  75. obj::IndexTuple(v1, None, None),
  76. obj::IndexTuple(v2, None, None),
  77. obj::IndexTuple(v3, None, None)
  78. ])*/
  79. ).collect();
  80. objects.push(
  81. obj::Object{
  82. name: format!("Cube-ID{}-NUM{}", cube.id, objects.len()),
  83. groups: vec![
  84. obj::Group {
  85. name: format!("Cube-ID{}-NUM{}-0", cube.id, objects.len()),
  86. index: 0,
  87. material: None,
  88. polys: polys
  89. },
  90. ]
  91. }
  92. );
  93. }
  94. println!("Last (index): {}, Vertices (len): {}", last, positions.len());
  95. obj::Obj{
  96. data: obj::ObjData {
  97. position: positions,
  98. texture: vec![[0.0, 0.0]],
  99. normal: normals,
  100. objects: objects,
  101. material_libs: Vec::new(),
  102. },
  103. path: std::path::PathBuf::new(),
  104. }
  105. }
  106. pub fn default_model_lut(id: u32) -> Vec<Quad<Vertex>> {
  107. // TODO generate non-cube blocks properly
  108. match id {
  109. _ => Cube::new().collect(),
  110. }
  111. }