skip to content
==> src/doom.c <==
1/* programs/doom's CPU reference (mesh-ref lib.rs `cast`, `height`, `doom_map`, `Pose`). */
2#include "mesh/ref/doom.h"
3
4#include "mesh/abi/view_doom.h"
5#include "mesh/ftz.h"
6
7/* f32 -> i32 as Rust's `as` and cvt.rzi: truncation, saturating, NaN to 0. */
8static int32_t to_i32(float x) {
9    if (mesh_ftz_is_nan(x)) return 0;
10    if (x >= 2147483648.0f) return INT32_MAX;
11    if (x <= -2147483648.0f) return INT32_MIN;
12    return (int32_t)x;
13}
14
15uint32_t mesh_doom_cast(const uint32_t map[256], float px, float py, float dx, float dy, float plx, float ply,
16                        uint32_t c, uint32_t max_steps, uint32_t *side_out, float *dist) {
17    float cam = mesh_ftz_sub(mesh_ftz_div(mesh_ftz_mul(2.0f, mesh_ftz_from_u32(c)), mesh_ftz_from_u32(MESH_DOOM_WIDTH)), 1.0f);
18    float rx = mesh_ftz_add(dx, mesh_ftz_mul(plx, cam));
19    float ry = mesh_ftz_add(dy, mesh_ftz_mul(ply, cam));
20    /* cvt.rzi.ftz.s32.f32: a subnormal truncates to 0 flushed or not. */
21    int32_t mx = to_i32(px), my = to_i32(py);
22    float ddx = mesh_ftz_eq(rx, 0.0f) ? 1e30f : mesh_ftz_abs(mesh_ftz_div(1.0f, rx));
23    float ddy = mesh_ftz_eq(ry, 0.0f) ? 1e30f : mesh_ftz_abs(mesh_ftz_div(1.0f, ry));
24    int32_t sx, sy;
25    float sdx, sdy;
26    if (mesh_ftz_lt(rx, 0.0f))
27        sx = -1, sdx = mesh_ftz_mul(mesh_ftz_sub(px, mesh_ftz_from_i32(mx)), ddx);
28    else
29        sx = 1, sdx = mesh_ftz_mul(mesh_ftz_sub(mesh_ftz_add(mesh_ftz_from_i32(mx), 1.0f), px), ddx);
30    if (mesh_ftz_lt(ry, 0.0f))
31        sy = -1, sdy = mesh_ftz_mul(mesh_ftz_sub(py, mesh_ftz_from_i32(my)), ddy);
32    else
33        sy = 1, sdy = mesh_ftz_mul(mesh_ftz_sub(mesh_ftz_add(mesh_ftz_from_i32(my), 1.0f), py), ddy);
34    uint32_t side = 0;
35    for (uint32_t i = 0; i < max_steps; i++) {
36        if (mesh_ftz_lt(sdx, sdy)) {
37            sdx = mesh_ftz_add(sdx, ddx);
38            mx += sx;
39            side = 0;
40        } else {
41            sdy = mesh_ftz_add(sdy, ddy);
42            my += sy;
43            side = 1;
44        }
45        if (mx < 0 || mx >= 16 || my < 0 || my >= 16) break;
46        uint32_t k = map[my * 16 + mx] & 15;
47        if (k != 0) {
48            *side_out = side;
49            *dist = side == 0 ? mesh_ftz_sub(sdx, ddx) : mesh_ftz_sub(sdy, ddy);
50            return k;
51        }
52    }
53    *side_out = side;
54    *dist = 1e30f;
55    return 0;
56}
57
58float mesh_doom_height(uint32_t kind, float dist) {
59    float rows = MESH_DOOM_ROWS;
60    if (kind == 0) return 0.0f;
61    float h = mesh_ftz_div(rows, dist);
62    float cap = mesh_ftz_mul(4.0f, rows);
63    return mesh_ftz_gt(h, cap) || mesh_ftz_is_nan(h) ? cap : h;
64}
65
66void mesh_doom_map(uint32_t out[256]) {
67    for (uint32_t c = 0; c < 256; c++) out[c] = mesh_doom_map_cell(c % 16, c / 16);
68}
69
70mesh_doom_pose mesh_doom_pose_start(void) {
71    mesh_doom_pose pose;
72    for (int i = 0; i < 6; i++) pose.p[i] = MESH_DOOM_START[i];
73    return pose;
74}
75
76bool mesh_doom_walkable(float x, float y) {
77    return mesh_ftz_ge(x, 0.0f) && mesh_ftz_ge(y, 0.0f) && mesh_ftz_lt(x, 16.0f) && mesh_ftz_lt(y, 16.0f) &&
78           mesh_doom_map_cell((size_t)to_i32(x), (size_t)to_i32(y)) == 0;
79}
80
81/* The radius's sign is the sign bit (copysign: no flush), as doom's `sign`. */
82static float sign(float v) { return (mesh_ftz_bits(v) >> 31) != 0 ? -1.0f : 1.0f; }
83
84void mesh_doom_pose_step(mesh_doom_pose *pose, float dx, float dy) {
85    float x = pose->p[0], y = pose->p[1];
86    if (mesh_doom_walkable(mesh_ftz_add(mesh_ftz_add(x, dx), mesh_ftz_mul(MESH_DOOM_RADIUS, sign(dx))), y))
87        pose->p[0] = mesh_ftz_add(x, dx);
88    x = pose->p[0];
89    if (mesh_doom_walkable(x, mesh_ftz_add(mesh_ftz_add(y, dy), mesh_ftz_mul(MESH_DOOM_RADIUS, sign(dy)))))
90        pose->p[1] = mesh_ftz_add(y, dy);
91}
92
93void mesh_doom_pose_rotate(mesh_doom_pose *pose, float s) {
94    float c = MESH_DOOM_TURN_COS;
95    for (int i = 2; i <= 4; i += 2) {
96        float x = pose->p[i], y = pose->p[i + 1];
97        pose->p[i] = mesh_ftz_sub(mesh_ftz_mul(x, c), mesh_ftz_mul(y, s));
98        pose->p[i + 1] = mesh_ftz_add(mesh_ftz_mul(x, s), mesh_ftz_mul(y, c));
99    }
100}
101
102void mesh_doom_pose_apply(mesh_doom_pose *pose, uint8_t key) {
103    float dx = mesh_ftz_mul(pose->p[2], MESH_DOOM_MOVE), dy = mesh_ftz_mul(pose->p[3], MESH_DOOM_MOVE);
104    float px = pose->p[4], py = pose->p[5];
105    switch (key) {
106    case 'w': mesh_doom_pose_step(pose, dx, dy); break;
107    case 's': mesh_doom_pose_step(pose, mesh_ftz_neg(dx), mesh_ftz_neg(dy)); break;
108    case 'a': mesh_doom_pose_rotate(pose, mesh_ftz_neg(MESH_DOOM_TURN_SIN)); break;
109    case 'd': mesh_doom_pose_rotate(pose, MESH_DOOM_TURN_SIN); break;
110    case 'q':
111        mesh_doom_pose_step(pose, mesh_ftz_mul(mesh_ftz_div(mesh_ftz_neg(px), MESH_DOOM_PLANE), MESH_DOOM_MOVE),
112                            mesh_ftz_mul(mesh_ftz_div(mesh_ftz_neg(py), MESH_DOOM_PLANE), MESH_DOOM_MOVE));
113        break;
114    case 'e':
115        mesh_doom_pose_step(pose, mesh_ftz_mul(mesh_ftz_div(px, MESH_DOOM_PLANE), MESH_DOOM_MOVE),
116                            mesh_ftz_mul(mesh_ftz_div(py, MESH_DOOM_PLANE), MESH_DOOM_MOVE));
117        break;
118    default: break;
119    }
120}