1package com.badlogic.gdx.tests.bullet;
2
3import com.badlogic.gdx.Gdx;
4import com.badlogic.gdx.Input.Keys;
5import com.badlogic.gdx.graphics.Texture;
6import com.badlogic.gdx.graphics.VertexAttributes.Usage;
7import com.badlogic.gdx.graphics.g3d.Material;
8import com.badlogic.gdx.graphics.g3d.Model;
9import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
10import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute;
11import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
12import com.badlogic.gdx.math.Matrix4;
13import com.badlogic.gdx.math.Vector3;
14import com.badlogic.gdx.physics.bullet.collision.btAxisSweep3;
15import com.badlogic.gdx.physics.bullet.collision.btBroadphaseProxy;
16import com.badlogic.gdx.physics.bullet.collision.btCapsuleShape;
17import com.badlogic.gdx.physics.bullet.collision.btCollisionDispatcher;
18import com.badlogic.gdx.physics.bullet.collision.btCollisionObject;
19import com.badlogic.gdx.physics.bullet.collision.btConvexShape;
20import com.badlogic.gdx.physics.bullet.collision.btDbvtBroadphase;
21import com.badlogic.gdx.physics.bullet.collision.btDefaultCollisionConfiguration;
22import com.badlogic.gdx.physics.bullet.collision.btGhostPairCallback;
23import com.badlogic.gdx.physics.bullet.collision.btPairCachingGhostObject;
24import com.badlogic.gdx.physics.bullet.dynamics.btDiscreteDynamicsWorld;
25import com.badlogic.gdx.physics.bullet.dynamics.btDynamicsWorld;
26import com.badlogic.gdx.physics.bullet.dynamics.btKinematicCharacterController;
27import com.badlogic.gdx.physics.bullet.dynamics.btSequentialImpulseConstraintSolver;
28
29public class CharacterTest extends BaseBulletTest {
30	final int BOXCOUNT_X = 5;
31	final int BOXCOUNT_Y = 5;
32	final int BOXCOUNT_Z = 1;
33
34	final float BOXOFFSET_X = -2.5f;
35	final float BOXOFFSET_Y = 0.5f;
36	final float BOXOFFSET_Z = 0f;
37
38	BulletEntity ground;
39	BulletEntity character;
40
41	btGhostPairCallback ghostPairCallback;
42	btPairCachingGhostObject ghostObject;
43	btConvexShape ghostShape;
44	btKinematicCharacterController characterController;
45	Matrix4 characterTransform;
46	Vector3 characterDirection = new Vector3();
47	Vector3 walkDirection = new Vector3();
48
49	@Override
50	public BulletWorld createWorld () {
51		// We create the world using an axis sweep broadphase for this test
52		btDefaultCollisionConfiguration collisionConfiguration = new btDefaultCollisionConfiguration();
53		btCollisionDispatcher dispatcher = new btCollisionDispatcher(collisionConfiguration);
54		btAxisSweep3 sweep = new btAxisSweep3(new Vector3(-1000, -1000, -1000), new Vector3(1000, 1000, 1000));
55		btSequentialImpulseConstraintSolver solver = new btSequentialImpulseConstraintSolver();
56		btDiscreteDynamicsWorld collisionWorld = new btDiscreteDynamicsWorld(dispatcher, sweep, solver, collisionConfiguration);
57		ghostPairCallback = new btGhostPairCallback();
58		sweep.getOverlappingPairCache().setInternalGhostPairCallback(ghostPairCallback);
59		return new BulletWorld(collisionConfiguration, dispatcher, sweep, solver, collisionWorld);
60	}
61
62	@Override
63	public void create () {
64		super.create();
65		instructions = "Tap to shoot\nArrow keys to move\nR to reset\nLong press to toggle debug mode\nSwipe for next test";
66
67		// Create a visual representation of the character (note that we don't use the physics part of BulletEntity, we'll do that manually)
68		final Texture texture = new Texture(Gdx.files.internal("data/badlogic.jpg"));
69		disposables.add(texture);
70		final Material material = new Material(TextureAttribute.createDiffuse(texture), ColorAttribute.createSpecular(1,1,1,1), FloatAttribute.createShininess(8f));
71		final long attributes = Usage.Position | Usage.Normal | Usage.TextureCoordinates;
72		final Model capsule = modelBuilder.createCapsule(2f, 6f, 16, material, attributes);
73		disposables.add(capsule);
74		world.addConstructor("capsule", new BulletConstructor(capsule, null));
75		character = world.add("capsule", 5f, 3f, 5f);
76		characterTransform = character.transform; // Set by reference
77
78		// Create the physics representation of the character
79		ghostObject = new btPairCachingGhostObject();
80		ghostObject.setWorldTransform(characterTransform);
81		ghostShape = new btCapsuleShape(2f, 2f);
82		ghostObject.setCollisionShape(ghostShape);
83		ghostObject.setCollisionFlags(btCollisionObject.CollisionFlags.CF_CHARACTER_OBJECT);
84		characterController = new btKinematicCharacterController(ghostObject, ghostShape, .35f);
85
86		// And add it to the physics world
87		world.collisionWorld.addCollisionObject(ghostObject,
88			(short)btBroadphaseProxy.CollisionFilterGroups.CharacterFilter,
89			(short)(btBroadphaseProxy.CollisionFilterGroups.StaticFilter | btBroadphaseProxy.CollisionFilterGroups.DefaultFilter));
90		((btDiscreteDynamicsWorld)(world.collisionWorld)).addAction(characterController);
91
92		// Add the ground
93		(ground = world.add("ground", 0f, 0f, 0f))
94			.setColor(0.25f + 0.5f * (float)Math.random(), 0.25f + 0.5f * (float)Math.random(), 0.25f + 0.5f * (float)Math.random(), 1f);
95		// Create some boxes to play with
96		for (int x = 0; x < BOXCOUNT_X; x++) {
97			for (int y = 0; y < BOXCOUNT_Y; y++) {
98				for (int z = 0; z < BOXCOUNT_Z; z++) {
99					world.add("box", BOXOFFSET_X + x, BOXOFFSET_Y + y, BOXOFFSET_Z + z)
100						.setColor(0.5f + 0.5f * (float)Math.random(), 0.5f + 0.5f * (float)Math.random(), 0.5f + 0.5f * (float)Math.random(), 1f);
101				}
102			}
103		}
104	}
105
106	@Override
107	public void update () {
108		// If the left or right key is pressed, rotate the character and update its physics update accordingly.
109		if (Gdx.input.isKeyPressed(Keys.LEFT)) {
110			characterTransform.rotate(0, 1, 0, 5f);
111			ghostObject.setWorldTransform(characterTransform);
112		}
113		if (Gdx.input.isKeyPressed(Keys.RIGHT)) {
114			characterTransform.rotate(0, 1, 0, -5f);
115			ghostObject.setWorldTransform(characterTransform);
116		}
117		// Fetch which direction the character is facing now
118		characterDirection.set(-1,0,0).rot(characterTransform).nor();
119		// Set the walking direction accordingly (either forward or backward)
120		walkDirection.set(0,0,0);
121		if (Gdx.input.isKeyPressed(Keys.UP))
122			walkDirection.add(characterDirection);
123		if (Gdx.input.isKeyPressed(Keys.DOWN))
124			walkDirection.add(-characterDirection.x, -characterDirection.y, -characterDirection.z);
125		walkDirection.scl(4f * Gdx.graphics.getDeltaTime());
126		// And update the character controller
127		characterController.setWalkDirection(walkDirection);
128		// Now we can update the world as normally
129		super.update();
130		// And fetch the new transformation of the character (this will make the model be rendered correctly)
131		ghostObject.getWorldTransform(characterTransform);
132	}
133
134	@Override
135	protected void renderWorld () {
136		// TODO Auto-generated method stub
137		super.renderWorld();
138	}
139
140	@Override
141	public boolean tap (float x, float y, int count, int button) {
142		shoot(x, y);
143		return true;
144	}
145
146	@Override
147	public void dispose () {
148		((btDiscreteDynamicsWorld)(world.collisionWorld)).removeAction(characterController);
149		world.collisionWorld.removeCollisionObject(ghostObject);
150		super.dispose();
151		characterController.dispose();
152		ghostObject.dispose();
153		ghostShape.dispose();
154		ghostPairCallback.dispose();
155		ground = null;
156	}
157}
158