pixelate_mesh 0.5.0

Apply a pixelation effect to any Bevy mesh or scene without post-processing.
Documentation
use crate::ready_checks::{PixelationTargetKind, PixelationTargetReadyEvent};
use bevy::pbr::NotShadowReceiver;
use bevy::prelude::*;
use bevy::scene::InstanceId;
use bevy::utils::HashSet;

#[derive(Debug, Clone, Resource, Deref, DerefMut)]
pub(crate) struct ShadowMaterialHandle(Handle<StandardMaterial>);

#[derive(Debug, Clone, Resource, Deref, DerefMut, Default)]
pub(crate) struct SetSceneShadow(HashSet<InstanceId>);

pub(crate) fn create_shadow_material(
    mut commands: Commands,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let handle = materials.add(StandardMaterial {
        base_color: Color::NONE,
        unlit: true,
        alpha_mode: AlphaMode::Multiply,
        ..Default::default()
    });
    commands.insert_resource(ShadowMaterialHandle(handle));
}

pub(crate) fn add_shadow_caster(
    mut commands: Commands,
    mut ready_event: EventReader<PixelationTargetReadyEvent>,
    mesh_handles: Query<&Mesh3d>,
    children: Query<&Children>,
    scene_handles: Query<&SceneRoot>,
    mut scene_spawner: ResMut<SceneSpawner>,
    shadow_material_handle: Res<ShadowMaterialHandle>,
    mut set_scene_shadow: ResMut<SetSceneShadow>,
) {
    for event in ready_event.read() {
        for (&entity, target) in event.iter() {
            match target.kind {
                PixelationTargetKind::Mesh => {
                    commands.entity(entity).with_children(|parent| {
                        parent
                            .spawn((
                                Name::new("Pixelation Shadow"),
                                Transform::default(),
                                Visibility::default(),
                            ))
                            .with_children(|parent| {
                                duplicate_children(
                                    entity,
                                    parent,
                                    &children,
                                    &mesh_handles,
                                    &shadow_material_handle,
                                );
                            });
                    });
                }
                PixelationTargetKind::Scene => {
                    let scene_handle = scene_handles.get(entity).unwrap();
                    let instance_id = scene_spawner.spawn_as_child(scene_handle.0.clone(), entity);
                    set_scene_shadow.insert(instance_id);
                }
            }
        }
    }
}

fn duplicate_children(
    entity: Entity,
    child_builder: &mut ChildBuilder,
    children: &Query<&Children>,
    mesh_handles: &Query<&Mesh3d>,
    shadow_material_handle: &Handle<StandardMaterial>,
) {
    let mut entity_commands = child_builder.spawn_empty();
    if let Ok(mesh_handle) = mesh_handles.get(entity) {
        entity_commands.insert((
            mesh_handle.clone(),
            MeshMaterial3d(shadow_material_handle.clone()),
        ));
    }
    let children_entities = match children.get(entity) {
        Ok(children) => children,
        _ => {
            return;
        }
    };
    for &child in children_entities.iter() {
        entity_commands.with_children(|parent| {
            duplicate_children(
                child,
                parent,
                children,
                mesh_handles,
                shadow_material_handle,
            );
        });
    }
}

pub(crate) fn set_scene_shadow(
    mut commands: Commands,
    mut set_scene_shadow: ResMut<SetSceneShadow>,
    scene_spawner: Res<SceneSpawner>,
    shadow_material_handle: Res<ShadowMaterialHandle>,
    mesh_query: Query<&Mesh3d>,
) {
    let mut done = HashSet::new();
    for instance_id in set_scene_shadow.iter() {
        if scene_spawner.instance_is_ready(*instance_id) {
            done.insert(*instance_id);
        }
    }
    set_scene_shadow.0 = set_scene_shadow.0.difference(&done).copied().collect();
    for instance_id in done {
        for entity in scene_spawner.iter_instance_entities(instance_id) {
            if mesh_query.contains(entity) {
                commands.entity(entity).insert((
                    NotShadowReceiver,
                    MeshMaterial3d(shadow_material_handle.0.clone()),
                ));
            }
        }
    }
}