1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.tests.bullet; 18 19 import com.badlogic.gdx.Gdx; 20 import com.badlogic.gdx.Input.Keys; 21 import com.badlogic.gdx.graphics.Color; 22 import com.badlogic.gdx.graphics.Texture; 23 import com.badlogic.gdx.graphics.VertexAttributes.Usage; 24 import com.badlogic.gdx.graphics.g3d.Material; 25 import com.badlogic.gdx.graphics.g3d.Model; 26 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; 27 import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute; 28 import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute; 29 import com.badlogic.gdx.math.MathUtils; 30 import com.badlogic.gdx.math.Vector3; 31 import com.badlogic.gdx.math.collision.BoundingBox; 32 import com.badlogic.gdx.physics.bullet.collision.Collision; 33 import com.badlogic.gdx.physics.bullet.collision.btBoxShape; 34 import com.badlogic.gdx.physics.bullet.dynamics.btDefaultVehicleRaycaster; 35 import com.badlogic.gdx.physics.bullet.dynamics.btDiscreteDynamicsWorld; 36 import com.badlogic.gdx.physics.bullet.dynamics.btDynamicsWorld; 37 import com.badlogic.gdx.physics.bullet.dynamics.btRaycastVehicle; 38 import com.badlogic.gdx.physics.bullet.dynamics.btRigidBody; 39 import com.badlogic.gdx.physics.bullet.dynamics.btVehicleRaycaster; 40 import com.badlogic.gdx.physics.bullet.dynamics.btRaycastVehicle.btVehicleTuning; 41 import com.badlogic.gdx.physics.bullet.dynamics.btWheelInfo; 42 43 /** @author Xoppa */ 44 public class VehicleTest extends BaseBulletTest { 45 public btVehicleRaycaster raycaster; 46 public btRaycastVehicle vehicle; 47 public btVehicleTuning tuning; 48 BulletEntity chassis; 49 BulletEntity wheels[] = new BulletEntity[4]; 50 51 boolean downPressed; 52 boolean upPressed; 53 boolean leftPressed; 54 boolean rightPressed; 55 Vector3 tmpV = new Vector3(); 56 57 @Override create()58 public void create () { 59 super.create(); 60 instructions = "Tap to shoot\nArrow keys to drive\nR to reset\nLong press to toggle debug mode\nSwipe for next test"; 61 62 final Model chassisModel = objLoader.loadModel(Gdx.files.internal("data/car.obj")); 63 disposables.add(chassisModel); 64 chassisModel.materials.get(0).clear(); 65 chassisModel.materials.get(0).set(ColorAttribute.createDiffuse(Color.RED), ColorAttribute.createSpecular(Color.WHITE)); 66 final Model wheelModel = objLoader.loadModel(Gdx.files.internal("data/wheel.obj")); 67 disposables.add(wheelModel); 68 wheelModel.materials.get(0).clear(); 69 wheelModel.materials.get(0).set(ColorAttribute.createDiffuse(Color.BLACK), ColorAttribute.createSpecular(Color.WHITE), 70 FloatAttribute.createShininess(128)); 71 Texture checkboard = new Texture(Gdx.files.internal("data/g3d/checkboard.png")); 72 final Model largeGroundModel = modelBuilder.createBox( 73 1000f, 74 2f, 75 1000f, 76 new Material(TextureAttribute.createDiffuse(checkboard), ColorAttribute.createSpecular(Color.WHITE), FloatAttribute 77 .createShininess(16f)), Usage.Position | Usage.Normal | Usage.TextureCoordinates); 78 largeGroundModel.manageDisposable(checkboard); 79 disposables.add(largeGroundModel); 80 world.addConstructor("largeground", new BulletConstructor(largeGroundModel, 0f)); 81 82 BoundingBox bounds = new BoundingBox(); 83 Vector3 chassisHalfExtents = chassisModel.calculateBoundingBox(bounds).getDimensions(new Vector3()).scl(0.5f); 84 Vector3 wheelHalfExtents = wheelModel.calculateBoundingBox(bounds).getDimensions(new Vector3()).scl(0.5f); 85 86 world.addConstructor("chassis", new BulletConstructor(chassisModel, 5f, new btBoxShape(chassisHalfExtents))); 87 world.addConstructor("wheel", new BulletConstructor(wheelModel, 0, null)); 88 89 world.add("largeground", 0, -1f, 0f); 90 91 chassis = world.add("chassis", 0, 3f, 0); 92 wheels[0] = world.add("wheel", 0, 0, 0); 93 wheels[1] = world.add("wheel", 0, 0, 0); 94 wheels[2] = world.add("wheel", 0, 0, 0); 95 wheels[3] = world.add("wheel", 0, 0, 0); 96 97 // Create the vehicle 98 raycaster = new btDefaultVehicleRaycaster((btDynamicsWorld)world.collisionWorld); 99 tuning = new btVehicleTuning(); 100 vehicle = new btRaycastVehicle(tuning, (btRigidBody)chassis.body, raycaster); 101 chassis.body.setActivationState(Collision.DISABLE_DEACTIVATION); 102 ((btDynamicsWorld)world.collisionWorld).addVehicle(vehicle); 103 104 vehicle.setCoordinateSystem(0, 1, 2); 105 106 btWheelInfo wheelInfo; 107 Vector3 point = new Vector3(); 108 Vector3 direction = new Vector3(0, -1, 0); 109 Vector3 axis = new Vector3(-1, 0, 0); 110 wheelInfo = vehicle.addWheel(point.set(chassisHalfExtents).scl(0.9f, -0.8f, 0.7f), direction, axis, 111 wheelHalfExtents.z * 0.3f, wheelHalfExtents.z, tuning, true); 112 wheelInfo = vehicle.addWheel(point.set(chassisHalfExtents).scl(-0.9f, -0.8f, 0.7f), direction, axis, 113 wheelHalfExtents.z * 0.3f, wheelHalfExtents.z, tuning, true); 114 wheelInfo = vehicle.addWheel(point.set(chassisHalfExtents).scl(0.9f, -0.8f, -0.5f), direction, axis, 115 wheelHalfExtents.z * 0.3f, wheelHalfExtents.z, tuning, false); 116 wheelInfo = vehicle.addWheel(point.set(chassisHalfExtents).scl(-0.9f, -0.8f, -0.5f), direction, axis, 117 wheelHalfExtents.z * 0.3f, wheelHalfExtents.z, tuning, false); 118 } 119 120 float maxForce = 100f; 121 float currentForce = 0f; 122 float acceleration = 50f; // force/second 123 float maxAngle = 60f; 124 float currentAngle = 0f; 125 float steerSpeed = 45f; // angle/second 126 127 @Override update()128 public void update () { 129 final float delta = Gdx.graphics.getDeltaTime(); 130 float angle = currentAngle; 131 if (rightPressed) { 132 if (angle > 0f) angle = 0f; 133 angle = MathUtils.clamp(angle - steerSpeed * delta, -maxAngle, 0f); 134 } else if (leftPressed) { 135 if (angle < 0f) angle = 0f; 136 angle = MathUtils.clamp(angle + steerSpeed * delta, 0f, maxAngle); 137 } else 138 angle = 0f; 139 if (angle != currentAngle) { 140 currentAngle = angle; 141 vehicle.setSteeringValue(angle * MathUtils.degreesToRadians, 0); 142 vehicle.setSteeringValue(angle * MathUtils.degreesToRadians, 1); 143 } 144 145 float force = currentForce; 146 if (upPressed) { 147 if (force < 0f) force = 0f; 148 force = MathUtils.clamp(force + acceleration * delta, 0f, maxForce); 149 } else if (downPressed) { 150 if (force > 0f) force = 0f; 151 force = MathUtils.clamp(force - acceleration * delta, -maxForce, 0f); 152 } else 153 force = 0f; 154 if (force != currentForce) { 155 currentForce = force; 156 vehicle.applyEngineForce(force, 0); 157 vehicle.applyEngineForce(force, 1); 158 } 159 160 super.update(); 161 162 for (int i = 0; i < wheels.length; i++) { 163 vehicle.updateWheelTransform(i, true); 164 vehicle.getWheelInfo(i).getWorldTransform().getOpenGLMatrix(wheels[i].transform.val); 165 } 166 167 chassis.transform.getTranslation(camera.position); 168 tmpV.set(camera.position).sub(5, 0, 5).y = 0f; 169 camera.position.add(tmpV.nor().scl(-6f)).y = 4.f; 170 chassis.transform.getTranslation(tmpV); 171 camera.lookAt(tmpV); 172 camera.up.set(Vector3.Y); 173 camera.update(); 174 } 175 176 @Override tap(float x, float y, int count, int button)177 public boolean tap (float x, float y, int count, int button) { 178 shoot(x, y); 179 return true; 180 } 181 182 @Override dispose()183 public void dispose () { 184 super.dispose(); 185 vehicle.dispose(); 186 vehicle = null; 187 raycaster.dispose(); 188 raycaster = null; 189 tuning.dispose(); 190 tuning = null; 191 } 192 193 @Override keyDown(int keycode)194 public boolean keyDown (int keycode) { 195 switch (keycode) { 196 case Keys.DOWN: 197 downPressed = true; 198 break; 199 case Keys.UP: 200 upPressed = true; 201 break; 202 case Keys.LEFT: 203 leftPressed = true; 204 break; 205 case Keys.RIGHT: 206 rightPressed = true; 207 break; 208 } 209 return super.keyDown(keycode); 210 } 211 212 @Override keyUp(int keycode)213 public boolean keyUp (int keycode) { 214 switch (keycode) { 215 case Keys.DOWN: 216 downPressed = false; 217 break; 218 case Keys.UP: 219 upPressed = false; 220 break; 221 case Keys.LEFT: 222 leftPressed = false; 223 break; 224 case Keys.RIGHT: 225 rightPressed = false; 226 break; 227 case Keys.R: 228 chassis.body.setWorldTransform(chassis.transform.setToTranslation(0, 5, 0)); 229 chassis.body.setInterpolationWorldTransform(chassis.transform); 230 ((btRigidBody)(chassis.body)).setLinearVelocity(Vector3.Zero); 231 ((btRigidBody)(chassis.body)).setAngularVelocity(Vector3.Zero); 232 chassis.body.activate(); 233 break; 234 } 235 return super.keyUp(keycode); 236 } 237 } 238