9#include <glm/ext/matrix_clip_space.hpp>
10#include <glm/ext/matrix_transform.hpp>
19 GlitchCubeWindow(
const std::string &filename,
const std::string &path,
const std::string &title,
int width,
int height,
bool fullscreen,
bool enable_vsync) :
mxvk::
VK_Window(title, width, height, fullscreen,
MXVK_VALIDATION, enable_vsync), assetRoot((path.empty() || path ==
".") ? std::string(GLITCH_CUBE_ASSET_DIR) : path) {
20 const std::string shaderRoot = assetRoot +
"/data";
21 const std::string modelPath = filename.empty() ? (assetRoot +
"/data/cube.mxmod.z") : filename;
22 const std::string textureManifestPath = assetRoot +
"/data/cube.tex";
23 const std::string textureBasePath = assetRoot +
"/data";
24 const std::string modelVertPath = shaderRoot +
"/model.vert.spv";
25 const std::string modelFragPath = shaderRoot +
"/model.frag.spv";
26 setFont(assetRoot +
"/data/font.ttf", 22);
27 model.load(
this, modelPath, textureManifestPath, textureBasePath, 1.0f);
28 model.setShaders(
this, modelVertPath, modelFragPath);
32 if (
device != VK_NULL_HANDLE) {
38 void event(SDL_Event &e)
override {
39 if (e.type == SDL_EVENT_KEY_DOWN) {
45 rotXFactor = (rotXFactor > 0.0f) ? 0.0f : 1.0f;
49 if (cubeScale > 4.0f) {
55 if (cubeScale < 0.2f) {
65 if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN && e.button.button == SDL_BUTTON_LEFT) {
67 lastMouseX =
static_cast<int>(e.button.x);
68 lastMouseY =
static_cast<int>(e.button.y);
72 if (e.type == SDL_EVENT_MOUSE_BUTTON_UP && e.button.button == SDL_BUTTON_LEFT) {
73 mouseDragging =
false;
77 if (e.type == SDL_EVENT_MOUSE_MOTION && mouseDragging) {
78 const int x =
static_cast<int>(e.motion.x);
79 const int y =
static_cast<int>(e.motion.y);
80 const int deltaX = x - lastMouseX;
81 const int deltaY = y - lastMouseY;
83 yawDegrees +=
static_cast<float>(deltaX) * mouseSensitivity;
84 pitchDegrees +=
static_cast<float>(deltaY) * mouseSensitivity;
85 pitchDegrees = std::clamp(pitchDegrees, -80.0f, 80.0f);
92 if (e.type == SDL_EVENT_MOUSE_WHEEL) {
93 const float delta = (e.wheel.y != 0.0f) ? e.wheel.y :
static_cast<float>(e.wheel.integer_y);
94 cameraDistance -= delta * 0.45f;
95 cameraDistance = std::clamp(cameraDistance, 1.8f, 12.0f);
100 printText(
"Drag left mouse to orbit the cube", 25, 25, {255, 255, 255, 255});
101 printText(
"Mouse wheel zooms the camera", 25, 60, {255, 255, 255, 255});
102 printText(
"Space toggles the rotation axis", 25, 95, {255, 255, 255, 255});
103 printText(
"Page Up / Page Down adjust cube scale", 25, 130, {255, 255, 255, 255});
104 printText(std::format(
"Current scale: {:.1f}", cubeScale), 25, 165, {230, 245, 255, 255});
110 const float elapsedSeconds = std::chrono::duration<float>(std::chrono::steady_clock::now() - start).count();
113 const float aspect = (extent.height > 0U) ?
static_cast<float>(extent.width) /
static_cast<float>(extent.height) : 1.0f;
115 const float rotationAngle = elapsedSeconds * glm::radians(50.0f);
118 ubo.
model = glm::rotate(glm::mat4(1.0f), rotationAngle, glm::vec3(rotXFactor, 1.0f, 0.0f));
119 ubo.
model = glm::scale(ubo.
model, glm::vec3(
model.modelRenderScale() * cubeScale));
122 const float yawRadians = glm::radians(yawDegrees);
123 const float pitchRadians = glm::radians(pitchDegrees);
124 const glm::vec3 cameraPos{cameraDistance * std::cos(pitchRadians) * std::sin(yawRadians), cameraDistance * std::sin(pitchRadians), cameraDistance * std::cos(pitchRadians) * std::cos(yawRadians)};
126 ubo.
view = glm::lookAt(cameraPos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
127 ubo.
proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 100.0f);
128 ubo.
proj[1][1] *= -1.0f;
130 model.updateUBO(imageIndex, ubo);
131 model.render(cmd, imageIndex,
false);
135 std::string assetRoot;
137 float rotXFactor = 1.0f;
138 float cubeScale = 1.0f;
139 std::chrono::steady_clock::time_point start{std::chrono::steady_clock::now()};
140 bool mouseDragging =
false;
143 float yawDegrees = 0.0f;
144 float pitchDegrees = 12.0f;
145 float cameraDistance = 5.0f;
146 float mouseSensitivity = 0.35f;
151int main(
int argc,
char **argv) {
157 std::cerr << std::format(
"mxvk: Exception: {}\n", e.
text());
160 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 event(SDL_Event &e) override
Handle one SDL event.
GlitchCubeWindow(const std::string &filename, const std::string &path, const std::string &title, int width, int height, bool fullscreen, bool enable_vsync)
~GlitchCubeWindow() override
void onSwapchainRecreated() override
Called after swapchain and render resources are recreated.
void proc() override
Execute one processing/update step.
void onRecordCustomRendering(VkCommandBuffer cmd, uint32_t imageIndex) override
Optional hook for derived classes to record extra draw commands.
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 exit()
Request loop termination.
VK_Window()=default
Construct an empty window object.
void setFont(const std::string &fontPath, int fontSize=24)
Set the active text-render font.
void printText(const std::string &text, int x, int y, const SDL_Color &col)
Queue a text string for rendering during the current frame.
High-level model wrapper integrated with MXVK dynamic rendering.
Utilities for loading and saving PNG images.
Plain data structure returned by proc_args() with all common libmx2 CLI options.
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 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).