1pub mod profiler;
20pub mod graph;
21pub mod console;
22pub mod metrics;
23pub mod inspector;
24
25pub use profiler::FrameProfiler;
26pub use graph::MathGraph;
27
28use crate::{ProofEngine, Glyph, RenderLayer, MathFunction};
29use crate::render::pipeline::FrameStats;
30use glam::{Vec3, Vec4};
31
32pub struct DebugOverlay {
40 pub enabled: bool,
41 pub show_fps: bool,
42 pub show_counts: bool,
43 pub show_fields: bool,
44 pub show_camera: bool,
45 pub show_time: bool,
46 pub text_color: Vec4,
47 pub warning_color: Vec4,
48 pub critical_color: Vec4,
49 glyph_ids: Vec<crate::glyph::GlyphId>,
51 profiler: FrameProfiler,
52 graph: Option<MathGraph>,
53}
54
55impl DebugOverlay {
56 pub fn new() -> Self {
57 Self {
58 enabled: true,
59 show_fps: true,
60 show_counts: true,
61 show_fields: false,
62 show_camera: false,
63 show_time: true,
64 text_color: Vec4::new(0.6, 1.0, 0.6, 0.8),
65 warning_color: Vec4::new(1.0, 0.8, 0.2, 1.0),
66 critical_color: Vec4::new(1.0, 0.2, 0.2, 1.0),
67 glyph_ids: Vec::new(),
68 profiler: FrameProfiler::new(120),
69 graph: None,
70 }
71 }
72
73 pub fn with_graph(mut self, func: MathFunction, range: (f32, f32)) -> Self {
75 self.graph = Some(MathGraph::new(func, range));
76 self
77 }
78
79 pub fn render(&mut self, engine: &mut ProofEngine, stats: &FrameStats) {
81 if !self.enabled { return; }
82
83 for id in self.glyph_ids.drain(..) {
85 engine.scene.glyphs.despawn(id);
86 }
87
88 let cam_pos = engine.camera.target.position();
90 let cam_z = engine.camera.position.position().z;
91 let fov_rad = engine.camera.fov.position.to_radians();
92 let half_h = cam_z * (fov_rad * 0.5).tan();
93 let aspect = 16.0 / 9.0; let half_w = half_h * aspect;
95 let origin = cam_pos + Vec3::new(-half_w + 0.5, half_h - 0.5, 0.0);
96
97 let mut line = 0;
98 let line_h = -0.65_f32;
99 let char_w = 0.40_f32;
100
101 if self.show_fps {
103 let fps = stats.fps;
104 let color = if fps >= 55.0 { self.text_color }
105 else if fps >= 30.0 { self.warning_color }
106 else { self.critical_color };
107 let label = format!("FPS:{:>5.1} dt:{:.1}ms", fps, stats.dt * 1000.0);
108 self.draw_string(engine, &label, origin + Vec3::new(0.0, line as f32 * line_h, 0.0), char_w, color);
109 line += 1;
110 }
111
112 if self.show_counts {
114 let label = format!(
115 "G:{:<4} P:{:<4} F:{:<2}",
116 stats.glyph_count, stats.particle_count, stats.frame_number % 10000
117 );
118 self.draw_string(engine, &label, origin + Vec3::new(0.0, line as f32 * line_h, 0.0), char_w, self.text_color);
119 line += 1;
120 }
121
122 if self.show_camera {
124 let pos = engine.camera.position.position();
125 let label = format!("CAM ({:.1},{:.1},{:.1})", pos.x, pos.y, pos.z);
126 self.draw_string(engine, &label, origin + Vec3::new(0.0, line as f32 * line_h, 0.0), char_w, self.text_color);
127 line += 1;
128 }
129
130 if self.show_time {
132 let label = format!("T:{:.2}s", engine.scene.time);
133 self.draw_string(engine, &label, origin + Vec3::new(0.0, line as f32 * line_h, 0.0), char_w, self.text_color);
134 }
135
136 if let Some(ref graph) = self.graph {
138 let graph_origin = origin + Vec3::new(0.0, -4.0, 0.0);
139 let ids = graph.render(engine, graph_origin, 20.0, 6.0, engine.scene.time);
140 self.glyph_ids.extend(ids);
141 }
142 }
143
144 fn draw_string(
147 &mut self,
148 engine: &mut ProofEngine,
149 text: &str,
150 position: Vec3,
151 char_w: f32,
152 color: Vec4,
153 ) {
154 for (i, ch) in text.chars().enumerate() {
155 let x = position.x + i as f32 * char_w;
156 let id = engine.scene.spawn_glyph(Glyph {
157 character: ch,
158 position: Vec3::new(x, position.y, position.z),
159 color,
160 emission: 0.2,
161 glow_color: Vec3::new(color.x, color.y, color.z),
162 glow_radius: 0.0,
163 scale: glam::Vec2::splat(0.55),
164 layer: RenderLayer::UI,
165 ..Default::default()
166 });
167 self.glyph_ids.push(id);
168 }
169 }
170}
171
172impl Default for DebugOverlay {
173 fn default() -> Self { Self::new() }
174}