/*******************************************************************************
* 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.tests.bullet;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.physics.bullet.collision.Collision;
import com.badlogic.gdx.physics.bullet.collision.btBoxShape;
import com.badlogic.gdx.physics.bullet.dynamics.btDefaultVehicleRaycaster;
import com.badlogic.gdx.physics.bullet.dynamics.btDiscreteDynamicsWorld;
import com.badlogic.gdx.physics.bullet.dynamics.btDynamicsWorld;
import com.badlogic.gdx.physics.bullet.dynamics.btRaycastVehicle;
import com.badlogic.gdx.physics.bullet.dynamics.btRigidBody;
import com.badlogic.gdx.physics.bullet.dynamics.btVehicleRaycaster;
import com.badlogic.gdx.physics.bullet.dynamics.btRaycastVehicle.btVehicleTuning;
import com.badlogic.gdx.physics.bullet.dynamics.btWheelInfo;
/** @author Xoppa */
public class VehicleTest extends BaseBulletTest {
public btVehicleRaycaster raycaster;
public btRaycastVehicle vehicle;
public btVehicleTuning tuning;
BulletEntity chassis;
BulletEntity wheels[] = new BulletEntity[4];
boolean downPressed;
boolean upPressed;
boolean leftPressed;
boolean rightPressed;
Vector3 tmpV = new Vector3();
protected btVehicleRaycaster getRaycaster() {
return new btDefaultVehicleRaycaster((btDynamicsWorld)world.collisionWorld);
}
@Override
public void create () {
super.create();
instructions = "Tap to shoot\nArrow keys to drive\nR to reset\nLong press to toggle debug mode\nSwipe for next test";
final Model chassisModel = objLoader.loadModel(Gdx.files.internal("data/car.obj"));
disposables.add(chassisModel);
chassisModel.materials.get(0).clear();
chassisModel.materials.get(0).set(ColorAttribute.createDiffuse(Color.RED), ColorAttribute.createSpecular(Color.WHITE));
final Model wheelModel = objLoader.loadModel(Gdx.files.internal("data/wheel.obj"));
disposables.add(wheelModel);
wheelModel.materials.get(0).clear();
wheelModel.materials.get(0).set(ColorAttribute.createDiffuse(Color.BLACK), ColorAttribute.createSpecular(Color.WHITE),
FloatAttribute.createShininess(128));
Texture checkboard = new Texture(Gdx.files.internal("data/g3d/checkboard.png"));
final Model largeGroundModel = modelBuilder.createBox(
200f,
2f,
200f,
new Material(TextureAttribute.createDiffuse(checkboard), ColorAttribute.createSpecular(Color.WHITE), FloatAttribute
.createShininess(16f)), Usage.Position | Usage.Normal | Usage.TextureCoordinates);
largeGroundModel.manageDisposable(checkboard);
disposables.add(largeGroundModel);
world.addConstructor("largeground", new BulletConstructor(largeGroundModel, 0f));
BoundingBox bounds = new BoundingBox();
Vector3 chassisHalfExtents = chassisModel.calculateBoundingBox(bounds).getDimensions(new Vector3()).scl(0.5f);
Vector3 wheelHalfExtents = wheelModel.calculateBoundingBox(bounds).getDimensions(new Vector3()).scl(0.5f);
world.addConstructor("chassis", new BulletConstructor(chassisModel, 5f, new btBoxShape(chassisHalfExtents)));
world.addConstructor("wheel", new BulletConstructor(wheelModel, 0, null));
for (int i = 0; i < 1000; i += 200)
world.add("largeground", 0f, -1f, i);
chassis = world.add("chassis", 0, 3f, 0);
wheels[0] = world.add("wheel", 0, 0, 0);
wheels[1] = world.add("wheel", 0, 0, 0);
wheels[2] = world.add("wheel", 0, 0, 0);
wheels[3] = world.add("wheel", 0, 0, 0);
// Create the vehicle
raycaster = getRaycaster();
tuning = new btVehicleTuning();
vehicle = new btRaycastVehicle(tuning, (btRigidBody)chassis.body, raycaster);
chassis.body.setActivationState(Collision.DISABLE_DEACTIVATION);
((btDynamicsWorld)world.collisionWorld).addVehicle(vehicle);
vehicle.setCoordinateSystem(0, 1, 2);
btWheelInfo wheelInfo;
Vector3 point = new Vector3();
Vector3 direction = new Vector3(0, -1, 0);
Vector3 axis = new Vector3(-1, 0, 0);
wheelInfo = vehicle.addWheel(point.set(chassisHalfExtents).scl(0.9f, -0.8f, 0.7f), direction, axis,
wheelHalfExtents.z * 0.3f, wheelHalfExtents.z, tuning, true);
wheelInfo = vehicle.addWheel(point.set(chassisHalfExtents).scl(-0.9f, -0.8f, 0.7f), direction, axis,
wheelHalfExtents.z * 0.3f, wheelHalfExtents.z, tuning, true);
wheelInfo = vehicle.addWheel(point.set(chassisHalfExtents).scl(0.9f, -0.8f, -0.5f), direction, axis,
wheelHalfExtents.z * 0.3f, wheelHalfExtents.z, tuning, false);
wheelInfo = vehicle.addWheel(point.set(chassisHalfExtents).scl(-0.9f, -0.8f, -0.5f), direction, axis,
wheelHalfExtents.z * 0.3f, wheelHalfExtents.z, tuning, false);
}
float maxForce = 100f;
float currentForce = 0f;
float acceleration = 50f; // force/second
float maxAngle = 60f;
float currentAngle = 0f;
float steerSpeed = 45f; // angle/second
@Override
public void update () {
final float delta = Gdx.graphics.getDeltaTime();
float angle = currentAngle;
if (rightPressed) {
if (angle > 0f) angle = 0f;
angle = MathUtils.clamp(angle - steerSpeed * delta, -maxAngle, 0f);
} else if (leftPressed) {
if (angle < 0f) angle = 0f;
angle = MathUtils.clamp(angle + steerSpeed * delta, 0f, maxAngle);
} else
angle = 0f;
if (angle != currentAngle) {
currentAngle = angle;
vehicle.setSteeringValue(angle * MathUtils.degreesToRadians, 0);
vehicle.setSteeringValue(angle * MathUtils.degreesToRadians, 1);
}
float force = currentForce;
if (upPressed) {
if (force < 0f) force = 0f;
force = MathUtils.clamp(force + acceleration * delta, 0f, maxForce);
} else if (downPressed) {
if (force > 0f) force = 0f;
force = MathUtils.clamp(force - acceleration * delta, -maxForce, 0f);
} else
force = 0f;
if (force != currentForce) {
currentForce = force;
vehicle.applyEngineForce(force, 0);
vehicle.applyEngineForce(force, 1);
}
super.update();
for (int i = 0; i < wheels.length; i++) {
vehicle.updateWheelTransform(i, true);
vehicle.getWheelInfo(i).getWorldTransform().getOpenGLMatrix(wheels[i].transform.val);
}
chassis.transform.getTranslation(camera.position);
tmpV.set(camera.position).sub(5, 0, 5).y = 0f;
camera.position.add(tmpV.nor().scl(-6f)).y = 4.f;
chassis.transform.getTranslation(tmpV);
camera.lookAt(tmpV);
camera.up.set(Vector3.Y);
camera.update();
}
@Override
public boolean tap (float x, float y, int count, int button) {
shoot(x, y);
return true;
}
@Override
public void dispose () {
super.dispose();
vehicle.dispose();
vehicle = null;
raycaster.dispose();
raycaster = null;
tuning.dispose();
tuning = null;
}
@Override
public boolean keyDown (int keycode) {
switch (keycode) {
case Keys.DOWN:
downPressed = true;
break;
case Keys.UP:
upPressed = true;
break;
case Keys.LEFT:
leftPressed = true;
break;
case Keys.RIGHT:
rightPressed = true;
break;
}
return super.keyDown(keycode);
}
@Override
public boolean keyUp (int keycode) {
switch (keycode) {
case Keys.DOWN:
downPressed = false;
break;
case Keys.UP:
upPressed = false;
break;
case Keys.LEFT:
leftPressed = false;
break;
case Keys.RIGHT:
rightPressed = false;
break;
case Keys.R:
chassis.body.setWorldTransform(chassis.transform.setToTranslation(0, 5, 0));
chassis.body.setInterpolationWorldTransform(chassis.transform);
((btRigidBody)(chassis.body)).setLinearVelocity(Vector3.Zero);
((btRigidBody)(chassis.body)).setAngularVelocity(Vector3.Zero);
chassis.body.activate();
break;
}
return super.keyUp(keycode);
}
}