// // EvoBot - Neoptolemus' Natural Selection bot, based on Botman's HPB bot template // // bot_math.cpp // // Contains all useful math functions for bot stuff // #include "AvHAIMath.h" #include "../dlls/util.h" #include #include #include extern enginefuncs_t g_engfuncs; bool isNumber(const char* line) { char* p; strtol(line, &p, 10); return *p == 0; } bool isFloat(const char* line) { std::string myString = line; std::istringstream iss(myString); float f; iss >> std::noskipws >> f; // noskipws considers leading whitespace invalid // Check the entire string was consumed and if either failbit or badbit is set return iss.eof() && !iss.fail(); } // Returns the normalized surface normal of a triangle defined by v1,v2,v3. Assumes clockwise indices. Vector UTIL_GetSurfaceNormal(const Vector v1, const Vector v2, const Vector v3) { Vector normal(((v2.y - v1.y) * (v3.z - v1.z)) - ((v2.z - v1.z) * (v3.y - v1.y)), ((v2.z - v1.z) * (v3.x - v1.x)) - ((v2.x - v1.x) * (v3.z - v1.z)), ((v2.x - v1.x) * (v3.y - v1.y)) - ((v2.y - v1.y) * (v3.x - v1.x))); UTIL_NormalizeVector(&normal); return normal; } bool vPointOverlaps3D(const Vector Point, const Vector MinBB, const Vector MaxBB) { return (Point.x >= MinBB.x && Point.x <= MaxBB.x && Point.y >= MinBB.y && Point.y <= MaxBB.y && Point.z >= MinBB.z && Point.z <= MaxBB.z); } bool vPointOverlaps2D(const Vector Point, const Vector MinBB, const Vector MaxBB) { return (Point.x >= MinBB.x && Point.x <= MaxBB.x && Point.y >= MinBB.y && Point.y <= MaxBB.y); } bool vBBOverlaps2D(const Vector MinBBA, const Vector MaxBBA, const Vector MinBBB, const Vector MaxBBB) { return ( (MinBBA.x < MaxBBB.x && MaxBBA.x > MinBBB.x) && (MinBBA.y < MaxBBB.y && MaxBBA.y > MinBBB.y)); } // Given three collinear points p, q, r, the function checks if // point q lies on line segment 'pr' bool onSegment(Vector p, Vector q, Vector r) { if (q.x <= fmaxf(p.x, r.x) && q.x >= fminf(p.x, r.x) && q.y <= fmaxf(p.y, r.y) && q.y >= fminf(p.y, r.y)) return true; return false; } // To find orientation of ordered triplet (p, q, r). // The function returns following values // 0 --> p, q and r are collinear // 1 --> Clockwise // 2 --> Counterclockwise int orientation(Vector p, Vector q, Vector r) { // See https://www.geeksforgeeks.org/orientation-3-ordered-points/ // for details of below formula. int val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); if (val == 0) return 0; // collinear return (val > 0) ? 1 : 2; // clock or counterclock wise } bool vIntersects2D(const Vector LineAStart, const Vector LineAEnd, const Vector LineBStart, const Vector LineBEnd) { int o1 = orientation(LineAStart, LineAEnd, LineBStart); int o2 = orientation(LineAStart, LineAEnd, LineBEnd); int o3 = orientation(LineBStart, LineBEnd, LineAStart); int o4 = orientation(LineBStart, LineBEnd, LineAEnd); // General case if (o1 != o2 && o3 != o4) return true; // Special Cases // p1, q1 and p2 are collinear and p2 lies on segment p1q1 if (o1 == 0 && onSegment(LineAStart, LineBStart, LineAEnd)) return true; // p1, q1 and q2 are collinear and q2 lies on segment p1q1 if (o2 == 0 && onSegment(LineAStart, LineBEnd, LineAEnd)) return true; // p2, q2 and p1 are collinear and p1 lies on segment p2q2 if (o3 == 0 && onSegment(LineBStart, LineAStart, LineBEnd)) return true; // p2, q2 and q1 are collinear and q1 lies on segment p2q2 if (o4 == 0 && onSegment(LineBStart, LineAEnd, LineBEnd)) return true; return false; // Doesn't fall in any of the above cases } Vector vClosestPointOnBB(const Vector Point, const Vector MinBB, const Vector MaxBB) { return Vector(clampf(Point.x, MinBB.x, MaxBB.x), clampf(Point.y, MinBB.y, MaxBB.y), clampf(Point.z, MinBB.z, MaxBB.z)); } void vScaleBB(Vector& MinBB, Vector& MaxBB, const float Scale) { Vector Centre = MinBB + ((MaxBB - MinBB) * 0.5f); float SizeX = MaxBB.x - MinBB.x; float SizeY = MaxBB.y - MinBB.y; float SizeZ = MaxBB.z - MinBB.z; MinBB.x = Centre.x - (SizeX * Scale); MinBB.y = Centre.y - (SizeY * Scale); MinBB.z = Centre.z - (SizeZ * Scale); MaxBB.x = Centre.x + (SizeX * Scale); MaxBB.y = Centre.y + (SizeY * Scale); MaxBB.z = Centre.z + (SizeZ * Scale); } // Returns the 3D distance of point from a line defined between lineFrom and lineTo float vDistanceFromLine3D(const Vector lineFrom, const Vector lineTo, const Vector CheckPoint) { Vector nearestToLine = vClosestPointOnLine(lineFrom, lineTo, CheckPoint); return vDist3D(CheckPoint, nearestToLine); } // Returns the 2D distance (Z axis ignored) of point from a line defined between lineFrom and lineTo float vDistanceFromLine2D(const Vector lineFrom, const Vector lineTo, const Vector CheckPoint) { Vector nearestToLine = vClosestPointOnLine2D(lineFrom, lineTo, CheckPoint); return vDist2D(CheckPoint, nearestToLine); } // Returns the 2D distance (Z axis ignored) of point from a line defined between lineFrom and lineTo float vDistanceFromLine2DSq(const Vector lineFrom, const Vector lineTo, const Vector CheckPoint) { Vector nearestToLine = vClosestPointOnLine2D(lineFrom, lineTo, CheckPoint); return vDist2DSq(CheckPoint, nearestToLine); } Vector vClosestPointOnInfiniteLine3D(const Vector PointOnLine, const Vector NormalisedLineDir, const Vector TestPoint) { Vector DirectionToPoint = UTIL_GetVectorNormal(PointOnLine - TestPoint); float DirectionDot = UTIL_GetDotProduct(DirectionToPoint, NormalisedLineDir); return TestPoint + (NormalisedLineDir * DirectionDot); } Vector vClosestPointOnInfiniteLine2D(const Vector PointOnLine, const Vector NormalisedLineDir, const Vector TestPoint) { Vector NormalisedLineDir2D = UTIL_GetVectorNormal2D(NormalisedLineDir); Vector DirectionToPoint = UTIL_GetVectorNormal2D(PointOnLine - TestPoint); float DirectionDot = UTIL_GetDotProduct2D(DirectionToPoint, NormalisedLineDir2D); return TestPoint + (NormalisedLineDir * DirectionDot); } // Returns 0 if point sits right on the line defined by lineFrom and lineTo, -1 if it sits to the left, 1 if it sits to the right. Ignores Z axis int vPointOnLine(const Vector lineFrom, const Vector lineTo, const Vector point) { float value = ((lineTo.x - lineFrom.x) * (point.y - lineFrom.y)) - ((point.x - lineFrom.x) * (lineTo.y - lineFrom.y)); if (value > 1.0f) return 1; if (value < -1.0f) return -1; return 0; } // For given line lineFrom -> lineTo, returns the point along that line closest to point Vector vClosestPointOnLine(const Vector lineFrom, const Vector lineTo, const Vector point) { Vector vVector1 = point - lineFrom; Vector vVector2 = UTIL_GetVectorNormal(lineTo - lineFrom); float d = vDist3D(lineFrom, lineTo); float t = UTIL_GetDotProduct(vVector2, vVector1); if (t <= 0) return lineFrom; if (t >= d) return lineTo; Vector vVector3 = vVector2 * t; Vector vClosestPoint = lineFrom + vVector3; return vClosestPoint; } // For given line lineFrom -> lineTo, returns the 2D point (Z axis ignored) along that line closest to point Vector vClosestPointOnLine2D(const Vector lineFrom, const Vector lineTo, const Vector point) { Vector lineFrom2D = Vector(lineFrom.x, lineFrom.y, 0.0f); Vector lineTo2D = Vector(lineTo.x, lineTo.y, 0.0f); Vector point2D = Vector(point.x, point.y, 0.0f); Vector vVector1 = point2D - lineFrom2D; Vector vVector2 = UTIL_GetVectorNormal2D(lineTo2D - lineFrom2D); float d = vDist2D(lineFrom2D, lineTo2D); float t = UTIL_GetDotProduct2D(vVector2, vVector1); if (t <= 0) return lineFrom2D; if (t >= d) return lineTo2D; Vector vVector3 = vVector2 * t; Vector vClosestPoint = lineFrom2D + vVector3; return vClosestPoint; } // Normalizes the supplied vector, overwriting it with the normalized value in the process void UTIL_NormalizeVector(Vector* vec) { float len = sqrt((vec->x * vec->x) + (vec->y * vec->y) + (vec->z * vec->z)); float div = 1.0f / len; vec->x *= div; vec->y *= div; vec->z *= div; } void UTIL_NormalizeVector2D(Vector* vec) { float len = sqrt((vec->x * vec->x) + (vec->y * vec->y)); float div = 1.0f / len; vec->x *= div; vec->y *= div; vec->z = 0.0f; } // Returns a normalized copy of the supplied Vector. Original value is unmodified Vector UTIL_GetVectorNormal(const Vector vec) { return vec.Normalize(); } // Returns a 2D (Z axis is 0) normalized copy of the supplied Vector. Original value is unmodified Vector UTIL_GetVectorNormal2D(const Vector vec) { if (vec.x == 0.0f && vec.y == 0.0f) { return ZERO_VECTOR; } Vector result; float len = sqrt((vec.x * vec.x) + (vec.y * vec.y)); float div = 1.0f / len; result.x = vec.x * div; result.y = vec.y * div; result.z = 0.0f; return result; } // Returns the cross product of v1 and v2. Vector UTIL_GetCrossProduct(const Vector v1, const Vector v2) { Vector result; result.x = v1.y * v2.z - v1.z * v2.y; result.y = v1.z * v2.x - v1.x * v2.z; result.z = v1.x * v2.y - v1.y * v2.x; return result; } // Returns the 2D (ignoring Z axis) distance between the two vectors float vDist2D(const Vector v1, const Vector v2) { return (float)sqrt((v2.x - v1.x) * (v2.x - v1.x) + (v2.y - v1.y) * (v2.y - v1.y)); } // Returns the 3D distance between the two vectors float vDist3D(const Vector v1, const Vector v2) { return (float)sqrt((v2.x - v1.x) * (v2.x - v1.x) + (v2.y - v1.y) * (v2.y - v1.y) + (v2.z - v1.z) * (v2.z - v1.z)); } // Returns the 2D (ignoring Z axis) squared distance between the two vectors float vDist2DSq(const Vector v1, const Vector v2) { return ((v2.x - v1.x) * (v2.x - v1.x) + (v2.y - v1.y) * (v2.y - v1.y)); } // Returns the 3D squared distance between the two vectors float vDist3DSq(const Vector v1, const Vector v2) { return ((v2.x - v1.x) * (v2.x - v1.x) + (v2.y - v1.y) * (v2.y - v1.y) + (v2.z - v1.z) * (v2.z - v1.z)); } float vSize3DSq(const Vector V) { return (V.x * V.x) + (V.y * V.y) + (V.z * V.z); } float vSize2DSq(const Vector V) { return (V.x * V.x) + (V.y * V.y); } float vSize3D(const Vector V) { return sqrtf((V.x * V.x) + (V.y * V.y) + (V.z * V.z)); } float vSize2D(const Vector V) { return sqrtf((V.x * V.x) + (V.y * V.y)); } // Returns true if the two vectors are the same (all components are within 0.0001f of each other) bool vEquals(const Vector v1, const Vector v2) { return fabsf(v1.x - v2.x) <= 0.0001f && fabsf(v1.y - v2.y) <= 0.0001f && fabsf(v1.z - v2.z) <= 0.0001f; } bool vEquals2D(const Vector v1, const Vector v2) { return fabsf(v1.x - v2.x) <= 0.01f && fabsf(v1.y - v2.y) <= 0.01f; } // Returns true if the two vectors are the same (all components are within epsilon of each other) bool vEquals(const Vector v1, const Vector v2, const float epsilon) { return fabsf(v1.x - v2.x) <= epsilon && fabsf(v1.y - v2.y) <= epsilon && fabsf(v1.z - v2.z) <= epsilon; } bool vEquals2D(const Vector v1, const Vector v2, const float epsilon) { return fabsf(v1.x - v2.x) <= epsilon && fabsf(v1.y - v2.y) <= epsilon; } bool vIsZero(const Vector v1) { return (fabsf(v1.x) < 0.0001f && fabsf(v1.y) < 0.0001f && fabsf(v1.z) < 0.0001f); } bool fNearlyEqual(const float f1, const float f2) { return fabsf(f1 - f2) < 0.001f; } // Returns the dot product of two vectors (1.0f if both vectors pointing exactly the same direction, -1.0f if opposites, 0.0f if perpendicular) //float UTIL_GetDotProduct(const Vector v1, const Vector v2) //{ // return ((v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z)); //} // Returns the 2D dot product (Z axis ignored) of two vectors (1.0f if both vectors pointing exactly the same direction, -1.0f if opposites, 0.0f if perpendicular) float UTIL_GetDotProduct2D(const Vector v1, const Vector v2) { return ((v1.x * v2.x) + (v1.y * v2.y)); } // Returns a random point along the circumference of a circle defined with the supplied origin and radius Vector UTIL_RandomPointOnCircle(const Vector origin, const float radius) { Vector result; float random = ((float)rand()) / (float)RAND_MAX; float a = random * (MATH_PI * 2); result.x = origin.x + (radius * cos(a)); result.y = origin.y + (radius * sin(a)); result.z = origin.z; return result; } // For given plane, determine if the given point sits within the plane or not bool UTIL_PointInsidePlane(const frustum_plane_t* plane, const Vector point) { float distance = plane->d + (plane->normal.x * point.x + plane->normal.y * point.y + plane->normal.z * point.z); return distance >= 0.0f; } bool UTIL_CylinderInsidePlane(const frustum_plane_t* plane, const Vector centre, float height, float radius) { Vector testNormal = plane->normal; testNormal.z = 0; Vector topPoint = centre + Vector(0.0f, 0.0f, height * 0.5f) + (testNormal * radius); Vector bottomPoint = centre - Vector(0.0f, 0.0f, height * 0.5f) + (testNormal * radius); return (UTIL_PointInsidePlane(plane, topPoint) || UTIL_PointInsidePlane(plane, bottomPoint)); } void UTIL_SetFrustumPlane(frustum_plane_t* plane, Vector v1, Vector v2, Vector v3) { Vector normal = UTIL_GetSurfaceNormal(v1, v2, v3); plane->normal.x = normal.x; plane->normal.y = normal.y; plane->normal.z = normal.z; plane->point.x = v2.x; plane->point.y = v2.y; plane->point.z = v2.z; plane->d = -(plane->normal.x * plane->point.x + plane->normal.y * plane->point.y + plane->normal.z * plane->point.z); } float UTIL_MetresToGoldSrcUnits(const float Metres) { return Metres * 52.4934f; } float UTIL_GoldSrcUnitsToMetres(const float GoldSrcUnits) { return GoldSrcUnits * 0.01905f; } float sqrf(float input) { return (input * input); } int imaxi(const int a, const int b) { return (a > b) ? a : b; } int imini(const int a, const int b) { return (a < b) ? a : b; } float clampf(float input, float inMin, float inMax) { return fmaxf(fminf(input, inMax), inMin); } float clampi(int input, int inMin, int inMax) { return imaxi(imini(input, inMax), inMin); } Vector GetPitchForProjectile(Vector LaunchPoint, Vector TargetPoint, const float ProjectileSpeed, const float Gravity) { double start_x = LaunchPoint.x; double start_y = LaunchPoint.y; double start_z = LaunchPoint.z; double target_x = TargetPoint.x; double target_y = TargetPoint.y; double target_z = TargetPoint.z; double x = target_x - start_x; double y = target_y - start_y; double z = target_z - start_z; double range = sqrt(x * x + y * y); double discriminant = pow(ProjectileSpeed, 4) - Gravity * (Gravity * pow(range, 2) + 2 * z * pow(ProjectileSpeed, 2)); if (discriminant < 0) { return ZERO_VECTOR; } double launch_angle = atan((pow(ProjectileSpeed, 2) - sqrt(discriminant)) / (Gravity * range)); // Calculate the components of the unit vector double unit_vector_x = cos(launch_angle) * cos(atan2(y, x)); double unit_vector_y = cos(launch_angle) * sin(atan2(y, x)); double unit_vector_z = sin(launch_angle); Vector LaunchVector = Vector(unit_vector_x, unit_vector_y, unit_vector_z); return LaunchVector; } void UTIL_AnglesToVector(const Vector angles, Vector* fwd, Vector* right, Vector* up) { g_engfuncs.pfnAngleVectors(angles, (float*)fwd, (float*)right, (float*)up); } float UTIL_WrapAngle(float angle) { // check for wraparound of angle if (angle > 180) angle -= 360; else if (angle < -180) angle += 360; return (angle); } Vector UTIL_WrapAngles(Vector angles) { // check for wraparound of angles if (angles.x > 180) angles.x -= 360; else if (angles.x < -180) angles.x += 360; if (angles.y > 180) angles.y -= 360; else if (angles.y < -180) angles.y += 360; if (angles.z > 180) angles.z -= 360; else if (angles.z < -180) angles.z += 360; return (angles); } float signf(float input) { return (input == 0.0f) ? 0.0f : ((input > 0.0f) ? 1.0f : -1.0f); } Vector ViewInterpTo(const Vector CurrentViewAngles, const Vector& TargetDirection, const float DeltaTime, const float InterpSpeed) { if (DeltaTime == 0.f) { return CurrentViewAngles; } // If no interp speed, jump to target value if (InterpSpeed <= 0.f) { return UTIL_VecToAngles(TargetDirection); } Vector TargetAngles = UTIL_VecToAngles(TargetDirection); const float DeltaInterpSpeed = InterpSpeed * DeltaTime; const Vector Delta = UTIL_GetVectorNormal(TargetAngles - CurrentViewAngles); // If steps are too small, just return Target and assume we have reached our destination. if (vEquals(CurrentViewAngles, TargetAngles)) { return TargetAngles; } // Delta Move, Clamp so we do not over shoot. const Vector DeltaMove = Delta * clampf(DeltaInterpSpeed, 0.f, 1.f); Vector NewAngle = (CurrentViewAngles + DeltaMove); if (NewAngle.y > 180) NewAngle.y -= 360; // Paulo-La-Frite - START bot aiming bug fix if (NewAngle.x > 180) NewAngle.x -= 360; NewAngle.z = 0.0f; return NewAngle; } float fInterpTo(float start, float end, float DeltaTime, float InterpSpeed) { // If no interp speed, jump to target value if (InterpSpeed <= 0.f) { return end; } // Distance to reach float Dist = end - start; // If distance is too small, just set the desired location if (fabsf(sqrf(Dist)) < 0.0001f) { return end; } // Delta Move, Clamp so we do not over shoot. float DeltaMove = Dist * clampf((DeltaTime * InterpSpeed), 0.f, 1.f); float NewVal = start + DeltaMove; return NewVal; } float fInterpConstantTo(float start, float end, float DeltaTime, float InterpSpeed) { float Dist = end - start; // If distance is too small, just set the desired location if (fabsf(sqrf(Dist)) < 0.0001f) { return end; } const float Step = InterpSpeed * DeltaTime; float NewVal = start + clampf(Dist, -Step, Step); return NewVal; } float frandrange(float MinValue, float MaxValue) { return ((float(rand()) / float(RAND_MAX)) * (MaxValue - MinValue)) + MinValue; } int irandrange(int MinValue, int MaxValue) { if (MinValue == MaxValue) { return MinValue; } return MinValue + rand() / (RAND_MAX / (MaxValue - MinValue + 1) + 1); } bool randbool() { return (rand() & 1); } Vector random_unit_vector_within_cone(const Vector Direction, double cone_half_angle) { Vector Result = ZERO_VECTOR; double u = ((double)rand() / RAND_MAX) * 2 - 1; // random number in [-1, 1] double phi = ((double)rand() / RAND_MAX) * 2 * M_PI; // random number in [0, 2*pi] double r = sqrt(1 - u * u); double x = r * cos(phi); double y = r * sin(phi); double z = u; // Rotate the random point to the cone direction using a quaternion. // First, compute the quaternion that rotates the z-axis to the cone direction. double cos_half_angle = cos(cone_half_angle / 2); double sin_half_angle = sin(cone_half_angle / 2); double qx = Direction.x * sin_half_angle; double qy = Direction.y * sin_half_angle; double qz = Direction.z * sin_half_angle; double qw = cos_half_angle; // Then, apply the quaternion rotation to the random point. double t = qw * z - qx * x - qy * y - qz * z; Result.x = qx * z + qw * x - qz * y + qy * t; Result.y = qy * z + qz * x + qw * y - qx * t; Result.z = qz * z - qy * x + qx * y + qw * t; return UTIL_GetVectorNormal(Result); } Vector UTIL_GetRandomUnitVectorInCone(const Vector ConeDirection, const float HalfAngleRadians) { Vector P = UTIL_GetVectorNormal(UTIL_GetCrossProduct(ConeDirection, UP_VECTOR)); Vector Q = UTIL_GetVectorNormal(UTIL_GetCrossProduct(ConeDirection, P)); float RMax = tanf(HalfAngleRadians); float Theta = frandrange(0.0f, 2.0f) * MATH_PI; float u = frandrange(cos(HalfAngleRadians), 1.0f); float r = RMax * sqrt(1.0f - sqrf(u)); return UTIL_GetVectorNormal((r * (P * cos(Theta) + Q * sin(Theta)))); } float fDegreesToRadians(const float Degrees) { return Degrees * DEGREES_RADIANS_CONV; } Vector UTIL_GetForwardVector(const Vector angles) { Vector fwd, right, up; UTIL_AnglesToVector(angles, &fwd, &right, &up); return fwd; } Vector UTIL_GetForwardVector2D(const Vector angles) { Vector fwd, right, up; UTIL_AnglesToVector(angles, &fwd, &right, &up); return UTIL_GetVectorNormal2D(fwd); } float UTIL_GetDistanceToPolygon2DSq(const Vector TestPoint, const Vector* Points, const int NumPoints) { float minDist = -1.0f; for (int i = 0; i < NumPoints; i++) { Vector destPoint = (i <= (NumPoints - 2)) ? Points[i + 1] : Points[0]; float thisDist = vDistanceFromLine2DSq(Points[i], destPoint, TestPoint); if (minDist < 0.0f) { minDist = thisDist; } else { minDist = fminf(minDist, thisDist); } } return minDist; } Vector UTIL_GetAimLocationToLeadTarget(const Vector ShooterLocation, const Vector TargetLocation, const Vector TargetVelocity, const float ProjectileVelocity) { // We interpret a speed of 0.0f to mean hitscan, i.e. infinitely fast if (ProjectileVelocity == 0.0f) { return TargetLocation; } Vector totarget = TargetLocation - ShooterLocation; float a = UTIL_GetDotProduct(TargetVelocity, TargetVelocity) - (ProjectileVelocity * ProjectileVelocity); float b = 2.0f * UTIL_GetDotProduct(TargetVelocity, totarget); float c = UTIL_GetDotProduct(totarget, totarget); float p = -b / (2.0f * a); float q = (float)sqrt((b * b) - 4.0f * a * c) / (2.0f * a); float t1 = p - q; float t2 = p + q; float t; if (t1 > t2 && t2 > 0) { t = t2; } else { t = t1; } return (TargetLocation + TargetVelocity * t); } float UTIL_GetVelocityRequiredToReachTarget(const Vector StartLocation, const Vector TargetLocation, float Gravity) { // Calculate the distance between the start and target positions double distance = vDist3D(StartLocation, TargetLocation); // Calculate the initial velocity required to reach the target with no air resistance double velocity = sqrt(2.0f * Gravity * distance); return velocity; } Vector UTIL_GetRandomPointInBoundingBox(const Vector BoxMin, const Vector BoxMax) { float RandX = frandrange(BoxMin.x, BoxMax.x); float RandY = frandrange(BoxMin.y, BoxMax.y); float RandZ = frandrange(BoxMin.z, BoxMax.z); return Vector(RandX, RandY, RandZ); } /* Function to get no of set bits in binary representation of positive integer n */ unsigned int UTIL_CountSetBitsInInteger(unsigned int n) { unsigned int count = 0; while (n) { count += n & 1; n >>= 1; } return count; } float UTIL_CalculateSlopeAngleBetweenPoints(const Vector StartPoint, const Vector EndPoint) { float Run = vDist2DSq(StartPoint, EndPoint); float Rise = fabsf(StartPoint.z - EndPoint.z); return atanf(Rise / Run); } // Function to check if a finite line intersects with an AABB bool vlineIntersectsAABB(Vector lineStart, Vector lineEnd, Vector BoxMinPosition, Vector BoxMaxPosition) { if (vPointOverlaps3D(lineStart, BoxMinPosition, BoxMaxPosition) || vPointOverlaps3D(lineEnd, BoxMinPosition, BoxMaxPosition)) { return true; } Vector RayDir = UTIL_GetVectorNormal(lineEnd - lineStart); float LineLength = vDist3D(lineStart, lineEnd); Vector dirfrac; float t = FLT_MAX; // r.dir is unit direction vector of ray dirfrac.x = 1.0f / RayDir.x; dirfrac.y = 1.0f / RayDir.y; dirfrac.z = 1.0f / RayDir.z; // lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner // r.org is origin of ray float t1 = (BoxMinPosition.x - lineStart.x) * dirfrac.x; float t2 = (BoxMaxPosition.x - lineStart.x) * dirfrac.x; float t3 = (BoxMinPosition.y - lineStart.y) * dirfrac.y; float t4 = (BoxMaxPosition.y - lineStart.y) * dirfrac.y; float t5 = (BoxMinPosition.z - lineStart.z) * dirfrac.z; float t6 = (BoxMaxPosition.z - lineStart.z) * dirfrac.z; float tmin = max(max(min(t1, t2), min(t3, t4)), min(t5, t6)); float tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6)); // if tmax < 0, ray (line) is intersecting AABB, but the whole AABB is behind us if (tmax < 0) { t = tmax; return false; } // if tmin > tmax, ray doesn't intersect AABB if (tmin > tmax) { t = tmax; return false; } t = tmin; return t <= LineLength; }