/* * 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", new BoundingSphere(1.0f, Vector3f.ZERO)); oc.write(ready, "ready", false); } @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", new BoundingSphere(1.0f, Vector3f.ZERO)); ready = ic.readBoolean("ready", false); } /** * 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); getBounds().setCenter(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; } @Override public String toString() { return "Light Probe : " + name + " at " + position + " / " + bounds; } @Override public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) { return getBounds().intersectsSphere(sphere); } }