11#include <glm/ext/matrix_clip_space.hpp>
12#include <glm/ext/matrix_transform.hpp>
27 using Clock = std::chrono::steady_clock;
34 const std::string &assetRoot,
37 const std::string &fontPath,
38 const std::string &color);
46 std::unique_ptr<matrix::Rain> rain;
47 Clock::time_point lastFrame{Clock::now()};
53 const std::string &path,
54 const std::string &title,
62 const std::string &color)
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";
71 model.load(
this, modelPath, textureManifestPath, textureBasePath, 1.0f);
72 model.setBackfaceCulling(
false);
73 model.setShaders(
this, vertPath, fragPath);
75 backdrop = std::make_unique<MatrixRainBackdrop>(*
this, assetRoot, binary,
font_size,
font_path, color);
79 if (
device != VK_NULL_HANDLE) {
85 void event(SDL_Event &e)
override {
86 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
91 if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN && e.button.button == SDL_BUTTON_LEFT) {
93 lastMouseX =
static_cast<int>(e.button.x);
94 lastMouseY =
static_cast<int>(e.button.y);
98 if (e.type == SDL_EVENT_MOUSE_BUTTON_UP && e.button.button == SDL_BUTTON_LEFT) {
99 mouseDragging =
false;
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;
109 yawDegrees +=
static_cast<float>(deltaX) * mouseSensitivity;
110 pitchDegrees +=
static_cast<float>(deltaY) * mouseSensitivity;
111 pitchDegrees = std::clamp(pitchDegrees, -80.0f, 80.0f);
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);
128 backdrop->onSwapchainAboutToRecreate();
135 backdrop->onSwapchainRecreated(*
this);
141 backdrop->updateAndRender(*
this, cmd);
144 const float elapsedSeconds = std::chrono::duration<float>(std::chrono::steady_clock::now() - start).count();
146 const float aspect = (extent.height > 0U)
147 ?
static_cast<float>(extent.width) /
static_cast<float>(extent.height)
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;
161 model.updateUBO(imageIndex, ubo);
162 model.render(cmd, imageIndex,
false);
170 std::string assetRoot;
172 std::unique_ptr<MatrixRainBackdrop> backdrop{};
173 std::chrono::steady_clock::time_point start{std::chrono::steady_clock::now()};
174 bool mouseDragging =
false;
177 float yawDegrees = 0.0f;
178 float pitchDegrees = 0.0f;
179 float cameraDistance = 4.6f;
180 float mouseSensitivity = 0.35f;
184 const std::string &assetRootPath,
185 bool binaryGlyphMode,
187 const std::string &fontPath,
188 const std::string &color)
193 if (binaryGlyphMode && fontPath.empty()) {
194 config.
font_path = assetRootPath +
"/data/binary.ttf";
196 config.
font_size = std::max(1, fontSize);
197 if (!fontPath.empty()) {
200 config.
color = color;
201 return std::make_unique<matrix::Rain>(window, std::move(config));
203 lastFrame = Clock::now();
214 rain->resize(window);
219 const auto now = Clock::now();
220 float dt = std::chrono::duration<float>(now - lastFrame).count();
222 dt = std::clamp(dt, 0.0f, 1.0f / 15.0f);
224 if (rain ==
nullptr) {
228 rain->resize(window);
231 rain->render(
static_cast<int>(extent.width),
static_cast<int>(extent.height));
232 if (rain->sprite() !=
nullptr) {
234 rain->sprite()->clearQueue();
239int main(
int argc,
char **argv) {
247 std::cerr << std::format(
"mxvk: Exception: {}\n", e.
text());
250 std::cerr << std::format(
"mxvk: Argument Exception: {}\n", e.
text());
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.
Exception thrown by Argz::proc() on unrecognised or malformed options.
void onSwapchainAboutToRecreate()
void onSwapchainRecreated(PlanetWindow &window)
MatrixRainBackdrop(PlanetWindow &window, const std::string &assetRoot, bool binaryGlyphMode, int fontSize, const std::string &fontPath, const std::string &color)
void updateAndRender(PlanetWindow &window, VkCommandBuffer cmd)
void onSwapchainRecreated() override
Called after swapchain and render resources are recreated.
void onSwapchainAboutToRecreate() override
Called right before swapchain-dependent resources are recreated.
void event(SDL_Event &e) override
Handle one SDL event.
void onRecordCustomRendering(VkCommandBuffer cmd, uint32_t imageIndex) override
Optional hook for derived classes to record extra draw commands.
void renderBackgroundSprite(mxvk::VK_Sprite &sprite, VkCommandBuffer cmd)
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)
Convenience wrapper that owns mesh, textures, descriptors, and pipeline state.
Main Vulkan window wrapper for MXVK.
VkExtent2D getSwapchainExtent() const noexcept
Get the current swapchain extent.
void loop()
Run the main event/render loop.
void renderStandaloneSprite(VK_Sprite &sprite, VkCommandBuffer cmd)
Render one standalone sprite using the window's shared sprite pipeline.
void exit()
Request loop termination.
VK_Window()=default
Construct an empty window object.
High-level model wrapper integrated with MXVK dynamic rendering.
constexpr int MATRIX_SURFACE_HEIGHT
constexpr int MATRIX_SURFACE_WIDTH
std::chrono::steady_clock Clock
RainConfig make_matrix_rain_config(const std::string &asset_root, bool binary_glyph_mode)
Utilities for loading and saving PNG images.
Plain data structure returned by proc_args() with all common libmx2 CLI options.
std::string font_path
Optional font file path (--font-path).
bool fullscreen
Whether fullscreen mode was requested.
bool enable_vsync
Enable FIFO present mode / v-sync (--enable-vsync).
int height
Viewport height in pixels (default: 720).
std::string color
Optional rain RGB tint (--color).
std::string filename
Optional input filename (--filename).
std::string path
Asset root; proc_args() defaults it to the executable directory.
int width
Viewport width in pixels (default: 1280).
int font_size
Matrix rain font size in pixels (--font-size).
bool binary
Use binary glyphs only (--binary).