Path: blob/master/3rdparty/openexr/Imath/ImathFrustumTest.h
16337 views
///////////////////////////////////////////////////////////////////////////1//2// Copyright (c) 2011, Industrial Light & Magic, a division of Lucas3// Digital Ltd. LLC4//5// All rights reserved.6//7// Redistribution and use in source and binary forms, with or without8// modification, are permitted provided that the following conditions are9// met:10// * Redistributions of source code must retain the above copyright11// notice, this list of conditions and the following disclaimer.12// * Redistributions in binary form must reproduce the above13// copyright notice, this list of conditions and the following disclaimer14// in the documentation and/or other materials provided with the15// distribution.16// * Neither the name of Industrial Light & Magic nor the names of17// its contributors may be used to endorse or promote products derived18// from this software without specific prior written permission.19//20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.31//32///////////////////////////////////////////////////////////////////////////333435#ifndef INCLUDED_IMATHFRUSTUMTEST_H36#define INCLUDED_IMATHFRUSTUMTEST_H3738//-------------------------------------------------------------------------39//40// This file contains algorithms applied to or in conjunction with41// Frustum visibility testing (Imath::Frustum).42//43// Methods for frustum-based rejection of primitives are contained here.44//45//-------------------------------------------------------------------------4647#include "ImathFrustum.h"48#include "ImathBox.h"49#include "ImathSphere.h"50#include "ImathMatrix.h"51#include "ImathVec.h"5253namespace Imath {5455/////////////////////////////////////////////////////////////////56// FrustumTest57//58// template class FrustumTest<T>59//60// This is a helper class, designed to accelerate the case61// where many tests are made against the same frustum.62// That's a really common case.63//64// The acceleration is achieved by pre-computing the planes of65// the frustum, along with the ablsolute values of the plane normals.66//67686970//////////////////////////////////////////////////////////////////71// How to use this72//73// Given that you already have:74// Imath::Frustum myFrustum75// IMath::Matrix44 myCameraWorldMatrix76//77// First, make a frustum test object:78// FrustumTest myFrustumTest(myFrustum, myCameraWorldMatrix)79//80// Whenever the camera or frustum changes, call:81// myFrustumTest.setFrustum(myFrustum, myCameraWorldMatrix)82//83// For each object you want to test for visibility, call:84// myFrustumTest.isVisible(myBox)85// myFrustumTest.isVisible(mySphere)86// myFrustumTest.isVisible(myVec3)87// myFrustumTest.completelyContains(myBox)88// myFrustumTest.completelyContains(mySphere)89//9091929394//////////////////////////////////////////////////////////////////95// Explanation of how it works96//97//98// We store six world-space Frustum planes (nx, ny, nz, offset)99//100// Points: To test a Vec3 for visibility, test it against each plane101// using the normal (v dot n - offset) method. (the result is exact)102//103// BBoxes: To test an axis-aligned bbox, test the center against each plane104// using the normal (v dot n - offset) method, but offset by the105// box extents dot the abs of the plane normal. (the result is NOT106// exact, but will not return false-negatives.)107//108// Spheres: To test a sphere, test the center against each plane109// using the normal (v dot n - offset) method, but offset by the110// sphere's radius. (the result is NOT exact, but will not return111// false-negatives.)112//113//114// SPECIAL NOTE: "Where are the dot products?"115// Actual dot products are currently slow for most SIMD architectures.116// In order to keep this code optimization-ready, the dot products117// are all performed using vector adds and multipies.118//119// In order to do this, the plane equations are stored in "transpose"120// form, with the X components grouped into an X vector, etc.121//122123124template <class T>125class FrustumTest126{127public:128FrustumTest()129{130Frustum<T> frust;131Matrix44<T> cameraMat;132cameraMat.makeIdentity();133setFrustum(frust, cameraMat);134}135FrustumTest(Frustum<T> &frustum, const Matrix44<T> &cameraMat)136{137setFrustum(frustum, cameraMat);138}139140////////////////////////////////////////////////////////////////////141// setFrustum()142// This updates the frustum test with a new frustum and matrix.143// This should usually be called just once per frame.144void setFrustum(Frustum<T> &frustum, const Matrix44<T> &cameraMat);145146////////////////////////////////////////////////////////////////////147// isVisible()148// Check to see if shapes are visible.149bool isVisible(const Sphere3<T> &sphere) const;150bool isVisible(const Box<Vec3<T> > &box) const;151bool isVisible(const Vec3<T> &vec) const;152153////////////////////////////////////////////////////////////////////154// completelyContains()155// Check to see if shapes are entirely contained.156bool completelyContains(const Sphere3<T> &sphere) const;157bool completelyContains(const Box<Vec3<T> > &box) const;158159// These next items are kept primarily for debugging tools.160// It's useful for drawing the culling environment, and also161// for getting an "outside view" of the culling frustum.162Imath::Matrix44<T> cameraMat() const {return cameraMatrix;}163Imath::Frustum<T> currentFrustum() const {return currFrustum;}164165protected:166// To understand why the planes are stored this way, see167// the SPECIAL NOTE above.168Vec3<T> planeNormX[2]; // The X compunents from 6 plane equations169Vec3<T> planeNormY[2]; // The Y compunents from 6 plane equations170Vec3<T> planeNormZ[2]; // The Z compunents from 6 plane equations171172Vec3<T> planeOffsetVec[2]; // The distance offsets from 6 plane equations173174// The absolute values are stored to assist with bounding box tests.175Vec3<T> planeNormAbsX[2]; // The abs(X) compunents from 6 plane equations176Vec3<T> planeNormAbsY[2]; // The abs(X) compunents from 6 plane equations177Vec3<T> planeNormAbsZ[2]; // The abs(X) compunents from 6 plane equations178179// These are kept primarily for debugging tools.180Frustum<T> currFrustum;181Matrix44<T> cameraMatrix;182};183184185////////////////////////////////////////////////////////////////////186// setFrustum()187// This should usually only be called once per frame, or however188// often the camera moves.189template<class T>190void FrustumTest<T>::setFrustum(Frustum<T> &frustum,191const Matrix44<T> &cameraMat)192{193Plane3<T> frustumPlanes[6];194frustum.planes(frustumPlanes, cameraMat);195196// Here's where we effectively transpose the plane equations.197// We stuff all six X's into the two planeNormX vectors, etc.198for (int i = 0; i < 2; ++i)199{200int index = i * 3;201202planeNormX[i] = Vec3<T>(frustumPlanes[index + 0].normal.x,203frustumPlanes[index + 1].normal.x,204frustumPlanes[index + 2].normal.x);205planeNormY[i] = Vec3<T>(frustumPlanes[index + 0].normal.y,206frustumPlanes[index + 1].normal.y,207frustumPlanes[index + 2].normal.y);208planeNormZ[i] = Vec3<T>(frustumPlanes[index + 0].normal.z,209frustumPlanes[index + 1].normal.z,210frustumPlanes[index + 2].normal.z);211212planeNormAbsX[i] = Vec3<T>(Imath::abs(planeNormX[i].x),213Imath::abs(planeNormX[i].y),214Imath::abs(planeNormX[i].z));215planeNormAbsY[i] = Vec3<T>(Imath::abs(planeNormY[i].x),216Imath::abs(planeNormY[i].y),217Imath::abs(planeNormY[i].z));218planeNormAbsZ[i] = Vec3<T>(Imath::abs(planeNormZ[i].x),219Imath::abs(planeNormZ[i].y),220Imath::abs(planeNormZ[i].z));221222planeOffsetVec[i] = Vec3<T>(frustumPlanes[index + 0].distance,223frustumPlanes[index + 1].distance,224frustumPlanes[index + 2].distance);225}226currFrustum = frustum;227cameraMatrix = cameraMat;228}229230231////////////////////////////////////////////////////////////////////232// isVisible(Sphere)233// Returns true if any part of the sphere is inside234// the frustum.235// The result MAY return close false-positives, but not false-negatives.236//237template<typename T>238bool FrustumTest<T>::isVisible(const Sphere3<T> &sphere) const239{240Vec3<T> center = sphere.center;241Vec3<T> radiusVec = Vec3<T>(sphere.radius, sphere.radius, sphere.radius);242243// This is a vertical dot-product on three vectors at once.244Vec3<T> d0 = planeNormX[0] * center.x245+ planeNormY[0] * center.y246+ planeNormZ[0] * center.z247- radiusVec248- planeOffsetVec[0];249250if (d0.x >= 0 || d0.y >= 0 || d0.z >= 0)251return false;252253Vec3<T> d1 = planeNormX[1] * center.x254+ planeNormY[1] * center.y255+ planeNormZ[1] * center.z256- radiusVec257- planeOffsetVec[1];258259if (d1.x >= 0 || d1.y >= 0 || d1.z >= 0)260return false;261262return true;263}264265////////////////////////////////////////////////////////////////////266// completelyContains(Sphere)267// Returns true if every part of the sphere is inside268// the frustum.269// The result MAY return close false-negatives, but not false-positives.270//271template<typename T>272bool FrustumTest<T>::completelyContains(const Sphere3<T> &sphere) const273{274Vec3<T> center = sphere.center;275Vec3<T> radiusVec = Vec3<T>(sphere.radius, sphere.radius, sphere.radius);276277// This is a vertical dot-product on three vectors at once.278Vec3<T> d0 = planeNormX[0] * center.x279+ planeNormY[0] * center.y280+ planeNormZ[0] * center.z281+ radiusVec282- planeOffsetVec[0];283284if (d0.x >= 0 || d0.y >= 0 || d0.z >= 0)285return false;286287Vec3<T> d1 = planeNormX[1] * center.x288+ planeNormY[1] * center.y289+ planeNormZ[1] * center.z290+ radiusVec291- planeOffsetVec[1];292293if (d1.x >= 0 || d1.y >= 0 || d1.z >= 0)294return false;295296return true;297}298299////////////////////////////////////////////////////////////////////300// isVisible(Box)301// Returns true if any part of the axis-aligned box302// is inside the frustum.303// The result MAY return close false-positives, but not false-negatives.304//305template<typename T>306bool FrustumTest<T>::isVisible(const Box<Vec3<T> > &box) const307{308Vec3<T> center = (box.min + box.max) / 2;309Vec3<T> extent = (box.max - center);310311// This is a vertical dot-product on three vectors at once.312Vec3<T> d0 = planeNormX[0] * center.x313+ planeNormY[0] * center.y314+ planeNormZ[0] * center.z315- planeNormAbsX[0] * extent.x316- planeNormAbsY[0] * extent.y317- planeNormAbsZ[0] * extent.z318- planeOffsetVec[0];319320if (d0.x >= 0 || d0.y >= 0 || d0.z >= 0)321return false;322323Vec3<T> d1 = planeNormX[1] * center.x324+ planeNormY[1] * center.y325+ planeNormZ[1] * center.z326- planeNormAbsX[1] * extent.x327- planeNormAbsY[1] * extent.y328- planeNormAbsZ[1] * extent.z329- planeOffsetVec[1];330331if (d1.x >= 0 || d1.y >= 0 || d1.z >= 0)332return false;333334return true;335}336337////////////////////////////////////////////////////////////////////338// completelyContains(Box)339// Returns true if every part of the axis-aligned box340// is inside the frustum.341// The result MAY return close false-negatives, but not false-positives.342//343template<typename T>344bool FrustumTest<T>::completelyContains(const Box<Vec3<T> > &box) const345{346Vec3<T> center = (box.min + box.max) / 2;347Vec3<T> extent = (box.max - center);348349// This is a vertical dot-product on three vectors at once.350Vec3<T> d0 = planeNormX[0] * center.x351+ planeNormY[0] * center.y352+ planeNormZ[0] * center.z353+ planeNormAbsX[0] * extent.x354+ planeNormAbsY[0] * extent.y355+ planeNormAbsZ[0] * extent.z356- planeOffsetVec[0];357358if (d0.x >= 0 || d0.y >= 0 || d0.z >= 0)359return false;360361Vec3<T> d1 = planeNormX[1] * center.x362+ planeNormY[1] * center.y363+ planeNormZ[1] * center.z364+ planeNormAbsX[1] * extent.x365+ planeNormAbsY[1] * extent.y366+ planeNormAbsZ[1] * extent.z367- planeOffsetVec[1];368369if (d1.x >= 0 || d1.y >= 0 || d1.z >= 0)370return false;371372return true;373}374375376////////////////////////////////////////////////////////////////////377// isVisible(Vec3)378// Returns true if the point is inside the frustum.379//380template<typename T>381bool FrustumTest<T>::isVisible(const Vec3<T> &vec) const382{383// This is a vertical dot-product on three vectors at once.384Vec3<T> d0 = (planeNormX[0] * vec.x)385+ (planeNormY[0] * vec.y)386+ (planeNormZ[0] * vec.z)387- planeOffsetVec[0];388389if (d0.x >= 0 || d0.y >= 0 || d0.z >= 0)390return false;391392Vec3<T> d1 = (planeNormX[1] * vec.x)393+ (planeNormY[1] * vec.y)394+ (planeNormZ[1] * vec.z)395- planeOffsetVec[1];396397if (d1.x >= 0 || d1.y >= 0 || d1.z >= 0)398return false;399400return true;401}402403404typedef FrustumTest<float> FrustumTestf;405typedef FrustumTest<double> FrustumTestd;406407} //namespace Imath408409#endif410411412