MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1#include "mxvk/argz.hpp"
2#include "mxvk/mxvk.hpp"
4#if defined(MXVK_USE_EIGEN_MATH)
6#else
7#include "mxvk/mxvk_math.h"
8#endif
9
10#include <SDL3/SDL.h>
11
12#include <algorithm>
13#include <array>
14#include <cstdint>
15#include <cstdlib>
16#include <format>
17#include <iostream>
18#include <memory>
19#include <string>
20#include <vector>
21
22namespace {
23 class SurfaceDeleter {
24 public:
25 void operator()(SDL_Surface *surface) const {
26 SDL_DestroySurface(surface);
27 }
28 };
29
30 using SurfacePtr = std::unique_ptr<SDL_Surface, SurfaceDeleter>;
31
32 SurfacePtr create_frame_surface(int width, int height) {
33 SurfacePtr surface(SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGBA32));
34 if (!surface) {
35 throw mxvk::Exception(std::format("Failed to create 3dmath_cube frame surface: {}", SDL_GetError()));
36 }
37 return surface;
38 }
39
40 struct FaceDraw {
41 std::array<int, 4> indices{};
42 float depth = 0.0f;
44 };
45
46} // namespace
47
48namespace example {
50 public:
51 Math3DCubeWindow(const std::string &, const std::string &title, int width, int height, bool fullscreen, bool enable_vsync, const FramebufferDimensions &framebuffer)
52 : mxvk::VK_Window(title, width, height, fullscreen, MXVK_VALIDATION, enable_vsync),
53 frame_width(framebuffer.width),
54 frame_height(framebuffer.height),
55 fallback_width(width),
56 fallback_height(height) {
57 setClearColor(0.012f, 0.015f, 0.022f, 1.0f);
59 }
60
61 void event(SDL_Event &e) override {
62 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
63 exit();
64 }
65 }
66
67 void proc() override {
68 const int output_width = swapchain_extent.width > 0U ? static_cast<int>(swapchain_extent.width) : fallback_width;
69 const int output_height = swapchain_extent.height > 0U ? static_cast<int>(swapchain_extent.height) : fallback_height;
70
71 ensure_framebuffer();
72 if (frame_sprite == nullptr || frame_surface == nullptr || frame_format == nullptr) {
73 return;
74 }
75
76 const float time = static_cast<float>(SDL_GetTicks()) * 0.001f;
77 clear_frame(mxvk::MXVK_RGB(3, 4, 8));
78
79 const std::array<mxvk::vec4D, 8> cube_vertices = {
80 mxvk::vec4D{-1.0f, -1.0f, -1.0f, 1.0f},
81 mxvk::vec4D{1.0f, -1.0f, -1.0f, 1.0f},
82 mxvk::vec4D{1.0f, 1.0f, -1.0f, 1.0f},
83 mxvk::vec4D{-1.0f, 1.0f, -1.0f, 1.0f},
84 mxvk::vec4D{-1.0f, -1.0f, 1.0f, 1.0f},
85 mxvk::vec4D{1.0f, -1.0f, 1.0f, 1.0f},
86 mxvk::vec4D{1.0f, 1.0f, 1.0f, 1.0f},
87 mxvk::vec4D{-1.0f, 1.0f, 1.0f, 1.0f},
88 };
89
90 mxvk::Mat4D rotation;
91 rotation.BuildXYZ(time * 31.0f, time * 43.0f, time * 17.0f);
92
93 std::array<mxvk::vec4D, 8> camera_vertices{};
94 std::array<mxvk::vec4D, 8> projected{};
95 for (std::size_t i = 0; i < cube_vertices.size(); ++i) {
96 mxvk::vec4D point = rotation.MulVec(cube_vertices[i]);
97 point.z += 4.25f;
98 camera_vertices[i] = point;
99 projected[i] = project_to_screen(point, frame_width, frame_height);
100 }
101
102 const std::array<std::array<int, 4>, 6> cube_faces = {{
103 {0, 3, 2, 1},
104 {4, 5, 6, 7},
105 {0, 4, 7, 3},
106 {1, 2, 6, 5},
107 {3, 7, 6, 2},
108 {0, 1, 5, 4},
109 }};
110
111 mxvk::vec3D light_dir(-0.35f, -0.55f, -1.0f);
112 light_dir.Normalize();
113
114 std::vector<FaceDraw> faces;
115 faces.reserve(cube_faces.size());
116 for (const auto &indices : cube_faces) {
117 const mxvk::vec4D &a = camera_vertices[static_cast<std::size_t>(indices[0])];
118 const mxvk::vec4D &b = camera_vertices[static_cast<std::size_t>(indices[1])];
119 const mxvk::vec4D &c = camera_vertices[static_cast<std::size_t>(indices[2])];
120 mxvk::vec4D normal = mxvk::vec4D().Build(a, b).CrossProduct(mxvk::vec4D().Build(a, c));
121 normal.Normalize();
122
123 const mxvk::vec4D center = (a + b + c + camera_vertices[static_cast<std::size_t>(indices[3])]) * 0.25f;
124 const mxvk::vec4D view_vector(-center.x, -center.y, -center.z, 1.0f);
125 if (normal.DotProduct(view_vector) <= 0.0f) {
126 continue;
127 }
128
129 const float diffuse = std::max(0.0f, normal.DotProduct(mxvk::vec4D(light_dir.x, light_dir.y, light_dir.z, 1.0f)));
130 const float intensity = std::clamp(0.25f + diffuse * 0.75f, 0.0f, 1.0f);
131 const mxvk::MXCOLOR color = mxvk::shade_color(mxvk::MXVK_RGB(76, 164, 255), intensity);
132 faces.push_back({indices, center.z, color});
133 }
134
135 std::ranges::sort(faces, [](const FaceDraw &left, const FaceDraw &right) {
136 return left.depth > right.depth;
137 });
138
139 for (const FaceDraw &face : faces) {
140 mxvk::PipeLine fill_pipeline;
141 fill_pipeline.Begin(frame_width, frame_height, [this](int x, int y, mxvk::MXCOLOR color) { put_pixel(x, y, color); });
142 const mxvk::vec4D &a = projected[static_cast<std::size_t>(face.indices[0])];
143 const mxvk::vec4D &b = projected[static_cast<std::size_t>(face.indices[1])];
144 const mxvk::vec4D &c = projected[static_cast<std::size_t>(face.indices[2])];
145 const mxvk::vec4D &d = projected[static_cast<std::size_t>(face.indices[3])];
146 fill_pipeline.DrawFilledTriangle(a, b, c, face.color);
147 fill_pipeline.DrawFilledTriangle(a, c, d, face.color);
148 fill_pipeline.End();
149 }
150
151 const int outline_size = std::max(1, frame_width / 640);
152 mxvk::PipeLine outline_pipeline;
153 outline_pipeline.Begin(frame_width, frame_height, [this, outline_size](int x, int y, mxvk::MXCOLOR color) { put_block(x, y, outline_size, color); });
154 for (const FaceDraw &face : faces) {
155 for (int i = 0; i < 4; ++i) {
156 const mxvk::vec4D &a = projected[static_cast<std::size_t>(face.indices[static_cast<std::size_t>(i)])];
157 const mxvk::vec4D &b = projected[static_cast<std::size_t>(face.indices[static_cast<std::size_t>((i + 1) % 4)])];
158 outline_pipeline.DrawClipedLine(static_cast<int>(a.x), static_cast<int>(a.y), static_cast<int>(b.x), static_cast<int>(b.y), mxvk::MXVK_RGB(235, 245, 255));
159 }
160 }
161 outline_pipeline.End();
162
163 frame_sprite->updateTexture(frame_surface.get());
164 frame_sprite->drawSpriteRect(0, 0, output_width, output_height);
165 }
166
167 private:
168 SurfacePtr frame_surface;
169 const SDL_PixelFormatDetails *frame_format = nullptr;
170 mxvk::VK_Sprite *frame_sprite = nullptr;
171 int frame_width = 1280;
172 int frame_height = 720;
173 int fallback_width = 1280;
174 int fallback_height = 720;
175
176 void ensure_framebuffer() {
177 if (frame_surface != nullptr) {
178 return;
179 }
180
181 frame_surface = create_frame_surface(frame_width, frame_height);
182 frame_format = SDL_GetPixelFormatDetails(frame_surface->format);
183 if (frame_format == nullptr) {
184 throw mxvk::Exception(std::format("Failed to query 3dmath_cube frame format: {}", SDL_GetError()));
185 }
186
187 clear_frame(mxvk::MXVK_RGB(3, 4, 8));
188 frame_sprite = createSprite(frame_surface.get());
189 frame_sprite->setTextureFilter(VK_FILTER_NEAREST);
190 }
191
192 [[nodiscard]] std::uint32_t map_color(mxvk::MXCOLOR color) const {
193 return SDL_MapRGBA(frame_format, nullptr, mxvk::color_r(color), mxvk::color_g(color), mxvk::color_b(color), mxvk::color_a(color));
194 }
195
196 void clear_frame(mxvk::MXCOLOR color) {
197 SDL_FillSurfaceRect(frame_surface.get(), nullptr, map_color(color));
198 }
199
200 void put_pixel(int x, int y, mxvk::MXCOLOR color) {
201 if (x < 0 || y < 0 || x >= frame_width || y >= frame_height) {
202 return;
203 }
204 auto *row = static_cast<std::uint8_t *>(frame_surface->pixels) + (static_cast<std::size_t>(y) * static_cast<std::size_t>(frame_surface->pitch));
205 auto *pixel = reinterpret_cast<std::uint32_t *>(row) + x;
206 *pixel = map_color(color);
207 }
208
209 void put_block(int x, int y, int size, mxvk::MXCOLOR color) {
210 for (int by = 0; by < size; ++by) {
211 for (int bx = 0; bx < size; ++bx) {
212 put_pixel(x + bx, y + by, color);
213 }
214 }
215 }
216
217 static mxvk::vec4D project_to_screen(const mxvk::vec4D &point, int width, int height) {
218 const float scale = static_cast<float>(std::min(width, height)) * 0.52f;
219 const float center_x = static_cast<float>(width) * 0.5f;
220 const float center_y = static_cast<float>(height) * 0.5f;
221 const float z = std::max(point.z, 0.001f);
222 return {center_x + (point.x / z) * scale, center_y - (point.y / z) * scale, point.z, 1.0f};
223 }
224 };
225} // namespace example
226
227int main(int argc, char **argv) {
228 try {
229 Arguments args = proc_args(argc, argv);
230 example::Math3DCubeWindow window(args.path, "MXVK 3D Math Cube", args.width, args.height, args.fullscreen, args.enable_vsync, args.framebuffer);
231 window.loop();
232 } catch (mxvk::Exception &e) {
233 std::cerr << std::format("mxvk: Exception: {}\n", e.text());
234 return EXIT_FAILURE;
235 } catch (ArgException<std::string> &e) {
236 std::cerr << std::format("mxvk: Argument Exception: {}\n", e.text());
237 return EXIT_FAILURE;
238 }
239 return EXIT_SUCCESS;
240}
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 operator()(SDL_Surface *surface) const
Definition main.cpp:25
Math3DCubeWindow(const std::string &, const std::string &title, int width, int height, bool fullscreen, bool enable_vsync, const FramebufferDimensions &framebuffer)
Definition main.cpp:51
void proc() override
Execute one processing/update step.
Definition main.cpp:67
void event(SDL_Event &e) override
Handle one SDL event.
Definition main.cpp:61
std::string text() const
Four-by-four homogeneous transform matrix.
Definition mxvk_math.h:812
void BuildXYZ(float theta_x, float theta_y, float theta_z)
Build an XYZ Euler rotation matrix from angles in degrees.
Definition mxvk_math.h:973
vec4D MulVec(const vec4D &in) const
Transform a homogeneous 4D vector by this matrix.
Definition mxvk_math.h:881
Clipped software rasterization pipeline for lines and filled triangles.
Definition mxvk_math.h:2273
void End()
End rendering and release the current plotting callbacks.
Definition mxvk_math.h:2671
void DrawFilledTriangle(const vec2D &p0, const vec2D &p1, const vec2D &p2, MXCOLOR color) const
Draw a clipped filled triangle from 2D screen-space vertices.
Definition mxvk_math.h:2572
void DrawClipedLine(int x0, int y0, int x1, int y1, MXCOLOR color) const
Draw a clipped line. Kept with the original misspelled name for compatibility.
Definition mxvk_math.h:2435
void Begin(int width, int height, std::function< void(int, int, MXCOLOR)> plotter)
Begin rendering with a custom pixel plotter.
Definition mxvk_math.h:2318
Main Vulkan window wrapper for MXVK.
Definition mxvk.hpp:37
void loop()
Run the main event/render loop.
Definition mxvk.cpp:600
VK_Sprite * createSprite(const std::string &pngPath, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Create a sprite from a PNG file and register it with this window.
Definition mxvk.cpp:3477
VkExtent2D swapchain_extent
Definition mxvk.hpp:495
void setClearColor(float r, float g, float b, float a=1.0f)
Set the per-frame color attachment clear color.
Definition mxvk.cpp:593
void exit()
Request loop termination.
Definition mxvk.cpp:1126
VK_Window()=default
Construct an empty window object.
Three-dimensional float vector with arithmetic, dot, and cross-product helpers.
Definition mxvk_math.h:291
float z
Z coordinate.
Definition mxvk_math.h:300
float x
X coordinate.
Definition mxvk_math.h:294
void Normalize()
Normalize this vector in place, or reset it to zero if it is too short.
Definition mxvk_math.h:376
float y
Y coordinate.
Definition mxvk_math.h:297
Four-dimensional float vector used for homogeneous 3D coordinates.
Definition mxvk_math.h:416
float y
Y coordinate.
Definition mxvk_math.h:422
float x
X coordinate.
Definition mxvk_math.h:419
float z
Z coordinate.
Definition mxvk_math.h:425
void Build(const vec4D &to)
Replace this vector with the direction from this point to to.
Definition mxvk_math.h:544
int main(void)
Definition main.cpp:7
#define MXVK_VALIDATION
Definition mxvk.hpp:27
Math, geometry, rasterization, and simple software 3D pipeline helpers for MXVK examples.
SurfacePtr create_frame_surface(int width, int height)
Definition main.cpp:31
Utilities for loading and saving PNG images.
Definition mxvk.hpp:30
constexpr std::uint8_t color_r(MXCOLOR color)
Extract the red component from a packed ARGB color.
Definition mxvk_math.h:54
std::uint32_t MXCOLOR
Packed 32-bit color in ARGB byte order.
Definition mxvk_math.h:40
void BuildTables()
Rebuild the sine and cosine lookup tables.
Definition mxvk_math.h:113
MXCOLOR shade_color(MXCOLOR color, float intensity)
Scale the RGB channels of a color while preserving alpha.
Definition mxvk_math.h:79
constexpr std::uint8_t color_g(MXCOLOR color)
Extract the green component from a packed ARGB color.
Definition mxvk_math.h:59
constexpr MXCOLOR MXVK_RGB(int r, int g, int b)
Build an opaque ARGB color from red, green, and blue components.
Definition mxvk_math.h:49
constexpr std::uint8_t color_a(MXCOLOR color)
Extract the alpha component from a packed ARGB color.
Definition mxvk_math.h:69
constexpr std::uint8_t color_b(MXCOLOR color)
Extract the blue component from a packed ARGB color.
Definition mxvk_math.h:64
Plain data structure returned by proc_args() with all common libmx2 CLI options.
Definition argz.hpp:730
FramebufferDimensions framebuffer
Software framebuffer size requested by --framebuffer.
Definition argz.hpp:758
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 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
Parsed software framebuffer dimensions.
Definition argz.hpp:721