rlbot/render.rs
1//! Rendering is RLBot's ability to draw directly inside the game window.
2
3use crate::{flat, rlbot::RLBot};
4use flatbuffers::{FlatBufferBuilder, WIPOffset};
5use std::error::Error;
6
7/// A render group in the process of being built.
8///
9/// The drawing methods in this class simply collect a list of items to be
10/// drawn later. Nothing will actually be drawn to screen until
11/// [`render`](RenderGroup::render) is called.
12///
13/// # Examples
14///
15/// ## Basic rendering
16///
17/// ```no_run
18/// # use rlbot::flat::ColorArgs;
19/// # use std::error::Error;
20/// #
21/// # fn main() -> Result<(), Box<Error>> {
22/// let rlbot = rlbot::init()?;
23/// let mut group = rlbot.begin_render_group(1234);
24/// let green = group.color_rgb(0, 255, 0);
25/// group.draw_string_2d((10.0, 10.0), (2, 2), "I am text!", green);
26/// group.render()?;
27/// # Ok(())
28/// # }
29/// ```
30///
31/// ## Clearing the screen
32///
33/// ```no_run
34/// # use std::error::Error;
35/// #
36/// # fn main() -> Result<(), Box<Error>> {
37/// let rlbot = rlbot::init()?;
38/// let group = rlbot.begin_render_group(1234);
39/// group.render()?;
40/// # Ok(())
41/// # }
42/// ```
43pub struct RenderGroup<'a> {
44 rlbot: &'a RLBot,
45 id: i32,
46 builder: FlatBufferBuilder<'a>,
47 messages: Vec<WIPOffset<flat::RenderMessage<'a>>>,
48}
49
50impl<'a> RenderGroup<'a> {
51 pub(crate) fn new(rlbot: &'a RLBot, id: i32) -> Self {
52 Self {
53 rlbot,
54 id,
55 builder: FlatBufferBuilder::new_with_capacity(1024),
56 messages: Vec::new(),
57 }
58 }
59}
60
61/// A color that can be used to draw in a [`RenderGroup`].
62#[derive(Copy, Clone)]
63pub struct Color<'a>(WIPOffset<flat::Color<'a>>);
64
65impl<'a> RenderGroup<'a> {
66 /// Send the collected drawings to RLBot to be rendered to screen.
67 pub fn render(mut self) -> Result<(), Box<dyn Error>> {
68 let messages = self.builder.create_vector(&self.messages);
69
70 let render_group = {
71 let mut rg = flat::RenderGroupBuilder::new(&mut self.builder);
72 rg.add_renderMessages(messages);
73 rg.add_id(self.id);
74 rg.finish()
75 };
76
77 self.builder.finish(render_group, None);
78 let data = self.builder.finished_data();
79 self.rlbot.interface().render_group(data)?;
80 Ok(())
81 }
82
83 /// Create a color with the given **a**lpha, **r**ed, **g**reen, and
84 /// **b**lue. An alpha of 255 is fully opaque, and 0 is fully transparent.
85 ///
86 /// A color can only be used with the `RenderGroup` that created it.
87 ///
88 /// # Example
89 ///
90 /// ```no_run
91 /// # use rlbot::RenderGroup;
92 /// # let mut group: RenderGroup = unsafe { ::std::mem::uninitialized() };
93 /// let transbluecent = group.color_argb(127, 0, 0, 255);
94 /// ```
95 pub fn color_argb(&mut self, a: u8, r: u8, g: u8, b: u8) -> Color<'a> {
96 let args = flat::ColorArgs { a, r, g, b };
97 Color(flat::Color::create(&mut self.builder, &args))
98 }
99
100 /// Create an opaque color with the given, **r**ed, **g**reen, and **b**lue.
101 ///
102 /// A color can only be used with the `RenderGroup` that created it.
103 ///
104 /// # Example
105 ///
106 /// ```no_run
107 /// # use rlbot::RenderGroup;
108 /// # let mut group: RenderGroup = unsafe { ::std::mem::uninitialized() };
109 /// let green = group.color_rgb(0, 255, 0);
110 /// ```
111 pub fn color_rgb(&mut self, r: u8, g: u8, b: u8) -> Color<'a> {
112 let args = flat::ColorArgs { a: 255, r, g, b };
113 Color(flat::Color::create(&mut self.builder, &args))
114 }
115
116 /// Draw a line using screen coordinates.
117 ///
118 /// # Example
119 ///
120 /// ```no_run
121 /// # use rlbot::RenderGroup;
122 /// # let mut group: RenderGroup = unsafe { ::std::mem::uninitialized() };
123 /// # let green = group.color_rgb(0, 255, 0);
124 /// group.draw_line_2d((10.0, 10.0), (100.0, 100.0), green);
125 /// ```
126 pub fn draw_line_2d(
127 &mut self,
128 (x1, y1): (f32, f32),
129 (x2, y2): (f32, f32),
130 Color(color): Color<'_>,
131 ) {
132 let start = flat::Vector3::new(x1, y1, 0.0);
133 let end = flat::Vector3::new(x2, y2, 0.0);
134
135 let mut rm = flat::RenderMessageBuilder::new(&mut self.builder);
136 rm.add_renderType(flat::RenderType::DrawLine2D);
137 rm.add_color(color);
138 rm.add_start(&start);
139 rm.add_end(&end);
140 self.messages.push(rm.finish());
141 }
142
143 /// Draw a line using world coordinates.
144 ///
145 /// # Example
146 ///
147 /// ```no_run
148 /// # use rlbot::RenderGroup;
149 /// # let mut group: RenderGroup = unsafe { ::std::mem::uninitialized() };
150 /// # let green = group.color_rgb(0, 255, 0);
151 /// group.draw_line_3d((10.0, 10.0, 10.0), (100.0, 100.0, 100.0), green);
152 /// ```
153 pub fn draw_line_3d(
154 &mut self,
155 (x1, y1, z1): (f32, f32, f32),
156 (x2, y2, z2): (f32, f32, f32),
157 Color(color): Color<'_>,
158 ) {
159 let start = flat::Vector3::new(x1, y1, z1);
160 let end = flat::Vector3::new(x2, y2, z2);
161
162 let mut rm = flat::RenderMessageBuilder::new(&mut self.builder);
163 rm.add_renderType(flat::RenderType::DrawLine3D);
164 rm.add_color(color);
165 rm.add_start(&start);
166 rm.add_end(&end);
167 self.messages.push(rm.finish());
168 }
169
170 /// Draw a line with one endpoint in screen coordinates and the other at a
171 /// point projected from world coordinates to screen coordinates.
172 ///
173 /// # Example
174 ///
175 /// ```no_run
176 /// # use rlbot::RenderGroup;
177 /// # let mut group: RenderGroup = unsafe { ::std::mem::uninitialized() };
178 /// # let green = group.color_rgb(0, 255, 0);
179 /// group.draw_line_2d_3d((10.0, 10.0), (100.0, 100.0, 100.0), green);
180 /// ```
181 pub fn draw_line_2d_3d(
182 &mut self,
183 (x1, y1): (f32, f32),
184 (x2, y2, z2): (f32, f32, f32),
185 Color(color): Color<'_>,
186 ) {
187 let start = flat::Vector3::new(x1, y1, 0.0);
188 let end = flat::Vector3::new(x2, y2, z2);
189
190 let mut rm = flat::RenderMessageBuilder::new(&mut self.builder);
191 rm.add_renderType(flat::RenderType::DrawLine2D_3D);
192 rm.add_color(color);
193 rm.add_start(&start);
194 rm.add_end(&end);
195 self.messages.push(rm.finish());
196 }
197
198 /// Draw text using screen coordinates.
199 ///
200 /// # Example
201 ///
202 /// ```no_run
203 /// # use rlbot::RenderGroup;
204 /// # let mut group: RenderGroup = unsafe { ::std::mem::uninitialized() };
205 /// # let green = group.color_rgb(0, 255, 0);
206 /// group.draw_string_2d((10.0, 10.0), (2, 2), "I am text!", green);
207 /// ```
208 pub fn draw_string_2d(
209 &mut self,
210 (x, y): (f32, f32),
211 (scale_x, scale_y): (i32, i32),
212 text: impl AsRef<str>,
213 Color(color): Color<'_>,
214 ) {
215 let start = flat::Vector3::new(x, y, 0.0);
216 let text = self.builder.create_string(text.as_ref());
217
218 let mut rm = flat::RenderMessageBuilder::new(&mut self.builder);
219 rm.add_renderType(flat::RenderType::DrawString2D);
220 rm.add_color(color);
221 rm.add_start(&start);
222 rm.add_scaleX(scale_x);
223 rm.add_scaleY(scale_y);
224 rm.add_text(text);
225 self.messages.push(rm.finish());
226 }
227
228 /// Draw text at a point projected from world coordinates to screen
229 /// coordinates.
230 ///
231 /// # Example
232 ///
233 /// ```no_run
234 /// # use rlbot::RenderGroup;
235 /// # let mut group: RenderGroup = unsafe { ::std::mem::uninitialized() };
236 /// # let green = group.color_rgb(0, 255, 0);
237 /// group.draw_string_3d((10.0, 10.0, 10.0), (2, 2), "I am text!", green);
238 /// ```
239 pub fn draw_string_3d(
240 &mut self,
241 (x, y, z): (f32, f32, f32),
242 (scale_x, scale_y): (i32, i32),
243 text: impl AsRef<str>,
244 Color(color): Color<'_>,
245 ) {
246 let start = flat::Vector3::new(x, y, z);
247 let text = self.builder.create_string(text.as_ref());
248
249 let mut rm = flat::RenderMessageBuilder::new(&mut self.builder);
250 rm.add_renderType(flat::RenderType::DrawString2D);
251 rm.add_color(color);
252 rm.add_start(&start);
253 rm.add_scaleX(scale_x);
254 rm.add_scaleY(scale_y);
255 rm.add_text(text);
256 self.messages.push(rm.finish());
257 }
258}