Procházet zdrojové kódy

Shadow system refactoring.
- Basic and PSSM shadow renderer are now deprecated
- There is now one processor and its filter conterpart for each light type
- created an abstract shadow processor that hold the common shadowing code. It's totally independent of the shadow technique used.
- extracted the CompareMode and FilterMode enum to their own files.
- renamed FilterMode enum to EdgeFilteringMode
- refactored the shader code, to avoid duplicate code. all shadow related code is now gathered into Shadows.glsllib and Shadows15.glsllib.
- added spot light Shadows
- removed the ShadowCamera class as it was not used.
- removed "pssm" in the naming of classes, shader and shader libs since it's not relevant anymore

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9971 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

rem..om před 13 roky
rodič
revize
0dadaa80f5
37 změnil soubory, kde provedl 3186 přidání a 1552 odebrání
  1. 4 4
      engine/src/core-data/Common/MatDefs/Light/Lighting.j3md
  2. 12 0
      engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.frag
  3. 28 0
      engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.j3md
  4. 31 0
      engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.vert
  5. 72 12
      engine/src/core-data/Common/MatDefs/Shadow/PostShadow.frag
  6. 60 3
      engine/src/core-data/Common/MatDefs/Shadow/PostShadow.j3md
  7. 56 15
      engine/src/core-data/Common/MatDefs/Shadow/PostShadow.vert
  8. 72 0
      engine/src/core-data/Common/MatDefs/Shadow/PostShadow15.frag
  9. 15 40
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag
  10. 4 4
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md
  11. 17 41
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag
  12. 0 96
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag
  13. 0 85
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md
  14. 0 62
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert
  15. 0 105
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag
  16. 0 0
      engine/src/core-data/Common/ShaderLib/BasicShadow.glsllib
  17. 82 14
      engine/src/core-data/Common/ShaderLib/Shadows.glsllib
  18. 81 15
      engine/src/core-data/Common/ShaderLib/Shadows15.glsllib
  19. 233 0
      engine/src/core/com/jme3/shadow/AbstractShadowFilter.java
  20. 563 0
      engine/src/core/com/jme3/shadow/AbstractShadowRenderer.java
  21. 3 1
      engine/src/core/com/jme3/shadow/BasicShadowRenderer.java
  22. 48 0
      engine/src/core/com/jme3/shadow/CompareMode.java
  23. 174 0
      engine/src/core/com/jme3/shadow/DirectionalLightShadowFilter.java
  24. 257 0
      engine/src/core/com/jme3/shadow/DirectionalLightShadowRenderer.java
  25. 39 34
      engine/src/core/com/jme3/shadow/EdgeFilteringMode.java
  26. 23 155
      engine/src/core/com/jme3/shadow/PointLightShadowFilter.java
  27. 36 632
      engine/src/core/com/jme3/shadow/PointLightShadowRenderer.java
  28. 2 0
      engine/src/core/com/jme3/shadow/PssmShadowFilter.java
  29. 114 79
      engine/src/core/com/jme3/shadow/PssmShadowRenderer.java
  30. 4 6
      engine/src/core/com/jme3/shadow/ShadowUtil.java
  31. 143 0
      engine/src/core/com/jme3/shadow/SpotLightShadowFilter.java
  32. 211 0
      engine/src/core/com/jme3/shadow/SpotLightShadowRenderer.java
  33. 154 0
      engine/src/test/jme3test/light/ShadowTestUIManager.java
  34. 311 0
      engine/src/test/jme3test/light/TestDirectionalLightShadow.java
  35. 29 137
      engine/src/test/jme3test/light/TestPointLightShadows.java
  36. 110 12
      engine/src/test/jme3test/light/TestPssmShadow.java
  37. 198 0
      engine/src/test/jme3test/light/TestSpotLightShadows.java

+ 4 - 4
engine/src/core-data/Common/MatDefs/Light/Lighting.j3md

@@ -200,8 +200,8 @@ MaterialDef Phong Lighting {
 
 
     Technique PostShadow15{
-        VertexShader GLSL150:   Common/MatDefs/Shadow/PostShadowPSSM.vert
-        FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM15.frag
+        VertexShader GLSL150:   Common/MatDefs/Shadow/PostShadow.vert
+        FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -228,8 +228,8 @@ MaterialDef Phong Lighting {
     }
 
     Technique PostShadow{
-        VertexShader GLSL100:   Common/MatDefs/Shadow/PostShadowPSSM.vert
-        FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.frag
+        VertexShader GLSL100:   Common/MatDefs/Shadow/PostShadow.vert
+        FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag
 
         WorldParameters {
             WorldViewProjectionMatrix

+ 12 - 0
engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.frag

@@ -0,0 +1,12 @@
+#import "Common/ShaderLib/BasicShadow.glsllib"
+
+uniform SHADOWMAP m_ShadowMap;
+varying vec4 projCoord;
+
+void main() {
+   vec4 coord = projCoord;
+   coord.xyz /= coord.w;
+   float shad = Shadow_GetShadow(m_ShadowMap, coord) * 0.7 + 0.3;
+   gl_FragColor = vec4(shad,shad,shad,1.0);
+}
+

+ 28 - 0
engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.j3md

@@ -0,0 +1,28 @@
+MaterialDef Basic Post Shadow {
+
+    MaterialParameters {
+        Texture2D ShadowMap
+        Matrix4 LightViewProjectionMatrix
+    }
+
+    Technique {
+        VertexShader GLSL100:   Common/MatDefs/Shadow/BasicPostShadow.vert
+        FragmentShader GLSL100: Common/MatDefs/Shadow/BasicPostShadow.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            WorldMatrix
+        }
+
+        Defines {
+            NO_SHADOW2DPROJ
+        }
+
+        RenderState {
+            Blend Modulate
+            DepthWrite Off    
+            PolyOffset -0.1 0
+        }
+    }
+
+}

+ 31 - 0
engine/src/core-data/Common/MatDefs/Shadow/BasicPostShadow.vert

@@ -0,0 +1,31 @@
+uniform mat4 m_LightViewProjectionMatrix;
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldMatrix;
+
+varying vec4 projCoord;
+
+attribute vec3 inPosition;
+
+const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
+                          0.0, 0.5, 0.0, 0.0,
+                          0.0, 0.0, 0.5, 0.0,
+                          0.5, 0.5, 0.5, 1.0);
+
+void main(){
+    gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+
+    // get the vertex in world space
+    vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
+
+    // convert vertex to light viewProj space
+    //projCoord = biasMat * (m_LightViewProjectionMatrix * worldPos);
+    vec4 coord = m_LightViewProjectionMatrix * worldPos;
+    projCoord = biasMat * coord;
+    //projCoord.z /= gl_DepthRange.far;
+    //projCoord = (m_LightViewProjectionMatrix * worldPos);
+    //projCoord /= projCoord.w;
+    //projCoord.xy = projCoord.xy * vec2(0.5, -0.5) + vec2(0.5);
+
+    // bias from [-1, 1] to [0, 1] for sampling shadow map
+    //projCoord = (projCoord.xyzw * vec4(0.5)) + vec4(0.5);
+}

+ 72 - 12
engine/src/core-data/Common/MatDefs/Shadow/PostShadow.frag

@@ -1,12 +1,72 @@
-#import "Common/ShaderLib/Shadow.glsllib"
-
-uniform SHADOWMAP m_ShadowMap;
-varying vec4 projCoord;
-
-void main() {
-   vec4 coord = projCoord;
-   coord.xyz /= coord.w;
-   float shad = Shadow_GetShadow(m_ShadowMap, coord) * 0.7 + 0.3;
-   gl_FragColor = vec4(shad,shad,shad,1.0);
-}
-
+#import "Common/ShaderLib/Shadows.glsllib"
+
+#ifdef PSSM
+varying float shadowPosition;
+#endif
+
+varying vec4 projCoord0;
+varying vec4 projCoord1;
+varying vec4 projCoord2;
+varying vec4 projCoord3;
+
+#ifdef POINTLIGHT
+    varying vec4 projCoord4;
+    varying vec4 projCoord5;
+    uniform vec3 m_LightPos;
+    varying vec4 worldPos;
+#endif
+
+#ifdef DISCARD_ALPHA
+    #ifdef COLOR_MAP
+        uniform sampler2D m_ColorMap;
+    #else    
+        uniform sampler2D m_DiffuseMap;
+    #endif
+    uniform float m_AlphaDiscardThreshold;
+    varying vec2 texCoord;
+#endif
+
+#ifdef FADE
+uniform vec2 m_FadeInfo;
+#endif
+
+void main(){   
+ 
+    #ifdef DISCARD_ALPHA
+        #ifdef COLOR_MAP
+            float alpha = texture2D(m_ColorMap,texCoord).a;
+        #else    
+            float alpha = texture2D(m_DiffuseMap,texCoord).a;
+        #endif
+        if(alpha<=m_AlphaDiscardThreshold){
+            discard;
+        }
+
+    #endif
+     
+    float shadow = 1.0;
+ 
+    #ifdef POINTLIGHT         
+            shadow = getPointLightShadows(worldPos, m_LightPos,
+                           m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5,
+                           projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5);
+    #else
+       #ifdef PSSM
+            shadow = getDirectionalLightShadows(m_Splits, shadowPosition,
+                           m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,
+                           projCoord0, projCoord1, projCoord2, projCoord3);
+       #else 
+            //spotlight
+            shadow = getSpotLightShadows(m_ShadowMap0,projCoord0);
+       #endif
+    #endif   
+
+    #ifdef FADE
+      shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    
+    #endif
+    shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
+
+  gl_FragColor = vec4(shadow, shadow, shadow, 1.0);
+
+}
+

+ 60 - 3
engine/src/core-data/Common/MatDefs/Shadow/PostShadow.j3md

@@ -1,8 +1,59 @@
 MaterialDef Post Shadow {
 
     MaterialParameters {
-        Texture2D ShadowMap
-        Matrix4 LightViewProjectionMatrix
+        Int FilterMode
+        Boolean HardwareShadows
+
+        Texture2D ShadowMap0
+        Texture2D ShadowMap1
+        Texture2D ShadowMap2
+        Texture2D ShadowMap3
+        //pointLights
+        Texture2D ShadowMap4
+        Texture2D ShadowMap5
+        
+        Float ShadowIntensity
+        Vector4 Splits
+        Vector2 FadeInfo
+
+        Matrix4 LightViewProjectionMatrix0
+        Matrix4 LightViewProjectionMatrix1
+        Matrix4 LightViewProjectionMatrix2
+        Matrix4 LightViewProjectionMatrix3
+        //pointLight
+        Matrix4 LightViewProjectionMatrix4
+        Matrix4 LightViewProjectionMatrix5
+        Vector3 LightPos
+
+        Float PCFEdge
+
+        Float ShadowMapSize
+    }
+
+    Technique {
+        VertexShader GLSL150:   Common/MatDefs/Shadow/PostShadow.vert
+        FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            WorldMatrix
+        }
+
+        Defines {
+            HARDWARE_SHADOWS : HardwareShadows
+            FILTER_MODE : FilterMode
+            PCFEDGE : PCFEdge
+            SHADOWMAP_SIZE : ShadowMapSize
+            FADE : FadeInfo
+            PSSM : Splits
+            POINTLIGHT : LightViewProjectionMatrix5
+        }
+
+        RenderState {
+            Blend Modulate
+            DepthWrite Off   
+            PolyOffset -0.1 0             
+        }
     }
 
     Technique {
@@ -15,7 +66,13 @@ MaterialDef Post Shadow {
         }
 
         Defines {
-            NO_SHADOW2DPROJ
+            HARDWARE_SHADOWS : HardwareShadows
+            FILTER_MODE : FilterMode
+            PCFEDGE : PCFEdge
+            SHADOWMAP_SIZE : ShadowMapSize
+            FADE : FadeInfo
+            PSSM : Splits
+            POINTLIGHT : LightViewProjectionMatrix5
         }
 
         RenderState {

+ 56 - 15
engine/src/core-data/Common/MatDefs/Shadow/PostShadow.vert

@@ -1,31 +1,72 @@
-uniform mat4 m_LightViewProjectionMatrix;
+uniform mat4 m_LightViewProjectionMatrix0;
+uniform mat4 m_LightViewProjectionMatrix1;
+uniform mat4 m_LightViewProjectionMatrix2;
+uniform mat4 m_LightViewProjectionMatrix3;
+
 uniform mat4 g_WorldViewProjectionMatrix;
 uniform mat4 g_WorldMatrix;
+uniform mat4 g_ViewMatrix;
+uniform vec3 m_LightPos; 
+
+varying vec4 projCoord0;
+varying vec4 projCoord1;
+varying vec4 projCoord2;
+varying vec4 projCoord3;
+
+#ifdef POINTLIGHT
+uniform mat4 m_LightViewProjectionMatrix4;
+uniform mat4 m_LightViewProjectionMatrix5;
+varying vec4 projCoord4;
+varying vec4 projCoord5;
+varying vec4 worldPos;
+#endif
+
+#ifdef PSSM
+varying float shadowPosition;
+#endif
+varying vec3 lightVec;
 
-varying vec4 projCoord;
+varying vec2 texCoord;
 
 attribute vec3 inPosition;
 
+#ifdef DISCARD_ALPHA
+    attribute vec2 inTexCoord;
+#endif
+
 const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
                           0.0, 0.5, 0.0, 0.0,
                           0.0, 0.0, 0.5, 0.0,
                           0.5, 0.5, 0.5, 1.0);
 
+
 void main(){
     gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
 
+    #ifndef POINTLIGHT
+        #ifdef PSSM
+             shadowPosition = gl_Position.z;
+        #endif        
+        vec4 worldPos=vec4(0.0);
+    #endif
     // get the vertex in world space
-    vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
-
-    // convert vertex to light viewProj space
-    //projCoord = biasMat * (m_LightViewProjectionMatrix * worldPos);
-    vec4 coord = m_LightViewProjectionMatrix * worldPos;
-    projCoord = biasMat * coord;
-    //projCoord.z /= gl_DepthRange.far;
-    //projCoord = (m_LightViewProjectionMatrix * worldPos);
-    //projCoord /= projCoord.w;
-    //projCoord.xy = projCoord.xy * vec2(0.5, -0.5) + vec2(0.5);
-
-    // bias from [-1, 1] to [0, 1] for sampling shadow map
-    //projCoord = (projCoord.xyzw * vec4(0.5)) + vec4(0.5);
+    worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
+
+    #ifdef DISCARD_ALPHA
+       texCoord = inTexCoord;
+    #endif
+    // populate the light view matrices array and convert vertex to light viewProj space
+    projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos;
+    projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
+    projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
+    projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
+    #ifdef POINTLIGHT
+        projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos;
+        projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
+    #else
+        
+        vec4 vLightPos = g_ViewMatrix * vec4(m_LightPos,1.0);
+        vec4 vPos = g_ViewMatrix * worldPos;        
+        lightVec = vLightPos.xyz - vPos.xyz;
+    #endif
 }

+ 72 - 0
engine/src/core-data/Common/MatDefs/Shadow/PostShadow15.frag

@@ -0,0 +1,72 @@
+#import "Common/ShaderLib/Shadows15.glsllib"
+
+out vec4 outFragColor;
+
+#ifdef PSSM
+in float shadowPosition;
+#endif
+
+in vec4 projCoord0;
+in vec4 projCoord1;
+in vec4 projCoord2;
+in vec4 projCoord3;
+
+#ifdef POINTLIGHT
+    in vec4 projCoord4;
+    in vec4 projCoord5;
+    in vec4 worldPos;
+    uniform vec3 m_LightPos; 
+#endif
+
+#ifdef DISCARD_ALPHA
+    #ifdef COLOR_MAP
+        uniform sampler2D m_ColorMap;
+    #else    
+        uniform sampler2D m_DiffuseMap;
+    #endif
+    uniform float m_AlphaDiscardThreshold;
+    varying vec2 texCoord;
+#endif
+
+#ifdef FADE
+uniform vec2 m_FadeInfo;
+#endif
+
+void main(){
+    
+    #ifdef DISCARD_ALPHA
+        #ifdef COLOR_MAP
+             float alpha = texture2D(m_ColorMap,texCoord).a;
+        #else    
+             float alpha = texture2D(m_DiffuseMap,texCoord).a;
+        #endif
+      
+        if(alpha < m_AlphaDiscardThreshold){
+            discard;
+        }
+    #endif
+ 
+    float shadow = 1.0;
+    #ifdef POINTLIGHT         
+            shadow = getPointLightShadows(worldPos, m_LightPos,
+                           m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5,
+                           projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5);
+    #else
+       #ifdef PSSM
+            shadow = getDirectionalLightShadows(m_Splits, shadowPosition,
+                           m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,
+                           projCoord0, projCoord1, projCoord2, projCoord3);
+       #else 
+            //spotlight
+            shadow = getSpotLightShadows(m_ShadowMap0,projCoord0);
+       #endif
+    #endif   
+ 
+    #ifdef FADE
+      shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    
+    #endif
+      
+    shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); 
+    outFragColor =  vec4(shadow, shadow, shadow, 1.0);
+}
+

+ 15 - 40
engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag

@@ -1,4 +1,4 @@
-#import "Common/ShaderLib/PssmShadows.glsllib"
+#import "Common/ShaderLib/Shadows.glsllib"
 
 uniform sampler2D m_Texture;
 uniform sampler2D m_DepthTexture;
@@ -59,46 +59,21 @@ void main(){
     #endif
 
     float shadow = 1.0;
-    #ifdef PSSM
-        float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x +  m_ViewProjectionMatrixRow2.y * worldPos.y +  m_ViewProjectionMatrixRow2.z * worldPos.z +  m_ViewProjectionMatrixRow2.w;
-
-        if(shadowPosition < m_Splits.x){
-            shadow = GETSHADOW(m_ShadowMap0, projCoord0);
-        }else if( shadowPosition <  m_Splits.y){
-            shadowBorderScale = 0.5;
-            shadow = GETSHADOW(m_ShadowMap1, projCoord1);
-        }else if( shadowPosition <  m_Splits.z){
-            shadowBorderScale = 0.25;
-            shadow = GETSHADOW(m_ShadowMap2, projCoord2);
-        }else if( shadowPosition <  m_Splits.w){
-            shadowBorderScale = 0.125;
-            shadow = GETSHADOW(m_ShadowMap3, projCoord3);
-        }
-    #endif
-
+    
     #ifdef POINTLIGHT         
-         vec3 vect = worldPos.xyz - m_LightPos;
-         vec3 absv= abs(vect);
-         float maxComp = max(absv.x,max(absv.y,absv.z));
-         if(maxComp == absv.y){
-            if(vect.y < 0.0){
-                shadow = GETSHADOW(m_ShadowMap0, projCoord0);             
-            }else{
-                shadow = GETSHADOW(m_ShadowMap1, projCoord1);
-            }
-         }else if(maxComp == absv.z){
-            if(vect.z < 0.0){
-                shadow = GETSHADOW(m_ShadowMap2, projCoord2);
-            }else{
-                shadow = GETSHADOW(m_ShadowMap3, projCoord3);
-            }
-         }else if(maxComp == absv.x){
-            if(vect.x < 0.0){
-                shadow = GETSHADOW(m_ShadowMap4, projCoord4);
-            }else{
-                shadow = GETSHADOW(m_ShadowMap5, projCoord5);
-            }
-         }                  
+            shadow = getPointLightShadows(worldPos, m_LightPos,
+                           m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5,
+                           projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5);
+    #else
+       #ifdef PSSM
+            float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x +  m_ViewProjectionMatrixRow2.y * worldPos.y +  m_ViewProjectionMatrixRow2.z * worldPos.z +  m_ViewProjectionMatrixRow2.w;
+            shadow = getDirectionalLightShadows(m_Splits, shadowPosition,
+                           m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,
+                           projCoord0, projCoord1, projCoord2, projCoord3);
+       #else 
+            //spotlight
+            shadow = getSpotLightShadows(m_ShadowMap0,projCoord0);
+       #endif
     #endif   
 
     #ifdef FADE

+ 4 - 4
engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md

@@ -11,8 +11,7 @@ MaterialDef Post Shadow {
         //pointLights
         Texture2D ShadowMap4
         Texture2D ShadowMap5
-        Vector3 LightPos
-        
+
         Float ShadowIntensity
         Vector4 Splits
         Vector2 FadeInfo
@@ -23,7 +22,8 @@ MaterialDef Post Shadow {
         Matrix4 LightViewProjectionMatrix3  
         //pointLight
         Matrix4 LightViewProjectionMatrix4
-        Matrix4 LightViewProjectionMatrix5   
+        Matrix4 LightViewProjectionMatrix5  
+        Vector3 LightPos 
 
         Float PCFEdge
 
@@ -44,7 +44,7 @@ MaterialDef Post Shadow {
         FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.frag
 
         WorldParameters {
-            WorldViewProjectionMatrix            
+            WorldViewProjectionMatrix          
         }
 
         Defines {

+ 17 - 41
engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag

@@ -1,5 +1,5 @@
 #import "Common/ShaderLib/MultiSample.glsllib"
-#import "Common/ShaderLib/PssmShadows15.glsllib"
+#import "Common/ShaderLib/Shadows15.glsllib"
 
 
 uniform COLORTEXTURE m_Texture;
@@ -59,53 +59,29 @@ vec4 main_multiSample(in int numSample){
     #endif
 
     float shadow = 1.0;
-    #ifdef PSSM
-        float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x +  m_ViewProjectionMatrixRow2.y * worldPos.y +  m_ViewProjectionMatrixRow2.z * worldPos.z +  m_ViewProjectionMatrixRow2.w;
-      
-        if(shadowPosition < m_Splits.x){
-            shadow = GETSHADOW(m_ShadowMap0, projCoord0);
-        }else if( shadowPosition <  m_Splits.y){
-            shadowBorderScale = 0.5;
-            shadow = GETSHADOW(m_ShadowMap1, projCoord1);
-        }else if( shadowPosition <  m_Splits.z){
-            shadowBorderScale = 0.25;
-            shadow = GETSHADOW(m_ShadowMap2, projCoord2);
-        }else if( shadowPosition <  m_Splits.w){
-            shadowBorderScale = 0.125;
-            shadow = GETSHADOW(m_ShadowMap3, projCoord3);
-        }
-    #endif
-
+  
     #ifdef POINTLIGHT         
-         vec3 vect = worldPos.xyz - m_LightPos;
-         vec3 absv= abs(vect);
-         float maxComp = max(absv.x,max(absv.y,absv.z));
-         if(maxComp == absv.y){
-            if(vect.y < 0.0){
-                shadow = GETSHADOW(m_ShadowMap0, projCoord0);             
-            }else{
-                shadow = GETSHADOW(m_ShadowMap1, projCoord1);
-            }
-         }else if(maxComp == absv.z){
-            if(vect.z < 0.0){
-                shadow = GETSHADOW(m_ShadowMap2, projCoord2);
-            }else{
-                shadow = GETSHADOW(m_ShadowMap3, projCoord3);
-            }
-         }else if(maxComp == absv.x){
-            if(vect.x < 0.0){
-                shadow = GETSHADOW(m_ShadowMap4, projCoord4);
-            }else{
-                shadow = GETSHADOW(m_ShadowMap5, projCoord5);
-            }
-         }                  
+            shadow = getPointLightShadows(worldPos, m_LightPos,
+                           m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5,
+                           projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5);
+    #else
+       #ifdef PSSM
+            float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x +  m_ViewProjectionMatrixRow2.y * worldPos.y +  m_ViewProjectionMatrixRow2.z * worldPos.z +  m_ViewProjectionMatrixRow2.w;
+            shadow = getDirectionalLightShadows(m_Splits, shadowPosition,
+                           m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,
+                           projCoord0, projCoord1, projCoord2, projCoord3);
+       #else 
+            //spotlight
+            shadow = getSpotLightShadows(m_ShadowMap0,projCoord0);
+       #endif
     #endif   
+  
 
     #ifdef FADE
       shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    
     #endif
+
     shadow= shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
-  
     return color * vec4(shadow, shadow, shadow, 1.0);
 }
 

+ 0 - 96
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag

@@ -1,96 +0,0 @@
-#import "Common/ShaderLib/PssmShadows.glsllib"
-
-#ifdef PSSM
-varying float shadowPosition;
-#endif
-
-varying vec4 projCoord0;
-varying vec4 projCoord1;
-varying vec4 projCoord2;
-varying vec4 projCoord3;
-
-#ifdef POINTLIGHT
-    varying vec4 projCoord4;
-    varying vec4 projCoord5;
-    uniform vec3 m_LightPos;
-    varying vec4 worldPos;
-#endif
-
-#ifdef DISCARD_ALPHA
-    #ifdef COLOR_MAP
-        uniform sampler2D m_ColorMap;
-    #else    
-        uniform sampler2D m_DiffuseMap;
-    #endif
-    uniform float m_AlphaDiscardThreshold;
-    varying vec2 texCoord;
-#endif
-
-#ifdef FADE
-uniform vec2 m_FadeInfo;
-#endif
-
-void main(){   
- 
-    #ifdef DISCARD_ALPHA
-        #ifdef COLOR_MAP
-            float alpha = texture2D(m_ColorMap,texCoord).a;
-        #else    
-            float alpha = texture2D(m_DiffuseMap,texCoord).a;
-        #endif
-        if(alpha<=m_AlphaDiscardThreshold){
-            discard;
-        }
-
-    #endif
-     
-   float shadow = 1.0;
-    #ifdef PSSM
-        if(shadowPosition < m_Splits.x){
-            shadow = GETSHADOW(m_ShadowMap0, projCoord0);
-        }else if( shadowPosition <  m_Splits.y){
-            shadowBorderScale = 0.5;
-            shadow = GETSHADOW(m_ShadowMap1, projCoord1);
-        }else if( shadowPosition <  m_Splits.z){
-            shadowBorderScale = 0.25;
-            shadow = GETSHADOW(m_ShadowMap2, projCoord2);
-        }else if( shadowPosition <  m_Splits.w){
-            shadowBorderScale = 0.125;
-            shadow = GETSHADOW(m_ShadowMap3, projCoord3);
-        }
-    #endif
-
-    #ifdef POINTLIGHT         
-         vec3 vect = worldPos.xyz - m_LightPos;
-         vec3 absv= abs(vect);
-         float maxComp = max(absv.x,max(absv.y,absv.z));
-         if(maxComp == absv.y){
-            if(vect.y < 0.0){
-                shadow = GETSHADOW(m_ShadowMap0, projCoord0);             
-            }else{
-                shadow = GETSHADOW(m_ShadowMap1, projCoord1);
-            }
-         }else if(maxComp == absv.z){
-            if(vect.z < 0.0){
-                shadow = GETSHADOW(m_ShadowMap2, projCoord2);
-            }else{
-                shadow = GETSHADOW(m_ShadowMap3, projCoord3);
-            }
-         }else if(maxComp == absv.x){
-            if(vect.x < 0.0){
-                shadow = GETSHADOW(m_ShadowMap4, projCoord4);
-            }else{
-                shadow = GETSHADOW(m_ShadowMap5, projCoord5);
-            }
-         }                  
-    #endif     
-
-    #ifdef FADE
-      shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    
-    #endif
-    shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
-
-  gl_FragColor = vec4(shadow, shadow, shadow, 1.0);
-
-}
-

+ 0 - 85
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md

@@ -1,85 +0,0 @@
-MaterialDef Post Shadow {
-
-    MaterialParameters {
-        Int FilterMode
-        Boolean HardwareShadows
-
-        Texture2D ShadowMap0
-        Texture2D ShadowMap1
-        Texture2D ShadowMap2
-        Texture2D ShadowMap3
-        //pointLights
-        Texture2D ShadowMap4
-        Texture2D ShadowMap5
-        
-        Float ShadowIntensity
-        Vector4 Splits
-        Vector2 FadeInfo
-
-        Matrix4 LightViewProjectionMatrix0
-        Matrix4 LightViewProjectionMatrix1
-        Matrix4 LightViewProjectionMatrix2
-        Matrix4 LightViewProjectionMatrix3
-        //pointLight
-        Matrix4 LightViewProjectionMatrix4
-        Matrix4 LightViewProjectionMatrix5
-        Vector3 LightPos
-
-        Float PCFEdge
-
-        Float ShadowMapSize
-    }
-
-    Technique {
-        VertexShader GLSL150:   Common/MatDefs/Shadow/PostShadowPSSM.vert
-        FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM15.frag
-
-        WorldParameters {
-            WorldViewProjectionMatrix
-            WorldMatrix
-        }
-
-        Defines {
-            HARDWARE_SHADOWS : HardwareShadows
-            FILTER_MODE : FilterMode
-            PCFEDGE : PCFEdge
-            SHADOWMAP_SIZE : ShadowMapSize
-            FADE : FadeInfo
-            PSSM : Splits
-            POINTLIGHT : LightViewProjectionMatrix5
-        }
-
-        RenderState {
-            Blend Modulate
-            DepthWrite Off   
-            PolyOffset -0.1 0             
-        }
-    }
-
-    Technique {
-        VertexShader GLSL100:   Common/MatDefs/Shadow/PostShadowPSSM.vert
-        FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.frag
-
-        WorldParameters {
-            WorldViewProjectionMatrix
-            WorldMatrix
-        }
-
-        Defines {
-            HARDWARE_SHADOWS : HardwareShadows
-            FILTER_MODE : FilterMode
-            PCFEDGE : PCFEdge
-            SHADOWMAP_SIZE : ShadowMapSize
-            FADE : FadeInfo
-            PSSM : Splits
-            POINTLIGHT : LightViewProjectionMatrix5
-        }
-
-        RenderState {
-            Blend Modulate
-            DepthWrite Off    
-            PolyOffset -0.1 0
-        }
-    }
-
-}

+ 0 - 62
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert

@@ -1,62 +0,0 @@
-uniform mat4 m_LightViewProjectionMatrix0;
-uniform mat4 m_LightViewProjectionMatrix1;
-uniform mat4 m_LightViewProjectionMatrix2;
-uniform mat4 m_LightViewProjectionMatrix3;
-
-uniform mat4 g_WorldViewProjectionMatrix;
-uniform mat4 g_WorldMatrix;
-
-varying vec4 projCoord0;
-varying vec4 projCoord1;
-varying vec4 projCoord2;
-varying vec4 projCoord3;
-
-#ifdef POINTLIGHT
-uniform mat4 m_LightViewProjectionMatrix4;
-uniform mat4 m_LightViewProjectionMatrix5;
-varying vec4 projCoord4;
-varying vec4 projCoord5;
-varying vec4 worldPos;
-#endif
-
-#ifdef PSSM
-varying float shadowPosition;
-#endif
-
-varying vec2 texCoord;
-
-attribute vec3 inPosition;
-
-#ifdef DISCARD_ALPHA
-    attribute vec2 inTexCoord;
-#endif
-
-const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
-                          0.0, 0.5, 0.0, 0.0,
-                          0.0, 0.0, 0.5, 0.0,
-                          0.5, 0.5, 0.5, 1.0);
-
-
-void main(){
-    gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
-
-    #ifdef PSSM
-        shadowPosition = gl_Position.z;
-        vec4 worldPos=vec4(0.0);
-    #endif
-    // get the vertex in world space
-    worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
-
-    #ifdef DISCARD_ALPHA
-       texCoord = inTexCoord;
-    #endif
-    // populate the light view matrices array and convert vertex to light viewProj space
-    projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos;
-    projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
-    projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
-    projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
-    #ifdef POINTLIGHT
-        projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos;
-        projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
-    #endif
-}

+ 0 - 105
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag

@@ -1,105 +0,0 @@
-#import "Common/ShaderLib/PssmShadows15.glsllib"
-
-out vec4 outFragColor;
-
-#ifdef PSSM
-in float shadowPosition;
-#endif
-
-in vec4 projCoord0;
-in vec4 projCoord1;
-in vec4 projCoord2;
-in vec4 projCoord3;
-
-#ifdef POINTLIGHT
-    in vec4 projCoord4;
-    in vec4 projCoord5;
-    uniform vec3 m_LightPos;
-    in vec4 worldPos;
-#endif
-
-#ifdef DISCARD_ALPHA
-    #ifdef COLOR_MAP
-        uniform sampler2D m_ColorMap;
-    #else    
-        uniform sampler2D m_DiffuseMap;
-    #endif
-    uniform float m_AlphaDiscardThreshold;
-    varying vec2 texCoord;
-#endif
-
-#ifdef FADE
-uniform vec2 m_FadeInfo;
-#endif
-
-void main(){
-    
-    #ifdef DISCARD_ALPHA
-        #ifdef COLOR_MAP
-             float alpha = texture2D(m_ColorMap,texCoord).a;
-        #else    
-             float alpha = texture2D(m_DiffuseMap,texCoord).a;
-        #endif
-      
-        if(alpha < m_AlphaDiscardThreshold){
-            discard;
-        }
-    #endif
-
-    float shadow = 1.0;
-    #ifdef PSSM
-        if(shadowPosition < m_Splits.x){
-            shadow = GETSHADOW(m_ShadowMap0, projCoord0);   
-        }else if( shadowPosition <  m_Splits.y){
-            shadowBorderScale = 0.5;
-            shadow = GETSHADOW(m_ShadowMap1, projCoord1);  
-        }else if( shadowPosition <  m_Splits.z){
-            shadowBorderScale = 0.25;
-            shadow = GETSHADOW(m_ShadowMap2, projCoord2); 
-        }else if( shadowPosition <  m_Splits.w){
-            shadowBorderScale = 0.125;
-            shadow = GETSHADOW(m_ShadowMap3, projCoord3); 
-        }
-    #endif
-
-
-    #ifdef POINTLIGHT         
-         vec3 vect = worldPos.xyz - m_LightPos;
-         vec3 absv= abs(vect);
-         float maxComp = max(absv.x,max(absv.y,absv.z));
-         if(maxComp == absv.y){
-            if(vect.y < 0.0){
-                shadow = GETSHADOW(m_ShadowMap0, projCoord0);
-outFragColor = vec4(projCoord0.z);             
-            }else{
-                shadow = GETSHADOW(m_ShadowMap1, projCoord1);
-outFragColor = vec4(projCoord1.z);
-            }
-         }else if(maxComp == absv.z){
-            if(vect.z < 0.0){
-                shadow = GETSHADOW(m_ShadowMap2, projCoord2);
-outFragColor =vec4(projCoord2.z);
-            }else{
-                shadow = GETSHADOW(m_ShadowMap3, projCoord3);
-outFragColor = vec4(projCoord3.z);
-            }
-         }else if(maxComp == absv.x){
-            if(vect.x < 0.0){
-                shadow = GETSHADOW(m_ShadowMap4, projCoord4);
-outFragColor = vec4(projCoord4.z);
-            }else{
-                shadow = GETSHADOW(m_ShadowMap5, projCoord5);
-outFragColor = vec4(projCoord5.z);
-            }
-         }                  
-    #endif   
-
-    #ifdef FADE
-      shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    
-    #endif
-  
-    shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);   
-    outFragColor =  vec4(shadow, shadow, shadow, 1.0);
-
-}
-

+ 0 - 0
engine/src/core-data/Common/ShaderLib/Shadow.glsllib → engine/src/core-data/Common/ShaderLib/BasicShadow.glsllib


+ 82 - 14
engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib → engine/src/core-data/Common/ShaderLib/Shadows.glsllib

@@ -3,7 +3,7 @@
     #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r 
 #else
     #define SHADOWMAP sampler2D
-    #define SHADOWCOMPARE(tex,coord) step(coord.z / coord.w, texture2DProj(tex, coord).r)
+    #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
 #endif
 
 #if FILTER_MODE == 0
@@ -49,29 +49,31 @@ uniform float m_ShadowIntensity;
 const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
 float shadowBorderScale = 1.0;
 
-float Shadow_DoShadowCompareOffset(in SHADOWMAP tex, in vec4 projCoord, in vec2 offset){
+float Shadow_DoShadowCompareOffset(SHADOWMAP tex, vec4 projCoord, vec2 offset){
     vec4 coord = vec4(projCoord.xy + offset.xy * pixSize2 * shadowBorderScale, projCoord.zw);
     return SHADOWCOMPARE(tex, coord);
 }
 
-float Shadow_DoShadowCompare(in SHADOWMAP tex, vec4 projCoord){
+float Shadow_DoShadowCompare(SHADOWMAP tex, vec4 projCoord){
     return SHADOWCOMPARE(tex, projCoord);
 }
 
-float Shadow_BorderCheck(in vec2 coord){
-#ifdef PSSM
+float Shadow_BorderCheck(vec2 coord){
     // Fastest, "hack" method (uses 4-5 instructions)
     vec4 t = vec4(coord.xy, 0.0, 1.0);
     t = step(t.wwxy, t.xyzz);
     return dot(t,t);
-#else
-//fix me this is a big fat hack to avoid issues whith point lights, 
-//this function fail to return correct values, but it works with PSSM
-    return 0.0;
-#endif
 }
 
-float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
+float Shadow_Nearest(SHADOWMAP tex, vec4 projCoord){
+    float border = Shadow_BorderCheck(projCoord.xy);
+    if (border > 0.0){
+        return 1.0;
+    }
+    return Shadow_DoShadowCompare(tex,projCoord);
+}
+
+float Shadow_DoDither_2x2(SHADOWMAP tex, vec4 projCoord){
     float border = Shadow_BorderCheck(projCoord.xy);
     if (border > 0.0)
         return 1.0;
@@ -87,7 +89,7 @@ float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
     return shadow;
 }
 
-float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
+float Shadow_DoBilinear_2x2(SHADOWMAP tex, vec4 projCoord){
     float border = Shadow_BorderCheck(projCoord.xy);
     if (border > 0.0)
         return 1.0;
@@ -102,7 +104,7 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
     return mix( mx.x, mx.y, f.y );
 }
 
-float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
+float Shadow_DoPCF(SHADOWMAP tex, vec4 projCoord){
     float shadow = 0.0;
     float border = Shadow_BorderCheck(projCoord.xy);
     if (border > 0.0)
@@ -136,7 +138,7 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
     const vec2 poissonDisk10 = vec2(0.5653144, 0.60262);
     const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419);
 
-float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){   
+float Shadow_DoPCFPoisson(SHADOWMAP tex, vec4 projCoord){   
     float shadow = 0.0;
     float border = Shadow_BorderCheck(projCoord.xy);
     if (border > 0.0)
@@ -161,3 +163,69 @@ float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
     return shadow;
 }
 
+
+#ifdef POINTLIGHT       
+    float getPointLightShadows(vec4 worldPos,vec3 lightPos,
+                           SHADOWMAP shadowMap0,SHADOWMAP shadowMap1,SHADOWMAP shadowMap2,SHADOWMAP shadowMap3,SHADOWMAP shadowMap4,SHADOWMAP shadowMap5,
+                           vec4 projCoord0,vec4 projCoord1,vec4 projCoord2,vec4 projCoord3,vec4 projCoord4,vec4 projCoord5){
+        float shadow = 1.0;
+        vec3 vect = worldPos.xyz - lightPos;
+        vec3 absv= abs(vect);
+        float maxComp = max(absv.x,max(absv.y,absv.z));
+        if(maxComp == absv.y){
+           if(vect.y < 0.0){
+               shadow = GETSHADOW(shadowMap0, projCoord0 / projCoord0.w);
+           }else{
+               shadow = GETSHADOW(shadowMap1, projCoord1 / projCoord1.w);
+           }
+        }else if(maxComp == absv.z){
+           if(vect.z < 0.0){
+               shadow = GETSHADOW(shadowMap2, projCoord2 / projCoord2.w);
+           }else{
+               shadow = GETSHADOW(shadowMap3, projCoord3 / projCoord3.w);
+           }
+        }else if(maxComp == absv.x){
+           if(vect.x < 0.0){
+               shadow = GETSHADOW(shadowMap4, projCoord4 / projCoord4.w);
+           }else{
+               shadow = GETSHADOW(shadowMap5, projCoord5 / projCoord5.w);
+           }
+        }  
+        return shadow;
+    }
+#else
+ #ifdef PSSM
+    float getDirectionalLightShadows(vec4 splits,float shadowPosition,
+                                    SHADOWMAP shadowMap0,SHADOWMAP shadowMap1,SHADOWMAP shadowMap2,SHADOWMAP shadowMap3,
+                                    vec4 projCoord0,vec4 projCoord1,vec4 projCoord2,vec4 projCoord3){    
+        float shadow = 1.0;   
+        if(shadowPosition < splits.x){
+            shadow = GETSHADOW(shadowMap0, projCoord0 );   
+        }else if( shadowPosition <  splits.y){
+            shadowBorderScale = 0.5;
+            shadow = GETSHADOW(shadowMap1, projCoord1);  
+        }else if( shadowPosition <  splits.z){
+            shadowBorderScale = 0.25;
+            shadow = GETSHADOW(shadowMap2, projCoord2); 
+        }else if( shadowPosition <  splits.w){
+            shadowBorderScale = 0.125;
+            shadow = GETSHADOW(shadowMap3, projCoord3); 
+        }
+        return shadow;
+    }
+ #else
+    float getSpotLightShadows(SHADOWMAP shadowMap, vec4 projCoord){
+        float shadow = 1.0;         
+        projCoord /= projCoord.w;
+        shadow = GETSHADOW(shadowMap, projCoord);
+        
+        //a small falloff to make the shadow blend nicely into the not lighten
+        //we translate the texture coordinate value to a -1,1 range so the length 
+        //of the texture coordinate vector is actually the radius of the lighten area on the ground
+        projCoord = projCoord * 2.0 - 1.0;
+        float fallOff = ( length(projCoord.xy) - 0.9 ) / 0.1;
+        return mix(shadow,1.0,clamp(fallOff,0.0,1.0));
+
+    }
+ #endif
+#endif

+ 81 - 15
engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib → engine/src/core-data/Common/ShaderLib/Shadows15.glsllib

@@ -9,18 +9,18 @@
     #define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z)
 #else
     #define SHADOWMAP sampler2D
-    #define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z / coord.w, textureProjOffset(tex, coord, offset).r)
-    #define SHADOWCOMPARE(tex,coord) step(coord.z / coord.w, textureProj(tex, coord).r) 
+    #define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r)
+    #define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r) 
     #define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy))
 #endif
 
 
 #if FILTER_MODE == 0
-    #define GETSHADOW SHADOWCOMPARE
+    #define GETSHADOW Shadow_Nearest
     #define KERNEL 1
 #elif FILTER_MODE == 1
     #ifdef HARDWARE_SHADOWS
-        #define GETSHADOW SHADOWCOMPARE
+        #define GETSHADOW Shadow_Nearest
     #else
         #define GETSHADOW Shadow_DoBilinear_2x2
     #endif
@@ -59,16 +59,18 @@ const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
 float shadowBorderScale = 1.0;
 
 float Shadow_BorderCheck(in vec2 coord){
-#ifdef PSSM    
     // Fastest, "hack" method (uses 4-5 instructions)
     vec4 t = vec4(coord.xy, 0.0, 1.0);
     t = step(t.wwxy, t.xyzz);
-    return dot(t,t);
-#else
-//fix me this is a big fat hack to avoid issues whith point lights, 
-//this function fail to return correct values, but it works with PSSM
-    return 0.0;
-#endif
+    return dot(t,t);  
+}
+
+float Shadow_Nearest(in SHADOWMAP tex, in vec4 projCoord){
+    float border = Shadow_BorderCheck(projCoord.xy);
+    if (border > 0.0){
+        return 1.0;
+    }
+    return SHADOWCOMPARE(tex,projCoord);
 }
 
 float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
@@ -109,9 +111,9 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
    return mix( mx.x, mx.y, f.y );
 }
 
-float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
-    
-    vec2 pixSize = pixSize2 * shadowBorderScale;   
+float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){    
+
+    vec2 pixSize = pixSize2 * shadowBorderScale;  
     float shadow = 0.0;
     float border = Shadow_BorderCheck(projCoord.xy);
     if (border > 0.0)
@@ -146,7 +148,7 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
     const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419);
 
 
-float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
+float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){  
 
     float shadow = 0.0;
     float border = Shadow_BorderCheck(projCoord.xy);
@@ -173,4 +175,68 @@ float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
     return shadow * 0.08333333333;
 }
 
+#ifdef POINTLIGHT       
+    float getPointLightShadows(in vec4 worldPos,in vec3 lightPos,
+                           in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,in SHADOWMAP shadowMap4,in SHADOWMAP shadowMap5,
+                           in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3,in vec4 projCoord4,in vec4 projCoord5){
+        float shadow = 1.0;
+        vec3 vect = worldPos.xyz - lightPos;
+        vec3 absv= abs(vect);
+        float maxComp = max(absv.x,max(absv.y,absv.z));
+        if(maxComp == absv.y){
+           if(vect.y < 0.0){
+               shadow = GETSHADOW(shadowMap0, projCoord0 / projCoord0.w);
+           }else{
+               shadow = GETSHADOW(shadowMap1, projCoord1 / projCoord1.w);
+           }
+        }else if(maxComp == absv.z){
+           if(vect.z < 0.0){
+               shadow = GETSHADOW(shadowMap2, projCoord2 / projCoord2.w);
+           }else{
+               shadow = GETSHADOW(shadowMap3, projCoord3 / projCoord3.w);
+           }
+        }else if(maxComp == absv.x){
+           if(vect.x < 0.0){
+               shadow = GETSHADOW(shadowMap4, projCoord4 / projCoord4.w);
+           }else{
+               shadow = GETSHADOW(shadowMap5, projCoord5 / projCoord5.w);
+           }
+        }  
+        return shadow;
+    }
+#else
+ #ifdef PSSM
+    float getDirectionalLightShadows(in vec4 splits,in float shadowPosition,
+                                    in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,
+                                    in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3){    
+        float shadow = 1.0;   
+        if(shadowPosition < splits.x){
+            shadow = GETSHADOW(shadowMap0, projCoord0 );   
+        }else if( shadowPosition <  splits.y){
+            shadowBorderScale = 0.5;
+            shadow = GETSHADOW(shadowMap1, projCoord1);  
+        }else if( shadowPosition <  splits.z){
+            shadowBorderScale = 0.25;
+            shadow = GETSHADOW(shadowMap2, projCoord2); 
+        }else if( shadowPosition <  splits.w){
+            shadowBorderScale = 0.125;
+            shadow = GETSHADOW(shadowMap3, projCoord3); 
+        }
+        return shadow;
+    }
+ #else
+    float getSpotLightShadows(in SHADOWMAP shadowMap,in  vec4 projCoord){
+        float shadow = 1.0;     
+        projCoord /= projCoord.w;
+        shadow = GETSHADOW(shadowMap,projCoord);
+        
+        //a small falloff to make the shadow blend nicely into the not lighten
+        //we translate the texture coordinate value to a -1,1 range so the length 
+        //of the texture coordinate vector is actually the radius of the lighten area on the ground
+        projCoord = projCoord * 2.0 - 1.0;
+        float fallOff = ( length(projCoord.xy) - 0.9 ) / 0.1;
+        return mix(shadow,1.0,clamp(fallOff,0.0,1.0));
 
+    }
+ #endif
+#endif

+ 233 - 0
engine/src/core/com/jme3/shadow/AbstractShadowFilter.java

@@ -0,0 +1,233 @@
+/*
+ * 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.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.texture.FrameBuffer;
+import java.io.IOException;
+
+/**
+ *
+ * Generic abstract filter that holds common implementations for the different
+ * shadow filtesr
+ *
+ * @author Rémy Bouquet aka Nehon
+ */
+public abstract class AbstractShadowFilter<T extends AbstractShadowRenderer> extends Filter {
+
+    protected T shadowRenderer;
+    protected ViewPort viewPort;
+
+    /**
+     * Abstract class constructor
+     *
+     * @param manager the application asset manager
+     * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048,
+     * etc...)
+     * @param nbShadowMaps the number of shadow maps rendered (the more shadow
+     * maps the more quality, the less fps).
+     * @param shadowRenderer the shadowRenderer to use for this Filter
+     */
+    @SuppressWarnings("all")
+    protected AbstractShadowFilter(AssetManager manager, int shadowMapSize, T shadowRenderer) {
+        super("Post Shadow");
+        material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md");
+        this.shadowRenderer = shadowRenderer;
+        this.shadowRenderer.setPostShadowMaterial(material);
+    }
+
+    @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) {
+        shadowRenderer.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) {
+        shadowRenderer.postQueue(queue);
+    }
+
+    @Override
+    protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
+        shadowRenderer.setPostShadowParams();
+    }
+
+    @Override
+    protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+        shadowRenderer.needsfallBackMaterial = true;
+        shadowRenderer.initialize(renderManager, vp);
+        this.viewPort = vp;
+    }
+
+    /**
+     * returns the shdaow intensity
+     *
+     * @see #setShadowIntensity(float shadowIntensity)
+     * @return shadowIntensity
+     */
+    public float getShadowIntensity() {
+        return shadowRenderer.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) {
+        shadowRenderer.setShadowIntensity(shadowIntensity);
+    }
+
+    /**
+     * returns the edges thickness <br>
+     *
+     * @see #setEdgesThickness(int edgesThickness)
+     * @return edgesThickness
+     */
+    public int getEdgesThickness() {
+        return shadowRenderer.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) {
+        shadowRenderer.setEdgesThickness(edgesThickness);
+    }
+
+    /**
+     * returns true if the PssmRenderer flushed the shadow queues
+     *
+     * @return flushQueues
+     */
+    public boolean isFlushQueues() {
+        return shadowRenderer.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) {
+        shadowRenderer.setFlushQueues(flushQueues);
+    }
+
+    /**
+     * sets the shadow compare mode see {@link CompareMode} for more info
+     *
+     * @param compareMode
+     */
+    final public void setShadowCompareMode(CompareMode compareMode) {
+        shadowRenderer.setShadowCompareMode(compareMode);
+    }
+
+    /**
+     * returns the shadow compare mode
+     *
+     * @see CompareMode
+     * @return the shadowCompareMode
+     */
+    public CompareMode getShadowCompareMode() {
+        return shadowRenderer.getShadowCompareMode();
+    }
+
+    /**
+     * Sets the filtering mode for shadow edges see {@link EdgeFilteringMode}
+     * for more info
+     *
+     * @param filterMode
+     */
+    final public void setEdgeFilteringMode(EdgeFilteringMode filterMode) {
+        shadowRenderer.setEdgeFilteringMode(filterMode);
+    }
+
+    /**
+     * returns the the edge filtering mode
+     *
+     * @see EdgeFilteringMode
+     * @return
+     */
+    public EdgeFilteringMode getEdgeFilteringMode() {
+        return shadowRenderer.getEdgeFilteringMode();
+    }
+
+    @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);
+
+    }
+}

+ 563 - 0
engine/src/core/com/jme3/shadow/AbstractShadowRenderer.java

@@ -0,0 +1,563 @@
+/*
+ * 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.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Matrix4f;
+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.RenderQueue;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.WireFrustum;
+import com.jme3.shadow.PssmShadowRenderer.FilterMode;
+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;
+
+/**
+ * abstract shadow renderer that holds commons feature to have for a shadow renderer
+ * @author Rémy Bouquet aka Nehon
+ */
+public abstract class AbstractShadowRenderer implements SceneProcessor {
+
+    protected int nbShadowMaps = 1;
+    protected float shadowMapSize;
+    protected float shadowIntensity = 0.7f;
+    protected RenderManager renderManager;
+    protected ViewPort viewPort;
+    protected FrameBuffer[] shadowFB;
+    protected Texture2D[] shadowMaps;
+    protected Texture2D dummyTex;
+    protected Material preshadowMat;
+    protected Material postshadowMat;
+    protected Matrix4f[] lightViewProjectionsMatrices;
+    protected boolean noOccluders = false;
+    protected AssetManager assetManager;
+    protected boolean debug = false;
+    protected float edgesThickness = 1.0f;
+    protected EdgeFilteringMode edgeFilteringMode;
+    protected CompareMode shadowCompareMode;
+    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
+    //a list of material of the post shadow queue geometries.
+    protected List<Material> matCache = new ArrayList<Material>();
+
+    /**
+     * Create an abstract shadow renderer, this is to be called in extending classes
+     * @param assetManager the application asset manager
+     * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048,
+     * etc...)
+     * @param nbShadowMaps the number of shadow maps rendered (the more shadow
+     * maps the more quality, the less fps).
+     */
+    protected AbstractShadowRenderer(AssetManager assetManager, int shadowMapSize, int nbShadowMaps) {
+
+        this.assetManager = assetManager;
+        this.postshadowMat = new Material(assetManager, "Common/MatDefs/Shadow/PostShadow.j3md");
+        this.nbShadowMaps = nbShadowMaps;
+        this.shadowMapSize = shadowMapSize;
+        shadowFB = new FrameBuffer[nbShadowMaps];
+        shadowMaps = new Texture2D[nbShadowMaps];
+        dispPic = new Picture[nbShadowMaps];
+        lightViewProjectionsMatrices = new Matrix4f[nbShadowMaps];
+
+        //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
+        dummyTex = new Texture2D(shadowMapSize, shadowMapSize, Format.RGBA8);
+
+        preshadowMat = new Material(assetManager, "Common/MatDefs/Shadow/PreShadow.j3md");
+        postshadowMat.setFloat("ShadowMapSize", shadowMapSize);
+
+        for (int i = 0; i < nbShadowMaps; i++) {
+            lightViewProjectionsMatrices[i] = new Matrix4f();
+            shadowFB[i] = new FrameBuffer(shadowMapSize, shadowMapSize, 1);
+            shadowMaps[i] = new Texture2D(shadowMapSize, shadowMapSize, 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(assetManager, shadowMaps[i], false);
+        }
+
+        setShadowCompareMode(CompareMode.Hardware);
+        setEdgeFilteringMode(EdgeFilteringMode.Bilinear);
+        setShadowIntensity(0.7f);
+
+    }
+
+    /**
+     * set the post shadow material for this renderer
+     * @param postShadowMat 
+     */
+    protected final void setPostShadowMaterial(Material postShadowMat) {
+        this.postshadowMat = postShadowMat;
+        postshadowMat.setFloat("ShadowMapSize", shadowMapSize);
+        for (int i = 0; i < nbShadowMaps; i++) {
+            postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]);
+        }
+        setShadowCompareMode(shadowCompareMode);
+        setEdgeFilteringMode(edgeFilteringMode);
+        setShadowIntensity(shadowIntensity);
+    }
+
+    /**
+     * Sets the filtering mode for shadow edges see {@link FilterMode} for more
+     * info
+     *
+     * @param filterMode
+     */
+    final public void setEdgeFilteringMode(EdgeFilteringMode filterMode) {
+        if (filterMode == null) {
+            throw new NullPointerException();
+        }
+
+        if (this.edgeFilteringMode == filterMode) {
+            return;
+        }
+
+        this.edgeFilteringMode = filterMode;
+        postshadowMat.setInt("FilterMode", filterMode.getMaterialParamValue());
+        postshadowMat.setFloat("PCFEdge", edgesThickness);
+        if (shadowCompareMode == CompareMode.Hardware) {
+            for (Texture2D shadowMap : shadowMaps) {
+                if (filterMode == EdgeFilteringMode.Bilinear) {
+                    shadowMap.setMagFilter(MagFilter.Bilinear);
+                    shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps);
+                } else {
+                    shadowMap.setMagFilter(MagFilter.Nearest);
+                    shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
+                }
+            }
+        }
+    }
+
+    /**
+     * returns the the edge filtering mode
+     *
+     * @see EdgeFilteringMode
+     * @return
+     */
+    public EdgeFilteringMode getEdgeFilteringMode() {
+        return edgeFilteringMode;
+    }
+
+    /**
+     * sets the shadow compare mode see {@link CompareMode} for more info
+     *
+     * @param compareMode
+     */
+    final public void setShadowCompareMode(CompareMode compareMode) {
+        if (compareMode == null) {
+            throw new NullPointerException();
+        }
+
+        if (this.shadowCompareMode == compareMode) {
+            return;
+        }
+
+        this.shadowCompareMode = compareMode;
+        for (Texture2D shadowMap : shadowMaps) {
+            if (compareMode == CompareMode.Hardware) {
+                shadowMap.setShadowCompareMode(ShadowCompareMode.LessOrEqual);
+                if (edgeFilteringMode == EdgeFilteringMode.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);
+    }
+
+    /**
+     * returns the shadow compare mode
+     *
+     * @see CompareMode
+     * @return the shadowCompareMode
+     */
+    public CompareMode getShadowCompareMode() {
+        return shadowCompareMode;
+    }
+
+    //debug function that create a displayable frustrum
+    protected 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;
+            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;
+    }
+
+    /**
+     * This mehtod is called once per frame.
+     * it is responsible for updating the shadow cams according to the light view.
+     * @param viewCam the scene cam
+     */
+    protected abstract void updateShadowCams(Camera viewCam);
+
+    /**
+     * this method must return the geomtryList that contains the oclluders to be rendered in the shadow map
+     * @param shadowMapIndex the index of the shadow map being rendered
+     * @param sceneOccluders the occluders of the whole scene
+     * @param sceneReceivers the recievers of the whole scene
+     * @return 
+     */
+    protected abstract GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers);
+
+    /**
+     * return the shadow camera to use for rendering the shadow map according the given index
+     * @param shadowMapIndex the index of the shadow map being rendered
+     * @return the shadowCam
+     */
+    protected abstract Camera getShadowCam(int shadowMapIndex);
+
+    /**
+     * responsible for displaying the frustum of the shadow cam for debug purpose
+     * @param shadowMapIndex 
+     */
+    protected void doDisplayFrustumDebug(int shadowMapIndex) {
+    }
+
+    @SuppressWarnings("fallthrough")
+    public void postQueue(RenderQueue rq) {
+        GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);
+        GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive);
+        if (receivers.size() == 0 || occluders.size() == 0) {
+            return;
+        }
+
+        updateShadowCams(viewPort.getCamera());
+
+        Renderer r = renderManager.getRenderer();
+        renderManager.setForcedMaterial(preshadowMat);
+        renderManager.setForcedTechnique("PreShadow");
+
+        for (int shadowMapIndex = 0; shadowMapIndex < nbShadowMaps; shadowMapIndex++) {
+
+            if (debugfrustums) {
+                doDisplayFrustumDebug(shadowMapIndex);
+            }
+            renderShadowMap(shadowMapIndex, occluders, receivers);
+
+        }
+
+        debugfrustums = false;
+        if (flushQueues) {
+            occluders.clear();
+        }
+        //restore setting for future rendering
+        r.setFrameBuffer(viewPort.getOutputFrameBuffer());
+        renderManager.setForcedMaterial(null);
+        renderManager.setForcedTechnique(null);
+        renderManager.setCamera(viewPort.getCamera(), false);
+
+    }
+
+    protected void renderShadowMap(int shadowMapIndex, GeometryList occluders, GeometryList receivers) {
+        GeometryList mapOccluders = getOccludersToRender(shadowMapIndex, occluders, receivers);
+        Camera shadowCam = getShadowCam(shadowMapIndex);
+
+        //saving light view projection matrix for this split            
+        lightViewProjectionsMatrices[shadowMapIndex].set(shadowCam.getViewProjectionMatrix());
+        renderManager.setCamera(shadowCam, false);
+
+        renderManager.getRenderer().setFrameBuffer(shadowFB[shadowMapIndex]);
+        renderManager.getRenderer().clearBuffers(false, true, false);
+
+        // render shadow casters to shadow map
+        viewPort.getQueue().renderShadowQueue(mapOccluders, renderManager, shadowCam, true);
+    }
+    boolean debugfrustums = false;
+
+    public void displayFrustum() {
+        debugfrustums = true;
+    }
+
+    //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);
+
+        }
+
+    }
+
+    /**
+     * This method is called once per frame and is responsible of setting the material 
+     * parameters than sub class may need to set on the post material
+     * @param material the materail to use for the post shadow pass
+     */
+    protected abstract void setMaterialParameters(Material material);
+
+    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) {
+
+            mat.setFloat("ShadowMapSize", shadowMapSize);
+
+            for (int j = 0; j < nbShadowMaps; j++) {
+                mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
+            }
+            for (int j = 0; j < nbShadowMaps; j++) {
+                mat.setTexture("ShadowMap" + j, shadowMaps[j]);
+            }
+            mat.setBoolean("HardwareShadows", shadowCompareMode == CompareMode.Hardware);
+            mat.setInt("FilterMode", edgeFilteringMode.getMaterialParamValue());
+            mat.setFloat("PCFEdge", edgesThickness);
+            mat.setFloat("ShadowIntensity", shadowIntensity);
+
+            setMaterialParameters(mat);
+        }
+
+        //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();
+        }
+
+    }
+
+    /**
+     * for internal use only
+     */
+    protected void setPostShadowParams() {
+        setMaterialParameters(postshadowMat);
+        for (int j = 0; j < nbShadowMaps; j++) {
+            postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
+            postshadowMat.setTexture("ShadowMap" + j, shadowMaps[j]);
+        }
+    }
+
+    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);
+    }
+
+    /**
+     * 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);
+    }
+
+    /**
+     * 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;
+    }
+}

+ 3 - 1
engine/src/core/com/jme3/shadow/BasicShadowRenderer.java

@@ -52,7 +52,9 @@ import com.jme3.ui.Picture;
  * it's useful to render shadows in a small scene, but edges might look a bit jagged.
  * 
  * @author Kirill Vainer
+ * @deprecated use {@link DirectionalLightShadowRenderer} with one split.
  */
+@Deprecated
 public class BasicShadowRenderer implements SceneProcessor {
 
     private RenderManager renderManager;
@@ -79,7 +81,7 @@ public class BasicShadowRenderer implements SceneProcessor {
         shadowCam = new Camera(size, size);
 
         preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
-        postshadowMat = new Material(manager, "Common/MatDefs/Shadow/PostShadow.j3md");
+        postshadowMat = new Material(manager, "Common/MatDefs/Shadow/BasicPostShadow.j3md");
         postshadowMat.setTexture("ShadowMap", shadowMap);
 
         dispPic.setTexture(manager, shadowMap, false);

+ 48 - 0
engine/src/core/com/jme3/shadow/CompareMode.java

@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+/**
+ * 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;
+}

+ 174 - 0
engine/src/core/com/jme3/shadow/DirectionalLightShadowFilter.java

@@ -0,0 +1,174 @@
+/*
+ * 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.DirectionalLight;
+import com.jme3.material.Material;
+import java.io.IOException;
+
+/**
+ *
+ * This Filter does basically the same as a DirectionalLightShadowRenderer
+ * except it renders the post shadow pass as a fulscreen quad pass instead of a
+ * geometry pass. It's mostly faster than PssmShadowRenderer 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 DirectionalLightShadowFilter extends AbstractShadowFilter<DirectionalLightShadowRenderer> {
+
+
+
+    /**
+     * Creates a DirectionalLightShadowFilter 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 assetManager the application asset manager
+     * @param shadowMapSize 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 DirectionalLightShadowFilter(AssetManager assetManager, int shadowMapSize, int nbSplits) {
+        super(assetManager, shadowMapSize, new DirectionalLightShadowRenderer(assetManager, shadowMapSize, nbSplits));  
+    }
+
+    /**
+     * return the light used to cast shadows
+     *
+     * @return the DirectionalLight
+     */
+    public DirectionalLight getLight() {
+        return shadowRenderer.getLight();
+    }
+
+    /**
+     * Sets the light to use to cast shadows
+     *
+     * @param light a DirectionalLight
+     */
+    public void setLight(DirectionalLight light) {
+        shadowRenderer.setLight(light);
+    }
+
+    /**
+     * returns the labda parameter
+     *
+     * @see #setLambda(float lambda)
+     * @return lambda
+     */
+    public float getLambda() {
+        return shadowRenderer.getLambda();
+    }
+
+    /**
+     * Adjust the repartition of the different shadow maps in the shadow extend
+     * usualy goes from 0.0 to 1.0 a low value give a more linear repartition
+     * resulting in a constant quality in the shadow over the extends, but near
+     * shadows could look very jagged a high value give a more logarithmic
+     * repartition resulting in a high quality for near shadows, but the quality
+     * quickly decrease over the extend. the default value is set to 0.65f
+     * (theoric optimal value).
+     *
+     * @param lambda the lambda value.
+     */
+    public void setLambda(float lambda) {
+        shadowRenderer.setLambda(lambda);
+    }
+
+    /**
+     * How far the shadows are rendered in the view
+     *
+     * @see setShadowZExtend(float zFar)
+     * @return shadowZExtend
+     */
+    public float getShadowZExtend() {
+        return shadowRenderer.getShadowZExtend();
+    }
+
+    /**
+     * 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) {
+        shadowRenderer.setShadowZExtend(zFar);
+    }
+
+    /**
+     * 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) {
+        shadowRenderer.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 shadowRenderer.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);
+
+    }
+}

+ 257 - 0
engine/src/core/com/jme3/shadow/DirectionalLightShadowRenderer.java

@@ -0,0 +1,257 @@
+/*
+ * 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.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.OpaqueComparator;
+import com.jme3.scene.Node;
+
+/**
+ * DirectionalLightShadowRenderer 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 DirectionalLightShadowRenderer extends AbstractShadowRenderer {
+
+    protected float lambda = 0.65f;
+    protected float zFarOverride = 0;
+    protected Camera shadowCam;
+    protected ColorRGBA splits;    
+    protected GeometryList splitOccluders = new GeometryList(new OpaqueComparator());
+    protected float[] splitsArray;
+    protected DirectionalLight light;
+    protected Vector3f[] points = new Vector3f[8];
+    //Holding the info for fading shadows in the far distance 
+    protected Vector2f fadeInfo;
+    protected float fadeLength;
+
+
+    /**
+     * Create a DirectionalLightShadowRenderer 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 assetManager the application asset manager
+     * @param shadowMapSize 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 DirectionalLightShadowRenderer(AssetManager assetManager, int shadowMapSize, int nbSplits) {
+        super(assetManager, shadowMapSize, nbSplits);
+
+        nbShadowMaps = Math.max(Math.min(nbSplits, 4), 1);
+        splits = new ColorRGBA();
+        splitsArray = new float[nbSplits + 1];
+        shadowCam = new Camera(shadowMapSize, shadowMapSize);
+        shadowCam.setParallelProjection(true);
+
+        for (int i = 0; i < points.length; i++) {
+            points[i] = new Vector3f();
+        }
+    }
+
+    /**
+     * return the light used to cast shadows
+     *
+     * @return the DirectionalLight
+     */
+    public DirectionalLight getLight() {
+        return light;
+    }
+
+    /**
+     * Sets the light to use to cast shadows
+     *
+     * @param light a DirectionalLight
+     */
+    public void setLight(DirectionalLight light) {
+        this.light = light;
+    }
+
+    @Override
+    protected void updateShadowCams(Camera viewCam) {
+
+        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);
+
+        //shadowCam.setDirection(direction);
+        shadowCam.getRotation().lookAt(light.getDirection(), shadowCam.getUp());
+        shadowCam.update();
+        shadowCam.updateViewProjection();
+
+        PssmShadowUtil.updateFrustumSplits(splitsArray, frustumNear, zFar, lambda);
+
+
+        switch (splitsArray.length) {
+            case 5:
+                splits.a = splitsArray[4];
+            case 4:
+                splits.b = splitsArray[3];
+            case 3:
+                splits.g = splitsArray[2];
+            case 2:
+            case 1:
+                splits.r = splitsArray[1];
+                break;
+        }
+
+    }
+
+    @Override
+    protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers) {
+
+        // update frustum points based on current camera and split
+        ShadowUtil.updateFrustumPoints(viewPort.getCamera(), splitsArray[shadowMapIndex], splitsArray[shadowMapIndex + 1], 1.0f, points);
+
+        //Updating shadow cam with curent split frustra
+        ShadowUtil.updateShadowCamera(sceneOccluders, sceneReceivers, shadowCam, points, splitOccluders);
+
+        return splitOccluders;
+    }
+
+    @Override
+    protected Camera getShadowCam(int shadowMapIndex) {
+        return shadowCam;
+    }    
+
+    @Override
+    protected void doDisplayFrustumDebug(int shadowMapIndex) {
+        ((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, shadowMapIndex));
+        ShadowUtil.updateFrustumPoints2(shadowCam, points);
+        ((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, shadowMapIndex));
+    }
+
+    @Override
+    protected void setMaterialParameters(Material material) {
+        material.setColor("Splits", splits);
+    }
+
+    /**
+     * returns the labda parameter see #setLambda(float lambda)
+     *
+     * @return lambda
+     */
+    public float getLambda() {
+        return lambda;
+    }
+
+    /*
+     * Adjust the repartition of the different shadow maps in the shadow extend
+     * usualy goes from 0.0 to 1.0
+     * a low value give a more linear repartition resulting in a constant quality in the shadow over the extends, but near shadows could look very jagged
+     * a high value give a more logarithmic repartition resulting in a high quality for near shadows, but the quality quickly decrease over the extend.
+     * the default value is set to 0.65f (theoric optimal value).
+     * @param lambda the lambda value.
+     */
+    public void setLambda(float lambda) {
+        this.lambda = lambda;
+    }
+
+    /**
+     * 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;
+
+    }
+
+    /**
+     * 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;
+    }
+}

+ 39 - 34
engine/src/core/com/jme3/shadow/ShadowCamera.java → engine/src/core/com/jme3/shadow/EdgeFilteringMode.java

@@ -31,45 +31,50 @@
  */
 package com.jme3.shadow;
 
-import com.jme3.light.DirectionalLight;
-import com.jme3.light.Light;
-import com.jme3.light.PointLight;
-import com.jme3.math.Vector3f;
-import com.jme3.renderer.Camera;
-
 /**
- * Creates a camera according to a light
- * Handy to compute projection matrix of a light
- * @author Kirill Vainer
+ * <code>ShadowEdgeFiltering</code> specifies how shadows are filtered
  */
-public class ShadowCamera {
+public enum EdgeFilteringMode {
 
-    private Vector3f[] points = new Vector3f[8];
-    private Light target;
+    /**
+     * Shadows are not filtered. Nearest sample is used, causing in blocky
+     * shadows.
+     */
+    Nearest(0),
+    /**
+     * Bilinear filtering is used. Has the potential of being hardware
+     * accelerated on some GPUs
+     */
+    Bilinear(1),
+    /**
+     * Dither-based sampling is used, very cheap but can look bad at low
+     * resolutions.
+     */
+    Dither(2),
+    /**
+     * 4x4 percentage-closer filtering is used. Shadows will be smoother at the
+     * cost of performance
+     */
+    PCF4(3),
+    /**
+     * 8x8 percentage-closer filtering is used. Shadows will be smoother at the
+     * cost of performance
+     */
+    PCFPOISSON(4),
+    /**
+     * 8x8 percentage-closer filtering is used. Shadows will be smoother at the
+     * cost of performance
+     */
+    PCF8(5);
+    
+    int materialParamValue;
 
-    public ShadowCamera(Light target) {
-        this.target = target;
-        for (int i = 0; i < points.length; i++) {
-            points[i] = new Vector3f();
-        }
+    private EdgeFilteringMode(int val) {
+        materialParamValue = val;
     }
 
-    /**
-     * Updates the camera view direction and position based on the light
-     */
-    public void updateLightCamera(Camera lightCam) {
-        if (target.getType() == Light.Type.Directional) {
-            DirectionalLight dl = (DirectionalLight) target;
-            lightCam.setParallelProjection(true);
-            lightCam.setLocation(Vector3f.ZERO);
-            lightCam.lookAtDirection(dl.getDirection(), Vector3f.UNIT_Y);
-            lightCam.setFrustum(-1, 1, -1, 1, 1, -1);
-        } else {
-            PointLight pl = (PointLight) target;
-            lightCam.setParallelProjection(false);
-            lightCam.setLocation(pl.getPosition());
-            // direction will have to be calculated automatically
-            lightCam.setFrustumPerspective(45, 1, 1, 300);
-        }
+    public int getMaterialParamValue() {
+        return materialParamValue;
     }
+    
 }

+ 23 - 155
engine/src/core/com/jme3/shadow/PointLightShadowFilter.java

@@ -44,90 +44,44 @@ 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
- * 
+/**
+ *
+ * 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;
+public class PointLightShadowFilter extends AbstractShadowFilter<PointLightShadowRenderer> {
 
     /**
-     * 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). 
+     * Creates a PointLightShadowFilter
+     *
+     * @param assetManager the application asset manager
+     * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048,
+     * etc...)
      */
-    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;
+    public PointLightShadowFilter(AssetManager assetManager, int shadowMapSize) {
+        super(assetManager, shadowMapSize,new PointLightShadowRenderer(assetManager, shadowMapSize));
     }
 
-    @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();
+        return shadowRenderer.getLight();
     }
 
     /**
@@ -136,93 +90,7 @@ public class PointLightShadowFilter extends Filter {
      * @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();        
+        shadowRenderer.setLight(light);
     }
 
     @Override

+ 36 - 632
engine/src/core/com/jme3/shadow/PointLightShadowRenderer.java

@@ -34,451 +34,47 @@ 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/>
+ * PointLightShadowRenderer renders shadows for a point light
+ *
  * @author Rémy Bouquet aka Nehon
  */
-public class PointLightShadowRenderer implements SceneProcessor {
-
-    /**
-     * <code>FilterMode</code> specifies how shadows are filtered
-     */
-    public enum FilterMode {
+public class PointLightShadowRenderer extends AbstractShadowRenderer {
 
-        /**
-         * 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;
+    public static 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"));
-    }
+    protected GeometryList shadowMapOccluders = new GeometryList(new OpaqueComparator());
+    private Geometry[] frustums = null;
 
     /**
-     * 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>
+     * Creates a PointLightShadowRenderer
      *
-     * @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
+     * @param assetManager the application asset manager
+     * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048,
+     * etc...)
      */
-    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);
-
+    public PointLightShadowRenderer(AssetManager assetManager, int shadowMapSize) {
+        super(assetManager, shadowMapSize, CAM_NUMBER);
         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;
+            shadowCams[i] = new Camera(shadowMapSize, shadowMapSize);
         }
-
-        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;
-        }
+    @Override
+    protected void updateShadowCams(Camera viewCam) {
 
-        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));
@@ -505,7 +101,22 @@ public class PointLightShadowRenderer implements SceneProcessor {
             shadowCams[i].updateViewProjection();
         }
 
-        if (debug && frustums == null) {
+    }
+
+    @Override
+    protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers) {
+        ShadowUtil.getOccludersInCamFrustum(sceneOccluders, shadowCams[shadowMapIndex], shadowMapOccluders);
+        return shadowMapOccluders;
+    }
+
+    @Override
+    protected Camera getShadowCam(int shadowMapIndex) {
+        return shadowCams[shadowMapIndex];
+    }
+
+    @Override
+    protected void doDisplayFrustumDebug(int shadowMapIndex) {
+        if (frustums == null) {
             frustums = new Geometry[CAM_NUMBER];
             Vector3f[] points = new Vector3f[8];
             for (int i = 0; i < 8; i++) {
@@ -516,220 +127,14 @@ public class PointLightShadowRenderer implements SceneProcessor {
                 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);
+        if (frustums[shadowMapIndex].getParent() == null) {
+            ((Node) viewPort.getScenes().get(0)).attachChild(frustums[shadowMapIndex]);
         }
     }
 
-    /**
-     * 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;
+    @Override
+    protected void setMaterialParameters(Material material) {
+        material.setVector3("LightPos", light.getPosition());
     }
 
     /**
@@ -748,6 +153,5 @@ public class PointLightShadowRenderer implements SceneProcessor {
      */
     public void setLight(PointLight light) {
         this.light = light;
-        updateShadowCams();
     }
 }

+ 2 - 0
engine/src/core/com/jme3/shadow/PssmShadowFilter.java

@@ -61,7 +61,9 @@ import java.io.IOException;
  * API is basically the same as the PssmShadowRenderer;
  * 
  * @author Rémy Bouquet aka Nehon
+ * @deprecated use {@link DirectionalLightShadowFilter}
  */
+@Deprecated
 public class PssmShadowFilter extends Filter {
 
     private PssmShadowRenderer pssmRenderer;

+ 114 - 79
engine/src/core/com/jme3/shadow/PssmShadowRenderer.java

@@ -48,6 +48,7 @@ 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;
@@ -66,17 +67,21 @@ import java.util.List;
  * 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>
+ * 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
+ * @deprecated use {@link DirectionalLightShadowRenderer}
  */
+@Deprecated
 public class PssmShadowRenderer implements SceneProcessor {
 
     /**
      * <code>FilterMode</code> specifies how shadows are filtered
+     * @deprecated use {@link EdgeFilteringMode}
      */
-    public enum FilterMode {
+    @Deprecated
+    public enum FilterMode{
 
         /**
          * Shadows are not filtered. Nearest sample is used, causing in blocky
@@ -89,30 +94,32 @@ public class PssmShadowRenderer implements SceneProcessor {
          */
         Bilinear,
         /**
-         * Dither-based sampling is used, very cheap but can look bad
-         * at low resolutions.
+         * 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
+         * 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
+         * 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
+         * 8x8 percentage-closer filtering is used. Shadows will be smoother at
+         * the cost of performance
          */
         PCF8
     }
 
     /**
-     * Specifies the shadow comparison mode 
+     * Specifies the shadow comparison mode
+     * @deprecated use {@link CompareMode}
      */
+    @Deprecated
     public enum CompareMode {
 
         /**
@@ -169,24 +176,30 @@ public class PssmShadowRenderer implements SceneProcessor {
     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>
+     * 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). 
+     * @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 PssmShadowRenderer(AssetManager manager, int size, int nbSplits) {
-        this(manager, size, nbSplits, new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md"));
+        this(manager, size, nbSplits, new Material(manager, "Common/MatDefs/Shadow/PostShadow.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>
+     * 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          
+     * @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 PssmShadowRenderer(AssetManager manager, int size, int nbSplits, Material postShadowMat) {
 
@@ -219,7 +232,7 @@ public class PssmShadowRenderer implements SceneProcessor {
             //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
             shadowFB[i].setColorTexture(dummyTex);
 
-            postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]);            
+            postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]);
 
             //quads for debuging purpose
             dispPic[i] = new Picture("Picture" + i);
@@ -240,8 +253,10 @@ public class PssmShadowRenderer implements SceneProcessor {
     }
 
     /**
-     * Sets the filtering mode for shadow edges see {@link FilterMode} for more info
-     * @param filterMode 
+     * Sets the filtering mode for shadow edges see {@link FilterMode} for more
+     * info
+     *
+     * @param filterMode
      */
     final public void setFilterMode(FilterMode filterMode) {
         if (filterMode == null) {
@@ -271,7 +286,8 @@ public class PssmShadowRenderer implements SceneProcessor {
 
     /**
      * sets the shadow compare mode see {@link CompareMode} for more info
-     * @param compareMode 
+     *
+     * @param compareMode
      */
     final public void setCompareMode(CompareMode compareMode) {
         if (compareMode == null) {
@@ -351,7 +367,8 @@ public class PssmShadowRenderer implements SceneProcessor {
 
     /**
      * returns the light direction used by the processor
-     * @return 
+     *
+     * @return
      */
     public Vector3f getDirection() {
         return direction;
@@ -359,7 +376,8 @@ public class PssmShadowRenderer implements SceneProcessor {
 
     /**
      * Sets the light direction to use to compute shadows
-     * @param direction 
+     *
+     * @param direction
      */
     public void setDirection(Vector3f direction) {
         this.direction.set(direction).normalizeLocal();
@@ -425,12 +443,23 @@ public class PssmShadowRenderer implements SceneProcessor {
             lightViewProjectionsMatrices[i].set(shadowCam.getViewProjectionMatrix());
             renderManager.setCamera(shadowCam, false);
 
+            if (debugfrustums) {
+//                    frustrumFromBound(b.casterBB,ColorRGBA.Blue );
+//                    frustrumFromBound(b.receiverBB,ColorRGBA.Green );
+//                    frustrumFromBound(b.splitBB,ColorRGBA.Yellow );
+                ((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, i));
+                ShadowUtil.updateFrustumPoints2(shadowCam, points);
+                ((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, i));
+
+            }
+
             r.setFrameBuffer(shadowFB[i]);
             r.clearBuffers(false, true, false);
 
             // render shadow casters to shadow map
             viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCam, true);
         }
+        debugfrustums = false;
         if (flushQueues) {
             occluders.clear();
         }
@@ -441,6 +470,11 @@ public class PssmShadowRenderer implements SceneProcessor {
         renderManager.setCamera(viewCam, false);
 
     }
+    boolean debugfrustums = false;
+
+    public void displayFrustum() {
+        debugfrustums = true;
+    }
 
     //debug only : displays depth shadow maps
     protected void displayShadowMap(Renderer r) {
@@ -457,8 +491,9 @@ public class PssmShadowRenderer implements SceneProcessor {
         renderManager.setCamera(cam, false);
     }
 
-    /**For dubuging purpose
-     * Allow to "snapshot" the current frustrum to the scene
+    /**
+     * For dubuging purpose Allow to "snapshot" the current frustrum to the
+     * scene
      */
     public void displayDebug() {
         debug = true;
@@ -515,36 +550,22 @@ public class PssmShadowRenderer implements SceneProcessor {
 
         //iterating through the mat cache and setting the parameters
         for (Material mat : matCache) {
-            if (mat.getParam("Splits") == null) {
-                mat.setColor("Splits", splits);
-            }
-            if (mat.getParam("ShadowMapSize") == null) {
-                mat.setFloat("ShadowMapSize", shadowMapSize);
-            }
+            mat.setColor("Splits", splits);
+            mat.setFloat("ShadowMapSize", shadowMapSize);
+
             for (int j = 0; j < nbSplits; j++) {
                 mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
             }
-            if (mat.getParam("ShadowMap0") == null) {
-                for (int j = 0; j < nbSplits; 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);                
+            for (int j = 0; j < nbSplits; j++) {
+                mat.setTexture("ShadowMap" + j, shadowMaps[j]);
             }
+            mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
+            mat.setInt("FilterMode", filterMode.ordinal());
+            mat.setFloat("PCFEdge", edgesThickness);
+            mat.setFloat("ShadowIntensity", shadowIntensity);
 
-            if (mat.getParam("ShadowIntensity") == null || applyShadowIntensity) {
-                mat.setFloat("ShadowIntensity", shadowIntensity);                
-            }
-            
-            if (fadeInfo != null && mat.getParam("FadeInfo") == null || applyFadeInfo) {
-                mat.setVector2("FadeInfo", fadeInfo);             
+            if (fadeInfo != null) {
+                mat.setVector2("FadeInfo", fadeInfo);
             }
 
         }
@@ -567,7 +588,8 @@ public class PssmShadowRenderer implements SceneProcessor {
         postshadowMat.setColor("Splits", splits);
         for (int j = 0; j < nbSplits; j++) {
             postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
-        }
+            postshadowMat.setTexture("ShadowMap" + j, shadowMaps[j]);
+        }        
     }
 
     public void preFrame(float tpf) {
@@ -580,8 +602,8 @@ public class PssmShadowRenderer implements SceneProcessor {
     }
 
     /**
-     * returns the labda parameter
-     * see #setLambda(float lambda)
+     * returns the labda parameter see #setLambda(float lambda)
+     *
      * @return lambda
      */
     public float getLambda() {
@@ -602,6 +624,7 @@ public class PssmShadowRenderer implements SceneProcessor {
 
     /**
      * How far the shadows are rendered in the view
+     *
      * @see #setShadowZExtend(float zFar)
      * @return shadowZExtend
      */
@@ -610,20 +633,23 @@ public class PssmShadowRenderer implements SceneProcessor {
     }
 
     /**
-     * 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.
+     * 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){
+        if (fadeInfo != null) {
             fadeInfo.set(zFar - fadeLength, 1f / fadeLength);
         }
         this.zFarOverride = zFar;
-        
+
     }
 
     /**
      * returns the shdaow intensity
+     *
      * @see #setShadowIntensity(float shadowIntensity)
      * @return shadowIntensity
      */
@@ -632,10 +658,10 @@ public class PssmShadowRenderer implements SceneProcessor {
     }
 
     /**
-     * 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
+     * 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) {
@@ -646,6 +672,7 @@ public class PssmShadowRenderer implements SceneProcessor {
 
     /**
      * returns the edges thickness
+     *
      * @see #setEdgesThickness(int edgesThickness)
      * @return edgesThickness
      */
@@ -654,8 +681,10 @@ public class PssmShadowRenderer implements SceneProcessor {
     }
 
     /**
-     * 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 
+     * 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));
@@ -666,6 +695,7 @@ public class PssmShadowRenderer implements SceneProcessor {
 
     /**
      * returns true if the PssmRenderer flushed the shadow queues
+     *
      * @return flushQueues
      */
     public boolean isFlushQueues() {
@@ -673,18 +703,21 @@ public class PssmShadowRenderer implements SceneProcessor {
     }
 
     /**
-     * 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 
+     * 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.
+     * 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) {
@@ -702,15 +735,17 @@ public class PssmShadowRenderer implements SceneProcessor {
             postshadowMat.setVector2("FadeInfo", fadeInfo);
         }
     }
-    
-     /**
-     * get the length over which the shadow will fade out when using a shadowZextend
+
+    /**
+     * 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){
+    public float getShadowZFadeLength() {
+        if (fadeInfo != null) {
             return zFarOverride - fadeInfo.x;
         }
-        return 0f;        
+        return 0f;
     }
 }

+ 4 - 6
engine/src/core/com/jme3/shadow/ShadowUtil.java

@@ -361,9 +361,7 @@ public class ShadowUtil {
 
         if (ortho) {
             shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
-        } else {
-            shadowCam.setFrustumPerspective(45, 1, 1, 150);
-        }
+        } 
 
         // create transform to rotate points to viewspace        
         Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix();
@@ -442,9 +440,9 @@ public class ShadowUtil {
 
         splitMin.z = 0;
 
-        if (!ortho) {
-            shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z);
-        }
+//        if (!ortho) {
+//            shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z);
+//        }
 
         Matrix4f projMatrix = shadowCam.getProjectionMatrix();
 

+ 143 - 0
engine/src/core/com/jme3/shadow/SpotLightShadowFilter.java

@@ -0,0 +1,143 @@
+/*
+ * 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.SpotLight;
+import java.io.IOException;
+
+/**
+ *
+ * This Filter does basically the same as a SpotLightShadowRenderer
+ * except it renders the post shadow pass as a fulscreen quad pass instead of a
+ * geometry pass. It's mostly faster than PssmShadowRenderer 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 SpotLightShadowFilter extends AbstractShadowFilter<SpotLightShadowRenderer> {
+
+
+    /**
+     * Creates a SpotLight Shadow Filter    
+     * @param assetManager the application asset manager
+     * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048,
+     * etc...)
+     * the more quality, the less fps).
+     */
+    public SpotLightShadowFilter(AssetManager assetManager, int shadowMapSize) {
+        super(assetManager, shadowMapSize, new SpotLightShadowRenderer(assetManager, shadowMapSize));     
+    }
+
+    /**
+     * return the light used to cast shadows
+     *
+     * @return the SpotLight
+     */
+    public SpotLight getLight() {
+        return shadowRenderer.getLight();
+    }
+
+    /**
+     * Sets the light to use to cast shadows
+     *
+     * @param light a SpotLight
+     */
+    public void setLight(SpotLight light) {
+        shadowRenderer.setLight(light);
+    }
+
+    /**
+     * How far the shadows are rendered in the view
+     *
+     * @see setShadowZExtend(float zFar)
+     * @return shadowZExtend
+     */
+    public float getShadowZExtend() {
+        return shadowRenderer.getShadowZExtend();
+    }
+
+    /**
+     * 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) {
+        shadowRenderer.setShadowZExtend(zFar);
+    }
+
+    /**
+     * 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) {
+        shadowRenderer.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 shadowRenderer.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);
+
+    }
+}

+ 211 - 0
engine/src/core/com/jme3/shadow/SpotLightShadowRenderer.java

@@ -0,0 +1,211 @@
+/*
+ * 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.DirectionalLight;
+import com.jme3.light.SpotLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.math.Vector4f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.OpaqueComparator;
+import com.jme3.scene.Node;
+
+/**
+ * SpotLightShadowRenderer 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 SpotLightShadowRenderer extends AbstractShadowRenderer {
+
+    protected float zFarOverride = 0;
+    protected Camera shadowCam;
+    protected GeometryList mapOccluders = new GeometryList(new OpaqueComparator());
+    protected SpotLight light;
+    protected Vector3f[] points = new Vector3f[8];
+    //Holding the info for fading shadows in the far distance 
+    protected Vector2f fadeInfo;
+    protected float fadeLength;
+
+    /**
+     * Create a SpotLightShadowRenderer This use standard shadow mapping
+     *
+     * @param assetManager the application asset manager
+     * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048,
+     * etc...) the more quality, the less fps).
+     */
+    public SpotLightShadowRenderer(AssetManager assetManager, int shadowMapSize) {
+        super(assetManager, shadowMapSize, 1);
+
+        shadowCam = new Camera(shadowMapSize, shadowMapSize);
+
+        for (int i = 0; i < points.length; i++) {
+            points[i] = new Vector3f();
+        }
+    }
+
+    /**
+     * return the light used to cast shadows
+     *
+     * @return the SpotLight
+     */
+    public SpotLight getLight() {
+        return light;
+    }
+
+    /**
+     * Sets the light to use to cast shadows
+     *
+     * @param light a SpotLight
+     */
+    public void setLight(SpotLight light) {
+        this.light = light;
+    }
+
+    @Override
+    protected void updateShadowCams(Camera viewCam) {
+
+        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);
+        //shadowCam.setDirection(direction);
+
+        shadowCam.setFrustumPerspective(light.getSpotOuterAngle() * FastMath.RAD_TO_DEG * 2.0f, 1, 1f, light.getSpotRange());
+        shadowCam.getRotation().lookAt(light.getDirection(), shadowCam.getUp());
+        shadowCam.setLocation(light.getPosition());
+
+        shadowCam.update();
+        shadowCam.updateViewProjection();
+
+    }
+
+    @Override
+    protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers) {
+        ShadowUtil.getOccludersInCamFrustum(sceneOccluders, shadowCam, mapOccluders);
+        return mapOccluders;
+    }
+
+    @Override
+    protected Camera getShadowCam(int shadowMapIndex) {
+        return shadowCam;
+    }
+
+    @Override
+    protected void doDisplayFrustumDebug(int shadowMapIndex) {
+        Vector3f[] points2 = points.clone();
+
+        ((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, shadowMapIndex));
+        ShadowUtil.updateFrustumPoints2(shadowCam, points2);
+        ((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points2, shadowMapIndex));
+    }
+
+    @Override
+    protected void setMaterialParameters(Material material) {    
+    }
+
+    /**
+     * 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;
+
+    }
+
+    /**
+     * 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;
+    }
+}

+ 154 - 0
engine/src/test/jme3test/light/ShadowTestUIManager.java

@@ -0,0 +1,154 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3test.light;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.input.InputManager;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Node;
+import com.jme3.shadow.AbstractShadowFilter;
+import com.jme3.shadow.AbstractShadowRenderer;
+import com.jme3.shadow.CompareMode;
+import com.jme3.shadow.EdgeFilteringMode;
+
+/**
+ *
+ * @author Nehon
+ */
+public class ShadowTestUIManager implements ActionListener {
+
+    private BitmapText shadowTypeText;
+    private BitmapText shadowCompareText;
+    private BitmapText shadowFilterText;
+    private BitmapText shadowIntensityText;
+    private final static String TYPE_TEXT = "(Space) Shadow type : ";
+    private final static String COMPARE_TEXT = "(enter) Shadow compare ";
+    private final static String FILTERING_TEXT = "(f) Edge filtering : ";
+    private final static String INTENSITY_TEXT = "(t:up, g:down) Shadow intensity : ";
+    private boolean hardwareShadows = true;
+    private AbstractShadowRenderer plsr;
+    private AbstractShadowFilter plsf;
+    private ViewPort viewPort;
+    private int filteringIndex = 0;
+    private int renderModeIndex = 0;
+    
+
+    public ShadowTestUIManager(AssetManager assetManager,AbstractShadowRenderer plsr, AbstractShadowFilter plsf, 
+            Node guiNode, InputManager inputManager, ViewPort viewPort) {
+        this.plsr = plsr;
+        this.plsf = plsf;
+        this.viewPort = viewPort;
+        BitmapFont guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+        shadowTypeText = createText(guiFont);
+        shadowCompareText = createText(guiFont);
+        shadowFilterText = createText(guiFont);
+        shadowIntensityText = createText(guiFont);
+
+        shadowTypeText.setText(TYPE_TEXT + "Processor");
+        shadowCompareText.setText(COMPARE_TEXT + (hardwareShadows ? "Hardware" : "Software"));
+        shadowFilterText.setText(FILTERING_TEXT + plsr.getEdgeFilteringMode().toString());
+        shadowIntensityText.setText(INTENSITY_TEXT + plsr.getShadowIntensity());
+
+        shadowTypeText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 20, 0);
+        shadowCompareText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 40, 0);
+        shadowFilterText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 60, 0);
+        shadowIntensityText.setLocalTranslation(10, viewPort.getCamera().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");
+
+    }
+
+
+    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.setShadowCompareMode(hardwareShadows ? CompareMode.Hardware : CompareMode.Software);
+            plsf.setShadowCompareMode(hardwareShadows ? CompareMode.Hardware : CompareMode.Software);
+
+            shadowCompareText.setText(COMPARE_TEXT + (hardwareShadows ? "Hardware" : "Software"));
+        }
+
+
+        if (name.equals("changeFiltering") && keyPressed) {
+            filteringIndex = plsr.getEdgeFilteringMode().ordinal();
+            filteringIndex = (filteringIndex + 1) % EdgeFilteringMode.values().length;
+            EdgeFilteringMode m = EdgeFilteringMode.values()[filteringIndex];
+            plsr.setEdgeFilteringMode(m);
+            plsf.setEdgeFilteringMode(m);
+            shadowFilterText.setText(FILTERING_TEXT + m.toString());
+        }
+
+        if (name.equals("ShadowUp") && keyPressed) {
+            plsr.setShadowIntensity(plsr.getShadowIntensity() + 0.1f);
+            plsf.setShadowIntensity(plsf.getShadowIntensity() + 0.1f);
+
+            shadowIntensityText.setText(INTENSITY_TEXT + plsr.getShadowIntensity());
+        }
+        if (name.equals("ShadowDown") && keyPressed) {
+            plsr.setShadowIntensity(plsr.getShadowIntensity() - 0.1f);
+            plsf.setShadowIntensity(plsf.getShadowIntensity() - 0.1f);
+            shadowIntensityText.setText(INTENSITY_TEXT + plsr.getShadowIntensity());
+        }
+        if (name.equals("ThicknessUp") && keyPressed) {
+            plsr.setEdgesThickness(plsr.getEdgesThickness() + 1);
+            plsf.setEdgesThickness(plsf.getEdgesThickness() + 1);
+            System.out.println("Shadow thickness : " + plsr.getEdgesThickness());
+        }
+        if (name.equals("ThicknessDown") && keyPressed) {
+            plsr.setEdgesThickness(plsr.getEdgesThickness() - 1);
+            plsf.setEdgesThickness(plsf.getEdgesThickness() - 1);
+            System.out.println("Shadow thickness : " + plsr.getEdgesThickness());
+        }
+
+    }
+
+    private BitmapText createText(BitmapFont guiFont) {
+        BitmapText t = new BitmapText(guiFont, false);
+        t.setSize(guiFont.getCharSet().getRenderedSize() * 0.75f);
+        return t;
+    }
+}

+ 311 - 0
engine/src/test/jme3test/light/TestDirectionalLightShadow.java

@@ -0,0 +1,311 @@
+/*
+ * 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.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.AbstractControl;
+import com.jme3.scene.control.Control;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.shadow.CompareMode;
+import com.jme3.shadow.DirectionalLightShadowFilter;
+import com.jme3.shadow.DirectionalLightShadowRenderer;
+import com.jme3.shadow.EdgeFilteringMode;
+import com.jme3.shadow.PssmShadowRenderer.FilterMode;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.SkyFactory;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestDirectionalLightShadow extends SimpleApplication implements ActionListener {
+
+    private Spatial[] obj;
+    private Material[] mat;
+    private boolean hardwareShadows = false;
+    private DirectionalLightShadowRenderer dlsr;
+    private DirectionalLightShadowFilter dlsf;
+    private Geometry ground;
+    private Material matGroundU;
+    private Material matGroundL;
+
+    public static void main(String[] args) {
+        TestDirectionalLightShadow app = new TestDirectionalLightShadow();
+        app.start();
+    }
+
+    public void loadScene() {
+        obj = new Spatial[2];
+        mat = new Material[2];
+        mat[0] = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
+        mat[1] = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+        mat[1].setBoolean("UseMaterialColors", true);
+        mat[1].setColor("Ambient", ColorRGBA.White.mult(0.5f));
+        mat[1].setColor("Diffuse", ColorRGBA.White.clone());
+
+
+        obj[0] = new Geometry("sphere", new Sphere(30, 30, 2));
+        obj[0].setShadowMode(ShadowMode.CastAndReceive);
+        obj[1] = new Geometry("cube", new Box(1.0f, 1.0f, 1.0f));
+        obj[1].setShadowMode(ShadowMode.CastAndReceive);
+        TangentBinormalGenerator.generate(obj[1]);
+        TangentBinormalGenerator.generate(obj[0]);
+
+
+        for (int i = 0; i < 60; i++) {
+            Spatial t = obj[FastMath.nextRandomInt(0, obj.length - 1)].clone(false);
+            t.setLocalScale(FastMath.nextRandomFloat() * 10f);
+            t.setMaterial(mat[FastMath.nextRandomInt(0, mat.length - 1)]);
+            rootNode.attachChild(t);
+            t.setLocalTranslation(FastMath.nextRandomFloat() * 200f, FastMath.nextRandomFloat() * 30f + 20, 30f * (i + 2f));
+        }
+
+        Box b = new Box(new Vector3f(0, 10, 550), 1000, 2, 1000);
+        b.scaleTextureCoordinates(new Vector2f(10, 10));
+        ground = new Geometry("soil", b);
+        matGroundU = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        matGroundU.setColor("Color", ColorRGBA.Green);
+
+
+        matGroundL = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+        Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+        grass.setWrap(WrapMode.Repeat);
+        matGroundL.setTexture("DiffuseMap", grass);
+
+        ground.setMaterial(matGroundL);
+
+        ground.setShadowMode(ShadowMode.CastAndReceive);
+        rootNode.attachChild(ground);
+
+        l = new DirectionalLight();
+        //new Vector3f(-0.5973172f, -0.56583486f, 0.8846725f).normalizeLocal()
+        l.setDirection(new Vector3f(-1, -1, -1));
+        rootNode.addLight(l);
+
+
+        AmbientLight al = new AmbientLight();
+        al.setColor(ColorRGBA.White.mult(0.5f));
+        rootNode.addLight(al);
+
+        Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false);
+        sky.setLocalScale(350);
+
+        rootNode.attachChild(sky);
+    }
+    DirectionalLight l;
+
+    @Override
+    public void simpleInitApp() {
+        // put the camera in a bad position
+        cam.setLocation(new Vector3f(65.25412f, 44.38738f, 9.087874f));
+        cam.setRotation(new Quaternion(0.078139365f, 0.050241485f, -0.003942559f, 0.9956679f));
+
+        flyCam.setMoveSpeed(100);
+
+        loadScene();
+
+        dlsr = new DirectionalLightShadowRenderer(assetManager, 1024, 3);
+        dlsr.setLight(l);
+        dlsr.setLambda(0.55f);
+        dlsr.setShadowIntensity(0.6f);        
+        dlsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
+        //dlsr.displayFrustum();
+        viewPort.addProcessor(dlsr);
+
+        dlsf = new DirectionalLightShadowFilter(assetManager, 1024, 3);
+        dlsf.setLight(l);
+        dlsf.setLambda(0.55f);
+        dlsf.setShadowIntensity(0.6f);        
+        dlsf.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
+        dlsf.setEnabled(false);
+
+        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+        fpp.addFilter(dlsf);
+
+        viewPort.addProcessor(fpp);
+
+        initInputs();
+    }
+
+    private void initInputs() {
+       
+        inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y));
+        inputManager.addMapping("ThicknessDown", new KeyTrigger(KeyInput.KEY_H));
+        inputManager.addMapping("lambdaUp", new KeyTrigger(KeyInput.KEY_U));
+        inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J));
+        inputManager.addMapping("switchGroundMat", new KeyTrigger(KeyInput.KEY_M));
+        inputManager.addMapping("splits", new KeyTrigger(KeyInput.KEY_X));
+
+        inputManager.addMapping("up", new KeyTrigger(KeyInput.KEY_NUMPAD8));
+        inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_NUMPAD2));
+        inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_NUMPAD6));
+        inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_NUMPAD4));
+        inputManager.addMapping("fwd", new KeyTrigger(KeyInput.KEY_PGUP));
+        inputManager.addMapping("back", new KeyTrigger(KeyInput.KEY_PGDN));
+
+
+        inputManager.addListener(this, "lambdaUp", "lambdaDown",   "ThicknessUp", "ThicknessDown", 
+                "switchGroundMat", "splits", "up", "down", "right", "left", "fwd", "back");
+
+        ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, dlsr, dlsf, guiNode, inputManager, viewPort);
+    }
+
+   
+   
+
+    public void onAction(String name, boolean keyPressed, float tpf) {
+       
+
+    
+        if (name.equals("lambdaUp") && keyPressed) {
+            dlsr.setLambda(dlsr.getLambda() + 0.01f);
+            dlsf.setLambda(dlsr.getLambda() + 0.01f);
+            System.out.println("Lambda : " + dlsr.getLambda());
+        } else if (name.equals("lambdaDown") && keyPressed) {
+            dlsr.setLambda(dlsr.getLambda() - 0.01f);
+            dlsf.setLambda(dlsr.getLambda() - 0.01f);
+            System.out.println("Lambda : " + dlsr.getLambda());
+        }
+
+        if (name.equals("ShadowUp") && keyPressed) {
+            dlsr.setShadowIntensity(dlsr.getShadowIntensity() + 0.1f);
+            dlsf.setShadowIntensity(dlsr.getShadowIntensity() + 0.1f);
+            System.out.println("Shadow intensity : " + dlsr.getShadowIntensity());
+        }
+        if (name.equals("ShadowDown") && keyPressed) {
+            dlsr.setShadowIntensity(dlsr.getShadowIntensity() - 0.1f);
+            dlsf.setShadowIntensity(dlsr.getShadowIntensity() - 0.1f);
+            System.out.println("Shadow intensity : " + dlsr.getShadowIntensity());
+        }
+        if (name.equals("ThicknessUp") && keyPressed) {
+            dlsr.setEdgesThickness(dlsr.getEdgesThickness() + 1);
+            dlsf.setEdgesThickness(dlsr.getEdgesThickness() + 1);
+            System.out.println("Shadow thickness : " + dlsr.getEdgesThickness());
+        }
+        if (name.equals("ThicknessDown") && keyPressed) {
+            dlsr.setEdgesThickness(dlsr.getEdgesThickness() - 1);
+            dlsf.setEdgesThickness(dlsr.getEdgesThickness() - 1);
+            System.out.println("Shadow thickness : " + dlsr.getEdgesThickness());
+        }
+        if (name.equals("switchGroundMat") && keyPressed) {
+            if (ground.getMaterial() == matGroundL) {
+                ground.setMaterial(matGroundU);
+            } else {
+                ground.setMaterial(matGroundL);
+            }
+        }
+
+        if (name.equals("up")) {
+            up = keyPressed;
+        }
+        if (name.equals("down")) {
+            down = keyPressed;
+        }
+        if (name.equals("right")) {
+            right = keyPressed;
+        }
+        if (name.equals("left")) {
+            left = keyPressed;
+        }
+        if (name.equals("fwd")) {
+            fwd = keyPressed;
+        }
+        if (name.equals("back")) {
+            back = keyPressed;
+        }
+
+    }
+    boolean up = false;
+    boolean down = false;
+    boolean left = false;
+    boolean right = false;
+    boolean fwd = false;
+    boolean back = false;
+    float time = 0;
+    float s = 1f;
+
+    @Override
+    public void simpleUpdate(float tpf) {
+        if (up) {
+            Vector3f v = l.getDirection();
+            v.y += tpf / s;
+            setDir(v);
+        }
+        if (down) {
+            Vector3f v = l.getDirection();
+            v.y -= tpf / s;
+            setDir(v);
+        }
+        if (right) {
+            Vector3f v = l.getDirection();
+            v.x += tpf / s;
+            setDir(v);
+        }
+        if (left) {
+            Vector3f v = l.getDirection();
+            v.x -= tpf / s;
+            setDir(v);
+        }
+        if (fwd) {
+            Vector3f v = l.getDirection();
+            v.z += tpf / s;
+            setDir(v);
+        }
+        if (back) {
+            Vector3f v = l.getDirection();
+            v.z -= tpf / s;
+            setDir(v);
+        }
+
+    }
+
+    private void setDir(Vector3f v) {
+        l.setDirection(v);
+    }
+}

+ 29 - 137
engine/src/test/jme3test/light/TestPointLightShadows.java

@@ -32,12 +32,7 @@
 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;
@@ -46,18 +41,18 @@ 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.EdgeFilteringMode;
 import com.jme3.shadow.PointLightShadowFilter;
 import com.jme3.shadow.PointLightShadowRenderer;
 
-public class TestPointLightShadows extends SimpleApplication implements ActionListener {
+public class TestPointLightShadows extends SimpleApplication {
 
     public static void main(String[] args) {
         TestPointLightShadows app = new TestPointLightShadows();
         app.start();
     }
     Node lightNode;
-    private boolean hardwareShadows = true;
-    PointLightShadowRenderer plsr ;
+    PointLightShadowRenderer plsr;
     PointLightShadowFilter plsf;
 
     @Override
@@ -91,147 +86,44 @@ public class TestPointLightShadows extends SimpleApplication implements ActionLi
 
         plsr = new PointLightShadowRenderer(assetManager, 512);
         plsr.setLight((PointLight) scene.getLocalLightList().get(0));
-        plsr.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest);
-    //   plsr.displayDebug();
+        plsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
+     //   plsr.setFlushQueues(false);
+        //   plsr.displayDebug();
         viewPort.addProcessor(plsr);
 
 
+//        PointLight pl = new PointLight();
+//        pl.setPosition(new Vector3f(0, 0.5f, 0));
+//        pl.setRadius(5);
+//        rootNode.addLight(pl);
+
+//        Geometry lightMdl2 = new Geometry("Light2", new Sphere(10, 10, 0.1f));
+//        //Geometry  lightMdl = new Geometry("Light", new Box(.1f,.1f,.1f));
+//        lightMdl2.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+//        lightMdl2.setShadowMode(RenderQueue.ShadowMode.Off);
+//        rootNode.attachChild(lightMdl2);
+//        lightMdl2.setLocalTranslation(pl.getPosition());
+//        PointLightShadowRenderer plsr2 = new PointLightShadowRenderer(assetManager, 512);
+//        plsr2.setLight(pl);
+//        plsr2.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
+//        //   plsr.displayDebug();
+//        viewPort.addProcessor(plsr2);
+
+
         plsf = new PointLightShadowFilter(assetManager, 512);
         plsf.setLight((PointLight) scene.getLocalLightList().get(0));
-        plsf.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest);
+        plsf.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
         plsf.setEnabled(false);
-        
+
         FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
-        fpp.addFilter(plsf);   
+        fpp.addFilter(plsf);
         viewPort.addProcessor(fpp);
 
-        initUIAndInputs();
+        ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, plsr, plsf, guiNode, inputManager, viewPort);
     }
-    
-     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;
+//        lightNode.move(FastMath.cos(tpf) * 0.4f, 0, FastMath.sin(tpf) * 0.4f);
     }
 }

+ 110 - 12
engine/src/test/jme3test/light/TestPssmShadow.java

@@ -32,9 +32,13 @@
 package jme3test.light;
 
 import com.jme3.app.SimpleApplication;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.Savable;
 import com.jme3.font.BitmapText;
 import com.jme3.input.KeyInput;
 import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
 import com.jme3.input.controls.KeyTrigger;
 import com.jme3.light.AmbientLight;
 import com.jme3.light.DirectionalLight;
@@ -63,6 +67,7 @@ import com.jme3.texture.Texture;
 import com.jme3.texture.Texture.WrapMode;
 import com.jme3.util.SkyFactory;
 import com.jme3.util.TangentBinormalGenerator;
+import java.io.IOException;
 
 public class TestPssmShadow extends SimpleApplication implements ActionListener {
 
@@ -123,7 +128,7 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
         ground.setShadowMode(ShadowMode.CastAndReceive);
         rootNode.attachChild(ground);
 
-        DirectionalLight l = new DirectionalLight();
+        l = new DirectionalLight();
         l.setDirection(new Vector3f(-1, -1, -1));
         rootNode.addLight(l);
 
@@ -136,6 +141,7 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
 
         rootNode.attachChild(sky);
     }
+    DirectionalLight l;
 
     @Override
     public void simpleInitApp() {
@@ -148,36 +154,42 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
         loadScene();
 
         pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 3);
-        pssmRenderer.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
-        //  pssmRenderer.setDirection(new Vector3f(0.5973172f, -0.16583486f, 0.7846725f).normalizeLocal());
+        //pssmRenderer.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+        pssmRenderer.setDirection(new Vector3f(-0.5973172f, -0.56583486f, 0.8846725f).normalizeLocal());
         pssmRenderer.setLambda(0.55f);
         pssmRenderer.setShadowIntensity(0.6f);
         pssmRenderer.setCompareMode(CompareMode.Software);
         pssmRenderer.setFilterMode(FilterMode.Dither);
-        //     pssmRenderer.displayDebug();
+                
+        pssmRenderer.displayFrustum();
         viewPort.addProcessor(pssmRenderer);
 
 
 
         pssmFilter = new PssmShadowFilter(assetManager, 1024, 3);
-        pssmFilter.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
-        //pssmRenderer.setDirection(new Vector3f(0.5973172f, -0.16583486f, 0.7846725f).normalizeLocal());
+        //pssmFilter.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+        pssmRenderer.setDirection(new Vector3f(-0.5973172f, -0.56583486f, 0.8846725f).normalizeLocal());
         pssmFilter.setLambda(0.55f);
         pssmFilter.setShadowIntensity(0.6f);
         pssmFilter.setCompareMode(CompareMode.Software);
         pssmFilter.setFilterMode(FilterMode.Dither);
         pssmFilter.setEnabled(false);
+        
+
+//        pssmFilter.setShadowZFadeLength(300);
+//        pssmFilter.setShadowZExtend(500);
+        
         FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
         //  fpp.setNumSamples(4);
         fpp.addFilter(pssmFilter);
-       
+
         viewPort.addProcessor(fpp);
 
 
         initInputs();
     }
-    BitmapText infoText;  
-
+    BitmapText infoText;
+    
     private void initInputs() {
         /** Write text on the screen (HUD) */
         guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
@@ -195,7 +207,20 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
         inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J));
         inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN));
         inputManager.addMapping("switchGroundMat", new KeyTrigger(KeyInput.KEY_M));
-        inputManager.addListener(this, "lambdaUp", "lambdaDown", "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown", "changeFiltering", "switchGroundMat");
+        inputManager.addMapping("splits", new KeyTrigger(KeyInput.KEY_X));
+
+        inputManager.addMapping("up", new KeyTrigger(KeyInput.KEY_NUMPAD8));
+        inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_NUMPAD2));
+        inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_NUMPAD6));
+        inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_NUMPAD4));
+        inputManager.addMapping("fwd", new KeyTrigger(KeyInput.KEY_PGUP));
+        inputManager.addMapping("back", new KeyTrigger(KeyInput.KEY_PGDN));
+
+
+
+        inputManager.addListener(this, "lambdaUp", "lambdaDown", "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown", "changeFiltering",
+                "switchGroundMat", "splits", "up", "down", "right", "left", "fwd", "back");
+
     }
 
     private void print(String str) {
@@ -246,10 +271,10 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
                     break;
                 case 1:
                     viewPort.removeProcessor(pssmRenderer);
-                    pssmFilter.setEnabled(true);                  
+                    pssmFilter.setEnabled(true);
                     break;
                 case 2:
-                    pssmFilter.setEnabled(false);                   
+                    pssmFilter.setEnabled(false);
                     break;
             }
 
@@ -311,5 +336,78 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
             }
         }
 
+//        if (name.equals("splits") && keyPressed) {
+//            pssmRenderer.displayFrustum();
+//        }
+
+
+        if (name.equals("up")) {
+            up = keyPressed;
+        }
+        if (name.equals("down")) {
+            down = keyPressed;
+        }
+        if (name.equals("right")) {
+            right = keyPressed;
+        }
+        if (name.equals("left") ) {
+            left = keyPressed;
+        }
+        if (name.equals("fwd")) {
+            fwd = keyPressed;
+        }
+        if (name.equals("back")) {
+            back = keyPressed;
+        }
+
+    }
+    boolean up = false;
+    boolean down = false;
+    boolean left = false;
+    boolean right = false;
+    boolean fwd = false;
+    boolean back = false;
+    float time = 0;
+    float s = 1f;
+
+    @Override
+    public void simpleUpdate(float tpf) {
+        if (up) {
+            Vector3f v = l.getDirection();
+            v.y += tpf / s;
+            setDir(v);
+        }
+        if (down) {
+            Vector3f v = l.getDirection();
+            v.y -= tpf / s;
+            setDir(v);
+        }
+        if (right) {
+            Vector3f v = l.getDirection();
+            v.x += tpf / s;
+            setDir(v);
+        }
+        if (left) {
+            Vector3f v = l.getDirection();
+            v.x -= tpf / s;
+            setDir(v);
+        }
+        if (fwd) {
+            Vector3f v = l.getDirection();
+            v.z += tpf / s;
+            setDir(v);
+        }
+        if (back) {
+            Vector3f v = l.getDirection();
+            v.z -= tpf / s;
+            setDir(v);
+        }
+
+    }
+
+    private void setDir(Vector3f v) {
+        l.setDirection(v);
+        pssmFilter.setDirection(v);
+        pssmRenderer.setDirection(v);
     }
 }

+ 198 - 0
engine/src/test/jme3test/light/TestSpotLightShadows.java

@@ -0,0 +1,198 @@
+/*
+ * 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.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.SpotLight;
+import com.jme3.material.Material;
+import com.jme3.math.*;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.shader.VarType;
+import com.jme3.shadow.CompareMode;
+import com.jme3.shadow.EdgeFilteringMode;
+import com.jme3.shadow.SpotLightShadowFilter;
+import com.jme3.shadow.SpotLightShadowRenderer;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.util.TangentBinormalGenerator;
+
+public class TestSpotLightShadows extends SimpleApplication {
+
+    private Vector3f lightTarget = new Vector3f(12, 3.5f, 30);
+
+    public static void main(String[] args) {
+        TestSpotLightShadows app = new TestSpotLightShadows();
+        app.start();
+    }
+    SpotLight spot;
+    Geometry lightMdl;
+
+    public void setupLighting() {
+        AmbientLight al = new AmbientLight();
+        al.setColor(ColorRGBA.White.mult(0.3f));
+        rootNode.addLight(al);
+
+        rootNode.setShadowMode(ShadowMode.CastAndReceive);
+
+        spot = new SpotLight();
+
+        spot.setSpotRange(1000);
+        spot.setSpotInnerAngle(5f * FastMath.DEG_TO_RAD);
+        spot.setSpotOuterAngle(10 * FastMath.DEG_TO_RAD);
+        spot.setPosition(new Vector3f(70.70334f, 34.013165f, 27.1017f));
+        spot.setDirection(lightTarget.subtract(spot.getPosition()));
+        spot.setColor(ColorRGBA.White.mult(2));
+        rootNode.addLight(spot);
+
+
+//        PointLight pl=new PointLight();
+//      pl.setPosition(new Vector3f(77.70334f, 34.013165f, 27.1017f));
+//      pl.setRadius(1000);     
+//      pl.setColor(ColorRGBA.White.mult(2));
+//      rootNode.addLight(pl);
+        lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+        lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+        lightMdl.setLocalTranslation(new Vector3f(77.70334f, 34.013165f, 27.1017f));
+        lightMdl.setLocalScale(5);
+        rootNode.attachChild(lightMdl);
+
+//        DirectionalLight dl = new DirectionalLight();
+//        dl.setDirection(lightTarget.subtract(new Vector3f(77.70334f, 34.013165f, 27.1017f)));
+//        dl.setColor(ColorRGBA.White.mult(0.7f));
+//        rootNode.addLight(dl);
+
+
+        final SpotLightShadowRenderer slsr = new SpotLightShadowRenderer(assetManager, 512);
+        slsr.setLight(spot);       
+        slsr.setShadowIntensity(0.5f);
+        slsr.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON);   
+        viewPort.addProcessor(slsr);
+
+        SpotLightShadowFilter slsf = new SpotLightShadowFilter(assetManager, 512);
+        slsf.setLight(spot);    
+        slsf.setShadowIntensity(0.5f);
+        slsf.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON);  
+        slsf.setEnabled(false);
+        
+        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+        fpp.addFilter(slsf);
+        viewPort.addProcessor(fpp);
+        
+        ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, slsr, slsf, guiNode, inputManager, viewPort);
+
+        inputManager.addListener(new ActionListener() {
+            public void onAction(String name, boolean isPressed, float tpf) {
+                if (name.equals("stop") && isPressed) {
+                    stop = !stop;
+                 //   slsr.displayFrustum();
+                    System.out.println("pos : " + spot.getPosition());
+                    System.out.println("dir : " + spot.getDirection());
+                }
+            }
+        }, "stop");
+
+        inputManager.addMapping("stop", new KeyTrigger(KeyInput.KEY_1));
+
+    }
+
+    public void setupFloor() {
+        Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
+        mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat);
+        mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat);
+        mat.setBoolean("UseMaterialColors", true);
+        mat.setColor("Diffuse", ColorRGBA.White.clone());
+        mat.setColor("Ambient", ColorRGBA.White.clone());
+       // mat.setColor("Specular", ColorRGBA.White.clone());
+        // mat.getTextureParam("ParallaxMap").getTextureValue().setWrap(WrapMode.Repeat);
+        mat.setFloat("Shininess", 0);
+        //  mat.setBoolean("VertexLighting", true);
+
+
+        Box floor = new Box(Vector3f.ZERO, 50, 1f, 50);
+        TangentBinormalGenerator.generate(floor);
+        floor.scaleTextureCoordinates(new Vector2f(5, 5));
+        Geometry floorGeom = new Geometry("Floor", floor);
+        floorGeom.setMaterial(mat);
+        floorGeom.setShadowMode(ShadowMode.Receive);
+        rootNode.attachChild(floorGeom);
+    }
+
+    public void setupSignpost() {
+        Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml");
+        Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m");
+        //   mat.setBoolean("VertexLighting", true);
+        signpost.setMaterial(mat);
+        signpost.rotate(0, FastMath.HALF_PI, 0);
+        signpost.setLocalTranslation(12, 3.5f, 30);
+        signpost.setLocalScale(4);
+        signpost.setShadowMode(ShadowMode.CastAndReceive);
+        TangentBinormalGenerator.generate(signpost);
+        rootNode.attachChild(signpost);
+    }
+
+    @Override
+    public void simpleInitApp() {
+        cam.setLocation(new Vector3f(27.492603f, 29.138166f, -13.232513f));
+        cam.setRotation(new Quaternion(0.25168246f, -0.10547892f, 0.02760565f, 0.96164864f));
+        flyCam.setMoveSpeed(30);
+
+        setupLighting();
+        setupFloor();
+        setupSignpost();
+
+
+    }
+    float angle;
+    boolean stop = true;
+
+    @Override
+    public void simpleUpdate(float tpf) {
+        if (!stop) {
+            super.simpleUpdate(tpf);
+            angle += tpf;
+            angle %= FastMath.TWO_PI;
+
+            spot.setPosition(new Vector3f(FastMath.cos(angle) * 30f, 34.013165f, FastMath.sin(angle) * 30f));
+            lightMdl.setLocalTranslation(spot.getPosition());
+            spot.setDirection(lightTarget.subtract(spot.getPosition()));
+        }
+    }
+}