==> 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}