/*
* Flingbox - An OpenSource physics sandbox for Google's Android
* Copyright (C) 2009 Jon Ander Peñalba & Endika Gutiérrez
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package edu.eside.flingbox.physics;
import java.util.ArrayList;
import java.util.concurrent.Semaphore;
import edu.eside.flingbox.math.Vector2D;
import edu.eside.flingbox.physics.collisions.Arbiter;
import edu.eside.flingbox.physics.gravity.GravitySource;
import edu.eside.flingbox.utils.PositionComparator;
/**
* Stores all physic object in scene and make those
* interact.
* ScenePhysics manage thread for update objects
*/
public class ScenePhysics implements Runnable {
private final GravitySource mGravity;
/** List of physical bodys on scene */
private final ArrayList<PhysicBody> mOnSceneBodies = new ArrayList<PhysicBody>();
/** Collision manager for current scene */
private final Arbiter mArbiter = new Arbiter();
/** Semaphore for lock writing on mOnSceneBodys */
private Semaphore mLockOnSceneBodys = new Semaphore(1);
/** Thread for simulation */
private Thread mSimulationThread;
/** Flag for kill simulation */
private boolean mDoKill = false;
/** Flag indicating if thread is running */
private Semaphore mSimulationMutex = new Semaphore(1, true);
/**
* Initializes an empty scene
*/
public ScenePhysics(final GravitySource gravity) {
mGravity = gravity;
PositionComparator.setGroundSense(gravity);
}
/**
* Adds physical object
* @param object object to be added
*/
public void add(final PhysicBody body) {
try {
mLockOnSceneBodys.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
mOnSceneBodies.add(body);
mArbiter.add(body.getCollider());
mLockOnSceneBodys.release();
}
public boolean remove(final PhysicBody body) {
try {
mLockOnSceneBodys.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean removed = mOnSceneBodies.remove(body);
mLockOnSceneBodys.release();
removed &= mArbiter.remove(body.getCollider());
return removed;
}
/**
* Starts simulation
*/
public void startSimulation() {
if (mSimulationThread != null && mSimulationThread.isAlive())
return;
mDoKill = false;
mSimulationThread = new Thread(this);
mSimulationThread.start();
}
/**
* Sends message to kill and waits
*/
public void stopSimulation() {
mDoKill = true;
try {
mSimulationMutex.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
mSimulationThread = null;
mSimulationMutex.release();
System.gc(); // Good moment to call to GC
}
/**
* @return true if simulating
*/
public boolean isSimulating() {
return mSimulationThread != null ? mSimulationThread.isAlive() : false;
}
/**
* Thread for simulation
*/
@Override
public void run() {
final ArrayList<PhysicBody> bodies = mOnSceneBodies;
long lastTime = System.currentTimeMillis();
long time;
final Vector2D force = new Vector2D();
for (; !mDoKill; ) {
try {
mSimulationMutex.acquire();
} catch (InterruptedException e2) {
e2.printStackTrace();
}
/* Compute time */
time = System.currentTimeMillis() - lastTime;
lastTime = System.currentTimeMillis();
/* We need a semaphore here */
try {
mLockOnSceneBodys.acquire();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
/* Apply gravity impulse */
for (PhysicBody body : bodies) {
force.set(mGravity);
body.applyImpulse(force.mul(body.getBodyMass() * (float) time / 1000f));
}
mLockOnSceneBodys.release();
/* Then apply collisions forces */
mArbiter.checkCollisions();
try {
mLockOnSceneBodys.acquire();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
/* Last update body */
for (PhysicBody body : bodies)
body.onUpdateBody((float) time / 1000f);
mLockOnSceneBodys.release();
mSimulationMutex.release();
/* Keep max frame-rate */
try {
if (time < 40)
Thread.sleep(40 - time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mDoKill = false;
}
}