- Changeable player skins. - Ammo LEDs are now 1:1 with UT by using canvas textures. - Integrate some add-ons, including reskins. - Various fixes (some backported from Demolitionist). - Migrated from libeye to Gutamatics.
319 lines
11 KiB
Text
319 lines
11 KiB
Text
class dt_GM_Matrix4 {
|
|
double values[4][4];
|
|
|
|
/// Initialises a new Matrix4 in a static context.
|
|
static dt_GM_Matrix4 create() {
|
|
return new("dt_GM_Matrix4");
|
|
}
|
|
|
|
/// Returns an identity matrix.
|
|
static dt_GM_Matrix4 identity() {
|
|
let ret = dt_GM_Matrix4.create();
|
|
ret.values[0][0] = 1;
|
|
ret.values[1][1] = 1;
|
|
ret.values[2][2] = 1;
|
|
ret.values[3][3] = 1;
|
|
return ret;
|
|
}
|
|
|
|
/// Returns a rotation matrix from euler angles.
|
|
static dt_GM_Matrix4 fromEulerAngles(double yaw, double pitch, double roll) {
|
|
dt_GM_Matrix4 rYaw = dt_GM_Matrix4.identity();
|
|
double sYaw = sin(yaw);
|
|
double cYaw = cos(yaw);
|
|
rYaw.values[0][0] = cYaw;
|
|
rYaw.values[0][1] = -sYaw;
|
|
rYaw.values[1][0] = sYaw;
|
|
rYaw.values[1][1] = cYaw;
|
|
|
|
dt_GM_Matrix4 rPitch = dt_GM_Matrix4.identity();
|
|
double sPitch = sin(pitch);
|
|
double cPitch = cos(pitch);
|
|
rPitch.values[0][0] = cPitch;
|
|
rPitch.values[2][0] = -sPitch;
|
|
rPitch.values[0][2] = sPitch;
|
|
rPitch.values[2][2] = cPitch;
|
|
|
|
dt_GM_Matrix4 rRoll = dt_GM_Matrix4.identity();
|
|
double sRoll = sin(roll);
|
|
double cRoll = cos(roll);
|
|
rRoll.values[1][1] = cRoll;
|
|
rRoll.values[1][2] = -sRoll;
|
|
rRoll.values[2][1] = sRoll;
|
|
rRoll.values[2][2] = cRoll;
|
|
|
|
// concatenate ypr to get the final matrix
|
|
dt_GM_Matrix4 ret = rYaw.multiplyMatrix(rPitch);
|
|
ret = ret.multiplyMatrix(rRoll);
|
|
return ret;
|
|
}
|
|
|
|
/// Returns a rotation matrix from an axis and an angle.
|
|
static dt_GM_Matrix4 fromAxisAngle(Vector3 axis, double angle) {
|
|
dt_GM_Matrix4 ret = dt_GM_Matrix4.identity();
|
|
double c = cos(angle);
|
|
double s = sin(angle);
|
|
double x = axis.x;
|
|
double y = axis.y;
|
|
double z = axis.z;
|
|
|
|
ret.values[0][0] = (x * x * (1.0 - c) + c);
|
|
ret.values[0][1] = (x * y * (1.0 - c) - z * s);
|
|
ret.values[0][2] = (x * z * (1.0 - c) + y * s);
|
|
ret.values[1][0] = (y * x * (1.0 - c) + z * s);
|
|
ret.values[1][1] = (y * y * (1.0 - c) + c);
|
|
ret.values[1][2] = (y * z * (1.0 - c) - x * s);
|
|
ret.values[2][0] = (x * z * (1.0 - c) - y * s);
|
|
ret.values[2][1] = (y * z * (1.0 - c) + x * s);
|
|
ret.values[2][2] = (z * z * (1.0 - c) + c);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/// Converts back from the rotation matrix to euler angles.
|
|
double, double, double rotationToEulerAngles() {
|
|
if (dt_GM_GlobalMaths.closeEnough(values[2][0], -1)) {
|
|
double x = 90;
|
|
double y = 0;
|
|
double z = atan2(values[0][1], values[0][2]);
|
|
return z, x, y;
|
|
}
|
|
else if (dt_GM_GlobalMaths.closeEnough(values[2][0], 1)) {
|
|
double x = -90;
|
|
double y = 0;
|
|
double z = atan2(-values[0][1], -values[0][2]);
|
|
return z, x, y;
|
|
}
|
|
else {
|
|
float x1 = -asin(values[2][0]);
|
|
float x2 = 180 - x1;
|
|
|
|
float y1 = atan2(values[2][1] / cos(x1), values[2][2] / cos(x1));
|
|
float y2 = atan2(values[2][1] / cos(x2), values[2][2] / cos(x2));
|
|
|
|
float z1 = atan2(values[1][0] / cos(x1), values[0][0] / cos(x1));
|
|
float z2 = atan2(values[1][0] / cos(x2), values[0][0] / cos(x2));
|
|
|
|
if ((abs(x1) + abs(y1) + abs(z1)) <= (abs(x2) + abs(y2) + abs(z2))) {
|
|
return z1, x1, y1;
|
|
}
|
|
else {
|
|
return z2, x2, y2;
|
|
}
|
|
}
|
|
}
|
|
|
|
static dt_GM_Matrix4 createTRSEuler(Vector3 translate, double yaw, double pitch, double roll, Vector3 scale) {
|
|
dt_GM_Matrix4 translateMat = dt_GM_Matrix4.identity();
|
|
translateMat.values[0][3] = translate.x;
|
|
translateMat.values[1][3] = translate.y;
|
|
translateMat.values[2][3] = translate.z;
|
|
|
|
dt_GM_Matrix4 rotateMat = dt_GM_Matrix4.fromEulerAngles(yaw, pitch, roll);
|
|
|
|
dt_GM_Matrix4 scaleMat = dt_GM_Matrix4.identity();
|
|
scaleMat.values[0][0] = scale.x;
|
|
scaleMat.values[1][1] = scale.y;
|
|
scaleMat.values[2][2] = scale.z;
|
|
|
|
dt_GM_Matrix4 ret = translateMat.multiplyMatrix(rotateMat);
|
|
ret = ret.multiplyMatrix(scaleMat);
|
|
return ret;
|
|
}
|
|
|
|
static dt_GM_Matrix4 createTRSAxisAngle(Vector3 translate, Vector3 axis, double angle, Vector3 scale) {
|
|
dt_GM_Matrix4 translateMat = dt_GM_Matrix4.identity();
|
|
translateMat.values[0][3] = translate.x;
|
|
translateMat.values[1][3] = translate.y;
|
|
translateMat.values[2][3] = translate.z;
|
|
|
|
dt_GM_Matrix4 rotateMat = dt_GM_Matrix4.fromAxisAngle(axis, angle);
|
|
|
|
dt_GM_Matrix4 scaleMat = dt_GM_Matrix4.identity();
|
|
scaleMat.values[0][0] = scale.x;
|
|
scaleMat.values[1][1] = scale.y;
|
|
scaleMat.values[2][2] = scale.z;
|
|
|
|
dt_GM_Matrix4 ret = translateMat.multiplyMatrix(rotateMat);
|
|
ret = ret.multiplyMatrix(scaleMat);
|
|
return ret;
|
|
}
|
|
|
|
/// Returns a view matrix.
|
|
static dt_GM_Matrix4 view(Vector3 camPos, double yaw, double pitch, double roll) {
|
|
// all of this is basically lifted and converted from PolyRenderer::SetupPerspectiveMatrix(),
|
|
// so credit goes to Graf Zahl/dpJudas/whoever else
|
|
// pitch needs to be adjusted by the pixel ratio
|
|
float pixelRatio = level.pixelstretch;
|
|
double angx = cos(pitch);
|
|
double angy = sin(pitch) * pixelRatio;
|
|
double alen = sqrt(angx * angx + angy * angy);
|
|
double adjustedPitch = asin(angy / alen);
|
|
double adjustedYaw = 90 - yaw;
|
|
|
|
// rotations
|
|
let cz = cos(roll);
|
|
let sz = sin(roll);
|
|
let cx = cos(adjustedPitch);
|
|
let sx = sin(adjustedPitch);
|
|
let cy = cos(adjustedYaw);
|
|
let sy = sin(adjustedYaw);
|
|
|
|
let rot = dt_GM_Matrix4.create();
|
|
rot.values[0][0] = cz * cy - sz * sx * sy;
|
|
rot.values[0][1] = -sz * cx;
|
|
rot.values[0][2] = cz * sy + sz * sx * cy;
|
|
|
|
rot.values[1][0] = sz * cy + cz * sx * sy;
|
|
rot.values[1][1] = cz * cx;
|
|
rot.values[1][2] = sz * sy - cz * sx * cy;
|
|
|
|
rot.values[2][0] = -cx * sy;
|
|
rot.values[2][1] = sx;
|
|
rot.values[2][2] = cx * cy;
|
|
|
|
rot.values[3][3] = 1.0;
|
|
|
|
// pixel ratio scaling
|
|
dt_GM_Matrix4 scale = dt_GM_Matrix4.identity();
|
|
scale.values[1][1] = pixelRatio;
|
|
// swapping y and z
|
|
dt_GM_Matrix4 swapYZ = dt_GM_Matrix4.create();
|
|
swapYZ.values[0][0] = 1;
|
|
swapYZ.values[1][2] = 1;
|
|
swapYZ.values[2][1] = -1;
|
|
swapYZ.values[3][3] = 1;
|
|
// translation
|
|
dt_GM_Matrix4 translate = dt_GM_Matrix4.identity();
|
|
translate.values[0][3] = -camPos.x;
|
|
translate.values[1][3] = -camPos.y;
|
|
translate.values[2][3] = -camPos.z;
|
|
|
|
// concatenate them all to get a final matrix
|
|
dt_GM_Matrix4 ret = rot.multiplyMatrix(scale);
|
|
ret = ret.multiplyMatrix(swapYZ);
|
|
ret = ret.multiplyMatrix(translate);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/// Returns a perspective matrix (same format as gluPerspective).
|
|
static dt_GM_Matrix4 perspective(double fovy, double aspect, double zNear, double zFar) {
|
|
dt_GM_Matrix4 ret = dt_GM_Matrix4.create();
|
|
double f = 1 / tan(fovy / 2.0);
|
|
// x coord
|
|
ret.values[0][0] = f / aspect;
|
|
// y coord
|
|
ret.values[1][1] = f;
|
|
// z buffer coord
|
|
ret.values[2][2] = (zFar + zNear) / (zNear - zFar);
|
|
ret.values[2][3] = (2 * zFar * zNear) / (zNear - zFar);
|
|
// w (homogeneous coordinates)
|
|
ret.values[3][2] = -1;
|
|
return ret;
|
|
}
|
|
|
|
/// Returns a world->clip coords matrix from the passed args.
|
|
static dt_GM_Matrix4 worldToClip(Vector3 viewPos, double yaw, double pitch, double roll, double FOV) {
|
|
double aspect = Screen.getAspectRatio();
|
|
double fovy = dt_GM_GlobalMaths.fovHToY(FOV);
|
|
dt_GM_Matrix4 view = dt_GM_Matrix4.view(viewPos, yaw, pitch, roll);
|
|
// 5 & 65535 are what are used internally, so they're used here for consistency
|
|
dt_GM_Matrix4 perp = dt_GM_Matrix4.perspective(fovy, aspect, 5, 65535);
|
|
dt_GM_Matrix4 worldToClip = perp.multiplyMatrix(view);
|
|
return worldToClip;
|
|
}
|
|
|
|
/// Adds two matrices and returns the result.
|
|
dt_GM_Matrix4 addMatrix(dt_GM_Matrix4 other) const {
|
|
dt_GM_Matrix4 ret = dt_GM_Matrix4.create();
|
|
ret.values[0][0] = values[0][0] + other.values[0][0];
|
|
ret.values[0][1] = values[0][1] + other.values[0][1];
|
|
ret.values[0][2] = values[0][2] + other.values[0][2];
|
|
ret.values[0][3] = values[0][3] + other.values[0][3];
|
|
ret.values[1][0] = values[1][0] + other.values[1][0];
|
|
ret.values[1][1] = values[1][1] + other.values[1][1];
|
|
ret.values[1][2] = values[1][2] + other.values[1][2];
|
|
ret.values[1][3] = values[1][3] + other.values[1][3];
|
|
ret.values[2][0] = values[2][0] + other.values[2][0];
|
|
ret.values[2][1] = values[2][1] + other.values[2][1];
|
|
ret.values[2][2] = values[2][2] + other.values[2][2];
|
|
ret.values[2][3] = values[2][3] + other.values[2][3];
|
|
ret.values[3][0] = values[3][0] + other.values[3][0];
|
|
ret.values[3][1] = values[3][1] + other.values[3][1];
|
|
ret.values[3][2] = values[3][2] + other.values[3][2];
|
|
ret.values[3][3] = values[3][3] + other.values[3][3];
|
|
return ret;
|
|
}
|
|
|
|
/// Multiplies the matrix by a scalar and returns the result.
|
|
dt_GM_Matrix4 multiplyScalar(double scalar) const {
|
|
dt_GM_Matrix4 ret = dt_GM_Matrix4.create();
|
|
ret.values[0][0] = values[0][0] * scalar;
|
|
ret.values[0][1] = values[0][1] * scalar;
|
|
ret.values[0][2] = values[0][2] * scalar;
|
|
ret.values[0][3] = values[0][3] * scalar;
|
|
ret.values[1][0] = values[1][0] * scalar;
|
|
ret.values[1][1] = values[1][1] * scalar;
|
|
ret.values[1][2] = values[1][2] * scalar;
|
|
ret.values[1][3] = values[1][3] * scalar;
|
|
ret.values[2][0] = values[2][0] * scalar;
|
|
ret.values[2][1] = values[2][1] * scalar;
|
|
ret.values[2][2] = values[2][2] * scalar;
|
|
ret.values[2][3] = values[2][3] * scalar;
|
|
ret.values[3][0] = values[3][0] * scalar;
|
|
ret.values[3][1] = values[3][1] * scalar;
|
|
ret.values[3][2] = values[3][2] * scalar;
|
|
ret.values[3][3] = values[3][3] * scalar;
|
|
return ret;
|
|
}
|
|
|
|
/// Multiplies two matrices and returns the result.
|
|
dt_GM_Matrix4 multiplyMatrix(dt_GM_Matrix4 other) const {
|
|
dt_GM_Matrix4 ret = dt_GM_Matrix4.create();
|
|
for (int row = 0; row < 4; row++) {
|
|
ret.values[row][0] =
|
|
values[row][0] * other.values[0][0] +
|
|
values[row][1] * other.values[1][0] +
|
|
values[row][2] * other.values[2][0] +
|
|
values[row][3] * other.values[3][0];
|
|
|
|
ret.values[row][1] =
|
|
values[row][0] * other.values[0][1] +
|
|
values[row][1] * other.values[1][1] +
|
|
values[row][2] * other.values[2][1] +
|
|
values[row][3] * other.values[3][1];
|
|
|
|
ret.values[row][2] =
|
|
values[row][0] * other.values[0][2] +
|
|
values[row][1] * other.values[1][2] +
|
|
values[row][2] * other.values[2][2] +
|
|
values[row][3] * other.values[3][2];
|
|
|
|
ret.values[row][3] =
|
|
values[row][0] * other.values[0][3] +
|
|
values[row][1] * other.values[1][3] +
|
|
values[row][2] * other.values[2][3] +
|
|
values[row][3] * other.values[3][3];
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/// Multiplies this Matrix by a 3D vector.
|
|
Vector3 multiplyVector3(Vector3 vec, dt_GM_VectorType type = dt_GM_Vector_Position, bool divideW = true) const {
|
|
let vecW = (type == dt_GM_Vector_Position) ? 1.0 : 0.0;
|
|
|
|
let ret = (
|
|
values[0][0] * vec.x + values[0][1] * vec.y + values[0][2] * vec.z + values[0][3] * vecW,
|
|
values[1][0] * vec.x + values[1][1] * vec.y + values[1][2] * vec.z + values[1][3] * vecW,
|
|
values[2][0] * vec.x + values[2][1] * vec.y + values[2][2] * vec.z + values[2][3] * vecW
|
|
);
|
|
|
|
if (divideW) {
|
|
let retW = values[3][0] * vec.x + values[3][1] * vec.y + values[3][2] * vec.z + values[3][3] * vecW;
|
|
ret /= retW;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
}
|