Java tutorial
/* * Copyright 2011 Benjamin Glatzel <benjamin.glatzel@me.com>. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.model.structures; import org.lwjgl.opengl.GL11; import org.terasology.game.Terasology; import javax.vecmath.Vector3d; import java.util.ArrayList; import static org.lwjgl.opengl.GL11.*; /** * An axis-aligned bounding box. Provides basic support for inclusion * and intersection tests. * * @author Benjamin Glatzel <benjamin.glatzel@me.com> */ public class AABB { private final Vector3d _position = new Vector3d(); private final Vector3d _dimensions; private Vector3d[] _vertices; private int _displayListWire = -1; private int _displayListSolid = -1; /** * Creates a new AABB at the given position with the given dimensions. * * @param position The position * @param dimensions The dimensions */ public AABB(Vector3d position, Vector3d dimensions) { setPosition(position); this._dimensions = dimensions; } /** * Returns true if this AABB overlaps the given AABB. * * @param aabb2 The AABB to check for overlapping * @return True if overlapping */ public boolean overlaps(AABB aabb2) { if (maxX() < aabb2.minX() || minX() > aabb2.maxX()) return false; if (maxY() < aabb2.minY() || minY() > aabb2.maxY()) return false; if (maxZ() < aabb2.minZ() || minZ() > aabb2.maxZ()) return false; return true; } /** * Returns true if the AABB contains the given point. * * @param point The point to check for inclusion * @return True if containing */ public boolean contains(Vector3d point) { if (maxX() < point.x || minX() > point.x) return false; if (maxY() < point.y || minY() > point.y) return false; if (maxZ() < point.z || minZ() > point.z) return false; return true; } /** * Returns the closest point on the AABB to a given point. * * @param p The point * @return The point on the AABB closest to the given point */ public Vector3d closestPointOnAABBToPoint(Vector3d p) { Vector3d r = new Vector3d(p); if (p.x < minX()) r.x = minX(); if (p.x > maxX()) r.x = maxX(); if (p.y < minY()) r.y = minY(); if (p.y > maxY()) r.y = maxY(); if (p.z < minZ()) r.z = minZ(); if (p.z > maxZ()) r.z = maxZ(); return r; } /** * Returns the normal of the plane closest to the given origin. * * @param pointOnAABB A point on the AABB * @param origin The origin * @param testX True if the x-axis should be tested * @param testY True if the y-axis should be tested * @param testZ True if the z-axis should be tested * @return The normal */ public Vector3d normalForPlaneClosestToOrigin(Vector3d pointOnAABB, Vector3d origin, boolean testX, boolean testY, boolean testZ) { ArrayList<Vector3d> normals = new ArrayList<Vector3d>(); if (pointOnAABB.z == minZ() && testZ) normals.add(new Vector3d(0, 0, -1)); if (pointOnAABB.z == maxZ() && testZ) normals.add(new Vector3d(0, 0, 1)); if (pointOnAABB.x == minX() && testX) normals.add(new Vector3d(-1, 0, 0)); if (pointOnAABB.x == maxX() && testX) normals.add(new Vector3d(1, 0, 0)); if (pointOnAABB.y == minY() && testY) normals.add(new Vector3d(0, -1, 0)); if (pointOnAABB.y == maxY() && testY) normals.add(new Vector3d(0, 1, 0)); double minDistance = Double.MAX_VALUE; Vector3d closestNormal = new Vector3d(); for (int i = 0; i < normals.size(); i++) { Vector3d n = normals.get(i); Vector3d diff = new Vector3d(centerPointForNormal(n)); diff.sub(origin); double distance = diff.length(); if (distance < minDistance) { minDistance = distance; closestNormal = n; } } return closestNormal; } /** * Returns the center point of one of the six planes for the given normal. * * @param normal The normal * @return The center point */ public Vector3d centerPointForNormal(Vector3d normal) { if (normal.x == 1 && normal.y == 0 && normal.z == 0) return new Vector3d(getPosition().x + _dimensions.x, getPosition().y, getPosition().z); if (normal.x == -1 && normal.y == 0 && normal.z == 0) return new Vector3d(getPosition().x - _dimensions.x, getPosition().y, getPosition().z); if (normal.x == 0 && normal.y == 0 && normal.z == 1) return new Vector3d(getPosition().x, getPosition().y, getPosition().z + _dimensions.z); if (normal.x == 0 && normal.y == 0 && normal.z == -1) return new Vector3d(getPosition().x, getPosition().y, getPosition().z - _dimensions.z); if (normal.x == 0 && normal.y == 1 && normal.z == 0) return new Vector3d(getPosition().x, getPosition().y + _dimensions.y, getPosition().z); if (normal.x == 0 && normal.y == -1 && normal.z == 0) return new Vector3d(getPosition().x, getPosition().y - _dimensions.y, getPosition().z); return new Vector3d(); } /** * Returns the vertices of this AABB. * * @return The vertices */ public Vector3d[] getVertices() { if (_vertices == null) { Vector3d[] vertices = new Vector3d[8]; // Front vertices[0] = new Vector3d(minX(), minY(), maxZ()); vertices[1] = new Vector3d(maxX(), minY(), maxZ()); vertices[2] = new Vector3d(maxX(), maxY(), maxZ()); vertices[3] = new Vector3d(minX(), maxY(), maxZ()); // Back vertices[4] = new Vector3d(minX(), minY(), minZ()); vertices[5] = new Vector3d(maxX(), minY(), minZ()); vertices[6] = new Vector3d(maxX(), maxY(), minZ()); vertices[7] = new Vector3d(minX(), maxY(), minZ()); _vertices = vertices; } return _vertices; } /** * Renders this AABB. * <p/> * @param lineThickness The thickness of the line */ public void render(float lineThickness) { glLineWidth(lineThickness); glPushMatrix(); Vector3d rp = Terasology.getInstance().getActiveWorldProvider().getRenderingReferencePoint(); glTranslated(getPosition().x - rp.x, getPosition().y - rp.y, getPosition().z - rp.z); if (_displayListWire == -1) { generateDisplayListWire(); } glCallList(_displayListWire); glPopMatrix(); } public void renderSolid() { glPushMatrix(); Vector3d rp = Terasology.getInstance().getActiveWorldProvider().getRenderingReferencePoint(); glTranslated(getPosition().x - rp.x, getPosition().y - rp.y, getPosition().z - rp.z); if (_displayListSolid == -1) { generateDisplayListSolid(); } glCallList(_displayListSolid); glPopMatrix(); } private void generateDisplayListSolid() { _displayListSolid = glGenLists(1); glNewList(_displayListSolid, GL11.GL_COMPILE); glBegin(GL_QUADS); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); GL11.glVertex3d(-_dimensions.x, _dimensions.y, _dimensions.z); GL11.glVertex3d(_dimensions.x, _dimensions.y, _dimensions.z); GL11.glVertex3d(_dimensions.x, _dimensions.y, -_dimensions.z); GL11.glVertex3d(-_dimensions.x, _dimensions.y, -_dimensions.z); GL11.glVertex3d(-_dimensions.x, -_dimensions.y, -_dimensions.z); GL11.glVertex3d(-_dimensions.x, -_dimensions.y, _dimensions.z); GL11.glVertex3d(-_dimensions.x, _dimensions.y, _dimensions.z); GL11.glVertex3d(-_dimensions.x, _dimensions.y, -_dimensions.z); GL11.glVertex3d(-_dimensions.x, -_dimensions.y, _dimensions.z); GL11.glVertex3d(_dimensions.x, -_dimensions.y, _dimensions.z); GL11.glVertex3d(_dimensions.x, _dimensions.y, _dimensions.z); GL11.glVertex3d(-_dimensions.x, _dimensions.y, _dimensions.z); GL11.glVertex3d(_dimensions.x, _dimensions.y, -_dimensions.z); GL11.glVertex3d(_dimensions.x, _dimensions.y, _dimensions.z); GL11.glVertex3d(_dimensions.x, -_dimensions.y, _dimensions.z); GL11.glVertex3d(_dimensions.x, -_dimensions.y, -_dimensions.z); GL11.glVertex3d(-_dimensions.x, _dimensions.y, -_dimensions.z); GL11.glVertex3d(_dimensions.x, _dimensions.y, -_dimensions.z); GL11.glVertex3d(_dimensions.x, -_dimensions.y, -_dimensions.z); GL11.glVertex3d(-_dimensions.x, -_dimensions.y, -_dimensions.z); GL11.glVertex3d(-_dimensions.x, -_dimensions.y, -_dimensions.z); GL11.glVertex3d(_dimensions.x, -_dimensions.y, -_dimensions.z); GL11.glVertex3d(_dimensions.x, -_dimensions.y, _dimensions.z); GL11.glVertex3d(-_dimensions.x, -_dimensions.y, _dimensions.z); glEnd(); glEndList(); } private void generateDisplayListWire() { double offset = 0.001; _displayListWire = glGenLists(1); glNewList(_displayListWire, GL11.GL_COMPILE); glColor4f(0.0f, 0.0f, 0.0f, 1.0f); // FRONT glBegin(GL_LINE_LOOP); glVertex3d(-_dimensions.x - offset, -_dimensions.y - offset, -_dimensions.z - offset); glVertex3d(+_dimensions.x + offset, -_dimensions.y - offset, -_dimensions.z - offset); glVertex3d(+_dimensions.x + offset, +_dimensions.y + offset, -_dimensions.z - offset); glVertex3d(-_dimensions.x - offset, +_dimensions.y + offset, -_dimensions.z - offset); glEnd(); // BACK glBegin(GL_LINE_LOOP); glVertex3d(-_dimensions.x - offset, -_dimensions.y - offset, +_dimensions.z + offset); glVertex3d(+_dimensions.x + offset, -_dimensions.y - offset, +_dimensions.z + offset); glVertex3d(+_dimensions.x + offset, +_dimensions.y + offset, +_dimensions.z + offset); glVertex3d(-_dimensions.x - offset, +_dimensions.y + offset, +_dimensions.z + offset); glEnd(); // TOP glBegin(GL_LINE_LOOP); glVertex3d(-_dimensions.x - offset, -_dimensions.y - offset, -_dimensions.z - offset); glVertex3d(+_dimensions.x + offset, -_dimensions.y - offset, -_dimensions.z - offset); glVertex3d(+_dimensions.x + offset, -_dimensions.y - offset, +_dimensions.z + offset); glVertex3d(-_dimensions.x - offset, -_dimensions.y - offset, +_dimensions.z + offset); glEnd(); // BOTTOM glBegin(GL_LINE_LOOP); glVertex3d(-_dimensions.x - offset, +_dimensions.y + offset, -_dimensions.z - offset); glVertex3d(+_dimensions.x + offset, +_dimensions.y + offset, -_dimensions.z - offset); glVertex3d(+_dimensions.x + offset, +_dimensions.y + offset, +_dimensions.z + offset); glVertex3d(-_dimensions.x - offset, +_dimensions.y + offset, +_dimensions.z + offset); glEnd(); // LEFT glBegin(GL_LINE_LOOP); glVertex3d(-_dimensions.x - offset, -_dimensions.y - offset, -_dimensions.z - offset); glVertex3d(-_dimensions.x - offset, -_dimensions.y - offset, +_dimensions.z + offset); glVertex3d(-_dimensions.x - offset, +_dimensions.y + offset, +_dimensions.z + offset); glVertex3d(-_dimensions.x - offset, +_dimensions.y + offset, -_dimensions.z - offset); glEnd(); // RIGHT glBegin(GL_LINE_LOOP); glVertex3d(+_dimensions.x + offset, -_dimensions.y - offset, -_dimensions.z - offset); glVertex3d(+_dimensions.x + offset, -_dimensions.y - offset, +_dimensions.z + offset); glVertex3d(+_dimensions.x + offset, +_dimensions.y + offset, +_dimensions.z + offset); glVertex3d(+_dimensions.x + offset, +_dimensions.y + offset, -_dimensions.z - offset); glEnd(); glEndList(); } public double minX() { return (getPosition().x - _dimensions.x); } public double minY() { return (getPosition().y - _dimensions.y); } public double minZ() { return (getPosition().z - _dimensions.z); } public double maxX() { return (getPosition().x + _dimensions.x); } public double maxY() { return (getPosition().y + _dimensions.y); } public double maxZ() { return (getPosition().z + _dimensions.z); } public Vector3d getDimensions() { return _dimensions; } public Vector3d getPosition() { return _position; } public void setPosition(Vector3d position) { _position.set(position); } }