/* * Copyright 2012 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.BufferUtils; import org.lwjgl.opengl.GL11; import org.terasology.game.CoreRegistry; import org.terasology.rendering.world.WorldRenderer; import javax.vecmath.Vector3d; import java.nio.FloatBuffer; /** * View frustum usable for frustum culling. * * @author Benjamin Glatzel <benjamin.glatzel@me.com> */ public class ViewFrustum { private final FrustumPlane[] _planes = new FrustumPlane[6]; private final FloatBuffer _proj = BufferUtils.createFloatBuffer(16); private final FloatBuffer _model = BufferUtils.createFloatBuffer(16); private final FloatBuffer _clip = BufferUtils.createFloatBuffer(16); /** * Init. a new view frustum. */ public ViewFrustum() { for (int i = 0; i < 6; i++) _planes[i] = new FrustumPlane(); } /** * Updates the view frustum using the currently active modelview and projection matrices. */ public void updateFrustum() { GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, _proj); GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, _model); _clip.put(0, _model.get(0) * _proj.get(0) + _model.get(1) * _proj.get(4) + _model.get(2) * _proj.get(8) + _model.get(3) * _proj.get(12)); _clip.put(1, _model.get(0) * _proj.get(1) + _model.get(1) * _proj.get(5) + _model.get(2) * _proj.get(9) + _model.get(3) * _proj.get(13)); _clip.put(2, _model.get(0) * _proj.get(2) + _model.get(1) * _proj.get(6) + _model.get(2) * _proj.get(10) + _model.get(3) * _proj.get(14)); _clip.put(3, _model.get(0) * _proj.get(3) + _model.get(1) * _proj.get(7) + _model.get(2) * _proj.get(11) + _model.get(3) * _proj.get(15)); _clip.put(4, _model.get(4) * _proj.get(0) + _model.get(5) * _proj.get(4) + _model.get(6) * _proj.get(8) + _model.get(7) * _proj.get(12)); _clip.put(5, _model.get(4) * _proj.get(1) + _model.get(5) * _proj.get(5) + _model.get(6) * _proj.get(9) + _model.get(7) * _proj.get(13)); _clip.put(6, _model.get(4) * _proj.get(2) + _model.get(5) * _proj.get(6) + _model.get(6) * _proj.get(10) + _model.get(7) * _proj.get(14)); _clip.put(7, _model.get(4) * _proj.get(3) + _model.get(5) * _proj.get(7) + _model.get(6) * _proj.get(11) + _model.get(7) * _proj.get(15)); _clip.put(8, _model.get(8) * _proj.get(0) + _model.get(9) * _proj.get(4) + _model.get(10) * _proj.get(8) + _model.get(11) * _proj.get(12)); _clip.put(9, _model.get(8) * _proj.get(1) + _model.get(9) * _proj.get(5) + _model.get(10) * _proj.get(9) + _model.get(11) * _proj.get(13)); _clip.put(10, _model.get(8) * _proj.get(2) + _model.get(9) * _proj.get(6) + _model.get(10) * _proj.get(10) + _model.get(11) * _proj.get(14)); _clip.put(11, _model.get(8) * _proj.get(3) + _model.get(9) * _proj.get(7) + _model.get(10) * _proj.get(11) + _model.get(11) * _proj.get(15)); _clip.put(12, _model.get(12) * _proj.get(0) + _model.get(13) * _proj.get(4) + _model.get(14) * _proj.get(8) + _model.get(15) * _proj.get(12)); _clip.put(13, _model.get(12) * _proj.get(1) + _model.get(13) * _proj.get(5) + _model.get(14) * _proj.get(9) + _model.get(15) * _proj.get(13)); _clip.put(14, _model.get(12) * _proj.get(2) + _model.get(13) * _proj.get(6) + _model.get(14) * _proj.get(10) + _model.get(15) * _proj.get(14)); _clip.put(15, _model.get(12) * _proj.get(3) + _model.get(13) * _proj.get(7) + _model.get(14) * _proj.get(11) + _model.get(15) * _proj.get(15)); // RIGHT _planes[0].setA(_clip.get(3) - _clip.get(0)); _planes[0].setB(_clip.get(7) - _clip.get(4)); _planes[0].setC(_clip.get(11) - _clip.get(8)); _planes[0].setD(_clip.get(15) - _clip.get(12)); _planes[0].normalize(); // LEFT _planes[1].setA(_clip.get(3) + _clip.get(0)); _planes[1].setB(_clip.get(7) + _clip.get(4)); _planes[1].setC(_clip.get(11) + _clip.get(8)); _planes[1].setD(_clip.get(15) + _clip.get(12)); _planes[1].normalize(); // BOTTOM _planes[2].setA(_clip.get(3) + _clip.get(1)); _planes[2].setB(_clip.get(7) + _clip.get(5)); _planes[2].setC(_clip.get(11) + _clip.get(9)); _planes[2].setD(_clip.get(15) + _clip.get(13)); _planes[2].normalize(); // TOP _planes[3].setA(_clip.get(3) - _clip.get(1)); _planes[3].setB(_clip.get(7) - _clip.get(5)); _planes[3].setC(_clip.get(11) - _clip.get(9)); _planes[3].setD(_clip.get(15) - _clip.get(13)); _planes[3].normalize(); // FAR _planes[4].setA(_clip.get(3) - _clip.get(2)); _planes[4].setB(_clip.get(7) - _clip.get(6)); _planes[4].setC(_clip.get(11) - _clip.get(10)); _planes[4].setD(_clip.get(15) - _clip.get(14)); _planes[4].normalize(); // NEAR _planes[5].setA(_clip.get(3) + _clip.get(2)); _planes[5].setB(_clip.get(7) + _clip.get(6)); _planes[5].setC(_clip.get(11) + _clip.get(10)); _planes[5].setD(_clip.get(15) + _clip.get(14)); _planes[5].normalize(); } /** * Returns true if the given point intersects the view frustum. */ public boolean intersects(double x, double y, double z) { for (int i = 0; i < 6; i++) { if (_planes[i].getA() * x + _planes[i].getB() * y + _planes[i].getC() * z + _planes[i].getD() <= 0) { return false; } } return true; } /** * Returns true if this view frustum intersects the given AABB. */ public boolean intersects(AABB aabb) { Vector3d[] aabbVertices = aabb.getVertices(); Vector3d cp = CoreRegistry.get(WorldRenderer.class).getActiveCamera().getPosition(); for (int i = 0; i < 6; i++) { if (_planes[i].getA() * (aabbVertices[0].x - cp.x) + _planes[i].getB() * (aabbVertices[0].y - cp.y) + _planes[i].getC() * (aabbVertices[0].z - cp.z) + _planes[i].getD() > 0) continue; if (_planes[i].getA() * (aabbVertices[1].x - cp.x) + _planes[i].getB() * (aabbVertices[1].y - cp.y) + _planes[i].getC() * (aabbVertices[1].z - cp.z) + _planes[i].getD() > 0) continue; if (_planes[i].getA() * (aabbVertices[2].x - cp.x) + _planes[i].getB() * (aabbVertices[2].y - cp.y) + _planes[i].getC() * (aabbVertices[2].z - cp.z) + _planes[i].getD() > 0) continue; if (_planes[i].getA() * (aabbVertices[3].x - cp.x) + _planes[i].getB() * (aabbVertices[3].y - cp.y) + _planes[i].getC() * (aabbVertices[3].z - cp.z) + _planes[i].getD() > 0) continue; if (_planes[i].getA() * (aabbVertices[4].x - cp.x) + _planes[i].getB() * (aabbVertices[4].y - cp.y) + _planes[i].getC() * (aabbVertices[4].z - cp.z) + _planes[i].getD() > 0) continue; if (_planes[i].getA() * (aabbVertices[5].x - cp.x) + _planes[i].getB() * (aabbVertices[5].y - cp.y) + _planes[i].getC() * (aabbVertices[5].z - cp.z) + _planes[i].getD() > 0) continue; if (_planes[i].getA() * (aabbVertices[6].x - cp.x) + _planes[i].getB() * (aabbVertices[6].y - cp.y) + _planes[i].getC() * (aabbVertices[6].z - cp.z) + _planes[i].getD() > 0) continue; if (_planes[i].getA() * (aabbVertices[7].x - cp.x) + _planes[i].getB() * (aabbVertices[7].y - cp.y) + _planes[i].getC() * (aabbVertices[7].z - cp.z) + _planes[i].getD() > 0) continue; return false; } return true; } }