Made a PointlLightShadowRenderer and a PointLightShadowFilter. - This will need a lot of refactoring as a lot of code is duplicated with the PSSMShadowRenderer - Also i plan to change the Shadow map rendering to a cubemap instead of 6 separate textures. Added a cornell box model and a test case git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9942 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
8d841b70dd
commit
66ddbb654d
@ -0,0 +1,241 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2012 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.shadow; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.export.InputCapsule; |
||||||
|
import com.jme3.export.JmeExporter; |
||||||
|
import com.jme3.export.JmeImporter; |
||||||
|
import com.jme3.export.OutputCapsule; |
||||||
|
import com.jme3.light.PointLight; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.Matrix4f; |
||||||
|
import com.jme3.math.Vector4f; |
||||||
|
import com.jme3.post.Filter; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.renderer.ViewPort; |
||||||
|
import com.jme3.renderer.queue.RenderQueue; |
||||||
|
import com.jme3.shadow.PointLightShadowRenderer.CompareMode; |
||||||
|
import com.jme3.shadow.PointLightShadowRenderer.FilterMode; |
||||||
|
import com.jme3.texture.FrameBuffer; |
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* This Filter does basically the same as a PointLightShadowRenderer except it renders |
||||||
|
* the post shadow pass as a fulscreen quad pass instead of a geometry pass. |
||||||
|
* It's mostly faster than PointLightShadowRenderer as long as you have more than a about ten shadow recieving objects. |
||||||
|
* The expense is the draw back that the shadow Recieve mode set on spatial is ignored. |
||||||
|
* So basically all and only objects that render depth in the scene receive shadows. |
||||||
|
* See this post for more details http://jmonkeyengine.org/groups/general-2/forum/topic/silly-question-about-shadow-rendering/#post-191599
|
||||||
|
* |
||||||
|
* API is basically the same as the PssmShadowRenderer; |
||||||
|
* |
||||||
|
* @author Rémy Bouquet aka Nehon |
||||||
|
*/ |
||||||
|
public class PointLightShadowFilter extends Filter { |
||||||
|
|
||||||
|
private PointLightShadowRenderer plRenderer; |
||||||
|
private ViewPort viewPort; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a PSSM Shadow Filter |
||||||
|
* More info on the technique at <a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
|
||||||
|
* @param manager the application asset manager |
||||||
|
* @param size the size of the rendered shadowmaps (512,1024,2048, etc...) |
||||||
|
* @param nbSplits the number of shadow maps rendered (the more shadow maps the more quality, the less fps). |
||||||
|
*/ |
||||||
|
public PointLightShadowFilter(AssetManager manager, int size) { |
||||||
|
super("Post Shadow"); |
||||||
|
material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); |
||||||
|
plRenderer = new PointLightShadowRenderer(manager, size, material); |
||||||
|
plRenderer.needsfallBackMaterial = true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Material getMaterial() { |
||||||
|
return material; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean isRequiresDepthTexture() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public Material getShadowMaterial() { |
||||||
|
return material; |
||||||
|
} |
||||||
|
Vector4f tmpv = new Vector4f(); |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void preFrame(float tpf) { |
||||||
|
plRenderer.preFrame(tpf); |
||||||
|
material.setMatrix4("ViewProjectionMatrixInverse", viewPort.getCamera().getViewProjectionMatrix().invert()); |
||||||
|
Matrix4f m = viewPort.getCamera().getViewProjectionMatrix(); |
||||||
|
material.setVector4("ViewProjectionMatrixRow2", tmpv.set(m.m20, m.m21, m.m22, m.m23)); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void postQueue(RenderQueue queue) { |
||||||
|
plRenderer.postQueue(queue); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { |
||||||
|
plRenderer.setPostShadowParams(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { |
||||||
|
plRenderer.initialize(renderManager, vp); |
||||||
|
this.viewPort = vp; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* gets the point light used to cast shadows with this processor |
||||||
|
* |
||||||
|
* @return the point light |
||||||
|
*/ |
||||||
|
public PointLight getLight() { |
||||||
|
return plRenderer.getLight(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* sets the light to use for casting shadows with this processor |
||||||
|
* |
||||||
|
* @param light the point light |
||||||
|
*/ |
||||||
|
public void setLight(PointLight light) { |
||||||
|
plRenderer.setLight(light); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the shdaow intensity |
||||||
|
* @see #setShadowIntensity(float shadowIntensity) |
||||||
|
* @return shadowIntensity |
||||||
|
*/ |
||||||
|
public float getShadowIntensity() { |
||||||
|
return plRenderer.getShadowIntensity(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the shadowIntensity, the value should be between 0 and 1, |
||||||
|
* a 0 value gives a bright and invisilble shadow, |
||||||
|
* a 1 value gives a pitch black shadow, |
||||||
|
* default is 0.7 |
||||||
|
* @param shadowIntensity the darkness of the shadow |
||||||
|
*/ |
||||||
|
final public void setShadowIntensity(float shadowIntensity) { |
||||||
|
plRenderer.setShadowIntensity(shadowIntensity); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the edges thickness <br> |
||||||
|
* @see #setEdgesThickness(int edgesThickness) |
||||||
|
* @return edgesThickness |
||||||
|
*/ |
||||||
|
public int getEdgesThickness() { |
||||||
|
return plRenderer.getEdgesThickness(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the shadow edges thickness. default is 1, setting it to lower values can help to reduce the jagged effect of the shadow edges |
||||||
|
* @param edgesThickness |
||||||
|
*/ |
||||||
|
public void setEdgesThickness(int edgesThickness) { |
||||||
|
plRenderer.setEdgesThickness(edgesThickness); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns true if the PssmRenderer flushed the shadow queues |
||||||
|
* @return flushQueues |
||||||
|
*/ |
||||||
|
public boolean isFlushQueues() { |
||||||
|
return plRenderer.isFlushQueues(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set this to false if you want to use several PssmRederers to have multiple shadows cast by multiple light sources. |
||||||
|
* Make sure the last PssmRenderer in the stack DO flush the queues, but not the others |
||||||
|
* @param flushQueues |
||||||
|
*/ |
||||||
|
public void setFlushQueues(boolean flushQueues) { |
||||||
|
plRenderer.setFlushQueues(flushQueues); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* sets the shadow compare mode see {@link CompareMode} for more info |
||||||
|
* @param compareMode |
||||||
|
*/ |
||||||
|
final public void setCompareMode(CompareMode compareMode) { |
||||||
|
plRenderer.setCompareMode(compareMode); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the filtering mode for shadow edges see {@link FilterMode} for more info |
||||||
|
* @param filterMode |
||||||
|
*/ |
||||||
|
final public void setFilterMode(FilterMode filterMode) { |
||||||
|
plRenderer.setFilterMode(filterMode); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Define the length over which the shadow will fade out when using a shadowZextend |
||||||
|
* @param length the fade length in world units |
||||||
|
*/ |
||||||
|
public void setShadowZFadeLength(float length){ |
||||||
|
plRenderer.setShadowZFadeLength(length); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get the length over which the shadow will fade out when using a shadowZextend |
||||||
|
* @return the fade length in world units |
||||||
|
*/ |
||||||
|
public float getShadowZFadeLength(){ |
||||||
|
return plRenderer.getShadowZFadeLength(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(JmeExporter ex) throws IOException { |
||||||
|
super.write(ex); |
||||||
|
OutputCapsule oc = ex.getCapsule(this); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void read(JmeImporter im) throws IOException { |
||||||
|
super.read(im); |
||||||
|
InputCapsule ic = im.getCapsule(this); |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,753 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2012 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.shadow; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.light.PointLight; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.Matrix4f; |
||||||
|
import com.jme3.math.Vector2f; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.post.SceneProcessor; |
||||||
|
import com.jme3.renderer.Camera; |
||||||
|
import com.jme3.renderer.Caps; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.renderer.Renderer; |
||||||
|
import com.jme3.renderer.ViewPort; |
||||||
|
import com.jme3.renderer.queue.GeometryList; |
||||||
|
import com.jme3.renderer.queue.OpaqueComparator; |
||||||
|
import com.jme3.renderer.queue.RenderQueue; |
||||||
|
import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.scene.debug.WireFrustum; |
||||||
|
import com.jme3.texture.FrameBuffer; |
||||||
|
import com.jme3.texture.Image.Format; |
||||||
|
import com.jme3.texture.Texture.MagFilter; |
||||||
|
import com.jme3.texture.Texture.MinFilter; |
||||||
|
import com.jme3.texture.Texture.ShadowCompareMode; |
||||||
|
import com.jme3.texture.Texture2D; |
||||||
|
import com.jme3.ui.Picture; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* PssmShadow renderer use Parrallel Split Shadow Mapping technique (pssm)<br> |
||||||
|
* It splits the view frustum in several parts and compute a shadow map for each |
||||||
|
* one.<br> splits are distributed so that the closer they are from the camera, |
||||||
|
* the smaller they are to maximize the resolution used of the shadow map.<br> |
||||||
|
* This result in a better quality shadow than standard shadow mapping.<br> for |
||||||
|
* more informations on this read this <a |
||||||
|
* href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a><br>
|
||||||
|
* <p/> |
||||||
|
* @author Rémy Bouquet aka Nehon |
||||||
|
*/ |
||||||
|
public class PointLightShadowRenderer implements SceneProcessor { |
||||||
|
|
||||||
|
/** |
||||||
|
* <code>FilterMode</code> specifies how shadows are filtered |
||||||
|
*/ |
||||||
|
public enum FilterMode { |
||||||
|
|
||||||
|
/** |
||||||
|
* Shadows are not filtered. Nearest sample is used, causing in blocky |
||||||
|
* shadows. |
||||||
|
*/ |
||||||
|
Nearest, |
||||||
|
/** |
||||||
|
* Bilinear filtering is used. Has the potential of being hardware |
||||||
|
* accelerated on some GPUs |
||||||
|
*/ |
||||||
|
Bilinear, |
||||||
|
/** |
||||||
|
* Dither-based sampling is used, very cheap but can look bad at low |
||||||
|
* resolutions. |
||||||
|
*/ |
||||||
|
Dither, |
||||||
|
/** |
||||||
|
* 4x4 percentage-closer filtering is used. Shadows will be smoother at |
||||||
|
* the cost of performance |
||||||
|
*/ |
||||||
|
PCF4, |
||||||
|
/** |
||||||
|
* 8x8 percentage-closer filtering is used. Shadows will be smoother at |
||||||
|
* the cost of performance |
||||||
|
*/ |
||||||
|
PCFPOISSON, |
||||||
|
/** |
||||||
|
* 8x8 percentage-closer filtering is used. Shadows will be smoother at |
||||||
|
* the cost of performance |
||||||
|
*/ |
||||||
|
PCF8 |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Specifies the shadow comparison mode |
||||||
|
*/ |
||||||
|
public enum CompareMode { |
||||||
|
|
||||||
|
/** |
||||||
|
* Shadow depth comparisons are done by using shader code |
||||||
|
*/ |
||||||
|
Software, |
||||||
|
/** |
||||||
|
* Shadow depth comparisons are done by using the GPU's dedicated |
||||||
|
* shadowing pipeline. |
||||||
|
*/ |
||||||
|
Hardware; |
||||||
|
} |
||||||
|
protected final int CAM_NUMBER = 6; |
||||||
|
protected PointLight light; |
||||||
|
//common
|
||||||
|
protected float shadowMapSize; |
||||||
|
protected float shadowIntensity = 0.7f; |
||||||
|
protected float zFarOverride = 0; |
||||||
|
protected RenderManager renderManager; |
||||||
|
protected ViewPort viewPort; |
||||||
|
protected FrameBuffer[] shadowFB; |
||||||
|
protected Texture2D[] shadowMaps; |
||||||
|
protected Texture2D dummyTex; |
||||||
|
protected Camera[] shadowCams; |
||||||
|
protected Material preshadowMat; |
||||||
|
protected Material postshadowMat; |
||||||
|
protected GeometryList splitOccluders = new GeometryList(new OpaqueComparator()); |
||||||
|
protected Matrix4f[] lightViewProjectionsMatrices; |
||||||
|
protected boolean noOccluders = false; |
||||||
|
protected AssetManager assetManager; |
||||||
|
protected boolean debug = false; |
||||||
|
protected float edgesThickness = 1.0f; |
||||||
|
protected FilterMode filterMode; |
||||||
|
protected CompareMode compareMode; |
||||||
|
protected Picture[] dispPic; |
||||||
|
protected boolean flushQueues = true; |
||||||
|
// define if the fallback material should be used.
|
||||||
|
protected boolean needsfallBackMaterial = false; |
||||||
|
//Name of the post material technique
|
||||||
|
protected String postTechniqueName = "PostShadow"; |
||||||
|
//flags to know when to change params in the materials
|
||||||
|
protected boolean applyHWShadows = true; |
||||||
|
protected boolean applyFilterMode = true; |
||||||
|
protected boolean applyPCFEdge = true; |
||||||
|
protected boolean applyShadowIntensity = true; |
||||||
|
//a list of material of the post shadow queue geometries.
|
||||||
|
protected List<Material> matCache = new ArrayList<Material>(); |
||||||
|
//Holding the info for fading shadows in the far distance
|
||||||
|
protected Vector2f fadeInfo; |
||||||
|
protected float fadeLength; |
||||||
|
protected boolean applyFadeInfo = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a PSSM Shadow Renderer More info on the technique at <a |
||||||
|
* href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
|
||||||
|
* |
||||||
|
* @param manager the application asset manager |
||||||
|
* @param size the size of the rendered shadowmaps (512,1024,2048, etc...) |
||||||
|
* @param nbSplits the number of shadow maps rendered (the more shadow maps |
||||||
|
* the more quality, the less fps). |
||||||
|
* @param nbSplits the number of shadow maps rendered (the more shadow maps |
||||||
|
* the more quality, the less fps). |
||||||
|
*/ |
||||||
|
public PointLightShadowRenderer(AssetManager manager, int size) { |
||||||
|
this(manager, size, new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md")); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a PSSM Shadow Renderer More info on the technique at <a |
||||||
|
* href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
|
||||||
|
* |
||||||
|
* @param manager the application asset manager |
||||||
|
* @param size the size of the rendered shadowmaps (512,1024,2048, etc...) |
||||||
|
* @param nbSplits the number of shadow maps rendered (the more shadow maps |
||||||
|
* the more quality, the less fps). |
||||||
|
* @param postShadowMat the material used for post shadows if you need to |
||||||
|
* override it |
||||||
|
*/ |
||||||
|
protected PointLightShadowRenderer(AssetManager manager, int size, Material postShadowMat) { |
||||||
|
|
||||||
|
this.postshadowMat = postShadowMat; |
||||||
|
assetManager = manager; |
||||||
|
shadowMapSize = size; |
||||||
|
|
||||||
|
shadowFB = new FrameBuffer[CAM_NUMBER]; |
||||||
|
shadowMaps = new Texture2D[CAM_NUMBER]; |
||||||
|
dispPic = new Picture[CAM_NUMBER]; |
||||||
|
lightViewProjectionsMatrices = new Matrix4f[CAM_NUMBER]; |
||||||
|
|
||||||
|
//DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
|
||||||
|
dummyTex = new Texture2D(size, size, Format.RGBA8); |
||||||
|
|
||||||
|
preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md"); |
||||||
|
postshadowMat.setFloat("ShadowMapSize", size); |
||||||
|
|
||||||
|
shadowCams = new Camera[CAM_NUMBER]; |
||||||
|
|
||||||
|
for (int i = 0; i < CAM_NUMBER; i++) { |
||||||
|
lightViewProjectionsMatrices[i] = new Matrix4f(); |
||||||
|
shadowFB[i] = new FrameBuffer(size, size, 1); |
||||||
|
shadowMaps[i] = new Texture2D(size, size, Format.Depth); |
||||||
|
|
||||||
|
shadowFB[i].setDepthTexture(shadowMaps[i]); |
||||||
|
|
||||||
|
//DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
|
||||||
|
shadowFB[i].setColorTexture(dummyTex); |
||||||
|
|
||||||
|
postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]); |
||||||
|
|
||||||
|
//quads for debuging purpose
|
||||||
|
dispPic[i] = new Picture("Picture" + i); |
||||||
|
dispPic[i].setTexture(manager, shadowMaps[i], false); |
||||||
|
|
||||||
|
shadowCams[i] = new Camera(size, size); |
||||||
|
} |
||||||
|
|
||||||
|
setCompareMode(CompareMode.Hardware); |
||||||
|
setFilterMode(FilterMode.Bilinear); |
||||||
|
setShadowIntensity(0.7f); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the filtering mode for shadow edges see {@link FilterMode} for more |
||||||
|
* info |
||||||
|
* |
||||||
|
* @param filterMode |
||||||
|
*/ |
||||||
|
final public void setFilterMode(FilterMode filterMode) { |
||||||
|
if (filterMode == null) { |
||||||
|
throw new NullPointerException(); |
||||||
|
} |
||||||
|
|
||||||
|
if (this.filterMode == filterMode) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.filterMode = filterMode; |
||||||
|
postshadowMat.setInt("FilterMode", filterMode.ordinal()); |
||||||
|
postshadowMat.setFloat("PCFEdge", edgesThickness); |
||||||
|
if (compareMode == CompareMode.Hardware) { |
||||||
|
for (Texture2D shadowMap : shadowMaps) { |
||||||
|
if (filterMode == FilterMode.Bilinear) { |
||||||
|
shadowMap.setMagFilter(MagFilter.Bilinear); |
||||||
|
shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps); |
||||||
|
} else { |
||||||
|
shadowMap.setMagFilter(MagFilter.Nearest); |
||||||
|
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
applyFilterMode = true; |
||||||
|
} |
||||||
|
|
||||||
|
public FilterMode getFilterMode() { |
||||||
|
return filterMode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* sets the shadow compare mode see {@link CompareMode} for more info |
||||||
|
* |
||||||
|
* @param compareMode |
||||||
|
*/ |
||||||
|
final public void setCompareMode(CompareMode compareMode) { |
||||||
|
if (compareMode == null) { |
||||||
|
throw new NullPointerException(); |
||||||
|
} |
||||||
|
|
||||||
|
if (this.compareMode == compareMode) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.compareMode = compareMode; |
||||||
|
for (Texture2D shadowMap : shadowMaps) { |
||||||
|
if (compareMode == CompareMode.Hardware) { |
||||||
|
shadowMap.setShadowCompareMode(ShadowCompareMode.LessOrEqual); |
||||||
|
if (filterMode == FilterMode.Bilinear) { |
||||||
|
shadowMap.setMagFilter(MagFilter.Bilinear); |
||||||
|
shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps); |
||||||
|
} else { |
||||||
|
shadowMap.setMagFilter(MagFilter.Nearest); |
||||||
|
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps); |
||||||
|
} |
||||||
|
} else { |
||||||
|
shadowMap.setShadowCompareMode(ShadowCompareMode.Off); |
||||||
|
shadowMap.setMagFilter(MagFilter.Nearest); |
||||||
|
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps); |
||||||
|
} |
||||||
|
} |
||||||
|
postshadowMat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware); |
||||||
|
applyHWShadows = true; |
||||||
|
} |
||||||
|
|
||||||
|
//debug function that create a displayable frustrum
|
||||||
|
private Geometry createFrustum(Vector3f[] pts, int i) { |
||||||
|
WireFrustum frustum = new WireFrustum(pts); |
||||||
|
Geometry frustumMdl = new Geometry("f", frustum); |
||||||
|
frustumMdl.setCullHint(Spatial.CullHint.Never); |
||||||
|
frustumMdl.setShadowMode(ShadowMode.Off); |
||||||
|
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
mat.getAdditionalRenderState().setWireframe(true); |
||||||
|
frustumMdl.setMaterial(mat); |
||||||
|
switch (i) { |
||||||
|
case 0: |
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Pink); |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Red); |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Green); |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Blue); |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Yellow); |
||||||
|
break; |
||||||
|
case 5: |
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Gray); |
||||||
|
break; |
||||||
|
default: |
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.White); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
frustumMdl.updateGeometricState(); |
||||||
|
return frustumMdl; |
||||||
|
} |
||||||
|
|
||||||
|
public void initialize(RenderManager rm, ViewPort vp) { |
||||||
|
renderManager = rm; |
||||||
|
viewPort = vp; |
||||||
|
//checking for caps to chosse the appropriate post material technique
|
||||||
|
if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) { |
||||||
|
postTechniqueName = "PostShadow15"; |
||||||
|
} else { |
||||||
|
postTechniqueName = "PostShadow"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isInitialized() { |
||||||
|
return viewPort != null; |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("fallthrough") |
||||||
|
public void postQueue(RenderQueue rq) { |
||||||
|
if (light == null) { |
||||||
|
throw new IllegalStateException("The light can't be null for a " + this.getClass().getName()); |
||||||
|
} |
||||||
|
GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast); |
||||||
|
if (occluders.size() == 0) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive); |
||||||
|
if (receivers.size() == 0) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Camera viewCam = viewPort.getCamera(); |
||||||
|
|
||||||
|
float zFar = zFarOverride; |
||||||
|
if (zFar == 0) { |
||||||
|
zFar = viewCam.getFrustumFar(); |
||||||
|
} |
||||||
|
|
||||||
|
//We prevent computing the frustum points and splits with zeroed or negative near clip value
|
||||||
|
//float frustumNear = Math.max(viewCam.getFrustumNear(), 0.001f);
|
||||||
|
// ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, 1.0f, points);
|
||||||
|
|
||||||
|
updateShadowCams(); |
||||||
|
|
||||||
|
Renderer r = renderManager.getRenderer(); |
||||||
|
renderManager.setForcedMaterial(preshadowMat); |
||||||
|
renderManager.setForcedTechnique("PreShadow"); |
||||||
|
|
||||||
|
for (int i = 0; i < CAM_NUMBER; i++) { |
||||||
|
|
||||||
|
//Updating shadow cam with curent split frustra
|
||||||
|
|
||||||
|
ShadowUtil.getOccludersInCamFrustum(occluders, shadowCams[i], splitOccluders); |
||||||
|
|
||||||
|
//saving light view projection matrix for this split
|
||||||
|
lightViewProjectionsMatrices[i].set(shadowCams[i].getViewProjectionMatrix()); |
||||||
|
renderManager.setCamera(shadowCams[i], false); |
||||||
|
|
||||||
|
if (debug && frustums[i].getParent() == null) { |
||||||
|
((Node) viewPort.getScenes().get(0)).attachChild(frustums[i]); |
||||||
|
} |
||||||
|
|
||||||
|
r.setFrameBuffer(shadowFB[i]); |
||||||
|
r.clearBuffers(false, true, false); |
||||||
|
|
||||||
|
// System.out.println("Face " + i + " nb occl " + splitOccluders.size());
|
||||||
|
// render shadow casters to shadow map
|
||||||
|
viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCams[i], true); |
||||||
|
} |
||||||
|
if (flushQueues) { |
||||||
|
occluders.clear(); |
||||||
|
} |
||||||
|
//restore setting for future rendering
|
||||||
|
r.setFrameBuffer(viewPort.getOutputFrameBuffer()); |
||||||
|
renderManager.setForcedMaterial(null); |
||||||
|
renderManager.setForcedTechnique(null); |
||||||
|
renderManager.setCamera(viewCam, false); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
//debug only : displays depth shadow maps
|
||||||
|
protected void displayShadowMap(Renderer r) { |
||||||
|
Camera cam = viewPort.getCamera(); |
||||||
|
renderManager.setCamera(cam, true); |
||||||
|
int h = cam.getHeight(); |
||||||
|
for (int i = 0; i < dispPic.length; i++) { |
||||||
|
dispPic[i].setPosition((128 * i) + (150 + 64 * (i + 1)), h / 20f); |
||||||
|
dispPic[i].setWidth(128); |
||||||
|
dispPic[i].setHeight(128); |
||||||
|
dispPic[i].updateGeometricState(); |
||||||
|
renderManager.renderGeometry(dispPic[i]); |
||||||
|
} |
||||||
|
renderManager.setCamera(cam, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* For dubuging purpose Allow to "snapshot" the current frustrum to the |
||||||
|
* scene |
||||||
|
*/ |
||||||
|
public void displayDebug() { |
||||||
|
debug = true; |
||||||
|
} |
||||||
|
|
||||||
|
public void postFrame(FrameBuffer out) { |
||||||
|
|
||||||
|
if (debug) { |
||||||
|
displayShadowMap(renderManager.getRenderer()); |
||||||
|
} |
||||||
|
if (!noOccluders) { |
||||||
|
//setting params to recieving geometry list
|
||||||
|
setMatParams(); |
||||||
|
|
||||||
|
Camera cam = viewPort.getCamera(); |
||||||
|
//some materials in the scene does not have a post shadow technique so we're using the fall back material
|
||||||
|
if (needsfallBackMaterial) { |
||||||
|
renderManager.setForcedMaterial(postshadowMat); |
||||||
|
} |
||||||
|
|
||||||
|
//forcing the post shadow technique and render state
|
||||||
|
renderManager.setForcedTechnique(postTechniqueName); |
||||||
|
|
||||||
|
//rendering the post shadow pass
|
||||||
|
viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, cam, flushQueues); |
||||||
|
|
||||||
|
//resetting renderManager settings
|
||||||
|
renderManager.setForcedTechnique(null); |
||||||
|
renderManager.setForcedMaterial(null); |
||||||
|
renderManager.setCamera(cam, false); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private void updateShadowCams() { |
||||||
|
|
||||||
|
|
||||||
|
//bottom
|
||||||
|
shadowCams[0].setAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y.mult(-1f)); |
||||||
|
|
||||||
|
//top
|
||||||
|
shadowCams[1].setAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z, Vector3f.UNIT_Y); |
||||||
|
|
||||||
|
//forward
|
||||||
|
shadowCams[2].setAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Y, Vector3f.UNIT_Z.mult(-1f)); |
||||||
|
|
||||||
|
//backward
|
||||||
|
shadowCams[3].setAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z); |
||||||
|
|
||||||
|
//left
|
||||||
|
shadowCams[4].setAxes(Vector3f.UNIT_Z, Vector3f.UNIT_Y, Vector3f.UNIT_X.mult(-1f)); |
||||||
|
|
||||||
|
//right
|
||||||
|
shadowCams[5].setAxes(Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y, Vector3f.UNIT_X); |
||||||
|
|
||||||
|
for (int i = 0; i < CAM_NUMBER; i++) { |
||||||
|
shadowCams[i].setFrustumPerspective(90f, 1f, 0.1f, light.getRadius()); |
||||||
|
shadowCams[i].setLocation(light.getPosition()); |
||||||
|
shadowCams[i].update(); |
||||||
|
shadowCams[i].updateViewProjection(); |
||||||
|
} |
||||||
|
|
||||||
|
if (debug && frustums == null) { |
||||||
|
frustums = new Geometry[CAM_NUMBER]; |
||||||
|
Vector3f[] points = new Vector3f[8]; |
||||||
|
for (int i = 0; i < 8; i++) { |
||||||
|
points[i] = new Vector3f(); |
||||||
|
} |
||||||
|
for (int i = 0; i < CAM_NUMBER; i++) { |
||||||
|
ShadowUtil.updateFrustumPoints2(shadowCams[i], points); |
||||||
|
frustums[i] = createFrustum(points, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
private Geometry[] frustums = null; |
||||||
|
|
||||||
|
private void setMatParams() { |
||||||
|
|
||||||
|
GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive); |
||||||
|
|
||||||
|
//iteration throught all the geometries of the list to gather the materials
|
||||||
|
|
||||||
|
matCache.clear(); |
||||||
|
for (int i = 0; i < l.size(); i++) { |
||||||
|
Material mat = l.get(i).getMaterial(); |
||||||
|
//checking if the material has the post technique and adding it to the material cache
|
||||||
|
if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) { |
||||||
|
if (!matCache.contains(mat)) { |
||||||
|
matCache.add(mat); |
||||||
|
} |
||||||
|
} else { |
||||||
|
needsfallBackMaterial = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//iterating through the mat cache and setting the parameters
|
||||||
|
for (Material mat : matCache) { |
||||||
|
|
||||||
|
if (mat.getParam("ShadowMapSize") == null) { |
||||||
|
mat.setFloat("ShadowMapSize", shadowMapSize); |
||||||
|
} |
||||||
|
for (int j = 0; j < CAM_NUMBER; j++) { |
||||||
|
mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); |
||||||
|
} |
||||||
|
mat.setVector3("LightPos", light.getPosition()); |
||||||
|
if (mat.getParam("ShadowMap0") == null) { |
||||||
|
for (int j = 0; j < CAM_NUMBER; j++) { |
||||||
|
mat.setTexture("ShadowMap" + j, shadowMaps[j]); |
||||||
|
} |
||||||
|
} |
||||||
|
if (applyHWShadows || mat.getParam("HardwareShadows") == null) { |
||||||
|
mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware); |
||||||
|
} |
||||||
|
if (applyFilterMode || mat.getParam("FilterMode") == null) { |
||||||
|
mat.setInt("FilterMode", filterMode.ordinal()); |
||||||
|
} |
||||||
|
if (mat.getParam("PCFEdge") == null || applyPCFEdge) { |
||||||
|
mat.setFloat("PCFEdge", edgesThickness); |
||||||
|
} |
||||||
|
|
||||||
|
if (mat.getParam("ShadowIntensity") == null || applyShadowIntensity) { |
||||||
|
mat.setFloat("ShadowIntensity", shadowIntensity); |
||||||
|
} |
||||||
|
|
||||||
|
if (fadeInfo != null && mat.getParam("FadeInfo") == null || applyFadeInfo) { |
||||||
|
mat.setVector2("FadeInfo", fadeInfo); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
applyHWShadows = false; |
||||||
|
applyFilterMode = false; |
||||||
|
applyPCFEdge = false; |
||||||
|
applyShadowIntensity = false; |
||||||
|
applyFadeInfo = false; |
||||||
|
|
||||||
|
//At least one material of the receiving geoms does not support the post shadow techniques
|
||||||
|
//so we fall back to the forced material solution (transparent shadows won't be supported for these objects)
|
||||||
|
if (needsfallBackMaterial) { |
||||||
|
setPostShadowParams(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
protected void setPostShadowParams() { |
||||||
|
for (int j = 0; j < CAM_NUMBER; j++) { |
||||||
|
postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); |
||||||
|
} |
||||||
|
postshadowMat.setVector3("LightPos", light.getPosition()); |
||||||
|
} |
||||||
|
|
||||||
|
public void preFrame(float tpf) { |
||||||
|
} |
||||||
|
|
||||||
|
public void cleanup() { |
||||||
|
} |
||||||
|
|
||||||
|
public void reshape(ViewPort vp, int w, int h) { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the shdaow intensity |
||||||
|
* |
||||||
|
* @see #setShadowIntensity(float shadowIntensity) |
||||||
|
* @return shadowIntensity |
||||||
|
*/ |
||||||
|
public float getShadowIntensity() { |
||||||
|
return shadowIntensity; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the shadowIntensity, the value should be between 0 and 1, a 0 value |
||||||
|
* gives a bright and invisilble shadow, a 1 value gives a pitch black |
||||||
|
* shadow, default is 0.7 |
||||||
|
* |
||||||
|
* @param shadowIntensity the darkness of the shadow |
||||||
|
*/ |
||||||
|
final public void setShadowIntensity(float shadowIntensity) { |
||||||
|
this.shadowIntensity = shadowIntensity; |
||||||
|
postshadowMat.setFloat("ShadowIntensity", shadowIntensity); |
||||||
|
applyShadowIntensity = true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* How far the shadows are rendered in the view |
||||||
|
* |
||||||
|
* @see #setShadowZExtend(float zFar) |
||||||
|
* @return shadowZExtend |
||||||
|
*/ |
||||||
|
public float getShadowZExtend() { |
||||||
|
return zFarOverride; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the distance from the eye where the shadows will be rendered default |
||||||
|
* value is dynamicaly computed to the shadow casters/receivers union bound |
||||||
|
* zFar, capped to view frustum far value. |
||||||
|
* |
||||||
|
* @param zFar the zFar values that override the computed one |
||||||
|
*/ |
||||||
|
public void setShadowZExtend(float zFar) { |
||||||
|
if (fadeInfo != null) { |
||||||
|
fadeInfo.set(zFar - fadeLength, 1f / fadeLength); |
||||||
|
} |
||||||
|
this.zFarOverride = zFar; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the edges thickness |
||||||
|
* |
||||||
|
* @see #setEdgesThickness(int edgesThickness) |
||||||
|
* @return edgesThickness |
||||||
|
*/ |
||||||
|
public int getEdgesThickness() { |
||||||
|
return (int) (edgesThickness * 10); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the shadow edges thickness. default is 1, setting it to lower values |
||||||
|
* can help to reduce the jagged effect of the shadow edges |
||||||
|
* |
||||||
|
* @param edgesThickness |
||||||
|
*/ |
||||||
|
public void setEdgesThickness(int edgesThickness) { |
||||||
|
this.edgesThickness = Math.max(1, Math.min(edgesThickness, 10)); |
||||||
|
this.edgesThickness *= 0.1f; |
||||||
|
postshadowMat.setFloat("PCFEdge", edgesThickness); |
||||||
|
applyPCFEdge = true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns true if the PssmRenderer flushed the shadow queues |
||||||
|
* |
||||||
|
* @return flushQueues |
||||||
|
*/ |
||||||
|
public boolean isFlushQueues() { |
||||||
|
return flushQueues; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set this to false if you want to use several PssmRederers to have |
||||||
|
* multiple shadows cast by multiple light sources. Make sure the last |
||||||
|
* PssmRenderer in the stack DO flush the queues, but not the others |
||||||
|
* |
||||||
|
* @param flushQueues |
||||||
|
*/ |
||||||
|
public void setFlushQueues(boolean flushQueues) { |
||||||
|
this.flushQueues = flushQueues; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Define the length over which the shadow will fade out when using a |
||||||
|
* shadowZextend This is useful to make dynamic shadows fade into baked |
||||||
|
* shadows in the distance. |
||||||
|
* |
||||||
|
* @param length the fade length in world units |
||||||
|
*/ |
||||||
|
public void setShadowZFadeLength(float length) { |
||||||
|
if (length == 0) { |
||||||
|
fadeInfo = null; |
||||||
|
fadeLength = 0; |
||||||
|
postshadowMat.clearParam("FadeInfo"); |
||||||
|
} else { |
||||||
|
if (zFarOverride == 0) { |
||||||
|
fadeInfo = new Vector2f(0, 0); |
||||||
|
} else { |
||||||
|
fadeInfo = new Vector2f(zFarOverride - length, 1.0f / length); |
||||||
|
} |
||||||
|
fadeLength = length; |
||||||
|
postshadowMat.setVector2("FadeInfo", fadeInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get the length over which the shadow will fade out when using a |
||||||
|
* shadowZextend |
||||||
|
* |
||||||
|
* @return the fade length in world units |
||||||
|
*/ |
||||||
|
public float getShadowZFadeLength() { |
||||||
|
if (fadeInfo != null) { |
||||||
|
return zFarOverride - fadeInfo.x; |
||||||
|
} |
||||||
|
return 0f; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* gets the point light used to cast shadows with this processor |
||||||
|
* |
||||||
|
* @return the point light |
||||||
|
*/ |
||||||
|
public PointLight getLight() { |
||||||
|
return light; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* sets the light to use for casting shadows with this processor |
||||||
|
* |
||||||
|
* @param light the point light |
||||||
|
*/ |
||||||
|
public void setLight(PointLight light) { |
||||||
|
this.light = light; |
||||||
|
updateShadowCams(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,237 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2012 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package jme3test.light; |
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.font.BitmapText; |
||||||
|
import com.jme3.input.KeyInput; |
||||||
|
import com.jme3.input.controls.ActionListener; |
||||||
|
import com.jme3.input.controls.KeyTrigger; |
||||||
|
import com.jme3.light.PointLight; |
||||||
|
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.Node; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.scene.shape.Sphere; |
||||||
|
import com.jme3.shadow.PointLightShadowFilter; |
||||||
|
import com.jme3.shadow.PointLightShadowRenderer; |
||||||
|
|
||||||
|
public class TestPointLightShadows extends SimpleApplication implements ActionListener { |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
TestPointLightShadows app = new TestPointLightShadows(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
Node lightNode; |
||||||
|
private boolean hardwareShadows = true; |
||||||
|
PointLightShadowRenderer plsr ; |
||||||
|
PointLightShadowFilter plsf; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
flyCam.setMoveSpeed(10); |
||||||
|
cam.setLocation(new Vector3f(0.040581334f, 1.7745866f, 6.155161f)); |
||||||
|
cam.setRotation(new Quaternion(4.3868728E-5f, 0.9999293f, -0.011230096f, 0.0039059948f)); |
||||||
|
|
||||||
|
|
||||||
|
Node scene = (Node) assetManager.loadModel("Models/Test/CornellBox.j3o"); |
||||||
|
scene.setShadowMode(RenderQueue.ShadowMode.CastAndReceive); |
||||||
|
rootNode.attachChild(scene); |
||||||
|
rootNode.getChild("Cube").setShadowMode(RenderQueue.ShadowMode.Receive); |
||||||
|
lightNode = (Node) rootNode.getChild("Lamp"); |
||||||
|
Geometry lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f)); |
||||||
|
//Geometry lightMdl = new Geometry("Light", new Box(.1f,.1f,.1f));
|
||||||
|
lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m")); |
||||||
|
lightMdl.setShadowMode(RenderQueue.ShadowMode.Off); |
||||||
|
lightNode.attachChild(lightMdl); |
||||||
|
//lightMdl.setLocalTranslation(lightNode.getLocalTranslation());
|
||||||
|
|
||||||
|
|
||||||
|
Geometry box = new Geometry("box", new Box(0.2f, 0.2f, 0.2f)); |
||||||
|
//Geometry lightMdl = new Geometry("Light", new Box(.1f,.1f,.1f));
|
||||||
|
box.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m")); |
||||||
|
box.setShadowMode(RenderQueue.ShadowMode.CastAndReceive); |
||||||
|
rootNode.attachChild(box); |
||||||
|
box.setLocalTranslation(-1f, 0.5f, -2); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
plsr = new PointLightShadowRenderer(assetManager, 512); |
||||||
|
plsr.setLight((PointLight) scene.getLocalLightList().get(0)); |
||||||
|
plsr.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest); |
||||||
|
// plsr.displayDebug();
|
||||||
|
viewPort.addProcessor(plsr); |
||||||
|
|
||||||
|
|
||||||
|
plsf = new PointLightShadowFilter(assetManager, 512); |
||||||
|
plsf.setLight((PointLight) scene.getLocalLightList().get(0)); |
||||||
|
plsf.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest); |
||||||
|
plsf.setEnabled(false); |
||||||
|
|
||||||
|
FilterPostProcessor fpp = new FilterPostProcessor(assetManager); |
||||||
|
fpp.addFilter(plsf); |
||||||
|
viewPort.addProcessor(fpp); |
||||||
|
|
||||||
|
initUIAndInputs(); |
||||||
|
} |
||||||
|
|
||||||
|
BitmapText shadowTypeText; |
||||||
|
BitmapText shadowCompareText; |
||||||
|
BitmapText shadowFilterText; |
||||||
|
BitmapText shadowIntensityText; |
||||||
|
final static String TYPE_TEXT = "(Space) Shadow type : "; |
||||||
|
final static String COMPARE_TEXT = "(enter) Shadow compare "; |
||||||
|
final static String FILTERING_TEXT = "(f) Edge filtering : "; |
||||||
|
final static String INTENSITY_TEXT = "(t:up, g:down) Shadow intensity : "; |
||||||
|
|
||||||
|
private void initUIAndInputs() { |
||||||
|
guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); |
||||||
|
shadowTypeText = createText(); |
||||||
|
shadowCompareText = createText(); |
||||||
|
shadowFilterText = createText(); |
||||||
|
shadowIntensityText = createText(); |
||||||
|
|
||||||
|
shadowTypeText.setText(TYPE_TEXT+"Processor"); |
||||||
|
shadowCompareText.setText(COMPARE_TEXT+(hardwareShadows?"Hardware":"Software")); |
||||||
|
shadowFilterText.setText(FILTERING_TEXT+plsr.getFilterMode().toString()); |
||||||
|
shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity()); |
||||||
|
|
||||||
|
shadowTypeText.setLocalTranslation(10, cam.getHeight()-20, 0); |
||||||
|
shadowCompareText.setLocalTranslation(10, cam.getHeight()-40, 0); |
||||||
|
shadowFilterText.setLocalTranslation(10, cam.getHeight()-60, 0); |
||||||
|
shadowIntensityText.setLocalTranslation(10, cam.getHeight()-80, 0); |
||||||
|
|
||||||
|
guiNode.attachChild(shadowTypeText); |
||||||
|
guiNode.attachChild(shadowCompareText); |
||||||
|
guiNode.attachChild(shadowFilterText); |
||||||
|
guiNode.attachChild(shadowIntensityText); |
||||||
|
|
||||||
|
inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE)); |
||||||
|
inputManager.addMapping("changeFiltering", new KeyTrigger(KeyInput.KEY_F)); |
||||||
|
inputManager.addMapping("ShadowUp", new KeyTrigger(KeyInput.KEY_T)); |
||||||
|
inputManager.addMapping("ShadowDown", new KeyTrigger(KeyInput.KEY_G)); |
||||||
|
inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y)); |
||||||
|
inputManager.addMapping("ThicknessDown", new KeyTrigger(KeyInput.KEY_H)); |
||||||
|
inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN)); |
||||||
|
|
||||||
|
|
||||||
|
inputManager.addListener(this, "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown", "changeFiltering"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
int filteringIndex = 0; |
||||||
|
int renderModeIndex = 0; |
||||||
|
|
||||||
|
public void onAction(String name, boolean keyPressed, float tpf) { |
||||||
|
if (name.equals("toggle") && keyPressed) { |
||||||
|
renderModeIndex += 1; |
||||||
|
renderModeIndex %= 3; |
||||||
|
|
||||||
|
switch (renderModeIndex) { |
||||||
|
case 0: |
||||||
|
viewPort.addProcessor(plsr); |
||||||
|
shadowTypeText.setText(TYPE_TEXT+"Processor"); |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
viewPort.removeProcessor(plsr); |
||||||
|
plsf.setEnabled(true); |
||||||
|
shadowTypeText.setText(TYPE_TEXT+"Filter"); |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
plsf.setEnabled(false); |
||||||
|
shadowTypeText.setText(TYPE_TEXT+"None"); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} else if (name.equals("toggleHW") && keyPressed) { |
||||||
|
hardwareShadows = !hardwareShadows; |
||||||
|
plsr.setCompareMode(hardwareShadows ? PointLightShadowRenderer.CompareMode.Hardware : PointLightShadowRenderer.CompareMode.Software); |
||||||
|
plsf.setCompareMode(hardwareShadows ? PointLightShadowRenderer.CompareMode.Hardware : PointLightShadowRenderer.CompareMode.Software); |
||||||
|
|
||||||
|
shadowCompareText.setText(COMPARE_TEXT+(hardwareShadows?"Hardware":"Software")); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
if (name.equals("changeFiltering") && keyPressed) { |
||||||
|
filteringIndex = plsr.getFilterMode().ordinal(); |
||||||
|
filteringIndex = (filteringIndex + 1) % PointLightShadowRenderer.FilterMode.values().length; |
||||||
|
PointLightShadowRenderer.FilterMode m = PointLightShadowRenderer.FilterMode.values()[filteringIndex]; |
||||||
|
plsr.setFilterMode(m); |
||||||
|
plsf.setFilterMode(m); |
||||||
|
shadowFilterText.setText(FILTERING_TEXT+m.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
if (name.equals("ShadowUp") && keyPressed) { |
||||||
|
plsr.setShadowIntensity(plsr.getShadowIntensity() + 0.1f); |
||||||
|
plsf.setShadowIntensity(plsr.getShadowIntensity() + 0.1f); |
||||||
|
|
||||||
|
shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity()); |
||||||
|
} |
||||||
|
if (name.equals("ShadowDown") && keyPressed) { |
||||||
|
plsr.setShadowIntensity(plsr.getShadowIntensity() - 0.1f); |
||||||
|
plsf.setShadowIntensity(plsr.getShadowIntensity() - 0.1f); |
||||||
|
shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity()); |
||||||
|
} |
||||||
|
if (name.equals("ThicknessUp") && keyPressed) { |
||||||
|
plsr.setEdgesThickness(plsr.getEdgesThickness() + 1); |
||||||
|
plsf.setEdgesThickness(plsr.getEdgesThickness() + 1); |
||||||
|
System.out.println("Shadow thickness : " + plsr.getEdgesThickness()); |
||||||
|
} |
||||||
|
if (name.equals("ThicknessDown") && keyPressed) { |
||||||
|
plsr.setEdgesThickness(plsr.getEdgesThickness() - 1); |
||||||
|
plsf.setEdgesThickness(plsr.getEdgesThickness() - 1); |
||||||
|
System.out.println("Shadow thickness : " + plsr.getEdgesThickness()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
float time; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleUpdate(float tpf) { |
||||||
|
time += tpf; |
||||||
|
lightNode.setLocalTranslation(FastMath.cos(time)*0.4f,lightNode.getLocalTranslation().y , FastMath.sin(time)*0.4f); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private BitmapText createText() { |
||||||
|
BitmapText t = new BitmapText(guiFont, false); |
||||||
|
t.setSize(guiFont.getCharSet().getRenderedSize()*0.75f); |
||||||
|
return t; |
||||||
|
} |
||||||
|
} |
Binary file not shown.
Loading…
Reference in new issue