use crate::color::conv::IntoLinSrgba;
use crate::draw::mesh::vertex::{self, Point, TexCoords, Vertex};
use crate::draw::primitive::Primitive;
use crate::draw::properties::spatial::{orientation, position};
use crate::draw::properties::{ColorScalar, LinSrgba, SetColor, SetOrientation, SetPosition};
use crate::draw::{self, Drawing};
use crate::geom;
use crate::math::BaseFloat;
use crate::wgpu;
use std::ops;
#[derive(Clone, Debug, Default)]
pub struct Vertexless;
#[derive(Clone, Debug)]
pub struct Mesh<S = geom::scalar::Default> {
position: position::Properties<S>,
orientation: orientation::Properties<S>,
vertex_range: ops::Range<usize>,
index_range: ops::Range<usize>,
vertex_mode: draw::renderer::VertexMode,
fill_color: Option<FillColor>,
texture_view: Option<wgpu::TextureView>,
}
#[derive(Clone, Debug, Default)]
struct FillColor(Option<LinSrgba>);
struct FlattenIndices<I> {
iter: I,
index: usize,
vertex_start_index: usize,
current: [usize; 3],
}
pub type DrawingMesh<'a, S> = Drawing<'a, Mesh<S>, S>;
impl Vertexless {
pub fn points_textured<S, I, P, T>(
self,
inner_mesh: &mut draw::Mesh<S>,
texture_view: &dyn wgpu::ToTextureView,
points: I,
) -> Mesh<S>
where
S: BaseFloat,
I: IntoIterator<Item = (P, T)>,
P: Into<Point<S>>,
T: Into<TexCoords<S>>,
{
let points = points.into_iter().map(|(p, t)| {
let point = p.into();
let color = vertex::DEFAULT_VERTEX_COLOR;
let tex_coords = t.into();
((point, color), tex_coords).into()
});
let vertex_mode = draw::renderer::VertexMode::Texture;
self.points_inner(
inner_mesh,
points,
vertex_mode,
Some(texture_view.to_texture_view()),
)
}
pub fn points_colored<S, I, P, C>(self, inner_mesh: &mut draw::Mesh<S>, points: I) -> Mesh<S>
where
S: BaseFloat,
I: IntoIterator<Item = (P, C)>,
P: Into<Point<S>>,
C: IntoLinSrgba<ColorScalar>,
{
let vertices = points.into_iter().map(|(p, c)| {
let point = p.into();
let color = c.into_lin_srgba();
let tex_coords = vertex::default_tex_coords();
((point, color), tex_coords).into()
});
let vertex_mode = draw::renderer::VertexMode::Color;
self.points_inner(inner_mesh, vertices, vertex_mode, None)
}
pub fn points<S, I>(self, inner_mesh: &mut draw::Mesh<S>, points: I) -> Mesh<S>
where
S: BaseFloat,
I: IntoIterator,
I::Item: Into<Point<S>>,
{
let vertices = points.into_iter().map(|p| {
let point = p.into();
let color = vertex::DEFAULT_VERTEX_COLOR;
let tex_coords = vertex::default_tex_coords();
((point, color), tex_coords).into()
});
let vertex_mode = draw::renderer::VertexMode::Color;
let mut mesh = self.points_inner(inner_mesh, vertices, vertex_mode, None);
mesh.fill_color = Some(FillColor(None));
mesh
}
fn points_inner<S, I>(
self,
inner_mesh: &mut draw::Mesh<S>,
vertices: I,
vertex_mode: draw::renderer::VertexMode,
texture_view: Option<wgpu::TextureView>,
) -> Mesh<S>
where
S: BaseFloat,
I: Iterator<Item = Vertex<S>>,
{
let v_start = inner_mesh.points().len();
let i_start = inner_mesh.indices().len();
for (i, vertex) in vertices.enumerate() {
inner_mesh.push_vertex(vertex);
inner_mesh.push_index((v_start + i) as u32);
}
let v_end = inner_mesh.points().len();
let i_end = inner_mesh.indices().len();
Mesh::new(v_start..v_end, i_start..i_end, vertex_mode, texture_view)
}
pub fn tris_textured<S, I, P, T>(
self,
inner_mesh: &mut draw::Mesh<S>,
texture_view: &dyn wgpu::ToTextureView,
tris: I,
) -> Mesh<S>
where
S: BaseFloat,
I: IntoIterator<Item = geom::Tri<(P, T)>>,
P: Into<Point<S>>,
T: Into<TexCoords<S>>,
{
let points = tris
.into_iter()
.map(|t| t.map_vertices(|(p, t)| (p.into(), t.into())))
.flat_map(geom::Tri::vertices);
self.points_textured(inner_mesh, texture_view, points)
}
pub fn tris_colored<S, I, P, C>(self, inner_mesh: &mut draw::Mesh<S>, tris: I) -> Mesh<S>
where
S: BaseFloat,
I: IntoIterator<Item = geom::Tri<(P, C)>>,
P: Into<Point<S>>,
C: IntoLinSrgba<ColorScalar>,
{
let points = tris
.into_iter()
.map(|t| t.map_vertices(|(p, c)| (p.into(), c.into_lin_srgba())))
.flat_map(geom::Tri::vertices);
self.points_colored(inner_mesh, points)
}
pub fn tris<S, I, V>(self, inner_mesh: &mut draw::Mesh<S>, tris: I) -> Mesh<S>
where
S: BaseFloat,
I: IntoIterator<Item = geom::Tri<V>>,
V: Into<Point<S>>,
{
let points = tris
.into_iter()
.map(|t| t.map_vertices(Into::into))
.flat_map(geom::Tri::vertices);
self.points(inner_mesh, points)
}
pub fn indexed_textured<S, V, I, P, T>(
self,
inner_mesh: &mut draw::Mesh<S>,
texture_view: &dyn wgpu::ToTextureView,
points: V,
indices: I,
) -> Mesh<S>
where
S: BaseFloat,
V: IntoIterator<Item = (P, T)>,
I: IntoIterator<Item = usize>,
P: Into<Point<S>>,
T: Into<TexCoords<S>>,
{
let vertices = points.into_iter().map(|(p, t)| {
let point = p.into();
let color = vertex::DEFAULT_VERTEX_COLOR;
let tex_coords = t.into();
((point, color), tex_coords).into()
});
let vertex_mode = draw::renderer::VertexMode::Texture;
self.indexed_inner(
inner_mesh,
vertices,
indices,
vertex_mode,
Some(texture_view.to_texture_view()),
)
}
pub fn indexed_colored<S, V, I, P, C>(
self,
inner_mesh: &mut draw::Mesh<S>,
points: V,
indices: I,
) -> Mesh<S>
where
S: BaseFloat,
V: IntoIterator<Item = (P, C)>,
I: IntoIterator<Item = usize>,
P: Into<Point<S>>,
C: IntoLinSrgba<ColorScalar>,
{
let vertices = points.into_iter().map(|(p, c)| {
let point = p.into();
let color = c.into_lin_srgba();
let tex_coords = vertex::default_tex_coords();
((point, color), tex_coords).into()
});
let vertex_mode = draw::renderer::VertexMode::Color;
self.indexed_inner(inner_mesh, vertices, indices, vertex_mode, None)
}
pub fn indexed<S, V, I>(self, inner_mesh: &mut draw::Mesh<S>, points: V, indices: I) -> Mesh<S>
where
S: BaseFloat,
V: IntoIterator,
V::Item: Into<Point<S>>,
I: IntoIterator<Item = usize>,
{
let vertices = points.into_iter().map(|p| {
let point = p.into();
let color = vertex::DEFAULT_VERTEX_COLOR;
let tex_coords = vertex::default_tex_coords();
((point, color), tex_coords).into()
});
let vertex_mode = draw::renderer::VertexMode::Color;
let mut mesh = self.indexed_inner(inner_mesh, vertices, indices, vertex_mode, None);
mesh.fill_color = Some(FillColor(None));
mesh
}
fn indexed_inner<S, V, I>(
self,
inner_mesh: &mut draw::Mesh<S>,
vertices: V,
indices: I,
vertex_mode: draw::renderer::VertexMode,
texture_view: Option<wgpu::TextureView>,
) -> Mesh<S>
where
S: BaseFloat,
V: IntoIterator<Item = Vertex<S>>,
I: IntoIterator<Item = usize>,
{
let v_start = inner_mesh.points().len();
let i_start = inner_mesh.indices().len();
inner_mesh.extend_vertices(vertices);
inner_mesh.extend_indices(indices.into_iter().map(|ix| ix as u32));
let v_end = inner_mesh.points().len();
let i_end = inner_mesh.indices().len();
Mesh::new(v_start..v_end, i_start..i_end, vertex_mode, texture_view)
}
}
impl<S> Mesh<S>
where
S: BaseFloat,
{
fn new(
vertex_range: ops::Range<usize>,
index_range: ops::Range<usize>,
vertex_mode: draw::renderer::VertexMode,
texture_view: Option<wgpu::TextureView>,
) -> Self {
let orientation = Default::default();
let position = Default::default();
let fill_color = None;
Mesh {
orientation,
position,
vertex_range,
index_range,
vertex_mode,
fill_color,
texture_view,
}
}
}
impl<'a, S> Drawing<'a, Vertexless, S>
where
S: BaseFloat,
{
pub fn points<I>(self, points: I) -> DrawingMesh<'a, S>
where
I: IntoIterator,
I::Item: Into<Point<S>>,
{
self.map_ty_with_context(|ty, ctxt| ty.points(ctxt.mesh, points))
}
pub fn points_colored<I, P, C>(self, points: I) -> DrawingMesh<'a, S>
where
I: IntoIterator<Item = (P, C)>,
P: Into<Point<S>>,
C: IntoLinSrgba<ColorScalar>,
{
self.map_ty_with_context(|ty, ctxt| ty.points_colored(ctxt.mesh, points))
}
pub fn points_textured<I, P, T>(
self,
view: &dyn wgpu::ToTextureView,
points: I,
) -> DrawingMesh<'a, S>
where
I: IntoIterator<Item = (P, T)>,
P: Into<Point<S>>,
T: Into<TexCoords<S>>,
{
self.map_ty_with_context(|ty, ctxt| ty.points_textured(ctxt.mesh, view, points))
}
pub fn tris<I, V>(self, tris: I) -> DrawingMesh<'a, S>
where
I: IntoIterator<Item = geom::Tri<V>>,
V: Into<Point<S>>,
{
self.map_ty_with_context(|ty, ctxt| ty.tris(ctxt.mesh, tris))
}
pub fn tris_colored<I, P, C>(self, tris: I) -> DrawingMesh<'a, S>
where
I: IntoIterator<Item = geom::Tri<(P, C)>>,
P: Into<Point<S>>,
C: IntoLinSrgba<ColorScalar>,
{
self.map_ty_with_context(|ty, ctxt| ty.tris_colored(ctxt.mesh, tris))
}
pub fn tris_textured<I, P, T>(
self,
view: &dyn wgpu::ToTextureView,
tris: I,
) -> DrawingMesh<'a, S>
where
I: IntoIterator<Item = geom::Tri<(P, T)>>,
P: Into<Point<S>>,
T: Into<TexCoords<S>>,
{
self.map_ty_with_context(|ty, ctxt| ty.tris_textured(ctxt.mesh, view, tris))
}
pub fn indexed<V, I>(self, points: V, indices: I) -> DrawingMesh<'a, S>
where
V: IntoIterator,
V::Item: Into<Point<S>>,
I: IntoIterator<Item = usize>,
{
self.map_ty_with_context(|ty, ctxt| ty.indexed(ctxt.mesh, points, indices))
}
pub fn indexed_colored<V, I, P, C>(self, points: V, indices: I) -> DrawingMesh<'a, S>
where
V: IntoIterator<Item = (P, C)>,
I: IntoIterator<Item = usize>,
P: Into<Point<S>>,
C: IntoLinSrgba<ColorScalar>,
{
self.map_ty_with_context(|ty, ctxt| ty.indexed_colored(ctxt.mesh, points, indices))
}
pub fn indexed_textured<V, I, P, T>(
self,
view: &dyn wgpu::ToTextureView,
points: V,
indices: I,
) -> DrawingMesh<'a, S>
where
V: IntoIterator<Item = (P, T)>,
I: IntoIterator<Item = usize>,
P: Into<Point<S>>,
T: Into<TexCoords<S>>,
{
self.map_ty_with_context(|ty, ctxt| ty.indexed_textured(ctxt.mesh, view, points, indices))
}
}
impl draw::renderer::RenderPrimitive for Mesh<f32> {
fn render_primitive(
self,
ctxt: draw::renderer::RenderContext,
mesh: &mut draw::Mesh,
) -> draw::renderer::PrimitiveRender {
let Mesh {
orientation,
position,
vertex_range,
index_range,
vertex_mode,
fill_color,
texture_view,
} = self;
let global_transform = ctxt.transform;
let local_transform = position.transform() * orientation.transform();
let transform = global_transform * local_transform;
let old_mesh_vertex_start = vertex_range.start as u32;
let new_mesh_vertex_start = mesh.raw_vertex_count() as u32;
let indices = index_range
.map(|i| ctxt.intermediary_mesh.indices()[i])
.map(|i| new_mesh_vertex_start + i - old_mesh_vertex_start);
let transform_point = |p: geom::Point3<f32>| -> geom::Point3<f32> {
let p = cgmath::Point3::new(p.x, p.y, p.z);
let p = cgmath::Transform::transform_point(&transform, p);
p.into()
};
match fill_color {
Some(fill) => {
let theme_prim = draw::theme::Primitive::Mesh;
let color = fill
.0
.unwrap_or_else(|| ctxt.theme.fill_lin_srgba(&theme_prim));
let vertices = vertex_range.map(|i| {
let point = transform_point(ctxt.intermediary_mesh.points()[i]);
let tex_coords = ctxt.intermediary_mesh.tex_coords()[i];
((point, color), tex_coords).into()
});
mesh.extend(vertices, indices);
}
None => {
let vertices = vertex_range.map(|i| {
let point = transform_point(ctxt.intermediary_mesh.points()[i]);
let color = ctxt.intermediary_mesh.colors()[i];
let tex_coords = ctxt.intermediary_mesh.tex_coords()[i];
((point, color), tex_coords).into()
});
mesh.extend(vertices, indices);
}
}
draw::renderer::PrimitiveRender {
texture_view,
vertex_mode,
}
}
}
impl<I> Iterator for FlattenIndices<I>
where
I: Iterator<Item = [usize; 3]>,
{
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.index < self.current.len() {
let ix = self.current[self.index];
self.index += 1;
return Some(self.vertex_start_index + ix);
}
match self.iter.next() {
None => return None,
Some(trio) => {
self.current = trio;
self.index = 0;
}
}
}
}
}
impl<S> SetOrientation<S> for Mesh<S> {
fn properties(&mut self) -> &mut orientation::Properties<S> {
SetOrientation::properties(&mut self.orientation)
}
}
impl<S> SetPosition<S> for Mesh<S> {
fn properties(&mut self) -> &mut position::Properties<S> {
SetPosition::properties(&mut self.position)
}
}
impl<S> SetColor<ColorScalar> for Mesh<S> {
fn rgba_mut(&mut self) -> &mut Option<LinSrgba> {
&mut self.fill_color.get_or_insert_with(Default::default).0
}
}
impl<S> From<Vertexless> for Primitive<S> {
fn from(prim: Vertexless) -> Self {
Primitive::MeshVertexless(prim)
}
}
impl<S> From<Mesh<S>> for Primitive<S> {
fn from(prim: Mesh<S>) -> Self {
Primitive::Mesh(prim)
}
}
impl<S> Into<Option<Vertexless>> for Primitive<S> {
fn into(self) -> Option<Vertexless> {
match self {
Primitive::MeshVertexless(prim) => Some(prim),
_ => None,
}
}
}
impl<S> Into<Option<Mesh<S>>> for Primitive<S> {
fn into(self) -> Option<Mesh<S>> {
match self {
Primitive::Mesh(prim) => Some(prim),
_ => None,
}
}
}