22#include <unordered_map>
34 inline constexpr float PI = 3.14159265358979323846f;
49 [[nodiscard]]
inline constexpr MXCOLOR MXVK_RGB(
int r,
int g,
int b) {
return 0xFF000000u | ((
static_cast<MXCOLOR>(r) & 0xFFu) << 16u) | ((
static_cast<MXCOLOR>(g) & 0xFFu) << 8u) | (
static_cast<MXCOLOR>(b) & 0xFFu); }
52 [[nodiscard]]
inline constexpr std::uint8_t
color_r(
MXCOLOR color) {
return static_cast<std::uint8_t
>((color >> 16u) & 0xFFu); }
55 [[nodiscard]]
inline constexpr std::uint8_t
color_g(
MXCOLOR color) {
return static_cast<std::uint8_t
>((color >> 8u) & 0xFFu); }
58 [[nodiscard]]
inline constexpr std::uint8_t
color_b(
MXCOLOR color) {
return static_cast<std::uint8_t
>(color & 0xFFu); }
61 [[nodiscard]]
inline constexpr std::uint8_t
color_a(
MXCOLOR color) {
return static_cast<std::uint8_t
>((color >> 24u) & 0xFFu); }
70 intensity = std::clamp(intensity, 0.0f, 1.0f);
71 const auto scale = [intensity](std::uint8_t component) {
return static_cast<int>(std::clamp(
static_cast<float>(component) * intensity, 0.0f, 255.0f)); };
76 std::array<float, 361> values{};
77 for (
int ang = 0; ang <= 360; ++ang) {
78 values[
static_cast<std::size_t
>(ang)] = std::sin(
static_cast<float>(ang) *
PI / 180.0f);
84 std::array<float, 361> values{};
85 for (
int ang = 0; ang <= 360; ++ang) {
86 values[
static_cast<std::size_t
>(ang)] = std::cos(
static_cast<float>(ang) *
PI / 180.0f);
99 std::cout <<
"mxvk_math: building trigonometric lookup tables\n";
100 for (
int ang = 0; ang <= 360; ++ang) {
101 const float theta =
static_cast<float>(ang) *
PI / 180.0f;
102 cos_look[
static_cast<std::size_t
>(ang)] = std::cos(theta);
103 sin_look[
static_cast<std::size_t
>(ang)] = std::sin(theta);
105 std::cout <<
"mxvk_math: trigonometric lookup tables ready (361 entries)\n";
109 [[nodiscard]]
inline float deg2rad(
float ang) {
return ang *
PI / 180.0f; }
112 [[nodiscard]]
inline float rad2deg(
float rad) {
return rad * 180.0f /
PI; }
119 [[nodiscard]]
inline float fast_cosf(
float theta_degrees) {
120 theta_degrees = std::fmod(theta_degrees, 360.0f);
121 if (theta_degrees < 0.0f) {
122 theta_degrees += 360.0f;
124 const int theta_int =
static_cast<int>(theta_degrees);
125 const float theta_frac = theta_degrees -
static_cast<float>(theta_int);
126 return cos_look[
static_cast<std::size_t
>(theta_int)] + theta_frac * (
cos_look[
static_cast<std::size_t
>(theta_int + 1)] -
cos_look[
static_cast<std::size_t
>(theta_int)]);
134 [[nodiscard]]
inline float fast_sinf(
float theta_degrees) {
135 theta_degrees = std::fmod(theta_degrees, 360.0f);
136 if (theta_degrees < 0.0f) {
137 theta_degrees += 360.0f;
139 const int theta_int =
static_cast<int>(theta_degrees);
140 const float theta_frac = theta_degrees -
static_cast<float>(theta_int);
141 return sin_look[
static_cast<std::size_t
>(theta_int)] + theta_frac * (
sin_look[
static_cast<std::size_t
>(theta_int + 1)] -
sin_look[
static_cast<std::size_t
>(theta_int)]);
150 [[nodiscard]]
inline int rrand(
int x,
int y) {
154 return x + (std::rand() % (y - x + 1));
170 constexpr vec2D(
float x_value,
float y_value) :
x(x_value),
y(y_value) {}
173 void Set(
float x_value,
float y_value) {
204 [[nodiscard]]
vec2D Scale(
float k)
const {
return *
this * k; }
220 const float length =
Length();
242 [[nodiscard]] std::string
Print(
const std::string &name =
"v")
const {
243 std::ostringstream out;
244 out << name <<
'<' <<
x <<
',' <<
y <<
'>';
271 constexpr vec3D(
float x_value,
float y_value,
float z_value) :
x(x_value),
y(y_value),
z(z_value) {}
274 void Set(
float x_value,
float y_value,
float z_value) {
308 [[nodiscard]]
vec3D Scale(
float k)
const {
return *
this * k; }
328 const float len =
Length();
349 [[nodiscard]] std::string
Print(
const std::string &name =
"v")
const {
350 std::ostringstream out;
351 out << name <<
'<' <<
x <<
',' <<
y <<
',' <<
z <<
'>';
378 constexpr vec4D() :
x(0.0f),
y(0.0f),
z(0.0f),
w(1.0f) {}
381 constexpr vec4D(
float x_value,
float y_value,
float z_value,
float w_value = 1.0f) :
x(x_value),
y(y_value),
z(z_value),
w(w_value) {}
384 void Set(
float x_value,
float y_value,
float z_value,
float w_value = 1.0f) {
427 [[nodiscard]]
vec4D Scale(
float k)
const {
return *
this * k; }
448 const float len =
Length();
479 [[nodiscard]] std::string
Print(
const std::string &name =
"v")
const {
480 std::ostringstream out;
481 out << name <<
'<' <<
x <<
',' <<
y <<
',' <<
z <<
',' <<
w <<
'>';
502 constexpr Mat1D(
float m0,
float m1) :
mat{m0, m1} {}
505 void Set(
float m0,
float m1) {
521 constexpr Mat1x3D(
float m0,
float m1,
float m2) :
mat{m0, m1, m2} {}
534 constexpr Mat1x4D(
float m0,
float m1,
float m2,
float m3) :
mat{m0, m1, m2, m3} {}
554 Mat2D(
float m00,
float m01,
float m10,
float m11) {
Set(m00, m01, m10, m11); }
557 void Set(
float m00,
float m01,
float m10,
float m11) {
589 const float inv = 1.0f / d;
590 out.
Set(
mat[1][1] * inv, -
mat[0][1] * inv, -
mat[1][0] * inv,
mat[0][0] * inv);
622 Mat3D(
float m00,
float m01,
float m02,
float m10,
float m11,
float m12,
float m20,
float m21,
float m22) {
Set(m00, m01, m02, m10, m11, m12, m20, m21, m22); }
625 void Set(
float m00,
float m01,
float m02,
float m10,
float m11,
float m12,
float m20,
float m21,
float m22) {
643 for (
int r = 0; r < 3; ++r) {
644 for (
int c = 0; c < 3; ++c) {
645 for (
int k = 0; k < 3; ++k) {
646 out.
mat[r][c] +=
mat[r][k] * m.
mat[k][c];
672 const float inv = 1.0f / d;
715 Mat4D(
float m00,
float m01,
float m02,
float m03,
float m10,
float m11,
float m12,
float m13,
float m20,
float m21,
float m22,
float m23,
float m30,
float m31,
float m32,
float m33) {
Set(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33); }
718 void Set(
float m00,
float m01,
float m02,
float m03,
float m10,
float m11,
float m12,
float m13,
float m20,
float m21,
float m22,
float m23,
float m30,
float m31,
float m32,
float m33) {
738 void LoadIdentity() {
Set(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); }
743 for (
int r = 0; r < 4; ++r) {
744 for (
int c = 0; c < 4; ++c) {
754 for (
int r = 0; r < 4; ++r) {
755 for (
int c = 0; c < 4; ++c) {
756 for (
int k = 0; k < 4; ++k) {
757 out.
mat[r][c] +=
mat[r][k] * m.
mat[k][c];
772 vec4D out(0.0f, 0.0f, 0.0f, 0.0f);
773 out.
x = in.
x *
mat[0][0] + in.
y *
mat[1][0] + in.
z *
mat[2][0] + in.
w *
mat[3][0];
774 out.
y = in.
x *
mat[0][1] + in.
y *
mat[1][1] + in.
z *
mat[2][1] + in.
w *
mat[3][1];
775 out.
z = in.
x *
mat[0][2] + in.
y *
mat[1][2] + in.
z *
mat[2][2] + in.
w *
mat[3][2];
776 out.
w = in.
x *
mat[0][3] + in.
y *
mat[1][3] + in.
z *
mat[2][3] + in.
w *
mat[3][3];
788 void MulVec(std::span<const vec4D> input, std::span<vec4D> output)
const {
789 if (input.size() != output.size()) {
790 throw std::invalid_argument(
"Mat4D::MulVec batch spans must have equal sizes");
792 for (std::size_t index = 0; index < input.size(); ++index) {
793 output[index] =
MulVec(input[index]);
800 return {r.
x, r.
y, r.
z};
813 for (
int r = 0; r < 4; ++r) {
814 for (
int c = 0; c < 4; ++c) {
820 for (
int c = 0; c < 4; ++c) {
822 for (
int r = c + 1; r < 4; ++r) {
823 if (std::fabs(a[r][c]) > std::fabs(a[pivot][c])) {
827 if (std::fabs(a[pivot][c]) <=
EPSILON) {
831 for (
int k = 0; k < 8; ++k) {
832 std::swap(a[c][k], a[pivot][k]);
835 const float inv_pivot = 1.0f / a[c][c];
836 for (
int k = 0; k < 8; ++k) {
837 a[c][k] *= inv_pivot;
839 for (
int r = 0; r < 4; ++r) {
843 const float factor = a[r][c];
844 for (
int k = 0; k < 8; ++k) {
845 a[r][k] -= factor * a[c][k];
850 for (
int r = 0; r < 4; ++r) {
851 for (
int c = 0; c < 4; ++c) {
852 out.
mat[r][c] = a[r][c + 4];
859 void BuildXYZ(
float theta_x,
float theta_y,
float theta_z) {
860 const float cx = std::cos(
deg2rad(theta_x));
861 const float sx = std::sin(
deg2rad(theta_x));
862 const float cy = std::cos(
deg2rad(theta_y));
863 const float sy = std::sin(
deg2rad(theta_y));
864 const float cz = std::cos(
deg2rad(theta_z));
865 const float sz = std::sin(
deg2rad(theta_z));
867 Mat4D mx(1, 0, 0, 0, 0, cx, sx, 0, 0, -sx, cx, 0, 0, 0, 0, 1);
868 Mat4D my(cy, 0, -sy, 0, 0, 1, 0, 0, sy, 0, cy, 0, 0, 0, 0, 1);
869 Mat4D mz(cz, sz, 0, 0, -sz, cz, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
870 *
this =
mx * my * mz;
922 const float det =
v.
x * line.
v.
y -
v.
y * line.
v.
x;
923 if (std::fabs(det) <=
EPSILON) {
927 t_this = (delta.
x * line.
v.
y - delta.
y * line.
v.
x) / det;
928 t_other = (delta.
x *
v.
y - delta.
y *
v.
x) / det;
929 return (t_this >= 0.0f && t_this <= 1.0f && t_other >= 0.0f && t_other <= 1.0f) ? 1 : 2;
940 float t_other = 0.0f;
941 const int result =
Intersect(line, t_this, t_other);
1066 constexpr QuatType(
float x_value,
float y_value,
float z_value,
float w_value) :
x(x_value),
y(y_value),
z(z_value),
w(w_value) {}
1102 [[nodiscard]]
float Norm()
const {
return std::sqrt(
Norm2()); }
1106 const float norm =
Norm();
1117 const float n2 =
Norm2();
1139 const float half =
deg2rad(theta_degrees) * 0.5f;
1140 const float s = std::sin(half);
1154 void EulerZYX(
float theta_x,
float theta_y,
float theta_z) {
1155 const float hx =
deg2rad(theta_x) * 0.5f;
1156 const float hy =
deg2rad(theta_y) * 0.5f;
1157 const float hz =
deg2rad(theta_z) * 0.5f;
1158 const float cx = std::cos(hx);
1159 const float sx = std::sin(hx);
1160 const float cy = std::cos(hy);
1161 const float sy = std::sin(hy);
1162 const float cz = std::cos(hz);
1163 const float sz = std::sin(hz);
1164 w = cz * cy * cx + sz * sy * sx;
1165 x = cz * cy * sx - sz * sy * cx;
1166 y = cz * sy * cx + sz * cy * sx;
1167 z = sz * cy * cx - cz * sy * sx;
1174 const float s = std::sqrt(std::max(0.0f, 1.0f - q.
w * q.
w));
1176 axis.
Set(1.0f, 0.0f, 0.0f);
1178 axis.
Set(q.
x / s, q.
y / s, q.
z / s);
1180 if (theta_degrees !=
nullptr) {
1181 *theta_degrees =
rad2deg(2.0f * std::acos(std::clamp(q.
w, -1.0f, 1.0f)));
1251 for (
auto &poly :
polys) {
1252 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
1255 for (
auto &vertex : poly.vlist) {
1256 vertex = mrot.
MulVec(vertex);
1263 for (
auto &poly :
polys) {
1264 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
1282 for (
auto &poly :
polys) {
1283 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
1286 for (
int i = 0; i < 3; ++i) {
1287 poly.tlist[i] = poly.vlist[i] + pos;
1294 polys.push_back(triangle);
1369 std::cout <<
"mxvk_math: loading PLG model: " << path <<
'\n';
1370 const auto load_failed = [&path](
const char *reason) {
1371 std::cerr <<
"mxvk_math: failed to load PLG model '" << path <<
"': " << reason <<
'\n';
1375 std::ifstream file(path);
1376 if (!file.is_open()) {
1377 return load_failed(
"could not open file");
1380 const auto read_data_line = [&file](std::string &line) {
1381 while (std::getline(file, line)) {
1382 const std::size_t comment = line.find(
'#');
1383 if (comment != std::string::npos) {
1384 line.erase(comment);
1386 if (line.find_first_not_of(
" \t\r\n") != std::string::npos) {
1394 if (!read_data_line(line)) {
1395 return load_failed(
"missing model header");
1399 int vertex_count = 0;
1401 std::istringstream header(line);
1402 if (!(header >> name >> vertex_count >> poly_count) || vertex_count < 0 || poly_count < 0) {
1403 return load_failed(
"invalid model header");
1405 std::cout <<
"mxvk_math: PLG header parsed (object='" << name <<
"', vertices=" << vertex_count <<
", triangles=" << poly_count <<
")\n";
1407 std::vector<vec4D> loaded_local;
1408 std::vector<vec4D> loaded_trans;
1409 std::vector<vec2D> loaded_texcoords;
1410 std::vector<Triangle> loaded_vlist;
1411 loaded_local.reserve(
static_cast<std::size_t
>(vertex_count));
1412 loaded_trans.resize(
static_cast<std::size_t
>(vertex_count));
1413 loaded_texcoords.reserve(
static_cast<std::size_t
>(vertex_count));
1414 loaded_vlist.reserve(
static_cast<std::size_t
>(poly_count));
1416 for (
int i = 0; i < vertex_count; ++i) {
1417 if (!read_data_line(line)) {
1418 return load_failed(
"vertex data ended early");
1422 std::istringstream vertex_line(line);
1423 if (!(vertex_line >> vertex.
x >> vertex.
y >> vertex.
z) || !std::isfinite(vertex.
x) || !std::isfinite(vertex.
y) || !std::isfinite(vertex.
z)) {
1424 return load_failed(
"invalid vertex data");
1428 if (vertex_line >> texcoord.
x) {
1429 if (!(vertex_line >> texcoord.
y) || !std::isfinite(texcoord.
x) || !std::isfinite(texcoord.
y)) {
1430 return load_failed(
"invalid texture coordinates");
1434 vertex.
x *= scale.
x;
1435 vertex.
y *= scale.
y;
1436 vertex.
z *= scale.
z;
1437 if (!std::isfinite(vertex.
x) || !std::isfinite(vertex.
y) || !std::isfinite(vertex.
z)) {
1438 return load_failed(
"scaled vertex is not finite");
1441 loaded_local.push_back(vertex);
1442 loaded_texcoords.push_back(texcoord);
1445 for (
int i = 0; i < poly_count; ++i) {
1446 if (!read_data_line(line)) {
1447 return load_failed(
"triangle data ended early");
1452 std::istringstream polygon_line(line);
1453 if (!(polygon_line >> std::hex >> tri.
state >> std::dec >> count) || count != 3 || !(polygon_line >> tri.
vert[0] >> tri.
vert[1] >> tri.
vert[2])) {
1454 return load_failed(
"invalid triangle data");
1456 for (
const int index : tri.
vert) {
1457 if (index < 0 || index >= vertex_count) {
1458 return load_failed(
"triangle vertex index is out of range");
1461 loaded_vlist.push_back(tri);
1464 for (
auto &triangle : loaded_vlist) {
1471 local = std::move(loaded_local);
1472 trans = std::move(loaded_trans);
1473 texcoords = std::move(loaded_texcoords);
1474 vlist = std::move(loaded_vlist);
1478 std::cout <<
"mxvk_math: PLG model ready (object='" <<
object_name <<
"', average radius=" <<
avg_rad <<
", maximum radius=" <<
max_rad <<
")\n";
1483 [[nodiscard]]
bool LoadMX(
const std::string &path,
const vec4D &scale,
const vec4D &obj_pos,
const vec4D &rotation) {
return LoadPLG(path, scale, obj_pos, rotation); }
1494 std::cout <<
"mxvk_math: loading OBJ model: " << path <<
'\n';
1498 std::cerr <<
"mxvk_math: failed to load OBJ model '" << path <<
"': " << error <<
'\n';
1502 std::unordered_map<std::string, int> material_indices;
1503 for (std::size_t index = 0; index < loaded.
materials.size(); ++index) {
1504 material_indices.emplace(loaded.
materials[index].name,
static_cast<int>(index));
1507 std::vector<vec4D> loaded_local;
1508 std::vector<vec2D> loaded_texcoords;
1509 std::vector<Triangle> loaded_vlist;
1510 loaded_local.reserve(loaded.
triangles.size() * 3);
1511 loaded_texcoords.reserve(loaded.
triangles.size() * 3);
1512 loaded_vlist.reserve(loaded.
triangles.size());
1519 const auto material = material_indices.find(source_triangle.
material_name);
1520 if (material != material_indices.end()) {
1522 triangle.
color = material_color(loaded.
materials[
static_cast<std::size_t
>(material->second)]);
1525 for (std::size_t vertex_index = 0; vertex_index < source_triangle.
vertices.size(); ++vertex_index) {
1527 const float x = source_vertex.
position[0] * scale.
x;
1528 const float y = source_vertex.
position[1] * scale.
y;
1529 const float z = source_vertex.
position[2] * scale.
z;
1530 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(z)) {
1531 std::cerr <<
"mxvk_math: failed to load OBJ model '" << path <<
"': scaled vertex is not finite\n";
1534 triangle.
vert[vertex_index] =
static_cast<int>(loaded_local.size());
1535 loaded_local.emplace_back(x, y, z, 1.0f);
1536 loaded_texcoords.emplace_back(source_vertex.
texcoord[0], source_vertex.
texcoord[1]);
1538 loaded_vlist.push_back(triangle);
1543 num_polys =
static_cast<int>(loaded_vlist.size());
1544 local = std::move(loaded_local);
1546 texcoords = std::move(loaded_texcoords);
1547 vlist = std::move(loaded_vlist);
1561 [[nodiscard]]
bool LoadMTL(
const std::string &path) {
1562 std::vector<OBJMaterial> loaded_materials;
1565 std::cerr <<
"mxvk_math: failed to load MTL file '" << path <<
"': " << error <<
'\n';
1569 std::unordered_map<std::string, int> material_indices;
1570 for (std::size_t index = 0; index < loaded_materials.size(); ++index) {
1571 material_indices.emplace(loaded_materials[index].name,
static_cast<int>(index));
1573 for (std::size_t index = 0; index <
vlist.size(); ++index) {
1574 const auto material = material_indices.find(
vlist[index].material_name);
1575 vlist[index].material_index = material == material_indices.end() ? -1 : material->second;
1576 if (material != material_indices.end()) {
1577 vlist[index].color = material_color(loaded_materials[
static_cast<std::size_t
>(material->second)]);
1581 materials = std::move(loaded_materials);
1590 for (std::size_t i = 0; i <
local.size(); ++i) {
1593 }
else if (type == 1) {
1594 for (
auto &vertex :
trans) {
1602 auto transform = [&mrot](std::vector<vec4D> &vertices) {
1603 for (
auto &vertex : vertices) {
1604 vertex = mrot.
MulVec(vertex);
1609 }
else if (type == 1) {
1611 }
else if (type == 2) {
1613 for (std::size_t i = 0; i <
local.size(); ++i) {
1621 for (
auto &poly :
vlist) {
1625 if (poly.vert[0] < 0 || poly.vert[1] < 0 || poly.vert[2] < 0 ||
static_cast<std::size_t
>(poly.vert[0]) >=
trans.size() ||
static_cast<std::size_t
>(poly.vert[1]) >=
trans.size() ||
static_cast<std::size_t
>(poly.vert[2]) >=
trans.size()) {
1628 const vec4D u =
vec4D().
Build(
trans[
static_cast<std::size_t
>(poly.vert[0])],
trans[
static_cast<std::size_t
>(poly.vert[1])]);
1629 const vec4D v =
vec4D().
Build(
trans[
static_cast<std::size_t
>(poly.vert[0])],
trans[
static_cast<std::size_t
>(poly.vert[2])]);
1640 for (
const auto &poly :
vlist) {
1642 for (
int i = 0; i < 3; ++i) {
1643 const auto index =
static_cast<std::size_t
>(poly.vert[i]);
1644 if (index <
local.size()) {
1647 if (index <
trans.size()) {
1657 for (
auto &poly :
vlist) {
1667 if (
local.empty()) {
1670 for (
const auto &vertex :
local) {
1671 const float dist = std::sqrt(vertex.x * vertex.x + vertex.y * vertex.y + vertex.z * vertex.z);
1684 const auto channel = [](
float value) {
return static_cast<MXCOLOR>(std::lround(std::clamp(value, 0.0f, 1.0f) * 255.0f)); };
1685 return (channel(material.
dissolve) << 24u) | (channel(material.
diffuse[0]) << 16u) | (channel(material.
diffuse[1]) << 8u) | channel(material.
diffuse[2]);
1779 pos.
Set(100.0f, 200.0f, 300.0f);
1780 dir.
Set(-48.0f, 0.0f, 0.0f);
1785 void Init(
int camera_attr,
const vec4D &camera_pos,
const vec4D &camera_dir,
const vec4D *camera_target,
float near_z,
float far_z,
float fov_degrees,
float width,
float height) {
1789 target = camera_target !=
nullptr ? *camera_target :
vec4D();
1808 Mat4D translation(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -
pos.
x, -
pos.
y, -
pos.
z, 1);
1811 mcam = translation * rotation;
1818 v.
Set(0.0f, 1.0f, 0.0f);
1823 Mat4D translation(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -
pos.
x, -
pos.
y, -
pos.
z, 1);
1824 Mat4D uvn(
u.
x,
v.
x,
n.
x, 0,
u.
y,
v.
y,
n.
y, 0,
u.
z,
v.
z,
n.
z, 0, 0, 0, 0, 1);
1825 mcam = translation * uvn;
1830 for (
auto &poly : list.
polys) {
1831 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
1834 for (
auto &vertex : poly.tlist) {
1842 for (
auto &vertex :
object.trans) {
1849 for (
auto &poly : list.
polys) {
1850 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
1853 for (
auto &vertex : poly.tlist) {
1854 if (std::fabs(vertex.z) <=
EPSILON) {
1857 vertex.x =
view_dist * vertex.x / vertex.z;
1865 for (
auto &vertex :
object.trans) {
1866 if (std::fabs(vertex.z) <=
EPSILON) {
1869 vertex.x =
view_dist * vertex.x / vertex.z;
1878 for (
auto &poly : list.
polys) {
1879 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
1882 for (
auto &vertex : poly.tlist) {
1883 vertex.x = alpha + alpha * vertex.x;
1884 vertex.y = beta - beta * vertex.y;
1893 for (
auto &vertex :
object.trans) {
1894 vertex.x = alpha + alpha * vertex.x;
1895 vertex.y = beta - beta * vertex.y;
1911 SDL_RenderPoint(
renderer,
static_cast<float>(x),
static_cast<float>(y));
1941 template <
typename PlotPixel>
void draw_line(
int x0,
int y0,
int x1,
int y1,
MXCOLOR color, PlotPixel &&plot_pixel) {
1942 const int dx = std::abs(x1 - x0);
1943 const int sx = x0 < x1 ? 1 : -1;
1944 const int dy = -std::abs(y1 - y0);
1945 const int sy = y0 < y1 ? 1 : -1;
1949 plot_pixel(x0, y0, color);
1950 if (x0 == x1 && y0 == y1) {
1953 const int e2 = 2 * err;
1966 inline void draw_line(SDL_Renderer *renderer,
int x0,
int y0,
int x1,
int y1,
MXCOLOR color) {
draw_line(x0, y0, x1, y1, color,
SDLRendererPixelPlotter{renderer}); }
1969 inline void draw_line(
VK_Sprite &sprite,
int x0,
int y0,
int x1,
int y1,
MXCOLOR color,
int pixel_size = 1) {
draw_line(x0, y0, x1, y1, color,
VKSpritePixelPlotter{&sprite, pixel_size}); }
1985 if (std::fabs(area) <=
EPSILON) {
1989 const int min_x =
static_cast<int>(std::floor(std::min({p0.
x, p1.
x, p2.
x})));
1990 const int max_x =
static_cast<int>(std::ceil(std::max({p0.
x, p1.
x, p2.
x})));
1991 const int min_y =
static_cast<int>(std::floor(std::min({p0.
y, p1.
y, p2.
y})));
1992 const int max_y =
static_cast<int>(std::ceil(std::max({p0.
y, p1.
y, p2.
y})));
1994 for (
int y = min_y; y <= max_y; ++y) {
1995 for (
int x = min_x; x <= max_x; ++x) {
1996 const vec2D p(
static_cast<float>(x) + 0.5f,
static_cast<float>(y) + 0.5f);
2000 if ((area > 0.0f && w0 >= 0.0f && w1 >= 0.0f && w2 >= 0.0f) || (area < 0.0f && w0 <= 0.0f && w1 <= 0.0f && w2 <= 0.0f)) {
2001 plot_pixel(x, y, color);
2011 inline void draw_filled_triangle(
VK_Sprite &sprite,
const vec2D &p0,
const vec2D &p1,
const vec2D &p2,
MXCOLOR color,
int pixel_size = 1) {
draw_filled_triangle(p0, p1, p2, color,
VKSpritePixelPlotter{&sprite, pixel_size}); }
2035 [[nodiscard]]
float x_at(
int y)
const {
return x0 +
static_cast<float>(y - y0) * slope; }
2038 const int min_y = std::max(clip_min_y,
static_cast<int>(std::floor(std::min({p0.
y, p1.
y, p2.
y}))));
2039 const int max_y = std::min(clip_max_y,
static_cast<int>(std::ceil(std::max({p0.
y, p1.
y, p2.
y}))));
2040 if (min_y > max_y) {
2044 std::array<SpanEdge, 3> edges{};
2046 const auto add_edge = [&](
vec2D a,
vec2D b) {
2047 if (std::fabs(a.
y - b.y) <=
EPSILON) {
2054 const int y0 = std::max(min_y,
static_cast<int>(std::ceil(a.
y - 0.5f)));
2055 const int y1 = std::min(max_y,
static_cast<int>(std::ceil(b.y - 0.5f)) - 1);
2060 SpanEdge &edge = edges[
static_cast<std::size_t
>(edge_count++)];
2063 edge.slope = (b.x - a.
x) / (b.y - a.
y);
2064 edge.x0 = a.
x + ((
static_cast<float>(y0) + 0.5f) - a.
y) * edge.slope;
2071 for (
int y = min_y; y <= max_y; ++y) {
2072 std::array<float, 3> intersections{};
2073 int intersection_count = 0;
2075 for (
int edge_index = 0; edge_index < edge_count; ++edge_index) {
2076 const SpanEdge &edge = edges[
static_cast<std::size_t
>(edge_index)];
2077 if (y >= edge.y0 && y <= edge.y1) {
2078 intersections[
static_cast<std::size_t
>(intersection_count++)] = edge.x_at(y);
2082 if (intersection_count < 2) {
2086 float min_x = intersections[0];
2087 float max_x = intersections[0];
2088 for (
int i = 1; i < intersection_count; ++i) {
2089 min_x = std::min(min_x, intersections[
static_cast<std::size_t
>(i)]);
2090 max_x = std::max(max_x, intersections[
static_cast<std::size_t
>(i)]);
2093 const int x0 =
static_cast<int>(std::ceil(min_x - 0.5f));
2094 const int x1 =
static_cast<int>(std::floor(max_x - 0.5f));
2096 draw_span(x0, x1, y, color);
2101 template <
typename DrawSpan>
void draw_filled_triangle_spans(
vec2D p0,
vec2D p1,
vec2D p2,
MXCOLOR color, DrawSpan &&draw_span) {
draw_filled_triangle_spans_clipped(p0, p1, p2, std::numeric_limits<int>::min(), std::numeric_limits<int>::max(), color, std::forward<DrawSpan>(draw_span)); }
2149 void Begin(
int width,
int height, std::function<
void(
int,
int,
MXCOLOR)> plotter) {
2150 plot_pixel = std::move(plotter);
2151 plot_span = [
this](
int x0,
int x1,
int y,
MXCOLOR color) {
2152 for (
int x = x0; x <= x1; ++x) {
2153 plot_pixel(x, y, color);
2160 [[maybe_unused]]
static const bool PIPELINE_LOGGED = [width, height] {
2161 std::cout <<
"mxvk_math: software raster pipeline ready (" << width <<
'x' << height <<
")\n";
2167 void Begin(SDL_Renderer *renderer,
int width,
int height) {
2169 plot_span = [renderer](
int x0,
int x1,
int y,
MXCOLOR color) {
2170 if (renderer ==
nullptr) {
2174 SDL_RenderLine(renderer,
static_cast<float>(x0),
static_cast<float>(y),
static_cast<float>(x1),
static_cast<float>(y));
2181 plot_span = [&sprite, pixel_size](
int x0,
int x1,
int y,
MXCOLOR) {
2182 const int size = std::max(1, pixel_size);
2211 bool ClipLine(
int &x0,
int &y0,
int &x1,
int &y1)
const {
2216 if ((code0 | code1) == 0) {
2219 if ((code0 & code1) != 0) {
2223 const int out_code = code0 != 0 ? code0 : code1;
2227 if ((out_code &
CODE_N) != 0) {
2231 x = x0 + (x1 - x0) * (
clip_min_y - y0) / (y1 - y0);
2233 }
else if ((out_code &
CODE_S) != 0) {
2237 x = x0 + (x1 - x0) * (
clip_max_y - y0) / (y1 - y0);
2239 }
else if ((out_code &
CODE_E) != 0) {
2243 y = y0 + (y1 - y0) * (
clip_max_x - x0) / (x1 - x0);
2249 y = y0 + (y1 - y0) * (
clip_min_x - x0) / (x1 - x0);
2253 if (out_code == code0) {
2267 if (plot_pixel &&
ClipLine(x0, y0, x1, y1)) {
2268 draw_line(x0, y0, x1, y1, color, plot_pixel);
2283 if (!plot_pixel || first.
z <=
EPSILON || second.
z <=
EPSILON || !std::isfinite(first.
x) || !std::isfinite(first.
y) || !std::isfinite(first.
z) || !std::isfinite(second.
x) || !std::isfinite(second.
y) || !std::isfinite(second.
z)) {
2287 const int framebuffer_width =
max_clip_x + 1;
2288 const int framebuffer_height =
max_clip_y + 1;
2289 const std::size_t required_depth_values =
static_cast<std::size_t
>(framebuffer_width) *
static_cast<std::size_t
>(framebuffer_height);
2290 if (framebuffer_width <= 0 || framebuffer_height <= 0 || depth_buffer.size() < required_depth_values) {
2294 const float delta_x = second.
x - first.
x;
2295 const float delta_y = second.
y - first.
y;
2296 float first_fraction = 0.0f;
2297 float last_fraction = 1.0f;
2298 const auto clip_fraction = [&first_fraction, &last_fraction](
float direction,
float distance) {
2299 if (std::abs(direction) <=
EPSILON) {
2300 return distance >= 0.0f;
2303 const float fraction = distance / direction;
2304 if (direction < 0.0f) {
2305 first_fraction = std::max(first_fraction, fraction);
2307 last_fraction = std::min(last_fraction, fraction);
2309 return first_fraction <= last_fraction;
2312 if (!clip_fraction(-delta_x, first.
x -
static_cast<float>(
clip_min_x)) || !clip_fraction(delta_x,
static_cast<float>(
clip_max_x) - first.
x) || !clip_fraction(-delta_y, first.
y -
static_cast<float>(
clip_min_y)) || !clip_fraction(delta_y,
static_cast<float>(
clip_max_y) - first.
y)) {
2316 const float clipped_delta_x = delta_x * (last_fraction - first_fraction);
2317 const float clipped_delta_y = delta_y * (last_fraction - first_fraction);
2318 const float step_count = std::ceil(std::max(std::abs(clipped_delta_x), std::abs(clipped_delta_y)));
2319 if (step_count >
static_cast<float>(std::numeric_limits<int>::max())) {
2322 const int steps = std::max(1,
static_cast<int>(step_count));
2323 const float first_reciprocal_depth = 1.0f / first.
z;
2324 const float second_reciprocal_depth = 1.0f / second.
z;
2326 for (
int step = 0; step <= steps; ++step) {
2327 const float clipped_fraction =
static_cast<float>(step) /
static_cast<float>(steps);
2328 const float fraction = first_fraction + (last_fraction - first_fraction) * clipped_fraction;
2329 const int x =
static_cast<int>(std::lround(first.
x + delta_x * fraction));
2330 const int y =
static_cast<int>(std::lround(first.
y + delta_y * fraction));
2335 const float reciprocal_depth = first_reciprocal_depth + (second_reciprocal_depth - first_reciprocal_depth) * fraction;
2336 if (reciprocal_depth <=
EPSILON) {
2340 const float depth = 1.0f / reciprocal_depth;
2341 const std::size_t pixel_index =
static_cast<std::size_t
>(y) *
static_cast<std::size_t
>(framebuffer_width) +
static_cast<std::size_t
>(x);
2342 if (depth >= depth_buffer[pixel_index]) {
2346 depth_buffer[pixel_index] = depth;
2347 plot_pixel(x, y, color);
2353 DrawClippedLine(
static_cast<int>(std::lround(first.
x)),
static_cast<int>(std::lround(first.
y)),
static_cast<int>(std::lround(second.
x)),
static_cast<int>(std::lround(second.
y)), color);
2354 DrawClippedLine(
static_cast<int>(std::lround(second.
x)),
static_cast<int>(std::lround(second.
y)),
static_cast<int>(std::lround(third.
x)),
static_cast<int>(std::lround(third.
y)), color);
2355 DrawClippedLine(
static_cast<int>(std::lround(third.
x)),
static_cast<int>(std::lround(third.
y)),
static_cast<int>(std::lround(first.
x)),
static_cast<int>(std::lround(first.
y)), color);
2371 const int min_x =
static_cast<int>(std::floor(std::min({p0.
x, p1.
x, p2.
x})));
2372 const int max_x =
static_cast<int>(std::ceil(std::max({p0.
x, p1.
x, p2.
x})));
2373 const int min_y =
static_cast<int>(std::floor(std::min({p0.
y, p1.
y, p2.
y})));
2374 const int max_y =
static_cast<int>(std::ceil(std::max({p0.
y, p1.
y, p2.
y})));
2386 plot_span(x0, x1, y, pixel_color);
2396 for (
const auto &poly : list.
polys) {
2397 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
2406 for (
const auto &poly : list.
polys) {
2407 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
2416 for (
const auto &poly : list.
polys) {
2417 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
2429 for (
const auto &poly :
object.vlist) {
2430 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
2433 const auto a =
static_cast<std::size_t
>(poly.vert[0]);
2434 const auto b =
static_cast<std::size_t
>(poly.vert[1]);
2435 const auto c =
static_cast<std::size_t
>(poly.vert[2]);
2436 if (a >=
object.trans.size() || b >=
object.trans.size() || c >=
object.trans.size()) {
2448 for (
const auto &poly :
object.vlist) {
2449 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
2452 const auto a =
static_cast<std::size_t
>(poly.vert[0]);
2453 const auto b =
static_cast<std::size_t
>(poly.vert[1]);
2454 const auto c =
static_cast<std::size_t
>(poly.vert[2]);
2455 if (a >=
object.trans.size() || b >=
object.trans.size() || c >=
object.trans.size()) {
2464 plot_pixel =
nullptr;
2465 plot_span =
nullptr;
2469 std::function<void(
int,
int,
MXCOLOR)> plot_pixel;
2470 std::function<void(
int,
int,
int,
MXCOLOR)> plot_span;
float viewport_width
Viewport width in pixels.
int state
Camera state flags.
float aspect_ratio
Viewport aspect ratio.
float far_clip_z
Far clipping plane Z.
int attr
Camera attributes.
vec4D target
Camera look-at target.
float view_dist
Distance from camera to view plane.
float near_clip_z
Near clipping plane Z.
void PerspectiveToScreen(RenderList &list) const
Convert a render list from perspective coordinates to screen coordinates.
Plane3D lt_clip_plane
Left clipping plane.
void PerspectiveToScreen(mxObject &object) const
Convert an object's transformed vertices from perspective coordinates to screen coordinates.
vec4D dir
Euler camera direction in degrees.
vec4D pos
Camera position.
float viewport_height
Viewport height in pixels.
float viewplane_width
View-plane width.
void InitalizeForEuler()
Initialize camera fields for the Euler example path.
void CameraToPerspective(mxObject &object) const
Project an object's transformed vertices from camera space to perspective space.
float viewport_center_y
Viewport center Y coordinate.
Mat4D mcam
World-to-camera transform matrix.
Plane3D bt_clip_plane
Bottom clipping plane.
float viewplane_height
View-plane height.
Mat4D mscr
Screen transform matrix placeholder.
void BuildUVN(int)
Build the world-to-camera matrix from the camera position and look-at target.
void WorldToCamera(RenderList &list) const
Transform a render list from world space to camera space.
vec4D u
UVN camera U basis vector.
void BuildEuler(int)
Build the world-to-camera matrix from Euler direction angles.
vec4D v
UVN camera V basis vector.
void Init(int camera_attr, const vec4D &camera_pos, const vec4D &camera_dir, const vec4D *camera_target, float near_z, float far_z, float fov_degrees, float width, float height)
Initialize camera projection, viewport, position, and direction parameters.
Plane3D tp_clip_plane
Top clipping plane.
Plane3D rt_clip_plane
Right clipping plane.
Mat4D mper
Perspective transform matrix placeholder.
Camera()
Construct a camera with identity matrices.
float fov
Vertical field of view in degrees.
void WorldToCamera(mxObject &object) const
Transform an object's transformed vertices from world space to camera space.
float viewport_center_x
Viewport center X coordinate.
void CameraToPerspective(RenderList &list) const
Project a render list from camera space to perspective space.
vec4D n
UVN camera N basis vector.
Two-element column-vector storage used by 2x2 linear solves.
constexpr Mat1D()=default
Construct a zero-initialized 2-element vector.
float mat[2]
Matrix/vector elements.
void Set(float m0, float m1)
Set both elements.
constexpr Mat1D(float m0, float m1)
Construct from explicit elements.
Three-element column-vector storage used by 3x3 linear solves.
float mat[3]
Matrix/vector elements.
constexpr Mat1x3D(float m0, float m1, float m2)
Construct from explicit elements.
constexpr Mat1x3D()=default
Construct a zero-initialized 3-element vector.
constexpr Mat1x4D(float m0, float m1, float m2, float m3)
Construct from explicit elements.
float mat[4]
Matrix/vector elements.
constexpr Mat1x4D()=default
Construct a zero-initialized 4-element vector.
Mat2D()=default
Construct a zero-initialized matrix.
Mat2D operator*(const Mat2D &m) const
Multiply two 2x2 matrices.
bool Inverse(Mat2D &out) const
Compute the inverse matrix.
bool Solve2x2(const Mat2D &a, Mat1D &out, const Mat1D &b) const
Solve a 2x2 linear system.
float Determinate() const
Compute the matrix determinant.
Mat2D operator-(const Mat2D &m) const
Subtract two matrices component-wise.
void LoadIdentity()
Set this matrix to the identity matrix.
Mat2D(float m00, float m01, float m10, float m11)
Construct from explicit row-major elements.
Mat2D operator+(const Mat2D &m) const
Add two matrices component-wise.
float mat[2][2]
Matrix elements indexed as row, column.
void Set(float m00, float m01, float m10, float m11)
Set all matrix elements in row-major order.
float Determinate() const
Compute the matrix determinant.
vec3D MulVec(const vec3D &in) const
Transform a 3D vector by this matrix.
bool Solve3x3(const Mat3D &a, Mat1x3D &out, const Mat1x3D &b) const
Solve a 3x3 linear system.
Mat3D operator*(const Mat3D &m) const
Multiply two 3x3 matrices.
Mat3D()=default
Construct a zero-initialized matrix.
Mat3D(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22)
Construct from explicit row-major elements.
float mat[3][3]
Matrix elements indexed as row, column.
void Set(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22)
Set all matrix elements in row-major order.
void LoadIdentity()
Set this matrix to the identity matrix.
void MulVec(const vec3D &in, vec3D &out) const
Transform a 3D vector and write the result to out.
bool Inverse(Mat3D &out) const
Compute the inverse matrix.
Four-by-four homogeneous transform matrix.
float mat[4][4]
Matrix elements indexed as row, column.
bool Inverse(Mat4D &out) const
Compute the inverse matrix using Gauss-Jordan elimination.
void BuildXYZ(float theta_x, float theta_y, float theta_z)
Build an XYZ Euler rotation matrix from angles in degrees.
Mat4D()=default
Construct a zero-initialized matrix.
Mat4D & operator*=(const Mat4D &m)
Multiply this matrix by another matrix in place.
void MulVec(const vec3D &in, vec3D &out) const
Transform a 3D point and write the result to out.
vec4D MulVec(const vec4D &in) const
Transform a homogeneous 4D vector by this matrix.
vec3D MulVec(const vec3D &in) const
Transform a 3D point by this matrix using W = 1.
void MulVec(std::span< const vec4D > input, std::span< vec4D > output) const
Transform a batch of homogeneous 4D vectors.
void Set(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, float m21, float m22, float m23, float m30, float m31, float m32, float m33)
Set all matrix elements in row-major order.
void MulVec(const vec4D &in, vec4D &out) const
Transform a homogeneous 4D vector and write the result to out.
void LoadIdentity()
Set this matrix to the identity matrix.
Mat4D operator+(const Mat4D &m) const
Add two matrices component-wise.
Mat4D operator*(const Mat4D &m) const
Multiply two 4x4 matrices.
Mat4D(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, float m21, float m22, float m23, float m30, float m31, float m32, float m33)
Construct from explicit row-major elements.
Four-by-three matrix storage.
float mat[4][3]
Matrix elements indexed as row, column.
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.
int min_clip_x
Minimum clip X coordinate.
void DrawFilledTriangle(const vec4D &p0, const vec4D &p1, const vec4D &p2, MXCOLOR color) const
Draw a clipped filled triangle from homogeneous screen-space vertices.
int min_clip_y
Minimum clip Y coordinate.
void DrawSolidPolys(const RenderList &list) const
Draw active render-list triangles as filled polygons.
void Begin(VK_Sprite &sprite, int width, int height, int pixel_size=1)
Begin rendering to a VK_Sprite with optional square pixel size.
void DrawObject(const mxObject &object) const
Draw an object's active transformed triangles as clipped wireframes.
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 DrawPolys(const RenderList &list) const
Draw active render-list triangles as clipped wireframes.
int clip_min_x
Active minimum clip X coordinate.
int clip_max_x
Active maximum clip X coordinate.
void DrawClippedLine(int x0, int y0, int x1, int y1, MXCOLOR color) const
Draw a clipped line.
int max_clip_y
Maximum clip Y coordinate.
void DrawObject(const mxObject &object, std::span< float > depth_buffer) const
Draw an object's active transformed triangles as depth-tested clipped wireframes.
void DrawPolys(const RenderList &list, std::span< float > depth_buffer) const
Draw active render-list triangles as depth-tested clipped wireframes.
void DrawWireframeTriangle(const vec4D &first, const vec4D &second, const vec4D &third, MXCOLOR color, std::span< float > depth_buffer) const
Draw a clipped wireframe triangle with perspective-correct depth testing.
void DrawDepthTestedLine(const vec4D &first, const vec4D &second, MXCOLOR color, std::span< float > depth_buffer) const
Draw a clipped screen-space line with perspective-correct depth testing.
bool ClipLine(int &x0, int &y0, int &x1, int &y1) const
Clip a line segment to the active clip rectangle.
LINE_CODE
Cohen-Sutherland region codes for line clipping.
@ CODE_C
Center/inside code.
@ CODE_S
South/bottom code.
void Begin(int width, int height, std::function< void(int, int, MXCOLOR)> plotter)
Begin rendering with a custom pixel plotter.
void Begin(SDL_Renderer *renderer, int width, int height)
Begin rendering to an SDL renderer.
int clip_min_y
Active minimum clip Y coordinate.
int ComputeCode(int x, int y) const
Compute the Cohen-Sutherland region code for a point.
int max_clip_x
Maximum clip X coordinate.
void DrawWireframeTriangle(const vec4D &first, const vec4D &second, const vec4D &third, MXCOLOR color) const
Draw a clipped wireframe triangle without depth testing.
int clip_max_y
Active maximum clip Y coordinate.
void Inverse()
Invert this quaternion in place.
void InverseNormal()
Invert a unit quaternion by conjugating it.
constexpr QuatType()
Construct the identity quaternion.
float Norm2() const
Compute the squared norm.
constexpr QuatType operator*(const QuatType &q) const
Multiply two quaternions.
float z
Z component of the vector part.
constexpr QuatType operator+(const QuatType &q) const
Add two quaternions component-wise.
constexpr QuatType operator-(const QuatType &q) const
Subtract two quaternions component-wise.
float Norm() const
Compute the norm.
void Scale(float f)
Scale all quaternion components in place.
void vec4DthetaQuat(float theta_degrees, const vec4D &axis)
Build a quaternion from a 4D axis vector and angle in degrees.
void vec3DthetaQuat(float theta_degrees, const vec3D &axis)
Build a quaternion from an axis and angle in degrees.
float x
X component of the vector part.
void EulerZYX(float theta_x, float theta_y, float theta_z)
Build a quaternion from Euler angles in ZYX order, with angles in degrees.
void Normalize()
Normalize this quaternion in place, or reset it to identity if it is too small.
void Conj()
Conjugate this quaternion in place.
constexpr QuatType(float x_value, float y_value, float z_value, float w_value)
Construct from explicit quaternion components.
QuatType TripleProduct(const QuatType &p1, const QuatType &p2) const
Return the quaternion product (*this * p1) * p2.
void QuatToVec3D(float *theta_degrees, vec3D &axis) const
Convert this quaternion to axis-angle form.
float y
Y component of the vector part.
QuatType & operator*=(const QuatType &q)
Multiply this quaternion by another quaternion in place.
Flat list of triangles prepared for transformation and rasterization.
void RemoveFaces(const vec4D &pos)
Mark back-facing triangles relative to a view position.
int num_polys
Cached polygon count matching polys.size().
void ModelToWorld(const vec4D &pos, int type)
Translate model-space vertices into world-space transformed vertices.
std::vector< Triangle > polys
Triangle storage.
void TransformRenderList(const Mat4D &mrot, int type)
Transform active non-backface triangles by a matrix.
void Reset()
Clear all triangles from the render list.
void BuildRenderList(const Triangle &triangle)
Append one triangle to the render list.
void drawSpriteRect(int x, int y, int w, int h)
Queue a draw into an explicit destination rectangle.
Simple mesh object loaded from PLG-style indexed triangle data.
void SetState(int new_state)
Replace the object state flags.
bool LoadMX(const std::string &path, const vec4D &scale, const vec4D &obj_pos, const vec4D &rotation)
Load an MX mesh file using the PLG loader compatibility path.
bool LoadMTL(const std::string &path)
Replace this object's material library with an MTL file.
float ComputeRad()
Compute and cache average and maximum local-space radii.
float max_rad
Maximum radius from the local origin.
vec4D world_pos
Object world position.
std::vector< vec2D > texcoords
Optional texture coordinates corresponding to local-space vertices.
float avg_rad
Average radius from the local origin.
std::string material_library_path
Resolved path of the OBJ material library.
vec4D ux
Local X basis vector.
bool LoadOBJ(const std::string &path, const vec4D &scale, const vec4D &obj_pos, const vec4D &rotation)
Load a Wavefront OBJ mesh and its referenced MTL material library.
int num_polys
Number of loaded polygons.
std::vector< OBJMaterial > materials
Materials loaded from the OBJ material library.
int attr
Object attributes.
void BuildRenderList(RenderList &list) const
Append this object's triangles to a render list.
std::string object_name
Object name loaded from the model file.
vec4D dir
Object direction.
void Reset()
Reset object and polygon state to active.
int num_vertices
Number of loaded vertices.
int state
Object state flags.
std::vector< vec4D > local
Local-space vertices.
std::vector< Triangle > vlist
Indexed triangle list.
vec4D uy
Local Y basis vector.
void RemoveFaces(const vec4D &pos)
Mark object polygons that face away from a view position.
std::vector< vec4D > trans
Transformed vertices.
void ModelToWorld(int type=0)
Convert local or transformed vertices to world space.
void TransformObject(const Mat4D &mrot, int type=0)
Apply a transform to local vertices, transformed vertices, or local-to-transformed output.
vec4D uz
Local Z basis vector.
bool LoadPLG(const std::string &path, const vec4D &scale, const vec4D &obj_pos, const vec4D &rotation)
Load a PLG mesh file.
Two-dimensional float vector with common arithmetic helpers.
float Length() const
Compute the Euclidean length of this vector.
constexpr vec2D()
Construct the zero vector.
constexpr vec2D operator-(const vec2D &v) const
Subtract two vectors component-wise.
constexpr float DotProduct(const vec2D &v) const
Compute the dot product with another vector.
constexpr vec2D operator*(float k) const
Scale this vector by a scalar.
void Normalize(vec2D &v) const
Write a normalized copy of this vector to v.
void Set(float x_value, float y_value)
Set both vector coordinates.
void Normalize()
Normalize this vector in place, or reset it to zero if it is too short.
vec2D & operator+=(const vec2D &v)
Add another vector to this vector.
constexpr vec2D operator+(const vec2D &v) const
Add two vectors component-wise.
vec2D & operator-=(const vec2D &v)
Subtract another vector from this vector.
float Cos(const vec2D &v) const
Compute the cosine of the angle between this vector and another vector.
vec2D Scale(float k) const
Return a scaled copy of this vector.
std::string Print(const std::string &name="v") const
Format this vector as a named angle-bracket tuple.
constexpr vec2D(float x_value, float y_value)
Construct a vector from explicit coordinates.
vec2D & operator=(const vec2D &)=default
void ScaleThis(float k)
Scale this vector in place.
Three-dimensional float vector with arithmetic, dot, and cross-product helpers.
constexpr vec3D operator+(const vec3D &v) const
Add two vectors component-wise.
constexpr vec3D(float x_value, float y_value, float z_value)
Construct a vector from explicit coordinates.
vec3D Scale(float k) const
Return a scaled copy of this vector.
constexpr float DotProduct(const vec3D &v) const
Compute the dot product with another vector.
constexpr vec3D operator-(const vec3D &v) const
Subtract two vectors component-wise.
vec3D & operator=(const vec3D &)=default
constexpr vec3D operator*(float k) const
Scale this vector by a scalar.
vec3D & operator-=(const vec3D &v)
Subtract another vector from this vector.
constexpr vec3D()
Construct the zero vector.
constexpr vec3D CrossProduct(const vec3D &v) const
Compute the right-handed cross product with another vector.
float Cos(const vec3D &v) const
Compute the cosine of the angle between this vector and another vector.
void Set(float x_value, float y_value, float z_value)
Set all vector coordinates.
vec3D & operator+=(const vec3D &v)
Add another vector to this vector.
float Length() const
Compute the Euclidean length of this vector.
void Normalize(vec3D &v) const
Write a normalized copy of this vector to v.
void Normalize()
Normalize this vector in place, or reset it to zero if it is too short.
void ScaleThis(float k)
Scale this vector in place.
std::string Print(const std::string &name="v") const
Format this vector as a named angle-bracket tuple.
Four-dimensional float vector used for homogeneous 3D coordinates.
constexpr vec4D()
Construct the homogeneous origin.
float Length() const
Compute the 3D Euclidean length, ignoring the W component.
float Cos(const vec4D &v) const
Compute the cosine of the angle between the 3D components of two vectors.
float w
Homogeneous W coordinate.
void Normalize()
Normalize the 3D components in place and reset W to 1.
constexpr vec4D(float x_value, float y_value, float z_value, float w_value=1.0f)
Construct a homogeneous vector from explicit coordinates.
vec4D & operator-=(const vec4D &v)
Subtract another vector from this vector.
constexpr float DotProduct(const vec4D &v) const
Compute the 3D dot product, ignoring the W component.
void Normalize(vec4D &v) const
Write a normalized copy of this vector to v.
vec4D Build(const vec4D &from, const vec4D &to) const
Build a direction vector from from to to with W set to 1.
constexpr vec4D operator*(float k) const
Scale this vector by a scalar.
void Build(const vec4D &to)
Replace this vector with the direction from this point to to.
vec4D & operator+=(const vec4D &v)
Add another vector to this vector.
constexpr vec4D operator-(const vec4D &v) const
Subtract two vectors component-wise.
std::string Print(const std::string &name="v") const
Format this vector as a named angle-bracket tuple.
constexpr vec4D operator*(const vec4D &v) const
Multiply two vectors component-wise.
void Set(const vec4D &v)
Copy coordinates from another vector.
vec4D Scale(float k) const
Return a scaled copy of this vector.
void Set(float x_value, float y_value, float z_value, float w_value=1.0f)
Set all vector coordinates.
vec4D & operator=(const vec4D &)=default
constexpr vec4D operator+(const vec4D &v) const
Add two vectors component-wise.
constexpr vec4D CrossProduct(const vec4D &v) const
Compute the 3D cross product and return it with W set to 1.
void ScaleThis(float k)
Scale this vector in place.
Vulkan 2-D sprite renderer with optional custom shaders and instancing.
bool load_mtl_file(const std::string &path, std::vector< OBJMaterial > &materials, std::string &error)
bool load_obj_file(const std::string &path, OBJLoadResult &result, std::string &error)
Utilities for loading and saving PNG images.
int rrand(int x, int y)
Return a pseudo-random integer in the inclusive range between two bounds.
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.
std::array< float, 361 > cos_look
Cosine lookup table with one entry per degree from 0 through 360.
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.
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.
std::array< float, 361 > build_cos_table()
void draw_filled_triangle(const vec2D &p0, const vec2D &p1, const vec2D &p2, MXCOLOR color, PlotPixel &&plot_pixel)
Rasterize a filled triangle by testing pixels against edge functions.
std::ostream & operator<<(std::ostream &out, const vec2D &v)
Write a 2D vector to a stream using vec2D::Print().
constexpr float EPSILON
Default tolerance used for floating-point singularity and zero-length checks.
float deg2rad(float ang)
Convert degrees to radians.
float fast_cosf(float theta_degrees)
Approximate cosine using the degree lookup table with linear interpolation.
void draw_filled_triangle_spans(vec2D p0, vec2D p1, vec2D p2, MXCOLOR color, DrawSpan &&draw_span)
@ MX_ACTIVE
Active object or polygon.
@ MX_CULLED
Object or polygon is culled.
@ MX_BACKFACE
Polygon is marked as a backface.
@ MX_VISIBLE
Visible object or polygon.
constexpr std::uint8_t color_b(MXCOLOR color)
Extract the blue component from a packed ARGB color.
constexpr float PI
Mathematical constant pi as a single-precision value.
std::array< float, 361 > sin_look
Sine lookup table with one entry per degree from 0 through 360.
float fast_sinf(float theta_degrees)
Approximate sine using the degree lookup table with linear interpolation.
void draw_filled_triangle_spans_clipped(vec2D p0, vec2D p1, vec2D p2, int clip_min_y, int clip_max_y, MXCOLOR color, DrawSpan &&draw_span)
Rasterize a filled triangle as horizontal spans.
std::istream & operator>>(std::istream &in, vec2D &v)
Read a 2D vector from a stream as two scalar coordinates.
void draw_line(int x0, int y0, int x1, int y1, MXCOLOR color, PlotPixel &&plot_pixel)
Draw a line with Bresenham-style integer stepping.
float rad2deg(float rad)
Convert radians to degrees.
std::array< float, 361 > build_sin_table()
float z
Height coordinate.
float theta
Azimuth angle in degrees for this framework's math helpers.
Wavefront material data used by the software rasterizer.
std::array< float, 3 > diffuse
Plane represented by a point and a normal vector.
void Set(const vec3D &point, const vec3D &normal, bool normalize)
Set the plane point and normal, optionally normalizing the normal.
Plane3D()=default
Construct an uninitialized plane.
vec3D p0
Point on the plane.
Plane3D(const vec3D &point, const vec3D &normal)
Construct from a point and normal vector.
float theta
Angle in degrees for this framework's math helpers.
Pixel plotter adapter that writes packed MXVK colors to an SDL renderer.
void operator()(int x, int y, MXCOLOR color) const
Plot one pixel if the renderer is valid.
SDL_Renderer * renderer
SDL renderer receiving plotted pixels.
float theta
Azimuth angle in degrees for this framework's math helpers.
float phi
Inclination angle in degrees for this framework's math helpers.
Triangle primitive used by the simple software rendering pipeline.
std::string material_name
Wavefront material name retained for later MTL replacement.
int attr
Application-defined polygon attributes.
std::string source_object_name
Wavefront object name from the o section containing this triangle.
vec4D tlist[3]
Transformed vertex positions.
int material_index
Index of the OBJ/MTL material used by this triangle, or -1.
MXCOLOR color
Triangle color.
int state
Polygon state flags.
vec4D vlist[3]
Working vertex positions.
int vert[3]
Indices into an object's vertex arrays.
Pixel plotter adapter that draws square pixels into a VK_Sprite.
void operator()(int x, int y, MXCOLOR) const
Plot one square pixel if the sprite is valid.
int size
Square pixel size in sprite coordinates.
VK_Sprite * sprite
Sprite receiving plotted pixels.
std::vector< OBJMaterial > materials
std::string material_library_path
std::vector< OBJTriangle > triangles
std::array< OBJVertex, 3 > vertices
std::string material_name
std::array< float, 3 > position
std::array< float, 2 > texcoord
paramLine2D(const vec2D &start, const vec2D &end, const vec2D &dir)
Construct from explicit endpoints and direction.
paramLine2D()=default
Construct an uninitialized line segment.
vec2D ComputePoint(float t, vec2D &out) const
Compute the point p0 + v * t and write it to out.
void Set(const vec2D &start, const vec2D &end, const vec2D &dir)
Set explicit endpoints and direction.
int Intersect(const paramLine2D &line, vec2D &out) const
Intersect this segment with another segment and compute the point.
vec2D p0
Segment start point.
int Intersect(const paramLine2D &line, float &t_this, float &t_other) const
Intersect this segment with another parametric segment.
void Init(const vec2D &start, const vec2D &end)
Initialize from endpoints and derive the direction vector.
vec2D p1
Segment end point.
vec2D ComputePoint(float t) const
Compute the point p0 + v * t.
vec2D v
Direction vector, commonly p1 - p0.
vec3D ComputePoint(float t) const
Compute the point p0 + v * t.
vec3D ComputePoint(float t, vec3D &out) const
Compute the point p0 + v * t and write it to out.
paramLine3D()=default
Construct an uninitialized line segment.
vec3D p1
Segment end point.
paramLine3D(const vec3D &start, const vec3D &end, const vec3D &dir)
Construct from explicit endpoints and direction.
vec3D p0
Segment start point.
void Set(const vec3D &start, const vec3D &end, const vec3D &dir)
Set explicit endpoints and direction.
vec3D v
Direction vector, commonly p1 - p0.
void Init(const vec3D &start, const vec3D &end)
Initialize from endpoints and derive the direction vector.