MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
mxvk_sprite3d.hpp
Go to the documentation of this file.
1/**
2 * @file mxvk_sprite3d.hpp
3 * @brief World-space textured billboard renderer for MXVK dynamic rendering.
4 *
5 * VK_Sprite3D manages a textured quad in world space, with per-frame camera
6 * uniforms, depth-tested rendering, and a draw queue for billboard sprites.
7 */
8#pragma once
9
10#include <volk/volk.h>
11
12#include <SDL3/SDL.h>
13#include <glm/glm.hpp>
14
15#include <string>
16#include <vector>
17
18namespace mxvk {
19
20 /**
21 * @brief Forward declaration of the MXVK window wrapper.
22 */
23 class VK_Window;
24
25 /**
26 * @class VK_Sprite3D
27 * @brief Depth-tested 3D billboard sprite batch.
28 *
29 * VK_Sprite3D renders textured quads in world space. Each queued sprite is
30 * camera-facing, uses the view/projection matrix supplied with updateCamera(),
31 * and participates in the same dynamic-rendering pass as models.
32 */
34 public:
35 /**
36 * @brief Construct an empty 3D sprite batch.
37 */
38 VK_Sprite3D() = default;
39
40 /**
41 * @brief Destroy owned Vulkan resources.
42 */
44
45 VK_Sprite3D(const VK_Sprite3D &) = delete;
46 VK_Sprite3D &operator=(const VK_Sprite3D &) = delete;
49
50 /**
51 * @brief Load sprite texture and build the 3D billboard pipeline from a PNG file.
52 * @param window Active MXVK window.
53 * @param pngPath Path to the PNG file.
54 * @param vertexShaderPath Optional custom vertex shader SPIR-V path.
55 * @param fragmentShaderPath Optional custom fragment shader SPIR-V path.
56 */
57 void load(VK_Window *window,
58 const std::string &pngPath,
59 const std::string &vertexShaderPath = "",
60 const std::string &fragmentShaderPath = "");
61
62 /**
63 * @brief Load sprite texture and build the 3D billboard pipeline from an SDL surface.
64 * @param window Active MXVK window.
65 * @param surface Source surface pointer.
66 * @param vertexShaderPath Optional custom vertex shader SPIR-V path.
67 * @param fragmentShaderPath Optional custom fragment shader SPIR-V path.
68 */
69 void load(VK_Window *window,
70 SDL_Surface *surface,
71 const std::string &vertexShaderPath = "",
72 const std::string &fragmentShaderPath = "");
73
74 /**
75 * @brief Upload the current camera matrices for one swapchain image.
76 * @param imageIndex Swapchain image index.
77 * @param view View matrix.
78 * @param proj Projection matrix.
79 */
80 void updateCamera(uint32_t imageIndex, const glm::mat4 &view, const glm::mat4 &proj);
81
82 /**
83 * @brief Queue a billboard sprite for rendering.
84 * @param position World-space center position.
85 * @param size Billboard size in world units.
86 * @param color Per-sprite tint color.
87 * @param rotationRadians Rotation around the camera-facing axis.
88 */
89 void drawSprite(const glm::vec3 &position,
90 const glm::vec2 &size,
91 const glm::vec4 &color = glm::vec4(1.0f),
92 float rotationRadians = 0.0f);
93
94 /**
95 * @brief Record all queued billboard draws into the given command buffer.
96 * @param cmd Active command buffer.
97 * @param imageIndex Current swapchain image index.
98 */
99 void render(VkCommandBuffer cmd, uint32_t imageIndex);
100
101 /**
102 * @brief Discard all queued sprite draws without rendering them.
103 */
104 void clearQueue();
105
106 /**
107 * @brief Enable or disable depth testing for the 3D sprite pipeline.
108 * @param enabled @c true to enable depth testing.
109 */
110 void setDepthTestEnabled(bool enabled);
111
112 /**
113 * @brief Enable or disable depth writes for the 3D sprite pipeline.
114 * @param enabled @c true to write depth values.
115 */
116 void setDepthWriteEnabled(bool enabled);
117
118 /**
119 * @brief Set the alpha threshold used to discard transparent texels.
120 * @param threshold Alpha cutoff value.
121 */
122 void setAlphaDiscardThreshold(float threshold) { alphaDiscardThreshold = threshold; }
123
124 /**
125 * @brief Rebuild swapchain-dependent resources after resize.
126 * @param window Active MXVK window.
127 */
128 void resize(VK_Window *window);
129
130 /**
131 * @brief Destroy all owned Vulkan resources.
132 */
133 void cleanup();
134
135 /** @return @c true if the sprite texture and pipeline are loaded. */
136 [[nodiscard]] bool loaded() const { return spriteLoaded; }
137 /** @return Sprite texture width in pixels. */
138 [[nodiscard]] int getWidth() const { return spriteWidth; }
139 /** @return Sprite texture height in pixels. */
140 [[nodiscard]] int getHeight() const { return spriteHeight; }
141
142 private:
143 /** @brief Static quad vertex containing local position and UV coordinates. */
144 struct Vertex {
145 glm::vec2 pos;
146 glm::vec2 uv;
147 };
148
149 /** @brief One queued 3D billboard draw command. */
150 struct DrawCmd {
151 glm::vec3 position;
152 glm::vec2 size;
153 glm::vec4 color;
154 float rotationRadians = 0.0f;
155 };
156
157 /** @brief Per-frame camera uniform payload. */
158 struct CameraUBO {
159 glm::mat4 view{1.0f};
160 glm::mat4 proj{1.0f};
161 };
162
163 VkDevice device = VK_NULL_HANDLE;
164 VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
165 VkQueue graphicsQueue = VK_NULL_HANDLE;
166 VkCommandPool commandPool = VK_NULL_HANDLE;
167 VkPipelineCache pipelineCache = VK_NULL_HANDLE;
168 VkFormat colorAttachmentFormat = VK_FORMAT_UNDEFINED;
169 VkFormat depthAttachmentFormat = VK_FORMAT_UNDEFINED;
170 size_t imageCount = 0;
171
172 VkImage spriteImage = VK_NULL_HANDLE;
173 VkDeviceMemory spriteImageMemory = VK_NULL_HANDLE;
174 VkImageView spriteImageView = VK_NULL_HANDLE;
175 VkSampler spriteSampler = VK_NULL_HANDLE;
176 int spriteWidth = 0;
177 int spriteHeight = 0;
178 bool spriteLoaded = false;
179
180 VkBuffer vertexBuffer = VK_NULL_HANDLE;
181 VkDeviceMemory vertexBufferMemory = VK_NULL_HANDLE;
182 VkBuffer indexBuffer = VK_NULL_HANDLE;
183 VkDeviceMemory indexBufferMemory = VK_NULL_HANDLE;
184
185 VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
186 VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
187 std::vector<VkDescriptorSet> descriptorSets;
188
189 std::vector<VkBuffer> cameraBuffers;
190 std::vector<VkDeviceMemory> cameraBufferMemory;
191 std::vector<void *> cameraBuffersMapped;
192
193 VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
194 VkPipeline pipeline = VK_NULL_HANDLE;
195 std::string vertexShaderPath;
196 std::string fragmentShaderPath;
197 bool depthTestEnabled = true;
198 bool depthWriteEnabled = false;
199 float alphaDiscardThreshold = 0.1f;
200
201 std::vector<DrawCmd> drawQueue;
202
203 /** @brief Build the quad vertex and index buffers. */
204 void createQuadBuffers();
205 /** @brief Upload and initialize the sprite texture image. */
206 void createTexture(SDL_Surface *surface);
207 /** @brief Create the sampler used to sample the sprite texture. */
208 void createSampler();
209 /** @brief Create the descriptor set layout used by the pipeline. */
210 void createDescriptorSetLayout();
211 /** @brief Allocate and map one camera UBO per swapchain image. */
212 void createCameraBuffers();
213 /** @brief Destroy the per-image camera UBO resources. */
214 void destroyCameraBuffers();
215 /** @brief Create the descriptor pool for texture and camera bindings. */
216 void createDescriptorPool();
217 /** @brief Allocate and update descriptor sets for the loaded sprite. */
218 void createDescriptorSets();
219 /** @brief Create or recreate the graphics pipeline. */
220 void createPipeline();
221 /** @brief Destroy the graphics pipeline and pipeline layout. */
222 void destroyPipeline();
223 /** @brief Destroy the texture image, view, and sampler. */
224 void destroyTexture();
225 /** @brief Destroy the quad vertex and index buffers. */
226 void destroyBuffers();
227 /** @brief Destroy descriptor resources owned by this sprite batch. */
228 void destroyDescriptors();
229
230 /**
231 * @brief Create a Vulkan buffer and bind device memory to it.
232 * @param size Buffer size in bytes.
233 * @param usage Buffer usage flags.
234 * @param properties Required memory properties.
235 * @param buffer Output buffer handle.
236 * @param bufferMemory Output device memory handle.
237 */
238 void createBuffer(VkDeviceSize size,
239 VkBufferUsageFlags usage,
240 VkMemoryPropertyFlags properties,
241 VkBuffer &buffer,
242 VkDeviceMemory &bufferMemory) const;
243 /** @brief Find a suitable memory type index for the requested properties. */
244 [[nodiscard]] uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) const;
245 /** @brief Begin a one-time command buffer on the sprite command pool. */
246 [[nodiscard]] VkCommandBuffer beginSingleTimeCommands() const;
247 /** @brief End and submit a one-time command buffer. */
248 void endSingleTimeCommands(VkCommandBuffer commandBuffer) const;
249 /**
250 * @brief Create a Vulkan image for the sprite texture.
251 * @param width Image width in pixels.
252 * @param height Image height in pixels.
253 * @param format Image format.
254 * @param tiling Image tiling mode.
255 * @param usage Image usage flags.
256 * @param properties Required memory properties.
257 * @param image Output image handle.
258 * @param imageMemory Output device memory handle.
259 */
260 void createImage(uint32_t width,
261 uint32_t height,
262 VkFormat format,
263 VkImageTiling tiling,
264 VkImageUsageFlags usage,
265 VkMemoryPropertyFlags properties,
266 VkImage &image,
267 VkDeviceMemory &imageMemory) const;
268 /** @brief Create an image view for the sprite texture. */
269 [[nodiscard]] VkImageView createImageView(VkImage image, VkFormat format) const;
270 /** @brief Transition an image between layouts for upload and sampling. */
271 void transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) const;
272 /** @brief Copy a staging buffer into the sprite texture image. */
273 void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) const;
274 /** @brief Convert an SDL surface to RGBA32 format. */
275 [[nodiscard]] SDL_Surface *convertToRGBA(SDL_Surface *surface) const;
276 /** @brief Read a SPIR-V shader file from disk. */
277 [[nodiscard]] std::vector<char> readShaderFile(const std::string &path) const;
278 };
279
280} // namespace mxvk
void drawSprite(const glm::vec3 &position, const glm::vec2 &size, const glm::vec4 &color=glm::vec4(1.0f), float rotationRadians=0.0f)
Queue a billboard sprite for rendering.
VK_Sprite3D & operator=(VK_Sprite3D &&)=delete
void cleanup()
Destroy all owned Vulkan resources.
VK_Sprite3D(const VK_Sprite3D &)=delete
void render(VkCommandBuffer cmd, uint32_t imageIndex)
Record all queued billboard draws into the given command buffer.
void setDepthWriteEnabled(bool enabled)
Enable or disable depth writes for the 3D sprite pipeline.
void setDepthTestEnabled(bool enabled)
Enable or disable depth testing for the 3D sprite pipeline.
VK_Sprite3D(VK_Sprite3D &&)=delete
void load(VK_Window *window, const std::string &pngPath, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Load sprite texture and build the 3D billboard pipeline from a PNG file.
VK_Sprite3D()=default
Construct an empty 3D sprite batch.
VK_Sprite3D & operator=(const VK_Sprite3D &)=delete
void clearQueue()
Discard all queued sprite draws without rendering them.
void resize(VK_Window *window)
Rebuild swapchain-dependent resources after resize.
void updateCamera(uint32_t imageIndex, const glm::mat4 &view, const glm::mat4 &proj)
Upload the current camera matrices for one swapchain image.
void setAlphaDiscardThreshold(float threshold)
Set the alpha threshold used to discard transparent texels.
~VK_Sprite3D()
Destroy owned Vulkan resources.
Main Vulkan window wrapper for MXVK.
Definition mxvk.hpp:37
Utilities for loading and saving PNG images.
Definition mxvk.hpp:30