Big refactoring to how PBR is handled.
- Introduced a new Light type : LightProbes that are lights holding Image based Lighting information that are sent to the shader. For now, only the closest LightProbe from a geometry is sent to the shader. This will be enhanced later as it's obviously not the best way to handle this. - Added a LightProbeFactory for easy creation and rendering of LightPorbes and associated maps. The maps generation process can also be monitored through a Listener class. - Added various utility classses for debuging purpose. - Added a new test case for environment with multiple LightProbes. - Adapted the previous test case to the new system.
This commit is contained in:
		
							parent
							
								
									7b7c6951ad
								
							
						
					
					
						commit
						a35b499ee7
					
				| @ -35,12 +35,13 @@ import com.jme3.environment.generation.JobProgressListener; | ||||
| import com.jme3.environment.util.EnvMapUtils; | ||||
| import com.jme3.app.Application; | ||||
| import com.jme3.app.state.BaseAppState; | ||||
| 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.scene.Node; | ||||
| import com.jme3.scene.Spatial; | ||||
| import com.jme3.texture.FrameBuffer; | ||||
| import com.jme3.texture.Image; | ||||
| import com.jme3.texture.Texture2D; | ||||
| @ -54,9 +55,12 @@ import java.util.concurrent.Callable; | ||||
| 
 | ||||
| /** | ||||
|  * A 360 camera that can capture a cube map of a scene, and then generate the | ||||
|  * Prefiltered Environment cube Map and the Irradiance cube Map needed for PBE | ||||
|  * Prefiltered Environment cube Map and the Irradiance cube Map needed for PBR | ||||
|  * indirect lighting | ||||
|  *  | ||||
|  * @see LightProbeFactory | ||||
|  * @see LightProbe | ||||
|  * | ||||
|  * @author Nehon | ||||
|  */ | ||||
| public class EnvironmentCamera extends BaseAppState { | ||||
| @ -105,10 +109,6 @@ public class EnvironmentCamera extends BaseAppState { | ||||
| 
 | ||||
|     private final List<SnapshotJob> jobs = new ArrayList<SnapshotJob>(); | ||||
| 
 | ||||
|     // debug to be removed | ||||
|     private Node debugPfemCm; | ||||
|     private Node debugIrrCm; | ||||
| 
 | ||||
|     /** | ||||
|      * Creates an EnvironmentCamera with a size of 128 | ||||
|      */ | ||||
| @ -154,9 +154,10 @@ public class EnvironmentCamera extends BaseAppState { | ||||
|      * @param scene the scene to snapshot. | ||||
|      * @param done a callback to call when the snapshot is done. | ||||
|      */ | ||||
|     public void snapshot(final Node scene, final JobProgressListener<TextureCubeMap> done) { | ||||
|     public void snapshot(final Spatial scene, final JobProgressListener<TextureCubeMap> done) { | ||||
|         getApplication().enqueue(new Callable<Void>() { | ||||
| 
 | ||||
|             @Override | ||||
|             public Void call() throws Exception { | ||||
|                 SnapshotJob job = new SnapshotJob(done, scene); | ||||
|                 jobs.add(job); | ||||
| @ -195,37 +196,10 @@ public class EnvironmentCamera extends BaseAppState { | ||||
|         return position; | ||||
|     } | ||||
|      | ||||
|      | ||||
| 
 | ||||
| //    /** | ||||
| //     * Displays or cycles through the generated maps. | ||||
| //     */ | ||||
| //    public void toggleDebug() { | ||||
| //        if (debugPfemCm == null) { | ||||
| //            debugPfemCm = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(currentEnvProbe.getPrefilteredEnvMap(), getApplication().getAssetManager()); | ||||
| //            debugPfemCm.setLocalTranslation(getApplication().getGuiViewPort().getCamera().getWidth() - 532, 20, 0); | ||||
| //        } | ||||
| //        if (debugIrrCm == null) { | ||||
| //            debugIrrCm = EnvMapUtils.getCubeMapCrossDebugView(currentEnvProbe.getIrradianceMap(), getApplication().getAssetManager()); | ||||
| //            debugIrrCm.setLocalTranslation(getApplication().getGuiViewPort().getCamera().getWidth() - 532, 20, 0); | ||||
| //        } | ||||
| // | ||||
| //        if (debugIrrCm.getParent() != null) { | ||||
| //            debugIrrCm.removeFromParent(); | ||||
| //            ((Node) (getApplication().getGuiViewPort().getScenes().get(0))).attachChild(debugPfemCm); | ||||
| // | ||||
| //        } else if (debugPfemCm.getParent() != null) { | ||||
| //            debugPfemCm.removeFromParent(); | ||||
| //        } else { | ||||
| //            ((Node) (getApplication().getGuiViewPort().getScenes().get(0))).attachChild(debugIrrCm); | ||||
| //        } | ||||
| // | ||||
| //    } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the camera position. | ||||
|      * Sets the camera position in world space. | ||||
|      * | ||||
|      * @param position | ||||
|      * @param position the position in world space | ||||
|      */ | ||||
|     public void setPosition(Vector3f position) { | ||||
|         this.position.set(position); | ||||
| @ -336,12 +310,15 @@ public class EnvironmentCamera extends BaseAppState { | ||||
|         return offBuffer; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * An inner class to keep track on a snapshot job. | ||||
|      */ | ||||
|     private class SnapshotJob { | ||||
| 
 | ||||
|         JobProgressListener<TextureCubeMap> callback; | ||||
|         Node scene; | ||||
|         Spatial scene; | ||||
| 
 | ||||
|         public SnapshotJob(JobProgressListener callback, Node scene) { | ||||
|         public SnapshotJob(JobProgressListener callback, Spatial scene) { | ||||
|             this.callback = callback; | ||||
|             this.scene = scene; | ||||
|         } | ||||
|  | ||||
| @ -0,0 +1,246 @@ | ||||
| /* | ||||
|  * 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.environment; | ||||
| 
 | ||||
| import com.jme3.light.LightProbe; | ||||
| import com.jme3.environment.generation.JobProgressListener; | ||||
| import com.jme3.environment.generation.PrefilteredEnvMapFaceGenerator; | ||||
| import com.jme3.environment.generation.IrradianceMapGenerator; | ||||
| import com.jme3.environment.util.EnvMapUtils; | ||||
| import com.jme3.environment.generation.JobProgressAdapter; | ||||
| import com.jme3.app.Application; | ||||
| import com.jme3.scene.Node; | ||||
| import com.jme3.scene.Spatial; | ||||
| import com.jme3.texture.TextureCubeMap; | ||||
| import java.util.concurrent.ScheduledThreadPoolExecutor; | ||||
| 
 | ||||
| /** | ||||
|  * This Factory allows to create LightProbes within a scene given an EnvironmentCamera. | ||||
|  *  | ||||
|  * Since the process can be long, you can provide a JobProgressListener that  | ||||
|  * will be notified of the ongoing generation process when calling the makeProbe method. | ||||
|  *  | ||||
|  * The process is the folowing :  | ||||
|  * 1. Create an EnvironmentCamera | ||||
|  * 2. give it a position in the scene | ||||
|  * 3. call {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)} | ||||
|  * 4. add the created LightProbe to a node with the {@link Node#addLight(com.jme3.light.Light) } method. | ||||
|  *  | ||||
|  * Optionally for step 3 call {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node, com.jme3.environment.generation.JobProgressListener) } | ||||
|  * with a {@link JobProgressListener} to be notified of the progress of the generation process. | ||||
|  *  | ||||
|  * The generation will be split in several threads for faster generation.  | ||||
|  *  | ||||
|  * This class is entirely thread safe and can be called from any thread.  | ||||
|  *  | ||||
|  * Note that in case you are using a {@link JobProgressListener} all the its  | ||||
|  * method will be called inside and app.enqueu callable. | ||||
|  * This means that it's completely safe to modify the scenegraph within the  | ||||
|  * Listener method, but also means that the even will be delayed until next update loop. | ||||
|  *  | ||||
|  * @see EnvironmentCamera | ||||
|  * @author bouquet | ||||
|  */ | ||||
| public class LightProbeFactory { | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a LightProbe with the giver EnvironmentCamera in the given scene. | ||||
|      *  | ||||
|      * Note that this is an assynchronous process that will run on multiple threads. | ||||
|      * The process is thread safe. | ||||
|      * The created lightProbe will only be marked as ready when the rendering process is done. | ||||
|      *  | ||||
|      * If you want to monitor the process use {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node, com.jme3.environment.generation.JobProgressListener) } | ||||
|      *  | ||||
|      * | ||||
|      *  | ||||
|      * @see LightProbe | ||||
|      * @see EnvironmentCamera | ||||
|      * @param envCam the EnvironmentCamera | ||||
|      * @param scene the Scene | ||||
|      * @return the created LightProbe | ||||
|      */ | ||||
|     public static LightProbe makeProbe(final EnvironmentCamera envCam, Spatial scene) { | ||||
|         return makeProbe(envCam, scene, null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a LightProbe with the giver EnvironmentCamera in the given scene. | ||||
|      *  | ||||
|      * Note that this is an assynchronous process that will run on multiple threads. | ||||
|      * The process is thread safe. | ||||
|      * The created lightProbe will only be marked as ready when the rendering process is done. | ||||
|      *       | ||||
|      * The JobProgressListener will be notified of the progress of the generation.  | ||||
|      * Note that you can also use a {@link JobProgressAdapter}.  | ||||
|      *  | ||||
|      * @see LightProbe | ||||
|      * @see EnvironmentCamera | ||||
|      * @see JobProgressListener | ||||
|       | ||||
|      * @param envCam the EnvironmentCamera | ||||
|      * @param scene the Scene | ||||
|      * @param listener the listener of the genration progress. | ||||
|      * @return the created LightProbe | ||||
|      */ | ||||
|     public static LightProbe makeProbe(final EnvironmentCamera envCam, Spatial scene, final JobProgressListener<LightProbe> listener) { | ||||
|         final LightProbe probe = new LightProbe(); | ||||
|         probe.setPosition(envCam.getPosition()); | ||||
|         probe.setIrradianceMap(EnvMapUtils.createIrradianceMap(envCam.getSize(), envCam.getImageFormat())); | ||||
|         probe.setPrefilteredMap(EnvMapUtils.createPrefilteredEnvMap(envCam.getSize(), envCam.getImageFormat())); | ||||
|         envCam.snapshot(scene, new JobProgressAdapter<TextureCubeMap>() { | ||||
| 
 | ||||
|             @Override | ||||
|             public void done(TextureCubeMap map) { | ||||
|                 generatePbrMaps(map, probe, envCam.getApplication(), listener); | ||||
|             } | ||||
|         }); | ||||
|         return probe; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Internally called to generate the maps. | ||||
|      * This method will spawn 7 thread (one for the IrradianceMap, and one for each face of the prefiltered env map). | ||||
|      * Those threads will be executed in a ScheduledThreadPoolExecutor that will be shutdown when the genration is done. | ||||
|      *  | ||||
|      * @param envMap the raw env map rendered by the env camera | ||||
|      * @param probe the LigthProbe to generate maps for | ||||
|      * @param app the Application | ||||
|      * @param listener a progress listener. (can be null if no progress reporting is needed) | ||||
|      */ | ||||
|     private static void generatePbrMaps(TextureCubeMap envMap, final LightProbe probe, Application app, final JobProgressListener<LightProbe> listener) { | ||||
|         IrradianceMapGenerator irrMapGenerator; | ||||
|         PrefilteredEnvMapFaceGenerator[] pemGenerators = new PrefilteredEnvMapFaceGenerator[6]; | ||||
| 
 | ||||
|         final JobState jobState = new JobState(new ScheduledThreadPoolExecutor(7)); | ||||
| 
 | ||||
|         irrMapGenerator = new IrradianceMapGenerator(app, new JobListener(listener, jobState, probe, 6)); | ||||
|         int size = envMap.getImage().getWidth(); | ||||
|         irrMapGenerator.setGenerationParam(EnvMapUtils.duplicateCubeMap(envMap), size, EnvMapUtils.FixSeamsMethod.Wrap, probe.getIrradianceMap()); | ||||
| 
 | ||||
|         jobState.executor.execute(irrMapGenerator); | ||||
| 
 | ||||
|         for (int i = 0; i < pemGenerators.length; i++) { | ||||
|             pemGenerators[i] = new PrefilteredEnvMapFaceGenerator(app, i, new JobListener(listener, jobState, probe, i)); | ||||
|             pemGenerators[i].setGenerationParam(EnvMapUtils.duplicateCubeMap(envMap), size, EnvMapUtils.FixSeamsMethod.Wrap, probe.getPrefilteredEnvMap()); | ||||
|             jobState.executor.execute(pemGenerators[i]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * An inner class to keep the state of a generation process | ||||
|      */ | ||||
|     private static class JobState { | ||||
| 
 | ||||
|         double progress[] = new double[7]; | ||||
|         boolean done[] = new boolean[7]; | ||||
|         ScheduledThreadPoolExecutor executor; | ||||
|         boolean started = false; | ||||
| 
 | ||||
|         public JobState(ScheduledThreadPoolExecutor executor) { | ||||
|             this.executor = executor; | ||||
|         } | ||||
| 
 | ||||
|         boolean isDone() { | ||||
|             for (boolean d : done) { | ||||
|                 if (d == false) { | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         float getProgress() { | ||||
|             float mean = 0; | ||||
|             for (double progres : progress) { | ||||
|                 mean += progres; | ||||
|             } | ||||
|             return mean / 7f; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * An inner JobProgressListener to controll the genration process and properly clean up when it's done | ||||
|      */ | ||||
|     private static class JobListener extends JobProgressAdapter<Integer> { | ||||
| 
 | ||||
|         JobProgressListener<LightProbe> globalListener; | ||||
|         JobState jobState; | ||||
|         LightProbe probe; | ||||
| 
 | ||||
|         int index; | ||||
| 
 | ||||
|         public JobListener(JobProgressListener<LightProbe> globalListener, JobState jobState, LightProbe probe, int index) { | ||||
|             this.globalListener = globalListener; | ||||
|             this.jobState = jobState; | ||||
|             this.probe = probe; | ||||
|             this.index = index; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void start() { | ||||
|             if (globalListener != null && !jobState.started) { | ||||
|                 jobState.started = true; | ||||
|                 globalListener.start(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void progress(double value) { | ||||
|             jobState.progress[index] = value; | ||||
|             if (globalListener != null) { | ||||
|                 globalListener.progress(jobState.getProgress()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void done(Integer result) { | ||||
|             if (globalListener != null) { | ||||
|                 if (result < 6) { | ||||
|                     globalListener.step("Prefiltered env map face " + result + " generated"); | ||||
|                 } else { | ||||
|                     globalListener.step("Irradiance map generated"); | ||||
| 
 | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             jobState.done[index] = true; | ||||
|             if (jobState.isDone()) { | ||||
|                 probe.setReady(true); | ||||
|                 if (globalListener != null) { | ||||
|                     globalListener.done(probe); | ||||
|                 } | ||||
|                 jobState.executor.shutdownNow(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,59 @@ | ||||
| /* | ||||
|  * 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.environment.generation; | ||||
| 
 | ||||
| /** | ||||
|  * Abstract Adapter class that implements optional methods of JobProgressListener. | ||||
|  * Extends this class instead of implementing a JobProgressListener if you need  | ||||
|  * only a subset of method implemented. | ||||
|  *  | ||||
|  * @author nehon | ||||
|  * @param <T> | ||||
|  */ | ||||
| public abstract class JobProgressAdapter<T> implements JobProgressListener<T>{ | ||||
| 
 | ||||
|     @Override | ||||
|     public void progress(double value) {         | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void start() { | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void step(String message) { | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public abstract void done(T result); | ||||
|      | ||||
| } | ||||
| @ -0,0 +1,67 @@ | ||||
| /* | ||||
|  * 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.environment.generation; | ||||
| 
 | ||||
| /** | ||||
|  * An interface listener that will be notified of the progress of an asynchronous | ||||
|  * generation job. | ||||
|  *   | ||||
|  * | ||||
|  * @author nehon | ||||
|  * @param <T> The type of object generated. | ||||
|  */ | ||||
| public interface JobProgressListener<T> { | ||||
|      | ||||
|     /** | ||||
|      * Called when the process starts. | ||||
|      */ | ||||
|     public void start(); | ||||
|      | ||||
|     /** | ||||
|      * Can be called when a step of the process has been completed with a relevant message. | ||||
|      * @param message the message stating of the paricular step completion. | ||||
|      */ | ||||
|     public void step(String message); | ||||
|      | ||||
|     /** | ||||
|      * Called when the process has made some progress. | ||||
|      * @param value a value from 0 to 1 representing the percentage of completion of the process. | ||||
|      */ | ||||
|     public void progress(double value); | ||||
|      | ||||
|     /** | ||||
|      * Called when the process is done. | ||||
|      * @param result the object generated by the process. | ||||
|      */ | ||||
|     public void done(T result); | ||||
|      | ||||
| } | ||||
| @ -46,7 +46,6 @@ import static com.jme3.environment.util.EnvMapUtils.getHammersleyPoint; | ||||
| import static com.jme3.environment.util.EnvMapUtils.getRoughnessFromMip; | ||||
| import static com.jme3.environment.util.EnvMapUtils.getSampleFromMip; | ||||
| import static com.jme3.environment.util.EnvMapUtils.getVectorFromCubemapFaceTexCoord; | ||||
| import com.jme3.math.FastMath; | ||||
| import java.util.concurrent.Callable; | ||||
| import java.util.logging.Level; | ||||
| import java.util.logging.Logger; | ||||
|  | ||||
| @ -0,0 +1,177 @@ | ||||
|  /* | ||||
|  * 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.environment.util; | ||||
| 
 | ||||
| import com.jme3.asset.AssetManager; | ||||
| import com.jme3.material.Material; | ||||
| import com.jme3.math.ColorRGBA; | ||||
| import com.jme3.math.FastMath; | ||||
| import com.jme3.scene.Geometry; | ||||
| import com.jme3.scene.Mesh; | ||||
| import com.jme3.scene.VertexBuffer.Type; | ||||
| import com.jme3.util.BufferUtils; | ||||
| import java.nio.FloatBuffer; | ||||
| import java.nio.ShortBuffer; | ||||
| 
 | ||||
| /** | ||||
|  *  | ||||
|  * A debuging shape for a BoundingSphere  | ||||
|  * Consists of 3 axis aligned circles. | ||||
|  *  | ||||
|  * @author nehon | ||||
|  */ | ||||
| public class BoundingSphereDebug extends Mesh { | ||||
| 
 | ||||
|     protected int vertCount; | ||||
|     protected int triCount; | ||||
|     protected int radialSamples = 32; | ||||
|     protected boolean useEvenSlices; | ||||
|     protected boolean interior; | ||||
|     /** | ||||
|      * the distance from the center point each point falls on | ||||
|      */ | ||||
|     public float radius; | ||||
| 
 | ||||
|     public float getRadius() { | ||||
|         return radius; | ||||
|     } | ||||
| 
 | ||||
|     public BoundingSphereDebug() { | ||||
|         setGeometryData(); | ||||
|         setIndexData(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * builds the vertices based on the radius | ||||
|      */ | ||||
|     private void setGeometryData() { | ||||
|         setMode(Mode.Lines); | ||||
| 
 | ||||
|         FloatBuffer posBuf = BufferUtils.createVector3Buffer((radialSamples + 1) * 3); | ||||
|         FloatBuffer colBuf = BufferUtils.createVector3Buffer((radialSamples + 1) * 4); | ||||
| 
 | ||||
|         setBuffer(Type.Position, 3, posBuf); | ||||
|         setBuffer(Type.Color, 4, colBuf); | ||||
| 
 | ||||
|         // generate geometry | ||||
|         float fInvRS = 1.0f / radialSamples; | ||||
| 
 | ||||
|         // Generate points on the unit circle to be used in computing the mesh | ||||
|         // points on a sphere slice. | ||||
|         float[] afSin = new float[(radialSamples + 1)]; | ||||
|         float[] afCos = new float[(radialSamples + 1)]; | ||||
|         for (int iR = 0; iR < radialSamples; iR++) { | ||||
|             float fAngle = FastMath.TWO_PI * fInvRS * iR; | ||||
|             afCos[iR] = FastMath.cos(fAngle); | ||||
|             afSin[iR] = FastMath.sin(fAngle); | ||||
|         } | ||||
|         afSin[radialSamples] = afSin[0]; | ||||
|         afCos[radialSamples] = afCos[0]; | ||||
| 
 | ||||
|         for (int iR = 0; iR <= radialSamples; iR++) { | ||||
|             posBuf.put(afCos[iR]) | ||||
|                     .put(afSin[iR]) | ||||
|                     .put(0); | ||||
|             colBuf.put(ColorRGBA.Blue.r) | ||||
|                     .put(ColorRGBA.Blue.g) | ||||
|                     .put(ColorRGBA.Blue.b) | ||||
|                     .put(ColorRGBA.Blue.a); | ||||
| 
 | ||||
|         } | ||||
|         for (int iR = 0; iR <= radialSamples; iR++) { | ||||
|             posBuf.put(afCos[iR]) | ||||
|                     .put(0) | ||||
|                     .put(afSin[iR]); | ||||
|             colBuf.put(ColorRGBA.Green.r) | ||||
|                     .put(ColorRGBA.Green.g) | ||||
|                     .put(ColorRGBA.Green.b) | ||||
|                     .put(ColorRGBA.Green.a); | ||||
|         } | ||||
|         for (int iR = 0; iR <= radialSamples; iR++) { | ||||
|             posBuf.put(0) | ||||
|                     .put(afCos[iR]) | ||||
|                     .put(afSin[iR]); | ||||
|             colBuf.put(ColorRGBA.Yellow.r) | ||||
|                     .put(ColorRGBA.Yellow.g) | ||||
|                     .put(ColorRGBA.Yellow.b) | ||||
|                     .put(ColorRGBA.Yellow.a); | ||||
|         } | ||||
| 
 | ||||
|         updateBound(); | ||||
|         setStatic(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * sets the indices for rendering the sphere. | ||||
|      */ | ||||
|     private void setIndexData() { | ||||
| 
 | ||||
|         // allocate connectivity | ||||
|         int nbSegments = (radialSamples) * 3; | ||||
| 
 | ||||
|         ShortBuffer idxBuf = BufferUtils.createShortBuffer(2 * nbSegments); | ||||
|         setBuffer(Type.Index, 2, idxBuf); | ||||
| 
 | ||||
|         int idx = 0; | ||||
|         int segDone = 0; | ||||
|         while (segDone < nbSegments) { | ||||
|             idxBuf.put((short) idx); | ||||
|             idxBuf.put((short) (idx + 1)); | ||||
|             idx++; | ||||
|             segDone++; | ||||
|             if (segDone == radialSamples || segDone == radialSamples * 2) { | ||||
|                 idx++; | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     /** | ||||
|      * Convenience factory method that creates a debuging bounding sphere geometry | ||||
|      * @param assetManager the assetManager | ||||
|      * @return the bounding sphere debug geometry. | ||||
|      */ | ||||
|     public static Geometry createDebugSphere(AssetManager assetManager) { | ||||
|         BoundingSphereDebug b = new BoundingSphereDebug(); | ||||
|         Geometry geom = new Geometry("BoundingDebug", b); | ||||
| 
 | ||||
|         Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); | ||||
|         mat.setBoolean("VertexColor", true); | ||||
|         mat.getAdditionalRenderState().setWireframe(true); | ||||
|          | ||||
|         geom.setMaterial(mat); | ||||
|         return geom; | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,214 @@ | ||||
| /* | ||||
|  * 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.environment.util; | ||||
| 
 | ||||
| import com.jme3.app.Application; | ||||
| import com.jme3.app.state.BaseAppState; | ||||
| import com.jme3.bounding.BoundingSphere; | ||||
| 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; | ||||
| import com.jme3.scene.Spatial; | ||||
| import com.jme3.scene.shape.Sphere; | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| /** | ||||
|  * A debug state that will display LIght gizmos on screen. | ||||
|  * Still a wip and for now it only displays light probes. | ||||
|  *  | ||||
|  * @author nehon | ||||
|  */ | ||||
| public class LightsDebugState extends BaseAppState { | ||||
| 
 | ||||
|     private Node debugNode; | ||||
|     private final Map<LightProbe, Node> probeMapping = new HashMap<LightProbe, Node>(); | ||||
|     private final List<LightProbe> garbage = new ArrayList<LightProbe>(); | ||||
|     private Geometry debugGeom; | ||||
|     private Geometry debugBounds; | ||||
|     private Material debugMaterial; | ||||
|     private DebugMode debugMode = DebugMode.PrefilteredEnvMap; | ||||
|     private float probeScale = 1.0f; | ||||
|     private Spatial scene = null; | ||||
|     private final List<LightProbe> probes = new ArrayList<LightProbe>(); | ||||
| 
 | ||||
|     /** | ||||
|      * Debug mode for light probes | ||||
|      */ | ||||
|     public enum DebugMode { | ||||
| 
 | ||||
|         /** | ||||
|          * Displays the prefiltered env maps on the debug sphere | ||||
|          */ | ||||
|         PrefilteredEnvMap, | ||||
|         /** | ||||
|          * displays the Irradiance map on the debug sphere | ||||
|          */ | ||||
|         IrradianceMap | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void initialize(Application app) { | ||||
|         debugNode = new Node("Environment debug Node"); | ||||
|         Sphere s = new Sphere(16, 16, 1); | ||||
|         debugGeom = new Geometry("debugEnvProbe", s); | ||||
|         debugMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/reflect.j3md"); | ||||
|         debugGeom.setMaterial(debugMaterial); | ||||
|         debugBounds = BoundingSphereDebug.createDebugSphere(app.getAssetManager()); | ||||
|         if (scene == null) { | ||||
|             scene = app.getViewPort().getScenes().get(0); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void update(float tpf) { | ||||
|         for (Light light : scene.getWorldLightList()) { | ||||
|             switch (light.getType()) { | ||||
| 
 | ||||
|                 case Probe: | ||||
|                     LightProbe probe = (LightProbe) light; | ||||
|                     probes.add(probe); | ||||
|                     Node n = probeMapping.get(probe); | ||||
|                     if (n == null) { | ||||
|                         n = new Node("DebugProbe"); | ||||
|                         n.attachChild(debugGeom.clone(true)); | ||||
|                         n.attachChild(debugBounds.clone(false)); | ||||
|                         debugNode.attachChild(n); | ||||
|                         probeMapping.put(probe, n); | ||||
|                     } | ||||
|                     Geometry probeGeom = ((Geometry) n.getChild(0)); | ||||
|                     Material m = probeGeom.getMaterial(); | ||||
|                     probeGeom.setLocalScale(probeScale); | ||||
|                     if (probe.isReady()) { | ||||
|                         if (debugMode == DebugMode.IrradianceMap) { | ||||
|                             m.setTexture("CubeMap", probe.getIrradianceMap()); | ||||
|                         } else { | ||||
|                             m.setTexture("CubeMap", probe.getPrefilteredEnvMap()); | ||||
|                         } | ||||
|                     } | ||||
|                     n.setLocalTranslation(probe.getPosition()); | ||||
|                     n.getChild(1).setLocalScale(((BoundingSphere) probe.getBounds()).getRadius()); | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|         debugNode.updateLogicalState(tpf); | ||||
|         debugNode.updateGeometricState(); | ||||
|         cleanProbes(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set the scenes for wich to render light gizmos. | ||||
|      * @param scene  | ||||
|      */ | ||||
|     public void setScene(Spatial scene) { | ||||
|         this.scene = scene; | ||||
|     } | ||||
| 
 | ||||
|     private void cleanProbes() { | ||||
|         if (probes.size() != probeMapping.size()) { | ||||
|             for (LightProbe probe : probeMapping.keySet()) { | ||||
|                 if (!probes.contains(probe)) { | ||||
|                     garbage.add(probe); | ||||
|                 } | ||||
|             } | ||||
|             for (LightProbe probe : garbage) { | ||||
|                 probeMapping.remove(probe); | ||||
|             } | ||||
|             garbage.clear(); | ||||
|             probes.clear(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void render(RenderManager rm) { | ||||
|         rm.renderScene(debugNode, getApplication().getViewPort()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @see DebugMode | ||||
|      * @return the debug mode | ||||
|      */ | ||||
|     public DebugMode getDebugMode() { | ||||
|         return debugMode; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * sets the debug mode | ||||
|      * @see DebugMode | ||||
|      * @param debugMode the debug mode | ||||
|      */ | ||||
|     public void setDebugMode(DebugMode debugMode) { | ||||
|         this.debugMode = debugMode; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * returns the scale of the probe's debug sphere | ||||
|      * @return  | ||||
|      */ | ||||
|     public float getProbeScale() { | ||||
|         return probeScale; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * sets the scale of the probe's debug sphere | ||||
|      * @param probeScale  | ||||
|      */ | ||||
|     public void setProbeScale(float probeScale) { | ||||
|         this.probeScale = probeScale; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void cleanup(Application app) { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onEnable() { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onDisable() { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -55,6 +55,7 @@ public final class DefaultLightFilter implements LightFilter { | ||||
|         TempVars vars = TempVars.get(); | ||||
|         try { | ||||
|             LightList worldLights = geometry.getWorldLightList(); | ||||
|             boolean probeAdded = false; | ||||
|             for (int i = 0; i < worldLights.size(); i++) { | ||||
|                 Light light = worldLights.get(i); | ||||
| 
 | ||||
| @ -81,7 +82,16 @@ public final class DefaultLightFilter implements LightFilter { | ||||
|                     } | ||||
|                 } | ||||
|                  | ||||
|                 filteredLightList.add(light); | ||||
|                 if (light.getType() == Light.Type.Probe) { | ||||
|                     if (!probeAdded && ((LightProbe)light).isReady()) { | ||||
|                         //only adding the first probe (the closest to the geom as lights are sorted by the distance to the geom | ||||
|                         probeAdded = true; | ||||
|                         filteredLightList.add(light); | ||||
|                     } | ||||
| 
 | ||||
|                 } else { | ||||
|                     filteredLightList.add(light); | ||||
|                 } | ||||
|             } | ||||
|         } finally { | ||||
|             vars.release(); | ||||
|  | ||||
| @ -77,7 +77,14 @@ public abstract class Light implements Savable, Cloneable { | ||||
|          *  | ||||
|          * @see AmbientLight | ||||
|          */ | ||||
|         Ambient(3); | ||||
|         Ambient(3), | ||||
|          | ||||
|         /** | ||||
|          * Light probe | ||||
|          * @see LightProbe | ||||
|          */ | ||||
|         Probe(4); | ||||
|                  | ||||
| 
 | ||||
|         private int typeId; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										251
									
								
								jme3-core/src/main/java/com/jme3/light/LightProbe.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								jme3-core/src/main/java/com/jme3/light/LightProbe.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,251 @@ | ||||
| /* | ||||
|  * 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.asset.AssetManager; | ||||
| import com.jme3.bounding.BoundingBox; | ||||
| import com.jme3.bounding.BoundingSphere; | ||||
| import com.jme3.bounding.BoundingVolume; | ||||
| 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.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; | ||||
| 
 | ||||
| /** | ||||
|  * 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 maps held by the LightProbe : | ||||
|  * - The irradiance map (used for indirect diffuse lighting in the PBR pipeline). | ||||
|  * - The prefiltered environment map (used for indirect specular lighting and reflection in the PBE pipeline). | ||||
|  * Note that when instanciating the LightProbe, both those maps are null.  | ||||
|  * To render 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.  | ||||
|  * A light probe is ready when it has valid environment map data set. | ||||
|  * Note that you should never call setReady yourself. | ||||
|  * | ||||
|  * @see LightProbeFactory | ||||
|  * @see EnvironmentCamera | ||||
|  * @author nehon | ||||
|  */ | ||||
| public class LightProbe extends Light implements Savable { | ||||
| 
 | ||||
|     private TextureCubeMap irradianceMap; | ||||
|     private TextureCubeMap prefilteredEnvMap; | ||||
|     private BoundingVolume bounds = new BoundingSphere(1.0f, Vector3f.ZERO); | ||||
|     private boolean ready = false; | ||||
|     private Vector3f position = new Vector3f(); | ||||
|     private Node debugNode; | ||||
| 
 | ||||
|     /** | ||||
|      * 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() {         | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * returns the irradiance map texture of this Light probe. | ||||
|      * Note that this Texture may not have image data yet if the LightProbe is not ready | ||||
|      * @return the irradiance map  | ||||
|      */ | ||||
|     public TextureCubeMap getIrradianceMap() { | ||||
|         return irradianceMap; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the irradiance map | ||||
|      * @param irradianceMap the irradiance map | ||||
|      */ | ||||
|     public void setIrradianceMap(TextureCubeMap irradianceMap) { | ||||
|         this.irradianceMap = irradianceMap; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * returns the prefiltered environment map texture of this light probe | ||||
|      * Note that this Texture may not have image data yet if the LightProbe is not ready | ||||
|      * @return the prefiltered environment map | ||||
|      */ | ||||
|     public TextureCubeMap getPrefilteredEnvMap() { | ||||
|         return prefilteredEnvMap; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the prefiltered environment map  | ||||
|      * @param prefileteredEnvMap the prefiltered environment map  | ||||
|      */ | ||||
|     public void setPrefilteredMap(TextureCubeMap prefileteredEnvMap) { | ||||
|         this.prefilteredEnvMap = prefileteredEnvMap; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void write(JmeExporter ex) throws IOException { | ||||
|         super.write(ex); | ||||
|         OutputCapsule oc = ex.getCapsule(this); | ||||
|         oc.write(irradianceMap, "irradianceMap", null); | ||||
|         oc.write(prefilteredEnvMap, "prefilteredEnvMap", null); | ||||
|         oc.write(position, "position", null); | ||||
|         oc.write(bounds, "bounds", bounds); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void read(JmeImporter im) throws IOException { | ||||
|         super.read(im); | ||||
|         InputCapsule ic = im.getCapsule(this); | ||||
|         irradianceMap = (TextureCubeMap) ic.readSavable("irradianceMap", null); | ||||
|         prefilteredEnvMap = (TextureCubeMap) ic.readSavable("prefilteredEnvMap", null); | ||||
|         position = (Vector3f) ic.readSavable("position", this); | ||||
|         bounds = (BoundingVolume) ic.readSavable("bounds", bounds); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * returns the bounding volume of this LightProbe | ||||
|      * @return a bounding volume. | ||||
|      */ | ||||
|     public BoundingVolume getBounds() { | ||||
|         return bounds; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Sets the bounds of this LightProbe | ||||
|      * 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 | ||||
|      */ | ||||
|     public void setBounds(BoundingVolume bounds) { | ||||
|         if( bounds.getType()!= BoundingVolume.Type.Sphere){ | ||||
|             throw new UnsupportedOperationException("For not only BoundingSphere are suported for LightProbe"); | ||||
|         } | ||||
|         this.bounds = bounds; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * return true if the LightProbe is ready, meaning the Environment maps have | ||||
|      * been loaded or rnedered and are ready to be used by a material | ||||
|      * @return the LightProbe ready state | ||||
|      */ | ||||
|     public boolean isReady() { | ||||
|         return ready; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Don't call this method directly. | ||||
|      * It's meant to be called by additional systems that will load or render | ||||
|      * the Environment maps of the LightProbe | ||||
|      * @param ready the ready state of the LightProbe. | ||||
|      */ | ||||
|     public void setReady(boolean ready) { | ||||
|         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 EnvProbeis not ready yet, try to test isReady()"); | ||||
|         } | ||||
|         if (debugNode == null) { | ||||
|             debugNode = new Node("debug gui probe"); | ||||
|             Node debugPfemCm = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(getPrefilteredEnvMap(), manager); | ||||
|             Node debugIrrCm = EnvMapUtils.getCubeMapCrossDebugView(getIrradianceMap(), manager); | ||||
| 
 | ||||
|             debugNode.attachChild(debugIrrCm); | ||||
|             debugNode.attachChild(debugPfemCm); | ||||
|             debugPfemCm.setLocalTranslation(520, 0, 0); | ||||
|         } | ||||
| 
 | ||||
|         return debugNode; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the position of the LightProbe in world space | ||||
|      * @return the wolrd space position | ||||
|      */ | ||||
|     public Vector3f getPosition() { | ||||
|         return position; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the position of the LightProbe in world space | ||||
|      * @param position the wolrd space position | ||||
|      */ | ||||
|     public void setPosition(Vector3f position) { | ||||
|         this.position.set(position); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean intersectsBox(BoundingBox box, TempVars vars) { | ||||
|         return getBounds().intersectsBoundingBox(box); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean intersectsFrustum(Camera camera, TempVars vars) { | ||||
|         return camera.contains(bounds) != Camera.FrustumIntersect.Outside; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void computeLastDistance(Spatial owner) { | ||||
|         if (owner.getWorldBound() != null) { | ||||
|             BoundingVolume bv = owner.getWorldBound(); | ||||
|             lastDistance = bv.distanceSquaredTo(position); | ||||
|         } else { | ||||
|             lastDistance = owner.getWorldTranslation().distanceSquared(position); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Type getType() { | ||||
|         return Type.Probe; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -34,6 +34,7 @@ package com.jme3.material; | ||||
| import com.jme3.asset.AssetKey; | ||||
| import com.jme3.asset.AssetManager; | ||||
| import com.jme3.asset.CloneableSmartAsset; | ||||
| import com.jme3.bounding.BoundingSphere; | ||||
| import com.jme3.export.*; | ||||
| import com.jme3.light.*; | ||||
| import com.jme3.material.RenderState.BlendMode; | ||||
| @ -104,6 +105,10 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|     private int sortingId = -1; | ||||
|     private transient ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1); | ||||
| 
 | ||||
|     //Env textures units | ||||
|     int irrUnit = -1; | ||||
|     int pemUnit = -1; | ||||
|      | ||||
|     public Material(MaterialDef def) { | ||||
|         if (def == null) { | ||||
|             throw new NullPointerException("Material definition cannot be null"); | ||||
| @ -505,22 +510,26 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|         paramValues.remove(name); | ||||
|         if (matParam instanceof MatParamTexture) { | ||||
|             int texUnit = ((MatParamTexture) matParam).getUnit(); | ||||
|             nextTexUnit--; | ||||
|             for (MatParam param : paramValues.values()) { | ||||
|                 if (param instanceof MatParamTexture) { | ||||
|                     MatParamTexture texParam = (MatParamTexture) param; | ||||
|                     if (texParam.getUnit() > texUnit) { | ||||
|                         texParam.setUnit(texParam.getUnit() - 1); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             sortingId = -1; | ||||
|             removeTexUnit(texUnit); | ||||
|         } | ||||
|         if (technique != null) { | ||||
|             technique.notifyParamChanged(name, null, null); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected void removeTexUnit(int texUnit) { | ||||
|         nextTexUnit--; | ||||
|         for (MatParam param : paramValues.values()) { | ||||
|             if (param instanceof MatParamTexture) { | ||||
|                 MatParamTexture texParam = (MatParamTexture) param; | ||||
|                 if (texParam.getUnit() > texUnit) { | ||||
|                     texParam.setUnit(texParam.getUnit() - 1); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         sortingId = -1; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set a texture parameter. | ||||
|      * | ||||
| @ -750,6 +759,9 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|         Uniform lightData = shader.getUniform("g_LightData"); | ||||
|         lightData.setVector4Length(numLights * 3);//8 lights * max 3 | ||||
|         Uniform ambientColor = shader.getUniform("g_AmbientLightColor"); | ||||
|         Uniform lightProbeData = shader.getUniform("g_LightProbeData"); | ||||
|         Uniform lightProbeIrrMap = shader.getUniform("g_IrradianceMap"); | ||||
|         Uniform lightProbePemMap = shader.getUniform("g_PrefEnvMap"); | ||||
|          | ||||
|          | ||||
|         if (startIndex != 0) { | ||||
| @ -825,6 +837,32 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|                         lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex); | ||||
|                         lightDataIndex++; | ||||
|                         break; | ||||
|                     case Probe: | ||||
|                          | ||||
|                         //There should be a better way to handle these texture units | ||||
|                         //for now they are removed and reassign on every frame which is a waste.                         | ||||
|                         if (irrUnit != -1) { | ||||
|                             lightProbeIrrMap.clearValue(); | ||||
|                             lightProbePemMap.clearValue(); | ||||
|                             removeTexUnit(irrUnit); | ||||
|                             removeTexUnit(pemUnit); | ||||
|                             irrUnit = -1; | ||||
|                             pemUnit = -1; | ||||
|                         } | ||||
|                         endIndex++; | ||||
|                         LightProbe probe = (LightProbe)l; | ||||
|                         BoundingSphere s = (BoundingSphere)probe.getBounds(); | ||||
|                         tmpVec.set(probe.getPosition().x, probe.getPosition().y, probe.getPosition().z, 1f/s.getRadius()); | ||||
|                         lightProbeData.setValue(VarType.Vector4, tmpVec); | ||||
|                         if( irrUnit == -1 ){ | ||||
|                             irrUnit = nextTexUnit++; | ||||
|                             pemUnit = nextTexUnit++; | ||||
|                         } | ||||
|                         rm.getRenderer().setTexture(irrUnit, probe.getIrradianceMap()); | ||||
|                         lightProbeIrrMap.setValue(VarType.Int, irrUnit); | ||||
|                         rm.getRenderer().setTexture(pemUnit, probe.getPrefilteredEnvMap()); | ||||
|                         lightProbePemMap.setValue(VarType.Int, pemUnit); | ||||
|                         break; | ||||
|                     default: | ||||
|                         throw new UnsupportedOperationException("Unknown type of light: " + l.getType()); | ||||
|                 } | ||||
|  | ||||
| @ -19,11 +19,12 @@ uniform float m_Metallic; | ||||
| varying vec3 wPosition;     | ||||
| 
 | ||||
| 
 | ||||
| #ifdef INDIRECT_LIGHTING | ||||
|   uniform sampler2D m_IntegrateBRDF; | ||||
|   uniform samplerCube m_PrefEnvMap; | ||||
|   uniform samplerCube m_IrradianceMap; | ||||
| #endif | ||||
| //#ifdef INDIRECT_LIGHTING | ||||
| //  uniform sampler2D m_IntegrateBRDF; | ||||
|   uniform samplerCube g_PrefEnvMap; | ||||
|   uniform samplerCube g_IrradianceMap; | ||||
|   uniform vec4 g_ProbeData; | ||||
| //#endif | ||||
| 
 | ||||
| #ifdef BASECOLORMAP | ||||
|   uniform sampler2D m_BaseColorMap; | ||||
| @ -201,8 +202,10 @@ void main(){ | ||||
|         gl_FragColor.rgb += directLighting * fallOff; | ||||
|     } | ||||
| 
 | ||||
|     #ifdef INDIRECT_LIGHTING | ||||
|  //   #ifdef INDIRECT_LIGHTING | ||||
|         vec3 rv = reflect(-viewDir.xyz, normal.xyz); | ||||
|         //prallax fix for spherical bounds. | ||||
|         rv = g_ProbeData.w * (wPosition - g_ProbeData.xyz) +rv; | ||||
|         | ||||
|          //horizon fade from http://marmosetco.tumblr.com/post/81245981087 | ||||
|         float horiz = dot(rv, wNormal.xyz); | ||||
| @ -212,15 +215,15 @@ void main(){ | ||||
|          | ||||
|         vec3 indirectDiffuse = vec3(0.0); | ||||
|         vec3 indirectSpecular = vec3(0.0);     | ||||
|         indirectDiffuse = textureCube(m_IrradianceMap, rv.xyz).rgb * albedo.rgb; | ||||
|         indirectDiffuse = textureCube(g_IrradianceMap, rv.xyz).rgb * albedo.rgb; | ||||
|          | ||||
|         indirectSpecular = ApproximateSpecularIBL(m_PrefEnvMap,m_IntegrateBRDF, specularColor.rgb, Roughness, ndotv, rv.xyz); | ||||
|         indirectSpecular = ApproximateSpecularIBLPolynomial(g_PrefEnvMap, specularColor.rgb, Roughness, ndotv, rv.xyz); | ||||
|         indirectSpecular *= vec3(horiz); | ||||
| 
 | ||||
|         vec3 indirectLighting =  indirectDiffuse + indirectSpecular; | ||||
|          | ||||
|         gl_FragColor.rgb = gl_FragColor.rgb + indirectLighting ;         | ||||
|     #endif | ||||
|   //  #endif | ||||
|   | ||||
|     #if defined(EMISSIVE) || defined (EMISSIVEMAP) | ||||
|         #ifdef EMISSIVEMAP | ||||
|  | ||||
| @ -37,6 +37,8 @@ MaterialDef PBR Lighting { | ||||
|         Texture2D SpecularMap | ||||
|         Texture2D GlossMap | ||||
| 
 | ||||
|         Vector4 ProbeData | ||||
| 
 | ||||
|         // Prefiltered Env Map for indirect specular lighting | ||||
|         TextureCubeMap PrefEnvMap -LINEAR | ||||
|          | ||||
| @ -132,7 +134,7 @@ MaterialDef PBR Lighting { | ||||
|             DISCARD_ALPHA : AlphaDiscardThreshold                         | ||||
|             NUM_BONES : NumberOfBones                         | ||||
|             INSTANCING : UseInstancing | ||||
|             INDIRECT_LIGHTING : IntegrateBRDF | ||||
|             //INDIRECT_LIGHTING : IntegrateBRDF | ||||
|             VERTEX_COLOR : UseVertexColor | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -0,0 +1,45 @@ | ||||
| MaterialDef Simple { | ||||
|     MaterialParameters { | ||||
|         TextureCubeMap CubeMap | ||||
|     } | ||||
|     Technique { | ||||
|         WorldParameters { | ||||
|             WorldViewProjectionMatrix | ||||
|             WorldMatrix | ||||
|             CameraPosition | ||||
|         } | ||||
|         VertexShaderNodes { | ||||
|             ShaderNode Reflect { | ||||
|                 Definition : Reflect : Common/MatDefs/ShaderNodes/Environment/reflect.j3sn | ||||
|                 InputMappings { | ||||
|                     normal = Attr.inNormal | ||||
|                     position = Global.position.xyz | ||||
|                     worldMatrix = WorldParam.WorldMatrix | ||||
|                     camPosition = WorldParam.CameraPosition | ||||
|                 } | ||||
|             } | ||||
|             ShaderNode CommonVert { | ||||
|                 Definition : CommonVert : Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn | ||||
|                 InputMappings { | ||||
|                     worldViewProjectionMatrix = WorldParam.WorldViewProjectionMatrix | ||||
|                     modelPosition = Global.position.xyz | ||||
|                 } | ||||
|                 OutputMappings { | ||||
|                     Global.position = projPosition | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         FragmentShaderNodes { | ||||
|             ShaderNode EnvMapping { | ||||
|                 Definition : EnvMapping : Common/MatDefs/ShaderNodes/Environment/envMapping.j3sn | ||||
|                 InputMappings { | ||||
|                     refVec = Reflect.refVec | ||||
|                     cubeMap = MatParam.CubeMap | ||||
|                 } | ||||
|                 OutputMappings { | ||||
|                     Global.color = color | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| ShaderNodeDefinitions{  | ||||
|     ShaderNodeDefinition EnvMapping {       | ||||
|         Type: Fragment | ||||
| 
 | ||||
|         Shader GLSL100: Common/MatDefs/ShaderNodes/Environment/envMapping100.frag | ||||
|         Shader GLSL130: Common/MatDefs/ShaderNodes/Environment/envMapping130.frag | ||||
|          | ||||
|         Documentation{ | ||||
|             fetches a texel in a cube map             | ||||
|             @input vec3 refVec the reflection vector | ||||
|             @input samplerCube cubeMap the cube map | ||||
|             @output vec4 color the output color | ||||
|         } | ||||
|         Input { | ||||
|             vec3 refVec | ||||
|             samplerCube cubeMap | ||||
|         } | ||||
|         Output { | ||||
|              vec4 color | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| 
 | ||||
| void main(){ | ||||
|         //@input vec3 refVec the reflection vector | ||||
|     //@input samplerCube cubeMap the cube map | ||||
|     //@output vec4 color the output color | ||||
| 
 | ||||
|     color = textureCube(cubeMap, refVec, 0.0); | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| 
 | ||||
| void main(){ | ||||
|         //@input vec3 refVec the reflection vector | ||||
|     //@input samplerCube cubeMap the cube map | ||||
|     //@output vec4 color the output color | ||||
| 
 | ||||
|         color = texture(cubeMap, refVec, 0.0); | ||||
| } | ||||
| @ -0,0 +1,25 @@ | ||||
| ShaderNodeDefinitions{  | ||||
|     ShaderNodeDefinition Reflect {       | ||||
|         Type: Vertex | ||||
| 
 | ||||
|         Shader GLSL100: Common/MatDefs/ShaderNodes/Environment/reflect100.vert | ||||
|          | ||||
|         Documentation{ | ||||
|             Computes the relfection vector necessary to do some environment mapping             | ||||
|             @input vec3 position position in model space | ||||
|             @input vec3 normal the normal of the vertex | ||||
|             @input vec3 camPosition camera position in world space | ||||
|             @input mat4 worldMatrix the world matrix | ||||
|             @output vec3 refVec the reflection vector | ||||
|         } | ||||
|         Input { | ||||
|             vec3 position | ||||
|             vec3 normal | ||||
|             vec3 camPosition | ||||
|             mat4 worldMatrix | ||||
|         } | ||||
|         Output { | ||||
|              vec3 refVec | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| 
 | ||||
| void main(){ | ||||
|         //@input vec3 position position in model space | ||||
|     //@input vec3 normal the normal of the vertex | ||||
|     //@input vec3 camPosition camera position in world space | ||||
|     //@input mat4 worldMatrix the world view matrix | ||||
|     //@output vec3 refVec the reflection vector | ||||
| 
 | ||||
|     vec3 worldPos = (worldMatrix * vec4(position, 1.0)).xyz; | ||||
|     vec3 N = normalize((worldMatrix * vec4(normal, 0.0)).xyz); | ||||
|     vec3 I = normalize( camPosition - worldPos  ).xyz; | ||||
|     refVec.xyz = reflect(-I, N); | ||||
| 
 | ||||
| } | ||||
| @ -106,6 +106,31 @@ void PBR_ComputeDirectLight(vec3 normal, vec3 lightDir, vec3 viewDir, | ||||
|     outSpecular = vec3(specular) * lightColor; | ||||
| } | ||||
| 
 | ||||
| vec3 EnvDFGPolynomial( vec3 specularColor, float roughness, float ndotv ){ | ||||
|     float x = 1.0 - roughness; | ||||
|     float y = ndotv; | ||||
|   | ||||
|     float b1 = -0.1688; | ||||
|     float b2 = 1.895; | ||||
|     float b3 = 0.9903; | ||||
|     float b4 = -4.853; | ||||
|     float b5 = 8.404; | ||||
|     float b6 = -5.069; | ||||
|     float bias = clamp( min( b1 * x + b2 * x * x, b3 + b4 * y + b5 * y * y + b6 * y * y * y ), 0.0, 1.0 ); | ||||
|   | ||||
|     float d0 = 0.6045; | ||||
|     float d1 = 1.699; | ||||
|     float d2 = -0.5228; | ||||
|     float d3 = -3.603; | ||||
|     float d4 = 1.404; | ||||
|     float d5 = 0.1939; | ||||
|     float d6 = 2.661; | ||||
|     float delta = clamp(( d0 + d1 * x + d2 * y + d3 * x * x + d4 * x * y + d5 * y * y + d6 * x * x * x ), 0.0, 1.0); | ||||
|     float scale = delta - bias; | ||||
|   | ||||
|     bias *= clamp( 50.0 * specularColor.y, 0.0, 1.0 ); | ||||
|     return specularColor * scale + bias; | ||||
| } | ||||
| 
 | ||||
| vec3 ApproximateSpecularIBL(samplerCube envMap,sampler2D integrateBRDF, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec){ | ||||
|     //TODO magic values should be replaced by defines. | ||||
| @ -115,6 +140,14 @@ vec3 ApproximateSpecularIBL(samplerCube envMap,sampler2D integrateBRDF, vec3 Spe | ||||
|     return PrefilteredColor * ( SpecularColor * EnvBRDF.x+ EnvBRDF.y );     | ||||
| } | ||||
| 
 | ||||
| vec3 ApproximateSpecularIBLPolynomial(samplerCube envMap, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec){ | ||||
|     //TODO magic values should be replaced by defines. | ||||
|     float Lod = log2(Roughness) * 1.2 + 6.0 - 1.0; | ||||
|     vec3 PrefilteredColor =  textureCube(envMap, refVec.xyz,Lod).rgb;     | ||||
|     return PrefilteredColor * EnvDFGPolynomial(SpecularColor, Roughness, ndotv); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										90
									
								
								jme3-examples/src/main/java/jme3test/light/TestColorApp.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								jme3-examples/src/main/java/jme3test/light/TestColorApp.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,90 @@ | ||||
| package jme3test.light; | ||||
| 
 | ||||
| import com.jme3.app.SimpleApplication; | ||||
| import com.jme3.input.ChaseCamera; | ||||
| import com.jme3.input.KeyInput; | ||||
| import com.jme3.input.controls.AnalogListener; | ||||
| import com.jme3.light.DirectionalLight; | ||||
| import com.jme3.input.controls.KeyTrigger; | ||||
| import com.jme3.material.Material; | ||||
| import com.jme3.math.ColorRGBA; | ||||
| import com.jme3.math.FastMath; | ||||
| import com.jme3.math.Quaternion; | ||||
| import com.jme3.math.Vector3f; | ||||
| import com.jme3.post.FilterPostProcessor; | ||||
| import com.jme3.renderer.queue.RenderQueue; | ||||
| import com.jme3.scene.Geometry; | ||||
| import com.jme3.scene.shape.Box; | ||||
| import com.jme3.shadow.DirectionalLightShadowFilter; | ||||
| import com.jme3.shadow.DirectionalLightShadowRenderer; | ||||
|   | ||||
| public class TestColorApp extends SimpleApplication { | ||||
|     public static void main(String[] args) { | ||||
|         TestColorApp app = new TestColorApp(); | ||||
|         app.start(); | ||||
|     } | ||||
|   | ||||
|     @Override | ||||
|     public void simpleInitApp() { | ||||
|         // Lights | ||||
|         DirectionalLight sun = new DirectionalLight(); | ||||
|         Vector3f sunPosition = new Vector3f(1, -1, 1); | ||||
|         sun.setDirection(sunPosition); | ||||
|         sun.setColor(new ColorRGBA(1f,1f,1f,1f)); | ||||
|         rootNode.addLight(sun); | ||||
|   | ||||
|         //DirectionalLightShadowFilter sun_renderer = new DirectionalLightShadowFilter(assetManager, 2048, 4); | ||||
|         DirectionalLightShadowRenderer sun_renderer = new DirectionalLightShadowRenderer(assetManager, 2048, 1); | ||||
|         sun_renderer.setLight(sun); | ||||
|         viewPort.addProcessor(sun_renderer); | ||||
|          | ||||
| //        FilterPostProcessor fpp = new FilterPostProcessor(assetManager); | ||||
| //        fpp.addFilter(sun_renderer); | ||||
| //        viewPort.addProcessor(fpp); | ||||
|          | ||||
|         rootNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive); | ||||
|   | ||||
|         // Camera | ||||
|         viewPort.setBackgroundColor(new ColorRGBA(.6f, .6f, .6f, 1f)); | ||||
|         ChaseCamera chaseCam = new ChaseCamera(cam, inputManager); | ||||
|   | ||||
|   | ||||
|         // Objects | ||||
|         // Ground Object | ||||
|         final Geometry groundBoxWhite = new Geometry("Box", new Box(7.5f, 7.5f, .25f)); | ||||
|         float[] f = {-FastMath.PI / 2, 3 * FastMath.PI / 2, 0f}; | ||||
|         groundBoxWhite.setLocalRotation(new Quaternion(f)); | ||||
|         groundBoxWhite.move(7.5f, -.75f, 7.5f); | ||||
|         final Material groundMaterial = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); | ||||
|         groundMaterial.setColor("Diffuse", new ColorRGBA(.9f, .9f, .9f, .9f)); | ||||
|         groundBoxWhite.setMaterial(groundMaterial); | ||||
|         groundBoxWhite.addControl(chaseCam); | ||||
|         rootNode.attachChild(groundBoxWhite); | ||||
|   | ||||
|         // Planter | ||||
|         Geometry planterBox = new Geometry("Box", new Box(.5f, .5f, .5f)); | ||||
|         final Material planterMaterial = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); | ||||
|         planterMaterial.setTexture("DiffuseMap", assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg")); | ||||
|         planterBox.setMaterial(groundMaterial); | ||||
|         planterBox.setLocalTranslation(10, 0, 9); | ||||
|         rootNode.attachChild(planterBox); | ||||
|   | ||||
|         // Action! | ||||
|         inputManager.addMapping("on", new KeyTrigger(KeyInput.KEY_Z)); | ||||
|         inputManager.addMapping("off", new KeyTrigger(KeyInput.KEY_X)); | ||||
|   | ||||
|         inputManager.addListener(new AnalogListener() { | ||||
|             @Override | ||||
|             public void onAnalog(String s, float v, float v1) { | ||||
|                 if (s.equals("on")) { | ||||
|                     groundBoxWhite.setMaterial(planterMaterial); | ||||
|                 } | ||||
|                 if (s.equals("off")) { | ||||
|                     groundBoxWhite.setMaterial(groundMaterial); | ||||
|                 } | ||||
|             } | ||||
|         }, "on", "off"); | ||||
|   | ||||
|         inputEnabled = true; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,96 @@ | ||||
| /* | ||||
|  * 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 jme3test.light; | ||||
| 
 | ||||
| import com.jme3.app.ChaseCameraAppState; | ||||
| import com.jme3.app.SimpleApplication; | ||||
| import com.jme3.light.AmbientLight; | ||||
| import com.jme3.light.PointLight; | ||||
| import com.jme3.material.Material; | ||||
| import com.jme3.math.ColorRGBA; | ||||
| import com.jme3.math.FastMath; | ||||
| import com.jme3.math.Vector3f; | ||||
| import com.jme3.scene.Geometry; | ||||
| import com.jme3.scene.shape.Box; | ||||
| import com.jme3.util.TangentBinormalGenerator; | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  * @author Nehon | ||||
|  */ | ||||
| public class TestTangentCube extends SimpleApplication { | ||||
| 
 | ||||
|     public static void main(String... args) { | ||||
|         TestTangentCube app = new TestTangentCube(); | ||||
|         app.start(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void simpleInitApp() { | ||||
|         Box aBox = new Box(1, 1, 1); | ||||
|         Geometry aGeometry = new Geometry("Box", aBox); | ||||
|         TangentBinormalGenerator.generate(aBox); | ||||
| 
 | ||||
|         Material aMaterial = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); | ||||
|         aMaterial.setTexture("DiffuseMap", | ||||
|                 assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg")); | ||||
|         aMaterial.setTexture("NormalMap", | ||||
|                 assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall_normal.jpg")); | ||||
|         aMaterial.setBoolean("UseMaterialColors", false); | ||||
|         aMaterial.setColor("Diffuse", ColorRGBA.White); | ||||
|         aMaterial.setColor("Specular", ColorRGBA.White); | ||||
|         aMaterial.setFloat("Shininess", 64f); | ||||
|         aGeometry.setMaterial(aMaterial); | ||||
| 
 | ||||
|         // Rotate 45 degrees to see multiple faces | ||||
|         aGeometry.rotate(FastMath.QUARTER_PI, FastMath.QUARTER_PI, 0.0f); | ||||
|         rootNode.attachChild(aGeometry); | ||||
| 
 | ||||
|         /** | ||||
|          * Must add a light to make the lit object visible! | ||||
|          */ | ||||
|         PointLight aLight = new PointLight(); | ||||
|         aLight.setPosition(new Vector3f(0, 3, 3)); | ||||
|         aLight.setColor(ColorRGBA.Red); | ||||
|         rootNode.addLight(aLight); | ||||
| // | ||||
| //        AmbientLight bLight = new AmbientLight(); | ||||
| //        bLight.setColor(ColorRGBA.Gray); | ||||
| //        rootNode.addLight(bLight); | ||||
| 
 | ||||
|          | ||||
|         ChaseCameraAppState chaser = new ChaseCameraAppState(); | ||||
|         chaser.setTarget(aGeometry); | ||||
|         getStateManager().attach(chaser); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,71 @@ | ||||
| /* | ||||
|  * 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 jme3test.light.pbr; | ||||
| 
 | ||||
| import com.jme3.environment.generation.JobProgressAdapter; | ||||
| import com.jme3.light.LightProbe; | ||||
| import java.util.logging.Level; | ||||
| import java.util.logging.Logger; | ||||
| 
 | ||||
| /** | ||||
|  * A basic logger for environment map rendering progress. | ||||
|  * @author nehon | ||||
|  */ | ||||
| public class ConsoleProgressReporter extends JobProgressAdapter<LightProbe>{ | ||||
| 
 | ||||
|     private static final Logger logger = Logger.getLogger(ConsoleProgressReporter.class.getName()); | ||||
|      | ||||
|     long time; | ||||
| 
 | ||||
|     @Override | ||||
|     public void start() { | ||||
|         time = System.currentTimeMillis(); | ||||
|         logger.log(Level.INFO,"Starting generation"); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void progress(double value) {        | ||||
|         logger.log(Level.INFO, "Progress : {0}%", (value * 100)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void step(String message) {        | ||||
|         logger.info(message); | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public void done(LightProbe result) { | ||||
|         long end = System.currentTimeMillis(); | ||||
|         logger.log(Level.INFO, "Generation done in {0}", ((float)(end - time) / 1000f)); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @ -1,6 +1,43 @@ | ||||
| /* | ||||
|  * 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 jme3test.light.pbr; | ||||
| 
 | ||||
| import com.jme3.app.SimpleApplication; | ||||
| import com.jme3.bounding.BoundingSphere; | ||||
| import com.jme3.light.LightProbe; | ||||
| import com.jme3.environment.LightProbeFactory; | ||||
| import com.jme3.environment.EnvironmentCamera; | ||||
| import com.jme3.environment.generation.JobProgressAdapter; | ||||
| import com.jme3.environment.util.LightsDebugState; | ||||
| import com.jme3.input.ChaseCamera; | ||||
| import com.jme3.input.KeyInput; | ||||
| import com.jme3.input.controls.ActionListener; | ||||
| @ -13,11 +50,12 @@ import com.jme3.math.Vector3f; | ||||
| import com.jme3.post.FilterPostProcessor; | ||||
| import com.jme3.post.filters.FXAAFilter; | ||||
| import com.jme3.post.filters.ToneMapFilter; | ||||
| import com.jme3.post.ssao.SSAOFilter; | ||||
| import com.jme3.scene.Geometry; | ||||
| import com.jme3.scene.Node; | ||||
| import com.jme3.scene.Spatial; | ||||
| import com.jme3.texture.pbr.EnvironmentCamera; | ||||
| import com.jme3.texture.plugins.ktx.KTXLoader; | ||||
| import com.jme3.util.MaterialDebugAppState; | ||||
| import com.jme3.util.SkyFactory; | ||||
| 
 | ||||
| /** | ||||
| @ -36,9 +74,7 @@ public class TestPBRLighting extends SimpleApplication { | ||||
|     private DirectionalLight dl; | ||||
|     private Node modelNode; | ||||
|     private int frame = 0;    | ||||
|     private boolean indirectLighting = true; | ||||
|     private Material pbrMat;     | ||||
|     private Material adHocMat; | ||||
| 
 | ||||
|     @Override | ||||
|     public void simpleInitApp() { | ||||
| @ -55,22 +91,34 @@ public class TestPBRLighting extends SimpleApplication { | ||||
|         dl.setColor(ColorRGBA.White); | ||||
|         rootNode.attachChild(modelNode); | ||||
| 
 | ||||
|         final EnvironmentCamera envCam = new EnvironmentCamera(128, new Vector3f(0, 3f, 0)); | ||||
|         stateManager.attach(envCam); | ||||
|        | ||||
|         FilterPostProcessor fpp = new FilterPostProcessor(assetManager); | ||||
|         fpp.addFilter(new FXAAFilter()); | ||||
|         fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(2.0f))); | ||||
|         fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(4.0f))); | ||||
|         fpp.addFilter(new SSAOFilter(0.5f, 3, 0.2f, 0.2f)); | ||||
|         viewPort.addProcessor(fpp); | ||||
| 
 | ||||
|         //Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Sky_Cloudy.hdr", SkyFactory.EnvMapType.EquirectMap); | ||||
|         Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap); | ||||
|         //Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Stonewall.hdr", SkyFactory.EnvMapType.EquirectMap); | ||||
|         //Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", SkyFactory.EnvMapType.CubeMap); | ||||
|         //Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/road.hdr", SkyFactory.EnvMapType.EquirectMap); | ||||
|         rootNode.attachChild(sky); | ||||
| 
 | ||||
|         pbrMat = assetManager.loadMaterial("Models/Tank/tank.j3m"); | ||||
|         model.setMaterial(pbrMat); | ||||
| 
 | ||||
| 
 | ||||
|         final EnvironmentCamera envCam = new EnvironmentCamera(128, new Vector3f(0, 3f, 0)); | ||||
|         stateManager.attach(envCam); | ||||
|          | ||||
| //        EnvironmentManager envManager = new EnvironmentManager(); | ||||
| //        stateManager.attach(envManager); | ||||
|          | ||||
|  //       envManager.setScene(rootNode); | ||||
|          | ||||
|         LightsDebugState debugState = new LightsDebugState(); | ||||
|         stateManager.attach(debugState); | ||||
|          | ||||
|         ChaseCamera chaser = new ChaseCamera(cam, modelNode, inputManager); | ||||
|         chaser.setDragToRotate(true); | ||||
|         chaser.setMinVerticalRotation(-FastMath.HALF_PI); | ||||
| @ -84,26 +132,8 @@ public class TestPBRLighting extends SimpleApplication { | ||||
|         inputManager.addListener(new ActionListener() { | ||||
|             @Override | ||||
|             public void onAction(String name, boolean isPressed, float tpf) { | ||||
|                 if (name.equals("toggle") && isPressed) { | ||||
|                     if (!indirectLighting) { | ||||
|                         toggleIBL(); | ||||
| 
 | ||||
|                     } else { | ||||
|                         pbrMat.clearParam("IntegrateBRDF"); | ||||
|                         indirectLighting = false; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if (name.equals("switchMats") && isPressed) { | ||||
|                     if (model.getMaterial() == pbrMat) { | ||||
|                         model.setMaterial(adHocMat); | ||||
|                     } else { | ||||
|                         model.setMaterial(pbrMat); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if (name.equals("debug") && isPressed) { | ||||
|                     envCam.toggleDebug(); | ||||
|                     //envCam.toggleDebug(); | ||||
|                 } | ||||
| 
 | ||||
|                 if (name.equals("up") && isPressed) { | ||||
| @ -133,20 +163,11 @@ public class TestPBRLighting extends SimpleApplication { | ||||
|         inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_RIGHT)); | ||||
|         inputManager.addMapping("debug", new KeyTrigger(KeyInput.KEY_D)); | ||||
|          | ||||
|     } | ||||
|          | ||||
|     private void toggleIBL() { | ||||
|         ensurePbrMat(); | ||||
|         pbrMat.setTexture("IrradianceMap", stateManager.getState(EnvironmentCamera.class).getIrradianceMap()); | ||||
|         pbrMat.setTexture("PrefEnvMap", stateManager.getState(EnvironmentCamera.class).getPrefilteredEnvMap()); | ||||
|         pbrMat.setTexture("IntegrateBRDF", assetManager.loadTexture("Common/Textures/integrateBRDF.ktx")); | ||||
|         indirectLighting = true; | ||||
|     } | ||||
|         MaterialDebugAppState debug = new MaterialDebugAppState(); | ||||
|         debug.registerBinding("Common/MatDefs/Light/PBRLighting.frag", rootNode); | ||||
|         getStateManager().attach(debug); | ||||
| 
 | ||||
|     private void ensurePbrMat() { | ||||
|         if (model.getMaterial() != pbrMat && model.getMaterial() != adHocMat) { | ||||
|             pbrMat = model.getMaterial(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
| @ -155,18 +176,22 @@ public class TestPBRLighting extends SimpleApplication { | ||||
| 
 | ||||
|         if (frame == 2) { | ||||
|             modelNode.removeFromParent(); | ||||
|             stateManager.getState(EnvironmentCamera.class).snapshot(rootNode, new Runnable() { | ||||
|             final LightProbe probe = LightProbeFactory.makeProbe(stateManager.getState(EnvironmentCamera.class), rootNode, new JobProgressAdapter<LightProbe>() { | ||||
| 
 | ||||
|                 //this code is ensured to be called in the update loop, the run method is called by the EnvCamera app state in it's update cycle | ||||
|                 @Override | ||||
|                 public void run() {                     | ||||
|                   toggleIBL(); | ||||
|                 public void done(LightProbe result) { | ||||
|                     System.err.println("Done rendering env maps"); | ||||
|                 } | ||||
|             }); | ||||
|             ((BoundingSphere)probe.getBounds()).setRadius(100); | ||||
|             rootNode.addLight(probe); | ||||
|             //getStateManager().getState(EnvironmentManager.class).addEnvProbe(probe); | ||||
|              | ||||
|         } | ||||
|         if (frame > 2 && modelNode.getParent() == null) { | ||||
|         if (frame > 10 && modelNode.getParent() == null) { | ||||
|             rootNode.attachChild(modelNode); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										378
									
								
								jme3-examples/src/main/java/jme3test/light/pbr/TestPbrEnv.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										378
									
								
								jme3-examples/src/main/java/jme3test/light/pbr/TestPbrEnv.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,378 @@ | ||||
| /* | ||||
|  * 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 jme3test.light.pbr; | ||||
| 
 | ||||
| import com.jme3.app.SimpleApplication; | ||||
| import com.jme3.bounding.BoundingSphere; | ||||
| import com.jme3.input.CameraInput; | ||||
| import com.jme3.input.KeyInput; | ||||
| import com.jme3.input.MouseInput; | ||||
| import com.jme3.input.controls.ActionListener; | ||||
| import com.jme3.input.controls.KeyTrigger; | ||||
| import com.jme3.input.controls.MouseAxisTrigger; | ||||
| import com.jme3.light.AmbientLight; | ||||
| import com.jme3.light.DirectionalLight; | ||||
| import com.jme3.material.Material; | ||||
| import com.jme3.math.ColorRGBA; | ||||
| import com.jme3.math.Quaternion; | ||||
| import com.jme3.math.Vector2f; | ||||
| import com.jme3.math.Vector3f; | ||||
| import com.jme3.renderer.queue.RenderQueue.ShadowMode; | ||||
| import com.jme3.scene.Geometry; | ||||
| import com.jme3.scene.Spatial; | ||||
| import com.jme3.scene.shape.Box; | ||||
| import com.jme3.scene.shape.Sphere; | ||||
| import com.jme3.shadow.DirectionalLightShadowRenderer; | ||||
| import com.jme3.shadow.EdgeFilteringMode; | ||||
| 
 | ||||
| import com.jme3.environment.LightProbeFactory; | ||||
| import com.jme3.environment.EnvironmentCamera; | ||||
| import com.jme3.environment.util.LightsDebugState; | ||||
| import com.jme3.light.LightProbe; | ||||
| import com.jme3.material.TechniqueDef; | ||||
| import com.jme3.post.FilterPostProcessor; | ||||
| import com.jme3.post.filters.BloomFilter; | ||||
| import com.jme3.post.filters.FXAAFilter; | ||||
| import com.jme3.post.filters.ToneMapFilter; | ||||
| import com.jme3.post.ssao.SSAOFilter; | ||||
| import com.jme3.scene.Node; | ||||
| import com.jme3.texture.plugins.ktx.KTXLoader; | ||||
| import com.jme3.util.SkyFactory; | ||||
| import com.jme3.util.TangentBinormalGenerator; | ||||
| 
 | ||||
| public class TestPbrEnv extends SimpleApplication implements ActionListener { | ||||
| 
 | ||||
|     public static final int SHADOWMAP_SIZE = 1024; | ||||
|     private Spatial[] obj; | ||||
|     private Material[] mat; | ||||
|     private DirectionalLightShadowRenderer dlsr; | ||||
|     private LightsDebugState debugState; | ||||
| 
 | ||||
|     private EnvironmentCamera envCam; | ||||
| 
 | ||||
|     private Geometry ground; | ||||
|     private Material matGroundU; | ||||
|     private Material matGroundL; | ||||
| 
 | ||||
|     private Geometry camGeom; | ||||
| 
 | ||||
|     public static void main(String[] args) { | ||||
|         TestPbrEnv app = new TestPbrEnv(); | ||||
|         app.start(); | ||||
|     }  | ||||
| 
 | ||||
|      | ||||
|     public void loadScene() { | ||||
|          | ||||
|         renderManager.setPreferredLightMode(TechniqueDef.LightMode.SinglePass); | ||||
|         renderManager.setSinglePassLightBatchSize(3); | ||||
|         obj = new Spatial[2]; | ||||
|         // Setup first view | ||||
| 
 | ||||
|         mat = new Material[2]; | ||||
|         mat[0] = assetManager.loadMaterial("jme3test/light/pbr/pbrMat.j3m"); | ||||
|         //mat[1] = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m"); | ||||
|         mat[1] = assetManager.loadMaterial("jme3test/light/pbr/pbrMat2.j3m"); | ||||
| //        mat[1].setBoolean("UseMaterialColors", true); | ||||
| //        mat[1].setColor("Ambient", ColorRGBA.White.mult(0.5f)); | ||||
| //        mat[1].setColor("Diffuse", ColorRGBA.White.clone()); | ||||
| 
 | ||||
|         obj[0] = new Geometry("sphere", new Sphere(30, 30, 2)); | ||||
|         obj[0].setShadowMode(ShadowMode.CastAndReceive); | ||||
|         obj[1] = new Geometry("cube", new Box(1.0f, 1.0f, 1.0f)); | ||||
|         obj[1].setShadowMode(ShadowMode.CastAndReceive); | ||||
|         TangentBinormalGenerator.generate(obj[1]); | ||||
|         TangentBinormalGenerator.generate(obj[0]); | ||||
| 
 | ||||
| //        for (int i = 0; i < 60; i++) { | ||||
| //            Spatial t = obj[FastMath.nextRandomInt(0, obj.length - 1)].clone(false); | ||||
| //            t.setName("Cube" + i); | ||||
| //            t.setLocalScale(FastMath.nextRandomFloat() * 10f); | ||||
| //            t.setMaterial(mat[FastMath.nextRandomInt(0, mat.length - 1)]); | ||||
| //            rootNode.attachChild(t); | ||||
| //            t.setLocalTranslation(FastMath.nextRandomFloat() * 200f, FastMath.nextRandomFloat() * 30f + 20, 30f * (i + 2f)); | ||||
| //        } | ||||
| 
 | ||||
|         for (int i = 0; i < 2; i++) { | ||||
|             Spatial t = obj[0].clone(false); | ||||
|             t.setName("Cube" + i); | ||||
|             t.setLocalScale( 10f); | ||||
|             t.setMaterial(mat[1].clone()); | ||||
|             rootNode.attachChild(t); | ||||
|             t.setLocalTranslation(i * 200f+ 100f, 50, 800f * (i)); | ||||
|         } | ||||
|          | ||||
|         Box b = new Box(1000, 2, 1000); | ||||
|         b.scaleTextureCoordinates(new Vector2f(20, 20)); | ||||
|         ground = new Geometry("soil", b); | ||||
|         TangentBinormalGenerator.generate(ground); | ||||
|         ground.setLocalTranslation(0, 10, 550); | ||||
|         matGroundU = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); | ||||
|         matGroundU.setColor("Color", ColorRGBA.Green); | ||||
| 
 | ||||
| //        matGroundL = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); | ||||
| //        Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); | ||||
| //        grass.setWrap(WrapMode.Repeat); | ||||
| //        matGroundL.setTexture("DiffuseMap", grass); | ||||
| 
 | ||||
|         matGroundL = assetManager.loadMaterial("jme3test/light/pbr/pbrMat4.j3m"); | ||||
|          | ||||
|         ground.setMaterial(matGroundL); | ||||
| 
 | ||||
|         //ground.setShadowMode(ShadowMode.CastAndReceive); | ||||
|         rootNode.attachChild(ground); | ||||
| 
 | ||||
|         l = new DirectionalLight(); | ||||
|         l.setColor(ColorRGBA.White); | ||||
|         //l.setDirection(new Vector3f(0.5973172f, -0.16583486f, 0.7846725f).normalizeLocal()); | ||||
|         l.setDirection(new Vector3f(-0.2823181f, -0.41889593f, 0.863031f).normalizeLocal()); | ||||
|          | ||||
|         rootNode.addLight(l); | ||||
| 
 | ||||
|         AmbientLight al = new AmbientLight(); | ||||
|         al.setColor(ColorRGBA.White.mult(0.5f)); | ||||
|       //  rootNode.addLight(al); | ||||
| 
 | ||||
|         //Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", SkyFactory.EnvMapType.CubeMap); | ||||
|         Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap); | ||||
|         sky.setLocalScale(350); | ||||
| 
 | ||||
|         rootNode.attachChild(sky); | ||||
|     } | ||||
|     DirectionalLight l; | ||||
| 
 | ||||
|     @Override | ||||
|     public void simpleInitApp() { | ||||
|         assetManager.registerLoader(KTXLoader.class, "ktx"); | ||||
|          | ||||
|          | ||||
|         // put the camera in a bad position | ||||
|         cam.setLocation(new Vector3f(-52.433647f, 68.69636f, -118.60924f)); | ||||
|         cam.setRotation(new Quaternion(0.10294232f, 0.25269797f, -0.027049713f, 0.96167296f));       | ||||
| 
 | ||||
|         flyCam.setMoveSpeed(100); | ||||
| 
 | ||||
|         loadScene(); | ||||
| 
 | ||||
|         dlsr = new DirectionalLightShadowRenderer(assetManager, SHADOWMAP_SIZE, 4); | ||||
|         dlsr.setLight(l); | ||||
|         //dlsr.setLambda(0.55f); | ||||
|         dlsr.setShadowIntensity(0.5f); | ||||
|         dlsr.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON); | ||||
|         //dlsr.displayDebug(); | ||||
|  //       viewPort.addProcessor(dlsr); | ||||
|          | ||||
|         FilterPostProcessor fpp = new FilterPostProcessor(assetManager); | ||||
|          | ||||
|         fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(6.0f))); | ||||
|         SSAOFilter ssao = new SSAOFilter(); | ||||
|         ssao.setIntensity(5); | ||||
|                  | ||||
|         fpp.addFilter(ssao); | ||||
|          | ||||
|         BloomFilter bloomFilter = new BloomFilter(); | ||||
|         fpp.addFilter(bloomFilter); | ||||
|         fpp.addFilter(new FXAAFilter()); | ||||
|         //viewPort.addProcessor(fpp); | ||||
| 
 | ||||
|         initInputs(); | ||||
| 
 | ||||
| //        envManager = new EnvironmentManager(); | ||||
| //        getStateManager().attach(envManager); | ||||
| //         | ||||
|         envCam = new EnvironmentCamera(); | ||||
|         getStateManager().attach(envCam); | ||||
| 
 | ||||
|         debugState = new LightsDebugState(); | ||||
|         debugState.setProbeScale(5);         | ||||
|         getStateManager().attach(debugState); | ||||
| 
 | ||||
|         camGeom = new Geometry("camGeom", new Sphere(16, 16, 2)); | ||||
| //        Material m = new Material(assetManager, "Common/MatDefs/Misc/UnshadedNodes.j3md"); | ||||
| //        m.setColor("Color", ColorRGBA.Green); | ||||
|         Material m = assetManager.loadMaterial("jme3test/light/pbr/pbrMat3.j3m"); | ||||
|         camGeom.setMaterial(m); | ||||
|         camGeom.setLocalTranslation(0, 20, 0); | ||||
|         camGeom.setLocalScale(5); | ||||
|         rootNode.attachChild(camGeom); | ||||
|          | ||||
|    //     envManager.setScene(rootNode); | ||||
|          | ||||
| //        MaterialDebugAppState debug = new MaterialDebugAppState(); | ||||
| //        debug.registerBinding("MatDefs/PBRLighting.frag", rootNode); | ||||
| //        getStateManager().attach(debug); | ||||
|          | ||||
|         flyCam.setDragToRotate(true); | ||||
|         setPauseOnLostFocus(false); | ||||
|          | ||||
|        // cam.lookAt(camGeom.getWorldTranslation(), Vector3f.UNIT_Y); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private void fixFLyCamInputs() { | ||||
|         inputManager.deleteMapping(CameraInput.FLYCAM_LEFT); | ||||
|         inputManager.deleteMapping(CameraInput.FLYCAM_RIGHT); | ||||
|         inputManager.deleteMapping(CameraInput.FLYCAM_UP); | ||||
|         inputManager.deleteMapping(CameraInput.FLYCAM_DOWN); | ||||
| 
 | ||||
|         inputManager.addMapping(CameraInput.FLYCAM_LEFT, new MouseAxisTrigger(MouseInput.AXIS_X, true)); | ||||
| 
 | ||||
|         inputManager.addMapping(CameraInput.FLYCAM_RIGHT, new MouseAxisTrigger(MouseInput.AXIS_X, false)); | ||||
| 
 | ||||
|         inputManager.addMapping(CameraInput.FLYCAM_UP, new MouseAxisTrigger(MouseInput.AXIS_Y, false)); | ||||
| 
 | ||||
|         inputManager.addMapping(CameraInput.FLYCAM_DOWN, new MouseAxisTrigger(MouseInput.AXIS_Y, true)); | ||||
| 
 | ||||
|         inputManager.addListener(flyCam, CameraInput.FLYCAM_LEFT, CameraInput.FLYCAM_RIGHT, CameraInput.FLYCAM_UP, CameraInput.FLYCAM_DOWN); | ||||
|     } | ||||
| 
 | ||||
|     private void initInputs() { | ||||
|         inputManager.addMapping("switchGroundMat", new KeyTrigger(KeyInput.KEY_M)); | ||||
|         inputManager.addMapping("snapshot", new KeyTrigger(KeyInput.KEY_SPACE)); | ||||
|         inputManager.addMapping("fc", new KeyTrigger(KeyInput.KEY_F)); | ||||
|         inputManager.addMapping("debugProbe", new KeyTrigger(KeyInput.KEY_RETURN)); | ||||
|         inputManager.addMapping("debugTex", new KeyTrigger(KeyInput.KEY_T)); | ||||
|         inputManager.addMapping("up", new KeyTrigger(KeyInput.KEY_UP)); | ||||
|         inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_DOWN)); | ||||
|         inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_RIGHT)); | ||||
|         inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_LEFT)); | ||||
| 
 | ||||
|         inputManager.addListener(this, "switchGroundMat", "snapshot", "debugTex", "debugProbe", "fc", "up", "down", "left", "right"); | ||||
|     } | ||||
|      | ||||
|     private LightProbe lastProbe; | ||||
|     private Node debugGui ; | ||||
| 
 | ||||
|     public void onAction(String name, boolean keyPressed, float tpf) { | ||||
| 
 | ||||
|         if (name.equals("switchGroundMat") && keyPressed) { | ||||
|             if (ground.getMaterial() == matGroundL) { | ||||
|                 ground.setMaterial(matGroundU); | ||||
|             } else { | ||||
|                  | ||||
|                 ground.setMaterial(matGroundL); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (name.equals("snapshot") && keyPressed) { | ||||
|             envCam.setPosition(camGeom.getWorldTranslation()); | ||||
|             lastProbe = LightProbeFactory.makeProbe(envCam, rootNode, new ConsoleProgressReporter());             | ||||
|             ((BoundingSphere)lastProbe.getBounds()).setRadius(200); | ||||
|             rootNode.addLight(lastProbe); | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         if (name.equals("fc") && keyPressed) { | ||||
| 
 | ||||
|             flyCam.setEnabled(true); | ||||
|         } | ||||
| 
 | ||||
|         if (name.equals("debugProbe") && keyPressed) { | ||||
|             //getStateManager().getState(EnvironmentCamera.class).toggleDebug(); | ||||
|             if (!debugState.isEnabled()) { | ||||
|                 debugState.setEnabled(true); | ||||
|                 debugState.setDebugMode(LightsDebugState.DebugMode.IrradianceMap); | ||||
|             } else if (debugState.getDebugMode() == LightsDebugState.DebugMode.IrradianceMap) { | ||||
|                 debugState.setDebugMode(LightsDebugState.DebugMode.PrefilteredEnvMap); | ||||
|             } else if (debugState.getDebugMode() == LightsDebugState.DebugMode.PrefilteredEnvMap) { | ||||
|                 debugState.setEnabled(false); | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|          | ||||
|         if (name.equals("debugTex") && keyPressed) { | ||||
|             if(debugGui == null || debugGui.getParent() == null){ | ||||
|                 debugGui = lastProbe.getDebugGui(assetManager); | ||||
|                 debugGui.setLocalTranslation(10, 200, 0); | ||||
|                 guiNode.attachChild(debugGui); | ||||
|             } else if(debugGui != null){ | ||||
|                 debugGui.removeFromParent(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (name.equals("up")) { | ||||
|             up = keyPressed; | ||||
|         } | ||||
|         if (name.equals("down")) { | ||||
|             down = keyPressed; | ||||
|         } | ||||
|         if (name.equals("right")) { | ||||
|             right = keyPressed; | ||||
|         } | ||||
|         if (name.equals("left")) { | ||||
|             left = keyPressed; | ||||
|         } | ||||
|         if (name.equals("fwd")) { | ||||
|             fwd = keyPressed; | ||||
|         } | ||||
|         if (name.equals("back")) { | ||||
|             back = keyPressed; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
|     boolean up = false; | ||||
|     boolean down = false; | ||||
|     boolean left = false; | ||||
|     boolean right = false; | ||||
|     boolean fwd = false; | ||||
|     boolean back = false; | ||||
|     float time = 0; | ||||
|     float s = 50f; | ||||
|     boolean initialized = false; | ||||
| 
 | ||||
|     @Override | ||||
|     public void simpleUpdate(float tpf) { | ||||
| 
 | ||||
|         if (!initialized) { | ||||
|             fixFLyCamInputs(); | ||||
|             initialized = true; | ||||
|         } | ||||
|         float val = tpf * s; | ||||
|         if (up) { | ||||
|             camGeom.move(0, 0, val); | ||||
|         } | ||||
|         if (down) { | ||||
|             camGeom.move(0, 0, -val); | ||||
| 
 | ||||
|         } | ||||
|         if (right) { | ||||
|             camGeom.move(-val, 0, 0); | ||||
| 
 | ||||
|         } | ||||
|         if (left) { | ||||
|             camGeom.move(val, 0, 0); | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,180 @@ | ||||
| /* | ||||
|  * 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 jme3test.post; | ||||
| 
 | ||||
| 
 | ||||
| import com.jme3.app.SimpleApplication; | ||||
| 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.material.Material; | ||||
| import com.jme3.math.ColorRGBA; | ||||
| import com.jme3.math.Quaternion; | ||||
| import com.jme3.math.Vector3f; | ||||
| import com.jme3.post.FilterPostProcessor; | ||||
| import com.jme3.post.filters.BloomFilter; | ||||
| import com.jme3.post.filters.BloomFilter.GlowMode; | ||||
| import com.jme3.renderer.queue.RenderQueue; | ||||
| import com.jme3.renderer.queue.RenderQueue.ShadowMode; | ||||
| import com.jme3.scene.Geometry; | ||||
| import com.jme3.scene.Spatial; | ||||
| import com.jme3.scene.debug.WireFrustum; | ||||
| import com.jme3.scene.shape.Box; | ||||
| import com.jme3.util.SkyFactory; | ||||
| 
 | ||||
| public class TestBloomAlphaThreshold extends SimpleApplication | ||||
| { | ||||
| 
 | ||||
| 	float angle; | ||||
| 	Spatial lightMdl; | ||||
| 	Spatial teapot; | ||||
| 	Geometry frustumMdl; | ||||
| 	WireFrustum frustum; | ||||
| 	boolean active = true; | ||||
| 	FilterPostProcessor fpp; | ||||
| 
 | ||||
| 	public static void main(String[] args) | ||||
| 	{ | ||||
| 		TestBloomAlphaThreshold app = new TestBloomAlphaThreshold(); | ||||
| 		app.start(); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void simpleInitApp() | ||||
| 	{ | ||||
| 		// put the camera in a bad position | ||||
| 		cam.setLocation(new Vector3f(-2.336393f, 11.91392f, -10)); | ||||
| 		cam.setRotation(new Quaternion(0.23602544f, 0.11321983f, -0.027698677f, 0.96473104f)); | ||||
| 		// cam.setFrustumFar(1000); | ||||
| 
 | ||||
| 		Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); | ||||
| 
 | ||||
| 		mat.setFloat("Shininess", 15f); | ||||
| 		mat.setBoolean("UseMaterialColors", true); | ||||
| 		mat.setColor("Ambient", ColorRGBA.Yellow.mult(0.2f)); | ||||
| 		mat.setColor("Diffuse", ColorRGBA.Yellow.mult(0.2f)); | ||||
| 		mat.setColor("Specular", ColorRGBA.Yellow.mult(0.8f)); | ||||
| 		mat.setColor("GlowColor", ColorRGBA.Green); | ||||
| 
 | ||||
| 		Material matSoil = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); | ||||
| 		matSoil.setFloat("Shininess", 15f); | ||||
| 		matSoil.setBoolean("UseMaterialColors", true); | ||||
| 		matSoil.setColor("Ambient", ColorRGBA.Gray); | ||||
| 		matSoil.setColor("Diffuse", ColorRGBA.Black); | ||||
| 		matSoil.setColor("Specular", ColorRGBA.Gray); | ||||
| 
 | ||||
| 		teapot = assetManager.loadModel("Models/Teapot/Teapot.obj"); | ||||
| 		teapot.setLocalTranslation(0, 0, 10); | ||||
| 
 | ||||
| 		teapot.setMaterial(mat); | ||||
| 		teapot.setShadowMode(ShadowMode.CastAndReceive); | ||||
| 		teapot.setLocalScale(10.0f); | ||||
| 		rootNode.attachChild(teapot); | ||||
| 
 | ||||
| 		Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -13, 550), 800, 10, 700)); | ||||
| 		soil.setMaterial(matSoil); | ||||
| 		soil.setShadowMode(ShadowMode.CastAndReceive); | ||||
| 		rootNode.attachChild(soil); | ||||
| 
 | ||||
| 		Material matBox = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); | ||||
| 		matBox.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png")); | ||||
| 		matBox.setFloat("AlphaDiscardThreshold", 0.5f); | ||||
|      | ||||
| 		Geometry box = new Geometry("box", new Box(new Vector3f(-3.5f, 10, -2), 2, 2, 2)); | ||||
| 		box.setMaterial(matBox); | ||||
|                 box.setQueueBucket(RenderQueue.Bucket.Translucent); | ||||
| 		// box.setShadowMode(ShadowMode.CastAndReceive); | ||||
| 		rootNode.attachChild(box); | ||||
| 
 | ||||
| 		DirectionalLight light = new DirectionalLight(); | ||||
| 		light.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); | ||||
| 		light.setColor(ColorRGBA.White.mult(1.5f)); | ||||
| 		rootNode.addLight(light); | ||||
| 
 | ||||
| 		// load sky | ||||
| 		Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Bright/FullskiesBlueClear03.dds", false); | ||||
| 		sky.setCullHint(Spatial.CullHint.Never); | ||||
| 		rootNode.attachChild(sky); | ||||
| 
 | ||||
| 		fpp = new FilterPostProcessor(assetManager); | ||||
| 		int numSamples = getContext().getSettings().getSamples(); | ||||
| 		if (numSamples > 0) | ||||
| 		{ | ||||
| 			fpp.setNumSamples(numSamples); | ||||
| 		} | ||||
| 
 | ||||
| 		BloomFilter bloom = new BloomFilter(GlowMode.Objects); | ||||
| 		bloom.setDownSamplingFactor(2); | ||||
| 		bloom.setBlurScale(1.37f); | ||||
| 		bloom.setExposurePower(3.30f); | ||||
| 		bloom.setExposureCutOff(0.2f); | ||||
| 		bloom.setBloomIntensity(2.45f); | ||||
| 		BloomUI ui = new BloomUI(inputManager, bloom); | ||||
| 
 | ||||
| 		viewPort.addProcessor(fpp); | ||||
| 		fpp.addFilter(bloom); | ||||
| 		initInputs(); | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	private void initInputs() | ||||
| 	{ | ||||
| 		inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE)); | ||||
| 
 | ||||
| 		ActionListener acl = new ActionListener() | ||||
| 		{ | ||||
| 
 | ||||
| 			@Override | ||||
| 			public void onAction(String name, boolean keyPressed, float tpf) | ||||
| 			{ | ||||
| 				if (name.equals("toggle") && keyPressed) | ||||
| 				{ | ||||
| 					if (active) | ||||
| 					{ | ||||
| 						active = false; | ||||
| 						viewPort.removeProcessor(fpp); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						active = true; | ||||
| 						viewPort.addProcessor(fpp); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}; | ||||
| 
 | ||||
| 		inputManager.addListener(acl, "toggle"); | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| Material My Material : Common/MatDefs/Light/PBRLighting.j3md { | ||||
|      MaterialParameters { | ||||
|         Roughness : 0.1 | ||||
|         BaseColor : 1.0 0.2 0.2 1.0 | ||||
|         Metallic : 0.0 | ||||
|         EmissivePower : 0.0 | ||||
|         EmissiveIntensity : 0.0 | ||||
|         Emissive : 1.0 0.8 0.8 1.0 | ||||
|         ParallaxHeight : 0.0 | ||||
|      } | ||||
|     AdditionalRenderState { | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| Material My Material : Common/MatDefs/Light/PBRLighting.j3md { | ||||
|      MaterialParameters { | ||||
|         Metallic : 0.0 | ||||
|         Roughness : 0.0 | ||||
|         BaseColor : 0.8 0.81960785 0.39607844 1.0 | ||||
|         EmissiveIntensity : 5.0 | ||||
|         EmissivePower : 2.0 | ||||
|         Emissive : 1.0 0.0 0.0 1.0 | ||||
|      } | ||||
|     AdditionalRenderState { | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| Material My Material : Common/MatDefs/Light/PBRLighting.j3md { | ||||
|      MaterialParameters { | ||||
|         BaseColor : 0.6 0.6 0.6 1.0 | ||||
|         Metallic : 1.0 | ||||
|         Roughness : 0.05 | ||||
|      } | ||||
|     AdditionalRenderState { | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| Material My Material : Common/MatDefs/Light/PBRLighting.j3md { | ||||
|      MaterialParameters { | ||||
|         BaseColorMap : Repeat Textures/Terrain/BrickWall/BrickWall.jpg | ||||
|         Roughness : 0.01 | ||||
|         NormalMap : Repeat Textures/Terrain/BrickWall/BrickWall_normal.jpg | ||||
|         Metallic : 1.0 | ||||
|         BaseColor : 1.0 1.0 1.0 1.0 | ||||
|      } | ||||
|     AdditionalRenderState { | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user