Skip to main content

ranim_render/
world.rs

1//! Shared metadata for render-world entities.
2
3use bevy_ecs::prelude::*;
4
5/// Source position of an entity produced by frame extraction.
6///
7/// Extraction systems copy [`ranim_core::frame::FrameItem::order`] into
8/// [`Self::order`] and assign successive [`Self::part`] values when one input
9/// produces several ordered entities. The renderer resolves these keys into
10/// [`SceneOrder`] after extraction, before preparing GPU data.
11#[derive(Component, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
12pub struct SourceOrder {
13    /// Global insertion order of the source frame item.
14    pub order: usize,
15    /// Output position within the source item.
16    pub part: usize,
17}
18
19/// Resolved painter's order of a render-world entity.
20///
21/// The renderer assigns consecutive ranks from [`SourceOrder`] after
22/// extraction. GPU preparation systems use this rank to resolve coplanar
23/// geometry consistently across every typed input batch and expanded output.
24#[derive(Component, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
25pub struct SceneOrder(
26    /// Zero-based rank in the extracted scene.
27    pub usize,
28);
29
30pub(crate) fn resolve_scene_order(world: &mut World) {
31    let mut ordered = world
32        .query::<(Entity, &SourceOrder)>()
33        .iter(world)
34        .map(|(entity, order)| (*order, entity))
35        .collect::<Vec<_>>();
36    ordered.sort_unstable_by_key(|(order, entity)| (*order, *entity));
37    for (rank, (_, entity)) in ordered.into_iter().enumerate() {
38        let order = SceneOrder(rank);
39        if world.get::<SceneOrder>(entity) != Some(&order) {
40            world.entity_mut(entity).insert(order);
41        }
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn expanded_inputs_keep_their_parts_between_surrounding_sources() {
51        let mut world = World::new();
52        let later = world.spawn(SourceOrder { order: 7, part: 0 }).id();
53        let second = world.spawn(SourceOrder { order: 3, part: 1 }).id();
54        let first = world.spawn(SourceOrder { order: 3, part: 0 }).id();
55        let earlier = world.spawn(SourceOrder { order: 0, part: 0 }).id();
56
57        resolve_scene_order(&mut world);
58
59        for (rank, entity) in [earlier, first, second, later].into_iter().enumerate() {
60            assert_eq!(world.get::<SceneOrder>(entity), Some(&SceneOrder(rank)));
61        }
62    }
63}