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