forked from Barnold1953/GraphicsTutorials
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBallController.cpp
More file actions
197 lines (170 loc) · 7.08 KB
/
Copy pathBallController.cpp
File metadata and controls
197 lines (170 loc) · 7.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#include "BallController.h"
#include "Grid.h"
void BallController::updateBalls(std::vector <Ball>& balls, Grid* grid, float deltaTime, int maxX, int maxY) {
const float FRICTION = 0.01f;
// Update our grabbed balls velocity
if (m_grabbedBall != -1) {
balls[m_grabbedBall].velocity = balls[m_grabbedBall].position - m_prevPos;
}
glm::vec2 gravity = getGravityAccel();
for (size_t i = 0; i < balls.size(); i++) {
// get handle for less typing
Ball& ball = balls[i];
// Update motion if its not grabbed
if (i != m_grabbedBall) {
ball.position += ball.velocity * deltaTime;
// Apply friction
glm::vec2 momentumVec = ball.velocity * ball.mass;
if (momentumVec.x != 0 || momentumVec.y != 0) {
if (FRICTION < glm::length(momentumVec)) {
ball.velocity -= deltaTime * FRICTION * glm::normalize(momentumVec) / ball.mass;
} else {
ball.velocity = glm::vec2(0.0f);
}
}
// Apply gravity
ball.velocity += gravity * deltaTime;
}
// Check wall collision
if (ball.position.x < ball.radius) {
ball.position.x = ball.radius;
if (ball.velocity.x < 0) ball.velocity.x *= -1;
} else if (ball.position.x + ball.radius >= maxX) {
ball.position.x = maxX - ball.radius - 1;
if (ball.velocity.x > 0) ball.velocity.x *= -1;
}
if (ball.position.y < ball.radius) {
ball.position.y = ball.radius;
if (ball.velocity.y < 0) ball.velocity.y *= -1;
} else if (ball.position.y + ball.radius >= maxY) {
ball.position.y = maxY - ball.radius - 1;
if (ball.velocity.y > 0) ball.velocity.y *= -1;
}
// Check to see if the ball moved
Cell* newCell = grid->getCell(ball.position);
if (newCell != ball.ownerCell) {
grid->removeBallFromCell(&balls[i]);
grid->addBall(&balls[i], newCell);
}
}
// Updates all collisions using the spatial partitioning
updateCollision(grid);
//// Update our grabbed ball
if (m_grabbedBall != -1) {
// Update the velocity again, in case it got changed by collision
balls[m_grabbedBall].velocity = balls[m_grabbedBall].position - m_prevPos;
m_prevPos = balls[m_grabbedBall].position;
}
}
void BallController::onMouseDown(std::vector <Ball>& balls, float mouseX, float mouseY) {
for (size_t i = 0; i < balls.size(); i++) {
// Check if the mouse is hovering over a ball
if (isMouseOnBall(balls[i], mouseX, mouseY)) {
m_grabbedBall = i; // BE AWARE, if you change the order of the balls in the vector, this becomes invalid!
m_grabOffset = glm::vec2(mouseX, mouseY) - balls[i].position;
m_prevPos = balls[i].position;
balls[i].velocity = glm::vec2(0.0f);
}
}
}
void BallController::onMouseUp(std::vector <Ball>& balls) {
if (m_grabbedBall != -1) {
// Throw the ball!
balls[m_grabbedBall].velocity = balls[m_grabbedBall].position - m_prevPos;
m_grabbedBall = -1;
}
}
void BallController::onMouseMove(std::vector <Ball>& balls, float mouseX, float mouseY) {
if (m_grabbedBall != -1) {
balls[m_grabbedBall].position = glm::vec2(mouseX, mouseY) - m_grabOffset;
}
}
void BallController::updateCollision(Grid* grid) {
for (size_t i = 0; i < grid->m_cells.size(); i++) {
int x = i % grid->m_numXCells;
int y = i / grid->m_numXCells;
Cell& cell = grid->m_cells[i];
// Loop through all balls in a cell
for (size_t j = 0; j < cell.balls.size(); j++) {
Ball* ball = cell.balls[j];
/// Update with the residing cell
checkCollision(ball, cell.balls, j + 1);
/// Update collision with neighbor cells
if (x > 0) {
// Left
checkCollision(ball, grid->getCell(x - 1, y)->balls, 0);
if (y > 0) {
/// Top left
checkCollision(ball, grid->getCell(x - 1, y - 1)->balls, 0);
}
if (y < grid->m_numYCells - 1) {
// Bottom left
checkCollision(ball, grid->getCell(x - 1, y + 1)->balls, 0);
}
}
// Up cell
if (y > 0) {
checkCollision(ball, grid->getCell(x, y - 1)->balls, 0);
}
}
}
}
void BallController::checkCollision(Ball* ball, std::vector<Ball*>& ballsToCheck, int startingIndex) {
for (size_t i = startingIndex; i < ballsToCheck.size(); i++) {
checkCollision(*ball, *ballsToCheck[i]);
}
}
void BallController::checkCollision(Ball& b1, Ball& b2) {
// We add radius since position is the top left corner
glm::vec2 distVec = b2.position - b1.position;
glm::vec2 distDir = glm::normalize(distVec);
float dist = glm::length(distVec);
float totalRadius = b1.radius + b2.radius;
float collisionDepth = totalRadius - dist;
// Check for collision
if (collisionDepth > 0) {
// Push away the balls based on ratio of masses
b1.position -= distDir * collisionDepth * (b2.mass / b1.mass) * 0.5f;
b2.position += distDir * collisionDepth * (b1.mass / b2.mass) * 0.5f;
// Calculate deflection. http://stackoverflow.com/a/345863
// Fixed thanks to youtube user Sketchy502
float aci = glm::dot(b1.velocity, distDir);
float bci = glm::dot(b2.velocity, distDir);
float acf = (aci * (b1.mass - b2.mass) + 2 * b2.mass * bci) / (b1.mass + b2.mass);
float bcf = (bci * (b2.mass - b1.mass) + 2 * b1.mass * aci) / (b1.mass + b2.mass);
b1.velocity += (acf - aci) * distDir;
b2.velocity += (bcf - bci) * distDir;
if (glm::length(b1.velocity + b2.velocity) > 0.5f) {
// Choose the faster ball
bool choice = glm::length(b1.velocity) < glm::length(b2.velocity);
// Faster ball transfers it's color to the slower ball
choice ? b2.color = b1.color : b1.color = b2.color;
}
}
}
bool BallController::isMouseOnBall(Ball&b, float mouseX, float mouseY) {
return (mouseX >= b.position.x - b.radius && mouseX < b.position.x + b.radius &&
mouseY >= b.position.y - b.radius && mouseY < b.position.y + b.radius);
}
glm::vec2 BallController::getGravityAccel() {
const float GRAVITY_FORCE = 0.02f;
glm::vec2 gravity;
switch (m_gravityDirection) {
case GravityDirection::DOWN:
gravity = glm::vec2(0.0f, -GRAVITY_FORCE);
break;
case GravityDirection::LEFT:
gravity = glm::vec2(-GRAVITY_FORCE, 0.0f);
break;
case GravityDirection::RIGHT:
gravity = glm::vec2(GRAVITY_FORCE, 0.0f);
break;
case GravityDirection::UP:
gravity = glm::vec2(0.0f, GRAVITY_FORCE);
break;
default:
gravity = glm::vec2(0.0f);
break;
}
return gravity;
}