From 1276dc583e3a4cfc4514218b265371678e193f43 Mon Sep 17 00:00:00 2001 From: Nehon Date: Sun, 29 Nov 2015 16:09:32 +0100 Subject: [PATCH] Point Of Interest light probe blending start of implementation : basic structure, probes selection and blend factor computation. --- .../main/java/com/jme3/light/LightProbe.java | 2 +- .../light/LightProbeBlendingProcessor.java | 208 ++++++++++++++++++ .../jme3/light/PoiLightProbeLightFilter.java | 107 +++++++++ .../java/com/jme3/renderer/RenderManager.java | 9 + 4 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 jme3-core/src/main/java/com/jme3/light/LightProbeBlendingProcessor.java create mode 100644 jme3-core/src/main/java/com/jme3/light/PoiLightProbeLightFilter.java 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 4abac3b4b..66c07342b 100644 --- a/jme3-core/src/main/java/com/jme3/light/LightProbe.java +++ b/jme3-core/src/main/java/com/jme3/light/LightProbe.java @@ -253,7 +253,7 @@ public class LightProbe extends Light implements Savable { @Override public String toString() { - return "Light Probe : " + position ; + return "Light Probe : " + name + " at " + position + " / " + bounds; } @Override diff --git a/jme3-core/src/main/java/com/jme3/light/LightProbeBlendingProcessor.java b/jme3-core/src/main/java/com/jme3/light/LightProbeBlendingProcessor.java new file mode 100644 index 000000000..9e1ec344b --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/light/LightProbeBlendingProcessor.java @@ -0,0 +1,208 @@ + /* + * 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.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; + + 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; + } + + + 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/PoiLightProbeLightFilter.java b/jme3-core/src/main/java/com/jme3/light/PoiLightProbeLightFilter.java new file mode 100644 index 000000000..b991036ae --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/light/PoiLightProbeLightFilter.java @@ -0,0 +1,107 @@ +/* + * 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/renderer/RenderManager.java b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java index 6d80d026f..f34d1d70f 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java @@ -754,6 +754,15 @@ public class RenderManager { public void setLightFilter(LightFilter lightFilter) { this.lightFilter = lightFilter; } + + /** + * Returns the current LightFilter. + * + * @return the current light filter + */ + public LightFilter getLightFilter() { + return this.lightFilter; + } /** * Defines what light mode will be selected when a technique offers several light modes.