MXVK Vulkan Framework 0.35.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#include "mxvk/mxvk_png.hpp"
10
11#include <SDL3/SDL.h>
12
13#include <algorithm>
14#include <array>
15#include <cstdint>
16#include <cstdlib>
17#include <filesystem>
18#include <format>
19#include <iostream>
20#include <memory>
21#include <string>
22#include <vector>
23
24namespace {
25 class SurfaceDeleter {
26 public:
27 void operator()(SDL_Surface *surface) const { SDL_DestroySurface(surface); }
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_texture_array frame surface: {}", SDL_GetError()));
36 }
37 return surface;
38 }
39
40 struct Texture {
41 int width = 0;
42 int height = 0;
43 std::vector<mxvk::MXCOLOR> pixels;
44
45 [[nodiscard]] mxvk::MXCOLOR sample(float u, float v) const {
46 if (width <= 0 || height <= 0 || pixels.empty()) {
47 return mxvk::MXVK_RGB(255, 255, 255);
48 }
49
50 u = std::clamp(u, 0.0f, 1.0f);
51 v = std::clamp(v, 0.0f, 1.0f);
52 const int x = std::clamp(static_cast<int>(u * static_cast<float>(width - 1) + 0.5f), 0, width - 1);
53 const int y = std::clamp(static_cast<int>(v * static_cast<float>(height - 1) + 0.5f), 0, height - 1);
54 return pixels[static_cast<std::size_t>(y * width + x)];
55 }
56
57 [[nodiscard]] mxvk::MXCOLOR sample_nearest(float u, float v) const {
58 u = std::clamp(u, 0.0f, 1.0f);
59 v = std::clamp(v, 0.0f, 1.0f);
60 const int x = static_cast<int>(u * static_cast<float>(width - 1) + 0.5f);
61 const int y = static_cast<int>(v * static_cast<float>(height - 1) + 0.5f);
62 return pixels[static_cast<std::size_t>(y * width + x)];
63 }
64 };
65
66 struct TexVertex {
67 mxvk::vec4D position;
68 mxvk::vec2D uv;
69 float depth = 1.0f;
70 };
71
72 struct FaceDraw {
73 std::array<TexVertex, 4> vertices{};
74 float depth = 0.0f;
75 float intensity = 1.0f;
76 };
77
78 [[nodiscard]] std::string resolve_texture_path(const Arguments &args) {
79 std::string texture_path = !args.filename.empty() ? args.filename : args.texture;
80 if (texture_path.empty()) {
81 throw mxvk::Exception("3dmath_texture_array: pass a PNG with --filename <file.png> or --texture <file.png>");
82 }
83
84 namespace fs = std::filesystem;
85 fs::path requested(texture_path);
86 if (requested.is_absolute() || fs::exists(requested)) {
87 return requested.string();
88 }
89
90 if (!args.path.empty()) {
91 const fs::path from_asset_path = fs::path(args.path) / requested;
92 if (fs::exists(from_asset_path)) {
93 return from_asset_path.string();
94 }
95 }
96
97 return requested.string();
98 }
99
100 [[nodiscard]] Texture load_texture(const std::string &path) {
101 SurfacePtr loaded(mxvk::LoadPNG(path.c_str()));
102 if (!loaded) {
103 throw mxvk::Exception(std::format("3dmath_texture_array: failed to load PNG '{}'", path));
104 }
105
106 SurfacePtr rgba(SDL_ConvertSurface(loaded.get(), SDL_PIXELFORMAT_RGBA32));
107 if (!rgba) {
108 throw mxvk::Exception(std::format("3dmath_texture_array: failed to convert PNG '{}': {}", path, SDL_GetError()));
109 }
110
111 const SDL_PixelFormatDetails *format = SDL_GetPixelFormatDetails(rgba->format);
112 if (format == nullptr) {
113 throw mxvk::Exception(std::format("3dmath_texture_array: failed to query PNG format '{}': {}", path, SDL_GetError()));
114 }
115
116 Texture texture;
117 texture.width = rgba->w;
118 texture.height = rgba->h;
119 texture.pixels.resize(static_cast<std::size_t>(texture.width * texture.height));
120
121 for (int y = 0; y < texture.height; ++y) {
122 const auto *row = static_cast<const std::uint8_t *>(rgba->pixels) + (static_cast<std::size_t>(y) * static_cast<std::size_t>(rgba->pitch));
123 const auto *src = reinterpret_cast<const std::uint32_t *>(row);
124 for (int x = 0; x < texture.width; ++x) {
125 std::uint8_t r = 0;
126 std::uint8_t g = 0;
127 std::uint8_t b = 0;
128 std::uint8_t a = 0;
129 SDL_GetRGBA(src[x], format, nullptr, &r, &g, &b, &a);
130 texture.pixels[static_cast<std::size_t>(y * texture.width + x)] = (static_cast<mxvk::MXCOLOR>(a) << 24U) | (static_cast<mxvk::MXCOLOR>(r) << 16U) | (static_cast<mxvk::MXCOLOR>(g) << 8U) | static_cast<mxvk::MXCOLOR>(b);
131 }
132 }
133
134 return texture;
135 }
136} // namespace
137
138namespace example {
140 public:
141 Math3DTextureArrayWindow(const Arguments &args, const std::string &title) : mxvk::VK_Window(title, args.width, args.height, args.fullscreen, MXVK_VALIDATION, args.enable_vsync), texture(load_texture(resolve_texture_path(args))), frame_width(args.framebuffer.width), frame_height(args.framebuffer.height), fallback_width(args.width), fallback_height(args.height) {
142 setClearColor(0.012f, 0.015f, 0.022f, 1.0f);
144 }
145
146 void event(SDL_Event &e) override {
147 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
148 exit();
149 }
150 if (e.type == SDL_EVENT_MOUSE_WHEEL) {
151 const float delta = (e.wheel.y != 0.0f) ? e.wheel.y : static_cast<float>(e.wheel.integer_y);
152 camera_distance = std::clamp(camera_distance - delta * CAMERA_ZOOM_STEP, MIN_CAMERA_DISTANCE, MAX_CAMERA_DISTANCE);
153 }
154 }
155
156 void proc() override {
157 const int output_width = swapchain_extent.width > 0U ? static_cast<int>(swapchain_extent.width) : fallback_width;
158 const int output_height = swapchain_extent.height > 0U ? static_cast<int>(swapchain_extent.height) : fallback_height;
159
160 ensure_framebuffer();
161 if (frame_sprite == nullptr || frame_surface == nullptr || frame_format == nullptr) {
162 return;
163 }
164
165 const float time = static_cast<float>(SDL_GetTicks()) * 0.001f;
166 clear_frame(mxvk::MXVK_RGB(3, 4, 8));
167
168 const std::array<mxvk::vec4D, 8> cube_vertices = {
169 mxvk::vec4D{-CUBE_HALF_EXTENT, -CUBE_HALF_EXTENT, -CUBE_HALF_EXTENT, 1.0f},
170 mxvk::vec4D{CUBE_HALF_EXTENT, -CUBE_HALF_EXTENT, -CUBE_HALF_EXTENT, 1.0f},
171 mxvk::vec4D{CUBE_HALF_EXTENT, CUBE_HALF_EXTENT, -CUBE_HALF_EXTENT, 1.0f},
172 mxvk::vec4D{-CUBE_HALF_EXTENT, CUBE_HALF_EXTENT, -CUBE_HALF_EXTENT, 1.0f},
173 mxvk::vec4D{-CUBE_HALF_EXTENT, -CUBE_HALF_EXTENT, CUBE_HALF_EXTENT, 1.0f},
174 mxvk::vec4D{CUBE_HALF_EXTENT, -CUBE_HALF_EXTENT, CUBE_HALF_EXTENT, 1.0f},
175 mxvk::vec4D{CUBE_HALF_EXTENT, CUBE_HALF_EXTENT, CUBE_HALF_EXTENT, 1.0f},
176 mxvk::vec4D{-CUBE_HALF_EXTENT, CUBE_HALF_EXTENT, CUBE_HALF_EXTENT, 1.0f},
177 };
178
179 mxvk::Mat4D rotation;
180 rotation.BuildXYZ(time * 31.0f, time * 43.0f, time * 17.0f);
181
182 const std::array<std::array<int, 4>, 6> cube_faces = {{
183 {0, 3, 2, 1},
184 {4, 5, 6, 7},
185 {0, 4, 7, 3},
186 {1, 2, 6, 5},
187 {3, 7, 6, 2},
188 {0, 1, 5, 4},
189 }};
190
191 mxvk::vec3D light_dir(-0.35f, -0.55f, -1.0f);
192 light_dir.Normalize();
193
194 std::vector<FaceDraw> faces;
195 faces.reserve(GRID_CUBE_COUNT * cube_faces.size());
196
197 for (int grid_z = -GRID_RADIUS; grid_z <= GRID_RADIUS; ++grid_z) {
198 for (int grid_y = -GRID_RADIUS; grid_y <= GRID_RADIUS; ++grid_y) {
199 for (int grid_x = -GRID_RADIUS; grid_x <= GRID_RADIUS; ++grid_x) {
200 const mxvk::vec4D cube_center(static_cast<float>(grid_x) * GRID_SPACING, static_cast<float>(grid_y) * GRID_SPACING, static_cast<float>(grid_z) * GRID_SPACING, 0.0f);
201
202 std::array<mxvk::vec4D, 8> camera_vertices{};
203 std::array<mxvk::vec4D, 8> projected{};
204 for (std::size_t i = 0; i < cube_vertices.size(); ++i) {
205 mxvk::vec4D point = rotation.MulVec(cube_vertices[i] + cube_center);
206 point.z += camera_distance;
207 camera_vertices[i] = point;
208 projected[i] = project_to_screen(point, frame_width, frame_height);
209 }
210
211 for (const auto &indices : cube_faces) {
212 const auto index0 = static_cast<std::size_t>(indices[0]);
213 const auto index1 = static_cast<std::size_t>(indices[1]);
214 const auto index2 = static_cast<std::size_t>(indices[2]);
215 const auto index3 = static_cast<std::size_t>(indices[3]);
216 const mxvk::vec4D &a = camera_vertices[index0];
217 const mxvk::vec4D &b = camera_vertices[index1];
218 const mxvk::vec4D &c = camera_vertices[index2];
219 mxvk::vec4D normal = mxvk::vec4D().Build(a, b).CrossProduct(mxvk::vec4D().Build(a, c));
220 normal.Normalize();
221
222 const mxvk::vec4D center = (a + b + c + camera_vertices[index3]) * 0.25f;
223 const mxvk::vec4D view_vector(-center.x, -center.y, -center.z, 1.0f);
224 if (normal.DotProduct(view_vector) <= 0.0f) {
225 continue;
226 }
227
228 const float diffuse = std::max(0.0f, normal.DotProduct(mxvk::vec4D(light_dir.x, light_dir.y, light_dir.z, 1.0f)));
229 FaceDraw face;
230 face.vertices = {{
231 {projected[index0], {0.0f, 1.0f}, camera_vertices[index0].z},
232 {projected[index1], {1.0f, 1.0f}, camera_vertices[index1].z},
233 {projected[index2], {1.0f, 0.0f}, camera_vertices[index2].z},
234 {projected[index3], {0.0f, 0.0f}, camera_vertices[index3].z},
235 }};
236 face.depth = center.z;
237 face.intensity = std::clamp(0.35f + diffuse * 0.65f, 0.0f, 1.0f);
238 faces.push_back(face);
239 }
240 }
241 }
242 }
243
244 std::ranges::sort(faces, [](const FaceDraw &left, const FaceDraw &right) { return left.depth > right.depth; });
245
246 for (const FaceDraw &face : faces) {
247 draw_textured_triangle(face.vertices[0], face.vertices[1], face.vertices[2], face.intensity);
248 draw_textured_triangle(face.vertices[0], face.vertices[2], face.vertices[3], face.intensity);
249 }
250
251 frame_sprite->updateTexture(frame_surface->pixels, frame_width, frame_height, frame_surface->pitch);
252 frame_sprite->drawSpriteRect(0, 0, output_width, output_height);
253 }
254
255 private:
256 Texture texture;
257 SurfacePtr frame_surface;
258 const SDL_PixelFormatDetails *frame_format = nullptr;
259 mxvk::VK_Sprite *frame_sprite = nullptr;
260 int frame_width = 1280;
261 int frame_height = 720;
262 int fallback_width = 1280;
263 int fallback_height = 720;
264 float camera_distance = 8.5f;
265 static constexpr int GRID_RADIUS = 1;
266 static constexpr std::size_t GRID_WIDTH = static_cast<std::size_t>((GRID_RADIUS * 2) + 1);
267 static constexpr std::size_t GRID_CUBE_COUNT = GRID_WIDTH * GRID_WIDTH * GRID_WIDTH;
268 static constexpr float CUBE_HALF_EXTENT = 0.52f;
269 static constexpr float GRID_SPACING = 1.45f;
270 static constexpr float MIN_CAMERA_DISTANCE = 5.0f;
271 static constexpr float MAX_CAMERA_DISTANCE = 18.0f;
272 static constexpr float CAMERA_ZOOM_STEP = 0.65f;
273
274 void ensure_framebuffer() {
275 if (frame_surface != nullptr) {
276 return;
277 }
278
279 frame_surface = create_frame_surface(frame_width, frame_height);
280 frame_format = SDL_GetPixelFormatDetails(frame_surface->format);
281 if (frame_format == nullptr) {
282 throw mxvk::Exception(std::format("Failed to query 3dmath_texture_array frame format: {}", SDL_GetError()));
283 }
284
285 clear_frame(mxvk::MXVK_RGB(3, 4, 8));
286 frame_sprite = createSprite(frame_surface.get());
287 frame_sprite->setTextureFilter(VK_FILTER_NEAREST);
288 }
289
290 [[nodiscard]] std::uint32_t map_color(mxvk::MXCOLOR color) const { return SDL_MapRGBA(frame_format, nullptr, mxvk::color_r(color), mxvk::color_g(color), mxvk::color_b(color), mxvk::color_a(color)); }
291
292 void clear_frame(mxvk::MXCOLOR color) { SDL_FillSurfaceRect(frame_surface.get(), nullptr, map_color(color)); }
293
294 void put_shaded_pixel_unchecked(int x, int y, mxvk::MXCOLOR color, std::uint16_t intensity) {
295 auto *row = static_cast<std::uint8_t *>(frame_surface->pixels) + (static_cast<std::size_t>(y) * static_cast<std::size_t>(frame_surface->pitch));
296 auto *pixel = row + (static_cast<std::size_t>(x) * 4U);
297 pixel[0] = static_cast<std::uint8_t>((static_cast<std::uint16_t>(mxvk::color_r(color)) * intensity) >> 8U);
298 pixel[1] = static_cast<std::uint8_t>((static_cast<std::uint16_t>(mxvk::color_g(color)) * intensity) >> 8U);
299 pixel[2] = static_cast<std::uint8_t>((static_cast<std::uint16_t>(mxvk::color_b(color)) * intensity) >> 8U);
300 pixel[3] = mxvk::color_a(color);
301 }
302
303 void draw_textured_triangle(const TexVertex &a, const TexVertex &b, const TexVertex &c, float intensity) {
304 if (texture.width <= 0 || texture.height <= 0 || texture.pixels.empty()) {
305 return;
306 }
307
308 const mxvk::vec2D p0(a.position.x, a.position.y);
309 const mxvk::vec2D p1(b.position.x, b.position.y);
310 const mxvk::vec2D p2(c.position.x, c.position.y);
311 const float area = mxvk::edge_function(p0, p1, p2);
312 if (std::fabs(area) <= mxvk::EPSILON) {
313 return;
314 }
315 const bool positive_area = area > 0.0f;
316
317 const int min_x = std::max(0, static_cast<int>(std::floor(std::min({p0.x, p1.x, p2.x}))));
318 const int max_x = std::min(frame_width - 1, static_cast<int>(std::ceil(std::max({p0.x, p1.x, p2.x}))));
319 const int min_y = std::max(0, static_cast<int>(std::floor(std::min({p0.y, p1.y, p2.y}))));
320 const int max_y = std::min(frame_height - 1, static_cast<int>(std::ceil(std::max({p0.y, p1.y, p2.y}))));
321
322 if (min_x > max_x || min_y > max_y) {
323 return;
324 }
325
326 const float inv_area = 1.0f / area;
327 const float inv_z0 = 1.0f / std::max(a.depth, 0.001f);
328 const float inv_z1 = 1.0f / std::max(b.depth, 0.001f);
329 const float inv_z2 = 1.0f / std::max(c.depth, 0.001f);
330 const float u_over_z0 = a.uv.x * inv_z0;
331 const float u_over_z1 = b.uv.x * inv_z1;
332 const float u_over_z2 = c.uv.x * inv_z2;
333 const float v_over_z0 = a.uv.y * inv_z0;
334 const float v_over_z1 = b.uv.y * inv_z1;
335 const float v_over_z2 = c.uv.y * inv_z2;
336 const std::uint16_t fixed_intensity = static_cast<std::uint16_t>(std::clamp(intensity, 0.0f, 1.0f) * 256.0f);
337
338 const float w0_dx = p2.y - p1.y;
339 const float w0_dy = -(p2.x - p1.x);
340 const float w1_dx = p0.y - p2.y;
341 const float w1_dy = -(p0.x - p2.x);
342 const float w2_dx = p1.y - p0.y;
343 const float w2_dy = -(p1.x - p0.x);
344
345 const mxvk::vec2D row_start(static_cast<float>(min_x) + 0.5f, static_cast<float>(min_y) + 0.5f);
346 float row_w0 = mxvk::edge_function(p1, p2, row_start);
347 float row_w1 = mxvk::edge_function(p2, p0, row_start);
348 float row_w2 = mxvk::edge_function(p0, p1, row_start);
349 float row_inv_z = ((row_w0 * inv_z0) + (row_w1 * inv_z1) + (row_w2 * inv_z2)) * inv_area;
350 float row_u_over_z = ((row_w0 * u_over_z0) + (row_w1 * u_over_z1) + (row_w2 * u_over_z2)) * inv_area;
351 float row_v_over_z = ((row_w0 * v_over_z0) + (row_w1 * v_over_z1) + (row_w2 * v_over_z2)) * inv_area;
352
353 const float inv_z_dx = ((w0_dx * inv_z0) + (w1_dx * inv_z1) + (w2_dx * inv_z2)) * inv_area;
354 const float inv_z_dy = ((w0_dy * inv_z0) + (w1_dy * inv_z1) + (w2_dy * inv_z2)) * inv_area;
355 const float u_over_z_dx = ((w0_dx * u_over_z0) + (w1_dx * u_over_z1) + (w2_dx * u_over_z2)) * inv_area;
356 const float u_over_z_dy = ((w0_dy * u_over_z0) + (w1_dy * u_over_z1) + (w2_dy * u_over_z2)) * inv_area;
357 const float v_over_z_dx = ((w0_dx * v_over_z0) + (w1_dx * v_over_z1) + (w2_dx * v_over_z2)) * inv_area;
358 const float v_over_z_dy = ((w0_dy * v_over_z0) + (w1_dy * v_over_z1) + (w2_dy * v_over_z2)) * inv_area;
359
360 for (int y = min_y; y <= max_y; ++y) {
361 float w0 = row_w0;
362 float w1 = row_w1;
363 float w2 = row_w2;
364 float inv_z = row_inv_z;
365 float u_over_z = row_u_over_z;
366 float v_over_z = row_v_over_z;
367
368 for (int x = min_x; x <= max_x; ++x) {
369 if ((positive_area && w0 >= 0.0f && w1 >= 0.0f && w2 >= 0.0f) || (!positive_area && w0 <= 0.0f && w1 <= 0.0f && w2 <= 0.0f)) {
370 if (std::fabs(inv_z) > mxvk::EPSILON) {
371 const float reciprocal_z = 1.0f / inv_z;
372 const float u = u_over_z * reciprocal_z;
373 const float v = v_over_z * reciprocal_z;
374 put_shaded_pixel_unchecked(x, y, texture.sample_nearest(u, v), fixed_intensity);
375 }
376 }
377
378 w0 += w0_dx;
379 w1 += w1_dx;
380 w2 += w2_dx;
381 inv_z += inv_z_dx;
382 u_over_z += u_over_z_dx;
383 v_over_z += v_over_z_dx;
384 }
385
386 row_w0 += w0_dy;
387 row_w1 += w1_dy;
388 row_w2 += w2_dy;
389 row_inv_z += inv_z_dy;
390 row_u_over_z += u_over_z_dy;
391 row_v_over_z += v_over_z_dy;
392 }
393 }
394
395 static mxvk::vec4D project_to_screen(const mxvk::vec4D &point, int width, int height) {
396 const float scale = static_cast<float>(std::min(width, height)) * 0.52f;
397 const float center_x = static_cast<float>(width) * 0.5f;
398 const float center_y = static_cast<float>(height) * 0.5f;
399 const float z = std::max(point.z, 0.001f);
400 return {center_x + (point.x / z) * scale, center_y - (point.y / z) * scale, point.z, 1.0f};
401 }
402 };
403} // namespace example
404
405int main(int argc, char **argv) {
406 try {
407 Arguments args = proc_args(argc, argv);
408 example::Math3DTextureArrayWindow window(args, "MXVK 3D Math Texture Array");
409 window.loop();
410 } catch (mxvk::Exception &e) {
411 std::cerr << std::format("mxvk: Exception: {}\n", e.text());
412 return EXIT_FAILURE;
413 } catch (ArgException<std::string> &e) {
414 std::cerr << std::format("mxvk: Argument Exception: {}\n", e.text());
415 return EXIT_FAILURE;
416 }
417 return EXIT_SUCCESS;
418}
constexpr int GRID_WIDTH
Definition acid.drop.cpp:25
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:854
Exception thrown by Argz::proc() on unrecognised or malformed options.
Definition argz.hpp:169
void operator()(SDL_Surface *surface) const
Definition main.cpp:27
void event(SDL_Event &e) override
Handle one SDL event.
Definition main.cpp:146
void proc() override
Execute one processing/update step.
Definition main.cpp:156
Math3DTextureArrayWindow(const Arguments &args, const std::string &title)
Definition main.cpp:141
std::string text() const
Four-by-four homogeneous transform matrix.
Definition mxvk_math.h:706
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:859
vec4D MulVec(const vec4D &in) const
Transform a homogeneous 4D vector by this matrix.
Definition mxvk_math.h:771
Main Vulkan window wrapper for MXVK.
Definition mxvk.hpp:39
void loop()
Run the main event/render loop.
Definition mxvk.cpp:651
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:4455
VkExtent2D swapchain_extent
Definition mxvk.hpp:616
void setClearColor(float r, float g, float b, float a=1.0f)
Set the per-frame color attachment clear color.
Definition mxvk.cpp:637
void exit()
Request loop termination.
Definition mxvk.cpp:1378
VK_Window()=default
Construct an empty window object.
Two-dimensional float vector with common arithmetic helpers.
Definition mxvk_math.h:158
Three-dimensional float vector with arithmetic, dot, and cross-product helpers.
Definition mxvk_math.h:256
float z
Z coordinate.
Definition mxvk_math.h:265
float x
X coordinate.
Definition mxvk_math.h:259
void Normalize()
Normalize this vector in place, or reset it to zero if it is too short.
Definition mxvk_math.h:327
float y
Y coordinate.
Definition mxvk_math.h:262
Four-dimensional float vector used for homogeneous 3D coordinates.
Definition mxvk_math.h:363
float y
Y coordinate.
Definition mxvk_math.h:369
float x
X coordinate.
Definition mxvk_math.h:366
void Normalize()
Normalize the 3D components in place and reset W to 1.
Definition mxvk_math.h:447
constexpr float DotProduct(const vec4D &v) const
Compute the 3D dot product, ignoring the W component.
Definition mxvk_math.h:438
float z
Z coordinate.
Definition mxvk_math.h:372
void Build(const vec4D &to)
Replace this vector with the direction from this point to to.
Definition mxvk_math.h:473
#define MXVK_VALIDATION
Definition mxvk.hpp:28
Math, geometry, rasterization, and simple software 3D pipeline helpers for MXVK examples.
PNG image loading and saving utilities via SDL3.
std::unique_ptr< SDL_Surface, SurfaceDeleter > SurfacePtr
Definition main.cpp:27
Texture load_texture(const std::string &filename, const std::string &asset_path, bool generate_mipmaps)
Definition main.cpp:135
int main()
Definition main.py:165
Utilities for loading and saving PNG images.
Definition mxvk.hpp:31
constexpr std::uint8_t color_r(MXCOLOR color)
Extract the red component from a packed ARGB color.
Definition mxvk_math.h:52
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:98
SDL_Surface * LoadPNG(const char *file)
Load a PNG file into an SDL_Surface.
Definition mxvk_png.cpp:89
constexpr std::uint8_t color_g(MXCOLOR color)
Extract the green component from a packed ARGB color.
Definition mxvk_math.h:55
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:61
float edge_function(const vec2D &a, const vec2D &b, const vec2D &p)
Compute the signed edge function value for point p relative to edge a-b.
Definition mxvk_math.h:1972
constexpr float EPSILON
Default tolerance used for floating-point singularity and zero-length checks.
Definition mxvk_math.h:37
constexpr std::uint8_t color_b(MXCOLOR color)
Extract the blue component from a packed ARGB color.
Definition mxvk_math.h:58
Plain data structure returned by proc_args() with all common libmx2 CLI options.
Definition argz.hpp:718
std::string texture
Optional texture file path (--texture).
Definition argz.hpp:752
std::string filename
Optional input filename (--filename).
Definition argz.hpp:726
std::string path
Asset root; proc_args() defaults it to the executable directory.
Definition argz.hpp:723
std::array< TexVertex, 4 > vertices
Definition main.cpp:73
mxvk::MXCOLOR sample(float u, float v) const
Definition main.cpp:45
std::vector< mxvk::MXCOLOR > pixels
Definition main.cpp:191
mxvk::MXCOLOR sample_nearest(float u, float v) const
Definition main.cpp:57