nsys-gl-utils 0.11.4

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

/// (vertex count, index count)
pub 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.
pub fn sphere (
  index_offset : u32, latitude_divisions : u16, longitude_divisions : u16
) -> (Vec <vertex::Vert3dInstanced>, Vec <u32>) {
  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);
      if i < num_parallels-1 {
        // if this is not the last parallel, connect this to the next parallel
        indices.push (parallel_base_index + j);
        indices.push (parallel_base_index + j + num_meridians);
      } else {
        // if this is the last parallel, connect it with the equator
        indices.push (parallel_base_index + j);
        indices.push (south_pole_index - num_meridians + j);
      }
    }
  }
  debug_assert_eq!(indices.len(), num_indices as usize);

  (vertices, indices)
}