Files
Microtransactions64/src/engine/math_util.c

1020 lines
31 KiB
C
Raw Normal View History

2019-08-25 00:46:40 -04:00
#include <ultra64.h>
#include "sm64.h"
#include "engine/graph_node.h"
#include "math_util.h"
#include "surface_collision.h"
#include "extended_bounds.h"
2019-11-03 14:36:27 -05:00
#include "trig_tables.inc.c"
#include "surface_load.h"
#include "game/puppyprint.h"
2021-08-25 19:01:54 +01:00
#include "game/rendering_graph_node.h"
2019-08-25 00:46:40 -04:00
#include "config.h"
2019-08-25 00:46:40 -04:00
// Variables for a spline curve animation (used for the flight path in the grand star cutscene)
Vec4s *gSplineKeyframe;
float gSplineKeyframeFraction;
int gSplineState;
/// Copy vector 'src' to 'dest'
void vec3f_copy(Vec3f dest, Vec3f src) {
2021-09-25 18:33:35 -07:00
((u64 *) dest)[0] = ((u64 *) src)[0];
((u32 *) dest)[2] = ((u32 *) src)[2];
2019-08-25 00:46:40 -04:00
}
/// Set vector 'dest' to (x, y, z)
void vec3f_set(Vec3f dest, f32 x, f32 y, f32 z) {
2021-09-23 18:56:14 -07:00
vec3_set(dest, x, y, z);
2019-08-25 00:46:40 -04:00
}
/// Add vector 'a' to 'dest'
void vec3f_add(Vec3f dest, Vec3f a) {
2021-09-25 18:33:35 -07:00
register f32 *temp = dest;
register s32 j;
register f32 sum, sum2;
for (j = 0; j < 3; j++) {
sum = *a;
a++;
sum2 = *temp;
temp++;
temp[-1] = (sum + sum2);
}
2019-08-25 00:46:40 -04:00
}
/// Make 'dest' the sum of vectors a and b.
void vec3f_sum(Vec3f dest, Vec3f a, Vec3f b) {
2021-09-23 18:56:14 -07:00
vec3_sum(dest, a, b);
2019-08-25 00:46:40 -04:00
}
/// Copy vector src to dest
void vec3s_copy(Vec3s dest, Vec3s src) {
2021-09-23 18:56:14 -07:00
vec3_copy(dest, src);
2019-08-25 00:46:40 -04:00
}
/// Set vector 'dest' to (x, y, z)
void vec3s_set(Vec3s dest, s16 x, s16 y, s16 z) {
2021-09-23 18:56:14 -07:00
vec3_set(dest, x, y, z);
2019-08-25 00:46:40 -04:00
}
/// Add vector a to 'dest'
void vec3s_add(Vec3s dest, Vec3s a) {
2021-09-23 18:56:14 -07:00
vec3_add(dest, a);
2019-08-25 00:46:40 -04:00
}
/// Make 'dest' the sum of vectors a and b.
void vec3s_sum(Vec3s dest, Vec3s a, Vec3s b) {
2021-09-23 18:56:14 -07:00
vec3_sum(dest, a, b);
2019-08-25 00:46:40 -04:00
}
/// Subtract vector a from 'dest'
void vec3s_sub(Vec3s dest, Vec3s a) {
2021-09-23 18:56:14 -07:00
vec3_sub(dest, a);
2019-08-25 00:46:40 -04:00
}
2021-09-25 09:31:45 -07:00
void vec3f_sub(Vec3f dest, Vec3f src) {
vec3_sub(dest, src);
}
2019-09-01 15:50:50 -04:00
/**
* Convert float vector a to a short vector 'dest' by rounding the components
* to the nearest integer.
2019-08-25 00:46:40 -04:00
*/
void vec3f_to_vec3s(Vec3s dest, Vec3f a) {
2019-08-25 00:46:40 -04:00
// add/subtract 0.5 in order to round to the nearest s32 instead of truncating
dest[0] = a[0] + ((a[0] > 0) ? 0.5f : -0.5f);
dest[1] = a[1] + ((a[1] > 0) ? 0.5f : -0.5f);
dest[2] = a[2] + ((a[2] > 0) ? 0.5f : -0.5f);
}
2019-09-01 15:50:50 -04:00
/**
* Set 'dest' the normal vector of a triangle with vertices a, b and c.
* It is similar to vec3f_cross, but it calculates the vectors (c-b) and (b-a)
* at the same time.
2019-08-25 00:46:40 -04:00
*/
void find_vector_perpendicular_to_plane(Vec3f dest, Vec3f a, Vec3f b, Vec3f c) {
2019-08-25 00:46:40 -04:00
dest[0] = (b[1] - a[1]) * (c[2] - b[2]) - (c[1] - b[1]) * (b[2] - a[2]);
dest[1] = (b[2] - a[2]) * (c[0] - b[0]) - (c[2] - b[2]) * (b[0] - a[0]);
dest[2] = (b[0] - a[0]) * (c[1] - b[1]) - (c[0] - b[0]) * (b[1] - a[1]);
}
/// Make vector 'dest' the cross product of vectors a and b.
void vec3f_cross(Vec3f dest, Vec3f a, Vec3f b) {
2021-09-23 18:56:14 -07:00
vec3_cross(dest, a, b);
2019-08-25 00:46:40 -04:00
}
/// Scale vector 'dest' so it has length 1
void vec3f_normalize(Vec3f dest) {
2021-09-22 18:03:54 -07:00
f32 invsqrt = sqrtf(sqr(dest[0]) + sqr(dest[1]) + sqr(dest[2]));
2021-09-22 18:14:19 -07:00
if (invsqrt < 0.00001f) return;
invsqrt = 1.0f / invsqrt;
2021-09-23 18:56:14 -07:00
vec3_mul_val(dest, invsqrt);
2019-08-25 00:46:40 -04:00
}
/// Copy matrix 'src' to 'dest'
void mtxf_copy(Mat4 dest, Mat4 src) {
register s32 i;
register u32 *d = (u32 *) dest;
register u32 *s = (u32 *) src;
2019-09-01 15:50:50 -04:00
for (i = 0; i < 16; i++) {
2019-08-25 00:46:40 -04:00
*d++ = *s++;
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04:00
/**
* Set mtx to the identity matrix
2019-08-25 00:46:40 -04:00
*/
void mtxf_identity(Mat4 mtx) {
register s32 i;
register f32 *dest;
2020-09-20 11:15:47 -04:00
// These loops must be one line to match on -O2
2019-08-25 00:46:40 -04:00
// initialize everything except the first and last cells to 0
2020-02-03 00:51:26 -05:00
for (dest = (f32 *) mtx + 1, i = 0; i < 14; dest++, i++) *dest = 0;
2019-08-25 00:46:40 -04:00
// initialize the diagonal cells to 1
2020-02-03 00:51:26 -05:00
for (dest = (f32 *) mtx, i = 0; i < 4; dest += 5, i++) *dest = 1;
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04:00
/**
* Set dest to a translation matrix of vector b
2019-08-25 00:46:40 -04:00
*/
void mtxf_translate(Mat4 dest, Vec3f b) {
mtxf_identity(dest);
2021-09-25 18:33:35 -07:00
vec3f_copy(dest[3], b);
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04:00
/**
* Set mtx to a look-at matrix for the camera. The resulting transformation
* transforms the world as if there exists a camera at position 'from' pointed
* at the position 'to'. The up-vector is assumed to be (0, 1, 0), but the 'roll'
* angle allows a bank rotation of the camera.
2019-08-25 00:46:40 -04:00
*/
void mtxf_lookat(Mat4 mtx, Vec3f from, Vec3f to, s32 roll) {
2021-09-23 18:56:14 -07:00
Vec3f colX, colY, colZ;
2019-08-25 00:46:40 -04:00
2021-09-23 18:56:14 -07:00
f32 dx = to[0] - from[0];
f32 dz = to[2] - from[2];
2019-08-25 00:46:40 -04:00
2021-09-23 18:56:14 -07:00
register f32 invLength = -1.0f / MAX(sqrtf(sqr(dx) + sqr(dz)), 0.00001f);
2019-08-25 00:46:40 -04:00
dx *= invLength;
dz *= invLength;
2021-09-23 18:56:14 -07:00
colY[1] = coss(roll);
colY[0] = sins(roll) * dz;
colY[2] = -sins(roll) * dx;
vec3_diff(colZ, to, from);
2019-08-25 00:46:40 -04:00
2021-09-23 18:56:14 -07:00
invLength = -1.0f / MAX(sqrtf(sqr(colZ[0]) + sqr(colZ[1]) + sqr(colZ[2])), 0.00001f);
vec3_mul_val(colZ, invLength);
2019-08-25 00:46:40 -04:00
2021-09-23 18:56:14 -07:00
vec3_cross(colX, colY, colZ);
2019-08-25 00:46:40 -04:00
2021-09-23 18:56:14 -07:00
invLength = 1.0f / MAX(sqrtf(sqr(colX[0]) + sqr(colX[1]) + sqr(colX[2])), 0.00001f);
vec3_mul_val(colX, invLength);
2019-08-25 00:46:40 -04:00
2021-09-23 18:56:14 -07:00
vec3_cross(colY, colZ, colX);
2019-08-25 00:46:40 -04:00
2021-09-23 18:56:14 -07:00
invLength = 1.0f / MAX(sqrtf(sqr(colY[0]) + sqr(colY[1]) + sqr(colY[2])), 0.00001f);
vec3_mul_val(colY, invLength);
2019-08-25 00:46:40 -04:00
2021-09-23 18:56:14 -07:00
mtx[0][0] = colX[0];
mtx[1][0] = colX[1];
mtx[2][0] = colX[2];
2019-08-25 00:46:40 -04:00
2021-09-23 18:56:14 -07:00
mtx[0][1] = colY[0];
mtx[1][1] = colY[1];
mtx[2][1] = colY[2];
2019-08-25 00:46:40 -04:00
2021-09-23 18:56:14 -07:00
mtx[0][2] = colZ[0];
mtx[1][2] = colZ[1];
mtx[2][2] = colZ[2];
mtx[3][0] = -vec3_dot(from, colX);
mtx[3][1] = -vec3_dot(from, colY);
mtx[3][2] = -vec3_dot(from, colZ);
2019-08-25 00:46:40 -04:00
mtx[0][3] = 0;
mtx[1][3] = 0;
mtx[2][3] = 0;
mtx[3][3] = 1;
}
/**
* Build a matrix that rotates around the z axis, then the x axis, then the y
* axis, and then translates.
*/
void mtxf_rotate_zxy_and_translate(Mat4 dest, Vec3f translate, Vec3s rotate) {
register f32 sx = sins(rotate[0]);
register f32 cx = coss(rotate[0]);
register f32 sy = sins(rotate[1]);
register f32 cy = coss(rotate[1]);
register f32 sz = sins(rotate[2]);
register f32 cz = coss(rotate[2]);
dest[0][0] = cy * cz + sx * sy * sz;
dest[1][0] = -cy * sz + sx * sy * cz;
dest[2][0] = cx * sy;
dest[3][0] = translate[0];
dest[0][1] = cx * sz;
dest[1][1] = cx * cz;
dest[2][1] = -sx;
dest[3][1] = translate[1];
dest[0][2] = -sy * cz + sx * cy * sz;
dest[1][2] = sy * sz + sx * cy * cz;
dest[2][2] = cx * cy;
dest[3][2] = translate[2];
dest[0][3] = dest[1][3] = dest[2][3] = 0.0f;
dest[3][3] = 1.0f;
}
/**
* Build a matrix that rotates around the x axis, then the y axis, then the z
* axis, and then translates.
*/
void mtxf_rotate_xyz_and_translate(Mat4 dest, Vec3f b, Vec3s c) {
register f32 sx = sins(c[0]);
register f32 cx = coss(c[0]);
register f32 sy = sins(c[1]);
register f32 cy = coss(c[1]);
register f32 sz = sins(c[2]);
register f32 cz = coss(c[2]);
dest[0][0] = cy * cz;
dest[0][1] = cy * sz;
dest[0][2] = -sy;
dest[0][3] = 0;
dest[1][0] = sx * sy * cz - cx * sz;
dest[1][1] = sx * sy * sz + cx * cz;
dest[1][2] = sx * cy;
dest[1][3] = 0;
dest[2][0] = cx * sy * cz + sx * sz;
dest[2][1] = cx * sy * sz - sx * cz;
dest[2][2] = cx * cy;
dest[2][3] = 0;
dest[3][0] = b[0];
dest[3][1] = b[1];
dest[3][2] = b[2];
dest[3][3] = 1;
}
2019-09-01 15:50:50 -04:00
/**
* Set 'dest' to a transformation matrix that turns an object to face the camera.
* 'mtx' is the look-at matrix from the camera
* 'position' is the position of the object in the world
* 'angle' rotates the object while still facing the camera.
2019-08-25 00:46:40 -04:00
*/
void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, s32 angle) {
2021-09-23 18:56:14 -07:00
if (angle == 0x0) {
dest[0][0] = 1;
dest[0][1] = 0;
} else {
dest[0][0] = coss(angle);
dest[0][1] = sins(angle);
}
2019-08-25 00:46:40 -04:00
dest[0][2] = 0;
dest[0][3] = 0;
dest[1][0] = -dest[0][1];
dest[1][1] = dest[0][0];
dest[1][2] = 0;
dest[1][3] = 0;
dest[2][0] = 0;
dest[2][1] = 0;
dest[2][2] = 1;
dest[2][3] = 0;
2021-09-23 18:56:14 -07:00
dest[3][0] = mtx[0][0] * position[0] + mtx[1][0] * position[1] + mtx[2][0] * position[2] + mtx[3][0];
dest[3][1] = mtx[0][1] * position[0] + mtx[1][1] * position[1] + mtx[2][1] * position[2] + mtx[3][1];
dest[3][2] = mtx[0][2] * position[0] + mtx[1][2] * position[1] + mtx[2][2] * position[2] + mtx[3][2];
2019-08-25 00:46:40 -04:00
dest[3][3] = 1;
}
2019-09-01 15:50:50 -04:00
/**
* Set 'dest' to a transformation matrix that aligns an object with the terrain
* based on the normal. Used for enemies.
* 'upDir' is the terrain normal
* 'yaw' is the angle which it should face
* 'pos' is the object's position in the world
2019-08-25 00:46:40 -04:00
*/
void mtxf_align_terrain_normal(Mat4 dest, Vec3f upDir, Vec3f pos, s32 yaw) {
2019-08-25 00:46:40 -04:00
Vec3f lateralDir;
Vec3f leftDir;
Vec3f forwardDir;
2021-09-23 18:56:14 -07:00
vec3_set(lateralDir, sins(yaw), 0, coss(yaw));
2019-08-25 00:46:40 -04:00
vec3f_normalize(upDir);
2021-09-23 18:56:14 -07:00
vec3_cross(leftDir, upDir, lateralDir);
2019-08-25 00:46:40 -04:00
vec3f_normalize(leftDir);
2021-09-23 18:56:14 -07:00
vec3_cross(forwardDir, leftDir, upDir);
2019-08-25 00:46:40 -04:00
vec3f_normalize(forwardDir);
2021-09-25 18:33:35 -07:00
vec3f_copy(dest[0], leftDir);
vec3f_copy(dest[1], upDir);
vec3f_copy(dest[2], forwardDir);
vec3f_copy(dest[3], pos);
2019-08-25 00:46:40 -04:00
dest[0][3] = 0.0f;
dest[1][3] = 0.0f;
dest[2][3] = 0.0f;
dest[3][3] = 1.0f;
}
2019-09-01 15:50:50 -04:00
/**
* Set 'mtx' to a transformation matrix that aligns an object with the terrain
* based on 3 height samples in an equilateral triangle around the object.
* Used for Mario when crawling or sliding.
* 'yaw' is the angle which it should face
* 'pos' is the object's position in the world
* 'radius' is the distance from each triangle vertex to the center
2019-08-25 00:46:40 -04:00
*/
void mtxf_align_terrain_triangle(Mat4 mtx, Vec3f pos, s32 yaw, f32 radius) {
2021-09-23 18:56:14 -07:00
struct Surface *floor;
Vec3f point0, point1, point2;
2019-08-25 00:46:40 -04:00
Vec3f forward;
2021-09-23 18:56:14 -07:00
Vec3f xColumn, yColumn, zColumn;
2019-08-25 00:46:40 -04:00
f32 avgY;
f32 minY = -radius * 3;
point0[0] = pos[0] + radius * sins(yaw + 0x2AAA);
point0[2] = pos[2] + radius * coss(yaw + 0x2AAA);
point1[0] = pos[0] + radius * sins(yaw + 0x8000);
point1[2] = pos[2] + radius * coss(yaw + 0x8000);
point2[0] = pos[0] + radius * sins(yaw + 0xD555);
point2[2] = pos[2] + radius * coss(yaw + 0xD555);
2021-09-23 18:56:14 -07:00
point0[1] = find_floor(point0[0], pos[1] + 150, point0[2], &floor);
point1[1] = find_floor(point1[0], pos[1] + 150, point1[2], &floor);
point2[1] = find_floor(point2[0], pos[1] + 150, point2[2], &floor);
2019-08-25 00:46:40 -04:00
2019-09-01 15:50:50 -04:00
if (point0[1] - pos[1] < minY) {
2019-08-25 00:46:40 -04:00
point0[1] = pos[1];
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
2019-09-01 15:50:50 -04:00
if (point1[1] - pos[1] < minY) {
2019-08-25 00:46:40 -04:00
point1[1] = pos[1];
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
2019-09-01 15:50:50 -04:00
if (point2[1] - pos[1] < minY) {
2019-08-25 00:46:40 -04:00
point2[1] = pos[1];
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
avgY = (point0[1] + point1[1] + point2[1]) / 3;
2021-09-23 18:56:14 -07:00
vec3_set(forward, sins(yaw), 0, coss(yaw));
2019-08-25 00:46:40 -04:00
find_vector_perpendicular_to_plane(yColumn, point0, point1, point2);
vec3f_normalize(yColumn);
2021-09-23 18:56:14 -07:00
vec3_cross(xColumn, yColumn, forward);
2019-08-25 00:46:40 -04:00
vec3f_normalize(xColumn);
2021-09-23 18:56:14 -07:00
vec3_cross(zColumn, xColumn, yColumn);
2019-08-25 00:46:40 -04:00
vec3f_normalize(zColumn);
2021-09-25 18:33:35 -07:00
vec3f_copy(mtx[0], xColumn);
vec3f_copy(mtx[1], yColumn);
vec3f_copy(mtx[2], zColumn);
2019-08-25 00:46:40 -04:00
mtx[3][0] = pos[0];
mtx[3][1] = (avgY < pos[1]) ? pos[1] : avgY;
mtx[3][2] = pos[2];
mtx[0][3] = 0;
mtx[1][3] = 0;
mtx[2][3] = 0;
mtx[3][3] = 1;
}
2019-09-01 15:50:50 -04:00
/**
* Sets matrix 'dest' to the matrix product b * a assuming they are both
* transformation matrices with a w-component of 1. Since the bottom row
* is assumed to equal [0, 0, 0, 1], it saves some multiplications and
* addition.
* The resulting matrix represents first applying transformation b and
* then a.
2019-08-25 00:46:40 -04:00
*/
void mtxf_mul(Mat4 dest, Mat4 a, Mat4 b) {
Mat4 temp;
2021-09-23 18:56:14 -07:00
register Vec3f entry;
2019-08-25 00:46:40 -04:00
// column 0
2021-09-23 18:56:14 -07:00
vec3_copy(entry, a[0]);
temp[0][0] = entry[0] * b[0][0] + entry[1] * b[1][0] + entry[2] * b[2][0];
temp[0][1] = entry[0] * b[0][1] + entry[1] * b[1][1] + entry[2] * b[2][1];
temp[0][2] = entry[0] * b[0][2] + entry[1] * b[1][2] + entry[2] * b[2][2];
2019-08-25 00:46:40 -04:00
// column 1
2021-09-23 18:56:14 -07:00
vec3_copy(entry, a[1]);
temp[1][0] = entry[0] * b[0][0] + entry[1] * b[1][0] + entry[2] * b[2][0];
temp[1][1] = entry[0] * b[0][1] + entry[1] * b[1][1] + entry[2] * b[2][1];
temp[1][2] = entry[0] * b[0][2] + entry[1] * b[1][2] + entry[2] * b[2][2];
2019-08-25 00:46:40 -04:00
// column 2
2021-09-23 18:56:14 -07:00
vec3_copy(entry, a[2]);
temp[2][0] = entry[0] * b[0][0] + entry[1] * b[1][0] + entry[2] * b[2][0];
temp[2][1] = entry[0] * b[0][1] + entry[1] * b[1][1] + entry[2] * b[2][1];
temp[2][2] = entry[0] * b[0][2] + entry[1] * b[1][2] + entry[2] * b[2][2];
2019-08-25 00:46:40 -04:00
// column 3
2021-09-23 18:56:14 -07:00
vec3_copy(entry, a[3]);
temp[3][0] = entry[0] * b[0][0] + entry[1] * b[1][0] + entry[2] * b[2][0] + b[3][0];
temp[3][1] = entry[0] * b[0][1] + entry[1] * b[1][1] + entry[2] * b[2][1] + b[3][1];
temp[3][2] = entry[0] * b[0][2] + entry[1] * b[1][2] + entry[2] * b[2][2] + b[3][2];
2019-08-25 00:46:40 -04:00
temp[0][3] = temp[1][3] = temp[2][3] = 0;
temp[3][3] = 1;
mtxf_copy(dest, temp);
}
2019-09-01 15:50:50 -04:00
/**
* Set matrix 'dest' to 'mtx' scaled by vector s
2019-08-25 00:46:40 -04:00
*/
void mtxf_scale_vec3f(Mat4 dest, Mat4 mtx, Vec3f s) {
register s32 i;
for (i = 0; i < 4; i++) {
dest[0][i] = mtx[0][i] * s[0];
dest[1][i] = mtx[1][i] * s[1];
dest[2][i] = mtx[2][i] * s[2];
dest[3][i] = mtx[3][i];
}
}
2019-09-01 15:50:50 -04:00
/**
* Multiply a vector with a transformation matrix, which applies the transformation
* to the point. Note that the bottom row is assumed to be [0, 0, 0, 1], which is
* true for transformation matrices if the translation has a w component of 1.
2019-08-25 00:46:40 -04:00
*/
void mtxf_mul_vec3s(Mat4 mtx, Vec3s b) {
register f32 x = b[0];
register f32 y = b[1];
register f32 z = b[2];
b[0] = x * mtx[0][0] + y * mtx[1][0] + z * mtx[2][0] + mtx[3][0];
b[1] = x * mtx[0][1] + y * mtx[1][1] + z * mtx[2][1] + mtx[3][1];
b[2] = x * mtx[0][2] + y * mtx[1][2] + z * mtx[2][2] + mtx[3][2];
}
2019-09-01 15:50:50 -04:00
/**
* Convert float matrix 'src' to fixed point matrix 'dest'.
* The float matrix may not contain entries larger than 65536 or the console
* crashes. The fixed point matrix has entries with a 16-bit integer part, so
2020-06-02 12:44:34 -04:00
* the floating point numbers are multiplied by 2^16 before being cast to a s32
2019-09-01 15:50:50 -04:00
* integer. If this doesn't fit, the N64 and iQue consoles will throw an
* exception. On Wii and Wii U Virtual Console the value will simply be clamped
* and no crashes occur.
* Modified into a hybrid of the original function and the worldscale altered function.
* Will check if the worldscale is below what's considered safe in vanilla bounds and
* just run the faster vanilla function, otherwise it'll run the slower, but safer scale
* function, for extended boundaries.
2019-08-25 00:46:40 -04:00
*/
void mtxf_to_mtx_scale(Mtx *dest, Mat4 src) {
Mat4 temp;
register s32 i, j;
for( i = 0; i < 4; i++ ) {
for( j = 0; j < 3; j++ ) {
2021-09-26 16:43:55 -07:00
temp[i][j] = src[i][j] / gWorldScale;
}
temp[i][3] = src[i][3];
}
guMtxF2L( temp, dest );
2019-08-25 00:46:40 -04:00
}
void mtxf_to_mtx_constant(register s16 *dest, register f32 *src) {
s32 asFixedPoint;
s32 i;
2021-09-26 16:43:55 -07:00
for (i = 0; i < 16; i++) {
asFixedPoint = (src[i] * (1 << 16));
dest[i] = (asFixedPoint >> 16);
dest[i + 16] = (asFixedPoint & 0xFFFF);
}
}
2021-09-26 16:43:55 -07:00
void mtxf_to_mtx(void *dest, void *src) {
if (gWorldScale > 2.0f) {
mtxf_to_mtx_scale(dest, src);
2021-09-26 16:43:55 -07:00
} else {
mtxf_to_mtx_constant(dest, src);
2021-09-26 16:43:55 -07:00
}
}
2019-09-01 15:50:50 -04:00
/**
* Set 'mtx' to a transformation matrix that rotates around the z axis.
2019-08-25 00:46:40 -04:00
*/
void mtxf_rotate_xy(Mtx *mtx, s32 angle) {
2019-08-25 00:46:40 -04:00
Mat4 temp;
mtxf_identity(temp);
temp[0][0] = coss(angle);
temp[0][1] = sins(angle);
temp[1][0] = -temp[0][1];
temp[1][1] = temp[0][0];
mtxf_to_mtx(mtx, temp);
}
2019-09-01 15:50:50 -04:00
/**
* Extract a position given an object's transformation matrix and a camera matrix.
* This is used for determining the world position of the held object: since objMtx
* inherits the transformation from both the camera and Mario, it calculates this
* by taking the camera matrix and inverting its transformation by first rotating
* objMtx back from screen orientation to world orientation, and then subtracting
* the camera position.
2019-08-25 00:46:40 -04:00
*/
void get_pos_from_transform_mtx(Vec3f dest, Mat4 objMtx, Mat4 camMtx) {
f32 camX = camMtx[3][0] * camMtx[0][0] + camMtx[3][1] * camMtx[0][1] + camMtx[3][2] * camMtx[0][2];
f32 camY = camMtx[3][0] * camMtx[1][0] + camMtx[3][1] * camMtx[1][1] + camMtx[3][2] * camMtx[1][2];
f32 camZ = camMtx[3][0] * camMtx[2][0] + camMtx[3][1] * camMtx[2][1] + camMtx[3][2] * camMtx[2][2];
2021-09-23 18:56:14 -07:00
dest[0] = objMtx[3][0] * camMtx[0][0] + objMtx[3][1] * camMtx[0][1] + objMtx[3][2] * camMtx[0][2] - camX;
dest[1] = objMtx[3][0] * camMtx[1][0] + objMtx[3][1] * camMtx[1][1] + objMtx[3][2] * camMtx[1][2] - camY;
dest[2] = objMtx[3][0] * camMtx[2][0] + objMtx[3][1] * camMtx[2][1] + objMtx[3][2] * camMtx[2][2] - camZ;
2019-08-25 00:46:40 -04:00
}
2021-09-26 16:43:55 -07:00
2019-09-01 15:50:50 -04:00
/**
* Take the vector starting at 'from' pointed at 'to' an retrieve the length
* of that vector, as well as the yaw and pitch angles.
2020-01-03 10:38:57 -05:00
* Basically it converts the direction to spherical coordinates.
2019-08-25 00:46:40 -04:00
*/
2021-09-26 16:43:55 -07:00
/// Finds the horizontal distance between two vectors.
void vec3f_get_lateral_dist(Vec3f from, Vec3f to, f32 *lateralDist) {
register f32 dx = (to[0] - from[0]);
register f32 dz = (to[2] - from[2]);
*lateralDist = sqrtf(sqr(dx) + sqr(dz));
}
/// Finds the squared horizontal distance between two vectors.
void vec3f_get_lateral_dist_squared(Vec3f from, Vec3f to, f32 *lateralDist) {
register f32 dx = (to[0] - from[0]);
register f32 dz = (to[2] - from[2]);
*lateralDist = (sqr(dx) + sqr(dz));
}
/// Finds the distance between two vectors.
void vec3f_get_dist(Vec3f from, Vec3f to, f32 *dist) {
register Vec3f d;
vec3_diff(d, to, from);
*dist = vec3_mag(d);
}
/// Finds the squared distance between two vectors.
void vec3f_get_dist_squared(Vec3f from, Vec3f to, f32 *dist) {
register Vec3f d;
vec3_diff(d, to, from);
*dist = vec3_sumsq(d);
}
/// Finds the distance and yaw etween two vectors.
void vec3f_get_dist_and_yaw(Vec3f from, Vec3f to, f32 *dist, s16 *yaw) {
register Vec3f d;
vec3_diff(d, to, from);
*dist = vec3_mag(d);
*yaw = atan2s(d[2], d[0]);
}
/// Finds the pitch between two vectors.
void vec3f_get_pitch(Vec3f from, Vec3f to, s16 *pitch) {
register Vec3f d;
vec3_diff(d, to, from);
*pitch = atan2s(sqrtf(sqr(d[0]) + sqr(d[2])), d[1]);
}
/// Finds the yaw between two vectors.
void vec3f_get_yaw(Vec3f from, Vec3f to, s16 *yaw) {
register f32 dx = (to[0] - from[0]);
register f32 dz = (to[2] - from[2]);
*yaw = atan2s(dz, dx);
}
/// Finds the pitch and yaw between two vectors.
void vec3f_get_angle(Vec3f from, Vec3f to, s16 *pitch, s16 *yaw) {
register Vec3f d;
vec3_diff(d, to, from);
*pitch = atan2s(sqrtf(sqr(d[0]) + sqr(d[2])), d[1]);
*yaw = atan2s(d[2], d[0]);
}
/// Finds the horizontal distance and pitch between two vectors.
void vec3f_get_lateral_dist_and_pitch(Vec3f from, Vec3f to, f32 *lateralDist, Angle *pitch) {
Vec3f d;
vec3_diff(d, to, from);
*lateralDist = sqrtf(sqr(d[0]) + sqr(d[2]));
*pitch = atan2s(*lateralDist, d[1]);
}
/// Finds the horizontal distance and yaw between two vectors.
void vec3f_get_lateral_dist_and_yaw(Vec3f from, Vec3f to, f32 *lateralDist, Angle *yaw) {
register f32 dx = (to[0] - from[0]);
register f32 dz = (to[2] - from[2]);
*lateralDist = sqrtf(sqr(dx) + sqr(dz));
*yaw = atan2s(dz, dx);
}
/// Finds the horizontal distance and angles between two vectors.
void vec3f_get_lateral_dist_and_angle(Vec3f from, Vec3f to, f32 *lateralDist, Angle *pitch, Angle *yaw) {
Vec3f d;
vec3_diff(d, to, from);
*lateralDist = sqrtf(sqr(d[0]) + sqr(d[2]));
*pitch = atan2s(*lateralDist, d[1]);
*yaw = atan2s(d[2], d[0]);
}
/// Finds the distance and angles between two vectors.
2019-08-25 00:46:40 -04:00
void vec3f_get_dist_and_angle(Vec3f from, Vec3f to, f32 *dist, s16 *pitch, s16 *yaw) {
2021-09-26 16:43:55 -07:00
register Vec3f d;
vec3_diff(d, to, from);
register f32 xz = sqr(d[0]) + sqr(d[2]);
*dist = sqrtf(xz + sqr(d[1]));
*pitch = atan2s(sqrtf(xz), d[1]);
*yaw = atan2s(d[2], d[0]);
}
void vec3s_get_dist_and_angle(Vec3s from, Vec3s to, s16 *dist, Angle *pitch, Angle *yaw) {
Vec3s d;
vec3_diff(d, to, from);
register f32 xz = (sqr(d[0]) + sqr(d[2]));
*dist = sqrtf(xz + sqr(d[1]));
*pitch = atan2s(sqrtf(xz), d[1]);
*yaw = atan2s(d[2], d[0]);
}
/// Finds the distance, horizontal distance, and angles between two vectors.
void vec3f_get_dist_and_lateral_dist_and_angle(Vec3f from, Vec3f to, f32 *dist, f32 *lateralDist, Angle *pitch, Angle *yaw) {
Vec3f d;
vec3_diff(d, to, from);
register f32 xz = (sqr(d[0]) + sqr(d[2]));
*dist = sqrtf(xz + sqr(d[1]));
*lateralDist = sqrtf(xz);
*pitch = atan2s(*lateralDist, d[1]);
*yaw = atan2s(d[2], d[0]);
2019-08-25 00:46:40 -04:00
}
2019-09-01 15:50:50 -04:00
/**
* Construct the 'to' point which is distance 'dist' away from the 'from' position,
* and has the angles pitch and yaw.
2019-08-25 00:46:40 -04:00
*/
void vec3f_set_dist_and_angle(Vec3f from, Vec3f to, f32 dist, s32 pitch, s32 yaw) {
2021-09-26 16:43:55 -07:00
register f32 dcos = (dist * coss(pitch));
to[0] = (from[0] + (dcos * sins(yaw )));
to[1] = (from[1] + (dist * sins(pitch)));
to[2] = (from[2] + (dcos * coss(yaw )));
}
void vec3s_set_dist_and_angle(Vec3s from, Vec3s to, s16 dist, Angle32 pitch, Angle32 yaw) {
register f32 dcos = (dist * coss(pitch));
to[0] = (from[0] + (dcos * sins(yaw )));
to[1] = (from[1] + (dist * sins(pitch)));
to[2] = (from[2] + (dcos * coss(yaw )));
2019-08-25 00:46:40 -04:00
}
2021-09-26 14:49:21 -07:00
s32 approach_s16(s32 current, s32 target, s32 inc, s32 dec) {
s16 dist = (target - current);
if (dist >= 0) { // target >= current
current = ((dist > inc) ? (current + inc) : target);
} else { // target < current
current = ((dist < -dec) ? (current - dec) : target);
}
return current;
}
Bool32 approach_s16_bool(s16 *current, s32 target, s32 inc, s32 dec) {
*current = approach_s16(*current, target, inc, dec);
return !(*current == target);
}
2019-09-01 15:50:50 -04:00
/**
* Return the value 'current' after it tries to approach target, going up at
* most 'inc' and going down at most 'dec'.
2019-08-25 00:46:40 -04:00
*/
s32 approach_s32(s32 current, s32 target, s32 inc, s32 dec) {
2021-09-23 18:56:14 -07:00
s32 dist = (target - current);
if (dist > 0) { // current < target
current = ((dist > inc) ? (current + inc) : target);
} else if (dist < 0) { // current > target
current = ((dist < -dec) ? (current - dec) : target);
2019-08-25 00:46:40 -04:00
}
return current;
}
2021-09-26 14:49:21 -07:00
Bool32 approach_s32_bool(s32 *current, s32 target, s32 inc, s32 dec) {
*current = approach_s32(*current, target, inc, dec);
return !(*current == target);
}
2019-09-01 15:50:50 -04:00
/**
* Return the value 'current' after it tries to approach target, going up at
* most 'inc' and going down at most 'dec'.
2019-08-25 00:46:40 -04:00
*/
f32 approach_f32(f32 current, f32 target, f32 inc, f32 dec) {
2021-09-23 18:56:14 -07:00
f32 dist = (target - current);
if (dist >= 0.0f) { // target >= current
current = ((dist > inc) ? (current + inc) : target);
} else { // target < current
current = ((dist < -dec) ? (current - dec) : target);
2019-08-25 00:46:40 -04:00
}
return current;
}
2021-09-26 14:49:21 -07:00
Bool32 approach_f32_bool(f32 *current, f32 target, f32 inc, f32 dec) {
*current = approach_f32(*current, target, inc, dec);
return !(*current == target);
}
s32 approach_f32_signed(f32 *current, f32 target, f32 inc) {
s32 reachedTarget = FALSE;
*current += inc;
if (inc >= 0.0f) {
if (*current > target) {
*current = target;
reachedTarget = TRUE;
}
} else {
if (*current < target) {
*current = target;
reachedTarget = TRUE;
}
}
return reachedTarget;
}
2021-09-24 18:26:36 -07:00
/**
* Similar to approach_s32, but converts to s16 and allows for overflow between 32767 and -32768
*/
s32 approach_angle(s32 current, s32 target, s32 inc) {
s32 dist = (s16)(target - current);
if (dist < 0) {
dist += inc;
2021-09-26 16:43:55 -07:00
if (dist > 0) dist = 0;
2021-09-24 18:26:36 -07:00
} else if (dist > 0) {
dist -= inc;
2021-09-26 16:43:55 -07:00
if (dist < 0) dist = 0;
2021-09-24 18:26:36 -07:00
}
return (target - dist);
}
2021-09-26 16:43:55 -07:00
/**
* Approaches an f32 value by taking the difference between the target and current value
* and adding a fraction of that to the current value.
* Edits the current value directly, returns TRUE if the target has been reached, FALSE otherwise.
*/
s32 approach_f32_asymptotic_bool(f32 *current, f32 target, f32 multiplier) {
if (multiplier > 1.f) {
multiplier = 1.f;
}
*current = *current + (target - *current) * multiplier;
return (*current != target);
}
/**
* Nearly the same as the above function, returns new value instead.
*/
f32 approach_f32_asymptotic(f32 current, f32 target, f32 multiplier) {
current = current + (target - current) * multiplier;
return current;
}
/**
* Approaches an s16 value in the same fashion as approach_f32_asymptotic_bool, returns TRUE if target
* is reached. Note: Since this function takes integers as parameters, the last argument is the
* reciprocal of what it would be in the previous two functions.
*/
s32 approach_s16_asymptotic_bool(s16 *current, s16 target, s16 divisor) {
s16 temp = *current;
if (divisor == 0) {
*current = target;
} else {
temp -= target;
temp -= temp / divisor;
temp += target;
*current = temp;
}
return (*current != target);
}
/**
* Approaches an s16 value in the same fashion as approach_f32_asymptotic, returns the new value.
* Note: last parameter is the reciprocal of what it would be in the f32 functions
*/
s32 approach_s16_asymptotic(s16 current, s16 target, s16 divisor) {
s16 temp = current;
if (divisor == 0) {
current = target;
} else {
temp -= target;
temp -= temp / divisor;
temp += target;
current = temp;
}
return current;
}
2019-09-01 15:50:50 -04:00
/**
* Helper function for atan2s. Does a look up of the arctangent of y/x assuming
* the resulting angle is in range [0, 0x2000] (1/8 of a circle).
2019-08-25 00:46:40 -04:00
*/
2021-09-23 18:56:14 -07:00
#define atan2_lookup(y, x) ((x == 0) ? 0x0 : atans((y) / (x)))
2019-08-25 00:46:40 -04:00
2019-09-01 15:50:50 -04:00
/**
* Compute the angle from (0, 0) to (x, y) as a s16. Given that terrain is in
* the xz-plane, this is commonly called with (z, x) to get a yaw angle.
2019-08-25 00:46:40 -04:00
*/
s16 atan2s(f32 y, f32 x) {
u16 ret;
if (x >= 0) {
if (y >= 0) {
2019-09-01 15:50:50 -04:00
if (y >= x) {
2019-08-25 00:46:40 -04:00
ret = atan2_lookup(x, y);
2019-09-01 15:50:50 -04:00
} else {
2021-09-26 16:43:55 -07:00
ret = (0x4000 - atan2_lookup(y, x));
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
} else {
y = -y;
2019-09-01 15:50:50 -04:00
if (y < x) {
2021-09-26 16:43:55 -07:00
ret = (0x4000 + atan2_lookup(y, x));
2019-09-01 15:50:50 -04:00
} else {
2021-09-26 16:43:55 -07:00
ret = (0x8000 - atan2_lookup(x, y));
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
} else {
x = -x;
if (y < 0) {
y = -y;
2019-09-01 15:50:50 -04:00
if (y >= x) {
2021-09-26 16:43:55 -07:00
ret = (0x8000 + atan2_lookup(x, y));
2019-09-01 15:50:50 -04:00
} else {
2021-09-26 16:43:55 -07:00
ret = (0xC000 - atan2_lookup(y, x));
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
} else {
2019-09-01 15:50:50 -04:00
if (y < x) {
2021-09-26 16:43:55 -07:00
ret = (0xC000 + atan2_lookup(y, x));
2019-09-01 15:50:50 -04:00
} else {
2019-08-25 00:46:40 -04:00
ret = -atan2_lookup(x, y);
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
}
}
return ret;
}
2019-09-01 15:50:50 -04:00
/**
* Compute the atan2 in radians by calling atan2s and converting the result.
2019-08-25 00:46:40 -04:00
*/
f32 atan2f(f32 y, f32 x) {
2021-09-26 16:43:55 -07:00
return ((f32) atan2s(y, x) * M_PI / 0x8000);
2019-08-25 00:46:40 -04:00
}
2021-09-23 18:56:14 -07:00
#define CURVE_BEGIN_1 0x1
#define CURVE_BEGIN_2 0x2
#define CURVE_MIDDLE 0x3
#define CURVE_END_1 0x4
#define CURVE_END_2 0x5
2019-08-25 00:46:40 -04:00
2019-09-01 15:50:50 -04:00
/**
* Set 'result' to a 4-vector with weights corresponding to interpolation
* value t in [0, 1] and gSplineState. Given the current control point P, these
* weights are for P[0], P[1], P[2] and P[3] to obtain an interpolated point.
* The weights naturally sum to 1, and they are also always in range [0, 1] so
2020-06-02 12:44:34 -04:00
* the interpolated point will never overshoot. The curve is guaranteed to go
2019-09-01 15:50:50 -04:00
* through the first and last point, but not through intermediate points.
2019-08-25 00:46:40 -04:00
*
2019-09-01 15:50:50 -04:00
* gSplineState ensures that the curve is clamped: the first two points
* and last two points have different weight formulas. These are the weights
* just before gSplineState transitions:
* 1: [1, 0, 0, 0]
* 1->2: [0, 3/12, 7/12, 2/12]
* 2->3: [0, 1/6, 4/6, 1/6]
* 3->3: [0, 1/6, 4/6, 1/6] (repeats)
* 3->4: [0, 1/6, 4/6, 1/6]
* 4->5: [0, 2/12, 7/12, 3/12]
* 5: [0, 0, 0, 1]
2019-08-25 00:46:40 -04:00
*
* I suspect that the weight formulas will give a 3rd degree B-spline with the
* common uniform clamped knot vector, e.g. for n points:
* [0, 0, 0, 0, 1, 2, ... n-1, n, n, n, n]
* TODO: verify the classification of the spline / figure out how polynomials were computed
*/
void spline_get_weights(Vec4f result, f32 t, UNUSED s32 c) {
f32 tinv = 1 - t;
f32 tinv2 = tinv * tinv;
f32 tinv3 = tinv2 * tinv;
f32 t2 = t * t;
f32 t3 = t2 * t;
switch (gSplineState) {
case CURVE_BEGIN_1:
result[0] = tinv3;
result[1] = t3 * 1.75f - t2 * 4.5f + t * 3.0f;
result[2] = -t3 * (11 / 12.0f) + t2 * 1.5f;
result[3] = t3 * (1 / 6.0f);
break;
case CURVE_BEGIN_2:
result[0] = tinv3 * 0.25f;
result[1] = t3 * (7 / 12.0f) - t2 * 1.25f + t * 0.25f + (7 / 12.0f);
result[2] = -t3 * 0.5f + t2 * 0.5f + t * 0.5f + (1 / 6.0f);
result[3] = t3 * (1 / 6.0f);
break;
case CURVE_MIDDLE:
result[0] = tinv3 * (1 / 6.0f);
result[1] = t3 * 0.5f - t2 + (4 / 6.0f);
result[2] = -t3 * 0.5f + t2 * 0.5f + t * 0.5f + (1 / 6.0f);
result[3] = t3 * (1 / 6.0f);
break;
case CURVE_END_1:
result[0] = tinv3 * (1 / 6.0f);
result[1] = -tinv3 * 0.5f + tinv2 * 0.5f + tinv * 0.5f + (1 / 6.0f);
result[2] = tinv3 * (7 / 12.0f) - tinv2 * 1.25f + tinv * 0.25f + (7 / 12.0f);
result[3] = t3 * 0.25f;
break;
case CURVE_END_2:
result[0] = tinv3 * (1 / 6.0f);
result[1] = -tinv3 * (11 / 12.0f) + tinv2 * 1.5f;
result[2] = tinv3 * 1.75f - tinv2 * 4.5f + tinv * 3.0f;
result[3] = t3;
break;
}
}
2019-09-01 15:50:50 -04:00
/**
* Initialize a spline animation.
2020-06-02 12:44:34 -04:00
* 'keyFrames' should be an array of (s, x, y, z) vectors
2019-09-01 15:50:50 -04:00
* s: the speed of the keyframe in 1000/frames, e.g. s=100 means the keyframe lasts 10 frames
* (x, y, z): point in 3D space on the curve
* The array should end with three entries with s=0 (infinite keyframe duration).
* That's because the spline has a 3rd degree polynomial, so it looks 3 points ahead.
2019-08-25 00:46:40 -04:00
*/
void anim_spline_init(Vec4s *keyFrames) {
gSplineKeyframe = keyFrames;
gSplineKeyframeFraction = 0;
gSplineState = 1;
}
2019-09-01 15:50:50 -04:00
/**
* Poll the next point from a spline animation.
* anim_spline_init should be called before polling for vectors.
* Returns TRUE when the last point is reached, FALSE otherwise.
2019-08-25 00:46:40 -04:00
*/
s32 anim_spline_poll(Vec3f result) {
Vec4f weights;
s32 i;
s32 hasEnded = FALSE;
2021-09-23 18:56:14 -07:00
vec3_zero(result);
2019-08-25 00:46:40 -04:00
spline_get_weights(weights, gSplineKeyframeFraction, gSplineState);
for (i = 0; i < 4; i++) {
result[0] += weights[i] * gSplineKeyframe[i][1];
result[1] += weights[i] * gSplineKeyframe[i][2];
result[2] += weights[i] * gSplineKeyframe[i][3];
}
if ((gSplineKeyframeFraction += gSplineKeyframe[0][0] / 1000.0f) >= 1) {
gSplineKeyframe++;
gSplineKeyframeFraction--;
switch (gSplineState) {
case CURVE_END_2:
hasEnded = TRUE;
break;
case CURVE_MIDDLE:
2019-09-01 15:50:50 -04:00
if (gSplineKeyframe[2][0] == 0) {
2019-08-25 00:46:40 -04:00
gSplineState = CURVE_END_1;
2019-09-01 15:50:50 -04:00
}
2019-08-25 00:46:40 -04:00
break;
default:
gSplineState++;
break;
}
}
return hasEnded;
}
/// Multiply vector 'dest' by a
void vec3f_mul(Vec3f dest, f32 a) {
2021-09-23 18:56:14 -07:00
vec3_mul_val(dest, a);
}
/// Get length of vector 'a'
f32 vec3f_length(Vec3f a) {
return sqrtf(sqr(a[0]) + sqr(a[1]) + sqr(a[2]));
}
/// Get dot product of vectors 'a' and 'b'
f32 vec3f_dot(Vec3f a, Vec3f b) {
2021-09-23 18:56:14 -07:00
return vec3_dot(a, b);
}
/// Make 'dest' the difference of vectors a and b.
void vec3f_dif(Vec3f dest, Vec3f a, Vec3f b) {
2021-09-23 18:56:14 -07:00
vec3_diff(dest, a, b);
}