Skip to main content

oxihuman_mesh/
mesh_multiresolution.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Multiresolution mesh editing levels.
6//! Stores a stack of subdivision levels and displacement deltas for each.
7
8/// A single level in a multiresolution mesh.
9#[derive(Debug, Clone)]
10pub struct MrLevel {
11    pub positions: Vec<[f32; 3]>,
12    pub indices: Vec<u32>,
13    pub displacements: Vec<[f32; 3]>,
14}
15
16/// Container for all multiresolution levels.
17#[derive(Debug, Clone)]
18pub struct MultiresolutionMesh {
19    pub levels: Vec<MrLevel>,
20    pub base_level: usize,
21}
22
23impl MultiresolutionMesh {
24    /// Create a new multiresolution mesh from a base mesh.
25    pub fn new(positions: Vec<[f32; 3]>, indices: Vec<u32>) -> Self {
26        let displacements = vec![[0.0_f32; 3]; positions.len()];
27        let level0 = MrLevel {
28            positions,
29            indices,
30            displacements,
31        };
32        Self {
33            levels: vec![level0],
34            base_level: 0,
35        }
36    }
37
38    /// Return the number of levels stored.
39    pub fn level_count(&self) -> usize {
40        self.levels.len()
41    }
42}
43
44/// Push a new level by simple midpoint subdivision (stub).
45pub fn push_level(mr: &mut MultiresolutionMesh) {
46    let top = mr.levels.last().expect("at least one level");
47    let new_positions = top.positions.clone();
48    let new_indices = top.indices.clone();
49    let displacements = vec![[0.0_f32; 3]; new_positions.len()];
50    mr.levels.push(MrLevel {
51        positions: new_positions,
52        indices: new_indices,
53        displacements,
54    });
55}
56
57/// Pop the finest level (if more than one level exists).
58pub fn pop_level(mr: &mut MultiresolutionMesh) -> bool {
59    if mr.levels.len() > 1 {
60        mr.levels.pop();
61        true
62    } else {
63        false
64    }
65}
66
67/// Apply a displacement to a vertex at the given level.
68pub fn apply_displacement(
69    mr: &mut MultiresolutionMesh,
70    level: usize,
71    vertex: usize,
72    delta: [f32; 3],
73) {
74    if let Some(lvl) = mr.levels.get_mut(level) {
75        if let Some(d) = lvl.displacements.get_mut(vertex) {
76            d[0] += delta[0];
77            d[1] += delta[1];
78            d[2] += delta[2];
79        }
80    }
81}
82
83/// Compute total displacement magnitude at a given level.
84pub fn total_displacement_magnitude(mr: &MultiresolutionMesh, level: usize) -> f32 {
85    if let Some(lvl) = mr.levels.get(level) {
86        lvl.displacements
87            .iter()
88            .map(|d| (d[0] * d[0] + d[1] * d[1] + d[2] * d[2]).sqrt())
89            .sum()
90    } else {
91        0.0
92    }
93}
94
95/// Reset all displacements at the given level to zero.
96pub fn reset_displacements(mr: &mut MultiresolutionMesh, level: usize) {
97    if let Some(lvl) = mr.levels.get_mut(level) {
98        for d in &mut lvl.displacements {
99            *d = [0.0, 0.0, 0.0];
100        }
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    fn sample_mr() -> MultiresolutionMesh {
109        let pos = vec![[0.0_f32, 0.0, 0.0], [1.0, 0.0, 0.0], [0.5, 1.0, 0.0]];
110        let idx = vec![0u32, 1, 2];
111        MultiresolutionMesh::new(pos, idx)
112    }
113
114    #[test]
115    fn test_new_has_one_level() {
116        /* newly created mesh starts with one level */
117        let mr = sample_mr();
118        assert_eq!(mr.level_count(), 1);
119    }
120
121    #[test]
122    fn test_push_level_increments_count() {
123        /* push should add a level */
124        let mut mr = sample_mr();
125        push_level(&mut mr);
126        assert_eq!(mr.level_count(), 2);
127    }
128
129    #[test]
130    fn test_pop_level_decrements_count() {
131        /* pop on multi-level mesh returns true and removes level */
132        let mut mr = sample_mr();
133        push_level(&mut mr);
134        assert!(pop_level(&mut mr));
135        assert_eq!(mr.level_count(), 1);
136    }
137
138    #[test]
139    fn test_pop_level_base_returns_false() {
140        /* pop on single-level mesh should return false */
141        let mut mr = sample_mr();
142        assert!(!pop_level(&mut mr));
143        assert_eq!(mr.level_count(), 1);
144    }
145
146    #[test]
147    fn test_apply_displacement_accumulates() {
148        /* displacement accumulates correctly */
149        let mut mr = sample_mr();
150        apply_displacement(&mut mr, 0, 0, [1.0, 0.0, 0.0]);
151        apply_displacement(&mut mr, 0, 0, [0.0, 2.0, 0.0]);
152        let d = mr.levels[0].displacements[0];
153        assert!((d[0] - 1.0).abs() < 1e-6);
154        assert!((d[1] - 2.0).abs() < 1e-6);
155    }
156
157    #[test]
158    fn test_total_displacement_magnitude_zero_initially() {
159        /* newly created level has zero displacement magnitude */
160        let mr = sample_mr();
161        assert!((total_displacement_magnitude(&mr, 0) - 0.0).abs() < 1e-6);
162    }
163
164    #[test]
165    fn test_total_displacement_magnitude_nonzero_after_apply() {
166        /* after displacement magnitude becomes positive */
167        let mut mr = sample_mr();
168        apply_displacement(&mut mr, 0, 0, [3.0, 4.0, 0.0]);
169        let mag = total_displacement_magnitude(&mr, 0);
170        assert!(mag > 4.9);
171    }
172
173    #[test]
174    fn test_reset_displacements() {
175        /* reset returns displacements to zero */
176        let mut mr = sample_mr();
177        apply_displacement(&mut mr, 0, 0, [1.0, 1.0, 1.0]);
178        reset_displacements(&mut mr, 0);
179        assert!((total_displacement_magnitude(&mr, 0) - 0.0).abs() < 1e-6);
180    }
181
182    #[test]
183    fn test_invalid_level_ignored() {
184        /* operations on invalid level should not panic */
185        let mut mr = sample_mr();
186        apply_displacement(&mut mr, 99, 0, [1.0, 0.0, 0.0]);
187        reset_displacements(&mut mr, 99);
188        assert_eq!(total_displacement_magnitude(&mr, 99), 0.0);
189    }
190}