11#include <glm/ext/matrix_clip_space.hpp>
12#include <glm/ext/matrix_transform.hpp>
27 using Clock = std::chrono::steady_clock;
33 MatrixRainBackdrop(
PlanetWindow &window,
const std::string &assetRoot,
bool binaryGlyphMode,
int fontSize,
const std::string &fontPath,
const std::string &color);
41 std::unique_ptr<matrix::Rain> rain;
42 Clock::time_point lastFrame{Clock::now()};
47 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) :
mxvk::
VK_Window(title, width, height, fullscreen,
MXVK_VALIDATION, enable_vsync), assetRoot(path.empty() ? std::string(PLANET_ASSET_DIR) : path) {
48 const std::string modelPath = filename.empty() ? (assetRoot +
"/data/saturn.mxmod.z") : filename;
49 const std::string textureManifestPath = assetRoot +
"/data/saturn.tex";
50 const std::string textureBasePath = assetRoot +
"/data";
51 const std::string vertPath = assetRoot +
"/data/model.vert.spv";
52 const std::string fragPath = assetRoot +
"/data/model.frag.spv";
54 model.load(
this, modelPath, textureManifestPath, textureBasePath, 1.0f);
55 model.setBackfaceCulling(
false);
56 model.setShaders(
this, vertPath, fragPath);
58 backdrop = std::make_unique<MatrixRainBackdrop>(*
this, assetRoot, binary,
font_size,
font_path, color);
62 if (
device != VK_NULL_HANDLE) {
68 void event(SDL_Event &e)
override {
69 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
74 if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN && e.button.button == SDL_BUTTON_LEFT) {
76 lastMouseX =
static_cast<int>(e.button.x);
77 lastMouseY =
static_cast<int>(e.button.y);
81 if (e.type == SDL_EVENT_MOUSE_BUTTON_UP && e.button.button == SDL_BUTTON_LEFT) {
82 mouseDragging =
false;
86 if (e.type == SDL_EVENT_MOUSE_MOTION && mouseDragging) {
87 const int x =
static_cast<int>(e.motion.x);
88 const int y =
static_cast<int>(e.motion.y);
89 const int deltaX = x - lastMouseX;
90 const int deltaY = y - lastMouseY;
92 yawDegrees +=
static_cast<float>(deltaX) * mouseSensitivity;
93 pitchDegrees +=
static_cast<float>(deltaY) * mouseSensitivity;
94 pitchDegrees = std::clamp(pitchDegrees, -80.0f, 80.0f);
101 if (e.type == SDL_EVENT_MOUSE_WHEEL) {
102 const float delta = (e.wheel.y != 0.0f) ? e.wheel.y :
static_cast<float>(e.wheel.integer_y);
103 cameraDistance -= delta * 0.45f;
104 cameraDistance = std::clamp(cameraDistance, 1.8f, 12.0f);
111 backdrop->onSwapchainAboutToRecreate();
118 backdrop->onSwapchainRecreated(*
this);
124 backdrop->updateAndRender(*
this, cmd);
127 const float elapsedSeconds = std::chrono::duration<float>(std::chrono::steady_clock::now() - start).count();
129 const float aspect = (extent.height > 0U) ?
static_cast<float>(extent.width) /
static_cast<float>(extent.height) : 1.0f;
132 ubo.
model = glm::rotate(glm::mat4(1.0f), glm::radians(pitchDegrees), glm::vec3(1.0f, 0.0f, 0.0f));
133 ubo.
model = glm::rotate(ubo.
model, glm::radians(yawDegrees), glm::vec3(0.0f, 1.0f, 0.0f));
134 ubo.
model = glm::rotate(ubo.
model, glm::radians(-18.0f), glm::vec3(1.0f, 0.0f, 0.0f));
135 ubo.
model = glm::rotate(ubo.
model, elapsedSeconds * 0.45f, glm::vec3(0.0f, 1.0f, 0.0f));
136 ubo.
model = glm::scale(ubo.
model, glm::vec3(
model.modelRenderScale()));
138 ubo.
view = glm::lookAt(glm::vec3(0.0f, 0.15f, cameraDistance), glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
139 ubo.
proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 100.0f);
140 ubo.
proj[1][1] *= -1.0f;
142 model.updateUBO(imageIndex, ubo);
143 model.render(cmd, imageIndex,
false);
149 std::string assetRoot;
151 std::unique_ptr<MatrixRainBackdrop> backdrop{};
152 std::chrono::steady_clock::time_point start{std::chrono::steady_clock::now()};
153 bool mouseDragging =
false;
156 float yawDegrees = 0.0f;
157 float pitchDegrees = 0.0f;
158 float cameraDistance = 4.6f;
159 float mouseSensitivity = 0.35f;
167 if (binaryGlyphMode && fontPath.empty()) {
168 config.
font_path = assetRootPath +
"/data/binary.ttf";
170 config.
font_size = std::max(1, fontSize);
171 if (!fontPath.empty()) {
174 config.
color = color;
175 return std::make_unique<matrix::Rain>(window, std::move(config));
177 lastFrame = Clock::now();
186 rain->resize(window);
191 const auto now = Clock::now();
192 float dt = std::chrono::duration<float>(now - lastFrame).count();
194 dt = std::clamp(dt, 0.0f, 1.0f / 15.0f);
196 if (rain ==
nullptr) {
200 rain->resize(window);
203 rain->render(
static_cast<int>(extent.width),
static_cast<int>(extent.height));
204 if (rain->sprite() !=
nullptr) {
206 rain->sprite()->clearQueue();
211int main(
int argc,
char **argv) {
214 example::PlanetWindow window(args.
filename, args.
path,
"MXVK Planet Example", args.
width, args.
height, args.
fullscreen, args.
enable_vsync, args.
binary, args.
font_size, args.
font_path, args.
color);
217 std::cerr << std::format(
"mxvk: Exception: {}\n", e.
text());
220 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).