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.Application.ApplicationType; 20 import com.badlogic.gdx.Gdx; 21 import com.badlogic.gdx.Input.Keys; 22 import com.badlogic.gdx.graphics.Color; 23 import com.badlogic.gdx.graphics.GL20; 24 import com.badlogic.gdx.graphics.PerspectiveCamera; 25 import com.badlogic.gdx.graphics.VertexAttributes.Usage; 26 import com.badlogic.gdx.graphics.g3d.Environment; 27 import com.badlogic.gdx.graphics.g3d.Material; 28 import com.badlogic.gdx.graphics.g3d.Model; 29 import com.badlogic.gdx.graphics.g3d.ModelBatch; 30 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; 31 import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute; 32 import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight; 33 import com.badlogic.gdx.graphics.g3d.environment.DirectionalShadowLight; 34 import com.badlogic.gdx.graphics.g3d.loader.ObjLoader; 35 import com.badlogic.gdx.graphics.g3d.utils.DepthShaderProvider; 36 import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder; 37 import com.badlogic.gdx.math.Vector3; 38 import com.badlogic.gdx.math.collision.Ray; 39 import com.badlogic.gdx.physics.bullet.Bullet; 40 import com.badlogic.gdx.physics.bullet.dynamics.btRigidBody; 41 import com.badlogic.gdx.physics.bullet.linearmath.LinearMath; 42 import com.badlogic.gdx.physics.bullet.linearmath.btIDebugDraw.DebugDrawModes; 43 import com.badlogic.gdx.utils.Array; 44 import com.badlogic.gdx.utils.Disposable; 45 46 /** @author xoppa */ 47 public class BaseBulletTest extends BulletTest { 48 // Set this to the path of the lib to use it on desktop instead of default lib. 49 private final static String customDesktopLib = null;//"D:\\Xoppa\\code\\libgdx\\extensions\\gdx-bullet\\jni\\vs\\gdxBullet\\x64\\Debug\\gdxBullet.dll"; 50 51 private static boolean initialized = false; 52 53 public static boolean shadows = true; 54 init()55 public static void init () { 56 if (initialized) return; 57 // Need to initialize bullet before using it. 58 if (Gdx.app.getType() == ApplicationType.Desktop && customDesktopLib != null) { 59 System.load(customDesktopLib); 60 } else 61 Bullet.init(); 62 Gdx.app.log("Bullet", "Version = " + LinearMath.btGetVersion()); 63 initialized = true; 64 } 65 66 public Environment environment; 67 public DirectionalLight light; 68 public ModelBatch shadowBatch; 69 70 public BulletWorld world; 71 public ObjLoader objLoader = new ObjLoader(); 72 public ModelBuilder modelBuilder = new ModelBuilder(); 73 public ModelBatch modelBatch; 74 public Array<Disposable> disposables = new Array<Disposable>(); 75 private int debugMode = DebugDrawModes.DBG_NoDebug; 76 77 protected final static Vector3 tmpV1 = new Vector3(), tmpV2 = new Vector3(); 78 createWorld()79 public BulletWorld createWorld () { 80 return new BulletWorld(); 81 } 82 83 @Override create()84 public void create () { 85 init(); 86 environment = new Environment(); 87 environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.3f, 0.3f, 0.3f, 1.f)); 88 light = shadows ? new DirectionalShadowLight(1024, 1024, 20f, 20f, 1f, 300f) : new DirectionalLight(); 89 light.set(0.8f, 0.8f, 0.8f, -0.5f, -1f, 0.7f); 90 environment.add(light); 91 if (shadows) 92 environment.shadowMap = (DirectionalShadowLight)light; 93 shadowBatch = new ModelBatch(new DepthShaderProvider()); 94 95 modelBatch = new ModelBatch(); 96 97 world = createWorld(); 98 world.performanceCounter = performanceCounter; 99 100 final float width = Gdx.graphics.getWidth(); 101 final float height = Gdx.graphics.getHeight(); 102 if (width > height) 103 camera = new PerspectiveCamera(67f, 3f * width / height, 3f); 104 else 105 camera = new PerspectiveCamera(67f, 3f, 3f * height / width); 106 camera.position.set(10f, 10f, 10f); 107 camera.lookAt(0, 0, 0); 108 camera.update(); 109 110 // Create some simple models 111 final Model groundModel = modelBuilder.createRect( 112 20f, 113 0f, 114 -20f, 115 -20f, 116 0f, 117 -20f, 118 -20f, 119 0f, 120 20f, 121 20f, 122 0f, 123 20f, 124 0, 125 1, 126 0, 127 new Material(ColorAttribute.createDiffuse(Color.WHITE), ColorAttribute.createSpecular(Color.WHITE), FloatAttribute 128 .createShininess(16f)), Usage.Position | Usage.Normal); 129 disposables.add(groundModel); 130 final Model boxModel = modelBuilder.createBox(1f, 1f, 1f, new Material(ColorAttribute.createDiffuse(Color.WHITE), 131 ColorAttribute.createSpecular(Color.WHITE), FloatAttribute.createShininess(64f)), Usage.Position | Usage.Normal); 132 disposables.add(boxModel); 133 134 // Add the constructors 135 world.addConstructor("ground", new BulletConstructor(groundModel, 0f)); // mass = 0: static body 136 world.addConstructor("box", new BulletConstructor(boxModel, 1f)); // mass = 1kg: dynamic body 137 world.addConstructor("staticbox", new BulletConstructor(boxModel, 0f)); // mass = 0: static body 138 } 139 140 @Override dispose()141 public void dispose () { 142 world.dispose(); 143 world = null; 144 145 for (Disposable disposable : disposables) 146 disposable.dispose(); 147 disposables.clear(); 148 149 modelBatch.dispose(); 150 modelBatch = null; 151 152 shadowBatch.dispose(); 153 shadowBatch = null; 154 155 if (shadows) 156 ((DirectionalShadowLight)light).dispose(); 157 light = null; 158 159 super.dispose(); 160 } 161 162 @Override render()163 public void render () { 164 render(true); 165 } 166 render(boolean update)167 public void render (boolean update) { 168 fpsCounter.put(Gdx.graphics.getFramesPerSecond()); 169 170 if (update) update(); 171 172 beginRender(true); 173 174 renderWorld(); 175 176 Gdx.gl.glDisable(GL20.GL_DEPTH_TEST); 177 if (debugMode != DebugDrawModes.DBG_NoDebug) world.setDebugMode(debugMode); 178 Gdx.gl.glEnable(GL20.GL_DEPTH_TEST); 179 180 performance.setLength(0); 181 performance.append("FPS: ").append(fpsCounter.value).append(", Bullet: ") 182 .append((int)(performanceCounter.load.value * 100f)).append("%"); 183 } 184 beginRender(boolean lighting)185 protected void beginRender (boolean lighting) { 186 Gdx.gl.glViewport(0, 0, Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight()); 187 Gdx.gl.glClearColor(0, 0, 0, 0); 188 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); 189 camera.update(); 190 } 191 renderWorld()192 protected void renderWorld () { 193 if (shadows) { 194 ((DirectionalShadowLight)light).begin(Vector3.Zero, camera.direction); 195 shadowBatch.begin(((DirectionalShadowLight)light).getCamera()); 196 world.render(shadowBatch, null); 197 shadowBatch.end(); 198 ((DirectionalShadowLight)light).end(); 199 } 200 201 modelBatch.begin(camera); 202 world.render(modelBatch, environment); 203 modelBatch.end(); 204 } 205 update()206 public void update () { 207 world.update(); 208 } 209 shoot(final float x, final float y)210 public BulletEntity shoot (final float x, final float y) { 211 return shoot(x, y, 30f); 212 } 213 shoot(final float x, final float y, final float impulse)214 public BulletEntity shoot (final float x, final float y, final float impulse) { 215 return shoot("box", x, y, impulse); 216 } 217 shoot(final String what, final float x, final float y, final float impulse)218 public BulletEntity shoot (final String what, final float x, final float y, final float impulse) { 219 // Shoot a box 220 Ray ray = camera.getPickRay(x, y); 221 BulletEntity entity = world.add(what, ray.origin.x, ray.origin.y, ray.origin.z); 222 entity.setColor(0.5f + 0.5f * (float)Math.random(), 0.5f + 0.5f * (float)Math.random(), 0.5f + 0.5f * (float)Math.random(), 223 1f); 224 ((btRigidBody)entity.body).applyCentralImpulse(ray.direction.scl(impulse)); 225 return entity; 226 } 227 setDebugMode(final int mode)228 public void setDebugMode (final int mode) { 229 world.setDebugMode(debugMode = mode); 230 } 231 toggleDebugMode()232 public void toggleDebugMode () { 233 if (world.getDebugMode() == DebugDrawModes.DBG_NoDebug) 234 setDebugMode(DebugDrawModes.DBG_DrawWireframe | DebugDrawModes.DBG_DrawFeaturesText | DebugDrawModes.DBG_DrawText | DebugDrawModes.DBG_DrawContactPoints); 235 else if (world.renderMeshes) 236 world.renderMeshes = false; 237 else { 238 world.renderMeshes = true; 239 setDebugMode(DebugDrawModes.DBG_NoDebug); 240 } 241 } 242 243 @Override longPress(float x, float y)244 public boolean longPress (float x, float y) { 245 toggleDebugMode(); 246 return true; 247 } 248 249 @Override keyUp(int keycode)250 public boolean keyUp (int keycode) { 251 if (keycode == Keys.ENTER) { 252 toggleDebugMode(); 253 return true; 254 } 255 return super.keyUp(keycode); 256 } 257 } 258