bool collideCircleAARectangle(vec2 cp, float cr, vec2 r) { // Adapted from http://stackoverflow.com/a/402010 vec2 circDist = {std::abs(cp.x) - r.x, std::abs(cp.y) - r.y}; if (circDist.x > cr || circDist.y > cr) { return false; } if (circDist.x <= 0.0f || circDist.y <= 0.0f) { return true; } return length_sqr(circDist) <= cr*cr; }
// Returns the nearest point in line segment a-b to point p. vec2 pointLineSegmentNearestPoint(vec2 p, vec2 a, vec2 b) { // Taken from http://stackoverflow.com/a/1501725 const float l2 = length_sqr(b - a); if (l2 == 0.0f) { return a; } const float t = dot(p - a, b - a) / l2; if (t < 0.0f) { return a; } else if (t > 1.0f) { return b; } else { return a + t * (b - a); } }
void collideBallWithPaddle(Gem& ball, const Paddle& paddle) { SpriteMatrix matrix = paddle.getSpriteMatrix(); // Left sphere vec2 left = {-24, 0}; // Right sphere vec2 right = {24, 0}; matrix.transform(&left.x, &left.y); matrix.transform(&right.x, &right.y); static const int PADDLE_RADIUS = 8; fixed24_8 rel_ball_x = ball.pos_x - paddle.pos_x; fixed24_8 rel_ball_y = ball.pos_y - paddle.pos_y; vec2 rel_ball = {rel_ball_x.toFloat(), rel_ball_y.toFloat()}; vec2 nearest_point = pointLineSegmentNearestPoint(rel_ball, left, right); vec2 penetration = rel_ball - nearest_point; float d_sqr = length_sqr(penetration); float r = PADDLE_RADIUS + Gem::RADIUS; if (d_sqr < r*r) { vec2 vel = {ball.vel_x.toFloat(), ball.vel_y.toFloat()}; int score_addition = static_cast<int>(ball.score_value * (ball.vel_y.toFloat() / 128.f)); ball.score_value = std::min(ball.score_value + std::max(score_addition, 0), Gem::MAX_VALUE); float d = std::sqrt(d_sqr); float sz = r - d; vec2 normal = penetration / d; fixed24_8 push_back_x(sz * normal.x); fixed24_8 push_back_y(sz * normal.y); ball.pos_x += push_back_x; ball.pos_y += push_back_y; vec2 par, perp; splitVector(vel, normal, &par, &perp); vel = perp - par; ball.vel_x = fixed16_16(vel.x); ball.vel_y = fixed16_16(vel.y); } }
float point::length() const { return std::sqrt(length_sqr()); }
void collideBallWithBall(Gem& a, Gem& b) { vec2 dv = {(a.pos_x - b.pos_x).toFloat(), (a.pos_y - b.pos_y).toFloat()}; float d_sqr = length_sqr(dv); if (d_sqr < (2*Gem::RADIUS)*(2*Gem::RADIUS)) { fixed16_16 rel_vel_x = a.vel_x - b.vel_x; fixed16_16 rel_vel_y = a.vel_y - b.vel_y; vec2 rel_vel = {rel_vel_x.toFloat(), rel_vel_y.toFloat()}; float rel_speed_sqr = length_sqr(rel_vel); if (rel_speed_sqr >= Gem::MERGE_SPEED*Gem::MERGE_SPEED) { fixed32_0 two(2); a.pos_x = (a.pos_x + b.pos_x) / two; a.pos_y = (a.pos_y + b.pos_y) / two; a.vel_x = a.vel_x + b.vel_x; a.vel_y = a.vel_y + b.vel_y; a.score_value += b.score_value; b.pos_x = -9999; b.pos_y = 9999; b.vel_x = b.vel_y = 0; b.score_value = 0; } else { float d = std::sqrt(d_sqr); float sz = Gem::RADIUS - d / 2.0f; vec2 normal = dv / d; fixed24_8 push_back_x(sz * normal.x); fixed24_8 push_back_y(sz * normal.y); a.pos_x += push_back_x; a.pos_y += push_back_y; b.pos_x -= push_back_x; b.pos_y -= push_back_y; vec2 a_par, a_perp; vec2 b_par, b_perp; vec2 a_vel = {a.vel_x.toFloat(), a.vel_y.toFloat()}; vec2 b_vel = {b.vel_x.toFloat(), b.vel_y.toFloat()}; splitVector(a_vel, normal, &a_par, &a_perp); splitVector(b_vel, -normal, &b_par, &b_perp); static const float friction = 1.0f; static const float bounce = 0.9f; float A = (1.0f + bounce) / 2.0f; float B = (1.0f - bounce) / 2.0f; a_vel = A*b_par + B*a_par + friction*a_perp; b_vel = A*a_par + B*b_par + friction*b_perp; a.vel_x = fixed16_16(a_vel.x); a.vel_y = fixed16_16(a_vel.y); b.vel_x = fixed16_16(b_vel.x); b.vel_y = fixed16_16(b_vel.y); } } }
vec3 cosine_weighted_point_on_hemisphere(const float u1, const float u2, const vec3 up) { const vec2 p = uniform_point_on_disk(u1, u2); return reorient_vector(mvec3(p[0], std::sqrt(1.0f - length_sqr(p)), p[1]), up); }
float distance_sqr(float4 p, float4 q) { return length_sqr(q - p); }