Skip to content

Commit

Permalink
Added support for custom shader widget for iced_wgpu backend.
Browse files Browse the repository at this point in the history
  • Loading branch information
bungoboingo committed Sep 14, 2023
1 parent 9245423 commit 8b25fce
Show file tree
Hide file tree
Showing 37 changed files with 2,131 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ maintenance = { status = "actively-developed" }
[features]
default = ["wgpu"]
# Enable the `wgpu` GPU-accelerated renderer backend
wgpu = ["iced_renderer/wgpu"]
wgpu = ["iced_renderer/wgpu", "iced_widget/wgpu"]
# Enables the `Image` widget
image = ["iced_widget/image", "dep:image"]
# Enables the `Svg` widget
Expand Down
11 changes: 11 additions & 0 deletions core/src/rectangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ impl From<Rectangle<u32>> for Rectangle<f32> {
}
}

impl From<Rectangle<f32>> for Rectangle<u32> {
fn from(rectangle: Rectangle<f32>) -> Self {
Rectangle {
x: rectangle.x as u32,
y: rectangle.y as u32,
width: rectangle.width as u32,
height: rectangle.height as u32,
}
}
}

impl<T> std::ops::Add<Vector<T>> for Rectangle<T>
where
T: std::ops::Add<Output = T>,
Expand Down
13 changes: 13 additions & 0 deletions examples/custom_shader/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "custom_shader"
version = "0.1.0"
authors = ["Bingus <[email protected]>"]
edition = "2021"

[dependencies]
iced = { path = "../..", features = ["debug", "advanced"]}
image = { version = "0.24.6"}
wgpu = "0.17"
bytemuck = { version = "1.13.1" }
glam = { version = "0.24.0", features = ["bytemuck"] }
rand = "0.8.5"
53 changes: 53 additions & 0 deletions examples/custom_shader/src/camera.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use glam::{mat4, vec3, vec4};
use iced::Rectangle;

#[derive(Copy, Clone)]
pub struct Camera {
eye: glam::Vec3,
target: glam::Vec3,
up: glam::Vec3,
fov_y: f32,
near: f32,
far: f32,
}

impl Default for Camera {
fn default() -> Self {
Self {
eye: vec3(0.0, 2.0, 3.0),
target: glam::Vec3::ZERO,
up: glam::Vec3::Y,
fov_y: 45.0,
near: 0.1,
far: 100.0,
}
}
}

pub const OPENGL_TO_WGPU_MATRIX: glam::Mat4 = mat4(
vec4(1.0, 0.0, 0.0, 0.0),
vec4(0.0, 1.0, 0.0, 0.0),
vec4(0.0, 0.0, 0.5, 0.0),
vec4(0.0, 0.0, 0.5, 1.0),
);

impl Camera {
pub fn build_view_proj_matrix(&self, bounds: Rectangle) -> glam::Mat4 {
//TODO looks distorted without padding; base on surface texture size instead?
let aspect_ratio = bounds.width / (bounds.height + 150.0);

let view = glam::Mat4::look_at_rh(self.eye, self.target, self.up);
let proj = glam::Mat4::perspective_rh(
self.fov_y,
aspect_ratio,
self.near,
self.far,
);

OPENGL_TO_WGPU_MATRIX * proj * view
}

pub fn position(&self) -> glam::Vec4 {
glam::Vec4::from((self.eye, 0.0))
}
}
99 changes: 99 additions & 0 deletions examples/custom_shader/src/cubes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use crate::camera::Camera;
use crate::primitive;
use crate::primitive::cube::Cube;
use glam::Vec3;
use iced::widget::shader;
use iced::{mouse, Color, Rectangle};
use rand::Rng;
use std::cmp::Ordering;
use std::iter;
use std::time::Duration;

pub const MAX: u32 = 500;

#[derive(Clone)]
pub struct Cubes {
pub size: f32,
pub cubes: Vec<Cube>,
pub camera: Camera,
pub show_depth_buffer: bool,
pub light_color: Color,
}

impl Cubes {
pub fn new() -> Self {
let mut cubes = Self {
size: 0.2,
cubes: vec![],
camera: Camera::default(),
show_depth_buffer: false,
light_color: Color::WHITE,
};

cubes.adjust_num_cubes(MAX);

cubes
}

pub fn update(&mut self, time: Duration) {
for cube in self.cubes.iter_mut() {
cube.update(self.size, time.as_secs_f32());
}
}

pub fn adjust_num_cubes(&mut self, num_cubes: u32) {
let curr_cubes = self.cubes.len() as u32;

match num_cubes.cmp(&curr_cubes) {
Ordering::Greater => {
// spawn
let cubes_2_spawn = (num_cubes - curr_cubes) as usize;

let mut cubes = 0;
self.cubes.extend(iter::from_fn(|| {
if cubes < cubes_2_spawn {
cubes += 1;
Some(Cube::new(self.size, rnd_origin()))
} else {
None
}
}));
}
Ordering::Less => {
// chop
let cubes_2_cut = curr_cubes - num_cubes;
let new_len = self.cubes.len() - cubes_2_cut as usize;
self.cubes.truncate(new_len);
}
_ => {}
}
}
}

impl<Message> shader::Program<Message> for Cubes {
type State = ();
type Primitive = primitive::Primitive;

fn draw(
&self,
_state: &Self::State,
_cursor: mouse::Cursor,
bounds: Rectangle,
) -> Self::Primitive {
primitive::Primitive::new(
&self.cubes,
&self.camera,
bounds,
self.show_depth_buffer,
self.light_color,
)
}
}

fn rnd_origin() -> Vec3 {
Vec3::new(
rand::thread_rng().gen_range(-4.0..4.0),
rand::thread_rng().gen_range(-4.0..4.0),
rand::thread_rng().gen_range(-4.0..2.0),
)
}
174 changes: 174 additions & 0 deletions examples/custom_shader/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
mod camera;
mod cubes;
mod pipeline;
mod primitive;

use crate::cubes::Cubes;
use iced::widget::{
checkbox, column, container, row, slider, text, vertical_space, Shader,
};
use iced::{
executor, window, Alignment, Application, Color, Command, Element, Length,
Renderer, Subscription, Theme,
};
use std::time::Instant;

fn main() -> iced::Result {
IcedCubes::run(iced::Settings::default())
}

struct IcedCubes {
start: Instant,
cubes: Cubes,
num_cubes_slider: u32,
}

impl Default for IcedCubes {
fn default() -> Self {
Self {
start: Instant::now(),
cubes: Cubes::new(),
num_cubes_slider: cubes::MAX,
}
}
}

#[derive(Debug, Clone)]
enum Message {
CubeAmountChanged(u32),
CubeSizeChanged(f32),
Tick(Instant),
ShowDepthBuffer(bool),
LightColorChanged(Color),
}

impl Application for IcedCubes {
type Executor = executor::Default;
type Message = Message;
type Theme = Theme;
type Flags = ();

fn new(_flags: Self::Flags) -> (Self, Command<Self::Message>) {
(IcedCubes::default(), Command::none())
}

fn title(&self) -> String {
"Iced Cubes".to_string()
}

fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
match message {
Message::CubeAmountChanged(num) => {
self.num_cubes_slider = num;
self.cubes.adjust_num_cubes(num);
}
Message::CubeSizeChanged(size) => {
self.cubes.size = size;
}
Message::Tick(time) => {
self.cubes.update(time - self.start);
}
Message::ShowDepthBuffer(show) => {
self.cubes.show_depth_buffer = show;
}
Message::LightColorChanged(color) => {
self.cubes.light_color = color;
}
}

Command::none()
}

fn view(&self) -> Element<'_, Self::Message, Renderer<Self::Theme>> {
let top_controls = row![
control(
"Amount",
slider(
1..=cubes::MAX,
self.num_cubes_slider,
Message::CubeAmountChanged
)
.width(100)
),
control(
"Size",
slider(0.1..=0.25, self.cubes.size, Message::CubeSizeChanged)
.step(0.01)
.width(100),
),
checkbox(
"Show Depth Buffer",
self.cubes.show_depth_buffer,
Message::ShowDepthBuffer
),
]
.spacing(40);

let bottom_controls = row![
control(
"R",
slider(0.0..=1.0, self.cubes.light_color.r, move |r| {
Message::LightColorChanged(Color {
r,
..self.cubes.light_color
})
})
.step(0.01)
.width(100)
),
control(
"G",
slider(0.0..=1.0, self.cubes.light_color.g, move |g| {
Message::LightColorChanged(Color {
g,
..self.cubes.light_color
})
})
.step(0.01)
.width(100)
),
control(
"B",
slider(0.0..=1.0, self.cubes.light_color.b, move |b| {
Message::LightColorChanged(Color {
b,
..self.cubes.light_color
})
})
.step(0.01)
.width(100)
)
]
.spacing(40);

let controls = column![top_controls, bottom_controls,]
.spacing(10)
.align_items(Alignment::Center);

let shader = Shader::new(&self.cubes)
.width(Length::Fill)
.height(Length::Fill);

container(
column![shader, controls, vertical_space(20),]
.spacing(40)
.align_items(Alignment::Center),
)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.into()
}

fn subscription(&self) -> Subscription<Self::Message> {
window::frames().map(Message::Tick)
}
}

fn control<'a>(
label: &'static str,
control: impl Into<Element<'a, Message>>,
) -> Element<'a, Message> {
row![text(label), control.into()].spacing(10).into()
}
Loading

0 comments on commit 8b25fce

Please sign in to comment.