2 * Copyright 2008 Jacek Caban
3 * Copyright 2008 Piotr Caban
4 * Copyright 2008 Jaroslaw Sobiecki
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (aU your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
36 //#define DEBUG_PHISICS
48 #define trace_vector(v) trace(#v ": %lf %lf %lf\n", (v)[0], (v)[1], (v)[2])
49 #define trace_quaternion(v) trace(#v ": %lf %lf %lf %lf\n", (v).w, (v).x, (v).y, (v).z)
51 const TableParameters
TableParameters::DefaultTableParameters
;
54 #define ALMOST_ZERO 0.0001
56 static Ogre::Real
inline deg2rad(Ogre::Real alpha
) {
57 return (alpha
/360.0)*2.0*M_PI
;
61 * Constructor of the Table class. Sets table and phisics parameters.
63 Table::Table(Real sx
, Real sy
, const TableParameters
*p
) : size_x(sx
/2.0), size_y(sy
/2.0), current_time(0), params(p
)
65 gettimeofday(&start_time
, NULL
);
69 * Updates state od balls diring calculated dt (small delta time).
71 Real
Table::eulerUpdate() {
72 BallVector::iterator iter
, iter2
;
73 Real dt
= MAX_DT
, tmp
;
74 Ball
*ball
= NULL
, *ball2
;
75 int bound
, bound_copy
;
81 } action_type
= BALL_NONE
;
83 trace("eulerUpdate\n");
85 /* Find the next action */
86 for(iter
= balls
.begin(); iter
!= balls
.end(); iter
++) {
87 tmp
= boundCrossTime(**iter
, bound
);
89 action_type
= BALL_BOUND
;
95 for(iter2
= iter
+1; iter2
!= balls
.end(); iter2
++) {
96 tmp
= ballCrossTime(**iter
, **iter2
);
98 action_type
= BALL_BALL
;
106 trace("dt=%lf (%d)\n", dt
, action_type
);
110 switch(action_type
) {
114 boundCross(*ball
, bound_copy
);
117 ballCross(*ball
, *ball2
);
125 * Updates balls state depending on their states.
127 void Table::simpleUpdate(Real dt
) {
128 BallVector::iterator iter
;
129 Vector3 norm
, r
= Vector3::ZERO
, tmp
, rotv
, rotp
, slow
;
132 /* Slow down all balls (resistance from the table) */
133 for(iter
= balls
.begin(); iter
!= balls
.end(); iter
++) {
135 norm
= ball
->speed
.normalisedCopy();
138 trace("preUpdate:\n");
139 trace_vector(ball
->position
);
140 trace_vector(ball
->speed
);
141 trace_vector(ball
->rotation
);
142 trace_quaternion(ball
->orientation
);
144 //Set ball orientation
145 ball
->orientation
= Quaternion(Radian(ball
->rotation
.length()*dt
), ball
->rotation
.normalisedCopy())
148 ball
->position
+= ball
->speed
*dt
;
149 tmp
= params
->slidingFriction
* params
->gravity
* dt
* norm
;
150 if(ball
->speed
.squaredLength() > tmp
.squaredLength())
153 ball
->speed
= Vector3::ZERO
;
158 rotv
= ball
->rotation
.dotProduct(tmp
)*tmp
;
159 rotp
= ball
->rotation
- rotv
;
160 tmp
= r
.crossProduct(rotv
);
161 slow
= 10*Vector3::UNIT_Z
.crossProduct(norm
)*params
->staticFriction
*dt
;
166 if(tmp
.dotProduct(rotv
) < 0.0 || tmp
.squaredLength() < ball
->speed
.squaredLength()) {
174 rotp
-= rotp
.normalisedCopy()*params
->staticFriction
*dt
;
175 ball
->rotation
= rotv
+rotp
;
177 tmp
= ball
->rotation
;
180 ball
->speed
+= -dt
*0.01*params
->staticFriction
*tmp
.crossProduct(r
);
182 trace("simpleUpdate:\n");
183 trace_vector(ball
->position
);
184 trace_vector(ball
->speed
);
185 trace_vector(ball
->rotation
);
190 * Sets the ball to be fallen.
192 inline void Table::ballFallIn(Ball
&ball
) {
193 ball
.position
.z
= -10.666;
194 ball
.onTable
= false;
195 ball
.speed
= ball
.rotation
= Vector3::ZERO
;
199 * Handles crossing ball and bound event.
201 void Table::boundCross(Ball
&ball
, int bound
) {
202 Real rot
= ball
.rotation
.z
*params
->boundFriction
*ball
.speed
.length();
204 if((ball
.position
-Vector3(size_x
, size_y
, 0.6)).squaredLength() < params
->pocketSize
) {
208 else if((ball
.position
-Vector3(size_x
, -size_y
, 0.6)).squaredLength() < params
->pocketSize
) {
212 else if((ball
.position
-Vector3(0.0, size_y
, 0.6)).squaredLength() < params
->pocketSize
) {
216 else if((ball
.position
-Vector3(0.0, -size_y
, 0.6)).squaredLength() < params
->pocketSize
) {
220 else if((ball
.position
-Vector3(-size_x
, size_y
, 0.6)).squaredLength() < params
->pocketSize
) {
224 else if((ball
.position
-Vector3(-size_x
, -size_y
, 0.6)).squaredLength() < params
->pocketSize
) {
229 trace("boundCross\n");
233 ball
.speed
[0] = -ball
.speed
[0];
234 ball
.speed
[1] += rot
;
237 ball
.speed
[0] = -ball
.speed
[0];
238 ball
.speed
[1] -= rot
;
241 ball
.speed
[0] -= rot
;
242 ball
.speed
[1] = -ball
.speed
[1];
245 ball
.speed
[0] += rot
;
246 ball
.speed
[1] = -ball
.speed
[1];
250 ball
.speed
*= params
->boundResistance
;
251 ball
.rotation
[2] = ALMOST_ZERO
;
252 ball
.rotation
*= 0.3;
256 * Handles two balls crossing event.
258 void Table::ballCross(Ball
&ball1
, Ball
&ball2
) {
259 Vector3 r
, norm
, v1x
, v2x
, v1y
, v2y
;
261 if(!ball1
.firstHit
) ball1
.firstHit
= ball2
.ballNo
;
262 if(!ball2
.firstHit
) ball2
.firstHit
= ball1
.ballNo
;
264 trace("preBallCross:\n");
265 trace_vector(ball1
.position
);
266 trace_vector(ball1
.speed
);
267 trace_vector(ball1
.rotation
);
268 trace_vector(ball2
.position
);
269 trace_vector(ball2
.speed
);
270 trace_vector(ball2
.rotation
);
272 r
= ball1
.position
-ball2
.position
;
279 v1x
= ball1
.speed
.dotProduct(norm
) * norm
;
280 v2x
= ball2
.speed
.dotProduct(norm
) * norm
;
281 v1y
= ball1
.speed
- v1x
;
282 v2y
= ball2
.speed
- v2x
;
289 ball1
.speed
= v1x
+v2y
;
290 ball2
.speed
= v2x
+v1y
;
292 trace("ballCross:\n");
293 trace_vector(ball1
.position
);
294 trace_vector(ball1
.speed
);
295 trace_vector(ball1
.rotation
);
296 trace_vector(ball2
.position
);
297 trace_vector(ball2
.speed
);
298 trace_vector(ball2
.rotation
);
302 * Calculates time to the nearest ball and bound cross.
304 Real
Table::boundCrossTime(Ball
&ball
, int &bound
) {
307 trace("boundCrossTime:\n");
308 if(ball
.speed
[0] < -ALMOST_ZERO
) {
310 ret
= (-(size_x
-ball
.r
) - ball
.position
[0]) / ball
.speed
[0];
311 trace("left %lf\n", ret
);
312 }else if(ball
.speed
[0] > ALMOST_ZERO
) {
314 ret
= (size_x
-ball
.r
- ball
.position
[0]) / ball
.speed
[0];
315 trace("right %lf\n", ret
);
317 trace("left right no\n");
321 if(ball
.speed
[1] < -ALMOST_ZERO
) {
322 tmp
= (-(size_y
-ball
.r
) - ball
.position
[1]) / ball
.speed
[1];
323 trace("bottom %lf\n", tmp
);
325 bound
= BOUND_BOTTOM
;
328 }else if(ball
.speed
[1] > ALMOST_ZERO
) {
329 tmp
= (size_y
-ball
.r
- ball
.position
[1]) / ball
.speed
[1];
330 trace("upper %lf\n", tmp
);
340 trace_vector(ball
.position
);
341 trace_vector(ball
.speed
);
342 trace_vector(ball
.rotation
);
343 trace("ret %lf\n", ret
);
352 * Calculates time to the nearest two balls cross.
354 Real
Table::ballCrossTime(Ball
&ball1
, Ball
&ball2
) {
356 Ball ball1_next
= ball1
;
357 Ball ball2_next
= ball2
;
359 ball1_next
.position
.x
+= ball1
.speed
.x
*ALMOST_ZERO
;
360 ball1_next
.position
.y
+= ball1
.speed
.y
*ALMOST_ZERO
;
361 ball2_next
.position
.x
+= ball2
.speed
.x
*ALMOST_ZERO
;
362 ball2_next
.position
.y
+= ball2
.speed
.y
*ALMOST_ZERO
;
364 dp
= ball2
.position
- ball1
.position
;
365 dp_next
= ball2_next
.position
- ball1_next
.position
;
367 if(dp
.squaredLength()<dp_next
.squaredLength()+ALMOST_ZERO
*ALMOST_ZERO
) return 100.0;
368 if(dp
.squaredLength()>4.0*ball1
.r
) return 100.0;
374 * Checks if there is any ball moving.
376 bool Table::ballsMoving() {
377 BallVector::iterator iter
;
380 for(iter
= balls
.begin(); iter
!= balls
.end(); iter
++) {
383 if(ball
->speed
.squaredLength() > ALMOST_ZERO
)
386 ball
->speed
= ball
->rotation
= Vector3::ZERO
;
392 static unsigned timeval2ms(const struct timeval
&t
) {
393 return t
.tv_sec
*1000 + t
.tv_usec
/1000;
397 * Updates balls state to the given time.
399 bool Table::update(Real time
) {
402 current_time
+= time
;
404 while(current_time
>0.0) {
406 trace("update <<\n");
410 current_time
-= eulerUpdate()/2.0;
417 * Handles shotting ball by cue.
419 void Ball::shot(Real direction
, Real force
, Vector3 shot_pos
, Real forceScale
, Real hitTime
) {
422 direction
= deg2rad(direction
);
423 shot_pos
[0] = deg2rad(shot_pos
[0]);
424 shot_pos
[1] = deg2rad(shot_pos
[1]);
425 shot_pos
[2] = deg2rad(shot_pos
[2]);
427 dir
[0] = -Math::Cos(direction
);
428 dir
[1] = -Math::Sin(direction
);
431 trace("shot: %lf %lf\n", direction
, force
);
432 trace_vector(shot_pos
);
436 vr
[0] = Math::Cos(direction
+ shot_pos
[0]);
437 vr
[1] = Math::Sin(direction
+ shot_pos
[0]);
438 vr
[2] = Math::Sin(shot_pos
[1]);
442 /* v' = (-k dt)/m * x */
443 speed
= hitTime
/mass
*dir
*force
;
447 /* w' = 2.5 * dt / (m r2) (r x (-kx)) */
448 rotation
= 2.5 * (20.0 * hitTime
/ (mass
* r
*r
)) * (vr
.crossProduct(dir
));
450 trace_vector(position
);
452 trace_vector(rotation
);