#include "base.h" //#define _USE_MATH_DEFINES #include #include #include #include "utils.h" #include "colors.h" #pragma region OpenGLDrawFunctionality void utils::SetColor(const Color4f& color) { glColor4f(color.r, color.g, color.b, color.a); } void utils::ClearBackground(const Color4f& color) { glClearColor(color.r, color.g, color.b, color.a); glClear(GL_COLOR_BUFFER_BIT); } void utils::DrawPoint(float x, float y, float pointSize) { glPointSize(pointSize); glBegin(GL_POINTS); { glVertex2f(x, y); } glEnd(); } void utils::DrawPoint(const Vector2f& p, float pointSize) { DrawPoint(p.x, p.y, pointSize); } void utils::DrawPoints(Vector2f* pVertices, int nrVertices, float pointSize) { glPointSize(pointSize); glBegin(GL_POINTS); { for (int idx { 0 }; idx < nrVertices; ++idx) { glVertex2f(pVertices[idx].x, pVertices[idx].y); } } glEnd(); } void utils::DrawLine(float x1, float y1, float x2, float y2, float lineWidth) { glLineWidth(lineWidth); glBegin(GL_LINES); { glVertex2f(x1, y1); glVertex2f(x2, y2); } glEnd(); } void utils::DrawLine(const Vector2f& p1, const Vector2f& p2, float lineWidth) { DrawLine(p1.x, p1.y, p2.x, p2.y, lineWidth); } void utils::DrawTriangle(const Vector2f& p1, const Vector2f& p2, const Vector2f& p3, float lineWidth) { glLineWidth(lineWidth); glBegin(GL_LINE_LOOP); { glVertex2f(p1.x, p1.y); glVertex2f(p2.x, p2.y); glVertex2f(p3.x, p3.y); } glEnd(); } void utils::FillTriangle(const Vector2f& p1, const Vector2f& p2, const Vector2f& p3) { glBegin(GL_TRIANGLES); { glVertex2f(p1.x, p1.y); glVertex2f(p2.x, p2.y); glVertex2f(p3.x, p3.y); } glEnd(); } void utils::DrawRect(float left, float bottom, float width, float height, float lineWidth) { if (width > 0 && height > 0 && lineWidth > 0) { glLineWidth(lineWidth); glBegin(GL_LINE_LOOP); { glVertex2f(left, bottom); glVertex2f(left + width, bottom); glVertex2f(left + width, bottom + height); glVertex2f(left, bottom + height); } glEnd(); } } void utils::DrawRect(const Vector2f& bottomLeft, float width, float height, float lineWidth) { DrawRect(bottomLeft.x, bottomLeft.y, width, height, lineWidth); } void utils::DrawRect(const Rectf& rect, float lineWidth) { DrawRect(rect.left, rect.bottom, rect.width, rect.height, lineWidth); } void utils::FillRect(float left, float bottom, float width, float height) { if (width > 0 && height > 0) { glBegin(GL_POLYGON); { glVertex2f(left, bottom); glVertex2f(left + width, bottom); glVertex2f(left + width, bottom + height); glVertex2f(left, bottom + height); } glEnd(); } } void utils::FillRect(const Vector2f& bottomLeft, float width, float height) { FillRect(bottomLeft.x, bottomLeft.y, width, height); } void utils::FillRect(const Rectf& rect) { FillRect(rect.left, rect.bottom, rect.width, rect.height); } void utils::DrawEllipse(float centerX, float centerY, float radX, float radY, float lineWidth) { if (radX > 0 && radY > 0 && lineWidth > 0) { float dAngle { radX > radY ? float(g_Pi / radX) : float(g_Pi / radY) }; glLineWidth(lineWidth); glBegin(GL_LINE_LOOP); { for (float angle = 0.0; angle < float(2 * g_Pi); angle += dAngle) { glVertex2f(centerX + radX * cos(angle), centerY + radY * sin(angle)); } } glEnd(); } } void utils::DrawEllipse(const Vector2f& center, float radX, float radY, float lineWidth) { DrawEllipse(center.x, center.y, radX, radY, lineWidth); } void utils::DrawEllipse(const Ellipsef& ellipse, float lineWidth) { DrawEllipse(ellipse.center.x, ellipse.center.y, ellipse.radiusX, ellipse.radiusY, lineWidth); } void utils::FillEllipse(float centerX, float centerY, float radX, float radY) { if (radX > 0 && radY > 0) { float dAngle { radX > radY ? float(g_Pi / radX) : float(g_Pi / radY) }; glBegin(GL_POLYGON); { for (float angle = 0.0; angle < float(2 * g_Pi); angle += dAngle) { glVertex2f(centerX + radX * cos(angle), centerY + radY * sin(angle)); } } glEnd(); } } void utils::FillEllipse(const Ellipsef& ellipse) { FillEllipse(ellipse.center.x, ellipse.center.y, ellipse.radiusX, ellipse.radiusY); } void utils::FillEllipse(const Vector2f& center, float radX, float radY) { FillEllipse(center.x, center.y, radX, radY); } void utils::DrawArc(float centerX, float centerY, float radX, float radY, float fromAngle, float tillAngle, float lineWidth) { if (fromAngle > tillAngle) { return; } float dAngle { radX > radY ? float(g_Pi / radX) : float(g_Pi / radY) }; glLineWidth(lineWidth); glBegin(GL_LINE_STRIP); { for (float angle = fromAngle; angle < tillAngle; angle += dAngle) { glVertex2f(centerX + radX * cos(angle), centerY + radY * sin(angle)); } glVertex2f(centerX + radX * cos(tillAngle), centerY + radY * sin(tillAngle)); } glEnd(); } void utils::DrawArc(const Vector2f& center, float radX, float radY, float fromAngle, float tillAngle, float lineWidth) { DrawArc(center.x, center.y, radX, radY, fromAngle, tillAngle, lineWidth); } void utils::FillArc(float centerX, float centerY, float radX, float radY, float fromAngle, float tillAngle) { if (fromAngle > tillAngle) { return; } float dAngle { radX > radY ? float(g_Pi / radX) : float(g_Pi / radY) }; glBegin(GL_POLYGON); { glVertex2f(centerX, centerY); for (float angle = fromAngle; angle < tillAngle; angle += dAngle) { glVertex2f(centerX + radX * cos(angle), centerY + radY * sin(angle)); } glVertex2f(centerX + radX * cos(tillAngle), centerY + radY * sin(tillAngle)); } glEnd(); } void utils::FillArc(const Vector2f& center, float radX, float radY, float fromAngle, float tillAngle) { FillArc(center.x, center.y, radX, radY, fromAngle, tillAngle); } void utils::DrawPolygon(const std::vector& vertices, bool closed, float lineWidth) { DrawPolygon(vertices.data(), vertices.size(), closed, lineWidth); } void utils::DrawPolygon(const Vector2f* pVertices, size_t nrVertices, bool closed, float lineWidth) { glLineWidth(lineWidth); closed ? glBegin(GL_LINE_LOOP) : glBegin(GL_LINE_STRIP); { for (size_t idx { 0 }; idx < nrVertices; ++idx) { glVertex2f(pVertices[idx].x, pVertices[idx].y); } } glEnd(); } void utils::FillPolygon(const std::vector& vertices) { FillPolygon(vertices.data(), vertices.size()); } void utils::DrawArrow(const Vector2f& start, const Vector2f& end, float lineWidth, float arrowSize) { // Origin is bottom left utils::DrawLine(start, end); const float arrowAngle = atan2f(end.y - start.y, end.x - start.x); const float arrowAngle1 = arrowAngle + g_Pi + 0.4f; const float arrowAngle2 = arrowAngle + g_Pi - 0.4f; const Vector2f arrow1 { end.x + arrowSize * cosf(arrowAngle1), end.y + arrowSize * sinf(arrowAngle1) }; const Vector2f arrow2 { end.x + arrowSize * cosf(arrowAngle2), end.y + arrowSize * sinf(arrowAngle2) }; utils::DrawLine(end, arrow1); utils::DrawLine(end, arrow2); } void utils::FillPolygon(const Vector2f* pVertices, size_t nrVertices) { glBegin(GL_POLYGON); { for (size_t idx { 0 }; idx < nrVertices; ++idx) { glVertex2f(pVertices[idx].x, pVertices[idx].y); } } glEnd(); } #pragma endregion OpenGLDrawFunctionality #pragma region CollisionFunctionality float utils::GetDistance(float x1, float y1, float x2, float y2) { return ( sqrtf(( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 )) ); } float utils::GetDistance(const Vector2f& p1, const Vector2f& p2) { return GetDistance(p1.x, p1.y, p2.x, p2.y); } bool utils::IsPointInRect(const Vector2f& p, const Rectf& r) { return ( p.x >= r.left && p.x <= r.left + r.width && p.y >= r.bottom && p.y <= r.bottom + r.height ); } bool utils::IsPointInCircle(const Vector2f& p, const Circlef& c) { float squaredDist { ( p.x - c.center.x ) * ( p.x - c.center.x ) + ( p.y - c.center.y ) * ( p.y - c.center.y ) }; float squaredRadius { c.radius * c.radius }; return ( squaredRadius >= squaredDist ); } bool utils::IsOverlapping(const Vector2f& a, const Vector2f& b, const Rectf& r) { // if one of the line segment end points is in the rect if (utils::IsPointInRect(a, r) || utils::IsPointInRect(b, r)) { return true; } HitInfo hitInfo {}; Vector2f vertices[] { Vector2f { r.left, r.bottom }, Vector2f { r.left + r.width, r.bottom }, Vector2f { r.left + r.width, r.bottom + r.height }, Vector2f { r.left, r.bottom + r.height } }; return Raycast(vertices, 4, a, b, hitInfo); } bool utils::IsOverlapping(const Rectf& r1, const Rectf& r2) { // If one rectangle is on left side of the other if (( r1.left + r1.width ) < r2.left || ( r2.left + r2.width ) < r1.left) { return false; } // If one rectangle is under the other if (r1.bottom > ( r2.bottom + r2.height ) || r2.bottom > ( r1.bottom + r1.height )) { return false; } return true; } bool utils::IsOverlapping(const Rectf& r, const Circlef& c) { // Is center of circle in the rectangle? if (IsPointInRect(c.center, r)) { return true; } // Check line segments if (utils::DistPointLineSegment(c.center, Vector2f { r.left, r.bottom }, Vector2f { r.left, r.bottom + r.height }) <= c.radius) { return true; } if (utils::DistPointLineSegment(c.center, Vector2f { r.left, r.bottom }, Vector2f { r.left + r.width, r.bottom }) <= c.radius) { return true; } if (utils::DistPointLineSegment(c.center, Vector2f { r.left + r.width, r.bottom + r.height }, Vector2f { r.left, r.bottom + r.height }) <= c.radius) { return true; } if (utils::DistPointLineSegment(c.center, Vector2f { r.left + r.width, r.bottom + r.height }, Vector2f { r.left + r.width, r.bottom }) <= c.radius) { return true; } return false; } bool utils::IsOverlapping(const Circlef& c1, const Circlef& c2) { // squared distance between centers float xDistance { c1.center.x - c2.center.x }; float yDistance { c1.center.y - c2.center.y }; float squaredDistance { xDistance * xDistance + yDistance * yDistance }; float squaredTouchingDistance { ( c1.radius + c2.radius ) * ( c1.radius + c2.radius ) }; return ( squaredDistance < squaredTouchingDistance ); } bool utils::IsOverlapping(const Vector2f& a, const Vector2f& b, const Circlef& c) { return utils::DistPointLineSegment(c.center, a, b) <= c.radius; } bool utils::IsOverlapping(const std::vector& vertices, const Circlef& c) { return IsOverlapping(vertices.data(), vertices.size(), c); } bool utils::IsOverlapping(const Vector2f* vertices, size_t nrVertices, const Circlef& c) { // Verify whether one of vertices is in circle for (size_t i { 0 }; i < nrVertices; ++i) { if (IsPointInCircle(vertices[i], c)) { return true; } } // Verify whether one of the polygon edges overlaps with circle for (size_t i { 0 }; i < nrVertices; ++i) { if (DistPointLineSegment(c.center, vertices[i], vertices[( i + 1 ) % nrVertices]) <= c.radius) { return true; } } // No overlapping with edges, verify whether circle is completely inside the polygon if (IsPointInPolygon(c.center, vertices, nrVertices)) { return true; } return false; } bool utils::IsPointInPolygon(const Vector2f& p, const std::vector& vertices) { return IsPointInPolygon(p, vertices.data(), vertices.size()); } bool utils::IsPointInPolygon(const Vector2f& p, const Vector2f* vertices, size_t nrVertices) { if (nrVertices < 2) { return false; } // 1. First do a simple test with axis aligned bounding box around the polygon float xMin { vertices[0].x }; float xMax { vertices[0].x }; float yMin { vertices[0].y }; float yMax { vertices[0].y }; for (size_t idx { 1 }; idx < nrVertices; ++idx) { if (xMin > vertices[idx].x) { xMin = vertices[idx].x; } if (xMax < vertices[idx].x) { xMax = vertices[idx].x; } if (yMin > vertices[idx].y) { yMin = vertices[idx].y; } if (yMax < vertices[idx].y) { yMax = vertices[idx].y; } } if (p.x < xMin || p.x > xMax || p.y < yMin || p.y > yMax) { return false; } // 2. Draw a virtual ray from anywhere outside the polygon to the point // and count how often it hits any side of the polygon. // If the number of hits is even, it's outside of the polygon, if it's odd, it's inside. int numberOfIntersectionPoints { 0 }; Vector2f p2 { xMax + 10.0f, p.y }; // Horizontal line from point to point outside polygon (p2) // Count the number of intersection points float lambda1 {}, lambda2 {}; for (size_t i { 0 }; i < nrVertices; ++i) { if (IntersectLineSegments(vertices[i], vertices[( i + 1 ) % nrVertices], p, p2, lambda1, lambda2)) { if (lambda1 > 0 && lambda1 <= 1 && lambda2 > 0 && lambda2 <= 1) { ++numberOfIntersectionPoints; } } } if (numberOfIntersectionPoints % 2 == 0) { return false; } else { return true; } } bool utils::IntersectLineSegments(const Vector2f& p1, const Vector2f& p2, const Vector2f& q1, const Vector2f& q2, float& outLambda1, float& outLambda2, float epsilon) { bool intersecting { false }; Vector2f p1p2 { p1, p2 }; Vector2f q1q2 { q1, q2 }; // Cross product to determine if parallel float denom = p1p2.CrossProduct(q1q2); // Don't divide by zero if (std::abs(denom) > epsilon) { intersecting = true; Vector2f p1q1 { p1, q1 }; float num1 = p1q1.CrossProduct(q1q2); float num2 = p1q1.CrossProduct(p1p2); outLambda1 = num1 / denom; outLambda2 = num2 / denom; } else // are parallel { // Connect start points Vector2f p1q1 { p1, q1 }; // Cross product to determine if segments and the line connecting their start points are parallel, // if so, than they are on a line // if not, then there is no intersection if (std::abs(p1q1.CrossProduct(q1q2)) > epsilon) { return false; } // Check the 4 conditions outLambda1 = 0; outLambda2 = 0; if (utils::IsPointOnLineSegment(p1, q1, q2) || utils::IsPointOnLineSegment(p2, q1, q2) || utils::IsPointOnLineSegment(q1, p1, p2) || utils::IsPointOnLineSegment(q2, p1, p2)) { intersecting = true; } } return intersecting; } bool utils::Raycast(const std::vector& vertices, const Vector2f& rayP1, const Vector2f& rayP2, HitInfo& hitInfo) { return Raycast(vertices.data(), vertices.size(), rayP1, rayP2, hitInfo); } bool utils::Raycast(const Vector2f* vertices, const size_t nrVertices, const Vector2f& rayP1, const Vector2f& rayP2, HitInfo& hitInfo) { if (nrVertices == 0) { return false; } std::vector hits; Rectf r1, r2; // r1: minimal AABB rect enclosing the ray r1.left = std::min(rayP1.x, rayP2.x); r1.bottom = std::min(rayP1.y, rayP2.y); r1.width = std::max(rayP1.x, rayP2.x) - r1.left; r1.height = std::max(rayP1.y, rayP2.y) - r1.bottom; // Line-line intersections. for (size_t idx { 0 }; idx <= nrVertices; ++idx) { // Consider line segment between 2 consecutive vertices // (modulo to allow closed polygon, last - first vertice) Vector2f q1 = vertices[( idx + 0 ) % nrVertices]; Vector2f q2 = vertices[( idx + 1 ) % nrVertices]; // r2: minimal AABB rect enclosing the 2 vertices r2.left = std::min(q1.x, q2.x); r2.bottom = std::min(q1.y, q2.y); r2.width = std::max(q1.x, q2.x) - r2.left; r2.height = std::max(q1.y, q2.y) - r2.bottom; if (IsOverlapping(r1, r2)) { float lambda1 {}; float lambda2 {}; if (IntersectLineSegments(rayP1, rayP2, q1, q2, lambda1, lambda2)) { if (lambda1 > 0 && lambda1 <= 1 && lambda2 > 0 && lambda2 <= 1) { HitInfo linesHitInfo {}; linesHitInfo.lambda = lambda1; linesHitInfo.intersectPoint = Vector2f { rayP1.x + ( ( rayP2.x - rayP1.x ) * lambda1 ), rayP1.y + ( ( rayP2.y - rayP1.y ) * lambda1 ) }; linesHitInfo.normal = Vector2f { q2.x - q1.x, q2.y - q2.y }.Orthogonal().Normalized(); hits.push_back(linesHitInfo); } } } } if (hits.size() == 0) { return false; } // Get closest intersection point and copy it into the hitInfo parameter hitInfo = *std::min_element ( hits.begin(), hits.end(), [](const HitInfo& first, const HitInfo& last) { return first.lambda < last.lambda; } ); return true; } bool utils::IsPointOnLineSegment(const Vector2f& p, const Vector2f& a, const Vector2f& b) { Vector2f ap { a, p }, bp { b, p }; // If not on same line, return false if (abs(ap.CrossProduct(bp)) > 0.001f) { return false; } // Both vectors must point in opposite directions if p is between a and b if (ap.DotProduct(bp) > 0) { return false; } return true; } float utils::DistPointLineSegment(const Vector2f& p, const Vector2f& a, const Vector2f& b) { Vector2f ab { a, b }; Vector2f ap { a, p }; Vector2f abNorm { ab.Normalized() }; float distToA { abNorm.DotProduct(ap) }; // If distToA is negative, then the closest point is A // return the distance a, p if (distToA < 0) { return ap.Length(); } // If distToA is > than dist(a,b) then the closest point is B // return the distance b, p float distAB { ab.Length() }; if (distToA > distAB) { return Vector2f { b, p }.Length(); } // Closest point is between A and B, calc intersection point Vector2f intersection { abNorm.DotProduct(ap) * abNorm + Vector2f { a } }; return Vector2f { p.x - intersection.x, p.y - intersection.y }.Length(); } bool utils::IntersectRectLine(const Rectf& r, const Vector2f& p1, const Vector2f& p2, float& intersectMin, float& intersectMax) { // Parameters // input: // r: axis aligned bounding box, start and end points of line segment. // p1, p2: line // output: // intersectMin and intersectMax: in the interval [0,1] if intersection point is on the line segment. // return // true if there is an intersection // Example of how to use //float min{}; //float max{}; //if (utils::IntersectRectLine(rect, p1, p2, min, max)) //{ // Vector2f intersectP1{ p1 + (Vector2f(p2) - Vector2f(p1)) * min }; // Vector2f intersectP2{ p1 + (Vector2f(p2) - Vector2f(p1)) * max }; //} // 4 floats to convert rect space to line space // x1: value between 0 and 1 where 0 is on p1 and 1 is on p2, <0 and >1 means intersection is not on line segment float x1 { ( r.left - p1.x ) / ( p2.x - p1.x ) }; float x2 { ( r.left + r.width - p1.x ) / ( p2.x - p1.x ) }; float y1 { ( r.bottom - p1.y ) / ( p2.y - p1.y ) }; float y2 { ( r.bottom + r.height - p1.y ) / ( p2.y - p1.y ) }; using std::max; using std::min; float tMin { max(min(x1, x2), min(y1, y2)) }; float tMax { min(max(x1, x2), max(y1, y2)) }; if (tMin > tMax) { return false; } intersectMin = tMin; intersectMax = tMax; return true; } bool utils::IsRectInRect(const Rectf& r1, const Rectf& r2) { // the origin of both rectangles is in bottom left return ( r1.left < r2.left + r2.width && r1.left + r1.width > r2.left && r1.bottom < r2.bottom + r2.height && r1.bottom + r1.height > r2.bottom ); } bool utils::RayVsRect(const Vector2f& rayOrigin, const Vector2f& rayDir, const Rectf& target, Vector2f& contactPoint, Vector2f& contactNormal, float& t_hit_near) { // Vector2f t_near = Vector2f{(target.BottomLeft() - rayOrigin).x / rayDir.x, (target.BottomLeft() - rayOrigin).y / rayDir.y}; // Vector2f t_far = Vector2f{(target.BottomLeft() + Vector2f{target.width, target.height} - rayOrigin).x / rayDir.x, (target.BottomLeft() + Vector2f{target.width, target.height} - rayOrigin).y / rayDir.y}; Vector2f t_near {}; Vector2f t_far {}; if (std::isnan(t_far.y) || std::isnan(t_far.x)) return false; if (std::isnan(t_near.y) || std::isnan(t_near.x)) return false; if (t_near.x > t_far.x) std::swap(t_near.x, t_far.x); if (t_near.y > t_far.y) std::swap(t_near.y, t_far.y); if (t_near.x > t_far.y || t_near.y > t_far.x) return false; t_hit_near = std::max(t_near.x, t_near.y); float t_hit_far = std::min(t_far.x, t_far.y); if (t_hit_far < 0) return false; contactPoint = rayOrigin + rayDir * t_hit_near; if (t_near.x > t_near.y) { if (rayDir.x < 0) { contactNormal = Vector2f { 1, 0 }; } else { contactNormal = Vector2f { -1, 0 }; } } else if (t_near.x < t_near.y) { if (rayDir.y < 0) { contactNormal = Vector2f { 0, 1 }; } else { contactNormal = Vector2f { 0, -1 }; } } return true; } bool utils::DynamicRectVsRect(const MovingRectf& in, const Rectf& target, Vector2f& contactPoint, Vector2f& contactNormal, float& contactTime, float dt) { if (in.velocity.x == 0 && in.velocity.y == 0) return false; Rectf expanded_target {}; expanded_target.left = target.left - in.width / 2; expanded_target.bottom = target.bottom - in.height / 2; expanded_target.width = target.width + in.width; expanded_target.height = target.height + in.height; if (RayVsRect(Vector2f { in.bottomLeft.x + in.width / 2, in.bottomLeft.y + in.height / 2 }, in.velocity * dt, expanded_target, contactPoint, contactNormal, contactTime)) { if (contactTime <= 1.0f && contactTime >= 0.0f) { return true; } } return false; } float utils::DotProduct(const Vector2f& a, const Vector2f& b) { return a.x * b.x + a.y * b.y; } #pragma endregion CollisionFunctionality int utils::randRange(int min, int max) { return min + rand() % ( ( max + 1 ) - min ); } float utils::lerp(float a, float b, float t) { return a + t * ( b - a ); } Vector2f utils::lerp(const Vector2f& a, const Vector2f& b, float t) { return Vector2f { lerp(a.x, b.x, t), lerp(a.y, b.y, t) }; } float utils::map(float value, float start1, float stop1, float start2, float stop2) { float newVal = (value - start1) / (stop1 - start1) * (stop2 - start2) + start2; return newVal; } bool utils::isKeyDown(int keycode) { const Uint8* pStates = SDL_GetKeyboardState(nullptr); if (pStates != nullptr) { return pStates[keycode]; } return false; } static bool S_PrevKeyStates[256] = { false }; bool utils::isKeyPressed(int keycode) { const Uint8* pStates = SDL_GetKeyboardState(nullptr); if (pStates == nullptr) { return false; } bool pressed { false }; bool currentPressed = pStates[keycode]; bool lastPressed = S_PrevKeyStates[keycode]; if (!lastPressed && currentPressed) { pressed = true; } S_PrevKeyStates[keycode] = currentPressed; return pressed; } bool utils::isKeyUp(int keycode) { const Uint8* pStates = SDL_GetKeyboardState(nullptr); if (pStates[keycode] == 0) { return true; } return false; } Vector2f utils::GetMousePos() { int x, y; SDL_GetMouseState(&x, &y); //TODO: make the screen size a global or something return Vector2f { float(x), float(500.f - y) }; } bool utils::IsMouseButtonDown(int button) { const Uint32 pStates = SDL_GetMouseState(nullptr, nullptr); if (pStates & SDL_BUTTON(button)) { return true; } return false; } static Vector2f ViewportSize{ 900.f, 500.f }; //TODO: somehow move this (Ask teacher) Vector2f utils::GetViewport() { return ViewportSize; } bool utils::isMouseDown(int button) { const Uint32 pStates = SDL_GetMouseState(nullptr, nullptr); if (pStates & SDL_BUTTON(button)) { return true; } return false; } //utils::getScrollMovement()