comfy_core/
quad.rs

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
8/// When set to `true` (default) sprites that are outside of the camera's viewport will
9/// not be drawn. This only affects sprites that are drawn using `draw_sprite_pro`, or using
10/// the `Sprite/AnimatedSprite` components.
11///
12/// Setting this to `false` disables this completely.
13///
14/// This setting has no effect
15pub fn set_sprite_culling(enabled: bool) {
16    SPRITE_CULLING_ENABLED.store(enabled, Ordering::SeqCst);
17}
18
19/// Returns the current setting of sprite culling.
20pub 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
103/// Draws a sprite on the screen.
104///
105/// * `texture` - A handle to the texture to draw.
106/// * `position` - World position where to draw.
107/// * `tint` - The color tint to apply to the sprite.
108/// * `z_index` - The z-index of the sprite. Higher values are drawn on top of lower values.
109/// * `world_size` - The size of the world. Used for scaling.
110pub 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    // if !CAMERA_BOUNDS
139    //     .load()
140    //     .contains_rect(position, raw.dest_size.unwrap_or(Vec2::ONE))
141    // {
142    //     return;
143    // }
144
145    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    /// The source rectangle in the texture to draw, uses pixel coordinates. If
193    /// `None`, the entire texture is drawn.
194    pub source_rect: Option<IRect>,
195    /// The alignment of the sprite. The sprite's origin (its position) will be
196    /// aligned according to this value. E.g. if `align` is `BottomRight`, at
197    /// the draw position the bottom right corner of the sprite will be drawn.
198    pub align: SpriteAlign,
199    /// Defined as an offset from the sprite position. The point around which
200    /// the sprite rotates. None means the pivot is the sprite's position.
201    pub pivot: Option<Vec2>,
202    /// The desired size of the sprite in world units.
203    pub size: Vec2,
204    /// The rotation to apply to the sprite, in radians.
205    pub rotation: f32,
206    /// Whether to flip the sprite horizontally.
207    pub flip_x: bool,
208    /// Whether to flip the sprite vertically.
209    pub flip_y: bool,
210    /// The blend mode to use when drawing the sprite.
211    pub blend_mode: BlendMode,
212    /// Rotation around the x axis for creating a 3d effect.
213    pub rotation_x: f32,
214    /// Offset for y-sorting in world-space. This is useful for when sprites have different
215    /// sizes and pivots, for example when a tree should be "behind" something based on its bottom-most
216    /// pixel, and not based on its size.
217    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    // Compute origin based on alignment
265    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            // m.transform_point3(),
350            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 t = thickness / 2.;
463    // #[rustfmt::skip]
464    // let vertices = vec![
465    //     SpriteVertex::new(vec3(x    , y    , z), vec2(0.0, 1.0), color),
466    //     SpriteVertex::new(vec3(x + w, y    , z), vec2(1.0, 0.0), color),
467    //     SpriteVertex::new(vec3(x + w, y + h, z), vec2(1.0, 1.0), color),
468    //     SpriteVertex::new(vec3(x    , y + h, z), vec2(0.0, 0.0), color),
469    //     //inner rectangle
470    //     SpriteVertex::new(vec3(x + t    , y + t    , z), vec2(0.0, 0.0), color),
471    //     SpriteVertex::new(vec3(x + w - t, y + t    , z), vec2(0.0, 0.0), color),
472    //     SpriteVertex::new(vec3(x + w - t, y + h - t, z), vec2(0.0, 0.0), color),
473    //     SpriteVertex::new(vec3(x + t    , y + h - t, z), vec2(0.0, 0.0), color),
474    // ];
475    //
476    // let indices: Vec<u32> = vec![
477    //     0, 1, 4, 1, 4, 5, 1, 5, 6, 1, 2, 6, 3, 7, 2, 2, 7, 6, 0, 4, 3, 3, 4, 7,
478    // ];
479
480    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    // bottom left
588    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    // top right
596    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    // bottom right
604    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    // top left
612    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        //inner rectangle
804        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    // Apply rotation to points
811    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            // Create two triangles
886            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 wrapped_y_uv_start = uv_offset % 1.0;
1003    // let wrapped_y_uv_end = (uv_offset + uv_size) % 1.0;
1004    //
1005
1006    let start = uv_offset % 1.0;
1007    let end = start + uv_size;
1008
1009    // let start = wrapped_y_uv_start;
1010    // let end = wrapped_y_uv_end;
1011
1012    // let y_uv_start = y_uv.start % 1.0;
1013    // let y_uv_end = y_uv.end % 1.0;
1014
1015    // const EPSILON: f32 = 1e-6;
1016    //
1017    // let y_uv_start = y_uv.start % 1.0;
1018    // let mut y_uv_end = y_uv.end % 1.0;
1019    //
1020    // if y_uv_end.abs() < EPSILON {
1021    //     y_uv_end = 1.0;
1022    // }
1023
1024    // let vertices = vec![
1025    //     SpriteVertex::new(
1026    //         vec3(x1 + tx1, y1 + ty1, z),
1027    //         vec2(0.0, y_uv_start),
1028    //         color,
1029    //     ),
1030    //     SpriteVertex::new(
1031    //         vec3(x1 - tx1, y1 - ty1, z),
1032    //         vec2(0.0, y_uv_end),
1033    //         color,
1034    //     ),
1035    //     SpriteVertex::new(
1036    //         vec3(x2 + tx2, y2 + ty2, z),
1037    //         vec2(1.0, y_uv_start),
1038    //         color,
1039    //     ),
1040    //     SpriteVertex::new(
1041    //         vec3(x2 - tx2, y2 - ty2, z),
1042    //         vec2(1.0, y_uv_end),
1043    //         color,
1044    //     ),
1045    // ];
1046
1047    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    // println!("y_uv_start: {}, y_uv_end: {}", y_uv_start, y_uv_end);
1062
1063    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    // https://stackoverflow.com/questions/1243614/how-do-i-calculate-the-normal-vector-of-a-line-segment
1091
1092    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    // 0 0      1 0
1103    //
1104    // 0 1      1 1
1105
1106    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 vertices = vec![
1116    //     SpriteVertex::new(vec2(x1 + tx, y1 + ty), vec2(0.0, 0.0), color),
1117    //     SpriteVertex::new(vec2(x1 - tx, y1 - ty), vec2(1.0, 0.0), color),
1118    //     SpriteVertex::new(vec2(x2 + tx, y2 + ty), vec2(1.0, 1.0), color),
1119    //     SpriteVertex::new(vec2(x2 - tx, y2 - ty), vec2(0.0, 1.0), color),
1120    // ];
1121
1122    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    // if end_angle < 0.0 {
1222    //     end_angle = 2.0 * PI + end_angle;
1223    // }
1224
1225    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 the arrow head
1424    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    // https://stackoverflow.com/questions/1243614/how-do-i-calculate-the-normal-vector-of-a-line-segment
1529
1530    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    // 0 0      1 0
1543    //
1544    // 0 1      1 1
1545    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 vertices = vec![
1572    //     SpriteVertex::new(vec2(x1 + tx, y1 + ty), vec2(0.0, 0.0), color),
1573    //     SpriteVertex::new(vec2(x1 - tx, y1 - ty), vec2(1.0, 0.0), color),
1574    //     SpriteVertex::new(vec2(x2 + tx, y2 + ty), vec2(1.0, 1.0), color),
1575    //     SpriteVertex::new(vec2(x2 - tx, y2 - ty), vec2(0.0, 1.0), color),
1576    // ];
1577
1578    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}