Skip to main content

yog_gfx/
draw2d.rs

1//! 2D drawing helpers — thin wrappers over the `draw2d_*` ABI functions.
2//!
3//! These use Minecraft's own rendering infrastructure (text renderer, texture
4//! manager) and are therefore only valid during `on_hud_render`.
5
6use yog_abi::YogStr;
7use yog_graphics::{Color, Rect, Vec2};
8
9use crate::GfxContext;
10
11/// 2D drawing helpers, valid only within `on_hud_render`.
12///
13/// Obtain via [`GfxContext::draw2d`].
14pub struct Draw2D<'ctx>(&'ctx GfxContext);
15
16impl<'ctx> Draw2D<'ctx> {
17    pub(crate) fn new(ctx: &'ctx GfxContext) -> Self {
18        Self(ctx)
19    }
20
21    /// Draws `text` at GUI position `at`, a drop shadow under it if `shadow`.
22    pub fn text(&self, text: &str, at: Vec2, color: Color, shadow: bool) {
23        let a = self.0.api();
24        unsafe { (a.draw2d_text)(YogStr::from_str(text), at.x, at.y, color.0, shadow) }
25    }
26
27    /// [`Self::text`] in the font `font` (empty: the default one).
28    pub fn text_font(&self, text: &str, font: &str, at: Vec2, color: Color, shadow: bool) {
29        let a = self.0.api();
30        unsafe { (a.draw2d_text_font)(YogStr::from_str(text), YogStr::from_str(font), at.x, at.y, color.0, shadow) }
31    }
32
33    /// Width in GUI pixels of `text` in `font` (empty = default), measured by
34    /// the host's text renderer.
35    pub fn text_width(&self, text: &str, font: &str) -> f32 {
36        let a = self.0.api();
37        unsafe { (a.text_width)(YogStr::from_str(text), YogStr::from_str(font)) }
38    }
39
40    /// Fills `r` with a flat colour.
41    pub fn rect(&self, r: Rect, color: Color) {
42        let a = self.0.api();
43        unsafe { (a.draw2d_rect)(r.x, r.y, r.right(), r.bottom(), color.0) }
44    }
45
46    /// Fills `r` with a vertical gradient, `top` to `bottom`.
47    pub fn gradient(&self, r: Rect, top: Color, bottom: Color) {
48        let a = self.0.api();
49        unsafe { (a.draw2d_gradient)(r.x, r.y, r.right(), r.bottom(), top.0, bottom.0) }
50    }
51
52    /// Renders an item stack (3D block models included) via Minecraft's item
53    /// renderer — like inventory slots or Patchouli's item icons: item `id`
54    /// (`minecraft:crafting_table`) at `at`, `size` GUI pixels big (16 = an
55    /// inventory icon).
56    pub fn item(&self, id: &str, at: Vec2, size: f32) {
57        let a = self.0.api();
58        unsafe { (a.draw2d_item)(YogStr::from_str(id), at.x, at.y, size) }
59    }
60
61    /// Draws the image `source` — a Yog image source such as `"item:apple"`,
62    /// `"block:stone"`, `"sprite:button"` or `"mymod:gui/logo"` (see
63    /// [`crate::image`]) — scaled into `r`. The host resolves it for its
64    /// Minecraft version.
65    pub fn image(&self, source: &str, r: Rect) {
66        self.image_region(source, r, None, Color::WHITE);
67    }
68
69    /// [`Self::image`] drawing only `region` of it (in the image's own
70    /// pixels; `None` = all of it) multiplied by `tint` (white = none).
71    /// Items and sprites are always drawn whole, and items ignore the tint.
72    pub fn image_region(&self, source: &str, r: Rect, region: Option<Rect>, tint: Color) {
73        let a = self.0.api();
74        let src = region.unwrap_or_default();
75        unsafe { (a.draw2d_image)(YogStr::from_str(source), r.x, r.y, r.w, r.h, src.x, src.y, src.w, src.h, tint.0) }
76    }
77
78    /// Draws the SVG document `data` scaled into `r` (sharp at any GUI
79    /// scale: the host rasterizes it at the screen's resolution). An `.svg`
80    /// file is an image source like any other — `mymod:gui/logo.svg` — for
81    /// [`Self::image`].
82    pub fn svg(&self, data: &str, r: Rect) {
83        let a = self.0.api();
84        unsafe { (a.draw2d_svg)(YogStr::from_str(data), r.x, r.y, r.w, r.h) }
85    }
86
87    /// [`Self::text_font`] `scale` times its size (1 = the font's own).
88    pub fn text_scaled(&self, text: &str, font: &str, at: Vec2, scale: f32, color: Color, shadow: bool) {
89        let a = self.0.api();
90        unsafe { (a.draw2d_text_scaled)(YogStr::from_str(text), YogStr::from_str(font), at.x, at.y, scale, color.0, shadow) }
91    }
92
93    /// Draws blocks in 3D, as big as fits in `r` — a multiblock structure,
94    /// a block on display: each is `((x, y, z), state)` with a full block
95    /// state (`minecraft:furnace[facing=north]`), turned `yaw` degrees about
96    /// the vertical and tilted `pitch` degrees toward the viewer (30 looks
97    /// down on it like Patchouli does).
98    pub fn blocks(&self, blocks: &[((i32, i32, i32), &str)], r: Rect, yaw: f32, pitch: f32) {
99        let mut list = String::new();
100        for ((bx, by, bz), state) in blocks {
101            use std::fmt::Write as _;
102            let _ = writeln!(list, "{bx} {by} {bz} {state}");
103        }
104        let a = self.0.api();
105        unsafe { (a.draw2d_blocks)(YogStr::from_str(&list), r.x, r.y, r.w, r.h, yaw, pitch) }
106    }
107
108    /// Draws a living entity of type `entity` (`minecraft:zombie`,
109    /// `mymod:wisp`) standing in `r`, as big as fits, turned `yaw` degrees
110    /// (0 = facing the viewer) and tilted `pitch` degrees.
111    pub fn entity(&self, entity: &str, r: Rect, yaw: f32, pitch: f32) {
112        let a = self.0.api();
113        unsafe { (a.draw2d_entity)(YogStr::from_str(entity), r.x, r.y, r.w, r.h, yaw, pitch) }
114    }
115
116    /// Natural size in GUI pixels of the image `source` (`None` if the host
117    /// can't resolve it).
118    pub fn image_size(&self, source: &str) -> Option<Vec2> {
119        image_size_with(self.0.image_size_fn(), source)
120    }
121}
122
123/// Calls an [`ImageSizeFn`](crate::ImageSizeFn).
124pub fn image_size_with(f: crate::ImageSizeFn, source: &str) -> Option<Vec2> {
125    let (mut w, mut h) = (0.0f32, 0.0f32);
126    unsafe { f(YogStr::from_str(source), &mut w, &mut h) }.then_some(Vec2::new(w, h))
127}