4#if defined(MXVK_USE_EIGEN_MATH)
23 class SurfaceDeleter {
30 using SurfacePtr = std::unique_ptr<SDL_Surface, SurfaceDeleter>;
33 SurfacePtr
surface(SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGBA32));
35 throw mxvk::Exception(std::format(
"Failed to create 3dmath_cube frame surface: {}", SDL_GetError()));
53 frame_width(framebuffer.width),
54 frame_height(framebuffer.height),
55 fallback_width(width),
56 fallback_height(height) {
61 void event(SDL_Event &e)
override {
62 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
72 if (frame_sprite ==
nullptr || frame_surface ==
nullptr || frame_format ==
nullptr) {
76 const float time =
static_cast<float>(SDL_GetTicks()) * 0.001f;
79 const std::array<mxvk::vec4D, 8> cube_vertices = {
91 rotation.
BuildXYZ(time * 31.0f, time * 43.0f, time * 17.0f);
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) {
98 camera_vertices[i] = point;
99 projected[i] = project_to_screen(point, frame_width, frame_height);
102 const std::array<std::array<int, 4>, 6> cube_faces = {{
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])];
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) {
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);
132 faces.push_back({indices, center.
z, color});
135 std::ranges::sort(faces, [](
const FaceDraw &left,
const FaceDraw &right) {
136 return left.depth > right.depth;
139 for (
const FaceDraw &face : faces) {
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])];
151 const int outline_size = std::max(1, frame_width / 640);
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));
161 outline_pipeline.
End();
163 frame_sprite->updateTexture(frame_surface.get());
164 frame_sprite->drawSpriteRect(0, 0, output_width, output_height);
168 SurfacePtr frame_surface;
169 const SDL_PixelFormatDetails *frame_format =
nullptr;
171 int frame_width = 1280;
172 int frame_height = 720;
173 int fallback_width = 1280;
174 int fallback_height = 720;
176 void ensure_framebuffer() {
177 if (frame_surface !=
nullptr) {
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()));
189 frame_sprite->setTextureFilter(VK_FILTER_NEAREST);
192 [[nodiscard]] std::uint32_t map_color(
mxvk::MXCOLOR color)
const {
197 SDL_FillSurfaceRect(frame_surface.get(),
nullptr, map_color(color));
201 if (x < 0 || y < 0 || x >= frame_width || y >= frame_height) {
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);
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);
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};
227int main(
int argc,
char **argv) {
233 std::cerr << std::format(
"mxvk: Exception: {}\n", e.
text());
236 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 operator()(SDL_Surface *surface) const
Math3DCubeWindow(const std::string &, const std::string &title, int width, int height, bool fullscreen, bool enable_vsync, const FramebufferDimensions &framebuffer)
void proc() override
Execute one processing/update step.
void event(SDL_Event &e) override
Handle one SDL event.
Four-by-four homogeneous transform matrix.
void BuildXYZ(float theta_x, float theta_y, float theta_z)
Build an XYZ Euler rotation matrix from angles in degrees.
vec4D MulVec(const vec4D &in) const
Transform a homogeneous 4D vector by this matrix.
Clipped software rasterization pipeline for lines and filled triangles.
void End()
End rendering and release the current plotting callbacks.
void DrawFilledTriangle(const vec2D &p0, const vec2D &p1, const vec2D &p2, MXCOLOR color) const
Draw a clipped filled triangle from 2D screen-space vertices.
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.
void Begin(int width, int height, std::function< void(int, int, MXCOLOR)> plotter)
Begin rendering with a custom pixel plotter.
Main Vulkan window wrapper for MXVK.
void loop()
Run the main event/render loop.
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.
VkExtent2D swapchain_extent
void setClearColor(float r, float g, float b, float a=1.0f)
Set the per-frame color attachment clear color.
void exit()
Request loop termination.
VK_Window()=default
Construct an empty window object.
Three-dimensional float vector with arithmetic, dot, and cross-product helpers.
void Normalize()
Normalize this vector in place, or reset it to zero if it is too short.
Four-dimensional float vector used for homogeneous 3D coordinates.
void Build(const vec4D &to)
Replace this vector with the direction from this point to to.
Math, geometry, rasterization, and simple software 3D pipeline helpers for MXVK examples.
SurfacePtr create_frame_surface(int width, int height)
Utilities for loading and saving PNG images.
constexpr std::uint8_t color_r(MXCOLOR color)
Extract the red component from a packed ARGB color.
std::uint32_t MXCOLOR
Packed 32-bit color in ARGB byte order.
void BuildTables()
Rebuild the sine and cosine lookup tables.
MXCOLOR shade_color(MXCOLOR color, float intensity)
Scale the RGB channels of a color while preserving alpha.
constexpr std::uint8_t color_g(MXCOLOR color)
Extract the green component from a packed ARGB color.
constexpr MXCOLOR MXVK_RGB(int r, int g, int b)
Build an opaque ARGB color from red, green, and blue components.
constexpr std::uint8_t color_a(MXCOLOR color)
Extract the alpha component from a packed ARGB color.
constexpr std::uint8_t color_b(MXCOLOR color)
Extract the blue component from a packed ARGB color.
Plain data structure returned by proc_args() with all common libmx2 CLI options.
FramebufferDimensions framebuffer
Software framebuffer size requested by --framebuffer.
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 path
Asset root; proc_args() defaults it to the executable directory.
int width
Viewport width in pixels (default: 1280).
Parsed software framebuffer dimensions.
std::array< int, 4 > indices