1use glam::{Vec2, Vec3, Vec4, Mat4};
10use std::collections::HashMap;
11
12use super::batch::{GlyphInstance, GlyphBatch, BatchKey, BlendMode, RenderLayerOrd};
13use super::sdf_atlas::SdfAtlas;
14use super::sdf_generator::SdfGlyphMetric;
15use super::RenderLayer;
16
17#[repr(C)]
24#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
25pub struct SdfInstanceExtra {
26 pub threshold: f32,
28 pub smoothing: f32,
30 pub outline_params: [f32; 4],
32 pub outline_color: [f32; 4],
34 pub shadow_params: [f32; 4],
36 pub shadow_color: [f32; 4],
38 pub glow_params: [f32; 4],
40 pub glow_color: [f32; 4],
42 pub distortion: [f32; 4],
44}
45
46impl Default for SdfInstanceExtra {
47 fn default() -> Self {
48 Self {
49 threshold: 0.5,
50 smoothing: 0.05,
51 outline_params: [0.0; 4],
52 outline_color: [0.0, 0.0, 0.0, 1.0],
53 shadow_params: [0.0; 4],
54 shadow_color: [0.0, 0.0, 0.0, 0.6],
55 glow_params: [0.0; 4],
56 glow_color: [1.0, 1.0, 1.0, 0.5],
57 distortion: [0.0; 4],
58 }
59 }
60}
61
62pub struct SdfGlyphBatch {
66 pub key: BatchKey,
67 pub base_instances: Vec<GlyphInstance>,
68 pub sdf_extras: Vec<SdfInstanceExtra>,
69}
70
71impl SdfGlyphBatch {
72 pub fn new(key: BatchKey) -> Self {
73 Self {
74 key,
75 base_instances: Vec::with_capacity(64),
76 sdf_extras: Vec::with_capacity(64),
77 }
78 }
79
80 pub fn clear(&mut self) {
81 self.base_instances.clear();
82 self.sdf_extras.clear();
83 }
84
85 pub fn push(&mut self, base: GlyphInstance, extra: SdfInstanceExtra) {
86 self.base_instances.push(base);
87 self.sdf_extras.push(extra);
88 }
89
90 pub fn len(&self) -> usize {
91 self.base_instances.len()
92 }
93
94 pub fn is_empty(&self) -> bool {
95 self.base_instances.is_empty()
96 }
97
98 pub fn base_bytes(&self) -> &[u8] {
100 bytemuck::cast_slice(&self.base_instances)
101 }
102
103 pub fn extra_bytes(&self) -> &[u8] {
105 bytemuck::cast_slice(&self.sdf_extras)
106 }
107
108 pub fn sort_back_to_front(&mut self) {
110 let mut indices: Vec<usize> = (0..self.base_instances.len()).collect();
112 indices.sort_by(|&a, &b| {
113 self.base_instances[b].position[2]
114 .partial_cmp(&self.base_instances[a].position[2])
115 .unwrap_or(std::cmp::Ordering::Equal)
116 });
117
118 let old_base = self.base_instances.clone();
119 let old_extra = self.sdf_extras.clone();
120 for (new_idx, &old_idx) in indices.iter().enumerate() {
121 self.base_instances[new_idx] = old_base[old_idx];
122 self.sdf_extras[new_idx] = old_extra[old_idx];
123 }
124 }
125}
126
127pub struct PendingSdfGlyph {
131 pub key: BatchKey,
132 pub base: GlyphInstance,
133 pub extra: SdfInstanceExtra,
134 pub depth: f32,
135}
136
137pub struct SdfGlyphBatcher {
139 pending: Vec<PendingSdfGlyph>,
140 batches: Vec<SdfGlyphBatch>,
141 stats: SdfBatchStats,
142}
143
144#[derive(Default, Debug, Clone)]
145pub struct SdfBatchStats {
146 pub total_glyphs: usize,
147 pub batch_count: usize,
148 pub outlined_glyphs: usize,
149 pub shadowed_glyphs: usize,
150}
151
152impl SdfGlyphBatcher {
153 pub fn new() -> Self {
154 Self {
155 pending: Vec::with_capacity(4096),
156 batches: Vec::with_capacity(16),
157 stats: SdfBatchStats::default(),
158 }
159 }
160
161 pub fn begin(&mut self) {
162 self.pending.clear();
163 self.stats = SdfBatchStats::default();
164 }
165
166 pub fn push(&mut self, key: BatchKey, base: GlyphInstance, extra: SdfInstanceExtra, depth: f32) {
167 self.pending.push(PendingSdfGlyph { key, base, extra, depth });
168 }
169
170 pub fn push_simple(
171 &mut self,
172 layer: RenderLayer,
173 base: GlyphInstance,
174 smoothing: f32,
175 depth: f32,
176 ) {
177 let key = BatchKey::default_for_layer(layer);
178 let extra = SdfInstanceExtra {
179 smoothing,
180 ..SdfInstanceExtra::default()
181 };
182 self.push(key, base, extra, depth);
183 }
184
185 pub fn finish(&mut self) {
186 self.stats.total_glyphs = self.pending.len();
187
188 self.pending.sort_by(|a, b| {
190 a.key
191 .cmp(&b.key)
192 .then(b.depth.partial_cmp(&a.depth).unwrap_or(std::cmp::Ordering::Equal))
193 });
194
195 self.batches.clear();
196 let mut current_key: Option<BatchKey> = None;
197
198 for item in &self.pending {
199 if item.extra.outline_params[0] > 0.0 {
200 self.stats.outlined_glyphs += 1;
201 }
202 if item.extra.shadow_params[0] > 0.0 {
203 self.stats.shadowed_glyphs += 1;
204 }
205
206 if current_key != Some(item.key) {
207 self.batches.push(SdfGlyphBatch::new(item.key));
208 current_key = Some(item.key);
209 }
210
211 let batch = self.batches.last_mut().unwrap();
212 batch.base_instances.push(item.base);
213 batch.sdf_extras.push(item.extra);
214 }
215
216 self.stats.batch_count = self.batches.len();
217 }
218
219 pub fn batches(&self) -> &[SdfGlyphBatch] {
220 &self.batches
221 }
222
223 pub fn stats(&self) -> &SdfBatchStats {
224 &self.stats
225 }
226
227 pub fn instance_count(&self) -> usize {
228 self.batches.iter().map(|b| b.len()).sum()
229 }
230}
231
232impl Default for SdfGlyphBatcher {
233 fn default() -> Self {
234 Self::new()
235 }
236}
237
238pub fn layout_sdf_text(
246 atlas: &SdfAtlas,
247 text: &str,
248 position: Vec3,
249 scale: f32,
250 color: Vec4,
251 emission: f32,
252 screen_px_per_unit: f32,
253) -> Vec<(GlyphInstance, SdfInstanceExtra)> {
254 let scale_factor = scale / atlas.font_size_px;
255 let smoothing = atlas.compute_smoothing(screen_px_per_unit, scale);
256 let mut result = Vec::with_capacity(text.len());
257 let mut cursor_x = position.x;
258
259 for ch in text.chars() {
260 if ch == ' ' {
261 let metric = atlas.metric_for(ch);
262 cursor_x += metric.advance * scale_factor;
263 continue;
264 }
265
266 let metric = atlas.metric_for(ch);
267 let uv = metric.uv_rect;
268
269 let glyph_pos = Vec3::new(
270 cursor_x + metric.bearing.x * scale_factor,
271 position.y - metric.bearing.y * scale_factor,
272 position.z,
273 );
274
275 let glyph_scale = Vec2::new(
276 metric.size.x * scale_factor,
277 metric.size.y * scale_factor,
278 );
279
280 let base = GlyphInstance {
281 position: glyph_pos.to_array(),
282 scale: [glyph_scale.x, glyph_scale.y],
283 rotation: 0.0,
284 color: color.to_array(),
285 emission,
286 glow_color: [color.x, color.y, color.z],
287 glow_radius: 0.0,
288 uv_offset: [uv[0], uv[1]],
289 uv_size: [uv[2] - uv[0], uv[3] - uv[1]],
290 _pad: [0.0; 2],
291 };
292
293 let extra = SdfInstanceExtra {
294 threshold: 0.5,
295 smoothing,
296 ..SdfInstanceExtra::default()
297 };
298
299 result.push((base, extra));
300 cursor_x += metric.advance * scale_factor;
301 }
302
303 result
304}
305
306pub fn layout_sdf_text_with_effects(
308 atlas: &SdfAtlas,
309 text: &str,
310 position: Vec3,
311 scale: f32,
312 color: Vec4,
313 emission: f32,
314 screen_px_per_unit: f32,
315 effects: &super::sdf_atlas::SdfEffects,
316) -> Vec<(GlyphInstance, SdfInstanceExtra)> {
317 let mut result = layout_sdf_text(atlas, text, position, scale, color, emission, screen_px_per_unit);
318
319 for (_, extra) in &mut result {
320 if effects.bold {
321 extra.threshold = 0.45;
322 }
323 if effects.outline {
324 extra.outline_params = [1.0, effects.outline_width, 0.0, 0.0];
325 extra.outline_color = effects.outline_color.to_array();
326 }
327 if effects.shadow {
328 let uv_off = atlas.shadow_uv_offset(effects.shadow_offset, scale);
329 extra.shadow_params = [1.0, effects.shadow_softness, uv_off.x, uv_off.y];
330 extra.shadow_color = effects.shadow_color.to_array();
331 }
332 if effects.glow {
333 extra.glow_params = [1.0, effects.glow_radius, 0.0, 0.0];
334 extra.glow_color = effects.glow_color.to_array();
335 }
336 extra.distortion = [
337 effects.wave_amplitude,
338 effects.wave_frequency,
339 effects.shake_amount,
340 effects.glitch_intensity,
341 ];
342 }
343
344 result
345}
346
347#[derive(Clone, Debug)]
351pub struct SdfBatchUniforms {
352 pub view_proj: Mat4,
354 pub threshold: f32,
356 pub smoothing: f32,
358 pub time: f32,
360 pub screen_size: Vec2,
362}
363
364impl Default for SdfBatchUniforms {
365 fn default() -> Self {
366 Self {
367 view_proj: Mat4::IDENTITY,
368 threshold: 0.5,
369 smoothing: 0.05,
370 time: 0.0,
371 screen_size: Vec2::new(1280.0, 800.0),
372 }
373 }
374}
375
376#[cfg(test)]
379mod tests {
380 use super::*;
381
382 fn make_sdf_instance(z: f32) -> (GlyphInstance, SdfInstanceExtra) {
383 let base = GlyphInstance::simple(Vec3::new(0.0, 0.0, z), Vec2::ZERO, Vec2::new(0.1, 0.1));
384 let extra = SdfInstanceExtra::default();
385 (base, extra)
386 }
387
388 #[test]
389 fn sdf_batcher_groups_by_key() {
390 let mut batcher = SdfGlyphBatcher::new();
391 batcher.begin();
392
393 let world_key = BatchKey::new(RenderLayer::World, BlendMode::Alpha, 0);
394 let ui_key = BatchKey::new(RenderLayer::UI, BlendMode::Alpha, 0);
395
396 let (b1, e1) = make_sdf_instance(0.0);
397 let (b2, e2) = make_sdf_instance(1.0);
398 let (b3, e3) = make_sdf_instance(0.0);
399
400 batcher.push(world_key, b1, e1, 0.0);
401 batcher.push(world_key, b2, e2, 1.0);
402 batcher.push(ui_key, b3, e3, 0.0);
403
404 batcher.finish();
405
406 assert_eq!(batcher.batches().len(), 2);
407 assert_eq!(batcher.instance_count(), 3);
408 }
409
410 #[test]
411 fn sdf_batch_sort_back_to_front() {
412 let key = BatchKey::new(RenderLayer::World, BlendMode::Alpha, 0);
413 let mut batch = SdfGlyphBatch::new(key);
414 let (b1, e1) = make_sdf_instance(0.0);
415 let (b2, e2) = make_sdf_instance(5.0);
416 let (b3, e3) = make_sdf_instance(2.0);
417 batch.push(b1, e1);
418 batch.push(b2, e2);
419 batch.push(b3, e3);
420 batch.sort_back_to_front();
421
422 let zs: Vec<f32> = batch.base_instances.iter().map(|i| i.position[2]).collect();
423 assert!(zs[0] >= zs[1] && zs[1] >= zs[2]);
424 }
425
426 #[test]
427 fn sdf_instance_extra_size() {
428 let extra = SdfInstanceExtra::default();
430 let bytes: &[u8] = bytemuck::bytes_of(&extra);
431 assert_eq!(bytes.len(), std::mem::size_of::<SdfInstanceExtra>());
432 }
433}