101 lines
2.9 KiB
C
101 lines
2.9 KiB
C
/*
|
|
Doom-inspired Q15.16 format used for most of everything.
|
|
https://hackmd.io/9uRB9YbTSBW4Qz_2b8TyDw#Examples-2-DOOM
|
|
*/
|
|
|
|
#ifndef COM_FIXED_H
|
|
#define COM_FIXED_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
#define COM_FIXED_FRACBITS 16
|
|
#define COM_FIXED_FRACUNIT (1 << COM_FIXED_FRACBITS)
|
|
#define COM_FIXED_FRACHALF (COM_FIXED_FRACUNIT >> 1)
|
|
#define COM_FIXED_FRACQRTR (COM_FIXED_FRACHALF >> 1)
|
|
|
|
#define COM_FIXED_PI (com_fixed_t)205887
|
|
#define COM_FIXED_PI2 (com_fixed_t)411774
|
|
#define COM_FIXED_PIHALF (com_fixed_t)102943
|
|
#define COM_FIXED_PIONEANDAHALF (COM_FIXED_PI + COM_FIXED_PIHALF)
|
|
|
|
typedef int32_t com_fixed_t;
|
|
|
|
extern const uint16_t com_fixed_sin_lut[128];
|
|
|
|
static inline com_fixed_t com_fixed_add(com_fixed_t a, com_fixed_t b) {
|
|
return a + b;
|
|
}
|
|
|
|
static inline com_fixed_t com_fixed_sub(com_fixed_t a, com_fixed_t b) {
|
|
return a - b;
|
|
}
|
|
|
|
static inline com_fixed_t com_fixed_mul(com_fixed_t a, com_fixed_t b) {
|
|
return (com_fixed_t)(((int64_t)a * (int64_t)b) >> COM_FIXED_FRACBITS);
|
|
}
|
|
|
|
/* Note: this does not clamp for over/underflow cases */
|
|
static inline com_fixed_t com_fixed_div(com_fixed_t a, com_fixed_t b) {
|
|
return (com_fixed_t)(((int64_t)a << COM_FIXED_FRACBITS) / (int64_t)b);
|
|
}
|
|
|
|
/* Is supposed to be only used for debugging, not in real code. */
|
|
/* Because of that we default to maximum precision here. */
|
|
static inline double com_fixed_as_float(com_fixed_t a) {
|
|
return (a >> COM_FIXED_FRACBITS) * (double)1.0 +
|
|
(a & 0xFFFF) * (double)(1.0 / COM_FIXED_FRACUNIT);
|
|
}
|
|
|
|
/* Approximated square root by Newton-Raphson in 2 iterations over small LUT. */
|
|
com_fixed_t com_fixed_sqrt(com_fixed_t a);
|
|
|
|
/* Approximated using LUT. */
|
|
/* TODO: test whether linearly interpolated version could be default. */
|
|
/* things like camera movement can be jarring if done without.*/
|
|
/* https://namoseley.wordpress.com/2015/07/26/sincos-generation-using-table-lookup-and-iterpolation/
|
|
*/
|
|
static inline com_fixed_t com_fixed_sin(com_fixed_t a) {
|
|
int32_t sign = 1;
|
|
|
|
/* Wrap to (-Pi2,+Pi2) */
|
|
com_fixed_t q = a % COM_FIXED_PI2;
|
|
|
|
/* Wrap to [0,+Pi2) */
|
|
if (q < 0)
|
|
q = COM_FIXED_PI2 + q;
|
|
|
|
/* Limit to [0,+1) */
|
|
q = com_fixed_div(q, COM_FIXED_PI2);
|
|
|
|
/* Handle cases of 3rd and 4th quadrant. */
|
|
if (q >= COM_FIXED_FRACHALF) {
|
|
q -= COM_FIXED_FRACHALF;
|
|
sign = -1;
|
|
}
|
|
|
|
/* Finally clculate the index into LUT. */
|
|
uint16_t const idx = q >= COM_FIXED_FRACQRTR ? 255 - (q >> 7) : q >> 7;
|
|
|
|
return com_fixed_sin_lut[idx] * sign;
|
|
}
|
|
|
|
/* Implemented over sin, as to share one single LUT. */
|
|
/* Additionally, optimizer probably can collapse some math for combined sincos
|
|
* case. */
|
|
static inline com_fixed_t com_fixed_cos(com_fixed_t a) {
|
|
return com_fixed_sin(a + COM_FIXED_PIHALF);
|
|
}
|
|
|
|
static inline void com_fixed_sincos(com_fixed_t a, com_fixed_t *restrict s,
|
|
com_fixed_t *restrict c) {
|
|
*s = com_fixed_sin(a);
|
|
*c = com_fixed_cos(a);
|
|
}
|
|
|
|
void com_fixed_print(com_fixed_t a);
|
|
|
|
void com_fixed_run_tests(void);
|
|
|
|
#endif
|