Skip to main content

appcore_render/
asset_data.rs

1// =============================================================================
2//        #######
3//     ###       ###     F: asset_data.rs
4//    ##   ## ##   ##    P: AppCore-Runtime
5//         ## ##
6//                       C: 2026/10/14 17:50:33 by dnettoRaw
7//    ##   ## ##   ##    U: 2026/10/15 10:24:40 by dnettoRaw
8//      ###########      S: 0.1.0-alpha.1
9// =============================================================================
10
11use crate::{RenderError, Transform};
12use std::sync::{
13    atomic::{AtomicU64, Ordering},
14    Arc,
15};
16pub(crate) struct AssetLease {
17    pub(crate) counter: Arc<AtomicU64>,
18    pub(crate) bytes: u64,
19}
20impl Drop for AssetLease {
21    fn drop(&mut self) {
22        self.counter.fetch_sub(self.bytes, Ordering::AcqRel);
23    }
24}
25/// Validated static mesh vertex, with finite bounded position and unit normal.
26#[derive(Debug, Clone, Copy)]
27pub struct MeshVertex {
28    position: [f32; 3],
29    normal: [f32; 3],
30}
31impl MeshVertex {
32    /// Reject invalid normals rather than substituting or normalizing them implicitly.
33    pub fn new(position: [f32; 3], normal: [f32; 3]) -> Result<Self, RenderError> {
34        if !position
35            .iter()
36            .all(|v| v.is_finite() && v.abs() <= 1_000_000.0)
37            || !normal.iter().all(|v| v.is_finite())
38            || (normal.iter().map(|v| v * v).sum::<f32>() - 1.0).abs() > 0.0001
39        {
40            return Err(RenderError::InvalidLimits);
41        }
42        Ok(Self { position, normal })
43    }
44    /// Local vertex position.
45    pub fn position(self) -> [f32; 3] {
46        self.position
47    }
48    /// Unit local normal.
49    pub fn normal(self) -> [f32; 3] {
50        self.normal
51    }
52}
53/// Conservative local sphere for visibility and spatial indexing.
54#[derive(Debug, Clone, Copy)]
55pub struct BoundingSphere {
56    /// Local/world center depending on the owning prepared instance.
57    pub center: [f32; 3],
58    /// Nonnegative radius.
59    pub radius: f32,
60}
61/// Immutable static triangle mesh. Cloned Arc ownership keeps retired payloads accounted.
62pub struct MeshAsset {
63    pub(crate) vertices: Vec<MeshVertex>,
64    pub(crate) indices: Vec<u32>,
65    pub(crate) bounds: BoundingSphere,
66    pub(crate) _lease: AssetLease,
67}
68impl MeshAsset {
69    /// Validated immutable vertices.
70    pub fn vertices(&self) -> &[MeshVertex] {
71        &self.vertices
72    }
73    /// Triangle indices, all within vertices().
74    pub fn indices(&self) -> &[u32] {
75        &self.indices
76    }
77    /// Conservative local sphere.
78    pub fn bounds(&self) -> BoundingSphere {
79        self.bounds
80    }
81}
82pub(crate) struct Material {
83    pub(crate) color: [f32; 4],
84    pub(crate) _lease: AssetLease,
85}
86pub(crate) fn bounds(vertices: &[MeshVertex]) -> BoundingSphere {
87    let mut low = [f32::INFINITY; 3];
88    let mut high = [f32::NEG_INFINITY; 3];
89    for vertex in vertices {
90        for i in 0..3 {
91            low[i] = low[i].min(vertex.position[i]);
92            high[i] = high[i].max(vertex.position[i]);
93        }
94    }
95    let center = std::array::from_fn(|i| (low[i] + high[i]) * 0.5);
96    let radius = vertices
97        .iter()
98        .map(|v| {
99            (0..3)
100                .map(|i| (v.position[i] - center[i]).powi(2))
101                .sum::<f32>()
102                .sqrt()
103        })
104        .fold(0.0, f32::max);
105    BoundingSphere {
106        center,
107        radius: radius * (1.0 + 0.0001),
108    }
109}
110pub(crate) fn world_bounds(local: BoundingSphere, transform: Transform) -> BoundingSphere {
111    let scaled: [f32; 3] = std::array::from_fn(|i| local.center[i] * transform.scale()[i]);
112    let q = transform.rotation();
113    let cross = |a: [f32; 3], b: [f32; 3]| {
114        [
115            a[1] * b[2] - a[2] * b[1],
116            a[2] * b[0] - a[0] * b[2],
117            a[0] * b[1] - a[1] * b[0],
118        ]
119    };
120    let u = [q[0], q[1], q[2]];
121    let v = cross(u, scaled);
122    let w = cross(u, v);
123    let center =
124        std::array::from_fn(|i| scaled[i] + 2.0 * (q[3] * v[i] + w[i]) + transform.position()[i]);
125    let radius = local.radius * transform.scale().into_iter().fold(0.0, f32::max) * (1.0 + 0.0002);
126    BoundingSphere { center, radius }
127}