use std::sync::Arc;
use glam::{uvec3, vec3, Vec3, Vec3A};
use hexasphere::BaseShape;
use itertools::Itertools as _;
use ordered_float::NotNan;
use smallvec::smallvec;
use re_math::MeshGen;
use re_renderer::{
mesh::{self, GpuMesh, MeshError},
RenderContext,
};
use re_viewer_context::Cache;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum ProcMeshKey {
Cube,
Sphere {
subdivisions: usize,
axes_only: bool,
},
Capsule {
length: NotNan<f32>,
subdivisions: usize,
},
}
impl ProcMeshKey {
pub fn simple_bounding_box(&self) -> re_math::BoundingBox {
match self {
Self::Sphere {
subdivisions: _,
axes_only: _,
} => {
re_math::BoundingBox::from_center_size(Vec3::splat(0.0), Vec3::splat(2.0))
}
Self::Cube => {
re_math::BoundingBox::from_center_size(Vec3::splat(0.0), Vec3::splat(1.0))
}
Self::Capsule {
subdivisions: _,
length,
} => re_math::BoundingBox::from_min_max(
Vec3::new(-1.0, -1.0, -1.0),
Vec3::new(1.0, 1.0, 1.0 + length.into_inner()),
),
}
}
}
#[derive(Debug)]
pub struct WireframeMesh {
#[allow(unused)]
pub bbox: re_math::BoundingBox,
#[allow(unused)]
pub vertex_count: usize,
pub line_strips: Vec<Vec<Vec3>>,
}
#[derive(Clone)]
pub struct SolidMesh {
#[allow(unused)]
pub bbox: re_math::BoundingBox,
pub gpu_mesh: Arc<GpuMesh>,
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
enum GenError {
#[error("creating a wireframe mesh is not supported")]
UnimplementedWireframe,
#[error(transparent)]
MeshProcessing(#[from] MeshError),
}
#[derive(Default)]
pub struct WireframeCache(ahash::HashMap<ProcMeshKey, Option<Arc<WireframeMesh>>>);
impl WireframeCache {
pub fn entry(
&mut self,
key: ProcMeshKey,
render_ctx: &RenderContext,
) -> Option<Arc<WireframeMesh>> {
re_tracing::profile_function!();
self.0
.entry(key)
.or_insert_with(|| {
re_tracing::profile_scope!("proc_mesh::WireframeCache(miss)", format!("{key:?}"));
re_log::trace!("Generating wireframe mesh {key:?}…");
match generate_wireframe(&key, render_ctx) {
Ok(mesh) => Some(Arc::new(mesh)),
Err(err) => {
re_log::warn!(
"Failed to generate mesh {key:?}: {}",
re_error::format_ref(&err)
);
None
}
}
})
.clone()
}
}
impl Cache for WireframeCache {
fn purge_memory(&mut self) {
self.0.clear();
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
fn generate_wireframe(
key: &ProcMeshKey,
render_ctx: &RenderContext,
) -> Result<WireframeMesh, GenError> {
re_tracing::profile_function!();
_ = render_ctx;
let mesh = match *key {
ProcMeshKey::Cube => {
let corners = [
vec3(-0.5, -0.5, -0.5),
vec3(-0.5, -0.5, 0.5),
vec3(-0.5, 0.5, -0.5),
vec3(-0.5, 0.5, 0.5),
vec3(0.5, -0.5, -0.5),
vec3(0.5, -0.5, 0.5),
vec3(0.5, 0.5, -0.5),
vec3(0.5, 0.5, 0.5),
];
let line_strips: Vec<Vec<Vec3>> = vec![
vec![
corners[0b000],
corners[0b001],
corners[0b011],
corners[0b010],
corners[0b000],
corners[0b100],
corners[0b101],
corners[0b111],
corners[0b110],
corners[0b100],
],
vec![corners[0b001], corners[0b101]],
vec![corners[0b010], corners[0b110]],
vec![corners[0b011], corners[0b111]],
];
WireframeMesh {
bbox: key.simple_bounding_box(),
vertex_count: line_strips.iter().map(|v| v.len()).sum(),
line_strips,
}
}
ProcMeshKey::Sphere {
subdivisions,
axes_only,
} => {
let subdiv: hexasphere::Subdivided<(), OctahedronBase> =
hexasphere::Subdivided::new(subdivisions, |_| ());
let sphere_points = subdiv.raw_points();
let line_strips: Vec<Vec<Vec3>> = if axes_only {
let mut buffer: Vec<u32> = Vec::new();
subdiv.get_major_edges_line_indices(&mut buffer, 1, |v| v.push(0));
buffer
.split(|&i| i == 0)
.map(|strip| -> Vec<Vec3> {
strip
.iter()
.map(|&i| sphere_points[i as usize - 1].into())
.collect()
})
.collect()
} else {
subdiv
.get_all_line_indices(1, |v| v.push(0))
.split(|&i| i == 0)
.map(|strip| -> Vec<Vec3> {
strip
.iter()
.map(|&i| sphere_points[i as usize - 1].into())
.collect()
})
.collect()
};
WireframeMesh {
bbox: key.simple_bounding_box(),
vertex_count: line_strips.iter().map(|v| v.len()).sum(),
line_strips,
}
}
ProcMeshKey::Capsule {
length: _,
subdivisions: _,
} => {
return Err(GenError::UnimplementedWireframe);
}
};
Ok(mesh)
}
#[derive(Default)]
pub struct SolidCache(ahash::HashMap<ProcMeshKey, Option<SolidMesh>>);
impl SolidCache {
pub fn entry(&mut self, key: ProcMeshKey, render_ctx: &RenderContext) -> Option<SolidMesh> {
re_tracing::profile_function!();
self.0
.entry(key)
.or_insert_with(|| {
re_tracing::profile_scope!("proc_mesh::SolidCache(miss)", format!("{key:?}"));
re_log::trace!("Generating solid mesh {key:?}…");
match generate_solid(&key, render_ctx) {
Ok(mesh) => Some(mesh),
Err(err) => {
re_log::warn!(
"Failed to generate mesh {key:?}: {}",
re_error::format_ref(&err)
);
None
}
}
})
.clone()
}
}
impl Cache for SolidCache {
fn purge_memory(&mut self) {
self.0.clear();
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
fn generate_solid(key: &ProcMeshKey, render_ctx: &RenderContext) -> Result<SolidMesh, GenError> {
re_tracing::profile_function!();
let mesh: mesh::CpuMesh = match *key {
ProcMeshKey::Cube => {
let mut mg = re_math::MeshGen::new();
mg.push_cube(Vec3::splat(0.5), re_math::IsoTransform::IDENTITY);
mesh_from_mesh_gen(format!("{key:?}").into(), mg, render_ctx)
}
ProcMeshKey::Sphere {
subdivisions,
axes_only: _, } => {
let subdiv: hexasphere::Subdivided<(), OctahedronBase> =
hexasphere::Subdivided::new(subdivisions, |_| ());
let vertex_positions: Vec<Vec3> =
subdiv.raw_points().iter().map(|&p| p.into()).collect();
let vertex_normals = vertex_positions.clone();
let num_vertices = vertex_positions.len();
let triangle_indices = subdiv.get_all_indices();
let triangle_indices: Vec<glam::UVec3> = triangle_indices
.into_iter()
.tuples()
.map(|(i1, i2, i3)| glam::uvec3(i1, i2, i3))
.collect();
let materials = materials_for_uncolored_mesh(render_ctx, triangle_indices.len());
mesh::CpuMesh {
label: format!("{key:?}").into(),
triangle_indices,
vertex_positions,
vertex_normals,
vertex_colors: vec![re_renderer::Rgba32Unmul::BLACK; num_vertices],
vertex_texcoords: vec![glam::Vec2::ZERO; num_vertices],
materials,
}
}
ProcMeshKey::Capsule {
length,
subdivisions,
} => {
let mg_subdivisions = (subdivisions + 1) * 4;
let mut mg = re_math::MeshGen::new();
mg.push_capsule(
1.0,
length.into_inner(),
mg_subdivisions,
mg_subdivisions,
re_math::IsoTransform::from_quat(glam::Quat::from_rotation_x(
std::f32::consts::FRAC_PI_2,
)),
);
mesh_from_mesh_gen(format!("{key:?}").into(), mg, render_ctx)
}
};
mesh.sanity_check()?;
Ok(SolidMesh {
bbox: key.simple_bounding_box(),
gpu_mesh: Arc::new(GpuMesh::new(render_ctx, &mesh)?),
})
}
fn mesh_from_mesh_gen(
label: re_renderer::DebugLabel,
mg: MeshGen,
render_ctx: &RenderContext,
) -> mesh::CpuMesh {
let num_vertices = mg.positions.len();
let triangle_indices: Vec<glam::UVec3> = mg
.indices
.into_iter()
.tuples()
.map(|(i1, i2, i3)| uvec3(i1, i2, i3))
.collect();
let materials = materials_for_uncolored_mesh(render_ctx, triangle_indices.len());
mesh::CpuMesh {
label,
materials,
triangle_indices,
vertex_positions: mg.positions,
vertex_normals: mg.normals,
vertex_colors: vec![re_renderer::Rgba32Unmul::BLACK; num_vertices],
vertex_texcoords: vec![glam::Vec2::ZERO; num_vertices],
}
}
fn materials_for_uncolored_mesh(
render_ctx: &RenderContext,
num_triangles: usize,
) -> smallvec::SmallVec<[mesh::Material; 1]> {
smallvec![mesh::Material {
label: "default material".into(),
index_range: 0..(num_triangles * 3) as u32,
albedo: render_ctx
.texture_manager_2d
.white_texture_unorm_handle()
.clone(),
albedo_factor: re_renderer::Rgba::BLACK,
}]
}
#[derive(Clone, Copy, Debug, Default)]
struct OctahedronBase;
impl BaseShape for OctahedronBase {
fn initial_points(&self) -> Vec<Vec3A> {
vec![
Vec3A::NEG_X,
Vec3A::NEG_Y,
Vec3A::NEG_Z,
Vec3A::X,
Vec3A::Y,
Vec3A::Z,
]
}
fn triangles(&self) -> Box<[hexasphere::Triangle]> {
use hexasphere::Triangle;
const TRIANGLES: [Triangle; 8] = [
Triangle::new(0, 2, 1, 1, 4, 0), Triangle::new(0, 1, 5, 0, 6, 3), Triangle::new(0, 4, 2, 2, 5, 1), Triangle::new(0, 5, 4, 3, 7, 2), Triangle::new(3, 1, 2, 8, 4, 9), Triangle::new(3, 5, 1, 11, 6, 8), Triangle::new(3, 2, 4, 9, 5, 10), Triangle::new(3, 4, 5, 10, 7, 11), ];
Box::new(TRIANGLES)
}
const EDGES: usize = 12;
fn interpolate(&self, a: Vec3A, b: Vec3A, p: f32) -> Vec3A {
hexasphere::interpolation::geometric_slerp(a, b, p)
}
}