From cfdb9a87596cb5acd63570afffc0a824e0ba039b Mon Sep 17 00:00:00 2001 From: Dokthar Date: Wed, 7 Oct 2015 22:25:36 +0200 Subject: [PATCH 1/4] Lights (see #362) : added light v. sphere intersection, and implementations of intersectsSphere(), second attempt --- .../java/com/jme3/light/AmbientLight.java | 6 +++ .../com/jme3/light/DefaultLightFilter.java | 7 ++-- .../java/com/jme3/light/DirectionalLight.java | 6 +++ .../src/main/java/com/jme3/light/Light.java | 15 +++++++ .../main/java/com/jme3/light/PointLight.java | 12 ++++++ .../main/java/com/jme3/light/SpotLight.java | 42 ++++++++++++++++++- 6 files changed, 83 insertions(+), 5 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/light/AmbientLight.java b/jme3-core/src/main/java/com/jme3/light/AmbientLight.java index 58676851c..a2dc1cd38 100644 --- a/jme3-core/src/main/java/com/jme3/light/AmbientLight.java +++ b/jme3-core/src/main/java/com/jme3/light/AmbientLight.java @@ -32,6 +32,7 @@ package com.jme3.light; import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingSphere; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; import com.jme3.renderer.Camera; @@ -62,6 +63,11 @@ public class AmbientLight extends Light { return true; } + @Override + public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) { + return true; + } + @Override public boolean intersectsFrustum(Camera camera, TempVars vars) { return true; diff --git a/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java b/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java index c8456529d..5d07e1a8d 100644 --- a/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java +++ b/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2014 jMonkeyEngine + * Copyright (c) 2009-2015 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -78,8 +78,9 @@ public final class DefaultLightFilter implements LightFilter { } } else if (bv instanceof BoundingSphere) { if (!Float.isInfinite( ((BoundingSphere)bv).getRadius() )) { - // Non-infinite bounding sphere... Not supported yet. - throw new UnsupportedOperationException("Only AABB supported for now"); + if (!light.intersectsSphere((BoundingSphere)bv, vars)) { + continue; + } } } diff --git a/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java b/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java index b8e1a1979..b1abb65cf 100644 --- a/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java +++ b/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java @@ -32,6 +32,7 @@ package com.jme3.light; import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingSphere; import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; @@ -116,6 +117,11 @@ public class DirectionalLight extends Light { return true; } + @Override + public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) { + return true; + } + @Override public boolean intersectsFrustum(Camera camera, TempVars vars) { return true; diff --git a/jme3-core/src/main/java/com/jme3/light/Light.java b/jme3-core/src/main/java/com/jme3/light/Light.java index bac775aeb..39a30980b 100644 --- a/jme3-core/src/main/java/com/jme3/light/Light.java +++ b/jme3-core/src/main/java/com/jme3/light/Light.java @@ -32,6 +32,7 @@ package com.jme3.light; import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingSphere; import com.jme3.export.*; import com.jme3.math.ColorRGBA; import com.jme3.renderer.Camera; @@ -196,6 +197,20 @@ public abstract class Light implements Savable, Cloneable { */ public abstract boolean intersectsBox(BoundingBox box, TempVars vars); + /** + * Determines if the light intersects with the given bounding sphere. + *

+ * For non-local lights, such as {@link DirectionalLight directional lights}, + * {@link AmbientLight ambient lights}, or {@link PointLight point lights} + * without influence radius, this method should always return true. + * + * @param sphere The sphere to check intersection against. + * @param vars TempVars in case it is needed. + * + * @return True if the light intersects the sphere, false otherwise. + */ + public abstract boolean intersectsSphere(BoundingSphere sphere, TempVars vars); + /** * Determines if the light intersects with the given camera frustum. * diff --git a/jme3-core/src/main/java/com/jme3/light/PointLight.java b/jme3-core/src/main/java/com/jme3/light/PointLight.java index 1f515e71c..1468d1ba1 100644 --- a/jme3-core/src/main/java/com/jme3/light/PointLight.java +++ b/jme3-core/src/main/java/com/jme3/light/PointLight.java @@ -32,12 +32,14 @@ package com.jme3.light; import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingSphere; import com.jme3.bounding.BoundingVolume; import com.jme3.bounding.Intersection; import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; +import com.jme3.math.FastMath; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; import com.jme3.renderer.Camera; @@ -195,6 +197,16 @@ public class PointLight extends Light { } } + @Override + public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) { + if (this.radius == 0) { + return true; + } else { + // Sphere v. sphere collision + return sphere.getCenter().subtract(position).lengthSquared() < FastMath.sqr(radius + sphere.getRadius()); + } + } + @Override public boolean intersectsFrustum(Camera camera, TempVars vars) { if (this.radius == 0) { diff --git a/jme3-core/src/main/java/com/jme3/light/SpotLight.java b/jme3-core/src/main/java/com/jme3/light/SpotLight.java index de24280ab..444d58b3a 100644 --- a/jme3-core/src/main/java/com/jme3/light/SpotLight.java +++ b/jme3-core/src/main/java/com/jme3/light/SpotLight.java @@ -32,6 +32,7 @@ package com.jme3.light; import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingSphere; import com.jme3.bounding.BoundingVolume; import com.jme3.export.*; import com.jme3.math.ColorRGBA; @@ -225,12 +226,49 @@ public class SpotLight extends Light { return false; } + @Override + public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) { + if (this.spotRange > 0f) { + // Check spot range first. + // Sphere v. sphere collision + if (sphere.getCenter().subtract(position).lengthSquared() >= FastMath.sqr(spotRange + sphere.getRadius())) { + return false; + } + } + + float otherRadiusSquared = FastMath.sqr(sphere.getRadius()); + float otherRadius = sphere.getRadius(); + + // Check if sphere is within spot angle. + // Cone v. sphere collision. + Vector3f E = direction.mult(otherRadius * outerAngleSinRcp, vars.vect1); + Vector3f U = position.subtract(E, vars.vect2); + Vector3f D = sphere.getCenter().subtract(U, vars.vect3); + + float dsqr = D.dot(D); + float e = direction.dot(D); + + if (e > 0f && e * e >= dsqr * outerAngleCosSqr) { + D = sphere.getCenter().subtract(position, vars.vect3); + dsqr = D.dot(D); + e = -direction.dot(D); + + if (e > 0f && e * e >= dsqr * outerAngleSinSqr) { + return dsqr <= otherRadiusSquared; + } else { + return true; + } + } + + return false; + } + @Override public boolean intersectsFrustum(Camera cam, TempVars vars) { if (spotRange == 0) { // The algorithm below does not support infinite spot range. - return true; - } + return true; + } Vector3f farPoint = vars.vect1.set(position).addLocal(vars.vect2.set(direction).multLocal(spotRange)); for (int i = 5; i >= 0; i--) { //check origin against the plane From 505aa23048cdc8eeb83eadb454d224f484aac69d Mon Sep 17 00:00:00 2001 From: Dokthar Date: Wed, 14 Oct 2015 20:30:20 +0200 Subject: [PATCH 2/4] light : added unit tests for the new support of bounding spheres intersections (for lightFilter) --- .../main/java/com/jme3/light/SpotLight.java | 4 +- .../java/com/jme3/light/LightFilterTest.java | 91 +++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/light/SpotLight.java b/jme3-core/src/main/java/com/jme3/light/SpotLight.java index 444d58b3a..bd1c03c6e 100644 --- a/jme3-core/src/main/java/com/jme3/light/SpotLight.java +++ b/jme3-core/src/main/java/com/jme3/light/SpotLight.java @@ -267,8 +267,8 @@ public class SpotLight extends Light { public boolean intersectsFrustum(Camera cam, TempVars vars) { if (spotRange == 0) { // The algorithm below does not support infinite spot range. - return true; - } + return true; + } Vector3f farPoint = vars.vect1.set(position).addLocal(vars.vect2.set(direction).multLocal(spotRange)); for (int i = 5; i >= 0; i--) { //check origin against the plane diff --git a/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java b/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java index 0d1e71988..d4fc18db9 100644 --- a/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java +++ b/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java @@ -31,6 +31,7 @@ */ package com.jme3.light; +import com.jme3.bounding.BoundingSphere; import com.jme3.math.FastMath; import com.jme3.math.Vector3f; import com.jme3.renderer.Camera; @@ -81,12 +82,20 @@ public class LightFilterTest { 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 @@ -127,6 +136,44 @@ public class LightFilterTest { // 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.setLocalTranslation(0, 0, 0); + + // Infinite point lights must never be filtered + pl.setRadius(0); + checkFilteredLights(1); + + pl.setRadius(1f); + geom.setModelBound(new BoundingSphere(1f, Vector3f.ZERO)); + + // 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, -2f)); + checkFilteredLights(0); + + // And more close - now its an intersection. + pl.setPosition(new Vector3f(0, 0, 0f)); + checkFilteredLights(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 @@ -175,5 +222,49 @@ public class LightFilterTest { // 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); } } From 4be09e3505b3fd07e93d747ac872a44e7155b775 Mon Sep 17 00:00:00 2001 From: Dokthar Date: Thu, 15 Oct 2015 19:27:50 +0200 Subject: [PATCH 3/4] light : replaced duplicated code by methods from Intersection --- jme3-core/src/main/java/com/jme3/light/PointLight.java | 2 +- jme3-core/src/main/java/com/jme3/light/SpotLight.java | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/light/PointLight.java b/jme3-core/src/main/java/com/jme3/light/PointLight.java index 1468d1ba1..4b5224c30 100644 --- a/jme3-core/src/main/java/com/jme3/light/PointLight.java +++ b/jme3-core/src/main/java/com/jme3/light/PointLight.java @@ -203,7 +203,7 @@ public class PointLight extends Light { return true; } else { // Sphere v. sphere collision - return sphere.getCenter().subtract(position).lengthSquared() < FastMath.sqr(radius + sphere.getRadius()); + return Intersection.intersect(sphere, position, radius); } } diff --git a/jme3-core/src/main/java/com/jme3/light/SpotLight.java b/jme3-core/src/main/java/com/jme3/light/SpotLight.java index bd1c03c6e..bc1335b5b 100644 --- a/jme3-core/src/main/java/com/jme3/light/SpotLight.java +++ b/jme3-core/src/main/java/com/jme3/light/SpotLight.java @@ -34,6 +34,7 @@ package com.jme3.light; import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingSphere; import com.jme3.bounding.BoundingVolume; +import com.jme3.bounding.Intersection; import com.jme3.export.*; import com.jme3.math.ColorRGBA; import com.jme3.math.FastMath; @@ -189,9 +190,7 @@ public class SpotLight extends Light { if (this.spotRange > 0f) { // Check spot range first. // Sphere v. box collision - if (FastMath.abs(box.getCenter().x - position.x) >= spotRange + box.getXExtent() - || FastMath.abs(box.getCenter().y - position.y) >= spotRange + box.getYExtent() - || FastMath.abs(box.getCenter().z - position.z) >= spotRange + box.getZExtent()) { + if (!Intersection.intersect(box, position, spotRange)) { return false; } } @@ -231,7 +230,7 @@ public class SpotLight extends Light { if (this.spotRange > 0f) { // Check spot range first. // Sphere v. sphere collision - if (sphere.getCenter().subtract(position).lengthSquared() >= FastMath.sqr(spotRange + sphere.getRadius())) { + if (!Intersection.intersect(sphere, position, spotRange)) { return false; } } From 071ad5c6183f588bc8d1eeedd7ae97901c94938b Mon Sep 17 00:00:00 2001 From: Dokthar Date: Fri, 16 Oct 2015 20:17:57 +0200 Subject: [PATCH 4/4] light : fixed pointLight v. bounding sphere unit test --- .../java/com/jme3/light/LightFilterTest.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java b/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java index d4fc18db9..447682964 100644 --- a/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java +++ b/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java @@ -139,25 +139,26 @@ public class LightFilterTest { // ================================== // Tests for bounding Sphere - geom.setLocalTranslation(0, 0, 0); - + 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); - geom.setModelBound(new BoundingSphere(1f, Vector3f.ZERO)); - // 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, -2f)); + pl.setPosition(new Vector3f(0, 0, 0)); checkFilteredLights(0); // And more close - now its an intersection. - pl.setPosition(new Vector3f(0, 0, 0f)); - checkFilteredLights(0); - + 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)