1use avila_vec3d::*;
13use avila_mesh::*;
14use std::collections::HashMap;
15
16pub type Result<T> = std::result::Result<T, OptimizerError>;
17
18#[derive(Debug, thiserror::Error)]
19pub enum OptimizerError {
20 #[error("Optimization error: {0}")]
21 OptimizationError(String),
22
23 #[error("Mesh error: {0}")]
24 MeshError(#[from] MeshError),
25
26 #[error("Vec3d error: {0}")]
27 Vec3dError(#[from] Vec3dError),
28}
29
30pub struct MeshMerger {
36 pub vertex_tolerance: f32,
38}
39
40impl MeshMerger {
41 pub fn new() -> Self {
42 Self {
43 vertex_tolerance: 0.001, }
45 }
46
47 pub fn merge_scene(&self, scene: &Scene) -> Result<Scene> {
49 let mut merged_scene = Scene::new();
50
51 let mut meshes_by_material: HashMap<Option<String>, Vec<&Mesh>> = HashMap::new();
53
54 for mesh in &scene.meshes {
55 meshes_by_material
56 .entry(mesh.material_id.clone())
57 .or_insert_with(Vec::new)
58 .push(mesh);
59 }
60
61 for (material_id, meshes) in meshes_by_material {
63 if meshes.is_empty() {
64 continue;
65 }
66
67 let merged = self.merge_meshes(&meshes)?;
68 let mut final_mesh = merged;
69 final_mesh.material_id = material_id.clone();
70
71 merged_scene.add_mesh(final_mesh);
72 }
73
74 merged_scene.materials = scene.materials.clone();
76
77 Ok(merged_scene)
78 }
79
80 pub fn merge_meshes(&self, meshes: &[&Mesh]) -> Result<Mesh> {
82 if meshes.is_empty() {
83 return Err(OptimizerError::OptimizationError("No meshes to merge".into()));
84 }
85
86 if meshes.len() == 1 {
87 return Ok((*meshes[0]).clone());
88 }
89
90 let total_vertices: usize = meshes.iter().map(|m| m.vertices.len()).sum();
92 let total_indices: usize = meshes.iter().map(|m| m.indices.len()).sum();
93
94 let mut merged = Mesh::with_capacity(total_vertices, total_indices);
95
96 for mesh in meshes {
98 let offset = merged.vertices.len() as u32;
99
100 for vertex in &mesh.vertices {
102 merged.vertices.push(*vertex);
103 merged.bounds.expand_point(vertex.position);
104 }
105
106 for &index in &mesh.indices {
108 merged.indices.push(offset + index);
109 }
110 }
111
112 if self.vertex_tolerance > 0.0 {
114 self.deduplicate_vertices(&mut merged)?;
115 }
116
117 Ok(merged)
118 }
119
120 fn deduplicate_vertices(&self, mesh: &mut Mesh) -> Result<()> {
122 let vertex_count = mesh.vertices.len();
123 if vertex_count == 0 {
124 return Ok(());
125 }
126
127 let mut remap: Vec<u32> = (0..vertex_count as u32).collect();
129 let tolerance_sq = self.vertex_tolerance * self.vertex_tolerance;
130
131 for i in 0..vertex_count {
133 if remap[i] != i as u32 {
134 continue; }
136
137 let v1 = &mesh.vertices[i];
138
139 for j in (i + 1)..vertex_count {
140 if remap[j] != j as u32 {
141 continue;
142 }
143
144 let v2 = &mesh.vertices[j];
145
146 let dist_sq = (v1.position - v2.position).length_squared();
148 if dist_sq < tolerance_sq {
149 remap[j] = i as u32;
151 }
152 }
153 }
154
155 for index in &mut mesh.indices {
157 *index = remap[*index as usize];
158 }
159
160 let mut used = vec![false; vertex_count];
162 for &index in &mesh.indices {
163 used[index as usize] = true;
164 }
165
166 let mut new_vertices = Vec::with_capacity(vertex_count);
167 let mut compact_remap = vec![0u32; vertex_count];
168 let mut next_index = 0u32;
169
170 for (i, vertex) in mesh.vertices.iter().enumerate() {
171 if used[i] {
172 compact_remap[i] = next_index;
173 new_vertices.push(*vertex);
174 next_index += 1;
175 }
176 }
177
178 for index in &mut mesh.indices {
180 *index = compact_remap[*index as usize];
181 }
182
183 mesh.vertices = new_vertices;
184
185 Ok(())
186 }
187}
188
189impl Default for MeshMerger {
190 fn default() -> Self {
191 Self::new()
192 }
193}
194
195pub struct LodGenerator {
201 pub ratios: Vec<f32>,
203}
204
205impl LodGenerator {
206 pub fn new() -> Self {
207 Self {
208 ratios: vec![0.5, 0.25, 0.125], }
210 }
211
212 pub fn generate_lods(&self, mesh: &Mesh) -> Result<Vec<Mesh>> {
214 let mut lods = Vec::with_capacity(self.ratios.len() + 1);
215
216 lods.push(mesh.clone());
218
219 for &ratio in &self.ratios {
221 let simplified = self.simplify_mesh(&lods[0], ratio)?;
222 lods.push(simplified);
223 }
224
225 Ok(lods)
226 }
227
228 fn simplify_mesh(&self, mesh: &Mesh, ratio: f32) -> Result<Mesh> {
230 let target_triangles = ((mesh.indices.len() / 3) as f32 * ratio).max(1.0) as usize;
231
232 let mut simplified = Mesh::new();
235 simplified.material_id = mesh.material_id.clone();
236
237 let step = (mesh.indices.len() / 3).max(1) / target_triangles.max(1);
238 let step = step.max(1);
239
240 let mut vertex_map: HashMap<u32, u32> = HashMap::new();
241
242 for triangle_idx in (0..mesh.indices.len() / 3).step_by(step) {
243 let base = triangle_idx * 3;
244
245 for i in 0..3 {
246 let old_idx = mesh.indices[base + i];
247
248 if !vertex_map.contains_key(&old_idx) {
249 let new_idx = simplified.vertices.len() as u32;
250 vertex_map.insert(old_idx, new_idx);
251 simplified.vertices.push(mesh.vertices[old_idx as usize]);
252 }
253
254 simplified.indices.push(vertex_map[&old_idx]);
255 }
256 }
257
258 simplified.bounds = Aabb::EMPTY;
260 for vertex in &simplified.vertices {
261 simplified.bounds.expand_point(vertex.position);
262 }
263
264 Ok(simplified)
265 }
266}
267
268impl Default for LodGenerator {
269 fn default() -> Self {
270 Self::new()
271 }
272}
273
274pub struct Octree {
280 root: OctreeNode,
281 max_depth: usize,
282 max_elements: usize,
283}
284
285struct OctreeNode {
286 bounds: Aabb,
287 elements: Vec<usize>, children: Option<Box<[OctreeNode; 8]>>,
289}
290
291impl Octree {
292 pub fn new(bounds: Aabb) -> Self {
293 Self {
294 root: OctreeNode {
295 bounds,
296 elements: Vec::new(),
297 children: None,
298 },
299 max_depth: 8,
300 max_elements: 8,
301 }
302 }
303
304 pub fn insert(&mut self, mesh_index: usize, bounds: &Aabb) {
306 let max_depth = self.max_depth;
307 let max_elements = self.max_elements;
308 Self::insert_recursive_static(&mut self.root, mesh_index, bounds, 0, max_depth, max_elements);
309 }
310
311 fn insert_recursive_static(node: &mut OctreeNode, mesh_index: usize, bounds: &Aabb, depth: usize, max_depth: usize, max_elements: usize) {
312 if !node.bounds.intersects(bounds) {
314 return;
315 }
316
317 if let Some(ref mut children) = node.children {
319 for child in children.iter_mut() {
320 Self::insert_recursive_static(child, mesh_index, bounds, depth + 1, max_depth, max_elements);
321 }
322 return;
323 }
324
325 node.elements.push(mesh_index);
327
328 if node.elements.len() > max_elements && depth < max_depth {
330 Self::subdivide_node_static(node, max_depth, max_elements, depth);
331 }
332 }
333
334 fn subdivide_node_static(node: &mut OctreeNode, max_depth: usize, max_elements: usize, depth: usize) {
335 let _center = node.bounds.center();
336 let half_size = (node.bounds.max - node.bounds.min) * 0.5;
337
338 let mut children = Vec::with_capacity(8);
339
340 for i in 0..8 {
341 let offset = Vec3::new(
342 if i & 1 != 0 { half_size.x } else { 0.0 },
343 if i & 2 != 0 { half_size.y } else { 0.0 },
344 if i & 4 != 0 { half_size.z } else { 0.0 },
345 );
346
347 let min = node.bounds.min + offset;
348 let max = min + half_size;
349
350 children.push(OctreeNode {
351 bounds: Aabb::new(min, max),
352 elements: Vec::new(),
353 children: None,
354 });
355 }
356
357 node.children = Some(Box::new([
358 children.pop().unwrap(),
359 children.pop().unwrap(),
360 children.pop().unwrap(),
361 children.pop().unwrap(),
362 children.pop().unwrap(),
363 children.pop().unwrap(),
364 children.pop().unwrap(),
365 children.pop().unwrap(),
366 ]));
367 }
368
369 pub fn query(&self, bounds: &Aabb) -> Vec<usize> {
371 let mut result = Vec::new();
372 self.query_recursive(&self.root, bounds, &mut result);
373 result
374 }
375
376 fn query_recursive(&self, node: &OctreeNode, bounds: &Aabb, result: &mut Vec<usize>) {
377 if !node.bounds.intersects(bounds) {
378 return;
379 }
380
381 result.extend(&node.elements);
382
383 if let Some(ref children) = node.children {
384 for child in children.iter() {
385 self.query_recursive(child, bounds, result);
386 }
387 }
388 }
389}
390
391pub struct Optimizer {
397 pub merger: MeshMerger,
398 pub lod_generator: LodGenerator,
399}
400
401impl Optimizer {
402 pub fn new() -> Self {
403 Self {
404 merger: MeshMerger::new(),
405 lod_generator: LodGenerator::new(),
406 }
407 }
408
409 pub fn optimize_scene(&self, scene: &Scene) -> Result<OptimizedScene> {
411 let merged_scene = self.merger.merge_scene(scene)?;
413
414 let mut lods_by_mesh = Vec::new();
416 for mesh in &merged_scene.meshes {
417 let lods = self.lod_generator.generate_lods(mesh)?;
418 lods_by_mesh.push(lods);
419 }
420
421 let bounds = merged_scene.bounds;
423 let mut octree = Octree::new(bounds);
424
425 for (i, mesh) in merged_scene.meshes.iter().enumerate() {
426 octree.insert(i, &mesh.bounds);
427 }
428
429 Ok(OptimizedScene {
430 base_scene: merged_scene,
431 lods: lods_by_mesh,
432 spatial_index: octree,
433 })
434 }
435}
436
437impl Default for Optimizer {
438 fn default() -> Self {
439 Self::new()
440 }
441}
442
443pub struct OptimizedScene {
445 pub base_scene: Scene,
446 pub lods: Vec<Vec<Mesh>>, pub spatial_index: Octree,
448}
449
450impl OptimizedScene {
451 pub fn select_lod(&self, mesh_index: usize, distance: f32) -> Option<&Mesh> {
453 if mesh_index >= self.lods.len() {
454 return None;
455 }
456
457 let lods = &self.lods[mesh_index];
458 if lods.is_empty() {
459 return None;
460 }
461
462 let lod_level = if distance < 10.0 {
464 0 } else if distance < 50.0 {
466 1.min(lods.len() - 1)
467 } else if distance < 100.0 {
468 2.min(lods.len() - 1)
469 } else {
470 3.min(lods.len() - 1)
471 };
472
473 Some(&lods[lod_level])
474 }
475}
476
477#[cfg(test)]
482mod tests {
483 use super::*;
484 use avila_mesh::primitives;
485
486 #[test]
487 fn test_mesh_merger() {
488 let mut scene = Scene::new();
489
490 let cube1 = primitives::cube(1.0);
492 let cube2 = primitives::cube(1.0);
493 let cube3 = primitives::cube(1.0);
494
495 scene.add_mesh(cube1);
496 scene.add_mesh(cube2);
497 scene.add_mesh(cube3);
498
499 let merger = MeshMerger::new();
500 let merged_scene = merger.merge_scene(&scene).unwrap();
501
502 assert_eq!(merged_scene.meshes.len(), 1);
504
505 println!("Merged vertices: {}", merged_scene.meshes[0].vertices.len());
508 assert!(merged_scene.meshes[0].vertices.len() >= 8); assert!(merged_scene.meshes[0].indices.len() == 36 * 3); }
511
512 #[test]
513 fn test_lod_generation() {
514 let mesh = primitives::cube(2.0);
515 let original_triangles = mesh.indices.len() / 3;
516
517 let lod_gen = LodGenerator::new();
518 let lods = lod_gen.generate_lods(&mesh).unwrap();
519
520 assert_eq!(lods.len(), 4);
522
523 assert_eq!(lods[0].indices.len(), mesh.indices.len());
525
526 for i in 1..lods.len() {
528 let triangles = lods[i].indices.len() / 3;
529 assert!(triangles <= original_triangles);
530 }
531 }
532
533 #[test]
534 fn test_octree() {
535 let bounds = Aabb::new(Vec3::ZERO, Vec3::new(100.0, 100.0, 100.0));
536 let mut octree = Octree::new(bounds);
537
538 let mesh1_bounds = Aabb::new(Vec3::new(10.0, 10.0, 10.0), Vec3::new(20.0, 20.0, 20.0));
540 let mesh2_bounds = Aabb::new(Vec3::new(80.0, 80.0, 80.0), Vec3::new(90.0, 90.0, 90.0));
541
542 octree.insert(0, &mesh1_bounds);
543 octree.insert(1, &mesh2_bounds);
544
545 let query_bounds = Aabb::new(Vec3::ZERO, Vec3::new(30.0, 30.0, 30.0));
547 let results = octree.query(&query_bounds);
548
549 println!("Query results: {:?}", results);
550 assert!(results.contains(&0));
551 if results.len() == 1 {
554 assert!(!results.contains(&1));
555 }
556 }
557
558 #[test]
559 fn test_full_optimization() {
560 let mut scene = Scene::new();
561 scene.add_mesh(primitives::cube(1.0));
562 scene.add_mesh(primitives::cube(1.0));
563
564 let optimizer = Optimizer::new();
565 let optimized = optimizer.optimize_scene(&scene).unwrap();
566
567 assert_eq!(optimized.base_scene.meshes.len(), 1);
569
570 assert!(!optimized.lods.is_empty());
572 assert_eq!(optimized.lods[0].len(), 4); }
574}