wasmcart 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,210 @@
1
+ /*
2
+ * wc_mat4.h - 4x4 matrix operations for wasmcart GL carts
3
+ *
4
+ * Single-header library providing column-major 4x4 matrix operations
5
+ * for OpenGL-style rendering. Uses wc_math.h for trig functions.
6
+ *
7
+ * USAGE:
8
+ * #include "wc_math.h"
9
+ * #include "wc_mat4.h"
10
+ *
11
+ * All functions are static inline. No separate .c file needed.
12
+ *
13
+ * CONVENTIONS:
14
+ * - Column-major layout (OpenGL convention)
15
+ * - mat4[col*4 + row], e.g. mat4[12] = translation X
16
+ * - Right-handed coordinate system
17
+ * - Angles in radians unless noted (wc_mat4_perspective takes radians)
18
+ * - wc_mat4_rotate takes degrees (matches glRotatef convention)
19
+ */
20
+
21
+ #ifndef WC_MAT4_H
22
+ #define WC_MAT4_H
23
+
24
+ #ifndef WC_MATH_H
25
+ #error "Include wc_math.h before wc_mat4.h"
26
+ #endif
27
+
28
+ /* ── Type ─────────────────────────────────────────────────────────── */
29
+
30
+ typedef float wc_mat4[16];
31
+
32
+ /* ── Identity ─────────────────────────────────────────────────────── */
33
+
34
+ static inline void wc_mat4_identity(wc_mat4 m) {
35
+ for (int i = 0; i < 16; i++) m[i] = 0.0f;
36
+ m[0] = m[5] = m[10] = m[15] = 1.0f;
37
+ }
38
+
39
+ static inline int wc_mat4_is_identity(const wc_mat4 m) {
40
+ const float e = 0.0001f;
41
+ for (int i = 0; i < 16; i++) {
42
+ float expected = (i == 0 || i == 5 || i == 10 || i == 15) ? 1.0f : 0.0f;
43
+ float d = m[i] - expected;
44
+ if (d > e || d < -e) return 0;
45
+ }
46
+ return 1;
47
+ }
48
+
49
+ /* ── Copy ─────────────────────────────────────────────────────────── */
50
+
51
+ static inline void wc_mat4_copy(wc_mat4 dst, const wc_mat4 src) {
52
+ for (int i = 0; i < 16; i++) dst[i] = src[i];
53
+ }
54
+
55
+ /* ── Multiply: out = a * b ────────────────────────────────────────── */
56
+ /* out may alias a or b safely (uses temp buffer) */
57
+
58
+ static inline void wc_mat4_multiply(wc_mat4 out, const wc_mat4 a, const wc_mat4 b) {
59
+ wc_mat4 tmp;
60
+ for (int c = 0; c < 4; c++)
61
+ for (int r = 0; r < 4; r++) {
62
+ tmp[c*4+r] = 0.0f;
63
+ for (int k = 0; k < 4; k++)
64
+ tmp[c*4+r] += a[k*4+r] * b[c*4+k];
65
+ }
66
+ for (int i = 0; i < 16; i++) out[i] = tmp[i];
67
+ }
68
+
69
+ /* ── Transform operations (modify m in-place, post-multiply) ───── */
70
+
71
+ /* Translate: m = m * T(x,y,z) */
72
+ static inline void wc_mat4_translate(wc_mat4 m, float x, float y, float z) {
73
+ m[12] += m[0]*x + m[4]*y + m[8]*z;
74
+ m[13] += m[1]*x + m[5]*y + m[9]*z;
75
+ m[14] += m[2]*x + m[6]*y + m[10]*z;
76
+ }
77
+
78
+ /* Scale: m = m * S(x,y,z) */
79
+ static inline void wc_mat4_scale(wc_mat4 m, float x, float y, float z) {
80
+ m[0] *= x; m[1] *= x; m[2] *= x; m[3] *= x;
81
+ m[4] *= y; m[5] *= y; m[6] *= y; m[7] *= y;
82
+ m[8] *= z; m[9] *= z; m[10] *= z; m[11] *= z;
83
+ }
84
+
85
+ /* Rotate: m = m * R(angle_deg, ax, ay, az) - matches glRotatef */
86
+ static inline void wc_mat4_rotate(wc_mat4 m, float angle_deg, float ax, float ay, float az) {
87
+ float rad = angle_deg * WC_DEG2RAD;
88
+ float c = wc_cosf(rad), s = wc_sinf(rad), t = 1.0f - c;
89
+ /* Normalize axis */
90
+ float len = ax*ax + ay*ay + az*az;
91
+ if (len > 0.0001f) {
92
+ float il = 1.0f / wc_sqrtf(len);
93
+ ax *= il; ay *= il; az *= il;
94
+ }
95
+ wc_mat4 rot;
96
+ rot[0] = t*ax*ax+c; rot[4] = t*ax*ay-s*az; rot[8] = t*ax*az+s*ay; rot[12] = 0;
97
+ rot[1] = t*ax*ay+s*az; rot[5] = t*ay*ay+c; rot[9] = t*ay*az-s*ax; rot[13] = 0;
98
+ rot[2] = t*ax*az-s*ay; rot[6] = t*ay*az+s*ax; rot[10] = t*az*az+c; rot[14] = 0;
99
+ rot[3] = 0; rot[7] = 0; rot[11] = 0; rot[15] = 1;
100
+ wc_mat4 tmp;
101
+ wc_mat4_copy(tmp, m);
102
+ wc_mat4_multiply(m, tmp, rot);
103
+ }
104
+
105
+ /* ── Projection matrices (write from scratch, not in-place) ──────── */
106
+
107
+ /* Perspective projection: fovy in radians, outputs to m */
108
+ static inline void wc_mat4_perspective(wc_mat4 m, float fovy_rad, float aspect,
109
+ float znear, float zfar) {
110
+ float f = wc_cosf(fovy_rad * 0.5f) / wc_sinf(fovy_rad * 0.5f);
111
+ for (int i = 0; i < 16; i++) m[i] = 0.0f;
112
+ m[0] = f / aspect;
113
+ m[5] = f;
114
+ m[10] = (zfar + znear) / (znear - zfar);
115
+ m[11] = -1.0f;
116
+ m[14] = (2.0f * zfar * znear) / (znear - zfar);
117
+ }
118
+
119
+ /* Perspective projection: fovy in degrees (matches gluPerspective) */
120
+ static inline void wc_mat4_perspective_deg(wc_mat4 m, float fovy_deg, float aspect,
121
+ float znear, float zfar) {
122
+ wc_mat4_perspective(m, fovy_deg * WC_DEG2RAD, aspect, znear, zfar);
123
+ }
124
+
125
+ /* Orthographic projection */
126
+ static inline void wc_mat4_ortho(wc_mat4 m, float left, float right,
127
+ float bottom, float top,
128
+ float znear, float zfar) {
129
+ for (int i = 0; i < 16; i++) m[i] = 0.0f;
130
+ m[0] = 2.0f / (right - left);
131
+ m[5] = 2.0f / (top - bottom);
132
+ m[10] = -2.0f / (zfar - znear);
133
+ m[12] = -(right + left) / (right - left);
134
+ m[13] = -(top + bottom) / (top - bottom);
135
+ m[14] = -(zfar + znear) / (zfar - znear);
136
+ m[15] = 1.0f;
137
+ }
138
+
139
+ /* ── View matrix ──────────────────────────────────────────────────── */
140
+
141
+ /* LookAt: build a view matrix (matches gluLookAt) */
142
+ static inline void wc_mat4_look_at(wc_mat4 m,
143
+ float eye_x, float eye_y, float eye_z,
144
+ float center_x, float center_y, float center_z,
145
+ float up_x, float up_y, float up_z) {
146
+ /* Forward = normalize(center - eye) */
147
+ float fx = center_x - eye_x, fy = center_y - eye_y, fz = center_z - eye_z;
148
+ float fl = wc_sqrtf(fx*fx + fy*fy + fz*fz);
149
+ if (fl > 0.0001f) { fx /= fl; fy /= fl; fz /= fl; }
150
+
151
+ /* Side = normalize(forward x up) */
152
+ float sx = fy*up_z - fz*up_y;
153
+ float sy = fz*up_x - fx*up_z;
154
+ float sz = fx*up_y - fy*up_x;
155
+ float sl = wc_sqrtf(sx*sx + sy*sy + sz*sz);
156
+ if (sl > 0.0001f) { sx /= sl; sy /= sl; sz /= sl; }
157
+
158
+ /* Recomputed up = side x forward */
159
+ float ux = sy*fz - sz*fy;
160
+ float uy = sz*fx - sx*fz;
161
+ float uz = sx*fy - sy*fx;
162
+
163
+ for (int i = 0; i < 16; i++) m[i] = 0.0f;
164
+ m[0] = sx; m[4] = sy; m[8] = sz;
165
+ m[1] = ux; m[5] = uy; m[9] = uz;
166
+ m[2] = -fx; m[6] = -fy; m[10] = -fz;
167
+ m[12] = -(sx*eye_x + sy*eye_y + sz*eye_z);
168
+ m[13] = -(ux*eye_x + uy*eye_y + uz*eye_z);
169
+ m[14] = (fx*eye_x + fy*eye_y + fz*eye_z);
170
+ m[15] = 1.0f;
171
+ }
172
+
173
+ /* ── Single-axis rotation helpers (write from scratch) ────────────── */
174
+
175
+ static inline void wc_mat4_rotate_x(wc_mat4 m, float angle_rad) {
176
+ wc_mat4_identity(m);
177
+ float c = wc_cosf(angle_rad), s = wc_sinf(angle_rad);
178
+ m[5] = c; m[6] = s;
179
+ m[9] = -s; m[10] = c;
180
+ }
181
+
182
+ static inline void wc_mat4_rotate_y(wc_mat4 m, float angle_rad) {
183
+ wc_mat4_identity(m);
184
+ float c = wc_cosf(angle_rad), s = wc_sinf(angle_rad);
185
+ m[0] = c; m[2] = -s;
186
+ m[8] = s; m[10] = c;
187
+ }
188
+
189
+ static inline void wc_mat4_rotate_z(wc_mat4 m, float angle_rad) {
190
+ wc_mat4_identity(m);
191
+ float c = wc_cosf(angle_rad), s = wc_sinf(angle_rad);
192
+ m[0] = c; m[1] = s;
193
+ m[4] = -s; m[5] = c;
194
+ }
195
+
196
+ /* ── Translation helper (write from scratch) ──────────────────────── */
197
+
198
+ static inline void wc_mat4_from_translation(wc_mat4 m, float x, float y, float z) {
199
+ wc_mat4_identity(m);
200
+ m[12] = x; m[13] = y; m[14] = z;
201
+ }
202
+
203
+ /* ── Scale helper (write from scratch) ────────────────────────────── */
204
+
205
+ static inline void wc_mat4_from_scale(wc_mat4 m, float x, float y, float z) {
206
+ wc_mat4_identity(m);
207
+ m[0] = x; m[5] = y; m[10] = z;
208
+ }
209
+
210
+ #endif /* WC_MAT4_H */
@@ -0,0 +1,116 @@
1
+ /*
2
+ * wc_math.h - Math functions without libm for wasmcart carts
3
+ *
4
+ * Single-header library providing trig, sqrt, atan2, and utility math
5
+ * functions that don't require linking against libm. Useful for WASM
6
+ * carts compiled with -nostdlib or minimal libc.
7
+ *
8
+ * USAGE:
9
+ * #include "wc_math.h"
10
+ *
11
+ * All functions are static inline, so just include this header wherever
12
+ * you need math. No separate .c file needed. No link dependencies.
13
+ *
14
+ * ACCURACY:
15
+ * - wc_sinf/wc_cosf: ~0.001 max error (Bhaskara I approximation)
16
+ * - wc_sqrtf: ~1e-6 relative error (10 Newton-Raphson iterations)
17
+ * - wc_atan2f: ~0.01 max error (polynomial approximation)
18
+ * - wc_tanf: limited near pi/2 (clamped denominator)
19
+ *
20
+ * If you need full libm precision, link against libm instead.
21
+ */
22
+
23
+ #ifndef WC_MATH_H
24
+ #define WC_MATH_H
25
+
26
+ /* ── Constants ────────────────────────────────────────────────────── */
27
+
28
+ #define WC_PI 3.14159265358979f
29
+ #define WC_TWO_PI 6.28318530717959f
30
+ #define WC_HALF_PI 1.57079632679490f
31
+ #define WC_DEG2RAD (WC_PI / 180.0f)
32
+ #define WC_RAD2DEG (180.0f / WC_PI)
33
+
34
+ /* ── Basic utilities ──────────────────────────────────────────────── */
35
+
36
+ static inline float wc_fabsf(float x) {
37
+ return x < 0.0f ? -x : x;
38
+ }
39
+
40
+ static inline float wc_fmodf(float a, float b) {
41
+ return a - (int)(a / b) * b;
42
+ }
43
+
44
+ static inline float wc_floorf(float x) {
45
+ int i = (int)x;
46
+ return (float)(x < (float)i ? i - 1 : i);
47
+ }
48
+
49
+ static inline float wc_clampf(float v, float lo, float hi) {
50
+ if (v < lo) return lo;
51
+ if (v > hi) return hi;
52
+ return v;
53
+ }
54
+
55
+ static inline float wc_lerpf(float a, float b, float t) {
56
+ return a + (b - a) * t;
57
+ }
58
+
59
+ static inline float wc_signf(float x) {
60
+ return x > 0.0f ? 1.0f : (x < 0.0f ? -1.0f : 0.0f);
61
+ }
62
+
63
+ static inline float wc_minf(float a, float b) { return a < b ? a : b; }
64
+ static inline float wc_maxf(float a, float b) { return a > b ? a : b; }
65
+
66
+ /* ── Trigonometry (Bhaskara I approximation) ──────────────────────── */
67
+
68
+ static inline float wc_sinf(float x) {
69
+ x = wc_fmodf(x, WC_TWO_PI);
70
+ if (x > WC_PI) x -= WC_TWO_PI;
71
+ if (x < -WC_PI) x += WC_TWO_PI;
72
+ float a = wc_fabsf(x);
73
+ return 16.0f * x * (WC_PI - a) /
74
+ (5.0f * WC_PI * WC_PI - 4.0f * x * (WC_PI - a));
75
+ }
76
+
77
+ static inline float wc_cosf(float x) {
78
+ return wc_sinf(x + WC_HALF_PI);
79
+ }
80
+
81
+ static inline float wc_tanf(float x) {
82
+ float c = wc_cosf(x);
83
+ if (wc_fabsf(c) < 0.0001f) c = 0.0001f;
84
+ return wc_sinf(x) / c;
85
+ }
86
+
87
+ /* ── Square root (Newton-Raphson, 10 iterations) ──────────────────── */
88
+
89
+ static inline float wc_sqrtf(float x) {
90
+ if (x <= 0.0f) return 0.0f;
91
+ float g = x * 0.5f;
92
+ for (int i = 0; i < 10; i++)
93
+ g = 0.5f * (g + x / g);
94
+ return g;
95
+ }
96
+
97
+ /* ── Atan2 (polynomial approximation) ─────────────────────────────── */
98
+
99
+ static inline float wc_atan2f(float y, float x) {
100
+ if (x == 0.0f) {
101
+ if (y > 0.0f) return WC_HALF_PI;
102
+ if (y < 0.0f) return -WC_HALF_PI;
103
+ return 0.0f;
104
+ }
105
+ float abs_x = wc_fabsf(x);
106
+ float abs_y = wc_fabsf(y);
107
+ float a = (abs_x < abs_y) ? abs_x / abs_y : abs_y / abs_x;
108
+ float s = a * a;
109
+ float r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a;
110
+ if (abs_y > abs_x) r = WC_HALF_PI - r;
111
+ if (x < 0.0f) r = WC_PI - r;
112
+ if (y < 0.0f) r = -r;
113
+ return r;
114
+ }
115
+
116
+ #endif /* WC_MATH_H */