1use glam::{Vec3, Vec4, UVec3};
2use super::octree::{SparseVoxelOctree, VoxelData};
3
4#[derive(Debug, Clone)]
6pub struct Brick {
7 pub position: UVec3,
8 pub data: Vec<Vec4>,
9}
10
11impl Brick {
12 pub fn new(position: UVec3, size: u32) -> Self {
13 let count = (size * size * size) as usize;
14 Self {
15 position,
16 data: vec![Vec4::ZERO; count],
17 }
18 }
19
20 pub fn volume(size: u32) -> usize {
21 (size * size * size) as usize
22 }
23}
24
25#[derive(Debug, Clone)]
27pub struct Shelf3D {
28 pub x: u32,
29 pub y: u32,
30 pub z: u32,
31 pub width: u32,
32 pub height: u32,
33 pub depth: u32,
34 pub used_width: u32,
35 pub used_height: u32,
36}
37
38impl Shelf3D {
39 pub fn new(x: u32, y: u32, z: u32, width: u32, height: u32, depth: u32) -> Self {
40 Self {
41 x, y, z, width, height, depth,
42 used_width: 0,
43 used_height: 0,
44 }
45 }
46
47 pub fn can_fit(&self, w: u32, h: u32, d: u32) -> bool {
48 self.used_width + w <= self.width && h <= self.height && d <= self.depth
49 }
50}
51
52#[derive(Debug, Clone)]
54pub struct BlockPacker {
55 pub shelves: Vec<Shelf3D>,
56 pub atlas_size: UVec3,
57 next_z: u32,
58 freed: Vec<(UVec3, UVec3)>,
59}
60
61impl BlockPacker {
62 pub fn new(atlas_size: UVec3) -> Self {
63 Self {
64 shelves: Vec::new(),
65 atlas_size,
66 next_z: 0,
67 freed: Vec::new(),
68 }
69 }
70
71 pub fn pack(&mut self, brick_size: UVec3) -> Option<UVec3> {
73 for i in 0..self.freed.len() {
75 let (pos, size) = self.freed[i];
76 if size.x >= brick_size.x && size.y >= brick_size.y && size.z >= brick_size.z {
77 self.freed.swap_remove(i);
78 return Some(pos);
79 }
80 }
81
82 for shelf in &mut self.shelves {
84 if shelf.can_fit(brick_size.x, brick_size.y, brick_size.z) {
85 let pos = UVec3::new(shelf.x + shelf.used_width, shelf.y, shelf.z);
86 shelf.used_width += brick_size.x;
87 if brick_size.y > shelf.used_height {
88 shelf.used_height = brick_size.y;
89 }
90 return Some(pos);
91 }
92 }
93
94 if self.next_z + brick_size.z <= self.atlas_size.z {
96 let shelf = Shelf3D::new(
97 0, 0, self.next_z,
98 self.atlas_size.x, brick_size.y, brick_size.z,
99 );
100 let pos = UVec3::new(0, 0, self.next_z);
101 self.next_z += brick_size.z;
102 let mut new_shelf = shelf;
103 new_shelf.used_width = brick_size.x;
104 new_shelf.used_height = brick_size.y;
105 self.shelves.push(new_shelf);
106 return Some(pos);
107 }
108
109 None }
111
112 pub fn free(&mut self, position: UVec3, size: UVec3) {
114 self.freed.push((position, size));
115 }
116
117 pub fn utilization(&self) -> f32 {
118 let total = self.atlas_size.x * self.atlas_size.y * self.atlas_size.z;
119 if total == 0 {
120 return 0.0;
121 }
122 let mut used = 0u32;
123 for shelf in &self.shelves {
124 used += shelf.used_width * shelf.used_height * shelf.depth;
125 }
126 let freed_vol: u32 = self.freed.iter().map(|(_, s)| s.x * s.y * s.z).sum();
127 let actual_used = used.saturating_sub(freed_vol);
128 actual_used as f32 / total as f32
129 }
130}
131
132#[derive(Debug, Clone)]
134pub struct AtlasStats {
135 pub total_bricks: u32,
136 pub used_bricks: u32,
137 pub utilization: f32,
138}
139
140#[derive(Debug, Clone)]
142pub struct BrickAtlas {
143 pub bricks: Vec<Brick>,
144 pub atlas_data: Vec<Vec4>,
145 pub atlas_size: UVec3,
146 pub brick_size: u32,
147 packer: BlockPacker,
148 brick_positions: Vec<UVec3>,
149}
150
151impl BrickAtlas {
152 pub fn new(atlas_size: UVec3, brick_size: u32) -> Self {
153 let total = (atlas_size.x * atlas_size.y * atlas_size.z) as usize;
154 Self {
155 bricks: Vec::new(),
156 atlas_data: vec![Vec4::ZERO; total],
157 atlas_size,
158 brick_size,
159 packer: BlockPacker::new(atlas_size),
160 brick_positions: Vec::new(),
161 }
162 }
163
164 pub fn add_brick(&mut self, brick: Brick) -> Option<UVec3> {
166 let bs = UVec3::splat(self.brick_size);
167 let pos = self.packer.pack(bs)?;
168 self.upload_brick(&brick, pos);
169 self.brick_positions.push(pos);
170 self.bricks.push(brick);
171 Some(pos)
172 }
173
174 pub fn upload_brick(&mut self, brick: &Brick, atlas_pos: UVec3) {
176 let bs = self.brick_size;
177 for z in 0..bs {
178 for y in 0..bs {
179 for x in 0..bs {
180 let brick_idx = (z * bs * bs + y * bs + x) as usize;
181 if brick_idx < brick.data.len() {
182 let ax = atlas_pos.x + x;
183 let ay = atlas_pos.y + y;
184 let az = atlas_pos.z + z;
185 if ax < self.atlas_size.x && ay < self.atlas_size.y && az < self.atlas_size.z {
186 let atlas_idx = (az * self.atlas_size.y * self.atlas_size.x
187 + ay * self.atlas_size.x + ax) as usize;
188 if atlas_idx < self.atlas_data.len() {
189 self.atlas_data[atlas_idx] = brick.data[brick_idx];
190 }
191 }
192 }
193 }
194 }
195 }
196 }
197
198 pub fn sample_atlas(&self, world_pos: Vec3, lod: f32) -> Vec4 {
200 let u = world_pos.x.fract() * self.atlas_size.x as f32;
203 let v = world_pos.y.fract() * self.atlas_size.y as f32;
204 let w = world_pos.z.fract() * self.atlas_size.z as f32;
205
206 let scale = 2.0f32.powf(lod);
208 let u = u / scale;
209 let v = v / scale;
210 let w = w / scale;
211
212 let x0 = u.floor() as i32;
214 let y0 = v.floor() as i32;
215 let z0 = w.floor() as i32;
216 let fx = u - u.floor();
217 let fy = v - v.floor();
218 let fz = w - w.floor();
219
220 let sample = |x: i32, y: i32, z: i32| -> Vec4 {
221 let x = x.clamp(0, self.atlas_size.x as i32 - 1) as u32;
222 let y = y.clamp(0, self.atlas_size.y as i32 - 1) as u32;
223 let z = z.clamp(0, self.atlas_size.z as i32 - 1) as u32;
224 let idx = (z * self.atlas_size.y * self.atlas_size.x
225 + y * self.atlas_size.x + x) as usize;
226 if idx < self.atlas_data.len() {
227 self.atlas_data[idx]
228 } else {
229 Vec4::ZERO
230 }
231 };
232
233 let c000 = sample(x0, y0, z0);
234 let c100 = sample(x0 + 1, y0, z0);
235 let c010 = sample(x0, y0 + 1, z0);
236 let c110 = sample(x0 + 1, y0 + 1, z0);
237 let c001 = sample(x0, y0, z0 + 1);
238 let c101 = sample(x0 + 1, y0, z0 + 1);
239 let c011 = sample(x0, y0 + 1, z0 + 1);
240 let c111 = sample(x0 + 1, y0 + 1, z0 + 1);
241
242 let c00 = c000 * (1.0 - fx) + c100 * fx;
243 let c10 = c010 * (1.0 - fx) + c110 * fx;
244 let c01 = c001 * (1.0 - fx) + c101 * fx;
245 let c11 = c011 * (1.0 - fx) + c111 * fx;
246
247 let c0 = c00 * (1.0 - fy) + c10 * fy;
248 let c1 = c01 * (1.0 - fy) + c11 * fy;
249
250 c0 * (1.0 - fz) + c1 * fz
251 }
252
253 pub fn stats(&self) -> AtlasStats {
254 AtlasStats {
255 total_bricks: (self.atlas_size.x / self.brick_size)
256 * (self.atlas_size.y / self.brick_size)
257 * (self.atlas_size.z / self.brick_size),
258 used_bricks: self.bricks.len() as u32,
259 utilization: self.packer.utilization(),
260 }
261 }
262
263 pub fn defragment(&mut self) {
265 let old_bricks: Vec<Brick> = self.bricks.drain(..).collect();
266 let old_positions: Vec<UVec3> = self.brick_positions.drain(..).collect();
267
268 for v in &mut self.atlas_data {
270 *v = Vec4::ZERO;
271 }
272 self.packer = BlockPacker::new(self.atlas_size);
273
274 for brick in old_bricks {
276 self.add_brick(brick);
277 }
278 }
279
280 pub fn remove_brick(&mut self, index: usize) {
282 if index >= self.bricks.len() {
283 return;
284 }
285 let pos = self.brick_positions[index];
286 let bs = UVec3::splat(self.brick_size);
287
288 for z in 0..self.brick_size {
290 for y in 0..self.brick_size {
291 for x in 0..self.brick_size {
292 let ax = pos.x + x;
293 let ay = pos.y + y;
294 let az = pos.z + z;
295 let idx = (az * self.atlas_size.y * self.atlas_size.x
296 + ay * self.atlas_size.x + ax) as usize;
297 if idx < self.atlas_data.len() {
298 self.atlas_data[idx] = Vec4::ZERO;
299 }
300 }
301 }
302 }
303
304 self.packer.free(pos, bs);
305 self.bricks.swap_remove(index);
306 self.brick_positions.swap_remove(index);
307 }
308}
309
310#[derive(Debug, Clone)]
312pub struct IndirectionTable {
313 pub data: Vec<u32>,
314 pub resolution: UVec3,
315}
316
317impl IndirectionTable {
318 pub fn new(resolution: UVec3) -> Self {
319 let count = (resolution.x * resolution.y * resolution.z) as usize;
320 Self {
321 data: vec![u32::MAX; count],
322 resolution,
323 }
324 }
325
326 pub fn set(&mut self, x: u32, y: u32, z: u32, brick_index: u32) {
327 let idx = (z * self.resolution.y * self.resolution.x
328 + y * self.resolution.x + x) as usize;
329 if idx < self.data.len() {
330 self.data[idx] = brick_index;
331 }
332 }
333
334 pub fn get(&self, x: u32, y: u32, z: u32) -> Option<u32> {
335 let idx = (z * self.resolution.y * self.resolution.x
336 + y * self.resolution.x + x) as usize;
337 if idx < self.data.len() && self.data[idx] != u32::MAX {
338 Some(self.data[idx])
339 } else {
340 None
341 }
342 }
343}
344
345pub fn build_indirection(octree: &SparseVoxelOctree, atlas: &BrickAtlas) -> IndirectionTable {
347 let res = UVec3::splat(1u32 << octree.max_depth);
348 let mut table = IndirectionTable::new(res);
349
350 let world_size = octree.world_bounds.size();
351 let voxel_size = world_size / Vec3::new(res.x as f32, res.y as f32, res.z as f32);
352
353 for (brick_idx, brick) in atlas.bricks.iter().enumerate() {
354 let bx = brick.position.x;
355 let by = brick.position.y;
356 let bz = brick.position.z;
357 if bx < res.x && by < res.y && bz < res.z {
358 table.set(bx, by, bz, brick_idx as u32);
359 }
360 }
361
362 table
363}
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368
369 #[test]
370 fn test_brick_atlas_pack_and_upload() {
371 let mut atlas = BrickAtlas::new(UVec3::new(32, 32, 32), 4);
372
373 let mut brick = Brick::new(UVec3::new(0, 0, 0), 4);
374 brick.data[0] = Vec4::new(1.0, 0.0, 0.0, 1.0);
375
376 let pos = atlas.add_brick(brick);
377 assert!(pos.is_some());
378
379 let stats = atlas.stats();
380 assert_eq!(stats.used_bricks, 1);
381 }
382
383 #[test]
384 fn test_packer_utilization() {
385 let mut packer = BlockPacker::new(UVec3::new(16, 16, 16));
386 let pos1 = packer.pack(UVec3::new(4, 4, 4));
387 assert!(pos1.is_some());
388
389 let pos2 = packer.pack(UVec3::new(4, 4, 4));
390 assert!(pos2.is_some());
391 assert_ne!(pos1, pos2);
392
393 let util = packer.utilization();
394 assert!(util > 0.0);
395 }
396
397 #[test]
398 fn test_packer_free_reuse() {
399 let mut packer = BlockPacker::new(UVec3::new(16, 16, 16));
400 let pos1 = packer.pack(UVec3::new(4, 4, 4)).unwrap();
401 packer.free(pos1, UVec3::new(4, 4, 4));
402 let pos2 = packer.pack(UVec3::new(4, 4, 4)).unwrap();
403 assert_eq!(pos1, pos2); }
405
406 #[test]
407 fn test_indirection_table() {
408 let mut table = IndirectionTable::new(UVec3::new(4, 4, 4));
409 assert!(table.get(0, 0, 0).is_none());
410 table.set(1, 2, 3, 42);
411 assert_eq!(table.get(1, 2, 3), Some(42));
412 }
413
414 #[test]
415 fn test_atlas_defragment() {
416 let mut atlas = BrickAtlas::new(UVec3::new(32, 32, 32), 4);
417
418 for i in 0..4 {
419 let brick = Brick::new(UVec3::new(i, 0, 0), 4);
420 atlas.add_brick(brick);
421 }
422 atlas.remove_brick(1);
423 assert_eq!(atlas.bricks.len(), 3);
424
425 atlas.defragment();
426 assert_eq!(atlas.bricks.len(), 3);
427 }
428
429 #[test]
430 fn test_atlas_sample() {
431 let mut atlas = BrickAtlas::new(UVec3::new(8, 8, 8), 4);
432
433 let mut brick = Brick::new(UVec3::ZERO, 4);
435 for v in &mut brick.data {
436 *v = Vec4::new(0.5, 0.5, 0.5, 1.0);
437 }
438 atlas.add_brick(brick);
439
440 let sample = atlas.sample_atlas(Vec3::new(0.05, 0.05, 0.05), 0.0);
442 let _ = sample;
444 }
445
446 #[test]
447 fn test_atlas_full() {
448 let mut atlas = BrickAtlas::new(UVec3::new(4, 4, 4), 4);
449 let brick = Brick::new(UVec3::ZERO, 4);
450 let pos1 = atlas.add_brick(brick.clone());
451 assert!(pos1.is_some());
452 let pos2 = atlas.add_brick(brick);
454 assert!(pos2.is_none());
455 }
456}