raylib/rlgl/mod.rs
1//! Safe wrappers for rlgl, raylib's immediate-mode OpenGL abstraction layer
2//! (`rlgl.h`). rlgl sits directly beneath raylib's higher-level drawing
3//! functions and supports OpenGL 3.3, OpenGL 2.1, OpenGL ES 2.0, and the
4//! software-renderer backend — the same source compiles for all back-ends.
5//!
6//! This module exposes the surface most useful for custom rendering:
7//!
8//! - **Matrix stack** — [`rl_push_matrix`](RaylibRlgl::rl_push_matrix) returns
9//! an [`RlMatrix`] RAII guard; `Drop` pops the matrix, so the stack is always
10//! balanced even on early return.
11//! - **Immediate-mode vertex streams** —
12//! [`rl_begin`](RaylibRlgl::rl_begin) / [`rl_draw`](RaylibRlgl::rl_draw) with
13//! an [`RlImmediate`] RAII guard that calls `rlEnd` on drop.
14//! - **Render-state toggles** — depth test, back-face culling, etc.
15//! - **Safe bind helpers** — [`rl_set_texture`](RaylibRlgl::rl_set_texture) and
16//! [`rl_enable_shader`](RaylibRlgl::rl_enable_shader) accept the crate's
17//! [`Texture2D`](crate::core::texture::Texture2D) /
18//! [`Shader`](crate::core::shaders::Shader) RAII handles instead of raw ids.
19//!
20//! GL-object *lifecycle* (create/destroy) stays with those safe types and raw
21//! [`ffi`]; the full rlgl surface is still available there as a power-user
22//! escape hatch.
23//!
24//! ## Coverage policy
25//!
26//! The safe surface deliberately covers the immediate-mode, matrix-stack, and
27//! render-state slice of rlgl. The per-function disposition of the entire
28//! rlgl FFI surface (wrapped / escape-hatch / future-work) is recorded in
29//! `docs/superpowers/notes/databuf-mesh-testing-complete.md`, along with the
30//! census script to regenerate it after a raylib bump.
31//!
32//! All entry points are on [`RaylibRlgl`], blanket-implemented for every draw
33//! handle, so they are only callable inside a `begin_drawing` frame.
34
35mod immediate;
36mod matrix;
37
38pub use immediate::{DrawMode, RlImmediate};
39pub use matrix::{MatrixMode, RlMatrix};
40
41use crate::core::drawing::RaylibDraw;
42use crate::ffi;
43use crate::math::Matrix;
44
45/// Immediate-mode rlgl drawing, available on every draw handle. See the module docs.
46pub trait RaylibRlgl: RaylibDraw + Sized {
47 // ── Matrix stack ────────────────────────────────────────────────────────
48
49 /// Push the current matrix onto the stack; the returned guard pops it on drop.
50 /// Apply transforms (`rl_translatef`/`rl_rotatef`/`rl_scalef`) through the guard.
51 #[inline]
52 fn rl_push_matrix(&mut self) -> RlMatrix<'_, Self> {
53 // SAFETY: the returned RlMatrix's Drop calls the matching rlPopMatrix.
54 unsafe { ffi::rlPushMatrix() };
55 RlMatrix::new(self)
56 }
57
58 /// Reset the current matrix to identity.
59 #[inline]
60 fn rl_load_identity(&mut self) {
61 // SAFETY: rlLoadIdentity is an unconditional state mutation; no preconditions.
62 unsafe { ffi::rlLoadIdentity() }
63 }
64
65 /// Multiply the current matrix by a translation.
66 #[inline]
67 fn rl_translatef(&mut self, x: f32, y: f32, z: f32) {
68 // SAFETY: pure rlgl state call; no preconditions.
69 unsafe { ffi::rlTranslatef(x, y, z) }
70 }
71
72 /// Multiply the current matrix by a rotation of `angle` degrees about (x,y,z).
73 #[inline]
74 fn rl_rotatef(&mut self, angle: f32, x: f32, y: f32, z: f32) {
75 // SAFETY: pure rlgl state call; no preconditions.
76 unsafe { ffi::rlRotatef(angle, x, y, z) }
77 }
78
79 /// Multiply the current matrix by a scale.
80 #[inline]
81 fn rl_scalef(&mut self, x: f32, y: f32, z: f32) {
82 // SAFETY: pure rlgl state call; no preconditions.
83 unsafe { ffi::rlScalef(x, y, z) }
84 }
85
86 /// Multiply the current matrix by `mat`.
87 #[inline]
88 fn rl_mult_matrixf(&mut self, mat: Matrix) {
89 // SAFETY: ffi::Matrix is repr(C) with 16 contiguous f32 fields in the
90 // column-major order rlMultMatrixf expects. Casting the Matrix address to
91 // *const f32 is sound: the struct starts with m0 at offset 0 and is
92 // exactly 16×f32 = 64 bytes (confirmed by bindgen size assertion).
93 unsafe { ffi::rlMultMatrixf(&mat as *const Matrix as *const f32) }
94 }
95
96 /// Choose which matrix subsequent operations affect.
97 #[inline]
98 fn rl_matrix_mode(&mut self, mode: MatrixMode) {
99 // SAFETY: pure rlgl state call; mode is guaranteed valid by the MatrixMode enum.
100 unsafe { ffi::rlMatrixMode(mode as i32) }
101 }
102
103 /// Multiply the current matrix by an orthographic projection.
104 #[inline]
105 fn rl_ortho(&mut self, left: f64, right: f64, bottom: f64, top: f64, near: f64, far: f64) {
106 // SAFETY: pure rlgl state call; no preconditions.
107 unsafe { ffi::rlOrtho(left, right, bottom, top, near, far) }
108 }
109
110 /// Set the projection matrix directly.
111 #[inline]
112 fn rl_set_matrix_projection(&mut self, proj: Matrix) {
113 // SAFETY: crate::math::Matrix is re-exported from ffi::Matrix (same type);
114 // passed by value to rlSetMatrixProjection which expects ffi::Matrix.
115 unsafe { ffi::rlSetMatrixProjection(proj) }
116 }
117
118 /// Set the model-view matrix directly.
119 #[inline]
120 fn rl_set_matrix_modelview(&mut self, view: Matrix) {
121 // SAFETY: same identity as rl_set_matrix_projection.
122 unsafe { ffi::rlSetMatrixModelview(view) }
123 }
124
125 // ── Immediate-mode vertex stream ─────────────────────────────────────────
126
127 /// Begin an immediate-mode vertex stream; the returned guard ends it on drop.
128 /// Emit vertices/colors through the guard. Prefer [`rl_draw`](RaylibRlgl::rl_draw).
129 #[inline]
130 fn rl_begin(&mut self, mode: DrawMode) -> RlImmediate<'_, Self> {
131 // SAFETY: the returned RlImmediate's Drop calls the matching rlEnd.
132 unsafe { ffi::rlBegin(mode as i32) };
133 RlImmediate::new(self)
134 }
135
136 /// Run `body` inside an immediate-mode block, ending it afterwards (closure form).
137 ///
138 /// # Example
139 /// ```no_run
140 /// use raylib::prelude::*;
141 /// fn frame(d: &mut RaylibDrawHandle) {
142 /// d.rl_draw(DrawMode::Triangles, |v| {
143 /// v.color4ub(Color::RED);
144 /// v.vertex2f(0.0, 0.0);
145 /// v.vertex2f(100.0, 0.0);
146 /// v.vertex2f(50.0, 100.0);
147 /// });
148 /// }
149 /// ```
150 #[inline]
151 fn rl_draw(&mut self, mode: DrawMode, body: impl FnOnce(&mut RlImmediate<'_, Self>)) {
152 let mut v = self.rl_begin(mode);
153 body(&mut v);
154 }
155
156 // ── Render-state toggles ─────────────────────────────────────────────────
157
158 /// Enable depth testing.
159 #[inline]
160 fn rl_enable_depth_test(&mut self) {
161 // SAFETY: unconditional rlgl state toggle; no preconditions.
162 unsafe { ffi::rlEnableDepthTest() }
163 }
164
165 /// Disable depth testing.
166 #[inline]
167 fn rl_disable_depth_test(&mut self) {
168 // SAFETY: unconditional rlgl state toggle; no preconditions.
169 unsafe { ffi::rlDisableDepthTest() }
170 }
171
172 /// Enable back-face culling.
173 #[inline]
174 fn rl_enable_backface_culling(&mut self) {
175 // SAFETY: unconditional rlgl state toggle; no preconditions.
176 unsafe { ffi::rlEnableBackfaceCulling() }
177 }
178
179 /// Disable back-face culling.
180 #[inline]
181 fn rl_disable_backface_culling(&mut self) {
182 // SAFETY: unconditional rlgl state toggle; no preconditions.
183 unsafe { ffi::rlDisableBackfaceCulling() }
184 }
185
186 // ── Bind-safe-handle methods ─────────────────────────────────────────────
187
188 /// Set the active texture for subsequent immediate-mode drawing.
189 ///
190 /// # Example
191 /// ```no_run
192 /// use raylib::prelude::*;
193 /// fn frame(d: &mut RaylibDrawHandle, tex: &Texture2D) {
194 /// d.rl_set_texture(tex);
195 /// d.rl_draw(DrawMode::Quads, |v| {
196 /// v.color4ub(Color::WHITE);
197 /// v.texcoord2f(0.0, 0.0); v.vertex2f(0.0, 0.0);
198 /// v.texcoord2f(1.0, 0.0); v.vertex2f(64.0, 0.0);
199 /// v.texcoord2f(1.0, 1.0); v.vertex2f(64.0, 64.0);
200 /// v.texcoord2f(0.0, 1.0); v.vertex2f(0.0, 64.0);
201 /// });
202 /// d.rl_disable_texture();
203 /// }
204 /// ```
205 #[inline]
206 fn rl_set_texture(&mut self, texture: &crate::core::texture::Texture2D) {
207 // SAFETY: texture.id is a valid GPU texture id owned by the Texture2D RAII.
208 unsafe { ffi::rlSetTexture(texture.id) }
209 }
210
211 /// Enable a texture (by binding the safe handle's GL id).
212 #[inline]
213 fn rl_enable_texture(&mut self, texture: &crate::core::texture::Texture2D) {
214 // SAFETY: texture.id is a valid GPU texture id owned by the Texture2D RAII.
215 unsafe { ffi::rlEnableTexture(texture.id) }
216 }
217
218 /// Disable the active texture.
219 #[inline]
220 fn rl_disable_texture(&mut self) {
221 // SAFETY: unconditional rlgl state call; no preconditions.
222 unsafe { ffi::rlDisableTexture() }
223 }
224
225 /// Select the active multitexture slot.
226 #[inline]
227 fn rl_active_texture_slot(&mut self, slot: i32) {
228 // SAFETY: rlgl clamps out-of-range slots; no UB from an invalid slot value.
229 unsafe { ffi::rlActiveTextureSlot(slot) }
230 }
231
232 /// Enable a shader program (by binding the safe handle's GL id).
233 #[inline]
234 fn rl_enable_shader(&mut self, shader: &crate::core::shaders::Shader) {
235 // SAFETY: shader.id is a valid GPU shader id owned by the Shader RAII.
236 unsafe { ffi::rlEnableShader(shader.id) }
237 }
238
239 /// Set the active shader and its uniform locations (from the safe handle).
240 #[inline]
241 fn rl_set_shader(&mut self, shader: &crate::core::shaders::Shader) {
242 // SAFETY: shader.id is a valid GPU shader id; shader.locs is a non-null
243 // pointer owned and initialized by the Shader RAII's load path.
244 unsafe { ffi::rlSetShader(shader.id, shader.locs) }
245 }
246}
247
248impl<D: RaylibDraw> RaylibRlgl for D {}