use std::f32::consts::PI;
use zaplib::*;
#[repr(C)]
pub struct Geometry3d {
geom_pos: Vec3,
geom_id: f32,
geom_normal: Vec3,
geom_uv: Vec2,
}
#[derive(Clone, Copy)]
enum GeometryAxis {
X = 0,
Y = 1,
Z = 2,
}
impl Geometry3d {
pub const SHADER: CodeFragment = code_fragment!(
r#"
geometry geom_pos: vec3;
geometry geom_id: float;
geometry geom_normal: vec3;
geometry geom_uv: vec2;
"#
);
pub fn cube(
width: f32,
height: f32,
depth: f32,
width_segments: usize,
height_segments: usize,
depth_segments: usize,
) -> Geometry {
let mut vertex_attributes = vec![];
let mut indices = vec![];
add_plane_3d(
&mut vertex_attributes,
&mut indices,
GeometryAxis::Z,
GeometryAxis::Y,
GeometryAxis::X,
-1.0,
-1.0,
depth,
height,
width,
depth_segments,
height_segments,
0.0,
);
add_plane_3d(
&mut vertex_attributes,
&mut indices,
GeometryAxis::Z,
GeometryAxis::Y,
GeometryAxis::X,
1.0,
-1.0,
depth,
height,
-width,
depth_segments,
height_segments,
1.0,
);
add_plane_3d(
&mut vertex_attributes,
&mut indices,
GeometryAxis::X,
GeometryAxis::Z,
GeometryAxis::Y,
1.0,
1.0,
width,
depth,
height,
width_segments,
depth_segments,
2.0,
);
add_plane_3d(
&mut vertex_attributes,
&mut indices,
GeometryAxis::X,
GeometryAxis::Z,
GeometryAxis::Y,
1.0,
-1.0,
width,
depth,
-height,
width_segments,
depth_segments,
3.0,
);
add_plane_3d(
&mut vertex_attributes,
&mut indices,
GeometryAxis::X,
GeometryAxis::Y,
GeometryAxis::Z,
1.0,
-1.0,
width,
height,
depth,
width_segments,
height_segments,
4.0,
);
add_plane_3d(
&mut vertex_attributes,
&mut indices,
GeometryAxis::X,
GeometryAxis::Y,
GeometryAxis::Z,
-1.0,
-1.0,
width,
height,
-depth,
width_segments,
height_segments,
5.0,
);
Geometry::new(vertex_attributes, indices)
}
pub fn sphere(num_parallels: u32, num_meridians: u32, radius: f32) -> Geometry {
let mut vertex_attributes = Vec::with_capacity((num_parallels * num_meridians + 2) as usize);
let mut indices = vec![];
vertex_attributes.push(Geometry3d {
geom_pos: vec3(0., 0., radius),
geom_id: 0.0,
geom_normal: vec3(0., 0., radius),
geom_uv: vec2(0., 0.),
});
vertex_attributes.push(Geometry3d {
geom_pos: vec3(0., 0., -radius),
geom_id: 0.0,
geom_normal: vec3(0., 0., -radius),
geom_uv: vec2(0., 0.),
});
let mut point_count: u32 = 2;
for i in 0..num_parallels {
for j in 0..num_meridians {
let phi = (((i + 1) as f32) / ((num_parallels + 1) as f32)) * PI;
let z = radius * phi.cos();
let width = radius * phi.sin();
let theta = (j as f32 * 2. * PI) / (num_meridians as f32);
let x = width * theta.cos();
let y = width * theta.sin();
vertex_attributes.push(Geometry3d {
geom_pos: vec3(x, y, z),
geom_id: 0.0,
geom_normal: vec3(x, y, z),
geom_uv: vec2(0., 0.),
});
point_count += 1;
if j > 0 {
let prev_meridian: u32 = if i == 0 { 0 } else { point_count - 1 - num_meridians };
indices.push([point_count - 2, point_count - 1, prev_meridian]);
if i > 0 {
indices.push([point_count - 2, prev_meridian - 1, prev_meridian]);
}
}
}
let prev_meridian: u32 = if i == 0 { 0 } else { point_count - 2 * num_meridians };
indices.push([point_count - 1, point_count - num_meridians, prev_meridian]);
if i > 0 {
indices.push([point_count - 1, point_count - num_meridians - 1, prev_meridian]);
}
}
for j in 0..num_meridians {
let pt = point_count - num_meridians + j;
let prev_pt = if j == 0 { point_count - 1 } else { pt - 1 };
indices.push([pt, prev_pt, 1]);
}
Geometry::new(vertex_attributes, indices)
}
pub fn cylinder_or_cone(num_segments: u32, is_cone: bool) -> Geometry {
let mut vertex_attributes = vec![];
let mut indices = vec![];
vertex_attributes.push(Geometry3d {
geom_pos: vec3(0., 0., 0.5),
geom_id: 0.0,
geom_normal: vec3(0., 1., 0.),
geom_uv: vec2(0., 0.),
});
vertex_attributes.push(Geometry3d {
geom_pos: vec3(0., 0., -0.5),
geom_id: 0.0,
geom_normal: vec3(0., -1., 0.),
geom_uv: vec2(0., 0.),
});
let mut side_faces = vec![];
let mut end_cap_faces = vec![];
let mut point_count = 0;
for i in 0..num_segments {
let theta = (2. * PI * i as f32) / num_segments as f32;
let x = 0.5 * theta.cos();
let y = 0.5 * theta.sin();
vertex_attributes.push(Geometry3d {
geom_pos: vec3(x, y, 0.5),
geom_id: 0.0,
geom_normal: vec3(x, y, 0.5),
geom_uv: vec2(0., 0.),
});
vertex_attributes.push(Geometry3d {
geom_pos: vec3(x, y, -0.5),
geom_id: 0.0,
geom_normal: vec3(x, y, -0.5),
geom_uv: vec2(0., 0.),
});
point_count += 2;
let bottom_left_pt = point_count - 1;
let top_right_pt = if is_cone {
0
} else if i + 1 == num_segments {
2
} else {
point_count
};
let bottom_right_pt = if i + 1 == num_segments { 3 } else { point_count + 1 };
side_faces.push([bottom_left_pt, top_right_pt, bottom_right_pt]);
end_cap_faces.push([bottom_left_pt, bottom_right_pt, 1]);
if !is_cone {
let top_left_pt = point_count - 2;
side_faces.push([top_left_pt, bottom_left_pt, top_right_pt]);
end_cap_faces.push([top_left_pt, top_right_pt, 0]);
}
}
indices.extend_from_slice(&side_faces[..]);
indices.extend_from_slice(&end_cap_faces[..]);
Geometry::new(vertex_attributes, indices)
}
}
#[warn(clippy::many_single_char_names)]
fn add_plane_3d(
vertex_attributes: &mut Vec<Geometry3d>,
indices: &mut Vec<[u32; 3]>,
u: GeometryAxis,
v: GeometryAxis,
w: GeometryAxis,
udir: f32,
vdir: f32,
width: f32,
height: f32,
depth: f32,
grid_x: usize,
grid_y: usize,
id: f32,
) {
let segment_width = width / (grid_x as f32);
let segment_height = height / (grid_y as f32);
let width_half = width / 2.0;
let height_half = height / 2.0;
let depth_half = depth / 2.0;
let grid_x1 = grid_x + 1;
let grid_y1 = grid_y + 1;
let vertex_offset = vertex_attributes.len();
for iy in 0..grid_y1 {
let y = (iy as f32) * segment_height - height_half;
for ix in 0..grid_x1 {
let x = (ix as f32) * segment_width - width_half;
let mut vertex_attribute = Geometry3d {
geom_pos: Default::default(),
geom_id: id,
geom_normal: Default::default(),
geom_uv: vec2((ix as f32) / (grid_x as f32), 1.0 - (iy as f32) / (grid_y as f32)),
};
let geom_pos = vertex_attribute.geom_pos.as_mut_array();
geom_pos[u as usize] = x * udir;
geom_pos[v as usize] = y * vdir;
geom_pos[w as usize] = depth_half;
let geom_normal = vertex_attribute.geom_normal.as_mut_array();
geom_normal[w as usize] = if depth > 0.0 { 1.0 } else { -1.0 };
vertex_attributes.push(vertex_attribute);
}
}
for iy in 0..grid_y {
for ix in 0..grid_x {
let a = vertex_offset + ix + grid_x1 * iy;
let b = vertex_offset + ix + grid_x1 * (iy + 1);
let c = vertex_offset + (ix + 1) + grid_x1 * (iy + 1);
let d = vertex_offset + (ix + 1) + grid_x1 * iy;
indices.push([a as u32, b as u32, d as u32]);
indices.push([b as u32, c as u32, d as u32]);
}
}
}