Spinning Cube
This commit is contained in:
59
mini-game/assets/shaders/post_processing.wgsl
Normal file
59
mini-game/assets/shaders/post_processing.wgsl
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#import bevy_core_pipeline::fullscreen_vertex_shader::FullscreenVertexOutput
|
||||||
|
|
||||||
|
struct PostProcessSettings {
|
||||||
|
scale: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0) var screen_texture: texture_2d<f32>;
|
||||||
|
@group(0) @binding(1) var texture_sampler: sampler;
|
||||||
|
@group(0) @binding(2) var<uniform> settings: PostProcessSettings;
|
||||||
|
|
||||||
|
const dither = array<array<f32, 8>, 8>(
|
||||||
|
array<f32, 8>( 0.0, 32.0, 8.0, 40.0, 2.0, 34.0, 10.0, 42.0),
|
||||||
|
array<f32, 8>(48.0, 16.0, 56.0, 24.0, 50.0, 18.0, 58.0, 26.0),
|
||||||
|
array<f32, 8>(12.0, 44.0, 4.0, 36.0, 14.0, 46.0, 6.0, 38.0),
|
||||||
|
array<f32, 8>(60.0, 28.0, 52.0, 20.0, 62.0, 30.0, 54.0, 22.0),
|
||||||
|
array<f32, 8>( 3.0, 35.0, 11.0, 43.0, 1.0, 33.0, 9.0, 41.0),
|
||||||
|
array<f32, 8>(51.0, 19.0, 59.0, 27.0, 49.0, 17.0, 57.0, 25.0),
|
||||||
|
array<f32, 8>(15.0, 47.0, 7.0, 39.0, 13.0, 45.0, 5.0, 37.0),
|
||||||
|
array<f32, 8>(63.0, 31.0, 55.0, 23.0, 61.0, 29.0, 53.0, 21.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
fn find_closest(x: i32, y: i32, c0: f32) -> f32 {
|
||||||
|
|
||||||
|
|
||||||
|
var limit = 0.0;
|
||||||
|
|
||||||
|
if (x < 8) {
|
||||||
|
limit = (dither[x][y]+1) / 64.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c0 < limit) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
let a: f32 = textureSample(screen_texture, texture_sampler, in.uv).a;
|
||||||
|
|
||||||
|
let gr: f32 = textureSample(screen_texture, texture_sampler, in.uv).r * 0.2989;
|
||||||
|
let gg: f32 = textureSample(screen_texture, texture_sampler, in.uv).g * 0.587;
|
||||||
|
let gb: f32 = textureSample(screen_texture, texture_sampler, in.uv).b * 0.114;
|
||||||
|
let grayscale: f32 = gr + gg + gb;
|
||||||
|
|
||||||
|
let uvx: f32 = in.position.x;
|
||||||
|
let uvy: f32 = in.position.y;
|
||||||
|
|
||||||
|
let x: i32 = i32(uvx * settings.scale) % 8;
|
||||||
|
let y: i32 = i32(uvy * settings.scale) % 8;
|
||||||
|
|
||||||
|
let final_gray = find_closest(x, y, grayscale); /// not even used?!
|
||||||
|
|
||||||
|
// Sample each color channel with an arbitrary shift
|
||||||
|
return vec4<f32>(
|
||||||
|
final_gray, final_gray, final_gray, a
|
||||||
|
);
|
||||||
|
}
|
||||||
182
mini-game/src/post_process.rs
Normal file
182
mini-game/src/post_process.rs
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
use bevy::core_pipeline::core_3d::graph::Core3d;
|
||||||
|
use bevy::ecs::query::QueryItem;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::render::extract_component::{
|
||||||
|
DynamicUniformIndex, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
|
||||||
|
};
|
||||||
|
use bevy::render::render_graph::{
|
||||||
|
NodeRunError, RenderGraphContext, RenderLabel, ViewNode, ViewNodeRunner,
|
||||||
|
};
|
||||||
|
use bevy::render::render_resource::{
|
||||||
|
BindGroupEntries, PipelineCache, RenderPipelineDescriptor, ShaderType, TextureFormat,
|
||||||
|
};
|
||||||
|
use bevy::render::renderer::RenderContext;
|
||||||
|
use bevy::render::view::ViewTarget;
|
||||||
|
use bevy::render::{RenderApp, RenderStartup};
|
||||||
|
|
||||||
|
use bevy::{
|
||||||
|
core_pipeline::{FullscreenShader, core_3d::graph::Node3d},
|
||||||
|
render::{
|
||||||
|
extract_component::ComponentUniforms,
|
||||||
|
render_graph::RenderGraphExt,
|
||||||
|
render_resource::{
|
||||||
|
binding_types::{sampler, texture_2d, uniform_buffer},
|
||||||
|
*,
|
||||||
|
},
|
||||||
|
renderer::RenderDevice,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const SHADER_ASSET_PATH: &str = "shaders/post_processing.wgsl";
|
||||||
|
|
||||||
|
/// It is generally encouraged to set up post processing effects as a plugin
|
||||||
|
pub struct PostProcessPlugin;
|
||||||
|
|
||||||
|
impl Plugin for PostProcessPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_plugins((
|
||||||
|
ExtractComponentPlugin::<PostProcessSettings>::default(),
|
||||||
|
UniformComponentPlugin::<PostProcessSettings>::default(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
render_app.add_systems(RenderStartup, init_post_process_pipeline);
|
||||||
|
|
||||||
|
render_app
|
||||||
|
.add_render_graph_node::<ViewNodeRunner<PostProcessNode>>(Core3d, PostProcessLabel)
|
||||||
|
.add_render_graph_edges(
|
||||||
|
Core3d,
|
||||||
|
(
|
||||||
|
Node3d::Tonemapping,
|
||||||
|
PostProcessLabel,
|
||||||
|
Node3d::EndMainPassPostProcessing,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
|
||||||
|
struct PostProcessLabel;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct PostProcessNode;
|
||||||
|
|
||||||
|
impl ViewNode for PostProcessNode {
|
||||||
|
type ViewQuery = (
|
||||||
|
&'static ViewTarget,
|
||||||
|
&'static PostProcessSettings,
|
||||||
|
&'static DynamicUniformIndex<PostProcessSettings>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_graph: &mut RenderGraphContext,
|
||||||
|
render_context: &mut RenderContext,
|
||||||
|
(view_target, _post_process_settings, settings_index): QueryItem<Self::ViewQuery>,
|
||||||
|
world: &World,
|
||||||
|
) -> Result<(), NodeRunError> {
|
||||||
|
let post_process_pipeline = world.resource::<PostProcessPipeline>();
|
||||||
|
let pipeline_cache = world.resource::<PipelineCache>();
|
||||||
|
let Some(pipeline) = pipeline_cache.get_render_pipeline(post_process_pipeline.pipeline_id)
|
||||||
|
else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let settings_uniforms = world.resource::<ComponentUniforms<PostProcessSettings>>();
|
||||||
|
let Some(settings_binding) = settings_uniforms.uniforms().binding() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let post_process = view_target.post_process_write();
|
||||||
|
|
||||||
|
let bind_group = render_context.render_device().create_bind_group(
|
||||||
|
"post_process_bind_group",
|
||||||
|
&pipeline_cache.get_bind_group_layout(&post_process_pipeline.layout),
|
||||||
|
&BindGroupEntries::sequential((
|
||||||
|
post_process.source,
|
||||||
|
&post_process_pipeline.sampler,
|
||||||
|
settings_binding.clone(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
|
||||||
|
label: Some("post_process_pass"),
|
||||||
|
color_attachments: &[Some(RenderPassColorAttachment {
|
||||||
|
view: post_process.destination,
|
||||||
|
depth_slice: None,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: Operations::default(),
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
timestamp_writes: None,
|
||||||
|
occlusion_query_set: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
render_pass.set_render_pipeline(pipeline);
|
||||||
|
render_pass.set_bind_group(0, &bind_group, &[settings_index.index()]);
|
||||||
|
render_pass.draw(0..3, 0..1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct PostProcessPipeline {
|
||||||
|
layout: BindGroupLayoutDescriptor,
|
||||||
|
sampler: Sampler,
|
||||||
|
pipeline_id: CachedRenderPipelineId,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_post_process_pipeline(
|
||||||
|
mut commands: Commands,
|
||||||
|
render_device: Res<RenderDevice>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
fullscreen_shader: Res<FullscreenShader>,
|
||||||
|
pipeline_cache: Res<PipelineCache>,
|
||||||
|
) {
|
||||||
|
let layout = BindGroupLayoutDescriptor::new(
|
||||||
|
"post_process_bind_group_layout",
|
||||||
|
&BindGroupLayoutEntries::sequential(
|
||||||
|
ShaderStages::FRAGMENT,
|
||||||
|
(
|
||||||
|
texture_2d(TextureSampleType::Float { filterable: true }),
|
||||||
|
sampler(SamplerBindingType::Filtering),
|
||||||
|
uniform_buffer::<PostProcessSettings>(true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let sampler = render_device.create_sampler(&SamplerDescriptor::default());
|
||||||
|
|
||||||
|
let shader = asset_server.load(SHADER_ASSET_PATH);
|
||||||
|
let vertex_state = fullscreen_shader.to_vertex_state();
|
||||||
|
let pipeline_id = pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor {
|
||||||
|
label: Some("post_process_pipeline".into()),
|
||||||
|
layout: vec![layout.clone()],
|
||||||
|
vertex: vertex_state,
|
||||||
|
fragment: Some(FragmentState {
|
||||||
|
shader,
|
||||||
|
targets: vec![Some(ColorTargetState {
|
||||||
|
format: TextureFormat::bevy_default(),
|
||||||
|
blend: None,
|
||||||
|
write_mask: ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.insert_resource(PostProcessPipeline {
|
||||||
|
layout,
|
||||||
|
sampler,
|
||||||
|
pipeline_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Default, Clone, Copy, ExtractComponent, ShaderType)]
|
||||||
|
pub struct PostProcessSettings {
|
||||||
|
pub scale: f32,
|
||||||
|
}
|
||||||
8
mini-game/src/renderer/mod.rs
Normal file
8
mini-game/src/renderer/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
mod node;
|
||||||
|
mod plugin;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub use plugin::{
|
||||||
|
G13Resource, GpuImageExportSource, ImageExport, ImageExportPlugin, ImageExportSource,
|
||||||
|
ImageExportSystems,
|
||||||
|
};
|
||||||
49
mini-game/src/renderer/node.rs
Normal file
49
mini-game/src/renderer/node.rs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
use super::GpuImageExportSource;
|
||||||
|
use bevy::{
|
||||||
|
prelude::*,
|
||||||
|
render::{
|
||||||
|
render_asset::RenderAssets,
|
||||||
|
render_graph::{Node, NodeRunError, RenderGraphContext, RenderLabel},
|
||||||
|
renderer::RenderContext,
|
||||||
|
texture::GpuImage,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use wgpu::{TexelCopyBufferInfo, TexelCopyBufferLayout};
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
|
||||||
|
pub struct ImageExportLabel;
|
||||||
|
|
||||||
|
pub struct ImageExportNode;
|
||||||
|
impl Node for ImageExportNode {
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_graph: &mut RenderGraphContext,
|
||||||
|
render_context: &mut RenderContext,
|
||||||
|
world: &World,
|
||||||
|
) -> Result<(), NodeRunError> {
|
||||||
|
for (_, source) in world
|
||||||
|
.resource::<RenderAssets<GpuImageExportSource>>()
|
||||||
|
.iter()
|
||||||
|
{
|
||||||
|
if let Some(gpu_image) = world
|
||||||
|
.resource::<RenderAssets<GpuImage>>()
|
||||||
|
.get(&source.source_handle)
|
||||||
|
{
|
||||||
|
render_context.command_encoder().copy_texture_to_buffer(
|
||||||
|
gpu_image.texture.as_image_copy(),
|
||||||
|
TexelCopyBufferInfo {
|
||||||
|
buffer: &source.buffer,
|
||||||
|
layout: TexelCopyBufferLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(source.padded_bytes_per_row),
|
||||||
|
rows_per_image: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
source.source_size,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
214
mini-game/src/renderer/plugin.rs
Normal file
214
mini-game/src/renderer/plugin.rs
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
use super::node::{ImageExportLabel, ImageExportNode};
|
||||||
|
use bevy::{
|
||||||
|
asset::RenderAssetUsages,
|
||||||
|
ecs::system::{SystemParamItem, lifetimeless::SRes},
|
||||||
|
prelude::*,
|
||||||
|
render::{
|
||||||
|
Render, RenderApp, RenderSystems,
|
||||||
|
extract_component::{ExtractComponent, ExtractComponentPlugin},
|
||||||
|
graph::CameraDriverLabel,
|
||||||
|
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
|
||||||
|
render_graph::RenderGraph,
|
||||||
|
render_resource::{Buffer, BufferDescriptor, BufferUsages, Extent3d, MapMode},
|
||||||
|
renderer::RenderDevice,
|
||||||
|
texture::GpuImage,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use embedded_graphics::pixelcolor::BinaryColor;
|
||||||
|
use futures::channel::oneshot;
|
||||||
|
use g13_driver::{G13, G13_LCD_BUF_SIZE};
|
||||||
|
use wgpu::PollType;
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct G13Resource {
|
||||||
|
pub g13: G13,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Asset, Reflect, Clone, Default)]
|
||||||
|
pub struct ImageExportSource(pub Handle<Image>);
|
||||||
|
|
||||||
|
impl From<Handle<Image>> for ImageExportSource {
|
||||||
|
fn from(value: Handle<Image>) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GpuImageExportSource {
|
||||||
|
pub buffer: Buffer,
|
||||||
|
pub source_handle: Handle<Image>,
|
||||||
|
pub source_size: Extent3d,
|
||||||
|
pub bytes_per_row: u32,
|
||||||
|
pub padded_bytes_per_row: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderAsset for GpuImageExportSource {
|
||||||
|
type SourceAsset = ImageExportSource;
|
||||||
|
type Param = (SRes<RenderDevice>, SRes<RenderAssets<GpuImage>>);
|
||||||
|
|
||||||
|
fn asset_usage(_: &Self::SourceAsset) -> RenderAssetUsages {
|
||||||
|
RenderAssetUsages::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_asset(
|
||||||
|
source_asset: Self::SourceAsset,
|
||||||
|
_asset_id: AssetId<Self::SourceAsset>,
|
||||||
|
(device, images): &mut SystemParamItem<Self::Param>,
|
||||||
|
_previous_asset: Option<&Self>,
|
||||||
|
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
|
||||||
|
let Some(gpu_image) = images.get(&source_asset.0) else {
|
||||||
|
return Err(PrepareAssetError::RetryNextUpdate(source_asset));
|
||||||
|
};
|
||||||
|
|
||||||
|
let size = gpu_image.texture.size();
|
||||||
|
let format = &gpu_image.texture_format;
|
||||||
|
let bytes_per_row =
|
||||||
|
(size.width / format.block_dimensions().0) * format.block_copy_size(None).unwrap();
|
||||||
|
let padded_bytes_per_row =
|
||||||
|
RenderDevice::align_copy_bytes_per_row(bytes_per_row as usize) as u32;
|
||||||
|
|
||||||
|
let source_size = gpu_image.texture.size();
|
||||||
|
|
||||||
|
Ok(GpuImageExportSource {
|
||||||
|
buffer: device.create_buffer(&BufferDescriptor {
|
||||||
|
label: Some("Image Export Buffer"),
|
||||||
|
size: (source_size.height * padded_bytes_per_row) as u64,
|
||||||
|
usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
}),
|
||||||
|
source_handle: source_asset.0,
|
||||||
|
source_size,
|
||||||
|
bytes_per_row,
|
||||||
|
padded_bytes_per_row,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn byte_len(_: &Self::SourceAsset) -> Option<usize> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, ExtractComponent, Clone, Default, Debug)]
|
||||||
|
pub struct ImageExport(pub Handle<ImageExportSource>);
|
||||||
|
|
||||||
|
fn save_buffer_to_disk(
|
||||||
|
export_bundles: Query<&ImageExport>,
|
||||||
|
sources: Res<RenderAssets<GpuImageExportSource>>,
|
||||||
|
render_device: Res<RenderDevice>,
|
||||||
|
mut g13: ResMut<G13Resource>,
|
||||||
|
) {
|
||||||
|
for export in &export_bundles {
|
||||||
|
if let Some(gpu_source) = sources.get(&export.0) {
|
||||||
|
let mut image_bytes = {
|
||||||
|
let slice = gpu_source.buffer.slice(..);
|
||||||
|
|
||||||
|
{
|
||||||
|
let (mapping_tx, mapping_rx) = oneshot::channel();
|
||||||
|
|
||||||
|
render_device.map_buffer(&slice, MapMode::Read, move |res| {
|
||||||
|
mapping_tx.send(res).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
if render_device
|
||||||
|
.poll(PollType::Wait {
|
||||||
|
submission_index: None,
|
||||||
|
timeout: None,
|
||||||
|
})
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
futures_lite::future::block_on(mapping_rx).unwrap().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.get_mapped_range().to_vec()
|
||||||
|
};
|
||||||
|
|
||||||
|
gpu_source.buffer.unmap();
|
||||||
|
|
||||||
|
let bytes_per_row = gpu_source.bytes_per_row as usize;
|
||||||
|
let padded_bytes_per_row = gpu_source.padded_bytes_per_row as usize;
|
||||||
|
let source_size = gpu_source.source_size;
|
||||||
|
|
||||||
|
if bytes_per_row != padded_bytes_per_row {
|
||||||
|
let mut unpadded_bytes =
|
||||||
|
Vec::<u8>::with_capacity(source_size.height as usize * bytes_per_row);
|
||||||
|
|
||||||
|
for padded_row in image_bytes.chunks(padded_bytes_per_row) {
|
||||||
|
unpadded_bytes.extend_from_slice(&padded_row[..bytes_per_row]);
|
||||||
|
}
|
||||||
|
|
||||||
|
image_bytes = unpadded_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut img_buffer = [0u8; G13_LCD_BUF_SIZE as usize];
|
||||||
|
|
||||||
|
for (i, b) in image_bytes
|
||||||
|
.iter()
|
||||||
|
.step_by(4)
|
||||||
|
.take(G13_LCD_BUF_SIZE as usize * 8)
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let offset = i / 8;
|
||||||
|
let mask = 1 << (7 - (i % 8));
|
||||||
|
|
||||||
|
if *b == 0xFF {
|
||||||
|
img_buffer[offset] |= mask;
|
||||||
|
} else {
|
||||||
|
img_buffer[offset] &= !mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use embedded_graphics::image::ImageDrawable;
|
||||||
|
embedded_graphics::image::ImageRaw::<BinaryColor>::new(&img_buffer, 160)
|
||||||
|
.draw(&mut g13.g13)
|
||||||
|
.expect("G13 to be connected");
|
||||||
|
|
||||||
|
let _ = g13.g13.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Plugin enabling the generation of image sequences.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ImageExportPlugin;
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
|
||||||
|
pub enum ImageExportSystems {
|
||||||
|
ImageExportSetup,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugin for ImageExportPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
use ImageExportSystems::*;
|
||||||
|
|
||||||
|
let g13 = G13::new().expect("G13 to be connected");
|
||||||
|
|
||||||
|
app.configure_sets(PostUpdate, ImageExportSetup)
|
||||||
|
.register_type::<ImageExportSource>()
|
||||||
|
.init_asset::<ImageExportSource>()
|
||||||
|
.register_asset_reflect::<ImageExportSource>()
|
||||||
|
.add_plugins((
|
||||||
|
RenderAssetPlugin::<GpuImageExportSource>::default(),
|
||||||
|
ExtractComponentPlugin::<ImageExport>::default(),
|
||||||
|
));
|
||||||
|
// .add_systems(PostUpdate, setup_exporters.in_set(ImageExportSetup));
|
||||||
|
|
||||||
|
let render_app = app.sub_app_mut(RenderApp);
|
||||||
|
|
||||||
|
render_app.insert_resource(G13Resource { g13 }).add_systems(
|
||||||
|
Render,
|
||||||
|
save_buffer_to_disk
|
||||||
|
.after(RenderSystems::Render)
|
||||||
|
.before(RenderSystems::Cleanup),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut graph = render_app
|
||||||
|
.world_mut()
|
||||||
|
.get_resource_mut::<RenderGraph>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
graph.add_node(ImageExportLabel, ImageExportNode);
|
||||||
|
graph.add_node_edge(CameraDriverLabel, ImageExportLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
47
mini-game/src/shared.rs
Normal file
47
mini-game/src/shared.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Spinner;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Resource, Default)]
|
||||||
|
pub struct Flags {
|
||||||
|
pub debug: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn spawn_3d_scene(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
commands.spawn((
|
||||||
|
Mesh3d(meshes.add(Cuboid::default())),
|
||||||
|
MeshMaterial3d(materials.add(Color::from(bevy::color::palettes::css::WHITE))),
|
||||||
|
Transform::from_xyz(0.0, 0.0, 0.0),
|
||||||
|
Spinner,
|
||||||
|
));
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
PointLight {
|
||||||
|
intensity: 6_000_000.,
|
||||||
|
shadows_enabled: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Transform::from_xyz(3., 4., 6.),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn spawn_2d_scene(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||||
|
) {
|
||||||
|
commands.spawn((
|
||||||
|
Spinner,
|
||||||
|
Mesh2d(meshes.add(RegularPolygon::new(66.0, 8))),
|
||||||
|
MeshMaterial2d(materials.add(Color::srgb(0.4, 0.4, 0.6))),
|
||||||
|
));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user