/* * Copyright (c) 2009-2015 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jme3.light; import com.jme3.bounding.BoundingSphere; import com.jme3.math.FastMath; import com.jme3.math.Vector3f; import com.jme3.renderer.Camera; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; import com.jme3.util.TempVars; import org.junit.Before; import org.junit.Test; /** * Test light filtering for various light types. * * @author Kirill Vainer */ public class LightFilterTest { private DefaultLightFilter filter; private Camera cam; private Geometry geom; private LightList list; private void checkFilteredLights(int expected) { geom.updateGeometricState(); filter.setCamera(cam); // setCamera resets the intersection cache list.clear(); filter.filterLights(geom, list); assert list.size() == expected; } @Before public void setUp() { filter = new DefaultLightFilter(); cam = new Camera(512, 512); cam.setFrustumPerspective(45, 1, 1, 1000); cam.setLocation(Vector3f.ZERO); cam.lookAtDirection(Vector3f.UNIT_Z, Vector3f.UNIT_Y); filter.setCamera(cam); Box box = new Box(1, 1, 1); geom = new Geometry("geom", box); geom.setLocalTranslation(0, 0, 10); geom.updateGeometricState(); list = new LightList(geom); } @Test public void testAmbientFiltering() { geom.addLight(new AmbientLight()); checkFilteredLights(1); // Ambient lights must never be filtered // Test for bounding Sphere geom.setModelBound(new BoundingSphere(0.5f, Vector3f.ZERO)); checkFilteredLights(1); // Ambient lights must never be filtered } @Test public void testDirectionalFiltering() { geom.addLight(new DirectionalLight(Vector3f.UNIT_Y)); checkFilteredLights(1); // Directional lights must never be filtered // Test for bounding Sphere geom.setModelBound(new BoundingSphere(0.5f, Vector3f.ZERO)); checkFilteredLights(1); // Directional lights must never be filtered } @Test public void testPointFiltering() { PointLight pl = new PointLight(Vector3f.ZERO); geom.addLight(pl); checkFilteredLights(1); // Infinite point lights must never be filtered // Light at origin does not intersect geom which is at Z=10 pl.setRadius(1); checkFilteredLights(0); // Put it closer to geom, the very edge of the sphere touches the box. // Still not considered an intersection though. pl.setPosition(new Vector3f(0, 0, 8f)); checkFilteredLights(0); // And more close - now its an intersection. pl.setPosition(new Vector3f(0, 0, 8f + FastMath.ZERO_TOLERANCE)); checkFilteredLights(1); // Move the geometry away geom.move(0, 0, FastMath.ZERO_TOLERANCE); checkFilteredLights(0); // Test if the algorithm converts the sphere // to a box before testing the collision (incorrect) float sqrt3 = FastMath.sqrt(3); pl.setPosition(new Vector3f(2, 2, 8)); pl.setRadius(sqrt3); checkFilteredLights(0); // Make it a wee bit larger. pl.setRadius(sqrt3 + FastMath.ZERO_TOLERANCE); checkFilteredLights(1); // Rotate the camera so it is up, light is outside frustum. cam.lookAtDirection(Vector3f.UNIT_Y, Vector3f.UNIT_Y); checkFilteredLights(0); // ================================== // Tests for bounding Sphere geom.setModelBound(new BoundingSphere(1f, Vector3f.ZERO)); geom.setLocalTranslation(0, 0, 2); pl.setPosition(new Vector3f(0, 0, 2f)); // Infinite point lights must never be filtered pl.setRadius(0); checkFilteredLights(1); pl.setRadius(1f); // Put the light at the very close to the geom, // the very edge of the sphere touches the other bounding sphere // Still not considered an intersection though. pl.setPosition(new Vector3f(0, 0, 0)); checkFilteredLights(0); // And more close - now its an intersection. pl.setPosition(new Vector3f(0, 0, 0f + FastMath.ZERO_TOLERANCE)); checkFilteredLights(1); geom.setLocalTranslation(0, 0, 0); // In this case its an intersection for pointLight v. box // But not for pointLight v. sphere // Vector3f(0, 0.5f, 0.5f).normalize().mult(2) ~ >= (0.0, 1.4142135, 1.4142135) //pl.setPosition(new Vector3f(0, 0.5f, 0.5f).normalizeLocal().multLocal(2 + FastMath.ZERO_TOLERANCE)); pl.setPosition(new Vector3f(0f, 1.4142135f, 1.4142135f).multLocal(1+FastMath.ZERO_TOLERANCE)); checkFilteredLights(0); // Make the distance a wee bit closer, now its an intersection //pl.setPosition(new Vector3f(0, 0.5f, 0.5f).normalizeLocal().multLocal(2 - FastMath.ZERO_TOLERANCE)); pl.setPosition(new Vector3f(0f, 1.4142135f, 1.4142135f).multLocal(1-FastMath.ZERO_TOLERANCE)); checkFilteredLights(1); // it's a point light, also test for the other corner pl.setPosition(new Vector3f(0f, -1.4142135f, -1.4142135f).multLocal(1-FastMath.ZERO_TOLERANCE)); checkFilteredLights(0); } @Test public void testSpotFiltering() { SpotLight sl = new SpotLight(Vector3f.ZERO, Vector3f.UNIT_Z); sl.setSpotRange(0); geom.addLight(sl); checkFilteredLights(1); // Infinite spot lights are only filtered // if the geometry is outside the infinite cone. TempVars vars = TempVars.get(); try { // The spot is not touching the near plane of the camera yet, // should still be culled. sl.setSpotRange(1f - FastMath.ZERO_TOLERANCE); assert !sl.intersectsFrustum(cam, vars); // should be culled from the geometry's PoV checkFilteredLights(0); // Now it touches the near plane. sl.setSpotRange(1f); // still culled from the geometry's PoV checkFilteredLights(0); assert sl.intersectsFrustum(cam, vars); } finally { vars.release(); } // make it barely reach the geometry sl.setSpotRange(9f); checkFilteredLights(0); // make it reach the geometry (touching its bound) sl.setSpotRange(9f + FastMath.ZERO_TOLERANCE); checkFilteredLights(1); // rotate the cone a bit so it no longer faces the geom sl.setDirection(new Vector3f(0.316f, 0, 0.948f).normalizeLocal()); checkFilteredLights(0); // extent the range much farther sl.setSpotRange(20); checkFilteredLights(0); // Create box of size X=10 (double the extent) // now, the spot will touch the box. geom.setMesh(new Box(5, 1, 1)); checkFilteredLights(1); // ================================== // Tests for bounding sphere, with a radius of 1f (in the box geom) sl.setPosition(Vector3f.ZERO); sl.setDirection(Vector3f.UNIT_Z); geom.setLocalTranslation(Vector3f.ZERO); geom.setModelBound(new BoundingSphere(1f, Vector3f.ZERO)); // Infinit spot lights are only filtered // if the geometry is outside the infinite cone. sl.setSpotRange(0); checkFilteredLights(1); //the geommetry is outside the infinit cone (cone direction going away from the geom) sl.setPosition(Vector3f.UNIT_Z.mult(1+FastMath.ZERO_TOLERANCE)); checkFilteredLights(0); //place the spote ligth in the corner of the box geom, (in order to test bounding sphere) sl.setDirection(new Vector3f(1, 1, 0).normalizeLocal()); geom.setLocalTranslation(0, 0, 10); sl.setPosition(sl.getDirection().mult(-2f).add(geom.getLocalTranslation())); // make it barely reach the sphere, incorect with a box sl.setSpotRange(1f - FastMath.ZERO_TOLERANCE); checkFilteredLights(0); // make it reach the sphere sl.setSpotRange(1f + FastMath.ZERO_TOLERANCE); checkFilteredLights(1); // extent the range sl.setPosition(Vector3f.ZERO); sl.setDirection(Vector3f.UNIT_Z); sl.setSpotRange(20); checkFilteredLights(1); // rotate the cone a bit so it no longer faces the geom sl.setDirection(new Vector3f(0, 0.3f, 0.7f).normalizeLocal()); checkFilteredLights(0); // Create sphere of size X=10 (double the radius) // now, the spot will touch the sphere. geom.setModelBound(new BoundingSphere(5f, Vector3f.ZERO)); checkFilteredLights(1); } }