1use std::sync::atomic::{AtomicBool, Ordering};
2
3use crate::*;
4
5const Z_DIV: f32 = 1000.0;
6static SPRITE_CULLING_ENABLED: AtomicBool = AtomicBool::new(true);
7
8pub fn set_sprite_culling(enabled: bool) {
16 SPRITE_CULLING_ENABLED.store(enabled, Ordering::SeqCst);
17}
18
19pub fn get_sprite_culling() -> bool {
21 SPRITE_CULLING_ENABLED.load(Ordering::SeqCst)
22}
23
24pub fn splat(v: f32) -> Vec2 {
25 Vec2::splat(v)
26}
27
28pub fn isplat(v: i32) -> IVec2 {
29 IVec2::splat(v)
30}
31
32pub fn usplat(v: u32) -> UVec2 {
33 UVec2::splat(v)
34}
35
36pub fn simple_window(title: &str) -> egui::Window {
37 egui::Window::new(title).resizable(false).collapsible(false)
38}
39
40pub trait Vec2EngineExtensions {
41 fn as_world(&self) -> Position;
42 fn as_world_size(&self) -> Size;
43}
44
45impl Vec2EngineExtensions for Vec2 {
46 fn as_world(&self) -> Position {
47 Position::world(self.x, self.y)
48 }
49
50 fn as_world_size(&self) -> Size {
51 Size::world(self.x, self.y)
52 }
53}
54
55pub fn get_fps() -> i32 {
56 GLOBAL_STATE.borrow().fps
57}
58
59pub fn get_frame() -> u32 {
60 GLOBAL_STATE.borrow().frame
61}
62
63pub fn inc_frame_num() {
64 GLOBAL_STATE.borrow_mut().frame += 1;
65}
66
67pub fn sin_range(offset: f32, speed: f32, min: f32, max: f32) -> f32 {
68 min + (max - min) *
69 ((speed * (get_time() as f32 + offset)).sin() / 2.0 + 0.5)
70}
71
72pub fn clear_background(color: Color) {
73 GLOBAL_STATE.borrow_mut().clear_color = color;
74}
75
76pub fn draw_quad(
77 position: Vec2,
78 size: Vec2,
79 rotation: f32,
80 color: Color,
81 z_index: i32,
82 texture: TextureHandle,
83 scroll_offset: Vec2,
84) {
85 draw_sprite_ex(texture, position, color, z_index, DrawTextureParams {
86 dest_size: Some(size.as_world_size()),
87 scroll_offset,
88 rotation,
89 ..Default::default()
90 });
91}
92
93pub fn draw_comfy(position: Vec2, tint: Color, z_index: i32, world_size: Vec2) {
94 draw_sprite(
95 texture_id("_builtin-comfy"),
96 position,
97 tint,
98 z_index,
99 world_size,
100 );
101}
102
103pub fn draw_sprite(
111 texture: TextureHandle,
112 position: Vec2,
113 tint: Color,
114 z_index: i32,
115 world_size: Vec2,
116) {
117 draw_sprite_rot(texture, position, tint, z_index, 0.0, world_size);
118}
119
120pub fn draw_sprite_ex(
121 texture: TextureHandle,
122 position: Vec2,
123 tint: Color,
124 z_index: i32,
125 params: DrawTextureParams,
126) {
127 let _span = span!("draw_sprite_ex");
128
129 let raw = RawDrawParams {
130 dest_size: params.dest_size.map(|s| s.to_world()),
131 source_rect: params.source_rect,
132 rotation: params.rotation,
133 flip_x: params.flip_x,
134 flip_y: params.flip_y,
135 pivot: params.pivot,
136 };
137
138 let size = match Assets::image_size(texture) {
146 ImageSizeResult::Loaded(size) => size,
147 ImageSizeResult::LoadingInProgress => {
148 return;
149 }
150 ImageSizeResult::ImageNotFound => {
151 error!("NO SIZE FOR TEXTURE {:?}", texture);
152 UVec2::ONE
153 }
154 };
155
156 let vertices = rotated_rectangle(
157 position.extend(z_index as f32 / Z_DIV),
158 raw,
159 size.x as f32,
160 size.y as f32,
161 tint,
162 params.scroll_offset,
163 );
164
165 const QUAD_INDICES_U32: &[u32] = &[0, 2, 1, 0, 3, 2];
166
167 let mesh = Mesh {
168 origin: position.extend(z_index as f32),
169 vertices: SmallVec::from_slice(&vertices),
170 indices: QUAD_INDICES_U32.into(),
171 z_index,
172 texture: Some(texture),
173 y_sort_offset: params.y_sort_offset,
174 };
175
176 draw_mesh_ex(mesh, params.blend_mode);
177}
178
179pub enum SpriteAlign {
180 TopLeft,
181 TopCenter,
182 TopRight,
183 CenterLeft,
184 Center,
185 CenterRight,
186 BottomLeft,
187 BottomCenter,
188 BottomRight,
189}
190
191pub struct DrawTextureProParams {
192 pub source_rect: Option<IRect>,
195 pub align: SpriteAlign,
199 pub pivot: Option<Vec2>,
202 pub size: Vec2,
204 pub rotation: f32,
206 pub flip_x: bool,
208 pub flip_y: bool,
210 pub blend_mode: BlendMode,
212 pub rotation_x: f32,
214 pub y_sort_offset: f32,
218}
219
220impl Default for DrawTextureProParams {
221 fn default() -> Self {
222 Self {
223 source_rect: None,
224 align: SpriteAlign::Center,
225 pivot: None,
226 size: Vec2::ONE,
227 rotation: 0.0,
228 flip_x: false,
229 flip_y: false,
230 blend_mode: Default::default(),
231 rotation_x: 0.0,
232 y_sort_offset: 0.0,
233 }
234 }
235}
236
237pub fn draw_sprite_pro(
238 texture: TextureHandle,
239 position: Vec2,
240 tint: Color,
241 z_index: i32,
242 params: DrawTextureProParams,
243) {
244 let _span = span!("draw_sprite_pro");
245
246 #[cfg(feature = "sprite-pro-counter")]
247 perf_counter_inc("draw_sprite_pro", 1);
248
249 if SPRITE_CULLING_ENABLED.load(Ordering::SeqCst) {
250 if !CAMERA_BOUNDS.load().contains_rect_safe(position, params.size) {
251 return;
252 }
253 }
254
255 fn rotate_point_around_pivot(point: Vec2, pivot: Vec2, angle: f32) -> Vec2 {
256 let s = angle.sin();
257 let c = angle.cos();
258 let point = point - pivot;
259 let xnew = point.x * c - point.y * s;
260 let ynew = point.x * s + point.y * c;
261 Vec2::new(xnew, ynew) + pivot
262 }
263
264 let origin = match params.align {
266 SpriteAlign::TopLeft => Vec2::new(0.0, params.size.y),
267 SpriteAlign::TopCenter => Vec2::new(params.size.x / 2.0, params.size.y),
268 SpriteAlign::TopRight => Vec2::new(params.size.x, params.size.y),
269 SpriteAlign::CenterLeft => Vec2::new(0.0, params.size.y / 2.0),
270 SpriteAlign::Center => {
271 Vec2::new(params.size.x / 2.0, params.size.y / 2.0)
272 }
273 SpriteAlign::CenterRight => {
274 Vec2::new(params.size.x, params.size.y / 2.0)
275 }
276 SpriteAlign::BottomLeft => Vec2::ZERO,
277 SpriteAlign::BottomCenter => Vec2::new(params.size.x / 2.0, 0.0),
278 SpriteAlign::BottomRight => Vec2::new(params.size.x, 0.0),
279 };
280
281 let corners = [
282 Vec2::ZERO,
283 Vec2::new(params.size.x, 0.0),
284 params.size,
285 Vec2::new(0.0, params.size.y),
286 ];
287
288 let pivot = params.pivot.unwrap_or(Vec2::ZERO);
289 let rotated_corners = corners.map(|corner| {
290 rotate_point_around_pivot(
291 position - origin + corner,
292 position + pivot,
293 params.rotation,
294 )
295 });
296
297 let texture_size = match Assets::image_size(texture) {
298 ImageSizeResult::Loaded(size) => size,
299 ImageSizeResult::LoadingInProgress => {
300 return;
301 }
302 ImageSizeResult::ImageNotFound => {
303 error!("NO SIZE FOR TEXTURE {:?}", texture);
304 UVec2::ONE
305 }
306 };
307
308 let source_rect = params.source_rect.unwrap_or(IRect {
309 offset: IVec2::new(0, 0),
310 size: IVec2::new(texture_size.x as i32, texture_size.y as i32),
311 });
312
313 let dims = IRect {
314 size: source_rect.size,
315 offset: ivec2(
316 source_rect.offset.x,
317 texture_size.y as i32 - source_rect.offset.y - source_rect.size.y,
318 ),
319 };
320
321 let mut tex_0_x = dims.offset.x as f32 / texture_size.x as f32;
322 let mut tex_0_y = dims.offset.y as f32 / texture_size.y as f32;
323 let mut tex_1_x =
324 (dims.offset.x + dims.size.x) as f32 / texture_size.x as f32;
325 let mut tex_1_y =
326 (dims.offset.y + dims.size.y) as f32 / texture_size.y as f32;
327
328 if params.flip_x {
329 std::mem::swap(&mut tex_0_x, &mut tex_1_x);
330 }
331 if params.flip_y {
332 std::mem::swap(&mut tex_0_y, &mut tex_1_y);
333 }
334
335 let tex_coords = [
336 Vec2::new(tex_0_x, tex_0_y),
337 Vec2::new(tex_1_x, tex_0_y),
338 Vec2::new(tex_1_x, tex_1_y),
339 Vec2::new(tex_0_x, tex_1_y),
340 ];
341
342 let vertices = [0, 1, 2, 3].map(|i| {
343 SpriteVertex::new(
344 rotate_around_point(
345 rotated_corners[i].extend(z_index as f32 / Z_DIV),
346 position.extend(z_index as f32 / Z_DIV),
347 params.rotation_x,
348 ),
349 tex_coords[i],
351 tint,
352 )
353 });
354
355
356 const QUAD_INDICES_U32: &[u32] = &[0, 2, 1, 0, 3, 2];
357
358 let mesh = Mesh {
359 origin: position.extend(z_index as f32),
360 vertices: SmallVec::from_slice(&vertices),
361 indices: QUAD_INDICES_U32.into(),
362 z_index,
363 texture: Some(texture),
364 y_sort_offset: params.y_sort_offset,
365 };
366
367 draw_mesh_ex(mesh, params.blend_mode);
368}
369
370fn rotate_around_point(point: Vec3, pivot: Vec3, angle_rad: f32) -> Vec3 {
371 let translate_to_origin = Mat4::from_translation(-pivot);
372 let rotate_around_x = Mat4::from_rotation_x(angle_rad);
373 let translate_back = Mat4::from_translation(pivot);
374
375 let combined_transform =
376 translate_back * rotate_around_x * translate_to_origin;
377 combined_transform.transform_point3(point)
378}
379
380pub fn draw_rectangle_z_tex(
381 position: Position,
382 w: f32,
383 h: f32,
384 color: Color,
385 z_index: i32,
386 texture: Option<TextureHandle>,
387 blend_mode: BlendMode,
388) {
389 let (x, y) = position.to_world().tuple();
390
391 let hw = w / 2.0;
392 let hh = h / 2.0;
393
394 let z = z_index as f32 / Z_DIV;
395
396 #[rustfmt::skip]
397 let vertices = [
398 SpriteVertex::new(vec3(x - hw, y - hh, z), vec2(0.0, 0.0), color),
399 SpriteVertex::new(vec3(x + hw, y - hh, z), vec2(1.0, 0.0), color),
400 SpriteVertex::new(vec3(x + hw, y + hh, z), vec2(1.0, 1.0), color),
401 SpriteVertex::new(vec3(x - hw, y + hh, z), vec2(0.0, 1.0), color),
402 ];
403 let indices = [0, 1, 2, 0, 2, 3];
404
405 draw_mesh_ex(
406 Mesh {
407 origin: vec3(x, y, z_index as f32),
408 vertices: SmallVec::from_slice(&vertices),
409 indices: indices.into(),
410 z_index,
411 texture,
412 y_sort_offset: 0.0,
413 },
414 blend_mode,
415 );
416}
417
418pub fn draw_rect(center: Vec2, size: Vec2, color: Color, z_index: i32) {
419 let _span = span!("draw_rect");
420 draw_quad(center, size, 0.0, color, z_index, texture_id("1px"), Vec2::ZERO);
421}
422
423pub fn draw_rect_rot(
424 center: Vec2,
425 size: Vec2,
426 rotation: f32,
427 color: Color,
428 z_index: i32,
429) {
430 let _span = span!("draw_rect_outline_rot");
431
432 draw_quad(
433 center,
434 size,
435 rotation,
436 color,
437 z_index,
438 texture_id("1px"),
439 Vec2::ZERO,
440 );
441}
442
443pub fn draw_rect_outline(
444 center: Vec2,
445 size: Vec2,
446 thickness: f32,
447 color: Color,
448 z_index: i32,
449) {
450 let _span = span!("draw_rect_outline");
451
452 let (x, y) = center.tuple();
453 let w = size.x;
454 let h = size.y;
455
456 let hw = w / 2.0;
457 let hh = h / 2.0;
458
459 let x = x - hw;
460 let y = y - hh;
461
462 let mut vertices = Vec::with_capacity(6 * 4);
481 let mut indices = Vec::with_capacity(6 * 6);
482
483 create_line_strip(
484 &[
485 vec2(x, y),
486 vec2(x, y + h),
487 vec2(x + w, y + h),
488 vec2(x + w, y),
489 vec2(x, y),
490 ],
491 thickness,
492 &mut vertices,
493 &mut indices,
494 );
495
496 let z = z_index as f32 / Z_DIV;
497
498 let vertices = vertices
499 .into_iter()
500 .map(|v| SpriteVertex::new(v.extend(z), Vec2::ZERO, color))
501 .collect_vec();
502
503 draw_mesh(Mesh {
504 origin: center.extend(z_index as f32),
505 vertices: vertices.into(),
506 indices: indices.into(),
507 z_index,
508 texture: None,
509 y_sort_offset: 0.0,
510 });
511}
512
513pub fn draw_labeled_rect_corners(
514 label: &str,
515 center: Vec2,
516 size: Vec2,
517 thickness: f32,
518 corner_size: f32,
519 color: Color,
520 z_index: i32,
521) {
522 draw_text_ex(
523 label,
524 center + vec2(-size.x, size.y) / 2.0,
525 TextAlign::BottomLeft,
526 TextParams {
527 z_index,
528 font: egui::FontId::new(
529 12.0 / egui_scale_factor(),
530 egui::FontFamily::Proportional,
531 ),
532 rotation: 0.0,
533 color: color.lighten(0.3),
534 },
535 );
536
537 draw_rect_corners(center, size, thickness, corner_size, color, z_index);
538}
539
540pub fn labeled_hover_aabb(
541 label: &str,
542 aabb: &AABB,
543 color: Color,
544 z_index: i32,
545) {
546 let hover = aabb.contains(mouse_world());
547 let color = if hover { color.lighten(0.15) } else { color.darken(0.1) };
548
549 labeled_aabb(label, aabb, color, z_index);
550}
551
552pub fn labeled_aabb(label: &str, aabb: &AABB, color: Color, z_index: i32) {
553 draw_labeled_rect_corners(
554 label,
555 aabb.center(),
556 aabb.size(),
557 4.0 * px(),
558 1.0,
559 color,
560 z_index,
561 );
562}
563
564pub fn draw_rect_corners(
565 center: Vec2,
566 size: Vec2,
567 thickness: f32,
568 corner_size: f32,
569 color: Color,
570 z_index: i32,
571) {
572 let (x, y) = center.tuple();
573 let w = size.x;
574 let h = size.y;
575
576 let hw = w / 2.0;
577 let hh = h / 2.0;
578
579 let x = x - hw;
580 let y = y - hh;
581
582 let c = corner_size;
583
584 let mut vertices = Vec::new();
585 let mut indices = Vec::new();
586
587 create_line_strip(
589 &[vec2(x, y + c), vec2(x, y), vec2(x + c, y)],
590 thickness,
591 &mut vertices,
592 &mut indices,
593 );
594
595 create_line_strip(
597 &[vec2(x + w - c, y + h), vec2(x + w, y + h), vec2(x + w, y + h - c)],
598 thickness,
599 &mut vertices,
600 &mut indices,
601 );
602
603 create_line_strip(
605 &[vec2(x + w - c, y), vec2(x + w, y), vec2(x + w, y + c)],
606 thickness,
607 &mut vertices,
608 &mut indices,
609 );
610
611 create_line_strip(
613 &[vec2(x + c, y + h), vec2(x, y + h), vec2(x, y + h - c)],
614 thickness,
615 &mut vertices,
616 &mut indices,
617 );
618
619 let z = z_index as f32 / Z_DIV;
620
621 let vertices = vertices
622 .into_iter()
623 .map(|v| SpriteVertex::new(v.extend(z), Vec2::ZERO, color))
624 .collect_vec();
625
626 draw_mesh(Mesh {
627 origin: center.extend(z_index as f32),
628 vertices: vertices.into(),
629 indices: indices.into(),
630 z_index,
631 texture: None,
632 y_sort_offset: 0.0,
633 });
634}
635
636pub fn create_line_strip(
637 points: &[Vec2],
638 thickness: f32,
639 vertices: &mut Vec<Vec2>,
640 indices: &mut Vec<u32>,
641) {
642 if points.len() < 2 {
643 panic!("Not enough points to create a line strip!");
644 }
645
646 let half_thickness = thickness / 4.0;
647 let idx_offset = vertices.len() as u32;
648
649 for i in 0..(points.len() - 1) {
650 let p0 = points[i];
651 let p1 = points[i + 1];
652
653 let direction = (p1 - p0).normalize_or_right();
654 let normal = vec2(-direction.y, direction.x);
655
656 vertices.push(p0 - normal * half_thickness);
657 vertices.push(p0 + normal * half_thickness);
658 vertices.push(p1 - normal * half_thickness);
659 vertices.push(p1 + normal * half_thickness);
660
661 let index_base = idx_offset + i as u32 * 4;
662
663 indices.push(index_base);
664 indices.push(index_base + 1);
665 indices.push(index_base + 2);
666
667 indices.push(index_base + 2);
668 indices.push(index_base + 1);
669 indices.push(index_base + 3);
670 }
671}
672
673pub fn rotated_rectangle(
674 position: Vec3,
675 params: RawDrawParams,
676 tex_width: f32,
677 tex_height: f32,
678 color: Color,
679 scroll_offset: Vec2,
680) -> [SpriteVertex; 4] {
681 let x = position.x;
682 let y = position.y;
683
684 let dims = params
685 .source_rect
686 .map(|rect| {
687 IRect {
688 size: rect.size,
689 offset: ivec2(
690 rect.offset.x,
691 tex_height as i32 - rect.offset.y - rect.size.y,
692 ),
693 }
694 })
695 .unwrap_or(IRect::new(
696 ivec2(0, 0),
697 ivec2(tex_width as i32, tex_height as i32),
698 ));
699
700 let sx = dims.offset.x as f32;
701 let sy = dims.offset.y as f32;
702 let sw = dims.size.x as f32;
703 let sh = dims.size.y as f32;
704
705 let (mut w, mut h) = match params.dest_size {
706 Some(dst) => (dst.x, dst.y),
707 _ => (1.0, 1.0),
708 };
709
710 if params.flip_x {
711 w = -w;
712 }
713 if params.flip_y {
714 h = -h;
715 }
716
717 let pivot = params.pivot.unwrap_or(vec2(x + w / 2.0, y + h / 2.0));
718 let m = pivot - vec2(w / 2.0, h / 2.0);
719
720 let r = params.rotation;
721
722 let p = [
723 vec2(x, y) - pivot,
724 vec2(x + w, y) - pivot,
725 vec2(x + w, y + h) - pivot,
726 vec2(x, y + h) - pivot,
727 ];
728
729 let p = [
730 vec2(
731 p[0].x * r.cos() - p[0].y * r.sin(),
732 p[0].x * r.sin() + p[0].y * r.cos(),
733 ) + m,
734 vec2(
735 p[1].x * r.cos() - p[1].y * r.sin(),
736 p[1].x * r.sin() + p[1].y * r.cos(),
737 ) + m,
738 vec2(
739 p[2].x * r.cos() - p[2].y * r.sin(),
740 p[2].x * r.sin() + p[2].y * r.cos(),
741 ) + m,
742 vec2(
743 p[3].x * r.cos() - p[3].y * r.sin(),
744 p[3].x * r.sin() + p[3].y * r.cos(),
745 ) + m,
746 ];
747
748 [
749 SpriteVertex::new(
750 vec3(p[0].x, p[0].y, position.z),
751 vec2(sx / tex_width, sy / tex_height) + scroll_offset,
752 color,
753 ),
754 SpriteVertex::new(
755 vec3(p[1].x, p[1].y, position.z),
756 vec2((sx + sw) / tex_width, sy / tex_height) + scroll_offset,
757 color,
758 ),
759 SpriteVertex::new(
760 vec3(p[2].x, p[2].y, position.z),
761 vec2((sx + sw) / tex_width, (sy + sh) / tex_height) + scroll_offset,
762 color,
763 ),
764 SpriteVertex::new(
765 vec3(p[3].x, p[3].y, position.z),
766 vec2(sx / tex_width, (sy + sh) / tex_height) + scroll_offset,
767 color,
768 ),
769 ]
770}
771
772pub fn draw_rect_outline_rot(
773 center: Vec2,
774 size: Vec2,
775 rotation: f32,
776 thickness: f32,
777 color: Color,
778 z_index: i32,
779) {
780 let _span = span!("draw_rect_outline_rot");
781
782 let (x, y) = center.tuple();
783 let t = thickness / 2.;
784 let w = size.x;
785 let h = size.y;
786
787 let hw = w / 2.0;
788 let hh = h / 2.0;
789
790 let x = x - hw;
791 let y = y - hh;
792
793 let pivot = vec2(x + w / 2.0, y + h / 2.0);
794
795 let z = z_index as f32 / Z_DIV;
796
797 #[rustfmt::skip]
798 let mut vertices = [
799 SpriteVertex::new(vec3(x , y , z), vec2(0.0, 1.0), color),
800 SpriteVertex::new(vec3(x + w, y , z), vec2(1.0, 0.0), color),
801 SpriteVertex::new(vec3(x + w, y + h, z), vec2(1.0, 1.0), color),
802 SpriteVertex::new(vec3(x , y + h, z), vec2(0.0, 0.0), color),
803 SpriteVertex::new(vec3(x + t , y + t , z), vec2(0.0, 0.0), color),
805 SpriteVertex::new(vec3(x + w - t, y + t , z), vec2(0.0, 0.0), color),
806 SpriteVertex::new(vec3(x + w - t, y + h - t, z), vec2(0.0, 0.0), color),
807 SpriteVertex::new(vec3(x + t , y + h - t, z), vec2(0.0, 0.0), color),
808 ];
809
810 for p in &mut vertices {
812 let px = p.position[0];
813 let py = p.position[1];
814 let pz = p.position[2];
815
816 let new_px = (px - pivot.x) * rotation.cos() -
817 (py - pivot.y) * rotation.sin() +
818 pivot.x;
819
820 let new_py = (px - pivot.x) * rotation.sin() +
821 (py - pivot.y) * rotation.cos() +
822 pivot.y;
823
824 p.position = [new_px, new_py, pz];
825 }
826
827 let indices: Vec<u32> = vec![
828 0, 1, 4, 1, 4, 5, 1, 5, 6, 1, 2, 6, 3, 7, 2, 2, 7, 6, 0, 4, 3, 3, 4, 7,
829 ];
830
831 draw_mesh(Mesh {
832 origin: center.extend(z_index as f32),
833 vertices: SmallVec::from_slice(&vertices),
834 indices: indices.into(),
835 z_index,
836 texture: None,
837 y_sort_offset: 0.0,
838 });
839}
840
841pub fn draw_circle(center: Vec2, r: f32, color: Color, z_index: i32) {
842 draw_poly_z(center, 40, r, 0.0, color, z_index, BlendMode::Alpha);
843}
844
845pub fn draw_ellipse(center: Vec2, radius: Vec2, color: Color, z_index: i32) {
846 draw_poly2_z(center, 40, radius, 0.0, color, z_index, BlendMode::Alpha);
847}
848
849pub fn draw_circle_outline(
850 center: Vec2,
851 radius: f32,
852 thickness: f32,
853 color: Color,
854 z_index: i32,
855) {
856 let inner_radius = radius - thickness / 2.0;
857 let outer_radius = radius + thickness / 2.0;
858
859 let mut vertices = vec![];
860 let mut indices = vec![];
861
862 let mut prev_inner_point: Option<Vec2> = None;
863 let mut prev_outer_point: Option<Vec2> = None;
864
865 let step_size = 0.1;
866 let steps = (2.0 * PI / step_size).round() as i32;
867
868 for i in 0..=steps {
869 let angle = i as f32 * step_size;
870 let cos = angle.cos();
871 let sin = angle.sin();
872
873 let inner_point = Vec2::new(
874 center.x + inner_radius * cos,
875 center.y + inner_radius * sin,
876 );
877 let outer_point = Vec2::new(
878 center.x + outer_radius * cos,
879 center.y + outer_radius * sin,
880 );
881
882 if let (Some(prev_inner), Some(prev_outer)) =
883 (prev_inner_point, prev_outer_point)
884 {
885 let z = z_index as f32 / Z_DIV;
887
888 vertices.push(SpriteVertex::new(
889 vec3(prev_inner.x, prev_inner.y, z),
890 vec2(0.0, 0.0),
891 color,
892 ));
893 vertices.push(SpriteVertex::new(
894 vec3(inner_point.x, inner_point.y, z),
895 vec2(1.0, 0.0),
896 color,
897 ));
898 vertices.push(SpriteVertex::new(
899 vec3(prev_outer.x, prev_outer.y, z),
900 vec2(0.0, 1.0),
901 color,
902 ));
903 vertices.push(SpriteVertex::new(
904 vec3(outer_point.x, outer_point.y, z),
905 vec2(1.0, 1.0),
906 color,
907 ));
908
909 let start_index = vertices.len() as u32 - 4;
910
911 indices.extend_from_slice(&[
912 start_index,
913 start_index + 1,
914 start_index + 2,
915 start_index + 1,
916 start_index + 2,
917 start_index + 3,
918 ]);
919 }
920
921 prev_inner_point = Some(inner_point);
922 prev_outer_point = Some(outer_point);
923 }
924
925 draw_mesh(Mesh {
926 origin: center.extend(z_index as f32),
927 vertices: vertices.into(),
928 indices: indices.into(),
929 z_index,
930 texture: None,
931 y_sort_offset: 0.0,
932 })
933}
934
935pub fn draw_circle_z(
936 center: Vec2,
937 r: f32,
938 color: Color,
939 z_index: i32,
940 blend_mode: BlendMode,
941) {
942 draw_poly_z(center, 40, r, 0.0, color, z_index, blend_mode);
943}
944
945pub fn draw_line(
946 p1: Vec2,
947 p2: Vec2,
948 thickness: f32,
949 color: Color,
950 z_index: i32,
951) {
952 draw_line_tex(p1, p2, thickness, z_index, color, None);
953}
954
955pub fn draw_ray(
956 pos: Vec2,
957 dir: Vec2,
958 thickness: f32,
959 color: Color,
960 z_index: i32,
961) {
962 draw_line(pos, pos + dir, thickness, color, z_index);
963}
964
965pub fn draw_line_tex_y_uv_flex(
966 p1: Position,
967 p2: Position,
968 start_thickness: f32,
969 end_thickness: f32,
970 color: Color,
971 texture: Option<TextureHandle>,
972 uv_offset: f32,
973 uv_size: f32,
974 z_index: i32,
975 blend_mode: BlendMode,
976) {
977 let (x1, y1) = p1.to_world().tuple();
978 let (x2, y2) = p2.to_world().tuple();
979
980 let dx = x2 - x1;
981 let dy = y2 - y1;
982
983 let nx = -dy;
984 let ny = dx;
985
986 let tlen = (nx * nx + ny * ny).sqrt();
987 if tlen < std::f32::EPSILON {
988 return;
989 }
990
991 let nxn = nx / tlen;
992 let nyn = ny / tlen;
993
994 let tx1 = nxn * start_thickness * 0.5;
995 let ty1 = nyn * start_thickness * 0.5;
996
997 let tx2 = nxn * end_thickness * 0.5;
998 let ty2 = nyn * end_thickness * 0.5;
999
1000 let z = z_index as f32 / Z_DIV;
1001
1002 let start = uv_offset % 1.0;
1007 let end = start + uv_size;
1008
1009 let top_left = vec3(x1 + tx1, y1 + ty1, z);
1048 let bottom_left = vec3(x1 - tx1, y1 - ty1, z);
1049 let top_right = vec3(x2 + tx2, y2 + ty2, z);
1050 let bottom_right = vec3(x2 - tx2, y2 - ty2, z);
1051
1052 let vertices = [
1053 SpriteVertex::new(top_left, vec2(0.0, start), color),
1054 SpriteVertex::new(bottom_left, vec2(1.0, start), color),
1055 SpriteVertex::new(top_right, vec2(0.0, end), color),
1056 SpriteVertex::new(bottom_right, vec2(1.0, end), color),
1057 ];
1058
1059 let indices = [0, 1, 2, 2, 1, 3];
1060
1061 draw_mesh_ex(
1064 Mesh {
1065 origin: vec3((x1 + x2) / 2.0, (y1 + y2) / 2.0, z_index as f32),
1066 vertices: SmallVec::from_slice(&vertices),
1067 indices: indices.into(),
1068 z_index,
1069 texture,
1070 y_sort_offset: 0.0,
1071 },
1072 blend_mode,
1073 )
1074}
1075
1076pub fn draw_line_tex(
1077 p1: Vec2,
1078 p2: Vec2,
1079 thickness: f32,
1080 z_index: i32,
1081 color: Color,
1082 texture: Option<TextureHandle>,
1083) {
1084 let (x1, y1) = p1.tuple();
1085 let (x2, y2) = p2.tuple();
1086
1087 let dx = x2 - x1;
1088 let dy = y2 - y1;
1089
1090 let nx = -dy;
1093 let ny = dx;
1094
1095 let tlen = (nx * nx + ny * ny).sqrt() / (thickness * 0.5);
1096 if tlen < std::f32::EPSILON {
1097 return;
1098 }
1099 let tx = nx / tlen;
1100 let ty = ny / tlen;
1101
1102 let z = z_index as f32 / Z_DIV;
1107
1108 let vertices = [
1109 SpriteVertex::new(vec3(x1 + tx, y1 + ty, z), vec2(0.0, 0.0), color),
1110 SpriteVertex::new(vec3(x1 - tx, y1 - ty, z), vec2(1.0, 0.0), color),
1111 SpriteVertex::new(vec3(x2 + tx, y2 + ty, z), vec2(0.0, 1.0), color),
1112 SpriteVertex::new(vec3(x2 - tx, y2 - ty, z), vec2(1.0, 1.0), color),
1113 ];
1114
1115 let indices = [0, 1, 2, 2, 1, 3];
1123
1124 draw_mesh(Mesh {
1125 origin: vec3((x1 + x2) / 2.0, (y1 + y2) / 2.0, z_index as f32),
1126 vertices: SmallVec::from_slice(&vertices),
1127 indices: indices.into(),
1128 z_index,
1129 texture,
1130 y_sort_offset: 0.0,
1131 })
1132}
1133
1134pub fn draw_poly_z(
1135 position: Vec2,
1136 sides: u8,
1137 radius: f32,
1138 rotation: f32,
1139 color: Color,
1140 z_index: i32,
1141 blend_mode: BlendMode,
1142) {
1143 draw_poly2_z(
1144 position,
1145 sides,
1146 Vec2::splat(radius),
1147 rotation,
1148 color,
1149 z_index,
1150 blend_mode,
1151 );
1152}
1153
1154pub fn draw_poly2_z(
1155 position: Vec2,
1156 sides: u8,
1157 radius: Vec2,
1158 rotation: f32,
1159 color: Color,
1160 z_index: i32,
1161 blend_mode: BlendMode,
1162) {
1163 let (x, y) = position.tuple();
1164 let z = z_index as f32 / Z_DIV;
1165
1166 let mut vertices = Vec::<SpriteVertex>::with_capacity(sides as usize + 2);
1167 let mut indices = Vec::<u32>::with_capacity(sides as usize * 3);
1168
1169 let rot = rotation.to_radians();
1170 vertices.push(SpriteVertex::new(vec3(x, y, z), vec2(0.0, 0.0), color));
1171
1172 for i in 0..sides + 1 {
1173 let rx =
1174 (i as f32 / sides as f32 * std::f32::consts::PI * 2. + rot).cos();
1175 let ry =
1176 (i as f32 / sides as f32 * std::f32::consts::PI * 2. + rot).sin();
1177
1178 let vertex = SpriteVertex::new(
1179 vec3(x + radius.x * rx, y + radius.y * ry, z),
1180 vec2(rx, ry),
1181 color,
1182 );
1183
1184 vertices.push(vertex);
1185
1186 if i != sides {
1187 indices.extend_from_slice(&[0, i as u32 + 1, i as u32 + 2]);
1188 }
1189 }
1190
1191 draw_mesh_ex(
1192 Mesh {
1193 origin: position.extend(z_index as f32),
1194 vertices: vertices.into(),
1195 indices: indices.into(),
1196 z_index,
1197 ..Default::default()
1198 },
1199 blend_mode,
1200 );
1201}
1202
1203pub fn draw_arc(
1204 position: Vec2,
1205 radius: f32,
1206 start_angle: f32,
1207 end_angle: f32,
1208 color: Color,
1209 z_index: i32,
1210) {
1211 let (x, y) = position.tuple();
1212 let z = z_index as f32 / Z_DIV;
1213 let segments = 40;
1214
1215 let mut vertices =
1216 Vec::<SpriteVertex>::with_capacity(segments as usize + 2);
1217 let mut indices = Vec::<u32>::with_capacity(segments as usize * 3);
1218
1219 vertices.push(SpriteVertex::new(vec3(x, y, z), vec2(0.0, 0.0), color));
1220
1221 for i in 0..segments + 1 {
1226 let angle = start_angle +
1227 (i as f32 / segments as f32 * (end_angle - start_angle));
1228
1229 let rx = angle.cos();
1230 let ry = angle.sin();
1231
1232 let vertex = SpriteVertex::new(
1233 vec3(x + radius * rx, y + radius * ry, z),
1234 vec2(rx, ry),
1235 color,
1236 );
1237
1238 vertices.push(vertex);
1239
1240 if i != segments {
1241 indices.extend_from_slice(&[0, i as u32 + 1, i as u32 + 2]);
1242 }
1243 }
1244
1245 draw_mesh(Mesh {
1246 vertices: vertices.into(),
1247 indices: indices.into(),
1248 z_index,
1249 ..Default::default()
1250 });
1251}
1252
1253pub fn draw_arc_outline(
1254 center: Vec2,
1255 radius: f32,
1256 thickness: f32,
1257 start_angle: f32,
1258 end_angle: f32,
1259 color: Color,
1260 z_index: i32,
1261) {
1262 let inner_radius = radius - thickness / 2.0;
1263 let outer_radius = radius + thickness / 2.0;
1264
1265 let two_pi = 2.0 * PI;
1266 let start_angle = start_angle % two_pi;
1267 let mut end_angle = end_angle % two_pi;
1268
1269 if end_angle < start_angle {
1270 end_angle += two_pi;
1271 }
1272
1273 let mut vertices = vec![];
1274 let mut indices = vec![];
1275
1276 let mut prev_inner_point: Option<Vec2> = None;
1277 let mut prev_outer_point: Option<Vec2> = None;
1278
1279 let step_size = 0.1;
1280 let steps = ((end_angle - start_angle) / step_size).round() as i32;
1281
1282 for i in 0..=steps {
1283 let angle = start_angle + i as f32 * step_size;
1284 let cos = angle.cos();
1285 let sin = angle.sin();
1286
1287 let inner_point = Vec2::new(
1288 center.x + inner_radius * cos,
1289 center.y + inner_radius * sin,
1290 );
1291 let outer_point = Vec2::new(
1292 center.x + outer_radius * cos,
1293 center.y + outer_radius * sin,
1294 );
1295
1296 if let (Some(prev_inner), Some(prev_outer)) =
1297 (prev_inner_point, prev_outer_point)
1298 {
1299 let z = z_index as f32 / Z_DIV;
1300
1301 vertices.push(SpriteVertex::new(
1302 vec3(prev_inner.x, prev_inner.y, z),
1303 vec2(0.0, 0.0),
1304 color,
1305 ));
1306 vertices.push(SpriteVertex::new(
1307 vec3(inner_point.x, inner_point.y, z),
1308 vec2(1.0, 0.0),
1309 color,
1310 ));
1311 vertices.push(SpriteVertex::new(
1312 vec3(prev_outer.x, prev_outer.y, z),
1313 vec2(0.0, 1.0),
1314 color,
1315 ));
1316 vertices.push(SpriteVertex::new(
1317 vec3(outer_point.x, outer_point.y, z),
1318 vec2(1.0, 1.0),
1319 color,
1320 ));
1321
1322 let start_index = vertices.len() as u32 - 4;
1323
1324 indices.extend_from_slice(&[
1325 start_index,
1326 start_index + 1,
1327 start_index + 2,
1328 start_index + 1,
1329 start_index + 2,
1330 start_index + 3,
1331 ]);
1332 }
1333
1334 prev_inner_point = Some(inner_point);
1335 prev_outer_point = Some(outer_point);
1336 }
1337
1338 draw_mesh(Mesh {
1339 origin: center.extend(z_index as f32),
1340 vertices: vertices.into(),
1341 indices: indices.into(),
1342 z_index,
1343 texture: None,
1344 y_sort_offset: 0.0,
1345 })
1346}
1347
1348pub fn draw_arc_wedge(
1349 center: Vec2,
1350 radius: f32,
1351 thickness: f32,
1352 start_angle: f32,
1353 end_angle: f32,
1354 color: Color,
1355 z_index: i32,
1356) {
1357 draw_arc_outline(
1358 center,
1359 radius,
1360 thickness,
1361 start_angle,
1362 end_angle,
1363 color,
1364 z_index,
1365 );
1366
1367 let start_point = vec2(start_angle.cos(), start_angle.sin()) * radius;
1368 let end_point = vec2(end_angle.cos(), end_angle.sin()) * radius;
1369
1370 draw_line(center, center + start_point, thickness, color, z_index);
1371 draw_line(center, center + end_point, thickness, color, z_index);
1372}
1373
1374pub fn draw_wedge(
1375 center: Vec2,
1376 radius: f32,
1377 thickness: f32,
1378 start_angle: f32,
1379 end_angle: f32,
1380 color: Color,
1381 z_index: i32,
1382) {
1383 let start_point = vec2(start_angle.cos(), start_angle.sin()) * radius;
1384 let end_point = vec2(end_angle.cos(), end_angle.sin()) * radius;
1385
1386 draw_line(center, center + start_point, thickness, color, z_index);
1387 draw_line(center, center + end_point, thickness, color, z_index);
1388
1389 draw_line(
1390 center + start_point,
1391 center + end_point,
1392 thickness,
1393 color,
1394 z_index,
1395 );
1396}
1397
1398pub fn draw_arrow(
1399 start: Vec2,
1400 end: Vec2,
1401 thickness: f32,
1402 color: Color,
1403 z_index: i32,
1404) {
1405 let len = 0.8;
1406 let spread = 0.15 * PI;
1407
1408 draw_arrow_pro(start, end, thickness, color, z_index, len, spread);
1409}
1410
1411pub fn draw_arrow_pro(
1412 start: Vec2,
1413 end: Vec2,
1414 thickness: f32,
1415 color: Color,
1416 z_index: i32,
1417 len: f32,
1418 spread: f32,
1419) {
1420 let dir = end - start;
1421 let angle = dir.angle();
1422
1423 draw_ray(
1425 end,
1426 -Vec2::from_angle(angle + spread) * len,
1427 thickness,
1428 color,
1429 z_index,
1430 );
1431 draw_ray(
1432 end,
1433 -Vec2::from_angle(angle - spread) * len,
1434 thickness,
1435 color,
1436 z_index,
1437 );
1438
1439 draw_ray(start, dir, thickness, color, z_index);
1440}
1441
1442pub fn draw_revs(position: Vec2, r: f32, rev: f32, color: Color, z_index: i32) {
1443 let rev_end_angle = PI / 4.0;
1444
1445 let px = px();
1446 let offset = 3.0 * px;
1447
1448 draw_arc_outline(
1449 position,
1450 r + offset,
1451 LINE_W * px,
1452 PI + rev_end_angle - rev,
1453 PI + rev_end_angle,
1454 color,
1455 z_index,
1456 );
1457
1458 draw_arc_outline(
1459 position,
1460 r + offset,
1461 LINE_W * px,
1462 -rev_end_angle,
1463 -rev_end_angle + rev,
1464 color,
1465 z_index,
1466 );
1467}
1468
1469pub fn draw_mesh(mesh: Mesh) {
1470 draw_mesh_ex(mesh, BlendMode::default());
1471}
1472
1473pub fn draw_mesh_ex(mesh: Mesh, blend_mode: BlendMode) {
1474 queue_mesh_draw(mesh, blend_mode);
1475}
1476
1477#[derive(Copy, Clone, Debug)]
1478pub struct DrawTextureParams {
1479 pub dest_size: Option<Size>,
1480 pub source_rect: Option<IRect>,
1481 pub scroll_offset: Vec2,
1482 pub rotation: f32,
1483 pub flip_x: bool,
1484 pub flip_y: bool,
1485 pub pivot: Option<Vec2>,
1486 pub blend_mode: BlendMode,
1487 pub y_sort_offset: f32,
1488}
1489
1490impl Default for DrawTextureParams {
1491 fn default() -> DrawTextureParams {
1492 DrawTextureParams {
1493 dest_size: None,
1494 source_rect: None,
1495 scroll_offset: Vec2::ZERO,
1496 rotation: 0.,
1497 pivot: None,
1498 flip_x: false,
1499 flip_y: false,
1500 blend_mode: BlendMode::None,
1501 y_sort_offset: 0.0,
1502 }
1503 }
1504}
1505
1506impl DrawTextureParams {
1507 pub fn blend(blend_mode: BlendMode) -> DrawTextureParams {
1508 DrawTextureParams { blend_mode, ..Default::default() }
1509 }
1510}
1511
1512pub fn draw_line_tex_y_uv(
1513 p1: Position,
1514 p2: Position,
1515 thickness: f32,
1516 color: Color,
1517 texture: Option<TextureHandle>,
1518 y_uv: Range<f32>,
1519 z_index: i32,
1520 blend_mode: BlendMode,
1521) {
1522 let (x1, y1) = p1.to_world().tuple();
1523 let (x2, y2) = p2.to_world().tuple();
1524
1525 let dx = x2 - x1;
1526 let dy = y2 - y1;
1527
1528 let nx = -dy;
1531 let ny = dx;
1532
1533 let tlen = (nx * nx + ny * ny).sqrt() / (thickness * 0.5);
1534 if tlen < std::f32::EPSILON {
1535 return;
1536 }
1537 let tx = nx / tlen;
1538 let ty = ny / tlen;
1539
1540 let z = z_index as f32 / Z_DIV;
1541
1542 let y_uv_start = y_uv.start % 1.0;
1546 let y_uv_end = y_uv.end % 1.0;
1547
1548 let vertices = [
1549 SpriteVertex::new(
1550 vec3(x1 + tx, y1 + ty, z),
1551 vec2(0.0, y_uv_start),
1552 color,
1553 ),
1554 SpriteVertex::new(
1555 vec3(x1 - tx, y1 - ty, z),
1556 vec2(1.0, y_uv_start),
1557 color,
1558 ),
1559 SpriteVertex::new(
1560 vec3(x2 + tx, y2 + ty, z),
1561 vec2(0.0, y_uv_end),
1562 color,
1563 ),
1564 SpriteVertex::new(
1565 vec3(x2 - tx, y2 - ty, z),
1566 vec2(1.0, y_uv_end),
1567 color,
1568 ),
1569 ];
1570
1571 let indices = [0, 1, 2, 2, 1, 3];
1579
1580 draw_mesh_ex(
1581 Mesh {
1582 origin: vec3((x1 + x2) / 2.0, (y1 + y2) / 2.0, z_index as f32),
1583 vertices: SmallVec::from_slice(&vertices),
1584 indices: indices.into(),
1585 z_index: 0,
1586 texture,
1587 y_sort_offset: 0.0,
1588 },
1589 blend_mode,
1590 )
1591}