• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Input.Buttons;
20 import com.badlogic.gdx.graphics.Color;
21 import com.badlogic.gdx.graphics.VertexAttributes.Usage;
22 import com.badlogic.gdx.graphics.g3d.Material;
23 import com.badlogic.gdx.graphics.g3d.Model;
24 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
25 import com.badlogic.gdx.math.MathUtils;
26 import com.badlogic.gdx.math.Matrix4;
27 import com.badlogic.gdx.math.Vector3;
28 import com.badlogic.gdx.math.collision.Ray;
29 import com.badlogic.gdx.physics.bullet.collision.Collision;
30 import com.badlogic.gdx.physics.bullet.collision.btCapsuleShape;
31 import com.badlogic.gdx.physics.bullet.collision.ClosestRayResultCallback;
32 import com.badlogic.gdx.physics.bullet.dynamics.btConeTwistConstraint;
33 import com.badlogic.gdx.physics.bullet.dynamics.btConstraintSetting;
34 import com.badlogic.gdx.physics.bullet.dynamics.btDynamicsWorld;
35 import com.badlogic.gdx.physics.bullet.dynamics.btHingeConstraint;
36 import com.badlogic.gdx.physics.bullet.dynamics.btPoint2PointConstraint;
37 import com.badlogic.gdx.physics.bullet.dynamics.btRigidBody;
38 import com.badlogic.gdx.physics.bullet.dynamics.btTypedConstraint;
39 import com.badlogic.gdx.utils.Array;
40 
41 /** @author xoppa */
42 public class RayPickRagdollTest extends BaseBulletTest {
43 
44 	final Array<btTypedConstraint> constraints = new Array<btTypedConstraint>();
45 	btPoint2PointConstraint pickConstraint = null;
46 	btRigidBody pickedBody = null;
47 	float pickDistance;
48 	Vector3 tmpV = new Vector3();
49 
50 	@Override
create()51 	public void create () {
52 		super.create();
53 		instructions = "Tap to shoot\nDrag ragdoll to pick\nLong press to toggle debug mode\nSwipe for next test\nCtrl+drag to rotate\nScroll to zoom";
54 
55 		camera.position.set(4f, 2f, 4f);
56 		camera.lookAt(0f, 1f, 0f);
57 		camera.update();
58 
59 		world.addConstructor("pelvis", new BulletConstructor(createCapsuleModel(0.15f, 0.2f), 1f, new btCapsuleShape(0.15f, 0.2f)));
60 		world
61 			.addConstructor("spine", new BulletConstructor(createCapsuleModel(0.15f, 0.28f), 1f, new btCapsuleShape(0.15f, 0.28f)));
62 		world.addConstructor("head", new BulletConstructor(createCapsuleModel(0.1f, 0.05f), 1f, new btCapsuleShape(0.1f, 0.05f)));
63 		world.addConstructor("upperleg", new BulletConstructor(createCapsuleModel(0.07f, 0.45f), 1f, new btCapsuleShape(0.07f,
64 			0.45f)));
65 		world.addConstructor("lowerleg", new BulletConstructor(createCapsuleModel(0.05f, 0.37f), 1f, new btCapsuleShape(0.05f,
66 			0.37f)));
67 		world.addConstructor("upperarm", new BulletConstructor(createCapsuleModel(0.05f, 0.33f), 1f, new btCapsuleShape(0.05f,
68 			0.33f)));
69 		world.addConstructor("lowerarm", new BulletConstructor(createCapsuleModel(0.04f, 0.25f), 1f, new btCapsuleShape(0.04f,
70 			0.25f)));
71 
72 		world.add("ground", 0f, 0f, 0f).setColor(0.25f + 0.5f * (float)Math.random(), 0.25f + 0.5f * (float)Math.random(),
73 			0.25f + 0.5f * (float)Math.random(), 1f);
74 
75 		addRagdoll(0, 3f, 0);
76 		addRagdoll(1f, 6f, 0);
77 		addRagdoll(-1f, 12f, 0);
78 	}
79 
80 	@Override
dispose()81 	public void dispose () {
82 		for (int i = 0; i < constraints.size; i++) {
83 			((btDynamicsWorld)world.collisionWorld).removeConstraint(constraints.get(i));
84 			constraints.get(i).dispose();
85 		}
86 		constraints.clear();
87 		super.dispose();
88 	}
89 
90 	@Override
touchDown(int screenX, int screenY, int pointer, int button)91 	public boolean touchDown (int screenX, int screenY, int pointer, int button) {
92 		boolean result = false;
93 		if (button == Buttons.LEFT) {
94 			Ray ray = camera.getPickRay(screenX, screenY);
95 			tmpV1.set(ray.direction).scl(10f).add(ray.origin);
96 			ClosestRayResultCallback cb = new ClosestRayResultCallback(ray.origin, tmpV1);
97 			world.collisionWorld.rayTest(ray.origin, tmpV1, cb);
98 			if (cb.hasHit()) {
99 				btRigidBody body = (btRigidBody)(cb.getCollisionObject());
100 				if (body != null && !body.isStaticObject() && !body.isKinematicObject()) {
101 					pickedBody = body;
102 					body.setActivationState(Collision.DISABLE_DEACTIVATION);
103 
104 					cb.getHitPointWorld(tmpV);
105 					tmpV.mul(body.getCenterOfMassTransform().inv());
106 
107 					pickConstraint = new btPoint2PointConstraint(body, tmpV);
108 					btConstraintSetting setting = pickConstraint.getSetting();
109 					setting.setImpulseClamp(30f);
110 					setting.setTau(0.001f);
111 					pickConstraint.setSetting(setting);
112 
113 					((btDynamicsWorld)world.collisionWorld).addConstraint(pickConstraint);
114 
115 					pickDistance = tmpV1.sub(camera.position).len();
116 					result = true;
117 				}
118 			}
119 			cb.dispose();
120 		}
121 		return result ? result : super.touchDown(screenX, screenY, pointer, button);
122 	}
123 
124 	@Override
touchUp(int screenX, int screenY, int pointer, int button)125 	public boolean touchUp (int screenX, int screenY, int pointer, int button) {
126 		boolean result = false;
127 		if (button == Buttons.LEFT) {
128 			if (pickConstraint != null) {
129 				((btDynamicsWorld)world.collisionWorld).removeConstraint(pickConstraint);
130 				pickConstraint.dispose();
131 				pickConstraint = null;
132 				result = true;
133 			}
134 			if (pickedBody != null) {
135 				pickedBody.forceActivationState(Collision.ACTIVE_TAG);
136 				pickedBody.setDeactivationTime(0f);
137 				pickedBody = null;
138 			}
139 		}
140 		return result ? result : super.touchUp(screenX, screenY, pointer, button);
141 	}
142 
143 	@Override
touchDragged(int screenX, int screenY, int pointer)144 	public boolean touchDragged (int screenX, int screenY, int pointer) {
145 		boolean result = false;
146 		if (pickConstraint != null) {
147 			Ray ray = camera.getPickRay(screenX, screenY);
148 			tmpV1.set(ray.direction).scl(pickDistance).add(camera.position);
149 			pickConstraint.setPivotB(tmpV1);
150 			result = true;
151 		}
152 		return result ? result : super.touchDragged(screenX, screenY, pointer);
153 	}
154 
155 	@Override
tap(float x, float y, int count, int button)156 	public boolean tap (float x, float y, int count, int button) {
157 		shoot(x, y);
158 		return true;
159 	}
160 
161 	final static float PI = MathUtils.PI;
162 	final static float PI2 = 0.5f * PI;
163 	final static float PI4 = 0.25f * PI;
164 
addRagdoll(final float x, final float y, final float z)165 	public void addRagdoll (final float x, final float y, final float z) {
166 		final Matrix4 tmpM = new Matrix4();
167 		btRigidBody pelvis = (btRigidBody)world.add("pelvis", x, y + 1, z).body;
168 		btRigidBody spine = (btRigidBody)world.add("spine", x, y + 1.2f, z).body;
169 		btRigidBody head = (btRigidBody)world.add("head", x, y + 1.6f, z).body;
170 		btRigidBody leftupperleg = (btRigidBody)world.add("upperleg", x - 0.18f, y + 0.65f, z).body;
171 		btRigidBody leftlowerleg = (btRigidBody)world.add("lowerleg", x - 0.18f, y + 0.2f, z).body;
172 		btRigidBody rightupperleg = (btRigidBody)world.add("upperleg", x + 0.18f, y + 0.65f, z).body;
173 		btRigidBody rightlowerleg = (btRigidBody)world.add("lowerleg", x + 0.18f, y + 0.2f, z).body;
174 		btRigidBody leftupperarm = (btRigidBody)world.add("upperarm",
175 			tmpM.setFromEulerAnglesRad(PI2, 0, 0).trn(x - 0.35f, y + 1.45f, z)).body;
176 		btRigidBody leftlowerarm = (btRigidBody)world.add("lowerarm", tmpM.setFromEulerAnglesRad(PI2, 0, 0)
177 			.trn(x - 0.7f, y + 1.45f, z)).body;
178 		btRigidBody rightupperarm = (btRigidBody)world.add("upperarm",
179 			tmpM.setFromEulerAnglesRad(-PI2, 0, 0).trn(x + 0.35f, y + 1.45f, z)).body;
180 		btRigidBody rightlowerarm = (btRigidBody)world.add("lowerarm",
181 			tmpM.setFromEulerAnglesRad(-PI2, 0, 0).trn(x + 0.7f, y + 1.45f, z)).body;
182 
183 		final Matrix4 localA = new Matrix4();
184 		final Matrix4 localB = new Matrix4();
185 		btHingeConstraint hingeC = null;
186 		btConeTwistConstraint coneC = null;
187 
188 		// PelvisSpine
189 		localA.setFromEulerAnglesRad(0, PI2, 0).trn(0, 0.15f, 0);
190 		localB.setFromEulerAnglesRad(0, PI2, 0).trn(0, -0.15f, 0);
191 		constraints.add(hingeC = new btHingeConstraint(pelvis, spine, localA, localB));
192 		hingeC.setLimit(-PI4, PI2);
193 		((btDynamicsWorld)world.collisionWorld).addConstraint(hingeC, true);
194 
195 		// SpineHead
196 		localA.setFromEulerAnglesRad(PI2, 0, 0).trn(0, 0.3f, 0);
197 		localB.setFromEulerAnglesRad(PI2, 0, 0).trn(0, -0.14f, 0);
198 		constraints.add(coneC = new btConeTwistConstraint(spine, head, localA, localB));
199 		coneC.setLimit(PI4, PI4, PI2);
200 		((btDynamicsWorld)world.collisionWorld).addConstraint(coneC, true);
201 
202 		// LeftHip
203 		localA.setFromEulerAnglesRad(-PI4 * 5f, 0, 0).trn(-0.18f, -0.1f, 0);
204 		localB.setFromEulerAnglesRad(-PI4 * 5f, 0, 0).trn(0, 0.225f, 0);
205 		constraints.add(coneC = new btConeTwistConstraint(pelvis, leftupperleg, localA, localB));
206 		coneC.setLimit(PI4, PI4, 0);
207 		((btDynamicsWorld)world.collisionWorld).addConstraint(coneC, true);
208 
209 		// LeftKnee
210 		localA.setFromEulerAnglesRad(0, PI2, 0).trn(0, -0.225f, 0);
211 		localB.setFromEulerAnglesRad(0, PI2, 0).trn(0, 0.185f, 0);
212 		constraints.add(hingeC = new btHingeConstraint(leftupperleg, leftlowerleg, localA, localB));
213 		hingeC.setLimit(0, PI2);
214 		((btDynamicsWorld)world.collisionWorld).addConstraint(hingeC, true);
215 
216 		// RightHip
217 		localA.setFromEulerAnglesRad(-PI4 * 5f, 0, 0).trn(0.18f, -0.1f, 0);
218 		localB.setFromEulerAnglesRad(-PI4 * 5f, 0, 0).trn(0, 0.225f, 0);
219 		constraints.add(coneC = new btConeTwistConstraint(pelvis, rightupperleg, localA, localB));
220 		coneC.setLimit(PI4, PI4, 0);
221 		((btDynamicsWorld)world.collisionWorld).addConstraint(coneC, true);
222 
223 		// RightKnee
224 		localA.setFromEulerAnglesRad(0, PI2, 0).trn(0, -0.225f, 0);
225 		localB.setFromEulerAnglesRad(0, PI2, 0).trn(0, 0.185f, 0);
226 		constraints.add(hingeC = new btHingeConstraint(rightupperleg, rightlowerleg, localA, localB));
227 		hingeC.setLimit(0, PI2);
228 		((btDynamicsWorld)world.collisionWorld).addConstraint(hingeC, true);
229 
230 		// LeftShoulder
231 		localA.setFromEulerAnglesRad(PI, 0, 0).trn(-0.2f, 0.15f, 0);
232 		localB.setFromEulerAnglesRad(PI2, 0, 0).trn(0, -0.18f, 0);
233 		constraints.add(coneC = new btConeTwistConstraint(pelvis, leftupperarm, localA, localB));
234 		coneC.setLimit(PI2, PI2, 0);
235 		((btDynamicsWorld)world.collisionWorld).addConstraint(coneC, true);
236 
237 		// LeftElbow
238 		localA.setFromEulerAnglesRad(0, PI2, 0).trn(0, 0.18f, 0);
239 		localB.setFromEulerAnglesRad(0, PI2, 0).trn(0, -0.14f, 0);
240 		constraints.add(hingeC = new btHingeConstraint(leftupperarm, leftlowerarm, localA, localB));
241 		hingeC.setLimit(0, PI2);
242 		((btDynamicsWorld)world.collisionWorld).addConstraint(hingeC, true);
243 
244 		// RightShoulder
245 		localA.setFromEulerAnglesRad(PI, 0, 0).trn(0.2f, 0.15f, 0);
246 		localB.setFromEulerAnglesRad(PI2, 0, 0).trn(0, -0.18f, 0);
247 		constraints.add(coneC = new btConeTwistConstraint(pelvis, rightupperarm, localA, localB));
248 		coneC.setLimit(PI2, PI2, 0);
249 		((btDynamicsWorld)world.collisionWorld).addConstraint(coneC, true);
250 
251 		// RightElbow
252 		localA.setFromEulerAnglesRad(0, PI2, 0).trn(0, 0.18f, 0);
253 		localB.setFromEulerAnglesRad(0, PI2, 0).trn(0, -0.14f, 0);
254 		constraints.add(hingeC = new btHingeConstraint(rightupperarm, rightlowerarm, localA, localB));
255 		hingeC.setLimit(0, PI2);
256 		((btDynamicsWorld)world.collisionWorld).addConstraint(hingeC, true);
257 	}
258 
createCapsuleModel(float radius, float height)259 	protected Model createCapsuleModel (float radius, float height) {
260 		final Model result = modelBuilder.createCapsule(radius, height + radius * 2f, 16,
261 			new Material(ColorAttribute.createDiffuse(Color.WHITE), ColorAttribute.createSpecular(Color.WHITE)), Usage.Position
262 				| Usage.Normal);
263 		disposables.add(result);
264 		return result;
265 	}
266 }
267