MXVK Vulkan Framework 0.35.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
graphics.py
Go to the documentation of this file.
1## @file graphics.py
2## @brief Simple drawable, model, and GPU-resource wrappers.
3
4from pathlib import Path
5from typing import TYPE_CHECKING, Sequence
6
7from ._native import mxvk
8
9if TYPE_CHECKING:
10 from .app import App
11
12
13def _abort_failed_resource(app: "App") -> None:
14 ## @brief Release an app after an app-owned resource could not be created.
15 ## @param app Application that owns resources created before the failure.
16 ## @details Cleanup errors are deliberately ignored so callers receive the
17 ## original missing-file or native resource-creation exception.
18 try:
19 app.close()
20 except Exception:
21 pass
22
23
24class Font:
25 ## @brief A reusable TrueType font for individual text draw calls.
26 ## @details Supplying this font to @c App.draw_text does not change or
27 ## reload the window's default font configured with @c App.set_font.
28
29 def __init__(self, path: str | Path, size: int) -> None:
30 ## @brief Load a font file at the requested point size.
31 ## @param path Path to a TrueType or OpenType font file.
32 ## @param size Font size in points.
33 self.native = mxvk.Font(str(path), size)
34
35 @property
36 def valid(self) -> bool:
37 ## @brief Return whether the native font was loaded successfully.
38 return self.native is not None and self.native.valid()
39
40 def close(self) -> None:
41 ## @brief Drop this font's native handle.
42 self.native = None
43
44
45class Sprite:
46 ## @brief A 2D image that can be queued for drawing each frame.
47
48 def __init__(self, app: "App", image: str | Path | None = None, *, width: int = 0, height: int = 0, vertex_shader: str = "", fragment_shader: str = "") -> None:
49 ## @brief Create an image sprite or an empty dynamic texture.
50 ## @param app The owning application.
51 ## @param image Image file to load; omit for an empty sprite.
52 ## @param width Empty texture width.
53 ## @param height Empty texture height.
54 ## @param vertex_shader Optional vertex SPIR-V path.
55 ## @param fragment_shader Optional fragment SPIR-V path.
56 self.native = None
57 try:
58 if image is not None:
59 self.native = app.create_sprite(str(image), vertex_shader, fragment_shader)
60 elif width > 0 and height > 0:
61 self.native = app.create_sprite(width, height, vertex_shader, fragment_shader)
62 else:
63 raise ValueError("provide image or positive width and height")
64 except Exception:
65 self.native = None
67 raise
68 app.add(self)
69
70 def draw(self, x: int = 0, y: int = 0, *, width: int | None = None, height: int | None = None, scale: float = 1.0, rotation: float = 0.0) -> None:
71 ## @brief Queue this sprite for the current frame.
72 ## @param x Left pixel coordinate.
73 ## @param y Top pixel coordinate.
74 ## @param width Optional output width; requires @p height too.
75 ## @param height Optional output height; requires @p width too.
76 ## @param scale Uniform scale used when no output rectangle is supplied.
77 ## @param rotation Rotation in radians used when no output rectangle is supplied.
78 if width is not None or height is not None:
79 if width is None or height is None:
80 raise ValueError("width and height must be supplied together")
81 self.native.draw_rect(x, y, width, height)
82 elif rotation != 0.0:
83 self.native.draw_rotated(x, y, scale, scale, rotation)
84 elif scale != 1.0:
85 self.native.draw(x, y, scale, scale)
86 else:
87 self.native.draw(x, y)
88
89 def update(self, pixels: object, width: int, height: int, *, pitch: int = 0) -> None:
90 ## @brief Replace this sprite's pixels with a contiguous RGBA8 array.
91 ## @param pixels NumPy-compatible RGBA8 pixel array.
92 ## @param width Pixel width.
93 ## @param height Pixel height.
94 ## @param pitch Bytes per row; zero uses @c width * 4.
95 self.native.update_texture(pixels, width, height, pitch)
96
97 def shader_params(self, first: float = 0.0, second: float = 0.0, third: float = 0.0, fourth: float = 0.0) -> None:
98 ## @brief Set the four generic shader parameters for this sprite.
99 self.native.set_shader_params(first, second, third, fourth)
100
101 def enable_extended_uniforms(self) -> None:
102 ## @brief Enable mouse and four-vector uniforms for a custom sprite shader.
103 self.native.enable_extended_ubo()
104
105 def set_mouse(self, x: float, y: float, pressed: bool = False) -> None:
106 ## @brief Set mouse data consumed by an extended sprite shader.
107 ## @param x Mouse X coordinate in window pixels.
108 ## @param y Mouse Y coordinate in window pixels.
109 ## @param pressed Whether a mouse button is currently pressed.
110 self.native.set_mouse_state(x, y, 1.0 if pressed else 0.0)
111
112 def set_uniform(self, index: int, x: float, y: float, z: float, w: float) -> None:
113 ## @brief Set one of four generic vectors for an extended sprite shader.
114 ## @param index Vector index from zero through three.
115 ## @param x First component.
116 ## @param y Second component.
117 ## @param z Third component.
118 ## @param w Fourth component.
119 if index not in range(4):
120 raise ValueError("uniform index must be from 0 through 3")
121 getattr(self.native, f"set_uniform{index}")(x, y, z, w)
122
123 def close(self) -> None:
124 ## @brief Drop this sprite's native handle before its app is released.
125 ## @details MXVK owns sprites through the window; releasing this Python
126 ## handle first prevents a Python/native reference cycle at shutdown.
127 self.native = None
128
129
131 ## @brief A billboard sprite rendered through MXVK's 3D command callback.
132
133 def __init__(self, app: "App", image: str | Path, *, vertex_shader: str = "", fragment_shader: str = "") -> None:
134 ## @brief Load a 3D sprite owned by @p app.
135 self.native = None
136 try:
137 self.native = app.create_sprite3d(str(image), vertex_shader, fragment_shader)
138 except Exception:
140 raise
141 app.add(self)
142
143 def queue(self, position: Sequence[float], size: Sequence[float], color: Sequence[float] = (1.0, 1.0, 1.0, 1.0), rotation: float = 0.0) -> None:
144 ## @brief Queue one billboard draw.
145 self.native.draw(tuple(position), tuple(size), tuple(color), rotation)
146
147 def close(self) -> None:
148 ## @brief Release 3D sprite resources and drop its native handle.
149 if self.native is not None:
150 self.native.cleanup()
151 self.native = None
152
153
154class Model:
155 ## @brief A loadable 3D model with one-call setup.
156
157 def __init__(self, app: "App", path: str | Path, *, textures: str | Path = "", texture_directory: str | Path = "", scale: float = 1.0) -> None:
158 ## @brief Load a model and retain its application lifetime.
159 self._app = app
160 self.native = None
161 try:
162 self.native = mxvk.AbstractModel()
163 self.native.load(app, str(path), str(textures), str(texture_directory), scale)
164 except Exception:
165 self.native = None
167 raise
168 app.add(self)
169
170 def shaders(self, app: "App", vertex: str | Path, fragment: str | Path) -> None:
171 ## @brief Select custom model shaders.
172 self.native.set_shaders(app, str(vertex), str(fragment))
173
174 def cleanup(self, app: "App") -> None:
175 ## @brief Explicitly free model GPU resources before closing @p app.
176 if self.native is not None:
177 self.native.cleanup(app)
178 self.native = None
179
180 def close(self) -> None:
181 ## @brief Free model GPU resources and drop its native handle.
182 if self.native is not None:
183 self.native.cleanup(self._app)
184 self.native = None
185
186
188 ## @brief A small host-writable uniform or storage buffer.
189
190 def __init__(self, app: "App", size: int, *, storage: bool = False) -> None:
191 ## @brief Allocate a buffer with @p size bytes.
192 self.native = None
193 try:
194 self.native = mxvk.create_storage_buffer(app, size) if storage else mxvk.create_uniform_buffer(app, size)
195 except Exception:
197 raise
198 app.add(self)
199
200 def write(self, data: bytes) -> None:
201 ## @brief Upload bytes to the buffer.
202 self.native.write(data)
203
204 def close(self) -> None:
205 ## @brief Release the underlying Vulkan buffer early.
206 if self.native is not None:
207 self.native.close()
208 self.native = None
209
210
212 ## @brief A GPU texture loaded from an image file.
213
214 def __init__(self, app: "App", path: str | Path) -> None:
215 ## @brief Load @p path into an application-owned GPU texture.
216 self.native = None
217 try:
218 self.native = mxvk.load_texture(app, str(path))
219 except Exception:
221 raise
222 app.add(self)
223
224 def close(self) -> None:
225 ## @brief Release the underlying Vulkan texture early.
226 if self.native is not None:
227 self.native.close()
228 self.native = None
Small RAII wrapper for an SDL_ttf font handle.
Definition mxvk_text.hpp:46
native
Load a font file at the requested point size.
Definition graphics.py:33
None __init__(self, str|Path path, int size)
A reusable TrueType font for individual text draw calls.
Definition graphics.py:29
None write(self, bytes data)
Definition graphics.py:200
native
Allocate a buffer with size bytes.
Definition graphics.py:192
None __init__(self, "App" app, int size, *, bool storage=False)
A small host-writable uniform or storage buffer.
Definition graphics.py:190
native
Load path into an application-owned GPU texture.
Definition graphics.py:216
None __init__(self, "App" app, str|Path path)
A GPU texture loaded from an image file.
Definition graphics.py:214
None shaders(self, "App" app, str|Path vertex, str|Path fragment)
Definition graphics.py:170
_app
Load a model and retain its application lifetime.
Definition graphics.py:159
None cleanup(self, "App" app)
Definition graphics.py:174
native
Explicitly free model GPU resources before closing app.
Definition graphics.py:160
None __init__(self, "App" app, str|Path path, *, str|Path textures="", str|Path texture_directory="", float scale=1.0)
A loadable 3D model with one-call setup.
Definition graphics.py:157
native
Load a 3D sprite owned by app.
Definition graphics.py:135
None __init__(self, "App" app, str|Path image, *, str vertex_shader="", str fragment_shader="")
A billboard sprite rendered through MXVK's 3D command callback.
Definition graphics.py:133
None queue(self, Sequence[float] position, Sequence[float] size, Sequence[float] color=(1.0, 1.0, 1.0, 1.0), float rotation=0.0)
Definition graphics.py:143
native
Create an image sprite or an empty dynamic texture.
Definition graphics.py:56
None enable_extended_uniforms(self)
Definition graphics.py:101
None set_mouse(self, float x, float y, bool pressed=False)
Definition graphics.py:105
None draw(self, int x=0, int y=0, *, int|None width=None, int|None height=None, float scale=1.0, float rotation=0.0)
Definition graphics.py:70
None set_uniform(self, int index, float x, float y, float z, float w)
Definition graphics.py:112
None shader_params(self, float first=0.0, float second=0.0, float third=0.0, float fourth=0.0)
Definition graphics.py:97
None update(self, object pixels, int width, int height, *, int pitch=0)
Definition graphics.py:89
None __init__(self, "App" app, str|Path|None image=None, *, int width=0, int height=0, str vertex_shader="", str fragment_shader="")
A 2D image that can be queued for drawing each frame.
Definition graphics.py:48
None _abort_failed_resource("App" app)
Definition graphics.py:13