Add a bevy-texture example based on wgpu-texture (#4360)
* Add a bevy-texture example based on wgpu-texture * Use git branch of Blitz * Update existing code to new blitz * Use Device from Dioxus for bevy * Use ManualTextureView Signed-off-by: Nico Burns <nico@nicoburns.com> * Fix texture size on window resize * Clean code * Rename example to 'bevy-example' in 'bevy' directory * Upgrade to blitz 0.1.0-alpha.5 and anyrender 0.4 * Install alsa in CI Signed-off-by: Nico Burns <nico@nicoburns.com> * Add libudev to CI Signed-off-by: Nico Burns <nico@nicoburns.com> --------- Signed-off-by: Nico Burns <nico@nicoburns.com> Co-authored-by: Jerome Caucat <jerome.caucat@smartshape.com> Co-authored-by: Nico Burns <nico@nicoburns.com>
This commit is contained in:
14 files changed
+2426
-79
No files matched your search
@@ -73,7 +73,7 @@ jobs:
|
|||||||
uses: ./.github/actions/free-disk-space
|
uses: ./.github/actions/free-disk-space
|
||||||
- uses: awalsh128/cache-apt-pkgs-action@latest
|
- uses: awalsh128/cache-apt-pkgs-action@latest
|
||||||
with:
|
with:
|
||||||
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libasound2-dev libudev-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
||||||
version: 1.0
|
version: 1.0
|
||||||
- uses: dtolnay/rust-toolchain@1.86.0
|
- uses: dtolnay/rust-toolchain@1.86.0
|
||||||
with:
|
with:
|
||||||
@@ -94,7 +94,7 @@ jobs:
|
|||||||
uses: ./.github/actions/free-disk-space
|
uses: ./.github/actions/free-disk-space
|
||||||
- uses: awalsh128/cache-apt-pkgs-action@latest
|
- uses: awalsh128/cache-apt-pkgs-action@latest
|
||||||
with:
|
with:
|
||||||
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libasound2-dev libudev-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
||||||
version: 1.0
|
version: 1.0
|
||||||
- uses: dtolnay/rust-toolchain@1.86.0
|
- uses: dtolnay/rust-toolchain@1.86.0
|
||||||
with:
|
with:
|
||||||
@@ -127,7 +127,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: awalsh128/cache-apt-pkgs-action@latest
|
- uses: awalsh128/cache-apt-pkgs-action@latest
|
||||||
with:
|
with:
|
||||||
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libasound2-dev libudev-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
||||||
version: 1.0
|
version: 1.0
|
||||||
- name: Install Rust ${{ env.rust_nightly }}
|
- name: Install Rust ${{ env.rust_nightly }}
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
uses: dtolnay/rust-toolchain@nightly
|
||||||
@@ -150,7 +150,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: awalsh128/cache-apt-pkgs-action@latest
|
- uses: awalsh128/cache-apt-pkgs-action@latest
|
||||||
with:
|
with:
|
||||||
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libasound2-dev libudev-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
||||||
version: 1.0
|
version: 1.0
|
||||||
- uses: dtolnay/rust-toolchain@1.86.0
|
- uses: dtolnay/rust-toolchain@1.86.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
@@ -166,7 +166,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: awalsh128/cache-apt-pkgs-action@latest
|
- uses: awalsh128/cache-apt-pkgs-action@latest
|
||||||
with:
|
with:
|
||||||
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libasound2-dev libudev-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
||||||
version: 1.0
|
version: 1.0
|
||||||
- uses: dtolnay/rust-toolchain@1.86.0
|
- uses: dtolnay/rust-toolchain@1.86.0
|
||||||
with:
|
with:
|
||||||
@@ -318,7 +318,7 @@ jobs:
|
|||||||
- uses: awalsh128/cache-apt-pkgs-action@latest
|
- uses: awalsh128/cache-apt-pkgs-action@latest
|
||||||
if: ${{ matrix.platform.os == 'ubuntu-24.04' || matrix.platform.os == 'ubuntu-24.04-arm' }}
|
if: ${{ matrix.platform.os == 'ubuntu-24.04' || matrix.platform.os == 'ubuntu-24.04-arm' }}
|
||||||
with:
|
with:
|
||||||
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libasound2-dev libudev-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
||||||
version: ${{ matrix.platform.target }}-${{ matrix.platform.os }} # disambiguate since we're in a matrix and this caching action doesn't factor in these variables
|
version: ${{ matrix.platform.target }}-${{ matrix.platform.os }} # disambiguate since we're in a matrix and this caching action doesn't factor in these variables
|
||||||
|
|
||||||
- name: Install cross
|
- name: Install cross
|
||||||
|
|||||||
Generated
+1872
-58
File diff suppressed because it is too large.
Load diff
@@ -114,6 +114,7 @@ members = [
|
|||||||
"examples/fullstack-auth",
|
"examples/fullstack-auth",
|
||||||
"examples/fullstack-websockets",
|
"examples/fullstack-websockets",
|
||||||
"examples/wgpu-texture",
|
"examples/wgpu-texture",
|
||||||
|
"examples/bevy",
|
||||||
|
|
||||||
# Playwright tests
|
# Playwright tests
|
||||||
"packages/playwright-tests/liveview",
|
"packages/playwright-tests/liveview",
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "bevy-example"
|
||||||
|
version = "0.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["desktop"]
|
||||||
|
desktop = ["dioxus/desktop"]
|
||||||
|
native = ["dioxus/native"]
|
||||||
|
tracing = ["dep:tracing-subscriber", "dioxus-native/tracing"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy = { version = "0.16" }
|
||||||
|
dioxus-native = { path = "../../packages/native" }
|
||||||
|
dioxus = { workspace = true }
|
||||||
|
wgpu = "24"
|
||||||
|
color = "0.3"
|
||||||
|
tracing-subscriber = { workspace = true, optional = true }
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
use crate::bevy_scene_plugin::BevyScenePlugin;
|
||||||
|
use bevy::{
|
||||||
|
prelude::*,
|
||||||
|
render::{
|
||||||
|
camera::{ManualTextureView, ManualTextureViewHandle, ManualTextureViews, RenderTarget},
|
||||||
|
render_resource::TextureFormat,
|
||||||
|
renderer::{
|
||||||
|
RenderAdapter, RenderAdapterInfo, RenderDevice, RenderInstance, RenderQueue,
|
||||||
|
WgpuWrapper,
|
||||||
|
},
|
||||||
|
settings::{RenderCreation, RenderResources},
|
||||||
|
RenderPlugin,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use dioxus_native::{CustomPaintCtx, DeviceHandle, TextureHandle};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use wgpu::Instance;
|
||||||
|
|
||||||
|
#[derive(Resource, Default)]
|
||||||
|
pub struct UIData {
|
||||||
|
pub color: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BevyRenderer {
|
||||||
|
app: App,
|
||||||
|
wgpu_device: wgpu::Device,
|
||||||
|
last_texture_size: (u32, u32),
|
||||||
|
texture_handle: Option<TextureHandle>,
|
||||||
|
manual_texture_view_handle: Option<ManualTextureViewHandle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BevyRenderer {
|
||||||
|
pub fn new(instance: &Instance, device_handle: &DeviceHandle) -> Self {
|
||||||
|
// Create a headless Bevy App.
|
||||||
|
let mut app = App::new();
|
||||||
|
app.add_plugins(
|
||||||
|
DefaultPlugins
|
||||||
|
.set(RenderPlugin {
|
||||||
|
// Reuse the render resources from the Dioxus native renderer.
|
||||||
|
render_creation: RenderCreation::Manual(RenderResources(
|
||||||
|
RenderDevice::new(WgpuWrapper::new(device_handle.device.clone())),
|
||||||
|
RenderQueue(Arc::new(WgpuWrapper::new(device_handle.queue.clone()))),
|
||||||
|
RenderAdapterInfo(WgpuWrapper::new(device_handle.adapter.get_info())),
|
||||||
|
RenderAdapter(Arc::new(WgpuWrapper::new(device_handle.adapter.clone()))),
|
||||||
|
RenderInstance(Arc::new(WgpuWrapper::new(instance.clone()))),
|
||||||
|
)),
|
||||||
|
synchronous_pipeline_compilation: true,
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.set(WindowPlugin {
|
||||||
|
primary_window: None,
|
||||||
|
exit_condition: bevy::window::ExitCondition::DontExit,
|
||||||
|
close_when_requested: false,
|
||||||
|
})
|
||||||
|
.disable::<bevy::winit::WinitPlugin>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setup the rendering to texture.
|
||||||
|
app.insert_resource(ManualTextureViews::default());
|
||||||
|
|
||||||
|
// Add data from the UI.
|
||||||
|
app.insert_resource(UIData::default());
|
||||||
|
|
||||||
|
// Add the Bevy scene.
|
||||||
|
app.add_plugins(BevyScenePlugin {});
|
||||||
|
|
||||||
|
// Initialize the app to set up the render world properly.
|
||||||
|
app.finish();
|
||||||
|
app.cleanup();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
app,
|
||||||
|
wgpu_device: device_handle.device.clone(),
|
||||||
|
last_texture_size: (0, 0),
|
||||||
|
texture_handle: None,
|
||||||
|
manual_texture_view_handle: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(
|
||||||
|
&mut self,
|
||||||
|
ctx: CustomPaintCtx<'_>,
|
||||||
|
color: [f32; 3],
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
_start_time: &std::time::Instant,
|
||||||
|
) -> Option<TextureHandle> {
|
||||||
|
// Update the UI data.
|
||||||
|
if let Some(mut ui) = self.app.world_mut().get_resource_mut::<UIData>() {
|
||||||
|
ui.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init self.texture_handle if None or if width/height changed.
|
||||||
|
self.init_texture(ctx, width, height);
|
||||||
|
// Run one frame of the Bevy app to render the 3D scene.
|
||||||
|
self.app.update();
|
||||||
|
|
||||||
|
self.texture_handle
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_texture(&mut self, mut ctx: CustomPaintCtx<'_>, width: u32, height: u32) {
|
||||||
|
// Reuse self.texture_handle if already initialized to the correct size.
|
||||||
|
let current_size = (width, height);
|
||||||
|
if self.texture_handle.is_some() && self.last_texture_size == current_size {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let world = self.app.world_mut();
|
||||||
|
|
||||||
|
// Skip if no camera.
|
||||||
|
if world.query::<&Camera>().single(world).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mut manual_texture_views) = world.get_resource_mut::<ManualTextureViews>() {
|
||||||
|
// Clean previous texture if any.
|
||||||
|
if let Some(texture_handle) = self.texture_handle {
|
||||||
|
ctx.unregister_texture(texture_handle);
|
||||||
|
self.texture_handle = None;
|
||||||
|
}
|
||||||
|
if let Some(old_handle) = self.manual_texture_view_handle {
|
||||||
|
manual_texture_views.remove(&old_handle);
|
||||||
|
self.manual_texture_view_handle = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the texture for the camera target and the CustomPaintCtx.
|
||||||
|
let format = TextureFormat::Rgba8UnormSrgb;
|
||||||
|
let wgpu_texture = self.wgpu_device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format,
|
||||||
|
usage: wgpu::TextureUsages::TEXTURE_BINDING
|
||||||
|
| wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||||
|
| wgpu::TextureUsages::COPY_SRC,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
let wgpu_texture_view =
|
||||||
|
wgpu_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
let manual_texture_view = ManualTextureView {
|
||||||
|
texture_view: wgpu_texture_view.into(),
|
||||||
|
size: bevy::math::UVec2::new(width, height),
|
||||||
|
format,
|
||||||
|
};
|
||||||
|
let manual_texture_view_handle = ManualTextureViewHandle(0);
|
||||||
|
manual_texture_views.insert(manual_texture_view_handle, manual_texture_view);
|
||||||
|
|
||||||
|
if let Ok(mut camera) = world.query::<&mut Camera>().single_mut(world) {
|
||||||
|
camera.target = RenderTarget::TextureView(manual_texture_view_handle);
|
||||||
|
|
||||||
|
self.last_texture_size = current_size;
|
||||||
|
self.manual_texture_view_handle = Some(manual_texture_view_handle);
|
||||||
|
self.texture_handle = Some(ctx.register_texture(wgpu_texture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
use crate::bevy_renderer::UIData;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct DynamicColoredCube;
|
||||||
|
|
||||||
|
pub struct BevyScenePlugin {}
|
||||||
|
|
||||||
|
impl Plugin for BevyScenePlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.insert_resource(ClearColor(bevy::color::Color::srgba(0.0, 0.0, 0.0, 0.0)));
|
||||||
|
app.add_systems(Startup, setup);
|
||||||
|
app.add_systems(Update, (animate, update_cube_color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
commands.spawn((
|
||||||
|
Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
|
||||||
|
MeshMaterial3d(materials.add(StandardMaterial {
|
||||||
|
base_color: bevy::color::Color::srgb(1.0, 0.0, 0.0),
|
||||||
|
metallic: 0.0,
|
||||||
|
perceptual_roughness: 0.5,
|
||||||
|
..default()
|
||||||
|
})),
|
||||||
|
Transform::from_xyz(0.0, 0.0, 0.0),
|
||||||
|
DynamicColoredCube,
|
||||||
|
));
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
DirectionalLight {
|
||||||
|
color: bevy::color::Color::WHITE,
|
||||||
|
illuminance: 10000.0,
|
||||||
|
shadows_enabled: false,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
));
|
||||||
|
|
||||||
|
commands.insert_resource(AmbientLight {
|
||||||
|
color: bevy::color::Color::WHITE,
|
||||||
|
brightness: 100.0,
|
||||||
|
affects_lightmapped_meshes: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
Camera3d::default(),
|
||||||
|
Transform::from_xyz(0.0, 0.0, 3.0).looking_at(Vec3::new(0.0, 0.0, 0.0), Vec3::Y),
|
||||||
|
Name::new("MainCamera"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn animate(time: Res<Time>, mut cube_query: Query<&mut Transform, With<DynamicColoredCube>>) {
|
||||||
|
for mut transform in cube_query.iter_mut() {
|
||||||
|
transform.rotation = Quat::from_rotation_y(time.elapsed_secs());
|
||||||
|
transform.translation.x = (time.elapsed_secs() * 2.0).sin() * 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_cube_color(
|
||||||
|
ui: Res<UIData>,
|
||||||
|
cube_query: Query<&MeshMaterial3d<StandardMaterial>, With<DynamicColoredCube>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
if ui.is_changed() {
|
||||||
|
for mesh_material in cube_query.iter() {
|
||||||
|
if let Some(material) = materials.get_mut(&mesh_material.0) {
|
||||||
|
let [r, g, b] = ui.color;
|
||||||
|
material.base_color = bevy::color::Color::srgb(r, g, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
use crate::bevy_renderer::BevyRenderer;
|
||||||
|
use crate::Color;
|
||||||
|
use dioxus_native::{CustomPaintCtx, CustomPaintSource, DeviceHandle, TextureHandle};
|
||||||
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
use wgpu::Instance;
|
||||||
|
|
||||||
|
pub enum DemoMessage {
|
||||||
|
// Color in RGB format
|
||||||
|
SetColor(Color),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DemoRendererState {
|
||||||
|
Active(Box<BevyRenderer>),
|
||||||
|
Suspended,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DemoPaintSource {
|
||||||
|
state: DemoRendererState,
|
||||||
|
start_time: std::time::Instant,
|
||||||
|
tx: Sender<DemoMessage>,
|
||||||
|
rx: Receiver<DemoMessage>,
|
||||||
|
color: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DemoPaintSource {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
Self::with_channel(tx, rx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_channel(tx: Sender<DemoMessage>, rx: Receiver<DemoMessage>) -> Self {
|
||||||
|
Self {
|
||||||
|
state: DemoRendererState::Suspended,
|
||||||
|
start_time: std::time::Instant::now(),
|
||||||
|
tx,
|
||||||
|
rx,
|
||||||
|
color: Color::WHITE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sender(&self) -> Sender<DemoMessage> {
|
||||||
|
self.tx.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_messages(&mut self) {
|
||||||
|
loop {
|
||||||
|
match self.rx.try_recv() {
|
||||||
|
Err(_) => return,
|
||||||
|
Ok(msg) => match msg {
|
||||||
|
DemoMessage::SetColor(color) => self.color = color,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
ctx: CustomPaintCtx<'_>,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> Option<TextureHandle> {
|
||||||
|
if width == 0 || height == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let DemoRendererState::Active(state) = &mut self.state else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
state.render(ctx, self.color.components, width, height, &self.start_time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomPaintSource for DemoPaintSource {
|
||||||
|
fn resume(&mut self, instance: &Instance, device_handle: &DeviceHandle) {
|
||||||
|
let active_state = BevyRenderer::new(instance, device_handle);
|
||||||
|
self.state = DemoRendererState::Active(Box::new(active_state));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suspend(&mut self) {
|
||||||
|
self.state = DemoRendererState::Suspended;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
ctx: CustomPaintCtx<'_>,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
_scale: f64,
|
||||||
|
) -> Option<TextureHandle> {
|
||||||
|
self.process_messages();
|
||||||
|
self.render(ctx, width, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
use color::{palette::css::WHITE, parse_color};
|
||||||
|
use color::{OpaqueColor, Srgb};
|
||||||
|
use demo_renderer::{DemoMessage, DemoPaintSource};
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_native::use_wgpu;
|
||||||
|
use wgpu::Limits;
|
||||||
|
|
||||||
|
mod bevy_renderer;
|
||||||
|
mod bevy_scene_plugin;
|
||||||
|
mod demo_renderer;
|
||||||
|
|
||||||
|
// CSS Styles
|
||||||
|
static STYLES: Asset = asset!("./src/styles.css");
|
||||||
|
|
||||||
|
type Color = OpaqueColor<Srgb>;
|
||||||
|
|
||||||
|
fn limits() -> Limits {
|
||||||
|
Limits {
|
||||||
|
max_storage_buffers_per_shader_stage: 12,
|
||||||
|
..Limits::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
let config: Vec<Box<dyn Any>> = vec![Box::new(limits())];
|
||||||
|
dioxus_native::launch_cfg(app, Vec::new(), config);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app() -> Element {
|
||||||
|
let mut show_cube = use_signal(|| true);
|
||||||
|
|
||||||
|
let color_str = use_signal(|| String::from("red"));
|
||||||
|
let color = use_memo(move || {
|
||||||
|
parse_color(&color_str())
|
||||||
|
.map(|c| c.to_alpha_color())
|
||||||
|
.unwrap_or(WHITE)
|
||||||
|
.split()
|
||||||
|
.0
|
||||||
|
});
|
||||||
|
|
||||||
|
use_effect(move || println!("{:?}", color().components));
|
||||||
|
|
||||||
|
rsx!(
|
||||||
|
document::Link { rel: "stylesheet", href: STYLES }
|
||||||
|
div { id:"overlay",
|
||||||
|
h2 { "Control Panel" },
|
||||||
|
button {
|
||||||
|
onclick: move |_| show_cube.toggle(),
|
||||||
|
if show_cube() {
|
||||||
|
"Hide cube"
|
||||||
|
} else {
|
||||||
|
"Show cube"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
br {}
|
||||||
|
ColorControl { label: "Color:", color_str },
|
||||||
|
p { "This overlay demonstrates that the custom Bevy content can be rendered beneath layers of HTML content" }
|
||||||
|
}
|
||||||
|
div { id:"underlay",
|
||||||
|
h2 { "Underlay" },
|
||||||
|
p { "This underlay demonstrates that the custom Bevy content can be rendered above layers and blended with the content underneath" }
|
||||||
|
}
|
||||||
|
header {
|
||||||
|
h1 { "Blitz Bevy Demo" }
|
||||||
|
}
|
||||||
|
if show_cube() {
|
||||||
|
SpinningCube { color }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn ColorControl(label: &'static str, color_str: Signal<String>) -> Element {
|
||||||
|
rsx!(div {
|
||||||
|
class: "color-control",
|
||||||
|
{ label },
|
||||||
|
input {
|
||||||
|
value: color_str(),
|
||||||
|
oninput: move |evt| {
|
||||||
|
*color_str.write() = evt.value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn SpinningCube(color: Memo<Color>) -> Element {
|
||||||
|
// Create custom paint source and register it with the renderer
|
||||||
|
let paint_source = DemoPaintSource::new();
|
||||||
|
let sender = paint_source.sender();
|
||||||
|
let paint_source_id = use_wgpu(move || paint_source);
|
||||||
|
|
||||||
|
use_effect(move || {
|
||||||
|
sender.send(DemoMessage::SetColor(color())).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
rsx!(
|
||||||
|
div { id:"canvas-container",
|
||||||
|
canvas {
|
||||||
|
id: "demo-canvas",
|
||||||
|
"src": paint_source_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body, main {
|
||||||
|
height: 100%;
|
||||||
|
font-family: system-ui, sans;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 100px 1fr;
|
||||||
|
grid-template-columns: 100%;
|
||||||
|
background: #f4e8d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#canvas-container {
|
||||||
|
display: grid;
|
||||||
|
opacity: 0.8;
|
||||||
|
grid-row: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
padding: 10px 40px;
|
||||||
|
background-color: white;
|
||||||
|
z-index: 100;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#overlay {
|
||||||
|
position: absolute;
|
||||||
|
width: 33%;
|
||||||
|
height: 100%;
|
||||||
|
right: 0;
|
||||||
|
z-index: 10;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
padding-top: 40%;
|
||||||
|
padding-inline: 20px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#underlay {
|
||||||
|
position: absolute;
|
||||||
|
width: 33%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -10;
|
||||||
|
background-color: black;
|
||||||
|
padding-top: 40%;
|
||||||
|
padding-inline: 20px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-control {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
> input {
|
||||||
|
width: 150px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
use crate::Color;
|
use crate::Color;
|
||||||
use dioxus_native::{CustomPaintCtx, CustomPaintSource, TextureHandle};
|
use dioxus_native::{CustomPaintCtx, CustomPaintSource, DeviceHandle, TextureHandle};
|
||||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use wgpu::{Device, Queue};
|
use wgpu::Instance;
|
||||||
|
|
||||||
pub struct DemoPaintSource {
|
pub struct DemoPaintSource {
|
||||||
state: DemoRendererState,
|
state: DemoRendererState,
|
||||||
@@ -15,9 +15,9 @@ pub struct DemoPaintSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CustomPaintSource for DemoPaintSource {
|
impl CustomPaintSource for DemoPaintSource {
|
||||||
fn resume(&mut self, device: &Device, queue: &Queue) {
|
fn resume(&mut self, _instance: &Instance, device_handle: &DeviceHandle) {
|
||||||
// TODO: work out what to do about width/height
|
// TODO: work out what to do about width/height
|
||||||
let active_state = ActiveDemoRenderer::new(device, queue);
|
let active_state = ActiveDemoRenderer::new(device_handle);
|
||||||
self.state = DemoRendererState::Active(Box::new(active_state));
|
self.state = DemoRendererState::Active(Box::new(active_state));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +110,10 @@ impl DemoPaintSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveDemoRenderer {
|
impl ActiveDemoRenderer {
|
||||||
pub(crate) fn new(device: &Device, queue: &Queue) -> Self {
|
pub(crate) fn new(device_handle: &DeviceHandle) -> Self {
|
||||||
|
let device = &device_handle.device;
|
||||||
|
let queue = &device_handle.queue;
|
||||||
|
|
||||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!(
|
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!(
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ autofocus = []
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Blitz dependencies
|
# Blitz dependencies
|
||||||
anyrender = { version = "0.3", default-features = false }
|
anyrender = { version = "0.4", default-features = false }
|
||||||
anyrender_vello = { version = "0.3", default-features = false }
|
anyrender_vello = { version = "0.4", default-features = false }
|
||||||
blitz-dom = { version = "=0.1.0-alpha.4", default-features = false }
|
blitz-dom = { version = "=0.1.0-alpha.5", default-features = false }
|
||||||
blitz-net = { version = "=0.1.0-alpha.4", optional = true }
|
blitz-net = { version = "=0.1.0-alpha.5", optional = true }
|
||||||
blitz-paint = { version = "=0.1.0-alpha.4", optional = true }
|
blitz-paint = { version = "=0.1.0-alpha.5", optional = true }
|
||||||
blitz-traits = { version = "=0.1.0-alpha.4" }
|
blitz-traits = { version = "=0.1.0-alpha.5" }
|
||||||
blitz-shell = { version = "=0.1.0-alpha.4", default-features = false }
|
blitz-shell = { version = "=0.1.0-alpha.5", default-features = false }
|
||||||
|
|
||||||
# DioxusLabs dependencies
|
# DioxusLabs dependencies
|
||||||
dioxus-core = { workspace = true }
|
dioxus-core = { workspace = true }
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ use blitz_dom::{
|
|||||||
net::Resource, BaseDocument, Document, EventDriver, EventHandler, Node, DEFAULT_CSS,
|
net::Resource, BaseDocument, Document, EventDriver, EventHandler, Node, DEFAULT_CSS,
|
||||||
};
|
};
|
||||||
use blitz_traits::{
|
use blitz_traits::{
|
||||||
events::UiEvent, net::NetProvider, ColorScheme, DomEvent, DomEventData, EventState, Viewport,
|
events::{DomEvent, DomEventData, EventState, UiEvent},
|
||||||
|
net::NetProvider,
|
||||||
|
shell::{ColorScheme, Viewport},
|
||||||
};
|
};
|
||||||
|
|
||||||
use dioxus_core::{ElementId, Event, VirtualDom};
|
use dioxus_core::{ElementId, Event, VirtualDom};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use blitz_traits::{BlitzKeyEvent, BlitzMouseButtonEvent, MouseEventButton};
|
use blitz_traits::events::{BlitzKeyEvent, BlitzMouseButtonEvent, MouseEventButton};
|
||||||
use dioxus_html::{
|
use dioxus_html::{
|
||||||
geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint},
|
geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint},
|
||||||
input_data::{MouseButton, MouseButtonSet},
|
input_data::{MouseButton, MouseButtonSet},
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ mod dioxus_renderer;
|
|||||||
mod events;
|
mod events;
|
||||||
mod mutation_writer;
|
mod mutation_writer;
|
||||||
|
|
||||||
pub use anyrender_vello::{CustomPaintCtx, CustomPaintSource, TextureHandle};
|
pub use anyrender_vello::{
|
||||||
|
wgpu_context::DeviceHandle, CustomPaintCtx, CustomPaintSource, TextureHandle,
|
||||||
|
};
|
||||||
use assets::DioxusNativeNetProvider;
|
use assets::DioxusNativeNetProvider;
|
||||||
use blitz_dom::{ns, LocalName, Namespace, QualName};
|
use blitz_dom::{ns, LocalName, Namespace, QualName};
|
||||||
pub use dioxus_application::{DioxusNativeApplication, DioxusNativeEvent};
|
pub use dioxus_application::{DioxusNativeApplication, DioxusNativeEvent};
|
||||||
|
|||||||
Reference in new issue
Block a user