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 17package com.badlogic.gdx.tests.bullet; 18 19import com.badlogic.gdx.Gdx; 20import com.badlogic.gdx.graphics.Color; 21import com.badlogic.gdx.graphics.PerspectiveCamera; 22import com.badlogic.gdx.graphics.g3d.Model; 23import com.badlogic.gdx.graphics.glutils.ShapeRenderer; 24import com.badlogic.gdx.math.Vector3; 25import com.badlogic.gdx.physics.bullet.collision.*; 26 27/** Based on FrustumCullingTest by Xoppa. 28 * 29 * @author jsjolund */ 30public class PairCacheTest extends BaseBulletTest { 31 32 final static float BOX_X_MIN = -25; 33 final static float BOX_Y_MIN = -25; 34 final static float BOX_Z_MIN = -25; 35 36 final static float BOX_X_MAX = 25; 37 final static float BOX_Y_MAX = 25; 38 final static float BOX_Z_MAX = 25; 39 40 final static float SPEED_X = 360f / 7f; 41 final static float SPEED_Y = 360f / 19f; 42 final static float SPEED_Z = 360f / 13f; 43 44 final static int BOXCOUNT = 100; 45 46 private boolean useFrustumCam = false; 47 48 private btPairCachingGhostObject ghostObject; 49 private BulletEntity ghostEntity; 50 private btPersistentManifoldArray manifoldArray; 51 52 private float angleX, angleY, angleZ; 53 54 private ShapeRenderer shapeRenderer; 55 56 private PerspectiveCamera frustumCam; 57 private PerspectiveCamera overviewCam; 58 59 @Override 60 public void create () { 61 super.create(); 62 63 instructions = "Tap to toggle view\nLong press to toggle debug mode\nSwipe for next test\nCtrl+drag to rotate\nScroll to zoom"; 64 65 world.addConstructor("collisionBox", new BulletConstructor(world.getConstructor("box").model)); 66 67 // Create the entities 68 final float dX = BOX_X_MAX - BOX_X_MIN; 69 final float dY = BOX_Y_MAX - BOX_Y_MIN; 70 final float dZ = BOX_Z_MAX - BOX_Z_MIN; 71 for (int i = 0; i < BOXCOUNT; i++) 72 world.add("collisionBox", BOX_X_MIN + dX * (float)Math.random(), BOX_Y_MIN + dY * (float)Math.random(), 73 BOX_Z_MIN + dZ * (float)Math.random()).setColor(0.25f + 0.5f * (float)Math.random(), 74 0.25f + 0.5f * (float)Math.random(), 0.25f + 0.5f * (float)Math.random(), 1f); 75 76 manifoldArray = new btPersistentManifoldArray(); 77 disposables.add(manifoldArray); 78 79 overviewCam = camera; 80 overviewCam.position.set(BOX_X_MAX, BOX_Y_MAX, BOX_Z_MAX); 81 overviewCam.lookAt(Vector3.Zero); 82 overviewCam.far = 150f; 83 overviewCam.update(); 84 85 frustumCam = new PerspectiveCamera(camera.fieldOfView, camera.viewportWidth, camera.viewportHeight); 86 frustumCam.far = Vector3.len(BOX_X_MAX, BOX_Y_MAX, BOX_Z_MAX); 87 frustumCam.update(); 88 89 final Model ghostModel = FrustumCullingTest.createFrustumModel(frustumCam.frustum.planePoints); 90 disposables.add(ghostModel); 91 92 // The ghost object does not need to be shaped as a camera frustum, it can have any collision shape. 93 ghostObject = FrustumCullingTest.createFrustumObject(frustumCam.frustum.planePoints); 94 disposables.add(ghostObject); 95 96 world.add(ghostEntity = new BulletEntity(ghostModel, ghostObject, 0, 0, 0)); 97 disposables.add(ghostEntity); 98 99 shapeRenderer = new ShapeRenderer(); 100 disposables.add(shapeRenderer); 101 102 } 103 104 @Override 105 public BulletWorld createWorld () { 106 // No need to use dynamics for this test 107 btDbvtBroadphase broadphase = new btDbvtBroadphase(); 108 btDefaultCollisionConfiguration collisionConfig = new btDefaultCollisionConfiguration(); 109 btCollisionDispatcher dispatcher = new btCollisionDispatcher(collisionConfig); 110 btCollisionWorld collisionWorld = new btCollisionWorld(dispatcher, broadphase, collisionConfig); 111 return new BulletWorld(collisionConfig, dispatcher, broadphase, null, collisionWorld); 112 } 113 114 @Override 115 public void render () { 116 final float dt = Gdx.graphics.getDeltaTime(); 117 ghostEntity.transform.idt(); 118 ghostEntity.transform.rotate(Vector3.X, angleX = (angleX + dt * SPEED_X) % 360); 119 ghostEntity.transform.rotate(Vector3.Y, angleY = (angleY + dt * SPEED_Y) % 360); 120 ghostEntity.transform.rotate(Vector3.Z, angleZ = (angleZ + dt * SPEED_Z) % 360); 121 122 // Transform the ghost object 123 ghostEntity.body.setWorldTransform(ghostEntity.transform); 124 125 // Transform the frustum cam 126 frustumCam.direction.set(0, 0, -1); 127 frustumCam.up.set(0, 1, 0); 128 frustumCam.position.set(0, 0, 0); 129 frustumCam.rotate(ghostEntity.transform); 130 frustumCam.update(); 131 132 super.render(); 133 134 // Find all overlapping pairs which contain the ghost object and draw lines between the collision points. 135 shapeRenderer.setProjectionMatrix(camera.combined); 136 shapeRenderer.begin(ShapeRenderer.ShapeType.Line); 137 shapeRenderer.setColor(Color.WHITE); 138 139 btBroadphasePairArray arr = world.broadphase.getOverlappingPairCache().getOverlappingPairArray(); 140 int numPairs = arr.size(); 141 for (int i = 0; i < numPairs; ++i) { 142 manifoldArray.clear(); 143 144 btBroadphasePair pair = arr.at(i); 145 btBroadphaseProxy proxy0 = btBroadphaseProxy.obtain(pair.getPProxy0().getCPointer(), false); 146 btBroadphaseProxy proxy1 = btBroadphaseProxy.obtain(pair.getPProxy1().getCPointer(), false); 147 148 btBroadphasePair collisionPair = world.collisionWorld.getPairCache().findPair(proxy0, proxy1); 149 150 if (collisionPair == null) continue; 151 152 btCollisionAlgorithm algorithm = collisionPair.getAlgorithm(); 153 if (algorithm != null) algorithm.getAllContactManifolds(manifoldArray); 154 155 for (int j = 0; j < manifoldArray.size(); j++) { 156 btPersistentManifold manifold = manifoldArray.at(j); 157 158 boolean isFirstBody = manifold.getBody0() == ghostObject; 159 int otherObjectIndex = isFirstBody ? manifold.getBody1().getUserValue() : manifold.getBody0().getUserValue(); 160 Color otherObjectColor = world.entities.get(otherObjectIndex).getColor(); 161 162 for (int p = 0; p < manifold.getNumContacts(); ++p) { 163 btManifoldPoint pt = manifold.getContactPoint(p); 164 165 if (pt.getDistance() < 0.f) { 166 if (isFirstBody) { 167 pt.getPositionWorldOnA(tmpV2); 168 pt.getPositionWorldOnB(tmpV1); 169 } else { 170 pt.getPositionWorldOnA(tmpV1); 171 pt.getPositionWorldOnB(tmpV2); 172 } 173 shapeRenderer.line(tmpV1.x, tmpV1.y, tmpV1.z, tmpV2.x, tmpV2.y, tmpV2.z, otherObjectColor, Color.WHITE); 174 } 175 } 176 } 177 btBroadphaseProxy.free(proxy0); 178 btBroadphaseProxy.free(proxy1); 179 } 180 shapeRenderer.end(); 181 } 182 183 @Override 184 public boolean tap (float x, float y, int count, int button) { 185 useFrustumCam = !useFrustumCam; 186 if (useFrustumCam) 187 camera = frustumCam; 188 else 189 camera = overviewCam; 190 return true; 191 } 192 193 @Override 194 public void update () { 195 super.update(); 196 // Not using dynamics, so update the collision world manually 197 world.collisionWorld.performDiscreteCollisionDetection(); 198 } 199} 200