From d0ed21e0fad00efccfd326aec72ed1c8104fbc0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20Kry=C5=84ski?= Date: Tue, 12 Jan 2021 19:59:02 +0100 Subject: [PATCH 1/9] render_to_texture example --- Cargo.toml | 4 + examples/3d/render_to_texture.rs | 304 +++++++++++++++++++++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 examples/3d/render_to_texture.rs diff --git a/Cargo.toml b/Cargo.toml index 36f8cd493318a..f70f595095712 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -150,6 +150,10 @@ path = "examples/3d/parenting.rs" name = "pbr" path = "examples/3d/pbr.rs" +[[example]] +name = "render_to_texture" +path = "examples/3d/render_to_texture.rs" + [[example]] name = "spawner" path = "examples/3d/spawner.rs" diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs new file mode 100644 index 0000000000000..fdf17779b9411 --- /dev/null +++ b/examples/3d/render_to_texture.rs @@ -0,0 +1,304 @@ +use std::borrow::Cow; + +use bevy::{ + prelude::*, + reflect::TypeUuid, + render::{ + camera::{ActiveCameras, Camera, CameraProjection}, + pass::{ + LoadOp, Operations, PassDescriptor, RenderPassColorAttachmentDescriptor, + RenderPassDepthStencilAttachmentDescriptor, TextureAttachment, + }, + render_graph::{ + base::{node::MAIN_PASS, MainPass}, + CameraNode, Node, PassNode, RenderGraph, ResourceSlotInfo, + }, + renderer::{RenderResourceId, RenderResourceType}, + texture::{ + Extent3d, SamplerDescriptor, TextureDescriptor, TextureDimension, TextureFormat, + TextureUsage, SAMPLER_ASSET_INDEX, TEXTURE_ASSET_INDEX, + }, + }, + window::WindowId, +}; + +pub const RENDER_TEXTURE_HANDLE: HandleUntyped = + HandleUntyped::weak_from_u64(Texture::TYPE_UUID, 13378939762009864029); + +pub const TEXTURE_NODE: &str = "texure_node"; +pub const DEPTH_TEXTURE_NODE: &str = "depth_texure_node"; +pub const FIRST_PASS: &str = "first_pass"; +pub const FIRST_PASS_CAMERA: &str = "first_pass_camera"; + +pub trait RenderToTextureGraphBuilder { + fn add_render_to_texture_graph(&mut self, active_cameras: &mut ActiveCameras) -> &mut Self; +} + +impl RenderToTextureGraphBuilder for RenderGraph { + fn add_render_to_texture_graph(&mut self, active_cameras: &mut ActiveCameras) -> &mut Self { + let mut pass_node = PassNode::<&FirstPass>::new(PassDescriptor { + color_attachments: vec![RenderPassColorAttachmentDescriptor { + attachment: TextureAttachment::Input("color_attachment".to_string()), + resolve_target: None, + ops: Operations { + load: LoadOp::Clear(Color::rgb(0.1, 0.2, 0.3)), + store: true, + }, + }], + depth_stencil_attachment: Some(RenderPassDepthStencilAttachmentDescriptor { + attachment: TextureAttachment::Input("depth".to_string()), + depth_ops: Some(Operations { + load: LoadOp::Clear(1.0), + store: true, + }), + stencil_ops: None, + }), + sample_count: 1, + }); + pass_node.add_camera(FIRST_PASS_CAMERA); + + self.add_node(FIRST_PASS, pass_node); + self.add_system_node(FIRST_PASS_CAMERA, CameraNode::new(FIRST_PASS_CAMERA)); + + active_cameras.add(FIRST_PASS_CAMERA); + self.add_node_edge(FIRST_PASS_CAMERA, FIRST_PASS).unwrap(); + + self.add_node( + TEXTURE_NODE, + TextureNode::new( + TextureDescriptor { + size: Extent3d::new(512, 512, 1), + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: Default::default(), + usage: TextureUsage::OUTPUT_ATTACHMENT | TextureUsage::SAMPLED, + }, + Some(SamplerDescriptor::default()), + Some(RENDER_TEXTURE_HANDLE), + ), + ); + + self.add_node( + DEPTH_TEXTURE_NODE, + TextureNode::new( + TextureDescriptor { + size: Extent3d::new(512, 512, 1), + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Depth32Float, + usage: TextureUsage::OUTPUT_ATTACHMENT | TextureUsage::SAMPLED, + }, + None, + None, + ), + ); + + self.add_node_edge(TEXTURE_NODE, FIRST_PASS).unwrap(); + self.add_slot_edge( + TEXTURE_NODE, + TextureNode::TEXTURE, + FIRST_PASS, + "color_attachment", + ) + .unwrap(); + self.add_slot_edge( + DEPTH_TEXTURE_NODE, + TextureNode::TEXTURE, + FIRST_PASS, + "depth", + ) + .unwrap(); + self.add_node_edge(FIRST_PASS, MAIN_PASS).unwrap(); + self.add_node_edge("transform", FIRST_PASS).unwrap(); + self + } +} + +/// this component indicates what entities should rotate +struct Rotator; +struct Cube; + +#[derive(Default)] +pub struct FirstPass; + +pub struct TextureNode { + pub texture_descriptor: TextureDescriptor, + pub sampler_descriptor: Option, + pub handle: Option, +} + +impl TextureNode { + pub const TEXTURE: &'static str = "texture"; + + pub fn new( + texture_descriptor: TextureDescriptor, + sampler_descriptor: Option, + handle: Option, + ) -> Self { + Self { + texture_descriptor, + sampler_descriptor, + handle, + } + } +} + +impl Node for TextureNode { + fn output(&self) -> &[ResourceSlotInfo] { + static OUTPUT: &[ResourceSlotInfo] = &[ResourceSlotInfo { + name: Cow::Borrowed(TextureNode::TEXTURE), + resource_type: RenderResourceType::Texture, + }]; + OUTPUT + } + + fn update( + &mut self, + _world: &World, + render_context: &mut dyn bevy::render::renderer::RenderContext, + _input: &bevy::render::render_graph::ResourceSlots, + output: &mut bevy::render::render_graph::ResourceSlots, + ) { + if output.get(0).is_none() { + let render_resource_context = render_context.resources_mut(); + let texture_id = render_resource_context.create_texture(self.texture_descriptor); + if let Some(handle) = &self.handle { + render_resource_context.set_asset_resource_untyped( + handle.clone(), + RenderResourceId::Texture(texture_id), + TEXTURE_ASSET_INDEX, + ); + if let Some(sampler_descriptor) = self.sampler_descriptor { + let sampler_id = render_resource_context.create_sampler(&sampler_descriptor); + render_resource_context.set_asset_resource_untyped( + handle.clone(), + RenderResourceId::Sampler(sampler_id), + SAMPLER_ASSET_INDEX, + ); + } + } + output.set(0, RenderResourceId::Texture(texture_id)); + } + } +} + +/// rotates the inner cube (first pass) +fn rotator_system(time: Res