Skip to content

Commit

Permalink
Merge branch 'iced-rs:master' into ime_adavanced_text
Browse files Browse the repository at this point in the history
  • Loading branch information
KentaTheBugMaker authored Nov 15, 2023
2 parents 087bb59 + b474a2b commit e6450c4
Show file tree
Hide file tree
Showing 34 changed files with 2,192 additions and 15 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
17 changes: 17 additions & 0 deletions examples/custom_shader/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "custom_shader"
version = "0.1.0"
authors = ["Bingus <shankern@protonmail.com>"]
edition = "2021"

[dependencies]
iced.workspace = true
iced.features = ["debug", "advanced"]

image.workspace = true
bytemuck.workspace = true

glam.workspace = true
glam.features = ["bytemuck"]

rand = "0.8.5"
168 changes: 168 additions & 0 deletions examples/custom_shader/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
mod scene;

use scene::Scene;

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

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

struct IcedCubes {
start: Instant,
scene: Scene,
}

#[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>) {
(
Self {
start: Instant::now(),
scene: Scene::new(),
},
Command::none(),
)
}

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

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

Command::none()
}

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

let bottom_controls = row![
control(
"R",
slider(0.0..=1.0, self.scene.light_color.r, move |r| {
Message::LightColorChanged(Color {
r,
..self.scene.light_color
})
})
.step(0.01)
.width(100)
),
control(
"G",
slider(0.0..=1.0, self.scene.light_color.g, move |g| {
Message::LightColorChanged(Color {
g,
..self.scene.light_color
})
})
.step(0.01)
.width(100)
),
control(
"B",
slider(0.0..=1.0, self.scene.light_color.b, move |b| {
Message::LightColorChanged(Color {
b,
..self.scene.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(&self.scene).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()
}
186 changes: 186 additions & 0 deletions examples/custom_shader/src/scene.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
mod camera;
mod pipeline;

use camera::Camera;
use pipeline::Pipeline;

use crate::wgpu;
use pipeline::cube::{self, Cube};

use iced::mouse;
use iced::time::Duration;
use iced::widget::shader;
use iced::{Color, Rectangle, Size};

use glam::Vec3;
use rand::Rng;
use std::cmp::Ordering;
use std::iter;

pub const MAX: u32 = 500;

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

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

scene.change_amount(MAX);

scene
}

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

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

match amount.cmp(&curr_cubes) {
Ordering::Greater => {
// spawn
let cubes_2_spawn = (amount - 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 - amount;
let new_len = self.cubes.len() - cubes_2_cut as usize;
self.cubes.truncate(new_len);
}
Ordering::Equal => {}
}
}
}

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

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

/// A collection of `Cube`s that can be rendered.
#[derive(Debug)]
pub struct Primitive {
cubes: Vec<cube::Raw>,
uniforms: pipeline::Uniforms,
show_depth_buffer: bool,
}

impl Primitive {
pub fn new(
cubes: &[Cube],
camera: &Camera,
bounds: Rectangle,
show_depth_buffer: bool,
light_color: Color,
) -> Self {
let uniforms = pipeline::Uniforms::new(camera, bounds, light_color);

Self {
cubes: cubes
.iter()
.map(cube::Raw::from_cube)
.collect::<Vec<cube::Raw>>(),
uniforms,
show_depth_buffer,
}
}
}

impl shader::Primitive for Primitive {
fn prepare(
&self,
format: wgpu::TextureFormat,
device: &wgpu::Device,
queue: &wgpu::Queue,
target_size: Size<u32>,
_scale_factor: f32,
_transform: shader::Transformation,
storage: &mut shader::Storage,
) {
if !storage.has::<Pipeline>() {
storage.store(Pipeline::new(device, queue, format, target_size));
}

let pipeline = storage.get_mut::<Pipeline>().unwrap();

//upload data to GPU
pipeline.update(
device,
queue,
target_size,
&self.uniforms,
self.cubes.len(),
&self.cubes,
);
}

fn render(
&self,
storage: &shader::Storage,
bounds: Rectangle<u32>,
target: &wgpu::TextureView,
_target_size: Size<u32>,
encoder: &mut wgpu::CommandEncoder,
) {
//at this point our pipeline should always be initialized
let pipeline = storage.get::<Pipeline>().unwrap();

//render primitive
pipeline.render(
target,
encoder,
bounds,
self.cubes.len() as u32,
self.show_depth_buffer,
);
}
}

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),
)
}
Loading

0 comments on commit e6450c4

Please sign in to comment.