22#include <unordered_map>
34 inline constexpr float PI = 3.14159265358979323846f;
50 return 0xFF000000u | ((
static_cast<MXCOLOR>(r) & 0xFFu) << 16u) | ((
static_cast<MXCOLOR>(g) & 0xFFu) << 8u) | (
static_cast<MXCOLOR>(b) & 0xFFu);
55 return static_cast<std::uint8_t
>((color >> 16u) & 0xFFu);
60 return static_cast<std::uint8_t
>((color >> 8u) & 0xFFu);
65 return static_cast<std::uint8_t
>(color & 0xFFu);
70 return static_cast<std::uint8_t
>((color >> 24u) & 0xFFu);
80 intensity = std::clamp(intensity, 0.0f, 1.0f);
81 const auto scale = [intensity](std::uint8_t component) {
82 return static_cast<int>(std::clamp(
static_cast<float>(component) * intensity, 0.0f, 255.0f));
91 std::array<float, 361> values{};
92 for (
int ang = 0; ang <= 360; ++ang) {
93 values[
static_cast<std::size_t
>(ang)] = std::sin(
static_cast<float>(ang) *
PI / 180.0f);
99 std::array<float, 361> values{};
100 for (
int ang = 0; ang <= 360; ++ang) {
101 values[
static_cast<std::size_t
>(ang)] = std::cos(
static_cast<float>(ang) *
PI / 180.0f);
114 std::cout <<
"mxvk_math: building trigonometric lookup tables\n";
115 for (
int ang = 0; ang <= 360; ++ang) {
116 const float theta =
static_cast<float>(ang) *
PI / 180.0f;
117 cos_look[
static_cast<std::size_t
>(ang)] = std::cos(theta);
118 sin_look[
static_cast<std::size_t
>(ang)] = std::sin(theta);
120 std::cout <<
"mxvk_math: trigonometric lookup tables ready (361 entries)\n";
124 [[nodiscard]]
inline float deg2rad(
float ang) {
125 return ang *
PI / 180.0f;
129 [[nodiscard]]
inline float rad2deg(
float rad) {
130 return rad * 180.0f /
PI;
138 [[nodiscard]]
inline float fast_cosf(
float theta_degrees) {
139 theta_degrees = std::fmod(theta_degrees, 360.0f);
140 if (theta_degrees < 0.0f) {
141 theta_degrees += 360.0f;
143 const int theta_int =
static_cast<int>(theta_degrees);
144 const float theta_frac = theta_degrees -
static_cast<float>(theta_int);
145 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)]);
153 [[nodiscard]]
inline float fast_sinf(
float theta_degrees) {
154 theta_degrees = std::fmod(theta_degrees, 360.0f);
155 if (theta_degrees < 0.0f) {
156 theta_degrees += 360.0f;
158 const int theta_int =
static_cast<int>(theta_degrees);
159 const float theta_frac = theta_degrees -
static_cast<float>(theta_int);
160 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)]);
169 [[nodiscard]]
inline int rrand(
int x,
int y) {
173 return x + (std::rand() % (y - x + 1));
189 constexpr vec2D(
float x_value,
float y_value) :
x(x_value),
y(y_value) {}
192 void Set(
float x_value,
float y_value) {
201 return {
x + v.
x,
y + v.
y};
213 return {
x - v.
x,
y - v.
y};
225 return {
x * k,
y * k};
241 return x * v.
x +
y * v.
y;
251 const float length =
Length();
273 [[nodiscard]] std::string
Print(
const std::string &name =
"v")
const {
274 std::ostringstream out;
275 out << name <<
'<' <<
x <<
',' <<
y <<
'>';
282 return out << v.
Print();
287 return in >> v.
x >> v.
y;
306 constexpr vec3D(
float x_value,
float y_value,
float z_value) :
x(x_value),
y(y_value),
z(z_value) {}
309 void Set(
float x_value,
float y_value,
float z_value) {
319 return {
x + v.
x,
y + v.
y,
z + v.
z};
332 return {
x - v.
x,
y - v.
y,
z - v.
z};
345 return {
x * k,
y * k,
z * k};
362 return x * v.
x +
y * v.
y +
z * v.
z;
367 return {
y * v.
z -
z * v.
y,
z * v.
x -
x * v.
z,
x * v.
y -
y * v.
x};
377 const float len =
Length();
398 [[nodiscard]] std::string
Print(
const std::string &name =
"v")
const {
399 std::ostringstream out;
400 out << name <<
'<' <<
x <<
',' <<
y <<
',' <<
z <<
'>';
407 return out << v.
Print();
412 return in >> v.
x >> v.
y >> v.
z;
431 constexpr vec4D() :
x(0.0f),
y(0.0f),
z(0.0f),
w(1.0f) {}
434 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) {}
437 void Set(
float x_value,
float y_value,
float z_value,
float w_value = 1.0f) {
453 return {
x + v.
x,
y + v.
y,
z + v.
z,
w + v.
w};
467 return {
x - v.
x,
y - v.
y,
z - v.
z,
w - v.
w};
481 return {
x * k,
y * k,
z * k,
w * k};
486 return {
x * v.
x,
y * v.
y,
z * v.
z,
w * v.
w};
504 return x * v.
x +
y * v.
y +
z * v.
z;
509 return {
y * v.
z -
z * v.
y,
z * v.
x -
x * v.
z,
x * v.
y -
y * v.
x, 1.0f};
519 const float len =
Length();
545 *
this =
Build(*
this, to);
550 return {to.
x - from.
x, to.
y - from.
y, to.
z - from.
z, 1.0f};
554 [[nodiscard]] std::string
Print(
const std::string &name =
"v")
const {
555 std::ostringstream out;
556 out << name <<
'<' <<
x <<
',' <<
y <<
',' <<
z <<
',' <<
w <<
'>';
563 return out << v.
Print();
568 return in >> v.
x >> v.
y >> v.
z >> v.
w;
581 constexpr Mat1D(
float m0,
float m1) :
mat{m0, m1} {}
584 void Set(
float m0,
float m1) {
600 constexpr Mat1x3D(
float m0,
float m1,
float m2) :
mat{m0, m1, m2} {}
613 constexpr Mat1x4D(
float m0,
float m1,
float m2,
float m3) :
mat{m0, m1, m2, m3} {}
633 Mat2D(
float m00,
float m01,
float m10,
float m11) {
634 Set(m00, m01, m10, m11);
638 void Set(
float m00,
float m01,
float m10,
float m11) {
647 Set(1.0f, 0.0f, 0.0f, 1.0f);
662 return {
mat[0][0] * m.
mat[0][0] +
mat[0][1] * m.
mat[1][0],
683 const float inv = 1.0f / d;
684 out.
Set(
mat[1][1] * inv, -
mat[0][1] * inv, -
mat[1][0] * inv,
mat[0][0] * inv);
716 Mat3D(
float m00,
float m01,
float m02,
float m10,
float m11,
float m12,
float m20,
float m21,
float m22) {
717 Set(m00, m01, m02, m10, m11, m12, m20, m21, m22);
721 void Set(
float m00,
float m01,
float m02,
float m10,
float m11,
float m12,
float m20,
float m21,
float m22) {
735 Set(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
741 for (
int r = 0; r < 3; ++r) {
742 for (
int c = 0; c < 3; ++c) {
743 for (
int k = 0; k < 3; ++k) {
744 out.
mat[r][c] +=
mat[r][k] * m.
mat[k][c];
753 return {in.
x *
mat[0][0] + in.
y *
mat[1][0] + in.
z *
mat[2][0],
755 in.
x *
mat[0][2] + in.
y *
mat[1][2] + in.
z *
mat[2][2]};
778 const float inv = 1.0f / d;
821 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) {
822 Set(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33);
826 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) {
847 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);
853 for (
int r = 0; r < 4; ++r) {
854 for (
int c = 0; c < 4; ++c) {
864 for (
int r = 0; r < 4; ++r) {
865 for (
int c = 0; c < 4; ++c) {
866 for (
int k = 0; k < 4; ++k) {
867 out.
mat[r][c] +=
mat[r][k] * m.
mat[k][c];
882 vec4D out(0.0f, 0.0f, 0.0f, 0.0f);
883 out.
x = in.
x *
mat[0][0] + in.
y *
mat[1][0] + in.
z *
mat[2][0] + in.
w *
mat[3][0];
884 out.
y = in.
x *
mat[0][1] + in.
y *
mat[1][1] + in.
z *
mat[2][1] + in.
w *
mat[3][1];
885 out.
z = in.
x *
mat[0][2] + in.
y *
mat[1][2] + in.
z *
mat[2][2] + in.
w *
mat[3][2];
886 out.
w = in.
x *
mat[0][3] + in.
y *
mat[1][3] + in.
z *
mat[2][3] + in.
w *
mat[3][3];
900 void MulVec(std::span<const vec4D> input, std::span<vec4D> output)
const {
901 if (input.size() != output.size()) {
902 throw std::invalid_argument(
"Mat4D::MulVec batch spans must have equal sizes");
904 for (std::size_t index = 0; index < input.size(); ++index) {
905 output[index] =
MulVec(input[index]);
912 return {r.
x, r.
y, r.
z};
927 for (
int r = 0; r < 4; ++r) {
928 for (
int c = 0; c < 4; ++c) {
934 for (
int c = 0; c < 4; ++c) {
936 for (
int r = c + 1; r < 4; ++r) {
937 if (std::fabs(a[r][c]) > std::fabs(a[pivot][c])) {
941 if (std::fabs(a[pivot][c]) <=
EPSILON) {
945 for (
int k = 0; k < 8; ++k) {
946 std::swap(a[c][k], a[pivot][k]);
949 const float inv_pivot = 1.0f / a[c][c];
950 for (
int k = 0; k < 8; ++k) {
951 a[c][k] *= inv_pivot;
953 for (
int r = 0; r < 4; ++r) {
957 const float factor = a[r][c];
958 for (
int k = 0; k < 8; ++k) {
959 a[r][k] -= factor * a[c][k];
964 for (
int r = 0; r < 4; ++r) {
965 for (
int c = 0; c < 4; ++c) {
966 out.
mat[r][c] = a[r][c + 4];
973 void BuildXYZ(
float theta_x,
float theta_y,
float theta_z) {
974 const float cx = std::cos(
deg2rad(theta_x));
975 const float sx = std::sin(
deg2rad(theta_x));
976 const float cy = std::cos(
deg2rad(theta_y));
977 const float sy = std::sin(
deg2rad(theta_y));
978 const float cz = std::cos(
deg2rad(theta_z));
979 const float sz = std::sin(
deg2rad(theta_z));
981 Mat4D mx(1, 0, 0, 0, 0, cx, sx, 0, 0, -sx, cx, 0, 0, 0, 0, 1);
982 Mat4D my(cy, 0, -sy, 0, 0, 1, 0, 0, sy, 0, cy, 0, 0, 0, 0, 1);
983 Mat4D mz(cz, sz, 0, 0, -sz, cz, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
984 *
this =
mx * my * mz;
1004 Set(start, end, dir);
1040 const float det =
v.x * line.
v.
y -
v.y * line.
v.
x;
1041 if (std::fabs(det) <=
EPSILON) {
1045 t_this = (delta.
x * line.
v.
y - delta.
y * line.
v.
x) / det;
1046 t_other = (delta.
x *
v.y - delta.
y *
v.x) / det;
1047 return (t_this >= 0.0f && t_this <= 1.0f && t_other >= 0.0f && t_other <= 1.0f) ? 1 : 2;
1057 float t_this = 0.0f;
1058 float t_other = 0.0f;
1059 const int result =
Intersect(line, t_this, t_other);
1083 Set(start, end, dir);
1188 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) {}
1192 return {
x + q.
x,
y + q.
y,
z + q.
z,
w + q.
w};
1197 return {
x - q.
x,
y - q.
y,
z - q.
z,
w - q.
w};
1202 return {
w * q.
x +
x * q.
w +
y * q.
z -
z * q.
y,
1203 w * q.
y -
x * q.
z +
y * q.
w +
z * q.
x,
1204 w * q.
z +
x * q.
y -
y * q.
x +
z * q.
w,
1205 w * q.
w -
x * q.
x -
y * q.
y -
z * q.
z};
1231 return w *
w +
x *
x +
y *
y +
z *
z;
1236 return std::sqrt(
Norm2());
1241 const float norm =
Norm();
1252 const float n2 =
Norm2();
1271 return (*
this * p1) * p2;
1278 const float half =
deg2rad(theta_degrees) * 0.5f;
1279 const float s = std::sin(half);
1293 void EulerZYX(
float theta_x,
float theta_y,
float theta_z) {
1294 const float hx =
deg2rad(theta_x) * 0.5f;
1295 const float hy =
deg2rad(theta_y) * 0.5f;
1296 const float hz =
deg2rad(theta_z) * 0.5f;
1297 const float cx = std::cos(hx);
1298 const float sx = std::sin(hx);
1299 const float cy = std::cos(hy);
1300 const float sy = std::sin(hy);
1301 const float cz = std::cos(hz);
1302 const float sz = std::sin(hz);
1303 w = cz * cy * cx + sz * sy * sx;
1304 x = cz * cy * sx - sz * sy * cx;
1305 y = cz * sy * cx + sz * cy * sx;
1306 z = sz * cy * cx - cz * sy * sx;
1313 const float s = std::sqrt(std::max(0.0f, 1.0f - q.
w * q.
w));
1315 axis.
Set(1.0f, 0.0f, 0.0f);
1317 axis.
Set(q.
x / s, q.
y / s, q.
z / s);
1319 if (theta_degrees !=
nullptr) {
1320 *theta_degrees =
rad2deg(2.0f * std::acos(std::clamp(q.
w, -1.0f, 1.0f)));
1390 for (
auto &poly :
polys) {
1391 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
1394 for (
auto &vertex : poly.vlist) {
1395 vertex = mrot.
MulVec(vertex);
1402 for (
auto &poly :
polys) {
1403 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
1421 for (
auto &poly :
polys) {
1422 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
1425 for (
int i = 0; i < 3; ++i) {
1426 poly.tlist[i] = poly.vlist[i] + pos;
1433 polys.push_back(triangle);
1508 std::cout <<
"mxvk_math: loading PLG model: " << path <<
'\n';
1509 const auto load_failed = [&path](
const char *reason) {
1510 std::cerr <<
"mxvk_math: failed to load PLG model '" << path <<
"': " << reason <<
'\n';
1514 std::ifstream file(path);
1515 if (!file.is_open()) {
1516 return load_failed(
"could not open file");
1519 const auto read_data_line = [&file](std::string &line) {
1520 while (std::getline(file, line)) {
1521 const std::size_t comment = line.find(
'#');
1522 if (comment != std::string::npos) {
1523 line.erase(comment);
1525 if (line.find_first_not_of(
" \t\r\n") != std::string::npos) {
1533 if (!read_data_line(line)) {
1534 return load_failed(
"missing model header");
1538 int vertex_count = 0;
1540 std::istringstream header(line);
1541 if (!(header >> name >> vertex_count >> poly_count) || vertex_count < 0 || poly_count < 0) {
1542 return load_failed(
"invalid model header");
1544 std::cout <<
"mxvk_math: PLG header parsed (object='" << name <<
"', vertices=" << vertex_count <<
", triangles=" << poly_count <<
")\n";
1546 std::vector<vec4D> loaded_local;
1547 std::vector<vec4D> loaded_trans;
1548 std::vector<vec2D> loaded_texcoords;
1549 std::vector<Triangle> loaded_vlist;
1550 loaded_local.reserve(
static_cast<std::size_t
>(vertex_count));
1551 loaded_trans.resize(
static_cast<std::size_t
>(vertex_count));
1552 loaded_texcoords.reserve(
static_cast<std::size_t
>(vertex_count));
1553 loaded_vlist.reserve(
static_cast<std::size_t
>(poly_count));
1555 for (
int i = 0; i < vertex_count; ++i) {
1556 if (!read_data_line(line)) {
1557 return load_failed(
"vertex data ended early");
1561 std::istringstream vertex_line(line);
1562 if (!(vertex_line >> vertex.
x >> vertex.
y >> vertex.
z) ||
1563 !std::isfinite(vertex.
x) || !std::isfinite(vertex.
y) || !std::isfinite(vertex.
z)) {
1564 return load_failed(
"invalid vertex data");
1568 if (vertex_line >> texcoord.
x) {
1569 if (!(vertex_line >> texcoord.
y) || !std::isfinite(texcoord.
x) || !std::isfinite(texcoord.
y)) {
1570 return load_failed(
"invalid texture coordinates");
1574 vertex.
x *= scale.
x;
1575 vertex.
y *= scale.
y;
1576 vertex.
z *= scale.
z;
1577 if (!std::isfinite(vertex.
x) || !std::isfinite(vertex.
y) || !std::isfinite(vertex.
z)) {
1578 return load_failed(
"scaled vertex is not finite");
1581 loaded_local.push_back(vertex);
1582 loaded_texcoords.push_back(texcoord);
1585 for (
int i = 0; i < poly_count; ++i) {
1586 if (!read_data_line(line)) {
1587 return load_failed(
"triangle data ended early");
1592 std::istringstream polygon_line(line);
1593 if (!(polygon_line >> std::hex >> tri.
state >> std::dec >> count) || count != 3 ||
1594 !(polygon_line >> tri.
vert[0] >> tri.
vert[1] >> tri.
vert[2])) {
1595 return load_failed(
"invalid triangle data");
1597 for (
const int index : tri.
vert) {
1598 if (index < 0 || index >= vertex_count) {
1599 return load_failed(
"triangle vertex index is out of range");
1602 loaded_vlist.push_back(tri);
1605 for (
auto &triangle : loaded_vlist) {
1612 local = std::move(loaded_local);
1613 trans = std::move(loaded_trans);
1614 texcoords = std::move(loaded_texcoords);
1615 vlist = std::move(loaded_vlist);
1619 std::cout <<
"mxvk_math: PLG model ready (object='" <<
object_name <<
"', average radius=" <<
avg_rad <<
", maximum radius=" <<
max_rad <<
")\n";
1625 return LoadPLG(path, scale, obj_pos, rotation);
1637 std::cout <<
"mxvk_math: loading OBJ model: " << path <<
'\n';
1641 std::cerr <<
"mxvk_math: failed to load OBJ model '" << path <<
"': " << error <<
'\n';
1645 std::unordered_map<std::string, int> material_indices;
1646 for (std::size_t index = 0; index < loaded.
materials.size(); ++index) {
1647 material_indices.emplace(loaded.
materials[index].name,
static_cast<int>(index));
1650 std::vector<vec4D> loaded_local;
1651 std::vector<vec2D> loaded_texcoords;
1652 std::vector<Triangle> loaded_vlist;
1653 loaded_local.reserve(loaded.
triangles.size() * 3);
1654 loaded_texcoords.reserve(loaded.
triangles.size() * 3);
1655 loaded_vlist.reserve(loaded.
triangles.size());
1662 const auto material = material_indices.find(source_triangle.
material_name);
1663 if (material != material_indices.end()) {
1665 triangle.
color = material_color(loaded.
materials[
static_cast<std::size_t
>(material->second)]);
1668 for (std::size_t vertex_index = 0; vertex_index < source_triangle.
vertices.size(); ++vertex_index) {
1670 const float x = source_vertex.
position[0] * scale.
x;
1671 const float y = source_vertex.
position[1] * scale.
y;
1672 const float z = source_vertex.
position[2] * scale.
z;
1673 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(z)) {
1674 std::cerr <<
"mxvk_math: failed to load OBJ model '" << path <<
"': scaled vertex is not finite\n";
1677 triangle.
vert[vertex_index] =
static_cast<int>(loaded_local.size());
1678 loaded_local.emplace_back(x, y, z, 1.0f);
1679 loaded_texcoords.emplace_back(source_vertex.
texcoord[0], source_vertex.
texcoord[1]);
1681 loaded_vlist.push_back(triangle);
1686 num_polys =
static_cast<int>(loaded_vlist.size());
1687 local = std::move(loaded_local);
1689 texcoords = std::move(loaded_texcoords);
1690 vlist = std::move(loaded_vlist);
1704 [[nodiscard]]
bool LoadMTL(
const std::string &path) {
1705 std::vector<OBJMaterial> loaded_materials;
1708 std::cerr <<
"mxvk_math: failed to load MTL file '" << path <<
"': " << error <<
'\n';
1712 std::unordered_map<std::string, int> material_indices;
1713 for (std::size_t index = 0; index < loaded_materials.size(); ++index) {
1714 material_indices.emplace(loaded_materials[index].name,
static_cast<int>(index));
1716 for (std::size_t index = 0; index <
vlist.size(); ++index) {
1717 const auto material = material_indices.find(
vlist[index].material_name);
1718 vlist[index].material_index = material == material_indices.end() ? -1 : material->second;
1719 if (material != material_indices.end()) {
1720 vlist[index].color = material_color(loaded_materials[
static_cast<std::size_t
>(material->second)]);
1724 materials = std::move(loaded_materials);
1733 for (std::size_t i = 0; i <
local.size(); ++i) {
1736 }
else if (type == 1) {
1737 for (
auto &vertex :
trans) {
1745 auto transform = [&mrot](std::vector<vec4D> &vertices) {
1746 for (
auto &vertex : vertices) {
1747 vertex = mrot.
MulVec(vertex);
1752 }
else if (type == 1) {
1754 }
else if (type == 2) {
1756 for (std::size_t i = 0; i <
local.size(); ++i) {
1764 for (
auto &poly :
vlist) {
1768 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()) {
1771 const vec4D u =
vec4D().
Build(
trans[
static_cast<std::size_t
>(poly.vert[0])],
trans[
static_cast<std::size_t
>(poly.vert[1])]);
1772 const vec4D v =
vec4D().
Build(
trans[
static_cast<std::size_t
>(poly.vert[0])],
trans[
static_cast<std::size_t
>(poly.vert[2])]);
1783 for (
const auto &poly :
vlist) {
1785 for (
int i = 0; i < 3; ++i) {
1786 const auto index =
static_cast<std::size_t
>(poly.vert[i]);
1787 if (index <
local.size()) {
1790 if (index <
trans.size()) {
1800 for (
auto &poly :
vlist) {
1810 if (
local.empty()) {
1813 for (
const auto &vertex :
local) {
1814 const float dist = std::sqrt(vertex.x * vertex.x + vertex.y * vertex.y + vertex.z * vertex.z);
1829 const auto channel = [](
float value) {
1830 return static_cast<MXCOLOR>(std::lround(std::clamp(value, 0.0f, 1.0f) * 255.0f));
1832 return (channel(material.
dissolve) << 24u) |
1833 (channel(material.
diffuse[0]) << 16u) |
1834 (channel(material.
diffuse[1]) << 8u) |
1922 mcam.LoadIdentity();
1923 mper.LoadIdentity();
1924 mscr.LoadIdentity();
1929 pos.Set(100.0f, 200.0f, 300.0f);
1930 dir.Set(-48.0f, 0.0f, 0.0f);
1935 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) {
1939 target = camera_target !=
nullptr ? *camera_target :
vec4D();
1951 mcam.LoadIdentity();
1952 mper.LoadIdentity();
1953 mscr.LoadIdentity();
1958 Mat4D translation(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -
pos.x, -
pos.y, -
pos.z, 1);
1961 mcam = translation * rotation;
1968 v.Set(0.0f, 1.0f, 0.0f);
1969 u =
v.CrossProduct(
n);
1971 v =
n.CrossProduct(
u);
1973 Mat4D translation(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -
pos.x, -
pos.y, -
pos.z, 1);
1974 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);
1975 mcam = translation * uvn;
1980 for (
auto &poly : list.
polys) {
1981 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
1984 for (
auto &vertex : poly.tlist) {
1985 vertex =
mcam.MulVec(vertex);
1992 for (
auto &vertex :
object.trans) {
1993 vertex =
mcam.MulVec(vertex);
1999 for (
auto &poly : list.
polys) {
2000 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
2003 for (
auto &vertex : poly.tlist) {
2004 if (std::fabs(vertex.z) <=
EPSILON) {
2007 vertex.x =
view_dist * vertex.x / vertex.z;
2015 for (
auto &vertex :
object.trans) {
2016 if (std::fabs(vertex.z) <=
EPSILON) {
2019 vertex.x =
view_dist * vertex.x / vertex.z;
2028 for (
auto &poly : list.
polys) {
2029 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
2032 for (
auto &vertex : poly.tlist) {
2033 vertex.x = alpha + alpha * vertex.x;
2034 vertex.y = beta - beta * vertex.y;
2043 for (
auto &vertex :
object.trans) {
2044 vertex.x = alpha + alpha * vertex.x;
2045 vertex.y = beta - beta * vertex.y;
2061 SDL_RenderPoint(
renderer,
static_cast<float>(x),
static_cast<float>(y));
2076 sprite->drawSpriteRect(x, y, std::max(1,
size), std::max(1,
size));
2091 template <
typename PlotPixel>
2093 const int dx = std::abs(x1 - x0);
2094 const int sx = x0 < x1 ? 1 : -1;
2095 const int dy = -std::abs(y1 - y0);
2096 const int sy = y0 < y1 ? 1 : -1;
2100 plot_pixel(x0, y0, color);
2101 if (x0 == x1 && y0 == y1) {
2104 const int e2 = 2 * err;
2128 return (p.
x - a.
x) * (b.
y - a.
y) - (p.
y - a.
y) * (b.
x - a.
x);
2140 template <
typename PlotPixel>
2143 if (std::fabs(area) <=
EPSILON) {
2147 const int min_x =
static_cast<int>(std::floor(std::min({p0.
x, p1.
x, p2.
x})));
2148 const int max_x =
static_cast<int>(std::ceil(std::max({p0.
x, p1.
x, p2.
x})));
2149 const int min_y =
static_cast<int>(std::floor(std::min({p0.
y, p1.
y, p2.
y})));
2150 const int max_y =
static_cast<int>(std::ceil(std::max({p0.
y, p1.
y, p2.
y})));
2152 for (
int y = min_y; y <= max_y; ++y) {
2153 for (
int x = min_x; x <= max_x; ++x) {
2154 const vec2D p(
static_cast<float>(x) + 0.5f,
static_cast<float>(y) + 0.5f);
2158 if ((area > 0.0f && w0 >= 0.0f && w1 >= 0.0f && w2 >= 0.0f) ||
2159 (area < 0.0f && w0 <= 0.0f && w1 <= 0.0f && w2 <= 0.0f)) {
2160 plot_pixel(x, y, color);
2187 template <
typename DrawSpan>
2199 [[nodiscard]]
float x_at(
int y)
const {
2200 return x0 +
static_cast<float>(y - y0) * slope;
2204 const int min_y = std::max(clip_min_y,
static_cast<int>(std::floor(std::min({p0.
y, p1.
y, p2.
y}))));
2205 const int max_y = std::min(clip_max_y,
static_cast<int>(std::ceil(std::max({p0.
y, p1.
y, p2.
y}))));
2206 if (min_y > max_y) {
2210 std::array<SpanEdge, 3> edges{};
2212 const auto add_edge = [&](
vec2D a,
vec2D b) {
2213 if (std::fabs(a.
y - b.y) <=
EPSILON) {
2220 const int y0 = std::max(min_y,
static_cast<int>(std::ceil(a.
y - 0.5f)));
2221 const int y1 = std::min(max_y,
static_cast<int>(std::ceil(b.y - 0.5f)) - 1);
2226 SpanEdge &edge = edges[
static_cast<std::size_t
>(edge_count++)];
2229 edge.slope = (b.x - a.
x) / (b.y - a.
y);
2230 edge.x0 = a.
x + ((
static_cast<float>(y0) + 0.5f) - a.
y) * edge.slope;
2237 for (
int y = min_y; y <= max_y; ++y) {
2238 std::array<float, 3> intersections{};
2239 int intersection_count = 0;
2241 for (
int edge_index = 0; edge_index < edge_count; ++edge_index) {
2242 const SpanEdge &edge = edges[
static_cast<std::size_t
>(edge_index)];
2243 if (y >= edge.y0 && y <= edge.y1) {
2244 intersections[
static_cast<std::size_t
>(intersection_count++)] = edge.x_at(y);
2248 if (intersection_count < 2) {
2252 float min_x = intersections[0];
2253 float max_x = intersections[0];
2254 for (
int i = 1; i < intersection_count; ++i) {
2255 min_x = std::min(min_x, intersections[
static_cast<std::size_t
>(i)]);
2256 max_x = std::max(max_x, intersections[
static_cast<std::size_t
>(i)]);
2259 const int x0 =
static_cast<int>(std::ceil(min_x - 0.5f));
2260 const int x1 =
static_cast<int>(std::floor(max_x - 0.5f));
2262 draw_span(x0, x1, y, color);
2267 template <
typename DrawSpan>
2318 void Begin(
int width,
int height, std::function<
void(
int,
int,
MXCOLOR)> plotter) {
2319 plot_pixel = std::move(plotter);
2320 plot_span = [
this](
int x0,
int x1,
int y,
MXCOLOR color) {
2321 for (
int x = x0; x <= x1; ++x) {
2322 plot_pixel(x, y, color);
2329 [[maybe_unused]]
static const bool PIPELINE_LOGGED = [width, height] {
2330 std::cout <<
"mxvk_math: software raster pipeline ready (" << width <<
'x' << height <<
")\n";
2336 void Begin(SDL_Renderer *renderer,
int width,
int height) {
2338 plot_span = [renderer](
int x0,
int x1,
int y,
MXCOLOR color) {
2339 if (renderer ==
nullptr) {
2343 SDL_RenderLine(renderer,
static_cast<float>(x0),
static_cast<float>(y),
static_cast<float>(x1),
static_cast<float>(y));
2350 plot_span = [&sprite, pixel_size](
int x0,
int x1,
int y,
MXCOLOR) {
2351 const int size = std::max(1, pixel_size);
2380 bool ClipLine(
int &x0,
int &y0,
int &x1,
int &y1)
const {
2385 if ((code0 | code1) == 0) {
2388 if ((code0 & code1) != 0) {
2392 const int out_code = code0 != 0 ? code0 : code1;
2396 if ((out_code &
CODE_N) != 0) {
2400 x = x0 + (x1 - x0) * (
clip_min_y - y0) / (y1 - y0);
2402 }
else if ((out_code &
CODE_S) != 0) {
2406 x = x0 + (x1 - x0) * (
clip_max_y - y0) / (y1 - y0);
2408 }
else if ((out_code &
CODE_E) != 0) {
2412 y = y0 + (y1 - y0) * (
clip_max_x - x0) / (x1 - x0);
2418 y = y0 + (y1 - y0) * (
clip_min_x - x0) / (x1 - x0);
2422 if (out_code == code0) {
2436 if (plot_pixel &&
ClipLine(x0, y0, x1, y1)) {
2437 draw_line(x0, y0, x1, y1, color, plot_pixel);
2457 !std::isfinite(first.
x) ||
2458 !std::isfinite(first.
y) ||
2459 !std::isfinite(first.
z) ||
2460 !std::isfinite(second.
x) ||
2461 !std::isfinite(second.
y) ||
2462 !std::isfinite(second.
z)) {
2466 const int framebuffer_width =
max_clip_x + 1;
2467 const int framebuffer_height =
max_clip_y + 1;
2468 const std::size_t required_depth_values =
2469 static_cast<std::size_t
>(framebuffer_width) *
static_cast<std::size_t
>(framebuffer_height);
2470 if (framebuffer_width <= 0 ||
2471 framebuffer_height <= 0 ||
2472 depth_buffer.size() < required_depth_values) {
2476 const float delta_x = second.
x - first.
x;
2477 const float delta_y = second.
y - first.
y;
2478 float first_fraction = 0.0f;
2479 float last_fraction = 1.0f;
2480 const auto clip_fraction = [&first_fraction, &last_fraction](
float direction,
float distance) {
2481 if (std::abs(direction) <=
EPSILON) {
2482 return distance >= 0.0f;
2485 const float fraction = distance / direction;
2486 if (direction < 0.0f) {
2487 first_fraction = std::max(first_fraction, fraction);
2489 last_fraction = std::min(last_fraction, fraction);
2491 return first_fraction <= last_fraction;
2494 if (!clip_fraction(-delta_x, first.
x -
static_cast<float>(
clip_min_x)) ||
2495 !clip_fraction(delta_x,
static_cast<float>(
clip_max_x) - first.
x) ||
2496 !clip_fraction(-delta_y, first.
y -
static_cast<float>(
clip_min_y)) ||
2497 !clip_fraction(delta_y,
static_cast<float>(
clip_max_y) - first.
y)) {
2501 const float clipped_delta_x = delta_x * (last_fraction - first_fraction);
2502 const float clipped_delta_y = delta_y * (last_fraction - first_fraction);
2503 const float step_count = std::ceil(std::max(std::abs(clipped_delta_x), std::abs(clipped_delta_y)));
2504 if (step_count >
static_cast<float>(std::numeric_limits<int>::max())) {
2507 const int steps = std::max(1,
static_cast<int>(step_count));
2508 const float first_reciprocal_depth = 1.0f / first.
z;
2509 const float second_reciprocal_depth = 1.0f / second.
z;
2511 for (
int step = 0; step <= steps; ++step) {
2512 const float clipped_fraction =
static_cast<float>(step) /
static_cast<float>(steps);
2513 const float fraction =
2515 (last_fraction - first_fraction) * clipped_fraction;
2516 const int x =
static_cast<int>(std::lround(first.
x + delta_x * fraction));
2517 const int y =
static_cast<int>(std::lround(first.
y + delta_y * fraction));
2522 const float reciprocal_depth =
2523 first_reciprocal_depth +
2524 (second_reciprocal_depth - first_reciprocal_depth) * fraction;
2525 if (reciprocal_depth <=
EPSILON) {
2529 const float depth = 1.0f / reciprocal_depth;
2530 const std::size_t pixel_index =
2531 static_cast<std::size_t
>(y) *
static_cast<std::size_t
>(framebuffer_width) +
2532 static_cast<std::size_t
>(x);
2533 if (depth >= depth_buffer[pixel_index]) {
2537 depth_buffer[pixel_index] = depth;
2538 plot_pixel(x, y, color);
2545 static_cast<int>(std::lround(first.
x)),
2546 static_cast<int>(std::lround(first.
y)),
2547 static_cast<int>(std::lround(second.
x)),
2548 static_cast<int>(std::lround(second.
y)),
2551 static_cast<int>(std::lround(second.
x)),
2552 static_cast<int>(std::lround(second.
y)),
2553 static_cast<int>(std::lround(third.
x)),
2554 static_cast<int>(std::lround(third.
y)),
2557 static_cast<int>(std::lround(third.
x)),
2558 static_cast<int>(std::lround(third.
y)),
2559 static_cast<int>(std::lround(first.
x)),
2560 static_cast<int>(std::lround(first.
y)),
2577 const int min_x =
static_cast<int>(std::floor(std::min({p0.
x, p1.
x, p2.
x})));
2578 const int max_x =
static_cast<int>(std::ceil(std::max({p0.
x, p1.
x, p2.
x})));
2579 const int min_y =
static_cast<int>(std::floor(std::min({p0.
y, p1.
y, p2.
y})));
2580 const int max_y =
static_cast<int>(std::ceil(std::max({p0.
y, p1.
y, p2.
y})));
2592 plot_span(x0, x1, y, pixel_color);
2604 for (
const auto &poly : list.
polys) {
2605 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
2614 for (
const auto &poly : list.
polys) {
2615 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
2624 for (
const auto &poly : list.
polys) {
2625 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
2637 for (
const auto &poly :
object.vlist) {
2638 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
2641 const auto a =
static_cast<std::size_t
>(poly.vert[0]);
2642 const auto b =
static_cast<std::size_t
>(poly.vert[1]);
2643 const auto c =
static_cast<std::size_t
>(poly.vert[2]);
2644 if (a >=
object.trans.size() || b >=
object.trans.size() || c >=
object.trans.size()) {
2656 for (
const auto &poly :
object.vlist) {
2657 if (poly.state == 0 || (poly.state &
MX_BACKFACE) != 0) {
2660 const auto a =
static_cast<std::size_t
>(poly.vert[0]);
2661 const auto b =
static_cast<std::size_t
>(poly.vert[1]);
2662 const auto c =
static_cast<std::size_t
>(poly.vert[2]);
2663 if (a >=
object.trans.size() || b >=
object.trans.size() || c >=
object.trans.size()) {
2672 plot_pixel =
nullptr;
2673 plot_span =
nullptr;
2677 std::function<void(
int,
int,
MXCOLOR)> plot_pixel;
2678 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.