Skip to content

Commit

Permalink
Implement RenderPrimitive2 for Text, Mesh, Texture
Browse files Browse the repository at this point in the history
  • Loading branch information
dzil123 committed Jul 28, 2021
1 parent 8b81e71 commit 6a2f3da
Show file tree
Hide file tree
Showing 8 changed files with 314 additions and 260 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod render;

use crate::color::conv::IntoLinSrgba;
use crate::draw::mesh::vertex::{self, Point, TexCoords, Vertex};
use crate::draw::primitive::Primitive;
Expand All @@ -6,6 +8,7 @@ use crate::draw::properties::{ColorScalar, LinSrgba, SetColor, SetOrientation, S
use crate::draw::{self, Drawing};
use crate::geom;
use crate::wgpu;
pub use render::render_mesh;
use std::ops;

/// The mesh type prior to being initialised with vertices or indices.
Expand Down Expand Up @@ -481,69 +484,6 @@ impl<'a> Drawing<'a, Vertexless> {
}
}

impl draw::renderer::RenderPrimitive for Mesh {
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;

// Determine the transform to apply to vertices.
let global_transform = *ctxt.transform;
let local_transform = position.transform() * orientation.transform();
let transform = global_transform * local_transform;

// We need to update the indices to point to where vertices will be in the new mesh.
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);

// A small function for transforming a point via the transform matrix.
let transform_point = |p: geom::Point3| -> geom::Point3 { transform.transform_point3(p) };

// Color the vertices based on whether or not we should fill, then extend the mesh!
match fill_color {
Some(fill) => {
let theme_primitive = draw::theme::Primitive::Mesh;
let color =
ctxt.theme
.resolve_color(fill.0, theme_primitive, draw::theme::ColorType::Fill);
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]>,
Expand Down
81 changes: 81 additions & 0 deletions nannou/src/draw/primitive/mesh/render.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use crate::draw;
use crate::draw::primitive::mesh::{FillColor, Mesh};
use crate::draw::properties::LinSrgba;
use crate::draw::theme::ColorType;
use crate::geom;
use crate::glam::Mat4;

pub fn render_mesh(
transform: Mat4,
vertex_range: std::ops::Range<usize>,
index_range: std::ops::Range<usize>,
fill_color: Option<LinSrgba>,
intermediary_mesh: &draw::Mesh,
mesh: &mut draw::Mesh,
) {
// We need to update the indices to point to where vertices will be in the new mesh.
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| intermediary_mesh.indices()[i])
.map(|i| new_mesh_vertex_start + i - old_mesh_vertex_start);

// A small function for transforming a point via the transform matrix.
let transform_point = |p: geom::Point3| -> geom::Point3 { transform.transform_point3(p) };

// Color the vertices based on whether or not we should fill, then extend the mesh!
match fill_color {
Some(color) => {
let vertices = vertex_range.map(|i| {
let point = transform_point(intermediary_mesh.points()[i]);
let tex_coords = 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(intermediary_mesh.points()[i]);
let color = intermediary_mesh.colors()[i];
let tex_coords = intermediary_mesh.tex_coords()[i];
((point, color), tex_coords).into()
});
mesh.extend(vertices, indices);
}
}
}

impl draw::renderer::RenderPrimitive2 for Mesh {
fn render_primitive<R>(
self,
ctxt: draw::renderer::RenderContext2,
mut renderer: R,
) -> draw::renderer::PrimitiveRender
where
R: draw::renderer::PrimitiveRenderer,
{
let Mesh {
orientation,
position,
vertex_range,
index_range,
vertex_mode,
fill_color,
texture_view,
} = self;

let theme_primitive = draw::theme::Primitive::Mesh;
let fill_color = fill_color.map(|FillColor(color)| {
ctxt.theme
.resolve_color(color, theme_primitive, ColorType::Fill)
});
let local_transform = position.transform() * orientation.transform();

renderer.mesh(local_transform, vertex_range, index_range, fill_color);

draw::renderer::PrimitiveRender {
texture_view,
vertex_mode,
}
}
}
2 changes: 1 addition & 1 deletion nannou/src/draw/primitive/path/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub mod render;

pub(crate) use self::render::{render_path_points_textured, PathEventSource};
pub(crate) use self::render::PathEventSource;
use crate::color::conv::IntoLinSrgba;
use crate::color::LinSrgba;
use crate::draw::mesh::vertex::TexCoords;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
pub mod render;

pub use self::render::render_text;
use crate::color::conv::IntoLinSrgba;
use crate::draw::drawing::DrawingContext;
use crate::draw::primitive::Primitive;
use crate::draw::properties::spatial::{self, dimension, orientation, position};
use crate::draw::properties::{
ColorScalar, LinSrgba, SetColor, SetDimensions, SetOrientation, SetPosition,
};
use crate::draw::{self, theme, Drawing};
use crate::geom::{self, Point2};
use crate::draw::Drawing;
use crate::text::{self, Align, Font, FontSize, Justify, Layout, Scalar, Wrap};

/// Properties related to drawing the **Text** primitive.
Expand Down Expand Up @@ -259,165 +261,6 @@ impl<'a> DrawingText<'a> {
}
}

impl draw::renderer::RenderPrimitive for Text {
fn render_primitive(
self,
ctxt: draw::renderer::RenderContext,
mesh: &mut draw::Mesh,
) -> draw::renderer::PrimitiveRender {
let Text {
spatial,
style,
text,
} = self;
let Style {
color,
glyph_colors,
layout,
} = style;
let layout = layout.build();
let (maybe_x, maybe_y, maybe_z) = (
spatial.dimensions.x,
spatial.dimensions.y,
spatial.dimensions.z,
);
assert!(
maybe_z.is_none(),
"z dimension support for text is unimplemented"
);
let w = maybe_x.unwrap_or(200.0);
let h = maybe_y.unwrap_or(200.0);
let rect: geom::Rect = geom::Rect::from_wh([w, h].into());
let color = ctxt
.theme
.resolve_color(color, theme::Primitive::Text, theme::ColorType::Fill);

let text_str = &ctxt.text_buffer[text.clone()];
let text = text::text(text_str).layout(&layout).build(rect);

// Queue the glyphs to be cached
let font_id = text::font::id(text.font());
let positioned_glyphs: Vec<_> = text
.rt_glyphs(
ctxt.output_attachment_size,
ctxt.output_attachment_scale_factor,
)
.collect();
for glyph in positioned_glyphs.iter() {
ctxt.glyph_cache.queue_glyph(font_id.index(), glyph.clone());
}

// Cache the enqueued glyphs within the pixel buffer.
let (glyph_cache_w, _) = ctxt.glyph_cache.dimensions();
{
let draw::renderer::RenderContext {
glyph_cache:
&mut draw::renderer::GlyphCache {
ref mut cache,
ref mut pixel_buffer,
ref mut requires_upload,
..
},
..
} = ctxt;
let glyph_cache_w = glyph_cache_w as usize;
let res = cache.cache_queued(|rect, data| {
let width = (rect.max.x - rect.min.x) as usize;
let height = (rect.max.y - rect.min.y) as usize;
let mut dst_ix = rect.min.y as usize * glyph_cache_w + rect.min.x as usize;
let mut src_ix = 0;
for _ in 0..height {
let dst_range = dst_ix..dst_ix + width;
let src_range = src_ix..src_ix + width;
let dst_slice = &mut pixel_buffer[dst_range];
let src_slice = &data[src_range];
dst_slice.copy_from_slice(src_slice);
dst_ix += glyph_cache_w;
src_ix += width;
}
*requires_upload = true;
});
if let Err(err) = res {
eprintln!("failed to cache queued glyphs: {}", err);
}
}

// Determine the transform to apply to all points.
let global_transform = *ctxt.transform;
let local_transform = spatial.position.transform() * spatial.orientation.transform();
let transform = global_transform * local_transform;

// A function for converting RustType rects to nannou rects.
let scale_factor = ctxt.output_attachment_scale_factor;
let (out_w, out_h) = ctxt.output_attachment_size.into();
let [half_out_w, half_out_h] = [out_w as f32 / 2.0, out_h as f32 / 2.0];
let to_nannou_rect = |screen_rect: text::rt::Rect<i32>| {
let l = screen_rect.min.x as f32 / scale_factor - half_out_w;
let r = screen_rect.max.x as f32 / scale_factor - half_out_w;
let t = -(screen_rect.min.y as f32 / scale_factor - half_out_h);
let b = -(screen_rect.max.y as f32 / scale_factor - half_out_h);
geom::Rect::from_corners([l, b].into(), [r, t].into())
};

// Skips non-rendered colors (e.g. due to line breaks),
// assuming LineInfos are ordered by ascending character position.
let glyph_colors_iter = text
.line_infos()
.iter()
.flat_map(|li| li.char_range())
.take_while(|&i| i < glyph_colors.len())
.map(|i| &glyph_colors[i])
// Repeat `color` if more glyphs than glyph_colors
.chain(std::iter::repeat(&color));

// Extend the mesh with a rect for each displayed glyph.
for (g, g_color) in positioned_glyphs.iter().zip(glyph_colors_iter) {
if let Ok(Some((uv_rect, screen_rect))) = ctxt.glyph_cache.rect_for(font_id.index(), &g)
{
let rect = to_nannou_rect(screen_rect);

// Create a mesh-compatible vertex from the position and tex_coords.
let v = |p: Point2, tex_coords: [f32; 2]| -> draw::mesh::Vertex {
let p = transform.transform_point3([p.x, p.y, 0.0].into());
let point = draw::mesh::vertex::Point::from(p);
draw::mesh::vertex::new(point, g_color.to_owned(), tex_coords.into())
};

// The sides of the UV rect.
let uv_l = uv_rect.min.x;
let uv_t = uv_rect.min.y;
let uv_r = uv_rect.max.x;
let uv_b = uv_rect.max.y;

// Insert the vertices.
let bottom_left = v(rect.bottom_left(), [uv_l, uv_b]);
let bottom_right = v(rect.bottom_right(), [uv_r, uv_b]);
let top_left = v(rect.top_left(), [uv_l, uv_t]);
let top_right = v(rect.top_right(), [uv_r, uv_t]);
let start_ix = mesh.points().len() as u32;
mesh.push_vertex(top_left);
mesh.push_vertex(bottom_left);
mesh.push_vertex(bottom_right);
mesh.push_vertex(top_right);

// Now the indices.
let tl_ix = start_ix;
let bl_ix = start_ix + 1;
let br_ix = start_ix + 2;
let tr_ix = start_ix + 3;
mesh.push_index(tl_ix);
mesh.push_index(bl_ix);
mesh.push_index(br_ix);
mesh.push_index(tl_ix);
mesh.push_index(br_ix);
mesh.push_index(tr_ix);
}
}

draw::renderer::PrimitiveRender::text()
}
}

impl SetOrientation for Text {
fn properties(&mut self) -> &mut orientation::Properties {
SetOrientation::properties(&mut self.spatial)
Expand Down
Loading

0 comments on commit 6a2f3da

Please sign in to comment.