bevy_megaui 0.1.4

Megaui integration for Bevy
Documentation
use crate::{MegaUiSettings, WindowSize, MEGAUI_TRANSFORM_RESOURCE_BINDING_NAME};
use bevy::{
    core::AsBytes,
    ecs::{Commands, IntoSystem, Local, Res, ResMut, Resources, System, World},
    render::{
        render_graph::{CommandQueue, Node, ResourceSlots, SystemNode},
        renderer::{
            BufferId, BufferInfo, BufferUsage, RenderContext, RenderResourceBinding,
            RenderResourceBindings, RenderResourceContext,
        },
    },
};

#[derive(Debug)]
pub struct MegaUiTransformNode {
    command_queue: CommandQueue,
}

impl MegaUiTransformNode {
    pub fn new() -> Self {
        MegaUiTransformNode {
            command_queue: Default::default(),
        }
    }
}

impl Node for MegaUiTransformNode {
    fn update(
        &mut self,
        _world: &World,
        _resources: &Resources,
        render_context: &mut dyn RenderContext,
        _input: &ResourceSlots,
        _output: &mut ResourceSlots,
    ) {
        self.command_queue.execute(render_context);
    }
}

impl SystemNode for MegaUiTransformNode {
    fn get_system(&self, commands: &mut Commands) -> Box<dyn System<In = (), Out = ()>> {
        let system = transform_node_system.system();
        commands.insert_local_resource(
            system.id(),
            TransformNodeState {
                command_queue: self.command_queue.clone(),
                transform_buffer: None,
                staging_buffer: None,
                prev_window_size: WindowSize::new(0.0, 0.0, 0.0),
                prev_scale_factor: 0.0,
            },
        );
        Box::new(system)
    }
}

#[derive(Default)]
pub struct TransformNodeState {
    command_queue: CommandQueue,
    transform_buffer: Option<BufferId>,
    staging_buffer: Option<BufferId>,
    prev_window_size: WindowSize,
    prev_scale_factor: f64,
}

fn transform_node_system(
    mut state: Local<TransformNodeState>,
    render_resource_context: Res<Box<dyn RenderResourceContext>>,
    window_size: Res<WindowSize>,
    megaui_settings: Res<MegaUiSettings>,
    mut render_resource_bindings: ResMut<RenderResourceBindings>,
) {
    #[allow(clippy::float_cmp)]
    if state.prev_window_size == *window_size
        && state.prev_scale_factor == megaui_settings.scale_factor
    {
        return;
    }
    state.prev_window_size = window_size.clone();
    state.prev_scale_factor = megaui_settings.scale_factor;

    let render_resource_context = &**render_resource_context;
    let transform_data_size = std::mem::size_of::<[[f32; 2]; 2]>();

    let staging_buffer = if let Some(staging_buffer) = state.staging_buffer {
        render_resource_context.map_buffer(staging_buffer);
        staging_buffer
    } else {
        let buffer = render_resource_context.create_buffer(BufferInfo {
            size: transform_data_size,
            buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
            ..Default::default()
        });
        render_resource_bindings.set(
            MEGAUI_TRANSFORM_RESOURCE_BINDING_NAME,
            RenderResourceBinding::Buffer {
                buffer,
                range: 0..transform_data_size as u64,
                dynamic_index: None,
            },
        );
        state.transform_buffer = Some(buffer);

        let staging_buffer = render_resource_context.create_buffer(BufferInfo {
            size: transform_data_size,
            buffer_usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
            mapped_at_creation: true,
        });

        state.staging_buffer = Some(staging_buffer);
        staging_buffer
    };

    let transform_data: [f32; 4] = [
        2.0 / (window_size.width() / megaui_settings.scale_factor as f32),
        -2.0 / (window_size.height() / megaui_settings.scale_factor as f32), // scale
        -1.0,
        1.0, // translation
    ];

    render_resource_context.write_mapped_buffer(
        staging_buffer,
        0..transform_data_size as u64,
        &mut |data, _renderer| {
            data[0..transform_data_size].copy_from_slice(transform_data.as_bytes());
        },
    );
    render_resource_context.unmap_buffer(staging_buffer);

    let transform_buffer = state.transform_buffer.unwrap();
    state.command_queue.copy_buffer_to_buffer(
        staging_buffer,
        0,
        transform_buffer,
        0,
        transform_data_size as u64,
    );
}