Path: blob/master/SonicMania/Objects/Helpers/MathHelpers.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: MathHelpers Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectMathHelpers *MathHelpers = NULL;1011void MathHelpers_Update(void) {}1213void MathHelpers_LateUpdate(void) {}1415void MathHelpers_StaticUpdate(void) {}1617void MathHelpers_Draw(void) {}1819void MathHelpers_Create(void *data) {}2021void MathHelpers_StageLoad(void) {}2223void MathHelpers_LerpToPos(Vector2 *pos, int32 percent, int32 posX, int32 posY)24{25if (percent < 0) {26pos->x = 0;27pos->y = 0;28}29else if (percent >= 0x100) {30pos->x = posX;31pos->y = posY;32}33else {34pos->x = percent * (posX >> 8);35pos->y = percent * (posY >> 8);36}37}3839void MathHelpers_Lerp(Vector2 *pos, int32 percent, int32 startX, int32 startY, int32 endX, int32 endY)40{41if (percent < 0) {42pos->x = startX;43pos->y = startY;44}45else if (percent >= 0x100) {46pos->x = endX;47pos->y = endY;48}49else {50pos->x = startX + percent * ((endX - startX) >> 8);51pos->y = startY + percent * ((endY - startY) >> 8);52}53}5455void MathHelpers_LerpSin1024(Vector2 *pos, int32 percent, int32 startX, int32 startY, int32 endX, int32 endY)56{57if (percent < 0) {58pos->x = startX;59pos->y = startY;60}61else if (percent >= 0x100) {62pos->x = endX;63pos->y = endY;64}65else {66int32 lerpPercent = (RSDK.Sin1024(percent + 0x300) >> 2) + 0x100;67pos->x = startX + lerpPercent * ((endX - startX) >> 8);68pos->y = startY + lerpPercent * ((endY - startY) >> 8);69}70}7172void MathHelpers_Lerp2Sin1024(Vector2 *pos, int32 percent, int32 startX, int32 startY, int32 endX, int32 endY)73{74if (percent < 0) {75pos->x = startX;76pos->y = startY;77}78else if (percent >= 0x100) {79pos->x = endX;80pos->y = endY;81}82else {83int32 lerpPercent = RSDK.Sin1024(percent) >> 2;84pos->x = startX + lerpPercent * ((endX - startX) >> 8);85pos->y = startY + lerpPercent * ((endY - startY) >> 8);86}87}8889void MathHelpers_LerpSin512(Vector2 *pos, int32 percent, int32 startX, int32 startY, int32 endX, int32 endY)90{91if (percent < 0) {92pos->x = startX;93pos->y = startY;94}95else if (percent >= 0x100) {96pos->x = endX;97pos->y = endY;98}99else {100int32 lerpPercent = (RSDK.Sin512(percent + 0x180) >> 2) + 0x80;101pos->x = startX + lerpPercent * ((endX - startX) >> 8);102pos->y = startY + lerpPercent * ((endY - startY) >> 8);103}104}105106Vector2 MathHelpers_GetBezierPoint(int32 percent, int32 x1, int32 y1, int32 x2, int32 y2, int32 x3, int32 y3, int32 x4, int32 y4)107{108int32 invPercent = 0x10000 - percent;109int32 point1 = invPercent * ((uint32)(invPercent * invPercent) >> 16) >> 16;110int32 point2 = percent * ((uint32)(invPercent * invPercent) >> 16) >> 16;111int32 point3 = invPercent * ((uint32)(percent * percent) >> 16) >> 16;112int32 point4 = percent * ((uint32)(percent * percent) >> 16) >> 16;113114Vector2 resultPos;115resultPos.x =116point4 * (x4 >> 16) + point3 * (x3 >> 16) + point2 * (x2 >> 16) + point1 * (x1 >> 16) + 2 * point2 * (x2 >> 16) + 2 * point3 * (x3 >> 16);117resultPos.y =118point4 * (y4 >> 16) + point3 * (y3 >> 16) + point2 * (y2 >> 16) + point1 * (y1 >> 16) + 2 * point2 * (y2 >> 16) + 2 * point3 * (y3 >> 16);119return resultPos;120}121122int32 MathHelpers_SquareRoot(uint32 num)123{124int32 rem = 1 << 30; // 1 << 31 would result in the value having to be unsigned, so this is the max125while (rem > num) rem >>= 2;126127uint32 root = 0;128while (rem) {129if (num >= rem + root) {130num -= rem + root;131root += rem << 1;132}133134rem >>= 2;135root >>= 1;136}137138return num <= root ? root : (root + 1);139}140141int32 MathHelpers_Distance(int32 x1, int32 y1, int32 x2, int32 y2)142{143int32 distanceX = abs(x2 - x1) >> 16;144int32 distanceY = abs(y2 - y1) >> 16;145146return (MathHelpers_SquareRoot(distanceX * distanceX + distanceY * distanceY) << 16);147}148149int32 MathHelpers_GetBezierCurveLength(int32 x1, int32 y1, int32 x2, int32 y2, int32 x3, int32 y3, int32 x4, int32 y4)150{151int32 lastX = x1;152int32 lastY = y1;153154int32 length = 0;155// 0x10000 = 1.0156// 0xCCC == 0.05157for (int32 percent = 0xCCC; percent <= 0x10000; percent += 0xCCC) {158Vector2 point = MathHelpers_GetBezierPoint(percent, x1, y1, x2, y2, x3, y3, x4, y4);159160length += MathHelpers_Distance(lastX, lastY, point.x, point.y);161lastX = point.x;162lastY = point.y;163}164return length;165}166167bool32 MathHelpers_PointInHitbox(int32 thisX, int32 thisY, int32 otherX, int32 otherY, int32 direction, Hitbox *hitbox)168{169int32 left, top, right, bottom;170171if ((direction & FLIP_X)) {172left = -hitbox->left;173right = -hitbox->right;174}175else {176right = hitbox->right;177left = hitbox->left;178}179180if ((direction & FLIP_Y)) {181bottom = -hitbox->bottom;182top = -hitbox->top;183}184else {185bottom = hitbox->bottom;186top = hitbox->top;187}188int32 hitboxX2 = right;189if (left < right)190hitboxX2 = left;191int32 hitboxX1 = left;192if (right > left)193hitboxX1 = right;194195int32 hitboxY1 = top;196int32 hitboxY2 = bottom;197if (top < bottom)198hitboxY2 = top;199if (bottom > top)200hitboxY1 = bottom;201return otherX >= thisX + (hitboxX2 << 16) && otherX <= thisX + (hitboxX1 << 16) && otherY >= thisY + (hitboxY2 << 16)202&& otherY <= thisY + (hitboxY1 << 16);203}204205bool32 MathHelpers_PositionBoxesIntersect(int32 otherX1, int32 otherY1, int32 otherX2, int32 otherY2, int32 thisX1, int32 thisY1, int32 thisX2,206int32 thisY2)207{208int32 left_other = MIN(otherX1, otherX2);209int32 top_other = MIN(otherY1, otherY2);210int32 right_other = MAX(otherX1, otherX2);211int32 bottom_other = MAX(otherY1, otherY2);212213int32 left_this = MIN(thisX1, thisX2);214int32 top_this = MIN(thisY1, thisY2);215int32 right_this = MAX(thisX1, thisX2);216int32 bottom_this = MAX(thisY1, thisY2);217218return left_other <= right_this && right_other >= left_this && top_other <= bottom_this && bottom_other >= top_this;219}220int32 MathHelpers_GetInteractionDir(int32 otherX1, int32 otherY1, int32 otherX2, int32 otherY2, int32 thisX, int32 thisY)221{222int32 dir = ((thisY - otherY1) >> 16) * ((otherX2 - otherX1) >> 16) - ((thisX - otherX1) >> 16) * ((otherY2 - otherY1) >> 16);223return dir > 0 ? 1 : dir < 0 ? -1 : 0;224}225bool32 MathHelpers_CheckValidIntersect(int32 otherX1, int32 otherY1, int32 otherX2, int32 otherY2, int32 thisX, int32 thisY)226{227if (otherX2 > otherX1) {228if (thisX < otherX1 || thisX > otherX2)229return false;230return true;231}232233if (otherX2 < otherX1) {234if (thisX < otherX1 || thisX > otherX1)235return false;236return true;237}238239if (otherY2 > otherY1) {240if (thisY < otherY1 || thisY > otherY2)241return false;242return true;243}244245if (otherY2 < otherY1) {246if (thisY < otherY2 || thisY > otherY1)247return false;248return true;249}250251if (thisX < otherX1 || thisY < otherY1)252return false;253return true;254}255int32 MathHelpers_CheckPositionOverlap(int32 otherX1, int32 otherY1, int32 otherX2, int32 otherY2, int32 thisX1, int32 thisY1, int32 thisX2,256int32 thisY2)257{258// Creates "hitboxes" from the positions and does a quick check to see if they overlap259if (!MathHelpers_PositionBoxesIntersect(otherX1, otherY1, otherX2, otherY2, thisX1, thisY1, thisX2, thisY2))260return false;261262if (otherX1 == otherX2 && otherY1 == otherY2) {263if (otherX1 != thisX1 || otherY1 != thisY1) {264if (otherX1 == thisX2 && otherY1 == thisY2)265return true;266return false;267}268return true;269}270271if (thisX1 == thisX2 && thisY1 == thisY2) {272if (thisX1 == otherX1 && thisY1 == otherY1)273return true;274275if (thisX1 == otherX2 && thisY1 == otherY2)276return true;277278return false;279}280281int32 thisInteractDir1 = MathHelpers_GetInteractionDir(otherX1, otherY1, otherX2, otherY2, thisX1, thisY1);282int32 thisInteractDir2 = MathHelpers_GetInteractionDir(otherX1, otherY1, otherX2, otherY2, thisX2, thisY2);283284if (thisInteractDir1) {285if (thisInteractDir1 == thisInteractDir2)286return false;287}288else if (!thisInteractDir2) {289if (MathHelpers_CheckValidIntersect(otherX1, otherY1, otherX2, otherY2, thisX1, thisY1)290|| MathHelpers_CheckValidIntersect(otherX1, otherY1, otherX2, otherY2, thisX2, thisY2)291|| MathHelpers_CheckValidIntersect(thisX1, thisY1, thisX2, thisY2, otherX1, otherY1)292|| MathHelpers_CheckValidIntersect(thisX1, thisY1, thisX2, thisY2, otherX2, otherY2)) {293return true;294}295296return false;297}298299int32 otherInteractDir1 = MathHelpers_GetInteractionDir(thisX1, thisY1, thisX2, thisY2, otherX1, otherY1);300if (!otherInteractDir1)301return true;302303int32 otherInteractDir2 = MathHelpers_GetInteractionDir(thisX1, thisY1, thisX2, thisY2, otherX2, otherY2);304if (otherInteractDir1 == otherInteractDir2)305return false;306307return true;308}309310int32 MathHelpers_GetEdgeDistance(int32 distance, int32 radius)311{312uint32 dist = abs(distance);313uint32 rad = abs(radius);314315uint32 result1 = (dist >> 16) * (rad >> 16) << 16;316uint32 result2 = (dist >> 16) * (rad & 0xFFFF);317uint32 result3 = (dist & 0xFFFF) * (rad >> 16);318uint32 result4 = (dist & 0xFFFF) * (rad & 0xFFFF) >> 16;319320uint32 edgeDistance = result1 + result2 + result3 + result4;321if ((radius ^ ~distance) >= 0) // if the signs do not match322return -(int32)edgeDistance;323else324return edgeDistance;325}326327bool32 MathHelpers_ConstrainToBox(Vector2 *pos, int32 x, int32 y, Vector2 boxPos, Hitbox hitbox)328{329int32 left = MIN(hitbox.left, hitbox.right);330int32 right = MAX(hitbox.right, hitbox.left);331int32 top = MIN(hitbox.top, hitbox.bottom);332int32 bottom = MAX(hitbox.bottom, hitbox.top);333334int32 boxPosLeft = boxPos.x + (left << 16);335int32 boxPosTop = boxPos.y + (top << 16);336int32 boxPosRight = boxPos.x + (right << 16);337int32 boxPosBottom = boxPos.y + (bottom << 16);338339if (x > boxPosLeft && x < boxPosRight && y > boxPosTop && y < boxPosBottom)340return false;341342// Check if they're on the same pixel on x axis (ignores subpixel positions)343if (!((x ^ boxPos.x) & 0xFFFF0000)) {344if (pos) {345pos->x = x & 0xFFFF0000;346if (y <= boxPos.y)347pos->y = boxPosTop;348else349pos->y = boxPosBottom;350}351return true;352}353354// Check if they're on the same pixel on y axis (ignores subpixel positions)355if (!((y ^ boxPos.y) & 0xFFFF0000)) {356if (pos) {357if (x <= boxPos.x)358pos->x = boxPosLeft;359else360pos->x = boxPosRight;361pos->y = y & 0xFFFF0000;362}363return true;364}365366double div = 1.0f / 65536.0f;367int32 radius = (((boxPos.y - y) * div) / ((boxPos.x - x) * div)) * 65536.0f;368if (!radius)369return false;370371int32 posY = 0;372if (x <= boxPos.x) {373posY = y + MathHelpers_GetEdgeDistance(boxPosLeft - x, radius);374if (boxPosTop <= posY && posY <= boxPosBottom) {375if (pos) {376pos->x = boxPosLeft;377pos->y = posY;378}379return true;380}381}382383if (x >= boxPos.x) {384posY = y + MathHelpers_GetEdgeDistance(boxPosRight - x, radius);385if (boxPosTop <= posY && posY <= boxPosBottom) {386if (pos) {387pos->x = boxPosRight;388pos->y = posY;389}390return true;391}392}393394if (y <= boxPos.y) {395radius = (((boxPosTop - y) * div) / (radius * div)) * -65536.0;396if (boxPosLeft <= x - radius && x - radius <= boxPosRight) {397if (pos) {398pos->x = x - radius;399pos->y = boxPosTop;400}401return true;402}403}404405if (y >= boxPos.y) {406radius = x - ((((boxPosBottom - y) * div) / (radius * div)) * -65536.0f);407if (boxPosLeft <= radius && radius <= boxPosRight) {408if (pos) {409pos->x = radius;410pos->y = boxPosBottom;411}412return true;413}414}415416return false;417}418419// RSDKv5U changed how the setPos param works, so this is added for compatibility420#if RETRO_REV0U421uint8 MathHelpers_CheckBoxCollision(void *thisEntity, Hitbox *thisHitbox, void *otherEntity, Hitbox *otherHitbox)422{423Entity *other = (Entity *)otherEntity;424425Vector2 storePos = other->position;426Vector2 storeVel = other->velocity;427int32 storeGroundVel = other->groundVel;428bool32 storeOnGround = other->onGround;429int32 storeAngle = other->angle;430431uint8 side = RSDK.CheckObjectCollisionBox(thisEntity, thisHitbox, otherEntity, otherHitbox, true);432Vector2 collidePos = other->position;433434other->position = storePos;435other->velocity = storeVel;436other->groundVel = storeGroundVel;437other->onGround = storeOnGround;438other->angle = storeAngle;439switch (side) {440default:441case C_NONE: break;442case C_TOP: other->position.y = collidePos.y; break;443case C_LEFT: other->position.x = collidePos.x; break;444case C_RIGHT: other->position.x = collidePos.x; break;445case C_BOTTOM: other->position.y = collidePos.y; break;446}447448return side;449}450#else451uint8 MathHelpers_CheckBoxCollision(void *thisEntity, Hitbox *thisHitbox, void *otherEntity, Hitbox *otherHitbox)452{453return RSDK.CheckObjectCollisionBox(thisEntity, thisHitbox, otherEntity, otherHitbox, false);454}455#endif456457#if GAME_INCLUDE_EDITOR458void MathHelpers_EditorDraw(void) {}459460void MathHelpers_EditorLoad(void) {}461#endif462463void MathHelpers_Serialize(void) {}464465466