Skip to main content

concinnity_render/
transparent.rs

1//! Backend-agnostic helpers for the transparent (translucent) pass. The pass
2//! itself is encoded per backend; this module owns only the CPU-side ordering
3//! policy so it can be unit-tested without a GPU and reused as the Vulkan /
4//! DirectX transparent ports land.
5//!
6//! Transparent fragments use SRC_ALPHA / ONE_MINUS_SRC_ALPHA blending, which is
7//! order-dependent: a draw must be composited after everything behind it.
8//! `back_to_front_order` returns the draw indices sorted farthest-first by
9//! camera distance so the blend resolves correctly. This is a single fixed
10//! sorted draw list, not order-independent transparency.
11
12use alloc::vec::Vec;
13
14/// Return the indices `0..distances.len()` ordered farthest camera distance
15/// first (back-to-front). The sort is stable, so draws at equal distance keep
16/// their original (declaration) order. Non-finite distances (NaN) are treated
17/// as nearest so a degenerate value never pushes a draw behind valid geometry.
18pub fn back_to_front_order(distances: &[f32]) -> Vec<usize> {
19    let mut order: Vec<usize> = (0..distances.len()).collect();
20    order.sort_by(|&a, &b| {
21        // Farther (larger distance) sorts first. Map NaN to -inf so it lands
22        // last (nearest), keeping a total order for `sort_by`.
23        let da = if distances[a].is_finite() {
24            distances[a]
25        } else {
26            f32::NEG_INFINITY
27        };
28        let db = if distances[b].is_finite() {
29            distances[b]
30        } else {
31            f32::NEG_INFINITY
32        };
33        db.partial_cmp(&da).unwrap_or(core::cmp::Ordering::Equal)
34    });
35    order
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41
42    use alloc::vec;
43    #[test]
44    fn orders_farthest_first() {
45        let d = [1.0, 5.0, 3.0];
46        assert_eq!(back_to_front_order(&d), vec![1, 2, 0]);
47    }
48
49    #[test]
50    fn empty_is_empty() {
51        assert!(back_to_front_order(&[]).is_empty());
52    }
53
54    #[test]
55    fn equal_distances_keep_declaration_order() {
56        let d = [2.0, 2.0, 2.0];
57        assert_eq!(back_to_front_order(&d), vec![0, 1, 2]);
58    }
59
60    #[test]
61    fn nan_sorts_last() {
62        let d = [4.0, f32::NAN, 2.0];
63        // 4.0 (farthest) → index 0, then 2.0 → index 2, then NaN → index 1.
64        assert_eq!(back_to_front_order(&d), vec![0, 2, 1]);
65    }
66}