diff --git a/jme3-core/src/main/java/com/jme3/bounding/Intersection.java b/jme3-core/src/main/java/com/jme3/bounding/Intersection.java index c627e23e5..37beede01 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/Intersection.java +++ b/jme3-core/src/main/java/com/jme3/bounding/Intersection.java @@ -34,6 +34,7 @@ package com.jme3.bounding; import com.jme3.math.FastMath; import com.jme3.math.Plane; import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; import com.jme3.util.TempVars; import static java.lang.Math.max; import static java.lang.Math.min; @@ -107,6 +108,15 @@ public final class Intersection { } } + public static boolean intersect(Camera camera, Vector3f center,float radius){ + for (int i = 5; i >= 0; i--) { + if (camera.getWorldPlane(i).pseudoDistance(center) <= -radius) { + return false; + } + } + return true; + } + // private boolean axisTest(float a, float b, float fa, float fb, Vector3f v0, Vector3f v1, ) // private boolean axisTestX01(float a, float b, float fa, float fb, // Vector3f center, Vector3f ext, diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java index aff4dd0b9..a8fa7de37 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java @@ -38,14 +38,9 @@ import com.jme3.environment.util.EnvMapUtils; import com.jme3.light.LightProbe; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; -import com.jme3.renderer.Camera; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; +import com.jme3.renderer.*; import com.jme3.scene.Spatial; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; -import com.jme3.texture.Texture2D; -import com.jme3.texture.TextureCubeMap; +import com.jme3.texture.*; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; import com.jme3.util.MipMapGenerator; @@ -119,7 +114,7 @@ public class EnvironmentCamera extends BaseAppState { private final List jobs = new ArrayList(); /** - * Creates an EnvironmentCamera with a size of 128 + * Creates an EnvironmentCamera with a size of 256 */ public EnvironmentCamera() { } @@ -322,7 +317,7 @@ public class EnvironmentCamera extends BaseAppState { final Camera offCamera = new Camera(mapSize, mapSize); offCamera.setLocation(worldPos); offCamera.setAxes(axisX, axisY, axisZ); - offCamera.setFrustumPerspective(90f, 1f, 1, 1000); + offCamera.setFrustumPerspective(90f, 1f, 0.1f, 1000); offCamera.setLocation(position); return offCamera; } diff --git a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java index 9a4259804..859472fe1 100644 --- a/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java +++ b/jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java @@ -32,6 +32,7 @@ package com.jme3.environment; import com.jme3.app.Application; +import com.jme3.asset.AssetManager; import com.jme3.environment.generation.*; import com.jme3.environment.util.EnvMapUtils; import com.jme3.light.LightProbe; @@ -204,6 +205,26 @@ public class LightProbeFactory { } } + /** + * For debuging porpose only + * Will return a Node meant to be added to a GUI presenting the 2 cube maps in a cross pattern with all the mip maps. + * + * @param manager the asset manager + * @return a debug node + */ + public static Node getDebugGui(AssetManager manager, LightProbe probe) { + if (!probe.isReady()) { + throw new UnsupportedOperationException("This EnvProbe is not ready yet, try to test isReady()"); + } + + Node debugNode = new Node("debug gui probe"); + Node debugPfemCm = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(probe.getPrefilteredEnvMap(), manager); + debugNode.attachChild(debugPfemCm); + debugPfemCm.setLocalTranslation(520, 0, 0); + + return debugNode; + } + /** * An inner class to keep the state of a generation process */ diff --git a/jme3-core/src/main/java/com/jme3/environment/util/LightsDebugState.java b/jme3-core/src/main/java/com/jme3/environment/util/LightsDebugState.java index 3d41148c4..a9d2a9e46 100644 --- a/jme3-core/src/main/java/com/jme3/environment/util/LightsDebugState.java +++ b/jme3-core/src/main/java/com/jme3/environment/util/LightsDebugState.java @@ -34,9 +34,8 @@ package com.jme3.environment.util; import com.jme3.app.Application; import com.jme3.app.state.BaseAppState; import com.jme3.bounding.BoundingSphere; +import com.jme3.light.*; import com.jme3.material.Material; -import com.jme3.light.LightProbe; -import com.jme3.light.Light; import com.jme3.renderer.RenderManager; import com.jme3.scene.Geometry; import com.jme3.scene.Node; @@ -68,7 +67,7 @@ public class LightsDebugState extends BaseAppState { @Override protected void initialize(Application app) { debugNode = new Node("Environment debug Node"); - Sphere s = new Sphere(16, 16, 1); + Sphere s = new Sphere(16, 16, 0.15f); debugGeom = new Geometry("debugEnvProbe", s); debugMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/reflect.j3md"); debugGeom.setMaterial(debugMaterial); @@ -80,6 +79,16 @@ public class LightsDebugState extends BaseAppState { @Override public void update(float tpf) { + if(!isEnabled()){ + return; + } + updateLights(scene); + debugNode.updateLogicalState(tpf); + debugNode.updateGeometricState(); + cleanProbes(); + } + + public void updateLights(Spatial scene) { for (Light light : scene.getWorldLightList()) { switch (light.getType()) { @@ -101,16 +110,18 @@ public class LightsDebugState extends BaseAppState { m.setTexture("CubeMap", probe.getPrefilteredEnvMap()); } n.setLocalTranslation(probe.getPosition()); - n.getChild(1).setLocalScale(((BoundingSphere) probe.getBounds()).getRadius()); + n.getChild(1).setLocalScale(probe.getArea().getRadius()); break; default: break; } } - debugNode.updateLogicalState(tpf); - debugNode.updateGeometricState(); - cleanProbes(); - + if( scene instanceof Node){ + Node n = (Node)scene; + for (Spatial spatial : n.getChildren()) { + updateLights(spatial); + } + } } /** @@ -138,6 +149,9 @@ public class LightsDebugState extends BaseAppState { @Override public void render(RenderManager rm) { + if(!isEnabled()){ + return; + } rm.renderScene(debugNode, getApplication().getViewPort()); } 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 84cdc17fc..df735095e 100644 --- a/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java +++ b/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java @@ -43,10 +43,10 @@ public final class DefaultLightFilter implements LightFilter { private Camera camera; private final HashSet processedLights = new HashSet(); - private final LightProbeBlendingStrategy probeBlendStrat; + private LightProbeBlendingStrategy probeBlendStrat; public DefaultLightFilter() { - probeBlendStrat = new BasicProbeBlendingStrategy(); + probeBlendStrat = new WeightedProbeBlendingStrategy(); } public DefaultLightFilter(LightProbeBlendingStrategy probeBlendStrat) { @@ -113,5 +113,9 @@ public final class DefaultLightFilter implements LightFilter { vars.release(); } } - + + public void setLightProbeBlendingStrategy(LightProbeBlendingStrategy strategy){ + probeBlendStrat = strategy; + } + } diff --git a/jme3-core/src/main/java/com/jme3/light/LightProbe.java b/jme3-core/src/main/java/com/jme3/light/LightProbe.java index 2b962fc97..d6d716877 100644 --- a/jme3-core/src/main/java/com/jme3/light/LightProbe.java +++ b/jme3-core/src/main/java/com/jme3/light/LightProbe.java @@ -31,24 +31,16 @@ */ package com.jme3.light; -import com.jme3.asset.AssetManager; -import com.jme3.bounding.BoundingBox; -import com.jme3.bounding.BoundingSphere; -import com.jme3.bounding.BoundingVolume; +import com.jme3.bounding.*; import com.jme3.environment.EnvironmentCamera; import com.jme3.environment.LightProbeFactory; -import com.jme3.environment.util.EnvMapUtils; -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.export.Savable; -import com.jme3.math.Vector3f; +import com.jme3.export.*; +import com.jme3.math.*; import com.jme3.renderer.Camera; -import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.texture.TextureCubeMap; import com.jme3.util.TempVars; + import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -56,7 +48,7 @@ import java.util.logging.Logger; /** * A LightProbe is not exactly a light. It holds environment map information used for Image Based Lighting. * This is used for indirect lighting in the Physically Based Rendering pipeline. - * + * * A light probe has a position in world space. This is the position from where the Environment Map are rendered. * There are two environment data structure held by the LightProbe : * - The irradiance spherical harmonics factors (used for indirect diffuse lighting in the PBR pipeline). @@ -64,10 +56,10 @@ import java.util.logging.Logger; * Note that when instantiating the LightProbe, both of those structures are null. * To compute them see {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)} * and {@link EnvironmentCamera}. - * - * The light probe has an area of effect that is a bounding volume centered on its position. (for now only Bounding spheres are supported). - * - * A LightProbe will only be taken into account when it's marked as ready. + * + * The light probe has an area of effect centered on its position. It can have a Spherical area or an Oriented Box area + * + * A LightProbe will only be taken into account when it's marked as ready and enabled. * A light probe is ready when it has valid environment map data set. * Note that you should never call setReady yourself. * @@ -78,20 +70,25 @@ import java.util.logging.Logger; public class LightProbe extends Light implements Savable { private static final Logger logger = Logger.getLogger(LightProbe.class.getName()); + public static final Matrix4f FALLBACK_MATRIX = new Matrix4f(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1); private Vector3f[] shCoeffs; private TextureCubeMap prefilteredEnvMap; - private BoundingVolume bounds = new BoundingSphere(1.0f, Vector3f.ZERO); + private ProbeArea area = new SphereProbeArea(Vector3f.ZERO, 1.0f); private boolean ready = false; private Vector3f position = new Vector3f(); - private Node debugNode; private int nbMipMaps; + public enum AreaType{ + Spherical, + OrientedBox + } + /** - * Empty constructor used for serialization. + * Empty constructor used for serialization. * You should never call it, use {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)} instead */ - public LightProbe() { + public LightProbe() { } /** @@ -104,13 +101,59 @@ public class LightProbe extends Light implements Savable { } /** - * Sets the prefiltered environment map - * @param prefileteredEnvMap the prefiltered environment map + * Sets the prefiltered environment map + * @param prefileteredEnvMap the prefiltered environment map */ public void setPrefilteredMap(TextureCubeMap prefileteredEnvMap) { this.prefilteredEnvMap = prefileteredEnvMap; } + /** + * Returns the data to send to the shader. + * This is a column major matrix that is not a classic transform matrix, it's laid out in a particular way + // 3x3 rot mat| + // 0 1 2 | 3 + // 0 | ax bx cx | px | ) + // 1 | ay by cy | py | probe position + // 2 | az bz cz | pz | ) + // --|----------| + // 3 | sx sy sz sp | -> 1/probe radius + nbMipMaps + // --scale-- + *

+ * (ax, ay, az) is the pitch rotation axis + * (bx, by, bz) is the yaw rotation axis + * (cx, cy, cz) is the roll rotation axis + * Like in a standard 3x3 rotation matrix. + * It's also the valid rotation matrix of the probe in world space. + * Note that for the Spherical Probe area this part is a 3x3 identity matrix. + *

+ * (px, py, pz) is the position of the center of the probe in world space + * Like in a valid 4x4 transform matrix. + *

+ * (sx, sy, sy) is the extent of the probe ( the scale ) + * In a standard transform matrix the scale is applied to the rotation matrix part. + * In the shader we need the rotation and the scale to be separated, doing this avoid to extract + * the scale from a classic transform matrix in the shader + *

+ * (sp) is a special entry, it contains the packed number of mip maps of the probe and the inverse radius for the probe. + * since the inverse radius in lower than 1, it's packed in the decimal part of the float. + * The number of mip maps is packed in the integer part of the float. + * (ie: for 6 mip maps and a radius of 3, sp= 6.3333333) + *

+ * The radius is obvious for a SphereProbeArea, + * but in the case of a OrientedBoxProbeArea it's the max of the extent vector's components. + */ + public Matrix4f getUniformMatrix(){ + + Matrix4f mat = area.getUniformMatrix(); + + // setting the (sp) entry of the matrix + mat.m33 = nbMipMaps + 1f / area.getRadius(); + + return mat; + } + + @Override public void write(JmeExporter ex) throws IOException { super.write(ex); @@ -118,7 +161,7 @@ public class LightProbe extends Light implements Savable { oc.write(shCoeffs, "shCoeffs", null); oc.write(prefilteredEnvMap, "prefilteredEnvMap", null); oc.write(position, "position", null); - oc.write(bounds, "bounds", new BoundingSphere(1.0f, Vector3f.ZERO)); + oc.write(area, "area", new SphereProbeArea(Vector3f.ZERO, 1.0f)); oc.write(ready, "ready", false); oc.write(nbMipMaps, "nbMipMaps", 0); } @@ -127,10 +170,16 @@ public class LightProbe extends Light implements Savable { public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule ic = im.getCapsule(this); - + prefilteredEnvMap = (TextureCubeMap) ic.readSavable("prefilteredEnvMap", null); position = (Vector3f) ic.readSavable("position", null); - bounds = (BoundingVolume) ic.readSavable("bounds", new BoundingSphere(1.0f, Vector3f.ZERO)); + area = (ProbeArea)ic.readSavable("area", null); + if(area == null) { + // retro compat + BoundingSphere bounds = (BoundingSphere) ic.readSavable("bounds", new BoundingSphere(1.0f, Vector3f.ZERO)); + area = new SphereProbeArea(bounds.getCenter(), bounds.getRadius()); + } + area.setCenter(position); nbMipMaps = ic.readInt("nbMipMaps", 0); ready = ic.readBoolean("ready", false); @@ -146,25 +195,49 @@ public class LightProbe extends Light implements Savable { } } + /** * returns the bounding volume of this LightProbe * @return a bounding volume. + * @deprecated use {@link LightProbe#getArea()} */ + @Deprecated public BoundingVolume getBounds() { - return bounds; + return new BoundingSphere(((SphereProbeArea)area).getRadius(), ((SphereProbeArea)area).getCenter()); } - + /** * Sets the bounds of this LightProbe - * Note that for now only BoundingSphere is supported and this method will + * Note that for now only BoundingSphere is supported and this method will * throw an UnsupportedOperationException with any other BoundingVolume type * @param bounds the bounds of the LightProbe + * @deprecated */ + @Deprecated public void setBounds(BoundingVolume bounds) { - if( bounds.getType()!= BoundingVolume.Type.Sphere){ - throw new UnsupportedOperationException("For not only BoundingSphere are suported for LightProbe"); + } + + public ProbeArea getArea() { + return area; + } + + public void setAreaType(AreaType type){ + switch (type){ + case Spherical: + area = new SphereProbeArea(Vector3f.ZERO, 1.0f); + break; + case OrientedBox: + area = new OrientedBoxProbeArea(new Transform()); + area.setCenter(position); + break; + } + } + + public AreaType getAreaType(){ + if(area instanceof SphereProbeArea){ + return AreaType.Spherical; } - this.bounds = bounds; + return AreaType.OrientedBox; } /** @@ -186,27 +259,6 @@ public class LightProbe extends Light implements Savable { this.ready = ready; } - /** - * For debuging porpose only - * Will return a Node meant to be added to a GUI presenting the 2 cube maps in a cross pattern with all the mip maps. - * - * @param manager the asset manager - * @return a debug node - */ - public Node getDebugGui(AssetManager manager) { - if (!ready) { - throw new UnsupportedOperationException("This EnvProbe is not ready yet, try to test isReady()"); - } - if (debugNode == null) { - debugNode = new Node("debug gui probe"); - Node debugPfemCm = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(getPrefilteredEnvMap(), manager); - debugNode.attachChild(debugPfemCm); - debugPfemCm.setLocalTranslation(520, 0, 0); - } - - return debugNode; - } - public Vector3f[] getShCoeffs() { return shCoeffs; } @@ -229,7 +281,7 @@ public class LightProbe extends Light implements Savable { */ public void setPosition(Vector3f position) { this.position.set(position); - getBounds().setCenter(position); + area.setCenter(position); } public int getNbMipMaps() { @@ -242,12 +294,17 @@ public class LightProbe extends Light implements Savable { @Override public boolean intersectsBox(BoundingBox box, TempVars vars) { - return getBounds().intersectsBoundingBox(box); + return area.intersectsBox(box, vars); } @Override public boolean intersectsFrustum(Camera camera, TempVars vars) { - return camera.contains(bounds) != Camera.FrustumIntersect.Outside; + return area.intersectsFrustum(camera, vars); + } + + @Override + public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) { + return area.intersectsSphere(sphere, vars); } @Override @@ -267,14 +324,8 @@ public class LightProbe extends Light implements Savable { @Override public String toString() { - return "Light Probe : " + name + " at " + position + " / " + bounds; + return "Light Probe : " + name + " at " + position + " / " + area; } - @Override - public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) { - return getBounds().intersectsSphere(sphere); - } - - } diff --git a/jme3-core/src/main/java/com/jme3/light/LightProbeBlendingProcessor.java b/jme3-core/src/main/java/com/jme3/light/LightProbeBlendingProcessor.java deleted file mode 100644 index 572fd5c18..000000000 --- a/jme3-core/src/main/java/com/jme3/light/LightProbeBlendingProcessor.java +++ /dev/null @@ -1,214 +0,0 @@ - /* - * 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.post.SceneProcessor; -import com.jme3.profile.AppProfiler; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; -import com.jme3.renderer.queue.RenderQueue; -import com.jme3.scene.Spatial; -import com.jme3.texture.FrameBuffer; -import com.jme3.util.TempVars; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * this processor allows to blend several light probes maps together according to a Point of Interest. - * This is all based on this article by Sebastien lagarde - * https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ - * @author Nehon - */ -public class LightProbeBlendingProcessor implements SceneProcessor { - - private ViewPort viewPort; - private LightFilter prevFilter; - private RenderManager renderManager; - private LightProbe probe = new LightProbe(); - private Spatial poi; - private AppProfiler prof; - - public LightProbeBlendingProcessor(Spatial poi) { - this.poi = poi; - } - - @Override - public void initialize(RenderManager rm, ViewPort vp) { - viewPort = vp; - renderManager = rm; - prevFilter = rm.getLightFilter(); - rm.setLightFilter(new PoiLightProbeLightFilter(this)); - } - - @Override - public void reshape(ViewPort vp, int w, int h) { - - } - - @Override - public boolean isInitialized() { - return viewPort != null; - } - - @Override - public void preFrame(float tpf) { - - } - - /** 1. For POI take a spatial in the constructor and make all calculation against its world pos - * - Alternatively compute an arbitrary POI by casting rays from the camera - * (one in the center and one for each corner and take the median point) - * 2. Take the 4 most weighted probes for default. Maybe allow the user to change this - * 3. For the inner influence radius take half of the radius for a start we'll see then how to change this. - * - */ - @Override - public void postQueue(RenderQueue rq) { - List blendFactors = new ArrayList(); - float sumBlendFactors = computeBlendFactors(blendFactors); - - //Sort blend factors according to their weight - Collections.sort(blendFactors); - - //normalize blend factors; - float normalizer = 1f / sumBlendFactors; - for (BlendFactor blendFactor : blendFactors) { - blendFactor.ndf *= normalizer; - // System.err.println(blendFactor); - } - - - //for now just pick the first probe. - if(!blendFactors.isEmpty()){ - probe = blendFactors.get(0).lightProbe; - }else{ - probe = null; - } - } - - private float computeBlendFactors(List blendFactors) { - float sumBlendFactors = 0; - for (Spatial scene : viewPort.getScenes()) { - for (Light light : scene.getWorldLightList()) { - if(light.getType() == Light.Type.Probe){ - LightProbe p = (LightProbe)light; - TempVars vars = TempVars.get(); - boolean intersect = p.intersectsFrustum(viewPort.getCamera(), vars); - vars.release(); - //check if the probe is inside the camera frustum - if(intersect){ - - //is the poi inside the bounds of this probe - if(poi.getWorldBound().intersects(p.getBounds())){ - - //computing the distance as we need it to check if th epoi in in the inner radius and later to compute the weight - float outerRadius = ((BoundingSphere)p.getBounds()).getRadius(); - float innerRadius = outerRadius * 0.5f; - float distance = p.getBounds().getCenter().distance(poi.getWorldTranslation()); - - // if the poi in inside the inner range of this probe, then this probe is the only one that matters. - if( distance < innerRadius ){ - blendFactors.clear(); - blendFactors.add(new BlendFactor(p, 1.0f)); - return 1.0f; - } - //else we need to compute the weight of this probe and collect it for blending - float ndf = (distance - innerRadius) / (outerRadius - innerRadius); - sumBlendFactors += ndf; - blendFactors.add(new BlendFactor(p, ndf)); - } - } - } - } - } - return sumBlendFactors; - } - - @Override - public void postFrame(FrameBuffer out) { - - } - - @Override - public void cleanup() { - viewPort = null; - renderManager.setLightFilter(prevFilter); - } - - public void populateProbe(LightList lightList){ - if(probe != null && probe.isReady()){ - lightList.add(probe); - } - } - - public Spatial getPoi() { - return poi; - } - - public void setPoi(Spatial poi) { - this.poi = poi; - } - - @Override - public void setProfiler(AppProfiler profiler) { - this.prof = profiler; - } - - private class BlendFactor implements Comparable{ - - LightProbe lightProbe; - float ndf; - - public BlendFactor(LightProbe lightProbe, float ndf) { - this.lightProbe = lightProbe; - this.ndf = ndf; - } - - @Override - public String toString() { - return "BlendFactor{" + "lightProbe=" + lightProbe + ", ndf=" + ndf + '}'; - } - - @Override - public int compareTo(BlendFactor o) { - if(o.ndf > ndf){ - return -1; - }else if(o.ndf < ndf){ - return 1; - } - return 0; - } - - } -} diff --git a/jme3-core/src/main/java/com/jme3/light/OrientedBoxProbeArea.java b/jme3-core/src/main/java/com/jme3/light/OrientedBoxProbeArea.java new file mode 100644 index 000000000..3b35c54a2 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/light/OrientedBoxProbeArea.java @@ -0,0 +1,249 @@ +package com.jme3.light; + +import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingSphere; +import com.jme3.export.*; +import com.jme3.math.*; +import com.jme3.renderer.Camera; +import com.jme3.util.TempVars; + +import java.io.IOException; + +public class OrientedBoxProbeArea implements ProbeArea { + private Transform transform = new Transform(); + + /** + * @see LightProbe#getUniformMatrix() + * for this Area type, the matrix is updated when the probe is transformed, + * and its data is used for bound checks in the light culling process. + */ + private Matrix4f uniformMatrix = new Matrix4f(); + + public OrientedBoxProbeArea() { + } + + public OrientedBoxProbeArea(Transform transform) { + transform.set(transform); + updateMatrix(); + } + + @Override + public boolean intersectsBox(BoundingBox box, TempVars vars) { + + Vector3f axis1 = getScaledAxis(0, vars.vect1); + Vector3f axis2 = getScaledAxis(1, vars.vect2); + Vector3f axis3 = getScaledAxis(2, vars.vect3); + + Vector3f tn = vars.vect4; + Plane p = vars.plane; + Vector3f c = box.getCenter(); + + p.setNormal(0, 0, -1); + p.setConstant(-(c.z + box.getZExtent())); + if (!insidePlane(p, axis1, axis2, axis3, tn)) return false; + + p.setNormal(0, 0, 1); + p.setConstant(c.z - box.getZExtent()); + if (!insidePlane(p, axis1, axis2, axis3, tn)) return false; + + + p.setNormal(0, -1, 0); + p.setConstant(-(c.y + box.getYExtent())); + if (!insidePlane(p, axis1, axis2, axis3, tn)) return false; + + p.setNormal(0, 1, 0); + p.setConstant(c.y - box.getYExtent()); + if (!insidePlane(p, axis1, axis2, axis3, tn)) return false; + + p.setNormal(-1, 0, 0); + p.setConstant(-(c.x + box.getXExtent())); + if (!insidePlane(p, axis1, axis2, axis3, tn)) return false; + + p.setNormal(1, 0, 0); + p.setConstant(c.x - box.getXExtent()); + if (!insidePlane(p, axis1, axis2, axis3, tn)) return false; + + return true; + + } + + @Override + public float getRadius() { + return Math.max(Math.max(transform.getScale().x, transform.getScale().y), transform.getScale().z); + } + + @Override + public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) { + + Vector3f closestPoint = getClosestPoint(vars, sphere.getCenter()); + // check if the point intersects with the sphere bound + if (sphere.intersects(closestPoint)) { + return true; + } + return false; + } + + @Override + public boolean intersectsFrustum(Camera camera, TempVars vars) { + + // extract the scaled axis + // this allows a small optimization. + Vector3f axis1 = getScaledAxis(0, vars.vect1); + Vector3f axis2 = getScaledAxis(1, vars.vect2); + Vector3f axis3 = getScaledAxis(2, vars.vect3); + + Vector3f tn = vars.vect4; + + for (int i = 5; i >= 0; i--) { + Plane p = camera.getWorldPlane(i); + if (!insidePlane(p, axis1, axis2, axis3, tn)) return false; + } + return true; + } + + private Vector3f getScaledAxis(int index, Vector3f store) { + Matrix4f u = uniformMatrix; + float x = 0, y = 0, z = 0, s = 1; + switch (index) { + case 0: + x = u.m00; + y = u.m10; + z = u.m20; + s = u.m30; + break; + case 1: + x = u.m01; + y = u.m11; + z = u.m21; + s = u.m31; + break; + case 2: + x = u.m02; + y = u.m12; + z = u.m22; + s = u.m32; + } + return store.set(x, y, z).multLocal(s); + } + + private boolean insidePlane(Plane p, Vector3f axis1, Vector3f axis2, Vector3f axis3, Vector3f tn) { + // transform the plane normal in the box local space. + tn.set(axis1.dot(p.getNormal()), axis2.dot(p.getNormal()), axis3.dot(p.getNormal())); + + // distance check + float radius = FastMath.abs(tn.x) + + FastMath.abs(tn.y) + + FastMath.abs(tn.z); + + float distance = p.pseudoDistance(transform.getTranslation()); + + if (distance < -radius) { + return false; + } + return true; + } + + private Vector3f getClosestPoint(TempVars vars, Vector3f point) { + // non normalized direction + Vector3f dir = vars.vect2.set(point).subtractLocal(transform.getTranslation()); + // initialize the closest point with box center + Vector3f closestPoint = vars.vect3.set(transform.getTranslation()); + + //store extent in an array + float[] r = vars.fWdU; + r[0] = transform.getScale().x; + r[1] = transform.getScale().y; + r[2] = transform.getScale().z; + + // computing closest point to sphere center + for (int i = 0; i < 3; i++) { + // extract the axis from the 3x3 matrix + Vector3f axis = getScaledAxis(i, vars.vect1); + // nomalize (here we just divide by the extent + axis.divideLocal(r[i]); + // distance to the closest point on this axis. + float d = FastMath.clamp(dir.dot(axis), -r[i], r[i]); + closestPoint.addLocal(vars.vect4.set(axis).multLocal(d)); + } + return closestPoint; + } + + private void updateMatrix() { + TempVars vars = TempVars.get(); + Matrix3f r = vars.tempMat3; + Matrix4f u = uniformMatrix; + transform.getRotation().toRotationMatrix(r); + + u.m00 = r.get(0,0); + u.m10 = r.get(1,0); + u.m20 = r.get(2,0); + u.m01 = r.get(0,1); + u.m11 = r.get(1,1); + u.m21 = r.get(2,1); + u.m02 = r.get(0,2); + u.m12 = r.get(1,2); + u.m22 = r.get(2,2); + + //scale + u.m30 = transform.getScale().x; + u.m31 = transform.getScale().y; + u.m32 = transform.getScale().z; + + //position + u.m03 = transform.getTranslation().x; + u.m13 = transform.getTranslation().y; + u.m23 = transform.getTranslation().z; + + vars.release(); + } + + public Matrix4f getUniformMatrix() { + return uniformMatrix; + } + + public Vector3f getExtent() { + return transform.getScale(); + } + + public void setExtent(Vector3f extent) { + transform.setScale(extent); + updateMatrix(); + } + + public Vector3f getCenter() { + return transform.getTranslation(); + } + + public void setCenter(Vector3f center) { + transform.setTranslation(center); + updateMatrix(); + } + + public Quaternion getRotation() { + return transform.getRotation(); + } + + public void setRotation(Quaternion rotation) { + transform.setRotation(rotation); + updateMatrix(); + } + + @Override + protected OrientedBoxProbeArea clone() throws CloneNotSupportedException { + return new OrientedBoxProbeArea(transform); + } + + @Override + public void write(JmeExporter e) throws IOException { + OutputCapsule oc = e.getCapsule(this); + oc.write(transform, "transform", new Transform()); + } + + @Override + public void read(JmeImporter i) throws IOException { + InputCapsule ic = i.getCapsule(this); + transform = (Transform) ic.readSavable("transform", new Transform()); + updateMatrix(); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/light/PoiLightProbeLightFilter.java b/jme3-core/src/main/java/com/jme3/light/PoiLightProbeLightFilter.java deleted file mode 100644 index b991036ae..000000000 --- a/jme3-core/src/main/java/com/jme3/light/PoiLightProbeLightFilter.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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.BoundingBox; -import com.jme3.bounding.BoundingSphere; -import com.jme3.bounding.BoundingVolume; -import com.jme3.renderer.Camera; -import com.jme3.scene.Geometry; -import com.jme3.util.TempVars; -import java.util.HashSet; - -public final class PoiLightProbeLightFilter implements LightFilter { - - private Camera camera; - private final HashSet processedLights = new HashSet(); - private final LightProbeBlendingProcessor processor; - - public PoiLightProbeLightFilter(LightProbeBlendingProcessor processor) { - this.processor = processor; - } - - @Override - public void setCamera(Camera camera) { - this.camera = camera; - for (Light light : processedLights) { - light.frustumCheckNeeded = true; - } - } - - @Override - public void filterLights(Geometry geometry, LightList filteredLightList) { - TempVars vars = TempVars.get(); - try { - LightList worldLights = geometry.getWorldLightList(); - - for (int i = 0; i < worldLights.size(); i++) { - Light light = worldLights.get(i); - - if (light.getType() == Light.Type.Probe) { - continue; - } - - if (light.frustumCheckNeeded) { - processedLights.add(light); - light.frustumCheckNeeded = false; - light.intersectsFrustum = light.intersectsFrustum(camera, vars); - } - - if (!light.intersectsFrustum) { - continue; - } - - BoundingVolume bv = geometry.getWorldBound(); - - if (bv instanceof BoundingBox) { - if (!light.intersectsBox((BoundingBox) bv, vars)) { - continue; - } - } else if (bv instanceof BoundingSphere) { - if (!Float.isInfinite(((BoundingSphere) bv).getRadius())) { - if (!light.intersectsSphere((BoundingSphere) bv, vars)) { - continue; - } - } - } - - filteredLightList.add(light); - } - - processor.populateProbe(filteredLightList); - - } finally { - vars.release(); - } - } - -} 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 69c096790..dddf06287 100644 --- a/jme3-core/src/main/java/com/jme3/light/PointLight.java +++ b/jme3-core/src/main/java/com/jme3/light/PointLight.java @@ -212,12 +212,7 @@ public class PointLight extends Light { if (this.radius == 0) { return true; } else { - for (int i = 5; i >= 0; i--) { - if (camera.getWorldPlane(i).pseudoDistance(position) <= -radius) { - return false; - } - } - return true; + return Intersection.intersect(camera, position, radius); } } diff --git a/jme3-core/src/main/java/com/jme3/light/ProbeArea.java b/jme3-core/src/main/java/com/jme3/light/ProbeArea.java new file mode 100644 index 000000000..da0e57174 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/light/ProbeArea.java @@ -0,0 +1,33 @@ +package com.jme3.light; + +import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingSphere; +import com.jme3.export.Savable; +import com.jme3.math.Matrix4f; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +import com.jme3.util.TempVars; + +public interface ProbeArea extends Savable, Cloneable{ + + public void setCenter(Vector3f center); + + public float getRadius(); + + public Matrix4f getUniformMatrix(); + + /** + * @see Light#intersectsBox(BoundingBox, TempVars) + */ + public boolean intersectsBox(BoundingBox box, TempVars vars); + + /** + * @see Light#intersectsSphere(BoundingSphere, TempVars) + */ + public boolean intersectsSphere(BoundingSphere sphere, TempVars vars); + + /** + * @see Light#intersectsFrustum(Camera, TempVars) + */ + public abstract boolean intersectsFrustum(Camera camera, TempVars vars); +} diff --git a/jme3-core/src/main/java/com/jme3/light/SphereProbeArea.java b/jme3-core/src/main/java/com/jme3/light/SphereProbeArea.java new file mode 100644 index 000000000..6fbf1a1cf --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/light/SphereProbeArea.java @@ -0,0 +1,102 @@ +package com.jme3.light; + +import com.jme3.bounding.*; +import com.jme3.export.*; +import com.jme3.math.Matrix4f; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +import com.jme3.util.TempVars; + +import java.io.IOException; +import java.util.logging.Level; + +public class SphereProbeArea implements ProbeArea { + + private Vector3f center = new Vector3f(); + private float radius = 1; + private Matrix4f uniformMatrix = new Matrix4f(); + + public SphereProbeArea() { + } + + public SphereProbeArea(Vector3f center, float radius) { + this.center.set(center); + this.radius = radius; + updateMatrix(); + } + + public Vector3f getCenter() { + return center; + } + + public void setCenter(Vector3f center) { + this.center.set(center); + updateMatrix(); + } + + public float getRadius() { + return radius; + } + + public void setRadius(float radius) { + this.radius = radius; + updateMatrix(); + } + + @Override + public Matrix4f getUniformMatrix() { + return uniformMatrix; + } + + private void updateMatrix(){ + //position + uniformMatrix.m03 = center.x; + uniformMatrix.m13 = center.y; + uniformMatrix.m23 = center.z; + + } + + @Override + public boolean intersectsBox(BoundingBox box, TempVars vars) { + return Intersection.intersect(box, center, radius); + } + + @Override + public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) { + return Intersection.intersect(sphere, center, radius); + } + + @Override + public boolean intersectsFrustum(Camera camera, TempVars vars) { + return Intersection.intersect(camera, center, radius); + } + + @Override + public String toString() { + return "SphereProbeArea{" + + "center=" + center + + ", radius=" + radius + + '}'; + } + + @Override + protected SphereProbeArea clone() throws CloneNotSupportedException { + return new SphereProbeArea(center, radius); + } + + @Override + public void write(JmeExporter e) throws IOException { + OutputCapsule oc = e.getCapsule(this); + oc.write(center, "center", new Vector3f()); + oc.write(radius, "radius", 1); + } + + @Override + public void read(JmeImporter i) throws IOException { + InputCapsule ic = i.getCapsule(this); + center = (Vector3f) ic.readSavable("center", new Vector3f()); + radius = ic.readFloat("radius", 1); + updateMatrix(); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/light/WeightedProbeBlendingStrategy.java b/jme3-core/src/main/java/com/jme3/light/WeightedProbeBlendingStrategy.java new file mode 100644 index 000000000..7e7f2f92f --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/light/WeightedProbeBlendingStrategy.java @@ -0,0 +1,77 @@ +/* + * 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.scene.Geometry; + +import java.util.ArrayList; +import java.util.List; + +/** + * This strategy returns the 3 closest probe from the rendered object. + *

+ * Image based lighting will be blended between those probes in the shader according to their distance and range. + * + * @author Nehon + */ +public class WeightedProbeBlendingStrategy implements LightProbeBlendingStrategy { + + private final static int MAX_PROBES = 3; + List lightProbes = new ArrayList(); + + @Override + public void registerProbe(LightProbe probe) { + lightProbes.add(probe); + } + + @Override + public void populateProbes(Geometry g, LightList lightList) { + if (!lightProbes.isEmpty()) { + //The 3 first probes are the closest to the geometry since the + //light list is sorted according to the distance to the geom. + int addedProbes = 0; + for (LightProbe p : lightProbes) { + if (p.isReady() && p.isEnabled()) { + lightList.add(p); + addedProbes ++; + } + if (addedProbes == MAX_PROBES) { + break; + } + } + + //clearing the list for next pass. + lightProbes.clear(); + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java index b6f89f292..f80c8274a 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java @@ -42,17 +42,17 @@ import com.jme3.scene.Geometry; import com.jme3.shader.*; import com.jme3.util.TempVars; -import java.util.EnumSet; +import java.util.*; public final class SinglePassAndImageBasedLightingLogic extends DefaultTechniqueDefLogic { private static final String DEFINE_SINGLE_PASS_LIGHTING = "SINGLE_PASS_LIGHTING"; private static final String DEFINE_NB_LIGHTS = "NB_LIGHTS"; - private static final String DEFINE_INDIRECT_LIGHTING = "INDIRECT_LIGHTING"; + private static final String DEFINE_NB_PROBES = "NB_PROBES"; private static final RenderState ADDITIVE_LIGHT = new RenderState(); private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1); - private LightProbe lightProbe = null; + private List lightProbes = new ArrayList<>(3); static { ADDITIVE_LIGHT.setBlendMode(BlendMode.AlphaAdditive); @@ -61,13 +61,13 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique private final int singlePassLightingDefineId; private final int nbLightsDefineId; - private final int indirectLightingDefineId; + private final int nbProbesDefineId; public SinglePassAndImageBasedLightingLogic(TechniqueDef techniqueDef) { super(techniqueDef); singlePassLightingDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_SINGLE_PASS_LIGHTING, VarType.Boolean); nbLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NB_LIGHTS, VarType.Int); - indirectLightingDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_INDIRECT_LIGHTING, VarType.Boolean); + nbProbesDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NB_PROBES, VarType.Int); } @Override @@ -81,12 +81,9 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique //Though the second pass should not render IBL as it is taken care of on first pass like ambient light in phong lighting. //We cannot change the define between passes and the old technique, and for some reason the code fails on mac (renders nothing). if(lights != null) { - lightProbe = extractIndirectLights(lights, false); - if (lightProbe == null) { - defines.set(indirectLightingDefineId, false); - } else { - defines.set(indirectLightingDefineId, true); - } + lightProbes.clear(); + extractIndirectLights(lights, false); + defines.set(nbProbesDefineId, lightProbes.size()); } return super.makeCurrent(assetManager, renderManager, rendererCaps, lights, defines); @@ -113,35 +110,44 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique Uniform lightData = shader.getUniform("g_LightData"); lightData.setVector4Length(numLights * 3);//8 lights * max 3 Uniform ambientColor = shader.getUniform("g_AmbientLightColor"); + + // Matrix4f Uniform lightProbeData = shader.getUniform("g_LightProbeData"); - lightProbeData.setVector4Length(1); + Uniform lightProbeData2 = shader.getUniform("g_LightProbeData2"); + Uniform lightProbeData3 = shader.getUniform("g_LightProbeData3"); - //TODO These 2 uniforms should be packed in an array, to be able to have several probes and blend between them. Uniform shCoeffs = shader.getUniform("g_ShCoeffs"); Uniform lightProbePemMap = shader.getUniform("g_PrefEnvMap"); + Uniform shCoeffs2 = shader.getUniform("g_ShCoeffs2"); + Uniform lightProbePemMap2 = shader.getUniform("g_PrefEnvMap2"); + Uniform shCoeffs3 = shader.getUniform("g_ShCoeffs3"); + Uniform lightProbePemMap3 = shader.getUniform("g_PrefEnvMap3"); - lightProbe = null; + lightProbes.clear(); if (startIndex != 0) { // apply additive blending for 2nd and future passes rm.getRenderer().applyRenderState(ADDITIVE_LIGHT); ambientColor.setValue(VarType.Vector4, ColorRGBA.Black); }else{ - lightProbe = extractIndirectLights(lightList,true); + extractIndirectLights(lightList,true); ambientColor.setValue(VarType.Vector4, ambientLightColor); } //If there is a lightProbe in the list we force its render on the first pass - if(lightProbe != null){ - BoundingSphere s = (BoundingSphere)lightProbe.getBounds(); - lightProbeData.setVector4InArray(lightProbe.getPosition().x, lightProbe.getPosition().y, lightProbe.getPosition().z, 1f / s.getRadius() + lightProbe.getNbMipMaps(), 0); - shCoeffs.setValue(VarType.Vector3Array, lightProbe.getShCoeffs()); - //assigning new texture indexes - int pemUnit = lastTexUnit++; - rm.getRenderer().setTexture(pemUnit, lightProbe.getPrefilteredEnvMap()); - lightProbePemMap.setValue(VarType.Int, pemUnit); + if (!lightProbes.isEmpty()) { + LightProbe lightProbe = lightProbes.get(0); + lastTexUnit = setProbeData(rm, lastTexUnit, lightProbeData, shCoeffs, lightProbePemMap, lightProbe); + if (lightProbes.size() > 1) { + lightProbe = lightProbes.get(1); + lastTexUnit = setProbeData(rm, lastTexUnit, lightProbeData2, shCoeffs2, lightProbePemMap2, lightProbe); + } + if (lightProbes.size() > 2) { + lightProbe = lightProbes.get(2); + setProbeData(rm, lastTexUnit, lightProbeData3, shCoeffs3, lightProbePemMap3, lightProbe); + } } else { //Disable IBL for this pass - lightProbeData.setVector4InArray(0,0,0,-1, 0); + lightProbeData.setValue(VarType.Matrix4, LightProbe.FALLBACK_MATRIX); } int lightDataIndex = 0; @@ -222,6 +228,18 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique return curIndex; } + private int setProbeData(RenderManager rm, int lastTexUnit, Uniform lightProbeData, Uniform shCoeffs, Uniform lightProbePemMap, LightProbe lightProbe) { + + lightProbeData.setValue(VarType.Matrix4, lightProbe.getUniformMatrix()); + //setVector4InArray(lightProbe.getPosition().x, lightProbe.getPosition().y, lightProbe.getPosition().z, 1f / area.getRadius() + lightProbe.getNbMipMaps(), 0); + shCoeffs.setValue(VarType.Vector3Array, lightProbe.getShCoeffs()); + //assigning new texture indexes + int pemUnit = lastTexUnit++; + rm.getRenderer().setTexture(pemUnit, lightProbe.getPrefilteredEnvMap()); + lightProbePemMap.setValue(VarType.Int, pemUnit); + return lastTexUnit; + } + @Override public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit) { int nbRenderedLights = 0; @@ -241,9 +259,8 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique return; } - protected LightProbe extractIndirectLights(LightList lightList, boolean removeLights) { + protected void extractIndirectLights(LightList lightList, boolean removeLights) { ambientLightColor.set(0, 0, 0, 1); - LightProbe probe = null; for (int j = 0; j < lightList.size(); j++) { Light l = lightList.get(j); if (l instanceof AmbientLight) { @@ -254,7 +271,7 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique } } if (l instanceof LightProbe) { - probe = (LightProbe)l; + lightProbes.add((LightProbe) l); if(removeLights){ lightList.remove(l); j--; @@ -262,6 +279,5 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique } } ambientLightColor.a = 1.0f; - return probe; } } diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java b/jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java index 4f5364753..7fabbc503 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java @@ -42,29 +42,39 @@ import java.nio.FloatBuffer; public class WireFrustum extends Mesh { public WireFrustum(Vector3f[] points){ + initGeom(this, points); + } + + public static Mesh makeFrustum(Vector3f[] points){ + Mesh m = new Mesh(); + initGeom(m, points); + return m; + } + + private static void initGeom(Mesh m, Vector3f[] points) { if (points != null) - setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(points)); + m.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(points)); - setBuffer(Type.Index, 2, + m.setBuffer(Type.Index, 2, new short[]{ - 0, 1, - 1, 2, - 2, 3, - 3, 0, + 0, 1, + 1, 2, + 2, 3, + 3, 0, - 4, 5, - 5, 6, - 6, 7, - 7, 4, + 4, 5, + 5, 6, + 6, 7, + 7, 4, - 0, 4, - 1, 5, - 2, 6, - 3, 7, + 0, 4, + 1, 5, + 2, 6, + 3, 7, } ); - getBuffer(Type.Index).setUsage(Usage.Static); - setMode(Mode.Lines); + m.getBuffer(Type.Index).setUsage(Usage.Static); + m.setMode(Mode.Lines); } public void update(Vector3f[] points){ diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag index 59a744ffd..91af5d6d9 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag @@ -3,7 +3,6 @@ #import "Common/ShaderLib/Parallax.glsllib" #import "Common/ShaderLib/Lighting.glsllib" - varying vec2 texCoord; #ifdef SEPARATE_TEXCOORD varying vec2 texCoord2; @@ -12,7 +11,6 @@ varying vec2 texCoord; varying vec4 Color; uniform vec4 g_LightData[NB_LIGHTS]; - uniform vec3 g_CameraPosition; uniform float m_Roughness; @@ -21,11 +19,20 @@ uniform float m_Metallic; varying vec3 wPosition; -#ifdef INDIRECT_LIGHTING -// uniform sampler2D m_IntegrateBRDF; +#if NB_PROBES >= 1 uniform samplerCube g_PrefEnvMap; uniform vec3 g_ShCoeffs[9]; - uniform vec4 g_LightProbeData; + uniform mat4 g_LightProbeData; +#endif +#if NB_PROBES >= 2 + uniform samplerCube g_PrefEnvMap2; + uniform vec3 g_ShCoeffs2[9]; + uniform mat4 g_LightProbeData2; +#endif +#if NB_PROBES == 3 + uniform samplerCube g_PrefEnvMap3; + uniform vec3 g_ShCoeffs3[9]; + uniform mat4 g_LightProbeData3; #endif #ifdef BASECOLORMAP @@ -87,6 +94,91 @@ varying vec3 wNormal; uniform float m_AlphaDiscardThreshold; #endif +float renderProbe(vec3 viewDir, vec3 normal, vec3 norm, float Roughness, vec4 diffuseColor, vec4 specularColor, float ndotv, vec3 ao, mat4 lightProbeData,vec3 shCoeffs[9],samplerCube prefEnvMap, inout vec3 color ){ + + // lightProbeData is a mat4 with this layout + // 3x3 rot mat| + // 0 1 2 | 3 + // 0 | ax bx cx | px | ) + // 1 | ay by cy | py | probe position + // 2 | az bz cz | pz | ) + // --|----------| + // 3 | sx sy sz sp | -> 1/probe radius + nbMipMaps + // --scale-- + // parallax fix for spherical / obb bounds and probe blending from + // from https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ + vec3 rv = reflect(-viewDir, normal); + vec4 probePos = lightProbeData[3]; + float invRadius = fract( probePos.w); + float nbMipMaps = probePos.w - invRadius; + vec3 direction = wPosition - probePos.xyz; + float ndf = 0.0; + + if(lightProbeData[0][3] != 0.0){ + // oriented box probe + mat3 wToLocalRot = mat3(lightProbeData); + wToLocalRot = inverse(wToLocalRot); + vec3 scale = vec3(lightProbeData[0][3], lightProbeData[1][3], lightProbeData[2][3]); + #if NB_PROBES >= 2 + // probe blending + // compute fragment position in probe local space + vec3 localPos = wToLocalRot * wPosition; + localPos -= probePos.xyz; + // compute normalized distance field + vec3 localDir = abs(localPos); + localDir /= scale; + ndf = max(max(localDir.x, localDir.y), localDir.z); + #endif + // parallax fix + vec3 rayLs = wToLocalRot * rv; + rayLs /= scale; + + vec3 positionLs = wPosition - probePos.xyz; + positionLs = wToLocalRot * positionLs; + positionLs /= scale; + + vec3 unit = vec3(1.0); + vec3 firstPlaneIntersect = (unit - positionLs) / rayLs; + vec3 secondPlaneIntersect = (-unit - positionLs) / rayLs; + vec3 furthestPlane = max(firstPlaneIntersect, secondPlaneIntersect); + float distance = min(min(furthestPlane.x, furthestPlane.y), furthestPlane.z); + + vec3 intersectPositionWs = wPosition + rv * distance; + rv = intersectPositionWs - probePos.xyz; + + } else { + // spherical probe + // paralax fix + rv = invRadius * direction + rv; + + #if NB_PROBES >= 2 + // probe blending + float dist = sqrt(dot(direction, direction)); + ndf = dist * invRadius; + #endif + } + + vec3 indirectDiffuse = vec3(0.0); + vec3 indirectSpecular = vec3(0.0); + indirectDiffuse = sphericalHarmonics(normal.xyz, shCoeffs) * diffuseColor.rgb; + vec3 dominantR = getSpecularDominantDir( normal, rv.xyz, Roughness * Roughness ); + indirectSpecular = ApproximateSpecularIBLPolynomial(prefEnvMap, specularColor.rgb, Roughness, ndotv, dominantR, nbMipMaps); + + #ifdef HORIZON_FADE + //horizon fade from http://marmosetco.tumblr.com/post/81245981087 + float horiz = dot(rv, norm); + float horizFadePower = 1.0 - Roughness; + horiz = clamp( 1.0 + horizFadePower * horiz, 0.0, 1.0 ); + horiz *= horiz; + indirectSpecular *= vec3(horiz); + #endif + + vec3 indirectLighting = (indirectDiffuse + indirectSpecular) * ao; + + color = indirectLighting * step( 0.0, probePos.w); + return ndf; +} + void main(){ vec2 newTexCoord; vec3 viewDir = normalize(g_CameraPosition - wPosition); @@ -250,32 +342,47 @@ void main(){ gl_FragColor.rgb += directLighting * fallOff; } - #ifdef INDIRECT_LIGHTING - vec3 rv = reflect(-viewDir.xyz, normal.xyz); - //prallax fix for spherical bounds from https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ - // g_LightProbeData.w is 1/probe radius + nbMipMaps, g_LightProbeData.xyz is the position of the lightProbe. - float invRadius = fract( g_LightProbeData.w); - float nbMipMaps = g_LightProbeData.w - invRadius; - rv = invRadius * (wPosition - g_LightProbeData.xyz) +rv; + #if NB_PROBES >= 1 + vec3 color1 = vec3(0.0); + vec3 color2 = vec3(0.0); + vec3 color3 = vec3(0.0); + float weight1 = 1.0; + float weight2 = 0.0; + float weight3 = 0.0; + + float ndf = renderProbe(viewDir, normal, norm, Roughness, diffuseColor, specularColor, ndotv, ao, g_LightProbeData, g_ShCoeffs, g_PrefEnvMap, color1); + #if NB_PROBES >= 2 + float ndf2 = renderProbe(viewDir, normal, norm, Roughness, diffuseColor, specularColor, ndotv, ao, g_LightProbeData2, g_ShCoeffs2, g_PrefEnvMap2, color2); + #endif + #if NB_PROBES == 3 + float ndf3 = renderProbe(viewDir, normal, norm, Roughness, diffuseColor, specularColor, ndotv, ao, g_LightProbeData3, g_ShCoeffs3, g_PrefEnvMap3, color3); + #endif - //horizon fade from http://marmosetco.tumblr.com/post/81245981087 - float horiz = dot(rv, norm); - float horizFadePower = 1.0 - Roughness; - horiz = clamp( 1.0 + horizFadePower * horiz, 0.0, 1.0 ); - horiz *= horiz; + #if NB_PROBES >= 2 + float invNdf = max(1.0 - ndf,0.0); + float invNdf2 = max(1.0 - ndf2,0.0); + float sumNdf = ndf + ndf2; + float sumInvNdf = invNdf + invNdf2; + #if NB_PROBES == 3 + float invNdf3 = max(1.0 - ndf3,0.0); + sumNdf += ndf3; + sumInvNdf += invNdf3; + weight3 = ((1.0 - (ndf3 / sumNdf)) / (NB_PROBES - 1)) * (invNdf3 / sumInvNdf); + #endif - vec3 indirectDiffuse = vec3(0.0); - vec3 indirectSpecular = vec3(0.0); - indirectDiffuse = sphericalHarmonics(normal.xyz, g_ShCoeffs) * diffuseColor.rgb; - vec3 dominantR = getSpecularDominantDir( normal, rv.xyz, Roughness*Roughness ); - indirectSpecular = ApproximateSpecularIBLPolynomial(g_PrefEnvMap, specularColor.rgb, Roughness, ndotv, dominantR, nbMipMaps); - indirectSpecular *= vec3(horiz); + weight1 = ((1.0 - (ndf / sumNdf)) / (NB_PROBES - 1)) * (invNdf / sumInvNdf); + weight2 = ((1.0 - (ndf2 / sumNdf)) / (NB_PROBES - 1)) * (invNdf2 / sumInvNdf); - vec3 indirectLighting = (indirectDiffuse + indirectSpecular) * ao; + float weightSum = weight1 + weight2 + weight3; + + weight1 /= weightSum; + weight2 /= weightSum; + weight3 /= weightSum; + #endif + gl_FragColor.rgb += color1 * clamp(weight1,0.0,1.0) + color2 * clamp(weight2,0.0,1.0) + color3 * clamp(weight3,0.0,1.0); - gl_FragColor.rgb = gl_FragColor.rgb + indirectLighting * step( 0.0, g_LightProbeData.w); #endif - + #if defined(EMISSIVE) || defined (EMISSIVEMAP) #ifdef EMISSIVEMAP vec4 emissive = texture2D(m_EmissiveMap, newTexCoord); diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md index 437947d6c..e300bffdc 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md @@ -62,6 +62,9 @@ MaterialDef PBR Lighting { //Set to true to activate Steep Parallax mapping Boolean SteepParallax + //Horizon fade + Boolean HorizonFade + // Set to Use Lightmap Texture2D LightMap @@ -157,6 +160,7 @@ MaterialDef PBR Lighting { AO_MAP: LightMapAsAOMap NUM_MORPH_TARGETS: NumberOfMorphTargets NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers + HORIZON_FADE: HorizonFade } } diff --git a/jme3-examples/src/main/java/jme3test/collision/Main.java b/jme3-examples/src/main/java/jme3test/collision/Main.java new file mode 100644 index 000000000..e69de29bb diff --git a/jme3-examples/src/main/java/jme3test/light/DlsfError.java b/jme3-examples/src/main/java/jme3test/light/DlsfError.java new file mode 100644 index 000000000..e69de29bb diff --git a/jme3-examples/src/main/java/jme3test/light/TestConeVSFrustum.java b/jme3-examples/src/main/java/jme3test/light/TestConeVSFrustum.java index 24729948d..d96edf706 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestConeVSFrustum.java +++ b/jme3-examples/src/main/java/jme3test/light/TestConeVSFrustum.java @@ -102,7 +102,7 @@ public class TestConeVSFrustum extends SimpleApplication { float radius = FastMath.tan(spotLight.getSpotOuterAngle()) * spotLight.getSpotRange(); - Cylinder cylinder = new Cylinder(5, 16, 0, radius, spotLight.getSpotRange(), true, false); + Cylinder cylinder = new Cylinder(5, 16, 0.01f, radius, spotLight.getSpotRange(), true, false); geom = new Geometry("light", cylinder); geom.setMaterial(new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md")); geom.getMaterial().setColor("Diffuse", ColorRGBA.White); diff --git a/jme3-examples/src/main/java/jme3test/light/TestObbVsBounds.java b/jme3-examples/src/main/java/jme3test/light/TestObbVsBounds.java new file mode 100644 index 000000000..c32773070 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/light/TestObbVsBounds.java @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2009-2012 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 jme3test.light; + +import com.jme3.app.ChaseCameraAppState; +import com.jme3.app.SimpleApplication; +import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingSphere; +import com.jme3.export.binary.BinaryExporter; +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.controls.*; +import com.jme3.light.*; +import com.jme3.material.Material; +import com.jme3.math.*; +import com.jme3.renderer.Camera; +import com.jme3.scene.*; +import com.jme3.scene.debug.Grid; +import com.jme3.scene.debug.WireFrustum; +import com.jme3.scene.shape.*; +import com.jme3.shadow.ShadowUtil; +import com.jme3.util.TempVars; + +import java.io.File; +import java.io.IOException; + +public class TestObbVsBounds extends SimpleApplication { + + private Node ln; + private BoundingBox aabb = new BoundingBox(); + private BoundingSphere sphere = new BoundingSphere(10, new Vector3f(-30, 0, -60)); + + private final static float MOVE_SPEED = 60; + private Vector3f tmp = new Vector3f(); + private Quaternion tmpQuat = new Quaternion(); + private boolean moving, shift; + private boolean panning; + + private OrientedBoxProbeArea area = new OrientedBoxProbeArea(); + private Camera frustumCam; + + private Geometry areaGeom; + private Geometry frustumGeom; + private Geometry aabbGeom; + private Geometry sphereGeom; + + public static void main(String[] args) { + TestObbVsBounds app = new TestObbVsBounds(); + app.start(); + } + + @Override + public void simpleInitApp() { + viewPort.setBackgroundColor(ColorRGBA.DarkGray); + frustumCam = cam.clone(); + frustumCam.setFrustumFar(25); + makeCamFrustum(); + aabb.setCenter(20, 10, -60); + aabb.setXExtent(10); + aabb.setYExtent(5); + aabb.setZExtent(3); + makeBoxWire(aabb); + makeSphereWire(sphere); + + rootNode.addLight(new DirectionalLight()); + AmbientLight al = new AmbientLight(); + al.setColor(ColorRGBA.White.mult(0.2f)); + rootNode.addLight(al); + + Grid grid = new Grid(50, 50, 5); + Geometry gridGeom = new Geometry("grid", grid); + gridGeom.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md")); + gridGeom.getMaterial().setColor("Color", ColorRGBA.Gray); + rootNode.attachChild(gridGeom); + gridGeom.setLocalTranslation(-125, -25, -125); + + area.setCenter(Vector3f.ZERO); + area.setExtent(new Vector3f(4, 8, 5)); + makeAreaGeom(); + + ln = new Node("lb"); + ln.setLocalRotation(new Quaternion(-0.18826798f, -0.38304946f, -0.12780227f, 0.895261f)); + ln.attachChild(areaGeom); + ln.setLocalScale(4,8,5); + rootNode.attachChild(ln); + + inputManager.addMapping("click", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); + inputManager.addMapping("shift", new KeyTrigger(KeyInput.KEY_LSHIFT), new KeyTrigger(KeyInput.KEY_RSHIFT)); + inputManager.addMapping("middleClick", new MouseButtonTrigger(MouseInput.BUTTON_MIDDLE)); + inputManager.addMapping("up", new MouseAxisTrigger(MouseInput.AXIS_Y, false)); + inputManager.addMapping("down", new MouseAxisTrigger(MouseInput.AXIS_Y, true)); + inputManager.addMapping("left", new MouseAxisTrigger(MouseInput.AXIS_X, true)); + inputManager.addMapping("right", new MouseAxisTrigger(MouseInput.AXIS_X, false)); + + + final Node camTarget = new Node("CamTarget"); + rootNode.attachChild(camTarget); + + ChaseCameraAppState chaser = new ChaseCameraAppState(); + chaser.setTarget(camTarget); + chaser.setMaxDistance(150); + chaser.setDefaultDistance(70); + chaser.setDefaultHorizontalRotation(FastMath.HALF_PI); + chaser.setMinVerticalRotation(-FastMath.PI); + chaser.setMaxVerticalRotation(FastMath.PI * 2); + chaser.setToggleRotationTrigger(new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); + stateManager.attach(chaser); + flyCam.setEnabled(false); + + inputManager.addListener(new AnalogListener() { + public void onAnalog(String name, float value, float tpf) { + Spatial s = null; + float mult = 1; + if (moving) { + s = ln; + } + if (panning) { + s = camTarget; + mult = -1; + } + if ((moving || panning) && s != null) { + if (shift) { + if (name.equals("left")) { + tmp.set(cam.getDirection()); + s.rotate(tmpQuat.fromAngleAxis(value, tmp)); + } + if (name.equals("right")) { + tmp.set(cam.getDirection()); + s.rotate(tmpQuat.fromAngleAxis(-value, tmp)); + } + } else { + value *= MOVE_SPEED * mult; + if (name.equals("up")) { + tmp.set(cam.getUp()).multLocal(value); + s.move(tmp); + } + if (name.equals("down")) { + tmp.set(cam.getUp()).multLocal(-value); + s.move(tmp); + } + if (name.equals("left")) { + tmp.set(cam.getLeft()).multLocal(value); + s.move(tmp); + } + if (name.equals("right")) { + tmp.set(cam.getLeft()).multLocal(-value); + s.move(tmp); + } + } + } + } + }, "up", "down", "left", "right"); + + inputManager.addListener(new ActionListener() { + public void onAction(String name, boolean isPressed, float tpf) { + if (name.equals("click")) { + if (isPressed) { + moving = true; + } else { + moving = false; + } + } + if (name.equals("middleClick")) { + if (isPressed) { + panning = true; + } else { + panning = false; + } + } + if (name.equals("shift")) { + if (isPressed) { + shift = true; + } else { + shift = false; + } + } + } + }, "click", "middleClick", "shift"); + + } + + public void makeAreaGeom() { + + Vector3f[] points = new Vector3f[8]; + + for (int i = 0; i < points.length; i++) { + points[i] = new Vector3f(); + } + + points[0].set(-1, -1, 1); + points[1].set(-1, 1, 1); + points[2].set(1, 1, 1); + points[3].set(1, -1, 1); + + points[4].set(-1, -1, -1); + points[5].set(-1, 1, -1); + points[6].set(1, 1, -1); + points[7].set(1, -1, -1); + + Mesh box = WireFrustum.makeFrustum(points); + areaGeom = new Geometry("light", (Mesh)box); + areaGeom.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md")); + areaGeom.getMaterial().setColor("Color", ColorRGBA.White); + } + + public void makeCamFrustum() { + Vector3f[] points = new Vector3f[8]; + for (int i = 0; i < 8; i++) { + points[i] = new Vector3f(); + } + ShadowUtil.updateFrustumPoints2(frustumCam, points); + WireFrustum frustumShape = new WireFrustum(points); + frustumGeom = new Geometry("frustum", frustumShape); + frustumGeom.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md")); + rootNode.attachChild(frustumGeom); + } + + public void makeBoxWire(BoundingBox box) { + Vector3f[] points = new Vector3f[8]; + for (int i = 0; i < 8; i++) { + points[i] = new Vector3f(); + } + points[0].set(-1, -1, 1); + points[1].set(-1, 1, 1); + points[2].set(1, 1, 1); + points[3].set(1, -1, 1); + + points[4].set(-1, -1, -1); + points[5].set(-1, 1, -1); + points[6].set(1, 1, -1); + points[7].set(1, -1, -1); + + WireFrustum frustumShape = new WireFrustum(points); + aabbGeom = new Geometry("box", frustumShape); + aabbGeom.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md")); + aabbGeom.getMaterial().getAdditionalRenderState().setWireframe(true); + aabbGeom.setLocalTranslation(box.getCenter()); + aabbGeom.setLocalScale(box.getXExtent(), box.getYExtent(), box.getZExtent()); + rootNode.attachChild(aabbGeom); + } + + public void makeSphereWire(BoundingSphere sphere) { + + sphereGeom = new Geometry("box", new Sphere(16, 16, 10)); + sphereGeom.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md")); + sphereGeom.getMaterial().getAdditionalRenderState().setWireframe(true); + sphereGeom.setLocalTranslation(sphere.getCenter()); + rootNode.attachChild(sphereGeom); + } + + + @Override + public void simpleUpdate(float tpf) { + + area.setCenter(ln.getLocalTranslation()); + area.setRotation(ln.getLocalRotation()); + + TempVars vars = TempVars.get(); + boolean intersectBox = area.intersectsBox(aabb, vars); + boolean intersectFrustum = area.intersectsFrustum(frustumCam, vars); + boolean intersectSphere = area.intersectsSphere(sphere, vars); + vars.release(); + + boolean intersect = intersectBox || intersectFrustum || intersectSphere; + + areaGeom.getMaterial().setColor("Color", intersect ? ColorRGBA.Green : ColorRGBA.White); + sphereGeom.getMaterial().setColor("Color", intersectSphere ? ColorRGBA.Cyan : ColorRGBA.White); + frustumGeom.getMaterial().setColor("Color", intersectFrustum ? ColorRGBA.Cyan : ColorRGBA.White); + aabbGeom.getMaterial().setColor("Color", intersectBox ? ColorRGBA.Cyan : ColorRGBA.White); + + } +} diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/RefEnv.java b/jme3-examples/src/main/java/jme3test/light/pbr/RefEnv.java index a88e12bb0..051202cda 100644 --- a/jme3-examples/src/main/java/jme3test/light/pbr/RefEnv.java +++ b/jme3-examples/src/main/java/jme3test/light/pbr/RefEnv.java @@ -1,7 +1,6 @@ package jme3test.light.pbr; import com.jme3.app.SimpleApplication; -import com.jme3.bounding.BoundingSphere; import com.jme3.environment.EnvironmentCamera; import com.jme3.environment.LightProbeFactory; import com.jme3.environment.generation.JobProgressAdapter; @@ -10,6 +9,7 @@ import com.jme3.input.KeyInput; import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.KeyTrigger; import com.jme3.light.LightProbe; +import com.jme3.light.SphereProbeArea; import com.jme3.material.Material; import com.jme3.math.*; import com.jme3.scene.*; @@ -127,7 +127,7 @@ public class RefEnv extends SimpleApplication { rootNode.getChild(0).setCullHint(Spatial.CullHint.Dynamic); } }); - ((BoundingSphere) probe.getBounds()).setRadius(100); + ((SphereProbeArea) probe.getArea()).setRadius(100); rootNode.addLight(probe); } diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java index 5cb276a07..4cc36b123 100644 --- a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java +++ b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java @@ -42,8 +42,7 @@ import com.jme3.input.ChaseCamera; import com.jme3.input.KeyInput; import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.KeyTrigger; -import com.jme3.light.DirectionalLight; -import com.jme3.light.LightProbe; +import com.jme3.light.*; import com.jme3.material.Material; import com.jme3.math.*; import com.jme3.post.FilterPostProcessor; @@ -205,7 +204,7 @@ public class TestPBRLighting extends SimpleApplication { tex = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(result.getPrefilteredEnvMap(), assetManager); } }); - ((BoundingSphere) probe.getBounds()).setRadius(100); + ((SphereProbeArea) probe.getArea()).setRadius(100); rootNode.addLight(probe); //getStateManager().getState(EnvironmentManager.class).addEnvProbe(probe);