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