001 package physics3d;
002 import java.util.ArrayList;
003 import java.util.List;
004 import java.util.Set;
005
006 import components.Ball;
007 import components.GameObject;
008
009 /**
010 * <p>
011 * OldGamePhysics is an immutable class with static methods that does physics calculations.
012 *
013 * OldGamePhysics does some things better than GamePhysics but does not have as many features,
014 * such as calculating collisions between balls.
015 *
016 * </p>
017 */
018
019 /*
020 * Rep Invariant:
021 *
022 * geometry != null
023 */
024
025 public class OldGamePhysics {
026
027 //fields
028 private static Geometry3D geometry = new Geometry3D(); //the brains of the operation
029
030 ////// THE CRUX OF THE MATTER /////
031
032 /** Move the given ball. **/
033 public static List<GameObject> moveBall(Ball ball) {
034 List<GameObject> collisions = new ArrayList<GameObject>(); //to be returned
035 if (ball.isFrozen())
036 return collisions;
037
038 //System.err.println("entering moveBall with "+ ball);
039 geometry.setForesightAndSubintervals(1.0/(double)ball.getGameSpace().getSettings().getFPS(), 10);
040
041 //some local variables
042 double timeleft = geometry.getMaximumForesight();
043 Set<GameObject> allObjects = ball.getGameSpace().getObjects();
044
045 while (timeleft > 0.0) {
046 //////System.out.println("timeleft="+timeleft);
047 double nextTime = Double.NaN; //maybe not a good idea
048
049 // get the possible collisions using bounding spheres
050 List<GameObject> candidates = willCollideWithBound(ball, allObjects, timeleft);
051 if (candidates.size() == 0) {
052 nextTime = timeleft;
053 freeMove(ball, timeleft);
054 } else {
055 //Collections.sort(candidates, new DistanceFrom(ball.getCenter()));
056
057 // get the next collision (with a PhysicsShape, at a time)
058 PhysicsShape nextShape = nextShape(ball, candidates, timeleft, collisions);
059 if (nextShape == null) {
060 nextTime = timeleft;
061 freeMove(ball, timeleft);
062 } else {
063
064 Sphere ballShape = (Sphere)(world(ball.getBounds(), ball));
065 nextTime = geometry.timeUntilCollision(nextShape, ballShape, ball.getVelocity());
066 if (nextTime == 0) {
067 System.err.println("In old GamePhysics, nextTime = 0");
068 }
069 //////System.out.println("sancheck: nextTime=" + nextTime);
070
071 if (farther(ball,candidates.get(0))) {
072 //Farther returns true, if the ball is actually getting father from the
073 //candidate, even though we think this should never happen, but it does.
074 //To deal with this, we let the ball naturally move (unimpeded by a superflourous reflection)
075
076 nextTime = 0.0003; //Just move a tiny bit
077 freeMove(ball, 0.0003);
078 }
079
080 // if collision, move for time s, call reflect() method, loop
081 if (nextTime < timeleft) { //which happens, btw, to be less than infinity
082 //////System.out.println("old old vel=" + ball.getVelocity());
083 freeMove(ball, nextTime);
084 //////System.out.println("old vel=" + ball.getVelocity());
085 ball.setVelocity(geometry.reflect(nextShape, ballShape, ball.getVelocity(), candidates.get(0).getCoRef()));
086 //////System.out.println("new vel=" + ball.getVelocity());
087 } else { // else move for timeleft, loop
088 nextTime = timeleft;
089 freeMove(ball, timeleft);
090 }
091 } //if not actually colliding with anything
092 } //if there are candidates
093 timeleft -= nextTime;
094 }
095
096 //System.err.println("exiting moveBall");
097 return collisions;
098 } //moveBall
099
100 /** Returns those GameObjects where the ball will collide with their bounds in the next
101 * timeleft seconds. */
102 public static List<GameObject> willCollideWithBound(Ball ball, Set<GameObject> objects, double timeleft) {
103 //System.err.println("entering willCollideWithBound");
104 List<GameObject> candidates = new ArrayList<GameObject>();
105 Sphere ballShape = (Sphere)(world(ball.getBounds(), ball));
106
107 for (GameObject g : objects) {
108 PhysicsShape gShape = world(g.getShape().getBound(), g);
109 //////System.out.println(gShape + ", "
110 // + ballShape + ", " + ball.getVelocity());
111 double time = geometry.timeUntilCollision(gShape, ballShape, ball.getVelocity());
112 //////System.out.println("time until ball "+ ballShape + "with velocity "+ ball.getVelocity() + " collides with bound " + gShape + ": "+ time);
113 if (time < timeleft)
114 candidates.add(g);
115 }
116 return candidates;
117 }
118
119 /** Return the closest PhysicsShape that this ball will collide with in the
120 * next timeleft seconds. If it will not collide with any PhysicsShape,
121 * null
122 * @returns a physicsshape set in WORLD SPACE, or null
123 * @requires: candidates in sorted order by distance from Ball
124 * @effects: adds object to collisions if there is one
125 */
126 public static PhysicsShape nextShape(Ball ball, List<GameObject> candidates, double timeleft, List<GameObject> collisions) {
127 //System.err.println("entering nextShape");
128 PhysicsShape next = null; //arbitrary
129 Sphere ballShape = (Sphere)(world(ball.getBounds(), ball));
130
131 //what we need is the gameobject, its physicsshape, and time for the first collision
132 GameObject leastObj = null;
133 PhysicsShape leastSh = null;
134 double leastTime = Double.MAX_VALUE;
135
136 for (GameObject g : candidates) {
137 //check each specific physics3d shape
138 for (PhysicsShape s : g.getShape().getParts()) {
139 PhysicsShape sShape = world(s, g);
140 double time = geometry.timeUntilCollision(sShape, ballShape, ball.getVelocity());
141 //////System.out.println("time until ball "+ ballShape + " collides with " + sShape + ": "+ time);
142 if (time < leastTime) {
143 leastTime = time;
144 leastSh = sShape;
145 leastObj = g;
146 } //if this is the least so far, set vars
147 }
148 }
149 if (leastTime < timeleft) { //if will collide in this frame
150 collisions.add(leastObj);
151
152 //I want to get the least candidate back!
153 candidates.clear();
154 candidates.add(leastObj);
155 next = leastSh;
156 }
157 //////System.out.println("nextShape: " + next);
158 return next;
159 } //nextShape
160
161 /** Move the given ball for the specified amount of time, under only the
162 * influence of gravity and air resistance
163 * @effects ball.velocity, ball.position
164 */
165 public static void freeMove(Ball ball, double time) {
166 Vect3 vel = ball.getVelocity();
167 double mass = 1.0; //ball.getMass();
168 Vect3 pos = ball.getCenter();
169
170 //f_d = -bv, b = mu + mu2*|v|
171 double b = ball.getGameSpace().getSettings().getMu() + ball.getGameSpace().getSettings().getMu2()*vel.rho();
172 Vect3 f_drag = vel.times(-1.0 * b);
173 //f_g = mg
174 Vect3 f_gravity = ball.getGameSpace().getSettings().getGravity().times(mass);
175 //f_net = f_d + f_g
176 Vect3 f_net = f_drag.plus(f_gravity);
177
178 //where the ball should move
179 //x_f = x_i + t*v_i + 1/2at^2 //assumes constant acceleration
180 Vect3 next_pos = pos.plus( vel.times(time) ).plus ( f_net.times(1/mass).times(0.5).times(time*time));
181 //v_f = v_i + f_net/mass * t
182 Vect3 next_vel = vel.plus(f_net.times(1/mass).times(time));
183
184 ball.setCenter(next_pos);
185 ball.setVelocity(next_vel);
186 } //freeMove
187
188 /** return p in the worldspace of o **/
189 public static PhysicsShape world(PhysicsShape p, GameObject o) {
190 //////System.out.println("world: p="+p+", o="+o+", pos="+o.getPosition());
191 return p.rotateAboutCwithAxisAandAngleT(Vect3.ZERO,
192 o.getOrientVector(), o.getOrientAngle()).translateByT(o.getCenter());
193 }
194
195 /**
196 * Given too game objects, ball and canidate, father will return true
197 * only if ball is getting closer to canidate.
198 * @requires ball and canidate != null
199 * @returns true iff ball is moving towards canidate
200 */
201 public static boolean farther(GameObject ball, GameObject canidate) {
202 if (canidate.getCenter().plus(ball.getVelocity()).rho() > canidate.getCenter().rho()) {
203 ////System.out.println("===>According to my math, I'm getting farther away, so I'm gonna free move!");
204 return true;
205 }
206 return false;
207 }
208
209 } //GamePhysics