flak_m/zscript/dt_Gutamatics/GlobalMaths.zsc
Marisa the Magician 602a89cc68 1.2 update w/ GZDoom 4.9 features:
- 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.
2022-11-05 23:59:16 +01:00

173 lines
5.5 KiB
Text

class dt_GM_GlobalMaths {
/// Returns the sign of s.
static int sign(double s) {
if (s > 0) return 1;
if (s < 0) return -1;
return 0;
}
/// Copies the sign from signSource to source.
static int copySignInt(int source, int signSource) {
return abs(source) * sign(signSource);
}
/// Copies the sign from signSource to source.
static double copySignDouble(double source, double signSource) {
return abs(source) * sign(signSource);
}
/// Remaps a value in a range to another range.
static double remapRange(double value, double range1L, double range1H, double range2L, double range2H) {
return range2L + (value - range1L) * (range2H - range1H) / (range1H - range1L);
}
/// Remaps a value in a range to another range.
static int remapRangeInt(int value, int range1L, int range1H, int range2L, int range2H) {
return int(range2L + (value - range1L) * (range2H - range1H) / double(range1H - range1L));
}
/// Returns if two values are close enough to be considered equal.
static bool closeEnough(double a, double b, double epsilon = double.epsilon) {
if (a == b) return true;
return abs(a - b) <= epsilon;
}
// Creates a smoothed transition between edge0 and edge1.
static double smoothStep(double x, double edge0 = 0, double edge1 = 1) {
x = clamp((x - edge0) / (edge1 - edge0), 0, 1);
return x * x * (3 - 2 * x);
}
// Creates a smoother transition between edge0 and edge1.
static double smootherStep(double x, double edge0 = 0, double edge1 = 1) {
x = clamp((x - edge0) / (edge1 - edge0), 0, 1);
return x * x * x * (x * (x * 6 - 15) + 10);
}
/// Converts from horizontal FOV to vertical FOV, according to how GZDoom handles it.
static double fovHToY(double fovH) {
// this is how gzdoom does it internally, so i'm using it here
double aspect = Screen.getAspectRatio();
double fovratio = (aspect >= 1.3) ? 1.333333 : aspect;
return 2 * atan(tan(clamp(fovH, 5, 170) / 2.0) / fovratio);
}
/// Linearly interpolates between two doubles, clamping the parameters.
static double lerpDouble(double from, double to, double time) {
time = clamp(time, 0, 1);
return lerpUnclampedDouble(from, to, time);
}
/// Linearly interpolates between two doubles.
static double lerpUnclampedDouble(double from, double to, double time) {
double ret;
double reverseTime = 1 - time;
ret = reverseTime * from + time * to;
return ret;
}
// Converts from Normalised Device Coordinates to Viewport coordinates.
// This is `ui` scope to safely access `screenblocks`.
static ui Vector2 ndcToViewport(Vector3 ndcCoords, bool useScreenblocks = true) {
if (useScreenblocks) {
int viewwindowx, viewwindowy, viewwidth, viewheight;
[viewwindowx, viewwindowy, viewwidth, viewheight] = Screen.getViewWindow();
int screenHeight = Screen.getHeight();
int height = screenHeight;
if (screenblocks < 10) {
height = (screenblocks * screenHeight / 10) & ~7;
}
int bottom = screenHeight - (height + viewwindowy - ((height - viewheight) / 2));
return (viewwindowx, screenHeight - bottom - height) + (((ndcCoords.x + 1) * viewwidth) / 2, ((-ndcCoords.y + 1) * height) / 2);
}
else {
return (((ndcCoords.x + 1) * Screen.getWidth()) / 2, ((-ndcCoords.y + 1) * Screen.getHeight()) / 2);
}
}
enum OutCode {
OUT_Inside = 0,
OUT_Left = 1 << 0,
OUT_Right = 1 << 1,
OUT_Bottom = 1 << 2,
OUT_Top = 1 << 3
}
/// Computes an outcode for a point in a rectangle.
static OutCode computeOutcode(Vector2 point, Vector2 min, Vector2 max) {
OutCode code = OUT_Inside;
if (point.x < min.x) {
code |= OUT_Left;
}
else if (point.x > max.x) {
code |= OUT_Right;
}
if (point.y < min.y) {
code |= OUT_Top;
}
else if (point.y > max.y) {
code |= OUT_Bottom;
}
return code;
}
/// Clips a line to a rectangle.
static bool, Vector2, Vector2 cohenSutherlandClip(Vector2 point0, Vector2 point1, Vector2 min, Vector2 max) {
OutCode outcode0 = computeOutCode(point0, min, max);
OutCode outcode1 = computeOutCode(point1, min, max);
while (true) {
// trivial accept - points are both on screen
if ((outcode0 | outcode1) == 0) {
return true, point0, point1;
}
// trivial reject - points are in the same region offscreen
else if ((outcode0 & outcode1) != 0) {
return false, point0, point1;
}
else {
Vector2 new;
OutCode outcodeOut = (outcode0 != 0) ? outcode0 : outcode1;
if ((outcodeOut & OUT_Bottom) != 0) {
new.x = point0.x + (point1.x - point0.x) * (max.y - point0.y) / (point1.y - point0.y);
new.y = max.y;
}
else if ((outcodeOut & OUT_Top) != 0) {
new.x = point0.x + (point1.x - point0.x) * (min.y - point0.y) / (point1.y - point0.y);
new.y = min.y;
}
else if ((outcodeOut & OUT_Right) != 0) {
new.y = point0.y + (point1.y - point0.y) * (max.x - point0.x) / (point1.x - point0.x);
new.x = max.x;
}
else if ((outcodeOut & OUT_Left) != 0) {
new.y = point0.y + (point1.y - point0.y) * (min.x - point0.x) / (point1.x - point0.x);
new.x = min.x;
}
if (outcodeOut == outCode0) {
point0.x = new.x;
point0.y = new.y;
outCode0 = computeOutCode(point0, min, max);
}
else {
point1.x = new.x;
point1.y = new.y;
outCode1 = computeOutCode(point1, min, max);
}
}
}
return false, (0, 0), (0, 0);
}
// Normalizes an angle to (-180, 180]. Like Actor.normalize180, but callable in data scope.
static double normalize180(double ang) {
ang = ang % 360;
ang = (ang + 360) % 360;
if (ang > 180) ang -= 360;
return ang;
}
}