nsys-gl-utils 0.11.9

OpenGL and graphics utilities
Documentation
use std;
use crate::{math, vertex};
use super::*;

/// (vertex count, index count)
pub const fn lines3d_vertex_index_counts (
  latitude_divisions : u16, longitude_divisions : u16
) -> (u32, u32) {
  let latitude_divisions  = (latitude_divisions - latitude_divisions % 2) as u32;
  let longitude_divisions = longitude_divisions as u32;
  ( 2 + (latitude_divisions - 1) * longitude_divisions,      // vertex_count
    2 * longitude_divisions * (2 * latitude_divisions - 1) ) // index count
}

/// Produces vertices and indices for a 3D lines list unit sphere (radius
/// $1.0$) with the given number of latitudinal divisions rounded down to the
/// nearest even number (ensuring the sphere always has an 'equator'), and with
/// the given number of longitudinal divisions.
///
/// The number of parallels will be equal to `latitude_divisions -
/// (latitude_divisions % 2) - 1` and the number of meridians will be equal to
/// `longitude_divisions`.
///
/// There will be `2 + (latitude_divisions-1) * longitude_divisions`
/// vertices and `4 * longitude_divisions * latitude_divisions -
/// 2 * longitude_divisions` indices.
///
/// The first `4 * (latitude_divisions / 2) * longitude_divisions` indices
/// can be used to render only the top hemisphere (the sphere itself is
/// computed progressively by adding more vertices and indices onto a base
/// hemisphere to form the southern hemisphere).
///
/// # Panics
///
/// Panics if `latitude_divisions` or `longitude_divisions` are less than two
/// (i.e. there must be at least an equator latitude dividing the north and
/// south hemispheres and a prime meridian dividing the east and west
/// hemispheres.
impl Lines3d {
  pub fn sphere (index_offset : u32, latitude_divisions : u16, longitude_divisions : u16)
    -> Self
  {
    use std::f32::consts::PI;
    assert!(2 <= latitude_divisions);
    assert!(2 <= longitude_divisions);
    let (num_vertices, num_indices) = lines3d_vertex_index_counts (
      latitude_divisions, longitude_divisions);
    let latitude_divisions  = (latitude_divisions - latitude_divisions % 2) as u32;
    let hemisphere_latitude_divisions = latitude_divisions / 2;
    let longitude_divisions = longitude_divisions as u32;
    let num_parallels       = latitude_divisions - 1;
    let num_meridians       = longitude_divisions;
    let latitude_angle      = PI / latitude_divisions  as f32;
    let longitude_angle     = 2.0 * (PI / longitude_divisions as f32);
    let (mut vertices, mut indices) = {
      let mut vertices =
        Vec::<vertex::Vert3dInstanced>::with_capacity (num_vertices as usize);
      let mut indices = Vec::<u32>::with_capacity (num_indices as usize);
      let Lines3d {
        vertices: mut hemisphere_vertices, indices: mut hemisphere_indices
      } = Lines3d::hemisphere (
        index_offset,
        hemisphere_latitude_divisions as u16,
        longitude_divisions as u16
      );
      vertices.append (&mut hemisphere_vertices);
      indices.append  (&mut hemisphere_indices);
      (vertices, indices)
    };
    let south_pole_index = index_offset + vertices.len() as u32;
    // the "south pole"
    vertices.push (vertex::Vert3dInstanced {
      inst_position: (-1.0 * math::Vector3::<f32>::unit_z()).into_array()
    });
    // for each southern parallel, generate a vertex for each meridian
    for i in 0..num_parallels / 2 {
      let i = i as f32 + 1.0;
      let v = *math::Rotation3::from_angle_x (math::Rad (-i * latitude_angle))
        * (-1.0 * math::Vector3::<f32>::unit_z());
      for j in 0..num_meridians {
        let j = j as f32;
        let w = *math::Rotation3::from_angle_z (math::Rad (j * longitude_angle)) * v;
        // each meridian for this parallel
        vertices.push (vertex::Vert3dInstanced { inst_position: w.into_array() });
      }
    }
    debug_assert_eq!(vertices.len(), num_vertices as usize);

    // connect south pole to last parallel
    for i in 0..num_meridians {
      indices.push (south_pole_index);
      indices.push (south_pole_index + 1 + i);
    }
    // connect each southern parallel with itself and the next parallel along the
    // meridians
    for i in hemisphere_latitude_divisions..num_parallels {
      let parallel_base_index = index_offset + 2 + i * num_meridians;
      for j in 0..num_meridians {
        indices.push (parallel_base_index + j);
        indices.push (parallel_base_index + (j + 1) % num_meridians);
        indices.push (parallel_base_index + j);
        if i < num_parallels-1 {
          // if this is not the last parallel, connect this to the next parallel
          indices.push (parallel_base_index + j + num_meridians);
        } else {
          // if this is the last parallel, connect it with the equator
          indices.push (south_pole_index - num_meridians + j);
        }
      }
    }
    debug_assert_eq!(indices.len(), num_indices as usize);

    Lines3d { vertices, indices }
  }
}