-
Notifications
You must be signed in to change notification settings - Fork 707
Bombshell 02 Dynamics
contriteobserver edited this page Oct 2, 2017
·
5 revisions
(Just dumping information here at the moment, this will be more coherent)
There are many ways to move objects in Rajawali scenes, one is the a built in animation system, another way us to move objects via the simulation of rigid body dynamics. Rajawali does not contain a built-in physics engine. So this tutorial will show how to integrate Rajawali with 2D physics simulation along a single plane in a Rajawali scene, where JBox2D provides 2D physics simulation.
To do this we construct a Rajawali Renderer
, a Physics world, and a bridge that links
Dynamic and Kinetic bodies in the Physics World with a corresponding Object3D
in the
renderer.
Before we begin, add the needed components to our build:
compile 'org.rajawali3d:rajawali:1.1.970'
compile 'org.jbox2d:jbox2d-library:2.2.1.1'
class SceneRenderer extends Renderer {
private Bridge bridge;
public SceneRenderer(Context context) {
super(context);
bridge = new Bridge();
}
@Override
protected void initScene() {
getCurrentScene().setBackgroundColor(Color.BLUE);
DirectionalLight key = new DirectionalLight(-3,-4,-5);
key.setPower(1);
getCurrentScene().addLight(key);
bridge.InitKinematics(getCurrentScene());
bridge.InitDynamics(getCurrentScene());
getCurrentCamera().setPosition(14,10,24);
getCurrentCamera().setLookAt(7,5,0);
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset) {
}
@Override
public void onTouchEvent(MotionEvent event) {
resetRequested = true;
}
@Override
protected void onRender(long ellapsedRealtime, double deltaTime) {
super.onRender(ellapsedRealtime, deltaTime);
bridge.Update();
if(resetRequested) {
bridge.ClearDynamics(getCurrentScene());
bridge.InitDynamics(getCurrentScene());
resetRequested = false;
} else {
bridge.SyncDynamics(getCurrentScene());
bridge.PruneDynamics(getCurrentScene());
}
}
}
class Dynamics2D {
private World b2World;
static private final int VELOCITY_ITERATIONS = 8;
static private final int POSITION_ITERATIONS = 3;
private enum ObjectType
{
Box,
Floor,
Ball
}
Dynamics2D()
{
b2World = new World(new Vec2(0.0f,-9.81f));
}
void Step(float delta)
{
b2World.step(delta, VELOCITY_ITERATIONS, POSITION_ITERATIONS);
}
void destroyBody(Body body) {
b2World.destroyBody(body);
}
Body CreateBox(Vec2 position, @Nullable Vec2 velocity)
{
Vec2 v = velocity == null ? new Vec2() : velocity;
BodyDef bodyDef = new BodyDef();
bodyDef.position = position;
bodyDef.angle = (float)Math.PI/4;
bodyDef.linearVelocity = v;
bodyDef.angularVelocity = 0.0f;
bodyDef.fixedRotation = false;
bodyDef.active = true;
bodyDef.bullet = false;
bodyDef.allowSleep = true;
bodyDef.gravityScale = 1.0f;
bodyDef.linearDamping = 0.0f;
bodyDef.angularDamping = 0.0f;
bodyDef.userData = ObjectType.Box;
bodyDef.type = BodyType.DYNAMIC;
PolygonShape shape = new PolygonShape();
shape.setAsBox(1, 1);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.userData = null;
fixtureDef.friction = 0.35f;
fixtureDef.restitution = 0.05f;
fixtureDef.density = 1f;
fixtureDef.isSensor = false;
Body body = b2World.createBody(bodyDef);
body.createFixture(fixtureDef);
return body;
}
Body CreateBall(Vec2 position, @Nullable Vec2 velocity)
{
Vec2 v = velocity == null ? new Vec2() : velocity;
BodyDef bodyDef = new BodyDef();
bodyDef.position = position;
bodyDef.angle = 0f;
bodyDef.linearVelocity = v;
bodyDef.angularVelocity = 0.0f;
bodyDef.fixedRotation = false;
bodyDef.active = true;
bodyDef.bullet = false;
bodyDef.allowSleep = true;
bodyDef.gravityScale = 1.0f;
bodyDef.linearDamping = 0.0f;
bodyDef.angularDamping = 0.0f;
bodyDef.userData = ObjectType.Ball;
bodyDef.type = BodyType.DYNAMIC;
CircleShape shape = new CircleShape();
shape.setRadius(1);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.userData = null;
fixtureDef.friction = 0.45f;
fixtureDef.restitution = 0.75f;
fixtureDef.density = 2.0f;
fixtureDef.isSensor = false;
Body body = b2World.createBody(bodyDef);
body.createFixture(fixtureDef);
return body;
}
Body CreateBarrier(Vec2 position, float width, float height)
{
BodyDef bodyDef = new BodyDef();
bodyDef.position = position;
bodyDef.angle = 0f;
bodyDef.linearVelocity = new Vec2(0.0f,0.0f);
bodyDef.angularVelocity = 0.0f;
bodyDef.fixedRotation = false;
bodyDef.active = true;
bodyDef.bullet = false;
bodyDef.allowSleep = true;
bodyDef.gravityScale = 1.0f;
bodyDef.linearDamping = 0.0f;
bodyDef.angularDamping = 0.0f;
bodyDef.userData = ObjectType.Floor;
bodyDef.type = BodyType.KINEMATIC;
PolygonShape shape = new PolygonShape();
shape.setAsBox(width/2f, height/2f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.userData = null;
fixtureDef.friction = 0.5f;
fixtureDef.restitution = 0.05f;
fixtureDef.density = 1.0f;
fixtureDef.isSensor = false;
Body body = b2World.createBody(bodyDef);
body.createFixture(fixtureDef);
return body;
}
}
class Bridge {
private HashMap<Body, Object3D> map;
private Dynamics2D dynamics2D;
private IDiffuseMethod lambert;
private long mLastRender;
Bridge() {
dynamics2D = new Dynamics2D();
lambert = new DiffuseMethod.Lambert();
map = new HashMap<>();
}
void Update() {
final long currentTime = System.nanoTime();
final float delta = (currentTime - mLastRender) / 1e9f;
mLastRender = currentTime;
dynamics2D.Step(delta);
}
void InitKinematics(Scene scene) {
Body floor = dynamics2D.CreateBarrier(new Vec2(7, 0), 10, 2);
Material gray = new Material();
gray.setColor(Color.LTGRAY);
gray.setAmbientColor(Color.GRAY);
gray.setDiffuseMethod(lambert);
gray.enableLighting(true);
Object3D floorPrism = new RectangularPrism(10,2,3);
floorPrism.setPosition(floor.getPosition().x, floor.getPosition().y, 0);
floorPrism.setMaterial(gray);
//floorPrism.setDrawingMode(GLES20.GL_LINES);
scene.addChild(floorPrism);
}
void InitDynamics(Scene scene) {
Body ball = dynamics2D.CreateBall(new Vec2(9.1f,15), null);
Material cyan = new Material();
cyan.setColor(Color.CYAN);
cyan.setAmbientColor(Color.GRAY);
cyan.setDiffuseMethod(lambert);
cyan.enableLighting(true);
Object3D sphere = new Sphere(1,12,12);
sphere.setPosition(ball.getPosition().x, ball.getPosition().y, 0);
sphere.setRotation(Vector3.NEG_Z, toDegrees(ball.getAngle()));
sphere.setMaterial(cyan);
//sphere.setDrawingMode(GLES20.GL_LINES);
scene.addChild(sphere);
map.put(ball, sphere);
Body box = dynamics2D.CreateBox(new Vec2(9.3f,12), null);
Material yellow = new Material();
yellow.setColor(Color.YELLOW);
yellow.setAmbientColor(Color.GRAY);
yellow.setDiffuseMethod(lambert);
yellow.enableLighting(true);
Object3D cube = new Cube(2);
cube.setPosition(box.getPosition().x, box.getPosition().y, 0);
cube.setRotation(Vector3.NEG_Z, toDegrees(box.getAngle()));
cube.setMaterial(yellow);
//cube.setDrawingMode(GLES20.GL_LINES);
scene.addChild(cube);
map.put(box, cube);
}
void ClearDynamics(Scene scene) {
for(Iterator<Map.Entry<Body, Object3D>> it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<Body, Object3D> entry = it.next();
physics2D.destroyBody(entry.getKey());
scene.removeChild(entry.getValue());
it.remove();
}
}
void SyncDynamics(Scene scene) {
for (Map.Entry<Body, Object3D> entry: map.entrySet()) {
Body body = entry.getKey();
Object3D object3D = entry.getValue();
object3D.setPosition(body.getPosition().x, body.getPosition().y, 0);
object3D.setRotation(Vector3.NEG_Z, toDegrees(body.getAngle()));
}
}
void PruneDynamics(Scene scene) {
for(Iterator<Map.Entry<Body, Object3D>> it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<Body, Object3D> entry = it.next();
if(entry.getKey().getPosition().y < -16) {
physics2D.destroyBody(entry.getKey());
scene.removeChild(entry.getValue());
it.remove();
}
}
}
}
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP) {
renderer.onTouchEvent(event);
}
return super.onTouchEvent(event);
}