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.math.collision; 18 19 import java.io.Serializable; 20 import java.util.List; 21 22 import com.badlogic.gdx.math.Matrix4; 23 import com.badlogic.gdx.math.Vector3; 24 25 /** Encapsulates an axis aligned bounding box represented by a minimum and a maximum Vector. Additionally you can query for the 26 * bounding box's center, dimensions and corner points. 27 * 28 * @author badlogicgames (at) gmail.com, Xoppa */ 29 public class BoundingBox implements Serializable { 30 private static final long serialVersionUID = -1286036817192127343L; 31 32 private final static Vector3 tmpVector = new Vector3(); 33 34 public final Vector3 min = new Vector3(); 35 public final Vector3 max = new Vector3(); 36 37 private final Vector3 cnt = new Vector3(); 38 private final Vector3 dim = new Vector3(); 39 40 /** @param out The {@link Vector3} to receive the center of the bounding box. 41 * @return The vector specified with the out argument. */ 42 public Vector3 getCenter (Vector3 out) { 43 return out.set(cnt); 44 } 45 46 public float getCenterX () { 47 return cnt.x; 48 } 49 50 public float getCenterY () { 51 return cnt.y; 52 } 53 54 public float getCenterZ () { 55 return cnt.z; 56 } 57 58 public Vector3 getCorner000 (final Vector3 out) { 59 return out.set(min.x, min.y, min.z); 60 } 61 62 public Vector3 getCorner001 (final Vector3 out) { 63 return out.set(min.x, min.y, max.z); 64 } 65 66 public Vector3 getCorner010 (final Vector3 out) { 67 return out.set(min.x, max.y, min.z); 68 } 69 70 public Vector3 getCorner011 (final Vector3 out) { 71 return out.set(min.x, max.y, max.z); 72 } 73 74 public Vector3 getCorner100 (final Vector3 out) { 75 return out.set(max.x, min.y, min.z); 76 } 77 78 public Vector3 getCorner101 (final Vector3 out) { 79 return out.set(max.x, min.y, max.z); 80 } 81 82 public Vector3 getCorner110 (final Vector3 out) { 83 return out.set(max.x, max.y, min.z); 84 } 85 86 public Vector3 getCorner111 (final Vector3 out) { 87 return out.set(max.x, max.y, max.z); 88 } 89 90 /** @param out The {@link Vector3} to receive the dimensions of this bounding box on all three axis. 91 * @return The vector specified with the out argument */ 92 public Vector3 getDimensions (final Vector3 out) { 93 return out.set(dim); 94 } 95 96 public float getWidth () { 97 return dim.x; 98 } 99 100 public float getHeight () { 101 return dim.y; 102 } 103 104 public float getDepth () { 105 return dim.z; 106 } 107 108 /** @param out The {@link Vector3} to receive the minimum values. 109 * @return The vector specified with the out argument */ 110 public Vector3 getMin (final Vector3 out) { 111 return out.set(min); 112 } 113 114 /** @param out The {@link Vector3} to receive the maximum values. 115 * @return The vector specified with the out argument */ 116 public Vector3 getMax (final Vector3 out) { 117 return out.set(max); 118 } 119 120 /** Constructs a new bounding box with the minimum and maximum vector set to zeros. */ 121 public BoundingBox () { 122 clr(); 123 } 124 125 /** Constructs a new bounding box from the given bounding box. 126 * 127 * @param bounds The bounding box to copy */ 128 public BoundingBox (BoundingBox bounds) { 129 this.set(bounds); 130 } 131 132 /** Constructs the new bounding box using the given minimum and maximum vector. 133 * 134 * @param minimum The minimum vector 135 * @param maximum The maximum vector */ 136 public BoundingBox (Vector3 minimum, Vector3 maximum) { 137 this.set(minimum, maximum); 138 } 139 140 /** Sets the given bounding box. 141 * 142 * @param bounds The bounds. 143 * @return This bounding box for chaining. */ 144 public BoundingBox set (BoundingBox bounds) { 145 return this.set(bounds.min, bounds.max); 146 } 147 148 /** Sets the given minimum and maximum vector. 149 * 150 * @param minimum The minimum vector 151 * @param maximum The maximum vector 152 * @return This bounding box for chaining. */ 153 public BoundingBox set (Vector3 minimum, Vector3 maximum) { 154 min.set(minimum.x < maximum.x ? minimum.x : maximum.x, minimum.y < maximum.y ? minimum.y : maximum.y, 155 minimum.z < maximum.z ? minimum.z : maximum.z); 156 max.set(minimum.x > maximum.x ? minimum.x : maximum.x, minimum.y > maximum.y ? minimum.y : maximum.y, 157 minimum.z > maximum.z ? minimum.z : maximum.z); 158 cnt.set(min).add(max).scl(0.5f); 159 dim.set(max).sub(min); 160 return this; 161 } 162 163 /** Sets the bounding box minimum and maximum vector from the given points. 164 * 165 * @param points The points. 166 * @return This bounding box for chaining. */ 167 public BoundingBox set (Vector3[] points) { 168 this.inf(); 169 for (Vector3 l_point : points) 170 this.ext(l_point); 171 return this; 172 } 173 174 /** Sets the bounding box minimum and maximum vector from the given points. 175 * 176 * @param points The points. 177 * @return This bounding box for chaining. */ 178 public BoundingBox set (List<Vector3> points) { 179 this.inf(); 180 for (Vector3 l_point : points) 181 this.ext(l_point); 182 return this; 183 } 184 185 /** Sets the minimum and maximum vector to positive and negative infinity. 186 * 187 * @return This bounding box for chaining. */ 188 public BoundingBox inf () { 189 min.set(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); 190 max.set(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); 191 cnt.set(0, 0, 0); 192 dim.set(0, 0, 0); 193 return this; 194 } 195 196 /** Extends the bounding box to incorporate the given {@link Vector3}. 197 * @param point The vector 198 * @return This bounding box for chaining. */ 199 public BoundingBox ext (Vector3 point) { 200 return this.set(min.set(min(min.x, point.x), min(min.y, point.y), min(min.z, point.z)), 201 max.set(Math.max(max.x, point.x), Math.max(max.y, point.y), Math.max(max.z, point.z))); 202 } 203 204 /** Sets the minimum and maximum vector to zeros. 205 * @return This bounding box for chaining. */ 206 public BoundingBox clr () { 207 return this.set(min.set(0, 0, 0), max.set(0, 0, 0)); 208 } 209 210 /** Returns whether this bounding box is valid. This means that {@link #max} is greater than {@link #min}. 211 * @return True in case the bounding box is valid, false otherwise */ 212 public boolean isValid () { 213 return min.x < max.x && min.y < max.y && min.z < max.z; 214 } 215 216 /** Extends this bounding box by the given bounding box. 217 * 218 * @param a_bounds The bounding box 219 * @return This bounding box for chaining. */ 220 public BoundingBox ext (BoundingBox a_bounds) { 221 return this.set(min.set(min(min.x, a_bounds.min.x), min(min.y, a_bounds.min.y), min(min.z, a_bounds.min.z)), 222 max.set(max(max.x, a_bounds.max.x), max(max.y, a_bounds.max.y), max(max.z, a_bounds.max.z))); 223 } 224 225 /** Extends this bounding box by the given sphere. 226 * 227 * @param center Sphere center 228 * @param radius Sphere radius 229 * @return This bounding box for chaining. */ 230 public BoundingBox ext (Vector3 center, float radius) { 231 return this.set(min.set(min(min.x, center.x - radius), min(min.y, center.y - radius), min(min.z, center.z - radius)), 232 max.set(max(max.x, center.x + radius), max(max.y, center.y + radius), max(max.z, center.z + radius))); 233 } 234 235 /** Extends this bounding box by the given transformed bounding box. 236 * 237 * @param bounds The bounding box 238 * @param transform The transformation matrix to apply to bounds, before using it to extend this bounding box. 239 * @return This bounding box for chaining. */ 240 public BoundingBox ext (BoundingBox bounds, Matrix4 transform) { 241 ext(tmpVector.set(bounds.min.x, bounds.min.y, bounds.min.z).mul(transform)); 242 ext(tmpVector.set(bounds.min.x, bounds.min.y, bounds.max.z).mul(transform)); 243 ext(tmpVector.set(bounds.min.x, bounds.max.y, bounds.min.z).mul(transform)); 244 ext(tmpVector.set(bounds.min.x, bounds.max.y, bounds.max.z).mul(transform)); 245 ext(tmpVector.set(bounds.max.x, bounds.min.y, bounds.min.z).mul(transform)); 246 ext(tmpVector.set(bounds.max.x, bounds.min.y, bounds.max.z).mul(transform)); 247 ext(tmpVector.set(bounds.max.x, bounds.max.y, bounds.min.z).mul(transform)); 248 ext(tmpVector.set(bounds.max.x, bounds.max.y, bounds.max.z).mul(transform)); 249 return this; 250 } 251 252 /** Multiplies the bounding box by the given matrix. This is achieved by multiplying the 8 corner points and then calculating 253 * the minimum and maximum vectors from the transformed points. 254 * 255 * @param transform The matrix 256 * @return This bounding box for chaining. */ 257 public BoundingBox mul (Matrix4 transform) { 258 final float x0 = min.x, y0 = min.y, z0 = min.z, x1 = max.x, y1 = max.y, z1 = max.z; 259 inf(); 260 ext(tmpVector.set(x0, y0, z0).mul(transform)); 261 ext(tmpVector.set(x0, y0, z1).mul(transform)); 262 ext(tmpVector.set(x0, y1, z0).mul(transform)); 263 ext(tmpVector.set(x0, y1, z1).mul(transform)); 264 ext(tmpVector.set(x1, y0, z0).mul(transform)); 265 ext(tmpVector.set(x1, y0, z1).mul(transform)); 266 ext(tmpVector.set(x1, y1, z0).mul(transform)); 267 ext(tmpVector.set(x1, y1, z1).mul(transform)); 268 return this; 269 } 270 271 /** Returns whether the given bounding box is contained in this bounding box. 272 * @param b The bounding box 273 * @return Whether the given bounding box is contained */ 274 public boolean contains (BoundingBox b) { 275 return !isValid() 276 || (min.x <= b.min.x && min.y <= b.min.y && min.z <= b.min.z && max.x >= b.max.x && max.y >= b.max.y && max.z >= b.max.z); 277 } 278 279 /** Returns whether the given bounding box is intersecting this bounding box (at least one point in). 280 * @param b The bounding box 281 * @return Whether the given bounding box is intersected */ 282 public boolean intersects (BoundingBox b) { 283 if (!isValid()) return false; 284 285 // test using SAT (separating axis theorem) 286 287 float lx = Math.abs(this.cnt.x - b.cnt.x); 288 float sumx = (this.dim.x / 2.0f) + (b.dim.x / 2.0f); 289 290 float ly = Math.abs(this.cnt.y - b.cnt.y); 291 float sumy = (this.dim.y / 2.0f) + (b.dim.y / 2.0f); 292 293 float lz = Math.abs(this.cnt.z - b.cnt.z); 294 float sumz = (this.dim.z / 2.0f) + (b.dim.z / 2.0f); 295 296 return (lx <= sumx && ly <= sumy && lz <= sumz); 297 298 } 299 300 /** Returns whether the given vector is contained in this bounding box. 301 * @param v The vector 302 * @return Whether the vector is contained or not. */ 303 public boolean contains (Vector3 v) { 304 return min.x <= v.x && max.x >= v.x && min.y <= v.y && max.y >= v.y && min.z <= v.z && max.z >= v.z; 305 } 306 307 @Override 308 public String toString () { 309 return "[" + min + "|" + max + "]"; 310 } 311 312 /** Extends the bounding box by the given vector. 313 * 314 * @param x The x-coordinate 315 * @param y The y-coordinate 316 * @param z The z-coordinate 317 * @return This bounding box for chaining. */ 318 public BoundingBox ext (float x, float y, float z) { 319 return this.set(min.set(min(min.x, x), min(min.y, y), min(min.z, z)), max.set(max(max.x, x), max(max.y, y), max(max.z, z))); 320 } 321 322 static final float min (final float a, final float b) { 323 return a > b ? b : a; 324 } 325 326 static final float max (final float a, final float b) { 327 return a > b ? a : b; 328 } 329 } 330