/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
******************************************************************************/
package com.badlogic.gdx.graphics.g2d;
import java.io.BufferedReader;
import java.io.IOException;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.RayCastCallback;
import com.badlogic.gdx.physics.box2d.World;
/**
* @author kalle_h
*
* ParticleEmitterBox2D use box2d rayCast:ing to achieve continuous collision detection against box2d fixtures.
* If particle detect collision it change it's direction before actual collision would occur. Velocity is 100%
* reflected.
*
* These particles does not have any other physical attributes or functionality. Particles can't collide to
* other particles.
*/
public class ParticleEmitterBox2D extends ParticleEmitter {
final World world;
final Vector2 startPoint = new Vector2();
final Vector2 endPoint = new Vector2();
/** collision flag */
boolean particleCollided;
float normalAngle;
/** If velocities squared is shorter than this it could lead 0 length rayCast that cause c++ assertion at box2d */
private final static float EPSILON = 0.001f;
/** default visibility to prevent synthetic accesor creation */
final RayCastCallback rayCallBack = new RayCastCallback() {
public float reportRayFixture(Fixture fixture, Vector2 point, Vector2 normal, float fraction) {
ParticleEmitterBox2D.this.particleCollided = true;
ParticleEmitterBox2D.this.normalAngle = MathUtils.atan2(normal.y, normal.x) * MathUtils.radiansToDegrees;
return fraction;
}
};
/**
* Constructs default ParticleEmitterBox2D. Box2d World is used for rayCasting. Assumes that particles use same unit
* system that box2d world does.
*
* @param world
*/
public ParticleEmitterBox2D(World world) {
super();
this.world = world;
}
/**
* /**Constructs ParticleEmitterBox2D using bufferedReader. Box2d World is used for rayCasting. Assumes that
* particles use same unit system that box2d world does.
*
* @param world
* @param reader
* @throws IOException
*/
public ParticleEmitterBox2D(World world, BufferedReader reader) throws IOException {
super(reader);
this.world = world;
}
/**
* Constructs ParticleEmitterBox2D fully copying given emitter attributes. Box2d World is used for rayCasting.
* Assumes that particles use same unit system that box2d world does.
*
* @param world
* @param emitter
*/
public ParticleEmitterBox2D(World world, ParticleEmitter emitter) {
super(emitter);
this.world = world;
}
@Override
protected Particle newParticle(Sprite sprite) {
return new ParticleBox2D(sprite);
}
/** Particle that can collide to box2d fixtures */
private class ParticleBox2D extends Particle {
public ParticleBox2D(Sprite sprite) {
super(sprite);
}
/**
* translate particle given amount. Continuous collision detection achieved by using RayCast from oldPos to
* newPos.
*
* @param velocityX
* @param velocityY
*/
@Override
public void translate(float velocityX, float velocityY) {
/**
* If velocities squares summed is shorter than Epsilon it could lead ~0 length rayCast that cause nasty c++
* assertion inside box2d. This is so short distance that moving particle has no effect so this return
* early.
*/
if ((velocityX * velocityX + velocityY * velocityY) < EPSILON)
return;
/** Position offset is half of sprite texture size. */
final float x = getX() + getWidth() / 2f;
final float y = getY() + getHeight() / 2f;
/** collision flag to false */
particleCollided = false;
startPoint.set(x, y);
endPoint.set(x + velocityX, y + velocityY);
if (world != null)
world.rayCast(rayCallBack, startPoint, endPoint);
/** If ray collided boolean has set to true at rayCallBack */
if (!particleCollided) {
// perfect reflection
angle = 2f * normalAngle - angle - 180f;
angleCos = MathUtils.cosDeg(angle);
angleSin = MathUtils.sinDeg(angle);
velocityX = velocity * angleCos;
velocityY = velocity * angleSin;
}
super.translate(velocityX, velocityY);
}
}
}