MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
planet.cpp
Go to the documentation of this file.
1#include <cstdlib>
2
3#include <algorithm>
4#include <chrono>
5#include <cmath>
6#include <format>
7#include <iostream>
8#include <memory>
9#include <string>
10
11#include <glm/ext/matrix_clip_space.hpp>
12#include <glm/ext/matrix_transform.hpp>
13#include <glm/glm.hpp>
14
15#include "mxvk/argz.hpp"
16#include "mxvk/mxvk.hpp"
19#include "rain.hpp"
20
21namespace example {
22 namespace {
23 constexpr int MATRIX_SURFACE_WIDTH = 1280;
24 constexpr int MATRIX_SURFACE_HEIGHT = 720;
25 }
26
27 using Clock = std::chrono::steady_clock;
28
29 class PlanetWindow;
30
32 public:
34 const std::string &assetRoot,
35 bool binaryGlyphMode,
36 int fontSize,
37 const std::string &fontPath,
38 const std::string &color);
40
43 void updateAndRender(PlanetWindow &window, VkCommandBuffer cmd);
44
45 private:
46 std::unique_ptr<matrix::Rain> rain;
47 Clock::time_point lastFrame{Clock::now()};
48 };
49
51 public:
52 PlanetWindow(const std::string &filename,
53 const std::string &path,
54 const std::string &title,
55 int width,
56 int height,
57 bool fullscreen,
58 bool enable_vsync,
59 bool binary,
60 int font_size,
61 const std::string &font_path,
62 const std::string &color)
63 : mxvk::VK_Window(title, width, height, fullscreen, MXVK_VALIDATION, enable_vsync),
64 assetRoot(path.empty() ? std::string(PLANET_ASSET_DIR) : path) {
65 const std::string modelPath = filename.empty() ? (assetRoot + "/data/saturn.mxmod.z") : filename;
66 const std::string textureManifestPath = assetRoot + "/data/saturn.tex";
67 const std::string textureBasePath = assetRoot + "/data";
68 const std::string vertPath = assetRoot + "/data/model.vert.spv";
69 const std::string fragPath = assetRoot + "/data/model.frag.spv";
70
71 model.load(this, modelPath, textureManifestPath, textureBasePath, 1.0f);
72 model.setBackfaceCulling(false);
73 model.setShaders(this, vertPath, fragPath);
74
75 backdrop = std::make_unique<MatrixRainBackdrop>(*this, assetRoot, binary, font_size, font_path, color);
76 }
77
78 ~PlanetWindow() override {
79 if (device != VK_NULL_HANDLE) {
80 vkDeviceWaitIdle(device);
81 }
82 model.cleanup(this);
83 }
84
85 void event(SDL_Event &e) override {
86 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
87 exit();
88 return;
89 }
90
91 if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN && e.button.button == SDL_BUTTON_LEFT) {
92 mouseDragging = true;
93 lastMouseX = static_cast<int>(e.button.x);
94 lastMouseY = static_cast<int>(e.button.y);
95 return;
96 }
97
98 if (e.type == SDL_EVENT_MOUSE_BUTTON_UP && e.button.button == SDL_BUTTON_LEFT) {
99 mouseDragging = false;
100 return;
101 }
102
103 if (e.type == SDL_EVENT_MOUSE_MOTION && mouseDragging) {
104 const int x = static_cast<int>(e.motion.x);
105 const int y = static_cast<int>(e.motion.y);
106 const int deltaX = x - lastMouseX;
107 const int deltaY = y - lastMouseY;
108
109 yawDegrees += static_cast<float>(deltaX) * mouseSensitivity;
110 pitchDegrees += static_cast<float>(deltaY) * mouseSensitivity;
111 pitchDegrees = std::clamp(pitchDegrees, -80.0f, 80.0f);
112
113 lastMouseX = x;
114 lastMouseY = y;
115 return;
116 }
117
118 if (e.type == SDL_EVENT_MOUSE_WHEEL) {
119 const float delta = (e.wheel.y != 0.0f) ? e.wheel.y : static_cast<float>(e.wheel.integer_y);
120 cameraDistance -= delta * 0.45f;
121 cameraDistance = std::clamp(cameraDistance, 1.8f, 12.0f);
122 return;
123 }
124 }
125
127 if (backdrop) {
128 backdrop->onSwapchainAboutToRecreate();
129 }
130 }
131
132 void onSwapchainRecreated() override {
133 model.resize(this);
134 if (backdrop) {
135 backdrop->onSwapchainRecreated(*this);
136 }
137 }
138
139 void onRecordCustomRendering(VkCommandBuffer cmd, uint32_t imageIndex) override {
140 if (backdrop) {
141 backdrop->updateAndRender(*this, cmd);
142 }
143
144 const float elapsedSeconds = std::chrono::duration<float>(std::chrono::steady_clock::now() - start).count();
145 const VkExtent2D extent = getSwapchainExtent();
146 const float aspect = (extent.height > 0U)
147 ? static_cast<float>(extent.width) / static_cast<float>(extent.height)
148 : 1.0f;
149
151 ubo.model = glm::rotate(glm::mat4(1.0f), glm::radians(pitchDegrees), glm::vec3(1.0f, 0.0f, 0.0f));
152 ubo.model = glm::rotate(ubo.model, glm::radians(yawDegrees), glm::vec3(0.0f, 1.0f, 0.0f));
153 ubo.model = glm::rotate(ubo.model, glm::radians(-18.0f), glm::vec3(1.0f, 0.0f, 0.0f));
154 ubo.model = glm::rotate(ubo.model, elapsedSeconds * 0.45f, glm::vec3(0.0f, 1.0f, 0.0f));
155 ubo.model = glm::scale(ubo.model, glm::vec3(model.modelRenderScale()));
156 ubo.model = glm::translate(ubo.model, model.modelCenterOffset());
157 ubo.view = glm::lookAt(glm::vec3(0.0f, 0.15f, cameraDistance), glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
158 ubo.proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 100.0f);
159 ubo.proj[1][1] *= -1.0f;
160
161 model.updateUBO(imageIndex, ubo);
162 model.render(cmd, imageIndex, false);
163 }
164
165 void renderBackgroundSprite(mxvk::VK_Sprite &sprite, VkCommandBuffer cmd) {
166 renderStandaloneSprite(sprite, cmd);
167 }
168
169 private:
170 std::string assetRoot;
171 mxvk::VKAbstractModel model{};
172 std::unique_ptr<MatrixRainBackdrop> backdrop{};
173 std::chrono::steady_clock::time_point start{std::chrono::steady_clock::now()};
174 bool mouseDragging = false;
175 int lastMouseX = 0;
176 int lastMouseY = 0;
177 float yawDegrees = 0.0f;
178 float pitchDegrees = 0.0f;
179 float cameraDistance = 4.6f;
180 float mouseSensitivity = 0.35f;
181 };
182
184 const std::string &assetRootPath,
185 bool binaryGlyphMode,
186 int fontSize,
187 const std::string &fontPath,
188 const std::string &color)
189 : rain([&]() {
190 matrix::RainConfig config = matrix::make_matrix_rain_config(assetRootPath, binaryGlyphMode);
191 config.surface_width = MATRIX_SURFACE_WIDTH;
192 config.surface_height = MATRIX_SURFACE_HEIGHT;
193 if (binaryGlyphMode && fontPath.empty()) {
194 config.font_path = assetRootPath + "/data/binary.ttf";
195 }
196 config.font_size = std::max(1, fontSize);
197 if (!fontPath.empty()) {
198 config.font_path = fontPath;
199 }
200 config.color = color;
201 return std::make_unique<matrix::Rain>(window, std::move(config));
202 }()) {
203 lastFrame = Clock::now();
204 }
205
208
211
213 if (rain) {
214 rain->resize(window);
215 }
216 }
217
218 void MatrixRainBackdrop::updateAndRender(PlanetWindow &window, VkCommandBuffer cmd) {
219 const auto now = Clock::now();
220 float dt = std::chrono::duration<float>(now - lastFrame).count();
221 lastFrame = now;
222 dt = std::clamp(dt, 0.0f, 1.0f / 15.0f);
223
224 if (rain == nullptr) {
225 return;
226 }
227
228 rain->resize(window);
229 rain->update(dt);
230 const VkExtent2D extent = window.getSwapchainExtent();
231 rain->render(static_cast<int>(extent.width), static_cast<int>(extent.height));
232 if (rain->sprite() != nullptr) {
233 window.renderBackgroundSprite(*rain->sprite(), cmd);
234 rain->sprite()->clearQueue();
235 }
236 }
237} // namespace example
238
239int main(int argc, char **argv) {
240 try {
241 const Arguments args = proc_args(argc, argv);
243 args.filename, args.path, "MXVK Planet Example", args.width, args.height, args.fullscreen, args.enable_vsync, args.binary,
244 args.font_size, args.font_path, args.color);
245 window.loop();
246 } catch (mxvk::Exception &e) {
247 std::cerr << std::format("mxvk: Exception: {}\n", e.text());
248 return EXIT_FAILURE;
249 } catch (ArgException<std::string> &e) {
250 std::cerr << std::format("mxvk: Argument Exception: {}\n", e.text());
251 return EXIT_FAILURE;
252 }
253
254 return EXIT_SUCCESS;
255}
Lightweight, header-only, template command-line argument parser.
Arguments proc_args(int &argc, char **argv)
Parse standard libmx2 command-line options from main()'s argv.
Definition argz.hpp:872
Exception thrown by Argz::proc() on unrecognised or malformed options.
Definition argz.hpp:178
void onSwapchainRecreated(PlanetWindow &window)
Definition planet.cpp:212
MatrixRainBackdrop(PlanetWindow &window, const std::string &assetRoot, bool binaryGlyphMode, int fontSize, const std::string &fontPath, const std::string &color)
Definition planet.cpp:183
void updateAndRender(PlanetWindow &window, VkCommandBuffer cmd)
Definition planet.cpp:218
~PlanetWindow() override
Definition planet.cpp:78
void onSwapchainRecreated() override
Called after swapchain and render resources are recreated.
Definition planet.cpp:132
void onSwapchainAboutToRecreate() override
Called right before swapchain-dependent resources are recreated.
Definition planet.cpp:126
void event(SDL_Event &e) override
Handle one SDL event.
Definition planet.cpp:85
void onRecordCustomRendering(VkCommandBuffer cmd, uint32_t imageIndex) override
Optional hook for derived classes to record extra draw commands.
Definition planet.cpp:139
void renderBackgroundSprite(mxvk::VK_Sprite &sprite, VkCommandBuffer cmd)
Definition planet.cpp:165
PlanetWindow(const std::string &filename, const std::string &path, const std::string &title, int width, int height, bool fullscreen, bool enable_vsync, bool binary, int font_size, const std::string &font_path, const std::string &color)
Definition planet.cpp:52
std::string text() const
Convenience wrapper that owns mesh, textures, descriptors, and pipeline state.
Main Vulkan window wrapper for MXVK.
Definition mxvk.hpp:37
VkExtent2D getSwapchainExtent() const noexcept
Get the current swapchain extent.
Definition mxvk.hpp:186
void loop()
Run the main event/render loop.
Definition mxvk.cpp:600
VkDevice device
Definition mxvk.hpp:485
std::string font_path
Definition mxvk.hpp:568
void renderStandaloneSprite(VK_Sprite &sprite, VkCommandBuffer cmd)
Render one standalone sprite using the window's shared sprite pipeline.
Definition mxvk.cpp:1142
void exit()
Request loop termination.
Definition mxvk.cpp:1126
VK_Window()=default
Construct an empty window object.
int main(void)
Definition main.cpp:7
#define MXVK_VALIDATION
Definition mxvk.hpp:27
High-level model wrapper integrated with MXVK dynamic rendering.
std::chrono::steady_clock Clock
Definition planet.cpp:27
RainConfig make_matrix_rain_config(const std::string &asset_root, bool binary_glyph_mode)
Definition rain.cpp:157
Utilities for loading and saving PNG images.
Definition mxvk.hpp:30
Plain data structure returned by proc_args() with all common libmx2 CLI options.
Definition argz.hpp:730
std::string font_path
Optional font file path (--font-path).
Definition argz.hpp:762
bool fullscreen
Whether fullscreen mode was requested.
Definition argz.hpp:736
bool enable_vsync
Enable FIFO present mode / v-sync (--enable-vsync).
Definition argz.hpp:750
int height
Viewport height in pixels (default: 720).
Definition argz.hpp:733
std::string color
Optional rain RGB tint (--color).
Definition argz.hpp:763
std::string filename
Optional input filename (--filename).
Definition argz.hpp:738
std::string path
Asset root; proc_args() defaults it to the executable directory.
Definition argz.hpp:735
int width
Viewport width in pixels (default: 1280).
Definition argz.hpp:732
int font_size
Matrix rain font size in pixels (--font-size).
Definition argz.hpp:761
bool binary
Use binary glyphs only (--binary).
Definition argz.hpp:748
std::string color
Definition rain.hpp:19
std::string font_path
Definition rain.hpp:18
Default transform UBO payload for model shaders.