use std::f32::consts::PI;
use crate::compat::{Point3, Vector2, Vector3};
use crate::builder_primitives::Triangle;
pub fn generate_plane(
width: f32,
depth: f32,
width_segments: usize,
depth_segments: usize,
) -> (Vec<Point3<f32>>, Vec<Triangle>, Vec<Vector3<f32>>, Vec<Vector2<f32>>) {
let width_half = width / 2.0;
let depth_half = depth / 2.0;
let grid_x = width_segments;
let grid_z = depth_segments;
let _segment_width = width / grid_x as f32;
let _segment_depth = depth / grid_z as f32;
let mut positions = Vec::new();
let mut normals = Vec::new();
let mut uvs = Vec::new();
let mut indices = Vec::new();
for z in 0..=grid_z {
let z_percent = z as f32 / grid_z as f32;
for x in 0..=grid_x {
let x_percent = x as f32 / grid_x as f32;
let x_pos = x_percent * width - width_half;
let z_pos = z_percent * depth - depth_half;
positions.push(crate::compat::point3::new(x_pos, 0.0, z_pos));
normals.push(crate::compat::vector3::new(0.0, 1.0, 0.0));
uvs.push(crate::compat::vector2::new(x_percent, 1.0 - z_percent)); }
}
let vertices_per_row = grid_x + 1;
for z in 0..grid_z {
for x in 0..grid_x {
let a = (z * vertices_per_row + x) as u32;
let b = (z * vertices_per_row + x + 1) as u32;
let c = ((z + 1) * vertices_per_row + x + 1) as u32;
let d = ((z + 1) * vertices_per_row + x) as u32;
indices.push(Triangle::new(a, b, d));
indices.push(Triangle::new(b, c, d));
}
}
(positions, indices, normals, uvs)
}
pub fn generate_sphere(
radius: f32,
width_segments: usize,
height_segments: usize,
) -> (Vec<Point3<f32>>, Vec<Triangle>, Vec<Vector3<f32>>, Vec<Vector2<f32>>) {
let width_segments = width_segments.max(3);
let height_segments = height_segments.max(2);
let mut positions = Vec::new();
let mut normals = Vec::new();
let mut uvs = Vec::new();
let mut indices = Vec::new();
for y in 0..=height_segments {
let v = y as f32 / height_segments as f32;
let phi = v * PI;
for x in 0..=width_segments {
let u = x as f32 / width_segments as f32;
let theta = u * 2.0 * PI;
let x_pos = -radius * theta.sin() * phi.sin();
let y_pos = radius * phi.cos();
let z_pos = radius * theta.cos() * phi.sin();
positions.push(crate::compat::point3::new(x_pos, y_pos, z_pos));
let length = (x_pos * x_pos + y_pos * y_pos + z_pos * z_pos).sqrt();
normals.push(crate::compat::vector3::new(x_pos / length, y_pos / length, z_pos / length));
uvs.push(crate::compat::vector2::new(u, 1.0 - v)); }
}
let vertices_per_row = width_segments + 1;
for y in 0..height_segments {
for x in 0..width_segments {
let a = (y * vertices_per_row + x) as u32;
let b = (y * vertices_per_row + x + 1) as u32;
let c = ((y + 1) * vertices_per_row + x + 1) as u32;
let d = ((y + 1) * vertices_per_row + x) as u32;
if y != 0 {
indices.push(Triangle::new(a, b, d));
}
if y != height_segments - 1 {
indices.push(Triangle::new(b, c, d));
}
}
}
(positions, indices, normals, uvs)
}
pub fn generate_cylinder(
radius_top: f32,
radius_bottom: f32,
height: f32,
radial_segments: usize,
height_segments: usize,
open_ended: bool,
) -> (Vec<Point3<f32>>, Vec<Triangle>, Vec<Vector3<f32>>, Vec<Vector2<f32>>) {
let radial_segments = radial_segments.max(3);
let height_segments = height_segments.max(1);
let mut positions = Vec::new();
let mut normals = Vec::new();
let mut uvs = Vec::new();
let mut indices = Vec::new();
let get_slope_normal = |_radius: f32, slope_factor: f32, u: f32| -> (f32, f32, f32) {
let sin_theta = (u * 2.0 * PI).sin();
let cos_theta = (u * 2.0 * PI).cos();
let nx = cos_theta;
let ny = slope_factor;
let nz = sin_theta;
let length = (nx * nx + ny * ny + nz * nz).sqrt();
(nx / length, ny / length, nz / length)
};
let slope_factor = if radius_top == radius_bottom {
0.0 } else {
height / (radius_bottom - radius_top)
};
for y in 0..=height_segments {
let v = y as f32 / height_segments as f32;
let y_pos = v * height - height / 2.0;
let radius = radius_bottom + v * (radius_top - radius_bottom);
for x in 0..=radial_segments {
let u = x as f32 / radial_segments as f32;
let theta = u * 2.0 * PI;
let sin_theta = theta.sin();
let cos_theta = theta.cos();
positions.push(crate::compat::point3::new(radius * cos_theta, y_pos, radius * sin_theta));
let (nx, ny, nz) = get_slope_normal(radius, slope_factor, u);
normals.push(crate::compat::vector3::new(nx, ny, nz));
uvs.push(crate::compat::vector2::new(u, 1.0 - v));
}
}
let vertices_per_row = radial_segments + 1;
for y in 0..height_segments {
for x in 0..radial_segments {
let a = (y * vertices_per_row + x) as u32;
let b = (y * vertices_per_row + x + 1) as u32;
let c = ((y + 1) * vertices_per_row + x + 1) as u32;
let d = ((y + 1) * vertices_per_row + x) as u32;
indices.push(Triangle::new(a, b, d));
indices.push(Triangle::new(b, c, d));
}
}
if !open_ended {
let start_index = positions.len() / 3;
positions.push(crate::compat::point3::new(0.0, height / 2.0, 0.0));
normals.push(crate::compat::vector3::new(0.0, 1.0, 0.0));
uvs.push(crate::compat::vector2::new(0.5, 0.5));
for x in 0..=radial_segments {
let u = x as f32 / radial_segments as f32;
let theta = u * 2.0 * PI;
let cos_theta = theta.cos();
let sin_theta = theta.sin();
positions.push(crate::compat::point3::new(radius_top * cos_theta, height / 2.0, radius_top * sin_theta));
normals.push(crate::compat::vector3::new(0.0, 1.0, 0.0));
uvs.push(crate::compat::vector2::new(cos_theta * 0.5 + 0.5, sin_theta * 0.5 + 0.5));
}
let center_index = start_index as u32;
for x in 0..radial_segments {
indices.push(Triangle::new(
center_index,
center_index + (x + 1) as u32,
center_index + (x + 2) as u32
));
}
let start_index = positions.len() / 3;
positions.push(crate::compat::point3::new(0.0, -height / 2.0, 0.0));
normals.push(crate::compat::vector3::new(0.0, -1.0, 0.0));
uvs.push(crate::compat::vector2::new(0.5, 0.5));
for x in 0..=radial_segments {
let u = x as f32 / radial_segments as f32;
let theta = u * 2.0 * PI;
let cos_theta = theta.cos();
let sin_theta = theta.sin();
positions.push(crate::compat::point3::new(radius_bottom * cos_theta, -height / 2.0, radius_bottom * sin_theta));
normals.push(crate::compat::vector3::new(0.0, -1.0, 0.0));
uvs.push(crate::compat::vector2::new(cos_theta * 0.5 + 0.5, sin_theta * 0.5 + 0.5));
}
let center_index = start_index as u32;
for x in 0..radial_segments {
indices.push(Triangle::new(
center_index,
center_index + (x + 2) as u32,
center_index + (x + 1) as u32
));
}
}
(positions, indices, normals, uvs)
}
pub fn generate_cone(
radius: f32,
height: f32,
radial_segments: usize,
height_segments: usize,
open_ended: bool,
) -> (Vec<Point3<f32>>, Vec<Triangle>, Vec<Vector3<f32>>, Vec<Vector2<f32>>) {
generate_cylinder(0.0, radius, height, radial_segments, height_segments, open_ended)
}
pub fn generate_torus(
radius: f32,
tube: f32,
radial_segments: usize,
tubular_segments: usize,
) -> (Vec<Point3<f32>>, Vec<Triangle>, Vec<Vector3<f32>>, Vec<Vector2<f32>>) {
let radial_segments = radial_segments.max(2);
let tubular_segments = tubular_segments.max(3);
let mut positions = Vec::new();
let mut normals = Vec::new();
let mut uvs = Vec::new();
let mut indices = Vec::new();
for j in 0..=radial_segments {
for i in 0..=tubular_segments {
let u = i as f32 / tubular_segments as f32 * 2.0 * PI;
let v = j as f32 / radial_segments as f32 * 2.0 * PI;
let x = (radius + tube * v.cos()) * u.cos();
let y = (radius + tube * v.cos()) * u.sin();
let z = tube * v.sin();
positions.push(crate::compat::point3::new(x, y, z));
let center_x = radius * u.cos();
let center_y = radius * u.sin();
let nx = x - center_x;
let ny = y - center_y;
let nz = z;
let length = (nx * nx + ny * ny + nz * nz).sqrt();
normals.push(crate::compat::vector3::new(nx / length, ny / length, nz / length));
uvs.push(crate::compat::vector2::new(i as f32 / tubular_segments as f32, j as f32 / radial_segments as f32));
}
}
for j in 0..radial_segments {
for i in 0..tubular_segments {
let a = (j * (tubular_segments + 1) + i) as u32;
let b = (j * (tubular_segments + 1) + i + 1) as u32;
let c = ((j + 1) * (tubular_segments + 1) + i + 1) as u32;
let d = ((j + 1) * (tubular_segments + 1) + i) as u32;
indices.push(Triangle::new(a, b, d));
indices.push(Triangle::new(b, c, d));
}
}
(positions, indices, normals, uvs)
}
pub fn generate_icosahedron(radius: f32) -> (Vec<Point3<f32>>, Vec<Triangle>, Vec<Vector3<f32>>, Vec<Vector2<f32>>) {
let t = (1.0 + 5.0_f32.sqrt()) / 2.0;
let mut positions = Vec::new();
let mut normals = Vec::new();
let mut uvs = Vec::new();
let mut indices = Vec::new();
let vertices = [
[-1.0, t, 0.0],
[1.0, t, 0.0],
[-1.0, -t, 0.0],
[1.0, -t, 0.0],
[0.0, -1.0, t],
[0.0, 1.0, t],
[0.0, -1.0, -t],
[0.0, 1.0, -t],
[t, 0.0, -1.0],
[t, 0.0, 1.0],
[-t, 0.0, -1.0],
[-t, 0.0, 1.0],
];
for vertex in &vertices {
let length = (vertex[0] * vertex[0] + vertex[1] * vertex[1] + vertex[2] * vertex[2]).sqrt();
let normalized = [
vertex[0] / length * radius,
vertex[1] / length * radius,
vertex[2] / length * radius,
];
positions.push(crate::compat::point3::new(normalized[0], normalized[1], normalized[2]));
normals.push(crate::compat::vector3::new(normalized[0] / radius, normalized[1] / radius, normalized[2] / radius));
let u = 0.5 + (normalized[0] / radius).atan2(normalized[2] / radius) / (2.0 * PI);
let v = 0.5 - (normalized[1] / radius).asin() / PI;
uvs.push(crate::compat::vector2::new(u, v));
}
let triangle_indices = [
[0, 11, 5],
[0, 5, 1],
[0, 1, 7],
[0, 7, 10],
[0, 10, 11],
[1, 5, 9],
[5, 11, 4],
[11, 10, 2],
[10, 7, 6],
[7, 1, 8],
[3, 9, 4],
[3, 4, 2],
[3, 2, 6],
[3, 6, 8],
[3, 8, 9],
[4, 9, 5],
[2, 4, 11],
[6, 2, 10],
[8, 6, 7],
[9, 8, 1],
];
for tri in &triangle_indices {
indices.push(Triangle::new(tri[0] as u32, tri[1] as u32, tri[2] as u32));
}
(positions, indices, normals, uvs)
}