From 8d1e2f1b0c9aebe7b1e8cf73dea6a1e33ea2ae40 Mon Sep 17 00:00:00 2001 From: Artur Sapek Date: Wed, 1 Jan 2020 15:50:18 -0700 Subject: [PATCH] Implement Geometry2D primitive --- core/src/geometry.rs | 14 +- examples/custom_widget.rs | 35 +---- examples/geometry.rs | 227 ++++++++++++++++++++++++++++++++ wgpu/src/geometry.rs | 9 -- wgpu/src/primitive.rs | 4 +- wgpu/src/renderer.rs | 12 +- wgpu/src/shader/geom2d.vert | 3 +- wgpu/src/shader/geom2d.vert.spv | Bin 2160 -> 1468 bytes 8 files changed, 251 insertions(+), 53 deletions(-) create mode 100644 examples/geometry.rs diff --git a/core/src/geometry.rs b/core/src/geometry.rs index 2c8ecccd6e..f04ce42e79 100644 --- a/core/src/geometry.rs +++ b/core/src/geometry.rs @@ -1,18 +1,20 @@ -/// Hai +/// A two-dimensional vertex which has a color #[repr(C)] #[derive(Copy, Clone, Debug)] pub struct Vertex2D { - /// Hai + /// The vertex position pub position: [f32; 2], - /// Hai + /// The vertex color in rgba pub color: [f32; 4], } -/// Hai +/// A set of [`Vertex2D`] and indices for drawing some 2D geometry on the GPU. +/// +/// [`Vertex2D`]: struct.Vertex2D.html #[derive(Clone, Debug)] pub struct Geometry2D { - /// Hai + /// The vertices for this geometry pub vertices: Vec, - /// Hai + /// The indices for this geometry pub indices: Vec, } diff --git a/examples/custom_widget.rs b/examples/custom_widget.rs index 731d79f369..cf2f7792ed 100644 --- a/examples/custom_widget.rs +++ b/examples/custom_widget.rs @@ -10,8 +10,8 @@ mod circle { // if you wish to, by creating your own `Renderer` trait, which could be // implemented by `iced_wgpu` and other renderers. use iced_native::{ - layout, Background, Color, Element, Geometry2D, Hasher, Layout, Length, - MouseCursor, Point, Size, Vertex2D, Widget, + layout, Background, Color, Element, Hasher, Layout, Length, + MouseCursor, Point, Size, Widget, }; use iced_wgpu::{Primitive, Renderer}; @@ -57,34 +57,11 @@ mod circle { layout: Layout<'_>, _cursor_position: Point, ) -> (Primitive, MouseCursor) { - let bounds = layout.bounds(); - - dbg!(bounds); - ( - Primitive::Geometry2D { - bounds, - geometry: Geometry2D { - vertices: vec![ - Vertex2D { - position: [bounds.x, bounds.y], - color: [1.0, 0.0, 0.0, 1.0], - }, - Vertex2D { - position: [bounds.x + bounds.width, bounds.y + bounds.height], - color: [0.0, 1.0, 0.0, 1.0], - }, - Vertex2D { - position: [bounds.x, bounds.y + bounds.height], - color: [0.0, 0.0, 1.0, 1.0], - }, - Vertex2D { - position: [bounds.x + bounds.width, bounds.y], - color: [0.0, 1.0, 1.0, 1.0], - }, - ], - indices: vec![0, 1, 2, 0, 1, 3], - }, + Primitive::Quad { + bounds: layout.bounds(), + background: Background::Color(Color::BLACK), + border_radius: self.radius, }, MouseCursor::OutOfBounds, ) diff --git a/examples/geometry.rs b/examples/geometry.rs new file mode 100644 index 0000000000..feb7684e95 --- /dev/null +++ b/examples/geometry.rs @@ -0,0 +1,227 @@ +//! This example showcases a simple native custom widget that renders using +//! arbitrary low-level geometry. +mod rainbow { + // For now, to implement a custom native widget you will need to add + // `iced_native` and `iced_wgpu` to your dependencies. + // + // Then, you simply need to define your widget type and implement the + // `iced_native::Widget` trait with the `iced_wgpu::Renderer`. + // + // Of course, you can choose to make the implementation renderer-agnostic, + // if you wish to, by creating your own `Renderer` trait, which could be + // implemented by `iced_wgpu` and other renderers. + use iced_native::{ + layout, Element, Geometry2D, Hasher, Layout, Length, + MouseCursor, Point, Size, Vertex2D, Widget, + }; + use iced_wgpu::{Primitive, Renderer}; + + pub struct Rainbow { + dimen: u16, + } + + impl Rainbow { + pub fn new(dimen: u16) -> Self { + Self { dimen } + } + } + + impl Widget for Rainbow { + fn width(&self) -> Length { + Length::Shrink + } + + fn height(&self) -> Length { + Length::Shrink + } + + fn layout( + &self, + _renderer: &Renderer, + _limits: &layout::Limits, + ) -> layout::Node { + layout::Node::new(Size::new( + f32::from(self.dimen), + f32::from(self.dimen), + )) + } + + fn hash_layout(&self, state: &mut Hasher) { + use std::hash::Hash; + + self.dimen.hash(state); + } + + fn draw( + &self, + _renderer: &mut Renderer, + layout: Layout<'_>, + cursor_position: Point, + ) -> (Primitive, MouseCursor) { + let b = layout.bounds(); + + // R O Y G B I V + let color_r = [1.0, 0.0, 0.0, 1.0]; + let color_o = [1.0, 0.5, 0.0, 1.0]; + let color_y = [1.0, 1.0, 0.0, 1.0]; + let color_g = [0.0, 1.0, 0.0, 1.0]; + let color_gb = [0.0, 1.0, 0.5, 1.0]; + let color_b = [0.0, 0.2, 1.0, 1.0]; + let color_i = [0.5, 0.0, 1.0, 1.0]; + let color_v = [0.75, 0.0, 0.5, 1.0]; + + let posn_center = { + if b.contains(cursor_position) { + [cursor_position.x, cursor_position.y] + } else { + [b.x + (b.width / 2.0), b.y + (b.height / 2.0)] + } + }; + + let posn_tl = [b.x, b.y]; + let posn_t = [b.x + (b.width / 2.0), b.y]; + let posn_tr = [b.x + b.width, b.y]; + let posn_r = [b.x + b.width, b.y + (b.height / 2.0)]; + let posn_br = [b.x + b.width, b.y + b.height]; + let posn_b = [b.x + (b.width / 2.0), b.y + b.height]; + let posn_bl = [b.x, b.y + b.height]; + let posn_l = [b.x, b.y + (b.height / 2.0)]; + + ( + Primitive::Geometry2D { + geometry: Geometry2D { + vertices: vec![ + Vertex2D { + position: posn_center, + color: [1.0, 1.0, 1.0, 1.0], + }, + Vertex2D { + position: posn_tl, + color: color_r, + }, + Vertex2D { + position: posn_t, + color: color_o, + }, + Vertex2D { + position: posn_tr, + color: color_y, + }, + Vertex2D { + position: posn_r, + color: color_g, + }, + Vertex2D { + position: posn_br, + color: color_gb, + }, + Vertex2D { + position: posn_b, + color: color_b, + }, + Vertex2D { + position: posn_bl, + color: color_i, + }, + Vertex2D { + position: posn_l, + color: color_v, + }, + ], + indices: vec![ + 0, 1, 2, // TL + 0, 2, 3, // T + 0, 3, 4, // TR + 0, 4, 5, // R + 0, 5, 6, // BR + 0, 6, 7, // B + 0, 7, 8, // BL + 0, 8, 1, // L + ], + }, + }, + MouseCursor::OutOfBounds, + ) + } + } + + impl<'a, Message> Into> for Rainbow { + fn into(self) -> Element<'a, Message, Renderer> { + Element::new(self) + } + } +} + +use iced::{ + scrollable, settings::Window, Align, Column, Container, Element, + Length, Sandbox, Scrollable, Settings, Text, +}; +use rainbow::Rainbow; + +pub fn main() { + Example::run(Settings { + window: Window { + size: (660, 660), + resizable: true, + decorations: true, + }, + }) +} + +struct Example { + dimen: u16, + scroll: scrollable::State, +} + +#[derive(Debug, Clone, Copy)] +enum Message {} + +impl Sandbox for Example { + type Message = Message; + + fn new() -> Self { + Example { + dimen: 500, + scroll: scrollable::State::new(), + } + } + + fn title(&self) -> String { + String::from("Custom 2D geometry - Iced") + } + + fn update(&mut self, _: Message) {} + + fn view(&mut self) -> Element { + let content = Column::new() + .padding(20) + .spacing(20) + .max_width(500) + .align_items(Align::Start) + .push(Rainbow::new(self.dimen)) + .push( + Text::new(String::from("In this example we draw a custom widget Rainbow, using the \ + Geometry2D primitive. This primitive supplies a list of triangles, expressed as vertices and indices.")) + .width(Length::Shrink), + ) + .push( + Text::new(String::from("Move your cursor over it, and see the center vertex follow you!")) + .width(Length::Shrink), + ) + .push( + Text::new(String::from("Every Vertex2D defines its own color. You could use the \ + Geometry2D primitive to render virtually any two-dimensional geometry for your widget.")) + .width(Length::Shrink), + ); + + let scrollable = + Scrollable::new(&mut self.scroll).push(Container::new(content)); + + Container::new(scrollable) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } +} diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index a419c61a7a..f79e9c4333 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -131,8 +131,6 @@ impl Pipeline { geometries: &Vec, bounds: Rectangle, ) { - println!("draw 2d geom"); - let uniforms = Uniforms { transform: transformation.into(), scale, @@ -151,10 +149,6 @@ impl Pipeline { ); for geom in geometries { - - - - let vertices_buffer = device .create_buffer_mapped( geom.vertices.len(), @@ -199,9 +193,6 @@ impl Pipeline { bounds.height, ); - - println!("Drawing indexed geometry2d {:?}", geom.indices.len()); - render_pass.draw_indexed(0..geom.indices.len() as u32, 0, 0..1); } } diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index 73a3f05853..06121262d1 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -65,9 +65,7 @@ pub enum Primitive { }, /// A low-level geometry primitive Geometry2D { - /// The bounds of the geometry - bounds: Rectangle, - /// The vertices of the geometry + /// The vertices and indices of the geometry geometry: Geometry2D, }, } diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index 2f39c5302f..3cfd746d61 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -127,8 +127,6 @@ impl Renderer { Vector::new(0, 0), )); - dbg!(width, height); - self.draw_primitive(primitive, &mut layers); self.draw_overlay(overlay, &mut layers); @@ -254,7 +252,7 @@ impl Renderer { scale: [bounds.width, bounds.height], }); } - Primitive::Geometry2D { geometry, bounds } => { + Primitive::Geometry2D { geometry } => { layer.geometries.push(geometry.clone()); } Primitive::Clip { @@ -416,11 +414,17 @@ impl Renderer { } if layer.geometries.len() > 0 { + let translated = transformation + * Transformation::translate( + -(layer.offset.x as f32) * dpi, + -(layer.offset.y as f32) * dpi, + ); + self.geometry_pipeline.draw( &mut self.device, encoder, target, - transformation, + translated, dpi, &layer.geometries, bounds, diff --git a/wgpu/src/shader/geom2d.vert b/wgpu/src/shader/geom2d.vert index e762889c9d..fd86ecd60c 100644 --- a/wgpu/src/shader/geom2d.vert +++ b/wgpu/src/shader/geom2d.vert @@ -12,7 +12,6 @@ layout (set = 0, binding = 0) uniform Globals { void main() { vec2 p_Position = i_Position * u_Scale; - gl_Position = u_Transform * vec4(p_Position, 0.0, 1.0); + gl_Position = u_Transform * vec4(p_Position, 0.0, 1.0); o_Color = i_Color; } - diff --git a/wgpu/src/shader/geom2d.vert.spv b/wgpu/src/shader/geom2d.vert.spv index bac235720ec7462a516a38f85936e4c250e04b87..c15c74512f8fcbae271ee1c9f15c0fd7fac61046 100644 GIT binary patch literal 1468 zcmY+E%We}v5Jj8#kw732LLNYH?3j0mVF3sU@lueSMHWFuY%LQE8foH*{E!eUz5t0| z;;Yyoan5+!khW^7@2%>p>N_Ks=9=>%oCyp39){^!4s$ReEQV_C+XtP4)@b~x_4vsn zGnPUrPc-LZIGgXwJdb`*ti*2)|BC~aQ0Dh9w*q5@lkQ1vzUZ zr?@h`LTIPi$6h*OvTBZ@!le6t*c*&KXTxLIO3pjmq}w^_rG1!EST>PWA>%s!OWHl` z4?pyW&d%}#bK&pKIg5M^T}Y`Nxt>Xi!tv;Y%fht-X^1QZ*bI~#Z=1JBu!^A z=TRS*dZ<&@-OJK!m}50d3fv~H7B$SZ4^{_P1>5HtaWiL6bG+Pe>AyVhVcu)3%^aR} ze*37=Hs*@BF@<@bsE_lw>@8*RY+v9UUM0WYVC&*7~leqP`>v*$0sd*0bLcrM}P#5JDkBsc9@&Na1{ zy!F&r{p41`m+^JXo;w9zDX{ZgVQ;SW%-_J*F_-z`tC;6E!PoHS%GKDZU7XpNX5CG^ z8X0#kvEQ0poFnRN{uaD*csJwrXkxC2Z|3OxEA#KMx0b!FFZSKZ8GnFx<`@_67Ti<5 z7kjCx%QL@W{En^VJM+K5ck}|RKacsI-jJnctC%{gr8&;KhROdR$204g=j30~#hP-~ z@V<4-8tPyT@7}=b1-8aE=DByAvB#}~!_Bzc1?PPa>Fk~FVCEWAN45J&4P$pP`qog1zxH!8%r373s|rPipe_WGY!*VZ55 zG3ipVBR&`0nOIKiebfp%C=;SB(Z1+Fl+rUQ{#zR|F0JRkZ$=0aj+^zU7xaTj5x|HM zoWjz|RbZArv3JwH_AJk}rTVUZh*m|$?jhzZ8|4MPTFCdkcR%S{jLjO0G> zIx(l^gp0qGQWR9~TFM(9en!3%%%dtC{`hg|na9=JlQw2SZ=f zn?8XJbEe~-k_()8^qBsE%}Bo_o)eLC+3V9@r=H7_@wIsKlBtax_=8^+Irn47^lP#1 z+=@J@Z9#+&xRKeedn{=;8+|r^vs3e?@SKQ6PW0y@&afc;g*bJAElMhiTi>ujob9DJ z_k^9^pfm4a){AY)Ec|!G=>=y;uZZJIZ1$rwA7I$uijRoQj?TP*ZD=Nm#l5lE^G68Z z3bB|AeuHcd=sUj==v%VG&xx3gUG3tYW<=aA_lFNXm=%Hlln;C6MC=3KR#tL=SL4#RX8W0l zoWv#uzRU{qhmCpTTPTT`H@*?(hrPR^lGmBHSEA%Rl|3eQ(wjYx(HpDpwPbv;**yJH nUS{o$h&=dEOGUgY;@hPbYDLG7y4X__@$F&T_xfMm#-`{WD-3?|