Files
slinky/src/engine/math/math_helpers.cpp
T
2026-11-05 19:45:34 +01:00

229 lines
10 KiB
C++

// 0xa000: Rot / Rect / float helpers, random numbers
#include "slinky/engine.h"
struct Rot { float yaw, pitch; };
struct Rect { float x, y, w, h; };
FN(float, libc_roundf, (float x));
FN(float, libc_fmodf, (float x, float y));
FN(double, time_now, ());
extern "C" float full_turn_degrees, half_turn_degrees_b, offset_neg_360, threshold_neg_180, threshold_1_in_math_helpers_a, three_constant;
extern "C" double current_time_seconds;
extern "C" __attribute__((noinline)) Rot rot_make_floats(float a, float b) { return Rot{a, b}; }
extern "C" Rect make_rect(float x, float y, float w, float h) { return Rect{x, y, w, h}; }
extern "C" bool point_in_rect(Rect *r, Rot p) {
return p.yaw >= r->x && p.pitch >= r->y && r->x + r->w > p.yaw && r->y + r->h > p.pitch;
}
extern "C" bool rects_intersect(Rect *a, Rect *b) {
if (a->x > b->x + b->w || b->x > a->x + a->w) return false;
if (a->y > b->y + b->h) return false;
return !(b->y > a->y + a->h);
}
typedef float v2f __attribute__((ext_vector_type(2)));
extern "C" Rect inset_rect(Rect r, float d) {
v2f pos = {r.x, r.y}, size = {r.w, r.h};
v2f dd = {d, d};
float d2 = d + d;
v2f d22 = {d2, d2};
pos += dd;
size -= d22;
return Rect{pos.x, pos.y, size.x, size.y};
}
extern "C" Rect round_rect(Rect *r) {
float x0 = r->x, y0 = r->y;
float x = libc_roundf(x0), y = libc_roundf(y0), w = libc_roundf(r->w), h = libc_roundf(r->h);
return Rect{x, y, w, h};
}
extern "C" Rot offset_rect(Rot *p) { return *p; }
extern "C" float limit_rotation_delta(float x, float a, float b) { return (b < x) ? x : (a < b ? a : b); }
extern "C" float wrap_angle(float x) {
float r = libc_fmodf(x, full_turn_degrees);
if (r >= half_turn_degrees_b) return r + offset_neg_360;
if (threshold_neg_180 > r) { r += full_turn_degrees; }
return r;
}
extern "C" float lerp_clamped_float(float a, float b, float t) {
float m = threshold_1_in_math_helpers_a < t ? threshold_1_in_math_helpers_a : t;
return a + m * (b - a);
}
extern "C" float ease_curve(float t) { float sq = t * t; float d = t + t; return (three_constant - d) * sq; }
extern "C" float lerp_unclamped_float(float a, float b, float t) { return a + (b - a) * t; }
extern "C" double lerp_double(double a, double b, double t) { return a + (b - a) * t; }
extern "C" int chunk_coordinate(int x) { return x >> 4; }
extern "C" int block_coordinate(int x) { return x << 4; }
extern "C" void update_frame_time() { current_time_seconds = time_now(); }
// ---- xoshiro128+ random numbers ----
extern "C" unsigned init_table[4];
extern "C" const float float_epsilon_unused_a, one_in_math_helpers_b, float_epsilon_unused_b, random_range_bound_1_in_math_helpers_c, random_walk_range_epsilon_a, random_range_bound_1_in_math_helpers_d, random_walk_range_epsilon_b;
extern "C" const double threshold_0_2, threshold_0_2_in_math_helpers;
typedef int v4i __attribute__((vector_size(16)));
typedef float v4f __attribute__((vector_size(16)));
extern "C" const v4i math_helpers_fabs_masks, math_helpers_fabs_masks_second; // fabs masks
static inline __attribute__((always_inline)) unsigned rng_next() {
unsigned *s = init_table;
unsigned result = s[0] + s[3];
unsigned t = s[1] << 9;
s[2] ^= s[0];
s[3] ^= s[1];
s[1] ^= s[2];
s[0] ^= s[3];
s[2] ^= t;
s[3] = (s[3] << 11) | (s[3] >> 21);
return result;
}
static inline __attribute__((always_inline)) float rng_range(Rot rotation, float scale) {
float d = rotation.pitch - rotation.yaw;
return (float)(rng_next() >> 8) * scale * d + rotation.yaw;
}
static inline __attribute__((always_inline)) float fabs_m(float x, v4i m) {
v4f v = {x, 0, 0, 0};
return ((v4f)((v4i)v & m))[0];
}
extern "C" float random_rotation(Rot rotation) { return rng_range(rotation, float_epsilon_unused_a); }
extern "C" bool random_chance(float p) { return p > rng_range(rot_make_floats(0.0f, one_in_math_helpers_b), float_epsilon_unused_b); }
struct Walk { double t0; float a, b; };
extern "C" __attribute__((noinline)) void walk_randomize(Walk *w, bool both) {
do {
if (both) w->a = rng_range(rot_make_floats(0.0f, random_range_bound_1_in_math_helpers_d), random_walk_range_epsilon_b);
w->b = rng_range(rot_make_floats(0.0f, random_range_bound_1_in_math_helpers_d), random_walk_range_epsilon_b);
} while (threshold_0_2_in_math_helpers > (double)fabs_m(w->a - w->b, math_helpers_fabs_masks_second));
}
// smooth random value in [r.yaw, r.pitch], re-targeted every `period` seconds
extern "C" __attribute__((noinline)) float walk_update(Walk *w, Rot rotation, double period) {
if (current_time_seconds > w->t0 + period) {
w->t0 = current_time_seconds;
if (w->a == 0.0f && w->b == 0.0f) {
walk_randomize(w, true);
} else {
w->a = w->b;
do {
w->b = rng_range(rot_make_floats(0.0f, random_range_bound_1_in_math_helpers_c), random_walk_range_epsilon_a);
} while (threshold_0_2 > (double)fabs_m(w->a - w->b, math_helpers_fabs_masks));
}
}
double t = (current_time_seconds - w->t0) / period;
double u = (double)(w->b - w->a) * t + (double)w->a;
return (float)((double)(rotation.pitch - rotation.yaw) * u + (double)rotation.yaw);
}
extern "C" const float random_range_bound_1_in_math_helpers_e, random_walk_range_epsilon_c, random_range_bound_1_in_math_helpers_f, random_walk_range_epsilon_d;
extern "C" const double threshold_0_2_in_math_helpers_a;
extern "C" const v4i abs_mask;
// same walk driven by an accumulated time step instead of the clock
extern "C" float walk_update_timed(Walk *w, Rot rotation, double period, double dt) {
w->t0 += dt;
if (w->t0 > period) {
w->t0 = 0.0;
if (w->a == 0.0f && w->b == 0.0f) {
walk_randomize(w, true);
} else {
w->a = w->b;
do {
w->b = rng_range(rot_make_floats(0.0f, random_range_bound_1_in_math_helpers_e), random_walk_range_epsilon_c);
} while (threshold_0_2_in_math_helpers_a > (double)fabs_m(w->a - w->b, abs_mask));
}
}
double t = w->t0 / period;
double u = (double)(w->b - w->a) * t + (double)w->a;
return (float)((double)(rotation.pitch - rotation.yaw) * u + (double)rotation.yaw);
}
extern "C" bool walk_should_change(Walk *w, Rot rotation, double period) {
float x = rng_range(rot_make_floats(0.0f, random_range_bound_1_in_math_helpers_f), random_walk_range_epsilon_d);
return walk_update(w, rotation, period) > x;
}
// Gaussian random number (Marsaglia polar method, second value cached)
FN(float, libc_hypotf_alt, (float a, float b));
FN(float, libc_logf, (float x)); // logf
FN(float, libc_sqrtf, (float x)); // sqrtf (errno path)
extern "C" const float factor_5_96046eneg_08_in_math_helpers_e, offset_neg_1, threshold_1_in_math_helpers, minus_two;
struct Gauss { float spare; bool has; };
extern "C" float gauss_random(Gauss *g) {
if (g->has) {
g->has = false;
return g->spare;
}
float u, v, s;
do {
u = (float)(rng_next() >> 8) * factor_5_96046eneg_08_in_math_helpers_e;
u = u + u + offset_neg_1;
v = (float)(rng_next() >> 8) * factor_5_96046eneg_08_in_math_helpers_e;
v = v + v + offset_neg_1;
s = libc_hypotf_alt(u, v);
} while (s >= threshold_1_in_math_helpers || s == 0.0f);
float x = libc_logf(s) * minus_two / s;
float m = x >= 0.0f ? __builtin_elementwise_sqrt(x) : libc_sqrtf(x);
g->spare = v * m;
g->has = true;
return u * m;
}
// define (or find) a class in `loader` and register its native methods
#include "slinky/jnienv.h"
FN(jclass, find_class, (jobject loader, JNIEnv *env, const char *name));
FN(jobject, class_loader_ref, (jobject loader));
extern "C" int define_class(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len,
const JNINativeMethod *methods, jclass *out) {
jclass cls = find_class(loader, env, name);
if (!cls) {
(*env)->ExceptionClear(env);
auto define = (*env)->DefineClass;
cls = define(env, name, class_loader_ref(loader), buf, len);
if (!cls) return 0x7d6;
}
if ((*env)->RegisterNatives(env, cls, methods, 1) != 0) return 0x7d1;
*out = cls;
return 0;
}
extern "C" int define_class_with_natives(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len,
const JNINativeMethod *methods, jint n, jclass *out) {
jclass cls = find_class(loader, env, name);
if (!cls) {
(*env)->ExceptionClear(env);
auto define = (*env)->DefineClass;
cls = define(env, name, class_loader_ref(loader), buf, len);
if (!cls) return 0x7d6;
}
if ((*env)->RegisterNatives(env, cls, methods, n) != 0) return 0x7d1;
*out = cls;
return 0;
}
// one xoshiro128+ step of the shared generator (out-of-line copy of rng_next)
extern "C" unsigned xoshiro128_step_shared_generator() { return rng_next(); }
// ---- JavaVM lookup: JNI_GetCreatedJavaVMs from the already loaded "jvm" module (one VM) ----
// 0x3e8: no jvm module, 0x3e9: no such export, 0x7d2: the call failed
extern "C" const char str_jvm[], str_jni_getcreatedjavavms[]; // "jvm", "JNI_GetCreatedJavaVMs"
FN(void *, kernel32_lookups, (const char *module));
FN(void *, get_proc_address, (void *module, const char *name));
typedef int (*GetCreatedJavaVMs)(void **vms, int n, int *count);
extern "C" int get_java_vm(void **vm) {
int err;
if (!vm) return 0x7d2;
*vm = nullptr;
void *jvm = kernel32_lookups(str_jvm);
if (!jvm) {
err = 0x3e8;
} else {
GetCreatedJavaVMs get = (GetCreatedJavaVMs)get_proc_address(jvm, str_jni_getcreatedjavavms);
if (!get) {
err = 0x3e9;
} else {
int count = 0;
void *created = nullptr;
int result = get(&created, 1, &count);
if (result == 0 && count >= 1 && created) {
*vm = created;
err = 0;
} else {
err = 0x7d2; // call failed or no VM has been created yet
}
}
}
SHARED_EPILOG();
return err;
}