package tests;
import java.util.ArrayList;
import box2dLight.ChainLight;
import box2dLight.ConeLight;
import box2dLight.DirectionalLight;
import box2dLight.Light;
import box2dLight.PointLight;
import box2dLight.RayHandler;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.ChainShape;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.QueryCallback;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.joints.MouseJoint;
import com.badlogic.gdx.physics.box2d.joints.MouseJointDef;
public class Box2dLightTest extends InputAdapter implements ApplicationListener {
static final int RAYS_PER_BALL = 128;
static final int BALLSNUM = 5;
static final float LIGHT_DISTANCE = 16f;
static final float RADIUS = 1f;
static final float viewportWidth = 48;
static final float viewportHeight = 32;
OrthographicCamera camera;
SpriteBatch batch;
BitmapFont font;
TextureRegion textureRegion;
Texture bg;
/** our box2D world **/
World world;
/** our boxes **/
ArrayList<Body> balls = new ArrayList<Body>(BALLSNUM);
/** our ground box **/
Body groundBody;
/** our mouse joint **/
MouseJoint mouseJoint = null;
/** a hit body **/
Body hitBody = null;
/** pixel perfect projection for font rendering */
Matrix4 normalProjection = new Matrix4();
boolean showText = true;
/** BOX2D LIGHT STUFF */
RayHandler rayHandler;
ArrayList<Light> lights = new ArrayList<Light>(BALLSNUM);
float sunDirection = -90f;
@Override
public void create() {
MathUtils.random.setSeed(Long.MIN_VALUE);
camera = new OrthographicCamera(viewportWidth, viewportHeight);
camera.position.set(0, viewportHeight / 2f, 0);
camera.update();
batch = new SpriteBatch();
font = new BitmapFont();
font.setColor(Color.RED);
textureRegion = new TextureRegion(new Texture(
Gdx.files.internal("data/marble.png")));
bg = new Texture(Gdx.files.internal("data/bg.png"));
createPhysicsWorld();
Gdx.input.setInputProcessor(this);
normalProjection.setToOrtho2D(
0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
/** BOX2D LIGHT STUFF BEGIN */
RayHandler.setGammaCorrection(true);
RayHandler.useDiffuseLight(true);
rayHandler = new RayHandler(world);
rayHandler.setAmbientLight(0f, 0f, 0f, 0.5f);
rayHandler.setBlurNum(3);
initPointLights();
/** BOX2D LIGHT STUFF END */
}
@Override
public void render() {
/** Rotate directional light like sun :) */
if (lightsType == 3) {
sunDirection += Gdx.graphics.getDeltaTime() * 4f;
lights.get(0).setDirection(sunDirection);
}
camera.update();
boolean stepped = fixedStep(Gdx.graphics.getDeltaTime());
Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.disableBlending();
batch.begin();
{
batch.draw(bg, -viewportWidth / 2f, 0, viewportWidth, viewportHeight);
batch.enableBlending();
for (int i = 0; i < BALLSNUM; i++) {
Body ball = balls.get(i);
Vector2 position = ball.getPosition();
float angle = MathUtils.radiansToDegrees * ball.getAngle();
batch.draw(
textureRegion,
position.x - RADIUS, position.y - RADIUS,
RADIUS, RADIUS,
RADIUS * 2, RADIUS * 2,
1f, 1f,
angle);
}
}
batch.end();
/** BOX2D LIGHT STUFF BEGIN */
rayHandler.setCombinedMatrix(camera);
if (stepped) rayHandler.update();
rayHandler.render();
/** BOX2D LIGHT STUFF END */
long time = System.nanoTime();
boolean atShadow = rayHandler.pointAtShadow(testPoint.x,
testPoint.y);
aika += System.nanoTime() - time;
/** FONT */
if (showText) {
batch.setProjectionMatrix(normalProjection);
batch.begin();
font.draw(batch,
"F1 - PointLight",
0, Gdx.graphics.getHeight());
font.draw(batch,
"F2 - ConeLight",
0, Gdx.graphics.getHeight() - 15);
font.draw(batch,
"F3 - ChainLight",
0, Gdx.graphics.getHeight() - 30);
font.draw(batch,
"F4 - DirectionalLight",
0, Gdx.graphics.getHeight() - 45);
font.draw(batch,
"F5 - random lights colors",
0, Gdx.graphics.getHeight() - 75);
font.draw(batch,
"F6 - random lights distance",
0, Gdx.graphics.getHeight() - 90);
font.draw(batch,
"F9 - default blending (1.3)",
0, Gdx.graphics.getHeight() - 120);
font.draw(batch,
"F10 - over-burn blending (default in 1.2)",
0, Gdx.graphics.getHeight() - 135);
font.draw(batch,
"F11 - some other blending",
0, Gdx.graphics.getHeight() - 150);
font.draw(batch,
"F12 - toggle help text",
0, Gdx.graphics.getHeight() - 180);
font.draw(batch,
Integer.toString(Gdx.graphics.getFramesPerSecond())
+ "mouse at shadows: " + atShadow
+ " time used for shadow calculation:"
+ aika / ++times + "ns" , 0, 20);
batch.end();
}
}
void clearLights() {
if (lights.size() > 0) {
for (Light light : lights) {
light.remove();
light.dispose();
}
lights.clear();
}
groundBody.setActive(true);
}
void initPointLights() {
clearLights();
for (int i = 0; i < BALLSNUM; i++) {
PointLight light = new PointLight(
rayHandler, RAYS_PER_BALL, null, LIGHT_DISTANCE, 0f, 0f);
light.attachToBody(balls.get(i), RADIUS / 2f, RADIUS / 2f);
light.setColor(
MathUtils.random(),
MathUtils.random(),
MathUtils.random(),
1f);
lights.add(light);
}
}
void initConeLights() {
clearLights();
for (int i = 0; i < BALLSNUM; i++) {
ConeLight light = new ConeLight(
rayHandler, RAYS_PER_BALL, null, LIGHT_DISTANCE,
0, 0, 0f, MathUtils.random(15f, 40f));
light.attachToBody(
balls.get(i),
RADIUS / 2f, RADIUS / 2f, MathUtils.random(0f, 360f));
light.setColor(
MathUtils.random(),
MathUtils.random(),
MathUtils.random(),
1f);
lights.add(light);
}
}
void initChainLights() {
clearLights();
for (int i = 0; i < BALLSNUM; i++) {
ChainLight light = new ChainLight(
rayHandler, RAYS_PER_BALL, null, LIGHT_DISTANCE, 1,
new float[]{-5, 0, 0, 3, 5, 0});
light.attachToBody(
balls.get(i),
MathUtils.random(0f, 360f));
light.setColor(
MathUtils.random(),
MathUtils.random(),
MathUtils.random(),
1f);
lights.add(light);
}
}
void initDirectionalLight() {
clearLights();
groundBody.setActive(false);
sunDirection = MathUtils.random(0f, 360f);
DirectionalLight light = new DirectionalLight(
rayHandler, 4 * RAYS_PER_BALL, null, sunDirection);
lights.add(light);
}
private final static int MAX_FPS = 30;
private final static int MIN_FPS = 15;
public final static float TIME_STEP = 1f / MAX_FPS;
private final static float MAX_STEPS = 1f + MAX_FPS / MIN_FPS;
private final static float MAX_TIME_PER_FRAME = TIME_STEP * MAX_STEPS;
private final static int VELOCITY_ITERS = 6;
private final static int POSITION_ITERS = 2;
float physicsTimeLeft;
long aika;
int times;
private boolean fixedStep(float delta) {
physicsTimeLeft += delta;
if (physicsTimeLeft > MAX_TIME_PER_FRAME)
physicsTimeLeft = MAX_TIME_PER_FRAME;
boolean stepped = false;
while (physicsTimeLeft >= TIME_STEP) {
world.step(TIME_STEP, VELOCITY_ITERS, POSITION_ITERS);
physicsTimeLeft -= TIME_STEP;
stepped = true;
}
return stepped;
}
private void createPhysicsWorld() {
world = new World(new Vector2(0, 0), true);
float halfWidth = viewportWidth / 2f;
ChainShape chainShape = new ChainShape();
chainShape.createLoop(new Vector2[] {
new Vector2(-halfWidth, 0f),
new Vector2(halfWidth, 0f),
new Vector2(halfWidth, viewportHeight),
new Vector2(-halfWidth, viewportHeight) });
BodyDef chainBodyDef = new BodyDef();
chainBodyDef.type = BodyType.StaticBody;
groundBody = world.createBody(chainBodyDef);
groundBody.createFixture(chainShape, 0);
chainShape.dispose();
createBoxes();
}
private void createBoxes() {
CircleShape ballShape = new CircleShape();
ballShape.setRadius(RADIUS);
FixtureDef def = new FixtureDef();
def.restitution = 0.9f;
def.friction = 0.01f;
def.shape = ballShape;
def.density = 1f;
BodyDef boxBodyDef = new BodyDef();
boxBodyDef.type = BodyType.DynamicBody;
for (int i = 0; i < BALLSNUM; i++) {
// Create the BodyDef, set a random position above the
// ground and create a new body
boxBodyDef.position.x = -20 + (float) (Math.random() * 40);
boxBodyDef.position.y = 10 + (float) (Math.random() * 15);
Body boxBody = world.createBody(boxBodyDef);
boxBody.createFixture(def);
balls.add(boxBody);
}
ballShape.dispose();
}
/**
* we instantiate this vector and the callback here so we don't irritate the
* GC
**/
Vector3 testPoint = new Vector3();
QueryCallback callback = new QueryCallback() {
@Override
public boolean reportFixture(Fixture fixture) {
if (fixture.getBody() == groundBody)
return true;
if (fixture.testPoint(testPoint.x, testPoint.y)) {
hitBody = fixture.getBody();
return false;
} else
return true;
}
};
@Override
public boolean touchDown(int x, int y, int pointer, int newParam) {
// translate the mouse coordinates to world coordinates
testPoint.set(x, y, 0);
camera.unproject(testPoint);
// ask the world which bodies are within the given
// bounding box around the mouse pointer
hitBody = null;
world.QueryAABB(callback, testPoint.x - 0.1f, testPoint.y - 0.1f,
testPoint.x + 0.1f, testPoint.y + 0.1f);
// if we hit something we create a new mouse joint
// and attach it to the hit body.
if (hitBody != null) {
MouseJointDef def = new MouseJointDef();
def.bodyA = groundBody;
def.bodyB = hitBody;
def.collideConnected = true;
def.target.set(testPoint.x, testPoint.y);
def.maxForce = 1000.0f * hitBody.getMass();
mouseJoint = (MouseJoint) world.createJoint(def);
hitBody.setAwake(true);
}
return false;
}
/** another temporary vector **/
Vector2 target = new Vector2();
@Override
public boolean touchDragged(int x, int y, int pointer) {
camera.unproject(testPoint.set(x, y, 0));
target.set(testPoint.x, testPoint.y);
// if a mouse joint exists we simply update
// the target of the joint based on the new
// mouse coordinates
if (mouseJoint != null) {
mouseJoint.setTarget(target);
}
return false;
}
@Override
public boolean touchUp(int x, int y, int pointer, int button) {
// if a mouse joint exists we simply destroy it
if (mouseJoint != null) {
world.destroyJoint(mouseJoint);
mouseJoint = null;
}
return false;
}
@Override
public void dispose() {
rayHandler.dispose();
world.dispose();
}
/**
* Type of lights to use:
* 0 - PointLight
* 1 - ConeLight
* 2 - ChainLight
* 3 - DirectionalLight
*/
int lightsType = 0;
@Override
public boolean keyDown(int keycode) {
switch (keycode) {
case Input.Keys.F1:
if (lightsType != 0) {
initPointLights();
lightsType = 0;
}
return true;
case Input.Keys.F2:
if (lightsType != 1) {
initConeLights();
lightsType = 1;
}
return true;
case Input.Keys.F3:
if (lightsType != 2) {
initChainLights();
lightsType = 2;
}
return true;
case Input.Keys.F4:
if (lightsType != 3) {
initDirectionalLight();
lightsType = 3;
}
return true;
case Input.Keys.F5:
for (Light light : lights)
light.setColor(
MathUtils.random(),
MathUtils.random(),
MathUtils.random(),
1f);
return true;
case Input.Keys.F6:
for (Light light : lights)
light.setDistance(MathUtils.random(
LIGHT_DISTANCE * 0.5f, LIGHT_DISTANCE * 2f));
return true;
case Input.Keys.F9:
rayHandler.diffuseBlendFunc.reset();
return true;
case Input.Keys.F10:
rayHandler.diffuseBlendFunc.set(
GL20.GL_DST_COLOR, GL20.GL_SRC_COLOR);
return true;
case Input.Keys.F11:
rayHandler.diffuseBlendFunc.set(
GL20.GL_SRC_COLOR, GL20.GL_DST_COLOR);
return true;
case Input.Keys.F12:
showText = !showText;
return true;
default:
return false;
}
}
@Override
public boolean mouseMoved(int x, int y) {
testPoint.set(x, y, 0);
camera.unproject(testPoint);
return false;
}
@Override
public boolean scrolled(int amount) {
camera.rotate((float) amount * 3f, 0, 0, 1);
return false;
}
@Override
public void pause() {
}
@Override
public void resize(int arg0, int arg1) {
}
@Override
public void resume() {
}
}