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.physics.bullet; 18 19 import java.util.Arrays; 20 21 import com.badlogic.gdx.graphics.g3d.model.MeshPart; 22 import com.badlogic.gdx.graphics.g3d.model.Node; 23 import com.badlogic.gdx.math.Matrix4; 24 import com.badlogic.gdx.physics.bullet.collision.btBvhTriangleMeshShape; 25 import com.badlogic.gdx.physics.bullet.collision.btCollisionShape; 26 import com.badlogic.gdx.physics.bullet.collision.btCompoundShape; 27 import com.badlogic.gdx.physics.bullet.linearmath.LinearMath; 28 import com.badlogic.gdx.physics.bullet.linearmath.LinearMathConstants; 29 import com.badlogic.gdx.utils.Array; 30 import com.badlogic.gdx.utils.GdxRuntimeException; 31 import com.badlogic.gdx.utils.Pool; 32 import com.badlogic.gdx.utils.SharedLibraryLoader; 33 34 public class Bullet { 35 /** The version of the Bullet library used by this wrapper. */ 36 public final static int VERSION = LinearMathConstants.BT_BULLET_VERSION; 37 38 protected static boolean useRefCounting = false; 39 protected static boolean enableLogging = true; 40 41 /** Loads the native Bullet native library and initializes the gdx-bullet extension. Must be called before any of the bullet 42 * classes/methods can be used. */ 43 public static void init () { 44 init(false); 45 } 46 47 /** Loads the native Bullet native library and initializes the gdx-bullet extension. Must be called before any of the bullet 48 * classes/methods can be used. 49 * @param useRefCounting Whether to use reference counting, causing object to be destroyed when no longer referenced. You must 50 * use {@link BulletBase#obtain()} and {@link BulletBase#release()} when using reference counting. */ 51 public static void init (boolean useRefCounting) { 52 init(useRefCounting, true); 53 } 54 55 /** Loads the native Bullet native library and initializes the gdx-bullet extension. Must be called before any of the bullet 56 * classes/methods can be used. 57 * @param useRefCounting Whether to use reference counting, causing object to be destroyed when no longer referenced. You must 58 * use {@link BulletBase#obtain()} and {@link BulletBase#release()} when using reference counting. 59 * @param logging Whether to log an error on potential errors in the application. */ 60 public static void init (boolean useRefCounting, boolean logging) { 61 Bullet.useRefCounting = useRefCounting; 62 Bullet.enableLogging = logging; 63 new SharedLibraryLoader().load("gdx-bullet"); 64 final int version = LinearMath.btGetVersion(); 65 if (version != VERSION) 66 throw new GdxRuntimeException("Bullet binaries version (" + version + ") does not match source version (" + VERSION 67 + ")"); 68 } 69 70 protected static class ShapePart { 71 public Array<MeshPart> parts = new Array<MeshPart>(); 72 public Matrix4 transform = new Matrix4(); 73 } 74 75 private final static Pool<ShapePart> shapePartPool = new Pool<ShapePart>() { 76 @Override 77 protected ShapePart newObject () { 78 return new ShapePart(); 79 } 80 }; 81 private final static Array<ShapePart> shapePartArray = new Array<ShapePart>(); 82 83 private final static Matrix4 idt = new Matrix4(); 84 private final static Matrix4 tmpM = new Matrix4(); 85 86 public static void getShapeParts (final Node node, final boolean applyTransform, final Array<ShapePart> out, final int offset, 87 final Pool<ShapePart> pool) { 88 final Matrix4 transform = applyTransform ? node.localTransform : idt; 89 if (node.parts.size > 0) { 90 ShapePart part = null; 91 for (int i = offset, n = out.size; i < n; i++) { 92 final ShapePart p = out.get(i); 93 if (Arrays.equals(p.transform.val, transform.val)) { 94 part = p; 95 break; 96 } 97 } 98 if (part == null) { 99 part = pool.obtain(); 100 part.parts.clear(); 101 part.transform.set(transform); 102 out.add(part); 103 } 104 for (int i = 0, n = node.parts.size; i < n; i++) 105 part.parts.add(node.parts.get(i).meshPart); 106 } 107 if (node.hasChildren()) { 108 final boolean transformed = applyTransform && !Arrays.equals(transform.val, idt.val); 109 final int o = transformed ? out.size : offset; 110 getShapeParts(node.getChildren(), out, o, pool); 111 if (transformed) { 112 for (int i = o, n = out.size; i < n; i++) { 113 final ShapePart part = out.get(i); 114 tmpM.set(part.transform); 115 part.transform.set(transform).mul(tmpM); 116 } 117 } 118 } 119 } 120 121 public static <T extends Node> void getShapeParts (final Iterable<T> nodes, final Array<ShapePart> out, final int offset, 122 final Pool<ShapePart> pool) { 123 for (T node : nodes) 124 getShapeParts(node, true, out, offset, pool); 125 } 126 127 public static btCollisionShape obtainStaticNodeShape (final Node node, final boolean applyTransform) { 128 getShapeParts(node, applyTransform, shapePartArray, 0, shapePartPool); 129 btCollisionShape result = obtainStaticShape(shapePartArray); 130 shapePartPool.freeAll(shapePartArray); 131 shapePartArray.clear(); 132 return result; 133 } 134 135 /** Obtain a {@link btCollisionShape} based on the specified nodes, which can be used for a static body but not for a dynamic 136 * body. Depending on the specified nodes the result will be either a {@link btBvhTriangleMeshShape} or a 137 * {@link btCompoundShape} of multiple btBvhTriangleMeshShape's. Where possible, the same btBvhTriangleMeshShape will be reused 138 * if multiple nodes use the same (mesh) part. The node transformation (translation and rotation) will be included, but scaling 139 * will be ignored. 140 * @param nodes The nodes for which to obtain a node, typically this would be: `model.nodes`. 141 * @return The obtained shape, if you're using reference counting then you can release the shape when no longer needed. */ 142 public static btCollisionShape obtainStaticNodeShape (final Array<Node> nodes) { 143 getShapeParts(nodes, shapePartArray, 0, shapePartPool); 144 btCollisionShape result = obtainStaticShape(shapePartArray); 145 shapePartPool.freeAll(shapePartArray); 146 shapePartArray.clear(); 147 return result; 148 } 149 150 public static btCollisionShape obtainStaticShape (final Array<ShapePart> parts) { 151 if (parts.size == 0) return null; 152 if (parts.size == 1 && Arrays.equals(parts.get(0).transform.val, idt.val)) 153 return btBvhTriangleMeshShape.obtain(parts.get(0).parts); 154 btCompoundShape result = new btCompoundShape(); 155 result.obtain(); 156 for (int i = 0, n = parts.size; i < n; i++) { 157 final btBvhTriangleMeshShape shape = btBvhTriangleMeshShape.obtain(parts.get(i).parts); 158 result.addChildShape(parts.get(i).transform, shape); 159 shape.release(); 160 } 161 return result; 162 } 163 } 164