Ver código fonte

Merge pull request #364 from Dokthar/light

Lights : added light v. sphere intersection
Kirill Vainer 10 anos atrás
pai
commit
edaf49c00c

+ 6 - 0
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;

+ 4 - 3
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;
+                        }
                     }
                 }
 

+ 6 - 0
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;

+ 15 - 0
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.
+     * <p>
+     * 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.
      * 

+ 12 - 0
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 Intersection.intersect(sphere, position, radius);
+        }
+    }
+    
     @Override
     public boolean intersectsFrustum(Camera camera, TempVars vars) {
         if (this.radius == 0) {

+ 40 - 3
jme3-core/src/main/java/com/jme3/light/SpotLight.java

@@ -32,7 +32,9 @@
 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;
@@ -188,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;
             }
         }
@@ -225,6 +225,43 @@ 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 (!Intersection.intersect(sphere, position, spotRange)) {
+                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) {

+ 92 - 0
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,45 @@ 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.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
@@ -175,5 +223,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);
     }
 }