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.

123 lines
5.0KB

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