Skip to main content

khora_lanes/render_lane/
mod.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Render-domain lanes (the hot-path strategies for the rendering subsystem).
16//!
17//! The per-frame `RenderWorld` and the extraction logic live in
18//! [`khora_data::render`].  This module exposes the lanes that consume that
19//! data and the UI-scene types specific to the UI render pipeline.
20
21mod forward_plus_lane;
22mod gizmo_lane;
23mod grid_lane;
24mod lit_forward_lane;
25pub mod shaders;
26pub mod shadows_lane;
27mod simple_unlit_lane;
28mod skybox_lane;
29mod standard_pbr_lane;
30mod ui_render_lane;
31pub mod util;
32mod wireframe_lane;
33
34/// Squared distance from the camera to a model's origin — the sort key the lit
35/// lanes use to order alpha-blended draws back-to-front.
36///
37/// The model's world position is the translation column of its matrix. Squared
38/// distance suffices for ordering (the square root is monotonic) and avoids a
39/// per-draw `sqrt`. Sorting per *object* rather than per fragment is the
40/// standard approximation: exact for separated objects, still approximate for
41/// intersecting or concave transparent geometry.
42pub(crate) fn camera_distance_sq(
43    model_matrix: &khora_core::math::Mat4,
44    camera: khora_core::math::Vec3,
45) -> f32 {
46    let cols = model_matrix.to_cols_array_2d();
47    let dx = cols[3][0] - camera.x;
48    let dy = cols[3][1] - camera.y;
49    let dz = cols[3][2] - camera.z;
50    dx * dx + dy * dy + dz * dz
51}
52
53pub use forward_plus_lane::*;
54pub use gizmo_lane::{GizmoLane, SharedGizmoFrame};
55pub use grid_lane::{GridLane, SharedGridConfig};
56pub use lit_forward_lane::*;
57pub use shadows_lane::{LowResShadowsLane, MediumShadowsLane, StandardShadowsLane};
58pub use simple_unlit_lane::*;
59pub use skybox_lane::SkyboxLane;
60pub use standard_pbr_lane::StandardPbrLane;
61pub use ui_render_lane::*;
62pub use wireframe_lane::{SharedWireframeConfig, WireframeLane};
63
64#[cfg(test)]
65mod transparency_tests {
66    use super::camera_distance_sq;
67    use khora_core::math::{Mat4, Vec3};
68
69    fn at(x: f32, y: f32, z: f32) -> Mat4 {
70        Mat4::from_translation(Vec3::new(x, y, z))
71    }
72
73    #[test]
74    fn distance_uses_the_model_translation() {
75        let camera = Vec3::new(0.0, 0.0, 0.0);
76        assert_eq!(camera_distance_sq(&at(3.0, 0.0, 4.0), camera), 25.0);
77        // Camera offset is accounted for, not just the model position.
78        assert_eq!(
79            camera_distance_sq(&at(3.0, 0.0, 4.0), Vec3::new(3.0, 0.0, 0.0)),
80            16.0
81        );
82    }
83
84    #[test]
85    fn sorting_by_descending_distance_is_back_to_front() {
86        let camera = Vec3::new(0.0, 0.0, 0.0);
87        let mut draws = [
88            ("near", camera_distance_sq(&at(0.0, 0.0, 1.0), camera)),
89            ("far", camera_distance_sq(&at(0.0, 0.0, 9.0), camera)),
90            ("mid", camera_distance_sq(&at(0.0, 0.0, 4.0), camera)),
91        ];
92        // The lit lanes' ordering: farthest first, so nearer transparent
93        // surfaces composite over what is behind them.
94        draws.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
95        let order: Vec<&str> = draws.iter().map(|(name, _)| *name).collect();
96        assert_eq!(order, ["far", "mid", "near"]);
97    }
98}