forked from iced-rs/iced
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'iced-rs:master' into ime_adavanced_text
- Loading branch information
Showing
34 changed files
with
2,192 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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), | ||
) | ||
} |
Oops, something went wrong.